diff --git a/apps/document_signatures/__init__.py b/apps/document_signatures/__init__.py index e4e03435e1..8b13789179 100644 --- a/apps/document_signatures/__init__.py +++ b/apps/document_signatures/__init__.py @@ -1,85 +1 @@ -from __future__ import absolute_import -import logging - -try: - from cStringIO import StringIO -except ImportError: - from StringIO import StringIO - -#from django.db.models.signals import post_save -#from django.dispatch import receiver - -from documents.models import Document, DocumentVersion -from navigation.api import bind_links -from django_gpg.runtime import gpg -from django_gpg.exceptions import GPGDecryptionError -from acls.api import class_permissions - -from .models import DocumentVersionSignature -from .permissions import ( - PERMISSION_DOCUMENT_VERIFY, PERMISSION_SIGNATURE_UPLOAD, - PERMISSION_SIGNATURE_DOWNLOAD, PERMISSION_SIGNATURE_DELETE -) -from .links import (document_signature_upload, document_signature_download, - document_signature_delete, document_verify) - -logger = logging.getLogger(__name__) - - -def document_pre_open_hook(descriptor, instance): - if DocumentVersionSignature.objects.has_embedded_signature(instance.document): - # If it has an embedded signature decrypt - try: - result = gpg.decrypt_file(descriptor, close_descriptor=False) - # gpg return a string, turn it into a file like object - except GPGDecryptionError: - # At least return the original raw content - descriptor.seek(0) - return descriptor - else: - descriptor.close() - return StringIO(result.data) - else: - # It no embedded signature pass along - # Doing this single DB lookup avoids trying to decrypt non signed - # files always, which could result in slow down for big non signed - # files - #descriptor.seek(0) - return descriptor - - #try: - # result = gpg.decrypt_file(descriptor, close_descriptor=False) - # # gpg return a string, turn it into a file like object - #except GPGDecryptionError: - # # At least return the original raw content - # descriptor.seek(0) - # return descriptor - #else: - # descriptor.close() - # return StringIO(result.data) - - -def document_post_save_hook(instance): - if not instance.pk: - document_signature, created = DocumentVersionSignature.objects.get_or_create( - document_version=instance.latest_version, - ) - #DocumentVersionSignature.objects.update_signed_state(instance.document) - -#@receiver(post_save, dispatch_uid='check_document_signature_state', sender=DocumentVersion) -#def check_document_signature_state(sender, instance, **kwargs): -# if kwargs.get('created', False): -# DocumentVersionSignature.objects.signature_state(instance.document) - - -bind_links([Document], [document_verify], menu_name='form_header') -bind_links(['document_verify', 'document_signature_upload', 'document_signature_download', 'document_signature_delete'], [document_signature_upload, document_signature_download, document_signature_delete], menu_name='sidebar') - -DocumentVersion.register_pre_open_hook(1, document_pre_open_hook) -DocumentVersion.register_post_save_hook(1, document_post_save_hook) - -class_permissions(Document, [ - PERMISSION_DOCUMENT_VERIFY, PERMISSION_SIGNATURE_UPLOAD, - PERMISSION_SIGNATURE_DOWNLOAD, PERMISSION_SIGNATURE_DELETE -]) diff --git a/apps/document_signatures/forms.py b/apps/document_signatures/forms.py index 7e3f568b1b..a1bc6871ec 100644 --- a/apps/document_signatures/forms.py +++ b/apps/document_signatures/forms.py @@ -1,9 +1,5 @@ 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 DetachedSignatureForm(forms.Form): diff --git a/apps/document_signatures/icons.py b/apps/document_signatures/icons.py new file mode 100644 index 0000000000..567c913947 --- /dev/null +++ b/apps/document_signatures/icons.py @@ -0,0 +1,9 @@ +from __future__ import absolute_import + +from icons.literals import PENCIL_ADD, DISK, PENCIL_DELETE, TEXT_SIGNATURE +from icons import Icon + +icon_document_signature_upload = Icon(PENCIL_ADD) +icon_document_signature_download = Icon(DISK) +icon_document_signature_delete = Icon(PENCIL_DELETE) +icon_document_verify = Icon(TEXT_SIGNATURE) diff --git a/apps/document_signatures/links.py b/apps/document_signatures/links.py index 5fd7ad0911..7647b5b42f 100644 --- a/apps/document_signatures/links.py +++ b/apps/document_signatures/links.py @@ -9,6 +9,8 @@ from .permissions import ( PERMISSION_DOCUMENT_VERIFY, PERMISSION_SIGNATURE_UPLOAD, PERMISSION_SIGNATURE_DOWNLOAD, PERMISSION_SIGNATURE_DELETE ) +from .icons import (icon_document_signature_upload, icon_document_signature_download, + icon_document_signature_delete, icon_document_verify) def has_embedded_signature(context): @@ -18,7 +20,8 @@ def has_embedded_signature(context): def doesnt_have_detached_signature(context): return DocumentVersionSignature.objects.has_detached_signature(context['object']) == False -document_signature_upload = Link(text=_(u'upload signature'), view='document_signature_upload', args='object.pk', sprite='pencil_add', permissions=[PERMISSION_SIGNATURE_UPLOAD], conditional_disable=has_embedded_signature) -document_signature_download = Link(text=_(u'download signature'), view='document_signature_download', args='object.pk', sprite='disk', permissions=[PERMISSION_SIGNATURE_DOWNLOAD], conditional_disable=doesnt_have_detached_signature) -document_signature_delete = Link(text=_(u'delete signature'), view='document_signature_delete', args='object.pk', sprite='pencil_delete', permissions=[PERMISSION_SIGNATURE_DELETE], conditional_disable=doesnt_have_detached_signature) -document_verify = Link(text=_(u'signatures'), view='document_verify', args='object.pk', sprite='text_signature', permissions=[PERMISSION_DOCUMENT_VERIFY]) + +document_signature_upload = Link(text=_(u'upload signature'), view='document_signature_upload', args='object.pk', icon=icon_document_signature_upload, permissions=[PERMISSION_SIGNATURE_UPLOAD], conditional_disable=has_embedded_signature) +document_signature_download = Link(text=_(u'download signature'), view='document_signature_download', args='object.pk', icon=icon_document_signature_download, permissions=[PERMISSION_SIGNATURE_DOWNLOAD], conditional_disable=doesnt_have_detached_signature) +document_signature_delete = Link(text=_(u'delete signature'), view='document_signature_delete', args='object.pk', icon=icon_document_signature_delete, permissions=[PERMISSION_SIGNATURE_DELETE], conditional_disable=doesnt_have_detached_signature) +document_verify = Link(text=_(u'signatures'), view='document_verify', args='object.pk', icon=icon_document_verify, permissions=[PERMISSION_DOCUMENT_VERIFY]) diff --git a/apps/document_signatures/managers.py b/apps/document_signatures/managers.py index 1052d6d988..497679aa92 100644 --- a/apps/document_signatures/managers.py +++ b/apps/document_signatures/managers.py @@ -2,7 +2,6 @@ import logging from django.db import models -from django_gpg.runtime import gpg from django_gpg.exceptions import GPGVerificationError logger = logging.getLogger(__name__) @@ -52,6 +51,8 @@ class DocumentVersionSignatureManager(models.Manager): return document_signature.signature_file.storage.open(document_signature.signature_file.path) def verify_signature(self, document): + from django_gpg.runtime import gpg + document_descriptor = document.open(raw=True) detached_signature = None if self.has_detached_signature(document): diff --git a/apps/document_signatures/models.py b/apps/document_signatures/models.py index 970826e709..1f007310f4 100644 --- a/apps/document_signatures/models.py +++ b/apps/document_signatures/models.py @@ -5,8 +5,6 @@ from django.db import models from django.utils.translation import ugettext_lazy as _ from documents.models import DocumentVersion, get_filename_from_uuid -from documents.conf.settings import STORAGE_BACKEND -from django_gpg.runtime import gpg from .managers import DocumentVersionSignatureManager @@ -18,7 +16,7 @@ class DocumentVersionSignature(models.Model): Model that describes a document version signature properties """ document_version = models.ForeignKey(DocumentVersion, verbose_name=_(u'document version'), 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) + signature_file = models.FileField(blank=True, null=True, upload_to=get_filename_from_uuid, verbose_name=_(u'signature file'), editable=False) has_embedded_signature = models.BooleanField(default=False, verbose_name=_(u'has embedded signature'), editable=False) objects = DocumentVersionSignatureManager() @@ -27,6 +25,8 @@ class DocumentVersionSignature(models.Model): self.signature_file.storage.delete(self.signature_file.path) def save(self, *args, **kwargs): + from django_gpg.runtime import gpg + if not self.pk: descriptor = self.document_version.open(raw=True) self.has_embedded_signature = gpg.has_embedded_signature(descriptor) diff --git a/apps/document_signatures/post_init.py b/apps/document_signatures/post_init.py new file mode 100644 index 0000000000..404363cf57 --- /dev/null +++ b/apps/document_signatures/post_init.py @@ -0,0 +1,89 @@ +from __future__ import absolute_import + +import logging + +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO + +#from django.db.models.signals import post_save +#from django.dispatch import receiver + +from documents.models import Document, DocumentVersion +from navigation.api import bind_links +from django_gpg.runtime import gpg +from django_gpg.exceptions import GPGDecryptionError +from acls.api import class_permissions +from documents.settings import STORAGE_BACKEND + +from .models import DocumentVersionSignature +from .permissions import ( + PERMISSION_DOCUMENT_VERIFY, PERMISSION_SIGNATURE_UPLOAD, + PERMISSION_SIGNATURE_DOWNLOAD, PERMISSION_SIGNATURE_DELETE +) +from .links import (document_signature_upload, document_signature_download, + document_signature_delete, document_verify) + +logger = logging.getLogger(__name__) + + +def document_pre_open_hook(descriptor, instance): + if DocumentVersionSignature.objects.has_embedded_signature(instance.document): + # If it has an embedded signature decrypt + try: + result = gpg.decrypt_file(descriptor, close_descriptor=False) + # gpg return a string, turn it into a file like object + except GPGDecryptionError: + # At least return the original raw content + descriptor.seek(0) + return descriptor + else: + descriptor.close() + return StringIO(result.data) + else: + # It no embedded signature pass along + # Doing this single DB lookup avoids trying to decrypt non signed + # files always, which could result in slow down for big non signed + # files + #descriptor.seek(0) + return descriptor + + #try: + # result = gpg.decrypt_file(descriptor, close_descriptor=False) + # # gpg return a string, turn it into a file like object + #except GPGDecryptionError: + # # At least return the original raw content + # descriptor.seek(0) + # return descriptor + #else: + # descriptor.close() + # return StringIO(result.data) + + +def document_post_save_hook(instance): + if not instance.pk: + document_signature, created = DocumentVersionSignature.objects.get_or_create( + document_version=instance.latest_version, + ) + #DocumentVersionSignature.objects.update_signed_state(instance.document) + +#@receiver(post_save, dispatch_uid='check_document_signature_state', sender=DocumentVersion) +#def check_document_signature_state(sender, instance, **kwargs): +# if kwargs.get('created', False): +# DocumentVersionSignature.objects.signature_state(instance.document) + +def init_app(): + bind_links([Document], [document_verify], menu_name='form_header') + bind_links(['document_verify', 'document_signature_upload', 'document_signature_download', 'document_signature_delete'], [document_signature_upload, document_signature_download, document_signature_delete], menu_name='sidebar') + + DocumentVersion.register_pre_open_hook(1, document_pre_open_hook) + DocumentVersion.register_post_save_hook(1, document_post_save_hook) + + class_permissions(Document, [ + PERMISSION_DOCUMENT_VERIFY, PERMISSION_SIGNATURE_UPLOAD, + PERMISSION_SIGNATURE_DOWNLOAD, PERMISSION_SIGNATURE_DELETE + ]) + + DocumentVersionSignature._meta.get_field('signature_file').storage=STORAGE_BACKEND() + diff --git a/apps/document_signatures/registry.py b/apps/document_signatures/registry.py new file mode 100644 index 0000000000..8a69cbea52 --- /dev/null +++ b/apps/document_signatures/registry.py @@ -0,0 +1,10 @@ +from __future__ import absolute_import + +from django.utils.translation import ugettext_lazy as _ + +from .icons import icon_document_verify + +label = _(u'Document signatures') +description = _(u'Handles document signatures.') +dependencies = ['app_registry', 'icons', 'navigation', 'django_gpg', 'documents', 'permissions'] +icon = icon_document_verify diff --git a/apps/document_signatures/views.py b/apps/document_signatures/views.py index 2912e8a6c1..f2ebbfd4a5 100644 --- a/apps/document_signatures/views.py +++ b/apps/document_signatures/views.py @@ -20,7 +20,7 @@ from acls.models import AccessEntry from django_gpg.api import SIGNATURE_STATES -from . import (PERMISSION_DOCUMENT_VERIFY, PERMISSION_SIGNATURE_UPLOAD, +from .permissions import (PERMISSION_DOCUMENT_VERIFY, PERMISSION_SIGNATURE_UPLOAD, PERMISSION_SIGNATURE_DOWNLOAD, PERMISSION_SIGNATURE_DELETE) from .forms import DetachedSignatureForm from .models import DocumentVersionSignature @@ -42,10 +42,9 @@ def document_verify(request, document_pk): 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), + _(u'Signature status: %(icon)s %(text)s') % { + 'icon': signature_state['icon'].display_big(), 'text': signature_state['text'] }, ] diff --git a/apps/icons/classes.py b/apps/icons/classes.py index 2a9b1c0a28..e3efeb1139 100644 --- a/apps/icons/classes.py +++ b/apps/icons/classes.py @@ -21,7 +21,7 @@ class Icon(object): return IconSetBase.get_by_name(self.icon_set or ICON_SET).get_url(self, size) def display(self, size): # TODO: move to widgets? - return mark_safe('' % (settings.STATIC_URL, self.get_url(size))) + return mark_safe('' % (settings.STATIC_URL, self.get_url(size))) def display_small(self): return self.display(SIZE_SMALL) diff --git a/apps/icons/iconsets/fat_cow.py b/apps/icons/iconsets/fat_cow.py index c68b2d077e..3227a6e3f4 100644 --- a/apps/icons/iconsets/fat_cow.py +++ b/apps/icons/iconsets/fat_cow.py @@ -39,6 +39,7 @@ class IconSet(IconSetBase): DATABASE_EDIT: 'database_edit.png', DATABASE_DELETE: 'database_delete.png', DELETE: 'delete.png', + DISK: 'disk.png', DRAW_AIRBRUSH: 'draw_airbrush.png', DOCUMENT_SIGNATURE: 'document_signature.png', ERROR: 'error.png', @@ -99,6 +100,8 @@ class IconSet(IconSetBase): PAGE_WHITE_PICTURE: 'page_white_picture.png', PAGE_WHITE_TEXT: 'page_white_text.png', PAGE_WORLD: 'page_world.png', + PENCIL_ADD: 'pencil_add.png', + PENCIL_DELETE: 'pencil_delete.png', PICTURES: 'pictures.png', PILL: 'pill.png', PLUGIN: 'plugin.png', @@ -123,6 +126,7 @@ class IconSet(IconSetBase): TAG_BLUE_DELETE: 'tag_blue_delete.png', TAG_BLUE_EDIT: 'tag_blue_edit.png', TEXT_DROPCAPS: 'text_dropcaps.png', + TEXT_SIGNATURE: 'text_signature.png', TEXT_STRIKETHROUGH: 'text_strikethrough.png', THEATER: 'theater.png', TICK: 'tick.png', @@ -131,7 +135,8 @@ class IconSet(IconSetBase): USER: 'user.png', USER_ADD:'user_add.png', USER_EDIT: 'user_edit.png', - USER_DELETE: 'user_delete.png', + USER_DELETE: 'user_delete.png', + USER_SILHOUETTE: 'user_silhouette.png', VCARD: 'vcard.png', VCARD_EDIT: 'vcard_edit.png', WRENCH: 'wrench.png', diff --git a/apps/icons/literals.py b/apps/icons/literals.py index f54d313263..24fd50519f 100644 --- a/apps/icons/literals.py +++ b/apps/icons/literals.py @@ -33,6 +33,7 @@ DATABASE_ADD = 'database_add' DATABASE_EDIT = 'database_edit' DATABASE_DELETE = 'database_delete' DELETE = 'delete' +DISK = 'disk' DRAW_AIRBRUSH = 'draw_airbrush' DOCUMENT_SIGNATURE = 'document_signature' ERROR = 'error' @@ -95,6 +96,8 @@ PAGE_WHITE_EDIT = 'page_white_edit' PAGE_WHITE_PICTURE = 'page_white_picture' PAGE_WHITE_TEXT = 'page_white_text' PAGE_WORLD = 'page_world' +PENCIL_ADD = 'pencil_add' +PENCIL_DELETE = 'pencil_delete' PICTURES = 'pictures' PILL = 'pill' PLUGIN = 'plugin' @@ -119,6 +122,7 @@ TAG_BLUE_ADD = 'tag_blue_add' TAG_BLUE_DELETE = 'tag_blue_delete' TAG_BLUE_EDIT = 'tag_blue_edit' TEXT_DROPCAPS = 'text_dropcaps' +TEXT_SIGNATURE = 'text_signature' TEXT_STRIKETHROUGH = 'text_strikethrough' THEATER = 'theater' TICK = 'tick' @@ -128,6 +132,7 @@ USER = 'user' USER_ADD = 'user_add' USER_EDIT = 'user_edit' USER_DELETE = 'user_delete' +USER_SILHOUETTE = 'user_silhouette' VCARD = 'vcard' VCARD_EDIT = 'vcard_edit' WRENCH = 'wrench' diff --git a/settings.py b/settings.py index 6ac0ff15aa..acd8f3d1e6 100644 --- a/settings.py +++ b/settings.py @@ -175,7 +175,7 @@ INSTALLED_APPS = ( 'folders', 'dynamic_search', 'document_comments', - #'document_signatures', + 'document_signatures', 'linking', 'metadata', 'ocr', diff --git a/urls.py b/urls.py index c7e841be89..9f2a7e9cbe 100644 --- a/urls.py +++ b/urls.py @@ -29,7 +29,7 @@ urlpatterns = patterns('', #(r'^document_acls/', include('document_acls.urls')), (r'^api/', include('rest_api.urls')), (r'^gpg/', include('django_gpg.urls')), - #(r'^documents/signatures/', include('document_signatures.urls')), + (r'^documents/signatures/', include('document_signatures.urls')), #(r'^mailer/', include('mailer.urls')), #(r'^workflows/', include('workflows.urls')), #(r'^checkouts/', include('checkouts.urls')),