Skip to content

Commit 5020caf

Browse files
committed
feature #3 add priority_level for menu order.
1 parent c03c77a commit 5020caf

File tree

6 files changed

+176
-28
lines changed

6 files changed

+176
-28
lines changed

adminlteui/admin.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77
from django.urls import path, reverse
88
from django.template.response import TemplateResponse
99
from django.utils import timezone
10+
from django.utils.encoding import force_str
1011
from django.utils.translation import gettext_lazy as _
1112
from django.utils.html import format_html
1213
from django.conf import settings
13-
from django.http.response import HttpResponse, HttpResponseForbidden
14+
from django.http.response import HttpResponse, HttpResponseForbidden, \
15+
HttpResponseBadRequest
1416
from adminlteui.widgets import AdminlteSelect
1517
from treebeard.admin import TreeAdmin
1618
from treebeard.forms import movenodeform_factory
@@ -225,10 +227,10 @@ def general_option_view(self, request):
225227
@admin.register(Menu)
226228
class MenuAdmin(TreeAdmin):
227229
list_display = ('name', 'position', 'link_type', 'display_link',
228-
'display_content_type', 'display_icon',
230+
'display_content_type', 'priority_level', 'display_icon',
229231
'valid')
230232
list_filter = ('position', 'link_type', 'valid')
231-
list_editable = ('valid',)
233+
list_editable = ('valid', 'priority_level')
232234
form = movenodeform_factory(Menu)
233235
change_list_template = 'adminlte/menu_change_list.html'
234236
change_form_template = 'adminlte/menu_change_form.html'
@@ -250,6 +252,16 @@ def formfield_for_foreignkey(self, db_field, request, **kwargs):
250252

251253
return super().formfield_for_foreignkey(db_field, request, **kwargs)
252254

255+
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
256+
try:
257+
return super().changeform_view(request, object_id, form_url, extra_context)
258+
except Exception as e:
259+
messages.error(request,
260+
_('Exception raised while add node: %s') % _(
261+
force_str(e)))
262+
return HttpResponseBadRequest(
263+
_('Exception raised while add node: %s') % _(force_str(e)))
264+
253265
def get_urls(self):
254266
base_urls = super().get_urls()
255267
urls = [
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 2.2.2 on 2019-08-06 01:37
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('django_admin_settings', '0004_auto_20190708_1832'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='menu',
15+
name='priority_level',
16+
field=models.IntegerField(default=100, verbose_name='Priority Level'),
17+
),
18+
]

adminlteui/models.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
# -*- coding: utf-8 -*-
2+
import itertools
23
from django.db import models
34
from django.utils import timezone
45
from django.utils.translation import gettext_lazy as _
56
from django.contrib.contenttypes.models import ContentType
67

7-
from treebeard.mp_tree import MP_Node
8+
from treebeard.mp_tree import MP_Node,\
9+
InvalidMoveToDescendant, MP_MoveHandler
810

911

1012
class Options(models.Model):
@@ -39,7 +41,8 @@ class Menu(MP_Node):
3941
verbose_name=_('Link Type'))
4042
link = models.CharField(max_length=255, blank=True, null=True,
4143
verbose_name=_('Link'),
42-
help_text=_('support admin:index or /admin/ or http://'))
44+
help_text=_(
45+
'support admin:index or /admin/ or http://'))
4346
icon = models.CharField(max_length=255,
4447
blank=True,
4548
null=True,
@@ -50,12 +53,26 @@ class Menu(MP_Node):
5053
on_delete=models.CASCADE,
5154
help_text=_(
5255
'use for permission control.'))
53-
56+
priority_level = models.IntegerField(default=100,
57+
verbose_name=_('Priority Level'),
58+
help_text=_('The bigger the priority'))
5459
valid = models.BooleanField(default=True, verbose_name=_('Valid'))
55-
node_order_by = ['name', 'position']
60+
node_order_by = ['priority_level']
61+
62+
def move(self, target, pos=None):
63+
"""
64+
Moves the current node and all it's descendants to a new position
65+
relative to another node.
66+
67+
:raise PathOverflow: when the library can't make room for the
68+
node's new position
69+
"""
70+
if target.depth == 2:
71+
raise InvalidMoveToDescendant(_('max depth is 2.'))
72+
return MP_MoveHandler(self, target, pos).process()
5673

