+
{% include "generic_form_subtemplate.html" %}
- {% if form.grid_clear or not form.grid %}
+ {% endif %}
+
+
+ {% for subtemplate in subtemplates_list %}
+
+ {% if subtemplate.form %}
+ {% render_subtemplate subtemplate.name subtemplate.context as rendered_subtemplate %}
+
+ {{ rendered_subtemplate }}
+
+ {% else %}
+ {% render_subtemplate subtemplate.name subtemplate.context as rendered_subtemplate %}
+ {{ rendered_subtemplate }}
+ {% endif %}
+
+ {% if subtemplate.grid_clear or not subtemplate.grid %}
{% endif %}
- {% endif %}
-
- {% for form in form_list %}
- {% with form.submit_method as submit_method %}
- {% with form.striptags as striptags %}
- {% with form.title as title %}
- {% with form.object as object %}
- {% with form.object_name as object_name %}
- {% with form.submit_label as submit_label %}
- {% with form.form_action as form_action %}
-
- {% with form.form as form %}
- {% include "generic_form_subtemplate.html" %}
- {% endwith %}
-
- {% if form.grid_clear or not form.grid %}
-
- {% endif %}
- {% endwith %}
- {% endwith %}
- {% endwith %}
- {% endwith %}
- {% endwith %}
- {% endwith %}
- {% endwith %}
- {% endfor %}
+ {% endfor %}
-
- {% for subtemplate in subtemplates_dict %}
- {% with subtemplate.title as title %}
- {% with subtemplate.object_list as object_list %}
- {% with subtemplate.extra_columns as extra_columns %}
- {% with subtemplate.hide_object as hide_object %}
- {% with subtemplate.main_object as main_object %}
- {% with subtemplate.hide_link as hide_link %}
- {% with subtemplate.hide_header as hide_header %}
- {% include subtemplate.name %}
- {% endwith %}
- {% endwith %}
- {% endwith %}
- {% endwith %}
- {% endwith %}
- {% endwith %}
- {% endwith %}
- {% endfor %}
-
{% endblock %}
diff --git a/apps/common/templates/generic_list.html b/apps/common/templates/generic_list.html
index 8f0c35f702..064a9752ed 100644
--- a/apps/common/templates/generic_list.html
+++ b/apps/common/templates/generic_list.html
@@ -1,87 +1,19 @@
{% extends "base.html" %}
{% load i18n %}
{% load navigation_tags %}
-{% block title %} :: {% blocktrans %}List of {{ title }}{% endblocktrans %}{% endblock %}
+{% block title %} :: {% blocktrans with title|striptags as stripped_title %}List of {{ stripped_title }}{% endblocktrans %}{% endblock %}
{#{% block secondary_links %}{{ secondary_links|safe }}{% endblock %}#}
-
{% block sidebar %}
{% for subtemplate in sidebar_subtemplates %}
{% include subtemplate %}
{% endfor %}
-
- {% for subtemplate in sidebar_subtemplates_list %}
- {% with subtemplate.title as title %}
- {% with subtemplate.object_list as object_list %}
- {% with subtemplate.extra_columns as extra_columns %}
- {% with subtemplate.hide_object as hide_object %}
- {% with subtemplate.main_object as main_object %}
- {% with subtemplate.hide_link as hide_link %}
- {% with subtemplate.hide_header as hide_header %}
- {% with subtemplate.hide_columns as hide_columns %}
- {% with "true" as side_bar %}
-
- {% with subtemplate.submit_method as submit_method %}
- {% with subtemplate.striptags as striptags %}
- {% with subtemplate.title as title %}
- {% with subtemplate.object as object %}
- {% with subtemplate.object_name as object_name %}
- {% with subtemplate.form_action as form_action %}
- {% with subtemplate.form as form %}
-
- {% with subtemplate.content as content %}
- {% with subtemplate.paragraphs as paragraphs %}
-
- {% include subtemplate.name %}
-
- {% endwith %}
- {% endwith %}
-
- {% endwith %}
- {% endwith %}
- {% endwith %}
- {% endwith %}
- {% endwith %}
- {% endwith %}
- {% endwith %}
-
-
- {% endwith %}
- {% endwith %}
- {% endwith %}
- {% endwith %}
- {% endwith %}
- {% endwith %}
- {% endwith %}
- {% endwith %}
- {% endwith %}
- {% endfor %}
{% endblock %}
{% block content %}
{% include "generic_list_subtemplate.html" %}
- {% for subtemplate in subtemplates_dict %}
- {% with subtemplate.title as title %}
- {% with subtemplate.object_list as object_list %}
- {% with subtemplate.extra_columns as extra_columns %}
- {% with subtemplate.hide_object as hide_object %}
- {% with subtemplate.main_object as main_object %}
- {% with subtemplate.hide_link as hide_link %}
- {% with subtemplate.hide_header as hide_header %}
- {% with subtemplate.multi_select as multi_select %}
- {% include subtemplate.name %}
- {% endwith %}
- {% endwith %}
- {% endwith %}
- {% endwith %}
- {% endwith %}
- {% endwith %}
- {% endwith %}
- {% endwith %}
- {% endfor %}
-
{% endblock %}
diff --git a/apps/common/templates/generic_list_subtemplate.html b/apps/common/templates/generic_list_subtemplate.html
index d87b6d0067..b92eb13b04 100644
--- a/apps/common/templates/generic_list_subtemplate.html
+++ b/apps/common/templates/generic_list_subtemplate.html
@@ -85,7 +85,7 @@
{% endif %}
{% empty %}
-
| {% blocktrans %}There are no {{ title }}{% endblocktrans %} |
+
| {% blocktrans with title|striptags as stripped_title %}There are no {{ stripped_title }}{% endblocktrans %} |
{% endfor %}
diff --git a/apps/common/utils.py b/apps/common/utils.py
index 32db044fc0..1881fe8cf7 100644
--- a/apps/common/utils.py
+++ b/apps/common/utils.py
@@ -317,7 +317,7 @@ def parse_range(astr):
x = part.split(u'-')
result.update(range(int(x[0]), int(x[-1]) + 1))
return sorted(result)
-
+
def generate_choices_w_labels(choices, display_object_type=True):
results = []
diff --git a/apps/common/views.py b/apps/common/views.py
index 2c6c32c7b0..c6b236665e 100644
--- a/apps/common/views.py
+++ b/apps/common/views.py
@@ -1,7 +1,12 @@
from django.shortcuts import redirect
from django.utils.translation import ugettext_lazy as _
-from django.contrib import messages
from django.http import HttpResponseRedirect
+from django.shortcuts import render_to_response
+from django.template import RequestContext
+from django.contrib import messages
+from django.contrib.contenttypes.models import ContentType
+
+from common.forms import ChoiceForm
def password_change_done(request):
@@ -15,8 +20,8 @@ def password_change_done(request):
def multi_object_action_view(request):
"""
- Proxy view called first when usuing a multi object action, which
- then redirects to the appropiate specialized view
+ Proxy view called first when usuing a multi object action, which
+ then redirects to the appropiate specialized view
"""
action = request.GET.get('action', None)
@@ -31,3 +36,96 @@ def multi_object_action_view(request):
return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
return HttpResponseRedirect('%s?id_list=%s' % (action, id_list))
+
+
+def get_obj_from_content_type_string(string):
+ model, pk = string.split(u',')
+ ct = ContentType.objects.get(model=model)
+ return ct.get_object_for_this_type(pk=pk)
+
+
+def assign_remove(request, left_list, right_list, add_method, remove_method, left_list_title, right_list_title, obj=None, object_name=None, decode_content_type=False):
+ left_list_name = u'left_list'
+ right_list_name = u'right_list'
+
+ if request.method == 'POST':
+ if u'%s-submit' % left_list_name in request.POST.keys():
+ unselected_list = ChoiceForm(request.POST,
+ prefix=left_list_name,
+ choices=left_list())
+ if unselected_list.is_valid():
+ for selection in unselected_list.cleaned_data['selection']:
+ label = dict(left_list())[selection]
+ if decode_content_type:
+ selection_obj = get_obj_from_content_type_string(selection)
+ else:
+ selection_obj = selection
+ try:
+ add_method(selection_obj)
+ messages.success(request, _(u'%(selection)s added successfully added to %(right_list_title)s.') % {
+ 'selection': label, 'right_list_title': right_list_title})
+ except:
+ messages.error(request, _(u'Unable to add %(selection)s to %(right_list_title)s.') % {
+ 'selection': label, 'right_list_title': right_list_title})
+
+ elif u'%s-submit' % right_list_name in request.POST.keys():
+ selected_list = ChoiceForm(request.POST,
+ prefix=right_list_name,
+ choices=right_list())
+ if selected_list.is_valid():
+ for selection in selected_list.cleaned_data['selection']:
+ label = dict(right_list())[selection]
+ if decode_content_type:
+ selection = get_obj_from_content_type_string(selection)
+ try:
+ remove_method(selection)
+ messages.success(request, _(u'%(selection)s added successfully removed from %(right_list_title)s.') % {
+ 'selection': label, 'right_list_title': right_list_title})
+ except:
+ messages.error(request, _(u'Unable to add %(selection)s to %(right_list_title)s.') % {
+ 'selection': label, 'right_list_title': right_list_title})
+ unselected_list = ChoiceForm(prefix=left_list_name,
+ choices=left_list())
+ selected_list = ChoiceForm(prefix=right_list_name,
+ choices=right_list())
+
+ context = {
+ 'subtemplates_list': [
+ {
+ 'name':'generic_form_subtemplate.html',
+ 'grid': 6,
+ 'context': {
+ 'form': unselected_list,
+ 'title': left_list_title,
+ 'submit_label': _(u'Add'),
+ }
+ },
+ {
+ 'name':'generic_form_subtemplate.html',
+ 'grid': 6,
+ 'grid_clear': True,
+ 'context': {
+ 'form': selected_list,
+ 'title': right_list_title,
+ 'submit_label': _(u'Remove'),
+ }
+ },
+
+ ],
+ }
+ if obj:
+ context.update(
+ {
+ 'object': obj
+ }
+ )
+
+ if object_name:
+ context.update(
+ {
+ 'object_name': object_name,
+ }
+ )
+
+ return render_to_response('generic_form.html', context,
+ context_instance=RequestContext(request))
diff --git a/apps/common/widgets.py b/apps/common/widgets.py
new file mode 100644
index 0000000000..2d07facbdf
--- /dev/null
+++ b/apps/common/widgets.py
@@ -0,0 +1,49 @@
+from django.utils.translation import ugettext_lazy as _
+from django.utils.safestring import mark_safe
+from django import forms
+
+
+class PlainWidget(forms.widgets.Widget):
+ def render(self, name, value, attrs=None):
+ return mark_safe(u'%s' % value)
+
+
+class DetailSelectMultiple(forms.widgets.SelectMultiple):
+ def __init__(self, queryset=None, *args, **kwargs):
+ self.queryset = queryset
+ super(DetailSelectMultiple, self).__init__(*args, **kwargs)
+
+ def render(self, name, value, attrs=None, choices=(), *args, **kwargs):
+ if value is None:
+ value = ''
+ final_attrs = self.build_attrs(attrs, name=name)
+ css_class = final_attrs.get('class', 'list')
+ output = u'
' % css_class
+ options = None
+ if value:
+ if getattr(value, '__iter__', None):
+ options = [(index, string) for index, string in \
+ self.choices if index in value]
+ else:
+ options = [(index, string) for index, string in \
+ self.choices if index == value]
+ else:
+ if self.choices:
+ if self.choices[0] != (u'', u'---------') and value != []:
+ options = [(index, string) for index, string in \
+ self.choices]
+
+ if options:
+ for index, string in options:
+ if self.queryset:
+ try:
+ output += u'- %s
' % (
+ self.queryset.get(pk=index).get_absolute_url(),
+ string)
+ except AttributeError:
+ output += u'- %s
' % (string)
+ else:
+ output += u'- %s
' % string
+ else:
+ output += u'- %s
' % _(u"None")
+ return mark_safe(output + u'
\n')
diff --git a/apps/document_comments/views.py b/apps/document_comments/views.py
index f56a0c353b..2b3d38b432 100644
--- a/apps/document_comments/views.py
+++ b/apps/document_comments/views.py
@@ -46,6 +46,7 @@ def comment_delete(request, comment_id=None, comment_id_list=None):
'delete_view': True,
'previous': previous,
'next': next,
+ 'form_icon': u'comment_delete.png',
}
if len(comments) == 1:
context['object'] = comments[0].content_object
diff --git a/apps/document_indexing/__init__.py b/apps/document_indexing/__init__.py
new file mode 100644
index 0000000000..65d1dd3dab
--- /dev/null
+++ b/apps/document_indexing/__init__.py
@@ -0,0 +1,23 @@
+from django.utils.translation import ugettext_lazy as _
+
+from navigation.api import register_menu
+from permissions.api import register_permissions
+from main.api import register_tool
+
+PERMISSION_DOCUMENT_INDEXING_VIEW = 'document_index_view'
+PERMISSION_DOCUMENT_INDEXING_REBUILD_INDEXES = 'document_rebuild_indexes'
+
+register_permissions('document_indexing', [
+ {'name': PERMISSION_DOCUMENT_INDEXING_VIEW, 'label': _(u'View document indexes')},
+ {'name': PERMISSION_DOCUMENT_INDEXING_REBUILD_INDEXES, 'label': _(u'Rebuild document indexes')},
+])
+
+index_list = {'text': _(u'index list'), 'view': 'index_instance_list', 'famfam': 'folder_link', 'permissions': {'namespace': 'document_indexing', 'permissions': [PERMISSION_DOCUMENT_INDEXING_VIEW]}}
+
+register_menu([
+ {'text': _('indexes'), 'view': 'index_instance_list', 'links': [
+ ], 'famfam': 'folder_link', 'position': 2, 'permissions': {'namespace': 'document_indexing', 'permissions': [PERMISSION_DOCUMENT_INDEXING_VIEW]}}])
+
+rebuild_index_instances = {'text': _('rebuild indexes'), 'view': 'rebuild_index_instances', 'famfam': 'folder_link', 'permissions': {'namespace': 'document_indexing', 'permissions': [PERMISSION_DOCUMENT_INDEXING_REBUILD_INDEXES]}, 'description': _(u'Deletes and creates from scratch all the document indexes.')}
+
+register_tool(rebuild_index_instances, namespace='document_indexing', title=_(u'Indexes'))
diff --git a/apps/document_indexing/admin.py b/apps/document_indexing/admin.py
new file mode 100644
index 0000000000..cfe2cd6f99
--- /dev/null
+++ b/apps/document_indexing/admin.py
@@ -0,0 +1,27 @@
+from django.contrib import admin
+
+from mptt.admin import MPTTModelAdmin
+
+from document_indexing.models import Index, IndexInstance, \
+ DocumentRenameCount
+
+
+class IndexInstanceInline(admin.StackedInline):
+ model = IndexInstance
+ extra = 1
+ classes = ('collapse-open',)
+ allow_add = True
+
+
+class IndexAdmin(MPTTModelAdmin):
+ list_display = ('expression', 'enabled', 'link_documents')
+
+
+class IndexInstanceAdmin(MPTTModelAdmin):
+ model = IndexInstance
+ list_display = ('value', 'index', 'get_document_list_display')
+
+
+admin.site.register(Index, IndexAdmin)
+admin.site.register(IndexInstance, IndexInstanceAdmin)
+admin.site.register(DocumentRenameCount)
diff --git a/apps/document_indexing/api.py b/apps/document_indexing/api.py
new file mode 100644
index 0000000000..b2e166f347
--- /dev/null
+++ b/apps/document_indexing/api.py
@@ -0,0 +1,211 @@
+from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import ugettext
+from django.core.urlresolvers import reverse
+from django.utils.safestring import mark_safe
+from django.template.defaultfilters import slugify
+
+from documents.models import Document
+from metadata.classes import MetadataObject
+
+from document_indexing.models import Index, IndexInstance, \
+ DocumentRenameCount
+from document_indexing.conf.settings import AVAILABLE_INDEXING_FUNCTIONS
+from document_indexing.conf.settings import MAX_SUFFIX_COUNT
+from document_indexing.filesystem import fs_create_index_directory, \
+ fs_create_document_link, fs_delete_document_link, \
+ fs_delete_index_directory, fs_delete_directory_recusive
+from document_indexing.conf.settings import SLUGIFY_PATHS
+from document_indexing.os_agnostic import assemble_document_filename
+
+if SLUGIFY_PATHS == False:
+ # Do not slugify path or filenames and extensions
+ SLUGIFY_FUNCTION = lambda x: x
+else:
+ SLUGIFY_FUNCTION = slugify
+
+
+class MaxSuffixCountReached(Exception):
+ pass
+
+
+# External functions
+def update_indexes(document):
+ """
+ Update or create all the index instances related to a document
+ """
+ warnings = []
+
+ eval_dict = {}
+ document_metadata_dict = dict([(metadata.metadata_type.name, metadata.value) for metadata in document.documentmetadata_set.all() if metadata.value])
+ eval_dict['document'] = document
+ eval_dict['metadata'] = MetadataObject(document_metadata_dict)
+
+ for root in Index.objects.filter(parent=None):
+ index_warnings = _evaluate_index(eval_dict, document, root)
+ warnings.extend(index_warnings)
+
+ return warnings
+
+
+def delete_indexes(document):
+ """
+ Delete all the index instances related to a document
+ """
+ warnings = []
+
+ for index_instance in document.indexinstance_set.all():
+ index_warnings = _remove_document_from_index_instance(document, index_instance)
+ warnings.extend(index_warnings)
+
+ return warnings
+
+
+def get_instance_link(index_instance=None, text=None, simple=False):
+ """
+ Return an HTML anchor to an index instance
+ """
+
+ if simple:
+ # Just display the instance's value or overrided text, no
+ # HTML anchor
+ template = u'%(value)s'
+ else:
+ template = u'
%(value)s'
+ if index_instance:
+ return template % {
+ 'url': index_instance.get_absolute_url(),
+ 'value': text if text else index_instance
+ }
+ else:
+ # Root node
+ return template % {
+ 'url': reverse('index_instance_list'),
+ 'value': ugettext(u'root')
+ }
+
+
+def get_breadcrumbs(index_instance, simple=False, single_link=False, include_count=False):
+ """
+ Return a joined string of HTML anchors to every index instance's
+ parent from the root of the tree to the index instance
+ """
+ result = []
+ if single_link:
+ # Return the entire breadcrumb path as a single HTML anchor
+ simple = True
+
+ result.append(get_instance_link(simple=simple))
+
+ for instance in index_instance.get_ancestors():
+ result.append(get_instance_link(instance, simple=simple))
+
+ result.append(get_instance_link(index_instance, simple=simple))
+
+ output = []
+
+ if include_count:
+ output.append(u'(%d)' % index_instance.documents.count())
+
+ if single_link:
+ # Return the entire breadcrumb path as a single HTML anchor
+ output.insert(0, get_instance_link(index_instance=index_instance, text=(u' / '.join(result))))
+ return mark_safe(u' '.join(output))
+ else:
+ output.insert(0, u' / '.join(result))
+ return mark_safe(u' '.join(output))
+
+
+def do_rebuild_all_indexes():
+ fs_delete_directory_recusive()
+ IndexInstance.objects.all().delete()
+ DocumentRenameCount.objects.all().delete()
+ for document in Document.objects.all():
+ update_indexes(document)
+
+ return [] # Warnings - None
+
+
+# Internal functions
+def find_lowest_available_suffix(index_instance, document):
+ index_instance_documents = DocumentRenameCount.objects.filter(index_instance=index_instance).filter(document__file_extension=document.file_extension)
+ files_list = []
+ for index_instance_document in index_instance_documents:
+ files_list.append(assemble_document_filename(index_instance_document.document, index_instance_document.suffix))
+
+ for suffix in xrange(MAX_SUFFIX_COUNT):
+ if assemble_document_filename(document, suffix) not in files_list:
+ return suffix
+
+ raise MaxSuffixCountReached(ugettext(u'Maximum suffix (%s) count reached.') % MAX_SUFFIX_COUNT)
+
+
+def _evaluate_index(eval_dict, document, node, parent_index_instance=None):
+ """
+ Evaluate an enabled index expression and update or create all the
+ related index instances also recursively calling itself to evaluate
+ all the index's children
+ """
+ warnings = []
+ if node.enabled:
+ try:
+ result = eval(node.expression, eval_dict, AVAILABLE_INDEXING_FUNCTIONS)
+ if result:
+ index_instance, created = IndexInstance.objects.get_or_create(index=node, value=result, parent=parent_index_instance)
+ #if created:
+ fs_create_index_directory(index_instance)
+ if node.link_documents:
+ suffix = find_lowest_available_suffix(index_instance, document)
+ document_count = DocumentRenameCount(
+ index_instance=index_instance,
+ document=document,
+ suffix=suffix
+ )
+ document_count.save()
+
+ fs_create_document_link(index_instance, document, suffix)
+ index_instance.documents.add(document)
+
+ for children in node.get_children():
+ children_warnings = _evaluate_index(
+ eval_dict, document, children, index_instance
+ )
+ warnings.extend(children_warnings)
+
+ except (NameError, AttributeError), exc:
+ warnings.append(_(u'Error in document indexing update expression: %(expression)s; %(exception)s') % {
+ 'expression': node.expression, 'exception': exc})
+ except Exception, exc:
+ warnings.append(_(u'Error updating document index, expression: %(expression)s; %(exception)s') % {
+ 'expression': node.expression, 'exception': exc})
+
+ return warnings
+
+
+def _remove_document_from_index_instance(document, index_instance):
+ """
+ Delete a documents reference from an index instance and call itself
+ recusively deleting documents and empty index instances up to the
+ root of the tree
+ """
+ warnings = []
+ try:
+ document_rename_count = DocumentRenameCount.objects.get(index_instance=index_instance, document=document)
+ fs_delete_document_link(index_instance, document, document_rename_count.suffix)
+ document_rename_count.delete()
+ index_instance.documents.remove(document)
+ if index_instance.documents.count() == 0 and index_instance.get_children().count() == 0:
+ # if there are no more documents and no children, delete
+ # node and check parent for the same conditions
+ parent = index_instance.parent
+ fs_delete_index_directory(index_instance)
+ index_instance.delete()
+ parent_warnings = _remove_document_from_index_instance(
+ document, parent
+ )
+ warnings.extend(parent_warnings)
+ except DocumentRenameCount.DoesNotExist:
+ return warnings
+ except Exception, exc:
+ warnings.append(_(u'Unable to delete document indexing node; %s') % exc)
+
+ return warnings
diff --git a/apps/filesystem_serving/conf/__init__.py b/apps/document_indexing/conf/__init__.py
similarity index 100%
rename from apps/filesystem_serving/conf/__init__.py
rename to apps/document_indexing/conf/__init__.py
diff --git a/apps/document_indexing/conf/settings.py b/apps/document_indexing/conf/settings.py
new file mode 100644
index 0000000000..9d471c19a1
--- /dev/null
+++ b/apps/document_indexing/conf/settings.py
@@ -0,0 +1,25 @@
+"""Configuration options for the document_indexing app"""
+
+from django.utils.translation import ugettext_lazy as _
+
+from common.utils import proper_name
+from smart_settings.api import register_settings
+
+available_indexing_functions = {
+ 'proper_name': proper_name
+}
+
+register_settings(
+ namespace=u'document_indexing',
+ module=u'document_indexing.conf.settings',
+ settings=[
+ # Definition
+ {'name': u'AVAILABLE_INDEXING_FUNCTIONS', 'global_name': u'DOCUMENT_INDEXING_AVAILABLE_INDEXING_FUNCTIONS', 'default': available_indexing_functions},
+ {'name': u'SUFFIX_SEPARATOR', 'global_name': u'DOCUMENT_INDEXING_SUFFIX_SEPARATOR', 'default': u'_'},
+ # Filesystem serving
+ {'name': u'SLUGIFY_PATHS', 'global_name': u'DOCUMENT_INDEXING_FILESYSTEM_SLUGIFY_PATHS', 'default': False},
+ {'name': u'MAX_SUFFIX_COUNT', 'global_name': u'DOCUMENT_INDEXING_FILESYSTEM_MAX_SUFFIX_COUNT', 'default': 1000},
+ {'name': u'FILESERVING_PATH', 'global_name': u'DOCUMENT_INDEXING_FILESYSTEM_FILESERVING_PATH', 'default': u'/tmp/mayan/documents', 'exists': True},
+ {'name': u'FILESERVING_ENABLE', 'global_name': u'DOCUMENT_INDEXING_FILESYSTEM_FILESERVING_ENABLE', 'default': True}
+ ]
+)
diff --git a/apps/document_indexing/filesystem.py b/apps/document_indexing/filesystem.py
new file mode 100644
index 0000000000..a405ca4f8d
--- /dev/null
+++ b/apps/document_indexing/filesystem.py
@@ -0,0 +1,92 @@
+import errno
+import os
+
+from django.utils.translation import ugettext_lazy as _
+
+from document_indexing.os_agnostic import assemble_document_filename
+from document_indexing.conf.settings import FILESERVING_ENABLE
+from document_indexing.conf.settings import FILESERVING_PATH
+
+
+def get_instance_path(index_instance):
+ """
+ Return a platform formated filesytem path corresponding to an
+ index instance
+ """
+ names = []
+ for ancestor in index_instance.get_ancestors():
+ names.append(ancestor.value)
+
+ names.append(index_instance.value)
+
+ return os.sep.join(names)
+
+
+def fs_create_index_directory(index_instance):
+ if FILESERVING_ENABLE:
+ target_directory = os.path.join(FILESERVING_PATH, get_instance_path(index_instance))
+ try:
+ os.makedirs(target_directory)
+ except OSError, exc:
+ if exc.errno == errno.EEXIST:
+ pass
+ else:
+ raise OSError(_(u'Unable to create indexing directory; %s') % exc)
+
+
+def fs_create_document_link(index_instance, document, suffix=0):
+ if FILESERVING_ENABLE:
+ name_part = assemble_document_filename(document, suffix)
+ filename = os.extsep.join([name_part, document.file_extension])
+ filepath = os.path.join(FILESERVING_PATH, get_instance_path(index_instance), filename)
+ try:
+ os.symlink(document.file.path, filepath)
+ except OSError, exc:
+ if exc.errno == errno.EEXIST:
+ # This link should not exist, try to delete it
+ try:
+ os.unlink(filepath)
+ # Try again
+ os.symlink(document.file.path, filepath)
+ except Exception, exc:
+ raise Exception(_(u'Unable to create symbolic link, file exists and could not be deleted: %(filepath)s; %(exc)s') % {'filepath': filepath, 'exc': exc})
+ else:
+ raise OSError(_(u'Unable to create symbolic link: %(filepath)s; %(exc)s') % {'filepath': filepath, 'exc': exc})
+
+
+def fs_delete_document_link(index_instance, document, suffix=0):
+ if FILESERVING_ENABLE:
+ name_part = document.file_filename
+ if suffix:
+ name_part = u'_'.join([name_part, unicode(suffix)])
+
+ filename = os.extsep.join([name_part, document.file_extension])
+ filepath = os.path.join(FILESERVING_PATH, get_instance_path(index_instance), filename)
+
+ try:
+ os.unlink(filepath)
+ except OSError, exc:
+ if exc.errno != errno.ENOENT:
+ # Raise when any error other than doesn't exits
+ raise OSError(_(u'Unable to delete document symbolic link; %s') % exc)
+
+
+def fs_delete_index_directory(index_instance):
+ if FILESERVING_ENABLE:
+ target_directory = os.path.join(FILESERVING_PATH, get_instance_path(index_instance))
+ try:
+ os.removedirs(target_directory)
+ except OSError, exc:
+ if exc.errno == errno.EEXIST:
+ pass
+ else:
+ raise OSError(_(u'Unable to delete indexing directory; %s') % exc)
+
+
+def fs_delete_directory_recusive(path=FILESERVING_PATH):
+ if FILESERVING_ENABLE:
+ for dirpath, dirnames, filenames in os.walk(path, topdown=False):
+ for filename in filenames:
+ os.unlink(os.path.join(dirpath, filename))
+ for dirname in dirnames:
+ os.rmdir(os.path.join(dirpath, dirname))
diff --git a/apps/document_indexing/models.py b/apps/document_indexing/models.py
new file mode 100644
index 0000000000..a8a1003131
--- /dev/null
+++ b/apps/document_indexing/models.py
@@ -0,0 +1,60 @@
+from django.db import models
+from django.utils.translation import ugettext_lazy as _
+
+from mptt.models import MPTTModel
+from mptt.fields import TreeForeignKey
+
+from documents.models import Document
+
+from document_indexing.conf.settings import AVAILABLE_INDEXING_FUNCTIONS
+
+available_indexing_functions_string = (_(u'Available functions: %s') % u','.join([u'%s()' % name for name, function in AVAILABLE_INDEXING_FUNCTIONS.items()])) if AVAILABLE_INDEXING_FUNCTIONS else u''
+
+
+class Index(MPTTModel):
+ parent = TreeForeignKey('self', null=True, blank=True, related_name='index_meta_class')
+ expression = models.CharField(max_length=128, verbose_name=_(u'indexing expression'), help_text=_(u'Enter a python string expression to be evaluated.'))
+ # % available_indexing_functions_string)
+ enabled = models.BooleanField(default=True, verbose_name=_(u'enabled'))
+ link_documents = models.BooleanField(default=False, verbose_name=_(u'link documents'))
+
+ def __unicode__(self):
+ return self.expression if not self.link_documents else u'%s/[document]' % self.expression
+
+ class Meta:
+ verbose_name = _(u'index')
+ verbose_name_plural = _(u'indexes')
+
+
+class IndexInstance(MPTTModel):
+ parent = TreeForeignKey('self', null=True, blank=True, related_name='index_meta_instance')
+ index = models.ForeignKey(Index, verbose_name=_(u'index'))
+ value = models.CharField(max_length=128, blank=True, verbose_name=_(u'value'))
+ documents = models.ManyToManyField(Document, verbose_name=_(u'documents'))
+
+ def __unicode__(self):
+ return self.value
+
+ @models.permalink
+ def get_absolute_url(self):
+ return ('index_instance_list', [self.pk])
+
+ def get_document_list_display(self):
+ return u', '.join([d.file_filename for d in self.documents.all()])
+
+ class Meta:
+ verbose_name = _(u'index instance')
+ verbose_name_plural = _(u'indexes instances')
+
+
+class DocumentRenameCount(models.Model):
+ index_instance = models.ForeignKey(IndexInstance, verbose_name=_(u'index instance'))
+ document = models.ForeignKey(Document, verbose_name=_(u'document'))
+ suffix = models.PositiveIntegerField(blank=True, verbose_name=(u'suffix'))
+
+ def __unicode__(self):
+ return u'%s - %s - %s' % (self.index_instance, self.document, self.suffix or u'0')
+
+ class Meta:
+ verbose_name = _(u'document rename count')
+ verbose_name_plural = _(u'documents rename count')
diff --git a/apps/document_indexing/os_agnostic.py b/apps/document_indexing/os_agnostic.py
new file mode 100644
index 0000000000..ac8ef648ec
--- /dev/null
+++ b/apps/document_indexing/os_agnostic.py
@@ -0,0 +1,8 @@
+from document_indexing.conf.settings import SUFFIX_SEPARATOR
+
+
+def assemble_document_filename(document, suffix=0):
+ if suffix:
+ return SUFFIX_SEPARATOR.join([document.file_filename, unicode(suffix)])
+ else:
+ return document.file_filename
diff --git a/apps/filesystem_serving/tests.py b/apps/document_indexing/tests.py
similarity index 100%
rename from apps/filesystem_serving/tests.py
rename to apps/document_indexing/tests.py
diff --git a/apps/document_indexing/urls.py b/apps/document_indexing/urls.py
new file mode 100644
index 0000000000..5aa4fcf3a5
--- /dev/null
+++ b/apps/document_indexing/urls.py
@@ -0,0 +1,7 @@
+from django.conf.urls.defaults import patterns, url
+
+urlpatterns = patterns('document_indexing.views',
+ url(r'^(?P
\d+)/list/$', 'index_instance_list', (), 'index_instance_list'),
+ url(r'^list/$', 'index_instance_list', (), 'index_instance_list'),
+ url(r'^rebuild/all/$', 'rebuild_index_instances', (), 'rebuild_index_instances'),
+)
diff --git a/apps/document_indexing/utils.py b/apps/document_indexing/utils.py
new file mode 100644
index 0000000000..24f32d7f12
--- /dev/null
+++ b/apps/document_indexing/utils.py
@@ -0,0 +1,23 @@
+from django.utils.translation import ugettext_lazy as _
+
+from document_indexing.api import get_breadcrumbs
+
+
+def get_document_indexing_subtemplate(document):
+ """
+ Return all the settings to render a subtemplate containing a
+ list of index instances where a document may be found
+ """
+ object_list = []
+
+ for index_instance in document.indexinstance_set.all():
+ object_list.append(get_breadcrumbs(index_instance, single_link=True, include_count=True))
+
+ return {
+ 'name': 'generic_list_subtemplate.html',
+ 'context': {
+ 'title': _(u'document indexes'),
+ 'object_list': object_list,
+ 'hide_link': True
+ }
+ }
diff --git a/apps/document_indexing/views.py b/apps/document_indexing/views.py
new file mode 100644
index 0000000000..bb341e3222
--- /dev/null
+++ b/apps/document_indexing/views.py
@@ -0,0 +1,64 @@
+from django.utils.translation import ugettext_lazy as _
+from django.http import HttpResponseRedirect
+from django.shortcuts import render_to_response, get_object_or_404
+from django.template import RequestContext
+from django.contrib import messages
+from django.utils.safestring import mark_safe
+
+from permissions.api import check_permissions
+
+from document_indexing import PERMISSION_DOCUMENT_INDEXING_VIEW, \
+ PERMISSION_DOCUMENT_INDEXING_REBUILD_INDEXES
+
+from document_indexing.models import IndexInstance
+from document_indexing.api import get_breadcrumbs, get_instance_link, \
+ do_rebuild_all_indexes
+
+
+def index_instance_list(request, index_id=None):
+ check_permissions(request.user, 'document_indexing', [PERMISSION_DOCUMENT_INDEXING_VIEW])
+
+ if index_id:
+ index_instance = get_object_or_404(IndexInstance, pk=index_id)
+ index_instance_list = [index for index in index_instance.get_children().order_by('value')]
+ breadcrumbs = get_breadcrumbs(index_instance)
+ if index_instance.documents.count():
+ for document in index_instance.documents.all().order_by('file_filename'):
+ index_instance_list.append(document)
+ else:
+ index_instance_list = IndexInstance.objects.filter(parent=None)
+ breadcrumbs = get_instance_link()
+
+ title = mark_safe(_(u'contents for index: %s') % breadcrumbs)
+
+ return render_to_response('generic_list.html', {
+ 'object_list': index_instance_list,
+ 'title': title,
+ 'hide_links': True,
+ }, context_instance=RequestContext(request))
+
+
+def rebuild_index_instances(request):
+ check_permissions(request.user, 'document_indexing', [PERMISSION_DOCUMENT_INDEXING_REBUILD_INDEXES])
+
+ previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', None)))
+ next = request.POST.get('next', request.GET.get('next', request.META.get('HTTP_REFERER', None)))
+
+ if request.method != 'POST':
+ return render_to_response('generic_confirm.html', {
+ 'previous': previous,
+ 'next': next,
+ 'message': _(u'On large databases this operation may take some time to execute.'),
+ 'form_icon': u'folder_link.png',
+ }, context_instance=RequestContext(request))
+ else:
+ try:
+ warnings = do_rebuild_all_indexes()
+ messages.success(request, _(u'Index rebuild completed successfully.'))
+ for warning in warnings:
+ messages.warning(request, warning)
+
+ except Exception, e:
+ messages.error(request, _(u'Index rebuild error: %s') % e)
+
+ return HttpResponseRedirect(next)
diff --git a/apps/documents/__init__.py b/apps/documents/__init__.py
index d6d1da3810..a6bb737d5f 100644
--- a/apps/documents/__init__.py
+++ b/apps/documents/__init__.py
@@ -11,22 +11,16 @@ from tags.widgets import get_tags_inline_widget_simple
from documents.models import Document, DocumentPage, DocumentPageTransformation
from documents.staging import StagingFile
from documents.conf.settings import ENABLE_SINGLE_DOCUMENT_UPLOAD
-
-PERMISSION_DOCUMENT_CREATE = 'document_create'
-PERMISSION_DOCUMENT_PROPERTIES_EDIT = 'document_properties_edit'
-PERMISSION_DOCUMENT_EDIT = 'document_edit'
-PERMISSION_DOCUMENT_METADATA_EDIT = 'document_metadata_edit'
-PERMISSION_DOCUMENT_VIEW = 'document_view'
-PERMISSION_DOCUMENT_DELETE = 'document_delete'
-PERMISSION_DOCUMENT_DOWNLOAD = 'document_download'
-PERMISSION_DOCUMENT_TRANSFORM = 'document_transform'
-PERMISSION_DOCUMENT_TOOLS = 'document_tools'
+from documents.literals import PERMISSION_DOCUMENT_CREATE, \
+ PERMISSION_DOCUMENT_PROPERTIES_EDIT, PERMISSION_DOCUMENT_VIEW, \
+ PERMISSION_DOCUMENT_DELETE, PERMISSION_DOCUMENT_DOWNLOAD, \
+ PERMISSION_DOCUMENT_TRANSFORM, PERMISSION_DOCUMENT_TOOLS, \
+ PERMISSION_DOCUMENT_EDIT
register_permissions('documents', [
{'name': PERMISSION_DOCUMENT_CREATE, 'label': _(u'Create document')},
{'name': PERMISSION_DOCUMENT_PROPERTIES_EDIT, 'label': _(u'Edit document properties')},
{'name': PERMISSION_DOCUMENT_EDIT, 'label': _(u'Edit document')},
- {'name': PERMISSION_DOCUMENT_METADATA_EDIT, 'label': _(u'Edit document metadata')},
{'name': PERMISSION_DOCUMENT_VIEW, 'label': _(u'View document')},
{'name': PERMISSION_DOCUMENT_DELETE, 'label': _(u'Delete document')},
{'name': PERMISSION_DOCUMENT_DOWNLOAD, 'label': _(u'Download document')},
@@ -39,13 +33,11 @@ document_list_recent = {'text': _(u'recent documents list'), 'view': 'document_l
document_create = {'text': _(u'upload a new document'), 'view': 'document_create', 'famfam': 'page_add', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_CREATE]}}
document_create_multiple = {'text': _(u'upload multiple new documents'), 'view': 'document_create_multiple', 'famfam': 'page_add', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_CREATE]}}
document_create_sibling = {'text': _(u'upload new document using same metadata'), 'view': 'document_create_sibling', 'args': 'object.id', 'famfam': 'page_copy', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_CREATE]}}
-document_view = {'text': _(u'details (advanced)'), 'view': 'document_view', 'args': 'object.id', 'famfam': 'page', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_VIEW]}}
document_view_simple = {'text': _(u'details (simple)'), 'view': 'document_view_simple', 'args': 'object.id', 'famfam': 'page', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_VIEW]}}
+document_view_advanced = {'text': _(u'details (advanced)'), 'view': 'document_view_advanced', 'args': 'object.id', 'famfam': 'page', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_VIEW]}}
document_delete = {'text': _(u'delete'), 'view': 'document_delete', 'args': 'object.id', 'famfam': 'page_delete', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_DELETE]}}
document_multiple_delete = {'text': _(u'delete'), 'view': 'document_multiple_delete', 'famfam': 'page_delete', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_DELETE]}}
document_edit = {'text': _(u'edit'), 'view': 'document_edit', 'args': 'object.id', 'famfam': 'page_edit', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_PROPERTIES_EDIT]}}
-document_edit_metadata = {'text': _(u'edit metadata'), 'view': 'document_edit_metadata', 'args': 'object.id', 'famfam': 'page_edit', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_METADATA_EDIT]}}
-document_multiple_edit_metadata = {'text': _(u'edit metadata'), 'view': 'document_multiple_edit_metadata', 'famfam': 'page_edit', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_METADATA_EDIT]}}
document_preview = {'text': _(u'preview'), 'class': 'fancybox', 'view': 'document_preview', 'args': 'object.id', 'famfam': 'magnifier', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_VIEW]}}
document_download = {'text': _(u'download'), 'view': 'document_download', 'args': 'object.id', 'famfam': 'page_save', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_DOWNLOAD]}}
document_find_duplicates = {'text': _(u'find duplicates'), 'view': 'document_find_duplicates', 'args': 'object.id', 'famfam': 'page_refresh', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_VIEW]}}
@@ -62,9 +54,9 @@ document_page_transformation_page_view = {'text': _(u'page details'), 'class': '
document_page_transformation_page_edit = {'text': _(u'edit page'), 'class': 'no-parent-history', 'view': 'document_page_edit', 'args': 'object.document_page.id', 'famfam': 'page_white', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_EDIT]}}
document_page_transformation_page_transformation_list = {'text': _(u'page transformations'), 'class': 'no-parent-history', 'view': 'document_page_transformation_list', 'args': 'object.document_page.id', 'famfam': 'pencil_go', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_TRANSFORM]}}
-document_page_view = {'text': _(u'page image'), 'class': 'no-parent-history', 'view': 'document_page_view', 'args': 'object.id', 'famfam': 'page_white', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_VIEW]}}
-document_page_text = {'text': _(u'page text'), 'class': 'no-parent-history', 'view': 'document_page_text', 'args': 'object.id', 'famfam': 'page_white', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_VIEW]}}
-document_page_edit = {'text': _(u'edit page text'), 'class': 'no-parent-history', 'view': 'document_page_edit', 'args': 'object.id', 'famfam': 'page_white', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_EDIT]}}
+document_page_view = {'text': _(u'page image'), 'class': 'no-parent-history', 'view': 'document_page_view', 'args': 'object.id', 'famfam': 'page_white_picture', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_VIEW]}}
+document_page_text = {'text': _(u'page text'), 'class': 'no-parent-history', 'view': 'document_page_text', 'args': 'object.id', 'famfam': 'page_white_text', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_VIEW]}}
+document_page_edit = {'text': _(u'edit page text'), 'class': 'no-parent-history', 'view': 'document_page_edit', 'args': 'object.id', 'famfam': 'page_white_edit', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_EDIT]}}
document_page_navigation_next = {'text': _(u'next page'), 'class': 'no-parent-history', 'view': 'document_page_navigation_next', 'args': 'object.id', 'famfam': 'resultset_next', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_VIEW]}}
document_page_navigation_previous = {'text': _(u'previous page'), 'class': 'no-parent-history', 'view': 'document_page_navigation_previous', 'args': 'object.id', 'famfam': 'resultset_previous', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_VIEW]}}
document_page_navigation_first = {'text': _(u'first page'), 'class': 'no-parent-history', 'view': 'document_page_navigation_first', 'args': 'object.id', 'famfam': 'resultset_first', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_VIEW]}}
@@ -76,22 +68,18 @@ document_page_rotate_left = {'text': _(u'rotate left'), 'class': 'no-parent-hist
document_missing_list = {'text': _(u'Find missing document files'), 'view': 'document_missing_list', 'famfam': 'folder_page', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_VIEW]}}
-metadata_group_link = {'text': _(u'group actions'), 'view': 'metadatagroup_view', 'famfam': 'page_go', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_VIEW]}}
-metadata_group_back_to_document = {'text': _(u'return to document'), 'view': 'document_view_simple', 'args': 'ref_object.id', 'famfam': 'page', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_VIEW]}}
-metadata_group_create_sibling = {'text': _(u'upload new document using same metadata'), 'view': 'document_create_sibling', 'args': 'ref_object.id', 'famfam': 'page_copy', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_CREATE]}}
-
staging_file_preview = {'text': _(u'preview'), 'class': 'fancybox-noscaling', 'view': 'staging_file_preview', 'args': 'object.id', 'famfam': 'drive_magnify'}
staging_file_delete = {'text': _(u'delete'), 'view': 'staging_file_delete', 'args': 'object.id', 'famfam': 'drive_delete'}
-register_links(Document, [document_view_simple, document_view, document_edit, document_edit_metadata, document_print, document_delete, document_download, document_find_duplicates, document_clear_transformations])
+register_links(Document, [document_view_simple, document_view_advanced, document_edit, document_print, document_delete, document_download, document_find_duplicates, document_clear_transformations])
register_links(Document, [document_create_sibling], menu_name='sidebar')
-register_multi_item_links(['metadatagroup_view', 'document_list', 'document_list_recent'], [document_multiple_clear_transformations, document_multiple_edit_metadata, document_multiple_delete])
+register_multi_item_links(['document_group_view', 'document_list', 'document_list_recent'], [document_multiple_clear_transformations, document_multiple_delete])
if ENABLE_SINGLE_DOCUMENT_UPLOAD:
- register_links(['document_list_recent', 'document_list', 'document_create', 'document_create_multiple', 'upload_document_with_type', 'upload_multiple_documents_with_type'], [document_list_recent, document_list, document_create, document_create_multiple], menu_name='sidebar')
+ register_links(['document_list_recent', 'document_list', 'document_create', 'document_create_multiple', 'upload_document', 'upload_document_multiple'], [document_list_recent, document_list, document_create, document_create_multiple], menu_name='sidebar')
else:
- register_links(['document_list_recent', 'document_list', 'document_create', 'document_create_multiple', 'upload_document_with_type', 'upload_multiple_documents_with_type'], [document_list_recent, document_list, document_create_multiple], menu_name='sidebar')
+ register_links(['document_list_recent', 'document_list', 'document_create', 'document_create_multiple', 'upload_document', 'upload_document_multiple'], [document_list_recent, document_list, document_create_multiple], menu_name='sidebar')
register_links(DocumentPage, [
document_page_transformation_list, document_page_view,
@@ -113,8 +101,6 @@ register_links(['document_page_transformation_edit', 'document_page_transformati
register_links(StagingFile, [staging_file_preview, staging_file_delete])
-register_links(['metadatagroup_view'], [metadata_group_back_to_document, metadata_group_create_sibling], menu_name='sidebar')
-
register_diagnostic('documents', _(u'Documents'), document_missing_list)
register_tool(document_find_all_duplicates, namespace='documents', title=_(u'documents'))
diff --git a/apps/documents/admin.py b/apps/documents/admin.py
index bc1a0c7c15..ff4aa33cb0 100644
--- a/apps/documents/admin.py
+++ b/apps/documents/admin.py
@@ -1,29 +1,10 @@
from django.contrib import admin
-from documents.models import MetadataType, DocumentType, Document, \
- DocumentTypeMetadataType, DocumentMetadata, DocumentTypeFilename, \
- MetadataIndex, DocumentPage, MetadataGroup, \
- MetadataGroupItem, DocumentPageTransformation, RecentDocument
+from metadata.admin import DocumentMetadataInline
-from filesystem_serving.admin import DocumentMetadataIndexInline
-
-
-class MetadataTypeAdmin(admin.ModelAdmin):
- list_display = ('name', 'title', 'default', 'lookup')
-
-
-class MetadataIndexInline(admin.StackedInline):
- model = MetadataIndex
- extra = 1
- classes = ('collapse-open',)
- allow_add = True
-
-
-class DocumentTypeMetadataTypeInline(admin.StackedInline):
- model = DocumentTypeMetadataType
- extra = 1
- classes = ('collapse-open',)
- allow_add = True
+from documents.models import DocumentType, Document, \
+ DocumentTypeFilename, DocumentPage, \
+ DocumentPageTransformation, RecentDocument
class DocumentTypeFilenameInline(admin.StackedInline):
@@ -35,18 +16,10 @@ class DocumentTypeFilenameInline(admin.StackedInline):
class DocumentTypeAdmin(admin.ModelAdmin):
inlines = [
- DocumentTypeFilenameInline, DocumentTypeMetadataTypeInline,
- MetadataIndexInline
+ DocumentTypeFilenameInline
]
-class DocumentMetadataInline(admin.StackedInline):
- model = DocumentMetadata
- extra = 0
- classes = ('collapse-open',)
- allow_add = False
-
-
class DocumentPageTransformationAdmin(admin.ModelAdmin):
model = DocumentPageTransformation
@@ -60,24 +33,11 @@ class DocumentPageInline(admin.StackedInline):
class DocumentAdmin(admin.ModelAdmin):
inlines = [
- DocumentMetadataInline, DocumentMetadataIndexInline,
- DocumentPageInline
+ DocumentMetadataInline, DocumentPageInline
]
list_display = ('uuid', 'file_filename', 'file_extension')
-class MetadataGroupItemInline(admin.StackedInline):
- model = MetadataGroupItem
- extra = 1
- classes = ('collapse-open',)
- allow_add = True
-
-
-class MetadataGroupAdmin(admin.ModelAdmin):
- inlines = [MetadataGroupItemInline]
- filter_horizontal = ['document_type']
-
-
class RecentDocumentAdmin(admin.ModelAdmin):
model = RecentDocument
list_display = ('user', 'document', 'datetime_accessed')
@@ -86,10 +46,8 @@ class RecentDocumentAdmin(admin.ModelAdmin):
date_hierarchy = 'datetime_accessed'
-admin.site.register(MetadataType, MetadataTypeAdmin)
admin.site.register(DocumentType, DocumentTypeAdmin)
admin.site.register(Document, DocumentAdmin)
-admin.site.register(MetadataGroup, MetadataGroupAdmin)
admin.site.register(DocumentPageTransformation,
DocumentPageTransformationAdmin)
admin.site.register(RecentDocument, RecentDocumentAdmin)
diff --git a/apps/documents/conf/settings.py b/apps/documents/conf/settings.py
index d504fef996..60f229fb5e 100644
--- a/apps/documents/conf/settings.py
+++ b/apps/documents/conf/settings.py
@@ -1,13 +1,10 @@
"""Configuration options for the documents app"""
-import datetime
import hashlib
import uuid
from django.utils.translation import ugettext_lazy as _
-from django.contrib.auth.models import User
-from common.utils import proper_name
from storage.backends.filebasedstorage import FileBasedStorage
from smart_settings.api import register_settings
@@ -21,30 +18,14 @@ def default_uuid():
"""unicode(uuid.uuid4())"""
return unicode(uuid.uuid4())
-default_available_functions = {
- 'current_date': datetime.datetime.now().date,
-}
-
-default_available_models = {
- 'User': User
-}
-
available_transformations = {
'rotate': {'label': _(u'Rotate [degrees]'), 'arguments': [{'name': 'degrees'}]}
}
-available_indexing_functions = {
- 'proper_name': proper_name
-}
-
register_settings(
namespace=u'documents',
module=u'documents.conf.settings',
settings=[
- # Definition
- {'name': u'AVAILABLE_FUNCTIONS', 'global_name': u'DOCUMENTS_METADATA_AVAILABLE_FUNCTIONS', 'default': default_available_functions},
- {'name': u'AVAILABLE_MODELS', 'global_name': u'DOCUMENTS_METADATA_AVAILABLE_MODELS', 'default': default_available_models},
- {'name': u'AVAILABLE_INDEXING_FUNCTIONS', 'global_name': u'DOCUMENTS_INDEXING_AVAILABLE_FUNCTIONS', 'default': available_indexing_functions},
# Upload
{'name': u'USE_STAGING_DIRECTORY', 'global_name': u'DOCUMENTS_USE_STAGING_DIRECTORY', 'default': False},
{'name': u'STAGING_DIRECTORY', 'global_name': u'DOCUMENTS_STAGING_DIRECTORY', 'default': u'/tmp/mayan/staging', 'exists': True},
@@ -72,7 +53,5 @@ register_settings(
{'name': u'ZOOM_MAX_LEVEL', 'global_name': u'DOCUMENTS_ZOOM_MAX_LEVEL', 'default': 200, 'description': _(u'Maximum amount in percent (%) to allow user to zoom in a document page interactively.')},
{'name': u'ZOOM_MIN_LEVEL', 'global_name': u'DOCUMENTS_ZOOM_MIN_LEVEL', 'default': 50, 'description': _(u'Minimum amount in percent (%) to allow user to zoom out a document page interactively.')},
{'name': u'ROTATION_STEP', 'global_name': u'DOCUMENTS_ROTATION_STEP', 'default': 90, 'description': _(u'Amount in degrees to rotate a document page per user interaction.')},
- #Groups
- {'name': u'GROUP_SHOW_EMPTY', 'global_name': u'DOCUMENTS_GROUP_SHOW_EMPTY', 'default': True},
]
)
diff --git a/apps/documents/forms.py b/apps/documents/forms.py
index 257a147f64..a81336be5a 100644
--- a/apps/documents/forms.py
+++ b/apps/documents/forms.py
@@ -2,25 +2,22 @@ from django import forms
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ugettext
from django.http import HttpResponseRedirect
-from django.utils.http import urlencode
from django.core.urlresolvers import reverse
from django.utils.safestring import mark_safe
-from django.forms.formsets import formset_factory
-from django.template.defaultfilters import capfirst
from django.conf import settings
-from tags.widgets import get_tags_inline_widget
from common.wizard import BoundFormWizard
from common.forms import DetailForm
from common.literals import PAGE_SIZE_CHOICES, PAGE_ORIENTATION_CHOICES
from common.conf.settings import DEFAULT_PAPER_SIZE
from common.conf.settings import DEFAULT_PAGE_ORIENTATION
+from common.utils import urlquote
+from metadata.models import MetadataSet, MetadataType
+from metadata.forms import MetadataFormSet
from documents.staging import StagingFile
-from documents.models import Document, DocumentType, DocumentTypeMetadataType, \
+from documents.models import Document, DocumentType, \
DocumentPage, DocumentPageTransformation
-from documents.conf.settings import AVAILABLE_FUNCTIONS
-from documents.conf.settings import AVAILABLE_MODELS
class DocumentPageTransformationForm(forms.ModelForm):
@@ -117,7 +114,7 @@ class ImageWidget(forms.widgets.Widget):
for page in value.documentpage_set.all():
output.append(
- u'''