Przeglądaj źródła

Add new menu customization

Denis K 8 lat temu
rodzic
commit
c385cfbc7a

+ 53 - 0
jet/management/commands/jet_side_menu_items_example.py

@@ -0,0 +1,53 @@
+try:
+    from django.core.management.base import NoArgsCommand
+except ImportError:
+    from django.core.management import BaseCommand as NoArgsCommand
+
+from jet.utils import get_app_list, get_original_menu_items
+
+
+class Command(NoArgsCommand):
+    help = 'Generates example of JET custom apps setting'
+    item_order = 0
+    
+    def handle(self, *args, **options):
+        if args:
+            raise CommandError("Command doesn't accept any arguments")
+        return self.handle_noargs(**options)
+    
+    def handle_noargs(self, **options):
+        class User:
+            is_active = True
+            is_staff = True
+            is_superuser = True
+
+            def has_module_perms(self, app):
+                return True
+
+            def has_perm(self, object):
+                return True
+
+        class Request:
+            user = User()
+
+        app_list = get_original_menu_items({
+            'request': Request(),
+            'user': None
+        })
+
+        self.stdout.write('# Add this to your settings.py to customize applications and models list')
+        self.stdout.write('JET_SIDE_MENU_ITEMS = [')
+
+        for app in app_list:
+            self.stdout.write('    {\'app_label\': \'%s\', \'models\': [' % (
+                app['app_label']
+            ))
+
+            for model in app['models']:
+                self.stdout.write('        {\'name\': \'%s\'},' % (
+                    model['name']
+                ))
+
+            self.stdout.write('    ]},')
+
+        self.stdout.write(']')

+ 1 - 0
jet/settings.py

@@ -6,6 +6,7 @@ JET_THEMES = getattr(settings, 'JET_THEMES', [])
 
 # Side menu
 JET_SIDE_MENU_COMPACT = getattr(settings, 'JET_SIDE_MENU_COMPACT', False)
+JET_SIDE_MENU_ITEMS = getattr(settings, 'JET_SIDE_MENU_ITEMS', None)
 JET_SIDE_MENU_CUSTOM_APPS = getattr(settings, 'JET_SIDE_MENU_CUSTOM_APPS', None)
 
 # Improved usability

+ 64 - 74
jet/templates/admin/base.html

@@ -174,50 +174,48 @@
 
                 {% if user.is_active and user.is_staff %}
                     {% jet_get_menu as app_list %}
-                    {% if app_list.apps or app_list.pinned_apps %}
-                        {% if SIDE_MENU_COMPACT %}
-                            {% for app in app_list.all_apps %}
+                    {% if SIDE_MENU_COMPACT %}
+                        {% for app in app_list %}
+                            {% if app.has_perms %}
                                 <div class="sidebar-section">
                                     <div class="sidebar-title">
-                                        <a href="{{ app.app_url }}" class="sidebar-title-link">
-                                            {% if app.name != app.app_label|capfirst|escape %}
-                                                {{ app.name }}
-                                            {% else %}
-                                                {% trans app.app_label as app_label %}{{ app_label|capfirst|escape }}
-                                            {% endif %}
+                                        <a{% if app.url %} href="{{ app.url }}"{% endif %} class="sidebar-title-link"{% if app.url_blank %} target="_blank"{% endif %}>
+                                            {{ app.label }}
                                         </a>
                                     </div>
                                     {% for model in app.models %}
-                                        {% if model.admin_url %}
+                                        {% if model.has_perms %}
                                             <div>
-                                                <a href="{{ model.admin_url }}" class="sidebar-link">
+                                                <a{% if model.url %} href="{{ model.url }}"{% endif %} class="sidebar-link"{% if model.url_blank %} target="_blank"{% endif %}>
                                                     <span class="sidebar-right">
                                                         <span class="sidebar-right-arrow icon-arrow-right"></span>
                                                     </span>
-                                                    <span class="sidebar-link-label">{{ model.name }}</span>
+                                                    <span class="sidebar-link-label">{{ model.label }}</span>
                                                 </a>
                                             </div>
                                         {% endif %}
                                     {% endfor %}
                                 </div>