5774
def __str__(self):
58-
return '{}'.format(self.name)
75+
return '{}|{}'.format(self.name, self.priority_level)
5976

6077
class Meta:
6178
verbose_name = _('Menu')

adminlteui/templates/adminlte/menu_change_list.html

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,32 @@
1-
{% extends "admin/tree_change_list.html" %}
2-
{% load i18n %}
1+
{# Used for MP and NS trees #}
2+
{% extends "admin/change_list.html" %}
3+
{% load admin_list admin_tree adminlte_list i18n %}
4+
5+
{% block extrastyle %}
6+
{{ block.super }}
7+
{% treebeard_css %}
8+
{% endblock %}
9+
10+
{% block extrahead %}
11+
{{ block.super }}
12+
{% treebeard_js %}
13+
{% endblock %}
14+
315
{% block object-tools-items %}
416
<button id="exchange_menu" class="btn btn-danger">{% trans 'Exchange Menu' %}</button>
517
{{ block.super }}
618
{% endblock %}
719

20+
{% block result_list %}
21+
{% if action_form and actions_on_top and cl.full_result_count %}
22+
{% admin_actions %}
23+
{% endif %}
24+
{% adminlte_result_tree cl request %}
25+
{% if action_form and actions_on_bottom and cl.full_result_count %}
26+
{% admin_actions %}
27+
{% endif %}
28+
{% endblock %}
29+
830
{% block extrajs %}
931
{{ block.super }}
1032
<script>
@@ -19,4 +41,4 @@
1941
})
2042
});
2143
</script>
22-
{% endblock %}
44+
{% endblock %}

adminlteui/templatetags/adminlte_list.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,17 @@
44
)
55
from django.utils.safestring import mark_safe
66
from django.utils.html import format_html
7+
from django.utils.translation import gettext_lazy as _
78
from django.template import Library
89
from django.template.loader import get_template
10+
from django.contrib.admin.templatetags.admin_list import (
11+
result_headers, result_hidden_fields)
912
import urllib.parse
1013

14+
from treebeard.templatetags import needs_checkboxes
15+
from treebeard.templatetags.admin_tree import check_empty_dict, get_parent_id, \
16+
items_for_result
17+
1118
register = Library()
1219

1320
DOT = '.'
@@ -97,3 +104,77 @@ def adminlte_admin_list_filter(cl, spec):
97104
'choices': choices,
98105
'spec': spec,
99106
})
107+
108+
109+
def results(cl):
110+
"""
111+
reorder by priority_level
112+
:param cl:
113+
:return:
114+
"""
115+
if cl.formset:
116+
new_result_list = []
117+
new_forms = []
118+
parent_nodes = cl.result_list.filter(depth=1).order_by(
119+
'-priority_level')
120+
for parent_node in parent_nodes:
121+
new_result_list.append(parent_node)
122+
123+
child_nodes = parent_node.get_children().order_by('-priority_level')
124+
for child_node in child_nodes:
125+
new_result_list.append(child_node)
126+
127+
order_ = []
128+
for i, obj in enumerate(new_result_list):
129+
for j, obj_new in enumerate(cl.result_list):
130+
if obj_new == obj:
131+
order_.append(j)
132+
133+
for o in order_:
134+
new_forms.append(cl.formset.forms[o])
135+
136+
for res, form in zip(new_result_list, new_forms):
137+
yield (res.pk, get_parent_id(res), res.get_depth(),
138+
res.get_children_count(),
139+
list(items_for_result(cl, res, form)))
140+
else:
141+
new_result_list = []
142+
parent_nodes = cl.result_list.filter(depth=1).order_by(
143+
'-priority_level')
144+
for parent_node in parent_nodes:
145+
new_result_list.append(parent_node)
146+
147+
child_nodes = parent_node.get_children().order_by('-priority_level')
148+
for child_node in child_nodes:
149+
new_result_list.append(child_node)
150+
151+
for res in new_result_list:
152+
yield (res.pk, get_parent_id(res), res.get_depth(),
153+
res.get_children_count(),
154+
list(items_for_result(cl, res, None)))
155+
156+
157+
@register.inclusion_tag(
158+
'admin/tree_change_list_results.html', takes_context=True)
159+
def adminlte_result_tree(context, cl, request):
160+
"""
161+
Added 'filtered' param, so the template's js knows whether the results have
162+
been affected by a GET param or not. Only when the results are not filtered
163+
you can drag and sort the tree
164+
"""
165+
166+
# Here I'm adding an extra col on pos 2 for the drag handlers
167+
headers = list(result_headers(cl))
168+
headers.insert(1 if needs_checkboxes(context) else 0, {
169+
'text': '+',
170+
'sortable': True,
171+
'url': request.path,
172+
'tooltip': _('Return to ordered tree'),
173+
'class_attrib': mark_safe(' class="oder-grabber"')
174+
})
175+
return {
176+
'filtered': not check_empty_dict(request.GET),
177+
'result_hidden_fields': list(result_hidden_fields(cl)),
178+
'result_headers': headers,
179+
'results': list(results(cl)),
180+
}

