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