12 |
12 |
13 import os, cgi, time, re, difflib, sys, zlib |
13 import os, cgi, time, re, difflib, sys, zlib |
14 from mercurial.hg import * |
14 from mercurial.hg import * |
15 |
15 |
16 def templatepath(): |
16 def templatepath(): |
17 for f in "templates/map", "../templates/map": |
17 for f in "templates", "../templates": |
18 p = os.path.join(os.path.dirname(__file__), f) |
18 p = os.path.join(os.path.dirname(__file__), f) |
19 if os.path.isfile(p): return p |
19 if os.path.isdir(p): return p |
20 |
20 |
21 def age(t): |
21 def age(t): |
22 def plural(t, c): |
22 def plural(t, c): |
23 if c == 1: return t |
23 if c == 1: return t |
24 return t + "s" |
24 return t + "s" |
65 for part in thing: |
65 for part in thing: |
66 write(part) |
66 write(part) |
67 else: |
67 else: |
68 sys.stdout.write(str(thing)) |
68 sys.stdout.write(str(thing)) |
69 |
69 |
70 def template(tmpl, **map): |
70 def template(tmpl, filters = {}, **map): |
71 while tmpl: |
71 while tmpl: |
72 m = re.search(r"#([a-zA-Z0-9]+)#", tmpl) |
72 m = re.search(r"#([a-zA-Z0-9]+)((\|[a-zA-Z0-9]+)*)#", tmpl) |
73 if m: |
73 if m: |
74 yield tmpl[:m.start(0)] |
74 yield tmpl[:m.start(0)] |
75 v = map.get(m.group(1), "") |
75 v = map.get(m.group(1), "") |
76 yield callable(v) and v() or v |
76 v = callable(v) and v() or v |
|
77 |
|
78 fl = m.group(2) |
|
79 if fl: |
|
80 for f in fl.split("|")[1:]: |
|
81 v = filters[f](v) |
|
82 |
|
83 yield v |
77 tmpl = tmpl[m.end(0):] |
84 tmpl = tmpl[m.end(0):] |
78 else: |
85 else: |
79 yield tmpl |
86 yield tmpl |
80 return |
87 return |
81 |
88 |
82 class templater: |
89 class templater: |
83 def __init__(self, mapfile): |
90 def __init__(self, mapfile, filters = {}): |
84 self.cache = {} |
91 self.cache = {} |
85 self.map = {} |
92 self.map = {} |
86 self.base = os.path.dirname(mapfile) |
93 self.base = os.path.dirname(mapfile) |
|
94 self.filters = filters |
87 |
95 |
88 for l in file(mapfile): |
96 for l in file(mapfile): |
89 m = re.match(r'(\S+)\s*=\s*"(.*)"$', l) |
97 m = re.match(r'(\S+)\s*=\s*"(.*)"$', l) |
90 if m: |
98 if m: |
91 self.cache[m.group(1)] = m.group(2) |
99 self.cache[m.group(1)] = m.group(2) |
99 def __call__(self, t, **map): |
107 def __call__(self, t, **map): |
100 try: |
108 try: |
101 tmpl = self.cache[t] |
109 tmpl = self.cache[t] |
102 except KeyError: |
110 except KeyError: |
103 tmpl = self.cache[t] = file(self.map[t]).read() |
111 tmpl = self.cache[t] = file(self.map[t]).read() |
104 return template(tmpl, **map) |
112 return template(tmpl, self.filters, **map) |
105 |
113 |
106 class hgweb: |
114 class hgweb: |
107 maxchanges = 20 |
115 maxchanges = 20 |
108 maxfiles = 10 |
116 maxfiles = 10 |
109 |
117 |
110 def __init__(self, path, name, templatemap = ""): |
118 def __init__(self, path, name, templates = ""): |
111 templatemap = templatemap or templatepath() |
119 self.templates = templates or templatepath() |
112 |
|
113 self.reponame = name |
120 self.reponame = name |
114 self.repo = repository(ui(), path) |
121 self.repo = repository(ui(), path) |
115 self.t = templater(templatemap) |
|
116 self.viewonly = 0 |
122 self.viewonly = 0 |
|
123 |
|
124 self.filters = { |
|
125 "escape": cgi.escape, |
|
126 "age": age, |
|
127 "date": (lambda x: time.asctime(time.gmtime(x))), |
|
128 "addbreaks": nl2br, |
|
129 "obfuscate": obfuscate, |
|
130 "firstline": (lambda x: x.splitlines(1)[0]), |
|
131 } |
117 |
132 |
118 def date(self, cs): |
133 def date(self, cs): |
119 return time.asctime(time.gmtime(float(cs[2].split(' ')[0]))) |
134 return time.asctime(time.gmtime(float(cs[2].split(' ')[0]))) |
120 |
135 |
121 def listfiles(self, files, mf): |
136 def listfiles(self, files, mf): |
152 filenode = hex(fn)) |
167 filenode = hex(fn)) |
153 parity[0] = 1 - parity[0] |
168 parity[0] = 1 - parity[0] |
154 |
169 |
155 def prettyprintlines(diff): |
170 def prettyprintlines(diff): |
156 for l in diff.splitlines(1): |
171 for l in diff.splitlines(1): |
157 line = cgi.escape(l) |
172 if l.startswith('+'): |
158 if line.startswith('+'): |
173 yield self.t("difflineplus", line = l) |
159 yield self.t("difflineplus", line = line) |
174 elif l.startswith('-'): |
160 elif line.startswith('-'): |
175 yield self.t("difflineminus", line = l) |
161 yield self.t("difflineminus", line = line) |
176 elif l.startswith('@'): |
162 elif line.startswith('@'): |
177 yield self.t("difflineat", line = l) |
163 yield self.t("difflineat", line = line) |
|
164 else: |
178 else: |
165 yield self.t("diffline", line = line) |
179 yield self.t("diffline", line = l) |
166 |
180 |
167 r = self.repo |
181 r = self.repo |
168 cl = r.changelog |
182 cl = r.changelog |
169 mf = r.manifest |
183 mf = r.manifest |
170 change1 = cl.read(node1) |
184 change1 = cl.read(node1) |
232 t = float(changes[2].split(' ')[0]) |
246 t = float(changes[2].split(' ')[0]) |
233 |
247 |
234 l.insert(0, self.t( |
248 l.insert(0, self.t( |
235 'changelogentry', |
249 'changelogentry', |
236 parity = parity, |
250 parity = parity, |
237 author = obfuscate(changes[1]), |
251 author = changes[1], |
238 shortdesc = cgi.escape(changes[4].splitlines()[0]), |
|
239 age = age(t), |
|
240 parent1 = self.parent("changelogparent", |
252 parent1 = self.parent("changelogparent", |
241 hex(p1), cl.rev(p1)), |
253 hex(p1), cl.rev(p1)), |
242 parent2 = self.parent("changelogparent", |
254 parent2 = self.parent("changelogparent", |
243 hex(p2), cl.rev(p2)), |
255 hex(p2), cl.rev(p2)), |
244 p1 = hex(p1), p2 = hex(p2), |
256 p1 = hex(p1), p2 = hex(p2), |
245 p1rev = cl.rev(p1), p2rev = cl.rev(p2), |
257 p1rev = cl.rev(p1), p2rev = cl.rev(p2), |
246 manifest = hex(changes[0]), |
258 manifest = hex(changes[0]), |
247 desc = nl2br(cgi.escape(changes[4])), |
259 desc = changes[4], |
248 date = time.asctime(time.gmtime(t)), |
260 date = t, |
249 files = self.listfilediffs(changes[3], n), |
261 files = self.listfilediffs(changes[3], n), |
250 rev = i, |
262 rev = i, |
251 node = hn)) |
263 node = hn)) |
252 parity = 1 - parity |
264 parity = 1 - parity |
253 |
265 |
290 footer = self.footer(), |
302 footer = self.footer(), |
291 repo = self.reponame, |
303 repo = self.reponame, |
292 diff = diff, |
304 diff = diff, |
293 rev = cl.rev(n), |
305 rev = cl.rev(n), |
294 node = nodeid, |
306 node = nodeid, |
295 shortdesc = cgi.escape(changes[4].splitlines()[0]), |
|
296 parent1 = self.parent("changesetparent", |
307 parent1 = self.parent("changesetparent", |
297 hex(p1), cl.rev(p1)), |
308 hex(p1), cl.rev(p1)), |
298 parent2 = self.parent("changesetparent", |
309 parent2 = self.parent("changesetparent", |
299 hex(p2), cl.rev(p2)), |
310 hex(p2), cl.rev(p2)), |
300 p1 = hex(p1), p2 = hex(p2), |
311 p1 = hex(p1), p2 = hex(p2), |
301 p1rev = cl.rev(p1), p2rev = cl.rev(p2), |
312 p1rev = cl.rev(p1), p2rev = cl.rev(p2), |
302 manifest = hex(changes[0]), |
313 manifest = hex(changes[0]), |
303 author = obfuscate(changes[1]), |
314 author = changes[1], |
304 desc = nl2br(cgi.escape(changes[4])), |
315 desc = changes[4], |
305 date = time.asctime(time.gmtime(t)), |
316 date = t, |
306 files = files) |
317 files = files) |
307 |
318 |
308 def filelog(self, f, filenode): |
319 def filelog(self, f, filenode): |
309 cl = self.repo.changelog |
320 cl = self.repo.changelog |
310 fl = self.repo.file(f) |
321 fl = self.repo.file(f) |
327 parity = parity, |
338 parity = parity, |
328 filenode = hex(n), |
339 filenode = hex(n), |
329 filerev = i, |
340 filerev = i, |
330 file = f, |
341 file = f, |
331 node = hex(cn), |
342 node = hex(cn), |
332 author = obfuscate(cs[1]), |
343 author = cs[1], |
333 age = age(t), |
344 date = t, |
334 date = time.asctime(time.gmtime(t)), |
345 desc = cs[4], |
335 shortdesc = cgi.escape(cs[4].splitlines()[0]), |
|
336 p1 = hex(p1), p2 = hex(p2), |
346 p1 = hex(p1), p2 = hex(p2), |
337 p1rev = fl.rev(p1), p2rev = fl.rev(p2))) |
347 p1rev = fl.rev(p1), p2rev = fl.rev(p2))) |
338 parity = 1 - parity |
348 parity = 1 - parity |
339 |
349 |
340 yield l |
350 yield l |
348 entries = entries) |
358 entries = entries) |
349 |
359 |
350 def filerevision(self, f, node): |
360 def filerevision(self, f, node): |
351 fl = self.repo.file(f) |
361 fl = self.repo.file(f) |
352 n = bin(node) |
362 n = bin(node) |
353 text = cgi.escape(fl.read(n)) |
363 text = fl.read(n) |
354 changerev = fl.linkrev(n) |
364 changerev = fl.linkrev(n) |
355 cl = self.repo.changelog |
365 cl = self.repo.changelog |
356 cn = cl.node(changerev) |
366 cn = cl.node(changerev) |
357 cs = cl.read(cn) |
367 cs = cl.read(cn) |
358 p1, p2 = fl.parents(n) |
368 p1, p2 = fl.parents(n) |
359 t = float(cs[2].split(' ')[0]) |
369 t = float(cs[2].split(' ')[0]) |
360 mfn = cs[0] |
370 mfn = cs[0] |
361 |
371 |
362 def lines(): |
372 def lines(): |
363 for l, t in enumerate(text.splitlines(1)): |
373 for l, t in enumerate(text.splitlines(1)): |
364 yield self.t("fileline", |
374 yield self.t("fileline", line = t, |
365 line = t, |
|
366 linenumber = "% 6d" % (l + 1), |
375 linenumber = "% 6d" % (l + 1), |
367 parity = l & 1) |
376 parity = l & 1) |
368 |
377 |
369 yield self.t("filerevision", file = f, |
378 yield self.t("filerevision", file = f, |
370 header = self.header(), |
379 header = self.header(), |
374 path = up(f), |
383 path = up(f), |
375 text = lines(), |
384 text = lines(), |
376 rev = changerev, |
385 rev = changerev, |
377 node = hex(cn), |
386 node = hex(cn), |
378 manifest = hex(mfn), |
387 manifest = hex(mfn), |
379 author = obfuscate(cs[1]), |
388 author = cs[1], |
380 age = age(t), |
389 date = t, |
381 date = time.asctime(time.gmtime(t)), |
|
382 shortdesc = cgi.escape(cs[4].splitlines()[0]), |
|
383 parent1 = self.parent("filerevparent", |
390 parent1 = self.parent("filerevparent", |
384 hex(p1), fl.rev(p1), file=f), |
391 hex(p1), fl.rev(p1), file=f), |
385 parent2 = self.parent("filerevparent", |
392 parent2 = self.parent("filerevparent", |
386 hex(p2), fl.rev(p2), file=f), |
393 hex(p2), fl.rev(p2), file=f), |
387 p1 = hex(p1), p2 = hex(p2), |
394 p1 = hex(p1), p2 = hex(p2), |
388 p1rev = fl.rev(p1), p2rev = fl.rev(p2)) |
395 p1rev = fl.rev(p1), p2rev = fl.rev(p2)) |
389 |
|
390 |
396 |
391 def fileannotate(self, f, node): |
397 def fileannotate(self, f, node): |
392 bcache = {} |
398 bcache = {} |
393 ncache = {} |
399 ncache = {} |
394 fl = self.repo.file(f) |
400 fl = self.repo.file(f) |
429 parity = parity, |
435 parity = parity, |
430 node = hex(cnode), |
436 node = hex(cnode), |
431 rev = r, |
437 rev = r, |
432 author = name, |
438 author = name, |
433 file = f, |
439 file = f, |
434 line = cgi.escape(l)) |
440 line = l) |
435 |
441 |
436 yield self.t("fileannotate", |
442 yield self.t("fileannotate", |
437 header = self.header(), |
443 header = self.header(), |
438 footer = self.footer(), |
444 footer = self.footer(), |
439 repo = self.reponame, |
445 repo = self.reponame, |
442 annotate = annotate, |
448 annotate = annotate, |
443 path = up(f), |
449 path = up(f), |
444 rev = changerev, |
450 rev = changerev, |
445 node = hex(cn), |
451 node = hex(cn), |
446 manifest = hex(mfn), |
452 manifest = hex(mfn), |
447 author = obfuscate(cs[1]), |
453 author = cs[1], |
448 age = age(t), |
454 date = t, |
449 date = time.asctime(time.gmtime(t)), |
|
450 shortdesc = cgi.escape(cs[4].splitlines()[0]), |
|
451 parent1 = self.parent("fileannotateparent", |
455 parent1 = self.parent("fileannotateparent", |
452 hex(p1), fl.rev(p1), file=f), |
456 hex(p1), fl.rev(p1), file=f), |
453 parent2 = self.parent("fileannotateparent", |
457 parent2 = self.parent("fileannotateparent", |
454 hex(p2), fl.rev(p2), file=f), |
458 hex(p2), fl.rev(p2), file=f), |
455 p1 = hex(p1), p2 = hex(p2), |
459 p1 = hex(p1), p2 = hex(p2), |
561 # find tag, changeset, file |
565 # find tag, changeset, file |
562 |
566 |
563 def run(self): |
567 def run(self): |
564 args = cgi.parse() |
568 args = cgi.parse() |
565 |
569 |
|
570 m = os.path.join(self.templates, "map") |
|
571 if args.has_key('style'): |
|
572 b = os.path.basename("map-" + args['style'][0]) |
|
573 p = os.path.join(self.templates, b) |
|
574 if os.path.isfile(p): m = p |
|
575 |
|
576 self.t = templater(m, self.filters) |
|
577 |
566 if not args.has_key('cmd') or args['cmd'][0] == 'changelog': |
578 if not args.has_key('cmd') or args['cmd'][0] == 'changelog': |
567 hi = self.repo.changelog.count() |
579 hi = self.repo.changelog.count() |
568 if args.has_key('rev'): |
580 if args.has_key('rev'): |
569 hi = args['rev'][0] |
581 hi = args['rev'][0] |
570 try: |
582 try: |