adminlteui/templatetags/adminlte_menu.py

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -51,52 +51,50 @@ def get_custom_menu(request, position):
5151

5252
limit_for_internal_link = set(limit_for_internal_link)
5353
new_available_apps = []
54-
menu = Menu.dump_bulk()
54+
menu = Menu.get_tree().filter(depth=1).order_by('-priority_level')
5555
for menu_item in menu:
56-
if menu_item.get('data').get('position') != position:
56+
if menu_item.position != position:
5757
continue
5858

5959
new_available_apps_item = {}
60-
data = (menu_item.get('data'))
61-
if data.get('valid') is False:
60+
if menu_item.valid is False:
6261
continue
6362

64-
new_available_apps_item['name'] = data.get('name')
65-
new_available_apps_item['icon'] = data.get('icon')
63+
new_available_apps_item['name'] = menu_item.name
64+
new_available_apps_item['icon'] = menu_item.icon
6665

67-
children = menu_item.get('children')
66+
children = menu_item.get_children().order_by('-priority_level')
6867
if not children:
6968
# skip menu_item that no children and link type is devide.
70-
if data.get('link_type') in (0, 1):
69+
if menu_item.link_type in (0, 1):
7170
new_available_apps_item['admin_url'] = get_reverse_link(
72-
data.get('link'))
71+
menu_item.link)
7372
new_available_apps.append(new_available_apps_item)
7473
continue
7574
new_available_apps_item['models'] = []
7675

7776
for children_item in children:
78-
if children_item.get('data').get('link_type') == 0:
77+
if children_item.link_type == 0:
7978
# internal link should connect a content_type, otherwise it will be hide.
80-
if children_item.get('data').get('content_type'):
79+
if children_item.content_type:
8180
obj = ContentType.objects.get(
82-
id=children_item.get('data').get('content_type'))
81+
id=children_item.content_type.id)
8382
# if user hasn't permission, the model will be skip.
8483
if obj.app_label + ':' + obj.model not in limit_for_internal_link:
8584
continue
8685
else:
8786
continue
8887

89-
if children_item.get('data').get('valid') is False:
88+
if children_item.valid is False:
9089
continue
9190
new_children_item = dict()
92-
new_children_item['name'] = children_item.get('data').get('name')
91+
new_children_item['name'] = children_item.name
9392
new_children_item['admin_url'] = get_reverse_link(
94-
children_item.get('data').get('link')
93+
children_item.link
9594
)
9695
if not new_children_item['admin_url']:
9796
continue
98-
new_children_item['icon'] = children_item.get('data').get('icon')
99-
# new_children_item['admin_url'] = children_item.get('link')
97+
new_children_item['icon'] = children_item.icon
10098
new_available_apps_item['models'].append(new_children_item)
10199
if new_available_apps_item['models']:
102100
new_available_apps.append(new_available_apps_item)

0 commit comments

Comments
 (0)