diff --git a/.gitignore b/.gitignore index c6ebd3a9d5..6a625abb94 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ misc/mayan.geany image_cache/ build/ _build/ +gpg_home/ diff --git a/.tx/config b/.tx/config index acbc95abba..27ed2c26db 100644 --- a/.tx/config +++ b/.tx/config @@ -4,6 +4,8 @@ source_lang = en trans.es = apps/converter/locale/es/LC_MESSAGES/django.po trans.pt = apps/converter/locale/pt/LC_MESSAGES/django.po trans.ru = apps/converter/locale/ru/LC_MESSAGES/django.po +trans.it = apps/converter/locale/it/LC_MESSAGES/django.po +trans.pl = apps/converter/locale/pl/LC_MESSAGES/django.po [mayan-edms.apps-common] source_file = apps/common/locale/en/LC_MESSAGES/django.po @@ -11,6 +13,8 @@ source_lang = en trans.es = apps/common/locale/es/LC_MESSAGES/django.po trans.pt = apps/common/locale/pt/LC_MESSAGES/django.po trans.ru = apps/common/locale/ru/LC_MESSAGES/django.po +trans.it = apps/common/locale/it/LC_MESSAGES/django.po +trans.pl = apps/common/locale/pl/LC_MESSAGES/django.po [mayan-edms.apps-permissions] source_file = apps/permissions/locale/en/LC_MESSAGES/django.po @@ -18,6 +22,8 @@ source_lang = en trans.es = apps/permissions/locale/es/LC_MESSAGES/django.po trans.pt = apps/permissions/locale/pt/LC_MESSAGES/django.po trans.ru = apps/permissions/locale/ru/LC_MESSAGES/django.po +trans.it = apps/permissions/locale/it/LC_MESSAGES/django.po +trans.pl = apps/permissions/locale/pl/LC_MESSAGES/django.po [mayan-edms.apps-sources] source_file = apps/sources/locale/en/LC_MESSAGES/django.po @@ -25,6 +31,8 @@ source_lang = en trans.es = apps/sources/locale/es/LC_MESSAGES/django.po trans.pt = apps/sources/locale/pt/LC_MESSAGES/django.po trans.ru = apps/sources/locale/ru/LC_MESSAGES/django.po +trans.it = apps/sources/locale/it/LC_MESSAGES/django.po +trans.pl = apps/sources/locale/pl/LC_MESSAGES/django.po [mayan-edms.apps-document_indexing] source_file = apps/document_indexing/locale/en/LC_MESSAGES/django.po @@ -32,6 +40,8 @@ source_lang = en trans.es = apps/document_indexing/locale/es/LC_MESSAGES/django.po trans.pt = apps/document_indexing/locale/pt/LC_MESSAGES/django.po trans.ru = apps/document_indexing/locale/ru/LC_MESSAGES/django.po +trans.it = apps/document_indexing/locale/it/LC_MESSAGES/django.po +trans.pl = apps/document_indexing/locale/pl/LC_MESSAGES/django.po [mayan-edms.apps-user_management] source_file = apps/user_management/locale/en/LC_MESSAGES/django.po @@ -39,6 +49,8 @@ source_lang = en trans.es = apps/user_management/locale/es/LC_MESSAGES/django.po trans.pt = apps/user_management/locale/pt/LC_MESSAGES/django.po trans.ru = apps/user_management/locale/ru/LC_MESSAGES/django.po +trans.it = apps/user_management/locale/it/LC_MESSAGES/django.po +trans.pl = apps/user_management/locale/pl/LC_MESSAGES/django.po [mayan-edms.apps-main] source_file = apps/main/locale/en/LC_MESSAGES/django.po @@ -46,6 +58,8 @@ source_lang = en trans.es = apps/main/locale/es/LC_MESSAGES/django.po trans.pt = apps/main/locale/pt/LC_MESSAGES/django.po trans.ru = apps/main/locale/ru/LC_MESSAGES/django.po +trans.it = apps/main/locale/it/LC_MESSAGES/django.po +trans.pl = apps/main/locale/pl/LC_MESSAGES/django.po [mayan-edms.apps-ocr] source_file = apps/ocr/locale/en/LC_MESSAGES/django.po @@ -53,6 +67,8 @@ source_lang = en trans.es = apps/ocr/locale/es/LC_MESSAGES/django.po trans.pt = apps/ocr/locale/pt/LC_MESSAGES/django.po trans.ru = apps/ocr/locale/ru/LC_MESSAGES/django.po +trans.it = apps/ocr/locale/it/LC_MESSAGES/django.po +trans.pl = apps/ocr/locale/pl/LC_MESSAGES/django.po [mayan-edms.apps-project_setup] source_file = apps/project_setup/locale/en/LC_MESSAGES/django.po @@ -60,6 +76,8 @@ source_lang = en trans.es = apps/project_setup/locale/es/LC_MESSAGES/django.po trans.pt = apps/project_setup/locale/pt/LC_MESSAGES/django.po trans.ru = apps/project_setup/locale/ru/LC_MESSAGES/django.po +trans.it = apps/project_setup/locale/it/LC_MESSAGES/django.po +trans.pl = apps/project_setup/locale/pl/LC_MESSAGES/django.po [main] host = https://www.transifex.net @@ -70,6 +88,8 @@ source_lang = en trans.es = apps/folders/locale/es/LC_MESSAGES/django.po trans.pt = apps/folders/locale/pt/LC_MESSAGES/django.po trans.ru = apps/folders/locale/ru/LC_MESSAGES/django.po +trans.it = apps/folders/locale/it/LC_MESSAGES/django.po +trans.pl = apps/folders/locale/pl/LC_MESSAGES/django.po [mayan-edms.apps-history] source_file = apps/history/locale/en/LC_MESSAGES/django.po @@ -77,6 +97,8 @@ source_lang = en trans.es = apps/history/locale/es/LC_MESSAGES/django.po trans.pt = apps/history/locale/pt/LC_MESSAGES/django.po trans.ru = apps/history/locale/ru/LC_MESSAGES/django.po +trans.it = apps/history/locale/it/LC_MESSAGES/django.po +trans.pl = apps/history/locale/pl/LC_MESSAGES/django.po [mayan-edms.apps-dynamic_search] source_file = apps/dynamic_search/locale/en/LC_MESSAGES/django.po @@ -84,6 +106,8 @@ source_lang = en trans.es = apps/dynamic_search/locale/es/LC_MESSAGES/django.po trans.pt = apps/dynamic_search/locale/pt/LC_MESSAGES/django.po trans.ru = apps/dynamic_search/locale/ru/LC_MESSAGES/django.po +trans.it = apps/dynamic_search/locale/it/LC_MESSAGES/django.po +trans.pl = apps/dynamic_search/locale/pl/LC_MESSAGES/django.po [mayan-edms.apps-smart_settings] source_file = apps/smart_settings/locale/en/LC_MESSAGES/django.po @@ -91,6 +115,8 @@ source_lang = en trans.es = apps/smart_settings/locale/es/LC_MESSAGES/django.po trans.pt = apps/smart_settings/locale/pt/LC_MESSAGES/django.po trans.ru = apps/smart_settings/locale/ru/LC_MESSAGES/django.po +trans.it = apps/smart_settings/locale/it/LC_MESSAGES/django.po +trans.pl = apps/smart_settings/locale/pl/LC_MESSAGES/django.po [mayan-edms.apps-navigation] source_file = apps/navigation/locale/en/LC_MESSAGES/django.po @@ -98,6 +124,8 @@ source_lang = en trans.es = apps/navigation/locale/es/LC_MESSAGES/django.po trans.pt = apps/navigation/locale/pt/LC_MESSAGES/django.po trans.ru = apps/navigation/locale/ru/LC_MESSAGES/django.po +trans.it = apps/navigation/locale/it/LC_MESSAGES/django.po +trans.pl = apps/navigation/locale/pl/LC_MESSAGES/django.po [mayan-edms.apps-tags] source_file = apps/tags/locale/en/LC_MESSAGES/django.po @@ -105,6 +133,8 @@ source_lang = en trans.es = apps/tags/locale/es/LC_MESSAGES/django.po trans.pt = apps/tags/locale/pt/LC_MESSAGES/django.po trans.ru = apps/tags/locale/ru/LC_MESSAGES/django.po +trans.it = apps/tags/locale/it/LC_MESSAGES/django.po +trans.pl = apps/tags/locale/pl/LC_MESSAGES/django.po [mayan-edms.apps-documents] source_file = apps/documents/locale/en/LC_MESSAGES/django.po @@ -112,6 +142,8 @@ source_lang = en trans.es = apps/documents/locale/es/LC_MESSAGES/django.po trans.pt = apps/documents/locale/pt/LC_MESSAGES/django.po trans.ru = apps/documents/locale/ru/LC_MESSAGES/django.po +trans.it = apps/documents/locale/it/LC_MESSAGES/django.po +trans.pl = apps/documents/locale/pl/LC_MESSAGES/django.po [mayan-edms.apps-project_tools] source_file = apps/project_tools/locale/en/LC_MESSAGES/django.po @@ -119,6 +151,8 @@ source_lang = en trans.es = apps/project_tools/locale/es/LC_MESSAGES/django.po trans.pt = apps/project_tools/locale/pt/LC_MESSAGES/django.po trans.ru = apps/project_tools/locale/ru/LC_MESSAGES/django.po +trans.it = apps/project_tools/locale/it/LC_MESSAGES/django.po +trans.pl = apps/project_tools/locale/pl/LC_MESSAGES/django.po [mayan-edms.apps-linking] source_file = apps/linking/locale/en/LC_MESSAGES/django.po @@ -126,6 +160,8 @@ source_lang = en trans.es = apps/linking/locale/es/LC_MESSAGES/django.po trans.pt = apps/linking/locale/pt/LC_MESSAGES/django.po trans.ru = apps/linking/locale/ru/LC_MESSAGES/django.po +trans.it = apps/linking/locale/it/LC_MESSAGES/django.po +trans.pl = apps/linking/locale/pl/LC_MESSAGES/django.po [mayan-edms.apps-document_comments] source_file = apps/document_comments/locale/en/LC_MESSAGES/django.po @@ -133,6 +169,8 @@ source_lang = en trans.es = apps/document_comments/locale/es/LC_MESSAGES/django.po trans.pt = apps/document_comments/locale/pt/LC_MESSAGES/django.po trans.ru = apps/document_comments/locale/ru/LC_MESSAGES/django.po +trans.it = apps/document_comments/locale/it/LC_MESSAGES/django.po +trans.pl = apps/document_comments/locale/pl/LC_MESSAGES/django.po [mayan-edms.apps-metadata] source_file = apps/metadata/locale/en/LC_MESSAGES/django.po @@ -140,6 +178,8 @@ source_lang = en trans.es = apps/metadata/locale/es/LC_MESSAGES/django.po trans.pt = apps/metadata/locale/pt/LC_MESSAGES/django.po trans.ru = apps/metadata/locale/ru/LC_MESSAGES/django.po +trans.it = apps/metadata/locale/it/LC_MESSAGES/django.po +trans.pl = apps/metadata/locale/pl/LC_MESSAGES/django.po [mayan-edms.apps-web_theme] source_file = apps/web_theme/locale/en/LC_MESSAGES/django.po @@ -147,6 +187,8 @@ source_lang = en trans.es = apps/web_theme/locale/es/LC_MESSAGES/django.po trans.pt = apps/web_theme/locale/pt/LC_MESSAGES/django.po trans.ru = apps/web_theme/locale/ru/LC_MESSAGES/django.po +trans.it = apps/web_theme/locale/it/LC_MESSAGES/django.po +trans.pl = apps/web_theme/locale/pl/LC_MESSAGES/django.po [mayan-edms.apps-django_gpg] source_file = apps/django_gpg/locale/en/LC_MESSAGES/django.po @@ -154,3 +196,32 @@ source_lang = en trans.es = apps/django_gpg/locale/es/LC_MESSAGES/django.po trans.pt = apps/django_gpg/locale/pt/LC_MESSAGES/django.po trans.ru = apps/django_gpg/locale/ru/LC_MESSAGES/django.po +trans.it = apps/django_gpg/locale/it/LC_MESSAGES/django.po +trans.pl = apps/django_gpg/locale/pl/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 +trans.pl = apps/document_signatures/locale/pl/LC_MESSAGES/django.po + +[mayan-edms.apps-acls] +source_file = apps/acls/locale/en/LC_MESSAGES/django.po +source_lang = en +trans.es = apps/acls/locale/es/LC_MESSAGES/django.po +trans.pt = apps/acls/locale/pt/LC_MESSAGES/django.po +trans.ru = apps/acls/locale/ru/LC_MESSAGES/django.po +trans.it = apps/acls/locale/it/LC_MESSAGES/django.po +trans.pl = apps/acls/locale/pl/LC_MESSAGES/django.po + +[mayan-edms.apps-feedback] +source_file = apps/feedback/locale/en/LC_MESSAGES/django.po +source_lang = en +trans.es = apps/feedback/locale/es/LC_MESSAGES/django.po +trans.pt = apps/feedback/locale/pt/LC_MESSAGES/django.po +trans.ru = apps/feedback/locale/ru/LC_MESSAGES/django.po +trans.it = apps/feedback/locale/it/LC_MESSAGES/django.po +trans.pl = apps/feedback/locale/pl/LC_MESSAGES/django.po diff --git a/README.md b/README.md index 0695d8fa3a..7e4b8e1203 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,7 @@ Mayan Open source, Django based document manager with custom metadata indexing, file serving integration and OCR capabilities. -[Website](http://www.mayan-edms.com) - -[Mailing list (via Google Groups)](http://groups.google.com/group/mayan-edms) +[Website](http://bit.ly/mayan-edms) [Video demostration](http://bit.ly/pADNXv) @@ -13,6 +11,7 @@ Open source, Django based document manager with custom metadata indexing, file s [Translations](https://www.transifex.net/projects/p/mayan-edms/) +[Mailing list (via Google Groups)](http://groups.google.com/group/mayan-edms) License ------- diff --git a/__init__.py b/__init__.py index e69de29bb2..a373a640b0 100644 --- a/__init__.py +++ b/__init__.py @@ -0,0 +1,2 @@ +from main import (__version__, __author__, __copyright__, __credits__, + __license__, __maintainer__, __email__, __status__) diff --git a/apps/acls/__init__.py b/apps/acls/__init__.py new file mode 100644 index 0000000000..7a83009b39 --- /dev/null +++ b/apps/acls/__init__.py @@ -0,0 +1,39 @@ +from __future__ import absolute_import + +from django.utils.translation import ugettext_lazy as _ + +from navigation.api import register_links, register_multi_item_links +from project_setup.api import register_setup + +from .classes import (AccessHolder, AccessObjectClass, ClassAccessHolder, + AccessObject) +from .permissions import (ACLS_EDIT_ACL, ACLS_VIEW_ACL, + ACLS_CLASS_EDIT_ACL, ACLS_CLASS_VIEW_ACL) + + +acl_list = {'text': _(u'ACLs'), 'view': 'acl_list', 'famfam': 'lock', 'permissions': [ACLS_VIEW_ACL]} +acl_detail = {'text': _(u'details'), 'view': 'acl_detail', 'args': ['access_object.gid', 'object.gid'], 'famfam': 'key_go', 'permissions': [ACLS_VIEW_ACL]} +acl_grant = {'text': _(u'grant'), 'view': 'acl_multiple_grant', 'famfam': 'key_add', 'permissions': [ACLS_EDIT_ACL]} +acl_revoke = {'text': _(u'revoke'), 'view': 'acl_multiple_revoke', 'famfam': 'key_delete', 'permissions': [ACLS_EDIT_ACL]} +acl_holder_new = {'text': _(u'New holder'), 'view': 'acl_holder_new', 'args': 'access_object.gid', 'famfam': 'user', 'permissions': [ACLS_EDIT_ACL]} + +acl_setup_valid_classes = {'text': _(u'Default ACLs'), 'view': 'acl_setup_valid_classes', 'icon': 'lock.png', 'permissions': [ACLS_CLASS_VIEW_ACL], 'children_view_regex': [r'^acl_class', r'^acl_setup']} +acl_class_list = {'text': _(u'List of classes'), 'view': 'acl_setup_valid_classes', 'famfam': 'package', 'permissions': [ACLS_CLASS_VIEW_ACL]} +acl_class_acl_list = {'text': _(u'ACLs for class'), 'view': 'acl_class_acl_list', 'args': 'object.gid', 'famfam': 'lock_go', 'permissions': [ACLS_CLASS_VIEW_ACL]} +acl_class_acl_detail = {'text': _(u'details'), 'view': 'acl_class_acl_detail', 'args': ['access_object_class.gid', 'object.gid'], 'famfam': 'key_go', 'permissions': [ACLS_CLASS_VIEW_ACL]} +acl_class_new_holder_for = {'text': _(u'New holder'), 'view': 'acl_class_new_holder_for', 'args': 'object.gid', 'famfam': 'user', 'permissions': [ACLS_CLASS_EDIT_ACL]} +acl_class_grant = {'text': _(u'grant'), 'view': 'acl_class_multiple_grant', 'famfam': 'key_add', 'permissions': [ACLS_CLASS_EDIT_ACL]} +acl_class_revoke = {'text': _(u'revoke'), 'view': 'acl_class_multiple_revoke', 'famfam': 'key_delete', 'permissions': [ACLS_CLASS_EDIT_ACL]} + +register_links(AccessHolder, [acl_detail]) +register_multi_item_links(['acl_detail'], [acl_grant, acl_revoke]) + +register_links([AccessObject], [acl_holder_new], menu_name='sidebar') + +register_setup(acl_setup_valid_classes) +register_links(['acl_setup_valid_classes', 'acl_class_acl_list', 'acl_class_new_holder_for', 'acl_class_acl_detail', 'acl_class_multiple_grant', 'acl_class_multiple_revoke'], [acl_class_list], menu_name='secondary_menu') + +register_links(ClassAccessHolder, [acl_class_acl_detail]) + +register_links(AccessObjectClass, [acl_class_acl_list, acl_class_new_holder_for]) +register_multi_item_links(['acl_class_acl_detail'], [acl_class_grant, acl_class_revoke]) diff --git a/apps/acls/admin.py b/apps/acls/admin.py new file mode 100644 index 0000000000..61c12d9498 --- /dev/null +++ b/apps/acls/admin.py @@ -0,0 +1,23 @@ +from __future__ import absolute_import + +from django.contrib import admin + +from .models import AccessEntry + + +#class PermissionHolderInline(admin.StackedInline): +# model = PermissionHolder +# extra = 1 +# classes = ('collapse-open',) +# allow_add = True# +# +class AccessEntryAdmin(admin.ModelAdmin): + related_lookup_fields = { + 'generic': [['holder_type', 'holder_id'], ['content_type', 'object_id']], + } + #inlines = [PermissionHolderInline] + list_display = ('pk', 'holder_object', 'permission', 'content_object') + list_display_links = ('pk',) + model = AccessEntry + +admin.site.register(AccessEntry, AccessEntryAdmin) diff --git a/apps/acls/api.py b/apps/acls/api.py new file mode 100644 index 0000000000..bfcdcf1e7b --- /dev/null +++ b/apps/acls/api.py @@ -0,0 +1,28 @@ +from __future__ import absolute_import + +from django.contrib.contenttypes.models import ContentType + +_class_permissions = {} + + +def class_permissions(cls, permission_list): + """ + Associate a permissions list to a class + """ + stored_permissions = _class_permissions.setdefault(cls, []) + stored_permissions.extend(permission_list) + + +def get_class_permissions_for(obj): + """ + Return a list of permissions associated with a content type + """ + content_type = ContentType.objects.get_for_model(obj) + return _class_permissions.get(content_type.model_class(), []) + + +def get_classes(): + """ + Return a list of encapsulated classes that have been registered + """ + return _class_permissions.keys() diff --git a/apps/acls/classes.py b/apps/acls/classes.py new file mode 100644 index 0000000000..89802566ea --- /dev/null +++ b/apps/acls/classes.py @@ -0,0 +1,158 @@ +from __future__ import absolute_import + +import logging +import sys +import types + +from django.contrib.contenttypes.models import ContentType +from django.db.models.base import ModelBase +from django.template.defaultfilters import capfirst +from django.core.exceptions import ObjectDoesNotExist + +from common.models import AnonymousUserSingleton + +logger = logging.getLogger(__name__) + +_cache = {} + + +def get_source_object(obj): + if isinstance(obj, EncapsulatedObject): + return obj.source_object + else: + return obj + + +class EncapsulatedObject(object): + source_object_name = u'source_object' + + @classmethod + def object_key(cls, app_label=None, model=None, pk=None): + if pk: + return '%s.%s.%s.%s' % (cls.__name__, app_label, model, pk) + else: + return '%s.%s.%s' % (cls.__name__, app_label, model) + + @classmethod + def add_to_class(cls, name, value): + if hasattr(value, 'contribute_to_class'): + value.contribute_to_class(cls, name) + else: + setattr(cls, name, value) + + @classmethod + def set_source_object_name(cls, new_name): + cls.source_object_name = new_name + + #@classmethod + #def encapsulate_list(cls, source_object=None, app_label=None, model=None, pk=None): + + @classmethod + def encapsulate(cls, source_object): + source_object = AnonymousUserSingleton.objects.passthru_check(source_object) + content_type = ContentType.objects.get_for_model(source_object) + + if hasattr(source_object, 'pk'): + # Object + object_key = cls.object_key(content_type.app_label, content_type.model, source_object.pk) + else: + # Class + object_key = cls.object_key(content_type.app_label, content_type.model) + + try: + return _cache[object_key] + except KeyError: + encapsulated_object = cls(source_object) + _cache[object_key] = encapsulated_object + return encapsulated_object + + @classmethod + def get(cls, gid): + elements = gid.split('.') + if len(elements) == 3: + app_label, model, pk = elements[0], elements[1], elements[2] + elif len(elements) == 2: + app_label, model = elements[0], elements[1] + pk = None + + object_key = cls.object_key(*elements) + + try: + return _cache[object_key] + except KeyError: + try: + content_type = ContentType.objects.get(app_label=app_label, model=model) + except ContentType.DoesNotExist: + #cls.add_to_class('DoesNotExist', subclass_exception('DoesNotExist', (ObjectDoesNotExist,), cls.__name__)) + #raise cls.DoesNotExist("%s matching query does not exist." % ContentType._meta.object_name) + raise ObjectDoesNotExist("%s matching query does not exist." % ContentType._meta.object_name) + else: + source_object_model_class = content_type.model_class() + if pk: + try: + source_object = content_type.get_object_for_this_type(pk=pk) + except source_object_model_class.DoesNotExist: + #cls.add_to_class('DoesNotExist', subclass_exception('DoesNotExist', (ObjectDoesNotExist,), cls.__name__)) + #raise cls.DoesNotExist("%s matching query does not exist." % source_object_model_class._meta.object_name) + raise ObjectDoesNotExist("%s matching query does not exist." % source_object_model_class._meta.object_name) + else: + source_object = source_object_model_class + + return cls.encapsulate(source_object) + + def __init__(self, source_object): + self.content_type = ContentType.objects.get_for_model(source_object) + self.ct_fullname = '%s.%s' % (self.content_type.app_label, self.content_type.model) + + if isinstance(source_object, ModelBase): + # Class + self.gid = '%s.%s' % (self.content_type.app_label, self.content_type.model) + else: + # Object + self.gid = '%s.%s.%s' % (self.content_type.app_label, self.content_type.model, source_object.pk) + + setattr(self, self.__class__.source_object_name, source_object) + + def __unicode__(self): + if isinstance(self.source_object, ModelBase): + return capfirst(unicode(self.source_object._meta.verbose_name_plural)) + elif self.ct_fullname == 'auth.user': + return u'%s %s' % (self.source_object._meta.verbose_name, self.source_object.get_full_name()) + elif self.ct_fullname == 'common.anonymoususersingleton': + return unicode(self.source_object) + elif self.ct_fullname == 'acls.creatorsingleton': + return unicode(self.source_object) + else: + return u'%s %s' % (self.source_object._meta.verbose_name, self.source_object) + + def __repr__(self): + return self.__unicode__() + + @property + def source_object(self): + return getattr(self, self.__class__.source_object_name, None) + + +class AccessHolder(EncapsulatedObject): + source_object_name = u'holder_object' + + +class AccessObject(EncapsulatedObject): + source_object_name = u'obj' + + +class AccessObjectClass(EncapsulatedObject): + source_object_name = u'cls' + + +class ClassAccessHolder(EncapsulatedObject): + source_object_name = u'class_holder' + + +if sys.version_info < (2, 5): + # Prior to Python 2.5, Exception was an old-style class + def subclass_exception(name, parents, unused): + return types.ClassType(name, parents, {}) +else: + def subclass_exception(name, parents, module): + return type(name, parents, {'__module__': module}) diff --git a/apps/acls/forms.py b/apps/acls/forms.py new file mode 100644 index 0000000000..6182f8fc5f --- /dev/null +++ b/apps/acls/forms.py @@ -0,0 +1,58 @@ +from __future__ import absolute_import + +from django import forms +from django.utils.translation import ugettext_lazy as _ +from django.contrib.auth.models import User, Group + +from permissions.models import Role +from common.utils import get_object_name +from common.models import AnonymousUserSingleton + +from .classes import AccessHolder +from .models import CreatorSingleton + + +def _as_choice_list(holders): + return sorted([(AccessHolder.encapsulate(holder).gid, get_object_name(holder, display_object_type=False)) for holder in holders], key=lambda x: x[1]) + + +class BaseHolderSelectionForm(forms.Form): + holder_gid = forms.ChoiceField( + label=_(u'New holder') + ) + + def __init__(self, *args, **kwargs): + current_holders = kwargs.pop('current_holders', []) + if current_holders: + current_holders = [holder.source_object for holder in current_holders] + + staff_users = User.objects.filter(is_staff=True) + super_users = User.objects.filter(is_superuser=True) + users = set(User.objects.filter(is_active=True)) - set(staff_users) - set(super_users) - set(current_holders) + roles = set(Role.objects.all()) - set(current_holders) + groups = set(Group.objects.all()) - set(current_holders) + special = set(self.special_holders) - set(current_holders) + + non_holder_list = [] + if users: + non_holder_list.append((_(u'Users'), _as_choice_list(list(users)))) + + if groups: + non_holder_list.append((_(u'Groups'), _as_choice_list(list(groups)))) + + if roles: + non_holder_list.append((_(u'Roles'), _as_choice_list(list(roles)))) + + if special: + non_holder_list.append((_(u'Special'), _as_choice_list(list(special)))) + + super(BaseHolderSelectionForm, self).__init__(*args, **kwargs) + self.fields['holder_gid'].choices = non_holder_list + + +class HolderSelectionForm(BaseHolderSelectionForm): + special_holders = [AnonymousUserSingleton.objects.get()] + + +class ClassHolderSelectionForm(BaseHolderSelectionForm): + special_holders = [AnonymousUserSingleton.objects.get(), CreatorSingleton.objects.get()] diff --git a/apps/acls/literals.py b/apps/acls/literals.py new file mode 100644 index 0000000000..0b867021a9 --- /dev/null +++ b/apps/acls/literals.py @@ -0,0 +1,13 @@ + +# Content type <-> fam fam icon mapping +CONTENT_TYPE_ICON_MAP = { + 'auth.user': 'user', + 'auth.group': 'group', + 'documents.document': 'page', + 'permissions.role': 'medal_gold_1', + 'folders.folder': 'folder', + 'taggit.tag': 'tag_blue', + 'linking.smartlink': 'page_link', + 'common.anonymoususersingleton': 'user', + 'acls.creatorsingleton': 'user', +} diff --git a/apps/acls/locale/en/LC_MESSAGES/django.po b/apps/acls/locale/en/LC_MESSAGES/django.po new file mode 100644 index 0000000000..e7e9ea010d --- /dev/null +++ b/apps/acls/locale/en/LC_MESSAGES/django.po @@ -0,0 +1,241 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: __init__.py:14 +msgid "ACLs" +msgstr "" + +#: __init__.py:15 __init__.py:23 +msgid "details" +msgstr "" + +#: __init__.py:16 __init__.py:25 +msgid "grant" +msgstr "" + +#: __init__.py:17 __init__.py:26 +msgid "revoke" +msgstr "" + +#: __init__.py:18 __init__.py:24 forms.py:21 +msgid "New holder" +msgstr "" + +#: __init__.py:20 +msgid "Default ACLs" +msgstr "" + +#: __init__.py:21 +msgid "List of classes" +msgstr "" + +#: __init__.py:22 +msgid "ACLs for class" +msgstr "" + +#: forms.py:38 +msgid "Users" +msgstr "" + +#: forms.py:41 +msgid "Groups" +msgstr "" + +#: forms.py:44 +msgid "Roles" +msgstr "" + +#: forms.py:47 +msgid "Special" +msgstr "" + +#: managers.py:116 managers.py:128 +msgid "Insufficient access." +msgstr "" + +#: models.py:27 models.py:69 +msgid "permission" +msgstr "" + +#: models.py:53 +msgid "access entry" +msgstr "" + +#: models.py:54 +msgid "access entries" +msgstr "" + +#: models.py:90 +msgid "default access entry" +msgstr "" + +#: models.py:91 +msgid "default access entries" +msgstr "" + +#: models.py:109 +msgid "Creator" +msgstr "" + +#: models.py:112 models.py:113 +msgid "creator" +msgstr "" + +#: permissions.py:7 permissions.py:8 +msgid "Access control lists" +msgstr "" + +#: permissions.py:10 +msgid "Edit ACLs" +msgstr "" + +#: permissions.py:11 +msgid "View ACLs" +msgstr "" + +#: permissions.py:13 +msgid "Edit class default ACLs" +msgstr "" + +#: permissions.py:14 +msgid "View class default ACLs" +msgstr "" + +#: views.py:47 +#, python-format +msgid "access control lists for: %s" +msgstr "" + +#: views.py:49 views.py:411 +msgid "holder" +msgstr "" + +#: views.py:50 views.py:412 +msgid "permissions" +msgstr "" + +#: views.py:97 +#, python-format +msgid "permissions available to: %(actor)s for %(obj)s" +msgstr "" + +#: views.py:104 views.py:444 +msgid "namespace" +msgstr "" + +#: views.py:105 views.py:445 +msgid "label" +msgstr "" + +#: views.py:107 views.py:447 +msgid "has permission" +msgstr "" + +#: views.py:185 views.py:279 views.py:528 views.py:608 +msgid ", " +msgstr "" + +#: views.py:186 views.py:280 views.py:529 views.py:609 +#, python-format +msgid " for %s" +msgstr "" + +#: views.py:187 views.py:530 +#, python-format +msgid " to %s" +msgstr "" + +#: views.py:190 views.py:533 +#, python-format +msgid "Are you sure you wish to grant the permission %(title_suffix)s?" +msgstr "" + +#: views.py:192 views.py:535 +#, python-format +msgid "Are you sure you wish to grant the permissions %(title_suffix)s?" +msgstr "" + +#: views.py:199 views.py:542 +#, python-format +msgid "Permission \"%(permission)s\" granted to %(actor)s for %(object)s." +msgstr "" + +#: views.py:205 views.py:548 +#, python-format +msgid "" +"%(actor)s, already had the permission \"%(permission)s\" granted for " +"%(object)s." +msgstr "" + +#: views.py:281 views.py:610 +#, python-format +msgid " from %s" +msgstr "" + +#: views.py:284 views.py:613 +#, python-format +msgid "Are you sure you wish to revoke the permission %(title_suffix)s?" +msgstr "" + +#: views.py:286 views.py:615 +#, python-format +msgid "Are you sure you wish to revoke the permissions %(title_suffix)s?" +msgstr "" + +#: views.py:293 views.py:622 +#, python-format +msgid "Permission \"%(permission)s\" revoked of %(actor)s for %(object)s." +msgstr "" + +#: views.py:299 views.py:628 +#, python-format +msgid "%(actor)s, didn't had the permission \"%(permission)s\" for %(object)s." +msgstr "" + +#: views.py:355 +#, python-format +msgid "add new holder for: %s" +msgstr "" + +#: views.py:356 views.py:488 +msgid "Select" +msgstr "" + +#: views.py:388 +msgid "classes" +msgstr "" + +#: views.py:390 +msgid "class" +msgstr "" + +#: views.py:409 +#, python-format +msgid "default access control lists for class: %s" +msgstr "" + +#: views.py:437 +#, python-format +msgid "permissions available to: %(actor)s for class %(class)s" +msgstr "" + +#: views.py:486 +#, python-format +msgid "add new holder for class: %s" +msgstr "" diff --git a/apps/acls/locale/es/LC_MESSAGES/django.mo b/apps/acls/locale/es/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..9570cc6260 Binary files /dev/null and b/apps/acls/locale/es/LC_MESSAGES/django.mo differ diff --git a/apps/acls/locale/es/LC_MESSAGES/django.po b/apps/acls/locale/es/LC_MESSAGES/django.po new file mode 100644 index 0000000000..59f10b37f5 --- /dev/null +++ b/apps/acls/locale/es/LC_MESSAGES/django.po @@ -0,0 +1,244 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Roberto Rosario , 2012. +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-12 00:16+0000\n" +"Last-Translator: Roberto Rosario \n" +"Language-Team: Spanish (Castilian) (http://www.transifex.net/projects/p/" +"mayan-edms/team/es/)\n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" + +#: __init__.py:14 +msgid "ACLs" +msgstr "LCA" + +#: __init__.py:15 __init__.py:23 +msgid "details" +msgstr "detalles" + +#: __init__.py:16 __init__.py:25 +msgid "grant" +msgstr "otorgar" + +#: __init__.py:17 __init__.py:26 +msgid "revoke" +msgstr "revocar" + +#: __init__.py:18 __init__.py:24 forms.py:21 +msgid "New holder" +msgstr "Nuevo titular" + +#: __init__.py:20 +msgid "Default ACLs" +msgstr "LCA por defecto" + +#: __init__.py:21 +msgid "List of classes" +msgstr "Lista de clases" + +#: __init__.py:22 +msgid "ACLs for class" +msgstr "LCA para la clase" + +#: forms.py:38 +msgid "Users" +msgstr "Usuarios" + +#: forms.py:41 +msgid "Groups" +msgstr "Grupos" + +#: forms.py:44 +msgid "Roles" +msgstr "Funciones" + +#: forms.py:47 +msgid "Special" +msgstr "Especial" + +#: managers.py:116 managers.py:128 +msgid "Insufficient access." +msgstr "Acceso insuficiente." + +#: models.py:27 models.py:69 +msgid "permission" +msgstr "permiso" + +#: models.py:53 +msgid "access entry" +msgstr "entrada de acceso" + +#: models.py:54 +msgid "access entries" +msgstr "entradas de acceso" + +#: models.py:90 +msgid "default access entry" +msgstr "entrada de acceso por defecto" + +#: models.py:91 +msgid "default access entries" +msgstr "entradas de acceso por defecto" + +#: models.py:109 +msgid "Creator" +msgstr "Creador" + +#: models.py:112 models.py:113 +msgid "creator" +msgstr "creador" + +#: permissions.py:7 permissions.py:8 +msgid "Access control lists" +msgstr "Listas de control de acceso" + +#: permissions.py:10 +msgid "Edit ACLs" +msgstr "Editar LCA" + +#: permissions.py:11 +msgid "View ACLs" +msgstr "Ver LCA" + +#: permissions.py:13 +msgid "Edit class default ACLs" +msgstr "Editar LCA por defecto de la clase" + +#: permissions.py:14 +msgid "View class default ACLs" +msgstr "Ver LCA por defecto de la clase" + +#: views.py:47 +#, python-format +msgid "access control lists for: %s" +msgstr "listas de control de acceso para: %s" + +#: views.py:49 views.py:411 +msgid "holder" +msgstr "titular" + +#: views.py:50 views.py:412 +msgid "permissions" +msgstr "permisos" + +#: views.py:97 +#, python-format +msgid "permissions available to: %(actor)s for %(obj)s" +msgstr "permisos disponibles a: %(actor)s para %(obj)s " + +#: views.py:104 views.py:444 +msgid "namespace" +msgstr "espacio de nombres" + +#: views.py:105 views.py:445 +msgid "label" +msgstr "etiqueta" + +#: views.py:107 views.py:447 +msgid "has permission" +msgstr "tiene permiso" + +#: views.py:185 views.py:279 views.py:528 views.py:608 +msgid ", " +msgstr ", " + +#: views.py:186 views.py:280 views.py:529 views.py:609 +#, python-format +msgid " for %s" +msgstr " para %s" + +#: views.py:187 views.py:530 +#, python-format +msgid " to %s" +msgstr " a %s" + +#: views.py:190 views.py:533 +#, python-format +msgid "Are you sure you wish to grant the permission %(title_suffix)s?" +msgstr "¿Está seguro que desea conceder el permiso %(title_suffix)s?" + +#: views.py:192 views.py:535 +#, python-format +msgid "Are you sure you wish to grant the permissions %(title_suffix)s?" +msgstr "¿Está seguro que desea conceder los permisos de %(title_suffix)s?" + +#: views.py:199 views.py:542 +#, python-format +msgid "Permission \"%(permission)s\" granted to %(actor)s for %(object)s." +msgstr "Permiso \"%(permission)s\" otorgado a %(actor)s para %(object)s." + +#: views.py:205 views.py:548 +#, python-format +msgid "" +"%(actor)s, already had the permission \"%(permission)s\" granted for " +"%(object)s." +msgstr "" +"%(actor)s, ya tenía el permiso \"%(permission)s\", otorgado para %(object)s." + +#: views.py:281 views.py:610 +#, python-format +msgid " from %s" +msgstr " de %s" + +#: views.py:284 views.py:613 +#, python-format +msgid "Are you sure you wish to revoke the permission %(title_suffix)s?" +msgstr "¿Está seguro que desea revocar el permiso %(title_suffix)s?" + +#: views.py:286 views.py:615 +#, python-format +msgid "Are you sure you wish to revoke the permissions %(title_suffix)s?" +msgstr "¿Está seguro de querer revocar los permisos %(title_suffix)s?" + +#: views.py:293 views.py:622 +#, python-format +msgid "Permission \"%(permission)s\" revoked of %(actor)s for %(object)s." +msgstr "Permiso \"%(permission)s\" revocado del %(actor)s para %(object)s." + +#: views.py:299 views.py:628 +#, python-format +msgid "%(actor)s, didn't had the permission \"%(permission)s\" for %(object)s." +msgstr "%(actor)s, no tenía el permiso \"%(permission)s\" para %(object)s." + +#: views.py:355 +#, python-format +msgid "add new holder for: %s" +msgstr "añadir nuevo titular para: %s" + +#: views.py:356 views.py:488 +msgid "Select" +msgstr "Seleccionar" + +#: views.py:388 +msgid "classes" +msgstr "clases" + +#: views.py:390 +msgid "class" +msgstr "clase" + +#: views.py:409 +#, python-format +msgid "default access control lists for class: %s" +msgstr "listas de control de acceso por defecto para la clase: %s" + +#: views.py:437 +#, python-format +msgid "permissions available to: %(actor)s for class %(class)s" +msgstr "permisos disponibles para: %(actor)s para la clase %(class)s " + +#: views.py:486 +#, python-format +msgid "add new holder for class: %s" +msgstr "añadir nuevo titular para la clase: %s" diff --git a/apps/acls/locale/it/LC_MESSAGES/django.mo b/apps/acls/locale/it/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..62329ea64c Binary files /dev/null and b/apps/acls/locale/it/LC_MESSAGES/django.mo differ diff --git a/apps/acls/locale/it/LC_MESSAGES/django.po b/apps/acls/locale/it/LC_MESSAGES/django.po new file mode 100644 index 0000000000..232cf6fbab --- /dev/null +++ b/apps/acls/locale/it/LC_MESSAGES/django.po @@ -0,0 +1,242 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Roberto Rosario , 2012. +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-21 00:41+0000\n" +"Last-Translator: Roberto Rosario \n" +"Language-Team: Italian (http://www.transifex.net/projects/p/mayan-edms/language/it/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: it\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" + +#: __init__.py:14 +msgid "ACLs" +msgstr "" + +#: __init__.py:15 __init__.py:23 +msgid "details" +msgstr "dettagli" + +#: __init__.py:16 __init__.py:25 +msgid "grant" +msgstr "concedere" + +#: __init__.py:17 __init__.py:26 +msgid "revoke" +msgstr "revocare" + +#: __init__.py:18 __init__.py:24 forms.py:21 +msgid "New holder" +msgstr "" + +#: __init__.py:20 +msgid "Default ACLs" +msgstr "" + +#: __init__.py:21 +msgid "List of classes" +msgstr "" + +#: __init__.py:22 +msgid "ACLs for class" +msgstr "" + +#: forms.py:38 +msgid "Users" +msgstr "Utenti" + +#: forms.py:41 +msgid "Groups" +msgstr "Gruppi" + +#: forms.py:44 +msgid "Roles" +msgstr "" + +#: forms.py:47 +msgid "Special" +msgstr "Speciale" + +#: managers.py:116 managers.py:128 +msgid "Insufficient access." +msgstr "" + +#: models.py:27 models.py:69 +msgid "permission" +msgstr "autorizzazione" + +#: models.py:53 +msgid "access entry" +msgstr "" + +#: models.py:54 +msgid "access entries" +msgstr "" + +#: models.py:90 +msgid "default access entry" +msgstr "" + +#: models.py:91 +msgid "default access entries" +msgstr "" + +#: models.py:109 +msgid "Creator" +msgstr "Creatore" + +#: models.py:112 models.py:113 +msgid "creator" +msgstr "creatore" + +#: permissions.py:7 permissions.py:8 +msgid "Access control lists" +msgstr "" + +#: permissions.py:10 +msgid "Edit ACLs" +msgstr "" + +#: permissions.py:11 +msgid "View ACLs" +msgstr "" + +#: permissions.py:13 +msgid "Edit class default ACLs" +msgstr "" + +#: permissions.py:14 +msgid "View class default ACLs" +msgstr "" + +#: views.py:47 +#, python-format +msgid "access control lists for: %s" +msgstr "" + +#: views.py:49 views.py:411 +msgid "holder" +msgstr "" + +#: views.py:50 views.py:412 +msgid "permissions" +msgstr "le autorizzazioni" + +#: views.py:97 +#, python-format +msgid "permissions available to: %(actor)s for %(obj)s" +msgstr "autorizzazioni disponibili per: %(actor)s per %(obj)s " + +#: views.py:104 views.py:444 +msgid "namespace" +msgstr "" + +#: views.py:105 views.py:445 +msgid "label" +msgstr "" + +#: views.py:107 views.py:447 +msgid "has permission" +msgstr "ha l'autorizzazione" + +#: views.py:185 views.py:279 views.py:528 views.py:608 +msgid ", " +msgstr ", " + +#: views.py:186 views.py:280 views.py:529 views.py:609 +#, python-format +msgid " for %s" +msgstr "per %s" + +#: views.py:187 views.py:530 +#, python-format +msgid " to %s" +msgstr "a %s" + +#: views.py:190 views.py:533 +#, python-format +msgid "Are you sure you wish to grant the permission %(title_suffix)s?" +msgstr "Sei sicuro di voler concedere l'autorizzazione %(title_suffix)s?" + +#: views.py:192 views.py:535 +#, python-format +msgid "Are you sure you wish to grant the permissions %(title_suffix)s?" +msgstr "Sei sicuro di voler concedere permessi %(title_suffix)s?" + +#: views.py:199 views.py:542 +#, python-format +msgid "Permission \"%(permission)s\" granted to %(actor)s for %(object)s." +msgstr "" + +#: views.py:205 views.py:548 +#, python-format +msgid "" +"%(actor)s, already had the permission \"%(permission)s\" granted for " +"%(object)s." +msgstr "" + +#: views.py:281 views.py:610 +#, python-format +msgid " from %s" +msgstr "da %s" + +#: views.py:284 views.py:613 +#, python-format +msgid "Are you sure you wish to revoke the permission %(title_suffix)s?" +msgstr "Sei sicuro di voler revocare l'autorizzazione %(title_suffix)s?" + +#: views.py:286 views.py:615 +#, python-format +msgid "Are you sure you wish to revoke the permissions %(title_suffix)s?" +msgstr "Sei sicuro di voler revocare permessi %(title_suffix)s?" + +#: views.py:293 views.py:622 +#, python-format +msgid "Permission \"%(permission)s\" revoked of %(actor)s for %(object)s." +msgstr "" + +#: views.py:299 views.py:628 +#, python-format +msgid "%(actor)s, didn't had the permission \"%(permission)s\" for %(object)s." +msgstr "" + +#: views.py:355 +#, python-format +msgid "add new holder for: %s" +msgstr "" + +#: views.py:356 views.py:488 +msgid "Select" +msgstr "Selezionare" + +#: views.py:388 +msgid "classes" +msgstr "" + +#: views.py:390 +msgid "class" +msgstr "" + +#: views.py:409 +#, python-format +msgid "default access control lists for class: %s" +msgstr "" + +#: views.py:437 +#, python-format +msgid "permissions available to: %(actor)s for class %(class)s" +msgstr "" + +#: views.py:486 +#, python-format +msgid "add new holder for class: %s" +msgstr "" diff --git a/apps/acls/locale/pl/LC_MESSAGES/django.mo b/apps/acls/locale/pl/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..a0ad603458 Binary files /dev/null and b/apps/acls/locale/pl/LC_MESSAGES/django.mo differ diff --git a/apps/acls/locale/pl/LC_MESSAGES/django.po b/apps/acls/locale/pl/LC_MESSAGES/django.po new file mode 100644 index 0000000000..00b723be0d --- /dev/null +++ b/apps/acls/locale/pl/LC_MESSAGES/django.po @@ -0,0 +1,241 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-01-02 09:45+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Polish (http://www.transifex.net/projects/p/mayan-edms/language/pl/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pl\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" + +#: __init__.py:14 +msgid "ACLs" +msgstr "" + +#: __init__.py:15 __init__.py:23 +msgid "details" +msgstr "" + +#: __init__.py:16 __init__.py:25 +msgid "grant" +msgstr "" + +#: __init__.py:17 __init__.py:26 +msgid "revoke" +msgstr "" + +#: __init__.py:18 __init__.py:24 forms.py:21 +msgid "New holder" +msgstr "" + +#: __init__.py:20 +msgid "Default ACLs" +msgstr "" + +#: __init__.py:21 +msgid "List of classes" +msgstr "" + +#: __init__.py:22 +msgid "ACLs for class" +msgstr "" + +#: forms.py:38 +msgid "Users" +msgstr "" + +#: forms.py:41 +msgid "Groups" +msgstr "" + +#: forms.py:44 +msgid "Roles" +msgstr "" + +#: forms.py:47 +msgid "Special" +msgstr "" + +#: managers.py:116 managers.py:128 +msgid "Insufficient access." +msgstr "" + +#: models.py:27 models.py:69 +msgid "permission" +msgstr "" + +#: models.py:53 +msgid "access entry" +msgstr "" + +#: models.py:54 +msgid "access entries" +msgstr "" + +#: models.py:90 +msgid "default access entry" +msgstr "" + +#: models.py:91 +msgid "default access entries" +msgstr "" + +#: models.py:109 +msgid "Creator" +msgstr "" + +#: models.py:112 models.py:113 +msgid "creator" +msgstr "" + +#: permissions.py:7 permissions.py:8 +msgid "Access control lists" +msgstr "" + +#: permissions.py:10 +msgid "Edit ACLs" +msgstr "" + +#: permissions.py:11 +msgid "View ACLs" +msgstr "" + +#: permissions.py:13 +msgid "Edit class default ACLs" +msgstr "" + +#: permissions.py:14 +msgid "View class default ACLs" +msgstr "" + +#: views.py:47 +#, python-format +msgid "access control lists for: %s" +msgstr "" + +#: views.py:49 views.py:411 +msgid "holder" +msgstr "" + +#: views.py:50 views.py:412 +msgid "permissions" +msgstr "" + +#: views.py:97 +#, python-format +msgid "permissions available to: %(actor)s for %(obj)s" +msgstr "" + +#: views.py:104 views.py:444 +msgid "namespace" +msgstr "" + +#: views.py:105 views.py:445 +msgid "label" +msgstr "" + +#: views.py:107 views.py:447 +msgid "has permission" +msgstr "" + +#: views.py:185 views.py:279 views.py:528 views.py:608 +msgid ", " +msgstr "" + +#: views.py:186 views.py:280 views.py:529 views.py:609 +#, python-format +msgid " for %s" +msgstr "" + +#: views.py:187 views.py:530 +#, python-format +msgid " to %s" +msgstr "" + +#: views.py:190 views.py:533 +#, python-format +msgid "Are you sure you wish to grant the permission %(title_suffix)s?" +msgstr "" + +#: views.py:192 views.py:535 +#, python-format +msgid "Are you sure you wish to grant the permissions %(title_suffix)s?" +msgstr "" + +#: views.py:199 views.py:542 +#, python-format +msgid "Permission \"%(permission)s\" granted to %(actor)s for %(object)s." +msgstr "" + +#: views.py:205 views.py:548 +#, python-format +msgid "" +"%(actor)s, already had the permission \"%(permission)s\" granted for " +"%(object)s." +msgstr "" + +#: views.py:281 views.py:610 +#, python-format +msgid " from %s" +msgstr "" + +#: views.py:284 views.py:613 +#, python-format +msgid "Are you sure you wish to revoke the permission %(title_suffix)s?" +msgstr "" + +#: views.py:286 views.py:615 +#, python-format +msgid "Are you sure you wish to revoke the permissions %(title_suffix)s?" +msgstr "" + +#: views.py:293 views.py:622 +#, python-format +msgid "Permission \"%(permission)s\" revoked of %(actor)s for %(object)s." +msgstr "" + +#: views.py:299 views.py:628 +#, python-format +msgid "%(actor)s, didn't had the permission \"%(permission)s\" for %(object)s." +msgstr "" + +#: views.py:355 +#, python-format +msgid "add new holder for: %s" +msgstr "" + +#: views.py:356 views.py:488 +msgid "Select" +msgstr "" + +#: views.py:388 +msgid "classes" +msgstr "" + +#: views.py:390 +msgid "class" +msgstr "" + +#: views.py:409 +#, python-format +msgid "default access control lists for class: %s" +msgstr "" + +#: views.py:437 +#, python-format +msgid "permissions available to: %(actor)s for class %(class)s" +msgstr "" + +#: views.py:486 +#, python-format +msgid "add new holder for class: %s" +msgstr "" diff --git a/apps/acls/locale/pt/LC_MESSAGES/django.mo b/apps/acls/locale/pt/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..e9bdbc03ba Binary files /dev/null and b/apps/acls/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/acls/locale/pt/LC_MESSAGES/django.po b/apps/acls/locale/pt/LC_MESSAGES/django.po new file mode 100644 index 0000000000..c9248d8cb3 --- /dev/null +++ b/apps/acls/locale/pt/LC_MESSAGES/django.po @@ -0,0 +1,242 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-02 18:20+0000\n" +"Last-Translator: Roberto Rosario \n" +"Language-Team: Portuguese (http://www.transifex.net/projects/p/mayan-edms/" +"team/pt/)\n" +"Language: pt\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" + +#: __init__.py:14 +msgid "ACLs" +msgstr "" + +#: __init__.py:15 __init__.py:23 +msgid "details" +msgstr "" + +#: __init__.py:16 __init__.py:25 +msgid "grant" +msgstr "" + +#: __init__.py:17 __init__.py:26 +msgid "revoke" +msgstr "" + +#: __init__.py:18 __init__.py:24 forms.py:21 +msgid "New holder" +msgstr "" + +#: __init__.py:20 +msgid "Default ACLs" +msgstr "" + +#: __init__.py:21 +msgid "List of classes" +msgstr "" + +#: __init__.py:22 +msgid "ACLs for class" +msgstr "" + +#: forms.py:38 +msgid "Users" +msgstr "" + +#: forms.py:41 +msgid "Groups" +msgstr "" + +#: forms.py:44 +msgid "Roles" +msgstr "" + +#: forms.py:47 +msgid "Special" +msgstr "" + +#: managers.py:116 managers.py:128 +msgid "Insufficient access." +msgstr "" + +#: models.py:27 models.py:69 +msgid "permission" +msgstr "" + +#: models.py:53 +msgid "access entry" +msgstr "" + +#: models.py:54 +msgid "access entries" +msgstr "" + +#: models.py:90 +msgid "default access entry" +msgstr "" + +#: models.py:91 +msgid "default access entries" +msgstr "" + +#: models.py:109 +msgid "Creator" +msgstr "" + +#: models.py:112 models.py:113 +msgid "creator" +msgstr "" + +#: permissions.py:7 permissions.py:8 +msgid "Access control lists" +msgstr "" + +#: permissions.py:10 +msgid "Edit ACLs" +msgstr "" + +#: permissions.py:11 +msgid "View ACLs" +msgstr "" + +#: permissions.py:13 +msgid "Edit class default ACLs" +msgstr "" + +#: permissions.py:14 +msgid "View class default ACLs" +msgstr "" + +#: views.py:47 +#, python-format +msgid "access control lists for: %s" +msgstr "" + +#: views.py:49 views.py:411 +msgid "holder" +msgstr "" + +#: views.py:50 views.py:412 +msgid "permissions" +msgstr "" + +#: views.py:97 +#, python-format +msgid "permissions available to: %(actor)s for %(obj)s" +msgstr "" + +#: views.py:104 views.py:444 +msgid "namespace" +msgstr "" + +#: views.py:105 views.py:445 +msgid "label" +msgstr "" + +#: views.py:107 views.py:447 +msgid "has permission" +msgstr "" + +#: views.py:185 views.py:279 views.py:528 views.py:608 +msgid ", " +msgstr "" + +#: views.py:186 views.py:280 views.py:529 views.py:609 +#, python-format +msgid " for %s" +msgstr "" + +#: views.py:187 views.py:530 +#, python-format +msgid " to %s" +msgstr "" + +#: views.py:190 views.py:533 +#, python-format +msgid "Are you sure you wish to grant the permission %(title_suffix)s?" +msgstr "" + +#: views.py:192 views.py:535 +#, python-format +msgid "Are you sure you wish to grant the permissions %(title_suffix)s?" +msgstr "" + +#: views.py:199 views.py:542 +#, python-format +msgid "Permission \"%(permission)s\" granted to %(actor)s for %(object)s." +msgstr "" + +#: views.py:205 views.py:548 +#, python-format +msgid "" +"%(actor)s, already had the permission \"%(permission)s\" granted for " +"%(object)s." +msgstr "" + +#: views.py:281 views.py:610 +#, python-format +msgid " from %s" +msgstr "" + +#: views.py:284 views.py:613 +#, python-format +msgid "Are you sure you wish to revoke the permission %(title_suffix)s?" +msgstr "" + +#: views.py:286 views.py:615 +#, python-format +msgid "Are you sure you wish to revoke the permissions %(title_suffix)s?" +msgstr "" + +#: views.py:293 views.py:622 +#, python-format +msgid "Permission \"%(permission)s\" revoked of %(actor)s for %(object)s." +msgstr "" + +#: views.py:299 views.py:628 +#, python-format +msgid "%(actor)s, didn't had the permission \"%(permission)s\" for %(object)s." +msgstr "" + +#: views.py:355 +#, python-format +msgid "add new holder for: %s" +msgstr "" + +#: views.py:356 views.py:488 +msgid "Select" +msgstr "" + +#: views.py:388 +msgid "classes" +msgstr "" + +#: views.py:390 +msgid "class" +msgstr "" + +#: views.py:409 +#, python-format +msgid "default access control lists for class: %s" +msgstr "" + +#: views.py:437 +#, python-format +msgid "permissions available to: %(actor)s for class %(class)s" +msgstr "" + +#: views.py:486 +#, python-format +msgid "add new holder for class: %s" +msgstr "" diff --git a/apps/acls/locale/ru/LC_MESSAGES/django.mo b/apps/acls/locale/ru/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..728a991b2b Binary files /dev/null and b/apps/acls/locale/ru/LC_MESSAGES/django.mo differ diff --git a/apps/acls/locale/ru/LC_MESSAGES/django.po b/apps/acls/locale/ru/LC_MESSAGES/django.po new file mode 100644 index 0000000000..6ce319b72c --- /dev/null +++ b/apps/acls/locale/ru/LC_MESSAGES/django.po @@ -0,0 +1,242 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Sergey Glita , 2012. +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-27 04:26+0000\n" +"Last-Translator: Sergey Glita \n" +"Language-Team: Russian (http://www.transifex.net/projects/p/mayan-edms/language/ru/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ru\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" + +#: __init__.py:14 +msgid "ACLs" +msgstr "Списки ACL" + +#: __init__.py:15 __init__.py:23 +msgid "details" +msgstr "детали" + +#: __init__.py:16 __init__.py:25 +msgid "grant" +msgstr "предоставить" + +#: __init__.py:17 __init__.py:26 +msgid "revoke" +msgstr "отозвать" + +#: __init__.py:18 __init__.py:24 forms.py:21 +msgid "New holder" +msgstr "Новый владелец" + +#: __init__.py:20 +msgid "Default ACLs" +msgstr "ACL по умолчанию" + +#: __init__.py:21 +msgid "List of classes" +msgstr "Список классов" + +#: __init__.py:22 +msgid "ACLs for class" +msgstr "ACL для класса" + +#: forms.py:38 +msgid "Users" +msgstr "Пользователи" + +#: forms.py:41 +msgid "Groups" +msgstr "Группы" + +#: forms.py:44 +msgid "Roles" +msgstr "Роли" + +#: forms.py:47 +msgid "Special" +msgstr "Специальный" + +#: managers.py:116 managers.py:128 +msgid "Insufficient access." +msgstr "Недостаточный доступ." + +#: models.py:27 models.py:69 +msgid "permission" +msgstr "разрешение" + +#: models.py:53 +msgid "access entry" +msgstr "запись доступа" + +#: models.py:54 +msgid "access entries" +msgstr "записи доступа" + +#: models.py:90 +msgid "default access entry" +msgstr "запись доступа по умолчанию" + +#: models.py:91 +msgid "default access entries" +msgstr "записи доступа по умолчанию" + +#: models.py:109 +msgid "Creator" +msgstr "Создатель" + +#: models.py:112 models.py:113 +msgid "creator" +msgstr "создатель" + +#: permissions.py:7 permissions.py:8 +msgid "Access control lists" +msgstr "Списки контроля доступа" + +#: permissions.py:10 +msgid "Edit ACLs" +msgstr "Редактировать списки ACL" + +#: permissions.py:11 +msgid "View ACLs" +msgstr "Просмотр списков ACL" + +#: permissions.py:13 +msgid "Edit class default ACLs" +msgstr "Редактировать списки ACL класса по умолчанию" + +#: permissions.py:14 +msgid "View class default ACLs" +msgstr "Просмотр списков ACL класса по умолчанию" + +#: views.py:47 +#, python-format +msgid "access control lists for: %s" +msgstr "списки контроля доступа для %s" + +#: views.py:49 views.py:411 +msgid "holder" +msgstr "владелец" + +#: views.py:50 views.py:412 +msgid "permissions" +msgstr "разрешения" + +#: views.py:97 +#, python-format +msgid "permissions available to: %(actor)s for %(obj)s" +msgstr "разрешения, доступные %(actor)s для %(obj)s" + +#: views.py:104 views.py:444 +msgid "namespace" +msgstr "пространство имен" + +#: views.py:105 views.py:445 +msgid "label" +msgstr "этикетка" + +#: views.py:107 views.py:447 +msgid "has permission" +msgstr "имеет разрешение" + +#: views.py:185 views.py:279 views.py:528 views.py:608 +msgid ", " +msgstr "," + +#: views.py:186 views.py:280 views.py:529 views.py:609 +#, python-format +msgid " for %s" +msgstr "для %s" + +#: views.py:187 views.py:530 +#, python-format +msgid " to %s" +msgstr "до %s" + +#: views.py:190 views.py:533 +#, python-format +msgid "Are you sure you wish to grant the permission %(title_suffix)s?" +msgstr "Вы действительно хотите предоставить разрешение %(title_suffix)s?" + +#: views.py:192 views.py:535 +#, python-format +msgid "Are you sure you wish to grant the permissions %(title_suffix)s?" +msgstr "Вы уверены, что хотите предоставить разрешения %(title_suffix)s?" + +#: views.py:199 views.py:542 +#, python-format +msgid "Permission \"%(permission)s\" granted to %(actor)s for %(object)s." +msgstr "Право \"%(permission)s\" проедоставлено %(actor)s для %(object)s." + +#: views.py:205 views.py:548 +#, python-format +msgid "" +"%(actor)s, already had the permission \"%(permission)s\" granted for " +"%(object)s." +msgstr "%(actor)s, уже имеет право \"%(permission)s\" для %(object)s." + +#: views.py:281 views.py:610 +#, python-format +msgid " from %s" +msgstr "от%s" + +#: views.py:284 views.py:613 +#, python-format +msgid "Are you sure you wish to revoke the permission %(title_suffix)s?" +msgstr "Вы уверены, что хотите отменить разрешение %(title_suffix)s?" + +#: views.py:286 views.py:615 +#, python-format +msgid "Are you sure you wish to revoke the permissions %(title_suffix)s?" +msgstr "Вы уверены, что хотите отменить разрешение %(title_suffix)s?" + +#: views.py:293 views.py:622 +#, python-format +msgid "Permission \"%(permission)s\" revoked of %(actor)s for %(object)s." +msgstr "Право \"%(permission)s\" для %(object)s отозвано у %(actor)s." + +#: views.py:299 views.py:628 +#, python-format +msgid "%(actor)s, didn't had the permission \"%(permission)s\" for %(object)s." +msgstr "%(actor)s не имеет права \"%(permission)s\" для %(object)s." + +#: views.py:355 +#, python-format +msgid "add new holder for: %s" +msgstr "добавить нового владельца для %s" + +#: views.py:356 views.py:488 +msgid "Select" +msgstr "Выбор" + +#: views.py:388 +msgid "classes" +msgstr "классы" + +#: views.py:390 +msgid "class" +msgstr "класс" + +#: views.py:409 +#, python-format +msgid "default access control lists for class: %s" +msgstr "списки контроля доступа умолчанию для класса %s" + +#: views.py:437 +#, python-format +msgid "permissions available to: %(actor)s for class %(class)s" +msgstr "разрешения доступные %(actor)s для класса %(class)s" + +#: views.py:486 +#, python-format +msgid "add new holder for class: %s" +msgstr "добавить нового владельца для класса %s" diff --git a/apps/acls/managers.py b/apps/acls/managers.py new file mode 100644 index 0000000000..42ad2f7bc4 --- /dev/null +++ b/apps/acls/managers.py @@ -0,0 +1,325 @@ +from __future__ import absolute_import + +import logging + +from django.db import models +from django.utils.translation import ugettext +from django.contrib.contenttypes.models import ContentType +from django.contrib.auth.models import User +from django.core.exceptions import PermissionDenied +from django.core.urlresolvers import reverse +from django.db.models import Q + +from common.models import AnonymousUserSingleton +from permissions.models import Permission, RoleMember + +from .classes import AccessHolder, ClassAccessHolder, get_source_object + +logger = logging.getLogger(__name__) + + +class AccessEntryManager(models.Manager): + """ + Implement a 3 tier permission system, involving a permissions, an actor + and an object + """ + def grant(self, permission, actor, obj): + """ + Grant a permission (what), (to) an actor, (on) a specific object + """ + obj = get_source_object(obj) + actor = get_source_object(actor) + + access_entry, created = self.model.objects.get_or_create( + permission=permission, + holder_type=ContentType.objects.get_for_model(actor), + holder_id=actor.pk, + content_type=ContentType.objects.get_for_model(obj), + object_id=obj.pk + ) + return created + + def revoke(self, permission, actor, obj): + """ + Revoke a permission (what), (from) an actor, (on) a specific object + """ + obj = get_source_object(obj) + actor = get_source_object(actor) + + try: + access_entry = self.model.objects.get( + permission=permission, + holder_type=ContentType.objects.get_for_model(actor), + holder_id=actor.pk, + content_type=ContentType.objects.get_for_model(obj), + object_id=obj.pk + ) + except self.model.DoesNotExist: + return False + else: + access_entry.delete() + return True + + def has_access(self, permission, actor, obj, db_only=False): + """ + Returns whether an actor has a specific permission for an object + """ + obj = get_source_object(obj) + actor = get_source_object(actor) + + if isinstance(actor, User) and db_only == False: + # db_only causes the return of only the stored permissions + # and not the perceived permissions for an actor + if actor.is_superuser or actor.is_staff: + return True + + actor = AnonymousUserSingleton.objects.passthru_check(actor) + + try: + self.model.objects.get( + permission=permission.get_stored_permission(), + holder_type=ContentType.objects.get_for_model(actor), + holder_id=actor.pk, + content_type=ContentType.objects.get_for_model(obj), + object_id=obj.pk + ) + except self.model.DoesNotExist: + # If not check if the actor's memberships is one of + # the access's holder? + roles = RoleMember.objects.get_roles_for_member(actor) + + if isinstance(actor, User): + groups = actor.groups.all() + else: + groups = [] + + for membership in list(set(roles) | set(groups)): + if self.has_access(permission, membership, obj, db_only): + return True + + logger.debug('Fallthru') + return False + else: + return True + + def check_access(self, permission, actor, obj): + # TODO: Merge with has_access + obj = get_source_object(obj) + actor = get_source_object(actor) + + if self.has_access(permission, actor, obj): + return True + else: + raise PermissionDenied(ugettext(u'Insufficient access.')) + + def check_accesses(self, permission_list, actor, obj): + """ + Returns whether an actor has at least one of a list of permissions for an object + """ + obj = get_source_object(obj) + actor = get_source_object(actor) + for permission in permission_list: + if self.has_access(permission, actor, obj): + return True + + raise PermissionDenied(ugettext(u'Insufficient access.')) + + def get_allowed_class_objects(self, permission, actor, cls, related=None): + logger.debug('related: %s' % related) + + actor = AnonymousUserSingleton.objects.passthru_check(actor) + actor_type = ContentType.objects.get_for_model(actor) + content_type = ContentType.objects.get_for_model(cls) + + # Calculate actor role membership ACL query + total_queries = None + for role in RoleMember.objects.get_roles_for_member(actor): + role_type = ContentType.objects.get_for_model(role) + if related: + query = Q(holder_type=role_type, holder_id=role.pk, permission=permission.get_stored_permission) + else: + query = Q(holder_type=role_type, holder_id=role.pk, content_type=content_type, permission=permission.get_stored_permission) + if total_queries is None: + total_queries = query + else: + total_queries = total_queries | query + + # Calculate actor group membership ACL query + if isinstance(actor, User): + groups = actor.groups.all() + else: + groups = [] + + for group in groups: + group_type = ContentType.objects.get_for_model(group) + if related: + query = Q(holder_type=group_type, holder_id=group.pk, permission=permission.get_stored_permission) + else: + query = Q(holder_type=group_type, holder_id=group.pk, content_type=content_type, permission=permission.get_stored_permission) + if total_queries is None: + total_queries = query + else: + total_queries = total_queries | query + + if related: + actor_query = Q(holder_type=actor_type, holder_id=actor.pk, permission=permission.get_stored_permission) + master_list = [obj.content_object for obj in self.model.objects.select_related().filter(actor_query | total_queries)] + logger.debug('master_list: %s' % master_list) + # TODO: update to use Q objects and check performance diff + # kwargs = {'%s__in' % related: master_list} + # Q(**kwargs) + return (obj for obj in cls.objects.all() if getattr(obj, related) in master_list) + else: + actor_query = Q(holder_type=actor_type, holder_id=actor.pk, content_type=content_type, permission=permission.get_stored_permission) + return (obj.content_object for obj in self.model.objects.filter(actor_query | total_queries)) + + def get_acl_url(self, obj): + content_type = ContentType.objects.get_for_model(obj) + return reverse('acl_list', args=[content_type.app_label, content_type.model, obj.pk]) + + def get_new_holder_url(self, obj): + content_type = ContentType.objects.get_for_model(obj) + return reverse('acl_new_holder_for', args=[content_type.app_label, content_type.model, obj.pk]) + + def get_holders_for(self, obj): + content_type = ContentType.objects.get_for_model(obj) + holder_list = [] + for access_entry in self.model.objects.filter(content_type=content_type, object_id=obj.pk): + if access_entry.holder_object: + # Don't add references to non existant content type objects + entry = AccessHolder.encapsulate(access_entry.holder_object) + + if entry not in holder_list: + holder_list.append(entry) + + return holder_list + + def get_holder_permissions_for(self, obj, actor, db_only=False): + """ + Returns a list of actors that hold at least one permission for + a specific object + """ + logger.debug('obj: %s' % obj) + logger.debug('actor: %s' % actor) + + if isinstance(actor, User) and db_only == False: + if actor.is_superuser or actor.is_staff: + return Permission.objects.all() + + actor_type = ContentType.objects.get_for_model(actor) + content_type = ContentType.objects.get_for_model(obj) + return (access.permission for access in self.model.objects.filter(content_type=content_type, object_id=obj.pk, holder_type=actor_type, holder_id=actor.pk)) + + def filter_objects_by_access(self, permission, actor, object_list, exception_on_empty=False, related=None): + """ + Filter a list of objects or a QuerySet elements depending on + whether the actor holds the specified permission + """ + logger.debug('exception_on_empty: %s' % exception_on_empty) + logger.debug('object_list: %s' % object_list) + + if isinstance(actor, User): + if actor.is_superuser or actor.is_staff: + return object_list + + try: + if object_list.count() == 0: + return object_list + except TypeError: + # object_list is not a queryset + if len(object_list) == 0: + return object_list + + try: + # Try to process as a QuerySet + qs = object_list.filter(pk__in=[obj.pk for obj in self.get_allowed_class_objects(permission, actor, object_list[0].__class__, related)]) + logger.debug('qs: %s' % qs) + + if qs.count() == 0 and exception_on_empty == True: + raise PermissionDenied + + return qs + except AttributeError: + # Fallback to a filtered list + object_list = list(set(object_list) & set(self.get_allowed_class_objects(permission, actor, object_list[0].__class__, related))) + logger.debug('object_list: %s' % object_list) + if len(object_list) == 0 and exception_on_empty == True: + raise PermissionDenied + + return object_list + + +class DefaultAccessEntryManager(models.Manager): + """ + Implement a 3 tier permission system, involving a permission, an actor + and a class or content type. This model keeps track of the access + control lists that will be added when an instance of the recorded + content type is created. + """ + def get_holders_for(self, cls): + cls = get_source_object(cls) + content_type = ContentType.objects.get_for_model(cls) + holder_list = [] + for access_entry in self.model.objects.filter(content_type=content_type): + if access_entry.holder_object: + # Don't add references to non existant content type objects + entry = ClassAccessHolder.encapsulate(access_entry.holder_object) + + if entry not in holder_list: + holder_list.append(entry) + + return holder_list + + def has_access(self, permission, actor, cls): + if isinstance(actor, User): + if actor.is_superuser or actor.is_staff: + return True + + try: + self.model.objects.get( + permission=permission.get_stored_permission(), + holder_type=ContentType.objects.get_for_model(actor), + holder_id=actor.pk, + content_type=ContentType.objects.get_for_model(cls), + ) + except self.model.DoesNotExist: + return False + else: + return True + + def grant(self, permission, actor, cls): + """ + Grant a permission (what), (to) an actor, (on) a specific class + """ + access_entry, created = self.model.objects.get_or_create( + permission=permission, + holder_type=ContentType.objects.get_for_model(actor), + holder_id=actor.pk, + content_type=ContentType.objects.get_for_model(cls), + ) + return created + + def revoke(self, permission, actor, cls): + """ + Revoke a permission (what), (from) an actor, (on) a specific class + """ + try: + access_entry = self.model.objects.get( + permission=permission, + holder_type=ContentType.objects.get_for_model(actor), + holder_id=actor.pk, + content_type=ContentType.objects.get_for_model(cls), + ) + access_entry.delete() + return True + except self.model.DoesNotExist: + return False + + def get_holder_permissions_for(self, cls, actor): + if isinstance(actor, User): + if actor.is_superuser or actor.is_staff: + return Permission.objects.all() + + actor_type = ContentType.objects.get_for_model(actor) + content_type = ContentType.objects.get_for_model(cls) + return [access.permission for access in self.model.objects.filter(content_type=content_type, holder_type=actor_type, holder_id=actor.pk)] diff --git a/apps/acls/models.py b/apps/acls/models.py new file mode 100644 index 0000000000..6637c12a24 --- /dev/null +++ b/apps/acls/models.py @@ -0,0 +1,112 @@ +from __future__ import absolute_import + +import logging + +from django.db import models +from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import ugettext +from django.contrib.contenttypes.models import ContentType +from django.contrib.contenttypes import generic + +from permissions.models import StoredPermission +from common.models import Singleton, SingletonManager + +from .managers import AccessEntryManager, DefaultAccessEntryManager +from .classes import AccessObjectClass +from .api import get_classes + +logger = logging.getLogger(__name__) + + +class AccessEntry(models.Model): + """ + Model that hold the permission, object, actor relationship + """ + permission = models.ForeignKey(StoredPermission, verbose_name=_(u'permission')) + + holder_type = models.ForeignKey( + ContentType, + related_name='access_holder', + limit_choices_to={'model__in': ('user', 'group', 'role')} + ) + holder_id = models.PositiveIntegerField() + holder_object = generic.GenericForeignKey( + ct_field='holder_type', + fk_field='holder_id' + ) + + content_type = models.ForeignKey( + ContentType, + related_name='object_content_type' + ) + object_id = models.PositiveIntegerField() + content_object = generic.GenericForeignKey( + ct_field='content_type', + fk_field='object_id' + ) + + objects = AccessEntryManager() + + class Meta: + verbose_name = _(u'access entry') + verbose_name_plural = _(u'access entries') + + def __unicode__(self): + return u'%s: %s' % (self.content_type, self.content_object) + + +class DefaultAccessEntry(models.Model): + """ + Model that holds the permission, class, actor relationship, that will + be added upon the creation of an instance of said class + """ + @classmethod + def get_classes(cls): + return [AccessObjectClass.encapsulate(cls) for cls in get_classes()] + + permission = models.ForeignKey(StoredPermission, verbose_name=_(u'permission')) + + holder_type = models.ForeignKey( + ContentType, + limit_choices_to={'model__in': ('user', 'group', 'role')}, + related_name='default_access_entry_holder' + ) + holder_id = models.PositiveIntegerField() + holder_object = generic.GenericForeignKey( + ct_field='holder_type', + fk_field='holder_id' + ) + + content_type = models.ForeignKey( + ContentType, + related_name='default_access_entry_class' + ) + + objects = DefaultAccessEntryManager() + + class Meta: + verbose_name = _(u'default access entry') + verbose_name_plural = _(u'default access entries') + + def __unicode__(self): + return u'%s: %s' % (self.content_type, self.content_object) + + +class CreatorSingletonManager(SingletonManager): + def passthru_check(self, holder, creator=None): + if isinstance(holder, self.model): + # TODO: raise explicit error if is instance and creator=None + return creator + else: + return holder + + +class CreatorSingleton(Singleton): + objects = CreatorSingletonManager() + + def __unicode__(self): + return ugettext('Creator') + + class Meta: + verbose_name = _(u'creator') + verbose_name_plural = _(u'creator') diff --git a/apps/acls/permissions.py b/apps/acls/permissions.py new file mode 100644 index 0000000000..2ab114070a --- /dev/null +++ b/apps/acls/permissions.py @@ -0,0 +1,14 @@ +from __future__ import absolute_import + +from django.utils.translation import ugettext_lazy as _ + +from permissions.models import PermissionNamespace, Permission + +acls_namespace = PermissionNamespace('acls', _(u'Access control lists')) +acls_setup_namespace = PermissionNamespace('acls_setup', _(u'Access control lists')) + +ACLS_EDIT_ACL = Permission.objects.register(acls_namespace, 'acl_edit', _(u'Edit ACLs')) +ACLS_VIEW_ACL = Permission.objects.register(acls_namespace, 'acl_view', _(u'View ACLs')) + +ACLS_CLASS_EDIT_ACL = Permission.objects.register(acls_setup_namespace, 'acl_class_edit', _(u'Edit class default ACLs')) +ACLS_CLASS_VIEW_ACL = Permission.objects.register(acls_setup_namespace, 'acl_class_view', _(u'View class default ACLs')) diff --git a/apps/acls/static/images/icons/lock.png b/apps/acls/static/images/icons/lock.png new file mode 100644 index 0000000000..ceebba9df3 Binary files /dev/null and b/apps/acls/static/images/icons/lock.png differ diff --git a/apps/exporter/__init__.py b/apps/acls/templatetags/__init__.py similarity index 100% rename from apps/exporter/__init__.py rename to apps/acls/templatetags/__init__.py diff --git a/apps/acls/templatetags/acl_tags.py b/apps/acls/templatetags/acl_tags.py new file mode 100644 index 0000000000..68ce30263d --- /dev/null +++ b/apps/acls/templatetags/acl_tags.py @@ -0,0 +1,71 @@ +import logging + +from django.core.exceptions import PermissionDenied +from django.template import (TemplateSyntaxError, Library, + Node, Variable, VariableDoesNotExist) + + +from acls.models import AccessEntry + +register = Library() +logger = logging.getLogger(__name__) + + +class CheckAccessNode(Node): + def __init__(self, permission_list=None, requester=None, obj=None, *args, **kwargs): + self.requester = requester + self.permission_list = permission_list + self.obj = obj + + def render(self, context): + permission_list = Variable(self.permission_list).resolve(context) + logger.debug('permission_list: %s' % u','.join([unicode(p) for p in permission_list])) + + try: + # Check access_object, useful for document_page views + obj = Variable('access_object').resolve(context) + logger.debug('access_object: %s' % obj) + except VariableDoesNotExist: + try: + obj = Variable(self.obj).resolve(context) + logger.debug('obj: %s' % obj) + except VariableDoesNotExist: + context[u'access'] = False + logger.debug('no obj, access False') + return u'' + + if not permission_list: + # There is no permissions list to check against which means + # this link is available for all + context[u'access'] = True + return u'' + + requester = Variable(self.requester).resolve(context) + logger.debug('requester: %s' % requester) + + if obj: + try: + AccessEntry.objects.check_accesses(permission_list, requester, obj) + except PermissionDenied: + context[u'access'] = False + logger.debug('access: False') + return u'' + else: + context[u'access'] = True + logger.debug('access: True') + return u'' + else: + context[u'access'] = False + logger.debug('No object, access: False') + return u'' + + +@register.tag +def check_access(parser, token): + try: + # Splitting by None == splitting by spaces. + tag_name, args = token.contents.split(None, 1) + except ValueError: + raise TemplateSyntaxError(u'%r tag requires arguments' % token.contents.split()[0]) + + return CheckAccessNode(*args.split()) diff --git a/apps/acls/urls.py b/apps/acls/urls.py new file mode 100644 index 0000000000..ba9aa8be92 --- /dev/null +++ b/apps/acls/urls.py @@ -0,0 +1,20 @@ +from django.conf.urls.defaults import patterns, url + +urlpatterns = patterns('acls.views', + url(r'^new_holder_for/(?P[-\w]+)/(?P[-\w]+)/(?P\d+)/$', 'acl_new_holder_for', (), 'acl_new_holder_for'), + url(r'^list_for/(?P[-\w]+)/(?P[-\w]+)/(?P\d+)/$', 'acl_list', (), 'acl_list'), + url(r'^details/(?P[.\w]+)/holder/(?P[.\w]+)/$', 'acl_detail', (), 'acl_detail'), + url(r'^holder/new/(?P[.\w]+)/$', 'acl_holder_new', (), 'acl_holder_new'), + + url(r'^multiple/grant/$', 'acl_grant', (), 'acl_multiple_grant'), + url(r'^multiple/revoke/$', 'acl_revoke', (), 'acl_multiple_revoke'), + + url(r'^class/$', 'acl_setup_valid_classes', (), 'acl_setup_valid_classes'), + url(r'^class/details/(?P[.\w]+)/holder/(?P[.\w]+)/$', 'acl_class_acl_detail', (), 'acl_class_acl_detail'), + url(r'^class/list_for/(?P[.\w]+)/$', 'acl_class_acl_list', (), 'acl_class_acl_list'), + url(r'^class/holder/new/(?P[.\w]+)/$', 'acl_class_new_holder_for', (), 'acl_class_new_holder_for'), + + url(r'^class/multiple/grant/$', 'acl_class_multiple_grant', (), 'acl_class_multiple_grant'), + url(r'^class/multiple/revoke/$', 'acl_class_multiple_revoke', (), 'acl_class_multiple_revoke'), + +) diff --git a/apps/acls/utils.py b/apps/acls/utils.py new file mode 100644 index 0000000000..17029e18be --- /dev/null +++ b/apps/acls/utils.py @@ -0,0 +1,34 @@ +from __future__ import absolute_import + +import logging + +from django.contrib.contenttypes.models import ContentType + +from common.models import AnonymousUserSingleton + +from .models import AccessEntry, DefaultAccessEntry, CreatorSingleton +from .classes import get_source_object + +logger = logging.getLogger(__name__) + + +def apply_default_acls(obj, actor=None): + logger.debug('actor, init: %s' % actor) + obj = get_source_object(obj) + + if actor: + actor = AnonymousUserSingleton.objects.passthru_check(actor) + + content_type = ContentType.objects.get_for_model(obj) + + for default_acl in DefaultAccessEntry.objects.filter(content_type=content_type): + holder = CreatorSingleton.objects.passthru_check(default_acl.holder_object, actor) + + if holder: + # When the creator is admin + access_entry = AccessEntry( + permission=default_acl.permission, + holder_object=holder, + content_object=obj, + ) + access_entry.save() diff --git a/apps/acls/views.py b/apps/acls/views.py new file mode 100644 index 0000000000..856b3edd00 --- /dev/null +++ b/apps/acls/views.py @@ -0,0 +1,649 @@ +from __future__ import absolute_import + +import logging + +from django.utils.translation import ugettext_lazy as _ +from django.http import HttpResponseRedirect, Http404 +from django.shortcuts import render_to_response, get_object_or_404 +from django.template import RequestContext +from django.contrib import messages +from django.core.urlresolvers import reverse +from django.contrib.contenttypes.models import ContentType +from django.core.exceptions import ObjectDoesNotExist +from django.utils.simplejson import loads +from django.core.exceptions import PermissionDenied +from django.utils.http import urlencode + +from permissions.models import Permission +from common.utils import encapsulate +from common.widgets import two_state_template + +from .permissions import (ACLS_EDIT_ACL, ACLS_VIEW_ACL, + ACLS_CLASS_EDIT_ACL, ACLS_CLASS_VIEW_ACL) +from .models import AccessEntry, DefaultAccessEntry +from .classes import (AccessHolder, AccessObject, AccessObjectClass, + ClassAccessHolder) +from .widgets import object_w_content_type_icon +from .forms import HolderSelectionForm, ClassHolderSelectionForm +from .api import get_class_permissions_for + +logger = logging.getLogger(__name__) + + +def _permission_titles(permission_list): + return u', '.join([unicode(permission) for permission in permission_list]) + + +def acl_list_for(request, obj, extra_context=None): + try: + Permission.objects.check_permissions(request.user, [ACLS_VIEW_ACL]) + except PermissionDenied: + AccessEntry.objects.check_access(ACLS_VIEW_ACL, request.user, obj) + + logger.debug('obj: %s' % obj) + + context = { + 'object_list': AccessEntry.objects.get_holders_for(obj), + 'title': _(u'access control lists for: %s' % obj), + 'extra_columns': [ + {'name': _(u'holder'), 'attribute': encapsulate(lambda x: object_w_content_type_icon(x.source_object))}, + {'name': _(u'permissions'), 'attribute': encapsulate(lambda x: _permission_titles(AccessEntry.objects.get_holder_permissions_for(obj, x.source_object, db_only=True)))}, + ], + 'hide_object': True, + 'access_object': AccessObject.encapsulate(obj), + 'object': obj, + 'navigation_object_list': [ + {'object': 'object'}, + {'object': 'access_object'} + ], + } + + if extra_context: + context.update(extra_context) + + return render_to_response('generic_list.html', context, + context_instance=RequestContext(request)) + + +def acl_list(request, app_label, model_name, object_id): + ct = get_object_or_404(ContentType, app_label=app_label, model=model_name) + obj = get_object_or_404(ct.get_object_for_this_type, pk=object_id) + return acl_list_for(request, obj) + + +def acl_detail(request, access_object_gid, holder_object_gid): + try: + holder = AccessHolder.get(gid=holder_object_gid) + access_object = AccessObject.get(gid=access_object_gid) + except ObjectDoesNotExist: + raise Http404 + + #return acl_detail_for(request, holder.source_object, access_object.source_object) + return acl_detail_for(request, holder, access_object) + + +def acl_detail_for(request, actor, obj): + try: + Permission.objects.check_permissions(request.user, [ACLS_VIEW_ACL]) + except PermissionDenied: + AccessEntry.objects.check_accesses([ACLS_VIEW_ACL], actor, obj) + + permission_list = get_class_permissions_for(obj.source_object) + #TODO : get all globally assigned permission, new function get_permissions_for_holder (roles aware) + subtemplates_list = [ + { + 'name': u'generic_list_subtemplate.html', + 'context': { + 'title': _(u'permissions available to: %(actor)s for %(obj)s' % { + 'actor': actor, + 'obj': obj + } + ), + 'object_list': permission_list, + 'extra_columns': [ + {'name': _(u'namespace'), 'attribute': 'namespace'}, + {'name': _(u'label'), 'attribute': 'label'}, + { + 'name':_(u'has permission'), + 'attribute': encapsulate(lambda permission: two_state_template(AccessEntry.objects.has_access(permission, actor, obj, db_only=True))) + }, + ], + 'hide_object': True, + } + }, + ] + + context = { + 'object': obj.source_object, + 'subtemplates_list': subtemplates_list, + 'multi_select_as_buttons': True, + 'multi_select_item_properties': { + 'permission_pk': lambda x: x.pk, + 'holder_gid': lambda x: actor.gid, + 'object_gid': lambda x: obj.gid, + }, + 'access_object': obj, + 'navigation_object_list': [ + {'object': 'object'}, + {'object': 'access_object'} + ], + } + + return render_to_response( + 'generic_detail.html', + context, + context_instance=RequestContext(request) + ) + + +def acl_grant(request): + items_property_list = loads(request.GET.get('items_property_list', [])) + + next = request.POST.get('next', request.GET.get('next', request.META.get('HTTP_REFERER', '/'))) + previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', '/'))) + + items = {} + title_suffix = [] + navigation_object = None + navigation_object_count = 0 + + for item_properties in items_property_list: + try: + permission = Permission.objects.get({'pk': item_properties['permission_pk']}) + except Permission.DoesNotExist: + raise Http404 + + try: + requester = AccessHolder.get(gid=item_properties['holder_gid']) + access_object = AccessObject.get(gid=item_properties['object_gid']) + except ObjectDoesNotExist: + raise Http404 + + try: + Permission.objects.check_permissions(request.user, [ACLS_EDIT_ACL]) + except PermissionDenied: + try: + AccessEntry.objects.check_access(ACLS_EDIT_ACL, request.user, access_object) + except PermissionDenied: + raise + else: + items.setdefault(requester, {}) + items[requester].setdefault(access_object, []) + items[requester][access_object].append(permission) + navigation_object = access_object + navigation_object_count += 1 + else: + items.setdefault(requester, {}) + items[requester].setdefault(access_object, []) + items[requester][access_object].append(permission) + navigation_object = access_object + navigation_object_count += 1 + + for requester, obj_ps in items.items(): + for obj, ps in obj_ps.items(): + title_suffix.append(_(u', ').join([u'"%s"' % unicode(p) for p in ps])) + title_suffix.append(_(u' for %s') % obj) + title_suffix.append(_(u' to %s') % requester) + + if len(items_property_list) == 1: + title_prefix = _(u'Are you sure you wish to grant the permission %(title_suffix)s?') + else: + title_prefix = _(u'Are you sure you wish to grant the permissions %(title_suffix)s?') + + if request.method == 'POST': + for requester, object_permissions in items.items(): + for obj, permissions in object_permissions.items(): + for permission in permissions: + if AccessEntry.objects.grant(permission, requester.source_object, obj.source_object): + messages.success(request, _(u'Permission "%(permission)s" granted to %(actor)s for %(object)s.') % { + 'permission': permission, + 'actor': requester, + 'object': obj + }) + else: + messages.warning(request, _(u'%(actor)s, already had the permission "%(permission)s" granted for %(object)s.') % { + 'actor': requester, + 'permission': permission, + 'object': obj, + }) + + return HttpResponseRedirect(next) + + context = { + 'delete_view': True, + 'previous': previous, + 'next': next, + 'form_icon': u'key_add.png', + } + + context['title'] = title_prefix % { + 'title_suffix': u''.join(title_suffix), + } + + logger.debug('navigation_object_count: %d' % navigation_object_count) + logger.debug('navigation_object: %s' % navigation_object) + if navigation_object_count == 1: + context['object'] = navigation_object.source_object + + return render_to_response('generic_confirm.html', context, + context_instance=RequestContext(request)) + + +def acl_revoke(request): + items_property_list = loads(request.GET.get('items_property_list', [])) + + next = request.POST.get('next', request.GET.get('next', request.META.get('HTTP_REFERER', '/'))) + previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', '/'))) + + items = {} + title_suffix = [] + navigation_object = None + navigation_object_count = 0 + + for item_properties in items_property_list: + try: + permission = Permission.objects.get({'pk': item_properties['permission_pk']}) + except Permission.DoesNotExist: + raise Http404 + + try: + requester = AccessHolder.get(gid=item_properties['holder_gid']) + access_object = AccessObject.get(gid=item_properties['object_gid']) + except ObjectDoesNotExist: + raise Http404 + + try: + Permission.objects.check_permissions(request.user, [ACLS_EDIT_ACL]) + except PermissionDenied: + try: + AccessEntry.objects.check_access(ACLS_EDIT_ACL, request.user, access_object) + except PermissionDenied: + raise + else: + items.setdefault(requester, {}) + items[requester].setdefault(access_object, []) + items[requester][access_object].append(permission) + navigation_object = access_object + navigation_object_count += 1 + else: + items.setdefault(requester, {}) + items[requester].setdefault(access_object, []) + items[requester][access_object].append(permission) + navigation_object = access_object + navigation_object_count += 1 + + for requester, obj_ps in items.items(): + for obj, ps in obj_ps.items(): + title_suffix.append(_(u', ').join([u'"%s"' % unicode(p) for p in ps])) + title_suffix.append(_(u' for %s') % obj) + title_suffix.append(_(u' from %s') % requester) + + if len(items_property_list) == 1: + title_prefix = _(u'Are you sure you wish to revoke the permission %(title_suffix)s?') + else: + title_prefix = _(u'Are you sure you wish to revoke the permissions %(title_suffix)s?') + + if request.method == 'POST': + for requester, object_permissions in items.items(): + for obj, permissions in object_permissions.items(): + for permission in permissions: + if AccessEntry.objects.revoke(permission, requester.source_object, obj.source_object): + messages.success(request, _(u'Permission "%(permission)s" revoked of %(actor)s for %(object)s.') % { + 'permission': permission, + 'actor': requester, + 'object': obj + }) + else: + messages.warning(request, _(u'%(actor)s, didn\'t had the permission "%(permission)s" for %(object)s.') % { + 'actor': requester, + 'permission': permission, + 'object': obj, + }) + + return HttpResponseRedirect(next) + + context = { + 'delete_view': True, + 'previous': previous, + 'next': next, + 'form_icon': u'key_delete.png', + } + + context['title'] = title_prefix % { + 'title_suffix': u''.join(title_suffix), + } + + logger.debug('navigation_object_count: %d' % navigation_object_count) + logger.debug('navigation_object: %s' % navigation_object) + if navigation_object_count == 1: + context['object'] = navigation_object.source_object + + return render_to_response('generic_confirm.html', context, + context_instance=RequestContext(request)) + + +def acl_new_holder_for(request, obj, extra_context=None, navigation_object=None): + try: + Permission.objects.check_permissions(request.user, [ACLS_EDIT_ACL]) + except PermissionDenied: + AccessEntry.objects.check_access(ACLS_EDIT_ACL, request.user, obj) + + if request.method == 'POST': + form = HolderSelectionForm(request.POST) + if form.is_valid(): + try: + access_object = AccessObject.encapsulate(obj) + access_holder = AccessHolder.get(form.cleaned_data['holder_gid']) + + query_string = {u'navigation_object': navigation_object} + + return HttpResponseRedirect( + u'%s?%s' % ( + reverse('acl_detail', args=[access_object.gid, access_holder.gid]), + urlencode(query_string) + ) + ) + except ObjectDoesNotExist: + raise Http404 + else: + form = HolderSelectionForm() + + context = { + 'form': form, + 'title': _(u'add new holder for: %s') % obj, + 'submit_label': _(u'Select'), + 'submit_icon_famfam': 'tick', + 'object': obj, + 'access_object': AccessObject.encapsulate(obj), + 'navigation_object_list': [ + {'object': 'object'}, + {'object': 'access_object'}, + ], + } + + if extra_context: + context.update(extra_context) + + return render_to_response('generic_form.html', context, + context_instance=RequestContext(request)) + + +def acl_holder_new(request, access_object_gid): + try: + access_object = AccessObject.get(gid=access_object_gid) + except ObjectDoesNotExist: + raise Http404 + + return acl_new_holder_for(request, access_object.source_object) # , extra_context={'access_object': access_object}) + + +# Setup views +def acl_setup_valid_classes(request): + Permission.objects.check_permissions(request.user, [ACLS_CLASS_VIEW_ACL]) + + context = { + 'object_list': DefaultAccessEntry.get_classes(), + 'title': _(u'classes'), + 'extra_columns': [ + {'name': _(u'class'), 'attribute': encapsulate(lambda x: object_w_content_type_icon(x.source_object))}, + ], + 'hide_object': True, + } + + return render_to_response('generic_list.html', context, + context_instance=RequestContext(request)) + + +def acl_class_acl_list(request, access_object_class_gid): + logger.debug('access_object_class_gid: %s' % access_object_class_gid) + + Permission.objects.check_permissions(request.user, [ACLS_CLASS_VIEW_ACL]) + + access_object_class = AccessObjectClass.get(gid=access_object_class_gid) + logger.debug('access_object_class: %s' % access_object_class) + + context = { + 'object_list': DefaultAccessEntry.objects.get_holders_for(access_object_class.source_object), + 'title': _(u'default access control lists for class: %s') % access_object_class, + 'extra_columns': [ + {'name': _(u'holder'), 'attribute': encapsulate(lambda x: object_w_content_type_icon(x.source_object))}, + {'name': _(u'permissions'), 'attribute': encapsulate(lambda x: _permission_titles(DefaultAccessEntry.objects.get_holder_permissions_for(access_object_class.source_object, x.source_object)))}, + ], + 'hide_object': True, + 'access_object_class': access_object_class, + 'object': access_object_class, + } + + return render_to_response('generic_list.html', context, + context_instance=RequestContext(request)) + + +def acl_class_acl_detail(request, access_object_class_gid, holder_object_gid): + Permission.objects.check_permissions(request.user, [ACLS_CLASS_VIEW_ACL]) + try: + actor = AccessHolder.get(gid=holder_object_gid) + access_object_class = AccessObjectClass.get(gid=access_object_class_gid) + except ObjectDoesNotExist: + raise Http404 + + permission_list = get_class_permissions_for(access_object_class.content_type.model_class()) + #TODO : get all globally assigned permission, new function get_permissions_for_holder (roles aware) + subtemplates_list = [ + { + 'name': u'generic_list_subtemplate.html', + 'context': { + 'title': _(u'permissions available to: %(actor)s for class %(class)s' % { + 'actor': actor, + 'class': access_object_class + } + ), + 'object_list': permission_list, + 'extra_columns': [ + {'name': _(u'namespace'), 'attribute': 'namespace'}, + {'name': _(u'label'), 'attribute': 'label'}, + { + 'name':_(u'has permission'), + 'attribute': encapsulate(lambda x: two_state_template(DefaultAccessEntry.objects.has_access(x, actor.source_object, access_object_class.source_object))) + }, + ], + 'hide_object': True, + } + }, + ] + + return render_to_response('generic_detail.html', { + 'object': access_object_class, + 'subtemplates_list': subtemplates_list, + 'multi_select_as_buttons': True, + 'multi_select_item_properties': { + 'permission_pk': lambda x: x.pk, + 'holder_gid': lambda x: actor.gid, + 'access_object_class_gid': lambda x: access_object_class.gid, + }, + }, context_instance=RequestContext(request)) + + +def acl_class_new_holder_for(request, access_object_class_gid): + Permission.objects.check_permissions(request.user, [ACLS_CLASS_EDIT_ACL]) + access_object_class = AccessObjectClass.get(gid=access_object_class_gid) + + if request.method == 'POST': + form = ClassHolderSelectionForm(request.POST) + if form.is_valid(): + try: + access_holder = ClassAccessHolder.get(form.cleaned_data['holder_gid']) + + return HttpResponseRedirect(reverse('acl_class_acl_detail', args=[access_object_class.gid, access_holder.gid])) + except ObjectDoesNotExist: + raise Http404 + else: + form = ClassHolderSelectionForm(current_holders=DefaultAccessEntry.objects.get_holders_for(access_object_class)) + + context = { + 'form': form, + 'title': _(u'add new holder for class: %s') % unicode(access_object_class), + 'object': access_object_class, + 'submit_label': _(u'Select'), + 'submit_icon_famfam': 'tick' + } + + return render_to_response('generic_form.html', context, + context_instance=RequestContext(request)) + + +def acl_class_multiple_grant(request): + Permission.objects.check_permissions(request.user, [ACLS_CLASS_EDIT_ACL]) + items_property_list = loads(request.GET.get('items_property_list', [])) + + next = request.POST.get('next', request.GET.get('next', request.META.get('HTTP_REFERER', '/'))) + previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', '/'))) + + items = {} + title_suffix = [] + navigation_object = None + navigation_object_count = 0 + + for item_properties in items_property_list: + try: + permission = Permission.objects.get({'pk': item_properties['permission_pk']}) + except Permission.DoesNotExist: + raise Http404 + try: + requester = AccessHolder.get(gid=item_properties['holder_gid']) + access_object_class = AccessObjectClass.get(gid=item_properties['access_object_class_gid']) + except ObjectDoesNotExist: + raise Http404 + + items.setdefault(requester, {}) + items[requester].setdefault(access_object_class, []) + items[requester][access_object_class].append(permission) + navigation_object = access_object_class + navigation_object_count += 1 + + for requester, obj_ps in items.items(): + for obj, ps in obj_ps.items(): + title_suffix.append(_(u', ').join([u'"%s"' % unicode(p) for p in ps])) + title_suffix.append(_(u' for %s') % obj) + title_suffix.append(_(u' to %s') % requester) + + if len(items_property_list) == 1: + title_prefix = _(u'Are you sure you wish to grant the permission %(title_suffix)s?') + else: + title_prefix = _(u'Are you sure you wish to grant the permissions %(title_suffix)s?') + + if request.method == 'POST': + for requester, object_permissions in items.items(): + for obj, permissions in object_permissions.items(): + for permission in permissions: + if DefaultAccessEntry.objects.grant(permission, requester.source_object, obj.source_object): + messages.success(request, _(u'Permission "%(permission)s" granted to %(actor)s for %(object)s.') % { + 'permission': permission, + 'actor': requester, + 'object': obj + }) + else: + messages.warning(request, _(u'%(actor)s, already had the permission "%(permission)s" granted for %(object)s.') % { + 'actor': requester, + 'permission': permission, + 'object': obj, + }) + + return HttpResponseRedirect(next) + + context = { + 'delete_view': True, + 'previous': previous, + 'next': next, + 'form_icon': u'key_add.png', + } + + context['title'] = title_prefix % { + 'title_suffix': u''.join(title_suffix), + } + + logger.debug('navigation_object_count: %d' % navigation_object_count) + logger.debug('navigation_object: %s' % navigation_object) + if navigation_object_count == 1: + context['object'] = navigation_object + + return render_to_response('generic_confirm.html', context, + context_instance=RequestContext(request)) + + +def acl_class_multiple_revoke(request): + Permission.objects.check_permissions(request.user, [ACLS_CLASS_EDIT_ACL]) + items_property_list = loads(request.GET.get('items_property_list', [])) + + next = request.POST.get('next', request.GET.get('next', request.META.get('HTTP_REFERER', '/'))) + previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', '/'))) + + items = {} + title_suffix = [] + navigation_object = None + navigation_object_count = 0 + + for item_properties in items_property_list: + try: + permission = Permission.objects.get({'pk': item_properties['permission_pk']}) + except Permission.DoesNotExist: + raise Http404 + try: + requester = AccessHolder.get(gid=item_properties['holder_gid']) + access_object_class = AccessObjectClass.get(gid=item_properties['access_object_class_gid']) + except ObjectDoesNotExist: + raise Http404 + + items.setdefault(requester, {}) + items[requester].setdefault(access_object_class, []) + items[requester][access_object_class].append(permission) + navigation_object = access_object_class + navigation_object_count += 1 + + for requester, obj_ps in items.items(): + for obj, ps in obj_ps.items(): + title_suffix.append(_(u', ').join([u'"%s"' % unicode(p) for p in ps])) + title_suffix.append(_(u' for %s') % obj) + title_suffix.append(_(u' from %s') % requester) + + if len(items_property_list) == 1: + title_prefix = _(u'Are you sure you wish to revoke the permission %(title_suffix)s?') + else: + title_prefix = _(u'Are you sure you wish to revoke the permissions %(title_suffix)s?') + + if request.method == 'POST': + for requester, object_permissions in items.items(): + for obj, permissions in object_permissions.items(): + for permission in permissions: + if DefaultAccessEntry.objects.revoke(permission, requester.source_object, obj.source_object): + messages.success(request, _(u'Permission "%(permission)s" revoked of %(actor)s for %(object)s.') % { + 'permission': permission, + 'actor': requester, + 'object': obj + }) + else: + messages.warning(request, _(u'%(actor)s, didn\'t had the permission "%(permission)s" for %(object)s.') % { + 'actor': requester, + 'permission': permission, + 'object': obj, + }) + + return HttpResponseRedirect(next) + + context = { + 'delete_view': True, + 'previous': previous, + 'next': next, + 'form_icon': u'key_delete.png', + } + + context['title'] = title_prefix % { + 'title_suffix': u''.join(title_suffix), + } + + logger.debug('navigation_object_count: %d' % navigation_object_count) + logger.debug('navigation_object: %s' % navigation_object) + if navigation_object_count == 1: + context['object'] = navigation_object + + return render_to_response('generic_confirm.html', context, + context_instance=RequestContext(request)) diff --git a/apps/acls/widgets.py b/apps/acls/widgets.py new file mode 100644 index 0000000000..896d99611b --- /dev/null +++ b/apps/acls/widgets.py @@ -0,0 +1,27 @@ +from __future__ import absolute_import + +from django.utils.safestring import mark_safe +from django.contrib.contenttypes.models import ContentType +from django.db.models.base import ModelBase +from django.template.defaultfilters import capfirst + +from .literals import CONTENT_TYPE_ICON_MAP + + +def content_type_icon(content_type): + return mark_safe(u'' % CONTENT_TYPE_ICON_MAP.get('%s.%s' % (content_type.app_label, content_type.model), 'help')) + + +def object_w_content_type_icon(obj): + content_type = ContentType.objects.get_for_model(obj) + + ct_fullname = '%s.%s' % (content_type.app_label, content_type.name) + if isinstance(obj, ModelBase): + label = getattr(obj._meta, 'verbose_name_plural', unicode(content_type)) + else: + if ct_fullname == 'auth.user': + label = obj.get_full_name() + else: + label = unicode(obj) + + return mark_safe('%s%s' % (content_type_icon(content_type), capfirst(label))) diff --git a/apps/common/__init__.py b/apps/common/__init__.py index 6c9e4b6287..bc663d91d9 100644 --- a/apps/common/__init__.py +++ b/apps/common/__init__.py @@ -1,14 +1,17 @@ +from __future__ import absolute_import + import tempfile from django.utils.translation import ugettext_lazy as _ from django.contrib.auth import models as auth_models from django.contrib.auth.management import create_superuser -from django.db.models import signals +from django.dispatch import receiver +from django.db.models.signals import post_syncdb from navigation.api import register_links, register_top_menu -from common.conf import settings as common_settings -from common.utils import validate_path +from .conf import settings as common_settings +from .utils import validate_path def has_usable_password(context): @@ -21,42 +24,38 @@ current_user_edit = {'text': _(u'edit details'), 'view': 'current_user_edit', 'f register_links(['current_user_details', 'current_user_edit', 'password_change_view'], [current_user_details, current_user_edit, password_change_view], menu_name='secondary_menu') about_view = {'text': _('about'), 'view': 'about_view', 'famfam': 'information'} -changelog_view = {'text': _('changelog'), 'view': 'changelog_view', 'famfam': 'book_open'} license_view = {'text': _('license'), 'view': 'license_view', 'famfam': 'script'} -register_links(['about_view', 'changelog_view', 'license_view'], [about_view, changelog_view, license_view], menu_name='secondary_menu') +register_links(['about_view', 'license_view'], [about_view, license_view], menu_name='secondary_menu') register_top_menu('about', link={'text': _(u'about'), 'view': 'about_view', 'famfam': 'information'}, position=-1) -if common_settings.AUTO_CREATE_ADMIN: - # From https://github.com/lambdalisue/django-qwert/blob/master/qwert/autoscript/__init__.py - # From http://stackoverflow.com/questions/1466827/ -- - # - # Prevent interactive question about wanting a superuser created. (This code - # has to go in this otherwise empty "models" module so that it gets processed by - # the "syncdb" command during database creation.) - # - # Create our own test user automatically. +@receiver(post_syncdb, dispatch_uid='create_superuser', sender=auth_models) +def create_superuser(sender, **kwargs): + """ + From https://github.com/lambdalisue/django-qwert/blob/master/qwert/autoscript/__init__.py + From http://stackoverflow.com/questions/1466827/ -- - def create_testuser(app, created_models, verbosity, **kwargs): + Prevent interactive question about wanting a superuser created. (This code + has to go in this otherwise empty "models" module so that it gets processed by + the "syncdb" command during database creation.) + + Create our own admin super user automatically. + """ + + if common_settings.AUTO_CREATE_ADMIN: USERNAME = common_settings.AUTO_ADMIN_USERNAME PASSWORD = common_settings.AUTO_ADMIN_PASSWORD try: auth_models.User.objects.get(username=USERNAME) except auth_models.User.DoesNotExist: print '*' * 80 - print 'Creating test user -- login: %s, password: %s' % (USERNAME, PASSWORD) + print 'Creating super admin user -- login: %s, password: %s' % (USERNAME, PASSWORD) print '*' * 80 assert auth_models.User.objects.create_superuser(USERNAME, 'x@x.com', PASSWORD) else: - print 'Test user already exists. -- login: %s, password: %s' % (USERNAME, PASSWORD) - signals.post_syncdb.disconnect( - create_superuser, - sender=auth_models, - dispatch_uid='django.contrib.auth.management.create_superuser') - signals.post_syncdb.connect(create_testuser, - sender=auth_models, dispatch_uid='common.models.create_testuser') + print 'Super admin user already exists. -- login: %s, password: %s' % (USERNAME, PASSWORD) if (validate_path(common_settings.TEMPORARY_DIRECTORY) == False) or (not common_settings.TEMPORARY_DIRECTORY): setattr(common_settings, 'TEMPORARY_DIRECTORY', tempfile.mkdtemp()) diff --git a/apps/common/compressed_files.py b/apps/common/compressed_files.py new file mode 100644 index 0000000000..2350f1ace1 --- /dev/null +++ b/apps/common/compressed_files.py @@ -0,0 +1,84 @@ +import zipfile + +try: + import zlib + COMPRESSION = zipfile.ZIP_DEFLATED +except: + COMPRESSION = zipfile.ZIP_STORED + +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO + +from django.core.files.uploadedfile import SimpleUploadedFile + + +class NotACompressedFile(Exception): + pass + + +class CompressedFile(object): + def __init__(self, file_input=None): + if file_input: + self._open(file_input) + else: + self._create() + + def _create(self): + self.descriptor = StringIO() + self.zf = zipfile.ZipFile(self.descriptor, mode='w') + + def _open(self, file_input): + try: + # Is it a file like object? + file_input.seek(0) + except AttributeError: + # If not, try open it. + self.descriptor = open(file_input, 'r+b') + else: + self.descriptor = file_input + + try: + test = zipfile.ZipFile(self.descriptor, mode='r') + except zipfile.BadZipfile: + raise NotACompressedFile + else: + test.close() + self.descriptor.seek(0) + self.zf = zipfile.ZipFile(self.descriptor, mode='a') + + def add_file(self, file_input, arcname=None): + try: + # Is it a file like object? + file_input.seek(0) + except AttributeError: + # If not, keep it + self.zf.write(file_input, arcname=arcname, compress_type=COMPRESSION) + else: + self.zf.writestr(arcname, file_input.read()) + + def contents(self): + return [filename for filename in self.zf.namelist() if not filename.endswith('/')] + + def get_content(self, filename): + return self.zf.read(filename) + + def write(self, filename=None): + # fix for Linux zip files read in Windows + for file in self.zf.filelist: + file.create_system = 0 + + self.descriptor.seek(0) + + if filename: + descriptor = open(filename, 'w') + descriptor.write(self.descriptor.read()) + else: + return self.descriptor + + def as_file(self, filename): + return SimpleUploadedFile(name=filename, content=self.write().read()) + + def close(self): + self.zf.close() diff --git a/apps/common/conf/settings.py b/apps/common/conf/settings.py index 14640aeb2e..e1ec67ada3 100644 --- a/apps/common/conf/settings.py +++ b/apps/common/conf/settings.py @@ -64,3 +64,12 @@ register_setting( default=u'username', description=_(u'Controls the mechanism used to authenticated user. Options are: username, email'), ) + +register_setting( + namespace=u'common', + module=u'common.conf.settings', + name=u'ALLOW_ANONYMOUS_ACCESS', + global_name=u'COMMON_ALLOW_ANONYMOUS_ACCESS', + default=False, + description=_(u'Allow non authenticated users, access to all views'), +) diff --git a/apps/common/forms.py b/apps/common/forms.py index 195bac61da..95f8a572dd 100644 --- a/apps/common/forms.py +++ b/apps/common/forms.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import + import os from django import forms @@ -8,9 +10,9 @@ from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth import authenticate from django.conf import settings -from common.utils import return_attrib -from common.widgets import DetailSelectMultiple, PlainWidget, \ - TextAreaDiv, EmailInput +from .utils import return_attrib +from .widgets import (DetailSelectMultiple, PlainWidget, TextAreaDiv, + EmailInput) class DetailForm(forms.ModelForm): @@ -120,7 +122,7 @@ class UserForm(forms.ModelForm): """ class Meta: model = User - fields = ('first_name', 'last_name') + fields = ('username', 'first_name', 'last_name', 'email') class EmailAuthenticationForm(AuthenticationForm): @@ -129,7 +131,7 @@ class EmailAuthenticationForm(AuthenticationForm): authentication """ email = forms.CharField(label=_(u'Email'), max_length=75, - widget=EmailInput() + widget=EmailInput(attrs={'style': 'width: 100%;'}) ) def clean(self): @@ -151,7 +153,7 @@ EmailAuthenticationForm.base_fields.keyOrder = ['email', 'password'] class FileDisplayForm(forms.Form): text = forms.CharField( - label='',#_(u'Text'), + label='', # _(u'Text'), widget=forms.widgets.Textarea( attrs={'cols': 40, 'rows': 20, 'readonly': 'readonly'} ) @@ -159,17 +161,12 @@ class FileDisplayForm(forms.Form): def __init__(self, *args, **kwargs): super(FileDisplayForm, self).__init__(*args, **kwargs) - changelog_path = os.path.join(settings.PROJECT_ROOT, self.DIRECTORY, self.FILENAME) + changelog_path = os.path.join(settings.PROJECT_ROOT, os.sep.join(self.DIRECTORY), self.FILENAME) fd = open(changelog_path) self.fields['text'].initial = fd.read() fd.close() -class ChangelogForm(FileDisplayForm): - FILENAME = u'changelog.rst' - DIRECTORY = u'docs' - - class LicenseForm(FileDisplayForm): FILENAME = u'LICENSE' - DIRECTORY = u'docs' + DIRECTORY = [u'docs', u'credits'] diff --git a/apps/common/locale/en/LC_MESSAGES/django.po b/apps/common/locale/en/LC_MESSAGES/django.po index 27835f371b..204dff27f1 100644 --- a/apps/common/locale/en/LC_MESSAGES/django.po +++ b/apps/common/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,45 +17,41 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: __init__.py:17 +#: __init__.py:20 msgid "change password" msgstr "" -#: __init__.py:18 +#: __init__.py:21 msgid "user details" msgstr "" -#: __init__.py:19 +#: __init__.py:22 msgid "edit details" msgstr "" -#: __init__.py:23 __init__.py:29 +#: __init__.py:26 __init__.py:31 msgid "about" msgstr "" -#: __init__.py:24 -msgid "changelog" -msgstr "" - -#: __init__.py:25 +#: __init__.py:27 msgid "license" msgstr "" -#: forms.py:99 +#: forms.py:101 msgid "Selection" msgstr "" -#: forms.py:131 +#: forms.py:133 msgid "Email" msgstr "" -#: forms.py:142 +#: forms.py:144 msgid "" "Please enter a correct email and password. Note that the password fields is " "case-sensitive." msgstr "" -#: forms.py:144 +#: forms.py:146 msgid "This account is inactive." msgstr "" @@ -99,68 +95,79 @@ msgstr "" msgid "Landscape" msgstr "" -#: utils.py:291 +#: models.py:16 +msgid "lock field" +msgstr "" + +#: models.py:43 +msgid "Anonymous user" +msgstr "" + +#: models.py:46 models.py:47 +msgid "anonymous user" +msgstr "" + +#: utils.py:295 msgid "function found" msgstr "" -#: utils.py:293 utils.py:295 -#, python-format -msgid "class found: %s" -msgstr "" - -#: views.py:24 templates/password_change_done.html:5 -msgid "Your password has been successfully changed." -msgstr "" - -#: views.py:41 +#: views.py:36 msgid "No action selected." msgstr "" -#: views.py:45 +#: views.py:40 msgid "Must select at least one item." msgstr "" -#: views.py:86 +#: views.py:88 #, python-format msgid "%(selection)s added successfully added to %(right_list_title)s." msgstr "" -#: views.py:89 views.py:106 +#: views.py:94 views.py:121 #, python-format msgid "Unable to add %(selection)s to %(right_list_title)s." msgstr "" -#: views.py:103 +#: views.py:115 #, python-format msgid "%(selection)s added successfully removed from %(right_list_title)s." msgstr "" -#: views.py:121 +#: views.py:136 msgid "Add" msgstr "" -#: views.py:132 +#: views.py:147 msgid "Remove" msgstr "" -#: views.py:155 +#: views.py:170 msgid "current user details" msgstr "" -#: views.py:172 +#: views.py:187 +msgid "E-mail conflict, another user has that same email." +msgstr "" + +#: views.py:190 msgid "Current user's details updated." msgstr "" -#: views.py:181 +#: views.py:199 msgid "edit current user details" msgstr "" -#: views.py:207 -msgid "Changelog" +#: views.py:230 +msgid "License" msgstr "" -#: views.py:220 -msgid "License" +#: views.py:239 +msgid "Current user password change" +msgstr "" + +#: views.py:254 templates/password_change_done.html:5 +msgid "Your password has been successfully changed." msgstr "" #: widgets.py:58 @@ -180,6 +187,10 @@ msgid "" "email" msgstr "" +#: conf/settings.py:74 +msgid "Allow non authenticated users, access to all views" +msgstr "" + #: templates/403.html:3 templates/403.html.py:7 msgid "Insufficient permissions" msgstr "" @@ -260,28 +271,32 @@ msgid "No" msgstr "" #: templates/generic_form_instance.html:37 -#: templates/generic_form_subtemplate.html:52 +#: templates/generic_form_subtemplate.html:56 msgid "required" msgstr "" -#: templates/generic_form_subtemplate.html:76 -#: templates/generic_form_subtemplate.html:78 +#: templates/generic_form_subtemplate.html:80 +#: templates/generic_form_subtemplate.html:82 #: templates/generic_list_horizontal_subtemplate.html:51 -#: templates/generic_list_horizontal_subtemplate.html:178 +#: templates/generic_list_horizontal_subtemplate.html:90 #: templates/generic_list_subtemplate.html:52 #: templates/generic_list_subtemplate.html:178 msgid "Save" msgstr "" -#: templates/generic_form_subtemplate.html:76 -#: templates/generic_form_subtemplate.html:78 +#: templates/generic_form_subtemplate.html:80 +#: templates/generic_form_subtemplate.html:82 #: templates/generic_list_horizontal_subtemplate.html:51 -#: templates/generic_list_horizontal_subtemplate.html:178 +#: templates/generic_list_horizontal_subtemplate.html:90 #: templates/generic_list_subtemplate.html:52 #: templates/generic_list_subtemplate.html:178 msgid "Submit" msgstr "" +#: templates/generic_form_subtemplate.html:87 +msgid "Cancel" +msgstr "" + #: templates/generic_list.html:6 templates/generic_list_horizontal.html:6 #, python-format msgid "List of %(stripped_title)s" diff --git a/apps/common/locale/es/LC_MESSAGES/django.mo b/apps/common/locale/es/LC_MESSAGES/django.mo index 00b642dc31..8eccd1e08b 100644 Binary files a/apps/common/locale/es/LC_MESSAGES/django.mo and b/apps/common/locale/es/LC_MESSAGES/django.mo differ diff --git a/apps/common/locale/es/LC_MESSAGES/django.po b/apps/common/locale/es/LC_MESSAGES/django.po index d4ccd9dd69..a1da71f28a 100644 --- a/apps/common/locale/es/LC_MESSAGES/django.po +++ b/apps/common/locale/es/LC_MESSAGES/django.po @@ -3,14 +3,14 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: -# Roberto Rosario , 2011. +# Roberto Rosario , 2011, 2012. msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" -"PO-Revision-Date: 2011-11-04 00:58+0000\n" -"Last-Translator: rosarior \n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-02 18:39+0000\n" +"Last-Translator: Roberto Rosario \n" "Language-Team: Spanish (Castilian) (http://www.transifex.net/projects/p/" "mayan-edms/team/es/)\n" "Language: es\n" @@ -19,39 +19,35 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -#: __init__.py:17 +#: __init__.py:20 msgid "change password" msgstr "cambiar contraseña" -#: __init__.py:18 +#: __init__.py:21 msgid "user details" msgstr "detalles de usuario" -#: __init__.py:19 +#: __init__.py:22 msgid "edit details" msgstr "editar detalles" -#: __init__.py:23 __init__.py:29 +#: __init__.py:26 __init__.py:31 msgid "about" msgstr "sobre" -#: __init__.py:24 -msgid "changelog" -msgstr "cambios" - -#: __init__.py:25 +#: __init__.py:27 msgid "license" msgstr "licencia" -#: forms.py:99 +#: forms.py:101 msgid "Selection" msgstr "Selección" -#: forms.py:131 +#: forms.py:133 msgid "Email" msgstr "E-mail" -#: forms.py:142 +#: forms.py:144 msgid "" "Please enter a correct email and password. Note that the password fields is " "case-sensitive." @@ -60,7 +56,7 @@ msgstr "" "correctas. Tenga en cuenta que los campos de contraseña distingue entre " "mayúsculas y minúsculas." -#: forms.py:144 +#: forms.py:146 msgid "This account is inactive." msgstr "Esta cuenta está inactiva." @@ -104,70 +100,83 @@ msgstr "Retrato" msgid "Landscape" msgstr "Paisaje" -#: utils.py:291 +#: models.py:16 +msgid "lock field" +msgstr "campo de bloqueo" + +#: models.py:43 +msgid "Anonymous user" +msgstr "Usuario anónimo" + +#: models.py:46 models.py:47 +msgid "anonymous user" +msgstr "usuario anónimo" + +#: utils.py:295 msgid "function found" msgstr "función encontrada" -#: utils.py:293 utils.py:295 -#, python-format -msgid "class found: %s" -msgstr "clase encontrada: %s" - -#: views.py:24 templates/password_change_done.html:5 -msgid "Your password has been successfully changed." -msgstr "Su contraseña se ha modificado correctamente." - -#: views.py:41 +#: views.py:36 msgid "No action selected." msgstr "Ninguna acción seleccionada." -#: views.py:45 +#: views.py:40 msgid "Must select at least one item." msgstr "Debe seleccionar al menos un artículo." -#: views.py:86 +#: views.py:88 #, python-format msgid "%(selection)s added successfully added to %(right_list_title)s." msgstr "Se agrego exitosamente %(selection)s a %(right_list_title)s." -#: views.py:89 views.py:106 +#: views.py:94 views.py:121 #, python-format msgid "Unable to add %(selection)s to %(right_list_title)s." msgstr "No se puede agregar %(selection)s a %(right_list_title)s." -#: views.py:103 +#: views.py:115 #, python-format msgid "%(selection)s added successfully removed from %(right_list_title)s." msgstr "Se removió exitosamente %(selection)s de %(right_list_title)s." -#: views.py:121 +#: views.py:136 msgid "Add" msgstr "Agregar" -#: views.py:132 +#: views.py:147 msgid "Remove" msgstr "Remover" -#: views.py:155 +#: views.py:170 msgid "current user details" msgstr "detalles del usuario corriente" -#: views.py:172 +#: views.py:187 +msgid "E-mail conflict, another user has that same email." +msgstr "" +"Conflicto de correo electrónica, otro usuario tiene ese mismo correo " +"electrónico." + +#: views.py:190 msgid "Current user's details updated." msgstr "Datos del usuario corriente actualizados." -#: views.py:181 +#: views.py:199 msgid "edit current user details" msgstr "editar detalles del usuario corriente" -#: views.py:207 -msgid "Changelog" -msgstr "Cambios" - -#: views.py:220 +#: views.py:230 msgid "License" msgstr "Licencia" +#: views.py:239 +msgid "Current user password change" +msgstr "Cambio de contraseña de usuario actual" + +#: views.py:254 templates/password_change_done.html:5 +msgid "Your password has been successfully changed." +msgstr "Su contraseña se ha modificado correctamente." + #: widgets.py:58 msgid "None" msgstr "Ninguno" @@ -190,6 +199,11 @@ msgstr "" "Controla el mecanismo utilizado para el usuario autenticado. Las opciones " "son: 'username' nombre de usuario, 'email' correo electrónico" +#: conf/settings.py:74 +msgid "Allow non authenticated users, access to all views" +msgstr "" +"Permita a los usuarios no autenticados, el acceso a todas las pantallas" + #: templates/403.html:3 templates/403.html.py:7 msgid "Insufficient permissions" msgstr "Permisos insuficientes" @@ -270,28 +284,32 @@ msgid "No" msgstr "No" #: templates/generic_form_instance.html:37 -#: templates/generic_form_subtemplate.html:52 +#: templates/generic_form_subtemplate.html:56 msgid "required" msgstr "requerido" -#: templates/generic_form_subtemplate.html:76 -#: templates/generic_form_subtemplate.html:78 +#: templates/generic_form_subtemplate.html:80 +#: templates/generic_form_subtemplate.html:82 #: templates/generic_list_horizontal_subtemplate.html:51 -#: templates/generic_list_horizontal_subtemplate.html:178 +#: templates/generic_list_horizontal_subtemplate.html:90 #: templates/generic_list_subtemplate.html:52 #: templates/generic_list_subtemplate.html:178 msgid "Save" msgstr "Guardar" -#: templates/generic_form_subtemplate.html:76 -#: templates/generic_form_subtemplate.html:78 +#: templates/generic_form_subtemplate.html:80 +#: templates/generic_form_subtemplate.html:82 #: templates/generic_list_horizontal_subtemplate.html:51 -#: templates/generic_list_horizontal_subtemplate.html:178 +#: templates/generic_list_horizontal_subtemplate.html:90 #: templates/generic_list_subtemplate.html:52 #: templates/generic_list_subtemplate.html:178 msgid "Submit" msgstr "Enviar" +#: templates/generic_form_subtemplate.html:87 +msgid "Cancel" +msgstr "Cancelar" + #: templates/generic_list.html:6 templates/generic_list_horizontal.html:6 #, python-format msgid "List of %(stripped_title)s" @@ -330,6 +348,3 @@ msgstr "Iniciar sesión" #: templates/password_change_form.html:5 msgid "Password change" msgstr "Cambio de contraseña" - -#~ msgid "Cancel" -#~ msgstr "Cancelar" diff --git a/apps/common/locale/it/LC_MESSAGES/django.mo b/apps/common/locale/it/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..6e25f2e7c5 Binary files /dev/null and b/apps/common/locale/it/LC_MESSAGES/django.mo differ diff --git a/apps/common/locale/it/LC_MESSAGES/django.po b/apps/common/locale/it/LC_MESSAGES/django.po new file mode 100644 index 0000000000..365d59fa16 --- /dev/null +++ b/apps/common/locale/it/LC_MESSAGES/django.po @@ -0,0 +1,337 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# , 2011. +# Roberto Rosario , 2012. +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-21 16:35+0000\n" +"Last-Translator: Roberto Rosario \n" +"Language-Team: Italian (http://www.transifex.net/projects/p/mayan-edms/language/it/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: it\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" + +#: __init__.py:20 +msgid "change password" +msgstr "cambia password" + +#: __init__.py:21 +msgid "user details" +msgstr "dettaglio utente" + +#: __init__.py:22 +msgid "edit details" +msgstr "modifica dettagli" + +#: __init__.py:26 __init__.py:31 +msgid "about" +msgstr "a rigurdo" + +#: __init__.py:27 +msgid "license" +msgstr "licenza" + +#: forms.py:101 +msgid "Selection" +msgstr "Selezione" + +#: forms.py:133 +msgid "Email" +msgstr "Email" + +#: forms.py:144 +msgid "" +"Please enter a correct email and password. Note that the password fields is " +"case-sensitive." +msgstr "Inserisci un'indirizzo mail valido e una password. Ricorda che il campo password è case-sensitive" + +#: forms.py:146 +msgid "This account is inactive." +msgstr "Questo account è inattivo" + +#: literals.py:24 +msgid "A5" +msgstr "A5" + +#: literals.py:25 +msgid "A4" +msgstr "A4" + +#: literals.py:26 +msgid "A3" +msgstr "A3" + +#: literals.py:27 +msgid "B5" +msgstr "B5" + +#: literals.py:28 +msgid "B4" +msgstr "B4" + +#: literals.py:29 +msgid "Letter" +msgstr "Letter" + +#: literals.py:30 +msgid "Legal" +msgstr "Legal" + +#: literals.py:31 +msgid "Ledger" +msgstr "Mastro" + +#: literals.py:38 +msgid "Portrait" +msgstr "Verticale" + +#: literals.py:39 +msgid "Landscape" +msgstr "Orizontale" + +#: models.py:16 +msgid "lock field" +msgstr "" + +#: models.py:43 +msgid "Anonymous user" +msgstr "Utente anonimo" + +#: models.py:46 models.py:47 +msgid "anonymous user" +msgstr "utente anonimo" + +#: utils.py:295 +msgid "function found" +msgstr "trovata funzione" + +#: views.py:36 +msgid "No action selected." +msgstr "Nessuna azione selezionata" + +#: views.py:40 +msgid "Must select at least one item." +msgstr "Devi selezionare un item" + +#: views.py:88 +#, python-format +msgid "%(selection)s added successfully added to %(right_list_title)s." +msgstr "%(selection)s aggiunto con successo a %(right_list_title)s." + +#: views.py:94 views.py:121 +#, python-format +msgid "Unable to add %(selection)s to %(right_list_title)s." +msgstr "Impossibile aggiungere %(selection)s a %(right_list_title)s." + +#: views.py:115 +#, python-format +msgid "%(selection)s added successfully removed from %(right_list_title)s." +msgstr "%(selection)s aggiunto correttamente rimosso dal %(right_list_title)s." + +#: views.py:136 +msgid "Add" +msgstr "Aggiungi" + +#: views.py:147 +msgid "Remove" +msgstr "Rimuovi" + +#: views.py:170 +msgid "current user details" +msgstr "dettagli dell'utente corrente" + +#: views.py:187 +msgid "E-mail conflict, another user has that same email." +msgstr "E-mail conflitto, un altro utente ha quella stessa email." + +#: views.py:190 +msgid "Current user's details updated." +msgstr "Dettagli dell'utente corrente aggiornati" + +#: views.py:199 +msgid "edit current user details" +msgstr "modifica i dettagli dell'utente corrente" + +#: views.py:230 +msgid "License" +msgstr "Licenza" + +#: views.py:239 +msgid "Current user password change" +msgstr "Modifica della password dell'utente corrente" + +#: views.py:254 templates/password_change_done.html:5 +msgid "Your password has been successfully changed." +msgstr "La tua password è stata cambiata con successo" + +#: widgets.py:58 +msgid "None" +msgstr "Nessuno" + +#: conf/settings.py:15 +msgid "" +"Temporary directory used site wide to store thumbnails, previews and " +"temporary files. If none is specified, one will be created using " +"tempfile.mkdtemp()" +msgstr "Directory temporanea utilizzata a livello di sito per thumbnails, anteprime e file temporanei. Se non viene specificato, ne verrà creata utilizzando tempfile.mkdtemp()" + +#: conf/settings.py:65 +msgid "" +"Controls the mechanism used to authenticated user. Options are: username, " +"email" +msgstr "Controllo del meccanismo di autenticazione. Le opzioni possibili sono:username,email" + +#: conf/settings.py:74 +msgid "Allow non authenticated users, access to all views" +msgstr "Consentire agli utenti non autenticati, l'accesso a tutte le viste" + +#: templates/403.html:3 templates/403.html.py:7 +msgid "Insufficient permissions" +msgstr "Permessi insufficienti" + +#: templates/403.html:9 +msgid "You don't have enough permissions for this operation." +msgstr "Non hai i permessi per effettuare questa operazione." + +#: templates/404.html:3 templates/404.html.py:7 +msgid "Page not found" +msgstr "Pagina non trovata" + +#: templates/404.html:9 +msgid "Sorry, but the requested page could not be found." +msgstr "Scusa ma la pagina richiesta non è disponibile" + +#: templates/calculate_form_title.html:11 +#, python-format +msgid "Details for %(object_name)s: %(object)s" +msgstr "Dettagli per %(object_name)s: %(object)s" + +#: templates/calculate_form_title.html:13 +#, python-format +msgid "Details for: %(object)s" +msgstr "Detaglio per: %(object)s" + +#: templates/calculate_form_title.html:18 +#, python-format +msgid "Edit %(object_name)s:" +msgstr "Modifica %(object_name)s:" + +#: templates/calculate_form_title.html:20 +msgid "Edit" +msgstr "Modifica" + +#: templates/calculate_form_title.html:24 +#, python-format +msgid "Create new %(object_name)s" +msgstr "Crea nuovo %(object_name)s" + +#: templates/calculate_form_title.html:26 +msgid "Create" +msgstr "Crea" + +#: templates/generic_assign_remove.html:3 +#, python-format +msgid "Assign %(title)s %(object)s" +msgstr "Assigna %(title)s %(object)s" + +#: templates/generic_confirm.html:3 templates/generic_confirm.html.py:18 +msgid "Confirm" +msgstr "Conferma" + +#: templates/generic_confirm.html:16 +msgid "Confirm delete" +msgstr "Conferma la cancellazione" + +#: templates/generic_confirm.html:32 +msgid "form icon" +msgstr "icona del modulo" + +#: templates/generic_confirm.html:40 +#, python-format +msgid "Are you sure you wish to delete %(object_name)s: %(object)s?" +msgstr "Sei sicuro di voler cancellare %(object_name)s: %(object)s?" + +#: templates/generic_confirm.html:42 +#, python-format +msgid "Are you sure you wish to delete: %(object)s?" +msgstr "Sei sicuro di volr cancellare: %(object)s?" + +#: templates/generic_confirm.html:50 +msgid "Yes" +msgstr "Si" + +#: templates/generic_confirm.html:54 +msgid "No" +msgstr "No" + +#: templates/generic_form_instance.html:37 +#: templates/generic_form_subtemplate.html:56 +msgid "required" +msgstr "richiesto" + +#: templates/generic_form_subtemplate.html:80 +#: templates/generic_form_subtemplate.html:82 +#: templates/generic_list_horizontal_subtemplate.html:51 +#: templates/generic_list_horizontal_subtemplate.html:90 +#: templates/generic_list_subtemplate.html:52 +#: templates/generic_list_subtemplate.html:178 +msgid "Save" +msgstr "Salva" + +#: templates/generic_form_subtemplate.html:80 +#: templates/generic_form_subtemplate.html:82 +#: templates/generic_list_horizontal_subtemplate.html:51 +#: templates/generic_list_horizontal_subtemplate.html:90 +#: templates/generic_list_subtemplate.html:52 +#: templates/generic_list_subtemplate.html:178 +msgid "Submit" +msgstr "Sottometti" + +#: templates/generic_form_subtemplate.html:87 +msgid "Cancel" +msgstr "Annullare" + +#: templates/generic_list.html:6 templates/generic_list_horizontal.html:6 +#, python-format +msgid "List of %(stripped_title)s" +msgstr "Lista di %(stripped_title)s" + +#: templates/generic_list_horizontal_subtemplate.html:23 +#: templates/generic_list_subtemplate.html:24 +#, python-format +msgid "" +"List of %(title)s (%(start)s - %(end)s out of %(total)s) (Page " +"%(page_number)s of %(total_pages)s)" +msgstr "Lista di %(title)s (%(start)s - %(end)s fuori %(total)s) (Page %(page_number)s of %(total_pages)s)" + +#: templates/generic_list_horizontal_subtemplate.html:25 +#: templates/generic_list_subtemplate.html:26 +#, python-format +msgid "List of %(title)s (%(total)s)" +msgstr "Lista di %(title)s (%(total)s)" + +#: templates/generic_list_subtemplate.html:72 +msgid "Identifier" +msgstr "Identificatore" + +#: templates/generic_list_subtemplate.html:152 +#, python-format +msgid "There are no %(stripped_title)s" +msgstr "Non ci sono %(stripped_title)s" + +#: templates/login.html:5 +msgid "Login" +msgstr "Login" + +#: templates/password_change_done.html:3 templates/password_change_form.html:3 +#: templates/password_change_form.html:5 +msgid "Password change" +msgstr "Cambia password" diff --git a/apps/common/locale/pl/LC_MESSAGES/django.mo b/apps/common/locale/pl/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..62f690afaa Binary files /dev/null and b/apps/common/locale/pl/LC_MESSAGES/django.mo differ diff --git a/apps/common/locale/pl/LC_MESSAGES/django.po b/apps/common/locale/pl/LC_MESSAGES/django.po new file mode 100644 index 0000000000..6998e25932 --- /dev/null +++ b/apps/common/locale/pl/LC_MESSAGES/django.po @@ -0,0 +1,339 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# mic, 2012. +# mic , 2012. +# , 2012. +# , 2012. +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-21 15:17+0000\n" +"Last-Translator: mic \n" +"Language-Team: Polish (http://www.transifex.net/projects/p/mayan-edms/language/pl/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pl\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" + +#: __init__.py:20 +msgid "change password" +msgstr "zmień hasło" + +#: __init__.py:21 +msgid "user details" +msgstr "szczegóły konta użytkownika" + +#: __init__.py:22 +msgid "edit details" +msgstr "edytuj szczegóły" + +#: __init__.py:26 __init__.py:31 +msgid "about" +msgstr "informacje o" + +#: __init__.py:27 +msgid "license" +msgstr "licencja" + +#: forms.py:101 +msgid "Selection" +msgstr "Zaznaczenie" + +#: forms.py:133 +msgid "Email" +msgstr "E-mail" + +#: forms.py:144 +msgid "" +"Please enter a correct email and password. Note that the password fields is " +"case-sensitive." +msgstr "Proszę wpisać poprawną nazwę użytkownika i hasło. Uwaga: wielkość liter ma znaczenie." + +#: forms.py:146 +msgid "This account is inactive." +msgstr "To konto jest nieaktywne." + +#: literals.py:24 +msgid "A5" +msgstr "A5" + +#: literals.py:25 +msgid "A4" +msgstr "A4" + +#: literals.py:26 +msgid "A3" +msgstr "A3" + +#: literals.py:27 +msgid "B5" +msgstr "B5" + +#: literals.py:28 +msgid "B4" +msgstr "B4" + +#: literals.py:29 +msgid "Letter" +msgstr "Letter" + +#: literals.py:30 +msgid "Legal" +msgstr "Legal" + +#: literals.py:31 +msgid "Ledger" +msgstr "Ledger" + +#: literals.py:38 +msgid "Portrait" +msgstr "Portrait" + +#: literals.py:39 +msgid "Landscape" +msgstr "Landscape" + +#: models.py:16 +msgid "lock field" +msgstr "zablokować pole" + +#: models.py:43 +msgid "Anonymous user" +msgstr "Użytkownik anonimowy" + +#: models.py:46 models.py:47 +msgid "anonymous user" +msgstr "użytkownik anonimowy" + +#: utils.py:295 +msgid "function found" +msgstr "znaleźć funkcję" + +#: views.py:36 +msgid "No action selected." +msgstr "Nie wybrano żadnego działania" + +#: views.py:40 +msgid "Must select at least one item." +msgstr "Musisz wybrać co najmniej jeden element." + +#: views.py:88 +#, python-format +msgid "%(selection)s added successfully added to %(right_list_title)s." +msgstr " %(selection)s pomyślnie dodana do %(right_list_title)s." + +#: views.py:94 views.py:121 +#, python-format +msgid "Unable to add %(selection)s to %(right_list_title)s." +msgstr "Nie można dodać %(selection)s do %(right_list_title)s." + +#: views.py:115 +#, python-format +msgid "%(selection)s added successfully removed from %(right_list_title)s." +msgstr " %(selection)s pomyślnie dodana usunięty z %(right_list_title)s." + +#: views.py:136 +msgid "Add" +msgstr "Dodaj" + +#: views.py:147 +msgid "Remove" +msgstr "Usuń" + +#: views.py:170 +msgid "current user details" +msgstr "aktualne dane użytkownika" + +#: views.py:187 +msgid "E-mail conflict, another user has that same email." +msgstr "Użytkownik o podanym adresie e-mail już istnieje." + +#: views.py:190 +msgid "Current user's details updated." +msgstr "Aktualne dane użytkownika aktualizowane." + +#: views.py:199 +msgid "edit current user details" +msgstr "edytuj aktualne dane użytkownika" + +#: views.py:230 +msgid "License" +msgstr "Licencja" + +#: views.py:239 +msgid "Current user password change" +msgstr "Zmiana hasła użytkownika" + +#: views.py:254 templates/password_change_done.html:5 +msgid "Your password has been successfully changed." +msgstr "Twoje hasło zostało pomyślnie zmienione." + +#: widgets.py:58 +msgid "None" +msgstr "Brak" + +#: conf/settings.py:15 +msgid "" +"Temporary directory used site wide to store thumbnails, previews and " +"temporary files. If none is specified, one will be created using " +"tempfile.mkdtemp()" +msgstr "Katalog tymczasowy używany do przechowywania całej witryny, miniatur, podglądów i plików tymczasowych. Jeśli nie zostanie określony, zostanie utworzony za pomocą tempfile.mkdtemp ()" + +#: conf/settings.py:65 +msgid "" +"Controls the mechanism used to authenticated user. Options are: username, " +"email" +msgstr "" + +#: conf/settings.py:74 +msgid "Allow non authenticated users, access to all views" +msgstr "" + +#: templates/403.html:3 templates/403.html.py:7 +msgid "Insufficient permissions" +msgstr "Niewystarczające uprawnienia" + +#: templates/403.html:9 +msgid "You don't have enough permissions for this operation." +msgstr "Nie masz wystarczających uprawnień do tej operacji." + +#: templates/404.html:3 templates/404.html.py:7 +msgid "Page not found" +msgstr "Nie znaleziono strony" + +#: templates/404.html:9 +msgid "Sorry, but the requested page could not be found." +msgstr "Przykro nam, ale żądana strona nie została odnaleziona." + +#: templates/calculate_form_title.html:11 +#, python-format +msgid "Details for %(object_name)s: %(object)s" +msgstr "Szczegóły dla %(object_name)s : %(object)s " + +#: templates/calculate_form_title.html:13 +#, python-format +msgid "Details for: %(object)s" +msgstr "Szczegóły: %(object)s " + +#: templates/calculate_form_title.html:18 +#, python-format +msgid "Edit %(object_name)s:" +msgstr "Edytuj %(object_name)s :" + +#: templates/calculate_form_title.html:20 +msgid "Edit" +msgstr "Edytuj" + +#: templates/calculate_form_title.html:24 +#, python-format +msgid "Create new %(object_name)s" +msgstr "Utwórz nową %(object_name)s " + +#: templates/calculate_form_title.html:26 +msgid "Create" +msgstr "Utwórz" + +#: templates/generic_assign_remove.html:3 +#, python-format +msgid "Assign %(title)s %(object)s" +msgstr "Przypisz %(title)s %(object)s" + +#: templates/generic_confirm.html:3 templates/generic_confirm.html.py:18 +msgid "Confirm" +msgstr "Potwierdź" + +#: templates/generic_confirm.html:16 +msgid "Confirm delete" +msgstr "Potwierdź usunięcie" + +#: templates/generic_confirm.html:32 +msgid "form icon" +msgstr "form icon" + +#: templates/generic_confirm.html:40 +#, python-format +msgid "Are you sure you wish to delete %(object_name)s: %(object)s?" +msgstr "Czy na pewno chcesz usunąć %(object_name)s : %(object)s ?" + +#: templates/generic_confirm.html:42 +#, python-format +msgid "Are you sure you wish to delete: %(object)s?" +msgstr "Czy na pewno chcesz usunąć: %(object)s?" + +#: templates/generic_confirm.html:50 +msgid "Yes" +msgstr "Tak" + +#: templates/generic_confirm.html:54 +msgid "No" +msgstr "Nie" + +#: templates/generic_form_instance.html:37 +#: templates/generic_form_subtemplate.html:56 +msgid "required" +msgstr "wymagane" + +#: templates/generic_form_subtemplate.html:80 +#: templates/generic_form_subtemplate.html:82 +#: templates/generic_list_horizontal_subtemplate.html:51 +#: templates/generic_list_horizontal_subtemplate.html:90 +#: templates/generic_list_subtemplate.html:52 +#: templates/generic_list_subtemplate.html:178 +msgid "Save" +msgstr "Zapisz" + +#: templates/generic_form_subtemplate.html:80 +#: templates/generic_form_subtemplate.html:82 +#: templates/generic_list_horizontal_subtemplate.html:51 +#: templates/generic_list_horizontal_subtemplate.html:90 +#: templates/generic_list_subtemplate.html:52 +#: templates/generic_list_subtemplate.html:178 +msgid "Submit" +msgstr "Wyślij" + +#: templates/generic_form_subtemplate.html:87 +msgid "Cancel" +msgstr "Anuluj" + +#: templates/generic_list.html:6 templates/generic_list_horizontal.html:6 +#, python-format +msgid "List of %(stripped_title)s" +msgstr "Wykaz %(stripped_title)s" + +#: templates/generic_list_horizontal_subtemplate.html:23 +#: templates/generic_list_subtemplate.html:24 +#, python-format +msgid "" +"List of %(title)s (%(start)s - %(end)s out of %(total)s) (Page " +"%(page_number)s of %(total_pages)s)" +msgstr "Wykaz %(title)s (%(start)s - %(end)s z %(total)s) (Page %(page_number)s z %(total_pages)s)" + +#: templates/generic_list_horizontal_subtemplate.html:25 +#: templates/generic_list_subtemplate.html:26 +#, python-format +msgid "List of %(title)s (%(total)s)" +msgstr "Wykaz %(title)s (%(total)s)" + +#: templates/generic_list_subtemplate.html:72 +msgid "Identifier" +msgstr "Identyfikator" + +#: templates/generic_list_subtemplate.html:152 +#, python-format +msgid "There are no %(stripped_title)s" +msgstr "Brak %(stripped_title)s" + +#: templates/login.html:5 +msgid "Login" +msgstr "Zaloguj" + +#: templates/password_change_done.html:3 templates/password_change_form.html:3 +#: templates/password_change_form.html:5 +msgid "Password change" +msgstr "Zmiana hasła" diff --git a/apps/common/locale/pt/LC_MESSAGES/django.mo b/apps/common/locale/pt/LC_MESSAGES/django.mo index ade5e888b5..eb30f08c65 100644 Binary files a/apps/common/locale/pt/LC_MESSAGES/django.mo and b/apps/common/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/common/locale/pt/LC_MESSAGES/django.po b/apps/common/locale/pt/LC_MESSAGES/django.po index d609fa4bc9..b20060cac8 100644 --- a/apps/common/locale/pt/LC_MESSAGES/django.po +++ b/apps/common/locale/pt/LC_MESSAGES/django.po @@ -1,65 +1,59 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: # , 2011. +# Roberto Rosario , 2012. msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" -"PO-Revision-Date: 2011-11-02 02:18+0000\n" -"Last-Translator: emersonsoares \n" -"Language-Team: Portuguese (http://www.transifex.net/projects/p/mayan-edms/" -"team/pt/)\n" -"Language: pt\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-21 15:07+0000\n" +"Last-Translator: Roberto Rosario \n" +"Language-Team: Portuguese (http://www.transifex.net/projects/p/mayan-edms/language/pt/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +"Language: pt\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -#: __init__.py:17 +#: __init__.py:20 msgid "change password" msgstr "alterar a senha" -#: __init__.py:18 +#: __init__.py:21 msgid "user details" msgstr "detalhes do usuário" -#: __init__.py:19 +#: __init__.py:22 msgid "edit details" msgstr "editar detalhes" -#: __init__.py:23 __init__.py:29 +#: __init__.py:26 __init__.py:31 msgid "about" msgstr "sobre" -#: __init__.py:24 -msgid "changelog" -msgstr "log de alterações" - -#: __init__.py:25 +#: __init__.py:27 msgid "license" msgstr "licença" -#: forms.py:99 +#: forms.py:101 msgid "Selection" msgstr "Seleção" -#: forms.py:131 +#: forms.py:133 msgid "Email" msgstr "E-mail" -#: forms.py:142 +#: forms.py:144 msgid "" "Please enter a correct email and password. Note that the password fields is " "case-sensitive." -msgstr "" -"Por favor insira o e-mail e senha corretos. Note que os campos de senha são " -"case-sensitive." +msgstr "Por favor insira o e-mail e senha corretos. Note que os campos de senha são case-sensitive." -#: forms.py:144 +#: forms.py:146 msgid "This account is inactive." msgstr "Esta conta está inativa." @@ -103,70 +97,81 @@ msgstr "Retrato" msgid "Landscape" msgstr "Paisagem" -#: utils.py:291 +#: models.py:16 +msgid "lock field" +msgstr "" + +#: models.py:43 +msgid "Anonymous user" +msgstr "Usuário anônimo" + +#: models.py:46 models.py:47 +msgid "anonymous user" +msgstr "usuário anônimo" + +#: utils.py:295 msgid "function found" msgstr "função encontrada" -#: utils.py:293 utils.py:295 -#, python-format -msgid "class found: %s" -msgstr "classe encontrada: %s" - -#: views.py:24 templates/password_change_done.html:5 -msgid "Your password has been successfully changed." -msgstr "Sua senha foi alterada com êxito." - -#: views.py:41 +#: views.py:36 msgid "No action selected." msgstr "Nenhuma ação selecionada." -#: views.py:45 +#: views.py:40 msgid "Must select at least one item." msgstr "Deve selecionar pelo menos um item." -#: views.py:86 +#: views.py:88 #, python-format msgid "%(selection)s added successfully added to %(right_list_title)s." msgstr "%(selection)s adicionadas com sucesso a %(right_list_title)s ." -#: views.py:89 views.py:106 +#: views.py:94 views.py:121 #, python-format msgid "Unable to add %(selection)s to %(right_list_title)s." msgstr "Não foi possível adicionar %(selection)s para %(right_list_title)s ." -#: views.py:103 +#: views.py:115 #, python-format msgid "%(selection)s added successfully removed from %(right_list_title)s." msgstr " %(selection)s adicionado com sucesso removidos %(right_list_title)s." -#: views.py:121 +#: views.py:136 msgid "Add" msgstr "Adicionar" -#: views.py:132 +#: views.py:147 msgid "Remove" msgstr "Remover" -#: views.py:155 +#: views.py:170 msgid "current user details" msgstr "detalhes atuais do usuário" -#: views.py:172 +#: views.py:187 +msgid "E-mail conflict, another user has that same email." +msgstr "E-mail conflito, outro usuário que tem mesmo e-mail." + +#: views.py:190 msgid "Current user's details updated." msgstr "Detalhes do usuário atual atualizados." -#: views.py:181 +#: views.py:199 msgid "edit current user details" msgstr "editar os detalhes do usuário atual" -#: views.py:207 -msgid "Changelog" -msgstr "Log de alterações" - -#: views.py:220 +#: views.py:230 msgid "License" msgstr "Licença" +#: views.py:239 +msgid "Current user password change" +msgstr "Alteração de senha do usuário atual" + +#: views.py:254 templates/password_change_done.html:5 +msgid "Your password has been successfully changed." +msgstr "Sua senha foi alterada com êxito." + #: widgets.py:58 msgid "None" msgstr "Nenhum" @@ -174,20 +179,19 @@ msgstr "Nenhum" #: conf/settings.py:15 msgid "" "Temporary directory used site wide to store thumbnails, previews and " -"temporary files. If none is specified, one will be created using tempfile." -"mkdtemp()" -msgstr "" -"Diretório temporário usado para armazenar miniaturas, previews e arquivos " -"temporários. Se nenhum for especificado, um será criado usando tempfile." -"mkdtemp()" +"temporary files. If none is specified, one will be created using " +"tempfile.mkdtemp()" +msgstr "Diretório temporário usado para armazenar miniaturas, previews e arquivos temporários. Se nenhum for especificado, um será criado usando tempfile.mkdtemp()" #: conf/settings.py:65 msgid "" "Controls the mechanism used to authenticated user. Options are: username, " "email" +msgstr "Controla o mecanismo usado para usuário autenticado. As opções são: e-mail, nome de usuário," + +#: conf/settings.py:74 +msgid "Allow non authenticated users, access to all views" msgstr "" -"Controla o mecanismo usado para usuário autenticado. As opções são: e-mail, " -"nome de usuário," #: templates/403.html:3 templates/403.html.py:7 msgid "Insufficient permissions" @@ -269,28 +273,32 @@ msgid "No" msgstr "Não" #: templates/generic_form_instance.html:37 -#: templates/generic_form_subtemplate.html:52 +#: templates/generic_form_subtemplate.html:56 msgid "required" msgstr "exigido" -#: templates/generic_form_subtemplate.html:76 -#: templates/generic_form_subtemplate.html:78 +#: templates/generic_form_subtemplate.html:80 +#: templates/generic_form_subtemplate.html:82 #: templates/generic_list_horizontal_subtemplate.html:51 -#: templates/generic_list_horizontal_subtemplate.html:178 +#: templates/generic_list_horizontal_subtemplate.html:90 #: templates/generic_list_subtemplate.html:52 #: templates/generic_list_subtemplate.html:178 msgid "Save" msgstr "Salvar" -#: templates/generic_form_subtemplate.html:76 -#: templates/generic_form_subtemplate.html:78 +#: templates/generic_form_subtemplate.html:80 +#: templates/generic_form_subtemplate.html:82 #: templates/generic_list_horizontal_subtemplate.html:51 -#: templates/generic_list_horizontal_subtemplate.html:178 +#: templates/generic_list_horizontal_subtemplate.html:90 #: templates/generic_list_subtemplate.html:52 #: templates/generic_list_subtemplate.html:178 msgid "Submit" msgstr "Submeter" +#: templates/generic_form_subtemplate.html:87 +msgid "Cancel" +msgstr "Cancelar" + #: templates/generic_list.html:6 templates/generic_list_horizontal.html:6 #, python-format msgid "List of %(stripped_title)s" @@ -302,9 +310,7 @@ msgstr "Lista de %(stripped_title)s " msgid "" "List of %(title)s (%(start)s - %(end)s out of %(total)s) (Page " "%(page_number)s of %(total_pages)s)" -msgstr "" -"Lista de %(title)s (%(start)s - %(end)s de %(total)s) (Page %(page_number)s " -"of %(total_pages)s)" +msgstr "Lista de %(title)s (%(start)s - %(end)s de %(total)s) (Page %(page_number)s of %(total_pages)s)" #: templates/generic_list_horizontal_subtemplate.html:25 #: templates/generic_list_subtemplate.html:26 @@ -329,6 +335,3 @@ msgstr "Login" #: templates/password_change_form.html:5 msgid "Password change" msgstr "Alterar a senha" - -#~ msgid "Cancel" -#~ msgstr "Cancelar" diff --git a/apps/common/locale/ru/LC_MESSAGES/django.mo b/apps/common/locale/ru/LC_MESSAGES/django.mo index a8f3d3b9e8..e56ec42c1a 100644 Binary files a/apps/common/locale/ru/LC_MESSAGES/django.mo and b/apps/common/locale/ru/LC_MESSAGES/django.mo differ diff --git a/apps/common/locale/ru/LC_MESSAGES/django.po b/apps/common/locale/ru/LC_MESSAGES/django.po index 44c11d8f5c..b2c8e773e7 100644 --- a/apps/common/locale/ru/LC_MESSAGES/django.po +++ b/apps/common/locale/ru/LC_MESSAGES/django.po @@ -1,56 +1,54 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: # Sergey Glita , 2011. msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" -"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" -"PO-Revision-Date: 2011-11-22 19:21+0000\n" -"Last-Translator: gsv70 \n" -"Language-Team: Russian (http://www.transifex.net/projects/p/mayan-edms/team/ru/)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-02 18:18+0000\n" +"Last-Translator: Roberto Rosario \n" +"Language-Team: Russian (http://www.transifex.net/projects/p/mayan-edms/team/" +"ru/)\n" +"Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: ru\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" +"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:17 +#: __init__.py:20 msgid "change password" msgstr "Изменение пароля" -#: __init__.py:18 +#: __init__.py:21 msgid "user details" msgstr "сведения о пользователе" -#: __init__.py:19 +#: __init__.py:22 msgid "edit details" msgstr "изменение сведений" -#: __init__.py:23 __init__.py:29 +#: __init__.py:26 __init__.py:31 msgid "about" msgstr "инфо" -#: __init__.py:24 -msgid "changelog" -msgstr "изменения" - -#: __init__.py:25 +#: __init__.py:27 msgid "license" msgstr "лицензия" -#: forms.py:99 +#: forms.py:101 msgid "Selection" msgstr "Выбор" -#: forms.py:131 +#: forms.py:133 msgid "Email" msgstr "Email" -#: forms.py:142 +#: forms.py:144 msgid "" "Please enter a correct email and password. Note that the password fields is " "case-sensitive." @@ -58,7 +56,7 @@ msgstr "" "Пожалуйста, введите правильный адрес электронной почты и пароль с учетом " "регистра." -#: forms.py:144 +#: forms.py:146 msgid "This account is inactive." msgstr "Эта учетная запись неактивна." @@ -102,70 +100,81 @@ msgstr "Портрет" msgid "Landscape" msgstr "Пейзаж" -#: utils.py:291 +#: models.py:16 +msgid "lock field" +msgstr "" + +#: models.py:43 +msgid "Anonymous user" +msgstr "" + +#: models.py:46 models.py:47 +msgid "anonymous user" +msgstr "" + +#: utils.py:295 msgid "function found" msgstr "функция найдена" -#: utils.py:293 utils.py:295 -#, python-format -msgid "class found: %s" -msgstr "класс найден: %s." - -#: views.py:24 templates/password_change_done.html:5 -msgid "Your password has been successfully changed." -msgstr "Ваш пароль был изменен." - -#: views.py:41 +#: views.py:36 msgid "No action selected." msgstr "Никаких действий не выбрано." -#: views.py:45 +#: views.py:40 msgid "Must select at least one item." msgstr "Необходимо выбрать хотя бы один элемент." -#: views.py:86 +#: views.py:88 #, python-format msgid "%(selection)s added successfully added to %(right_list_title)s." msgstr "%(selection)s успешно добавлен в %(right_list_title)s ." -#: views.py:89 views.py:106 +#: views.py:94 views.py:121 #, python-format msgid "Unable to add %(selection)s to %(right_list_title)s." msgstr "Не удалось добавить %(selection)s до %(right_list_title)s ." -#: views.py:103 +#: views.py:115 #, python-format msgid "%(selection)s added successfully removed from %(right_list_title)s." msgstr "%(selection)s успешно удален из %(right_list_title)s ." -#: views.py:121 +#: views.py:136 msgid "Add" msgstr "Добавить" -#: views.py:132 +#: views.py:147 msgid "Remove" msgstr "Удалить" -#: views.py:155 +#: views.py:170 msgid "current user details" msgstr "данные пользователя" -#: views.py:172 +#: views.py:187 +msgid "E-mail conflict, another user has that same email." +msgstr "" + +#: views.py:190 msgid "Current user's details updated." msgstr "Данные пользователя обновлены." -#: views.py:181 +#: views.py:199 msgid "edit current user details" msgstr "редактировать данные пользователя" -#: views.py:207 -msgid "Changelog" -msgstr "Изменения" - -#: views.py:220 +#: views.py:230 msgid "License" msgstr "Лицензия" +#: views.py:239 +msgid "Current user password change" +msgstr "" + +#: views.py:254 templates/password_change_done.html:5 +msgid "Your password has been successfully changed." +msgstr "Ваш пароль был изменен." + #: widgets.py:58 msgid "None" msgstr "Ни один" @@ -173,8 +182,8 @@ msgstr "Ни один" #: conf/settings.py:15 msgid "" "Temporary directory used site wide to store thumbnails, previews and " -"temporary files. If none is specified, one will be created using " -"tempfile.mkdtemp()" +"temporary files. If none is specified, one will be created using tempfile." +"mkdtemp()" msgstr "" "Временный каталог, используемый сайтом для хранения миниатюр, превью и " "временных файлов. Если он не указан, он будет создан с использованием " @@ -188,6 +197,10 @@ msgstr "" "Управление механизмом, используемым для аутентификации пользователя. " "Возможные варианты: имя пользователя, адрес электронной почты" +#: conf/settings.py:74 +msgid "Allow non authenticated users, access to all views" +msgstr "" + #: templates/403.html:3 templates/403.html.py:7 msgid "Insufficient permissions" msgstr "Недостаточно прав" @@ -268,28 +281,32 @@ msgid "No" msgstr "Нет" #: templates/generic_form_instance.html:37 -#: templates/generic_form_subtemplate.html:52 +#: templates/generic_form_subtemplate.html:56 msgid "required" msgstr "требуется" -#: templates/generic_form_subtemplate.html:76 -#: templates/generic_form_subtemplate.html:78 +#: templates/generic_form_subtemplate.html:80 +#: templates/generic_form_subtemplate.html:82 #: templates/generic_list_horizontal_subtemplate.html:51 -#: templates/generic_list_horizontal_subtemplate.html:178 +#: templates/generic_list_horizontal_subtemplate.html:90 #: templates/generic_list_subtemplate.html:52 #: templates/generic_list_subtemplate.html:178 msgid "Save" msgstr "Сохранить" -#: templates/generic_form_subtemplate.html:76 -#: templates/generic_form_subtemplate.html:78 +#: templates/generic_form_subtemplate.html:80 +#: templates/generic_form_subtemplate.html:82 #: templates/generic_list_horizontal_subtemplate.html:51 -#: templates/generic_list_horizontal_subtemplate.html:178 +#: templates/generic_list_horizontal_subtemplate.html:90 #: templates/generic_list_subtemplate.html:52 #: templates/generic_list_subtemplate.html:178 msgid "Submit" msgstr "Выполнить" +#: templates/generic_form_subtemplate.html:87 +msgid "Cancel" +msgstr "" + #: templates/generic_list.html:6 templates/generic_list_horizontal.html:6 #, python-format msgid "List of %(stripped_title)s" @@ -302,8 +319,8 @@ msgid "" "List of %(title)s (%(start)s - %(end)s out of %(total)s) (Page " "%(page_number)s of %(total_pages)s)" msgstr "" -"Список %(title)s (%(start)s - %(end)s из %(total)s) (Page %(page_number)s из" -" %(total_pages)s)" +"Список %(title)s (%(start)s - %(end)s из %(total)s) (Page %(page_number)s из " +"%(total_pages)s)" #: templates/generic_list_horizontal_subtemplate.html:25 #: templates/generic_list_subtemplate.html:26 @@ -328,5 +345,3 @@ msgstr "Войти" #: templates/password_change_form.html:5 msgid "Password change" msgstr "Изменение пароля" - - diff --git a/apps/common/middleware/login_required_middleware.py b/apps/common/middleware/login_required_middleware.py index 91af7dc92f..94331d32aa 100644 --- a/apps/common/middleware/login_required_middleware.py +++ b/apps/common/middleware/login_required_middleware.py @@ -1,8 +1,12 @@ +from __future__ import absolute_import + import re from django.http import HttpResponseRedirect from django.conf import settings +from ..conf.settings import ALLOW_ANONYMOUS_ACCESS + EXEMPT_URLS = [re.compile(settings.LOGIN_URL.lstrip('/'))] if hasattr(settings, 'LOGIN_EXEMPT_URLS'): EXEMPT_URLS += [re.compile(expr) for expr in settings.LOGIN_EXEMPT_URLS] @@ -20,13 +24,14 @@ class LoginRequiredMiddleware: """ def process_request(self, request): - assert hasattr(request, 'user'), "The Login Required middleware\ - requires authentication middleware to be installed. Edit your\ - MIDDLEWARE_CLASSES setting to insert\ - 'django.contrib.auth.middlware.AuthenticationMiddleware'. If that doesn't\ - work, ensure your TEMPLATE_CONTEXT_PROCESSORS setting includes\ - 'django.core.context_processors.auth'." - if not request.user.is_authenticated(): - path = request.path_info.lstrip('/') - if not any(m.match(path) for m in EXEMPT_URLS): - return HttpResponseRedirect(settings.LOGIN_URL) + if not ALLOW_ANONYMOUS_ACCESS: + assert hasattr(request, 'user'), "The Login Required middleware\ + requires authentication middleware to be installed. Edit your\ + MIDDLEWARE_CLASSES setting to insert\ + 'django.contrib.auth.middlware.AuthenticationMiddleware'. If that doesn't\ + work, ensure your TEMPLATE_CONTEXT_PROCESSORS setting includes\ + 'django.core.context_processors.auth'." + if not request.user.is_authenticated(): + path = request.path_info.lstrip('/') + if not any(m.match(path) for m in EXEMPT_URLS): + return HttpResponseRedirect(settings.LOGIN_URL) diff --git a/apps/common/models.py b/apps/common/models.py index 71a8362390..effe8196ed 100644 --- a/apps/common/models.py +++ b/apps/common/models.py @@ -1,3 +1,47 @@ 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 AnonymousUser -# Create your models here. +SINGLETON_LOCK_ID = 1 + + +class SingletonManager(models.Manager): + def get(self, **kwargs): + instance, created = self.model.objects.get_or_create(lock_id=SINGLETON_LOCK_ID, **kwargs) + return instance + + +class Singleton(models.Model): + lock_id = models.CharField(max_length=1, default=SINGLETON_LOCK_ID, editable=False, verbose_name=_(u'lock field'), unique=True) + + objects = SingletonManager() + + def save(self, *args, **kwargs): + self.id = 1 + super(Singleton, self).save(*args, **kwargs) + + def delete(self): + pass + + class Meta: + abstract = True + + +class AnonymousUserSingletonManager(SingletonManager): + def passthru_check(self, user): + if isinstance(user, AnonymousUser): + return self.model.objects.get() + else: + return user + + +class AnonymousUserSingleton(Singleton): + objects = AnonymousUserSingletonManager() + + def __unicode__(self): + return ugettext('Anonymous user') + + class Meta: + verbose_name = _(u'anonymous user') + verbose_name_plural = _(u'anonymous user') diff --git a/apps/common/templates/generic_form_subtemplate.html b/apps/common/templates/generic_form_subtemplate.html index e502d78fd6..37eeee5db4 100644 --- a/apps/common/templates/generic_form_subtemplate.html +++ b/apps/common/templates/generic_form_subtemplate.html @@ -35,6 +35,10 @@ {% endif %} + {% if previous %} + + {% endif %} + {% for hidden_field in hidden_fields %} {{ hidden_field.as_hidden }} {% endfor %} @@ -77,6 +81,13 @@ {% endif %} {% if submit_label %}{{ submit_label }}{% else %}{% if object %}{% trans "Save" %}{% else %}{% trans "Submit" %}{% endif %}{% endif %} + + {% if previous %} + + {% if cancel_label %}{{ cancel_label }}{% else %}{% trans 'Cancel' %}{% endif %} {% if cancel_label %}{{ cancel_label }}{% else %}{% trans "Cancel" %}{% endif %} + + {% endif %} + {% comment %} {% trans 'Cancel' %} {% trans 'Cancel' %} diff --git a/apps/common/templates/generic_list_horizontal_subtemplate.html b/apps/common/templates/generic_list_horizontal_subtemplate.html index 5edd4f47fe..8b62536db7 100644 --- a/apps/common/templates/generic_list_horizontal_subtemplate.html +++ b/apps/common/templates/generic_list_horizontal_subtemplate.html @@ -59,95 +59,7 @@
{% endif %} -{% comment %} - - - {% if not hide_header %} - - {% if multi_select or multi_select_as_buttons %} - - {% endif %} - {% if not hide_object %} - - {% endif %} - - {% for column in extra_columns_preffixed %} - - {% endfor %} - - {% for column in object_list.0|get_model_list_columns %} - - {% endfor %} - - {% for column in extra_columns %} - - {% endfor %} - - {% if not hide_links %} - - {% endif %} - - {% endif %} - {% for object in object_list %} - - {% if multi_select or multi_select_as_buttons %} - - {% endif %} - {% if not hide_object %} - {% if main_object %} - {% with object|object_property:main_object as object %} - - {% endwith %} - {% else %} - - {% endif %} - {% endif %} - {% for column in extra_columns_preffixed %} - {% if column.keep_together %} - - {% else %} - - {% endif %} - {% endfor %} - {% if not hide_columns %} - {% for column in object|get_model_list_columns %} - - {% endfor %} - {% endif %} - {% for column in extra_columns %} - {% if column.keep_together %} - - {% else %} - - {% endif %} - {% endfor %} - {% if not hide_links %} - {% if list_object_variable_name %} - {% copy_variable object as list_object_variable_name %} - {% copy_variable list_object_variable_name as "navigation_object_name" %} - {% endif %} - - {% endif %} - - {% empty %} - - {% endfor %} - -
{% trans "Identifier" %}{{ column.name|capfirst }}{{ column.name|capfirst }}{{ column.name|capfirst }} 
{% if not hide_link %}{{ object }}{% else %}{{ object }}{% endif %}{% if not hide_link %}{{ object }}{% else %}{{ object }}{% endif %} - {{ object|object_property:column.attribute|make_non_breakable }} - {{ object|object_property:column.attribute }}{{ object|object_property:column.attribute }} - {{ object|object_property:column.attribute|make_non_breakable }} - {{ object|object_property:column.attribute }} - {% if navigation_object_links %} - {% with navigation_object_links as overrided_object_links %} - {% object_navigation_template %} - {% endwith %} - {% else %} - {% object_navigation_template %} - {% endif %} -
{% blocktrans with title|striptags as stripped_title %}There are no {{ stripped_title }}{% endblocktrans %}
-{% endcomment %}
') output.append( u'
%s' % @@ -68,9 +68,9 @@ class SmartLinkInstanceForm(forms.Form): smart_link_instances = kwargs.pop('smart_link_instances', None) links = kwargs.pop('links', None) current_document = kwargs.pop('current_document', None) - + super(SmartLinkInstanceForm, self).__init__(*args, **kwargs) - + for smart_link_instance, data in smart_link_instances.items(): self.fields['preview-%s' % smart_link_instance] = forms.CharField( widget=SmartLinkImageWidget(), diff --git a/apps/linking/locale/en/LC_MESSAGES/django.po b/apps/linking/locale/en/LC_MESSAGES/django.po index 17272844d4..e9e180dde8 100644 --- a/apps/linking/locale/en/LC_MESSAGES/django.po +++ b/apps/linking/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,67 +17,51 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: __init__.py:11 -msgid "View existing smart links" -msgstr "" - -#: __init__.py:12 -msgid "Create new smart links" -msgstr "" - -#: __init__.py:13 -msgid "Delete smart links" -msgstr "" - -#: __init__.py:14 -msgid "Edit smart links" -msgstr "" - -#: __init__.py:16 -msgid "Smart links" -msgstr "" - -#: __init__.py:22 +#: __init__.py:17 msgid "smart links actions" msgstr "" -#: __init__.py:23 __init__.py:25 models.py:21 views.py:105 +#: __init__.py:18 __init__.py:20 models.py:23 views.py:132 msgid "smart links" msgstr "" -#: __init__.py:26 +#: __init__.py:21 msgid "smart links list" msgstr "" -#: __init__.py:27 +#: __init__.py:22 msgid "create new smart link" msgstr "" -#: __init__.py:28 __init__.py:33 +#: __init__.py:23 __init__.py:28 msgid "edit" msgstr "" -#: __init__.py:29 __init__.py:34 +#: __init__.py:24 __init__.py:29 msgid "delete" msgstr "" -#: __init__.py:31 +#: __init__.py:26 msgid "conditions" msgstr "" -#: __init__.py:32 +#: __init__.py:27 msgid "create condition" msgstr "" -#: forms.py:48 +#: __init__.py:31 +msgid "ACLs" +msgstr "" + +#: forms.py:50 msgid "Pages" msgstr "" -#: forms.py:54 +#: forms.py:56 msgid "Select" msgstr "" -#: forms.py:61 +#: forms.py:63 msgid "Click on the image for full size view of the first page." msgstr "" @@ -149,164 +133,184 @@ msgstr "" msgid "is in regular expression (case insensitive)" msgstr "" -#: models.py:10 +#: models.py:12 msgid "title" msgstr "" -#: models.py:11 views.py:108 +#: models.py:13 views.py:135 msgid "dynamic title" msgstr "" -#: models.py:11 models.py:29 +#: models.py:13 models.py:31 msgid "" "This expression will be evaluated against the current selected document. " "The document metadata is available as variables `metadata` and document " "properties under the variable `document`." msgstr "" -#: models.py:12 models.py:31 views.py:109 views.py:196 +#: models.py:14 models.py:33 views.py:136 views.py:232 msgid "enabled" msgstr "" -#: models.py:20 models.py:25 views.py:256 views.py:287 +#: models.py:22 models.py:27 views.py:296 views.py:330 msgid "smart link" msgstr "" -#: models.py:26 +#: models.py:28 msgid "The inclusion is ignored for the first item." msgstr "" -#: models.py:27 +#: models.py:29 msgid "foreign document data" msgstr "" -#: models.py:27 +#: models.py:29 msgid "" "This represents the metadata of all other documents. Available objects: " "`document.` and `metadata.`." msgstr "" -#: models.py:29 +#: models.py:31 msgid "expression" msgstr "" -#: models.py:30 +#: models.py:32 msgid "negated" msgstr "" -#: models.py:30 +#: models.py:32 msgid "Inverts the logic of the operator." msgstr "" -#: models.py:34 +#: models.py:36 msgid "not" msgstr "" -#: models.py:37 +#: models.py:39 msgid "link condition" msgstr "" -#: models.py:38 +#: models.py:40 msgid "link conditions" msgstr "" -#: views.py:32 +#: permissions.py:7 +msgid "Smart links" +msgstr "" + +#: permissions.py:9 +msgid "View existing smart links" +msgstr "" + +#: permissions.py:10 +msgid "Create new smart links" +msgstr "" + +#: permissions.py:11 +msgid "Delete smart links" +msgstr "" + +#: permissions.py:12 +msgid "Edit smart links" +msgstr "" + +#: views.py:40 msgid "No action selected." msgstr "" -#: views.py:47 +#: views.py:59 #, python-format msgid "documents in smart link: %(group)s" msgstr "" -#: views.py:65 +#: views.py:75 #, python-format msgid "Smart link query error: %s" msgstr "" -#: views.py:76 +#: views.py:97 #, python-format msgid "smart links (%s)" msgstr "" -#: views.py:90 +#: views.py:111 msgid "There no defined smart links for the current document." msgstr "" -#: views.py:124 +#: views.py:152 #, python-format msgid "Smart link: %s created successfully." msgstr "" -#: views.py:131 +#: views.py:159 msgid "Create new smart link" msgstr "" -#: views.py:144 +#: views.py:175 #, python-format msgid "Smart link: %s edited successfully." msgstr "" -#: views.py:153 +#: views.py:184 #, python-format msgid "Edit smart link: %s" msgstr "" -#: views.py:168 +#: views.py:202 #, python-format msgid "Smart link: %s deleted successfully." msgstr "" -#: views.py:170 +#: views.py:204 #, python-format msgid "Error deleting smart link: %(smart_link)s; %(error)s." msgstr "" -#: views.py:180 +#: views.py:213 #, python-format msgid "Are you sure you wish to delete smart link: %s?" msgstr "" -#: views.py:193 +#: views.py:229 #, python-format msgid "conditions for smart link: %s" msgstr "" -#: views.py:216 +#: views.py:254 #, python-format msgid "Smart link condition: \"%s\" created successfully." msgstr "" -#: views.py:223 +#: views.py:261 #, python-format msgid "Add new conditions to smart link: \"%s\"" msgstr "" -#: views.py:243 +#: views.py:283 #, python-format msgid "Smart link condition: \"%s\" edited successfully." msgstr "" -#: views.py:250 +#: views.py:290 msgid "Edit smart link condition" msgstr "" -#: views.py:257 views.py:288 +#: views.py:297 views.py:331 msgid "condition" msgstr "" -#: views.py:274 +#: views.py:317 #, python-format msgid "Smart link condition: \"%s\" deleted successfully." msgstr "" -#: views.py:276 +#: views.py:319 #, python-format msgid "" "Error deleting smart link condition: %(smart_link_condition)s; %(error)s." msgstr "" -#: views.py:290 +#: views.py:333 #, python-format msgid "Are you sure you wish to delete smart link condition: \"%s\"?" msgstr "" diff --git a/apps/linking/locale/es/LC_MESSAGES/django.mo b/apps/linking/locale/es/LC_MESSAGES/django.mo index 17ed6df31e..b4ce8f22c0 100644 Binary files a/apps/linking/locale/es/LC_MESSAGES/django.mo and b/apps/linking/locale/es/LC_MESSAGES/django.mo differ diff --git a/apps/linking/locale/es/LC_MESSAGES/django.po b/apps/linking/locale/es/LC_MESSAGES/django.po index 653a34abd0..a1bc222068 100644 --- a/apps/linking/locale/es/LC_MESSAGES/django.po +++ b/apps/linking/locale/es/LC_MESSAGES/django.po @@ -1,84 +1,69 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: -# Roberto Rosario , 2011. +# Roberto Rosario , 2011, 2012. msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" -"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" -"PO-Revision-Date: 2011-11-22 20:56+0000\n" -"Last-Translator: rosarior \n" -"Language-Team: Spanish (Castilian) (http://www.transifex.net/projects/p/mayan-edms/team/es/)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-02 18:29+0000\n" +"Last-Translator: Roberto Rosario \n" +"Language-Team: Spanish (Castilian) (http://www.transifex.net/projects/p/" +"mayan-edms/team/es/)\n" +"Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: es\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -#: __init__.py:11 -msgid "View existing smart links" -msgstr "Ver enlaces inteligentes existentes" - -#: __init__.py:12 -msgid "Create new smart links" -msgstr "Crear nuevos enlaces inteligentes" - -#: __init__.py:13 -msgid "Delete smart links" -msgstr "Eliminar enlaces inteligentes" - -#: __init__.py:14 -msgid "Edit smart links" -msgstr "Editar enlaces inteligentes" - -#: __init__.py:16 -msgid "Smart links" -msgstr "Enlaces inteligentes" - -#: __init__.py:22 +#: __init__.py:17 msgid "smart links actions" msgstr "acciones de enlaces inteligentes" -#: __init__.py:23 __init__.py:25 models.py:21 views.py:105 +#: __init__.py:18 __init__.py:20 models.py:23 views.py:132 msgid "smart links" msgstr "enlaces inteligentes" -#: __init__.py:26 +#: __init__.py:21 msgid "smart links list" msgstr "lista de enlaces inteligentes" -#: __init__.py:27 +#: __init__.py:22 msgid "create new smart link" msgstr "crear un enlace inteligente nuevo" -#: __init__.py:28 __init__.py:33 +#: __init__.py:23 __init__.py:28 msgid "edit" msgstr "editar" -#: __init__.py:29 __init__.py:34 +#: __init__.py:24 __init__.py:29 msgid "delete" msgstr "borrar" -#: __init__.py:31 +#: __init__.py:26 msgid "conditions" msgstr "condiciones" -#: __init__.py:32 +#: __init__.py:27 msgid "create condition" msgstr "crear condicion" -#: forms.py:48 +#: __init__.py:31 +msgid "ACLs" +msgstr "LCAs" + +#: forms.py:50 msgid "Pages" msgstr "Páginas" -#: forms.py:54 +#: forms.py:56 msgid "Select" msgstr "Seleccionar" -#: forms.py:61 +#: forms.py:63 msgid "Click on the image for full size view of the first page." msgstr "" "Haga clic en la imagen para ver el tamaño completo de la primera página." @@ -151,41 +136,41 @@ msgstr "está en la expresión regular" msgid "is in regular expression (case insensitive)" msgstr "está en la expresión regular (mayúsculas y minúsculas)" -#: models.py:10 +#: models.py:12 msgid "title" msgstr "título" -#: models.py:11 views.py:108 +#: models.py:13 views.py:135 msgid "dynamic title" msgstr "título dinámico" -#: models.py:11 models.py:29 +#: models.py:13 models.py:31 msgid "" "This expression will be evaluated against the current selected document. " "The document metadata is available as variables `metadata` and document " "properties under the variable `document`." msgstr "" -"Esta expresión sera evaluada con respecto al documento seleccionado actual." -" Los metadatos del documento están disponible como variables `metadata` y " -"las propiedades del documento en la variable `document`." +"Esta expresión sera evaluada con respecto al documento seleccionado actual. " +"Los metadatos del documento están disponible como variables `metadata` y las " +"propiedades del documento en la variable `document`." -#: models.py:12 models.py:31 views.py:109 views.py:196 +#: models.py:14 models.py:33 views.py:136 views.py:232 msgid "enabled" msgstr "habilitado" -#: models.py:20 models.py:25 views.py:256 views.py:287 +#: models.py:22 models.py:27 views.py:296 views.py:330 msgid "smart link" msgstr "enlace inteligente" -#: models.py:26 +#: models.py:28 msgid "The inclusion is ignored for the first item." msgstr "La inclusión es ignorada para el primer artículo." -#: models.py:27 +#: models.py:29 msgid "foreign document data" msgstr "datos de documento foráneo" -#: models.py:27 +#: models.py:29 msgid "" "This represents the metadata of all other documents. Available objects: " "`document.` and `metadata.`." @@ -194,122 +179,142 @@ msgstr "" "disponibles: `document.` y `metadata.`." -#: models.py:29 +#: models.py:31 msgid "expression" msgstr "expresión" -#: models.py:30 +#: models.py:32 msgid "negated" msgstr "negado" -#: models.py:30 +#: models.py:32 msgid "Inverts the logic of the operator." msgstr "Invierte la lógica del operador." -#: models.py:34 +#: models.py:36 msgid "not" msgstr "no" -#: models.py:37 +#: models.py:39 msgid "link condition" msgstr "condición de enlace" -#: models.py:38 +#: models.py:40 msgid "link conditions" msgstr "condiciones de enlace" -#: views.py:32 +#: permissions.py:7 +msgid "Smart links" +msgstr "Enlaces inteligentes" + +#: permissions.py:9 +msgid "View existing smart links" +msgstr "Ver enlaces inteligentes existentes" + +#: permissions.py:10 +msgid "Create new smart links" +msgstr "Crear nuevos enlaces inteligentes" + +#: permissions.py:11 +msgid "Delete smart links" +msgstr "Eliminar enlaces inteligentes" + +#: permissions.py:12 +msgid "Edit smart links" +msgstr "Editar enlaces inteligentes" + +#: views.py:40 msgid "No action selected." msgstr "Ninguna acción seleccionada." -#: views.py:47 +#: views.py:59 #, python-format msgid "documents in smart link: %(group)s" msgstr "documentos en el enlace inteligente: %(group)s" -#: views.py:65 +#: views.py:75 #, python-format msgid "Smart link query error: %s" msgstr "Error en consulta de enlace inteligente: %s" -#: views.py:76 +#: views.py:97 #, python-format msgid "smart links (%s)" msgstr "enlaces inteligentes (%s)" -#: views.py:90 +#: views.py:111 msgid "There no defined smart links for the current document." msgstr "No hay enlaces inteligentes definidos para el documento actual." -#: views.py:124 +#: views.py:152 #, python-format msgid "Smart link: %s created successfully." msgstr "Enlace inteligente: %s creado exitosamente." -#: views.py:131 +#: views.py:159 msgid "Create new smart link" msgstr "Crear un enlace inteligente nuevo" -#: views.py:144 +#: views.py:175 #, python-format msgid "Smart link: %s edited successfully." msgstr "Enlace inteligente: %s editado exitosamente." -#: views.py:153 +#: views.py:184 #, python-format msgid "Edit smart link: %s" msgstr "Editar enlace inteligente: %s" -#: views.py:168 +#: views.py:202 #, python-format msgid "Smart link: %s deleted successfully." msgstr "Enlace inteligente: %s eliminado exitosamente." -#: views.py:170 +#: views.py:204 #, python-format msgid "Error deleting smart link: %(smart_link)s; %(error)s." msgstr "" "Error al tratar de eliminar enlace inteligente: %(smart_link)s; %(error)s." -#: views.py:180 +#: views.py:213 #, python-format msgid "Are you sure you wish to delete smart link: %s?" msgstr "¿Está seguro que desea eliminar el enlace inteligente: %s?" -#: views.py:193 +#: views.py:229 #, python-format msgid "conditions for smart link: %s" msgstr "condiciones de enlace inteligente: %s" -#: views.py:216 +#: views.py:254 #, python-format msgid "Smart link condition: \"%s\" created successfully." msgstr "Condición de enlace inteligente: \"%s\" creada exitosamente." -#: views.py:223 +#: views.py:261 #, python-format msgid "Add new conditions to smart link: \"%s\"" msgstr "Añadir nuevas condiciones de enlace inteligente: \"%s\"" -#: views.py:243 +#: views.py:283 #, python-format msgid "Smart link condition: \"%s\" edited successfully." msgstr "Condición de enlace inteligente: \"%s\", editada exitosamente." -#: views.py:250 +#: views.py:290 msgid "Edit smart link condition" msgstr "Editar condición de enlace inteligente" -#: views.py:257 views.py:288 +#: views.py:297 views.py:331 msgid "condition" msgstr "condición" -#: views.py:274 +#: views.py:317 #, python-format msgid "Smart link condition: \"%s\" deleted successfully." msgstr "Condición de enlace inteligente: \"%s\" ha eliminada exitosamente." -#: views.py:276 +#: views.py:319 #, python-format msgid "" "Error deleting smart link condition: %(smart_link_condition)s; %(error)s." @@ -317,7 +322,7 @@ msgstr "" "Error al tratar de eliminar la condición de enlace inteligente: " "%(smart_link_condition)s; %(error)s." -#: views.py:290 +#: views.py:333 #, python-format msgid "Are you sure you wish to delete smart link condition: \"%s\"?" msgstr "" @@ -326,8 +331,7 @@ msgstr "" #: conf/settings.py:11 msgid "Show smart link that don't return any documents." msgstr "" -"Mostrar enlace inteligente que no devuelven ningun documentos como " -"resultado." +"Mostrar enlace inteligente que no devuelven ningun documentos como resultado." #: templates/smart_links_help.html:3 msgid "What are smart links?" @@ -347,5 +351,3 @@ msgstr "" "documentos que se relacionan de alguna manera al documento que se muestra y " "permite a los usuarios la capacidad de navegar entre los documentos " "vinculados con mucha facilidad." - - diff --git a/apps/linking/locale/it/LC_MESSAGES/django.mo b/apps/linking/locale/it/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..0fd371e57b Binary files /dev/null and b/apps/linking/locale/it/LC_MESSAGES/django.mo differ diff --git a/apps/linking/locale/it/LC_MESSAGES/django.po b/apps/linking/locale/it/LC_MESSAGES/django.po new file mode 100644 index 0000000000..5951339586 --- /dev/null +++ b/apps/linking/locale/it/LC_MESSAGES/django.po @@ -0,0 +1,350 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# , 2011. +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-02 18:19+0000\n" +"Last-Translator: Roberto Rosario \n" +"Language-Team: Italian (http://www.transifex.net/projects/p/mayan-edms/team/" +"it/)\n" +"Language: it\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:17 +msgid "smart links actions" +msgstr "azioni dei link intelligenti" + +#: __init__.py:18 __init__.py:20 models.py:23 views.py:132 +msgid "smart links" +msgstr "link intelligenti" + +#: __init__.py:21 +msgid "smart links list" +msgstr "lista dei link intelligenti" + +#: __init__.py:22 +msgid "create new smart link" +msgstr "crea nuovi link intelligenti" + +#: __init__.py:23 __init__.py:28 +msgid "edit" +msgstr "modifica" + +#: __init__.py:24 __init__.py:29 +msgid "delete" +msgstr "cancella" + +#: __init__.py:26 +msgid "conditions" +msgstr "condizioni" + +#: __init__.py:27 +msgid "create condition" +msgstr "crea condizioni" + +#: __init__.py:31 +msgid "ACLs" +msgstr "" + +#: forms.py:50 +msgid "Pages" +msgstr "Pagine" + +#: forms.py:56 +msgid "Select" +msgstr "Seleziona" + +#: forms.py:63 +msgid "Click on the image for full size view of the first page." +msgstr "Click sull'immagine per vedere la prima pagina" + +#: literals.py:7 +msgid "and" +msgstr "e" + +#: literals.py:8 +msgid "or" +msgstr "o" + +#: literals.py:12 +msgid "is equal to" +msgstr "è uguale" + +#: literals.py:13 +msgid "is equal to (case insensitive)" +msgstr "è uguale a (case insensitive)" + +#: literals.py:14 +msgid "contains" +msgstr "contiene" + +#: literals.py:15 +msgid "contains (case insensitive)" +msgstr "contiene (case insensitive)" + +#: literals.py:16 +msgid "is in" +msgstr "è in" + +#: literals.py:17 +msgid "is greater than" +msgstr "è più grande di" + +#: literals.py:18 +msgid "is greater than or equal to" +msgstr "è più grande o uguale a" + +#: literals.py:19 +msgid "is less than" +msgstr "è inferiore a " + +#: literals.py:20 +msgid "is less than or equal to" +msgstr "è inferiore o uguale a" + +#: literals.py:21 +msgid "starts with" +msgstr "inizia con" + +#: literals.py:22 +msgid "starts with (case insensitive)" +msgstr "inizia con (case insensitive)" + +#: literals.py:23 +msgid "ends with" +msgstr "finisce con" + +#: literals.py:24 +msgid "ends with (case insensitive)" +msgstr "finisce con (case insensitive)" + +#: literals.py:25 +msgid "is in regular expression" +msgstr "è un'espressione regolare" + +#: literals.py:26 +msgid "is in regular expression (case insensitive)" +msgstr "è un'espressione regolare (case insensitive)" + +#: models.py:12 +msgid "title" +msgstr "titolo" + +#: models.py:13 views.py:135 +msgid "dynamic title" +msgstr "titolo dinamico" + +#: models.py:13 models.py:31 +msgid "" +"This expression will be evaluated against the current selected document. " +"The document metadata is available as variables `metadata` and document " +"properties under the variable `document`." +msgstr "" +"Questa espressione sarà valutata per il documento corrente selezionato. I " +"metadati del documento sono disponibile come variabili `metadati` e le " +"proprietà dei documenti sotto la variabile `documento`." + +#: models.py:14 models.py:33 views.py:136 views.py:232 +msgid "enabled" +msgstr "abilitato" + +#: models.py:22 models.py:27 views.py:296 views.py:330 +msgid "smart link" +msgstr "link intelligente" + +#: models.py:28 +msgid "The inclusion is ignored for the first item." +msgstr "L'inserimento viene ignorato per la prima voce." + +#: models.py:29 +msgid "foreign document data" +msgstr "dati del documento estero" + +#: models.py:29 +msgid "" +"This represents the metadata of all other documents. Available objects: " +"`document.` and `metadata.`." +msgstr "" +"Questo rappresenta i metadati di tutti gli altri documenti. Oggetti " +"disponibili: `document.` e `metadata.`." + +#: models.py:31 +msgid "expression" +msgstr "espressione" + +#: models.py:32 +msgid "negated" +msgstr "negato" + +#: models.py:32 +msgid "Inverts the logic of the operator." +msgstr "Inverti la logica dell'operazione" + +#: models.py:36 +msgid "not" +msgstr "not" + +#: models.py:39 +msgid "link condition" +msgstr "condizione per il link" + +#: models.py:40 +msgid "link conditions" +msgstr "condizioni per i link" + +#: permissions.py:7 +msgid "Smart links" +msgstr "Link intelligenti" + +#: permissions.py:9 +msgid "View existing smart links" +msgstr "Vista intelligente dei link esistenti" + +#: permissions.py:10 +msgid "Create new smart links" +msgstr "Crea link intelligenti" + +#: permissions.py:11 +msgid "Delete smart links" +msgstr "Cancella link intelligenti" + +#: permissions.py:12 +msgid "Edit smart links" +msgstr "Modifica link intelligenti" + +#: views.py:40 +msgid "No action selected." +msgstr "Nessuna azione selezionata " + +#: views.py:59 +#, python-format +msgid "documents in smart link: %(group)s" +msgstr "documenti nei link intelligenti: %(group)s" + +#: views.py:75 +#, python-format +msgid "Smart link query error: %s" +msgstr "Interrogazione dei link intelligenti, errore: %s" + +#: views.py:97 +#, python-format +msgid "smart links (%s)" +msgstr "link intelligenti (%s)" + +#: views.py:111 +msgid "There no defined smart links for the current document." +msgstr "Non ci sono link intelligenti per questo documento." + +#: views.py:152 +#, python-format +msgid "Smart link: %s created successfully." +msgstr "Link inteligente: %s creato con successo." + +#: views.py:159 +msgid "Create new smart link" +msgstr "Crea un nuovo link intelligente" + +#: views.py:175 +#, python-format +msgid "Smart link: %s edited successfully." +msgstr "Link intelligente: %s modificato con successo." + +#: views.py:184 +#, python-format +msgid "Edit smart link: %s" +msgstr "Modifica il link intelligente: %s" + +#: views.py:202 +#, python-format +msgid "Smart link: %s deleted successfully." +msgstr "Link intelligente: %s cancellato con successo." + +#: views.py:204 +#, python-format +msgid "Error deleting smart link: %(smart_link)s; %(error)s." +msgstr "Errore link intelligente: %(smart_link)s; %(error)s." + +#: views.py:213 +#, python-format +msgid "Are you sure you wish to delete smart link: %s?" +msgstr "Sei sicuro di voler cancella questo link intelligente: %s?" + +#: views.py:229 +#, python-format +msgid "conditions for smart link: %s" +msgstr "condizioni per il link intelligente: %s" + +#: views.py:254 +#, python-format +msgid "Smart link condition: \"%s\" created successfully." +msgstr "Condizione per il link intelligente: \"%s\" creata con successo." + +#: views.py:261 +#, python-format +msgid "Add new conditions to smart link: \"%s\"" +msgstr "Aggiungi una nuova condizione al link intelligente: \"%s\"" + +#: views.py:283 +#, python-format +msgid "Smart link condition: \"%s\" edited successfully." +msgstr "Condizione per il link intelligente: \"%s\" modificato con successo." + +#: views.py:290 +msgid "Edit smart link condition" +msgstr "Modifica condizioni per i link intelligenti" + +#: views.py:297 views.py:331 +msgid "condition" +msgstr "condizioni" + +#: views.py:317 +#, python-format +msgid "Smart link condition: \"%s\" deleted successfully." +msgstr "Condizioni per il link intelligente: \"%s\" cancellato con successo." + +#: views.py:319 +#, python-format +msgid "" +"Error deleting smart link condition: %(smart_link_condition)s; %(error)s." +msgstr "" +"Errore nella cancellazione del link intelligente: %(smart_link_condition)s; " +"%(error)s." + +#: views.py:333 +#, python-format +msgid "Are you sure you wish to delete smart link condition: \"%s\"?" +msgstr "" +"Sei sicuro di voler cancellare le condizioni per il link intelligente : \"%s" +"\"?" + +#: conf/settings.py:11 +msgid "Show smart link that don't return any documents." +msgstr "Mostra i link intelligenti che non restituiscono documenti" + +#: templates/smart_links_help.html:3 +msgid "What are smart links?" +msgstr "Che cosa sono i link intelligenti ?" + +#: templates/smart_links_help.html:4 +msgid "" +"Smart links are a set of conditional statements that are used to query the " +"database using the current document the user is accessing as the data " +"source, the results of these queries are a list of documents that relate in " +"some manner to the document being displayed and allow users the ability to " +"jump to and from linked documents very easily." +msgstr "" +"Collegamenti intelligenti sono un insieme di istruzioni condizionali che " +"vengono utilizzati per interrogare il database utilizzando il documento " +"corrente l'utente sta accedendo come origine dati, i risultati di queste " +"query sono un elenco di documenti che riguardano in qualche modo al " +"documento e consentire la visualizzazione agli utenti la possibilità di " +"saltare da e per i documenti collegati molto facilmente." diff --git a/apps/linking/locale/pl/LC_MESSAGES/django.mo b/apps/linking/locale/pl/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..8acf791a6f Binary files /dev/null and b/apps/linking/locale/pl/LC_MESSAGES/django.mo differ diff --git a/apps/linking/locale/pl/LC_MESSAGES/django.po b/apps/linking/locale/pl/LC_MESSAGES/django.po new file mode 100644 index 0000000000..e24d50d2f7 --- /dev/null +++ b/apps/linking/locale/pl/LC_MESSAGES/django.po @@ -0,0 +1,334 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# , 2012. +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-20 16:14+0000\n" +"Last-Translator: mic \n" +"Language-Team: Polish (http://www.transifex.net/projects/p/mayan-edms/language/pl/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pl\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" + +#: __init__.py:17 +msgid "smart links actions" +msgstr "" + +#: __init__.py:18 __init__.py:20 models.py:23 views.py:132 +msgid "smart links" +msgstr "" + +#: __init__.py:21 +msgid "smart links list" +msgstr "" + +#: __init__.py:22 +msgid "create new smart link" +msgstr "" + +#: __init__.py:23 __init__.py:28 +msgid "edit" +msgstr "edycja" + +#: __init__.py:24 __init__.py:29 +msgid "delete" +msgstr "kasuj" + +#: __init__.py:26 +msgid "conditions" +msgstr "" + +#: __init__.py:27 +msgid "create condition" +msgstr "" + +#: __init__.py:31 +msgid "ACLs" +msgstr "" + +#: forms.py:50 +msgid "Pages" +msgstr "Strony" + +#: forms.py:56 +msgid "Select" +msgstr "Wybierz" + +#: forms.py:63 +msgid "Click on the image for full size view of the first page." +msgstr "" + +#: literals.py:7 +msgid "and" +msgstr "i" + +#: literals.py:8 +msgid "or" +msgstr "lub" + +#: literals.py:12 +msgid "is equal to" +msgstr "jest równy" + +#: literals.py:13 +msgid "is equal to (case insensitive)" +msgstr "jest równa (wielkość liter ma znaczenie)" + +#: literals.py:14 +msgid "contains" +msgstr "zawiera" + +#: literals.py:15 +msgid "contains (case insensitive)" +msgstr "zawiera (wielkość liter ma znaczenie)" + +#: literals.py:16 +msgid "is in" +msgstr "jest" + +#: literals.py:17 +msgid "is greater than" +msgstr "jest większa niż" + +#: literals.py:18 +msgid "is greater than or equal to" +msgstr "jest większa lub równa" + +#: literals.py:19 +msgid "is less than" +msgstr "jest mniejsza niż" + +#: literals.py:20 +msgid "is less than or equal to" +msgstr "jest mniejsza lub równa" + +#: literals.py:21 +msgid "starts with" +msgstr "zaczyna się" + +#: literals.py:22 +msgid "starts with (case insensitive)" +msgstr "rozpoczyna się (wielkość liter ma znaczenie)" + +#: literals.py:23 +msgid "ends with" +msgstr "kończy" + +#: literals.py:24 +msgid "ends with (case insensitive)" +msgstr "kończy (wielkość liter ma znaczenie)" + +#: literals.py:25 +msgid "is in regular expression" +msgstr "" + +#: literals.py:26 +msgid "is in regular expression (case insensitive)" +msgstr "" + +#: models.py:12 +msgid "title" +msgstr "tytuł" + +#: models.py:13 views.py:135 +msgid "dynamic title" +msgstr "dynamiczny tytuł" + +#: models.py:13 models.py:31 +msgid "" +"This expression will be evaluated against the current selected document. " +"The document metadata is available as variables `metadata` and document " +"properties under the variable `document`." +msgstr "" + +#: models.py:14 models.py:33 views.py:136 views.py:232 +msgid "enabled" +msgstr "włączony" + +#: models.py:22 models.py:27 views.py:296 views.py:330 +msgid "smart link" +msgstr "" + +#: models.py:28 +msgid "The inclusion is ignored for the first item." +msgstr "" + +#: models.py:29 +msgid "foreign document data" +msgstr "" + +#: models.py:29 +msgid "" +"This represents the metadata of all other documents. Available objects: " +"`document.` and `metadata.`." +msgstr "" + +#: models.py:31 +msgid "expression" +msgstr "" + +#: models.py:32 +msgid "negated" +msgstr "" + +#: models.py:32 +msgid "Inverts the logic of the operator." +msgstr "" + +#: models.py:36 +msgid "not" +msgstr "nie" + +#: models.py:39 +msgid "link condition" +msgstr "" + +#: models.py:40 +msgid "link conditions" +msgstr "" + +#: permissions.py:7 +msgid "Smart links" +msgstr "" + +#: permissions.py:9 +msgid "View existing smart links" +msgstr "" + +#: permissions.py:10 +msgid "Create new smart links" +msgstr "" + +#: permissions.py:11 +msgid "Delete smart links" +msgstr "" + +#: permissions.py:12 +msgid "Edit smart links" +msgstr "" + +#: views.py:40 +msgid "No action selected." +msgstr "Żadne działanie nie wybrane." + +#: views.py:59 +#, python-format +msgid "documents in smart link: %(group)s" +msgstr "" + +#: views.py:75 +#, python-format +msgid "Smart link query error: %s" +msgstr "" + +#: views.py:97 +#, python-format +msgid "smart links (%s)" +msgstr "" + +#: views.py:111 +msgid "There no defined smart links for the current document." +msgstr "" + +#: views.py:152 +#, python-format +msgid "Smart link: %s created successfully." +msgstr "" + +#: views.py:159 +msgid "Create new smart link" +msgstr "" + +#: views.py:175 +#, python-format +msgid "Smart link: %s edited successfully." +msgstr "" + +#: views.py:184 +#, python-format +msgid "Edit smart link: %s" +msgstr "" + +#: views.py:202 +#, python-format +msgid "Smart link: %s deleted successfully." +msgstr "" + +#: views.py:204 +#, python-format +msgid "Error deleting smart link: %(smart_link)s; %(error)s." +msgstr "" + +#: views.py:213 +#, python-format +msgid "Are you sure you wish to delete smart link: %s?" +msgstr "" + +#: views.py:229 +#, python-format +msgid "conditions for smart link: %s" +msgstr "" + +#: views.py:254 +#, python-format +msgid "Smart link condition: \"%s\" created successfully." +msgstr "" + +#: views.py:261 +#, python-format +msgid "Add new conditions to smart link: \"%s\"" +msgstr "" + +#: views.py:283 +#, python-format +msgid "Smart link condition: \"%s\" edited successfully." +msgstr "" + +#: views.py:290 +msgid "Edit smart link condition" +msgstr "" + +#: views.py:297 views.py:331 +msgid "condition" +msgstr "" + +#: views.py:317 +#, python-format +msgid "Smart link condition: \"%s\" deleted successfully." +msgstr "" + +#: views.py:319 +#, python-format +msgid "" +"Error deleting smart link condition: %(smart_link_condition)s; %(error)s." +msgstr "" + +#: views.py:333 +#, python-format +msgid "Are you sure you wish to delete smart link condition: \"%s\"?" +msgstr "" + +#: conf/settings.py:11 +msgid "Show smart link that don't return any documents." +msgstr "" + +#: templates/smart_links_help.html:3 +msgid "What are smart links?" +msgstr "" + +#: templates/smart_links_help.html:4 +msgid "" +"Smart links are a set of conditional statements that are used to query the " +"database using the current document the user is accessing as the data " +"source, the results of these queries are a list of documents that relate in " +"some manner to the document being displayed and allow users the ability to " +"jump to and from linked documents very easily." +msgstr "" diff --git a/apps/linking/locale/pt/LC_MESSAGES/django.mo b/apps/linking/locale/pt/LC_MESSAGES/django.mo index b9625e5478..6f66ae9735 100644 Binary files a/apps/linking/locale/pt/LC_MESSAGES/django.mo and b/apps/linking/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/linking/locale/pt/LC_MESSAGES/django.po b/apps/linking/locale/pt/LC_MESSAGES/django.po index 120688a322..69519ca3b9 100644 --- a/apps/linking/locale/pt/LC_MESSAGES/django.po +++ b/apps/linking/locale/pt/LC_MESSAGES/django.po @@ -4,13 +4,14 @@ # # Translators: # , 2011. +# Roberto Rosario , 2012. msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" -"PO-Revision-Date: 2011-11-02 05:05+0000\n" -"Last-Translator: emersonsoares \n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-02 18:19+0000\n" +"Last-Translator: Roberto Rosario \n" "Language-Team: Portuguese (http://www.transifex.net/projects/p/mayan-edms/" "team/pt/)\n" "Language: pt\n" @@ -19,67 +20,51 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -#: __init__.py:11 -msgid "View existing smart links" -msgstr "" +#: __init__.py:17 +msgid "smart links actions" +msgstr "ações do ligações inteligentes" -#: __init__.py:12 -msgid "Create new smart links" -msgstr "" +#: __init__.py:18 __init__.py:20 models.py:23 views.py:132 +msgid "smart links" +msgstr "ligações inteligentes" -#: __init__.py:13 -msgid "Delete smart links" -msgstr "" - -#: __init__.py:14 -msgid "Edit smart links" -msgstr "" - -#: __init__.py:16 -msgid "Smart links" -msgstr "" +#: __init__.py:21 +msgid "smart links list" +msgstr "lista de ligações inteligentes" #: __init__.py:22 -msgid "smart links actions" -msgstr "" +msgid "create new smart link" +msgstr "criar um novo ligação inteligente" -#: __init__.py:23 __init__.py:25 models.py:21 views.py:105 -msgid "smart links" -msgstr "" +#: __init__.py:23 __init__.py:28 +msgid "edit" +msgstr "editar" + +#: __init__.py:24 __init__.py:29 +msgid "delete" +msgstr "excluir" #: __init__.py:26 -msgid "smart links list" -msgstr "" +msgid "conditions" +msgstr "condições" #: __init__.py:27 -msgid "create new smart link" -msgstr "" - -#: __init__.py:28 __init__.py:33 -msgid "edit" -msgstr "" - -#: __init__.py:29 __init__.py:34 -msgid "delete" -msgstr "" +msgid "create condition" +msgstr "criar condições" #: __init__.py:31 -msgid "conditions" +msgid "ACLs" msgstr "" -#: __init__.py:32 -msgid "create condition" -msgstr "" - -#: forms.py:48 +#: forms.py:50 msgid "Pages" msgstr "Páginas" -#: forms.py:54 +#: forms.py:56 msgid "Select" msgstr "Selecionar" -#: forms.py:61 +#: forms.py:63 msgid "Click on the image for full size view of the first page." msgstr "Clique na imagem para ver em tamanho grande a primeira página." @@ -151,15 +136,15 @@ msgstr "está em expressão regular" msgid "is in regular expression (case insensitive)" msgstr "está em expressão regular (case insensitive)" -#: models.py:10 +#: models.py:12 msgid "title" msgstr "título" -#: models.py:11 views.py:108 +#: models.py:13 views.py:135 msgid "dynamic title" msgstr "título dinâmico" -#: models.py:11 models.py:29 +#: models.py:13 models.py:31 msgid "" "This expression will be evaluated against the current selected document. " "The document metadata is available as variables `metadata` and document " @@ -169,23 +154,23 @@ msgstr "" "metadados do documento estão disponíveis como variáveis \"​metadados \" e " "propriedades do documento em variáveis `documento`." -#: models.py:12 models.py:31 views.py:109 views.py:196 +#: models.py:14 models.py:33 views.py:136 views.py:232 msgid "enabled" msgstr "habilitado" -#: models.py:20 models.py:25 views.py:256 views.py:287 +#: models.py:22 models.py:27 views.py:296 views.py:330 msgid "smart link" -msgstr "" +msgstr "ligação inteligente" -#: models.py:26 +#: models.py:28 msgid "The inclusion is ignored for the first item." msgstr "A inclusão é ignorada para o primeiro item." -#: models.py:27 +#: models.py:29 msgid "foreign document data" msgstr "dados de documentos estrangeiros" -#: models.py:27 +#: models.py:29 msgid "" "This represents the metadata of all other documents. Available objects: " "`document.` and `metadata.`." @@ -193,128 +178,147 @@ msgstr "" "Isto representa os metadados de todos os outros documentos. Objetos " "disponíveis: `document. ` e ` metadata. `." -#: models.py:29 +#: models.py:31 msgid "expression" msgstr "expressão" -#: models.py:30 +#: models.py:32 msgid "negated" msgstr "negada" -#: models.py:30 +#: models.py:32 msgid "Inverts the logic of the operator." msgstr "Inverte a lógica do operador." -#: models.py:34 +#: models.py:36 msgid "not" msgstr "não" -#: models.py:37 +#: models.py:39 msgid "link condition" -msgstr "" +msgstr "condição de ligação" -#: models.py:38 +#: models.py:40 msgid "link conditions" -msgstr "" +msgstr "condições de ligação" -#: views.py:32 +#: permissions.py:7 +msgid "Smart links" +msgstr "Ligações inteligentes" + +#: permissions.py:9 +msgid "View existing smart links" +msgstr "Ver os ligações inteligentes" + +#: permissions.py:10 +msgid "Create new smart links" +msgstr "Criar novas ligações inteligentes" + +#: permissions.py:11 +msgid "Delete smart links" +msgstr "Excluir ligações inteligentes" + +#: permissions.py:12 +msgid "Edit smart links" +msgstr "Editar ligações inteligentes" + +#: views.py:40 msgid "No action selected." msgstr "Nenhuma ação selecionada." -#: views.py:47 -#, fuzzy, python-format +#: views.py:59 +#, python-format msgid "documents in smart link: %(group)s" -msgstr "documentos no grupo: %(group)s " +msgstr "" -#: views.py:65 -#, fuzzy, python-format +#: views.py:75 +#, python-format msgid "Smart link query error: %s" -msgstr "Erro na consulta de grupo do documento: %s" +msgstr "" -#: views.py:76 +#: views.py:97 #, python-format msgid "smart links (%s)" +msgstr "ligações inteligente (%s)" + +#: views.py:111 +msgid "There no defined smart links for the current document." msgstr "" -#: views.py:90 -#, fuzzy -msgid "There no defined smart links for the current document." -msgstr "Não há grupos definidos para o documento atual." - -#: views.py:124 +#: views.py:152 #, python-format msgid "Smart link: %s created successfully." -msgstr "" +msgstr "Ligação inteligente: %s criado com sucesso." -#: views.py:131 +#: views.py:159 msgid "Create new smart link" msgstr "" -#: views.py:144 +#: views.py:175 #, python-format msgid "Smart link: %s edited successfully." msgstr "" -#: views.py:153 +#: views.py:184 #, python-format msgid "Edit smart link: %s" -msgstr "" +msgstr "Editar Ligação inteligente: %s" -#: views.py:168 +#: views.py:202 #, python-format msgid "Smart link: %s deleted successfully." msgstr "" -#: views.py:170 +#: views.py:204 #, python-format msgid "Error deleting smart link: %(smart_link)s; %(error)s." msgstr "" -#: views.py:180 +#: views.py:213 #, python-format msgid "Are you sure you wish to delete smart link: %s?" msgstr "" -#: views.py:193 +#: views.py:229 #, python-format msgid "conditions for smart link: %s" msgstr "" -#: views.py:216 +#: views.py:254 #, python-format msgid "Smart link condition: \"%s\" created successfully." msgstr "" -#: views.py:223 +#: views.py:261 #, python-format msgid "Add new conditions to smart link: \"%s\"" msgstr "" -#: views.py:243 +#: views.py:283 #, python-format msgid "Smart link condition: \"%s\" edited successfully." msgstr "" -#: views.py:250 +#: views.py:290 msgid "Edit smart link condition" msgstr "" -#: views.py:257 views.py:288 +#: views.py:297 views.py:331 msgid "condition" -msgstr "" +msgstr "condição" -#: views.py:274 +#: views.py:317 #, python-format msgid "Smart link condition: \"%s\" deleted successfully." msgstr "" -#: views.py:276 +#: views.py:319 #, python-format msgid "" "Error deleting smart link condition: %(smart_link_condition)s; %(error)s." msgstr "" -#: views.py:290 +#: views.py:333 #, python-format msgid "Are you sure you wish to delete smart link condition: \"%s\"?" msgstr "" @@ -335,27 +339,3 @@ msgid "" "some manner to the document being displayed and allow users the ability to " "jump to and from linked documents very easily." msgstr "" - -#~ msgid "group actions" -#~ msgstr "ações do grupo" - -#~ msgid "groups" -#~ msgstr "grupos" - -#~ msgid "group document" -#~ msgstr "agrupar documento" - -#~ msgid "document group" -#~ msgstr "grupo de documentos" - -#~ msgid "document groups" -#~ msgstr "grupos de documentos" - -#~ msgid "group item" -#~ msgstr "item do grupo" - -#~ msgid "group items" -#~ msgstr "itens do grupo" - -#~ msgid "document groups (%s)" -#~ msgstr "grupos do documento (%s)" diff --git a/apps/linking/locale/ru/LC_MESSAGES/django.mo b/apps/linking/locale/ru/LC_MESSAGES/django.mo index 982203f193..f19463bcf2 100644 Binary files a/apps/linking/locale/ru/LC_MESSAGES/django.mo and b/apps/linking/locale/ru/LC_MESSAGES/django.mo differ diff --git a/apps/linking/locale/ru/LC_MESSAGES/django.po b/apps/linking/locale/ru/LC_MESSAGES/django.po index 5a02a2120d..859baea2aa 100644 --- a/apps/linking/locale/ru/LC_MESSAGES/django.po +++ b/apps/linking/locale/ru/LC_MESSAGES/django.po @@ -1,84 +1,70 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: # Sergey Glita , 2011. msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" -"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" -"PO-Revision-Date: 2011-11-22 18:47+0000\n" -"Last-Translator: gsv70 \n" -"Language-Team: Russian (http://www.transifex.net/projects/p/mayan-edms/team/ru/)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-02 18:19+0000\n" +"Last-Translator: Roberto Rosario \n" +"Language-Team: Russian (http://www.transifex.net/projects/p/mayan-edms/team/" +"ru/)\n" +"Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: ru\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" +"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:11 -msgid "View existing smart links" -msgstr "Просмотр отношений" - -#: __init__.py:12 -msgid "Create new smart links" -msgstr "Создать отношение" - -#: __init__.py:13 -msgid "Delete smart links" -msgstr "Удалить отношения" - -#: __init__.py:14 -msgid "Edit smart links" -msgstr "Редактировать отношения" - -#: __init__.py:16 -msgid "Smart links" -msgstr "Отношения" - -#: __init__.py:22 +#: __init__.py:17 msgid "smart links actions" msgstr "действия отношений" -#: __init__.py:23 __init__.py:25 models.py:21 views.py:105 +#: __init__.py:18 __init__.py:20 models.py:23 views.py:132 msgid "smart links" msgstr "отношения" -#: __init__.py:26 +#: __init__.py:21 msgid "smart links list" msgstr "список отношений" -#: __init__.py:27 +#: __init__.py:22 msgid "create new smart link" msgstr "создать отношение" -#: __init__.py:28 __init__.py:33 +#: __init__.py:23 __init__.py:28 msgid "edit" msgstr "редактировать" -#: __init__.py:29 __init__.py:34 +#: __init__.py:24 __init__.py:29 msgid "delete" msgstr "удалить" -#: __init__.py:31 +#: __init__.py:26 msgid "conditions" msgstr "условия" -#: __init__.py:32 +#: __init__.py:27 msgid "create condition" msgstr "создать условие" -#: forms.py:48 +#: __init__.py:31 +msgid "ACLs" +msgstr "" + +#: forms.py:50 msgid "Pages" msgstr "Страницы" -#: forms.py:54 +#: forms.py:56 msgid "Select" msgstr "Выбрать" -#: forms.py:61 +#: forms.py:63 msgid "Click on the image for full size view of the first page." msgstr "" "Нажмите на изображение для просмотра первой страницы в полном размере." @@ -151,15 +137,15 @@ msgstr "В регулярном выражении" msgid "is in regular expression (case insensitive)" msgstr "В регулярном выражении (без учета регистра)" -#: models.py:10 +#: models.py:12 msgid "title" msgstr "название" -#: models.py:11 views.py:108 +#: models.py:13 views.py:135 msgid "dynamic title" msgstr "динамический заголовок" -#: models.py:11 models.py:29 +#: models.py:13 models.py:31 msgid "" "This expression will be evaluated against the current selected document. " "The document metadata is available as variables `metadata` and document " @@ -169,23 +155,23 @@ msgstr "" "документа доступны как переменные `metadata` и свойства документа в " "переменной `document`." -#: models.py:12 models.py:31 views.py:109 views.py:196 +#: models.py:14 models.py:33 views.py:136 views.py:232 msgid "enabled" msgstr "разрешено" -#: models.py:20 models.py:25 views.py:256 views.py:287 +#: models.py:22 models.py:27 views.py:296 views.py:330 msgid "smart link" msgstr "отношение" -#: models.py:26 +#: models.py:28 msgid "The inclusion is ignored for the first item." msgstr "Включение игнорируется для первого элемента." -#: models.py:27 +#: models.py:29 msgid "foreign document data" msgstr "foreign document data" -#: models.py:27 +#: models.py:29 msgid "" "This represents the metadata of all other documents. Available objects: " "`document.` and `metadata.`." @@ -193,127 +179,147 @@ msgstr "" "Это представляет метаданные всех других документов. Доступные объекты: " "`document. ` и `metadata. `." -#: models.py:29 +#: models.py:31 msgid "expression" msgstr "выражение" -#: models.py:30 +#: models.py:32 msgid "negated" msgstr "отрицание" -#: models.py:30 +#: models.py:32 msgid "Inverts the logic of the operator." msgstr "Инвертирует логику оператора." -#: models.py:34 +#: models.py:36 msgid "not" msgstr "не" -#: models.py:37 +#: models.py:39 msgid "link condition" msgstr "условие ссылки" -#: models.py:38 +#: models.py:40 msgid "link conditions" msgstr "условия ссылки" -#: views.py:32 +#: permissions.py:7 +msgid "Smart links" +msgstr "Отношения" + +#: permissions.py:9 +msgid "View existing smart links" +msgstr "Просмотр отношений" + +#: permissions.py:10 +msgid "Create new smart links" +msgstr "Создать отношение" + +#: permissions.py:11 +msgid "Delete smart links" +msgstr "Удалить отношения" + +#: permissions.py:12 +msgid "Edit smart links" +msgstr "Редактировать отношения" + +#: views.py:40 msgid "No action selected." msgstr "Никаких действий не выбрано." -#: views.py:47 +#: views.py:59 #, python-format msgid "documents in smart link: %(group)s" msgstr "документы в отношении %(group)s" -#: views.py:65 +#: views.py:75 #, python-format msgid "Smart link query error: %s" msgstr "Ошибка запроса в отношении %s" -#: views.py:76 +#: views.py:97 #, python-format msgid "smart links (%s)" msgstr "отношения (%s)" -#: views.py:90 +#: views.py:111 msgid "There no defined smart links for the current document." msgstr "Для этого документа отношения не определены." -#: views.py:124 +#: views.py:152 #, python-format msgid "Smart link: %s created successfully." msgstr "Отношение %s создано." -#: views.py:131 +#: views.py:159 msgid "Create new smart link" msgstr "Создать новое отношение" -#: views.py:144 +#: views.py:175 #, python-format msgid "Smart link: %s edited successfully." msgstr "Отношение %s изменено." -#: views.py:153 +#: views.py:184 #, python-format msgid "Edit smart link: %s" msgstr "Редактировать отношение %s" -#: views.py:168 +#: views.py:202 #, python-format msgid "Smart link: %s deleted successfully." msgstr "Отношение %s удалено." -#: views.py:170 +#: views.py:204 #, python-format msgid "Error deleting smart link: %(smart_link)s; %(error)s." msgstr "Ошибка при удалении отношения %(smart_link)s; %(error)s." -#: views.py:180 +#: views.py:213 #, python-format msgid "Are you sure you wish to delete smart link: %s?" msgstr "Вы действительно хотите удалить отношение %s?" -#: views.py:193 +#: views.py:229 #, python-format msgid "conditions for smart link: %s" msgstr "условия для отношения %s" -#: views.py:216 +#: views.py:254 #, python-format msgid "Smart link condition: \"%s\" created successfully." msgstr "Условие для отношения \"%s\" успешно создано." -#: views.py:223 +#: views.py:261 #, python-format msgid "Add new conditions to smart link: \"%s\"" msgstr "Добавить новые условия отношения \"%s\"" -#: views.py:243 +#: views.py:283 #, python-format msgid "Smart link condition: \"%s\" edited successfully." msgstr "Условие отношения \"%s\" изменено." -#: views.py:250 +#: views.py:290 msgid "Edit smart link condition" msgstr "Изменить условие отношения" -#: views.py:257 views.py:288 +#: views.py:297 views.py:331 msgid "condition" msgstr "состояние" -#: views.py:274 +#: views.py:317 #, python-format msgid "Smart link condition: \"%s\" deleted successfully." msgstr "Условие отношения \"%s\" удалено." -#: views.py:276 +#: views.py:319 #, python-format msgid "" "Error deleting smart link condition: %(smart_link_condition)s; %(error)s." msgstr "Ошибка при удалении условия %(smart_link_condition)s; %(error)s." -#: views.py:290 +#: views.py:333 #, python-format msgid "Are you sure you wish to delete smart link condition: \"%s\"?" msgstr "Вы действительно хотите удалить условие отношения \"%s\"?" @@ -334,10 +340,8 @@ msgid "" "some manner to the document being displayed and allow users the ability to " "jump to and from linked documents very easily." msgstr "" -"Отношение, в оригинале smart-link, представляет собой набор условий, которые" -" используются для поиска документов отвечающих им . Результатом такого " +"Отношение, в оригинале smart-link, представляет собой набор условий, которые " +"используются для поиска документов отвечающих им . Результатом такого " "поискового запроса является список документов, относящихся к текущему, и " "позволяющих быстро переходить от одного к другому. Условия строятся исходя " "из содержимого документа и метаданных." - - diff --git a/apps/linking/managers.py b/apps/linking/managers.py index 050ddffea2..41d4275e15 100644 --- a/apps/linking/managers.py +++ b/apps/linking/managers.py @@ -1,10 +1,12 @@ +from __future__ import absolute_import + from django.db import models from django.db.models import Q -from metadata.classes import MetadataObject +from metadata.classes import MetadataClass from documents.models import Document -from linking.literals import INCLUSION_AND, INCLUSION_OR +from .literals import INCLUSION_AND, INCLUSION_OR class SmartLinkManager(models.Manager): @@ -16,7 +18,7 @@ class SmartLinkManager(models.Manager): metadata_dict[document_metadata.metadata_type.name] = document_metadata.value eval_dict = {} eval_dict['document'] = document - eval_dict['metadata'] = MetadataObject(metadata_dict) + eval_dict['metadata'] = MetadataClass(metadata_dict) if smart_link_obj: smart_link_qs = self.model.objects.filter(Q(enabled=True) & Q(pk=smart_link_obj.pk)) diff --git a/apps/linking/models.py b/apps/linking/models.py index ccc04f5537..0af9522e77 100644 --- a/apps/linking/models.py +++ b/apps/linking/models.py @@ -1,9 +1,11 @@ +from __future__ import absolute_import + from django.db import models from django.utils.translation import ugettext_lazy as _ -from linking.managers import SmartLinkManager -from linking.literals import OPERATOR_CHOICES, INCLUSION_AND, \ - INCLUSION_CHOICES +from .managers import SmartLinkManager +from .literals import (OPERATOR_CHOICES, INCLUSION_AND, + INCLUSION_CHOICES) class SmartLink(models.Model): diff --git a/apps/linking/permissions.py b/apps/linking/permissions.py new file mode 100644 index 0000000000..f5e7cc30b7 --- /dev/null +++ b/apps/linking/permissions.py @@ -0,0 +1,12 @@ +from __future__ import absolute_import + +from django.utils.translation import ugettext_lazy as _ + +from permissions.models import PermissionNamespace, Permission + +linking_namespace = PermissionNamespace('linking', _(u'Smart links')) + +PERMISSION_SMART_LINK_VIEW = Permission.objects.register(linking_namespace, 'smart_link_view', _(u'View existing smart links')) +PERMISSION_SMART_LINK_CREATE = Permission.objects.register(linking_namespace, 'smart_link_create', _(u'Create new smart links')) +PERMISSION_SMART_LINK_DELETE = Permission.objects.register(linking_namespace, 'smart_link_delete', _(u'Delete smart links')) +PERMISSION_SMART_LINK_EDIT = Permission.objects.register(linking_namespace, 'smart_link_edit', _(u'Edit smart links')) diff --git a/apps/linking/tests.py b/apps/linking/tests.py deleted file mode 100644 index 2247054b35..0000000000 --- a/apps/linking/tests.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -This file demonstrates two different styles of tests (one doctest and one -unittest). These will both pass when you run "manage.py test". - -Replace these 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.failUnlessEqual(1 + 1, 2) - -__test__ = {"doctest": """ -Another way to test that 1 + 1 is equal to 2. - ->>> 1 + 1 == 2 -True -"""} - diff --git a/apps/linking/urls.py b/apps/linking/urls.py index 080a180b2b..7cb2749d6e 100644 --- a/apps/linking/urls.py +++ b/apps/linking/urls.py @@ -4,14 +4,16 @@ urlpatterns = patterns('linking.views', url(r'^action/$', 'smart_link_action', (), 'smart_link_action'), url(r'^document/(?P\d+)/smart_link/(?P\d+)/$', 'smart_link_instance_view', (), 'smart_link_instance_view'), url(r'^smart/for_document/(?P\d+)/$', 'smart_link_instances_for_document', (), 'smart_link_instances_for_document'), - + url(r'^setup/list/$', 'smart_link_list', (), 'smart_link_list'), url(r'^setup/create/$', 'smart_link_create', (), 'smart_link_create'), url(r'^setup/(?P\d+)/delete/$', 'smart_link_delete', (), 'smart_link_delete'), url(r'^setup/(?P\d+)/edit/$', 'smart_link_edit', (), 'smart_link_edit'), - + url(r'^setup/(?P\d+)/condition/list/$', 'smart_link_condition_list', (), 'smart_link_condition_list'), url(r'^setup/(?P\d+)/condition/create/$', 'smart_link_condition_create', (), 'smart_link_condition_create'), url(r'^setup/smart_link/condition/(?P\d+)/edit/$', 'smart_link_condition_edit', (), 'smart_link_condition_edit'), url(r'^setup/smart_link/condition/(?P\d+)/delete/$', 'smart_link_condition_delete', (), 'smart_link_condition_delete'), + + url(r'^(?P\d+)/acl/list/$', 'smart_link_acl_list', (), 'smart_link_acl_list'), ) diff --git a/apps/linking/views.py b/apps/linking/views.py index bcc92fcfe6..ab85947403 100644 --- a/apps/linking/views.py +++ b/apps/linking/views.py @@ -1,31 +1,40 @@ +from __future__ import absolute_import + +import logging + from django.utils.translation import ugettext_lazy as _ from django.contrib import messages from django.http import HttpResponseRedirect from django.shortcuts import get_object_or_404, render_to_response from django.core.urlresolvers import reverse from django.template import RequestContext +from django.core.exceptions import PermissionDenied -from common.utils import generate_choices_w_labels, encapsulate +from common.utils import encapsulate from common.widgets import two_state_template - from documents.models import Document from documents.views import document_list +from documents.permissions import PERMISSION_DOCUMENT_VIEW +from permissions.models import Permission +from acls.views import acl_list_for +from acls.models import AccessEntry +from acls.utils import apply_default_acls -from permissions.api import check_permissions - -from linking.models import SmartLink, SmartLinkCondition -from linking.conf.settings import SHOW_EMPTY_SMART_LINKS -from linking.forms import (SmartLinkInstanceForm, SmartLinkForm, +from .models import SmartLink, SmartLinkCondition +from .conf.settings import SHOW_EMPTY_SMART_LINKS +from .forms import (SmartLinkInstanceForm, SmartLinkForm, SmartLinkConditionForm) -from linking import smart_link_instance_view_link -from linking import (PERMISSION_SMART_LINK_VIEW, +from . import smart_link_instance_view_link +from .permissions import (PERMISSION_SMART_LINK_VIEW, PERMISSION_SMART_LINK_CREATE, PERMISSION_SMART_LINK_DELETE, PERMISSION_SMART_LINK_EDIT) +logger = logging.getLogger(__name__) + def smart_link_action(request): - check_permissions(request.user, [PERMISSION_SMART_LINK_VIEW]) - + #Permission.objects.check_permissions(request.user, [PERMISSION_SMART_LINK_VIEW]) + action = request.GET.get('action', None) if not action: @@ -36,10 +45,14 @@ def smart_link_action(request): def smart_link_instance_view(request, document_id, smart_link_pk): - check_permissions(request.user, [PERMISSION_SMART_LINK_VIEW]) - document = get_object_or_404(Document, pk=document_id) smart_link = get_object_or_404(SmartLink, pk=smart_link_pk) + + try: + Permission.objects.check_permissions(request.user, [PERMISSION_SMART_LINK_VIEW]) + except PermissionDenied: + AccessEntry.objects.check_access(PERMISSION_SMART_LINK_VIEW, request.user, smart_link) + object_list, errors = SmartLink.objects.get_smart_link_instances_for(document, smart_link) return document_list( @@ -55,8 +68,6 @@ def smart_link_instance_view(request, document_id, smart_link_pk): def smart_link_instances_for_document(request, document_id): - check_permissions(request.user, [PERMISSION_SMART_LINK_VIEW]) - subtemplates_list = [] document = get_object_or_404(Document, pk=document_id) smart_link_instances, errors = SmartLink.objects.get_smart_link_instances_for(document) @@ -69,6 +80,17 @@ def smart_link_instances_for_document(request, document_id): #dictionary smart_link_instances = dict([(group, data) for group, data in smart_link_instances.items() if data['documents']]) + try: + Permission.objects.check_permissions(request.user, [PERMISSION_SMART_LINK_VIEW]) + except PermissionDenied: + smart_link_instances_keys_filtered = AccessEntry.objects.filter_objects_by_access(PERMISSION_SMART_LINK_VIEW, request.user, smart_link_instances.keys()) + # Remove smart link instances not found in the new filtered key list + for key, value in smart_link_instances.items(): + if key not in smart_link_instances_keys_filtered: + smart_link_instances.pop(key) + + value['documents'] = AccessEntry.objects.filter_objects_by_access(PERMISSION_DOCUMENT_VIEW, request.user, value['documents']) + if smart_link_instances: subtemplates_list = [{ 'name': 'generic_form_subtemplate.html', @@ -96,31 +118,37 @@ def smart_link_instances_for_document(request, document_id): 'document': document, 'subtemplates_list': subtemplates_list, }, context_instance=RequestContext(request)) - - + + def smart_link_list(request): - check_permissions(request.user, [PERMISSION_SMART_LINK_CREATE]) - + qs = SmartLink.objects.all() + + try: + Permission.objects.check_permissions(request.user, [PERMISSION_SMART_LINK_VIEW]) + except PermissionDenied: + qs = AccessEntry.objects.filter_objects_by_access(PERMISSION_SMART_LINK_VIEW, request.user, qs) + return render_to_response('generic_list.html', { 'title': _(u'smart links'), - 'object_list': SmartLink.objects.all(), + 'object_list': qs, 'extra_columns': [ {'name': _(u'dynamic title'), 'attribute': 'dynamic_title'}, {'name': _(u'enabled'), 'attribute': encapsulate(lambda x: two_state_template(x.enabled))}, - ], + ], 'hide_link': True, 'list_object_variable_name': 'smart_link', }, context_instance=RequestContext(request)) - - + + def smart_link_create(request): - check_permissions(request.user, [PERMISSION_SMART_LINK_CREATE]) + Permission.objects.check_permissions(request.user, [PERMISSION_SMART_LINK_CREATE]) if request.method == 'POST': form = SmartLinkForm(request.POST) if form.is_valid(): document_group = form.save() + apply_default_acls(document_group, request.user) messages.success(request, _(u'Smart link: %s created successfully.') % document_group) return HttpResponseRedirect(reverse('smart_link_list')) else: @@ -129,14 +157,17 @@ def smart_link_create(request): return render_to_response('generic_form.html', { 'form': form, 'title': _(u'Create new smart link') - }, context_instance=RequestContext(request)) - - + }, context_instance=RequestContext(request)) + + def smart_link_edit(request, smart_link_pk): - check_permissions(request.user, [PERMISSION_SMART_LINK_EDIT]) - smart_link = get_object_or_404(SmartLink, pk=smart_link_pk) + try: + Permission.objects.check_permissions(request.user, [PERMISSION_SMART_LINK_EDIT]) + except PermissionDenied: + AccessEntry.objects.check_access(PERMISSION_SMART_LINK_EDIT, request.user, smart_link) + if request.method == 'POST': form = SmartLinkForm(request.POST, instance=smart_link) if form.is_valid(): @@ -147,18 +178,21 @@ def smart_link_edit(request, smart_link_pk): form = SmartLinkForm(instance=smart_link) return render_to_response('generic_form.html', { - 'navigation_object_name': 'smart_link', - 'smart_link': smart_link, + #'navigation_object_name': 'smart_link', + 'object': smart_link, 'form': form, 'title': _(u'Edit smart link: %s') % smart_link - }, context_instance=RequestContext(request)) - - + }, context_instance=RequestContext(request)) + + def smart_link_delete(request, smart_link_pk): - check_permissions(request.user, [PERMISSION_SMART_LINK_DELETE]) - smart_link = get_object_or_404(SmartLink, pk=smart_link_pk) + try: + Permission.objects.check_permissions(request.user, [PERMISSION_SMART_LINK_DELETE]) + except PermissionDenied: + AccessEntry.objects.check_access(PERMISSION_SMART_LINK_DELETE, request.user, smart_link) + next = request.POST.get('next', request.GET.get('next', request.META.get('HTTP_REFERER', '/'))) previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', '/'))) @@ -175,44 +209,48 @@ def smart_link_delete(request, smart_link_pk): return render_to_response('generic_confirm.html', { 'delete_view': True, - 'navigation_object_name': 'smart_link', - 'smart_link': smart_link, + 'object': smart_link, 'title': _(u'Are you sure you wish to delete smart link: %s?') % smart_link, 'next': next, 'previous': previous, 'form_icon': u'link_delete.png', - }, context_instance=RequestContext(request)) - + }, context_instance=RequestContext(request)) + def smart_link_condition_list(request, smart_link_pk): - check_permissions(request.user, [PERMISSION_SMART_LINK_CREATE, PERMISSION_SMART_LINK_EDIT]) - smart_link = get_object_or_404(SmartLink, pk=smart_link_pk) - + + try: + Permission.objects.check_permissions(request.user, [PERMISSION_SMART_LINK_CREATE, PERMISSION_SMART_LINK_EDIT]) + except PermissionDenied: + AccessEntry.objects.check_accesses([PERMISSION_SMART_LINK_CREATE, PERMISSION_SMART_LINK_EDIT], request.user, smart_link) + return render_to_response('generic_list.html', { 'title': _(u'conditions for smart link: %s') % smart_link, 'object_list': smart_link.smartlinkcondition_set.all(), 'extra_columns': [ {'name': _(u'enabled'), 'attribute': encapsulate(lambda x: two_state_template(x.enabled))}, - ], + ], 'hide_link': True, - 'smart_link': smart_link, - 'navigation_object_name': 'smart_link', - 'list_object_variable_name': 'condition', + 'object': smart_link, + 'list_object_variable_name': 'condition', }, context_instance=RequestContext(request)) def smart_link_condition_create(request, smart_link_pk): - check_permissions(request.user, [PERMISSION_SMART_LINK_CREATE, PERMISSION_SMART_LINK_EDIT]) - smart_link = get_object_or_404(SmartLink, pk=smart_link_pk) + try: + Permission.objects.check_permissions(request.user, [PERMISSION_SMART_LINK_CREATE, PERMISSION_SMART_LINK_EDIT]) + except PermissionDenied: + AccessEntry.objects.check_accesses([PERMISSION_SMART_LINK_CREATE, PERMISSION_SMART_LINK_EDIT], request.user, smart_link) + if request.method == 'POST': form = SmartLinkConditionForm(request.POST) if form.is_valid(): new_smart_link_condition = form.save(commit=False) new_smart_link_condition.smart_link = smart_link - new_smart_link_condition.save() + new_smart_link_condition.save() messages.success(request, _(u'Smart link condition: "%s" created successfully.') % new_smart_link_condition) return HttpResponseRedirect(reverse('smart_link_condition_list', args=[smart_link.pk])) else: @@ -221,16 +259,18 @@ def smart_link_condition_create(request, smart_link_pk): return render_to_response('generic_form.html', { 'form': form, 'title': _(u'Add new conditions to smart link: "%s"') % smart_link, - 'navigation_object_name': 'smart_link', - 'smart_link': smart_link, - }, context_instance=RequestContext(request)) + 'object': smart_link, + }, context_instance=RequestContext(request)) def smart_link_condition_edit(request, smart_link_condition_pk): - check_permissions(request.user, [PERMISSION_SMART_LINK_CREATE, PERMISSION_SMART_LINK_EDIT]) - smart_link_condition = get_object_or_404(SmartLinkCondition, pk=smart_link_condition_pk) + try: + Permission.objects.check_permissions(request.user, [PERMISSION_SMART_LINK_CREATE, PERMISSION_SMART_LINK_EDIT]) + except PermissionDenied: + AccessEntry.objects.check_accesses([PERMISSION_SMART_LINK_CREATE, PERMISSION_SMART_LINK_EDIT], request.user, smart_link_condition.smart_link) + next = request.POST.get('next', request.GET.get('next', request.META.get('HTTP_REFERER', '/'))) previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', '/'))) @@ -251,20 +291,23 @@ def smart_link_condition_edit(request, smart_link_condition_pk): 'next': next, 'previous': previous, 'condition': smart_link_condition, - 'smart_link': smart_link_condition.smart_link, + 'object': smart_link_condition.smart_link, 'navigation_object_list': [ - {'object': 'smart_link', 'name': _(u'smart link')}, + {'object': 'object', 'name': _(u'smart link')}, {'object': 'condition', 'name': _(u'condition')} ], - - }, context_instance=RequestContext(request)) + + }, context_instance=RequestContext(request)) def smart_link_condition_delete(request, smart_link_condition_pk): - check_permissions(request.user, [PERMISSION_SMART_LINK_CREATE, PERMISSION_SMART_LINK_EDIT]) - smart_link_condition = get_object_or_404(SmartLinkCondition, pk=smart_link_condition_pk) + try: + Permission.objects.check_permissions(request.user, [PERMISSION_SMART_LINK_CREATE, PERMISSION_SMART_LINK_EDIT]) + except PermissionDenied: + AccessEntry.objects.check_accesses([PERMISSION_SMART_LINK_CREATE, PERMISSION_SMART_LINK_EDIT], request.user, smart_link_condition.smart_link) + next = request.POST.get('next', request.GET.get('next', request.META.get('HTTP_REFERER', '/'))) previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', '/'))) @@ -282,13 +325,27 @@ def smart_link_condition_delete(request, smart_link_condition_pk): return render_to_response('generic_confirm.html', { 'delete_view': True, 'condition': smart_link_condition, - 'smart_link': smart_link_condition.smart_link, + 'object': smart_link_condition.smart_link, 'navigation_object_list': [ - {'object': 'smart_link', 'name': _(u'smart link')}, + {'object': 'object', 'name': _(u'smart link')}, {'object': 'condition', 'name': _(u'condition')} ], 'title': _(u'Are you sure you wish to delete smart link condition: "%s"?') % smart_link_condition, 'next': next, 'previous': previous, 'form_icon': u'cog_delete.png', - }, context_instance=RequestContext(request)) + }, context_instance=RequestContext(request)) + + +def smart_link_acl_list(request, smart_link_pk): + smart_link = get_object_or_404(SmartLink, pk=smart_link_pk) + logger.debug('smart_link: %s' % smart_link) + + return acl_list_for( + request, + smart_link, + extra_context={ + 'object': smart_link, + 'smart_link': smart_link, + } + ) diff --git a/apps/lock_manager/__init__.py b/apps/lock_manager/__init__.py index 34913f856f..61fe7fbf78 100644 --- a/apps/lock_manager/__init__.py +++ b/apps/lock_manager/__init__.py @@ -1,4 +1,6 @@ -from lock_manager.exceptions import LockError -from lock_manager.models import Lock as LockModel +from __future__ import absolute_import + +from .exceptions import LockError +from .models import Lock as LockModel Lock = LockModel.objects diff --git a/apps/lock_manager/admin.py b/apps/lock_manager/admin.py index 12dbee8fe1..d8c353e7e3 100644 --- a/apps/lock_manager/admin.py +++ b/apps/lock_manager/admin.py @@ -1,6 +1,8 @@ +from __future__ import absolute_import + from django.contrib import admin -from lock_manager.models import Lock +from .models import Lock class LockAdmin(admin.ModelAdmin): diff --git a/apps/lock_manager/managers.py b/apps/lock_manager/managers.py index f356be42dd..99145e8c5a 100644 --- a/apps/lock_manager/managers.py +++ b/apps/lock_manager/managers.py @@ -1,12 +1,13 @@ +from __future__ import absolute_import + import logging import datetime -from django.db.utils import DatabaseError from django.db.utils import IntegrityError from django.db import transaction from django.db import models -from lock_manager.exceptions import LockError +from .exceptions import LockError logger = logging.getLogger(__name__) @@ -21,6 +22,7 @@ class LockManager(models.Manager): logger.debug('acquired lock: %s' % name) return lock except IntegrityError, msg: + transaction.rollback() logger.debug('IntegrityError: %s', msg) # There is already an existing lock # Check it's expiration date and if expired, reset it diff --git a/apps/lock_manager/models.py b/apps/lock_manager/models.py index 535d3366a6..a70af8230c 100644 --- a/apps/lock_manager/models.py +++ b/apps/lock_manager/models.py @@ -1,10 +1,12 @@ +from __future__ import absolute_import + import datetime from django.db import models from django.utils.translation import ugettext_lazy as _ -from lock_manager.managers import LockManager -from lock_manager.conf.settings import DEFAULT_LOCK_TIMEOUT +from .managers import LockManager +from .conf.settings import DEFAULT_LOCK_TIMEOUT class Lock(models.Model): @@ -19,7 +21,7 @@ class Lock(models.Model): def save(self, *args, **kwargs): self.creation_datetime = datetime.datetime.now() - if not self.timeout and not kwarget.get('timeout'): + if not self.timeout and not kwargs.get('timeout'): self.timeout = DEFAULT_LOCK_TIMEOUT super(Lock, self).save(*args, **kwargs) diff --git a/apps/main/__init__.py b/apps/main/__init__.py index 5a086612d9..97aa7458f4 100644 --- a/apps/main/__init__.py +++ b/apps/main/__init__.py @@ -1,16 +1,18 @@ +from __future__ import absolute_import + from django.utils.translation import ugettext_lazy as _ +from django.conf import settings from navigation.api import register_top_menu from navigation.api import register_links from project_setup.api import register_setup from project_tools.api import register_tool -from main.conf.settings import SIDE_BAR_SEARCH -from main.conf.settings import DISABLE_HOME_VIEW +from .conf.settings import SIDE_BAR_SEARCH, DISABLE_HOME_VIEW __author__ = 'Roberto Rosario' __copyright__ = 'Copyright 2011 Roberto Rosario' -__credits__ = ['Roberto Rosario',] +__credits__ = ['Roberto Rosario',] __license__ = 'GPL' __maintainer__ = 'Roberto Rosario' __email__ = 'roberto.rosario.gonzalez@gmail.com' @@ -18,18 +20,18 @@ __status__ = 'Production' __version_info__ = { 'major': 0, - 'minor': 11, - 'micro': 2, + 'minor': 12, + 'micro': 0, 'releaselevel': 'final', 'serial': 0 } + def is_superuser(context): return context['request'].user.is_staff or context['request'].user.is_superuser - maintenance_menu = {'text': _(u'maintenance'), 'view': 'maintenance_menu', 'famfam': 'wrench', 'icon': 'wrench.png'} -statistics = {'text': _(u'statistics'), 'view': 'statistics', 'famfam': 'table', 'icon': 'blackboard_sum.png'} +statistics = {'text': _(u'statistics'), 'view': 'statistics', 'famfam': 'table', 'icon': 'blackboard_sum.png', 'condition': is_superuser, 'children_view_regex': [r'statistics']} diagnostics = {'text': _(u'diagnostics'), 'view': 'diagnostics', 'famfam': 'pill', 'icon': 'pill.png'} sentry = {'text': _(u'sentry'), 'view': 'sentry', 'famfam': 'bug', 'icon': 'bug.png', 'condition': is_superuser} admin_site = {'text': _(u'admin site'), 'view': 'admin:index', 'famfam': 'keyboard', 'icon': 'keyboard.png', 'condition': is_superuser} @@ -41,9 +43,9 @@ if not SIDE_BAR_SEARCH: def get_version(): - """ + ''' Return the formatted version information - """ + ''' vers = ['%(major)i.%(minor)i' % __version_info__, ] if __version_info__['micro']: @@ -52,11 +54,14 @@ def get_version(): vers.append('%(releaselevel)s%(serial)i' % __version_info__) return ''.join(vers) - __version__ = get_version() -register_setup(admin_site) +if 'django.contrib.admin' in settings.INSTALLED_APPS: + register_setup(admin_site) + register_tool(maintenance_menu) register_tool(statistics) register_tool(diagnostics) -register_tool(sentry) + +if 'sentry' in settings.INSTALLED_APPS: + register_tool(sentry) diff --git a/apps/main/locale/en/LC_MESSAGES/django.po b/apps/main/locale/en/LC_MESSAGES/django.po index 3f41a881ab..35a5b1a1dc 100644 --- a/apps/main/locale/en/LC_MESSAGES/django.po +++ b/apps/main/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-12-06 03:26-0400\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,43 +17,43 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: __init__.py:31 +#: __init__.py:33 msgid "maintenance" msgstr "" -#: __init__.py:32 +#: __init__.py:34 msgid "statistics" msgstr "" -#: __init__.py:33 +#: __init__.py:35 msgid "diagnostics" msgstr "" -#: __init__.py:34 +#: __init__.py:36 msgid "sentry" msgstr "" -#: __init__.py:35 +#: __init__.py:37 msgid "admin site" msgstr "" -#: __init__.py:38 +#: __init__.py:40 msgid "home" msgstr "" -#: __init__.py:40 +#: __init__.py:42 msgid "search" msgstr "" -#: views.py:41 +#: views.py:43 msgid "maintenance menu" msgstr "" -#: views.py:53 +#: views.py:56 msgid "Statistics" msgstr "" -#: views.py:61 +#: views.py:66 msgid "Diagnostics" msgstr "" @@ -99,37 +99,37 @@ msgstr "" msgid "Secondary menu" msgstr "" -#: templates/base.html:302 +#: templates/base.html:301 #, python-format msgid "Actions for %(name)s: %(navigation_object)s" msgstr "" -#: templates/base.html:304 templates/base.html.py:336 +#: templates/base.html:303 templates/base.html.py:346 #, python-format msgid "Actions for: %(navigation_object)s" msgstr "" -#: templates/base.html:307 +#: templates/base.html:306 msgid "Available actions" msgstr "" -#: templates/base.html:319 templates/base.html.py:351 +#: templates/base.html:318 templates/base.html.py:361 msgid "Related actions" msgstr "" -#: templates/base.html:334 +#: templates/base.html:329 templates/base.html.py:374 +msgid "Other available actions" +msgstr "" + +#: templates/base.html:344 #, python-format msgid "Actions for %(object_name)s: %(navigation_object)s" msgstr "" -#: templates/base.html:339 +#: templates/base.html:349 msgid "Actions" msgstr "" -#: templates/base.html:364 -msgid "Other available actions" -msgstr "" - #: templates/home.html:8 msgid "Django based open source document management system" msgstr "" @@ -140,6 +140,6 @@ msgid "" "indexing, tagging, file serving integration and OCR capabilities" msgstr "" -#: templates/project_description.html:15 +#: templates/project_description.html:18 msgid "Released under the GPL V3 License" msgstr "" diff --git a/apps/main/locale/es/LC_MESSAGES/django.mo b/apps/main/locale/es/LC_MESSAGES/django.mo index 3c57a910a9..ea9b441199 100644 Binary files a/apps/main/locale/es/LC_MESSAGES/django.mo and b/apps/main/locale/es/LC_MESSAGES/django.mo differ diff --git a/apps/main/locale/es/LC_MESSAGES/django.po b/apps/main/locale/es/LC_MESSAGES/django.po index da34696a3f..1f1d1f34d9 100644 --- a/apps/main/locale/es/LC_MESSAGES/django.po +++ b/apps/main/locale/es/LC_MESSAGES/django.po @@ -1,71 +1,71 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: -# Roberto Rosario , 2011. +# Roberto Rosario , 2011, 2012. msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-12-06 03:26-0400\n" -"PO-Revision-Date: 2011-12-06 03:27\n" -"Last-Translator: Administrador \n" -"Language-Team: Spanish (Castilian) (http://www.transifex.net/projects/p/mayan-edms/team/es/)\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-01-02 05:42+0000\n" +"Last-Translator: Roberto Rosario \n" +"Language-Team: Spanish (Castilian) (http://www.transifex.net/projects/p/" +"mayan-edms/team/es/)\n" +"Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: es\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"X-Translated-Using: django-rosetta 0.6.2\n" -#: __init__.py:31 +#: __init__.py:33 msgid "maintenance" msgstr "mantenimiento" -#: __init__.py:32 +#: __init__.py:34 msgid "statistics" msgstr "estadísticas" -#: __init__.py:33 +#: __init__.py:35 msgid "diagnostics" msgstr "diagnósticos" -#: __init__.py:34 +#: __init__.py:36 msgid "sentry" msgstr "sentry" -#: __init__.py:35 +#: __init__.py:37 msgid "admin site" msgstr "sitio administrativo" -#: __init__.py:38 +#: __init__.py:40 msgid "home" msgstr "inicio" -#: __init__.py:40 +#: __init__.py:42 msgid "search" msgstr "búsqueda" -#: views.py:41 +#: views.py:43 msgid "maintenance menu" msgstr "menú de mantenimiento" -#: views.py:53 +#: views.py:56 msgid "Statistics" msgstr "Estadísticas" -#: views.py:61 +#: views.py:66 msgid "Diagnostics" msgstr "Diagnósticos" #: conf/settings.py:12 msgid "" -"Controls whether the search functionality is provided by a sidebar " -"widget or by a menu entry." +"Controls whether the search functionality is provided by a sidebar widget or " +"by a menu entry." msgstr "" -"Controla si la funcionalidad de búsqueda es proporcionada por una " -"barra lateral o por una entrada de menú." +"Controla si la funcionalidad de búsqueda es proporcionada por una barra " +"lateral o por una entrada de menú." #: templates/about.html:5 msgid "About this program" @@ -103,51 +103,50 @@ msgstr "Desconectarse" msgid "Secondary menu" msgstr "Menú secundario" -#: templates/base.html:302 +#: templates/base.html:301 #, python-format msgid "Actions for %(name)s: %(navigation_object)s" msgstr "Acciones para %(name)s: %(navigation_object)s " -#: templates/base.html:304 templates/base.html.py:336 +#: templates/base.html:303 templates/base.html.py:346 #, python-format msgid "Actions for: %(navigation_object)s" msgstr "Acciones para: %(navigation_object)s " -#: templates/base.html:307 +#: templates/base.html:306 msgid "Available actions" msgstr "Acciones disponibles" -#: templates/base.html:319 templates/base.html.py:351 +#: templates/base.html:318 templates/base.html.py:361 msgid "Related actions" msgstr "Acciones relacionadas" -#: templates/base.html:334 +#: templates/base.html:329 templates/base.html.py:374 +msgid "Other available actions" +msgstr "Otras acciones disponibles" + +#: templates/base.html:344 #, python-format msgid "Actions for %(object_name)s: %(navigation_object)s" msgstr "Acciones para %(object_name)s: %(navigation_object)s " -#: templates/base.html:339 +#: templates/base.html:349 msgid "Actions" msgstr "Acciones" -#: templates/base.html:364 -msgid "Other available actions" -msgstr "Otras acciones disponibles" - #: templates/home.html:8 msgid "Django based open source document management system" -msgstr "Gestor de documentos de código abierto, basado en Django." +msgstr "Sistema de manejo de documentos de código abierto basado en Django" #: templates/project_description.html:6 msgid "" -"Open source, Django based electronic document manager with custom " -"metadata, indexing, tagging, file serving integration and OCR " -"capabilities" +"Open source, Django based electronic document manager with custom metadata, " +"indexing, tagging, file serving integration and OCR capabilities" msgstr "" "Gestor documental de código abierto, basado en Django con metadatos " "personaliables, indexación, etiquedado de documentos, integración de " "servicio de archivos y capacidades de OCR" -#: templates/project_description.html:15 +#: templates/project_description.html:18 msgid "Released under the GPL V3 License" msgstr "Publicado bajo la licencia GPL v3" diff --git a/apps/main/locale/it/LC_MESSAGES/django.mo b/apps/main/locale/it/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..197d7863aa Binary files /dev/null and b/apps/main/locale/it/LC_MESSAGES/django.mo differ diff --git a/apps/main/locale/it/LC_MESSAGES/django.po b/apps/main/locale/it/LC_MESSAGES/django.po new file mode 100644 index 0000000000..19e9f80487 --- /dev/null +++ b/apps/main/locale/it/LC_MESSAGES/django.po @@ -0,0 +1,147 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# , 2011. +# Roberto Rosario , 2012. +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-21 16:42+0000\n" +"Last-Translator: Roberto Rosario \n" +"Language-Team: Italian (http://www.transifex.net/projects/p/mayan-edms/language/it/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: it\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" + +#: __init__.py:33 +msgid "maintenance" +msgstr "manutenzione" + +#: __init__.py:34 +msgid "statistics" +msgstr "statistiche" + +#: __init__.py:35 +msgid "diagnostics" +msgstr "disgnostica" + +#: __init__.py:36 +msgid "sentry" +msgstr "guardia" + +#: __init__.py:37 +msgid "admin site" +msgstr "sito d'amministrazione" + +#: __init__.py:40 +msgid "home" +msgstr "home" + +#: __init__.py:42 +msgid "search" +msgstr "cerca" + +#: views.py:43 +msgid "maintenance menu" +msgstr "menu manutenzione" + +#: views.py:56 +msgid "Statistics" +msgstr "Statistiche" + +#: views.py:66 +msgid "Diagnostics" +msgstr "Diagnostica" + +#: conf/settings.py:12 +msgid "" +"Controls whether the search functionality is provided by a sidebar widget or" +" by a menu entry." +msgstr "Controlla se la funzionalità di ricerca è fornita da un widget sidebar o da una voce di menu." + +#: templates/about.html:5 +msgid "About this program" +msgstr "Rigurdo a questo programma" + +#: templates/about.html:9 templates/verbose_login.html:4 +msgid "Version" +msgstr "Versione" + +#: templates/base.html:28 +msgid "(DEBUG)" +msgstr "(DEBUG)" + +#: templates/base.html:183 +msgid "User" +msgstr "Utente" + +#: templates/base.html:185 +msgid "Anonymous" +msgstr "Anonimo" + +#: templates/base.html:188 +msgid "User details" +msgstr "Dettagli utente" + +#: templates/base.html:205 +msgid "Login" +msgstr "Login" + +#: templates/base.html:205 +msgid "Logout" +msgstr "Logout" + +#: templates/base.html:285 +msgid "Secondary menu" +msgstr "Menu secondario" + +#: templates/base.html:301 +#, python-format +msgid "Actions for %(name)s: %(navigation_object)s" +msgstr "Azione per %(name)s: %(navigation_object)s" + +#: templates/base.html:303 templates/base.html.py:346 +#, python-format +msgid "Actions for: %(navigation_object)s" +msgstr "Azione per : %(navigation_object)s" + +#: templates/base.html:306 +msgid "Available actions" +msgstr "Azioni disponibili" + +#: templates/base.html:318 templates/base.html.py:361 +msgid "Related actions" +msgstr "Azioni relative" + +#: templates/base.html:329 templates/base.html.py:374 +msgid "Other available actions" +msgstr "Altre azioni disponibili" + +#: templates/base.html:344 +#, python-format +msgid "Actions for %(object_name)s: %(navigation_object)s" +msgstr "Azioni per %(object_name)s: %(navigation_object)s" + +#: templates/base.html:349 +msgid "Actions" +msgstr "Azioni" + +#: templates/home.html:8 +msgid "Django based open source document management system" +msgstr "Sistema open source di gestione dei documenti basato su Django" + +#: templates/project_description.html:6 +msgid "" +"Open source, Django based electronic document manager with custom metadata, " +"indexing, tagging, file serving integration and OCR capabilities" +msgstr "Open Source, Django programma per la gestione documentale con la possibilità di metadati personalizabili, tagging, indicizzazione, integrazione con file server e con possibilità di OCR" + +#: templates/project_description.html:18 +msgid "Released under the GPL V3 License" +msgstr "Rilasciato con licenza GPL V3" diff --git a/apps/main/locale/pl/LC_MESSAGES/django.mo b/apps/main/locale/pl/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..5e53a249bb Binary files /dev/null and b/apps/main/locale/pl/LC_MESSAGES/django.mo differ diff --git a/apps/main/locale/pl/LC_MESSAGES/django.po b/apps/main/locale/pl/LC_MESSAGES/django.po new file mode 100644 index 0000000000..64265937de --- /dev/null +++ b/apps/main/locale/pl/LC_MESSAGES/django.po @@ -0,0 +1,146 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# , 2012. +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-21 15:36+0000\n" +"Last-Translator: mic \n" +"Language-Team: Polish (http://www.transifex.net/projects/p/mayan-edms/language/pl/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pl\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" + +#: __init__.py:33 +msgid "maintenance" +msgstr "utrzymanie" + +#: __init__.py:34 +msgid "statistics" +msgstr "statystyka" + +#: __init__.py:35 +msgid "diagnostics" +msgstr "diagnostyka" + +#: __init__.py:36 +msgid "sentry" +msgstr "" + +#: __init__.py:37 +msgid "admin site" +msgstr "strona administracyjna" + +#: __init__.py:40 +msgid "home" +msgstr "home" + +#: __init__.py:42 +msgid "search" +msgstr "szukaj" + +#: views.py:43 +msgid "maintenance menu" +msgstr "" + +#: views.py:56 +msgid "Statistics" +msgstr "Statystyka" + +#: views.py:66 +msgid "Diagnostics" +msgstr "Diagnostyka" + +#: conf/settings.py:12 +msgid "" +"Controls whether the search functionality is provided by a sidebar widget or" +" by a menu entry." +msgstr "" + +#: templates/about.html:5 +msgid "About this program" +msgstr "O programie" + +#: templates/about.html:9 templates/verbose_login.html:4 +msgid "Version" +msgstr "Wersja" + +#: templates/base.html:28 +msgid "(DEBUG)" +msgstr "(DEBUG)" + +#: templates/base.html:183 +msgid "User" +msgstr "Użytkownik" + +#: templates/base.html:185 +msgid "Anonymous" +msgstr "Anonimowy" + +#: templates/base.html:188 +msgid "User details" +msgstr "Dane użytkownika" + +#: templates/base.html:205 +msgid "Login" +msgstr "Login" + +#: templates/base.html:205 +msgid "Logout" +msgstr "Wyloguj się" + +#: templates/base.html:285 +msgid "Secondary menu" +msgstr "" + +#: templates/base.html:301 +#, python-format +msgid "Actions for %(name)s: %(navigation_object)s" +msgstr "" + +#: templates/base.html:303 templates/base.html.py:346 +#, python-format +msgid "Actions for: %(navigation_object)s" +msgstr "" + +#: templates/base.html:306 +msgid "Available actions" +msgstr "" + +#: templates/base.html:318 templates/base.html.py:361 +msgid "Related actions" +msgstr "" + +#: templates/base.html:329 templates/base.html.py:374 +msgid "Other available actions" +msgstr "" + +#: templates/base.html:344 +#, python-format +msgid "Actions for %(object_name)s: %(navigation_object)s" +msgstr "" + +#: templates/base.html:349 +msgid "Actions" +msgstr "" + +#: templates/home.html:8 +msgid "Django based open source document management system" +msgstr "Django based open source document management system" + +#: templates/project_description.html:6 +msgid "" +"Open source, Django based electronic document manager with custom metadata, " +"indexing, tagging, file serving integration and OCR capabilities" +msgstr "" + +#: templates/project_description.html:18 +msgid "Released under the GPL V3 License" +msgstr "Released under the GPL V3 License" diff --git a/apps/main/locale/pt/LC_MESSAGES/django.mo b/apps/main/locale/pt/LC_MESSAGES/django.mo index f0e4a1a726..103ebe7dc8 100644 Binary files a/apps/main/locale/pt/LC_MESSAGES/django.mo and b/apps/main/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/main/locale/pt/LC_MESSAGES/django.po b/apps/main/locale/pt/LC_MESSAGES/django.po index 02ed418063..b60110fbf9 100644 --- a/apps/main/locale/pt/LC_MESSAGES/django.po +++ b/apps/main/locale/pt/LC_MESSAGES/django.po @@ -1,71 +1,69 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: # , 2011. +# Roberto Rosario , 2012. msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" -"PO-Revision-Date: 2011-11-03 21:55+0000\n" -"Last-Translator: emersonsoares \n" -"Language-Team: Portuguese (http://www.transifex.net/projects/p/mayan-edms/" -"team/pt/)\n" -"Language: pt\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-21 16:38+0000\n" +"Last-Translator: Roberto Rosario \n" +"Language-Team: Portuguese (http://www.transifex.net/projects/p/mayan-edms/language/pt/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +"Language: pt\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -#: __init__.py:15 +#: __init__.py:33 msgid "maintenance" msgstr "manutenção" -#: __init__.py:16 +#: __init__.py:34 msgid "statistics" msgstr "estatísticas" -#: __init__.py:17 +#: __init__.py:35 msgid "diagnostics" msgstr "diagnósticos" -#: __init__.py:18 +#: __init__.py:36 msgid "sentry" msgstr "sentinela" -#: __init__.py:19 +#: __init__.py:37 msgid "admin site" msgstr "site de administação" -#: __init__.py:30 +#: __init__.py:40 msgid "home" msgstr "inicio" -#: __init__.py:32 +#: __init__.py:42 msgid "search" msgstr "pesquisa" -#: views.py:41 +#: views.py:43 msgid "maintenance menu" msgstr "menu de manutenção" -#: views.py:53 +#: views.py:56 msgid "Statistics" msgstr "Estatísticas" -#: views.py:61 +#: views.py:66 msgid "Diagnostics" msgstr "Diagnósticos" #: conf/settings.py:12 msgid "" -"Controls whether the search functionality is provided by a sidebar widget or " -"by a menu entry." -msgstr "" -"Controla-se a funcionalidade de pesquisa é fornecido por um widget da barra " -"lateral ou por uma entrada de menu." +"Controls whether the search functionality is provided by a sidebar widget or" +" by a menu entry." +msgstr "Controla-se a funcionalidade de pesquisa é fornecido por um widget da barra lateral ou por uma entrada de menu." #: templates/about.html:5 msgid "About this program" @@ -103,46 +101,47 @@ msgstr "Sair" msgid "Secondary menu" msgstr "Menu secundário" -#: templates/base.html:302 +#: templates/base.html:301 #, python-format msgid "Actions for %(name)s: %(navigation_object)s" msgstr "Ações para %(name)s: %(navigation_object)s " -#: templates/base.html:304 templates/base.html.py:336 +#: templates/base.html:303 templates/base.html.py:346 #, python-format msgid "Actions for: %(navigation_object)s" msgstr "Ações para: %(navigation_object)s " -#: templates/base.html:307 +#: templates/base.html:306 msgid "Available actions" msgstr "Ações disponíveis" -#: templates/base.html:319 templates/base.html.py:351 +#: templates/base.html:318 templates/base.html.py:361 msgid "Related actions" msgstr "Ações relacionadas" -#: templates/base.html:334 +#: templates/base.html:329 templates/base.html.py:374 +msgid "Other available actions" +msgstr "Outras ações disponíveis" + +#: templates/base.html:344 #, python-format msgid "Actions for %(object_name)s: %(navigation_object)s" msgstr "Ações para %(object_name)s : %(navigation_object)s " -#: templates/base.html:339 +#: templates/base.html:349 msgid "Actions" msgstr "Ações" -#: templates/base.html:364 -msgid "Other available actions" -msgstr "Outras ações disponíveis" +#: templates/home.html:8 +msgid "Django based open source document management system" +msgstr "Sistema aberto de gerenciamento de documentos baseado em Django " #: templates/project_description.html:6 msgid "" "Open source, Django based electronic document manager with custom metadata, " "indexing, tagging, file serving integration and OCR capabilities" -msgstr "" -"Código Aberto, Django gestão de documentos eletrônicos com metadados " -"personalizados, indexação, etiquetagem, a integração do serviço de arquivos " -"e recursos de OCR" +msgstr "Código Aberto, Django gestão de documentos eletrônicos com metadados personalizados, indexação, etiquetagem, a integração do serviço de arquivos e recursos de OCR" -#: templates/project_description.html:15 +#: templates/project_description.html:18 msgid "Released under the GPL V3 License" msgstr "Liberado sob a licença GPL V3" diff --git a/apps/main/locale/ru/LC_MESSAGES/django.mo b/apps/main/locale/ru/LC_MESSAGES/django.mo index 4dd6882e6c..ded377f111 100644 Binary files a/apps/main/locale/ru/LC_MESSAGES/django.mo and b/apps/main/locale/ru/LC_MESSAGES/django.mo differ diff --git a/apps/main/locale/ru/LC_MESSAGES/django.po b/apps/main/locale/ru/LC_MESSAGES/django.po index 068fcbe6a4..a465e0d820 100644 --- a/apps/main/locale/ru/LC_MESSAGES/django.po +++ b/apps/main/locale/ru/LC_MESSAGES/django.po @@ -3,14 +3,14 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: -# Sergey Glita , 2011. +# Sergey Glita , 2011, 2012. msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" -"PO-Revision-Date: 2011-11-19 21:04+0000\n" -"Last-Translator: gsv70 \n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-01-17 10:58+0000\n" +"Last-Translator: Sergey Glita \n" "Language-Team: Russian (http://www.transifex.net/projects/p/mayan-edms/team/" "ru/)\n" "Language: ru\n" @@ -20,43 +20,43 @@ msgstr "" "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:15 +#: __init__.py:33 msgid "maintenance" msgstr "техническое обслуживание" -#: __init__.py:16 +#: __init__.py:34 msgid "statistics" msgstr "статистика" -#: __init__.py:17 +#: __init__.py:35 msgid "diagnostics" msgstr "диагностика" -#: __init__.py:18 +#: __init__.py:36 msgid "sentry" msgstr "sentry" -#: __init__.py:19 +#: __init__.py:37 msgid "admin site" msgstr "админка" -#: __init__.py:30 +#: __init__.py:40 msgid "home" msgstr "Начало" -#: __init__.py:32 +#: __init__.py:42 msgid "search" msgstr "поиск" -#: views.py:41 +#: views.py:43 msgid "maintenance menu" msgstr "меню техобслуживания" -#: views.py:53 +#: views.py:56 msgid "Statistics" msgstr "Статистика" -#: views.py:61 +#: views.py:66 msgid "Diagnostics" msgstr "Диагностика" @@ -104,36 +104,40 @@ msgstr "Выход" msgid "Secondary menu" msgstr "Вторичное меню" -#: templates/base.html:302 +#: templates/base.html:301 #, python-format msgid "Actions for %(name)s: %(navigation_object)s" msgstr "Действия для %(name)s: %(navigation_object)s" -#: templates/base.html:304 templates/base.html.py:336 +#: templates/base.html:303 templates/base.html.py:346 #, python-format msgid "Actions for: %(navigation_object)s" msgstr "Действия для: %(navigation_object)s" -#: templates/base.html:307 +#: templates/base.html:306 msgid "Available actions" msgstr "Доступные действия" -#: templates/base.html:319 templates/base.html.py:351 +#: templates/base.html:318 templates/base.html.py:361 msgid "Related actions" msgstr "Связанные действия" -#: templates/base.html:334 +#: templates/base.html:329 templates/base.html.py:374 +msgid "Other available actions" +msgstr "Другие возможные действия" + +#: templates/base.html:344 #, python-format msgid "Actions for %(object_name)s: %(navigation_object)s" msgstr "Действия для %(object_name)s: %(navigation_object)s" -#: templates/base.html:339 +#: templates/base.html:349 msgid "Actions" msgstr "Действия" -#: templates/base.html:364 -msgid "Other available actions" -msgstr "Другие возможные действия" +#: templates/home.html:8 +msgid "Django based open source document management system" +msgstr "Система управления электронными документами на основе Django" #: templates/project_description.html:6 msgid "" @@ -144,6 +148,6 @@ msgstr "" "управления электронными документами с метаданными, индексами, тегами, " "интеграцией с файл-сервером и возможностью оптического распознавания текста." -#: templates/project_description.html:15 +#: templates/project_description.html:18 msgid "Released under the GPL V3 License" msgstr "Выпущено под лицензией GPL V3" diff --git a/apps/main/templates/base.html b/apps/main/templates/base.html index c1fcedd0cb..311d930dc8 100644 --- a/apps/main/templates/base.html +++ b/apps/main/templates/base.html @@ -139,6 +139,16 @@ 'type' : 'image', 'autoScale' : true }); + + $("a.fancybox-staging").fancybox({ + 'titleShow' : true, + 'transitionIn' : 'elastic', + 'transitionOut' : 'elastic', + 'easingIn' : 'easeOutBack', + 'easingOut' : 'easeInBack', + 'type' : 'image', + 'autoScale' : true + }); $("a.fancybox-noscaling").fancybox({ 'titleShow' : false, @@ -181,7 +191,7 @@ {% block web_theme_user_navigation %}
  • {% trans "User" %}: - {% if user.is_anonymous %} + {% if not user.is_authenticated %} {% trans "Anonymous" %} {% else %} {{ user.get_full_name|default:user }} @@ -202,7 +212,7 @@
  • {% endif %} {% get_setting "LOGIN_URL" as login_url %} -
  • {% if user.is_anonymous %}{% trans "Login" %}{% else %}{% trans "Logout" %}{% endif %}
  • +
  • {% if not user.is_authenticated %}{% trans "Login" %}{% else %}{% trans "Logout" %}{% endif %}
  • {% endblock %} {% block web_theme_main_navigation %} @@ -278,7 +288,7 @@ {% endwith %} {% endwith %} {% endif %} - + {% get_object_navigation_links "secondary_menu" as object_navigation_links %} {% if object_navigation_links %}
    @@ -290,7 +300,6 @@
    {% endif %} - {% if navigation_object_list %} {% for navigation_object_dict in navigation_object_list %} {% copy_variable navigation_object_dict.object as "navigation_object_name" %} @@ -324,6 +333,17 @@
    {% endif %} + {% get_object_navigation_links "sidebar" as object_navigation_links %} + {% if object_navigation_links %} +
    +

    {% trans "Other available actions" %}

    + +
    + {% endif %} {% endfor %} {% else %} {% get_object_navigation_links as object_navigation_links %} diff --git a/apps/main/templates/project_description.html b/apps/main/templates/project_description.html index aac2662fd8..16a53de480 100644 --- a/apps/main/templates/project_description.html +++ b/apps/main/templates/project_description.html @@ -6,7 +6,10 @@ {% trans "Open source, Django based electronic document manager with custom metadata, indexing, tagging, file serving integration and OCR capabilities" %}

    - http://bit.ly/mayan-edms + http://www.mayan-edms.com +

    +

    + http://mayan.readthedocs.org/

    http://www.github.com/rosarior/mayan/ diff --git a/apps/main/tests.py b/apps/main/tests.py deleted file mode 100644 index 2247054b35..0000000000 --- a/apps/main/tests.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -This file demonstrates two different styles of tests (one doctest and one -unittest). These will both pass when you run "manage.py test". - -Replace these 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.failUnlessEqual(1 + 1, 2) - -__test__ = {"doctest": """ -Another way to test that 1 + 1 is equal to 2. - ->>> 1 + 1 == 2 -True -"""} - diff --git a/apps/main/views.py b/apps/main/views.py index f4ff678e46..12aabda9e5 100644 --- a/apps/main/views.py +++ b/apps/main/views.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import + from django.shortcuts import render_to_response from django.template import RequestContext from django.utils.translation import ugettext_lazy as _ @@ -7,10 +9,10 @@ from django.core.urlresolvers import reverse from documents.statistics import get_statistics as documents_statistics from ocr.statistics import get_statistics as ocr_statistics -from permissions.api import check_permissions +from permissions.models import Permission -from main.api import diagnostics, tools -from main.conf.settings import DISABLE_HOME_VIEW +from .api import diagnostics, tools +from .conf.settings import DISABLE_HOME_VIEW def home(request): @@ -31,7 +33,7 @@ def maintenance_menu(request): for link in values['links']: try: permissions = link.get('permissions', []) - check_permissions(request.user, permissions) + Permission.objects.check_permissions(request.user, permissions) user_tools[namespace]['links'].append(link) except PermissionDenied: pass @@ -44,15 +46,18 @@ def maintenance_menu(request): def statistics(request): - blocks = [] - blocks.append(documents_statistics()) - blocks.append(ocr_statistics()) + if request.user.is_superuser or request.user.is_staff: + blocks = [] + blocks.append(documents_statistics()) + blocks.append(ocr_statistics()) - return render_to_response('statistics.html', { - 'blocks': blocks, - 'title': _(u'Statistics') - }, - context_instance=RequestContext(request)) + return render_to_response('statistics.html', { + 'blocks': blocks, + 'title': _(u'Statistics') + }, + context_instance=RequestContext(request)) + else: + raise PermissionDenied def diagnostics_view(request): diff --git a/apps/metadata/__init__.py b/apps/metadata/__init__.py index b61556e268..9f7ea6bb6e 100644 --- a/apps/metadata/__init__.py +++ b/apps/metadata/__init__.py @@ -1,76 +1,52 @@ +from __future__ import absolute_import + from django.utils.translation import ugettext_lazy as _ -from navigation.api import register_links, register_multi_item_links, \ - register_sidebar_template -from permissions.api import register_permission, set_namespace_title +from navigation.api import (register_links, register_multi_item_links, + register_sidebar_template) from documents.models import Document, DocumentType -from documents.literals import PERMISSION_DOCUMENT_TYPE_EDIT +from documents.permissions import PERMISSION_DOCUMENT_TYPE_EDIT from project_setup.api import register_setup +from acls.api import class_permissions -from metadata.models import MetadataType, MetadataSet - -PERMISSION_METADATA_DOCUMENT_EDIT = {'namespace': 'metadata', 'name': u'metadata_document_edit', 'label': _(u'Edit a document\'s metadata')} -PERMISSION_METADATA_DOCUMENT_ADD = {'namespace': 'metadata', 'name': u'metadata_document_add', 'label': _(u'Add metadata to a document')} -PERMISSION_METADATA_DOCUMENT_REMOVE = {'namespace': 'metadata', 'name': u'metadata_document_remove', 'label': _(u'Remove metadata from a document')} -PERMISSION_METADATA_DOCUMENT_VIEW = {'namespace': 'metadata', 'name': u'metadata_document_view', 'label': _(u'View metadata from a document')} - -PERMISSION_METADATA_TYPE_EDIT = {'namespace': 'metadata_setup', 'name': u'metadata_type_edit', 'label': _(u'Edit metadata types')} -PERMISSION_METADATA_TYPE_CREATE = {'namespace': 'metadata_setup', 'name': u'metadata_type_create', 'label': _(u'Create new metadata types')} -PERMISSION_METADATA_TYPE_DELETE = {'namespace': 'metadata_setup', 'name': u'metadata_type_delete', 'label': _(u'Delete metadata types')} -PERMISSION_METADATA_TYPE_VIEW = {'namespace': 'metadata_setup', 'name': u'metadata_type_view', 'label': _(u'View metadata types')} - -PERMISSION_METADATA_SET_EDIT = {'namespace': 'metadata_setup', 'name': u'metadata_set_edit', 'label': _(u'Edit metadata sets')} -PERMISSION_METADATA_SET_CREATE = {'namespace': 'metadata_setup', 'name': u'metadata_set_create', 'label': _(u'Create new metadata sets')} -PERMISSION_METADATA_SET_DELETE = {'namespace': 'metadata_setup', 'name': u'metadata_set_delete', 'label': _(u'Delete metadata sets')} -PERMISSION_METADATA_SET_VIEW = {'namespace': 'metadata_setup', 'name': u'metadata_set_view', 'label': _(u'View metadata sets')} - -set_namespace_title('metadata', _(u'Metadata')) -register_permission(PERMISSION_METADATA_DOCUMENT_EDIT) -register_permission(PERMISSION_METADATA_DOCUMENT_ADD) -register_permission(PERMISSION_METADATA_DOCUMENT_REMOVE) -register_permission(PERMISSION_METADATA_DOCUMENT_VIEW) - -set_namespace_title('metadata_setup', _(u'Metadata setup')) -register_permission(PERMISSION_METADATA_TYPE_EDIT) -register_permission(PERMISSION_METADATA_TYPE_CREATE) -register_permission(PERMISSION_METADATA_TYPE_DELETE) -register_permission(PERMISSION_METADATA_TYPE_VIEW) - -register_permission(PERMISSION_METADATA_SET_EDIT) -register_permission(PERMISSION_METADATA_SET_CREATE) -register_permission(PERMISSION_METADATA_SET_DELETE) -register_permission(PERMISSION_METADATA_SET_VIEW) +from .models import MetadataType, MetadataSet +from .permissions import (PERMISSION_METADATA_DOCUMENT_EDIT, + PERMISSION_METADATA_DOCUMENT_ADD, PERMISSION_METADATA_DOCUMENT_REMOVE, + PERMISSION_METADATA_DOCUMENT_VIEW, PERMISSION_METADATA_TYPE_EDIT, + PERMISSION_METADATA_TYPE_CREATE, PERMISSION_METADATA_TYPE_DELETE, + PERMISSION_METADATA_TYPE_VIEW, PERMISSION_METADATA_SET_EDIT, + PERMISSION_METADATA_SET_CREATE, PERMISSION_METADATA_SET_DELETE, + PERMISSION_METADATA_SET_VIEW) metadata_edit = {'text': _(u'edit metadata'), 'view': 'metadata_edit', 'args': 'object.pk', 'famfam': 'xhtml_go', 'permissions': [PERMISSION_METADATA_DOCUMENT_EDIT]} -metadata_view = {'text': _(u'metadata'), 'view': 'metadata_view', 'args': 'object.pk', 'famfam': 'xhtml_go', 'permissions': [PERMISSION_METADATA_DOCUMENT_EDIT], 'children_view_regex': ['metadata']} +metadata_view = {'text': _(u'metadata'), 'view': 'metadata_view', 'args': 'object.pk', 'famfam': 'xhtml_go', 'permissions': [PERMISSION_METADATA_DOCUMENT_VIEW], 'children_view_regex': ['metadata']} metadata_multiple_edit = {'text': _(u'edit metadata'), 'view': 'metadata_multiple_edit', 'famfam': 'xhtml_go', 'permissions': [PERMISSION_METADATA_DOCUMENT_EDIT]} metadata_add = {'text': _(u'add metadata'), 'view': 'metadata_add', 'args': 'object.pk', 'famfam': 'xhtml_add', 'permissions': [PERMISSION_METADATA_DOCUMENT_ADD]} metadata_multiple_add = {'text': _(u'add metadata'), 'view': 'metadata_multiple_add', 'famfam': 'xhtml_add', 'permissions': [PERMISSION_METADATA_DOCUMENT_ADD]} metadata_remove = {'text': _(u'remove metadata'), 'view': 'metadata_remove', 'args': 'object.pk', 'famfam': 'xhtml_delete', 'permissions': [PERMISSION_METADATA_DOCUMENT_REMOVE]} metadata_multiple_remove = {'text': _(u'remove metadata'), 'view': 'metadata_multiple_remove', 'famfam': 'xhtml_delete', 'permissions': [PERMISSION_METADATA_DOCUMENT_REMOVE]} -setup_metadata_type_list = {'text': _(u'metadata types'), 'view': 'setup_metadata_type_list', 'famfam': 'xhtml_go', 'icon': 'xhtml.png', 'permissions': [PERMISSION_METADATA_TYPE_VIEW], 'children_view_regex': ['setup_metadata_type']} +setup_metadata_type_list = {'text': _(u'metadata types'), 'view': 'setup_metadata_type_list', 'famfam': 'xhtml_go', 'icon': 'xhtml.png', 'permissions': [PERMISSION_METADATA_TYPE_VIEW], 'children_view_regex': [r'setup_metadata_type']} setup_metadata_type_edit = {'text': _(u'edit'), 'view': 'setup_metadata_type_edit', 'args': 'object.pk', 'famfam': 'xhtml', 'permissions': [PERMISSION_METADATA_TYPE_EDIT]} setup_metadata_type_delete = {'text': _(u'delete'), 'view': 'setup_metadata_type_delete', 'args': 'object.pk', 'famfam': 'xhtml_delete', 'permissions': [PERMISSION_METADATA_TYPE_DELETE]} setup_metadata_type_create = {'text': _(u'create new'), 'view': 'setup_metadata_type_create', 'famfam': 'xhtml_add', 'permissions': [PERMISSION_METADATA_TYPE_CREATE]} -setup_metadata_set_list = {'text': _(u'metadata sets'), 'view': 'setup_metadata_set_list', 'famfam': 'table', 'icon': 'table.png', 'permissions': [PERMISSION_METADATA_SET_VIEW], 'children_view_regex': ['setup_metadata_set']} +setup_metadata_set_list = {'text': _(u'metadata sets'), 'view': 'setup_metadata_set_list', 'famfam': 'table', 'icon': 'table.png', 'permissions': [PERMISSION_METADATA_SET_VIEW], 'children_view_regex': [r'setup_metadata_set']} setup_metadata_set_edit = {'text': _(u'edit'), 'view': 'setup_metadata_set_edit', 'args': 'object.pk', 'famfam': 'table_edit', 'permissions': [PERMISSION_METADATA_SET_EDIT]} setup_metadata_set_delete = {'text': _(u'delete'), 'view': 'setup_metadata_set_delete', 'args': 'object.pk', 'famfam': 'table_delete', 'permissions': [PERMISSION_METADATA_SET_DELETE]} setup_metadata_set_create = {'text': _(u'create new'), 'view': 'setup_metadata_set_create', 'famfam': 'table_add', 'permissions': [PERMISSION_METADATA_SET_CREATE]} setup_document_type_metadata = {'text': _(u'default metadata'), 'view': 'setup_document_type_metadata', 'args': 'document_type.pk', 'famfam': 'xhtml', 'permissions': [PERMISSION_DOCUMENT_TYPE_EDIT]} -#register_links(Document, [metadata_add, metadata_edit, metadata_remove]) register_links(['metadata_add', 'metadata_edit', 'metadata_remove', 'metadata_view'], [metadata_add, metadata_edit, metadata_remove], menu_name='sidebar') -register_links(Document, [metadata_view], menu_name='form_header') #, metadata_edit, metadata_remove]) +register_links(Document, [metadata_view], menu_name='form_header') register_multi_item_links(['document_find_duplicates', 'folder_view', 'index_instance_list', 'document_type_document_list', 'search', 'results', 'document_group_view', 'document_list', 'document_list_recent'], [metadata_multiple_add, metadata_multiple_edit, metadata_multiple_remove]) register_links(MetadataType, [setup_metadata_type_edit, setup_metadata_type_delete]) -register_links(['setup_metadata_type_delete', 'setup_metadata_type_edit', 'setup_metadata_type_list', 'setup_metadata_type_create'], [setup_metadata_type_list, setup_metadata_type_create], menu_name='sidebar') +register_links([MetadataType, 'setup_metadata_type_list', 'setup_metadata_type_create'], [setup_metadata_type_list, setup_metadata_type_create], menu_name='secondary_menu') register_links(MetadataSet, [setup_metadata_set_edit, setup_metadata_set_delete]) -register_links(['setup_metadata_set_delete', 'setup_metadata_set_edit', 'setup_metadata_set_list', 'setup_metadata_set_create'], [setup_metadata_set_list, setup_metadata_set_create], menu_name='sidebar') +register_links([MetadataSet, 'setup_metadata_set_list', 'setup_metadata_set_create'], [setup_metadata_set_list, setup_metadata_set_create], menu_name='secondary_menu') register_links(DocumentType, [setup_document_type_metadata]) @@ -83,3 +59,9 @@ register_sidebar_template(['setup_metadata_set_list'], 'metadata_set_help.html') register_setup(setup_metadata_type_list) register_setup(setup_metadata_set_list) +class_permissions(Document, [ + PERMISSION_METADATA_DOCUMENT_EDIT, + PERMISSION_METADATA_DOCUMENT_ADD, + PERMISSION_METADATA_DOCUMENT_REMOVE, + PERMISSION_METADATA_DOCUMENT_VIEW, +]) diff --git a/apps/metadata/admin.py b/apps/metadata/admin.py index ac32bdc131..058d5d0544 100644 --- a/apps/metadata/admin.py +++ b/apps/metadata/admin.py @@ -1,7 +1,9 @@ +from __future__ import absolute_import + from django.contrib import admin -from metadata.models import MetadataType, MetadataSet, MetadataSetItem, \ - DocumentMetadata, DocumentTypeDefaults +from .models import (MetadataType, MetadataSet, MetadataSetItem, + DocumentMetadata, DocumentTypeDefaults) class MetadataTypeAdmin(admin.ModelAdmin): diff --git a/apps/metadata/api.py b/apps/metadata/api.py index 3208ef7e23..77a86056bb 100644 --- a/apps/metadata/api.py +++ b/apps/metadata/api.py @@ -1,14 +1,17 @@ """Metadata handling commonalities""" +from __future__ import absolute_import from urllib import unquote_plus from django.shortcuts import get_object_or_404 -from metadata.models import DocumentMetadata, MetadataType +from .models import DocumentMetadata, MetadataType def decode_metadata_from_url(url_dict): - """Parse a URL query string to a list of metadata""" + """ + Parse a URL query string to a list of metadata + """ metadata_dict = { 'id': {}, 'value': {} @@ -78,7 +81,9 @@ def save_metadata(metadata_dict, document, create=False): def metadata_repr(metadata_list): - """Return a printable representation of a metadata list""" + """ + Return a printable representation of a metadata list + """ return u', '.join(metadata_repr_as_list(metadata_list)) @@ -102,3 +107,15 @@ def get_metadata_string(document): Return a formated representation of a document's metadata values """ return u', '.join([u'%s - %s' % (metadata.metadata_type, metadata.value) for metadata in DocumentMetadata.objects.filter(document=document).select_related('metadata_type')]) + + +def convert_dict_to_dict_list(dictionary): + result = [] + for key, value in dictionary.items(): + try: + metadata_type = MetadataType.objects.get(name=key) + except MetadataType.DoesNotExist: + raise ValueError('Unknown metadata type name') + result.append({'id': metadata_type.pk, 'value': value}) + + return result diff --git a/apps/metadata/classes.py b/apps/metadata/classes.py index b8476a744f..5bb9db0709 100644 --- a/apps/metadata/classes.py +++ b/apps/metadata/classes.py @@ -1,7 +1,9 @@ from django.utils.translation import ugettext_lazy as _ +from acls.classes import EncapsulatedObject -class MetadataObject(object): + +class MetadataClass(object): def __init__(self, dictionary): self.dictionary = dictionary @@ -10,3 +12,7 @@ class MetadataObject(object): return self.dictionary.get(name) else: raise AttributeError(_(u'\'metadata\' object has no attribute \'%s\'') % name) + + +class MetadataObjectWrapper(EncapsulatedObject): + source_object_name = u'metadata_object' diff --git a/apps/metadata/forms.py b/apps/metadata/forms.py index ce921ee47c..c2e281242c 100644 --- a/apps/metadata/forms.py +++ b/apps/metadata/forms.py @@ -1,13 +1,13 @@ +from __future__ import absolute_import + from django import forms from django.utils.translation import ugettext_lazy as _ from django.forms.formsets import formset_factory from common.widgets import ScrollableCheckboxSelectMultiple -from metadata.conf.settings import AVAILABLE_MODELS -from metadata.conf.settings import AVAILABLE_FUNCTIONS -from metadata.models import MetadataSet, MetadataType, \ - DocumentTypeDefaults +from .conf.settings import AVAILABLE_MODELS, AVAILABLE_FUNCTIONS +from .models import MetadataSet, MetadataType, DocumentTypeDefaults class MetadataForm(forms.Form): diff --git a/apps/metadata/locale/en/LC_MESSAGES/django.po b/apps/metadata/locale/en/LC_MESSAGES/django.po index 6f75f44ea8..6d79066c9f 100644 --- a/apps/metadata/locale/en/LC_MESSAGES/django.po +++ b/apps/metadata/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,103 +17,47 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: __init__.py:12 -msgid "Edit a document's metadata" -msgstr "" - -#: __init__.py:13 -msgid "Add metadata to a document" -msgstr "" - -#: __init__.py:14 -msgid "Remove metadata from a document" -msgstr "" - -#: __init__.py:15 -msgid "View metadata from a document" -msgstr "" - -#: __init__.py:17 -msgid "Edit metadata types" -msgstr "" - -#: __init__.py:18 -msgid "Create new metadata types" -msgstr "" - -#: __init__.py:19 -msgid "Delete metadata types" -msgstr "" - -#: __init__.py:20 -msgid "View metadata types" -msgstr "" - -#: __init__.py:22 -msgid "Edit metadata sets" -msgstr "" - -#: __init__.py:23 -msgid "Create new metadata sets" -msgstr "" - -#: __init__.py:24 -msgid "Delete metadata sets" -msgstr "" - -#: __init__.py:25 -msgid "View metadata sets" -msgstr "" - -#: __init__.py:27 forms.py:94 -msgid "Metadata" -msgstr "" - -#: __init__.py:33 -msgid "Metadata setup" -msgstr "" - -#: __init__.py:44 __init__.py:46 +#: __init__.py:21 __init__.py:23 msgid "edit metadata" msgstr "" -#: __init__.py:45 +#: __init__.py:22 msgid "metadata" msgstr "" -#: __init__.py:47 __init__.py:48 +#: __init__.py:24 __init__.py:25 msgid "add metadata" msgstr "" -#: __init__.py:49 __init__.py:50 +#: __init__.py:26 __init__.py:27 msgid "remove metadata" msgstr "" -#: __init__.py:52 models.py:33 views.py:294 +#: __init__.py:29 models.py:34 views.py:316 msgid "metadata types" msgstr "" -#: __init__.py:53 __init__.py:58 +#: __init__.py:30 __init__.py:35 msgid "edit" msgstr "" -#: __init__.py:54 __init__.py:59 +#: __init__.py:31 __init__.py:36 msgid "delete" msgstr "" -#: __init__.py:55 __init__.py:60 +#: __init__.py:32 __init__.py:37 msgid "create new" msgstr "" -#: __init__.py:57 views.py:394 +#: __init__.py:34 views.py:416 msgid "metadata sets" msgstr "" -#: __init__.py:62 models.py:92 +#: __init__.py:39 models.py:93 msgid "default metadata" msgstr "" -#: classes.py:12 +#: classes.py:14 #, python-format msgid "'metadata' object has no attribute '%s'" msgstr "" @@ -146,276 +90,336 @@ msgstr "" msgid "Remove" msgstr "" -#: forms.py:86 +#: forms.py:86 views.py:541 views.py:559 msgid "Metadata sets" msgstr "" -#: models.py:9 -#, python-format -msgid " Available models: %s" +#: forms.py:94 permissions.py:7 +msgid "Metadata" msgstr "" #: models.py:10 #, python-format +msgid " Available models: %s" +msgstr "" + +#: models.py:11 +#, python-format msgid " Available functions: %s" msgstr "" -#: models.py:17 +#: models.py:18 msgid "name" msgstr "" -#: models.py:17 +#: models.py:18 msgid "Do not use python reserved words, or spaces." msgstr "" -#: models.py:18 models.py:40 +#: models.py:19 models.py:41 msgid "title" msgstr "" -#: models.py:20 +#: models.py:21 msgid "default" msgstr "" -#: models.py:21 +#: models.py:22 #, python-format msgid "Enter a string to be evaluated.%s" msgstr "" -#: models.py:23 +#: models.py:24 msgid "lookup" msgstr "" -#: models.py:24 +#: models.py:25 #, python-format msgid "" "Enter a string to be evaluated. Example: [user.get_full_name() for user in " "User.objects.all()].%s" msgstr "" -#: models.py:32 models.py:57 views.py:331 views.py:376 +#: models.py:33 models.py:58 views.py:353 views.py:398 msgid "metadata type" msgstr "" -#: models.py:47 models.py:48 models.py:56 views.py:446 views.py:491 +#: models.py:48 models.py:49 models.py:57 views.py:468 views.py:514 msgid "metadata set" msgstr "" -#: models.py:64 +#: models.py:65 msgid "metadata set item" msgstr "" -#: models.py:65 +#: models.py:66 msgid "metadata set items" msgstr "" -#: models.py:73 +#: models.py:74 msgid "document" msgstr "" -#: models.py:74 +#: models.py:75 msgid "type" msgstr "" -#: models.py:75 views.py:283 +#: models.py:76 views.py:304 msgid "value" msgstr "" -#: models.py:81 models.py:82 +#: models.py:82 models.py:83 msgid "document metadata" msgstr "" -#: models.py:90 views.py:559 +#: models.py:91 views.py:599 msgid "document type" msgstr "" -#: models.py:91 +#: models.py:92 msgid "default metadata sets" msgstr "" -#: models.py:98 +#: models.py:99 msgid "document type defaults" msgstr "" -#: models.py:99 +#: models.py:100 msgid "document types defaults" msgstr "" -#: views.py:38 views.py:193 +#: permissions.py:8 +msgid "Edit a document's metadata" +msgstr "" + +#: permissions.py:9 +msgid "Add metadata to a document" +msgstr "" + +#: permissions.py:10 +msgid "Remove metadata from a document" +msgstr "" + +#: permissions.py:11 +msgid "View metadata from a document" +msgstr "" + +#: permissions.py:13 +msgid "Metadata setup" +msgstr "" + +#: permissions.py:14 +msgid "Edit metadata types" +msgstr "" + +#: permissions.py:15 +msgid "Create new metadata types" +msgstr "" + +#: permissions.py:16 +msgid "Delete metadata types" +msgstr "" + +#: permissions.py:17 +msgid "View metadata types" +msgstr "" + +#: permissions.py:19 +msgid "Edit metadata sets" +msgstr "" + +#: permissions.py:20 +msgid "Create new metadata sets" +msgstr "" + +#: permissions.py:21 +msgid "Delete metadata sets" +msgstr "" + +#: permissions.py:22 +msgid "View metadata sets" +msgstr "" + +#: views.py:41 views.py:204 msgid "The selected document doesn't have any metadata." msgstr "" -#: views.py:43 views.py:131 views.py:199 +#: views.py:52 views.py:144 views.py:216 msgid "Must provide at least one document." msgstr "" -#: views.py:78 views.py:234 +#: views.py:87 views.py:251 #, python-format msgid "Error deleting document indexes; %s" msgstr "" -#: views.py:90 +#: views.py:99 #, python-format msgid "Error editing metadata for document %(document)s; %(error)s." msgstr "" -#: views.py:93 +#: views.py:102 #, python-format msgid "Metadata for document %s edited successfully." msgstr "" -#: views.py:98 views.py:251 +#: views.py:107 views.py:268 #, python-format msgid "Error updating document indexes; %s" msgstr "" -#: views.py:100 views.py:253 +#: views.py:109 views.py:270 msgid "Document indexes updated successfully." msgstr "" -#: views.py:111 +#: views.py:120 #, python-format msgid "Edit metadata for document: %s" msgstr "" -#: views.py:113 +#: views.py:122 #, python-format msgid "Edit metadata for documents: %s" msgstr "" -#: views.py:148 +#: views.py:161 #, python-format msgid "" "Metadata type: %(metadata_type)s successfully added to document %(document)s." msgstr "" -#: views.py:151 +#: views.py:164 #, python-format msgid "" "Metadata type: %(metadata_type)s already present in document %(document)s." msgstr "" -#: views.py:175 +#: views.py:188 #, python-format msgid "Add metadata type to document: %s" msgstr "" -#: views.py:177 +#: views.py:190 #, python-format msgid "Add metadata type to documents: %s" msgstr "" -#: views.py:242 +#: views.py:259 #, python-format msgid "" "Successfully remove metadata type: %(metadata_type)s from document: " "%(document)s." msgstr "" -#: views.py:245 +#: views.py:262 #, python-format msgid "" "Error removing metadata type: %(metadata_type)s from document: %(document)s." msgstr "" -#: views.py:264 +#: views.py:281 #, python-format msgid "Remove metadata types from document: %s" msgstr "" -#: views.py:266 +#: views.py:283 #, python-format msgid "Remove metadata types from documents: %s" msgstr "" -#: views.py:281 +#: views.py:302 #, python-format msgid "metadata for: %s" msgstr "" -#: views.py:298 +#: views.py:320 msgid "internal name" msgstr "" -#: views.py:319 +#: views.py:341 msgid "Metadata type edited successfully" msgstr "" -#: views.py:322 +#: views.py:344 #, python-format msgid "Error editing metadata type; %s" msgstr "" -#: views.py:328 +#: views.py:350 #, python-format msgid "edit metadata type: %s" msgstr "" -#: views.py:343 +#: views.py:365 msgid "Metadata type created successfully" msgstr "" -#: views.py:349 +#: views.py:371 msgid "create metadata type" msgstr "" -#: views.py:368 +#: views.py:390 #, python-format msgid "Metadata type: %s deleted successfully." msgstr "" -#: views.py:370 +#: views.py:392 #, python-format msgid "Metadata type: %(metadata_type)s delete error: %(error)s" msgstr "" -#: views.py:381 +#: views.py:403 #, python-format msgid "Are you sure you wish to delete the metadata type: %s?" msgstr "" -#: views.py:398 +#: views.py:420 msgid "members" msgstr "" -#: views.py:442 +#: views.py:464 #, python-format msgid "non members of metadata set: %s" msgstr "" -#: views.py:443 +#: views.py:465 #, python-format msgid "members of metadata set: %s" msgstr "" -#: views.py:458 +#: views.py:480 msgid "Metadata set created successfully" msgstr "" -#: views.py:464 +#: views.py:486 msgid "create metadata set" msgstr "" -#: views.py:483 +#: views.py:505 #, python-format msgid "Metadata set: %s deleted successfully." msgstr "" -#: views.py:485 +#: views.py:508 #, python-format msgid "Metadata set: %(metadata_set)s delete error: %(error)s" msgstr "" -#: views.py:496 +#: views.py:519 #, python-format msgid "Are you sure you wish to delete the metadata set: %s?" msgstr "" -#: views.py:554 +#: views.py:538 views.py:556 +msgid "Metadata types" +msgstr "" + +#: views.py:594 #, python-format msgid "non members of document type: %s" msgstr "" -#: views.py:555 +#: views.py:595 #, python-format msgid "members of document type: %s" msgstr "" diff --git a/apps/metadata/locale/es/LC_MESSAGES/django.mo b/apps/metadata/locale/es/LC_MESSAGES/django.mo index be0c179aa5..1448638f9e 100644 Binary files a/apps/metadata/locale/es/LC_MESSAGES/django.mo and b/apps/metadata/locale/es/LC_MESSAGES/django.mo differ diff --git a/apps/metadata/locale/es/LC_MESSAGES/django.po b/apps/metadata/locale/es/LC_MESSAGES/django.po index 03f08e5442..6134319ba7 100644 --- a/apps/metadata/locale/es/LC_MESSAGES/django.po +++ b/apps/metadata/locale/es/LC_MESSAGES/django.po @@ -1,120 +1,64 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: +# Roberto Rosario , 2012. msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" -"PO-Revision-Date: 2011-09-30 05:09+0000\n" -"Last-Translator: rosarior \n" -"Language-Team: Spanish (Castilian) (http://www.transifex.net/projects/p/" -"mayan-edms/team/es/)\n" -"Language: es\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-12 19:27+0000\n" +"Last-Translator: Roberto Rosario \n" +"Language-Team: Spanish (Castilian) (http://www.transifex.net/projects/p/mayan-edms/team/es/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +"Language: es\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -#: __init__.py:12 -msgid "Edit a document's metadata" -msgstr "Editar metadatos de documento" - -#: __init__.py:13 -msgid "Add metadata to a document" -msgstr "Añadir metadatos a documento" - -#: __init__.py:14 -msgid "Remove metadata from a document" -msgstr "Eliminar metadatos de un documento" - -#: __init__.py:15 -msgid "View metadata from a document" -msgstr "Ver los metadatos de un documento" - -#: __init__.py:17 -msgid "Edit metadata types" -msgstr "Editar tipos de metadatos" - -#: __init__.py:18 -msgid "Create new metadata types" -msgstr "Crear nuevos tipos de metadatos" - -#: __init__.py:19 -msgid "Delete metadata types" -msgstr "Eliminar tipos de metadatos" - -#: __init__.py:20 -msgid "View metadata types" -msgstr "Ver los tipos de metadatos" - -#: __init__.py:22 -msgid "Edit metadata sets" -msgstr "Editar conjuntos de metadatos" - -#: __init__.py:23 -msgid "Create new metadata sets" -msgstr "Crear nuevos conjuntos de metadatos" - -#: __init__.py:24 -msgid "Delete metadata sets" -msgstr "Eliminar conjuntos de metadatos" - -#: __init__.py:25 -msgid "View metadata sets" -msgstr "Ver los conjuntos de metadatos" - -#: __init__.py:27 forms.py:94 -msgid "Metadata" -msgstr "Metadatos" - -#: __init__.py:33 -msgid "Metadata setup" -msgstr "Configuración de metadatos" - -#: __init__.py:44 __init__.py:46 +#: __init__.py:21 __init__.py:23 msgid "edit metadata" msgstr "editar metadatos" -#: __init__.py:45 +#: __init__.py:22 msgid "metadata" msgstr "metadatos" -#: __init__.py:47 __init__.py:48 +#: __init__.py:24 __init__.py:25 msgid "add metadata" msgstr "añadir metadatos" -#: __init__.py:49 __init__.py:50 +#: __init__.py:26 __init__.py:27 msgid "remove metadata" msgstr "eliminar los metadatos" -#: __init__.py:52 models.py:33 views.py:294 +#: __init__.py:29 models.py:34 views.py:316 msgid "metadata types" msgstr "tipos de metadatos" -#: __init__.py:53 __init__.py:58 +#: __init__.py:30 __init__.py:35 msgid "edit" msgstr "editar" -#: __init__.py:54 __init__.py:59 +#: __init__.py:31 __init__.py:36 msgid "delete" msgstr "borrar" -#: __init__.py:55 __init__.py:60 +#: __init__.py:32 __init__.py:37 msgid "create new" msgstr "crear nuevo" -#: __init__.py:57 views.py:394 +#: __init__.py:34 views.py:416 msgid "metadata sets" msgstr "conjuntos de metadatos" -#: __init__.py:62 models.py:92 +#: __init__.py:39 models.py:93 msgid "default metadata" msgstr "metadatos predeterminados" -#: classes.py:12 +#: classes.py:14 #, python-format msgid "'metadata' object has no attribute '%s'" msgstr "objeto 'metadata' no tiene attributo '%s'" @@ -147,153 +91,210 @@ msgstr "Tipo de metadato" msgid "Remove" msgstr "Eliminar" -#: forms.py:86 +#: forms.py:86 views.py:541 views.py:559 msgid "Metadata sets" msgstr "Conjunto de metadatos" -#: models.py:9 +#: forms.py:94 permissions.py:7 +msgid "Metadata" +msgstr "Metadatos" + +#: models.py:10 #, python-format msgid " Available models: %s" msgstr "Modelos disponibles: %s" -#: models.py:10 +#: models.py:11 #, python-format msgid " Available functions: %s" msgstr "Funciones disponibles: %s" -#: models.py:17 +#: models.py:18 msgid "name" msgstr "nombre" -#: models.py:17 +#: models.py:18 msgid "Do not use python reserved words, or spaces." msgstr "No utilice palabras reservadas de Python, o espacios." -#: models.py:18 models.py:40 +#: models.py:19 models.py:41 msgid "title" msgstr "título" -#: models.py:20 +#: models.py:21 msgid "default" msgstr "por defecto" -#: models.py:21 +#: models.py:22 #, python-format msgid "Enter a string to be evaluated.%s" msgstr "Introduzca una cadena para ser evaluada. %s" -#: models.py:23 +#: models.py:24 msgid "lookup" msgstr "búsqueda" -#: models.py:24 +#: models.py:25 #, python-format msgid "" "Enter a string to be evaluated. Example: [user.get_full_name() for user in " "User.objects.all()].%s" msgstr "" -"Introduzca una cadena para ser evaluada. Ejemplo: [user.get_full_name() for " -"user in User.objects.all ()].%s" +"Introduzca una cadena para ser evaluada. Ejemplo: [user.get_full_name() for" +" user in User.objects.all ()].%s" -#: models.py:32 models.py:57 views.py:331 views.py:376 +#: models.py:33 models.py:58 views.py:353 views.py:398 msgid "metadata type" msgstr "tipos de metadatos" -#: models.py:47 models.py:48 models.py:56 views.py:446 views.py:491 +#: models.py:48 models.py:49 models.py:57 views.py:468 views.py:514 msgid "metadata set" msgstr "conjunto de metadatos" -#: models.py:64 +#: models.py:65 msgid "metadata set item" msgstr "artículo de conjunto de metadatos" -#: models.py:65 +#: models.py:66 msgid "metadata set items" msgstr "artículos de conjuntos de metadatos" -#: models.py:73 +#: models.py:74 msgid "document" msgstr "documento" -#: models.py:74 +#: models.py:75 msgid "type" msgstr "tipo" -#: models.py:75 views.py:283 +#: models.py:76 views.py:304 msgid "value" msgstr "valor" -#: models.py:81 models.py:82 +#: models.py:82 models.py:83 msgid "document metadata" msgstr "metadatos de documento" -#: models.py:90 views.py:559 +#: models.py:91 views.py:599 msgid "document type" msgstr "tipo de documento" -#: models.py:91 +#: models.py:92 msgid "default metadata sets" msgstr "conjuntos de metadatos predeterminados " -#: models.py:98 +#: models.py:99 msgid "document type defaults" msgstr "predeterminados de tipo de documento" -#: models.py:99 +#: models.py:100 msgid "document types defaults" msgstr "predeterminados de tipos de documentos" -#: views.py:38 views.py:193 +#: permissions.py:8 +msgid "Edit a document's metadata" +msgstr "Editar metadatos de documento" + +#: permissions.py:9 +msgid "Add metadata to a document" +msgstr "Añadir metadatos a documento" + +#: permissions.py:10 +msgid "Remove metadata from a document" +msgstr "Eliminar metadatos de un documento" + +#: permissions.py:11 +msgid "View metadata from a document" +msgstr "Ver los metadatos de un documento" + +#: permissions.py:13 +msgid "Metadata setup" +msgstr "Configuración de metadatos" + +#: permissions.py:14 +msgid "Edit metadata types" +msgstr "Editar tipos de metadatos" + +#: permissions.py:15 +msgid "Create new metadata types" +msgstr "Crear nuevos tipos de metadatos" + +#: permissions.py:16 +msgid "Delete metadata types" +msgstr "Eliminar tipos de metadatos" + +#: permissions.py:17 +msgid "View metadata types" +msgstr "Ver los tipos de metadatos" + +#: permissions.py:19 +msgid "Edit metadata sets" +msgstr "Editar conjuntos de metadatos" + +#: permissions.py:20 +msgid "Create new metadata sets" +msgstr "Crear nuevos conjuntos de metadatos" + +#: permissions.py:21 +msgid "Delete metadata sets" +msgstr "Eliminar conjuntos de metadatos" + +#: permissions.py:22 +msgid "View metadata sets" +msgstr "Ver los conjuntos de metadatos" + +#: views.py:41 views.py:204 msgid "The selected document doesn't have any metadata." msgstr "El documento seleccionado no tiene ningún tipo de metadatos." -#: views.py:43 views.py:131 views.py:199 +#: views.py:52 views.py:144 views.py:216 msgid "Must provide at least one document." msgstr "Debe proveer al menos un documento." -#: views.py:78 views.py:234 +#: views.py:87 views.py:251 #, python-format msgid "Error deleting document indexes; %s" msgstr "Error eliminando indicies de documento; %s" -#: views.py:90 +#: views.py:99 #, python-format msgid "Error editing metadata for document %(document)s; %(error)s." msgstr "Error editando metadatos para documento %(document)s; %(error)s." -#: views.py:93 +#: views.py:102 #, python-format msgid "Metadata for document %s edited successfully." msgstr "Metadatos para documento %s editados exitosamente." -#: views.py:98 views.py:251 +#: views.py:107 views.py:268 #, python-format msgid "Error updating document indexes; %s" msgstr "Error editando indices de documento; %s" -#: views.py:100 views.py:253 +#: views.py:109 views.py:270 msgid "Document indexes updated successfully." msgstr "Indices documento actualizados exitosamente." -#: views.py:111 +#: views.py:120 #, python-format msgid "Edit metadata for document: %s" msgstr "Editar metadatos para documento: %s" -#: views.py:113 +#: views.py:122 #, python-format msgid "Edit metadata for documents: %s" msgstr "Editar metadatos para documentos: %s" -#: views.py:148 +#: views.py:161 #, python-format msgid "" -"Metadata type: %(metadata_type)s successfully added to document %(document)s." +"Metadata type: %(metadata_type)s successfully added to document " +"%(document)s." msgstr "" "Typo de metadatos: %(metadata_type)s agregado exitosamente al documento " "%(document)s." -#: views.py:151 +#: views.py:164 #, python-format msgid "" "Metadata type: %(metadata_type)s already present in document %(document)s." @@ -301,17 +302,17 @@ msgstr "" "Typo de metadatos: %(metadata_type)s ya esta presente en el documento " "%(document)s." -#: views.py:175 +#: views.py:188 #, python-format msgid "Add metadata type to document: %s" msgstr "Agregar tipo de metadato al documento: %s" -#: views.py:177 +#: views.py:190 #, python-format msgid "Add metadata type to documents: %s" msgstr "Agregar tipo de metadato a los documentos: %s" -#: views.py:242 +#: views.py:259 #, python-format msgid "" "Successfully remove metadata type: %(metadata_type)s from document: " @@ -320,7 +321,7 @@ msgstr "" "Se elimino exitosamente el tipo de metadatos: %(metadata_type)s del " "documento: %(document)s." -#: views.py:245 +#: views.py:262 #, python-format msgid "" "Error removing metadata type: %(metadata_type)s from document: %(document)s." @@ -328,106 +329,110 @@ msgstr "" "Se elimino exitosamente el tipo de metadatos: %(metadata_type)s de los " "documentos: %(document)s." -#: views.py:264 +#: views.py:281 #, python-format msgid "Remove metadata types from document: %s" msgstr "Eliminar tipos de metadatos del documento: %s" -#: views.py:266 +#: views.py:283 #, python-format msgid "Remove metadata types from documents: %s" msgstr "Eliminar tipos de metadatos de los documentos: %s" -#: views.py:281 +#: views.py:302 #, python-format msgid "metadata for: %s" msgstr "metadatos para: %s" -#: views.py:298 +#: views.py:320 msgid "internal name" msgstr "nombre interno" -#: views.py:319 +#: views.py:341 msgid "Metadata type edited successfully" msgstr "Tipo de metadatos editado exitosamente." -#: views.py:322 +#: views.py:344 #, python-format msgid "Error editing metadata type; %s" msgstr "Error editando tipo de metadatos; %s" -#: views.py:328 +#: views.py:350 #, python-format msgid "edit metadata type: %s" msgstr "editar tipo de metadatos: %s" -#: views.py:343 +#: views.py:365 msgid "Metadata type created successfully" msgstr "Tipo de metadatos creado exitosamente" -#: views.py:349 +#: views.py:371 msgid "create metadata type" msgstr "crear tipo de metadatos" -#: views.py:368 +#: views.py:390 #, python-format msgid "Metadata type: %s deleted successfully." msgstr "Tipos de metadatos: %s eliminado exitosamente." -#: views.py:370 +#: views.py:392 #, python-format msgid "Metadata type: %(metadata_type)s delete error: %(error)s" msgstr "Error: %(error)s, eliminando tipos de metadatos: %(metadata_type)s" -#: views.py:381 +#: views.py:403 #, python-format msgid "Are you sure you wish to delete the metadata type: %s?" msgstr "¿Está seguro que desea eliminar el tipo de metadatos: %s?" -#: views.py:398 +#: views.py:420 msgid "members" msgstr "miembros" -#: views.py:442 +#: views.py:464 #, python-format msgid "non members of metadata set: %s" msgstr "no miembros del conjunto de metadatos: %s" -#: views.py:443 +#: views.py:465 #, python-format msgid "members of metadata set: %s" msgstr "miembros del conjunto de metadatos: %s" -#: views.py:458 +#: views.py:480 msgid "Metadata set created successfully" msgstr "Conjunto de metadatos creados exitosamente" -#: views.py:464 +#: views.py:486 msgid "create metadata set" msgstr "crear conjunto de metadatos" -#: views.py:483 +#: views.py:505 #, python-format msgid "Metadata set: %s deleted successfully." msgstr "Conjunto de metadatos: %s eliminado exitosamente." -#: views.py:485 +#: views.py:508 #, python-format msgid "Metadata set: %(metadata_set)s delete error: %(error)s" msgstr "" "Error: %(error)s, eliminando el conjunto de metadatos: %(metadata_set)s" -#: views.py:496 +#: views.py:519 #, python-format msgid "Are you sure you wish to delete the metadata set: %s?" msgstr "¿Está seguro que desea eliminar el conjunto de metadatos: %s?" -#: views.py:554 +#: views.py:538 views.py:556 +msgid "Metadata types" +msgstr "Tipos de metadatos" + +#: views.py:594 #, python-format msgid "non members of document type: %s" msgstr "no miembros del tipo de documento: %s" -#: views.py:555 +#: views.py:595 #, python-format msgid "members of document type: %s" msgstr "miembros del tipo de documento: %s" @@ -453,15 +458,15 @@ msgstr "¿Cuáles son los tipos de metadatos?" #: templates/metadata_type_help.html:4 msgid "" -"A metadata type defines the characteristics of a value of some kind that can " -"be attached to a document. Examples of metadata types are: a client name, a " -"date, or a project to which several documents belong. A metadata type's " +"A metadata type defines the characteristics of a value of some kind that can" +" be attached to a document. Examples of metadata types are: a client name, " +"a date, or a project to which several documents belong. A metadata type's " "name is the internal identifier with which it can be referenced to by other " -"modules such as the indexing module, the title is the value that is shown to " -"the users, the default value is the value an instance of this metadata type " -"will have initially, and the lookup value turns an instance of a metadata of " -"this type into a choice list which options are the result of the lookup's " -"code execution." +"modules such as the indexing module, the title is the value that is shown to" +" the users, the default value is the value an instance of this metadata type" +" will have initially, and the lookup value turns an instance of a metadata " +"of this type into a choice list which options are the result of the lookup's" +" code execution." msgstr "" "Un tipo de metadatos define las características de un valor de algún tipo " "que se puede conectar a un documento. Ejemplos de tipos de metadatos son: " @@ -471,5 +476,7 @@ msgstr "" "indexación, el título es el valor que se muestra a los usuarios, el valor " "por defecto es el valor que una instancia de este tipo de metadatos tendrá " "inicialmente, y el valor de búsqueda vuelve a una instancia de metadatos de " -"este tipo en una lista de opciones donde las opciones son el resultado de la " -"ejecución del valor de búsqueda." +"este tipo en una lista de opciones donde las opciones son el resultado de la" +" ejecución del valor de búsqueda." + + diff --git a/apps/metadata/locale/it/LC_MESSAGES/django.mo b/apps/metadata/locale/it/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..0303c94f85 Binary files /dev/null and b/apps/metadata/locale/it/LC_MESSAGES/django.mo differ diff --git a/apps/metadata/locale/it/LC_MESSAGES/django.po b/apps/metadata/locale/it/LC_MESSAGES/django.po new file mode 100644 index 0000000000..a37122bdee --- /dev/null +++ b/apps/metadata/locale/it/LC_MESSAGES/django.po @@ -0,0 +1,481 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# , 2011. +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-12 19:24+0000\n" +"Last-Translator: Roberto Rosario \n" +"Language-Team: Italian (http://www.transifex.net/projects/p/mayan-edms/team/it/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: it\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" + +#: __init__.py:21 __init__.py:23 +msgid "edit metadata" +msgstr "modifica metadati" + +#: __init__.py:22 +msgid "metadata" +msgstr "metadati" + +#: __init__.py:24 __init__.py:25 +msgid "add metadata" +msgstr "aggiungi metadata" + +#: __init__.py:26 __init__.py:27 +msgid "remove metadata" +msgstr "revoca metadata" + +#: __init__.py:29 models.py:34 views.py:316 +msgid "metadata types" +msgstr "tipo di metadata" + +#: __init__.py:30 __init__.py:35 +msgid "edit" +msgstr "modifica" + +#: __init__.py:31 __init__.py:36 +msgid "delete" +msgstr "cancella" + +#: __init__.py:32 __init__.py:37 +msgid "create new" +msgstr "crea nuovo" + +#: __init__.py:34 views.py:416 +msgid "metadata sets" +msgstr "set di metadati" + +#: __init__.py:39 models.py:93 +msgid "default metadata" +msgstr "metadati di default" + +#: classes.py:14 +#, python-format +msgid "'metadata' object has no attribute '%s'" +msgstr "'metadata' non ha gli attributi '%s'" + +#: forms.py:28 +msgid "required" +msgstr "richiesto" + +#: forms.py:54 +msgid "id" +msgstr "id" + +#: forms.py:55 +msgid "Name" +msgstr "Nome" + +#: forms.py:57 +msgid "Value" +msgstr "Valore" + +#: forms.py:58 +msgid "Update" +msgstr "Aggiurnato" + +#: forms.py:64 +msgid "Metadata type" +msgstr "Tipo di metadato" + +#: forms.py:68 +msgid "Remove" +msgstr "Revoca" + +#: forms.py:86 views.py:541 views.py:559 +msgid "Metadata sets" +msgstr "Set di metadati" + +#: forms.py:94 permissions.py:7 +msgid "Metadata" +msgstr "Metadati" + +#: models.py:10 +#, python-format +msgid " Available models: %s" +msgstr " Modelli disponibili: %s" + +#: models.py:11 +#, python-format +msgid " Available functions: %s" +msgstr " Funzioni disponibili: %s" + +#: models.py:18 +msgid "name" +msgstr "nome" + +#: models.py:18 +msgid "Do not use python reserved words, or spaces." +msgstr "Non usare parole riservate python, o spazi." + +#: models.py:19 models.py:41 +msgid "title" +msgstr "titolo" + +#: models.py:21 +msgid "default" +msgstr "default" + +#: models.py:22 +#, python-format +msgid "Enter a string to be evaluated.%s" +msgstr "Inserisci una stringa per la valutazione.%s" + +#: models.py:24 +msgid "lookup" +msgstr "lookup" + +#: models.py:25 +#, python-format +msgid "" +"Enter a string to be evaluated. Example: [user.get_full_name() for user in " +"User.objects.all()].%s" +msgstr "" +"Inserisci una stringa per la valutazione. Esempio: [user.get_full_name() " +"per l'utente User.objects.all()].%s" + +#: models.py:33 models.py:58 views.py:353 views.py:398 +msgid "metadata type" +msgstr "tipo di metadata" + +#: models.py:48 models.py:49 models.py:57 views.py:468 views.py:514 +msgid "metadata set" +msgstr "set di metadata" + +#: models.py:65 +msgid "metadata set item" +msgstr "set di elemento per il metadata" + +#: models.py:66 +msgid "metadata set items" +msgstr "set di elementi per il metadata" + +#: models.py:74 +msgid "document" +msgstr "documento" + +#: models.py:75 +msgid "type" +msgstr "tipo" + +#: models.py:76 views.py:304 +msgid "value" +msgstr "valore" + +#: models.py:82 models.py:83 +msgid "document metadata" +msgstr "metadata per il doccumento" + +#: models.py:91 views.py:599 +msgid "document type" +msgstr "tipo documento" + +#: models.py:92 +msgid "default metadata sets" +msgstr "set di default di metadata" + +#: models.py:99 +msgid "document type defaults" +msgstr "tipo documento predefinito" + +#: models.py:100 +msgid "document types defaults" +msgstr "tipi di documento predefiniti" + +#: permissions.py:8 +msgid "Edit a document's metadata" +msgstr "Modifica i metadata dei documenti" + +#: permissions.py:9 +msgid "Add metadata to a document" +msgstr "Aggiungi il metadato al documento" + +#: permissions.py:10 +msgid "Remove metadata from a document" +msgstr "Revoca il metadato al documento" + +#: permissions.py:11 +msgid "View metadata from a document" +msgstr "Visualizza il metadato per il documento" + +#: permissions.py:13 +msgid "Metadata setup" +msgstr "Setup metadati" + +#: permissions.py:14 +msgid "Edit metadata types" +msgstr "Modifica il tipo di metadato" + +#: permissions.py:15 +msgid "Create new metadata types" +msgstr "Crea il nuovo tipo di metadato" + +#: permissions.py:16 +msgid "Delete metadata types" +msgstr "Cancella il tipo di metadato" + +#: permissions.py:17 +msgid "View metadata types" +msgstr "Visualizza il tipo di metadato" + +#: permissions.py:19 +msgid "Edit metadata sets" +msgstr "Modifica il set di metadati" + +#: permissions.py:20 +msgid "Create new metadata sets" +msgstr "Crea un nuovo set di metadati" + +#: permissions.py:21 +msgid "Delete metadata sets" +msgstr "Cancella il set dei metadati" + +#: permissions.py:22 +msgid "View metadata sets" +msgstr "Visualizza il set dei metadati" + +#: views.py:41 views.py:204 +msgid "The selected document doesn't have any metadata." +msgstr "Il documento selezionato non ha metadati." + +#: views.py:52 views.py:144 views.py:216 +msgid "Must provide at least one document." +msgstr "Devi fornire almeno un documento." + +#: views.py:87 views.py:251 +#, python-format +msgid "Error deleting document indexes; %s" +msgstr "Errore nella cancellazione degli indici di documento;%s" + +#: views.py:99 +#, python-format +msgid "Error editing metadata for document %(document)s; %(error)s." +msgstr "" +"Errore nella modifica dei metadata per il documento %(document)s; %(error)s." + +#: views.py:102 +#, python-format +msgid "Metadata for document %s edited successfully." +msgstr "Metadata per il documento %s modificato con successo." + +#: views.py:107 views.py:268 +#, python-format +msgid "Error updating document indexes; %s" +msgstr "Errore nella'ggiornamento degli indici del documento; %s" + +#: views.py:109 views.py:270 +msgid "Document indexes updated successfully." +msgstr "Indici documento aggiornati con successo." + +#: views.py:120 +#, python-format +msgid "Edit metadata for document: %s" +msgstr "Modifica metadata per il documento: %s" + +#: views.py:122 +#, python-format +msgid "Edit metadata for documents: %s" +msgstr "Modifica metadata per i documenti: %s" + +#: views.py:161 +#, python-format +msgid "" +"Metadata type: %(metadata_type)s successfully added to document " +"%(document)s." +msgstr "" +"Tipo metadata: %(metadata_type)s aggiunto con successo al documento " +"%(document)s." + +#: views.py:164 +#, python-format +msgid "" +"Metadata type: %(metadata_type)s already present in document %(document)s." +msgstr "" +"Tipo Metadata: %(metadata_type)s già presente per il documento %(document)s." + +#: views.py:188 +#, python-format +msgid "Add metadata type to document: %s" +msgstr "Aggiungi tipo metadata al document: %s" + +#: views.py:190 +#, python-format +msgid "Add metadata type to documents: %s" +msgstr "Aggiungi tipo metadata ai documents: %s" + +#: views.py:259 +#, python-format +msgid "" +"Successfully remove metadata type: %(metadata_type)s from document: " +"%(document)s." +msgstr "" +"Rimuovere con successo tipo di metadati: %(metadata_type)s per il " +"documento: %(document)s." + +#: views.py:262 +#, python-format +msgid "" +"Error removing metadata type: %(metadata_type)s from document: %(document)s." +msgstr "" +"Errore durante la rimozione dei metadati di tipo: %(metadata_type)s per il " +"documento: %(document)s." + +#: views.py:281 +#, python-format +msgid "Remove metadata types from document: %s" +msgstr "Rimuovi il tipo metadata per il documento: %s" + +#: views.py:283 +#, python-format +msgid "Remove metadata types from documents: %s" +msgstr "Rimuovi il tipo metadata per il documenti: %s" + +#: views.py:302 +#, python-format +msgid "metadata for: %s" +msgstr "metadata per:%s" + +#: views.py:320 +msgid "internal name" +msgstr "nome interno" + +#: views.py:341 +msgid "Metadata type edited successfully" +msgstr "Tipo di metadata modificato con successo" + +#: views.py:344 +#, python-format +msgid "Error editing metadata type; %s" +msgstr "Errore nella modifica del tipo di metadata ; %s" + +#: views.py:350 +#, python-format +msgid "edit metadata type: %s" +msgstr "modifica tipo di metadata: %s" + +#: views.py:365 +msgid "Metadata type created successfully" +msgstr "Tipo metadata creato con successo" + +#: views.py:371 +msgid "create metadata type" +msgstr "create tipo di metadata" + +#: views.py:390 +#, python-format +msgid "Metadata type: %s deleted successfully." +msgstr "Tipo metadata:%s cancellato con successo." + +#: views.py:392 +#, python-format +msgid "Metadata type: %(metadata_type)s delete error: %(error)s" +msgstr "Tipo metadata: %(metadata_type)s erroce di cancellazione: %(error)s" + +#: views.py:403 +#, python-format +msgid "Are you sure you wish to delete the metadata type: %s?" +msgstr "Sei sicuro di voler cancellare il tipo di metadata: %s?" + +#: views.py:420 +msgid "members" +msgstr "membri" + +#: views.py:464 +#, python-format +msgid "non members of metadata set: %s" +msgstr "non membri del set di metadata:%s" + +#: views.py:465 +#, python-format +msgid "members of metadata set: %s" +msgstr "membri del set di metadata:%s" + +#: views.py:480 +msgid "Metadata set created successfully" +msgstr "Set di metadata creata con successo" + +#: views.py:486 +msgid "create metadata set" +msgstr "creazione del set di metadata" + +#: views.py:505 +#, python-format +msgid "Metadata set: %s deleted successfully." +msgstr "Set di metadata: %s cancellata con successo." + +#: views.py:508 +#, python-format +msgid "Metadata set: %(metadata_set)s delete error: %(error)s" +msgstr "Set di metadata: %(metadata_set)s errore di cancellazione: %(error)s" + +#: views.py:519 +#, python-format +msgid "Are you sure you wish to delete the metadata set: %s?" +msgstr "Sei sicuro di voler eliminare il set di metadati: %s?" + +#: views.py:538 views.py:556 +msgid "Metadata types" +msgstr "" + +#: views.py:594 +#, python-format +msgid "non members of document type: %s" +msgstr "non membri del tipo di documento: %s" + +#: views.py:595 +#, python-format +msgid "members of document type: %s" +msgstr "membri del tipo di documento: %s" + +#: templates/metadata_set_help.html:3 +msgid "What are metadata sets?" +msgstr "Cosa sono i set di metadati ?" + +#: templates/metadata_set_help.html:4 +msgid "" +"A metadata set is a group of one or more metadata types. Metadata sets are " +"useful when creating new documents; selecing a metadata set automatically " +"attaches it's member metadata types to said document." +msgstr "" +"Un insieme di metadati è un gruppo di uno o più tipi di metadati. Set di " +"metadati sono utili durante la creazione di nuovi documenti e, selezionando " +"un set di metadati allega automaticamente è membro tipi di metadati per " +"documentare detto." + +#: templates/metadata_type_help.html:3 +msgid "What are metadata types?" +msgstr "Cosa sono i tipi di metadati?" + +#: templates/metadata_type_help.html:4 +msgid "" +"A metadata type defines the characteristics of a value of some kind that can" +" be attached to a document. Examples of metadata types are: a client name, " +"a date, or a project to which several documents belong. A metadata type's " +"name is the internal identifier with which it can be referenced to by other " +"modules such as the indexing module, the title is the value that is shown to" +" the users, the default value is the value an instance of this metadata type" +" will have initially, and the lookup value turns an instance of a metadata " +"of this type into a choice list which options are the result of the lookup's" +" code execution." +msgstr "" +"Un tipo di metadati definisce le caratteristiche di un valore di qualche " +"tipo che può essere collegato a un documento. Esempi di tipi di metadati: il" +" nome del client, una data o un progetto a cui appartengono diversi " +"documenti. Il nome di un tipo di metadati è l'identificatore interno con il " +"quale possono essere pubblicati da altri moduli come il modulo di " +"indicizzazione, il titolo è il valore che viene mostrato agli utenti, il " +"valore predefinito è il valore di un'istanza di questo tipo di metadati avrà" +" inizialmente, e il valore di ricerca si trasforma un'istanza di metadati di" +" questo tipo in un elenco di opzioni di scelta che sono il risultato della " +"esecuzione di codice la ricerca di." + + diff --git a/apps/metadata/locale/pl/LC_MESSAGES/django.mo b/apps/metadata/locale/pl/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..2a866ba861 Binary files /dev/null and b/apps/metadata/locale/pl/LC_MESSAGES/django.mo differ diff --git a/apps/metadata/locale/pl/LC_MESSAGES/django.po b/apps/metadata/locale/pl/LC_MESSAGES/django.po new file mode 100644 index 0000000000..25e7b5a7be --- /dev/null +++ b/apps/metadata/locale/pl/LC_MESSAGES/django.po @@ -0,0 +1,455 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# , 2012. +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-20 17:15+0000\n" +"Last-Translator: mic \n" +"Language-Team: Polish (http://www.transifex.net/projects/p/mayan-edms/language/pl/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pl\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" + +#: __init__.py:21 __init__.py:23 +msgid "edit metadata" +msgstr "" + +#: __init__.py:22 +msgid "metadata" +msgstr "" + +#: __init__.py:24 __init__.py:25 +msgid "add metadata" +msgstr "" + +#: __init__.py:26 __init__.py:27 +msgid "remove metadata" +msgstr "" + +#: __init__.py:29 models.py:34 views.py:316 +msgid "metadata types" +msgstr "" + +#: __init__.py:30 __init__.py:35 +msgid "edit" +msgstr "" + +#: __init__.py:31 __init__.py:36 +msgid "delete" +msgstr "" + +#: __init__.py:32 __init__.py:37 +msgid "create new" +msgstr "" + +#: __init__.py:34 views.py:416 +msgid "metadata sets" +msgstr "" + +#: __init__.py:39 models.py:93 +msgid "default metadata" +msgstr "" + +#: classes.py:14 +#, python-format +msgid "'metadata' object has no attribute '%s'" +msgstr "" + +#: forms.py:28 +msgid "required" +msgstr "wymagany" + +#: forms.py:54 +msgid "id" +msgstr "" + +#: forms.py:55 +msgid "Name" +msgstr "Nazwa" + +#: forms.py:57 +msgid "Value" +msgstr "Wartość" + +#: forms.py:58 +msgid "Update" +msgstr "" + +#: forms.py:64 +msgid "Metadata type" +msgstr "" + +#: forms.py:68 +msgid "Remove" +msgstr "Usuń" + +#: forms.py:86 views.py:541 views.py:559 +msgid "Metadata sets" +msgstr "" + +#: forms.py:94 permissions.py:7 +msgid "Metadata" +msgstr "" + +#: models.py:10 +#, python-format +msgid " Available models: %s" +msgstr "" + +#: models.py:11 +#, python-format +msgid " Available functions: %s" +msgstr "" + +#: models.py:18 +msgid "name" +msgstr "nazwa" + +#: models.py:18 +msgid "Do not use python reserved words, or spaces." +msgstr "" + +#: models.py:19 models.py:41 +msgid "title" +msgstr "tytuł" + +#: models.py:21 +msgid "default" +msgstr "domyślne" + +#: models.py:22 +#, python-format +msgid "Enter a string to be evaluated.%s" +msgstr "" + +#: models.py:24 +msgid "lookup" +msgstr "" + +#: models.py:25 +#, python-format +msgid "" +"Enter a string to be evaluated. Example: [user.get_full_name() for user in " +"User.objects.all()].%s" +msgstr "" + +#: models.py:33 models.py:58 views.py:353 views.py:398 +msgid "metadata type" +msgstr "" + +#: models.py:48 models.py:49 models.py:57 views.py:468 views.py:514 +msgid "metadata set" +msgstr "" + +#: models.py:65 +msgid "metadata set item" +msgstr "" + +#: models.py:66 +msgid "metadata set items" +msgstr "" + +#: models.py:74 +msgid "document" +msgstr "dokument" + +#: models.py:75 +msgid "type" +msgstr "typ" + +#: models.py:76 views.py:304 +msgid "value" +msgstr "wartość" + +#: models.py:82 models.py:83 +msgid "document metadata" +msgstr "" + +#: models.py:91 views.py:599 +msgid "document type" +msgstr "" + +#: models.py:92 +msgid "default metadata sets" +msgstr "" + +#: models.py:99 +msgid "document type defaults" +msgstr "" + +#: models.py:100 +msgid "document types defaults" +msgstr "" + +#: permissions.py:8 +msgid "Edit a document's metadata" +msgstr "" + +#: permissions.py:9 +msgid "Add metadata to a document" +msgstr "" + +#: permissions.py:10 +msgid "Remove metadata from a document" +msgstr "" + +#: permissions.py:11 +msgid "View metadata from a document" +msgstr "" + +#: permissions.py:13 +msgid "Metadata setup" +msgstr "" + +#: permissions.py:14 +msgid "Edit metadata types" +msgstr "" + +#: permissions.py:15 +msgid "Create new metadata types" +msgstr "" + +#: permissions.py:16 +msgid "Delete metadata types" +msgstr "" + +#: permissions.py:17 +msgid "View metadata types" +msgstr "" + +#: permissions.py:19 +msgid "Edit metadata sets" +msgstr "" + +#: permissions.py:20 +msgid "Create new metadata sets" +msgstr "" + +#: permissions.py:21 +msgid "Delete metadata sets" +msgstr "" + +#: permissions.py:22 +msgid "View metadata sets" +msgstr "" + +#: views.py:41 views.py:204 +msgid "The selected document doesn't have any metadata." +msgstr "" + +#: views.py:52 views.py:144 views.py:216 +msgid "Must provide at least one document." +msgstr "" + +#: views.py:87 views.py:251 +#, python-format +msgid "Error deleting document indexes; %s" +msgstr "" + +#: views.py:99 +#, python-format +msgid "Error editing metadata for document %(document)s; %(error)s." +msgstr "" + +#: views.py:102 +#, python-format +msgid "Metadata for document %s edited successfully." +msgstr "" + +#: views.py:107 views.py:268 +#, python-format +msgid "Error updating document indexes; %s" +msgstr "" + +#: views.py:109 views.py:270 +msgid "Document indexes updated successfully." +msgstr "" + +#: views.py:120 +#, python-format +msgid "Edit metadata for document: %s" +msgstr "" + +#: views.py:122 +#, python-format +msgid "Edit metadata for documents: %s" +msgstr "" + +#: views.py:161 +#, python-format +msgid "" +"Metadata type: %(metadata_type)s successfully added to document " +"%(document)s." +msgstr "" + +#: views.py:164 +#, python-format +msgid "" +"Metadata type: %(metadata_type)s already present in document %(document)s." +msgstr "" + +#: views.py:188 +#, python-format +msgid "Add metadata type to document: %s" +msgstr "" + +#: views.py:190 +#, python-format +msgid "Add metadata type to documents: %s" +msgstr "" + +#: views.py:259 +#, python-format +msgid "" +"Successfully remove metadata type: %(metadata_type)s from document: " +"%(document)s." +msgstr "" + +#: views.py:262 +#, python-format +msgid "" +"Error removing metadata type: %(metadata_type)s from document: %(document)s." +msgstr "" + +#: views.py:281 +#, python-format +msgid "Remove metadata types from document: %s" +msgstr "" + +#: views.py:283 +#, python-format +msgid "Remove metadata types from documents: %s" +msgstr "" + +#: views.py:302 +#, python-format +msgid "metadata for: %s" +msgstr "" + +#: views.py:320 +msgid "internal name" +msgstr "" + +#: views.py:341 +msgid "Metadata type edited successfully" +msgstr "" + +#: views.py:344 +#, python-format +msgid "Error editing metadata type; %s" +msgstr "" + +#: views.py:350 +#, python-format +msgid "edit metadata type: %s" +msgstr "" + +#: views.py:365 +msgid "Metadata type created successfully" +msgstr "" + +#: views.py:371 +msgid "create metadata type" +msgstr "" + +#: views.py:390 +#, python-format +msgid "Metadata type: %s deleted successfully." +msgstr "" + +#: views.py:392 +#, python-format +msgid "Metadata type: %(metadata_type)s delete error: %(error)s" +msgstr "" + +#: views.py:403 +#, python-format +msgid "Are you sure you wish to delete the metadata type: %s?" +msgstr "" + +#: views.py:420 +msgid "members" +msgstr "" + +#: views.py:464 +#, python-format +msgid "non members of metadata set: %s" +msgstr "" + +#: views.py:465 +#, python-format +msgid "members of metadata set: %s" +msgstr "" + +#: views.py:480 +msgid "Metadata set created successfully" +msgstr "" + +#: views.py:486 +msgid "create metadata set" +msgstr "" + +#: views.py:505 +#, python-format +msgid "Metadata set: %s deleted successfully." +msgstr "" + +#: views.py:508 +#, python-format +msgid "Metadata set: %(metadata_set)s delete error: %(error)s" +msgstr "" + +#: views.py:519 +#, python-format +msgid "Are you sure you wish to delete the metadata set: %s?" +msgstr "" + +#: views.py:538 views.py:556 +msgid "Metadata types" +msgstr "" + +#: views.py:594 +#, python-format +msgid "non members of document type: %s" +msgstr "" + +#: views.py:595 +#, python-format +msgid "members of document type: %s" +msgstr "" + +#: templates/metadata_set_help.html:3 +msgid "What are metadata sets?" +msgstr "" + +#: templates/metadata_set_help.html:4 +msgid "" +"A metadata set is a group of one or more metadata types. Metadata sets are " +"useful when creating new documents; selecing a metadata set automatically " +"attaches it's member metadata types to said document." +msgstr "" + +#: templates/metadata_type_help.html:3 +msgid "What are metadata types?" +msgstr "" + +#: templates/metadata_type_help.html:4 +msgid "" +"A metadata type defines the characteristics of a value of some kind that can" +" be attached to a document. Examples of metadata types are: a client name, " +"a date, or a project to which several documents belong. A metadata type's " +"name is the internal identifier with which it can be referenced to by other " +"modules such as the indexing module, the title is the value that is shown to" +" the users, the default value is the value an instance of this metadata type" +" will have initially, and the lookup value turns an instance of a metadata " +"of this type into a choice list which options are the result of the lookup's" +" code execution." +msgstr "" diff --git a/apps/metadata/locale/pt/LC_MESSAGES/django.mo b/apps/metadata/locale/pt/LC_MESSAGES/django.mo index ad93c5f075..e2f2d544c3 100644 Binary files a/apps/metadata/locale/pt/LC_MESSAGES/django.mo and b/apps/metadata/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/metadata/locale/pt/LC_MESSAGES/django.po b/apps/metadata/locale/pt/LC_MESSAGES/django.po index 4d7805cb79..bbf8bee6ad 100644 --- a/apps/metadata/locale/pt/LC_MESSAGES/django.po +++ b/apps/metadata/locale/pt/LC_MESSAGES/django.po @@ -1,122 +1,65 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: # , 2011. # Renata Oliveira , 2011. msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" -"PO-Revision-Date: 2011-11-04 00:42+0000\n" -"Last-Translator: emersonsoares \n" -"Language-Team: Portuguese (http://www.transifex.net/projects/p/mayan-edms/" -"team/pt/)\n" -"Language: pt\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-12 19:24+0000\n" +"Last-Translator: Roberto Rosario \n" +"Language-Team: Portuguese (http://www.transifex.net/projects/p/mayan-edms/team/pt/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +"Language: pt\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -#: __init__.py:12 -msgid "Edit a document's metadata" -msgstr "Editar metadados de um documento" - -#: __init__.py:13 -msgid "Add metadata to a document" -msgstr "Adicionar metadados a um documento" - -#: __init__.py:14 -msgid "Remove metadata from a document" -msgstr "Remover metadados de um documento" - -#: __init__.py:15 -msgid "View metadata from a document" -msgstr "Ver os metadados de um documento" - -#: __init__.py:17 -msgid "Edit metadata types" -msgstr "Editar tipos de metadados" - -#: __init__.py:18 -msgid "Create new metadata types" -msgstr "Criar novos tipos de metadados" - -#: __init__.py:19 -msgid "Delete metadata types" -msgstr "Excluir tipos de metadados" - -#: __init__.py:20 -msgid "View metadata types" -msgstr "Ver tipos de metadados" - -#: __init__.py:22 -msgid "Edit metadata sets" -msgstr "Editar conjuntos de metadados" - -#: __init__.py:23 -msgid "Create new metadata sets" -msgstr "Criar novos conjuntos de metadados" - -#: __init__.py:24 -msgid "Delete metadata sets" -msgstr "Excluir conjuntos de metadados" - -#: __init__.py:25 -msgid "View metadata sets" -msgstr "Ver conjuntos de metadados" - -#: __init__.py:27 forms.py:94 -msgid "Metadata" -msgstr "Metadados" - -#: __init__.py:33 -msgid "Metadata setup" -msgstr "Configuração de metadados" - -#: __init__.py:44 __init__.py:46 +#: __init__.py:21 __init__.py:23 msgid "edit metadata" msgstr "editar metadados" -#: __init__.py:45 +#: __init__.py:22 msgid "metadata" msgstr "metadados" -#: __init__.py:47 __init__.py:48 +#: __init__.py:24 __init__.py:25 msgid "add metadata" msgstr "adicionar metadados" -#: __init__.py:49 __init__.py:50 +#: __init__.py:26 __init__.py:27 msgid "remove metadata" msgstr "remover metadados" -#: __init__.py:52 models.py:33 views.py:294 +#: __init__.py:29 models.py:34 views.py:316 msgid "metadata types" msgstr "tipos de metadados" -#: __init__.py:53 __init__.py:58 +#: __init__.py:30 __init__.py:35 msgid "edit" msgstr "editar" -#: __init__.py:54 __init__.py:59 +#: __init__.py:31 __init__.py:36 msgid "delete" msgstr "excluir" -#: __init__.py:55 __init__.py:60 +#: __init__.py:32 __init__.py:37 msgid "create new" msgstr "criar novo" -#: __init__.py:57 views.py:394 +#: __init__.py:34 views.py:416 msgid "metadata sets" msgstr "conjuntos de metadados" -#: __init__.py:62 models.py:92 +#: __init__.py:39 models.py:93 msgid "default metadata" msgstr "metadados padrão" -#: classes.py:12 +#: classes.py:14 #, python-format msgid "'metadata' object has no attribute '%s'" msgstr "Objeto 'metadados' tem nenhum atributo '%s'" @@ -149,46 +92,50 @@ msgstr "Tipo de metadados" msgid "Remove" msgstr "Remover" -#: forms.py:86 +#: forms.py:86 views.py:541 views.py:559 msgid "Metadata sets" msgstr "Conjuntos de metadados" -#: models.py:9 +#: forms.py:94 permissions.py:7 +msgid "Metadata" +msgstr "Metadados" + +#: models.py:10 #, python-format msgid " Available models: %s" msgstr "Modelos disponíveis: %s" -#: models.py:10 +#: models.py:11 #, python-format msgid " Available functions: %s" msgstr "Funções disponíveis: %s" -#: models.py:17 +#: models.py:18 msgid "name" msgstr "nome" -#: models.py:17 +#: models.py:18 msgid "Do not use python reserved words, or spaces." msgstr "Não use palavras reservadas python, ou espaços." -#: models.py:18 models.py:40 +#: models.py:19 models.py:41 msgid "title" msgstr "título" -#: models.py:20 +#: models.py:21 msgid "default" msgstr "padrão" -#: models.py:21 +#: models.py:22 #, python-format msgid "Enter a string to be evaluated.%s" msgstr "Digite uma expressão a ser avaliada. %s" -#: models.py:23 +#: models.py:24 msgid "lookup" msgstr "pesquisa" -#: models.py:24 +#: models.py:25 #, python-format msgid "" "Enter a string to be evaluated. Example: [user.get_full_name() for user in " @@ -197,122 +144,176 @@ msgstr "" "Digite uma expressão a ser avaliada. Exemplo: [user.get_full_name()para o " "usuário em User.objects.all()]. %s" -#: models.py:32 models.py:57 views.py:331 views.py:376 +#: models.py:33 models.py:58 views.py:353 views.py:398 msgid "metadata type" msgstr "tipo de metadados" -#: models.py:47 models.py:48 models.py:56 views.py:446 views.py:491 +#: models.py:48 models.py:49 models.py:57 views.py:468 views.py:514 msgid "metadata set" msgstr "conjunto de metadados" -#: models.py:64 +#: models.py:65 msgid "metadata set item" msgstr "conjunto de item de metadados" -#: models.py:65 +#: models.py:66 msgid "metadata set items" msgstr "conjunto de itens de metadados" -#: models.py:73 +#: models.py:74 msgid "document" msgstr "documento" -#: models.py:74 +#: models.py:75 msgid "type" msgstr "tipo" -#: models.py:75 views.py:283 +#: models.py:76 views.py:304 msgid "value" msgstr "valor" -#: models.py:81 models.py:82 +#: models.py:82 models.py:83 msgid "document metadata" msgstr "metadados do documento" -#: models.py:90 views.py:559 +#: models.py:91 views.py:599 msgid "document type" msgstr "tipo de documento" -#: models.py:91 +#: models.py:92 msgid "default metadata sets" msgstr "padrão de metadados definidos" -#: models.py:98 +#: models.py:99 msgid "document type defaults" msgstr "padrões de tipo de documento" -#: models.py:99 +#: models.py:100 msgid "document types defaults" msgstr "padrões de tipos de documentos" -#: views.py:38 views.py:193 +#: permissions.py:8 +msgid "Edit a document's metadata" +msgstr "Editar metadados de um documento" + +#: permissions.py:9 +msgid "Add metadata to a document" +msgstr "Adicionar metadados a um documento" + +#: permissions.py:10 +msgid "Remove metadata from a document" +msgstr "Remover metadados de um documento" + +#: permissions.py:11 +msgid "View metadata from a document" +msgstr "Ver os metadados de um documento" + +#: permissions.py:13 +msgid "Metadata setup" +msgstr "Configuração de metadados" + +#: permissions.py:14 +msgid "Edit metadata types" +msgstr "Editar tipos de metadados" + +#: permissions.py:15 +msgid "Create new metadata types" +msgstr "Criar novos tipos de metadados" + +#: permissions.py:16 +msgid "Delete metadata types" +msgstr "Excluir tipos de metadados" + +#: permissions.py:17 +msgid "View metadata types" +msgstr "Ver tipos de metadados" + +#: permissions.py:19 +msgid "Edit metadata sets" +msgstr "Editar conjuntos de metadados" + +#: permissions.py:20 +msgid "Create new metadata sets" +msgstr "Criar novos conjuntos de metadados" + +#: permissions.py:21 +msgid "Delete metadata sets" +msgstr "Excluir conjuntos de metadados" + +#: permissions.py:22 +msgid "View metadata sets" +msgstr "Ver conjuntos de metadados" + +#: views.py:41 views.py:204 msgid "The selected document doesn't have any metadata." msgstr "O documento selecionado não tem qualquer metadado." -#: views.py:43 views.py:131 views.py:199 +#: views.py:52 views.py:144 views.py:216 msgid "Must provide at least one document." msgstr "Deve fornecer pelo menos um documento." -#: views.py:78 views.py:234 +#: views.py:87 views.py:251 #, python-format msgid "Error deleting document indexes; %s" msgstr "Erro ao excluir índices de documento; %s" -#: views.py:90 +#: views.py:99 #, python-format msgid "Error editing metadata for document %(document)s; %(error)s." msgstr "Erro de edição de metadados para documento %(document)s; %(error)s." -#: views.py:93 +#: views.py:102 #, python-format msgid "Metadata for document %s edited successfully." msgstr "Metadados para o documento %s alterados com sucesso." -#: views.py:98 views.py:251 +#: views.py:107 views.py:268 #, python-format msgid "Error updating document indexes; %s" msgstr "Erro ao atualizar índices de documento; %s" -#: views.py:100 views.py:253 +#: views.py:109 views.py:270 msgid "Document indexes updated successfully." msgstr "Índices de documento atualizados com sucesso. " -#: views.py:111 +#: views.py:120 #, python-format msgid "Edit metadata for document: %s" msgstr "Editar os metadados do documento: %s" -#: views.py:113 +#: views.py:122 #, python-format msgid "Edit metadata for documents: %s" msgstr "Editar os metadados do documentos: %s" -#: views.py:148 +#: views.py:161 #, python-format msgid "" -"Metadata type: %(metadata_type)s successfully added to document %(document)s." +"Metadata type: %(metadata_type)s successfully added to document " +"%(document)s." msgstr "" "Tipo de metadados: %(metadata_type)s adicionado com sucesso para documento " "%(document)s." -#: views.py:151 +#: views.py:164 #, python-format msgid "" "Metadata type: %(metadata_type)s already present in document %(document)s." msgstr "" -"Tipo de metadados: %(metadata_type)s já presente no documento %(document)s ." +"Tipo de metadados: %(metadata_type)s já presente no documento %(document)s " +"." -#: views.py:175 +#: views.py:188 #, python-format msgid "Add metadata type to document: %s" msgstr "Adicionar tipo de metadados ao documento: %s" -#: views.py:177 +#: views.py:190 #, python-format msgid "Add metadata type to documents: %s" msgstr "Adicionar tipo de metadados aos documentos: %s" -#: views.py:242 +#: views.py:259 #, python-format msgid "" "Successfully remove metadata type: %(metadata_type)s from document: " @@ -321,7 +322,7 @@ msgstr "" "Tipos de metadados removidos com êxito: %(metadata_type)s do documento: " "%(document)s." -#: views.py:245 +#: views.py:262 #, python-format msgid "" "Error removing metadata type: %(metadata_type)s from document: %(document)s." @@ -329,105 +330,109 @@ msgstr "" "Erro ao remover tipo de metadados: %(metadata_type)s do documento: " "%(document)s." -#: views.py:264 +#: views.py:281 #, python-format msgid "Remove metadata types from document: %s" msgstr "Remover tipos de metadados do documento: %s" -#: views.py:266 +#: views.py:283 #, python-format msgid "Remove metadata types from documents: %s" msgstr "Remover tipos de metadados dos documentos: %s" -#: views.py:281 +#: views.py:302 #, python-format msgid "metadata for: %s" msgstr "metadados para: %s" -#: views.py:298 +#: views.py:320 msgid "internal name" msgstr "nome interno" -#: views.py:319 +#: views.py:341 msgid "Metadata type edited successfully" msgstr "Tipo de metadados editados com sucesso" -#: views.py:322 +#: views.py:344 #, python-format msgid "Error editing metadata type; %s" msgstr "Erro de edição de tipo de metadados; %s" -#: views.py:328 +#: views.py:350 #, python-format msgid "edit metadata type: %s" msgstr "editar tipo de metadados: %s" -#: views.py:343 +#: views.py:365 msgid "Metadata type created successfully" msgstr "Tipo de metadados criado com sucesso" -#: views.py:349 +#: views.py:371 msgid "create metadata type" msgstr "criar um tipo de metadados" -#: views.py:368 +#: views.py:390 #, python-format msgid "Metadata type: %s deleted successfully." msgstr "Tipo de metadados: %s removido com sucesso." -#: views.py:370 +#: views.py:392 #, python-format msgid "Metadata type: %(metadata_type)s delete error: %(error)s" msgstr "Tipo de metadados: %(metadata_type)s erro ao deletar: %(error)s" -#: views.py:381 +#: views.py:403 #, python-format msgid "Are you sure you wish to delete the metadata type: %s?" msgstr "Tem certeza de que deseja excluir o tipo de metadados: %s?" -#: views.py:398 +#: views.py:420 msgid "members" msgstr "membros" -#: views.py:442 +#: views.py:464 #, python-format msgid "non members of metadata set: %s" msgstr "não-membros do conjunto de metadados: %s" -#: views.py:443 +#: views.py:465 #, python-format msgid "members of metadata set: %s" msgstr "membros do conjunto de metadados: %s" -#: views.py:458 +#: views.py:480 msgid "Metadata set created successfully" msgstr "Conjunto de metadados criado com sucesso" -#: views.py:464 +#: views.py:486 msgid "create metadata set" msgstr "criar um conjunto de metadados" -#: views.py:483 +#: views.py:505 #, python-format msgid "Metadata set: %s deleted successfully." msgstr "Conjunto de metadados: %s removido com sucesso." -#: views.py:485 +#: views.py:508 #, python-format msgid "Metadata set: %(metadata_set)s delete error: %(error)s" msgstr "Conjunto de metadados: %(metadata_set)s erro ao deletar: %(error)s " -#: views.py:496 +#: views.py:519 #, python-format msgid "Are you sure you wish to delete the metadata set: %s?" msgstr "Tem certeza de que deseja excluir o conjunto de metadados: %s?" -#: views.py:554 +#: views.py:538 views.py:556 +msgid "Metadata types" +msgstr "" + +#: views.py:594 #, python-format msgid "non members of document type: %s" msgstr "não membros do tipo de documento: %s" -#: views.py:555 +#: views.py:595 #, python-format msgid "members of document type: %s" msgstr "membros do tipo de documento: %s" @@ -453,22 +458,24 @@ msgstr "Quais são os tipos de metadados?" #: templates/metadata_type_help.html:4 msgid "" -"A metadata type defines the characteristics of a value of some kind that can " -"be attached to a document. Examples of metadata types are: a client name, a " -"date, or a project to which several documents belong. A metadata type's " +"A metadata type defines the characteristics of a value of some kind that can" +" be attached to a document. Examples of metadata types are: a client name, " +"a date, or a project to which several documents belong. A metadata type's " "name is the internal identifier with which it can be referenced to by other " -"modules such as the indexing module, the title is the value that is shown to " -"the users, the default value is the value an instance of this metadata type " -"will have initially, and the lookup value turns an instance of a metadata of " -"this type into a choice list which options are the result of the lookup's " -"code execution." +"modules such as the indexing module, the title is the value that is shown to" +" the users, the default value is the value an instance of this metadata type" +" will have initially, and the lookup value turns an instance of a metadata " +"of this type into a choice list which options are the result of the lookup's" +" code execution." msgstr "" -"Um tipo de metadados define as características de um valor de algum tipo que " -"pode ser anexado a um documento. Exemplos de tipos de metadados são: um nome " -"de cliente, uma data ou um projeto ao qual pertencem vários documentos. O " -"nome de um tipo de metadados é o identificador interno com o qual ele pode " -"ser referenciado pelos outros módulos, como o módulo de indexação, o título " -"é o valor que é mostrado para os usuários, o valor padrão é o valor de uma " -"instância desse tipo de metadados terá inicialmente, o valor de pesquisa e " -"vira um exemplo de um metadados deste tipo em uma lista de escolha de quais " -"opções são o resultado da execução a pesquisa de código." +"Um tipo de metadados define as características de um valor de algum tipo que" +" pode ser anexado a um documento. Exemplos de tipos de metadados são: um " +"nome de cliente, uma data ou um projeto ao qual pertencem vários documentos." +" O nome de um tipo de metadados é o identificador interno com o qual ele " +"pode ser referenciado pelos outros módulos, como o módulo de indexação, o " +"título é o valor que é mostrado para os usuários, o valor padrão é o valor " +"de uma instância desse tipo de metadados terá inicialmente, o valor de " +"pesquisa e vira um exemplo de um metadados deste tipo em uma lista de " +"escolha de quais opções são o resultado da execução a pesquisa de código." + + diff --git a/apps/metadata/locale/ru/LC_MESSAGES/django.mo b/apps/metadata/locale/ru/LC_MESSAGES/django.mo index d58dc848fd..c0c8184351 100644 Binary files a/apps/metadata/locale/ru/LC_MESSAGES/django.mo and b/apps/metadata/locale/ru/LC_MESSAGES/django.mo differ diff --git a/apps/metadata/locale/ru/LC_MESSAGES/django.po b/apps/metadata/locale/ru/LC_MESSAGES/django.po index bb28056cda..851c2f577b 100644 --- a/apps/metadata/locale/ru/LC_MESSAGES/django.po +++ b/apps/metadata/locale/ru/LC_MESSAGES/django.po @@ -1,122 +1,64 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: -# Sergey Glita , 2011. +# Sergey Glita , 2011, 2012. msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" -"PO-Revision-Date: 2011-11-04 10:20+0000\n" -"Last-Translator: gsv70 \n" -"Language-Team: Russian (http://www.transifex.net/projects/p/mayan-edms/team/" -"ru/)\n" -"Language: ru\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-27 04:25+0000\n" +"Last-Translator: Sergey Glita \n" +"Language-Team: Russian (http://www.transifex.net/projects/p/mayan-edms/language/ru/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" -"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" +"Language: ru\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:12 -msgid "Edit a document's metadata" -msgstr "Редактирование метаданные документа" - -#: __init__.py:13 -msgid "Add metadata to a document" -msgstr "Добавить метаданные в документ" - -#: __init__.py:14 -msgid "Remove metadata from a document" -msgstr "Удаление метаданных из документа" - -#: __init__.py:15 -msgid "View metadata from a document" -msgstr "Просмотр метаданных из документа" - -#: __init__.py:17 -msgid "Edit metadata types" -msgstr "Редактировать типы метаданных" - -#: __init__.py:18 -msgid "Create new metadata types" -msgstr "Создание новых типов метаданных" - -#: __init__.py:19 -msgid "Delete metadata types" -msgstr "Удаление типов метаданных" - -#: __init__.py:20 -msgid "View metadata types" -msgstr "Просмотр типов метаданных" - -#: __init__.py:22 -msgid "Edit metadata sets" -msgstr "Редактирование наборов метаданных" - -#: __init__.py:23 -msgid "Create new metadata sets" -msgstr "Создание новых наборов метаданных" - -#: __init__.py:24 -msgid "Delete metadata sets" -msgstr "Удаление наборов метаданных" - -#: __init__.py:25 -msgid "View metadata sets" -msgstr "Просмотр наборов метаданных" - -#: __init__.py:27 forms.py:94 -msgid "Metadata" -msgstr "Метаданные" - -#: __init__.py:33 -msgid "Metadata setup" -msgstr "Настройки метаданных " - -#: __init__.py:44 __init__.py:46 +#: __init__.py:21 __init__.py:23 msgid "edit metadata" msgstr "редактировать метаданные" -#: __init__.py:45 +#: __init__.py:22 msgid "metadata" msgstr "метаданные" -#: __init__.py:47 __init__.py:48 +#: __init__.py:24 __init__.py:25 msgid "add metadata" msgstr "добавить метаданные" -#: __init__.py:49 __init__.py:50 +#: __init__.py:26 __init__.py:27 msgid "remove metadata" msgstr "удалить метаданные" -#: __init__.py:52 models.py:33 views.py:294 +#: __init__.py:29 models.py:34 views.py:316 msgid "metadata types" msgstr "типы метаданных" -#: __init__.py:53 __init__.py:58 +#: __init__.py:30 __init__.py:35 msgid "edit" msgstr "редактировать" -#: __init__.py:54 __init__.py:59 +#: __init__.py:31 __init__.py:36 msgid "delete" msgstr "удалить" -#: __init__.py:55 __init__.py:60 +#: __init__.py:32 __init__.py:37 msgid "create new" msgstr "создание новых" -#: __init__.py:57 views.py:394 +#: __init__.py:34 views.py:416 msgid "metadata sets" msgstr "наборы метаданных" -#: __init__.py:62 models.py:92 +#: __init__.py:39 models.py:93 msgid "default metadata" msgstr "метаданные по умолчанию" -#: classes.py:12 +#: classes.py:14 #, python-format msgid "'metadata' object has no attribute '%s'" msgstr "объект метаданных не имеет аттрибута '%s'" @@ -149,284 +91,337 @@ msgstr "Тип метаданных" msgid "Remove" msgstr "Удалить" -#: forms.py:86 +#: forms.py:86 views.py:541 views.py:559 msgid "Metadata sets" msgstr "Наборы метаданных" -#: models.py:9 +#: forms.py:94 permissions.py:7 +msgid "Metadata" +msgstr "Метаданные" + +#: models.py:10 #, python-format msgid " Available models: %s" msgstr "Доступные модели: %s" -#: models.py:10 +#: models.py:11 #, python-format msgid " Available functions: %s" msgstr "Доступные функции:%s." -#: models.py:17 +#: models.py:18 msgid "name" msgstr "имя" -#: models.py:17 +#: models.py:18 msgid "Do not use python reserved words, or spaces." msgstr "Не используйте зарезервированные слова python, или пробелы." -#: models.py:18 models.py:40 +#: models.py:19 models.py:41 msgid "title" msgstr "название" -#: models.py:20 +#: models.py:21 msgid "default" msgstr "умолчание" -#: models.py:21 +#: models.py:22 #, python-format msgid "Enter a string to be evaluated.%s" msgstr "Введите строку для вычисления %s" -#: models.py:23 +#: models.py:24 msgid "lookup" msgstr "поиск" -#: models.py:24 +#: models.py:25 #, python-format msgid "" "Enter a string to be evaluated. Example: [user.get_full_name() for user in " "User.objects.all()].%s" -msgstr "" -"Введите строку для вычисления. Пример: [user.get_full_name() for user in " -"User.objects.all()].%s" +msgstr "Введите строку для вычисления. Пример: [user.get_full_name() for user in User.objects.all()].%s" -#: models.py:32 models.py:57 views.py:331 views.py:376 +#: models.py:33 models.py:58 views.py:353 views.py:398 msgid "metadata type" msgstr "тип метаданных" -#: models.py:47 models.py:48 models.py:56 views.py:446 views.py:491 +#: models.py:48 models.py:49 models.py:57 views.py:468 views.py:514 msgid "metadata set" msgstr "набор метаданных" -#: models.py:64 +#: models.py:65 msgid "metadata set item" msgstr "элемент набора метаданных" -#: models.py:65 +#: models.py:66 msgid "metadata set items" msgstr "элементы набора метаданных" -#: models.py:73 +#: models.py:74 msgid "document" msgstr "документ" -#: models.py:74 +#: models.py:75 msgid "type" msgstr "тип" -#: models.py:75 views.py:283 +#: models.py:76 views.py:304 msgid "value" msgstr "значение" -#: models.py:81 models.py:82 +#: models.py:82 models.py:83 msgid "document metadata" msgstr "метаданные документа" -#: models.py:90 views.py:559 +#: models.py:91 views.py:599 msgid "document type" msgstr "тип документа" -#: models.py:91 +#: models.py:92 msgid "default metadata sets" msgstr "набор метаданных по умолчанию" -#: models.py:98 +#: models.py:99 msgid "document type defaults" msgstr "умолчания для типа документа" -#: models.py:99 +#: models.py:100 msgid "document types defaults" msgstr "тип документа по умолчанию " -#: views.py:38 views.py:193 +#: permissions.py:8 +msgid "Edit a document's metadata" +msgstr "Редактирование метаданные документа" + +#: permissions.py:9 +msgid "Add metadata to a document" +msgstr "Добавить метаданные в документ" + +#: permissions.py:10 +msgid "Remove metadata from a document" +msgstr "Удаление метаданных из документа" + +#: permissions.py:11 +msgid "View metadata from a document" +msgstr "Просмотр метаданных из документа" + +#: permissions.py:13 +msgid "Metadata setup" +msgstr "Настройки метаданных " + +#: permissions.py:14 +msgid "Edit metadata types" +msgstr "Редактировать типы метаданных" + +#: permissions.py:15 +msgid "Create new metadata types" +msgstr "Создание новых типов метаданных" + +#: permissions.py:16 +msgid "Delete metadata types" +msgstr "Удаление типов метаданных" + +#: permissions.py:17 +msgid "View metadata types" +msgstr "Просмотр типов метаданных" + +#: permissions.py:19 +msgid "Edit metadata sets" +msgstr "Редактирование наборов метаданных" + +#: permissions.py:20 +msgid "Create new metadata sets" +msgstr "Создание новых наборов метаданных" + +#: permissions.py:21 +msgid "Delete metadata sets" +msgstr "Удаление наборов метаданных" + +#: permissions.py:22 +msgid "View metadata sets" +msgstr "Просмотр наборов метаданных" + +#: views.py:41 views.py:204 msgid "The selected document doesn't have any metadata." msgstr "Выбранный документ не имеет метаданных." -#: views.py:43 views.py:131 views.py:199 +#: views.py:52 views.py:144 views.py:216 msgid "Must provide at least one document." msgstr "Необходимо предоставить хотя бы один документ." -#: views.py:78 views.py:234 +#: views.py:87 views.py:251 #, python-format msgid "Error deleting document indexes; %s" msgstr "Ошибка при удалении индексов документа; %s" -#: views.py:90 +#: views.py:99 #, python-format msgid "Error editing metadata for document %(document)s; %(error)s." -msgstr "" -"Ошибка редактирования метаданных для документа %(document)s; %(error)s." +msgstr "Ошибка редактирования метаданных для документа %(document)s; %(error)s." -#: views.py:93 +#: views.py:102 #, python-format msgid "Metadata for document %s edited successfully." msgstr "Метаданные для документов %s изменены." -#: views.py:98 views.py:251 +#: views.py:107 views.py:268 #, python-format msgid "Error updating document indexes; %s" msgstr "Ошибка при обновлении индексов документа; %s" -#: views.py:100 views.py:253 +#: views.py:109 views.py:270 msgid "Document indexes updated successfully." msgstr "Индексы документа успешно обновлены." -#: views.py:111 +#: views.py:120 #, python-format msgid "Edit metadata for document: %s" msgstr "Редактировать метаданные документа:%s." -#: views.py:113 +#: views.py:122 #, python-format msgid "Edit metadata for documents: %s" msgstr "Редактирование метаданных для документов: %s" -#: views.py:148 +#: views.py:161 #, python-format msgid "" -"Metadata type: %(metadata_type)s successfully added to document %(document)s." -msgstr "" -"Тип метаданных: %(metadata_type)s успешно добавлены к документу %(document)s." +"Metadata type: %(metadata_type)s successfully added to document " +"%(document)s." +msgstr "Тип метаданных: %(metadata_type)s успешно добавлены к документу %(document)s." -#: views.py:151 +#: views.py:164 #, python-format msgid "" "Metadata type: %(metadata_type)s already present in document %(document)s." msgstr "Тип метаданных: %(metadata_type)s уже есть в документе %(document)s." -#: views.py:175 +#: views.py:188 #, python-format msgid "Add metadata type to document: %s" msgstr "Добавить метаданные типа к документу: %s." -#: views.py:177 +#: views.py:190 #, python-format msgid "Add metadata type to documents: %s" msgstr "Добавляйте метаданные типа документов: %s" -#: views.py:242 +#: views.py:259 #, python-format msgid "" "Successfully remove metadata type: %(metadata_type)s from document: " "%(document)s." -msgstr "" -"Метаданные типа: %(metadata_type)s успешно удалены из документа: " -"%(document)s." +msgstr "Метаданные типа: %(metadata_type)s успешно удалены из документа: %(document)s." -#: views.py:245 +#: views.py:262 #, python-format msgid "" "Error removing metadata type: %(metadata_type)s from document: %(document)s." -msgstr "" -"Ошибка удаления метаданных, наберите:%(metadata_type)s из документа: " -"%(document)s ." +msgstr "Ошибка удаления метаданных, наберите:%(metadata_type)s из документа: %(document)s ." -#: views.py:264 +#: views.py:281 #, python-format msgid "Remove metadata types from document: %s" msgstr "Удалить типы метаданных из документа: %s" -#: views.py:266 +#: views.py:283 #, python-format msgid "Remove metadata types from documents: %s" msgstr "Удалить типы метаданных из документа: %s" -#: views.py:281 +#: views.py:302 #, python-format msgid "metadata for: %s" msgstr "метаданных для: %s" -#: views.py:298 +#: views.py:320 msgid "internal name" msgstr "внутреннее имя" -#: views.py:319 +#: views.py:341 msgid "Metadata type edited successfully" msgstr "Тип метаданных отредактирован." -#: views.py:322 +#: views.py:344 #, python-format msgid "Error editing metadata type; %s" msgstr "Ошибка редактирования типа метаданных; %s" -#: views.py:328 +#: views.py:350 #, python-format msgid "edit metadata type: %s" msgstr "редактировать метаданные типа: %s" -#: views.py:343 +#: views.py:365 msgid "Metadata type created successfully" msgstr "Тип метаданных успешно создан" -#: views.py:349 +#: views.py:371 msgid "create metadata type" msgstr "создать тип метаданных" -#: views.py:368 +#: views.py:390 #, python-format msgid "Metadata type: %s deleted successfully." msgstr "Тип метаданных: %s успешно удален." -#: views.py:370 +#: views.py:392 #, python-format msgid "Metadata type: %(metadata_type)s delete error: %(error)s" msgstr "Метаданные типа: %(metadata_type)s ошибка удаления: %(error)s" -#: views.py:381 +#: views.py:403 #, python-format msgid "Are you sure you wish to delete the metadata type: %s?" msgstr "Вы действительно хотите удалить метаданные:%s?" -#: views.py:398 +#: views.py:420 msgid "members" msgstr "элементы" -#: views.py:442 +#: views.py:464 #, python-format msgid "non members of metadata set: %s" msgstr "не входят в набор метаданных: %s" -#: views.py:443 +#: views.py:465 #, python-format msgid "members of metadata set: %s" msgstr "входят в набор метаданных: %s" -#: views.py:458 +#: views.py:480 msgid "Metadata set created successfully" msgstr "Набор метаданных создан" -#: views.py:464 +#: views.py:486 msgid "create metadata set" msgstr "создать набор метаданных" -#: views.py:483 +#: views.py:505 #, python-format msgid "Metadata set: %s deleted successfully." msgstr "Набор метаданных: %s удалён." -#: views.py:485 +#: views.py:508 #, python-format msgid "Metadata set: %(metadata_set)s delete error: %(error)s" msgstr "Набор метаданных: %(metadata_set)s ошибка удаления: %(error)s" -#: views.py:496 +#: views.py:519 #, python-format msgid "Are you sure you wish to delete the metadata set: %s?" msgstr "Вы действительно хотите удалить набор метаданных: %s?" -#: views.py:554 +#: views.py:538 views.py:556 +msgid "Metadata types" +msgstr "Типы метаданных" + +#: views.py:594 #, python-format msgid "non members of document type: %s" msgstr "не относится к типу документа: %s." -#: views.py:555 +#: views.py:595 #, python-format msgid "members of document type: %s" msgstr "относится к типу документа: %s." @@ -440,10 +435,7 @@ msgid "" "A metadata set is a group of one or more metadata types. Metadata sets are " "useful when creating new documents; selecing a metadata set automatically " "attaches it's member metadata types to said document." -msgstr "" -"Набор метаданных это группа из одного или более типов метаданных. Наборы " -"метаданных полезны при создании новых документов; указание набора " -"метаданных автоматически добавляет метаданные набора к документу." +msgstr "Набор метаданных это группа из одного или более типов метаданных. Наборы метаданных полезны при создании новых документов; указание набора метаданных автоматически добавляет метаданные набора к документу." #: templates/metadata_type_help.html:3 msgid "What are metadata types?" @@ -451,25 +443,13 @@ msgstr "Что такое типы метаданных?" #: templates/metadata_type_help.html:4 msgid "" -"A metadata type defines the characteristics of a value of some kind that can " -"be attached to a document. Examples of metadata types are: a client name, a " -"date, or a project to which several documents belong. A metadata type's " +"A metadata type defines the characteristics of a value of some kind that can" +" be attached to a document. Examples of metadata types are: a client name, " +"a date, or a project to which several documents belong. A metadata type's " "name is the internal identifier with which it can be referenced to by other " -"modules such as the indexing module, the title is the value that is shown to " -"the users, the default value is the value an instance of this metadata type " -"will have initially, and the lookup value turns an instance of a metadata of " -"this type into a choice list which options are the result of the lookup's " -"code execution." -msgstr "" -"Тип метаданных определяет характеристики информации которая может быть " -"присоединена к документу. Примеры типов метаданных : имя клиента, дата или " -"проект, к которому принадлежат несколько документов. Имя типа метаданных " -"является внутренним идентификатором, на который могут ссылаться другие " -"модули, такие как модуль индексирования\n" -"\n" -"Имя это значение, которое показано пользователям\n" -"\n" -"Значение по умолчанию - значение экземпляра этого типа метаданных будет на " -"начальном этапе,\n" -"\n" -"Краткое имя служит для выбора из списка значений в результатах поиска." +"modules such as the indexing module, the title is the value that is shown to" +" the users, the default value is the value an instance of this metadata type" +" will have initially, and the lookup value turns an instance of a metadata " +"of this type into a choice list which options are the result of the lookup's" +" code execution." +msgstr "Тип метаданных определяет характеристики информации которая может быть присоединена к документу. Примеры типов метаданных : имя клиента, дата или проект, к которому принадлежат несколько документов. Имя типа метаданных является внутренним идентификатором, на который могут ссылаться другие модули, такие как модуль индексирования\n\nИмя это значение, которое показано пользователям\n\nЗначение по умолчанию - значение экземпляра этого типа метаданных будет на начальном этапе,\n\nКраткое имя служит для выбора из списка значений в результатах поиска." diff --git a/apps/metadata/models.py b/apps/metadata/models.py index 8b3d6463ea..5e13f65c48 100644 --- a/apps/metadata/models.py +++ b/apps/metadata/models.py @@ -1,10 +1,11 @@ +from __future__ import absolute_import + from django.db import models from django.utils.translation import ugettext_lazy as _ from documents.models import Document, DocumentType -from metadata.conf.settings import AVAILABLE_MODELS -from metadata.conf.settings import AVAILABLE_FUNCTIONS +from .conf.settings import (AVAILABLE_MODELS, AVAILABLE_FUNCTIONS) available_models_string = (_(u' Available models: %s') % u','.join([name for name, model in AVAILABLE_MODELS.items()])) if AVAILABLE_MODELS else u'' available_functions_string = (_(u' Available functions: %s') % u','.join([u'%s()' % name for name, function in AVAILABLE_FUNCTIONS.items()])) if AVAILABLE_FUNCTIONS else u'' @@ -13,7 +14,7 @@ available_functions_string = (_(u' Available functions: %s') % u','.join([u'%s() class MetadataType(models.Model): """ Define a type of metadata - """ + """ name = models.CharField(unique=True, max_length=48, verbose_name=_(u'name'), help_text=_(u'Do not use python reserved words, or spaces.')) title = models.CharField(max_length=48, verbose_name=_(u'title'), blank=True, null=True) default = models.CharField(max_length=128, blank=True, null=True, @@ -86,7 +87,7 @@ class DocumentTypeDefaults(models.Model): """ Default preselected metadata types and metadata set per document type - """ + """ document_type = models.ForeignKey(DocumentType, verbose_name=_(u'document type')) default_metadata_sets = models.ManyToManyField(MetadataSet, blank=True, verbose_name=_(u'default metadata sets')) default_metadata = models.ManyToManyField(MetadataType, blank=True, verbose_name=_(u'default metadata')) diff --git a/apps/metadata/permissions.py b/apps/metadata/permissions.py new file mode 100644 index 0000000000..9f6ff38a76 --- /dev/null +++ b/apps/metadata/permissions.py @@ -0,0 +1,22 @@ +from __future__ import absolute_import + +from django.utils.translation import ugettext_lazy as _ + +from permissions.models import Permission, PermissionNamespace + +metadata_namespace = PermissionNamespace('metadata', _(u'Metadata')) +PERMISSION_METADATA_DOCUMENT_EDIT = Permission.objects.register(metadata_namespace, 'metadata_document_edit', _(u'Edit a document\'s metadata')) +PERMISSION_METADATA_DOCUMENT_ADD = Permission.objects.register(metadata_namespace, 'metadata_document_add', _(u'Add metadata to a document')) +PERMISSION_METADATA_DOCUMENT_REMOVE = Permission.objects.register(metadata_namespace, 'metadata_document_remove', _(u'Remove metadata from a document')) +PERMISSION_METADATA_DOCUMENT_VIEW = Permission.objects.register(metadata_namespace, 'metadata_document_view', _(u'View metadata from a document')) + +metadata_setup_namespace = PermissionNamespace('metadata_setup', _(u'Metadata setup')) +PERMISSION_METADATA_TYPE_EDIT = Permission.objects.register(metadata_setup_namespace, 'metadata_type_edit', _(u'Edit metadata types')) +PERMISSION_METADATA_TYPE_CREATE = Permission.objects.register(metadata_setup_namespace, 'metadata_type_create', _(u'Create new metadata types')) +PERMISSION_METADATA_TYPE_DELETE = Permission.objects.register(metadata_setup_namespace, 'metadata_type_delete', _(u'Delete metadata types')) +PERMISSION_METADATA_TYPE_VIEW = Permission.objects.register(metadata_setup_namespace, 'metadata_type_view', _(u'View metadata types')) + +PERMISSION_METADATA_SET_EDIT = Permission.objects.register(metadata_setup_namespace, 'metadata_set_edit', _(u'Edit metadata sets')) +PERMISSION_METADATA_SET_CREATE = Permission.objects.register(metadata_setup_namespace, 'metadata_set_create', _(u'Create new metadata sets')) +PERMISSION_METADATA_SET_DELETE = Permission.objects.register(metadata_setup_namespace, 'metadata_set_delete', _(u'Delete metadata sets')) +PERMISSION_METADATA_SET_VIEW = Permission.objects.register(metadata_setup_namespace, 'metadata_set_view', _(u'View metadata sets')) diff --git a/apps/metadata/tests.py b/apps/metadata/tests.py deleted file mode 100644 index 2247054b35..0000000000 --- a/apps/metadata/tests.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -This file demonstrates two different styles of tests (one doctest and one -unittest). These will both pass when you run "manage.py test". - -Replace these 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.failUnlessEqual(1 + 1, 2) - -__test__ = {"doctest": """ -Another way to test that 1 + 1 is equal to 2. - ->>> 1 + 1 == 2 -True -"""} - diff --git a/apps/metadata/views.py b/apps/metadata/views.py index 028700a46d..31e9a74549 100644 --- a/apps/metadata/views.py +++ b/apps/metadata/views.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import + from django.shortcuts import render_to_response from django.template import RequestContext from django.utils.translation import ugettext_lazy as _ @@ -6,32 +8,33 @@ from django.contrib import messages from django.http import HttpResponseRedirect from django.core.urlresolvers import reverse from django.utils.http import urlencode +from django.core.exceptions import PermissionDenied -from documents.literals import PERMISSION_DOCUMENT_TYPE_EDIT +from documents.permissions import PERMISSION_DOCUMENT_TYPE_EDIT from documents.models import Document, RecentDocument, DocumentType -from permissions.api import check_permissions +from permissions.models import Permission from document_indexing.api import update_indexes, delete_indexes +from acls.models import AccessEntry -from common.utils import generate_choices_w_labels, encapsulate +from common.utils import generate_choices_w_labels, encapsulate, get_object_name from common.views import assign_remove -from metadata import PERMISSION_METADATA_DOCUMENT_VIEW, \ - PERMISSION_METADATA_DOCUMENT_EDIT, \ - PERMISSION_METADATA_DOCUMENT_ADD, PERMISSION_METADATA_DOCUMENT_REMOVE, \ - PERMISSION_METADATA_TYPE_EDIT, PERMISSION_METADATA_TYPE_CREATE, \ - PERMISSION_METADATA_TYPE_DELETE, PERMISSION_METADATA_TYPE_VIEW, \ - PERMISSION_METADATA_SET_EDIT, PERMISSION_METADATA_SET_CREATE, \ - PERMISSION_METADATA_SET_DELETE, PERMISSION_METADATA_SET_VIEW -from metadata.forms import MetadataFormSet, AddMetadataForm, \ - MetadataRemoveFormSet, MetadataTypeForm, MetadataSetForm -from metadata.api import save_metadata_list -from metadata.models import DocumentMetadata, MetadataType, MetadataSet, \ - MetadataSetItem, DocumentTypeDefaults +from .permissions import (PERMISSION_METADATA_DOCUMENT_EDIT, + PERMISSION_METADATA_DOCUMENT_ADD, PERMISSION_METADATA_DOCUMENT_REMOVE, + PERMISSION_METADATA_DOCUMENT_VIEW, PERMISSION_METADATA_TYPE_EDIT, + PERMISSION_METADATA_TYPE_CREATE, PERMISSION_METADATA_TYPE_DELETE, + PERMISSION_METADATA_TYPE_VIEW, PERMISSION_METADATA_SET_EDIT, + PERMISSION_METADATA_SET_CREATE, PERMISSION_METADATA_SET_DELETE, + PERMISSION_METADATA_SET_VIEW) +from .forms import (MetadataFormSet, AddMetadataForm, + MetadataRemoveFormSet, MetadataTypeForm, MetadataSetForm) +from .api import save_metadata_list +from .models import (DocumentMetadata, MetadataType, MetadataSet, + MetadataSetItem, DocumentTypeDefaults) +from .classes import MetadataObjectWrapper def metadata_edit(request, document_id=None, document_id_list=None): - check_permissions(request.user, [PERMISSION_METADATA_DOCUMENT_EDIT]) - if document_id: documents = [get_object_or_404(Document, pk=document_id)] if documents[0].documentmetadata_set.count() == 0: @@ -39,7 +42,13 @@ def metadata_edit(request, document_id=None, document_id_list=None): return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/')) elif document_id_list: documents = [get_object_or_404(Document, pk=document_id) for document_id in document_id_list.split(',')] - else: + + try: + Permission.objects.check_permissions(request.user, [PERMISSION_METADATA_DOCUMENT_EDIT]) + except PermissionDenied: + documents = AccessEntry.objects.filter_objects_by_access(PERMISSION_METADATA_DOCUMENT_EDIT, request.user, documents) + + if not documents: messages.error(request, _(u'Must provide at least one document.')) return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/')) @@ -121,13 +130,17 @@ def metadata_multiple_edit(request): def metadata_add(request, document_id=None, document_id_list=None): - check_permissions(request.user, [PERMISSION_METADATA_DOCUMENT_ADD]) - if document_id: documents = [get_object_or_404(Document, pk=document_id)] elif document_id_list: documents = [get_object_or_404(Document, pk=document_id) for document_id in document_id_list.split(',')] - else: + + try: + Permission.objects.check_permissions(request.user, [PERMISSION_METADATA_DOCUMENT_ADD]) + except PermissionDenied: + documents = AccessEntry.objects.filter_objects_by_access(PERMISSION_METADATA_DOCUMENT_ADD, request.user, documents) + + if not documents: messages.error(request, _(u'Must provide at least one document.')) return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/')) @@ -185,8 +198,6 @@ def metadata_multiple_add(request): def metadata_remove(request, document_id=None, document_id_list=None): - check_permissions(request.user, [PERMISSION_METADATA_DOCUMENT_REMOVE]) - if document_id: documents = [get_object_or_404(Document, pk=document_id)] if documents[0].documentmetadata_set.count() == 0: @@ -195,7 +206,13 @@ def metadata_remove(request, document_id=None, document_id_list=None): elif document_id_list: documents = [get_object_or_404(Document, pk=document_id) for document_id in document_id_list.split(',')] - else: + + try: + Permission.objects.check_permissions(request.user, [PERMISSION_METADATA_DOCUMENT_REMOVE]) + except PermissionDenied: + documents = AccessEntry.objects.filter_objects_by_access(PERMISSION_METADATA_DOCUMENT_REMOVE, request.user, documents) + + if not documents: messages.error(request, _(u'Must provide at least one document.')) return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/')) @@ -274,9 +291,13 @@ def metadata_multiple_remove(request): def metadata_view(request, document_id): - check_permissions(request.user, [PERMISSION_METADATA_DOCUMENT_VIEW]) document = get_object_or_404(Document, pk=document_id) + try: + Permission.objects.check_permissions(request.user, [PERMISSION_METADATA_DOCUMENT_VIEW]) + except PermissionDenied: + AccessEntry.objects.check_access(PERMISSION_METADATA_DOCUMENT_VIEW, request.user, document) + return render_to_response('generic_list.html', { 'title': _(u'metadata for: %s') % document, 'object_list': document.documentmetadata_set.all(), @@ -284,10 +305,11 @@ def metadata_view(request, document_id): 'hide_link': True, 'object': document, }, context_instance=RequestContext(request)) - + +# Setup views def setup_metadata_type_list(request): - check_permissions(request.user, [PERMISSION_METADATA_TYPE_VIEW]) + Permission.objects.check_permissions(request.user, [PERMISSION_METADATA_TYPE_VIEW]) context = { 'object_list': MetadataType.objects.all(), @@ -302,12 +324,12 @@ def setup_metadata_type_list(request): } return render_to_response('generic_list.html', context, - context_instance=RequestContext(request)) + context_instance=RequestContext(request)) def setup_metadata_type_edit(request, metadatatype_id): - check_permissions(request.user, [PERMISSION_METADATA_TYPE_EDIT]) - + Permission.objects.check_permissions(request.user, [PERMISSION_METADATA_TYPE_EDIT]) + metadata_type = get_object_or_404(MetadataType, pk=metadatatype_id) if request.method == 'POST': @@ -330,12 +352,12 @@ def setup_metadata_type_edit(request, metadatatype_id): 'object': metadata_type, 'object_name': _(u'metadata type'), }, - context_instance=RequestContext(request)) - - + context_instance=RequestContext(request)) + + def setup_metadata_type_create(request): - check_permissions(request.user, [PERMISSION_METADATA_TYPE_CREATE]) - + Permission.objects.check_permissions(request.user, [PERMISSION_METADATA_TYPE_CREATE]) + if request.method == 'POST': form = MetadataTypeForm(request.POST) if form.is_valid(): @@ -353,8 +375,8 @@ def setup_metadata_type_create(request): def setup_metadata_type_delete(request, metadatatype_id): - check_permissions(request.user, [PERMISSION_METADATA_TYPE_DELETE]) - + Permission.objects.check_permissions(request.user, [PERMISSION_METADATA_TYPE_DELETE]) + metadata_type = get_object_or_404(MetadataType, pk=metadatatype_id) post_action_redirect = reverse('setup_metadata_type_list') @@ -387,7 +409,7 @@ def setup_metadata_type_delete(request, metadatatype_id): def setup_metadata_set_list(request): - check_permissions(request.user, [PERMISSION_METADATA_SET_VIEW]) + Permission.objects.check_permissions(request.user, [PERMISSION_METADATA_SET_VIEW]) context = { 'object_list': MetadataSet.objects.all(), @@ -402,7 +424,7 @@ def setup_metadata_set_list(request): } return render_to_response('generic_list.html', context, - context_instance=RequestContext(request)) + context_instance=RequestContext(request)) def get_set_members(metadata_set): @@ -429,14 +451,14 @@ def remove_set_member(metadata_set, selection): def setup_metadata_set_edit(request, metadata_set_id): - check_permissions(request.user, [PERMISSION_METADATA_SET_EDIT]) + Permission.objects.check_permissions(request.user, [PERMISSION_METADATA_SET_EDIT]) metadata_set = get_object_or_404(MetadataSet, pk=metadata_set_id) return assign_remove( request, - left_list=lambda: generate_choices_w_labels(get_non_set_members(metadata_set)), - right_list=lambda: generate_choices_w_labels(get_set_members(metadata_set)), + left_list=lambda: generate_choices_w_labels(get_non_set_members(metadata_set), display_object_type=False), + right_list=lambda: generate_choices_w_labels(get_set_members(metadata_set), display_object_type=False), add_method=lambda x: add_set_member(metadata_set, x), remove_method=lambda x: remove_set_member(metadata_set, x), left_list_title=_(u'non members of metadata set: %s') % metadata_set, @@ -449,8 +471,8 @@ def setup_metadata_set_edit(request, metadata_set_id): def setup_metadata_set_create(request): - check_permissions(request.user, [PERMISSION_METADATA_SET_CREATE]) - + Permission.objects.check_permissions(request.user, [PERMISSION_METADATA_SET_CREATE]) + if request.method == 'POST': form = MetadataSetForm(request.POST) if form.is_valid(): @@ -468,8 +490,8 @@ def setup_metadata_set_create(request): def setup_metadata_set_delete(request, metadata_set_id): - check_permissions(request.user, [PERMISSION_METADATA_SET_DELETE]) - + Permission.objects.check_permissions(request.user, [PERMISSION_METADATA_SET_DELETE]) + metadata_set = get_object_or_404(MetadataSet, pk=metadata_set_id) post_action_redirect = reverse('setup_metadata_set_list') @@ -502,54 +524,71 @@ def setup_metadata_set_delete(request, metadata_set_id): context_instance=RequestContext(request)) -def get_document_type_metadata_members(document_type): - metadata_types = set(document_type.documenttypedefaults_set.get().default_metadata.all()) - metadata_sets = set(document_type.documenttypedefaults_set.get().default_metadata_sets.all()) - return list(metadata_types | metadata_sets) +def _as_choice_list(items): + return sorted([(MetadataObjectWrapper.encapsulate(item).gid, get_object_name(item, display_object_type=False)) for item in items], key=lambda x: x[1]) def get_document_type_metadata_non_members(document_type): - members = set(get_document_type_metadata_members(document_type)) - all_metadata_objects = set(MetadataType.objects.all()) | set(MetadataSet.objects.all()) - return list(all_metadata_objects - members) + metadata_types, metadata_sets = get_document_type_metadata_members(document_type, separate=True) + metadata_types = set(MetadataType.objects.all()) - set(metadata_types) + metadata_sets = set(MetadataSet.objects.all()) - set(metadata_sets) + + non_members = [] + if metadata_types: + non_members.append((_(u'Metadata types'), _as_choice_list(list(metadata_types)))) + + if metadata_sets: + non_members.append((_(u'Metadata sets'), _as_choice_list(list(metadata_sets)))) + + return non_members + + +def get_document_type_metadata_members(document_type, separate=False): + metadata_types = set(document_type.documenttypedefaults_set.get().default_metadata.all()) + metadata_sets = set(document_type.documenttypedefaults_set.get().default_metadata_sets.all()) + + if separate: + return metadata_types, metadata_sets + else: + members = [] + + if metadata_types: + members.append((_(u'Metadata types'), _as_choice_list(list(metadata_types)))) + + if metadata_sets: + members.append((_(u'Metadata sets'), _as_choice_list(list(metadata_sets)))) + + return members def add_document_type_metadata(document_type, selection): - model, pk = selection.split(u',') - if model == 'metadata type': - metadata_type = get_object_or_404(MetadataType, pk=pk) - document_type.documenttypedefaults_set.get().default_metadata.add(metadata_type) - elif model == 'metadata set': - metadata_set = get_object_or_404(MetadataSet, pk=pk) - document_type.documenttypedefaults_set.get().default_metadata_sets.add(metadata_set) - else: - raise Exception + metadata_object = MetadataObjectWrapper.get(selection).source_object + try: + document_type.documenttypedefaults_set.get().default_metadata.add(metadata_object) + except TypeError: + document_type.documenttypedefaults_set.get().default_metadata_sets.add(metadata_object) def remove_document_type_metadata(document_type, selection): - model, pk = selection.split(u',') - if model == 'metadata type': - metadata_type = get_object_or_404(MetadataType, pk=pk) - document_type.documenttypedefaults_set.get().default_metadata.remove(metadata_type) - elif model == 'metadata set': - metadata_set = get_object_or_404(MetadataSet, pk=pk) - document_type.documenttypedefaults_set.get().default_metadata_sets.remove(metadata_set) + metadata_object = MetadataObjectWrapper.get(selection).source_object + if isinstance(metadata_object, MetadataType): + document_type.documenttypedefaults_set.get().default_metadata.remove(metadata_object) else: - raise Exception + document_type.documenttypedefaults_set.get().default_metadata_sets.remove(metadata_object) def setup_document_type_metadata(request, document_type_id): - check_permissions(request.user, [PERMISSION_DOCUMENT_TYPE_EDIT]) + Permission.objects.check_permissions(request.user, [PERMISSION_DOCUMENT_TYPE_EDIT]) document_type = get_object_or_404(DocumentType, pk=document_type_id) # Initialize defaults DocumentTypeDefaults.objects.get_or_create(document_type=document_type) - + return assign_remove( request, - left_list=lambda: generate_choices_w_labels(get_document_type_metadata_non_members(document_type)), - right_list=lambda: generate_choices_w_labels(get_document_type_metadata_members(document_type)), + left_list=lambda: get_document_type_metadata_non_members(document_type), + right_list=lambda: get_document_type_metadata_members(document_type), add_method=lambda x: add_document_type_metadata(document_type, x), remove_method=lambda x: remove_document_type_metadata(document_type, x), left_list_title=_(u'non members of document type: %s') % document_type, @@ -559,4 +598,5 @@ def setup_document_type_metadata(request, document_type_id): 'navigation_object_name': 'document_type', 'object_name': _(u'document type'), }, + grouped=True, ) diff --git a/apps/mimetype/api.py b/apps/mimetype/api.py index 5790bce8a2..cc7e93db32 100644 --- a/apps/mimetype/api.py +++ b/apps/mimetype/api.py @@ -83,10 +83,11 @@ def get_error_icon_file_path(): else: return os.path.join(settings.STATIC_ROOT, MIMETYPE_ICONS_DIRECTORY_NAME, ERROR_FILE_NAME) + def get_error_icon_url(): return os.path.join(MIMETYPE_ICONS_DIRECTORY_NAME, ERROR_FILE_NAME) - + def get_mimetype(file_description, filepath, mimetype_only=False): """ Determine a file's mimetype by calling the system's libmagic diff --git a/apps/mimetype/tests.py b/apps/mimetype/tests.py deleted file mode 100644 index 2247054b35..0000000000 --- a/apps/mimetype/tests.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -This file demonstrates two different styles of tests (one doctest and one -unittest). These will both pass when you run "manage.py test". - -Replace these 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.failUnlessEqual(1 + 1, 2) - -__test__ = {"doctest": """ -Another way to test that 1 + 1 is equal to 2. - ->>> 1 + 1 == 2 -True -"""} - diff --git a/apps/navigation/api.py b/apps/navigation/api.py index 8939370cb9..56bb037245 100644 --- a/apps/navigation/api.py +++ b/apps/navigation/api.py @@ -1,5 +1,3 @@ -import copy - object_navigation = {} multi_object_navigation = {} model_list_columns = {} @@ -46,7 +44,7 @@ def register_links(src, links, menu_name=None, position=None): object_navigation[menu_name][src]['links'].extend(links) -def register_top_menu(name, link, children_views=None, +def register_top_menu(name, link, children_views=None, children_path_regex=None, children_view_regex=None, position=None): """ @@ -70,6 +68,8 @@ def register_top_menu(name, link, children_views=None, top_menu_entries.append(entry) sort_menu_entries() + + return entry def sort_menu_entries(): diff --git a/apps/navigation/locale/en/LC_MESSAGES/django.po b/apps/navigation/locale/en/LC_MESSAGES/django.po index 073df2fae0..5b3c695fac 100644 --- a/apps/navigation/locale/en/LC_MESSAGES/django.po +++ b/apps/navigation/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -21,10 +21,10 @@ msgstr "" msgid "Multi item action" msgstr "" -#: widgets.py:28 +#: widgets.py:48 msgid "icon" msgstr "" -#: templatetags/navigation_tags.py:275 +#: templatetags/navigation_tags.py:278 msgid "Selected item actions:" msgstr "" diff --git a/apps/navigation/locale/es/LC_MESSAGES/django.mo b/apps/navigation/locale/es/LC_MESSAGES/django.mo index 6091110708..7ab9cf55c5 100644 Binary files a/apps/navigation/locale/es/LC_MESSAGES/django.mo and b/apps/navigation/locale/es/LC_MESSAGES/django.mo differ diff --git a/apps/navigation/locale/es/LC_MESSAGES/django.po b/apps/navigation/locale/es/LC_MESSAGES/django.po index 4fa778b876..3079e51a80 100644 --- a/apps/navigation/locale/es/LC_MESSAGES/django.po +++ b/apps/navigation/locale/es/LC_MESSAGES/django.po @@ -8,9 +8,9 @@ msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" "PO-Revision-Date: 2011-11-04 16:58+0000\n" -"Last-Translator: rosarior \n" +"Last-Translator: Roberto Rosario \n" "Language-Team: Spanish (Castilian) (http://www.transifex.net/projects/p/" "mayan-edms/team/es/)\n" "Language: es\n" @@ -23,10 +23,10 @@ msgstr "" msgid "Multi item action" msgstr "Acción para multiple artículos" -#: widgets.py:28 +#: widgets.py:48 msgid "icon" msgstr "emblema" -#: templatetags/navigation_tags.py:275 +#: templatetags/navigation_tags.py:278 msgid "Selected item actions:" msgstr "Acciones para el artículo seleccionado:" diff --git a/apps/navigation/locale/it/LC_MESSAGES/django.mo b/apps/navigation/locale/it/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..bdb151f775 Binary files /dev/null and b/apps/navigation/locale/it/LC_MESSAGES/django.mo differ diff --git a/apps/navigation/locale/it/LC_MESSAGES/django.po b/apps/navigation/locale/it/LC_MESSAGES/django.po new file mode 100644 index 0000000000..957fcd5f25 --- /dev/null +++ b/apps/navigation/locale/it/LC_MESSAGES/django.po @@ -0,0 +1,32 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# , 2011. +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2011-12-09 18:00+0000\n" +"Last-Translator: Pierpaolo Baldan \n" +"Language-Team: Italian (http://www.transifex.net/projects/p/mayan-edms/team/" +"it/)\n" +"Language: it\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" + +#: forms.py:14 +msgid "Multi item action" +msgstr "Voce per azioni multiple" + +#: widgets.py:48 +msgid "icon" +msgstr "icon" + +#: templatetags/navigation_tags.py:278 +msgid "Selected item actions:" +msgstr "Selezione le azioni multiple" diff --git a/apps/navigation/locale/pl/LC_MESSAGES/django.mo b/apps/navigation/locale/pl/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..d4e9505785 Binary files /dev/null and b/apps/navigation/locale/pl/LC_MESSAGES/django.mo differ diff --git a/apps/navigation/locale/pl/LC_MESSAGES/django.po b/apps/navigation/locale/pl/LC_MESSAGES/django.po new file mode 100644 index 0000000000..ce02d46463 --- /dev/null +++ b/apps/navigation/locale/pl/LC_MESSAGES/django.po @@ -0,0 +1,31 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# , 2012. +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-21 20:59+0000\n" +"Last-Translator: mic \n" +"Language-Team: Polish (http://www.transifex.net/projects/p/mayan-edms/language/pl/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pl\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" + +#: forms.py:14 +msgid "Multi item action" +msgstr "Multi item action" + +#: widgets.py:48 +msgid "icon" +msgstr "ikona" + +#: templatetags/navigation_tags.py:278 +msgid "Selected item actions:" +msgstr "Selected item actions:" diff --git a/apps/navigation/locale/pt/LC_MESSAGES/django.mo b/apps/navigation/locale/pt/LC_MESSAGES/django.mo index da6beaf7ea..268de030a2 100644 Binary files a/apps/navigation/locale/pt/LC_MESSAGES/django.mo and b/apps/navigation/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/navigation/locale/pt/LC_MESSAGES/django.po b/apps/navigation/locale/pt/LC_MESSAGES/django.po index 7f003b944a..4392ccf2c6 100644 --- a/apps/navigation/locale/pt/LC_MESSAGES/django.po +++ b/apps/navigation/locale/pt/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" "PO-Revision-Date: 2011-11-02 01:24+0000\n" "Last-Translator: emersonsoares \n" "Language-Team: Portuguese (http://www.transifex.net/projects/p/mayan-edms/" @@ -23,10 +23,10 @@ msgstr "" msgid "Multi item action" msgstr "Ação de vários itens" -#: widgets.py:28 +#: widgets.py:48 msgid "icon" msgstr "ícone" -#: templatetags/navigation_tags.py:275 +#: templatetags/navigation_tags.py:278 msgid "Selected item actions:" msgstr "Ações de item selecionadas:" diff --git a/apps/navigation/locale/ru/LC_MESSAGES/django.mo b/apps/navigation/locale/ru/LC_MESSAGES/django.mo index 8dec51de24..fa4dd7336e 100644 Binary files a/apps/navigation/locale/ru/LC_MESSAGES/django.mo and b/apps/navigation/locale/ru/LC_MESSAGES/django.mo differ diff --git a/apps/navigation/locale/ru/LC_MESSAGES/django.po b/apps/navigation/locale/ru/LC_MESSAGES/django.po index ac633acf3c..dffe342d8a 100644 --- a/apps/navigation/locale/ru/LC_MESSAGES/django.po +++ b/apps/navigation/locale/ru/LC_MESSAGES/django.po @@ -8,9 +8,9 @@ msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" "PO-Revision-Date: 2011-11-19 21:05+0000\n" -"Last-Translator: gsv70 \n" +"Last-Translator: Sergey Glita \n" "Language-Team: Russian (http://www.transifex.net/projects/p/mayan-edms/team/" "ru/)\n" "Language: ru\n" @@ -24,10 +24,10 @@ msgstr "" msgid "Multi item action" msgstr "Массовое действие" -#: widgets.py:28 +#: widgets.py:48 msgid "icon" msgstr "значок" -#: templatetags/navigation_tags.py:275 +#: templatetags/navigation_tags.py:278 msgid "Selected item actions:" msgstr "Действия с выбранными пунктами: " diff --git a/apps/navigation/templates/generic_link_instance.html b/apps/navigation/templates/generic_link_instance.html index 808ed2f65b..098810ee13 100644 --- a/apps/navigation/templates/generic_link_instance.html +++ b/apps/navigation/templates/generic_link_instance.html @@ -3,7 +3,7 @@ {% get_main_setting "DISABLE_ICONS" as disable_icons %} {% if link.disabled %} - {% if link.famfam and not disable_icons %}{% endif %}{{ link.text|capfirst }}{% if link.error %} - {{ link.error }}{% endif %}{% if link.active and not hide_active_anchor %}{% endif %}{% if horizontal %}{% if not forloop.last %} | {% endif %}{% endif %} + {% if link.famfam and not disable_icons %}{% endif %}{{ link.text|capfirst }}{% if link.error %} - {{ link.error }}{% endif %}{% if link.active and not hide_active_anchor %}{% endif %}{% if horizontal %}{% if not forloop.last %} | {% endif %}{% endif %} {% else %} {% if link.famfam and not disable_icons %}{% endif %}{{ link.text|capfirst }}{% if link.error %} - {{ link.error }}{% endif %}{% if link.active and not hide_active_anchor %}{% endif %}{% if horizontal %}{% if not forloop.last %} | {% endif %}{% endif %} {% endif %} diff --git a/apps/navigation/templates/generic_subnavigation.html b/apps/navigation/templates/generic_subnavigation.html index cac3301f00..eac581d5e0 100644 --- a/apps/navigation/templates/generic_subnavigation.html +++ b/apps/navigation/templates/generic_subnavigation.html @@ -1,9 +1,12 @@ {% load permission_tags %} {% load navigation_tags %} +{% load acl_tags %} {% with link.permissions as permissions %} {% check_permissions request.user permissions %} - {% if permission %} + {% check_access permissions request.user object %} + + {% if permission or access %} {% if as_li %}

  • {% endif %} diff --git a/apps/navigation/templatetags/navigation_tags.py b/apps/navigation/templatetags/navigation_tags.py index 815af1ea0d..f3b989a511 100644 --- a/apps/navigation/templatetags/navigation_tags.py +++ b/apps/navigation/templatetags/navigation_tags.py @@ -1,19 +1,21 @@ +from __future__ import absolute_import + import copy import re import urlparse from django.core.urlresolvers import reverse, NoReverseMatch -from django.template import TemplateSyntaxError, Library, \ - VariableDoesNotExist, Node, Variable +from django.template import (TemplateSyntaxError, Library, + VariableDoesNotExist, Node, Variable) from django.utils.text import unescape_string_literal from django.utils.translation import ugettext as _ from common.utils import urlquote -from navigation.api import object_navigation, multi_object_navigation, \ - top_menu_entries, sidebar_templates -from navigation.forms import MultiItemForm -from navigation.utils import resolve_to_name +from ..api import (object_navigation, multi_object_navigation, + top_menu_entries, sidebar_templates) +from ..forms import MultiItemForm +from ..utils import resolve_to_name register = Library() @@ -36,7 +38,7 @@ class TopMenuNavigationNode(Node): menu_links[index]['active'] = True for children_view_regex in link.get('children_view_regex', []): - if re.compile(children_view_regex).match(children_view_regex): + if re.compile(children_view_regex).match(current_view): menu_links[index]['active'] = True context['menu_links'] = menu_links @@ -249,13 +251,14 @@ def get_object_navigation_links(parser, token): @register.inclusion_tag('generic_navigation.html', takes_context=True) def object_navigation_template(context): - return { - 'request': context['request'], + new_context = copy.copy(context) + new_context.update({ 'horizontal': True, - 'object_navigation_links': _get_object_navigation_links(context) - } - - + 'object_navigation_links': _get_object_navigation_links(context) + }) + return new_context + + @register.tag def get_multi_item_links(parser, token): tag_name, arg = token.contents.split(None, 1) diff --git a/apps/navigation/tests.py b/apps/navigation/tests.py deleted file mode 100644 index 2247054b35..0000000000 --- a/apps/navigation/tests.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -This file demonstrates two different styles of tests (one doctest and one -unittest). These will both pass when you run "manage.py test". - -Replace these 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.failUnlessEqual(1 + 1, 2) - -__test__ = {"doctest": """ -Another way to test that 1 + 1 is equal to 2. - ->>> 1 + 1 == 2 -True -"""} - diff --git a/apps/navigation/widgets.py b/apps/navigation/widgets.py index 949fae4dd3..f587fc6ebb 100644 --- a/apps/navigation/widgets.py +++ b/apps/navigation/widgets.py @@ -1,29 +1,51 @@ +from __future__ import absolute_import + +import urlparse + from django.utils.safestring import mark_safe from django.conf import settings from django.utils.translation import ugettext_lazy as _ from django.core.urlresolvers import reverse from django.template.defaultfilters import capfirst from django.core.exceptions import PermissionDenied +from django.template import RequestContext, Variable -from permissions.api import check_permissions +from permissions.models import Permission + +from .templatetags.navigation_tags import resolve_links +from .utils import resolve_to_name def button_navigation_widget(request, link): if 'permissions' in link: try: - check_permissions(request.user, link['permissions']) - return render_widget(link) + Permission.objects.check_permissions(request.user, link['permissions']) + return render_widget(request, link) except PermissionDenied: return u'' else: - return render_widget(link) + return render_widget(request, link) - -def render_widget(link): - return mark_safe(u'' % { - 'url': reverse(link['view']) if 'view' in link else link['url'], - 'icon': link.get('icon', 'link_button.png'), - 'static_url': settings.STATIC_URL, - 'string': capfirst(link['text']), - 'image_alt': _(u'icon'), - }) + +def render_widget(request, link): + context = RequestContext(request) + + request = Variable('request').resolve(context) + current_path = request.META['PATH_INFO'] + current_view = resolve_to_name(current_path) + + query_string = urlparse.urlparse(request.get_full_path()).query or urlparse.urlparse(request.META.get('HTTP_REFERER', u'/')).query + parsed_query_string = urlparse.parse_qs(query_string) + + links = resolve_links(context, [link], current_view, current_path, parsed_query_string) + if links: + link = links[0] + return mark_safe(u'' % { + 'url': reverse(link['view']) if 'view' in link else link['url'], + 'icon': link.get('icon', 'link_button.png'), + 'static_url': settings.STATIC_URL, + 'string': capfirst(link['text']), + 'image_alt': _(u'icon'), + }) + else: + return u'' diff --git a/apps/ocr/__init__.py b/apps/ocr/__init__.py index f35c7cbe30..3a3b0ac7c9 100644 --- a/apps/ocr/__init__.py +++ b/apps/ocr/__init__.py @@ -1,45 +1,33 @@ +from __future__ import absolute_import + import logging - -from django.core.exceptions import ImproperlyConfigured + from django.db import transaction from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext -from django.db.utils import DatabaseError from django.db.models.signals import post_save, post_syncdb from django.dispatch import receiver +from django.db.utils import DatabaseError -from navigation.api import register_links, register_top_menu, register_multi_item_links -from permissions.api import register_permission, set_namespace_title -from documents.models import Document +from navigation.api import register_links, register_multi_item_links +from documents.models import Document, DocumentVersion from main.api import register_maintenance_links from project_tools.api import register_tool +from acls.api import class_permissions from scheduler.api import register_interval_job -from ocr.conf.settings import AUTOMATIC_OCR -from ocr.conf.settings import QUEUE_PROCESSING_INTERVAL -from ocr.models import DocumentQueue, QueueTransformation, QueueDocument -from ocr.tasks import task_process_document_queues -from ocr import models as ocr_models +from .conf.settings import (AUTOMATIC_OCR, QUEUE_PROCESSING_INTERVAL) +from .models import DocumentQueue, QueueTransformation +from .tasks import task_process_document_queues +from .permissions import (PERMISSION_OCR_DOCUMENT, + PERMISSION_OCR_DOCUMENT_DELETE, PERMISSION_OCR_QUEUE_ENABLE_DISABLE, + PERMISSION_OCR_CLEAN_ALL_PAGES) +from .exceptions import AlreadyQueued +from . import models as ocr_models logger = logging.getLogger(__name__) -#Permissions -PERMISSION_OCR_DOCUMENT = {'namespace': 'ocr', 'name': 'ocr_document', 'label': _(u'Submit document for OCR')} -PERMISSION_OCR_DOCUMENT_DELETE = {'namespace': 'ocr', 'name': 'ocr_document_delete', 'label': _(u'Delete document for OCR queue')} -PERMISSION_OCR_QUEUE_ENABLE_DISABLE = {'namespace': 'ocr', 'name': 'ocr_queue_enable_disable', 'label': _(u'Can enable/disable an OCR queue')} -PERMISSION_OCR_CLEAN_ALL_PAGES = {'namespace': 'ocr', 'name': 'ocr_clean_all_pages', 'label': _(u'Can execute an OCR clean up on all document pages')} -PERMISSION_OCR_QUEUE_EDIT = {'namespace': 'ocr_setup', 'name': 'ocr_queue_edit', 'label': _(u'Can edit an OCR queue properties')} - -set_namespace_title('ocr', _(u'OCR')) -register_permission(PERMISSION_OCR_DOCUMENT) -register_permission(PERMISSION_OCR_DOCUMENT_DELETE) -register_permission(PERMISSION_OCR_QUEUE_ENABLE_DISABLE) -register_permission(PERMISSION_OCR_CLEAN_ALL_PAGES) - -set_namespace_title('ocr_setup', _(u'OCR Setup')) -register_permission(PERMISSION_OCR_QUEUE_EDIT) - #Links submit_document = {'text': _('submit to OCR queue'), 'view': 'submit_document', 'args': 'object.id', 'famfam': 'hourglass_add', 'permissions': [PERMISSION_OCR_DOCUMENT]} submit_document_multiple = {'text': _('submit to OCR queue'), 'view': 'submit_document_multiple', 'famfam': 'hourglass_add', 'permissions': [PERMISSION_OCR_DOCUMENT]} @@ -54,9 +42,7 @@ document_queue_enable = {'text': _(u'activate queue'), 'view': 'document_queue_e all_document_ocr_cleanup = {'text': _(u'clean up pages content'), 'view': 'all_document_ocr_cleanup', 'famfam': 'text_strikethrough', 'permissions': [PERMISSION_OCR_CLEAN_ALL_PAGES], 'description': _(u'Runs a language filter to remove common OCR mistakes from document pages content.')} queue_document_list = {'text': _(u'queue document list'), 'view': 'queue_document_list', 'famfam': 'hourglass', 'permissions': [PERMISSION_OCR_DOCUMENT]} -ocr_tool_link = {'text': _(u'OCR'), 'view': 'queue_document_list', 'famfam': 'hourglass', 'icon': 'text.png', 'permissions': [PERMISSION_OCR_DOCUMENT]} - -node_active_list = {'text': _(u'active tasks'), 'view': 'node_active_list', 'famfam': 'server_chart', 'permissions': [PERMISSION_OCR_DOCUMENT]} +ocr_tool_link = {'text': _(u'OCR'), 'view': 'queue_document_list', 'famfam': 'hourglass', 'icon': 'text.png', 'permissions': [PERMISSION_OCR_DOCUMENT], 'children_view_regex': [r'queue_', r'document_queue']} setup_queue_transformation_list = {'text': _(u'transformations'), 'view': 'setup_queue_transformation_list', 'args': 'queue.pk', 'famfam': 'shape_move_front'} setup_queue_transformation_create = {'text': _(u'add transformation'), 'view': 'setup_queue_transformation_create', 'args': 'queue.pk', 'famfam': 'shape_square_add'} @@ -71,7 +57,7 @@ register_links(QueueTransformation, [setup_queue_transformation_edit, setup_queu register_multi_item_links(['queue_document_list'], [re_queue_multiple_document, queue_document_multiple_delete]) -register_links(['setup_queue_transformation_create', 'setup_queue_transformation_edit', 'setup_queue_transformation_delete', 'document_queue_disable', 'document_queue_enable', 'queue_document_list', 'node_active_list', 'setup_queue_transformation_list'], [queue_document_list, node_active_list], menu_name='secondary_menu') +register_links(['setup_queue_transformation_create', 'setup_queue_transformation_edit', 'setup_queue_transformation_delete', 'document_queue_disable', 'document_queue_enable', 'queue_document_list', 'setup_queue_transformation_list'], [queue_document_list], menu_name='secondary_menu') register_links(['setup_queue_transformation_edit', 'setup_queue_transformation_delete', 'setup_queue_transformation_list', 'setup_queue_transformation_create'], [setup_queue_transformation_create], menu_name='sidebar') register_maintenance_links([all_document_ocr_cleanup], namespace='ocr', title=_(u'OCR')) @@ -79,26 +65,33 @@ register_maintenance_links([all_document_ocr_cleanup], namespace='ocr', title=_( @transaction.commit_on_success def create_default_queue(): - default_queue, created = DocumentQueue.objects.get_or_create(name='default') - if created: - default_queue.label = ugettext(u'Default') - default_queue.save() + try: + default_queue, created = DocumentQueue.objects.get_or_create(name='default') + except DatabaseError: + transaction.rollback() + else: + if created: + default_queue.label = ugettext(u'Default') + default_queue.save() +@receiver(post_save, dispatch_uid='document_post_save', sender=DocumentVersion) def document_post_save(sender, instance, **kwargs): + logger.debug('received post save signal') + logger.debug('instance: %s' % instance) if kwargs.get('created', False): if AUTOMATIC_OCR: - DocumentQueue.objects.queue_document(instance) - -post_save.connect(document_post_save, sender=Document) - + try: + DocumentQueue.objects.queue_document(instance.document) + except AlreadyQueued: + pass # Disabled because it appears Django execute signals using the same -# process effectively blocking the view until the OCR process completes -# which could take several minutes :/ +# process of the signal emiter effectively blocking the view until +# the OCR process completes which could take several minutes :/ #@receiver(post_save, dispatch_uid='call_queue', sender=QueueDocument) #def call_queue(sender, **kwargs): -# if kwargs.get('created', False): +# if kwargs.get('created', False): # logger.debug('got call_queue signal: %s' % kwargs) # task_process_document_queues() @@ -109,3 +102,7 @@ def create_default_queue_signal_handler(sender, **kwargs): register_interval_job('task_process_document_queues', _(u'Checks the OCR queue for pending documents.'), task_process_document_queues, seconds=QUEUE_PROCESSING_INTERVAL) register_tool(ocr_tool_link) + +class_permissions(Document, [ + PERMISSION_OCR_DOCUMENT, +]) diff --git a/apps/ocr/api.py b/apps/ocr/api.py index 958ff864eb..33450b0862 100644 --- a/apps/ocr/api.py +++ b/apps/ocr/api.py @@ -1,4 +1,5 @@ #Some code from http://wiki.github.com/hoffstaetter/python-tesseract +from __future__ import absolute_import import codecs import os @@ -13,14 +14,12 @@ from common.conf.settings import TEMPORARY_DIRECTORY from converter.api import convert from documents.models import DocumentPage -from ocr.conf.settings import TESSERACT_PATH -from ocr.conf.settings import TESSERACT_LANGUAGE -from ocr.exceptions import TesseractError, UnpaperError -from ocr.conf.settings import UNPAPER_PATH -from ocr.parsers import parse_document_page -from ocr.parsers.exceptions import ParserError, ParserUnknownFile -from ocr.literals import DEFAULT_OCR_FILE_FORMAT, UNPAPER_FILE_FORMAT, \ - DEFAULT_OCR_FILE_EXTENSION +from .conf.settings import (TESSERACT_PATH, TESSERACT_LANGUAGE, UNPAPER_PATH) +from .exceptions import TesseractError, UnpaperError +from .parsers import parse_document_page +from .parsers.exceptions import ParserError, ParserUnknownFile +from .literals import (DEFAULT_OCR_FILE_FORMAT, UNPAPER_FILE_FORMAT, + DEFAULT_OCR_FILE_EXTENSION) def get_language_backend(): diff --git a/apps/ocr/conf/settings.py b/apps/ocr/conf/settings.py index ff2f7ca04d..31e2c908b8 100644 --- a/apps/ocr/conf/settings.py +++ b/apps/ocr/conf/settings.py @@ -14,7 +14,6 @@ register_settings( {'name': u'NODE_CONCURRENT_EXECUTION', 'global_name': u'OCR_NODE_CONCURRENT_EXECUTION', 'default': 1, 'description': _(u'Maximum amount of concurrent document OCRs a node can perform.')}, {'name': u'AUTOMATIC_OCR', 'global_name': u'OCR_AUTOMATIC_OCR', 'default': False, 'description': _(u'Automatically queue newly created documents for OCR.')}, {'name': u'QUEUE_PROCESSING_INTERVAL', 'global_name': u'OCR_QUEUE_PROCESSING_INTERVAL', 'default': 10}, - {'name': u'CACHE_URI', 'global_name': u'OCR_CACHE_URI', 'default': None, 'description': _(u'URI in the form: "memcached://127.0.0.1:11211/" to specify a cache backend to use for locking. Multiple hosts can be specified separated by a semicolon.')}, {'name': u'UNPAPER_PATH', 'global_name': u'OCR_UNPAPER_PATH', 'default': u'/usr/bin/unpaper', 'description': _(u'File path to unpaper program.'), 'exists': True}, ] ) diff --git a/apps/ocr/exceptions.py b/apps/ocr/exceptions.py index 704e0ae9c7..32ec4c4c07 100644 --- a/apps/ocr/exceptions.py +++ b/apps/ocr/exceptions.py @@ -1,8 +1,14 @@ class AlreadyQueued(Exception): + """ + Raised when a trying to queue document already in the queue + """ pass class TesseractError(Exception): + """ + Raised by tesseract + """ pass diff --git a/apps/ocr/locale/en/LC_MESSAGES/django.po b/apps/ocr/locale/en/LC_MESSAGES/django.po index 8aa482cb75..47baef0ce4 100644 --- a/apps/ocr/locale/en/LC_MESSAGES/django.po +++ b/apps/ocr/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,93 +17,65 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: __init__.py:28 -msgid "Submit document for OCR" -msgstr "" - -#: __init__.py:29 -msgid "Delete document for OCR queue" -msgstr "" - -#: __init__.py:30 -msgid "Can enable/disable an OCR queue" -msgstr "" - -#: __init__.py:31 -msgid "Can execute an OCR clean up on all document pages" -msgstr "" - -#: __init__.py:32 -msgid "Can edit an OCR queue properties" -msgstr "" - -#: __init__.py:34 __init__.py:56 __init__.py:74 -msgid "OCR" -msgstr "" - -#: __init__.py:40 -msgid "OCR Setup" -msgstr "" - -#: __init__.py:44 +#: __init__.py:32 __init__.py:33 msgid "submit to OCR queue" msgstr "" -#: __init__.py:45 __init__.py:46 +#: __init__.py:34 __init__.py:35 msgid "re-queue" msgstr "" -#: __init__.py:47 __init__.py:48 __init__.py:63 +#: __init__.py:36 __init__.py:37 __init__.py:50 msgid "delete" msgstr "" -#: __init__.py:50 +#: __init__.py:39 msgid "stop queue" msgstr "" -#: __init__.py:51 +#: __init__.py:40 msgid "activate queue" msgstr "" -#: __init__.py:53 +#: __init__.py:42 msgid "clean up pages content" msgstr "" -#: __init__.py:53 +#: __init__.py:42 msgid "" "Runs a language filter to remove common OCR mistakes from document pages " "content." msgstr "" -#: __init__.py:55 +#: __init__.py:44 msgid "queue document list" msgstr "" -#: __init__.py:58 views.py:316 -msgid "active tasks" +#: __init__.py:45 __init__.py:63 permissions.py:7 +msgid "OCR" msgstr "" -#: __init__.py:60 +#: __init__.py:47 msgid "transformations" msgstr "" -#: __init__.py:61 +#: __init__.py:48 msgid "add transformation" msgstr "" -#: __init__.py:62 +#: __init__.py:49 msgid "edit" msgstr "" -#: __init__.py:82 +#: __init__.py:74 msgid "Default" msgstr "" -#: __init__.py:104 +#: __init__.py:102 msgid "Checks the OCR queue for pending documents." msgstr "" -#: api.py:119 +#: api.py:122 msgid "Text from OCR" msgstr "" @@ -127,88 +99,108 @@ msgstr "" msgid "error" msgstr "" -#: models.py:22 +#: models.py:26 msgid "name" msgstr "" -#: models.py:23 +#: models.py:27 msgid "label" msgstr "" -#: models.py:27 models.py:47 +#: models.py:31 models.py:51 msgid "state" msgstr "" -#: models.py:32 models.py:40 views.py:44 views.py:337 views.py:378 -#: views.py:408 views.py:444 +#: models.py:36 models.py:44 views.py:45 views.py:315 views.py:356 +#: views.py:386 views.py:422 msgid "document queue" msgstr "" -#: models.py:33 +#: models.py:37 msgid "document queues" msgstr "" -#: models.py:41 +#: models.py:45 msgid "document" msgstr "" -#: models.py:42 +#: models.py:46 msgid "date time submitted" msgstr "" -#: models.py:43 +#: models.py:47 msgid "delay ocr" msgstr "" -#: models.py:48 +#: models.py:52 msgid "result" msgstr "" -#: models.py:49 +#: models.py:53 msgid "node name" msgstr "" -#: models.py:53 +#: models.py:57 msgid "queue document" msgstr "" -#: models.py:54 +#: models.py:58 msgid "queue documents" msgstr "" -#: models.py:63 views.py:48 +#: models.py:78 views.py:49 msgid "Missing document." msgstr "" -#: models.py:67 +#: models.py:82 msgid "Enter a valid value." msgstr "" -#: models.py:95 views.py:341 +#: models.py:110 views.py:319 msgid "order" msgstr "" -#: models.py:96 views.py:342 views.py:379 views.py:409 +#: models.py:111 views.py:320 views.py:357 views.py:387 msgid "transformation" msgstr "" -#: models.py:97 views.py:343 +#: models.py:112 views.py:321 msgid "arguments" msgstr "" -#: models.py:97 +#: models.py:112 #, python-format msgid "Use dictionaries to indentify arguments, example: %s" msgstr "" -#: models.py:107 +#: models.py:122 msgid "document queue transformation" msgstr "" -#: models.py:108 +#: models.py:123 msgid "document queue transformations" msgstr "" +#: permissions.py:8 +msgid "Submit documents for OCR" +msgstr "" + +#: permissions.py:9 +msgid "Delete documents from OCR queue" +msgstr "" + +#: permissions.py:10 +msgid "Can enable/disable the OCR queue" +msgstr "" + +#: permissions.py:11 +msgid "Can execute the OCR clean up on all document pages" +msgstr "" + +#: permissions.py:12 +msgid "Can edit an OCR queue properties" +msgstr "" + #: statistics.py:8 #, python-format msgid "Document queues: %d" @@ -223,195 +215,179 @@ msgstr "" msgid "OCR statistics" msgstr "" -#: views.py:41 +#: views.py:42 #, python-format msgid "documents in queue: %s" msgstr "" -#: views.py:49 +#: views.py:50 msgid "thumbnail" msgstr "" -#: views.py:62 +#: views.py:63 msgid "document queue properties" msgstr "" -#: views.py:63 +#: views.py:64 #, python-format msgid "Current state: %s" msgstr "" -#: views.py:79 views.py:154 +#: views.py:80 views.py:168 msgid "Must provide at least one queue document." msgstr "" -#: views.py:89 +#: views.py:90 #, python-format msgid "Document: %s is being processed and can't be deleted." msgstr "" -#: views.py:92 +#: views.py:93 #, python-format msgid "Queue document: %(document)s deleted successfully." msgstr "" -#: views.py:96 +#: views.py:97 #, python-format msgid "Error deleting document: %(document)s; %(error)s" msgstr "" -#: views.py:109 +#: views.py:110 #, python-format msgid "Are you sure you wish to delete queue document: %s?" msgstr "" -#: views.py:111 +#: views.py:112 #, python-format msgid "Are you sure you wish to delete queue documents: %s?" msgstr "" -#: views.py:134 +#: views.py:148 #, python-format msgid "Document: %(document)s was added to the OCR queue: %(queue)s." msgstr "" -#: views.py:137 +#: views.py:151 #, python-format msgid "Document: %(document)s is already queued." msgstr "" -#: views.py:165 -#, python-format -msgid "Document: %s is already being processed and can't be re-queded." -msgstr "" - -#: views.py:173 +#: views.py:180 #, python-format msgid "Document: %(document)s was re-queued to the OCR queue: %(queue)s" msgstr "" -#: views.py:176 +#: views.py:186 #, python-format msgid "Document id#: %d, no longer exists." msgstr "" -#: views.py:189 +#: views.py:190 +#, python-format +msgid "Document: %s is already being processed and can't be re-queded." +msgstr "" + +#: views.py:202 #, python-format msgid "Are you sure you wish to re-queue document: %s?" msgstr "" -#: views.py:191 +#: views.py:204 #, python-format msgid "Are you sure you wish to re-queue documents: %s?" msgstr "" -#: views.py:209 +#: views.py:222 #, python-format msgid "Document queue: %s, already stopped." msgstr "" -#: views.py:215 +#: views.py:228 #, python-format msgid "Document queue: %s, stopped successfully." msgstr "" -#: views.py:221 +#: views.py:234 #, python-format msgid "Are you sure you wish to disable document queue: %s" msgstr "" -#: views.py:236 +#: views.py:249 #, python-format msgid "Document queue: %s, already active." msgstr "" -#: views.py:242 +#: views.py:255 #, python-format msgid "Document queue: %s, activated successfully." msgstr "" -#: views.py:248 +#: views.py:261 #, python-format msgid "Are you sure you wish to activate document queue: %s" msgstr "" -#: views.py:265 +#: views.py:278 msgid "Are you sure you wish to clean up all the pages content?" msgstr "" -#: views.py:266 +#: views.py:279 msgid "On large databases this operation may take some time to execute." msgstr "" -#: views.py:272 +#: views.py:285 msgid "Document pages content clean up complete." msgstr "" -#: views.py:274 +#: views.py:287 #, python-format msgid "Document pages content clean up error: %s" msgstr "" -#: views.py:320 -msgid "node" -msgstr "" - -#: views.py:321 -msgid "task id" -msgstr "" - -#: views.py:322 -msgid "task name" -msgstr "" - -#: views.py:323 -msgid "related object" -msgstr "" - -#: views.py:335 +#: views.py:313 #, python-format msgid "transformations for: %s" msgstr "" -#: views.py:365 +#: views.py:343 msgid "Queue transformation edited successfully" msgstr "" -#: views.py:368 +#: views.py:346 #, python-format msgid "Error editing queue transformation; %s" msgstr "" -#: views.py:373 +#: views.py:351 #, python-format msgid "Edit transformation: %s" msgstr "" -#: views.py:396 +#: views.py:374 msgid "Queue transformation deleted successfully." msgstr "" -#: views.py:398 +#: views.py:376 #, python-format msgid "Error deleting queue transformation; %(error)s" msgstr "" -#: views.py:411 +#: views.py:389 #, python-format msgid "" "Are you sure you wish to delete queue transformation \"%(transformation)s\"" msgstr "" -#: views.py:434 +#: views.py:412 msgid "Queue transformation created successfully" msgstr "" -#: views.py:437 +#: views.py:415 #, python-format msgid "Error creating queue transformation; %s" msgstr "" -#: views.py:446 +#: views.py:424 #, python-format msgid "Create new transformation for queue: %s" msgstr "" @@ -431,15 +407,9 @@ msgid "Automatically queue newly created documents for OCR." msgstr "" #: conf/settings.py:17 -msgid "" -"URI in the form: \"memcached://127.0.0.1:11211/\" to specify a cache backend " -"to use for locking. Multiple hosts can be specified separated by a semicolon." -msgstr "" - -#: conf/settings.py:18 msgid "File path to unpaper program." msgstr "" -#: parsers/__init__.py:23 +#: parsers/__init__.py:37 msgid "Text extracted from PDF" msgstr "" diff --git a/apps/ocr/locale/es/LC_MESSAGES/django.mo b/apps/ocr/locale/es/LC_MESSAGES/django.mo index 9c4c0ed1f3..4b72681b3c 100644 Binary files a/apps/ocr/locale/es/LC_MESSAGES/django.mo and b/apps/ocr/locale/es/LC_MESSAGES/django.mo differ diff --git a/apps/ocr/locale/es/LC_MESSAGES/django.po b/apps/ocr/locale/es/LC_MESSAGES/django.po index 6905387d1e..4c81e3a84c 100644 --- a/apps/ocr/locale/es/LC_MESSAGES/django.po +++ b/apps/ocr/locale/es/LC_MESSAGES/django.po @@ -1,79 +1,48 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: -# Roberto Rosario , 2011. +# Roberto Rosario , 2011, 2012. msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" -"PO-Revision-Date: 2011-11-07 04:17+0000\n" -"Last-Translator: rosarior \n" -"Language-Team: Spanish (Castilian) (http://www.transifex.net/projects/p/" -"mayan-edms/team/es/)\n" -"Language: es\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-12 19:29+0000\n" +"Last-Translator: Roberto Rosario \n" +"Language-Team: Spanish (Castilian) (http://www.transifex.net/projects/p/mayan-edms/team/es/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +"Language: es\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -#: __init__.py:28 -msgid "Submit document for OCR" -msgstr "Enviar documento para OCR" - -#: __init__.py:29 -msgid "Delete document for OCR queue" -msgstr "Eliminar documento de la cola de OCR" - -#: __init__.py:30 -msgid "Can enable/disable an OCR queue" -msgstr "Puede activar / desactivar una cola de OCR" - -#: __init__.py:31 -msgid "Can execute an OCR clean up on all document pages" -msgstr "" -"Puede ejecutar un programa de OCR de limpieza en todas las páginas de los " -"documentos" - -#: __init__.py:32 -msgid "Can edit an OCR queue properties" -msgstr "Puede editar las propiedades de un cola de OCR" - -#: __init__.py:34 __init__.py:56 __init__.py:74 -msgid "OCR" -msgstr "OCR" - -#: __init__.py:40 -msgid "OCR Setup" -msgstr "Configuración de OCR" - -#: __init__.py:44 +#: __init__.py:32 __init__.py:33 msgid "submit to OCR queue" msgstr "enviar a lista de OCR" -#: __init__.py:45 __init__.py:46 +#: __init__.py:34 __init__.py:35 msgid "re-queue" msgstr "volver a la cola" -#: __init__.py:47 __init__.py:48 __init__.py:63 +#: __init__.py:36 __init__.py:37 __init__.py:50 msgid "delete" msgstr "eliminar" -#: __init__.py:50 +#: __init__.py:39 msgid "stop queue" msgstr "detener cola" -#: __init__.py:51 +#: __init__.py:40 msgid "activate queue" msgstr "activar cola" -#: __init__.py:53 +#: __init__.py:42 msgid "clean up pages content" msgstr "limpiar el contenido" -#: __init__.py:53 +#: __init__.py:42 msgid "" "Runs a language filter to remove common OCR mistakes from document pages " "content." @@ -81,35 +50,35 @@ msgstr "" "Ejecuta un filtro de lenguaje para eliminar los errores más comunes de OCR " "del contenido de las paginas del documento." -#: __init__.py:55 +#: __init__.py:44 msgid "queue document list" msgstr "lista de documentos en la cola" -#: __init__.py:58 views.py:316 -msgid "active tasks" -msgstr "tareas activas" +#: __init__.py:45 __init__.py:63 permissions.py:7 +msgid "OCR" +msgstr "OCR" -#: __init__.py:60 +#: __init__.py:47 msgid "transformations" msgstr "transformaciones" -#: __init__.py:61 +#: __init__.py:48 msgid "add transformation" msgstr "añadir transformación" -#: __init__.py:62 +#: __init__.py:49 msgid "edit" msgstr "editar" -#: __init__.py:82 +#: __init__.py:74 msgid "Default" msgstr "Por defecto" -#: __init__.py:104 +#: __init__.py:102 msgid "Checks the OCR queue for pending documents." msgstr "Comprueba la cola de OCR para documentos pendientes." -#: api.py:119 +#: api.py:122 msgid "Text from OCR" msgstr "Texto de OCR" @@ -133,88 +102,108 @@ msgstr "en procesamiento" msgid "error" msgstr "error" -#: models.py:22 +#: models.py:26 msgid "name" msgstr "nombre" -#: models.py:23 +#: models.py:27 msgid "label" msgstr "etiqueta" -#: models.py:27 models.py:47 +#: models.py:31 models.py:51 msgid "state" msgstr "estado" -#: models.py:32 models.py:40 views.py:44 views.py:337 views.py:378 -#: views.py:408 views.py:444 +#: models.py:36 models.py:44 views.py:45 views.py:315 views.py:356 +#: views.py:386 views.py:422 msgid "document queue" msgstr "cola de documento" -#: models.py:33 +#: models.py:37 msgid "document queues" msgstr "colas de documentos" -#: models.py:41 +#: models.py:45 msgid "document" msgstr "documento" -#: models.py:42 +#: models.py:46 msgid "date time submitted" msgstr "fecha y hora sometido" -#: models.py:43 +#: models.py:47 msgid "delay ocr" msgstr "retraso ocr" -#: models.py:48 +#: models.py:52 msgid "result" msgstr "resultado" -#: models.py:49 +#: models.py:53 msgid "node name" msgstr "nombre del nodo" -#: models.py:53 +#: models.py:57 msgid "queue document" msgstr "documento en la cola" -#: models.py:54 +#: models.py:58 msgid "queue documents" msgstr "documentos en la cola" -#: models.py:63 views.py:48 +#: models.py:78 views.py:49 msgid "Missing document." msgstr "Documento no encontrado." -#: models.py:67 +#: models.py:82 msgid "Enter a valid value." msgstr "Introduzca un valor válido." -#: models.py:95 views.py:341 +#: models.py:110 views.py:319 msgid "order" msgstr "orden" -#: models.py:96 views.py:342 views.py:379 views.py:409 +#: models.py:111 views.py:320 views.py:357 views.py:387 msgid "transformation" msgstr "transformación" -#: models.py:97 views.py:343 +#: models.py:112 views.py:321 msgid "arguments" msgstr "argumentos" -#: models.py:97 +#: models.py:112 #, python-format msgid "Use dictionaries to indentify arguments, example: %s" msgstr "Utilize diccionarios para indentificar argumentos, por ejemplo: %s" -#: models.py:107 +#: models.py:122 msgid "document queue transformation" msgstr "transformación de cola de documentos" -#: models.py:108 +#: models.py:123 msgid "document queue transformations" msgstr "transformaciones de cola de documentos" +#: permissions.py:8 +msgid "Submit documents for OCR" +msgstr "Enviar documentos para OCR" + +#: permissions.py:9 +msgid "Delete documents from OCR queue" +msgstr "Eliminar documentos de la cola de OCR" + +#: permissions.py:10 +msgid "Can enable/disable the OCR queue" +msgstr "Puede activar/desactivar la cola de OCR" + +#: permissions.py:11 +msgid "Can execute the OCR clean up on all document pages" +msgstr "Se puede ejecutar limpieza de OCR en todas las páginas de documentos" + +#: permissions.py:12 +msgid "Can edit an OCR queue properties" +msgstr "Puede editar las propiedades de un cola de OCR" + #: statistics.py:8 #, python-format msgid "Document queues: %d" @@ -229,204 +218,189 @@ msgstr "Documentos en la cola: %d" msgid "OCR statistics" msgstr "Estadísticas de OCR" -#: views.py:41 +#: views.py:42 #, python-format msgid "documents in queue: %s" msgstr "documentos en la cola: %s" -#: views.py:49 +#: views.py:50 msgid "thumbnail" msgstr "miniatura" -#: views.py:62 +#: views.py:63 msgid "document queue properties" msgstr "propiedades de la cola de documentos" -#: views.py:63 +#: views.py:64 #, python-format msgid "Current state: %s" msgstr "Estado actual: %s" -#: views.py:79 views.py:154 +#: views.py:80 views.py:168 msgid "Must provide at least one queue document." msgstr "Debe proveer al menos un documento en cola." -#: views.py:89 +#: views.py:90 #, python-format msgid "Document: %s is being processed and can't be deleted." msgstr "El documento: %s ya está en proceso y no se puede se eliminado." -#: views.py:92 +#: views.py:93 #, python-format msgid "Queue document: %(document)s deleted successfully." msgstr "Documento de la cola: %(document)s eliminado exitosamente." -#: views.py:96 +#: views.py:97 #, python-format msgid "Error deleting document: %(document)s; %(error)s" msgstr "Error al eliminar el documento: %(document)s; %(error)s " -#: views.py:109 +#: views.py:110 #, python-format msgid "Are you sure you wish to delete queue document: %s?" msgstr "¿Está seguro que desea eliminar de la cola el documento: %s?" -#: views.py:111 +#: views.py:112 #, python-format msgid "Are you sure you wish to delete queue documents: %s?" msgstr "¿Está seguro que desea eliminar de la cola los documentos: %s?" -#: views.py:134 +#: views.py:148 #, python-format msgid "Document: %(document)s was added to the OCR queue: %(queue)s." msgstr "" "Documento: %(document)s ha sido añadido a la cola de reconocimiento óptico " "de caracteres: %(queue)s." -#: views.py:137 +#: views.py:151 #, python-format msgid "Document: %(document)s is already queued." msgstr "Documento: %(document)s ya está en cola." -#: views.py:165 -#, python-format -msgid "Document: %s is already being processed and can't be re-queded." -msgstr "" -"El documento: %s ya está en proceso y no se puede volver a agregar a la cola." - -#: views.py:173 +#: views.py:180 #, python-format msgid "Document: %(document)s was re-queued to the OCR queue: %(queue)s" msgstr "" "Documento: %(document)s fue puesto de vuelta en la cola de reconocimiento " "óptico de caracteres: %(queue)s " -#: views.py:176 +#: views.py:186 #, python-format msgid "Document id#: %d, no longer exists." msgstr "Documento de #: %d, ya no existe." -#: views.py:189 +#: views.py:190 +#, python-format +msgid "Document: %s is already being processed and can't be re-queded." +msgstr "" +"El documento: %s ya está en proceso y no se puede volver a agregar a la " +"cola." + +#: views.py:202 #, python-format msgid "Are you sure you wish to re-queue document: %s?" msgstr "¿Está seguro desea volver a agregar a la cola el documento: %s?" -#: views.py:191 +#: views.py:204 #, python-format msgid "Are you sure you wish to re-queue documents: %s?" msgstr "¿Está seguro desea volver a agregar a la cola los documento: %s?" -#: views.py:209 +#: views.py:222 #, python-format msgid "Document queue: %s, already stopped." msgstr "Cola de documento: %s, ya esta detenida." -#: views.py:215 +#: views.py:228 #, python-format msgid "Document queue: %s, stopped successfully." msgstr "Cola de documento: %s, se ha detenido con éxito." -#: views.py:221 +#: views.py:234 #, python-format msgid "Are you sure you wish to disable document queue: %s" msgstr "¿Está seguro de que desea desactivar la cola de documento: %s?" -#: views.py:236 +#: views.py:249 #, python-format msgid "Document queue: %s, already active." msgstr "Cola de documento: %s, ya esta activa." -#: views.py:242 +#: views.py:255 #, python-format msgid "Document queue: %s, activated successfully." msgstr "Cola de documento: %s, activada con éxito." -#: views.py:248 +#: views.py:261 #, python-format msgid "Are you sure you wish to activate document queue: %s" msgstr "¿Estás seguro de que desea activar la cola de documento: %s?" -#: views.py:265 +#: views.py:278 msgid "Are you sure you wish to clean up all the pages content?" msgstr "¿Está seguro que desea limpiar el contenido de todas las páginas?" -#: views.py:266 +#: views.py:279 msgid "On large databases this operation may take some time to execute." msgstr "" "En grandes bases de datos esta operación puede tardar algún tiempo en " "ejecutarse." -#: views.py:272 +#: views.py:285 msgid "Document pages content clean up complete." msgstr "Limpieza de contenido completada." -#: views.py:274 +#: views.py:287 #, python-format msgid "Document pages content clean up error: %s" msgstr "Error en limpieza de contenido: %s" -#: views.py:320 -msgid "node" -msgstr "nodo" - -#: views.py:321 -msgid "task id" -msgstr "Identificación de tarea" - -#: views.py:322 -msgid "task name" -msgstr "nombre de tarea" - -#: views.py:323 -msgid "related object" -msgstr "objeto relacionado" - -#: views.py:335 +#: views.py:313 #, python-format msgid "transformations for: %s" msgstr "transformaciones para: %s" -#: views.py:365 +#: views.py:343 msgid "Queue transformation edited successfully" msgstr "Transformación de la cola editada con exitosamente" -#: views.py:368 +#: views.py:346 #, python-format msgid "Error editing queue transformation; %s" msgstr "Error al modificar la transformación de cola; %s" -#: views.py:373 +#: views.py:351 #, python-format msgid "Edit transformation: %s" msgstr "Editar transformación: %s" -#: views.py:396 +#: views.py:374 msgid "Queue transformation deleted successfully." msgstr "Transformación de la cola borrada exitosamente." -#: views.py:398 +#: views.py:376 #, python-format msgid "Error deleting queue transformation; %(error)s" msgstr "Error al tratar de borrar la transformación de cola; %(error)s " -#: views.py:411 +#: views.py:389 #, python-format msgid "" "Are you sure you wish to delete queue transformation \"%(transformation)s\"" msgstr "" -"¿Está seguro que desea borrar la transformación de cola \"%(transformation)s" -"\"" +"¿Está seguro que desea borrar la transformación de cola " +"\"%(transformation)s\"" -#: views.py:434 +#: views.py:412 msgid "Queue transformation created successfully" msgstr "Transformación de cola creada exitosamente" -#: views.py:437 +#: views.py:415 #, python-format msgid "Error creating queue transformation; %s" msgstr "Error al crear la transformación de cola; %s" -#: views.py:446 +#: views.py:424 #, python-format msgid "Create new transformation for queue: %s" msgstr "Crear nueva transformación para la cola: %s" @@ -450,18 +424,11 @@ msgid "Automatically queue newly created documents for OCR." msgstr "Agregar automáticamente la cola de OCR los documentos creados." #: conf/settings.py:17 -msgid "" -"URI in the form: \"memcached://127.0.0.1:11211/\" to specify a cache backend " -"to use for locking. Multiple hosts can be specified separated by a semicolon." -msgstr "" -"URI de la forma: \"memcached:/ /127.0.0.1:11211/\" para especificar un " -"servidor de caché para usar de bloqueo. Multiple servidores se pueden " -"especificar separados por un punto y coma." - -#: conf/settings.py:18 msgid "File path to unpaper program." msgstr "La ruta de archivo del programa unpaper." -#: parsers/__init__.py:23 +#: parsers/__init__.py:37 msgid "Text extracted from PDF" msgstr "Texto extraído de PDF" + + diff --git a/apps/ocr/locale/it/LC_MESSAGES/django.mo b/apps/ocr/locale/it/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..75ac3ea7ef Binary files /dev/null and b/apps/ocr/locale/it/LC_MESSAGES/django.mo differ diff --git a/apps/ocr/locale/it/LC_MESSAGES/django.po b/apps/ocr/locale/it/LC_MESSAGES/django.po new file mode 100644 index 0000000000..18fd6b973a --- /dev/null +++ b/apps/ocr/locale/it/LC_MESSAGES/django.po @@ -0,0 +1,426 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# , 2011. +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-02 18:19+0000\n" +"Last-Translator: Roberto Rosario \n" +"Language-Team: Italian (http://www.transifex.net/projects/p/mayan-edms/team/" +"it/)\n" +"Language: it\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:32 __init__.py:33 +msgid "submit to OCR queue" +msgstr "Sottoponi una coda di OCR" + +#: __init__.py:34 __init__.py:35 +msgid "re-queue" +msgstr "riaccoda" + +#: __init__.py:36 __init__.py:37 __init__.py:50 +msgid "delete" +msgstr "cancella" + +#: __init__.py:39 +msgid "stop queue" +msgstr "stoppa la coda" + +#: __init__.py:40 +msgid "activate queue" +msgstr "attiva la coda" + +#: __init__.py:42 +msgid "clean up pages content" +msgstr "ripulisci il contenuto delle pagine" + +#: __init__.py:42 +msgid "" +"Runs a language filter to remove common OCR mistakes from document pages " +"content." +msgstr "" +"Esegue un filtro per rimuovere i comuni errori di OCR dal contenuto del " +"documento pagine." + +#: __init__.py:44 +msgid "queue document list" +msgstr "lista dei documenti in coda" + +#: __init__.py:45 __init__.py:63 permissions.py:7 +msgid "OCR" +msgstr "OCR" + +#: __init__.py:47 +msgid "transformations" +msgstr "transformazioni" + +#: __init__.py:48 +msgid "add transformation" +msgstr "aggiungi trasformazione" + +#: __init__.py:49 +msgid "edit" +msgstr "modifica" + +#: __init__.py:74 +msgid "Default" +msgstr "Default" + +#: __init__.py:102 +msgid "Checks the OCR queue for pending documents." +msgstr "Controlla i documenti nella coda dell'OCR" + +#: api.py:122 +msgid "Text from OCR" +msgstr "testo dall'OCR" + +#: literals.py:8 +msgid "stopped" +msgstr "fermato" + +#: literals.py:9 +msgid "active" +msgstr "attivo" + +#: literals.py:18 +msgid "pending" +msgstr "in esecuzione" + +#: literals.py:19 +msgid "processing" +msgstr "in elaborazione" + +#: literals.py:20 +msgid "error" +msgstr "errore" + +#: models.py:26 +msgid "name" +msgstr "nome" + +#: models.py:27 +msgid "label" +msgstr "etichetta" + +#: models.py:31 models.py:51 +msgid "state" +msgstr "stato" + +#: models.py:36 models.py:44 views.py:45 views.py:315 views.py:356 +#: views.py:386 views.py:422 +msgid "document queue" +msgstr "coda del documento" + +#: models.py:37 +msgid "document queues" +msgstr "code di documenti" + +#: models.py:45 +msgid "document" +msgstr "documento" + +#: models.py:46 +msgid "date time submitted" +msgstr "orario di esecuzione" + +#: models.py:47 +msgid "delay ocr" +msgstr "proroga ocr" + +#: models.py:52 +msgid "result" +msgstr "risultato" + +#: models.py:53 +msgid "node name" +msgstr "nome del nodo" + +#: models.py:57 +msgid "queue document" +msgstr "coda del documento" + +#: models.py:58 +msgid "queue documents" +msgstr "code dei documenti" + +#: models.py:78 views.py:49 +msgid "Missing document." +msgstr "Documento perso" + +#: models.py:82 +msgid "Enter a valid value." +msgstr "Inserisci un valore valido" + +#: models.py:110 views.py:319 +msgid "order" +msgstr "ordina" + +#: models.py:111 views.py:320 views.py:357 views.py:387 +msgid "transformation" +msgstr "trasforma" + +#: models.py:112 views.py:321 +msgid "arguments" +msgstr "argomenti" + +#: models.py:112 +#, python-format +msgid "Use dictionaries to indentify arguments, example: %s" +msgstr "Usa un dizionario per identificare gli argomenti, esempio: %s" + +#: models.py:122 +msgid "document queue transformation" +msgstr "coda del documento in trasformazione" + +#: models.py:123 +msgid "document queue transformations" +msgstr "code dei documenti in trasformazione" + +#: permissions.py:8 +msgid "Submit documents for OCR" +msgstr "" + +#: permissions.py:9 +msgid "Delete documents from OCR queue" +msgstr "" + +#: permissions.py:10 +msgid "Can enable/disable the OCR queue" +msgstr "" + +#: permissions.py:11 +msgid "Can execute the OCR clean up on all document pages" +msgstr "" + +#: permissions.py:12 +msgid "Can edit an OCR queue properties" +msgstr "Posso modificare le proprità della coda di OCR" + +#: statistics.py:8 +#, python-format +msgid "Document queues: %d" +msgstr "Coda documento:%d" + +#: statistics.py:9 +#, python-format +msgid "Queued documents: %d" +msgstr "Code di documenti:%d" + +#: statistics.py:13 +msgid "OCR statistics" +msgstr "Statistiche OCR" + +#: views.py:42 +#, python-format +msgid "documents in queue: %s" +msgstr "documenti in coda: %s" + +#: views.py:50 +msgid "thumbnail" +msgstr "thumbnail" + +#: views.py:63 +msgid "document queue properties" +msgstr "proprietà della coda documenti" + +#: views.py:64 +#, python-format +msgid "Current state: %s" +msgstr "Stato corrente: %s" + +#: views.py:80 views.py:168 +msgid "Must provide at least one queue document." +msgstr "Deve fornire almeno un documento di coda." + +#: views.py:90 +#, python-format +msgid "Document: %s is being processed and can't be deleted." +msgstr "Il document: %s è in elaborazione e non può essere cancellato." + +#: views.py:93 +#, python-format +msgid "Queue document: %(document)s deleted successfully." +msgstr "Coda documento: %(document)s cancellata con successo." + +#: views.py:97 +#, python-format +msgid "Error deleting document: %(document)s; %(error)s" +msgstr "Errore nella cancellazione del documento: %(document)s; %(error)s" + +#: views.py:110 +#, python-format +msgid "Are you sure you wish to delete queue document: %s?" +msgstr "Sei sicuro di voler cancellare questa coda documento: %s?" + +#: views.py:112 +#, python-format +msgid "Are you sure you wish to delete queue documents: %s?" +msgstr "Sei sicuro di voler cancellare queste code documento: %s?" + +#: views.py:148 +#, python-format +msgid "Document: %(document)s was added to the OCR queue: %(queue)s." +msgstr "" +"Il documento: %(document)s è stato aggiunto alla coda %(queue)s per OCR." + +#: views.py:151 +#, python-format +msgid "Document: %(document)s is already queued." +msgstr "Il documento: %(document)s è gia stato elaborato." + +#: views.py:180 +#, python-format +msgid "Document: %(document)s was re-queued to the OCR queue: %(queue)s" +msgstr "Il documento: %(document)s è stato riprocessato %(queue)s per OCR." + +#: views.py:186 +#, python-format +msgid "Document id#: %d, no longer exists." +msgstr "il documento id#: %d,non esiste più." + +#: views.py:190 +#, python-format +msgid "Document: %s is already being processed and can't be re-queded." +msgstr "Il documento: %s è gia stato processato non può essere riprocessato." + +#: views.py:202 +#, python-format +msgid "Are you sure you wish to re-queue document: %s?" +msgstr "Sei sicuro di volere riprocessare il documento: %s?" + +#: views.py:204 +#, python-format +msgid "Are you sure you wish to re-queue documents: %s?" +msgstr "Sei sicuro di volere riprocessare questi documenti: %s?" + +#: views.py:222 +#, python-format +msgid "Document queue: %s, already stopped." +msgstr "Questa coda: %s, è stata appena fermata." + +#: views.py:228 +#, python-format +msgid "Document queue: %s, stopped successfully." +msgstr "Questa coda: %s,è stata fermata con successo." + +#: views.py:234 +#, python-format +msgid "Are you sure you wish to disable document queue: %s" +msgstr "Sei sicuro di voler fermare la coda per il documento: %s" + +#: views.py:249 +#, python-format +msgid "Document queue: %s, already active." +msgstr "La coda per questo documento: %s, è già attiva." + +#: views.py:255 +#, python-format +msgid "Document queue: %s, activated successfully." +msgstr "Coda documento: %s, attivata con successo." + +#: views.py:261 +#, python-format +msgid "Are you sure you wish to activate document queue: %s" +msgstr "Sei sicuro di voler attivare questa coda documento: %s" + +#: views.py:278 +msgid "Are you sure you wish to clean up all the pages content?" +msgstr "Sei sicuro di voler ripulire tutto il contenuto delle pagine?" + +#: views.py:279 +msgid "On large databases this operation may take some time to execute." +msgstr "Nei database grandi questa operazione può richiedere del tempo." + +#: views.py:285 +msgid "Document pages content clean up complete." +msgstr "Pulizia del contenuto delle pagine completata." + +#: views.py:287 +#, python-format +msgid "Document pages content clean up error: %s" +msgstr "Errore nella pulizia del contenuto delle pagine: %s" + +#: views.py:313 +#, python-format +msgid "transformations for: %s" +msgstr "trasformazione per: %s" + +#: views.py:343 +msgid "Queue transformation edited successfully" +msgstr "Modifica della coda di trasformazione effettuata con successo" + +#: views.py:346 +#, python-format +msgid "Error editing queue transformation; %s" +msgstr "Errore nella modifica alla coda di trasformazione; %s" + +#: views.py:351 +#, python-format +msgid "Edit transformation: %s" +msgstr "Modifica trasformazioni:%s" + +#: views.py:374 +msgid "Queue transformation deleted successfully." +msgstr "Coda di trasformazione cancellata con successo" + +#: views.py:376 +#, python-format +msgid "Error deleting queue transformation; %(error)s" +msgstr "Errore nella cancellazione della coda di trasformazione; %(error)s" + +#: views.py:389 +#, python-format +msgid "" +"Are you sure you wish to delete queue transformation \"%(transformation)s\"" +msgstr "" +"Sei sicuro di voler cancellare la coda di trasformazione \"%(transformation)s" +"\"" + +#: views.py:412 +msgid "Queue transformation created successfully" +msgstr "Coda di trasformazione creata con successo" + +#: views.py:415 +#, python-format +msgid "Error creating queue transformation; %s" +msgstr "Errore creano la coda di trasformazione; %s" + +#: views.py:424 +#, python-format +msgid "Create new transformation for queue: %s" +msgstr "Crea una nuova coda di trasformazione:%s" + +#: conf/settings.py:13 +msgid "" +"Amount of seconds to delay OCR of documents to allow for the node's storage " +"replication overhead." +msgstr "" +"Quantità di secondi di ritardo OCR di documenti per consentire lo stoccaggio " +"nel nodo di replica." + +#: conf/settings.py:14 +msgid "Maximum amount of concurrent document OCRs a node can perform." +msgstr "" +"Importo massimo di documenti concorrenti per OCR che un nodo è in grado di " +"eseguire." + +#: conf/settings.py:15 +msgid "Automatically queue newly created documents for OCR." +msgstr "Automaticamente crea una coda appena si sottomone un documento ad OCR." + +#: conf/settings.py:17 +msgid "File path to unpaper program." +msgstr "File path per il programma unpaper" + +#: parsers/__init__.py:37 +msgid "Text extracted from PDF" +msgstr "Testo estratto da PDF" diff --git a/apps/ocr/locale/pl/LC_MESSAGES/django.mo b/apps/ocr/locale/pl/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..25c2414931 Binary files /dev/null and b/apps/ocr/locale/pl/LC_MESSAGES/django.mo differ diff --git a/apps/ocr/locale/pl/LC_MESSAGES/django.po b/apps/ocr/locale/pl/LC_MESSAGES/django.po new file mode 100644 index 0000000000..a42db9d45d --- /dev/null +++ b/apps/ocr/locale/pl/LC_MESSAGES/django.po @@ -0,0 +1,415 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2011-09-30 04:41+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Polish (http://www.transifex.net/projects/p/mayan-edms/language/pl/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pl\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" + +#: __init__.py:32 __init__.py:33 +msgid "submit to OCR queue" +msgstr "" + +#: __init__.py:34 __init__.py:35 +msgid "re-queue" +msgstr "" + +#: __init__.py:36 __init__.py:37 __init__.py:50 +msgid "delete" +msgstr "" + +#: __init__.py:39 +msgid "stop queue" +msgstr "" + +#: __init__.py:40 +msgid "activate queue" +msgstr "" + +#: __init__.py:42 +msgid "clean up pages content" +msgstr "" + +#: __init__.py:42 +msgid "" +"Runs a language filter to remove common OCR mistakes from document pages " +"content." +msgstr "" + +#: __init__.py:44 +msgid "queue document list" +msgstr "" + +#: __init__.py:45 __init__.py:63 permissions.py:7 +msgid "OCR" +msgstr "" + +#: __init__.py:47 +msgid "transformations" +msgstr "" + +#: __init__.py:48 +msgid "add transformation" +msgstr "" + +#: __init__.py:49 +msgid "edit" +msgstr "" + +#: __init__.py:74 +msgid "Default" +msgstr "" + +#: __init__.py:102 +msgid "Checks the OCR queue for pending documents." +msgstr "" + +#: api.py:122 +msgid "Text from OCR" +msgstr "" + +#: literals.py:8 +msgid "stopped" +msgstr "" + +#: literals.py:9 +msgid "active" +msgstr "" + +#: literals.py:18 +msgid "pending" +msgstr "" + +#: literals.py:19 +msgid "processing" +msgstr "" + +#: literals.py:20 +msgid "error" +msgstr "" + +#: models.py:26 +msgid "name" +msgstr "" + +#: models.py:27 +msgid "label" +msgstr "" + +#: models.py:31 models.py:51 +msgid "state" +msgstr "" + +#: models.py:36 models.py:44 views.py:45 views.py:315 views.py:356 +#: views.py:386 views.py:422 +msgid "document queue" +msgstr "" + +#: models.py:37 +msgid "document queues" +msgstr "" + +#: models.py:45 +msgid "document" +msgstr "" + +#: models.py:46 +msgid "date time submitted" +msgstr "" + +#: models.py:47 +msgid "delay ocr" +msgstr "" + +#: models.py:52 +msgid "result" +msgstr "" + +#: models.py:53 +msgid "node name" +msgstr "" + +#: models.py:57 +msgid "queue document" +msgstr "" + +#: models.py:58 +msgid "queue documents" +msgstr "" + +#: models.py:78 views.py:49 +msgid "Missing document." +msgstr "" + +#: models.py:82 +msgid "Enter a valid value." +msgstr "" + +#: models.py:110 views.py:319 +msgid "order" +msgstr "" + +#: models.py:111 views.py:320 views.py:357 views.py:387 +msgid "transformation" +msgstr "" + +#: models.py:112 views.py:321 +msgid "arguments" +msgstr "" + +#: models.py:112 +#, python-format +msgid "Use dictionaries to indentify arguments, example: %s" +msgstr "" + +#: models.py:122 +msgid "document queue transformation" +msgstr "" + +#: models.py:123 +msgid "document queue transformations" +msgstr "" + +#: permissions.py:8 +msgid "Submit documents for OCR" +msgstr "" + +#: permissions.py:9 +msgid "Delete documents from OCR queue" +msgstr "" + +#: permissions.py:10 +msgid "Can enable/disable the OCR queue" +msgstr "" + +#: permissions.py:11 +msgid "Can execute the OCR clean up on all document pages" +msgstr "" + +#: permissions.py:12 +msgid "Can edit an OCR queue properties" +msgstr "" + +#: statistics.py:8 +#, python-format +msgid "Document queues: %d" +msgstr "" + +#: statistics.py:9 +#, python-format +msgid "Queued documents: %d" +msgstr "" + +#: statistics.py:13 +msgid "OCR statistics" +msgstr "" + +#: views.py:42 +#, python-format +msgid "documents in queue: %s" +msgstr "" + +#: views.py:50 +msgid "thumbnail" +msgstr "" + +#: views.py:63 +msgid "document queue properties" +msgstr "" + +#: views.py:64 +#, python-format +msgid "Current state: %s" +msgstr "" + +#: views.py:80 views.py:168 +msgid "Must provide at least one queue document." +msgstr "" + +#: views.py:90 +#, python-format +msgid "Document: %s is being processed and can't be deleted." +msgstr "" + +#: views.py:93 +#, python-format +msgid "Queue document: %(document)s deleted successfully." +msgstr "" + +#: views.py:97 +#, python-format +msgid "Error deleting document: %(document)s; %(error)s" +msgstr "" + +#: views.py:110 +#, python-format +msgid "Are you sure you wish to delete queue document: %s?" +msgstr "" + +#: views.py:112 +#, python-format +msgid "Are you sure you wish to delete queue documents: %s?" +msgstr "" + +#: views.py:148 +#, python-format +msgid "Document: %(document)s was added to the OCR queue: %(queue)s." +msgstr "" + +#: views.py:151 +#, python-format +msgid "Document: %(document)s is already queued." +msgstr "" + +#: views.py:180 +#, python-format +msgid "Document: %(document)s was re-queued to the OCR queue: %(queue)s" +msgstr "" + +#: views.py:186 +#, python-format +msgid "Document id#: %d, no longer exists." +msgstr "" + +#: views.py:190 +#, python-format +msgid "Document: %s is already being processed and can't be re-queded." +msgstr "" + +#: views.py:202 +#, python-format +msgid "Are you sure you wish to re-queue document: %s?" +msgstr "" + +#: views.py:204 +#, python-format +msgid "Are you sure you wish to re-queue documents: %s?" +msgstr "" + +#: views.py:222 +#, python-format +msgid "Document queue: %s, already stopped." +msgstr "" + +#: views.py:228 +#, python-format +msgid "Document queue: %s, stopped successfully." +msgstr "" + +#: views.py:234 +#, python-format +msgid "Are you sure you wish to disable document queue: %s" +msgstr "" + +#: views.py:249 +#, python-format +msgid "Document queue: %s, already active." +msgstr "" + +#: views.py:255 +#, python-format +msgid "Document queue: %s, activated successfully." +msgstr "" + +#: views.py:261 +#, python-format +msgid "Are you sure you wish to activate document queue: %s" +msgstr "" + +#: views.py:278 +msgid "Are you sure you wish to clean up all the pages content?" +msgstr "" + +#: views.py:279 +msgid "On large databases this operation may take some time to execute." +msgstr "" + +#: views.py:285 +msgid "Document pages content clean up complete." +msgstr "" + +#: views.py:287 +#, python-format +msgid "Document pages content clean up error: %s" +msgstr "" + +#: views.py:313 +#, python-format +msgid "transformations for: %s" +msgstr "" + +#: views.py:343 +msgid "Queue transformation edited successfully" +msgstr "" + +#: views.py:346 +#, python-format +msgid "Error editing queue transformation; %s" +msgstr "" + +#: views.py:351 +#, python-format +msgid "Edit transformation: %s" +msgstr "" + +#: views.py:374 +msgid "Queue transformation deleted successfully." +msgstr "" + +#: views.py:376 +#, python-format +msgid "Error deleting queue transformation; %(error)s" +msgstr "" + +#: views.py:389 +#, python-format +msgid "" +"Are you sure you wish to delete queue transformation \"%(transformation)s\"" +msgstr "" + +#: views.py:412 +msgid "Queue transformation created successfully" +msgstr "" + +#: views.py:415 +#, python-format +msgid "Error creating queue transformation; %s" +msgstr "" + +#: views.py:424 +#, python-format +msgid "Create new transformation for queue: %s" +msgstr "" + +#: conf/settings.py:13 +msgid "" +"Amount of seconds to delay OCR of documents to allow for the node's storage " +"replication overhead." +msgstr "" + +#: conf/settings.py:14 +msgid "Maximum amount of concurrent document OCRs a node can perform." +msgstr "" + +#: conf/settings.py:15 +msgid "Automatically queue newly created documents for OCR." +msgstr "" + +#: conf/settings.py:17 +msgid "File path to unpaper program." +msgstr "" + +#: parsers/__init__.py:37 +msgid "Text extracted from PDF" +msgstr "" diff --git a/apps/ocr/locale/pt/LC_MESSAGES/django.mo b/apps/ocr/locale/pt/LC_MESSAGES/django.mo index 065b53a37a..7bf81b457b 100644 Binary files a/apps/ocr/locale/pt/LC_MESSAGES/django.mo and b/apps/ocr/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/ocr/locale/pt/LC_MESSAGES/django.po b/apps/ocr/locale/pt/LC_MESSAGES/django.po index a7d598736f..c6418c4f40 100644 --- a/apps/ocr/locale/pt/LC_MESSAGES/django.po +++ b/apps/ocr/locale/pt/LC_MESSAGES/django.po @@ -9,9 +9,9 @@ msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" -"PO-Revision-Date: 2011-11-02 05:10+0000\n" -"Last-Translator: emersonsoares \n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-02 18:19+0000\n" +"Last-Translator: Roberto Rosario \n" "Language-Team: Portuguese (http://www.transifex.net/projects/p/mayan-edms/" "team/pt/)\n" "Language: pt\n" @@ -20,59 +20,31 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -#: __init__.py:28 -msgid "Submit document for OCR" -msgstr "Submeter documento para OCR" - -#: __init__.py:29 -msgid "Delete document for OCR queue" -msgstr "Excluir documento da lista de OCR" - -#: __init__.py:30 -msgid "Can enable/disable an OCR queue" -msgstr "Pode ativar/desativar uma lista de OCR" - -#: __init__.py:31 -msgid "Can execute an OCR clean up on all document pages" -msgstr "Pode executar uma limpeza de OCR em todas as páginas do documento" - -#: __init__.py:32 -msgid "Can edit an OCR queue properties" -msgstr "Pode editar uma lista de propriedades OCR" - -#: __init__.py:34 __init__.py:56 __init__.py:74 -msgid "OCR" -msgstr "OCR" - -#: __init__.py:40 -msgid "OCR Setup" -msgstr "Configuração de OCR" - -#: __init__.py:44 +#: __init__.py:32 __init__.py:33 msgid "submit to OCR queue" msgstr "submeter à lista de OCR" -#: __init__.py:45 __init__.py:46 +#: __init__.py:34 __init__.py:35 msgid "re-queue" msgstr "re-enfileirar" -#: __init__.py:47 __init__.py:48 __init__.py:63 +#: __init__.py:36 __init__.py:37 __init__.py:50 msgid "delete" msgstr "excluir" -#: __init__.py:50 +#: __init__.py:39 msgid "stop queue" msgstr "parar lista" -#: __init__.py:51 +#: __init__.py:40 msgid "activate queue" msgstr "ativar lista" -#: __init__.py:53 +#: __init__.py:42 msgid "clean up pages content" msgstr "limpar conteúdo das páginas" -#: __init__.py:53 +#: __init__.py:42 msgid "" "Runs a language filter to remove common OCR mistakes from document pages " "content." @@ -80,35 +52,35 @@ msgstr "" "Executa um filtro de linguagem para remover erros comuns de OCR do conteúdo " "das páginas do documento." -#: __init__.py:55 +#: __init__.py:44 msgid "queue document list" msgstr "lista de documentos da fila" -#: __init__.py:58 views.py:316 -msgid "active tasks" -msgstr "tarefas ativas" +#: __init__.py:45 __init__.py:63 permissions.py:7 +msgid "OCR" +msgstr "OCR" -#: __init__.py:60 +#: __init__.py:47 msgid "transformations" msgstr "transformações" -#: __init__.py:61 +#: __init__.py:48 msgid "add transformation" msgstr "adicionar transformação" -#: __init__.py:62 +#: __init__.py:49 msgid "edit" msgstr "editar" -#: __init__.py:82 +#: __init__.py:74 msgid "Default" msgstr "Padrão" -#: __init__.py:104 +#: __init__.py:102 msgid "Checks the OCR queue for pending documents." msgstr "Verifica a lista de OCR para documentos pendentes." -#: api.py:119 +#: api.py:122 msgid "Text from OCR" msgstr "Texto de OCR" @@ -132,88 +104,108 @@ msgstr "processamento" msgid "error" msgstr "erro" -#: models.py:22 +#: models.py:26 msgid "name" msgstr "nome" -#: models.py:23 +#: models.py:27 msgid "label" msgstr "rótulo" -#: models.py:27 models.py:47 +#: models.py:31 models.py:51 msgid "state" msgstr "estado" -#: models.py:32 models.py:40 views.py:44 views.py:337 views.py:378 -#: views.py:408 views.py:444 +#: models.py:36 models.py:44 views.py:45 views.py:315 views.py:356 +#: views.py:386 views.py:422 msgid "document queue" msgstr "lista de documento" -#: models.py:33 +#: models.py:37 msgid "document queues" msgstr "listas de documento" -#: models.py:41 +#: models.py:45 msgid "document" msgstr "documento" -#: models.py:42 +#: models.py:46 msgid "date time submitted" msgstr "data e hora submetido" -#: models.py:43 +#: models.py:47 msgid "delay ocr" msgstr "atraso ocr" -#: models.py:48 +#: models.py:52 msgid "result" msgstr "resultado" -#: models.py:49 +#: models.py:53 msgid "node name" msgstr "nome do nó" -#: models.py:53 +#: models.py:57 msgid "queue document" msgstr "enfileirar documento" -#: models.py:54 +#: models.py:58 msgid "queue documents" msgstr "enfileirar documentos" -#: models.py:63 views.py:48 +#: models.py:78 views.py:49 msgid "Missing document." msgstr "Falta de documento." -#: models.py:67 +#: models.py:82 msgid "Enter a valid value." msgstr "Digite um valor válido." -#: models.py:95 views.py:341 +#: models.py:110 views.py:319 msgid "order" msgstr "ordem" -#: models.py:96 views.py:342 views.py:379 views.py:409 +#: models.py:111 views.py:320 views.py:357 views.py:387 msgid "transformation" msgstr "transformação" -#: models.py:97 views.py:343 +#: models.py:112 views.py:321 msgid "arguments" msgstr "argumentos" -#: models.py:97 +#: models.py:112 #, python-format msgid "Use dictionaries to indentify arguments, example: %s" msgstr "Use dicionários para identificar argumentos, exemplo: %s" -#: models.py:107 +#: models.py:122 msgid "document queue transformation" msgstr "transformação de lista de documento ocr" -#: models.py:108 +#: models.py:123 msgid "document queue transformations" msgstr "transformações de listas de documentos" +#: permissions.py:8 +msgid "Submit documents for OCR" +msgstr "" + +#: permissions.py:9 +msgid "Delete documents from OCR queue" +msgstr "" + +#: permissions.py:10 +msgid "Can enable/disable the OCR queue" +msgstr "" + +#: permissions.py:11 +msgid "Can execute the OCR clean up on all document pages" +msgstr "" + +#: permissions.py:12 +msgid "Can edit an OCR queue properties" +msgstr "Pode editar uma lista de propriedades OCR" + #: statistics.py:8 #, python-format msgid "Document queues: %d" @@ -228,182 +220,166 @@ msgstr "Documentos na lista: %d" msgid "OCR statistics" msgstr "estatísticas de OCR" -#: views.py:41 +#: views.py:42 #, python-format msgid "documents in queue: %s" msgstr "documentos na lista: %s" -#: views.py:49 +#: views.py:50 msgid "thumbnail" msgstr "miniatura" -#: views.py:62 +#: views.py:63 msgid "document queue properties" msgstr "propriedades da lista de documento" -#: views.py:63 +#: views.py:64 #, python-format msgid "Current state: %s" msgstr "Estado atual: %s" -#: views.py:79 views.py:154 +#: views.py:80 views.py:168 msgid "Must provide at least one queue document." msgstr "Deve fornecer pelo menos um documento da lista." -#: views.py:89 +#: views.py:90 #, python-format msgid "Document: %s is being processed and can't be deleted." msgstr "Documento: %s está sendo processado e não pode ser excluído." -#: views.py:92 +#: views.py:93 #, python-format msgid "Queue document: %(document)s deleted successfully." msgstr "Lista de documento: %(document)s removido com sucesso." -#: views.py:96 +#: views.py:97 #, python-format msgid "Error deleting document: %(document)s; %(error)s" msgstr "Erro ao excluir documento: %(document)s; %(error)s " -#: views.py:109 +#: views.py:110 #, python-format msgid "Are you sure you wish to delete queue document: %s?" msgstr "Você tem certeza que deseja deletar o documento da lista: %s?" -#: views.py:111 +#: views.py:112 #, python-format msgid "Are you sure you wish to delete queue documents: %s?" msgstr "Tem certeza de que deseja excluir os documentos da lista: %s?" -#: views.py:134 +#: views.py:148 #, python-format msgid "Document: %(document)s was added to the OCR queue: %(queue)s." msgstr "Documento: %(document)s foi adicionado à lista de OCR: %(queue)s ." -#: views.py:137 +#: views.py:151 #, python-format msgid "Document: %(document)s is already queued." msgstr "Documento: %(document)s já está na lista." -#: views.py:165 -#, python-format -msgid "Document: %s is already being processed and can't be re-queded." -msgstr "Documento: %s já está sendo processado e não pode ser re-enfileirado." - -#: views.py:173 +#: views.py:180 #, python-format msgid "Document: %(document)s was re-queued to the OCR queue: %(queue)s" msgstr "" "Documento: %(document)s foi re-enfileirado para a lista de OCR: %(queue)s " -#: views.py:176 +#: views.py:186 #, python-format msgid "Document id#: %d, no longer exists." msgstr "#ID do documento: %d, não existe mais." -#: views.py:189 +#: views.py:190 +#, python-format +msgid "Document: %s is already being processed and can't be re-queded." +msgstr "Documento: %s já está sendo processado e não pode ser re-enfileirado." + +#: views.py:202 #, python-format msgid "Are you sure you wish to re-queue document: %s?" msgstr "Tem certeza de que deseja re-enfileirar o documento: %s?" -#: views.py:191 +#: views.py:204 #, python-format msgid "Are you sure you wish to re-queue documents: %s?" msgstr "Tem certeza de que deseja voltar a lista de documentos: %s?" -#: views.py:209 +#: views.py:222 #, python-format msgid "Document queue: %s, already stopped." msgstr "Lista de documento: %s, já está parado." -#: views.py:215 +#: views.py:228 #, python-format msgid "Document queue: %s, stopped successfully." msgstr "Lista de documento: %s, parada com êxito." -#: views.py:221 +#: views.py:234 #, python-format msgid "Are you sure you wish to disable document queue: %s" msgstr "Tem certeza de que deseja desativar a lista de documento: %s" -#: views.py:236 +#: views.py:249 #, python-format msgid "Document queue: %s, already active." msgstr "Lista de documento: %s, já está ativa." -#: views.py:242 +#: views.py:255 #, python-format msgid "Document queue: %s, activated successfully." msgstr "Lista de documento: %s, ativada com sucesso." -#: views.py:248 +#: views.py:261 #, python-format msgid "Are you sure you wish to activate document queue: %s" msgstr "Tem certeza de que deseja ativar a lista de documento: %s" -#: views.py:265 +#: views.py:278 msgid "Are you sure you wish to clean up all the pages content?" msgstr "Tem certeza de que deseja limpar todo o conteúdo das páginas?" -#: views.py:266 +#: views.py:279 msgid "On large databases this operation may take some time to execute." msgstr "" "Em grandes bases de dados esta operação pode levar algum tempo para executar." -#: views.py:272 +#: views.py:285 msgid "Document pages content clean up complete." msgstr "Limpeza do conteúdo das páginas do documento completa." -#: views.py:274 +#: views.py:287 #, python-format msgid "Document pages content clean up error: %s" msgstr "Erro ao limpar o conteúdo das páginas do documento: %s" -#: views.py:320 -msgid "node" -msgstr "nó" - -#: views.py:321 -msgid "task id" -msgstr "id da tarefa" - -#: views.py:322 -msgid "task name" -msgstr "nome da tarefa" - -#: views.py:323 -msgid "related object" -msgstr "objeto relacionado" - -#: views.py:335 +#: views.py:313 #, python-format msgid "transformations for: %s" msgstr "transformações para: %s" -#: views.py:365 +#: views.py:343 msgid "Queue transformation edited successfully" msgstr "Transformação da lista editada com sucesso" -#: views.py:368 +#: views.py:346 #, python-format msgid "Error editing queue transformation; %s" msgstr "Erro ao editar transformação da lista; %s" -#: views.py:373 +#: views.py:351 #, python-format msgid "Edit transformation: %s" msgstr "Editar Transformação: %s" -#: views.py:396 +#: views.py:374 msgid "Queue transformation deleted successfully." msgstr "Transformação de lista apagada com sucesso." -#: views.py:398 +#: views.py:376 #, python-format msgid "Error deleting queue transformation; %(error)s" msgstr "Erro ao deletar transformação de lista; %(error)s " -#: views.py:411 +#: views.py:389 #, python-format msgid "" "Are you sure you wish to delete queue transformation \"%(transformation)s\"" @@ -411,16 +387,16 @@ msgstr "" "Tem certeza que deseja deletar a transformação de lista \"%(transformation)s" "\"" -#: views.py:434 +#: views.py:412 msgid "Queue transformation created successfully" msgstr "Transformação de lista criada com sucesso" -#: views.py:437 +#: views.py:415 #, python-format msgid "Error creating queue transformation; %s" msgstr "Erro ao criar a transformação de lista; %s" -#: views.py:446 +#: views.py:424 #, python-format msgid "Create new transformation for queue: %s" msgstr "Criar nova transformação para a lista: %s" @@ -443,18 +419,9 @@ msgid "Automatically queue newly created documents for OCR." msgstr "Listar automaticamente os novos documentos criados para OCR " #: conf/settings.py:17 -msgid "" -"URI in the form: \"memcached://127.0.0.1:11211/\" to specify a cache backend " -"to use for locking. Multiple hosts can be specified separated by a semicolon." -msgstr "" -"URI na forma: \"memcached: / / 127.0.0.1:11211 /\" para especificar um " -"backend de cache para usar para o bloqueio. Hosts múltiplos podem ser " -"especificados separados por ponto-evírgula." - -#: conf/settings.py:18 msgid "File path to unpaper program." msgstr "Caminho do arquivo para o programa unpaper." -#: parsers/__init__.py:23 +#: parsers/__init__.py:37 msgid "Text extracted from PDF" msgstr "Texto extraído de PDF" diff --git a/apps/ocr/locale/ru/LC_MESSAGES/django.mo b/apps/ocr/locale/ru/LC_MESSAGES/django.mo index 2532ba2c71..bfb70a6258 100644 Binary files a/apps/ocr/locale/ru/LC_MESSAGES/django.mo and b/apps/ocr/locale/ru/LC_MESSAGES/django.mo differ diff --git a/apps/ocr/locale/ru/LC_MESSAGES/django.po b/apps/ocr/locale/ru/LC_MESSAGES/django.po index e4dd103e6d..0990e65ad2 100644 --- a/apps/ocr/locale/ru/LC_MESSAGES/django.po +++ b/apps/ocr/locale/ru/LC_MESSAGES/django.po @@ -8,9 +8,9 @@ msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" -"PO-Revision-Date: 2011-11-04 10:48+0000\n" -"Last-Translator: gsv70 \n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-02 18:19+0000\n" +"Last-Translator: Roberto Rosario \n" "Language-Team: Russian (http://www.transifex.net/projects/p/mayan-edms/team/" "ru/)\n" "Language: ru\n" @@ -20,59 +20,31 @@ msgstr "" "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:28 -msgid "Submit document for OCR" -msgstr "Отправить документ для распознавания текста" - -#: __init__.py:29 -msgid "Delete document for OCR queue" -msgstr "Удалить документ из очереди распознавания" - -#: __init__.py:30 -msgid "Can enable/disable an OCR queue" -msgstr "Можно включить / выключить очереди распознавания" - -#: __init__.py:31 -msgid "Can execute an OCR clean up on all document pages" -msgstr "Невозможно выполнить распознавание на всех страницах документа" - -#: __init__.py:32 -msgid "Can edit an OCR queue properties" -msgstr "Можно редактировать свойства очереди распознавания" - -#: __init__.py:34 __init__.py:56 __init__.py:74 -msgid "OCR" -msgstr "Распознавание текста" - -#: __init__.py:40 -msgid "OCR Setup" -msgstr "Настройки распознавания " - -#: __init__.py:44 +#: __init__.py:32 __init__.py:33 msgid "submit to OCR queue" msgstr "отправить на распознавание" -#: __init__.py:45 __init__.py:46 +#: __init__.py:34 __init__.py:35 msgid "re-queue" msgstr "переотправить" -#: __init__.py:47 __init__.py:48 __init__.py:63 +#: __init__.py:36 __init__.py:37 __init__.py:50 msgid "delete" msgstr "удалить" -#: __init__.py:50 +#: __init__.py:39 msgid "stop queue" msgstr "остановка очереди" -#: __init__.py:51 +#: __init__.py:40 msgid "activate queue" msgstr "активировать очередь" -#: __init__.py:53 +#: __init__.py:42 msgid "clean up pages content" msgstr "очистка содержимого страниц" -#: __init__.py:53 +#: __init__.py:42 msgid "" "Runs a language filter to remove common OCR mistakes from document pages " "content." @@ -80,35 +52,35 @@ msgstr "" "Применить языковый фильтр для удаления общих ошибок распознавания " "содержимого страниц документа." -#: __init__.py:55 +#: __init__.py:44 msgid "queue document list" msgstr "список очереди документов" -#: __init__.py:58 views.py:316 -msgid "active tasks" -msgstr "активные задачи" +#: __init__.py:45 __init__.py:63 permissions.py:7 +msgid "OCR" +msgstr "Распознавание текста" -#: __init__.py:60 +#: __init__.py:47 msgid "transformations" msgstr "преобразования" -#: __init__.py:61 +#: __init__.py:48 msgid "add transformation" msgstr "добавить преобразование" -#: __init__.py:62 +#: __init__.py:49 msgid "edit" msgstr "редактировать" -#: __init__.py:82 +#: __init__.py:74 msgid "Default" msgstr "Умолчание" -#: __init__.py:104 +#: __init__.py:102 msgid "Checks the OCR queue for pending documents." msgstr "Проверить очередь документов ожидающих распознавания ." -#: api.py:119 +#: api.py:122 msgid "Text from OCR" msgstr "Распознанный текст" @@ -132,88 +104,108 @@ msgstr "обработка" msgid "error" msgstr "ошибка" -#: models.py:22 +#: models.py:26 msgid "name" msgstr "имя" -#: models.py:23 +#: models.py:27 msgid "label" msgstr "этикетка" -#: models.py:27 models.py:47 +#: models.py:31 models.py:51 msgid "state" msgstr "состояние" -#: models.py:32 models.py:40 views.py:44 views.py:337 views.py:378 -#: views.py:408 views.py:444 +#: models.py:36 models.py:44 views.py:45 views.py:315 views.py:356 +#: views.py:386 views.py:422 msgid "document queue" msgstr "очередь документов" -#: models.py:33 +#: models.py:37 msgid "document queues" msgstr "очереди документов" -#: models.py:41 +#: models.py:45 msgid "document" msgstr "документ" -#: models.py:42 +#: models.py:46 msgid "date time submitted" msgstr "дата и время отправления" -#: models.py:43 +#: models.py:47 msgid "delay ocr" msgstr "задержка распознавания" -#: models.py:48 +#: models.py:52 msgid "result" msgstr "результат" -#: models.py:49 +#: models.py:53 msgid "node name" msgstr "имя узла" -#: models.py:53 +#: models.py:57 msgid "queue document" msgstr "документ очереди" -#: models.py:54 +#: models.py:58 msgid "queue documents" msgstr "документы очереди" -#: models.py:63 views.py:48 +#: models.py:78 views.py:49 msgid "Missing document." msgstr "Документа отсутствует." -#: models.py:67 +#: models.py:82 msgid "Enter a valid value." msgstr "Введите допустимое значение." -#: models.py:95 views.py:341 +#: models.py:110 views.py:319 msgid "order" msgstr "порядок" -#: models.py:96 views.py:342 views.py:379 views.py:409 +#: models.py:111 views.py:320 views.py:357 views.py:387 msgid "transformation" msgstr "преобразование" -#: models.py:97 views.py:343 +#: models.py:112 views.py:321 msgid "arguments" msgstr "аргументы" -#: models.py:97 +#: models.py:112 #, python-format msgid "Use dictionaries to indentify arguments, example: %s" msgstr "Использование словарей для определения аргументов, например: %s" -#: models.py:107 +#: models.py:122 msgid "document queue transformation" msgstr "преобразование очереди документов " -#: models.py:108 +#: models.py:123 msgid "document queue transformations" msgstr "преобразование очереди документов " +#: permissions.py:8 +msgid "Submit documents for OCR" +msgstr "" + +#: permissions.py:9 +msgid "Delete documents from OCR queue" +msgstr "" + +#: permissions.py:10 +msgid "Can enable/disable the OCR queue" +msgstr "" + +#: permissions.py:11 +msgid "Can execute the OCR clean up on all document pages" +msgstr "" + +#: permissions.py:12 +msgid "Can edit an OCR queue properties" +msgstr "Можно редактировать свойства очереди распознавания" + #: statistics.py:8 #, python-format msgid "Document queues: %d" @@ -228,200 +220,184 @@ msgstr "Документов в очереди : %d" msgid "OCR statistics" msgstr "статистика распознавания" -#: views.py:41 +#: views.py:42 #, python-format msgid "documents in queue: %s" msgstr "документы в очереди: %s" -#: views.py:49 +#: views.py:50 msgid "thumbnail" msgstr "миниатюра" -#: views.py:62 +#: views.py:63 msgid "document queue properties" msgstr "свойства очереди документов" -#: views.py:63 +#: views.py:64 #, python-format msgid "Current state: %s" msgstr "Текущее состояние:%s" -#: views.py:79 views.py:154 +#: views.py:80 views.py:168 msgid "Must provide at least one queue document." msgstr "Должна быть хотя бы одна очередь документов." -#: views.py:89 +#: views.py:90 #, python-format msgid "Document: %s is being processed and can't be deleted." msgstr "Документ: %s обрабатывается и не может быть удален." -#: views.py:92 +#: views.py:93 #, python-format msgid "Queue document: %(document)s deleted successfully." msgstr "Очередь документов: %(document)s успешно удалён." -#: views.py:96 +#: views.py:97 #, python-format msgid "Error deleting document: %(document)s; %(error)s" msgstr "Ошибка при удалении документа:%(document)s; %(error)s" -#: views.py:109 +#: views.py:110 #, python-format msgid "Are you sure you wish to delete queue document: %s?" msgstr "Вы действительно хотите удалить очередь документа: %s?" -#: views.py:111 +#: views.py:112 #, python-format msgid "Are you sure you wish to delete queue documents: %s?" msgstr "Вы действительно хотите удалить очереди документов: %s?" -#: views.py:134 +#: views.py:148 #, python-format msgid "Document: %(document)s was added to the OCR queue: %(queue)s." msgstr "" "Документ: %(document)s добавлен для распознавания в очередь: %(queue)s." -#: views.py:137 +#: views.py:151 #, python-format msgid "Document: %(document)s is already queued." msgstr "Документ: %(document)s уже находится в очереди." -#: views.py:165 +#: views.py:180 +#, python-format +msgid "Document: %(document)s was re-queued to the OCR queue: %(queue)s" +msgstr "Документ: %(document)s повторно добавлен в очередь: %(queue)s" + +#: views.py:186 +#, python-format +msgid "Document id#: %d, no longer exists." +msgstr "Документ №: %d больше не существует." + +#: views.py:190 #, python-format msgid "Document: %s is already being processed and can't be re-queded." msgstr "" "Документ: %s уже обработан и не может быть повторно добавлен в очередь." -#: views.py:173 -#, python-format -msgid "Document: %(document)s was re-queued to the OCR queue: %(queue)s" -msgstr "Документ: %(document)s повторно добавлен в очередь: %(queue)s" - -#: views.py:176 -#, python-format -msgid "Document id#: %d, no longer exists." -msgstr "Документ №: %d больше не существует." - -#: views.py:189 +#: views.py:202 #, python-format msgid "Are you sure you wish to re-queue document: %s?" msgstr "Вы действительно хотите повторно добавить документ в очередь: %s?" -#: views.py:191 +#: views.py:204 #, python-format msgid "Are you sure you wish to re-queue documents: %s?" msgstr "Вы действительно хотите повторно добавить документы в очередь: %s?" -#: views.py:209 +#: views.py:222 #, python-format msgid "Document queue: %s, already stopped." msgstr "Очередь документов: %s, уже остановлена." -#: views.py:215 +#: views.py:228 #, python-format msgid "Document queue: %s, stopped successfully." msgstr "Очередь документов: %s успешно остановлена." -#: views.py:221 +#: views.py:234 #, python-format msgid "Are you sure you wish to disable document queue: %s" msgstr "Вы действительно хотите отключить очередь документов: %s " -#: views.py:236 +#: views.py:249 #, python-format msgid "Document queue: %s, already active." msgstr "Очередь документов %s уже активна." -#: views.py:242 +#: views.py:255 #, python-format msgid "Document queue: %s, activated successfully." msgstr "Очередь документов %s успешно активирована." -#: views.py:248 +#: views.py:261 #, python-format msgid "Are you sure you wish to activate document queue: %s" msgstr "Вы действительно хотите активировать очередь документов %s" -#: views.py:265 +#: views.py:278 msgid "Are you sure you wish to clean up all the pages content?" msgstr "Вы уверены, что хотите очистить все содержимое страниц?" -#: views.py:266 +#: views.py:279 msgid "On large databases this operation may take some time to execute." msgstr "" "В больших базах данных эта операция может занять некоторое время для " "выполнения." -#: views.py:272 +#: views.py:285 msgid "Document pages content clean up complete." msgstr "Чистка содержимого страниц документа завершена." -#: views.py:274 +#: views.py:287 #, python-format msgid "Document pages content clean up error: %s" msgstr "Ошибка:%s при чистка содержимого страниц документа" -#: views.py:320 -msgid "node" -msgstr "узел" - -#: views.py:321 -msgid "task id" -msgstr "идентификатор задачи" - -#: views.py:322 -msgid "task name" -msgstr "имя задачи" - -#: views.py:323 -msgid "related object" -msgstr "связанный объект" - -#: views.py:335 +#: views.py:313 #, python-format msgid "transformations for: %s" msgstr "преобразования для: %s" -#: views.py:365 +#: views.py:343 msgid "Queue transformation edited successfully" msgstr "Преобразование очереди изменено" -#: views.py:368 +#: views.py:346 #, python-format msgid "Error editing queue transformation; %s" msgstr "Ошибка редактирования преобразования очереди; %s" -#: views.py:373 +#: views.py:351 #, python-format msgid "Edit transformation: %s" msgstr "Изменить преобразование: %s" -#: views.py:396 +#: views.py:374 msgid "Queue transformation deleted successfully." msgstr "Преобразование очереди успешно удалено." -#: views.py:398 +#: views.py:376 #, python-format msgid "Error deleting queue transformation; %(error)s" msgstr "Ошибка при удалении преобразования очереди; %(error)s" -#: views.py:411 +#: views.py:389 #, python-format msgid "" "Are you sure you wish to delete queue transformation \"%(transformation)s\"" msgstr "" "Вы действительно хотите удалить преобразование очереди \"%(transformation)s\"" -#: views.py:434 +#: views.py:412 msgid "Queue transformation created successfully" msgstr "Преобразование очереди создано" -#: views.py:437 +#: views.py:415 #, python-format msgid "Error creating queue transformation; %s" msgstr "Ошибка при создании преобразования очереди; %s" -#: views.py:446 +#: views.py:424 #, python-format msgid "Create new transformation for queue: %s" msgstr "Создать новое преобразование для очереди:% s." @@ -445,18 +421,9 @@ msgstr "" "Автоматически ставить в очередь новые документы для распознавания текста." #: conf/settings.py:17 -msgid "" -"URI in the form: \"memcached://127.0.0.1:11211/\" to specify a cache backend " -"to use for locking. Multiple hosts can be specified separated by a semicolon." -msgstr "" -"URI в виде: \"memcached://127.0.0.1:11211/\", для определения обработчика, " -"используемого для блокирования. Несколько хостов могут быть указаны через " -"точку с запятой." - -#: conf/settings.py:18 msgid "File path to unpaper program." msgstr "Путь к программе unpaper." -#: parsers/__init__.py:23 +#: parsers/__init__.py:37 msgid "Text extracted from PDF" msgstr "Текст, извлеченный из PDF" diff --git a/apps/ocr/managers.py b/apps/ocr/managers.py index a1fdb80b8c..b4596356d6 100644 --- a/apps/ocr/managers.py +++ b/apps/ocr/managers.py @@ -1,13 +1,15 @@ +from __future__ import absolute_import + from django.db import models -from ocr.exceptions import AlreadyQueued +from .exceptions import AlreadyQueued class DocumentQueueManager(models.Manager): - """ + ''' Module manager class to handle adding documents to an OCR document queue - """ + ''' def queue_document(self, document, queue_name='default'): document_queue = self.model.objects.get(name=queue_name) if document_queue.queuedocument_set.filter(document=document): diff --git a/apps/ocr/models.py b/apps/ocr/models.py index f33ee3fb35..3717f0ffe9 100644 --- a/apps/ocr/models.py +++ b/apps/ocr/models.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import + from ast import literal_eval from datetime import datetime @@ -13,11 +15,11 @@ from documents.models import Document from converter.api import get_available_transformations_choices from sources.managers import SourceTransformationManager -from ocr.literals import DOCUMENTQUEUE_STATE_STOPPED, \ - DOCUMENTQUEUE_STATE_CHOICES, QUEUEDOCUMENT_STATE_PENDING, \ - QUEUEDOCUMENT_STATE_CHOICES, QUEUEDOCUMENT_STATE_PROCESSING -from ocr.managers import DocumentQueueManager -from ocr.exceptions import ReQueueError +from .literals import (DOCUMENTQUEUE_STATE_STOPPED, + DOCUMENTQUEUE_STATE_CHOICES, QUEUEDOCUMENT_STATE_PENDING, + QUEUEDOCUMENT_STATE_CHOICES, QUEUEDOCUMENT_STATE_PROCESSING) +from .managers import DocumentQueueManager +from .exceptions import ReQueueError class DocumentQueue(models.Model): @@ -87,9 +89,9 @@ class ArgumentsValidator(object): self.code = code def __call__(self, value): - """ + ''' Validates that the input evaluates correctly. - """ + ''' value = value.strip() try: literal_eval(value) @@ -98,10 +100,10 @@ class ArgumentsValidator(object): class QueueTransformation(models.Model): - """ + ''' Model that stores the transformation and transformation arguments for a given document queue - """ + ''' content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() content_object = generic.GenericForeignKey('content_type', 'object_id') diff --git a/apps/ocr/parsers/__init__.py b/apps/ocr/parsers/__init__.py index 3d5a39635e..6a91d392d4 100644 --- a/apps/ocr/parsers/__init__.py +++ b/apps/ocr/parsers/__init__.py @@ -3,10 +3,9 @@ import logging from django.utils.translation import ugettext as _ -from converter import office_converter from converter import office_converter from converter.office_converter import OfficeConverter -from converter.exceptions import OfficeBackendError, OfficeConversionError +from converter.exceptions import OfficeConversionError from documents.utils import document_save_to_temp_dir from ocr.parsers.exceptions import ParserError, ParserUnknownFile @@ -27,7 +26,7 @@ def register_parser(function, mimetype=None, mimetypes=None): def pdf_parser(document_page, descriptor=None): if not descriptor: descriptor = document_page.document_version.open() - + pdf_pages = slate.PDF(descriptor) descriptor.close() @@ -45,7 +44,7 @@ def office_parser(document_page): office_converter = OfficeConverter() document_file = document_save_to_temp_dir(document_page.document, document_page.document.checksum) logger.debug('document_file: %s', document_file) - + office_converter.convert(document_file, mimetype=document_page.document.file_mimetype) if office_converter.exists: input_filepath = office_converter.output_filepath @@ -58,7 +57,7 @@ def office_parser(document_page): except OfficeConversionError, msg: print msg raise ParserError - + def parse_document_page(document_page): logger.debug('executing') diff --git a/apps/ocr/permissions.py b/apps/ocr/permissions.py new file mode 100644 index 0000000000..f74f1ec267 --- /dev/null +++ b/apps/ocr/permissions.py @@ -0,0 +1,12 @@ +from __future__ import absolute_import + +from django.utils.translation import ugettext_lazy as _ + +from permissions.models import Permission, PermissionNamespace + +ocr_namespace = PermissionNamespace('ocr', _(u'OCR')) +PERMISSION_OCR_DOCUMENT = Permission.objects.register(ocr_namespace, 'ocr_document', _(u'Submit documents for OCR')) +PERMISSION_OCR_DOCUMENT_DELETE = Permission.objects.register(ocr_namespace, 'ocr_document_delete', _(u'Delete documents from OCR queue')) +PERMISSION_OCR_QUEUE_ENABLE_DISABLE = Permission.objects.register(ocr_namespace, 'ocr_queue_enable_disable', _(u'Can enable/disable the OCR queue')) +PERMISSION_OCR_CLEAN_ALL_PAGES = Permission.objects.register(ocr_namespace, 'ocr_clean_all_pages', _(u'Can execute the OCR clean up on all document pages')) +PERMISSION_OCR_QUEUE_EDIT = Permission.objects.register(ocr_namespace, 'ocr_queue_edit', _(u'Can edit an OCR queue properties')) diff --git a/apps/ocr/tasks.py b/apps/ocr/tasks.py index 7c01a36e4d..800d0623d2 100644 --- a/apps/ocr/tasks.py +++ b/apps/ocr/tasks.py @@ -1,7 +1,7 @@ +from __future__ import absolute_import + from datetime import timedelta, datetime import platform -from time import sleep -from random import random import logging from django.db.models import Q @@ -9,15 +9,13 @@ from django.db.models import Q from job_processor.api import process_job from lock_manager import Lock, LockError -from ocr.api import do_document_ocr -from ocr.literals import QUEUEDOCUMENT_STATE_PENDING, \ - QUEUEDOCUMENT_STATE_PROCESSING, DOCUMENTQUEUE_STATE_ACTIVE, \ - QUEUEDOCUMENT_STATE_ERROR -from ocr.models import QueueDocument, DocumentQueue -from ocr.conf.settings import NODE_CONCURRENT_EXECUTION -from ocr.conf.settings import REPLICATION_DELAY -from ocr.conf.settings import CACHE_URI -from ocr.conf.settings import QUEUE_PROCESSING_INTERVAL +from .api import do_document_ocr +from .literals import (QUEUEDOCUMENT_STATE_PENDING, + QUEUEDOCUMENT_STATE_PROCESSING, DOCUMENTQUEUE_STATE_ACTIVE, + QUEUEDOCUMENT_STATE_ERROR) +from .models import QueueDocument, DocumentQueue +from .conf.settings import (NODE_CONCURRENT_EXECUTION, REPLICATION_DELAY, + QUEUE_PROCESSING_INTERVAL) LOCK_EXPIRE = 60 * 10 # Lock expires in 10 minutes # TODO: Tie LOCK_EXPIRATION with hard task timeout @@ -42,44 +40,16 @@ def task_process_queue_document(queue_document_id): queue_document.state = QUEUEDOCUMENT_STATE_ERROR queue_document.result = e queue_document.save() - + lock.release() except LockError: logger.debug('unable to obtain lock') pass -def reset_orphans(): - pass - ''' - i = inspect().active() - active_tasks = [] - orphans = [] - - if i: - for host, instances in i.items(): - for instance in instances: - active_tasks.append(instance['id']) - - for document_queue in DocumentQueue.objects.filter(state=DOCUMENTQUEUE_STATE_ACTIVE): - orphans = document_queue.queuedocument_set.\ - filter(state=QUEUEDOCUMENT_STATE_PROCESSING).\ - exclude(result__in=active_tasks) - - for orphan in orphans: - orphan.result = _(u'Orphaned') - orphan.state = QUEUEDOCUMENT_STATE_PENDING - orphan.delay = False - orphan.node_name = None - orphan.save() - ''' - - def task_process_document_queues(): logger.debug('executed') - # reset_orphans() - # Causes problems with big clusters increased latency - # Disabled until better solution + # TODO: reset_orphans() q_pending = Q(state=QUEUEDOCUMENT_STATE_PENDING) q_delayed = Q(delay=True) q_delay_interval = Q(datetime_submitted__lt=datetime.now() - timedelta(seconds=REPLICATION_DELAY)) @@ -100,7 +70,7 @@ def task_process_document_queues(): #print 'DocumentQueueWatcher exception: %s' % e finally: # Don't process anymore from this queryset, might be stale - break; + break else: logger.debug('already processing maximun') else: diff --git a/apps/ocr/tests.py b/apps/ocr/tests.py deleted file mode 100644 index 2247054b35..0000000000 --- a/apps/ocr/tests.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -This file demonstrates two different styles of tests (one doctest and one -unittest). These will both pass when you run "manage.py test". - -Replace these 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.failUnlessEqual(1 + 1, 2) - -__test__ = {"doctest": """ -Another way to test that 1 + 1 is equal to 2. - ->>> 1 + 1 == 2 -True -"""} - diff --git a/apps/ocr/urls.py b/apps/ocr/urls.py index dd8529119b..d77be818f8 100644 --- a/apps/ocr/urls.py +++ b/apps/ocr/urls.py @@ -13,7 +13,6 @@ urlpatterns = patterns('ocr.views', url(r'^queue/(?P\d+)/disable/$', 'document_queue_disable', (), 'document_queue_disable'), url(r'^document/all/clean_up/$', 'all_document_ocr_cleanup', (), 'all_document_ocr_cleanup'), - url(r'^node/active/list/$', 'node_active_list', (), 'node_active_list'), url(r'^queue/(?P\d+)/transformation/list/$', 'setup_queue_transformation_list', (), 'setup_queue_transformation_list'), url(r'^queue/(?P\w+)/transformation/create/$', 'setup_queue_transformation_create', (), 'setup_queue_transformation_create'), diff --git a/apps/ocr/views.py b/apps/ocr/views.py index 8fbc40d444..92772ad03e 100644 --- a/apps/ocr/views.py +++ b/apps/ocr/views.py @@ -1,4 +1,4 @@ -import socket +from __future__ import absolute_import from django.http import HttpResponseRedirect from django.shortcuts import render_to_response, get_object_or_404 @@ -7,28 +7,28 @@ from django.contrib import messages from django.views.generic.list_detail import object_list from django.utils.translation import ugettext_lazy as _ from django.core.urlresolvers import reverse +from django.core.exceptions import PermissionDenied -from celery.task.control import inspect -from permissions.api import check_permissions +from permissions.models import Permission from documents.models import Document from documents.widgets import document_link, document_thumbnail from common.utils import encapsulate +from acls.models import AccessEntry -from ocr import PERMISSION_OCR_DOCUMENT, PERMISSION_OCR_DOCUMENT_DELETE, \ - PERMISSION_OCR_QUEUE_ENABLE_DISABLE, PERMISSION_OCR_CLEAN_ALL_PAGES, \ - PERMISSION_OCR_QUEUE_EDIT - -from ocr.models import DocumentQueue, QueueDocument, QueueTransformation -from ocr.literals import QUEUEDOCUMENT_STATE_PENDING, \ - QUEUEDOCUMENT_STATE_PROCESSING, DOCUMENTQUEUE_STATE_STOPPED, \ - DOCUMENTQUEUE_STATE_ACTIVE -from ocr.exceptions import AlreadyQueued, ReQueueError -from ocr.api import clean_pages -from ocr.forms import QueueTransformationForm, QueueTransformationForm_create +from .permissions import (PERMISSION_OCR_DOCUMENT, + PERMISSION_OCR_DOCUMENT_DELETE, PERMISSION_OCR_QUEUE_ENABLE_DISABLE, + PERMISSION_OCR_CLEAN_ALL_PAGES, PERMISSION_OCR_QUEUE_EDIT) +from .models import DocumentQueue, QueueDocument, QueueTransformation +from .literals import (QUEUEDOCUMENT_STATE_PENDING, + QUEUEDOCUMENT_STATE_PROCESSING, DOCUMENTQUEUE_STATE_STOPPED, + DOCUMENTQUEUE_STATE_ACTIVE) +from .exceptions import AlreadyQueued, ReQueueError +from .api import clean_pages +from .forms import QueueTransformationForm, QueueTransformationForm_create def queue_document_list(request, queue_name='default'): - check_permissions(request.user, [PERMISSION_OCR_DOCUMENT]) + Permission.objects.check_permissions(request.user, [PERMISSION_OCR_DOCUMENT]) document_queue = get_object_or_404(DocumentQueue, name=queue_name) @@ -68,7 +68,7 @@ def queue_document_list(request, queue_name='default'): def queue_document_delete(request, queue_document_id=None, queue_document_id_list=None): - check_permissions(request.user, [PERMISSION_OCR_DOCUMENT_DELETE]) + Permission.objects.check_permissions(request.user, [PERMISSION_OCR_DOCUMENT_DELETE]) if queue_document_id: queue_documents = [get_object_or_404(QueueDocument, pk=queue_document_id)] @@ -120,20 +120,26 @@ def queue_document_multiple_delete(request): def submit_document_multiple(request): for item_id in request.GET.get('id_list', '').split(','): submit_document(request, item_id) - + return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/')) - + def submit_document(request, document_id): - check_permissions(request.user, [PERMISSION_OCR_DOCUMENT]) - document = get_object_or_404(Document, pk=document_id) + + try: + Permission.objects.check_permissions(request.user, [PERMISSION_OCR_DOCUMENT]) + except PermissionDenied: + AccessEntry.objects.check_access(PERMISSION_OCR_DOCUMENT, request.user, document) + return submit_document_to_queue(request, document=document, post_submit_redirect=request.META.get('HTTP_REFERER', '/')) def submit_document_to_queue(request, document, post_submit_redirect=None): - """This view is meant to be reusable""" + ''' + This view is meant to be reusable + ''' try: document_queue = DocumentQueue.objects.queue_document(document) @@ -150,7 +156,7 @@ def submit_document_to_queue(request, document, post_submit_redirect=None): def re_queue_document(request, queue_document_id=None, queue_document_id_list=None): - check_permissions(request.user, [PERMISSION_OCR_DOCUMENT]) + Permission.objects.check_permissions(request.user, [PERMISSION_OCR_DOCUMENT]) if queue_document_id: queue_documents = [get_object_or_404(QueueDocument, pk=queue_document_id)] @@ -204,7 +210,7 @@ def re_queue_multiple_document(request): def document_queue_disable(request, document_queue_id): - check_permissions(request.user, [PERMISSION_OCR_QUEUE_ENABLE_DISABLE]) + Permission.objects.check_permissions(request.user, [PERMISSION_OCR_QUEUE_ENABLE_DISABLE]) next = request.POST.get('next', request.GET.get('next', request.META.get('HTTP_REFERER', None))) previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', None))) @@ -231,7 +237,7 @@ def document_queue_disable(request, document_queue_id): def document_queue_enable(request, document_queue_id): - check_permissions(request.user, [PERMISSION_OCR_QUEUE_ENABLE_DISABLE]) + Permission.objects.check_permissions(request.user, [PERMISSION_OCR_QUEUE_ENABLE_DISABLE]) next = request.POST.get('next', request.GET.get('next', request.META.get('HTTP_REFERER', None))) previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', None))) @@ -258,7 +264,7 @@ def document_queue_enable(request, document_queue_id): def all_document_ocr_cleanup(request): - check_permissions(request.user, [PERMISSION_OCR_CLEAN_ALL_PAGES]) + Permission.objects.check_permissions(request.user, [PERMISSION_OCR_CLEAN_ALL_PAGES]) previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', None))) next = request.POST.get('next', request.GET.get('next', request.META.get('HTTP_REFERER', None))) @@ -294,44 +300,9 @@ def display_link(obj): return obj -def node_active_list(request): - check_permissions(request.user, [PERMISSION_OCR_DOCUMENT]) - - i = inspect() - active_tasks = [] - try: - active_nodes = i.active() - if active_nodes: - for node, tasks in active_nodes.items(): - for task in tasks: - task_info = { - 'node': node, - 'task_name': task['name'], - 'task_id': task['id'], - 'related_object': None, - } - if task['name'] == u'ocr.tasks.task_process_queue_document': - task_info['related_object'] = QueueDocument.objects.get(pk=eval(task['args'])[0]).document - active_tasks.append(task_info) - except socket.error: - active_tasks = [] - - return render_to_response('generic_list.html', { - 'object_list': active_tasks, - 'title': _(u'active tasks'), - 'hide_links': True, - 'hide_object': True, - 'extra_columns': [ - {'name': _(u'node'), 'attribute': 'node'}, - {'name': _(u'task id'), 'attribute': 'task_id'}, - {'name': _(u'task name'), 'attribute': 'task_name'}, - {'name': _(u'related object'), 'attribute': lambda x: display_link(x['related_object']) if x['related_object'] else u''} - ], - }, context_instance=RequestContext(request)) - - +# Setup views def setup_queue_transformation_list(request, document_queue_id): - check_permissions(request.user, [PERMISSION_OCR_QUEUE_EDIT]) + Permission.objects.check_permissions(request.user, [PERMISSION_OCR_QUEUE_EDIT]) document_queue = get_object_or_404(DocumentQueue, pk=document_queue_id) @@ -356,7 +327,7 @@ def setup_queue_transformation_list(request, document_queue_id): def setup_queue_transformation_edit(request, transformation_id): - check_permissions(request.user, [PERMISSION_OCR_QUEUE_EDIT]) + Permission.objects.check_permissions(request.user, [PERMISSION_OCR_QUEUE_EDIT]) transformation = get_object_or_404(QueueTransformation, pk=transformation_id) redirect_view = reverse('setup_queue_transformation_list', args=[transformation.content_object.pk]) @@ -389,7 +360,7 @@ def setup_queue_transformation_edit(request, transformation_id): def setup_queue_transformation_delete(request, transformation_id): - check_permissions(request.user, [PERMISSION_OCR_QUEUE_EDIT]) + Permission.objects.check_permissions(request.user, [PERMISSION_OCR_QUEUE_EDIT]) transformation = get_object_or_404(QueueTransformation, pk=transformation_id) redirect_view = reverse('setup_queue_transformation_list', args=[transformation.content_object.pk]) @@ -423,7 +394,7 @@ def setup_queue_transformation_delete(request, transformation_id): def setup_queue_transformation_create(request, document_queue_id): - check_permissions(request.user, [PERMISSION_OCR_QUEUE_EDIT]) + Permission.objects.check_permissions(request.user, [PERMISSION_OCR_QUEUE_EDIT]) document_queue = get_object_or_404(DocumentQueue, pk=document_queue_id) diff --git a/apps/permissions/__init__.py b/apps/permissions/__init__.py index e143c8bbb8..73731a97db 100644 --- a/apps/permissions/__init__.py +++ b/apps/permissions/__init__.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import + from django.contrib.auth.models import User from django.db.models.signals import post_save from django.core.exceptions import ObjectDoesNotExist @@ -6,17 +8,13 @@ from django.utils.translation import ugettext_lazy as _ from navigation.api import register_links, register_multi_item_links from project_setup.api import register_setup -from permissions.conf.settings import DEFAULT_ROLES -from permissions.models import Role +from .conf.settings import DEFAULT_ROLES +from .models import Role, Permission, PermissionNamespace +from .permissions import (PERMISSION_ROLE_VIEW, PERMISSION_ROLE_EDIT, + PERMISSION_ROLE_CREATE, PERMISSION_ROLE_DELETE, + PERMISSION_PERMISSION_GRANT, PERMISSION_PERMISSION_REVOKE) -PERMISSION_ROLE_VIEW = {'namespace': 'permissions', 'name': 'role_view', 'label': _(u'View roles')} -PERMISSION_ROLE_EDIT = {'namespace': 'permissions', 'name': 'role_edit', 'label': _(u'Edit roles')} -PERMISSION_ROLE_CREATE = {'namespace': 'permissions', 'name': 'role_create', 'label': _(u'Create roles')} -PERMISSION_ROLE_DELETE = {'namespace': 'permissions', 'name': 'role_delete', 'label': _(u'Delete roles')} -PERMISSION_PERMISSION_GRANT = {'namespace': 'permissions', 'name': 'permission_grant', 'label': _(u'Grant permissions')} -PERMISSION_PERMISSION_REVOKE = {'namespace': 'permissions', 'name': 'permission_revoke', 'label': _(u'Revoke permissions')} - -role_list = {'text': _(u'roles'), 'view': 'role_list', 'famfam': 'medal_gold_1', 'icon': 'medal_gold_1.png', 'permissions': [PERMISSION_ROLE_VIEW]} +role_list = {'text': _(u'roles'), 'view': 'role_list', 'famfam': 'medal_gold_1', 'icon': 'medal_gold_1.png', 'permissions': [PERMISSION_ROLE_VIEW], 'children_view_regex': [r'^permission_', r'^role_']} role_create = {'text': _(u'create new role'), 'view': 'role_create', 'famfam': 'medal_gold_add', 'permissions': [PERMISSION_ROLE_CREATE]} role_edit = {'text': _(u'edit'), 'view': 'role_edit', 'args': 'object.id', 'famfam': 'medal_gold_1', 'permissions': [PERMISSION_ROLE_EDIT]} role_members = {'text': _(u'members'), 'view': 'role_members', 'args': 'object.id', 'famfam': 'group_key', 'permissions': [PERMISSION_ROLE_EDIT]} @@ -27,7 +25,7 @@ permission_grant = {'text': _(u'grant'), 'view': 'permission_multiple_grant', 'f permission_revoke = {'text': _(u'revoke'), 'view': 'permission_multiple_revoke', 'famfam': 'key_delete', 'permissions': [PERMISSION_PERMISSION_REVOKE]} register_links(Role, [role_edit, role_delete, role_permissions, role_members]) -register_links(['role_members', 'role_list', 'role_view', 'role_create', 'role_edit', 'role_permissions', 'role_delete'], [role_list, role_create], menu_name='sidebar') +register_links([Role, 'role_list', 'role_create'], [role_list, role_create], menu_name='secondary_menu') register_multi_item_links(['role_permissions'], [permission_grant, permission_revoke]) permission_views = ['role_list', 'role_create', 'role_edit', 'role_members', 'role_permissions', 'role_delete'] diff --git a/apps/permissions/admin.py b/apps/permissions/admin.py index 1728199d71..e38f1f81d0 100644 --- a/apps/permissions/admin.py +++ b/apps/permissions/admin.py @@ -1,6 +1,8 @@ +from __future__ import absolute_import + from django.contrib import admin -from permissions.models import Permission, PermissionHolder, Role, RoleMember +from .models import StoredPermission, PermissionHolder, Role, RoleMember class PermissionHolderInline(admin.StackedInline): @@ -12,7 +14,7 @@ class PermissionHolderInline(admin.StackedInline): class PermissionAdmin(admin.ModelAdmin): inlines = [PermissionHolderInline] - list_display = ('namespace', 'name', 'label') + list_display = ('namespace', 'name') list_display_links = list_display @@ -27,5 +29,5 @@ class RoleAdmin(admin.ModelAdmin): inlines = [RoleMemberInline] -admin.site.register(Permission, PermissionAdmin) +admin.site.register(StoredPermission, PermissionAdmin) admin.site.register(Role, RoleAdmin) diff --git a/apps/permissions/api.py b/apps/permissions/api.py index 13efbbed28..e69de29bb2 100644 --- a/apps/permissions/api.py +++ b/apps/permissions/api.py @@ -1,62 +0,0 @@ -try: - from psycopg2 import OperationalError -except ImportError: - class OperationalError(Exception): - pass - -from django.core.exceptions import ImproperlyConfigured -from django.db import transaction -from django.db.utils import DatabaseError -from django.shortcuts import get_object_or_404 -from django.utils.translation import ugettext -from django.core.exceptions import PermissionDenied -from django.utils.translation import ugettext_lazy as _ - -from permissions import PERMISSION_ROLE_VIEW, PERMISSION_ROLE_EDIT, \ - PERMISSION_ROLE_CREATE, PERMISSION_ROLE_DELETE, \ - PERMISSION_PERMISSION_GRANT, PERMISSION_PERMISSION_REVOKE - -from permissions.models import Permission - -namespace_titles = { - 'permissions': _(u'Permissions') -} - - -def set_namespace_title(namespace, title): - namespace_titles.setdefault(namespace, title) - - -@transaction.commit_manually -def register_permission(permission): - try: - permission_obj, created = Permission.objects.get_or_create( - namespace=permission['namespace'], name=permission['name']) - permission_obj.label = unicode(permission['label']) - permission_obj.save() - except DatabaseError: - transaction.rollback() - # Special case for ./manage.py syncdb - except (OperationalError, ImproperlyConfigured): - transaction.rollback() - # Special for DjangoZoom, which executes collectstatic media - # doing syncdb and creating the database tables - else: - transaction.commit() - - -def check_permissions(requester, permission_list): - for permission_item in permission_list: - permission = get_object_or_404(Permission, - namespace=permission_item['namespace'], name=permission_item['name']) - if permission.has_permission(requester): - return True - - raise PermissionDenied(ugettext(u'Insufficient permissions.')) - -register_permission(PERMISSION_ROLE_VIEW) -register_permission(PERMISSION_ROLE_EDIT) -register_permission(PERMISSION_ROLE_CREATE) -register_permission(PERMISSION_ROLE_DELETE) -register_permission(PERMISSION_PERMISSION_GRANT) -register_permission(PERMISSION_PERMISSION_REVOKE) diff --git a/apps/permissions/forms.py b/apps/permissions/forms.py index 87da905a83..b0a6a89008 100644 --- a/apps/permissions/forms.py +++ b/apps/permissions/forms.py @@ -1,8 +1,10 @@ +from __future__ import absolute_import + from django import forms from common.forms import DetailForm -from permissions.models import Role +from .models import Role class RoleForm(forms.ModelForm): diff --git a/apps/permissions/locale/en/LC_MESSAGES/django.po b/apps/permissions/locale/en/LC_MESSAGES/django.po index a2354e2728..634c035eee 100644 --- a/apps/permissions/locale/en/LC_MESSAGES/django.po +++ b/apps/permissions/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,162 +17,174 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: __init__.py:12 -msgid "View roles" -msgstr "" - -#: __init__.py:13 -msgid "Edit roles" -msgstr "" - -#: __init__.py:14 -msgid "Create roles" -msgstr "" - -#: __init__.py:15 -msgid "Delete roles" -msgstr "" - -#: __init__.py:16 -msgid "Grant permissions" -msgstr "" - -#: __init__.py:17 -msgid "Revoke permissions" -msgstr "" - -#: __init__.py:19 models.py:87 views.py:38 +#: __init__.py:17 models.py:209 views.py:40 msgid "roles" msgstr "" -#: __init__.py:20 +#: __init__.py:18 msgid "create new role" msgstr "" -#: __init__.py:21 +#: __init__.py:19 msgid "edit" msgstr "" -#: __init__.py:22 +#: __init__.py:20 msgid "members" msgstr "" -#: __init__.py:23 +#: __init__.py:21 msgid "role permissions" msgstr "" -#: __init__.py:24 +#: __init__.py:22 msgid "delete" msgstr "" -#: __init__.py:26 +#: __init__.py:24 msgid "grant" msgstr "" -#: __init__.py:27 +#: __init__.py:25 msgid "revoke" msgstr "" -#: api.py:22 -msgid "Permissions" -msgstr "" - -#: api.py:55 +#: models.py:51 msgid "Insufficient permissions." msgstr "" -#: models.py:11 views.py:58 +#: models.py:123 views.py:60 msgid "namespace" msgstr "" -#: models.py:12 views.py:59 +#: models.py:124 views.py:61 msgid "name" msgstr "" -#: models.py:13 models.py:82 -msgid "label" -msgstr "" - -#: models.py:20 models.py:65 views.py:145 views.py:204 +#: models.py:131 models.py:187 views.py:152 views.py:215 msgid "permission" msgstr "" -#: models.py:21 views.py:55 views.py:147 views.py:206 +#: models.py:132 views.py:57 views.py:154 views.py:217 msgid "permissions" msgstr "" -#: models.py:73 +#: models.py:195 msgid "permission holder" msgstr "" -#: models.py:74 +#: models.py:196 msgid "permission holders" msgstr "" -#: models.py:86 models.py:104 views.py:74 views.py:91 views.py:115 -#: views.py:282 +#: models.py:204 +msgid "label" +msgstr "" + +#: models.py:208 models.py:239 views.py:76 views.py:93 views.py:117 +#: views.py:334 msgid "role" msgstr "" -#: models.py:115 +#: models.py:255 msgid "role member" msgstr "" -#: models.py:116 +#: models.py:256 msgid "role members" msgstr "" -#: views.py:61 +#: permissions.py:7 +msgid "Permissions" +msgstr "" + +#: permissions.py:9 +msgid "View roles" +msgstr "" + +#: permissions.py:10 +msgid "Edit roles" +msgstr "" + +#: permissions.py:11 +msgid "Create roles" +msgstr "" + +#: permissions.py:12 +msgid "Delete roles" +msgstr "" + +#: permissions.py:13 +msgid "Grant permissions" +msgstr "" + +#: permissions.py:14 +msgid "Revoke permissions" +msgstr "" + +#: views.py:63 msgid "has permission" msgstr "" -#: views.py:142 views.py:201 +#: views.py:149 views.py:212 msgid " and " msgstr "" -#: views.py:142 views.py:201 +#: views.py:149 views.py:212 #, python-format msgid "%(permissions)s to %(requester)s" msgstr "" -#: views.py:152 +#: views.py:159 #, python-format msgid "Permission \"%(permission)s\" granted to: %(requester)s." msgstr "" -#: views.py:155 +#: views.py:162 #, python-format msgid "%(requester)s, already had the permission \"%(permission)s\" granted." msgstr "" -#: views.py:167 +#: views.py:173 #, python-format msgid "" "Are you sure you wish to grant the %(permissions_label)s %(title_suffix)s?" msgstr "" -#: views.py:211 +#: views.py:222 #, python-format msgid "Permission \"%(permission)s\" revoked from: %(requester)s." msgstr "" -#: views.py:214 +#: views.py:225 #, python-format msgid "%(requester)s, doesn't have the permission \"%(permission)s\" granted." msgstr "" -#: views.py:226 +#: views.py:236 #, python-format msgid "" "Are you sure you wish to revoke the %(permissions_label)s %(title_suffix)s?" msgstr "" -#: views.py:278 +#: views.py:271 views.py:295 +msgid "Users" +msgstr "" + +#: views.py:274 views.py:298 +msgid "Groups" +msgstr "" + +#: views.py:277 views.py:301 +msgid "Special" +msgstr "" + +#: views.py:330 #, python-format msgid "non members of role: %s" msgstr "" -#: views.py:279 +#: views.py:331 #, python-format msgid "members of role: %s" msgstr "" diff --git a/apps/permissions/locale/es/LC_MESSAGES/django.mo b/apps/permissions/locale/es/LC_MESSAGES/django.mo index ddd24a555b..21e0374d21 100644 Binary files a/apps/permissions/locale/es/LC_MESSAGES/django.mo and b/apps/permissions/locale/es/LC_MESSAGES/django.mo differ diff --git a/apps/permissions/locale/es/LC_MESSAGES/django.po b/apps/permissions/locale/es/LC_MESSAGES/django.po index 2ae5d18ed6..ab9efdf661 100644 --- a/apps/permissions/locale/es/LC_MESSAGES/django.po +++ b/apps/permissions/locale/es/LC_MESSAGES/django.po @@ -1,181 +1,194 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: -# Roberto Rosario , 2011. +# Roberto Rosario , 2011, 2012. msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" -"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" -"PO-Revision-Date: 2011-11-22 15:37+0000\n" -"Last-Translator: rosarior \n" -"Language-Team: Spanish (Castilian) (http://www.transifex.net/projects/p/mayan-edms/team/es/)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-12 00:25+0000\n" +"Last-Translator: Roberto Rosario \n" +"Language-Team: Spanish (Castilian) (http://www.transifex.net/projects/p/" +"mayan-edms/team/es/)\n" +"Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: es\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -#: __init__.py:12 -msgid "View roles" -msgstr "Ver las funciones" - -#: __init__.py:13 -msgid "Edit roles" -msgstr "Modificar las funciones" - -#: __init__.py:14 -msgid "Create roles" -msgstr "Crear funciones" - -#: __init__.py:15 -msgid "Delete roles" -msgstr "Eliminar funciones" - -#: __init__.py:16 -msgid "Grant permissions" -msgstr "Conceder permisos" - -#: __init__.py:17 -msgid "Revoke permissions" -msgstr "Revocar permisos" - -#: __init__.py:19 models.py:87 views.py:38 +#: __init__.py:17 models.py:209 views.py:40 msgid "roles" msgstr "funciones" -#: __init__.py:20 +#: __init__.py:18 msgid "create new role" msgstr "crear nueva función" -#: __init__.py:21 +#: __init__.py:19 msgid "edit" msgstr "editar" -#: __init__.py:22 +#: __init__.py:20 msgid "members" msgstr "miembros" -#: __init__.py:23 +#: __init__.py:21 msgid "role permissions" msgstr "permisos de la funcion" -#: __init__.py:24 +#: __init__.py:22 msgid "delete" msgstr "eliminar" -#: __init__.py:26 +#: __init__.py:24 msgid "grant" msgstr "otorgar" -#: __init__.py:27 +#: __init__.py:25 msgid "revoke" msgstr "revocar" -#: api.py:22 -msgid "Permissions" -msgstr "Permisos" - -#: api.py:55 +#: models.py:51 msgid "Insufficient permissions." msgstr "Permisos insuficientes." -#: models.py:11 views.py:58 +#: models.py:123 views.py:60 msgid "namespace" msgstr "espacio de nombres" -#: models.py:12 views.py:59 +#: models.py:124 views.py:61 msgid "name" msgstr "nombre" -#: models.py:13 models.py:82 -msgid "label" -msgstr "etiqueta" - -#: models.py:20 models.py:65 views.py:145 views.py:204 +#: models.py:131 models.py:187 views.py:152 views.py:215 msgid "permission" msgstr "permiso" -#: models.py:21 views.py:55 views.py:147 views.py:206 +#: models.py:132 views.py:57 views.py:154 views.py:217 msgid "permissions" msgstr "permisos" -#: models.py:73 +#: models.py:195 msgid "permission holder" msgstr "titular de la autorización" -#: models.py:74 +#: models.py:196 msgid "permission holders" msgstr "titulares de autorización" -#: models.py:86 models.py:104 views.py:74 views.py:91 views.py:115 -#: views.py:282 +#: models.py:204 +msgid "label" +msgstr "etiqueta" + +#: models.py:208 models.py:239 views.py:76 views.py:93 views.py:117 +#: views.py:334 msgid "role" msgstr "función" -#: models.py:115 +#: models.py:255 msgid "role member" msgstr "miembro de la función" -#: models.py:116 +#: models.py:256 msgid "role members" msgstr "miembros de las functiones" -#: views.py:61 +#: permissions.py:7 +msgid "Permissions" +msgstr "Permisos" + +#: permissions.py:9 +msgid "View roles" +msgstr "Ver las funciones" + +#: permissions.py:10 +msgid "Edit roles" +msgstr "Modificar las funciones" + +#: permissions.py:11 +msgid "Create roles" +msgstr "Crear funciones" + +#: permissions.py:12 +msgid "Delete roles" +msgstr "Eliminar funciones" + +#: permissions.py:13 +msgid "Grant permissions" +msgstr "Conceder permisos" + +#: permissions.py:14 +msgid "Revoke permissions" +msgstr "Revocar permisos" + +#: views.py:63 msgid "has permission" msgstr "tiene permiso" -#: views.py:142 views.py:201 +#: views.py:149 views.py:212 msgid " and " msgstr "y" -#: views.py:142 views.py:201 +#: views.py:149 views.py:212 #, python-format msgid "%(permissions)s to %(requester)s" msgstr "%(permissions)s a %(requester)s" -#: views.py:152 +#: views.py:159 #, python-format msgid "Permission \"%(permission)s\" granted to: %(requester)s." msgstr "Permiso \"%(permission)s\" otorgado a: %(requester)s." -#: views.py:155 +#: views.py:162 #, python-format msgid "%(requester)s, already had the permission \"%(permission)s\" granted." msgstr "%(requester)s, ya tenía el permiso \"%(permission)s\" concedido." -#: views.py:167 +#: views.py:173 #, python-format msgid "" "Are you sure you wish to grant the %(permissions_label)s %(title_suffix)s?" msgstr "" "¿Está seguro que desea otorgar el %(permissions_label)s %(title_suffix)s?" -#: views.py:211 +#: views.py:222 #, python-format msgid "Permission \"%(permission)s\" revoked from: %(requester)s." msgstr "Permiso \"%(permission)s\" revocado de: %(requester)s." -#: views.py:214 +#: views.py:225 #, python-format msgid "%(requester)s, doesn't have the permission \"%(permission)s\" granted." msgstr "%(requester)s, no tiene el permiso \"%(permission)s\" concedido." -#: views.py:226 +#: views.py:236 #, python-format msgid "" "Are you sure you wish to revoke the %(permissions_label)s %(title_suffix)s?" msgstr "" "¿Estás seguro que quiere revocar el %(permissions_label)s %(title_suffix)s?" -#: views.py:278 +#: views.py:271 views.py:295 +msgid "Users" +msgstr "Usuarios" + +#: views.py:274 views.py:298 +msgid "Groups" +msgstr "Grupos" + +#: views.py:277 views.py:301 +msgid "Special" +msgstr "Especial" + +#: views.py:330 #, python-format msgid "non members of role: %s" msgstr "no miembros de la función: %s" -#: views.py:279 +#: views.py:331 #, python-format msgid "members of role: %s" msgstr "miembros de la función: %s" @@ -195,5 +208,3 @@ msgid "" msgstr "" "Una lista de funciones existentes que se asignan automáticamente a los " "usuarios nuevos" - - diff --git a/apps/permissions/locale/it/LC_MESSAGES/django.mo b/apps/permissions/locale/it/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..1662db40f5 Binary files /dev/null and b/apps/permissions/locale/it/LC_MESSAGES/django.mo differ diff --git a/apps/permissions/locale/it/LC_MESSAGES/django.po b/apps/permissions/locale/it/LC_MESSAGES/django.po new file mode 100644 index 0000000000..c9c70931c1 --- /dev/null +++ b/apps/permissions/locale/it/LC_MESSAGES/django.po @@ -0,0 +1,212 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# , 2011. +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-02 18:18+0000\n" +"Last-Translator: Roberto Rosario \n" +"Language-Team: Italian (http://www.transifex.net/projects/p/mayan-edms/team/" +"it/)\n" +"Language: it\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:17 models.py:209 views.py:40 +msgid "roles" +msgstr "ruoli" + +#: __init__.py:18 +msgid "create new role" +msgstr "crea nuovo ruolo" + +#: __init__.py:19 +msgid "edit" +msgstr "modifica" + +#: __init__.py:20 +msgid "members" +msgstr "membri" + +#: __init__.py:21 +msgid "role permissions" +msgstr "permessi dei ruoli" + +#: __init__.py:22 +msgid "delete" +msgstr "cancella" + +#: __init__.py:24 +msgid "grant" +msgstr "concessione" + +#: __init__.py:25 +msgid "revoke" +msgstr "revoca" + +#: models.py:51 +msgid "Insufficient permissions." +msgstr "Permessi insufficienti" + +#: models.py:123 views.py:60 +msgid "namespace" +msgstr "namespace" + +#: models.py:124 views.py:61 +msgid "name" +msgstr "nome" + +#: models.py:131 models.py:187 views.py:152 views.py:215 +msgid "permission" +msgstr "permesso" + +#: models.py:132 views.py:57 views.py:154 views.py:217 +msgid "permissions" +msgstr "permessi" + +#: models.py:195 +msgid "permission holder" +msgstr "titolare del permesso" + +#: models.py:196 +msgid "permission holders" +msgstr "titolari dei permessi" + +#: models.py:204 +msgid "label" +msgstr "etichetta" + +#: models.py:208 models.py:239 views.py:76 views.py:93 views.py:117 +#: views.py:334 +msgid "role" +msgstr "ruolo" + +#: models.py:255 +msgid "role member" +msgstr "membro del ruolo" + +#: models.py:256 +msgid "role members" +msgstr "membri del ruolo" + +#: permissions.py:7 +msgid "Permissions" +msgstr "Permessi" + +#: permissions.py:9 +msgid "View roles" +msgstr "Vedi i ruoli" + +#: permissions.py:10 +msgid "Edit roles" +msgstr "Modifica i ruoli" + +#: permissions.py:11 +msgid "Create roles" +msgstr "Crea ruoli" + +#: permissions.py:12 +msgid "Delete roles" +msgstr "Cancella i ruoli" + +#: permissions.py:13 +msgid "Grant permissions" +msgstr "Concedere le autorizzazioni" + +#: permissions.py:14 +msgid "Revoke permissions" +msgstr "Revoca le autorizzazioni" + +#: views.py:63 +msgid "has permission" +msgstr "ha il permesso" + +#: views.py:149 views.py:212 +msgid " and " +msgstr " and " + +#: views.py:149 views.py:212 +#, python-format +msgid "%(permissions)s to %(requester)s" +msgstr "%(permissions)s a %(requester)s" + +#: views.py:159 +#, python-format +msgid "Permission \"%(permission)s\" granted to: %(requester)s." +msgstr "Permesso \"%(permission)s\" concesso a: %(requester)s." + +#: views.py:162 +#, python-format +msgid "%(requester)s, already had the permission \"%(permission)s\" granted." +msgstr "%(requester)s, ha già il permesso \"%(permission)s\" concesso." + +#: views.py:173 +#, python-format +msgid "" +"Are you sure you wish to grant the %(permissions_label)s %(title_suffix)s?" +msgstr "" +"Sei sicuro che tu voglia concedere questo permesso %(permissions_label)s " +"%(title_suffix)s?" + +#: views.py:222 +#, python-format +msgid "Permission \"%(permission)s\" revoked from: %(requester)s." +msgstr "Permesso \"%(permission)s\" revocato per: %(requester)s." + +#: views.py:225 +#, python-format +msgid "%(requester)s, doesn't have the permission \"%(permission)s\" granted." +msgstr "%(requester)s, non ha i permessi \"%(permission)s\" consentiti." + +#: views.py:236 +#, python-format +msgid "" +"Are you sure you wish to revoke the %(permissions_label)s %(title_suffix)s?" +msgstr "" +"Sei sicuro di voler revocare questo permesso %(permissions_label)s " +"%(title_suffix)s?" + +#: views.py:271 views.py:295 +msgid "Users" +msgstr "" + +#: views.py:274 views.py:298 +msgid "Groups" +msgstr "" + +#: views.py:277 views.py:301 +msgid "Special" +msgstr "" + +#: views.py:330 +#, python-format +msgid "non members of role: %s" +msgstr "nessun menbro per il ruolo:%s" + +#: views.py:331 +#, python-format +msgid "members of role: %s" +msgstr "membri per il ruolo:%s" + +#: widgets.py:16 +msgid "Revoke" +msgstr "Revoca" + +#: widgets.py:21 +msgid "Grant" +msgstr "Concessione" + +#: conf/settings.py:10 +msgid "" +"A list of existing roles that are automatically assigned to newly created " +"users" +msgstr "" +"Un elenco di ruoli esistenti che vengono automaticamente assegnati agli " +"utenti appena creati" diff --git a/apps/permissions/locale/pl/LC_MESSAGES/django.mo b/apps/permissions/locale/pl/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..41353099df Binary files /dev/null and b/apps/permissions/locale/pl/LC_MESSAGES/django.mo differ diff --git a/apps/permissions/locale/pl/LC_MESSAGES/django.po b/apps/permissions/locale/pl/LC_MESSAGES/django.po new file mode 100644 index 0000000000..1818fe2c69 --- /dev/null +++ b/apps/permissions/locale/pl/LC_MESSAGES/django.po @@ -0,0 +1,205 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# , 2012. +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-20 16:08+0000\n" +"Last-Translator: mic \n" +"Language-Team: Polish (http://www.transifex.net/projects/p/mayan-edms/language/pl/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pl\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" + +#: __init__.py:17 models.py:209 views.py:40 +msgid "roles" +msgstr "" + +#: __init__.py:18 +msgid "create new role" +msgstr "" + +#: __init__.py:19 +msgid "edit" +msgstr "" + +#: __init__.py:20 +msgid "members" +msgstr "" + +#: __init__.py:21 +msgid "role permissions" +msgstr "" + +#: __init__.py:22 +msgid "delete" +msgstr "" + +#: __init__.py:24 +msgid "grant" +msgstr "przyznać" + +#: __init__.py:25 +msgid "revoke" +msgstr "odwołać" + +#: models.py:51 +msgid "Insufficient permissions." +msgstr "Niewystarczające uprawnienia." + +#: models.py:123 views.py:60 +msgid "namespace" +msgstr "" + +#: models.py:124 views.py:61 +msgid "name" +msgstr "nazwa" + +#: models.py:131 models.py:187 views.py:152 views.py:215 +msgid "permission" +msgstr "uprawnienie" + +#: models.py:132 views.py:57 views.py:154 views.py:217 +msgid "permissions" +msgstr "uprawnienia" + +#: models.py:195 +msgid "permission holder" +msgstr "" + +#: models.py:196 +msgid "permission holders" +msgstr "" + +#: models.py:204 +msgid "label" +msgstr "etykieta" + +#: models.py:208 models.py:239 views.py:76 views.py:93 views.py:117 +#: views.py:334 +msgid "role" +msgstr "" + +#: models.py:255 +msgid "role member" +msgstr "" + +#: models.py:256 +msgid "role members" +msgstr "" + +#: permissions.py:7 +msgid "Permissions" +msgstr "Uprawnienia" + +#: permissions.py:9 +msgid "View roles" +msgstr "" + +#: permissions.py:10 +msgid "Edit roles" +msgstr "" + +#: permissions.py:11 +msgid "Create roles" +msgstr "" + +#: permissions.py:12 +msgid "Delete roles" +msgstr "" + +#: permissions.py:13 +msgid "Grant permissions" +msgstr "" + +#: permissions.py:14 +msgid "Revoke permissions" +msgstr "" + +#: views.py:63 +msgid "has permission" +msgstr "ma uprawnienie" + +#: views.py:149 views.py:212 +msgid " and " +msgstr "i" + +#: views.py:149 views.py:212 +#, python-format +msgid "%(permissions)s to %(requester)s" +msgstr "" + +#: views.py:159 +#, python-format +msgid "Permission \"%(permission)s\" granted to: %(requester)s." +msgstr "" + +#: views.py:162 +#, python-format +msgid "%(requester)s, already had the permission \"%(permission)s\" granted." +msgstr "" + +#: views.py:173 +#, python-format +msgid "" +"Are you sure you wish to grant the %(permissions_label)s %(title_suffix)s?" +msgstr "" + +#: views.py:222 +#, python-format +msgid "Permission \"%(permission)s\" revoked from: %(requester)s." +msgstr "" + +#: views.py:225 +#, python-format +msgid "%(requester)s, doesn't have the permission \"%(permission)s\" granted." +msgstr "" + +#: views.py:236 +#, python-format +msgid "" +"Are you sure you wish to revoke the %(permissions_label)s %(title_suffix)s?" +msgstr "" + +#: views.py:271 views.py:295 +msgid "Users" +msgstr "Użytkownicy" + +#: views.py:274 views.py:298 +msgid "Groups" +msgstr "Grupy" + +#: views.py:277 views.py:301 +msgid "Special" +msgstr "Specjalny" + +#: views.py:330 +#, python-format +msgid "non members of role: %s" +msgstr "" + +#: views.py:331 +#, python-format +msgid "members of role: %s" +msgstr "" + +#: widgets.py:16 +msgid "Revoke" +msgstr "Odwołać" + +#: widgets.py:21 +msgid "Grant" +msgstr "" + +#: conf/settings.py:10 +msgid "" +"A list of existing roles that are automatically assigned to newly created " +"users" +msgstr "" diff --git a/apps/permissions/locale/pt/LC_MESSAGES/django.mo b/apps/permissions/locale/pt/LC_MESSAGES/django.mo index 715b0f389b..04280bb198 100644 Binary files a/apps/permissions/locale/pt/LC_MESSAGES/django.mo and b/apps/permissions/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/permissions/locale/pt/LC_MESSAGES/django.po b/apps/permissions/locale/pt/LC_MESSAGES/django.po index dd93d53119..f3e0abc0ae 100644 --- a/apps/permissions/locale/pt/LC_MESSAGES/django.po +++ b/apps/permissions/locale/pt/LC_MESSAGES/django.po @@ -8,9 +8,9 @@ msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" -"PO-Revision-Date: 2011-11-02 04:52+0000\n" -"Last-Translator: emersonsoares \n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-02 18:18+0000\n" +"Last-Translator: Roberto Rosario \n" "Language-Team: Portuguese (http://www.transifex.net/projects/p/mayan-edms/" "team/pt/)\n" "Language: pt\n" @@ -19,172 +19,174 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -#: __init__.py:12 -msgid "View roles" -msgstr "Ver funções" - -#: __init__.py:13 -msgid "Edit roles" -msgstr "Editar funções" - -#: __init__.py:14 -msgid "Create roles" -msgstr "Criar funções" - -#: __init__.py:15 -msgid "Delete roles" -msgstr "Deletar funções" - -#: __init__.py:16 -msgid "Grant permissions" -msgstr "Conceder permissões" - -#: __init__.py:17 -msgid "Revoke permissions" -msgstr "Revogar as permissões" - -#: __init__.py:19 models.py:87 views.py:38 +#: __init__.py:17 models.py:209 views.py:40 msgid "roles" msgstr "funções" -#: __init__.py:20 +#: __init__.py:18 msgid "create new role" msgstr "criar nova função" -#: __init__.py:21 +#: __init__.py:19 msgid "edit" msgstr "editar" -#: __init__.py:22 +#: __init__.py:20 msgid "members" msgstr "membros" -#: __init__.py:23 +#: __init__.py:21 msgid "role permissions" msgstr "permissões de função" -#: __init__.py:24 +#: __init__.py:22 msgid "delete" msgstr "excluir" -#: __init__.py:26 -#, fuzzy +#: __init__.py:24 msgid "grant" -msgstr "Conceder" +msgstr "" -#: __init__.py:27 -#, fuzzy +#: __init__.py:25 msgid "revoke" -msgstr "Revogar" +msgstr "" -#: api.py:22 -msgid "Permissions" -msgstr "Permissões" - -#: api.py:55 +#: models.py:51 msgid "Insufficient permissions." msgstr "Permissões insuficientes." -#: models.py:11 views.py:58 +#: models.py:123 views.py:60 msgid "namespace" msgstr "namespace" -#: models.py:12 views.py:59 +#: models.py:124 views.py:61 msgid "name" msgstr "nome" -#: models.py:13 models.py:82 -msgid "label" -msgstr "rótulo" - -#: models.py:20 models.py:65 views.py:145 views.py:204 +#: models.py:131 models.py:187 views.py:152 views.py:215 msgid "permission" msgstr "permissão" -#: models.py:21 views.py:55 views.py:147 views.py:206 +#: models.py:132 views.py:57 views.py:154 views.py:217 msgid "permissions" msgstr "permissões" -#: models.py:73 +#: models.py:195 msgid "permission holder" msgstr "titular de permissão" -#: models.py:74 +#: models.py:196 msgid "permission holders" msgstr "titulares de permissões" -#: models.py:86 models.py:104 views.py:74 views.py:91 views.py:115 -#: views.py:282 +#: models.py:204 +msgid "label" +msgstr "rótulo" + +#: models.py:208 models.py:239 views.py:76 views.py:93 views.py:117 +#: views.py:334 msgid "role" msgstr "função" -#: models.py:115 +#: models.py:255 msgid "role member" msgstr "membro da função" -#: models.py:116 +#: models.py:256 msgid "role members" msgstr "membros da função" -#: views.py:61 -#, fuzzy -msgid "has permission" -msgstr "permissão" +#: permissions.py:7 +msgid "Permissions" +msgstr "Permissões" -#: views.py:142 views.py:201 +#: permissions.py:9 +msgid "View roles" +msgstr "Ver funções" + +#: permissions.py:10 +msgid "Edit roles" +msgstr "Editar funções" + +#: permissions.py:11 +msgid "Create roles" +msgstr "Criar funções" + +#: permissions.py:12 +msgid "Delete roles" +msgstr "Deletar funções" + +#: permissions.py:13 +msgid "Grant permissions" +msgstr "Conceder permissões" + +#: permissions.py:14 +msgid "Revoke permissions" +msgstr "Revogar as permissões" + +#: views.py:63 +msgid "has permission" +msgstr "" + +#: views.py:149 views.py:212 msgid " and " msgstr "" -#: views.py:142 views.py:201 -#, fuzzy, python-format +#: views.py:149 views.py:212 +#, python-format msgid "%(permissions)s to %(requester)s" msgstr "" -"Permissão \"%(permission)s\" concedida para %(ct_name)s: %(requester)s." -#: views.py:152 -#, fuzzy, python-format +#: views.py:159 +#, python-format msgid "Permission \"%(permission)s\" granted to: %(requester)s." msgstr "" -"Permissão \"%(permission)s\" concedida para %(ct_name)s: %(requester)s." -#: views.py:155 -#, fuzzy, python-format +#: views.py:162 +#, python-format msgid "%(requester)s, already had the permission \"%(permission)s\" granted." msgstr "" -"%(ct_name)s: %(requester)s, já tem permissão \"%(permission)s\" concedida." -#: views.py:167 -#, fuzzy, python-format +#: views.py:173 +#, python-format msgid "" "Are you sure you wish to grant the %(permissions_label)s %(title_suffix)s?" msgstr "" -"Tem certeza que deseja conceder permissão \"%(permission)s\" para " -"%(ct_name)s: %(requester)s" -#: views.py:211 -#, fuzzy, python-format +#: views.py:222 +#, python-format msgid "Permission \"%(permission)s\" revoked from: %(requester)s." -msgstr "Permissão \"%(permission)s\" revogada de %(ct_name)s: %(requester)s." +msgstr "" -#: views.py:214 -#, fuzzy, python-format +#: views.py:225 +#, python-format msgid "%(requester)s, doesn't have the permission \"%(permission)s\" granted." -msgstr "%(ct_name)s: %(requester)s não tem a permissão \"%(permission)s\"." +msgstr "" -#: views.py:226 -#, fuzzy, python-format +#: views.py:236 +#, python-format msgid "" "Are you sure you wish to revoke the %(permissions_label)s %(title_suffix)s?" msgstr "" -"Tem certeza que deseja revogar a permissão \"%(permission)s\" de " -"%(ct_name)s: %(requester)s" -#: views.py:278 +#: views.py:271 views.py:295 +msgid "Users" +msgstr "" + +#: views.py:274 views.py:298 +msgid "Groups" +msgstr "" + +#: views.py:277 views.py:301 +msgid "Special" +msgstr "" + +#: views.py:330 #, python-format msgid "non members of role: %s" msgstr "não membros da função: %s" -#: views.py:279 +#: views.py:331 #, python-format msgid "members of role: %s" msgstr "membros da função: %s" @@ -204,6 +206,3 @@ msgid "" msgstr "" "A lista de funções existentes que são automaticamente designados para " "usuários recém-criados" - -#~ msgid "state" -#~ msgstr "estado" diff --git a/apps/permissions/locale/ru/LC_MESSAGES/django.mo b/apps/permissions/locale/ru/LC_MESSAGES/django.mo index 8f8ad487ab..9ab687cace 100644 Binary files a/apps/permissions/locale/ru/LC_MESSAGES/django.mo and b/apps/permissions/locale/ru/LC_MESSAGES/django.mo differ diff --git a/apps/permissions/locale/ru/LC_MESSAGES/django.po b/apps/permissions/locale/ru/LC_MESSAGES/django.po index b30891a80f..ecf7a2a65d 100644 --- a/apps/permissions/locale/ru/LC_MESSAGES/django.po +++ b/apps/permissions/locale/ru/LC_MESSAGES/django.po @@ -1,179 +1,193 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: # Sergey Glita , 2011. msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" -"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" -"PO-Revision-Date: 2011-11-22 18:57+0000\n" -"Last-Translator: gsv70 \n" -"Language-Team: Russian (http://www.transifex.net/projects/p/mayan-edms/team/ru/)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-02 18:18+0000\n" +"Last-Translator: Roberto Rosario \n" +"Language-Team: Russian (http://www.transifex.net/projects/p/mayan-edms/team/" +"ru/)\n" +"Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: ru\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" +"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:12 -msgid "View roles" -msgstr "Просмотр ролей" - -#: __init__.py:13 -msgid "Edit roles" -msgstr "Изменить роли" - -#: __init__.py:14 -msgid "Create roles" -msgstr "Создание ролей" - -#: __init__.py:15 -msgid "Delete roles" -msgstr "Удаление ролей" - -#: __init__.py:16 -msgid "Grant permissions" -msgstr "Предоставление разрешений" - -#: __init__.py:17 -msgid "Revoke permissions" -msgstr "Отмена разрешений" - -#: __init__.py:19 models.py:87 views.py:38 +#: __init__.py:17 models.py:209 views.py:40 msgid "roles" msgstr "роли" -#: __init__.py:20 +#: __init__.py:18 msgid "create new role" msgstr "создать новую роль" -#: __init__.py:21 +#: __init__.py:19 msgid "edit" msgstr "редактировать" -#: __init__.py:22 +#: __init__.py:20 msgid "members" msgstr "участники" -#: __init__.py:23 +#: __init__.py:21 msgid "role permissions" msgstr "разрешения роли" -#: __init__.py:24 +#: __init__.py:22 msgid "delete" msgstr "удалить" -#: __init__.py:26 +#: __init__.py:24 msgid "grant" msgstr "предоставить" -#: __init__.py:27 +#: __init__.py:25 msgid "revoke" msgstr "отозвать" -#: api.py:22 -msgid "Permissions" -msgstr "Разрешения" - -#: api.py:55 +#: models.py:51 msgid "Insufficient permissions." msgstr "Недостаточно разрешений." -#: models.py:11 views.py:58 +#: models.py:123 views.py:60 msgid "namespace" msgstr "пространство имен" -#: models.py:12 views.py:59 +#: models.py:124 views.py:61 msgid "name" msgstr "имя" -#: models.py:13 models.py:82 -msgid "label" -msgstr "надпись" - -#: models.py:20 models.py:65 views.py:145 views.py:204 +#: models.py:131 models.py:187 views.py:152 views.py:215 msgid "permission" msgstr "разрешение" -#: models.py:21 views.py:55 views.py:147 views.py:206 +#: models.py:132 views.py:57 views.py:154 views.py:217 msgid "permissions" msgstr "разрешения" -#: models.py:73 +#: models.py:195 msgid "permission holder" msgstr "владелец разрешения" -#: models.py:74 +#: models.py:196 msgid "permission holders" msgstr "владельцы разрешения " -#: models.py:86 models.py:104 views.py:74 views.py:91 views.py:115 -#: views.py:282 +#: models.py:204 +msgid "label" +msgstr "надпись" + +#: models.py:208 models.py:239 views.py:76 views.py:93 views.py:117 +#: views.py:334 msgid "role" msgstr "роль" -#: models.py:115 +#: models.py:255 msgid "role member" msgstr "участник" -#: models.py:116 +#: models.py:256 msgid "role members" msgstr "участники" -#: views.py:61 +#: permissions.py:7 +msgid "Permissions" +msgstr "Разрешения" + +#: permissions.py:9 +msgid "View roles" +msgstr "Просмотр ролей" + +#: permissions.py:10 +msgid "Edit roles" +msgstr "Изменить роли" + +#: permissions.py:11 +msgid "Create roles" +msgstr "Создание ролей" + +#: permissions.py:12 +msgid "Delete roles" +msgstr "Удаление ролей" + +#: permissions.py:13 +msgid "Grant permissions" +msgstr "Предоставление разрешений" + +#: permissions.py:14 +msgid "Revoke permissions" +msgstr "Отмена разрешений" + +#: views.py:63 msgid "has permission" msgstr "имеет право" -#: views.py:142 views.py:201 +#: views.py:149 views.py:212 msgid " and " msgstr "и" -#: views.py:142 views.py:201 +#: views.py:149 views.py:212 #, python-format msgid "%(permissions)s to %(requester)s" msgstr "%(permissions)s для %(requester)s" -#: views.py:152 +#: views.py:159 #, python-format msgid "Permission \"%(permission)s\" granted to: %(requester)s." msgstr "Право \"%(permission)s\" предоставлено %(requester)s." -#: views.py:155 +#: views.py:162 #, python-format msgid "%(requester)s, already had the permission \"%(permission)s\" granted." msgstr "%(requester)s уже имеет право \"%(permission)s\"." -#: views.py:167 +#: views.py:173 #, python-format msgid "" "Are you sure you wish to grant the %(permissions_label)s %(title_suffix)s?" msgstr "Вы хотите разрешить %(permissions_label)s %(title_suffix)s?" -#: views.py:211 +#: views.py:222 #, python-format msgid "Permission \"%(permission)s\" revoked from: %(requester)s." msgstr "Право \"%(permission)s\" отозвано у %(requester)s." -#: views.py:214 +#: views.py:225 #, python-format msgid "%(requester)s, doesn't have the permission \"%(permission)s\" granted." msgstr "%(requester)s не имеет права \"%(permission)s\"." -#: views.py:226 +#: views.py:236 #, python-format msgid "" "Are you sure you wish to revoke the %(permissions_label)s %(title_suffix)s?" msgstr "Вы хотите отозвать %(permissions_label)s %(title_suffix)s?" -#: views.py:278 +#: views.py:271 views.py:295 +msgid "Users" +msgstr "" + +#: views.py:274 views.py:298 +msgid "Groups" +msgstr "" + +#: views.py:277 views.py:301 +msgid "Special" +msgstr "" + +#: views.py:330 #, python-format msgid "non members of role: %s" msgstr "не входит в %s" -#: views.py:279 +#: views.py:331 #, python-format msgid "members of role: %s" msgstr "входит в %s" @@ -193,5 +207,3 @@ msgid "" msgstr "" "Список существующих ролей, которые автоматически назначаются вновь " "создаваемым пользователям" - - diff --git a/apps/permissions/managers.py b/apps/permissions/managers.py index e4cfd1fdf5..2284f55e01 100644 --- a/apps/permissions/managers.py +++ b/apps/permissions/managers.py @@ -1,14 +1,27 @@ +import logging + from django.db import models from django.contrib.contenttypes.models import ContentType +from django.db import transaction +from django.core.exceptions import PermissionDenied +from django.shortcuts import get_object_or_404 +from django.db.utils import IntegrityError +from django.core.exceptions import ImproperlyConfigured + +from common.models import AnonymousUserSingleton + + +logger = logging.getLogger(__name__) class RoleMemberManager(models.Manager): def get_roles_for_member(self, member_obj): + member_obj = AnonymousUserSingleton.objects.passthru_check(member_obj) member_type = ContentType.objects.get_for_model(member_obj) return [role_member.role for role_member in self.model.objects.filter(member_type=member_type, member_id=member_obj.pk)] -class PermissionManager(models.Manager): +class StoredPermissionManager(models.Manager): def get_for_holder(self, holder): ct = ContentType.objects.get_for_model(holder) return self.model.objects.filter(permissionholder__holder_type=ct).filter(permissionholder__holder_id=holder.pk) diff --git a/apps/permissions/migrations/0001_initial.py b/apps/permissions/migrations/0001_initial.py new file mode 100644 index 0000000000..e69a10a4db --- /dev/null +++ b/apps/permissions/migrations/0001_initial.py @@ -0,0 +1,105 @@ +# 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 'Permission' + db.create_table('permissions_permission', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('namespace', self.gf('django.db.models.fields.CharField')(max_length=64)), + ('name', self.gf('django.db.models.fields.CharField')(max_length=64)), + ('label', self.gf('django.db.models.fields.CharField')(max_length=96)), + )) + db.send_create_signal('permissions', ['Permission']) + + # Adding unique constraint on 'Permission', fields ['namespace', 'name'] + db.create_unique('permissions_permission', ['namespace', 'name']) + + # Adding model 'PermissionHolder' + db.create_table('permissions_permissionholder', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('permission', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['permissions.Permission'])), + ('holder_type', self.gf('django.db.models.fields.related.ForeignKey')(related_name='permission_holder', to=orm['contenttypes.ContentType'])), + ('holder_id', self.gf('django.db.models.fields.PositiveIntegerField')()), + )) + db.send_create_signal('permissions', ['PermissionHolder']) + + # Adding model 'Role' + db.create_table('permissions_role', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=64)), + ('label', self.gf('django.db.models.fields.CharField')(unique=True, max_length=64)), + )) + db.send_create_signal('permissions', ['Role']) + + # Adding model 'RoleMember' + db.create_table('permissions_rolemember', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('role', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['permissions.Role'])), + ('member_type', self.gf('django.db.models.fields.related.ForeignKey')(related_name='role_member', to=orm['contenttypes.ContentType'])), + ('member_id', self.gf('django.db.models.fields.PositiveIntegerField')()), + )) + db.send_create_signal('permissions', ['RoleMember']) + + + def backwards(self, orm): + + # Removing unique constraint on 'Permission', fields ['namespace', 'name'] + db.delete_unique('permissions_permission', ['namespace', 'name']) + + # Deleting model 'Permission' + db.delete_table('permissions_permission') + + # Deleting model 'PermissionHolder' + db.delete_table('permissions_permissionholder') + + # Deleting model 'Role' + db.delete_table('permissions_role') + + # Deleting model 'RoleMember' + db.delete_table('permissions_rolemember') + + + models = { + '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'}) + }, + 'permissions.permission': { + 'Meta': {'ordering': "('namespace', 'label')", 'unique_together': "(('namespace', 'name'),)", 'object_name': 'Permission'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '96'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'namespace': ('django.db.models.fields.CharField', [], {'max_length': '64'}) + }, + 'permissions.permissionholder': { + 'Meta': {'object_name': 'PermissionHolder'}, + 'holder_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'holder_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'permission_holder'", 'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']"}) + }, + 'permissions.role': { + 'Meta': {'ordering': "('label',)", 'object_name': 'Role'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}) + }, + 'permissions.rolemember': { + 'Meta': {'object_name': 'RoleMember'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'member_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'member_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'role_member'", 'to': "orm['contenttypes.ContentType']"}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Role']"}) + } + } + + complete_apps = ['permissions'] diff --git a/apps/permissions/migrations/0002_auto__add_storedpermission__add_unique_storedpermission_namespace_name.py b/apps/permissions/migrations/0002_auto__add_storedpermission__add_unique_storedpermission_namespace_name.py new file mode 100644 index 0000000000..0109b2548e --- /dev/null +++ b/apps/permissions/migrations/0002_auto__add_storedpermission__add_unique_storedpermission_namespace_name.py @@ -0,0 +1,81 @@ +# 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 'StoredPermission' + db.create_table('permissions_storedpermission', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('namespace', self.gf('django.db.models.fields.CharField')(max_length=64)), + ('name', self.gf('django.db.models.fields.CharField')(max_length=64)), + )) + db.send_create_signal('permissions', ['StoredPermission']) + + # Adding unique constraint on 'StoredPermission', fields ['namespace', 'name'] + db.create_unique('permissions_storedpermission', ['namespace', 'name']) + + # Changing field 'PermissionHolder.permission' + db.alter_column('permissions_permissionholder', 'permission_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['permissions.StoredPermission'])) + + + def backwards(self, orm): + + # Removing unique constraint on 'StoredPermission', fields ['namespace', 'name'] + db.delete_unique('permissions_storedpermission', ['namespace', 'name']) + + # Deleting model 'StoredPermission' + db.delete_table('permissions_storedpermission') + + # Changing field 'PermissionHolder.permission' + db.alter_column('permissions_permissionholder', 'permission_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['permissions.Permission'])) + + + models = { + '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'}) + }, + 'permissions.permission': { + 'Meta': {'ordering': "('namespace',)", 'unique_together': "(('namespace', 'name'),)", 'object_name': 'Permission'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '96'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'namespace': ('django.db.models.fields.CharField', [], {'max_length': '64'}) + }, + 'permissions.permissionholder': { + 'Meta': {'object_name': 'PermissionHolder'}, + 'holder_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'holder_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'permission_holder'", 'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.StoredPermission']"}) + }, + 'permissions.role': { + 'Meta': {'ordering': "('label',)", 'object_name': 'Role'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}) + }, + 'permissions.rolemember': { + 'Meta': {'object_name': 'RoleMember'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'member_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'member_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'role_member'", 'to': "orm['contenttypes.ContentType']"}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Role']"}) + }, + 'permissions.storedpermission': { + 'Meta': {'ordering': "('namespace',)", 'unique_together': "(('namespace', 'name'),)", 'object_name': 'StoredPermission'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'namespace': ('django.db.models.fields.CharField', [], {'max_length': '64'}) + } + } + + complete_apps = ['permissions'] diff --git a/apps/permissions/migrations/0003_clear_permission_holders.py b/apps/permissions/migrations/0003_clear_permission_holders.py new file mode 100644 index 0000000000..b745b97a3a --- /dev/null +++ b/apps/permissions/migrations/0003_clear_permission_holders.py @@ -0,0 +1,61 @@ +# 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 permission_holder in orm.PermissionHolder.objects.all(): + permission_holder.delete() + + + def backwards(self, orm): + raise RuntimeError("Cannot reverse this migration.") + + + models = { + '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'}) + }, + 'permissions.permission': { + 'Meta': {'ordering': "('namespace',)", 'unique_together': "(('namespace', 'name'),)", 'object_name': 'Permission'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '96'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'namespace': ('django.db.models.fields.CharField', [], {'max_length': '64'}) + }, + 'permissions.permissionholder': { + 'Meta': {'object_name': 'PermissionHolder'}, + 'holder_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'holder_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'permission_holder'", 'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.StoredPermission']"}) + }, + 'permissions.role': { + 'Meta': {'ordering': "('label',)", 'object_name': 'Role'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}) + }, + 'permissions.rolemember': { + 'Meta': {'object_name': 'RoleMember'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'member_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'member_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'role_member'", 'to': "orm['contenttypes.ContentType']"}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Role']"}) + }, + 'permissions.storedpermission': { + 'Meta': {'ordering': "('namespace',)", 'unique_together': "(('namespace', 'name'),)", 'object_name': 'StoredPermission'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'namespace': ('django.db.models.fields.CharField', [], {'max_length': '64'}) + } + } + + complete_apps = ['permissions'] diff --git a/apps/permissions/migrations/0004_auto__del_permission__del_unique_permission_namespace_name.py b/apps/permissions/migrations/0004_auto__del_permission__del_unique_permission_namespace_name.py new file mode 100644 index 0000000000..adb93d9490 --- /dev/null +++ b/apps/permissions/migrations/0004_auto__del_permission__del_unique_permission_namespace_name.py @@ -0,0 +1,69 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Removing unique constraint on 'Permission', fields ['namespace', 'name'] + db.delete_unique('permissions_permission', ['namespace', 'name']) + + # Deleting model 'Permission' + db.delete_table('permissions_permission') + + + def backwards(self, orm): + + # Adding model 'Permission' + db.create_table('permissions_permission', ( + ('namespace', self.gf('django.db.models.fields.CharField')(max_length=64)), + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('name', self.gf('django.db.models.fields.CharField')(max_length=64)), + ('label', self.gf('django.db.models.fields.CharField')(max_length=96)), + )) + db.send_create_signal('permissions', ['Permission']) + + # Adding unique constraint on 'Permission', fields ['namespace', 'name'] + db.create_unique('permissions_permission', ['namespace', 'name']) + + + models = { + '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'}) + }, + 'permissions.permissionholder': { + 'Meta': {'object_name': 'PermissionHolder'}, + 'holder_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'holder_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'permission_holder'", 'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.StoredPermission']"}) + }, + 'permissions.role': { + 'Meta': {'ordering': "('label',)", 'object_name': 'Role'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}) + }, + 'permissions.rolemember': { + 'Meta': {'object_name': 'RoleMember'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'member_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'member_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'role_member'", 'to': "orm['contenttypes.ContentType']"}), + 'role': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Role']"}) + }, + 'permissions.storedpermission': { + 'Meta': {'ordering': "('namespace',)", 'unique_together': "(('namespace', 'name'),)", 'object_name': 'StoredPermission'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'namespace': ('django.db.models.fields.CharField', [], {'max_length': '64'}) + } + } + + complete_apps = ['permissions'] diff --git a/apps/permissions/migrations/__init__.py b/apps/permissions/migrations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/permissions/models.py b/apps/permissions/models.py index 637a59efab..ff7dfd957f 100644 --- a/apps/permissions/models.py +++ b/apps/permissions/models.py @@ -1,60 +1,182 @@ +from __future__ import absolute_import + +import logging + from django.db import models from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import ugettext from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes import generic from django.contrib.auth.models import User +from django.core.exceptions import PermissionDenied -from permissions.managers import RoleMemberManager, PermissionManager +from common.models import AnonymousUserSingleton + +from .managers import (RoleMemberManager, StoredPermissionManager) + +logger = logging.getLogger(__name__) -class Permission(models.Model): +class PermissionNamespace(object): + def __init__(self, name, label): + self.name = name + self.label = label + + def __unicode__(self): + return unicode(self.label) + + +class PermissionDoesNotExists(Exception): + pass + + +class PermissionManager(object): + _permissions = {} + DoesNotExist = PermissionDoesNotExists() + + @classmethod + def register(cls, namespace, name, label): + permission = Permission(namespace, name, label) + cls._permissions[permission.uuid] = permission + return permission + + @classmethod + def check_permissions(cls, requester, permission_list): + for permission in permission_list: + if permission.requester_has_this(requester): + return True + + logger.debug('no permission') + + raise PermissionDenied(ugettext(u'Insufficient permissions.')) + + @classmethod + def get_for_holder(cls, holder): + return StoredPermission.objects.get_for_holder(holder) + + @classmethod + def all(cls): + # Return sorted permisions by namespace.name + return sorted(cls._permissions.values(), key=lambda x: x.namespace.name) + + @classmethod + def get(cls, get_dict, proxy_only=False): + if 'pk' in get_dict: + try: + if proxy_only: + return cls._permissions[get_dict['pk']] + else: + return cls._permissions[get_dict['pk']].get_stored_permission() + except KeyError: + raise Permission.DoesNotExist + + + def __init__(self, model): + self.model = model + + +class Permission(object): + DoesNotExist = PermissionDoesNotExists + + def __init__(self, namespace, name, label): + self.namespace = namespace + self.name = name + self.label = label + self.pk = self.uuid + + def __unicode__(self): + return unicode(self.label) + + def __str__(self): + return str(self.__unicode__()) + + @property + def uuid(self): + return u'%s.%s' % (self.namespace.name, self.name) + + @property + def stored_permission(self): + return self.get_stored_permission() + + def get_stored_permission(self): + stored_permission, created = StoredPermission.objects.get_or_create( + namespace=self.namespace.name, + name=self.name, + ) + stored_permission.label = self.label + stored_permission.save() + stored_permission.volatile_permission = self + return stored_permission + + def requester_has_this(self, requester): + stored_permission = self.get_stored_permission() + return stored_permission.requester_has_this(requester) + + def save(self, *args, **kwargs): + return self.get_stored_permission() + +Permission.objects = PermissionManager(Permission) +Permission._default_manager = Permission.objects + + +class StoredPermission(models.Model): namespace = models.CharField(max_length=64, verbose_name=_(u'namespace')) name = models.CharField(max_length=64, verbose_name=_(u'name')) - label = models.CharField(max_length=96, verbose_name=_(u'label')) - objects = PermissionManager() + objects = StoredPermissionManager() class Meta: - ordering = ('namespace', 'label') + ordering = ('namespace', ) unique_together = ('namespace', 'name') verbose_name = _(u'permission') verbose_name_plural = _(u'permissions') + def __init__(self, *args, **kwargs): + super(StoredPermission, self).__init__(*args, **kwargs) + self.volatile_permission = Permission.objects.get({'pk': '%s.%s' % (self.namespace, self.name)}, proxy_only=True) + def __unicode__(self): - return self.label + return unicode(getattr(self, 'volatile_permission', self.name)) def get_holders(self): return [holder.holder_object for holder in self.permissionholder_set.all()] - def has_permission(self, requester): - if isinstance(requester, User): - if requester.is_superuser or requester.is_staff: + def requester_has_this(self, actor): + actor = AnonymousUserSingleton.objects.passthru_check(actor) + logger.debug('actor: %s' % actor) + if isinstance(actor, User): + if actor.is_superuser or actor.is_staff: return True # Request is one of the permission's holders? - if requester in self.get_holders(): + if actor in self.get_holders(): return True # If not check if the requesters memberships objects is one of # the permission's holder? - roles = RoleMember.objects.get_roles_for_member(requester) + roles = RoleMember.objects.get_roles_for_member(actor) - if isinstance(requester, User): - groups = requester.groups.all() + if isinstance(actor, User): + groups = actor.groups.all() else: groups = [] for membership in list(set(roles) | set(groups)): - if self.has_permission(membership): + if self.requester_has_this(membership): return True - def grant_to(self, requester): - permission_holder, created = PermissionHolder.objects.get_or_create(permission=self, holder_type=ContentType.objects.get_for_model(requester), holder_id=requester.pk) + logger.debug('Fallthru') + return False + + def grant_to(self, actor): + actor = AnonymousUserSingleton.objects.passthru_check(actor) + permission_holder, created = PermissionHolder.objects.get_or_create(permission=self, holder_type=ContentType.objects.get_for_model(actor), holder_id=actor.pk) return created - def revoke_from(self, holder): + def revoke_from(self, actor): + actor = AnonymousUserSingleton.objects.passthru_check(actor) try: - permission_holder = PermissionHolder.objects.get(permission=self, holder_type=ContentType.objects.get_for_model(holder), holder_id=holder.pk) + permission_holder = PermissionHolder.objects.get(permission=self, holder_type=ContentType.objects.get_for_model(actor), holder_id=actor.pk) permission_holder.delete() return True except PermissionHolder.DoesNotExist: @@ -62,7 +184,7 @@ class Permission(models.Model): class PermissionHolder(models.Model): - permission = models.ForeignKey(Permission, verbose_name=_(u'permission')) + permission = models.ForeignKey(StoredPermission, verbose_name=_(u'permission')) holder_type = models.ForeignKey(ContentType, related_name='permission_holder', limit_choices_to={'model__in': ('user', 'group', 'role')}) @@ -86,12 +208,6 @@ class Role(models.Model): verbose_name = _(u'role') verbose_name_plural = _(u'roles') - def add_member(self, member): - role_member, created = RoleMember.objects.get_or_create( - role=self, - member_type=ContentType.objects.get_for_model(member), - member_id=member.pk) - def __unicode__(self): return self.label @@ -99,12 +215,36 @@ class Role(models.Model): def get_absolute_url(self): return ('role_list',) + def add_member(self, member): + member = AnonymousUserSingleton.objects.passthru_check(member) + role_member, created = RoleMember.objects.get_or_create( + role=self, + member_type=ContentType.objects.get_for_model(member), + member_id=member.pk) + if not created: + raise Exception('Unable to add member to role') + + def remove_member(self, member): + member = AnonymousUserSingleton.objects.passthru_check(member) + member_type=ContentType.objects.get_for_model(member) + role_member = RoleMember.objects.get(role=self, member_type=member_type, member_id=member.pk) + role_member.delete() + + def members(self, filter_dict=None): + filter_dict = filter_dict or {} + return [member.member_object for member in self.rolemember_set.filter(**filter_dict)] + class RoleMember(models.Model): role = models.ForeignKey(Role, verbose_name=_(u'role')) member_type = models.ForeignKey(ContentType, related_name='role_member', - limit_choices_to={'model__in': ('user', 'group')}) + limit_choices_to={ + 'model__in': ( + 'user', 'group', 'anonymoususersingleton' + ) + } + ) member_id = models.PositiveIntegerField() member_object = generic.GenericForeignKey(ct_field='member_type', fk_field='member_id') diff --git a/apps/permissions/permissions.py b/apps/permissions/permissions.py new file mode 100644 index 0000000000..ce97e1e150 --- /dev/null +++ b/apps/permissions/permissions.py @@ -0,0 +1,14 @@ +from __future__ import absolute_import + +from django.utils.translation import ugettext_lazy as _ + +from .models import Permission, PermissionNamespace + +permissions_namespace = PermissionNamespace('permissions', _(u'Permissions')) + +PERMISSION_ROLE_VIEW = Permission.objects.register(permissions_namespace, 'role_view', _(u'View roles')) +PERMISSION_ROLE_EDIT = Permission.objects.register(permissions_namespace, 'role_edit', _(u'Edit roles')) +PERMISSION_ROLE_CREATE = Permission.objects.register(permissions_namespace, 'role_create', _(u'Create roles')) +PERMISSION_ROLE_DELETE = Permission.objects.register(permissions_namespace, 'role_delete', _(u'Delete roles')) +PERMISSION_PERMISSION_GRANT = Permission.objects.register(permissions_namespace, 'permission_grant', _(u'Grant permissions')) +PERMISSION_PERMISSION_REVOKE = Permission.objects.register(permissions_namespace, 'permission_revoke', _(u'Revoke permissions')) diff --git a/apps/permissions/runtime.py b/apps/permissions/runtime.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/permissions/templatetags/permission_tags.py b/apps/permissions/templatetags/permission_tags.py index fecffa2796..0d0974145b 100644 --- a/apps/permissions/templatetags/permission_tags.py +++ b/apps/permissions/templatetags/permission_tags.py @@ -1,8 +1,7 @@ from django.core.exceptions import PermissionDenied -from django.template import TemplateSyntaxError, Library, \ - Node, Variable +from django.template import TemplateSyntaxError, Library, Node, Variable -from permissions.api import check_permissions as check_permission_function +from permissions.models import Permission register = Library() @@ -21,7 +20,7 @@ class CheckPermissionsNode(Node): return u'' requester = Variable(self.requester).resolve(context) try: - check_permission_function(requester, permission_list) + Permission.objects.check_permissions(requester, permission_list) context[u'permission'] = True return u'' except PermissionDenied: diff --git a/apps/permissions/tests.py b/apps/permissions/tests.py deleted file mode 100644 index 2247054b35..0000000000 --- a/apps/permissions/tests.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -This file demonstrates two different styles of tests (one doctest and one -unittest). These will both pass when you run "manage.py test". - -Replace these 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.failUnlessEqual(1 + 1, 2) - -__test__ = {"doctest": """ -Another way to test that 1 + 1 is equal to 2. - ->>> 1 + 1 == 2 -True -"""} - diff --git a/apps/permissions/urls.py b/apps/permissions/urls.py index ed85c44527..a052f1471b 100644 --- a/apps/permissions/urls.py +++ b/apps/permissions/urls.py @@ -7,7 +7,7 @@ urlpatterns = patterns('permissions.views', url(r'^role/(?P\d+)/edit/$', 'role_edit', (), 'role_edit'), url(r'^role/(?P\d+)/delete/$', 'role_delete', (), 'role_delete'), url(r'^role/(?P\d+)/members/$', 'role_members', (), 'role_members'), - + url(r'^permissions/multiple/grant/$', 'permission_grant', (), 'permission_multiple_grant'), url(r'^permissions/multiple/revoke/$', 'permission_revoke', (), 'permission_multiple_revoke'), ) diff --git a/apps/permissions/views.py b/apps/permissions/views.py index 3f58846eb9..6e8673de12 100644 --- a/apps/permissions/views.py +++ b/apps/permissions/views.py @@ -1,8 +1,10 @@ +from __future__ import absolute_import + import operator import itertools from django.utils.translation import ugettext_lazy as _ -from django.http import HttpResponseRedirect +from django.http import HttpResponseRedirect, Http404 from django.shortcuts import render_to_response, get_object_or_404 from django.template import RequestContext from django.contrib import messages @@ -11,24 +13,24 @@ from django.core.urlresolvers import reverse from django.views.generic.create_update import create_object, delete_object, update_object from django.contrib.contenttypes.models import ContentType from django.contrib.auth.models import User, Group -from django.contrib.contenttypes.models import ContentType from django.utils.simplejson import loads from common.views import assign_remove -from common.utils import generate_choices_w_labels, encapsulate +from common.utils import generate_choices_w_labels, encapsulate, get_object_name from common.widgets import two_state_template +from common.models import AnonymousUserSingleton +from acls.classes import EncapsulatedObject -from permissions.models import Role, Permission, PermissionHolder, RoleMember -from permissions.forms import RoleForm, RoleForm_view -from permissions import PERMISSION_ROLE_VIEW, PERMISSION_ROLE_EDIT, \ - PERMISSION_ROLE_CREATE, PERMISSION_ROLE_DELETE, PERMISSION_PERMISSION_GRANT, \ - PERMISSION_PERMISSION_REVOKE -from permissions.api import check_permissions, namespace_titles -from permissions.widgets import role_permission_link +from .models import Role, Permission, PermissionHolder, RoleMember +from .forms import RoleForm, RoleForm_view +from .permissions import (PERMISSION_ROLE_VIEW, PERMISSION_ROLE_EDIT, + PERMISSION_ROLE_CREATE, PERMISSION_ROLE_DELETE, + PERMISSION_PERMISSION_GRANT, PERMISSION_PERMISSION_REVOKE) +from .widgets import role_permission_link def role_list(request): - check_permissions(request.user, [PERMISSION_ROLE_VIEW]) + Permission.objects.check_permissions(request.user, [PERMISSION_ROLE_VIEW]) return object_list( request, @@ -42,7 +44,7 @@ def role_list(request): def role_permissions(request, role_id): - check_permissions(request.user, [PERMISSION_PERMISSION_GRANT, PERMISSION_PERMISSION_REVOKE]) + Permission.objects.check_permissions(request.user, [PERMISSION_PERMISSION_GRANT, PERMISSION_PERMISSION_REVOKE]) role = get_object_or_404(Role, pk=role_id) form = RoleForm_view(instance=role) @@ -55,11 +57,11 @@ def role_permissions(request, role_id): 'title': _(u'permissions'), 'object_list': Permission.objects.all(), 'extra_columns': [ - {'name': _(u'namespace'), 'attribute': encapsulate(lambda x: namespace_titles[x.namespace] if x.namespace in namespace_titles else x.namespace)}, - {'name': _(u'name'), 'attribute': u'label'}, + {'name': _(u'namespace'), 'attribute': encapsulate(lambda x: x.namespace)}, + {'name': _(u'name'), 'attribute': encapsulate(lambda x: x.label)}, { 'name':_(u'has permission'), - 'attribute': encapsulate(lambda x: two_state_template(x.has_permission(role))), + 'attribute': encapsulate(lambda x: two_state_template(x.requester_has_this(role))), }, ], 'hide_link': True, @@ -84,7 +86,7 @@ def role_permissions(request, role_id): def role_edit(request, role_id): - check_permissions(request.user, [PERMISSION_ROLE_EDIT]) + Permission.objects.check_permissions(request.user, [PERMISSION_ROLE_EDIT]) return update_object(request, template_name='generic_form.html', form_class=RoleForm, object_id=role_id, extra_context={ @@ -92,7 +94,7 @@ def role_edit(request, role_id): def role_create(request): - check_permissions(request.user, [PERMISSION_ROLE_CREATE]) + Permission.objects.check_permissions(request.user, [PERMISSION_ROLE_CREATE]) return create_object(request, model=Role, template_name='generic_form.html', @@ -100,7 +102,7 @@ def role_create(request): def role_delete(request, role_id): - check_permissions(request.user, [PERMISSION_ROLE_DELETE]) + Permission.objects.check_permissions(request.user, [PERMISSION_ROLE_DELETE]) next = request.POST.get('next', request.GET.get('next', request.META.get('HTTP_REFERER', '/'))) previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', '/'))) @@ -118,7 +120,7 @@ def role_delete(request, role_id): def permission_grant(request): - check_permissions(request.user, [PERMISSION_PERMISSION_GRANT]) + Permission.objects.check_permissions(request.user, [PERMISSION_PERMISSION_GRANT]) items_property_list = loads(request.GET.get('items_property_list', [])) post_action_redirect = None @@ -127,20 +129,25 @@ def permission_grant(request): items = [] for item_properties in items_property_list: - permission = get_object_or_404(Permission, pk=item_properties['permission_id']) + #permission = get_object_or_404(Permission, pk=item_properties['permission_id']) + try: + permission = Permission.objects.get({'pk': item_properties['permission_id']}) + except Permission.DoesNotExist: + raise Http404 + ct = get_object_or_404(ContentType, app_label=item_properties['requester_app_label'], model=item_properties['requester_model']) requester_model = ct.model_class() requester = get_object_or_404(requester_model, pk=item_properties['requester_id']) items.append({'requester': requester, 'permission': permission}) - + sorted_items = sorted(items, key=operator.itemgetter('requester')) # Group items by requester groups = itertools.groupby(sorted_items, key=operator.itemgetter('requester')) grouped_items = [(grouper, [permission['permission'] for permission in group_data]) for grouper, group_data in groups] - + # Warning: trial and error black magic ahead title_suffix = _(u' and ').join([_(u'%(permissions)s to %(requester)s') % {'permissions': ', '.join(['"%s"' % unicode(ps) for ps in p]), 'requester': unicode(r)} for r, p in grouped_items]) - + if len(grouped_items) == 1 and len(grouped_items[0][1]) == 1: permissions_label = _(u'permission') else: @@ -158,7 +165,6 @@ def permission_grant(request): return HttpResponseRedirect(next) context = { - 'delete_view': True, 'previous': previous, 'next': next, 'form_icon': u'key_add.png', @@ -168,16 +174,16 @@ def permission_grant(request): 'permissions_label': permissions_label, 'title_suffix': title_suffix, } - + if len(grouped_items) == 1: context['object'] = grouped_items[0][0] return render_to_response('generic_confirm.html', context, context_instance=RequestContext(request)) - + def permission_revoke(request): - check_permissions(request.user, [PERMISSION_PERMISSION_REVOKE]) + Permission.objects.check_permissions(request.user, [PERMISSION_PERMISSION_REVOKE]) items_property_list = loads(request.GET.get('items_property_list', [])) post_action_redirect = None @@ -186,20 +192,25 @@ def permission_revoke(request): items = [] for item_properties in items_property_list: - permission = get_object_or_404(Permission, pk=item_properties['permission_id']) + #permission = get_object_or_404(Permission, pk=item_properties['permission_id']) + try: + permission = Permission.objects.get({'pk': item_properties['permission_id']}) + except Permission.DoesNotExist: + raise Http404 + ct = get_object_or_404(ContentType, app_label=item_properties['requester_app_label'], model=item_properties['requester_model']) requester_model = ct.model_class() requester = get_object_or_404(requester_model, pk=item_properties['requester_id']) items.append({'requester': requester, 'permission': permission}) - + sorted_items = sorted(items, key=operator.itemgetter('requester')) # Group items by requester groups = itertools.groupby(sorted_items, key=operator.itemgetter('requester')) grouped_items = [(grouper, [permission['permission'] for permission in group_data]) for grouper, group_data in groups] - + # Warning: trial and error black magic ahead title_suffix = _(u' and ').join([_(u'%(permissions)s to %(requester)s') % {'permissions': ', '.join(['"%s"' % unicode(ps) for ps in p]), 'requester': unicode(r)} for r, p in grouped_items]) - + if len(grouped_items) == 1 and len(grouped_items[0][1]) == 1: permissions_label = _(u'permission') else: @@ -217,7 +228,6 @@ def permission_revoke(request): return HttpResponseRedirect(next) context = { - 'delete_view': True, 'previous': previous, 'next': next, 'form_icon': u'key_delete.png', @@ -227,7 +237,7 @@ def permission_revoke(request): 'permissions_label': permissions_label, 'title_suffix': title_suffix, } - + if len(grouped_items) == 1: context['object'] = grouped_items[0][0] @@ -235,44 +245,86 @@ def permission_revoke(request): context_instance=RequestContext(request)) -def get_role_members(role): +class Member(EncapsulatedObject): + source_object_name = u'member_object' + + +def _as_choice_list(items): + return sorted([(Member.encapsulate(item).gid, get_object_name(item, display_object_type=False)) for item in items], key=lambda x: x[1]) + + +def get_role_members(role, separate=False): user_ct = ContentType.objects.get(model='user') group_ct = ContentType.objects.get(model='group') - return [member.member_object for member in role.rolemember_set.filter(member_type__in=[user_ct, group_ct])] + anonymous = ContentType.objects.get(model='anonymoususersingleton') + + users = role.members(filter_dict={'member_type': user_ct}) + groups = role.members(filter_dict={'member_type': group_ct}) + anonymous = role.members(filter_dict={'member_type': anonymous}) + + if separate: + return users, groups, anonymous + else: + members = [] + + if users: + members.append((_(u'Users'), _as_choice_list(list(users)))) + + if groups: + members.append((_(u'Groups'), _as_choice_list(list(groups)))) + + if anonymous: + members.append((_(u'Special'), _as_choice_list(list(anonymous)))) + + return members def get_non_role_members(role): #non members = all users - members - staff - super users + member_users, member_groups, member_anonymous = get_role_members(role, separate=True) + staff_users = User.objects.filter(is_staff=True) super_users = User.objects.filter(is_superuser=True) - users = set(User.objects.exclude(pk__in=[member.pk for member in get_role_members(role)])) - set(staff_users) - set(super_users) - groups = set(Group.objects.exclude(pk__in=[member.pk for member in get_role_members(role)])) - return list(users | groups) + + users = set(User.objects.all()) - set(member_users) - set(staff_users) - set(super_users) + groups = set(Group.objects.all()) - set(member_groups) + anonymous = set([AnonymousUserSingleton.objects.get()]) - set(member_anonymous) + + non_members = [] + if users: + non_members.append((_(u'Users'), _as_choice_list(list(users)))) + + if groups: + non_members.append((_(u'Groups'), _as_choice_list(list(groups)))) + + if anonymous: + non_members.append((_(u'Special'), _as_choice_list(list(anonymous)))) + + #non_holder_list.append((_(u'Special'), _as_choice_list([AnonymousUserSingleton.objects.get()]))) + + return non_members def add_role_member(role, selection): - model, pk = selection.split(u',') - ct = ContentType.objects.get(model=model) - new_member, created = RoleMember.objects.get_or_create(role=role, member_type=ct, member_id=pk) - if not created: - raise Exception + member = Member.get(selection).source_object + role.add_member(member) def remove_role_member(role, selection): - model, pk = selection.split(u',') - ct = ContentType.objects.get(model=model) - member = RoleMember.objects.get(role=role, member_type=ct, member_id=pk) - member.delete() + member = Member.get(selection).source_object + role.remove_member(member) def role_members(request, role_id): - check_permissions(request.user, [PERMISSION_ROLE_EDIT]) + Permission.objects.check_permissions(request.user, [PERMISSION_ROLE_EDIT]) role = get_object_or_404(Role, pk=role_id) return assign_remove( request, - left_list=lambda: generate_choices_w_labels(get_non_role_members(role)), - right_list=lambda: generate_choices_w_labels(get_role_members(role)), + #left_list=lambda: generate_choices_w_labels(get_non_role_members(role)), + left_list=lambda: get_non_role_members(role), + #right_list=lambda: generate_choices_w_labels(get_role_members(role)), + right_list=lambda: get_role_members(role), add_method=lambda x: add_role_member(role, x), remove_method=lambda x: remove_role_member(role, x), left_list_title=_(u'non members of role: %s') % role, @@ -280,5 +332,6 @@ def role_members(request, role_id): extra_context={ 'object': role, 'object_name': _(u'role'), - } + }, + grouped=True, ) diff --git a/apps/project_setup/__init__.py b/apps/project_setup/__init__.py index 7f160810a2..c8eca31d1f 100644 --- a/apps/project_setup/__init__.py +++ b/apps/project_setup/__init__.py @@ -2,5 +2,4 @@ from django.utils.translation import ugettext_lazy as _ from navigation.api import register_top_menu -#TODO: FIXME dynamic children_path_regext on api register_setup -register_top_menu('setup_menu', link={'text': _(u'setup'), 'view': 'setup_list', 'famfam': 'cog'}, children_path_regex=[r'^settings/', r'^user_management/', r'^permissions', r'^documents/type', r'^metadata/setup', r'sources/setup', r'grouping/setup'], position=-2) +setup_link = register_top_menu('setup_menu', link={'text': _(u'setup'), 'view': 'setup_list', 'famfam': 'cog'}, position=-2) diff --git a/apps/project_setup/api.py b/apps/project_setup/api.py index 424d250915..1d11179701 100644 --- a/apps/project_setup/api.py +++ b/apps/project_setup/api.py @@ -1,5 +1,13 @@ +from __future__ import absolute_import + +from . import setup_link + setup_items = [] def register_setup(link): setup_items.append(link) + + # Append the link's children_view_regex to the setup main menu children view regex + setup_link.setdefault('children_view_regex', []) + setup_link['children_view_regex'].extend(link.get('children_view_regex', [])) diff --git a/apps/project_setup/locale/en/LC_MESSAGES/django.po b/apps/project_setup/locale/en/LC_MESSAGES/django.po index 2a81c4bb2a..9a6e1c4017 100644 --- a/apps/project_setup/locale/en/LC_MESSAGES/django.po +++ b/apps/project_setup/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -21,6 +21,6 @@ msgstr "" msgid "setup" msgstr "" -#: views.py:13 +#: views.py:15 msgid "setup items" msgstr "" diff --git a/apps/project_setup/locale/es/LC_MESSAGES/django.mo b/apps/project_setup/locale/es/LC_MESSAGES/django.mo index 37f3fb8053..8094ae0b61 100644 Binary files a/apps/project_setup/locale/es/LC_MESSAGES/django.mo and b/apps/project_setup/locale/es/LC_MESSAGES/django.mo differ diff --git a/apps/project_setup/locale/es/LC_MESSAGES/django.po b/apps/project_setup/locale/es/LC_MESSAGES/django.po index 022627b88d..82e75e203a 100644 --- a/apps/project_setup/locale/es/LC_MESSAGES/django.po +++ b/apps/project_setup/locale/es/LC_MESSAGES/django.po @@ -8,9 +8,9 @@ msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" "PO-Revision-Date: 2011-11-04 01:03+0000\n" -"Last-Translator: rosarior \n" +"Last-Translator: Roberto Rosario \n" "Language-Team: Spanish (Castilian) (http://www.transifex.net/projects/p/" "mayan-edms/team/es/)\n" "Language: es\n" @@ -23,6 +23,6 @@ msgstr "" msgid "setup" msgstr "configuración" -#: views.py:13 +#: views.py:15 msgid "setup items" msgstr "elementos de configuración" diff --git a/apps/project_setup/locale/it/LC_MESSAGES/django.mo b/apps/project_setup/locale/it/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..8147693df5 Binary files /dev/null and b/apps/project_setup/locale/it/LC_MESSAGES/django.mo differ diff --git a/apps/project_setup/locale/it/LC_MESSAGES/django.po b/apps/project_setup/locale/it/LC_MESSAGES/django.po new file mode 100644 index 0000000000..647c2314ad --- /dev/null +++ b/apps/project_setup/locale/it/LC_MESSAGES/django.po @@ -0,0 +1,28 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# , 2011. +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2011-12-09 18:08+0000\n" +"Last-Translator: Pierpaolo Baldan \n" +"Language-Team: Italian (http://www.transifex.net/projects/p/mayan-edms/team/" +"it/)\n" +"Language: it\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:6 +msgid "setup" +msgstr "configura" + +#: views.py:15 +msgid "setup items" +msgstr "parametri di configurazione" diff --git a/apps/project_setup/locale/pl/LC_MESSAGES/django.mo b/apps/project_setup/locale/pl/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..278fc3c122 Binary files /dev/null and b/apps/project_setup/locale/pl/LC_MESSAGES/django.mo differ diff --git a/apps/project_setup/locale/pl/LC_MESSAGES/django.po b/apps/project_setup/locale/pl/LC_MESSAGES/django.po new file mode 100644 index 0000000000..d908334073 --- /dev/null +++ b/apps/project_setup/locale/pl/LC_MESSAGES/django.po @@ -0,0 +1,29 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# mic , 2012. +# , 2012. +# , 2012. +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-20 21:42+0000\n" +"Last-Translator: mic \n" +"Language-Team: Polish (http://www.transifex.net/projects/p/mayan-edms/language/pl/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pl\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" + +#: __init__.py:6 +msgid "setup" +msgstr "ustawienia" + +#: views.py:15 +msgid "setup items" +msgstr "elementy konfiguracji" diff --git a/apps/project_setup/locale/pt/LC_MESSAGES/django.mo b/apps/project_setup/locale/pt/LC_MESSAGES/django.mo index 7c88252009..0686fd6a3c 100644 Binary files a/apps/project_setup/locale/pt/LC_MESSAGES/django.mo and b/apps/project_setup/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/project_setup/locale/pt/LC_MESSAGES/django.po b/apps/project_setup/locale/pt/LC_MESSAGES/django.po index 7ffd621c32..b345be074d 100644 --- a/apps/project_setup/locale/pt/LC_MESSAGES/django.po +++ b/apps/project_setup/locale/pt/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" "PO-Revision-Date: 2011-11-02 01:26+0000\n" "Last-Translator: emersonsoares \n" "Language-Team: Portuguese (http://www.transifex.net/projects/p/mayan-edms/" @@ -23,6 +23,6 @@ msgstr "" msgid "setup" msgstr "instalação" -#: views.py:13 +#: views.py:15 msgid "setup items" msgstr "itens de configuração" diff --git a/apps/project_setup/locale/ru/LC_MESSAGES/django.mo b/apps/project_setup/locale/ru/LC_MESSAGES/django.mo index 6eafab0493..1be87c998b 100644 Binary files a/apps/project_setup/locale/ru/LC_MESSAGES/django.mo and b/apps/project_setup/locale/ru/LC_MESSAGES/django.mo differ diff --git a/apps/project_setup/locale/ru/LC_MESSAGES/django.po b/apps/project_setup/locale/ru/LC_MESSAGES/django.po index e6069f5616..f0c539bbd6 100644 --- a/apps/project_setup/locale/ru/LC_MESSAGES/django.po +++ b/apps/project_setup/locale/ru/LC_MESSAGES/django.po @@ -8,9 +8,9 @@ msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" "PO-Revision-Date: 2011-11-19 20:30+0000\n" -"Last-Translator: gsv70 \n" +"Last-Translator: Sergey Glita \n" "Language-Team: Russian (http://www.transifex.net/projects/p/mayan-edms/team/" "ru/)\n" "Language: ru\n" @@ -24,6 +24,6 @@ msgstr "" msgid "setup" msgstr "настройки" -#: views.py:13 +#: views.py:15 msgid "setup items" msgstr "настроек" diff --git a/apps/project_setup/tests.py b/apps/project_setup/tests.py deleted file mode 100644 index 2247054b35..0000000000 --- a/apps/project_setup/tests.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -This file demonstrates two different styles of tests (one doctest and one -unittest). These will both pass when you run "manage.py test". - -Replace these 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.failUnlessEqual(1 + 1, 2) - -__test__ = {"doctest": """ -Another way to test that 1 + 1 is equal to 2. - ->>> 1 + 1 == 2 -True -"""} - diff --git a/apps/project_setup/views.py b/apps/project_setup/views.py index 82f45ac40a..8ab0426572 100644 --- a/apps/project_setup/views.py +++ b/apps/project_setup/views.py @@ -1,10 +1,12 @@ +from __future__ import absolute_import + from django.shortcuts import render_to_response, get_object_or_404 from django.template import RequestContext from django.utils.translation import ugettext_lazy as _ from navigation.widgets import button_navigation_widget -from project_setup.api import setup_items +from .api import setup_items def setup_list(request): diff --git a/apps/project_tools/__init__.py b/apps/project_tools/__init__.py index 176afc9fd6..3fc34151c1 100644 --- a/apps/project_tools/__init__.py +++ b/apps/project_tools/__init__.py @@ -2,6 +2,4 @@ from django.utils.translation import ugettext_lazy as _ from navigation.api import register_top_menu - -#TODO: FIXME dynamic children_path_regext on api register_setup -register_top_menu('tools', link={'text': _(u'tools'), 'view': 'tools_list', 'famfam': 'wrench'}, children_views=['statistics', 'history_list', 'formats_list'], position=-3) +tool_link = register_top_menu('tools', link={'text': _(u'tools'), 'view': 'tools_list', 'famfam': 'wrench'}, position=-3) diff --git a/apps/project_tools/api.py b/apps/project_tools/api.py index 873a901af4..d8e66ab7d0 100644 --- a/apps/project_tools/api.py +++ b/apps/project_tools/api.py @@ -1,5 +1,12 @@ -tool_items = [] +from __future__ import absolute_import +from . import tool_link + +tool_items = [] def register_tool(link): tool_items.append(link) + + # Append the link's children_view_regex to the tool main menu children view regex + tool_link.setdefault('children_view_regex', []) + tool_link['children_view_regex'].extend(link.get('children_view_regex', [])) diff --git a/apps/project_tools/locale/en/LC_MESSAGES/django.po b/apps/project_tools/locale/en/LC_MESSAGES/django.po index 255cb9e18c..7ac152f443 100644 --- a/apps/project_tools/locale/en/LC_MESSAGES/django.po +++ b/apps/project_tools/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,6 +17,6 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: __init__.py:7 views.py:13 +#: __init__.py:7 views.py:15 msgid "tools" msgstr "" diff --git a/apps/project_tools/locale/es/LC_MESSAGES/django.mo b/apps/project_tools/locale/es/LC_MESSAGES/django.mo index 9416fb42f7..7387ce96d9 100644 Binary files a/apps/project_tools/locale/es/LC_MESSAGES/django.mo and b/apps/project_tools/locale/es/LC_MESSAGES/django.mo differ diff --git a/apps/project_tools/locale/es/LC_MESSAGES/django.po b/apps/project_tools/locale/es/LC_MESSAGES/django.po index 9db820eb03..714f2f9f66 100644 --- a/apps/project_tools/locale/es/LC_MESSAGES/django.po +++ b/apps/project_tools/locale/es/LC_MESSAGES/django.po @@ -8,9 +8,9 @@ msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" "PO-Revision-Date: 2011-09-30 05:22+0000\n" -"Last-Translator: rosarior \n" +"Last-Translator: Roberto Rosario \n" "Language-Team: Spanish (Castilian) (http://www.transifex.net/projects/p/" "mayan-edms/team/es/)\n" "Language: es\n" @@ -19,6 +19,6 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -#: __init__.py:7 views.py:13 +#: __init__.py:7 views.py:15 msgid "tools" msgstr "herramientas" diff --git a/apps/project_tools/locale/it/LC_MESSAGES/django.mo b/apps/project_tools/locale/it/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..de891e390b Binary files /dev/null and b/apps/project_tools/locale/it/LC_MESSAGES/django.mo differ diff --git a/apps/project_tools/locale/it/LC_MESSAGES/django.po b/apps/project_tools/locale/it/LC_MESSAGES/django.po new file mode 100644 index 0000000000..ca3f7d9be2 --- /dev/null +++ b/apps/project_tools/locale/it/LC_MESSAGES/django.po @@ -0,0 +1,24 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# , 2011. +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2011-12-09 18:01+0000\n" +"Last-Translator: Pierpaolo Baldan \n" +"Language-Team: Italian (http://www.transifex.net/projects/p/mayan-edms/team/" +"it/)\n" +"Language: it\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:7 views.py:15 +msgid "tools" +msgstr "strumenti" diff --git a/apps/project_tools/locale/pl/LC_MESSAGES/django.mo b/apps/project_tools/locale/pl/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..9c7f96b33b Binary files /dev/null and b/apps/project_tools/locale/pl/LC_MESSAGES/django.mo differ diff --git a/apps/project_tools/locale/pl/LC_MESSAGES/django.po b/apps/project_tools/locale/pl/LC_MESSAGES/django.po new file mode 100644 index 0000000000..d6ff720ee6 --- /dev/null +++ b/apps/project_tools/locale/pl/LC_MESSAGES/django.po @@ -0,0 +1,23 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# , 2012. +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-20 16:08+0000\n" +"Last-Translator: mic \n" +"Language-Team: Polish (http://www.transifex.net/projects/p/mayan-edms/language/pl/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pl\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" + +#: __init__.py:7 views.py:15 +msgid "tools" +msgstr "narzędzia" diff --git a/apps/project_tools/locale/pt/LC_MESSAGES/django.mo b/apps/project_tools/locale/pt/LC_MESSAGES/django.mo index 424ab9c3ee..b11a377f8d 100644 Binary files a/apps/project_tools/locale/pt/LC_MESSAGES/django.mo and b/apps/project_tools/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/project_tools/locale/pt/LC_MESSAGES/django.po b/apps/project_tools/locale/pt/LC_MESSAGES/django.po index da0f490f89..d56141c08f 100644 --- a/apps/project_tools/locale/pt/LC_MESSAGES/django.po +++ b/apps/project_tools/locale/pt/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" "PO-Revision-Date: 2011-11-02 01:25+0000\n" "Last-Translator: emersonsoares \n" "Language-Team: Portuguese (http://www.transifex.net/projects/p/mayan-edms/" @@ -19,6 +19,6 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -#: __init__.py:7 views.py:13 +#: __init__.py:7 views.py:15 msgid "tools" msgstr "ferramentas" diff --git a/apps/project_tools/locale/ru/LC_MESSAGES/django.mo b/apps/project_tools/locale/ru/LC_MESSAGES/django.mo index 99daa2c0af..cedcf43ffc 100644 Binary files a/apps/project_tools/locale/ru/LC_MESSAGES/django.mo and b/apps/project_tools/locale/ru/LC_MESSAGES/django.mo differ diff --git a/apps/project_tools/locale/ru/LC_MESSAGES/django.po b/apps/project_tools/locale/ru/LC_MESSAGES/django.po index b4158a9108..6cc3064eb2 100644 --- a/apps/project_tools/locale/ru/LC_MESSAGES/django.po +++ b/apps/project_tools/locale/ru/LC_MESSAGES/django.po @@ -1,24 +1,25 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. # -#, fuzzy +# Translators: +# Sergey Glita , 2011. msgid "" msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" +"Project-Id-Version: Mayan EDMS\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2011-11-03 17:24+0000\n" +"Last-Translator: Sergey Glita \n" +"Language-Team: Russian (http://www.transifex.net/projects/p/mayan-edms/team/" +"ru/)\n" +"Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" -#: __init__.py:7 views.py:13 +#: __init__.py:7 views.py:15 msgid "tools" -msgstr "" +msgstr "инструменты" diff --git a/apps/project_tools/tests.py b/apps/project_tools/tests.py deleted file mode 100644 index 2247054b35..0000000000 --- a/apps/project_tools/tests.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -This file demonstrates two different styles of tests (one doctest and one -unittest). These will both pass when you run "manage.py test". - -Replace these 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.failUnlessEqual(1 + 1, 2) - -__test__ = {"doctest": """ -Another way to test that 1 + 1 is equal to 2. - ->>> 1 + 1 == 2 -True -"""} - diff --git a/apps/project_tools/views.py b/apps/project_tools/views.py index 8bba4fc024..61693a100c 100644 --- a/apps/project_tools/views.py +++ b/apps/project_tools/views.py @@ -1,10 +1,12 @@ +from __future__ import absolute_import + from django.shortcuts import render_to_response, get_object_or_404 from django.template import RequestContext from django.utils.translation import ugettext_lazy as _ from navigation.widgets import button_navigation_widget -from project_tools.api import tool_items +from .api import tool_items def tools_list(request): diff --git a/apps/rest_api/urls.py b/apps/rest_api/urls.py index 34553951a6..315bd5f1bc 100644 --- a/apps/rest_api/urls.py +++ b/apps/rest_api/urls.py @@ -1,10 +1,12 @@ +from __future__ import absolute_import + from django.conf.urls.defaults import patterns, url from djangorestframework.views import ListModelView from djangorestframework.views import ListOrCreateModelView, InstanceModelView -from rest_api.views import APIBase, Version_0, ReadOnlyInstanceModelView, IsZoomable -from rest_api.resources import DocumentResourceSimple +from .views import APIBase, Version_0, ReadOnlyInstanceModelView, IsZoomable +from .resources import DocumentResourceSimple urlpatterns = patterns('', url(r'^$', APIBase.as_view(), name='api-root'), diff --git a/apps/scheduler/__init__.py b/apps/scheduler/__init__.py index 8b13789179..80c406fd3b 100644 --- a/apps/scheduler/__init__.py +++ b/apps/scheduler/__init__.py @@ -1 +1,32 @@ +from __future__ import absolute_import +import logging + +from .runtime import scheduler + +from django.db.models.signals import post_syncdb +from django.dispatch import receiver + +from south.signals import pre_migrate + +from signaler.signals import pre_collectstatic + +logger = logging.getLogger(__name__) + + +@receiver(post_syncdb, dispatch_uid='scheduler_shutdown_post_syncdb') +def scheduler_shutdown_post_syncdb(sender, **kwargs): + logger.debug('Scheduler shut down on post syncdb signal') + scheduler.shutdown() + + +@receiver(pre_collectstatic, dispatch_uid='sheduler_shutdown_pre_collectstatic') +def sheduler_shutdown_pre_collectstatic(sender, **kwargs): + logger.debug('Scheduler shut down on collectstatic signal') + scheduler.shutdown() + + +@receiver(pre_migrate, dispatch_uid='sheduler_shutdown_pre_migrate') +def sheduler_shutdown_pre_migrate(sender, **kwargs): + logger.debug('Scheduler shut down on pre_migrate signal') + scheduler.shutdown() diff --git a/apps/scheduler/api.py b/apps/scheduler/api.py index 5f79d2433b..148320c3ab 100644 --- a/apps/scheduler/api.py +++ b/apps/scheduler/api.py @@ -1,5 +1,7 @@ -from scheduler.runtime import scheduler -from scheduler.exceptions import AlreadyScheduled +from __future__ import absolute_import + +from .runtime import scheduler +from .exceptions import AlreadyScheduled registered_jobs = {} diff --git a/apps/scheduler/tests.py b/apps/scheduler/tests.py deleted file mode 100644 index 2247054b35..0000000000 --- a/apps/scheduler/tests.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -This file demonstrates two different styles of tests (one doctest and one -unittest). These will both pass when you run "manage.py test". - -Replace these 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.failUnlessEqual(1 + 1, 2) - -__test__ = {"doctest": """ -Another way to test that 1 + 1 is equal to 2. - ->>> 1 + 1 == 2 -True -"""} - diff --git a/apps/rest_api/tests.py b/apps/signaler/__init__.py similarity index 100% rename from apps/rest_api/tests.py rename to apps/signaler/__init__.py diff --git a/apps/signaler/management/__init__.py b/apps/signaler/management/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/signaler/management/commands/__init__.py b/apps/signaler/management/commands/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/signaler/management/commands/collectstatic.py b/apps/signaler/management/commands/collectstatic.py new file mode 100644 index 0000000000..28b63cd9f2 --- /dev/null +++ b/apps/signaler/management/commands/collectstatic.py @@ -0,0 +1,13 @@ +from django.contrib.staticfiles.management.commands import collectstatic + +from signaler.signals import pre_collectstatic + + +class Command(collectstatic.Command): + """ + Wrapper for the collectstatic command + """ + + def handle_noargs(self, *args, **kwargs): + pre_collectstatic.send(sender=self) + super(Command, self).handle_noargs(*args, **kwargs) diff --git a/apps/signaler/models.py b/apps/signaler/models.py new file mode 100644 index 0000000000..71a8362390 --- /dev/null +++ b/apps/signaler/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/apps/signaler/signals.py b/apps/signaler/signals.py new file mode 100644 index 0000000000..c102b00357 --- /dev/null +++ b/apps/signaler/signals.py @@ -0,0 +1,3 @@ +from django.dispatch import Signal + +pre_collectstatic = Signal() diff --git a/apps/smart_settings/__init__.py b/apps/smart_settings/__init__.py index 5926e68754..bfc495e54d 100644 --- a/apps/smart_settings/__init__.py +++ b/apps/smart_settings/__init__.py @@ -2,9 +2,10 @@ from django.utils.translation import ugettext_lazy as _ from project_setup.api import register_setup + def is_superuser(context): return context['request'].user.is_staff or context['request'].user.is_superuser -check_settings = {'text': _(u'settings'), 'view': 'setting_list', 'famfam': 'cog', 'icon': 'cog.png', 'condition': is_superuser} +check_settings = {'text': _(u'settings'), 'view': 'setting_list', 'famfam': 'cog', 'icon': 'cog.png', 'condition': is_superuser, 'children_view_regex': [r'^setting_']} register_setup(check_settings) diff --git a/apps/smart_settings/locale/en/LC_MESSAGES/django.po b/apps/smart_settings/locale/en/LC_MESSAGES/django.po index d0e511720a..3e123c6230 100644 --- a/apps/smart_settings/locale/en/LC_MESSAGES/django.po +++ b/apps/smart_settings/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,18 +17,18 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: __init__.py:8 views.py:26 +#: __init__.py:9 views.py:28 msgid "settings" msgstr "" -#: views.py:31 +#: views.py:33 msgid "name" msgstr "" -#: views.py:32 +#: views.py:34 msgid "default" msgstr "" -#: views.py:33 +#: views.py:35 msgid "value" msgstr "" diff --git a/apps/smart_settings/locale/es/LC_MESSAGES/django.mo b/apps/smart_settings/locale/es/LC_MESSAGES/django.mo index 71eba7fd8b..23f3eb628b 100644 Binary files a/apps/smart_settings/locale/es/LC_MESSAGES/django.mo and b/apps/smart_settings/locale/es/LC_MESSAGES/django.mo differ diff --git a/apps/smart_settings/locale/es/LC_MESSAGES/django.po b/apps/smart_settings/locale/es/LC_MESSAGES/django.po index 2188ccd324..70fb0f67bb 100644 --- a/apps/smart_settings/locale/es/LC_MESSAGES/django.po +++ b/apps/smart_settings/locale/es/LC_MESSAGES/django.po @@ -7,9 +7,9 @@ msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" "PO-Revision-Date: 2011-09-30 05:09+0000\n" -"Last-Translator: rosarior \n" +"Last-Translator: Roberto Rosario \n" "Language-Team: Spanish (Castilian) (http://www.transifex.net/projects/p/" "mayan-edms/team/es/)\n" "Language: es\n" @@ -18,18 +18,18 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -#: __init__.py:8 views.py:26 +#: __init__.py:9 views.py:28 msgid "settings" msgstr "configuración" -#: views.py:31 +#: views.py:33 msgid "name" msgstr "nombre" -#: views.py:32 +#: views.py:34 msgid "default" msgstr "por defecto" -#: views.py:33 +#: views.py:35 msgid "value" msgstr "valor" diff --git a/apps/smart_settings/locale/it/LC_MESSAGES/django.mo b/apps/smart_settings/locale/it/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..d36c2eacdc Binary files /dev/null and b/apps/smart_settings/locale/it/LC_MESSAGES/django.mo differ diff --git a/apps/smart_settings/locale/it/LC_MESSAGES/django.po b/apps/smart_settings/locale/it/LC_MESSAGES/django.po new file mode 100644 index 0000000000..ddd908c065 --- /dev/null +++ b/apps/smart_settings/locale/it/LC_MESSAGES/django.po @@ -0,0 +1,36 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# , 2011. +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2011-12-09 17:38+0000\n" +"Last-Translator: Pierpaolo Baldan \n" +"Language-Team: Italian (http://www.transifex.net/projects/p/mayan-edms/team/" +"it/)\n" +"Language: it\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:9 views.py:28 +msgid "settings" +msgstr "configurazione" + +#: views.py:33 +msgid "name" +msgstr "nome" + +#: views.py:34 +msgid "default" +msgstr "default" + +#: views.py:35 +msgid "value" +msgstr "valore" diff --git a/apps/smart_settings/locale/pl/LC_MESSAGES/django.mo b/apps/smart_settings/locale/pl/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..2ce2fce94d Binary files /dev/null and b/apps/smart_settings/locale/pl/LC_MESSAGES/django.mo differ diff --git a/apps/smart_settings/locale/pl/LC_MESSAGES/django.po b/apps/smart_settings/locale/pl/LC_MESSAGES/django.po new file mode 100644 index 0000000000..d07a0fe587 --- /dev/null +++ b/apps/smart_settings/locale/pl/LC_MESSAGES/django.po @@ -0,0 +1,37 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# mic , 2012. +# , 2012. +# , 2012. +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-20 21:27+0000\n" +"Last-Translator: mic \n" +"Language-Team: Polish (http://www.transifex.net/projects/p/mayan-edms/language/pl/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pl\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" + +#: __init__.py:9 views.py:28 +msgid "settings" +msgstr "ustawienia" + +#: views.py:33 +msgid "name" +msgstr "nazwa" + +#: views.py:34 +msgid "default" +msgstr "domyślny" + +#: views.py:35 +msgid "value" +msgstr "wartość" diff --git a/apps/smart_settings/locale/pt/LC_MESSAGES/django.mo b/apps/smart_settings/locale/pt/LC_MESSAGES/django.mo index 868ac5ecd2..bd58bb60a2 100644 Binary files a/apps/smart_settings/locale/pt/LC_MESSAGES/django.mo and b/apps/smart_settings/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/smart_settings/locale/pt/LC_MESSAGES/django.po b/apps/smart_settings/locale/pt/LC_MESSAGES/django.po index 9e0ed5e9ea..175a5f859c 100644 --- a/apps/smart_settings/locale/pt/LC_MESSAGES/django.po +++ b/apps/smart_settings/locale/pt/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" "PO-Revision-Date: 2011-11-02 01:22+0000\n" "Last-Translator: emersonsoares \n" "Language-Team: Portuguese (http://www.transifex.net/projects/p/mayan-edms/" @@ -19,18 +19,18 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -#: __init__.py:8 views.py:26 +#: __init__.py:9 views.py:28 msgid "settings" msgstr "configurações" -#: views.py:31 +#: views.py:33 msgid "name" msgstr "nome" -#: views.py:32 +#: views.py:34 msgid "default" msgstr "padrão" -#: views.py:33 +#: views.py:35 msgid "value" msgstr "valor" diff --git a/apps/smart_settings/locale/ru/LC_MESSAGES/django.mo b/apps/smart_settings/locale/ru/LC_MESSAGES/django.mo index d8be586181..e4856bc7ca 100644 Binary files a/apps/smart_settings/locale/ru/LC_MESSAGES/django.mo and b/apps/smart_settings/locale/ru/LC_MESSAGES/django.mo differ diff --git a/apps/smart_settings/locale/ru/LC_MESSAGES/django.po b/apps/smart_settings/locale/ru/LC_MESSAGES/django.po index be3705264a..3c14af4fdd 100644 --- a/apps/smart_settings/locale/ru/LC_MESSAGES/django.po +++ b/apps/smart_settings/locale/ru/LC_MESSAGES/django.po @@ -8,9 +8,9 @@ msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" "PO-Revision-Date: 2011-11-03 17:15+0000\n" -"Last-Translator: gsv70 \n" +"Last-Translator: Sergey Glita \n" "Language-Team: Russian (http://www.transifex.net/projects/p/mayan-edms/team/" "ru/)\n" "Language: ru\n" @@ -20,18 +20,18 @@ msgstr "" "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:8 views.py:26 +#: __init__.py:9 views.py:28 msgid "settings" msgstr "настройки" -#: views.py:31 +#: views.py:33 msgid "name" msgstr "имя" -#: views.py:32 +#: views.py:34 msgid "default" msgstr "по умолчанию" -#: views.py:33 +#: views.py:35 msgid "value" msgstr "значение" diff --git a/apps/smart_settings/tests.py b/apps/smart_settings/tests.py deleted file mode 100644 index 2247054b35..0000000000 --- a/apps/smart_settings/tests.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -This file demonstrates two different styles of tests (one doctest and one -unittest). These will both pass when you run "manage.py test". - -Replace these 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.failUnlessEqual(1 + 1, 2) - -__test__ = {"doctest": """ -Another way to test that 1 + 1 is equal to 2. - ->>> 1 + 1 == 2 -True -"""} - diff --git a/apps/smart_settings/views.py b/apps/smart_settings/views.py index e4d1b4d75a..a08872d053 100644 --- a/apps/smart_settings/views.py +++ b/apps/smart_settings/views.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import + from django.shortcuts import render_to_response from django.template import RequestContext from django.utils.translation import ugettext_lazy as _ @@ -6,7 +8,7 @@ from django.utils.safestring import mark_safe from common.utils import return_type, encapsulate from common.widgets import exists_with_famfam -from smart_settings.api import settings +from .api import settings def setting_list(request): @@ -28,7 +30,7 @@ def setting_list(request): 'hide_link': True, 'hide_object': True, 'extra_columns': [ - {'name': _(u'name'), 'attribute': encapsulate(lambda x: mark_safe(u'%s
    %s' % (x.get('global_name'), x.get('description'))))}, + {'name': _(u'name'), 'attribute': encapsulate(lambda x: mark_safe(u'%s
    %s' % (x.get('global_name'), x.get('description'))))}, {'name': _(u'default'), 'attribute': encapsulate(lambda x: return_type(x['default']))}, {'name': _(u'value'), 'attribute': encapsulate(lambda x: mark_safe(u'
    %s %s
    ' % ( return_type(getattr(x['module'], x['name'])), diff --git a/apps/sources/__init__.py b/apps/sources/__init__.py index faca4f075c..d9612a6f9d 100644 --- a/apps/sources/__init__.py +++ b/apps/sources/__init__.py @@ -1,49 +1,41 @@ +from __future__ import absolute_import + from django.utils.translation import ugettext_lazy as _ -from navigation.api import register_links, \ - register_model_list_columns -from permissions.api import register_permission, set_namespace_title +from navigation.api import (register_links, + register_model_list_columns) from common.utils import encapsulate from project_setup.api import register_setup -from documents.models import Document -from documents.literals import PERMISSION_DOCUMENT_CREATE +from documents.permissions import PERMISSION_DOCUMENT_NEW_VERSION -from sources.staging import StagingFile -from sources.models import WebForm, StagingFolder, SourceTransformation, \ - WatchFolder -from sources.widgets import staging_file_thumbnail - -PERMISSION_SOURCES_SETUP_VIEW = {'namespace': 'sources_setup', 'name': 'sources_setup_view', 'label': _(u'View existing document sources')} -PERMISSION_SOURCES_SETUP_EDIT = {'namespace': 'sources_setup', 'name': 'sources_setup_edit', 'label': _(u'Edit document sources')} -PERMISSION_SOURCES_SETUP_DELETE = {'namespace': 'sources_setup', 'name': 'sources_setup_delete', 'label': _(u'Delete document sources')} -PERMISSION_SOURCES_SETUP_CREATE = {'namespace': 'sources_setup', 'name': 'sources_setup_create', 'label': _(u'Create new document sources')} - -set_namespace_title('sources_setup', _(u'Sources setup')) -register_permission(PERMISSION_SOURCES_SETUP_VIEW) -register_permission(PERMISSION_SOURCES_SETUP_EDIT) -register_permission(PERMISSION_SOURCES_SETUP_DELETE) -register_permission(PERMISSION_SOURCES_SETUP_CREATE) +from .staging import StagingFile +from .models import (WebForm, StagingFolder, SourceTransformation, + WatchFolder) +from .widgets import staging_file_thumbnail +from .permissions import (PERMISSION_SOURCES_SETUP_VIEW, + PERMISSION_SOURCES_SETUP_EDIT, PERMISSION_SOURCES_SETUP_DELETE, + PERMISSION_SOURCES_SETUP_CREATE) staging_file_preview = {'text': _(u'preview'), 'class': 'fancybox-noscaling', 'view': 'staging_file_preview', 'args': ['source.source_type', 'source.pk', 'object.id'], 'famfam': 'zoom'} staging_file_delete = {'text': _(u'delete'), 'view': 'staging_file_delete', 'args': ['source.source_type', 'source.pk', 'object.id'], 'famfam': 'delete', 'keep_query': True} -setup_sources = {'text': _(u'sources'), 'view': 'setup_web_form_list', 'famfam': 'application_form', 'icon': 'application_form.png', 'children_classes': [WebForm]} -setup_web_form_list = {'text': _(u'web forms'), 'view': 'setup_web_form_list', 'famfam': 'application_form', 'icon': 'application_form.png', 'children_classes': [WebForm]} -setup_staging_folder_list = {'text': _(u'staging folders'), 'view': 'setup_staging_folder_list', 'famfam': 'folder_camera', 'children_classes': [StagingFolder]} -setup_watch_folder_list = {'text': _(u'watch folders'), 'view': 'setup_watch_folder_list', 'famfam': 'folder_magnify', 'children_classes': [WatchFolder]} +setup_sources = {'text': _(u'sources'), 'view': 'setup_web_form_list', 'famfam': 'application_form', 'icon': 'application_form.png', 'children_classes': [WebForm], 'permissions': [PERMISSION_SOURCES_SETUP_VIEW], 'children_view_regex': [r'setup_web_form', r'setup_staging_folder', r'setup_source_']} +setup_web_form_list = {'text': _(u'web forms'), 'view': 'setup_web_form_list', 'famfam': 'application_form', 'icon': 'application_form.png', 'children_classes': [WebForm], 'permissions': [PERMISSION_SOURCES_SETUP_VIEW]} +setup_staging_folder_list = {'text': _(u'staging folders'), 'view': 'setup_staging_folder_list', 'famfam': 'folder_camera', 'children_classes': [StagingFolder], 'permissions': [PERMISSION_SOURCES_SETUP_VIEW]} +setup_watch_folder_list = {'text': _(u'watch folders'), 'view': 'setup_watch_folder_list', 'famfam': 'folder_magnify', 'children_classes': [WatchFolder], 'permissions': [PERMISSION_SOURCES_SETUP_VIEW]} -setup_source_edit = {'text': _(u'edit'), 'view': 'setup_source_edit', 'args': ['source.source_type', 'source.pk'], 'famfam': 'application_form_edit'} -setup_source_delete = {'text': _(u'delete'), 'view': 'setup_source_delete', 'args': ['source.source_type', 'source.pk'], 'famfam': 'application_form_delete'} -setup_source_create = {'text': _(u'add new source'), 'view': 'setup_source_create', 'args': 'source_type', 'famfam': 'application_form_add'} +setup_source_edit = {'text': _(u'edit'), 'view': 'setup_source_edit', 'args': ['source.source_type', 'source.pk'], 'famfam': 'application_form_edit', 'permissions': [PERMISSION_SOURCES_SETUP_EDIT]} +setup_source_delete = {'text': _(u'delete'), 'view': 'setup_source_delete', 'args': ['source.source_type', 'source.pk'], 'famfam': 'application_form_delete', 'permissions': [PERMISSION_SOURCES_SETUP_DELETE]} +setup_source_create = {'text': _(u'add new source'), 'view': 'setup_source_create', 'args': 'source_type', 'famfam': 'application_form_add', 'permissions': [PERMISSION_SOURCES_SETUP_CREATE]} -setup_source_transformation_list = {'text': _(u'transformations'), 'view': 'setup_source_transformation_list', 'args': ['source.source_type', 'source.pk'], 'famfam': 'shape_move_front'} -setup_source_transformation_create = {'text': _(u'add transformation'), 'view': 'setup_source_transformation_create', 'args': ['source.source_type', 'source.pk'], 'famfam': 'shape_square_add'} -setup_source_transformation_edit = {'text': _(u'edit'), 'view': 'setup_source_transformation_edit', 'args': 'transformation.pk', 'famfam': 'shape_square_edit'} -setup_source_transformation_delete = {'text': _(u'delete'), 'view': 'setup_source_transformation_delete', 'args': 'transformation.pk', 'famfam': 'shape_square_delete'} +setup_source_transformation_list = {'text': _(u'transformations'), 'view': 'setup_source_transformation_list', 'args': ['source.source_type', 'source.pk'], 'famfam': 'shape_move_front', 'permissions': [PERMISSION_SOURCES_SETUP_EDIT]} +setup_source_transformation_create = {'text': _(u'add transformation'), 'view': 'setup_source_transformation_create', 'args': ['source.source_type', 'source.pk'], 'famfam': 'shape_square_add', 'permissions': [PERMISSION_SOURCES_SETUP_EDIT]} +setup_source_transformation_edit = {'text': _(u'edit'), 'view': 'setup_source_transformation_edit', 'args': 'transformation.pk', 'famfam': 'shape_square_edit', 'permissions': [PERMISSION_SOURCES_SETUP_EDIT]} +setup_source_transformation_delete = {'text': _(u'delete'), 'view': 'setup_source_transformation_delete', 'args': 'transformation.pk', 'famfam': 'shape_square_delete', 'permissions': [PERMISSION_SOURCES_SETUP_EDIT]} -source_list = {'text': _(u'Document sources'), 'view': 'setup_web_form_list', 'famfam': 'page_add', 'children_url_regex': [r'sources/setup']} +source_list = {'text': _(u'Document sources'), 'view': 'setup_web_form_list', 'famfam': 'page_add', 'children_url_regex': [r'sources/setup'], 'permissions': [PERMISSION_SOURCES_SETUP_VIEW]} -upload_version = {'text': _(u'upload new version'), 'view': 'upload_version', 'args': 'object.pk', 'famfam': 'page_add', 'permissions': [PERMISSION_DOCUMENT_CREATE]} +upload_version = {'text': _(u'upload new version'), 'view': 'upload_version', 'args': 'object.pk', 'famfam': 'page_add', 'permissions': [PERMISSION_DOCUMENT_NEW_VERSION]} register_links(StagingFile, [staging_file_delete]) @@ -78,5 +70,4 @@ register_model_list_columns(StagingFile, [ }, ]) - register_setup(setup_sources) diff --git a/apps/sources/admin.py b/apps/sources/admin.py index ac03e7c9f8..987ce6d89b 100644 --- a/apps/sources/admin.py +++ b/apps/sources/admin.py @@ -1,6 +1,8 @@ +from __future__ import absolute_import + from django.contrib import admin -from sources.models import StagingFolder, WebForm, SourceTransformation +from .models import StagingFolder, WebForm, SourceTransformation admin.site.register(StagingFolder) admin.site.register(WebForm) diff --git a/apps/sources/forms.py b/apps/sources/forms.py index 4b046994cf..714343530b 100644 --- a/apps/sources/forms.py +++ b/apps/sources/forms.py @@ -1,13 +1,15 @@ +from __future__ import absolute_import + from django import forms from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext from documents.forms import DocumentForm -from sources.models import WebForm, StagingFolder, SourceTransformation, \ - WatchFolder -from sources.widgets import FamFamRadioSelect -from sources.utils import validate_whitelist_blacklist +from .models import (WebForm, StagingFolder, SourceTransformation, + WatchFolder) +from .widgets import FamFamRadioSelect +from .utils import validate_whitelist_blacklist class StagingDocumentForm(DocumentForm): @@ -37,7 +39,7 @@ class StagingDocumentForm(DocumentForm): staging_list_index = self.fields.keyOrder.index('staging_file_id') staging_list = self.fields.keyOrder.pop(staging_list_index) self.fields.keyOrder.insert(0, staging_list) - + staging_file_id = forms.ChoiceField(label=_(u'Staging file')) class Meta(DocumentForm.Meta): @@ -61,7 +63,7 @@ class WebFormForm(DocumentForm): # Move the file filed to the top self.fields.keyOrder.remove('file') self.fields.keyOrder.insert(0, 'file') - + def clean_file(self): data = self.cleaned_data['file'] validate_whitelist_blacklist(data.name, self.source.whitelist.split(','), self.source.blacklist.split(',')) diff --git a/apps/sources/locale/en/LC_MESSAGES/django.po b/apps/sources/locale/en/LC_MESSAGES/django.po index 609adf9151..9e37468a16 100644 --- a/apps/sources/locale/en/LC_MESSAGES/django.po +++ b/apps/sources/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,86 +17,74 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: __init__.py:14 -msgid "View existing document sources" -msgstr "" - -#: __init__.py:15 -msgid "Edit document sources" -msgstr "" - -#: __init__.py:16 -msgid "Delete document sources" -msgstr "" - -#: __init__.py:17 -msgid "Create new document sources" -msgstr "" - #: __init__.py:19 -msgid "Sources setup" -msgstr "" - -#: __init__.py:25 msgid "preview" msgstr "" -#: __init__.py:26 __init__.py:34 __init__.py:40 +#: __init__.py:20 __init__.py:28 __init__.py:34 msgid "delete" msgstr "" -#: __init__.py:28 +#: __init__.py:22 msgid "sources" msgstr "" -#: __init__.py:29 literals.py:53 models.py:159 +#: __init__.py:23 literals.py:53 models.py:203 msgid "web forms" msgstr "" -#: __init__.py:30 models.py:130 +#: __init__.py:24 models.py:174 msgid "staging folders" msgstr "" -#: __init__.py:31 models.py:194 +#: __init__.py:25 models.py:238 msgid "watch folders" msgstr "" -#: __init__.py:33 __init__.py:39 +#: __init__.py:27 __init__.py:33 msgid "edit" msgstr "" -#: __init__.py:35 +#: __init__.py:29 msgid "add new source" msgstr "" -#: __init__.py:37 +#: __init__.py:31 msgid "transformations" msgstr "" -#: __init__.py:38 +#: __init__.py:32 msgid "add transformation" msgstr "" -#: __init__.py:42 +#: __init__.py:36 msgid "Document sources" msgstr "" -#: __init__.py:69 widgets.py:39 +#: __init__.py:38 +msgid "upload new version" +msgstr "" + +#: __init__.py:68 widgets.py:39 msgid "thumbnail" msgstr "" -#: forms.py:32 forms.py:55 +#: forms.py:34 forms.py:59 msgid "Expand compressed files" msgstr "" -#: forms.py:33 forms.py:56 +#: forms.py:35 forms.py:60 msgid "Upload a compressed file's contained files as individual documents" msgstr "" -#: forms.py:41 +#: forms.py:43 msgid "Staging file" msgstr "" +#: forms.py:50 +msgid "File" +msgstr "" + #: literals.py:8 literals.py:13 msgid "Always" msgstr "" @@ -149,7 +137,7 @@ msgstr "" msgid "Empty printer" msgstr "" -#: literals.py:47 models.py:158 +#: literals.py:47 models.py:202 msgid "web form" msgstr "" @@ -169,127 +157,151 @@ msgstr "" msgid "server watch folders" msgstr "" -#: models.py:29 +#: models.py:37 msgid "title" msgstr "" -#: models.py:30 +#: models.py:38 msgid "enabled" msgstr "" -#: models.py:31 +#: models.py:39 msgid "whitelist" msgstr "" -#: models.py:32 +#: models.py:40 msgid "blacklist" msgstr "" -#: models.py:98 +#: models.py:142 msgid "icon" msgstr "" -#: models.py:98 +#: models.py:142 msgid "An icon to visually distinguish this source." msgstr "" -#: models.py:114 models.py:166 +#: models.py:158 models.py:210 msgid "folder path" msgstr "" -#: models.py:114 models.py:166 +#: models.py:158 models.py:210 msgid "Server side filesystem path." msgstr "" -#: models.py:115 +#: models.py:159 msgid "preview width" msgstr "" -#: models.py:115 +#: models.py:159 msgid "Width value to be passed to the converter backend." msgstr "" -#: models.py:116 +#: models.py:160 msgid "preview height" msgstr "" -#: models.py:116 +#: models.py:160 msgid "Height value to be passed to the converter backend." msgstr "" -#: models.py:117 models.py:154 models.py:167 +#: models.py:161 models.py:198 models.py:211 msgid "uncompress" msgstr "" -#: models.py:117 models.py:154 models.py:167 +#: models.py:161 models.py:198 models.py:211 msgid "Whether to expand or not compressed archives." msgstr "" -#: models.py:118 models.py:168 +#: models.py:162 models.py:212 msgid "delete after upload" msgstr "" -#: models.py:118 models.py:168 +#: models.py:162 models.py:212 msgid "Delete the file after is has been successfully uploaded." msgstr "" -#: models.py:129 +#: models.py:173 msgid "staging folder" msgstr "" -#: models.py:169 +#: models.py:213 msgid "interval" msgstr "" -#: models.py:169 +#: models.py:213 msgid "" "Inverval in seconds where the watch folder path is checked for new documents." msgstr "" -#: models.py:193 +#: models.py:237 msgid "watch folder" msgstr "" -#: models.py:198 +#: models.py:242 msgid "Enter a valid value." msgstr "" -#: models.py:226 views.py:487 +#: models.py:270 views.py:589 msgid "order" msgstr "" -#: models.py:227 views.py:488 views.py:525 views.py:555 +#: models.py:271 views.py:590 views.py:627 views.py:657 msgid "transformation" msgstr "" -#: models.py:228 views.py:489 +#: models.py:272 views.py:591 msgid "arguments" msgstr "" -#: models.py:228 +#: models.py:272 #, python-format msgid "Use dictionaries to indentify arguments, example: %s" msgstr "" -#: models.py:239 +#: models.py:283 msgid "document source transformation" msgstr "" -#: models.py:240 +#: models.py:284 msgid "document source transformations" msgstr "" -#: staging.py:42 +#: models.py:290 models.py:291 +msgid "out of process" +msgstr "" + +#: permissions.py:7 +msgid "Sources setup" +msgstr "" + +#: permissions.py:8 +msgid "View existing document sources" +msgstr "" + +#: permissions.py:9 +msgid "Edit document sources" +msgstr "" + +#: permissions.py:10 +msgid "Delete document sources" +msgstr "" + +#: permissions.py:11 +msgid "Create new document sources" +msgstr "" + +#: staging.py:44 #, python-format msgid "Unable get list of staging files: %s" msgstr "" -#: staging.py:127 +#: staging.py:129 #, python-format msgid "Unable to upload staging file: %s" msgstr "" -#: staging.py:137 +#: staging.py:139 #, python-format msgid "Unable to delete staging file: %s" msgstr "" @@ -298,162 +310,213 @@ msgstr "" msgid "Whitelist Blacklist validation error." msgstr "" -#: views.py:80 +#: views.py:98 msgid "here" msgstr "" -#: views.py:85 +#: views.py:103 msgid "Upload sources" msgstr "" -#: views.py:87 +#: views.py:105 msgid "" "No interactive document sources have been defined or none have been enabled." msgstr "" -#: views.py:88 +#: views.py:106 #, python-format msgid "Click %(setup_link)s to add or enable some document sources." msgstr "" -#: views.py:136 -msgid "Document uploaded successfully." +#: views.py:163 +msgid "New document version uploaded successfully." msgstr "" -#: views.py:152 +#: views.py:167 +msgid "File uploaded successfully." +msgstr "" + +#: views.py:170 +msgid "File uncompressed successfully and uploaded as individual files." +msgstr "" + +#: views.py:173 +msgid "File was not a compressed file, uploaded as it was." +msgstr "" + +#: views.py:179 views.py:258 +#, python-format +msgid "Unhandled exception: %s" +msgstr "" + +#: views.py:188 +#, python-format +msgid "upload a new version from source: %s" +msgstr "" + +#: views.py:190 #, python-format msgid "upload a local document from source: %s" msgstr "" -#: views.py:182 +#: views.py:236 +#, python-format +msgid "Document version from staging file: %s, uploaded successfully." +msgstr "" + +#: views.py:239 #, python-format msgid "Staging file: %s, uploaded successfully." msgstr "" -#: views.py:187 +#: views.py:242 +#, python-format +msgid "" +"Staging file: %s, uncompressed successfully and uploaded as individual files." +msgstr "" + +#: views.py:245 +#, python-format +msgid "Staging file: %s, was not compressed, uploaded as a single file." +msgstr "" + +#: views.py:250 #, python-format msgid "Staging file: %s, deleted successfully." msgstr "" -#: views.py:209 +#: views.py:273 +#, python-format +msgid "upload a new version from staging source: %s" +msgstr "" + +#: views.py:275 #, python-format msgid "upload a document from staging source: %s" msgstr "" -#: views.py:215 +#: views.py:288 msgid "files in staging path" msgstr "" -#: views.py:229 +#: views.py:320 +msgid "Current document type" +msgstr "" + +#: views.py:321 +msgid "None" +msgstr "" + +#: views.py:328 msgid "Current metadata" msgstr "" -#: views.py:265 views.py:284 +#: views.py:366 views.py:385 #, python-format msgid "Staging file transformation error: %(error)s" msgstr "" -#: views.py:307 +#: views.py:408 msgid "Staging file delete successfully." msgstr "" -#: views.py:309 -#, python-format -msgid "Staging file delete error; %s." -msgstr "" - -#: views.py:368 -msgid "Source edited successfully" -msgstr "" - -#: views.py:371 -#, python-format -msgid "Error editing source; %s" -msgstr "" - -#: views.py:376 -#, python-format -msgid "edit source: %s" -msgstr "" - -#: views.py:381 views.py:421 views.py:483 views.py:524 views.py:554 -#: views.py:597 -msgid "source" -msgstr "" - #: views.py:410 #, python-format +msgid "Staging file delete error; %s." +msgstr "" + +#: views.py:470 +msgid "Source edited successfully" +msgstr "" + +#: views.py:473 +#, python-format +msgid "Error editing source; %s" +msgstr "" + +#: views.py:478 +#, python-format +msgid "edit source: %s" +msgstr "" + +#: views.py:483 views.py:523 views.py:585 views.py:626 views.py:656 +#: views.py:699 +msgid "source" +msgstr "" + +#: views.py:512 +#, python-format msgid "Source \"%s\" deleted successfully." msgstr "" -#: views.py:412 -#, python-format -msgid "Error deleting source \"%(source)s\": %(error)s" -msgstr "" - -#: views.py:419 -#, python-format -msgid "Are you sure you wish to delete the source: %s?" -msgstr "" - -#: views.py:451 -msgid "Source created successfully" -msgstr "" - -#: views.py:454 -#, python-format -msgid "Error creating source; %s" -msgstr "" - -#: views.py:459 -#, python-format -msgid "Create new source of type: %s" -msgstr "" - -#: views.py:481 -#, python-format -msgid "transformations for: %s" -msgstr "" - -#: views.py:511 -msgid "Source transformation edited successfully" -msgstr "" - #: views.py:514 #, python-format +msgid "Error deleting source \"%(source)s\": %(error)s" +msgstr "" + +#: views.py:521 +#, python-format +msgid "Are you sure you wish to delete the source: %s?" +msgstr "" + +#: views.py:553 +msgid "Source created successfully" +msgstr "" + +#: views.py:556 +#, python-format +msgid "Error creating source; %s" +msgstr "" + +#: views.py:561 +#, python-format +msgid "Create new source of type: %s" +msgstr "" + +#: views.py:583 +#, python-format +msgid "transformations for: %s" +msgstr "" + +#: views.py:613 +msgid "Source transformation edited successfully" +msgstr "" + +#: views.py:616 +#, python-format msgid "Error editing source transformation; %s" msgstr "" -#: views.py:519 +#: views.py:621 #, python-format msgid "Edit transformation: %s" msgstr "" -#: views.py:542 +#: views.py:644 msgid "Source transformation deleted successfully." msgstr "" -#: views.py:544 +#: views.py:646 #, python-format msgid "Error deleting source transformation; %(error)s" msgstr "" -#: views.py:557 +#: views.py:659 #, python-format msgid "" "Are you sure you wish to delete source transformation \"%(transformation)s\"" msgstr "" -#: views.py:587 +#: views.py:689 msgid "Source transformation created successfully" msgstr "" -#: views.py:590 +#: views.py:692 #, python-format msgid "Error creating source transformation; %s" msgstr "" -#: views.py:599 +#: views.py:701 #, python-format msgid "Create new transformation for source: %s" msgstr "" diff --git a/apps/sources/locale/es/LC_MESSAGES/django.mo b/apps/sources/locale/es/LC_MESSAGES/django.mo index 4395893c48..7cdd8eb998 100644 Binary files a/apps/sources/locale/es/LC_MESSAGES/django.mo and b/apps/sources/locale/es/LC_MESSAGES/django.mo differ diff --git a/apps/sources/locale/es/LC_MESSAGES/django.po b/apps/sources/locale/es/LC_MESSAGES/django.po index b71b65dc33..9074aeb8ad 100644 --- a/apps/sources/locale/es/LC_MESSAGES/django.po +++ b/apps/sources/locale/es/LC_MESSAGES/django.po @@ -3,14 +3,14 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: -# Roberto Rosario , 2011. +# Roberto Rosario , 2011, 2012. msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" "Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" -"PO-Revision-Date: 2011-11-22 21:02+0000\n" -"Last-Translator: rosarior \n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-12 19:28+0000\n" +"Last-Translator: Roberto Rosario \n" "Language-Team: Spanish (Castilian) (http://www.transifex.net/projects/p/mayan-edms/team/es/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -18,87 +18,75 @@ msgstr "" "Language: es\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -#: __init__.py:14 -msgid "View existing document sources" -msgstr "Ver fuentes de documento existentes" - -#: __init__.py:15 -msgid "Edit document sources" -msgstr "Editar fuentes de documentos" - -#: __init__.py:16 -msgid "Delete document sources" -msgstr "Eliminar fuentes de documentos" - -#: __init__.py:17 -msgid "Create new document sources" -msgstr "Crear nuevas fuentes de documentos" - #: __init__.py:19 -msgid "Sources setup" -msgstr "Configuración de fuentes de documentos" - -#: __init__.py:25 msgid "preview" msgstr "muestra" -#: __init__.py:26 __init__.py:34 __init__.py:40 +#: __init__.py:20 __init__.py:28 __init__.py:34 msgid "delete" msgstr "borrar" -#: __init__.py:28 +#: __init__.py:22 msgid "sources" msgstr "fuentes" -#: __init__.py:29 literals.py:53 models.py:159 +#: __init__.py:23 literals.py:53 models.py:203 msgid "web forms" msgstr "formularios web" -#: __init__.py:30 models.py:130 +#: __init__.py:24 models.py:174 msgid "staging folders" msgstr "carpetas de archivos provisionales" -#: __init__.py:31 models.py:194 +#: __init__.py:25 models.py:238 msgid "watch folders" msgstr "carpetas bajo observación" -#: __init__.py:33 __init__.py:39 +#: __init__.py:27 __init__.py:33 msgid "edit" msgstr "editar" -#: __init__.py:35 +#: __init__.py:29 msgid "add new source" msgstr "agregar nueva fuente" -#: __init__.py:37 +#: __init__.py:31 msgid "transformations" msgstr "transformaciones" -#: __init__.py:38 +#: __init__.py:32 msgid "add transformation" msgstr "añadir transformación" -#: __init__.py:42 +#: __init__.py:36 msgid "Document sources" msgstr "Fuentes de documentos" -#: __init__.py:69 widgets.py:39 +#: __init__.py:38 +msgid "upload new version" +msgstr "subir nueva versión" + +#: __init__.py:68 widgets.py:39 msgid "thumbnail" msgstr "muestra" -#: forms.py:32 forms.py:55 +#: forms.py:34 forms.py:59 msgid "Expand compressed files" msgstr "Expandir archivos comprimidos" -#: forms.py:33 forms.py:56 +#: forms.py:35 forms.py:60 msgid "Upload a compressed file's contained files as individual documents" msgstr "" "Subir los archivos de un archivo comprimido como documentos individuales" -#: forms.py:41 +#: forms.py:43 msgid "Staging file" msgstr "Archivo provisional" +#: forms.py:50 +msgid "File" +msgstr "Archivo" + #: literals.py:8 literals.py:13 msgid "Always" msgstr "Siempre" @@ -151,7 +139,7 @@ msgstr "Impresora" msgid "Empty printer" msgstr "Impresora vacia" -#: literals.py:47 models.py:158 +#: literals.py:47 models.py:202 msgid "web form" msgstr "formulario web" @@ -171,79 +159,79 @@ msgstr "carpetas de archivos provisionales de servidor" msgid "server watch folders" msgstr "carpetas observadas de servidor" -#: models.py:29 +#: models.py:37 msgid "title" msgstr "título" -#: models.py:30 +#: models.py:38 msgid "enabled" msgstr "activado" -#: models.py:31 +#: models.py:39 msgid "whitelist" msgstr "lista blanca" -#: models.py:32 +#: models.py:40 msgid "blacklist" msgstr "lista negra" -#: models.py:98 +#: models.py:142 msgid "icon" msgstr "icono" -#: models.py:98 +#: models.py:142 msgid "An icon to visually distinguish this source." msgstr "Un icono para distinguir visualmente esta fuente." -#: models.py:114 models.py:166 +#: models.py:158 models.py:210 msgid "folder path" msgstr "ruta de la carpeta" -#: models.py:114 models.py:166 +#: models.py:158 models.py:210 msgid "Server side filesystem path." msgstr "Camino a los archivos en el servidor." -#: models.py:115 +#: models.py:159 msgid "preview width" msgstr "ancho de muestra" -#: models.py:115 +#: models.py:159 msgid "Width value to be passed to the converter backend." msgstr "Valor de la anchura que se pasa al backend convertidor." -#: models.py:116 +#: models.py:160 msgid "preview height" msgstr "alto de muestra" -#: models.py:116 +#: models.py:160 msgid "Height value to be passed to the converter backend." msgstr "Valor de la altura que se pasa al backend convertidor." -#: models.py:117 models.py:154 models.py:167 +#: models.py:161 models.py:198 models.py:211 msgid "uncompress" msgstr "descomprimir" -#: models.py:117 models.py:154 models.py:167 +#: models.py:161 models.py:198 models.py:211 msgid "Whether to expand or not compressed archives." msgstr "La posibilidad de ampliar o no archivos comprimidos." -#: models.py:118 models.py:168 +#: models.py:162 models.py:212 msgid "delete after upload" msgstr "eliminar después de subir" -#: models.py:118 models.py:168 +#: models.py:162 models.py:212 msgid "Delete the file after is has been successfully uploaded." msgstr "Eliminar el archivo después de que se haya cargado exitosamente." -#: models.py:129 +#: models.py:173 msgid "staging folder" msgstr "carpeta de archivos provisionales" -#: models.py:169 +#: models.py:213 msgid "interval" msgstr "intervalo" -#: models.py:169 +#: models.py:213 msgid "" "Inverval in seconds where the watch folder path is checked for new " "documents." @@ -251,50 +239,74 @@ msgstr "" "Inverval es segundos, donde se comprueba la ruta de la carpeta para detectar" " nuevos documentos." -#: models.py:193 +#: models.py:237 msgid "watch folder" msgstr "carpeta observada" -#: models.py:198 +#: models.py:242 msgid "Enter a valid value." msgstr "Introduzca un valor válido." -#: models.py:226 views.py:487 +#: models.py:270 views.py:589 msgid "order" msgstr "orden" -#: models.py:227 views.py:488 views.py:525 views.py:555 +#: models.py:271 views.py:590 views.py:627 views.py:657 msgid "transformation" msgstr "transformación" -#: models.py:228 views.py:489 +#: models.py:272 views.py:591 msgid "arguments" msgstr "argumentos" -#: models.py:228 +#: models.py:272 #, python-format msgid "Use dictionaries to indentify arguments, example: %s" msgstr "Utilizar diccionarios para identificar argumentos, por ejemplo: %s" -#: models.py:239 +#: models.py:283 msgid "document source transformation" msgstr "transformación de fuente de documentos" -#: models.py:240 +#: models.py:284 msgid "document source transformations" msgstr "transformaciones de fuentes de documentos" -#: staging.py:42 +#: models.py:290 models.py:291 +msgid "out of process" +msgstr "fuera de proceso" + +#: permissions.py:7 +msgid "Sources setup" +msgstr "Configuración de fuentes de documentos" + +#: permissions.py:8 +msgid "View existing document sources" +msgstr "Ver fuentes de documento existentes" + +#: permissions.py:9 +msgid "Edit document sources" +msgstr "Editar fuentes de documentos" + +#: permissions.py:10 +msgid "Delete document sources" +msgstr "Eliminar fuentes de documentos" + +#: permissions.py:11 +msgid "Create new document sources" +msgstr "Crear nuevas fuentes de documentos" + +#: staging.py:44 #, python-format msgid "Unable get list of staging files: %s" msgstr "No es posible obtener la lista de los archivos provisionales: %s" -#: staging.py:127 +#: staging.py:129 #, python-format msgid "Unable to upload staging file: %s" msgstr "No se puede cargar archivo provisional: %s" -#: staging.py:137 +#: staging.py:139 #, python-format msgid "Unable to delete staging file: %s" msgstr "No se puede eliminar archivo provisional: %s" @@ -303,151 +315,208 @@ msgstr "No se puede eliminar archivo provisional: %s" msgid "Whitelist Blacklist validation error." msgstr "Error de validación de Lista Negra Lista Blanca" -#: views.py:80 +#: views.py:98 msgid "here" msgstr "aquí" -#: views.py:85 +#: views.py:103 msgid "Upload sources" msgstr "Upload sources" -#: views.py:87 +#: views.py:105 msgid "" "No interactive document sources have been defined or none have been enabled." msgstr "" "No hay fuentes de documentos interactivos definidos o ninguna de ellas ha " "sido activada." -#: views.py:88 +#: views.py:106 #, python-format msgid "Click %(setup_link)s to add or enable some document sources." msgstr "" "Haga clic en %(setup_link)s para agregar o habilitar algunas fuentes de " "documentos." -#: views.py:136 -msgid "Document uploaded successfully." -msgstr "Documento cargado con exitosamente." +#: views.py:163 +msgid "New document version uploaded successfully." +msgstr "Nueva versión del documento subida exitosamente." -#: views.py:152 +#: views.py:167 +msgid "File uploaded successfully." +msgstr "Archivo subido correctamente." + +#: views.py:170 +msgid "File uncompressed successfully and uploaded as individual files." +msgstr "" +"Archivo descomprimido exitosamente y subido como archivos individuales." + +#: views.py:173 +msgid "File was not a compressed file, uploaded as it was." +msgstr "El archivo no era un archivo comprimido, cargado como estaba." + +#: views.py:179 views.py:258 +#, python-format +msgid "Unhandled exception: %s" +msgstr "Excepción sin manejar: %s" + +#: views.py:188 +#, python-format +msgid "upload a new version from source: %s" +msgstr "subir una nueva versión desde la fuente: %s" + +#: views.py:190 #, python-format msgid "upload a local document from source: %s" msgstr "subir un documento local de la fuente: %s" -#: views.py:182 +#: views.py:236 +#, python-format +msgid "Document version from staging file: %s, uploaded successfully." +msgstr "" +"Versión de documento del archivo provisional: %s, subido exitosamente." + +#: views.py:239 #, python-format msgid "Staging file: %s, uploaded successfully." msgstr "Archivo provisional: %s, subido exitosamente." -#: views.py:187 +#: views.py:242 +#, python-format +msgid "" +"Staging file: %s, uncompressed successfully and uploaded as individual " +"files." +msgstr "" +"Archivo provisional: %s, descomprido exitosamente y subido como documentos " +"individuales." + +#: views.py:245 +#, python-format +msgid "Staging file: %s, was not compressed, uploaded as a single file." +msgstr "" +"Archivo provisional: %s, no esta comprimido, subido como un solo documento." + +#: views.py:250 #, python-format msgid "Staging file: %s, deleted successfully." msgstr "Archivo provisional: %s, borrado exitosamente." -#: views.py:209 +#: views.py:273 +#, python-format +msgid "upload a new version from staging source: %s" +msgstr "subir una versión nueva de la fuente de archivo provisionales: %s" + +#: views.py:275 #, python-format msgid "upload a document from staging source: %s" msgstr "cargar un documento de la fuente de archivos provisionales: %s" -#: views.py:215 +#: views.py:288 msgid "files in staging path" msgstr "archivos en la direccion de archivos provisionales" -#: views.py:229 +#: views.py:320 +msgid "Current document type" +msgstr "Tipo de documento actual" + +#: views.py:321 +msgid "None" +msgstr "Ninguno" + +#: views.py:328 msgid "Current metadata" msgstr "Metadatos actuales" -#: views.py:265 views.py:284 +#: views.py:366 views.py:385 #, python-format msgid "Staging file transformation error: %(error)s" msgstr "Error de transformación de archivo provisional: %(error)s" -#: views.py:307 +#: views.py:408 msgid "Staging file delete successfully." msgstr "Archivos provisional borrado exitosamente." -#: views.py:309 +#: views.py:410 #, python-format msgid "Staging file delete error; %s." msgstr "Error al borrar archivo provisional; %s." -#: views.py:368 +#: views.py:470 msgid "Source edited successfully" msgstr "Fuente editada exitosamente" -#: views.py:371 +#: views.py:473 #, python-format msgid "Error editing source; %s" msgstr "Error editando fuente; %s" -#: views.py:376 +#: views.py:478 #, python-format msgid "edit source: %s" msgstr "editar fuente: %s" -#: views.py:381 views.py:421 views.py:483 views.py:524 views.py:554 -#: views.py:597 +#: views.py:483 views.py:523 views.py:585 views.py:626 views.py:656 +#: views.py:699 msgid "source" msgstr "fuente" -#: views.py:410 +#: views.py:512 #, python-format msgid "Source \"%s\" deleted successfully." msgstr "Fuente \"%s\" borrada exitosamente." -#: views.py:412 +#: views.py:514 #, python-format msgid "Error deleting source \"%(source)s\": %(error)s" msgstr "Error borrando fuente \"%(source)s\": %(error)s" -#: views.py:419 +#: views.py:521 #, python-format msgid "Are you sure you wish to delete the source: %s?" msgstr "¿Está seguro que desea eliminar la fuente: %s?" -#: views.py:451 +#: views.py:553 msgid "Source created successfully" msgstr "Fuente creada exitosamente" -#: views.py:454 +#: views.py:556 #, python-format msgid "Error creating source; %s" msgstr "Error creando fuente; %s" -#: views.py:459 +#: views.py:561 #, python-format msgid "Create new source of type: %s" msgstr "Crear nuevo tipo de fuente: %s" -#: views.py:481 +#: views.py:583 #, python-format msgid "transformations for: %s" msgstr "transformaciones para: %s" -#: views.py:511 +#: views.py:613 msgid "Source transformation edited successfully" msgstr "Transformación de la fuente editada exitosamente" -#: views.py:514 +#: views.py:616 #, python-format msgid "Error editing source transformation; %s" msgstr "Error al editar la transformación de la fuente; %s" -#: views.py:519 +#: views.py:621 #, python-format msgid "Edit transformation: %s" msgstr "Editar transformación: %s" -#: views.py:542 +#: views.py:644 msgid "Source transformation deleted successfully." msgstr "Transformación de la fuente borrada exitosamente." -#: views.py:544 +#: views.py:646 #, python-format msgid "Error deleting source transformation; %(error)s" msgstr "Error borrando la transformación de la fuente; %(error)s" -#: views.py:557 +#: views.py:659 #, python-format msgid "" "Are you sure you wish to delete source transformation \"%(transformation)s\"" @@ -455,16 +524,16 @@ msgstr "" "¿Está seguro que desea eliminar la transformación de la fuente " "\"%(transformation)s\"" -#: views.py:587 +#: views.py:689 msgid "Source transformation created successfully" msgstr "Transformación de la fuente creado exitosamente" -#: views.py:590 +#: views.py:692 #, python-format msgid "Error creating source transformation; %s" msgstr "Error al crear la transformación de la fuente; %s" -#: views.py:599 +#: views.py:701 #, python-format msgid "Create new transformation for source: %s" msgstr "Crear una nueva transformación para la fuente: %s" diff --git a/apps/sources/locale/it/LC_MESSAGES/django.mo b/apps/sources/locale/it/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..73223cfe1a Binary files /dev/null and b/apps/sources/locale/it/LC_MESSAGES/django.mo differ diff --git a/apps/sources/locale/it/LC_MESSAGES/django.po b/apps/sources/locale/it/LC_MESSAGES/django.po new file mode 100644 index 0000000000..3905bd9f58 --- /dev/null +++ b/apps/sources/locale/it/LC_MESSAGES/django.po @@ -0,0 +1,538 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# , 2011. +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-12 19:23+0000\n" +"Last-Translator: Roberto Rosario \n" +"Language-Team: Italian (http://www.transifex.net/projects/p/mayan-edms/team/it/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: it\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" + +#: __init__.py:19 +msgid "preview" +msgstr "anteprima" + +#: __init__.py:20 __init__.py:28 __init__.py:34 +msgid "delete" +msgstr "cancella" + +#: __init__.py:22 +msgid "sources" +msgstr "sorgenti" + +#: __init__.py:23 literals.py:53 models.py:203 +msgid "web forms" +msgstr "web forms" + +#: __init__.py:24 models.py:174 +msgid "staging folders" +msgstr "cartelle per la gestione temporanea" + +#: __init__.py:25 models.py:238 +msgid "watch folders" +msgstr "cartelle di" + +#: __init__.py:27 __init__.py:33 +msgid "edit" +msgstr "modifica" + +#: __init__.py:29 +msgid "add new source" +msgstr "aggiungi una nuova sorgente" + +#: __init__.py:31 +msgid "transformations" +msgstr "trasformazioni" + +#: __init__.py:32 +msgid "add transformation" +msgstr "aggiungi una trasformazione" + +#: __init__.py:36 +msgid "Document sources" +msgstr "Sorgente del documento" + +#: __init__.py:38 +msgid "upload new version" +msgstr "" + +#: __init__.py:68 widgets.py:39 +msgid "thumbnail" +msgstr "thumbnail" + +#: forms.py:34 forms.py:59 +msgid "Expand compressed files" +msgstr "Espandi" + +#: forms.py:35 forms.py:60 +msgid "Upload a compressed file's contained files as individual documents" +msgstr "Pubblicare un file compresso contenente singoli documenti" + +#: forms.py:43 +msgid "Staging file" +msgstr "Mostra file" + +#: forms.py:50 +msgid "File" +msgstr "" + +#: literals.py:8 literals.py:13 +msgid "Always" +msgstr "Sempre" + +#: literals.py:9 literals.py:14 +msgid "Never" +msgstr "Mai" + +#: literals.py:15 +msgid "Ask user" +msgstr "Chiedi all'utente" + +#: literals.py:30 +msgid "Disk" +msgstr "Disco" + +#: literals.py:31 +msgid "Database" +msgstr "Database" + +#: literals.py:32 +msgid "Drive" +msgstr "Drive" + +#: literals.py:33 +msgid "Network drive" +msgstr "Disco di rete" + +#: literals.py:34 +msgid "User drive" +msgstr "Disco locale" + +#: literals.py:35 +msgid "Envelope" +msgstr "Busta" + +#: literals.py:36 +msgid "Folder" +msgstr "Cartella" + +#: literals.py:37 +msgid "World" +msgstr "Mondo" + +#: literals.py:38 +msgid "Printer" +msgstr "Stampante" + +#: literals.py:39 +msgid "Empty printer" +msgstr "Stampante vuota" + +#: literals.py:47 models.py:202 +msgid "web form" +msgstr "web form" + +#: literals.py:48 +msgid "server staging folder" +msgstr "server per la gestione temporanea della cartella" + +#: literals.py:49 +msgid "server watch folder" +msgstr "server di salvataggio cartella" + +#: literals.py:54 +msgid "server staging folders" +msgstr "server per la gestione temporanea delle cartelle" + +#: literals.py:55 +msgid "server watch folders" +msgstr "server di salvataggio delle cartelle" + +#: models.py:37 +msgid "title" +msgstr "titolo" + +#: models.py:38 +msgid "enabled" +msgstr "abilitato" + +#: models.py:39 +msgid "whitelist" +msgstr "whitelist" + +#: models.py:40 +msgid "blacklist" +msgstr "blacklist" + +#: models.py:142 +msgid "icon" +msgstr "icona" + +#: models.py:142 +msgid "An icon to visually distinguish this source." +msgstr "Un'icona per distinguere visivamente questa fonte." + +#: models.py:158 models.py:210 +msgid "folder path" +msgstr "path della cartella" + +#: models.py:158 models.py:210 +msgid "Server side filesystem path." +msgstr "Path del server di filesystem" + +#: models.py:159 +msgid "preview width" +msgstr "anteprima larghezza" + +#: models.py:159 +msgid "Width value to be passed to the converter backend." +msgstr "" +"valore della larghezza da passare per le operazioni di conversione in " +"backend" + +#: models.py:160 +msgid "preview height" +msgstr "anteprima altezza" + +#: models.py:160 +msgid "Height value to be passed to the converter backend." +msgstr "" +"valore dell'altezza da passare per le operazioni di conversione in backend" + +#: models.py:161 models.py:198 models.py:211 +msgid "uncompress" +msgstr "decomprimere" + +#: models.py:161 models.py:198 models.py:211 +msgid "Whether to expand or not compressed archives." +msgstr "Se espandere o meno degli archivi compressi." + +#: models.py:162 models.py:212 +msgid "delete after upload" +msgstr "cancella dopo il caricamento" + +#: models.py:162 models.py:212 +msgid "Delete the file after is has been successfully uploaded." +msgstr "Cancella il file dopo essere stato caricato" + +#: models.py:173 +msgid "staging folder" +msgstr "Cartella di conservazione" + +#: models.py:213 +msgid "interval" +msgstr "intervallo" + +#: models.py:213 +msgid "" +"Inverval in seconds where the watch folder path is checked for new " +"documents." +msgstr "" +"Invervallo di pochi secondi in cui viene controllato il percorso cartella di" +" controllo per i nuovi documenti." + +#: models.py:237 +msgid "watch folder" +msgstr "controlla cartella" + +#: models.py:242 +msgid "Enter a valid value." +msgstr "Inserisci un valore valido" + +#: models.py:270 views.py:589 +msgid "order" +msgstr "ordine" + +#: models.py:271 views.py:590 views.py:627 views.py:657 +msgid "transformation" +msgstr "trasformazione" + +#: models.py:272 views.py:591 +msgid "arguments" +msgstr "argomenti" + +#: models.py:272 +#, python-format +msgid "Use dictionaries to indentify arguments, example: %s" +msgstr "Usa dei dizionari per identificare gli argomenti , esempio: %s" + +#: models.py:283 +msgid "document source transformation" +msgstr "trasformazione del documento sorgente" + +#: models.py:284 +msgid "document source transformations" +msgstr "trasformazioni dei documenti sorgente" + +#: models.py:290 models.py:291 +msgid "out of process" +msgstr "" + +#: permissions.py:7 +msgid "Sources setup" +msgstr "Setup sorgente" + +#: permissions.py:8 +msgid "View existing document sources" +msgstr "Visualizza sorgenti documento esistente" + +#: permissions.py:9 +msgid "Edit document sources" +msgstr "Modifica sorgenti dei documenti" + +#: permissions.py:10 +msgid "Delete document sources" +msgstr "Cancella sorgenti dei documenti" + +#: permissions.py:11 +msgid "Create new document sources" +msgstr "Crea nuova sorgente dei documenti" + +#: staging.py:44 +#, python-format +msgid "Unable get list of staging files: %s" +msgstr "Impossibile ottenere lista dei file di gestione temporanea: %s" + +#: staging.py:129 +#, python-format +msgid "Unable to upload staging file: %s" +msgstr "Impossibile caricare file di gestione temporanea: %s" + +#: staging.py:139 +#, python-format +msgid "Unable to delete staging file: %s" +msgstr "Impossibile eliminare file di gestione temporanea: %s" + +#: utils.py:40 +msgid "Whitelist Blacklist validation error." +msgstr "Errori di validazione nelle Whitelist e Blacklist." + +#: views.py:98 +msgid "here" +msgstr "qui" + +#: views.py:103 +msgid "Upload sources" +msgstr "Sorgenti caricamento" + +#: views.py:105 +msgid "" +"No interactive document sources have been defined or none have been enabled." +msgstr "" +"Nessuna fonte interattiva dei documenti sono state definite o non ne sono " +"state attivate." + +#: views.py:106 +#, python-format +msgid "Click %(setup_link)s to add or enable some document sources." +msgstr "" +"Click %(setup_link)s per aggiungere o abilitare una sorgente documenti." + +#: views.py:163 +msgid "New document version uploaded successfully." +msgstr "" + +#: views.py:167 +msgid "File uploaded successfully." +msgstr "" + +#: views.py:170 +msgid "File uncompressed successfully and uploaded as individual files." +msgstr "" + +#: views.py:173 +msgid "File was not a compressed file, uploaded as it was." +msgstr "" + +#: views.py:179 views.py:258 +#, python-format +msgid "Unhandled exception: %s" +msgstr "" + +#: views.py:188 +#, python-format +msgid "upload a new version from source: %s" +msgstr "" + +#: views.py:190 +#, python-format +msgid "upload a local document from source: %s" +msgstr "carica un documento in locale dalla sorgente: %s" + +#: views.py:236 +#, python-format +msgid "Document version from staging file: %s, uploaded successfully." +msgstr "" + +#: views.py:239 +#, python-format +msgid "Staging file: %s, uploaded successfully." +msgstr "File in allestimento:%s, caricato con successo." + +#: views.py:242 +#, python-format +msgid "" +"Staging file: %s, uncompressed successfully and uploaded as individual " +"files." +msgstr "" + +#: views.py:245 +#, python-format +msgid "Staging file: %s, was not compressed, uploaded as a single file." +msgstr "" + +#: views.py:250 +#, python-format +msgid "Staging file: %s, deleted successfully." +msgstr "File in allestimento:%s, cancellato con successo." + +#: views.py:273 +#, python-format +msgid "upload a new version from staging source: %s" +msgstr "" + +#: views.py:275 +#, python-format +msgid "upload a document from staging source: %s" +msgstr "carica documento dalla sorgente allestimento:%s" + +#: views.py:288 +msgid "files in staging path" +msgstr "path dei file in allestimento" + +#: views.py:320 +msgid "Current document type" +msgstr "" + +#: views.py:321 +msgid "None" +msgstr "" + +#: views.py:328 +msgid "Current metadata" +msgstr "Metadati correnti" + +#: views.py:366 views.py:385 +#, python-format +msgid "Staging file transformation error: %(error)s" +msgstr "Errore nella trasformazione del file: %(error)s" + +#: views.py:408 +msgid "Staging file delete successfully." +msgstr "File in allestimento cancellato con successo." + +#: views.py:410 +#, python-format +msgid "Staging file delete error; %s." +msgstr "Errore nella cancellazione del file in allestimento;%s." + +#: views.py:470 +msgid "Source edited successfully" +msgstr "Sorgente modificata con successo" + +#: views.py:473 +#, python-format +msgid "Error editing source; %s" +msgstr "Errore nella modifica del sorgente;%s" + +#: views.py:478 +#, python-format +msgid "edit source: %s" +msgstr "modifica sorgente:%s" + +#: views.py:483 views.py:523 views.py:585 views.py:626 views.py:656 +#: views.py:699 +msgid "source" +msgstr "sorgente" + +#: views.py:512 +#, python-format +msgid "Source \"%s\" deleted successfully." +msgstr "Sorgente \"%s\" cancellata con successo." + +#: views.py:514 +#, python-format +msgid "Error deleting source \"%(source)s\": %(error)s" +msgstr "Errore nella cancellazione della sorgente \"%(source)s\": %(error)s" + +#: views.py:521 +#, python-format +msgid "Are you sure you wish to delete the source: %s?" +msgstr "Sei sicuro di voler cancellare la sorgente: %s?" + +#: views.py:553 +msgid "Source created successfully" +msgstr "Sorgente creata con successo" + +#: views.py:556 +#, python-format +msgid "Error creating source; %s" +msgstr "Errore nella creazione della sorgente;%s" + +#: views.py:561 +#, python-format +msgid "Create new source of type: %s" +msgstr "Crea nuovo tipo di sorgente:%s" + +#: views.py:583 +#, python-format +msgid "transformations for: %s" +msgstr "trasformazione per: %s" + +#: views.py:613 +msgid "Source transformation edited successfully" +msgstr "Sorgente per la trasformazione modificata con successo" + +#: views.py:616 +#, python-format +msgid "Error editing source transformation; %s" +msgstr "Errore nella modifica della sorgente per la trasformazione;%s" + +#: views.py:621 +#, python-format +msgid "Edit transformation: %s" +msgstr "Modifica trasformazione:%s" + +#: views.py:644 +msgid "Source transformation deleted successfully." +msgstr "Sorgente per la trasformazione cancellata con successo." + +#: views.py:646 +#, python-format +msgid "Error deleting source transformation; %(error)s" +msgstr "" +"Erroro nella cancellazione della sorgente per la trasformazione; %(error)s" + +#: views.py:659 +#, python-format +msgid "" +"Are you sure you wish to delete source transformation \"%(transformation)s\"" +msgstr "" +"Sei sicuro di voler cancellare la sorgente di trasformazione " +"\"%(transformation)s\"" + +#: views.py:689 +msgid "Source transformation created successfully" +msgstr "Sorgente di trasformazione creata con successo" + +#: views.py:692 +#, python-format +msgid "Error creating source transformation; %s" +msgstr "Errore nella creazione della sorgente di trasformazione; %s" + +#: views.py:701 +#, python-format +msgid "Create new transformation for source: %s" +msgstr "Crea una nuova sorgente per la trasformazione:%s" + + diff --git a/apps/sources/locale/pl/LC_MESSAGES/django.mo b/apps/sources/locale/pl/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..77f71c4ef5 Binary files /dev/null and b/apps/sources/locale/pl/LC_MESSAGES/django.mo differ diff --git a/apps/sources/locale/pl/LC_MESSAGES/django.po b/apps/sources/locale/pl/LC_MESSAGES/django.po new file mode 100644 index 0000000000..e046cb780b --- /dev/null +++ b/apps/sources/locale/pl/LC_MESSAGES/django.po @@ -0,0 +1,525 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# , 2012. +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-21 21:20+0000\n" +"Last-Translator: mic \n" +"Language-Team: Polish (http://www.transifex.net/projects/p/mayan-edms/language/pl/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pl\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" + +#: __init__.py:19 +msgid "preview" +msgstr "podgląd" + +#: __init__.py:20 __init__.py:28 __init__.py:34 +msgid "delete" +msgstr "usunąć" + +#: __init__.py:22 +msgid "sources" +msgstr "źródła" + +#: __init__.py:23 literals.py:53 models.py:203 +msgid "web forms" +msgstr "" + +#: __init__.py:24 models.py:174 +msgid "staging folders" +msgstr "" + +#: __init__.py:25 models.py:238 +msgid "watch folders" +msgstr "" + +#: __init__.py:27 __init__.py:33 +msgid "edit" +msgstr "edycja" + +#: __init__.py:29 +msgid "add new source" +msgstr "" + +#: __init__.py:31 +msgid "transformations" +msgstr "" + +#: __init__.py:32 +msgid "add transformation" +msgstr "" + +#: __init__.py:36 +msgid "Document sources" +msgstr "" + +#: __init__.py:38 +msgid "upload new version" +msgstr "Prześlij nową wersję" + +#: __init__.py:68 widgets.py:39 +msgid "thumbnail" +msgstr "miniatura" + +#: forms.py:34 forms.py:59 +msgid "Expand compressed files" +msgstr "" + +#: forms.py:35 forms.py:60 +msgid "Upload a compressed file's contained files as individual documents" +msgstr "" + +#: forms.py:43 +msgid "Staging file" +msgstr "" + +#: forms.py:50 +msgid "File" +msgstr "Plik" + +#: literals.py:8 literals.py:13 +msgid "Always" +msgstr "Zawsze" + +#: literals.py:9 literals.py:14 +msgid "Never" +msgstr "Nigdy" + +#: literals.py:15 +msgid "Ask user" +msgstr "Pytaj użytkownika" + +#: literals.py:30 +msgid "Disk" +msgstr "Dysk" + +#: literals.py:31 +msgid "Database" +msgstr "Baza danych" + +#: literals.py:32 +msgid "Drive" +msgstr "" + +#: literals.py:33 +msgid "Network drive" +msgstr "Dysk sieciowy" + +#: literals.py:34 +msgid "User drive" +msgstr "" + +#: literals.py:35 +msgid "Envelope" +msgstr "Koperta" + +#: literals.py:36 +msgid "Folder" +msgstr "Folder" + +#: literals.py:37 +msgid "World" +msgstr "Świat" + +#: literals.py:38 +msgid "Printer" +msgstr "Drukarka" + +#: literals.py:39 +msgid "Empty printer" +msgstr "Pusta drukarka" + +#: literals.py:47 models.py:202 +msgid "web form" +msgstr "" + +#: literals.py:48 +msgid "server staging folder" +msgstr "" + +#: literals.py:49 +msgid "server watch folder" +msgstr "" + +#: literals.py:54 +msgid "server staging folders" +msgstr "" + +#: literals.py:55 +msgid "server watch folders" +msgstr "" + +#: models.py:37 +msgid "title" +msgstr "tytuł" + +#: models.py:38 +msgid "enabled" +msgstr "włączony" + +#: models.py:39 +msgid "whitelist" +msgstr "biała lista" + +#: models.py:40 +msgid "blacklist" +msgstr "czarna lista" + +#: models.py:142 +msgid "icon" +msgstr "ikona" + +#: models.py:142 +msgid "An icon to visually distinguish this source." +msgstr "" + +#: models.py:158 models.py:210 +msgid "folder path" +msgstr "" + +#: models.py:158 models.py:210 +msgid "Server side filesystem path." +msgstr "" + +#: models.py:159 +msgid "preview width" +msgstr "" + +#: models.py:159 +msgid "Width value to be passed to the converter backend." +msgstr "" + +#: models.py:160 +msgid "preview height" +msgstr "" + +#: models.py:160 +msgid "Height value to be passed to the converter backend." +msgstr "" + +#: models.py:161 models.py:198 models.py:211 +msgid "uncompress" +msgstr "rozpakuj" + +#: models.py:161 models.py:198 models.py:211 +msgid "Whether to expand or not compressed archives." +msgstr "" + +#: models.py:162 models.py:212 +msgid "delete after upload" +msgstr "" + +#: models.py:162 models.py:212 +msgid "Delete the file after is has been successfully uploaded." +msgstr "" + +#: models.py:173 +msgid "staging folder" +msgstr "" + +#: models.py:213 +msgid "interval" +msgstr "" + +#: models.py:213 +msgid "" +"Inverval in seconds where the watch folder path is checked for new " +"documents." +msgstr "" + +#: models.py:237 +msgid "watch folder" +msgstr "sprawdzaj folder" + +#: models.py:242 +msgid "Enter a valid value." +msgstr "Wprowadź poprawną wartość." + +#: models.py:270 views.py:589 +msgid "order" +msgstr "" + +#: models.py:271 views.py:590 views.py:627 views.py:657 +msgid "transformation" +msgstr "" + +#: models.py:272 views.py:591 +msgid "arguments" +msgstr "" + +#: models.py:272 +#, python-format +msgid "Use dictionaries to indentify arguments, example: %s" +msgstr "" + +#: models.py:283 +msgid "document source transformation" +msgstr "" + +#: models.py:284 +msgid "document source transformations" +msgstr "" + +#: models.py:290 models.py:291 +msgid "out of process" +msgstr "" + +#: permissions.py:7 +msgid "Sources setup" +msgstr "" + +#: permissions.py:8 +msgid "View existing document sources" +msgstr "" + +#: permissions.py:9 +msgid "Edit document sources" +msgstr "" + +#: permissions.py:10 +msgid "Delete document sources" +msgstr "" + +#: permissions.py:11 +msgid "Create new document sources" +msgstr "" + +#: staging.py:44 +#, python-format +msgid "Unable get list of staging files: %s" +msgstr "" + +#: staging.py:129 +#, python-format +msgid "Unable to upload staging file: %s" +msgstr "" + +#: staging.py:139 +#, python-format +msgid "Unable to delete staging file: %s" +msgstr "" + +#: utils.py:40 +msgid "Whitelist Blacklist validation error." +msgstr "" + +#: views.py:98 +msgid "here" +msgstr "" + +#: views.py:103 +msgid "Upload sources" +msgstr "" + +#: views.py:105 +msgid "" +"No interactive document sources have been defined or none have been enabled." +msgstr "" + +#: views.py:106 +#, python-format +msgid "Click %(setup_link)s to add or enable some document sources." +msgstr "" + +#: views.py:163 +msgid "New document version uploaded successfully." +msgstr "Nowa wersja dokument została pomyślnie przesłana." + +#: views.py:167 +msgid "File uploaded successfully." +msgstr "Plik został pomyślnie przesłany." + +#: views.py:170 +msgid "File uncompressed successfully and uploaded as individual files." +msgstr "Plik rozpakowany pomyślnie i przesłany w osobnych plikach." + +#: views.py:173 +msgid "File was not a compressed file, uploaded as it was." +msgstr "Plik nie był skompresowany , przesłane w oryginale" + +#: views.py:179 views.py:258 +#, python-format +msgid "Unhandled exception: %s" +msgstr "" + +#: views.py:188 +#, python-format +msgid "upload a new version from source: %s" +msgstr "" + +#: views.py:190 +#, python-format +msgid "upload a local document from source: %s" +msgstr "" + +#: views.py:236 +#, python-format +msgid "Document version from staging file: %s, uploaded successfully." +msgstr "" + +#: views.py:239 +#, python-format +msgid "Staging file: %s, uploaded successfully." +msgstr "" + +#: views.py:242 +#, python-format +msgid "" +"Staging file: %s, uncompressed successfully and uploaded as individual " +"files." +msgstr "" + +#: views.py:245 +#, python-format +msgid "Staging file: %s, was not compressed, uploaded as a single file." +msgstr "" + +#: views.py:250 +#, python-format +msgid "Staging file: %s, deleted successfully." +msgstr "" + +#: views.py:273 +#, python-format +msgid "upload a new version from staging source: %s" +msgstr "" + +#: views.py:275 +#, python-format +msgid "upload a document from staging source: %s" +msgstr "" + +#: views.py:288 +msgid "files in staging path" +msgstr "" + +#: views.py:320 +msgid "Current document type" +msgstr "" + +#: views.py:321 +msgid "None" +msgstr "" + +#: views.py:328 +msgid "Current metadata" +msgstr "" + +#: views.py:366 views.py:385 +#, python-format +msgid "Staging file transformation error: %(error)s" +msgstr "" + +#: views.py:408 +msgid "Staging file delete successfully." +msgstr "" + +#: views.py:410 +#, python-format +msgid "Staging file delete error; %s." +msgstr "" + +#: views.py:470 +msgid "Source edited successfully" +msgstr "" + +#: views.py:473 +#, python-format +msgid "Error editing source; %s" +msgstr "" + +#: views.py:478 +#, python-format +msgid "edit source: %s" +msgstr "edytować źródło:%s" + +#: views.py:483 views.py:523 views.py:585 views.py:626 views.py:656 +#: views.py:699 +msgid "source" +msgstr "źródło" + +#: views.py:512 +#, python-format +msgid "Source \"%s\" deleted successfully." +msgstr "Źródło \"%s\" zostało usunięte." + +#: views.py:514 +#, python-format +msgid "Error deleting source \"%(source)s\": %(error)s" +msgstr "" + +#: views.py:521 +#, python-format +msgid "Are you sure you wish to delete the source: %s?" +msgstr "Czy na pewno chcesz usunąć źródło:%s?" + +#: views.py:553 +msgid "Source created successfully" +msgstr "Źródło pomyślnie utworzone" + +#: views.py:556 +#, python-format +msgid "Error creating source; %s" +msgstr "Błąd podczas tworzenia źródła;%s" + +#: views.py:561 +#, python-format +msgid "Create new source of type: %s" +msgstr "Utwórz nowe typ źródło:%s" + +#: views.py:583 +#, python-format +msgid "transformations for: %s" +msgstr "" + +#: views.py:613 +msgid "Source transformation edited successfully" +msgstr "" + +#: views.py:616 +#, python-format +msgid "Error editing source transformation; %s" +msgstr "" + +#: views.py:621 +#, python-format +msgid "Edit transformation: %s" +msgstr "" + +#: views.py:644 +msgid "Source transformation deleted successfully." +msgstr "" + +#: views.py:646 +#, python-format +msgid "Error deleting source transformation; %(error)s" +msgstr "" + +#: views.py:659 +#, python-format +msgid "" +"Are you sure you wish to delete source transformation \"%(transformation)s\"" +msgstr "" + +#: views.py:689 +msgid "Source transformation created successfully" +msgstr "" + +#: views.py:692 +#, python-format +msgid "Error creating source transformation; %s" +msgstr "" + +#: views.py:701 +#, python-format +msgid "Create new transformation for source: %s" +msgstr "" diff --git a/apps/sources/locale/pt/LC_MESSAGES/django.mo b/apps/sources/locale/pt/LC_MESSAGES/django.mo index 242213a5ba..6f2e4bc1cd 100644 Binary files a/apps/sources/locale/pt/LC_MESSAGES/django.mo and b/apps/sources/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/sources/locale/pt/LC_MESSAGES/django.po b/apps/sources/locale/pt/LC_MESSAGES/django.po index 0e71ddee4e..2898677a7d 100644 --- a/apps/sources/locale/pt/LC_MESSAGES/django.po +++ b/apps/sources/locale/pt/LC_MESSAGES/django.po @@ -1,105 +1,93 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: # , 2011. msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" -"PO-Revision-Date: 2011-11-02 01:21+0000\n" -"Last-Translator: emersonsoares \n" -"Language-Team: Portuguese (http://www.transifex.net/projects/p/mayan-edms/" -"team/pt/)\n" -"Language: pt\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-12 19:23+0000\n" +"Last-Translator: Roberto Rosario \n" +"Language-Team: Portuguese (http://www.transifex.net/projects/p/mayan-edms/team/pt/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +"Language: pt\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -#: __init__.py:14 -msgid "View existing document sources" -msgstr "Ver fontes de documentos existentes" - -#: __init__.py:15 -msgid "Edit document sources" -msgstr "Editar fontes de documentos" - -#: __init__.py:16 -msgid "Delete document sources" -msgstr "Excluir fontes de documentos" - -#: __init__.py:17 -msgid "Create new document sources" -msgstr "Criar novas fontes de documentos" - #: __init__.py:19 -msgid "Sources setup" -msgstr "Configuração de fontes" - -#: __init__.py:25 msgid "preview" msgstr "visualização" -#: __init__.py:26 __init__.py:34 __init__.py:40 +#: __init__.py:20 __init__.py:28 __init__.py:34 msgid "delete" msgstr "excluir" -#: __init__.py:28 +#: __init__.py:22 msgid "sources" msgstr "fontes" -#: __init__.py:29 literals.py:53 models.py:159 +#: __init__.py:23 literals.py:53 models.py:203 msgid "web forms" msgstr "formulários web" -#: __init__.py:30 models.py:130 +#: __init__.py:24 models.py:174 msgid "staging folders" msgstr "staging folders" -#: __init__.py:31 models.py:194 +#: __init__.py:25 models.py:238 msgid "watch folders" msgstr "assistir pastas" -#: __init__.py:33 __init__.py:39 +#: __init__.py:27 __init__.py:33 msgid "edit" msgstr "editar" -#: __init__.py:35 +#: __init__.py:29 msgid "add new source" msgstr "adicionar nova fonte" -#: __init__.py:37 +#: __init__.py:31 msgid "transformations" msgstr "transformações" -#: __init__.py:38 +#: __init__.py:32 msgid "add transformation" msgstr "adicionar transformação" -#: __init__.py:42 +#: __init__.py:36 msgid "Document sources" msgstr "Fontes de documentos" -#: __init__.py:69 widgets.py:39 +#: __init__.py:38 +msgid "upload new version" +msgstr "" + +#: __init__.py:68 widgets.py:39 msgid "thumbnail" msgstr "miniaturas" -#: forms.py:32 forms.py:55 +#: forms.py:34 forms.py:59 msgid "Expand compressed files" msgstr "Expandir arquivos compactados" -#: forms.py:33 forms.py:56 +#: forms.py:35 forms.py:60 msgid "Upload a compressed file's contained files as individual documents" msgstr "" -"Upload de um arquivo compactado contendo arquivos como documentos individuais" +"Upload de um arquivo compactado contendo arquivos como documentos " +"individuais" -#: forms.py:41 +#: forms.py:43 msgid "Staging file" msgstr "Preparação de arquivo" +#: forms.py:50 +msgid "File" +msgstr "" + #: literals.py:8 literals.py:13 msgid "Always" msgstr "Sempre" @@ -152,7 +140,7 @@ msgstr "Impressora" msgid "Empty printer" msgstr "Impressora vazia" -#: literals.py:47 models.py:158 +#: literals.py:47 models.py:202 msgid "web form" msgstr "formulário web" @@ -172,129 +160,154 @@ msgstr "server staging folders" msgid "server watch folders" msgstr "assistir pastas do servidor" -#: models.py:29 +#: models.py:37 msgid "title" msgstr "título" -#: models.py:30 +#: models.py:38 msgid "enabled" msgstr "habilitado" -#: models.py:31 +#: models.py:39 msgid "whitelist" msgstr "lista branca" -#: models.py:32 +#: models.py:40 msgid "blacklist" msgstr "lista negra" -#: models.py:98 +#: models.py:142 msgid "icon" msgstr "ícone" -#: models.py:98 +#: models.py:142 msgid "An icon to visually distinguish this source." msgstr "Um ícone para distinguir visualmente essa fonte." -#: models.py:114 models.py:166 +#: models.py:158 models.py:210 msgid "folder path" msgstr "caminho da pasta" -#: models.py:114 models.py:166 +#: models.py:158 models.py:210 msgid "Server side filesystem path." msgstr "Caminho do sistema do servidor" -#: models.py:115 +#: models.py:159 msgid "preview width" msgstr "largura de visualização" -#: models.py:115 +#: models.py:159 msgid "Width value to be passed to the converter backend." msgstr "Valor da largura a ser passado para o backend conversor." -#: models.py:116 +#: models.py:160 msgid "preview height" msgstr "altura de visualização" -#: models.py:116 +#: models.py:160 msgid "Height value to be passed to the converter backend." msgstr "Valor de altura para ser passado para o backend conversor." -#: models.py:117 models.py:154 models.py:167 +#: models.py:161 models.py:198 models.py:211 msgid "uncompress" msgstr "descompactar" -#: models.py:117 models.py:154 models.py:167 +#: models.py:161 models.py:198 models.py:211 msgid "Whether to expand or not compressed archives." msgstr "Se expandir ou não arquivos compactados." -#: models.py:118 models.py:168 +#: models.py:162 models.py:212 msgid "delete after upload" msgstr "excluir após o upload" -#: models.py:118 models.py:168 +#: models.py:162 models.py:212 msgid "Delete the file after is has been successfully uploaded." msgstr "Excluir o arquivo depois de ter sido carregado com sucesso." -#: models.py:129 +#: models.py:173 msgid "staging folder" msgstr "preparação de pasta" -#: models.py:169 +#: models.py:213 msgid "interval" msgstr "intervalo" -#: models.py:169 +#: models.py:213 msgid "" -"Inverval in seconds where the watch folder path is checked for new documents." +"Inverval in seconds where the watch folder path is checked for new " +"documents." msgstr "" "Invervalo em segundos, onde o caminho da pasta assistida está marcada para " "novos documentos." -#: models.py:193 +#: models.py:237 msgid "watch folder" msgstr "assistir pasta" -#: models.py:198 +#: models.py:242 msgid "Enter a valid value." msgstr "Digite um valor válido." -#: models.py:226 views.py:487 +#: models.py:270 views.py:589 msgid "order" msgstr "ordem" -#: models.py:227 views.py:488 views.py:525 views.py:555 +#: models.py:271 views.py:590 views.py:627 views.py:657 msgid "transformation" msgstr "transformação" -#: models.py:228 views.py:489 +#: models.py:272 views.py:591 msgid "arguments" msgstr "argumentos" -#: models.py:228 +#: models.py:272 #, python-format msgid "Use dictionaries to indentify arguments, example: %s" msgstr "Use dicionários para identificar os argumentos, exemplo: %s" -#: models.py:239 +#: models.py:283 msgid "document source transformation" msgstr "transformação do documento de origem" -#: models.py:240 +#: models.py:284 msgid "document source transformations" msgstr "fonte de transformações de documentos" -#: staging.py:42 +#: models.py:290 models.py:291 +msgid "out of process" +msgstr "" + +#: permissions.py:7 +msgid "Sources setup" +msgstr "Configuração de fontes" + +#: permissions.py:8 +msgid "View existing document sources" +msgstr "Ver fontes de documentos existentes" + +#: permissions.py:9 +msgid "Edit document sources" +msgstr "Editar fontes de documentos" + +#: permissions.py:10 +msgid "Delete document sources" +msgstr "Excluir fontes de documentos" + +#: permissions.py:11 +msgid "Create new document sources" +msgstr "Criar novas fontes de documentos" + +#: staging.py:44 #, python-format msgid "Unable get list of staging files: %s" msgstr "Unable get list of staging files: %s" -#: staging.py:127 +#: staging.py:129 #, python-format msgid "Unable to upload staging file: %s" msgstr "Unable to upload staging file: %s" -#: staging.py:137 +#: staging.py:139 #, python-format msgid "Unable to delete staging file: %s" msgstr "Unable to delete staging file: %s" @@ -303,151 +316,203 @@ msgstr "Unable to delete staging file: %s" msgid "Whitelist Blacklist validation error." msgstr "Erro de validação da Lista Negra e da Lista Branca" -#: views.py:80 +#: views.py:98 msgid "here" msgstr "aqui" -#: views.py:85 +#: views.py:103 msgid "Upload sources" msgstr "Carregar fontes" -#: views.py:87 +#: views.py:105 msgid "" "No interactive document sources have been defined or none have been enabled." msgstr "" "Nenhuma fonte de documento interativo foi definido ou nenhuma delas foi " "ativada." -#: views.py:88 +#: views.py:106 #, python-format msgid "Click %(setup_link)s to add or enable some document sources." msgstr "" "Clique %(setup_link)s para adicionar ou permitir algumas fontes de " "documentos." -#: views.py:136 -msgid "Document uploaded successfully." -msgstr "Documento enviado com sucesso." +#: views.py:163 +msgid "New document version uploaded successfully." +msgstr "" -#: views.py:152 +#: views.py:167 +msgid "File uploaded successfully." +msgstr "" + +#: views.py:170 +msgid "File uncompressed successfully and uploaded as individual files." +msgstr "" + +#: views.py:173 +msgid "File was not a compressed file, uploaded as it was." +msgstr "" + +#: views.py:179 views.py:258 +#, python-format +msgid "Unhandled exception: %s" +msgstr "" + +#: views.py:188 +#, python-format +msgid "upload a new version from source: %s" +msgstr "" + +#: views.py:190 #, python-format msgid "upload a local document from source: %s" msgstr "carregar um documento local da fonte: %s" -#: views.py:182 +#: views.py:236 +#, python-format +msgid "Document version from staging file: %s, uploaded successfully." +msgstr "" + +#: views.py:239 #, python-format msgid "Staging file: %s, uploaded successfully." msgstr "Staging file: %s, uploaded successfully." -#: views.py:187 +#: views.py:242 +#, python-format +msgid "" +"Staging file: %s, uncompressed successfully and uploaded as individual " +"files." +msgstr "" + +#: views.py:245 +#, python-format +msgid "Staging file: %s, was not compressed, uploaded as a single file." +msgstr "" + +#: views.py:250 #, python-format msgid "Staging file: %s, deleted successfully." msgstr "Staging file: %s, deleted successfully." -#: views.py:209 +#: views.py:273 +#, python-format +msgid "upload a new version from staging source: %s" +msgstr "" + +#: views.py:275 #, python-format msgid "upload a document from staging source: %s" msgstr "upload a document from staging source: %s" -#: views.py:215 +#: views.py:288 msgid "files in staging path" msgstr "files in staging path" -#: views.py:229 +#: views.py:320 +msgid "Current document type" +msgstr "" + +#: views.py:321 +msgid "None" +msgstr "" + +#: views.py:328 msgid "Current metadata" msgstr "Metadados atuais" -#: views.py:265 views.py:284 +#: views.py:366 views.py:385 #, python-format msgid "Staging file transformation error: %(error)s" msgstr "Staging file transformation error: %(error)s" -#: views.py:307 +#: views.py:408 msgid "Staging file delete successfully." msgstr "Staging file delete successfully." -#: views.py:309 +#: views.py:410 #, python-format msgid "Staging file delete error; %s." msgstr "Staging file delete error; %s." -#: views.py:368 +#: views.py:470 msgid "Source edited successfully" msgstr "Fonte editada com sucesso" -#: views.py:371 +#: views.py:473 #, python-format msgid "Error editing source; %s" msgstr "Erro ao editar fonte; %s" -#: views.py:376 +#: views.py:478 #, python-format msgid "edit source: %s" msgstr "editar fonte: %s" -#: views.py:381 views.py:421 views.py:483 views.py:524 views.py:554 -#: views.py:597 +#: views.py:483 views.py:523 views.py:585 views.py:626 views.py:656 +#: views.py:699 msgid "source" msgstr "fonte" -#: views.py:410 +#: views.py:512 #, python-format msgid "Source \"%s\" deleted successfully." msgstr "Fonte \"%s\" removida com sucesso." -#: views.py:412 +#: views.py:514 #, python-format msgid "Error deleting source \"%(source)s\": %(error)s" msgstr "Erro ao excluir fonte \" %(source)s \": %(error)s " -#: views.py:419 +#: views.py:521 #, python-format msgid "Are you sure you wish to delete the source: %s?" msgstr "Tem certeza de que deseja deletar a fonte: %s?" -#: views.py:451 +#: views.py:553 msgid "Source created successfully" msgstr "Fonte criada com sucesso" -#: views.py:454 +#: views.py:556 #, python-format msgid "Error creating source; %s" msgstr "Erro ao criar fonte; %s" -#: views.py:459 +#: views.py:561 #, python-format msgid "Create new source of type: %s" msgstr "Criar nova fonte do tipo: %s" -#: views.py:481 +#: views.py:583 #, python-format msgid "transformations for: %s" msgstr "transformações para: %s" -#: views.py:511 +#: views.py:613 msgid "Source transformation edited successfully" msgstr "Transformação de fonte alterado com sucesso" -#: views.py:514 +#: views.py:616 #, python-format msgid "Error editing source transformation; %s" msgstr "Erro ao editar transformação de fonte; %s" -#: views.py:519 +#: views.py:621 #, python-format msgid "Edit transformation: %s" msgstr "Editar transformação: %s" -#: views.py:542 +#: views.py:644 msgid "Source transformation deleted successfully." msgstr "Transformação de fonte excluída com sucesso." -#: views.py:544 +#: views.py:646 #, python-format msgid "Error deleting source transformation; %(error)s" msgstr "Erro ao deletar transformação de fonte; %(error)s " -#: views.py:557 +#: views.py:659 #, python-format msgid "" "Are you sure you wish to delete source transformation \"%(transformation)s\"" @@ -455,16 +520,18 @@ msgstr "" "Tem certeza de que deseja deletar a transformação de fonte \" " "%(transformation)s \"" -#: views.py:587 +#: views.py:689 msgid "Source transformation created successfully" msgstr "Transformação de fonte criada com sucesso" -#: views.py:590 +#: views.py:692 #, python-format msgid "Error creating source transformation; %s" msgstr "Erro ao criar a transformação de fonte; %s" -#: views.py:599 +#: views.py:701 #, python-format msgid "Create new transformation for source: %s" msgstr "Criar nova transformação de fonte: %s" + + diff --git a/apps/sources/locale/ru/LC_MESSAGES/django.mo b/apps/sources/locale/ru/LC_MESSAGES/django.mo index 90fbdf8a30..4a4717424c 100644 Binary files a/apps/sources/locale/ru/LC_MESSAGES/django.mo and b/apps/sources/locale/ru/LC_MESSAGES/django.mo differ diff --git a/apps/sources/locale/ru/LC_MESSAGES/django.po b/apps/sources/locale/ru/LC_MESSAGES/django.po index 86c34d5294..9683a35df2 100644 --- a/apps/sources/locale/ru/LC_MESSAGES/django.po +++ b/apps/sources/locale/ru/LC_MESSAGES/django.po @@ -1,105 +1,91 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: -# Sergey Glita , 2011. +# Sergey Glita , 2011, 2012. msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" -"PO-Revision-Date: 2011-11-04 15:25+0000\n" -"Last-Translator: gsv70 \n" -"Language-Team: Russian (http://www.transifex.net/projects/p/mayan-edms/team/" -"ru/)\n" -"Language: ru\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-27 04:24+0000\n" +"Last-Translator: Sergey Glita \n" +"Language-Team: Russian (http://www.transifex.net/projects/p/mayan-edms/language/ru/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" -"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" - -#: __init__.py:14 -msgid "View existing document sources" -msgstr "Просмотр существующих источников документов" - -#: __init__.py:15 -msgid "Edit document sources" -msgstr "Редактировать источник документов" - -#: __init__.py:16 -msgid "Delete document sources" -msgstr "Удалить источник документов " - -#: __init__.py:17 -msgid "Create new document sources" -msgstr "Создать новый источник документов" +"Language: ru\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:19 -msgid "Sources setup" -msgstr "Настройки источников" - -#: __init__.py:25 msgid "preview" msgstr "предварительный просмотр" -#: __init__.py:26 __init__.py:34 __init__.py:40 +#: __init__.py:20 __init__.py:28 __init__.py:34 msgid "delete" msgstr "удалить" -#: __init__.py:28 +#: __init__.py:22 msgid "sources" msgstr "источники" -#: __init__.py:29 literals.py:53 models.py:159 +#: __init__.py:23 literals.py:53 models.py:203 msgid "web forms" msgstr "web-формы" -#: __init__.py:30 models.py:130 +#: __init__.py:24 models.py:174 msgid "staging folders" msgstr "транспортные папки" -#: __init__.py:31 models.py:194 +#: __init__.py:25 models.py:238 msgid "watch folders" msgstr "наблюдаемые папки" -#: __init__.py:33 __init__.py:39 +#: __init__.py:27 __init__.py:33 msgid "edit" msgstr "редактировать" -#: __init__.py:35 +#: __init__.py:29 msgid "add new source" msgstr "добавить новый источник" -#: __init__.py:37 +#: __init__.py:31 msgid "transformations" msgstr "преобразования" -#: __init__.py:38 +#: __init__.py:32 msgid "add transformation" msgstr "добавить преобразование" -#: __init__.py:42 +#: __init__.py:36 msgid "Document sources" msgstr "Источники документов" -#: __init__.py:69 widgets.py:39 +#: __init__.py:38 +msgid "upload new version" +msgstr "загрузить новую версию" + +#: __init__.py:68 widgets.py:39 msgid "thumbnail" msgstr "миниатюра" -#: forms.py:32 forms.py:55 +#: forms.py:34 forms.py:59 msgid "Expand compressed files" msgstr "Извлекать из архивов?" -#: forms.py:33 forms.py:56 +#: forms.py:35 forms.py:60 msgid "Upload a compressed file's contained files as individual documents" msgstr "Загрузить файлы, содержащиеся в архиве в качестве отдельных документов" -#: forms.py:41 +#: forms.py:43 msgid "Staging file" msgstr "Промежуточный файл" +#: forms.py:50 +msgid "File" +msgstr "Файл" + #: literals.py:8 literals.py:13 msgid "Always" msgstr "Всегда" @@ -152,7 +138,7 @@ msgstr "Принтер" msgid "Empty printer" msgstr "Пустой принтер" -#: literals.py:47 models.py:158 +#: literals.py:47 models.py:202 msgid "web form" msgstr "веб-формы" @@ -172,128 +158,152 @@ msgstr "папки на промежуточном сервере" msgid "server watch folders" msgstr "наблюдаемые папки" -#: models.py:29 +#: models.py:37 msgid "title" msgstr "название" -#: models.py:30 +#: models.py:38 msgid "enabled" msgstr "разрешено" -#: models.py:31 +#: models.py:39 msgid "whitelist" msgstr "белый список" -#: models.py:32 +#: models.py:40 msgid "blacklist" msgstr "черный список" -#: models.py:98 +#: models.py:142 msgid "icon" msgstr "иконка" -#: models.py:98 +#: models.py:142 msgid "An icon to visually distinguish this source." msgstr "Значок, чтобы визуально отличать этот источник." -#: models.py:114 models.py:166 +#: models.py:158 models.py:210 msgid "folder path" msgstr "путь к папке" -#: models.py:114 models.py:166 +#: models.py:158 models.py:210 msgid "Server side filesystem path." msgstr "Путь на сервере" -#: models.py:115 +#: models.py:159 msgid "preview width" msgstr "ширина предпросмотра" -#: models.py:115 +#: models.py:159 msgid "Width value to be passed to the converter backend." msgstr "Ширина после обработки." -#: models.py:116 +#: models.py:160 msgid "preview height" msgstr "Предварительный просмотр высоты" -#: models.py:116 +#: models.py:160 msgid "Height value to be passed to the converter backend." msgstr "Высота после обработки." -#: models.py:117 models.py:154 models.py:167 +#: models.py:161 models.py:198 models.py:211 msgid "uncompress" msgstr "распаковать" -#: models.py:117 models.py:154 models.py:167 +#: models.py:161 models.py:198 models.py:211 msgid "Whether to expand or not compressed archives." msgstr "Независимо от того распакованы или нет архивы." -#: models.py:118 models.py:168 +#: models.py:162 models.py:212 msgid "delete after upload" msgstr "удалить после загрузки" -#: models.py:118 models.py:168 +#: models.py:162 models.py:212 msgid "Delete the file after is has been successfully uploaded." msgstr "Удалить файл после загрузки." -#: models.py:129 +#: models.py:173 msgid "staging folder" msgstr "промежуточная папка" -#: models.py:169 +#: models.py:213 msgid "interval" msgstr "интервал" -#: models.py:169 +#: models.py:213 msgid "" -"Inverval in seconds where the watch folder path is checked for new documents." -msgstr "" -"Интервал в секундах, между проверками папки на появление новых документов." +"Inverval in seconds where the watch folder path is checked for new " +"documents." +msgstr "Интервал в секундах, между проверками папки на появление новых документов." -#: models.py:193 +#: models.py:237 msgid "watch folder" msgstr "просматривать папку" -#: models.py:198 +#: models.py:242 msgid "Enter a valid value." msgstr "Введите допустимое значение." -#: models.py:226 views.py:487 +#: models.py:270 views.py:589 msgid "order" msgstr "порядок" -#: models.py:227 views.py:488 views.py:525 views.py:555 +#: models.py:271 views.py:590 views.py:627 views.py:657 msgid "transformation" msgstr "преобразование" -#: models.py:228 views.py:489 +#: models.py:272 views.py:591 msgid "arguments" msgstr "аргументы" -#: models.py:228 +#: models.py:272 #, python-format msgid "Use dictionaries to indentify arguments, example: %s" msgstr "Использование словарей для определения аргументов, например: %s" -#: models.py:239 +#: models.py:283 msgid "document source transformation" msgstr "преобразования источника документов" -#: models.py:240 +#: models.py:284 msgid "document source transformations" msgstr "преобразования источника документов" -#: staging.py:42 +#: models.py:290 models.py:291 +msgid "out of process" +msgstr "из процесса" + +#: permissions.py:7 +msgid "Sources setup" +msgstr "Настройки источников" + +#: permissions.py:8 +msgid "View existing document sources" +msgstr "Просмотр существующих источников документов" + +#: permissions.py:9 +msgid "Edit document sources" +msgstr "Редактировать источник документов" + +#: permissions.py:10 +msgid "Delete document sources" +msgstr "Удалить источник документов " + +#: permissions.py:11 +msgid "Create new document sources" +msgstr "Создать новый источник документов" + +#: staging.py:44 #, python-format msgid "Unable get list of staging files: %s" msgstr "Не удалось получить список промежуточных файлов: %s" -#: staging.py:127 +#: staging.py:129 #, python-format msgid "Unable to upload staging file: %s" msgstr "Невозможно загрузить промежуточный файл: %s" -#: staging.py:137 +#: staging.py:139 #, python-format msgid "Unable to delete staging file: %s" msgstr "Не удается удалить промежуточный файл: %s" @@ -302,165 +312,214 @@ msgstr "Не удается удалить промежуточный файл: msgid "Whitelist Blacklist validation error." msgstr "Ошибка проверки белого или черного списков." -#: views.py:80 +#: views.py:98 msgid "here" msgstr "здесь" -#: views.py:85 +#: views.py:103 msgid "Upload sources" msgstr "Загрузить источники" -#: views.py:87 +#: views.py:105 msgid "" "No interactive document sources have been defined or none have been enabled." msgstr "Интерактивные источники документов не были определены разрешены." -#: views.py:88 +#: views.py:106 #, python-format msgid "Click %(setup_link)s to add or enable some document sources." -msgstr "" -"Нажмите %(setup_link)s, чтобы добавить или включить какой-нибудь документ " -"источников." +msgstr "Нажмите %(setup_link)s, чтобы добавить или включить какой-нибудь документ источников." -#: views.py:136 -msgid "Document uploaded successfully." -msgstr "Документ загружен успешно." +#: views.py:163 +msgid "New document version uploaded successfully." +msgstr "Новая версия документа загружена." -#: views.py:152 +#: views.py:167 +msgid "File uploaded successfully." +msgstr "Файл загружен." + +#: views.py:170 +msgid "File uncompressed successfully and uploaded as individual files." +msgstr "Файл распакован и загружен в виде отдельных файлов." + +#: views.py:173 +msgid "File was not a compressed file, uploaded as it was." +msgstr "Файл не сжат и загружен как есть." + +#: views.py:179 views.py:258 +#, python-format +msgid "Unhandled exception: %s" +msgstr "Необработанное исключение %s" + +#: views.py:188 +#, python-format +msgid "upload a new version from source: %s" +msgstr "загрузка новой версии из источника %s" + +#: views.py:190 #, python-format msgid "upload a local document from source: %s" msgstr "загрузить локальный документ из источника: %s" -#: views.py:182 +#: views.py:236 +#, python-format +msgid "Document version from staging file: %s, uploaded successfully." +msgstr "Версия документа из транспортного файла %s загружена." + +#: views.py:239 #, python-format msgid "Staging file: %s, uploaded successfully." msgstr "Промежуточный файл %s загружен." -#: views.py:187 +#: views.py:242 +#, python-format +msgid "" +"Staging file: %s, uncompressed successfully and uploaded as individual " +"files." +msgstr "Транспортный файл %s распакован и загружен в виде отдельных файлов." + +#: views.py:245 +#, python-format +msgid "Staging file: %s, was not compressed, uploaded as a single file." +msgstr "Транспортный файл %s не был сжат загруженные в исходном виде." + +#: views.py:250 #, python-format msgid "Staging file: %s, deleted successfully." msgstr "Постановка файл %s успешно удален." -#: views.py:209 +#: views.py:273 +#, python-format +msgid "upload a new version from staging source: %s" +msgstr "загрузка новой версии из источника %s" + +#: views.py:275 #, python-format msgid "upload a document from staging source: %s" msgstr "загрузить документ из промежуточного источника %s" -#: views.py:215 +#: views.py:288 msgid "files in staging path" msgstr " файлы в транспортном пути" -#: views.py:229 +#: views.py:320 +msgid "Current document type" +msgstr "Текущий тип документа" + +#: views.py:321 +msgid "None" +msgstr "Нет" + +#: views.py:328 msgid "Current metadata" msgstr "Действующие метаданные" -#: views.py:265 views.py:284 +#: views.py:366 views.py:385 #, python-format msgid "Staging file transformation error: %(error)s" msgstr "Ошибка преобразования транспортного файла: %(error)s" -#: views.py:307 +#: views.py:408 msgid "Staging file delete successfully." msgstr "Транспортный файл удалён." -#: views.py:309 +#: views.py:410 #, python-format msgid "Staging file delete error; %s." msgstr "Ошибка удаления транспортного файла %s." -#: views.py:368 +#: views.py:470 msgid "Source edited successfully" msgstr "Источник успешно изменен" -#: views.py:371 +#: views.py:473 #, python-format msgid "Error editing source; %s" msgstr "Ошибка редактирования источника; %s" -#: views.py:376 +#: views.py:478 #, python-format msgid "edit source: %s" msgstr "редактировать источник: %s" -#: views.py:381 views.py:421 views.py:483 views.py:524 views.py:554 -#: views.py:597 +#: views.py:483 views.py:523 views.py:585 views.py:626 views.py:656 +#: views.py:699 msgid "source" msgstr "источник" -#: views.py:410 +#: views.py:512 #, python-format msgid "Source \"%s\" deleted successfully." msgstr "Источник \"%s\"удален." -#: views.py:412 +#: views.py:514 #, python-format msgid "Error deleting source \"%(source)s\": %(error)s" msgstr "Ошибка при удалении источника \"%(source)s\": %(error)s" -#: views.py:419 +#: views.py:521 #, python-format msgid "Are you sure you wish to delete the source: %s?" msgstr "Вы действительно хотите удалить источник: %s?" -#: views.py:451 +#: views.py:553 msgid "Source created successfully" msgstr "Источник создан" -#: views.py:454 +#: views.py:556 #, python-format msgid "Error creating source; %s" msgstr "Ошибка создания источника; %s" -#: views.py:459 +#: views.py:561 #, python-format msgid "Create new source of type: %s" msgstr "Создать новый источник типа: %s" -#: views.py:481 +#: views.py:583 #, python-format msgid "transformations for: %s" msgstr "преобразования для: %s" -#: views.py:511 +#: views.py:613 msgid "Source transformation edited successfully" msgstr "Преобразование источника изменено" -#: views.py:514 +#: views.py:616 #, python-format msgid "Error editing source transformation; %s" msgstr "Ошибка редактирования преобразования источника; %s" -#: views.py:519 +#: views.py:621 #, python-format msgid "Edit transformation: %s" msgstr "Изменить преобразование: %s" -#: views.py:542 +#: views.py:644 msgid "Source transformation deleted successfully." msgstr "Преобразование источника удалено." -#: views.py:544 +#: views.py:646 #, python-format msgid "Error deleting source transformation; %(error)s" msgstr "Ошибка при удалении преобразования источника; %(error)s" -#: views.py:557 +#: views.py:659 #, python-format msgid "" "Are you sure you wish to delete source transformation \"%(transformation)s\"" -msgstr "" -"Вы действительно хотите удалить источник трансформации \"%(transformation)s\"" +msgstr "Вы действительно хотите удалить источник трансформации \"%(transformation)s\"" -#: views.py:587 +#: views.py:689 msgid "Source transformation created successfully" msgstr "Преобразование источника создано" -#: views.py:590 +#: views.py:692 #, python-format msgid "Error creating source transformation; %s" msgstr "Ошибка создания преобразования источника; %s" -#: views.py:599 +#: views.py:701 #, python-format msgid "Create new transformation for source: %s" msgstr "Создать новое преобразование для источника: %s" diff --git a/apps/sources/management/__init__.py b/apps/sources/management/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/sources/management/commands/__init__.py b/apps/sources/management/commands/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/sources/management/commands/bulk_upload.py b/apps/sources/management/commands/bulk_upload.py new file mode 100644 index 0000000000..8fa11f652e --- /dev/null +++ b/apps/sources/management/commands/bulk_upload.py @@ -0,0 +1,81 @@ +from __future__ import absolute_import + +import os +import sys +from optparse import make_option + +from django.core.management.base import BaseCommand, CommandError, LabelCommand +from django.utils.simplejson import loads + +from metadata.api import convert_dict_to_dict_list +from documents.models import DocumentType + +from ...models import OutOfProcess +from ...compressed_file import CompressedFile, NotACompressedFile + + +class Command(LabelCommand): + args = '' + help = 'Upload documents from a compressed file in to the database.' + option_list = LabelCommand.option_list + ( + make_option('--noinput', action='store_false', dest='interactive', + default=True, help='Do not ask the user for confirmation before ' + 'starting.'), + make_option('--metadata', action='store', dest='metadata', + help='A metadata dictionary list to apply to the documents.'), + make_option('--document_type', action='store', dest='document_type_name', + help='The document type to apply to the uploaded documents.'), + ) + + def handle_label(self, label, **options): + if not os.access(label, os.R_OK): + raise CommandError("File '%s' is not readable." % label) + + if options['metadata']: + try: + metadata_dict = loads(options['metadata']) + metadata_dict_list = convert_dict_to_dict_list(metadata_dict) + except Exception, e: + sys.exit('Metadata error: %s' % e) + else: + metadata_dict_list = None + + if options['document_type_name']: + try: + document_type = DocumentType.objects.get(name=options['document_type_name']) + except DocumentType.DoesNotExist: + sys.exit('Unknown document type') + else: + document_type = None + + if _confirm(options['interactive']) == 'yes': + print 'Beginning upload...' + if metadata_dict_list: + print 'Using the metadata values:' + for key, value in metadata_dict.items(): + print '%s: %s' % (key, value) + + if document_type: + print 'Uploaded document will be of type: %s' % options['document_type_name'] + + source = OutOfProcess() + fd = open(label) + try: + result = source.upload_file(fd, filename=None, use_file_name=False, document_type=document_type, expand=True, metadata_dict_list=metadata_dict_list, user=None, document=None, new_version_data=None, command_line=True) + pass + except NotACompressedFile: + print '%s is not a compressed file.' % label + else: + print 'Finished.' + + fd.close() + else: + print 'Cancelled.' + + +def _confirm(interactive): + if not interactive: + return 'yes' + return raw_input('You have requested to bulk upload a number of documents from a compressed file.\n' + 'Are you sure you want to do this?\n' + 'Type \'yes\' to continue, or any other value to cancel: ') diff --git a/apps/sources/managers.py b/apps/sources/managers.py index 53aa143d5b..4e74ec7d4f 100644 --- a/apps/sources/managers.py +++ b/apps/sources/managers.py @@ -22,5 +22,5 @@ class SourceTransformationManager(models.Manager): ) except (ValueError, SyntaxError), e: warnings.append(e) - + return transformations, warnings diff --git a/apps/sources/migrations/0001_initial.py b/apps/sources/migrations/0001_initial.py new file mode 100644 index 0000000000..e92afa7fcb --- /dev/null +++ b/apps/sources/migrations/0001_initial.py @@ -0,0 +1,156 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Adding model 'StagingFolder' + db.create_table('sources_stagingfolder', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('title', self.gf('django.db.models.fields.CharField')(max_length=64)), + ('enabled', self.gf('django.db.models.fields.BooleanField')(default=True)), + ('whitelist', self.gf('django.db.models.fields.TextField')(blank=True)), + ('blacklist', self.gf('django.db.models.fields.TextField')(blank=True)), + ('icon', self.gf('django.db.models.fields.CharField')(max_length=24, null=True, blank=True)), + ('folder_path', self.gf('django.db.models.fields.CharField')(max_length=255)), + ('preview_width', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True)), + ('preview_height', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True)), + ('uncompress', self.gf('django.db.models.fields.CharField')(max_length=1)), + ('delete_after_upload', self.gf('django.db.models.fields.BooleanField')(default=True)), + )) + db.send_create_signal('sources', ['StagingFolder']) + + # Adding model 'WebForm' + db.create_table('sources_webform', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('title', self.gf('django.db.models.fields.CharField')(max_length=64)), + ('enabled', self.gf('django.db.models.fields.BooleanField')(default=True)), + ('whitelist', self.gf('django.db.models.fields.TextField')(blank=True)), + ('blacklist', self.gf('django.db.models.fields.TextField')(blank=True)), + ('icon', self.gf('django.db.models.fields.CharField')(max_length=24, null=True, blank=True)), + ('uncompress', self.gf('django.db.models.fields.CharField')(max_length=1)), + )) + db.send_create_signal('sources', ['WebForm']) + + # Adding model 'WatchFolder' + db.create_table('sources_watchfolder', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('title', self.gf('django.db.models.fields.CharField')(max_length=64)), + ('enabled', self.gf('django.db.models.fields.BooleanField')(default=True)), + ('whitelist', self.gf('django.db.models.fields.TextField')(blank=True)), + ('blacklist', self.gf('django.db.models.fields.TextField')(blank=True)), + ('folder_path', self.gf('django.db.models.fields.CharField')(max_length=255)), + ('uncompress', self.gf('django.db.models.fields.CharField')(max_length=1)), + ('delete_after_upload', self.gf('django.db.models.fields.BooleanField')(default=True)), + ('interval', self.gf('django.db.models.fields.PositiveIntegerField')()), + )) + db.send_create_signal('sources', ['WatchFolder']) + + # Adding model 'SourceTransformation' + db.create_table('sources_sourcetransformation', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('content_type', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['contenttypes.ContentType'])), + ('object_id', self.gf('django.db.models.fields.PositiveIntegerField')()), + ('order', self.gf('django.db.models.fields.PositiveIntegerField')(default=0, null=True, db_index=True, blank=True)), + ('transformation', self.gf('django.db.models.fields.CharField')(max_length=128)), + ('arguments', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), + )) + db.send_create_signal('sources', ['SourceTransformation']) + + # Adding model 'OutOfProcess' + db.create_table('sources_outofprocess', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('title', self.gf('django.db.models.fields.CharField')(max_length=64)), + ('enabled', self.gf('django.db.models.fields.BooleanField')(default=True)), + ('whitelist', self.gf('django.db.models.fields.TextField')(blank=True)), + ('blacklist', self.gf('django.db.models.fields.TextField')(blank=True)), + )) + db.send_create_signal('sources', ['OutOfProcess']) + + + def backwards(self, orm): + + # Deleting model 'StagingFolder' + db.delete_table('sources_stagingfolder') + + # Deleting model 'WebForm' + db.delete_table('sources_webform') + + # Deleting model 'WatchFolder' + db.delete_table('sources_watchfolder') + + # Deleting model 'SourceTransformation' + db.delete_table('sources_sourcetransformation') + + # Deleting model 'OutOfProcess' + db.delete_table('sources_outofprocess') + + + models = { + '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'}) + }, + 'sources.outofprocess': { + 'Meta': {'ordering': "('title',)", 'object_name': 'OutOfProcess'}, + 'blacklist': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'whitelist': ('django.db.models.fields.TextField', [], {'blank': 'True'}) + }, + 'sources.sourcetransformation': { + 'Meta': {'ordering': "('order',)", 'object_name': 'SourceTransformation'}, + 'arguments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'order': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'null': 'True', 'db_index': 'True', 'blank': 'True'}), + 'transformation': ('django.db.models.fields.CharField', [], {'max_length': '128'}) + }, + 'sources.stagingfolder': { + 'Meta': {'ordering': "('title',)", 'object_name': 'StagingFolder'}, + 'blacklist': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'delete_after_upload': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'folder_path': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'icon': ('django.db.models.fields.CharField', [], {'max_length': '24', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'preview_height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'preview_width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'uncompress': ('django.db.models.fields.CharField', [], {'max_length': '1'}), + 'whitelist': ('django.db.models.fields.TextField', [], {'blank': 'True'}) + }, + 'sources.watchfolder': { + 'Meta': {'ordering': "('title',)", 'object_name': 'WatchFolder'}, + 'blacklist': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'delete_after_upload': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'folder_path': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'interval': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'uncompress': ('django.db.models.fields.CharField', [], {'max_length': '1'}), + 'whitelist': ('django.db.models.fields.TextField', [], {'blank': 'True'}) + }, + 'sources.webform': { + 'Meta': {'ordering': "('title',)", 'object_name': 'WebForm'}, + 'blacklist': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'icon': ('django.db.models.fields.CharField', [], {'max_length': '24', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'uncompress': ('django.db.models.fields.CharField', [], {'max_length': '1'}), + 'whitelist': ('django.db.models.fields.TextField', [], {'blank': 'True'}) + } + } + + complete_apps = ['sources'] diff --git a/apps/sources/migrations/0002_preview_width_required.py b/apps/sources/migrations/0002_preview_width_required.py new file mode 100644 index 0000000000..3cb2587561 --- /dev/null +++ b/apps/sources/migrations/0002_preview_width_required.py @@ -0,0 +1,84 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Changing field 'StagingFolder.preview_width' + db.alter_column('sources_stagingfolder', 'preview_width', self.gf('django.db.models.fields.IntegerField')(default=640)) + + + def backwards(self, orm): + + # Changing field 'StagingFolder.preview_width' + db.alter_column('sources_stagingfolder', 'preview_width', self.gf('django.db.models.fields.IntegerField')(null=True)) + + + models = { + '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'}) + }, + 'sources.outofprocess': { + 'Meta': {'ordering': "('title',)", 'object_name': 'OutOfProcess'}, + 'blacklist': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'whitelist': ('django.db.models.fields.TextField', [], {'blank': 'True'}) + }, + 'sources.sourcetransformation': { + 'Meta': {'ordering': "('order',)", 'object_name': 'SourceTransformation'}, + 'arguments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'order': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'null': 'True', 'db_index': 'True', 'blank': 'True'}), + 'transformation': ('django.db.models.fields.CharField', [], {'max_length': '128'}) + }, + 'sources.stagingfolder': { + 'Meta': {'ordering': "('title',)", 'object_name': 'StagingFolder'}, + 'blacklist': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'delete_after_upload': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'folder_path': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'icon': ('django.db.models.fields.CharField', [], {'max_length': '24', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'preview_height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'preview_width': ('django.db.models.fields.IntegerField', [], {}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'uncompress': ('django.db.models.fields.CharField', [], {'max_length': '1'}), + 'whitelist': ('django.db.models.fields.TextField', [], {'blank': 'True'}) + }, + 'sources.watchfolder': { + 'Meta': {'ordering': "('title',)", 'object_name': 'WatchFolder'}, + 'blacklist': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'delete_after_upload': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'folder_path': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'interval': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'uncompress': ('django.db.models.fields.CharField', [], {'max_length': '1'}), + 'whitelist': ('django.db.models.fields.TextField', [], {'blank': 'True'}) + }, + 'sources.webform': { + 'Meta': {'ordering': "('title',)", 'object_name': 'WebForm'}, + 'blacklist': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'icon': ('django.db.models.fields.CharField', [], {'max_length': '24', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'uncompress': ('django.db.models.fields.CharField', [], {'max_length': '1'}), + 'whitelist': ('django.db.models.fields.TextField', [], {'blank': 'True'}) + } + } + + complete_apps = ['sources'] diff --git a/apps/sources/migrations/__init__.py b/apps/sources/migrations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/sources/models.py b/apps/sources/models.py index 3d09915617..0941abd00d 100644 --- a/apps/sources/models.py +++ b/apps/sources/models.py @@ -1,13 +1,17 @@ +from __future__ import absolute_import + from ast import literal_eval +import logging from django.db import models from django.utils.translation import ugettext_lazy as _ from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes import generic from django.core.exceptions import ValidationError +from django.db import transaction from converter.api import get_available_transformations_choices -from converter.literals import DIMENSION_SEPARATOR +from converter.literals import DIMENSION_SEPARATOR from documents.models import DocumentType, Document from documents.literals import HISTORY_DOCUMENT_CREATED from document_indexing.api import update_indexes @@ -15,14 +19,17 @@ from history.api import create_history from metadata.models import MetadataType from metadata.api import save_metadata_list from scheduler.api import register_interval_job, remove_job +from acls.utils import apply_default_acls -from sources.managers import SourceTransformationManager -from sources.literals import SOURCE_CHOICES, SOURCE_CHOICES_PLURAL, \ - SOURCE_INTERACTIVE_UNCOMPRESS_CHOICES, SOURCE_CHOICE_WEB_FORM, \ - SOURCE_CHOICE_STAGING, SOURCE_ICON_DISK, SOURCE_ICON_DRIVE, \ - SOURCE_ICON_CHOICES, SOURCE_CHOICE_WATCH, SOURCE_UNCOMPRESS_CHOICES, \ - SOURCE_UNCOMPRESS_CHOICE_Y -from sources.compressed_file import CompressedFile, NotACompressedFile +from .managers import SourceTransformationManager +from .literals import (SOURCE_CHOICES, SOURCE_CHOICES_PLURAL, + SOURCE_INTERACTIVE_UNCOMPRESS_CHOICES, SOURCE_CHOICE_WEB_FORM, + SOURCE_CHOICE_STAGING, SOURCE_ICON_DISK, SOURCE_ICON_DRIVE, + SOURCE_ICON_CHOICES, SOURCE_CHOICE_WATCH, SOURCE_UNCOMPRESS_CHOICES, + SOURCE_UNCOMPRESS_CHOICE_Y) +from .compressed_file import CompressedFile, NotACompressedFile + +logger = logging.getLogger(__name__) class BaseModel(models.Model): @@ -31,7 +38,7 @@ class BaseModel(models.Model): whitelist = models.TextField(blank=True, verbose_name=_(u'whitelist'), editable=False) blacklist = models.TextField(blank=True, verbose_name=_(u'blacklist'), editable=False) #document_type = models.ForeignKey(DocumentType, blank=True, null=True, verbose_name=_(u'document type'), help_text=(u'Optional document type to be applied to documents uploaded from this source.')) - + @classmethod def class_fullname(cls): return unicode(dict(SOURCE_CHOICES).get(cls.source_type)) @@ -42,41 +49,55 @@ class BaseModel(models.Model): def __unicode__(self): return u'%s' % self.title - + def fullname(self): return u' '.join([self.class_fullname(), '"%s"' % self.title]) - + def internal_name(self): return u'%s_%d' % (self.source_type, self.pk) def get_transformation_list(self): return SourceTransformation.transformations.get_for_object_as_list(self) - def upload_file(self, file_object, filename=None, use_file_name=False, document_type=None, expand=False, metadata_dict_list=None, user=None, document=None, new_version_data=None): + def upload_file(self, file_object, filename=None, use_file_name=False, document_type=None, expand=False, metadata_dict_list=None, user=None, document=None, new_version_data=None, command_line=False): + is_compressed = None + if expand: try: cf = CompressedFile(file_object) + count = 1 for fp in cf.children(): - self.upload_single_file(fp, None, document_type, metadata_dict_list, user) + if command_line: + print 'Uploading file #%d: %s' % (count, fp) + self.upload_single_file(file_object=fp, filename=None, document_type=document_type, metadata_dict_list=metadata_dict_list, user=user) fp.close() + count += 1 except NotACompressedFile: - self.upload_single_file(file_object, filename, document_type, metadata_dict_list, user) + is_compressed = False + logging.debug('Exception: NotACompressedFile') + if command_line: + raise + self.upload_single_file(file_object=file_object, filename=filename, document_type=document_type, metadata_dict_list=metadata_dict_list, user=user) + else: + is_compressed = True else: self.upload_single_file(file_object, filename, use_file_name, document_type, metadata_dict_list, user, document, new_version_data) - + file_object.close() - + return {'is_compressed': is_compressed} + + @transaction.commit_on_success def upload_single_file(self, file_object, filename=None, use_file_name=False, document_type=None, metadata_dict_list=None, user=None, document=None, new_version_data=None): + new_document = not document + if not document: document = Document() if document_type: document.document_type = document_type - document.save() + document.save() - if metadata_dict_list: - save_metadata_list(metadata_dict_list, document, create=True) - warnings = update_indexes(document) + apply_default_acls(document, user) if user: document.add_as_recent_document_for_user(user) @@ -91,17 +112,28 @@ class BaseModel(models.Model): if not new_version_data: new_version_data = {} - - new_version = document.new_version(file=file_object, **new_version_data) + + try: + new_version = document.new_version(file=file_object, **new_version_data) + except Exception: + # Don't leave the database in a broken state + # document.delete() + transaction.rollback() + raise + if filename: - new_version.filename = filename - new_version.save() + document.rename(filename) transformations, errors = self.get_transformation_list() new_version.apply_default_transformations(transformations) #TODO: new HISTORY for version updates - + + if metadata_dict_list and new_document: + # Only do for new documents + save_metadata_list(metadata_dict_list, document, create=True) + warnings = update_indexes(document) + class Meta: ordering = ('title',) abstract = True @@ -118,14 +150,14 @@ class InteractiveBaseModel(BaseModel): class Meta(BaseModel.Meta): abstract = True - + class StagingFolder(InteractiveBaseModel): is_interactive = True source_type = SOURCE_CHOICE_STAGING default_icon = SOURCE_ICON_DRIVE - + folder_path = models.CharField(max_length=255, verbose_name=_(u'folder path'), help_text=_(u'Server side filesystem path.')) - preview_width = models.IntegerField(blank=True, null=True, verbose_name=_(u'preview width'), help_text=_(u'Width value to be passed to the converter backend.')) + preview_width = models.IntegerField(verbose_name=_(u'preview width'), help_text=_(u'Width value to be passed to the converter backend.')) preview_height = models.IntegerField(blank=True, null=True, verbose_name=_(u'preview height'), help_text=_(u'Height value to be passed to the converter backend.')) uncompress = models.CharField(max_length=1, choices=SOURCE_INTERACTIVE_UNCOMPRESS_CHOICES, verbose_name=_(u'uncompress'), help_text=_(u'Whether to expand or not compressed archives.')) delete_after_upload = models.BooleanField(default=True, verbose_name=_(u'delete after upload'), help_text=_(u'Delete the file after is has been successfully uploaded.')) @@ -142,7 +174,7 @@ class StagingFolder(InteractiveBaseModel): verbose_name = _(u'staging folder') verbose_name_plural = _(u'staging folders') -''' +""" class SourceMetadata(models.Model): content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() @@ -156,7 +188,7 @@ class SourceMetadata(models.Model): class Meta: verbose_name = _(u'source metadata') verbose_name_plural = _(u'sources metadata') -''' +""" class WebForm(InteractiveBaseModel): @@ -171,16 +203,16 @@ class WebForm(InteractiveBaseModel): verbose_name = _(u'web form') verbose_name_plural = _(u'web forms') - + class WatchFolder(BaseModel): is_interactive = False source_type = SOURCE_CHOICE_WATCH - + folder_path = models.CharField(max_length=255, verbose_name=_(u'folder path'), help_text=_(u'Server side filesystem path.')) uncompress = models.CharField(max_length=1, choices=SOURCE_UNCOMPRESS_CHOICES, verbose_name=_(u'uncompress'), help_text=_(u'Whether to expand or not compressed archives.')) delete_after_upload = models.BooleanField(default=True, verbose_name=_(u'delete after upload'), help_text=_(u'Delete the file after is has been successfully uploaded.')) interval = models.PositiveIntegerField(verbose_name=_(u'interval'), help_text=_(u'Inverval in seconds where the watch folder path is checked for new documents.')) - + def save(self, *args, **kwargs): if self.pk: remove_job(self.internal_name()) @@ -189,11 +221,11 @@ class WatchFolder(BaseModel): def schedule(self): if self.enabled: - register_interval_job(self.internal_name(), - title=self.fullname(), func=self.execute, + register_interval_job(self.internal_name(), + title=self.fullname(), func=self.execute, kwargs={'source_id': self.pk}, seconds=self.interval ) - + def execute(self, source_id): source = WatchFolder.objects.get(pk=source_id) if source.uncompress == SOURCE_UNCOMPRESS_CHOICE_Y: @@ -201,11 +233,11 @@ class WatchFolder(BaseModel): else: expand = False print 'execute: %s' % self.internal_name() - + class Meta(BaseModel.Meta): verbose_name = _(u'watch folder') verbose_name_plural = _(u'watch folders') - + class ArgumentsValidator(object): message = _(u'Enter a valid value.') @@ -251,3 +283,11 @@ class SourceTransformation(models.Model): ordering = ('order',) verbose_name = _(u'document source transformation') verbose_name_plural = _(u'document source transformations') + + +class OutOfProcess(BaseModel): + is_interactive = False + + class Meta(BaseModel.Meta): + verbose_name = _(u'out of process') + verbose_name_plural = _(u'out of process') diff --git a/apps/sources/permissions.py b/apps/sources/permissions.py new file mode 100644 index 0000000000..e5911fe72c --- /dev/null +++ b/apps/sources/permissions.py @@ -0,0 +1,11 @@ +from __future__ import absolute_import + +from django.utils.translation import ugettext_lazy as _ + +from permissions.models import Permission, PermissionNamespace + +sources_setup_namespace = PermissionNamespace('sources_setup', _(u'Sources setup')) +PERMISSION_SOURCES_SETUP_VIEW = Permission.objects.register(sources_setup_namespace, 'sources_setup_view', _(u'View existing document sources')) +PERMISSION_SOURCES_SETUP_EDIT = Permission.objects.register(sources_setup_namespace, 'sources_setup_edit', _(u'Edit document sources')) +PERMISSION_SOURCES_SETUP_DELETE = Permission.objects.register(sources_setup_namespace, 'sources_setup_delete', _(u'Delete document sources')) +PERMISSION_SOURCES_SETUP_CREATE = Permission.objects.register(sources_setup_namespace, 'sources_setup_create', _(u'Create new document sources')) diff --git a/apps/sources/staging.py b/apps/sources/staging.py index 774bc01db8..c6a92a2bcb 100644 --- a/apps/sources/staging.py +++ b/apps/sources/staging.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import + import errno import os import hashlib @@ -39,7 +41,7 @@ def get_all_files(path): try: return sorted([os.path.normcase(f) for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))]) except OSError, exc: - raise OSError(ugettext(u'Unable get list of staging files: %s') % exc) + raise Exception(ugettext(u'Unable get list of staging files: %s') % exc) def _return_new_class(): @@ -100,9 +102,7 @@ class StagingFile(object): self.source = source self.filepath = filepath self.filename = os.path.basename(filepath) - fd = open(filepath, 'rb') - self._id = HASH_FUNCTION(fd.read()) - fd.close() + self._id = HASH_FUNCTION(filepath) def __unicode__(self): return self.filename @@ -134,7 +134,7 @@ class StagingFile(object): if exc.errno == errno.ENOENT: pass else: - raise OSError(ugettext(u'Unable to delete staging file: %s') % exc) + raise Exception(ugettext(u'Unable to delete staging file: %s') % exc) def get_valid_image(self, size=THUMBNAIL_SIZE, transformations=None): return convert(self.filepath, size=size, cleanup_files=False, transformations=transformations) diff --git a/apps/sources/tests.py b/apps/sources/tests.py deleted file mode 100644 index 2247054b35..0000000000 --- a/apps/sources/tests.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -This file demonstrates two different styles of tests (one doctest and one -unittest). These will both pass when you run "manage.py test". - -Replace these 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.failUnlessEqual(1 + 1, 2) - -__test__ = {"doctest": """ -Another way to test that 1 + 1 is equal to 2. - ->>> 1 + 1 == 2 -True -"""} - diff --git a/apps/sources/urls.py b/apps/sources/urls.py index 7baf07a1a9..9b8f6ad59f 100644 --- a/apps/sources/urls.py +++ b/apps/sources/urls.py @@ -1,7 +1,9 @@ +from __future__ import absolute_import + from django.conf.urls.defaults import patterns, url -from sources.literals import SOURCE_CHOICE_WEB_FORM, SOURCE_CHOICE_STAGING, \ - SOURCE_CHOICE_WATCH +from .literals import (SOURCE_CHOICE_WEB_FORM, SOURCE_CHOICE_STAGING, + SOURCE_CHOICE_WATCH) urlpatterns = patterns('sources.views', url(r'^staging_file/type/(?P\w+)/(?P\d+)/(?P\w+)/preview/$', 'staging_file_preview', (), 'staging_file_preview'), diff --git a/apps/sources/utils.py b/apps/sources/utils.py index 6d61ad9ebb..79a2c80fc4 100644 --- a/apps/sources/utils.py +++ b/apps/sources/utils.py @@ -9,28 +9,28 @@ def accept_item(value, whitelist, blacklist, default_accept=True): """ return true if this item is either whitelisted or not blacklisted - """ + """ if not whitelist: whitelist = [] - + if not blacklist: blacklist = [] - # note the order - for reject, item_list in ([False, whitelist], [True, blacklist]): + # note the order + for reject, item_list in ([False, whitelist], [True, blacklist]): #print 'item_list: %s' % item_list #print 'reject: %s' % reject for okpattern in item_list: #print 'okpattern: %s' % okpattern - if re.findall(okpattern.replace('*', '\S+'), value, re.I): - # match! + if re.findall(okpattern.replace('*', '\S+'), value, re.I): + # match! #print 'MATCH' - if reject: - return False - else: + if reject: + return False + else: return True - # default is to accept all + # default is to accept all return default_accept diff --git a/apps/sources/views.py b/apps/sources/views.py index d3b8a628a1..ddbf4c71b0 100644 --- a/apps/sources/views.py +++ b/apps/sources/views.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import + from django.http import HttpResponseRedirect from django.shortcuts import render_to_response, get_object_or_404 from django.template import RequestContext @@ -6,29 +8,33 @@ 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 django.core.exceptions import PermissionDenied -from documents.literals import PERMISSION_DOCUMENT_CREATE +from documents.permissions import (PERMISSION_DOCUMENT_CREATE, + PERMISSION_DOCUMENT_NEW_VERSION) from documents.models import DocumentType, Document from documents.conf.settings import THUMBNAIL_SIZE from metadata.api import decode_metadata_from_url, metadata_repr_as_list -from permissions.api import check_permissions +from permissions.models import Permission from common.utils import encapsulate import sendfile +from acls.models import AccessEntry -from sources.models import WebForm, StagingFolder, SourceTransformation, \ - WatchFolder -from sources.literals import SOURCE_CHOICE_WEB_FORM, SOURCE_CHOICE_STAGING, \ - SOURCE_CHOICE_WATCH -from sources.literals import SOURCE_UNCOMPRESS_CHOICE_Y, \ - SOURCE_UNCOMPRESS_CHOICE_ASK -from sources.staging import create_staging_file_class, StagingFile -from sources.forms import StagingDocumentForm, WebFormForm, \ - WatchFolderSetupForm +from sources.models import (WebForm, StagingFolder, SourceTransformation, + WatchFolder) +from sources.literals import (SOURCE_CHOICE_WEB_FORM, SOURCE_CHOICE_STAGING, + SOURCE_CHOICE_WATCH) +from sources.literals import (SOURCE_UNCOMPRESS_CHOICE_Y, + SOURCE_UNCOMPRESS_CHOICE_ASK) +from sources.staging import create_staging_file_class +from sources.forms import (StagingDocumentForm, WebFormForm, + WatchFolderSetupForm) from sources.forms import WebFormSetupForm, StagingFolderSetupForm from sources.forms import SourceTransformationForm, SourceTransformationForm_create -from sources import PERMISSION_SOURCES_SETUP_VIEW, \ - PERMISSION_SOURCES_SETUP_EDIT, PERMISSION_SOURCES_SETUP_DELETE, \ - PERMISSION_SOURCES_SETUP_CREATE +from .permissions import (PERMISSION_SOURCES_SETUP_VIEW, + PERMISSION_SOURCES_SETUP_EDIT, PERMISSION_SOURCES_SETUP_DELETE, + PERMISSION_SOURCES_SETUP_CREATE) def return_function(obj): @@ -42,7 +48,7 @@ def get_tab_link_for_source(source, document=None): else: view = u'upload_interactive' args = [u'"%s"' % source.source_type, source.pk] - + return { 'text': source.title, 'view': view, @@ -50,19 +56,19 @@ def get_tab_link_for_source(source, document=None): 'famfam': source.icon, 'keep_query': True, 'conditional_highlight': return_function(source), - } + } def get_active_tab_links(document=None): tab_links = [] - + web_forms = WebForm.objects.filter(enabled=True) for web_form in web_forms: tab_links.append(get_tab_link_for_source(web_form, document)) staging_folders = StagingFolder.objects.filter(enabled=True) for staging_folder in staging_folders: - tab_links.append(get_tab_link_for_source(staging_folder, document)) + tab_links.append(get_tab_link_for_source(staging_folder, document)) return { 'tab_links': tab_links, @@ -70,15 +76,20 @@ def get_active_tab_links(document=None): SOURCE_CHOICE_STAGING: staging_folders } -def upload_interactive(request, source_type=None, source_id=None, document_pk=None): - check_permissions(request.user, [PERMISSION_DOCUMENT_CREATE]) +def upload_interactive(request, source_type=None, source_id=None, document_pk=None): subtemplates_list = [] if document_pk: document = get_object_or_404(Document, pk=document_pk) + try: + Permission.objects.check_permissions(request.user, [PERMISSION_DOCUMENT_NEW_VERSION]) + except PermissionDenied: + AccessEntry.objects.check_access(PERMISSION_DOCUMENT_NEW_VERSION, request.user, document) + results = get_active_tab_links(document) else: + Permission.objects.check_permissions(request.user, [PERMISSION_DOCUMENT_CREATE]) document = None results = get_active_tab_links() @@ -139,23 +150,33 @@ def upload_interactive(request, source_type=None, source_id=None, document_pk=No expand = False new_filename = get_form_filename(form) - - web_form.upload_file(request.FILES['file'], + + result = web_form.upload_file(request.FILES['file'], new_filename, use_file_name=form.cleaned_data.get('use_file_name', False), document_type=document_type, expand=expand, metadata_dict_list=decode_metadata_from_url(request.GET), user=request.user, document=document, - new_version_data=form.cleaned_data.get('new_version_data') + new_version_data=form.cleaned_data.get('new_version_data') ) if document: - messages.success(request, _(u'Document version uploaded successfully.')) + messages.success(request, _(u'New document version uploaded successfully.')) return HttpResponseRedirect(reverse('document_view_simple', args=[document.pk])) else: - messages.success(request, _(u'Document uploaded successfully.')) + if result['is_compressed'] == None: + messages.success(request, _(u'File uploaded successfully.')) + + if result['is_compressed'] == True: + messages.success(request, _(u'File uncompressed successfully and uploaded as individual files.')) + + if result['is_compressed'] == False: + messages.warning(request, _(u'File was not a compressed file, uploaded as it was.')) + return HttpResponseRedirect(request.get_full_path()) except Exception, e: + if settings.DEBUG: + raise messages.error(request, _(u'Unhandled exception: %s') % e) else: form = WebFormForm( @@ -168,7 +189,7 @@ def upload_interactive(request, source_type=None, source_id=None, document_pk=No title = _(u'upload a new version from source: %s') % web_form.title else: title = _(u'upload a local document from source: %s') % web_form.title - + subtemplates_list.append({ 'name': 'generic_form_subtemplate.html', 'context': { @@ -202,20 +223,27 @@ def upload_interactive(request, source_type=None, source_id=None, document_pk=No expand = False new_filename = get_form_filename(form) - - staging_folder.upload_file(staging_file.upload(), + + result = staging_folder.upload_file(staging_file.upload(), new_filename, use_file_name=form.cleaned_data.get('use_file_name', False), document_type=document_type, expand=expand, metadata_dict_list=decode_metadata_from_url(request.GET), user=request.user, document=document, - new_version_data=form.cleaned_data.get('new_version_data') + new_version_data=form.cleaned_data.get('new_version_data') ) if document: messages.success(request, _(u'Document version from staging file: %s, uploaded successfully.') % staging_file.filename) else: - messages.success(request, _(u'Staging file: %s, uploaded successfully.') % staging_file.filename) + if result['is_compressed'] == None: + messages.success(request, _(u'Staging file: %s, uploaded successfully.') % staging_file.filename) + + if result['is_compressed'] == True: + messages.success(request, _(u'Staging file: %s, uncompressed successfully and uploaded as individual files.') % staging_file.filename) + + if result['is_compressed'] == False: + messages.warning(request, _(u'Staging file: %s, was not compressed, uploaded as a single file.') % staging_file.filename) if staging_folder.delete_after_upload: transformations, errors = staging_folder.get_transformation_list() @@ -226,6 +254,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, @@ -244,7 +274,7 @@ def upload_interactive(request, source_type=None, source_id=None, document_pk=No title = _(u'upload a new version from staging source: %s') % staging_folder.title else: title = _(u'upload a document from staging source: %s') % staging_folder.title - + subtemplates_list = [ { 'name': 'generic_form_subtemplate.html', @@ -261,7 +291,7 @@ def upload_interactive(request, source_type=None, source_id=None, document_pk=No 'hide_link': True, } }, - ] + ] if document: context['object'] = document @@ -276,15 +306,23 @@ def upload_interactive(request, source_type=None, source_id=None, document_pk=No }, 'upload_interactive': { 'links': results['tab_links'] - } + } } }, }) - + if not document: context.update( { 'sidebar_subtemplates_list': [ + { + 'name': 'generic_subtemplate.html', + 'context': { + 'title': _(u'Current document type'), + 'paragraphs': [document_type if document_type else _(u'None')], + 'side_bar': True, + } + }, { 'name': 'generic_subtemplate.html', 'context': { @@ -296,7 +334,7 @@ def upload_interactive(request, source_type=None, source_id=None, document_pk=No ], } ) - + return render_to_response('generic_form.html', context, context_instance=RequestContext(request)) @@ -315,7 +353,7 @@ def get_form_filename(form): def staging_file_preview(request, source_type, source_id, staging_file_id): - check_permissions(request.user, [PERMISSION_DOCUMENT_CREATE]) + Permission.objects.check_permissions(request.user, [PERMISSION_DOCUMENT_CREATE]) staging_folder = get_object_or_404(StagingFolder, pk=source_id) StagingFile = create_staging_file_class(request, staging_folder.folder_path) transformations, errors = SourceTransformation.transformations.get_for_object_as_list(staging_folder) @@ -334,7 +372,7 @@ def staging_file_preview(request, source_type, source_id, staging_file_id): def staging_file_thumbnail(request, source_id, staging_file_id): - check_permissions(request.user, [PERMISSION_DOCUMENT_CREATE]) + Permission.objects.check_permissions(request.user, [PERMISSION_DOCUMENT_CREATE]) staging_folder = get_object_or_404(StagingFolder, pk=source_id) StagingFile = create_staging_file_class(request, staging_folder.folder_path, source=staging_folder) transformations, errors = SourceTransformation.transformations.get_for_object_as_list(staging_folder) @@ -353,7 +391,7 @@ def staging_file_thumbnail(request, source_id, staging_file_id): def staging_file_delete(request, source_type, source_id, staging_file_id): - check_permissions(request.user, [PERMISSION_DOCUMENT_CREATE]) + Permission.objects.check_permissions(request.user, [PERMISSION_DOCUMENT_CREATE]) staging_folder = get_object_or_404(StagingFolder, pk=source_id) StagingFile = create_staging_file_class(request, staging_folder.folder_path) @@ -386,8 +424,9 @@ def staging_file_delete(request, source_type, source_id, staging_file_id): }, context_instance=RequestContext(request)) +# Setup views def setup_source_list(request, source_type): - check_permissions(request.user, [PERMISSION_SOURCES_SETUP_VIEW]) + Permission.objects.check_permissions(request.user, [PERMISSION_SOURCES_SETUP_VIEW]) if source_type == SOURCE_CHOICE_WEB_FORM: cls = WebForm @@ -405,12 +444,12 @@ def setup_source_list(request, source_type): } return render_to_response('generic_list.html', context, - context_instance=RequestContext(request)) + context_instance=RequestContext(request)) def setup_source_edit(request, source_type, source_id): - check_permissions(request.user, [PERMISSION_SOURCES_SETUP_EDIT]) - + Permission.objects.check_permissions(request.user, [PERMISSION_SOURCES_SETUP_EDIT]) + if source_type == SOURCE_CHOICE_WEB_FORM: cls = WebForm form_class = WebFormSetupForm @@ -420,7 +459,7 @@ def setup_source_edit(request, source_type, source_id): elif source_type == SOURCE_CHOICE_WATCH: cls = WatchFolder form_class = WatchFolderSetupForm - + source = get_object_or_404(cls, pk=source_id) next = request.POST.get('next', request.GET.get('next', request.META.get('HTTP_REFERER', '/'))) @@ -449,7 +488,7 @@ def setup_source_edit(request, source_type, source_id): def setup_source_delete(request, source_type, source_id): - check_permissions(request.user, [PERMISSION_SOURCES_SETUP_DELETE]) + Permission.objects.check_permissions(request.user, [PERMISSION_SOURCES_SETUP_DELETE]) if source_type == SOURCE_CHOICE_WEB_FORM: cls = WebForm form_icon = u'application_form_delete.png' @@ -462,12 +501,12 @@ def setup_source_delete(request, source_type, source_id): cls = WatchFolder form_icon = u'folder_delete.png' redirect_view = 'setup_watch_folder_list' - + redirect_view = reverse('setup_source_list', args=[source_type]) previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', redirect_view))) source = get_object_or_404(cls, pk=source_id) - + if request.method == 'POST': try: source.delete() @@ -495,8 +534,8 @@ def setup_source_delete(request, source_type, source_id): def setup_source_create(request, source_type): - check_permissions(request.user, [PERMISSION_SOURCES_SETUP_CREATE]) - + Permission.objects.check_permissions(request.user, [PERMISSION_SOURCES_SETUP_CREATE]) + if source_type == SOURCE_CHOICE_WEB_FORM: cls = WebForm form_class = WebFormSetupForm @@ -506,7 +545,7 @@ def setup_source_create(request, source_type): elif source_type == SOURCE_CHOICE_WATCH: cls = WatchFolder form_class = WatchFolderSetupForm - + if request.method == 'POST': form = form_class(data=request.POST) if form.is_valid(): @@ -529,8 +568,8 @@ def setup_source_create(request, source_type): def setup_source_transformation_list(request, source_type, source_id): - check_permissions(request.user, [PERMISSION_SOURCES_SETUP_EDIT]) - + Permission.objects.check_permissions(request.user, [PERMISSION_SOURCES_SETUP_EDIT]) + if source_type == SOURCE_CHOICE_WEB_FORM: cls = WebForm elif source_type == SOURCE_CHOICE_STAGING: @@ -557,12 +596,12 @@ def setup_source_transformation_list(request, source_type, source_id): } return render_to_response('generic_list.html', context, - context_instance=RequestContext(request)) + context_instance=RequestContext(request)) def setup_source_transformation_edit(request, transformation_id): - check_permissions(request.user, [PERMISSION_SOURCES_SETUP_EDIT]) - + Permission.objects.check_permissions(request.user, [PERMISSION_SOURCES_SETUP_EDIT]) + source_transformation = get_object_or_404(SourceTransformation, pk=transformation_id) redirect_view = reverse('setup_source_transformation_list', args=[source_transformation.content_object.source_type, source_transformation.content_object.pk]) next = request.POST.get('next', request.GET.get('next', request.META.get('HTTP_REFERER', redirect_view))) @@ -590,11 +629,11 @@ def setup_source_transformation_edit(request, transformation_id): ], 'next': next, }, - context_instance=RequestContext(request)) + context_instance=RequestContext(request)) def setup_source_transformation_delete(request, transformation_id): - check_permissions(request.user, [PERMISSION_SOURCES_SETUP_EDIT]) + Permission.objects.check_permissions(request.user, [PERMISSION_SOURCES_SETUP_EDIT]) source_transformation = get_object_or_404(SourceTransformation, pk=transformation_id) redirect_view = reverse('setup_source_transformation_list', args=[source_transformation.content_object.source_type, source_transformation.content_object.pk]) @@ -617,18 +656,18 @@ def setup_source_transformation_delete(request, transformation_id): 'navigation_object_list': [ {'object': 'source', 'name': _(u'source')}, {'object': 'transformation', 'name': _(u'transformation')} - ], + ], 'title': _(u'Are you sure you wish to delete source transformation "%(transformation)s"') % { 'transformation': source_transformation.get_transformation_display(), }, 'previous': previous, 'form_icon': u'shape_square_delete.png', }, - context_instance=RequestContext(request)) + context_instance=RequestContext(request)) def setup_source_transformation_create(request, source_type, source_id): - check_permissions(request.user, [PERMISSION_SOURCES_SETUP_EDIT]) + Permission.objects.check_permissions(request.user, [PERMISSION_SOURCES_SETUP_EDIT]) if source_type == SOURCE_CHOICE_WEB_FORM: cls = WebForm @@ -636,11 +675,11 @@ def setup_source_transformation_create(request, source_type, source_id): cls = StagingFolder elif source_type == SOURCE_CHOICE_WATCH: cls = WatchFolder - + source = get_object_or_404(cls, pk=source_id) - + redirect_view = reverse('setup_source_transformation_list', args=[source.source_type, source.pk]) - + if request.method == 'POST': form = SourceTransformationForm_create(request.POST) if form.is_valid(): @@ -654,7 +693,7 @@ def setup_source_transformation_create(request, source_type, source_id): messages.error(request, _(u'Error creating source transformation; %s') % e) else: form = SourceTransformationForm_create() - + return render_to_response('generic_form.html', { 'form': form, 'source': source, diff --git a/apps/sources/widgets.py b/apps/sources/widgets.py index ae5bf8e1bc..f55223290a 100644 --- a/apps/sources/widgets.py +++ b/apps/sources/widgets.py @@ -28,7 +28,7 @@ class FamFamRadioSelect(forms.widgets.RadioSelect): def staging_file_thumbnail(staging_file): try: staging_file.get_valid_image() - template = u'%(string)s' + template = u'%(string)s' except: template = u'%(string)s' @@ -36,5 +36,6 @@ def staging_file_thumbnail(staging_file): 'url': reverse('staging_file_preview', args=[staging_file.source.source_type, staging_file.source.pk, staging_file.id]), 'thumbnail': reverse('staging_file_thumbnail', args=[staging_file.source.pk, staging_file.id]), 'static_url': settings.STATIC_URL, - 'string': _(u'thumbnail') + 'string': _(u'thumbnail'), + 'filename': staging_file.filename }) diff --git a/apps/storage/conf/settings.py b/apps/storage/conf/settings.py index edd332734a..a410692e9e 100644 --- a/apps/storage/conf/settings.py +++ b/apps/storage/conf/settings.py @@ -1,5 +1,8 @@ """Configuration options for the storage app""" +import os + from django.utils.translation import ugettext_lazy as _ +from django.conf import settings from smart_settings.api import register_settings @@ -10,6 +13,6 @@ register_settings( {'name': u'GRIDFS_HOST', 'global_name': u'STORAGE_GRIDFS_HOST', 'default': u'localhost'}, {'name': u'GRIDFS_PORT', 'global_name': u'STORAGE_GRIDFS_PORT', 'default': 27017}, {'name': u'GRIDFS_DATABASE_NAME', 'global_name': u'STORAGE_GRIDFS_DATABASE_NAME', 'default': u'document_storage'}, - {'name': u'FILESTORAGE_LOCATION', 'global_name': u'STORAGE_FILESTORAGE_LOCATION', 'default': u'document_storage', 'exists': True}, + {'name': u'FILESTORAGE_LOCATION', 'global_name': u'STORAGE_FILESTORAGE_LOCATION', 'default': os.path.join(settings.PROJECT_ROOT, u'document_storage'), 'exists': True}, ] ) diff --git a/apps/storage/tests.py b/apps/storage/tests.py deleted file mode 100644 index 2247054b35..0000000000 --- a/apps/storage/tests.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -This file demonstrates two different styles of tests (one doctest and one -unittest). These will both pass when you run "manage.py test". - -Replace these 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.failUnlessEqual(1 + 1, 2) - -__test__ = {"doctest": """ -Another way to test that 1 + 1 is equal to 2. - ->>> 1 + 1 == 2 -True -"""} - diff --git a/apps/tags/__init__.py b/apps/tags/__init__.py index 4b349e012e..2bfcb2f508 100644 --- a/apps/tags/__init__.py +++ b/apps/tags/__init__.py @@ -1,63 +1,69 @@ +from __future__ import absolute_import + from django.utils.translation import ugettext_lazy as _ -from navigation.api import register_links, register_top_menu, \ - register_model_list_columns, register_multi_item_links -from permissions.api import register_permission, set_namespace_title +from navigation.api import (register_links, register_top_menu, + register_model_list_columns, register_multi_item_links) from common.utils import encapsulate from documents.models import Document +from acls.api import class_permissions +from acls.permissions import ACLS_VIEW_ACL from taggit.models import Tag +from taggit.managers import TaggableManager -from tags.widgets import tag_color_block - -PERMISSION_TAG_CREATE = {'namespace': 'tags', 'name': 'tag_create', 'label': _(u'Create new tags')} -PERMISSION_TAG_ATTACH = {'namespace': 'tags', 'name': 'tag_attach', 'label': _(u'Attach exising tags')} -PERMISSION_TAG_REMOVE = {'namespace': 'tags', 'name': 'tag_remove', 'label': _(u'Remove tags from documents')} -PERMISSION_TAG_DELETE = {'namespace': 'tags', 'name': 'tag_delete', 'label': _(u'Delete global tags')} -PERMISSION_TAG_EDIT = {'namespace': 'tags', 'name': 'tag_edit', 'label': _(u'Edit global tags')} -PERMISSION_TAG_VIEW = {'namespace': 'tags', 'name': 'tag_view', 'label': _(u'View a document\'s tags')} - -set_namespace_title('tags', _(u'Tags')) -register_permission(PERMISSION_TAG_CREATE) -register_permission(PERMISSION_TAG_ATTACH) -register_permission(PERMISSION_TAG_REMOVE) -register_permission(PERMISSION_TAG_DELETE) -register_permission(PERMISSION_TAG_EDIT) -register_permission(PERMISSION_TAG_VIEW) +from .widgets import (get_tags_inline_widget_simple, single_tag_widget) +from .permissions import (PERMISSION_TAG_CREATE, PERMISSION_TAG_ATTACH, + PERMISSION_TAG_REMOVE, PERMISSION_TAG_DELETE, PERMISSION_TAG_EDIT, + PERMISSION_TAG_VIEW) tag_list = {'text': _(u'tag list'), 'view': 'tag_list', 'famfam': 'tag_blue'} -tag_create = {'text': _(u'create new tag'), 'view': 'tag_create', 'famfam': 'tag_blue_add'} -tag_add_attach = {'text': _(u'attach tag'), 'view': 'tag_add_attach', 'args': 'object.pk', 'famfam': 'tag_blue_add', 'permission': [PERMISSION_TAG_CREATE, PERMISSION_TAG_ATTACH]} +tag_create = {'text': _(u'create new tag'), 'view': 'tag_create', 'famfam': 'tag_blue_add', 'permissions': [PERMISSION_TAG_CREATE]} +tag_attach = {'text': _(u'attach tag'), 'view': 'tag_attach', 'args': 'object.pk', 'famfam': 'tag_blue_add', 'permissions': [PERMISSION_TAG_ATTACH]} tag_document_remove = {'text': _(u'remove'), 'view': 'tag_remove', 'args': ['object.id', 'document.id'], 'famfam': 'tag_blue_delete', 'permissions': [PERMISSION_TAG_REMOVE]} tag_document_remove_multiple = {'text': _(u'remove'), 'view': 'tag_multiple_remove', 'args': 'document.id', 'famfam': 'tag_blue_delete', 'permissions': [PERMISSION_TAG_REMOVE]} -tag_document_list = {'text': _(u'tags'), 'view': 'document_tags', 'args': 'object.pk', 'famfam': 'tag_blue', 'permissions': [PERMISSION_TAG_REMOVE], 'children_view_regex': ['tag']} +tag_document_list = {'text': _(u'tags'), 'view': 'document_tags', 'args': 'object.pk', 'famfam': 'tag_blue', 'permissions': [PERMISSION_TAG_REMOVE, PERMISSION_TAG_ATTACH], 'children_view_regex': ['tag']} tag_delete = {'text': _(u'delete'), 'view': 'tag_delete', 'args': 'object.id', 'famfam': 'tag_blue_delete', 'permissions': [PERMISSION_TAG_DELETE]} tag_edit = {'text': _(u'edit'), 'view': 'tag_edit', 'args': 'object.id', 'famfam': 'tag_blue_edit', 'permissions': [PERMISSION_TAG_EDIT]} tag_tagged_item_list = {'text': _(u'tagged documents'), 'view': 'tag_tagged_item_list', 'args': 'object.id', 'famfam': 'page'} tag_multiple_delete = {'text': _(u'delete'), 'view': 'tag_multiple_delete', 'famfam': 'tag_blue_delete', 'permissions': [PERMISSION_TAG_DELETE]} +tag_acl_list = {'text': _(u'ACLs'), 'view': 'tag_acl_list', 'args': 'object.pk', 'famfam': 'lock', 'permissions': [ACLS_VIEW_ACL]} register_model_list_columns(Tag, [ { - 'name': _(u'color'), - 'attribute': encapsulate(lambda x: tag_color_block(x)) + 'name': _(u'preview'), + 'attribute': encapsulate(lambda x: single_tag_widget(x)) }, { - 'name': _(u'color name'), - 'attribute': encapsulate(lambda x: x.tagproperties_set.get().get_color_display()), + 'name': _(u'tagged items'), + 'attribute': encapsulate(lambda x: x.taggit_taggeditem_items.count()) } ]) -register_links(Tag, [tag_tagged_item_list, tag_edit, tag_delete]) +register_model_list_columns(Document, [ + {'name':_(u'tags'), 'attribute': + encapsulate(lambda x: get_tags_inline_widget_simple(x)) + }, +]) +register_links(Tag, [tag_tagged_item_list, tag_edit, tag_delete, tag_acl_list]) register_multi_item_links(['tag_list'], [tag_multiple_delete]) - -register_links(['tag_list', 'tag_delete', 'tag_edit', 'tag_tagged_item_list', 'tag_multiple_delete', 'tag_create'], [tag_list, tag_create], menu_name='secondary_menu') - -#register_sidebar_template(['document_tags'], 'tags_sidebar_template.html') - -register_top_menu('tags', link={'text': _(u'tags'), 'view': 'tag_list', 'famfam': 'tag_blue'}, children_path_regex=[r'^tags/[^d]']) +register_links([Tag, 'tag_list', 'tag_create'], [tag_list, tag_create], menu_name='secondary_menu') +register_top_menu('tags', link={'text': _(u'tags'), 'view': 'tag_list', 'famfam': 'tag_blue'}, children_view_regex=[r'^tag_(list|create|delete|edit|tagged|acl)']) register_links(Document, [tag_document_list], menu_name='form_header') -register_links(['document_tags', 'tag_add_attach', 'tag_remove', 'tag_multiple_remove'], [tag_add_attach], menu_name='sidebar') - +register_links(['document_tags', 'tag_remove', 'tag_multiple_remove', 'tag_attach'], [tag_attach], menu_name='sidebar') register_multi_item_links(['document_tags'], [tag_document_remove_multiple]) + +class_permissions(Document, [ + PERMISSION_TAG_ATTACH, + PERMISSION_TAG_REMOVE, +]) + +class_permissions(Tag, [ + PERMISSION_TAG_DELETE, + PERMISSION_TAG_EDIT, + PERMISSION_TAG_VIEW, +]) + +Document.add_to_class('tags', TaggableManager()) diff --git a/apps/tags/admin.py b/apps/tags/admin.py index 495fc03066..009033002f 100644 --- a/apps/tags/admin.py +++ b/apps/tags/admin.py @@ -1,6 +1,7 @@ +from __future__ import absolute_import + from django.contrib import admin -from tags.models import TagProperties - +from .models import TagProperties admin.site.register(TagProperties) diff --git a/apps/tags/forms.py b/apps/tags/forms.py index 046809c7c8..b10ac68679 100644 --- a/apps/tags/forms.py +++ b/apps/tags/forms.py @@ -1,19 +1,20 @@ +from __future__ import absolute_import + +import logging + from django import forms from django.utils.translation import ugettext_lazy as _ +from django.core.exceptions import PermissionDenied from taggit.models import Tag -from models import COLOR_CHOICES +from acls.models import AccessEntry +from permissions.models import Permission +from .models import COLOR_CHOICES +from .permissions import PERMISSION_TAG_VIEW -class AddTagForm(forms.Form): - """ - Form to be displayed in the sidebar of a document and allow adding - new or existing tags - """ - new_tag = forms.CharField(required=False, label=_(u'New tag')) - color = forms.ChoiceField(choices=COLOR_CHOICES, required=False, label=_(u'Color')) - existing_tags = forms.ModelChoiceField(required=False, queryset=Tag.objects.all(), label=_(u'Existing tags')) +logger = logging.getLogger(__name__) class TagForm(forms.Form): @@ -22,3 +23,20 @@ class TagForm(forms.Form): """ name = forms.CharField(label=_(u'Name')) color = forms.ChoiceField(choices=COLOR_CHOICES, label=_(u'Color')) + + +class TagListForm(forms.Form): + def __init__(self, *args, **kwargs): + user = kwargs.pop('user', None) + logger.debug('user: %s' % user) + super(TagListForm, self).__init__(*args, **kwargs) + + queryset = Tag.objects.all() + try: + Permission.objects.check_permissions(user, [PERMISSION_TAG_VIEW]) + except PermissionDenied: + queryset = AccessEntry.objects.filter_objects_by_access(PERMISSION_TAG_VIEW, user, queryset) + + self.fields['tag'] = forms.ModelChoiceField( + queryset=queryset, + label=_(u'Tags')) diff --git a/apps/tags/literals.py b/apps/tags/literals.py new file mode 100644 index 0000000000..4053c3273a --- /dev/null +++ b/apps/tags/literals.py @@ -0,0 +1,41 @@ +from __future__ import absolute_import + +from django.utils.translation import ugettext_lazy as _ + + +COLOR_RED = u'red' +COLOR_BLUE = u'blu' +COLOR_MAGENTA = u'mag' +COLOR_CYAN = u'cya' +COLOR_YELLOW = u'yel' +COLOR_GREENYELLOW = u'gry' +COLOR_CORAL = u'crl' +COLOR_KHAKI = u'kki' +COLOR_LIGHTGREY = u'lig' +COLOR_ORANGE = u'org' + +COLOR_CHOICES = ( + (COLOR_BLUE, _(u'Blue')), + (COLOR_CYAN, _(u'Cyan')), + (COLOR_CORAL, _(u'Coral')), + (COLOR_GREENYELLOW, _(u'Green-Yellow')), + (COLOR_KHAKI, _(u'Khaki')), + (COLOR_LIGHTGREY, _(u'LightGrey')), + (COLOR_MAGENTA, _(u'Magenta')), + (COLOR_RED, _(u'Red')), + (COLOR_ORANGE, _(u'Orange')), + (COLOR_YELLOW, _(u'Yellow')) +) + +COLOR_CODES = ( + (COLOR_RED, u'red'), + (COLOR_BLUE, u'blue'), + (COLOR_MAGENTA, u'magenta'), + (COLOR_CYAN, u'cyan'), + (COLOR_YELLOW, u'yellow'), + (COLOR_GREENYELLOW, u'greenyellow '), + (COLOR_CORAL, u'coral'), + (COLOR_KHAKI, u'khaki'), + (COLOR_ORANGE, u'orange'), + (COLOR_LIGHTGREY, u'lightgrey'), +) diff --git a/apps/tags/locale/en/LC_MESSAGES/django.po b/apps/tags/locale/en/LC_MESSAGES/django.po index 3a71fb37af..c80ab3348f 100644 --- a/apps/tags/locale/en/LC_MESSAGES/django.po +++ b/apps/tags/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,245 +17,227 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: __init__.py:13 -msgid "Create new tags" -msgstr "" - -#: __init__.py:14 -msgid "Attach exising tags" -msgstr "" - -#: __init__.py:15 -msgid "Remove tags from documents" -msgstr "" - -#: __init__.py:16 -msgid "Delete global tags" -msgstr "" - -#: __init__.py:17 -msgid "Edit global tags" -msgstr "" - -#: __init__.py:18 -msgid "View a document's tags" -msgstr "" - #: __init__.py:20 -msgid "Tags" -msgstr "" - -#: __init__.py:28 msgid "tag list" msgstr "" -#: __init__.py:29 +#: __init__.py:21 msgid "create new tag" msgstr "" -#: __init__.py:30 +#: __init__.py:22 msgid "attach tag" msgstr "" -#: __init__.py:31 __init__.py:32 +#: __init__.py:23 __init__.py:24 msgid "remove" msgstr "" -#: __init__.py:33 __init__.py:58 utils.py:14 views.py:144 +#: __init__.py:25 __init__.py:44 __init__.py:52 views.py:98 msgid "tags" msgstr "" -#: __init__.py:34 __init__.py:37 +#: __init__.py:26 __init__.py:29 msgid "delete" msgstr "" -#: __init__.py:35 +#: __init__.py:27 msgid "edit" msgstr "" -#: __init__.py:36 +#: __init__.py:28 msgid "tagged documents" msgstr "" -#: __init__.py:41 models.py:46 -msgid "color" +#: __init__.py:30 +msgid "ACLs" msgstr "" -#: __init__.py:45 -msgid "color name" +#: __init__.py:34 +msgid "preview" msgstr "" -#: forms.py:14 -msgid "New tag" +#: __init__.py:38 +msgid "tagged items" msgstr "" -#: forms.py:15 forms.py:24 -msgid "Color" -msgstr "" - -#: forms.py:16 -msgid "Existing tags" -msgstr "" - -#: forms.py:23 +#: forms.py:24 msgid "Name" msgstr "" -#: models.py:18 +#: forms.py:25 +msgid "Color" +msgstr "" + +#: forms.py:42 permissions.py:7 +msgid "Tags" +msgstr "" + +#: literals.py:18 msgid "Blue" msgstr "" -#: models.py:19 +#: literals.py:19 msgid "Cyan" msgstr "" -#: models.py:20 +#: literals.py:20 msgid "Coral" msgstr "" -#: models.py:21 +#: literals.py:21 msgid "Green-Yellow" msgstr "" -#: models.py:22 +#: literals.py:22 msgid "Khaki" msgstr "" -#: models.py:23 +#: literals.py:23 msgid "LightGrey" msgstr "" -#: models.py:24 +#: literals.py:24 msgid "Magenta" msgstr "" -#: models.py:25 +#: literals.py:25 msgid "Red" msgstr "" -#: models.py:26 +#: literals.py:26 msgid "Orange" msgstr "" -#: models.py:27 +#: literals.py:27 msgid "Yellow" msgstr "" -#: models.py:45 views.py:185 views.py:233 views.py:248 +#: models.py:12 views.py:154 views.py:206 views.py:220 msgid "tag" msgstr "" -#: models.py:49 +#: models.py:13 +msgid "color" +msgstr "" + +#: models.py:16 msgid "tag properties" msgstr "" -#: models.py:50 +#: models.py:17 msgid "tags properties" msgstr "" -#: views.py:33 +#: permissions.py:9 +msgid "Create new tags" +msgstr "" + +#: permissions.py:10 +msgid "Delete tags" +msgstr "" + +#: permissions.py:11 +msgid "Edit tags" +msgstr "" + +#: permissions.py:12 +msgid "View tags" +msgstr "" + +#: permissions.py:13 +msgid "Attach tags to documents" +msgstr "" + +#: permissions.py:14 +msgid "Remove tags from documents" +msgstr "" + +#: views.py:42 msgid "Tag already exists." msgstr "" -#: views.py:40 +#: views.py:50 msgid "Tag created succesfully." msgstr "" -#: views.py:46 +#: views.py:56 msgid "create tag" msgstr "" -#: views.py:73 views.py:112 -msgid "Must choose either a new tag or an existing one." -msgstr "" - -#: views.py:77 views.py:116 +#: views.py:77 #, python-format msgid "Document is already tagged as \"%s\"" msgstr "" -#: views.py:86 -#, python-format -msgid "Tag \"%s\" added successfully." -msgstr "" - -#: views.py:124 -#, python-format -msgid "Tag \"%s\" added and attached successfully." -msgstr "" - -#: views.py:126 +#: views.py:82 #, python-format msgid "Tag \"%s\" attached successfully." msgstr "" -#: views.py:133 +#: views.py:88 #, python-format msgid "attach tag to: %s" msgstr "" -#: views.py:149 -msgid "tagged items" -msgstr "" - -#: views.py:166 views.py:280 +#: views.py:130 views.py:257 msgid "Must provide at least one tag." msgstr "" -#: views.py:176 +#: views.py:145 #, python-format msgid "Tag \"%s\" deleted successfully." msgstr "" -#: views.py:178 views.py:294 +#: views.py:147 views.py:269 #, python-format msgid "Error deleting tag \"%(tag)s\": %(error)s" msgstr "" -#: views.py:193 +#: views.py:162 #, python-format msgid "Are you sure you wish to delete the tag: %s?" msgstr "" -#: views.py:194 views.py:197 +#: views.py:163 views.py:166 msgid "Will be removed from all documents." msgstr "" -#: views.py:196 +#: views.py:165 #, python-format msgid "Are you sure you wish to delete the tags: %s?" msgstr "" -#: views.py:221 +#: views.py:194 msgid "Tag updated succesfully." msgstr "" -#: views.py:230 +#: views.py:203 #, python-format msgid "edit tag: %s" msgstr "" -#: views.py:245 +#: views.py:217 #, python-format msgid "documents with the tag \"%s\"" msgstr "" -#: views.py:258 +#: views.py:236 #, python-format msgid "tags for: %s" msgstr "" -#: views.py:292 +#: views.py:267 #, python-format msgid "Tag \"%s\" removed successfully." msgstr "" -#: views.py:308 +#: views.py:283 #, python-format msgid "Are you sure you wish to remove the tag: %s?" msgstr "" -#: views.py:310 +#: views.py:285 #, python-format msgid "Are you sure you wish to remove the tags: %s?" msgstr "" diff --git a/apps/tags/locale/es/LC_MESSAGES/django.mo b/apps/tags/locale/es/LC_MESSAGES/django.mo index 36248389b6..23f977dc77 100644 Binary files a/apps/tags/locale/es/LC_MESSAGES/django.mo and b/apps/tags/locale/es/LC_MESSAGES/django.mo differ diff --git a/apps/tags/locale/es/LC_MESSAGES/django.po b/apps/tags/locale/es/LC_MESSAGES/django.po index 1bd56cabd7..2a28bd8cd7 100644 --- a/apps/tags/locale/es/LC_MESSAGES/django.po +++ b/apps/tags/locale/es/LC_MESSAGES/django.po @@ -1,263 +1,244 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: -# Roberto Rosario , 2011. +# Roberto Rosario , 2011, 2012. msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" -"PO-Revision-Date: 2011-11-04 01:05+0000\n" -"Last-Translator: rosarior \n" -"Language-Team: Spanish (Castilian) (http://www.transifex.net/projects/p/" -"mayan-edms/team/es/)\n" -"Language: es\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-12 19:27+0000\n" +"Last-Translator: Roberto Rosario \n" +"Language-Team: Spanish (Castilian) (http://www.transifex.net/projects/p/mayan-edms/team/es/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +"Language: es\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -#: __init__.py:13 -msgid "Create new tags" -msgstr "Crear nuevas etiquetas" - -#: __init__.py:14 -msgid "Attach exising tags" -msgstr "Anejar etiquetas existentes" - -#: __init__.py:15 -msgid "Remove tags from documents" -msgstr "Remover etiquetas de los documentos" - -#: __init__.py:16 -msgid "Delete global tags" -msgstr "Eliminar las etiquetas globales" - -#: __init__.py:17 -msgid "Edit global tags" -msgstr "Editar etiquetas globales" - -#: __init__.py:18 -msgid "View a document's tags" -msgstr "Ver etiquetas de un documento" - #: __init__.py:20 -msgid "Tags" -msgstr "Etiquetas" - -#: __init__.py:28 msgid "tag list" msgstr "lista de etiquetas" -#: __init__.py:29 +#: __init__.py:21 msgid "create new tag" msgstr "crear nueva etiqueta" -#: __init__.py:30 +#: __init__.py:22 msgid "attach tag" msgstr "adjuntar etiqueta" -#: __init__.py:31 __init__.py:32 +#: __init__.py:23 __init__.py:24 msgid "remove" msgstr "remover" -#: __init__.py:33 __init__.py:58 utils.py:14 views.py:144 +#: __init__.py:25 __init__.py:44 __init__.py:52 views.py:98 msgid "tags" msgstr "etiquetas" -#: __init__.py:34 __init__.py:37 +#: __init__.py:26 __init__.py:29 msgid "delete" msgstr "eliminar" -#: __init__.py:35 +#: __init__.py:27 msgid "edit" msgstr "editar" -#: __init__.py:36 +#: __init__.py:28 msgid "tagged documents" msgstr "documentos etiquetados" -#: __init__.py:41 models.py:46 -msgid "color" -msgstr "color" +#: __init__.py:30 +msgid "ACLs" +msgstr "LCAs" -#: __init__.py:45 -msgid "color name" -msgstr "nombre del color" +#: __init__.py:34 +msgid "preview" +msgstr "muestra" -#: forms.py:14 -msgid "New tag" -msgstr "Nueva etiqueta" +#: __init__.py:38 +msgid "tagged items" +msgstr "artículos etiquetados" -#: forms.py:15 forms.py:24 -msgid "Color" -msgstr "Color" - -#: forms.py:16 -msgid "Existing tags" -msgstr "Etiquetas existentes" - -#: forms.py:23 +#: forms.py:24 msgid "Name" msgstr "Nombre" -#: models.py:18 +#: forms.py:25 +msgid "Color" +msgstr "Color" + +#: forms.py:42 permissions.py:7 +msgid "Tags" +msgstr "Etiquetas" + +#: literals.py:18 msgid "Blue" msgstr "Azul" -#: models.py:19 +#: literals.py:19 msgid "Cyan" msgstr "Cian" -#: models.py:20 +#: literals.py:20 msgid "Coral" msgstr "Coral" -#: models.py:21 +#: literals.py:21 msgid "Green-Yellow" msgstr "Verde-Amarillo" -#: models.py:22 +#: literals.py:22 msgid "Khaki" msgstr "Caqui" -#: models.py:23 +#: literals.py:23 msgid "LightGrey" msgstr "Gris claro" -#: models.py:24 +#: literals.py:24 msgid "Magenta" msgstr "Magenta" -#: models.py:25 +#: literals.py:25 msgid "Red" msgstr "Rojo" -#: models.py:26 +#: literals.py:26 msgid "Orange" msgstr "Naranja" -#: models.py:27 +#: literals.py:27 msgid "Yellow" msgstr "Amarillo" -#: models.py:45 views.py:185 views.py:233 views.py:248 +#: models.py:12 views.py:154 views.py:206 views.py:220 msgid "tag" msgstr "etiqueta" -#: models.py:49 +#: models.py:13 +msgid "color" +msgstr "color" + +#: models.py:16 msgid "tag properties" msgstr "propiedades de etiqueta" -#: models.py:50 +#: models.py:17 msgid "tags properties" msgstr "propiedades de etiquetas" -#: views.py:33 +#: permissions.py:9 +msgid "Create new tags" +msgstr "Crear nuevas etiquetas" + +#: permissions.py:10 +msgid "Delete tags" +msgstr "Borrar etiquetas" + +#: permissions.py:11 +msgid "Edit tags" +msgstr "Editar etiquetas" + +#: permissions.py:12 +msgid "View tags" +msgstr "Ver etiquetas" + +#: permissions.py:13 +msgid "Attach tags to documents" +msgstr "Anejar etiquetas a documentos" + +#: permissions.py:14 +msgid "Remove tags from documents" +msgstr "Remover etiquetas de los documentos" + +#: views.py:42 msgid "Tag already exists." msgstr "Etiqueta ya existe." -#: views.py:40 +#: views.py:50 msgid "Tag created succesfully." msgstr "Etiqueta creada exitosamente." -#: views.py:46 +#: views.py:56 msgid "create tag" msgstr "crear etiquetas" -#: views.py:73 views.py:112 -msgid "Must choose either a new tag or an existing one." -msgstr "Debe elegir una etiqueta nueva o una ya existente." - -#: views.py:77 views.py:116 +#: views.py:77 #, python-format msgid "Document is already tagged as \"%s\"" msgstr "Documento ya está etiquetado como \"%s\"" -#: views.py:86 -#, python-format -msgid "Tag \"%s\" added successfully." -msgstr "Etiqueta \"%s\", agregada exitosamente." - -#: views.py:124 -#, python-format -msgid "Tag \"%s\" added and attached successfully." -msgstr "Etiqueta \"%s\" agregada y se adjuntada exitosamente." - -#: views.py:126 +#: views.py:82 #, python-format msgid "Tag \"%s\" attached successfully." msgstr "Etiqueta \"%s\" adjuntada exitosamente." -#: views.py:133 +#: views.py:88 #, python-format msgid "attach tag to: %s" msgstr "adjuntar etiqueta a: %s" -#: views.py:149 -msgid "tagged items" -msgstr "artículos etiquetados" - -#: views.py:166 views.py:280 +#: views.py:130 views.py:257 msgid "Must provide at least one tag." msgstr "Debe proveer al menos una etiqueta." -#: views.py:176 +#: views.py:145 #, python-format msgid "Tag \"%s\" deleted successfully." msgstr "Etiqueta \"%s\" borrada exitosamente." -#: views.py:178 views.py:294 +#: views.py:147 views.py:269 #, python-format msgid "Error deleting tag \"%(tag)s\": %(error)s" msgstr "Error al eliminar la etiqueta \"%(tag)s\": %(error)s" -#: views.py:193 +#: views.py:162 #, python-format msgid "Are you sure you wish to delete the tag: %s?" msgstr "¿Está seguro que desea eliminar la etiqueta: %s?" -#: views.py:194 views.py:197 +#: views.py:163 views.py:166 msgid "Will be removed from all documents." msgstr "Se removerá de todos los documentos." -#: views.py:196 +#: views.py:165 #, python-format msgid "Are you sure you wish to delete the tags: %s?" msgstr "¿Está seguro que desea eliminar de las etiquetas: %s?" -#: views.py:221 +#: views.py:194 msgid "Tag updated succesfully." msgstr "Etiqueta actualizada exitosamente." -#: views.py:230 +#: views.py:203 #, python-format msgid "edit tag: %s" msgstr "editar la etiqueta: %s" -#: views.py:245 +#: views.py:217 #, python-format msgid "documents with the tag \"%s\"" msgstr "documentos con la etiqueta \"%s\"" -#: views.py:258 +#: views.py:236 #, python-format msgid "tags for: %s" msgstr "etiquetas para: %s" -#: views.py:292 +#: views.py:267 #, python-format msgid "Tag \"%s\" removed successfully." msgstr "Etiqueta \"%s\" removida exitosamente." -#: views.py:308 +#: views.py:283 #, python-format msgid "Are you sure you wish to remove the tag: %s?" msgstr "¿Está seguro que desea eliminar la etiqueta: %s?" -#: views.py:310 +#: views.py:285 #, python-format msgid "Are you sure you wish to remove the tags: %s?" msgstr "¿Está seguro que desea eliminar de las etiquetas: %s?" @@ -265,3 +246,5 @@ msgstr "¿Está seguro que desea eliminar de las etiquetas: %s?" #: templatetags/tags_tags.py:17 msgid "Add tag to document" msgstr "Agregar etiqueta al documento" + + diff --git a/apps/tags/locale/it/LC_MESSAGES/django.mo b/apps/tags/locale/it/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..053f74c273 Binary files /dev/null and b/apps/tags/locale/it/LC_MESSAGES/django.mo differ diff --git a/apps/tags/locale/it/LC_MESSAGES/django.po b/apps/tags/locale/it/LC_MESSAGES/django.po new file mode 100644 index 0000000000..34e4aceea0 --- /dev/null +++ b/apps/tags/locale/it/LC_MESSAGES/django.po @@ -0,0 +1,251 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# , 2011. +# Roberto Rosario , 2011. +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-12 19:23+0000\n" +"Last-Translator: Roberto Rosario \n" +"Language-Team: Italian (http://www.transifex.net/projects/p/mayan-edms/team/it/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: it\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" + +#: __init__.py:20 +msgid "tag list" +msgstr "lista delle etichette" + +#: __init__.py:21 +msgid "create new tag" +msgstr "crea una nuova etichetta" + +#: __init__.py:22 +msgid "attach tag" +msgstr "allega un'etichetta" + +#: __init__.py:23 __init__.py:24 +msgid "remove" +msgstr "rimuovi" + +#: __init__.py:25 __init__.py:44 __init__.py:52 views.py:98 +msgid "tags" +msgstr "etichette" + +#: __init__.py:26 __init__.py:29 +msgid "delete" +msgstr "cancella" + +#: __init__.py:27 +msgid "edit" +msgstr "modifica" + +#: __init__.py:28 +msgid "tagged documents" +msgstr "documenti etichettati" + +#: __init__.py:30 +msgid "ACLs" +msgstr "" + +#: __init__.py:34 +msgid "preview" +msgstr "" + +#: __init__.py:38 +msgid "tagged items" +msgstr "articoli etichettati." + +#: forms.py:24 +msgid "Name" +msgstr "Nome" + +#: forms.py:25 +msgid "Color" +msgstr "Colori" + +#: forms.py:42 permissions.py:7 +msgid "Tags" +msgstr "Etichette" + +#: literals.py:18 +msgid "Blue" +msgstr "Blue" + +#: literals.py:19 +msgid "Cyan" +msgstr "Ciaono" + +#: literals.py:20 +msgid "Coral" +msgstr "Corallo" + +#: literals.py:21 +msgid "Green-Yellow" +msgstr "Verdognolo" + +#: literals.py:22 +msgid "Khaki" +msgstr "Khaki" + +#: literals.py:23 +msgid "LightGrey" +msgstr "Grigio chiaro" + +#: literals.py:24 +msgid "Magenta" +msgstr "Magenta" + +#: literals.py:25 +msgid "Red" +msgstr "Rosso" + +#: literals.py:26 +msgid "Orange" +msgstr "Arancione" + +#: literals.py:27 +msgid "Yellow" +msgstr "Giallo" + +#: models.py:12 views.py:154 views.py:206 views.py:220 +msgid "tag" +msgstr "etichetta" + +#: models.py:13 +msgid "color" +msgstr "colore" + +#: models.py:16 +msgid "tag properties" +msgstr "proprità dell'etichetta" + +#: models.py:17 +msgid "tags properties" +msgstr "proprietà delle etichette" + +#: permissions.py:9 +msgid "Create new tags" +msgstr "Crea un nuova etichetta" + +#: permissions.py:10 +msgid "Delete tags" +msgstr "" + +#: permissions.py:11 +msgid "Edit tags" +msgstr "" + +#: permissions.py:12 +msgid "View tags" +msgstr "" + +#: permissions.py:13 +msgid "Attach tags to documents" +msgstr "" + +#: permissions.py:14 +msgid "Remove tags from documents" +msgstr "Rimuovi etichetta dal documento" + +#: views.py:42 +msgid "Tag already exists." +msgstr "L'etichetta già esiste" + +#: views.py:50 +msgid "Tag created succesfully." +msgstr "Etichetta creata con successo" + +#: views.py:56 +msgid "create tag" +msgstr "crea etichetta" + +#: views.py:77 +#, python-format +msgid "Document is already tagged as \"%s\"" +msgstr "Il documento è giià stato etichettato come \"%s\"." + +#: views.py:82 +#, python-format +msgid "Tag \"%s\" attached successfully." +msgstr "Eticetta \"%s\" allegata con successo." + +#: views.py:88 +#, python-format +msgid "attach tag to: %s" +msgstr "allega etichetta a:%s" + +#: views.py:130 views.py:257 +msgid "Must provide at least one tag." +msgstr "Devi fornire almeno un'etichetta" + +#: views.py:145 +#, python-format +msgid "Tag \"%s\" deleted successfully." +msgstr "Etichetta \"%s\" cancellata con successo." + +#: views.py:147 views.py:269 +#, python-format +msgid "Error deleting tag \"%(tag)s\": %(error)s" +msgstr "Errore nel cancellare l'etichetta \"%(tag)s\": %(error)s" + +#: views.py:162 +#, python-format +msgid "Are you sure you wish to delete the tag: %s?" +msgstr "Sei sicuro di voler cancellare questa etichetta: %s?" + +#: views.py:163 views.py:166 +msgid "Will be removed from all documents." +msgstr "Sarà rimossa da tutti i documenti" + +#: views.py:165 +#, python-format +msgid "Are you sure you wish to delete the tags: %s?" +msgstr "Sei sicuro di voler cancellare tutte queste etichette: %s?" + +#: views.py:194 +msgid "Tag updated succesfully." +msgstr "Etichetta aggiornata con successo" + +#: views.py:203 +#, python-format +msgid "edit tag: %s" +msgstr "modifica l'etichetta:%s" + +#: views.py:217 +#, python-format +msgid "documents with the tag \"%s\"" +msgstr "documenti con l'etichetta \"%s\"" + +#: views.py:236 +#, python-format +msgid "tags for: %s" +msgstr "etichette per: %s" + +#: views.py:267 +#, python-format +msgid "Tag \"%s\" removed successfully." +msgstr "Etichetta \"%s\" rimossa con successo." + +#: views.py:283 +#, python-format +msgid "Are you sure you wish to remove the tag: %s?" +msgstr "Sei sicuro di voler rimuovere le etichetta: %s ?" + +#: views.py:285 +#, python-format +msgid "Are you sure you wish to remove the tags: %s?" +msgstr "Sei sicuro di voler rimuovere le etichette: %s ?" + +#: templatetags/tags_tags.py:17 +msgid "Add tag to document" +msgstr "Aggiungi l'etichetta al documento" + + diff --git a/apps/tags/locale/pl/LC_MESSAGES/django.mo b/apps/tags/locale/pl/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..1d19aee01f Binary files /dev/null and b/apps/tags/locale/pl/LC_MESSAGES/django.mo differ diff --git a/apps/tags/locale/pl/LC_MESSAGES/django.po b/apps/tags/locale/pl/LC_MESSAGES/django.po new file mode 100644 index 0000000000..0934ee350b --- /dev/null +++ b/apps/tags/locale/pl/LC_MESSAGES/django.po @@ -0,0 +1,248 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# , 2012. +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-20 16:03+0000\n" +"Last-Translator: mic \n" +"Language-Team: Polish (http://www.transifex.net/projects/p/mayan-edms/language/pl/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pl\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" + +#: __init__.py:20 +msgid "tag list" +msgstr "lista tagów" + +#: __init__.py:21 +msgid "create new tag" +msgstr "utworzyć nowy tag" + +#: __init__.py:22 +msgid "attach tag" +msgstr "" + +#: __init__.py:23 __init__.py:24 +msgid "remove" +msgstr "usuń" + +#: __init__.py:25 __init__.py:44 __init__.py:52 views.py:98 +msgid "tags" +msgstr "tagi" + +#: __init__.py:26 __init__.py:29 +msgid "delete" +msgstr "usunąć" + +#: __init__.py:27 +msgid "edit" +msgstr "edytuj" + +#: __init__.py:28 +msgid "tagged documents" +msgstr "" + +#: __init__.py:30 +msgid "ACLs" +msgstr "" + +#: __init__.py:34 +msgid "preview" +msgstr "" + +#: __init__.py:38 +msgid "tagged items" +msgstr "" + +#: forms.py:24 +msgid "Name" +msgstr "Nazwa" + +#: forms.py:25 +msgid "Color" +msgstr "" + +#: forms.py:42 permissions.py:7 +msgid "Tags" +msgstr "Tagi" + +#: literals.py:18 +msgid "Blue" +msgstr "Niebieski" + +#: literals.py:19 +msgid "Cyan" +msgstr "Cyan" + +#: literals.py:20 +msgid "Coral" +msgstr "Koral" + +#: literals.py:21 +msgid "Green-Yellow" +msgstr "Zielono-żółty" + +#: literals.py:22 +msgid "Khaki" +msgstr "Khaki" + +#: literals.py:23 +msgid "LightGrey" +msgstr "Jasnoszary" + +#: literals.py:24 +msgid "Magenta" +msgstr "Magenta" + +#: literals.py:25 +msgid "Red" +msgstr "Czerwony" + +#: literals.py:26 +msgid "Orange" +msgstr "Pomarańczowy" + +#: literals.py:27 +msgid "Yellow" +msgstr "Żółty" + +#: models.py:12 views.py:154 views.py:206 views.py:220 +msgid "tag" +msgstr "tag" + +#: models.py:13 +msgid "color" +msgstr "" + +#: models.py:16 +msgid "tag properties" +msgstr "" + +#: models.py:17 +msgid "tags properties" +msgstr "" + +#: permissions.py:9 +msgid "Create new tags" +msgstr "Utwórz nowe tagi" + +#: permissions.py:10 +msgid "Delete tags" +msgstr "" + +#: permissions.py:11 +msgid "Edit tags" +msgstr "" + +#: permissions.py:12 +msgid "View tags" +msgstr "" + +#: permissions.py:13 +msgid "Attach tags to documents" +msgstr "" + +#: permissions.py:14 +msgid "Remove tags from documents" +msgstr "Usuń tagi z dokumentów" + +#: views.py:42 +msgid "Tag already exists." +msgstr "" + +#: views.py:50 +msgid "Tag created succesfully." +msgstr "" + +#: views.py:56 +msgid "create tag" +msgstr "" + +#: views.py:77 +#, python-format +msgid "Document is already tagged as \"%s\"" +msgstr "" + +#: views.py:82 +#, python-format +msgid "Tag \"%s\" attached successfully." +msgstr "" + +#: views.py:88 +#, python-format +msgid "attach tag to: %s" +msgstr "" + +#: views.py:130 views.py:257 +msgid "Must provide at least one tag." +msgstr "" + +#: views.py:145 +#, python-format +msgid "Tag \"%s\" deleted successfully." +msgstr "" + +#: views.py:147 views.py:269 +#, python-format +msgid "Error deleting tag \"%(tag)s\": %(error)s" +msgstr "" + +#: views.py:162 +#, python-format +msgid "Are you sure you wish to delete the tag: %s?" +msgstr "" + +#: views.py:163 views.py:166 +msgid "Will be removed from all documents." +msgstr "" + +#: views.py:165 +#, python-format +msgid "Are you sure you wish to delete the tags: %s?" +msgstr "" + +#: views.py:194 +msgid "Tag updated succesfully." +msgstr "" + +#: views.py:203 +#, python-format +msgid "edit tag: %s" +msgstr "" + +#: views.py:217 +#, python-format +msgid "documents with the tag \"%s\"" +msgstr "" + +#: views.py:236 +#, python-format +msgid "tags for: %s" +msgstr "" + +#: views.py:267 +#, python-format +msgid "Tag \"%s\" removed successfully." +msgstr "" + +#: views.py:283 +#, python-format +msgid "Are you sure you wish to remove the tag: %s?" +msgstr "" + +#: views.py:285 +#, python-format +msgid "Are you sure you wish to remove the tags: %s?" +msgstr "" + +#: templatetags/tags_tags.py:17 +msgid "Add tag to document" +msgstr "" diff --git a/apps/tags/locale/pt/LC_MESSAGES/django.mo b/apps/tags/locale/pt/LC_MESSAGES/django.mo index db86c8cc36..cbec8673bc 100644 Binary files a/apps/tags/locale/pt/LC_MESSAGES/django.mo and b/apps/tags/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/tags/locale/pt/LC_MESSAGES/django.po b/apps/tags/locale/pt/LC_MESSAGES/django.po index bbc0936a4d..72632ba281 100644 --- a/apps/tags/locale/pt/LC_MESSAGES/django.po +++ b/apps/tags/locale/pt/LC_MESSAGES/django.po @@ -1,263 +1,245 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: # , 2011. +# Roberto Rosario , 2012. msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" -"PO-Revision-Date: 2011-11-02 01:45+0000\n" -"Last-Translator: emersonsoares \n" -"Language-Team: Portuguese (http://www.transifex.net/projects/p/mayan-edms/" -"team/pt/)\n" -"Language: pt\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-21 16:45+0000\n" +"Last-Translator: Roberto Rosario \n" +"Language-Team: Portuguese (http://www.transifex.net/projects/p/mayan-edms/language/pt/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +"Language: pt\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -#: __init__.py:13 -msgid "Create new tags" -msgstr "Criar novas etiquetas" - -#: __init__.py:14 -msgid "Attach exising tags" -msgstr "Anexar etiquetas existentes" - -#: __init__.py:15 -msgid "Remove tags from documents" -msgstr "Remover etiquetas de documentos" - -#: __init__.py:16 -msgid "Delete global tags" -msgstr "Excluir etiquetas globais" - -#: __init__.py:17 -msgid "Edit global tags" -msgstr "Editar etiquetas globais" - -#: __init__.py:18 -msgid "View a document's tags" -msgstr "Ver etiquetas de um documento" - #: __init__.py:20 -msgid "Tags" -msgstr "Etiquetas" - -#: __init__.py:28 msgid "tag list" msgstr "lista de etiquetas" -#: __init__.py:29 +#: __init__.py:21 msgid "create new tag" msgstr "criar nova etiqueta" -#: __init__.py:30 +#: __init__.py:22 msgid "attach tag" msgstr "anexar etiqueta" -#: __init__.py:31 __init__.py:32 +#: __init__.py:23 __init__.py:24 msgid "remove" msgstr "remover" -#: __init__.py:33 __init__.py:58 utils.py:14 views.py:144 +#: __init__.py:25 __init__.py:44 __init__.py:52 views.py:98 msgid "tags" msgstr "Etiquetas" -#: __init__.py:34 __init__.py:37 +#: __init__.py:26 __init__.py:29 msgid "delete" msgstr "excluir" -#: __init__.py:35 +#: __init__.py:27 msgid "edit" msgstr "editar" -#: __init__.py:36 +#: __init__.py:28 msgid "tagged documents" msgstr "documentos etiquetados" -#: __init__.py:41 models.py:46 -msgid "color" -msgstr "cor" +#: __init__.py:30 +msgid "ACLs" +msgstr "" -#: __init__.py:45 -msgid "color name" -msgstr "nome da cor" +#: __init__.py:34 +msgid "preview" +msgstr "" -#: forms.py:14 -msgid "New tag" -msgstr "Nova etiqueta" +#: __init__.py:38 +msgid "tagged items" +msgstr "itens marcados" -#: forms.py:15 forms.py:24 -msgid "Color" -msgstr "Cor" - -#: forms.py:16 -msgid "Existing tags" -msgstr "Etiquetas existentes" - -#: forms.py:23 +#: forms.py:24 msgid "Name" msgstr "Nome" -#: models.py:18 +#: forms.py:25 +msgid "Color" +msgstr "Cor" + +#: forms.py:42 permissions.py:7 +msgid "Tags" +msgstr "Etiquetas" + +#: literals.py:18 msgid "Blue" msgstr "Azul" -#: models.py:19 +#: literals.py:19 msgid "Cyan" msgstr "Ciano" -#: models.py:20 +#: literals.py:20 msgid "Coral" msgstr "Coral" -#: models.py:21 +#: literals.py:21 msgid "Green-Yellow" msgstr "Verde-Amarelo" -#: models.py:22 +#: literals.py:22 msgid "Khaki" msgstr "Caqui" -#: models.py:23 +#: literals.py:23 msgid "LightGrey" msgstr "Cinza Claro" -#: models.py:24 +#: literals.py:24 msgid "Magenta" msgstr "Magenta" -#: models.py:25 +#: literals.py:25 msgid "Red" msgstr "Vermelho" -#: models.py:26 +#: literals.py:26 msgid "Orange" msgstr "Laranja" -#: models.py:27 +#: literals.py:27 msgid "Yellow" msgstr "Amarelo" -#: models.py:45 views.py:185 views.py:233 views.py:248 +#: models.py:12 views.py:154 views.py:206 views.py:220 msgid "tag" msgstr "etiqueta" -#: models.py:49 +#: models.py:13 +msgid "color" +msgstr "cor" + +#: models.py:16 msgid "tag properties" msgstr "propriedades de etiqueta" -#: models.py:50 +#: models.py:17 msgid "tags properties" msgstr "propriedades de etiquetas" -#: views.py:33 +#: permissions.py:9 +msgid "Create new tags" +msgstr "Criar novas etiquetas" + +#: permissions.py:10 +msgid "Delete tags" +msgstr "Excluir etiquetas" + +#: permissions.py:11 +msgid "Edit tags" +msgstr "Editar as etiquetas" + +#: permissions.py:12 +msgid "View tags" +msgstr "Ver etiquetas" + +#: permissions.py:13 +msgid "Attach tags to documents" +msgstr "Anexar etiquetas para documentos" + +#: permissions.py:14 +msgid "Remove tags from documents" +msgstr "Remover etiquetas de documentos" + +#: views.py:42 msgid "Tag already exists." msgstr "Etiqueta já existe." -#: views.py:40 +#: views.py:50 msgid "Tag created succesfully." msgstr "Etiqueta criada com sucesso." -#: views.py:46 +#: views.py:56 msgid "create tag" msgstr "criar etiqueta" -#: views.py:73 views.py:112 -msgid "Must choose either a new tag or an existing one." -msgstr "Deve optar por uma nova etiqueta ou uma já existente." - -#: views.py:77 views.py:116 +#: views.py:77 #, python-format msgid "Document is already tagged as \"%s\"" msgstr "Documento já está marcado como \"%s\"" -#: views.py:86 -#, python-format -msgid "Tag \"%s\" added successfully." -msgstr "Etiqueta \"%s\" adicionada com sucesso." - -#: views.py:124 -#, python-format -msgid "Tag \"%s\" added and attached successfully." -msgstr "Etiqueta \"%s\", acrescentada e anexada com sucesso." - -#: views.py:126 +#: views.py:82 #, python-format msgid "Tag \"%s\" attached successfully." msgstr "Etiqueta \"%s\" anexada com sucesso." -#: views.py:133 +#: views.py:88 #, python-format msgid "attach tag to: %s" msgstr "anexar etiqueta a: %s" -#: views.py:149 -msgid "tagged items" -msgstr "itens marcados" - -#: views.py:166 views.py:280 +#: views.py:130 views.py:257 msgid "Must provide at least one tag." msgstr "Deve fornecer pelo menos uma etiqueta." -#: views.py:176 +#: views.py:145 #, python-format msgid "Tag \"%s\" deleted successfully." msgstr "Etiqueta \"%s\" removida com sucesso." -#: views.py:178 views.py:294 +#: views.py:147 views.py:269 #, python-format msgid "Error deleting tag \"%(tag)s\": %(error)s" msgstr "Erro ao excluir etiqueta \" %(tag)s \": %(error)s " -#: views.py:193 +#: views.py:162 #, python-format msgid "Are you sure you wish to delete the tag: %s?" msgstr "Tem certeza de que deseja excluir a etiqueta: %s?" -#: views.py:194 views.py:197 +#: views.py:163 views.py:166 msgid "Will be removed from all documents." msgstr "Será removido de todos os documentos." -#: views.py:196 +#: views.py:165 #, python-format msgid "Are you sure you wish to delete the tags: %s?" msgstr "Você tem certeza que deseja deletar as etiquetas: %s?" -#: views.py:221 +#: views.py:194 msgid "Tag updated succesfully." msgstr "Etiqueta atualizada com sucesso." -#: views.py:230 +#: views.py:203 #, python-format msgid "edit tag: %s" msgstr "editar etiqueta: %s" -#: views.py:245 +#: views.py:217 #, python-format msgid "documents with the tag \"%s\"" msgstr "documentos com a etiqueta \"%s\"" -#: views.py:258 +#: views.py:236 #, python-format msgid "tags for: %s" msgstr "etiquetas para: %s" -#: views.py:292 +#: views.py:267 #, python-format msgid "Tag \"%s\" removed successfully." msgstr "Etiqueta \"%s\" removida com sucesso." -#: views.py:308 +#: views.py:283 #, python-format msgid "Are you sure you wish to remove the tag: %s?" msgstr "Tem certeza de que deseja remover a etiqueta: %s?" -#: views.py:310 +#: views.py:285 #, python-format msgid "Are you sure you wish to remove the tags: %s?" msgstr "Tem certeza de que deseja remover as etiquetas: %s?" diff --git a/apps/tags/locale/ru/LC_MESSAGES/django.mo b/apps/tags/locale/ru/LC_MESSAGES/django.mo index 9caaeae59d..52c652c98d 100644 Binary files a/apps/tags/locale/ru/LC_MESSAGES/django.mo and b/apps/tags/locale/ru/LC_MESSAGES/django.mo differ diff --git a/apps/tags/locale/ru/LC_MESSAGES/django.po b/apps/tags/locale/ru/LC_MESSAGES/django.po index 1e057efbd3..4ac1a23f22 100644 --- a/apps/tags/locale/ru/LC_MESSAGES/django.po +++ b/apps/tags/locale/ru/LC_MESSAGES/django.po @@ -1,263 +1,244 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: +# Sergey Glita , 2012. msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" -"PO-Revision-Date: 2011-11-04 15:20+0000\n" -"Last-Translator: gsv70 \n" -"Language-Team: Russian (http://www.transifex.net/projects/p/mayan-edms/team/" -"ru/)\n" -"Language: ru\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-27 04:24+0000\n" +"Last-Translator: Sergey Glita \n" +"Language-Team: Russian (http://www.transifex.net/projects/p/mayan-edms/language/ru/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" -"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" - -#: __init__.py:13 -msgid "Create new tags" -msgstr "Создание новых меток" - -#: __init__.py:14 -msgid "Attach exising tags" -msgstr "Прикрепить существующие метки" - -#: __init__.py:15 -msgid "Remove tags from documents" -msgstr "Удаление тегов из документов" - -#: __init__.py:16 -msgid "Delete global tags" -msgstr "Удалить общие метки" - -#: __init__.py:17 -msgid "Edit global tags" -msgstr "Изменить общие метки" - -#: __init__.py:18 -msgid "View a document's tags" -msgstr "Просмотр тегов документа" +"Language: ru\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:20 -msgid "Tags" -msgstr "Метки" - -#: __init__.py:28 msgid "tag list" msgstr "список меток" -#: __init__.py:29 +#: __init__.py:21 msgid "create new tag" msgstr "создать новую метку" -#: __init__.py:30 +#: __init__.py:22 msgid "attach tag" msgstr "пометить" -#: __init__.py:31 __init__.py:32 +#: __init__.py:23 __init__.py:24 msgid "remove" msgstr "удаление" -#: __init__.py:33 __init__.py:58 utils.py:14 views.py:144 +#: __init__.py:25 __init__.py:44 __init__.py:52 views.py:98 msgid "tags" msgstr "метки" -#: __init__.py:34 __init__.py:37 +#: __init__.py:26 __init__.py:29 msgid "delete" msgstr "удалить" -#: __init__.py:35 +#: __init__.py:27 msgid "edit" msgstr "редактировать" -#: __init__.py:36 +#: __init__.py:28 msgid "tagged documents" msgstr "помеченные документы" -#: __init__.py:41 models.py:46 -msgid "color" -msgstr "цвет" +#: __init__.py:30 +msgid "ACLs" +msgstr "ACLs" -#: __init__.py:45 -msgid "color name" -msgstr "название цвета" +#: __init__.py:34 +msgid "preview" +msgstr "предварительный просмотр" -#: forms.py:14 -msgid "New tag" -msgstr "Новая метка" +#: __init__.py:38 +msgid "tagged items" +msgstr "помеченные элементы" -#: forms.py:15 forms.py:24 -msgid "Color" -msgstr "Цвет" - -#: forms.py:16 -msgid "Existing tags" -msgstr "Существующие метки" - -#: forms.py:23 +#: forms.py:24 msgid "Name" msgstr "Имя" -#: models.py:18 +#: forms.py:25 +msgid "Color" +msgstr "Цвет" + +#: forms.py:42 permissions.py:7 +msgid "Tags" +msgstr "Метки" + +#: literals.py:18 msgid "Blue" msgstr "Синий" -#: models.py:19 +#: literals.py:19 msgid "Cyan" msgstr "Голубой" -#: models.py:20 +#: literals.py:20 msgid "Coral" msgstr "Коралловый" -#: models.py:21 +#: literals.py:21 msgid "Green-Yellow" msgstr "Зелено-желтый" -#: models.py:22 +#: literals.py:22 msgid "Khaki" msgstr "Хаки" -#: models.py:23 +#: literals.py:23 msgid "LightGrey" msgstr "Светло-серый" -#: models.py:24 +#: literals.py:24 msgid "Magenta" msgstr "Пурпурный" -#: models.py:25 +#: literals.py:25 msgid "Red" msgstr "Красный" -#: models.py:26 +#: literals.py:26 msgid "Orange" msgstr "Оранжевый" -#: models.py:27 +#: literals.py:27 msgid "Yellow" msgstr "Желтый" -#: models.py:45 views.py:185 views.py:233 views.py:248 +#: models.py:12 views.py:154 views.py:206 views.py:220 msgid "tag" msgstr "метка" -#: models.py:49 +#: models.py:13 +msgid "color" +msgstr "цвет" + +#: models.py:16 msgid "tag properties" msgstr "свойства метки" -#: models.py:50 +#: models.py:17 msgid "tags properties" msgstr "свойства метки" -#: views.py:33 +#: permissions.py:9 +msgid "Create new tags" +msgstr "Создание новых меток" + +#: permissions.py:10 +msgid "Delete tags" +msgstr "Удалить метки" + +#: permissions.py:11 +msgid "Edit tags" +msgstr "Редактировать метки" + +#: permissions.py:12 +msgid "View tags" +msgstr "Посмотреть метки" + +#: permissions.py:13 +msgid "Attach tags to documents" +msgstr "Прикрепить метки к документам" + +#: permissions.py:14 +msgid "Remove tags from documents" +msgstr "Удаление тегов из документов" + +#: views.py:42 msgid "Tag already exists." msgstr "Метка уже существует." -#: views.py:40 +#: views.py:50 msgid "Tag created succesfully." msgstr "Метка создана." -#: views.py:46 +#: views.py:56 msgid "create tag" msgstr "создать метку" -#: views.py:73 views.py:112 -msgid "Must choose either a new tag or an existing one." -msgstr "Необходимо выбрать новые метки или существующие." - -#: views.py:77 views.py:116 +#: views.py:77 #, python-format msgid "Document is already tagged as \"%s\"" msgstr "Документ уже помечены как \"%s\"" -#: views.py:86 -#, python-format -msgid "Tag \"%s\" added successfully." -msgstr "Метка \"%s\" добавлена" - -#: views.py:124 -#, python-format -msgid "Tag \"%s\" added and attached successfully." -msgstr "Метка\"%s\" добавлена и прикреплена." - -#: views.py:126 +#: views.py:82 #, python-format msgid "Tag \"%s\" attached successfully." msgstr "Помечено как \"%s\"" -#: views.py:133 +#: views.py:88 #, python-format msgid "attach tag to: %s" msgstr "пометить как %s" -#: views.py:149 -msgid "tagged items" -msgstr "помеченные элементы" - -#: views.py:166 views.py:280 +#: views.py:130 views.py:257 msgid "Must provide at least one tag." msgstr "Должна быть хотя бы одна метка." -#: views.py:176 +#: views.py:145 #, python-format msgid "Tag \"%s\" deleted successfully." msgstr "Метка \"%s\"удалён." -#: views.py:178 views.py:294 +#: views.py:147 views.py:269 #, python-format msgid "Error deleting tag \"%(tag)s\": %(error)s" msgstr "Ошибка при удалении метки \"%(tag)s\": %(error)s" -#: views.py:193 +#: views.py:162 #, python-format msgid "Are you sure you wish to delete the tag: %s?" msgstr "Вы действительно хотите удалить метку: %s?" -#: views.py:194 views.py:197 +#: views.py:163 views.py:166 msgid "Will be removed from all documents." msgstr "Будет удален из всех документов." -#: views.py:196 +#: views.py:165 #, python-format msgid "Are you sure you wish to delete the tags: %s?" msgstr "Вы действительно хотите удалить метки: %s?" -#: views.py:221 +#: views.py:194 msgid "Tag updated succesfully." msgstr "Метка обновлена." -#: views.py:230 +#: views.py:203 #, python-format msgid "edit tag: %s" msgstr "редактировать метку %s" -#: views.py:245 +#: views.py:217 #, python-format msgid "documents with the tag \"%s\"" msgstr "документы с тегом \"%s\"" -#: views.py:258 +#: views.py:236 #, python-format msgid "tags for: %s" msgstr "теги для:%s" -#: views.py:292 +#: views.py:267 #, python-format msgid "Tag \"%s\" removed successfully." msgstr "Метка \"%s\" удалена." -#: views.py:308 +#: views.py:283 #, python-format msgid "Are you sure you wish to remove the tag: %s?" msgstr "Вы действительно хотите снять метку: %s?" -#: views.py:310 +#: views.py:285 #, python-format msgid "Are you sure you wish to remove the tags: %s?" msgstr "Вы действительно хотите снять метку: %s?" diff --git a/apps/tags/models.py b/apps/tags/models.py index 8c36a2cb7c..98bd405f3b 100644 --- a/apps/tags/models.py +++ b/apps/tags/models.py @@ -1,44 +1,11 @@ +from __future__ import absolute_import + from django.db import models from django.utils.translation import ugettext_lazy as _ from taggit.models import Tag -COLOR_RED = u'red' -COLOR_BLUE = u'blu' -COLOR_MAGENTA = u'mag' -COLOR_CYAN = u'cya' -COLOR_YELLOW = u'yel' -COLOR_GREENYELLOW = u'gry' -COLOR_CORAL = u'crl' -COLOR_KHAKI = u'kki' -COLOR_LIGHTGREY = u'lig' -COLOR_ORANGE = u'org' - -COLOR_CHOICES = ( - (COLOR_BLUE, _(u'Blue')), - (COLOR_CYAN, _(u'Cyan')), - (COLOR_CORAL, _(u'Coral')), - (COLOR_GREENYELLOW, _(u'Green-Yellow')), - (COLOR_KHAKI, _(u'Khaki')), - (COLOR_LIGHTGREY, _(u'LightGrey')), - (COLOR_MAGENTA, _(u'Magenta')), - (COLOR_RED, _(u'Red')), - (COLOR_ORANGE, _(u'Orange')), - (COLOR_YELLOW, _(u'Yellow')) -) - -COLOR_CODES = ( - (COLOR_RED, u'red'), - (COLOR_BLUE, u'blue'), - (COLOR_MAGENTA, u'magenta'), - (COLOR_CYAN, u'cyan'), - (COLOR_YELLOW, u'yellow'), - (COLOR_GREENYELLOW, u'greenyellow '), - (COLOR_CORAL, u'coral'), - (COLOR_KHAKI, u'khaki'), - (COLOR_ORANGE, u'orange'), - (COLOR_LIGHTGREY, u'lightgrey'), -) +from .literals import COLOR_CHOICES, COLOR_CODES class TagProperties(models.Model): diff --git a/apps/tags/permissions.py b/apps/tags/permissions.py new file mode 100644 index 0000000000..cb4c7f9ae7 --- /dev/null +++ b/apps/tags/permissions.py @@ -0,0 +1,14 @@ +from __future__ import absolute_import + +from django.utils.translation import ugettext_lazy as _ + +from permissions.models import PermissionNamespace, Permission + +tags_namespace = PermissionNamespace('tags', _(u'Tags')) + +PERMISSION_TAG_CREATE = Permission.objects.register(tags_namespace, 'tag_create', _(u'Create new tags')) +PERMISSION_TAG_DELETE = Permission.objects.register(tags_namespace, 'tag_delete', _(u'Delete tags')) +PERMISSION_TAG_EDIT = Permission.objects.register(tags_namespace, 'tag_edit', _(u'Edit tags')) +PERMISSION_TAG_VIEW = Permission.objects.register(tags_namespace, 'tag_view', _(u'View tags')) +PERMISSION_TAG_ATTACH = Permission.objects.register(tags_namespace, 'tag_attach', _(u'Attach tags to documents')) +PERMISSION_TAG_REMOVE = Permission.objects.register(tags_namespace, 'tag_remove', _(u'Remove tags from documents')) diff --git a/apps/tags/tests.py b/apps/tags/tests.py index 2247054b35..094293d35c 100644 --- a/apps/tags/tests.py +++ b/apps/tags/tests.py @@ -1,23 +1,16 @@ -""" -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 +from .literals import 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') diff --git a/apps/tags/urls.py b/apps/tags/urls.py index d649819642..14c9902303 100644 --- a/apps/tags/urls.py +++ b/apps/tags/urls.py @@ -10,7 +10,8 @@ urlpatterns = patterns('tags.views', url(r'^(?P\d+)/remove_from_document/(?P\d+)/$', 'tag_remove', (), 'tag_remove'), url(r'^multiple/remove_from_document/(?P\d+)/$', 'tag_multiple_remove', (), 'tag_multiple_remove'), - url(r'^document/(?P\d+)/add/$', 'tag_add_attach', (), 'tag_add_attach'), - url(r'^document/(?P\d+)/add/from_sidebar/$', 'tag_add_sidebar', (), 'tag_add_sidebar'), + url(r'^document/(?P\d+)/add/$', 'tag_attach', (), 'tag_attach'), url(r'^document/(?P\d+)/list/$', 'document_tags', (), 'document_tags'), + + url(r'^(?P\d+)/acl/list/$', 'tag_acl_list', (), 'tag_acl_list'), ) diff --git a/apps/tags/utils.py b/apps/tags/utils.py deleted file mode 100644 index 9496d9e88e..0000000000 --- a/apps/tags/utils.py +++ /dev/null @@ -1,19 +0,0 @@ -from django.utils.translation import ugettext_lazy as _ - -from tags import tag_document_remove, tag_tagged_item_list - - -def get_tags_subtemplate(obj): - """ - Return all the settings to render a subtemplate containing an - object's tags - """ - return { - 'name': 'generic_list_subtemplate.html', - 'context': { - 'title': _(u'tags'), - 'object_list': obj.tags.all(), - 'hide_link': True, - 'navigation_object_links': [tag_tagged_item_list, tag_document_remove], - } - } diff --git a/apps/tags/views.py b/apps/tags/views.py index 45d91c1921..a153506e02 100644 --- a/apps/tags/views.py +++ b/apps/tags/views.py @@ -1,26 +1,35 @@ +from __future__ import absolute_import + +import logging + from django.http import HttpResponseRedirect from django.shortcuts import render_to_response, get_object_or_404 from django.template import RequestContext from django.contrib import messages from django.core.urlresolvers import reverse from django.utils.translation import ugettext_lazy as _ +from django.core.exceptions import PermissionDenied -from permissions.api import check_permissions +from permissions import Permission from taggit.models import Tag from documents.models import Document from documents.views import document_list -from common.utils import encapsulate +from documents.permissions import PERMISSION_DOCUMENT_VIEW +from acls.models import AccessEntry +from acls.views import acl_list_for +from acls.utils import apply_default_acls -from tags.forms import AddTagForm, TagForm -from tags.models import TagProperties -from tags import PERMISSION_TAG_CREATE, PERMISSION_TAG_ATTACH, \ - PERMISSION_TAG_REMOVE, PERMISSION_TAG_DELETE, PERMISSION_TAG_EDIT, \ - PERMISSION_TAG_VIEW -from tags import tag_tagged_item_list as tag_tagged_item_list_link +from .forms import TagListForm, TagForm +from .models import TagProperties +from .permissions import (PERMISSION_TAG_CREATE, PERMISSION_TAG_ATTACH, + PERMISSION_TAG_REMOVE, PERMISSION_TAG_DELETE, PERMISSION_TAG_EDIT, + PERMISSION_TAG_VIEW) + +logger = logging.getLogger(__name__) def tag_create(request): - #check_permissions(request.user, [PERMISSION_TAG_EDIT]) + Permission.objects.check_permissions(request.user, [PERMISSION_TAG_CREATE]) redirect_url = reverse('tag_list') previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', redirect_url))) @@ -32,11 +41,12 @@ def tag_create(request): if tag_name in Tag.objects.values_list('name', flat=True): messages.error(request, _(u'Tag already exists.')) return HttpResponseRedirect(previous) - + tag = Tag(name=tag_name) tag.save() TagProperties(tag=tag, color=form.cleaned_data['color']).save() - + apply_default_acls(tag, request.user) + messages.success(request, _(u'Tag created succesfully.')) return HttpResponseRedirect(redirect_url) else: @@ -46,88 +56,33 @@ def tag_create(request): 'title': _(u'create tag'), 'form': form, }, - context_instance=RequestContext(request)) + context_instance=RequestContext(request)) -def tag_add_sidebar(request, document_id): +def tag_attach(request, document_id): document = get_object_or_404(Document, pk=document_id) - previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', reverse('tag_list')))) - - if request.method == 'POST': - previous = request.META.get('HTTP_REFERER', '/') - form = AddTagForm(request.POST) - if form.is_valid(): - if form.cleaned_data['new_tag']: - check_permissions(request.user, [PERMISSION_TAG_CREATE]) - tag_name = form.cleaned_data['new_tag'] - if Tag.objects.filter(name=tag_name): - is_new = False - else: - is_new = True - elif form.cleaned_data['existing_tags']: - check_permissions(request.user, [PERMISSION_TAG_ATTACH]) - tag_name = form.cleaned_data['existing_tags'] - is_new = False - else: - messages.error(request, _(u'Must choose either a new tag or an existing one.')) - return HttpResponseRedirect(previous) - - if tag_name in document.tags.values_list('name', flat=True): - messages.warning(request, _(u'Document is already tagged as "%s"') % tag_name) - return HttpResponseRedirect(previous) - - document.tags.add(tag_name) - - if is_new: - tag = Tag.objects.get(name=tag_name) - TagProperties(tag=tag, color=form.cleaned_data['color']).save() - - messages.success(request, _(u'Tag "%s" added successfully.') % tag_name) - - return HttpResponseRedirect(previous) - - -def tag_add_attach(request, document_id): - # TODO: merge with tag_add_sidebar - document = get_object_or_404(Document, pk=document_id) + try: + Permission.objects.check_permissions(request.user, [PERMISSION_TAG_ATTACH]) + except PermissionDenied: + AccessEntry.objects.check_access(PERMISSION_TAG_ATTACH, request.user, document) next = request.POST.get('next', request.GET.get('next', request.META.get('HTTP_REFERER', reverse('document_tags', args=[document.pk])))) if request.method == 'POST': - form = AddTagForm(request.POST) + form = TagListForm(request.POST, user=request.user) if form.is_valid(): - if form.cleaned_data['new_tag']: - check_permissions(request.user, [PERMISSION_TAG_CREATE]) - tag_name = form.cleaned_data['new_tag'] - if Tag.objects.filter(name=tag_name): - is_new = False - else: - is_new = True - elif form.cleaned_data['existing_tags']: - check_permissions(request.user, [PERMISSION_TAG_ATTACH]) - tag_name = form.cleaned_data['existing_tags'] - is_new = False - else: - messages.error(request, _(u'Must choose either a new tag or an existing one.')) + tag = form.cleaned_data['tag'] + if tag in document.tags.all(): + messages.warning(request, _(u'Document is already tagged as "%s"') % tag) return HttpResponseRedirect(next) - if tag_name in document.tags.values_list('name', flat=True): - messages.warning(request, _(u'Document is already tagged as "%s"') % tag_name) - return HttpResponseRedirect(next) - - document.tags.add(tag_name) - - if is_new: - tag = Tag.objects.get(name=tag_name) - TagProperties(tag=tag, color=form.cleaned_data['color']).save() - messages.success(request, _(u'Tag "%s" added and attached successfully.') % tag_name) - else: - messages.success(request, _(u'Tag "%s" attached successfully.') % tag_name) + document.tags.add(tag) + messages.success(request, _(u'Tag "%s" attached successfully.') % tag) return HttpResponseRedirect(next) else: - form = AddTagForm() + form = TagListForm(user=request.user) return render_to_response('generic_form.html', { 'title': _(u'attach tag to: %s') % document, @@ -138,23 +93,32 @@ def tag_add_attach(request, document_id): context_instance=RequestContext(request)) -def tag_list(request): - return render_to_response('generic_list.html', { - 'object_list': Tag.objects.all(), +def tag_list(request, queryset=None, extra_context=None): + context = { 'title': _(u'tags'), 'hide_link': True, 'multi_select_as_buttons': True, - 'extra_columns': [ - { - 'name': _(u'tagged items'), - 'attribute': encapsulate(lambda x: x.taggit_taggeditem_items.count()) - } - ] - }, context_instance=RequestContext(request)) + 'hide_object': True, + } + if extra_context: + context.update(extra_context) + + queryset = queryset if not (queryset is None) else Tag.objects.all() + + try: + Permission.objects.check_permissions(request.user, [PERMISSION_TAG_VIEW]) + except PermissionDenied: + queryset = AccessEntry.objects.filter_objects_by_access(PERMISSION_TAG_VIEW, request.user, queryset) + + context['object_list'] = queryset + + return render_to_response('generic_list.html', + context, + context_instance=RequestContext(request) + ) def tag_delete(request, tag_id=None, tag_id_list=None): - check_permissions(request.user, [PERMISSION_TAG_DELETE]) post_action_redirect = None if tag_id: @@ -166,6 +130,11 @@ def tag_delete(request, tag_id=None, tag_id_list=None): messages.error(request, _(u'Must provide at least one tag.')) return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/')) + try: + Permission.objects.check_permissions(request.user, [PERMISSION_TAG_DELETE]) + except PermissionDenied: + tags = AccessEntry.objects.filter_objects_by_access(PERMISSION_TAG_DELETE, request.user, tags) + 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', '/'))) @@ -207,9 +176,13 @@ def tag_multiple_delete(request): def tag_edit(request, tag_id): - check_permissions(request.user, [PERMISSION_TAG_EDIT]) tag = get_object_or_404(Tag, pk=tag_id) + try: + Permission.objects.check_permissions(request.user, [PERMISSION_TAG_EDIT]) + except PermissionDenied: + AccessEntry.objects.check_access(PERMISSION_TAG_EDIT, request.user, tag) + if request.method == 'POST': form = TagForm(request.POST) if form.is_valid(): @@ -237,11 +210,10 @@ def tag_edit(request, tag_id): def tag_tagged_item_list(request, tag_id): tag = get_object_or_404(Tag, pk=tag_id) - object_list = [tagged_item.content_object for tagged_item in tag.taggit_taggeditem_items.all()] return document_list( request, - object_list=object_list, + object_list=Document.objects.filter(tags__in=[tag]), title=_('documents with the tag "%s"') % tag, extra_context={ 'object': tag, @@ -251,24 +223,29 @@ def tag_tagged_item_list(request, tag_id): def document_tags(request, document_id): - check_permissions(request.user, [PERMISSION_TAG_VIEW]) document = get_object_or_404(Document, pk=document_id) - return render_to_response('generic_list.html', { - 'title': _(u'tags for: %s') % document, - 'object_list': document.tags.all(), - 'hide_link': True, - 'navigation_object_links': [tag_tagged_item_list_link], + try: + Permission.objects.check_permissions(request.user, [PERMISSION_DOCUMENT_VIEW]) + except PermissionDenied: + AccessEntry.objects.check_access(PERMISSION_DOCUMENT_VIEW, request.user, document) + + context = { 'object': document, 'document': document, - 'disable_auto_focus': True, - 'multi_select_as_buttons': True, - }, - context_instance=RequestContext(request)) + 'title': _(u'tags for: %s') % document, + } + + return tag_list(request, queryset=document.tags.all(), extra_context=context) def tag_remove(request, document_id, tag_id=None, tag_id_list=None): - check_permissions(request.user, [PERMISSION_TAG_REMOVE]) + document = get_object_or_404(Document, pk=document_id) + + try: + Permission.objects.check_permissions(request.user, [PERMISSION_TAG_REMOVE]) + except PermissionDenied: + AccessEntry.objects.check_access(PERMISSION_TAG_REMOVE, request.user, document) post_action_redirect = None @@ -280,8 +257,6 @@ def tag_remove(request, document_id, tag_id=None, tag_id_list=None): messages.error(request, _(u'Must provide at least one tag.')) return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/')) - document = get_object_or_404(Document, pk=document_id) - 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', '/'))) @@ -315,3 +290,16 @@ def tag_remove(request, document_id, tag_id=None, tag_id_list=None): def tag_multiple_remove(request, document_id): return tag_remove(request, document_id=document_id, tag_id_list=request.GET.get('id_list', [])) + + +def tag_acl_list(request, tag_pk): + tag = get_object_or_404(Tag, pk=tag_pk) + logger.debug('tag: %s' % tag) + + return acl_list_for( + request, + tag, + extra_context={ + 'object': tag, + } + ) diff --git a/apps/tags/widgets.py b/apps/tags/widgets.py index ab1b6762b1..ecf966612e 100644 --- a/apps/tags/widgets.py +++ b/apps/tags/widgets.py @@ -1,4 +1,3 @@ -from django.utils.translation import ugettext_lazy as _ from django.utils.safestring import mark_safe @@ -6,6 +5,7 @@ def get_tags_inline_widget(document): """ A tag widget that includes the total tag count for a given document """ + # TODO: merge widgets tags_template = [] tag_count = document.tags.count() if tag_count: @@ -13,7 +13,7 @@ def get_tags_inline_widget(document): for tag in document.tags.all(): tags_template.append(u'
    • %s
    ' % (tag.tagproperties_set.get().get_color_code(), tag.name)) - + tags_template.append(u'
    ') tags_template.append(u'') return mark_safe(u''.join(tags_template)) @@ -21,8 +21,7 @@ def get_tags_inline_widget(document): def get_tags_inline_widget_simple(document): """ - A tag widget that only displayes the rectangular colored boxes for a - given document + A tag widget that displays the tags for the given document """ tags_template = [] @@ -30,12 +29,20 @@ def get_tags_inline_widget_simple(document): if tag_count: tags_template.append('
      ') for tag in document.tags.all(): - tags_template.append('
    • %s
    • ' % (tag.tagproperties_set.get().get_color_code(), tag.name)) + tags_template.append(get_single_tag_template(tag)) tags_template.append('
    ') return mark_safe(u''.join(tags_template)) -def tag_color_block(tag): - return mark_safe(u'
    ' % tag.tagproperties_set.get().get_color_code()) +def single_tag_widget(tag): + tags_template = [] + tags_template.append('
      ') + tags_template.append(get_single_tag_template(tag)) + tags_template.append('
    ') + return mark_safe(u''.join(tags_template)) + + +def get_single_tag_template(tag): + return '
  • %s
  • ' % (tag.tagproperties_set.get().get_color_code(), tag.name.replace(u' ', u' ')) diff --git a/apps/user_management/__init__.py b/apps/user_management/__init__.py index 6b2bc6aa82..00d0e50533 100644 --- a/apps/user_management/__init__.py +++ b/apps/user_management/__init__.py @@ -1,32 +1,17 @@ +from __future__ import absolute_import + from django.utils.translation import ugettext_lazy as _ from django.contrib.auth.models import User, Group from navigation.api import register_links, register_multi_item_links -from permissions.api import register_permission, set_namespace_title from project_setup.api import register_setup -PERMISSION_USER_CREATE = {'namespace': 'user_management', 'name': 'user_create', 'label': _(u'Create new users')} -PERMISSION_USER_EDIT = {'namespace': 'user_management', 'name': 'user_edit', 'label': _(u'Edit existing users')} -PERMISSION_USER_VIEW = {'namespace': 'user_management', 'name': 'user_view', 'label': _(u'View existing users')} -PERMISSION_USER_DELETE = {'namespace': 'user_management', 'name': 'user_delete', 'label': _(u'Delete existing users')} - -PERMISSION_GROUP_CREATE = {'namespace': 'user_management', 'name': 'group_create', 'label': _(u'Create new groups')} -PERMISSION_GROUP_EDIT = {'namespace': 'user_management', 'name': 'group_edit', 'label': _(u'Edit existing groups')} -PERMISSION_GROUP_VIEW = {'namespace': 'user_management', 'name': 'group_view', 'label': _(u'View existing groups')} -PERMISSION_GROUP_DELETE = {'namespace': 'user_management', 'name': 'group_delete', 'label': _(u'Delete existing groups')} - -set_namespace_title('user_management', _(u'User management')) -register_permission(PERMISSION_USER_CREATE) -register_permission(PERMISSION_USER_EDIT) -register_permission(PERMISSION_USER_VIEW) -register_permission(PERMISSION_USER_DELETE) -register_permission(PERMISSION_GROUP_CREATE) -register_permission(PERMISSION_GROUP_EDIT) -register_permission(PERMISSION_GROUP_VIEW) -register_permission(PERMISSION_GROUP_DELETE) +from .permissions import (PERMISSION_USER_CREATE, PERMISSION_USER_EDIT, + PERMISSION_USER_VIEW, PERMISSION_USER_DELETE, PERMISSION_GROUP_CREATE, + PERMISSION_GROUP_EDIT, PERMISSION_GROUP_VIEW, PERMISSION_GROUP_DELETE) user_list = {'text': _(u'user list'), 'view': 'user_list', 'famfam': 'user', 'permissions': [PERMISSION_USER_VIEW]} -user_setup = {'text': _(u'users'), 'view': 'user_list', 'famfam': 'user', 'icon': 'user.png', 'permissions': [PERMISSION_USER_VIEW]} +user_setup = {'text': _(u'users'), 'view': 'user_list', 'famfam': 'user', 'icon': 'user.png', 'permissions': [PERMISSION_USER_VIEW], 'children_view_regex': [r'^user_']} user_edit = {'text': _(u'edit'), 'view': 'user_edit', 'args': 'object.id', 'famfam': 'user_edit', 'permissions': [PERMISSION_USER_EDIT]} user_add = {'text': _(u'create new user'), 'view': 'user_add', 'famfam': 'user_add', 'permissions': [PERMISSION_USER_CREATE]} user_delete = {u'text': _('delete'), 'view': 'user_delete', 'args': 'object.id', 'famfam': 'user_delete', 'permissions': [PERMISSION_USER_DELETE]} @@ -35,7 +20,7 @@ user_set_password = {u'text': _('reset password'), 'view': 'user_set_password', user_multiple_set_password = {u'text': _('reset password'), 'view': 'user_multiple_set_password', 'famfam': 'lock_edit', 'permissions': [PERMISSION_USER_EDIT]} group_list = {'text': _(u'group list'), 'view': 'group_list', 'famfam': 'group', 'permissions': [PERMISSION_GROUP_VIEW]} -group_setup = {'text': _(u'groups'), 'view': 'group_list', 'famfam': 'group', 'icon': 'group.png', 'permissions': [PERMISSION_GROUP_VIEW]} +group_setup = {'text': _(u'groups'), 'view': 'group_list', 'famfam': 'group', 'icon': 'group.png', 'permissions': [PERMISSION_GROUP_VIEW], 'children_view_regex': [r'^group_']} group_edit = {'text': _(u'edit'), 'view': 'group_edit', 'args': 'object.id', 'famfam': 'group_edit', 'permissions': [PERMISSION_GROUP_EDIT]} group_add = {'text': _(u'create new group'), 'view': 'group_add', 'famfam': 'group_add', 'permissions': [PERMISSION_GROUP_CREATE]} group_delete = {u'text': _('delete'), 'view': 'group_delete', 'args': 'object.id', 'famfam': 'group_delete', 'permissions': [PERMISSION_GROUP_DELETE]} @@ -43,11 +28,11 @@ group_multiple_delete = {u'text': _('delete'), 'view': 'group_multiple_delete', group_members = {'text': _(u'members'), 'view': 'group_members', 'args': 'object.id', 'famfam': 'group_link', 'permissions': [PERMISSION_GROUP_EDIT]} register_links(User, [user_edit, user_set_password, user_delete]) -register_links(['user_multiple_set_password', 'user_set_password', 'user_multiple_delete', 'user_delete', 'user_edit', 'user_list', 'user_add'], [user_list, user_add], menu_name=u'sidebar') +register_links(['user_multiple_set_password', 'user_set_password', 'user_multiple_delete', 'user_delete', 'user_edit', 'user_list', 'user_add'], [user_list, user_add], menu_name=u'secondary_menu') register_multi_item_links(['user_list'], [user_multiple_set_password, user_multiple_delete]) register_links(Group, [group_edit, group_members, group_delete]) -register_links(['group_multiple_delete', 'group_delete', 'group_edit', 'group_list', 'group_add', 'group_members'], [group_list, group_add], menu_name=u'sidebar') +register_links(['group_multiple_delete', 'group_delete', 'group_edit', 'group_list', 'group_add', 'group_members'], [group_list, group_add], menu_name=u'secondary_menu') register_multi_item_links(['group_list'], [group_multiple_delete]) user_management_views = [ diff --git a/apps/user_management/locale/en/LC_MESSAGES/django.po b/apps/user_management/locale/en/LC_MESSAGES/django.po index 2c85ff5f68..5a895404b4 100644 --- a/apps/user_management/locale/en/LC_MESSAGES/django.po +++ b/apps/user_management/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,79 +17,43 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: __init__.py:8 -msgid "Create new users" -msgstr "" - -#: __init__.py:9 -msgid "Edit existing users" -msgstr "" - -#: __init__.py:10 -msgid "View existing users" -msgstr "" - -#: __init__.py:11 -msgid "Delete existing users" -msgstr "" - #: __init__.py:13 -msgid "Create new groups" -msgstr "" - -#: __init__.py:14 -msgid "Edit existing groups" -msgstr "" - -#: __init__.py:15 -msgid "View existing groups" -msgstr "" - -#: __init__.py:16 -msgid "Delete existing groups" -msgstr "" - -#: __init__.py:18 -msgid "User management" -msgstr "" - -#: __init__.py:28 msgid "user list" msgstr "" -#: __init__.py:29 views.py:31 +#: __init__.py:14 views.py:31 msgid "users" msgstr "" -#: __init__.py:30 __init__.py:39 +#: __init__.py:15 __init__.py:24 msgid "edit" msgstr "" -#: __init__.py:31 views.py:92 +#: __init__.py:16 views.py:97 msgid "create new user" msgstr "" -#: __init__.py:32 __init__.py:33 __init__.py:41 __init__.py:42 +#: __init__.py:17 __init__.py:18 __init__.py:26 __init__.py:27 msgid "delete" msgstr "" -#: __init__.py:34 __init__.py:35 +#: __init__.py:19 __init__.py:20 msgid "reset password" msgstr "" -#: __init__.py:37 +#: __init__.py:22 msgid "group list" msgstr "" -#: __init__.py:38 views.py:222 +#: __init__.py:23 views.py:228 msgid "groups" msgstr "" -#: __init__.py:40 views.py:270 +#: __init__.py:25 views.py:276 msgid "create new group" msgstr "" -#: __init__.py:43 views.py:226 +#: __init__.py:28 views.py:232 msgid "members" msgstr "" @@ -101,6 +65,42 @@ msgstr "" msgid "Confirm password" msgstr "" +#: permissions.py:7 +msgid "User management" +msgstr "" + +#: permissions.py:9 +msgid "Create new users" +msgstr "" + +#: permissions.py:10 +msgid "Edit existing users" +msgstr "" + +#: permissions.py:11 +msgid "View existing users" +msgstr "" + +#: permissions.py:12 +msgid "Delete existing users" +msgstr "" + +#: permissions.py:14 +msgid "Create new groups" +msgstr "" + +#: permissions.py:15 +msgid "Edit existing groups" +msgstr "" + +#: permissions.py:16 +msgid "View existing groups" +msgstr "" + +#: permissions.py:17 +msgid "Delete existing groups" +msgstr "" + #: views.py:35 msgid "full name" msgstr "" @@ -113,140 +113,144 @@ msgstr "" msgid "active" msgstr "" -#: views.py:58 +#: views.py:47 +msgid "has usable password?" +msgstr "" + +#: views.py:61 msgid "" "Super user and staff user editing is not allowed, use the admin interface " "for these cases." msgstr "" -#: views.py:65 +#: views.py:68 #, python-format msgid "User \"%s\" updated successfully." msgstr "" -#: views.py:71 +#: views.py:74 #, python-format msgid "edit user: %s" msgstr "" -#: views.py:74 views.py:130 views.py:193 +#: views.py:77 views.py:135 views.py:198 msgid "user" msgstr "" -#: views.py:86 +#: views.py:91 #, python-format msgid "User \"%s\" created successfully." msgstr "" -#: views.py:108 views.py:162 +#: views.py:113 views.py:167 msgid "Must provide at least one user." msgstr "" -#: views.py:118 +#: views.py:123 msgid "" "Super user and staff user deleting is not allowed, use the admin interface " "for these cases." msgstr "" -#: views.py:121 +#: views.py:126 #, python-format msgid "User \"%s\" deleted successfully." msgstr "" -#: views.py:123 +#: views.py:128 #, python-format msgid "Error deleting user \"%(user)s\": %(error)s" msgstr "" -#: views.py:138 +#: views.py:143 #, python-format msgid "Are you sure you wish to delete the user: %s?" msgstr "" -#: views.py:140 +#: views.py:145 #, python-format msgid "Are you sure you wish to delete the users: %s?" msgstr "" -#: views.py:173 +#: views.py:178 msgid "Passwords do not match, try again." msgstr "" -#: views.py:178 +#: views.py:183 msgid "" "Super user and staff user password reseting is not allowed, use the admin " "interface for these cases." msgstr "" -#: views.py:182 +#: views.py:187 #, python-format msgid "Successfull password reset for user: %s." msgstr "" -#: views.py:184 +#: views.py:189 #, python-format msgid "Error reseting password for user \"%(user)s\": %(error)s" msgstr "" -#: views.py:200 +#: views.py:205 #, python-format msgid "Reseting password for user: %s" msgstr "" -#: views.py:202 +#: views.py:207 #, python-format msgid "Reseting password for users: %s" msgstr "" -#: views.py:243 +#: views.py:249 #, python-format msgid "Group \"%s\" updated successfully." msgstr "" -#: views.py:249 +#: views.py:255 #, python-format msgid "edit group: %s" msgstr "" -#: views.py:252 views.py:305 views.py:350 +#: views.py:258 views.py:311 views.py:356 msgid "group" msgstr "" -#: views.py:264 +#: views.py:270 #, python-format msgid "Group \"%s\" created successfully." msgstr "" -#: views.py:286 +#: views.py:292 msgid "Must provide at least one group." msgstr "" -#: views.py:296 +#: views.py:302 #, python-format msgid "Group \"%s\" deleted successfully." msgstr "" -#: views.py:298 +#: views.py:304 #, python-format msgid "Error deleting group \"%(group)s\": %(error)s" msgstr "" -#: views.py:313 +#: views.py:319 #, python-format msgid "Are you sure you wish to delete the group: %s?" msgstr "" -#: views.py:315 +#: views.py:321 #, python-format msgid "Are you sure you wish to delete the groups: %s?" msgstr "" -#: views.py:345 +#: views.py:351 #, python-format msgid "non members of group: %s" msgstr "" -#: views.py:346 +#: views.py:352 #, python-format msgid "members of group: %s" msgstr "" diff --git a/apps/user_management/locale/es/LC_MESSAGES/django.mo b/apps/user_management/locale/es/LC_MESSAGES/django.mo index ff5716ae9b..fc5b05d027 100644 Binary files a/apps/user_management/locale/es/LC_MESSAGES/django.mo and b/apps/user_management/locale/es/LC_MESSAGES/django.mo differ diff --git a/apps/user_management/locale/es/LC_MESSAGES/django.po b/apps/user_management/locale/es/LC_MESSAGES/django.po index 11f2ee564d..031b6e4bf2 100644 --- a/apps/user_management/locale/es/LC_MESSAGES/django.po +++ b/apps/user_management/locale/es/LC_MESSAGES/django.po @@ -3,13 +3,14 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: +# Roberto Rosario , 2012. msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" -"PO-Revision-Date: 2011-09-30 05:10+0000\n" -"Last-Translator: rosarior \n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-02 18:30+0000\n" +"Last-Translator: Roberto Rosario \n" "Language-Team: Spanish (Castilian) (http://www.transifex.net/projects/p/" "mayan-edms/team/es/)\n" "Language: es\n" @@ -18,79 +19,43 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -#: __init__.py:8 -msgid "Create new users" -msgstr "Crear nuevos usuarios" - -#: __init__.py:9 -msgid "Edit existing users" -msgstr "Editar usuarios existentes" - -#: __init__.py:10 -msgid "View existing users" -msgstr "Ver usuarios existentes" - -#: __init__.py:11 -msgid "Delete existing users" -msgstr "Eliminar usuarios existentes" - #: __init__.py:13 -msgid "Create new groups" -msgstr "Crear nuevos grupos" - -#: __init__.py:14 -msgid "Edit existing groups" -msgstr "Editar grupos existentes" - -#: __init__.py:15 -msgid "View existing groups" -msgstr "Ver grupos existentes" - -#: __init__.py:16 -msgid "Delete existing groups" -msgstr "Eliminar grupos existentes" - -#: __init__.py:18 -msgid "User management" -msgstr "Administración de usuarios" - -#: __init__.py:28 msgid "user list" msgstr "lista de usuarios" -#: __init__.py:29 views.py:31 +#: __init__.py:14 views.py:31 msgid "users" msgstr "usuarios" -#: __init__.py:30 __init__.py:39 +#: __init__.py:15 __init__.py:24 msgid "edit" msgstr "editar" -#: __init__.py:31 views.py:92 +#: __init__.py:16 views.py:97 msgid "create new user" msgstr "crear nuevo usuario" -#: __init__.py:32 __init__.py:33 __init__.py:41 __init__.py:42 +#: __init__.py:17 __init__.py:18 __init__.py:26 __init__.py:27 msgid "delete" msgstr "eliminar" -#: __init__.py:34 __init__.py:35 +#: __init__.py:19 __init__.py:20 msgid "reset password" msgstr "restablecer contraseña" -#: __init__.py:37 +#: __init__.py:22 msgid "group list" msgstr "list de group" -#: __init__.py:38 views.py:222 +#: __init__.py:23 views.py:228 msgid "groups" msgstr "grupos" -#: __init__.py:40 views.py:270 +#: __init__.py:25 views.py:276 msgid "create new group" msgstr "crear nuevo grupo" -#: __init__.py:43 views.py:226 +#: __init__.py:28 views.py:232 msgid "members" msgstr "miembros" @@ -102,6 +67,42 @@ msgstr "Nueva contraseña" msgid "Confirm password" msgstr "Confirmar contraseña" +#: permissions.py:7 +msgid "User management" +msgstr "Administración de usuarios" + +#: permissions.py:9 +msgid "Create new users" +msgstr "Crear nuevos usuarios" + +#: permissions.py:10 +msgid "Edit existing users" +msgstr "Editar usuarios existentes" + +#: permissions.py:11 +msgid "View existing users" +msgstr "Ver usuarios existentes" + +#: permissions.py:12 +msgid "Delete existing users" +msgstr "Eliminar usuarios existentes" + +#: permissions.py:14 +msgid "Create new groups" +msgstr "Crear nuevos grupos" + +#: permissions.py:15 +msgid "Edit existing groups" +msgstr "Editar grupos existentes" + +#: permissions.py:16 +msgid "View existing groups" +msgstr "Ver grupos existentes" + +#: permissions.py:17 +msgid "Delete existing groups" +msgstr "Eliminar grupos existentes" + #: views.py:35 msgid "full name" msgstr "nombre completo" @@ -114,7 +115,11 @@ msgstr "correo electrónico" msgid "active" msgstr "activo" -#: views.py:58 +#: views.py:47 +msgid "has usable password?" +msgstr "¿Tiene contraseña utilizable?" + +#: views.py:61 msgid "" "Super user and staff user editing is not allowed, use the admin interface " "for these cases." @@ -122,30 +127,30 @@ msgstr "" "No se permite editar el super usuario y usuario de personal, use la interfaz " "de administración para estos casos." -#: views.py:65 +#: views.py:68 #, python-format msgid "User \"%s\" updated successfully." msgstr "Usuario \"%s\" actualizado exitsamente." -#: views.py:71 +#: views.py:74 #, python-format msgid "edit user: %s" msgstr "editar usuario: %s" -#: views.py:74 views.py:130 views.py:193 +#: views.py:77 views.py:135 views.py:198 msgid "user" msgstr "usuario" -#: views.py:86 +#: views.py:91 #, python-format msgid "User \"%s\" created successfully." msgstr "Usuario \"%s\" ha creado exitosamente." -#: views.py:108 views.py:162 +#: views.py:113 views.py:167 msgid "Must provide at least one user." msgstr "Debe proveer al menos un usuario." -#: views.py:118 +#: views.py:123 msgid "" "Super user and staff user deleting is not allowed, use the admin interface " "for these cases." @@ -153,31 +158,31 @@ msgstr "" "No se permite eliminar el super usuario y usuario de personal, use la " "interfaz de administración para estos casos." -#: views.py:121 +#: views.py:126 #, python-format msgid "User \"%s\" deleted successfully." msgstr "Usuario \"%s\" eliminado exitosamente." -#: views.py:123 +#: views.py:128 #, python-format msgid "Error deleting user \"%(user)s\": %(error)s" msgstr "Error eliminando el usuario \"%(user)s\": %(error)s " -#: views.py:138 +#: views.py:143 #, python-format msgid "Are you sure you wish to delete the user: %s?" msgstr "¿Está seguro que desea eliminar el usuario: %s?" -#: views.py:140 +#: views.py:145 #, python-format msgid "Are you sure you wish to delete the users: %s?" msgstr "¿Está seguro que desea eliminar los usuarios: %s?" -#: views.py:173 +#: views.py:178 msgid "Passwords do not match, try again." msgstr "Las contraseñas no coinciden, vuelva a intentarlo." -#: views.py:178 +#: views.py:183 msgid "" "Super user and staff user password reseting is not allowed, use the admin " "interface for these cases." @@ -185,76 +190,76 @@ msgstr "" "No se permite cambiar la contraseña del super usuario y usuarios de " "personal, use la interfaz de administración para estos casos." -#: views.py:182 +#: views.py:187 #, python-format msgid "Successfull password reset for user: %s." msgstr "Restablecimiento exitoso de contraseña para el usuario: %s." -#: views.py:184 +#: views.py:189 #, python-format msgid "Error reseting password for user \"%(user)s\": %(error)s" msgstr "" "Error de restaurando la contraseña para el usuario \"%(user)s\": %(error)s " -#: views.py:200 +#: views.py:205 #, python-format msgid "Reseting password for user: %s" msgstr "Restaurando contraseña del usuario: %s" -#: views.py:202 +#: views.py:207 #, python-format msgid "Reseting password for users: %s" msgstr "Restaurando la contraseña de los usuarios: %s" -#: views.py:243 +#: views.py:249 #, python-format msgid "Group \"%s\" updated successfully." msgstr "Grupo \"%s\" actualizado exitosamente." -#: views.py:249 +#: views.py:255 #, python-format msgid "edit group: %s" msgstr "editar grupo: %s" -#: views.py:252 views.py:305 views.py:350 +#: views.py:258 views.py:311 views.py:356 msgid "group" msgstr "grupo" -#: views.py:264 +#: views.py:270 #, python-format msgid "Group \"%s\" created successfully." msgstr "Grupo \"%s\" creado exitosamente." -#: views.py:286 +#: views.py:292 msgid "Must provide at least one group." msgstr "Debe proveer al menos un grupo." -#: views.py:296 +#: views.py:302 #, python-format msgid "Group \"%s\" deleted successfully." msgstr "Grupo \"%s\" eliminado con exitosamente." -#: views.py:298 +#: views.py:304 #, python-format msgid "Error deleting group \"%(group)s\": %(error)s" msgstr "Error al eliminar el grupo \"%(group)s\": %(error)s " -#: views.py:313 +#: views.py:319 #, python-format msgid "Are you sure you wish to delete the group: %s?" msgstr "¿Está seguro que desea eliminar el grupo: %s?" -#: views.py:315 +#: views.py:321 #, python-format msgid "Are you sure you wish to delete the groups: %s?" msgstr "¿Está seguro que desea eliminar los grupos: %s?" -#: views.py:345 +#: views.py:351 #, python-format msgid "non members of group: %s" msgstr "no miembros del grupo: %s" -#: views.py:346 +#: views.py:352 #, python-format msgid "members of group: %s" msgstr "miembros del grupo: %s" diff --git a/apps/user_management/locale/it/LC_MESSAGES/django.mo b/apps/user_management/locale/it/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..9ab2965f2d Binary files /dev/null and b/apps/user_management/locale/it/LC_MESSAGES/django.mo differ diff --git a/apps/user_management/locale/it/LC_MESSAGES/django.po b/apps/user_management/locale/it/LC_MESSAGES/django.po new file mode 100644 index 0000000000..26a11e096d --- /dev/null +++ b/apps/user_management/locale/it/LC_MESSAGES/django.po @@ -0,0 +1,258 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# , 2011. +# Roberto Rosario , 2012. +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-21 16:36+0000\n" +"Last-Translator: Roberto Rosario \n" +"Language-Team: Italian (http://www.transifex.net/projects/p/mayan-edms/language/it/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: it\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" + +#: __init__.py:13 +msgid "user list" +msgstr "lista utenti" + +#: __init__.py:14 views.py:31 +msgid "users" +msgstr "utenti" + +#: __init__.py:15 __init__.py:24 +msgid "edit" +msgstr "modifica" + +#: __init__.py:16 views.py:97 +msgid "create new user" +msgstr "crea un nuovo utente" + +#: __init__.py:17 __init__.py:18 __init__.py:26 __init__.py:27 +msgid "delete" +msgstr "cancella" + +#: __init__.py:19 __init__.py:20 +msgid "reset password" +msgstr "riassegna la password" + +#: __init__.py:22 +msgid "group list" +msgstr "lista dei gruppi" + +#: __init__.py:23 views.py:228 +msgid "groups" +msgstr "gruppi" + +#: __init__.py:25 views.py:276 +msgid "create new group" +msgstr "crea un nuovo gruppo" + +#: __init__.py:28 views.py:232 +msgid "members" +msgstr "membri" + +#: forms.py:13 +msgid "New password" +msgstr "Nuova password" + +#: forms.py:14 +msgid "Confirm password" +msgstr "Conferma password" + +#: permissions.py:7 +msgid "User management" +msgstr "Gestione utenti" + +#: permissions.py:9 +msgid "Create new users" +msgstr "Crea un nuovo utente" + +#: permissions.py:10 +msgid "Edit existing users" +msgstr "Modifica utenti " + +#: permissions.py:11 +msgid "View existing users" +msgstr "Visualizza utenti" + +#: permissions.py:12 +msgid "Delete existing users" +msgstr "Cancella utenti" + +#: permissions.py:14 +msgid "Create new groups" +msgstr "Crea nuovi gruppi" + +#: permissions.py:15 +msgid "Edit existing groups" +msgstr "Modifica gruppi esistenti" + +#: permissions.py:16 +msgid "View existing groups" +msgstr "Visualizza i gruppi" + +#: permissions.py:17 +msgid "Delete existing groups" +msgstr "Cancella i gruppi esistenti" + +#: views.py:35 +msgid "full name" +msgstr "nome completo" + +#: views.py:39 +msgid "email" +msgstr "email" + +#: views.py:43 +msgid "active" +msgstr "attivo" + +#: views.py:47 +msgid "has usable password?" +msgstr "la password è utilizzabile?" + +#: views.py:61 +msgid "" +"Super user and staff user editing is not allowed, use the admin interface " +"for these cases." +msgstr "Super utente e utente modifica il personale non è consentito, utilizzare l'interfaccia di amministrazione per questi casi." + +#: views.py:68 +#, python-format +msgid "User \"%s\" updated successfully." +msgstr "Utente \"%s\" aggiornato con successo." + +#: views.py:74 +#, python-format +msgid "edit user: %s" +msgstr "modifica utente:%s" + +#: views.py:77 views.py:135 views.py:198 +msgid "user" +msgstr "utente" + +#: views.py:91 +#, python-format +msgid "User \"%s\" created successfully." +msgstr "Utente \"%s\" creato con successo." + +#: views.py:113 views.py:167 +msgid "Must provide at least one user." +msgstr "Devi fornire almeno un utente." + +#: views.py:123 +msgid "" +"Super user and staff user deleting is not allowed, use the admin interface " +"for these cases." +msgstr "Al super utente e utente non è consentito la cancellazione del personale, utilizzare l'interfaccia di amministrazione per questi casi." + +#: views.py:126 +#, python-format +msgid "User \"%s\" deleted successfully." +msgstr "Utente \"%s\" cancellato con successo." + +#: views.py:128 +#, python-format +msgid "Error deleting user \"%(user)s\": %(error)s" +msgstr "Errore nella cancellazione dell'utente \"%(user)s\": %(error)s" + +#: views.py:143 +#, python-format +msgid "Are you sure you wish to delete the user: %s?" +msgstr "Sei sicuro di voler cancellare l'utente: %s?" + +#: views.py:145 +#, python-format +msgid "Are you sure you wish to delete the users: %s?" +msgstr "Sei sicuro di voler cancellare gli utenti: %s?" + +#: views.py:178 +msgid "Passwords do not match, try again." +msgstr "La password non corrisponde, riprova." + +#: views.py:183 +msgid "" +"Super user and staff user password reseting is not allowed, use the admin " +"interface for these cases." +msgstr "Al super utente e utente non è consentito di reimpostare la password personale, utilizzare l'interfaccia di amministrazione per questi casi." + +#: views.py:187 +#, python-format +msgid "Successfull password reset for user: %s." +msgstr "Password reimpostata per l'utente: %s." + +#: views.py:189 +#, python-format +msgid "Error reseting password for user \"%(user)s\": %(error)s" +msgstr "Errore per il reimpostamento della password per l'utente \"%(user)s\": %(error)s" + +#: views.py:205 +#, python-format +msgid "Reseting password for user: %s" +msgstr "Reimposta la password per l'utente:%s" + +#: views.py:207 +#, python-format +msgid "Reseting password for users: %s" +msgstr "Reimposta la password per gli utenti:%s" + +#: views.py:249 +#, python-format +msgid "Group \"%s\" updated successfully." +msgstr "Gruppo \"%s\" aggiornato con successo." + +#: views.py:255 +#, python-format +msgid "edit group: %s" +msgstr "modifica gruppo: %s" + +#: views.py:258 views.py:311 views.py:356 +msgid "group" +msgstr "gruppo" + +#: views.py:270 +#, python-format +msgid "Group \"%s\" created successfully." +msgstr "Gruppo \"%s\" creato con successo." + +#: views.py:292 +msgid "Must provide at least one group." +msgstr "Devi almeno indicare un gruppo" + +#: views.py:302 +#, python-format +msgid "Group \"%s\" deleted successfully." +msgstr "Gruppo \"%s\" cancellato con successo." + +#: views.py:304 +#, python-format +msgid "Error deleting group \"%(group)s\": %(error)s" +msgstr "Erroro nella cancellazione del gruppo\"%(group)s\": %(error)s" + +#: views.py:319 +#, python-format +msgid "Are you sure you wish to delete the group: %s?" +msgstr "Sei sicuro di voler cancellare il gruppo: %s?" + +#: views.py:321 +#, python-format +msgid "Are you sure you wish to delete the groups: %s?" +msgstr "Sei sicuro di voler cancellare i gruppi: %s?" + +#: views.py:351 +#, python-format +msgid "non members of group: %s" +msgstr "non membri del gruppo: %s" + +#: views.py:352 +#, python-format +msgid "members of group: %s" +msgstr "membri del gruppo: %s" diff --git a/apps/user_management/locale/pl/LC_MESSAGES/django.mo b/apps/user_management/locale/pl/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..355d578b0f Binary files /dev/null and b/apps/user_management/locale/pl/LC_MESSAGES/django.mo differ diff --git a/apps/user_management/locale/pl/LC_MESSAGES/django.po b/apps/user_management/locale/pl/LC_MESSAGES/django.po new file mode 100644 index 0000000000..541da70147 --- /dev/null +++ b/apps/user_management/locale/pl/LC_MESSAGES/django.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# , 2012. +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-21 15:33+0000\n" +"Last-Translator: mic \n" +"Language-Team: Polish (http://www.transifex.net/projects/p/mayan-edms/language/pl/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pl\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" + +#: __init__.py:13 +msgid "user list" +msgstr "lista użytkowników" + +#: __init__.py:14 views.py:31 +msgid "users" +msgstr "użytkownicy" + +#: __init__.py:15 __init__.py:24 +msgid "edit" +msgstr "edytuj" + +#: __init__.py:16 views.py:97 +msgid "create new user" +msgstr "rejestracja nowego użytkownika" + +#: __init__.py:17 __init__.py:18 __init__.py:26 __init__.py:27 +msgid "delete" +msgstr "usunąć" + +#: __init__.py:19 __init__.py:20 +msgid "reset password" +msgstr "zresetować hasło" + +#: __init__.py:22 +msgid "group list" +msgstr "lista grupa" + +#: __init__.py:23 views.py:228 +msgid "groups" +msgstr "grupy" + +#: __init__.py:25 views.py:276 +msgid "create new group" +msgstr "utwórz nową grupę" + +#: __init__.py:28 views.py:232 +msgid "members" +msgstr "członkowie" + +#: forms.py:13 +msgid "New password" +msgstr "Nowe hasło" + +#: forms.py:14 +msgid "Confirm password" +msgstr "Potwierdź hasło" + +#: permissions.py:7 +msgid "User management" +msgstr "Zarządzanie użytkownikami" + +#: permissions.py:9 +msgid "Create new users" +msgstr "Tworzenie nowych użytkowników" + +#: permissions.py:10 +msgid "Edit existing users" +msgstr "Edycja istniejących użytkowników" + +#: permissions.py:11 +msgid "View existing users" +msgstr "Zobacz istniejących użytkowników" + +#: permissions.py:12 +msgid "Delete existing users" +msgstr "Usuwanie istniejących użytkowników" + +#: permissions.py:14 +msgid "Create new groups" +msgstr "Tworzyć nowe grupy" + +#: permissions.py:15 +msgid "Edit existing groups" +msgstr "Edytować istniejące grupy" + +#: permissions.py:16 +msgid "View existing groups" +msgstr "Zobacz istniejących grup" + +#: permissions.py:17 +msgid "Delete existing groups" +msgstr "Usunąć istniejące grupy" + +#: views.py:35 +msgid "full name" +msgstr "pełne imię i nazwisko" + +#: views.py:39 +msgid "email" +msgstr "e-mail" + +#: views.py:43 +msgid "active" +msgstr "aktywny" + +#: views.py:47 +msgid "has usable password?" +msgstr "posiada hasło?" + +#: views.py:61 +msgid "" +"Super user and staff user editing is not allowed, use the admin interface " +"for these cases." +msgstr "Super user oraz staff user edycja nie jest możliwa , należy użyć interfejsu administratora w takich przypadkach." + +#: views.py:68 +#, python-format +msgid "User \"%s\" updated successfully." +msgstr "Użytkownik \"%s\" został zaktualizowany." + +#: views.py:74 +#, python-format +msgid "edit user: %s" +msgstr "edytuj użytkownika: %s" + +#: views.py:77 views.py:135 views.py:198 +msgid "user" +msgstr "użytkownik" + +#: views.py:91 +#, python-format +msgid "User \"%s\" created successfully." +msgstr "Użytkownik \"%s\" został utworzony pomyślnie." + +#: views.py:113 views.py:167 +msgid "Must provide at least one user." +msgstr "Musi podać co najmniej jednego użytkownika." + +#: views.py:123 +msgid "" +"Super user and staff user deleting is not allowed, use the admin interface " +"for these cases." +msgstr "Super user oraz staff user usuwanie nie jest możliwa , należy użyć interfejsu administratora w takich przypadkach." + +#: views.py:126 +#, python-format +msgid "User \"%s\" deleted successfully." +msgstr "Użytkownik \"%s\" został usunięta." + +#: views.py:128 +#, python-format +msgid "Error deleting user \"%(user)s\": %(error)s" +msgstr "Błąd podczas usuwania użytkownika \" %(user)s \": %(error)s " + +#: views.py:143 +#, python-format +msgid "Are you sure you wish to delete the user: %s?" +msgstr "Czy na pewno chcesz usunąć użytkownika:%s.?" + +#: views.py:145 +#, python-format +msgid "Are you sure you wish to delete the users: %s?" +msgstr "Czy na pewno chcesz usunąć użytkowników:%s?" + +#: views.py:178 +msgid "Passwords do not match, try again." +msgstr "Hasła nie pasują, spróbuj ponownie." + +#: views.py:183 +msgid "" +"Super user and staff user password reseting is not allowed, use the admin " +"interface for these cases." +msgstr "Super user oraz staff user reset nie jest możliwa , należy użyć interfejsu administratora w takich przypadkach." + +#: views.py:187 +#, python-format +msgid "Successfull password reset for user: %s." +msgstr "Pomyślne resetowania hasła użytkownika:%s." + +#: views.py:189 +#, python-format +msgid "Error reseting password for user \"%(user)s\": %(error)s" +msgstr "Błąd podczas resetowania hasło użytkownika \" %(user)s \": %(error)s " + +#: views.py:205 +#, python-format +msgid "Reseting password for user: %s" +msgstr "Resetowanie hasła użytkownika:%s" + +#: views.py:207 +#, python-format +msgid "Reseting password for users: %s" +msgstr "Resetowanie hasła dla użytkowników:%s" + +#: views.py:249 +#, python-format +msgid "Group \"%s\" updated successfully." +msgstr "Grupa \"%s\" została zaktualizowany." + +#: views.py:255 +#, python-format +msgid "edit group: %s" +msgstr "edycja grupy:%s" + +#: views.py:258 views.py:311 views.py:356 +msgid "group" +msgstr "grupa" + +#: views.py:270 +#, python-format +msgid "Group \"%s\" created successfully." +msgstr "Grupa \"%s\" została utworzona pomyślnie." + +#: views.py:292 +msgid "Must provide at least one group." +msgstr "Musi podać co najmniej jedną grupę." + +#: views.py:302 +#, python-format +msgid "Group \"%s\" deleted successfully." +msgstr "Grupa \"%s\" została usunięta." + +#: views.py:304 +#, python-format +msgid "Error deleting group \"%(group)s\": %(error)s" +msgstr "Błąd podczas usuwania grupy \" %(group)s \": %(error)s " + +#: views.py:319 +#, python-format +msgid "Are you sure you wish to delete the group: %s?" +msgstr "Czy na pewno chcesz usunąć grupę:%s?" + +#: views.py:321 +#, python-format +msgid "Are you sure you wish to delete the groups: %s?" +msgstr "" + +#: views.py:351 +#, python-format +msgid "non members of group: %s" +msgstr "niebędących członkami grupy:%s" + +#: views.py:352 +#, python-format +msgid "members of group: %s" +msgstr "Członkowie grupy:%s" diff --git a/apps/user_management/locale/pt/LC_MESSAGES/django.mo b/apps/user_management/locale/pt/LC_MESSAGES/django.mo index b5441bf678..828d2b0f6c 100644 Binary files a/apps/user_management/locale/pt/LC_MESSAGES/django.mo and b/apps/user_management/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/user_management/locale/pt/LC_MESSAGES/django.po b/apps/user_management/locale/pt/LC_MESSAGES/django.po index 45dc3f91b4..98a9cfcddf 100644 --- a/apps/user_management/locale/pt/LC_MESSAGES/django.po +++ b/apps/user_management/locale/pt/LC_MESSAGES/django.po @@ -1,97 +1,61 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: # Renata Oliveira , 2011. +# Roberto Rosario , 2012. msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" -"PO-Revision-Date: 2011-11-03 03:18+0000\n" -"Last-Translator: renataoliveira \n" -"Language-Team: Portuguese (http://www.transifex.net/projects/p/mayan-edms/" -"team/pt/)\n" -"Language: pt\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-21 15:07+0000\n" +"Last-Translator: Roberto Rosario \n" +"Language-Team: Portuguese (http://www.transifex.net/projects/p/mayan-edms/language/pt/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +"Language: pt\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -#: __init__.py:8 -msgid "Create new users" -msgstr "Criar novos usuários" - -#: __init__.py:9 -msgid "Edit existing users" -msgstr "Editar usuários existentes" - -#: __init__.py:10 -msgid "View existing users" -msgstr "Ver os usuários existentes" - -#: __init__.py:11 -msgid "Delete existing users" -msgstr "Exclui usuários existentes" - #: __init__.py:13 -msgid "Create new groups" -msgstr "Criar novos grupos" - -#: __init__.py:14 -msgid "Edit existing groups" -msgstr "Editar grupos existentes" - -#: __init__.py:15 -msgid "View existing groups" -msgstr "Ver grupos existentes" - -#: __init__.py:16 -msgid "Delete existing groups" -msgstr "Excluir grupos existentes" - -#: __init__.py:18 -msgid "User management" -msgstr "Gerenciar usuários" - -#: __init__.py:28 msgid "user list" msgstr "lista de usuários" -#: __init__.py:29 views.py:31 +#: __init__.py:14 views.py:31 msgid "users" msgstr "usuários" -#: __init__.py:30 __init__.py:39 +#: __init__.py:15 __init__.py:24 msgid "edit" msgstr "editar" -#: __init__.py:31 views.py:92 +#: __init__.py:16 views.py:97 msgid "create new user" msgstr "criar novo usuário" -#: __init__.py:32 __init__.py:33 __init__.py:41 __init__.py:42 +#: __init__.py:17 __init__.py:18 __init__.py:26 __init__.py:27 msgid "delete" msgstr "excluir" -#: __init__.py:34 __init__.py:35 +#: __init__.py:19 __init__.py:20 msgid "reset password" msgstr "redefinir senha" -#: __init__.py:37 +#: __init__.py:22 msgid "group list" msgstr "lista de grupos" -#: __init__.py:38 views.py:222 +#: __init__.py:23 views.py:228 msgid "groups" msgstr "grupos" -#: __init__.py:40 views.py:270 +#: __init__.py:25 views.py:276 msgid "create new group" msgstr "criar novo grupo" -#: __init__.py:43 views.py:226 +#: __init__.py:28 views.py:232 msgid "members" msgstr "membros" @@ -103,6 +67,42 @@ msgstr "Nova senha" msgid "Confirm password" msgstr "Confirmar senha" +#: permissions.py:7 +msgid "User management" +msgstr "Gerenciar usuários" + +#: permissions.py:9 +msgid "Create new users" +msgstr "Criar novos usuários" + +#: permissions.py:10 +msgid "Edit existing users" +msgstr "Editar usuários existentes" + +#: permissions.py:11 +msgid "View existing users" +msgstr "Ver os usuários existentes" + +#: permissions.py:12 +msgid "Delete existing users" +msgstr "Exclui usuários existentes" + +#: permissions.py:14 +msgid "Create new groups" +msgstr "Criar novos grupos" + +#: permissions.py:15 +msgid "Edit existing groups" +msgstr "Editar grupos existentes" + +#: permissions.py:16 +msgid "View existing groups" +msgstr "Ver grupos existentes" + +#: permissions.py:17 +msgid "Delete existing groups" +msgstr "Excluir grupos existentes" + #: views.py:35 msgid "full name" msgstr "nome completo" @@ -115,146 +115,144 @@ msgstr "e-mail" msgid "active" msgstr "ativo" -#: views.py:58 +#: views.py:47 +msgid "has usable password?" +msgstr "tem senha usável?" + +#: views.py:61 msgid "" "Super user and staff user editing is not allowed, use the admin interface " "for these cases." -msgstr "" -"Edição de super usuário e usuário pessoal não é permitida, use a interface " -"de administração para esses casos." +msgstr "Edição de super usuário e usuário pessoal não é permitida, use a interface de administração para esses casos." -#: views.py:65 +#: views.py:68 #, python-format msgid "User \"%s\" updated successfully." msgstr "Usuário \"%s\" atualizado com sucesso." -#: views.py:71 +#: views.py:74 #, python-format msgid "edit user: %s" msgstr "editar usuário: %s" -#: views.py:74 views.py:130 views.py:193 +#: views.py:77 views.py:135 views.py:198 msgid "user" msgstr "usuário" -#: views.py:86 +#: views.py:91 #, python-format msgid "User \"%s\" created successfully." msgstr "Usuário \"%s\" criado com sucesso." -#: views.py:108 views.py:162 +#: views.py:113 views.py:167 msgid "Must provide at least one user." msgstr "Deve fornecer pelo menos um usuário." -#: views.py:118 +#: views.py:123 msgid "" "Super user and staff user deleting is not allowed, use the admin interface " "for these cases." -msgstr "" -"Excluir super usuário e usuário pessoal não é permitido, use a interface de " -"administração para esses casos." +msgstr "Excluir super usuário e usuário pessoal não é permitido, use a interface de administração para esses casos." -#: views.py:121 +#: views.py:126 #, python-format msgid "User \"%s\" deleted successfully." msgstr "Usuário \"%s\" removido com sucesso." -#: views.py:123 +#: views.py:128 #, python-format msgid "Error deleting user \"%(user)s\": %(error)s" msgstr "Erro ao excluir usuário \"%(user)s\": %(error)s " -#: views.py:138 +#: views.py:143 #, python-format msgid "Are you sure you wish to delete the user: %s?" msgstr "Tem certeza de que deseja excluir o usuário: %s?" -#: views.py:140 +#: views.py:145 #, python-format msgid "Are you sure you wish to delete the users: %s?" msgstr "Tem certeza de que deseja excluir os usuários: %s?" -#: views.py:173 +#: views.py:178 msgid "Passwords do not match, try again." msgstr "Senhas não coincidem, tente novamente." -#: views.py:178 +#: views.py:183 msgid "" "Super user and staff user password reseting is not allowed, use the admin " "interface for these cases." -msgstr "" -"Redefinir senha de super usuário e usuário pessoal não é permitido, use a " -"interface de administração para esses casos." +msgstr "Redefinir senha de super usuário e usuário pessoal não é permitido, use a interface de administração para esses casos." -#: views.py:182 +#: views.py:187 #, python-format msgid "Successfull password reset for user: %s." msgstr "Redefinição de senha do usuário bem-sucedida: %s." -#: views.py:184 +#: views.py:189 #, python-format msgid "Error reseting password for user \"%(user)s\": %(error)s" msgstr "Erro de redefinição de senha para o usuário \"%(user)s\": %(error)s " -#: views.py:200 +#: views.py:205 #, python-format msgid "Reseting password for user: %s" msgstr "Redefinindo senha para o usuário: %s" -#: views.py:202 +#: views.py:207 #, python-format msgid "Reseting password for users: %s" msgstr "Redefinindo senha para os usuários: %s" -#: views.py:243 +#: views.py:249 #, python-format msgid "Group \"%s\" updated successfully." msgstr "Grupo \"%s\" atualizado com sucesso." -#: views.py:249 +#: views.py:255 #, python-format msgid "edit group: %s" msgstr "editar grupo: %s" -#: views.py:252 views.py:305 views.py:350 +#: views.py:258 views.py:311 views.py:356 msgid "group" msgstr "grupo" -#: views.py:264 +#: views.py:270 #, python-format msgid "Group \"%s\" created successfully." msgstr "Grupo \"%s\" criado com sucesso." -#: views.py:286 +#: views.py:292 msgid "Must provide at least one group." msgstr "Deve fornecer pelo menos um grupo." -#: views.py:296 +#: views.py:302 #, python-format msgid "Group \"%s\" deleted successfully." msgstr "Grupo \"%s\" removido com sucesso." -#: views.py:298 +#: views.py:304 #, python-format msgid "Error deleting group \"%(group)s\": %(error)s" msgstr "Erro ao excluir o grupo \"%(group)s\": %(error)s " -#: views.py:313 +#: views.py:319 #, python-format msgid "Are you sure you wish to delete the group: %s?" msgstr "Tem certeza de que deseja excluir o grupo: %s?" -#: views.py:315 +#: views.py:321 #, python-format msgid "Are you sure you wish to delete the groups: %s?" msgstr "Tem certeza de que deseja excluir os grupos: %s?" -#: views.py:345 +#: views.py:351 #, python-format msgid "non members of group: %s" msgstr "não-membros do grupo: %s" -#: views.py:346 +#: views.py:352 #, python-format msgid "members of group: %s" msgstr "membros do grupo: %s" diff --git a/apps/user_management/locale/ru/LC_MESSAGES/django.mo b/apps/user_management/locale/ru/LC_MESSAGES/django.mo index 6783d4921d..d1955097ec 100644 Binary files a/apps/user_management/locale/ru/LC_MESSAGES/django.mo and b/apps/user_management/locale/ru/LC_MESSAGES/django.mo differ diff --git a/apps/user_management/locale/ru/LC_MESSAGES/django.po b/apps/user_management/locale/ru/LC_MESSAGES/django.po index 552793cf5e..08a91e48ef 100644 --- a/apps/user_management/locale/ru/LC_MESSAGES/django.po +++ b/apps/user_management/locale/ru/LC_MESSAGES/django.po @@ -7,9 +7,9 @@ msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" -"PO-Revision-Date: 2011-11-19 21:07+0000\n" -"Last-Translator: gsv70 \n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-02 18:18+0000\n" +"Last-Translator: Roberto Rosario \n" "Language-Team: Russian (http://www.transifex.net/projects/p/mayan-edms/team/" "ru/)\n" "Language: ru\n" @@ -19,79 +19,43 @@ msgstr "" "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:8 -msgid "Create new users" -msgstr "Создание новых пользователей" - -#: __init__.py:9 -msgid "Edit existing users" -msgstr "Редактирование существующих пользователей" - -#: __init__.py:10 -msgid "View existing users" -msgstr "Просмотр существующих пользователей" - -#: __init__.py:11 -msgid "Delete existing users" -msgstr "Удаление существующих пользователей" - #: __init__.py:13 -msgid "Create new groups" -msgstr "Создание новых групп" - -#: __init__.py:14 -msgid "Edit existing groups" -msgstr "Редактирование существующих групп" - -#: __init__.py:15 -msgid "View existing groups" -msgstr "Просмотр существующих групп" - -#: __init__.py:16 -msgid "Delete existing groups" -msgstr "Удалить существующие группы" - -#: __init__.py:18 -msgid "User management" -msgstr "Управление пользователями" - -#: __init__.py:28 msgid "user list" msgstr "список пользователей" -#: __init__.py:29 views.py:31 +#: __init__.py:14 views.py:31 msgid "users" msgstr "пользователи" -#: __init__.py:30 __init__.py:39 +#: __init__.py:15 __init__.py:24 msgid "edit" msgstr "редактировать" -#: __init__.py:31 views.py:92 +#: __init__.py:16 views.py:97 msgid "create new user" msgstr "создать нового пользователя" -#: __init__.py:32 __init__.py:33 __init__.py:41 __init__.py:42 +#: __init__.py:17 __init__.py:18 __init__.py:26 __init__.py:27 msgid "delete" msgstr "удалить" -#: __init__.py:34 __init__.py:35 +#: __init__.py:19 __init__.py:20 msgid "reset password" msgstr "сброс пароля" -#: __init__.py:37 +#: __init__.py:22 msgid "group list" msgstr "список групп" -#: __init__.py:38 views.py:222 +#: __init__.py:23 views.py:228 msgid "groups" msgstr "группы" -#: __init__.py:40 views.py:270 +#: __init__.py:25 views.py:276 msgid "create new group" msgstr "создать новую группу" -#: __init__.py:43 views.py:226 +#: __init__.py:28 views.py:232 msgid "members" msgstr "Участники" @@ -103,6 +67,42 @@ msgstr "Новый пароль" msgid "Confirm password" msgstr "Подтвердите пароль" +#: permissions.py:7 +msgid "User management" +msgstr "Управление пользователями" + +#: permissions.py:9 +msgid "Create new users" +msgstr "Создание новых пользователей" + +#: permissions.py:10 +msgid "Edit existing users" +msgstr "Редактирование существующих пользователей" + +#: permissions.py:11 +msgid "View existing users" +msgstr "Просмотр существующих пользователей" + +#: permissions.py:12 +msgid "Delete existing users" +msgstr "Удаление существующих пользователей" + +#: permissions.py:14 +msgid "Create new groups" +msgstr "Создание новых групп" + +#: permissions.py:15 +msgid "Edit existing groups" +msgstr "Редактирование существующих групп" + +#: permissions.py:16 +msgid "View existing groups" +msgstr "Просмотр существующих групп" + +#: permissions.py:17 +msgid "Delete existing groups" +msgstr "Удалить существующие группы" + #: views.py:35 msgid "full name" msgstr "полное имя" @@ -115,7 +115,11 @@ msgstr "электронная почта" msgid "active" msgstr "активно" -#: views.py:58 +#: views.py:47 +msgid "has usable password?" +msgstr "" + +#: views.py:61 msgid "" "Super user and staff user editing is not allowed, use the admin interface " "for these cases." @@ -123,30 +127,30 @@ msgstr "" "Редактирование суперпользователя и персонала не допускается, используйте для " "этого интерфейс администратора." -#: views.py:65 +#: views.py:68 #, python-format msgid "User \"%s\" updated successfully." msgstr "Пользователь \"%s\"обновлён" -#: views.py:71 +#: views.py:74 #, python-format msgid "edit user: %s" msgstr "редактировать пользователя: %s." -#: views.py:74 views.py:130 views.py:193 +#: views.py:77 views.py:135 views.py:198 msgid "user" msgstr "пользователь" -#: views.py:86 +#: views.py:91 #, python-format msgid "User \"%s\" created successfully." msgstr "Пользователь \"%s\" создан." -#: views.py:108 views.py:162 +#: views.py:113 views.py:167 msgid "Must provide at least one user." msgstr "Должен быть хотя бы один пользователь." -#: views.py:118 +#: views.py:123 msgid "" "Super user and staff user deleting is not allowed, use the admin interface " "for these cases." @@ -154,31 +158,31 @@ msgstr "" "Удаление суперпользователя и персонала не допускается, используйте " "интерфейс администратора для этих случаев." -#: views.py:121 +#: views.py:126 #, python-format msgid "User \"%s\" deleted successfully." msgstr "Пользователь \"%s\" удален." -#: views.py:123 +#: views.py:128 #, python-format msgid "Error deleting user \"%(user)s\": %(error)s" msgstr "Ошибка при удалении пользователя \"%(user)s\": %(error)s" -#: views.py:138 +#: views.py:143 #, python-format msgid "Are you sure you wish to delete the user: %s?" msgstr "Вы действительно хотите удалить пользователя:%s?" -#: views.py:140 +#: views.py:145 #, python-format msgid "Are you sure you wish to delete the users: %s?" msgstr "Вы действительно хотите удалить пользователей:%s?" -#: views.py:173 +#: views.py:178 msgid "Passwords do not match, try again." msgstr "Пароли не совпадают, попробуйте еще раз." -#: views.py:178 +#: views.py:183 msgid "" "Super user and staff user password reseting is not allowed, use the admin " "interface for these cases." @@ -186,75 +190,75 @@ msgstr "" "Сброс паролей суперпользователя и персонала не допускается, используйте " "интерфейс администратора для этих случаев." -#: views.py:182 +#: views.py:187 #, python-format msgid "Successfull password reset for user: %s." msgstr "Пароль пользователя %s сброшен." -#: views.py:184 +#: views.py:189 #, python-format msgid "Error reseting password for user \"%(user)s\": %(error)s" msgstr "Ошибка сброса пароля для пользователя \"%(user)s\": %(error)s" -#: views.py:200 +#: views.py:205 #, python-format msgid "Reseting password for user: %s" msgstr "Сброс пароля пользователя: %s" -#: views.py:202 +#: views.py:207 #, python-format msgid "Reseting password for users: %s" msgstr "Сброс пароля для пользователей: %s" -#: views.py:243 +#: views.py:249 #, python-format msgid "Group \"%s\" updated successfully." msgstr "Группа \"%s\" обновлены." -#: views.py:249 +#: views.py:255 #, python-format msgid "edit group: %s" msgstr "редактировать группу: %s" -#: views.py:252 views.py:305 views.py:350 +#: views.py:258 views.py:311 views.py:356 msgid "group" msgstr "группа" -#: views.py:264 +#: views.py:270 #, python-format msgid "Group \"%s\" created successfully." msgstr "Группа \"%s\"создана." -#: views.py:286 +#: views.py:292 msgid "Must provide at least one group." msgstr "Должна быть хотя бы одна группа." -#: views.py:296 +#: views.py:302 #, python-format msgid "Group \"%s\" deleted successfully." msgstr "Группа \"%s\" удалена." -#: views.py:298 +#: views.py:304 #, python-format msgid "Error deleting group \"%(group)s\": %(error)s" msgstr "Ошибка при удалении группы \"%(group)s\": %(error)s" -#: views.py:313 +#: views.py:319 #, python-format msgid "Are you sure you wish to delete the group: %s?" msgstr "Вы действительно хотите удалить группу: %s?" -#: views.py:315 +#: views.py:321 #, python-format msgid "Are you sure you wish to delete the groups: %s?" msgstr "Вы действительно хотите удалить группы: %s?" -#: views.py:345 +#: views.py:351 #, python-format msgid "non members of group: %s" msgstr "не входят в группу: %s" -#: views.py:346 +#: views.py:352 #, python-format msgid "members of group: %s" msgstr "входят в группу: %s" diff --git a/apps/user_management/management/__init__.py b/apps/user_management/management/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/user_management/management/commands/__init__.py b/apps/user_management/management/commands/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/user_management/management/commands/import_users.py b/apps/user_management/management/commands/import_users.py new file mode 100644 index 0000000000..9ccfbe09dc --- /dev/null +++ b/apps/user_management/management/commands/import_users.py @@ -0,0 +1,85 @@ +from __future__ import absolute_import + +import csv +import os +import sys +from optparse import make_option + +from django.core.management.base import BaseCommand, CommandError, LabelCommand +from django.contrib.auth.models import User +from django.db.utils import IntegrityError + + +def unicode_csv_reader(unicode_csv_data, dialect=csv.excel, **kwargs): + # csv.py doesn't do Unicode; encode temporarily as UTF-8: + csv_reader = csv.reader(utf_8_encoder(unicode_csv_data), + dialect=dialect, **kwargs) + for row in csv_reader: + # decode UTF-8 back to Unicode, cell by cell: + yield [unicode(cell, 'utf-8') for cell in row] + + +def utf_8_encoder(unicode_csv_data): + for line in unicode_csv_data: + yield line.encode('utf-8') + + +class Command(LabelCommand): + args = '' + help = 'Import users from a CSV file with the field order: username, firstname, lastname, email.' + option_list = LabelCommand.option_list + ( + make_option('--noinput', action='store_false', dest='interactive', + default=True, help='Do not ask the user for confirmation before ' + 'starting.'), + make_option('--password', action='store', dest='password', + help='The default password to assign to each new user.'), + make_option('--skip-repeated', action='store_true', dest='skip_repeated', + default=False, help='Don\'t exit if the user already exists.'), + ) + + def handle_label(self, label, **options): + if not os.access(label, os.R_OK): + raise CommandError("File '%s' is not readable." % label) + + if options['password']: + default_password = options['password'] + else: + default_password = None + + if _confirm(options['interactive']) == 'yes': + print 'Beginning import...' + with open(label, 'rb') as f: + reader = unicode_csv_reader(f) + try: + for row in reader: + print 'Adding: %s' % ', '.join(row) + try: + user = User( + username=row[0], + first_name=row[1], + last_name=row[2], + email=row[3] + ) + user.set_password(default_password) + user.save() + except IntegrityError: + print 'Repeated user entry: %s' % ', '.join(row) + if options['skip_repeated']: + print 'Ignoring.' + else: + sys.exit() + + except csv.Error, e: + sys.exit('file %s, line %d: %s' % (label, reader.line_num, e)) + else: + print 'Finish.' + else: + print 'Cancelled.' + + +def _confirm(interactive): + if not interactive: + return 'yes' + return raw_input('You have requested to import a number of users from a CSV file.\n' + 'Are you sure you want to do this?\n' + 'Type \'yes\' to continue, or any other value to cancel: ') diff --git a/apps/user_management/permissions.py b/apps/user_management/permissions.py new file mode 100644 index 0000000000..e644b1cb5e --- /dev/null +++ b/apps/user_management/permissions.py @@ -0,0 +1,17 @@ +from __future__ import absolute_import + +from django.utils.translation import ugettext_lazy as _ + +from permissions.models import PermissionNamespace, Permission + +user_management_namespace = PermissionNamespace('user_management', _(u'User management')) + +PERMISSION_USER_CREATE = Permission.objects.register(user_management_namespace, 'user_create', _(u'Create new users')) +PERMISSION_USER_EDIT = Permission.objects.register(user_management_namespace, 'user_edit', _(u'Edit existing users')) +PERMISSION_USER_VIEW = Permission.objects.register(user_management_namespace, 'user_view', _(u'View existing users')) +PERMISSION_USER_DELETE = Permission.objects.register(user_management_namespace, 'user_delete', _(u'Delete existing users')) + +PERMISSION_GROUP_CREATE = Permission.objects.register(user_management_namespace, 'group_create', _(u'Create new groups')) +PERMISSION_GROUP_EDIT = Permission.objects.register(user_management_namespace, 'group_edit', _(u'Edit existing groups')) +PERMISSION_GROUP_VIEW = Permission.objects.register(user_management_namespace, 'group_view', _(u'View existing groups')) +PERMISSION_GROUP_DELETE = Permission.objects.register(user_management_namespace, 'group_delete', _(u'Delete existing groups')) diff --git a/apps/user_management/tests.py b/apps/user_management/tests.py deleted file mode 100644 index 2247054b35..0000000000 --- a/apps/user_management/tests.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -This file demonstrates two different styles of tests (one doctest and one -unittest). These will both pass when you run "manage.py test". - -Replace these 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.failUnlessEqual(1 + 1, 2) - -__test__ = {"doctest": """ -Another way to test that 1 + 1 is equal to 2. - ->>> 1 + 1 == 2 -True -"""} - diff --git a/apps/user_management/views.py b/apps/user_management/views.py index 7fc5eee7ae..85fea6a198 100644 --- a/apps/user_management/views.py +++ b/apps/user_management/views.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import + from django.utils.translation import ugettext_lazy as _ from django.http import HttpResponseRedirect from django.shortcuts import render_to_response, get_object_or_404 @@ -7,21 +9,19 @@ from django.views.generic.list_detail import object_list from django.core.urlresolvers import reverse from django.contrib.auth.models import User, Group -from permissions.api import check_permissions +from permissions.models import Permission from common.utils import generate_choices_w_labels, encapsulate from common.widgets import two_state_template from common.views import assign_remove -from user_management import PERMISSION_USER_VIEW, \ - PERMISSION_USER_EDIT, PERMISSION_USER_CREATE, \ - PERMISSION_USER_DELETE, PERMISSION_GROUP_CREATE, \ - PERMISSION_GROUP_EDIT, PERMISSION_GROUP_VIEW, \ - PERMISSION_GROUP_DELETE -from user_management.forms import UserForm, PasswordForm, GroupForm +from .permissions import (PERMISSION_USER_CREATE, PERMISSION_USER_EDIT, + PERMISSION_USER_VIEW, PERMISSION_USER_DELETE, PERMISSION_GROUP_CREATE, + PERMISSION_GROUP_EDIT, PERMISSION_GROUP_VIEW, PERMISSION_GROUP_DELETE) +from .forms import UserForm, PasswordForm, GroupForm def user_list(request): - check_permissions(request.user, [PERMISSION_USER_VIEW]) + Permission.objects.check_permissions(request.user, [PERMISSION_USER_VIEW]) return object_list( request, @@ -42,8 +42,11 @@ def user_list(request): { 'name': _(u'active'), 'attribute': encapsulate(lambda x: two_state_template(x.is_active)), - } - + }, + { + 'name': _(u'has usable password?'), + 'attribute': encapsulate(lambda x: two_state_template(x.has_usable_password())), + }, ], 'multi_select_as_buttons': True, }, @@ -51,7 +54,7 @@ def user_list(request): def user_edit(request, user_id): - check_permissions(request.user, [PERMISSION_USER_EDIT]) + Permission.objects.check_permissions(request.user, [PERMISSION_USER_EDIT]) user = get_object_or_404(User, pk=user_id) if user.is_superuser or user.is_staff: @@ -77,14 +80,16 @@ def user_edit(request, user_id): def user_add(request): - check_permissions(request.user, [PERMISSION_USER_CREATE]) + Permission.objects.check_permissions(request.user, [PERMISSION_USER_CREATE]) if request.method == 'POST': form = UserForm(request.POST) if form.is_valid(): - user = form.save() + user = form.save(commit=False) + user.set_unusable_password() + user.save() messages.success(request, _(u'User "%s" created successfully.') % user) - return HttpResponseRedirect(reverse('user_list')) + return HttpResponseRedirect(reverse('user_set_password', args=[user.pk])) else: form = UserForm() @@ -96,7 +101,7 @@ def user_add(request): def user_delete(request, user_id=None, user_id_list=None): - check_permissions(request.user, [PERMISSION_USER_DELETE]) + Permission.objects.check_permissions(request.user, [PERMISSION_USER_DELETE]) post_action_redirect = None if user_id: @@ -150,7 +155,7 @@ def user_multiple_delete(request): def user_set_password(request, user_id=None, user_id_list=None): - check_permissions(request.user, [PERMISSION_USER_EDIT]) + Permission.objects.check_permissions(request.user, [PERMISSION_USER_EDIT]) post_action_redirect = None if user_id: @@ -211,8 +216,9 @@ def user_multiple_set_password(request): ) +# Group views def group_list(request): - check_permissions(request.user, [PERMISSION_GROUP_VIEW]) + Permission.objects.check_permissions(request.user, [PERMISSION_GROUP_VIEW]) return object_list( request, @@ -233,7 +239,7 @@ def group_list(request): def group_edit(request, group_id): - check_permissions(request.user, [PERMISSION_GROUP_EDIT]) + Permission.objects.check_permissions(request.user, [PERMISSION_GROUP_EDIT]) group = get_object_or_404(Group, pk=group_id) if request.method == 'POST': @@ -255,7 +261,7 @@ def group_edit(request, group_id): def group_add(request): - check_permissions(request.user, [PERMISSION_GROUP_CREATE]) + Permission.objects.check_permissions(request.user, [PERMISSION_GROUP_CREATE]) if request.method == 'POST': form = GroupForm(request.POST) @@ -274,7 +280,7 @@ def group_add(request): def group_delete(request, group_id=None, group_id_list=None): - check_permissions(request.user, [PERMISSION_GROUP_DELETE]) + Permission.objects.check_permissions(request.user, [PERMISSION_GROUP_DELETE]) post_action_redirect = None if group_id: @@ -333,7 +339,7 @@ def get_non_group_members(group): def group_members(request, group_id): - check_permissions(request.user, [PERMISSION_GROUP_EDIT]) + Permission.objects.check_permissions(request.user, [PERMISSION_GROUP_EDIT]) group = get_object_or_404(Group, pk=group_id) return assign_remove( diff --git a/apps/web_theme/locale/en/LC_MESSAGES/django.po b/apps/web_theme/locale/en/LC_MESSAGES/django.po index 230b0e0867..08e76067bc 100644 --- a/apps/web_theme/locale/en/LC_MESSAGES/django.po +++ b/apps/web_theme/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -44,19 +44,19 @@ msgstr "" msgid "close" msgstr "" -#: templates/web_theme_login.html:12 templates/web_theme_login.html.py:33 +#: templates/web_theme_login.html:12 templates/web_theme_login.html.py:34 msgid "Login" msgstr "" -#: templates/web_theme_login.html:17 +#: templates/web_theme_login.html:18 msgid "You are already logged in" msgstr "" -#: templates/web_theme_login.html:20 +#: templates/web_theme_login.html:21 msgid "Redirecting you to the website entry point in 5 seconds." msgstr "" -#: templates/web_theme_login.html:23 +#: templates/web_theme_login.html:24 #, python-format msgid "" "Or click here if redirection doesn't " diff --git a/apps/web_theme/locale/es/LC_MESSAGES/django.mo b/apps/web_theme/locale/es/LC_MESSAGES/django.mo index 937619e07a..c2f179bf10 100644 Binary files a/apps/web_theme/locale/es/LC_MESSAGES/django.mo and b/apps/web_theme/locale/es/LC_MESSAGES/django.mo differ diff --git a/apps/web_theme/locale/es/LC_MESSAGES/django.po b/apps/web_theme/locale/es/LC_MESSAGES/django.po index 6048de518a..13984eb57f 100644 --- a/apps/web_theme/locale/es/LC_MESSAGES/django.po +++ b/apps/web_theme/locale/es/LC_MESSAGES/django.po @@ -7,9 +7,9 @@ msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" "PO-Revision-Date: 2011-11-03 21:44+0000\n" -"Last-Translator: rosarior \n" +"Last-Translator: Roberto Rosario \n" "Language-Team: Spanish (Castilian) (http://www.transifex.net/projects/p/" "mayan-edms/team/es/)\n" "Language: es\n" @@ -48,19 +48,19 @@ msgstr "cerrar esta notificación" msgid "close" msgstr "cerrar" -#: templates/web_theme_login.html:12 templates/web_theme_login.html.py:33 +#: templates/web_theme_login.html:12 templates/web_theme_login.html.py:34 msgid "Login" msgstr "Iniciar sesión" -#: templates/web_theme_login.html:17 +#: templates/web_theme_login.html:18 msgid "You are already logged in" msgstr "Usted ya ha entrado" -#: templates/web_theme_login.html:20 +#: templates/web_theme_login.html:21 msgid "Redirecting you to the website entry point in 5 seconds." msgstr "Se va a redirigir al punto de entrada al sitio web en 5 segundos." -#: templates/web_theme_login.html:23 +#: templates/web_theme_login.html:24 #, python-format msgid "" "Or click here if redirection doesn't " diff --git a/apps/web_theme/locale/it/LC_MESSAGES/django.mo b/apps/web_theme/locale/it/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..989093a22a Binary files /dev/null and b/apps/web_theme/locale/it/LC_MESSAGES/django.mo differ diff --git a/apps/web_theme/locale/it/LC_MESSAGES/django.po b/apps/web_theme/locale/it/LC_MESSAGES/django.po new file mode 100644 index 0000000000..26968a531a --- /dev/null +++ b/apps/web_theme/locale/it/LC_MESSAGES/django.po @@ -0,0 +1,81 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# , 2011. +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2011-12-09 18:07+0000\n" +"Last-Translator: Pierpaolo Baldan \n" +"Language-Team: Italian (http://www.transifex.net/projects/p/mayan-edms/team/" +"it/)\n" +"Language: it\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" + +#: conf/settings.py:10 +msgid "" +"CSS theme to apply, options are: amro, bec, bec-green, blue, default, djime-" +"cerulean, drastic-dark, kathleene, olive, orange, red, reidb-greenish and " +"warehouse." +msgstr "" +"Tema CSS da applicare, le opzioni sono: ABN AMRO, bec, bec-verde, blu, di " +"default, djime-ceruleo, drastica-scuro, kathleene, oliva, arancio, rosso, " +"reidb-verdastro e magazzino." + +#: conf/settings.py:12 +msgid "Display extra information in the login screen." +msgstr "Mostra informazioni extra al login" + +#: templates/web_theme_base.html:101 +msgid "dismiss all notifications" +msgstr "Smetti tutte le notifiche" + +#: templates/web_theme_base.html:101 +msgid "close all" +msgstr "ciudi tutto" + +#: templates/web_theme_base.html:102 +msgid "dismiss this notification" +msgstr "respingere questa notifica" + +#: templates/web_theme_base.html:102 +msgid "close" +msgstr "chiudi" + +#: templates/web_theme_login.html:12 templates/web_theme_login.html.py:34 +msgid "Login" +msgstr "Login" + +#: templates/web_theme_login.html:18 +msgid "You are already logged in" +msgstr "Sei pronto per entrare" + +#: templates/web_theme_login.html:21 +msgid "Redirecting you to the website entry point in 5 seconds." +msgstr "Reindirizzamento al tuo punto di ingresso al sito in 5 secondi." + +#: templates/web_theme_login.html:24 +#, python-format +msgid "" +"Or click here if redirection doesn't " +"work." +msgstr "" +"Oppure click qui if per essere " +"rimandato al tuo sito nel caso non funzioni." + +#: templates/pagination/pagination.html:6 +#: templates/pagination/pagination.html:8 +msgid "Previous" +msgstr "Precedente" + +#: templates/pagination/pagination.html:26 +#: templates/pagination/pagination.html:28 +msgid "Next" +msgstr "Successivo" diff --git a/apps/web_theme/locale/pl/LC_MESSAGES/django.mo b/apps/web_theme/locale/pl/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..a213504995 Binary files /dev/null and b/apps/web_theme/locale/pl/LC_MESSAGES/django.mo differ diff --git a/apps/web_theme/locale/pl/LC_MESSAGES/django.po b/apps/web_theme/locale/pl/LC_MESSAGES/django.po new file mode 100644 index 0000000000..71e1e38ae1 --- /dev/null +++ b/apps/web_theme/locale/pl/LC_MESSAGES/django.po @@ -0,0 +1,75 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# , 2012. +msgid "" +msgstr "" +"Project-Id-Version: Mayan EDMS\n" +"Report-Msgid-Bugs-To: http://github.com/rosarior/mayan/issues\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" +"PO-Revision-Date: 2012-02-20 17:16+0000\n" +"Last-Translator: mic \n" +"Language-Team: Polish (http://www.transifex.net/projects/p/mayan-edms/language/pl/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pl\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" + +#: conf/settings.py:10 +msgid "" +"CSS theme to apply, options are: amro, bec, bec-green, blue, default, djime-" +"cerulean, drastic-dark, kathleene, olive, orange, red, reidb-greenish and " +"warehouse." +msgstr "" + +#: conf/settings.py:12 +msgid "Display extra information in the login screen." +msgstr "" + +#: templates/web_theme_base.html:101 +msgid "dismiss all notifications" +msgstr "" + +#: templates/web_theme_base.html:101 +msgid "close all" +msgstr "" + +#: templates/web_theme_base.html:102 +msgid "dismiss this notification" +msgstr "" + +#: templates/web_theme_base.html:102 +msgid "close" +msgstr "" + +#: templates/web_theme_login.html:12 templates/web_theme_login.html.py:34 +msgid "Login" +msgstr "" + +#: templates/web_theme_login.html:18 +msgid "You are already logged in" +msgstr "Użytkownik jest już zalogowany" + +#: templates/web_theme_login.html:21 +msgid "Redirecting you to the website entry point in 5 seconds." +msgstr "" + +#: templates/web_theme_login.html:24 +#, python-format +msgid "" +"Or click here if redirection doesn't " +"work." +msgstr "" + +#: templates/pagination/pagination.html:6 +#: templates/pagination/pagination.html:8 +msgid "Previous" +msgstr "Poprzedni" + +#: templates/pagination/pagination.html:26 +#: templates/pagination/pagination.html:28 +msgid "Next" +msgstr "Następny" diff --git a/apps/web_theme/locale/pt/LC_MESSAGES/django.mo b/apps/web_theme/locale/pt/LC_MESSAGES/django.mo index 24ff710f54..ed5c36e7c9 100644 Binary files a/apps/web_theme/locale/pt/LC_MESSAGES/django.mo and b/apps/web_theme/locale/pt/LC_MESSAGES/django.mo differ diff --git a/apps/web_theme/locale/pt/LC_MESSAGES/django.po b/apps/web_theme/locale/pt/LC_MESSAGES/django.po index 44231ccd0b..a5f21f001f 100644 --- a/apps/web_theme/locale/pt/LC_MESSAGES/django.po +++ b/apps/web_theme/locale/pt/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" "PO-Revision-Date: 2011-11-04 00:46+0000\n" "Last-Translator: emersonsoares \n" "Language-Team: Portuguese (http://www.transifex.net/projects/p/mayan-edms/" @@ -49,19 +49,19 @@ msgstr "descartar essa notificação" msgid "close" msgstr "fechar" -#: templates/web_theme_login.html:12 templates/web_theme_login.html.py:33 +#: templates/web_theme_login.html:12 templates/web_theme_login.html.py:34 msgid "Login" msgstr "Login" -#: templates/web_theme_login.html:17 +#: templates/web_theme_login.html:18 msgid "You are already logged in" msgstr "Você já está logado" -#: templates/web_theme_login.html:20 +#: templates/web_theme_login.html:21 msgid "Redirecting you to the website entry point in 5 seconds." msgstr "Redirecionando você para o ponto de entrada do site em 5 segundos." -#: templates/web_theme_login.html:23 +#: templates/web_theme_login.html:24 #, python-format msgid "" "Or click here if redirection doesn't " diff --git a/apps/web_theme/locale/ru/LC_MESSAGES/django.mo b/apps/web_theme/locale/ru/LC_MESSAGES/django.mo index 53af98872f..bceb298da5 100644 Binary files a/apps/web_theme/locale/ru/LC_MESSAGES/django.mo and b/apps/web_theme/locale/ru/LC_MESSAGES/django.mo differ diff --git a/apps/web_theme/locale/ru/LC_MESSAGES/django.po b/apps/web_theme/locale/ru/LC_MESSAGES/django.po index 4f4ba45d35..5557684b8b 100644 --- a/apps/web_theme/locale/ru/LC_MESSAGES/django.po +++ b/apps/web_theme/locale/ru/LC_MESSAGES/django.po @@ -7,9 +7,9 @@ msgid "" msgstr "" "Project-Id-Version: Mayan EDMS\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-22 11:26-0400\n" +"POT-Creation-Date: 2012-02-12 15:20-0400\n" "PO-Revision-Date: 2011-11-04 15:22+0000\n" -"Last-Translator: gsv70 \n" +"Last-Translator: Sergey Glita \n" "Language-Team: Russian (http://www.transifex.net/projects/p/mayan-edms/team/" "ru/)\n" "Language: ru\n" @@ -49,19 +49,19 @@ msgstr "убрать это уведомление" msgid "close" msgstr "закрыть" -#: templates/web_theme_login.html:12 templates/web_theme_login.html.py:33 +#: templates/web_theme_login.html:12 templates/web_theme_login.html.py:34 msgid "Login" msgstr "Войти" -#: templates/web_theme_login.html:17 +#: templates/web_theme_login.html:18 msgid "You are already logged in" msgstr "Вы уже вошли в систему" -#: templates/web_theme_login.html:20 +#: templates/web_theme_login.html:21 msgid "Redirecting you to the website entry point in 5 seconds." msgstr "Перенаправит вас на вход веб-сайта через 5 секунд." -#: templates/web_theme_login.html:23 +#: templates/web_theme_login.html:24 #, python-format msgid "" "Or click here if redirection doesn't " diff --git a/apps/web_theme/templates/web_theme_base.html b/apps/web_theme/templates/web_theme_base.html index 56aa5294cb..d60b8368df 100644 --- a/apps/web_theme/templates/web_theme_base.html +++ b/apps/web_theme/templates/web_theme_base.html @@ -69,7 +69,7 @@
    - {% if user.is_anonymous %} + {% if web_theme_view_type == 'plain' %}
    {% block content_plain %}{% endblock %}
    diff --git a/apps/web_theme/templates/web_theme_login.html b/apps/web_theme/templates/web_theme_login.html index ca98f0c7e9..302e371226 100644 --- a/apps/web_theme/templates/web_theme_login.html +++ b/apps/web_theme/templates/web_theme_login.html @@ -3,51 +3,52 @@ {% load theme_tags %} {% block web_theme_head %} - {% if not user.is_anonymous %} + {% if user.is_authenticated %} {% get_login_redirect_url %} {% endif %} {% endblock %} {% block html_title %}{% trans "Login" %}{% endblock %} -{% if not user.is_anonymous %} - {% block web_theme_content %} - {% get_login_redirect_url %} -
    -

    {% trans "You are already logged in" %}

    -
    -

    - {% trans "Redirecting you to the website entry point in 5 seconds." %} -

    -

    - {% blocktrans %}Or click here if redirection doesn't work.{% endblocktrans %} -

    -
    -
    - {% endblock %} -{% else %} - {% block content_plain %} -
    -

    {% block project_name %}{% endblock %}

    -
    -

    {% trans "Login" %}

    - - {% get_web_theme_setting "VERBOSE_LOGIN" as verbose_login %} - {% if verbose_login %} - {% include "verbose_login.html" %} - {% endif %} - {% endblock %} -{% endif %} + {% endblock %} + {% else %} + {% block content_plain %} +
    +

    {% block project_name %}{% endblock %}

    +
    +

    {% trans "Login" %}

    + +
    + {% get_web_theme_setting "VERBOSE_LOGIN" as verbose_login %} + {% if verbose_login %} + {% include "verbose_login.html" %} + {% endif %} + {% endblock %} + {% endif %} diff --git a/apps/web_theme/tests.py b/apps/web_theme/tests.py deleted file mode 100644 index 2247054b35..0000000000 --- a/apps/web_theme/tests.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -This file demonstrates two different styles of tests (one doctest and one -unittest). These will both pass when you run "manage.py test". - -Replace these 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.failUnlessEqual(1 + 1, 2) - -__test__ = {"doctest": """ -Another way to test that 1 + 1 is equal to 2. - ->>> 1 + 1 == 2 -True -"""} - diff --git a/contrib/mayan_11_1.pdf b/contrib/mayan_11_1.pdf new file mode 100644 index 0000000000..5fba2c87c3 Binary files /dev/null and b/contrib/mayan_11_1.pdf differ diff --git a/contrib/mayan_11_1.pdf.gpg b/contrib/mayan_11_1.pdf.gpg new file mode 100644 index 0000000000..e271fe8df3 Binary files /dev/null and b/contrib/mayan_11_1.pdf.gpg differ diff --git a/contrib/mayan_11_1.pdf.sig b/contrib/mayan_11_1.pdf.sig new file mode 100644 index 0000000000..de11aa6db1 Binary files /dev/null and b/contrib/mayan_11_1.pdf.sig differ diff --git a/contrib/supervisor/mayan-celery.conf b/contrib/supervisor/mayan-celery.conf deleted file mode 100644 index efcfd35927..0000000000 --- a/contrib/supervisor/mayan-celery.conf +++ /dev/null @@ -1,23 +0,0 @@ -; ======================================= -; celeryd supervisor example for Django -; ======================================= - -[program:celery] -command=/usr/share/mayan/bin/python /usr/share/mayan/mayan/manage.py celeryd --loglevel=INFO -directory=/usr/share/mayan/mayan -user=nobody -numprocs=1 -stdout_logfile=/var/log/mayan-celeryd.log -stderr_logfile=/var/log/mayan-celeryd.log -autostart=true -autorestart=true -startsecs=10 - -; Need to wait for currently executing tasks to finish at shutdown. -; Increase this if you have very long running tasks. -stopwaitsecs = 600 - -; if rabbitmq is supervised, set its priority higher -; so it starts first -priority=998 - diff --git a/contrib/supervisor/mayan-celerybeat.conf b/contrib/supervisor/mayan-celerybeat.conf deleted file mode 100644 index 7a37e9437f..0000000000 --- a/contrib/supervisor/mayan-celerybeat.conf +++ /dev/null @@ -1,19 +0,0 @@ -; ========================================== -; celerybeat supervisor example for Django -; ========================================== - -[program:celerybeat] -command=/usr/share/mayan/bin/python /usr/share/mayan/mayan/manage.py celerybeat --schedule=/var/lib/celery/celerybeat-schedule --loglevel=INFO -directory=/usr/share/mayan/mayan -user=nobody -numprocs=1 -stdout_logfile=/var/log/mayan-celerybeat.log -stderr_logfile=/var/log/mayan-celerybeat.log -autostart=true -autorestart=true -startsecs=10 - -; if rabbitmq is supervised, set its priority higher -; so it starts first -priority=999 - diff --git a/docs/_ext/djangodocs.py b/docs/_ext/djangodocs.py new file mode 100644 index 0000000000..3cf00a38e1 --- /dev/null +++ b/docs/_ext/djangodocs.py @@ -0,0 +1,229 @@ +""" +Sphinx plugins for Django documentation. +""" +import os +import re + +from docutils import nodes, transforms +try: + import json +except ImportError: + try: + import simplejson as json + except ImportError: + try: + from django.utils import simplejson as json + except ImportError: + json = None + +from sphinx import addnodes, roles, __version__ as sphinx_ver +from sphinx.builders.html import StandaloneHTMLBuilder +from sphinx.writers.html import SmartyPantsHTMLTranslator +from sphinx.util.console import bold +from sphinx.util.compat import Directive + +# RE for option descriptions without a '--' prefix +simple_option_desc_re = re.compile( + r'([-_a-zA-Z0-9]+)(\s*.*?)(?=,\s+(?:/|-|--)|$)') + +def setup(app): + app.add_crossref_type( + directivename = "setting", + rolename = "setting", + indextemplate = "pair: %s; setting", + ) + app.add_crossref_type( + directivename = "templatetag", + rolename = "ttag", + indextemplate = "pair: %s; template tag" + ) + app.add_crossref_type( + directivename = "templatefilter", + rolename = "tfilter", + indextemplate = "pair: %s; template filter" + ) + app.add_crossref_type( + directivename = "fieldlookup", + rolename = "lookup", + indextemplate = "pair: %s; field lookup type", + ) + app.add_description_unit( + directivename = "django-admin", + rolename = "djadmin", + indextemplate = "pair: %s; django-admin command", + parse_node = parse_django_admin_node, + ) + app.add_description_unit( + directivename = "django-admin-option", + rolename = "djadminopt", + indextemplate = "pair: %s; django-admin command-line option", + parse_node = parse_django_adminopt_node, + ) + app.add_config_value('django_next_version', '0.0', True) + app.add_directive('versionadded', VersionDirective) + app.add_directive('versionchanged', VersionDirective) + app.add_builder(DjangoStandaloneHTMLBuilder) + + +class VersionDirective(Directive): + has_content = True + required_arguments = 1 + optional_arguments = 1 + final_argument_whitespace = True + option_spec = {} + + def run(self): + env = self.state.document.settings.env + arg0 = self.arguments[0] + is_nextversion = env.config.django_next_version == arg0 + ret = [] + node = addnodes.versionmodified() + ret.append(node) + if not is_nextversion: + if len(self.arguments) == 1: + linktext = 'Please see the release notes ' % (arg0) + xrefs = roles.XRefRole()('doc', linktext, linktext, self.lineno, self.state) + node.extend(xrefs[0]) + node['version'] = arg0 + else: + node['version'] = "Development version" + node['type'] = self.name + if len(self.arguments) == 2: + inodes, messages = self.state.inline_text(self.arguments[1], self.lineno+1) + node.extend(inodes) + if self.content: + self.state.nested_parse(self.content, self.content_offset, node) + ret = ret + messages + env.note_versionchange(node['type'], node['version'], node, self.lineno) + return ret + + +class DjangoHTMLTranslator(SmartyPantsHTMLTranslator): + """ + Django-specific reST to HTML tweaks. + """ + + # Don't use border=1, which docutils does by default. + def visit_table(self, node): + self._table_row_index = 0 # Needed by Sphinx + self.body.append(self.starttag(node, 'table', CLASS='docutils')) + + # ? Really? + def visit_desc_parameterlist(self, node): + self.body.append('(') + self.first_param = 1 + self.param_separator = node.child_text_separator + + def depart_desc_parameterlist(self, node): + self.body.append(')') + + if sphinx_ver < '1.0.8': + # + # Don't apply smartypants to literal blocks + # + def visit_literal_block(self, node): + self.no_smarty += 1 + SmartyPantsHTMLTranslator.visit_literal_block(self, node) + + def depart_literal_block(self, node): + SmartyPantsHTMLTranslator.depart_literal_block(self, node) + self.no_smarty -= 1 + + # + # Turn the "new in version" stuff (versionadded/versionchanged) into a + # better callout -- the Sphinx default is just a little span, + # which is a bit less obvious that I'd like. + # + # FIXME: these messages are all hardcoded in English. We need to change + # that to accomodate other language docs, but I can't work out how to make + # that work. + # + version_text = { + 'deprecated': 'Deprecated in Django %s', + 'versionchanged': 'Changed in Django %s', + 'versionadded': 'New in Django %s', + } + + def visit_versionmodified(self, node): + self.body.append( + self.starttag(node, 'div', CLASS=node['type']) + ) + title = "%s%s" % ( + self.version_text[node['type']] % node['version'], + len(node) and ":" or "." + ) + self.body.append('%s ' % title) + + def depart_versionmodified(self, node): + self.body.append("
    \n") + + # Give each section a unique ID -- nice for custom CSS hooks + def visit_section(self, node): + old_ids = node.get('ids', []) + node['ids'] = ['s-' + i for i in old_ids] + node['ids'].extend(old_ids) + SmartyPantsHTMLTranslator.visit_section(self, node) + node['ids'] = old_ids + +def parse_django_admin_node(env, sig, signode): + command = sig.split(' ')[0] + env._django_curr_admin_command = command + title = "django-admin.py %s" % sig + signode += addnodes.desc_name(title, title) + return sig + +def parse_django_adminopt_node(env, sig, signode): + """A copy of sphinx.directives.CmdoptionDesc.parse_signature()""" + from sphinx.domains.std import option_desc_re + count = 0 + firstname = '' + for m in option_desc_re.finditer(sig): + optname, args = m.groups() + if count: + signode += addnodes.desc_addname(', ', ', ') + signode += addnodes.desc_name(optname, optname) + signode += addnodes.desc_addname(args, args) + if not count: + firstname = optname + count += 1 + if not count: + for m in simple_option_desc_re.finditer(sig): + optname, args = m.groups() + if count: + signode += addnodes.desc_addname(', ', ', ') + signode += addnodes.desc_name(optname, optname) + signode += addnodes.desc_addname(args, args) + if not count: + firstname = optname + count += 1 + if not firstname: + raise ValueError + return firstname + + +class DjangoStandaloneHTMLBuilder(StandaloneHTMLBuilder): + """ + Subclass to add some extra things we need. + """ + + name = 'djangohtml' + + def finish(self): + super(DjangoStandaloneHTMLBuilder, self).finish() + if json is None: + self.warn("cannot create templatebuiltins.js due to missing simplejson dependency") + return + self.info(bold("writing templatebuiltins.js...")) + xrefs = self.env.domaindata["std"]["objects"] + templatebuiltins = { + "ttags": [n for ((t, n), (l, a)) in xrefs.items() + if t == "templatetag" and l == "ref/templates/builtins"], + "tfilters": [n for ((t, n), (l, a)) in xrefs.items() + if t == "templatefilter" and l == "ref/templates/builtins"], + } + outfilename = os.path.join(self.outdir, "templatebuiltins.js") + f = open(outfilename, 'wb') + f.write('var django_template_builtins = ') + json.dump(templatebuiltins, f) + f.write(';\n') + f.close(); diff --git a/docs/changelog.rst b/docs/changelog.rst deleted file mode 100644 index 3396407b17..0000000000 --- a/docs/changelog.rst +++ /dev/null @@ -1,716 +0,0 @@ -Version 0.11.2 --------------- -* Fix issue #17, thanks to Сергей Глита (https://github.com/gsv70) for - finding this one. - -Version 0.11 ------------- -* Support for signed documents verification added, embedded and detached - signatures are supported. When verifying a document Mayan EDMS will - try to fetch the public key from the list of keyservers provided in the - configuration option SIGNATURES_KEYSERVERS (which defaults to - 'pool.sks-keyservers.net'). A public key management view has been added - to the setup menu as well as a key query and fetching view to manually - import keys from a keyserver. -* Added support for document versions. Users can upload unlimited amount - of versions for a document using a very flexible document version numbering - system, users can also revert to a previous document version. -* OCR queue processing improvements. -* Office documents handling improvements. -* Text extraction support for office documents. -* RTF text documents are now handled as office documents. -* Added a view to delete the document image cache, useful when switching - converter backends or doing diagnostics. -* Added South to the requirements. -* Merged documents' filename and extension database fiels into a single - filename field, filename are store as uploaded not manipulation is done - Users with existing data must install South and run the appropiate - migrate commands:: - $ pip install -r requirements/production.txt - $ ./manager syncdb - $ ./manage.py migrate documents 0001 --fake - $ ./manage.py migrate documents -* Added new office document mimetype - * application/vnd.ms-office -* Fixed documents not saving the file encoding -* Removed extra slash in ajax-loader.gif URL fixes #15, thanks to - IHLeanne for finding this one - - -Version 0.10.1 --------------- -* Upgraded django-compressor to version 1.1.1, run:: - - $ pip install --upgrade -r requirements/production.txt - - to upgrade - -* django-compressor is now disabled by default, users must explicitly - enable it adding COMPRESS_ENABLED=True to their settings_local.py file - - -Version 0.10 ------------- -* Added a proper setup views for the document grouping functionality. -* Document grouping is now called smart linking as it relates better to - how it actually works. The data base schema was changed and users must - do the required:: - - $ ./manager syncdb - - for the new tables to be created. -* Grappelli is no longer required as can be uninstalled. -* New smarter document preview widget that doesn't allow zooming or viewing - unknown or invalid documents. -* New office document converter, requires: - - * LibreOffice (https://www.libreoffice.org/) - * unoconv [version 0.5] (https://github.com/dagwieers/unoconv) - -* The new office documents converter won't convert files with the extension - .docx because these files are recognized as zip files instead. This - is an issue of the libmagic library. - -* New configuration option added ``CONVERTER_UNOCONV_USE_PIPE`` that controls - how unoconv handles the communication with LibreOffice. The default of - ``True`` causes unoconv to use **pipes**, this approach is slower than using - **TCP/IP** ports but it is more stable. - -* Initial `REST` `API` that exposes documents properties and one method, this - new `API` is used by the new smart document widget and requires the - package ``djangorestframework``, users must issue a:: - - $ pip install -r requirements/production.txt - - to install this new requirement. - -* MIME type detection and caching performance updates. -* Updated the included version of ``jQuery`` to 1.7 -* Updated the included version of ``JqueryAsynchImageLoader`` to 0.9.7 -* Document image serving response now specifies a MIME type for increased - browser compatibility. -* Small change in the scheduler that increases stability. -* Russian translation updates (Сергей Глита [Sergey Glita]) -* Improved and generalized the OCR queue locking mechanism, this should - eliminate any posibility of race conditions between Mayan EDMS OCR nodes. -* Added support for signals to the OCR queue, this results in instant OCR - processing upon submittal of a document to the OCR queue, this works in - addition to the current polling processing which eliminates the - posibility of stale documents in the OCR queue. -* Added multiple document OCR submit link -* Re enabled tesseract language specific OCR processing and added a one - (1) time language neutral retry for failed language specific OCR - -Version 0.9.1 -------------- -* Added handling percent encoded unicode query strings in search URL, - thanks to (Сергей Глита [Sergei Glita]) for reporting. -* Added a FAQ explaing how to fix MySQL collation related error when - doing searches also thanks to (Сергей Глита [Sergei Glita]) for - reporting this one. - -Version 0.9.0 -------------- -* Simplified getting mimetypes from files by merging 2 implementations - (document based and file based) -* Updated python converter backend, document model and staging module - to use the new get_mimetype API -* Only allow clickable thumbnails for document and staging files with a - valid image -* Removed tag count from the group document list widget to conserve - vertical space -* Updated required Django version to 1.3.1 -* Removed the included 3rd party module django-sendfile, now added to - the requirement files. - - * User should do a pip install -r requirements/production.txt to update - -* Changed to Semantic Versioning (http://semver.org/), with - recommendations 7, 8 and 9 causing the most effect in the versioning number. -* Added Russian locale post OCR cleanup backend (Сергей Глита [Sergei Glita]) -* Reduced severity of the messages displayed when no OCR cleanup backend - is found for a language -* Complete Portuguese translation (Emerson Soares and Renata Oliveira) -* Complete Russian translation (Сергей Глита [Sergei Glita]) -* Added animate.css to use CSS to animate flash messages with better - fallback on non JS browsers -* The admin and sentry links are no longer hard-coded (Meurig Freeman) -* Improved appearance of the document tag widget - (https://p.twimg.com/Ac0Q0b-CAAE1lfA.png:large) -* Added django_compress and cssmin to the requirements files and enabled - django_compress for CSS and JS files -* Added granting and revoking permission methods to the permission model -* Correctly calculate the mimetype icons paths when on development mode -* Added a new more comprehensive method of passing multiple variables - per item in multi item selection views -* Used new multi parameter passing method to improve the usability of - the grant/revoke permission view, thanks to Cezar Jenkins - (https://twitter.com/#!/emperorcezar) for the suggestion -* Added step to the documentation explaining how to install Mayan EDMS - on Webfaction -* Added an entry in the documentation to the screencast explaining how - to install Mayan EDMS on DjangoZoom -* Added required changes to add Mayan EDMS to Transifex.com -* Fixed the apache contrib file static file directory name -* Added improved documentation - -Version 0.8.3 -------------- -* Added a Contributors file under the docs directory -* Moved the document grouping subtemplate windows into a document - information tab -* Change the mode the setup options are shown, opting to use a more of a - dashboard style now -* Changed the tool menu to use the same button layout of the setup menu -* Moved OCR related handling to the tools main menu -* Improved the metadata type and metadata set selection widget during - the document upload wizard -* Added a view to the about menu to read the LICENSE file included with - Mayan -* Added converter backend agnostic image file format descriptions -* Disable whitelist and blacklist temporarily, removed document_type - field from interactive sources -* Fully disabled watch folders until they are working correctly -* Updated the project title to 'Mayan EDMS' -* If ghostscript is installed add PDF and PS to the list of file formats - by the python converter backend -* Use Pillow (http://pypi.python.org/pypi/Pillow) instead of PIL - - - Pillow is a fork of PIL with several updated including better jpeg and png library detection - - Users must uninstall PIL before installing Pillow - -* Updated the static media url in the login excempt url list -* Added remediatory code to sidestep issue #10 caused by DjangoZoom's deployment script executing the collectstatic command before creating the database structure with syncdb. Thanks to Joost Cassee (https://github.com/jcassee) for reporting this one. -* Perform extra validation of the image cache directory and fallback to creating a temporary directory on validation failure -* Fixed a source creation bug, that caused invalid links to a non existing source transformation to appear on the sidebar - - -Version 0.8.2 -------------- -* Moved code to Django 1.3 - - - Users have to use the ``collectstatic`` management command:: - - $ ./manage.py collectstatic - - - The ``site_media`` directory is no more, users must update the media - serving directives in current deployments and point them to the - ``static`` directory instead - -* The changelog is now available under the ``about`` main menu -* ``Grappelli`` no longer bundled with Mayan - - - Users must install Grappelli or execute:: - - $ pip install --upgrade -r requirements/production.txt - -* Even easier UI language switching -* Added email login method, to enable it, set:: - - AUTHENTICATION_BACKENDS = ('common.auth.email_auth_backend.EmailAuthBackend',) - COMMON_LOGIN_METHOD = 'email' - - -Version 0.8.1 -------------- -* Tags can now also be created from the main menu -* Added item count column to index instance list view -* Updated document indexing widget to show different icon for indexes or - indexes that contain documents -* Replaced the Textarea widget with the TextAreaDiv widget on document - and document page detail views - - - This change will allow highlighting search terms in the future - -* Unknown document file format page count now defaults to 1 - - - When uploading documents which the selected converted backend doesn't - understand, the total page count will fallback to 1 page to at least - show some data, and a comment will be automatically added to the - document description - -* Added new MAIN_DISABLE_ICONS to turn off all icons - - - This options works very well when using the ``default`` theme - -* The default theme is now ``activo`` -* Improved document page views and document page transformation views - navigation -* Added OCR queue document transformations - - - Use this for doing resizing or rotation fixes to improve OCR results - -* Added reset view link to the document page view to reset the zoom - level and page rotation -* Staging files now show a thumbnail preview instead of preview link - - -Version 0.8.0 -------------- -* Distributed OCR queue processing via celery is disabled for the time - being -* Added support for local scheduling of jobs - - - This addition removes celery beat requirement, and make is optional - -* Improve link highlighting -* Navigation improvements -* Documents with an unknown file format now display a mime type place - holder icon instead of a error icon -* Mayan now does pre caching of document visual representation improving - overall thumbnail, preview and display speed - - - Page image rotation and zooming is faster too with this update - -* Removed all QUALITY related settings -* ``COMMON_TEMPORARY_DIRECTORY`` is now validated when Mayan starts and if - not valid falls back to creating it's own temporary folder -* Added PDF file support to the python converter backend via ghostscript - - - This requires the installation of: - - + ghostscript python package - + ghostscript system binaries and libraries - -* Added PDF text parsing support to the python converter backend - - - This requires the installation of: - - + pdfminer python package - -* Added PDF page count support to the python converter backend -* Added python only converter backend supporting resizing, zooming and rotation - - - This backend required the installation of the python image library (PIL) - - This backend is useful when Graphicsmagick or Imagemagick can not be installed for some reason - - If understand fewer file format than the other 2 backends - -* Added default tranformation support to document sources -* Removed ``DOCUMENT_DEFAULT_TRANSFORMATIONS`` setup options -* Document sources are now defined via a series of view under the setup main menu -* This removes all the ``DOCUMENT_STAGING`` related setup options - - - Two document source types are supported local (via a web form), - and staging - - However multiple document sources can be defined each with their own - set of transformations and default metadata selection - -* Use ``python-magic`` to determine a document's mimetype otherwise - fallback to use python's mimetypes library -* Remove the included sources for ``python-magic`` instead it is now fetched - from github by pip -* Removed the document subtemplates and changed to a tabbed style -* Added link to document index content view to navigate the tree upwards -* Added new option ``MAIN_DISABLE_HOME_VIEW`` to disable the home main menu - tab and save some space -* Added new option to the web theme app, ``WEB_THEME_VERBOSE_LOGIN`` - that display a more information on the login screen - (version, copyright, logos) -* Added a confirmation dialog to the document tag removal view - -Version 0.7.6 -------------- -* Added recent searches per user support - - - The ammount of searches stored is controlled by the setup option - ``SEARCH_RECENT_COUNT`` - -* The document page zoom button are now disabled when reaching the minimum - or maximum zoom level -* The document page navigation links are now disabled when view the first - and last page of a document -* Document page title now displays the current page vs the total page - count -* Document page title now displays the current zoom level and rotation - degrees -* Added means set the expansion compressed files during document creation, - via web interface removing the need for the configuration options: - ``UNCOMPRESS_COMPRESSED_LOCAL_FILES`` and ``UNCOMPRESS_COMPRESSED_STAGING_FILES`` -* Added 'search again' button to the advances search results view -* Implementes an advanced search feature, which allows for individual field terms - - - Search fields supported: document type, MIME type, filename, - extension, metadata values, content, description, tags, comments - -Version 0.7.5 -------------- -* Added a help messages to the sidebar of some views -* Renamed some forms submit button to more intuitive one - - - 'Search' on the submit button of the search form - - 'Next step' on the document creation wizard - -* Added view to list supported file formats and reported by the - converter backend -* Added redirection support to multi object action views -* Renamed 'document list' link to 'all documents' and - 'recent document list' to 'recent documents' -* Removed 'change password' link next to the current user's name and - added a few views to handle the current user's password, details and - details editing - -Version 0.7.4 -------------- -* Renamed 'secondary actions' to 'secondary menu' -* Added document type setup views to the setup menu -* Added document type file name editing views to the setup menu -* Fixed document queue properties sidebar template not showing - -Version 0.7.3 -------------- -* Refactored main menu navigation and converted all apps to this new - system -* Multi item links are now displayed on top of generic lists as well as - on the bottom -* Spanish translation updates -* Updated requirements to use the latest development version of - django-mptt -* Improved user folder document removal views -* Added ability to specify default metadata or metadataset per - document type -* Converted filename handling to use os.path library for improved - portability -* Added edit source object attribute difference detection and logging - to history app -* Missing metadata type in a document during a multi document editing doesn't raise errors anymore. - - - This allows for multi document heterogeneous metadata editing in a single step. - -* Added document multi item links in search results - - - Direct editing can be done from the search result list - -* Permissions are now grouped and assigned a group name -* Improved role management views -* Document type is now an optional document property - - - Documents can be created without an explicit document type - -* Added support for per user staging directories -* Updated logos - -Version 0.7 ------------ -* Added confirmation dialogs icons -* Added comment app with support for adding and deleting comments to - and from documents -* Updated requirements files as per issue #9 -* Show tagged item count in the tag list view -* Show tagget document link in the tags subtemplate of documents -* Made comment sorted by oldest first, made comment subtemplate - scrollable -* Rename comments app to document_comment to avoid conflict with - Django's comment app -* Made document comments searchable - -Version 0.5.1 -------------- -* Applied initial merge of the new subtemplate renderer -* Fixed tag removal logic -* Initial commit to support document comments -* Updated so that loading spinner is displayed always -* Exclude tags from the local document upload form -* Added document tagging support - - - Requires installing ``django-taggit`` and doing a ``sync-db`` - -Version 0.5 ------------ -* Added tag list view and global tag delete support -* Added tag editing view and listing documents with an specific tag -* Changed the previewing and deleting staging files views to required - ``DOCUMENT_CREATE`` permission -* Added no-parent-history class to document page links so that iframe clicking doesn't affect the parent window history - - - Fixes back button issue on Chrome 9 & 10 - -* Added per app version display tag -* Added loading spinner animation -* Messages tweaks and translation updates -* Converter app cleanups, document pre-cache, magic number removal -* Added OCR view displaying all active OCR tasks from all cluster nodes -* Disabled ``CELERY_DISABLE_RATE_LIMITS`` by default -* Implement local task locking using Django locmem cache backend -* Added doc extension to office document format list -* Removed redundant transformation calculation -* Make sure OCR in processing documents cannot be deleted -* PEP8, pylint cleanups and removal of relative imports -* Removed the obsolete ``DOCUMENTS_GROUP_MAX_RESULTS`` setting option -* Improved visual appearance of messages by displaying them outside the - main form -* Added link to close all notifications with one click -* Made the queue processing interval configurable by means of a new - setting: ``OCR_QUEUE_PROCESSING_INTERVAL`` -* Added detection and reset of orphaned ocr documents being left as - 'processing' when celery dies -* Improved unknown format detection in the graphicsmagick backend -* Improved document convertion API -* Added initial support for converting office documents (only ods and - docx tested) -* Added sample configuration files for supervisor and apache under - contrib/ -* Avoid duplicates in recent document list -* Added the configuration option CONVERTER_GM_SETTINGS to pass - GraphicsMagicks specific commands the the GM backend -* Lower image convertion quality if the format is jpg -* Inverted the rotation button, more intuitive this way -* Merged and reduced the document page zoom and rotation views -* Increased permissions app permission's label field size - - - DB Update required - -* Added support for metadata group actions -* Reduced the document pages widget size -* Display the metadata group numeric total in the metadata group form - title -* Reorganized page detail icons -* Added first & last page navigation links to document page view -* Added interactive zoom support to document page detail view -* Spanish translation updates -* Added ``DOCUMENTS_ZOOM_PERCENT_STEP``, ``DOCUMENTS_ZOOM_MAX_LEVEL``, - ``DOCUMENTS_ZOOM_MIN_LEVEL`` configuration options to allow detailed - zoom control -* Added interactive document page view rotation support -* Changed the side bar document grouping with carousel style document - grouping form widget -* Removed the obsolete ``DOCUMENTS_TRANFORMATION_PREVIEW_SIZE`` and - ``DOCUMENTS_GROUP_SHOW_THUMBNAIL`` setting options -* Improved double submit prevention -* Added a direct rename field to the local update and staging upload - forms -* Separated document page detail view into document text and document - image views -* Added grab-scroll to document page view -* Disabled submit buttons and any buttons when during a form submit -* Updated the page preview widget to display a infinite-style horizontal - carousel of page previews -* Added support user document folders - - - Must do a ``syncdb`` to add the new tables - -* Added support for listing the most recent accessed documents per user -* Added document page navigation -* Fixed diagnostics url resolution -* Added confirmation dialog to document's find missing document file - diagnostic -* Added a document page edit view -* Added support for the command line program pdftotext from the - poppler-utils packages to extract text from PDF documents without - doing OCR -* Fixed document description editing -* Replaced page break text with page number when displaying document - content -* Implemented detail form readonly fields the correct way, this fixes - copy & paste issues with Firefox -* New document page view -* Added view to add or remove user to a specific role -* Updated the jQuery packages with the web_theme app to version 1.5.2 -* Made ``AVAILABLE_INDEXING_FUNCTION`` setting a setting of the documents - app instead of the filesystem_serving app -* Fixed document download in FireFox for documents containing spaces in - the filename -* If mime detection fails set mime type to '' instead of 'unknown' -* Use document MIME type when downloading otherwise use - 'application/octet-stream' if none -* Changed the way document page count is parsed from the graphics - backend, fixing issue #7 -* Optimized document metadata query and display -* Implemented OCR output cleanups for English and Spanish -* Redirect user to the website entry point if already logged and lands - in the login template -* Changed from using SimpleUploadedFile class to stream file to the - simpler File class wrapper -* Updated staging files previews to use sendfile instead of serve_file -* Moved staging file preview creation logic from documents.views to - staging.py -* When deleting staging file, it's cached preview is also deleted -* Added a new setup option: - - - ``FILESYSTEM_INDEXING_AVAILABLE_FUNCTIONS`` - a dictionary to allow users - to add custom functions - -* Made automatic OCR a function of the OCR app and not of Documents app (via signals) - - - Renamed setup option ``DOCUMENT_AUTOMATIC_OCR`` to ``OCR_AUTOMATIC_OCR`` - -* Clear node name when requeueing a document for OCR -* Added support for editing the metadata of multiple documents at the - same time -* Added Graphics magick support by means of user selectable graphic convertion backends - - - Some settings renamed to support this change: - - + ``CONVERTER_CONVERT_PATH`` is now ``CONVERTER_IM_CONVERT_PATH`` - + ``CONVERTER_IDENTIFY_PATH`` is now ``CONVERTER_IM_IDENTIFY_PATH`` - - - Added options: - - + ``CONVERTER_GM_PATH`` - File path to graphicsmagick's program. - + ``CONVERTER_GRAPHICS_BACKEND`` - Backend to use: ``ImageMagick`` or - ``GraphicMagick`` - -* Raise ImportError and notify user when specifying a non existant - converter graphics backend -* Fixed issue #4, avoid circular import in permissions/__init__.py -* Add a user to a default role only when the user is created -* Added total page count to statistics view -* Added support to disable the default scrolling JS code included in - web_theme app, saving some KBs in transfer -* Clear last ocr results when requeueing a document -* Removed the 'exists' column in document list view, diagnostics - superceded this -* Added 3rd party sendfile app (support apache's X-sendfile) -* Updated the get_document_image view to use the new sendfile app -* Fixed the issue of the strip spaces middleware conflicting with - downloads -* Removed custom IE9 tags -* Closed Issue #6 -* Allow deletion of non existing documents from OCR queue -* Allow OCR requeue of pending documents -* Invalid page numbers now raise Http404, not found instead of error -* Added an additional check to lower the chance of OCR race conditions - between nodes -* Introduce a random delay to each node to further reduce the chance of - a race condition, until row locking can be implemented or is - implemented by Django -* Moved navigation code to its own app -* Reimplemented OCR delay code, only delay new document - Added a new field: delay, update your database schema accordingly -* Made the concurrent ocr code more granular, per node, every node can - handle different amounts of concurrent ocr tasks - Added a new field: node_name, update your database schema acordinging -* Reduced default ocr delay time -* Added a new diagnostics tab under the tools menu -* Added a new option ``OCR_REPLICATION_DELAY`` to allow the storage some - time for replication before attempting to do OCR to a document -* Added OCR multi document re-queue and delete support -* Added simple statistics page (total used storage, total docs, etc) -* Implemented form based and button based multi item actions (button - based by default) -* Added multi document delete -* Fixed a few HTML validation errors -* Issues are now tracked using github -* Added indexing flags to ocr model -* Small optimization in document list view -* Small search optimization -* Display "DEBUG mode" string in title if ``DEBUG`` variable is set to True -* Added the fix-permissions bash script under misc/ folder -* Plugged another file descriptor leak -* Show class name in config settings view -* Added missing config option from the setup menu -* Close file descriptor to avoid leaks -* Don't allow duplicate documents in queues -* Don't raise ``PermissionDenied`` exception in ``PermissionDenied middleware``, - even while debugging -* Fixed page number detection -* Created 'simple document' for non technical users with all of a - document pages content -* Use document preview code for staging file also -* Error picture literal name removal -* Spanish translation updates -* Show document file path in regards of its storage -* Added new setting: side bar search box -* Implemented new ``PermissioDenied`` exception middleware handler -* Permissions app api now returns a ``PermissionDenied`` exception instead - of a custom one -* Added new 403 error template -* Updated the 404 template to display only a not found message -* Moved the login required middleware to the common app -* Fixed search app's model.objects.filter indentation, improved result - count calculation -* Added dynamic comparison types to search app -* Separated search code from view code -* Correctly calculate show result count for multi model searches -* Fixed OCR queue list showing wrong thumbnail -* Fixed staging file preview -* Show current metadata in document upload view sidebar -* Show sentry login for admin users -* Do not reinitialize document queue and/or queued document on reentry -* Try extra hard not to assign same uuid to two documents -* Added new transformation preview size setting -* Renamed document queue state links -* Changed ocr status display sidebar from form based to text based -* Added document action to clear all the document's page transformations -* Allow search across related fields -* Optimzed search for speed and memory footprint -* Added ``LIMIT`` setting to search -* Show search elapsed time on result page -* Converter now differentiates between unknown file format and convert - errors -* Close file descriptors when executing external programs to - prevent/reduce file descriptior leaks -* Improved exception handling of external programs -* Show document thumbnail in document ocr queue list -* Make ocr document date submitted column non breakable -* Fix permissions, directories set to mode 755 and files to mode 644 -* Try to fix issue #2, "random ORM field error on search while doing OCR" -* Added configurable location setting for file based storage -* Prepend storage name to differentiate config options -* Fixed duplicated document search -* Optimized document duplicate search -* Added locale middleware, menu bar language switching works now -* Only show language selection list if localemiddleware is active -* Spanish translation updates -* Added links, views and permissions to disable or enable an OCR queue -* Enabled Django's template caching -* Added document queue property side bar window to the document queue - list view -* Added HTML spaceless middleware to remove whitespace in HTML code -* If current user is superuser or staff show thumbnail & preview - generation error messages -* Added a setting to show document thumbnail in metadata group list -* Started adding configurations setting descriptions -* Initial GridFS storage support -* Implemented size and delete methods for GridFS -* Implement GridFS storage user settings -* Added document link in the OCR document queue list -* Link to manually re queue failed OCR -* Don't separate links (encose object list links with white-space: - nowrap;) -* Added document description to the field search list -* Sort OCR queued documents according to submitted date & time -* Document filesystem serving is now a separate app - - - Steps to update (Some warnings may be returned, but these are not - fatal as they might be related to missing metadata in some documents): - - + rename the following settings: - - + ``DOCUMENTS_FILESYSTEM_FILESERVING_ENABLE`` to ``FILESYSTEM_FILESERVING_ENABLE`` - + ``DOCUMENTS_FILESYSTEM_FILESERVING_PATH`` to ``FILESYSTEM_FILESERVING_PATH`` - + ``DOCUMENTS_FILESYSTEM_SLUGIFY_PATHS`` to ``FILESYSTEM_SLUGIFY_PATHS`` - + ``DOCUMENTS_FILESYSTEM_MAX_RENAME_COUNT`` to ``FILESYSTEM_MAX_RENAME_COUNT`` - - + Do a ./manage.py syncdb - + Execute 'Recreate index links' locate in the tools menu - + Wait a few minutes - -* Added per document duplicate search and a tools menu option to seach - all duplicated documents -* Added document tool that deletes and re-creates all documents - filesystem links -* Increased document's and document metadata index filename field's size - to 255 characters -* Added sentry to monitor and store error for later debugging -* Zip files can now be uncompressed in memory and their content uploaded - individually in one step -* Added support for concurrent, queued OCR processing using celery -* Apply default transformations to document before OCR -* Added unpaper to the OCR convertion pipe -* Added views to create, edit and grant/revoke permissions to roles -* Added multipage documents support (only tested on pdfs) - - - To update a previous database do: [d.update_page_count() for d in Document.objects.all()] - -* Added support for document page transformation (no GUI yet) -* Added permissions and roles support -* Added python-magic for smarter MIME type detection - (https://github.com/ahupp/python-magic). -* Added a new Document model field: file_mime_encoding. -* Show only document metadata in document list view. -* If one document type exists, the create document wizard skips the - first step. -* Changed to a liquid css grid -* Added the ability to group documents by their metadata -* New abstracted options to adjust document conversion quality (default, - low, high) diff --git a/docs/conf.py b/docs/conf.py index 48200208da..ded1ddc7f8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -17,6 +17,7 @@ import sys, os # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "_ext"))) # -- General configuration ----------------------------------------------------- @@ -25,7 +26,9 @@ import sys, os # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode'] +#extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode'] +#extensions = ["djangodocs", "sphinx.ext.intersphinx"] +extensions = ['djangodocs'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -37,7 +40,8 @@ source_suffix = '.rst' #source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +#master_doc = 'index' +master_doc = 'contents' # General information about the project. project = u'Mayan EDMS' @@ -48,10 +52,10 @@ copyright = u'2011, Roberto Rosario' # built documents. # # The short X.Y version. -version = '0.11.1' +version = '0.12' # The full version, including alpha/beta/rc tags. -release = '0.11.1' +release = '0.12' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -71,15 +75,15 @@ exclude_patterns = ['_build'] #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +add_module_names = False # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' @@ -179,7 +183,7 @@ htmlhelp_basename = 'MayanEDMSdoc' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'MayanEDMS.tex', u'Mayan EDMS Documentation', + ('contents', 'MayanEDMS.tex', u'Mayan EDMS Documentation', u'Roberto Rosario', 'manual'), ] @@ -212,6 +216,6 @@ latex_documents = [ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'mayanedms', u'Mayan EDMS Documentation', + ('contents', 'mayanedms', u'Mayan EDMS Documentation', [u'Roberto Rosario'], 1) ] diff --git a/docs/contents.rst b/docs/contents.rst new file mode 100644 index 0000000000..d97a668d11 --- /dev/null +++ b/docs/contents.rst @@ -0,0 +1,20 @@ +.. _contents: + +================================= +Mayan EDMS documentation contents +================================= + +.. toctree:: + :hidden: + + index + +.. toctree:: + :maxdepth: 3 + + intro/index + topics/index + releases/index + credits/index + faq/index + diff --git a/docs/credits.rst b/docs/credits.rst deleted file mode 100644 index e0b3e815d6..0000000000 --- a/docs/credits.rst +++ /dev/null @@ -1,124 +0,0 @@ -======= -Credits -======= - -* Python - * Copyright (c) 2001-2010 Python Software Foundation. - * Copyright (c) 2000 BeOpen.com. - * Copyright (c) 1995-2001 Corporation for National Research Initiatives. - * Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam. - -* Django - A high-level Python Web framework that encourages rapid development and clean, pragmatic design. - * Copyright Django Software Foundation - * http://www.djangoproject.com/ - -* django-pagination - * Copyright Eric Florenzano (floguy@gmail.com) - * http://django-pagination.googlecode.com/ - -* Web App Theme - * Copyright Andrea Franz (http://gravityblast.com) - * git://github.com/pilu/web-app-theme.git - -* Imagemagick - Convert, Edit, Or Compose Bitmap Images - * Copyright 1999-2011 ImageMagick Studio LLC - * http://www.imagemagick.org/script/index.php - -* FAMFAMFAM Silk icons - * Copyright Mark James (http://www.twitter.com/markjames) - * http://www.famfamfam.com/lab/icons/silk/ - -* 3 state FAMFAMFAM Silk icon sets: discrete images and CSS sprite palette - * Copyright Sky Sanders - * skysanders.net/subtext - -* django-extensions - Extensions for Django - * Copyright Bas van Oostveen (v.oostveen@gmail.com) - * http://code.google.com/p/django-command-extensions/ - -* django-rosetta - A Django application that eases the translation of Django projects - * Copyright Marco Bonetti (mbonetti@gmail.com) - * http://code.google.com/p/django-rosetta/ - -* Werkzeug - The Swiss Army knife of Python web development - * Copyright Armin Ronacher (armin.ronacher@active-4.com) - * http://werkzeug.pocoo.org/ - -* BoundFormWizard - A subclass of Django's FormWizard that handled FormSets. - * Matthew Flanagan (http://www.blogger.com/profile/15093905875465763876) - * http://code.google.com/p/wadofstuff/ - -* django-filetransfers - File upload/download abstraction - * Waldemar Kornewald - * http://www.allbuttonspressed.com/projects/django-filetransfers - -* tesseract - An OCR Engine that was developed at HP Labs between 1985 and 1995... and now at Google. - * http://code.google.com/p/tesseract-ocr/ - -* Image file 1068504_92921456 "Mayan piramid" (Stock Exchange) - * Andres Ojeda (http://www.sxc.hu/profile/andres_ol) - -* Image 1297211435_error - * http://kde-look.org/usermanager/search.php?username=InFeRnODeMoN - -* Fat cow icon set - * http://www.fatcow.com/free-icons - -* Python-magic - python-magic is a simple wrapper for libmagic - * Adam Hupp - * https://github.com/ahupp/python-magic - -* Fancybox - FancyBox is a tool for displaying images, html content and multi-media in a Mac-style "lightbox" that floats overtop of web page. - * http://fancybox.net - -* unpaper - post-processing scanned and photocopied book pages - * Jens Gulden 2005-2007 - unpaper@jensgulden.de. - * http://unpaper.berlios.de/ - -* celery - Celery is an open source asynchronous task queue/job queue based on distributed message passing. It is focused on real-time operation, but supports scheduling as well. - * Copyright 2009-2011, Ask Solem & contributors - * http://ask.github.com/celery/getting-started/introduction.html - -* django-celery - django-celery provides Celery integration for Django - * Copyright Ask Solem & contributors - * http://github.com/ask/django-celery/ - -* favicon - * http://www.iconfinder.com/icondetails/21581/24/draw_pyramid_icon - * Gnome Project - -* MongoDB - (from "humongous") is a scalable, high-performance, open source, document-oriented database. - * Copyright 10gen - * http://www.mongodb.org/ - -* PyMongo - is a Python distribution containing tools for working with MongoDB, and is the recommended way to work with MongoDB from Python. - * Copyright 2009, Michael Dirolf - * http://api.mongodb.org/python/ - -* GridFS - is a storage specification for large objects in MongoDB - * Copyright 10gen - * http://www.mongodb.org/display/DOCS/GridFS+Specification - -* django-sendfile - This is a wrapper around web-server specific methods for sending files to web clients. - * johnsensible (John Montgomery) - * https://github.com/johnsensible/django-sendfile - -* jQuery-Jail - Jquery Asynchronous Image Loader (JAIL) - * Sebastiano Armeli-Battana (contact@sebarmeli.com) - * http://www.sebastianoarmelibattana.com/projects/jail - -* django-taggit - is a reusable Django application for simple tagging - * Alex Gaynor (alex.gaynor@gmail.com) - * http://pypi.python.org/pypi/django-taggit - -* Image 392336_7079 (stock exchange) - -* djangorestframework - -* South - -* python-gnupg - -* python-hkp - - diff --git a/docs/LICENSE b/docs/credits/LICENSE similarity index 100% rename from docs/LICENSE rename to docs/credits/LICENSE diff --git a/docs/contributors.rst b/docs/credits/contributors.rst similarity index 55% rename from docs/contributors.rst rename to docs/credits/contributors.rst index cae85f3c13..7d015e8d78 100644 --- a/docs/contributors.rst +++ b/docs/credits/contributors.rst @@ -4,17 +4,21 @@ Contributors ============ + How to contribute? ------------------ You can help further the development of **Mayan EDMS** by reporting bugs, submitting documentation, patches, with monetary or hardware donations. + Bug fixes --------- * Aziz M. Bookwala (https://github.com/azizmb) * IHLeanne (https://github.com/IHLeanne) * Сергей Глита [Sergey Glita] (s.v.glita@gmail.com) * Meurig Freeman (https://github.com/meurig) +* David Herring (https://github.com/abadger1406) + Bug reports ----------- @@ -25,19 +29,53 @@ Bug reports * dAnjou (https://github.com/dAnjou) * Сергей Глита [Sergey Glita] (s.v.glita@gmail.com) * IHLeanne (https://github.com/IHLeanne) +* valterwill (https://github.com/valterwill) +* David Herring (https://github.com/abadger1406) + Patches ------- * Meurig Freeman (https://github.com/meurig) * Сергей Глита [Sergey Glita] (s.v.glita@gmail.com) + Suggestions ----------- * Cezar Jenkins (https://twitter.com/#!/emperorcezar) * Сергей Глита [Sergey Glita] (s.v.glita@gmail.com) +* Barry Rowlingson (http://geospaced.blogspot.com) +* Gour (https://github.com/gour) + Translations ------------ -* Emerson Soares (http://emersonsoares.com) -* Renata Oliveira (https://twitter.com/#!/rnataoliveira) +* Portuguese + + - Emerson Soares (http://emersonsoares.com) + - Renata Oliveira (https://twitter.com/#!/rnataoliveira) + +* Russian + + - Сергей Глита [Sergey Glita] (s.v.glita@gmail.com) + +* Italian + + - SeeOpen.IT (Numero Verde: 800.910.125, E-mail: sales@seeopen.it) + +* Polish + + - mic (https://www.transifex.net/accounts/profile/mic/) + + +Remote access for debugging +--------------------------- * Сергей Глита [Sergey Glita] (s.v.glita@gmail.com) +* David Herring (https://github.com/abadger1406) +* Michael Terretta (terretta@gmail.com) + + +Monetary donations +------------------ +* David Herring (https://github.com/abadger1406) - $100 +* David Herring (https://github.com/abadger1406) - $250 +* Michael Terretta (terretta@gmail.com) - $100 diff --git a/docs/credits/index.rst b/docs/credits/index.rst new file mode 100644 index 0000000000..0280222168 --- /dev/null +++ b/docs/credits/index.rst @@ -0,0 +1,12 @@ +Credits +======= + +Here we list everything and everybody that has made his/her/its part in improving +**Mayan EDMS** + +.. toctree:: + :maxdepth: 1 + + contributors + software_used + license diff --git a/docs/license.rst b/docs/credits/license.rst similarity index 100% rename from docs/license.rst rename to docs/credits/license.rst diff --git a/docs/credits/software_used.rst b/docs/credits/software_used.rst new file mode 100644 index 0000000000..0725bd6d66 --- /dev/null +++ b/docs/credits/software_used.rst @@ -0,0 +1,156 @@ +============= +Software used +============= + +* Python + + * Copyright (c) 2001-2010 Python Software Foundation. + * Copyright (c) 2000 BeOpen.com. + * Copyright (c) 1995-2001 Corporation for National Research Initiatives. + * Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam. + +* Django - A high-level Python Web framework that encourages rapid development and clean, pragmatic design. + + * Copyright Django Software Foundation + * http://www.djangoproject.com/ + +* django-pagination + + * Copyright Eric Florenzano (floguy@gmail.com) + * http://django-pagination.googlecode.com/ + +* Web App Theme + + * Copyright Andrea Franz (http://gravityblast.com) + * git://github.com/pilu/web-app-theme.git + +* Imagemagick - Convert, Edit, Or Compose Bitmap Images + + * Copyright 1999-2011 ImageMagick Studio LLC + * http://www.imagemagick.org/script/index.php + +* FAMFAMFAM Silk icons + + * Copyright Mark James (http://www.twitter.com/markjames) + * http://www.famfamfam.com/lab/icons/silk/ + +* 3 state FAMFAMFAM Silk icon sets: discrete images and CSS sprite palette + + * Copyright Sky Sanders + * skysanders.net/subtext + +* django-extensions - Extensions for Django + + * Copyright Bas van Oostveen (v.oostveen@gmail.com) + * http://code.google.com/p/django-command-extensions/ + +* django-rosetta - A Django application that eases the translation of Django projects + + * Copyright Marco Bonetti (mbonetti@gmail.com) + * http://code.google.com/p/django-rosetta/ + +* Werkzeug - The Swiss Army knife of Python web development + + * Copyright Armin Ronacher (armin.ronacher@active-4.com) + * http://werkzeug.pocoo.org/ + +* BoundFormWizard - A subclass of Django's FormWizard that handled FormSets. + + * Matthew Flanagan (http://www.blogger.com/profile/15093905875465763876) + * http://code.google.com/p/wadofstuff/ + +* django-filetransfers - File upload/download abstraction + + * Waldemar Kornewald + * http://www.allbuttonspressed.com/projects/django-filetransfers + +* tesseract - An OCR Engine that was developed at HP Labs between 1985 and 1995... and now at Google. + + * http://code.google.com/p/tesseract-ocr/ + +* Image file 1068504_92921456 "Mayan piramid" (Stock Exchange) + + * Andres Ojeda (http://www.sxc.hu/profile/andres_ol) + * http://www.sxc.hu/browse.phtml?f=view&id=1068504 + +* Image 1297211435_error + + * http://kde-look.org/usermanager/search.php?username=InFeRnODeMoN + +* Fat cow icon set + + * http://www.fatcow.com/free-icons + +* Python-magic - python-magic is a simple wrapper for libmagic + + * Adam Hupp + * https://github.com/ahupp/python-magic + +* Fancybox - FancyBox is a tool for displaying images, html content and multi-media in a Mac-style "lightbox" that floats overtop of web page. + + * http://fancybox.net + +* unpaper - post-processing scanned and photocopied book pages + + * Jens Gulden 2005-2007 - unpaper@jensgulden.de. + * http://unpaper.berlios.de/ + +* favicon + + * http://www.iconfinder.com/icondetails/21581/24/draw_pyramid_icon + * Gnome Project + +* MongoDB - (from "humongous") is a scalable, high-performance, open source, document-oriented database. + + * Copyright 10gen + * http://www.mongodb.org/ + +* PyMongo - is a Python distribution containing tools for working with MongoDB, and is the recommended way to work with MongoDB from Python. + + * Copyright 2009, Michael Dirolf + * http://api.mongodb.org/python/ + +* GridFS - is a storage specification for large objects in MongoDB + + * Copyright 10gen + * http://www.mongodb.org/display/DOCS/GridFS+Specification + +* django-sendfile - This is a wrapper around web-server specific methods for sending files to web clients. + + * johnsensible (John Montgomery) + * https://github.com/johnsensible/django-sendfile + +* jQuery-Jail - Jquery Asynchronous Image Loader (JAIL) + + * Sebastiano Armeli-Battana (contact@sebarmeli.com) + * http://www.sebastianoarmelibattana.com/projects/jail + +* django-taggit - is a reusable Django application for simple tagging + + * Alex Gaynor (alex.gaynor@gmail.com) + * http://pypi.python.org/pypi/django-taggit + +* Image 392336_7079 (stock exchange) "Chichen Itza" (Stock Exchange) + + * Joerg Witthoeft "Beamer29" (http://www.sxc.hu/profile/Beamer29) + * http://www.sxc.hu/browse.phtml?f=view&id=392336 + +* Django REST framework - lightweight REST framework for Django, that aims to make it easy to build well-connected, self-describing RESTful Web APIs. + + * http://django-rest-framework.org/ + +* South - intelligent schema and data migrations for Django projects. + + * http://south.aeracode.org/ + +* python-gnupg - a Python library which takes care of the internal details and allows its users to generate and manage keys, encrypt and decrypt data, and sign and verify messages. + + * http://code.google.com/p/python-gnupg/ + +* python-hkp - Python HKP client + + * Copyright Dmitry Gladkov + * https://github.com/dgladkov/python-hkp + + + diff --git a/docs/faq.rst b/docs/faq.rst deleted file mode 100644 index 241e76b4a2..0000000000 --- a/docs/faq.rst +++ /dev/null @@ -1,131 +0,0 @@ -=== -FAQ -=== - -Frequently asked questions and solutions - - - -_mysql_exceptions.OperationalError: (1267, "Illegal mix of collations (latin1_swedish_ci,IMPLICIT) and (utf8_general_ci,COERCIBLE) for operation '='") ------------------------------------------------------------------------------------------------------------------------------------------------------- - - * Solution:: - - $ manage.py shell - >>> from django.db import connection - >>> cursor = connection.cursor() - >>> cursor.execute('SHOW TABLES') - >>> results=[] - >>> for row in cursor.fetchall(): results.append(row) - >>> for row in results: cursor.execute('ALTER TABLE %s CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;' % (row[0])) - - - * References: - - - http://www.djangoshmango.com/?p=99 - - http://stackoverflow.com/questions/1073295/django-character-set-with-mysql-weirdness - - - -Incorrect string value: ``'\xE2\x80\x95rs6...'`` for column ``'content'`` at row 1 ----------------------------------------------------------------------------------- - -When using ``MySQL`` and doing OCR on languages other than English - - * Solution: - - - Use utf-8 collation on MySQL server, or at least in table 'documents_documentpage', 'content' field - - Ref: 1- http://groups.google.com/group/django-users/browse_thread/thread/429447086fca6412 - - Ref: 2- http://markmail.org/message/bqajx2utvmtriixi - -File system links not showing when serving content with ``Samba`` ------------------------------------------------------------------ - - * Solution: - - - Disable unix extensions in the [global] section and enable wide links for the file serving share - - - Example:: - - [global] - unix extensions = no - - ... - - [digitalizacion] - path = /var/local/mayan - guest ok = yes - read only = yes - wide links = yes - follow symlinks = yes - - - - Ref: 1- http://www.samba.org/samba/docs/man/manpages-3/smb.conf.5.html - - -How to store documents outside of **Mayan EDMS's** path -------------------------------------------------------- - - * Sub class Django's ``FileSystemStorage`` class: - - - Create a file called ``customstorage.py``:: - - from django.core.files.storage import FileSystemStorage - - class CustomStorage(FileSystemStorage): - def __init__(self, *args, **kwargs): - super(CustomStorage, self).__init__(*args, **kwargs) - self.location='/new/path/to/documents/' - self.base_url='document_storage' - - - In the ``settings.py`` add:: - - from customstorage import CustomStorage - DOCUMENTS_STORAGE_BACKEND = CustomStorage - - -How to enable the ``GridFS`` storage backend --------------------------------------------- - - * Solution: - - - Add the following lines to ``settings.py``:: - - from storage.backends.gridfsstorage import GridFSStorage - DOCUMENTS_STORAGE_BACKEND = GridFSStorage - - - Filesystem metadata indexing will not work with this storage backend as - the files are inside a ``MongoDB`` database and can't be linked (at least for now) - - -Site search is slow -------------------- - - * Add indexes to the following fields: - - - ``documents_document`` - description, recommended size: 160 - - ``documents_documentmetadata`` - value, recommended size: 80 - - ``documents_documentpage`` - content, recommended size: 3000 - - -How to enable x-sendile support for ``Apache`` ----------------------------------------------- - - * Add the following line to your ``settings.py`` file:: - - SENDFILE_BACKEND = 'sendfile.backends.xsendfile' - - * On your apache configuration file add:: - - XSendFile on - XSendFileAllowAbove on - - -The included version of ``unoconv`` in my distribution is too old -------------------------------------------------------------- - - * Only the file 'unoconv' file from https://github.com/dagwieers/unoconv is needed. - Put it in a user designated directory for binaries such as /usr/local/bin and - setup Mayan's configuration option in your settings_local.py file like this:: - - CONVERTER_UNOCONV_PATH = '/usr/local/bin/unoconv' diff --git a/docs/faq/index.rst b/docs/faq/index.rst new file mode 100644 index 0000000000..f24adbae02 --- /dev/null +++ b/docs/faq/index.rst @@ -0,0 +1,253 @@ +=== +FAQ +=== + +Frequently asked questions and solutions + +Database related +---------------- + +**Q: PostgreSQL vs. MySQL** + +Since Django abstracts database operations from a functional point of view +**Mayan EDMS** will behave exactly the same either way. The only concern +would be that MySQL doesn't support transactions for schema modifying +commands. The only moment this could cause problems is when running +South migrations during upgrades, if a migration fails the database +structure is left in a transitory state and has to be reverted manually +before trying again. + + +**Q: _mysql_exceptions. OperationalError: (1267, "Illegal mix of collations (latin1_swedish_ci, IMPLICIT) and (utf8_general_ci, COERCIBLE) for operation '='")** + +* Solution:: + + $ manage.py shell + >>> from django.db import connection + >>> cursor = connection.cursor() + >>> cursor.execute('SHOW TABLES') + >>> results=[] + >>> for row in cursor.fetchall(): results.append(row) + >>> for row in results: cursor.execute('ALTER TABLE %s CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;' % (row[0])) + + +* References: + + - http://www.djangoshmango.com/?p=99 + - http://stackoverflow.com/questions/1073295/django-character-set-with-mysql-weirdness + + +**Q: Incorrect string value: ``'\xE2\x80\x95rs6...'`` for column ``'content'`` at row 1** + +When using ``MySQL`` and doing OCR on languages other than English + +* Solution: + + - Use utf-8 collation on MySQL server, or at least in table 'documents_documentpage', 'content' field + - Ref: 1- http://groups.google.com/group/django-users/browse_thread/thread/429447086fca6412 + - Ref: 2- http://markmail.org/message/bqajx2utvmtriixi + + +Document sharing +---------------- + +**Q: File system links not showing when serving content with ``Samba``** + +* Solution: + + - Disable unix extensions in the [global] section and enable wide links for the file serving share + - Example:: + + [global] + unix extensions = no + + ... + + [digitalizacion] + path = /var/local/mayan + guest ok = yes + read only = yes + wide links = yes + follow symlinks = yes + + + - Ref: 1- http://www.samba.org/samba/docs/man/manpages-3/smb.conf.5.html + + +Document handling +----------------- + +**How to store documents outside of **Mayan EDMS's** path** + +* Sub class Django's ``FileSystemStorage`` class: + + - Create a file called ``customstorage.py``:: + + from django.core.files.storage import FileSystemStorage + + class CustomStorage(FileSystemStorage): + def __init__(self, *args, **kwargs): + super(CustomStorage, self).__init__(*args, **kwargs) + self.location='/new/path/to/documents/' + self.base_url='document_storage' + + - In the ``settings.py`` add:: + + from customstorage import CustomStorage + DOCUMENTS_STORAGE_BACKEND = CustomStorage + + +**Q: How to enable the ``GridFS`` storage backend** + +* Solution: + + - Add the following lines to ``settings.py``:: + + from storage.backends.gridfsstorage import GridFSStorage + DOCUMENTS_STORAGE_BACKEND = GridFSStorage + - Filesystem metadata indexing will not work with this storage backend as + the files are inside a ``MongoDB`` database and can't be linked (at least for now) + +**Q: How do you upload a new version of an existing file?** + +* Solution: + + - Choose a document, and go to the versions tab, on the right menu at + the bottom under ``Other available action`` there is + ``Upload new version``. Clicking it will take you to a very similar + view as the ``Upload new document`` but you will be able to specify + version number and comments for the new version being uploaded. + +**Q: Site search is slow** + +* Add indexes to the following fields: + + - ``documents_document`` - description, recommended size: 160 + - ``documents_documentpage`` - content, recommended size: 3000 + +This is a temporary solution as **Mayan EDMS** will soon be moving to a +specialized full text search solution. + + +Webserver +--------- + +**Q: How to enable x-sendile support for ``Apache``** + +* If using Ubuntu execute the following:: + + $ sudo apt-get install libapache2-mod-xsendfile + +* Add the following line to your ``settings.py`` file:: + + SENDFILE_BACKEND = 'sendfile.backends.xsendfile' + +* On your apache configuration file add:: + + XSendFile on + XSendFileAllowAbove on + + +OCR +--- + +**Q: The included version of ``unoconv`` in my distribution is too old** + +* Only the file 'unoconv' file from https://github.com/dagwieers/unoconv is needed. + Put it in a user designated directory for binaries such as /usr/local/bin and + setup Mayan's configuration option in your settings_local.py file like this:: + + CONVERTER_UNOCONV_PATH = '/usr/local/bin/unoconv' + +If you already have office or text documents uploaded into **Mayan EDMS**, +after setting up and testing ``unoconv`` by hand, go to 'Tools', +'Maintenance', 'Update office documents' page count', this will force a +re-detection and re-processing of any document found to be of office format. + + +Deployments +----------- + +**Q: Is virtualenv required as specified in the documentation?** + +* It is not necessary, it's just a strong recommendation mainly to reduce + dependency conflicts by isolation from the main Python system install. + If not using a virtualenv, pip would install Mayan's dependencies + globally coming in conflict with the distribution's prepackaged Python + libraries messing other Django projects or Python programs, or another + later Python/Django project dependencies coming into conflict causing + Mayan to stop working for no apparent reason. + + +**Q: Mayan EDMS installed correctly and works, but static files are not served** + +Django's development server doesn't serve static files unless the ``DEBUG`` +option is set to ``True``, this mode of operation should only be used for +development or testing. For production deployments the management command:: + + $ ./manage.py collectstatic + +should be used and the resulting ``static`` folder served from a webserver. +For more information, read https://docs.djangoproject.com/en/dev/howto/static-files/ +and https://docs.djangoproject.com/en/1.2/howto/static-files/ or +http://mayan-edms-ru.blogspot.com/2011/11/blog-post_09.html + + +Other +----- + + +**Q: How to connect Mayan EDMS to an Active Directory tree** + +I used these two libraries as they seemed the most maintained from the quick search I did. + +* http://www.python-ldap.org/ +* http://packages.python.org/django-auth-ldap/ + +After figuring out the corresponding OU, CN and such (which took quite a while since I'm not well versed in LDAP). For configuration options, Mayan EDMS imports settings_local.py after importing settings.py to allow users to override the defaults without modifying any file tracked by Git, this makes upgrading by using Git's pull command extremely easy. My settings_local.py file is as follows: + +:: + + import ldap + from django_auth_ldap.config import LDAPSearch + + # makes sure this works in Active Directory + ldap.set_option(ldap.OPT_REFERRALS, 0) + + AUTH_LDAP_SERVER_URI = "ldap://172.16.XX.XX:389" + AUTH_LDAP_BIND_DN = 'cn=Roberto Rosario Gonzalez,ou=Aguadilla,ou=XX,ou=XX,dc=XX,dc=XX,dc=XX' + AUTH_LDAP_BIND_PASSWORD = 'XXXXXXXXXXXXXX' + AUTH_LDAP_USER_SEARCH = LDAPSearch('dc=XX,dc=XX,dc=XX', ldap.SCOPE_SUBTREE, '(SAMAccountName=%(user)s)') + + # Populate the Django user from the LDAP directory. + AUTH_LDAP_USER_ATTR_MAP = { + "first_name": "givenName", + "last_name": "sn", + "email": "mail" + } + + # This is the default, but I like to be explicit. + AUTH_LDAP_ALWAYS_UPDATE_USER = True + + AUTHENTICATION_BACKENDS = ( + 'django_auth_ldap.backend.LDAPBackend', + 'django.contrib.auth.backends.ModelBackend', + ) + + + +if your organization policies don't allow anonymous directory queries, +create a dummy account and set the ``AUTH_LDAP_BIND_DN`` and +``AUTH_LDAP_BIND_PASSWORD`` options to match the account. + +For a more advanced example check this StackOverflow question: +http://stackoverflow.com/questions/6493985/django-auth-ldap + + +**Q: Can you change the display order of documents...i.e can they be in alphabetical order?** + +A the moment no, but it is something being considered. + + + + diff --git a/docs/features.rst b/docs/features.rst deleted file mode 100644 index e272d75e40..0000000000 --- a/docs/features.rst +++ /dev/null @@ -1,100 +0,0 @@ -======== -Features -======== - - -* Document versioning. - - * Store many versions of the same document, download or revert to a previous version. - -* Electronic signature verification. - - * Check the authenticity of documents by verifying their embedded cryptographic signatures or upload detached signatures for document signed after they were stored. - -* Collaboration tools. - - * Discuss documents, comment on new version of a document. - -* Office document format support. - - * Word processing files? Spreadsheets? Sresentations? They are supported too. - -* User defined metadata fields and meta data sets. - - * Metadata fields can be grouped into sets per technical, legal or structural requirements such as the `Dublin core`_ - -.. _`Dublin core`: http://dublincore.org/metadata-basics/ - -* Dynamic default values for metadata. - - * Metadata fields can have an initial value which can be static or determined by an user provided Python code snipped. - -* Filesystem integration. - - * If enabled, the document database index can be mirrored in the filesystem of the hosting computers and shared via Samba_ or any other method to clients computers on a network. - -.. _Samba: http://www.samba.org/ - -* User defined document unique identifier and checksum algorithms. - - * Users can alter the default method used to uniquely indentify documents. - -* Documents can be uploaded from different sources. - - * Local file or server side file uploads. - -* Batch upload many documents with the same metadata. - - * Clone a document's metadata for speedier uploads and eliminate repetitive data entry. - -* Previews for a great deal of image formats, including PDF. - - * **Mayan EDMS** provides different file conversion backends with different levels of functionality and requirements to adapt to different deployment environments. - -* Full text searching. - - * Document can be searched by their text content, their metadata or any other file attribute such as name, extension, etc. - -* Configurable document grouping. - - * Automatic linking of documents based on metadata values or document properties. - -* Roles support. - - * Users can created an unlimited amount of different roles and are not restricted to the traditional admin, operator, guest paradigm. - -* Fine grained permissions system. - - * There is a permission for every atomic operation performed by users. - -* Multi page document support. - - * Multiple page PDFs and TIFFs files supported. - -* Distributed OCR processing. - - * The task of transcribing text from documents via OCR can be distributed among several physical or virtual computers to decrease load and increase availability. - -* Multilingual user interface (English, Spanish, Portuguese, Russian). - - * **Mayan EDMS** is written using the Django_ framework which natively support Unicode, this coupled with the use of text templates allows **Mayan EDMS** to be translated to practically any language spoken in the world, by default four translations are provided: English, Spanish, Portuguese and Russian. - -.. _Django: https://www.djangoproject.com/ - -* Multilingual OCR support. - - * *As supported by the OCR engine tesseract. - -* Duplicated document search. - -* Plugable storage backends (File based and GridFS included). - - * Very easy to convert other 3rd party such as the ones available for Amazon EC2. - -* Color coded tagging. - - * Labeled and color coded tags that are intituitive. - -* Staging folders to receive scanned documents directly from network attached scanners. - - * Preview scanned files even before uploading them. diff --git a/docs/figure_src/ACL.svg b/docs/figure_src/ACL.svg new file mode 100644 index 0000000000..f0d4c66162 --- /dev/null +++ b/docs/figure_src/ACL.svg @@ -0,0 +1,594 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + Roles + Groups + Users + Users + Groups + Users + Permissions + Documents + Tags + Folders + Tier 1: Actor + Tier 2: Access + Tier 3: Object + + + diff --git a/docs/figure_src/index_instance.svg b/docs/figure_src/index_instance.svg new file mode 100644 index 0000000000..938d7edccf --- /dev/null +++ b/docs/figure_src/index_instance.svg @@ -0,0 +1,724 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + due_data = '1/1/2012'supplier = 'ABC inc' + due_date = '2/1/2012'supplier = 'Z inc' + Documents + Sample index + 'Due dates' + '1/1/2012' + Document A + Document B + '2/1/2012' + Document A + Document B + 'Suppliers' + 'ABC inc' + 'Z inc' + Document A + Document B + + due_date = '1/1/2012'supplier = 'Z inc' + Document C + + Document C + + Document C + Index (generated) + + + + + + + + + + + + + + + + + + + + + + + Sample index + 'Due dates' + metadata.due_date + 'Suppliers' + metadata.supplier + Index template + + + + + + + + + + + diff --git a/docs/figure_src/index_template.svg b/docs/figure_src/index_template.svg new file mode 100644 index 0000000000..9a586b74db --- /dev/null +++ b/docs/figure_src/index_template.svg @@ -0,0 +1,204 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + Sample index + 'Due dates' + metadata.due_date + 'Suppliers' + metadata.supplier + Index template + + + + + + diff --git a/docs/figure_src/indexes.svg b/docs/figure_src/indexes.svg new file mode 100644 index 0000000000..f08d8ab0fd --- /dev/null +++ b/docs/figure_src/indexes.svg @@ -0,0 +1,513 @@ + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + Database + Index templates + Documents + Generated indexes + Disk + Directory tree(that mirrors an index) + + + + File servingsoftware + + Mayan EDMS server or cluster + + Clients + Networks attached drivers + + + + + + + + + Continuous synchonizationof generated indexes anddirectory tree + Document usage with existing software + Users don't needto learn new software + + + diff --git a/docs/figure_src/permissions.svg b/docs/figure_src/permissions.svg new file mode 100644 index 0000000000..3588418dfd --- /dev/null +++ b/docs/figure_src/permissions.svg @@ -0,0 +1,392 @@ + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + Users + Users + Groups + Roles + Permissions + Tier 1: Actor + Tier 2: Access + + + diff --git a/docs/figure_src/versioning.svg b/docs/figure_src/versioning.svg new file mode 100644 index 0000000000..e1f16f0618 --- /dev/null +++ b/docs/figure_src/versioning.svg @@ -0,0 +1,214 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + Original version: 1.0.0 + + New major version: 2.0.0 + + New minor version: 1.1.0 + + New micro version: 1.0.1 + + + + diff --git a/docs/index.rst b/docs/index.rst index 5c115df121..0079a21776 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,49 +1,96 @@ -======== -Overview -======== -Open source, Django_ based document manager with custom metadata indexing, file serving integration and OCR_ capabilities. +.. _index: -.. _Django: http://www.djangoproject.com/ +======================== +Mayan EDMS documentation +======================== +.. rubric:: `Open source`_, Django_ based document manager with custom + metadata_ indexing_, file serving integration, OCR_ capabilities, + document versioning_ and `digital signature verification`_. -:Website: http://bit.ly/mayan-edms -:Source: http://github.com/rosarior/mayan -:Video: http://bit.ly/pADNXv -:Issue tracker: http://github.com/rosarior/mayan/issues - - -**Mayan EDMS** started as a simple project whose only requirement was the storage of PDF files, from there it has grown into a complete electronic document management solution. -**Mayan EDMS** can optimize an organization's bulk upload, storage and retrieval or documents. -Documents are organized using document classes, user defined metadata fields as well as automatic document grouping and indexing. Documents can be retrieved from the document index or by means of full -text searching. Users can search for terms in the document's metadata, properties or contents extracted from PDFs or transcribed by OCR_. **Mayan EDMS** is written in Python_ using the Django_ framework, which makes it very agile and fast, specially when compared with existing Java based solutions. -Being based on patent free, `Open source`_ technologies, **Mayan EDMS** provides legal safety to users and organizations, as well as peace of mind as documents and all related information is stored in open source and transparent formats allowing portability and avoiding `vendor lock-in`_. -Being written using Python_, **Mayan EDMS** runs on many POSIX compliant operating systems, this coupled with many configuration parameters, allows **Mayan EDMS** to be deployed on many hardware and software configurations such as single server based, clusters, virtualized and cloud based hosting giving adopters the choice of using the infrastructure of their choice. -On hosting providers that support Django_ such as DjangoZoom_, **Mayan EDMS** can be deployed in under 2 minutes with just a few clicks of the mouse [#]_. - -.. [#] "Deploying Mayan EDMS on DjangoZoom.com" @ Youtube (http://bit.ly/mayan-djangozoom) -.. _`vendor lock-in`: https://secure.wikimedia.org/wikipedia/en/wiki/Vendor_lock-in -.. _Python: http://www.python.org/ .. _Django: http://www.djangoproject.com/ .. _OCR: https://secure.wikimedia.org/wikipedia/en/wiki/Optical_character_recognition -.. _`Open source`: https://secure.wikimedia.org/wikipedia/en/wiki/Open_source -.. _DjangoZoom: http://djangozoom.com/ +.. _digital signature verification: http://en.wikipedia.org/wiki/Digital_signature +.. _versioning: http://en.wikipedia.org/wiki/Versioning +.. _metadata: http://en.wikipedia.org/wiki/Metadata +.. _indexing: http://en.wikipedia.org/wiki/Index_card +.. _Open source: http://en.wikipedia.org/wiki/Open_source + +On the Web +===================== + +* Website: http://www.mayan-edms.com +* Source: http://github.com/rosarior/mayan +* Video: http://bit.ly/Mayan-Intro + +Looking for specific information? Try the :doc:`detailed table of contents ` otherwise below are the different part of the documentation. + +First steps +=========== + + :doc:`Overview ` | + :doc:`Features ` | + :doc:`Requirements ` | + :doc:`Installation ` -======== -Contents -======== +Understanding Mayan EDMS +======================== + :doc:`File storage ` | + :doc:`Initial data loading ` | + :doc:`Permission system ` | + :doc:`Transformations ` | + :doc:`Document visualization ` | + :doc:`Document versioning ` | + :doc:`Document signatures ` | + :doc:`Indexes ` | + :doc:`Smart links ` | + :doc:`OCR ` + + +Between versions +================ .. toctree:: :maxdepth: 2 - features - requirements - installation - settings - updates - development - contributors - credits - faq - license + releases/index + + +Customization and fine tunning +============================== + + :doc:`Settings ` | :doc:`Customization ` + + +For developers +============== + + :doc:`Development ` | :doc:`Documentation ` | :doc:`Translations ` + + +Credits +======= + + :doc:`Contributors ` | + :doc:`Software used ` | + :doc:`Licensing ` + + +Getting help +============ + +Having trouble? We'd like to help! + +* Try the :doc:`FAQ ` -- it's got answers to many common questions. + +* Search for information in the `archives of the mayan-edms mailing list`_, or + `post a question`_. If you prefer news servers, use the gateway provided by Gname_. + +* Report bugs with **Mayan EDMS** using Github's `ticket tracker`_. + +.. _archives of the mayan-edms mailing list: http://groups.google.com/group/mayan-edms/ +.. _Gname: http://news.gmane.org/gmane.comp.python.django.mayan-edms +.. _post a question: http://groups.google.com/group/mayan-edms +.. _ticket tracker: http://github.com/rosarior/mayan/issues diff --git a/docs/intro/features.rst b/docs/intro/features.rst new file mode 100644 index 0000000000..fc8a34139e --- /dev/null +++ b/docs/intro/features.rst @@ -0,0 +1,100 @@ +======== +Features +======== + +* :doc:`Document versioning <../topics/versioning>`. + + * Store many versions of the same document, download or revert to a previous version. + +* :doc:`Electronic signature verification <../topics/signatures>`. + + * Check the authenticity of documents by verifying their embedded + cryptographic signatures or upload detached signatures for document + signed after they were stored. + +* Collaboration tools. + + * Discuss documents, comment on new version of a document. + +* Office document format support. + + * Word processing files? Spreadsheets? Presentations? They are supported too. + +* User defined metadata fields and meta data sets. + + * Metadata fields can be grouped into sets per technical, legal or structural requirements such as the `Dublin core`_ + +* Dynamic default values for metadata. + + * Metadata fields can have an initial value which can be static or determined by an user provided Python code snipped. + +* Filesystem integration. + + * If enabled, the document database index can be mirrored in the filesystem of the hosting computers and shared via Samba_ or any other method to clients computers on a network. + +* User defined document unique identifier and checksum algorithms. + + * Users can alter the default method used to uniquely indentify documents. + +* Documents can be uploaded from different sources. + + * Local file or server side file uploads. + +* Batch upload many documents with the same metadata. + + * Clone a document's metadata for speedier uploads and eliminate repetitive data entry. + +* Previews for a great deal of image formats, including PDF. + + * **Mayan EDMS** provides different file conversion backends with different levels of functionality and requirements to adapt to different deployment environments. + +* Full text searching. + + * Document can be searched by their text content, their metadata or any other file attribute such as name, extension, etc. + +* Configurable document grouping. + + * Automatic linking of documents based on metadata values or document properties. + +* :doc:`Roles support <../topics/permissions>`. + + * Users can created an unlimited amount of different roles and are not restricted to the traditional admin, operator, guest paradigm. + +* :doc:`Fine grained permissions system <../topics/permissions>`. + + * There is a permission for every atomic operation performed by users. + +* Multi page document support. + + * Multiple page PDFs and TIFFs files supported. + +* :doc:`Distributed OCR processing <../topics/ocr>`. + + * The task of transcribing text from documents via OCR can be distributed among several physical or virtual computers to decrease load and increase availability. + +* Multilingual user interface (English, Spanish, Portuguese, Russian, Polish). + + * **Mayan EDMS** is written using the Django_ framework which natively support Unicode, this coupled with the use of text templates allows **Mayan EDMS** to be translated to practically any language spoken in the world, by default four translations are provided: English, Spanish, Portuguese and Russian. + +* :doc:`Multilingual OCR support <../topics/ocr>`. + + * As supported by the OCR engine tesseract. + +* Duplicated document search. + +* :doc:`Plugable storage backends <../topics/file_storage>` (File based and GridFS included). + + * Very easy to use 3rd party plugins such as the ones available for Amazon EC2. + +* Color coded tagging. + + * Labeled and color coded tags that are intituitive. + +* Staging folders to receive scanned documents directly from network attached scanners. + + * Preview scanned files even before uploading them. + + +.. _`Dublin core`: http://dublincore.org/metadata-basics/ +.. _Samba: http://www.samba.org/ +.. _Django: https://www.djangoproject.com/ diff --git a/docs/intro/index.rst b/docs/intro/index.rst new file mode 100644 index 0000000000..de2ea70513 --- /dev/null +++ b/docs/intro/index.rst @@ -0,0 +1,33 @@ +Getting started +=============== + +New to Mayan EDMS? Read this material to quickly get up and running. + +.. toctree:: + :maxdepth: 1 + + overview + features + requirements + installation + + +.. seealso:: + + If you're new to Python_, you might want to start by getting an idea of what + the language is like. Mayan EDMS is 100% Python, so if you've got minimal + comfort with Python you'll probably get a lot more out of Mayan EDMS. + + If you're new to programming entirely, you might want to start with this + `list of Python resources for non-programmers`_ + + If you already know a few other languages and want to get up to speed with + Python quickly, we recommend `Dive Into Python`_ (also available in a + `dead-tree version`_). If that's not quite your style, there are quite + a few other `books about Python`_. + + .. _python: http://python.org/ + .. _list of Python resources for non-programmers: http://wiki.python.org/moin/BeginnersGuide/NonProgrammers + .. _dive into python: http://diveintopython.net/ + .. _dead-tree version: http://www.amazon.com/exec/obidos/ASIN/1590593561/ref=nosim/jacobian20 + .. _books about Python: http://wiki.python.org/moin/PythonBooks diff --git a/docs/installation.rst b/docs/intro/installation.rst similarity index 53% rename from docs/installation.rst rename to docs/intro/installation.rst index 98e96a283b..3d9056fab7 100644 --- a/docs/installation.rst +++ b/docs/intro/installation.rst @@ -36,10 +36,15 @@ If using the ``MySQL`` database manager, use the following commands:: $ apt-get install python-dev libmysqlclient-dev gcc -y $ pip install MySQL-python + +If using PostgreSQL, enter the following:: + + $ apt-get install python-dev libpq-dev gcc-y + $ pip install pip install psycopg2 Populate the database with the project's schema doing:: - $ ./manage.py syncdb + $ ./manage.py syncdb --migrate Collect the static files of the project into the ``static`` folder for serving via a webserver:: @@ -52,25 +57,25 @@ Webfaction To install **Mayan EDMS** on Webfaction_, follow these steps: -#. Create a new database: +1. Create a new database: - * Enter the following selections: + * Enter the following selections: - * Type:* ``Mysql`` - * Name:* ``_mayan`` - * Encoding:* ``utf-8`` + * Type:* ``Mysql`` + * Name:* ``_mayan`` + * Encoding:* ``utf-8`` - * Anotate the provided password. + * Anotate the provided password. -#. Create a new app: +2. Create a new app: - * Enter the following in the textbox: + * Enter the following in the textbox: - * Name:* ``mayan`` - * App category:* ``mod_wsgi`` - * App type:* ``mod_wsgi 3.3/Python 2.7`` + * Name:* ``mayan_app`` + * App category:* ``mod_wsgi`` + * App type:* ``mod_wsgi 3.3/Python 2.7`` -#. Login via ssh, and execute:: +3. Login via ssh, and execute:: $ easy_install-2.7 virtualenv $ cd ~/webapps/mayan_app @@ -81,63 +86,81 @@ To install **Mayan EDMS** on Webfaction_, follow these steps: $ source ../bin/activate $ pip install -r requirements/production.txt -#. Install the Python MySQL database driver:: +4. Install the Python MySQL database driver:: $ pip install MySQL-python -#. Create a settings_local.py file, and paste into it the following:: +5. Create a settings_local.py file, and paste into it the following:: - $ DATABASES = { - $ 'default': { - $ 'ENGINE': 'django.db.backends.mysql', - $ 'NAME': '_mayan', - $ 'USER': '_mayan', - $ 'PASSWORD': '', - $ 'HOST': '', - $ 'PORT': '', - $ } - $ } + DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.mysql', + 'NAME': '_mayan', + 'USER': '_mayan', + 'PASSWORD': '', + 'HOST': '', + 'PORT': '', + } + } -#. Create the database schema (during this step two errors will appears about failling to install indexes on ``documents.Document`` and ``documents.DocumentPage`` models, ignore them for now):: +6. Create the database schema:: - $ ./manage.py syncdb + $ ./manage.py syncdb --migrate -#. Collect the static files of the apps:: +7. Collect the static files of the apps:: $ ./manage.py collectstatic -l --noinput -#. Create a new app: +8. Create a new app: - * Enter the following: + * Enter the following: - * Name:* ``mayan_static`` - * App category:* ``Symbolic link`` - * App type:* ``Symbolic link to static-only app`` - * Extra info: ``/home//webapps/mayan_app/mayan/mayan/static`` + * Name:* ``mayan_static`` + * App category:* ``Symbolic link`` + * App type:* ``Symbolic link to static-only app`` + * Extra info: ``/home//webapps/mayan_app/mayan/mayan/static`` -#. Create the website: +9. Create the website: - * Name: ``mayan_edms`` - * Choose a subdomain - * Under ``Site apps:`` enter the following selections: + * Name: ``mayan_edms`` + * Choose a subdomain + * Under ``Site apps:`` enter the following selections: - * App #1 + * App #1 - * App:* ``mayan_app`` - * URL path (ex: '/' or '/blog'):* ``/`` + * App:* ``mayan_app`` + * URL path (ex: '/' or '/blog'):* ``/`` - * App #2 + * App #2 - * App:* ``mayan_static`` - * URL path (ex: '/' or '/blog'):* ``/mayan-static`` + * App:* ``mayan_static`` + * URL path (ex: '/' or '/blog'):* ``/mayan-static`` -#. Edit the file ``~/webapps/mayan_app/apache2/conf/httpd.conf``: +10. Edit the file ``~/webapps/mayan_app/apache2/conf/httpd.conf``: - * Disable the ``DirectoryIndex`` line and the ``DocumentRoot`` line - * Add the following line:: + * Disable the ``DirectoryIndex`` line and the ``DocumentRoot`` line. + * Add the following line:: - WSGIScriptAlias / /home//webapps/mayan_app/mayan/mayan/wsgi/dispatch.wsgi + WSGIScriptAlias / /home//webapps/mayan_app/mayan/mayan/wsgi/dispatch.wsgi + + * Tune your WSGI process to only use 2 workers (as explained here: `Reducing mod_wsgi Memory Consumption`_) + to keep the memory usage under the basic 256MB of RAM provided or upgrade your plan to 512MB, + the line that controls the amount of workers launched is:: + + WSGIDaemonProcess mayan_app processes=5 python-path=/home//webapps/mayan_app/lib/python2.7 threads=1 + + change it to:: + + WSGIDaemonProcess mayan_app processes=2 python-path=/home//webapps/mayan_app/lib/python2.7 threads=1 + +11. Restart your apache instance: + + * Execute:: + + apache2/bin/restart + + DjangoZoom ---------- For instructions on how to deploy **Mayan EDMS** on DjangoZoom, watch the screencast: @@ -152,6 +175,7 @@ For instructions on how to deploy **Mayan EDMS** on DjangoZoom, watch the screen .. _`Open source`: https://secure.wikimedia.org/wikipedia/en/wiki/Open_source .. _DjangoZoom: http://djangozoom.com/ .. _Youtube: http://bit.ly/mayan-djangozoom +.. _Django: http://www.djangoproject.com/ .. _Apache: https://www.apache.org/ @@ -160,5 +184,5 @@ For instructions on how to deploy **Mayan EDMS** on DjangoZoom, watch the screen .. _Download: https://github.com/rosarior/mayan/archives/master .. _Webfaction: http://www.webfaction.com .. _deployed: https://docs.djangoproject.com/en/1.3/howto/deployment/ -.. _Django: https://www.djangoproject.com .. _virtualenv: http://www.virtualenv.org/en/latest/index.html +.. _`Reducing mod_wsgi Memory Consumption`: http://docs.webfaction.com/software/mod-wsgi.html#mod-wsgi-reducing-memory-consumption diff --git a/docs/intro/overview.rst b/docs/intro/overview.rst new file mode 100644 index 0000000000..f79fa55471 --- /dev/null +++ b/docs/intro/overview.rst @@ -0,0 +1,19 @@ +==================== +History and overview +==================== + +**Mayan EDMS** started as a simple project whose only requirement was the storage of PDF files, from there it has grown into a complete electronic document management solution. +**Mayan EDMS** can optimize an organization's bulk upload, storage and retrieval or documents. +Documents are organized using document classes, user defined metadata fields as well as automatic document grouping and indexing. Documents can be retrieved from the document index or by means of full +text searching. Users can search for terms in the document's metadata, properties or contents extracted from PDFs or transcribed by OCR_. **Mayan EDMS** is written in Python_ using the Django_ framework, which makes it very agile and fast, specially when compared with existing Java based solutions. +Being based on patent free, `Open source`_ technologies, **Mayan EDMS** provides legal safety to users and organizations, as well as peace of mind as documents and all related information is stored in open source and transparent formats allowing portability and avoiding `vendor lock-in`_. +Being written using Python_, **Mayan EDMS** runs on many POSIX compliant operating systems, this coupled with many configuration parameters, allows **Mayan EDMS** to be deployed on many hardware and software configurations such as single server based, clusters, virtualized and cloud based hosting giving adopters the choice of using the infrastructure of their choice. +On hosting providers that support Django_ such as DjangoZoom_, **Mayan EDMS** can be deployed in under 2 minutes with just a few clicks of the mouse [#]_. + +.. [#] "Deploying Mayan EDMS on DjangoZoom.com" @ Youtube (http://bit.ly/mayan-djangozoom) +.. _`vendor lock-in`: https://secure.wikimedia.org/wikipedia/en/wiki/Vendor_lock-in +.. _Python: http://www.python.org/ +.. _Django: http://www.djangoproject.com/ +.. _OCR: https://secure.wikimedia.org/wikipedia/en/wiki/Optical_character_recognition +.. _`Open source`: https://secure.wikimedia.org/wikipedia/en/wiki/Open_source +.. _DjangoZoom: http://djangozoom.com/ diff --git a/docs/requirements.rst b/docs/intro/requirements.rst similarity index 90% rename from docs/requirements.rst rename to docs/intro/requirements.rst index 665b9ba641..4b4cf9b00d 100644 --- a/docs/requirements.rst +++ b/docs/intro/requirements.rst @@ -28,12 +28,6 @@ Executables: Optional requirements ===================== -To enable distributed OCR support ---------------------------------- - -* ``celery`` - asynchronous task queue/job queue based on distributed message passing -* ``django-celery`` - celery integration for Django - To store documents in a GridFS database --------------------------------------- diff --git a/docs/releases/0.10.1.rst b/docs/releases/0.10.1.rst new file mode 100644 index 0000000000..d038b853f7 --- /dev/null +++ b/docs/releases/0.10.1.rst @@ -0,0 +1,10 @@ +Version 0.10.1 +-------------- +* Upgraded django-compressor to version 1.1.1, run:: + + $ pip install --upgrade -r requirements/production.txt + + to upgrade + +* django-compressor is now disabled by default, users must explicitly + enable it adding COMPRESS_ENABLED=True to their settings_local.py file diff --git a/docs/releases/0.10.rst b/docs/releases/0.10.rst new file mode 100644 index 0000000000..cbe2f65ff5 --- /dev/null +++ b/docs/releases/0.10.rst @@ -0,0 +1,51 @@ +Version 0.10 +------------ +* Added a proper setup views for the document grouping functionality. +* Document grouping is now called smart linking as it relates better to + how it actually works. The data base schema was changed and users must + do the required:: + + $ ./manager syncdb + + for the new tables to be created. +* Grappelli is no longer required as can be uninstalled. +* New smarter document preview widget that doesn't allow zooming or viewing + unknown or invalid documents. +* New office document converter, requires: + + * LibreOffice (https://www.libreoffice.org/) + * unoconv [version 0.5] (https://github.com/dagwieers/unoconv) + +* The new office documents converter won't convert files with the extension + .docx because these files are recognized as zip files instead. This + is an issue of the libmagic library. + +* New configuration option added ``CONVERTER_UNOCONV_USE_PIPE`` that controls + how unoconv handles the communication with LibreOffice. The default of + ``True`` causes unoconv to use **pipes**, this approach is slower than using + **TCP/IP** ports but it is more stable. + +* Initial `REST` `API` that exposes documents properties and one method, this + new `API` is used by the new smart document widget and requires the + package ``djangorestframework``, users must issue a:: + + $ pip install -r requirements/production.txt + + to install this new requirement. + +* MIME type detection and caching performance updates. +* Updated the included version of ``jQuery`` to 1.7 +* Updated the included version of ``JqueryAsynchImageLoader`` to 0.9.7 +* Document image serving response now specifies a MIME type for increased + browser compatibility. +* Small change in the scheduler that increases stability. +* Russian translation updates (Сергей Глита [Sergey Glita]) +* Improved and generalized the OCR queue locking mechanism, this should + eliminate any posibility of race conditions between Mayan EDMS OCR nodes. +* Added support for signals to the OCR queue, this results in instant OCR + processing upon submittal of a document to the OCR queue, this works in + addition to the current polling processing which eliminates the + posibility of stale documents in the OCR queue. +* Added multiple document OCR submit link +* Re enabled tesseract language specific OCR processing and added a one + (1) time language neutral retry for failed language specific OCR diff --git a/docs/releases/0.11.1.rst b/docs/releases/0.11.1.rst new file mode 100644 index 0000000000..f618bcf604 --- /dev/null +++ b/docs/releases/0.11.1.rst @@ -0,0 +1,8 @@ +Version 0.11.1 +-------------- +* Fixed a document deletion regression +* Improves error detection when importing keys from a keyserver, catching + the exception KeyImportError and not KeyFetchingError +* Fixes a wrong method call when verifying signatures for the first time upon document uploading +* django-compress is now disabled by default to avoid problems when deploying with DjangoZoom +* Improve post metadata set delete redirection diff --git a/docs/releases/0.11.rst b/docs/releases/0.11.rst new file mode 100644 index 0000000000..df691977e4 --- /dev/null +++ b/docs/releases/0.11.rst @@ -0,0 +1,34 @@ +Version 0.11 +------------ +* Support for signed documents verification added, embedded and detached + signatures are supported. When verifying a document Mayan EDMS will + try to fetch the public key from the list of keyservers provided in the + configuration option SIGNATURES_KEYSERVERS (which defaults to + 'pool.sks-keyservers.net'). A public key management view has been added + to the setup menu as well as a key query and fetching view to manually + import keys from a keyserver. +* Added support for document versions. Users can upload unlimited amount + of versions for a document using a very flexible document version numbering + system, users can also revert to a previous document version. +* OCR queue processing improvements. +* Office documents handling improvements. +* Text extraction support for office documents. +* RTF text documents are now handled as office documents. +* Added a view to delete the document image cache, useful when switching + converter backends or doing diagnostics. +* Added South to the requirements. +* Merged documents' filename and extension database fiels into a single + filename field, filename are store as uploaded not manipulation is done + Users with existing data must install South and run the appropiate + migrate commands:: + + $ pip install -r requirements/production.txt + $ ./manager syncdb + $ ./manage.py migrate documents 0001 --fake + $ ./manage.py migrate documents + +* Added new office document mimetype + * application/vnd.ms-office +* Fixed documents not saving the file encoding +* Removed extra slash in ajax-loader.gif URL fixes #15, thanks to + IHLeanne for finding this one diff --git a/docs/releases/0.12.rst b/docs/releases/0.12.rst new file mode 100644 index 0000000000..d382caf9cb --- /dev/null +++ b/docs/releases/0.12.rst @@ -0,0 +1,232 @@ +============================== +Mayan EDMS v0.12 release notes +============================== + +*February 2012* + +Welcome to Mayan EDMS v0.12! + +This release commemorates **Mayan EDMS** first aniversary! + +Overview +======== + +Aside from new features, the focus of this release of **Mayan EDMS** also +been about improving the code and documentation quality standard +even further. The permission system has been completely overhauled to make +it entire class based. The other big change is the addition of object +level permissions, with this new system being applied to documents, +folder, tags and smart links. There is also a small batch of navigation +improvements. Big code cleanup and lots of changes 'under the hood', +most of these are not visible to the end user, but make the code cleaner +and more manageable so that more and better features can be added in future +releases: + +* Absolute imports used throught the code +* All app permissions have been move to a separate permissions.py file + per app +* Complete permission system refactor. +* Document signining code move to it's own app +* Initial unit tests +* A lot of logging used throught the entire project. +* Much functionality moved to model managers. +* A lot of code converted into classes. +* Coding style improvements. +* Template user authentication state logic improvements, for stonger + prevention against intrusion or unintentional display or access + of restricted data. +* Removal of remarked code. + + +What's new in Mayan EDMS v0.12 +============================== + +ACL support +~~~~~~~~~~~ +* Object level access control is now in place for documents, folders, + tags and smart links. What this means is that administrators can now + grant permissions to users, groups or roles on for specific objects. + A more in-depth explanation of how this new ACL system can be found in + the :doc:`3 tier access control <../topics/permissions>` section of the + permissions chapter. + +* Default class ACL support. Administrators can setup the access control + lists that new documents, folders and tags will automatically inheric + when created. Aside from asigning permission to users, groups and roles + to specific objects, there is a special user called `Creator`, use to + allow the access control list that the actual creator of an object will + inherit. + +Anonymous user support +~~~~~~~~~~~~~~~~~~~~~~ +Anonymous user support is a two tier function, first is the addition of +the :setting:`COMMON_ALLOW_ANONYMOUS_ACCESS` configuration option that +allows non authenticated user to browse all the pages of a **Mayan EDMS** installation. +The second part of this support is the ability to assign permissions +or individual access to objects to anonymous users. + +Translations +~~~~~~~~~~~~~~~~~~~ +A new Italian translation is available, provided by SeeOpen.IT +(www.seeopen.it, info@seeopen.it) as well as complete Russian translation +update by Сергей Глита. Included in this release also the initial translation +to Polish by mic. + +Usability improvements +~~~~~~~~~~~~~~~~~~~~~~ +* Detached signature behavior improved, uploading a new detached signature + erases the previous one. +* Usability improvement in the role member's add/removal form, by using + HTML's option groups tag property + +2 Step download process +~~~~~~~~~~~~~~~~~~~~~~~ +The code for downloading single and multiple document and document versions +has been merged with compression support also added. This allows for the +download of documents in their original format or compressed and well as +the download of several documents in a single compressed file. + +Customizable GPG home directory +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Addition of the :setting:`SIGNATURES_GPG_HOME` configuration option to let +administrators set **Mayan EDMS**'s GPG instance home directory, used to +store keyrings and other GPG configuration files. + +Out of process bulk uploading +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +A management command has been added to help upload a large number of documents +from a compressed file. For information about this new feature check the +:doc:`Initial data loading <../topics/initial_import>` chapter. + + +Out of process user import +~~~~~~~~~~~~~~~~~~~~~~~~~~ +A management command has been added to import a large number users +from a CSV file. More information about this new feature can also be found +in the :doc:`Initial data loading <../topics/initial_import>` chapter. + + +Refactored document indexing +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +:doc:`The document indexing <../topics/indexes>` functionality has been +improved and moved from experimental +stage to beta stage. Index configuration menus are now available on the +``Setup`` menu and allows administrators to create skeleton trees that will +be populated with document links depending on their metadata and properties. +These populated trees can also be mirrored on the physical filesystem and shared +using Samba or another filesharing server giving users a structured view +of the documents contained within **Mayan EDMS** from the ``Indexes`` tab +or from a mirrored index shared via the network. A new configuration option +has been added, :setting:`DOCUMENT_INDEXING_FILESYSTEM_SERVING`, which maps +the index internal name with the physical directory where such index will be +mirrored on disk. + +Help shape Mayan EDMS's future +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Included in this version is a small feedback application, found under the +``About`` main menu, where users by just answering some questions can +help determine the priority of the next planned features on the pipeline, +or even help add new features if enough requests are received. All questions +are optional but answering as many as possible will help greatly understand +the need of the **Mayan EDMS** user base. + +Staging file previews +~~~~~~~~~~~~~~~~~~~~~ +The staging file previews now show the filename for easier +identification and speedier upload selection. The staging files previews +are now treated as a gallery which means that users can preview an entire +page of staging files without having to click and close each one +individually. + + +Upgrading from a previous version +================================= + +Start off by adding the new requirements:: + + $ pip install -r requirements/production.txt + +Then create the new database structures with:: + + $ ./manage.py syncdb + +Afterwards migrate existing database schema with:: + + $ ./manage.py migrate permissions 0001 --fake + $ ./manage.py migrate permissions + + +When the following message appears + +:: + + The following content types are stale and need to be deleted: + + permissions | permission + + Any objects related to these content types by a foreign key will also + be deleted. Are you sure you want to delete these content types? + If you're unsure, answer 'no'. + + Type 'yes' to continue, or 'no' to cancel: + +Type ``yes`` and press **Enter** + +And continue migrating database schema with:: + + $ ./manage.py migrate documents + $ ./manage.py migrate document_signatures + $ ./manage.py migrate folders 0001 --fake + $ ./manage.py migrate folders + $ ./manage.py migrate document_indexing 0001 --fake + $ ./manage.py migrate document_indexing + $ ./manage.py migrate sources 0001 --fake + $ ./manage.py migrate sources + +Again when a similar messages appears +:: + + The following content types are stale and need to be deleted: + + document_indexing | indexinstance + + Any objects related to these content types by a foreign key will also + be deleted. Are you sure you want to delete these content types? + If you're unsure, answer 'no'. + + Type 'yes' to continue, or 'no' to cancel: + +Type ``yes`` and press **Enter** + +The upgrade procedure is now complete. + + +Backward incompatible changes +============================= +The permission system has been completely reworked so sadly this is a +place where even data migration can't help and the permissions assigned +to roles will be lost during the upgrade to version 0.12. Users, groups +and roles will be preserved only permissions need to be assigned again, +so write down your role permission setup before upgrading. + +Bugs fixed +========== +* Issue #17, special thanks to Dave Herring for all the help including + access to a machine suffering with the issue, and to Сергей Глита for + his research and eventual find of the core cause. +* Statistics fixes. +* Fixed get_image_cache_name regression in the OCR app. + +Stuff removed +============= +* Support for Celery and Sentry has been drop + for now. +* Removed the 'db_index' argument from Text fields definition and + migrations as it was causing error messages for MySQL users, thanks to + Сергей Глита for reporting this one. +* Configuration options removed: + + * OCR_CACHE_URI + * DOCUMENT_INDEXING_FILESYSTEM_FILESERVING_PATH - Use the newest :setting:`DOCUMENT_INDEXING_FILESYSTEM_SERVING` + * DOCUMENT_INDEXING_FILESYSTEM_FILESERVING_ENABLE - Use the newest :setting:`DOCUMENT_INDEXING_FILESYSTEM_SERVING` + diff --git a/docs/releases/0.5.1.rst b/docs/releases/0.5.1.rst new file mode 100644 index 0000000000..e003f04ca3 --- /dev/null +++ b/docs/releases/0.5.1.rst @@ -0,0 +1,10 @@ +Version 0.5.1 +------------- +* Applied initial merge of the new subtemplate renderer +* Fixed tag removal logic +* Initial commit to support document comments +* Updated so that loading spinner is displayed always +* Exclude tags from the local document upload form +* Added document tagging support + + - Requires installing ``django-taggit`` and doing a ``sync-db`` diff --git a/docs/releases/0.5.rst b/docs/releases/0.5.rst new file mode 100644 index 0000000000..3081395e65 --- /dev/null +++ b/docs/releases/0.5.rst @@ -0,0 +1,300 @@ +Version 0.5 +----------- +* Added tag list view and global tag delete support +* Added tag editing view and listing documents with an specific tag +* Changed the previewing and deleting staging files views to required + ``DOCUMENT_CREATE`` permission +* Added no-parent-history class to document page links so that iframe clicking doesn't affect the parent window history + + - Fixes back button issue on Chrome 9 & 10 + +* Added per app version display tag +* Added loading spinner animation +* Messages tweaks and translation updates +* Converter app cleanups, document pre-cache, magic number removal +* Added OCR view displaying all active OCR tasks from all cluster nodes +* Disabled ``CELERY_DISABLE_RATE_LIMITS`` by default +* Implement local task locking using Django locmem cache backend +* Added doc extension to office document format list +* Removed redundant transformation calculation +* Make sure OCR in processing documents cannot be deleted +* PEP8, pylint cleanups and removal of relative imports +* Removed the obsolete ``DOCUMENTS_GROUP_MAX_RESULTS`` setting option +* Improved visual appearance of messages by displaying them outside the + main form +* Added link to close all notifications with one click +* Made the queue processing interval configurable by means of a new + setting: ``OCR_QUEUE_PROCESSING_INTERVAL`` +* Added detection and reset of orphaned ocr documents being left as + 'processing' when celery dies +* Improved unknown format detection in the graphicsmagick backend +* Improved document convertion API +* Added initial support for converting office documents (only ods and + docx tested) +* Added sample configuration files for supervisor and apache under + contrib/ +* Avoid duplicates in recent document list +* Added the configuration option CONVERTER_GM_SETTINGS to pass + GraphicsMagicks specific commands the the GM backend +* Lower image convertion quality if the format is jpg +* Inverted the rotation button, more intuitive this way +* Merged and reduced the document page zoom and rotation views +* Increased permissions app permission's label field size + + - DB Update required + +* Added support for metadata group actions +* Reduced the document pages widget size +* Display the metadata group numeric total in the metadata group form + title +* Reorganized page detail icons +* Added first & last page navigation links to document page view +* Added interactive zoom support to document page detail view +* Spanish translation updates +* Added ``DOCUMENTS_ZOOM_PERCENT_STEP``, ``DOCUMENTS_ZOOM_MAX_LEVEL``, + ``DOCUMENTS_ZOOM_MIN_LEVEL`` configuration options to allow detailed + zoom control +* Added interactive document page view rotation support +* Changed the side bar document grouping with carousel style document + grouping form widget +* Removed the obsolete ``DOCUMENTS_TRANFORMATION_PREVIEW_SIZE`` and + ``DOCUMENTS_GROUP_SHOW_THUMBNAIL`` setting options +* Improved double submit prevention +* Added a direct rename field to the local update and staging upload + forms +* Separated document page detail view into document text and document + image views +* Added grab-scroll to document page view +* Disabled submit buttons and any buttons when during a form submit +* Updated the page preview widget to display a infinite-style horizontal + carousel of page previews +* Added support user document folders + + - Must do a ``syncdb`` to add the new tables + +* Added support for listing the most recent accessed documents per user +* Added document page navigation +* Fixed diagnostics url resolution +* Added confirmation dialog to document's find missing document file + diagnostic +* Added a document page edit view +* Added support for the command line program pdftotext from the + poppler-utils packages to extract text from PDF documents without + doing OCR +* Fixed document description editing +* Replaced page break text with page number when displaying document + content +* Implemented detail form readonly fields the correct way, this fixes + copy & paste issues with Firefox +* New document page view +* Added view to add or remove user to a specific role +* Updated the jQuery packages with the web_theme app to version 1.5.2 +* Made ``AVAILABLE_INDEXING_FUNCTION`` setting a setting of the documents + app instead of the filesystem_serving app +* Fixed document download in FireFox for documents containing spaces in + the filename +* If mime detection fails set mime type to '' instead of 'unknown' +* Use document MIME type when downloading otherwise use + 'application/octet-stream' if none +* Changed the way document page count is parsed from the graphics + backend, fixing issue #7 +* Optimized document metadata query and display +* Implemented OCR output cleanups for English and Spanish +* Redirect user to the website entry point if already logged and lands + in the login template +* Changed from using SimpleUploadedFile class to stream file to the + simpler File class wrapper +* Updated staging files previews to use sendfile instead of serve_file +* Moved staging file preview creation logic from documents.views to + staging.py +* When deleting staging file, it's cached preview is also deleted +* Added a new setup option: + + - ``FILESYSTEM_INDEXING_AVAILABLE_FUNCTIONS`` - a dictionary to allow users + to add custom functions + +* Made automatic OCR a function of the OCR app and not of Documents app (via signals) + + - Renamed setup option ``DOCUMENT_AUTOMATIC_OCR`` to ``OCR_AUTOMATIC_OCR`` + +* Clear node name when requeueing a document for OCR +* Added support for editing the metadata of multiple documents at the + same time +* Added Graphics magick support by means of user selectable graphic convertion backends + + - Some settings renamed to support this change: + + + ``CONVERTER_CONVERT_PATH`` is now ``CONVERTER_IM_CONVERT_PATH`` + + ``CONVERTER_IDENTIFY_PATH`` is now ``CONVERTER_IM_IDENTIFY_PATH`` + + - Added options: + + + ``CONVERTER_GM_PATH`` - File path to graphicsmagick's program. + + ``CONVERTER_GRAPHICS_BACKEND`` - Backend to use: ``ImageMagick`` or + ``GraphicMagick`` + +* Raise ImportError and notify user when specifying a non existant + converter graphics backend +* Fixed issue #4, avoid circular import in permissions/__init__.py +* Add a user to a default role only when the user is created +* Added total page count to statistics view +* Added support to disable the default scrolling JS code included in + web_theme app, saving some KBs in transfer +* Clear last ocr results when requeueing a document +* Removed the 'exists' column in document list view, diagnostics + superceded this +* Added 3rd party sendfile app (support apache's X-sendfile) +* Updated the get_document_image view to use the new sendfile app +* Fixed the issue of the strip spaces middleware conflicting with + downloads +* Removed custom IE9 tags +* Closed Issue #6 +* Allow deletion of non existing documents from OCR queue +* Allow OCR requeue of pending documents +* Invalid page numbers now raise Http404, not found instead of error +* Added an additional check to lower the chance of OCR race conditions + between nodes +* Introduce a random delay to each node to further reduce the chance of + a race condition, until row locking can be implemented or is + implemented by Django +* Moved navigation code to its own app +* Reimplemented OCR delay code, only delay new document + Added a new field: delay, update your database schema accordingly +* Made the concurrent ocr code more granular, per node, every node can + handle different amounts of concurrent ocr tasks + Added a new field: node_name, update your database schema acordinging +* Reduced default ocr delay time +* Added a new diagnostics tab under the tools menu +* Added a new option ``OCR_REPLICATION_DELAY`` to allow the storage some + time for replication before attempting to do OCR to a document +* Added OCR multi document re-queue and delete support +* Added simple statistics page (total used storage, total docs, etc) +* Implemented form based and button based multi item actions (button + based by default) +* Added multi document delete +* Fixed a few HTML validation errors +* Issues are now tracked using github +* Added indexing flags to ocr model +* Small optimization in document list view +* Small search optimization +* Display "DEBUG mode" string in title if ``DEBUG`` variable is set to True +* Added the fix-permissions bash script under misc/ folder +* Plugged another file descriptor leak +* Show class name in config settings view +* Added missing config option from the setup menu +* Close file descriptor to avoid leaks +* Don't allow duplicate documents in queues +* Don't raise ``PermissionDenied`` exception in ``PermissionDenied middleware``, + even while debugging +* Fixed page number detection +* Created 'simple document' for non technical users with all of a + document pages content +* Use document preview code for staging file also +* Error picture literal name removal +* Spanish translation updates +* Show document file path in regards of its storage +* Added new setting: side bar search box +* Implemented new ``PermissioDenied`` exception middleware handler +* Permissions app api now returns a ``PermissionDenied`` exception instead + of a custom one +* Added new 403 error template +* Updated the 404 template to display only a not found message +* Moved the login required middleware to the common app +* Fixed search app's model.objects.filter indentation, improved result + count calculation +* Added dynamic comparison types to search app +* Separated search code from view code +* Correctly calculate show result count for multi model searches +* Fixed OCR queue list showing wrong thumbnail +* Fixed staging file preview +* Show current metadata in document upload view sidebar +* Show sentry login for admin users +* Do not reinitialize document queue and/or queued document on reentry +* Try extra hard not to assign same uuid to two documents +* Added new transformation preview size setting +* Renamed document queue state links +* Changed ocr status display sidebar from form based to text based +* Added document action to clear all the document's page transformations +* Allow search across related fields +* Optimzed search for speed and memory footprint +* Added ``LIMIT`` setting to search +* Show search elapsed time on result page +* Converter now differentiates between unknown file format and convert + errors +* Close file descriptors when executing external programs to + prevent/reduce file descriptior leaks +* Improved exception handling of external programs +* Show document thumbnail in document ocr queue list +* Make ocr document date submitted column non breakable +* Fix permissions, directories set to mode 755 and files to mode 644 +* Try to fix issue #2, "random ORM field error on search while doing OCR" +* Added configurable location setting for file based storage +* Prepend storage name to differentiate config options +* Fixed duplicated document search +* Optimized document duplicate search +* Added locale middleware, menu bar language switching works now +* Only show language selection list if localemiddleware is active +* Spanish translation updates +* Added links, views and permissions to disable or enable an OCR queue +* Enabled Django's template caching +* Added document queue property side bar window to the document queue + list view +* Added HTML spaceless middleware to remove whitespace in HTML code +* If current user is superuser or staff show thumbnail & preview + generation error messages +* Added a setting to show document thumbnail in metadata group list +* Started adding configurations setting descriptions +* Initial GridFS storage support +* Implemented size and delete methods for GridFS +* Implement GridFS storage user settings +* Added document link in the OCR document queue list +* Link to manually re queue failed OCR +* Don't separate links (encose object list links with white-space: + nowrap;) +* Added document description to the field search list +* Sort OCR queued documents according to submitted date & time +* Document filesystem serving is now a separate app + + - Steps to update (Some warnings may be returned, but these are not + fatal as they might be related to missing metadata in some documents): + + + rename the following settings: + + + ``DOCUMENTS_FILESYSTEM_FILESERVING_ENABLE`` to ``FILESYSTEM_FILESERVING_ENABLE`` + + ``DOCUMENTS_FILESYSTEM_FILESERVING_PATH`` to ``FILESYSTEM_FILESERVING_PATH`` + + ``DOCUMENTS_FILESYSTEM_SLUGIFY_PATHS`` to ``FILESYSTEM_SLUGIFY_PATHS`` + + ``DOCUMENTS_FILESYSTEM_MAX_RENAME_COUNT`` to ``FILESYSTEM_MAX_RENAME_COUNT`` + + + Do a ./manage.py syncdb + + Execute 'Recreate index links' locate in the tools menu + + Wait a few minutes + +* Added per document duplicate search and a tools menu option to seach + all duplicated documents +* Added document tool that deletes and re-creates all documents + filesystem links +* Increased document's and document metadata index filename field's size + to 255 characters +* Added sentry to monitor and store error for later debugging +* Zip files can now be uncompressed in memory and their content uploaded + individually in one step +* Added support for concurrent, queued OCR processing using celery +* Apply default transformations to document before OCR +* Added unpaper to the OCR convertion pipe +* Added views to create, edit and grant/revoke permissions to roles +* Added multipage documents support (only tested on pdfs) + + - To update a previous database do: [d.update_page_count() for d in Document.objects.all()] + +* Added support for document page transformation (no GUI yet) +* Added permissions and roles support +* Added python-magic for smarter MIME type detection + (https://github.com/ahupp/python-magic). +* Added a new Document model field: file_mime_encoding. +* Show only document metadata in document list view. +* If one document type exists, the create document wizard skips the + first step. +* Changed to a liquid css grid +* Added the ability to group documents by their metadata +* New abstracted options to adjust document conversion quality (default, + low, high) diff --git a/docs/releases/0.7.3.rst b/docs/releases/0.7.3.rst new file mode 100644 index 0000000000..5e02a70106 --- /dev/null +++ b/docs/releases/0.7.3.rst @@ -0,0 +1,32 @@ +Version 0.7.3 +------------- +* Refactored main menu navigation and converted all apps to this new + system +* Multi item links are now displayed on top of generic lists as well as + on the bottom +* Spanish translation updates +* Updated requirements to use the latest development version of + django-mptt +* Improved user folder document removal views +* Added ability to specify default metadata or metadataset per + document type +* Converted filename handling to use os.path library for improved + portability +* Added edit source object attribute difference detection and logging + to history app +* Missing metadata type in a document during a multi document editing doesn't raise errors anymore. + + - This allows for multi document heterogeneous metadata editing in a single step. + +* Added document multi item links in search results + + - Direct editing can be done from the search result list + +* Permissions are now grouped and assigned a group name +* Improved role management views +* Document type is now an optional document property + + - Documents can be created without an explicit document type + +* Added support for per user staging directories +* Updated logos diff --git a/docs/releases/0.7.4.rst b/docs/releases/0.7.4.rst new file mode 100644 index 0000000000..0005fd5358 --- /dev/null +++ b/docs/releases/0.7.4.rst @@ -0,0 +1,6 @@ +Version 0.7.4 +------------- +* Renamed 'secondary actions' to 'secondary menu' +* Added document type setup views to the setup menu +* Added document type file name editing views to the setup menu +* Fixed document queue properties sidebar template not showing diff --git a/docs/releases/0.7.5.rst b/docs/releases/0.7.5.rst new file mode 100644 index 0000000000..9a065aa6d3 --- /dev/null +++ b/docs/releases/0.7.5.rst @@ -0,0 +1,16 @@ +Version 0.7.5 +------------- +* Added a help messages to the sidebar of some views +* Renamed some forms submit button to more intuitive one + + - 'Search' on the submit button of the search form + - 'Next step' on the document creation wizard + +* Added view to list supported file formats and reported by the + converter backend +* Added redirection support to multi object action views +* Renamed 'document list' link to 'all documents' and + 'recent document list' to 'recent documents' +* Removed 'change password' link next to the current user's name and + added a few views to handle the current user's password, details and + details editing diff --git a/docs/releases/0.7.6.rst b/docs/releases/0.7.6.rst new file mode 100644 index 0000000000..4f0607045d --- /dev/null +++ b/docs/releases/0.7.6.rst @@ -0,0 +1,23 @@ +Version 0.7.6 +------------- +* Added recent searches per user support + + - The ammount of searches stored is controlled by the setup option + ``SEARCH_RECENT_COUNT`` + +* The document page zoom button are now disabled when reaching the minimum + or maximum zoom level +* The document page navigation links are now disabled when view the first + and last page of a document +* Document page title now displays the current page vs the total page + count +* Document page title now displays the current zoom level and rotation + degrees +* Added means set the expansion compressed files during document creation, + via web interface removing the need for the configuration options: + ``UNCOMPRESS_COMPRESSED_LOCAL_FILES`` and ``UNCOMPRESS_COMPRESSED_STAGING_FILES`` +* Added 'search again' button to the advances search results view +* Implementes an advanced search feature, which allows for individual field terms + + - Search fields supported: document type, MIME type, filename, + extension, metadata values, content, description, tags, comments diff --git a/docs/releases/0.7.rst b/docs/releases/0.7.rst new file mode 100644 index 0000000000..4fd683c3a1 --- /dev/null +++ b/docs/releases/0.7.rst @@ -0,0 +1,13 @@ +Version 0.7 +----------- +* Added confirmation dialogs icons +* Added comment app with support for adding and deleting comments to + and from documents +* Updated requirements files as per issue #9 +* Show tagged item count in the tag list view +* Show tagget document link in the tags subtemplate of documents +* Made comment sorted by oldest first, made comment subtemplate + scrollable +* Rename comments app to document_comment to avoid conflict with + Django's comment app +* Made document comments searchable diff --git a/docs/releases/0.8.1.rst b/docs/releases/0.8.1.rst new file mode 100644 index 0000000000..e001c9115b --- /dev/null +++ b/docs/releases/0.8.1.rst @@ -0,0 +1,32 @@ +Version 0.8.1 +------------- +* Tags can now also be created from the main menu +* Added item count column to index instance list view +* Updated document indexing widget to show different icon for indexes or + indexes that contain documents +* Replaced the Textarea widget with the TextAreaDiv widget on document + and document page detail views + + - This change will allow highlighting search terms in the future + +* Unknown document file format page count now defaults to 1 + + - When uploading documents which the selected converted backend doesn't + understand, the total page count will fallback to 1 page to at least + show some data, and a comment will be automatically added to the + document description + +* Added new MAIN_DISABLE_ICONS to turn off all icons + + - This options works very well when using the ``default`` theme + +* The default theme is now ``activo`` +* Improved document page views and document page transformation views + navigation +* Added OCR queue document transformations + + - Use this for doing resizing or rotation fixes to improve OCR results + +* Added reset view link to the document page view to reset the zoom + level and page rotation +* Staging files now show a thumbnail preview instead of preview link diff --git a/docs/releases/0.8.2.rst b/docs/releases/0.8.2.rst new file mode 100644 index 0000000000..c4fd0dee5f --- /dev/null +++ b/docs/releases/0.8.2.rst @@ -0,0 +1,24 @@ +Version 0.8.2 +------------- +* Moved code to Django 1.3 + + - Users have to use the ``collectstatic`` management command:: + + $ ./manage.py collectstatic + + - The ``site_media`` directory is no more, users must update the media + serving directives in current deployments and point them to the + ``static`` directory instead + +* The changelog is now available under the ``about`` main menu +* ``Grappelli`` no longer bundled with Mayan + + - Users must install Grappelli or execute:: + + $ pip install --upgrade -r requirements/production.txt + +* Even easier UI language switching +* Added email login method, to enable it, set:: + + AUTHENTICATION_BACKENDS = ('common.auth.email_auth_backend.EmailAuthBackend',) + COMMON_LOGIN_METHOD = 'email' diff --git a/docs/releases/0.8.3.rst b/docs/releases/0.8.3.rst new file mode 100644 index 0000000000..4e9f907a58 --- /dev/null +++ b/docs/releases/0.8.3.rst @@ -0,0 +1,29 @@ +Version 0.8.3 +------------- +* Added a Contributors file under the docs directory +* Moved the document grouping subtemplate windows into a document + information tab +* Change the mode the setup options are shown, opting to use a more of a + dashboard style now +* Changed the tool menu to use the same button layout of the setup menu +* Moved OCR related handling to the tools main menu +* Improved the metadata type and metadata set selection widget during + the document upload wizard +* Added a view to the about menu to read the LICENSE file included with + Mayan +* Added converter backend agnostic image file format descriptions +* Disable whitelist and blacklist temporarily, removed document_type + field from interactive sources +* Fully disabled watch folders until they are working correctly +* Updated the project title to 'Mayan EDMS' +* If ghostscript is installed add PDF and PS to the list of file formats + by the python converter backend +* Use Pillow (http://pypi.python.org/pypi/Pillow) instead of PIL + + - Pillow is a fork of PIL with several updated including better jpeg and png library detection + - Users must uninstall PIL before installing Pillow + +* Updated the static media url in the login excempt url list +* Added remediatory code to sidestep issue #10 caused by DjangoZoom's deployment script executing the collectstatic command before creating the database structure with syncdb. Thanks to Joost Cassee (https://github.com/jcassee) for reporting this one. +* Perform extra validation of the image cache directory and fallback to creating a temporary directory on validation failure +* Fixed a source creation bug, that caused invalid links to a non existing source transformation to appear on the sidebar diff --git a/docs/releases/0.8.rst b/docs/releases/0.8.rst new file mode 100644 index 0000000000..bb70049bfb --- /dev/null +++ b/docs/releases/0.8.rst @@ -0,0 +1,62 @@ +Version 0.8 +----------- +* Distributed OCR queue processing via celery is disabled for the time + being +* Added support for local scheduling of jobs + + - This addition removes celery beat requirement, and make is optional + +* Improve link highlighting +* Navigation improvements +* Documents with an unknown file format now display a mime type place + holder icon instead of a error icon +* Mayan now does pre caching of document visual representation improving + overall thumbnail, preview and display speed + + - Page image rotation and zooming is faster too with this update + +* Removed all QUALITY related settings +* ``COMMON_TEMPORARY_DIRECTORY`` is now validated when Mayan starts and if + not valid falls back to creating it's own temporary folder +* Added PDF file support to the python converter backend via ghostscript + + - This requires the installation of: + + + ghostscript python package + + ghostscript system binaries and libraries + +* Added PDF text parsing support to the python converter backend + + - This requires the installation of: + + + pdfminer python package + +* Added PDF page count support to the python converter backend +* Added python only converter backend supporting resizing, zooming and rotation + + - This backend required the installation of the python image library (PIL) + - This backend is useful when Graphicsmagick or Imagemagick can not be installed for some reason + - If understand fewer file format than the other 2 backends + +* Added default tranformation support to document sources +* Removed ``DOCUMENT_DEFAULT_TRANSFORMATIONS`` setup options +* Document sources are now defined via a series of view under the setup main menu +* This removes all the ``DOCUMENT_STAGING`` related setup options + + - Two document source types are supported local (via a web form), + and staging + - However multiple document sources can be defined each with their own + set of transformations and default metadata selection + +* Use ``python-magic`` to determine a document's mimetype otherwise + fallback to use python's mimetypes library +* Remove the included sources for ``python-magic`` instead it is now fetched + from github by pip +* Removed the document subtemplates and changed to a tabbed style +* Added link to document index content view to navigate the tree upwards +* Added new option ``MAIN_DISABLE_HOME_VIEW`` to disable the home main menu + tab and save some space +* Added new option to the web theme app, ``WEB_THEME_VERBOSE_LOGIN`` + that display a more information on the login screen + (version, copyright, logos) +* Added a confirmation dialog to the document tag removal view diff --git a/docs/releases/0.9.1.rst b/docs/releases/0.9.1.rst new file mode 100644 index 0000000000..2dc29b1a65 --- /dev/null +++ b/docs/releases/0.9.1.rst @@ -0,0 +1,7 @@ +Version 0.9.1 +------------- +* Added handling percent encoded unicode query strings in search URL, + thanks to (Сергей Глита [Sergei Glita]) for reporting. +* Added a FAQ explaing how to fix MySQL collation related error when + doing searches also thanks to (Сергей Глита [Sergei Glita]) for + reporting this one. diff --git a/docs/releases/0.9.rst b/docs/releases/0.9.rst new file mode 100644 index 0000000000..c3709ffe2f --- /dev/null +++ b/docs/releases/0.9.rst @@ -0,0 +1,44 @@ +Version 0.9 +----------- +* Simplified getting mimetypes from files by merging 2 implementations + (document based and file based) +* Updated python converter backend, document model and staging module + to use the new get_mimetype API +* Only allow clickable thumbnails for document and staging files with a + valid image +* Removed tag count from the group document list widget to conserve + vertical space +* Updated required Django version to 1.3.1 +* Removed the included 3rd party module django-sendfile, now added to + the requirement files. + + * User should do a pip install -r requirements/production.txt to update + +* Changed to Semantic Versioning (http://semver.org/), with + recommendations 7, 8 and 9 causing the most effect in the versioning number. +* Added Russian locale post OCR cleanup backend (Сергей Глита [Sergei Glita]) +* Reduced severity of the messages displayed when no OCR cleanup backend + is found for a language +* Complete Portuguese translation (Emerson Soares and Renata Oliveira) +* Complete Russian translation (Сергей Глита [Sergei Glita]) +* Added animate.css to use CSS to animate flash messages with better + fallback on non JS browsers +* The admin and sentry links are no longer hard-coded (Meurig Freeman) +* Improved appearance of the document tag widget + (https://p.twimg.com/Ac0Q0b-CAAE1lfA.png:large) +* Added django_compress and cssmin to the requirements files and enabled + django_compress for CSS and JS files +* Added granting and revoking permission methods to the permission model +* Correctly calculate the mimetype icons paths when on development mode +* Added a new more comprehensive method of passing multiple variables + per item in multi item selection views +* Used new multi parameter passing method to improve the usability of + the grant/revoke permission view, thanks to Cezar Jenkins + (https://twitter.com/#!/emperorcezar) for the suggestion +* Added step to the documentation explaining how to install Mayan EDMS + on Webfaction +* Added an entry in the documentation to the screencast explaining how + to install Mayan EDMS on DjangoZoom +* Added required changes to add Mayan EDMS to Transifex.com +* Fixed the apache contrib file static file directory name +* Added improved documentation diff --git a/docs/releases/index.rst b/docs/releases/index.rst new file mode 100644 index 0000000000..3bbee2aa57 --- /dev/null +++ b/docs/releases/index.rst @@ -0,0 +1,42 @@ +============= +Release notes +============= + +Release notes for the official **Mayan EDMS** releases. Each release note will tell you +what's new in each version, and will also describe any backwards-incompatible +changes made in that version. + +For those upgrading to a new version of **Mayan EDMS**, you will need to check +all the backwards-incompatible changes and deprecated features for +each 'final' release from the one after your current **Mayan EDMS** version, +up to and including the new version. + +Latest version (0.12) +--------------------- +.. toctree:: + :maxdepth: 1 + + 0.12 + +Historic changelogs +------------------- +.. toctree:: + :maxdepth: 1 + + 0.11.1 + 0.11 + 0.10.1 + 0.10 + 0.9.1 + 0.9 + 0.8.3 + 0.8.2 + 0.8.1 + 0.8 + 0.7.6 + 0.7.5 + 0.7.4 + 0.7.3 + 0.7 + 0.5.1 + 0.5 diff --git a/docs/settings.rst b/docs/settings.rst deleted file mode 100644 index 323bf84405..0000000000 --- a/docs/settings.rst +++ /dev/null @@ -1,411 +0,0 @@ -======== -Settings -======== - -**Mayan EDMS** has many configuration options that make it very adaptable to -different server configurations. - -Documents ---------- - -.. data:: DOCUMENTS_CHECKSUM_FUNCTION - - Default: ``hashlib.sha256(x).hexdigest()`` - - -.. data:: DOCUMENTS_UUID_FUNCTION - - Default: ``unicode(uuid.uuid4())`` - - -.. data:: DOCUMENTS_STORAGE_BACKEND - - Default: ``FileBasedStorage`` class - - -.. data:: DOCUMENTS_PREVIEW_SIZE - - Default: ``640x480`` - - -.. data:: DOCUMENTS_PRINT_SIZE - - Default: ``1400`` - - -.. data:: DOCUMENTS_MULTIPAGE_PREVIEW_SIZE - - Default: ``160x120`` - - -.. data:: DOCUMENTS_THUMBNAIL_SIZE - - Default: ``50x50`` - - -.. data:: DOCUMENTS_DISPLAY_SIZE - - Default: ``1200`` - - -.. data:: DOCUMENTS_RECENT_COUNT - - Default: ``40`` - - Maximum number of recent (created, edited, viewed) documents to - remember per user. - - -.. data:: DOCUMENTS_ZOOM_PERCENT_STEP - - Default: ``50`` - - Amount in percent zoom in or out a document page per user interaction. - - -.. data:: DOCUMENTS_ZOOM_MAX_LEVEL - - Default: ``200`` - - Maximum amount in percent (%) to allow user to zoom in a document page interactively. - - -.. data:: DOCUMENTS_ZOOM_MIN_LEVEL - - Default: ``50`` - - Minimum amount in percent (%) to allow user to zoom out a document page interactively. - - -.. data:: DOCUMENTS_ROTATION_STEP - - Default: ``90`` - - Amount in degrees to rotate a document page per user interaction. - - -.. data:: DOCUMENTS_CACHE_PATH - - Default: ``image_cache`` (relative to the installation path) - - The path where the visual representations of the documents are stored for fast display. - - -Converter ---------- - -.. data:: CONVERTER_IM_CONVERT_PATH - - Default: ``/usr/bin/convert`` - - - File path to imagemagick's convert program. - - -.. data:: CONVERTER_IM_IDENTIFY_PATH - - Default: ``/usr/bin/identify`` - - - File path to imagemagick's identify program. - - -.. data:: CONVERTER_GM_PATH - - Default: ``/usr/bin/gm`` - - - File path to graphicsmagick's program. - - -.. data:: CONVERTER_GM_SETTINGS - - Default: None - - -.. data:: CONVERTER_GRAPHICS_BACKEND - - Default: ``converter.backends.python`` - - Graphics conversion backend to use. Options are: ``converter.backends.imagemagick``, - ``converter.backends.graphicsmagick`` and ``converter.backends.python``. - - Suggested options: ``-limit files 1 -limit memory 1GB -limit map 2GB -density 200`` - - -.. data:: CONVERTER_UNOCONV_PATH - - Default: ``/usr/bin/unoconv`` - - Path to the unoconv program. - - -.. data:: CONVERTER_UNOCONV_USE_PIPE - - Default: ``True`` - - Use alternate method of connection to LibreOffice using a pipe, it is slower but less prone to segmentation faults. - - -Linking -------- - -.. data:: LINKING_SHOW_EMPTY_SMART_LINKS - - Default: ``True`` - - Show smart links even when they don't return any documents. - - -Storage -------- - -.. data:: STORAGE_GRIDFS_HOST - - Default: ``localhost`` - - -.. data:: STORAGE_GRIDFS_PORT - - Default: ``27017`` - - -.. data:: STORAGE_GRIDFS_DATABASE_NAME - - Default: ``document_storage`` - - -.. data:: STORAGE_FILESTORAGE_LOCATION - - Default: ``document_storage`` - - -Job processor -------------- - -.. data:: JOB_PROCESSING_BACKEND - - Default: ``None`` - - - Specified which job processing library to use, option are: None and celery. - - -Document indexing ------------------ - -.. data:: DOCUMENT_INDEXING_AVAILABLE_INDEXING_FUNCTIONS - - Default: ``proper_name`` - - -.. data:: DOCUMENT_INDEXING_SUFFIX_SEPARATOR - - Default: ``_`` (underscore) - - -.. data:: DOCUMENT_INDEXING_FILESYSTEM_SLUGIFY_PATHS - - Default: ``False`` - - -.. data:: DOCUMENT_INDEXING_FILESYSTEM_MAX_SUFFIX_COUNT - - Default: ``1000`` - - -.. data:: DOCUMENT_INDEXING_FILESYSTEM_FILESERVING_PATH - - Default: ``/tmp/mayan/documents`` - - -.. data:: DOCUMENT_INDEXING_FILESYSTEM_FILESERVING_ENABLE - - Default: ``True`` - - -OCR ---- - -.. data:: OCR_TESSERACT_PATH - - Default: ``/bin/tesseract`` - - -.. data:: OCR_TESSERACT_LANGUAGE - - Default: ``eng`` - - -.. data:: OCR_REPLICATION_DELAY - - Default: ``0`` - - Amount of seconds to delay OCR of documents to allow for the node's - storage replication overhead. - - -.. data:: OCR_NODE_CONCURRENT_EXECUTION - - Default: ``1`` - - Maximum amount of concurrent document OCRs a node can perform. - - -.. data:: OCR_AUTOMATIC_OCR - - Default: ``False`` - - Automatically queue newly created documents for OCR. - - -.. data:: OCR_QUEUE_PROCESSING_INTERVAL - - Default: ``10`` - - -.. data:: OCR_CACHE_URI - - Default: ``None`` - - URI in the form: ``"memcached://127.0.0.1:11211/"`` to specify a cache - backend to use for locking. Multiple hosts can be specified separated - by a semicolon. - - -.. data:: OCR_UNPAPER_PATH - - Default: ``/usr/bin/unpaper`` - - File path to unpaper program. - - -Metadata --------- - -.. data:: METADATA_AVAILABLE_FUNCTIONS - - Default: ``current_date`` - - -.. data:: METADATA_AVAILABLE_MODELS - - Default: ``User`` - - -Common ------- - -.. data:: COMMON_TEMPORARY_DIRECTORY - - Default: ``/tmp`` - - Temporary directory used site wide to store thumbnails, previews - and temporary files. If none is specified, one will be created - using tempfile.mkdtemp() - - -.. data:: COMMON_DEFAULT_PAPER_SIZE - - Default: ``Letter`` - - -.. data:: COMMON_DEFAULT_PAGE_ORIENTATION - - Default: ``Portrait`` - - -.. data:: COMMON_AUTO_CREATE_ADMIN - - Default: ``True`` - - -.. data:: COMMON_AUTO_ADMIN_USERNAME - - Default: ``admin`` - - -.. data:: COMMON_AUTO_ADMIN_PASSWORD - - Default: ``admin`` - - -.. data:: COMMON_LOGIN_METHOD - - Default: ``username`` - - Controls the mechanism used to authenticated user. Options are: ``username``, ``email`` - - -Search ------- - -.. data:: SEARCH_LIMIT - - Default: ``100`` - - Maximum amount search hits to fetch and display. - - -.. data:: SEARCH_RECENT_COUNT - - Default: ``5`` - - Maximum number of search queries to remember per user. - - -Web theme ---------- - -.. data:: WEB_THEME_THEME - - Default: ``activo`` - - CSS theme to apply, options are: ``amro``, ``bec``, ``bec-green``, ``blue``, ``default``, ``djime-cerulean``, ``drastic-dark``, ``kathleene``, ``olive``, ``orange``, ``red``, ``reidb-greenish`` and ``warehouse``. - - -.. data:: WEB_THEME_VERBOSE_LOGIN - - Default: ``True`` - - Display extra information in the login screen. - - -Main ----- - -.. data:: MAIN_SIDE_BAR_SEARCH - - Default: ``False`` - - Controls whether the search functionality is provided by a sidebar widget or by a menu entry. - - -.. data:: MAIN_DISABLE_HOME_VIEW - - Default: ``False`` - - -.. data:: MAIN_DISABLE_ICONS - - Default: ``False`` - - -User management ---------------- - -.. data:: ROLES_DEFAULT_ROLES - - Default: ``[]`` - - A list of existing roles that are automatically assigned to newly created users - - -Signatures ----------- - -.. data:: SIGNATURES_KEYSERVERS - - Default: ``['pool.sks-keyservers.net']`` - - List of keyservers to be queried for unknown keys. diff --git a/docs/topics/ACL.png b/docs/topics/ACL.png new file mode 100644 index 0000000000..acd3458113 Binary files /dev/null and b/docs/topics/ACL.png differ diff --git a/docs/topics/customization.rst b/docs/topics/customization.rst new file mode 100644 index 0000000000..927bced0d3 --- /dev/null +++ b/docs/topics/customization.rst @@ -0,0 +1,49 @@ +============= +Customization +============= + +The general appearance of **Mayan EDMS** can be customized entirely just +by changing a few settings. + +------ +Themes +------ +**Mayan EDMS** uses `Andrea Franz's excellent web app template`_ which includes +several themes that could be used to adapt **Mayan EDMS**'s appearance to match +an organtization existing applications' look and feel. + +The theme can be changed very easily by settings the :setting:`WEB_THEME_THEME` +configuration option to one of its valid values. + +.. image:: themes.png + :alt: themes + +------------ +Login screen +------------ +The amount of information presented at the login screen can also be restricted +if for security or desgin reasons using :setting:`WEB_THEME_VERBOSE_LOGIN` +configuration option. + +.. image:: mayan-login.png + :alt: mayan login screen + +----------- +Home screen +----------- +Sometimes users just want to go directly to work and not just be greeted with +a home screen, for these kind of situations **Mayan EDMS** has the +:setting:`MAIN_DISABLE_HOME_VIEW` configuration option which will cause +users to land on their ``recent document list`` as soon as they log in. + +----- +Icons +----- +Some themes such as ``default`` might be more visually appealing to some +people without the menu icons, for this **Mayan EDMS** provides the +:setting:`MAIN_DISABLE_ICONS` configuration option. + +.. image:: no-icons.png + :alt: mayan screens with out icons + +.. _`Andrea Franz's excellent web app template`: https://github.com/pilu/web-app-theme diff --git a/docs/development.rst b/docs/topics/development.rst similarity index 54% rename from docs/development.rst rename to docs/topics/development.rst index b55371ac69..5ef1e680d6 100644 --- a/docs/development.rst +++ b/docs/topics/development.rst @@ -51,31 +51,54 @@ To familiarize yourself with the technical details of the project read the :ref: .. _docs: ------------------ -Documentation ------------------ -The documentation is written in `reStructured Text`_ format. +--------- +Debugging +--------- -The documentation lives in the ``docs`` directory. In order to build it, you will first need to install Sphinx_. :: +**Mayan EDMS** makes extensive use of Django's new `logging capabilities`_. +To enable debug logging for the ``documents`` app for example add the following +lines to your ``settings_local.py`` file:: - $ pip install sphinx + LOGGING = { + 'version': 1, + 'disable_existing_loggers': True, + 'formatters': { + 'verbose': { + 'format': '%(levelname)s %(asctime)s %(name)s %(process)d %(thread)d %(message)s' + }, + 'intermediate': { + 'format': '%(name)s <%(process)d> [%(levelname)s] "%(funcName)s() %(message)s"' + }, + 'simple': { + 'format': '%(levelname)s %(message)s' + }, + }, + 'handlers': { + 'console':{ + 'level':'DEBUG', + 'class':'logging.StreamHandler', + 'formatter': 'intermediate' + } + }, + 'loggers': { + 'documents': { + 'handlers':['console'], + 'propagate': True, + 'level':'DEBUG', + }, + } + } -Then, to build an HTML version of the documentation, simply run the following from the **docs** directory:: - - $ make html - -Your ``docs/_build/html`` directory will then contain an HTML version of the documentation, ready for publication on most web servers. - -You can also generate the documentation in format other than HTML. - -.. _`reStructured Text`: http://docutils.sourceforge.net/rst.html -.. _Sphinx: http://sphinx.pocoo.org +Likewise, to see the debug output of the ``tags`` app, just add the following inside the ``loggers`` block:: ---------------- -Translations ---------------- + 'tags': { + 'handlers':['console'], + 'propagate': True, + 'level':'DEBUG', + }, -Translations are now being handled online via the **Transifex** website: https://www.transifex.net/projects/p/mayan-edms/ + +.. _`logging capabilities`: https://docs.djangoproject.com/en/dev/topics/logging diff --git a/docs/topics/document_visualization.rst b/docs/topics/document_visualization.rst new file mode 100644 index 0000000000..cfedc23ad3 --- /dev/null +++ b/docs/topics/document_visualization.rst @@ -0,0 +1,37 @@ +====================== +Document visualization +====================== + + +The philosophy in place is to try to avoid having users download a documents and leave +**Mayan EDMS** to be able to see them, so in essence making **Mayan EDMS** a +visualization tool too. The conversion backend is a stack of functions, +first the mimetype is evaluated, if it is an office document it is passed +to LibreOffice_ working in headless mode (and managed by supervisor_) +via unoconv_ (more information about ``unoconv`` can be found in the :doc:`FAQ section <../faq/index>`) +for conversion to PDF_. The PDF_ is stored in a temporary +cache along side all the other files that were not office documents, +from here they are inspected to determine the page count and the +corresponding blank database entires are created. After the database +update they all go to the conversion driver specified by the configuration +option :setting:`CONVERTER_GRAPHICS_BACKEND` and a high resolution +master preview of each file is generated and stored in the persistent +cache. From the master previews in the persistent cache, volatile +previews are then created on demand for the different sizes requested +(thumbnail, page preview, full preview) and rotated interactively +in the details view. + + +Office document conversion however won't always work as expected because +LibreOffice_ do not provide proper API's, so subprocess calling, +temporary files and other black magic needs to be invoked to get it +properly integrated. **Mayan EDMS** treats documents as collections of pages +or frames, and text extraction and OCR is done per page not per document, +thats why even text documents need to be rendered by LibreOffice_ +before they can be previewed and text can be extracted. + + +.. _PDF: http://en.wikipedia.org/wiki/Portable_Document_Format +.. _LibreOffice: http://www.libreoffice.org/ +.. _unoconv: https://github.com/dagwieers/unoconv/ +.. _supervisor: http://supervisord.org/introduction.html diff --git a/docs/topics/documentation.rst b/docs/topics/documentation.rst new file mode 100644 index 0000000000..9c6af6e058 --- /dev/null +++ b/docs/topics/documentation.rst @@ -0,0 +1,21 @@ +============= +Documentation +============= + +**Mayan EDMS**'s documentation is written in `reStructured Text`_ format. + +The documentation lives in the ``docs`` directory. In order to build it, you will first need to install Sphinx_. :: + + $ pip install sphinx + + +Then, to build an HTML version of the documentation, simply run the following from the **docs** directory:: + + $ make html + +Your ``docs/_build/html`` directory will then contain an HTML version of the documentation, ready for publication on most web servers. + +You can also generate the documentation in formats other than HTML. + +.. _`reStructured Text`: http://docutils.sourceforge.net/rst.html +.. _Sphinx: http://sphinx.pocoo.org diff --git a/docs/topics/file_storage.rst b/docs/topics/file_storage.rst new file mode 100644 index 0000000000..8d980a15cf --- /dev/null +++ b/docs/topics/file_storage.rst @@ -0,0 +1,26 @@ +============ +File storage +============ + +The files are stored and placed under **Mayan EDMS** "control" to avoid +filename clashes (each file gets renamed to its UUID and with an extension) +and stored in a simple flat arrangement in a directory. This doesn't +stop access to the files but it is not recommended because moving, +renaming or updating the files directly would throw the database out +of sync. For direct access to the files the recommended way is to create an +:doc:`index `, use the indexing mirroring feature and share the result via +file serving software [#f1]_. + +**Mayan EDMS** components are as decoupled from each other as possible, +storage in this case is very decoupled and its behavior is controlled +not by the project but by the Storage progamming class. Why this design? +All the other part don't make any assumptions about the actual file +storage, so that **Mayan EDMS** can work saving files locally, over the +network or even across the internet and still operate exactly the same. + +The file storage behavior is controlled by the :setting:`DOCUMENTS_STORAGE_BACKEND` +and should be set to a class or subclass of Django's ``django.core.files.storage.FileSystemStorage`` class. + +.. rubric:: Footnotes + +.. [#f1] http://en.wikipedia.org/wiki/File_server diff --git a/docs/topics/index.rst b/docs/topics/index.rst new file mode 100644 index 0000000000..b5ca990d6b --- /dev/null +++ b/docs/topics/index.rst @@ -0,0 +1,23 @@ +Using Mayan EDMS +================ + +Introductions to all the key parts of Mayan EDMS you'll need to know: + +.. toctree:: + :maxdepth: 1 + + file_storage + initial_import + permissions + transformations + document_visualization + versioning + signatures + indexes + smart_links + ocr + settings + customization + development + documentation + translations diff --git a/docs/topics/index_instance.png b/docs/topics/index_instance.png new file mode 100644 index 0000000000..9310c1c013 Binary files /dev/null and b/docs/topics/index_instance.png differ diff --git a/docs/topics/index_template.png b/docs/topics/index_template.png new file mode 100644 index 0000000000..6d5596dd89 Binary files /dev/null and b/docs/topics/index_template.png differ diff --git a/docs/topics/indexes.png b/docs/topics/indexes.png new file mode 100644 index 0000000000..a1a5c776e6 Binary files /dev/null and b/docs/topics/indexes.png differ diff --git a/docs/topics/indexes.rst b/docs/topics/indexes.rst new file mode 100644 index 0000000000..9f818e5876 --- /dev/null +++ b/docs/topics/indexes.rst @@ -0,0 +1,59 @@ +======= +Indexes +======= + +Indexes are an automatic method to hierarchically organize documents in relation to their metadata and to each other. + +Index templates +=============== + +Since multiple indexes can be defined, the first step is to create an empty index. +Administrators then define the tree template showing how the index will be structured. +Each branch can be a pseudo folder, which can hold other child 'folders' or +a document container which will have all the links to the documents that +matched the path to reach the document container. + +.. image:: index_template.png + :alt: index template + +Index instances +=============== + +The template is the skeleton from which an instance of the index is then +auto-populated with links to the documents depending on the rules of each +branch of the index evaluated against the metadata and properties of the documents. + +.. image:: index_instance.png + :alt: index instance + +Index serving +============= + +Indexes can be mirrored to the operating system filesystem +using the configuration option +:setting:`DOCUMENT_INDEXING_FILESYSTEM_SERVING`. + +``settings_local.py``:: + + # Supposing the 'Sample index' internal name is 'sample_index' + DOCUMENT_INDEXING_FILESYSTEM_SERVING = { + 'sample_index': '/var/local/document/sharing/invoices/', + } + +This creates an actual directory tree and links to the actual stored files but using +the filename of the documents as stored in the database. + +.. image:: indexes.png + :alt: indexes diagram + +This filesystem mirror of the index can them be served with Samba_ across the +network. This access would be read-only, with new versions of the files +being uploaded from the web GUI using the document versioning support. + +The index cannot be edited manually to protect it's integrity, only changing +the rules or the metadata of the documents would cause the index to be +regenerated. For manual organization of documents there are the folders, +their structure is however flat, and they have to be manually updated and +curated. + +.. _Samba: http://www.samba.org/ diff --git a/docs/topics/initial_import.rst b/docs/topics/initial_import.rst new file mode 100644 index 0000000000..72859f49bf --- /dev/null +++ b/docs/topics/initial_import.rst @@ -0,0 +1,48 @@ +==================== +Initial data loading +==================== + +Bulk document import +-------------------- + +**Mayan EDMS** has the ability to individually upload the contents of compressed +files, however by nature of being a web based application it is bounded by the +limitations of the HTTP protocol, this imposes a limit on the file size and +the amount of time **Mayan EDMS** may keep a connection open while it processes +compressed files. When the desired amount of documents is bigger than what +these limitations allow, **Mayan EDMS** provides a command line tool for out of +process document importation. + +The command line options for this feature are as follows:: + + $ ./manage.py bulk_upload --noinput --metadata '{"project": "bulk"}' --document_type "Accounting documents" compressed.zip + +**Optional arguments** + +* The ``--noinput`` argument skips confirmation and starts the upload immediately. +* The ``--metadata`` argument allows specifing what metadata will be assigned + to the documents when uploaded. +* And the ``--document_type`` applies a previously defined + document type to the uploaded documents. + + +Bulk user import +---------------- + +As well as providing bulk document import functionality **Mayan EDMS** also +includes a management command to import a large number users +from a CSV file. The command line options for this feature are as +follow:: + + $ ./manage.py import_users --noinput --password=welcome123 --skip-repeated user_list.csv + +The CSV field order must be: username, first name, last name and email, any other +column after those is ignored. + +**Optional arguments** + +* The ``--noinput`` argument skips confirmation and starts the import immediately. +* The ``--password`` argument allows specifing what default password will be assigned + to all the new users that are imported. +* The ``--skip-repeated`` tells the importedr to not stop when finding + that a user already exists in the database. diff --git a/docs/topics/mayan-login.png b/docs/topics/mayan-login.png new file mode 100644 index 0000000000..65673aa303 Binary files /dev/null and b/docs/topics/mayan-login.png differ diff --git a/docs/topics/no-icons.png b/docs/topics/no-icons.png new file mode 100644 index 0000000000..d00eec2d62 Binary files /dev/null and b/docs/topics/no-icons.png differ diff --git a/docs/topics/ocr.rst b/docs/topics/ocr.rst new file mode 100644 index 0000000000..52c57282ef --- /dev/null +++ b/docs/topics/ocr.rst @@ -0,0 +1,18 @@ +=== +OCR +=== + +Because OCR is an intensive operation, documents are queued for OCR for +later handling, the amount of documents processed in parallel is +controlled by the :setting:`OCR_NODE_CONCURRENT_EXECUTION` configuration +option. Ideally the machine serving **Mayan EDMS** should disable OCR +processing by settings this options to 0, with other machines or cloud +instances then connected to the same database doing the OCR processing. +The document is checked to see if there are text parsers available, is +no parser is available for that file type then the document is passed +to tesseract page by page and the results stored per page, this is to +keep the page image in sync with the transcribed text. However when +viewing the document in the details tab all the pages text are +concatenated and shown to the user. Setting the :setting:`OCR_AUTOMATIC_OCR` +option to ``True`` would cause all newly uploaded documents to be +queued automatically for OCR. diff --git a/docs/topics/permissions.png b/docs/topics/permissions.png new file mode 100644 index 0000000000..3c3809f405 Binary files /dev/null and b/docs/topics/permissions.png differ diff --git a/docs/topics/permissions.rst b/docs/topics/permissions.rst new file mode 100644 index 0000000000..4948654e85 --- /dev/null +++ b/docs/topics/permissions.rst @@ -0,0 +1,46 @@ +=========== +Permissions +=========== + +**Mayan EDMS** provides very exact control over what activies users can +perform. This control is divided into two levels of operation: + +2 tier permissions assignement +============================== + +This level of activity control works +by allowing roles that are composed of users and group, to be granted +a permission such that the holder of that permission can exercise it +throught the entire collection of objects (document, folders, tags, etc), +this method could be thought out as a global permission granting level. +Example: Roles being granted the ``Document view`` permission will be able to view +**all** documents in existance. + +.. image:: permissions.png + :alt: 2-tier permission diagram + + +3 tier access control +===================== + +When more control is desired over which objects +actors(user, groups and roles) can exercise an action this method should be +used. Under this level, actors are granted a +permission but only in relation to a selected object. Example: Granting user +``Joe`` the ``Document view`` access control for document ``Payroll``, +would allow him to view this document only. + +.. image:: ACL.png + :alt: 3-tier access control diagram + +The permission system enforces inheritance by first checking if the user +has a global permission, is a member of a group or a role that has a global +permission and if not then checks to see if that user, a group or role to +which he belongs, has been granted access to the specific object to which +he is desiring to perform a given action that requires a permission. +Only when these checks fails the user +is forbidden to perform the action and a generic message indicating this is +displayed to avoid providing any information that could be used to sidetrack +the permission system or obtain any kind of information about the object +from which the user was not allowed access. + diff --git a/docs/topics/settings.rst b/docs/topics/settings.rst new file mode 100644 index 0000000000..ae395a435e --- /dev/null +++ b/docs/topics/settings.rst @@ -0,0 +1,565 @@ +======== +Settings +======== + +**Mayan EDMS** has many configuration options that make it very adaptable to +different server configurations. + +Documents +========= + +.. setting:: DOCUMENTS_CHECKSUM_FUNCTION + +**DOCUMENTS_CHECKSUM_FUNCTION** + +Default: ``hashlib.sha256(x).hexdigest()`` + +The function that will be used to calculate the hash value of each uploaded document. + + +.. setting:: DOCUMENTS_UUID_FUNCTION + +**DOCUMENTS_UUID_FUNCTION** + +Default: ``unicode(uuid.uuid4())`` + +The function that will be used to internally identify each uploaded document. + + +.. setting:: DOCUMENTS_STORAGE_BACKEND + +**DOCUMENTS_STORAGE_BACKEND** + +Default: ``FileBasedStorage`` class + +The storage backend that will be used to store every document. + + +.. setting:: DOCUMENTS_PREVIEW_SIZE + +**DOCUMENTS_PREVIEW_SIZE** + +Default: ``640x480`` + +Size of the document list and recent document list previews. + + +.. setting:: DOCUMENTS_PRINT_SIZE + +**DOCUMENTS_PRINT_SIZE** + +Default: ``1400`` + + +.. setting:: DOCUMENTS_MULTIPAGE_PREVIEW_SIZE + +**DOCUMENTS_MULTIPAGE_PREVIEW_SIZE** + +Default: ``160x120`` + + +.. setting:: DOCUMENTS_THUMBNAIL_SIZE + +**DOCUMENTS_THUMBNAIL_SIZE** + +Default: ``50x50`` + + +.. setting:: DOCUMENTS_DISPLAY_SIZE + +**DOCUMENTS_DISPLAY_SIZE** + +Default: ``1200`` + + +.. setting:: DOCUMENTS_RECENT_COUNT + +**DOCUMENTS_RECENT_COUNT** + +Default: ``40`` + +Maximum number of recent (created, edited, viewed) documents to +remember per user. + + +.. setting:: DOCUMENTS_ZOOM_PERCENT_STEP + +**DOCUMENTS_ZOOM_PERCENT_STEP** + +Default: ``50`` + +Amount in percent zoom in or out a document page per user interaction. + + +.. setting:: DOCUMENTS_ZOOM_MAX_LEVEL + +**DOCUMENTS_ZOOM_MAX_LEVEL** + +Default: ``200`` + +Maximum amount in percent (%) to allow user to zoom in a document page interactively. + + +.. setting:: DOCUMENTS_ZOOM_MIN_LEVEL + +**DOCUMENTS_ZOOM_MIN_LEVEL** + +Default: ``50`` + +Minimum amount in percent (%) to allow user to zoom out a document page interactively. + + +.. setting:: DOCUMENTS_ROTATION_STEP + +**DOCUMENTS_ROTATION_STEP** + +Default: ``90`` + +Amount in degrees to rotate a document page per user interaction. + + +.. setting:: DOCUMENTS_CACHE_PATH + +**DOCUMENTS_CACHE_PATH** + +Default: ``image_cache`` (relative to the installation path) + +The path where the visual representations of the documents are stored for fast display. + + +Converter +========= +.. setting:: CONVERTER_GRAPHICS_BACKEND + +**CONVERTER_GRAPHICS_BACKEND** + +Default: ``converter.backends.python`` + +Graphics conversion backend to use. Options are: + +* ``converter.backends.imagemagick`` - Wrapper for ImageMagick + + * Use the :setting:`CONVERTER_IM_CONVERT_PATH` and :setting:`CONVERTER_IM_IDENTIFY_PATH` to specify the binary files locations. + +* ``converter.backends.graphicsmagick`` - Wrapper for GraphicsMagick + + * Use the :setting:`CONVERTER_GM_PATH` and :setting:`CONVERTER_GM_SETTINGS` to specify the binary file location and customized settings. + +* ``converter.backends.python`` - Wrapper for Pillow_ and Ghostscript_ + + +.. _Pillow: http://pypi.python.org/pypi/Pillow +.. _Ghostscript: http://www.ghostscript.com/ + + +.. setting:: CONVERTER_IM_CONVERT_PATH + +**CONVERTER_IM_CONVERT_PATH** + +Default: ``/usr/bin/convert`` + +File path to imagemagick's convert program. + + +.. setting:: CONVERTER_IM_IDENTIFY_PATH + +**CONVERTER_IM_IDENTIFY_PATH** + +Default: ``/usr/bin/identify`` + +File path to imagemagick's identify program. + + +.. setting:: CONVERTER_GM_PATH + +**CONVERTER_GM_PATH** + +Default: ``/usr/bin/gm`` + +File path to graphicsmagick's program. + + +.. setting:: CONVERTER_GM_SETTINGS + +**CONVERTER_GM_SETTINGS** + +Default: None + +Suggested options: ``-limit files 1 -limit memory 1GB -limit map 2GB -density 200`` + +Set of configuration options to pass to the GraphicsMagick executable to +fine tune it's functionality as explained in the `GraphicsMagick documentation`_ + +.. _GraphicsMagick documentation: http://www.graphicsmagick.org/convert.html#conv-opti + + +.. setting:: CONVERTER_UNOCONV_PATH + + +**CONVERTER_UNOCONV_PATH** + +Default: ``/usr/bin/unoconv`` + +Path to the unoconv program used to call LibreOffice for office document convertion. + + +.. setting:: CONVERTER_UNOCONV_USE_PIPE + + +**CONVERTER_UNOCONV_USE_PIPE** + +Default: ``True`` + +Use alternate method of connection to LibreOffice using a pipe, it is slower but less prone to segmentation faults. + + +Linking +======= + +.. setting:: LINKING_SHOW_EMPTY_SMART_LINKS + +**LINKING_SHOW_EMPTY_SMART_LINKS** + +Default: ``True`` + +Show smart links even when they don't return any documents. + + +Storage +======= + +.. setting:: STORAGE_GRIDFS_HOST + +**STORAGE_GRIDFS_HOST** + +Default: ``localhost`` + + +.. setting:: STORAGE_GRIDFS_PORT + +**STORAGE_GRIDFS_PORT** + +Default: ``27017`` + + +.. setting:: STORAGE_GRIDFS_DATABASE_NAME + +**STORAGE_GRIDFS_DATABASE_NAME** + +Default: ``document_storage`` + + +.. setting:: STORAGE_FILESTORAGE_LOCATION + +**STORAGE_FILESTORAGE_LOCATION** + +Default: ``document_storage`` + + +Document indexing +================= + +.. setting:: DOCUMENT_INDEXING_AVAILABLE_INDEXING_FUNCTIONS + +**DOCUMENT_INDEXING_AVAILABLE_INDEXING_FUNCTIONS** + +Default: ``proper_name`` + + +.. setting:: DOCUMENT_INDEXING_SUFFIX_SEPARATOR + +**DOCUMENT_INDEXING_SUFFIX_SEPARATOR** + +Default: ``_`` (underscore) + + +.. setting:: DOCUMENT_INDEXING_FILESYSTEM_SLUGIFY_PATHS + +**DOCUMENT_INDEXING_FILESYSTEM_SLUGIFY_PATHS** + +Default: ``False`` + + +.. setting:: DOCUMENT_INDEXING_FILESYSTEM_MAX_SUFFIX_COUNT + +**DOCUMENT_INDEXING_FILESYSTEM_MAX_SUFFIX_COUNT** + +Default: ``1000`` + + +.. setting:: DOCUMENT_INDEXING_FILESYSTEM_SERVING + +**DOCUMENT_INDEXING_FILESYSTEM_SERVING** + +Default: ``{}`` + +A dictionary that maps the index name and where on the filesystem that index will be mirrored. + + +OCR +=== + +.. setting:: OCR_TESSERACT_PATH + +**OCR_TESSERACT_PATH** + +Default: ``/bin/tesseract`` + +File path to the ``tesseract`` executable, used to perform OCR on document +page's images. + + +.. setting:: OCR_TESSERACT_LANGUAGE + +**OCR_TESSERACT_LANGUAGE** + +Default: ``eng`` + +Language code passed to the ``tesseract`` executable. + + +.. setting:: OCR_REPLICATION_DELAY + +**OCR_REPLICATION_DELAY** + +Default: ``0`` + +Amount of seconds to delay OCR of documents to allow for the node's +storage replication overhead. + + +.. setting:: OCR_NODE_CONCURRENT_EXECUTION + +**OCR_NODE_CONCURRENT_EXECUTION** + +Default: ``1`` + +Maximum amount of concurrent document OCRs a node can perform. + + +.. setting:: OCR_AUTOMATIC_OCR + +**OCR_AUTOMATIC_OCR** + +Default: ``False`` + +Automatically queue newly created documents or newly uploaded versions +of existing documents for OCR. + + +.. setting:: OCR_QUEUE_PROCESSING_INTERVAL + +**OCR_QUEUE_PROCESSING_INTERVAL** + +Default: ``10`` + + +.. setting:: OCR_UNPAPER_PATH + +**OCR_UNPAPER_PATH** + +Default: ``/usr/bin/unpaper`` + +File path to the ``unpaper`` executable, used to clean up images before +doing OCR. + + +Metadata +======== + +.. setting:: METADATA_AVAILABLE_FUNCTIONS + +**METADATA_AVAILABLE_FUNCTIONS** + +Default: ``current_date`` + + +.. setting:: METADATA_AVAILABLE_MODELS + +**METADATA_AVAILABLE_MODELS** + +Default: ``User`` + + +Common +====== + +.. setting:: COMMON_TEMPORARY_DIRECTORY + +**COMMON_TEMPORARY_DIRECTORY** + +Default: ``/tmp`` + +Temporary directory used site wide to store thumbnails, previews +and temporary files. If none is specified, one will be created +using tempfile.mkdtemp() + + +.. setting:: COMMON_DEFAULT_PAPER_SIZE + +**COMMON_DEFAULT_PAPER_SIZE** + +Default: ``Letter`` + + +.. setting:: COMMON_DEFAULT_PAGE_ORIENTATION + +**COMMON_DEFAULT_PAGE_ORIENTATION** + +Default: ``Portrait`` + + +.. setting:: COMMON_AUTO_CREATE_ADMIN + +**COMMON_AUTO_CREATE_ADMIN** + +Default: ``True`` + +Automatically creates an administrator superuser with the username +specified by COMMON_AUTO_ADMIN_USERNAME and with the default password +specified by COMMON_AUTO_ADMIN_PASSWORD + + +.. setting:: COMMON_AUTO_ADMIN_USERNAME + +**COMMON_AUTO_ADMIN_USERNAME** + +Default: ``admin`` + +Username of the automatically created superuser + + +.. setting:: COMMON_AUTO_ADMIN_PASSWORD + +**COMMON_AUTO_ADMIN_PASSWORD** + +Default: ``admin`` + +Default password of the automatically created superuser + + +.. setting:: COMMON_LOGIN_METHOD + +**COMMON_LOGIN_METHOD** + +Default: ``username`` + +Controls the mechanism used to authenticated user. Options are: ``username``, ``email`` +If using the ``email`` login method a proper email authentication backend must used +such as AUTHENTICATION_BACKENDS = ('common.auth.email_auth_backend.EmailAuthBackend',) + + +.. setting:: COMMON_ALLOW_ANONYMOUS_ACCESS + +**COMMON_ALLOW_ANONYMOUS_ACCESS** + +Default: ``False`` + +Allow non authenticated users, access to all views. + + +Search +====== + +.. setting:: SEARCH_LIMIT + +**SEARCH_LIMIT** + +Default: ``100`` + +Maximum amount search hits to fetch and display. + + +.. setting:: SEARCH_RECENT_COUNT + +**SEARCH_RECENT_COUNT** + +Default: ``5`` + +Maximum number of search queries to remember per user. + + +Web theme +========= + +.. setting:: WEB_THEME_THEME + +**WEB_THEME_THEME** + +Default: ``activo`` + +CSS theme to apply, options are: ``amro``, ``bec``, ``bec-green``, ``blue``, +``default``, ``djime-cerulean``, ``drastic-dark``, ``kathleene``, ``olive``, +``orange``, ``red``, ``reidb-greenish`` and ``warehouse``. + + +.. setting:: WEB_THEME_VERBOSE_LOGIN + +**WEB_THEME_VERBOSE_LOGIN** + +Default: ``True`` + +Display extra information in the login screen. + + +Main +==== + +.. setting:: MAIN_SIDE_BAR_SEARCH + +**MAIN_SIDE_BAR_SEARCH** + +Default: ``False`` + +Controls whether the search functionality is provided by a sidebar widget or by a menu entry. + + +.. setting:: MAIN_DISABLE_HOME_VIEW + +**MAIN_DISABLE_HOME_VIEW** + +Default: ``False`` + +Disable the home view and redirect users straight to the recent document list as soon as they log in. + + +.. setting:: MAIN_DISABLE_ICONS + +**MAIN_DISABLE_ICONS** + +Default: ``False`` + +Turns off navigation links' icons. + + +User management +=============== + +.. setting:: ROLES_DEFAULT_ROLES + +**ROLES_DEFAULT_ROLES** + +Default: ``[]`` + +A list of existing roles that are automatically assigned to newly created users + + +Signatures +========== + +.. setting:: SIGNATURES_KEYSERVERS + +**SIGNATURES_KEYSERVERS** + +Default: ``['pool.sks-keyservers.net']`` + +List of keyservers to be queried for unknown keys. + + +.. setting:: SIGNATURES_GPG_HOME + +**SIGNATURES_GPG_HOME** + +Default: ``gpg_home`` + +Home directory used to store keys as well as configuration files. diff --git a/docs/topics/signatures.rst b/docs/topics/signatures.rst new file mode 100644 index 0000000000..f2cad89fc2 --- /dev/null +++ b/docs/topics/signatures.rst @@ -0,0 +1,24 @@ +=================== +Document signatures +=================== + +**Mayan EDMS** supports two types of document signatures, these are embedded and +detached signatures. When a document with an embedded signature is +uploaded, this signature is readily detected as part of the document +inspection step. If the public key corresponding to the signee of the +document is not found, **Mayan EDMS** will try to obtain it from the list of +keyserver specified in the config option :setting:`SIGNATURES_KEYSERVERS`. +Failing that, **Mayan EDMS** will indicate that the document is signed +but that it has no way to verify such signature. +Existing non signed documents can be signed in one of two way: +by downloading the document, signing it, and uploading the signed document +as a new version of the existing one using **Mayan EDMS** :doc:`versioning support ` +or by creating a detached signature for the non signed document and uploading +such detached signature file using the option likewise named menu option. + +Maintenance of the public keyring can be done using the ``Key management`` +functionality in the ``Setup menu`` + +From this menu, key servers can be queried +and the results imported. Public keys no longer needed can also be deleted +from this menu. diff --git a/docs/topics/smart_links.rst b/docs/topics/smart_links.rst new file mode 100644 index 0000000000..07db50a9d0 --- /dev/null +++ b/docs/topics/smart_links.rst @@ -0,0 +1,9 @@ +=========== +Smart links +=========== + +Smart links are usefull for navigation between documents. They are rule +based but don't created any organizational structure just show the documents +that match the rules as evaluated against the metadata of the currently +displayed document. The index is global, the smart links are dependant +on the current document the user is viewing. diff --git a/docs/technical.rst b/docs/topics/technical.rst similarity index 100% rename from docs/technical.rst rename to docs/topics/technical.rst diff --git a/docs/topics/themes.png b/docs/topics/themes.png new file mode 100644 index 0000000000..47ef20d00d Binary files /dev/null and b/docs/topics/themes.png differ diff --git a/docs/topics/transformations.rst b/docs/topics/transformations.rst new file mode 100644 index 0000000000..7584b69698 --- /dev/null +++ b/docs/topics/transformations.rst @@ -0,0 +1,12 @@ +========================= +What are transformations? +========================= + +Transformation are useful to manipulate the preview of the stored documents +in a persistent manner, for example some scanning equipment only produce +landscape PDFs, in this case a default transformation for that document +source would be "rotation: 270 degress", this way whenever a document is +uploaded from that scanner it appears in portrait orientation. +The transformation remains attached to the document, this way the file +is preserved in it's original state (a requirement in legal environments) +but only the representation is transformed to make it look right to the user. diff --git a/docs/topics/translations.rst b/docs/topics/translations.rst new file mode 100644 index 0000000000..65910f7025 --- /dev/null +++ b/docs/topics/translations.rst @@ -0,0 +1,8 @@ +============ +Translations +============ + +Translations are now being handled online via the **Transifex** website: https://www.transifex.net/projects/p/mayan-edms/. +To create a translation team for a new language or contribute to an already +existing language translation, create a **Transifex** account and contact +the team coordinator of the respective language in which you are interested. diff --git a/docs/topics/versioning.png b/docs/topics/versioning.png new file mode 100644 index 0000000000..6ec2f24d86 Binary files /dev/null and b/docs/topics/versioning.png differ diff --git a/docs/topics/versioning.rst b/docs/topics/versioning.rst new file mode 100644 index 0000000000..5aa6dedee9 --- /dev/null +++ b/docs/topics/versioning.rst @@ -0,0 +1,25 @@ +=================== +Document versioning +=================== + +**Mayan EDMS** has the ability to store different versions of the same +document. Users are provided with a very comprehensive but easy to use +version numbering system that allows specifying a major, minor or micro +version number increase. + +.. image:: versioning.png + :alt: versioning diagram + +A comment field is also provided to allow users +to summarize the new verdion changes in comparison with the previous +one. If a new version was uploded by mistake or such new version is no +longer necessary **Mayan EDMS** provides the option to revert to a previous +version of the document. + +To upload a new document version, select an existing document, click on the +version tab of the document, and click on the 'upload new version' on the +side bar. A new view very similar to the new document upload view will +appear show the same interactive document sources that have been defined, +but with new options to specify the new version number and an optional +comment. + diff --git a/docs/updates.rst b/docs/updates.rst deleted file mode 100644 index 09112c0c52..0000000000 --- a/docs/updates.rst +++ /dev/null @@ -1,5 +0,0 @@ -========= -Changelog -========= - -.. include:: changelog.rst diff --git a/locale/es/LC_MESSAGES/django.mo b/locale/es/LC_MESSAGES/django.mo deleted file mode 100644 index f3e6b3187d..0000000000 Binary files a/locale/es/LC_MESSAGES/django.mo and /dev/null differ diff --git a/locale/es/LC_MESSAGES/django.po b/locale/es/LC_MESSAGES/django.po deleted file mode 100644 index f10a098074..0000000000 --- a/locale/es/LC_MESSAGES/django.po +++ /dev/null @@ -1,28 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-03-05 20:26-0400\n" -"PO-Revision-Date: 2011-03-05 20:46\n" -"Last-Translator: Roberto Rosario \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Language: \n" -"X-Translated-Using: django-rosetta 0.5.6\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: settings.py:52 -msgid "Spanish" -msgstr "Español" - -#: settings.py:53 -msgid "English" -msgstr "Inglés" diff --git a/misc/compilemessages_all.sh b/misc/compilemessages_all.sh index 5e3c5cf2a7..2b446850d5 100755 --- a/misc/compilemessages_all.sh +++ b/misc/compilemessages_all.sh @@ -1,5 +1,5 @@ #!/bin/sh -COMPILEMESSAGES="django-admin compilemessages" +COMPILEMESSAGES="django-admin.py compilemessages" PWD=`pwd` BASE=$PWD @@ -7,108 +7,173 @@ cd $BASE/apps/common $COMPILEMESSAGES -l pt $COMPILEMESSAGES -l ru $COMPILEMESSAGES -l es +$COMPILEMESSAGES -l it +$COMPILEMESSAGES -l pl cd $BASE/apps/converter $COMPILEMESSAGES -l pt $COMPILEMESSAGES -l ru $COMPILEMESSAGES -l es +$COMPILEMESSAGES -l it +$COMPILEMESSAGES -l pl cd $BASE/apps/documents $COMPILEMESSAGES -l pt $COMPILEMESSAGES -l ru $COMPILEMESSAGES -l es +$COMPILEMESSAGES -l it +$COMPILEMESSAGES -l pl cd $BASE/apps/document_comments $COMPILEMESSAGES -l pt $COMPILEMESSAGES -l ru $COMPILEMESSAGES -l es +$COMPILEMESSAGES -l it +$COMPILEMESSAGES -l pl cd $BASE/apps/document_indexing $COMPILEMESSAGES -l pt $COMPILEMESSAGES -l ru $COMPILEMESSAGES -l es +$COMPILEMESSAGES -l it +$COMPILEMESSAGES -l pl cd $BASE/apps/dynamic_search $COMPILEMESSAGES -l pt $COMPILEMESSAGES -l ru $COMPILEMESSAGES -l es +$COMPILEMESSAGES -l it +$COMPILEMESSAGES -l pl cd $BASE/apps/folders $COMPILEMESSAGES -l pt $COMPILEMESSAGES -l ru $COMPILEMESSAGES -l es +$COMPILEMESSAGES -l it +$COMPILEMESSAGES -l pl cd $BASE/apps/history $COMPILEMESSAGES -l pt $COMPILEMESSAGES -l ru $COMPILEMESSAGES -l es +$COMPILEMESSAGES -l it +$COMPILEMESSAGES -l pl cd $BASE/apps/linking $COMPILEMESSAGES -l pt $COMPILEMESSAGES -l ru $COMPILEMESSAGES -l es +$COMPILEMESSAGES -l it +$COMPILEMESSAGES -l pl cd $BASE/apps/main $COMPILEMESSAGES -l pt $COMPILEMESSAGES -l ru $COMPILEMESSAGES -l es +$COMPILEMESSAGES -l it +$COMPILEMESSAGES -l pl cd $BASE/apps/metadata $COMPILEMESSAGES -l pt $COMPILEMESSAGES -l ru $COMPILEMESSAGES -l es +$COMPILEMESSAGES -l it +$COMPILEMESSAGES -l pl cd $BASE/apps/navigation $COMPILEMESSAGES -l pt $COMPILEMESSAGES -l ru $COMPILEMESSAGES -l es +$COMPILEMESSAGES -l it +$COMPILEMESSAGES -l pl cd $BASE/apps/ocr $COMPILEMESSAGES -l pt $COMPILEMESSAGES -l ru $COMPILEMESSAGES -l es +$COMPILEMESSAGES -l it +$COMPILEMESSAGES -l pl cd $BASE/apps/permissions $COMPILEMESSAGES -l pt $COMPILEMESSAGES -l ru $COMPILEMESSAGES -l es +$COMPILEMESSAGES -l it +$COMPILEMESSAGES -l pl cd $BASE/apps/project_setup $COMPILEMESSAGES -l pt $COMPILEMESSAGES -l ru $COMPILEMESSAGES -l es +$COMPILEMESSAGES -l it +$COMPILEMESSAGES -l pl cd $BASE/apps/project_tools $COMPILEMESSAGES -l pt $COMPILEMESSAGES -l ru $COMPILEMESSAGES -l es +$COMPILEMESSAGES -l it +$COMPILEMESSAGES -l pl cd $BASE/apps/smart_settings $COMPILEMESSAGES -l pt $COMPILEMESSAGES -l ru $COMPILEMESSAGES -l es +$COMPILEMESSAGES -l it +$COMPILEMESSAGES -l pl cd $BASE/apps/sources $COMPILEMESSAGES -l pt $COMPILEMESSAGES -l ru $COMPILEMESSAGES -l es +$COMPILEMESSAGES -l it +$COMPILEMESSAGES -l pl cd $BASE/apps/tags $COMPILEMESSAGES -l pt $COMPILEMESSAGES -l ru $COMPILEMESSAGES -l es +$COMPILEMESSAGES -l it +$COMPILEMESSAGES -l pl cd $BASE/apps/user_management $COMPILEMESSAGES -l pt $COMPILEMESSAGES -l ru $COMPILEMESSAGES -l es +$COMPILEMESSAGES -l it +$COMPILEMESSAGES -l pl cd $BASE/apps/web_theme $COMPILEMESSAGES -l pt $COMPILEMESSAGES -l ru $COMPILEMESSAGES -l es +$COMPILEMESSAGES -l it +$COMPILEMESSAGES -l pl cd $BASE/apps/django_gpg $COMPILEMESSAGES -l pt $COMPILEMESSAGES -l ru $COMPILEMESSAGES -l es +$COMPILEMESSAGES -l it +$COMPILEMESSAGES -l pl + +cd $BASE/apps/document_signatures +$COMPILEMESSAGES -l pt +$COMPILEMESSAGES -l ru +$COMPILEMESSAGES -l es +$COMPILEMESSAGES -l it +$COMPILEMESSAGES -l pl + +cd $BASE/apps/acls +$COMPILEMESSAGES -l pt +$COMPILEMESSAGES -l ru +$COMPILEMESSAGES -l es +$COMPILEMESSAGES -l it +$COMPILEMESSAGES -l pl + +cd $BASE/apps/feedback +$COMPILEMESSAGES -l pt +$COMPILEMESSAGES -l ru +$COMPILEMESSAGES -l es +$COMPILEMESSAGES -l it +$COMPILEMESSAGES -l pl diff --git a/misc/makemessages_all.sh b/misc/makemessages_all.sh index d1e0fcd79e..96e45e6064 100755 --- a/misc/makemessages_all.sh +++ b/misc/makemessages_all.sh @@ -1,5 +1,5 @@ #!/bin/sh -MAKEMESSAGES="django-admin makemessages" +MAKEMESSAGES="django-admin.py makemessages" PWD=`pwd` BASE=$PWD @@ -8,129 +8,197 @@ $MAKEMESSAGES -l en $MAKEMESSAGES -l pt $MAKEMESSAGES -l ru $MAKEMESSAGES -l es +$MAKEMESSAGES -l it +$MAKEMESSAGES -l pl cd $BASE/apps/converter $MAKEMESSAGES -l en $MAKEMESSAGES -l pt $MAKEMESSAGES -l ru $MAKEMESSAGES -l es +$MAKEMESSAGES -l it +$MAKEMESSAGES -l pl cd $BASE/apps/documents $MAKEMESSAGES -l en $MAKEMESSAGES -l pt $MAKEMESSAGES -l ru $MAKEMESSAGES -l es +$MAKEMESSAGES -l it +$MAKEMESSAGES -l pl cd $BASE/apps/document_comments $MAKEMESSAGES -l en $MAKEMESSAGES -l pt $MAKEMESSAGES -l ru $MAKEMESSAGES -l es +$MAKEMESSAGES -l it +$MAKEMESSAGES -l pl cd $BASE/apps/document_indexing $MAKEMESSAGES -l en $MAKEMESSAGES -l pt $MAKEMESSAGES -l ru $MAKEMESSAGES -l es +$MAKEMESSAGES -l it +$MAKEMESSAGES -l pl cd $BASE/apps/dynamic_search $MAKEMESSAGES -l en $MAKEMESSAGES -l pt $MAKEMESSAGES -l ru $MAKEMESSAGES -l es +$MAKEMESSAGES -l it +$MAKEMESSAGES -l pl cd $BASE/apps/folders $MAKEMESSAGES -l en $MAKEMESSAGES -l pt $MAKEMESSAGES -l ru $MAKEMESSAGES -l es +$MAKEMESSAGES -l it +$MAKEMESSAGES -l pl cd $BASE/apps/history $MAKEMESSAGES -l en $MAKEMESSAGES -l pt $MAKEMESSAGES -l ru $MAKEMESSAGES -l es +$MAKEMESSAGES -l it +$MAKEMESSAGES -l pl cd $BASE/apps/linking $MAKEMESSAGES -l en $MAKEMESSAGES -l pt $MAKEMESSAGES -l ru $MAKEMESSAGES -l es +$MAKEMESSAGES -l it +$MAKEMESSAGES -l pl cd $BASE/apps/main $MAKEMESSAGES -l en $MAKEMESSAGES -l pt $MAKEMESSAGES -l ru $MAKEMESSAGES -l es +$MAKEMESSAGES -l it +$MAKEMESSAGES -l pl cd $BASE/apps/metadata $MAKEMESSAGES -l en $MAKEMESSAGES -l pt $MAKEMESSAGES -l ru $MAKEMESSAGES -l es +$MAKEMESSAGES -l it +$MAKEMESSAGES -l pl cd $BASE/apps/navigation $MAKEMESSAGES -l en $MAKEMESSAGES -l pt $MAKEMESSAGES -l ru $MAKEMESSAGES -l es +$MAKEMESSAGES -l it +$MAKEMESSAGES -l pl cd $BASE/apps/ocr $MAKEMESSAGES -l en $MAKEMESSAGES -l pt $MAKEMESSAGES -l ru $MAKEMESSAGES -l es +$MAKEMESSAGES -l it +$MAKEMESSAGES -l pl cd $BASE/apps/permissions $MAKEMESSAGES -l en $MAKEMESSAGES -l pt $MAKEMESSAGES -l ru $MAKEMESSAGES -l es +$MAKEMESSAGES -l it +$MAKEMESSAGES -l pl cd $BASE/apps/project_setup $MAKEMESSAGES -l en $MAKEMESSAGES -l pt $MAKEMESSAGES -l ru $MAKEMESSAGES -l es +$MAKEMESSAGES -l it +$MAKEMESSAGES -l pl cd $BASE/apps/project_tools $MAKEMESSAGES -l en $MAKEMESSAGES -l pt $MAKEMESSAGES -l ru $MAKEMESSAGES -l es +$MAKEMESSAGES -l it +$MAKEMESSAGES -l pl cd $BASE/apps/smart_settings $MAKEMESSAGES -l en $MAKEMESSAGES -l pt $MAKEMESSAGES -l ru $MAKEMESSAGES -l es +$MAKEMESSAGES -l it +$MAKEMESSAGES -l pl cd $BASE/apps/sources $MAKEMESSAGES -l en $MAKEMESSAGES -l pt $MAKEMESSAGES -l ru $MAKEMESSAGES -l es +$MAKEMESSAGES -l it +$MAKEMESSAGES -l pl cd $BASE/apps/tags $MAKEMESSAGES -l en $MAKEMESSAGES -l pt $MAKEMESSAGES -l ru $MAKEMESSAGES -l es +$MAKEMESSAGES -l it +$MAKEMESSAGES -l pl cd $BASE/apps/user_management $MAKEMESSAGES -l en $MAKEMESSAGES -l pt $MAKEMESSAGES -l ru $MAKEMESSAGES -l es +$MAKEMESSAGES -l it +$MAKEMESSAGES -l pl cd $BASE/apps/web_theme $MAKEMESSAGES -l en $MAKEMESSAGES -l pt $MAKEMESSAGES -l ru $MAKEMESSAGES -l es +$MAKEMESSAGES -l it +$MAKEMESSAGES -l pl cd $BASE/apps/django_gpg $MAKEMESSAGES -l en $MAKEMESSAGES -l pt $MAKEMESSAGES -l ru $MAKEMESSAGES -l es +$MAKEMESSAGES -l it +$MAKEMESSAGES -l pl + +cd $BASE/apps/document_signatures +$MAKEMESSAGES -l en +$MAKEMESSAGES -l pt +$MAKEMESSAGES -l ru +$MAKEMESSAGES -l es +$MAKEMESSAGES -l it +$MAKEMESSAGES -l pl + +cd $BASE/apps/acls +$MAKEMESSAGES -l en +$MAKEMESSAGES -l pt +$MAKEMESSAGES -l ru +$MAKEMESSAGES -l es +$MAKEMESSAGES -l it +$MAKEMESSAGES -l pl + +cd $BASE/apps/feedback +$MAKEMESSAGES -l en +$MAKEMESSAGES -l pt +$MAKEMESSAGES -l ru +$MAKEMESSAGES -l es +$MAKEMESSAGES -l it +$MAKEMESSAGES -l pl diff --git a/misc/mayan.geany b/misc/mayan.geany deleted file mode 100644 index fa2d7fac31..0000000000 --- a/misc/mayan.geany +++ /dev/null @@ -1,41 +0,0 @@ - -[indentation] -indent_width=4 -indent_type=0 -indent_hard_tab_width=8 -detect_indent=false -indent_mode=2 - -[project] -name=mayan -base_path=/home/rosarior/development/mayan/mayan/misc -description= - -[long line marker] -long_line_behaviour=1 -long_line_column=72 - -[files] -current_page=21 -FILE_NAME_0=275;Python;0;16;0;1;0;/home/rosarior/development/mayan/mayan/apps/main/__init__.py;0 -FILE_NAME_1=3931;Python;0;16;0;1;0;/home/rosarior/development/mayan/mayan/apps/documents/__init__.py;0 -FILE_NAME_2=1132;Python;0;16;0;1;0;/home/rosarior/development/mayan/mayan/apps/documents/urls.py;0 -FILE_NAME_3=0;Python;0;16;0;1;0;/home/rosarior/development/mayan/mayan/apps/documents/staging.py;0 -FILE_NAME_4=9731;Python;0;16;0;1;0;/home/rosarior/development/mayan/mayan/apps/documents/views.py;0 -FILE_NAME_5=4010;Python;0;16;0;1;0;/home/rosarior/development/mayan/mayan/apps/converter/api.py;0 -FILE_NAME_6=2005;Python;0;16;0;1;0;/home/rosarior/development/mayan/mayan/apps/documents/conf/settings.py;0 -FILE_NAME_7=541;Python;0;16;0;1;0;/home/rosarior/development/mayan/mayan/apps/converter/conf/settings.py;0 -FILE_NAME_8=1428;Python;0;16;0;1;0;/home/rosarior/development/mayan/mayan/apps/ocr/tasks.py;0 -FILE_NAME_9=3262;Python;0;16;0;1;0;/home/rosarior/development/mayan/mayan/apps/documents/models.py;0 -FILE_NAME_10=2574;Python;0;16;0;1;0;/home/rosarior/development/mayan/mayan/apps/dynamic_search/views.py;0 -FILE_NAME_11=5970;None;0;16;0;1;0;/home/rosarior/development/mayan/mayan/docs/TODO;0 -FILE_NAME_12=679;Markdown;0;16;0;1;0;/home/rosarior/development/mayan/mayan/README.md;0 -FILE_NAME_13=3347;Python;0;16;0;1;0;/home/rosarior/development/mayan/mayan/settings.py;0 -FILE_NAME_14=0;Python;0;16;0;1;0;/home/rosarior/development/mayan/mayan/apps/filesystem_serving/models.py;0 -FILE_NAME_15=2206;Python;0;16;0;1;0;/home/rosarior/development/mayan/mayan/apps/filesystem_serving/api.py;0 -FILE_NAME_16=0;Python;0;16;0;1;0;/home/rosarior/development/mayan/mayan/apps/ocr/views.py;0 -FILE_NAME_17=123;Python;0;16;0;1;0;/home/rosarior/development/mayan/mayan/apps/ocr/api.py;0 -FILE_NAME_18=7270;Python;0;16;0;1;0;/home/rosarior/development/mayan/mayan/apps/permissions/views.py;0 -FILE_NAME_19=3613;HTML;0;16;0;1;0;/home/rosarior/development/mayan/mayan/apps/web_theme/templates/web_theme_base.html;0 -FILE_NAME_20=1800;HTML;0;16;0;1;0;/home/rosarior/development/mayan/mayan/apps/main/templates/base.html;0 -FILE_NAME_21=1588;HTML;0;16;0;1;0;/home/rosarior/development/mayan/mayan/apps/documents/templates/document_upload.html;0 diff --git a/requirements/development.txt b/requirements/development.txt index 67436315af..427ff2cf00 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -1,24 +1,4 @@ -Django==1.3.1 Werkzeug==0.6.2 -django-extensions==0.6 -django-pagination==1.0.7 +django-extensions==0.7.1 django-rosetta==0.6.2 -wsgiref==0.1.2 -celery==2.2.2 -django-celery==2.2.2 -django-sentry==1.6.0 -django-taggit==0.9.3 --e git://github.com/django-mptt/django-mptt.git@0af02a95877041b2fd6d458bd95413dc1666c321#egg=django-mptt --e git://github.com/ahupp/python-magic.git@a75cf0a4a7790eb106155c947af9612f15693b6e#egg=python-magic -slate==0.3 -ghostscript==0.4.1 -pdfminer==20110227 -APScheduler==2.0.2 -Pillow==1.7.4 -cssmin==0.1.4 -django-compressor==1.1.1 --e git://github.com/rosarior/django-sendfile.git#egg=django-sendfile -djangorestframework==0.2.3 -South==0.7.3 -python-gnupg==0.2.8 -python-hkp==0.1.3 +transifex-client==0.6.1 diff --git a/requirements/production.txt b/requirements/production.txt index c6c3feac05..41ab10ed77 100644 --- a/requirements/production.txt +++ b/requirements/production.txt @@ -1,9 +1,6 @@ Django==1.3.1 django-pagination==1.0.7 wsgiref==0.1.2 -celery==2.2.2 -django-celery==2.2.2 -django-sentry==1.6.0 django-taggit==0.9.3 -e git://github.com/django-mptt/django-mptt.git@0af02a95877041b2fd6d458bd95413dc1666c321#egg=django-mptt -e git://github.com/ahupp/python-magic.git@a75cf0a4a7790eb106155c947af9612f15693b6e#egg=python-magic @@ -19,4 +16,4 @@ djangorestframework==0.2.3 South==0.7.3 python-gnupg==0.2.8 python-hkp==0.1.3 -kombu==1.4.2 +requests==0.10.1 diff --git a/runserver_plus.sh b/runserver_plus.sh index 0dda6d19db..5fbf555024 100755 --- a/runserver_plus.sh +++ b/runserver_plus.sh @@ -1,6 +1,6 @@ #!/bin/sh if [ -n "$1" ]; then - ./manage.py runserver_plus $1 --adminmedia ./static/grappelli/ + ./manage.py runserver_plus $1 --adminmedia ./static/admin/ else - ./manage.py runserver_plus --adminmedia ./static/grappelli/ + ./manage.py runserver_plus --adminmedia ./static/admin/ fi diff --git a/settings.py b/settings.py index bd59969f15..a0da8da709 100644 --- a/settings.py +++ b/settings.py @@ -18,7 +18,6 @@ DEVELOPMENT = False TEMPLATE_DEBUG = False ADMINS = () -SENTRY_ADMINS = ('root@localhost',) MANAGERS = ADMINS DATABASES = { @@ -53,6 +52,8 @@ LANGUAGES = ( ('es', ugettext('Spanish')), ('pt', ugettext('Portuguese')), ('ru', ugettext('Russian')), + ('it', ugettext('Italian')), + ('pl', ugettext('Polish')), ) SITE_ID = 1 @@ -117,6 +118,7 @@ TEMPLATE_DIRS = ( ) INSTALLED_APPS = ( +#Django 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', @@ -126,56 +128,62 @@ INSTALLED_APPS = ( 'django.contrib.admindocs', 'django.contrib.comments', 'django.contrib.staticfiles', +# 3rd party +# South + 'south', +# Others + 'filetransfers', + 'taggit', + 'mptt', + 'compressor', + 'djangorestframework', +# Base generic + 'permissions', 'project_setup', 'project_tools', 'smart_settings', 'navigation', 'lock_manager', 'web_theme', +# pagination needs to go after web_theme so that the pagination template +# if found + 'pagination', 'common', 'django_gpg', - 'pagination', 'dynamic_search', - 'filetransfers', + 'acls', 'converter', - 'permissions', - 'djcelery', - 'indexer', - 'paging', - 'sentry', - 'sentry.client', - 'sentry.client.celery', - 'storage', - 'folders', - 'taggit', - 'tags', - 'document_comments', 'user_management', - 'metadata', - 'documents', - 'linking', - 'mptt', - 'document_indexing', - 'ocr', - 'sources', 'mimetype', 'scheduler', 'job_processor', + 'feedback', +# Mayan EDMS + 'storage', + 'folders', + 'tags', + 'document_comments', + 'metadata', + 'documents', + 'linking', + 'document_indexing', + 'document_acls', + 'ocr', + 'sources', 'history', 'main', - 'compressor', - 'djangorestframework', 'rest_api', - 'south', + 'document_signatures', + +# Has to be last so the other apps can register it's signals + 'signaler', ) TEMPLATE_CONTEXT_PROCESSORS = ( 'django.contrib.auth.context_processors.auth', 'django.core.context_processors.i18n', - #'django.core.context_processors.media', 'django.core.context_processors.static', 'django.core.context_processors.request', -# 'django.contrib.messages.context_processors.messages', ) STATICFILES_FINDERS = ( @@ -188,106 +196,7 @@ COMPRESS_PARSER = 'compressor.parser.HtmlParser' COMPRESS_CSS_FILTERS = ['compressor.filters.css_default.CssAbsoluteFilter', 'compressor.filters.cssmin.CSSMinFilter'] COMPRESS_ENABLED=False - -#===== User configuration options =============== -#--------- Pagination ------------------ -#PAGINATION_DEFAULT_PAGINATION = 10 -#--------- Web theme app --------------- -#WEB_THEME_THEME = 'default' -#-------------- Main ----------------- -#MAIN_SIDE_BAR_SEARCH = False -#------------ Common -------------- -# Printing -# from common.literals import PAGE_SIZE_LETTER, PAGE_ORIENTATION_PORTRAIT -#COMMON_DEFAULT_PAPER_SIZE = PAGE_SIZE_LETTER -#COMMON_DEFAULT_PAGE_ORIENTATION = PAGE_ORIENTATION_PORTRAIT -#------------ Storage -------------- -#DOCUMENTS_STORAGE_BACKEND = FileBasedStorage -# GridFS settings -#STORAGE_GRIDFS_HOST = 'localhost' # or list ['host a', 'host b'] -#STORAGE_GRIDFS_PORT = 27017 -#STORAGE_GRIDFS_DATABASE_NAME = u'document_storage' -# Filebased -#STORAGE_FILESTORAGE_LOCATION = u'document_storage' -#---------- Metadata ----------------- -# METADATA_AVAILABLE_FUNCTIONS = {} -# METADATA_AVAILABLE_MODELS = {} -#---------- Indexing ----------------- -#DOCUMENT_INDEXING_AVAILABLE_INDEXING_FUNCTIONS = {} -# Flesystem serving -#DOCUMENT_INDEXING_FILESYSTEM_FILESERVING_ENABLE = True -#DOCUMENT_INDEXING_FILESYSTEM_FILESERVING_PATH = u'/tmp/mayan/documents' -#DOCUMENT_INDEXING_FILESYSTEM_SLUGIFY_PATHS = False -#---------- Documents ------------------ -# Upload -#DOCUMENTS_USE_STAGING_DIRECTORY = False -#DOCUMENTS_STAGING_DIRECTORY = u'/tmp/mayan/staging' -#DOCUMENTS_DELETE_STAGING_FILE_AFTER_UPLOAD = False -#DOCUMENTS_STAGING_FILES_PREVIEW_SIZE = '640x480' -#DOCUMENTS_ENABLE_SINGLE_DOCUMENT_UPLOAD = True -#DOCUMENTS_UNCOMPRESS_COMPRESSED_LOCAL_FILES = True -#DOCUMENTS_UNCOMPRESS_COMPRESSED_STAGING_FILES = True - -# Saving -#DOCUMENTS_CHECKSUM_FUNCTION = lambda x: hashlib.sha256(x).hexdigest()) -#DOCUMENTS_UUID_FUNCTION = lambda:unicode(uuid.uuid4()) -#DOCUMENTS_DEFAULT_TRANSFORMATIONS = [] - -# Usage -#DOCUMENTS_PREVIEW_SIZE = '640x480' -#DOCUMENTS_PRINT_SIZE = '640x480' -#DOCUMENTS_THUMBNAIL_SIZE = '50x50' -#DOCUMENTS_DISPLAY_SIZE = '1200' -#DOCUMENTS_MULTIPAGE_PREVIEW_SIZE = '160x120' -#DOCUMENTS_AVAILABLE_TRANSFORMATIONS = {} -#example: DOCUMENTS_DEFAULT_TRANSFORMATIONS = [{'name':'rotate', 'arguments':"{'degrees':270}"}] -#DOCUMENTS_RECENT_COUNT = 40 -#DOCUMENTS_ZOOM_PERCENT_STEP = 50 -#DOCUMENTS_ZOOM_MAX_LEVEL = 200 -#DOCUMENTS_ZOOM_MIN_LEVEL = 50 -#DOCUMENTS_ROTATION_STEP = 90 - -#------------- Groups -------------------- -#GROUPING_SHOW_EMPTY_GROUPS = True -#------------ Converter -------------- -#CONVERTER_DEFAULT_OPTIONS = u'' -#CONVERTER_LOW_QUALITY_OPTIONS = u'' -#CONVERTER_HIGH_QUALITY_OPTIONS = u'-density 400' -#CONVERTER_OCR_OPTIONS = u'-colorspace Gray -depth 8 -resample 200x200' -#CONVERTER_IM_CONVERT_PATH = u'/usr/bin/convert' -#CONVERTER_IM_IDENTIFY_PATH = u'/usr/bin/identify' -#CONVERTER_UNPAPER_PATH = u'/usr/bin/unpaper' -#CONVERTER_GRAPHICS_BACKEND = u'converter.backends.imagemagick' -#CONVERTER_GM_PATH = u'/usr/bin/gm' -#CONVERTER_GM_SETTINGS = u'' -#------------ OCR -------------- -#OCR_TESSERACT_PATH = u'/usr/bin/tesseract' -#OCR_NODE_CONCURRENT_EXECUTION = 1 -#OCR_TESSERACT_LANGUAGE = u'eng' -#OCR_REPLICATION_DELAY = 10 -#OCR_AUTOMATIC_OCR = False -#OCR_PDFTOTEXT_PATH = u'/usr/bin/pdftotext' -#OCR_QUEUE_PROCESSING_INTERVAL = 10 # In seconds -#OCR_CACHE_URI = None # Can be a single host (u'memcached://127.0.0.1:11211/'), or multiple separated by a semicolon -#------------ Permissions -------------- -#ROLES_DEFAULT_ROLES = [] -#------------ Searching -------------- -#SEARCH_LIMIT = 100 -#------------ django-sendfile -------------- -# Change to xsendfile for apache if x-sendfile is enabled SENDFILE_BACKEND = 'sendfile.backends.simple' -#----------- django-celery -------------- -import djcelery -djcelery.setup_loader() -BROKER_HOST = "localhost" -BROKER_PORT = 5672 -BROKER_USER = "guest" -BROKER_PASSWORD = "guest" -BROKER_VHOST = "/" -CELERYBEAT_SCHEDULER = 'djcelery.schedulers.DatabaseScheduler' -#======== End of user configuration options ======= -#--------- Celery ------------------ -CELERY_DISABLE_RATE_LIMITS = True #--------- Web theme --------------- WEB_THEME_ENABLE_SCROLL_JS = False #--------- Django ------------------- @@ -334,22 +243,19 @@ if DEVELOPMENT: import rosetta INSTALLED_APPS += ('rosetta',) except ImportError: - #print 'rosetta is not installed' pass try: import django_extensions INSTALLED_APPS += ('django_extensions',) except ImportError: - #print 'django_extensions is not installed' pass try: import debug_toolbar #INSTALLED_APPS +=('debug_toolbar',) except ImportError: - #print 'debug_toolbar is not installed' - pass + pass TEMPLATE_CONTEXT_PROCESSORS += ('django.core.context_processors.debug',) diff --git a/urls.py b/urls.py index a4ccd38cef..a6ae677f51 100644 --- a/urls.py +++ b/urls.py @@ -15,7 +15,6 @@ urlpatterns = patterns('', (r'^tags/', include('tags.urls')), (r'^admin/doc/', include('django.contrib.admindocs.urls')), (r'^admin/', include(admin.site.urls)), - (r'^sentry/', include('sentry.urls')), (r'^comments/', include('document_comments.urls')), (r'^user_management/', include('user_management.urls')), (r'^settings/', include('smart_settings.urls')), @@ -27,8 +26,12 @@ urlpatterns = patterns('', (r'^sources/', include('sources.urls')), (r'^project_setup/', include('project_setup.urls')), (r'^project_tools/', include('project_tools.urls')), + (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')), + (r'^feedback/', include('feedback.urls')), ) diff --git a/wsgi/dispatch.wsgi b/wsgi/dispatch.wsgi index fc2615a09a..68cd5368ac 100644 --- a/wsgi/dispatch.wsgi +++ b/wsgi/dispatch.wsgi @@ -4,7 +4,9 @@ import site sys.stdout = sys.stderr -ve_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'lib/python2.6/site-packages')) +#TODO fix properly +ve_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'lib/python2.7/site-packages')) # Change python 2.6 to the python version you are using + # Add the virtual Python environment site-packages directory to the path site.addsitedir(ve_path) @@ -16,7 +18,7 @@ sys.path.insert(0, ve_path) # Avoid ``[Errno 13] Permission denied: '/var/www/.python-eggs'`` messages os.environ['PYTHON_EGG_CACHE'] = '/tmp' os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' -os.environ['CELERY_LOADER'] = 'django' from django.core.handlers.wsgi import WSGIHandler + application = WSGIHandler()