Show More
@@ -0,0 +1,15 | |||||
|
1 | changeset = '{date|shortdate} {author|person} <{author|email}> ({node|short}{tags})\n\n\t* {files|stringify|fill68|tabindent}{desc|fill68|tabindent|strip}\n\n' | |||
|
2 | changeset_quiet = '{date|shortdate} {author|person} <{author|email}>\n\n\t* {desc|firstline|fill68|tabindent|strip}\n\n' | |||
|
3 | changeset_verbose = '{date|isodate} {author|person} <{author|email}> ({node|short}{tags})\n\n\t* {file_adds|stringify|fill68|tabindent}{file_dels|stringify|fill68|tabindent}{files|stringify|fill68|tabindent}{desc|fill68|tabindent|strip}\n\n' | |||
|
4 | start_tags = ' [' | |||
|
5 | tag = '{tag}, ' | |||
|
6 | last_tag = '{tag}]' | |||
|
7 | start_files = '(' | |||
|
8 | file = '{file}, ' | |||
|
9 | last_file = '{file}):\n\t' | |||
|
10 | start_file_adds = '(' | |||
|
11 | file_add = '{file_add}, ' | |||
|
12 | last_file_add = '{file_add}): new file.\n* ' | |||
|
13 | start_file_dels = '(' | |||
|
14 | file_del = '{file_del}, ' | |||
|
15 | last_file_del = '{file_del}): deleted file.\n* ' |
@@ -8,7 +8,7 | |||||
8 | import re |
|
8 | import re | |
9 | from demandload import demandload |
|
9 | from demandload import demandload | |
10 | from i18n import gettext as _ |
|
10 | from i18n import gettext as _ | |
11 | demandload(globals(), "cStringIO cgi sys os time urllib util") |
|
11 | demandload(globals(), "cStringIO cgi re sys os time urllib util textwrap") | |
12 |
|
12 | |||
13 | esctable = { |
|
13 | esctable = { | |
14 | '\\': '\\', |
|
14 | '\\': '\\', | |
@@ -181,8 +181,43 def age(date): | |||||
181 | if n >= 2 or s == 1: |
|
181 | if n >= 2 or s == 1: | |
182 | return fmt(t, n) |
|
182 | return fmt(t, n) | |
183 |
|
183 | |||
|
184 | def stringify(thing): | |||
|
185 | '''turn nested template iterator into string.''' | |||
|
186 | cs = cStringIO.StringIO() | |||
|
187 | def walk(things): | |||
|
188 | for t in things: | |||
|
189 | if hasattr(t, '__iter__'): | |||
|
190 | walk(t) | |||
|
191 | else: | |||
|
192 | cs.write(t) | |||
|
193 | walk(thing) | |||
|
194 | return cs.getvalue() | |||
|
195 | ||||
|
196 | para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M) | |||
|
197 | space_re = re.compile(r' +') | |||
|
198 | ||||
|
199 | def fill(text, width): | |||
|
200 | '''fill many paragraphs.''' | |||
|
201 | def findparas(): | |||
|
202 | start = 0 | |||
|
203 | while True: | |||
|
204 | m = para_re.search(text, start) | |||
|
205 | if not m: | |||
|
206 | w = len(text) | |||
|
207 | while w > start and text[w-1].isspace(): w -= 1 | |||
|
208 | yield text[start:w], text[w:] | |||
|
209 | break | |||
|
210 | yield text[start:m.start(0)], m.group(1) | |||
|
211 | start = m.end(1) | |||
|
212 | ||||
|
213 | fp = cStringIO.StringIO() | |||
|
214 | for para, rest in findparas(): | |||
|
215 | fp.write(space_re.sub(' ', textwrap.fill(para, width))) | |||
|
216 | fp.write(rest) | |||
|
217 | return fp.getvalue() | |||
|
218 | ||||
184 | def isodate(date): |
|
219 | def isodate(date): | |
185 | '''turn a (timestamp, tzoff) tuple into an iso 8631 date.''' |
|
220 | '''turn a (timestamp, tzoff) tuple into an iso 8631 date and time.''' | |
186 | return util.datestr(date, format='%Y-%m-%d %H:%M') |
|
221 | return util.datestr(date, format='%Y-%m-%d %H:%M') | |
187 |
|
222 | |||
188 | def nl2br(text): |
|
223 | def nl2br(text): | |
@@ -201,25 +236,54 def domain(author): | |||||
201 | if f >= 0: author = author[:f] |
|
236 | if f >= 0: author = author[:f] | |
202 | return author |
|
237 | return author | |
203 |
|
238 | |||
|
239 | def email(author): | |||
|
240 | '''get email of author.''' | |||
|
241 | r = author.find('>') | |||
|
242 | if r == -1: r = None | |||
|
243 | return author[author.find('<')+1:r] | |||
|
244 | ||||
204 | def person(author): |
|
245 | def person(author): | |
205 | '''get name of author, or else username.''' |
|
246 | '''get name of author, or else username.''' | |
206 | f = author.find('<') |
|
247 | f = author.find('<') | |
207 | if f == -1: return util.shortuser(author) |
|
248 | if f == -1: return util.shortuser(author) | |
208 | return author[:f].rstrip() |
|
249 | return author[:f].rstrip() | |
209 |
|
250 | |||
|
251 | def shortdate(date): | |||
|
252 | '''turn (timestamp, tzoff) tuple into iso 8631 date.''' | |||
|
253 | return util.datestr(date, format='%Y-%m-%d', timezone=False) | |||
|
254 | ||||
|
255 | def indent(text, prefix): | |||
|
256 | '''indent each non-empty line of text after first with prefix.''' | |||
|
257 | fp = cStringIO.StringIO() | |||
|
258 | lines = text.splitlines() | |||
|
259 | num_lines = len(lines) | |||
|
260 | for i in xrange(num_lines): | |||
|
261 | l = lines[i] | |||
|
262 | if i and l.strip(): fp.write(prefix) | |||
|
263 | fp.write(l) | |||
|
264 | if i < num_lines - 1 or text.endswith('\n'): | |||
|
265 | fp.write('\n') | |||
|
266 | return fp.getvalue() | |||
|
267 | ||||
210 | common_filters = { |
|
268 | common_filters = { | |
211 | "addbreaks": nl2br, |
|
269 | "addbreaks": nl2br, | |
212 | "age": age, |
|
270 | "age": age, | |
213 | "date": lambda x: util.datestr(x), |
|
271 | "date": lambda x: util.datestr(x), | |
214 | "domain": domain, |
|
272 | "domain": domain, | |
|
273 | "email": email, | |||
215 | "escape": lambda x: cgi.escape(x, True), |
|
274 | "escape": lambda x: cgi.escape(x, True), | |
|
275 | "fill68": lambda x: fill(x, width=68), | |||
|
276 | "fill76": lambda x: fill(x, width=76), | |||
216 | "firstline": lambda x: x.splitlines(1)[0].rstrip('\r\n'), |
|
277 | "firstline": lambda x: x.splitlines(1)[0].rstrip('\r\n'), | |
|
278 | "tabindent": lambda x: indent(x, '\t'), | |||
217 | "isodate": isodate, |
|
279 | "isodate": isodate, | |
218 | "obfuscate": obfuscate, |
|
280 | "obfuscate": obfuscate, | |
219 | "permissions": lambda x: x and "-rwxr-xr-x" or "-rw-r--r--", |
|
281 | "permissions": lambda x: x and "-rwxr-xr-x" or "-rw-r--r--", | |
220 | "person": person, |
|
282 | "person": person, | |
221 | "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S"), |
|
283 | "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S"), | |
222 | "short": lambda x: x[:12], |
|
284 | "short": lambda x: x[:12], | |
|
285 | "shortdate": shortdate, | |||
|
286 | "stringify": stringify, | |||
223 | "strip": lambda x: x.strip(), |
|
287 | "strip": lambda x: x.strip(), | |
224 | "urlescape": lambda x: urllib.quote(x), |
|
288 | "urlescape": lambda x: urllib.quote(x), | |
225 | "user": lambda x: util.shortuser(x), |
|
289 | "user": lambda x: util.shortuser(x), |
@@ -744,15 +744,16 def makedate(): | |||||
744 | tz = time.timezone |
|
744 | tz = time.timezone | |
745 | return time.mktime(lt), tz |
|
745 | return time.mktime(lt), tz | |
746 |
|
746 | |||
747 | def datestr(date=None, format='%a %b %d %H:%M:%S %Y'): |
|
747 | def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True): | |
748 | """represent a (unixtime, offset) tuple as a localized time. |
|
748 | """represent a (unixtime, offset) tuple as a localized time. | |
749 | unixtime is seconds since the epoch, and offset is the time zone's |
|
749 | unixtime is seconds since the epoch, and offset is the time zone's | |
750 |
number of seconds away from UTC. |
|
750 | number of seconds away from UTC. if timezone is false, do not | |
|
751 | append time zone to string.""" | |||
751 | t, tz = date or makedate() |
|
752 | t, tz = date or makedate() | |
752 | return ("%s %+03d%02d" % |
|
753 | s = time.strftime(format, time.gmtime(float(t) - tz)) | |
753 | (time.strftime(format, time.gmtime(float(t) - tz)), |
|
754 | if timezone: | |
754 | -tz / 3600, |
|
755 | s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60)) | |
755 | ((-tz % 3600) / 60))) |
|
756 | return s | |
756 |
|
757 | |||
757 | def shortuser(user): |
|
758 | def shortuser(user): | |
758 | """Return a short representation of a user name or email address.""" |
|
759 | """Return a short representation of a user name or email address.""" |
General Comments 0
You need to be logged in to leave comments.
Login now