Skip to content
This repository has been archived by the owner on Apr 8, 2023. It is now read-only.

[wip] Search all documents #658

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions _1327/documents/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@


from django.conf import settings
from django.contrib.auth.models import Group
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from django.core.exceptions import SuspiciousOperation
from django.db import transaction
from django.shortcuts import Http404
from django.utils import timezone
from guardian.core import ObjectPermissionChecker
from reversion import revisions
from reversion.models import Version

Expand Down Expand Up @@ -174,3 +178,40 @@ def delete_cascade_to_json(cascade):
"name": str(cascade_item),
})
return items


def get_permitted_documents(documents, request, groupid):
groupid = int(groupid)
try:
group = Group.objects.get(id=groupid)
except ObjectDoesNotExist:
raise Http404

own_group = request.user.is_superuser or group in request.user.groups.all()

# Prefetch group permissions
group_checker = ObjectPermissionChecker(group)
group_checker.prefetch_perms(documents)

# Prefetch user permissions
user_checker = ObjectPermissionChecker(request.user)
user_checker.prefetch_perms(documents)

# Prefetch ip-range group permissions
ip_range_group_name = getattr(request.user, '_ip_range_group_name', None)
if ip_range_group_name:
ip_range_group = Group.objects.get(name=ip_range_group_name)
ip_range_group_checker = ObjectPermissionChecker(ip_range_group)

permitted_documents = []
for document in documents:
# we show all documents for which the requested group has edit permissions
# e.g. if you request FSR documents, all documents for which the FSR group has edit rights will be shown
if not group_checker.has_perm(document.edit_permission_name, document):
continue
# we only show documents for which the user has view permissions
if not user_checker.has_perm(Document.get_view_permission(), document) and (not ip_range_group_name or not ip_range_group_checker.has_perm(Document.get_view_permission(), document)):
continue
permitted_documents.append(document)

return permitted_documents, own_group
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.15 on 2018-08-13 19:06
from __future__ import unicode_literals

from django.db import migrations


class Migration(migrations.Migration):

replaces = [('information_pages', '0004_auto_20180813_2050'), ('information_pages', '0005_auto_20180813_2102')]

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a squashed migration although that's not necessary because both 0004 and 0005 didn't exist before. please create a plain new migration instead.

dependencies = [
('information_pages', '0003_auto_20180201_2301'),
]

operations = [
migrations.AlterModelOptions(
name='informationdocument',
options={'base_manager_name': 'objects', 'permissions': (('view_informationdocument', 'User/Group is allowed to view that document'),), 'verbose_name': 'Information document', 'verbose_name_plural': 'Information documents'},
),
]
2 changes: 1 addition & 1 deletion _1327/main/templates/menu_item_edit.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
{% endblock %}

{% block content %}
<form method="post" class="form-horizontal" role="form" enctype="multipart/form-data">
<form method="post" class="form-horizontal" role="form" enctype="multipart/form-data" id="menu_item_edit">
{% bootstrap_form form layout='horizontal' %}
{% csrf_token %}
{% if formset %}
Expand Down
55 changes: 55 additions & 0 deletions _1327/main/templates/searched_list.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{% extends 'base_with_sidebar.html' %}
{% load i18n %}

{% block title %}
{% blocktrans count counter=2 %}Documents{% plural %}Documents{% endblocktrans %}
{% endblock %}

{% block sidebar %}
<div class="toc hidden-print">
<ul>
{% for type, documents in searched_documents %}
<li><a href="#type{{ type }}">{{ type }}</a></li>
{% endfor %}
</ul>
</div>
{% endblock %}

{% block content %}
{% for type, documents in searched_documents %}
<h3 name="type{{ type }}">{{ type }}</h3>
<table class="table table-striped">
{% for document, lines in documents %}
<tr>
<td class="width: 95%;">
<a href="{{ document.get_view_url }}">{{ document.title}} {% if document.date %}({{ document.date| date:"d.m.Y" }}){% endif %}</a>
<ul class="minutes-lines">
{% for line in lines %}
<li>{{ line }}</li>
{% endfor %}
</ul>
</td>
<td style="width: 5%; text-align: center;">
{% if document.attachments.count > 0 %}
<span class="text-gray" data-toggle="tooltip" data-placement="left" data-container="body" title="{{ document.attachments.all|join:', ' }}">
<span class="glyphicon glyphicon-file" aria-hidden="true"></span>
</span>
{% endif %}
</td>
</tr>
{% endfor %}
</table>
{% empty %}
{% block searched_documentsempty %}
<em>
{% blocktrans %}No documents containing "{{ phrase }}" found.{% endblocktrans %}
</em>
{% url "login" as anchor_url %}
{% if not user.is_authenticated %}
<em>
{% blocktrans with anchor='<a href="'|add:anchor_url|add:'">'|safe anchor_end='</a>'|safe %}You might have to {{ anchor }} login {{ anchor_end }} first.{% endblocktrans %}
</em>
{% endif %}
{% endblock %}
{% endfor %}
{% endblock %}
154 changes: 142 additions & 12 deletions _1327/main/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from _1327.information_pages.models import InformationDocument
from _1327.main.utils import find_root_menu_items
from _1327.minutes.models import MinutesDocument
from _1327.polls.models import Poll
from _1327.user_management.models import UserProfile
from .context_processors import mark_selected
from .models import MenuItem
Expand Down Expand Up @@ -105,7 +106,7 @@ def test_create_menu_item_as_superuser_no_document_and_link(self):
self.assertEqual(response.status_code, 200)
self.assertIn("Link", response.body.decode('utf-8'))

