From 7321b12c7db482d74ba7c2773c3ab64672383553 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Sun, 20 Jul 2014 22:44:12 -0400 Subject: [PATCH 01/20] Update remaining exception syntaxes --- mayan/apps/bootstrap/views.py | 8 +++--- mayan/apps/checkouts/views.py | 8 +++--- mayan/apps/common/utils.py | 4 +-- mayan/apps/common/widgets.py | 4 +-- mayan/apps/converter/office_converter.py | 12 ++++---- mayan/apps/django_gpg/views.py | 4 +-- mayan/apps/document_indexing/api.py | 16 +++++------ mayan/apps/document_indexing/filesystem.py | 28 +++++++++---------- mayan/apps/document_signatures/views.py | 8 +++--- mayan/apps/linking/views.py | 15 ++++------ mayan/apps/lock_manager/managers.py | 4 +-- mayan/apps/metadata/forms.py | 14 ++++------ .../templatetags/navigation_tags.py | 4 +-- mayan/apps/ocr/parsers/__init__.py | 4 +-- 14 files changed, 63 insertions(+), 70 deletions(-) diff --git a/mayan/apps/bootstrap/views.py b/mayan/apps/bootstrap/views.py index 40a4b737e0..694d067b2b 100644 --- a/mayan/apps/bootstrap/views.py +++ b/mayan/apps/bootstrap/views.py @@ -163,8 +163,8 @@ def bootstrap_setup_execute(request, bootstrap_setup_pk): bootstrap_setup.execute() except ExistingData: messages.error(request, _(u'Cannot execute bootstrap setup, there is existing data. Erase all data and try again.')) - except Exception, exc: - messages.error(request, _(u'Error executing bootstrap setup; %s') % exc) + except Exception as exception: + messages.error(request, _(u'Error executing bootstrap setup; %s') % exception) else: messages.success(request, _(u'Bootstrap setup "%s" executed successfully.') % bootstrap_setup) return HttpResponseRedirect(next) @@ -295,8 +295,8 @@ def erase_database_view(request): if request.method == 'POST': try: Cleanup.execute_all() - except Exception, exc: - messages.error(request, _(u'Error erasing database; %s') % exc) + except Exception as exception: + messages.error(request, _(u'Error erasing database; %s') % exception) else: messages.success(request, _(u'Database erased successfully.')) return HttpResponseRedirect(next) diff --git a/mayan/apps/checkouts/views.py b/mayan/apps/checkouts/views.py index 8102d24d03..1e611e122c 100644 --- a/mayan/apps/checkouts/views.py +++ b/mayan/apps/checkouts/views.py @@ -78,8 +78,8 @@ def checkout_document(request, document_pk): document_checkout = form.save(commit=False) document_checkout.user_object = request.user document_checkout.save() - except Exception, exc: - messages.error(request, _(u'Error trying to check out document; %s') % exc) + except Exception as exception: + messages.error(request, _(u'Error trying to check out document; %s') % exception) else: messages.success(request, _(u'Document "%s" checked out successfully.') % document) return HttpResponseRedirect(reverse('checkout_info', args=[document.pk])) @@ -122,8 +122,8 @@ def checkin_document(request, document_pk): document.check_in(user=request.user) except DocumentNotCheckedOut: messages.error(request, _(u'Document has not been checked out.')) - except Exception, exc: - messages.error(request, _(u'Error trying to check in document; %s') % exc) + except Exception as exception: + messages.error(request, _(u'Error trying to check in document; %s') % exception) else: messages.success(request, _(u'Document "%s" checked in successfully.') % document) return HttpResponseRedirect(next) diff --git a/mayan/apps/common/utils.py b/mayan/apps/common/utils.py index 0c8d6ec103..20d921ce9f 100644 --- a/mayan/apps/common/utils.py +++ b/mayan/apps/common/utils.py @@ -78,9 +78,9 @@ def return_attrib(obj, attrib, arguments=None): return result() else: return result - except Exception, err: + except Exception as exception: if settings.DEBUG: - return 'Attribute error: %s; %s' % (attrib, err) + return 'Attribute error: %s; %s' % (attrib, exception) else: pass diff --git a/mayan/apps/common/widgets.py b/mayan/apps/common/widgets.py index a134f788d0..cafcb3e890 100644 --- a/mayan/apps/common/widgets.py +++ b/mayan/apps/common/widgets.py @@ -62,8 +62,8 @@ class DetailSelectMultiple(forms.widgets.SelectMultiple): def exists_with_famfam(path): try: return two_state_template(os.path.exists(path)) - except Exception, exc: - return exc + except Exception as exception: + return exception def two_state_template(state, famfam_ok_icon=u'tick', famfam_fail_icon=u'cross'): diff --git a/mayan/apps/converter/office_converter.py b/mayan/apps/converter/office_converter.py index fd31d13eb6..d94ae91bec 100644 --- a/mayan/apps/converter/office_converter.py +++ b/mayan/apps/converter/office_converter.py @@ -85,9 +85,9 @@ class OfficeConverter(object): try: self.backend.convert(self.input_filepath, self.output_filepath) self.exists = True - except OfficeBackendError, msg: + except OfficeBackendError as exception: # convert exception so that at least the mime type icon is displayed - raise UnknownFileFormat(msg) + raise UnknownFileFormat(exception) def __unicode__(self): return getattr(self, 'output_filepath', None) @@ -140,7 +140,7 @@ class OfficeConverterBackendDirect(object): logger.debug('converted_output: %s' % converted_output) os.rename(converted_output, self.output_filepath) - except OSError, msg: - raise OfficeBackendError(msg) - except Exception, msg: - logger.error('Unhandled exception', exc_info=msg) + except OSError as exception: + raise OfficeBackendError(exception) + except Exception as exception: + logger.error('Unhandled exception', exc_info=exception) diff --git a/mayan/apps/django_gpg/views.py b/mayan/apps/django_gpg/views.py index 21e2720e1d..441775b749 100644 --- a/mayan/apps/django_gpg/views.py +++ b/mayan/apps/django_gpg/views.py @@ -100,8 +100,8 @@ def key_delete(request, fingerprint, key_type): gpg.delete_key(key) messages.success(request, _(u'Key: %s, deleted successfully.') % fingerprint) return HttpResponseRedirect(next) - except Exception, msg: - messages.error(request, msg) + except Exception as exception: + messages.error(request, exception) return HttpResponseRedirect(previous) return render_to_response('generic_confirm.html', { diff --git a/mayan/apps/document_indexing/api.py b/mayan/apps/document_indexing/api.py index a059512543..7504ff6056 100644 --- a/mayan/apps/document_indexing/api.py +++ b/mayan/apps/document_indexing/api.py @@ -81,18 +81,18 @@ def cascade_eval(eval_dict, document, template_node, parent_index_instance=None) if template_node.enabled: try: result = eval(template_node.expression, eval_dict, AVAILABLE_INDEXING_FUNCTIONS) - except Exception, exc: + except Exception as exception: warnings.append(_(u'Error in document indexing update expression: %(expression)s; %(exception)s') % { - 'expression': template_node.expression, 'exception': exc}) + 'expression': template_node.expression, 'exception': exception}) else: if result: index_instance, created = IndexInstanceNode.objects.get_or_create(index_template_node=template_node, value=result, parent=parent_index_instance) # if created: try: fs_create_index_directory(index_instance) - except Exception, exc: + except Exception as exception: warnings.append(_(u'Error updating document index, expression: %(expression)s; %(exception)s') % { - 'expression': template_node.expression, 'exception': exc}) + 'expression': template_node.expression, 'exception': exception}) if template_node.link_documents: suffix = find_lowest_available_suffix(index_instance, document) @@ -105,9 +105,9 @@ def cascade_eval(eval_dict, document, template_node, parent_index_instance=None) try: fs_create_document_link(index_instance, document, suffix) - except Exception, exc: + except Exception as exception: warnings.append(_(u'Error updating document index, expression: %(expression)s; %(exception)s') % { - 'expression': template_node.expression, 'exception': exc}) + 'expression': template_node.expression, 'exception': exception}) index_instance.documents.add(document) @@ -147,7 +147,7 @@ def cascade_document_remove(document, index_instance): warnings.extend(parent_warnings) except DocumentRenameCount.DoesNotExist: return warnings - except Exception, exc: - warnings.append(_(u'Unable to delete document indexing node; %s') % exc) + except Exception as exception: + warnings.append(_(u'Unable to delete document indexing node; %s') % exception) return warnings diff --git a/mayan/apps/document_indexing/filesystem.py b/mayan/apps/document_indexing/filesystem.py index bf6ead9655..0158b92cdf 100644 --- a/mayan/apps/document_indexing/filesystem.py +++ b/mayan/apps/document_indexing/filesystem.py @@ -44,11 +44,11 @@ def fs_create_index_directory(index_instance): target_directory = assemble_path_from_list([FILESYSTEM_SERVING[index_instance.index_template_node.index.name], get_instance_path(index_instance)]) try: os.mkdir(target_directory) - except OSError, exc: - if exc.errno == errno.EEXIST: + except OSError as exception: + if exception.errno == errno.EEXIST: pass else: - raise Exception(_(u'Unable to create indexing directory; %s') % exc) + raise Exception(_(u'Unable to create indexing directory; %s') % exception) def fs_create_document_link(index_instance, document, suffix=0): @@ -58,17 +58,17 @@ def fs_create_document_link(index_instance, document, suffix=0): try: os.symlink(document.file.path, filepath) - except OSError, exc: - if exc.errno == errno.EEXIST: + except OSError as exception: + if exception.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}) + except Exception as exception: + raise Exception(_(u'Unable to create symbolic link, file exists and could not be deleted: %(filepath)s; %(exception)s') % {'filepath': filepath, 'exception': exception}) else: - raise Exception(_(u'Unable to create symbolic link: %(filepath)s; %(exc)s') % {'filepath': filepath, 'exc': exc}) + raise Exception(_(u'Unable to create symbolic link: %(filepath)s; %(exception)s') % {'filepath': filepath, 'exception': exception}) def fs_delete_document_link(index_instance, document, suffix=0): @@ -78,10 +78,10 @@ def fs_delete_document_link(index_instance, document, suffix=0): try: os.unlink(filepath) - except OSError, exc: - if exc.errno != errno.ENOENT: + except OSError as exception: + if exception.errno != errno.ENOENT: # Raise when any error other than doesn't exits - raise Exception(_(u'Unable to delete document symbolic link; %s') % exc) + raise Exception(_(u'Unable to delete document symbolic link; %s') % exception) def fs_delete_index_directory(index_instance): @@ -89,11 +89,11 @@ def fs_delete_index_directory(index_instance): target_directory = assemble_path_from_list([FILESYSTEM_SERVING[index_instance.index_template_node.index.name], get_instance_path(index_instance)]) try: os.removedirs(target_directory) - except OSError, exc: - if exc.errno == errno.EEXIST: + except OSError as exception: + if exception.errno == errno.EEXIST: pass else: - raise Exception(_(u'Unable to delete indexing directory; %s') % exc) + raise Exception(_(u'Unable to delete indexing directory; %s') % exception) def fs_delete_directory_recusive(index): diff --git a/mayan/apps/document_signatures/views.py b/mayan/apps/document_signatures/views.py index 19cfe26e8b..132db4b8e8 100644 --- a/mayan/apps/document_signatures/views.py +++ b/mayan/apps/document_signatures/views.py @@ -94,8 +94,8 @@ def document_signature_upload(request, document_pk): DocumentVersionSignature.objects.add_detached_signature(document, request.FILES['file']) messages.success(request, _(u'Detached signature uploaded successfully.')) return HttpResponseRedirect(next) - except Exception, msg: - messages.error(request, msg) + except Exception as exception: + messages.error(request, exception) return HttpResponseRedirect(previous) else: form = DetachedSignatureForm() @@ -153,8 +153,8 @@ def document_signature_delete(request, document_pk): DocumentVersionSignature.objects.clear_detached_signature(document) messages.success(request, _(u'Detached signature deleted successfully.')) return HttpResponseRedirect(next) - except Exception, exc: - messages.error(request, _(u'Error while deleting the detached signature; %s') % exc) + except Exception as exception: + messages.error(request, _(u'Error while deleting the detached signature; %s') % exception) return HttpResponseRedirect(previous) return render_to_response('generic_confirm.html', { diff --git a/mayan/apps/linking/views.py b/mayan/apps/linking/views.py index 217306dedd..73540fc3ab 100644 --- a/mayan/apps/linking/views.py +++ b/mayan/apps/linking/views.py @@ -33,8 +33,6 @@ logger = logging.getLogger(__name__) def smart_link_action(request): - # Permission.objects.check_permissions(request.user, [PERMISSION_SMART_LINK_VIEW]) - action = request.GET.get('action', None) if not action: @@ -178,7 +176,6 @@ def smart_link_edit(request, smart_link_pk): form = SmartLinkForm(instance=smart_link) return render_to_response('generic_form.html', { - # 'navigation_object_name': 'smart_link', 'object': smart_link, 'form': form, 'title': _(u'Edit smart link: %s') % smart_link @@ -200,10 +197,10 @@ def smart_link_delete(request, smart_link_pk): try: smart_link.delete() messages.success(request, _(u'Smart link: %s deleted successfully.') % smart_link) - except Exception, error: - messages.error(request, _(u'Error deleting smart link: %(smart_link)s; %(error)s.') % { + except Exception as exception: + messages.error(request, _(u'Error deleting smart link: %(smart_link)s; %(exception)s.') % { 'smart_link': smart_link, - 'error': error + 'exception': exception }) return HttpResponseRedirect(next) @@ -315,10 +312,10 @@ def smart_link_condition_delete(request, smart_link_condition_pk): try: smart_link_condition.delete() messages.success(request, _(u'Smart link condition: "%s" deleted successfully.') % smart_link_condition) - except Exception, error: - messages.error(request, _(u'Error deleting smart link condition: %(smart_link_condition)s; %(error)s.') % { + except Exception as exception: + messages.error(request, _(u'Error deleting smart link condition: %(smart_link_condition)s; %(exception)s.') % { 'smart_link_condition': smart_link_condition, - 'error': error + 'exception': exception }) return HttpResponseRedirect(next) diff --git a/mayan/apps/lock_manager/managers.py b/mayan/apps/lock_manager/managers.py index bf8d5133c5..afe3bb4392 100644 --- a/mayan/apps/lock_manager/managers.py +++ b/mayan/apps/lock_manager/managers.py @@ -22,8 +22,8 @@ class LockManager(models.Manager): lock.save(force_insert=True) logger.debug('acquired lock: %s' % name) return lock - except IntegrityError, msg: - logger.debug('IntegrityError: %s', msg) + except IntegrityError as exception: + logger.debug('IntegrityError: %s', exception) # There is already an existing lock # Check it's expiration date and if expired, reset it try: diff --git a/mayan/apps/metadata/forms.py b/mayan/apps/metadata/forms.py index 50da37036f..6bd11a0775 100644 --- a/mayan/apps/metadata/forms.py +++ b/mayan/apps/metadata/forms.py @@ -1,8 +1,8 @@ from __future__ import absolute_import from django import forms -from django.utils.translation import ugettext_lazy as _ from django.forms.formsets import formset_factory +from django.utils.translation import ugettext_lazy as _ from common.widgets import ScrollableCheckboxSelectMultiple @@ -17,7 +17,6 @@ class MetadataForm(forms.Form): # Set form fields initial values if 'initial' in kwargs: self.metadata_type = kwargs['initial'].pop('metadata_type', None) - # self.document_type = kwargs['initial'].pop('document_type', None) # FIXME: # required = self.document_type.documenttypemetadatatype_set.get(metadata_type=self.metadata_type).required @@ -42,15 +41,15 @@ class MetadataForm(forms.Form): choices.insert(0, ('', '------')) self.fields['value'].choices = choices self.fields['value'].required = required - except Exception, err: - self.fields['value'].initial = err + except Exception as exception: + self.fields['value'].initial = exception self.fields['value'].widget = forms.TextInput(attrs={'readonly': 'readonly'}) if self.metadata_type.default: try: self.fields['value'].initial = eval(self.metadata_type.default, AVAILABLE_FUNCTIONS) - except Exception, err: - self.fields['value'].initial = err + except Exception as exception: + self.fields['value'].initial = exception id = forms.CharField(label=_(u'id'), widget=forms.HiddenInput) name = forms.CharField(label=_(u'Name'), @@ -71,7 +70,6 @@ class MetadataRemoveForm(MetadataForm): class MetadataSelectionForm(forms.Form): def __init__(self, *args, **kwargs): - # document_type = kwargs.pop('document_type', None) super(MetadataSelectionForm, self).__init__(*args, **kwargs) document_type = getattr(self, 'initial', {}).get('document_type', None) if document_type: @@ -86,7 +84,6 @@ class MetadataSelectionForm(forms.Form): queryset=MetadataSet.objects.all(), label=_(u'Metadata sets'), required=False, - # widget=forms.widgets.SelectMultiple(attrs={'size': 10, 'class': 'choice_form'}) widget=ScrollableCheckboxSelectMultiple(attrs={'size': 10, 'class': 'choice_form'}) ) @@ -94,7 +91,6 @@ class MetadataSelectionForm(forms.Form): queryset=MetadataType.objects.all(), label=_(u'Metadata'), required=False, - # widget=forms.widgets.SelectMultiple(attrs={'size': 10, 'class': 'choice_form'}) widget=ScrollableCheckboxSelectMultiple(attrs={'size': 10, 'class': 'choice_form'}) ) diff --git a/mayan/apps/navigation/templatetags/navigation_tags.py b/mayan/apps/navigation/templatetags/navigation_tags.py index 54392ae444..fd698ddf29 100644 --- a/mayan/apps/navigation/templatetags/navigation_tags.py +++ b/mayan/apps/navigation/templatetags/navigation_tags.py @@ -105,9 +105,9 @@ def resolve_links(context, links, current_view, current_path, parsed_query_strin new_link['url'] = reverse(link['view'], args=args) if link.get('keep_query', False): new_link['url'] = urlquote(new_link['url'], parsed_query_string) - except NoReverseMatch, err: + except NoReverseMatch as exception: new_link['url'] = '#' - new_link['error'] = err + new_link['error'] = exception elif 'url' in link: if not link.get('dont_mark_active', False): new_link['active'] = link['url'] == current_path diff --git a/mayan/apps/ocr/parsers/__init__.py b/mayan/apps/ocr/parsers/__init__.py index e158f66afa..a145ef53d4 100644 --- a/mayan/apps/ocr/parsers/__init__.py +++ b/mayan/apps/ocr/parsers/__init__.py @@ -116,8 +116,8 @@ class OfficeParser(Parser): else: raise ParserError - except OfficeConversionError, msg: - logger.error(msg) + except OfficeConversionError as exception: + logger.error(exception) raise ParserError From 793767f19e1539e5b10acc733ac53bdc096cdcae Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Tue, 22 Jul 2014 02:12:31 -0400 Subject: [PATCH 02/20] Fix models states in lastest migration --- .../0018_auto__chg_field_documentpage_page_label.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/mayan/apps/documents/migrations/0018_auto__chg_field_documentpage_page_label.py b/mayan/apps/documents/migrations/0018_auto__chg_field_documentpage_page_label.py index 9c3e9d123b..17a624061e 100644 --- a/mayan/apps/documents/migrations/0018_auto__chg_field_documentpage_page_label.py +++ b/mayan/apps/documents/migrations/0018_auto__chg_field_documentpage_page_label.py @@ -86,6 +86,14 @@ class Migration(SchemaMigration): 'page_label': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}), 'page_number': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1', 'db_index': 'True'}) }, + 'documents.documentpagetransformation': { + 'Meta': {'ordering': "('order',)", 'object_name': 'DocumentPageTransformation'}, + 'arguments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'document_page': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentPage']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'order': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'null': 'True', 'db_index': 'True', 'blank': 'True'}), + 'transformation': ('django.db.models.fields.CharField', [], {'max_length': '128'}) + }, 'documents.documenttype': { 'Meta': {'ordering': "['name']", 'object_name': 'DocumentType'}, 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), @@ -109,7 +117,7 @@ class Migration(SchemaMigration): 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'major': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), 'micro': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), - 'mimetype': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}), + 'mimetype': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), 'minor': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), 'release_level': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), 'serial': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), @@ -143,4 +151,4 @@ class Migration(SchemaMigration): } } - complete_apps = ['documents'] \ No newline at end of file + complete_apps = ['documents'] From 5e21b91b24c9972da0956ab599f535466d73a168 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Tue, 22 Jul 2014 02:17:18 -0400 Subject: [PATCH 03/20] The timestamp field is used constantly to determine the latest version of a document, index it --- ...to__add_index_documentversion_timestamp.py | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 mayan/apps/documents/migrations/0019_auto__add_index_documentversion_timestamp.py diff --git a/mayan/apps/documents/migrations/0019_auto__add_index_documentversion_timestamp.py b/mayan/apps/documents/migrations/0019_auto__add_index_documentversion_timestamp.py new file mode 100644 index 0000000000..c4c3a2ee05 --- /dev/null +++ b/mayan/apps/documents/migrations/0019_auto__add_index_documentversion_timestamp.py @@ -0,0 +1,119 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding index on 'DocumentVersion', fields ['timestamp'] + db.create_index(u'documents_documentversion', ['timestamp']) + + + def backwards(self, orm): + # Removing index on 'DocumentVersion', fields ['timestamp'] + db.delete_index(u'documents_documentversion', ['timestamp']) + + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'documents.document': { + 'Meta': {'ordering': "['-date_added']", 'object_name': 'Document'}, + 'date_added': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'document_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['documents.DocumentType']", 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '48', 'blank': 'True'}) + }, + u'documents.documentpage': { + 'Meta': {'ordering': "['page_number']", 'object_name': 'DocumentPage'}, + 'content': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'document_version': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'pages'", 'to': u"orm['documents.DocumentVersion']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'page_label': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}), + 'page_number': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1', 'db_index': 'True'}) + }, + u'documents.documentpagetransformation': { + 'Meta': {'ordering': "('order',)", 'object_name': 'DocumentPageTransformation'}, + 'arguments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'document_page': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['documents.DocumentPage']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'order': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'null': 'True', 'db_index': 'True', 'blank': 'True'}), + 'transformation': ('django.db.models.fields.CharField', [], {'max_length': '128'}) + }, + u'documents.documenttype': { + 'Meta': {'ordering': "['name']", 'object_name': 'DocumentType'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}) + }, + u'documents.documenttypefilename': { + 'Meta': {'ordering': "['filename']", 'object_name': 'DocumentTypeFilename'}, + 'document_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['documents.DocumentType']"}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + u'documents.documentversion': { + 'Meta': {'unique_together': "(('document', 'major', 'minor', 'micro', 'release_level', 'serial'),)", 'object_name': 'DocumentVersion'}, + 'checksum': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'versions'", 'to': u"orm['documents.Document']"}), + 'encoding': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}), + 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), + 'filename': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'major': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'micro': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'mimetype': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'minor': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'release_level': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'serial': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'timestamp': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}) + }, + u'documents.recentdocument': { + 'Meta': {'ordering': "('-datetime_accessed',)", 'object_name': 'RecentDocument'}, + 'datetime_accessed': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2014, 7, 22, 0, 0)', 'db_index': 'True'}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['documents.Document']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) + } + } + + complete_apps = ['documents'] \ No newline at end of file From e16889824887c7eead1c4cb546bdaabd8986aac5 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Tue, 22 Jul 2014 02:32:06 -0400 Subject: [PATCH 04/20] Make API writable --- mayan/apps/documents/api_views.py | 21 ++++++++++++++++----- mayan/apps/documents/models.py | 28 ++++++++++++++++++---------- mayan/apps/documents/serializers.py | 3 ++- mayan/apps/documents/urls.py | 4 +++- 4 files changed, 39 insertions(+), 17 deletions(-) diff --git a/mayan/apps/documents/api_views.py b/mayan/apps/documents/api_views.py index 5d1de99c2b..35dd435b88 100644 --- a/mayan/apps/documents/api_views.py +++ b/mayan/apps/documents/api_views.py @@ -20,8 +20,9 @@ from .models import Document, DocumentPage, DocumentVersion from .serializers import (DocumentImageSerializer, DocumentPageSerializer, DocumentSerializer, DocumentVersionSerializer) +#TODO: PUT, POST, permissions -class APIDocumentListView(generics.ListAPIView): +class APIDocumentListView(generics.ListCreateAPIView): """ Returns a list of all the documents. """ @@ -33,12 +34,11 @@ class APIDocumentListView(generics.ListAPIView): mayan_object_permissions = [PERMISSION_DOCUMENT_VIEW] -class APIDocumentPageView(generics.RetrieveAPIView): +class APIDocumentPageView(generics.RetrieveUpdateAPIView): """ Returns the selected document page details. """ - allowed_methods = ['GET'] serializer_class = DocumentPageSerializer queryset = DocumentPage.objects.all() @@ -47,12 +47,11 @@ class APIDocumentPageView(generics.RetrieveAPIView): mayan_permission_attribute_check = 'document' -class APIDocumentView(generics.RetrieveAPIView): +class APIDocumentView(generics.RetrieveUpdateDestroyAPIView): """ Returns the selected document details. """ - allowed_methods = ['GET'] serializer_class = DocumentSerializer queryset = Document.objects.all() @@ -60,6 +59,18 @@ class APIDocumentView(generics.RetrieveAPIView): mayan_object_permissions = [PERMISSION_DOCUMENT_VIEW] +class APIDocumentVersionCreateView(generics.CreateAPIView): + """ + Create a new document version. + """ + + serializer_class = DocumentVersionSerializer + queryset = DocumentVersion.objects.all() + + #filter_backends = (MayanObjectPermissionsFilter,) + #mayan_object_permissions = [PERMISSION_DOCUMENT_VIEW] + + class APIDocumentVersionView(generics.RetrieveAPIView): """ Returns the selected document version details. diff --git a/mayan/apps/documents/models.py b/mayan/apps/documents/models.py index f3f5579719..3bf44d051d 100644 --- a/mayan/apps/documents/models.py +++ b/mayan/apps/documents/models.py @@ -95,7 +95,11 @@ class Document(models.Model): ordering = ['-date_added'] def __unicode__(self): - return self.latest_version.filename + try: + return self.latest_version.filename + except AttributeError: + # Document has no version yet, let's return a place holder text + return ugettext(u'Uninitialized document') @models.permalink def get_absolute_url(self): @@ -250,7 +254,11 @@ class Document(models.Model): @property def pages(self): - return self.latest_version.pages + try: + return self.latest_version.pages + except AttributeError: + # Document has no version yet + return 0 @property def page_count(self): @@ -258,11 +266,11 @@ class Document(models.Model): @property def latest_version(self): - return self.versions.order_by('-timestamp')[0] + return self.versions.order_by('timestamp').last() @property def first_version(self): - return self.versions.order_by('timestamp')[0] + return self.versions.order_by('timestamp').first() def rename(self, new_name): version = self.latest_version @@ -302,12 +310,12 @@ class DocumentVersion(models.Model): def register_post_save_hook(cls, order, func): cls._post_save_hooks[order] = func - document = models.ForeignKey(Document, verbose_name=_(u'document'), editable=False, related_name='versions') - major = models.PositiveIntegerField(verbose_name=_(u'mayor'), default=1, editable=False) - minor = models.PositiveIntegerField(verbose_name=_(u'minor'), default=0, editable=False) - micro = models.PositiveIntegerField(verbose_name=_(u'micro'), default=0, editable=False) - release_level = models.PositiveIntegerField(choices=RELEASE_LEVEL_CHOICES, default=RELEASE_LEVEL_FINAL, verbose_name=_(u'release level'), editable=False) - serial = models.PositiveIntegerField(verbose_name=_(u'serial'), default=0, editable=False) + document = models.ForeignKey(Document, verbose_name=_(u'document'), related_name='versions') + major = models.PositiveIntegerField(verbose_name=_(u'mayor'), default=1) + minor = models.PositiveIntegerField(verbose_name=_(u'minor'), default=0) + micro = models.PositiveIntegerField(verbose_name=_(u'micro'), default=0) + release_level = models.PositiveIntegerField(choices=RELEASE_LEVEL_CHOICES, default=RELEASE_LEVEL_FINAL, verbose_name=_(u'release level')) + serial = models.PositiveIntegerField(verbose_name=_(u'serial'), default=0) timestamp = models.DateTimeField(verbose_name=_(u'timestamp'), editable=False) comment = models.TextField(blank=True, verbose_name=_(u'comment')) diff --git a/mayan/apps/documents/serializers.py b/mayan/apps/documents/serializers.py index a1720e0eb5..14f580f73b 100644 --- a/mayan/apps/documents/serializers.py +++ b/mayan/apps/documents/serializers.py @@ -11,7 +11,7 @@ class DocumentPageSerializer(serializers.HyperlinkedModelSerializer): class DocumentVersionSerializer(serializers.HyperlinkedModelSerializer): - pages = DocumentPageSerializer(many=True, read_only=True) + pages = DocumentPageSerializer(many=True, required=False, read_only=True) class Meta: model = DocumentVersion @@ -25,6 +25,7 @@ class DocumentImageSerializer(serializers.Serializer): class DocumentSerializer(serializers.HyperlinkedModelSerializer): versions = DocumentVersionSerializer(many=True, read_only=True) image = serializers.HyperlinkedIdentityField(view_name='document-image') + new_version = serializers.HyperlinkedIdentityField(view_name='document-new-version') class Meta: model = Document diff --git a/mayan/apps/documents/urls.py b/mayan/apps/documents/urls.py index b5d44a653b..9d36cc6cc4 100644 --- a/mayan/apps/documents/urls.py +++ b/mayan/apps/documents/urls.py @@ -3,7 +3,8 @@ from __future__ import absolute_import from django.conf.urls import patterns, url from .api_views import (APIDocumentView, APIDocumentImageView, APIDocumentListView, - APIDocumentPageView, APIDocumentVersionView) + APIDocumentPageView, APIDocumentVersionCreateView, + APIDocumentVersionView) from .conf.settings import PRINT_SIZE, DISPLAY_SIZE urlpatterns = patterns('documents.views', @@ -74,4 +75,5 @@ api_urls = patterns('', url(r'^document_version/(?P[0-9]+)/$', APIDocumentVersionView.as_view(), name='documentversion-detail'), url(r'^document_page/(?P[0-9]+)/$', APIDocumentPageView.as_view(), name='documentpage-detail'), url(r'^documents/(?P[0-9]+)/image/$', APIDocumentImageView.as_view(), name='document-image'), + url(r'^documents/(?P[0-9]+)/new_version/$', APIDocumentVersionCreateView.as_view(), name='document-new-version'), ) From bd69ed00a5a75fe4d4b1cffe8f205d6da35e8c4e Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Tue, 22 Jul 2014 02:32:39 -0400 Subject: [PATCH 05/20] Add missing db_index=True option, ref: 5e21b91b24c9972da0956ab599f535466d73a168 --- mayan/apps/documents/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mayan/apps/documents/models.py b/mayan/apps/documents/models.py index 3bf44d051d..d64b200b26 100644 --- a/mayan/apps/documents/models.py +++ b/mayan/apps/documents/models.py @@ -316,7 +316,7 @@ class DocumentVersion(models.Model): micro = models.PositiveIntegerField(verbose_name=_(u'micro'), default=0) release_level = models.PositiveIntegerField(choices=RELEASE_LEVEL_CHOICES, default=RELEASE_LEVEL_FINAL, verbose_name=_(u'release level')) serial = models.PositiveIntegerField(verbose_name=_(u'serial'), default=0) - timestamp = models.DateTimeField(verbose_name=_(u'timestamp'), editable=False) + timestamp = models.DateTimeField(verbose_name=_(u'timestamp'), editable=False, db_index=True) comment = models.TextField(blank=True, verbose_name=_(u'comment')) # File related fields From 5d0b477e8feba0b0757562d05d25d69460365e2b Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Tue, 22 Jul 2014 02:34:12 -0400 Subject: [PATCH 06/20] Allow documents with no versions to exist, previously these were considered an anomaly and were unhandled --- mayan/apps/document_signatures/managers.py | 22 +++++++++------ mayan/apps/document_signatures/views.py | 23 +++++++++------ mayan/apps/documents/__init__.py | 2 +- mayan/apps/documents/forms.py | 27 +++++++++++++----- mayan/apps/documents/views.py | 33 ++++++++++++++-------- mayan/apps/documents/widgets.py | 11 ++++++-- 6 files changed, 80 insertions(+), 38 deletions(-) diff --git a/mayan/apps/document_signatures/managers.py b/mayan/apps/document_signatures/managers.py index 6f0fbc888a..4898df1828 100644 --- a/mayan/apps/document_signatures/managers.py +++ b/mayan/apps/document_signatures/managers.py @@ -32,19 +32,25 @@ class DocumentVersionSignatureManager(models.Manager): document_signature.save() def has_detached_signature(self, document): - document_signature = self.get_document_signature(document) - - if document_signature.signature_file: - return True - else: + try: + document_signature = self.get_document_signature(document) + except ValueError: return False + else: + if document_signature.signature_file: + return True + else: + return False def has_embedded_signature(self, document): logger.debug('document: %s' % document) - document_signature = self.get_document_signature(document) - - return document_signature.has_embedded_signature + try: + document_signature = self.get_document_signature(document) + except ValueError: + return False + else: + return document_signature.has_embedded_signature def detached_signature(self, document): document_signature = self.get_document_signature(document) diff --git a/mayan/apps/document_signatures/views.py b/mayan/apps/document_signatures/views.py index 132db4b8e8..14d1a550aa 100644 --- a/mayan/apps/document_signatures/views.py +++ b/mayan/apps/document_signatures/views.py @@ -15,7 +15,7 @@ from django.utils.translation import ugettext_lazy as _ from acls.models import AccessEntry from filetransfers.api import serve_file -from django_gpg.api import SIGNATURE_STATES +from django_gpg.api import SIGNATURE_STATE_NONE, SIGNATURE_STATES from documents.models import Document, RecentDocument from permissions.models import Permission @@ -37,9 +37,13 @@ def document_verify(request, document_pk): RecentDocument.objects.add_document_for_user(request.user, document) - signature = DocumentVersionSignature.objects.verify_signature(document) - - signature_state = SIGNATURE_STATES.get(getattr(signature, 'status', None)) + try: + signature = DocumentVersionSignature.objects.verify_signature(document) + except AttributeError: + signature_state = SIGNATURE_STATES.get(SIGNATURE_STATE_NONE) + signature = None + else: + signature_state = SIGNATURE_STATES.get(getattr(signature, 'status', None)) widget = (u'' % (settings.STATIC_URL, signature_state['icon'])) paragraphs = [ @@ -49,10 +53,13 @@ def document_verify(request, document_pk): }, ] - if DocumentVersionSignature.objects.has_embedded_signature(document): - signature_type = _(u'embedded') - else: - signature_type = _(u'detached') + try: + if DocumentVersionSignature.objects.has_embedded_signature(document): + signature_type = _(u'embedded') + else: + signature_type = _(u'detached') + except ValueError: + signature_type = _(u'None') if signature: paragraphs.extend( diff --git a/mayan/apps/documents/__init__.py b/mayan/apps/documents/__init__.py index d67ffef569..6e93f6a158 100644 --- a/mayan/apps/documents/__init__.py +++ b/mayan/apps/documents/__init__.py @@ -100,7 +100,7 @@ register_maintenance_links([document_find_all_duplicates, document_update_page_c register_model_list_columns(Document, [ { 'name': _(u'thumbnail'), 'attribute': - encapsulate(lambda x: document_thumbnail(x, gallery_name='document_list', title=x.filename, size=THUMBNAIL_SIZE)) + encapsulate(lambda x: document_thumbnail(x, gallery_name='document_list', title=getattr(x, 'filename', None), size=THUMBNAIL_SIZE)) }, ]) diff --git a/mayan/apps/documents/forms.py b/mayan/apps/documents/forms.py index 015971bd31..4141a06f08 100644 --- a/mayan/apps/documents/forms.py +++ b/mayan/apps/documents/forms.py @@ -86,7 +86,10 @@ class DocumentPreviewForm(forms.Form): document = kwargs.pop('document', None) super(DocumentPreviewForm, self).__init__(*args, **kwargs) self.fields['preview'].initial = document - self.fields['preview'].label = _(u'Document pages (%s)') % document.pages.count() + try: + self.fields['preview'].label = _(u'Document pages (%d)') % document.pages.count() + except AttributeError: + self.fields['preview'].label = _(u'Document pages (%d)') % 0 preview = forms.CharField(widget=DocumentPagesCarouselWidget()) @@ -131,7 +134,8 @@ class DocumentForm(forms.ModelForm): label=_(u'Quick document rename')) if instance: - self.version_fields(instance) + if instance.latest_version: + self.version_fields(instance) def version_fields(self, document): self.fields['version_update'] = forms.ChoiceField( @@ -186,10 +190,14 @@ class DocumentForm_edit(DocumentForm): def __init__(self, *args, **kwargs): super(DocumentForm_edit, self).__init__(*args, **kwargs) - self.fields.pop('serial') - self.fields.pop('release_level') - self.fields.pop('version_update') - self.fields.pop('comment') + if kwargs['instance'].latest_version: + self.fields.pop('serial') + self.fields.pop('release_level') + self.fields.pop('version_update') + self.fields.pop('comment') + else: + self.fields.pop('new_filename') + self.fields.pop('use_file_name') @@ -212,7 +220,12 @@ class DocumentContentForm(forms.Form): super(DocumentContentForm, self).__init__(*args, **kwargs) content = [] self.fields['contents'].initial = u'' - for page in self.document.pages.all(): + try: + document_pages = self.document.pages.all() + except AttributeError: + document_pages = [] + + for page in document_pages: if page.content: content.append(conditional_escape(force_unicode(page.content))) content.append(u'\n\n\n
- %s %s -

