##// END OF EJS Templates
[hgweb] Fixed up gitweb templates...
Josef "Jeff" Sipek -
r2683:8a798185 default
parent child Browse files
Show More
@@ -1,960 +1,961
1 1 # hgweb/hgweb_mod.py - Web interface for a 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 mimetools cStringIO sys tempfile")
14 14 demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,streamclone")
15 15 demandload(globals(), "mercurial:templater")
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 self.templatepath = self.repo.ui.config("web", "templates",
41 41 templater.templatepath())
42 42
43 43 def refresh(self):
44 44 mtime = get_mtime(self.repo.root)
45 45 if mtime != self.mtime:
46 46 self.mtime = mtime
47 47 self.repo = hg.repository(self.repo.ui, self.repo.root)
48 48 self.maxchanges = int(self.repo.ui.config("web", "maxchanges", 10))
49 49 self.maxfiles = int(self.repo.ui.config("web", "maxfiles", 10))
50 50 self.allowpull = self.repo.ui.configbool("web", "allowpull", True)
51 51
52 52 def archivelist(self, nodeid):
53 53 allowed = self.repo.ui.configlist("web", "allow_archive")
54 54 for i in self.archives:
55 55 if i in allowed or self.repo.ui.configbool("web", "allow" + i):
56 56 yield {"type" : i, "node" : nodeid, "url": ""}
57 57
58 58 def listfiles(self, files, mf):
59 59 for f in files[:self.maxfiles]:
60 60 yield self.t("filenodelink", node=hex(mf[f]), file=f)
61 61 if len(files) > self.maxfiles:
62 62 yield self.t("fileellipses")
63 63
64 64 def listfilediffs(self, files, changeset):
65 65 for f in files[:self.maxfiles]:
66 66 yield self.t("filedifflink", node=hex(changeset), file=f)
67 67 if len(files) > self.maxfiles:
68 68 yield self.t("fileellipses")
69 69
70 70 def siblings(self, siblings=[], rev=None, hiderev=None, **args):
71 71 if not rev:
72 72 rev = lambda x: ""
73 73 siblings = [s for s in siblings if s != nullid]
74 74 if len(siblings) == 1 and rev(siblings[0]) == hiderev:
75 75 return
76 76 for s in siblings:
77 77 yield dict(node=hex(s), rev=rev(s), **args)
78 78
79 79 def renamelink(self, fl, node):
80 80 r = fl.renamed(node)
81 81 if r:
82 82 return [dict(file=r[0], node=hex(r[1]))]
83 83 return []
84 84
85 85 def showtag(self, t1, node=nullid, **args):
86 86 for t in self.repo.nodetags(node):
87 87 yield self.t(t1, tag=t, **args)
88 88
89 89 def diff(self, node1, node2, files):
90 90 def filterfiles(filters, files):
91 91 l = [x for x in files if x in filters]
92 92
93 93 for t in filters:
94 94 if t and t[-1] != os.sep:
95 95 t += os.sep
96 96 l += [x for x in files if x.startswith(t)]
97 97 return l
98 98
99 99 parity = [0]
100 100 def diffblock(diff, f, fn):
101 101 yield self.t("diffblock",
102 102 lines=prettyprintlines(diff),
103 103 parity=parity[0],
104 104 file=f,
105 105 filenode=hex(fn or nullid))
106 106 parity[0] = 1 - parity[0]
107 107
108 108 def prettyprintlines(diff):
109 109 for l in diff.splitlines(1):
110 110 if l.startswith('+'):
111 111 yield self.t("difflineplus", line=l)
112 112 elif l.startswith('-'):
113 113 yield self.t("difflineminus", line=l)
114 114 elif l.startswith('@'):
115 115 yield self.t("difflineat", line=l)
116 116 else:
117 117 yield self.t("diffline", line=l)
118 118
119 119 r = self.repo
120 120 cl = r.changelog
121 121 mf = r.manifest
122 122 change1 = cl.read(node1)
123 123 change2 = cl.read(node2)
124 124 mmap1 = mf.read(change1[0])
125 125 mmap2 = mf.read(change2[0])
126 126 date1 = util.datestr(change1[2])
127 127 date2 = util.datestr(change2[2])
128 128
129 129 modified, added, removed, deleted, unknown = r.changes(node1, node2)
130 130 if files:
131 131 modified, added, removed = map(lambda x: filterfiles(files, x),
132 132 (modified, added, removed))
133 133
134 134 diffopts = self.repo.ui.diffopts()
135 135 showfunc = diffopts['showfunc']
136 136 ignorews = diffopts['ignorews']
137 137 ignorewsamount = diffopts['ignorewsamount']
138 138 ignoreblanklines = diffopts['ignoreblanklines']
139 139 for f in modified:
140 140 to = r.file(f).read(mmap1[f])
141 141 tn = r.file(f).read(mmap2[f])
142 142 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
143 143 showfunc=showfunc, ignorews=ignorews,
144 144 ignorewsamount=ignorewsamount,
145 145 ignoreblanklines=ignoreblanklines), f, tn)
146 146 for f in added:
147 147 to = None
148 148 tn = r.file(f).read(mmap2[f])
149 149 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
150 150 showfunc=showfunc, ignorews=ignorews,
151 151 ignorewsamount=ignorewsamount,
152 152 ignoreblanklines=ignoreblanklines), f, tn)
153 153 for f in removed:
154 154 to = r.file(f).read(mmap1[f])
155 155 tn = None
156 156 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
157 157 showfunc=showfunc, ignorews=ignorews,
158 158 ignorewsamount=ignorewsamount,
159 159 ignoreblanklines=ignoreblanklines), f, tn)
160 160
161 161 def changelog(self, pos):
162 162 def changenav(**map):
163 163 def seq(factor, maxchanges=None):
164 164 if maxchanges:
165 165 yield maxchanges
166 166 if maxchanges >= 20 and maxchanges <= 40:
167 167 yield 50
168 168 else:
169 169 yield 1 * factor
170 170 yield 3 * factor
171 171 for f in seq(factor * 10):
172 172 yield f
173 173
174 174 l = []
175 175 last = 0
176 176 for f in seq(1, self.maxchanges):
177 177 if f < self.maxchanges or f <= last:
178 178 continue
179 179 if f > count:
180 180 break
181 181 last = f
182 182 r = "%d" % f
183 183 if pos + f < count:
184 184 l.append(("+" + r, pos + f))
185 185 if pos - f >= 0:
186 186 l.insert(0, ("-" + r, pos - f))
187 187
188 188 yield {"rev": 0, "label": "(0)"}
189 189
190 190 for label, rev in l:
191 191 yield {"label": label, "rev": rev}
192 192
193 193 yield {"label": "tip", "rev": "tip"}
194 194
195 195 def changelist(**map):
196 196 parity = (start - end) & 1
197 197 cl = self.repo.changelog
198 198 l = [] # build a list in forward order for efficiency
199 199 for i in range(start, end):
200 200 n = cl.node(i)
201 201 changes = cl.read(n)
202 202 hn = hex(n)
203 203
204 204 l.insert(0, {"parity": parity,
205 205 "author": changes[1],
206 206 "parent": self.siblings(cl.parents(n), cl.rev,
207 207 cl.rev(n) - 1),
208 208 "child": self.siblings(cl.children(n), cl.rev,
209 209 cl.rev(n) + 1),
210 210 "changelogtag": self.showtag("changelogtag",n),
211 211 "manifest": hex(changes[0]),
212 212 "desc": changes[4],
213 213 "date": changes[2],
214 214 "files": self.listfilediffs(changes[3], n),
215 215 "rev": i,
216 216 "node": hn})
217 217 parity = 1 - parity
218 218
219 219 for e in l:
220 220 yield e
221 221
222 222 cl = self.repo.changelog
223 223 mf = cl.read(cl.tip())[0]
224 224 count = cl.count()
225 225 start = max(0, pos - self.maxchanges + 1)
226 226 end = min(count, start + self.maxchanges)
227 227 pos = end - 1
228 228
229 229 yield self.t('changelog',
230 230 changenav=changenav,
231 231 manifest=hex(mf),
232 232 rev=pos, changesets=count, entries=changelist,
233 233 archives=self.archivelist("tip"))
234 234
235 235 def search(self, query):
236 236
237 237 def changelist(**map):
238 238 cl = self.repo.changelog
239 239 count = 0
240 240 qw = query.lower().split()
241 241
242 242 def revgen():
243 243 for i in range(cl.count() - 1, 0, -100):
244 244 l = []
245 245 for j in range(max(0, i - 100), i):
246 246 n = cl.node(j)
247 247 changes = cl.read(n)
248 248 l.append((n, j, changes))
249 249 l.reverse()
250 250 for e in l:
251 251 yield e
252 252
253 253 for n, i, changes in revgen():
254 254 miss = 0
255 255 for q in qw:
256 256 if not (q in changes[1].lower() or
257 257 q in changes[4].lower() or
258 258 q in " ".join(changes[3][:20]).lower()):
259 259 miss = 1
260 260 break
261 261 if miss:
262 262 continue
263 263
264 264 count += 1
265 265 hn = hex(n)
266 266
267 267 yield self.t('searchentry',
268 268 parity=count & 1,
269 269 author=changes[1],
270 270 parent=self.siblings(cl.parents(n), cl.rev),
271 271 child=self.siblings(cl.children(n), cl.rev),
272 272 changelogtag=self.showtag("changelogtag",n),
273 273 manifest=hex(changes[0]),
274 274 desc=changes[4],
275 275 date=changes[2],
276 276 files=self.listfilediffs(changes[3], n),
277 277 rev=i,
278 278 node=hn)
279 279
280 280 if count >= self.maxchanges:
281 281 break
282 282
283 283 cl = self.repo.changelog
284 284 mf = cl.read(cl.tip())[0]
285 285
286 286 yield self.t('search',
287 287 query=query,
288 288 manifest=hex(mf),
289 289 entries=changelist)
290 290
291 291 def changeset(self, nodeid):
292 292 cl = self.repo.changelog
293 293 n = self.repo.lookup(nodeid)
294 294 nodeid = hex(n)
295 295 changes = cl.read(n)
296 296 p1 = cl.parents(n)[0]
297 297
298 298 files = []
299 299 mf = self.repo.manifest.read(changes[0])
300 300 for f in changes[3]:
301 301 files.append(self.t("filenodelink",
302 302 filenode=hex(mf.get(f, nullid)), file=f))
303 303
304 304 def diff(**map):
305 305 yield self.diff(p1, n, None)
306 306
307 307 yield self.t('changeset',
308 308 diff=diff,
309 309 rev=cl.rev(n),
310 310 node=nodeid,
311 311 parent=self.siblings(cl.parents(n), cl.rev),
312 312 child=self.siblings(cl.children(n), cl.rev),
313 313 changesettag=self.showtag("changesettag",n),
314 314 manifest=hex(changes[0]),
315 315 author=changes[1],
316 316 desc=changes[4],
317 317 date=changes[2],
318 318 files=files,
319 319 archives=self.archivelist(nodeid))
320 320
321 321 def filelog(self, f, filenode):
322 322 cl = self.repo.changelog
323 323 fl = self.repo.file(f)
324 324 filenode = hex(fl.lookup(filenode))
325 325 count = fl.count()
326 326
327 327 def entries(**map):
328 328 l = []
329 329 parity = (count - 1) & 1
330 330
331 331 for i in range(count):
332 332 n = fl.node(i)
333 333 lr = fl.linkrev(n)
334 334 cn = cl.node(lr)
335 335 cs = cl.read(cl.node(lr))
336 336
337 337 l.insert(0, {"parity": parity,
338 338 "filenode": hex(n),
339 339 "filerev": i,
340 340 "file": f,
341 341 "node": hex(cn),
342 342 "author": cs[1],
343 343 "date": cs[2],
344 344 "rename": self.renamelink(fl, n),
345 345 "parent": self.siblings(fl.parents(n),
346 346 fl.rev, file=f),
347 347 "child": self.siblings(fl.children(n),
348 348 fl.rev, file=f),
349 349 "desc": cs[4]})
350 350 parity = 1 - parity
351 351
352 352 for e in l:
353 353 yield e
354 354
355 355 yield self.t("filelog", file=f, filenode=filenode, entries=entries)
356 356
357 357 def filerevision(self, f, node):
358 358 fl = self.repo.file(f)
359 359 n = fl.lookup(node)
360 360 node = hex(n)
361 361 text = fl.read(n)
362 362 changerev = fl.linkrev(n)
363 363 cl = self.repo.changelog
364 364 cn = cl.node(changerev)
365 365 cs = cl.read(cn)
366 366 mfn = cs[0]
367 367
368 368 mt = mimetypes.guess_type(f)[0]
369 369 rawtext = text
370 370 if util.binary(text):
371 371 mt = mt or 'application/octet-stream'
372 372 text = "(binary:%s)" % mt
373 373 mt = mt or 'text/plain'
374 374
375 375 def lines():
376 376 for l, t in enumerate(text.splitlines(1)):
377 377 yield {"line": t,
378 378 "linenumber": "% 6d" % (l + 1),
379 379 "parity": l & 1}
380 380
381 381 yield self.t("filerevision",
382 382 file=f,
383 383 filenode=node,
384 384 path=_up(f),
385 385 text=lines(),
386 386 raw=rawtext,
387 387 mimetype=mt,
388 388 rev=changerev,
389 389 node=hex(cn),
390 390 manifest=hex(mfn),
391 391 author=cs[1],
392 392 date=cs[2],
393 393 parent=self.siblings(fl.parents(n), fl.rev, file=f),
394 394 child=self.siblings(fl.children(n), fl.rev, file=f),
395 395 rename=self.renamelink(fl, n),
396 396 permissions=self.repo.manifest.readflags(mfn)[f])
397 397
398 398 def fileannotate(self, f, node):
399 399 bcache = {}
400 400 ncache = {}
401 401 fl = self.repo.file(f)
402 402 n = fl.lookup(node)
403 403 node = hex(n)
404 404 changerev = fl.linkrev(n)
405 405
406 406 cl = self.repo.changelog
407 407 cn = cl.node(changerev)
408 408 cs = cl.read(cn)
409 409 mfn = cs[0]
410 410
411 411 def annotate(**map):
412 412 parity = 1
413 413 last = None
414 414 for r, l in fl.annotate(n):
415 415 try:
416 416 cnode = ncache[r]
417 417 except KeyError:
418 418 cnode = ncache[r] = self.repo.changelog.node(r)
419 419
420 420 try:
421 421 name = bcache[r]
422 422 except KeyError:
423 423 cl = self.repo.changelog.read(cnode)
424 424 bcache[r] = name = self.repo.ui.shortuser(cl[1])
425 425
426 426 if last != cnode:
427 427 parity = 1 - parity
428 428 last = cnode
429 429
430 430 yield {"parity": parity,
431 431 "node": hex(cnode),
432 432 "rev": r,
433 433 "author": name,
434 434 "file": f,
435 435 "line": l}
436 436
437 437 yield self.t("fileannotate",
438 438 file=f,
439 439 filenode=node,
440 440 annotate=annotate,
441 441 path=_up(f),
442 442 rev=changerev,
443 443 node=hex(cn),
444 444 manifest=hex(mfn),
445 445 author=cs[1],
446 446 date=cs[2],
447 447 rename=self.renamelink(fl, n),
448 448 parent=self.siblings(fl.parents(n), fl.rev, file=f),
449 449 child=self.siblings(fl.children(n), fl.rev, file=f),
450 450 permissions=self.repo.manifest.readflags(mfn)[f])
451 451
452 452 def manifest(self, mnode, path):
453 453 man = self.repo.manifest
454 454 mn = man.lookup(mnode)
455 455 mnode = hex(mn)
456 456 mf = man.read(mn)
457 457 rev = man.rev(mn)
458 458 changerev = man.linkrev(mn)
459 459 node = self.repo.changelog.node(changerev)
460 460 mff = man.readflags(mn)
461 461
462 462 files = {}
463 463
464 464 p = path[1:]
465 465 if p and p[-1] != "/":
466 466 p += "/"
467 467 l = len(p)
468 468
469 469 for f,n in mf.items():
470 470 if f[:l] != p:
471 471 continue
472 472 remain = f[l:]
473 473 if "/" in remain:
474 474 short = remain[:remain.index("/") + 1] # bleah
475 475 files[short] = (f, None)
476 476 else:
477 477 short = os.path.basename(remain)
478 478 files[short] = (f, n)
479 479
480 480 def filelist(**map):
481 481 parity = 0
482 482 fl = files.keys()
483 483 fl.sort()
484 484 for f in fl:
485 485 full, fnode = files[f]
486 486 if not fnode:
487 487 continue
488 488
489 489 yield {"file": full,
490 490 "manifest": mnode,
491 491 "filenode": hex(fnode),
492 492 "parity": parity,
493 493 "basename": f,
494 494 "permissions": mff[full]}
495 495 parity = 1 - parity
496 496
497 497 def dirlist(**map):
498 498 parity = 0
499 499 fl = files.keys()
500 500 fl.sort()
501 501 for f in fl:
502 502 full, fnode = files[f]
503 503 if fnode:
504 504 continue
505 505
506 506 yield {"parity": parity,
507 507 "path": os.path.join(path, f),
508 508 "manifest": mnode,
509 509 "basename": f[:-1]}
510 510 parity = 1 - parity
511 511
512 512 yield self.t("manifest",
513 513 manifest=mnode,
514 514 rev=rev,
515 515 node=hex(node),
516 516 path=path,
517 517 up=_up(path),
518 518 fentries=filelist,
519 519 dentries=dirlist,
520 520 archives=self.archivelist(hex(node)))
521 521
522 522 def tags(self):
523 523 cl = self.repo.changelog
524 524 mf = cl.read(cl.tip())[0]
525 525
526 526 i = self.repo.tagslist()
527 527 i.reverse()
528 528
529 529 def entries(notip=False, **map):
530 530 parity = 0
531 531 for k,n in i:
532 532 if notip and k == "tip": continue
533 533 yield {"parity": parity,
534 534 "tag": k,
535 535 "tagmanifest": hex(cl.read(n)[0]),
536 536 "date": cl.read(n)[2],
537 537 "node": hex(n)}
538 538 parity = 1 - parity
539 539
540 540 yield self.t("tags",
541 541 manifest=hex(mf),
542 542 entries=lambda **x: entries(False, **x),
543 543 entriesnotip=lambda **x: entries(True, **x))
544 544
545 545 def summary(self):
546 546 cl = self.repo.changelog
547 547 mf = cl.read(cl.tip())[0]
548 548
549 549 i = self.repo.tagslist()
550 550 i.reverse()
551 551
552 552 def tagentries(**map):
553 553 parity = 0
554 554 count = 0
555 555 for k,n in i:
556 556 if k == "tip": # skip tip
557 557 continue;
558 558
559 559 count += 1
560 560 if count > 10: # limit to 10 tags
561 561 break;
562 562
563 563 c = cl.read(n)
564 564 m = c[0]
565 565 t = c[2]
566 566
567 567 yield self.t("tagentry",
568 568 parity = parity,
569 569 tag = k,
570 570 node = hex(n),
571 571 date = t,
572 572 tagmanifest = hex(m))
573 573 parity = 1 - parity
574 574
575 575 def changelist(**map):
576 576 parity = 0
577 577 cl = self.repo.changelog
578 578 l = [] # build a list in forward order for efficiency
579 579 for i in range(start, end):
580 580 n = cl.node(i)
581 581 changes = cl.read(n)
582 582 hn = hex(n)
583 583 t = changes[2]
584 584
585 585 l.insert(0, self.t(
586 586 'shortlogentry',
587 587 parity = parity,
588 588 author = changes[1],
589 589 manifest = hex(changes[0]),
590 590 desc = changes[4],
591 591 date = t,
592 592 rev = i,
593 593 node = hn))
594 594 parity = 1 - parity
595 595
596 596 yield l
597 597
598 598 cl = self.repo.changelog
599 599 mf = cl.read(cl.tip())[0]
600 600 count = cl.count()
601 601 start = max(0, count - self.maxchanges)
602 602 end = min(count, start + self.maxchanges)
603 603
604 604 yield self.t("summary",
605 605 desc = self.repo.ui.config("web", "description", "unknown"),
606 606 owner = (self.repo.ui.config("ui", "username") or # preferred
607 607 self.repo.ui.config("web", "contact") or # deprecated
608 608 self.repo.ui.config("web", "author", "unknown")), # also
609 609 lastchange = (0, 0), # FIXME
610 610 manifest = hex(mf),
611 611 tags = tagentries,
612 shortlog = changelist)
612 shortlog = changelist,
613 archives=self.archivelist("tip"))
613 614
614 615 def filediff(self, file, changeset):
615 616 cl = self.repo.changelog
616 617 n = self.repo.lookup(changeset)
617 618 changeset = hex(n)
618 619 p1 = cl.parents(n)[0]
619 620 cs = cl.read(n)
620 621 mf = self.repo.manifest.read(cs[0])
621 622
622 623 def diff(**map):
623 624 yield self.diff(p1, n, [file])
624 625
625 626 yield self.t("filediff",
626 627 file=file,
627 628 filenode=hex(mf.get(file, nullid)),
628 629 node=changeset,
629 630 rev=self.repo.changelog.rev(n),
630 631 parent=self.siblings(cl.parents(n), cl.rev),
631 632 child=self.siblings(cl.children(n), cl.rev),
632 633 diff=diff)
633 634
634 635 archive_specs = {
635 636 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
636 637 'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
637 638 'zip': ('application/zip', 'zip', '.zip', None),
638 639 }
639 640
640 641 def archive(self, req, cnode, type_):
641 642 reponame = re.sub(r"\W+", "-", os.path.basename(self.reponame))
642 643 name = "%s-%s" % (reponame, short(cnode))
643 644 mimetype, artype, extension, encoding = self.archive_specs[type_]
644 645 headers = [('Content-type', mimetype),
645 646 ('Content-disposition', 'attachment; filename=%s%s' %
646 647 (name, extension))]
647 648 if encoding:
648 649 headers.append(('Content-encoding', encoding))
649 650 req.header(headers)
650 651 archival.archive(self.repo, req.out, cnode, artype, prefix=name)
651 652
652 653 # add tags to things
653 654 # tags -> list of changesets corresponding to tags
654 655 # find tag, changeset, file
655 656
656 657 def cleanpath(self, path):
657 658 p = util.normpath(path)
658 659 if p[:2] == "..":
659 660 raise Exception("suspicious path")
660 661 return p
661 662
662 663 def run(self):
663 664 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
664 665 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
665 666 import mercurial.hgweb.wsgicgi as wsgicgi
666 667 from request import wsgiapplication
667 668 def make_web_app():
668 669 return self
669 670 wsgicgi.launch(wsgiapplication(make_web_app))
670 671
671 672 def run_wsgi(self, req):
672 673 def header(**map):
673 674 header_file = cStringIO.StringIO(''.join(self.t("header", **map)))
674 675 msg = mimetools.Message(header_file, 0)
675 676 req.header(msg.items())
676 677 yield header_file.read()
677 678
678 679 def rawfileheader(**map):
679 680 req.header([('Content-type', map['mimetype']),
680 681 ('Content-disposition', 'filename=%s' % map['file']),
681 682 ('Content-length', str(len(map['raw'])))])
682 683 yield ''
683 684
684 685 def footer(**map):
685 686 yield self.t("footer",
686 687 motd=self.repo.ui.config("web", "motd", ""),
687 688 **map)
688 689
689 690 def expand_form(form):
690 691 shortcuts = {
691 692 'cl': [('cmd', ['changelog']), ('rev', None)],
692 693 'cs': [('cmd', ['changeset']), ('node', None)],
693 694 'f': [('cmd', ['file']), ('filenode', None)],
694 695 'fl': [('cmd', ['filelog']), ('filenode', None)],
695 696 'fd': [('cmd', ['filediff']), ('node', None)],
696 697 'fa': [('cmd', ['annotate']), ('filenode', None)],
697 698 'mf': [('cmd', ['manifest']), ('manifest', None)],
698 699 'ca': [('cmd', ['archive']), ('node', None)],
699 700 'tags': [('cmd', ['tags'])],
700 701 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
701 702 'static': [('cmd', ['static']), ('file', None)]
702 703 }
703 704
704 705 for k in shortcuts.iterkeys():
705 706 if form.has_key(k):
706 707 for name, value in shortcuts[k]:
707 708 if value is None:
708 709 value = form[k]
709 710 form[name] = value
710 711 del form[k]
711 712
712 713 self.refresh()
713 714
714 715 expand_form(req.form)
715 716
716 717 m = os.path.join(self.templatepath, "map")
717 718 style = self.repo.ui.config("web", "style", "")
718 719 if req.form.has_key('style'):
719 720 style = req.form['style'][0]
720 721 if style:
721 722 b = os.path.basename("map-" + style)
722 723 p = os.path.join(self.templatepath, b)
723 724 if os.path.isfile(p):
724 725 m = p
725 726
726 727 port = req.env["SERVER_PORT"]
727 728 port = port != "80" and (":" + port) or ""
728 729 uri = req.env["REQUEST_URI"]
729 730 if "?" in uri:
730 731 uri = uri.split("?")[0]
731 732 url = "http://%s%s%s" % (req.env["SERVER_NAME"], port, uri)
732 733 if not self.reponame:
733 734 self.reponame = (self.repo.ui.config("web", "name")
734 735 or uri.strip('/') or self.repo.root)
735 736
736 737 self.t = templater.templater(m, templater.common_filters,
737 738 defaults={"url": url,
738 739 "repo": self.reponame,
739 740 "header": header,
740 741 "footer": footer,
741 742 "rawfileheader": rawfileheader,
742 743 })
743 744
744 745 if not req.form.has_key('cmd'):
745 746 req.form['cmd'] = [self.t.cache['default'],]
746 747
747 748 cmd = req.form['cmd'][0]
748 749
749 750 method = getattr(self, 'do_' + cmd, None)
750 751 if method:
751 752 method(req)
752 753 else:
753 754 req.write(self.t("error"))
754 755
755 756 def do_changelog(self, req):
756 757 hi = self.repo.changelog.count() - 1
757 758 if req.form.has_key('rev'):
758 759 hi = req.form['rev'][0]
759 760 try:
760 761 hi = self.repo.changelog.rev(self.repo.lookup(hi))
761 762 except hg.RepoError:
762 763 req.write(self.search(hi)) # XXX redirect to 404 page?
763 764 return
764 765
765 766 req.write(self.changelog(hi))
766 767
767 768 def do_changeset(self, req):
768 769 req.write(self.changeset(req.form['node'][0]))
769 770
770 771 def do_manifest(self, req):
771 772 req.write(self.manifest(req.form['manifest'][0],
772 773 self.cleanpath(req.form['path'][0])))
773 774
774 775 def do_tags(self, req):
775 776 req.write(self.tags())
776 777
777 778 def do_summary(self, req):
778 779 req.write(self.summary())
779 780
780 781 def do_filediff(self, req):
781 782 req.write(self.filediff(self.cleanpath(req.form['file'][0]),
782 783 req.form['node'][0]))
783 784
784 785 def do_file(self, req):
785 786 req.write(self.filerevision(self.cleanpath(req.form['file'][0]),
786 787 req.form['filenode'][0]))
787 788
788 789 def do_annotate(self, req):
789 790 req.write(self.fileannotate(self.cleanpath(req.form['file'][0]),
790 791 req.form['filenode'][0]))
791 792
792 793 def do_filelog(self, req):
793 794 req.write(self.filelog(self.cleanpath(req.form['file'][0]),
794 795 req.form['filenode'][0]))
795 796
796 797 def do_heads(self, req):
797 798 resp = " ".join(map(hex, self.repo.heads())) + "\n"
798 799 req.httphdr("application/mercurial-0.1", length=len(resp))
799 800 req.write(resp)
800 801
801 802 def do_branches(self, req):
802 803 nodes = []
803 804 if req.form.has_key('nodes'):
804 805 nodes = map(bin, req.form['nodes'][0].split(" "))
805 806 resp = cStringIO.StringIO()
806 807 for b in self.repo.branches(nodes):
807 808 resp.write(" ".join(map(hex, b)) + "\n")
808 809 resp = resp.getvalue()
809 810 req.httphdr("application/mercurial-0.1", length=len(resp))
810 811 req.write(resp)
811 812
812 813 def do_between(self, req):
813 814 nodes = []
814 815 if req.form.has_key('pairs'):
815 816 pairs = [map(bin, p.split("-"))
816 817 for p in req.form['pairs'][0].split(" ")]
817 818 resp = cStringIO.StringIO()
818 819 for b in self.repo.between(pairs):
819 820 resp.write(" ".join(map(hex, b)) + "\n")
820 821 resp = resp.getvalue()
821 822 req.httphdr("application/mercurial-0.1", length=len(resp))
822 823 req.write(resp)
823 824
824 825 def do_changegroup(self, req):
825 826 req.httphdr("application/mercurial-0.1")
826 827 nodes = []
827 828 if not self.allowpull:
828 829 return
829 830
830 831 if req.form.has_key('roots'):
831 832 nodes = map(bin, req.form['roots'][0].split(" "))
832 833
833 834 z = zlib.compressobj()
834 835 f = self.repo.changegroup(nodes, 'serve')
835 836 while 1:
836 837 chunk = f.read(4096)
837 838 if not chunk:
838 839 break
839 840 req.write(z.compress(chunk))
840 841
841 842 req.write(z.flush())
842 843
843 844 def do_archive(self, req):
844 845 changeset = self.repo.lookup(req.form['node'][0])
845 846 type_ = req.form['type'][0]
846 847 allowed = self.repo.ui.configlist("web", "allow_archive")
847 848 if (type_ in self.archives and (type_ in allowed or
848 849 self.repo.ui.configbool("web", "allow" + type_, False))):
849 850 self.archive(req, changeset, type_)
850 851 return
851 852
852 853 req.write(self.t("error"))
853 854
854 855 def do_static(self, req):
855 856 fname = req.form['file'][0]
856 857 static = self.repo.ui.config("web", "static",
857 858 os.path.join(self.templatepath,
858 859 "static"))
859 860 req.write(staticfile(static, fname, req)
860 861 or self.t("error", error="%r not found" % fname))
861 862
862 863 def do_capabilities(self, req):
863 864 caps = ['unbundle']
864 865 if self.repo.ui.configbool('server', 'uncompressed'):
865 866 caps.append('stream=%d' % self.repo.revlogversion)
866 867 resp = ' '.join(caps)
867 868 req.httphdr("application/mercurial-0.1", length=len(resp))
868 869 req.write(resp)
869 870
870 871 def check_perm(self, req, op, default):
871 872 '''check permission for operation based on user auth.
872 873 return true if op allowed, else false.
873 874 default is policy to use if no config given.'''
874 875
875 876 user = req.env.get('REMOTE_USER')
876 877
877 878 deny = self.repo.ui.configlist('web', 'deny_' + op)
878 879 if deny and (not user or deny == ['*'] or user in deny):
879 880 return False
880 881
881 882 allow = self.repo.ui.configlist('web', 'allow_' + op)
882 883 return (allow and (allow == ['*'] or user in allow)) or default
883 884
884 885 def do_unbundle(self, req):
885 886 def bail(response, headers={}):
886 887 length = int(req.env['CONTENT_LENGTH'])
887 888 for s in util.filechunkiter(req, limit=length):
888 889 # drain incoming bundle, else client will not see
889 890 # response when run outside cgi script
890 891 pass
891 892 req.httphdr("application/mercurial-0.1", headers=headers)
892 893 req.write('0\n')
893 894 req.write(response)
894 895
895 896 # require ssl by default, auth info cannot be sniffed and
896 897 # replayed
897 898 ssl_req = self.repo.ui.configbool('web', 'push_ssl', True)
898 899 if ssl_req and not req.env.get('HTTPS'):
899 900 bail(_('ssl required\n'))
900 901 return
901 902
902 903 # do not allow push unless explicitly allowed
903 904 if not self.check_perm(req, 'push', False):
904 905 bail(_('push not authorized\n'),
905 906 headers={'status': '401 Unauthorized'})
906 907 return
907 908
908 909 req.httphdr("application/mercurial-0.1")
909 910
910 911 their_heads = req.form['heads'][0].split(' ')
911 912
912 913 def check_heads():
913 914 heads = map(hex, self.repo.heads())
914 915 return their_heads == [hex('force')] or their_heads == heads
915 916
916 917 # fail early if possible
917 918 if not check_heads():
918 919 bail(_('unsynced changes\n'))
919 920 return
920 921
921 922 # do not lock repo until all changegroup data is
922 923 # streamed. save to temporary file.
923 924
924 925 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
925 926 fp = os.fdopen(fd, 'wb+')
926 927 try:
927 928 length = int(req.env['CONTENT_LENGTH'])
928 929 for s in util.filechunkiter(req, limit=length):
929 930 fp.write(s)
930 931
931 932 lock = self.repo.lock()
932 933 try:
933 934 if not check_heads():
934 935 req.write('0\n')
935 936 req.write(_('unsynced changes\n'))
936 937 return
937 938
938 939 fp.seek(0)
939 940
940 941 # send addchangegroup output to client
941 942
942 943 old_stdout = sys.stdout
943 944 sys.stdout = cStringIO.StringIO()
944 945
945 946 try:
946 947 ret = self.repo.addchangegroup(fp, 'serve')
947 948 finally:
948 949 val = sys.stdout.getvalue()
949 950 sys.stdout = old_stdout
950 951 req.write('%d\n' % ret)
951 952 req.write(val)
952 953 finally:
953 954 lock.release()
954 955 finally:
955 956 fp.close()
956 957 os.unlink(tempname)
957 958
958 959 def do_stream_out(self, req):
959 960 req.httphdr("application/mercurial-0.1")
960 961 streamclone.stream_out(self.repo, req)
@@ -1,34 +1,34
1 1 #header#
2 2 <title>#repo|escape#: Changelog</title>
3 3 <link rel="alternate" type="application/rss+xml"
4 4 href="?cmd=changelog;style=rss" title="RSS feed for #repo|escape#">
5 5 </head>
6 6 <body>
7 7
8 8 <div class="page_header">
9 9 <a href="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a><a href="?cmd=summary;style=gitweb">#repo|escape#</a> / changelog
10 10 </div>
11 11
12 12 <form action="#">
13 13 <div class="search">
14 14 <input type="hidden" name="repo" value="#repo|escape#" />
15 15 <input type="hidden" name="style" value="gitweb" />
16 16 <input type="hidden" name="cmd" value="changelog" />
17 17 <input type="text" name="rev" />
18 18 </div>
19 19 </form>
20 20 </div>
21 21
22 22 <div class="page_nav">
23 <a href="?cmd=summary;style=gitweb">summary</a> | changelog | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a><br/>
23 <a href="?cmd=summary;style=gitweb">summary</a> | changelog | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a>#archives%archiveentry#<br/>
24 24 <br/>
25 25 #changenav%naventry#<br/>
26 26 </div>
27 27
28 28 #entries%changelogentry#
29 29
30 30 <div class="page_nav">
31 31 #changenav%naventry#<br/>
32 32 </div>
33 33
34 34 #footer#
@@ -1,13 +1,14
1 1 #header#
2 2
3 3 <div class="page_nav">
4 <a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=changelog;style=gitweb">log</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a><br/>
4 <a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=changelog;style=gitweb">log</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a>#archives%archiveentry#
5 <br/>
5 6
6 7 #changenav%naventry#<br/>
7 8 </div>
8 9
9 10 <table cellspacing="0">
10 11 #entries#
11 12 </table>
12 13
13 14 #footer#
@@ -1,34 +1,35
1 1 #header#
2 2 <title>#repo|escape#: Summary</title>
3 3 <link rel="alternate" type="application/rss+xml"
4 4 href="?cmd=changelog;style=rss" title="RSS feed for #repo|escape#">
5 5 </head>
6 6 <body>
7 7
8 8 <div class="page_header">
9 9 <a href="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a><a href="?cmd=summary;style=gitweb">#repo|escape#</a> / summary
10 10 </div>
11 11 <div class="page_nav">
12 summary | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a><br/>
12 summary | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a>#archives%archiveentry#
13 <br/>
13 14 </div>
14 15
15 16 <div class="title">&nbsp;</div>
16 17 <table cellspacing="0">
17 18 <tr><td>description</td><td>#desc#</td></tr>
18 19 <tr><td>owner</td><td>#owner|escape#</td></tr>
19 20 <!-- <tr><td>last change</td><td>#lastchange|rfc822date#</td></tr> -->
20 21 </table>
21 22
22 23 <div><a class="title" href="?cmd=changelog;style=gitweb">changes</a></div>
23 24 <table cellspacing="0">
24 25 #shortlog#
25 26 <tr class="light"><td colspan="3"><a class="list" href="?cmd=changelog;style=gitweb">...</a></td></tr>
26 27 </table>
27 28
28 29 <div><a class="title" href="?cmd=tags;style=gitweb">tags</a></div>
29 30 <table cellspacing="0">
30 31 #tags#
31 32 <tr class="light"><td colspan="3"><a class="list" href="?cmd=tags;style=gitweb">...</a></td></tr>
32 33 </table>
33 34
34 35 #footer#
General Comments 0
You need to be logged in to leave comments. Login now