##// END OF EJS Templates
add iso date template filter.
Vadim Gelfer -
r1906:9dec2479 default
parent child Browse files
Show More
@@ -1,189 +1,193
1 import re
1 import re
2 from demandload import demandload
2 from demandload import demandload
3 from i18n import gettext as _
3 from i18n import gettext as _
4 demandload(globals(), "cStringIO cgi os time urllib util")
4 demandload(globals(), "cStringIO cgi os time urllib util")
5
5
6 esctable = {
6 esctable = {
7 '\\': '\\',
7 '\\': '\\',
8 'r': '\r',
8 'r': '\r',
9 't': '\t',
9 't': '\t',
10 'n': '\n',
10 'n': '\n',
11 'v': '\v',
11 'v': '\v',
12 }
12 }
13
13
14 def parsestring(s, quoted=True):
14 def parsestring(s, quoted=True):
15 fp = cStringIO.StringIO()
15 fp = cStringIO.StringIO()
16 if quoted:
16 if quoted:
17 first = s[0]
17 first = s[0]
18 if len(s) < 2: raise SyntaxError(_('string too short'))
18 if len(s) < 2: raise SyntaxError(_('string too short'))
19 if first not in "'\"": raise SyntaxError(_('invalid quote'))
19 if first not in "'\"": raise SyntaxError(_('invalid quote'))
20 if s[-1] != first: raise SyntaxError(_('unmatched quotes'))
20 if s[-1] != first: raise SyntaxError(_('unmatched quotes'))
21 s = s[1:-1]
21 s = s[1:-1]
22 escape = False
22 escape = False
23 for c in s:
23 for c in s:
24 if escape:
24 if escape:
25 fp.write(esctable.get(c, c))
25 fp.write(esctable.get(c, c))
26 escape = False
26 escape = False
27 elif c == '\\': escape = True
27 elif c == '\\': escape = True
28 elif quoted and c == first: raise SyntaxError(_('string ends early'))
28 elif quoted and c == first: raise SyntaxError(_('string ends early'))
29 else: fp.write(c)
29 else: fp.write(c)
30 if escape: raise SyntaxError(_('unterminated escape'))
30 if escape: raise SyntaxError(_('unterminated escape'))
31 return fp.getvalue()
31 return fp.getvalue()
32
32
33 class templater(object):
33 class templater(object):
34 def __init__(self, mapfile, filters={}, defaults={}):
34 def __init__(self, mapfile, filters={}, defaults={}):
35 self.mapfile = mapfile or 'template'
35 self.mapfile = mapfile or 'template'
36 self.cache = {}
36 self.cache = {}
37 self.map = {}
37 self.map = {}
38 self.base = (mapfile and os.path.dirname(mapfile)) or ''
38 self.base = (mapfile and os.path.dirname(mapfile)) or ''
39 self.filters = filters
39 self.filters = filters
40 self.defaults = defaults
40 self.defaults = defaults
41
41
42 if not mapfile:
42 if not mapfile:
43 return
43 return
44 i = 0
44 i = 0
45 for l in file(mapfile):
45 for l in file(mapfile):
46 l = l.rstrip('\r\n')
46 l = l.rstrip('\r\n')
47 i += 1
47 i += 1
48 if l.startswith('#') or not l.strip(): continue
48 if l.startswith('#') or not l.strip(): continue
49 m = re.match(r'([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.+)$', l)
49 m = re.match(r'([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.+)$', l)
50 if m:
50 if m:
51 key, val = m.groups()
51 key, val = m.groups()
52 if val[0] in "'\"":
52 if val[0] in "'\"":
53 try:
53 try:
54 self.cache[key] = parsestring(val)
54 self.cache[key] = parsestring(val)
55 except SyntaxError, inst:
55 except SyntaxError, inst:
56 raise SyntaxError('%s:%s: %s' %
56 raise SyntaxError('%s:%s: %s' %
57 (mapfile, i, inst.args[0]))
57 (mapfile, i, inst.args[0]))
58 else:
58 else:
59 self.map[key] = os.path.join(self.base, val)
59 self.map[key] = os.path.join(self.base, val)
60 else:
60 else:
61 raise SyntaxError(_("%s:%s: parse error") % (mapfile, i))
61 raise SyntaxError(_("%s:%s: parse error") % (mapfile, i))
62
62
63 def __contains__(self, key):
63 def __contains__(self, key):
64 return key in self.cache
64 return key in self.cache
65
65
66 def __call__(self, t, **map):
66 def __call__(self, t, **map):
67 m = self.defaults.copy()
67 m = self.defaults.copy()
68 m.update(map)
68 m.update(map)
69 try:
69 try:
70 tmpl = self.cache[t]
70 tmpl = self.cache[t]
71 except KeyError:
71 except KeyError:
72 try:
72 try:
73 tmpl = self.cache[t] = file(self.map[t]).read()
73 tmpl = self.cache[t] = file(self.map[t]).read()
74 except IOError, inst:
74 except IOError, inst:
75 raise IOError(inst.args[0], _('template file %s: %s') %
75 raise IOError(inst.args[0], _('template file %s: %s') %
76 (self.map[t], inst.args[1]))
76 (self.map[t], inst.args[1]))
77 return self.template(tmpl, self.filters, **m)
77 return self.template(tmpl, self.filters, **m)
78
78
79 template_re = re.compile(r"[#{]([a-zA-Z_][a-zA-Z0-9_]*)"
79 template_re = re.compile(r"[#{]([a-zA-Z_][a-zA-Z0-9_]*)"
80 r"((%[a-zA-Z_][a-zA-Z0-9_]*)*)"
80 r"((%[a-zA-Z_][a-zA-Z0-9_]*)*)"
81 r"((\|[a-zA-Z_][a-zA-Z0-9_]*)*)[#}]")
81 r"((\|[a-zA-Z_][a-zA-Z0-9_]*)*)[#}]")
82
82
83 def template(self, tmpl, filters={}, **map):
83 def template(self, tmpl, filters={}, **map):
84 lm = map.copy()
84 lm = map.copy()
85 while tmpl:
85 while tmpl:
86 m = self.template_re.search(tmpl)
86 m = self.template_re.search(tmpl)
87 if m:
87 if m:
88 start, end = m.span(0)
88 start, end = m.span(0)
89 s, e = tmpl[start], tmpl[end - 1]
89 s, e = tmpl[start], tmpl[end - 1]
90 key = m.group(1)
90 key = m.group(1)
91 if ((s == '#' and e != '#') or (s == '{' and e != '}')):
91 if ((s == '#' and e != '#') or (s == '{' and e != '}')):
92 raise SyntaxError(_("'%s'/'%s' mismatch expanding '%s'") %
92 raise SyntaxError(_("'%s'/'%s' mismatch expanding '%s'") %
93 (s, e, key))
93 (s, e, key))
94 if start:
94 if start:
95 yield tmpl[:start]
95 yield tmpl[:start]
96 v = map.get(key, "")
96 v = map.get(key, "")
97 v = callable(v) and v(**map) or v
97 v = callable(v) and v(**map) or v
98
98
99 format = m.group(2)
99 format = m.group(2)
100 fl = m.group(4)
100 fl = m.group(4)
101
101
102 if format:
102 if format:
103 q = v.__iter__
103 q = v.__iter__
104 for i in q():
104 for i in q():
105 lm.update(i)
105 lm.update(i)
106 yield self(format[1:], **lm)
106 yield self(format[1:], **lm)
107
107
108 v = ""
108 v = ""
109
109
110 elif fl:
110 elif fl:
111 for f in fl.split("|")[1:]:
111 for f in fl.split("|")[1:]:
112 v = filters[f](v)
112 v = filters[f](v)
113
113
114 yield v
114 yield v
115 tmpl = tmpl[end:]
115 tmpl = tmpl[end:]
116 else:
116 else:
117 yield tmpl
117 yield tmpl
118 break
118 break
119
119
120 agescales = [("second", 1),
120 agescales = [("second", 1),
121 ("minute", 60),
121 ("minute", 60),
122 ("hour", 3600),
122 ("hour", 3600),
123 ("day", 3600 * 24),
123 ("day", 3600 * 24),
124 ("week", 3600 * 24 * 7),
124 ("week", 3600 * 24 * 7),
125 ("month", 3600 * 24 * 30),
125 ("month", 3600 * 24 * 30),
126 ("year", 3600 * 24 * 365)]
126 ("year", 3600 * 24 * 365)]
127
127
128 agescales.reverse()
128 agescales.reverse()
129
129
130 def age(x):
130 def age(x):
131 def plural(t, c):
131 def plural(t, c):
132 if c == 1:
132 if c == 1:
133 return t
133 return t
134 return t + "s"
134 return t + "s"
135 def fmt(t, c):
135 def fmt(t, c):
136 return "%d %s" % (c, plural(t, c))
136 return "%d %s" % (c, plural(t, c))
137
137
138 now = time.time()
138 now = time.time()
139 then = x[0]
139 then = x[0]
140 delta = max(1, int(now - then))
140 delta = max(1, int(now - then))
141
141
142 for t, s in agescales:
142 for t, s in agescales:
143 n = delta / s
143 n = delta / s
144 if n >= 2 or s == 1:
144 if n >= 2 or s == 1:
145 return fmt(t, n)
145 return fmt(t, n)
146
146
147 def isodate(date):
148 return util.datestr(date, format='%Y-%m-%d %H:%M')
149
147 def nl2br(text):
150 def nl2br(text):
148 return text.replace('\n', '<br/>\n')
151 return text.replace('\n', '<br/>\n')
149
152
150 def obfuscate(text):
153 def obfuscate(text):
151 return ''.join(['&#%d;' % ord(c) for c in text])
154 return ''.join(['&#%d;' % ord(c) for c in text])
152
155
153 def domain(author):
156 def domain(author):
154 f = author.find('@')
157 f = author.find('@')
155 if f == -1: return ''
158 if f == -1: return ''
156 author = author[f+1:]
159 author = author[f+1:]
157 f = author.find('>')
160 f = author.find('>')
158 if f >= 0: author = author[:f]
161 if f >= 0: author = author[:f]
159 return author
162 return author
160
163
161 def person(author):
164 def person(author):
162 f = author.find('<')
165 f = author.find('<')
163 if f == -1: return util.shortuser(author)
166 if f == -1: return util.shortuser(author)
164 return author[:f].rstrip()
167 return author[:f].rstrip()
165
168
166 common_filters = {
169 common_filters = {
167 "addbreaks": nl2br,
170 "addbreaks": nl2br,
168 "age": age,
171 "age": age,
169 "date": lambda x: util.datestr(x),
172 "date": lambda x: util.datestr(x),
170 "domain": domain,
173 "domain": domain,
171 "escape": lambda x: cgi.escape(x, True),
174 "escape": lambda x: cgi.escape(x, True),
172 "firstline": (lambda x: x.splitlines(1)[0]),
175 "firstline": (lambda x: x.splitlines(1)[0]),
176 "isodate": isodate,
173 "obfuscate": obfuscate,
177 "obfuscate": obfuscate,
174 "permissions": (lambda x: x and "-rwxr-xr-x" or "-rw-r--r--"),
178 "permissions": (lambda x: x and "-rwxr-xr-x" or "-rw-r--r--"),
175 "person": person,
179 "person": person,
176 "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S"),
180 "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S"),
177 "short": (lambda x: x[:12]),
181 "short": (lambda x: x[:12]),
178 "strip": lambda x: x.strip(),
182 "strip": lambda x: x.strip(),
179 "urlescape": urllib.quote,
183 "urlescape": urllib.quote,
180 "user": util.shortuser,
184 "user": util.shortuser,
181 }
185 }
182
186
183 def templatepath(name=None):
187 def templatepath(name=None):
184 for f in 'templates', '../templates':
188 for f in 'templates', '../templates':
185 fl = f.split('/')
189 fl = f.split('/')
186 if name: fl.append(name)
190 if name: fl.append(name)
187 p = os.path.join(os.path.dirname(__file__), *fl)
191 p = os.path.join(os.path.dirname(__file__), *fl)
188 if (name and os.path.exists(p)) or os.path.isdir(p):
192 if (name and os.path.exists(p)) or os.path.isdir(p):
189 return os.path.normpath(p)
193 return os.path.normpath(p)
General Comments 0
You need to be logged in to leave comments. Login now