diff --git a/mayan/apps/document_indexing/apps.py b/mayan/apps/document_indexing/apps.py index fed0dadcaa..97b176a16b 100644 --- a/mayan/apps/document_indexing/apps.py +++ b/mayan/apps/document_indexing/apps.py @@ -6,6 +6,10 @@ from django.apps import apps from django.db.models.signals import post_save, post_delete from django.utils.translation import ugettext_lazy as _ +from acls import ModelPermission +from acls.links import link_acl_list +from acls.permissions import permission_acl_edit, permission_acl_view + from common import ( MayanAppConfig, menu_facet, menu_main, menu_object, menu_secondary, menu_setup, menu_tools @@ -29,6 +33,11 @@ from .links import ( link_template_node_create, link_template_node_delete, link_template_node_edit ) +from .permissions import ( + permission_document_indexing_create, permission_document_indexing_delete, + permission_document_indexing_edit, permission_document_indexing_view, + permission_document_indexing_rebuild +) from .widgets import get_instance_link, index_instance_item_link, node_level @@ -51,6 +60,7 @@ class DocumentIndexingApp(MayanAppConfig): ) DocumentIndexInstanceNode = self.get_model('DocumentIndexInstanceNode') + Index = self.get_model('Index') IndexInstance = self.get_model('IndexInstance') IndexInstanceNode = self.get_model('IndexInstanceNode') @@ -58,6 +68,16 @@ class DocumentIndexingApp(MayanAppConfig): APIEndPoint(app=self, version_string='1') + ModelPermission.register( + model=Index, permissions=( + permission_acl_edit, permission_acl_view, + permission_document_indexing_create, + permission_document_indexing_delete, + permission_document_indexing_edit, + permission_document_indexing_view, + ) + ) + Package(label='Django MPTT', license_text=''' Django MPTT ----------- @@ -179,7 +199,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. menu_object.bind_links( links=( link_index_setup_edit, link_index_setup_view, - link_index_setup_document_types, link_index_setup_delete + link_index_setup_document_types, link_acl_list, + link_index_setup_delete ), sources=(Index,) ) menu_object.bind_links( diff --git a/mayan/apps/document_indexing/links.py b/mayan/apps/document_indexing/links.py index d5b622393f..2fc8b50622 100644 --- a/mayan/apps/document_indexing/links.py +++ b/mayan/apps/document_indexing/links.py @@ -7,9 +7,8 @@ from navigation import Link from .permissions import ( permission_document_indexing_create, permission_document_indexing_edit, - permission_document_indexing_delete, - permission_document_indexing_rebuild_indexes, - permission_document_indexing_setup, permission_document_indexing_view + permission_document_indexing_delete, permission_document_indexing_rebuild, + permission_document_indexing_view ) @@ -18,23 +17,17 @@ def is_not_root_node(context): link_document_index_list = Link( - icon='fa fa-list-ul', permissions=(permission_document_view,), - text=_('Indexes'), view='indexing:document_index_list', args='object.pk' -) -link_index_list = Link( - permissions=(permission_document_indexing_view,), text=_('Index list'), - view='indexing:index_list' + icon='fa fa-list-ul', text=_('Indexes'), + view='indexing:document_index_list', args='object.pk' ) link_index_main_menu = Link( icon='fa fa-list-ul', text=_('Indexes'), view='indexing:index_list' ) link_index_setup = Link( - icon='fa fa-list-ul', permissions=(permission_document_indexing_setup,), - text=_('Indexes'), view='indexing:index_setup_list' + icon='fa fa-list-ul', text=_('Indexes'), view='indexing:index_setup_list' ) link_index_setup_list = Link( - permissions=(permission_document_indexing_setup,), text=_('Indexes'), - view='indexing:index_setup_list' + text=_('Indexes'), view='indexing:index_setup_list' ) link_index_setup_create = Link( permissions=(permission_document_indexing_create,), text=_('Create index'), @@ -50,7 +43,7 @@ link_index_setup_delete = Link( args='resolved_object.pk' ) link_index_setup_view = Link( - permissions=(permission_document_indexing_setup,), text=_('Tree template'), + permissions=(permission_document_indexing_edit,), text=_('Tree template'), view='indexing:index_setup_view', args='resolved_object.pk' ) link_index_setup_document_types = Link( @@ -62,23 +55,18 @@ link_rebuild_index_instances = Link( description=_( 'Deletes and creates from scratch all the document indexes.' ), - permissions=(permission_document_indexing_rebuild_indexes,), + permissions=(permission_document_indexing_rebuild,), text=_('Rebuild indexes'), view='indexing:rebuild_index_instances' ) link_template_node_create = Link( - permissions=(permission_document_indexing_setup,), text=_('New child node'), view='indexing:template_node_create', args='resolved_object.pk' ) link_template_node_edit = Link( - condition=is_not_root_node, - permissions=(permission_document_indexing_setup,), text=_('Edit'), + condition=is_not_root_node, text=_('Edit'), view='indexing:template_node_edit', args='resolved_object.pk' ) link_template_node_delete = Link( - condition=is_not_root_node, permissions=( - permission_document_indexing_setup, - ), - tags='dangerous', text=_('Delete'), view='indexing:template_node_delete', - args='resolved_object.pk' + condition=is_not_root_node, tags='dangerous', text=_('Delete'), + view='indexing:template_node_delete', args='resolved_object.pk' ) diff --git a/mayan/apps/document_indexing/models.py b/mayan/apps/document_indexing/models.py index 744d94767e..e6c983813c 100644 --- a/mayan/apps/document_indexing/models.py +++ b/mayan/apps/document_indexing/models.py @@ -136,7 +136,7 @@ class IndexTemplateNode(MPTTModel): def __str__(self): if self.is_root_node(): - return ugettext('<%s Root>') % self.index + return ugettext('Root') else: return self.expression diff --git a/mayan/apps/document_indexing/permissions.py b/mayan/apps/document_indexing/permissions.py index ab735647a8..4fa5ad4253 100644 --- a/mayan/apps/document_indexing/permissions.py +++ b/mayan/apps/document_indexing/permissions.py @@ -6,9 +6,6 @@ from permissions import PermissionNamespace namespace = PermissionNamespace('document_indexing', _('Indexing')) -permission_document_indexing_setup = namespace.add_permission( - name='document_index_setup', label=_('Configure document indexes') -) permission_document_indexing_create = namespace.add_permission( name='document_index_create', label=_('Create new document indexes') ) @@ -21,6 +18,6 @@ permission_document_indexing_delete = namespace.add_permission( permission_document_indexing_view = namespace.add_permission( name='document_index_view', label=_('View document indexes') ) -permission_document_indexing_rebuild_indexes = namespace.add_permission( +permission_document_indexing_rebuild = namespace.add_permission( name='document_rebuild_indexes', label=_('Rebuild document indexes') ) diff --git a/mayan/apps/document_indexing/tests/test_views.py b/mayan/apps/document_indexing/tests/test_views.py new file mode 100644 index 0000000000..576f2f73db --- /dev/null +++ b/mayan/apps/document_indexing/tests/test_views.py @@ -0,0 +1,114 @@ +from __future__ import absolute_import, unicode_literals + +from documents.permissions import permission_document_view +from documents.tests.test_views import GenericDocumentViewTestCase +from user_management.tests import ( + TEST_USER_USERNAME, TEST_USER_PASSWORD +) + +from ..models import Index +from ..permissions import ( + permission_document_indexing_create, permission_document_indexing_delete, + permission_document_indexing_edit, permission_document_indexing_view +) + +TEST_INDEX_LABEL = 'test label' +TEST_INDEX_EDITED_LABEL = 'test edited label' +TEST_INDEX_SLUG = 'test_slug' + + +class IndexViewTestCase(GenericDocumentViewTestCase): + def test_index_create_view_no_permission(self): + self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD) + + response = self.post( + 'indexing:index_setup_create', data={ + 'label': TEST_INDEX_LABEL, 'slug': TEST_INDEX_SLUG + } + ) + + self.assertEquals(response.status_code, 403) + self.assertEqual(Index.objects.count(), 0) + + def test_index_create_view_with_permission(self): + self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD) + + self.role.permissions.add( + permission_document_indexing_create.stored_permission + ) + + response = self.post( + 'indexing:index_setup_create', data={ + 'label': TEST_INDEX_LABEL, 'slug': TEST_INDEX_SLUG + }, follow=True + ) + + self.assertContains(response, text='created', status_code=200) + self.assertEqual(Index.objects.count(), 1) + self.assertEqual(Index.objects.first().label, TEST_INDEX_LABEL) + + def test_index_delete_view_no_permission(self): + self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD) + + index = Index.objects.create( + label=TEST_INDEX_LABEL, slug=TEST_INDEX_SLUG + ) + + response = self.post('indexing:index_setup_delete', args=(index.pk,)) + self.assertEqual(response.status_code, 403) + self.assertEqual(Index.objects.count(), 1) + + def test_index_delete_view_with_permission(self): + self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD) + + self.role.permissions.add( + permission_document_indexing_delete.stored_permission + ) + + index = Index.objects.create( + label=TEST_INDEX_LABEL, slug=TEST_INDEX_SLUG + ) + + response = self.post( + 'indexing:index_setup_delete', args=(index.pk,), follow=True + ) + + self.assertContains(response, text='deleted', status_code=200) + self.assertEqual(Index.objects.count(), 0) + + def test_index_edit_view_no_permission(self): + self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD) + + index = Index.objects.create( + label=TEST_INDEX_LABEL, slug=TEST_INDEX_SLUG + ) + + response = self.post( + 'indexing:index_setup_edit', args=(index.pk,), data={ + 'label': TEST_INDEX_EDITED_LABEL, 'slug': TEST_INDEX_SLUG + } + ) + self.assertEqual(response.status_code, 403) + index = Index.objects.get(pk=index.pk) + self.assertEqual(index.label, TEST_INDEX_LABEL) + + def test_index_edit_view_with_permission(self): + self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD) + + self.role.permissions.add( + permission_document_indexing_edit.stored_permission + ) + + index = Index.objects.create( + label=TEST_INDEX_LABEL, slug=TEST_INDEX_SLUG + ) + + response = self.post( + 'indexing:index_setup_edit', args=(index.pk,), data={ + 'label': TEST_INDEX_EDITED_LABEL, 'slug': TEST_INDEX_SLUG + }, follow=True + ) + + index = Index.objects.get(pk=index.pk) + self.assertContains(response, text='update', status_code=200) + self.assertEqual(index.label, TEST_INDEX_EDITED_LABEL) diff --git a/mayan/apps/document_indexing/urls.py b/mayan/apps/document_indexing/urls.py index d7af8cd1d0..1aa8306b90 100644 --- a/mayan/apps/document_indexing/urls.py +++ b/mayan/apps/document_indexing/urls.py @@ -11,11 +11,12 @@ from .views import ( DocumentIndexNodeListView, IndexInstanceNodeView, IndexListView, RebuildIndexesConfirmView, SetupIndexDocumentTypesView, SetupIndexCreateView, SetupIndexDeleteView, SetupIndexEditView, - SetupIndexListView, SetupIndexTreeTemplateListView, TemplateNodeDeleteView + SetupIndexListView, SetupIndexTreeTemplateListView, TemplateNodeCreateView, + TemplateNodeDeleteView, TemplateNodeEditView ) urlpatterns = patterns( - 'document_indexing.views', + '', url( r'^setup/index/list/$', SetupIndexListView.as_view(), name='index_setup_list' @@ -42,12 +43,12 @@ urlpatterns = patterns( name='index_setup_document_types' ), url( - r'^setup/template/node/(?P\d+)/create/child/$', - 'template_node_create', name='template_node_create' + r'^setup/template/node/(?P\d+)/create/child/$', + TemplateNodeCreateView.as_view(), name='template_node_create' ), url( - r'^setup/template/node/(?P\d+)/edit/$', 'template_node_edit', - name='template_node_edit' + r'^setup/template/node/(?P\d+)/edit/$', + TemplateNodeEditView.as_view(), name='template_node_edit' ), url( r'^setup/template/node/(?P\d+)/delete/$', diff --git a/mayan/apps/document_indexing/views.py b/mayan/apps/document_indexing/views.py index fb0da0beb7..b510a81fa8 100644 --- a/mayan/apps/document_indexing/views.py +++ b/mayan/apps/document_indexing/views.py @@ -26,9 +26,8 @@ from .models import ( ) from .permissions import ( permission_document_indexing_create, permission_document_indexing_delete, - permission_document_indexing_edit, - permission_document_indexing_rebuild_indexes, - permission_document_indexing_setup, permission_document_indexing_view + permission_document_indexing_edit, permission_document_indexing_rebuild, + permission_document_indexing_view ) from .tasks import task_do_rebuild_all_indexes from .widgets import node_tree @@ -43,34 +42,10 @@ class SetupIndexCreateView(SingleObjectCreateView): view_permission = permission_document_indexing_create -class SetupIndexListView(SingleObjectListView): - model = Index - view_permission = permission_document_indexing_setup - - def get_extra_context(self): - return { - 'hide_object': True, - 'title': _('Indexes'), - } - - -class SetupIndexEditView(SingleObjectEditView): - fields = ('label', 'slug', 'enabled') - model = Index - post_action_redirect = reverse_lazy('indexing:index_setup_list') - view_permission = permission_document_indexing_edit - - def get_extra_context(self): - return { - 'object': self.get_object(), - 'title': _('Edit index: %s') % self.get_object(), - } - - class SetupIndexDeleteView(SingleObjectDeleteView): model = Index post_action_redirect = reverse_lazy('indexing:index_setup_list') - view_permission = permission_document_indexing_delete + object_permission = permission_document_indexing_delete def get_extra_context(self): return { @@ -79,8 +54,82 @@ class SetupIndexDeleteView(SingleObjectDeleteView): } +class SetupIndexEditView(SingleObjectEditView): + fields = ('label', 'slug', 'enabled') + model = Index + post_action_redirect = reverse_lazy('indexing:index_setup_list') + object_permission = permission_document_indexing_edit + + def get_extra_context(self): + return { + 'object': self.get_object(), + 'title': _('Edit index: %s') % self.get_object(), + } + + +class SetupIndexListView(SingleObjectListView): + model = Index + object_permission = permission_document_indexing_view + + def get_extra_context(self): + return { + 'hide_object': True, + 'title': _('Indexes'), + } + + +class SetupIndexDocumentTypesView(AssignRemoveView): + decode_content_type = True + left_list_title = _('Available document types') + object_permission = permission_document_indexing_edit + right_list_title = _('Document types linked') + + def add(self, item): + self.get_object().document_types.add(item) + + def get_document_queryset(self): + queryset = DocumentType.objects.all() + + try: + Permission.check_permissions( + self.request.user, (permission_document_view,) + ) + except PermissionDenied: + queryset = AccessControlList.objects.filter_by_access( + permission_document_view, self.request.user, queryset + ) + + return queryset + + def get_extra_context(self): + return { + 'object': self.get_object(), + 'title': _( + 'Document types linked to index: %s' + ) % self.get_object() + } + + def get_object(self): + return get_object_or_404(Index, pk=self.kwargs['pk']) + + def left_list(self): + return AssignRemoveView.generate_choices( + self.get_document_queryset().exclude( + id__in=self.get_object().document_types.all() + ) + ) + + def remove(self, item): + self.get_object().document_types.remove(item) + + def right_list(self): + return AssignRemoveView.generate_choices( + self.get_document_queryset() & self.get_object().document_types.all() + ) + + class SetupIndexTreeTemplateListView(SingleObjectListView): - view_permission = permission_document_indexing_setup + object_permission = permission_document_indexing_edit def get_index(self): return get_object_or_404(Index, pk=self.kwargs['pk']) @@ -98,116 +147,46 @@ class SetupIndexTreeTemplateListView(SingleObjectListView): 'title': _('Tree template nodes for index: %s') % self.get_index(), } +class TemplateNodeCreateView(SingleObjectCreateView): + form_class = IndexTemplateNodeForm + model = IndexTemplateNode -class SetupIndexDocumentTypesView(AssignRemoveView): - decode_content_type = True - object_permission = permission_document_indexing_edit - left_list_title = _('Available document types') - right_list_title = _('Document types linked') - - def add(self, item): - self.get_object().document_types.add(item) - - def get_object(self): - return get_object_or_404(Index, pk=self.kwargs['pk']) - - def left_list(self): - return AssignRemoveView.generate_choices( - DocumentType.objects.exclude( - pk__in=self.get_object().document_types.all() + def dispatch(self, request, *args, **kwargs): + try: + Permission.check_permissions( + request.user, (permission_document_indexing_edit,) + ) + except PermissionDenied: + AccessControlList.objects.check_access( + permission_document_indexing_edit, request.user, + self.get_parent_node().index ) - ) - def right_list(self): - return AssignRemoveView.generate_choices( - self.get_object().document_types.all() - ) - - def remove(self, item): - self.get_object().document_types.remove(item) + return super( + TemplateNodeCreateView, self + ).dispatch(request, *args, **kwargs) def get_extra_context(self): return { - 'object': self.get_object(), - 'title': _( - 'Document types linked to index: %s' - ) % self.get_object() + 'index': self.get_parent_node().index, + 'navigation_object_list': ('index',), + 'title': _('Create child node of: %s') % self.get_parent_node(), } + def get_initial(self): + parent_node = self.get_parent_node() + return { + 'index': parent_node.index, 'parent': parent_node + } -# Node views -def template_node_create(request, parent_pk): - parent_node = get_object_or_404(IndexTemplateNode, pk=parent_pk) - - try: - Permission.check_permissions( - request.user, (permission_document_indexing_edit,) - ) - except PermissionDenied: - AccessControlList.objects.check_access( - permission_document_indexing_edit, request.user, parent_node.index - ) - - if request.method == 'POST': - form = IndexTemplateNodeForm(request.POST) - if form.is_valid(): - node = form.save() - messages.success( - request, _('Index template node created successfully.') - ) - return HttpResponseRedirect( - reverse('indexing:index_setup_view', args=(node.index.pk,)) - ) - else: - form = IndexTemplateNodeForm( - initial={'index': parent_node.index, 'parent': parent_node} - ) - - return render_to_response('appearance/generic_form.html', { - 'form': form, - 'index': parent_node.index, - 'navigation_object_list': ('index',), - 'title': _('Create child node'), - }, context_instance=RequestContext(request)) - - -def template_node_edit(request, node_pk): - node = get_object_or_404(IndexTemplateNode, pk=node_pk) - - try: - Permission.check_permissions( - request.user, (permission_document_indexing_edit,) - ) - except PermissionDenied: - AccessControlList.objects.check_access( - permission_document_indexing_edit, request.user, node.index - ) - - if request.method == 'POST': - form = IndexTemplateNodeForm(request.POST, instance=node) - if form.is_valid(): - form.save() - messages.success( - request, _('Index template node edited successfully') - ) - return HttpResponseRedirect( - reverse('indexing:index_setup_view', args=(node.index.pk,)) - ) - else: - form = IndexTemplateNodeForm(instance=node) - - return render_to_response('appearance/generic_form.html', { - 'form': form, - 'index': node.index, - 'navigation_object_list': ('index', 'node'), - 'node': node, - 'title': _('Edit index template node: %s') % node, - }, context_instance=RequestContext(request)) + def get_parent_node(self): + return get_object_or_404(IndexTemplateNode, pk=self.kwargs['pk']) class TemplateNodeDeleteView(SingleObjectDeleteView): model = IndexTemplateNode - view_permission = permission_document_indexing_edit + object_permission = permission_document_indexing_edit + object_permission_related = 'index' def get_extra_context(self): return { @@ -225,6 +204,28 @@ class TemplateNodeDeleteView(SingleObjectDeleteView): ) +class TemplateNodeEditView(SingleObjectEditView): + form_class = IndexTemplateNodeForm + model = IndexTemplateNode + object_permission = permission_document_indexing_edit + object_permission_related = 'index' + + def get_extra_context(self): + return { + 'index': self.get_object().index, + 'navigation_object_list': ('index', 'node'), + 'node': self.get_object(), + 'title': _( + 'Edit the index template node: %s?' + ) % self.get_object(), + } + + def get_post_action_redirect(self): + return reverse( + 'indexing:index_setup_view', args=(self.get_object().index.pk,) + ) + + class IndexListView(SingleObjectListView): object_permission = permission_document_indexing_view queryset = IndexInstance.objects.filter(enabled=True) @@ -341,7 +342,7 @@ class RebuildIndexesConfirmView(ConfirmView): 'message': _('On large databases this operation may take some time to execute.'), 'title': _('Rebuild all indexes?'), } - view_permission = permission_document_indexing_rebuild_indexes + view_permission = permission_document_indexing_rebuild def get_post_action_redirect(self): return reverse('common:tools_list') diff --git a/mayan/apps/document_indexing/widgets.py b/mayan/apps/document_indexing/widgets.py index 7c684dfc29..70d9fdd16a 100644 --- a/mayan/apps/document_indexing/widgets.py +++ b/mayan/apps/document_indexing/widgets.py @@ -49,10 +49,9 @@ def node_level(node): return mark_safe( ''.join( [ - '     ' * ( - getattr(node, node._mptt_meta.level_attr) - 1 - ), '' if node.is_root_node() else ' ', - ugettext('Root') if node.is_root_node() else unicode(node) + '     ' * node.get_level(), + '' if node.is_root_node() else ' ', + unicode(node) ] ) )