Merge branch 'feature/document_signature_app' into feature/ACL_support_w_permission_refactor
Conflicts: apps/django_gpg/__init__.py apps/django_gpg/views.py apps/tags/__init__.py
This commit is contained in:
@@ -176,3 +176,11 @@ 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
|
||||
trans.it = apps/django_gpg/locale/it/LC_MESSAGES/django.po
|
||||
|
||||
[mayan-edms.apps-document_signatures]
|
||||
source_file = apps/document_signatures/locale/en/LC_MESSAGES/django.po
|
||||
source_lang = en
|
||||
trans.es = apps/document_signatures/locale/es/LC_MESSAGES/django.po
|
||||
trans.pt = apps/document_signatures/locale/pt/LC_MESSAGES/django.po
|
||||
trans.ru = apps/document_signatures/locale/ru/LC_MESSAGES/django.po
|
||||
trans.it = apps/document_signatures/locale/it/LC_MESSAGES/django.po
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
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
|
||||
@@ -11,21 +10,12 @@ from hkp import Key as KeyServerKey
|
||||
|
||||
from django_gpg.api import Key
|
||||
|
||||
django_gpg_namespace = PermissionNamespace('django_gpg', _(u'Signatures'))
|
||||
django_gpg_namespace = PermissionNamespace('django_gpg', _(u'Key management'))
|
||||
|
||||
PERMISSION_DOCUMENT_VERIFY = Permission.objects.register(django_gpg_namespace, 'document_verify', _(u'Verify document signatures'))
|
||||
PERMISSION_KEY_VIEW = Permission.objects.register(django_gpg_namespace, 'key_view', _(u'View keys'))
|
||||
PERMISSION_KEY_DELETE = Permission.objects.register(django_gpg_namespace, 'key_delete', _(u'Delete keys'))
|
||||
PERMISSION_KEYSERVER_QUERY = Permission.objects.register(django_gpg_namespace, 'keyserver_query', _(u'Query keyservers'))
|
||||
PERMISSION_KEY_RECEIVE = Permission.objects.register(django_gpg_namespace, 'key_receive', _(u'Import keys from keyservers'))
|
||||
PERMISSION_SIGNATURE_UPLOAD = Permission.objects.register(django_gpg_namespace, 'signature_upload', _(u'Upload detached signatures'))
|
||||
PERMISSION_SIGNATURE_DOWNLOAD = Permission.objects.register(django_gpg_namespace, 'signature_download', _(u'Download detached signatures'))
|
||||
|
||||
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]}
|
||||
@@ -33,17 +23,8 @@ public_keys = {'text': _(u'public keys'), 'view': 'key_public_list', 'args': 'ob
|
||||
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')
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import types
|
||||
from StringIO import StringIO
|
||||
|
||||
from pickle import dumps
|
||||
import logging
|
||||
import tempfile
|
||||
@@ -151,6 +151,17 @@ class Key(object):
|
||||
|
||||
|
||||
class GPG(object):
|
||||
@staticmethod
|
||||
def get_descriptor(file_input):
|
||||
try:
|
||||
# Is it a file like object?
|
||||
file_input.seek(0)
|
||||
except AttributeError:
|
||||
# If not, try open it.
|
||||
return open(file_input, 'rb')
|
||||
else:
|
||||
return file_input
|
||||
|
||||
def __init__(self, binary_path=None, home=None, keyring=None, keyservers=None):
|
||||
kwargs = {}
|
||||
if binary_path:
|
||||
@@ -167,52 +178,46 @@ class GPG(object):
|
||||
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')
|
||||
logger.debug('file_input type: %s' % type(file_input))
|
||||
|
||||
input_descriptor = GPG.get_descriptor(file_input)
|
||||
|
||||
try:
|
||||
verify = self.verify_file(input_descriptor, detached_signature)
|
||||
verify = self.verify_file(input_descriptor, detached_signature, close_descriptor=False)
|
||||
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)
|
||||
return self.verify_w_retry(input_descriptor, detached_signature)
|
||||
except KeyFetchingError:
|
||||
return verify
|
||||
else:
|
||||
input_descriptor.close()
|
||||
return verify
|
||||
except IOError:
|
||||
return False
|
||||
|
||||
def verify_file(self, file_input, detached_signature=None):
|
||||
"""
|
||||
def verify_file(self, file_input, detached_signature=None, close_descriptor=True):
|
||||
'''
|
||||
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')
|
||||
'''
|
||||
|
||||
input_descriptor = GPG.get_descriptor(file_input)
|
||||
|
||||
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_data = input_descriptor.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()
|
||||
verify = self.gpg.verify_file(input_descriptor)
|
||||
|
||||
if close_descriptor:
|
||||
input_descriptor.close()
|
||||
|
||||
if verify:
|
||||
return verify
|
||||
@@ -232,12 +237,13 @@ class GPG(object):
|
||||
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
|
||||
|
||||
@@ -250,14 +256,7 @@ class GPG(object):
|
||||
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')
|
||||
input_descriptor = GPG.get_descriptor(file_input)
|
||||
|
||||
if destination:
|
||||
output_descriptor = open(destination, 'wb')
|
||||
@@ -277,16 +276,13 @@ class GPG(object):
|
||||
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')
|
||||
def decrypt_file(self, file_input, close_descriptor=True):
|
||||
input_descriptor = GPG.get_descriptor(file_input)
|
||||
|
||||
result = self.gpg.decrypt_file(input_descriptor)
|
||||
input_descriptor.close()
|
||||
if close_descriptor:
|
||||
input_descriptor.close()
|
||||
|
||||
if not result.status:
|
||||
raise GPGDecryptionError('Unable to decrypt file')
|
||||
|
||||
|
||||
@@ -11,9 +11,3 @@ class KeySearchForm(forms.Form):
|
||||
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'),
|
||||
)
|
||||
|
||||
@@ -4,9 +4,6 @@ urlpatterns = patterns('django_gpg.views',
|
||||
url(r'^delete/(?P<fingerprint>.+)/(?P<key_type>\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<document_pk>\d+)/$', 'document_verify', (), 'document_verify'),
|
||||
url(r'^upload/signature/(?P<document_pk>\d+)/$', 'document_signature_upload', (), 'document_signature_upload'),
|
||||
url(r'^download/signature/(?P<document_pk>\d+)/$', 'document_signature_download', (), 'document_signature_download'),
|
||||
url(r'^query/$', 'key_query', (), 'key_query'),
|
||||
url(r'^receive/(?P<key_id>.+)/$', 'key_receive', (), 'key_receive'),
|
||||
)
|
||||
|
||||
@@ -11,21 +11,17 @@ 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.models import Permission
|
||||
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,
|
||||
KeyImportError)
|
||||
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
|
||||
from django_gpg import (PERMISSION_KEY_VIEW, PERMISSION_KEY_DELETE,
|
||||
PERMISSION_KEYSERVER_QUERY, PERMISSION_KEY_RECEIVE)
|
||||
from django_gpg.forms import KeySearchForm
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -189,98 +185,3 @@ def key_query(request):
|
||||
return render_to_response('generic_form.html', {
|
||||
'subtemplates_list': subtemplates_list,
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
|
||||
def document_verify(request, document_pk):
|
||||
Permission.objects.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'<img style="vertical-align: middle;" src="%simages/icons/%s" />' % (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):
|
||||
Permission.objects.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):
|
||||
Permission.objects.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'])
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.conf import settings
|
||||
from django.contrib.comments.models import Comment
|
||||
from django.contrib.contenttypes import generic
|
||||
|
||||
from navigation.api import register_links, register_model_list_columns
|
||||
from permissions.models import PermissionNamespace, Permission
|
||||
@@ -41,3 +42,12 @@ register_model_list_columns(Comment, [
|
||||
register_links(['comments_for_object', 'comment_add', 'comment_delete', 'comment_multiple_delete'], [comment_add], menu_name='sidebar')
|
||||
register_links(Comment, [comment_delete])
|
||||
register_links(Document, [comments_for_object], menu_name='form_header')
|
||||
|
||||
Document.add_to_class(
|
||||
'comments',
|
||||
generic.GenericRelation(
|
||||
Comment,
|
||||
content_type_field='content_type',
|
||||
object_id_field='object_pk'
|
||||
)
|
||||
)
|
||||
|
||||
61
apps/document_signatures/__init__.py
Normal file
61
apps/document_signatures/__init__.py
Normal file
@@ -0,0 +1,61 @@
|
||||
from __future__ import absolute_import
|
||||
import logging
|
||||
|
||||
try:
|
||||
from cStringIO import StringIO
|
||||
except ImportError:
|
||||
from StringIO import StringIO
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.db.models.signals import post_save
|
||||
|
||||
from documents.models import Document, DocumentVersion
|
||||
from navigation.api import register_links
|
||||
|
||||
from django_gpg.runtime import gpg
|
||||
from django_gpg.exceptions import GPGDecryptionError
|
||||
|
||||
from .models import DocumentVersionSignature
|
||||
from .permissions import (
|
||||
PERMISSION_DOCUMENT_VERIFY,
|
||||
PERMISSION_SIGNATURE_UPLOAD,
|
||||
PERMISSION_SIGNATURE_DOWNLOAD
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def has_embedded_signature(context):
|
||||
return DocumentVersionSignature.objects.has_embedded_signature(context['object'])
|
||||
|
||||
|
||||
def doesnt_have_detached_signature(context):
|
||||
return DocumentVersionSignature.objects.has_detached_signature(context['object']) == False
|
||||
|
||||
|
||||
def document_pre_open_hook(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:
|
||||
return StringIO(result.data)
|
||||
|
||||
|
||||
def document_post_save(sender, instance, **kwargs):
|
||||
if kwargs.get('created', False):
|
||||
DocumentVersionSignature.objects.signature_state(instance.document)
|
||||
|
||||
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}
|
||||
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')
|
||||
|
||||
DocumentVersion.register_pre_open_hook(1, document_pre_open_hook)
|
||||
|
||||
post_save.connect(document_post_save, sender=DocumentVersion)
|
||||
12
apps/document_signatures/forms.py
Normal file
12
apps/document_signatures/forms.py
Normal file
@@ -0,0 +1,12 @@
|
||||
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):
|
||||
file = forms.FileField(
|
||||
label=_(u'Signature file'),
|
||||
)
|
||||
122
apps/document_signatures/locale/en/LC_MESSAGES/django.po
Normal file
122
apps/document_signatures/locale/en/LC_MESSAGES/django.po
Normal file
@@ -0,0 +1,122 @@
|
||||
# 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 <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2012-01-01 20:14-0400\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: __init__.py:50
|
||||
msgid "upload signature"
|
||||
msgstr ""
|
||||
|
||||
#: __init__.py:51
|
||||
msgid "download signature"
|
||||
msgstr ""
|
||||
|
||||
#: __init__.py:52
|
||||
msgid "signatures"
|
||||
msgstr ""
|
||||
|
||||
#: forms.py:11
|
||||
msgid "Signature file"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:19
|
||||
msgid "document version"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:20
|
||||
msgid "signature state"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:21
|
||||
msgid "signature file"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:26
|
||||
msgid "document version signature"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:27
|
||||
msgid "document version signatures"
|
||||
msgstr ""
|
||||
|
||||
#: permissions.py:7
|
||||
msgid "Verify document signatures"
|
||||
msgstr ""
|
||||
|
||||
#: permissions.py:8
|
||||
msgid "Upload detached signatures"
|
||||
msgstr ""
|
||||
|
||||
#: permissions.py:9
|
||||
msgid "Download detached signatures"
|
||||
msgstr ""
|
||||
|
||||
#: permissions.py:12
|
||||
msgid "Document signatures"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:47
|
||||
#, python-format
|
||||
msgid "Signature status: %(widget)s %(text)s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:54
|
||||
msgid "embedded"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:56
|
||||
msgid "detached"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:61
|
||||
#, python-format
|
||||
msgid "Signature ID: %s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:62
|
||||
#, python-format
|
||||
msgid "Signature type: %s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:63
|
||||
#, python-format
|
||||
msgid "Key ID: %s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:64
|
||||
#, python-format
|
||||
msgid "Timestamp: %s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:65
|
||||
#, python-format
|
||||
msgid "Signee: %s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:70
|
||||
#, python-format
|
||||
msgid "signature properties for: %s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:92
|
||||
msgid "Detached signature uploaded successfully."
|
||||
msgstr ""
|
||||
|
||||
#: views.py:101
|
||||
#, python-format
|
||||
msgid "Upload detached signature for: %s"
|
||||
msgstr ""
|
||||
123
apps/document_signatures/locale/es/LC_MESSAGES/django.po
Normal file
123
apps/document_signatures/locale/es/LC_MESSAGES/django.po
Normal file
@@ -0,0 +1,123 @@
|
||||
# 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 <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2012-01-01 20:14-0400\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: \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:50
|
||||
msgid "upload signature"
|
||||
msgstr ""
|
||||
|
||||
#: __init__.py:51
|
||||
msgid "download signature"
|
||||
msgstr ""
|
||||
|
||||
#: __init__.py:52
|
||||
msgid "signatures"
|
||||
msgstr ""
|
||||
|
||||
#: forms.py:11
|
||||
msgid "Signature file"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:19
|
||||
msgid "document version"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:20
|
||||
msgid "signature state"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:21
|
||||
msgid "signature file"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:26
|
||||
msgid "document version signature"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:27
|
||||
msgid "document version signatures"
|
||||
msgstr ""
|
||||
|
||||
#: permissions.py:7
|
||||
msgid "Verify document signatures"
|
||||
msgstr ""
|
||||
|
||||
#: permissions.py:8
|
||||
msgid "Upload detached signatures"
|
||||
msgstr ""
|
||||
|
||||
#: permissions.py:9
|
||||
msgid "Download detached signatures"
|
||||
msgstr ""
|
||||
|
||||
#: permissions.py:12
|
||||
msgid "Document signatures"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:47
|
||||
#, python-format
|
||||
msgid "Signature status: %(widget)s %(text)s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:54
|
||||
msgid "embedded"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:56
|
||||
msgid "detached"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:61
|
||||
#, python-format
|
||||
msgid "Signature ID: %s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:62
|
||||
#, python-format
|
||||
msgid "Signature type: %s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:63
|
||||
#, python-format
|
||||
msgid "Key ID: %s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:64
|
||||
#, python-format
|
||||
msgid "Timestamp: %s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:65
|
||||
#, python-format
|
||||
msgid "Signee: %s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:70
|
||||
#, python-format
|
||||
msgid "signature properties for: %s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:92
|
||||
msgid "Detached signature uploaded successfully."
|
||||
msgstr ""
|
||||
|
||||
#: views.py:101
|
||||
#, python-format
|
||||
msgid "Upload detached signature for: %s"
|
||||
msgstr ""
|
||||
123
apps/document_signatures/locale/it/LC_MESSAGES/django.po
Normal file
123
apps/document_signatures/locale/it/LC_MESSAGES/django.po
Normal file
@@ -0,0 +1,123 @@
|
||||
# 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 <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2012-01-01 20:14-0400\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: \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:50
|
||||
msgid "upload signature"
|
||||
msgstr ""
|
||||
|
||||
#: __init__.py:51
|
||||
msgid "download signature"
|
||||
msgstr ""
|
||||
|
||||
#: __init__.py:52
|
||||
msgid "signatures"
|
||||
msgstr ""
|
||||
|
||||
#: forms.py:11
|
||||
msgid "Signature file"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:19
|
||||
msgid "document version"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:20
|
||||
msgid "signature state"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:21
|
||||
msgid "signature file"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:26
|
||||
msgid "document version signature"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:27
|
||||
msgid "document version signatures"
|
||||
msgstr ""
|
||||
|
||||
#: permissions.py:7
|
||||
msgid "Verify document signatures"
|
||||
msgstr ""
|
||||
|
||||
#: permissions.py:8
|
||||
msgid "Upload detached signatures"
|
||||
msgstr ""
|
||||
|
||||
#: permissions.py:9
|
||||
msgid "Download detached signatures"
|
||||
msgstr ""
|
||||
|
||||
#: permissions.py:12
|
||||
msgid "Document signatures"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:47
|
||||
#, python-format
|
||||
msgid "Signature status: %(widget)s %(text)s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:54
|
||||
msgid "embedded"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:56
|
||||
msgid "detached"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:61
|
||||
#, python-format
|
||||
msgid "Signature ID: %s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:62
|
||||
#, python-format
|
||||
msgid "Signature type: %s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:63
|
||||
#, python-format
|
||||
msgid "Key ID: %s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:64
|
||||
#, python-format
|
||||
msgid "Timestamp: %s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:65
|
||||
#, python-format
|
||||
msgid "Signee: %s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:70
|
||||
#, python-format
|
||||
msgid "signature properties for: %s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:92
|
||||
msgid "Detached signature uploaded successfully."
|
||||
msgstr ""
|
||||
|
||||
#: views.py:101
|
||||
#, python-format
|
||||
msgid "Upload detached signature for: %s"
|
||||
msgstr ""
|
||||
123
apps/document_signatures/locale/pt/LC_MESSAGES/django.po
Normal file
123
apps/document_signatures/locale/pt/LC_MESSAGES/django.po
Normal file
@@ -0,0 +1,123 @@
|
||||
# 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 <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2012-01-01 20:14-0400\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: \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:50
|
||||
msgid "upload signature"
|
||||
msgstr ""
|
||||
|
||||
#: __init__.py:51
|
||||
msgid "download signature"
|
||||
msgstr ""
|
||||
|
||||
#: __init__.py:52
|
||||
msgid "signatures"
|
||||
msgstr ""
|
||||
|
||||
#: forms.py:11
|
||||
msgid "Signature file"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:19
|
||||
msgid "document version"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:20
|
||||
msgid "signature state"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:21
|
||||
msgid "signature file"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:26
|
||||
msgid "document version signature"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:27
|
||||
msgid "document version signatures"
|
||||
msgstr ""
|
||||
|
||||
#: permissions.py:7
|
||||
msgid "Verify document signatures"
|
||||
msgstr ""
|
||||
|
||||
#: permissions.py:8
|
||||
msgid "Upload detached signatures"
|
||||
msgstr ""
|
||||
|
||||
#: permissions.py:9
|
||||
msgid "Download detached signatures"
|
||||
msgstr ""
|
||||
|
||||
#: permissions.py:12
|
||||
msgid "Document signatures"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:47
|
||||
#, python-format
|
||||
msgid "Signature status: %(widget)s %(text)s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:54
|
||||
msgid "embedded"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:56
|
||||
msgid "detached"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:61
|
||||
#, python-format
|
||||
msgid "Signature ID: %s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:62
|
||||
#, python-format
|
||||
msgid "Signature type: %s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:63
|
||||
#, python-format
|
||||
msgid "Key ID: %s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:64
|
||||
#, python-format
|
||||
msgid "Timestamp: %s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:65
|
||||
#, python-format
|
||||
msgid "Signee: %s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:70
|
||||
#, python-format
|
||||
msgid "signature properties for: %s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:92
|
||||
msgid "Detached signature uploaded successfully."
|
||||
msgstr ""
|
||||
|
||||
#: views.py:101
|
||||
#, python-format
|
||||
msgid "Upload detached signature for: %s"
|
||||
msgstr ""
|
||||
124
apps/document_signatures/locale/ru/LC_MESSAGES/django.po
Normal file
124
apps/document_signatures/locale/ru/LC_MESSAGES/django.po
Normal file
@@ -0,0 +1,124 @@
|
||||
# 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 <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2012-01-01 20:14-0400\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: \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:50
|
||||
msgid "upload signature"
|
||||
msgstr ""
|
||||
|
||||
#: __init__.py:51
|
||||
msgid "download signature"
|
||||
msgstr ""
|
||||
|
||||
#: __init__.py:52
|
||||
msgid "signatures"
|
||||
msgstr ""
|
||||
|
||||
#: forms.py:11
|
||||
msgid "Signature file"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:19
|
||||
msgid "document version"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:20
|
||||
msgid "signature state"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:21
|
||||
msgid "signature file"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:26
|
||||
msgid "document version signature"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:27
|
||||
msgid "document version signatures"
|
||||
msgstr ""
|
||||
|
||||
#: permissions.py:7
|
||||
msgid "Verify document signatures"
|
||||
msgstr ""
|
||||
|
||||
#: permissions.py:8
|
||||
msgid "Upload detached signatures"
|
||||
msgstr ""
|
||||
|
||||
#: permissions.py:9
|
||||
msgid "Download detached signatures"
|
||||
msgstr ""
|
||||
|
||||
#: permissions.py:12
|
||||
msgid "Document signatures"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:47
|
||||
#, python-format
|
||||
msgid "Signature status: %(widget)s %(text)s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:54
|
||||
msgid "embedded"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:56
|
||||
msgid "detached"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:61
|
||||
#, python-format
|
||||
msgid "Signature ID: %s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:62
|
||||
#, python-format
|
||||
msgid "Signature type: %s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:63
|
||||
#, python-format
|
||||
msgid "Key ID: %s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:64
|
||||
#, python-format
|
||||
msgid "Timestamp: %s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:65
|
||||
#, python-format
|
||||
msgid "Signee: %s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:70
|
||||
#, python-format
|
||||
msgid "signature properties for: %s"
|
||||
msgstr ""
|
||||
|
||||
#: views.py:92
|
||||
msgid "Detached signature uploaded successfully."
|
||||
msgstr ""
|
||||
|
||||
#: views.py:101
|
||||
#, python-format
|
||||
msgid "Upload detached signature for: %s"
|
||||
msgstr ""
|
||||
91
apps/document_signatures/managers.py
Normal file
91
apps/document_signatures/managers.py
Normal file
@@ -0,0 +1,91 @@
|
||||
import logging
|
||||
|
||||
from django.db import models
|
||||
|
||||
from django_gpg.runtime import gpg
|
||||
from django_gpg.exceptions import GPGVerificationError
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DocumentVersionSignatureManager(models.Manager):
|
||||
#def update_signed_state(self, document):
|
||||
# document_signature, created = self.model.get_or_create(
|
||||
# document_version=document.latest_version,
|
||||
# )
|
||||
# if document.exists():
|
||||
# descriptor = document.open()
|
||||
# try:
|
||||
# document_signature.signature_state = gpg.verify_file(descriptor).status
|
||||
# # TODO: give use choice for auto public key fetch?
|
||||
# # OR maybe new config option
|
||||
# except GPGVerificationError:
|
||||
# document_signature.signature_state = None
|
||||
# finally:
|
||||
# document_signature.save()
|
||||
|
||||
def add_detached_signature(self, document, detached_signature):
|
||||
document_signature, created = self.model.objects.get_or_create(
|
||||
document_version=document.latest_version,
|
||||
)
|
||||
if not self.signature_state(document):
|
||||
document_signature.signature_file = detached_signature
|
||||
document_signature.save()
|
||||
else:
|
||||
raise Exception('document already has an embedded signature')
|
||||
|
||||
def has_detached_signature(self, document):
|
||||
document_signature, created = self.model.objects.get_or_create(
|
||||
document_version=document.latest_version,
|
||||
)
|
||||
if document_signature.signature_file:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def has_embedded_signature(self, document):
|
||||
logger.debug('document: %s' % document)
|
||||
|
||||
if self.signature_state(document):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def signature_state(self, document):
|
||||
document_signature, created = self.model.objects.get_or_create(
|
||||
document_version=document.latest_version,
|
||||
)
|
||||
logger.debug('created: %s' % created)
|
||||
if created and document.exists():
|
||||
descriptor = document.open(raw=True)
|
||||
try:
|
||||
document_signature.signature_state = gpg.verify_file(descriptor).status
|
||||
# TODO: give use choice for auto public key fetch?
|
||||
# OR maybe new config option
|
||||
except GPGVerificationError:
|
||||
document_signature.signature_state = None
|
||||
finally:
|
||||
document_signature.save()
|
||||
|
||||
#document_signature.signature_state = self.verify_signature(document).status
|
||||
#document_signature.save()
|
||||
|
||||
return document_signature.signature_state
|
||||
|
||||
def detached_signature(self, document):
|
||||
document_signature, created = self.model.objects.get_or_create(
|
||||
document_version=document.latest_version,
|
||||
)
|
||||
return document_signature.signature_file.storage.open(document_signature.signature_file.path)
|
||||
|
||||
def verify_signature(self, document):
|
||||
if self.has_detached_signature(document):
|
||||
logger.debug('has detached signature')
|
||||
args = (document.open(raw=True), self.detached_signature(document))
|
||||
else:
|
||||
args = (document.open(raw=True),)
|
||||
|
||||
try:
|
||||
return gpg.verify_w_retry(*args)
|
||||
except GPGVerificationError:
|
||||
return None
|
||||
140
apps/document_signatures/migrations/0001_initial.py
Normal file
140
apps/document_signatures/migrations/0001_initial.py
Normal file
@@ -0,0 +1,140 @@
|
||||
# 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 'DocumentVersionSignature'
|
||||
db.create_table('document_signatures_documentversionsignature', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('document_version', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['documents.DocumentVersion'])),
|
||||
('signature_state', self.gf('django.db.models.fields.CharField')(max_length=16, null=True, blank=True)),
|
||||
('signature_file', self.gf('django.db.models.fields.files.FileField')(max_length=100, null=True, blank=True)),
|
||||
))
|
||||
db.send_create_signal('document_signatures', ['DocumentVersionSignature'])
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
|
||||
# Deleting model 'DocumentVersionSignature'
|
||||
db.delete_table('document_signatures_documentversionsignature')
|
||||
|
||||
|
||||
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'})
|
||||
},
|
||||
'document_signatures.documentversionsignature': {
|
||||
'Meta': {'object_name': 'DocumentVersionSignature'},
|
||||
'document_version': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentVersion']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'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'})
|
||||
},
|
||||
'documents.document': {
|
||||
'Meta': {'ordering': "['-date_added']", 'object_name': 'Document'},
|
||||
'date_added': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'document_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "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.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.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', [], {})
|
||||
},
|
||||
'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 = ['document_signatures']
|
||||
@@ -0,0 +1,145 @@
|
||||
# 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_version in orm.DocumentVersion.objects.all():
|
||||
if document_version.signature_state or document_version.signature_file:
|
||||
document_signature = orm.DocumentVersionSignature(
|
||||
document_version=document_version,
|
||||
signature_state=document_version.signature_state,
|
||||
signature_file=document_version.signature_file,
|
||||
)
|
||||
document_signature.save()
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
for document_signature in orm.DocumentVersionSignature.objects.all():
|
||||
try:
|
||||
document_version = orm.DocumentVersion.objects.get(document_version=document_version)
|
||||
except orm.DocumentVersion.DoesNotExists:
|
||||
pass
|
||||
else:
|
||||
document_version.signature_state=document_signature.signature_state
|
||||
document_version.signature_file=document_signature.signature_file
|
||||
document_version.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'})
|
||||
},
|
||||
'document_signatures.documentversionsignature': {
|
||||
'Meta': {'object_name': 'DocumentVersionSignature'},
|
||||
'document_version': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['documents.DocumentVersion']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'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'})
|
||||
},
|
||||
'documents.document': {
|
||||
'Meta': {'ordering': "['-date_added']", 'object_name': 'Document'},
|
||||
'date_added': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'document_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "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.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.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', [], {})
|
||||
},
|
||||
'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 = ['document_signatures']
|
||||
0
apps/document_signatures/migrations/__init__.py
Normal file
0
apps/document_signatures/migrations/__init__.py
Normal file
27
apps/document_signatures/models.py
Normal file
27
apps/document_signatures/models.py
Normal file
@@ -0,0 +1,27 @@
|
||||
from __future__ import absolute_import
|
||||
import logging
|
||||
|
||||
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 .managers import DocumentVersionSignatureManager
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
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_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)
|
||||
|
||||
objects = DocumentVersionSignatureManager()
|
||||
|
||||
class Meta:
|
||||
verbose_name = _(u'document version signature')
|
||||
verbose_name_plural = _(u'document version signatures')
|
||||
15
apps/document_signatures/permissions.py
Normal file
15
apps/document_signatures/permissions.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from permissions.api import register_permission, set_namespace_title
|
||||
|
||||
PERMISSION_DOCUMENT_VERIFY = {'namespace': 'document_signatures', 'name': 'document_verify', 'label': _(u'Verify document signatures')}
|
||||
PERMISSION_SIGNATURE_UPLOAD = {'namespace': 'document_signatures', 'name': 'signature_upload', 'label': _(u'Upload detached signatures')}
|
||||
PERMISSION_SIGNATURE_DOWNLOAD = {'namespace': 'document_signatures', 'name': 'key_receive', 'label': _(u'Download detached signatures')}
|
||||
|
||||
# Permission setup
|
||||
set_namespace_title('document_signatures', _(u'Document signatures'))
|
||||
register_permission(PERMISSION_DOCUMENT_VERIFY)
|
||||
register_permission(PERMISSION_SIGNATURE_UPLOAD)
|
||||
register_permission(PERMISSION_SIGNATURE_DOWNLOAD)
|
||||
16
apps/document_signatures/tests.py
Normal file
16
apps/document_signatures/tests.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
This file demonstrates writing tests using the unittest module. These will pass
|
||||
when you run "manage.py test".
|
||||
|
||||
Replace this with more appropriate tests for your application.
|
||||
"""
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
|
||||
class SimpleTest(TestCase):
|
||||
def test_basic_addition(self):
|
||||
"""
|
||||
Tests that 1 + 1 always equals 2.
|
||||
"""
|
||||
self.assertEqual(1 + 1, 2)
|
||||
7
apps/document_signatures/urls.py
Normal file
7
apps/document_signatures/urls.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from django.conf.urls.defaults import patterns, url
|
||||
|
||||
urlpatterns = patterns('document_signatures.views',
|
||||
url(r'^verify/(?P<document_pk>\d+)/$', 'document_verify', (), 'document_verify'),
|
||||
url(r'^upload/signature/(?P<document_pk>\d+)/$', 'document_signature_upload', (), 'document_signature_upload'),
|
||||
url(r'^download/signature/(?P<document_pk>\d+)/$', 'document_signature_download', (), 'document_signature_download'),
|
||||
)
|
||||
121
apps/document_signatures/views.py
Normal file
121
apps/document_signatures/views.py
Normal file
@@ -0,0 +1,121 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
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.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 filetransfers.api import serve_file
|
||||
|
||||
from django_gpg.api import SIGNATURE_STATES
|
||||
|
||||
from . import (PERMISSION_DOCUMENT_VERIFY, PERMISSION_SIGNATURE_UPLOAD,
|
||||
PERMISSION_SIGNATURE_DOWNLOAD)
|
||||
from .forms import DetachedSignatureForm
|
||||
from .models import DocumentVersionSignature
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
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 = DocumentVersionSignature.objects.verify_signature(document)
|
||||
|
||||
signature_state = SIGNATURE_STATES.get(getattr(signature, 'status', None))
|
||||
|
||||
widget = (u'<img style="vertical-align: middle;" src="%simages/icons/%s" />' % (settings.STATIC_URL, signature_state['icon']))
|
||||
paragraphs = [
|
||||
_(u'Signature status: %(widget)s %(text)s') % {
|
||||
'widget': mark_safe(widget),
|
||||
'text': signature_state['text']
|
||||
},
|
||||
]
|
||||
|
||||
if DocumentVersionSignature.objects.has_embedded_signature(document):
|
||||
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:
|
||||
DocumentVersionSignature.objects.add_detached_signature(document, request.FILES['file'])
|
||||
messages.success(request, _(u'Detached signature uploaded successfully.'))
|
||||
return HttpResponseRedirect(next)
|
||||
except Exception, msg:
|
||||
messages.error(request, msg)
|
||||
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 DocumentVersionSignature.objects.has_detached_signature(document):
|
||||
signature = DocumentVersionSignature.objects.detached_signature(document)
|
||||
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'])
|
||||
@@ -15,11 +15,8 @@ from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ugettext
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.contenttypes import generic
|
||||
from django.contrib.comments.models import Comment
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
from taggit.managers import TaggableManager
|
||||
from dynamic_search.api import register
|
||||
from converter.api import get_page_count
|
||||
from converter.api import get_available_transformations_choices
|
||||
@@ -29,8 +26,6 @@ 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
|
||||
@@ -86,14 +81,6 @@ class Document(models.Model):
|
||||
description = models.TextField(blank=True, null=True, verbose_name=_(u'description'))
|
||||
date_added = models.DateTimeField(verbose_name=_(u'added'), db_index=True, editable=False)
|
||||
|
||||
tags = TaggableManager()
|
||||
|
||||
comments = generic.GenericRelation(
|
||||
Comment,
|
||||
content_type_field='content_type',
|
||||
object_id_field='object_pk'
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def clear_image_cache():
|
||||
for the_file in os.listdir(CACHE_PATH):
|
||||
@@ -288,7 +275,9 @@ class Document(models.Model):
|
||||
return version.save()
|
||||
|
||||
filename = property(_get_filename, _set_filename)
|
||||
|
||||
|
||||
#TODO: remove after migration
|
||||
"""
|
||||
def add_detached_signature(self, *args, **kwargs):
|
||||
return self.latest_version.add_detached_signature(*args, **kwargs)
|
||||
|
||||
@@ -300,12 +289,14 @@ class Document(models.Model):
|
||||
|
||||
def verify_signature(self):
|
||||
return self.latest_version.verify_signature()
|
||||
|
||||
"""
|
||||
|
||||
class DocumentVersion(models.Model):
|
||||
'''
|
||||
Model that describes a document version and its properties
|
||||
'''
|
||||
_pre_open_hooks = {}
|
||||
|
||||
@staticmethod
|
||||
def get_version_update_choices(document_version):
|
||||
return (
|
||||
@@ -314,6 +305,10 @@ class DocumentVersion(models.Model):
|
||||
(VERSION_UPDATE_MICRO, _(u'Micro %(major)i.%(minor)i.%(micro)i, (fixes)') % document_version.get_new_version_dict(VERSION_UPDATE_MICRO))
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def register_pre_open_hook(cls, order, func):
|
||||
cls._pre_open_hooks[order] = func
|
||||
|
||||
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)
|
||||
@@ -329,6 +324,8 @@ class DocumentVersion(models.Model):
|
||||
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)
|
||||
|
||||
#TODO: to be removed after migration
|
||||
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)
|
||||
|
||||
@@ -393,7 +390,9 @@ class DocumentVersion(models.Model):
|
||||
|
||||
if new_document:
|
||||
#Only do this for new documents
|
||||
self.update_signed_state(save=False)
|
||||
#Only do this for new documents
|
||||
# TODO: remove after migration
|
||||
#self.update_signed_state(save=False)
|
||||
self.update_checksum(save=False)
|
||||
self.update_mimetype(save=False)
|
||||
self.save()
|
||||
@@ -467,6 +466,8 @@ class DocumentVersion(models.Model):
|
||||
for version in self.document.versions.filter(timestamp__gt=self.timestamp):
|
||||
version.delete()
|
||||
|
||||
#TODO: remove after migration
|
||||
"""
|
||||
def update_signed_state(self, save=True):
|
||||
if self.exists():
|
||||
try:
|
||||
@@ -478,7 +479,8 @@ class DocumentVersion(models.Model):
|
||||
|
||||
if save:
|
||||
self.save()
|
||||
|
||||
"""
|
||||
|
||||
def update_mimetype(self, save=True):
|
||||
'''
|
||||
Read a document verions's file and determine the mimetype by calling the
|
||||
@@ -510,6 +512,16 @@ class DocumentVersion(models.Model):
|
||||
Return a file descriptor to a document version's file irrespective of
|
||||
the storage backend
|
||||
'''
|
||||
if raw:
|
||||
return self.file.storage.open(self.file.path)
|
||||
else:
|
||||
result = self.file.storage.open(self.file.path)
|
||||
for key in sorted(DocumentVersion._pre_open_hooks):
|
||||
result = DocumentVersion._pre_open_hooks[key](result)
|
||||
|
||||
return result
|
||||
#TODO: remove after migration
|
||||
"""
|
||||
if self.signature_state and not raw:
|
||||
try:
|
||||
result = gpg.decrypt_file(self.file.storage.open(self.file.path))
|
||||
@@ -520,6 +532,7 @@ class DocumentVersion(models.Model):
|
||||
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):
|
||||
'''
|
||||
@@ -545,7 +558,8 @@ class DocumentVersion(models.Model):
|
||||
return self.file.storage.size(self.file.path)
|
||||
else:
|
||||
return None
|
||||
|
||||
#TODO: remove after migration
|
||||
"""
|
||||
def add_detached_signature(self, detached_signature):
|
||||
if not self.signature_state:
|
||||
self.signature_file = detached_signature
|
||||
@@ -573,6 +587,7 @@ class DocumentVersion(models.Model):
|
||||
signature = None
|
||||
|
||||
return signature
|
||||
"""
|
||||
|
||||
|
||||
class DocumentTypeFilename(models.Model):
|
||||
|
||||
@@ -1,23 +1,85 @@
|
||||
"""
|
||||
This file demonstrates two different styles of tests (one doctest and one
|
||||
unittest). These will both pass when you run "manage.py test".
|
||||
import os
|
||||
|
||||
Replace these with more appropriate tests for your application.
|
||||
"""
|
||||
from django.utils import unittest
|
||||
from django.test.client import Client
|
||||
from django.conf import settings
|
||||
from django.core.files.base import File
|
||||
|
||||
from django.test import TestCase
|
||||
from django_gpg.api import SIGNATURE_STATE_VALID
|
||||
|
||||
class SimpleTest(TestCase):
|
||||
def test_basic_addition(self):
|
||||
"""
|
||||
Tests that 1 + 1 always equals 2.
|
||||
"""
|
||||
self.failUnlessEqual(1 + 1, 2)
|
||||
from .models import Document, DocumentType
|
||||
from .literals import VERSION_UPDATE_MAJOR, RELEASE_LEVEL_FINAL
|
||||
|
||||
__test__ = {"doctest": """
|
||||
Another way to test that 1 + 1 is equal to 2.
|
||||
|
||||
>>> 1 + 1 == 2
|
||||
True
|
||||
"""}
|
||||
class DocumentTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.document_type = DocumentType(name='test doc type')
|
||||
self.document_type.save()
|
||||
|
||||
self.document = Document(
|
||||
document_type = self.document_type,
|
||||
description = 'description',
|
||||
)
|
||||
self.document.save()
|
||||
#return File(file(self.filepath, 'rb'), name=self.filename)
|
||||
|
||||
file_object = open(os.path.join(settings.PROJECT_ROOT, 'contrib', 'mayan_11_1.pdf'))
|
||||
new_version = self.document.new_version(file=File(file_object, name='mayan_11_1.pdf'))
|
||||
file_object.close()
|
||||
|
||||
def runTest(self):
|
||||
self.failUnlessEqual(self.document_type.name, 'test doc type')
|
||||
|
||||
self.failUnlessEqual(self.document.exists(), True)
|
||||
self.failUnlessEqual(self.document.size, 272213)
|
||||
|
||||
self.failUnlessEqual(self.document.file_mimetype, 'application/pdf')
|
||||
self.failUnlessEqual(self.document.file_mime_encoding, 'binary')
|
||||
self.failUnlessEqual(self.document.file_filename, 'mayan_11_1.pdf')
|
||||
self.failUnlessEqual(self.document.checksum, 'c637ffab6b8bb026ed3784afdb07663fddc60099853fae2be93890852a69ecf3')
|
||||
self.failUnlessEqual(self.document.page_count, 47)
|
||||
|
||||
self.failUnlessEqual(self.document.latest_version.get_formated_version(), '1.0')
|
||||
self.failUnlessEqual(self.document.has_detached_signature(), False)
|
||||
|
||||
file_object = open(os.path.join(settings.PROJECT_ROOT, 'contrib', 'mayan_11_1.pdf.gpg'))
|
||||
new_version_data = {
|
||||
'comment': 'test comment 1',
|
||||
'version_update': VERSION_UPDATE_MAJOR,
|
||||
'release_level': RELEASE_LEVEL_FINAL,
|
||||
'serial': 0,
|
||||
}
|
||||
|
||||
new_version = self.document.new_version(file=File(file_object, name='mayan_11_1.pdf.gpg'), **new_version_data)
|
||||
file_object.close()
|
||||
|
||||
self.failUnlessEqual(self.document.latest_version.get_formated_version(), '2.0')
|
||||
self.failUnlessEqual(self.document.has_detached_signature(), False)
|
||||
|
||||
self.failUnlessEqual(self.document.verify_signature().status, SIGNATURE_STATE_VALID)
|
||||
|
||||
new_version_data = {
|
||||
'comment': 'test comment 2',
|
||||
'version_update': VERSION_UPDATE_MAJOR,
|
||||
'release_level': RELEASE_LEVEL_FINAL,
|
||||
'serial': 0,
|
||||
}
|
||||
file_object = open(os.path.join(settings.PROJECT_ROOT, 'contrib', 'mayan_11_1.pdf'))
|
||||
new_version = self.document.new_version(file=File(file_object), **new_version_data)
|
||||
file_object.close()
|
||||
|
||||
self.failUnlessEqual(self.document.latest_version.get_formated_version(), '3.0')
|
||||
|
||||
#GPGVerificationError
|
||||
self.failUnlessEqual(self.document.verify_signature(), None)
|
||||
|
||||
file_object = open(os.path.join(settings.PROJECT_ROOT, 'contrib', 'mayan_11_1.pdf.sig'), 'rb')
|
||||
new_version = self.document.add_detached_signature(File(file_object))
|
||||
file_object.close()
|
||||
|
||||
self.failUnlessEqual(self.document.has_detached_signature(), True)
|
||||
self.failUnlessEqual(self.document.verify_signature().status, SIGNATURE_STATE_VALID)
|
||||
|
||||
|
||||
def tearDown(self):
|
||||
self.document.delete()
|
||||
|
||||
@@ -96,7 +96,7 @@ def do_document_ocr(queue_document):
|
||||
# Fall back to doing visual OCR
|
||||
ocr_transformations, warnings = queue_document.get_transformation_list()
|
||||
|
||||
document_filepath = document_page.document.get_image_cache_name(page=document_page.page_number)
|
||||
document_filepath = document_page.document.get_image_cache_name(page=document_page.page_number, version=document_page.document_version.pk)
|
||||
unpaper_output_filename = u'%s_unpaper_out_page_%s%s%s' % (document_page.document.uuid, document_page.page_number, os.extsep, UNPAPER_FILE_FORMAT)
|
||||
unpaper_output_filepath = os.path.join(TEMPORARY_DIRECTORY, unpaper_output_filename)
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ugettext
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.conf import settings
|
||||
|
||||
from documents.literals import PERMISSION_DOCUMENT_CREATE
|
||||
from documents.models import DocumentType, Document
|
||||
@@ -161,6 +162,8 @@ def upload_interactive(request, source_type=None, source_id=None, document_pk=No
|
||||
messages.success(request, _(u'Document uploaded successfully.'))
|
||||
return HttpResponseRedirect(request.get_full_path())
|
||||
except Exception, e:
|
||||
if settings.DEBUG:
|
||||
raise
|
||||
messages.error(request, _(u'Unhandled exception: %s') % e)
|
||||
else:
|
||||
form = WebFormForm(
|
||||
@@ -231,6 +234,8 @@ def upload_interactive(request, source_type=None, source_id=None, document_pk=No
|
||||
else:
|
||||
return HttpResponseRedirect(request.get_full_path())
|
||||
except Exception, e:
|
||||
if settings.DEBUG:
|
||||
raise
|
||||
messages.error(request, _(u'Unhandled exception: %s') % e)
|
||||
else:
|
||||
form = StagingDocumentForm(cls=StagingFile,
|
||||
|
||||
@@ -8,6 +8,7 @@ from documents.models import Document
|
||||
from acls.models import class_permissions
|
||||
|
||||
from taggit.models import Tag
|
||||
from taggit.managers import TaggableManager
|
||||
|
||||
from tags.widgets import tag_color_block
|
||||
|
||||
@@ -67,3 +68,5 @@ class_permissions(Document, [
|
||||
# PERMISSION_TAG_EDIT,
|
||||
# PERMISSION_TAG_VIEW,
|
||||
#])
|
||||
|
||||
Document.add_to_class('tags', TaggableManager())
|
||||
|
||||
@@ -1,23 +1,15 @@
|
||||
"""
|
||||
This file demonstrates two different styles of tests (one doctest and one
|
||||
unittest). These will both pass when you run "manage.py test".
|
||||
from django.utils import unittest
|
||||
|
||||
Replace these with more appropriate tests for your application.
|
||||
"""
|
||||
from .models import Tag, TagProperties, COLOR_RED
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
class SimpleTest(TestCase):
|
||||
def test_basic_addition(self):
|
||||
"""
|
||||
Tests that 1 + 1 always equals 2.
|
||||
"""
|
||||
self.failUnlessEqual(1 + 1, 2)
|
||||
|
||||
__test__ = {"doctest": """
|
||||
Another way to test that 1 + 1 is equal to 2.
|
||||
|
||||
>>> 1 + 1 == 2
|
||||
True
|
||||
"""}
|
||||
class TagTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.tag = Tag(name='test')
|
||||
self.tag.save()
|
||||
self.tp = TagProperties(tag=self.tag, color=COLOR_RED)
|
||||
self.tp.save()
|
||||
|
||||
def runTest(self):
|
||||
self.failUnlessEqual(self.tag.name, 'test')
|
||||
self.failUnlessEqual(self.tp.get_color_code(), 'red')
|
||||
|
||||
BIN
contrib/mayan_11_1.pdf
Normal file
BIN
contrib/mayan_11_1.pdf
Normal file
Binary file not shown.
BIN
contrib/mayan_11_1.pdf.gpg
Normal file
BIN
contrib/mayan_11_1.pdf.gpg
Normal file
Binary file not shown.
BIN
contrib/mayan_11_1.pdf.sig
Normal file
BIN
contrib/mayan_11_1.pdf.sig
Normal file
Binary file not shown.
@@ -134,3 +134,9 @@ $COMPILEMESSAGES -l pt
|
||||
$COMPILEMESSAGES -l ru
|
||||
$COMPILEMESSAGES -l es
|
||||
$COMPILEMESSAGES -l it
|
||||
|
||||
cd $BASE/apps/document_signatures
|
||||
$COMPILEMESSAGES -l pt
|
||||
$COMPILEMESSAGES -l ru
|
||||
$COMPILEMESSAGES -l es
|
||||
$COMPILEMESSAGES -l it
|
||||
|
||||
@@ -156,3 +156,10 @@ $MAKEMESSAGES -l pt
|
||||
$MAKEMESSAGES -l ru
|
||||
$MAKEMESSAGES -l es
|
||||
$MAKEMESSAGES -l it
|
||||
|
||||
cd $BASE/apps/document_signatures
|
||||
$MAKEMESSAGES -l en
|
||||
$MAKEMESSAGES -l pt
|
||||
$MAKEMESSAGES -l ru
|
||||
$MAKEMESSAGES -l es
|
||||
$MAKEMESSAGES -l it
|
||||
|
||||
@@ -170,6 +170,7 @@ INSTALLED_APPS = (
|
||||
'djangorestframework',
|
||||
'rest_api',
|
||||
'south',
|
||||
'document_signatures',
|
||||
)
|
||||
|
||||
TEMPLATE_CONTEXT_PROCESSORS = (
|
||||
|
||||
3
urls.py
3
urls.py
@@ -30,7 +30,8 @@ urlpatterns = patterns('',
|
||||
(r'^acls/', include('acls.urls')),
|
||||
(r'^document_acls/', include('document_acls.urls')),
|
||||
(r'^api/', include('rest_api.urls')),
|
||||
(r'^signatures/', include('django_gpg.urls')),
|
||||
(r'^gpg/', include('django_gpg.urls')),
|
||||
(r'^documents/signatures/', include('document_signatures.urls')),
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user