-                            {% endfor %}
-                        {% else %}
-                            <form action="{% url "jet:toggle_application_pin" %}" method="POST" id="toggle-application-pin-form">
-                                {% csrf_token %}
-                                <input type="hidden" name="app_label">
-                            </form>
+                            {% endif %}
+                        {% endfor %}
+                    {% else %}
+                        <form action="{% url "jet:toggle_application_pin" %}" method="POST" id="toggle-application-pin-form">
+                            {% csrf_token %}
+                            <input type="hidden" name="app_label">
+                        </form>
 
-                            <div class="sidebar-section">
-                                <div class="sidebar-title">
-                                    <span class="sidebar-right">
-                                        <a href="#" class="sidebar-right-edit edit-apps-list"><span class="icon-settings"></span></a>
-                                    </span>
-                                    {% trans 'Applications' %}
-                                </div>
+                        <div class="sidebar-section">
+                            <div class="sidebar-title">
+                                <span class="sidebar-right">
+                                    <a href="#" class="sidebar-right-edit edit-apps-list"><span class="icon-settings"></span></a>
+                                </span>
+                                {% trans 'Applications' %}
+                            </div>
 
-                                <div class="apps-list-pinned">
-                                    {% for app in app_list.pinned_apps %}
-                                        <a href="{{ app.app_url }}" class="sidebar-link popup-section-link app-item" data-app-label="{{ app.app_label }}" data-popup-section-class="sidebar-popup-section-{{ app.app_label }}">
+                            <div class="apps-list-pinned">
+                                {% for app in app_list %}
+                                    {% if app.has_perms and app.pinned %}
+                                        <a{% if app.url %} href="{{ app.url }}"{% endif %} class="sidebar-link popup-section-link app-item" data-app-label="{{ app.app_label }}" data-popup-section-class="sidebar-popup-section-{{ app.app_label }}" data-order="{{ forloop.counter }}"{% if app.url_blank %} target="_blank"{% endif %}>
                                             <span class="sidebar-left collapsible">
                                                 <span class="sidebar-left-pin icon-star pin-toggle"></span>
                                                 <span class="sidebar-left-unpin icon-cross pin-toggle"></span>
@@ -228,22 +226,20 @@
                                             </span>
 
                                             <span class="sidebar-link-label">
-                                                {% if app.name != app.app_label|capfirst|escape %}
-                                                    {{ app.name }}
-                                                {% else %}
-                                                    {% trans app.app_label as app_label %}{{ app_label|capfirst|escape }}
-                                                {% endif %}
+                                                {{ app.label }}
                                             </span>
                                         </a>
-                                    {% endfor %}
-                                </div>
-                                <a href="#" class="sidebar-center-link apps-hide">
-                                    <span class="apps-hide-label apps-visible">{% trans "Hide applications" %}</span>
-                                    <span class="apps-hide-label apps-hidden">{% trans "Show hidden" %}</span>
-                                </a>
-                                <div class="apps-list">
-                                    {% for app in app_list.apps %}
-                                        <a href="{{ app.app_url }}" class="sidebar-link popup-section-link app-item" data-app-label="{{ app.app_label }}" data-popup-section-class="sidebar-popup-section-{{ app.app_label }}">
+                                    {% endif %}
+                                {% endfor %}
+                            </div>
+                            <a href="#" class="sidebar-center-link apps-hide">
+                                <span class="apps-hide-label apps-visible">{% trans "Hide applications" %}</span>
+                                <span class="apps-hide-label apps-hidden">{% trans "Show hidden" %}</span>
+                            </a>
+                            <div class="apps-list">
+                                {% for app in app_list %}
+                                    {% if app.has_perms and not app.pinned %}
+                                        <a{% if app.url %} href="{{ app.url }}"{% endif %} class="sidebar-link popup-section-link app-item" data-app-label="{{ app.app_label }}" data-popup-section-class="sidebar-popup-section-{{ app.app_label }}" data-order="{{ forloop.counter }}"{% if app.url_blank %} target="_blank"{% endif %}>
                                             <span class="sidebar-left collapsible">
                                                 <span class="sidebar-left-pin icon-star pin-toggle"></span>
                                                 <span class="sidebar-left-unpin icon-cross pin-toggle"></span>
@@ -254,17 +250,13 @@
                                             </span>
 
                                             <span class="sidebar-link-label">
-                                                {% if app.name != app.app_label|capfirst|escape %}
-                                                    {{ app.name }}
-                                                {% else %}
-                                                    {% trans app.app_label as app_label %}{{ app_label|capfirst|escape }}
-                                                {% endif %}
+                                                {{ app.label }}
                                             </span>
                                         </a>
