##// END OF EJS Templates
hgweb: add methods to get contexts from request
Brendan Cully -
r3226:5c602877 default
parent child Browse files
Show More
@@ -1,943 +1,943 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,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 152 def changelog(self, ctx, shortlog=False):
153 153 pos = ctx.rev()
154 154 def changenav(**map):
155 155 def seq(factor, maxchanges=None):
156 156 if maxchanges:
157 157 yield maxchanges
158 158 if maxchanges >= 20 and maxchanges <= 40:
159 159 yield 50
160 160 else:
161 161 yield 1 * factor
162 162 yield 3 * factor
163 163 for f in seq(factor * 10):
164 164 yield f
165 165
166 166 l = []
167 167 last = 0
168 168 maxchanges = shortlog and self.maxshortchanges or self.maxchanges
169 169 for f in seq(1, maxchanges):
170 170 if f < maxchanges or f <= last:
171 171 continue
172 172 if f > count:
173 173 break
174 174 last = f
175 175 r = "%d" % f
176 176 if pos + f < count:
177 177 l.append(("+" + r, pos + f))
178 178 if pos - f >= 0:
179 179 l.insert(0, ("-" + r, pos - f))
180 180
181 181 yield {"rev": 0, "label": "(0)"}
182 182
183 183 for label, rev in l:
184 184 yield {"label": label, "rev": rev}
185 185
186 186 yield {"label": "tip", "rev": "tip"}
187 187
188 188 def changelist(**map):
189 189 parity = (start - end) & 1
190 190 cl = self.repo.changelog
191 191 l = [] # build a list in forward order for efficiency
192 192 for i in range(start, end):
193 193 ctx = self.repo.changectx(i)
194 194 n = ctx.node()
195 195
196 196 l.insert(0, {"parity": parity,
197 197 "author": ctx.user(),
198 198 "parent": self.siblings(ctx.parents(), i - 1),
199 199 "child": self.siblings(ctx.children(), i + 1),
200 200 "changelogtag": self.showtag("changelogtag",n),
201 201 "desc": ctx.description(),
202 202 "date": ctx.date(),
203 203 "files": self.listfilediffs(ctx.files(), n),
204 204 "rev": i,
205 205 "node": hex(n)})
206 206 parity = 1 - parity
207 207
208 208 for e in l:
209 209 yield e
210 210
211 211 maxchanges = shortlog and self.maxshortchanges or self.maxchanges
212 212 cl = self.repo.changelog
213 213 mf = cl.read(cl.tip())[0]
214 214 count = cl.count()
215 215 start = max(0, pos - maxchanges + 1)
216 216 end = min(count, start + maxchanges)
217 217 pos = end - 1
218 218
219 219 yield self.t(shortlog and 'shortlog' or 'changelog',
220 220 changenav=changenav,
221 221 node=hex(cl.tip()),
222 222 rev=pos, changesets=count, entries=changelist,
223 223 archives=self.archivelist("tip"))
224 224
225 225 def search(self, query):
226 226
227 227 def changelist(**map):
228 228 cl = self.repo.changelog
229 229 count = 0
230 230 qw = query.lower().split()
231 231
232 232 def revgen():
233 233 for i in range(cl.count() - 1, 0, -100):
234 234 l = []
235 235 for j in range(max(0, i - 100), i):
236 236 ctx = self.repo.changectx(j)
237 237 l.append(ctx)
238 238 l.reverse()
239 239 for e in l:
240 240 yield e
241 241
242 242 for ctx in revgen():
243 243 miss = 0
244 244 for q in qw:
245 245 if not (q in ctx.user().lower() or
246 246 q in ctx.description().lower() or
247 247 q in " ".join(ctx.files()[:20]).lower()):
248 248 miss = 1
249 249 break
250 250 if miss:
251 251 continue
252 252
253 253 count += 1
254 254 n = ctx.node()
255 255
256 256 yield self.t('searchentry',
257 257 parity=self.stripes(count),
258 258 author=ctx.user(),
259 259 parent=self.siblings(ctx.parents()),
260 260 child=self.siblings(ctx.children()),
261 261 changelogtag=self.showtag("changelogtag",n),
262 262 desc=ctx.description(),
263 263 date=ctx.date(),
264 264 files=self.listfilediffs(ctx.files(), n),
265 265 rev=ctx.rev(),
266 266 node=hex(n))
267 267
268 268 if count >= self.maxchanges:
269 269 break
270 270
271 271 cl = self.repo.changelog
272 272
273 273 yield self.t('search',
274 274 query=query,
275 275 node=hex(cl.tip()),
276 276 entries=changelist)
277 277
278 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 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 555 def filediff(self, fctx):
556 556 n = fctx.node()
557 557 path = fctx.path()
558 558 parents = fctx.changectx().parents()
559 559 p1 = parents[0].node()
560 560
561 561 def diff(**map):
562 562 yield self.diff(p1, n, [path])
563 563
564 564 yield self.t("filediff",
565 565 file=path,
566 566 node=hex(n),
567 567 rev=fctx.rev(),
568 568 parent=self.siblings(parents),
569 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 if form.has_key('manifest'):
652 changeid = form['manifest'][0]
653 try:
654 req.changectx = self.repo.changectx(changeid)
655 except hg.RepoError:
656 man = self.repo.manifest
657 mn = man.lookup(changeid)
658 req.changectx = self.repo.changectx(man.linkrev(mn))
659
660 if form.has_key('filenode'):
661 changeid = form['filenode'][0]
662 path = self.cleanpath(form['file'][0])
663 try:
664 req.changectx = self.repo.changectx(changeid)
665 req.filectx = req.changectx.filectx(path)
666 except hg.RepoError:
667 req.filectx = self.repo.filectx(path, fileid=changeid)
668 req.changectx = req.filectx.changectx()
669
670 651 self.refresh()
671 652
672 653 expand_form(req.form)
673 654
674 655 m = os.path.join(self.templatepath, "map")
675 656 style = self.repo.ui.config("web", "style", "")
676 657 if req.form.has_key('style'):
677 658 style = req.form['style'][0]
678 659 if style:
679 660 b = os.path.basename("map-" + style)
680 661 p = os.path.join(self.templatepath, b)
681 662 if os.path.isfile(p):
682 663 m = p
683 664
684 665 port = req.env["SERVER_PORT"]
685 666 port = port != "80" and (":" + port) or ""
686 667 uri = req.env["REQUEST_URI"]
687 668 if "?" in uri:
688 669 uri = uri.split("?")[0]
689 670 url = "http://%s%s%s" % (req.env["SERVER_NAME"], port, uri)
690 671 if not self.reponame:
691 672 self.reponame = (self.repo.ui.config("web", "name")
692 673 or uri.strip('/') or self.repo.root)
693 674
694 675 self.t = templater.templater(m, templater.common_filters,
695 676 defaults={"url": url,
696 677 "repo": self.reponame,
697 678 "header": header,
698 679 "footer": footer,
699 680 "rawfileheader": rawfileheader,
700 681 })
701 682
702 683 if not req.form.has_key('cmd'):
703 684 req.form['cmd'] = [self.t.cache['default'],]
704 685
705 686 cmd = req.form['cmd'][0]
706 687
707 688 method = getattr(self, 'do_' + cmd, None)
708 689 if method:
709 690 method(req)
710 691 else:
711 692 req.write(self.t("error"))
712 693
694 def changectx(self, req):
695 if req.form.has_key('node'):
696 changeid = req.form['node'][0]
697 else:
698 changeid = req.form['manifest'][0]
699 try:
700 ctx = self.repo.changectx(changeid)
701 except hg.RepoError:
702 man = self.repo.manifest
703 mn = man.lookup(changeid)
704 ctx = self.repo.changectx(man.linkrev(mn))
705
706 return ctx
707
708 def filectx(self, req):
709 path = self.cleanpath(req.form['file'][0])
710 if req.form.has_key('node'):
711 changeid = req.form['node'][0]
712 else:
713 changeid = req.form['filenode'][0]
714 try:
715 ctx = self.repo.changectx(changeid)
716 fctx = ctx.filectx(path)
717 except hg.RepoError:
718 fctx = self.repo.filectx(path, fileid=changeid)
719
720 return fctx
721
713 722 def stripes(self, parity):
714 723 "make horizontal stripes for easier reading"
715 724 if self.stripecount:
716 725 return (1 + parity / self.stripecount) & 1
717 726 else:
718 727 return 0
719 728
720 def do_changelog(self, req):
729 def do_changelog(self, req, shortlog = False):
730 if req.form.has_key('node'):
731 ctx = self.changectx(req)
732 else:
721 733 if req.form.has_key('rev'):
722 734 hi = req.form['rev'][0]
723 735 else:
724 736 hi = self.repo.changelog.count() - 1
725 737 try:
726 738 ctx = self.repo.changectx(hi)
727 739 except hg.RepoError:
728 740 req.write(self.search(hi)) # XXX redirect to 404 page?
729 741 return
730 742
731 req.write(self.changelog(ctx))
743 req.write(self.changelog(ctx, shortlog = shortlog))
732 744
733 745 def do_shortlog(self, req):
734 if req.form.has_key('rev'):
735 hi = req.form['rev'][0]
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
743
744 req.write(self.changelog(ctx, shortlog = True))
746 self.do_changelog(req, shortlog = True)
745 747
746 748 def do_changeset(self, req):
747 749 ctx = self.repo.changectx(req.form['node'][0])
748 750 req.write(self.changeset(ctx))
749 751
750 752 def do_manifest(self, req):
751 req.write(self.manifest(req.changectx,
753 req.write(self.manifest(self.changectx(req),
752 754 self.cleanpath(req.form['path'][0])))
753 755
754 756 def do_tags(self, req):
755 757 req.write(self.tags())
756 758
757 759 def do_summary(self, req):
758 760 req.write(self.summary())
759 761
760 762 def do_filediff(self, req):
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))
763 req.write(self.filediff(self.filectx(req)))
764 764
765 765 def do_file(self, req):
766 req.write(self.filerevision(req.filectx))
766 req.write(self.filerevision(self.filectx(req)))
767 767
768 768 def do_annotate(self, req):
769 req.write(self.fileannotate(req.filectx))
769 req.write(self.fileannotate(self.filectx(req)))
770 770
771 771 def do_filelog(self, req):
772 req.write(self.filelog(req.filectx))
772 req.write(self.filelog(self.filectx(req)))
773 773
774 774 def do_heads(self, req):
775 775 resp = " ".join(map(hex, self.repo.heads())) + "\n"
776 776 req.httphdr("application/mercurial-0.1", length=len(resp))
777 777 req.write(resp)
778 778
779 779 def do_branches(self, req):
780 780 nodes = []
781 781 if req.form.has_key('nodes'):
782 782 nodes = map(bin, req.form['nodes'][0].split(" "))
783 783 resp = cStringIO.StringIO()
784 784 for b in self.repo.branches(nodes):
785 785 resp.write(" ".join(map(hex, b)) + "\n")
786 786 resp = resp.getvalue()
787 787 req.httphdr("application/mercurial-0.1", length=len(resp))
788 788 req.write(resp)
789 789
790 790 def do_between(self, req):
791 791 if req.form.has_key('pairs'):
792 792 pairs = [map(bin, p.split("-"))
793 793 for p in req.form['pairs'][0].split(" ")]
794 794 resp = cStringIO.StringIO()
795 795 for b in self.repo.between(pairs):
796 796 resp.write(" ".join(map(hex, b)) + "\n")
797 797 resp = resp.getvalue()
798 798 req.httphdr("application/mercurial-0.1", length=len(resp))
799 799 req.write(resp)
800 800
801 801 def do_changegroup(self, req):
802 802 req.httphdr("application/mercurial-0.1")
803 803 nodes = []
804 804 if not self.allowpull:
805 805 return
806 806
807 807 if req.form.has_key('roots'):
808 808 nodes = map(bin, req.form['roots'][0].split(" "))
809 809
810 810 z = zlib.compressobj()
811 811 f = self.repo.changegroup(nodes, 'serve')
812 812 while 1:
813 813 chunk = f.read(4096)
814 814 if not chunk:
815 815 break
816 816 req.write(z.compress(chunk))
817 817
818 818 req.write(z.flush())
819 819
820 820 def do_archive(self, req):
821 821 changeset = self.repo.lookup(req.form['node'][0])
822 822 type_ = req.form['type'][0]
823 823 allowed = self.repo.ui.configlist("web", "allow_archive")
824 824 if (type_ in self.archives and (type_ in allowed or
825 825 self.repo.ui.configbool("web", "allow" + type_, False))):
826 826 self.archive(req, changeset, type_)
827 827 return
828 828
829 829 req.write(self.t("error"))
830 830
831 831 def do_static(self, req):
832 832 fname = req.form['file'][0]
833 833 static = self.repo.ui.config("web", "static",
834 834 os.path.join(self.templatepath,
835 835 "static"))
836 836 req.write(staticfile(static, fname, req)
837 837 or self.t("error", error="%r not found" % fname))
838 838
839 839 def do_capabilities(self, req):
840 840 caps = ['unbundle']
841 841 if self.repo.ui.configbool('server', 'uncompressed'):
842 842 caps.append('stream=%d' % self.repo.revlogversion)
843 843 resp = ' '.join(caps)
844 844 req.httphdr("application/mercurial-0.1", length=len(resp))
845 845 req.write(resp)
846 846
847 847 def check_perm(self, req, op, default):
848 848 '''check permission for operation based on user auth.
849 849 return true if op allowed, else false.
850 850 default is policy to use if no config given.'''
851 851
852 852 user = req.env.get('REMOTE_USER')
853 853
854 854 deny = self.repo.ui.configlist('web', 'deny_' + op)
855 855 if deny and (not user or deny == ['*'] or user in deny):
856 856 return False
857 857
858 858 allow = self.repo.ui.configlist('web', 'allow_' + op)
859 859 return (allow and (allow == ['*'] or user in allow)) or default
860 860
861 861 def do_unbundle(self, req):
862 862 def bail(response, headers={}):
863 863 length = int(req.env['CONTENT_LENGTH'])
864 864 for s in util.filechunkiter(req, limit=length):
865 865 # drain incoming bundle, else client will not see
866 866 # response when run outside cgi script
867 867 pass
868 868 req.httphdr("application/mercurial-0.1", headers=headers)
869 869 req.write('0\n')
870 870 req.write(response)
871 871
872 872 # require ssl by default, auth info cannot be sniffed and
873 873 # replayed
874 874 ssl_req = self.repo.ui.configbool('web', 'push_ssl', True)
875 875 if ssl_req:
876 876 if not req.env.get('HTTPS'):
877 877 bail(_('ssl required\n'))
878 878 return
879 879 proto = 'https'
880 880 else:
881 881 proto = 'http'
882 882
883 883 # do not allow push unless explicitly allowed
884 884 if not self.check_perm(req, 'push', False):
885 885 bail(_('push not authorized\n'),
886 886 headers={'status': '401 Unauthorized'})
887 887 return
888 888
889 889 req.httphdr("application/mercurial-0.1")
890 890
891 891 their_heads = req.form['heads'][0].split(' ')
892 892
893 893 def check_heads():
894 894 heads = map(hex, self.repo.heads())
895 895 return their_heads == [hex('force')] or their_heads == heads
896 896
897 897 # fail early if possible
898 898 if not check_heads():
899 899 bail(_('unsynced changes\n'))
900 900 return
901 901
902 902 # do not lock repo until all changegroup data is
903 903 # streamed. save to temporary file.
904 904
905 905 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
906 906 fp = os.fdopen(fd, 'wb+')
907 907 try:
908 908 length = int(req.env['CONTENT_LENGTH'])
909 909 for s in util.filechunkiter(req, limit=length):
910 910 fp.write(s)
911 911
912 912 lock = self.repo.lock()
913 913 try:
914 914 if not check_heads():
915 915 req.write('0\n')
916 916 req.write(_('unsynced changes\n'))
917 917 return
918 918
919 919 fp.seek(0)
920 920
921 921 # send addchangegroup output to client
922 922
923 923 old_stdout = sys.stdout
924 924 sys.stdout = cStringIO.StringIO()
925 925
926 926 try:
927 927 url = 'remote:%s:%s' % (proto,
928 928 req.env.get('REMOTE_HOST', ''))
929 929 ret = self.repo.addchangegroup(fp, 'serve', url)
930 930 finally:
931 931 val = sys.stdout.getvalue()
932 932 sys.stdout = old_stdout
933 933 req.write('%d\n' % ret)
934 934 req.write(val)
935 935 finally:
936 936 lock.release()
937 937 finally:
938 938 fp.close()
939 939 os.unlink(tempname)
940 940
941 941 def do_stream_out(self, req):
942 942 req.httphdr("application/mercurial-0.1")
943 943 streamclone.stream_out(self.repo, req)
General Comments 0
You need to be logged in to leave comments. Login now