diff --git a/HISTORY.rst b/HISTORY.rst index 9a5ce21641..2bf1504909 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -128,6 +128,7 @@ - Support for proxy model permission inheritance. Proxy models now get the permission inheritance from their base models. - Update common.http.URL to allow passing a query dictionary. +- Add the document template sandbox feature. 3.2.9 (2019-11-03) ================== diff --git a/mayan/apps/document_indexing/apps.py b/mayan/apps/document_indexing/apps.py index 560b4cacfc..09b6f3bc8c 100644 --- a/mayan/apps/document_indexing/apps.py +++ b/mayan/apps/document_indexing/apps.py @@ -30,14 +30,14 @@ from .html_widgets import ( get_instance_link, index_instance_item_link, node_level ) from .links import ( - link_document_index_instance_list, link_document_type_index_templates, - link_index_instance_menu, link_index_instance_rebuild, - link_index_template_setup, link_index_template_create, - link_index_template_document_types, link_index_template_delete, - link_index_template_edit, link_index_template_list, - link_index_template_node_tree_view, link_index_instances_rebuild, - link_index_template_node_create, link_index_template_node_delete, - link_index_template_node_edit + link_document_index_instance_list, link_document_template_sandbox, + link_document_type_index_templates, link_index_instance_menu, + link_index_instance_rebuild, link_index_template_setup, + link_index_template_create, link_index_template_document_types, + link_index_template_delete, link_index_template_edit, + link_index_template_list, link_index_template_node_tree_view, + link_index_instances_rebuild, link_index_template_node_create, + link_index_template_node_delete, link_index_template_node_edit ) from .permissions import ( permission_document_indexing_create, permission_document_indexing_delete, @@ -183,7 +183,10 @@ class DocumentIndexingApp(MayanAppConfig): ) menu_facet.bind_links( - links=(link_document_index_instance_list,), sources=(Document,) + links=( + link_document_index_instance_list, + link_document_template_sandbox + ), sources=(Document,) ) menu_list_facet.bind_links( links=(link_document_type_index_templates,), diff --git a/mayan/apps/document_indexing/forms.py b/mayan/apps/document_indexing/forms.py index 790a5e6e2e..935ea3b343 100644 --- a/mayan/apps/document_indexing/forms.py +++ b/mayan/apps/document_indexing/forms.py @@ -12,6 +12,41 @@ from .models import Index, IndexTemplateNode from .permissions import permission_document_indexing_rebuild +class DocumentTemplateSandboxForm(forms.Form): + template = forms.CharField( + help_text=_( + 'The template string to be evaluated. The current document ' + 'is available as the {{ document }} variable.' + ), label=_('Template'), widget=forms.widgets.Textarea( + attrs={'rows': 5} + ) + ) + result = forms.CharField( + help_text=_('Resulting text from the evaluated template.'), + label=_('Result'), required=False, widget=forms.widgets.Textarea( + attrs={'rows': 5} + ) + ) + + def __init__(self, *args, **kwargs): + super(DocumentTemplateSandboxForm, self).__init__(*args, **kwargs) + self.fields['template'].help_text = ' '.join( + [ + force_text(self.fields['template'].help_text), + force_text( + _( + 'Use Django\'s default templating language ' + '(https://docs.djangoproject.com/en/1.11/ref/templates/builtins/)' + ), + ), + '
', + ModelProperty.get_help_text_for( + model=Document, show_name=True + ).replace('\n', '
') + ] + ) + + class IndexTemplateFilteredForm(FilteredSelectionForm): class Meta: allow_multiple = True diff --git a/mayan/apps/document_indexing/icons.py b/mayan/apps/document_indexing/icons.py index 304e854881..bd7f2d71fb 100644 --- a/mayan/apps/document_indexing/icons.py +++ b/mayan/apps/document_indexing/icons.py @@ -3,10 +3,13 @@ from __future__ import absolute_import, unicode_literals from mayan.apps.appearance.classes import Icon from mayan.apps.documents.icons import icon_document_type +icon_index = Icon(driver_name='fontawesome', symbol='list-ul') icon_document_index_instance_list = Icon( driver_name='fontawesome', symbol='list-ul' ) -icon_index = Icon(driver_name='fontawesome', symbol='list-ul') +icon_document_template_sandbox = Icon( + driver_name='fontawesome', symbol='flask' +) icon_document_type_index_templates = icon_index icon_index_level_up = Icon( driver_name='fontawesomecss', css_classes='fa-level-up-alt fa-rotate-90' diff --git a/mayan/apps/document_indexing/links.py b/mayan/apps/document_indexing/links.py index a9441e3e9c..3bbf672f11 100644 --- a/mayan/apps/document_indexing/links.py +++ b/mayan/apps/document_indexing/links.py @@ -2,7 +2,6 @@ from __future__ import unicode_literals from django.utils.translation import ugettext_lazy as _ -from mayan.apps.documents.permissions import permission_document_type_edit from mayan.apps.navigation.classes import Link from mayan.apps.navigation.utils import get_cascade_condition @@ -24,10 +23,17 @@ link_document_index_instance_list = Link( text=_('Indexes'), view='indexing:document_index_list', ) +link_document_template_sandbox = Link( + args='resolved_object.pk', + icon_class_path='mayan.apps.document_indexing.icons.icon_document_template_sandbox', + permissions=(permission_document_indexing_edit,), + text=_('Sandbox'), + view='indexing:document_template_sandbox', +) link_document_type_index_templates = Link( args='resolved_object.pk', icon_class_path='mayan.apps.document_indexing.icons.icon_document_type_index_templates', - permissions=(permission_document_type_edit,), text=_('Index templates'), + permissions=(permission_document_indexing_create,), text=_('Index templates'), view='indexing:document_type_index_templates', ) diff --git a/mayan/apps/document_indexing/urls.py b/mayan/apps/document_indexing/urls.py index 0cdefeeb6c..2172315760 100644 --- a/mayan/apps/document_indexing/urls.py +++ b/mayan/apps/document_indexing/urls.py @@ -8,10 +8,11 @@ from .api_views import ( APIIndexTemplateView, APIIndexView ) from .views import ( - DocumentIndexNodeListView, DocumentTypeIndexesView, IndexInstanceNodeView, - IndexListView, IndexesRebuildView, SetupIndexDocumentTypesView, - SetupIndexCreateView, SetupIndexDeleteView, SetupIndexEditView, - SetupIndexListView, SetupIndexRebuildView, SetupIndexTreeTemplateListView, + DocumentIndexNodeListView, DocumentTemplateSandboxView, + DocumentTypeIndexesView, IndexInstanceNodeView, IndexListView, + IndexesRebuildView, SetupIndexDocumentTypesView, SetupIndexCreateView, + SetupIndexDeleteView, SetupIndexEditView, SetupIndexListView, + SetupIndexRebuildView, SetupIndexTreeTemplateListView, TemplateNodeCreateView, TemplateNodeDeleteView, TemplateNodeEditView ) @@ -83,6 +84,11 @@ urlpatterns_tools = [ regex=r'^instances/rebuild/$', view=IndexesRebuildView.as_view(), name='rebuild_index_instances' ), + url( + regex=r'^documents/(?P\d+)/sandbox/$', + view=DocumentTemplateSandboxView.as_view(), + name='document_template_sandbox' + ), ] urlpatterns = [] diff --git a/mayan/apps/document_indexing/views.py b/mayan/apps/document_indexing/views.py index 384e45064f..2a57a5e196 100644 --- a/mayan/apps/document_indexing/views.py +++ b/mayan/apps/document_indexing/views.py @@ -1,8 +1,11 @@ from __future__ import absolute_import, unicode_literals from django.contrib import messages +from django.http import HttpResponseRedirect from django.shortcuts import get_object_or_404 -from django.template import RequestContext +from django.template import ( + Context, RequestContext, Template, TemplateSyntaxError +) from django.urls import reverse, reverse_lazy from django.utils.html import mark_safe from django.utils.translation import ugettext_lazy as _, ungettext @@ -12,6 +15,8 @@ from mayan.apps.common.generics import ( AddRemoveView, ConfirmView, FormView, SingleObjectCreateView, SingleObjectDeleteView, SingleObjectEditView, SingleObjectListView ) +from mayan.apps.common.http import URL +from mayan.apps.common.mixins import ExternalObjectMixin from mayan.apps.documents.events import event_document_type_edited from mayan.apps.documents.models import Document, DocumentType from mayan.apps.documents.permissions import ( @@ -20,7 +25,9 @@ from mayan.apps.documents.permissions import ( from mayan.apps.documents.views import DocumentListView from .events import event_index_template_edited -from .forms import IndexTemplateFilteredForm, IndexTemplateNodeForm +from .forms import ( + DocumentTemplateSandboxForm, IndexTemplateFilteredForm, IndexTemplateNodeForm +) from .html_widgets import node_tree from .icons import icon_index from .links import link_index_template_create @@ -474,3 +481,47 @@ class IndexesRebuildView(FormView): def get_post_action_redirect(self): return reverse(viewname='common:tools_list') + + +class DocumentTemplateSandboxView(ExternalObjectMixin, FormView): + external_object_class = Document + external_object_permission = permission_document_indexing_create + form_class = DocumentTemplateSandboxForm + + def form_valid(self, form): + path = reverse( + viewname='indexing:document_template_sandbox', + kwargs={'pk': self.external_object.pk} + ) + url = URL( + path=path, query={'template': form.cleaned_data['template']} + ) + + return HttpResponseRedirect(redirect_to=url.to_string()) + + def get_initial(self): + template_string = self.request.GET.get('template') + try: + context = Context( + {'document': self.external_object} + ) + template = Template(template_string=template_string) + result = template.render(context=context) + except TemplateSyntaxError as exception: + result = '' + error_message = _( + 'Template error; %(exception)s' + ) % { + 'exception': exception + } + messages.error(request=self.request, message=error_message) + + return { + 'template': template_string, 'result': result + } + + def get_extra_context(self): + return { + 'object': self.external_object, + 'title': _('Template sandbox for: %s') % self.external_object + }