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