form = response.form
form = response.forms['menu_item_edit']
form['group'].select(text=self.staff_group.name)

response = form.submit()
Expand All @@ -118,7 +119,7 @@ def test_create_menu_item_as_superuser_document_and_link(self):
document = mommy.make(InformationDocument)

response = self.app.get(reverse('menu_item_create'), user=self.root_user)
form = response.form
form = response.forms['menu_item_edit']
form['link'] = 'polls:index'
form['document'].select(text=document.title)
form['group'].select(text=self.staff_group.name)
Expand All @@ -132,7 +133,7 @@ def test_create_menu_item_as_superuser_with_link(self):
menu_item_count = MenuItem.objects.count()

response = self.app.get(reverse('menu_item_create'), user=self.root_user)
form = response.form
form = response.forms['menu_item_edit']
form['title'] = 'test title'
form['link'] = 'polls:index'
form['group'].select(text=self.staff_group.name)
Expand All @@ -146,7 +147,7 @@ def test_create_menu_item_as_superuser_with_link_and_param(self):
menu_item_count = MenuItem.objects.count()

response = self.app.get(reverse('menu_item_create'), user=self.root_user)
form = response.form
form = response.forms['menu_item_edit']
form['title'] = 'test title'
form['link'] = 'minutes:list?groupid={}'.format(self.staff_group.id)
form['group'].select(text=self.staff_group.name)
Expand All @@ -160,7 +161,7 @@ def test_create_menu_item_as_superuser_wrong_link(self):
menu_item_count = MenuItem.objects.count()

response = self.app.get(reverse('menu_item_create'), user=self.root_user)
form = response.form
form = response.forms['menu_item_edit']
form['title'] = 'test title'
form['link'] = 'polls:index?kekse?kekse2'
form['group'].select(text=self.staff_group.name)
Expand All @@ -174,7 +175,7 @@ def test_create_menu_item_as_superuser_wrong_link_2(self):
menu_item_count = MenuItem.objects.count()

response = self.app.get(reverse('menu_item_create'), user=self.root_user)
form = response.form
form = response.forms['menu_item_edit']
form['title'] = 'test title'
form['link'] = 'www.example.com'
form['group'].select(text=self.staff_group.name)
Expand All @@ -189,7 +190,7 @@ def test_create_menu_item_as_superuser_with_document(self):
document = mommy.make(InformationDocument)

response = self.app.get(reverse('menu_item_create'), user=self.root_user)
form = response.form
form = response.forms['menu_item_edit']
form['title'] = 'test title'
form['document'].select(text=document.title)
form['group'].select(text=self.staff_group.name)
Expand All @@ -208,7 +209,7 @@ def test_create_menu_item_as_normal_user_no_document_and_link(self):
menu_item_count = MenuItem.objects.count()

response = self.app.get(reverse('menu_item_create'), user=self.user)
form = response.form
form = response.forms['menu_item_edit']
form['group'].select(text=self.staff_group.name)

response = form.submit()
Expand All @@ -221,7 +222,7 @@ def test_create_menu_item_as_normal_user_with_document(self):
document = mommy.make(InformationDocument)

response = self.app.get(reverse('menu_item_create'), user=self.user)
form = response.form
form = response.forms['menu_item_edit']
form['title'] = 'test title'
form['document'].select(text=document.title)
form['group'].select(text=self.staff_group.name)
Expand All @@ -237,7 +238,7 @@ def test_create_menu_item_as_normal_user_with_document_without_parent(self):
document = mommy.make(InformationDocument)

response = self.app.get(reverse('menu_item_create'), user=self.user)
form = response.form
form = response.forms['menu_item_edit']
form['title'] = 'test title'
form['document'].select(text=document.title)
form['group'].select(text=self.staff_group.name)
Expand All @@ -253,7 +254,7 @@ def test_create_menu_wrong_group(self):
group = mommy.make(Group)

