##// END OF EJS Templates
i18n: show localized messages for commands/extensions in hgweb help top (issue3383)...
FUJIWARA Katsunori -
r16469:dd68c972 stable
parent child Browse files
Show More
@@ -1,857 +1,857 b''
1 1 #
2 2 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 import os, mimetypes, re, cgi, copy
9 9 import webutil
10 10 from mercurial import error, encoding, archival, templater, templatefilters
11 11 from mercurial.node import short, hex
12 12 from mercurial.util import binary
13 13 from common import paritygen, staticfile, get_contact, ErrorResponse
14 14 from common import HTTP_OK, HTTP_FORBIDDEN, HTTP_NOT_FOUND
15 15 from mercurial import graphmod, patch
16 16 from mercurial import help as helpmod
17 17 from mercurial.i18n import _
18 18
19 19 # __all__ is populated with the allowed commands. Be sure to add to it if
20 20 # you're adding a new command, or the new command won't work.
21 21
22 22 __all__ = [
23 23 'log', 'rawfile', 'file', 'changelog', 'shortlog', 'changeset', 'rev',
24 24 'manifest', 'tags', 'bookmarks', 'branches', 'summary', 'filediff', 'diff',
25 25 'annotate', 'filelog', 'archive', 'static', 'graph', 'help',
26 26 ]
27 27
28 28 def log(web, req, tmpl):
29 29 if 'file' in req.form and req.form['file'][0]:
30 30 return filelog(web, req, tmpl)
31 31 else:
32 32 return changelog(web, req, tmpl)
33 33
34 34 def rawfile(web, req, tmpl):
35 35 guessmime = web.configbool('web', 'guessmime', False)
36 36
37 37 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
38 38 if not path:
39 39 content = manifest(web, req, tmpl)
40 40 req.respond(HTTP_OK, web.ctype)
41 41 return content
42 42
43 43 try:
44 44 fctx = webutil.filectx(web.repo, req)
45 45 except error.LookupError, inst:
46 46 try:
47 47 content = manifest(web, req, tmpl)
48 48 req.respond(HTTP_OK, web.ctype)
49 49 return content
50 50 except ErrorResponse:
51 51 raise inst
52 52
53 53 path = fctx.path()
54 54 text = fctx.data()
55 55 mt = 'application/binary'
56 56 if guessmime:
57 57 mt = mimetypes.guess_type(path)[0]
58 58 if mt is None:
59 59 mt = binary(text) and 'application/binary' or 'text/plain'
60 60 if mt.startswith('text/'):
61 61 mt += '; charset="%s"' % encoding.encoding
62 62
63 63 req.respond(HTTP_OK, mt, path, len(text))
64 64 return [text]
65 65
66 66 def _filerevision(web, tmpl, fctx):
67 67 f = fctx.path()
68 68 text = fctx.data()
69 69 parity = paritygen(web.stripecount)
70 70
71 71 if binary(text):
72 72 mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
73 73 text = '(binary:%s)' % mt
74 74
75 75 def lines():
76 76 for lineno, t in enumerate(text.splitlines(True)):
77 77 yield {"line": t,
78 78 "lineid": "l%d" % (lineno + 1),
79 79 "linenumber": "% 6d" % (lineno + 1),
80 80 "parity": parity.next()}
81 81
82 82 return tmpl("filerevision",
83 83 file=f,
84 84 path=webutil.up(f),
85 85 text=lines(),
86 86 rev=fctx.rev(),
87 87 node=fctx.hex(),
88 88 author=fctx.user(),
89 89 date=fctx.date(),
90 90 desc=fctx.description(),
91 91 branch=webutil.nodebranchnodefault(fctx),
92 92 parent=webutil.parents(fctx),
93 93 child=webutil.children(fctx),
94 94 rename=webutil.renamelink(fctx),
95 95 permissions=fctx.manifest().flags(f))
96 96
97 97 def file(web, req, tmpl):
98 98 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
99 99 if not path:
100 100 return manifest(web, req, tmpl)
101 101 try:
102 102 return _filerevision(web, tmpl, webutil.filectx(web.repo, req))
103 103 except error.LookupError, inst:
104 104 try:
105 105 return manifest(web, req, tmpl)
106 106 except ErrorResponse:
107 107 raise inst
108 108
109 109 def _search(web, req, tmpl):
110 110
111 111 query = req.form['rev'][0]
112 112 revcount = web.maxchanges
113 113 if 'revcount' in req.form:
114 114 revcount = int(req.form.get('revcount', [revcount])[0])
115 115 revcount = max(revcount, 1)
116 116 tmpl.defaults['sessionvars']['revcount'] = revcount
117 117
118 118 lessvars = copy.copy(tmpl.defaults['sessionvars'])
119 119 lessvars['revcount'] = max(revcount / 2, 1)
120 120 lessvars['rev'] = query
121 121 morevars = copy.copy(tmpl.defaults['sessionvars'])
122 122 morevars['revcount'] = revcount * 2
123 123 morevars['rev'] = query
124 124
125 125 def changelist(**map):
126 126 count = 0
127 127 lower = encoding.lower
128 128 qw = lower(query).split()
129 129
130 130 def revgen():
131 131 for i in xrange(len(web.repo) - 1, 0, -100):
132 132 l = []
133 133 for j in xrange(max(0, i - 100), i + 1):
134 134 ctx = web.repo[j]
135 135 l.append(ctx)
136 136 l.reverse()
137 137 for e in l:
138 138 yield e
139 139
140 140 for ctx in revgen():
141 141 miss = 0
142 142 for q in qw:
143 143 if not (q in lower(ctx.user()) or
144 144 q in lower(ctx.description()) or
145 145 q in lower(" ".join(ctx.files()))):
146 146 miss = 1
147 147 break
148 148 if miss:
149 149 continue
150 150
151 151 count += 1
152 152 n = ctx.node()
153 153 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
154 154 files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
155 155
156 156 yield tmpl('searchentry',
157 157 parity=parity.next(),
158 158 author=ctx.user(),
159 159 parent=webutil.parents(ctx),
160 160 child=webutil.children(ctx),
161 161 changelogtag=showtags,
162 162 desc=ctx.description(),
163 163 date=ctx.date(),
164 164 files=files,
165 165 rev=ctx.rev(),
166 166 node=hex(n),
167 167 tags=webutil.nodetagsdict(web.repo, n),
168 168 bookmarks=webutil.nodebookmarksdict(web.repo, n),
169 169 inbranch=webutil.nodeinbranch(web.repo, ctx),
170 170 branches=webutil.nodebranchdict(web.repo, ctx))
171 171
172 172 if count >= revcount:
173 173 break
174 174
175 175 tip = web.repo['tip']
176 176 parity = paritygen(web.stripecount)
177 177
178 178 return tmpl('search', query=query, node=tip.hex(),
179 179 entries=changelist, archives=web.archivelist("tip"),
180 180 morevars=morevars, lessvars=lessvars)
181 181
182 182 def changelog(web, req, tmpl, shortlog=False):
183 183
184 184 if 'node' in req.form:
185 185 ctx = webutil.changectx(web.repo, req)
186 186 else:
187 187 if 'rev' in req.form:
188 188 hi = req.form['rev'][0]
189 189 else:
190 190 hi = len(web.repo) - 1
191 191 try:
192 192 ctx = web.repo[hi]
193 193 except error.RepoError:
194 194 return _search(web, req, tmpl) # XXX redirect to 404 page?
195 195
196 196 def changelist(limit=0, **map):
197 197 l = [] # build a list in forward order for efficiency
198 198 for i in xrange(start, end):
199 199 ctx = web.repo[i]
200 200 n = ctx.node()
201 201 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
202 202 files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
203 203
204 204 l.insert(0, {"parity": parity.next(),
205 205 "author": ctx.user(),
206 206 "parent": webutil.parents(ctx, i - 1),
207 207 "child": webutil.children(ctx, i + 1),
208 208 "changelogtag": showtags,
209 209 "desc": ctx.description(),
210 210 "date": ctx.date(),
211 211 "files": files,
212 212 "rev": i,
213 213 "node": hex(n),
214 214 "tags": webutil.nodetagsdict(web.repo, n),
215 215 "bookmarks": webutil.nodebookmarksdict(web.repo, n),
216 216 "inbranch": webutil.nodeinbranch(web.repo, ctx),
217 217 "branches": webutil.nodebranchdict(web.repo, ctx)
218 218 })
219 219
220 220 if limit > 0:
221 221 l = l[:limit]
222 222
223 223 for e in l:
224 224 yield e
225 225
226 226 revcount = shortlog and web.maxshortchanges or web.maxchanges
227 227 if 'revcount' in req.form:
228 228 revcount = int(req.form.get('revcount', [revcount])[0])
229 229 revcount = max(revcount, 1)
230 230 tmpl.defaults['sessionvars']['revcount'] = revcount
231 231
232 232 lessvars = copy.copy(tmpl.defaults['sessionvars'])
233 233 lessvars['revcount'] = max(revcount / 2, 1)
234 234 morevars = copy.copy(tmpl.defaults['sessionvars'])
235 235 morevars['revcount'] = revcount * 2
236 236
237 237 count = len(web.repo)
238 238 pos = ctx.rev()
239 239 start = max(0, pos - revcount + 1)
240 240 end = min(count, start + revcount)
241 241 pos = end - 1
242 242 parity = paritygen(web.stripecount, offset=start - end)
243 243
244 244 changenav = webutil.revnavgen(pos, revcount, count, web.repo.changectx)
245 245
246 246 return tmpl(shortlog and 'shortlog' or 'changelog', changenav=changenav,
247 247 node=ctx.hex(), rev=pos, changesets=count,
248 248 entries=lambda **x: changelist(limit=0,**x),
249 249 latestentry=lambda **x: changelist(limit=1,**x),
250 250 archives=web.archivelist("tip"), revcount=revcount,
251 251 morevars=morevars, lessvars=lessvars)
252 252
253 253 def shortlog(web, req, tmpl):
254 254 return changelog(web, req, tmpl, shortlog = True)
255 255
256 256 def changeset(web, req, tmpl):
257 257 ctx = webutil.changectx(web.repo, req)
258 258 showtags = webutil.showtag(web.repo, tmpl, 'changesettag', ctx.node())
259 259 showbookmarks = webutil.showbookmark(web.repo, tmpl, 'changesetbookmark',
260 260 ctx.node())
261 261 showbranch = webutil.nodebranchnodefault(ctx)
262 262
263 263 files = []
264 264 parity = paritygen(web.stripecount)
265 265 for blockno, f in enumerate(ctx.files()):
266 266 template = f in ctx and 'filenodelink' or 'filenolink'
267 267 files.append(tmpl(template,
268 268 node=ctx.hex(), file=f, blockno=blockno + 1,
269 269 parity=parity.next()))
270 270
271 271 style = web.config('web', 'style', 'paper')
272 272 if 'style' in req.form:
273 273 style = req.form['style'][0]
274 274
275 275 parity = paritygen(web.stripecount)
276 276 diffs = webutil.diffs(web.repo, tmpl, ctx, None, parity, style)
277 277
278 278 parity = paritygen(web.stripecount)
279 279 diffstatgen = webutil.diffstatgen(ctx)
280 280 diffstat = webutil.diffstat(tmpl, ctx, diffstatgen, parity)
281 281
282 282 return tmpl('changeset',
283 283 diff=diffs,
284 284 rev=ctx.rev(),
285 285 node=ctx.hex(),
286 286 parent=webutil.parents(ctx),
287 287 child=webutil.children(ctx),
288 288 changesettag=showtags,
289 289 changesetbookmark=showbookmarks,
290 290 changesetbranch=showbranch,
291 291 author=ctx.user(),
292 292 desc=ctx.description(),
293 293 date=ctx.date(),
294 294 files=files,
295 295 diffsummary=lambda **x: webutil.diffsummary(diffstatgen),
296 296 diffstat=diffstat,
297 297 archives=web.archivelist(ctx.hex()),
298 298 tags=webutil.nodetagsdict(web.repo, ctx.node()),
299 299 bookmarks=webutil.nodebookmarksdict(web.repo, ctx.node()),
300 300 branch=webutil.nodebranchnodefault(ctx),
301 301 inbranch=webutil.nodeinbranch(web.repo, ctx),
302 302 branches=webutil.nodebranchdict(web.repo, ctx))
303 303
304 304 rev = changeset
305 305
306 306 def decodepath(path):
307 307 """Hook for mapping a path in the repository to a path in the
308 308 working copy.
309 309
310 310 Extensions (e.g., largefiles) can override this to remap files in
311 311 the virtual file system presented by the manifest command below."""
312 312 return path
313 313
314 314 def manifest(web, req, tmpl):
315 315 ctx = webutil.changectx(web.repo, req)
316 316 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
317 317 mf = ctx.manifest()
318 318 node = ctx.node()
319 319
320 320 files = {}
321 321 dirs = {}
322 322 parity = paritygen(web.stripecount)
323 323
324 324 if path and path[-1] != "/":
325 325 path += "/"
326 326 l = len(path)
327 327 abspath = "/" + path
328 328
329 329 for full, n in mf.iteritems():
330 330 # the virtual path (working copy path) used for the full
331 331 # (repository) path
332 332 f = decodepath(full)
333 333
334 334 if f[:l] != path:
335 335 continue
336 336 remain = f[l:]
337 337 elements = remain.split('/')
338 338 if len(elements) == 1:
339 339 files[remain] = full
340 340 else:
341 341 h = dirs # need to retain ref to dirs (root)
342 342 for elem in elements[0:-1]:
343 343 if elem not in h:
344 344 h[elem] = {}
345 345 h = h[elem]
346 346 if len(h) > 1:
347 347 break
348 348 h[None] = None # denotes files present
349 349
350 350 if mf and not files and not dirs:
351 351 raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
352 352
353 353 def filelist(**map):
354 354 for f in sorted(files):
355 355 full = files[f]
356 356
357 357 fctx = ctx.filectx(full)
358 358 yield {"file": full,
359 359 "parity": parity.next(),
360 360 "basename": f,
361 361 "date": fctx.date(),
362 362 "size": fctx.size(),
363 363 "permissions": mf.flags(full)}
364 364
365 365 def dirlist(**map):
366 366 for d in sorted(dirs):
367 367
368 368 emptydirs = []
369 369 h = dirs[d]
370 370 while isinstance(h, dict) and len(h) == 1:
371 371 k, v = h.items()[0]
372 372 if v:
373 373 emptydirs.append(k)
374 374 h = v
375 375
376 376 path = "%s%s" % (abspath, d)
377 377 yield {"parity": parity.next(),
378 378 "path": path,
379 379 "emptydirs": "/".join(emptydirs),
380 380 "basename": d}
381 381
382 382 return tmpl("manifest",
383 383 rev=ctx.rev(),
384 384 node=hex(node),
385 385 path=abspath,
386 386 up=webutil.up(abspath),
387 387 upparity=parity.next(),
388 388 fentries=filelist,
389 389 dentries=dirlist,
390 390 archives=web.archivelist(hex(node)),
391 391 tags=webutil.nodetagsdict(web.repo, node),
392 392 bookmarks=webutil.nodebookmarksdict(web.repo, node),
393 393 inbranch=webutil.nodeinbranch(web.repo, ctx),
394 394 branches=webutil.nodebranchdict(web.repo, ctx))
395 395
396 396 def tags(web, req, tmpl):
397 397 i = web.repo.tagslist()
398 398 i.reverse()
399 399 parity = paritygen(web.stripecount)
400 400
401 401 def entries(notip=False, limit=0, **map):
402 402 count = 0
403 403 for k, n in i:
404 404 if notip and k == "tip":
405 405 continue
406 406 if limit > 0 and count >= limit:
407 407 continue
408 408 count = count + 1
409 409 yield {"parity": parity.next(),
410 410 "tag": k,
411 411 "date": web.repo[n].date(),
412 412 "node": hex(n)}
413 413
414 414 return tmpl("tags",
415 415 node=hex(web.repo.changelog.tip()),
416 416 entries=lambda **x: entries(False, 0, **x),
417 417 entriesnotip=lambda **x: entries(True, 0, **x),
418 418 latestentry=lambda **x: entries(True, 1, **x))
419 419
420 420 def bookmarks(web, req, tmpl):
421 421 i = web.repo._bookmarks.items()
422 422 parity = paritygen(web.stripecount)
423 423
424 424 def entries(limit=0, **map):
425 425 count = 0
426 426 for k, n in sorted(i):
427 427 if limit > 0 and count >= limit:
428 428 continue
429 429 count = count + 1
430 430 yield {"parity": parity.next(),
431 431 "bookmark": k,
432 432 "date": web.repo[n].date(),
433 433 "node": hex(n)}
434 434
435 435 return tmpl("bookmarks",
436 436 node=hex(web.repo.changelog.tip()),
437 437 entries=lambda **x: entries(0, **x),
438 438 latestentry=lambda **x: entries(1, **x))
439 439
440 440 def branches(web, req, tmpl):
441 441 tips = (web.repo[n] for t, n in web.repo.branchtags().iteritems())
442 442 heads = web.repo.heads()
443 443 parity = paritygen(web.stripecount)
444 444 sortkey = lambda ctx: ('close' not in ctx.extra(), ctx.rev())
445 445
446 446 def entries(limit, **map):
447 447 count = 0
448 448 for ctx in sorted(tips, key=sortkey, reverse=True):
449 449 if limit > 0 and count >= limit:
450 450 return
451 451 count += 1
452 452 if not web.repo.branchheads(ctx.branch()):
453 453 status = 'closed'
454 454 elif ctx.node() not in heads:
455 455 status = 'inactive'
456 456 else:
457 457 status = 'open'
458 458 yield {'parity': parity.next(),
459 459 'branch': ctx.branch(),
460 460 'status': status,
461 461 'node': ctx.hex(),
462 462 'date': ctx.date()}
463 463
464 464 return tmpl('branches', node=hex(web.repo.changelog.tip()),
465 465 entries=lambda **x: entries(0, **x),
466 466 latestentry=lambda **x: entries(1, **x))
467 467
468 468 def summary(web, req, tmpl):
469 469 i = web.repo.tagslist()
470 470 i.reverse()
471 471
472 472 def tagentries(**map):
473 473 parity = paritygen(web.stripecount)
474 474 count = 0
475 475 for k, n in i:
476 476 if k == "tip": # skip tip
477 477 continue
478 478
479 479 count += 1
480 480 if count > 10: # limit to 10 tags
481 481 break
482 482
483 483 yield tmpl("tagentry",
484 484 parity=parity.next(),
485 485 tag=k,
486 486 node=hex(n),
487 487 date=web.repo[n].date())
488 488
489 489 def bookmarks(**map):
490 490 parity = paritygen(web.stripecount)
491 491 b = web.repo._bookmarks.items()
492 492 for k, n in sorted(b)[:10]: # limit to 10 bookmarks
493 493 yield {'parity': parity.next(),
494 494 'bookmark': k,
495 495 'date': web.repo[n].date(),
496 496 'node': hex(n)}
497 497
498 498 def branches(**map):
499 499 parity = paritygen(web.stripecount)
500 500
501 501 b = web.repo.branchtags()
502 502 l = [(-web.repo.changelog.rev(n), n, t) for t, n in b.iteritems()]
503 503 for r, n, t in sorted(l):
504 504 yield {'parity': parity.next(),
505 505 'branch': t,
506 506 'node': hex(n),
507 507 'date': web.repo[n].date()}
508 508
509 509 def changelist(**map):
510 510 parity = paritygen(web.stripecount, offset=start - end)
511 511 l = [] # build a list in forward order for efficiency
512 512 for i in xrange(start, end):
513 513 ctx = web.repo[i]
514 514 n = ctx.node()
515 515 hn = hex(n)
516 516
517 517 l.insert(0, tmpl(
518 518 'shortlogentry',
519 519 parity=parity.next(),
520 520 author=ctx.user(),
521 521 desc=ctx.description(),
522 522 date=ctx.date(),
523 523 rev=i,
524 524 node=hn,
525 525 tags=webutil.nodetagsdict(web.repo, n),
526 526 bookmarks=webutil.nodebookmarksdict(web.repo, n),
527 527 inbranch=webutil.nodeinbranch(web.repo, ctx),
528 528 branches=webutil.nodebranchdict(web.repo, ctx)))
529 529
530 530 yield l
531 531
532 532 tip = web.repo['tip']
533 533 count = len(web.repo)
534 534 start = max(0, count - web.maxchanges)
535 535 end = min(count, start + web.maxchanges)
536 536
537 537 return tmpl("summary",
538 538 desc=web.config("web", "description", "unknown"),
539 539 owner=get_contact(web.config) or "unknown",
540 540 lastchange=tip.date(),
541 541 tags=tagentries,
542 542 bookmarks=bookmarks,
543 543 branches=branches,
544 544 shortlog=changelist,
545 545 node=tip.hex(),
546 546 archives=web.archivelist("tip"))
547 547
548 548 def filediff(web, req, tmpl):
549 549 fctx, ctx = None, None
550 550 try:
551 551 fctx = webutil.filectx(web.repo, req)
552 552 except LookupError:
553 553 ctx = webutil.changectx(web.repo, req)
554 554 path = webutil.cleanpath(web.repo, req.form['file'][0])
555 555 if path not in ctx.files():
556 556 raise
557 557
558 558 if fctx is not None:
559 559 n = fctx.node()
560 560 path = fctx.path()
561 561 else:
562 562 n = ctx.node()
563 563 # path already defined in except clause
564 564
565 565 parity = paritygen(web.stripecount)
566 566 style = web.config('web', 'style', 'paper')
567 567 if 'style' in req.form:
568 568 style = req.form['style'][0]
569 569
570 570 diffs = webutil.diffs(web.repo, tmpl, fctx or ctx, [path], parity, style)
571 571 rename = fctx and webutil.renamelink(fctx) or []
572 572 ctx = fctx and fctx or ctx
573 573 return tmpl("filediff",
574 574 file=path,
575 575 node=hex(n),
576 576 rev=ctx.rev(),
577 577 date=ctx.date(),
578 578 desc=ctx.description(),
579 579 author=ctx.user(),
580 580 rename=rename,
581 581 branch=webutil.nodebranchnodefault(ctx),
582 582 parent=webutil.parents(ctx),
583 583 child=webutil.children(ctx),
584 584 diff=diffs)
585 585
586 586 diff = filediff
587 587
588 588 def annotate(web, req, tmpl):
589 589 fctx = webutil.filectx(web.repo, req)
590 590 f = fctx.path()
591 591 parity = paritygen(web.stripecount)
592 592 diffopts = patch.diffopts(web.repo.ui, untrusted=True, section='annotate')
593 593
594 594 def annotate(**map):
595 595 last = None
596 596 if binary(fctx.data()):
597 597 mt = (mimetypes.guess_type(fctx.path())[0]
598 598 or 'application/octet-stream')
599 599 lines = enumerate([((fctx.filectx(fctx.filerev()), 1),
600 600 '(binary:%s)' % mt)])
601 601 else:
602 602 lines = enumerate(fctx.annotate(follow=True, linenumber=True,
603 603 diffopts=diffopts))
604 604 for lineno, ((f, targetline), l) in lines:
605 605 fnode = f.filenode()
606 606
607 607 if last != fnode:
608 608 last = fnode
609 609
610 610 yield {"parity": parity.next(),
611 611 "node": f.hex(),
612 612 "rev": f.rev(),
613 613 "author": f.user(),
614 614 "desc": f.description(),
615 615 "file": f.path(),
616 616 "targetline": targetline,
617 617 "line": l,
618 618 "lineid": "l%d" % (lineno + 1),
619 619 "linenumber": "% 6d" % (lineno + 1),
620 620 "revdate": f.date()}
621 621
622 622 return tmpl("fileannotate",
623 623 file=f,
624 624 annotate=annotate,
625 625 path=webutil.up(f),
626 626 rev=fctx.rev(),
627 627 node=fctx.hex(),
628 628 author=fctx.user(),
629 629 date=fctx.date(),
630 630 desc=fctx.description(),
631 631 rename=webutil.renamelink(fctx),
632 632 branch=webutil.nodebranchnodefault(fctx),
633 633 parent=webutil.parents(fctx),
634 634 child=webutil.children(fctx),
635 635 permissions=fctx.manifest().flags(f))
636 636
637 637 def filelog(web, req, tmpl):
638 638
639 639 try:
640 640 fctx = webutil.filectx(web.repo, req)
641 641 f = fctx.path()
642 642 fl = fctx.filelog()
643 643 except error.LookupError:
644 644 f = webutil.cleanpath(web.repo, req.form['file'][0])
645 645 fl = web.repo.file(f)
646 646 numrevs = len(fl)
647 647 if not numrevs: # file doesn't exist at all
648 648 raise
649 649 rev = webutil.changectx(web.repo, req).rev()
650 650 first = fl.linkrev(0)
651 651 if rev < first: # current rev is from before file existed
652 652 raise
653 653 frev = numrevs - 1
654 654 while fl.linkrev(frev) > rev:
655 655 frev -= 1
656 656 fctx = web.repo.filectx(f, fl.linkrev(frev))
657 657
658 658 revcount = web.maxshortchanges
659 659 if 'revcount' in req.form:
660 660 revcount = int(req.form.get('revcount', [revcount])[0])
661 661 revcount = max(revcount, 1)
662 662 tmpl.defaults['sessionvars']['revcount'] = revcount
663 663
664 664 lessvars = copy.copy(tmpl.defaults['sessionvars'])
665 665 lessvars['revcount'] = max(revcount / 2, 1)
666 666 morevars = copy.copy(tmpl.defaults['sessionvars'])
667 667 morevars['revcount'] = revcount * 2
668 668
669 669 count = fctx.filerev() + 1
670 670 start = max(0, fctx.filerev() - revcount + 1) # first rev on this page
671 671 end = min(count, start + revcount) # last rev on this page
672 672 parity = paritygen(web.stripecount, offset=start - end)
673 673
674 674 def entries(limit=0, **map):
675 675 l = []
676 676
677 677 repo = web.repo
678 678 for i in xrange(start, end):
679 679 iterfctx = fctx.filectx(i)
680 680
681 681 l.insert(0, {"parity": parity.next(),
682 682 "filerev": i,
683 683 "file": f,
684 684 "node": iterfctx.hex(),
685 685 "author": iterfctx.user(),
686 686 "date": iterfctx.date(),
687 687 "rename": webutil.renamelink(iterfctx),
688 688 "parent": webutil.parents(iterfctx),
689 689 "child": webutil.children(iterfctx),
690 690 "desc": iterfctx.description(),
691 691 "tags": webutil.nodetagsdict(repo, iterfctx.node()),
692 692 "bookmarks": webutil.nodebookmarksdict(
693 693 repo, iterfctx.node()),
694 694 "branch": webutil.nodebranchnodefault(iterfctx),
695 695 "inbranch": webutil.nodeinbranch(repo, iterfctx),
696 696 "branches": webutil.nodebranchdict(repo, iterfctx)})
697 697
698 698 if limit > 0:
699 699 l = l[:limit]
700 700
701 701 for e in l:
702 702 yield e
703 703
704 704 nodefunc = lambda x: fctx.filectx(fileid=x)
705 705 nav = webutil.revnavgen(end - 1, revcount, count, nodefunc)
706 706 return tmpl("filelog", file=f, node=fctx.hex(), nav=nav,
707 707 entries=lambda **x: entries(limit=0, **x),
708 708 latestentry=lambda **x: entries(limit=1, **x),
709 709 revcount=revcount, morevars=morevars, lessvars=lessvars)
710 710
711 711 def archive(web, req, tmpl):
712 712 type_ = req.form.get('type', [None])[0]
713 713 allowed = web.configlist("web", "allow_archive")
714 714 key = req.form['node'][0]
715 715
716 716 if type_ not in web.archives:
717 717 msg = 'Unsupported archive type: %s' % type_
718 718 raise ErrorResponse(HTTP_NOT_FOUND, msg)
719 719
720 720 if not ((type_ in allowed or
721 721 web.configbool("web", "allow" + type_, False))):
722 722 msg = 'Archive type not allowed: %s' % type_
723 723 raise ErrorResponse(HTTP_FORBIDDEN, msg)
724 724
725 725 reponame = re.sub(r"\W+", "-", os.path.basename(web.reponame))
726 726 cnode = web.repo.lookup(key)
727 727 arch_version = key
728 728 if cnode == key or key == 'tip':
729 729 arch_version = short(cnode)
730 730 name = "%s-%s" % (reponame, arch_version)
731 731 mimetype, artype, extension, encoding = web.archive_specs[type_]
732 732 headers = [
733 733 ('Content-Type', mimetype),
734 734 ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
735 735 ]
736 736 if encoding:
737 737 headers.append(('Content-Encoding', encoding))
738 738 req.header(headers)
739 739 req.respond(HTTP_OK)
740 740 archival.archive(web.repo, req, cnode, artype, prefix=name)
741 741 return []
742 742
743 743
744 744 def static(web, req, tmpl):
745 745 fname = req.form['file'][0]
746 746 # a repo owner may set web.static in .hg/hgrc to get any file
747 747 # readable by the user running the CGI script
748 748 static = web.config("web", "static", None, untrusted=False)
749 749 if not static:
750 750 tp = web.templatepath or templater.templatepath()
751 751 if isinstance(tp, str):
752 752 tp = [tp]
753 753 static = [os.path.join(p, 'static') for p in tp]
754 754 return [staticfile(static, fname, req)]
755 755
756 756 def graph(web, req, tmpl):
757 757
758 758 rev = webutil.changectx(web.repo, req).rev()
759 759 bg_height = 39
760 760 revcount = web.maxshortchanges
761 761 if 'revcount' in req.form:
762 762 revcount = int(req.form.get('revcount', [revcount])[0])
763 763 revcount = max(revcount, 1)
764 764 tmpl.defaults['sessionvars']['revcount'] = revcount
765 765
766 766 lessvars = copy.copy(tmpl.defaults['sessionvars'])
767 767 lessvars['revcount'] = max(revcount / 2, 1)
768 768 morevars = copy.copy(tmpl.defaults['sessionvars'])
769 769 morevars['revcount'] = revcount * 2
770 770
771 771 max_rev = len(web.repo) - 1
772 772 revcount = min(max_rev, revcount)
773 773 revnode = web.repo.changelog.node(rev)
774 774 revnode_hex = hex(revnode)
775 775 uprev = min(max_rev, rev + revcount)
776 776 downrev = max(0, rev - revcount)
777 777 count = len(web.repo)
778 778 changenav = webutil.revnavgen(rev, revcount, count, web.repo.changectx)
779 779 startrev = rev
780 780 # if starting revision is less than 60 set it to uprev
781 781 if rev < web.maxshortchanges:
782 782 startrev = uprev
783 783
784 784 dag = graphmod.dagwalker(web.repo, range(startrev, downrev - 1, -1))
785 785 tree = list(graphmod.colored(dag, web.repo))
786 786 canvasheight = (len(tree) + 1) * bg_height - 27
787 787 data = []
788 788 for (id, type, ctx, vtx, edges) in tree:
789 789 if type != graphmod.CHANGESET:
790 790 continue
791 791 node = str(ctx)
792 792 age = templatefilters.age(ctx.date())
793 793 desc = templatefilters.firstline(ctx.description())
794 794 desc = cgi.escape(templatefilters.nonempty(desc))
795 795 user = cgi.escape(templatefilters.person(ctx.user()))
796 796 branch = ctx.branch()
797 797 branch = branch, web.repo.branchtags().get(branch) == ctx.node()
798 798 data.append((node, vtx, edges, desc, user, age, branch, ctx.tags(),
799 799 ctx.bookmarks()))
800 800
801 801 return tmpl('graph', rev=rev, revcount=revcount, uprev=uprev,
802 802 lessvars=lessvars, morevars=morevars, downrev=downrev,
803 803 canvasheight=canvasheight, jsdata=data, bg_height=bg_height,
804 804 node=revnode_hex, changenav=changenav)
805 805
806 806 def _getdoc(e):
807 807 doc = e[0].__doc__
808 808 if doc:
809 doc = doc.split('\n')[0]
809 doc = _(doc).split('\n')[0]
810 810 else:
811 811 doc = _('(no help text available)')
812 812 return doc
813 813
814 814 def help(web, req, tmpl):
815 815 from mercurial import commands # avoid cycle
816 816
817 817 topicname = req.form.get('node', [None])[0]
818 818 if not topicname:
819 819 def topics(**map):
820 820 for entries, summary, _ in helpmod.helptable:
821 821 entries = sorted(entries, key=len)
822 822 yield {'topic': entries[-1], 'summary': summary}
823 823
824 824 early, other = [], []
825 825 primary = lambda s: s.split('|')[0]
826 826 for c, e in commands.table.iteritems():
827 827 doc = _getdoc(e)
828 828 if 'DEPRECATED' in doc or c.startswith('debug'):
829 829 continue
830 830 cmd = primary(c)
831 831 if cmd.startswith('^'):
832 832 early.append((cmd[1:], doc))
833 833 else:
834 834 other.append((cmd, doc))
835 835
836 836 early.sort()
837 837 other.sort()
838 838
839 839 def earlycommands(**map):
840 840 for c, doc in early:
841 841 yield {'topic': c, 'summary': doc}
842 842
843 843 def othercommands(**map):
844 844 for c, doc in other:
845 845 yield {'topic': c, 'summary': doc}
846 846
847 847 return tmpl('helptopics', topics=topics, earlycommands=earlycommands,
848 848 othercommands=othercommands, title='Index')
849 849
850 850 u = webutil.wsgiui()
851 851 u.pushbuffer()
852 852 try:
853 853 commands.help_(u, topicname)
854 854 except error.UnknownCommand:
855 855 raise ErrorResponse(HTTP_NOT_FOUND)
856 856 doc = u.popbuffer()
857 857 return tmpl('help', topic=topicname, doc=doc)
General Comments 0
You need to be logged in to leave comments. Login now