-                                    {% endfor %}
-                                </div>
+                                    {% endif %}
+                                {% endfor %}
                             </div>
-                        {% endif %}
+                        </div>
                     {% endif %}
 
                     <div class="sidebar-section last">
@@ -319,34 +311,32 @@
                         <a href="#" class="sidebar-close sidebar-back">
                             <span class="sidebar-close-icon icon-arrow-left"></span>
                         </a>
-                        {% for app in app_list.apps|add:app_list.pinned_apps %}
-                            <div class="sidebar-popup-section sidebar-popup-section-{{ app.app_label }}">
-                                <div class="sidebar-popup-title">
-                                    {% if app.name != app.app_label|capfirst|escape %}
-                                        {{ app.name }}
-                                    {% else %}
-                                        {% trans app.app_label as app_label %}{{ app_label|capfirst|escape }}
-                                    {% endif %}
-                                </div>
+                        {% for app in app_list %}
+                            {% if app.has_perms %}
+                                <div class="sidebar-popup-section sidebar-popup-section-{{ app.app_label }}">
+                                    <div class="sidebar-popup-title">
+                                        {{ app.label }}
+                                    </div>
 
-                                <input class="sidebar-popup-search" placeholder="Search...">
+                                    <input class="sidebar-popup-search" placeholder="Search...">
 
-                                <ul class="sidebar-popup-list">
-                                    <li class="sidebar-popup-list-item app-{{ app.app_label }}{{ app.current|yesno:" current," }}">
-                                        <a href="{{ app.app_url }}" class="sidebar-popup-list-item-link">{% trans 'Application page' %}</a>
-                                    </li>
+                                    <ul class="sidebar-popup-list">
+                                        <li class="sidebar-popup-list-item app-{{ app.app_label }}{{ app.current|yesno:" current," }}">
+                                            <a href="{{ app.app_url }}" class="sidebar-popup-list-item-link">{% trans 'Application page' %}</a>
+                                        </li>
 
-                                    {% for model in app.models %}
-                                        <li class="sidebar-popup-list-item model-{{ model.object_name|lower }}{{ model.current|yesno:" current," }}">
-                                            {% if model.admin_url %}
-                                                <a href="{{ model.admin_url }}" class="sidebar-popup-list-item-link">{{ model.name }}</a>
-                                            {% else %}
-                                                {{ model.name }}
+                                        {% for model in app.models %}
+                                            {% if model.has_perms %}
+                                                <li class="sidebar-popup-list-item{% if model.name %} model-{{ model.name }}{% endif %}{{ model.current|yesno:" current," }}">
+                                                    <a{% if model.url %} href="{{ model.url }}"{% endif %} class="sidebar-popup-list-item-link"{% if model.url_blank %} target="_blank"{% endif %}>
+                                                        {{ model.label }}
+                                                    </a>
+                                                </li>
                                             {% endif %}
-                                        </li>
-                                    {% endfor %}
-                                </ul>
-                            </div>
+                                        {% endfor %}
+                                    </ul>
+                                </div>
+                            {% endif %}
                         {% endfor %}
                     </div>
                 </div>

+ 4 - 73
jet/templatetags/jet_tags.py

@@ -9,9 +9,9 @@ from django.utils.formats import get_format
 from django.utils.safestring import mark_safe
 from django.utils.encoding import smart_text
 from jet import settings, VERSION
-from jet.models import Bookmark, PinnedApplication
-from jet.utils import get_app_list, get_model_instance_label, get_model_queryset, get_possible_language_codes, \
-    get_admin_site
+from jet.models import Bookmark
+from jet.utils import get_model_instance_label, get_model_queryset, get_possible_language_codes, \
+    get_admin_site, get_menu_items
 
 try:
     from urllib.parse import parse_qsl
@@ -39,76 +39,7 @@ def jet_get_datetime_format():
 
 @register.assignment_tag(takes_context=True)
 def jet_get_menu(context):
