forked from stephenmcd/mezzanine
/
pages_tags.py
142 lines (127 loc) · 5.28 KB
/
pages_tags.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
from collections import defaultdict
from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import reverse, NoReverseMatch
from django.db.models import get_models
from django.template import TemplateSyntaxError, Variable
from mezzanine.pages.models import Page
from mezzanine.utils.urls import admin_url
from mezzanine import template
from mezzanine.template.loader import get_template
register = template.Library()
@register.render_tag
def page_menu(context, token):
"""
Return a list of child pages for the given parent, storing all
pages in a dict in the context when first called using parents as keys
for retrieval on subsequent recursive calls from the menu template.
"""
# First arg could be the menu template file name, or the parent page.
# Also allow for both to be used.
template_name = None
parent_page = None
parts = token.split_contents()[1:]
for part in parts:
part = Variable(part).resolve(context)
if isinstance(part, unicode):
template_name = part
elif isinstance(part, Page):
parent_page = part
if template_name is None:
try:
template_name = context["menu_template_name"]
except KeyError:
error = "No template found for page_menu in: %s" % parts
raise TemplateSyntaxError(error)
context["menu_template_name"] = template_name
if "menu_pages" not in context:
pages = defaultdict(list)
try:
user = context["request"].user
slug = context["request"].path
except KeyError:
user = None
slug = ""
has_children = lambda page_id: lambda: page_id in context["menu_pages"]
published = Page.objects.published(for_user=user)
for page in published.select_related(depth=2).order_by("_order"):
page.set_menu_helpers(slug)
setattr(page, "has_children", has_children(page.id))
pages[page.parent_id].append(page)
context["menu_pages"] = pages
context["on_home"] = slug == reverse("home")
# ``branch_level`` must be stored against each page so that the
# calculation of it is correctly applied. This looks weird but if we do
# the ``branch_level`` as a separate arg to the template tag with the
# addition performed on it, the addition occurs each time the template
# tag is called rather than once per level.
context["branch_level"] = 0
if parent_page is not None:
context["branch_level"] = getattr(parent_page, "branch_level", 0) + 1
parent_page = parent_page.id
context["page_branch"] = context["menu_pages"].get(parent_page, [])
for i, page in enumerate(context["page_branch"]):
context["page_branch"][i].branch_level = context["branch_level"]
t = get_template(template_name, context)
return t.render(context)
@register.as_tag
def models_for_pages(*args):
"""
Create a select list containing each of the models that subclass the
``Page`` model.
"""
page_models = []
for model in get_models():
if model is not Page and issubclass(model, Page):
try:
admin_url(model, "add")
except NoReverseMatch:
continue
else:
setattr(model, "name", model._meta.verbose_name)
setattr(model, "add_url", admin_url(model, "add"))
page_models.append(model)
return page_models
@register.render_tag
def set_model_permissions(context, token):
"""
Assigns a permissions dict to the given model, much like Django
does with its dashboard app list.
Used within the change list for pages, to implement permission
checks for the navigation tree.
"""
model = context[token.split_contents()[1]]
opts = model._meta
perm_name = opts.app_label + ".%s_" + opts.object_name.lower()
request = context["request"]
setattr(model, "perms", {})
for perm_type in ("add", "change", "delete"):
model.perms[perm_type] = request.user.has_perm(perm_name % perm_type)
return ""
@register.render_tag
def set_page_permissions(context, token):
"""
Assigns a permissions dict to the given page instance, combining
Django's permission for the page's model and a permission check
against the instance itself calling the page's ``can_add``,
``can_change`` and ``can_delete`` custom methods.
Used within the change list for pages, to implement permission
checks for the navigation tree.
"""
page = context[token.split_contents()[1]]
model = page.get_content_model()
try:
opts = model._meta
except AttributeError:
# A missing inner Meta class usually means the Page model
# hasn't been directly subclassed.
error = _("An error occured with the following class. Does "
"it subclass Page directly?")
raise ImproperlyConfigured(error + " '%s'" % page.__class__.__name__)
perm_name = opts.app_label + ".%s_" + opts.object_name.lower()
request = context["request"]
setattr(page, "perms", {})
for perm_type in ("add", "change", "delete"):
perm = request.user.has_perm(perm_name % perm_type)
perm = perm and getattr(page, "can_%s" % perm_type)(request)
page.perms[perm_type] = perm
return ""