diff --git a/mayan/apps/cabinets/apps.py b/mayan/apps/cabinets/apps.py
index 71e327c0bd..ec970affc7 100644
--- a/mayan/apps/cabinets/apps.py
+++ b/mayan/apps/cabinets/apps.py
@@ -7,7 +7,8 @@ from mayan.apps.acls.classes import ModelPermission
from mayan.apps.acls.permissions import permission_acl_edit, permission_acl_view
from mayan.apps.common.apps import MayanAppConfig
from mayan.apps.common.menus import (
- menu_facet, menu_main, menu_multi_item, menu_object, menu_sidebar
+ menu_facet, menu_list_facet, menu_main, menu_multi_item, menu_object,
+ menu_sidebar
)
from mayan.apps.documents.search import document_page_search, document_search
from mayan.apps.navigation import SourceColumn
@@ -99,6 +100,10 @@ class CabinetsApp(MayanAppConfig):
link_cabinet_list, link_cabinet_create
)
)
+ menu_list_facet.bind_links(
+ links=(link_cabinet_view, link_custom_acl_list),
+ sources=(Cabinet,)
+ )
menu_main.bind_links(links=(menu_cabinets,), position=98)
@@ -115,9 +120,7 @@ class CabinetsApp(MayanAppConfig):
)
menu_object.bind_links(
links=(
- link_cabinet_view, link_cabinet_edit,
- link_custom_acl_list, link_cabinet_child_add,
- link_cabinet_delete
+ link_cabinet_delete, link_cabinet_edit, link_cabinet_child_add
), sources=(Cabinet,)
)
menu_sidebar.bind_links(
diff --git a/mayan/apps/common/menus.py b/mayan/apps/common/menus.py
index 74c6cae07e..72ee8843c1 100644
--- a/mayan/apps/common/menus.py
+++ b/mayan/apps/common/menus.py
@@ -15,6 +15,7 @@ menu_about = Menu(
icon_class=icon_menu_about, label=_('System'), name='about'
)
menu_facet = Menu(label=_('Facet'), name='facet')
+menu_list_facet = Menu(label=_('Facet'), name='list facet')
menu_main = Menu(name='main')
menu_multi_item = Menu(name='multi item')
menu_object = Menu(label=_('Actions'), name='object')
diff --git a/mayan/apps/common/templatetags/common_tags.py b/mayan/apps/common/templatetags/common_tags.py
index addaf4fef1..ef3d96dffc 100644
--- a/mayan/apps/common/templatetags/common_tags.py
+++ b/mayan/apps/common/templatetags/common_tags.py
@@ -21,6 +21,14 @@ def check_sqlite():
return MESSAGE_SQLITE_WARNING
+@register.simple_tag
+def common_get_object_verbose_name(obj):
+ try:
+ return obj._meta.verbose_name
+ except AttributeError:
+ return type(obj)
+
+
@register.simple_tag
def get_collections():
return Collection.get_all()
diff --git a/mayan/apps/document_indexing/apps.py b/mayan/apps/document_indexing/apps.py
index 7160e41555..bf9a021e1f 100644
--- a/mayan/apps/document_indexing/apps.py
+++ b/mayan/apps/document_indexing/apps.py
@@ -11,7 +11,8 @@ from mayan.apps.acls.links import link_acl_list
from mayan.apps.acls.permissions import permission_acl_edit, permission_acl_view
from mayan.apps.common.apps import MayanAppConfig
from mayan.apps.common.menus import (
- menu_facet, menu_main, menu_object, menu_secondary, menu_setup, menu_tools
+ menu_facet, menu_list_facet, menu_main, menu_object, menu_secondary,
+ menu_setup, menu_tools
)
from mayan.apps.common.widgets import TwoStateWidget
from mayan.apps.documents.signals import post_document_created, post_initial_document_type
@@ -183,11 +184,15 @@ class DocumentIndexingApp(MayanAppConfig):
menu_facet.bind_links(
links=(link_document_index_list,), sources=(Document,)
)
+ menu_list_facet.bind_links(
+ links=(
+ link_acl_list, link_index_setup_document_types,
+ link_index_setup_view,
+ ), sources=(Index,)
+ )
menu_object.bind_links(
links=(
- link_index_setup_edit, link_index_setup_view,
- link_index_setup_document_types, link_acl_list,
- link_index_setup_delete
+ link_index_setup_delete, link_index_setup_edit,
), sources=(Index,)
)
menu_object.bind_links(
diff --git a/mayan/apps/document_parsing/apps.py b/mayan/apps/document_parsing/apps.py
index 3b3d68099e..22883c51a0 100644
--- a/mayan/apps/document_parsing/apps.py
+++ b/mayan/apps/document_parsing/apps.py
@@ -12,7 +12,8 @@ from mayan.apps.acls.classes import ModelPermission
from mayan.apps.common.apps import MayanAppConfig
from mayan.apps.common.classes import ModelField
from mayan.apps.common.menus import (
- menu_facet, menu_multi_item, menu_object, menu_secondary, menu_tools
+ menu_facet, menu_list_facet, menu_multi_item, menu_object, menu_secondary,
+ menu_tools
)
from mayan.apps.documents.search import document_search, document_page_search
from mayan.apps.documents.signals import post_version_upload
@@ -145,16 +146,16 @@ class DocumentParsingApp(MayanAppConfig):
menu_facet.bind_links(
links=(link_document_page_content,), sources=(DocumentPage,)
)
+ menu_list_facet.bind_links(
+ links=(link_document_type_parsing_settings,),
+ sources=(DocumentType,)
+ )
menu_multi_item.bind_links(
links=(link_document_submit_multiple,), sources=(Document,)
)
menu_object.bind_links(
links=(link_document_submit,), sources=(Document,)
)
- menu_object.bind_links(
- links=(link_document_type_parsing_settings,), sources=(DocumentType,),
- position=99
- )
menu_secondary.bind_links(
links=(
link_document_content, link_document_parsing_errors_list,
diff --git a/mayan/apps/document_states/apps.py b/mayan/apps/document_states/apps.py
index eb4e730032..d4666c38f0 100644
--- a/mayan/apps/document_states/apps.py
+++ b/mayan/apps/document_states/apps.py
@@ -12,8 +12,8 @@ from mayan.apps.common.apps import MayanAppConfig
from mayan.apps.common.classes import ModelAttribute
from mayan.apps.common.links import link_object_error_list
from mayan.apps.common.menus import (
- menu_facet, menu_main, menu_object, menu_secondary, menu_setup,
- menu_sidebar, menu_tools
+ menu_facet, menu_list_facet, menu_main, menu_object, menu_secondary,
+ menu_setup, menu_sidebar, menu_tools
)
from mayan.apps.common.permissions_runtime import permission_error_log_view
from mayan.apps.common.widgets import TwoStateWidget
@@ -262,13 +262,17 @@ class DocumentStatesApp(MayanAppConfig):
menu_facet.bind_links(
links=(link_document_workflow_instance_list,), sources=(Document,)
)
+ menu_list_facet.bind_links(
+ links=(
+ link_setup_workflow_document_types,
+ link_setup_workflow_states, link_setup_workflow_transitions,
+ link_workflow_preview, link_acl_list
+ ), sources=(Workflow,)
+ )
menu_main.bind_links(links=(link_workflow_list,), position=10)
menu_object.bind_links(
links=(
- link_setup_workflow_states, link_setup_workflow_transitions,
- link_setup_workflow_document_types, link_setup_workflow_edit,
- link_acl_list, link_workflow_preview,
- link_setup_workflow_delete
+ link_setup_workflow_delete, link_setup_workflow_edit
), sources=(Workflow,)
)
menu_object.bind_links(
@@ -291,12 +295,14 @@ class DocumentStatesApp(MayanAppConfig):
link_workflow_instance_transition
), sources=(WorkflowInstance,)
)
- menu_object.bind_links(
+
+ menu_list_facet.bind_links(
links=(
- link_workflow_document_list, link_workflow_state_list,
+ link_workflow_document_list,
+ link_workflow_state_list,
), sources=(WorkflowRuntimeProxy,)
)
- menu_object.bind_links(
+ menu_list_facet.bind_links(
links=(
link_workflow_state_document_list,
), sources=(WorkflowStateRuntimeProxy,)
diff --git a/mayan/apps/documents/apps.py b/mayan/apps/documents/apps.py
index 2a615c50e3..f596957966 100644
--- a/mayan/apps/documents/apps.py
+++ b/mayan/apps/documents/apps.py
@@ -13,8 +13,8 @@ from mayan.apps.acls.permissions import permission_acl_edit, permission_acl_view
from mayan.apps.common.apps import MayanAppConfig
from mayan.apps.common.classes import MissingItem, ModelField, Template
from mayan.apps.common.menus import (
- menu_facet, menu_main, menu_object, menu_secondary, menu_setup,
- menu_sidebar, menu_multi_item, menu_tools
+ menu_facet, menu_list_facet, menu_main, menu_object, menu_secondary,
+ menu_setup, menu_sidebar, menu_multi_item, menu_tools
)
from mayan.apps.common.signals import post_initial_setup
from mayan.apps.common.widgets import TwoStateWidget
@@ -459,12 +459,17 @@ class DocumentsApp(MayanAppConfig):
)
# Document type links
+ menu_list_facet.bind_links(
+ links=(
+ link_document_type_filename_list,
+ link_acl_list, link_object_event_types_user_subcriptions_list,
+ link_events_for_object,
+ ), sources=(DocumentType,)
+ )
+
menu_object.bind_links(
links=(
- link_document_type_edit, link_document_type_filename_list,
- link_acl_list, link_object_event_types_user_subcriptions_list,
- link_document_type_delete,
- link_events_for_object,
+ link_document_type_delete, link_document_type_edit
), sources=(DocumentType,)
)
menu_object.bind_links(
@@ -572,10 +577,10 @@ class DocumentsApp(MayanAppConfig):
link_document_page_navigation_first,
link_document_page_navigation_previous,
link_document_page_navigation_next,
- link_document_page_navigation_last, link_transformation_list
+ link_document_page_navigation_last
), sources=(DocumentPage,)
)
- menu_object.bind_links(
+ menu_list_facet.bind_links(
links=(link_transformation_list,), sources=(DocumentPage,)
)
diff --git a/mayan/apps/linking/apps.py b/mayan/apps/linking/apps.py
index 1cf95484fd..df10a152f0 100644
--- a/mayan/apps/linking/apps.py
+++ b/mayan/apps/linking/apps.py
@@ -8,7 +8,8 @@ from mayan.apps.acls.links import link_acl_list
from mayan.apps.acls.permissions import permission_acl_edit, permission_acl_view
from mayan.apps.common.apps import MayanAppConfig
from mayan.apps.common.menus import (
- menu_facet, menu_object, menu_secondary, menu_setup, menu_sidebar
+ menu_facet, menu_list_facet, menu_object, menu_secondary, menu_setup,
+ menu_sidebar
)
from mayan.apps.common.widgets import TwoStateWidget
from mayan.apps.navigation import SourceColumn
@@ -83,6 +84,12 @@ class LinkingApp(MayanAppConfig):
links=(link_smart_link_instances_for_document,),
sources=(Document,)
)
+ menu_list_facet.bind_links(
+ links=(
+ link_acl_list, link_smart_link_document_types,
+ link_smart_link_condition_list,
+ ), sources=(SmartLink,)
+ )
menu_object.bind_links(
links=(
link_smart_link_condition_edit,
@@ -91,9 +98,7 @@ class LinkingApp(MayanAppConfig):
)
menu_object.bind_links(
links=(
- link_smart_link_edit, link_smart_link_document_types,
- link_smart_link_condition_list, link_acl_list,
- link_smart_link_delete
+ link_smart_link_delete, link_smart_link_edit
), sources=(SmartLink,)
)
menu_object.bind_links(
diff --git a/mayan/apps/mailer/apps.py b/mayan/apps/mailer/apps.py
index 959041787d..99803b8ecb 100644
--- a/mayan/apps/mailer/apps.py
+++ b/mayan/apps/mailer/apps.py
@@ -10,7 +10,8 @@ from mayan.apps.acls.links import link_acl_list
from mayan.apps.acls.permissions import permission_acl_edit, permission_acl_view
from mayan.apps.common.apps import MayanAppConfig
from mayan.apps.common.menus import (
- menu_object, menu_multi_item, menu_secondary, menu_setup, menu_tools
+ menu_list_facet, menu_multi_item, menu_object, menu_secondary, menu_setup,
+ menu_tools
)
from mayan.apps.common.widgets import TwoStateWidget
from mayan.apps.navigation import SourceColumn
@@ -102,6 +103,12 @@ class MailerApp(MayanAppConfig):
}
)
+ menu_list_facet.bind_links(
+ links=(
+ link_acl_list, link_user_mailer_log_list
+ ), sources=(UserMailer,)
+ )
+
menu_multi_item.bind_links(
links=(
link_send_multiple_document, link_send_multiple_document_link
@@ -116,8 +123,8 @@ class MailerApp(MayanAppConfig):
menu_object.bind_links(
links=(
- link_user_mailer_edit, link_user_mailer_log_list,
- link_user_mailer_test, link_acl_list, link_user_mailer_delete,
+ link_user_mailer_delete, link_user_mailer_edit,
+ link_user_mailer_test
), sources=(UserMailer,)
)
diff --git a/mayan/apps/metadata/apps.py b/mayan/apps/metadata/apps.py
index b373645441..4f8ffea7bf 100644
--- a/mayan/apps/metadata/apps.py
+++ b/mayan/apps/metadata/apps.py
@@ -14,8 +14,8 @@ from mayan.apps.acls.permissions import permission_acl_edit, permission_acl_view
from mayan.apps.common.apps import MayanAppConfig
from mayan.apps.common.classes import ModelAttribute, ModelField
from mayan.apps.common.menus import (
- menu_facet, menu_multi_item, menu_object, menu_secondary, menu_setup,
- menu_sidebar
+ menu_facet, menu_list_facet, menu_multi_item, menu_object, menu_secondary,
+ menu_setup, menu_sidebar
)
from mayan.apps.common.widgets import TwoStateWidget
from mayan.apps.documents.search import document_page_search, document_search
@@ -204,23 +204,27 @@ class MetadataApp(MayanAppConfig):
)
menu_facet.bind_links(links=(link_metadata_view,), sources=(Document,))
+ menu_list_facet.bind_links(
+ links=(link_setup_document_type_metadata_types,), sources=(
+ DocumentType,
+ )
+ )
menu_multi_item.bind_links(
links=(
link_metadata_multiple_add, link_metadata_multiple_edit,
link_metadata_multiple_remove
), sources=(Document,)
)
- menu_object.bind_links(
+ menu_list_facet.bind_links(
links=(
- link_setup_document_type_metadata_types,
- ), sources=(DocumentType,)
+ link_acl_list, link_setup_metadata_type_document_types,
+ link_object_event_types_user_subcriptions_list,
+ link_events_for_object,
+ ), sources=(MetadataType,)
)
menu_object.bind_links(
links=(
- link_setup_metadata_type_edit,
- link_setup_metadata_type_document_types, link_acl_list,
- link_object_event_types_user_subcriptions_list,
- link_events_for_object, link_setup_metadata_type_delete,
+ link_setup_metadata_type_delete, link_setup_metadata_type_edit
), sources=(MetadataType,)
)
menu_secondary.bind_links(
diff --git a/mayan/apps/motd/apps.py b/mayan/apps/motd/apps.py
index 6c18918551..782480feff 100644
--- a/mayan/apps/motd/apps.py
+++ b/mayan/apps/motd/apps.py
@@ -8,7 +8,9 @@ from mayan.apps.acls.classes import ModelPermission
from mayan.apps.acls.links import link_acl_list
from mayan.apps.acls.permissions import permission_acl_edit, permission_acl_view
from mayan.apps.common.apps import MayanAppConfig
-from mayan.apps.common.menus import menu_object, menu_secondary, menu_setup
+from mayan.apps.common.menus import (
+ menu_list_facet, menu_object, menu_secondary, menu_setup
+)
from mayan.apps.navigation import SourceColumn
from .links import (
@@ -54,9 +56,14 @@ class MOTDApp(MayanAppConfig):
func=lambda context: context['object'].end_datetime or _('None')
)
+ menu_list_facet.bind_links(
+ links=(
+ link_acl_list,
+ ), sources=(Message,)
+ )
menu_object.bind_links(
links=(
- link_message_edit, link_acl_list, link_message_delete
+ link_message_delete, link_message_edit
), sources=(Message,)
)
menu_secondary.bind_links(
diff --git a/mayan/apps/navigation/classes.py b/mayan/apps/navigation/classes.py
index 0b6c7cf0b1..a8f95d7ef2 100644
--- a/mayan/apps/navigation/classes.py
+++ b/mayan/apps/navigation/classes.py
@@ -6,33 +6,37 @@ import logging
from furl import furl
from django.apps import apps
-from django.conf import settings
from django.contrib.admin.utils import label_for_field
from django.core.exceptions import PermissionDenied
-from django.shortcuts import resolve_url
from django.template import VariableDoesNotExist, Variable
from django.template.defaulttags import URLNode
-from django.urls import Resolver404, resolve
+from django.urls import resolve, reverse
from django.utils.encoding import force_str, force_text
+from mayan.apps.common.settings import setting_home_view
from mayan.apps.common.utils import return_attrib
from mayan.apps.permissions import Permission
+from .utils import get_current_view_name
+
logger = logging.getLogger(__name__)
class ResolvedLink(object):
- def __init__(self, link, current_view):
- self.current_view = current_view
+ def __init__(self, link, current_view_name):
+ self.context = None
+ self.current_view_name = current_view_name
self.disabled = False
self.link = link
- self.url = '#'
- self.context = None
self.request = None
+ self.url = '#'
+
+ def __repr__(self):
+ return '
'.format(self.url)
@property
def active(self):
- return self.link.view == self.current_view
+ return self.link.view == self.current_view_name
@property
def badge_text(self):
@@ -97,12 +101,15 @@ class Menu(object):
self.__class__._registry[name] = self
self.non_sorted_sources = non_sorted_sources or []
+ def __repr__(self):
+ return ''.format(self.name)
+
def _map_links_to_source(self, links, source, map_variable='bound_links', position=None):
source_links = getattr(self, map_variable).setdefault(source, [])
for link in links:
source_links.append(link)
- self.link_positions[link] = position
+ self.link_positions[link] = position or 0
def add_unsorted_source(self, source):
self.non_sorted_sources.append(source)
@@ -159,6 +166,24 @@ class Menu(object):
)
return resolved_navigation_object_list
+ def get_result_position(self, item):
+ """
+ Method to help sort results by position.
+ """
+ if isinstance(item, ResolvedLink):
+ return self.link_positions.get(item.link, 0)
+ else:
+ return self.link_positions.get(item, 0) or 0
+
+ def get_result_label(self, item):
+ """
+ Method to help sort results by label.
+ """
+ if isinstance(item, ResolvedLink):
+ return item.link.text
+ else:
+ return item.label
+
def resolve(self, context, source=None, sort_results=False):
if not self.check_condition(context=context):
return []
@@ -178,15 +203,8 @@ class Menu(object):
logger.warning('No request variable, aborting menu resolution')
return ()
- current_path = request.META['PATH_INFO']
-
- # Get sources: view name, view objects
- try:
- current_view = resolve(current_path).view_name
- except Resolver404:
- # Can't figure out which view corresponds to this URL.
- # Most likely it is an invalid URL.
- logger.warning('Can\'t figure out which view corresponds to this URL: %s; aborting menu resolution.', current_path)
+ current_view_name = get_current_view_name(request=request)
+ if not current_view_name:
return ()
resolved_navigation_object_list = self.get_resolved_navigation_object_list(
@@ -227,18 +245,28 @@ class Menu(object):
pass
if resolved_links:
- result.append(resolved_links)
+ result.append(
+ {
+ 'object': resolved_navigation_object,
+ 'links': resolved_links
+ }
+ )
resolved_links = []
# View links
- for link in self.bound_links.get(current_view, []):
+ for link in self.bound_links.get(current_view_name, []):
resolved_link = link.resolve(context=context)
if resolved_link:
- if resolved_link.link not in self.unbound_links.get(current_view, ()):
+ if resolved_link.link not in self.unbound_links.get(current_view_name, ()):
resolved_links.append(resolved_link)
if resolved_links:
- result.append(resolved_links)
+ result.append(
+ {
+ 'object': current_view_name,
+ 'links': resolved_links
+ }
+ )
resolved_links = []
@@ -256,7 +284,12 @@ class Menu(object):
resolved_links.append(resolved_link)
if resolved_links:
- result.append(resolved_links)
+ result.append(
+ {
+ 'object': None,
+ 'links': resolved_links
+ }
+ )
if result:
unsorted_source = False
@@ -267,16 +300,15 @@ class Menu(object):
break
if sort_results and not unsorted_source:
- result[0] = sorted(
- result[0], key=lambda item: (
- item.link.text if isinstance(item, ResolvedLink) else item.label
+ for link_group in result:
+ link_group['links'] = sorted(
+ link_group['links'], key=self.get_result_label
)
- )
else:
- # Sort links by position value passed during bind
- result[0] = sorted(
- result[0], key=lambda item: (self.link_positions.get(item.link) or 0) if isinstance(item, ResolvedLink) else (self.link_positions.get(item) or 0)
- )
+ for link_group in result:
+ link_group['links'] = sorted(
+ link_group['links'], key=self.get_result_position
+ )
return result
@@ -308,7 +340,7 @@ class Link(object):
def remove(cls, name):
del cls._registry[name]
- def __init__(self, badge_text=None, text=None, view=None, args=None, condition=None,
+ def __init__(self, text=None, view=None, args=None, badge_text=None, condition=None,
conditional_disable=None, description=None, html_data=None,
html_extra_classes=None, icon=None, icon_class=None,
keep_query=False, kwargs=None, name=None, permissions=None,
@@ -351,7 +383,7 @@ class Link(object):
request = Variable('request').resolve(context)
current_path = request.META['PATH_INFO']
- current_view = resolve(current_path).view_name
+ current_view_name = resolve(current_path).view_name
# ACL is tested agains the resolved_object or just {{ object }} if not
if not resolved_object:
@@ -386,7 +418,9 @@ class Link(object):
if not self.condition(context):
return None
- resolved_link = ResolvedLink(current_view=current_view, link=self)
+ resolved_link = ResolvedLink(
+ current_view_name=current_view_name, link=self
+ )
if self.view:
view_name = Variable('"{}"'.format(self.view))
@@ -419,7 +453,7 @@ class Link(object):
try:
resolved_link.url = node.render(context)
except Exception as exception:
- logger.debug(
+ logger.error(
'Error resolving link "%s" URL; %s', self.text, exception
)
elif self.url:
@@ -437,7 +471,7 @@ class Link(object):
parsed_url = furl(
force_str(
request.get_full_path() or request.META.get(
- 'HTTP_REFERER', resolve_url(settings.LOGIN_REDIRECT_URL)
+ 'HTTP_REFERER', reverse(setting_home_view.value)
)
)
)
@@ -467,7 +501,7 @@ class Separator(Link):
self.view = None
def resolve(self, *args, **kwargs):
- result = ResolvedLink(current_view=None, link=self)
+ result = ResolvedLink(current_view_name=None, link=self)
result.separator = True
return result
@@ -543,7 +577,7 @@ class Text(Link):
self.view = None
def resolve(self, *args, **kwargs):
- result = ResolvedLink(current_view=None, link=self)
+ result = ResolvedLink(current_view_name=None, link=self)
result.context = kwargs.get('context')
result.text_span = True
return result
diff --git a/mayan/apps/navigation/templates/navigation/generic_navigation.html b/mayan/apps/navigation/templates/navigation/generic_navigation.html
index 0a7782a231..7ef2b1d45c 100644
--- a/mayan/apps/navigation/templates/navigation/generic_navigation.html
+++ b/mayan/apps/navigation/templates/navigation/generic_navigation.html
@@ -1,5 +1,6 @@
{% load i18n %}
+{% spaceless %}
{% if as_dropdown %}