diff --git a/README.md b/README.md
index 7e4b8e1203..000f5f57a3 100644
--- a/README.md
+++ b/README.md
@@ -27,4 +27,3 @@ Donations
---------
Please [donate](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=W6LMMZHTNUJ6L) if you are willing to support the further development of this project.
-[](https://flattr.com/submit/auto?user_id=rosarior&url=http://github.com/rosarior/mayan&title=Mayan EDMS&language=en_GB&tags=github&category=software)
diff --git a/apps/acls/__init__.py b/apps/acls/__init__.py
index 7a83009b39..fe2e4b2cf7 100644
--- a/apps/acls/__init__.py
+++ b/apps/acls/__init__.py
@@ -2,7 +2,7 @@ from __future__ import absolute_import
from django.utils.translation import ugettext_lazy as _
-from navigation.api import register_links, register_multi_item_links
+from navigation.api import bind_links, register_multi_item_links, Link
from project_setup.api import register_setup
from .classes import (AccessHolder, AccessObjectClass, ClassAccessHolder,
@@ -11,29 +11,30 @@ 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_list = Link(text=_(u'ACLs'), view='acl_list', sprite='lock', permissions=[ACLS_VIEW_ACL])
+acl_detail = Link(text=_(u'details'), view='acl_detail', args=['access_object.gid', 'object.gid'], sprite='key_go', permissions=[ACLS_VIEW_ACL])
+acl_grant = Link(text=_(u'grant'), view='acl_multiple_grant', sprite='key_add', permissions=[ACLS_EDIT_ACL])
+acl_revoke = Link(text=_(u'revoke'), view='acl_multiple_revoke', sprite='key_delete', permissions=[ACLS_EDIT_ACL])
+acl_holder_new = Link(text=_(u'New holder'), view='acl_holder_new', args='access_object.gid', sprite='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]}
+acl_setup_valid_classes = Link(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 = Link(text=_(u'List of classes'), view='acl_setup_valid_classes', sprite='package', permissions=[ACLS_CLASS_VIEW_ACL])
+acl_class_acl_list = Link(text=_(u'ACLs for class'), view='acl_class_acl_list', args='object.gid', sprite='lock_go', permissions=[ACLS_CLASS_VIEW_ACL])
+acl_class_acl_detail = Link(text=_(u'details'), view='acl_class_acl_detail', args=['access_object_class.gid', 'object.gid'], sprite='key_go', permissions=[ACLS_CLASS_VIEW_ACL])
+acl_class_new_holder_for = Link(text=_(u'New holder'), view='acl_class_new_holder_for', args='object.gid', sprite='user', permissions=[ACLS_CLASS_EDIT_ACL])
+acl_class_grant = Link(text=_(u'grant'), view='acl_class_multiple_grant', sprite='key_add', permissions=[ACLS_CLASS_EDIT_ACL])
+acl_class_revoke = Link(text=_(u'revoke'), view='acl_class_multiple_revoke', sprite='key_delete', permissions=[ACLS_CLASS_EDIT_ACL])
-register_links(AccessHolder, [acl_detail])
+bind_links([AccessHolder], [acl_detail])
register_multi_item_links(['acl_detail'], [acl_grant, acl_revoke])
-register_links([AccessObject], [acl_holder_new], menu_name='sidebar')
+bind_links([AccessObject], [acl_holder_new], menu_name='sidebar')
+
+bind_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')
+
+bind_links([ClassAccessHolder], [acl_class_acl_detail])
+
+bind_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])
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/common/__init__.py b/apps/common/__init__.py
index 77519a42d3..a92a834217 100644
--- a/apps/common/__init__.py
+++ b/apps/common/__init__.py
@@ -8,7 +8,7 @@ from django.contrib.auth.management import create_superuser
from django.dispatch import receiver
from django.db.models.signals import post_syncdb
-from navigation.api import register_links, register_top_menu
+from navigation.api import bind_links, register_top_menu, Link
from .conf.settings import (AUTO_CREATE_ADMIN, AUTO_ADMIN_USERNAME,
AUTO_ADMIN_PASSWORD, TEMPORARY_DIRECTORY)
@@ -19,18 +19,16 @@ from .utils import validate_path
def has_usable_password(context):
return context['request'].user.has_usable_password
-password_change_view = {'text': _(u'change password'), 'view': 'password_change_view', 'famfam': 'computer_key', 'condition': has_usable_password}
-current_user_details = {'text': _(u'user details'), 'view': 'current_user_details', 'famfam': 'vcard'}
-current_user_edit = {'text': _(u'edit details'), 'view': 'current_user_edit', 'famfam': 'vcard_edit'}
+password_change_view = Link(text=_(u'change password'), view='password_change_view', sprite='computer_key', condition=has_usable_password)
+current_user_details = Link(text=_(u'user details'), view='current_user_details', sprite='vcard')
+current_user_edit = Link(text=_(u'edit details'), view='current_user_edit', sprite='vcard_edit')
+about_view = Link(text=_('about'), view='about_view', sprite='information')
+license_view = Link(text=_('license'), view='license_view', sprite='script')
-register_links(['current_user_details', 'current_user_edit', 'password_change_view'], [current_user_details, current_user_edit, password_change_view], menu_name='secondary_menu')
+bind_links(['about_view', 'license_view'], [about_view, license_view], menu_name='secondary_menu')
+bind_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'}
-license_view = {'text': _('license'), 'view': 'license_view', 'famfam': 'script'}
-
-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)
+register_top_menu('about', link=Link(text=_(u'about'), view='about_view', sprite='information'), position=-1)
@receiver(post_syncdb, dispatch_uid='create_superuser', sender=auth_models)
diff --git a/apps/common/templates/generic_list_horizontal_subtemplate.html b/apps/common/templates/generic_list_horizontal_subtemplate.html
index 8b62536db7..43b3211d89 100644
--- a/apps/common/templates/generic_list_horizontal_subtemplate.html
+++ b/apps/common/templates/generic_list_horizontal_subtemplate.html
@@ -38,7 +38,7 @@
{% for link in multi_item_links %}
- {% if link.famfam and not disable_icons %} {% endif %}{{ link.text|capfirst }}
+ {% if link.sprite and not disable_icons %} {% endif %}{{ link.text|capfirst }}
{% endfor %}
@@ -62,7 +62,11 @@
{% for object in object_list %}
- {{ object }}
+ {% if object %}
+ {{ object }}
+ {% endif %}
+ {% empty %}
+ {% blocktrans with title|striptags as stripped_title %}There are no {{ stripped_title }}{% endblocktrans %}
{% endfor %}
@@ -77,7 +81,7 @@
{% for link in multi_item_links %}
- {% if link.famfam and not disable_icons %} {% endif %}{{ link.text|capfirst }}
+ {% if link.sprite and not disable_icons %} {% endif %}{{ link.text|capfirst }}
{% endfor %}
diff --git a/apps/common/templates/generic_list_subtemplate.html b/apps/common/templates/generic_list_subtemplate.html
index 1e0868a227..fd4076470f 100644
--- a/apps/common/templates/generic_list_subtemplate.html
+++ b/apps/common/templates/generic_list_subtemplate.html
@@ -38,10 +38,12 @@
{% if multi_select_as_buttons %}
{% get_multi_item_links as multi_item_links %}
- {% for link in multi_item_links %}
-
- {% if link.famfam and not disable_icons %} {% endif %}{{ link.text|capfirst }}
-
+ {% for object_reference, object_links in multi_item_links.items %}
+ {% for link in object_links %}
+
+ {% if link.sprite and not disable_icons %} {% endif %}{{ link.text|capfirst }}
+
+ {% endfor %}
{% endfor %}
{% else %}
@@ -170,10 +172,12 @@
{% if multi_select_as_buttons %}
{% get_multi_item_links as multi_item_links %}
- {% for link in multi_item_links %}
-
- {% if link.famfam and not disable_icons %} {% endif %}{{ link.text|capfirst }}
-
+ {% for object_reference, object_links in multi_item_links.items %}
+ {% for link in object_links %}
+
+ {% if link.sprite and not disable_icons %} {% endif %}{{ link.text|capfirst }}
+
+ {% endfor %}
{% endfor %}
{% else %}
diff --git a/apps/converter/__init__.py b/apps/converter/__init__.py
index 29dcb978fe..83fe67b65c 100644
--- a/apps/converter/__init__.py
+++ b/apps/converter/__init__.py
@@ -3,7 +3,7 @@ from __future__ import absolute_import
from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ImproperlyConfigured
-from navigation.api import register_sidebar_template
+from navigation.api import register_sidebar_template, Link
from project_tools.api import register_tool
from .utils import load_backend
@@ -12,7 +12,7 @@ from .conf.settings import GRAPHICS_BACKEND
def is_superuser(context):
return context['request'].user.is_staff or context['request'].user.is_superuser
-formats_list = {'text': _('file formats'), 'view': 'formats_list', 'famfam': 'pictures', 'icon': 'pictures.png', 'condition': is_superuser, 'children_view_regex': [r'formats_list']}
+formats_list = Link(text=_('file formats'), view='formats_list', sprite='pictures', icon='pictures.png', condition=is_superuser, children_view_regex=[r'formats_list'])
register_sidebar_template(['formats_list'], 'converter_file_formats_help.html')
diff --git a/apps/django_gpg/__init__.py b/apps/django_gpg/__init__.py
index a49c0f26b9..44e78fb81d 100644
--- a/apps/django_gpg/__init__.py
+++ b/apps/django_gpg/__init__.py
@@ -2,7 +2,7 @@ from __future__ import absolute_import
from django.utils.translation import ugettext_lazy as _
-from navigation.api import register_links
+from navigation.api import bind_links, Link
from project_setup.api import register_setup
from hkp import Key as KeyServerKey
@@ -11,17 +11,17 @@ from .permissions import (PERMISSION_KEY_VIEW, PERMISSION_KEY_DELETE,
PERMISSION_KEYSERVER_QUERY, PERMISSION_KEY_RECEIVE)
# Setup views
-private_keys = {'text': _(u'private keys'), 'view': 'key_private_list', 'args': 'object.pk', 'famfam': 'key', 'icon': 'key.png', 'permissions': [PERMISSION_KEY_VIEW]}
-public_keys = {'text': _(u'public keys'), 'view': 'key_public_list', 'args': 'object.pk', 'famfam': 'key', 'icon': 'key.png', 'permissions': [PERMISSION_KEY_VIEW]}
-key_delete = {'text': _(u'delete'), 'view': 'key_delete', 'args': ['object.fingerprint', 'object.type'], 'famfam': 'key_delete', 'permissions': [PERMISSION_KEY_DELETE]}
-key_query = {'text': _(u'query keyservers'), 'view': 'key_query', 'famfam': 'zoom', 'permissions': [PERMISSION_KEYSERVER_QUERY]}
-key_receive = {'text': _(u'import'), 'view': 'key_receive', 'args': 'object.keyid', 'famfam': 'key_add', 'keep_query': True, 'permissions': [PERMISSION_KEY_RECEIVE]}
-key_setup = {'text': _(u'key management'), 'view': 'key_public_list', 'args': 'object.pk', 'famfam': 'key', 'icon': 'key.png', 'permissions': [PERMISSION_KEY_VIEW], 'children_view_regex': [r'^key_']}
+private_keys = Link(text=_(u'private keys'), view='key_private_list', args='object.pk', sprite='key', icon='key.png', permissions=[PERMISSION_KEY_VIEW])
+public_keys = Link(text=_(u'public keys'), view='key_public_list', args='object.pk', sprite='key', icon='key.png', permissions=[PERMISSION_KEY_VIEW])
+key_delete = Link(text=_(u'delete'), view='key_delete', args=['object.fingerprint', 'object.type'], sprite='key_delete', permissions=[PERMISSION_KEY_DELETE])
+key_query = Link(text=_(u'query keyservers'), view='key_query', sprite='zoom', permissions=[PERMISSION_KEYSERVER_QUERY])
+key_receive = Link(text=_(u'import'), view='key_receive', args='object.keyid', sprite='key_add', keep_query=True, permissions=[PERMISSION_KEY_RECEIVE])
+key_setup = Link(text=_(u'key management'), view='key_public_list', args='object.pk', sprite='key', icon='key.png', permissions=[PERMISSION_KEY_VIEW], children_view_regex=[r'^key_'])
-#register_links(['key_delete', 'key_private_list', 'key_public_list', 'key_query'], [private_keys, public_keys, key_query], menu_name='sidebar')
-register_links(['key_delete', 'key_public_list', 'key_query'], [public_keys, key_query], menu_name='sidebar')
+#bind_links(['key_delete', 'key_private_list', 'key_public_list', 'key_query'], [private_keys, public_keys, key_query], menu_name='sidebar')
+bind_links(['key_delete', 'key_public_list', 'key_query'], [public_keys, key_query], menu_name='sidebar')
-register_links(Key, [key_delete])
-register_links(KeyServerKey, [key_receive])
+bind_links([Key], [key_delete])
+bind_links([KeyServerKey], [key_receive])
register_setup(key_setup)
diff --git a/apps/document_acls/__init__.py b/apps/document_acls/__init__.py
index 376b048843..e14d021e20 100644
--- a/apps/document_acls/__init__.py
+++ b/apps/document_acls/__init__.py
@@ -1,13 +1,13 @@
from django.utils.translation import ugettext_lazy as _
from documents.models import Document
-from navigation.api import register_links
+from navigation.api import bind_links, Link
from acls import ACLS_VIEW_ACL, ACLS_EDIT_ACL
from acls.api import class_permissions
-acl_list = {'text': _(u'ACLs'), 'view': 'document_acl_list', 'args': 'object.pk', 'famfam': 'lock', 'permissions': [ACLS_VIEW_ACL]}
+acl_list = Link(text=_(u'ACLs'), view='document_acl_list', args='object.pk', sprite='lock', permissions=[ACLS_VIEW_ACL])
-register_links(Document, [acl_list], menu_name='form_header')
+bind_links([Document], [acl_list], menu_name='form_header')
class_permissions(Document, [
ACLS_VIEW_ACL,
diff --git a/apps/document_acls/views.py b/apps/document_acls/views.py
index 5938fec434..16f5cd1ca7 100644
--- a/apps/document_acls/views.py
+++ b/apps/document_acls/views.py
@@ -10,7 +10,4 @@ def document_acl_list(request, document_id):
return acl_list_for(
request,
document,
- extra_context={
- 'object': document,
- }
)
diff --git a/apps/document_comments/__init__.py b/apps/document_comments/__init__.py
index 2356f36885..2d8a304ebc 100644
--- a/apps/document_comments/__init__.py
+++ b/apps/document_comments/__init__.py
@@ -5,7 +5,7 @@ from django.conf import settings
from django.contrib.comments.models import Comment
from django.contrib.contenttypes import generic
-from navigation.api import register_links, register_model_list_columns
+from navigation.api import bind_links, register_model_list_columns, Link
from common.utils import encapsulate
from acls.api import class_permissions
from documents.models import Document
@@ -16,10 +16,10 @@ if 'django.contrib.comments' not in settings.INSTALLED_APPS:
from .permissions import (PERMISSION_COMMENT_CREATE,
PERMISSION_COMMENT_DELETE, PERMISSION_COMMENT_VIEW)
-comment_delete = {'text': _('delete'), 'view': 'comment_delete', 'args': 'object.pk', 'famfam': 'comment_delete', 'permissions': [PERMISSION_COMMENT_DELETE]}
-comment_multiple_delete = {'text': _('delete'), 'view': 'comment_multiple_delete', 'args': 'object.pk', 'famfam': 'comments_delete', 'permissions': [PERMISSION_COMMENT_DELETE]}
-comment_add = {'text': _('add comment'), 'view': 'comment_add', 'args': 'object.pk', 'famfam': 'comment_add', 'permissions': [PERMISSION_COMMENT_CREATE]}
-comments_for_document = {'text': _('comments'), 'view': 'comments_for_document', 'args': 'object.pk', 'famfam': 'comments', 'permissions': [PERMISSION_COMMENT_VIEW], 'children_view_regex': ['comment']}
+comment_delete = Link(text=_('delete'), view='comment_delete', args='object.pk', sprite='comment_delete', permissions=[PERMISSION_COMMENT_DELETE])
+comment_multiple_delete = Link(text=_('delete'), view='comment_multiple_delete', args='object.pk', sprite='comments_delete', permissions=[PERMISSION_COMMENT_DELETE])
+comment_add = Link(text=_('add comment'), view='comment_add', args='object.pk', sprite='comment_add', permissions=[PERMISSION_COMMENT_CREATE])
+comments_for_document = Link(text=_('comments'), view='comments_for_document', args='object.pk', sprite='comments', permissions=[PERMISSION_COMMENT_VIEW], children_view_regex=['comment'])
register_model_list_columns(Comment, [
{
@@ -36,9 +36,9 @@ register_model_list_columns(Comment, [
}
])
-register_links(['comments_for_document', 'comment_add', 'comment_delete', 'comment_multiple_delete'], [comment_add], menu_name='sidebar')
-register_links(Comment, [comment_delete])
-register_links(Document, [comments_for_document], menu_name='form_header')
+bind_links(['comments_for_document', 'comment_add', 'comment_delete', 'comment_multiple_delete'], [comment_add], menu_name='sidebar')
+bind_links([Comment], [comment_delete])
+bind_links([Document], [comments_for_document], menu_name='form_header')
Document.add_to_class(
'comments',
diff --git a/apps/document_indexing/__init__.py b/apps/document_indexing/__init__.py
index 8c79a8f3f9..579facc80d 100644
--- a/apps/document_indexing/__init__.py
+++ b/apps/document_indexing/__init__.py
@@ -7,7 +7,7 @@ from django.db.models.signals import pre_save, post_save, pre_delete
from django.dispatch import receiver
from navigation.api import (register_top_menu, register_sidebar_template,
- register_links)
+ bind_links, Link)
from main.api import register_maintenance_links
from documents.permissions import PERMISSION_DOCUMENT_VIEW
@@ -34,42 +34,36 @@ def is_not_instance_root_node(context):
logger = logging.getLogger(__name__)
-index_setup = {'text': _(u'indexes'), 'view': 'index_setup_list', 'icon': 'tab.png', 'permissions': [PERMISSION_DOCUMENT_INDEXING_SETUP], 'children_view_regex': [r'^index_setup', r'^template_node']}
-index_setup_list = {'text': _(u'index list'), 'view': 'index_setup_list', 'famfam': 'tab', 'permissions': [PERMISSION_DOCUMENT_INDEXING_SETUP]}
-index_setup_create = {'text': _(u'create index'), 'view': 'index_setup_create', 'famfam': 'tab_add', 'permissions': [PERMISSION_DOCUMENT_INDEXING_CREATE]}
-index_setup_edit = {'text': _(u'edit'), 'view': 'index_setup_edit', 'args': 'index.pk', 'famfam': 'tab_edit', 'permissions': [PERMISSION_DOCUMENT_INDEXING_EDIT]}
-index_setup_delete = {'text': _(u'delete'), 'view': 'index_setup_delete', 'args': 'index.pk', 'famfam': 'tab_delete', 'permissions': [PERMISSION_DOCUMENT_INDEXING_DELETE]}
-index_setup_view = {'text': _(u'tree template'), 'view': 'index_setup_view', 'args': 'index.pk', 'famfam': 'textfield', 'permissions': [PERMISSION_DOCUMENT_INDEXING_SETUP]}
+index_setup = Link(text=_(u'indexes'), view='index_setup_list', icon='tab.png', permissions=[PERMISSION_DOCUMENT_INDEXING_SETUP])#, children_view_regex=[r'^index_setup', r'^template_node'])
+index_setup_list = Link(text=_(u'index list'), view='index_setup_list', sprite='tab', permissions=[PERMISSION_DOCUMENT_INDEXING_SETUP])
+index_setup_create = Link(text=_(u'create index'), view='index_setup_create', sprite='tab_add', permissions=[PERMISSION_DOCUMENT_INDEXING_CREATE])
+index_setup_edit = Link(text=_(u'edit'), view='index_setup_edit', args='index.pk', sprite='tab_edit', permissions=[PERMISSION_DOCUMENT_INDEXING_EDIT])
+index_setup_delete = Link(text=_(u'delete'), view='index_setup_delete', args='index.pk', sprite='tab_delete', permissions=[PERMISSION_DOCUMENT_INDEXING_DELETE])
+index_setup_view = Link(text=_(u'tree template'), view='index_setup_view', args='index.pk', sprite='textfield', permissions=[PERMISSION_DOCUMENT_INDEXING_SETUP])
-template_node_create = {'text': _(u'new child node'), 'view': 'template_node_create', 'args': 'node.pk', 'famfam': 'textfield_add', 'permissions': [PERMISSION_DOCUMENT_INDEXING_SETUP]}
-template_node_edit = {'text': _(u'edit'), 'view': 'template_node_edit', 'args': 'node.pk', 'famfam': 'textfield', 'permissions': [PERMISSION_DOCUMENT_INDEXING_SETUP], 'conditional_disable': is_root_node}
-template_node_delete = {'text': _(u'delete'), 'view': 'template_node_delete', 'args': 'node.pk', 'famfam': 'textfield_delete', 'permissions': [PERMISSION_DOCUMENT_INDEXING_SETUP], 'conditional_disable': is_root_node}
+template_node_create = Link(text=_(u'new child node'), view='template_node_create', args='node.pk', sprite='textfield_add', permissions=[PERMISSION_DOCUMENT_INDEXING_SETUP])
+template_node_edit = Link(text=_(u'edit'), view='template_node_edit', args='node.pk', sprite='textfield', permissions=[PERMISSION_DOCUMENT_INDEXING_SETUP], conditional_disable=is_root_node)
+template_node_delete = Link(text=_(u'delete'), view='template_node_delete', args='node.pk', sprite='textfield_delete', permissions=[PERMISSION_DOCUMENT_INDEXING_SETUP], conditional_disable=is_root_node)
-index_list = {'text': _(u'index list'), 'view': 'index_list', 'famfam': 'tab', 'permissions': [PERMISSION_DOCUMENT_INDEXING_VIEW]}
+index_list = Link(text=_(u'index list'), view='index_list', sprite='tab', permissions=[PERMISSION_DOCUMENT_INDEXING_VIEW])
-index_parent = {'text': _(u'go up one level'), 'view': 'index_instance_node_view', 'args': 'object.parent.pk', 'famfam': 'arrow_up', 'permissions': [PERMISSION_DOCUMENT_INDEXING_VIEW], 'dont_mark_active': True, 'condition': is_not_instance_root_node}
-document_index_list = {'text': _(u'indexes'), 'view': 'document_index_list', 'args': 'object.pk', 'famfam': 'folder_page', 'permissions': [PERMISSION_DOCUMENT_INDEXING_VIEW, PERMISSION_DOCUMENT_VIEW]}
+index_parent = Link(text=_(u'go up one level'), view='index_instance_node_view', args='object.parent.pk', sprite='arrow_up', permissions=[PERMISSION_DOCUMENT_INDEXING_VIEW], dont_mark_active=True, condition=is_not_instance_root_node)
+document_index_list = Link(text= _(u'indexes'), view='document_index_list', args='object.pk', sprite='folder_page', permissions=[PERMISSION_DOCUMENT_INDEXING_VIEW, PERMISSION_DOCUMENT_VIEW])
-register_top_menu('indexes', link={'text': _('indexes'), 'famfam': 'tab', 'view': 'index_list', 'children_view_regex': [r'^index_[i,l]']})
-
-rebuild_index_instances = {'text': _('rebuild indexes'), 'view': 'rebuild_index_instances', 'famfam': 'folder_page', 'permissions': [PERMISSION_DOCUMENT_INDEXING_REBUILD_INDEXES], 'description': _(u'Deletes and creates from scratch all the document indexes.')}
+rebuild_index_instances = Link(text=_('rebuild indexes'), view='rebuild_index_instances', sprite='folder_page', permissions=[PERMISSION_DOCUMENT_INDEXING_REBUILD_INDEXES], description=_(u'Deletes and creates from scratch all the document indexes.'))
+register_top_menu('indexes', link=Link(text=_('indexes'), sprite='tab', view='index_list', children_view_regex=[r'^index_[i,l]']))
register_maintenance_links([rebuild_index_instances], namespace='document_indexing', title=_(u'Indexes'))
-
register_sidebar_template(['index_instance_list'], 'indexing_help.html')
-register_links(IndexInstanceNode, [index_parent])
-
-register_links(Document, [document_index_list], menu_name='form_header')
+bind_links([IndexInstanceNode], [index_parent])
+bind_links([Document], [document_index_list], menu_name='form_header')
+bind_links([Index, 'index_setup_list', 'index_setup_create', 'template_node_edit', 'template_node_delete'], [index_setup_list, index_setup_create], menu_name='secondary_menu')
+bind_links([Index], [index_setup_edit, index_setup_delete, index_setup_view])
+bind_links([IndexTemplateNode], [template_node_create, template_node_edit, template_node_delete])
register_setup(index_setup)
-register_links([Index, 'index_setup_list', 'index_setup_create', 'template_node_edit', 'template_node_delete'], [index_setup_list, index_setup_create], menu_name='secondary_menu')
-
-register_links(Index, [index_setup_edit, index_setup_delete, index_setup_view])
-
-register_links(IndexTemplateNode, [template_node_create, template_node_edit, template_node_delete])
-
def delete_indexes_handler(sender, instance, **kwargs):
if isinstance(instance, DocumentVersion):
diff --git a/apps/document_signatures/__init__.py b/apps/document_signatures/__init__.py
index d413453dc2..dcd4dbc7fd 100644
--- a/apps/document_signatures/__init__.py
+++ b/apps/document_signatures/__init__.py
@@ -12,7 +12,7 @@ from django.utils.translation import ugettext_lazy as _
#from django.dispatch import receiver
from documents.models import Document, DocumentVersion
-from navigation.api import register_links
+from navigation.api import bind_links, Link
from django_gpg.runtime import gpg
from django_gpg.exceptions import GPGDecryptionError
from acls.api import class_permissions
@@ -79,13 +79,13 @@ def document_post_save_hook(instance):
# if kwargs.get('created', False):
# DocumentVersionSignature.objects.signature_state(instance.document)
-document_signature_upload = {'text': _(u'upload signature'), 'view': 'document_signature_upload', 'args': 'object.pk', 'famfam': 'pencil_add', 'permissions': [PERMISSION_SIGNATURE_UPLOAD], 'conditional_disable': has_embedded_signature}
-document_signature_download = {'text': _(u'download signature'), 'view': 'document_signature_download', 'args': 'object.pk', 'famfam': 'disk', 'permissions': [PERMISSION_SIGNATURE_DOWNLOAD], 'conditional_disable': doesnt_have_detached_signature}
-document_signature_delete = {'text': _(u'delete signature'), 'view': 'document_signature_delete', 'args': 'object.pk', 'famfam': 'pencil_delete', 'permissions': [PERMISSION_SIGNATURE_DELETE], 'conditional_disable': doesnt_have_detached_signature}
-document_verify = {'text': _(u'signatures'), 'view': 'document_verify', 'args': 'object.pk', 'famfam': 'text_signature', 'permissions': [PERMISSION_DOCUMENT_VERIFY]}
+document_signature_upload = Link(text=_(u'upload signature'), view='document_signature_upload', args='object.pk', sprite='pencil_add', permissions=[PERMISSION_SIGNATURE_UPLOAD], conditional_disable=has_embedded_signature)
+document_signature_download = Link(text=_(u'download signature'), view='document_signature_download', args='object.pk', sprite='disk', permissions=[PERMISSION_SIGNATURE_DOWNLOAD], conditional_disable=doesnt_have_detached_signature)
+document_signature_delete = Link(text=_(u'delete signature'), view='document_signature_delete', args='object.pk', sprite='pencil_delete', permissions=[PERMISSION_SIGNATURE_DELETE], conditional_disable=doesnt_have_detached_signature)
+document_verify = Link(text=_(u'signatures'), view='document_verify', args='object.pk', sprite='text_signature', permissions=[PERMISSION_DOCUMENT_VERIFY])
-register_links(Document, [document_verify], menu_name='form_header')
-register_links(['document_verify', 'document_signature_upload', 'document_signature_download', 'document_signature_delete'], [document_signature_upload, document_signature_download, document_signature_delete], menu_name='sidebar')
+bind_links([Document], [document_verify], menu_name='form_header')
+bind_links(['document_verify', 'document_signature_upload', 'document_signature_download', 'document_signature_delete'], [document_signature_upload, document_signature_download, document_signature_delete], menu_name='sidebar')
DocumentVersion.register_pre_open_hook(1, document_pre_open_hook)
DocumentVersion.register_post_save_hook(1, document_post_save_hook)
diff --git a/apps/documents/__init__.py b/apps/documents/__init__.py
index d3873ad600..8fba2a2c32 100644
--- a/apps/documents/__init__.py
+++ b/apps/documents/__init__.py
@@ -5,9 +5,9 @@ import tempfile
from django.utils.translation import ugettext_lazy as _
from common.utils import validate_path, encapsulate
-from navigation.api import (register_links, register_top_menu,
+from navigation.api import (bind_links, register_top_menu,
register_model_list_columns, register_multi_item_links,
- register_sidebar_template)
+ register_sidebar_template, Link)
from main.api import register_diagnostic, register_maintenance_links
from history.api import register_history_type
from history.permissions import PERMISSION_HISTORY_VIEW
@@ -60,106 +60,107 @@ register_history_type(HISTORY_DOCUMENT_CREATED)
register_history_type(HISTORY_DOCUMENT_EDITED)
register_history_type(HISTORY_DOCUMENT_DELETED)
-document_list = {'text': _(u'all documents'), 'view': 'document_list', 'famfam': 'page'}
-document_list_recent = {'text': _(u'recent documents'), 'view': 'document_list_recent', 'famfam': 'page'}
-document_create_multiple = {'text': _(u'upload new documents'), 'view': 'document_create_multiple', 'famfam': 'page_add', 'permissions': [PERMISSION_DOCUMENT_CREATE], 'children_view_regex': [r'upload_interactive']}
-document_create_siblings = {'text': _(u'clone metadata'), 'view': 'document_create_siblings', 'args': 'object.id', 'famfam': 'page_copy', 'permissions': [PERMISSION_DOCUMENT_CREATE]}
-document_view_simple = {'text': _(u'details'), 'view': 'document_view_simple', 'args': 'object.id', 'famfam': 'page', 'permissions': [PERMISSION_DOCUMENT_VIEW]}
-document_view_advanced = {'text': _(u'properties'), 'view': 'document_view_advanced', 'args': 'object.id', 'famfam': 'page_gear', 'permissions': [PERMISSION_DOCUMENT_VIEW]}
-document_delete = {'text': _(u'delete'), 'view': 'document_delete', 'args': 'object.id', 'famfam': 'page_delete', 'permissions': [PERMISSION_DOCUMENT_DELETE]}
-document_multiple_delete = {'text': _(u'delete'), 'view': 'document_multiple_delete', 'famfam': 'page_delete', 'permissions': [PERMISSION_DOCUMENT_DELETE]}
-document_edit = {'text': _(u'edit'), 'view': 'document_edit', 'args': 'object.id', 'famfam': 'page_edit', 'permissions': [PERMISSION_DOCUMENT_PROPERTIES_EDIT]}
-document_preview = {'text': _(u'preview'), 'class': 'fancybox', 'view': 'document_preview', 'args': 'object.id', 'famfam': 'magnifier', 'permissions': [PERMISSION_DOCUMENT_VIEW]}
-document_download = {'text': _(u'download'), 'view': 'document_download', 'args': 'object.id', 'famfam': 'page_save', 'permissions': [PERMISSION_DOCUMENT_DOWNLOAD]}
-document_multiple_download = {'text': _(u'download'), 'view': 'document_multiple_download', 'famfam': 'page_save', 'permissions': [PERMISSION_DOCUMENT_DOWNLOAD]}
-document_version_download = {'text': _(u'download'), 'view': 'document_version_download', 'args': 'object.pk', 'famfam': 'page_save', 'permissions': [PERMISSION_DOCUMENT_DOWNLOAD]}
-document_find_duplicates = {'text': _(u'find duplicates'), 'view': 'document_find_duplicates', 'args': 'object.id', 'famfam': 'page_white_copy', 'permissions': [PERMISSION_DOCUMENT_VIEW]}
-document_find_all_duplicates = {'text': _(u'find all duplicates'), 'view': 'document_find_all_duplicates', 'famfam': 'page_white_copy', 'permissions': [PERMISSION_DOCUMENT_VIEW], 'description': _(u'Search all the documents\' checksums and return a list of the exact matches.')}
-document_update_page_count = {'text': _(u'update office documents\' page count'), 'view': 'document_update_page_count', 'famfam': 'page_white_csharp', 'permissions': [PERMISSION_DOCUMENT_TOOLS], 'description': _(u'Update the page count of the office type documents. This is useful when enabling office document support after there were already office type documents in the database.')}
-document_clear_transformations = {'text': _(u'clear transformations'), 'view': 'document_clear_transformations', 'args': 'object.id', 'famfam': 'page_paintbrush', 'permissions': [PERMISSION_DOCUMENT_TRANSFORM]}
-document_multiple_clear_transformations = {'text': _(u'clear transformations'), 'view': 'document_multiple_clear_transformations', 'famfam': 'page_paintbrush', 'permissions': [PERMISSION_DOCUMENT_TRANSFORM]}
-document_print = {'text': _(u'print'), 'view': 'document_print', 'args': 'object.id', 'famfam': 'printer', 'permissions': [PERMISSION_DOCUMENT_VIEW]}
-document_history_view = {'text': _(u'history'), 'view': 'history_for_object', 'args': ['"documents"', '"document"', 'object.id'], 'famfam': 'book_go', 'permissions': [PERMISSION_HISTORY_VIEW]}
-document_missing_list = {'text': _(u'Find missing document files'), 'view': 'document_missing_list', 'famfam': 'folder_page', 'permissions': [PERMISSION_DOCUMENT_VIEW]}
+document_list = Link(text=_(u'all documents'), view='document_list', sprite='page')
+document_list_recent = Link(text=_(u'recent documents'), view='document_list_recent', sprite='page')
+document_create_multiple = Link(text=_(u'upload new documents'), view='document_create_multiple', sprite='page_add', permissions=[PERMISSION_DOCUMENT_CREATE], children_view_regex=[r'upload_interactive'])
+document_create_siblings = Link(text=_(u'clone metadata'), view='document_create_siblings', args='object.id', sprite='page_copy', permissions=[PERMISSION_DOCUMENT_CREATE])
+document_view_simple = Link(text=_(u'details'), view='document_view_simple', args='object.id', sprite='page', permissions=[PERMISSION_DOCUMENT_VIEW])
+document_view_advanced = Link(text=_(u'properties'), view='document_view_advanced', args='object.id', sprite='page_gear', permissions=[PERMISSION_DOCUMENT_VIEW])
+document_delete = Link(text=_(u'delete'), view='document_delete', args='object.id', sprite='page_delete', permissions=[PERMISSION_DOCUMENT_DELETE])
+document_multiple_delete = Link(text=_(u'delete'), view='document_multiple_delete', sprite='page_delete', permissions=[PERMISSION_DOCUMENT_DELETE])
+document_edit = Link(text=_(u'edit'), view='document_edit', args='object.id', sprite='page_edit', permissions=[PERMISSION_DOCUMENT_PROPERTIES_EDIT])
+document_preview = Link(text=_(u'preview'), klass='fancybox', view='document_preview', args='object.id', sprite='magnifier', permissions=[PERMISSION_DOCUMENT_VIEW])
+document_download = Link(text=_(u'download'), view='document_download', args='object.id', sprite='page_save', permissions=[PERMISSION_DOCUMENT_DOWNLOAD])
+document_multiple_download = Link(text=_(u'download'), view='document_multiple_download', sprite='page_save', permissions=[PERMISSION_DOCUMENT_DOWNLOAD])
+document_version_download = Link(text=_(u'download'), view='document_version_download', args='object.pk', sprite='page_save', permissions=[PERMISSION_DOCUMENT_DOWNLOAD])
+document_find_duplicates = Link(text=_(u'find duplicates'), view='document_find_duplicates', args='object.id', sprite='page_white_copy', permissions=[PERMISSION_DOCUMENT_VIEW])
+document_find_all_duplicates = Link(text=_(u'find all duplicates'), view='document_find_all_duplicates', sprite='page_white_copy', permissions=[PERMISSION_DOCUMENT_VIEW], description=_(u'Search all the documents\' checksums and return a list of the exact matches.'))
+document_update_page_count = Link(text=_(u'update office documents\' page count'), view='document_update_page_count', sprite='page_white_csharp', permissions=[PERMISSION_DOCUMENT_TOOLS], description=_(u'Update the page count of the office type documents. This is useful when enabling office document support after there were already office type documents in the database.'))
+document_clear_transformations = Link(text=_(u'clear transformations'), view='document_clear_transformations', args='object.id', sprite='page_paintbrush', permissions=[PERMISSION_DOCUMENT_TRANSFORM])
+document_multiple_clear_transformations = Link(text=_(u'clear transformations'), view='document_multiple_clear_transformations', sprite='page_paintbrush', permissions=[PERMISSION_DOCUMENT_TRANSFORM])
+document_print = Link(text=_(u'print'), view='document_print', args='object.id', sprite='printer', permissions=[PERMISSION_DOCUMENT_VIEW])
+document_history_view = Link(text=_(u'history'), view='history_for_object', args=['"documents"', '"document"', 'object.pk'], sprite='book_go', permissions=[PERMISSION_HISTORY_VIEW])
+document_missing_list = Link(text=_(u'Find missing document files'), view='document_missing_list', sprite='folder_page', permissions=[PERMISSION_DOCUMENT_VIEW])
# Tools
-document_clear_image_cache = {'text': _(u'Clear the document image cache'), 'view': 'document_clear_image_cache', 'famfam': 'camera_delete', 'permissions': [PERMISSION_DOCUMENT_TOOLS], 'description': _(u'Clear the graphics representations used to speed up the documents\' display and interactive transformations results.')}
+document_clear_image_cache = Link(text=_(u'Clear the document image cache'), view='document_clear_image_cache', sprite='camera_delete', permissions=[PERMISSION_DOCUMENT_TOOLS], description=_(u'Clear the graphics representations used to speed up the documents\' display and interactive transformations results.'))
# Document pages
-document_page_transformation_list = {'text': _(u'page transformations'), 'class': 'no-parent-history', 'view': 'document_page_transformation_list', 'args': 'page.pk', 'famfam': 'pencil_go', 'permissions': [PERMISSION_DOCUMENT_TRANSFORM]}
-document_page_transformation_create = {'text': _(u'create new transformation'), 'class': 'no-parent-history', 'view': 'document_page_transformation_create', 'args': 'page.pk', 'famfam': 'pencil_add', 'permissions': [PERMISSION_DOCUMENT_TRANSFORM]}
-document_page_transformation_edit = {'text': _(u'edit'), 'class': 'no-parent-history', 'view': 'document_page_transformation_edit', 'args': 'transformation.pk', 'famfam': 'pencil_go', 'permissions': [PERMISSION_DOCUMENT_TRANSFORM]}
-document_page_transformation_delete = {'text': _(u'delete'), 'class': 'no-parent-history', 'view': 'document_page_transformation_delete', 'args': 'transformation.pk', 'famfam': 'pencil_delete', 'permissions': [PERMISSION_DOCUMENT_TRANSFORM]}
+document_page_transformation_list = Link(text=_(u'page transformations'), klass='no-parent-history', view='document_page_transformation_list', args='page.pk', sprite='pencil_go', permissions=[PERMISSION_DOCUMENT_TRANSFORM])
+document_page_transformation_create = Link(text=_(u'create new transformation'), klass='no-parent-history', view='document_page_transformation_create', args='page.pk', sprite='pencil_add', permissions=[PERMISSION_DOCUMENT_TRANSFORM])
+document_page_transformation_edit = Link(text=_(u'edit'), klass='no-parent-history', view='document_page_transformation_edit', args='transformation.pk', sprite='pencil_go', permissions=[PERMISSION_DOCUMENT_TRANSFORM])
+document_page_transformation_delete = Link(text=_(u'delete'), klass='no-parent-history', view='document_page_transformation_delete', args='transformation.pk', sprite='pencil_delete', permissions=[PERMISSION_DOCUMENT_TRANSFORM])
-document_page_view = {'text': _(u'page image'), 'class': 'no-parent-history', 'view': 'document_page_view', 'args': 'page.pk', 'famfam': 'page_white_picture', 'permissions': [PERMISSION_DOCUMENT_VIEW]}
-document_page_text = {'text': _(u'page text'), 'class': 'no-parent-history', 'view': 'document_page_text', 'args': 'page.pk', 'famfam': 'page_white_text', 'permissions': [PERMISSION_DOCUMENT_VIEW]}
-document_page_edit = {'text': _(u'edit page text'), 'class': 'no-parent-history', 'view': 'document_page_edit', 'args': 'page.pk', 'famfam': 'page_white_edit', 'permissions': [PERMISSION_DOCUMENT_EDIT]}
-document_page_navigation_next = {'text': _(u'next page'), 'class': 'no-parent-history', 'view': 'document_page_navigation_next', 'args': 'page.pk', 'famfam': 'resultset_next', 'permissions': [PERMISSION_DOCUMENT_VIEW], 'conditional_disable': is_last_page}
-document_page_navigation_previous = {'text': _(u'previous page'), 'class': 'no-parent-history', 'view': 'document_page_navigation_previous', 'args': 'page.pk', 'famfam': 'resultset_previous', 'permissions': [PERMISSION_DOCUMENT_VIEW], 'conditional_disable': is_first_page}
-document_page_navigation_first = {'text': _(u'first page'), 'class': 'no-parent-history', 'view': 'document_page_navigation_first', 'args': 'page.pk', 'famfam': 'resultset_first', 'permissions': [PERMISSION_DOCUMENT_VIEW], 'conditional_disable': is_first_page}
-document_page_navigation_last = {'text': _(u'last page'), 'class': 'no-parent-history', 'view': 'document_page_navigation_last', 'args': 'page.pk', 'famfam': 'resultset_last', 'permissions': [PERMISSION_DOCUMENT_VIEW], 'conditional_disable': is_last_page}
-document_page_zoom_in = {'text': _(u'zoom in'), 'class': 'no-parent-history', 'view': 'document_page_zoom_in', 'args': 'page.pk', 'famfam': 'zoom_in', 'permissions': [PERMISSION_DOCUMENT_VIEW], 'conditional_disable': is_max_zoom}
-document_page_zoom_out = {'text': _(u'zoom out'), 'class': 'no-parent-history', 'view': 'document_page_zoom_out', 'args': 'page.pk', 'famfam': 'zoom_out', 'permissions': [PERMISSION_DOCUMENT_VIEW], 'conditional_disable': is_min_zoom}
-document_page_rotate_right = {'text': _(u'rotate right'), 'class': 'no-parent-history', 'view': 'document_page_rotate_right', 'args': 'page.pk', 'famfam': 'arrow_turn_right', 'permissions': [PERMISSION_DOCUMENT_VIEW]}
-document_page_rotate_left = {'text': _(u'rotate left'), 'class': 'no-parent-history', 'view': 'document_page_rotate_left', 'args': 'page.pk', 'famfam': 'arrow_turn_left', 'permissions': [PERMISSION_DOCUMENT_VIEW]}
-document_page_view_reset = {'text': _(u'reset view'), 'class': 'no-parent-history', 'view': 'document_page_view_reset', 'args': 'page.pk', 'famfam': 'page_white', 'permissions': [PERMISSION_DOCUMENT_VIEW]}
+document_page_view = Link(text=_(u'page image'), klass='no-parent-history', view='document_page_view', args='page.pk', sprite='page_white_picture', permissions=[PERMISSION_DOCUMENT_VIEW])
+document_page_text = Link(text=_(u'page text'), klass='no-parent-history', view='document_page_text', args='page.pk', sprite='page_white_text', permissions=[PERMISSION_DOCUMENT_VIEW])
+document_page_edit = Link(text=_(u'edit page text'), klass='no-parent-history', view='document_page_edit', args='page.pk', sprite='page_white_edit', permissions=[PERMISSION_DOCUMENT_EDIT])
+document_page_navigation_next = Link(text=_(u'next page'), klass='no-parent-history', view='document_page_navigation_next', args='page.pk', sprite='resultset_next', permissions=[PERMISSION_DOCUMENT_VIEW], conditional_disable=is_last_page)
+document_page_navigation_previous = Link(text=_(u'previous page'), klass='no-parent-history', view='document_page_navigation_previous', args='page.pk', sprite='resultset_previous', permissions=[PERMISSION_DOCUMENT_VIEW], conditional_disable=is_first_page)
+document_page_navigation_first = Link(text=_(u'first page'), klass='no-parent-history', view='document_page_navigation_first', args='page.pk', sprite='resultset_first', permissions=[PERMISSION_DOCUMENT_VIEW], conditional_disable=is_first_page)
+document_page_navigation_last = Link(text=_(u'last page'), klass='no-parent-history', view='document_page_navigation_last', args='page.pk', sprite='resultset_last', permissions=[PERMISSION_DOCUMENT_VIEW], conditional_disable=is_last_page)
+document_page_zoom_in = Link(text=_(u'zoom in'), klass='no-parent-history', view='document_page_zoom_in', args='page.pk', sprite='zoom_in', permissions=[PERMISSION_DOCUMENT_VIEW], conditional_disable=is_max_zoom)
+document_page_zoom_out = Link(text=_(u'zoom out'), klass='no-parent-history', view='document_page_zoom_out', args='page.pk', sprite='zoom_out', permissions=[PERMISSION_DOCUMENT_VIEW], conditional_disable=is_min_zoom)
+document_page_rotate_right = Link(text=_(u'rotate right'), klass='no-parent-history', view='document_page_rotate_right', args='page.pk', sprite='arrow_turn_right', permissions=[PERMISSION_DOCUMENT_VIEW])
+document_page_rotate_left = Link(text=_(u'rotate left'), klass='no-parent-history', view='document_page_rotate_left', args='page.pk', sprite='arrow_turn_left', permissions=[PERMISSION_DOCUMENT_VIEW])
+document_page_view_reset = Link(text=_(u'reset view'), klass='no-parent-history', view='document_page_view_reset', args='page.pk', sprite='page_white', permissions=[PERMISSION_DOCUMENT_VIEW])
# Document versions
-document_version_list = {'text': _(u'versions'), 'view': 'document_version_list', 'args': 'object.pk', 'famfam': 'page_world', 'permissions': [PERMISSION_DOCUMENT_VIEW]}
-document_version_revert = {'text': _(u'revert'), 'view': 'document_version_revert', 'args': 'object.pk', 'famfam': 'page_refresh', 'permissions': [PERMISSION_DOCUMENT_VERSION_REVERT], 'conditional_disable': is_current_version}
+document_version_list = Link(text=_(u'versions'), view='document_version_list', args='object.pk', sprite='page_world', permissions=[PERMISSION_DOCUMENT_VIEW])
+document_version_revert = Link(text=_(u'revert'), view='document_version_revert', args='object.pk', sprite='page_refresh', permissions=[PERMISSION_DOCUMENT_VERSION_REVERT], conditional_disable=is_current_version)
# Document type related links
-document_type_list = {'text': _(u'document type list'), 'view': 'document_type_list', 'famfam': 'layout', 'permissions': [PERMISSION_DOCUMENT_TYPE_VIEW]}
-document_type_setup = {'text': _(u'document types'), 'view': 'document_type_list', 'famfam': 'layout', 'icon': 'layout.png', 'permissions': [PERMISSION_DOCUMENT_TYPE_VIEW], 'children_view_regex': [r'^document_type_']}
-document_type_document_list = {'text': _(u'documents of this type'), 'view': 'document_type_document_list', 'args': 'document_type.id', 'famfam': 'page_go', 'permissions': [PERMISSION_DOCUMENT_TYPE_VIEW]}
-document_type_edit = {'text': _(u'edit'), 'view': 'document_type_edit', 'args': 'document_type.id', 'famfam': 'layout_edit', 'permissions': [PERMISSION_DOCUMENT_TYPE_EDIT]}
-document_type_delete = {'text': _(u'delete'), 'view': 'document_type_delete', 'args': 'document_type.id', 'famfam': 'layout_delete', 'permissions': [PERMISSION_DOCUMENT_TYPE_DELETE]}
-document_type_create = {'text': _(u'create document type'), 'view': 'document_type_create', 'famfam': 'layout_add', 'permissions': [PERMISSION_DOCUMENT_TYPE_CREATE]}
+document_type_list = Link(text=_(u'document type list'), view='document_type_list', sprite='layout', permissions=[PERMISSION_DOCUMENT_TYPE_VIEW])
+document_type_setup = Link(text=_(u'document types'), view='document_type_list', sprite='layout', icon='layout.png', permissions=[PERMISSION_DOCUMENT_TYPE_VIEW], children_view_regex=[r'^document_type_'])
+document_type_document_list = Link(text=_(u'documents of this type'), view='document_type_document_list', args='document_type.id', sprite='page_go', permissions=[PERMISSION_DOCUMENT_TYPE_VIEW])
+document_type_edit = Link(text=_(u'edit'), view='document_type_edit', args='document_type.id', sprite='layout_edit', permissions=[PERMISSION_DOCUMENT_TYPE_EDIT])
+document_type_delete = Link(text=_(u'delete'), view='document_type_delete', args='document_type.id', sprite='layout_delete', permissions=[PERMISSION_DOCUMENT_TYPE_DELETE])
+document_type_create = Link(text=_(u'create document type'), view='document_type_create', sprite='layout_add', permissions=[PERMISSION_DOCUMENT_TYPE_CREATE])
-document_type_filename_list = {'text': _(u'filenames'), 'view': 'document_type_filename_list', 'args': 'document_type.id', 'famfam': 'database', 'permissions': [PERMISSION_DOCUMENT_TYPE_VIEW]}
-document_type_filename_create = {'text': _(u'add filename to document type'), 'view': 'document_type_filename_create', 'args': 'document_type.id', 'famfam': 'database_add', 'permissions': [PERMISSION_DOCUMENT_TYPE_EDIT]}
-document_type_filename_edit = {'text': _(u'edit'), 'view': 'document_type_filename_edit', 'args': 'filename.id', 'famfam': 'database_edit', 'permissions': [PERMISSION_DOCUMENT_TYPE_EDIT]}
-document_type_filename_delete = {'text': _(u'delete'), 'view': 'document_type_filename_delete', 'args': 'filename.id', 'famfam': 'database_delete', 'permissions': [PERMISSION_DOCUMENT_TYPE_EDIT]}
+document_type_filename_list = Link(text=_(u'filenames'), view='document_type_filename_list', args='document_type.id', sprite='database', permissions=[PERMISSION_DOCUMENT_TYPE_VIEW])
+document_type_filename_create = Link(text=_(u'add filename to document type'), view='document_type_filename_create', args='document_type.id', sprite='database_add', permissions=[PERMISSION_DOCUMENT_TYPE_EDIT])
+document_type_filename_edit = Link(text=_(u'edit'), view='document_type_filename_edit', args='filename.id', sprite='database_edit', permissions=[PERMISSION_DOCUMENT_TYPE_EDIT])
+document_type_filename_delete = Link(text=_(u'delete'), view='document_type_filename_delete', args='filename.id', sprite='database_delete', permissions=[PERMISSION_DOCUMENT_TYPE_EDIT])
-document_type_views = ['setup_document_type_metadata', 'document_type_list', 'document_type_document_list', 'document_type_edit', 'document_type_delete', 'document_type_create', 'document_type_filename_list', 'document_type_filename_create', 'document_type_filename_edit', 'document_type_filename_delete']
+# TODO: remove this
+document_type_views=['setup_document_type_metadata', 'document_type_list', 'document_type_document_list', 'document_type_edit', 'document_type_delete', 'document_type_create', 'document_type_filename_list', 'document_type_filename_create', 'document_type_filename_edit', 'document_type_filename_delete']
# Register document type links
-register_links(DocumentType, [document_type_document_list, document_type_filename_list, document_type_edit, document_type_delete])
-register_links(DocumentTypeFilename, [document_type_filename_edit, document_type_filename_delete])
+bind_links([DocumentType], [document_type_document_list, document_type_filename_list, document_type_edit, document_type_delete])
+bind_links([DocumentTypeFilename], [document_type_filename_edit, document_type_filename_delete])
-register_links(['setup_document_type_metadata', 'document_type_filename_delete', 'document_type_create', 'document_type_filename_create', 'document_type_filename_edit', 'document_type_filename_list', 'document_type_list', 'document_type_document_list', 'document_type_edit', 'document_type_delete'], [document_type_list, document_type_create], menu_name='sidebar')
-register_links(['document_type_filename_create', 'document_type_filename_list', 'document_type_filename_edit', 'document_type_filename_delete'], [document_type_filename_create], menu_name='sidebar')
+bind_links(['setup_document_type_metadata', 'document_type_filename_delete', 'document_type_create', 'document_type_filename_create', 'document_type_filename_edit', 'document_type_filename_list', 'document_type_list', 'document_type_document_list', 'document_type_edit', 'document_type_delete'], [document_type_list, document_type_create], menu_name='sidebar')
+bind_links(['document_type_filename_create', 'document_type_filename_list', 'document_type_filename_edit', 'document_type_filename_delete'], [document_type_filename_create], menu_name='sidebar')
# Register document links
-register_links(Document, [document_view_simple, document_edit, document_print, document_delete, document_download, document_find_duplicates, document_clear_transformations, document_create_siblings])
+bind_links([Document], [document_view_simple, document_edit, document_print, document_delete, document_download, document_find_duplicates, document_clear_transformations, document_create_siblings])
register_multi_item_links(['document_find_duplicates', 'folder_view', 'index_instance_list', 'document_type_document_list', 'search', 'results', 'document_group_view', 'document_list', 'document_list_recent'], [document_multiple_clear_transformations, document_multiple_delete, document_multiple_download])
# Document Version links
-register_links(DocumentVersion, [document_version_revert, document_version_download])
+bind_links([DocumentVersion], [document_version_revert, document_version_download])
secondary_menu_links = [document_list_recent, document_list, document_create_multiple]
-register_links(['document_list_recent', 'document_list', 'document_create', 'document_create_multiple', 'upload_interactive', 'staging_file_delete'], secondary_menu_links, menu_name='secondary_menu')
-register_links(Document, secondary_menu_links, menu_name='secondary_menu')
+bind_links(['document_list_recent', 'document_list', 'document_create', 'document_create_multiple', 'upload_interactive', 'staging_file_delete'], secondary_menu_links, menu_name='secondary_menu')
+bind_links([Document], secondary_menu_links, menu_name='secondary_menu')
# Document page links
-register_links(DocumentPage, [
+bind_links([DocumentPage], [
document_page_transformation_list, document_page_view,
document_page_text, document_page_edit,
])
# Document page navigation links
-register_links(DocumentPage, [
+bind_links([DocumentPage], [
document_page_navigation_first, document_page_navigation_previous,
document_page_navigation_next, document_page_navigation_last
-], menu_name='related')
+], menu_name='sidebar')
-register_links(['document_page_view'], [document_page_rotate_left, document_page_rotate_right, document_page_zoom_in, document_page_zoom_out, document_page_view_reset], menu_name='form_header')
+bind_links(['document_page_view'], [document_page_rotate_left, document_page_rotate_right, document_page_zoom_in, document_page_zoom_out, document_page_view_reset], menu_name='form_header')
-register_links(DocumentPageTransformation, [document_page_transformation_edit, document_page_transformation_delete])
-register_links('document_page_transformation_list', [document_page_transformation_create], menu_name='sidebar')
-register_links('document_page_transformation_create', [document_page_transformation_create], menu_name='sidebar')
-register_links(['document_page_transformation_edit', 'document_page_transformation_delete'], [document_page_transformation_create], menu_name='sidebar')
+bind_links([DocumentPageTransformation], [document_page_transformation_edit, document_page_transformation_delete])
+bind_links('document_page_transformation_list', [document_page_transformation_create], menu_name='sidebar')
+bind_links('document_page_transformation_create', [document_page_transformation_create], menu_name='sidebar')
+bind_links(['document_page_transformation_edit', 'document_page_transformation_delete'], [document_page_transformation_create], menu_name='sidebar')
register_diagnostic('documents', _(u'Documents'), document_missing_list)
@@ -176,22 +177,20 @@ register_model_list_columns(Document, [
register_top_menu(
'documents',
- link={'famfam': 'page', 'text': _(u'documents'), 'view': 'document_list_recent'},
- children_path_regex=[
- r'^documents/[^t]', r'^metadata/[^s]', r'comments', r'tags/document', r'grouping/[^s]', r'history/list/for_object/documents',
- ],
+ link=Link(sprite='page', text=_(u'documents'), view='document_list_recent',
+ children_url_regex=[r'^documents/[^t]', r'^metadata/[^s]', r'comments', r'tags/document', r'grouping/[^s]', r'history/list/for_object/documents'],
children_view_regex=[r'document_acl', r'smart_link_instance'],
- children_views=['document_folder_list', 'folder_add_document', 'document_index_list', 'upload_version', ],
+ children_views=['document_folder_list', 'folder_add_document', 'document_index_list', 'upload_version', ]),
position=1
)
register_sidebar_template(['document_list_recent'], 'recent_document_list_help.html')
register_sidebar_template(['document_type_list'], 'document_types_help.html')
-register_links(Document, [document_view_simple], menu_name='form_header', position=0)
-register_links(Document, [document_view_advanced], menu_name='form_header', position=1)
-register_links(Document, [document_history_view], menu_name='form_header')
-register_links(Document, [document_version_list], menu_name='form_header')
+bind_links([Document], [document_view_simple], menu_name='form_header', position=0)
+bind_links([Document], [document_view_advanced], menu_name='form_header', position=1)
+bind_links([Document], [document_history_view], menu_name='form_header')
+bind_links([Document], [document_version_list], menu_name='form_header')
if (validate_path(document_settings.CACHE_PATH) == False) or (not document_settings.CACHE_PATH):
setattr(document_settings, 'CACHE_PATH', tempfile.mkdtemp())
diff --git a/apps/documents/views.py b/apps/documents/views.py
index 70846628a6..17e6ff9950 100644
--- a/apps/documents/views.py
+++ b/apps/documents/views.py
@@ -259,8 +259,6 @@ def document_edit(request, document_id):
else:
new_filename = form.cleaned_data['new_filename']
- print 'new_filename', new_filename
-
document.filename = new_filename
document.description = form.cleaned_data['description']
document.save()
diff --git a/apps/dynamic_search/__init__.py b/apps/dynamic_search/__init__.py
index cb35778be2..b2e441fa50 100644
--- a/apps/dynamic_search/__init__.py
+++ b/apps/dynamic_search/__init__.py
@@ -7,7 +7,7 @@ from django.dispatch import receiver
from django.core.management import call_command
-from navigation.api import register_sidebar_template, register_links
+from navigation.api import register_sidebar_template, bind_links, Link
from documents.models import Document
from scheduler.runtime import scheduler
from signaler.signals import post_update_index, pre_update_index
@@ -19,7 +19,9 @@ from .conf.settings import INDEX_UPDATE_INTERVAL
logger = logging.getLogger(__name__)
-search = {'text': _(u'search'), 'view': 'search', 'famfam': 'zoom'}
+search = Link(text=_(u'search'), view='search', sprite='zoom')
+search_advanced = Link(text=_(u'advanced search'), view='search_advanced', sprite='zoom_in')
+search_again = Link(text=_(u'search again'), view='search_again', sprite='arrow_undo')
register_sidebar_template(['search'], 'search_help.html')
@@ -59,5 +61,7 @@ def search_index_update():
lock.release()
pass
+bind_links(['search', 'search_advanced', 'results'], [search, search_advanced], menu_name='form_header')
+bind_links(['results'], [search_again], menu_name='sidebar')
register_interval_job('search_index_update', _(u'Update the search index with the most recent modified documents.'), search_index_update, seconds=INDEX_UPDATE_INTERVAL)
diff --git a/apps/feedback/__init__.py b/apps/feedback/__init__.py
index 8e6c005ae4..7229d895ae 100644
--- a/apps/feedback/__init__.py
+++ b/apps/feedback/__init__.py
@@ -2,10 +2,10 @@ from __future__ import absolute_import
from django.utils.translation import ugettext_lazy as _
-from navigation.api import register_links
+from navigation.api import bind_links, Link
from common import about_view, license_view
-form_view = {'text': _('Feedback'), 'view': 'form_view', 'famfam': 'telephone'}
+form_view = Link(text=_('Feedback'), view='form_view', sprite='telephone')
-register_links(['form_view'], [about_view, license_view], menu_name='secondary_menu')
-register_links(['form_view', 'about_view', 'license_view'], [form_view], menu_name='secondary_menu')
+bind_links(['form_view'], [about_view, license_view], menu_name='secondary_menu')
+bind_links(['form_view', 'about_view', 'license_view'], [form_view], menu_name='secondary_menu')
diff --git a/apps/folders/__init__.py b/apps/folders/__init__.py
index 8bbc3126b2..f72c92fd4f 100644
--- a/apps/folders/__init__.py
+++ b/apps/folders/__init__.py
@@ -2,8 +2,8 @@ from __future__ import absolute_import
from django.utils.translation import ugettext_lazy as _
-from navigation.api import (register_links, register_top_menu,
- register_multi_item_links, register_sidebar_template)
+from navigation.api import (bind_links, register_top_menu,
+ register_multi_item_links, register_sidebar_template, Link)
from documents.models import Document
from documents.permissions import PERMISSION_DOCUMENT_VIEW
from acls.api import class_permissions
@@ -15,30 +15,30 @@ from .permissions import (PERMISSION_FOLDER_CREATE,
PERMISSION_FOLDER_REMOVE_DOCUMENT, PERMISSION_FOLDER_VIEW,
PERMISSION_FOLDER_ADD_DOCUMENT)
-folder_list = {'text': _(u'folder list'), 'view': 'folder_list', 'famfam': 'folder_user'}
-folder_create = {'text': _('create folder'), 'view': 'folder_create', 'famfam': 'folder_add', 'permissions': [PERMISSION_FOLDER_CREATE]}
-folder_edit = {'text': _('edit'), 'view': 'folder_edit', 'args': 'object.pk', 'famfam': 'folder_edit', 'permissions': [PERMISSION_FOLDER_EDIT]}
-folder_delete = {'text': _('delete'), 'view': 'folder_delete', 'args': 'object.pk', 'famfam': 'folder_delete', 'permissions': [PERMISSION_FOLDER_DELETE]}
-folder_document_multiple_remove = {'text': _('remove from folder'), 'view': 'folder_document_multiple_remove', 'args': 'object.pk', 'famfam': 'delete', 'permissions': [PERMISSION_FOLDER_REMOVE_DOCUMENT]}
-folder_view = {'text': _(u'folder documents'), 'view': 'folder_view', 'args': 'object.pk', 'famfam': 'folder_go', 'permissions': [PERMISSION_FOLDER_VIEW]}
-folder_add_document = {'text': _('add to a folder'), 'view': 'folder_add_document', 'args': 'object.pk', 'famfam': 'add', 'permissions': [PERMISSION_FOLDER_ADD_DOCUMENT]}
-document_folder_list = {'text': _(u'folders'), 'view': 'document_folder_list', 'args': 'object.pk', 'famfam': 'folder_user', 'permissions': [PERMISSION_DOCUMENT_VIEW], 'children_view_regex': [r'folder']}
+folder_list = Link(text=_(u'folder list'), view='folder_list', sprite='folder_user')
+folder_create = Link(text=_('create folder'), view='folder_create', sprite='folder_add', permissions=[PERMISSION_FOLDER_CREATE])
+folder_edit = Link(text=_('edit'), view='folder_edit', args='object.pk', sprite='folder_edit', permissions=[PERMISSION_FOLDER_EDIT])
+folder_delete = Link(text=_('delete'), view='folder_delete', args='object.pk', sprite='folder_delete', permissions=[PERMISSION_FOLDER_DELETE])
+folder_document_multiple_remove = Link(text=_('remove from folder'), view='folder_document_multiple_remove', args='object.pk', sprite='delete', permissions=[PERMISSION_FOLDER_REMOVE_DOCUMENT])
+folder_view = Link(text=_(u'folder documents'), view='folder_view', args='object.pk', sprite='folder_go', permissions=[PERMISSION_FOLDER_VIEW])
+folder_add_document = Link(text=_('add to a folder'), view='folder_add_document', args='object.pk', sprite='add', permissions=[PERMISSION_FOLDER_ADD_DOCUMENT])
+document_folder_list = Link(text=_(u'folders'), view='document_folder_list', args='object.pk', sprite='folder_user', permissions=[PERMISSION_DOCUMENT_VIEW], children_view_regex=[r'folder'])
-folder_acl_list = {'text': _(u'ACLs'), 'view': 'folder_acl_list', 'args': 'object.pk', 'famfam': 'lock', 'permissions': [ACLS_VIEW_ACL]}
+folder_acl_list = Link(text=_(u'ACLs'), view='folder_acl_list', args='object.pk', sprite='lock', permissions=[ACLS_VIEW_ACL])
register_multi_item_links(['folder_view'], [folder_document_multiple_remove])
-register_links(Folder, [folder_view, folder_edit, folder_delete, folder_acl_list])
+bind_links([Folder], [folder_view, folder_edit, folder_delete, folder_acl_list])
-register_links([Folder, 'folder_list', 'folder_create'], [folder_list, folder_create], menu_name='secondary_menu')
+bind_links([Folder, 'folder_list', 'folder_create'], [folder_list, folder_create], menu_name='secondary_menu')
-register_top_menu(name='folders', link={'text': _('folders'), 'famfam': 'folder_user', 'view': 'folder_list'}, children_views=['folder_list', 'folder_create', 'folder_edit', 'folder_delete', 'folder_view', 'folder_document_multiple_remove'])
+register_top_menu(name='folders', link=Link(text=_('folders'), sprite='folder_user', view='folder_list', children_views=['folder_list', 'folder_create', 'folder_edit', 'folder_delete', 'folder_view', 'folder_document_multiple_remove']))
-register_links(Document, [document_folder_list], menu_name='form_header')
+bind_links([Document], [document_folder_list], menu_name='form_header')
register_sidebar_template(['folder_list'], 'folders_help.html')
-register_links(['document_folder_list', 'folder_add_document'], [folder_add_document], menu_name="sidebar")
+bind_links(['document_folder_list', 'folder_add_document'], [folder_add_document], menu_name="sidebar")
class_permissions(Folder, [
PERMISSION_FOLDER_EDIT,
diff --git a/apps/folders/views.py b/apps/folders/views.py
index 3c4a494aa6..257a0ec886 100644
--- a/apps/folders/views.py
+++ b/apps/folders/views.py
@@ -32,7 +32,6 @@ logger = logging.getLogger(__name__)
def folder_list(request, queryset=None, extra_context=None):
context = {
'title': _(u'folders'),
- 'multi_select_as_buttons': True,
'extra_columns': [
{'name': _(u'created'), 'attribute': 'datetime_created'},
{'name': _(u'documents'), 'attribute': encapsulate(lambda x: x.folderdocument_set.count())}
diff --git a/apps/history/__init__.py b/apps/history/__init__.py
index b0b4025a3e..1ecda16eea 100644
--- a/apps/history/__init__.py
+++ b/apps/history/__init__.py
@@ -3,10 +3,8 @@ from __future__ import absolute_import
from django.utils.translation import ugettext_lazy as _
from project_tools.api import register_tool
+from navigation.api import Link
from .permissions import PERMISSION_HISTORY_VIEW
-
-history_list = {'text': _(u'history'), 'view': 'history_list', 'famfam': 'book', 'icon': 'book.png', 'permissions': [PERMISSION_HISTORY_VIEW], 'children_view_regex': [r'history_[l,v]']}
-
-register_tool(history_list)
+register_tool(Link(text=_(u'history'), view='history_list', sprite='book', icon='book.png', permissions=[PERMISSION_HISTORY_VIEW], children_view_regex=[r'history_[l,v]']))
diff --git a/apps/linking/__init__.py b/apps/linking/__init__.py
index b6608ca829..14caa3309b 100644
--- a/apps/linking/__init__.py
+++ b/apps/linking/__init__.py
@@ -2,7 +2,7 @@ from __future__ import absolute_import
from django.utils.translation import ugettext_lazy as _
-from navigation.api import register_links, register_sidebar_template
+from navigation.api import bind_links, register_sidebar_template, Link
from project_setup.api import register_setup
from documents.permissions import PERMISSION_DOCUMENT_VIEW
from documents.models import Document
@@ -14,29 +14,29 @@ from .permissions import (PERMISSION_SMART_LINK_VIEW,
PERMISSION_SMART_LINK_CREATE, PERMISSION_SMART_LINK_DELETE,
PERMISSION_SMART_LINK_EDIT)
-smart_link_instance_view_link = {'text': _(u'smart links actions'), 'view': 'smart_link_instance_view', 'famfam': 'page_link', 'permissions': [PERMISSION_DOCUMENT_VIEW]}
-smart_link_instances_for_document = {'text': _(u'smart links'), 'view': 'smart_link_instances_for_document', 'args': 'object.pk', 'famfam': 'page_link', 'permissions': [PERMISSION_DOCUMENT_VIEW]}
+smart_link_instance_view_link = Link(text=_(u'smart links actions'), view='smart_link_instance_view', sprite='page_link', permissions=[PERMISSION_DOCUMENT_VIEW])
+smart_link_instances_for_document = Link(text=_(u'smart links'), view='smart_link_instances_for_document', args='object.pk', sprite='page_link', permissions=[PERMISSION_DOCUMENT_VIEW])
-smart_link_setup = {'text': _(u'smart links'), 'view': 'smart_link_list', 'icon': 'link.png', 'permissions': [PERMISSION_SMART_LINK_CREATE], 'children_view_regex': [r'smart_link_list', 'smart_link_create', 'smart_link_delete', 'smart_link_edit', 'smart_link_condition_']}
-smart_link_list = {'text': _(u'smart links list'), 'view': 'smart_link_list', 'famfam': 'link', 'permissions': [PERMISSION_SMART_LINK_CREATE]}
-smart_link_create = {'text': _(u'create new smart link'), 'view': 'smart_link_create', 'famfam': 'link_add', 'permissions': [PERMISSION_SMART_LINK_CREATE]}
-smart_link_edit = {'text': _(u'edit'), 'view': 'smart_link_edit', 'args': 'object.pk', 'famfam': 'link_edit', 'permissions': [PERMISSION_SMART_LINK_EDIT]}
-smart_link_delete = {'text': _(u'delete'), 'view': 'smart_link_delete', 'args': 'object.pk', 'famfam': 'link_delete', 'permissions': [PERMISSION_SMART_LINK_DELETE]}
+smart_link_setup = Link(text=_(u'smart links'), view='smart_link_list', icon='link.png', permissions=[PERMISSION_SMART_LINK_CREATE], children_view_regex=[r'smart_link_list', 'smart_link_create', 'smart_link_delete', 'smart_link_edit', 'smart_link_condition_'])
+smart_link_list = Link(text=_(u'smart links list'), view='smart_link_list', sprite='link', permissions=[PERMISSION_SMART_LINK_CREATE])
+smart_link_create = Link(text=_(u'create new smart link'), view='smart_link_create', sprite='link_add', permissions=[PERMISSION_SMART_LINK_CREATE])
+smart_link_edit = Link(text=_(u'edit'), view='smart_link_edit', args='object.pk', sprite='link_edit', permissions=[PERMISSION_SMART_LINK_EDIT])
+smart_link_delete = Link(text=_(u'delete'), view='smart_link_delete', args='object.pk', sprite='link_delete', permissions=[PERMISSION_SMART_LINK_DELETE])
-smart_link_condition_list = {'text': _(u'conditions'), 'view': 'smart_link_condition_list', 'args': 'object.pk', 'famfam': 'cog', 'permissions': [PERMISSION_SMART_LINK_CREATE, PERMISSION_SMART_LINK_CREATE]}
-smart_link_condition_create = {'text': _(u'create condition'), 'view': 'smart_link_condition_create', 'args': 'object.pk', 'famfam': 'cog_add', 'permissions': [PERMISSION_SMART_LINK_CREATE, PERMISSION_SMART_LINK_EDIT]}
-smart_link_condition_edit = {'text': _(u'edit'), 'view': 'smart_link_condition_edit', 'args': 'condition.pk', 'famfam': 'cog_edit', 'permissions': [PERMISSION_SMART_LINK_CREATE, PERMISSION_SMART_LINK_EDIT]}
-smart_link_condition_delete = {'text': _(u'delete'), 'view': 'smart_link_condition_delete', 'args': 'condition.pk', 'famfam': 'cog_delete', 'permissions': [PERMISSION_SMART_LINK_CREATE, PERMISSION_SMART_LINK_EDIT]}
+smart_link_condition_list = Link(text=_(u'conditions'), view='smart_link_condition_list', args='object.pk', sprite='cog', permissions=[PERMISSION_SMART_LINK_CREATE, PERMISSION_SMART_LINK_CREATE])
+smart_link_condition_create = Link(text=_(u'create condition'), view='smart_link_condition_create', args='object.pk', sprite='cog_add', permissions=[PERMISSION_SMART_LINK_CREATE, PERMISSION_SMART_LINK_EDIT])
+smart_link_condition_edit = Link(text=_(u'edit'), view='smart_link_condition_edit', args='condition.pk', sprite='cog_edit', permissions=[PERMISSION_SMART_LINK_CREATE, PERMISSION_SMART_LINK_EDIT])
+smart_link_condition_delete = Link(text=_(u'delete'), view='smart_link_condition_delete', args='condition.pk', sprite='cog_delete', permissions=[PERMISSION_SMART_LINK_CREATE, PERMISSION_SMART_LINK_EDIT])
-smart_link_acl_list = {'text': _(u'ACLs'), 'view': 'smart_link_acl_list', 'args': 'object.pk', 'famfam': 'lock', 'permissions': [ACLS_VIEW_ACL]}
+smart_link_acl_list = Link(text=_(u'ACLs'), view='smart_link_acl_list', args='object.pk', sprite='lock', permissions=[ACLS_VIEW_ACL])
-register_links(Document, [smart_link_instances_for_document], menu_name='form_header')
+bind_links([Document], [smart_link_instances_for_document], menu_name='form_header')
-register_links(SmartLink, [smart_link_edit, smart_link_delete, smart_link_condition_list, smart_link_acl_list])
-register_links([SmartLink, 'smart_link_list', 'smart_link_create'], [smart_link_list, smart_link_create], menu_name='secondary_menu')
+bind_links([SmartLink], [smart_link_edit, smart_link_delete, smart_link_condition_list, smart_link_acl_list])
+bind_links([SmartLink, 'smart_link_list', 'smart_link_create'], [smart_link_list, smart_link_create], menu_name='secondary_menu')
-register_links(SmartLinkCondition, [smart_link_condition_edit, smart_link_condition_delete])
-register_links(['smart_link_condition_list', 'smart_link_condition_create', 'smart_link_condition_edit', 'smart_link_condition_delete'], [smart_link_condition_create], menu_name='sidebar')
+bind_links([SmartLinkCondition], [smart_link_condition_edit, smart_link_condition_delete])
+bind_links(['smart_link_condition_list', 'smart_link_condition_create', 'smart_link_condition_edit', 'smart_link_condition_delete'], [smart_link_condition_create], menu_name='sidebar')
register_setup(smart_link_setup)
register_sidebar_template(['smart_link_list'], 'smart_links_help.html')
diff --git a/apps/linking/forms.py b/apps/linking/forms.py
index 16400ece57..419945a8d7 100644
--- a/apps/linking/forms.py
+++ b/apps/linking/forms.py
@@ -35,9 +35,9 @@ class SmartLinkImageWidget(forms.widgets.Widget):
%(text)s
''' % {
- 'famfam': link.get('famfam', u'link'),
- 'text': capfirst(link['text']),
- 'action': reverse(link.get('view'), args=[value['current_document'].pk, value['smart_link_instance'].pk])
+ 'famfam': getattr(link, 'famfam', u'link'),
+ 'text': capfirst(link.text),
+ 'action': reverse(link.view, args=[value['current_document'].pk, value['smart_link_instance'].pk])
})
output.append(u'')
diff --git a/apps/mailer/__init__.py b/apps/mailer/__init__.py
index 5d6e967d51..9774bc887e 100644
--- a/apps/mailer/__init__.py
+++ b/apps/mailer/__init__.py
@@ -2,16 +2,16 @@ from __future__ import absolute_import
from django.utils.translation import ugettext_lazy as _
-from navigation.api import register_links
+from navigation.api import bind_links, Link
from documents.models import Document
from acls.api import class_permissions
from .permissions import PERMISSION_MAILING_LINK, PERMISSION_MAILING_SEND_DOCUMENT
-send_document_link = {'text': _(u'email link'), 'view': 'send_document_link', 'args': 'object.pk', 'famfam': 'email_link', 'permissions': [PERMISSION_MAILING_LINK]}
-send_document = {'text': _(u'email document'), 'view': 'send_document', 'args': 'object.pk', 'famfam': 'email_open', 'permissions': [PERMISSION_MAILING_SEND_DOCUMENT]}
+send_document_link = Link(text=_(u'email link'), view='send_document_link', args='object.pk', sprite='email_link', permissions=[PERMISSION_MAILING_LINK])
+send_document = Link(text=_(u'email document'), view='send_document', args='object.pk', sprite='email_open', permissions=[PERMISSION_MAILING_SEND_DOCUMENT])
-register_links(Document, [send_document_link, send_document])
+bind_links([Document], [send_document_link, send_document])
class_permissions(Document, [
PERMISSION_MAILING_LINK,
diff --git a/apps/main/__init__.py b/apps/main/__init__.py
index e057b7b219..6044c27fcc 100644
--- a/apps/main/__init__.py
+++ b/apps/main/__init__.py
@@ -3,8 +3,7 @@ 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 navigation.api import bind_links, Link, register_top_menu
from project_setup.api import register_setup
from project_tools.api import register_tool
@@ -30,16 +29,16 @@ __version_info__ = {
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', '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}
+maintenance_menu = Link(text=_(u'maintenance'), view='maintenance_menu', sprite='wrench', icon='wrench.png')
+statistics = Link(text=_(u'statistics'), view='statistics', sprite='table', icon='blackboard_sum.png', condition=is_superuser, children_view_regex=[r'statistics'])
+diagnostics = Link(text=_(u'diagnostics'), view='diagnostics', sprite='pill', icon='pill.png')
+sentry = Link(text=_(u'sentry'), view='sentry', sprite='bug', icon='bug.png', condition=is_superuser)
+admin_site = Link(text=_(u'admin site'), view='admin:index', sprite='keyboard', icon='keyboard.png', condition=is_superuser)
if not DISABLE_HOME_VIEW:
- register_top_menu('home', link={'text': _(u'home'), 'view': 'home', 'famfam': 'house'}, position=0)
+ register_top_menu('home', link=Link(text=_(u'home'), view='home', sprite='house'), position=0)
if not SIDE_BAR_SEARCH:
- register_top_menu('search', link={'text': _(u'search'), 'view': 'search', 'famfam': 'zoom'}, children_path_regex=[r'^search/'])
+ register_top_menu('search', link=Link(text=_(u'search'), view='search', sprite='zoom', children_url_regex=[r'^search/']))
def get_version():
diff --git a/apps/main/api.py b/apps/main/api.py
index f8a48ce78e..f22e0150c3 100644
--- a/apps/main/api.py
+++ b/apps/main/api.py
@@ -7,7 +7,7 @@ tools = {}
def register_diagnostic(namespace, title, link):
namespace_dict = diagnostics.get(namespace, {'title': None, 'links': []})
namespace_dict['title'] = title
- link['url'] = link.get('url', reverse_lazy(link['view']))
+ link.url = getattr(link, 'url', reverse_lazy(link.view))
namespace_dict['links'].append(link)
diagnostics[namespace] = namespace_dict
@@ -16,6 +16,6 @@ def register_maintenance_links(links, title=None, namespace=None):
namespace_dict = tools.get(namespace, {'title': None, 'links': []})
namespace_dict['title'] = title
for link in links:
- link['url'] = link.get('url', reverse_lazy(link['view']))
+ link.url = getattr(link, 'url', reverse_lazy(link.view))
namespace_dict['links'].append(link)
tools[namespace] = namespace_dict
diff --git a/apps/main/templates/base.html b/apps/main/templates/base.html
index 86fa6495d1..aab2d8fb24 100644
--- a/apps/main/templates/base.html
+++ b/apps/main/templates/base.html
@@ -240,47 +240,27 @@
{% endblock %}
{% block web_theme_secondary_navigation %}
- {% if navigation_object_list %}
- {% for navigation_object_dict in navigation_object_list %}
- {% copy_variable navigation_object_dict.object as "navigation_object_name" %}
- {% get_object_navigation_links "form_header" as form_navigation_links %}
- {% if form_navigation_links %}
-
-
- {% with form_navigation_links as object_navigation_links %}
- {% with "true" as as_li %}
- {% with "true" as hide_active_anchor %}
- {% with "active" as li_class_active %}
- {% with "first" as li_class_first %}
- {% include "generic_navigation.html" %}
- {% endwith %}
- {% endwith %}
- {% endwith %}
- {% endwith %}
- {% endwith %}
-
-
- {% endif %}
- {% endfor %}
- {% else %}
- {% get_object_navigation_links "form_header" as form_navigation_links %}
- {% if form_navigation_links %}
+ {% get_object_navigation_links "form_header" as form_header_navigation_links %}
+ {% if form_header_navigation_links %}
+
+ {% for object_reference, object_links in form_header_navigation_links.items %}
- {% with form_navigation_links as object_navigation_links %}
- {% with "true" as as_li %}
- {% with "true" as hide_active_anchor %}
- {% with "active" as li_class_active %}
- {% with "first" as li_class_first %}
- {% include "generic_navigation.html" %}
- {% endwith %}
- {% endwith %}
- {% endwith %}
- {% endwith %}
- {% endwith %}
+ {% with object_links as links %}
+ {% with "true" as as_li %}
+ {% with "true" as hide_active_anchor %}
+ {% with "active" as li_class_active %}
+ {% with "first" as li_class_first %}
+ {% include "generic_navigation.html" %}
+ {% endwith %}
+ {% endwith %}
+ {% endwith %}
+ {% endwith %}
+ {% endwith %}
-
- {% endif %}
+
+ {% endfor %}
+
{% endif %}
{% endblock %}
@@ -298,106 +278,61 @@
{% endwith %}
{% endif %}
- {% get_object_navigation_links "secondary_menu" as object_navigation_links %}
- {% if object_navigation_links %}
+ {% get_object_navigation_links "secondary_menu" as secondary_menu_navigation_links %}
+ {% if secondary_menu_navigation_links %}
{% trans "Secondary menu" %}
- {% with "true" as as_li %}
- {% include "generic_navigation.html" %}
- {% endwith %}
+ {% for object_reference, object_links in secondary_menu_navigation_links.items %}
+ {% with "true" as as_li %}
+ {% with object_links as links %}
+ {% include "generic_navigation.html" %}
+ {% endwith %}
+ {% endwith %}
+ {% endfor %}
{% endif %}
- {% if navigation_object_list %}
- {% for navigation_object_dict in navigation_object_list %}
- {% copy_variable navigation_object_dict.object as "navigation_object_name" %}
- {% get_object_navigation_links as object_navigation_links %}
- {% if object_navigation_links %}
-
- {% if navigation_object %}
- {% if navigation_object_dict.name %}
-
{% blocktrans with navigation_object_dict.name as name %}Actions for {{ name }}: {{ navigation_object }}{% endblocktrans %}
- {% else %}
-
{% blocktrans %}Actions for: {{ navigation_object }}{% endblocktrans %}
- {% endif %}
+
+ {% get_object_navigation_links as secondary_menu_navigation_links %}
+ {% if secondary_menu_navigation_links %}
+
+ {% for object_reference, object_links in secondary_menu_navigation_links.items %}
+ {% if object_reference %}
+ {% if navigation_object_dict.name %}
+
{% blocktrans with navigation_object_dict.name as name %}Actions for {{ name }}: {{ object_reference }}{% endblocktrans %}
{% else %}
-
{% trans "Available actions" %}
- {% endif %}
-
- {% with "true" as as_li %}
- {% include "generic_navigation.html" %}
- {% endwith %}
-
-
- {% endif %}
- {% get_object_navigation_links "related" as object_navigation_links %}
- {% if object_navigation_links %}
-
-
{% trans "Related actions" %}
-
- {% with "true" as as_li %}
- {% include "generic_navigation.html" %}
- {% endwith %}
-
-
- {% endif %}
- {% get_object_navigation_links "sidebar" as object_navigation_links %}
- {% if object_navigation_links %}
-
-
{% trans "Other available actions" %}
-
- {% with "true" as as_li %}
- {% include "generic_navigation.html" %}
- {% endwith %}
-
-
- {% endif %}
- {% endfor %}
- {% else %}
- {% get_object_navigation_links as object_navigation_links %}
- {% if object_navigation_links %}
-
- {% if navigation_object %}
- {% if object_name %}
-
{% blocktrans %}Actions for {{ object_name }}: {{ navigation_object }}{% endblocktrans %}
- {% else %}
-
{% blocktrans %}Actions for: {{ navigation_object }}{% endblocktrans %}
+
{% blocktrans %}Actions for: {{ object_reference }}{% endblocktrans %}
{% endif %}
{% else %}
-
{% trans "Actions" %}
- {% endif %}
+
{% trans "Available actions" %}
+ {% endif %}
- {% with "true" as as_li %}
- {% include "generic_navigation.html" %}
- {% endwith %}
+ {% with "true" as as_li %}
+ {% with object_links as links %}
+ {% include "generic_navigation.html" %}
+ {% endwith %}
+ {% endwith %}
-
- {% endif %}
- {% get_object_navigation_links "related" as object_navigation_links %}
- {% if object_navigation_links %}
-
-
{% trans "Related actions" %}
-
- {% with "true" as as_li %}
- {% include "generic_navigation.html" %}
- {% endwith %}
-
-
- {% endif %}
+ {% endfor %}
+
{% endif %}
- {% get_object_navigation_links "sidebar" as object_navigation_links %}
- {% if object_navigation_links %}
+ {% get_object_navigation_links "sidebar" as sidebar_navigation_links %}
+ {% if sidebar_navigation_links %}
{% trans "Other available actions" %}
- {% with "true" as as_li %}
- {% include "generic_navigation.html" %}
- {% endwith %}
+ {% for object_reference, object_links in sidebar_navigation_links.items %}
+ {% with "true" as as_li %}
+ {% with object_links as links %}
+ {% include "generic_navigation.html" %}
+ {% endwith %}
+ {% endwith %}
+ {% endfor %}
- {% endif %}
+ {% endif %}
{% get_sidebar_templates as sidebar_templates %}
{% for template in sidebar_templates %}
diff --git a/apps/main/views.py b/apps/main/views.py
index 12aabda9e5..75d04971ff 100644
--- a/apps/main/views.py
+++ b/apps/main/views.py
@@ -32,7 +32,7 @@ def maintenance_menu(request):
user_tools[namespace].setdefault('links', [])
for link in values['links']:
try:
- permissions = link.get('permissions', [])
+ permissions = link.permissions
Permission.objects.check_permissions(request.user, permissions)
user_tools[namespace]['links'].append(link)
except PermissionDenied:
diff --git a/apps/metadata/__init__.py b/apps/metadata/__init__.py
index 9f7ea6bb6e..6510383601 100644
--- a/apps/metadata/__init__.py
+++ b/apps/metadata/__init__.py
@@ -2,8 +2,8 @@ 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 navigation.api import (bind_links, register_multi_item_links,
+ register_sidebar_template, Link)
from documents.models import Document, DocumentType
from documents.permissions import PERMISSION_DOCUMENT_TYPE_EDIT
from project_setup.api import register_setup
@@ -18,37 +18,37 @@ from .permissions import (PERMISSION_METADATA_DOCUMENT_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_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]}
+metadata_edit = Link(text=_(u'edit metadata'), view='metadata_edit', args='object.pk', sprite='xhtml_go', permissions=[PERMISSION_METADATA_DOCUMENT_EDIT])
+metadata_view = Link(text=_(u'metadata'), view='metadata_view', args='object.pk', sprite='xhtml_go', permissions=[PERMISSION_METADATA_DOCUMENT_VIEW])#, children_view_regex=['metadata'])
+metadata_multiple_edit = Link(text=_(u'edit metadata'), view='metadata_multiple_edit', sprite='xhtml_go', permissions=[PERMISSION_METADATA_DOCUMENT_EDIT])
+metadata_add = Link(text=_(u'add metadata'), view='metadata_add', args='object.pk', sprite='xhtml_add', permissions=[PERMISSION_METADATA_DOCUMENT_ADD])
+metadata_multiple_add = Link(text=_(u'add metadata'), view='metadata_multiple_add', sprite='xhtml_add', permissions=[PERMISSION_METADATA_DOCUMENT_ADD])
+metadata_remove = Link(text=_(u'remove metadata'), view='metadata_remove', args='object.pk', sprite='xhtml_delete', permissions=[PERMISSION_METADATA_DOCUMENT_REMOVE])
+metadata_multiple_remove = Link(text=_(u'remove metadata'), view='metadata_multiple_remove', sprite='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': [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_type_list = Link(text=_(u'metadata types'), view='setup_metadata_type_list', sprite='xhtml_go', icon='xhtml.png', permissions=[PERMISSION_METADATA_TYPE_VIEW])#, children_view_regex=[r'setup_metadata_type'])
+setup_metadata_type_edit = Link(text=_(u'edit'), view='setup_metadata_type_edit', args='object.pk', sprite='xhtml', permissions=[PERMISSION_METADATA_TYPE_EDIT])
+setup_metadata_type_delete = Link(text=_(u'delete'), view='setup_metadata_type_delete', args='object.pk', sprite='xhtml_delete', permissions=[PERMISSION_METADATA_TYPE_DELETE])
+setup_metadata_type_create = Link(text=_(u'create new'), view='setup_metadata_type_create', sprite='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': [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_metadata_set_list = Link(text=_(u'metadata sets'), view='setup_metadata_set_list', sprite='table', icon='table.png', permissions=[PERMISSION_METADATA_SET_VIEW])#, children_view_regex=[r'setup_metadata_set'])
+setup_metadata_set_edit = Link(text=_(u'edit'), view='setup_metadata_set_edit', args='object.pk', sprite='table_edit', permissions=[PERMISSION_METADATA_SET_EDIT])
+setup_metadata_set_delete = Link(text=_(u'delete'), view='setup_metadata_set_delete', args='object.pk', sprite='table_delete', permissions=[PERMISSION_METADATA_SET_DELETE])
+setup_metadata_set_create = Link(text=_(u'create new'), view='setup_metadata_set_create', sprite='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]}
+setup_document_type_metadata = Link(text=_(u'default metadata'), view='setup_document_type_metadata', args='document_type.pk', sprite='xhtml', permissions=[PERMISSION_DOCUMENT_TYPE_EDIT])
-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')
+bind_links(['metadata_add', 'metadata_edit', 'metadata_remove', 'metadata_view'], [metadata_add, metadata_edit, metadata_remove], menu_name='sidebar')
+bind_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([MetadataType, 'setup_metadata_type_list', 'setup_metadata_type_create'], [setup_metadata_type_list, setup_metadata_type_create], menu_name='secondary_menu')
+bind_links([MetadataType], [setup_metadata_type_edit, setup_metadata_type_delete])
+bind_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([MetadataSet, 'setup_metadata_set_list', 'setup_metadata_set_create'], [setup_metadata_set_list, setup_metadata_set_create], menu_name='secondary_menu')
+bind_links([MetadataSet], [setup_metadata_set_edit, setup_metadata_set_delete])
+bind_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])
+bind_links([DocumentType], [setup_document_type_metadata])
metadata_type_setup_views = ['setup_metadata_type_list', 'setup_metadata_type_edit', 'setup_metadata_type_delete', 'setup_metadata_type_create']
metadata_set_setup_views = ['setup_metadata_set_list', 'setup_metadata_set_edit', 'setup_metadata_set_delete', 'setup_metadata_set_create']
diff --git a/apps/navigation/__init__.py b/apps/navigation/__init__.py
index e69de29bb2..5b5faffadc 100644
--- a/apps/navigation/__init__.py
+++ b/apps/navigation/__init__.py
@@ -0,0 +1,5 @@
+from __future__ import absolute_import
+
+from elementtree.ElementTree import Element
+
+main_menu = Element('root')
diff --git a/apps/navigation/api.py b/apps/navigation/api.py
index 56bb037245..efdc441cfa 100644
--- a/apps/navigation/api.py
+++ b/apps/navigation/api.py
@@ -1,80 +1,180 @@
-object_navigation = {}
+from __future__ import absolute_import
+
+import urlparse
+import urllib
+import logging
+import re
+
+from django.template import (VariableDoesNotExist, Variable)
+from django.utils.encoding import smart_str, smart_unicode
+from django.core.urlresolvers import reverse, NoReverseMatch
+from django.utils.http import urlquote, urlencode
+from django.utils.translation import ugettext_lazy as _
+
+from elementtree.ElementTree import SubElement
+
+from .utils import (resolve_to_name, resolve_arguments,
+ get_navigation_objects)
+from . import main_menu
+
multi_object_navigation = {}
model_list_columns = {}
sidebar_templates = {}
-top_menu_entries = []
+
+link_binding = {}
+
+logger = logging.getLogger(__name__)
-def register_multi_item_links(src, links, menu_name=None):
- """
- Register a multiple item action action to be displayed in the
- generic list template
- """
-
- multi_object_navigation.setdefault(menu_name, {})
- if hasattr(src, '__iter__'):
- for one_src in src:
- multi_object_navigation[menu_name].setdefault(one_src, {'links': []})
- multi_object_navigation[menu_name][one_src]['links'].extend(links)
- else:
- multi_object_navigation[menu_name].setdefault(src, {'links': []})
- multi_object_navigation[menu_name][src]['links'].extend(links)
+class ResolvedLink(object):
+ active = False
+ url = '#'
+ text = _('Unnamed link')
-def register_links(src, links, menu_name=None, position=None):
- """
- Associate a link to a model a view, or an url
- """
+class Link(object):
+ def __init__(self, text, view, klass=None, args=None, sprite=None,
+ icon=None, permissions=None, condition=None, conditional_disable=None,
+ description=None, dont_mark_active=False, children_view_regex=None,
+ keep_query=False, children_classes=None, children_url_regex=None,
+ children_views=None, conditional_highlight=None):
- object_navigation.setdefault(menu_name, {})
- if hasattr(src, '__iter__'):
- for one_src in src:
- object_navigation[menu_name].setdefault(one_src, {'links': []})
- if position is not None:
- for link in reversed(links):
- object_navigation[menu_name][one_src]['links'].insert(position, link)
- else:
- object_navigation[menu_name][one_src]['links'].extend(links)
- else:
- object_navigation[menu_name].setdefault(src, {'links': []})
- if position is not None:
- for link in reversed(links):
- object_navigation[menu_name][src]['links'].insert(position, link)
+ self.text = text
+ self.view = view
+ self.args = args or {}
+ #self.kwargs = kwargs or {}
+ self.sprite = sprite
+ self.icon = icon
+ self.permissions = permissions or []
+ self.condition = condition
+ self.conditional_disable = conditional_disable
+ self.description = description
+ self.dont_mark_active = dont_mark_active
+ self.klass = klass
+ self.keep_query = keep_query
+ self.conditional_highlight = conditional_highlight # Used by dynamic sources
+ self.children_views = children_views or []
+ self.children_classes = children_classes or []
+ self.children_url_regex = children_url_regex or []
+ self.children_view_regex = children_view_regex or []
+
+ def resolve(self, context):
+ # TODO: don't calculate these if passed in an argument
+ request = Variable('request').resolve(context)
+ current_path = request.META['PATH_INFO']
+ current_view = resolve_to_name(current_path)
+
+ # Preserve unicode data in URL query
+ previous_path = smart_unicode(urllib.unquote_plus(smart_str(request.get_full_path()) or smart_str(request.META.get('HTTP_REFERER', u'/'))))
+ query_string = urlparse.urlparse(previous_path).query
+ parsed_query_string = urlparse.parse_qs(query_string)
+
+ logger.debug('condition: %s', self.condition)
+
+ # Check to see if link has conditional display
+ if self.condition:
+ condition_result = self.condition(context)
else:
- object_navigation[menu_name][src]['links'].extend(links)
+ condition_result = True
+
+ logger.debug('condition_result: %s', condition_result)
+
+ if condition_result:
+ resolved_link = ResolvedLink()
+ resolved_link.text = self.text
+ resolved_link.sprite = self.sprite
+ resolved_link.icon = self.icon
+ resolved_link.permissions = self.permissions
+
+ try:
+ #args, kwargs = resolve_arguments(context, self.get('args', {}))
+ args, kwargs = resolve_arguments(context, self.args)
+ except VariableDoesNotExist:
+ args = []
+ kwargs = {}
+
+ if self.view:
+ if not self.dont_mark_active:
+ resolved_link.active = self.view == current_view
+
+ try:
+ if kwargs:
+ resolved_link.url = reverse(self.view, kwargs=kwargs)
+ else:
+ resolved_link.url = reverse(self.view, args=args)
+ if self.keep_query:
+ resolved_link.url = u'%s?%s' % (urlquote(resolved_link.url), urlencode(parsed_query_string, doseq=True))
+
+ except NoReverseMatch, exc:
+ resolved_link.url = '#'
+ resolved_link.error = exc
+ elif self.url:
+ if not self.dont_mark_active:
+ resolved_link.url.active = self.url == current_path
+
+ if kwargs:
+ resolved_link.url = self.url % kwargs
+ else:
+ resolved_link.url = self.url % args
+ if self.keep_query:
+ resolved_link.url = u'%s?%s' % (urlquote(resolved_link.url), urlencode(parsed_query_string, doseq=True))
+ else:
+ resolved_link.active = False
+
+ if self.conditional_highlight:
+ resolved_link.active = self.conditional_highlight(context)
+
+ if self.conditional_disable:
+ resolved_link.disabled = self.conditional_disable(context)
+ else:
+ resolved_link.disabled = False
+
+ if current_view in self.children_views:
+ resolved_link.active = True
+
+ # TODO: eliminate url_regexes and use new tree base main menu
+ for child_url_regex in self.children_url_regex:
+ if re.compile(child_url_regex).match(current_path.lstrip('/')):
+ resolved_link.active = True
+
+ for children_view_regex in self.children_view_regex:
+ if re.compile(children_view_regex).match(current_view):
+ resolved_link.active = True
+
+ for cls in self.children_classes:
+ object_list = get_navigation_objects(context)
+ if object_list:
+ if type(object_list[0]['object']) == cls or object_list[0]['object'] == cls:
+ #new_link['active'] = True
+ resolved_link.active = True
+
+ return resolved_link
-def register_top_menu(name, link, children_views=None,
- children_path_regex=None, children_view_regex=None,
- position=None):
+def bind_links(sources, links, menu_name=None, position=0):
+ """
+ Associate a link to a model, a view, or an url
+ """
+ link_binding.setdefault(menu_name, {})
+ for source in sources:
+ link_binding[menu_name].setdefault(source, {'links': []})
+ link_binding[menu_name][source]['links'].extend(links)
+
+
+def register_top_menu(name, link, position=None):
"""
Register a new menu entry for the main menu displayed at the top
of the page
"""
+ new_menu = SubElement(main_menu, name, link=link, position=position)
- entry = {'link': link, 'name': name}
- if children_views:
- entry['children_views'] = children_views
- if children_path_regex:
- entry['children_path_regex'] = children_path_regex
- if children_view_regex:
- entry['children_view_regex'] = children_view_regex
- if position is not None:
- entry['position'] = position
- top_menu_entries.insert(position, entry)
- else:
- length = len(top_menu_entries)
- entry['position'] = length
- top_menu_entries.append(entry)
+ sorted_menus = sorted(main_menu.getchildren(), key=lambda k: (k.get('position') < 0, k.get('position')))
+ main_menu.clear()
- sort_menu_entries()
-
- return entry
+ for menu in sorted_menus:
+ main_menu.append(menu)
-
-def sort_menu_entries():
- global top_menu_entries
- top_menu_entries = sorted(top_menu_entries, key=lambda k: (k['position'] < 0, k['position']))
+ return new_menu
def register_model_list_columns(model, columns):
@@ -91,3 +191,75 @@ def register_sidebar_template(source_list, template_name):
for source in source_list:
sidebar_templates.setdefault(source, [])
sidebar_templates[source].append(template_name)
+
+
+def register_multi_item_links(sources, links, menu_name=None):
+ """
+ Register a multiple item action action to be displayed in the
+ generic list template
+ """
+ multi_object_navigation.setdefault(menu_name, {})
+ for source in sources:
+ multi_object_navigation[menu_name].setdefault(source, {'links': []})
+ multi_object_navigation[menu_name][source]['links'].extend(links)
+
+
+def get_context_navigation_links(context, menu_name=None, links_dict=link_binding):
+ request = Variable('request').resolve(context)
+ current_path = request.META['PATH_INFO']
+ current_view = resolve_to_name(current_path)
+ context_links = {}
+
+ # Don't fudge with the original global dictionary
+ # TODO: fix this
+ links_dict = links_dict.copy()
+
+ # TODO: doesn't appear to be used
+ '''
+ try:
+ """
+ Override the navigation links dictionary with the provided
+ link list
+ """
+ navigation_object_links = Variable('overrided_object_links').resolve(context)
+ if navigation_object_links:
+ return [link.resolve(context) for link in navigation_object_links]
+ except VariableDoesNotExist:
+ pass
+ '''
+ # TODO: who uses this? Remove if no one.
+ # Dynamic sources
+ # TODO: improve name to 'injected...'
+ try:
+ """
+ Check for and inject a temporary navigation dictionary
+ """
+ temp_navigation_links = Variable('temporary_navigation_links').resolve(context)
+ if temp_navigation_links:
+ links_dict.update(temp_navigation_links)
+ except VariableDoesNotExist:
+ pass
+
+ try:
+ view_links = links_dict[menu_name][current_view]['links']
+ if view_links:
+ context_links.setdefault(None, [])
+
+ for link in view_links:
+ context_links[None].append(link.resolve(context))
+ except KeyError:
+ pass
+
+ for resolved_object, object_properties in get_navigation_objects(context).items():
+ try:
+ resolved_object_reference = resolved_object
+ object_links = links_dict[menu_name][type(resolved_object_reference)]['links']
+ if object_links:
+ context_links.setdefault(resolved_object_reference, [])
+
+ for link in object_links:
+ context_links[resolved_object_reference].append(link.resolve(context))
+ except KeyError:
+ pass
+
+ return context_links
diff --git a/apps/navigation/templates/generic_link_instance.html b/apps/navigation/templates/generic_link_instance.html
index 098810ee13..a1f18baf8b 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.sprite 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 %}
+ {% if link.sprite 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_navigation.html b/apps/navigation/templates/generic_navigation.html
index bff532fbee..722d3e6857 100644
--- a/apps/navigation/templates/generic_navigation.html
+++ b/apps/navigation/templates/generic_navigation.html
@@ -1,5 +1,5 @@
- {% for link in object_navigation_links %}
+ {% for link in links %}
{% include "generic_subnavigation.html" %}
{% endfor %}
diff --git a/apps/navigation/templatetags/navigation_tags.py b/apps/navigation/templatetags/navigation_tags.py
index d4533c09a3..aefcef42af 100644
--- a/apps/navigation/templatetags/navigation_tags.py
+++ b/apps/navigation/templatetags/navigation_tags.py
@@ -1,49 +1,27 @@
-from __future__ import absolute_import
+from __future__ import absolute_import
import copy
import re
-import urlparse
-import urllib
+import logging
-from django.core.urlresolvers import reverse, NoReverseMatch
+from django.core.urlresolvers import reverse
from django.template import (TemplateSyntaxError, Library,
- VariableDoesNotExist, Node, Variable)
-from django.utils.text import unescape_string_literal
+ Node, Variable, VariableDoesNotExist)
from django.utils.translation import ugettext as _
-from django.utils.encoding import smart_str, force_unicode, smart_unicode
-from common.utils import urlquote
-
-from ..api import (object_navigation, multi_object_navigation,
- top_menu_entries, sidebar_templates)
+from ..api import (link_binding, multi_object_navigation,
+ sidebar_templates, get_context_navigation_links)
from ..forms import MultiItemForm
-from ..utils import resolve_to_name
+from ..utils import resolve_to_name, resolve_template_variable
+from .. import main_menu
register = Library()
+logger = logging.getLogger(__name__)
class TopMenuNavigationNode(Node):
def render(self, context):
- request = Variable('request').resolve(context)
- current_path = request.META['PATH_INFO']
- current_view = resolve_to_name(current_path)
-
- all_menu_links = [entry.get('link', {}) for entry in top_menu_entries]
- menu_links = resolve_links(context, all_menu_links, current_view, current_path)
-
- for index, link in enumerate(top_menu_entries):
- if current_view in link.get('children_views', []):
- menu_links[index]['active'] = True
-
- for child_path_regex in link.get('children_path_regex', []):
- if re.compile(child_path_regex).match(current_path.lstrip('/')):
- menu_links[index]['active'] = True
-
- for children_view_regex in link.get('children_view_regex', []):
- if re.compile(children_view_regex).match(current_view):
- menu_links[index]['active'] = True
-
- context['menu_links'] = menu_links
+ context['menu_links'] = [menu.get('link').resolve(context) for menu in main_menu.getchildren()]
return ''
@@ -52,198 +30,22 @@ def get_top_menu_links(parser, token):
return TopMenuNavigationNode()
-def resolve_arguments(context, src_args):
- args = []
- kwargs = {}
- if type(src_args) == type([]):
- for i in src_args:
- val = resolve_template_variable(context, i)
- if val:
- args.append(val)
- elif type(src_args) == type({}):
- for key, value in src_args.items():
- val = resolve_template_variable(context, value)
- if val:
- kwargs[key] = val
- else:
- val = resolve_template_variable(context, src_args)
- if val:
- args.append(val)
-
- return args, kwargs
-
-
-def resolve_links(context, links, current_view, current_path, parsed_query_string=None):
- """
- Express a list of links from definition to final values
- """
- context_links = []
- for link in links:
- # Check to see if link has conditional display
- if 'condition' in link:
- condition_result = link['condition'](context)
- else:
- condition_result = True
-
- if condition_result:
- new_link = copy.copy(link)
- try:
- args, kwargs = resolve_arguments(context, link.get('args', {}))
- except VariableDoesNotExist:
- args = []
- kwargs = {}
-
- if 'view' in link:
- if not link.get('dont_mark_active', False):
- new_link['active'] = link['view'] == current_view
-
- try:
- if kwargs:
- new_link['url'] = reverse(link['view'], kwargs=kwargs)
- else:
- new_link['url'] = reverse(link['view'], args=args)
- if link.get('keep_query', False):
- print 'parsed_query_string', parsed_query_string
- new_link['url'] = urlquote(new_link['url'], parsed_query_string)
- except NoReverseMatch, err:
- new_link['url'] = '#'
- new_link['error'] = err
- elif 'url' in link:
- if not link.get('dont_mark_active', False):
- new_link['active'] = link['url'] == current_path
-
- if kwargs:
- new_link['url'] = link['url'] % kwargs
- else:
- new_link['url'] = link['url'] % args
- if link.get('keep_query', False):
- new_link['url'] = urlquote(new_link['url'], parsed_query_string)
- else:
- new_link['active'] = False
-
- if 'conditional_highlight' in link:
- new_link['active'] = link['conditional_highlight'](context)
-
- if 'conditional_disable' in link:
- new_link['disabled'] = link['conditional_disable'](context)
- else:
- new_link['disabled'] = False
-
- if current_view in link.get('children_views', []):
- new_link['active'] = True
-
- for child_url_regex in link.get('children_url_regex', []):
- if re.compile(child_url_regex).match(current_path.lstrip('/')):
- new_link['active'] = True
-
- for children_view_regex in link.get('children_view_regex', []):
- if re.compile(children_view_regex).match(current_view):
- new_link['active'] = True
-
- for cls in link.get('children_classes', []):
- obj, object_name = get_navigation_object(context)
- if type(obj) == cls or obj == cls:
- new_link['active'] = True
-
- context_links.append(new_link)
- return context_links
-
-
-def get_navigation_object(context):
- try:
- object_name = Variable('navigation_object_name').resolve(context)
- except VariableDoesNotExist:
- object_name = 'object'
-
- try:
- obj = Variable(object_name).resolve(context)
- except VariableDoesNotExist:
- obj = None
-
- return obj, object_name
-
-
-def _get_object_navigation_links(context, menu_name=None, links_dict=object_navigation):
- request = Variable('request').resolve(context)
- current_path = request.META['PATH_INFO']
- current_view = resolve_to_name(current_path)
- context_links = []
-
- # Don't fudge with the original global dictionary
- links_dict = links_dict.copy()
-
- # Preserve unicode data in URL query
- previous_path = smart_unicode(urllib.unquote_plus(smart_str(request.get_full_path()) or smart_str(request.META.get('HTTP_REFERER', u'/'))))
- query_string = urlparse.urlparse(previous_path).query
- parsed_query_string = urlparse.parse_qs(query_string)
-
- try:
- """
- Override the navigation links dictionary with the provided
- link list
- """
- navigation_object_links = Variable('overrided_object_links').resolve(context)
- if navigation_object_links:
- return [link for link in resolve_links(context, navigation_object_links, current_view, current_path, parsed_query_string)]
- except VariableDoesNotExist:
- pass
-
- try:
- """
- Check for and inject a temporary navigation dictionary
- """
- temp_navigation_links = Variable('temporary_navigation_links').resolve(context)
- if temp_navigation_links:
- links_dict.update(temp_navigation_links)
- except VariableDoesNotExist:
- pass
-
- try:
- links = links_dict[menu_name][current_view]['links']
- for link in resolve_links(context, links, current_view, current_path, parsed_query_string):
- context_links.append(link)
- except KeyError:
- pass
-
- obj, object_name = get_navigation_object(context)
-
- try:
- links = links_dict[menu_name][type(obj)]['links']
- for link in resolve_links(context, links, current_view, current_path, parsed_query_string):
- context_links.append(link)
- except KeyError:
- pass
-
- return context_links
-
-
-def resolve_template_variable(context, name):
- try:
- return unescape_string_literal(name)
- except ValueError:
- #return Variable(name).resolve(context)
- #TODO: Research if should return always as a str
- return str(Variable(name).resolve(context))
- except TypeError:
- return name
-
-
class GetNavigationLinks(Node):
- def __init__(self, menu_name=None, links_dict=object_navigation, var_name='object_navigation_links'):
+ def __init__(self, menu_name=None, links_dict=link_binding, var_name='object_navigation_links'):
self.menu_name = menu_name
self.links_dict = links_dict
self.var_name = var_name
+ logger.debug('menu_name: %s' % menu_name)
def render(self, context):
menu_name = resolve_template_variable(context, self.menu_name)
- context[self.var_name] = _get_object_navigation_links(context, menu_name, links_dict=self.links_dict)
- obj, object_name = get_navigation_object(context)
- context['navigation_object'] = obj
+ context[self.var_name] = get_context_navigation_links(context, menu_name, links_dict=self.links_dict)
return ''
-@register.tag
-def get_object_navigation_links(parser, token):
+@register.tag(name='get_object_navigation_links')
+def get_context_navigation_links_tag(parser, token):
+ logger.debug('getting links')
tag_name, arg = token.contents.split(None, 1)
m = re.search(r'("?\w+"?)?.?as (\w+)', arg)
@@ -256,14 +58,28 @@ def get_object_navigation_links(parser, token):
@register.inclusion_tag('generic_navigation.html', takes_context=True)
def object_navigation_template(context):
+ # Used by list subtemplate
new_context = copy.copy(context)
- new_context.update({
- 'horizontal': True,
- 'object_navigation_links': _get_object_navigation_links(context)
- })
+ try:
+ object_variable_name = Variable('navigation_object_name').resolve(context)
+ except VariableDoesNotExist:
+ object_variable_name = 'object'
+ finally:
+ logger.debug('object_variable_name: %s' % object_variable_name)
+
+ try:
+ object_reference = Variable(object_variable_name).resolve(context)
+ except VariableDoesNotExist:
+ pass
+ else:
+ new_context.update({
+ 'horizontal': True,
+ 'links': get_context_navigation_links(context).get(object_reference)
+ })
+
return new_context
-
-
+
+
@register.tag
def get_multi_item_links(parser, token):
tag_name, arg = token.contents.split(None, 1)
@@ -277,9 +93,14 @@ def get_multi_item_links(parser, token):
@register.inclusion_tag('generic_form_instance.html', takes_context=True)
def get_multi_item_links_form(context):
+ logger.debug('starting')
+ links = []
+ for object_reference, object_links in get_context_object_navigation_links(context, links_dict=multi_object_navigation).items():
+ links.extend(object_links)
+
new_context = copy.copy(context)
new_context.update({
- 'form': MultiItemForm(actions=[(link['url'], link['text']) for link in _get_object_navigation_links(context, links_dict=multi_object_navigation)]),
+ 'form': MultiItemForm(actions=[(link.url, link.text) for link in links]),
'title': _(u'Selected item actions:'),
'form_action': reverse('multi_object_action_view'),
'submit_method': 'get',
diff --git a/apps/navigation/utils.py b/apps/navigation/utils.py
index 6c008e5576..89cd9ec9a2 100644
--- a/apps/navigation/utils.py
+++ b/apps/navigation/utils.py
@@ -1,11 +1,122 @@
-#http://www.djangosnippets.org/snippets/1378/
+from __future__ import absolute_import
+
+import logging
from django.core.urlresolvers import RegexURLResolver, RegexURLPattern, Resolver404, get_resolver
+from django.template import (VariableDoesNotExist, Variable)
+from django.utils.text import unescape_string_literal
-__all__ = ('resolve_to_name',)
+logger = logging.getLogger(__name__)
+def get_navigation_objects(context):
+ objects = {}
+
+ try:
+ indirect_reference_list = Variable('navigation_object_list').resolve(context)
+ except VariableDoesNotExist:
+ pass
+ else:
+ logger.debug('found: navigation_object_list')
+ for indirect_reference in indirect_reference_list:
+ try:
+ resolved_object = Variable(indirect_reference['object']).resolve(context)
+ except VariableDoesNotExist:
+ resolved_object = None
+ else:
+ objects.setdefault(resolved_object, {})
+ objects[resolved_object]['label'] = indirect_reference.get('object_name')
+
+ try:
+ indirect_reference = Variable('navigation_object_name').resolve(context)
+ except VariableDoesNotExist:
+ pass
+ else:
+ logger.debug('found: navigation_object_name')
+ try:
+ object_label = Variable('object_name').resolve(context)
+ except VariableDoesNotExist:
+ object_label = None
+ finally:
+ try:
+ resolved_object = Variable(indirect_reference).resolve(context)
+ except VariableDoesNotExist:
+ resolved_object = None
+
+ objects.setdefault(resolved_object, {})
+ objects[resolved_object]['label'] = object_label
+
+ try:
+ indirect_reference = Variable('list_object_variable_name').resolve(context)
+ except VariableDoesNotExist:
+ pass
+ else:
+ logger.debug('found renamed list object')
+ try:
+ object_label = Variable('object_name').resolve(context)
+ except VariableDoesNotExist:
+ object_label = None
+ finally:
+ try:
+ resolved_object = Variable(indirect_reference).resolve(context)
+ except VariableDoesNotExist:
+ resolved_object = None
+ else:
+ objects.setdefault(resolved_object, {})
+ objects[resolved_object]['label'] = object_label
+
+ try:
+ resolved_object = Variable('object').resolve(context)
+ except VariableDoesNotExist:
+ pass
+ else:
+ logger.debug('found single object')
+ try:
+ object_label = Variable('object_name').resolve(context)
+ except VariableDoesNotExist:
+ object_label = None
+ finally:
+ objects.setdefault(resolved_object, {})
+ objects[resolved_object]['label'] = object_label
+
+ logger.debug('objects: %s' % objects)
+ return objects
+
+
+def resolve_template_variable(context, name):
+ try:
+ return unescape_string_literal(name)
+ except ValueError:
+ #return Variable(name).resolve(context)
+ #TODO: Research if should return always as a str
+ return str(Variable(name).resolve(context))
+ except TypeError:
+ return name
+
+
+def resolve_arguments(context, src_args):
+ args = []
+ kwargs = {}
+ if type(src_args) == type([]):
+ for i in src_args:
+ val = resolve_template_variable(context, i)
+ if val:
+ args.append(val)
+ elif type(src_args) == type({}):
+ for key, value in src_args.items():
+ val = resolve_template_variable(context, value)
+ if val:
+ kwargs[key] = val
+ else:
+ val = resolve_template_variable(context, src_args)
+ if val:
+ args.append(val)
+
+ return args, kwargs
+
+
+#http://www.djangosnippets.org/snippets/1378/
def _pattern_resolve_to_name(self, path):
match = self.regex.search(path)
if match:
diff --git a/apps/navigation/widgets.py b/apps/navigation/widgets.py
index f587fc6ebb..361daf77e7 100644
--- a/apps/navigation/widgets.py
+++ b/apps/navigation/widgets.py
@@ -1,25 +1,19 @@
-from __future__ import absolute_import
-
-import urlparse
+from __future__ import absolute_import
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 django.template import RequestContext
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:
+ if link.permissions:
try:
- Permission.objects.check_permissions(request.user, link['permissions'])
+ Permission.objects.check_permissions(request.user, link.permissions)
return render_widget(request, link)
except PermissionDenied:
return u''
@@ -29,22 +23,13 @@ def button_navigation_widget(request, link):
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]
+ resolved_link = link.resolve(context)
+ if resolved_link:
return mark_safe(u'%(string)s
' % {
- 'url': reverse(link['view']) if 'view' in link else link['url'],
- 'icon': link.get('icon', 'link_button.png'),
+ 'url': resolved_link.url,
+ 'icon': getattr(resolved_link, 'icon', 'link_button.png'),
'static_url': settings.STATIC_URL,
- 'string': capfirst(link['text']),
+ 'string': capfirst(resolved_link.text),
'image_alt': _(u'icon'),
})
else:
diff --git a/apps/ocr/__init__.py b/apps/ocr/__init__.py
index 3a3b0ac7c9..a1141f19c7 100644
--- a/apps/ocr/__init__.py
+++ b/apps/ocr/__init__.py
@@ -9,7 +9,7 @@ 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_multi_item_links
+from navigation.api import bind_links, register_multi_item_links, Link
from documents.models import Document, DocumentVersion
from main.api import register_maintenance_links
from project_tools.api import register_tool
@@ -29,36 +29,36 @@ from . import models as ocr_models
logger = logging.getLogger(__name__)
#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]}
-re_queue_document = {'text': _('re-queue'), 'view': 're_queue_document', 'args': 'object.id', 'famfam': 'hourglass_add', 'permissions': [PERMISSION_OCR_DOCUMENT]}
-re_queue_multiple_document = {'text': _('re-queue'), 'view': 're_queue_multiple_document', 'famfam': 'hourglass_add', 'permissions': [PERMISSION_OCR_DOCUMENT]}
-queue_document_delete = {'text': _(u'delete'), 'view': 'queue_document_delete', 'args': 'object.id', 'famfam': 'hourglass_delete', 'permissions': [PERMISSION_OCR_DOCUMENT_DELETE]}
-queue_document_multiple_delete = {'text': _(u'delete'), 'view': 'queue_document_multiple_delete', 'famfam': 'hourglass_delete', 'permissions': [PERMISSION_OCR_DOCUMENT_DELETE]}
+submit_document = Link(text=_('submit to OCR queue'),view='submit_document',args='object.id',sprite='hourglass_add',permissions=[PERMISSION_OCR_DOCUMENT])
+submit_document_multiple = Link(text=_('submit to OCR queue'),view='submit_document_multiple',sprite='hourglass_add',permissions=[PERMISSION_OCR_DOCUMENT])
+re_queue_document = Link(text=_('re-queue'),view='re_queue_document',args='object.id',sprite='hourglass_add',permissions=[PERMISSION_OCR_DOCUMENT])
+re_queue_multiple_document = Link(text=_('re-queue'),view='re_queue_multiple_document',sprite='hourglass_add',permissions=[PERMISSION_OCR_DOCUMENT])
+queue_document_delete = Link(text=_(u'delete'),view='queue_document_delete',args='object.id',sprite='hourglass_delete',permissions=[PERMISSION_OCR_DOCUMENT_DELETE])
+queue_document_multiple_delete = Link(text=_(u'delete'),view='queue_document_multiple_delete',sprite='hourglass_delete',permissions=[PERMISSION_OCR_DOCUMENT_DELETE])
-document_queue_disable = {'text': _(u'stop queue'), 'view': 'document_queue_disable', 'args': 'queue.id', 'famfam': 'control_stop_blue', 'permissions': [PERMISSION_OCR_QUEUE_ENABLE_DISABLE]}
-document_queue_enable = {'text': _(u'activate queue'), 'view': 'document_queue_enable', 'args': 'queue.id', 'famfam': 'control_play_blue', 'permissions': [PERMISSION_OCR_QUEUE_ENABLE_DISABLE]}
+document_queue_disable = Link(text=_(u'stop queue'),view='document_queue_disable',args='queue.id',sprite='control_stop_blue',permissions=[PERMISSION_OCR_QUEUE_ENABLE_DISABLE])
+document_queue_enable = Link(text=_(u'activate queue'),view='document_queue_enable',args='queue.id',sprite='control_play_blue',permissions=[PERMISSION_OCR_QUEUE_ENABLE_DISABLE])
-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.')}
+all_document_ocr_cleanup = Link(text=_(u'clean up pages content'),view='all_document_ocr_cleanup',sprite='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], 'children_view_regex': [r'queue_', r'document_queue']}
+queue_document_list = Link(text=_(u'queue document list'),view='queue_document_list',sprite='hourglass',permissions=[PERMISSION_OCR_DOCUMENT])
+ocr_tool_link = Link(text=_(u'OCR'),view='queue_document_list',sprite='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'}
-setup_queue_transformation_edit = {'text': _(u'edit'), 'view': 'setup_queue_transformation_edit', 'args': 'transformation.pk', 'famfam': 'shape_square_edit'}
-setup_queue_transformation_delete = {'text': _(u'delete'), 'view': 'setup_queue_transformation_delete', 'args': 'transformation.pk', 'famfam': 'shape_square_delete'}
+setup_queue_transformation_list = Link(text=_(u'transformations'),view='setup_queue_transformation_list',args='queue.pk',sprite='shape_move_front')
+setup_queue_transformation_create = Link(text=_(u'add transformation'),view='setup_queue_transformation_create',args='queue.pk',sprite='shape_square_add')
+setup_queue_transformation_edit = Link(text=_(u'edit'),view='setup_queue_transformation_edit',args='transformation.pk',sprite='shape_square_edit')
+setup_queue_transformation_delete = Link(text=_(u'delete'),view='setup_queue_transformation_delete',args='transformation.pk',sprite='shape_square_delete')
-register_links(Document, [submit_document])
+bind_links([Document], [submit_document])
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'], [submit_document_multiple])
-register_links(DocumentQueue, [document_queue_disable, document_queue_enable, setup_queue_transformation_list])
-register_links(QueueTransformation, [setup_queue_transformation_edit, setup_queue_transformation_delete])
+bind_links([DocumentQueue], [document_queue_disable, document_queue_enable, setup_queue_transformation_list])
+bind_links([QueueTransformation], [setup_queue_transformation_edit, setup_queue_transformation_delete])
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', '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')
+bind_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')
+bind_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'))
diff --git a/apps/permissions/__init__.py b/apps/permissions/__init__.py
index 73731a97db..d70a1e27ec 100644
--- a/apps/permissions/__init__.py
+++ b/apps/permissions/__init__.py
@@ -5,7 +5,7 @@ from django.db.models.signals import post_save
from django.core.exceptions import ObjectDoesNotExist
from django.utils.translation import ugettext_lazy as _
-from navigation.api import register_links, register_multi_item_links
+from navigation.api import bind_links, register_multi_item_links, Link
from project_setup.api import register_setup
from .conf.settings import DEFAULT_ROLES
@@ -14,20 +14,21 @@ from .permissions import (PERMISSION_ROLE_VIEW, PERMISSION_ROLE_EDIT,
PERMISSION_ROLE_CREATE, PERMISSION_ROLE_DELETE,
PERMISSION_PERMISSION_GRANT, PERMISSION_PERMISSION_REVOKE)
-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]}
-role_permissions = {'text': _(u'role permissions'), 'view': 'role_permissions', 'args': 'object.id', 'famfam': 'key_go', 'permissions': [PERMISSION_PERMISSION_GRANT, PERMISSION_PERMISSION_REVOKE]}
-role_delete = {'text': _(u'delete'), 'view': 'role_delete', 'args': 'object.id', 'famfam': 'medal_gold_delete', 'permissions': [PERMISSION_ROLE_DELETE]}
+role_list = Link(text=_(u'roles'), view='role_list', sprite='medal_gold_1', icon='medal_gold_1.png', permissions=[PERMISSION_ROLE_VIEW])#, 'children_view_regex': [r'^permission_', r'^role_'])
+role_create = Link(text=_(u'create new role'), view='role_create', sprite='medal_gold_add', permissions=[PERMISSION_ROLE_CREATE])
+role_edit = Link(text=_(u'edit'), view='role_edit', args='object.id', sprite='medal_gold_1', permissions=[PERMISSION_ROLE_EDIT])
+role_members = Link(text=_(u'members'), view='role_members', args='object.id', sprite='group_key', permissions=[PERMISSION_ROLE_EDIT])
+role_permissions = Link(text=_(u'role permissions'), view='role_permissions', args='object.id', sprite='key_go', permissions=[PERMISSION_PERMISSION_GRANT, PERMISSION_PERMISSION_REVOKE])
+role_delete = Link(text=_(u'delete'), view='role_delete', args='object.id', sprite='medal_gold_delete', permissions=[PERMISSION_ROLE_DELETE])
-permission_grant = {'text': _(u'grant'), 'view': 'permission_multiple_grant', 'famfam': 'key_add', 'permissions': [PERMISSION_PERMISSION_GRANT]}
-permission_revoke = {'text': _(u'revoke'), 'view': 'permission_multiple_revoke', 'famfam': 'key_delete', 'permissions': [PERMISSION_PERMISSION_REVOKE]}
+permission_grant = Link(text=_(u'grant'), view='permission_multiple_grant', sprite='key_add', permissions=[PERMISSION_PERMISSION_GRANT])
+permission_revoke = Link(text=_(u'revoke'), view='permission_multiple_revoke', sprite='key_delete', permissions=[PERMISSION_PERMISSION_REVOKE])
-register_links(Role, [role_edit, role_delete, role_permissions, role_members])
-register_links([Role, 'role_list', 'role_create'], [role_list, role_create], menu_name='secondary_menu')
+bind_links([Role], [role_edit, role_delete, role_permissions, role_members])
+bind_links([Role, 'role_list', 'role_create'], [role_list, role_create], menu_name='secondary_menu')
register_multi_item_links(['role_permissions'], [permission_grant, permission_revoke])
+# TODO: eliminate this
permission_views = ['role_list', 'role_create', 'role_edit', 'role_members', 'role_permissions', 'role_delete']
diff --git a/apps/permissions/models.py b/apps/permissions/models.py
index ff7dfd957f..6c559a5ecb 100644
--- a/apps/permissions/models.py
+++ b/apps/permissions/models.py
@@ -232,7 +232,7 @@ class Role(models.Model):
def members(self, filter_dict=None):
filter_dict = filter_dict or {}
- return [member.member_object for member in self.rolemember_set.filter(**filter_dict)]
+ return [member.member_object for member in self.rolemember_set.filter(**filter_dict) if member is None]
class RoleMember(models.Model):
diff --git a/apps/project_setup/__init__.py b/apps/project_setup/__init__.py
index c8eca31d1f..fd543290ed 100644
--- a/apps/project_setup/__init__.py
+++ b/apps/project_setup/__init__.py
@@ -1,5 +1,5 @@
from django.utils.translation import ugettext_lazy as _
-from navigation.api import register_top_menu
+from navigation.api import register_top_menu, Link
-setup_link = register_top_menu('setup_menu', link={'text': _(u'setup'), 'view': 'setup_list', 'famfam': 'cog'}, position=-2)
+setup_menu = register_top_menu('setup_menu', link=Link(text=_(u'setup'), view='setup_list', sprite='cog'), position=-2)
diff --git a/apps/project_setup/api.py b/apps/project_setup/api.py
index 1d11179701..7535981277 100644
--- a/apps/project_setup/api.py
+++ b/apps/project_setup/api.py
@@ -1,13 +1,9 @@
from __future__ import absolute_import
-from . import setup_link
+from elementtree.ElementTree import Element, SubElement
-setup_items = []
+from . import setup_menu
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', []))
+ SubElement(setup_menu, 'setup_link', link=link)
diff --git a/apps/project_setup/views.py b/apps/project_setup/views.py
index 8ab0426572..cdb8d780ad 100644
--- a/apps/project_setup/views.py
+++ b/apps/project_setup/views.py
@@ -6,14 +6,17 @@ from django.utils.translation import ugettext_lazy as _
from navigation.widgets import button_navigation_widget
-from .api import setup_items
+from . import setup_menu
def setup_list(request):
context = {
- 'object_list': [button_navigation_widget(request, item) for item in setup_items],
+ 'object_list': [button_navigation_widget(request, item.get('link')) for item in setup_menu.getchildren()],
'title': _(u'setup items'),
}
+ # Remove unresolved links form list
+ context['object_list'] = [obj for obj in context['object_list'] if obj]
+
return render_to_response('generic_list_horizontal.html', context,
context_instance=RequestContext(request))
diff --git a/apps/project_tools/__init__.py b/apps/project_tools/__init__.py
index 3fc34151c1..f5bc1dd0e3 100644
--- a/apps/project_tools/__init__.py
+++ b/apps/project_tools/__init__.py
@@ -1,5 +1,5 @@
from django.utils.translation import ugettext_lazy as _
-from navigation.api import register_top_menu
+from navigation.api import register_top_menu, Link
-tool_link = register_top_menu('tools', link={'text': _(u'tools'), 'view': 'tools_list', 'famfam': 'wrench'}, position=-3)
+tool_menu = register_top_menu('tools', link=Link(text=_(u'tools'), view='tools_list', sprite='wrench'), position=-3)
diff --git a/apps/project_tools/api.py b/apps/project_tools/api.py
index d8e66ab7d0..634d6590d9 100644
--- a/apps/project_tools/api.py
+++ b/apps/project_tools/api.py
@@ -1,12 +1,9 @@
from __future__ import absolute_import
-from . import tool_link
+from elementtree.ElementTree import Element, SubElement
+
+from . import tool_menu
-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', []))
+ SubElement(tool_menu, 'tool_link', link=link)
diff --git a/apps/project_tools/views.py b/apps/project_tools/views.py
index 61693a100c..033049b50d 100644
--- a/apps/project_tools/views.py
+++ b/apps/project_tools/views.py
@@ -6,14 +6,17 @@ from django.utils.translation import ugettext_lazy as _
from navigation.widgets import button_navigation_widget
-from .api import tool_items
+from . import tool_menu
def tools_list(request):
context = {
- 'object_list': [button_navigation_widget(request, item) for item in tool_items],
+ 'object_list': [button_navigation_widget(request, item.get('link')) for item in tool_menu.getchildren()],
'title': _(u'tools'),
}
+ # Remove unresolved links form list
+ context['object_list'] = [obj for obj in context['object_list'] if obj]
+
return render_to_response('generic_list_horizontal.html', context,
context_instance=RequestContext(request))
diff --git a/apps/smart_settings/__init__.py b/apps/smart_settings/__init__.py
index bfc495e54d..1f1f71071e 100644
--- a/apps/smart_settings/__init__.py
+++ b/apps/smart_settings/__init__.py
@@ -1,11 +1,12 @@
from django.utils.translation import ugettext_lazy as _
from project_setup.api import register_setup
+from navigation.api import Link
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, 'children_view_regex': [r'^setting_']}
+check_settings = Link(text=_(u'settings'), view='setting_list', sprite='cog', icon='cog.png', condition=is_superuser, children_view_regex=[r'^setting_'])
register_setup(check_settings)
diff --git a/apps/sources/__init__.py b/apps/sources/__init__.py
index aad255b28d..196f8cd02b 100644
--- a/apps/sources/__init__.py
+++ b/apps/sources/__init__.py
@@ -2,8 +2,8 @@ from __future__ import absolute_import
from django.utils.translation import ugettext_lazy as _
-from navigation.api import (register_links,
- register_model_list_columns)
+from navigation.api import (bind_links,
+ register_model_list_columns, Link)
from common.utils import encapsulate
from project_setup.api import register_setup
from documents.permissions import (PERMISSION_DOCUMENT_NEW_VERSION,
@@ -20,58 +20,58 @@ from .permissions import (PERMISSION_SOURCES_SETUP_VIEW,
from .tasks import task_fetch_pop3_emails, task_fetch_imap_emails
from .conf.settings import EMAIL_PROCESSING_INTERVAL
-staging_file_preview = {'text': _(u'preview'), 'class': 'fancybox-noscaling', 'view': 'staging_file_preview', 'args': ['source.source_type', 'source.pk', 'object.id'], 'famfam': 'zoom', 'permissions': [PERMISSION_DOCUMENT_NEW_VERSION, PERMISSION_DOCUMENT_CREATE]}
-staging_file_delete = {'text': _(u'delete'), 'view': 'staging_file_delete', 'args': ['source.source_type', 'source.pk', 'object.id'], 'famfam': 'delete', 'keep_query': True, 'permissions': [PERMISSION_DOCUMENT_NEW_VERSION, PERMISSION_DOCUMENT_CREATE]}
+staging_file_preview = Link(text=_(u'preview'), klass='fancybox-noscaling', view='staging_file_preview', args=['source.source_type', 'source.pk', 'object.pk'], sprite='zoom', permissions=[PERMISSION_DOCUMENT_NEW_VERSION, PERMISSION_DOCUMENT_CREATE])
+staging_file_delete = Link(text=_(u'delete'), view='staging_file_delete', args=['source.source_type', 'source.pk', 'object.pk'], sprite='delete', keep_query=True, permissions=[PERMISSION_DOCUMENT_NEW_VERSION, PERMISSION_DOCUMENT_CREATE])
-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_', r'setup_pop3', r'setup_imap']}
-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_pop3_email_list = {'text': _(u'POP3 email'), 'view': 'setup_pop3_email_list', 'famfam': 'email', 'children_classes': [POP3Email], 'permissions': [PERMISSION_SOURCES_SETUP_VIEW]}
-setup_imap_email_list = {'text': _(u'IMAP email'), 'view': 'setup_imap_email_list', 'famfam': 'email', 'children_classes': [IMAPEmail], 'permissions': [PERMISSION_SOURCES_SETUP_VIEW]}
+setup_sources = Link(text=_(u'sources'), view='setup_web_form_list', sprite='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_', r'setup_pop3', r'setup_imap'])
+setup_web_form_list = Link(text=_(u'web forms'), view='setup_web_form_list', sprite='application_form', icon='application_form.png', children_classes=[WebForm], permissions=[PERMISSION_SOURCES_SETUP_VIEW])
+setup_staging_folder_list = Link(text=_(u'staging folders'), view='setup_staging_folder_list', sprite='folder_camera', children_classes=[StagingFolder], permissions=[PERMISSION_SOURCES_SETUP_VIEW])
+setup_watch_folder_list = Link(text=_(u'watch folders'), view='setup_watch_folder_list', sprite='folder_magnify', children_classes=[WatchFolder], permissions=[PERMISSION_SOURCES_SETUP_VIEW])
+setup_pop3_email_list = Link(text=_(u'POP3 email'), view='setup_pop3_email_list', sprite='email', children_classes=[POP3Email], permissions=[PERMISSION_SOURCES_SETUP_VIEW])
+setup_imap_email_list = Link(text=_(u'IMAP email'), view='setup_imap_email_list', sprite='email', children_classes=[IMAPEmail], 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', '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_log_list = {'text': _(u'logs'), 'view': 'setup_source_log_list', 'args': ['source.source_type', 'source.pk'], 'famfam': 'book', 'permissions': [PERMISSION_SOURCES_SETUP_EDIT]}
+setup_source_edit = Link(text=_(u'edit'), view='setup_source_edit', args=['source.source_type', 'source.pk'], sprite='application_form_edit', permissions=[PERMISSION_SOURCES_SETUP_EDIT])
+setup_source_delete = Link(text=_(u'delete'), view='setup_source_delete', args=['source.source_type', 'source.pk'], sprite='application_form_delete', permissions=[PERMISSION_SOURCES_SETUP_DELETE])
+setup_source_create = Link(text=_(u'add new source'), view='setup_source_create', args='source_type', sprite='application_form_add', permissions=[PERMISSION_SOURCES_SETUP_CREATE])
+setup_source_log_list = Link(text=_(u'logs'), view='setup_source_log_list', args=['source.source_type', 'source.pk'], sprite='book', permissions=[PERMISSION_SOURCES_SETUP_EDIT])
-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]}
+setup_source_transformation_list = Link(text=_(u'transformations'), view='setup_source_transformation_list', args=['source.source_type', 'source.pk'], sprite='shape_move_front', permissions=[PERMISSION_SOURCES_SETUP_EDIT])
+setup_source_transformation_create = Link(text=_(u'add transformation'), view='setup_source_transformation_create', args=['source.source_type', 'source.pk'], sprite='shape_square_add', permissions=[PERMISSION_SOURCES_SETUP_EDIT])
+setup_source_transformation_edit = Link(text=_(u'edit'), view='setup_source_transformation_edit', args='transformation.pk', sprite='shape_square_edit', permissions=[PERMISSION_SOURCES_SETUP_EDIT])
+setup_source_transformation_delete = Link(text=_(u'delete'), view='setup_source_transformation_delete', args='transformation.pk', sprite='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'], 'permissions': [PERMISSION_SOURCES_SETUP_VIEW]}
+source_list = Link(text=_(u'Document sources'), view='setup_web_form_list', sprite='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_NEW_VERSION]}
+upload_version = Link(text=_(u'upload new version'), view='upload_version', args='object.pk', sprite='page_add', permissions=[PERMISSION_DOCUMENT_NEW_VERSION])
-register_links(StagingFile, [staging_file_delete])
+bind_links([StagingFile], [staging_file_delete])
-register_links(SourceTransformation, [setup_source_transformation_edit, setup_source_transformation_delete])
+bind_links([SourceTransformation], [setup_source_transformation_edit, setup_source_transformation_delete])
-register_links(['setup_imap_email_list', 'setup_pop3_email_list', 'setup_web_form_list', 'setup_staging_folder_list', 'setup_watch_folder_list', 'setup_source_create'], [setup_web_form_list, setup_staging_folder_list, setup_pop3_email_list, setup_imap_email_list], menu_name='form_header')
-register_links([WebForm, StagingFolder, POP3Email, IMAPEmail, 'setup_web_form_list', 'setup_staging_folder_list', 'setup_watch_folder_list', 'setup_source_create', 'setup_pop3_email_list', 'setup_imap_email_list'], [setup_source_create], menu_name='secondary_menu')
+bind_links(['setup_imap_email_list', 'setup_pop3_email_list', 'setup_web_form_list', 'setup_staging_folder_list', 'setup_watch_folder_list', 'setup_source_create'], [setup_web_form_list, setup_staging_folder_list, setup_pop3_email_list, setup_imap_email_list], menu_name='form_header')
+bind_links([WebForm, StagingFolder, POP3Email, IMAPEmail, 'setup_web_form_list', 'setup_staging_folder_list', 'setup_watch_folder_list', 'setup_source_create', 'setup_pop3_email_list', 'setup_imap_email_list'], [setup_source_create], menu_name='secondary_menu')
-register_links(WebForm, [setup_web_form_list, setup_staging_folder_list, setup_pop3_email_list, setup_imap_email_list], menu_name='form_header')
-register_links(WebForm, [setup_source_transformation_list, setup_source_edit, setup_source_delete])
+bind_links([WebForm], [setup_web_form_list, setup_staging_folder_list, setup_pop3_email_list, setup_imap_email_list], menu_name='form_header')
+bind_links([WebForm], [setup_source_transformation_list, setup_source_edit, setup_source_delete])
-register_links(StagingFolder, [setup_web_form_list, setup_staging_folder_list, setup_pop3_email_list, setup_imap_email_list], menu_name='form_header')
-register_links(StagingFolder, [setup_source_transformation_list, setup_source_edit, setup_source_delete])
+bind_links([StagingFolder], [setup_web_form_list, setup_staging_folder_list, setup_pop3_email_list, setup_imap_email_list], menu_name='form_header')
+bind_links([StagingFolder], [setup_source_transformation_list, setup_source_edit, setup_source_delete])
-register_links(POP3Email, [setup_web_form_list, setup_staging_folder_list, setup_pop3_email_list, setup_imap_email_list], menu_name='form_header')
-register_links(POP3Email, [setup_source_transformation_list, setup_source_edit, setup_source_delete])
-register_links(POP3Email, [setup_source_log_list])
+bind_links([POP3Email], [setup_web_form_list, setup_staging_folder_list, setup_pop3_email_list, setup_imap_email_list], menu_name='form_header')
+bind_links([POP3Email], [setup_source_transformation_list, setup_source_edit, setup_source_delete])
+bind_links([POP3Email], [setup_source_log_list])
-register_links(IMAPEmail, [setup_web_form_list, setup_staging_folder_list, setup_pop3_email_list, setup_imap_email_list], menu_name='form_header')
-register_links(IMAPEmail, [setup_source_transformation_list, setup_source_edit, setup_source_delete])
-register_links(IMAPEmail, [setup_source_log_list])
+bind_links([IMAPEmail], [setup_web_form_list, setup_staging_folder_list, setup_pop3_email_list, setup_imap_email_list], menu_name='form_header')
+bind_links([IMAPEmail], [setup_source_transformation_list, setup_source_edit, setup_source_delete])
+bind_links([IMAPEmail], [setup_source_log_list])
-register_links(WatchFolder, [setup_web_form_list, setup_staging_folder_list, setup_watch_folder_list, setup_imap_email_list], menu_name='form_header')
-register_links(WatchFolder, [setup_source_transformation_list, setup_source_edit, setup_source_delete])
+bind_links([WatchFolder], [setup_web_form_list, setup_staging_folder_list, setup_watch_folder_list, setup_imap_email_list], menu_name='form_header')
+bind_links([WatchFolder], [setup_source_transformation_list, setup_source_edit, setup_source_delete])
# Document version
-register_links(['document_version_list', 'upload_version', 'document_version_revert'], [upload_version], menu_name='sidebar')
+bind_links(['document_version_list', 'upload_version', 'document_version_revert'], [upload_version], menu_name='sidebar')
-register_links(['setup_source_transformation_create', 'setup_source_transformation_edit', 'setup_source_transformation_delete', 'setup_source_transformation_list'], [setup_source_transformation_create], menu_name='sidebar')
+bind_links(['setup_source_transformation_create', 'setup_source_transformation_edit', 'setup_source_transformation_delete', 'setup_source_transformation_list'], [setup_source_transformation_create], menu_name='sidebar')
source_views = ['setup_web_form_list', 'setup_staging_folder_list', 'setup_watch_folder_list', 'setup_source_edit', 'setup_source_delete', 'setup_source_create', 'setup_source_transformation_list', 'setup_source_transformation_edit', 'setup_source_transformation_delete', 'setup_source_transformation_create']
diff --git a/apps/sources/views.py b/apps/sources/views.py
index 351a6e80e3..54d5fb7f43 100644
--- a/apps/sources/views.py
+++ b/apps/sources/views.py
@@ -23,6 +23,7 @@ from common.utils import encapsulate
from common.widgets import two_state_template
import sendfile
from acls.models import AccessEntry
+from navigation.api import Link
from .models import (WebForm, StagingFolder, SourceTransformation,
WatchFolder, POP3Email, SourceLog, IMAPEmail)
@@ -55,14 +56,7 @@ def get_tab_link_for_source(source, document=None):
view = u'upload_interactive'
args = [u'"%s"' % source.source_type, source.pk]
- return {
- 'text': source.title,
- 'view': view,
- 'args': args,
- 'famfam': source.icon,
- 'keep_query': True,
- 'conditional_highlight': return_function(source),
- }
+ return Link(text=source.title, view=view, args=args, sprite=source.icon, keep_query=True, conditional_highlight=return_function(source))
def get_active_tab_links(document=None):
@@ -703,6 +697,7 @@ def setup_source_transformation_edit(request, transformation_id):
return render_to_response('generic_form.html', {
'title': _(u'Edit transformation: %s') % source_transformation,
'form': form,
+ 'source_type': source_transformation.content_object.source_type,
'source': source_transformation.content_object,
'transformation': source_transformation,
'navigation_object_list': [
@@ -735,6 +730,7 @@ def setup_source_transformation_delete(request, transformation_id):
'delete_view': True,
'transformation': source_transformation,
'source': source_transformation.content_object,
+ 'source_type': source_transformation.content_object.source_type,
'navigation_object_list': [
{'object': 'source', 'name': _(u'source')},
{'object': 'transformation', 'name': _(u'transformation')}
diff --git a/apps/tags/__init__.py b/apps/tags/__init__.py
index 2bfcb2f508..ec5df2c9db 100644
--- a/apps/tags/__init__.py
+++ b/apps/tags/__init__.py
@@ -2,8 +2,8 @@ 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 navigation.api import (bind_links, register_top_menu,
+ register_model_list_columns, register_multi_item_links, Link)
from common.utils import encapsulate
from documents.models import Document
from acls.api import class_permissions
@@ -17,17 +17,17 @@ 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', '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, 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]}
+tag_list = Link(text=_(u'tag list'), view='tag_list', sprite='tag_blue')
+tag_create = Link(text=_(u'create new tag'), view='tag_create', sprite='tag_blue_add', permissions=[PERMISSION_TAG_CREATE])
+tag_attach = Link(text=_(u'attach tag'), view='tag_attach', args='object.pk', sprite='tag_blue_add', permissions=[PERMISSION_TAG_ATTACH])
+tag_document_remove = Link(text=_(u'remove'), view='tag_remove', args=['object.pk', 'document.pk'], sprite='tag_blue_delete', permissions=[PERMISSION_TAG_REMOVE])
+tag_document_remove_multiple = Link(text=_(u'remove'), view='tag_multiple_remove', args='document.pk', sprite='tag_blue_delete', permissions=[PERMISSION_TAG_REMOVE])
+tag_document_list = Link(text=_(u'tags'), view='document_tags', args='object.pk', sprite='tag_blue', permissions=[PERMISSION_TAG_REMOVE, PERMISSION_TAG_ATTACH], children_view_regex=['tag'])
+tag_delete = Link(text=_(u'delete'), view='tag_delete', args='object.pk', sprite='tag_blue_delete', permissions=[PERMISSION_TAG_DELETE])
+tag_edit = Link(text=_(u'edit'), view='tag_edit', args='object.pk', sprite='tag_blue_edit', permissions=[PERMISSION_TAG_EDIT])
+tag_tagged_item_list = Link(text=_(u'tagged documents'), view='tag_tagged_item_list', args='object.pk', sprite='page')
+tag_multiple_delete = Link(text=_(u'delete'), view='tag_multiple_delete', sprite='tag_blue_delete', permissions=[PERMISSION_TAG_DELETE])
+tag_acl_list = Link(text=_(u'ACLs'), view='tag_acl_list', args='object.pk', sprite='lock', permissions=[ACLS_VIEW_ACL])
register_model_list_columns(Tag, [
{
@@ -46,13 +46,13 @@ register_model_list_columns(Document, [
},
])
-register_links(Tag, [tag_tagged_item_list, tag_edit, tag_delete, tag_acl_list])
+bind_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, '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)'])
+bind_links([Tag, 'tag_list', 'tag_create'], [tag_list, tag_create], menu_name='secondary_menu')
+register_top_menu('tags', link=Link(text=_(u'tags'), view='tag_list', sprite='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_remove', 'tag_multiple_remove', 'tag_attach'], [tag_attach], menu_name='sidebar')
+bind_links([Document], [tag_document_list], menu_name='form_header')
+bind_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, [
diff --git a/apps/user_management/__init__.py b/apps/user_management/__init__.py
index 00d0e50533..4e04ca2c9c 100644
--- a/apps/user_management/__init__.py
+++ b/apps/user_management/__init__.py
@@ -3,36 +3,36 @@ 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 navigation.api import bind_links, register_multi_item_links, Link
from project_setup.api import register_setup
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], '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]}
-user_multiple_delete = {u'text': _('delete'), 'view': 'user_multiple_delete', 'famfam': 'user_delete', 'permissions': [PERMISSION_USER_DELETE]}
-user_set_password = {u'text': _('reset password'), 'view': 'user_set_password', 'args': 'object.id', 'famfam': 'lock_edit', 'permissions': [PERMISSION_USER_EDIT]}
-user_multiple_set_password = {u'text': _('reset password'), 'view': 'user_multiple_set_password', 'famfam': 'lock_edit', 'permissions': [PERMISSION_USER_EDIT]}
+user_list = Link(text=_(u'user list'), view='user_list', sprite='user', permissions=[PERMISSION_USER_VIEW])
+user_setup = Link(text=_(u'users'), view='user_list', sprite='user', icon='user.png', permissions=[PERMISSION_USER_VIEW], children_view_regex=[r'^user_'])
+user_edit = Link(text=_(u'edit'), view='user_edit', args='object.id', sprite='user_edit', permissions=[PERMISSION_USER_EDIT])
+user_add = Link(text=_(u'create new user'), view='user_add', sprite='user_add', permissions=[PERMISSION_USER_CREATE])
+user_delete = Link(text=_('delete'), view='user_delete', args='object.id', sprite='user_delete', permissions=[PERMISSION_USER_DELETE])
+user_multiple_delete = Link(text=_('delete'), view='user_multiple_delete', sprite='user_delete', permissions=[PERMISSION_USER_DELETE])
+user_set_password = Link(text=_('reset password'), view='user_set_password', args='object.id', sprite='lock_edit', permissions=[PERMISSION_USER_EDIT])
+user_multiple_set_password = Link(text=_('reset password'), view='user_multiple_set_password', sprite='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], '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]}
-group_multiple_delete = {u'text': _('delete'), 'view': 'group_multiple_delete', 'famfam': 'group_delete', 'permissions': [PERMISSION_GROUP_DELETE]}
-group_members = {'text': _(u'members'), 'view': 'group_members', 'args': 'object.id', 'famfam': 'group_link', 'permissions': [PERMISSION_GROUP_EDIT]}
+group_list = Link(text=_(u'group list'), view='group_list', sprite='group', permissions=[PERMISSION_GROUP_VIEW])
+group_setup = Link(text=_(u'groups'), view='group_list', sprite='group', icon='group.png', permissions=[PERMISSION_GROUP_VIEW], children_view_regex=[r'^group_'])
+group_edit = Link(text=_(u'edit'), view='group_edit', args='object.id', sprite='group_edit', permissions=[PERMISSION_GROUP_EDIT])
+group_add = Link(text=_(u'create new group'), view='group_add', sprite='group_add', permissions=[PERMISSION_GROUP_CREATE])
+group_delete = Link(text=_('delete'), view='group_delete', args='object.id', sprite='group_delete', permissions=[PERMISSION_GROUP_DELETE])
+group_multiple_delete = Link(text=_('delete'), view='group_multiple_delete', sprite='group_delete', permissions=[PERMISSION_GROUP_DELETE])
+group_members = Link(text=_(u'members'), view='group_members', args='object.id', sprite='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'secondary_menu')
+bind_links([User], [user_edit, user_set_password, user_delete])
+bind_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'secondary_menu')
+bind_links([Group], [group_edit, group_members, group_delete])
+bind_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/workflows/__init__.py b/apps/workflows/__init__.py
new file mode 100644
index 0000000000..d4a714a21e
--- /dev/null
+++ b/apps/workflows/__init__.py
@@ -0,0 +1,68 @@
+from __future__ import absolute_import
+
+from django.utils.translation import ugettext_lazy as _
+
+from project_setup.api import register_setup
+from navigation.api import Link, bind_links
+
+from .permissions import (PERMISSION_WORKFLOW_SETUP_VIEW,
+ PERMISSION_WORKFLOW_SETUP_CREATE, PERMISSION_WORKFLOW_SETUP_EDIT,
+ PERMISSION_WORKFLOW_SETUP_DELETE, PERMISSION_STATE_SETUP_VIEW,
+ PERMISSION_STATE_SETUP_CREATE, PERMISSION_STATE_SETUP_EDIT,
+ PERMISSION_STATE_SETUP_DELETE)
+
+from .models import (Workflow, State, WorkflowState, WorkflowNode)
+
+setup_workflow_list_link = Link(text=_(u'workflow list'), view='setup_workflow_list', sprite='chart_organisation', permissions=[PERMISSION_WORKFLOW_SETUP_VIEW])
+setup_workflow_create_link = Link(text=_(u'create new workflow'), view='setup_workflow_create', sprite='chart_organisation_add', permissions=[PERMISSION_WORKFLOW_SETUP_CREATE])
+setup_workflow_edit_link = Link(text=_(u'edit'), view='setup_workflow_edit', args='workflow.pk', sprite='chart_organisation', permissions=[PERMISSION_WORKFLOW_SETUP_EDIT])
+setup_workflow_delete_link = Link(text=_(u'delete'), view='setup_workflow_delete', args='workflow.pk', sprite='chart_organisation_delete', permissions=[PERMISSION_WORKFLOW_SETUP_DELETE])
+setup_workflow_states_list_link = Link(text=_(u'states'), view='setup_workflow_states_list', args='workflow.pk', sprite='transmit_go', permissions=[PERMISSION_WORKFLOW_SETUP_EDIT])
+setup_workflow_states_add_link = Link(text=_(u'add workflow state'), view='setup_workflow_state_add', args='workflow.pk', sprite='transmit_add', permissions=[PERMISSION_WORKFLOW_SETUP_EDIT])
+setup_workflow_states_edit_link = Link(text=_(u'edit workflow state'), view='setup_workflow_state_edit', args='workflow_state.pk', sprite='transmit_edit', permissions=[PERMISSION_WORKFLOW_SETUP_EDIT])
+setup_workflow_states_remove_link = Link(text=_(u'remove workflow state'), view='setup_workflow_state_remove', args='workflow_state.pk', sprite='transmit_delete', permissions=[PERMISSION_WORKFLOW_SETUP_EDIT])
+
+setup_workflow_state_transitions_list_link = Link(text=_(u'workflow state transitions'), view='setup_workflow_state_transitions_list', args='workflow_state.pk', sprite='chart_line', permissions=[PERMISSION_WORKFLOW_SETUP_EDIT])
+setup_workflow_state_transition_add_link = Link(text=_(u'add workflow state transition'), view='setup_workflow_state_transition_add', args='workflow_state.pk', sprite='chart_line_add', permissions=[PERMISSION_WORKFLOW_SETUP_EDIT])
+setup_workflow_state_transition_edit_link = Link(text=_(u'edit workflow state transition'), view='setup_workflow_state_transition_edit', args='workflow_state_transition.pk', sprite='chart_line_edit', permissions=[PERMISSION_WORKFLOW_SETUP_EDIT])
+
+setup_state_list_link = Link(text=_(u'state list'), view='setup_state_list', sprite='transmit', permissions=[PERMISSION_STATE_SETUP_VIEW])
+setup_state_create_link = Link(text=_(u'create new state'), view='setup_state_create', sprite='transmit_add', permissions=[PERMISSION_STATE_SETUP_CREATE])
+setup_state_edit_link = Link(text=_(u'edit'), view='setup_state_edit', args='object.pk', sprite='transmit_edit', permissions=[PERMISSION_STATE_SETUP_EDIT])
+setup_state_delete_link = Link(text=_(u'delete'), view='setup_state_delete', args='object.pk', sprite='transmit_delete', permissions=[PERMISSION_STATE_SETUP_DELETE])
+
+setup_workflow_node_list_link = Link(text=_(u'node list'), view='setup_workflow_node_list', args='workflow.pk', sprite='chart_line', permissions=[PERMISSION_WORKFLOW_SETUP_EDIT])
+#setup_workflow_node_create_link = Link(text=_(u'create new transition'), view='setup_transition_create', sprite='chart_line_add', permissions=[PERMISSION_TRANSITION_SETUP_CREATE])
+setup_workflow_node_edit_link = Link(text=_(u'edit'), view='setup_workflow_node_edit', args='workflow_node.pk', sprite='chart_line_edit', permissions=[PERMISSION_WORKFLOW_SETUP_EDIT])
+#setup_workflow_node_delete_link = Link(text=_(u'delete'), view='setup_transition_delete', args='transition.pk', sprite='chart_line_delete', permissions=[PERMISSION_TRANSITION_SETUP_DELETE])
+
+bind_links(
+ [
+ Workflow, State,
+ 'setup_workflow_list', 'setup_workflow_create',
+ 'setup_state_list', 'setup_state_create',
+ #'setup_transition_list', 'setup_transition_create',
+ 'setup_transition_create',
+ ], [
+ setup_workflow_list_link, setup_state_list_link#, setup_transition_list_link
+ ], menu_name=u'form_header')
+
+bind_links([Workflow], [setup_workflow_node_list_link, setup_workflow_states_list_link, setup_workflow_edit_link, setup_workflow_delete_link])
+bind_links([Workflow, 'setup_workflow_list', 'setup_workflow_create'], [setup_workflow_create_link], menu_name=u'secondary_menu')
+bind_links([WorkflowState, 'setup_workflow_states_list', 'setup_workflow_states_add'], [setup_workflow_states_add_link], menu_name=u'sidebar')
+
+bind_links([State], [setup_state_edit_link, setup_state_delete_link])
+bind_links([State, 'setup_state_list', 'setup_state_create'], [setup_state_create_link], menu_name=u'secondary_menu')
+
+#bind_links([Transition], [setup_transition_edit_link, setup_transition_delete_link])
+#bind_links([Transition, 'setup_transition_list', 'setup_transition_create'], [setup_transition_create_link], menu_name=u'secondary_menu')
+
+#bind_links([WorkflowState], [setup_workflow_state_transitions_list_link, setup_workflow_states_edit_link, setup_workflow_states_remove_link])
+bind_links([WorkflowState], [setup_workflow_states_edit_link, setup_workflow_states_remove_link])
+
+#bind_links([WorkflowState], [setup_workflow_state_transition_add_link], menu_name=u'sidebar')
+#bind_links([WorkflowNode], [setup_workflow_state_transition_add_link], menu_name=u'sidebar')
+
+bind_links([WorkflowNode], [setup_workflow_node_edit_link])#, setup_transition_delete_link])
+
+register_setup(Link(text=_(u'workflows'), view='setup_workflow_list', icon='chart_organisation.png', permissions=[PERMISSION_WORKFLOW_SETUP_VIEW]))
diff --git a/apps/workflows/forms.py b/apps/workflows/forms.py
new file mode 100644
index 0000000000..5f0e897ebd
--- /dev/null
+++ b/apps/workflows/forms.py
@@ -0,0 +1,70 @@
+from __future__ import absolute_import
+
+from django import forms
+from django.utils.translation import ugettext_lazy as _
+
+from .models import Workflow, State, WorkflowState, WorkflowNode
+
+
+class NodeForm(forms.Form):
+ def __init__(self, *args, **kwargs):
+ #workflow = kwargs.pop('workflow')
+ super(WorkflowStateSetupForm, self).__init__(*args, **kwargs)
+ #self.fields['workflow'].initial = workflow
+ #self.fields['workflow'].widget = forms.widgets.HiddenInput()
+ print self.instance
+
+ #def choices(self, workflow):
+ # return {
+ ## 'next_node': workflow.nodes.all()
+ # }
+
+
+class WorkflowSetupForm(forms.ModelForm):
+ class Meta:
+ exclude = ('initial_node,')
+ model = Workflow
+
+
+class StateSetupForm(forms.ModelForm):
+ class Meta:
+ model = State
+
+
+class WorkflowStateSetupForm(forms.ModelForm):
+ def __init__(self, *args, **kwargs):
+ workflow = kwargs.pop('workflow')
+ super(WorkflowStateSetupForm, self).__init__(*args, **kwargs)
+ self.fields['workflow'].initial = workflow
+ self.fields['workflow'].widget = forms.widgets.HiddenInput()
+
+ class Meta:
+ model = WorkflowState
+
+
+class WorkflowNodeSetupForm(forms.ModelForm):
+ def __init__(self, *args, **kwargs):
+ workflow = kwargs.pop('workflow')
+ super(WorkflowNodeSetupForm, self).__init__(*args, **kwargs)
+ self.fields['workflow'].initial = workflow
+ self.fields['workflow'].widget = forms.widgets.HiddenInput()
+
+ class Meta:
+ model = WorkflowNode
+
+#class TransitionSetupForm(forms.ModelForm):
+# class Meta:
+# model = Transition
+
+
+#class WorkflowStateTransitionSetupForm(forms.ModelForm):
+# def __init__(self, *args, **kwargs):
+# workflow_state = kwargs.pop('workflow_state')
+# super(WorkflowStateTransitionSetupForm, self).__init__(*args, **kwargs)
+# self.fields['workflow_state_source'].initial = workflow_state
+# self.fields['workflow_state_source'].widget = forms.widgets.HiddenInput()
+#
+# class Meta:
+# model = WorkflowStateTransition
+
+
diff --git a/apps/workflows/literals.py b/apps/workflows/literals.py
new file mode 100644
index 0000000000..da3e4aeb23
--- /dev/null
+++ b/apps/workflows/literals.py
@@ -0,0 +1,19 @@
+from django.utils.translation import ugettext_lazy as _
+
+OPERAND_OR = 'or'
+OPERAND_AND = 'and'
+
+OPERAND_CHOICES = (
+ (OPERAND_OR, _(u'or')),
+ (OPERAND_AND, _(u'and'))
+)
+
+NODE_TYPE_TASK = 'task'
+NODE_TYPE_START = 'start'
+NODE_TYPE_END = 'end'
+
+NODE_TYPE_CHOICES = (
+ (NODE_TYPE_TASK, _(u'Simple task')),
+ (NODE_TYPE_START, _(u'Start')),
+ (NODE_TYPE_END, _(u'End')),
+)
diff --git a/apps/workflows/migrations/0001_initial.py b/apps/workflows/migrations/0001_initial.py
new file mode 100644
index 0000000000..015bc09db3
--- /dev/null
+++ b/apps/workflows/migrations/0001_initial.py
@@ -0,0 +1,202 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding model 'Ability'
+ db.create_table('workflows_ability', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('label', self.gf('django.db.models.fields.CharField')(unique=True, max_length=128)),
+ ('description', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ))
+ db.send_create_signal('workflows', ['Ability'])
+
+ # Adding model 'Workflow'
+ db.create_table('workflows_workflow', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('label', self.gf('django.db.models.fields.CharField')(unique=True, max_length=128)),
+ ('initial_state', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='workflow_initial_state', null=True, to=orm['workflows.WorkflowState'])),
+ ('description', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ))
+ db.send_create_signal('workflows', ['Workflow'])
+
+ # Adding model 'State'
+ db.create_table('workflows_state', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('label', self.gf('django.db.models.fields.CharField')(max_length=128)),
+ ('description', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ))
+ db.send_create_signal('workflows', ['State'])
+
+ # Adding model 'Transition'
+ db.create_table('workflows_transition', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('label', self.gf('django.db.models.fields.CharField')(max_length=128)),
+ ('description', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ))
+ db.send_create_signal('workflows', ['Transition'])
+
+ # Adding model 'WorkflowState'
+ db.create_table('workflows_workflowstate', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('workflow', self.gf('django.db.models.fields.related.ForeignKey')(related_name='workflow_state_workflow', to=orm['workflows.Workflow'])),
+ ('state', self.gf('django.db.models.fields.related.ForeignKey')(related_name='workflow_state_state', to=orm['workflows.State'])),
+ ('description', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ))
+ db.send_create_signal('workflows', ['WorkflowState'])
+
+ # Adding model 'WorkflowStateAbilityGrant'
+ db.create_table('workflows_workflowstateabilitygrant', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('workflow_state', self.gf('django.db.models.fields.related.ForeignKey')(related_name='workflow_state_ability', to=orm['workflows.WorkflowState'])),
+ ('content_type', self.gf('django.db.models.fields.related.ForeignKey')(related_name='workflow_state_ability_object', to=orm['contenttypes.ContentType'])),
+ ('object_id', self.gf('django.db.models.fields.PositiveIntegerField')()),
+ ))
+ db.send_create_signal('workflows', ['WorkflowStateAbilityGrant'])
+
+ # Adding model 'WorkflowStateTransition'
+ db.create_table('workflows_workflowstatetransition', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('workflow_state_source', self.gf('django.db.models.fields.related.ForeignKey')(related_name='workflow_state_transition_source', to=orm['workflows.WorkflowState'])),
+ ('transition', self.gf('django.db.models.fields.related.ForeignKey')(related_name='workflow_state_transition', to=orm['workflows.Transition'])),
+ ('workflow_state_destination', self.gf('django.db.models.fields.related.ForeignKey')(related_name='workflow_state_transition_destination', to=orm['workflows.WorkflowState'])),
+ ('description', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ))
+ db.send_create_signal('workflows', ['WorkflowStateTransition'])
+
+ # Adding model 'WorkflowStateTransitionAbility'
+ db.create_table('workflows_workflowstatetransitionability', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('attribute_comparison_operand', self.gf('django.db.models.fields.CharField')(default='and', max_length=8)),
+ ('negate', self.gf('django.db.models.fields.BooleanField')(default=False)),
+ ('ability', self.gf('django.db.models.fields.related.ForeignKey')(related_name='workflow_state_transition_ability', to=orm['workflows.Ability'])),
+ ('description', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ))
+ db.send_create_signal('workflows', ['WorkflowStateTransitionAbility'])
+
+ # Adding model 'WorkflowInstance'
+ db.create_table('workflows_workflowinstance', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('workflow', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['workflows.Workflow'])),
+ ('content_type', self.gf('django.db.models.fields.related.ForeignKey')(related_name='workflow_instance_object', to=orm['contenttypes.ContentType'])),
+ ('object_id', self.gf('django.db.models.fields.PositiveIntegerField')()),
+ ('workflow_state', self.gf('django.db.models.fields.related.ForeignKey')(related_name='workflow_instance_state', to=orm['workflows.WorkflowState'])),
+ ))
+ db.send_create_signal('workflows', ['WorkflowInstance'])
+
+ # Adding unique constraint on 'WorkflowInstance', fields ['content_type', 'object_id', 'workflow']
+ db.create_unique('workflows_workflowinstance', ['content_type_id', 'object_id', 'workflow_id'])
+
+
+ def backwards(self, orm):
+
+ # Removing unique constraint on 'WorkflowInstance', fields ['content_type', 'object_id', 'workflow']
+ db.delete_unique('workflows_workflowinstance', ['content_type_id', 'object_id', 'workflow_id'])
+
+ # Deleting model 'Ability'
+ db.delete_table('workflows_ability')
+
+ # Deleting model 'Workflow'
+ db.delete_table('workflows_workflow')
+
+ # Deleting model 'State'
+ db.delete_table('workflows_state')
+
+ # Deleting model 'Transition'
+ db.delete_table('workflows_transition')
+
+ # Deleting model 'WorkflowState'
+ db.delete_table('workflows_workflowstate')
+
+ # Deleting model 'WorkflowStateAbilityGrant'
+ db.delete_table('workflows_workflowstateabilitygrant')
+
+ # Deleting model 'WorkflowStateTransition'
+ db.delete_table('workflows_workflowstatetransition')
+
+ # Deleting model 'WorkflowStateTransitionAbility'
+ db.delete_table('workflows_workflowstatetransitionability')
+
+ # Deleting model 'WorkflowInstance'
+ db.delete_table('workflows_workflowinstance')
+
+
+ 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'})
+ },
+ 'workflows.ability': {
+ 'Meta': {'object_name': 'Ability'},
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'label': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'})
+ },
+ 'workflows.state': {
+ 'Meta': {'object_name': 'State'},
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'label': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'workflows.transition': {
+ 'Meta': {'object_name': 'Transition'},
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'label': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'workflows.workflow': {
+ 'Meta': {'object_name': 'Workflow'},
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'initial_state': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_initial_state'", 'null': 'True', 'to': "orm['workflows.WorkflowState']"}),
+ 'label': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'})
+ },
+ 'workflows.workflowinstance': {
+ 'Meta': {'unique_together': "(('content_type', 'object_id', 'workflow'),)", 'object_name': 'WorkflowInstance'},
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'workflow_instance_object'", 'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.Workflow']"}),
+ 'workflow_state': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'workflow_instance_state'", 'to': "orm['workflows.WorkflowState']"})
+ },
+ 'workflows.workflowstate': {
+ 'Meta': {'object_name': 'WorkflowState'},
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'state': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'workflow_state_state'", 'to': "orm['workflows.State']"}),
+ 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'workflow_state_workflow'", 'to': "orm['workflows.Workflow']"})
+ },
+ 'workflows.workflowstateabilitygrant': {
+ 'Meta': {'object_name': 'WorkflowStateAbilityGrant'},
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'workflow_state_ability_object'", 'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'workflow_state': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'workflow_state_ability'", 'to': "orm['workflows.WorkflowState']"})
+ },
+ 'workflows.workflowstatetransition': {
+ 'Meta': {'object_name': 'WorkflowStateTransition'},
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'transition': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'workflow_state_transition'", 'to': "orm['workflows.Transition']"}),
+ 'workflow_state_destination': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'workflow_state_transition_destination'", 'to': "orm['workflows.WorkflowState']"}),
+ 'workflow_state_source': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'workflow_state_transition_source'", 'to': "orm['workflows.WorkflowState']"})
+ },
+ 'workflows.workflowstatetransitionability': {
+ 'Meta': {'object_name': 'WorkflowStateTransitionAbility'},
+ 'ability': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'workflow_state_transition_ability'", 'to': "orm['workflows.Ability']"}),
+ 'attribute_comparison_operand': ('django.db.models.fields.CharField', [], {'default': "'and'", 'max_length': '8'}),
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'negate': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ }
+ }
+
+ complete_apps = ['workflows']
diff --git a/apps/workflows/migrations/0002_auto__del_workflowstateabilitygrant__del_workflowstatetransition__del_.py b/apps/workflows/migrations/0002_auto__del_workflowstateabilitygrant__del_workflowstatetransition__del_.py
new file mode 100644
index 0000000000..6c8fb3eac5
--- /dev/null
+++ b/apps/workflows/migrations/0002_auto__del_workflowstateabilitygrant__del_workflowstatetransition__del_.py
@@ -0,0 +1,231 @@
+# 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 'WorkflowInstance', fields ['object_id', 'content_type', 'workflow']
+ db.delete_unique('workflows_workflowinstance', ['object_id', 'content_type_id', 'workflow_id'])
+
+ # Deleting model 'WorkflowStateAbilityGrant'
+ db.delete_table('workflows_workflowstateabilitygrant')
+
+ # Deleting model 'WorkflowStateTransition'
+ db.delete_table('workflows_workflowstatetransition')
+
+ # Deleting model 'WorkflowStateTransitionAbility'
+ db.delete_table('workflows_workflowstatetransitionability')
+
+ # Deleting model 'Transition'
+ db.delete_table('workflows_transition')
+
+ # Adding model 'WorkflowInstanceActiveState'
+ db.create_table('workflows_workflowinstanceactivestate', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('workflow_instance', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['workflows.WorkflowInstance'])),
+ ('state', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['workflows.State'], null=True)),
+ ))
+ db.send_create_signal('workflows', ['WorkflowInstanceActiveState'])
+
+ # Adding unique constraint on 'WorkflowInstanceActiveState', fields ['workflow_instance', 'state']
+ db.create_unique('workflows_workflowinstanceactivestate', ['workflow_instance_id', 'state_id'])
+
+ # Adding model 'End'
+ db.create_table('workflows_end', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('label', self.gf('django.db.models.fields.CharField')(max_length=128)),
+ ('description', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ))
+ db.send_create_signal('workflows', ['End'])
+
+ # Adding model 'WorkflowInstanceActiveNode'
+ db.create_table('workflows_workflowinstanceactivenode', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('workflow_instance', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['workflows.WorkflowInstance'])),
+ ('workflow_node', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['workflows.WorkflowNode'])),
+ ))
+ db.send_create_signal('workflows', ['WorkflowInstanceActiveNode'])
+
+ # Adding model 'Start'
+ db.create_table('workflows_start', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('label', self.gf('django.db.models.fields.CharField')(max_length=128)),
+ ('description', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('content_type', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['contenttypes.ContentType'], null=True)),
+ ('object_id', self.gf('django.db.models.fields.PositiveIntegerField')(null=True)),
+ ))
+ db.send_create_signal('workflows', ['Start'])
+
+ # Adding model 'WorkflowNode'
+ db.create_table('workflows_workflownode', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('workflow', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['workflows.Workflow'])),
+ ('content_type', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['contenttypes.ContentType'])),
+ ('object_id', self.gf('django.db.models.fields.PositiveIntegerField')()),
+ ('description', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ))
+ db.send_create_signal('workflows', ['WorkflowNode'])
+
+ # Adding field 'Workflow.initial_node'
+ db.add_column('workflows_workflow', 'initial_node', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='workflow_initial_node', null=True, to=orm['workflows.WorkflowNode']), keep_default=False)
+
+ # Deleting field 'WorkflowInstance.workflow_state'
+ db.delete_column('workflows_workflowinstance', 'workflow_state_id')
+
+ # Adding unique constraint on 'WorkflowState', fields ['state', 'workflow']
+ db.create_unique('workflows_workflowstate', ['state_id', 'workflow_id'])
+
+
+ def backwards(self, orm):
+
+ # Removing unique constraint on 'WorkflowState', fields ['state', 'workflow']
+ db.delete_unique('workflows_workflowstate', ['state_id', 'workflow_id'])
+
+ # Removing unique constraint on 'WorkflowInstanceActiveState', fields ['workflow_instance', 'state']
+ db.delete_unique('workflows_workflowinstanceactivestate', ['workflow_instance_id', 'state_id'])
+
+ # Adding model 'WorkflowStateAbilityGrant'
+ db.create_table('workflows_workflowstateabilitygrant', (
+ ('workflow_state', self.gf('django.db.models.fields.related.ForeignKey')(related_name='workflow_state_ability', to=orm['workflows.WorkflowState'])),
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('content_type', self.gf('django.db.models.fields.related.ForeignKey')(related_name='workflow_state_ability_object', to=orm['contenttypes.ContentType'])),
+ ('object_id', self.gf('django.db.models.fields.PositiveIntegerField')()),
+ ))
+ db.send_create_signal('workflows', ['WorkflowStateAbilityGrant'])
+
+ # Adding model 'WorkflowStateTransition'
+ db.create_table('workflows_workflowstatetransition', (
+ ('description', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('workflow_state_source', self.gf('django.db.models.fields.related.ForeignKey')(related_name='workflow_state_transition_source', to=orm['workflows.WorkflowState'])),
+ ('transition', self.gf('django.db.models.fields.related.ForeignKey')(related_name='workflow_state_transition', to=orm['workflows.Transition'])),
+ ('workflow_state_destination', self.gf('django.db.models.fields.related.ForeignKey')(related_name='workflow_state_transition_destination', to=orm['workflows.WorkflowState'])),
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ))
+ db.send_create_signal('workflows', ['WorkflowStateTransition'])
+
+ # Adding model 'WorkflowStateTransitionAbility'
+ db.create_table('workflows_workflowstatetransitionability', (
+ ('ability', self.gf('django.db.models.fields.related.ForeignKey')(related_name='workflow_state_transition_ability', to=orm['workflows.Ability'])),
+ ('attribute_comparison_operand', self.gf('django.db.models.fields.CharField')(default='and', max_length=8)),
+ ('negate', self.gf('django.db.models.fields.BooleanField')(default=False)),
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('description', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ))
+ db.send_create_signal('workflows', ['WorkflowStateTransitionAbility'])
+
+ # Adding model 'Transition'
+ db.create_table('workflows_transition', (
+ ('label', self.gf('django.db.models.fields.CharField')(max_length=128)),
+ ('description', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ))
+ db.send_create_signal('workflows', ['Transition'])
+
+ # Deleting model 'WorkflowInstanceActiveState'
+ db.delete_table('workflows_workflowinstanceactivestate')
+
+ # Deleting model 'End'
+ db.delete_table('workflows_end')
+
+ # Deleting model 'WorkflowInstanceActiveNode'
+ db.delete_table('workflows_workflowinstanceactivenode')
+
+ # Deleting model 'Start'
+ db.delete_table('workflows_start')
+
+ # Deleting model 'WorkflowNode'
+ db.delete_table('workflows_workflownode')
+
+ # Deleting field 'Workflow.initial_node'
+ db.delete_column('workflows_workflow', 'initial_node_id')
+
+ # User chose to not deal with backwards NULL issues for 'WorkflowInstance.workflow_state'
+ raise RuntimeError("Cannot reverse this migration. 'WorkflowInstance.workflow_state' and its values cannot be restored.")
+
+ # Adding unique constraint on 'WorkflowInstance', fields ['object_id', 'content_type', 'workflow']
+ db.create_unique('workflows_workflowinstance', ['object_id', 'content_type_id', 'workflow_id'])
+
+
+ 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'})
+ },
+ 'workflows.ability': {
+ 'Meta': {'object_name': 'Ability'},
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'label': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'})
+ },
+ 'workflows.end': {
+ 'Meta': {'object_name': 'End'},
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'label': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'workflows.start': {
+ 'Meta': {'object_name': 'Start'},
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True'}),
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'label': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'})
+ },
+ 'workflows.state': {
+ 'Meta': {'object_name': 'State'},
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'label': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'workflows.workflow': {
+ 'Meta': {'object_name': 'Workflow'},
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'initial_node': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_initial_node'", 'null': 'True', 'to': "orm['workflows.WorkflowNode']"}),
+ 'initial_state': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_initial_state'", 'null': 'True', 'to': "orm['workflows.WorkflowState']"}),
+ 'label': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'})
+ },
+ 'workflows.workflowinstance': {
+ 'Meta': {'object_name': 'WorkflowInstance'},
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'workflow_instance_object'", 'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.Workflow']"})
+ },
+ 'workflows.workflowinstanceactivenode': {
+ 'Meta': {'object_name': 'WorkflowInstanceActiveNode'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'workflow_instance': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.WorkflowInstance']"}),
+ 'workflow_node': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.WorkflowNode']"})
+ },
+ 'workflows.workflowinstanceactivestate': {
+ 'Meta': {'unique_together': "(('workflow_instance', 'state'),)", 'object_name': 'WorkflowInstanceActiveState'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']", 'null': 'True'}),
+ 'workflow_instance': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.WorkflowInstance']"})
+ },
+ 'workflows.workflownode': {
+ 'Meta': {'object_name': 'WorkflowNode'},
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.Workflow']"})
+ },
+ 'workflows.workflowstate': {
+ 'Meta': {'unique_together': "(('workflow', 'state'),)", 'object_name': 'WorkflowState'},
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}),
+ 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.Workflow']"})
+ }
+ }
+
+ complete_apps = ['workflows']
diff --git a/apps/workflows/migrations/__init__.py b/apps/workflows/migrations/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/apps/workflows/models.py b/apps/workflows/models.py
new file mode 100644
index 0000000000..509e317e3e
--- /dev/null
+++ b/apps/workflows/models.py
@@ -0,0 +1,325 @@
+from __future__ import absolute_import
+
+from django.db import models
+from django.contrib.contenttypes import generic
+from django.contrib.contenttypes.models import ContentType
+from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import ugettext
+
+from permissions.models import StoredPermission
+
+from .literals import OPERAND_CHOICES, OPERAND_AND
+#from .literals import NODE_TYPE_TASK, NODE_TYPE_START, NODE_TYPE_END
+
+#NODE_TYPE_CHOICES
+
+class Ability(models.Model):
+ label = models.CharField(max_length=128, unique=True, verbose_name = _(u'label'))
+ description = models.TextField(blank=True, verbose_name=_(u'description'))
+
+ def __unicode__(self):
+ return self.label
+
+ class Meta:
+ verbose_name = _(u'ability')
+ verbose_name_plural = _(u'abilities')
+
+
+class Workflow(models.Model):
+ label = models.CharField(max_length=128, unique=True, verbose_name = _(u'label'))
+ initial_node = models.ForeignKey('WorkflowNode', related_name='workflow_initial_node', blank=True, null=True, verbose_name=_(u'initial node'))
+ initial_state = models.ForeignKey('WorkflowState', related_name='workflow_initial_state', blank=True, null=True, verbose_name=_(u'initial state'))
+ description = models.TextField(blank=True, verbose_name=_(u'description'))
+
+ def __unicode__(self):
+ return self.label
+
+ @property
+ def workflow_nodes(self):
+ return self.workflownode_set
+
+ def get_nodes(self):
+ return [workflow_node.node for workflow_node in self.workflownode_set.all()]
+
+ def add_node(self, node):
+ workflow_node = WorkflowNode(
+ workflow=self,
+ node=node)
+ workflow_node.save()
+ return workflow_node
+
+ def save(self, *args, **kwargs):
+ is_new = not self.pk
+ result = super(Workflow, self).save(*args, **kwargs)
+
+ if is_new:
+ # Instanciate a new start node
+ start_node = Start()
+ start_node.save()
+
+ # Set the start node a
+ workflow_node = self.add_node(node=start_node)
+
+ self.initial_node = workflow_node
+ self.save()
+
+ return result
+
+ class Meta:
+ verbose_name = _(u'workflow')
+ verbose_name_plural = _(u'workflows')
+
+
+class Node(models.Model):
+ """
+ Must provide:
+ possible_next_nodes()
+ Arguments: None
+ Returns:
+ List of possible nodes after this one executes
+
+ choices()
+ Arguments: workflow
+ Returns:
+ {
+ 'next_node': workflow.nodes.all()
+ }
+
+ execute()
+ Arguments: workflow_instance
+ Returns: next_node
+ """
+
+ label = models.CharField(max_length=128, verbose_name=_(u'label'))
+ description = models.TextField(blank=True, verbose_name=_(u'description'))
+
+ def __unicode__(self):
+ return self.label
+
+ class Meta:
+ verbose_name = _(u'state')
+ verbose_name_plural = _(u'states')
+ abstract = True
+
+
+class Start(Node):
+ """
+ The node with which all workflows start
+ """
+ content_type = models.ForeignKey(ContentType, null=True)
+ object_id = models.PositiveIntegerField(null=True)
+ next_node = generic.GenericForeignKey(ct_field='content_type', fk_field='object_id')
+
+ def __unicode__(self):
+ return ugettext(u'Start')
+
+ def possible_next_nodes(self):
+ return self.next_node
+
+ def choices(self, workflow):
+ return {
+ 'next_node': workflow.nodes.all()
+ }
+
+ def execute(self, workflow_instance):
+ return self.next_node
+
+ class Meta(Node.Meta):
+ verbose_name = _(u'start node')
+ verbose_name_plural = _(u'start nodes')
+
+
+class End(Node):
+ class Meta(Node.Meta):
+ verbose_name = _(u'end node')
+ verbose_name_plural = _(u'end nodes')
+
+
+'''
+class Sequence(Node):
+ """
+ A node that is enabled after the completion of a preceding node in the same workflow
+ """
+ content_type = models.ForeignKey(ContentType)
+ object_id = models.PositiveIntegerField()
+ next_node = generic.GenericForeignKey(ct_field='content_type', fk_field='object_id')
+ #node_type = NODE_TYPE_TASK
+
+ def execute(self):
+ return self.next_node
+
+ class Meta(Node.Meta):
+ verbose_name = _(u'task')
+ verbose_name_plural = _(u'tasks')
+'''
+
+class State(models.Model):
+ label = models.CharField(max_length=128, verbose_name=_(u'label'))
+ description = models.TextField(blank=True, verbose_name=_(u'description'))
+
+ def __unicode__(self):
+ return self.label
+
+ class Meta:
+ verbose_name = _(u'state')
+ verbose_name_plural = _(u'states')
+ ordering = ('label',)
+
+
+class WorkflowState(models.Model):
+ """
+ List of possible states the object of this worflow could be in
+ """
+ workflow = models.ForeignKey(Workflow, verbose_name=_(u'workflow'))
+ state = models.ForeignKey(State, verbose_name=_(u'state'))
+ description = models.TextField(blank=True, verbose_name=_(u'description'))
+
+ def __unicode__(self):
+ return unicode(self.state)
+
+ class Meta:
+ unique_together = ('workflow', 'state')
+ verbose_name = _(u'workflow state')
+ verbose_name_plural = _(u'workflows states')
+
+# TODO: Reduntant - remove
+class WorkflowNode(models.Model):
+ workflow = models.ForeignKey(Workflow, verbose_name=_(u'workflow'))
+ content_type = models.ForeignKey(ContentType)#, limit_choices_to={'model__in': ('model1', 'model2')})#, related_name='workflow_state_ability_object')#, blank=True, null=True)
+ object_id = models.PositiveIntegerField()#blank=True, null=True)
+ node = generic.GenericForeignKey(ct_field='content_type', fk_field='object_id')
+ description = models.TextField(blank=True, verbose_name=_(u'description'))
+
+ def __unicode__(self):
+ return unicode(self.node)
+
+ #def save(self, *args, **kwargs):
+ # if not issubclass(
+ # return super(WorkflowNode, self).save(*args, **kwargs)
+
+ class Meta:
+ #unique_together = ('workflow', 'state')
+ verbose_name = _(u'workflow node')
+ verbose_name_plural = _(u'workflows nodes')
+
+"""
+class WorkflowTaskAbilityGrant(models.Model):
+ workflow_state = models.ForeignKey(WorkflowState, related_name='workflow_state_ability', verbose_name=_(u'workflow state'))
+ content_type = models.ForeignKey(ContentType, related_name='workflow_state_ability_object')#, blank=True, null=True)
+ object_id = models.PositiveIntegerField()#blank=True, null=True)
+ content_object = generic.GenericForeignKey(ct_field='content_type', fk_field='object_id')
+
+ def __unicode__(self):
+ return unicode(self.content_object)
+
+ class Meta:
+ verbose_name = _(u'workflow state ability grant')
+ verbose_name_plural = _(u'workflows states ability grant')
+
+#TODO: WorkflowStateACLEntry
+#WorkflowState
+#Actor
+#Object
+#Permission (s)
+
+
+#TODO: WorkflowStateAlarm
+#label
+#timedate
+#interval
+
+
+class WorkflowStateTransition(models.Model):
+ workflow_state_source = models.ForeignKey(WorkflowState, verbose_name=_(u'workflow state source'))
+ transition = models.ForeignKey(Transition, related_name='workflow_state_transition', verbose_name=_(u'transition'))
+ workflow_state_destination = models.ForeignKey(WorkflowState, related_name='workflow_state_transition_destination', verbose_name=_(u'workflow state destination'))
+ description = models.TextField(blank=True, verbose_name=_(u'description'))
+
+ def __unicode__(self):
+ return unicode(self.transition)
+
+ class Meta:
+ verbose_name = _(u'workflow state transition')
+ verbose_name_plural = _(u'workflows states transitions')
+
+
+class WorkflowStateTransitionAbility(models.Model):
+ attribute_comparison_operand = models.CharField(max_length=8, default=OPERAND_AND, choices=OPERAND_CHOICES, verbose_name=_(u'operand'))
+ negate = models.BooleanField(verbose_name=_(u'negate'), help_text=_(u'Inverts the attribute comparison.'))
+ ability = models.ForeignKey(Ability, related_name='workflow_state_transition_ability', verbose_name=_(u'ability'))
+
+ description = models.TextField(blank=True, verbose_name=_(u'description'))
+
+ def __unicode__(self):
+ return unicode(self.ability)
+
+ class Meta:
+ verbose_name = _(u'transition')
+ verbose_name_plural = _(u'transitions')
+
+"""
+class WorkflowInstance(models.Model):
+ workflow = models.ForeignKey(Workflow, verbose_name=_(u'workflow'))
+ content_type = models.ForeignKey(ContentType, verbose_name=_(u'Content type'), related_name='workflow_instance_object')#, blank=True, null=True)
+ object_id = models.PositiveIntegerField()#blank=True, null=True)
+ content_object = generic.GenericForeignKey(ct_field='content_type', fk_field='object_id')
+
+ def __unicode__(self):
+ return unicode(self.content_object)
+
+ @property
+ def active_nodes(self):
+ return self.workflowinstanceactivenode_set
+
+ def set_active_state(self, state):
+ active_state = self.get_active_state()
+ if active_state:
+ active_state.delete()
+
+ # Trigger an exception if the state argument if not allowed for this workflow
+ state = WorkflowState.objects.get(workflow=self.workflow, state=state).state
+
+ self.workflowinstanceactivestate_set.create(
+ workflow_instance = self,
+ state = state
+ )
+
+ def get_active_state(self):
+ try:
+ return self.workflowinstanceactivestate_set.get().state
+ except WorkflowInstanceActiveState.DoesNotExist:
+ return None
+
+ active_state = property(get_active_state, set_active_state)
+
+ class Meta:
+ verbose_name = _(u'workflow instance')
+ verbose_name_plural = _(u'workflow instances')
+
+ # unique_together = ('content_type', 'object_id', 'workflow')
+
+
+class WorkflowInstanceActiveNode(models.Model):
+ workflow_instance = models.ForeignKey(WorkflowInstance, verbose_name=_(u'workflow instance'))
+ workflow_node = models.ForeignKey(WorkflowNode, verbose_name=_(u'workflow node'))
+
+ class Meta:
+ verbose_name = _(u'workflow instance active node')
+ verbose_name_plural = _(u'workflow instances active nodes')
+
+
+class WorkflowInstanceState(models.Model):
+ """
+ This class holds the active state for the workflow instance
+ """
+ workflow_instance = models.ForeignKey(WorkflowInstance, verbose_name=_(u'workflow instance'))
+ state = models.ForeignKey(State, null=True, verbose_name=_(u'state'))
+
+ class Meta:
+ unique_together = ('workflow_instance', 'state')
+ verbose_name = _(u'workflow instance active state')
+ verbose_name_plural = _(u'workflow instances active states')
+
+
+# TODO: WorkflowInstanceActiveNodeHistory
+# TODO: WorkflowInstanceActiveStateHistory
diff --git a/apps/workflows/permissions.py b/apps/workflows/permissions.py
new file mode 100644
index 0000000000..eaece09a8a
--- /dev/null
+++ b/apps/workflows/permissions.py
@@ -0,0 +1,25 @@
+from __future__ import absolute_import
+
+from django.utils.translation import ugettext_lazy as _
+
+from permissions.models import PermissionNamespace, Permission
+
+namespace = PermissionNamespace('workflows', _(u'Workflows'))
+
+PERMISSION_WORKFLOW_SETUP_VIEW = Permission.objects.register(namespace, 'workflow_setup_view', _(u'View existing workflow templates'))
+PERMISSION_WORKFLOW_SETUP_CREATE = Permission.objects.register(namespace, 'workflow_setup_create', _(u'Create new workflow templates'))
+PERMISSION_WORKFLOW_SETUP_EDIT = Permission.objects.register(namespace, 'workflow_setup_edit', _(u'Edit existing workflow templates'))
+PERMISSION_WORKFLOW_SETUP_DELETE = Permission.objects.register(namespace, 'workflow_setup_delete', _(u'Delete existing workflow templates'))
+
+PERMISSION_WORKFLOW_RUN = Permission.objects.register(namespace, 'workflow_setup_run', _(u'Execute a workflow'))
+
+#PERMISSION_STATE_SETUP_VIEW = Permission.objects.register(namespace, 'state_setup_view', _(u'View existing states'))
+#PERMISSION_STATE_SETUP_CREATE = Permission.objects.register(namespace, 'state_setup_create', _(u'Create new state templates'))
+#PERMISSION_STATE_SETUP_EDIT = Permission.objects.register(namespace, 'state_setup_edit', _(u'Edit existing state templates'))
+#PERMISSION_STATE_SETUP_DELETE = Permission.objects.register(namespace, 'state_setup_delete', _(u'Delete existing state templates'))
+
+#PERMISSION_TRANSITION_SETUP_VIEW = Permission.objects.register(namespace, 'transition_setup_view', _(u'View existing transition templates'))
+#PERMISSION_TRANSITION_SETUP_CREATE = Permission.objects.register(namespace, 'transition_setup_create', _(u'Create new transition templates'))
+#PERMISSION_TRANSITION_SETUP_EDIT = Permission.objects.register(namespace, 'transition_setup_edit', _(u'Edit existing transition templates'))
+#PERMISSION_TRANSITION_SETUP_DELETE = Permission.objects.register(namespace, 'transition_setup_delete', _(u'Delete existing transition templates'))
+
diff --git a/apps/workflows/static/images/icons/chart_organisation.png b/apps/workflows/static/images/icons/chart_organisation.png
new file mode 100644
index 0000000000..5d408a347e
Binary files /dev/null and b/apps/workflows/static/images/icons/chart_organisation.png differ
diff --git a/apps/workflows/static/images/icons/chart_organisation_delete.png b/apps/workflows/static/images/icons/chart_organisation_delete.png
new file mode 100644
index 0000000000..78cc3b9d17
Binary files /dev/null and b/apps/workflows/static/images/icons/chart_organisation_delete.png differ
diff --git a/apps/workflows/static/images/icons/transmit_delete.png b/apps/workflows/static/images/icons/transmit_delete.png
new file mode 100644
index 0000000000..f34531ca78
Binary files /dev/null and b/apps/workflows/static/images/icons/transmit_delete.png differ
diff --git a/apps/workflows/tests.py b/apps/workflows/tests.py
new file mode 100644
index 0000000000..501deb776c
--- /dev/null
+++ b/apps/workflows/tests.py
@@ -0,0 +1,16 @@
+"""
+This file demonstrates writing tests using the unittest module. These will pass
+when you run "manage.py test".
+
+Replace this with more appropriate tests for your application.
+"""
+
+from django.test import TestCase
+
+
+class SimpleTest(TestCase):
+ def test_basic_addition(self):
+ """
+ Tests that 1 + 1 always equals 2.
+ """
+ self.assertEqual(1 + 1, 2)
diff --git a/apps/workflows/urls.py b/apps/workflows/urls.py
new file mode 100644
index 0000000000..29b54ab972
--- /dev/null
+++ b/apps/workflows/urls.py
@@ -0,0 +1,21 @@
+from django.conf.urls.defaults import patterns, url
+
+urlpatterns = patterns('workflows.views',
+ url(r'^setup/workflow/list/$', 'setup_workflow_list', (), 'setup_workflow_list'),
+ url(r'^setup/workflow/create/$', 'setup_workflow_create', (), 'setup_workflow_create'),
+ url(r'^setup/workflow/(?P\d+)/edit/$', 'setup_workflow_edit', (), 'setup_workflow_edit'),
+ url(r'^setup/workflow/(?P\d+)/delete/$', 'setup_workflow_delete', (), 'setup_workflow_delete'),
+
+ url(r'^setup/workflow/(?P\d+)/state/list/$', 'setup_workflow_states_list', (), 'setup_workflow_states_list'),
+ url(r'^setup/workflow/(?P\d+)/state/add/$', 'setup_workflow_state_add', (), 'setup_workflow_state_add'),
+ url(r'^setup/workflow/state/(?P\d+)/edit/$', 'setup_workflow_state_edit', (), 'setup_workflow_state_edit'),
+ url(r'^setup/workflow/state/(?P\d+)/remove/$', 'setup_workflow_state_remove', (), 'setup_workflow_state_remove'),
+
+ url(r'^setup/state/list/$', 'setup_state_list', (), 'setup_state_list'),
+ url(r'^setup/state/create/$', 'setup_state_create', (), 'setup_state_create'),
+ url(r'^setup/state/(?P\d+)/edit/$', 'setup_state_edit', (), 'setup_state_edit'),
+ url(r'^setup/state/(?P\d+)/delete/$', 'setup_state_delete', (), 'setup_state_delete'),
+
+ url(r'^setup/workflow/(?P\d+)/node/list/$', 'setup_workflow_node_list', (), 'setup_workflow_node_list'),
+ url(r'^setup/workflow/node/(?P\d+)/edit/$', 'setup_workflow_node_edit', (), 'setup_workflow_node_edit'),
+)
diff --git a/apps/workflows/views.py b/apps/workflows/views.py
new file mode 100644
index 0000000000..33c5a778b9
--- /dev/null
+++ b/apps/workflows/views.py
@@ -0,0 +1,689 @@
+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.utils.translation import ugettext
+from django.utils.safestring import mark_safe
+from django.conf import settings
+from django.core.exceptions import PermissionDenied
+
+from permissions.models import Permission
+from common.utils import encapsulate
+#from common.widgets import two_state_template
+#from acls.models import AccessEntry
+
+from .models import Workflow, State, WorkflowState, WorkflowNode
+from .forms import (WorkflowSetupForm, StateSetupForm,
+ WorkflowStateSetupForm, WorkflowNodeSetupForm)
+from .permissions import (PERMISSION_WORKFLOW_SETUP_VIEW,
+ PERMISSION_WORKFLOW_SETUP_CREATE, PERMISSION_WORKFLOW_SETUP_EDIT,
+ PERMISSION_WORKFLOW_SETUP_DELETE, PERMISSION_STATE_SETUP_VIEW,
+ PERMISSION_STATE_SETUP_CREATE, PERMISSION_STATE_SETUP_EDIT,
+ PERMISSION_STATE_SETUP_DELETE)
+
+logger = logging.getLogger(__name__)
+
+
+# Setup views
+def setup_workflow_list(request):
+ Permission.objects.check_permissions(request.user, [PERMISSION_WORKFLOW_SETUP_VIEW])
+
+ context = {
+ 'object_list': Workflow.objects.all(),
+ 'title': _(u'workflows'),
+ 'hide_link': True,
+ 'extra_columns': [
+ {'name': _(u'Initial state'), 'attribute': encapsulate(lambda workflow: workflow.initial_state or _(u'None'))},
+ ],
+ 'list_object_variable_name': 'workflow',
+ }
+
+ return render_to_response('generic_list.html', context,
+ context_instance=RequestContext(request))
+
+
+def setup_workflow_create(request):
+ Permission.objects.check_permissions(request.user, [PERMISSION_WORKFLOW_SETUP_CREATE])
+ redirect_url = reverse('setup_workflow_list')
+
+ if request.method == 'POST':
+ form = WorkflowSetupForm(request.POST)
+ if form.is_valid():
+ workflow = form.save()
+ messages.success(request, _(u'Workflow created succesfully.'))
+ return HttpResponseRedirect(redirect_url)
+ else:
+ form = WorkflowSetupForm()
+
+ return render_to_response('generic_form.html', {
+ 'title': _(u'create workflow'),
+ 'form': form,
+ },
+ context_instance=RequestContext(request))
+
+
+def setup_workflow_edit(request, workflow_pk):
+ Permission.objects.check_permissions(request.user, [PERMISSION_WORKFLOW_SETUP_EDIT])
+ workflow = get_object_or_404(Workflow, pk=workflow_pk)
+
+ if request.method == 'POST':
+ form = WorkflowSetupForm(instance=workflow, data=request.POST)
+ if form.is_valid():
+ form.save()
+ messages.success(request, _(u'Workflow "%s" updated successfully.') % workflow)
+ return HttpResponseRedirect(reverse('setup_workflow_list'))
+ else:
+ form = WorkflowSetupForm(instance=workflow)
+
+ return render_to_response('generic_form.html', {
+ 'title': _(u'edit workflow: %s') % workflow,
+ 'form': form,
+ 'workflow': workflow,
+ 'navigation_object_list': [
+ {'object': 'workflow', 'name': _(u'workflow')},
+ ],
+ },
+ context_instance=RequestContext(request))
+
+
+def setup_workflow_delete(request, workflow_pk=None, workflow_pk_list=None):
+ post_action_redirect = None
+
+ if workflow_pk:
+ workflows = [get_object_or_404(Workflow, pk=workflow_pk)]
+ post_action_redirect = reverse('setup_workflow_list')
+ elif workflow_pk_list:
+ workflows = [get_object_or_404(Workflow, pk=workflow_pk) for workflow_pk in workflow_pk_list.split(',')]
+ else:
+ messages.error(request, _(u'Must provide at least one workflow.'))
+ return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
+
+ try:
+ Permission.objects.check_permissions(request.user, [PERMISSION_WORKFLOW_SETUP_DELETE])
+ except PermissionDenied:
+ workflows = AccessEntry.objects.filter_objects_by_access(PERMISSION_WORKFLOW_SETUP_DELETE, request.user, workflows)
+
+ previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', '/')))
+ next = request.POST.get('next', request.GET.get('next', post_action_redirect if post_action_redirect else request.META.get('HTTP_REFERER', '/')))
+
+ if request.method == 'POST':
+ for workflow in workflows:
+ try:
+ workflow.delete()
+ messages.success(request, _(u'Workflows "%s" deleted successfully.') % workflow)
+ except Exception, e:
+ messages.error(request, _(u'Error deleting workflow "%(workflow)s": %(error)s') % {
+ 'workflow': workflow, 'error': e
+ })
+
+ return HttpResponseRedirect(next)
+
+ context = {
+ 'delete_view': True,
+ 'previous': previous,
+ 'next': next,
+ 'form_icon': u'chart_organisation_delete.png',
+ 'navigation_object_list': [
+ {'object': 'workflow', 'name': _(u'workflow')},
+ ],
+ }
+ if len(workflows) == 1:
+ context['workflow'] = workflows[0]
+ context['title'] = _(u'Are you sure you wish to delete the workflow: %s?') % ', '.join([unicode(d) for d in workflows])
+ context['message'] = _('Will be removed from all documents.')
+ elif len(workflows) > 1:
+ context['title'] = _(u'Are you sure you wish to delete the workflows: %s?') % ', '.join([unicode(d) for d in workflows])
+ context['message'] = _('Will be removed from all documents.')
+
+ return render_to_response('generic_confirm.html', context,
+ context_instance=RequestContext(request))
+
+
+def setup_workflow_states_list(request, workflow_pk):
+ Permission.objects.check_permissions(request.user, [PERMISSION_WORKFLOW_SETUP_EDIT])
+ workflow = get_object_or_404(Workflow, pk=workflow_pk)
+
+ context = {
+ 'object_list': workflow.workflowstate_set.all(),
+ 'title': _(u'states for workflow: %s') % workflow,
+ 'hide_link': True,
+ 'workflow': workflow,
+ 'navigation_object_list': [
+ {'object': 'workflow', 'name': _(u'workflow')},
+ ],
+ 'list_object_variable_name': 'workflow_state',
+ }
+
+ return render_to_response('generic_list.html', context,
+ context_instance=RequestContext(request))
+
+
+def setup_workflow_state_add(request, workflow_pk):
+ Permission.objects.check_permissions(request.user, [PERMISSION_WORKFLOW_SETUP_EDIT])
+ redirect_url = reverse('setup_workflow_states_list', args=[workflow_pk])
+ workflow = get_object_or_404(Workflow, pk=workflow_pk)
+
+ if request.method == 'POST':
+ form = WorkflowStateSetupForm(workflow=workflow, data=request.POST)
+ if form.is_valid():
+ state = form.save()
+ messages.success(request, _(u'worflow state created succesfully.'))
+ return HttpResponseRedirect(redirect_url)
+ else:
+ form = WorkflowStateSetupForm(workflow=workflow)
+
+ return render_to_response('generic_form.html', {
+ 'title': _(u'add worflow state'),
+ 'form': form,
+ 'workflow': workflow,
+ 'navigation_object_list': [
+ {'object': 'workflow', 'name': _(u'workflow')},
+ ],
+ }, context_instance=RequestContext(request))
+
+
+def setup_workflow_state_edit(request, workflow_state_pk):
+ Permission.objects.check_permissions(request.user, [PERMISSION_WORKFLOW_SETUP_EDIT])
+ workflow_state = get_object_or_404(WorkflowState, pk=workflow_state_pk)
+ redirect_url = reverse('setup_workflow_states_list', args=[workflow_state.workflow.pk])
+
+ if request.method == 'POST':
+ form = WorkflowStateSetupForm(workflow=workflow_state.workflow, instance=workflow_state, data=request.POST)
+ if form.is_valid():
+ state = form.save()
+ messages.success(request, _(u'worflow state edited succesfully.'))
+ return HttpResponseRedirect(redirect_url)
+ else:
+ form = WorkflowStateSetupForm(workflow=workflow_state.workflow, instance=workflow_state)
+
+ return render_to_response('generic_form.html', {
+ 'title': _(u'edit worflow state: %s') % workflow_state,
+ 'form': form,
+ 'workflow': workflow_state.workflow,
+ 'workflow_state': workflow_state,
+ 'navigation_object_list': [
+ {'object': 'workflow', 'name': _(u'workflow')},
+ {'object': 'workflow_state', 'name': _(u'workflow state')}
+ ],
+ }, context_instance=RequestContext(request))
+
+
+def setup_workflow_state_remove(request, workflow_state_pk=None, workflow_state_pk_list=None):
+ post_action_redirect = None
+
+ if workflow_state_pk:
+ workflow_states = [get_object_or_404(WorkflowState, pk=workflow_state_pk)]
+ post_action_redirect = reverse('setup_workflow_states_list', args=[workflow_states[0].workflow.pk])
+ elif workflow_state_pk_list:
+ workflow_states = [get_object_or_404(WorkflowState, pk=workflow_state_pk) for workflow_state_pk in workflow_state_pk_list.split(',')]
+ else:
+ messages.error(request, _(u'Must provide at least one workflow state.'))
+ return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
+
+ try:
+ Permission.objects.check_permissions(request.user, [PERMISSION_WORKFLOW_SETUP_EDIT])
+ except PermissionDenied:
+ workflow_states = AccessEntry.objects.filter_objects_by_access(PERMISSION_WORKFLOW_SETUP_EDIT, request.user, workflow_states)
+
+ previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', '/')))
+ next = request.POST.get('next', request.GET.get('next', post_action_redirect if post_action_redirect else request.META.get('HTTP_REFERER', '/')))
+
+ if request.method == 'POST':
+ for workflow_state in workflow_states:
+ try:
+ workflow_state.delete()
+ messages.success(request, _(u'Workflow states "%s" removed successfully.') % workflow_state)
+ except Exception, e:
+ messages.error(request, _(u'Error removing workflow state "%(workflow_state)s": %(error)s') % {
+ 'workflow_state': workflow_state, 'error': e
+ })
+
+ return HttpResponseRedirect(next)
+
+ context = {
+ 'object_name': _(u'workflow state'),
+ 'delete_view': True,
+ 'previous': previous,
+ 'next': next,
+ 'form_icon': u'transmit_delete.png',
+ 'workflow': workflow_states[0].workflow,
+ 'navigation_object_list': [
+ {'object': 'workflow', 'name': _(u'workflow')},
+ {'object': 'workflow_state', 'name': _(u'workflow state')}
+ ],
+ }
+ if len(workflow_states) == 1:
+ context['title'] = _(u'Are you sure you wish to remove the workflow state: %s?') % ', '.join([unicode(d) for d in workflow_states])
+ context['workflow_state'] = workflow_states[0]
+ elif len(states) > 1:
+ context['title'] = _(u'Are you sure you wish to remove the workflow states: %s?') % ', '.join([unicode(d) for d in workflow_states])
+
+ return render_to_response('generic_confirm.html', context,
+ context_instance=RequestContext(request))
+
+
+# Nodes
+def setup_workflow_node_list(request, workflow_pk):
+ Permission.objects.check_permissions(request.user, [PERMISSION_WORKFLOW_SETUP_EDIT])
+ workflow = get_object_or_404(Workflow, pk=workflow_pk)
+
+ context = {
+ 'object_list': workflow.workflow_nodes.all(),
+ 'extra_columns': [
+ {'name': _(u'Posible next nodes'), 'attribute': encapsulate(lambda workflow_node: workflow_node.node.possible_next_nodes() or _(u'None'))},
+ ],
+ 'title': _(u'nodes of workflow: %s') % workflow,
+ 'hide_link': True,
+ 'workflow': workflow,
+ 'navigation_object_list': [
+ {'object': 'workflow', 'name': _(u'workflow')},
+ ],
+ 'list_object_variable_name': 'workflow_node',
+ }
+
+ return render_to_response('generic_list.html', context,
+ context_instance=RequestContext(request))
+
+
+def setup_workflow_node_edit(request, workflow_node_pk):
+ Permission.objects.check_permissions(request.user, [PERMISSION_WORKFLOW_SETUP_EDIT])
+ workflow_node = get_object_or_404(WorkflowNode, pk=workflow_node_pk)
+ redirect_url = reverse('setup_workflow_node_list', args=[workflow_node.workflow.pk])
+
+ if request.method == 'POST':
+ form = WorkflowNodeSetupForm(workflow=workflow_node.workflow, instance=workflow_node, data=request.POST)
+ if form.is_valid():
+ state = form.save()
+ messages.success(request, _(u'worflow node edited succesfully.'))
+ return HttpResponseRedirect(redirect_url)
+ else:
+ form = WorkflowNodeSetupForm(workflow=workflow_node.workflow, instance=workflow_node)
+
+ return render_to_response('generic_form.html', {
+ 'title': _(u'edit worflow node: %s') % workflow_node,
+ 'form': form,
+ 'workflow': workflow_node.workflow,
+ 'workflow_node': workflow_node,
+ 'navigation_object_list': [
+ {'object': 'workflow', 'name': _(u'workflow')},
+ {'object': 'workflow_node', 'name': _(u'workflow node')}
+ ],
+ }, context_instance=RequestContext(request))
+"""
+def setup_workflow_transition_list(request, state_workflow_pk):
+ Permission.objects.check_permissions(request.user, [PERMISSION_WORKFLOW_SETUP_EDIT])
+ workflow = get_object_or_404(Workflow, pk=workflow_pk)
+
+ context = {
+ 'object_list': workflow.workflowstate_set.all(),
+ 'title': _(u'states for workflow: %s') % workflow,
+ 'hide_link': True,
+ 'workflow': workflow,
+ 'navigation_object_list': [
+ {'object': 'workflow', 'name': _(u'workflow')},
+ ],
+ 'list_object_variable_name': 'workflow_state',
+ }
+
+ return render_to_response('generic_list.html', context,
+ context_instance=RequestContext(request))
+"""
+# States
+def setup_state_list(request):
+ Permission.objects.check_permissions(request.user, [PERMISSION_STATE_SETUP_VIEW])
+
+ context = {
+ 'object_list': State.objects.all(),
+ 'title': _(u'states'),
+ 'hide_link': True,
+ }
+
+ return render_to_response('generic_list.html', context,
+ context_instance=RequestContext(request))
+
+
+def setup_state_create(request):
+ Permission.objects.check_permissions(request.user, [PERMISSION_STATE_SETUP_CREATE])
+ redirect_url = reverse('setup_state_list')
+
+ if request.method == 'POST':
+ form = StateSetupForm(request.POST)
+ if form.is_valid():
+ state = form.save()
+ messages.success(request, _(u'State created succesfully.'))
+ return HttpResponseRedirect(redirect_url)
+ else:
+ form = StateSetupForm()
+
+ return render_to_response('generic_form.html', {
+ 'title': _(u'create state'),
+ 'form': form,
+ },
+ context_instance=RequestContext(request))
+
+
+def setup_state_edit(request, state_pk):
+ Permission.objects.check_permissions(request.user, [PERMISSION_STATE_SETUP_EDIT])
+ state = get_object_or_404(State, pk=state_pk)
+
+ if request.method == 'POST':
+ form = StateSetupForm(instance=state, data=request.POST)
+ if form.is_valid():
+ form.save()
+ messages.success(request, _(u'State "%s" updated successfully.') % state)
+ return HttpResponseRedirect(reverse('setup_state_list'))
+ else:
+ form = StateSetupForm(instance=state)
+
+ return render_to_response('generic_form.html', {
+ 'title': _(u'edit state: %s') % state,
+ 'form': form,
+ 'object': state,
+ 'object_name': _(u'state'),
+ },
+ context_instance=RequestContext(request))
+
+
+def setup_state_delete(request, state_pk=None, state_pk_list=None):
+ post_action_redirect = None
+
+ if state_pk:
+ states = [get_object_or_404(State, pk=state_pk)]
+ post_action_redirect = reverse('setup_state_list')
+ elif state_pk_list:
+ states = [get_object_or_404(State, pk=state_pk) for state_pk in state_pk_list.split(',')]
+ else:
+ messages.error(request, _(u'Must provide at least one state.'))
+ return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
+
+ try:
+ Permission.objects.check_permissions(request.user, [PERMISSION_STATE_SETUP_DELETE])
+ except PermissionDenied:
+ states = AccessEntry.objects.filter_objects_by_access(PERMISSION_STATE_SETUP_DELETE, request.user, states)
+
+ previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', '/')))
+ next = request.POST.get('next', request.GET.get('next', post_action_redirect if post_action_redirect else request.META.get('HTTP_REFERER', '/')))
+
+ if request.method == 'POST':
+ for state in states:
+ try:
+ state.delete()
+ messages.success(request, _(u'States "%s" deleted successfully.') % state)
+ except Exception, e:
+ messages.error(request, _(u'Error deleting state "%(state)s": %(error)s') % {
+ 'state': state, 'error': e
+ })
+
+ return HttpResponseRedirect(next)
+
+ context = {
+ 'object_name': _(u'state'),
+ 'delete_view': True,
+ 'previous': previous,
+ 'next': next,
+ 'form_icon': u'transmit_delete.png',
+ }
+ if len(states) == 1:
+ context['object'] = states[0]
+ context['title'] = _(u'Are you sure you wish to delete the state: %s?') % ', '.join([unicode(d) for d in states])
+ context['message'] = _('Will be removed from all documents.')
+ elif len(states) > 1:
+ context['title'] = _(u'Are you sure you wish to delete the states: %s?') % ', '.join([unicode(d) for d in states])
+ context['message'] = _('Will be removed from all documents.')
+
+ return render_to_response('generic_confirm.html', context,
+ context_instance=RequestContext(request))
+
+"""
+# Transitions
+def setup_transition_list(request):
+ Permission.objects.check_permissions(request.user, [PERMISSION_TRANSITION_SETUP_VIEW])
+
+ context = {
+ 'object_list': Transition.objects.all(),
+ 'title': _(u'transitions'),
+ 'hide_link': True,
+ 'list_object_variable_name': 'transition',
+ }
+
+ return render_to_response('generic_list.html', context,
+ context_instance=RequestContext(request))
+
+
+def setup_transition_create(request):
+ Permission.objects.check_permissions(request.user, [PERMISSION_TRANSITION_SETUP_CREATE])
+ redirect_url = reverse('setup_transition_list')
+
+ if request.method == 'POST':
+ form = TransitionSetupForm(request.POST)
+ if form.is_valid():
+ transition = form.save()
+ messages.success(request, _(u'Transition created succesfully.'))
+ return HttpResponseRedirect(redirect_url)
+ else:
+ form = TransitionSetupForm()
+
+ return render_to_response('generic_form.html', {
+ 'title': _(u'create transition'),
+ 'form': form,
+ },
+ context_instance=RequestContext(request))
+
+
+def setup_transition_edit(request, transition_pk):
+ Permission.objects.check_permissions(request.user, [PERMISSION_TRANSITION_SETUP_EDIT])
+ transition = get_object_or_404(Transition, pk=transition_pk)
+
+ if request.method == 'POST':
+ form = TransitionSetupForm(instance=transition, data=request.POST)
+ if form.is_valid():
+ form.save()
+ messages.success(request, _(u'Transition "%s" updated successfully.') % transition)
+ return HttpResponseRedirect(reverse('setup_transition_list'))
+ else:
+ form = TransitionSetupForm(instance=transition)
+
+ return render_to_response('generic_form.html', {
+ 'title': _(u'edit transition: %s') % transition,
+ 'form': form,
+ 'transition': transition,
+ 'navigation_object_list': [
+ {'object': 'transition', 'name': _(u'transition')},
+ ],
+ },
+ context_instance=RequestContext(request))
+
+
+def setup_transition_delete(request, transition_pk=None, transition_pk_list=None):
+ post_action_redirect = None
+
+ if transition_pk:
+ transitions = [get_object_or_404(Transition, pk=transition_pk)]
+ post_action_redirect = reverse('setup_transition_list')
+ elif transition_pk_list:
+ transitions = [get_object_or_404(Transition, pk=transition_pk) for transition_pk in transition_pk_list.split(',')]
+ else:
+ messages.error(request, _(u'Must provide at least one transition.'))
+ return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
+
+ try:
+ Permission.objects.check_permissions(request.user, [PERMISSION_TRANSITION_SETUP_DELETE])
+ except PermissionDenied:
+ transitions = AccessEntry.objects.filter_objects_by_access(PERMISSION_TRANSITION_SETUP_DELETE, request.user, transitions)
+
+ previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', '/')))
+ next = request.POST.get('next', request.GET.get('next', post_action_redirect if post_action_redirect else request.META.get('HTTP_REFERER', '/')))
+
+ if request.method == 'POST':
+ for transition in transitions:
+ try:
+ transition.delete()
+ messages.success(request, _(u'Transitions "%s" deleted successfully.') % transition)
+ except Exception, e:
+ messages.error(request, _(u'Error deleting transition "%(transition)s": %(error)s') % {
+ 'transition': transition, 'error': e
+ })
+
+ return HttpResponseRedirect(next)
+
+ context = {
+ 'object_name': _(u'transition'),
+ 'delete_view': True,
+ 'previous': previous,
+ 'next': next,
+ 'form_icon': u'transmit_delete.png',
+ 'navigation_object_list': [
+ {'object': 'transition', 'name': _(u'transition')},
+ ],
+ }
+ if len(transitions) == 1:
+ context['transition'] = transitions[0]
+ context['title'] = _(u'Are you sure you wish to delete the transition: %s?') % ', '.join([unicode(d) for d in transitions])
+ context['message'] = _('Will be removed from all documents.')
+ elif len(transitions) > 1:
+ context['title'] = _(u'Are you sure you wish to delete the transitions: %s?') % ', '.join([unicode(d) for d in transitions])
+ context['message'] = _('Will be removed from all documents.')
+
+ return render_to_response('generic_confirm.html', context,
+ context_instance=RequestContext(request))
+"""
+"""
+# State transitions
+def setup_workflow_state_transitions_list(request, workflow_state_pk):
+ Permission.objects.check_permissions(request.user, [PERMISSION_WORKFLOW_SETUP_EDIT])
+ workflow_state = get_object_or_404(WorkflowState, pk=workflow_state_pk)
+
+ context = {
+ 'object_list': workflow_state.transitions.all(),
+ 'title': _(u'transitions for workflow state: %s') % workflow_state,
+ 'hide_link': True,
+ 'workflow_state': workflow_state,
+ 'workflow': workflow_state.workflow,
+ 'navigation_object_list': [
+ {'object': 'workflow', 'name': _(u'workflow')},
+ {'object': 'workflow_state', 'name': _(u'state')},
+ ],
+ 'list_object_variable_name': 'state_transition',
+ 'extra_columns': [
+ {'name': _(u'Destination'), 'attribute': 'workflow_state_destination'},
+ ],
+ }
+
+ return render_to_response('generic_list.html', context,
+ context_instance=RequestContext(request))
+
+
+def setup_workflow_state_transition_add(request, workflow_state_pk):
+ Permission.objects.check_permissions(request.user, [PERMISSION_WORKFLOW_SETUP_EDIT])
+ workflow_state = get_object_or_404(WorkflowState, pk=workflow_state_pk)
+ redirect_url = reverse('setup_workflow_state_transitions_list', args=[workflow_state_pk])
+
+ if request.method == 'POST':
+ form = WorkflowStateTransitionSetupForm(workflow_state=workflow_state, data=request.POST)
+ if form.is_valid():
+ workflow_state_transition = form.save()
+ messages.success(request, _(u'worflow state transition created succesfully.'))
+ return HttpResponseRedirect(redirect_url)
+ else:
+ form = WorkflowStateTransitionSetupForm(workflow_state=workflow_state)
+
+ return render_to_response('generic_form.html', {
+ 'title': _(u'add transition to worflow state: %s') % workflow_state,
+ 'form': form,
+ 'workflow_state': workflow_state,
+ 'workflow': workflow_state.workflow,
+ 'navigation_object_list': [
+ {'object': 'workflow', 'name': _(u'workflow')},
+ {'object': 'workflow_state', 'name': _(u'state')},
+ ],
+ }, context_instance=RequestContext(request))
+
+
+def setup_workflow_state_transition_edit(request, work_state_transition_pk):
+ Permission.objects.check_permissions(request.user, [PERMISSION_WORKFLOW_SETUP_EDIT])
+ workflow_state_transition = get_object_or_404(WorkflowStateTransition, pk=work_state_transition_pk)
+ redirect_url = reverse('setup_workflow_state_transitions_list', args=[workflow_state_transition.workflow_state_source.pk])
+
+ if request.method == 'POST':
+ form = WorkflowStateTransitionSetupForm(workflow_state=workflow_state_transition.workflow_state_source, instance=workflow_state_transition, data=request.POST)
+ if form.is_valid():
+ workflow_state_transition = form.save()
+ messages.success(request, _(u'worflow state transition edited succesfully.'))
+ return HttpResponseRedirect(redirect_url)
+ else:
+ form = WorkflowStateSetupForm(workflow_state=workflow_state_transition.workflow_state_source, instance=workflow_state_transition)
+
+ return render_to_response('generic_form.html', {
+ 'title': _(u'edit worflow state transition: %s') % workflow_state_transition,
+ 'form': form,
+ 'workflow_state': workflow_state_transition.workflow_state_source,
+ 'workflow': workflow_state_transition.workflow_state_source.workflow,
+ 'workflow_state_transition': workflow_state_transition,
+ 'navigation_object_list': [
+ {'object': 'workflow', 'name': _(u'workflow')},
+ {'object': 'workflow_state', 'name': _(u'state')},
+ {'object': 'workflow_state_transition', 'name': _(u'transition')},
+ ],
+ 'list_object_variable_name': 'workflow_state_transition',
+ }, context_instance=RequestContext(request))
+"""
+"""
+def setup_state_transition_remove(request, state_transition_pk=None, state_transition_pk_list=None):
+ post_action_redirect = None
+
+ if state_transition_pk:
+ state_transitions = [get_object_or_404(WorkflowState, pk=state_transition_pk)]
+ post_action_redirect = reverse('setup_state_transitions_list', args=[state_transitions[0].state.pk])
+ elif state_transition_pk_list:
+ state_transitions = [get_object_or_404(WorkflowState, pk=state_transition_pk) for state_transition_pk in state_transition_pk_list.split(',')]
+ else:
+ messages.error(request, _(u'Must provide at least one state state.'))
+ return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
+
+ try:
+ Permission.objects.check_permissions(request.user, [PERMISSION_STATE_SETUP_DELETE])
+ except PermissionDenied:
+ state_transitions = AccessEntry.objects.filter_objects_by_access(PERMISSION_STATE_SETUP_DELETE, request.user, state_transitions)
+
+ previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', '/')))
+ next = request.POST.get('next', request.GET.get('next', post_action_redirect if post_action_redirect else request.META.get('HTTP_REFERER', '/')))
+
+ if request.method == 'POST':
+ for state_transition in state_transitions:
+ try:
+ state_transition.delete()
+ messages.success(request, _(u'Workflow states "%s" removed successfully.') % state_transition)
+ except Exception, e:
+ messages.error(request, _(u'Error removing state state "%(state_transition)s": %(error)s') % {
+ 'state_transition': state_transition, 'error': e
+ })
+
+ return HttpResponseRedirect(next)
+
+ context = {
+ 'object_name': _(u'state state'),
+ 'delete_view': True,
+ 'previous': previous,
+ 'next': next,
+ 'form_icon': u'transmit_delete.png',
+ 'state': state_transitions[0].state,
+ 'navigation_object_list': [
+ {'object': 'state', 'name': _(u'state')},
+ {'object': 'state_transition', 'name': _(u'state state')}
+ ],
+ }
+ if len(state_transitions) == 1:
+ context['title'] = _(u'Are you sure you wish to remove the state state: %s?') % ', '.join([unicode(d) for d in state_transitions])
+ context['state_transition'] = state_transitions[0]
+ elif len(states) > 1:
+ context['title'] = _(u'Are you sure you wish to remove the state states: %s?') % ', '.join([unicode(d) for d in state_transitions])
+
+ return render_to_response('generic_confirm.html', context,
+ context_instance=RequestContext(request))
+"""
diff --git a/requirements/production.txt b/requirements/production.txt
index 0e4c94e327..a6b71bd4a1 100644
--- a/requirements/production.txt
+++ b/requirements/production.txt
@@ -58,3 +58,4 @@ requests==0.10.1
-e git+https://github.com/toastdriven/django-haystack.git@c6fd81d4163eb816476a853d416b68757e0c7ca4#egg=django_haystack-dev
Whoosh==2.3.2
Unidecode==0.04.9
+elementtree==1.2.7-20070827-preview
diff --git a/settings.py b/settings.py
index b1f4ba0929..82f5a48c27 100644
--- a/settings.py
+++ b/settings.py
@@ -176,7 +176,7 @@ INSTALLED_APPS = (
'main',
'rest_api',
'document_signatures',
-
+ 'workflows',
# Has to be last so the other apps can register it's signals
'signaler',
)
diff --git a/urls.py b/urls.py
index ee1f199449..9a4cd7ad26 100644
--- a/urls.py
+++ b/urls.py
@@ -33,6 +33,7 @@ urlpatterns = patterns('',
(r'^documents/signatures/', include('document_signatures.urls')),
(r'^feedback/', include('feedback.urls')),
(r'^mailer/', include('mailer.urls')),
+ (r'^workflows/', include('workflows.urls')),
)