response = self.app.get(reverse('menu_item_create'), user=self.user)
form = response.form
form = response.forms['menu_item_edit']
form['title'] = 'test title'
form['document'].select(text=document.title)
form['group'].force_value(group.id)
Expand Down Expand Up @@ -401,7 +402,7 @@ def test_menu_item_edit(self):

original_menu_item = self.sub_item

form = response.form
form = response.forms['menu_item_edit']
form['title'] = 'Lorem Ipsum'
form['document'] = document.pk

Expand Down Expand Up @@ -522,3 +523,132 @@ def test_remind_users_about_due_unpublished_minutes_documents(self):

management.call_command('send_reminders')
self.assertEqual(len(mail.outbox), 2)


class TestSearch(WebTest):
csrf_checks = False

@classmethod
def setUpTestData(cls):
cls.user = mommy.make(UserProfile, is_superuser=True)

text1 = "both notO \n Case notB notO \n two notB notO \n two lines notB notO \n all three notB notO"
text2 = "in both minutes notO \n one notB \n substring notB notO \n all three notB notO"
text3 = "all three notB notO"
text4 = "<script>alert(Hello);</script> something else"
text5 = "this will never show up notB notO"

cls.minutes_document = mommy.make(MinutesDocument, text=text1, title="TestMinute")
cls.poll = mommy.make(Poll, text=text2, title="TestPoll")
cls.information_document = mommy.make(InformationDocument, text=text3, title="TestInformationDocument")
cls.minutes_document_w_script = mommy.make(MinutesDocument, text=text4, title="TestMinuteWithScript")
cls.information_document_never = mommy.make(InformationDocument, text=text5, title="TestInformationDocumentNever")
cls.group = mommy.make(Group)
cls.minutes_document.set_all_permissions(cls.group)
cls.poll.set_all_permissions(cls.group)
cls.information_document.set_all_permissions(cls.group)
cls.minutes_document_w_script.set_all_permissions(cls.group)
cls.information_document_never.set_all_permissions(cls.group)

def test_no_permission(self):
user = mommy.make(UserProfile)
search_string = "both"

response = self.app.get(reverse('index'), user=user)
form = response.forms["general_search"]
form.set('search_phrase', search_string)

response = form.submit()

self.assertIn('No documents containing "both" found.', response)
self.assertNotIn('TestMinute', response)
self.assertNotIn('TestPoll', response)

def test_some_permissions(self):
user = mommy.make(UserProfile)
self.minutes_document.set_all_permissions(user)
search_string = "both"

response = self.app.get(reverse('index'), user=user)
form = response.forms["general_search"]
form.set('search_phrase', search_string)

response = form.submit()

self.assertIn('TestMinute', response)
self.assertNotIn('TestPoll', response)

def test_all_types_of_documents(self):
search_string = "all three"

response = self.app.get(reverse('index'), user=self.user)

form = response.forms["general_search"]
form.set('search_phrase', search_string)

response = form.submit()

self.assertIn('TestMinute', response)
self.assertIn('TestPoll', response)
self.assertIn('TestInformationDocument', response)
self.assertNotIn('TestInformationDocumentNever', response)

def test_case_insensitive_result(self):
search_string = "case"

response = self.app.get(reverse('index'), user=self.user)

form = response.forms["general_search"]
form.set('search_phrase', search_string)

response = form.submit()

self.assertIn('TestMinute', response)
self.assertNotIn('TestPoll', response)
self.assertNotIn('TestInformationDocument', response)
self.assertNotIn('TestInformationDocumentNever', response)

self.assertIn('<b>Case</b> notB notO', response)

def test_substring_result(self):
search_string = "bstrin"

response = self.app.get(reverse('index'), user=self.user)

form = response.forms["general_search"]
form.set('search_phrase', search_string)

response = form.submit()

self.assertIn('TestPoll', response)
self.assertNotIn('TestMinute', response)
self.assertNotIn('TestInformationDocument', response)
self.assertNotIn('TestInformationDocumentNever', response)

self.assertIn('su<b>bstrin</b>g notB notO', response)

def test_nothing_found_message(self):
search_string = "not in the minutes"

response = self.app.get(reverse('index'), user=self.user)

form = response.forms["general_search"]
form.set('search_phrase', search_string)

response = form.submit()

self.assertIn('No documents containing "not in the minutes" found.', response.body.decode('utf-8'))
self.assertNotIn('notB', response)
self.assertNotIn('notO', response)

def test_correct_escaping(self):
search_string = "<script>alert(Hello);</script>"

response = self.app.get(reverse('index'), user=self.user)

form = response.forms["general_search"]
form.set('search_phrase', search_string)

response = form.submit()

self.assertIn('<b>&lt;script&gt;alert(Hello);&lt;/script&gt;</b> something else', response.body.decode('utf-8'))
Loading