##// END OF EJS Templates
Allow comma to separate types in allow_archive, too. Use longer variable name.
Thomas Arendsen Hein -
r2359:a392eaa8 default
parent child Browse files
Show More
@@ -1,821 +1,822 b''
1 1 # hgweb.py - web interface to a mercurial repository
2 2 #
3 3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 4 # Copyright 2005 Matt Mackall <mpm@selenic.com>
5 5 #
6 6 # This software may be used and distributed according to the terms
7 7 # of the GNU General Public License, incorporated herein by reference.
8 8
9 9 import os
10 10 import os.path
11 11 import mimetypes
12 12 from mercurial.demandload import demandload
13 13 demandload(globals(), "re zlib ConfigParser")
14 14 demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,templater")
15 15 demandload(globals(), "mercurial.hgweb.request:hgrequest")
16 16 demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile")
17 17 from mercurial.node import *
18 18 from mercurial.i18n import gettext as _
19 19
20 20 def _up(p):
21 21 if p[0] != "/":
22 22 p = "/" + p
23 23 if p[-1] == "/":
24 24 p = p[:-1]
25 25 up = os.path.dirname(p)
26 26 if up == "/":
27 27 return "/"
28 28 return up + "/"
29 29
30 30 class hgweb(object):
31 31 def __init__(self, repo, name=None):
32 32 if type(repo) == type(""):
33 33 self.repo = hg.repository(ui.ui(), repo)
34 34 else:
35 35 self.repo = repo
36 36
37 37 self.mtime = -1
38 38 self.reponame = name
39 39 self.archives = 'zip', 'gz', 'bz2'
40 40
41 41 def refresh(self):
42 42 mtime = get_mtime(self.repo.root)
43 43 if mtime != self.mtime:
44 44 self.mtime = mtime
45 45 self.repo = hg.repository(self.repo.ui, self.repo.root)
46 46 self.maxchanges = int(self.repo.ui.config("web", "maxchanges", 10))
47 47 self.maxfiles = int(self.repo.ui.config("web", "maxfiles", 10))
48 48 self.allowpull = self.repo.ui.configbool("web", "allowpull", True)
49 49
50 50 def archivelist(self, nodeid):
51 al = self.repo.ui.config("web", "allow_archive", "").split()
51 allowed = (self.repo.ui.config("web", "allow_archive", "")
52 .replace(",", " ").split())
52 53 for i in self.archives:
53 if i in al or self.repo.ui.configbool("web", "allow" + i, False):
54 if i in allowed or self.repo.ui.configbool("web", "allow" + i):
54 55 yield {"type" : i, "node" : nodeid, "url": ""}
55 56
56 57 def listfiles(self, files, mf):
57 58 for f in files[:self.maxfiles]:
58 59 yield self.t("filenodelink", node=hex(mf[f]), file=f)
59 60 if len(files) > self.maxfiles:
60 61 yield self.t("fileellipses")
61 62
62 63 def listfilediffs(self, files, changeset):
63 64 for f in files[:self.maxfiles]:
64 65 yield self.t("filedifflink", node=hex(changeset), file=f)
65 66 if len(files) > self.maxfiles:
66 67 yield self.t("fileellipses")
67 68
68 69 def siblings(self, siblings=[], rev=None, hiderev=None, **args):
69 70 if not rev:
70 71 rev = lambda x: ""
71 72 siblings = [s for s in siblings if s != nullid]
72 73 if len(siblings) == 1 and rev(siblings[0]) == hiderev:
73 74 return
74 75 for s in siblings:
75 76 yield dict(node=hex(s), rev=rev(s), **args)
76 77
77 78 def renamelink(self, fl, node):
78 79 r = fl.renamed(node)
79 80 if r:
80 81 return [dict(file=r[0], node=hex(r[1]))]
81 82 return []
82 83
83 84 def showtag(self, t1, node=nullid, **args):
84 85 for t in self.repo.nodetags(node):
85 86 yield self.t(t1, tag=t, **args)
86 87
87 88 def diff(self, node1, node2, files):
88 89 def filterfiles(filters, files):
89 90 l = [x for x in files if x in filters]
90 91
91 92 for t in filters:
92 93 if t and t[-1] != os.sep:
93 94 t += os.sep
94 95 l += [x for x in files if x.startswith(t)]
95 96 return l
96 97
97 98 parity = [0]
98 99 def diffblock(diff, f, fn):
99 100 yield self.t("diffblock",
100 101 lines=prettyprintlines(diff),
101 102 parity=parity[0],
102 103 file=f,
103 104 filenode=hex(fn or nullid))
104 105 parity[0] = 1 - parity[0]
105 106
106 107 def prettyprintlines(diff):
107 108 for l in diff.splitlines(1):
108 109 if l.startswith('+'):
109 110 yield self.t("difflineplus", line=l)
110 111 elif l.startswith('-'):
111 112 yield self.t("difflineminus", line=l)
112 113 elif l.startswith('@'):
113 114 yield self.t("difflineat", line=l)
114 115 else:
115 116 yield self.t("diffline", line=l)
116 117
117 118 r = self.repo
118 119 cl = r.changelog
119 120 mf = r.manifest
120 121 change1 = cl.read(node1)
121 122 change2 = cl.read(node2)
122 123 mmap1 = mf.read(change1[0])
123 124 mmap2 = mf.read(change2[0])
124 125 date1 = util.datestr(change1[2])
125 126 date2 = util.datestr(change2[2])
126 127
127 128 modified, added, removed, deleted, unknown = r.changes(node1, node2)
128 129 if files:
129 130 modified, added, removed = map(lambda x: filterfiles(files, x),
130 131 (modified, added, removed))
131 132
132 133 diffopts = self.repo.ui.diffopts()
133 134 showfunc = diffopts['showfunc']
134 135 ignorews = diffopts['ignorews']
135 136 for f in modified:
136 137 to = r.file(f).read(mmap1[f])
137 138 tn = r.file(f).read(mmap2[f])
138 139 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
139 140 showfunc=showfunc, ignorews=ignorews), f, tn)
140 141 for f in added:
141 142 to = None
142 143 tn = r.file(f).read(mmap2[f])
143 144 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
144 145 showfunc=showfunc, ignorews=ignorews), f, tn)
145 146 for f in removed:
146 147 to = r.file(f).read(mmap1[f])
147 148 tn = None
148 149 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
149 150 showfunc=showfunc, ignorews=ignorews), f, tn)
150 151
151 152 def changelog(self, pos):
152 153 def changenav(**map):
153 154 def seq(factor, maxchanges=None):
154 155 if maxchanges:
155 156 yield maxchanges
156 157 if maxchanges >= 20 and maxchanges <= 40:
157 158 yield 50
158 159 else:
159 160 yield 1 * factor
160 161 yield 3 * factor
161 162 for f in seq(factor * 10):
162 163 yield f
163 164
164 165 l = []
165 166 last = 0
166 167 for f in seq(1, self.maxchanges):
167 168 if f < self.maxchanges or f <= last:
168 169 continue
169 170 if f > count:
170 171 break
171 172 last = f
172 173 r = "%d" % f
173 174 if pos + f < count:
174 175 l.append(("+" + r, pos + f))
175 176 if pos - f >= 0:
176 177 l.insert(0, ("-" + r, pos - f))
177 178
178 179 yield {"rev": 0, "label": "(0)"}
179 180
180 181 for label, rev in l:
181 182 yield {"label": label, "rev": rev}
182 183
183 184 yield {"label": "tip", "rev": "tip"}
184 185
185 186 def changelist(**map):
186 187 parity = (start - end) & 1
187 188 cl = self.repo.changelog
188 189 l = [] # build a list in forward order for efficiency
189 190 for i in range(start, end):
190 191 n = cl.node(i)
191 192 changes = cl.read(n)
192 193 hn = hex(n)
193 194
194 195 l.insert(0, {"parity": parity,
195 196 "author": changes[1],
196 197 "parent": self.siblings(cl.parents(n), cl.rev,
197 198 cl.rev(n) - 1),
198 199 "child": self.siblings(cl.children(n), cl.rev,
199 200 cl.rev(n) + 1),
200 201 "changelogtag": self.showtag("changelogtag",n),
201 202 "manifest": hex(changes[0]),
202 203 "desc": changes[4],
203 204 "date": changes[2],
204 205 "files": self.listfilediffs(changes[3], n),
205 206 "rev": i,
206 207 "node": hn})
207 208 parity = 1 - parity
208 209
209 210 for e in l:
210 211 yield e
211 212
212 213 cl = self.repo.changelog
213 214 mf = cl.read(cl.tip())[0]
214 215 count = cl.count()
215 216 start = max(0, pos - self.maxchanges + 1)
216 217 end = min(count, start + self.maxchanges)
217 218 pos = end - 1
218 219
219 220 yield self.t('changelog',
220 221 changenav=changenav,
221 222 manifest=hex(mf),
222 223 rev=pos, changesets=count, entries=changelist,
223 224 archives=self.archivelist("tip"))
224 225
225 226 def search(self, query):
226 227
227 228 def changelist(**map):
228 229 cl = self.repo.changelog
229 230 count = 0
230 231 qw = query.lower().split()
231 232
232 233 def revgen():
233 234 for i in range(cl.count() - 1, 0, -100):
234 235 l = []
235 236 for j in range(max(0, i - 100), i):
236 237 n = cl.node(j)
237 238 changes = cl.read(n)
238 239 l.append((n, j, changes))
239 240 l.reverse()
240 241 for e in l:
241 242 yield e
242 243
243 244 for n, i, changes in revgen():
244 245 miss = 0
245 246 for q in qw:
246 247 if not (q in changes[1].lower() or
247 248 q in changes[4].lower() or
248 249 q in " ".join(changes[3][:20]).lower()):
249 250 miss = 1
250 251 break
251 252 if miss:
252 253 continue
253 254
254 255 count += 1
255 256 hn = hex(n)
256 257
257 258 yield self.t('searchentry',
258 259 parity=count & 1,
259 260 author=changes[1],
260 261 parent=self.siblings(cl.parents(n), cl.rev),
261 262 child=self.siblings(cl.children(n), cl.rev),
262 263 changelogtag=self.showtag("changelogtag",n),
263 264 manifest=hex(changes[0]),
264 265 desc=changes[4],
265 266 date=changes[2],
266 267 files=self.listfilediffs(changes[3], n),
267 268 rev=i,
268 269 node=hn)
269 270
270 271 if count >= self.maxchanges:
271 272 break
272 273
273 274 cl = self.repo.changelog
274 275 mf = cl.read(cl.tip())[0]
275 276
276 277 yield self.t('search',
277 278 query=query,
278 279 manifest=hex(mf),
279 280 entries=changelist)
280 281
281 282 def changeset(self, nodeid):
282 283 cl = self.repo.changelog
283 284 n = self.repo.lookup(nodeid)
284 285 nodeid = hex(n)
285 286 changes = cl.read(n)
286 287 p1 = cl.parents(n)[0]
287 288
288 289 files = []
289 290 mf = self.repo.manifest.read(changes[0])
290 291 for f in changes[3]:
291 292 files.append(self.t("filenodelink",
292 293 filenode=hex(mf.get(f, nullid)), file=f))
293 294
294 295 def diff(**map):
295 296 yield self.diff(p1, n, None)
296 297
297 298 yield self.t('changeset',
298 299 diff=diff,
299 300 rev=cl.rev(n),
300 301 node=nodeid,
301 302 parent=self.siblings(cl.parents(n), cl.rev),
302 303 child=self.siblings(cl.children(n), cl.rev),
303 304 changesettag=self.showtag("changesettag",n),
304 305 manifest=hex(changes[0]),
305 306 author=changes[1],
306 307 desc=changes[4],
307 308 date=changes[2],
308 309 files=files,
309 310 archives=self.archivelist(nodeid))
310 311
311 312 def filelog(self, f, filenode):
312 313 cl = self.repo.changelog
313 314 fl = self.repo.file(f)
314 315 filenode = hex(fl.lookup(filenode))
315 316 count = fl.count()
316 317
317 318 def entries(**map):
318 319 l = []
319 320 parity = (count - 1) & 1
320 321
321 322 for i in range(count):
322 323 n = fl.node(i)
323 324 lr = fl.linkrev(n)
324 325 cn = cl.node(lr)
325 326 cs = cl.read(cl.node(lr))
326 327
327 328 l.insert(0, {"parity": parity,
328 329 "filenode": hex(n),
329 330 "filerev": i,
330 331 "file": f,
331 332 "node": hex(cn),
332 333 "author": cs[1],
333 334 "date": cs[2],
334 335 "rename": self.renamelink(fl, n),
335 336 "parent": self.siblings(fl.parents(n),
336 337 fl.rev, file=f),
337 338 "child": self.siblings(fl.children(n),
338 339 fl.rev, file=f),
339 340 "desc": cs[4]})
340 341 parity = 1 - parity
341 342
342 343 for e in l:
343 344 yield e
344 345
345 346 yield self.t("filelog", file=f, filenode=filenode, entries=entries)
346 347
347 348 def filerevision(self, f, node):
348 349 fl = self.repo.file(f)
349 350 n = fl.lookup(node)
350 351 node = hex(n)
351 352 text = fl.read(n)
352 353 changerev = fl.linkrev(n)
353 354 cl = self.repo.changelog
354 355 cn = cl.node(changerev)
355 356 cs = cl.read(cn)
356 357 mfn = cs[0]
357 358
358 359 mt = mimetypes.guess_type(f)[0]
359 360 rawtext = text
360 361 if util.binary(text):
361 362 mt = mt or 'application/octet-stream'
362 363 text = "(binary:%s)" % mt
363 364 mt = mt or 'text/plain'
364 365
365 366 def lines():
366 367 for l, t in enumerate(text.splitlines(1)):
367 368 yield {"line": t,
368 369 "linenumber": "% 6d" % (l + 1),
369 370 "parity": l & 1}
370 371
371 372 yield self.t("filerevision",
372 373 file=f,
373 374 filenode=node,
374 375 path=_up(f),
375 376 text=lines(),
376 377 raw=rawtext,
377 378 mimetype=mt,
378 379 rev=changerev,
379 380 node=hex(cn),
380 381 manifest=hex(mfn),
381 382 author=cs[1],
382 383 date=cs[2],
383 384 parent=self.siblings(fl.parents(n), fl.rev, file=f),
384 385 child=self.siblings(fl.children(n), fl.rev, file=f),
385 386 rename=self.renamelink(fl, n),
386 387 permissions=self.repo.manifest.readflags(mfn)[f])
387 388
388 389 def fileannotate(self, f, node):
389 390 bcache = {}
390 391 ncache = {}
391 392 fl = self.repo.file(f)
392 393 n = fl.lookup(node)
393 394 node = hex(n)
394 395 changerev = fl.linkrev(n)
395 396
396 397 cl = self.repo.changelog
397 398 cn = cl.node(changerev)
398 399 cs = cl.read(cn)
399 400 mfn = cs[0]
400 401
401 402 def annotate(**map):
402 403 parity = 1
403 404 last = None
404 405 for r, l in fl.annotate(n):
405 406 try:
406 407 cnode = ncache[r]
407 408 except KeyError:
408 409 cnode = ncache[r] = self.repo.changelog.node(r)
409 410
410 411 try:
411 412 name = bcache[r]
412 413 except KeyError:
413 414 cl = self.repo.changelog.read(cnode)
414 415 bcache[r] = name = self.repo.ui.shortuser(cl[1])
415 416
416 417 if last != cnode:
417 418 parity = 1 - parity
418 419 last = cnode
419 420
420 421 yield {"parity": parity,
421 422 "node": hex(cnode),
422 423 "rev": r,
423 424 "author": name,
424 425 "file": f,
425 426 "line": l}
426 427
427 428 yield self.t("fileannotate",
428 429 file=f,
429 430 filenode=node,
430 431 annotate=annotate,
431 432 path=_up(f),
432 433 rev=changerev,
433 434 node=hex(cn),
434 435 manifest=hex(mfn),
435 436 author=cs[1],
436 437 date=cs[2],
437 438 rename=self.renamelink(fl, n),
438 439 parent=self.siblings(fl.parents(n), fl.rev, file=f),
439 440 child=self.siblings(fl.children(n), fl.rev, file=f),
440 441 permissions=self.repo.manifest.readflags(mfn)[f])
441 442
442 443 def manifest(self, mnode, path):
443 444 man = self.repo.manifest
444 445 mn = man.lookup(mnode)
445 446 mnode = hex(mn)
446 447 mf = man.read(mn)
447 448 rev = man.rev(mn)
448 449 changerev = man.linkrev(mn)
449 450 node = self.repo.changelog.node(changerev)
450 451 mff = man.readflags(mn)
451 452
452 453 files = {}
453 454
454 455 p = path[1:]
455 456 if p and p[-1] != "/":
456 457 p += "/"
457 458 l = len(p)
458 459
459 460 for f,n in mf.items():
460 461 if f[:l] != p:
461 462 continue
462 463 remain = f[l:]
463 464 if "/" in remain:
464 465 short = remain[:remain.find("/") + 1] # bleah
465 466 files[short] = (f, None)
466 467 else:
467 468 short = os.path.basename(remain)
468 469 files[short] = (f, n)
469 470
470 471 def filelist(**map):
471 472 parity = 0
472 473 fl = files.keys()
473 474 fl.sort()
474 475 for f in fl:
475 476 full, fnode = files[f]
476 477 if not fnode:
477 478 continue
478 479
479 480 yield {"file": full,
480 481 "manifest": mnode,
481 482 "filenode": hex(fnode),
482 483 "parity": parity,
483 484 "basename": f,
484 485 "permissions": mff[full]}
485 486 parity = 1 - parity
486 487
487 488 def dirlist(**map):
488 489 parity = 0
489 490 fl = files.keys()
490 491 fl.sort()
491 492 for f in fl:
492 493 full, fnode = files[f]
493 494 if fnode:
494 495 continue
495 496
496 497 yield {"parity": parity,
497 498 "path": os.path.join(path, f),
498 499 "manifest": mnode,
499 500 "basename": f[:-1]}
500 501 parity = 1 - parity
501 502
502 503 yield self.t("manifest",
503 504 manifest=mnode,
504 505 rev=rev,
505 506 node=hex(node),
506 507 path=path,
507 508 up=_up(path),
508 509 fentries=filelist,
509 510 dentries=dirlist,
510 511 archives=self.archivelist(hex(node)))
511 512
512 513 def tags(self):
513 514 cl = self.repo.changelog
514 515 mf = cl.read(cl.tip())[0]
515 516
516 517 i = self.repo.tagslist()
517 518 i.reverse()
518 519
519 520 def entries(notip=False, **map):
520 521 parity = 0
521 522 for k,n in i:
522 523 if notip and k == "tip": continue
523 524 yield {"parity": parity,
524 525 "tag": k,
525 526 "tagmanifest": hex(cl.read(n)[0]),
526 527 "date": cl.read(n)[2],
527 528 "node": hex(n)}
528 529 parity = 1 - parity
529 530
530 531 yield self.t("tags",
531 532 manifest=hex(mf),
532 533 entries=lambda **x: entries(False, **x),
533 534 entriesnotip=lambda **x: entries(True, **x))
534 535
535 536 def summary(self):
536 537 cl = self.repo.changelog
537 538 mf = cl.read(cl.tip())[0]
538 539
539 540 i = self.repo.tagslist()
540 541 i.reverse()
541 542
542 543 def tagentries(**map):
543 544 parity = 0
544 545 count = 0
545 546 for k,n in i:
546 547 if k == "tip": # skip tip
547 548 continue;
548 549
549 550 count += 1
550 551 if count > 10: # limit to 10 tags
551 552 break;
552 553
553 554 c = cl.read(n)
554 555 m = c[0]
555 556 t = c[2]
556 557
557 558 yield self.t("tagentry",
558 559 parity = parity,
559 560 tag = k,
560 561 node = hex(n),
561 562 date = t,
562 563 tagmanifest = hex(m))
563 564 parity = 1 - parity
564 565
565 566 def changelist(**map):
566 567 parity = 0
567 568 cl = self.repo.changelog
568 569 l = [] # build a list in forward order for efficiency
569 570 for i in range(start, end):
570 571 n = cl.node(i)
571 572 changes = cl.read(n)
572 573 hn = hex(n)
573 574 t = changes[2]
574 575
575 576 l.insert(0, self.t(
576 577 'shortlogentry',
577 578 parity = parity,
578 579 author = changes[1],
579 580 manifest = hex(changes[0]),
580 581 desc = changes[4],
581 582 date = t,
582 583 rev = i,
583 584 node = hn))
584 585 parity = 1 - parity
585 586
586 587 yield l
587 588
588 589 cl = self.repo.changelog
589 590 mf = cl.read(cl.tip())[0]
590 591 count = cl.count()
591 592 start = max(0, count - self.maxchanges)
592 593 end = min(count, start + self.maxchanges)
593 594 pos = end - 1
594 595
595 596 yield self.t("summary",
596 597 desc = self.repo.ui.config("web", "description", "unknown"),
597 598 owner = (self.repo.ui.config("ui", "username") or # preferred
598 599 self.repo.ui.config("web", "contact") or # deprecated
599 600 self.repo.ui.config("web", "author", "unknown")), # also
600 601 lastchange = (0, 0), # FIXME
601 602 manifest = hex(mf),
602 603 tags = tagentries,
603 604 shortlog = changelist)
604 605
605 606 def filediff(self, file, changeset):
606 607 cl = self.repo.changelog
607 608 n = self.repo.lookup(changeset)
608 609 changeset = hex(n)
609 610 p1 = cl.parents(n)[0]
610 611 cs = cl.read(n)
611 612 mf = self.repo.manifest.read(cs[0])
612 613
613 614 def diff(**map):
614 615 yield self.diff(p1, n, [file])
615 616
616 617 yield self.t("filediff",
617 618 file=file,
618 619 filenode=hex(mf.get(file, nullid)),
619 620 node=changeset,
620 621 rev=self.repo.changelog.rev(n),
621 622 parent=self.siblings(cl.parents(n), cl.rev),
622 623 child=self.siblings(cl.children(n), cl.rev),
623 624 diff=diff)
624 625
625 626 archive_specs = {
626 627 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', 'x-bzip2'),
627 628 'gz': ('application/x-tar', 'tgz', '.tar.gz', 'x-gzip'),
628 629 'zip': ('application/zip', 'zip', '.zip', None),
629 630 }
630 631
631 632 def archive(self, req, cnode, type):
632 633 reponame = re.sub(r"\W+", "-", os.path.basename(self.reponame))
633 634 name = "%s-%s" % (reponame, short(cnode))
634 635 mimetype, artype, extension, encoding = self.archive_specs[type]
635 636 headers = [('Content-type', mimetype),
636 637 ('Content-disposition', 'attachment; filename=%s%s' %
637 638 (name, extension))]
638 639 if encoding:
639 640 headers.append(('Content-encoding', encoding))
640 641 req.header(headers)
641 642 archival.archive(self.repo, req.out, cnode, artype, prefix=name)
642 643
643 644 # add tags to things
644 645 # tags -> list of changesets corresponding to tags
645 646 # find tag, changeset, file
646 647
647 648 def run(self, req=hgrequest()):
648 649 def clean(path):
649 650 p = util.normpath(path)
650 651 if p[:2] == "..":
651 652 raise "suspicious path"
652 653 return p
653 654
654 655 def header(**map):
655 656 yield self.t("header", **map)
656 657
657 658 def footer(**map):
658 659 yield self.t("footer",
659 660 motd=self.repo.ui.config("web", "motd", ""),
660 661 **map)
661 662
662 663 def expand_form(form):
663 664 shortcuts = {
664 665 'cl': [('cmd', ['changelog']), ('rev', None)],
665 666 'cs': [('cmd', ['changeset']), ('node', None)],
666 667 'f': [('cmd', ['file']), ('filenode', None)],
667 668 'fl': [('cmd', ['filelog']), ('filenode', None)],
668 669 'fd': [('cmd', ['filediff']), ('node', None)],
669 670 'fa': [('cmd', ['annotate']), ('filenode', None)],
670 671 'mf': [('cmd', ['manifest']), ('manifest', None)],
671 672 'ca': [('cmd', ['archive']), ('node', None)],
672 673 'tags': [('cmd', ['tags'])],
673 674 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
674 675 'static': [('cmd', ['static']), ('file', None)]
675 676 }
676 677
677 678 for k in shortcuts.iterkeys():
678 679 if form.has_key(k):
679 680 for name, value in shortcuts[k]:
680 681 if value is None:
681 682 value = form[k]
682 683 form[name] = value
683 684 del form[k]
684 685
685 686 self.refresh()
686 687
687 688 expand_form(req.form)
688 689
689 690 t = self.repo.ui.config("web", "templates", templater.templatepath())
690 691 static = self.repo.ui.config("web", "static", os.path.join(t,"static"))
691 692 m = os.path.join(t, "map")
692 693 style = self.repo.ui.config("web", "style", "")
693 694 if req.form.has_key('style'):
694 695 style = req.form['style'][0]
695 696 if style:
696 697 b = os.path.basename("map-" + style)
697 698 p = os.path.join(t, b)
698 699 if os.path.isfile(p):
699 700 m = p
700 701
701 702 port = req.env["SERVER_PORT"]
702 703 port = port != "80" and (":" + port) or ""
703 704 uri = req.env["REQUEST_URI"]
704 705 if "?" in uri:
705 706 uri = uri.split("?")[0]
706 707 url = "http://%s%s%s" % (req.env["SERVER_NAME"], port, uri)
707 708 if not self.reponame:
708 709 self.reponame = (self.repo.ui.config("web", "name")
709 710 or uri.strip('/') or self.repo.root)
710 711
711 712 self.t = templater.templater(m, templater.common_filters,
712 713 defaults={"url": url,
713 714 "repo": self.reponame,
714 715 "header": header,
715 716 "footer": footer,
716 717 })
717 718
718 719 if not req.form.has_key('cmd'):
719 720 req.form['cmd'] = [self.t.cache['default'],]
720 721
721 722 cmd = req.form['cmd'][0]
722 723 if cmd == 'changelog':
723 724 hi = self.repo.changelog.count() - 1
724 725 if req.form.has_key('rev'):
725 726 hi = req.form['rev'][0]
726 727 try:
727 728 hi = self.repo.changelog.rev(self.repo.lookup(hi))
728 729 except hg.RepoError:
729 730 req.write(self.search(hi)) # XXX redirect to 404 page?
730 731 return
731 732
732 733 req.write(self.changelog(hi))
733 734
734 735 elif cmd == 'changeset':
735 736 req.write(self.changeset(req.form['node'][0]))
736 737
737 738 elif cmd == 'manifest':
738 739 req.write(self.manifest(req.form['manifest'][0],
739 740 clean(req.form['path'][0])))
740 741
741 742 elif cmd == 'tags':
742 743 req.write(self.tags())
743 744
744 745 elif cmd == 'summary':
745 746 req.write(self.summary())
746 747
747 748 elif cmd == 'filediff':
748 749 req.write(self.filediff(clean(req.form['file'][0]),
749 750 req.form['node'][0]))
750 751
751 752 elif cmd == 'file':
752 753 req.write(self.filerevision(clean(req.form['file'][0]),
753 754 req.form['filenode'][0]))
754 755
755 756 elif cmd == 'annotate':
756 757 req.write(self.fileannotate(clean(req.form['file'][0]),
757 758 req.form['filenode'][0]))
758 759
759 760 elif cmd == 'filelog':
760 761 req.write(self.filelog(clean(req.form['file'][0]),
761 762 req.form['filenode'][0]))
762 763
763 764 elif cmd == 'heads':
764 765 req.httphdr("application/mercurial-0.1")
765 766 h = self.repo.heads()
766 767 req.write(" ".join(map(hex, h)) + "\n")
767 768
768 769 elif cmd == 'branches':
769 770 req.httphdr("application/mercurial-0.1")
770 771 nodes = []
771 772 if req.form.has_key('nodes'):
772 773 nodes = map(bin, req.form['nodes'][0].split(" "))
773 774 for b in self.repo.branches(nodes):
774 775 req.write(" ".join(map(hex, b)) + "\n")
775 776
776 777 elif cmd == 'between':
777 778 req.httphdr("application/mercurial-0.1")
778 779 nodes = []
779 780 if req.form.has_key('pairs'):
780 781 pairs = [map(bin, p.split("-"))
781 782 for p in req.form['pairs'][0].split(" ")]
782 783 for b in self.repo.between(pairs):
783 784 req.write(" ".join(map(hex, b)) + "\n")
784 785
785 786 elif cmd == 'changegroup':
786 787 req.httphdr("application/mercurial-0.1")
787 788 nodes = []
788 789 if not self.allowpull:
789 790 return
790 791
791 792 if req.form.has_key('roots'):
792 793 nodes = map(bin, req.form['roots'][0].split(" "))
793 794
794 795 z = zlib.compressobj()
795 796 f = self.repo.changegroup(nodes, 'serve')
796 797 while 1:
797 798 chunk = f.read(4096)
798 799 if not chunk:
799 800 break
800 801 req.write(z.compress(chunk))
801 802
802 803 req.write(z.flush())
803 804
804 805 elif cmd == 'archive':
805 806 changeset = self.repo.lookup(req.form['node'][0])
806 807 type = req.form['type'][0]
807 808 allowed = self.repo.ui.config("web", "allow_archive", "").split()
808 809 if (type in self.archives and (type in allowed or
809 810 self.repo.ui.configbool("web", "allow" + type, False))):
810 811 self.archive(req, changeset, type)
811 812 return
812 813
813 814 req.write(self.t("error"))
814 815
815 816 elif cmd == 'static':
816 817 fname = req.form['file'][0]
817 818 req.write(staticfile(static, fname)
818 819 or self.t("error", error="%r not found" % fname))
819 820
820 821 else:
821 822 req.write(self.t("error"))
@@ -1,153 +1,154 b''
1 1 # hgweb.py - web interface to a mercurial repository
2 2 #
3 3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 4 # Copyright 2005 Matt Mackall <mpm@selenic.com>
5 5 #
6 6 # This software may be used and distributed according to the terms
7 7 # of the GNU General Public License, incorporated herein by reference.
8 8
9 9 import os
10 10 from mercurial.demandload import demandload
11 11 demandload(globals(), "ConfigParser")
12 12 demandload(globals(), "mercurial:ui,hg,util,templater")
13 13 demandload(globals(), "mercurial.hgweb.request:hgrequest")
14 14 from mercurial.i18n import gettext as _
15 15
16 16 # This is a stopgap
17 17 class hgwebdir(object):
18 18 def __init__(self, config):
19 19 def cleannames(items):
20 20 return [(name.strip(os.sep), path) for name, path in items]
21 21
22 22 self.motd = ""
23 23 self.repos_sorted = ('name', False)
24 24 if isinstance(config, (list, tuple)):
25 25 self.repos = cleannames(config)
26 26 self.repos_sorted = ('', False)
27 27 elif isinstance(config, dict):
28 28 self.repos = cleannames(config.items())
29 29 self.repos.sort()
30 30 else:
31 31 cp = ConfigParser.SafeConfigParser()
32 32 cp.read(config)
33 33 self.repos = []
34 34 if cp.has_section('web') and cp.has_option('web', 'motd'):
35 35 self.motd = cp.get('web', 'motd')
36 36 if cp.has_section('paths'):
37 37 self.repos.extend(cleannames(cp.items('paths')))
38 38 if cp.has_section('collections'):
39 39 for prefix, root in cp.items('collections'):
40 40 for path in util.walkrepos(root):
41 41 repo = os.path.normpath(path)
42 42 name = repo
43 43 if name.startswith(prefix):
44 44 name = name[len(prefix):]
45 45 self.repos.append((name.lstrip(os.sep), repo))
46 46 self.repos.sort()
47 47
48 48 def run(self, req=hgrequest()):
49 49 def header(**map):
50 50 yield tmpl("header", **map)
51 51
52 52 def footer(**map):
53 53 yield tmpl("footer", motd=self.motd, **map)
54 54
55 55 m = os.path.join(templater.templatepath(), "map")
56 56 tmpl = templater.templater(m, templater.common_filters,
57 57 defaults={"header": header,
58 58 "footer": footer})
59 59
60 60 def archivelist(ui, nodeid, url):
61 al = ui.config("web", "allow_archive", "").split()
61 allowed = (ui.config("web", "allow_archive", "")
62 .replace(",", " ").split())
62 63 for i in ['zip', 'gz', 'bz2']:
63 if i in al or ui.configbool("web", "allow" + i, False):
64 if i in allowed or ui.configbool("web", "allow" + i):
64 65 yield {"type" : i, "node": nodeid, "url": url}
65 66
66 67 def entries(sortcolumn="", descending=False, **map):
67 68 rows = []
68 69 parity = 0
69 70 for name, path in self.repos:
70 71 u = ui.ui()
71 72 try:
72 73 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
73 74 except IOError:
74 75 pass
75 76 get = u.config
76 77
77 78 url = ('/'.join([req.env["REQUEST_URI"].split('?')[0], name])
78 79 .replace("//", "/"))
79 80
80 81 # update time with local timezone
81 82 try:
82 83 d = (get_mtime(path), util.makedate()[1])
83 84 except OSError:
84 85 continue
85 86
86 87 contact = (get("ui", "username") or # preferred
87 88 get("web", "contact") or # deprecated
88 89 get("web", "author", "")) # also
89 90 description = get("web", "description", "")
90 91 name = get("web", "name", name)
91 92 row = dict(contact=contact or "unknown",
92 93 contact_sort=contact.upper() or "unknown",
93 94 name=name,
94 95 name_sort=name,
95 96 url=url,
96 97 description=description or "unknown",
97 98 description_sort=description.upper() or "unknown",
98 99 lastchange=d,
99 100 lastchange_sort=d[1]-d[0],
100 101 archives=archivelist(u, "tip", url))
101 102 if (not sortcolumn
102 103 or (sortcolumn, descending) == self.repos_sorted):
103 104 # fast path for unsorted output
104 105 row['parity'] = parity
105 106 parity = 1 - parity
106 107 yield row
107 108 else:
108 109 rows.append((row["%s_sort" % sortcolumn], row))
109 110 if rows:
110 111 rows.sort()
111 112 if descending:
112 113 rows.reverse()
113 114 for key, row in rows:
114 115 row['parity'] = parity
115 116 parity = 1 - parity
116 117 yield row
117 118
118 119 virtual = req.env.get("PATH_INFO", "").strip('/')
119 120 if virtual:
120 121 real = dict(self.repos).get(virtual)
121 122 if real:
122 123 try:
123 124 hgweb(real).run(req)
124 125 except IOError, inst:
125 126 req.write(tmpl("error", error=inst.strerror))
126 127 except hg.RepoError, inst:
127 128 req.write(tmpl("error", error=str(inst)))
128 129 else:
129 130 req.write(tmpl("notfound", repo=virtual))
130 131 else:
131 132 if req.form.has_key('static'):
132 133 static = os.path.join(templater.templatepath(), "static")
133 134 fname = req.form['static'][0]
134 135 req.write(staticfile(static, fname)
135 136 or tmpl("error", error="%r not found" % fname))
136 137 else:
137 138 sortable = ["name", "description", "contact", "lastchange"]
138 139 sortcolumn, descending = self.repos_sorted
139 140 if req.form.has_key('sort'):
140 141 sortcolumn = req.form['sort'][0]
141 142 descending = sortcolumn.startswith('-')
142 143 if descending:
143 144 sortcolumn = sortcolumn[1:]
144 145 if sortcolumn not in sortable:
145 146 sortcolumn = ""
146 147
147 148 sort = [("sort_%s" % column,
148 149 "%s%s" % ((not descending and column == sortcolumn)
149 150 and "-" or "", column))
150 151 for column in sortable]
151 152 req.write(tmpl("index", entries=entries,
152 153 sortcolumn=sortcolumn, descending=descending,
153 154 **dict(sort)))
General Comments 0
You need to be logged in to leave comments. Login now