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 1 commit
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
9 changes: 9 additions & 0 deletions _1327/documents/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ def get_hash():

DOCUMENT_LINK_REGEX = r'\[(?P<title>[^\[]+)\]\(document:(?P<id>\d+)\)'
VIEW_PERMISSION_NAME = DOCUMENT_VIEW_PERMISSION_NAME
PLURAL_CONTENT_TYPE_NAME = {
'Information document': _('Information documents'),
'Minutes': _('Minutes'),
'poll': _('Polls')
}

class Meta:
verbose_name = _("Document")
Expand Down Expand Up @@ -103,6 +108,10 @@ def authors(self):
authors.add(version.revision.user)
return authors

def plural_content_type(self):
content_type = ContentType.objects.get_for_model(self)
return Document.PLURAL_CONTENT_TYPE_NAME[str(content_type)]

@classmethod
def generate_new_title(cls):
return _("New Page from {}").format(str(datetime.now()))
Expand Down
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 group permissions
ip_range_group_name = request.user._ip_range_group_name if hasattr(request.user, '_ip_range_group_name') else 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 d 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(d.edit_permission_name, d):
continue
# we only show documents for which the user has view permissions
if not user_checker.has_perm(Document.get_view_permission(), d) and (not ip_range_group_name or not ip_range_group_checker.has_perm(Document.get_view_permission(), d)):
continue
permitted_documents.append(d)

return permitted_documents, own_group
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 id="type{{ type }}">{{ type }}</h3>
<table class="table table-striped">
{% for document, lines in documents %}
<tr>
<td style="width: 55%;">
<a href="{{ document.get_view_url }}">{{ document.title}} {{ document.date| date:"d.m.Y" }}</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 %}
51 changes: 51 additions & 0 deletions _1327/main/views.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import json

import re

from django.conf import settings
from django.contrib import messages
from django.contrib.auth.models import Group
Expand All @@ -9,6 +11,8 @@
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, Http404, redirect, render
from django.urls import reverse
from django.utils.html import escape
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
from django.views.decorators.http import require_POST
from guardian.shortcuts import get_objects_for_user
Expand Down Expand Up @@ -170,3 +174,50 @@ def abbreviation_explanation_edit(request):
return redirect('abbreviation_explanation')
else:
return render(request, "abbreviation_explanation.html", dict(formset=formset))


def search(request):
search_text = None
if request.method == 'POST':
search_text = request.POST.get('search_phrase')

if not search_text:
return render(request, "searched_list.html", {
'searched_documents': [],
'phrase': "",
})

# filter for documents that contain the searched for string
documents = Document.objects.filter(text__icontains=search_text)

# find documents and lines containing the searched for string
result = {}
for d in documents:
# check if the user has permission to view the document
if not (request.user.has_perm(d.view_permission_name) or request.user.has_perm(d.view_permission_name, d)):
continue
# find lines with the searched for string and mark it as bold
lines = d.text.splitlines()
lines = [
mark_safe(
re.sub(
r'(' + re.escape(escape(search_text)) + ')',
r'<b>\1</b>', escape(line),
flags=re.IGNORECASE
)
)
for line in lines if (line.casefold().find(search_text.casefold()) != -1)
]
content_type = d.plural_content_type()

if content_type not in result:
result[content_type] = []

result[content_type].append((d, lines))

if 'Minutes' in result:
result['Minutes'].sort(key=lambda minute: minute[0].date, reverse=True)
return render(request, "searched_list.html", {
'searched_documents': result.items(),
'phrase': search_text,
})
47 changes: 4 additions & 43 deletions _1327/minutes/views.py
Original file line number Diff line number Diff line change
@@ -1,53 +1,14 @@
import re

from django.contrib.auth.models import Group
from django.core.exceptions import ObjectDoesNotExist
from django.shortcuts import Http404, redirect, render
from django.shortcuts import redirect, render
from django.utils.html import escape
from django.utils.safestring import mark_safe
from guardian.core import ObjectPermissionChecker

from _1327.documents.utils import get_permitted_documents
from _1327.minutes.forms import SearchForm
from _1327.minutes.models import MinutesDocument


def get_permitted_minutes(minutes, 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(minutes)

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

# Prefetch ip group permissions
ip_range_group_name = request.user._ip_range_group_name if hasattr(request.user, '_ip_range_group_name') else 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_minutes = []
for m in minutes:
# we show all documents for which the requested group has edit permissions
# e.g. if you request FSR minutes, all minutes for which the FSR group has edit rights will be shown
if not group_checker.has_perm(m.edit_permission_name, m):
continue
# we only show documents for which the user has view permissions
if not user_checker.has_perm(MinutesDocument.get_view_permission(), m) and (not ip_range_group_name or not ip_range_group_checker.has_perm(MinutesDocument.get_view_permission(), m)):
continue
permitted_minutes.append(m)

return permitted_minutes, own_group


def search(request, groupid):
if request.method == 'POST':
form = SearchForm(request.POST)
Expand All @@ -61,7 +22,7 @@ def search(request, groupid):
minutes = MinutesDocument.objects.filter(text__icontains=search_text).prefetch_related('labels').order_by('-date')

# only show permitted documents
minutes, own_group = get_permitted_minutes(minutes, request, groupid)
minutes, own_group = get_permitted_documents(minutes, request, groupid)

# find lines containing the searched for string
result = {}
Expand Down Expand Up @@ -95,7 +56,7 @@ def search(request, groupid):

def list(request, groupid):
minutes = MinutesDocument.objects.all().prefetch_related('labels').order_by('-date')
minutes, own_group = get_permitted_minutes(minutes, request, groupid)
minutes, own_group = get_permitted_documents(minutes, request, groupid)

result = {}
for m in minutes:
Expand Down
24 changes: 24 additions & 0 deletions _1327/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,18 @@
</ul>
</li>
{% endif %}
{# Search form #}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

please add some javascript that automatically focuses the search text field on opening the popover

<li class ="pull-right">
<a href="#" id="popover" data-placement="bottom">
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

<span class="glyphicon glyphicon-search" aria-hidden="true"></span>
</a>
<div id="popover-content" class="hide">
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

the indentation here is wrong

<form action='/search' method='post'>
{% csrf_token %}
<input type='text' name='search_phrase'>
</form>
</div>
</li>
</ul>
</nav>

Expand Down Expand Up @@ -244,5 +256,17 @@
$(this).parent().removeClass('menu-hover');
});
});

// initialize popovers
$(function () {
$('[data-toggle="popover"]').popover()
});

$('#popover').popover({
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

can you combine these two method calls into one?

html : true,
content: function() {
return $("#popover-content").html();
}
});
</script>
{% endblock %}
2 changes: 2 additions & 0 deletions _1327/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
url(r"^menu_item_delete$", main_views.menu_item_delete, name="menu_item_delete"),
url(r"^menu_item/update_order$", main_views.menu_items_update_order, name="menu_items_update_order"),

url(r"search$", main_views.search, name='search'),

url(r'^shortlinks$', shortlinks_views.shortlinks_index, name='shortlinks_index'),
url(r'^shortlink/create$', shortlinks_views.shortlink_create, name='shortlink_create'),
url(r'^shortlink/delete$', shortlinks_views.shortlink_delete, name='shortlink_delete'),
Expand Down