-    if settings.JET_SIDE_MENU_CUSTOM_APPS not in (None, False):
-        app_list = get_app_list(context, False)
-        app_dict = {}
-        models_dict = {}
-
-        for app in app_list:
-            app_label = app.get('app_label', app.get('name'))
-            app_dict[app_label] = app
-
-            for model in app['models']:
-                if app_label not in models_dict:
-                    models_dict[app_label] = {}
-
-                models_dict[app_label][model['object_name']] = model
-
-            app['models'] = []
-
-        app_list = []
-        settings_app_list = settings.JET_SIDE_MENU_CUSTOM_APPS
-
-        if isinstance(settings_app_list, dict):
-            admin_site = get_admin_site(context)
-            settings_app_list = settings_app_list.get(admin_site.name, [])
-
-        for item in settings_app_list:
-            app_label, models = item
-
-            if app_label in app_dict:
-                app = app_dict[app_label]
-
-                for model_label in models:
-                    if model_label == '__all__':
-                        app['models'] = models_dict[app_label].values()
-                        break
-                    elif model_label in models_dict[app_label]:
-                        model = models_dict[app_label][model_label]
-                        app['models'].append(model)
-
-                app_list.append(app)
-    else:
-        app_list = get_app_list(context)
-
-    current_found = False
-
-    pinned = PinnedApplication.objects.filter(user=context.get('user').pk).values_list('app_label', flat=True)
-
-    all_aps = []
-    apps = []
-    pinned_apps = []
-
-    for app in app_list:
-        if not current_found:
-            for model in app['models']:
-                if 'admin_url' in model and context['request'].path.startswith(model['admin_url']):
-                    model['current'] = True
-                    current_found = True
-                    break
-
-            if not current_found and context['request'].path.startswith(app['app_url']):
-                app['current'] = True
-                current_found = True
-
-        if app.get('app_label', app.get('name')) in pinned:
-            pinned_apps.append(app)
-        else:
-            apps.append(app)
-
-        all_aps.append(app)
-
-    return {'apps': apps, 'pinned_apps': pinned_apps, 'all_apps': all_aps}
+    return get_menu_items(context)
 
 
 @register.assignment_tag

+ 185 - 0
jet/utils.py

@@ -1,6 +1,8 @@
 import datetime
 import json
 from django.utils import translation
+from jet import settings
+from jet.models import PinnedApplication
 
 try:
     from django.apps.registry import apps
@@ -21,6 +23,9 @@ from django.utils.functional import Promise
 from django.contrib.admin.options import IncorrectLookupParameters
 from django.core import urlresolvers
 from django.contrib import admin
+from django.utils.translation import ugettext_lazy as _
+from django.utils.text import slugify
+from collections import OrderedDict
 
 
 class JsonResponse(HttpResponse):
@@ -67,6 +72,7 @@ def get_app_list(context, order=True):
                     'name': capfirst(model._meta.verbose_name_plural),
                     'object_name': model._meta.object_name,
                     'perms': perms,
+                    'model_name': model._meta.model_name
                 }
                 if perms.get('change', False):
                     try:
@@ -233,3 +239,182 @@ def get_possible_language_codes():
         language_codes.append(split[0].lower())
 
     return language_codes
