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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
|
# Copyright (C) 2010, 2011 Linaro Limited
#
# Author: Zygmunt Krynicki <zygmunt.krynicki@linaro.org>
#
# This file is part of LAVA Server.
#
# LAVA Server is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License version 3
# as published by the Free Software Foundation
#
# LAVA Server is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with LAVA Server. If not, see <http://www.gnu.org/licenses/>.
"""
lava_server.bread_crumbs
========================
Bread crumb management for LAVA server.
This system allows one to construct static trees of views or even site maps,
where each view has at most one parent. In this model any view could be
followed back through the parent link to create a bread crumb trail of named
URLs.
It is important to emphasize that this system is STATIC, that is, it is not
based on browsing history. Regardless on how the user got to a particular view
the bread crumb system will report the same set of pages. The idea is not to
let users go back (that's the what the browser allows them to do) but to put
the current page into context of where it "belongs".
To use this system apply the @BreadCrumb(name, parent=parent_view,
needs=['required', 'keywords']) decorator to your view function. To render
breadcrumbs you can use the default template that is a part of
"layouts/content.html" template. Your context must include the
bread_crumb_trail variable. To construct it call
BreadCrumbTrail.leading_to(your_view_name, ...) passing any of the keyword
arguments specified in needs of your and any parent views (yes this is
annoying).
A mistake in pairing 'needs' to keywords passed to BreadCrumbTrail.leading_to()
will result in logged warnings (either a name of the URL being not
constructible). To fix that simply add the missing keyword argument and reload.
"""
from django.core.urlresolvers import reverse
import logging
class BreadCrumb(object):
"""
A crumb of bread left in the forest of pages to let you go back to (no, not
to where you came from) where the developer desired you to go.
"""
def __init__(self, name, parent=None, needs=None):
"""
Construct a bread crumb object.
The name is the essential property creating the actual text visible on
web pages. It may be a static string or a new-style python string
template. Parent allows one to construct a static bread crumb tree where
each crumb may have at most one parent. Needs, if specified, must be
an array of strings that denote identifiers required to resolve the URL
of this bread crumb. The identifiers are obtained from the call
BreadCrumbTrail.leading_to().
"""
self.name = name
self.view = None
self.parent = parent
self.needs = needs or []
def __repr__(self):
return "<BreadCrumb name=%r view=%r parent=%r>" % (
self.name, self.view, self.parent)
def __call__(self, view):
"""
Call method, used when decorating function-based views
Id does not redefine the function (so is not a real decorator) but
instead stores the bread crumb object in the _bread_crumb attribute of
the function.
"""
self.view = view
view._bread_crumb = self
return view
def get_name(self, kwargs):
"""
Get the name of this crumb.
The name is formatted with the specified keyword arguments.
"""
try:
return self.name.format(**kwargs)
except:
logging.exception(
"Unable to construct breadcrumb name for view %r", self.view)
raise
def get_absolute_url(self, kwargs):
"""
Get the URL of this crumb.
The URL is constructed with a call to Dajngo's reverse() function. It
is supplemented with the same variables that were listed in needs array
in the bread crumb constructor. The arguments are passed in order, from
the kwargs dictionary.
"""
try:
return reverse(
self.view,
args=[kwargs[name] for name in self.needs])
except:
logging.exception(
"Unable to construct breadcrumb URL for view %r", self.view)
raise
class LiveBreadCrumb(object):
"""
Bread crumb instance as observed by a particular request.
It is a binding between the global view-specific bread crumb object and
dynamic request-specific keyword arguments.
For convenience it provides two bread crumb functions (get_name() and
get_absolute_url()) that automatically provide the correct keyword
arguments.
"""
def __init__(self, bread_crumb, kwargs):
self.bread_crumb = bread_crumb
self.kwargs = kwargs
def __unicode__(self):
return self.get_name()
def get_name(self):
return self.bread_crumb.get_name(self.kwargs)
def get_absolute_url(self):
return self.bread_crumb.get_absolute_url(self.kwargs)
class BreadCrumbTrail(object):
"""
A list of live bread crumbs that lead from a particular view, along the
parent chain, all the way to the root view (that is without any parent
view).
"""
def __init__(self, bread_crumb_list, kwargs):
self.bread_crumb_list = bread_crumb_list
self.kwargs = kwargs
def __iter__(self):
for bread_crumb in self.bread_crumb_list:
yield LiveBreadCrumb(bread_crumb, self.kwargs)
@classmethod
def leading_to(cls, view, **kwargs):
"""
Create an instance of BreadCrumbTrail that starts at the specified
view.
Additional keyword arguments, if provided, will be available to
get_name() and get_absolute_url() of each LiveBreadCrumb that makes up
this trail. In practice they should contain a set of arguments that are
needed by any parent bread crumb URL or name.
TODO: could we check this statically somehow?
"""
lst = []
while view is not None:
lst.append(view._bread_crumb)
view = view._bread_crumb.parent
lst.reverse()
return cls(lst, kwargs or {})
@classmethod
def show_help(cls, view, **kwargs):
"""
Create a context-sensitive help string from this crumb.
The URL is constructed with a call to Dajngo's reverse() function. It
is supplemented with the same variables that were listed in needs array
in the bread crumb constructor. The arguments are passed in order, from
the kwargs dictionary.
"""
lst = []
while view is not None:
lst.append(view._bread_crumb)
view = view._bread_crumb.parent
lst.reverse()
return cls(lst, kwargs or {})
|