diff --git a/apps/documents/forms.py b/apps/documents/forms.py index 47ac8215da..1033692bfe 100644 --- a/apps/documents/forms.py +++ b/apps/documents/forms.py @@ -11,9 +11,11 @@ from common.conf.settings import DEFAULT_PAPER_SIZE from common.conf.settings import DEFAULT_PAGE_ORIENTATION from common.widgets import TextAreaDiv -from documents.models import Document, DocumentType, \ - DocumentPage, DocumentPageTransformation, DocumentTypeFilename +from documents.models import (Document, DocumentType, + DocumentPage, DocumentPageTransformation, DocumentTypeFilename, + DocumentVersion) from documents.widgets import document_html_widget +from documents.literals import (RELEASE_LEVEL_FINAL, RELEASE_LEVEL_CHOICES) # Document page forms class DocumentPageTransformationForm(forms.ModelForm): @@ -147,6 +149,8 @@ class DocumentForm(forms.ModelForm): instance = kwargs.pop('instance', None) super(DocumentForm, self).__init__(*args, **kwargs) + if instance: + self.version_fields(instance) if 'document_type' in self.fields: # To allow merging with DocumentForm_edit @@ -164,11 +168,51 @@ class DocumentForm(forms.ModelForm): queryset=filenames_qs, required=False, label=_(u'Quick document rename')) + + def version_fields(self, document): + self.fields['comment'] = forms.CharField( + label=_(u'Comment'), + required=False, + widget=forms.widgets.Textarea(attrs={'rows': 4}), + ) + + self.fields['version_update'] = forms.ChoiceField( + label=_(u'Version update'), + #widget=forms.widgets.RadioSelect(), + choices=DocumentVersion.get_version_update_choices(document.latest_version) + ) + + self.fields['release_level'] = forms.ChoiceField( + label=_(u'Release level'), + choices=RELEASE_LEVEL_CHOICES, + initial=RELEASE_LEVEL_FINAL, + #required=False, + ) + + self.fields['serial'] = forms.IntegerField( + label=_(u'Release level serial'), + initial=0, + widget=forms.widgets.TextInput( + attrs = {'style': 'width: auto;'} + ), + #required=False + ) new_filename = forms.CharField( label=_('New document filename'), required=False ) + + def clean(self): + cleaned_data = self.cleaned_data + cleaned_data['new_version_data'] = { + 'comment': self.cleaned_data.get('comment'), + 'version_update': self.cleaned_data.get('version_update'), + 'release_level': self.cleaned_data.get('release_level'), + 'serial': self.cleaned_data.get('serial'), + } + # Always return the full collection of cleaned data. + return cleaned_data class DocumentForm_edit(DocumentForm): """ @@ -177,6 +221,13 @@ class DocumentForm_edit(DocumentForm): class Meta: model = Document exclude = ('file', 'document_type', 'tags') + + 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') class DocumentPropertiesForm(DetailForm): diff --git a/apps/documents/literals.py b/apps/documents/literals.py index cbb3b919d9..30b0284f5f 100644 --- a/apps/documents/literals.py +++ b/apps/documents/literals.py @@ -44,3 +44,21 @@ HISTORY_DOCUMENT_DELETED = { 'details': _(u'Document "%(document)s" deleted on %(datetime)s by %(fullname)s.'), 'expressions': {'fullname': 'user.get_full_name() if user.get_full_name() else user.username'} } + +RELEASE_LEVEL_FINAL = 1 +RELEASE_LEVEL_ALPHA = 2 +RELEASE_LEVEL_BETA = 3 +RELEASE_LEVEL_RC = 4 +RELEASE_LEVEL_HF = 5 + +RELEASE_LEVEL_CHOICES = ( + (RELEASE_LEVEL_FINAL, _(u'final')), + (RELEASE_LEVEL_ALPHA, _(u'alpha')), + (RELEASE_LEVEL_BETA, _(u'beta')), + (RELEASE_LEVEL_RC, _(u'release candidate')), + (RELEASE_LEVEL_HF, _(u'hotfix')), +) + +VERSION_UPDATE_MAJOR = u'major' +VERSION_UPDATE_MINOR = u'minor' +VERSION_UPDATE_MICRO = u'micro' diff --git a/apps/documents/models.py b/apps/documents/models.py index ab1165bf5f..04c633556f 100644 --- a/apps/documents/models.py +++ b/apps/documents/models.py @@ -4,6 +4,8 @@ import hashlib from ast import literal_eval import base64 from StringIO import StringIO +import datetime +import logging from django.db import models from django.utils.translation import ugettext_lazy as _ @@ -21,6 +23,8 @@ from converter.api import convert from converter.exceptions import UnknownFileFormat, UnkownConvertError from mimetype.api import get_mimetype, get_icon_file_path, \ get_error_icon_file_path +from converter.literals import (DEFAULT_ZOOM_LEVEL, DEFAULT_ROTATION, + DEFAULT_PAGE_NUMBER) from documents.conf.settings import CHECKSUM_FUNCTION from documents.conf.settings import UUID_FUNCTION @@ -30,26 +34,25 @@ from documents.conf.settings import DISPLAY_SIZE from documents.conf.settings import CACHE_PATH from documents.conf.settings import ZOOM_MAX_LEVEL from documents.conf.settings import ZOOM_MIN_LEVEL - from documents.managers import RecentDocumentManager, \ DocumentPageTransformationManager from documents.utils import document_save_to_temp_dir -from converter.literals import DEFAULT_ZOOM_LEVEL, DEFAULT_ROTATION, \ - DEFAULT_PAGE_NUMBER +from documents.literals import (RELEASE_LEVEL_FINAL, RELEASE_LEVEL_CHOICES, + VERSION_UPDATE_MAJOR, VERSION_UPDATE_MINOR, VERSION_UPDATE_MICRO) # document image cache name hash function HASH_FUNCTION = lambda x: hashlib.sha256(x).hexdigest() +logger = logging.getLogger(__name__) + def get_filename_from_uuid(instance, filename): """ Store the orignal filename of the uploaded file and replace it with a UUID """ - instance.file_filename = filename - uuid = UUID_FUNCTION() - instance.uuid = uuid - return uuid + instance.filename = filename + return UUID_FUNCTION() class DocumentType(models.Model): @@ -69,24 +72,15 @@ class DocumentType(models.Model): class Document(models.Model): - """ + ''' Defines a single document with it's fields and properties - """ - # Base fields + ''' + uuid = models.CharField(max_length=48, blank=True, editable=False) document_type = models.ForeignKey(DocumentType, verbose_name=_(u'document type'), null=True, blank=True) - uuid = models.CharField(max_length=48, default=UUID_FUNCTION(), blank=True, editable=False) description = models.TextField(blank=True, null=True, verbose_name=_(u'description'), db_index=True) + #TODO: remove date_added, it is the timestamp of the first version date_added = models.DateTimeField(verbose_name=_(u'added'), auto_now_add=True, db_index=True) - ## Fields to migrate - #file = models.FileField(upload_to=get_filename_from_uuid, storage=STORAGE_BACKEND(), verbose_name=_(u'file')) - #file_mimetype = models.CharField(max_length=64, default='', editable=False) - #file_mime_encoding = models.CharField(max_length=64, default='', editable=False) - ##FAT filename can be up to 255 using LFN - #file_filename = models.CharField(max_length=255, default=u'', editable=False, db_index=True) - #date_updated = models.DateTimeField(verbose_name=_(u'updated'), auto_now=True) - #checksum = models.TextField(blank=True, null=True, verbose_name=_(u'checksum'), editable=False) - tags = TaggableManager() comments = generic.GenericRelation( @@ -108,147 +102,16 @@ class Document(models.Model): ordering = ['-date_added'] def __unicode__(self): - return self.get_fullname() - - def save(self, *args, **kwargs): - """ - Overloaded save method that updates the document's checksum, - mimetype, page count and transformation when originally created - """ - new_document = not self.pk - transformations = kwargs.pop('transformations', None) - super(Document, self).save(*args, **kwargs) - - if new_document: - #Only do this for new documents - self.update_checksum(save=False) - self.update_mimetype(save=False) - self.save() - self.update_page_count(save=False) - if transformations: - self.apply_default_transformations(transformations) + return self.latest_version.filename @models.permalink def get_absolute_url(self): return ('document_view_simple', [self.pk]) - def get_fullname(self): - """ - Return the fullname of the document's file - """ - return self.file_filename - - def update_mimetype(self, save=True): - """ - Read a document's file and determine the mimetype by calling the - get_mimetype wrapper - """ - if self.exists(): - try: - self.file_mimetype, self.file_mime_encoding = get_mimetype(self.open(), self.get_fullname()) - except: - self.file_mimetype = u'' - self.file_mime_encoding = u'' - finally: - if save: - self.save() - - def open(self): - """ - Return a file descriptor to a document's file irrespective of - the storage backend - """ - #return self.file.storage.open(self.file.path) - return self.get_latest_version().file.storage.open(self.get_latest_version().file.path) - - def update_checksum(self, save=True): - """ - Open a document's file and update the checksum field using the - user provided checksum function - """ - if self.exists(): - source = self.get_latest_version().open() - self.get_latest_version().checksum = unicode(CHECKSUM_FUNCTION(source.read())) - source.close() - if save: - self.get_latest_version().save() - - def update_page_count(self, save=True): - handle, filepath = tempfile.mkstemp() - # Just need the filepath, close the file description - os.close(handle) - - self.save_to_file(filepath) - try: - detected_pages = get_page_count(filepath) - except UnknownFileFormat: - # If converter backend doesn't understand the format, - # use 1 as the total page count - detected_pages = 1 - self.description = ugettext(u'This document\'s file format is not known, the page count has therefore defaulted to 1.') - self.save() - try: - os.remove(filepath) - except OSError: - pass - - current_pages = DocumentPage.objects.filter(document=self).order_by('page_number',) - if current_pages.count() > detected_pages: - for page in current_pages[detected_pages:]: - page.delete() - - for page_number in range(detected_pages): - DocumentPage.objects.get_or_create( - document=self, page_number=page_number + 1) - - if save: - self.save() - - return detected_pages - - @property - def page_count(self): - #return self.documentpage_set.count() - return self.get_latest_version().documentpage_set.count() - - def save_to_file(self, filepath, buffer_size=1024 * 1024): - """ - Save a copy of the document from the document storage backend - to the local filesystem - """ - input_descriptor = self.open() - output_descriptor = open(filepath, 'wb') - while True: - copy_buffer = input_descriptor.read(buffer_size) - if copy_buffer: - output_descriptor.write(copy_buffer) - else: - break - - output_descriptor.close() - input_descriptor.close() - return filepath - - def exists(self): - """ - Returns a boolean value that indicates if the document's file - exists in storage - """ - return self.get_latest_version().file.storage.exists(self.get_latest_version().file.path) - - def apply_default_transformations(self, transformations): - #Only apply default transformations on new documents - if reduce(lambda x, y: x + y, [page.documentpagetransformation_set.count() for page in self.pages.all()]) == 0: - for transformation in transformations: - for document_page in self.pages.all(): - page_transformation = DocumentPageTransformation( - document_page=document_page, - order=0, - transformation=transformation.get('transformation'), - arguments=transformation.get('arguments') - ) - - page_transformation.save() + def save(self, *args, **kwargs): + if not self.pk: + self.uuid = UUID_FUNCTION() + super(Document, self).save(*args, **kwargs) def get_cached_image_name(self, page): document_page = self.pages.get(page_number=page) @@ -302,95 +165,154 @@ class Document(models.Model): def add_as_recent_document_for_user(self, user): RecentDocument.objects.add_document_for_user(user, self) - - def delete(self, *args, **kwargs): - super(Document, self).delete(*args, **kwargs) - for version in self.documentversion_set.all(): - version.file.storage.delete(version.file.path) - #return self.get_latest_version().file.storage.delete(self.get_latest_version().file.path) + + # TODO: investigate if Document's save method calls all of it + # DocumentVersion's delete methods + #def delete(self, *args, **kwargs): + # super(Document, self).delete(*args, **kwargs) + # for version in self.documentversion_set.all(): + # version.file.storage.delete(version.file.path) @property def size(self): if self.exists(): - return self.get_latest_version().file.storage.size(self.get_latest_version().file.path) + return self.latest_version.exists() else: return None + + def new_version(self, file, comment=None, version_update=None, release_level=None, serial=None): + logger.debug('creating new document version') + if version_update: + new_version_dict = self.latest_version.get_new_version_dict(version_update) + new_version = DocumentVersion( + document=self, + file=file, + major = new_version_dict.get('major'), + minor = new_version_dict.get('minor'), + micro = new_version_dict.get('micro'), + release_level = release_level, + serial = serial, + comment = comment, + ) + new_version.save() + else: + new_version_dict = {} + new_version = DocumentVersion( + document=self, + file=file, + #major = new_version_dict.get('major'), + #minor = new_version_dict.get('minor'), + #micro = new_version_dict.get('micro'), + #release_level = release_level, + #serial = serial, + #comment = comment, + ) + new_version.save() - # Compatibiliy methods + + logger.debug('new_version_dict: %s' % new_version_dict) + + logger.debug('new_version saved') + return new_version + + # Proxy methods + def open(self): + ''' + Return a file descriptor to a document's file irrespective of + the storage backend + ''' + return self.latest_version.open() + + def save_to_file(self, *args, **kwargs): + return self.latest_version.save_to_file(*args, **kwargs) + + def exists(self): + ''' + Returns a boolean value that indicates if the document's + latest version file exists in storage + ''' + return self.latest_version.exists() + + # Compatibility methods @property def file(self): - return self.get_latest_version().file + return self.latest_version.file @property def file_mimetype(self): - return self.get_latest_version().mimetype + return self.latest_version.mimetype @property def file_mime_encoding(self): - return self.get_latest_version().encoding + return self.latest_version.encoding @property def file_filename(self): - return self.get_latest_version().filename + return self.latest_version.filename @property def date_updated(self): - return self.get_latest_version().timestamp + return self.latest_version.timestamp + # TODO: uncomment when date_added is removed #@property #def date_added(self): - # return self.get_latest_version().timestamp + # return self.first_version.timestamp @property def checksum(self): - return self.get_latest_version().checksum + return self.latest_version.checksum @property def pages(self): - return self.get_latest_version().pages + return self.latest_version.pages - - #file = models.FileField(upload_to=get_filename_from_uuid, storage=STORAGE_BACKEND(), verbose_name=_(u'file')) - #file_mimetype = models.CharField(max_length=64, default='', editable=False) - #file_mime_encoding = models.CharField(max_length=64, default='', editable=False) - ##FAT filename can be up to 255 using LFN - #file_filename = models.CharField(max_length=255, default=u'', editable=False, db_index=True) - #date_updated = models.DateTimeField(verbose_name=_(u'updated'), auto_now=True) - #checksum = models.TextField(blank=True, null=True, verbose_name=_(u'checksum'), editable=False) + @property + def page_count(self): + return self.pages.count() - def get_latest_version(self): + @property + def latest_version(self): return self.documentversion_set.order_by('-timestamp')[0] + @property + def first_version(self): + return self.documentversion_set.order_by('timestamp')[0] + @property def versions(self): return self.documentversion_set + def _get_filename(self): + return self.latest_version.filename -RELEASE_LEVEL_FINAL = 1 -RELEASE_LEVEL_ALPHA = 2 -RELEASE_LEVEL_BETA = 3 -RELEASE_LEVEL_RC = 4 -RELEASE_LEVEL_HF = 5 + def _set_filename(self, value): + version = self.latest_version + version.filename = value + return version.save() -RELEASE_LEVEL_CHOICES = ( - (RELEASE_LEVEL_FINAL, _(u'final')), - (RELEASE_LEVEL_ALPHA, _(u'alpha')), - (RELEASE_LEVEL_BETA, _(u'beta')), - (RELEASE_LEVEL_RC, _(u'release candidate')), - (RELEASE_LEVEL_HF, _(u'hotfix')), -) + filename = property(_get_filename, _set_filename) + class DocumentVersion(models.Model): ''' - Model that describes a document version and it properties + Model that describes a document version and its properties ''' - document = models.ForeignKey(Document, verbose_name=_(u'document')) - 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')) + @staticmethod + def get_version_update_choices(document_version): + return ( + (VERSION_UPDATE_MAJOR, _(u'Major %(major)i.%(minor)i, (new release)') % document_version.get_new_version_dict(VERSION_UPDATE_MAJOR)), + (VERSION_UPDATE_MINOR, _(u'Minor %(major)i.%(minor)i, (some updates)') % document_version.get_new_version_dict(VERSION_UPDATE_MINOR)), + (VERSION_UPDATE_MICRO, _(u'Micro %(major)i.%(minor)i.%(micro)i, (fixes)') % document_version.get_new_version_dict(VERSION_UPDATE_MICRO)) + ) + + document = models.ForeignKey(Document, verbose_name=_(u'document'), editable=False) + 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) comment = models.TextField(blank=True, verbose_name=_(u'comment')) # File related fields @@ -406,11 +328,31 @@ class DocumentVersion(models.Model): verbose_name_plural = _(u'document version') def __unicode__(self): - return self.get_version() + return self.get_formated_version() - # TODO: Update timestamp + def get_new_version_dict(self, version_update_type): + logger.debug('version_update_type: %s' % version_update_type) - def get_version(self): + if version_update_type == VERSION_UPDATE_MAJOR: + return { + 'major': self.major + 1, + 'minor': 0, + 'micro': 0, + } + elif version_update_type == VERSION_UPDATE_MINOR: + return { + 'major': self.major, + 'minor': self.minor + 1, + 'micro': 0, + } + elif version_update_type == VERSION_UPDATE_MICRO: + return { + 'major': self.major, + 'minor': self.minor, + 'micro': self.micro + 1, + } + + def get_formated_version(self): ''' Return the formatted version information ''' @@ -419,26 +361,151 @@ class DocumentVersion(models.Model): if self.micro: vers.append(u'.%i' % self.micro) if self.release_level != RELEASE_LEVEL_FINAL: - vers.append(u'%s%i' % (self.release_level, self.serial)) + vers.append(u'%s%i' % (self.get_release_level_display(), self.serial)) return u''.join(vers) @property def pages(self): return self.documentpage_set + def save(self, *args, **kwargs): + ''' + Overloaded save method that updates the document version's checksum, + mimetype, page count and transformation when created + ''' + new_document = not self.pk + if not self.pk: + self.timestamp = datetime.datetime.now() + + #Only do this for new documents + transformations = kwargs.pop('transformations', None) + super(DocumentVersion, self).save(*args, **kwargs) + + if new_document: + #Only do this for new documents + self.update_checksum(save=False) + self.update_mimetype(save=False) + self.save() + self.update_page_count(save=False) + if transformations: + self.apply_default_transformations(transformations) + + def update_checksum(self, save=True): + ''' + Open a document version's file and update the checksum field using the + user provided checksum function + ''' + if self.exists(): + source = self.open() + self.checksum = unicode(CHECKSUM_FUNCTION(source.read())) + source.close() + if save: + self.save() + + def update_page_count(self, save=True): + handle, filepath = tempfile.mkstemp() + # Just need the filepath, close the file description + os.close(handle) + + self.save_to_file(filepath) + try: + detected_pages = get_page_count(filepath) + except UnknownFileFormat: + # If converter backend doesn't understand the format, + # use 1 as the total page count + detected_pages = 1 + self.description = ugettext(u'This document\'s file format is not known, the page count has therefore defaulted to 1.') + self.save() + try: + os.remove(filepath) + except OSError: + pass + + current_pages = self.documentpage_set.order_by('page_number',) + if current_pages.count() > detected_pages: + for page in current_pages[detected_pages:]: + page.delete() + + for page_number in range(detected_pages): + DocumentPage.objects.get_or_create( + document_version=self, page_number=page_number + 1) + + if save: + self.save() + + return detected_pages + + def apply_default_transformations(self, transformations): + #Only apply default transformations on new documents + if reduce(lambda x, y: x + y, [page.documentpagetransformation_set.count() for page in self.pages.all()]) == 0: + for transformation in transformations: + for document_page in self.pages.all(): + page_transformation = DocumentPageTransformation( + document_page=document_page, + order=0, + transformation=transformation.get('transformation'), + arguments=transformation.get('arguments') + ) + + page_transformation.save() + + def update_mimetype(self, save=True): + ''' + Read a document verions's file and determine the mimetype by calling the + get_mimetype wrapper + ''' + if self.exists(): + try: + self.mimetype, self.encoding = get_mimetype(self.open(), self.filename) + except: + self.mimetype = u'' + self.encoding = u'' + finally: + if save: + self.save() + + def delete(self, *args, **kwargs): + super(Document, self).delete(*args, **kwargs) + return self.file.storage.delete(self.file.path) + + def exists(self): + ''' + Returns a boolean value that indicates if the document's file + exists in storage + ''' + return self.file.storage.exists(self.file.path) + def open(self): ''' Return a file descriptor to a document version's file irrespective of the storage backend ''' return self.file.storage.open(self.file.path) + + def save_to_file(self, filepath, buffer_size=1024 * 1024): + ''' + Save a copy of the document from the document storage backend + to the local filesystem + ''' + input_descriptor = self.open() + output_descriptor = open(filepath, 'wb') + while True: + copy_buffer = input_descriptor.read(buffer_size) + if copy_buffer: + output_descriptor.write(copy_buffer) + else: + break + + output_descriptor.close() + input_descriptor.close() + return filepath class DocumentTypeFilename(models.Model): - """ + ''' List of filenames available to a specific document type for the quick rename functionality - """ + ''' document_type = models.ForeignKey(DocumentType, verbose_name=_(u'document type')) filename = models.CharField(max_length=128, verbose_name=_(u'filename'), db_index=True) enabled = models.BooleanField(default=True, verbose_name=_(u'enabled')) @@ -453,12 +520,9 @@ class DocumentTypeFilename(models.Model): class DocumentPage(models.Model): - """ - Model that describes a document page including it's content - """ - ## This field is to be removed - #document = models.ForeignKey(Document, verbose_name=_(u'document')) - + ''' + Model that describes a document version page including it's content + ''' # New parent field document_version = models.ForeignKey(DocumentVersion, verbose_name=_(u'document version'))#, null=True, blank=True) # TODO: Remove these after datamigration diff --git a/apps/documents/urls.py b/apps/documents/urls.py index 88650155a7..239ec62df4 100644 --- a/apps/documents/urls.py +++ b/apps/documents/urls.py @@ -35,7 +35,7 @@ urlpatterns = patterns('documents.views', url(r'^(?P\d+)/clear_transformations/$', 'document_clear_transformations', (), 'document_clear_transformations'), url(r'^(?P\d+)/version/all/$', 'document_version_list', (), 'document_version_list'), - url(r'^document/version/(?P\d+)/download/$', 'document_version_download', (), 'document_version_download'), + url(r'^document/version/(?P\d+)/download/$', 'document_download', (), 'document_version_download'), url(r'^multiple/clear_transformations/$', 'document_multiple_clear_transformations', (), 'document_multiple_clear_transformations'), url(r'^duplicates/list/$', 'document_find_all_duplicates', (), 'document_find_all_duplicates'), diff --git a/apps/documents/views.py b/apps/documents/views.py index efaaf3e2da..bb74f6c62a 100644 --- a/apps/documents/views.py +++ b/apps/documents/views.py @@ -45,13 +45,13 @@ from documents.literals import PERMISSION_DOCUMENT_CREATE, \ from documents.literals import HISTORY_DOCUMENT_CREATED, \ HISTORY_DOCUMENT_EDITED, HISTORY_DOCUMENT_DELETED -from documents.forms import DocumentTypeSelectForm, \ - DocumentForm_edit, DocumentPropertiesForm, \ - DocumentPreviewForm, \ - DocumentPageForm, DocumentPageTransformationForm, \ - DocumentContentForm, DocumentPageForm_edit, \ - DocumentPageForm_text, PrintForm, DocumentTypeForm, \ - DocumentTypeFilenameForm, DocumentTypeFilenameForm_create +from documents.forms import (DocumentTypeSelectForm, + DocumentForm_edit, DocumentPropertiesForm, + DocumentPreviewForm, DocumentPageForm, + DocumentPageTransformationForm, DocumentContentForm, + DocumentPageForm_edit, DocumentPageForm_text, PrintForm, + DocumentTypeForm, DocumentTypeFilenameForm, + DocumentTypeFilenameForm_create) from documents.wizards import DocumentCreateWizard from documents.models import (Document, DocumentType, DocumentPage, DocumentPageTransformation, RecentDocument, DocumentTypeFilename, @@ -242,12 +242,12 @@ def document_edit(request, document_id): for warning in warnings: messages.warning(request, warning) - document.file_filename = form.cleaned_data['new_filename'] + document.filename = form.cleaned_data['new_filename'] document.description = form.cleaned_data['description'] if 'document_type_available_filenames' in form.cleaned_data: if form.cleaned_data['document_type_available_filenames']: - document.file_filename = form.cleaned_data['document_type_available_filenames'].filename + document.filename = form.cleaned_data['document_type_available_filenames'].filename document.save() create_history(HISTORY_DOCUMENT_EDITED, document, {'user': request.user, 'diff': return_diff(old_document, document, ['file_filename', 'description'])}) @@ -291,27 +291,27 @@ def get_document_image(request, document_id, size=PREVIEW_SIZE, base64_version=F if base64_version: return HttpResponse(u'' % document.get_image(size=size, page=page, zoom=zoom, rotation=rotation, as_base64=True)) else: - # TODO: hardcoded MIMETYPE + # TODO: fix hardcoded MIMETYPE return sendfile.sendfile(request, document.get_image(size=size, page=page, zoom=zoom, rotation=rotation), mimetype=DEFAULT_FILE_FORMAT_MIMETYPE) -def document_version_download(request, document_version_pk): - document_version = get_object_or_404(DocumentVersion, pk=document_version_pk) - return document_download(request, document_version.document.pk) - - -def document_download(request, document_id): +def document_download(request, document_id=None, document_version_pk=None): check_permissions(request.user, [PERMISSION_DOCUMENT_DOWNLOAD]) - document = get_object_or_404(Document, pk=document_id) + if document_version_pk: + document_version = get_object_or_404(DocumentVersion, pk=document_version_pk) + else: + document_version = get_object_or_404(Document, pk=document_id).latest_version + try: - #Test permissions and trigger exception - document.open() + # Test permissions and trigger exception + fd = document_version.open() + fd.close() return serve_file( request, - document.file, - save_as=u'"%s"' % document.get_fullname(), - content_type=document.file_mimetype if document.file_mimetype else 'application/octet-stream' + document_version.file, + save_as=u'"%s"' % document_version.filename, + content_type=document_version.mimetype if document_version.mimetype else 'application/octet-stream' ) except Exception, e: messages.error(request, e) @@ -1154,14 +1154,14 @@ def document_version_list(request, document_pk): document = get_object_or_404(Document, pk=document_pk) context = { - 'object_list': document.versions.all(), + 'object_list': document.versions.order_by('-timestamp'), 'title': _(u'versions for document: %s') % document, 'hide_object': True, 'object': document, 'extra_columns': [ { 'name': _(u'version'), - 'attribute': 'get_version', + 'attribute': 'get_formated_version', }, { 'name': _(u'time and date'),