diff --git a/.tx/config b/.tx/config index cbd85fb879..acbc95abba 100644 --- a/.tx/config +++ b/.tx/config @@ -148,3 +148,9 @@ trans.es = apps/web_theme/locale/es/LC_MESSAGES/django.po trans.pt = apps/web_theme/locale/pt/LC_MESSAGES/django.po trans.ru = apps/web_theme/locale/ru/LC_MESSAGES/django.po +[mayan-edms.apps-django_gpg] +source_file = apps/django_gpg/locale/en/LC_MESSAGES/django.po +source_lang = en +trans.es = apps/django_gpg/locale/es/LC_MESSAGES/django.po +trans.pt = apps/django_gpg/locale/pt/LC_MESSAGES/django.po +trans.ru = apps/django_gpg/locale/ru/LC_MESSAGES/django.po diff --git a/apps/common/locale/es/LC_MESSAGES/django.mo b/apps/common/locale/es/LC_MESSAGES/django.mo index 80f426bc7a..00b642dc31 100644 Binary files a/apps/common/locale/es/LC_MESSAGES/django.mo and b/apps/common/locale/es/LC_MESSAGES/django.mo differ diff --git a/apps/common/locale/pt/LC_MESSAGES/django.mo b/apps/common/locale/pt/LC_MESSAGES/django.mo index 059d5b191c..ade5e888b5 100644 Binary files a/apps/common/locale/pt/LC_MESSAGES/django.mo and b/apps/common/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/converter/api.py b/apps/converter/api.py index f48bf88986..ff4e9d645b 100644 --- a/apps/converter/api.py +++ b/apps/converter/api.py @@ -63,7 +63,7 @@ def convert(input_filepath, output_filepath=None, cleanup_files=False, mimetype= mimetype = office_converter.mimetype except OfficeConversionError: - raise UnknownFileFormat('office converter exception') + raise UnknownFileFormat('office converter exception') if size: transformations.append( @@ -110,7 +110,7 @@ def get_page_count(input_filepath): return backend.get_page_count(input_filepath) - +''' def get_document_dimensions(document, *args, **kwargs): document_filepath = create_image_cache_filename(document.checksum, *args, **kwargs) if os.path.exists(document_filepath): @@ -118,7 +118,7 @@ def get_document_dimensions(document, *args, **kwargs): return [int(dimension) for dimension in backend.identify_file(unicode(document_filepath), options).split()] else: return [0, 0] - +''' def get_available_transformations_choices(): result = [] diff --git a/apps/converter/locale/es/LC_MESSAGES/django.mo b/apps/converter/locale/es/LC_MESSAGES/django.mo index 30ce640c0d..e0c200c67c 100644 Binary files a/apps/converter/locale/es/LC_MESSAGES/django.mo and b/apps/converter/locale/es/LC_MESSAGES/django.mo differ diff --git a/apps/converter/locale/es/LC_MESSAGES/django.po b/apps/converter/locale/es/LC_MESSAGES/django.po index 68d184d9c0..710d29bd7a 100644 --- a/apps/converter/locale/es/LC_MESSAGES/django.po +++ b/apps/converter/locale/es/LC_MESSAGES/django.po @@ -9,7 +9,7 @@ msgstr "" "Project-Id-Version: Mayan EDMS\n" "Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" "POT-Creation-Date: 2011-11-22 11:26-0400\n" -"PO-Revision-Date: 2011-11-22 21:01+0000\n" +"PO-Revision-Date: 2011-12-06 06:17+0000\n" "Last-Translator: rosarior \n" "Language-Team: Spanish (Castilian) (http://www.transifex.net/projects/p/mayan-edms/team/es/)\n" "MIME-Version: 1.0\n" @@ -557,11 +557,11 @@ msgstr "" #: literals.py:205 literals.py:206 msgid "Photo CD" -msgstr "" +msgstr "Photo CD" #: literals.py:207 msgid "Page Control Language" -msgstr "" +msgstr "Page Control Language" #: literals.py:208 literals.py:221 msgid "Apple Macintosh QuickDraw/PICT" @@ -577,11 +577,11 @@ msgstr "" #: literals.py:211 msgid "Portable Document Format" -msgstr "" +msgstr "Formato portatil de documento" #: literals.py:212 msgid "Portable Document Archive Format" -msgstr "" +msgstr "Format portatil de archivo de documento" #: literals.py:213 msgid "Pentax Electronic File" diff --git a/apps/converter/locale/pt/LC_MESSAGES/django.mo b/apps/converter/locale/pt/LC_MESSAGES/django.mo index 56ca29109d..5b966ac130 100644 Binary files a/apps/converter/locale/pt/LC_MESSAGES/django.mo and b/apps/converter/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/converter/office_converter.py b/apps/converter/office_converter.py index dac7ea3d69..a0c93aa6d9 100644 --- a/apps/converter/office_converter.py +++ b/apps/converter/office_converter.py @@ -13,21 +13,24 @@ from converter.exceptions import (OfficeConversionError, CACHED_FILE_SUFFIX = u'_office_converter' CONVERTER_OFFICE_FILE_MIMETYPES = [ - 'application/msword', - 'application/mswrite', - 'application/mspowerpoint', - 'application/msexcel', - 'application/vnd.ms-excel', - 'application/vnd.ms-powerpoint', - 'text/plain', - 'application/vnd.oasis.opendocument.presentation', - 'application/vnd.oasis.opendocument.text', - 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', - 'application/vnd.oasis.opendocument.spreadsheet', - 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - 'application/vnd.oasis.opendocument.graphics', + u'application/msword', + u'application/mswrite', + u'application/mspowerpoint', + u'application/msexcel', + u'application/vnd.ms-excel', + u'application/vnd.ms-powerpoint', + u'application/vnd.oasis.opendocument.presentation', + u'application/vnd.oasis.opendocument.text', + u'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + u'application/vnd.oasis.opendocument.spreadsheet', + u'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + u'application/vnd.oasis.opendocument.graphics', + u'application/vnd.ms-office', + u'text/plain', + u'text/rtf', ] + logger = logging.getLogger(__name__) @@ -94,20 +97,22 @@ class OfficeConverterBackendUnoconv(object): command.append(u'--pipe') command.append(u'mayan-%s' % id_generator()) - command.append(u'--format=pdf') - command.append(u'--output=%s' % self.output_filepath) + command.append(u'--format') + command.append(u'pdf') + command.append(u'--output') + command.append(self.output_filepath) command.append(self.input_filepath) try: - logger.debug('prev environment: %s' % os.environ) proc = subprocess.Popen(command, close_fds=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE) return_code = proc.wait() - logger.debug('post environment: %s' % os.environ) + logger.debug('return_code: %s' % return_code) readline = proc.stderr.readline() + logger.debug('stderr: %s' % readline) if return_code != 0: - raise OfficeBackendError(proc.stderr.readline()) + raise OfficeBackendError(readline) except OSError, msg: raise OfficeBackendError(msg) except Exception, msg: - logger.error('Unhandled exception: %s' % msg) + logger.error('Unhandled exception', exc_info=msg) diff --git a/apps/django_gpg/__init__.py b/apps/django_gpg/__init__.py new file mode 100644 index 0000000000..e45ae78165 --- /dev/null +++ b/apps/django_gpg/__init__.py @@ -0,0 +1,64 @@ +from django.utils.translation import ugettext_lazy as _ + +from documents.models import Document +from navigation.api import register_links, register_top_menu, \ + register_model_list_columns, register_multi_item_links, \ + register_sidebar_template +from main.api import register_diagnostic, register_maintenance_links +from permissions.api import register_permission, set_namespace_title +from project_setup.api import register_setup +from hkp import Key as KeyServerKey + +from django_gpg.api import Key + +PERMISSION_DOCUMENT_VERIFY = {'namespace': 'django_gpg', 'name': 'document_verify', 'label': _(u'Verify document signatures')} +PERMISSION_KEY_VIEW = {'namespace': 'django_gpg', 'name': 'key_view', 'label': _(u'View keys')} +PERMISSION_KEY_DELETE = {'namespace': 'django_gpg', 'name': 'key_delete', 'label': _(u'Delete keys')} +PERMISSION_KEYSERVER_QUERY = {'namespace': 'django_gpg', 'name': 'keyserver_query', 'label': _(u'Query keyservers')} +PERMISSION_KEY_RECEIVE = {'namespace': 'django_gpg', 'name': 'key_receive', 'label': _(u'Import key from keyservers')} +PERMISSION_SIGNATURE_UPLOAD = {'namespace': 'django_gpg', 'name': 'signature_upload', 'label': _(u'Upload detached signatures')} +PERMISSION_SIGNATURE_DOWNLOAD = {'namespace': 'django_gpg', 'name': 'key_receive', 'label': _(u'Download detached signatures')} + +# Permission setup +set_namespace_title('django_gpg', _(u'Signatures')) +register_permission(PERMISSION_DOCUMENT_VERIFY) +register_permission(PERMISSION_KEY_VIEW) +register_permission(PERMISSION_KEY_DELETE) +register_permission(PERMISSION_KEYSERVER_QUERY) +register_permission(PERMISSION_KEY_RECEIVE) +register_permission(PERMISSION_SIGNATURE_UPLOAD) +register_permission(PERMISSION_SIGNATURE_DOWNLOAD) + +def has_embedded_signature(context): + return context['object'].signature_state + +def doesnt_have_detached_signature(context): + return context['object'].has_detached_signature() == False + +# Setup views +private_keys = {'text': _(u'private keys'), 'view': 'key_private_list', 'args': 'object.pk', 'famfam': 'key', 'icon': 'key.png', 'permissions': [PERMISSION_KEY_VIEW]} +public_keys = {'text': _(u'public keys'), 'view': 'key_public_list', 'args': 'object.pk', 'famfam': 'key', 'icon': 'key.png', 'permissions': [PERMISSION_KEY_VIEW]} +key_delete = {'text': _(u'delete'), 'view': 'key_delete', 'args': ['object.fingerprint', 'object.type'], 'famfam': 'key_delete', 'permissions': [PERMISSION_KEY_DELETE]} +key_query = {'text': _(u'query keyservers'), 'view': 'key_query', 'famfam': 'zoom', 'permissions': [PERMISSION_KEYSERVER_QUERY]} +key_receive = {'text': _(u'import'), 'view': 'key_receive', 'args': 'object.keyid', 'famfam': 'key_add', 'keep_query': True, 'permissions': [PERMISSION_KEY_RECEIVE]} +document_signature_upload = {'text': _(u'upload signature'), 'view': 'document_signature_upload', 'args': 'object.pk', 'famfam': 'pencil_add', 'permissions': [PERMISSION_SIGNATURE_UPLOAD], 'conditional_disable': has_embedded_signature} +document_signature_download = {'text': _(u'download signature'), 'view': 'document_signature_download', 'args': 'object.pk', 'famfam': 'disk', 'permissions': [PERMISSION_SIGNATURE_DOWNLOAD], 'conditional_disable': doesnt_have_detached_signature} +key_setup = {'text': _(u'key management'), 'view': 'key_public_list', 'args': 'object.pk', 'famfam': 'key', 'icon': 'key.png', 'permissions': [PERMISSION_KEY_VIEW]} + +# Document views +document_verify = {'text': _(u'signatures'), 'view': 'document_verify', 'args': 'object.pk', 'famfam': 'text_signature', 'permissions': [PERMISSION_DOCUMENT_VERIFY]} + +register_links(Document, [document_verify], menu_name='form_header') + +register_links(['document_verify', 'document_signature_upload', 'document_signature_download'], [document_signature_upload, document_signature_download], menu_name='sidebar') + +#register_links(['key_delete', 'key_private_list', 'key_public_list', 'key_query'], [private_keys, public_keys, key_query], menu_name='sidebar') +register_links(['key_delete', 'key_public_list', 'key_query'], [public_keys, key_query], menu_name='sidebar') + +register_links(Key, [key_delete]) +register_links(KeyServerKey, [key_receive]) + +register_setup(key_setup) + + + diff --git a/apps/django_gpg/api.py b/apps/django_gpg/api.py new file mode 100644 index 0000000000..8d9f2e2b4b --- /dev/null +++ b/apps/django_gpg/api.py @@ -0,0 +1,343 @@ +import types +from StringIO import StringIO +from pickle import dumps +import logging +import tempfile +import os + +from django.core.files.base import File +from django.utils.translation import ugettext_lazy as _ +from django.utils.http import urlquote_plus + +from hkp import KeyServer +import gnupg + +from django_gpg.exceptions import (GPGVerificationError, GPGSigningError, + GPGDecryptionError, KeyDeleteError, KeyGenerationError, + KeyFetchingError, KeyDoesNotExist, KeyImportError) + +logger = logging.getLogger(__name__) + +KEY_TYPES = { + 'pub': _(u'Public'), + 'sec': _(u'Secret'), +} + +KEY_CLASS_RSA = 'RSA' +KEY_CLASS_DSA = 'DSA' +KEY_CLASS_ELG = 'ELG-E' + +KEY_PRIMARY_CLASSES = ( + ((KEY_CLASS_RSA), _(u'RSA')), + ((KEY_CLASS_DSA), _(u'DSA')), +) + +KEY_SECONDARY_CLASSES = ( + ((KEY_CLASS_RSA), _(u'RSA')), + ((KEY_CLASS_ELG), _(u'Elgamal')), +) + +KEYSERVER_DEFAULT_PORT = 11371 + +SIGNATURE_STATE_BAD = 'signature bad' +SIGNATURE_STATE_NONE = None +SIGNATURE_STATE_ERROR = 'signature error' +SIGNATURE_STATE_NO_PUBLIC_KEY = 'no public key' +SIGNATURE_STATE_GOOD = 'signature good' +SIGNATURE_STATE_VALID = 'signature valid' + +SIGNATURE_STATES = { + SIGNATURE_STATE_BAD: { + 'text': _(u'Bad signature.'), + 'icon': 'cross.png' + }, + SIGNATURE_STATE_NONE: { + 'text': _(u'Document not signed or invalid signature.'), + 'icon': 'cross.png' + }, + SIGNATURE_STATE_ERROR: { + 'text': _(u'Signature error.'), + 'icon': 'cross.png' + }, + SIGNATURE_STATE_NO_PUBLIC_KEY: { + 'text': _(u'Document is signed but no public key is available for verification.'), + 'icon': 'user_silhouette.png' + }, + SIGNATURE_STATE_GOOD: { + 'text': _(u'Document is signed, and signature is good.'), + 'icon': 'document_signature.png' + }, + SIGNATURE_STATE_VALID: { + 'text': _(u'Document is signed with a valid signature.'), + 'icon': 'document_signature.png' + }, +} + + +class Key(object): + @staticmethod + def get_key_id(fingerprint): + return fingerprint[-16:] + + @classmethod + def get_all(cls, gpg, secret=False, exclude=None): + result = [] + keys = gpg.gpg.list_keys(secret=secret) + if exclude: + excluded_id = exclude.key_id + else: + excluded_id = u'' + for key in keys: + if not key['keyid'] in excluded_id: + key_instance = Key( + fingerprint=key['fingerprint'], + uids=key['uids'], + type=key['type'], + data=gpg.gpg.export_keys([key['keyid']], secret=secret) + ) + result.append(key_instance) + + return result + + @classmethod + def get(cls, gpg, key_id, secret=False, search_keyservers=False): + if len(key_id) > 16: + # key_id is a fingerprint + key_id = Key.get_key_id(key_id) + + keys = gpg.gpg.list_keys(secret=secret) + key = next((key for key in keys if key['keyid'] == key_id), None) + if not key: + if search_keyservers and secret==False: + try: + gpg.receive_key(key_id) + return Key(gpg, key_id) + except KeyFetchingError: + raise KeyDoesNotExist + else: + raise KeyDoesNotExist + + key_instance = Key( + fingerprint=key['fingerprint'], + uids=key['uids'], + type=key['type'], + data=gpg.gpg.export_keys([key['keyid']], secret=secret) + ) + + return key_instance + + def __init__(self, fingerprint, uids, type, data): + self.fingerprint = fingerprint + self.uids = uids + self.type = type + self.data = data + + @property + def key_id(self): + return Key.get_key_id(self.fingerprint) + + @property + def user_ids(self): + return u', '.join(self.uids) + + def __str__(self): + return '%s "%s" (%s)' % (self.key_id, self.user_ids, KEY_TYPES.get(self.type, _(u'unknown'))) + + def __unicode__(self): + return unicode(self.__str__()) + + def __repr__(self): + return self.__unicode__() + + +class GPG(object): + def __init__(self, binary_path=None, home=None, keyring=None, keyservers=None): + kwargs = {} + if binary_path: + kwargs['gpgbinary'] = binary_path + + if home: + kwargs['gnupghome'] = home + + if keyring: + kwargs['keyring'] = keyring + + self.keyservers = keyservers + + self.gpg = gnupg.GPG(**kwargs) + + def verify_w_retry(self, file_input, detached_signature=None): + if isinstance(file_input, types.StringTypes): + input_descriptor = open(file_input, 'rb') + elif isinstance(file_input, types.FileType) or isinstance(file_input, File): + input_descriptor = file_input + elif issubclass(file_input.__class__, StringIO): + input_descriptor = file_input + else: + raise ValueError('Invalid file_input argument type') + + try: + verify = self.verify_file(input_descriptor, detached_signature) + if verify.status == 'no public key': + # Try to fetch the public key from the keyservers + try: + self.receive_key(verify.key_id) + return self.verify_w_retry(file_input, detached_signature) + except KeyFetchingError: + return verify + else: + return verify + except IOError: + return False + + def verify_file(self, file_input, detached_signature=None): + """ + Verify the signature of a file. + """ + if isinstance(file_input, types.StringTypes): + descriptor = open(file_input, 'rb') + elif isinstance(file_input, types.FileType) or isinstance(file_input, File) or isinstance(file_input, StringIO): + descriptor = file_input + else: + raise ValueError('Invalid file_input argument type') + + if detached_signature: + # Save the original data and invert the argument order + # Signature first, file second + file_descriptor, filename = tempfile.mkstemp(prefix='django_gpg') + file_data = file_input.read() + file_input.close() + os.write(file_descriptor, file_data) + os.close(file_descriptor) + verify = self.gpg.verify_file(detached_signature, data_filename=filename) + else: + verify = self.gpg.verify_file(descriptor) + descriptor.close() + + if verify: + return verify + #elif getattr(verify, 'status', None) == 'no public key': + # # Exception to the rule, to be able to query the keyservers + # return verify + else: + raise GPGVerificationError() + + def verify(self, data): + # TODO: try to merge with verify_file + verify = self.gpg.verify(data) + + if verify: + return verify + else: + raise GPGVerificationError(verify.status) + + def sign_file(self, file_input, key=None, destination=None, key_id=None, passphrase=None, clearsign=False): + """ + Signs a filename, storing the signature and the original file + in the destination filename provided (the destination file is + overrided if it already exists), if no destination file name is + provided the signature is returned. + """ + kwargs = {} + kwargs['clearsign'] = clearsign + + if key_id: + kwargs['keyid'] = key_id + + if key: + kwargs['keyid'] = key.key_id + + if passphrase: + kwargs['passphrase'] = passphrase + + if isinstance(file_input, types.StringTypes): + input_descriptor = open(file_input, 'rb') + elif isinstance(file_input, types.FileType) or isinstance(file_input, File): + input_descriptor = file_input + elif issubclass(file_input.__class__, StringIO): + input_descriptor = file_input + else: + raise ValueError('Invalid file_input argument type') + + if destination: + output_descriptor = open(destination, 'wb') + + signed_data = self.gpg.sign_file(input_descriptor, **kwargs) + if not signed_data.fingerprint: + raise GPGSigningError('Unable to sign file') + + if destination: + output_descriptor.write(signed_data.data) + + input_descriptor.close() + + if destination: + output_descriptor.close() + + if not destination: + return signed_data + + def decrypt_file(self, file_input): + if isinstance(file_input, types.StringTypes): + input_descriptor = open(file_input, 'rb') + elif isinstance(file_input, types.FileType) or isinstance(file_input, File) or isinstance(file_input, StringIO): + input_descriptor = file_input + else: + raise ValueError('Invalid file_input argument type') + + result = self.gpg.decrypt_file(input_descriptor) + input_descriptor.close() + if not result.status: + raise GPGDecryptionError('Unable to decrypt file') + + return result + + def create_key(self, *args, **kwargs): + if kwargs.get('passphrase') == u'': + kwargs.pop('passphrase') + + input_data = self.gpg.gen_key_input(**kwargs) + key = self.gpg.gen_key(input_data) + if not key: + raise KeyGenerationError('Unable to generate key') + + return Key.get(self, key.fingerprint) + + def delete_key(self, key): + status = self.gpg.delete_keys(key.fingerprint, key.type == 'sec').status + if status == 'Must delete secret key first': + self.delete_key(Key.get(self, key.fingerprint, secret=True)) + self.delete_key(key) + elif status != 'ok': + raise KeyDeleteError('Unable to delete key') + + def receive_key(self, key_id): + for keyserver in self.keyservers: + import_result = self.gpg.recv_keys(keyserver, key_id) + if import_result: + return Key.get(self, import_result.fingerprints[0], secret=False) + + raise KeyFetchingError + + def query(self, term): + results = {} + for keyserver in self.keyservers: + url = u'http://%s' % keyserver + server = KeyServer(url) + try: + key_list = server.search(term) + for key in key_list: + results[key.keyid] = key + except: + pass + + return results.values() + + def import_key(self, key_data): + import_result = self.gpg.import_keys(key_data) + logger.debug('import_result: %s' % import_result) + + if import_result: + return Key.get(self, import_result.fingerprints[0], secret=False) + + raise KeyImportError diff --git a/apps/django_gpg/conf/__init__.py b/apps/django_gpg/conf/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/django_gpg/conf/settings.py b/apps/django_gpg/conf/settings.py new file mode 100644 index 0000000000..696447b94a --- /dev/null +++ b/apps/django_gpg/conf/settings.py @@ -0,0 +1,15 @@ +''' +Configuration options for the django_gpg app +''' + +from django.utils.translation import ugettext_lazy as _ + +from smart_settings.api import register_settings + +register_settings( + namespace=u'django_gpg', + module=u'django_gpg.conf.settings', + settings=[ + {'name': u'KEYSERVERS', 'global_name': u'SIGNATURES_KEYSERVERS', 'default': ['pool.sks-keyservers.net'], 'description': _(u'List of keyservers to be queried for unknown keys.')}, + ] +) diff --git a/apps/django_gpg/exceptions.py b/apps/django_gpg/exceptions.py new file mode 100644 index 0000000000..52e62315b0 --- /dev/null +++ b/apps/django_gpg/exceptions.py @@ -0,0 +1,34 @@ +class GPGException(Exception): + pass + + +class GPGVerificationError(GPGException): + pass + + +class GPGSigningError(GPGException): + pass + + +class GPGDecryptionError(GPGException): + pass + + +class KeyDeleteError(GPGException): + pass + + +class KeyGenerationError(GPGException): + pass + + +class KeyFetchingError(GPGException): + pass + + +class KeyDoesNotExist(GPGException): + pass + + +class KeyImportError(GPGException): + pass diff --git a/apps/django_gpg/forms.py b/apps/django_gpg/forms.py new file mode 100644 index 0000000000..b961daf037 --- /dev/null +++ b/apps/django_gpg/forms.py @@ -0,0 +1,19 @@ +from django import forms +from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import ugettext +from django.core.urlresolvers import reverse +from django.utils.safestring import mark_safe +from django.conf import settings + + +class KeySearchForm(forms.Form): + term = forms.CharField( + label=_(u'Term'), + help_text=_(u'Name, e-mail, key ID or key fingerprint to look for.') + ) + + +class DetachedSignatureForm(forms.Form): + file = forms.FileField( + label=_(u'Signature file'), + ) diff --git a/apps/django_gpg/locale/en/LC_MESSAGES/django.po b/apps/django_gpg/locale/en/LC_MESSAGES/django.po new file mode 100644 index 0000000000..bb2d01367b --- /dev/null +++ b/apps/django_gpg/locale/en/LC_MESSAGES/django.po @@ -0,0 +1,290 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2011-12-06 02:06-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: __init__.py:14 +msgid "Verify document signatures" +msgstr "" + +#: __init__.py:15 +msgid "View keys" +msgstr "" + +#: __init__.py:16 +msgid "Delete keys" +msgstr "" + +#: __init__.py:17 +msgid "Query keyservers" +msgstr "" + +#: __init__.py:18 +msgid "Import key from keyservers" +msgstr "" + +#: __init__.py:19 +msgid "Upload detached signatures" +msgstr "" + +#: __init__.py:20 +msgid "Download detached signatures" +msgstr "" + +#: __init__.py:23 +msgid "Signatures" +msgstr "" + +#: __init__.py:39 views.py:67 +msgid "private keys" +msgstr "" + +#: __init__.py:40 views.py:70 +msgid "public keys" +msgstr "" + +#: __init__.py:41 +msgid "delete" +msgstr "" + +#: __init__.py:42 +msgid "query keyservers" +msgstr "" + +#: __init__.py:43 +msgid "import" +msgstr "" + +#: __init__.py:44 +msgid "upload signature" +msgstr "" + +#: __init__.py:45 +msgid "download signature" +msgstr "" + +#: __init__.py:46 +msgid "key management" +msgstr "" + +#: __init__.py:49 +msgid "signatures" +msgstr "" + +#: api.py:22 +msgid "Public" +msgstr "" + +#: api.py:23 +msgid "Secret" +msgstr "" + +#: api.py:31 api.py:36 +msgid "RSA" +msgstr "" + +#: api.py:32 +msgid "DSA" +msgstr "" + +#: api.py:37 +msgid "Elgamal" +msgstr "" + +#: api.py:51 +msgid "Bad signature." +msgstr "" + +#: api.py:55 +msgid "Document not signed or invalid signature." +msgstr "" + +#: api.py:59 +msgid "Signature error." +msgstr "" + +#: api.py:63 +msgid "Document is signed but no public key is available for verification." +msgstr "" + +#: api.py:67 +msgid "Document is signed, and signature is good." +msgstr "" + +#: api.py:71 +msgid "Document is signed with a valid signature." +msgstr "" + +#: api.py:144 +msgid "unknown" +msgstr "" + +#: forms.py:11 +msgid "Term" +msgstr "" + +#: forms.py:12 +msgid "Name, e-mail, key ID or key fingerprint to look for." +msgstr "" + +#: forms.py:18 +msgid "Signature file" +msgstr "" + +#: views.py:45 +#, python-format +msgid "Key: %s, imported successfully." +msgstr "" + +#: views.py:48 +#, python-format +msgid "Unable to import key id: %s" +msgstr "" + +#: views.py:52 +msgid "Import key" +msgstr "" + +#: views.py:53 +#, python-format +msgid "Are you sure you wish to import key id: %s?" +msgstr "" + +#: views.py:78 +msgid "Key ID" +msgstr "" + +#: views.py:82 +msgid "Owner" +msgstr "" + +#: views.py:102 +#, python-format +msgid "Key: %s, deleted successfully." +msgstr "" + +#: views.py:109 +msgid "Delete key" +msgstr "" + +#: views.py:111 +#, python-format +msgid "" +"Are you sure you wish to delete key: %s? If you try to delete a public key " +"that is part of a public/private pair the private key will be deleted as " +"well." +msgstr "" + +#: views.py:129 +msgid "Query key server" +msgstr "" + +#: views.py:142 +msgid "results" +msgstr "" + +#: views.py:147 +msgid "ID" +msgstr "" + +#: views.py:151 +msgid "type" +msgstr "" + +#: views.py:155 +msgid "creation date" +msgstr "" + +#: views.py:159 +msgid "disabled" +msgstr "" + +#: views.py:163 +msgid "expiration date" +msgstr "" + +#: views.py:167 +msgid "expired" +msgstr "" + +#: views.py:171 +msgid "length" +msgstr "" + +#: views.py:175 +msgid "revoked" +msgstr "" + +#: views.py:180 +msgid "Identifies" +msgstr "" + +#: views.py:205 +#, python-format +msgid "Signature status: %(widget)s %(text)s" +msgstr "" + +#: views.py:212 +msgid "embedded" +msgstr "" + +#: views.py:214 +msgid "detached" +msgstr "" + +#: views.py:219 +#, python-format +msgid "Signature ID: %s" +msgstr "" + +#: views.py:220 +#, python-format +msgid "Signature type: %s" +msgstr "" + +#: views.py:221 +#, python-format +msgid "Key ID: %s" +msgstr "" + +#: views.py:222 +#, python-format +msgid "Timestamp: %s" +msgstr "" + +#: views.py:223 +#, python-format +msgid "Signee: %s" +msgstr "" + +#: views.py:228 +#, python-format +msgid "signature properties for: %s" +msgstr "" + +#: views.py:250 +msgid "Detached signature uploaded successfully." +msgstr "" + +#: views.py:259 +#, python-format +msgid "Upload detached signature for: %s" +msgstr "" + +#: conf/settings.py:13 +msgid "List of keyservers to be queried for unknown keys." +msgstr "" diff --git a/apps/django_gpg/locale/es/LC_MESSAGES/django.mo b/apps/django_gpg/locale/es/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..e37a8ed1ed Binary files /dev/null and b/apps/django_gpg/locale/es/LC_MESSAGES/django.mo differ diff --git a/apps/django_gpg/locale/es/LC_MESSAGES/django.po b/apps/django_gpg/locale/es/LC_MESSAGES/django.po new file mode 100644 index 0000000000..7517475856 --- /dev/null +++ b/apps/django_gpg/locale/es/LC_MESSAGES/django.po @@ -0,0 +1,302 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Roberto Rosario , 2011. +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2011-12-06 02:06-0400\n" +"PO-Revision-Date: 2011-12-06 06:14+0000\n" +"Last-Translator: rosarior \n" +"Language-Team: Spanish (Castilian) (http://www.transifex.net/projects/p/mayan-edms/team/es/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: es\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" + +#: __init__.py:14 +msgid "Verify document signatures" +msgstr "Verificar las firmas de documentos" + +#: __init__.py:15 +msgid "View keys" +msgstr "Ver llaves" + +#: __init__.py:16 +msgid "Delete keys" +msgstr "Borrar llaves" + +#: __init__.py:17 +msgid "Query keyservers" +msgstr "Hacer búsquedas en servidores de llaves" + +#: __init__.py:18 +msgid "Import key from keyservers" +msgstr "Importar llaves de los servidores de llaves" + +#: __init__.py:19 +msgid "Upload detached signatures" +msgstr "Subir una firma a parte" + +#: __init__.py:20 +msgid "Download detached signatures" +msgstr "Descargar la fima" + +#: __init__.py:23 +msgid "Signatures" +msgstr "Firmas" + +#: __init__.py:39 views.py:67 +msgid "private keys" +msgstr "llaves privadas" + +#: __init__.py:40 views.py:70 +msgid "public keys" +msgstr "llaves públicas" + +#: __init__.py:41 +msgid "delete" +msgstr "borrar" + +#: __init__.py:42 +msgid "query keyservers" +msgstr "consultar servidor de llaves" + +#: __init__.py:43 +msgid "import" +msgstr "importar" + +#: __init__.py:44 +msgid "upload signature" +msgstr "subir firma" + +#: __init__.py:45 +msgid "download signature" +msgstr "descargar firma" + +#: __init__.py:46 +msgid "key management" +msgstr "manejo de llaves" + +#: __init__.py:49 +msgid "signatures" +msgstr "firmas" + +#: api.py:22 +msgid "Public" +msgstr "Pública" + +#: api.py:23 +msgid "Secret" +msgstr "Secreta" + +#: api.py:31 api.py:36 +msgid "RSA" +msgstr "RSA" + +#: api.py:32 +msgid "DSA" +msgstr "DSA" + +#: api.py:37 +msgid "Elgamal" +msgstr "Elgamal" + +#: api.py:51 +msgid "Bad signature." +msgstr "Firma invalida." + +#: api.py:55 +msgid "Document not signed or invalid signature." +msgstr "Documento no firmado o firma invalida." + +#: api.py:59 +msgid "Signature error." +msgstr "Error de firma." + +#: api.py:63 +msgid "Document is signed but no public key is available for verification." +msgstr "" +"El document ha sido firmado pero no hay llave pública disponible para " +"verificación." + +#: api.py:67 +msgid "Document is signed, and signature is good." +msgstr "El document ha sido firmado y la firma esta es buen estado." + +#: api.py:71 +msgid "Document is signed with a valid signature." +msgstr "El document ha sido firmado y la firma ha sido validada." + +#: api.py:144 +msgid "unknown" +msgstr "desconocida" + +#: forms.py:11 +msgid "Term" +msgstr "Término" + +#: forms.py:12 +msgid "Name, e-mail, key ID or key fingerprint to look for." +msgstr "" +"Nombre, dirección de correo electrónico, identificador de llave or huella " +"digital de llave a buscar." + +#: forms.py:18 +msgid "Signature file" +msgstr "Archivo de firma" + +#: views.py:45 +#, python-format +msgid "Key: %s, imported successfully." +msgstr "Llave: %s, importada exitosamente." + +#: views.py:48 +#, python-format +msgid "Unable to import key id: %s" +msgstr "No se pudo importa la llave: %s" + +#: views.py:52 +msgid "Import key" +msgstr "Importar llave" + +#: views.py:53 +#, python-format +msgid "Are you sure you wish to import key id: %s?" +msgstr "¿Esta seguro que desea importar la llave: %s?" + +#: views.py:78 +msgid "Key ID" +msgstr "Identificador de llave" + +#: views.py:82 +msgid "Owner" +msgstr "Dueño" + +#: views.py:102 +#, python-format +msgid "Key: %s, deleted successfully." +msgstr "Llave: %s, borrada exitosamente." + +#: views.py:109 +msgid "Delete key" +msgstr "Borrar llave" + +#: views.py:111 +#, python-format +msgid "" +"Are you sure you wish to delete key: %s? If you try to delete a public key " +"that is part of a public/private pair the private key will be deleted as " +"well." +msgstr "" +"¿Esta seguro que desea borrar la llave: %s? Si trata de borrar una llave " +"pública que es parte de un par público/privado la llave privada será borrada" +" también." + +#: views.py:129 +msgid "Query key server" +msgstr "Consultar servidor de llaves" + +#: views.py:142 +msgid "results" +msgstr "resultados" + +#: views.py:147 +msgid "ID" +msgstr "ID" + +#: views.py:151 +msgid "type" +msgstr "tipo" + +#: views.py:155 +msgid "creation date" +msgstr "fecha de creación" + +#: views.py:159 +msgid "disabled" +msgstr "desactivada" + +#: views.py:163 +msgid "expiration date" +msgstr "fecha de expiración" + +#: views.py:167 +msgid "expired" +msgstr "expirada" + +#: views.py:171 +msgid "length" +msgstr "tamaño" + +#: views.py:175 +msgid "revoked" +msgstr "revocada" + +#: views.py:180 +msgid "Identifies" +msgstr "Identidades" + +#: views.py:205 +#, python-format +msgid "Signature status: %(widget)s %(text)s" +msgstr "Estado de la firma: %(widget)s %(text)s" + +#: views.py:212 +msgid "embedded" +msgstr "integrada" + +#: views.py:214 +msgid "detached" +msgstr "a parte" + +#: views.py:219 +#, python-format +msgid "Signature ID: %s" +msgstr "ID de la firma: %s" + +#: views.py:220 +#, python-format +msgid "Signature type: %s" +msgstr "Tipo de firma: %s" + +#: views.py:221 +#, python-format +msgid "Key ID: %s" +msgstr "ID de la llave: %s" + +#: views.py:222 +#, python-format +msgid "Timestamp: %s" +msgstr "Fecha y hora: %s" + +#: views.py:223 +#, python-format +msgid "Signee: %s" +msgstr "Firmantes: %s" + +#: views.py:228 +#, python-format +msgid "signature properties for: %s" +msgstr "propiedades de firma para: %s" + +#: views.py:250 +msgid "Detached signature uploaded successfully." +msgstr "El archivo de firma fue subido exitosamente." + +#: views.py:259 +#, python-format +msgid "Upload detached signature for: %s" +msgstr "Subir firma a parte para: %s" + +#: conf/settings.py:13 +msgid "List of keyservers to be queried for unknown keys." +msgstr "" +"Lista de servidores de llaves a ser utilizados para buscar llaves " +"desconocidas." + + diff --git a/apps/django_gpg/locale/pt/LC_MESSAGES/django.mo b/apps/django_gpg/locale/pt/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..9aea042e15 Binary files /dev/null and b/apps/django_gpg/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/django_gpg/locale/pt/LC_MESSAGES/django.po b/apps/django_gpg/locale/pt/LC_MESSAGES/django.po new file mode 100644 index 0000000000..7b89397c22 --- /dev/null +++ b/apps/django_gpg/locale/pt/LC_MESSAGES/django.po @@ -0,0 +1,291 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2011-12-06 02:06-0400\n" +"PO-Revision-Date: 2011-12-05 17:43+0000\n" +"Last-Translator: rosarior \n" +"Language-Team: Portuguese (http://www.transifex.net/projects/p/mayan-edms/" +"team/pt/)\n" +"Language: pt\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" + +#: __init__.py:14 +msgid "Verify document signatures" +msgstr "" + +#: __init__.py:15 +msgid "View keys" +msgstr "" + +#: __init__.py:16 +msgid "Delete keys" +msgstr "" + +#: __init__.py:17 +msgid "Query keyservers" +msgstr "" + +#: __init__.py:18 +msgid "Import key from keyservers" +msgstr "" + +#: __init__.py:19 +msgid "Upload detached signatures" +msgstr "" + +#: __init__.py:20 +msgid "Download detached signatures" +msgstr "" + +#: __init__.py:23 +msgid "Signatures" +msgstr "" + +#: __init__.py:39 views.py:67 +msgid "private keys" +msgstr "" + +#: __init__.py:40 views.py:70 +msgid "public keys" +msgstr "" + +#: __init__.py:41 +msgid "delete" +msgstr "" + +#: __init__.py:42 +msgid "query keyservers" +msgstr "" + +#: __init__.py:43 +msgid "import" +msgstr "" + +#: __init__.py:44 +msgid "upload signature" +msgstr "" + +#: __init__.py:45 +msgid "download signature" +msgstr "" + +#: __init__.py:46 +msgid "key management" +msgstr "" + +#: __init__.py:49 +msgid "signatures" +msgstr "" + +#: api.py:22 +msgid "Public" +msgstr "" + +#: api.py:23 +msgid "Secret" +msgstr "" + +#: api.py:31 api.py:36 +msgid "RSA" +msgstr "" + +#: api.py:32 +msgid "DSA" +msgstr "" + +#: api.py:37 +msgid "Elgamal" +msgstr "" + +#: api.py:51 +msgid "Bad signature." +msgstr "" + +#: api.py:55 +msgid "Document not signed or invalid signature." +msgstr "" + +#: api.py:59 +msgid "Signature error." +msgstr "" + +#: api.py:63 +msgid "Document is signed but no public key is available for verification." +msgstr "" + +#: api.py:67 +msgid "Document is signed, and signature is good." +msgstr "" + +#: api.py:71 +msgid "Document is signed with a valid signature." +msgstr "" + +#: api.py:144 +msgid "unknown" +msgstr "" + +#: forms.py:11 +msgid "Term" +msgstr "" + +#: forms.py:12 +msgid "Name, e-mail, key ID or key fingerprint to look for." +msgstr "" + +#: forms.py:18 +msgid "Signature file" +msgstr "" + +#: views.py:45 +#, python-format +msgid "Key: %s, imported successfully." +msgstr "" + +#: views.py:48 +#, python-format +msgid "Unable to import key id: %s" +msgstr "" + +#: views.py:52 +msgid "Import key" +msgstr "" + +#: views.py:53 +#, python-format +msgid "Are you sure you wish to import key id: %s?" +msgstr "" + +#: views.py:78 +msgid "Key ID" +msgstr "" + +#: views.py:82 +msgid "Owner" +msgstr "" + +#: views.py:102 +#, python-format +msgid "Key: %s, deleted successfully." +msgstr "" + +#: views.py:109 +msgid "Delete key" +msgstr "" + +#: views.py:111 +#, python-format +msgid "" +"Are you sure you wish to delete key: %s? If you try to delete a public key " +"that is part of a public/private pair the private key will be deleted as " +"well." +msgstr "" + +#: views.py:129 +msgid "Query key server" +msgstr "" + +#: views.py:142 +msgid "results" +msgstr "" + +#: views.py:147 +msgid "ID" +msgstr "" + +#: views.py:151 +msgid "type" +msgstr "" + +#: views.py:155 +msgid "creation date" +msgstr "" + +#: views.py:159 +msgid "disabled" +msgstr "" + +#: views.py:163 +msgid "expiration date" +msgstr "" + +#: views.py:167 +msgid "expired" +msgstr "" + +#: views.py:171 +msgid "length" +msgstr "" + +#: views.py:175 +msgid "revoked" +msgstr "" + +#: views.py:180 +msgid "Identifies" +msgstr "" + +#: views.py:205 +#, python-format +msgid "Signature status: %(widget)s %(text)s" +msgstr "" + +#: views.py:212 +msgid "embedded" +msgstr "" + +#: views.py:214 +msgid "detached" +msgstr "" + +#: views.py:219 +#, python-format +msgid "Signature ID: %s" +msgstr "" + +#: views.py:220 +#, python-format +msgid "Signature type: %s" +msgstr "" + +#: views.py:221 +#, python-format +msgid "Key ID: %s" +msgstr "" + +#: views.py:222 +#, python-format +msgid "Timestamp: %s" +msgstr "" + +#: views.py:223 +#, python-format +msgid "Signee: %s" +msgstr "" + +#: views.py:228 +#, python-format +msgid "signature properties for: %s" +msgstr "" + +#: views.py:250 +msgid "Detached signature uploaded successfully." +msgstr "" + +#: views.py:259 +#, python-format +msgid "Upload detached signature for: %s" +msgstr "" + +#: conf/settings.py:13 +msgid "List of keyservers to be queried for unknown keys." +msgstr "" diff --git a/apps/django_gpg/locale/ru/LC_MESSAGES/django.mo b/apps/django_gpg/locale/ru/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..93a6b82858 Binary files /dev/null and b/apps/django_gpg/locale/ru/LC_MESSAGES/django.mo differ diff --git a/apps/django_gpg/locale/ru/LC_MESSAGES/django.po b/apps/django_gpg/locale/ru/LC_MESSAGES/django.po new file mode 100644 index 0000000000..fdc8b3cca2 --- /dev/null +++ b/apps/django_gpg/locale/ru/LC_MESSAGES/django.po @@ -0,0 +1,292 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2011-12-06 02:06-0400\n" +"PO-Revision-Date: 2011-12-05 17:43+0000\n" +"Last-Translator: rosarior \n" +"Language-Team: Russian (http://www.transifex.net/projects/p/mayan-edms/team/" +"ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" + +#: __init__.py:14 +msgid "Verify document signatures" +msgstr "" + +#: __init__.py:15 +msgid "View keys" +msgstr "" + +#: __init__.py:16 +msgid "Delete keys" +msgstr "" + +#: __init__.py:17 +msgid "Query keyservers" +msgstr "" + +#: __init__.py:18 +msgid "Import key from keyservers" +msgstr "" + +#: __init__.py:19 +msgid "Upload detached signatures" +msgstr "" + +#: __init__.py:20 +msgid "Download detached signatures" +msgstr "" + +#: __init__.py:23 +msgid "Signatures" +msgstr "" + +#: __init__.py:39 views.py:67 +msgid "private keys" +msgstr "" + +#: __init__.py:40 views.py:70 +msgid "public keys" +msgstr "" + +#: __init__.py:41 +msgid "delete" +msgstr "" + +#: __init__.py:42 +msgid "query keyservers" +msgstr "" + +#: __init__.py:43 +msgid "import" +msgstr "" + +#: __init__.py:44 +msgid "upload signature" +msgstr "" + +#: __init__.py:45 +msgid "download signature" +msgstr "" + +#: __init__.py:46 +msgid "key management" +msgstr "" + +#: __init__.py:49 +msgid "signatures" +msgstr "" + +#: api.py:22 +msgid "Public" +msgstr "" + +#: api.py:23 +msgid "Secret" +msgstr "" + +#: api.py:31 api.py:36 +msgid "RSA" +msgstr "" + +#: api.py:32 +msgid "DSA" +msgstr "" + +#: api.py:37 +msgid "Elgamal" +msgstr "" + +#: api.py:51 +msgid "Bad signature." +msgstr "" + +#: api.py:55 +msgid "Document not signed or invalid signature." +msgstr "" + +#: api.py:59 +msgid "Signature error." +msgstr "" + +#: api.py:63 +msgid "Document is signed but no public key is available for verification." +msgstr "" + +#: api.py:67 +msgid "Document is signed, and signature is good." +msgstr "" + +#: api.py:71 +msgid "Document is signed with a valid signature." +msgstr "" + +#: api.py:144 +msgid "unknown" +msgstr "" + +#: forms.py:11 +msgid "Term" +msgstr "" + +#: forms.py:12 +msgid "Name, e-mail, key ID or key fingerprint to look for." +msgstr "" + +#: forms.py:18 +msgid "Signature file" +msgstr "" + +#: views.py:45 +#, python-format +msgid "Key: %s, imported successfully." +msgstr "" + +#: views.py:48 +#, python-format +msgid "Unable to import key id: %s" +msgstr "" + +#: views.py:52 +msgid "Import key" +msgstr "" + +#: views.py:53 +#, python-format +msgid "Are you sure you wish to import key id: %s?" +msgstr "" + +#: views.py:78 +msgid "Key ID" +msgstr "" + +#: views.py:82 +msgid "Owner" +msgstr "" + +#: views.py:102 +#, python-format +msgid "Key: %s, deleted successfully." +msgstr "" + +#: views.py:109 +msgid "Delete key" +msgstr "" + +#: views.py:111 +#, python-format +msgid "" +"Are you sure you wish to delete key: %s? If you try to delete a public key " +"that is part of a public/private pair the private key will be deleted as " +"well." +msgstr "" + +#: views.py:129 +msgid "Query key server" +msgstr "" + +#: views.py:142 +msgid "results" +msgstr "" + +#: views.py:147 +msgid "ID" +msgstr "" + +#: views.py:151 +msgid "type" +msgstr "" + +#: views.py:155 +msgid "creation date" +msgstr "" + +#: views.py:159 +msgid "disabled" +msgstr "" + +#: views.py:163 +msgid "expiration date" +msgstr "" + +#: views.py:167 +msgid "expired" +msgstr "" + +#: views.py:171 +msgid "length" +msgstr "" + +#: views.py:175 +msgid "revoked" +msgstr "" + +#: views.py:180 +msgid "Identifies" +msgstr "" + +#: views.py:205 +#, python-format +msgid "Signature status: %(widget)s %(text)s" +msgstr "" + +#: views.py:212 +msgid "embedded" +msgstr "" + +#: views.py:214 +msgid "detached" +msgstr "" + +#: views.py:219 +#, python-format +msgid "Signature ID: %s" +msgstr "" + +#: views.py:220 +#, python-format +msgid "Signature type: %s" +msgstr "" + +#: views.py:221 +#, python-format +msgid "Key ID: %s" +msgstr "" + +#: views.py:222 +#, python-format +msgid "Timestamp: %s" +msgstr "" + +#: views.py:223 +#, python-format +msgid "Signee: %s" +msgstr "" + +#: views.py:228 +#, python-format +msgid "signature properties for: %s" +msgstr "" + +#: views.py:250 +msgid "Detached signature uploaded successfully." +msgstr "" + +#: views.py:259 +#, python-format +msgid "Upload detached signature for: %s" +msgstr "" + +#: conf/settings.py:13 +msgid "List of keyservers to be queried for unknown keys." +msgstr "" diff --git a/apps/django_gpg/models.py b/apps/django_gpg/models.py new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/apps/django_gpg/models.py @@ -0,0 +1 @@ + diff --git a/apps/django_gpg/runtime.py b/apps/django_gpg/runtime.py new file mode 100644 index 0000000000..b3303059ff --- /dev/null +++ b/apps/django_gpg/runtime.py @@ -0,0 +1,4 @@ +from django_gpg.api import GPG +from django_gpg.conf.settings import KEYSERVERS + +gpg = GPG(keyservers=KEYSERVERS) diff --git a/apps/django_gpg/static/images/icons/cross.png b/apps/django_gpg/static/images/icons/cross.png new file mode 100644 index 0000000000..4ee1253736 Binary files /dev/null and b/apps/django_gpg/static/images/icons/cross.png differ diff --git a/apps/django_gpg/static/images/icons/document_signature.png b/apps/django_gpg/static/images/icons/document_signature.png new file mode 100644 index 0000000000..60155cf1ba Binary files /dev/null and b/apps/django_gpg/static/images/icons/document_signature.png differ diff --git a/apps/django_gpg/static/images/icons/key.png b/apps/django_gpg/static/images/icons/key.png new file mode 100644 index 0000000000..3cf460e112 Binary files /dev/null and b/apps/django_gpg/static/images/icons/key.png differ diff --git a/apps/django_gpg/static/images/icons/key_add.png b/apps/django_gpg/static/images/icons/key_add.png new file mode 100644 index 0000000000..da22eb0570 Binary files /dev/null and b/apps/django_gpg/static/images/icons/key_add.png differ diff --git a/apps/django_gpg/static/images/icons/key_delete.png b/apps/django_gpg/static/images/icons/key_delete.png new file mode 100644 index 0000000000..9462a1272d Binary files /dev/null and b/apps/django_gpg/static/images/icons/key_delete.png differ diff --git a/apps/django_gpg/static/images/icons/user_silhouette.png b/apps/django_gpg/static/images/icons/user_silhouette.png new file mode 100644 index 0000000000..109d113eec Binary files /dev/null and b/apps/django_gpg/static/images/icons/user_silhouette.png differ diff --git a/apps/django_gpg/urls.py b/apps/django_gpg/urls.py new file mode 100644 index 0000000000..4a22882d06 --- /dev/null +++ b/apps/django_gpg/urls.py @@ -0,0 +1,12 @@ +from django.conf.urls.defaults import patterns, url + +urlpatterns = patterns('django_gpg.views', + url(r'^delete/(?P.+)/(?P\w+)/$', 'key_delete', (), 'key_delete'), + url(r'^list/private/$', 'key_list', {'secret': True}, 'key_private_list'), + url(r'^list/public/$', 'key_list', {'secret': False}, 'key_public_list'), + url(r'^verify/(?P\d+)/$', 'document_verify', (), 'document_verify'), + url(r'^upload/signature/(?P\d+)/$', 'document_signature_upload', (), 'document_signature_upload'), + url(r'^download/signature/(?P\d+)/$', 'document_signature_download', (), 'document_signature_download'), + url(r'^query/$', 'key_query', (), 'key_query'), + url(r'^receive/(?P.+)/$', 'key_receive', (), 'key_receive'), +) diff --git a/apps/django_gpg/views.py b/apps/django_gpg/views.py new file mode 100644 index 0000000000..d908d011e0 --- /dev/null +++ b/apps/django_gpg/views.py @@ -0,0 +1,285 @@ +from datetime import datetime +import logging + +from django.utils.translation import ugettext_lazy as _ +from django.http import HttpResponseRedirect +from django.shortcuts import render_to_response, get_object_or_404 +from django.template import RequestContext +from django.contrib import messages +from django.core.urlresolvers import reverse +from django.utils.safestring import mark_safe +from django.conf import settings +from django.template.defaultfilters import force_escape + +from documents.models import Document, RecentDocument +from permissions.api import check_permissions +from common.utils import pretty_size, parse_range, urlquote, \ + return_diff, encapsulate +from filetransfers.api import serve_file + +from django_gpg.api import Key, SIGNATURE_STATES +from django_gpg.runtime import gpg +from django_gpg.exceptions import GPGVerificationError, KeyFetchingError +from django_gpg import (PERMISSION_DOCUMENT_VERIFY, PERMISSION_KEY_VIEW, + PERMISSION_KEY_DELETE, PERMISSION_KEYSERVER_QUERY, + PERMISSION_KEY_RECEIVE, PERMISSION_SIGNATURE_UPLOAD, + PERMISSION_SIGNATURE_DOWNLOAD) +from django_gpg.forms import KeySearchForm, DetachedSignatureForm + +logger = logging.getLogger(__name__) + + +def key_receive(request, key_id): + check_permissions(request.user, [PERMISSION_KEY_RECEIVE]) + + post_action_redirect = None + previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', '/'))) + next = request.POST.get('next', request.GET.get('next', post_action_redirect if post_action_redirect else request.META.get('HTTP_REFERER', '/'))) + + if request.method == 'POST': + try: + term = request.GET.get('term') + results = gpg.query(term) + keys_dict = dict([(key.keyid, key) for key in results]) + key = gpg.import_key(keys_dict[key_id].key) + messages.success(request, _(u'Key: %s, imported successfully.') % key) + return HttpResponseRedirect(next) + except (KeyFetchingError, KeyError, TypeError): + messages.error(request, _(u'Unable to import key id: %s') % key_id) + return HttpResponseRedirect(previous) + + return render_to_response('generic_confirm.html', { + 'title': _(u'Import key'), + 'message': _(u'Are you sure you wish to import key id: %s?') % key_id, + 'form_icon': 'key_add.png', + 'next': next, + 'previous': previous, + 'submit_method': 'GET', + + }, context_instance=RequestContext(request)) + + +def key_list(request, secret=True): + check_permissions(request.user, [PERMISSION_KEY_VIEW]) + + if secret: + object_list = Key.get_all(gpg, secret=True) + title = _(u'private keys') + else: + object_list = Key.get_all(gpg) + title = _(u'public keys') + + return render_to_response('generic_list.html', { + 'object_list': object_list, + 'title': title, + 'hide_object': True, + 'extra_columns': [ + { + 'name': _(u'Key ID'), + 'attribute': 'key_id', + }, + { + 'name': _(u'Owner'), + 'attribute': encapsulate(lambda x: u', '.join(x.uids)), + }, + ] + }, context_instance=RequestContext(request)) + + +def key_delete(request, fingerprint, key_type): + check_permissions(request.user, [PERMISSION_KEY_DELETE]) + + secret = key_type == 'sec' + key = Key.get(gpg, fingerprint, secret=secret) + + post_action_redirect = None + previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', '/'))) + next = request.POST.get('next', request.GET.get('next', post_action_redirect if post_action_redirect else request.META.get('HTTP_REFERER', '/'))) + + if request.method == 'POST': + try: + gpg.delete_key(key) + messages.success(request, _(u'Key: %s, deleted successfully.') % fingerprint) + return HttpResponseRedirect(next) + except Exception, msg: + messages.error(request, msg) + return HttpResponseRedirect(previous) + + return render_to_response('generic_confirm.html', { + 'title': _(u'Delete key'), + 'delete_view': True, + 'message': _(u'Are you sure you wish to delete key: %s? If you try to delete a public key that is part of a public/private pair the private key will be deleted as well.') % key, + 'form_icon': 'key_delete.png', + 'next': next, + 'previous': previous, + }, context_instance=RequestContext(request)) + + +def key_query(request): + check_permissions(request.user, [PERMISSION_KEYSERVER_QUERY]) + + subtemplates_list = [] + term = request.GET.get('term') + + form = KeySearchForm(initial={'term': term}) + subtemplates_list.append( + { + 'name': 'generic_form_subtemplate.html', + 'context': { + 'title': _(u'Query key server'), + 'form': form, + 'submit_method': 'GET', + }, + } + ) + + if term: + results = gpg.query(term) + subtemplates_list.append( + { + 'name': 'generic_list_subtemplate.html', + 'context': { + 'title': _(u'results'), + 'object_list': results, + 'hide_object': True, + 'extra_columns': [ + { + 'name': _(u'ID'), + 'attribute': 'keyid', + }, + { + 'name': _(u'type'), + 'attribute': 'algo', + }, + { + 'name': _(u'creation date'), + 'attribute': 'creation_date', + }, + { + 'name': _(u'disabled'), + 'attribute': 'disabled', + }, + { + 'name': _(u'expiration date'), + 'attribute': 'expiration_date', + }, + { + 'name': _(u'expired'), + 'attribute': 'expired', + }, + { + 'name': _(u'length'), + 'attribute': 'key_length', + }, + { + 'name': _(u'revoked'), + 'attribute': 'revoked', + }, + + { + 'name': _(u'Identifies'), + 'attribute': encapsulate(lambda x: u', '.join([identity.uid for identity in x.identities])), + }, + ] + }, + } + ) + + return render_to_response('generic_form.html', { + 'subtemplates_list': subtemplates_list, + }, context_instance=RequestContext(request)) + + +def document_verify(request, document_pk): + check_permissions(request.user, [PERMISSION_DOCUMENT_VERIFY]) + document = get_object_or_404(Document, pk=document_pk) + + RecentDocument.objects.add_document_for_user(request.user, document) + + signature = document.verify_signature() + + signature_state = SIGNATURE_STATES.get(getattr(signature, 'status', None)) + + widget = (u'' % (settings.STATIC_URL, signature_state['icon'])) + paragraphs = [ + _(u'Signature status: %(widget)s %(text)s') % { + 'widget': mark_safe(widget), + 'text': signature_state['text'] + }, + ] + + if document.signature_state: + signature_type = _(u'embedded') + else: + signature_type = _(u'detached') + + if signature: + paragraphs.extend( + [ + _(u'Signature ID: %s') % signature.signature_id, + _(u'Signature type: %s') % signature_type, + _(u'Key ID: %s') % signature.key_id, + _(u'Timestamp: %s') % datetime.fromtimestamp(int(signature.sig_timestamp)), + _(u'Signee: %s') % force_escape(getattr(signature, 'username', u'')), + ] + ) + + return render_to_response('generic_template.html', { + 'title': _(u'signature properties for: %s') % document, + 'object': document, + 'document': document, + 'paragraphs': paragraphs, + }, context_instance=RequestContext(request)) + + +def document_signature_upload(request, document_pk): + check_permissions(request.user, [PERMISSION_SIGNATURE_UPLOAD]) + document = get_object_or_404(Document, pk=document_pk) + + RecentDocument.objects.add_document_for_user(request.user, document) + + post_action_redirect = None + previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', '/'))) + next = request.POST.get('next', request.GET.get('next', post_action_redirect if post_action_redirect else request.META.get('HTTP_REFERER', '/'))) + + if request.method == 'POST': + form = DetachedSignatureForm(request.POST, request.FILES) + if form.is_valid(): + try: + document.add_detached_signature(request.FILES['file']) + messages.success(request, _(u'Detached signature uploaded successfully.')) + return HttpResponseRedirect(next) + except Exception, msg: + messages.error(request, msg) + return HttpResponseRedirect(previous) + else: + form = DetachedSignatureForm() + + return render_to_response('generic_form.html', { + 'title': _(u'Upload detached signature for: %s') % document, + 'form_icon': 'key_delete.png', + 'next': next, + 'form': form, + 'previous': previous, + 'object': document, + }, context_instance=RequestContext(request)) + + +def document_signature_download(request, document_pk): + check_permissions(request.user, [PERMISSION_SIGNATURE_DOWNLOAD]) + document = get_object_or_404(Document, pk=document_pk) + + try: + if document.has_detached_signature(): + signature = document.detached_signature() + return serve_file( + request, + signature, + save_as=u'"%s.sig"' % document.filename, + content_type=u'application/octet-stream' + ) + except Exception, e: + messages.error(request, e) + return HttpResponseRedirect(request.META['HTTP_REFERER']) + + return HttpResponseRedirect(request.META['HTTP_REFERER']) diff --git a/apps/document_comments/locale/es/LC_MESSAGES/django.mo b/apps/document_comments/locale/es/LC_MESSAGES/django.mo index 47f03d30c8..d2d8efb3e2 100644 Binary files a/apps/document_comments/locale/es/LC_MESSAGES/django.mo and b/apps/document_comments/locale/es/LC_MESSAGES/django.mo differ diff --git a/apps/document_comments/locale/pt/LC_MESSAGES/django.mo b/apps/document_comments/locale/pt/LC_MESSAGES/django.mo index 3c578fea0e..83bf515a70 100644 Binary files a/apps/document_comments/locale/pt/LC_MESSAGES/django.mo and b/apps/document_comments/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/document_comments/locale/ru/LC_MESSAGES/django.mo b/apps/document_comments/locale/ru/LC_MESSAGES/django.mo index 718e22ed33..b31ea9bdc2 100644 Binary files a/apps/document_comments/locale/ru/LC_MESSAGES/django.mo and b/apps/document_comments/locale/ru/LC_MESSAGES/django.mo differ diff --git a/apps/document_indexing/api.py b/apps/document_indexing/api.py index b2e166f347..269d5ceddd 100644 --- a/apps/document_indexing/api.py +++ b/apps/document_indexing/api.py @@ -15,7 +15,7 @@ from document_indexing.filesystem import fs_create_index_directory, \ fs_create_document_link, fs_delete_document_link, \ fs_delete_index_directory, fs_delete_directory_recusive from document_indexing.conf.settings import SLUGIFY_PATHS -from document_indexing.os_agnostic import assemble_document_filename +from document_indexing.os_specifics import assemble_suffixed_filename if SLUGIFY_PATHS == False: # Do not slugify path or filenames and extensions @@ -127,13 +127,14 @@ def do_rebuild_all_indexes(): # Internal functions def find_lowest_available_suffix(index_instance, document): - index_instance_documents = DocumentRenameCount.objects.filter(index_instance=index_instance).filter(document__file_extension=document.file_extension) + # TODO: verify extension's role in query + index_instance_documents = DocumentRenameCount.objects.filter(index_instance=index_instance)#.filter(document__file_extension=document.file_extension) files_list = [] for index_instance_document in index_instance_documents: - files_list.append(assemble_document_filename(index_instance_document.document, index_instance_document.suffix)) + files_list.append(assemble_suffixed_filename(index_instance_document.document.file.name, index_instance_document.suffix)) for suffix in xrange(MAX_SUFFIX_COUNT): - if assemble_document_filename(document, suffix) not in files_list: + if assemble_suffixed_filename(document.file.name, suffix) not in files_list: return suffix raise MaxSuffixCountReached(ugettext(u'Maximum suffix (%s) count reached.') % MAX_SUFFIX_COUNT) diff --git a/apps/document_indexing/filesystem.py b/apps/document_indexing/filesystem.py index 04744f4254..c89dad4cf6 100644 --- a/apps/document_indexing/filesystem.py +++ b/apps/document_indexing/filesystem.py @@ -3,7 +3,8 @@ import os from django.utils.translation import ugettext_lazy as _ -from document_indexing.os_agnostic import assemble_document_filename +from document_indexing.os_specifics import (assemble_suffixed_filename, + assemble_path_from_list) from document_indexing.conf.settings import FILESERVING_ENABLE from document_indexing.conf.settings import FILESERVING_PATH @@ -19,12 +20,12 @@ def get_instance_path(index_instance): names.append(index_instance.value) - return os.sep.join(names) + return assemble_path_from_list(names) def fs_create_index_directory(index_instance): if FILESERVING_ENABLE: - target_directory = os.path.join(FILESERVING_PATH, get_instance_path(index_instance)) + target_directory = assemble_path_from_list([FILESERVING_PATH, get_instance_path(index_instance)]) try: os.mkdir(target_directory) except OSError, exc: @@ -36,9 +37,9 @@ def fs_create_index_directory(index_instance): def fs_create_document_link(index_instance, document, suffix=0): if FILESERVING_ENABLE: - name_part = assemble_document_filename(document, suffix) - filename = os.extsep.join([name_part, document.file_extension]) - filepath = os.path.join(FILESERVING_PATH, get_instance_path(index_instance), filename) + filename = assemble_suffixed_filename(document.file.name, suffix) + filepath = assemble_path_from_list([FILESERVING_PATH, get_instance_path(index_instance), filename]) + try: os.symlink(document.file.path, filepath) except OSError, exc: @@ -56,12 +57,8 @@ def fs_create_document_link(index_instance, document, suffix=0): def fs_delete_document_link(index_instance, document, suffix=0): if FILESERVING_ENABLE: - name_part = document.file_filename - if suffix: - name_part = u'_'.join([name_part, unicode(suffix)]) - - filename = os.extsep.join([name_part, document.file_extension]) - filepath = os.path.join(FILESERVING_PATH, get_instance_path(index_instance), filename) + filename = assemble_suffixed_filename(document.file.name, suffix) + filepath = assemble_path_from_list([FILESERVING_PATH, get_instance_path(index_instance), filename]) try: os.unlink(filepath) @@ -73,7 +70,7 @@ def fs_delete_document_link(index_instance, document, suffix=0): def fs_delete_index_directory(index_instance): if FILESERVING_ENABLE: - target_directory = os.path.join(FILESERVING_PATH, get_instance_path(index_instance)) + target_directory = assemble_path_from_list([FILESERVING_PATH, get_instance_path(index_instance)]) try: os.removedirs(target_directory) except OSError, exc: diff --git a/apps/document_indexing/locale/es/LC_MESSAGES/django.mo b/apps/document_indexing/locale/es/LC_MESSAGES/django.mo index 659ff46558..d52ef7c7cd 100644 Binary files a/apps/document_indexing/locale/es/LC_MESSAGES/django.mo and b/apps/document_indexing/locale/es/LC_MESSAGES/django.mo differ diff --git a/apps/document_indexing/locale/pt/LC_MESSAGES/django.mo b/apps/document_indexing/locale/pt/LC_MESSAGES/django.mo index 5c371fea17..3039293cfc 100644 Binary files a/apps/document_indexing/locale/pt/LC_MESSAGES/django.mo and b/apps/document_indexing/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/document_indexing/locale/ru/LC_MESSAGES/django.mo b/apps/document_indexing/locale/ru/LC_MESSAGES/django.mo index bd8617e870..59a1601a1a 100644 Binary files a/apps/document_indexing/locale/ru/LC_MESSAGES/django.mo and b/apps/document_indexing/locale/ru/LC_MESSAGES/django.mo differ diff --git a/apps/document_indexing/os_agnostic.py b/apps/document_indexing/os_agnostic.py deleted file mode 100644 index ac8ef648ec..0000000000 --- a/apps/document_indexing/os_agnostic.py +++ /dev/null @@ -1,8 +0,0 @@ -from document_indexing.conf.settings import SUFFIX_SEPARATOR - - -def assemble_document_filename(document, suffix=0): - if suffix: - return SUFFIX_SEPARATOR.join([document.file_filename, unicode(suffix)]) - else: - return document.file_filename diff --git a/apps/document_indexing/os_specifics.py b/apps/document_indexing/os_specifics.py new file mode 100644 index 0000000000..90a9ba8a75 --- /dev/null +++ b/apps/document_indexing/os_specifics.py @@ -0,0 +1,20 @@ +import os + +from document_indexing.conf.settings import SUFFIX_SEPARATOR + + +def assemble_suffixed_filename(filename, suffix=0): + ''' + Split document filename, to attach suffix to the name part then + re attacht the extension + ''' + + if suffix: + name, extension = filename.split(os.split(os.extsep)) + return SUFFIX_SEPARATOR.join([name, unicode(suffix), os.extsep, extension]) + else: + return file_filename + + +def assemble_path_from_list(directory_list): + return os.sep.join(directory_list) diff --git a/apps/documents/__init__.py b/apps/documents/__init__.py index 5ff5a84c9a..26c3e9852d 100644 --- a/apps/documents/__init__.py +++ b/apps/documents/__init__.py @@ -13,17 +13,18 @@ from history.api import register_history_type from metadata.api import get_metadata_string from project_setup.api import register_setup -from documents.models import Document, DocumentPage, \ - DocumentPageTransformation, DocumentType, DocumentTypeFilename -from documents.literals import PERMISSION_DOCUMENT_CREATE, \ - PERMISSION_DOCUMENT_PROPERTIES_EDIT, PERMISSION_DOCUMENT_VIEW, \ - PERMISSION_DOCUMENT_DELETE, PERMISSION_DOCUMENT_DOWNLOAD, \ - PERMISSION_DOCUMENT_TRANSFORM, PERMISSION_DOCUMENT_TOOLS, \ - PERMISSION_DOCUMENT_EDIT -from documents.literals import PERMISSION_DOCUMENT_TYPE_EDIT, \ - PERMISSION_DOCUMENT_TYPE_DELETE, PERMISSION_DOCUMENT_TYPE_CREATE -from documents.literals import HISTORY_DOCUMENT_CREATED, \ - HISTORY_DOCUMENT_EDITED, HISTORY_DOCUMENT_DELETED +from documents.models import (Document, DocumentPage, + DocumentPageTransformation, DocumentType, DocumentTypeFilename, + DocumentVersion) +from documents.literals import (PERMISSION_DOCUMENT_CREATE, + PERMISSION_DOCUMENT_PROPERTIES_EDIT, PERMISSION_DOCUMENT_VIEW, + PERMISSION_DOCUMENT_DELETE, PERMISSION_DOCUMENT_DOWNLOAD, + PERMISSION_DOCUMENT_TRANSFORM, PERMISSION_DOCUMENT_TOOLS, + PERMISSION_DOCUMENT_EDIT, PERMISSION_DOCUMENT_VERSION_REVERT) +from documents.literals import (PERMISSION_DOCUMENT_TYPE_EDIT, + PERMISSION_DOCUMENT_TYPE_DELETE, PERMISSION_DOCUMENT_TYPE_CREATE) +from documents.literals import (HISTORY_DOCUMENT_CREATED, + HISTORY_DOCUMENT_EDITED, HISTORY_DOCUMENT_DELETED) from documents.conf.settings import ZOOM_MAX_LEVEL from documents.conf.settings import ZOOM_MIN_LEVEL from documents.conf import settings as document_settings @@ -35,7 +36,7 @@ def is_first_page(context): def is_last_page(context): - return context['page'].page_number >= context['page'].document.documentpage_set.count() + return context['page'].page_number >= context['page'].document_version.pages.count() def is_min_zoom(context): @@ -45,6 +46,10 @@ def is_min_zoom(context): def is_max_zoom(context): return context['zoom'] >= ZOOM_MAX_LEVEL + +def is_current_version(context): + return context['object'].document.latest_version.timestamp == context['object'].timestamp + # Permission setup set_namespace_title('documents', _(u'Documents')) register_permission(PERMISSION_DOCUMENT_CREATE) @@ -55,6 +60,7 @@ register_permission(PERMISSION_DOCUMENT_DELETE) register_permission(PERMISSION_DOCUMENT_DOWNLOAD) register_permission(PERMISSION_DOCUMENT_TRANSFORM) register_permission(PERMISSION_DOCUMENT_TOOLS) +register_permission(PERMISSION_DOCUMENT_VERSION_REVERT) # Document type permissions set_namespace_title('documents_setup', _(u'Documents setup')) @@ -69,7 +75,7 @@ register_history_type(HISTORY_DOCUMENT_DELETED) document_list = {'text': _(u'all documents'), 'view': 'document_list', 'famfam': 'page', 'permissions': [PERMISSION_DOCUMENT_VIEW]} document_list_recent = {'text': _(u'recent documents'), 'view': 'document_list_recent', 'famfam': 'page', 'permissions': [PERMISSION_DOCUMENT_VIEW]} -document_create_multiple = {'text': _(u'upload new documents'), 'view': 'document_create_multiple', 'famfam': 'page_add', 'permissions': [PERMISSION_DOCUMENT_CREATE], 'children_view_regex': ['upload']} +document_create_multiple = {'text': _(u'upload new documents'), 'view': 'document_create_multiple', 'famfam': 'page_add', 'permissions': [PERMISSION_DOCUMENT_CREATE], 'children_view_regex': [r'upload_interactive']} document_create_siblings = {'text': _(u'clone metadata'), 'view': 'document_create_siblings', 'args': 'object.id', 'famfam': 'page_copy', 'permissions': [PERMISSION_DOCUMENT_CREATE]} document_view_simple = {'text': _(u'details'), 'view': 'document_view_simple', 'args': 'object.id', 'famfam': 'page', 'permissions': [PERMISSION_DOCUMENT_VIEW]} document_view_advanced = {'text': _(u'properties'), 'view': 'document_view_advanced', 'args': 'object.id', 'famfam': 'page_gear', 'permissions': [PERMISSION_DOCUMENT_VIEW]} @@ -78,14 +84,20 @@ document_multiple_delete = {'text': _(u'delete'), 'view': 'document_multiple_del document_edit = {'text': _(u'edit'), 'view': 'document_edit', 'args': 'object.id', 'famfam': 'page_edit', 'permissions': [PERMISSION_DOCUMENT_PROPERTIES_EDIT]} document_preview = {'text': _(u'preview'), 'class': 'fancybox', 'view': 'document_preview', 'args': 'object.id', 'famfam': 'magnifier', 'permissions': [PERMISSION_DOCUMENT_VIEW]} document_download = {'text': _(u'download'), 'view': 'document_download', 'args': 'object.id', 'famfam': 'page_save', 'permissions': [PERMISSION_DOCUMENT_DOWNLOAD]} -document_find_duplicates = {'text': _(u'find duplicates'), 'view': 'document_find_duplicates', 'args': 'object.id', 'famfam': 'page_refresh', 'permissions': [PERMISSION_DOCUMENT_VIEW]} -document_find_all_duplicates = {'text': _(u'find all duplicates'), 'view': 'document_find_all_duplicates', 'famfam': 'page_refresh', 'permissions': [PERMISSION_DOCUMENT_VIEW], 'description': _(u'Search all the documents\' checksums and return a list of the exact matches.')} +document_version_download = {'text': _(u'download'), 'view': 'document_version_download', 'args': 'object.pk', 'famfam': 'page_save', 'permissions': [PERMISSION_DOCUMENT_DOWNLOAD]} +document_find_duplicates = {'text': _(u'find duplicates'), 'view': 'document_find_duplicates', 'args': 'object.id', 'famfam': 'page_white_copy', 'permissions': [PERMISSION_DOCUMENT_VIEW]} +document_find_all_duplicates = {'text': _(u'find all duplicates'), 'view': 'document_find_all_duplicates', 'famfam': 'page_white_copy', 'permissions': [PERMISSION_DOCUMENT_VIEW], 'description': _(u'Search all the documents\' checksums and return a list of the exact matches.')} document_update_page_count = {'text': _(u'update office documents\' page count'), 'view': 'document_update_page_count', 'famfam': 'page_white_csharp', 'permissions': [PERMISSION_DOCUMENT_TOOLS], 'description': _(u'Update the page count of the office type documents. This is useful when enabling office document support after there were already office type documents in the database.')} document_clear_transformations = {'text': _(u'clear transformations'), 'view': 'document_clear_transformations', 'args': 'object.id', 'famfam': 'page_paintbrush', 'permissions': [PERMISSION_DOCUMENT_TRANSFORM]} document_multiple_clear_transformations = {'text': _(u'clear transformations'), 'view': 'document_multiple_clear_transformations', 'famfam': 'page_paintbrush', 'permissions': [PERMISSION_DOCUMENT_TRANSFORM]} document_print = {'text': _(u'print'), 'view': 'document_print', 'args': 'object.id', 'famfam': 'printer', 'permissions': [PERMISSION_DOCUMENT_VIEW]} document_history_view = {'text': _(u'history'), 'view': 'history_for_object', 'args': ['"documents"', '"document"', 'object.id'], 'famfam': 'book_go', 'permissions': [PERMISSION_DOCUMENT_VIEW]} +document_missing_list = {'text': _(u'Find missing document files'), 'view': 'document_missing_list', 'famfam': 'folder_page', 'permissions': [PERMISSION_DOCUMENT_VIEW]} +# Tools +document_clear_image_cache = {'text': _(u'Clear the document image cache'), 'view': 'document_clear_image_cache', 'famfam': 'camera_delete', 'permissions': [PERMISSION_DOCUMENT_TOOLS], 'description': _(u'Clear the graphics representations used to speed up the documents\' display and interactive transformations results.')} + +# Document pages document_page_transformation_list = {'text': _(u'page transformations'), 'class': 'no-parent-history', 'view': 'document_page_transformation_list', 'args': 'page.pk', 'famfam': 'pencil_go', 'permissions': [PERMISSION_DOCUMENT_TRANSFORM]} document_page_transformation_create = {'text': _(u'create new transformation'), 'class': 'no-parent-history', 'view': 'document_page_transformation_create', 'args': 'page.pk', 'famfam': 'pencil_add', 'permissions': [PERMISSION_DOCUMENT_TRANSFORM]} document_page_transformation_edit = {'text': _(u'edit'), 'class': 'no-parent-history', 'view': 'document_page_transformation_edit', 'args': 'transformation.pk', 'famfam': 'pencil_go', 'permissions': [PERMISSION_DOCUMENT_TRANSFORM]} @@ -104,7 +116,9 @@ document_page_rotate_right = {'text': _(u'rotate right'), 'class': 'no-parent-hi document_page_rotate_left = {'text': _(u'rotate left'), 'class': 'no-parent-history', 'view': 'document_page_rotate_left', 'args': 'page.pk', 'famfam': 'arrow_turn_left', 'permissions': [PERMISSION_DOCUMENT_VIEW]} document_page_view_reset = {'text': _(u'reset view'), 'class': 'no-parent-history', 'view': 'document_page_view_reset', 'args': 'page.pk', 'famfam': 'page_white', 'permissions': [PERMISSION_DOCUMENT_VIEW]} -document_missing_list = {'text': _(u'Find missing document files'), 'view': 'document_missing_list', 'famfam': 'folder_page', 'permissions': [PERMISSION_DOCUMENT_VIEW]} +# Document versions +document_version_list = {'text': _(u'versions'), 'view': 'document_version_list', 'args': 'object.pk', 'famfam': 'page_world', 'permissions': [PERMISSION_DOCUMENT_VIEW]} +document_version_revert = {'text': _(u'revert'), 'view': 'document_version_revert', 'args': 'object.pk', 'famfam': 'page_refresh', 'permissions': [PERMISSION_DOCUMENT_VERSION_REVERT], 'conditional_disable': is_current_version} # Document type related links document_type_list = {'text': _(u'document type list'), 'view': 'document_type_list', 'famfam': 'layout', 'permissions': [PERMISSION_DOCUMENT_VIEW]} @@ -132,6 +146,9 @@ register_links(['document_type_filename_create', 'document_type_filename_list', register_links(Document, [document_edit, document_print, document_delete, document_download, document_find_duplicates, document_clear_transformations, document_create_siblings]) register_multi_item_links(['document_find_duplicates', 'folder_view', 'index_instance_list', 'document_type_document_list', 'search', 'results', 'document_group_view', 'document_list', 'document_list_recent'], [document_multiple_clear_transformations, document_multiple_delete]) +# Document Version links +register_links(DocumentVersion, [document_version_revert, document_version_download]) + secondary_menu_links = [document_list_recent, document_list, document_create_multiple] register_links(['document_list_recent', 'document_list', 'document_create', 'document_create_multiple', 'upload_interactive', 'staging_file_delete'], secondary_menu_links, menu_name='secondary_menu') @@ -158,7 +175,7 @@ register_links(['document_page_transformation_edit', 'document_page_transformati register_diagnostic('documents', _(u'Documents'), document_missing_list) -register_maintenance_links([document_find_all_duplicates, document_update_page_count], namespace='documents', title=_(u'documents')) +register_maintenance_links([document_find_all_duplicates, document_update_page_count, document_clear_image_cache], namespace='documents', title=_(u'documents')) #def document_exists(document): # try: @@ -187,7 +204,8 @@ register_top_menu( children_path_regex=[ r'^documents/[^t]', r'^metadata/[^s]', r'comments', r'tags/document', r'grouping/[^s]', r'history/list/for_object/documents' ], - children_views=['document_folder_list', 'folder_add_document', 'document_index_list'], + #children_view_regex=[r'upload'], + children_views=['document_folder_list', 'folder_add_document', 'document_index_list', 'upload_version',], position=1 ) @@ -197,6 +215,7 @@ register_sidebar_template(['document_type_list'], 'document_types_help.html') register_links(Document, [document_view_simple], menu_name='form_header', position=0) register_links(Document, [document_view_advanced], menu_name='form_header', position=1) register_links(Document, [document_history_view], menu_name='form_header') +register_links(Document, [document_version_list], menu_name='form_header') if (validate_path(document_settings.CACHE_PATH) == False) or (not document_settings.CACHE_PATH): setattr(document_settings, 'CACHE_PATH', tempfile.mkdtemp()) diff --git a/apps/documents/admin.py b/apps/documents/admin.py index ff4aa33cb0..8fc0e838bb 100644 --- a/apps/documents/admin.py +++ b/apps/documents/admin.py @@ -2,11 +2,29 @@ from django.contrib import admin from metadata.admin import DocumentMetadataInline -from documents.models import DocumentType, Document, \ - DocumentTypeFilename, DocumentPage, \ - DocumentPageTransformation, RecentDocument +from documents.models import (DocumentType, Document, + DocumentTypeFilename, DocumentPage, + DocumentPageTransformation, RecentDocument, + DocumentVersion) +class DocumentPageInline(admin.StackedInline): + model = DocumentPage + extra = 1 + classes = ('collapse-open',) + allow_add = True + + +class DocumentVersionInline(admin.StackedInline): + model = DocumentVersion + extra = 1 + classes = ('collapse-open',) + allow_add = True + #inlines = [ + # DocumentPageInline, + #] + + class DocumentTypeFilenameInline(admin.StackedInline): model = DocumentTypeFilename extra = 1 @@ -24,18 +42,11 @@ class DocumentPageTransformationAdmin(admin.ModelAdmin): model = DocumentPageTransformation -class DocumentPageInline(admin.StackedInline): - model = DocumentPage - extra = 1 - classes = ('collapse-open',) - allow_add = True - - class DocumentAdmin(admin.ModelAdmin): inlines = [ - DocumentMetadataInline, DocumentPageInline + DocumentMetadataInline, DocumentVersionInline ] - list_display = ('uuid', 'file_filename', 'file_extension') + list_display = ('uuid', 'file_filename',) class RecentDocumentAdmin(admin.ModelAdmin): diff --git a/apps/documents/forms.py b/apps/documents/forms.py index 320b60bc71..63e4470a55 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): @@ -100,8 +102,10 @@ class DocumentPagesCarouselWidget(forms.widgets.Widget): output = [] output.append(u'
') - for page in value.documentpage_set.all(): + for page in value.pages.all(): + output.append(u'
') + output.append(u'
%(page_string)s %(page)s
' % {'page_string': ugettext(u'Page'), 'page': page.page_number}) output.append( document_html_widget( page.document, @@ -128,7 +132,7 @@ 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.documentpage_set.count() + self.fields['preview'].label = _(u'Document pages (%s)') % document.pages.count() preview = forms.CharField(widget=DocumentPagesCarouselWidget()) @@ -152,6 +156,13 @@ class DocumentForm(forms.ModelForm): # To allow merging with DocumentForm_edit self.fields['document_type'].widget = forms.HiddenInput() + if instance: + self.fields['use_file_name'] = forms.BooleanField( + label=_(u'Use the new version filename as the document filename'), + initial=False, + required=False, + ) + # Instance's document_type overrides the passed document_type if instance: if hasattr(instance, 'document_type'): @@ -165,10 +176,50 @@ class DocumentForm(forms.ModelForm): required=False, label=_(u'Quick document rename')) + if instance: + self.version_fields(instance) + + def version_fields(self, document): + self.fields['version_update'] = forms.ChoiceField( + label=_(u'Version update'), + 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, + ) + + self.fields['serial'] = forms.IntegerField( + label=_(u'Release level serial'), + initial=0, + widget=forms.widgets.TextInput( + attrs = {'style': 'width: auto;'} + ), + ) + + self.fields['comment'] = forms.CharField( + label=_(u'Comment'), + required=False, + widget=forms.widgets.Textarea(attrs={'rows': 4}), + ) + 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 +228,14 @@ 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') + self.fields.pop('use_file_name') class DocumentPropertiesForm(DetailForm): @@ -198,7 +257,7 @@ class DocumentContentForm(forms.Form): super(DocumentContentForm, self).__init__(*args, **kwargs) content = [] self.fields['contents'].initial = u'' - for page in self.document.documentpage_set.all(): + for page in self.document.pages.all(): if page.content: content.append(page.content) content.append(u'\n\n\n - Page %s - \n\n\n' % page.page_number) @@ -220,10 +279,10 @@ class DocumentTypeSelectForm(forms.Form): class PrintForm(forms.Form): - page_size = forms.ChoiceField(choices=PAGE_SIZE_CHOICES, initial=DEFAULT_PAPER_SIZE, label=_(u'Page size'), required=False) - custom_page_width = forms.CharField(label=_(u'Custom page width'), required=False) - custom_page_height = forms.CharField(label=_(u'Custom page height'), required=False) - page_orientation = forms.ChoiceField(choices=PAGE_ORIENTATION_CHOICES, initial=DEFAULT_PAGE_ORIENTATION, label=_(u'Page orientation'), required=True) + #page_size = forms.ChoiceField(choices=PAGE_SIZE_CHOICES, initial=DEFAULT_PAPER_SIZE, label=_(u'Page size'), required=False) + #custom_page_width = forms.CharField(label=_(u'Custom page width'), required=False) + #custom_page_height = forms.CharField(label=_(u'Custom page height'), required=False) + #page_orientation = forms.ChoiceField(choices=PAGE_ORIENTATION_CHOICES, initial=DEFAULT_PAGE_ORIENTATION, label=_(u'Page orientation'), required=True) page_range = forms.CharField(label=_(u'Page range'), required=False) diff --git a/apps/documents/literals.py b/apps/documents/literals.py index cbb3b919d9..54df1d03d2 100644 --- a/apps/documents/literals.py +++ b/apps/documents/literals.py @@ -13,6 +13,7 @@ PERMISSION_DOCUMENT_DELETE = {'namespace': 'documents', 'name': 'document_delete PERMISSION_DOCUMENT_DOWNLOAD = {'namespace': 'documents', 'name': 'document_download', 'label': _(u'Download documents')} PERMISSION_DOCUMENT_TRANSFORM = {'namespace': 'documents', 'name': 'document_transform', 'label': _(u'Transform documents')} PERMISSION_DOCUMENT_TOOLS = {'namespace': 'documents', 'name': 'document_tools', 'label': _(u'Execute document modifying tools')} +PERMISSION_DOCUMENT_VERSION_REVERT = {'namespace': 'documents', 'name': 'document_version_revert', 'label': _(u'Revert documents to a previous version')} PERMISSION_DOCUMENT_TYPE_EDIT = {'namespace': 'documents_setup', 'name': 'document_type_edit', 'label': _(u'Edit document types')} PERMISSION_DOCUMENT_TYPE_DELETE = {'namespace': 'documents_setup', 'name': 'document_type_delete', 'label': _(u'Delete document types')} @@ -44,3 +45,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/locale/pt/LC_MESSAGES/django.mo b/apps/documents/locale/pt/LC_MESSAGES/django.mo index cdde47b0a9..4e663ecd4d 100644 Binary files a/apps/documents/locale/pt/LC_MESSAGES/django.mo and b/apps/documents/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/documents/migrations/0001_initial.py b/apps/documents/migrations/0001_initial.py new file mode 100644 index 0000000000..25acbc89de --- /dev/null +++ b/apps/documents/migrations/0001_initial.py @@ -0,0 +1,219 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Adding model 'DocumentType' + db.create_table('documents_documenttype', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('name', self.gf('django.db.models.fields.CharField')(max_length=32)), + )) + db.send_create_signal('documents', ['DocumentType']) + + # Adding model 'Document' + db.create_table('documents_document', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('document_type', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['documents.DocumentType'], null=True, blank=True)), + ('file', self.gf('django.db.models.fields.files.FileField')(max_length=100)), + ('uuid', self.gf('django.db.models.fields.CharField')(default=u'107e50a8-83b3-46da-bd14-460489527ab1', max_length=48, blank=True)), + ('file_mimetype', self.gf('django.db.models.fields.CharField')(default='', max_length=64)), + ('file_mime_encoding', self.gf('django.db.models.fields.CharField')(default='', max_length=64)), + ('file_filename', self.gf('django.db.models.fields.CharField')(default=u'', max_length=255, db_index=True)), + ('file_extension', self.gf('django.db.models.fields.CharField')(default=u'', max_length=16, db_index=True)), + ('date_added', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, db_index=True, blank=True)), + ('date_updated', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)), + ('checksum', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), + ('description', self.gf('django.db.models.fields.TextField')(db_index=True, null=True, blank=True)), + )) + db.send_create_signal('documents', ['Document']) + + # Adding model 'DocumentTypeFilename' + db.create_table('documents_documenttypefilename', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('document_type', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['documents.DocumentType'])), + ('filename', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)), + ('enabled', self.gf('django.db.models.fields.BooleanField')(default=True)), + )) + db.send_create_signal('documents', ['DocumentTypeFilename']) + + # Adding model 'DocumentPage' + db.create_table('documents_documentpage', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('document', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['documents.Document'])), + ('content', self.gf('django.db.models.fields.TextField')(db_index=True, null=True, blank=True)), + ('page_label', self.gf('django.db.models.fields.CharField')(max_length=32, null=True, blank=True)), + ('page_number', self.gf('django.db.models.fields.PositiveIntegerField')(default=1, db_index=True)), + )) + db.send_create_signal('documents', ['DocumentPage']) + + # Adding model 'DocumentPageTransformation' + db.create_table('documents_documentpagetransformation', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('document_page', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['documents.DocumentPage'])), + ('order', self.gf('django.db.models.fields.PositiveIntegerField')(default=0, null=True, db_index=True, blank=True)), + ('transformation', self.gf('django.db.models.fields.CharField')(max_length=128)), + ('arguments', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), + )) + db.send_create_signal('documents', ['DocumentPageTransformation']) + + # Adding model 'RecentDocument' + db.create_table('documents_recentdocument', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])), + ('document', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['documents.Document'])), + ('datetime_accessed', self.gf('django.db.models.fields.DateTimeField')(db_index=True)), + )) + db.send_create_signal('documents', ['RecentDocument']) + + + def backwards(self, orm): + + # Deleting model 'DocumentType' + db.delete_table('documents_documenttype') + + # Deleting model 'Document' + db.delete_table('documents_document') + + # Deleting model 'DocumentTypeFilename' + db.delete_table('documents_documenttypefilename') + + # Deleting model 'DocumentPage' + db.delete_table('documents_documentpage') + + # Deleting model 'DocumentPageTransformation' + db.delete_table('documents_documentpagetransformation') + + # Deleting model 'RecentDocument' + db.delete_table('documents_recentdocument') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + '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': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + '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', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + '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', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'comments.comment': { + 'Meta': {'ordering': "('submit_date',)", 'object_name': 'Comment', 'db_table': "'django_comments'"}, + 'comment': ('django.db.models.fields.TextField', [], {'max_length': '3000'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'content_type_set_for_comment'", 'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip_address': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_removed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'object_pk': ('django.db.models.fields.TextField', [], {}), + 'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sites.Site']"}), + 'submit_date': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'comment_comments'", 'null': 'True', 'to': "orm['auth.User']"}), + 'user_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'user_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), + 'user_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + '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'}), + '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'}) + }, + 'documents.document': { + 'Meta': {'ordering': "['-date_added']", 'object_name': 'Document'}, + 'checksum': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'date_added': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'date_updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'document_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentType']", 'null': 'True', 'blank': 'True'}), + 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), + 'file_extension': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '16', 'db_index': 'True'}), + 'file_filename': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'db_index': 'True'}), + 'file_mime_encoding': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64'}), + 'file_mimetype': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'uuid': ('django.db.models.fields.CharField', [], {'default': "u'107e50a8-83b3-46da-bd14-460489527ab1'", 'max_length': '48', 'blank': 'True'}) + }, + 'documents.documentpage': { + 'Meta': {'ordering': "['page_number']", 'object_name': 'DocumentPage'}, + 'content': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.Document']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'page_label': ('django.db.models.fields.CharField', [], {'max_length': '32', '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'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '32'}) + }, + 'documents.documenttypefilename': { + 'Meta': {'ordering': "['filename']", 'object_name': 'DocumentTypeFilename'}, + 'document_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentType']"}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + 'documents.recentdocument': { + 'Meta': {'ordering': "('-datetime_accessed',)", 'object_name': 'RecentDocument'}, + 'datetime_accessed': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.Document']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'sites.site': { + 'Meta': {'ordering': "('domain',)", 'object_name': 'Site', 'db_table': "'django_site'"}, + 'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'taggit.tag': { + 'Meta': {'object_name': 'Tag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100', 'db_index': 'True'}) + }, + 'taggit.taggeditem': { + 'Meta': {'object_name': 'TaggedItem'}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_tagged_items'", 'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_items'", 'to': "orm['taggit.Tag']"}) + } + } + + complete_apps = ['documents'] diff --git a/apps/documents/migrations/0002_filename_extension_merge.py b/apps/documents/migrations/0002_filename_extension_merge.py new file mode 100644 index 0000000000..6d7dc4182b --- /dev/null +++ b/apps/documents/migrations/0002_filename_extension_merge.py @@ -0,0 +1,146 @@ +# encoding: utf-8 +import os +import datetime +from south.db import db +from south.v2 import DataMigration +from django.db import models + +class Migration(DataMigration): + + def forwards(self, orm): + "Write your forwards methods here." + for document in orm.Document.objects.all(): + document.file_filename = os.extsep.join([document.file_filename, document.file_extension]) + document.save() + + def backwards(self, orm): + "Write your backwards methods here." + for document in orm.Document.objects.all(): + document.file_filename, document.file_extension = document.file_filename.split(os.extsep) + document.save() + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + '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': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + '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', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + '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', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'comments.comment': { + 'Meta': {'ordering': "('submit_date',)", 'object_name': 'Comment', 'db_table': "'django_comments'"}, + 'comment': ('django.db.models.fields.TextField', [], {'max_length': '3000'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'content_type_set_for_comment'", 'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip_address': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_removed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'object_pk': ('django.db.models.fields.TextField', [], {}), + 'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sites.Site']"}), + 'submit_date': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'comment_comments'", 'null': 'True', 'to': "orm['auth.User']"}), + 'user_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'user_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), + 'user_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + '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'}), + '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'}) + }, + 'documents.document': { + 'Meta': {'ordering': "['-date_added']", 'object_name': 'Document'}, + 'checksum': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'date_added': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'date_updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'document_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentType']", 'null': 'True', 'blank': 'True'}), + 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), + 'file_extension': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '16', 'db_index': 'True'}), + 'file_filename': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'db_index': 'True'}), + 'file_mime_encoding': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64'}), + 'file_mimetype': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'uuid': ('django.db.models.fields.CharField', [], {'default': "u'28bd60c6-a5c2-4adb-8dab-1b6c0098cc9c'", 'max_length': '48', 'blank': 'True'}) + }, + 'documents.documentpage': { + 'Meta': {'ordering': "['page_number']", 'object_name': 'DocumentPage'}, + 'content': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.Document']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'page_label': ('django.db.models.fields.CharField', [], {'max_length': '32', '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'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '32'}) + }, + 'documents.documenttypefilename': { + 'Meta': {'ordering': "['filename']", 'object_name': 'DocumentTypeFilename'}, + 'document_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentType']"}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + 'documents.recentdocument': { + 'Meta': {'ordering': "('-datetime_accessed',)", 'object_name': 'RecentDocument'}, + 'datetime_accessed': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.Document']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'sites.site': { + 'Meta': {'ordering': "('domain',)", 'object_name': 'Site', 'db_table': "'django_site'"}, + 'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'taggit.tag': { + 'Meta': {'object_name': 'Tag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100', 'db_index': 'True'}) + }, + 'taggit.taggeditem': { + 'Meta': {'object_name': 'TaggedItem'}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_tagged_items'", 'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_items'", 'to': "orm['taggit.Tag']"}) + } + } + + complete_apps = ['documents'] diff --git a/apps/documents/migrations/0003_auto__del_field_document_file_extension.py b/apps/documents/migrations/0003_auto__del_field_document_file_extension.py new file mode 100644 index 0000000000..85f3a1821f --- /dev/null +++ b/apps/documents/migrations/0003_auto__del_field_document_file_extension.py @@ -0,0 +1,144 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Deleting field 'Document.file_extension' + db.delete_column('documents_document', 'file_extension') + + + def backwards(self, orm): + + # Adding field 'Document.file_extension' + db.add_column('documents_document', 'file_extension', self.gf('django.db.models.fields.CharField')(default=u'', max_length=16, db_index=True), keep_default=False) + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + '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': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + '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', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + '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', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'comments.comment': { + 'Meta': {'ordering': "('submit_date',)", 'object_name': 'Comment', 'db_table': "'django_comments'"}, + 'comment': ('django.db.models.fields.TextField', [], {'max_length': '3000'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'content_type_set_for_comment'", 'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip_address': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_removed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'object_pk': ('django.db.models.fields.TextField', [], {}), + 'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sites.Site']"}), + 'submit_date': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'comment_comments'", 'null': 'True', 'to': "orm['auth.User']"}), + 'user_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'user_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), + 'user_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + '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'}), + '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'}) + }, + 'documents.document': { + 'Meta': {'ordering': "['-date_added']", 'object_name': 'Document'}, + 'checksum': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'date_added': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'date_updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'document_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentType']", 'null': 'True', 'blank': 'True'}), + 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), + 'file_filename': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'db_index': 'True'}), + 'file_mime_encoding': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64'}), + 'file_mimetype': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'uuid': ('django.db.models.fields.CharField', [], {'default': "u'06a88ff6-11b2-44b3-8409-21bd58577d4f'", 'max_length': '48', 'blank': 'True'}) + }, + 'documents.documentpage': { + 'Meta': {'ordering': "['page_number']", 'object_name': 'DocumentPage'}, + 'content': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.Document']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'page_label': ('django.db.models.fields.CharField', [], {'max_length': '32', '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'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '32'}) + }, + 'documents.documenttypefilename': { + 'Meta': {'ordering': "['filename']", 'object_name': 'DocumentTypeFilename'}, + 'document_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentType']"}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + 'documents.recentdocument': { + 'Meta': {'ordering': "('-datetime_accessed',)", 'object_name': 'RecentDocument'}, + 'datetime_accessed': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.Document']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'sites.site': { + 'Meta': {'ordering': "('domain',)", 'object_name': 'Site', 'db_table': "'django_site'"}, + 'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'taggit.tag': { + 'Meta': {'object_name': 'Tag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100', 'db_index': 'True'}) + }, + 'taggit.taggeditem': { + 'Meta': {'object_name': 'TaggedItem'}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_tagged_items'", 'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_items'", 'to': "orm['taggit.Tag']"}) + } + } + + complete_apps = ['documents'] diff --git a/apps/documents/migrations/0004_auto__add_documentversion__add_unique_documentversion_document_mayor_m.py b/apps/documents/migrations/0004_auto__add_documentversion__add_unique_documentversion_document_mayor_m.py new file mode 100644 index 0000000000..e6b0c9bf10 --- /dev/null +++ b/apps/documents/migrations/0004_auto__add_documentversion__add_unique_documentversion_document_mayor_m.py @@ -0,0 +1,188 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Adding model 'DocumentVersion' + db.create_table('documents_documentversion', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('document', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['documents.Document'], null=True, blank=True)), + ('mayor', self.gf('django.db.models.fields.PositiveIntegerField')(default=1)), + ('minor', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)), + ('micro', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)), + ('release_level', self.gf('django.db.models.fields.PositiveIntegerField')(default=1)), + ('serial', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)), + ('timestamp', self.gf('django.db.models.fields.DateTimeField')()), + ('file', self.gf('django.db.models.fields.files.FileField')(max_length=100)), + ('mimetype', self.gf('django.db.models.fields.CharField')(default='', max_length=64)), + ('encoding', self.gf('django.db.models.fields.CharField')(default='', max_length=64)), + ('filename', self.gf('django.db.models.fields.CharField')(default=u'', max_length=255, db_index=True)), + ('checksum', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), + )) + db.send_create_signal('documents', ['DocumentVersion']) + + # Adding unique constraint on 'DocumentVersion', fields ['document', 'mayor', 'minor', 'micro', 'release_level', 'serial'] + db.create_unique('documents_documentversion', ['document_id', 'mayor', 'minor', 'micro', 'release_level', 'serial']) + + # Adding field 'DocumentPage.document_version' + db.add_column('documents_documentpage', 'document_version', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['documents.DocumentVersion'], null=True, blank=True), keep_default=False) + + + def backwards(self, orm): + + # Removing unique constraint on 'DocumentVersion', fields ['document', 'mayor', 'minor', 'micro', 'release_level', 'serial'] + db.delete_unique('documents_documentversion', ['document_id', 'mayor', 'minor', 'micro', 'release_level', 'serial']) + + # Deleting model 'DocumentVersion' + db.delete_table('documents_documentversion') + + # Deleting field 'DocumentPage.document_version' + db.delete_column('documents_documentpage', 'document_version_id') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + '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': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + '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', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + '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', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'comments.comment': { + 'Meta': {'ordering': "('submit_date',)", 'object_name': 'Comment', 'db_table': "'django_comments'"}, + 'comment': ('django.db.models.fields.TextField', [], {'max_length': '3000'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'content_type_set_for_comment'", 'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip_address': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_removed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'object_pk': ('django.db.models.fields.TextField', [], {}), + 'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sites.Site']"}), + 'submit_date': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'comment_comments'", 'null': 'True', 'to': "orm['auth.User']"}), + 'user_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'user_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), + 'user_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + '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'}), + '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'}) + }, + 'documents.document': { + 'Meta': {'ordering': "['-date_added']", 'object_name': 'Document'}, + 'checksum': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'date_added': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'date_updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'document_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentType']", 'null': 'True', 'blank': 'True'}), + 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), + 'file_filename': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'db_index': 'True'}), + 'file_mime_encoding': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64'}), + 'file_mimetype': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'uuid': ('django.db.models.fields.CharField', [], {'default': "u'a8389d7d-b9f4-4e51-ac24-dd9dd310fd8c'", 'max_length': '48', 'blank': 'True'}) + }, + 'documents.documentpage': { + 'Meta': {'ordering': "['page_number']", 'object_name': 'DocumentPage'}, + 'content': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.Document']"}), + 'document_version': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentVersion']", 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'page_label': ('django.db.models.fields.CharField', [], {'max_length': '32', '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'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '32'}) + }, + 'documents.documenttypefilename': { + 'Meta': {'ordering': "['filename']", 'object_name': 'DocumentTypeFilename'}, + 'document_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentType']"}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + 'documents.documentversion': { + 'Meta': {'unique_together': "(('document', 'mayor', 'minor', 'micro', 'release_level', 'serial'),)", 'object_name': 'DocumentVersion'}, + 'checksum': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.Document']", 'null': 'True', 'blank': 'True'}), + 'encoding': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64'}), + 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), + 'filename': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'mayor': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'micro': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'mimetype': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64'}), + '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', [], {}) + }, + 'documents.recentdocument': { + 'Meta': {'ordering': "('-datetime_accessed',)", 'object_name': 'RecentDocument'}, + 'datetime_accessed': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.Document']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'sites.site': { + 'Meta': {'ordering': "('domain',)", 'object_name': 'Site', 'db_table': "'django_site'"}, + 'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'taggit.tag': { + 'Meta': {'object_name': 'Tag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100', 'db_index': 'True'}) + }, + 'taggit.taggeditem': { + 'Meta': {'object_name': 'TaggedItem'}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_tagged_items'", 'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_items'", 'to': "orm['taggit.Tag']"}) + } + } + + complete_apps = ['documents'] diff --git a/apps/documents/migrations/0005_document_versions.py b/apps/documents/migrations/0005_document_versions.py new file mode 100644 index 0000000000..7244593da3 --- /dev/null +++ b/apps/documents/migrations/0005_document_versions.py @@ -0,0 +1,176 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import DataMigration +from django.db import models + +class Migration(DataMigration): + + def forwards(self, orm): + for document in orm.Document.objects.all(): + document_version = document.documentversion_set.create( + document = document, + timestamp = document.date_added, + file = document.file, + mimetype = document.file_mimetype, + encoding = document.file_mime_encoding, + filename = document.file_filename, + checksum = document.checksum, + ) + document_version.save() + for document_page in document.documentpage_set.all(): + document_page.document_version = document_version + document_page.save() + + def backwards(self, orm): + for document in orm.Document.objects.all(): + document_version = document.documentversion_set.all()[0] + document.date_added = document_version.timestamp + document.file = document_version.file + document.file_mimetype = document_version.mimetype + document.file_mime_encoding = document_version.encoding + document.filename = document_version.filename + document.checksum = document_version.checksum + document.save() + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + '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': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + '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', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + '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', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'comments.comment': { + 'Meta': {'ordering': "('submit_date',)", 'object_name': 'Comment', 'db_table': "'django_comments'"}, + 'comment': ('django.db.models.fields.TextField', [], {'max_length': '3000'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'content_type_set_for_comment'", 'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip_address': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_removed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'object_pk': ('django.db.models.fields.TextField', [], {}), + 'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sites.Site']"}), + 'submit_date': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'comment_comments'", 'null': 'True', 'to': "orm['auth.User']"}), + 'user_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'user_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), + 'user_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + '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'}), + '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'}) + }, + 'documents.document': { + 'Meta': {'ordering': "['-date_added']", 'object_name': 'Document'}, + 'checksum': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'date_added': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'date_updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'document_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentType']", 'null': 'True', 'blank': 'True'}), + 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), + 'file_filename': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'db_index': 'True'}), + 'file_mime_encoding': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64'}), + 'file_mimetype': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'uuid': ('django.db.models.fields.CharField', [], {'default': "u'6c189f1f-1d85-48b5-9b7d-e8e319603e77'", 'max_length': '48', 'blank': 'True'}) + }, + 'documents.documentpage': { + 'Meta': {'ordering': "['page_number']", 'object_name': 'DocumentPage'}, + 'content': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.Document']"}), + 'document_version': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentVersion']", 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'page_label': ('django.db.models.fields.CharField', [], {'max_length': '32', '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'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '32'}) + }, + 'documents.documenttypefilename': { + 'Meta': {'ordering': "['filename']", 'object_name': 'DocumentTypeFilename'}, + 'document_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentType']"}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + 'documents.documentversion': { + 'Meta': {'unique_together': "(('document', 'mayor', 'minor', 'micro', 'release_level', 'serial'),)", 'object_name': 'DocumentVersion'}, + 'checksum': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.Document']", 'null': 'True', 'blank': 'True'}), + 'encoding': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64'}), + 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), + 'filename': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'mayor': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'micro': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'mimetype': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64'}), + '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', [], {}) + }, + 'documents.recentdocument': { + 'Meta': {'ordering': "('-datetime_accessed',)", 'object_name': 'RecentDocument'}, + 'datetime_accessed': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.Document']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'sites.site': { + 'Meta': {'ordering': "('domain',)", 'object_name': 'Site', 'db_table': "'django_site'"}, + 'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'taggit.tag': { + 'Meta': {'object_name': 'Tag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100', 'db_index': 'True'}) + }, + 'taggit.taggeditem': { + 'Meta': {'object_name': 'TaggedItem'}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_tagged_items'", 'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_items'", 'to': "orm['taggit.Tag']"}) + } + } + + complete_apps = ['documents'] diff --git a/apps/documents/migrations/0006_fix_invalid_document_version_id_keys.py b/apps/documents/migrations/0006_fix_invalid_document_version_id_keys.py new file mode 100644 index 0000000000..03f4ba4133 --- /dev/null +++ b/apps/documents/migrations/0006_fix_invalid_document_version_id_keys.py @@ -0,0 +1,159 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import DataMigration +from django.db import models + +class Migration(DataMigration): + + def forwards(self, orm): + qs = orm.DocumentPage.objects.filter(document_version=None) + #print 'Invalid document pages to delete: %s' % qs.count() + for document_page in qs: + document_page.delete() + + def backwards(self, orm): + def backwards(self, orm): + raise RuntimeError('Cannot reverse this migration.') + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + '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': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + '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', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + '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', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'comments.comment': { + 'Meta': {'ordering': "('submit_date',)", 'object_name': 'Comment', 'db_table': "'django_comments'"}, + 'comment': ('django.db.models.fields.TextField', [], {'max_length': '3000'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'content_type_set_for_comment'", 'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip_address': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_removed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'object_pk': ('django.db.models.fields.TextField', [], {}), + 'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sites.Site']"}), + 'submit_date': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'comment_comments'", 'null': 'True', 'to': "orm['auth.User']"}), + 'user_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'user_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), + 'user_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + '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'}), + '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'}) + }, + 'documents.document': { + 'Meta': {'ordering': "['-date_added']", 'object_name': 'Document'}, + 'checksum': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'date_added': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'date_updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'document_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentType']", 'null': 'True', 'blank': 'True'}), + 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), + 'file_filename': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'db_index': 'True'}), + 'file_mime_encoding': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64'}), + 'file_mimetype': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'uuid': ('django.db.models.fields.CharField', [], {'default': "u'6c189f1f-1d85-48b5-9b7d-e8e319603e77'", 'max_length': '48', 'blank': 'True'}) + }, + 'documents.documentpage': { + 'Meta': {'ordering': "['page_number']", 'object_name': 'DocumentPage'}, + 'content': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.Document']"}), + 'document_version': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentVersion']", 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'page_label': ('django.db.models.fields.CharField', [], {'max_length': '32', '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'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '32'}) + }, + 'documents.documenttypefilename': { + 'Meta': {'ordering': "['filename']", 'object_name': 'DocumentTypeFilename'}, + 'document_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentType']"}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + 'documents.documentversion': { + 'Meta': {'unique_together': "(('document', 'mayor', 'minor', 'micro', 'release_level', 'serial'),)", 'object_name': 'DocumentVersion'}, + 'checksum': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.Document']", 'null': 'True', 'blank': 'True'}), + 'encoding': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64'}), + 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), + 'filename': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'mayor': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'micro': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'mimetype': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64'}), + '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', [], {}) + }, + 'documents.recentdocument': { + 'Meta': {'ordering': "('-datetime_accessed',)", 'object_name': 'RecentDocument'}, + 'datetime_accessed': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.Document']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'sites.site': { + 'Meta': {'ordering': "('domain',)", 'object_name': 'Site', 'db_table': "'django_site'"}, + 'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'taggit.tag': { + 'Meta': {'object_name': 'Tag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100', 'db_index': 'True'}) + }, + 'taggit.taggeditem': { + 'Meta': {'object_name': 'TaggedItem'}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_tagged_items'", 'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_items'", 'to': "orm['taggit.Tag']"}) + } + } + + complete_apps = ['documents'] diff --git a/apps/documents/migrations/0007_remove_old_file_fields.py b/apps/documents/migrations/0007_remove_old_file_fields.py new file mode 100644 index 0000000000..02c074fcfd --- /dev/null +++ b/apps/documents/migrations/0007_remove_old_file_fields.py @@ -0,0 +1,202 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Changing field 'DocumentVersion.document' + db.alter_column('documents_documentversion', 'document_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['documents.Document'])) + + # Deleting field 'Document.date_updated' + db.delete_column('documents_document', 'date_updated') + + # Deleting field 'Document.file' + db.delete_column('documents_document', 'file') + + # Deleting field 'Document.file_filename' + db.delete_column('documents_document', 'file_filename') + + # Deleting field 'Document.file_mimetype' + db.delete_column('documents_document', 'file_mimetype') + + # Deleting field 'Document.checksum' + db.delete_column('documents_document', 'checksum') + + # Deleting field 'Document.file_mime_encoding' + db.delete_column('documents_document', 'file_mime_encoding') + + # Deleting field 'DocumentPage.document' + db.delete_column('documents_documentpage', 'document_id') + + # Changing field 'DocumentPage.document_version' + db.alter_column('documents_documentpage', 'document_version_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['documents.DocumentVersion'])) + + + def backwards(self, orm): + + # Changing field 'DocumentVersion.document' + db.alter_column('documents_documentversion', 'document_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['documents.Document'], null=True)) + + # Adding field 'Document.date_updated' + db.add_column('documents_document', 'date_updated', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, default=datetime.datetime(2011, 12, 2, 2, 17, 25, 53565), blank=True), keep_default=False) + + # Adding field 'Document.file' + db.add_column('documents_document', 'file', self.gf('django.db.models.fields.files.FileField')(default='', max_length=100), keep_default=False) + + # Adding field 'Document.file_filename' + db.add_column('documents_document', 'file_filename', self.gf('django.db.models.fields.CharField')(default=u'', max_length=255, db_index=True), keep_default=False) + + # Adding field 'Document.file_mimetype' + db.add_column('documents_document', 'file_mimetype', self.gf('django.db.models.fields.CharField')(default='', max_length=64), keep_default=False) + + # Adding field 'Document.checksum' + db.add_column('documents_document', 'checksum', self.gf('django.db.models.fields.TextField')(null=True, blank=True), keep_default=False) + + # Adding field 'Document.file_mime_encoding' + db.add_column('documents_document', 'file_mime_encoding', self.gf('django.db.models.fields.CharField')(default='', max_length=64), keep_default=False) + + # Adding field 'DocumentPage.document' + db.add_column('documents_documentpage', 'document', self.gf('django.db.models.fields.related.ForeignKey')(default=0, to=orm['documents.Document']), keep_default=False) + + # Changing field 'DocumentPage.document_version' + db.alter_column('documents_documentpage', 'document_version_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['documents.DocumentVersion'], null=True)) + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + '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': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + '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', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + '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', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'comments.comment': { + 'Meta': {'ordering': "('submit_date',)", 'object_name': 'Comment', 'db_table': "'django_comments'"}, + 'comment': ('django.db.models.fields.TextField', [], {'max_length': '3000'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'content_type_set_for_comment'", 'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip_address': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_removed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'object_pk': ('django.db.models.fields.TextField', [], {}), + 'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sites.Site']"}), + 'submit_date': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'comment_comments'", 'null': 'True', 'to': "orm['auth.User']"}), + 'user_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'user_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), + 'user_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + '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'}), + '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'}) + }, + 'documents.document': { + 'Meta': {'ordering': "['-date_added']", 'object_name': 'Document'}, + 'date_added': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'document_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentType']", 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'uuid': ('django.db.models.fields.CharField', [], {'default': "u'83100718-e901-4880-95f8-3618749c8a99'", 'max_length': '48', 'blank': 'True'}) + }, + 'documents.documentpage': { + 'Meta': {'ordering': "['page_number']", 'object_name': 'DocumentPage'}, + 'content': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'document_version': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentVersion']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'page_label': ('django.db.models.fields.CharField', [], {'max_length': '32', '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'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '32'}) + }, + 'documents.documenttypefilename': { + 'Meta': {'ordering': "['filename']", 'object_name': 'DocumentTypeFilename'}, + 'document_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentType']"}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + 'documents.documentversion': { + 'Meta': {'unique_together': "(('document', 'mayor', 'minor', 'micro', 'release_level', 'serial'),)", 'object_name': 'DocumentVersion'}, + 'checksum': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.Document']"}), + 'encoding': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64'}), + 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), + 'filename': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'mayor': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'micro': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'mimetype': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64'}), + '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', [], {}) + }, + 'documents.recentdocument': { + 'Meta': {'ordering': "('-datetime_accessed',)", 'object_name': 'RecentDocument'}, + 'datetime_accessed': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.Document']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'sites.site': { + 'Meta': {'ordering': "('domain',)", 'object_name': 'Site', 'db_table': "'django_site'"}, + 'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'taggit.tag': { + 'Meta': {'object_name': 'Tag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100', 'db_index': 'True'}) + }, + 'taggit.taggeditem': { + 'Meta': {'object_name': 'TaggedItem'}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_tagged_items'", 'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_items'", 'to': "orm['taggit.Tag']"}) + } + } + + complete_apps = ['documents'] diff --git a/apps/documents/migrations/0008_fix_mayor_field_name.py b/apps/documents/migrations/0008_fix_mayor_field_name.py new file mode 100644 index 0000000000..dc540dfbec --- /dev/null +++ b/apps/documents/migrations/0008_fix_mayor_field_name.py @@ -0,0 +1,172 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Removing unique constraint on 'DocumentVersion', fields ['release_level', 'micro', 'serial', 'document', 'mayor', 'minor'] + db.delete_unique('documents_documentversion', ['release_level', 'micro', 'serial', 'document_id', 'mayor', 'minor']) + + # Deleting field 'DocumentVersion.mayor' + db.delete_column('documents_documentversion', 'mayor') + + # Adding field 'DocumentVersion.major' + db.add_column('documents_documentversion', 'major', self.gf('django.db.models.fields.PositiveIntegerField')(default=1), keep_default=False) + + # Adding unique constraint on 'DocumentVersion', fields ['major', 'release_level', 'micro', 'serial', 'document', 'minor'] + db.create_unique('documents_documentversion', ['major', 'release_level', 'micro', 'serial', 'document_id', 'minor']) + + + def backwards(self, orm): + + # Removing unique constraint on 'DocumentVersion', fields ['major', 'release_level', 'micro', 'serial', 'document', 'minor'] + db.delete_unique('documents_documentversion', ['major', 'release_level', 'micro', 'serial', 'document_id', 'minor']) + + # Adding field 'DocumentVersion.mayor' + db.add_column('documents_documentversion', 'mayor', self.gf('django.db.models.fields.PositiveIntegerField')(default=1), keep_default=False) + + # Deleting field 'DocumentVersion.major' + db.delete_column('documents_documentversion', 'major') + + # Adding unique constraint on 'DocumentVersion', fields ['release_level', 'micro', 'serial', 'document', 'mayor', 'minor'] + db.create_unique('documents_documentversion', ['release_level', 'micro', 'serial', 'document_id', 'mayor', 'minor']) + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + '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': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + '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', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + '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', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'comments.comment': { + 'Meta': {'ordering': "('submit_date',)", 'object_name': 'Comment', 'db_table': "'django_comments'"}, + 'comment': ('django.db.models.fields.TextField', [], {'max_length': '3000'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'content_type_set_for_comment'", 'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip_address': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_removed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'object_pk': ('django.db.models.fields.TextField', [], {}), + 'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sites.Site']"}), + 'submit_date': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'comment_comments'", 'null': 'True', 'to': "orm['auth.User']"}), + 'user_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'user_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), + 'user_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + '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'}), + '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'}) + }, + 'documents.document': { + 'Meta': {'ordering': "['-date_added']", 'object_name': 'Document'}, + 'date_added': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'document_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentType']", 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'uuid': ('django.db.models.fields.CharField', [], {'default': "u'750a3848-39cf-45a5-9a96-e948d09833d7'", 'max_length': '48', 'blank': 'True'}) + }, + 'documents.documentpage': { + 'Meta': {'ordering': "['page_number']", 'object_name': 'DocumentPage'}, + 'content': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'document_version': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentVersion']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'page_label': ('django.db.models.fields.CharField', [], {'max_length': '32', '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'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '32'}) + }, + 'documents.documenttypefilename': { + 'Meta': {'ordering': "['filename']", 'object_name': 'DocumentTypeFilename'}, + 'document_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentType']"}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + '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'}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.Document']"}), + 'encoding': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64'}), + 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), + 'filename': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'db_index': 'True'}), + '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', [], {'default': "''", 'max_length': '64'}), + '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', [], {}) + }, + 'documents.recentdocument': { + 'Meta': {'ordering': "('-datetime_accessed',)", 'object_name': 'RecentDocument'}, + 'datetime_accessed': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.Document']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'sites.site': { + 'Meta': {'ordering': "('domain',)", 'object_name': 'Site', 'db_table': "'django_site'"}, + 'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'taggit.tag': { + 'Meta': {'object_name': 'Tag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100', 'db_index': 'True'}) + }, + 'taggit.taggeditem': { + 'Meta': {'object_name': 'TaggedItem'}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_tagged_items'", 'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_items'", 'to': "orm['taggit.Tag']"}) + } + } + + complete_apps = ['documents'] diff --git a/apps/documents/migrations/0009_add_comment_field.py b/apps/documents/migrations/0009_add_comment_field.py new file mode 100644 index 0000000000..2010270407 --- /dev/null +++ b/apps/documents/migrations/0009_add_comment_field.py @@ -0,0 +1,155 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Adding field 'DocumentVersion.comment' + db.add_column('documents_documentversion', 'comment', self.gf('django.db.models.fields.TextField')(default='', blank=True), keep_default=False) + + + def backwards(self, orm): + + # Deleting field 'DocumentVersion.comment' + db.delete_column('documents_documentversion', 'comment') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + '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': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + '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', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + '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', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'comments.comment': { + 'Meta': {'ordering': "('submit_date',)", 'object_name': 'Comment', 'db_table': "'django_comments'"}, + 'comment': ('django.db.models.fields.TextField', [], {'max_length': '3000'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'content_type_set_for_comment'", 'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip_address': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_removed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'object_pk': ('django.db.models.fields.TextField', [], {}), + 'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sites.Site']"}), + 'submit_date': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'comment_comments'", 'null': 'True', 'to': "orm['auth.User']"}), + 'user_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'user_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), + 'user_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + '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'}), + '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'}) + }, + 'documents.document': { + 'Meta': {'ordering': "['-date_added']", 'object_name': 'Document'}, + 'date_added': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'document_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentType']", 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'uuid': ('django.db.models.fields.CharField', [], {'default': "u'123068ef-26d2-45bb-8933-cb6818cd87e4'", 'max_length': '48', 'blank': 'True'}) + }, + 'documents.documentpage': { + 'Meta': {'ordering': "['page_number']", 'object_name': 'DocumentPage'}, + 'content': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'document_version': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentVersion']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'page_label': ('django.db.models.fields.CharField', [], {'max_length': '32', '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'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '32'}) + }, + 'documents.documenttypefilename': { + 'Meta': {'ordering': "['filename']", 'object_name': 'DocumentTypeFilename'}, + 'document_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentType']"}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + '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', [], {'to': "orm['documents.Document']"}), + 'encoding': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64'}), + 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), + 'filename': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'db_index': 'True'}), + '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', [], {'default': "''", 'max_length': '64'}), + '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', [], {}) + }, + 'documents.recentdocument': { + 'Meta': {'ordering': "('-datetime_accessed',)", 'object_name': 'RecentDocument'}, + 'datetime_accessed': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.Document']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'sites.site': { + 'Meta': {'ordering': "('domain',)", 'object_name': 'Site', 'db_table': "'django_site'"}, + 'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'taggit.tag': { + 'Meta': {'object_name': 'Tag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100', 'db_index': 'True'}) + }, + 'taggit.taggeditem': { + 'Meta': {'object_name': 'TaggedItem'}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_tagged_items'", 'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_items'", 'to': "orm['taggit.Tag']"}) + } + } + + complete_apps = ['documents'] diff --git a/apps/documents/migrations/0010_auto__chg_field_document_date_added.py b/apps/documents/migrations/0010_auto__chg_field_document_date_added.py new file mode 100644 index 0000000000..6220ef13ad --- /dev/null +++ b/apps/documents/migrations/0010_auto__chg_field_document_date_added.py @@ -0,0 +1,155 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Changing field 'Document.date_added' + db.alter_column('documents_document', 'date_added', self.gf('django.db.models.fields.DateTimeField')()) + + + def backwards(self, orm): + + # Changing field 'Document.date_added' + db.alter_column('documents_document', 'date_added', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True)) + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + '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': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + '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', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + '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', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'comments.comment': { + 'Meta': {'ordering': "('submit_date',)", 'object_name': 'Comment', 'db_table': "'django_comments'"}, + 'comment': ('django.db.models.fields.TextField', [], {'max_length': '3000'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'content_type_set_for_comment'", 'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip_address': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_removed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'object_pk': ('django.db.models.fields.TextField', [], {}), + 'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sites.Site']"}), + 'submit_date': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'comment_comments'", 'null': 'True', 'to': "orm['auth.User']"}), + 'user_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'user_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), + 'user_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + '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'}), + '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'}) + }, + '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', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'document_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentType']", 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '48', 'blank': 'True'}) + }, + 'documents.documentpage': { + 'Meta': {'ordering': "['page_number']", 'object_name': 'DocumentPage'}, + 'content': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'document_version': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentVersion']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'page_label': ('django.db.models.fields.CharField', [], {'max_length': '32', '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'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '32'}) + }, + 'documents.documenttypefilename': { + 'Meta': {'ordering': "['filename']", 'object_name': 'DocumentTypeFilename'}, + 'document_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentType']"}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + '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', [], {'to': "orm['documents.Document']"}), + 'encoding': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64'}), + 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), + 'filename': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'db_index': 'True'}), + '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', [], {'default': "''", 'max_length': '64'}), + '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', [], {}) + }, + 'documents.recentdocument': { + 'Meta': {'ordering': "('-datetime_accessed',)", 'object_name': 'RecentDocument'}, + 'datetime_accessed': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.Document']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'sites.site': { + 'Meta': {'ordering': "('domain',)", 'object_name': 'Site', 'db_table': "'django_site'"}, + 'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'taggit.tag': { + 'Meta': {'object_name': 'Tag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100', 'db_index': 'True'}) + }, + 'taggit.taggeditem': { + 'Meta': {'object_name': 'TaggedItem'}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_tagged_items'", 'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_items'", 'to': "orm['taggit.Tag']"}) + } + } + + complete_apps = ['documents'] diff --git a/apps/documents/migrations/0011_auto__add_field_documentversion_signature_state.py b/apps/documents/migrations/0011_auto__add_field_documentversion_signature_state.py new file mode 100644 index 0000000000..54a039b91f --- /dev/null +++ b/apps/documents/migrations/0011_auto__add_field_documentversion_signature_state.py @@ -0,0 +1,156 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Adding field 'DocumentVersion.signature_state' + db.add_column('documents_documentversion', 'signature_state', self.gf('django.db.models.fields.CharField')(max_length=16, null=True, blank=True), keep_default=False) + + + def backwards(self, orm): + + # Deleting field 'DocumentVersion.signature_state' + db.delete_column('documents_documentversion', 'signature_state') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + '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': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + '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', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + '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', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'comments.comment': { + 'Meta': {'ordering': "('submit_date',)", 'object_name': 'Comment', 'db_table': "'django_comments'"}, + 'comment': ('django.db.models.fields.TextField', [], {'max_length': '3000'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'content_type_set_for_comment'", 'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip_address': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_removed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'object_pk': ('django.db.models.fields.TextField', [], {}), + 'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sites.Site']"}), + 'submit_date': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'comment_comments'", 'null': 'True', 'to': "orm['auth.User']"}), + 'user_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'user_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), + 'user_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + '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'}), + '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'}) + }, + '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', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'document_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentType']", 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '48', 'blank': 'True'}) + }, + 'documents.documentpage': { + 'Meta': {'ordering': "['page_number']", 'object_name': 'DocumentPage'}, + 'content': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'document_version': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentVersion']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'page_label': ('django.db.models.fields.CharField', [], {'max_length': '32', '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'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '32'}) + }, + 'documents.documenttypefilename': { + 'Meta': {'ordering': "['filename']", 'object_name': 'DocumentTypeFilename'}, + 'document_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentType']"}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + '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', [], {'to': "orm['documents.Document']"}), + 'encoding': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64'}), + 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), + 'filename': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'db_index': 'True'}), + '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', [], {'default': "''", 'max_length': '64'}), + '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'}), + 'signature_state': ('django.db.models.fields.CharField', [], {'max_length': '16', 'null': 'True', 'blank': 'True'}), + 'timestamp': ('django.db.models.fields.DateTimeField', [], {}) + }, + 'documents.recentdocument': { + 'Meta': {'ordering': "('-datetime_accessed',)", 'object_name': 'RecentDocument'}, + 'datetime_accessed': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.Document']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'sites.site': { + 'Meta': {'ordering': "('domain',)", 'object_name': 'Site', 'db_table': "'django_site'"}, + 'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'taggit.tag': { + 'Meta': {'object_name': 'Tag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100', 'db_index': 'True'}) + }, + 'taggit.taggeditem': { + 'Meta': {'object_name': 'TaggedItem'}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_tagged_items'", 'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_items'", 'to': "orm['taggit.Tag']"}) + } + } + + complete_apps = ['documents'] diff --git a/apps/documents/migrations/0012_auto__add_field_documentversion_signature_file.py b/apps/documents/migrations/0012_auto__add_field_documentversion_signature_file.py new file mode 100644 index 0000000000..2938016090 --- /dev/null +++ b/apps/documents/migrations/0012_auto__add_field_documentversion_signature_file.py @@ -0,0 +1,157 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Adding field 'DocumentVersion.signature_file' + db.add_column('documents_documentversion', 'signature_file', self.gf('django.db.models.fields.files.FileField')(max_length=100, null=True, blank=True), keep_default=False) + + + def backwards(self, orm): + + # Deleting field 'DocumentVersion.signature_file' + db.delete_column('documents_documentversion', 'signature_file') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + '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': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + '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', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + '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', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'comments.comment': { + 'Meta': {'ordering': "('submit_date',)", 'object_name': 'Comment', 'db_table': "'django_comments'"}, + 'comment': ('django.db.models.fields.TextField', [], {'max_length': '3000'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'content_type_set_for_comment'", 'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip_address': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_removed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'object_pk': ('django.db.models.fields.TextField', [], {}), + 'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sites.Site']"}), + 'submit_date': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'comment_comments'", 'null': 'True', 'to': "orm['auth.User']"}), + 'user_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'user_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), + 'user_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + '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'}), + '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'}) + }, + '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', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'document_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentType']", 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '48', 'blank': 'True'}) + }, + 'documents.documentpage': { + 'Meta': {'ordering': "['page_number']", 'object_name': 'DocumentPage'}, + 'content': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'document_version': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentVersion']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'page_label': ('django.db.models.fields.CharField', [], {'max_length': '32', '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'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '32'}) + }, + 'documents.documenttypefilename': { + 'Meta': {'ordering': "['filename']", 'object_name': 'DocumentTypeFilename'}, + 'document_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentType']"}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + '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', [], {'to': "orm['documents.Document']"}), + 'encoding': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64'}), + 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), + 'filename': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'db_index': 'True'}), + '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', [], {'default': "''", 'max_length': '64'}), + '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'}), + 'signature_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'signature_state': ('django.db.models.fields.CharField', [], {'max_length': '16', 'null': 'True', 'blank': 'True'}), + 'timestamp': ('django.db.models.fields.DateTimeField', [], {}) + }, + 'documents.recentdocument': { + 'Meta': {'ordering': "('-datetime_accessed',)", 'object_name': 'RecentDocument'}, + 'datetime_accessed': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.Document']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'sites.site': { + 'Meta': {'ordering': "('domain',)", 'object_name': 'Site', 'db_table': "'django_site'"}, + 'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'taggit.tag': { + 'Meta': {'object_name': 'Tag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100', 'db_index': 'True'}) + }, + 'taggit.taggeditem': { + 'Meta': {'object_name': 'TaggedItem'}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_tagged_items'", 'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_items'", 'to': "orm['taggit.Tag']"}) + } + } + + complete_apps = ['documents'] diff --git a/apps/documents/migrations/__init__.py b/apps/documents/migrations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/documents/models.py b/apps/documents/models.py index 914426a568..b691cdc36d 100644 --- a/apps/documents/models.py +++ b/apps/documents/models.py @@ -3,7 +3,13 @@ import tempfile import hashlib from ast import literal_eval import base64 -from StringIO import StringIO +import datetime +import logging + +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO from django.db import models from django.utils.translation import ugettext_lazy as _ @@ -19,8 +25,12 @@ from converter.api import get_page_count from converter.api import get_available_transformations_choices 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 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 django_gpg.runtime import gpg +from django_gpg.exceptions import GPGVerificationError, GPGDecryptionError from documents.conf.settings import CHECKSUM_FUNCTION from documents.conf.settings import UUID_FUNCTION @@ -30,29 +40,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 """ - filename, extension = os.path.splitext(filename) - instance.file_filename = filename - #remove prefix '.' - instance.file_extension = extension[1:] - uuid = UUID_FUNCTION() - instance.uuid = uuid - return uuid + instance.filename = filename + return UUID_FUNCTION() class DocumentType(models.Model): @@ -72,21 +78,13 @@ class DocumentType(models.Model): class Document(models.Model): - """ + ''' Defines a single document with it's fields and properties - """ + ''' + uuid = models.CharField(max_length=48, blank=True, editable=False) document_type = models.ForeignKey(DocumentType, verbose_name=_(u'document type'), null=True, blank=True) - file = models.FileField(upload_to=get_filename_from_uuid, storage=STORAGE_BACKEND(), verbose_name=_(u'file')) - uuid = models.CharField(max_length=48, default=UUID_FUNCTION(), blank=True, editable=False) - 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) - file_extension = models.CharField(max_length=16, default=u'', editable=False, db_index=True) - date_added = models.DateTimeField(verbose_name=_(u'added'), auto_now_add=True, 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) description = models.TextField(blank=True, null=True, verbose_name=_(u'description'), db_index=True) + date_added = models.DateTimeField(verbose_name=_(u'added'), db_index=True, editable=False) tags = TaggableManager() @@ -96,25 +94,308 @@ class Document(models.Model): object_id_field='object_pk' ) + @staticmethod + def clear_image_cache(): + for the_file in os.listdir(CACHE_PATH): + file_path = os.path.join(CACHE_PATH, the_file) + if os.path.isfile(file_path): + os.unlink(file_path) + class Meta: verbose_name = _(u'document') verbose_name_plural = _(u'documents') ordering = ['-date_added'] def __unicode__(self): - return os.extsep.join([self.file_filename, self.file_extension]) + return self.latest_version.filename + + @models.permalink + def get_absolute_url(self): + return ('document_view_simple', [self.pk]) 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) + if not self.pk: + self.uuid = UUID_FUNCTION() + self.date_added = datetime.datetime.now() super(Document, self).save(*args, **kwargs) + def get_cached_image_name(self, page, version): + document_version = DocumentVersion.objects.get(pk=version) + document_page = document_version.documentpage_set.get(page_number=page) + transformations, warnings = document_page.get_transformation_list() + hash_value = HASH_FUNCTION(u''.join([document_version.checksum, unicode(page), unicode(transformations)])) + return os.path.join(CACHE_PATH, hash_value), transformations + + def get_image_cache_name(self, page, version): + cache_file_path, transformations = self.get_cached_image_name(page, version) + if os.path.exists(cache_file_path): + return cache_file_path + else: + document_version = DocumentVersion.objects.get(pk=version) + document_file = document_save_to_temp_dir(document_version, document_version.checksum) + return convert(document_file, output_filepath=cache_file_path, page=page, transformations=transformations, mimetype=self.file_mimetype) + + def get_valid_image(self, size=DISPLAY_SIZE, page=DEFAULT_PAGE_NUMBER, zoom=DEFAULT_ZOOM_LEVEL, rotation=DEFAULT_ROTATION, version=None): + if not version: + version = self.latest_version.pk + image_cache_name = self.get_image_cache_name(page=page, version=version) + return convert(image_cache_name, cleanup_files=False, size=size, zoom=zoom, rotation=rotation) + + def get_image(self, size=DISPLAY_SIZE, page=DEFAULT_PAGE_NUMBER, zoom=DEFAULT_ZOOM_LEVEL, rotation=DEFAULT_ROTATION, as_base64=False, version=None): + if zoom < ZOOM_MIN_LEVEL: + zoom = ZOOM_MIN_LEVEL + + if zoom > ZOOM_MAX_LEVEL: + zoom = ZOOM_MAX_LEVEL + + rotation = rotation % 360 + + try: + file_path = self.get_valid_image(size=size, page=page, zoom=zoom, rotation=rotation, version=version) + except UnknownFileFormat: + file_path = get_icon_file_path(self.file_mimetype) + except UnkownConvertError: + file_path = get_error_icon_file_path() + except: + file_path = get_error_icon_file_path() + + if as_base64: + image = open(file_path, 'r') + out = StringIO() + base64.encode(image, out) + return u'data:%s;base64,%s' % (get_mimetype(open(file_path, 'r'), file_path, mimetype_only=True)[0], out.getvalue().replace('\n', '')) + else: + return file_path + + def invalidate_cached_image(self, page): + try: + os.unlink(self.get_cached_image_name(page, self.latest_version.pk)[0]) + except OSError: + pass + + def add_as_recent_document_for_user(self, user): + RecentDocument.objects.add_document_for_user(user, self) + + # 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): + return self.latest_version.size + + 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) + logger.debug('new_version_dict: %s' % 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() + else: + new_version_dict = {} + new_version = DocumentVersion( + document=self, + file=file, + ) + new_version.save() + + logger.debug('new_version saved') + return new_version + + # Proxy methods + def open(self, *args, **kwargs): + ''' + Return a file descriptor to a document's file irrespective of + the storage backend + ''' + return self.latest_version.open(*args, **kwargs) + + 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.latest_version.file + + @property + def file_mimetype(self): + return self.latest_version.mimetype + + @property + def file_mime_encoding(self): + return self.latest_version.encoding + + @property + def file_filename(self): + return self.latest_version.filename + + @property + def date_updated(self): + return self.latest_version.timestamp + + @property + def checksum(self): + return self.latest_version.checksum + + @property + def signature_state(self): + return self.latest_version.signature_state + + @property + def pages(self): + return self.latest_version.pages + + @property + def page_count(self): + return self.pages.count() + + @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 + + def _set_filename(self, value): + version = self.latest_version + version.filename = value + return version.save() + + filename = property(_get_filename, _set_filename) + + def add_detached_signature(self, *args, **kwargs): + return self.latest_version.add_detached_signature(*args, **kwargs) + + def has_detached_signature(self): + return self.latest_version.has_detached_signature() + + def detached_signature(self): + return self.latest_version.detached_signature() + + def verify_signature(self): + return self.latest_version.verify_signature() + + +class DocumentVersion(models.Model): + ''' + Model that describes a document version and its properties + ''' + @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 + file = models.FileField(upload_to=get_filename_from_uuid, storage=STORAGE_BACKEND(), verbose_name=_(u'file')) + mimetype = models.CharField(max_length=64, default='', editable=False) + encoding = models.CharField(max_length=64, default='', editable=False) + filename = models.CharField(max_length=255, default=u'', editable=False, db_index=True) + checksum = models.TextField(blank=True, null=True, verbose_name=_(u'checksum'), editable=False) + signature_state = models.CharField(blank=True, null=True, max_length=16, verbose_name=_(u'signature state'), editable=False) + signature_file = models.FileField(blank=True, null=True, upload_to=get_filename_from_uuid, storage=STORAGE_BACKEND(), verbose_name=_(u'signature file'), editable=False) + + class Meta: + unique_together = ('document', 'major', 'minor', 'micro', 'release_level', 'serial') + verbose_name = _(u'document version') + verbose_name_plural = _(u'document version') + + def __unicode__(self): + return self.get_formated_version() + + def get_new_version_dict(self, version_update_type): + logger.debug('version_update_type: %s' % version_update_type) + + 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 + ''' + vers = [u'%i.%i' % (self.major, self.minor), ] + + if self.micro: + vers.append(u'.%i' % self.micro) + if self.release_level != RELEASE_LEVEL_FINAL: + 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_signed_state(save=False) self.update_checksum(save=False) self.update_mimetype(save=False) self.save() @@ -122,43 +403,11 @@ class Document(models.Model): if transformations: self.apply_default_transformations(transformations) - @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 os.extsep.join([self.file_filename, self.file_extension]) - - 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.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) - def update_checksum(self, save=True): - """ - Open a document's file and update the checksum field using the + ''' + 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())) @@ -185,29 +434,100 @@ class Document(models.Model): except OSError: pass - current_pages = DocumentPage.objects.filter(document=self).order_by('page_number',) + 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=self, page_number=page_number + 1) + document_version=self, page_number=page_number + 1) if save: self.save() return detected_pages - - @property - def page_count(self): - return self.documentpage_set.count() + + 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 revert(self): + ''' + Delete the subsequent versions after this one + ''' + for version in self.document.versions.filter(timestamp__gt=self.timestamp): + version.delete() + + def update_signed_state(self, save=True): + if self.exists(): + try: + self.signature_state = gpg.verify(self.open()).status + # TODO: give use choice for auto public key fetch? + # OR maybe new config option + except GPGVerificationError: + self.signature_state = None + + if save: + self.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(DocumentVersion, 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, raw=False): + ''' + Return a file descriptor to a document version's file irrespective of + the storage backend + ''' + if self.signature_state and not raw: + try: + result = gpg.decrypt_file(self.file.storage.open(self.file.path)) + # gpg return a string, turn it into a file like object + return StringIO(result.data) + except GPGDecryptionError: + # At least return the original raw content + return self.file.storage.open(self.file.path) + else: + 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: @@ -220,84 +540,6 @@ class Document(models.Model): 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.file.storage.exists(self.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.documentpage_set.all()]) == 0: - for transformation in transformations: - for document_page in self.documentpage_set.all(): - page_transformation = DocumentPageTransformation( - document_page=document_page, - order=0, - transformation=transformation.get('transformation'), - arguments=transformation.get('arguments') - ) - - page_transformation.save() - - def get_cached_image_name(self, page): - document_page = self.documentpage_set.get(page_number=page) - transformations, warnings = document_page.get_transformation_list() - hash_value = HASH_FUNCTION(u''.join([self.checksum, unicode(page), unicode(transformations)])) - return os.path.join(CACHE_PATH, hash_value), transformations - - def get_image_cache_name(self, page): - cache_file_path, transformations = self.get_cached_image_name(page) - if os.path.exists(cache_file_path): - return cache_file_path - else: - document_file = document_save_to_temp_dir(self, self.checksum) - return convert(document_file, output_filepath=cache_file_path, page=page, transformations=transformations, mimetype=self.file_mimetype) - - def get_valid_image(self, size=DISPLAY_SIZE, page=DEFAULT_PAGE_NUMBER, zoom=DEFAULT_ZOOM_LEVEL, rotation=DEFAULT_ROTATION): - image_cache_name = self.get_image_cache_name(page=page) - return convert(image_cache_name, cleanup_files=False, size=size, zoom=zoom, rotation=rotation) - - def get_image(self, size=DISPLAY_SIZE, page=DEFAULT_PAGE_NUMBER, zoom=DEFAULT_ZOOM_LEVEL, rotation=DEFAULT_ROTATION, as_base64=False): - if zoom < ZOOM_MIN_LEVEL: - zoom = ZOOM_MIN_LEVEL - - if zoom > ZOOM_MAX_LEVEL: - zoom = ZOOM_MAX_LEVEL - - rotation = rotation % 360 - - try: - file_path = self.get_valid_image(size=size, page=page, zoom=zoom, rotation=rotation) - except UnknownFileFormat: - file_path = get_icon_file_path(self.file_mimetype) - except UnkownConvertError: - file_path = get_error_icon_file_path() - except: - file_path = get_error_icon_file_path() - - if as_base64: - image = open(file_path, 'r') - out = StringIO() - base64.encode(image, out) - return u'data:%s;base64,%s' % (get_mimetype(open(file_path, 'r'), file_path, mimetype_only=True)[0], out.getvalue().replace('\n', '')) - else: - return file_path - - def invalidate_cached_image(self, page): - try: - os.unlink(self.get_cached_image_name(page)[0]) - except OSError: - pass - - 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) - return self.file.storage.delete(self.file.path) @property def size(self): @@ -305,13 +547,41 @@ class Document(models.Model): return self.file.storage.size(self.file.path) else: return None + + def add_detached_signature(self, detached_signature): + if not self.signature_state: + self.signature_file = detached_signature + self.save() + else: + raise Exception('document already has an embedded signature') + + def has_detached_signature(self): + if self.signature_file: + return self.signature_file.storage.exists(self.signature_file.path) + else: + return False + + def detached_signature(self): + return self.signature_file.storage.open(self.signature_file.path) + + def verify_signature(self): + try: + if self.has_detached_signature(): + logger.debug('has detached signature') + signature = gpg.verify_w_retry(self.open(), self.detached_signature()) + else: + signature = gpg.verify_w_retry(self.open(raw=True)) + except GPGVerificationError: + signature = None + return signature + 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')) @@ -326,10 +596,13 @@ class DocumentTypeFilename(models.Model): class DocumentPage(models.Model): - """ - Model that describes a document page including it's content - """ - 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 + + # Unchanged fields content = models.TextField(blank=True, null=True, verbose_name=_(u'content'), db_index=True) page_label = models.CharField(max_length=32, blank=True, null=True, verbose_name=_(u'page label')) page_number = models.PositiveIntegerField(default=1, editable=False, verbose_name=_(u'page number'), db_index=True) @@ -338,7 +611,7 @@ class DocumentPage(models.Model): return _(u'Page %(page_num)d out of %(total_pages)d of %(document)s') % { 'document': unicode(self.document), 'page_num': self.page_number, - 'total_pages': self.document.documentpage_set.count() + 'total_pages': self.document_version.documentpage_set.count() } class Meta: @@ -353,6 +626,15 @@ class DocumentPage(models.Model): def get_absolute_url(self): return ('document_page_view', [self.pk]) + @property + def siblings(self): + return DocumentPage.objects.filter(document_version=self.document_version) + + # Compatibility methods + @property + def document(self): + return self.document_version.document + class ArgumentsValidator(object): message = _(u'Enter a valid value.') @@ -418,14 +700,13 @@ class RecentDocument(models.Model): # Register the fields that will be searchable register('document', Document, _(u'document'), [ {'name': u'document_type__name', 'title': _(u'Document type')}, - {'name': u'file_mimetype', 'title': _(u'MIME type')}, - {'name': u'file_filename', 'title': _(u'Filename')}, - {'name': u'file_extension', 'title': _(u'Filename extension')}, + {'name': u'documentversion__mimetype', 'title': _(u'MIME type')}, + {'name': u'documentversion__filename', 'title': _(u'Filename')}, {'name': u'documentmetadata__value', 'title': _(u'Metadata value')}, - {'name': u'documentpage__content', 'title': _(u'Content')}, + {'name': u'documentversion__documentpage__content', 'title': _(u'Content')}, {'name': u'description', 'title': _(u'Description')}, {'name': u'tags__name', 'title': _(u'Tags')}, {'name': u'comments__comment', 'title': _(u'Comments')}, ] ) -#register(Document, _(u'document'), ['document_type__name', 'file_mimetype', 'file_extension', 'documentmetadata__value', 'documentpage__content', 'description', {'field_name':'file_filename', 'comparison':'iexact'}]) +#register(Document, _(u'document'), ['document_type__name', 'file_mimetype', 'documentmetadata__value', 'documentpage__content', 'description', {'field_name':'file_filename', 'comparison':'iexact'}]) diff --git a/apps/documents/static/images/icons/camera_delete.png b/apps/documents/static/images/icons/camera_delete.png new file mode 100644 index 0000000000..05616882a7 Binary files /dev/null and b/apps/documents/static/images/icons/camera_delete.png differ diff --git a/apps/documents/static/images/icons/page_white_copy.png b/apps/documents/static/images/icons/page_white_copy.png new file mode 100644 index 0000000000..4c21085e2b Binary files /dev/null and b/apps/documents/static/images/icons/page_white_copy.png differ diff --git a/apps/documents/templates/document_print.html b/apps/documents/templates/document_print.html index 06068fc31b..35f08abaf8 100644 --- a/apps/documents/templates/document_print.html +++ b/apps/documents/templates/document_print.html @@ -1,5 +1,5 @@ {% load project_tags %} -{% load printing_tags %} +{#{% load printing_tags %}#} @@ -48,9 +48,10 @@ {% for page in pages %} - {% get_document_size object %} + {#{% get_document_size object %}#}
- page_aspect %}width="97%"{% else %}height="97%"{% endif %} /> + {# page_aspect %}width="97%"{% else %}height="97%"{% endif %} />#} +
{% endfor %} diff --git a/apps/documents/templatetags/printing_tags.py b/apps/documents/templatetags/printing_tags.py index c2fc6b4e29..7dccae8085 100644 --- a/apps/documents/templatetags/printing_tags.py +++ b/apps/documents/templatetags/printing_tags.py @@ -1,6 +1,6 @@ from django.template import Library, Node, Variable -from converter.api import get_document_dimensions +from converter.api import get_dimensions from documents.conf.settings import PRINT_SIZE @@ -13,7 +13,7 @@ class GetImageSizeNode(Node): def render(self, context): document = Variable(self.document).resolve(context) - width, height = get_document_dimensions(document) + width, height = get_dimensions(document) context[u'document_width'], context['document_height'] = width, height context[u'document_aspect'] = float(width) / float(height) return u'' diff --git a/apps/documents/urls.py b/apps/documents/urls.py index 0d16fb3880..14193c1d8a 100644 --- a/apps/documents/urls.py +++ b/apps/documents/urls.py @@ -33,10 +33,15 @@ urlpatterns = patterns('documents.views', url(r'^(?P\d+)/create/siblings/$', 'document_create_siblings', (), 'document_create_siblings'), url(r'^(?P\d+)/find_duplicates/$', 'document_find_duplicates', (), 'document_find_duplicates'), 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_download', (), 'document_version_download'), + url(r'^document/version/(?P\d+)/revert/$', 'document_version_revert', (), 'document_version_revert'), 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'), url(r'^maintenance/update_page_count/$', 'document_update_page_count', (), 'document_update_page_count'), + url(r'^maintenance/clear_image_cache/$', 'document_clear_image_cache', (), 'document_clear_image_cache'), url(r'^page/(?P\d+)/$', 'document_page_view', (), 'document_page_view'), url(r'^page/(?P\d+)/text/$', 'document_page_text', (), 'document_page_text'), diff --git a/apps/documents/views.py b/apps/documents/views.py index 60a03ee74c..3234a2014e 100644 --- a/apps/documents/views.py +++ b/apps/documents/views.py @@ -36,25 +36,27 @@ from documents.conf.settings import ROTATION_STEP from documents.conf.settings import PRINT_SIZE from documents.conf.settings import RECENT_COUNT -from documents.literals import PERMISSION_DOCUMENT_CREATE, \ - PERMISSION_DOCUMENT_PROPERTIES_EDIT, \ - PERMISSION_DOCUMENT_VIEW, \ - PERMISSION_DOCUMENT_DELETE, PERMISSION_DOCUMENT_DOWNLOAD, \ - PERMISSION_DOCUMENT_TRANSFORM, \ - PERMISSION_DOCUMENT_EDIT, PERMISSION_DOCUMENT_TOOLS -from documents.literals import HISTORY_DOCUMENT_CREATED, \ - HISTORY_DOCUMENT_EDITED, HISTORY_DOCUMENT_DELETED +from documents.literals import (PERMISSION_DOCUMENT_CREATE, + PERMISSION_DOCUMENT_PROPERTIES_EDIT, + PERMISSION_DOCUMENT_VIEW, + PERMISSION_DOCUMENT_DELETE, PERMISSION_DOCUMENT_DOWNLOAD, + PERMISSION_DOCUMENT_TRANSFORM, + PERMISSION_DOCUMENT_EDIT, PERMISSION_DOCUMENT_TOOLS, + PERMISSION_DOCUMENT_VERSION_REVERT) +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 +from documents.models import (Document, DocumentType, DocumentPage, + DocumentPageTransformation, RecentDocument, DocumentTypeFilename, + DocumentVersion) # Document type permissions from documents.literals import PERMISSION_DOCUMENT_TYPE_EDIT, \ @@ -65,7 +67,7 @@ def document_list(request, object_list=None, title=None, extra_context=None): check_permissions(request.user, [PERMISSION_DOCUMENT_VIEW]) context = { - 'object_list': object_list if not (object_list is None) else Document.objects.only('file_filename', 'file_extension').all(), + 'object_list': object_list if not (object_list is None) else Document.objects.all(), 'title': title if title else _(u'documents'), 'multi_select_as_buttons': True, 'hide_links': True, @@ -114,8 +116,7 @@ def document_view(request, document_id, advanced=False): if advanced: document_properties_form = DocumentPropertiesForm(instance=document, extra_fields=[ - {'label': _(u'Filename'), 'field': 'file_filename'}, - {'label': _(u'File extension'), 'field': 'file_extension'}, + {'label': _(u'Filename'), 'field': 'filename'}, {'label': _(u'File mimetype'), 'field': 'file_mimetype'}, {'label': _(u'File mime encoding'), 'field': 'file_mime_encoding'}, {'label': _(u'File size'), 'field':lambda x: pretty_size(x.size) if x.size else '-'}, @@ -242,15 +243,15 @@ 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'])}) + create_history(HISTORY_DOCUMENT_EDITED, document, {'user': request.user, 'diff': return_diff(old_document, document, ['filename', 'description'])}) RecentDocument.objects.add_document_for_user(request.user, document) messages.success(request, _(u'Document "%s" edited successfully.') % document) @@ -263,7 +264,7 @@ def document_edit(request, document_id): return HttpResponseRedirect(document.get_absolute_url()) else: form = DocumentForm_edit(instance=document, initial={ - 'new_filename': document.file_filename}) + 'new_filename': document.filename}) return render_to_response('generic_form.html', { 'form': form, @@ -279,6 +280,8 @@ def get_document_image(request, document_id, size=PREVIEW_SIZE, base64_version=F page = int(request.GET.get('page', DEFAULT_PAGE_NUMBER)) zoom = int(request.GET.get('zoom', DEFAULT_ZOOM_LEVEL)) + + version = int(request.GET.get('version', document.latest_version.pk)) if zoom < ZOOM_MIN_LEVEL: zoom = ZOOM_MIN_LEVEL @@ -289,25 +292,29 @@ def get_document_image(request, document_id, size=PREVIEW_SIZE, base64_version=F rotation = int(request.GET.get('rotation', DEFAULT_ROTATION)) % 360 if base64_version: - return HttpResponse(u'' % document.get_image(size=size, page=page, zoom=zoom, rotation=rotation, as_base64=True)) + return HttpResponse(u'' % document.get_image(size=size, page=page, zoom=zoom, rotation=rotation, as_base64=True, version=version)) else: - # TODO: hardcoded MIMETYPE - return sendfile.sendfile(request, document.get_image(size=size, page=page, zoom=zoom, rotation=rotation), mimetype=DEFAULT_FILE_FORMAT_MIMETYPE) + # TODO: fix hardcoded MIMETYPE + return sendfile.sendfile(request, document.get_image(size=size, page=page, zoom=zoom, rotation=rotation, version=version), mimetype=DEFAULT_FILE_FORMAT_MIMETYPE) - -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) @@ -451,12 +458,11 @@ def _find_duplicate_list(request, source_document_list=Document.objects.all(), i duplicated = [] for document in source_document_list: if document.pk not in duplicated: - results = Document.objects.filter(checksum=document.checksum).exclude(id__in=duplicated).exclude(pk=document.pk).values_list('pk', flat=True) + results = DocumentVersion.objects.filter(checksum=document.latest_version.checksum).exclude(id__in=duplicated).exclude(pk=document.pk).values_list('document__pk', flat=True) duplicated.extend(results) if include_source and results: duplicated.append(document.pk) - context = { 'object_list': Document.objects.filter(pk__in=duplicated), 'title': _(u'duplicated documents'), @@ -482,16 +488,16 @@ def document_update_page_count(request): previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', '/'))) office_converter = OfficeConverter() - qs = Document.objects.exclude(file_extension__iendswith='dxf').filter(file_mimetype__in=office_converter.mimetypes()) + qs = DocumentVersion.objects.exclude(filename__iendswith='dxf').filter(mimetype__in=office_converter.mimetypes()) if request.method == 'POST': updated = 0 processed = 0 - for document in qs: - old_page_count = document.page_count - document.update_page_count() + for document_version in qs: + old_page_count = document_version.pages.count() + document_version.update_page_count() processed += 1 - if old_page_count != document.page_count: + if old_page_count != document_version.pages.count(): updated += 1 messages.success(request, _(u'Page count update complete. Documents processed: %(total)d, documents with changed page count: %(change)d') % { @@ -527,7 +533,7 @@ def document_clear_transformations(request, document_id=None, document_id_list=N if request.method == 'POST': for document in documents: try: - for document_page in document.documentpage_set.all(): + for document_page in document.pages.all(): document_page.document.invalidate_cached_image(document_page.page_number) for transformation in document_page.documentpagetransformation_set.all(): transformation.delete() @@ -663,11 +669,11 @@ def document_page_navigation_next(request, document_page_id): view = resolve_to_name(urlparse.urlparse(request.META.get('HTTP_REFERER', u'/')).path) document_page = get_object_or_404(DocumentPage, pk=document_page_id) - if document_page.page_number >= document_page.document.documentpage_set.count(): + if document_page.page_number >= document_page.siblings.count(): messages.warning(request, _(u'There are no more pages in this document')) return HttpResponseRedirect(request.META.get('HTTP_REFERER', u'/')) else: - document_page = get_object_or_404(DocumentPage, document=document_page.document, page_number=document_page.page_number + 1) + document_page = get_object_or_404(document_page.siblings, page_number=document_page.page_number + 1) return HttpResponseRedirect(reverse(view, args=[document_page.pk])) @@ -680,7 +686,7 @@ def document_page_navigation_previous(request, document_page_id): messages.warning(request, _(u'You are already at the first page of this document')) return HttpResponseRedirect(request.META.get('HTTP_REFERER', u'/')) else: - document_page = get_object_or_404(DocumentPage, document=document_page.document, page_number=document_page.page_number - 1) + document_page = get_object_or_404(document_page.siblings, page_number=document_page.page_number - 1) return HttpResponseRedirect(reverse(view, args=[document_page.pk])) @@ -689,7 +695,7 @@ def document_page_navigation_first(request, document_page_id): view = resolve_to_name(urlparse.urlparse(request.META.get('HTTP_REFERER', u'/')).path) document_page = get_object_or_404(DocumentPage, pk=document_page_id) - document_page = get_object_or_404(DocumentPage, document=document_page.document, page_number=1) + document_page = get_object_or_404(document_page.siblings, page_number=1) return HttpResponseRedirect(reverse(view, args=[document_page.pk])) @@ -698,7 +704,7 @@ def document_page_navigation_last(request, document_page_id): view = resolve_to_name(urlparse.urlparse(request.META.get('HTTP_REFERER', u'/')).path) document_page = get_object_or_404(DocumentPage, pk=document_page_id) - document_page = get_object_or_404(DocumentPage, document=document_page.document, page_number=document_page.document.documentpage_set.count()) + document_page = get_object_or_404(document_page.siblings, page_number=document_page.siblings.count()) return HttpResponseRedirect(reverse(view, args=[document_page.pk])) @@ -793,18 +799,18 @@ def document_print(request, document_id): hard_copy_arguments['page_range'] = form.cleaned_data['page_range'] # Compute page width and height - if form.cleaned_data['custom_page_width'] and form.cleaned_data['custom_page_height']: - page_width = form.cleaned_data['custom_page_width'] - page_height = form.cleaned_data['custom_page_height'] - elif form.cleaned_data['page_size']: - page_width, page_height = dict(PAGE_SIZE_DIMENSIONS)[form.cleaned_data['page_size']] + #if form.cleaned_data['custom_page_width'] and form.cleaned_data['custom_page_height']: + # page_width = form.cleaned_data['custom_page_width'] + # page_height = form.cleaned_data['custom_page_height'] + #elif form.cleaned_data['page_size']: + # page_width, page_height = dict(PAGE_SIZE_DIMENSIONS)[form.cleaned_data['page_size']] # Page orientation - if form.cleaned_data['page_orientation'] == PAGE_ORIENTATION_LANDSCAPE: - page_width, page_height = page_height, page_width + #if form.cleaned_data['page_orientation'] == PAGE_ORIENTATION_LANDSCAPE: + # page_width, page_height = page_height, page_width - hard_copy_arguments['page_width'] = page_width - hard_copy_arguments['page_height'] = page_height + #hard_copy_arguments['page_width'] = page_width + #hard_copy_arguments['page_height'] = page_height new_url = [reverse('document_hard_copy', args=[document_id])] if hard_copy_arguments: @@ -852,9 +858,9 @@ def document_hard_copy(request, document_id): if page_range: page_range = parse_range(page_range) - pages = document.documentpage_set.filter(page_number__in=page_range) + pages = document.pages.filter(page_number__in=page_range) else: - pages = document.documentpage_set.all() + pages = document.pages.all() return render_to_response('document_print.html', { 'object': document, @@ -1122,3 +1128,91 @@ def document_type_filename_create(request, document_type_id): 'document_type': document_type, }, context_instance=RequestContext(request)) + + +def document_clear_image_cache(request): + check_permissions(request.user, [PERMISSION_DOCUMENT_TOOLS]) + + previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', '/'))) + + if request.method == 'POST': + try: + Document.clear_image_cache() + messages.success(request, _(u'Document image cache cleared successfully')) + except Exception, msg: + messages.error(request, _(u'Error clearing document image cache; %s') % msg) + + return HttpResponseRedirect(previous) + + return render_to_response('generic_confirm.html', { + 'previous': previous, + 'title': _(u'Are you sure you wish to clear the document image cache?'), + 'form_icon': u'camera_delete.png', + }, context_instance=RequestContext(request)) + + +def document_version_list(request, document_pk): + check_permissions(request.user, [PERMISSION_DOCUMENT_VIEW]) + document = get_object_or_404(Document, pk=document_pk) + + RecentDocument.objects.add_document_for_user(request.user, document) + + context = { + '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_formated_version', + }, + { + 'name': _(u'time and date'), + 'attribute': 'timestamp', + }, + { + 'name': _(u'mimetype'), + 'attribute': 'mimetype', + }, + { + 'name': _(u'encoding'), + 'attribute': 'encoding', + }, + { + 'name': _(u'filename'), + 'attribute': 'filename', + }, + { + 'name': _(u'comment'), + 'attribute': 'comment', + }, + ] + } + + return render_to_response('generic_list.html', context, + context_instance=RequestContext(request)) + + +def document_version_revert(request, document_version_pk): + check_permissions(request.user, [PERMISSION_DOCUMENT_VERSION_REVERT]) + + previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', '/'))) + document_version = get_object_or_404(DocumentVersion, pk=document_version_pk) + + if request.method == 'POST': + try: + document_version.revert() + messages.success(request, _(u'Document version reverted successfully')) + except Exception, msg: + messages.error(request, _(u'Error reverting document version; %s') % msg) + + return HttpResponseRedirect(previous) + + return render_to_response('generic_confirm.html', { + 'previous': previous, + 'object': document_version.document, + 'title': _(u'Are you sure you wish to revert to this version?'), + 'message': _(u'All later version after this one will be deleted too.'), + 'form_icon': u'page_refresh.png', + }, context_instance=RequestContext(request)) diff --git a/apps/documents/widgets.py b/apps/documents/widgets.py index e449ffc335..031461047f 100644 --- a/apps/documents/widgets.py +++ b/apps/documents/widgets.py @@ -20,14 +20,19 @@ def document_link(document): return mark_safe(u'%s' % (reverse('document_view_simple', args=[document.pk]), document)) -def document_html_widget(document, view='document_thumbnail', click_view=None, page=DEFAULT_PAGE_NUMBER, zoom=DEFAULT_ZOOM_LEVEL, rotation=DEFAULT_ROTATION, gallery_name=None, fancybox_class='fancybox'): +def document_html_widget(document, view='document_thumbnail', click_view=None, page=DEFAULT_PAGE_NUMBER, zoom=DEFAULT_ZOOM_LEVEL, rotation=DEFAULT_ROTATION, gallery_name=None, fancybox_class='fancybox', version=None): result = [] alt_text = _(u'document page image') + + if not version: + version = document.latest_version.pk + query_dict = { 'page': page, 'zoom': zoom, 'rotation': rotation, + 'version': version, } if gallery_name: @@ -67,7 +72,7 @@ def document_html_widget(document, view='document_thumbnail', click_view=None, p }); ''' % { - 'url': reverse('documents-expensive-is_zoomable', args=[document.pk, page]), + 'url': reverse('documents-expensive-is_zoomable', args=[document.pk, version, page]), 'pk': document.pk, 'page': page if page else 1, 'plain_template': mark_safe(u''.join(plain_template)), diff --git a/apps/dynamic_search/locale/es/LC_MESSAGES/django.mo b/apps/dynamic_search/locale/es/LC_MESSAGES/django.mo index 9b4fdbe230..7512e7945d 100644 Binary files a/apps/dynamic_search/locale/es/LC_MESSAGES/django.mo and b/apps/dynamic_search/locale/es/LC_MESSAGES/django.mo differ diff --git a/apps/dynamic_search/locale/pt/LC_MESSAGES/django.mo b/apps/dynamic_search/locale/pt/LC_MESSAGES/django.mo index 31bf49eb59..85a80a99c7 100644 Binary files a/apps/dynamic_search/locale/pt/LC_MESSAGES/django.mo and b/apps/dynamic_search/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/dynamic_search/locale/ru/LC_MESSAGES/django.mo b/apps/dynamic_search/locale/ru/LC_MESSAGES/django.mo index 0591c4f58f..00f56a26ed 100644 Binary files a/apps/dynamic_search/locale/ru/LC_MESSAGES/django.mo and b/apps/dynamic_search/locale/ru/LC_MESSAGES/django.mo differ diff --git a/apps/folders/locale/es/LC_MESSAGES/django.mo b/apps/folders/locale/es/LC_MESSAGES/django.mo index 782395ee7a..8d12b0b0e1 100644 Binary files a/apps/folders/locale/es/LC_MESSAGES/django.mo and b/apps/folders/locale/es/LC_MESSAGES/django.mo differ diff --git a/apps/folders/locale/pt/LC_MESSAGES/django.mo b/apps/folders/locale/pt/LC_MESSAGES/django.mo index 539af2adda..d91efb95dd 100644 Binary files a/apps/folders/locale/pt/LC_MESSAGES/django.mo and b/apps/folders/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/folders/locale/ru/LC_MESSAGES/django.mo b/apps/folders/locale/ru/LC_MESSAGES/django.mo index 6bdb087a35..17af131f95 100644 Binary files a/apps/folders/locale/ru/LC_MESSAGES/django.mo and b/apps/folders/locale/ru/LC_MESSAGES/django.mo differ diff --git a/apps/history/locale/es/LC_MESSAGES/django.mo b/apps/history/locale/es/LC_MESSAGES/django.mo index b7409ec321..f5c1c8a299 100644 Binary files a/apps/history/locale/es/LC_MESSAGES/django.mo and b/apps/history/locale/es/LC_MESSAGES/django.mo differ diff --git a/apps/history/locale/pt/LC_MESSAGES/django.mo b/apps/history/locale/pt/LC_MESSAGES/django.mo index c1895c3b29..75258a208b 100644 Binary files a/apps/history/locale/pt/LC_MESSAGES/django.mo and b/apps/history/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/history/locale/ru/LC_MESSAGES/django.mo b/apps/history/locale/ru/LC_MESSAGES/django.mo index 445418b39e..20fe9c37ee 100644 Binary files a/apps/history/locale/ru/LC_MESSAGES/django.mo and b/apps/history/locale/ru/LC_MESSAGES/django.mo differ diff --git a/apps/linking/forms.py b/apps/linking/forms.py index 9568994b89..1e90e16c8a 100644 --- a/apps/linking/forms.py +++ b/apps/linking/forms.py @@ -45,7 +45,7 @@ class SmartLinkImageWidget(forms.widgets.Widget): for document in value['documents']: output.append(u'
' % (u'border: 5px solid black; padding: 3px;' if value['current_document'] == document else u'')) output.append(u'
%s
' % document) - output.append(u'
%s: %d
' % (ugettext(u'Pages'), document.documentpage_set.count())) + output.append(u'
%s: %d
' % (ugettext(u'Pages'), document.pages.count())) output.append(get_tags_inline_widget(document)) output.append(u'
' % document) output.append(document_html_widget(document, click_view='document_display', view='document_preview_multipage', fancybox_class='fancybox-noscaling', gallery_name=u'smart_link_%d_documents_gallery' % value['smart_link_instance'].pk)) diff --git a/apps/linking/locale/pt/LC_MESSAGES/django.mo b/apps/linking/locale/pt/LC_MESSAGES/django.mo index 35b5bd5ea8..b9625e5478 100644 Binary files a/apps/linking/locale/pt/LC_MESSAGES/django.mo and b/apps/linking/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/linking/managers.py b/apps/linking/managers.py index 03f9203b24..050ddffea2 100644 --- a/apps/linking/managers.py +++ b/apps/linking/managers.py @@ -59,7 +59,7 @@ class SmartLinkManager(models.Manager): if total_query: try: document_qs = Document.objects.filter(total_query) - result[smart_link] = {'documents': document_qs.order_by('file_filename') or []} + result[smart_link] = {'documents': document_qs.order_by('date_added') or []} except Exception, e: result[smart_link] = {'documents': []} errors.append(e) diff --git a/apps/lock_manager/managers.py b/apps/lock_manager/managers.py index 6c69fada19..f356be42dd 100644 --- a/apps/lock_manager/managers.py +++ b/apps/lock_manager/managers.py @@ -20,7 +20,8 @@ class LockManager(models.Manager): lock.save(force_insert=True) logger.debug('acquired lock: %s' % name) return lock - except IntegrityError: + except IntegrityError, msg: + logger.debug('IntegrityError: %s', msg) # There is already an existing lock # Check it's expiration date and if expired, reset it try: @@ -32,9 +33,10 @@ class LockManager(models.Manager): if datetime.datetime.now() > lock.creation_datetime + datetime.timedelta(seconds=lock.timeout): logger.debug('reseting deleting stale lock: %s' % name) - lock.timeout=timeout - logger.debug('try to reacquire stale lock: %s' % name) + lock.timeout = timeout + logger.debug('trying to reacquire stale lock: %s' % name) lock.save() + logger.debug('reacquired stale lock: %s' % name) return lock else: logger.debug('unable to acquire lock: %s' % name) diff --git a/apps/main/__init__.py b/apps/main/__init__.py index 6ae26a0009..e75d65f796 100644 --- a/apps/main/__init__.py +++ b/apps/main/__init__.py @@ -8,24 +8,32 @@ from project_tools.api import register_tool from main.conf.settings import SIDE_BAR_SEARCH from main.conf.settings import DISABLE_HOME_VIEW +__author__ = 'Roberto Rosario' +__copyright__ = 'Copyright 2011 Roberto Rosario' +__credits__ = ['Roberto Rosario',] +__license__ = 'GPL' +__maintainer__ = 'Roberto Rosario' +__email__ = 'roberto.rosario.gonzalez@gmail.com' +__status__ = 'Production' + +__version_info__ = { + 'major': 0, + 'minor': 11, + 'micro': 0, + 'releaselevel': 'final', + 'serial': 0 +} def is_superuser(context): return context['request'].user.is_staff or context['request'].user.is_superuser + maintenance_menu = {'text': _(u'maintenance'), 'view': 'maintenance_menu', 'famfam': 'wrench', 'icon': 'wrench.png'} statistics = {'text': _(u'statistics'), 'view': 'statistics', 'famfam': 'table', 'icon': 'blackboard_sum.png'} diagnostics = {'text': _(u'diagnostics'), 'view': 'diagnostics', 'famfam': 'pill', 'icon': 'pill.png'} sentry = {'text': _(u'sentry'), 'view': 'sentry', 'famfam': 'bug', 'icon': 'bug.png', 'condition': is_superuser} admin_site = {'text': _(u'admin site'), 'view': 'admin:index', 'famfam': 'keyboard', 'icon': 'keyboard.png', 'condition': is_superuser} -__version_info__ = { - 'major': 0, - 'minor': 10, - 'micro': 0, - 'releaselevel': 'hotfix', - 'serial': 4 -} - if not DISABLE_HOME_VIEW: register_top_menu('home', link={'text': _(u'home'), 'view': 'home', 'famfam': 'house'}, position=0) if not SIDE_BAR_SEARCH: @@ -36,14 +44,15 @@ def get_version(): """ Return the formatted version information """ - vers = ["%(major)i.%(minor)i" % __version_info__, ] + vers = ['%(major)i.%(minor)i' % __version_info__, ] if __version_info__['micro']: - vers.append(".%(micro)i" % __version_info__) + vers.append('.%(micro)i' % __version_info__) if __version_info__['releaselevel'] != 'final': vers.append('%(releaselevel)s%(serial)i' % __version_info__) return ''.join(vers) + __version__ = get_version() register_setup(admin_site) diff --git a/apps/main/locale/en/LC_MESSAGES/django.po b/apps/main/locale/en/LC_MESSAGES/django.po index ad9034e619..3f41a881ab 100644 --- a/apps/main/locale/en/LC_MESSAGES/django.po +++ b/apps/main/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" +"POT-Creation-Date: 2011-12-06 03:26-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,31 +17,31 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: __init__.py:15 +#: __init__.py:31 msgid "maintenance" msgstr "" -#: __init__.py:16 +#: __init__.py:32 msgid "statistics" msgstr "" -#: __init__.py:17 +#: __init__.py:33 msgid "diagnostics" msgstr "" -#: __init__.py:18 +#: __init__.py:34 msgid "sentry" msgstr "" -#: __init__.py:19 +#: __init__.py:35 msgid "admin site" msgstr "" -#: __init__.py:30 +#: __init__.py:38 msgid "home" msgstr "" -#: __init__.py:32 +#: __init__.py:40 msgid "search" msgstr "" @@ -130,6 +130,10 @@ msgstr "" msgid "Other available actions" msgstr "" +#: templates/home.html:8 +msgid "Django based open source document management system" +msgstr "" + #: templates/project_description.html:6 msgid "" "Open source, Django based electronic document manager with custom metadata, " diff --git a/apps/main/locale/es/LC_MESSAGES/django.mo b/apps/main/locale/es/LC_MESSAGES/django.mo index eae2b50bb5..3c57a910a9 100644 Binary files a/apps/main/locale/es/LC_MESSAGES/django.mo and b/apps/main/locale/es/LC_MESSAGES/django.mo differ diff --git a/apps/main/locale/es/LC_MESSAGES/django.po b/apps/main/locale/es/LC_MESSAGES/django.po index c530cc4628..da34696a3f 100644 --- a/apps/main/locale/es/LC_MESSAGES/django.po +++ b/apps/main/locale/es/LC_MESSAGES/django.po @@ -1,49 +1,49 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: # Roberto Rosario , 2011. msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" -"PO-Revision-Date: 2011-11-04 00:56+0000\n" -"Last-Translator: rosarior \n" -"Language-Team: Spanish (Castilian) (http://www.transifex.net/projects/p/" -"mayan-edms/team/es/)\n" -"Language: es\n" +"POT-Creation-Date: 2011-12-06 03:26-0400\n" +"PO-Revision-Date: 2011-12-06 03:27\n" +"Last-Translator: Administrador \n" +"Language-Team: Spanish (Castilian) (http://www.transifex.net/projects/p/mayan-edms/team/es/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +"Language: es\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" +"X-Translated-Using: django-rosetta 0.6.2\n" -#: __init__.py:15 +#: __init__.py:31 msgid "maintenance" msgstr "mantenimiento" -#: __init__.py:16 +#: __init__.py:32 msgid "statistics" msgstr "estadísticas" -#: __init__.py:17 +#: __init__.py:33 msgid "diagnostics" msgstr "diagnósticos" -#: __init__.py:18 +#: __init__.py:34 msgid "sentry" msgstr "sentry" -#: __init__.py:19 +#: __init__.py:35 msgid "admin site" msgstr "sitio administrativo" -#: __init__.py:30 +#: __init__.py:38 msgid "home" msgstr "inicio" -#: __init__.py:32 +#: __init__.py:40 msgid "search" msgstr "búsqueda" @@ -61,11 +61,11 @@ msgstr "Diagnósticos" #: conf/settings.py:12 msgid "" -"Controls whether the search functionality is provided by a sidebar widget or " -"by a menu entry." +"Controls whether the search functionality is provided by a sidebar " +"widget or by a menu entry." msgstr "" -"Controla si la funcionalidad de búsqueda es proporcionada por una barra " -"lateral o por una entrada de menú." +"Controla si la funcionalidad de búsqueda es proporcionada por una " +"barra lateral o por una entrada de menú." #: templates/about.html:5 msgid "About this program" @@ -134,10 +134,15 @@ msgstr "Acciones" msgid "Other available actions" msgstr "Otras acciones disponibles" +#: templates/home.html:8 +msgid "Django based open source document management system" +msgstr "Gestor de documentos de código abierto, basado en Django." + #: templates/project_description.html:6 msgid "" -"Open source, Django based electronic document manager with custom metadata, " -"indexing, tagging, file serving integration and OCR capabilities" +"Open source, Django based electronic document manager with custom " +"metadata, indexing, tagging, file serving integration and OCR " +"capabilities" msgstr "" "Gestor documental de código abierto, basado en Django con metadatos " "personaliables, indexación, etiquedado de documentos, integración de " diff --git a/apps/main/locale/pt/LC_MESSAGES/django.mo b/apps/main/locale/pt/LC_MESSAGES/django.mo index 602863c9f4..f0e4a1a726 100644 Binary files a/apps/main/locale/pt/LC_MESSAGES/django.mo and b/apps/main/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/main/locale/ru/LC_MESSAGES/django.mo b/apps/main/locale/ru/LC_MESSAGES/django.mo index ea406179a2..4dd6882e6c 100644 Binary files a/apps/main/locale/ru/LC_MESSAGES/django.mo and b/apps/main/locale/ru/LC_MESSAGES/django.mo differ diff --git a/apps/main/templates/home.html b/apps/main/templates/home.html index c4f0d284b2..095ed73001 100644 --- a/apps/main/templates/home.html +++ b/apps/main/templates/home.html @@ -5,6 +5,7 @@

{% project_name %}

+

{% trans 'Django based open source document management system' %}


{% endblock %} {% block footer %} diff --git a/apps/metadata/locale/es/LC_MESSAGES/django.mo b/apps/metadata/locale/es/LC_MESSAGES/django.mo index 0b8f28b148..be0c179aa5 100644 Binary files a/apps/metadata/locale/es/LC_MESSAGES/django.mo and b/apps/metadata/locale/es/LC_MESSAGES/django.mo differ diff --git a/apps/metadata/locale/pt/LC_MESSAGES/django.mo b/apps/metadata/locale/pt/LC_MESSAGES/django.mo index c4d0149b8c..ad93c5f075 100644 Binary files a/apps/metadata/locale/pt/LC_MESSAGES/django.mo and b/apps/metadata/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/metadata/locale/ru/LC_MESSAGES/django.mo b/apps/metadata/locale/ru/LC_MESSAGES/django.mo index ec8d146e9c..d58dc848fd 100644 Binary files a/apps/metadata/locale/ru/LC_MESSAGES/django.mo and b/apps/metadata/locale/ru/LC_MESSAGES/django.mo differ diff --git a/apps/navigation/locale/es/LC_MESSAGES/django.mo b/apps/navigation/locale/es/LC_MESSAGES/django.mo index a94ea1cd06..6091110708 100644 Binary files a/apps/navigation/locale/es/LC_MESSAGES/django.mo and b/apps/navigation/locale/es/LC_MESSAGES/django.mo differ diff --git a/apps/navigation/locale/pt/LC_MESSAGES/django.mo b/apps/navigation/locale/pt/LC_MESSAGES/django.mo index a75c87ed16..da6beaf7ea 100644 Binary files a/apps/navigation/locale/pt/LC_MESSAGES/django.mo and b/apps/navigation/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/navigation/locale/ru/LC_MESSAGES/django.mo b/apps/navigation/locale/ru/LC_MESSAGES/django.mo index 61a3328f93..8dec51de24 100644 Binary files a/apps/navigation/locale/ru/LC_MESSAGES/django.mo and b/apps/navigation/locale/ru/LC_MESSAGES/django.mo differ diff --git a/apps/ocr/__init__.py b/apps/ocr/__init__.py index cb60eadc57..b07fa07d72 100644 --- a/apps/ocr/__init__.py +++ b/apps/ocr/__init__.py @@ -108,10 +108,14 @@ def document_post_save(sender, instance, **kwargs): post_save.connect(document_post_save, sender=Document) -@receiver(post_save, dispatch_uid='call_queue', sender=QueueDocument) -def call_queue(sender, **kwargs): - logger.debug('got call_queue signal') - task_process_document_queues() +# Disabled because it appears Django execute signals using the same +# process effectively blocking the view until the OCR process completes +# which could take several minutes :/ +#@receiver(post_save, dispatch_uid='call_queue', sender=QueueDocument) +#def call_queue(sender, **kwargs): +# if kwargs.get('created', False): +# logger.debug('got call_queue signal: %s' % kwargs) +# task_process_document_queues() create_default_queue() diff --git a/apps/ocr/api.py b/apps/ocr/api.py index 4d70443f92..e568ecf6c8 100644 --- a/apps/ocr/api.py +++ b/apps/ocr/api.py @@ -88,7 +88,7 @@ def do_document_ocr(queue_document): parser, if the parser fails or if there is no parser registered for the document mimetype do a visual OCR by calling tesseract """ - for document_page in queue_document.document.documentpage_set.all(): + for document_page in queue_document.document.pages.all(): try: # Try to extract text by means of a parser parse_document_page(document_page) diff --git a/apps/ocr/conf/settings.py b/apps/ocr/conf/settings.py index bfc285f9b2..ff2f7ca04d 100644 --- a/apps/ocr/conf/settings.py +++ b/apps/ocr/conf/settings.py @@ -10,7 +10,7 @@ register_settings( settings=[ {'name': u'TESSERACT_PATH', 'global_name': u'OCR_TESSERACT_PATH', 'default': u'/usr/bin/tesseract', 'exists': True}, {'name': u'TESSERACT_LANGUAGE', 'global_name': u'OCR_TESSERACT_LANGUAGE', 'default': u'eng'}, - {'name': u'REPLICATION_DELAY', 'global_name': u'OCR_REPLICATION_DELAY', 'default': 10, 'description': _(u'Amount of seconds to delay OCR of documents to allow for the node\'s storage replication overhead.')}, + {'name': u'REPLICATION_DELAY', 'global_name': u'OCR_REPLICATION_DELAY', 'default': 0, 'description': _(u'Amount of seconds to delay OCR of documents to allow for the node\'s storage replication overhead.')}, {'name': u'NODE_CONCURRENT_EXECUTION', 'global_name': u'OCR_NODE_CONCURRENT_EXECUTION', 'default': 1, 'description': _(u'Maximum amount of concurrent document OCRs a node can perform.')}, {'name': u'AUTOMATIC_OCR', 'global_name': u'OCR_AUTOMATIC_OCR', 'default': False, 'description': _(u'Automatically queue newly created documents for OCR.')}, {'name': u'QUEUE_PROCESSING_INTERVAL', 'global_name': u'OCR_QUEUE_PROCESSING_INTERVAL', 'default': 10}, diff --git a/apps/ocr/exceptions.py b/apps/ocr/exceptions.py index 41ebe0c8ca..704e0ae9c7 100644 --- a/apps/ocr/exceptions.py +++ b/apps/ocr/exceptions.py @@ -11,3 +11,7 @@ class UnpaperError(Exception): Raised by unpaper """ pass + + +class ReQueueError(Exception): + pass diff --git a/apps/ocr/locale/es/LC_MESSAGES/django.mo b/apps/ocr/locale/es/LC_MESSAGES/django.mo index d70f38f4ed..9c4c0ed1f3 100644 Binary files a/apps/ocr/locale/es/LC_MESSAGES/django.mo and b/apps/ocr/locale/es/LC_MESSAGES/django.mo differ diff --git a/apps/ocr/locale/pt/LC_MESSAGES/django.mo b/apps/ocr/locale/pt/LC_MESSAGES/django.mo index cc071a684c..065b53a37a 100644 Binary files a/apps/ocr/locale/pt/LC_MESSAGES/django.mo and b/apps/ocr/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/ocr/locale/ru/LC_MESSAGES/django.mo b/apps/ocr/locale/ru/LC_MESSAGES/django.mo index 97665fd5de..2532ba2c71 100644 Binary files a/apps/ocr/locale/ru/LC_MESSAGES/django.mo and b/apps/ocr/locale/ru/LC_MESSAGES/django.mo differ diff --git a/apps/ocr/models.py b/apps/ocr/models.py index 6e25ef334f..f33ee3fb35 100644 --- a/apps/ocr/models.py +++ b/apps/ocr/models.py @@ -1,4 +1,5 @@ from ast import literal_eval +from datetime import datetime from django.db import models from django.utils.translation import ugettext_lazy as _ @@ -14,8 +15,9 @@ from sources.managers import SourceTransformationManager from ocr.literals import DOCUMENTQUEUE_STATE_STOPPED, \ DOCUMENTQUEUE_STATE_CHOICES, QUEUEDOCUMENT_STATE_PENDING, \ - QUEUEDOCUMENT_STATE_CHOICES + QUEUEDOCUMENT_STATE_CHOICES, QUEUEDOCUMENT_STATE_PROCESSING from ocr.managers import DocumentQueueManager +from ocr.exceptions import ReQueueError class DocumentQueue(models.Model): @@ -56,6 +58,17 @@ class QueueDocument(models.Model): def get_transformation_list(self): return QueueTransformation.transformations.get_for_object_as_list(self) + def requeue(self): + if self.state == QUEUEDOCUMENT_STATE_PROCESSING: + raise ReQueueError + else: + self.datetime_submitted = datetime.now() + self.state = QUEUEDOCUMENT_STATE_PENDING + self.delay = False + self.result = None + self.node_name = None + self.save() + def __unicode__(self): try: return unicode(self.document) diff --git a/apps/ocr/parsers/__init__.py b/apps/ocr/parsers/__init__.py index 8ed4be7cb7..3d5a39635e 100644 --- a/apps/ocr/parsers/__init__.py +++ b/apps/ocr/parsers/__init__.py @@ -1,20 +1,35 @@ import slate +import logging from django.utils.translation import ugettext as _ +from converter import office_converter +from converter import office_converter +from converter.office_converter import OfficeConverter +from converter.exceptions import OfficeBackendError, OfficeConversionError +from documents.utils import document_save_to_temp_dir + from ocr.parsers.exceptions import ParserError, ParserUnknownFile + mimetype_registry = {} +logger = logging.getLogger(__name__) -def register_parser(mimetype, function): - mimetype_registry[mimetype] = {'function': function} +def register_parser(function, mimetype=None, mimetypes=None): + if mimetypes: + for mimetype in mimetypes: + mimetype_registry[mimetype] = {'function': function} + else: + mimetype_registry[mimetype] = {'function': function} -def pdf_parser(document_page): - fd = document_page.document.open() - pdf_pages = slate.PDF(fd) - fd.close() +def pdf_parser(document_page, descriptor=None): + if not descriptor: + descriptor = document_page.document_version.open() + + pdf_pages = slate.PDF(descriptor) + descriptor.close() if pdf_pages[document_page.page_number - 1] == '\x0c': raise ParserError @@ -24,11 +39,37 @@ def pdf_parser(document_page): document_page.save() +def office_parser(document_page): + logger.debug('executing') + try: + office_converter = OfficeConverter() + document_file = document_save_to_temp_dir(document_page.document, document_page.document.checksum) + logger.debug('document_file: %s', document_file) + + office_converter.convert(document_file, mimetype=document_page.document.file_mimetype) + if office_converter.exists: + input_filepath = office_converter.output_filepath + logger.debug('office_converter.output_filepath: %s', input_filepath) + + pdf_parser(document_page, descriptor=open(input_filepath)) + else: + raise ParserError + + except OfficeConversionError, msg: + print msg + raise ParserError + + def parse_document_page(document_page): + logger.debug('executing') + logger.debug('document_page: %s' % document_page) + logger.debug('mimetype: %s' % document_page.document.file_mimetype) + try: mimetype_registry[document_page.document.file_mimetype]['function'](document_page) except KeyError: raise ParserUnknownFile -register_parser('application/pdf', pdf_parser) +register_parser(mimetype=u'application/pdf', function=pdf_parser) +register_parser(mimetypes=office_converter.CONVERTER_OFFICE_FILE_MIMETYPES, function=office_parser) diff --git a/apps/ocr/tasks.py b/apps/ocr/tasks.py index 06f55bd69d..7c01a36e4d 100644 --- a/apps/ocr/tasks.py +++ b/apps/ocr/tasks.py @@ -2,6 +2,7 @@ from datetime import timedelta, datetime import platform from time import sleep from random import random +import logging from django.db.models import Q @@ -21,10 +22,15 @@ from ocr.conf.settings import QUEUE_PROCESSING_INTERVAL LOCK_EXPIRE = 60 * 10 # Lock expires in 10 minutes # TODO: Tie LOCK_EXPIRATION with hard task timeout +logger = logging.getLogger(__name__) + + def task_process_queue_document(queue_document_id): lock_id = u'task_proc_queue_doc-%d' % queue_document_id try: + logger.debug('trying to acquire lock: %s' % lock_id) lock = Lock.acquire_lock(lock_id, LOCK_EXPIRE) + logger.debug('acquired lock: %s' % lock_id) queue_document = QueueDocument.objects.get(pk=queue_document_id) queue_document.state = QUEUEDOCUMENT_STATE_PROCESSING queue_document.node_name = platform.node() @@ -39,6 +45,7 @@ def task_process_queue_document(queue_document_id): lock.release() except LockError: + logger.debug('unable to obtain lock') pass @@ -69,6 +76,7 @@ def reset_orphans(): def task_process_document_queues(): + logger.debug('executed') # reset_orphans() # Causes problems with big clusters increased latency # Disabled until better solution @@ -90,3 +98,10 @@ def task_process_document_queues(): except Exception, e: pass #print 'DocumentQueueWatcher exception: %s' % e + finally: + # Don't process anymore from this queryset, might be stale + break; + else: + logger.debug('already processing maximun') + else: + logger.debug('nothing to process') diff --git a/apps/ocr/views.py b/apps/ocr/views.py index d6102f1200..8fbc40d444 100644 --- a/apps/ocr/views.py +++ b/apps/ocr/views.py @@ -1,4 +1,3 @@ -import datetime import socket from django.http import HttpResponseRedirect @@ -23,7 +22,7 @@ from ocr.models import DocumentQueue, QueueDocument, QueueTransformation from ocr.literals import QUEUEDOCUMENT_STATE_PENDING, \ QUEUEDOCUMENT_STATE_PROCESSING, DOCUMENTQUEUE_STATE_STOPPED, \ DOCUMENTQUEUE_STATE_ACTIVE -from ocr.exceptions import AlreadyQueued +from ocr.exceptions import AlreadyQueued, ReQueueError from ocr.api import clean_pages from ocr.forms import QueueTransformationForm, QueueTransformationForm_create @@ -122,7 +121,7 @@ def submit_document_multiple(request): for item_id in request.GET.get('id_list', '').split(','): submit_document(request, item_id) - return HttpResponseRedirect(request.META['HTTP_REFERER']) + return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/')) def submit_document(request, document_id): @@ -130,7 +129,7 @@ def submit_document(request, document_id): document = get_object_or_404(Document, pk=document_id) return submit_document_to_queue(request, document=document, - post_submit_redirect=request.META['HTTP_REFERER']) + post_submit_redirect=request.META.get('HTTP_REFERER', '/')) def submit_document_to_queue(request, document, post_submit_redirect=None): @@ -167,22 +166,21 @@ def re_queue_document(request, queue_document_id=None, queue_document_id_list=No if request.method == 'POST': for queue_document in queue_documents: try: - queue_document.document - if queue_document.state == QUEUEDOCUMENT_STATE_PROCESSING: - messages.warning(request, _(u'Document: %s is already being processed and can\'t be re-queded.') % queue_document) - else: - queue_document.datetime_submitted = datetime.datetime.now() - queue_document.state = QUEUEDOCUMENT_STATE_PENDING - queue_document.delay = False - queue_document.result = None - queue_document.node_name = None - queue_document.save() - messages.success(request, _(u'Document: %(document)s was re-queued to the OCR queue: %(queue)s') % { - 'document': queue_document.document, 'queue': queue_document.document_queue.label}) + queue_document.requeue() + messages.success( + request, + _(u'Document: %(document)s was re-queued to the OCR queue: %(queue)s') % { + 'document': queue_document.document, + 'queue': queue_document.document_queue.label + } + ) except Document.DoesNotExist: messages.error(request, _(u'Document id#: %d, no longer exists.') % queue_document.document_id) - except Exception, e: - messages.error(request, e) + except ReQueueError: + messages.warning( + request, + _(u'Document: %s is already being processed and can\'t be re-queded.') % queue_document + ) return HttpResponseRedirect(next) context = { diff --git a/apps/permissions/locale/pt/LC_MESSAGES/django.mo b/apps/permissions/locale/pt/LC_MESSAGES/django.mo index ef53a7d248..715b0f389b 100644 Binary files a/apps/permissions/locale/pt/LC_MESSAGES/django.mo and b/apps/permissions/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/project_setup/locale/es/LC_MESSAGES/django.mo b/apps/project_setup/locale/es/LC_MESSAGES/django.mo index e76f92294e..37f3fb8053 100644 Binary files a/apps/project_setup/locale/es/LC_MESSAGES/django.mo and b/apps/project_setup/locale/es/LC_MESSAGES/django.mo differ diff --git a/apps/project_setup/locale/pt/LC_MESSAGES/django.mo b/apps/project_setup/locale/pt/LC_MESSAGES/django.mo index 912f33b52a..7c88252009 100644 Binary files a/apps/project_setup/locale/pt/LC_MESSAGES/django.mo and b/apps/project_setup/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/project_setup/locale/ru/LC_MESSAGES/django.mo b/apps/project_setup/locale/ru/LC_MESSAGES/django.mo index 8bc36db741..6eafab0493 100644 Binary files a/apps/project_setup/locale/ru/LC_MESSAGES/django.mo and b/apps/project_setup/locale/ru/LC_MESSAGES/django.mo differ diff --git a/apps/project_tools/locale/es/LC_MESSAGES/django.mo b/apps/project_tools/locale/es/LC_MESSAGES/django.mo index 848f4b6922..9416fb42f7 100644 Binary files a/apps/project_tools/locale/es/LC_MESSAGES/django.mo and b/apps/project_tools/locale/es/LC_MESSAGES/django.mo differ diff --git a/apps/project_tools/locale/pt/LC_MESSAGES/django.mo b/apps/project_tools/locale/pt/LC_MESSAGES/django.mo index a38466231c..424ab9c3ee 100644 Binary files a/apps/project_tools/locale/pt/LC_MESSAGES/django.mo and b/apps/project_tools/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/project_tools/locale/ru/LC_MESSAGES/django.mo b/apps/project_tools/locale/ru/LC_MESSAGES/django.mo index 728bb26bdd..99daa2c0af 100644 Binary files a/apps/project_tools/locale/ru/LC_MESSAGES/django.mo and b/apps/project_tools/locale/ru/LC_MESSAGES/django.mo differ diff --git a/apps/rest_api/resources.py b/apps/rest_api/resources.py index 99a5c26613..d127096f3a 100644 --- a/apps/rest_api/resources.py +++ b/apps/rest_api/resources.py @@ -8,31 +8,38 @@ from converter.exceptions import UnknownFileFormat, UnkownConvertError class DocumentResourceSimple(ModelResource): model = Document - fields = ('url', 'pk', 'document_type', 'uuid', 'date_added', 'description', 'tags', 'comments', 'expensive_methods', 'files') + fields = ('url', 'pk', 'document_type', 'uuid', 'date_added', 'description', 'tags', 'comments', 'expensive_methods', 'versions') - def files(self, instance): + def versions(self, instance): return [ { - 'version': 1, - 'mimetype': instance.file_mimetype, - 'encoding': instance.file_mime_encoding, - 'filename': instance.get_fullname(), - 'date_updated': instance.date_updated, - 'checksum': instance.checksum, - 'size': instance.size, - 'exists': instance.exists(), + 'version': version.get_formated_version(), + 'major': version.major, + 'minor': version.minor, + 'micro': version.micro, + 'release_level': version.release_level, + 'serial': version.serial, + 'timestamp': version.timestamp, + 'comment': version.comment, + 'mimetype': version.mimetype, + 'encoding': version.encoding, + 'filename': version.filename, + 'checksum': version.checksum, + 'download': reverse('document_version_download', args=[version.pk]), + 'stored_filename': version.file.name, + # TODO: Add transformations 'pages': [ { - 'page_numer': page.page_number, + 'content': page.content, 'page_label': page.page_label, - 'is_zoomable': reverse('documents-expensive-is_zoomable', args=[instance.pk, page.page_number]), - - #'content': + 'page_number': page.page_number, } - for page in instance.documentpage_set.all() + for page in version.pages.all() ] + } + for version in instance.versions.all() ] - + def expensive_methods(self, instance): return [] diff --git a/apps/rest_api/urls.py b/apps/rest_api/urls.py index 3265dd7574..34553951a6 100644 --- a/apps/rest_api/urls.py +++ b/apps/rest_api/urls.py @@ -12,5 +12,5 @@ urlpatterns = patterns('', # Version 0 alpha API calls url(r'^v0/document/(?P[0-9]+)/$', ReadOnlyInstanceModelView.as_view(resource=DocumentResourceSimple), name='documents-simple'), - url(r'^v0/document/(?P[0-9]+)/page/(?P[0-9]+)/expensive/is_zoomable/$', IsZoomable.as_view(), name='documents-expensive-is_zoomable'), + url(r'^v0/document/(?P[0-9]+)/version/(?P[0-9]+)/page/(?P[0-9]+)/expensive/is_zoomable/$', IsZoomable.as_view(), name='documents-expensive-is_zoomable'), ) diff --git a/apps/rest_api/views.py b/apps/rest_api/views.py index 6a1b5dfc14..6624e41510 100644 --- a/apps/rest_api/views.py +++ b/apps/rest_api/views.py @@ -6,7 +6,7 @@ from django.utils.translation import ugettext_lazy as _ from django.shortcuts import get_object_or_404 from django.core.urlresolvers import reverse -from documents.models import Document +from documents.models import Document, DocumentVersion, DocumentPage from converter.exceptions import UnknownFileFormat, UnkownConvertError from djangorestframework.views import View, ModelView, ListModelView, InstanceModelView @@ -45,11 +45,13 @@ class Version_0(View): class IsZoomable(View): - def get(self, request, pk, page_number): + def get(self, request, pk, page_number, version_pk): logger.info('received is_zoomable call from: %s' % (request.META['REMOTE_ADDR'])) - document = get_object_or_404(Document, pk=pk) + document_version = get_object_or_404(DocumentVersion, pk=version_pk) try: - document.get_image_cache_name(int(page_number)) + document_version.document.get_image_cache_name(int(page_number), version_pk) return {'result': True} - except (UnknownFileFormat, UnkownConvertError): + except (UnknownFileFormat, UnkownConvertError, + DocumentPage.DoesNotExist, Document.DoesNotExist, + DocumentVersion.DoesNotExist): return {'result': False} diff --git a/apps/smart_settings/locale/es/LC_MESSAGES/django.mo b/apps/smart_settings/locale/es/LC_MESSAGES/django.mo index 0fa0826b1c..71eba7fd8b 100644 Binary files a/apps/smart_settings/locale/es/LC_MESSAGES/django.mo and b/apps/smart_settings/locale/es/LC_MESSAGES/django.mo differ diff --git a/apps/smart_settings/locale/pt/LC_MESSAGES/django.mo b/apps/smart_settings/locale/pt/LC_MESSAGES/django.mo index c82fae32fc..868ac5ecd2 100644 Binary files a/apps/smart_settings/locale/pt/LC_MESSAGES/django.mo and b/apps/smart_settings/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/smart_settings/locale/ru/LC_MESSAGES/django.mo b/apps/smart_settings/locale/ru/LC_MESSAGES/django.mo index 236652066e..d8be586181 100644 Binary files a/apps/smart_settings/locale/ru/LC_MESSAGES/django.mo and b/apps/smart_settings/locale/ru/LC_MESSAGES/django.mo differ diff --git a/apps/sources/__init__.py b/apps/sources/__init__.py index ad34940468..faca4f075c 100644 --- a/apps/sources/__init__.py +++ b/apps/sources/__init__.py @@ -5,6 +5,8 @@ from navigation.api import register_links, \ from permissions.api import register_permission, set_namespace_title from common.utils import encapsulate from project_setup.api import register_setup +from documents.models import Document +from documents.literals import PERMISSION_DOCUMENT_CREATE from sources.staging import StagingFile from sources.models import WebForm, StagingFolder, SourceTransformation, \ @@ -41,6 +43,8 @@ setup_source_transformation_delete = {'text': _(u'delete'), 'view': 'setup_sourc source_list = {'text': _(u'Document sources'), 'view': 'setup_web_form_list', 'famfam': 'page_add', 'children_url_regex': [r'sources/setup']} +upload_version = {'text': _(u'upload new version'), 'view': 'upload_version', 'args': 'object.pk', 'famfam': 'page_add', 'permissions': [PERMISSION_DOCUMENT_CREATE]} + register_links(StagingFile, [staging_file_delete]) register_links(SourceTransformation, [setup_source_transformation_edit, setup_source_transformation_delete]) @@ -61,6 +65,9 @@ register_links(StagingFolder, [setup_source_transformation_list, setup_source_ed register_links(WatchFolder, [setup_web_form_list, setup_staging_folder_list, setup_watch_folder_list], menu_name='form_header') register_links(WatchFolder, [setup_source_transformation_list, setup_source_edit, setup_source_delete]) +# Document version +register_links(['document_version_list', 'upload_version', 'document_version_revert'], [upload_version], menu_name='sidebar') + register_links(['setup_source_transformation_create', 'setup_source_transformation_edit', 'setup_source_transformation_delete', 'setup_source_transformation_list'], [setup_source_transformation_create], menu_name='sidebar') source_views = ['setup_web_form_list', 'setup_staging_folder_list', 'setup_watch_folder_list', 'setup_source_edit', 'setup_source_delete', 'setup_source_create', 'setup_source_transformation_list', 'setup_source_transformation_edit', 'setup_source_transformation_delete', 'setup_source_transformation_create'] diff --git a/apps/sources/forms.py b/apps/sources/forms.py index de9b7ee1ce..4b046994cf 100644 --- a/apps/sources/forms.py +++ b/apps/sources/forms.py @@ -37,7 +37,7 @@ class StagingDocumentForm(DocumentForm): staging_list_index = self.fields.keyOrder.index('staging_file_id') staging_list = self.fields.keyOrder.pop(staging_list_index) self.fields.keyOrder.insert(0, staging_list) - + staging_file_id = forms.ChoiceField(label=_(u'Staging file')) class Meta(DocumentForm.Meta): @@ -45,6 +45,8 @@ class StagingDocumentForm(DocumentForm): class WebFormForm(DocumentForm): + file = forms.FileField(label=_(u'File')) + def __init__(self, *args, **kwargs): show_expand = kwargs.pop('show_expand', False) self.source = kwargs.pop('source') @@ -56,6 +58,10 @@ class WebFormForm(DocumentForm): help_text=ugettext(u'Upload a compressed file\'s contained files as individual documents') ) + # Move the file filed to the top + self.fields.keyOrder.remove('file') + self.fields.keyOrder.insert(0, 'file') + def clean_file(self): data = self.cleaned_data['file'] validate_whitelist_blacklist(data.name, self.source.whitelist.split(','), self.source.blacklist.split(',')) diff --git a/apps/sources/locale/pt/LC_MESSAGES/django.mo b/apps/sources/locale/pt/LC_MESSAGES/django.mo index 184999dc83..242213a5ba 100644 Binary files a/apps/sources/locale/pt/LC_MESSAGES/django.mo and b/apps/sources/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/sources/locale/ru/LC_MESSAGES/django.mo b/apps/sources/locale/ru/LC_MESSAGES/django.mo index 8c226a7ffc..90fbdf8a30 100644 Binary files a/apps/sources/locale/ru/LC_MESSAGES/django.mo and b/apps/sources/locale/ru/LC_MESSAGES/django.mo differ diff --git a/apps/sources/models.py b/apps/sources/models.py index c97f2f4ca4..3d09915617 100644 --- a/apps/sources/models.py +++ b/apps/sources/models.py @@ -52,7 +52,7 @@ class BaseModel(models.Model): def get_transformation_list(self): return SourceTransformation.transformations.get_for_object_as_list(self) - def upload_file(self, file_object, filename=None, document_type=None, expand=False, metadata_dict_list=None, user=None): + def upload_file(self, file_object, filename=None, use_file_name=False, document_type=None, expand=False, metadata_dict_list=None, user=None, document=None, new_version_data=None): if expand: try: cf = CompressedFile(file_object) @@ -63,31 +63,44 @@ class BaseModel(models.Model): except NotACompressedFile: self.upload_single_file(file_object, filename, document_type, metadata_dict_list, user) else: - self.upload_single_file(file_object, filename, document_type, metadata_dict_list, user) + self.upload_single_file(file_object, filename, use_file_name, document_type, metadata_dict_list, user, document, new_version_data) file_object.close() - def upload_single_file(self, file_object, filename=None, document_type=None, metadata_dict_list=None, user=None): - transformations, errors = self.get_transformation_list() - document = Document(file=file_object) - if document_type: - document.document_type = document_type - document.save() - if filename: - document.file_filename = filename - document.save() + def upload_single_file(self, file_object, filename=None, use_file_name=False, document_type=None, metadata_dict_list=None, user=None, document=None, new_version_data=None): + if not document: + document = Document() + if document_type: + document.document_type = document_type + document.save() - document.apply_default_transformations(transformations) + if metadata_dict_list: + save_metadata_list(metadata_dict_list, document, create=True) + warnings = update_indexes(document) - if metadata_dict_list: - save_metadata_list(metadata_dict_list, document, create=True) - warnings = update_indexes(document) - - if user: - document.add_as_recent_document_for_user(user) - create_history(HISTORY_DOCUMENT_CREATED, document, {'user': user}) + if user: + document.add_as_recent_document_for_user(user) + create_history(HISTORY_DOCUMENT_CREATED, document, {'user': user}) + else: + create_history(HISTORY_DOCUMENT_CREATED, document) else: - create_history(HISTORY_DOCUMENT_CREATED, document) + if use_file_name: + filename = None + else: + filename = filename if filename else document.latest_version.filename + + if not new_version_data: + new_version_data = {} + + new_version = document.new_version(file=file_object, **new_version_data) + if filename: + new_version.filename = filename + new_version.save() + + transformations, errors = self.get_transformation_list() + + new_version.apply_default_transformations(transformations) + #TODO: new HISTORY for version updates class Meta: ordering = ('title',) diff --git a/apps/sources/urls.py b/apps/sources/urls.py index b75573382d..7baf07a1a9 100644 --- a/apps/sources/urls.py +++ b/apps/sources/urls.py @@ -8,8 +8,11 @@ urlpatterns = patterns('sources.views', url(r'^staging_file/type/(?P\w+)/(?P\d+)/(?P\w+)/delete/$', 'staging_file_delete', (), 'staging_file_delete'), url(r'^staging_file/type/staging_folder/(?P\d+)/(?P\w+)/thumbnail/$', 'staging_file_thumbnail', (), 'staging_file_thumbnail'), - url(r'^upload/interactive/(?P\w+)/(?P\d+)/$', 'upload_interactive', (), 'upload_interactive'), - url(r'^upload/interactive/$', 'upload_interactive', (), 'upload_interactive'), + url(r'^upload/document/new/interactive/(?P\w+)/(?P\d+)/$', 'upload_interactive', (), 'upload_interactive'), + url(r'^upload/document/new/interactive/$', 'upload_interactive', (), 'upload_interactive'), + + url(r'^upload/document/(?P\d+)/version/interactive/(?P\w+)/(?P\d+)/$', 'upload_interactive', (), 'upload_version'), + url(r'^upload/document/(?P\d+)/version/interactive/$', 'upload_interactive', (), 'upload_version'), #Setup views diff --git a/apps/sources/views.py b/apps/sources/views.py index c2cde5e7d8..17e8c380a6 100644 --- a/apps/sources/views.py +++ b/apps/sources/views.py @@ -8,7 +8,7 @@ from django.utils.translation import ugettext from django.utils.safestring import mark_safe from documents.literals import PERMISSION_DOCUMENT_CREATE -from documents.models import DocumentType +from documents.models import DocumentType, Document from documents.conf.settings import THUMBNAIL_SIZE from metadata.api import decode_metadata_from_url, metadata_repr_as_list from permissions.api import check_permissions @@ -35,30 +35,34 @@ def return_function(obj): return lambda context: context['source'].source_type == obj.source_type and context['source'].pk == obj.pk -def get_active_tab_links(): - tab_links = [] +def get_tab_link_for_source(source, document=None): + if document: + view = u'upload_version' + args = [document.pk, u'"%s"' % source.source_type, source.pk] + else: + view = u'upload_interactive' + args = [u'"%s"' % source.source_type, source.pk] + + return { + 'text': source.title, + 'view': view, + 'args': args, + 'famfam': source.icon, + 'keep_query': True, + 'conditional_highlight': return_function(source), + } + +def get_active_tab_links(document=None): + tab_links = [] + web_forms = WebForm.objects.filter(enabled=True) for web_form in web_forms: - tab_links.append({ - 'text': web_form.title, - 'view': 'upload_interactive', - 'args': [u'"%s"' % web_form.source_type, web_form.pk], - 'famfam': web_form.icon, - 'keep_query': True, - 'conditional_highlight': return_function(web_form), - }) + tab_links.append(get_tab_link_for_source(web_form, document)) staging_folders = StagingFolder.objects.filter(enabled=True) for staging_folder in staging_folders: - tab_links.append({ - 'text': staging_folder.title, - 'view': 'upload_interactive', - 'args': [u'"%s"' % staging_folder.source_type, staging_folder.pk], - 'famfam': staging_folder.icon, - 'keep_query': True, - 'conditional_highlight': return_function(staging_folder), - }) + tab_links.append(get_tab_link_for_source(staging_folder, document)) return { 'tab_links': tab_links, @@ -66,15 +70,19 @@ def get_active_tab_links(): SOURCE_CHOICE_STAGING: staging_folders } - -def upload_interactive(request, source_type=None, source_id=None): +def upload_interactive(request, source_type=None, source_id=None, document_pk=None): check_permissions(request.user, [PERMISSION_DOCUMENT_CREATE]) subtemplates_list = [] - context = {} + if document_pk: + document = get_object_or_404(Document, pk=document_pk) + results = get_active_tab_links(document) + else: + document = None + results = get_active_tab_links() - results = get_active_tab_links() + context = {} if results[SOURCE_CHOICE_WEB_FORM].count() == 0 and results[SOURCE_CHOICE_STAGING].count() == 0: source_setup_link = mark_safe('%s' % (reverse('setup_web_form_list'), ugettext(u'here'))) @@ -113,43 +121,59 @@ def upload_interactive(request, source_type=None, source_id=None): if request.method == 'POST': form = WebFormForm(request.POST, request.FILES, document_type=document_type, - show_expand=(web_form.uncompress == SOURCE_UNCOMPRESS_CHOICE_ASK), - source=web_form + show_expand=(web_form.uncompress == SOURCE_UNCOMPRESS_CHOICE_ASK) and not document, + source=web_form, + instance=document ) if form.is_valid(): try: - if web_form.uncompress == SOURCE_UNCOMPRESS_CHOICE_ASK: - expand = form.cleaned_data['expand'] + if document: + expand = False else: - if web_form.uncompress == SOURCE_UNCOMPRESS_CHOICE_Y: - expand = True + if web_form.uncompress == SOURCE_UNCOMPRESS_CHOICE_ASK: + expand = form.cleaned_data.get('expand') else: - expand = False + if web_form.uncompress == SOURCE_UNCOMPRESS_CHOICE_Y: + expand = True + else: + expand = False new_filename = get_form_filename(form) + web_form.upload_file(request.FILES['file'], - new_filename, document_type=document_type, + new_filename, use_file_name=form.cleaned_data.get('use_file_name', False), + document_type=document_type, expand=expand, metadata_dict_list=decode_metadata_from_url(request.GET), - user=request.user + user=request.user, + document=document, + new_version_data=form.cleaned_data.get('new_version_data') ) - messages.success(request, _(u'Document uploaded successfully.')) + if document: + messages.success(request, _(u'Document version uploaded successfully.')) + return HttpResponseRedirect(reverse('document_view_simple', args=[document.pk])) + else: + messages.success(request, _(u'Document uploaded successfully.')) + return HttpResponseRedirect(request.get_full_path()) except Exception, e: - messages.error(request, e) - - return HttpResponseRedirect(request.get_full_path()) + messages.error(request, _(u'Unhandled exception: %s') % e) else: form = WebFormForm( - show_expand=(web_form.uncompress == SOURCE_UNCOMPRESS_CHOICE_ASK), + show_expand=(web_form.uncompress == SOURCE_UNCOMPRESS_CHOICE_ASK) and not document, document_type=document_type, - source=web_form + source=web_form, + instance=document ) - + if document: + title = _(u'upload a new version from source: %s') % web_form.title + else: + title = _(u'upload a local document from source: %s') % web_form.title + subtemplates_list.append({ 'name': 'generic_form_subtemplate.html', 'context': { 'form': form, - 'title': _(u'upload a local document from source: %s') % web_form.title, + 'title': title, }, }) elif source_type == SOURCE_CHOICE_STAGING: @@ -159,41 +183,56 @@ def upload_interactive(request, source_type=None, source_id=None): if request.method == 'POST': form = StagingDocumentForm(request.POST, request.FILES, cls=StagingFile, document_type=document_type, - show_expand=(staging_folder.uncompress == SOURCE_UNCOMPRESS_CHOICE_ASK), - source=staging_folder + show_expand=(staging_folder.uncompress == SOURCE_UNCOMPRESS_CHOICE_ASK) and not document, + source=staging_folder, + instance=document ) if form.is_valid(): try: staging_file = StagingFile.get(form.cleaned_data['staging_file_id']) - if staging_folder.uncompress == SOURCE_UNCOMPRESS_CHOICE_ASK: - expand = form.cleaned_data['expand'] + if document: + expand = False else: - if staging_folder.uncompress == SOURCE_UNCOMPRESS_CHOICE_Y: - expand = True + if staging_folder.uncompress == SOURCE_UNCOMPRESS_CHOICE_ASK: + expand = form.cleaned_dataget('expand') else: - expand = False + if staging_folder.uncompress == SOURCE_UNCOMPRESS_CHOICE_Y: + expand = True + else: + expand = False + new_filename = get_form_filename(form) + staging_folder.upload_file(staging_file.upload(), - new_filename, document_type=document_type, + new_filename, use_file_name=form.cleaned_data.get('use_file_name', False), + document_type=document_type, expand=expand, metadata_dict_list=decode_metadata_from_url(request.GET), - user=request.user + user=request.user, + document=document, + new_version_data=form.cleaned_data.get('new_version_data') ) - messages.success(request, _(u'Staging file: %s, uploaded successfully.') % staging_file.filename) + if document: + messages.success(request, _(u'Document version from staging file: %s, uploaded successfully.') % staging_file.filename) + else: + messages.success(request, _(u'Staging file: %s, uploaded successfully.') % staging_file.filename) if staging_folder.delete_after_upload: transformations, errors = staging_folder.get_transformation_list() staging_file.delete(preview_size=staging_folder.get_preview_size(), transformations=transformations) messages.success(request, _(u'Staging file: %s, deleted successfully.') % staging_file.filename) + if document: + return HttpResponseRedirect(reverse('document_view_simple', args=[document.pk])) + else: + return HttpResponseRedirect(request.get_full_path()) except Exception, e: - messages.error(request, e) - - return HttpResponseRedirect(request.get_full_path()) + messages.error(request, _(u'Unhandled exception: %s') % e) else: form = StagingDocumentForm(cls=StagingFile, document_type=document_type, - show_expand=(staging_folder.uncompress == SOURCE_UNCOMPRESS_CHOICE_ASK), - source=staging_folder + show_expand=(staging_folder.uncompress == SOURCE_UNCOMPRESS_CHOICE_ASK) and not document, + source=staging_folder, + instance=document ) try: staging_filelist = StagingFile.get_all() @@ -201,12 +240,17 @@ def upload_interactive(request, source_type=None, source_id=None): messages.error(request, e) staging_filelist = [] finally: + if document: + title = _(u'upload a new version from staging source: %s') % staging_folder.title + else: + title = _(u'upload a document from staging source: %s') % staging_folder.title + subtemplates_list = [ { 'name': 'generic_form_subtemplate.html', 'context': { 'form': form, - 'title': _(u'upload a document from staging source: %s') % staging_folder.title, + 'title': title, } }, { @@ -219,20 +263,40 @@ def upload_interactive(request, source_type=None, source_id=None): }, ] + if document: + context['object'] = document + context.update({ 'document_type_id': document_type_id, 'subtemplates_list': subtemplates_list, - 'sidebar_subtemplates_list': [ - { - 'name': 'generic_subtemplate.html', - 'context': { - 'title': _(u'Current metadata'), - 'paragraphs': metadata_repr_as_list(decode_metadata_from_url(request.GET)), - 'side_bar': True, - } - }], - 'temporary_navigation_links': {'form_header': {'upload_interactive': {'links': results['tab_links']}}}, + 'temporary_navigation_links': { + 'form_header': { + 'upload_version': { + 'links': results['tab_links'] + }, + 'upload_interactive': { + 'links': results['tab_links'] + } + } + }, }) + + if not document: + context.update( + { + 'sidebar_subtemplates_list': [ + { + 'name': 'generic_subtemplate.html', + 'context': { + 'title': _(u'Current metadata'), + 'paragraphs': metadata_repr_as_list(decode_metadata_from_url(request.GET)), + 'side_bar': True, + } + } + ], + } + ) + return render_to_response('generic_form.html', context, context_instance=RequestContext(request)) diff --git a/apps/sources/widgets.py b/apps/sources/widgets.py index 665303e427..ae5bf8e1bc 100644 --- a/apps/sources/widgets.py +++ b/apps/sources/widgets.py @@ -28,9 +28,9 @@ class FamFamRadioSelect(forms.widgets.RadioSelect): def staging_file_thumbnail(staging_file): try: staging_file.get_valid_image() - template = u'%(string)s' + template = u'%(string)s' except: - template = u'%(string)s' + template = u'%(string)s' return mark_safe(template % { 'url': reverse('staging_file_preview', args=[staging_file.source.source_type, staging_file.source.pk, staging_file.id]), diff --git a/apps/tags/locale/es/LC_MESSAGES/django.mo b/apps/tags/locale/es/LC_MESSAGES/django.mo index 1f704e983d..36248389b6 100644 Binary files a/apps/tags/locale/es/LC_MESSAGES/django.mo and b/apps/tags/locale/es/LC_MESSAGES/django.mo differ diff --git a/apps/tags/locale/pt/LC_MESSAGES/django.mo b/apps/tags/locale/pt/LC_MESSAGES/django.mo index b73dcaac57..db86c8cc36 100644 Binary files a/apps/tags/locale/pt/LC_MESSAGES/django.mo and b/apps/tags/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/tags/locale/ru/LC_MESSAGES/django.mo b/apps/tags/locale/ru/LC_MESSAGES/django.mo index 54f236cd26..9caaeae59d 100644 Binary files a/apps/tags/locale/ru/LC_MESSAGES/django.mo and b/apps/tags/locale/ru/LC_MESSAGES/django.mo differ diff --git a/apps/user_management/locale/es/LC_MESSAGES/django.mo b/apps/user_management/locale/es/LC_MESSAGES/django.mo index 00e662ae56..ff5716ae9b 100644 Binary files a/apps/user_management/locale/es/LC_MESSAGES/django.mo and b/apps/user_management/locale/es/LC_MESSAGES/django.mo differ diff --git a/apps/user_management/locale/pt/LC_MESSAGES/django.mo b/apps/user_management/locale/pt/LC_MESSAGES/django.mo index 6915454337..b5441bf678 100644 Binary files a/apps/user_management/locale/pt/LC_MESSAGES/django.mo and b/apps/user_management/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/user_management/locale/ru/LC_MESSAGES/django.mo b/apps/user_management/locale/ru/LC_MESSAGES/django.mo index 31694dab69..6783d4921d 100644 Binary files a/apps/user_management/locale/ru/LC_MESSAGES/django.mo and b/apps/user_management/locale/ru/LC_MESSAGES/django.mo differ diff --git a/apps/web_theme/locale/es/LC_MESSAGES/django.mo b/apps/web_theme/locale/es/LC_MESSAGES/django.mo index 20e43d26fd..937619e07a 100644 Binary files a/apps/web_theme/locale/es/LC_MESSAGES/django.mo and b/apps/web_theme/locale/es/LC_MESSAGES/django.mo differ diff --git a/apps/web_theme/locale/pt/LC_MESSAGES/django.mo b/apps/web_theme/locale/pt/LC_MESSAGES/django.mo index e7f0bd59d1..24ff710f54 100644 Binary files a/apps/web_theme/locale/pt/LC_MESSAGES/django.mo and b/apps/web_theme/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/web_theme/locale/ru/LC_MESSAGES/django.mo b/apps/web_theme/locale/ru/LC_MESSAGES/django.mo index bf3748bb25..53af98872f 100644 Binary files a/apps/web_theme/locale/ru/LC_MESSAGES/django.mo and b/apps/web_theme/locale/ru/LC_MESSAGES/django.mo differ diff --git a/docs/changelog.rst b/docs/changelog.rst index d06397f62c..80d0787351 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,3 +1,37 @@ +Version 0.11 +------------ +* Support for signed documents verification added, embedded and detached + signatures are supported. When verifying a document Mayan EDMS will + try to fetch the public key from the list of keyservers provided in the + configuration option SIGNATURES_KEYSERVERS (which defaults to + 'pool.sks-keyservers.net'). A public key management view has been added + to the setup menu as well as a key query and fetching view to manually + import keys from a keyserver. +* Added support for document versions. Users can upload unlimited amount + of versions for a document using a very flexible document version numbering + system, users can also revert to a previous document version. +* OCR queue processing improvements. +* Office documents handling improvements. +* Text extraction support for office documents. +* RTF text documents are now handled as office documents. +* Added a view to delete the document image cache, useful when switching + converter backends or doing diagnostics. +* Added South to the requirements. +* Merged documents' filename and extension database fiels into a single + filename field, filename are store as uploaded not manipulation is done + Users with existing data must install South and run the appropiate + migrate commands:: + $ pip install -r requirements/production.txt + $ ./manager syncdb + $ ./manage.py migrate documents 0001 --fake + $ ./manage.py migrate documents +* Added new office document mimetype + * application/vnd.ms-office +* Fixed documents not saving the file encoding +* Removed extra slash in ajax-loader.gif URL fixes #15, thanks to + IHLeanne for finding this one + + Version 0.10.1 -------------- * Upgraded django-compressor to version 1.1.1, run:: @@ -5,6 +39,9 @@ Version 0.10.1 $ pip install --upgrade -r requirements/production.txt to upgrade + +* django-compressor is now disabled by default, users must explicitly + enable it adding COMPRESS_ENABLED=True to their settings_local.py file Version 0.10 diff --git a/docs/conf.py b/docs/conf.py index 2b9ba3a5ff..cb26e3e359 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -48,10 +48,10 @@ copyright = u'2011, Roberto Rosario' # built documents. # # The short X.Y version. -version = '0.10.1' +version = '0.11' # The full version, including alpha/beta/rc tags. -release = '0.10' +release = '0.11' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/contributors.rst b/docs/contributors.rst index f6179efb07..cae85f3c13 100644 --- a/docs/contributors.rst +++ b/docs/contributors.rst @@ -12,6 +12,9 @@ You can help further the development of **Mayan EDMS** by reporting bugs, submit Bug fixes --------- * Aziz M. Bookwala (https://github.com/azizmb) +* IHLeanne (https://github.com/IHLeanne) +* Сергей Глита [Sergey Glita] (s.v.glita@gmail.com) +* Meurig Freeman (https://github.com/meurig) Bug reports ----------- @@ -21,6 +24,7 @@ Bug reports * Brian Huxley * dAnjou (https://github.com/dAnjou) * Сергей Глита [Sergey Glita] (s.v.glita@gmail.com) +* IHLeanne (https://github.com/IHLeanne) Patches ------- diff --git a/docs/credits.rst b/docs/credits.rst index d56f2c0d93..e0b3e815d6 100644 --- a/docs/credits.rst +++ b/docs/credits.rst @@ -112,3 +112,13 @@ Credits * http://pypi.python.org/pypi/django-taggit * Image 392336_7079 (stock exchange) + +* djangorestframework + +* South + +* python-gnupg + +* python-hkp + + diff --git a/docs/features.rst b/docs/features.rst index 75360e15f6..e272d75e40 100644 --- a/docs/features.rst +++ b/docs/features.rst @@ -2,6 +2,23 @@ Features ======== + +* Document versioning. + + * Store many versions of the same document, download or revert to a previous version. + +* Electronic signature verification. + + * Check the authenticity of documents by verifying their embedded cryptographic signatures or upload detached signatures for document signed after they were stored. + +* Collaboration tools. + + * Discuss documents, comment on new version of a document. + +* Office document format support. + + * Word processing files? Spreadsheets? Sresentations? They are supported too. + * User defined metadata fields and meta data sets. * Metadata fields can be grouped into sets per technical, legal or structural requirements such as the `Dublin core`_ @@ -27,18 +44,28 @@ Features * Local file or server side file uploads. * Batch upload many documents with the same metadata. + + * Clone a document's metadata for speedier uploads and eliminate repetitive data entry. + * Previews for a great deal of image formats, including PDF. * **Mayan EDMS** provides different file conversion backends with different levels of functionality and requirements to adapt to different deployment environments. * Full text searching. + + * Document can be searched by their text content, their metadata or any other file attribute such as name, extension, etc. + * Configurable document grouping. * Automatic linking of documents based on metadata values or document properties. -* Permissions and roles support. +* Roles support. - * User can created many different roles and are not limited to the traditional limited admin, operator, guest paradigm. + * Users can created an unlimited amount of different roles and are not restricted to the traditional admin, operator, guest paradigm. + +* Fine grained permissions system. + + * There is a permission for every atomic operation performed by users. * Multi page document support. @@ -55,7 +82,19 @@ Features .. _Django: https://www.djangoproject.com/ * Multilingual OCR support. + + * *As supported by the OCR engine tesseract. + * Duplicated document search. + * Plugable storage backends (File based and GridFS included). + + * Very easy to convert other 3rd party such as the ones available for Amazon EC2. + * Color coded tagging. + + * Labeled and color coded tags that are intituitive. + * Staging folders to receive scanned documents directly from network attached scanners. + + * Preview scanned files even before uploading them. diff --git a/docs/index.rst b/docs/index.rst index 2f2f9d96fd..5c115df121 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -43,7 +43,6 @@ Contents settings updates development - technical contributors credits faq diff --git a/docs/requirements.rst b/docs/requirements.rst index 1ba97459bc..665b9ba641 100644 --- a/docs/requirements.rst +++ b/docs/requirements.rst @@ -23,6 +23,7 @@ Executables: * ``tesseract-ocr`` - An OCR Engine that was developed at HP Labs between 1985 and 1995... and now at Google. * ``unpaper`` - post-processing scanned and photocopied book pages +* ``gpg`` - The GNU Privacy Guard Optional requirements ===================== diff --git a/docs/settings.rst b/docs/settings.rst index 0f95b103d4..323bf84405 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -88,6 +88,8 @@ Documents Default: ``image_cache`` (relative to the installation path) + The path where the visual representations of the documents are stored for fast display. + Converter --------- @@ -128,19 +130,32 @@ Converter Graphics conversion backend to use. Options are: ``converter.backends.imagemagick``, ``converter.backends.graphicsmagick`` and ``converter.backends.python``. + Suggested options: ``-limit files 1 -limit memory 1GB -limit map 2GB -density 200`` + .. data:: CONVERTER_UNOCONV_PATH Default: ``/usr/bin/unoconv`` + Path to the unoconv program. -Grouping ---------- - -.. data:: GROUPING_SHOW_EMPTY_GROUPS + +.. data:: CONVERTER_UNOCONV_USE_PIPE Default: ``True`` + Use alternate method of connection to LibreOffice using a pipe, it is slower but less prone to segmentation faults. + + +Linking +------- + +.. data:: LINKING_SHOW_EMPTY_SMART_LINKS + + Default: ``True`` + + Show smart links even when they don't return any documents. + Storage ------- @@ -224,7 +239,7 @@ OCR .. data:: OCR_REPLICATION_DELAY - Default: ``10`` + Default: ``0`` Amount of seconds to delay OCR of documents to allow for the node's storage replication overhead. @@ -384,3 +399,13 @@ User management Default: ``[]`` A list of existing roles that are automatically assigned to newly created users + + +Signatures +---------- + +.. data:: SIGNATURES_KEYSERVERS + + Default: ``['pool.sks-keyservers.net']`` + + List of keyservers to be queried for unknown keys. diff --git a/misc/compilemessages_all.sh b/misc/compilemessages_all.sh index 4ad10e85ae..5e3c5cf2a7 100755 --- a/misc/compilemessages_all.sh +++ b/misc/compilemessages_all.sh @@ -107,3 +107,8 @@ cd $BASE/apps/web_theme $COMPILEMESSAGES -l pt $COMPILEMESSAGES -l ru $COMPILEMESSAGES -l es + +cd $BASE/apps/django_gpg +$COMPILEMESSAGES -l pt +$COMPILEMESSAGES -l ru +$COMPILEMESSAGES -l es diff --git a/misc/makemessages_all.sh b/misc/makemessages_all.sh index 79a1354cee..d1e0fcd79e 100755 --- a/misc/makemessages_all.sh +++ b/misc/makemessages_all.sh @@ -128,3 +128,9 @@ $MAKEMESSAGES -l en $MAKEMESSAGES -l pt $MAKEMESSAGES -l ru $MAKEMESSAGES -l es + +cd $BASE/apps/django_gpg +$MAKEMESSAGES -l en +$MAKEMESSAGES -l pt +$MAKEMESSAGES -l ru +$MAKEMESSAGES -l es diff --git a/requirements/development.txt b/requirements/development.txt index 251f11e5e0..67436315af 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -19,3 +19,6 @@ cssmin==0.1.4 django-compressor==1.1.1 -e git://github.com/rosarior/django-sendfile.git#egg=django-sendfile djangorestframework==0.2.3 +South==0.7.3 +python-gnupg==0.2.8 +python-hkp==0.1.3 diff --git a/requirements/production.txt b/requirements/production.txt index c9b455a807..cc3232892b 100644 --- a/requirements/production.txt +++ b/requirements/production.txt @@ -16,3 +16,6 @@ cssmin==0.1.4 django-compressor==1.1.1 -e git://github.com/rosarior/django-sendfile.git#egg=django-sendfile djangorestframework==0.2.3 +South==0.7.3 +python-gnupg==0.2.8 +python-hkp==0.1.3 diff --git a/settings.py b/settings.py index 60d7446eba..bd59969f15 100644 --- a/settings.py +++ b/settings.py @@ -133,7 +133,7 @@ INSTALLED_APPS = ( 'lock_manager', 'web_theme', 'common', - 'metadata', + 'django_gpg', 'pagination', 'dynamic_search', 'filetransfers', @@ -151,6 +151,7 @@ INSTALLED_APPS = ( 'tags', 'document_comments', 'user_management', + 'metadata', 'documents', 'linking', 'mptt', @@ -165,6 +166,7 @@ INSTALLED_APPS = ( 'compressor', 'djangorestframework', 'rest_api', + 'south', ) TEMPLATE_CONTEXT_PROCESSORS = ( diff --git a/static/CACHE/empty.txt b/static/CACHE/empty.txt deleted file mode 100644 index 6068d6a5eb..0000000000 --- a/static/CACHE/empty.txt +++ /dev/null @@ -1 +0,0 @@ -empty file diff --git a/urls.py b/urls.py index 38ab8788b2..a4ccd38cef 100644 --- a/urls.py +++ b/urls.py @@ -28,6 +28,7 @@ urlpatterns = patterns('', (r'^project_setup/', include('project_setup.urls')), (r'^project_tools/', include('project_tools.urls')), (r'^api/', include('rest_api.urls')), + (r'^signatures/', include('django_gpg.urls')), )