\n\n\n' % (ugettext(u'Page'), page.page_number)) diff --git a/mayan/apps/documents/views.py b/mayan/apps/documents/views.py index b7c8ad928e..ba735e6aab 100644 --- a/mayan/apps/documents/views.py +++ b/mayan/apps/documents/views.py @@ -96,19 +96,24 @@ def document_view(request, document_id, advanced=False): subtemplates_list = [] if advanced: - document_properties_form = DocumentPropertiesForm(instance=document, extra_fields=[ - {'label': _(u'Filename'), 'field': 'filename'}, - {'label': _(u'File mimetype'), 'field': lambda x: x.file_mimetype or _(u'None')}, - {'label': _(u'File mime encoding'), 'field': lambda x: x.file_mime_encoding or _(u'None')}, - {'label': _(u'File size'), 'field': lambda x: pretty_size(x.size) if x.size else '-'}, - {'label': _(u'Exists in storage'), 'field': 'exists'}, - {'label': _(u'File path in storage'), 'field': 'file'}, + document_fields = [ {'label': _(u'Date added'), 'field': lambda x: x.date_added.date()}, {'label': _(u'Time added'), 'field': lambda x: unicode(x.date_added.time()).split('.')[0]}, - {'label': _(u'Checksum'), 'field': 'checksum'}, {'label': _(u'UUID'), 'field': 'uuid'}, - {'label': _(u'Pages'), 'field': 'page_count'}, - ]) + ] + if document.latest_version: + document_fields.extend([ + {'label': _(u'Filename'), 'field': 'filename'}, + {'label': _(u'File mimetype'), 'field': lambda x: x.file_mimetype or _(u'None')}, + {'label': _(u'File mime encoding'), 'field': lambda x: x.file_mime_encoding or _(u'None')}, + {'label': _(u'File size'), 'field': lambda x: pretty_size(x.size) if x.size else '-'}, + {'label': _(u'Exists in storage'), 'field': 'exists'}, + {'label': _(u'File path in storage'), 'field': 'file'}, + {'label': _(u'Checksum'), 'field': 'checksum'}, + {'label': _(u'Pages'), 'field': 'page_count'}, + ]) + + document_properties_form = DocumentPropertiesForm(instance=document, extra_fields=document_fields) subtemplates_list.append( { @@ -235,8 +240,12 @@ def document_edit(request, document_id): return HttpResponseRedirect(document.get_absolute_url()) else: - form = DocumentForm_edit(instance=document, initial={ - 'new_filename': document.filename, 'description': document.description}) + if document.latest_version: + form = DocumentForm_edit(instance=document, initial={ + 'new_filename': document.filename, 'description': document.description}) + else: + form = DocumentForm_edit(instance=document, initial={ + 'description': document.description}) return render_to_response('generic_form.html', { 'form': form, diff --git a/mayan/apps/documents/widgets.py b/mayan/apps/documents/widgets.py index 2a6d7d7a4c..91d167f5f9 100644 --- a/mayan/apps/documents/widgets.py +++ b/mayan/apps/documents/widgets.py @@ -38,8 +38,12 @@ class DocumentPagesCarouselWidget(forms.widgets.Widget): output = [] output.append(u'