Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -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))
|
||||
},
|
||||
])
|
||||
|
||||
|
||||
@@ -3,25 +3,29 @@ from __future__ import absolute_import
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
from rest_framework import generics, status
|
||||
from rest_framework.response import Response
|
||||
|
||||
from acls.models import AccessEntry
|
||||
from converter.exceptions import UnkownConvertError, UnknownFileFormat
|
||||
from converter.literals import (DEFAULT_PAGE_NUMBER, DEFAULT_ROTATION,
|
||||
DEFAULT_ZOOM_LEVEL)
|
||||
from permissions.models import Permission
|
||||
from rest_framework import generics
|
||||
from rest_framework.response import Response
|
||||
|
||||
from acls.models import AccessEntry
|
||||
from rest_api.filters import MayanObjectPermissionsFilter
|
||||
from rest_api.permissions import MayanPermission
|
||||
|
||||
from .conf.settings import DISPLAY_SIZE, ZOOM_MAX_LEVEL, ZOOM_MIN_LEVEL
|
||||
from .permissions import PERMISSION_DOCUMENT_VIEW
|
||||
from .models import Document, DocumentPage, DocumentVersion
|
||||
from .permissions import (PERMISSION_DOCUMENT_CREATE,
|
||||
PERMISSION_DOCUMENT_DELETE, PERMISSION_DOCUMENT_EDIT,
|
||||
PERMISSION_DOCUMENT_NEW_VERSION,
|
||||
PERMISSION_DOCUMENT_PROPERTIES_EDIT,
|
||||
PERMISSION_DOCUMENT_VIEW)
|
||||
from .serializers import (DocumentImageSerializer, DocumentPageSerializer,
|
||||
DocumentSerializer, DocumentVersionSerializer)
|
||||
|
||||
|
||||
class APIDocumentListView(generics.ListAPIView):
|
||||
class APIDocumentListView(generics.ListCreateAPIView):
|
||||
"""
|
||||
Returns a list of all the documents.
|
||||
"""
|
||||
@@ -29,35 +33,75 @@ class APIDocumentListView(generics.ListAPIView):
|
||||
serializer_class = DocumentSerializer
|
||||
queryset = Document.objects.all()
|
||||
|
||||
filter_backends = (MayanObjectPermissionsFilter,)
|
||||
mayan_object_permissions = [PERMISSION_DOCUMENT_VIEW]
|
||||
|
||||
|
||||
class APIDocumentPageView(generics.RetrieveAPIView):
|
||||
"""
|
||||
Returns the selected document page details.
|
||||
"""
|
||||
|
||||
allowed_methods = ['GET']
|
||||
serializer_class = DocumentPageSerializer
|
||||
queryset = DocumentPage.objects.all()
|
||||
|
||||
permission_classes = (MayanPermission,)
|
||||
mayan_object_permissions = [PERMISSION_DOCUMENT_VIEW]
|
||||
mayan_permission_attribute_check = 'document'
|
||||
filter_backends = (MayanObjectPermissionsFilter,)
|
||||
mayan_object_permissions = {'GET': [PERMISSION_DOCUMENT_VIEW]}
|
||||
mayan_view_permissions = {'POST': [PERMISSION_DOCUMENT_CREATE]}
|
||||
|
||||
|
||||
class APIDocumentView(generics.RetrieveAPIView):
|
||||
class APIDocumentView(generics.RetrieveUpdateDestroyAPIView):
|
||||
"""
|
||||
Returns the selected document details.
|
||||
"""
|
||||
|
||||
allowed_methods = ['GET']
|
||||
serializer_class = DocumentSerializer
|
||||
queryset = Document.objects.all()
|
||||
|
||||
permission_classes = (MayanPermission,)
|
||||
mayan_object_permissions = [PERMISSION_DOCUMENT_VIEW]
|
||||
mayan_object_permissions = {
|
||||
'GET': [PERMISSION_DOCUMENT_VIEW],
|
||||
'PUT': [PERMISSION_DOCUMENT_PROPERTIES_EDIT],
|
||||
'PATCH': [PERMISSION_DOCUMENT_PROPERTIES_EDIT],
|
||||
'DELETE': [PERMISSION_DOCUMENT_DELETE]
|
||||
}
|
||||
|
||||
|
||||
class APIDocumentVersionCreateView(generics.CreateAPIView):
|
||||
"""
|
||||
Create a new document version.
|
||||
"""
|
||||
|
||||
serializer_class = DocumentVersionSerializer
|
||||
queryset = DocumentVersion.objects.all()
|
||||
|
||||
permission_classes = (MayanPermission,)
|
||||
mayan_view_permissions = {'POST': [PERMISSION_DOCUMENT_NEW_VERSION]}
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
serializer = self.get_serializer(data=request.DATA, files=request.FILES)
|
||||
|
||||
if serializer.is_valid():
|
||||
self.pre_save(serializer.object)
|
||||
# Nested resource we take the document pk from the URL and insert it
|
||||
# so that it needs not to be specified by the user, we mark it as
|
||||
# a read only field in the serializer
|
||||
serializer.object.document = get_object_or_404(Document, pk=kwargs['pk'])
|
||||
|
||||
try:
|
||||
# Check the uniqueness of this version for this document instead
|
||||
# of letting Django explode with an IntegrityError
|
||||
DocumentVersion.objects.get(
|
||||
document=serializer.object.document,
|
||||
major=serializer.object.major,
|
||||
minor=serializer.object.minor,
|
||||
micro=serializer.object.micro,
|
||||
release_level=serializer.object.release_level,
|
||||
serial=serializer.object.serial
|
||||
)
|
||||
except DocumentVersion.DoesNotExist:
|
||||
self.object = serializer.save(force_insert=True)
|
||||
else:
|
||||
return Response(
|
||||
{'non_field_errors': 'A version with the same major, minor, micro, release_level and serial values already exist for this document.'},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
self.post_save(self.object, created=True)
|
||||
headers = self.get_success_headers(serializer.data)
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED,
|
||||
headers=headers)
|
||||
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
class APIDocumentVersionView(generics.RetrieveAPIView):
|
||||
@@ -70,7 +114,7 @@ class APIDocumentVersionView(generics.RetrieveAPIView):
|
||||
queryset = DocumentVersion.objects.all()
|
||||
|
||||
permission_classes = (MayanPermission,)
|
||||
mayan_object_permissions = [PERMISSION_DOCUMENT_VIEW]
|
||||
mayan_object_permissions = {'GET': [PERMISSION_DOCUMENT_VIEW]}
|
||||
mayan_permission_attribute_check = 'document'
|
||||
|
||||
|
||||
@@ -120,3 +164,20 @@ class APIDocumentImageView(generics.GenericAPIView):
|
||||
return Response({'status': 'error', 'detail': 'unknown_file_format', 'message': unicode(exception)})
|
||||
except UnkownConvertError as exception:
|
||||
return Response({'status': 'error', 'detail': 'converter_error', 'message': unicode(exception)})
|
||||
|
||||
|
||||
class APIDocumentPageView(generics.RetrieveUpdateAPIView):
|
||||
"""
|
||||
Returns the selected document page details.
|
||||
"""
|
||||
|
||||
serializer_class = DocumentPageSerializer
|
||||
queryset = DocumentPage.objects.all()
|
||||
|
||||
permission_classes = (MayanPermission,)
|
||||
mayan_object_permissions = {
|
||||
'GET': [PERMISSION_DOCUMENT_VIEW],
|
||||
'PUT': [PERMISSION_DOCUMENT_EDIT],
|
||||
'PATCH': [PERMISSION_DOCUMENT_EDIT]
|
||||
}
|
||||
mayan_permission_attribute_check = 'document'
|
||||
|
||||
@@ -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<hr/><div style="text-align: center;">- %s %s -</div><hr/>\n\n\n' % (ugettext(u'Page'), page.page_number))
|
||||
|
||||
@@ -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']
|
||||
complete_apps = ['documents']
|
||||
|
||||
@@ -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']
|
||||
@@ -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,13 +310,13 @@ 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)
|
||||
timestamp = models.DateTimeField(verbose_name=_(u'timestamp'), 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, db_index=True)
|
||||
comment = models.TextField(blank=True, verbose_name=_(u'comment'))
|
||||
|
||||
# File related fields
|
||||
|
||||
@@ -11,10 +11,11 @@ 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
|
||||
read_only_fields = ('document',)
|
||||
|
||||
|
||||
class DocumentImageSerializer(serializers.Serializer):
|
||||
@@ -25,6 +26,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
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from json import loads
|
||||
import os
|
||||
|
||||
from django.conf import settings
|
||||
@@ -9,6 +10,9 @@ from django.core.urlresolvers import reverse
|
||||
from django.test.client import Client
|
||||
from django.test import TestCase
|
||||
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from .literals import VERSION_UPDATE_MAJOR, RELEASE_LEVEL_FINAL
|
||||
from .models import Document, DocumentType
|
||||
|
||||
@@ -95,11 +99,13 @@ class DocumentSearchTestCase(TestCase):
|
||||
parse_document_page(self.document.latest_version.pages.all()[0])
|
||||
|
||||
def test_simple_search_after_related_name_change(self):
|
||||
from . import document_search
|
||||
"""
|
||||
Test that simple search works after related_name changes to
|
||||
document versions and document version pages
|
||||
"""
|
||||
|
||||
from . import document_search
|
||||
|
||||
model_list, flat_list, shown_result_count, result_count, elapsed_time = document_search.simple_search('Mayan')
|
||||
self.assertEqual(result_count, 1)
|
||||
self.assertEqual(flat_list, [self.document])
|
||||
@@ -128,6 +134,11 @@ class DocumentSearchTestCase(TestCase):
|
||||
|
||||
|
||||
class DocumentUploadFunctionalTestCase(TestCase):
|
||||
"""
|
||||
Functional test to make sure all the moving parts to create a document from
|
||||
the frontend are working correctly
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.admin_user = User.objects.create_superuser(username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL, password=TEST_ADMIN_PASSWORD)
|
||||
self.client = Client()
|
||||
@@ -163,3 +174,73 @@ class DocumentUploadFunctionalTestCase(TestCase):
|
||||
# Delete the document
|
||||
response = self.client.post(reverse('document_delete', args=[self.document.pk]))
|
||||
self.assertEqual(Document.objects.count(), 0)
|
||||
|
||||
|
||||
class DocumentAPICreateDocumentTestCase(TestCase):
|
||||
"""
|
||||
Functional test to make sure all the moving parts to create a document from
|
||||
the API are working correctly
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.admin_user = User.objects.create_superuser(username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL, password=TEST_ADMIN_PASSWORD)
|
||||
|
||||
def test_uploading_a_document_using_token_auth(self):
|
||||
# Get the an user token
|
||||
token_client = APIClient()
|
||||
response = token_client.post(reverse('auth_token_obtain'), {'username': TEST_ADMIN_USERNAME, 'password': TEST_ADMIN_PASSWORD})
|
||||
|
||||
# Be able to get authentication token
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
# Make a token was returned
|
||||
self.assertTrue(u'token' in response.content)
|
||||
|
||||
token = loads(response.content)['token']
|
||||
|
||||
# Create a new client to simulate a different request
|
||||
document_client = APIClient()
|
||||
|
||||
# Create a blank document with no token in the header
|
||||
response = document_client.post(reverse('document-list'), {'description': 'test document'})
|
||||
|
||||
# Make sure toke authentication is working, should fail
|
||||
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
|
||||
|
||||
document_client.credentials(HTTP_AUTHORIZATION='Token ' + token)
|
||||
|
||||
# Create a blank document
|
||||
document_response = document_client.post(reverse('document-list'), {'description': 'test document'})
|
||||
self.assertEqual(document_response.status_code, status.HTTP_201_CREATED)
|
||||
|
||||
# The document was created in the DB?
|
||||
self.assertEqual(Document.objects.count(), 1)
|
||||
|
||||
new_version_url = loads(document_response.content)['new_version']
|
||||
|
||||
with open(TEST_DOCUMENT_PATH) as file_descriptor:
|
||||
response = document_client.post(new_version_url, {'file': file_descriptor})
|
||||
|
||||
# Make sure the document uploaded correctly
|
||||
document = Document.objects.first()
|
||||
self.failUnlessEqual(document.exists(), True)
|
||||
self.failUnlessEqual(document.size, 272213)
|
||||
|
||||
self.failUnlessEqual(document.file_mimetype, 'application/pdf')
|
||||
self.failUnlessEqual(document.file_mime_encoding, 'binary')
|
||||
self.failUnlessEqual(document.file_filename, 'mayan_11_1.pdf')
|
||||
self.failUnlessEqual(document.checksum, 'c637ffab6b8bb026ed3784afdb07663fddc60099853fae2be93890852a69ecf3')
|
||||
self.failUnlessEqual(document.page_count, 47)
|
||||
|
||||
# Make sure we can edit the document via the API
|
||||
document_url = loads(document_response.content)['url']
|
||||
|
||||
response = document_client.post(document_url, {'description': 'edited test document'})
|
||||
|
||||
self.assertTrue(document.description, 'edited test document')
|
||||
|
||||
# Make sure we can delete the document via the API
|
||||
response = document_client.delete(document_url)
|
||||
|
||||
# The document was deleted from the the DB?
|
||||
self.assertEqual(Document.objects.count(), 0)
|
||||
|
||||
@@ -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<pk>[0-9]+)/$', APIDocumentVersionView.as_view(), name='documentversion-detail'),
|
||||
url(r'^document_page/(?P<pk>[0-9]+)/$', APIDocumentPageView.as_view(), name='documentpage-detail'),
|
||||
url(r'^documents/(?P<pk>[0-9]+)/image/$', APIDocumentImageView.as_view(), name='document-image'),
|
||||
url(r'^documents/(?P<pk>[0-9]+)/new_version/$', APIDocumentVersionCreateView.as_view(), name='document-new-version'),
|
||||
)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -38,8 +38,12 @@ class DocumentPagesCarouselWidget(forms.widgets.Widget):
|
||||
output = []
|
||||
output.append(u'<div class="carousel-container" style="white-space:nowrap; overflow: auto;">')
|
||||
|
||||
for page in value.pages.all():
|
||||
try:
|
||||
document_pages = value.pages.all()
|
||||
except AttributeError:
|
||||
document_pages = []
|
||||
|
||||
for page in document_pages:
|
||||
output.append(u'<div style="display: inline-block; margin: 5px 10px 10px 10px;">')
|
||||
output.append(u'<div class="tc">%(page_string)s %(page)s</div>' % {'page_string': ugettext(u'Page'), 'page': page.page_number})
|
||||
output.append(
|
||||
@@ -77,7 +81,10 @@ def document_html_widget(document, click_view=None, page=DEFAULT_PAGE_NUMBER, zo
|
||||
alt_text = _(u'document page image')
|
||||
|
||||
if not version:
|
||||
version = document.latest_version.pk
|
||||
try:
|
||||
version = document.latest_version.pk
|
||||
except AttributeError:
|
||||
version = None
|
||||
|
||||
query_dict = {
|
||||
'page': page,
|
||||
|
||||
Reference in New Issue
Block a user