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