+
+
+def get_original_menu_items(context):
+    if context.get('user') and context['user'].is_authenticated():
+        pinned_apps = PinnedApplication.objects.filter(user=context['user'].pk).values_list('app_label', flat=True)
+    else:
+        pinned_apps = []
+
+    original_app_list = get_app_list(context)
+
+    return map(lambda app: {
+        'app_label': app['app_label'],
+        'url': app['app_url'],
+        'url_blank': False,
+        'label': app.get('name', capfirst(_(app['app_label']))),
+        'has_perms': app.get('has_module_perms', False),
+        'models': list(map(lambda model: {
+            'url': model.get('admin_url'),
+            'url_blank': False,
+            'name': model['model_name'],
+            'object_name': model['object_name'],
+            'label': model.get('name', model['object_name']),
+            'has_perms': any(model.get('perms', {}).values()),
+        }, app['models'])),
+        'pinned': app['app_label'] in pinned_apps,
+        'custom': False
+    }, original_app_list)
+
+
+def get_menu_item_url(url, original_app_list):
+    if isinstance(url, dict):
+        url_type = url.get('type')
+
+        if url_type == 'app':
+            return original_app_list[url['app_label']]['url']
+        elif url_type == 'model':
+            models = dict(map(
+                lambda x: (x['name'], x['url']),
+                original_app_list[url['app_label']]['models']
+            ))
+            return models[url['model']]
+        elif url_type == 'reverse':
+            return reverse(url['name'], args=url.get('args'), kwargs=url.get('kwargs'))
+    elif isinstance(url, str):
+        return url
+
+
+def get_menu_items(context):
+    pinned_apps = PinnedApplication.objects.filter(user=context['user'].pk).values_list('app_label', flat=True)
+    original_app_list = OrderedDict(map(lambda app: (app['app_label'], app), get_original_menu_items(context)))
+    custom_app_list = settings.JET_SIDE_MENU_ITEMS
+    custom_app_list_deprecated = settings.JET_SIDE_MENU_CUSTOM_APPS
+
+    if custom_app_list not in (None, False):
+        if isinstance(custom_app_list, dict):
+            admin_site = get_admin_site(context)
+            custom_app_list = custom_app_list.get(admin_site.name, [])
+
+        app_list = []
+
+        def get_menu_item_app_model(app_label, data):
+            item = {'has_perms': True}
+
+            if 'name' in data and app_label in original_app_list:
+                models = dict(map(
+                    lambda x: (x['name'], x),
+                    original_app_list[app_label]['models']
+                ))
+
+                if data['name'] in models:
+                    item = models[data['name']]
+
+            if 'label' in data:
+                item['label'] = data['label']
+
+            if 'url' in data:
+                item['url'] = get_menu_item_url(data['url'], original_app_list)
+
+            if 'url_blank' in data:
+                item['url_blank'] = data['url_blank']
+
+            if 'permissions' in data:
+                item['has_perms'] = item.get('has_perms', True) and context['user'].has_perms(data['permissions'])
+
+            return item
+
+        def get_menu_item_app(data):
+            app_label = data.get('app_label')
+
+            if not app_label:
+                if 'label' not in data:
+                    raise Exception('Custom menu items should at least have \'label\' or \'app_label\' key')
+                app_label = 'custom_%s' % slugify(data['label'])
+
+            if app_label in original_app_list:
+                item = original_app_list[app_label]
+            else:
+                item = {'app_label': app_label, 'has_perms': True}
+
+            if 'label' in data:
+                item['label'] = data['label']
+
+            if 'models' in data:
+                item['models'] = list(map(lambda x: get_menu_item_app_model(app_label, x), data['models']))
+
+            if 'url' in data:
+                item['url'] = get_menu_item_url(data['url'], original_app_list)
+
+            if 'url_blank' in data:
+                item['url_blank'] = data['url_blank']
+
+            if 'permissions' in data:
+                item['has_perms'] = item.get('has_perms', True) and context['user'].has_perms(data['permissions'])
+
+            item['pinned'] = item['app_label'] in pinned_apps
+
+            return item
+
+        for data in custom_app_list:
+            item = get_menu_item_app(data)
+            app_list.append(item)
+    elif custom_app_list_deprecated not in (None, False):
+        app_dict = {}
+        models_dict = {}
+
+        for app in original_app_list.values():
+            app_label = app['app_label']
+            app_dict[app_label] = app
+
+            for model in app['models']:
+                if app_label not in models_dict:
+                    models_dict[app_label] = {}
+
+                models_dict[app_label][model['object_name']] = model
+
+            app['models'] = []
+
+        app_list = []
+
+        if isinstance(custom_app_list_deprecated, dict):
+            admin_site = get_admin_site(context)
+            custom_app_list_deprecated = custom_app_list_deprecated.get(admin_site.name, [])
+
+        for item in custom_app_list_deprecated:
+            app_label, models = item
+
+            if app_label in app_dict:
+                app = app_dict[app_label]
+
+                for model_label in models:
+                    if model_label == '__all__':
+                        app['models'] = models_dict[app_label].values()
+                        break
+                    elif model_label in models_dict[app_label]:
+                        model = models_dict[app_label][model_label]
+                        app['models'].append(model)
+
+                app_list.append(app)
+    else:
+        app_list = original_app_list.values()
+
+    current_found = False
+
+    for app in app_list:
+        if not current_found:
+            for model in app['models']:
+                if not current_found and model.get('url') and context['request'].path.startswith(model['url']):
+                    model['current'] = True
+                    current_found = True
+                else:
+                    model['current'] = False
+
+            if not current_found and app.get('url') and context['request'].path.startswith(app['url']):
+                app['current'] = True
+                current_found = True
+            else:
+                app['current'] = False
+
+    return app_list