138
137
def __init__(self, _dict=None, **kw):
139
self._properties = {}
140
138
if _dict is not None:
141
139
for key, value in _dict.iteritems():
142
140
setattr(self, key, value)
156
def __getattr__(self, attr):
157
"""Used for handling things that aren't already available."""
158
if attr.startswith('_') or attr not in self._properties:
159
raise AttributeError('No attribute: %s' % (attr,))
160
val = self._properties[attr](self, attr)
161
setattr(self, attr, val)
164
def _set_property(self, attr, prop_func):
165
"""Set a function that will be called when an attribute is desired.
167
We will cache the return value, so the function call should be
168
idempotent. We will pass 'self' and the 'attr' name when triggered.
170
if attr.startswith('_'):
171
raise ValueError("Cannot create properties that start with _")
172
self._properties[attr] = prop_func
175
155
def trunc(text, limit=10):
176
156
if len(text) <= limit:
213
193
# only do this if unicode turns out to be a problem
214
194
#_BADCHARS_RE = re.compile(ur'[\u007f-\uffff]')
216
# Can't be a dict; & needs to be done first.
220
("'", "'"), # ' is defined in XML, but not HTML.
227
"""Transform dangerous (X)HTML characters into entities.
229
Like cgi.escape, except also escaping " and '. This makes it safe to use
230
in both attribute and element content.
232
If you want to safely fill a format string with escaped values, use
235
for char, repl in html_entity_subs:
236
s = s.replace(char, repl)
240
def html_format(template, *args):
241
"""Safely format an HTML template string, escaping the arguments.
243
The template string must not be user-controlled; it will not be escaped.
245
return template % tuple(html_escape(arg) for arg in args)
248
196
# FIXME: get rid of this method; use fixed_width() and avoid XML().
250
199
def html_clean(s):
252
201
clean up a string for html display. expand any tabs, encode any html
253
202
entities, and replace spaces with ' '. this is primarily for use
254
203
in displaying monospace text.
256
s = html_escape(s.expandtabs())
205
s = cgi.escape(s.expandtabs())
257
206
s = s.replace(' ', ' ')
299
248
except UnicodeDecodeError:
300
249
s = s.decode('iso-8859-15')
302
s = html_escape(s).expandtabs().replace(' ', NONBREAKING_SPACE)
251
s = cgi.escape(s).expandtabs().replace(' ', NONBREAKING_SPACE)
304
253
return HSC.clean(s).replace('\n', '<br/>')
378
def local_path_from_url(url):
379
"""Convert Bazaar URL to local path, ignoring readonly+ prefix"""
380
readonly_prefix = 'readonly+'
381
if url.startswith(readonly_prefix):
382
url = url[len(readonly_prefix):]
383
return urlutils.local_path_from_url(url)
386
327
def fill_in_navigation(navigation):
388
329
given a navigation block (used by the template for the page header), fill