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
+ }