##// END OF EJS Templates
hgweb: set minimum number of revision to display to 1 when revcount is 0...
Md. O. Shayan -
r13931:c3372529 default
parent child Browse files
Show More
@@ -1,829 +1,833 b''
1 #
1 #
2 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
2 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 import os, mimetypes, re, cgi, copy
8 import os, mimetypes, re, cgi, copy
9 import webutil
9 import webutil
10 from mercurial import error, encoding, archival, templater, templatefilters
10 from mercurial import error, encoding, archival, templater, templatefilters
11 from mercurial.node import short, hex
11 from mercurial.node import short, hex
12 from mercurial.util import binary
12 from mercurial.util import binary
13 from common import paritygen, staticfile, get_contact, ErrorResponse
13 from common import paritygen, staticfile, get_contact, ErrorResponse
14 from common import HTTP_OK, HTTP_FORBIDDEN, HTTP_NOT_FOUND
14 from common import HTTP_OK, HTTP_FORBIDDEN, HTTP_NOT_FOUND
15 from mercurial import graphmod
15 from mercurial import graphmod
16 from mercurial import help as helpmod
16 from mercurial import help as helpmod
17 from mercurial.i18n import _
17 from mercurial.i18n import _
18
18
19 # __all__ is populated with the allowed commands. Be sure to add to it if
19 # __all__ is populated with the allowed commands. Be sure to add to it if
20 # you're adding a new command, or the new command won't work.
20 # you're adding a new command, or the new command won't work.
21
21
22 __all__ = [
22 __all__ = [
23 'log', 'rawfile', 'file', 'changelog', 'shortlog', 'changeset', 'rev',
23 'log', 'rawfile', 'file', 'changelog', 'shortlog', 'changeset', 'rev',
24 'manifest', 'tags', 'bookmarks', 'branches', 'summary', 'filediff', 'diff',
24 'manifest', 'tags', 'bookmarks', 'branches', 'summary', 'filediff', 'diff',
25 'annotate', 'filelog', 'archive', 'static', 'graph', 'help',
25 'annotate', 'filelog', 'archive', 'static', 'graph', 'help',
26 ]
26 ]
27
27
28 def log(web, req, tmpl):
28 def log(web, req, tmpl):
29 if 'file' in req.form and req.form['file'][0]:
29 if 'file' in req.form and req.form['file'][0]:
30 return filelog(web, req, tmpl)
30 return filelog(web, req, tmpl)
31 else:
31 else:
32 return changelog(web, req, tmpl)
32 return changelog(web, req, tmpl)
33
33
34 def rawfile(web, req, tmpl):
34 def rawfile(web, req, tmpl):
35 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
35 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
36 if not path:
36 if not path:
37 content = manifest(web, req, tmpl)
37 content = manifest(web, req, tmpl)
38 req.respond(HTTP_OK, web.ctype)
38 req.respond(HTTP_OK, web.ctype)
39 return content
39 return content
40
40
41 try:
41 try:
42 fctx = webutil.filectx(web.repo, req)
42 fctx = webutil.filectx(web.repo, req)
43 except error.LookupError, inst:
43 except error.LookupError, inst:
44 try:
44 try:
45 content = manifest(web, req, tmpl)
45 content = manifest(web, req, tmpl)
46 req.respond(HTTP_OK, web.ctype)
46 req.respond(HTTP_OK, web.ctype)
47 return content
47 return content
48 except ErrorResponse:
48 except ErrorResponse:
49 raise inst
49 raise inst
50
50
51 path = fctx.path()
51 path = fctx.path()
52 text = fctx.data()
52 text = fctx.data()
53 mt = mimetypes.guess_type(path)[0]
53 mt = mimetypes.guess_type(path)[0]
54 if mt is None:
54 if mt is None:
55 mt = binary(text) and 'application/octet-stream' or 'text/plain'
55 mt = binary(text) and 'application/octet-stream' or 'text/plain'
56 if mt.startswith('text/'):
56 if mt.startswith('text/'):
57 mt += '; charset="%s"' % encoding.encoding
57 mt += '; charset="%s"' % encoding.encoding
58
58
59 req.respond(HTTP_OK, mt, path, len(text))
59 req.respond(HTTP_OK, mt, path, len(text))
60 return [text]
60 return [text]
61
61
62 def _filerevision(web, tmpl, fctx):
62 def _filerevision(web, tmpl, fctx):
63 f = fctx.path()
63 f = fctx.path()
64 text = fctx.data()
64 text = fctx.data()
65 parity = paritygen(web.stripecount)
65 parity = paritygen(web.stripecount)
66
66
67 if binary(text):
67 if binary(text):
68 mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
68 mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
69 text = '(binary:%s)' % mt
69 text = '(binary:%s)' % mt
70
70
71 def lines():
71 def lines():
72 for lineno, t in enumerate(text.splitlines(True)):
72 for lineno, t in enumerate(text.splitlines(True)):
73 yield {"line": t,
73 yield {"line": t,
74 "lineid": "l%d" % (lineno + 1),
74 "lineid": "l%d" % (lineno + 1),
75 "linenumber": "% 6d" % (lineno + 1),
75 "linenumber": "% 6d" % (lineno + 1),
76 "parity": parity.next()}
76 "parity": parity.next()}
77
77
78 return tmpl("filerevision",
78 return tmpl("filerevision",
79 file=f,
79 file=f,
80 path=webutil.up(f),
80 path=webutil.up(f),
81 text=lines(),
81 text=lines(),
82 rev=fctx.rev(),
82 rev=fctx.rev(),
83 node=hex(fctx.node()),
83 node=hex(fctx.node()),
84 author=fctx.user(),
84 author=fctx.user(),
85 date=fctx.date(),
85 date=fctx.date(),
86 desc=fctx.description(),
86 desc=fctx.description(),
87 branch=webutil.nodebranchnodefault(fctx),
87 branch=webutil.nodebranchnodefault(fctx),
88 parent=webutil.parents(fctx),
88 parent=webutil.parents(fctx),
89 child=webutil.children(fctx),
89 child=webutil.children(fctx),
90 rename=webutil.renamelink(fctx),
90 rename=webutil.renamelink(fctx),
91 permissions=fctx.manifest().flags(f))
91 permissions=fctx.manifest().flags(f))
92
92
93 def file(web, req, tmpl):
93 def file(web, req, tmpl):
94 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
94 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
95 if not path:
95 if not path:
96 return manifest(web, req, tmpl)
96 return manifest(web, req, tmpl)
97 try:
97 try:
98 return _filerevision(web, tmpl, webutil.filectx(web.repo, req))
98 return _filerevision(web, tmpl, webutil.filectx(web.repo, req))
99 except error.LookupError, inst:
99 except error.LookupError, inst:
100 try:
100 try:
101 return manifest(web, req, tmpl)
101 return manifest(web, req, tmpl)
102 except ErrorResponse:
102 except ErrorResponse:
103 raise inst
103 raise inst
104
104
105 def _search(web, req, tmpl):
105 def _search(web, req, tmpl):
106
106
107 query = req.form['rev'][0]
107 query = req.form['rev'][0]
108 revcount = web.maxchanges
108 revcount = web.maxchanges
109 if 'revcount' in req.form:
109 if 'revcount' in req.form:
110 revcount = int(req.form.get('revcount', [revcount])[0])
110 revcount = int(req.form.get('revcount', [revcount])[0])
111 revcount = max(revcount, 1)
111 tmpl.defaults['sessionvars']['revcount'] = revcount
112 tmpl.defaults['sessionvars']['revcount'] = revcount
112
113
113 lessvars = copy.copy(tmpl.defaults['sessionvars'])
114 lessvars = copy.copy(tmpl.defaults['sessionvars'])
114 lessvars['revcount'] = revcount / 2
115 lessvars['revcount'] = max(revcount / 2, 1)
115 lessvars['rev'] = query
116 lessvars['rev'] = query
116 morevars = copy.copy(tmpl.defaults['sessionvars'])
117 morevars = copy.copy(tmpl.defaults['sessionvars'])
117 morevars['revcount'] = revcount * 2
118 morevars['revcount'] = revcount * 2
118 morevars['rev'] = query
119 morevars['rev'] = query
119
120
120 def changelist(**map):
121 def changelist(**map):
121 count = 0
122 count = 0
122 qw = query.lower().split()
123 qw = query.lower().split()
123
124
124 def revgen():
125 def revgen():
125 for i in xrange(len(web.repo) - 1, 0, -100):
126 for i in xrange(len(web.repo) - 1, 0, -100):
126 l = []
127 l = []
127 for j in xrange(max(0, i - 100), i + 1):
128 for j in xrange(max(0, i - 100), i + 1):
128 ctx = web.repo[j]
129 ctx = web.repo[j]
129 l.append(ctx)
130 l.append(ctx)
130 l.reverse()
131 l.reverse()
131 for e in l:
132 for e in l:
132 yield e
133 yield e
133
134
134 for ctx in revgen():
135 for ctx in revgen():
135 miss = 0
136 miss = 0
136 for q in qw:
137 for q in qw:
137 if not (q in ctx.user().lower() or
138 if not (q in ctx.user().lower() or
138 q in ctx.description().lower() or
139 q in ctx.description().lower() or
139 q in " ".join(ctx.files()).lower()):
140 q in " ".join(ctx.files()).lower()):
140 miss = 1
141 miss = 1
141 break
142 break
142 if miss:
143 if miss:
143 continue
144 continue
144
145
145 count += 1
146 count += 1
146 n = ctx.node()
147 n = ctx.node()
147 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
148 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
148 files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
149 files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
149
150
150 yield tmpl('searchentry',
151 yield tmpl('searchentry',
151 parity=parity.next(),
152 parity=parity.next(),
152 author=ctx.user(),
153 author=ctx.user(),
153 parent=webutil.parents(ctx),
154 parent=webutil.parents(ctx),
154 child=webutil.children(ctx),
155 child=webutil.children(ctx),
155 changelogtag=showtags,
156 changelogtag=showtags,
156 desc=ctx.description(),
157 desc=ctx.description(),
157 date=ctx.date(),
158 date=ctx.date(),
158 files=files,
159 files=files,
159 rev=ctx.rev(),
160 rev=ctx.rev(),
160 node=hex(n),
161 node=hex(n),
161 tags=webutil.nodetagsdict(web.repo, n),
162 tags=webutil.nodetagsdict(web.repo, n),
162 bookmarks=webutil.nodebookmarksdict(web.repo, n),
163 bookmarks=webutil.nodebookmarksdict(web.repo, n),
163 inbranch=webutil.nodeinbranch(web.repo, ctx),
164 inbranch=webutil.nodeinbranch(web.repo, ctx),
164 branches=webutil.nodebranchdict(web.repo, ctx))
165 branches=webutil.nodebranchdict(web.repo, ctx))
165
166
166 if count >= revcount:
167 if count >= revcount:
167 break
168 break
168
169
169 tip = web.repo['tip']
170 tip = web.repo['tip']
170 parity = paritygen(web.stripecount)
171 parity = paritygen(web.stripecount)
171
172
172 return tmpl('search', query=query, node=tip.hex(),
173 return tmpl('search', query=query, node=tip.hex(),
173 entries=changelist, archives=web.archivelist("tip"),
174 entries=changelist, archives=web.archivelist("tip"),
174 morevars=morevars, lessvars=lessvars)
175 morevars=morevars, lessvars=lessvars)
175
176
176 def changelog(web, req, tmpl, shortlog=False):
177 def changelog(web, req, tmpl, shortlog=False):
177
178
178 if 'node' in req.form:
179 if 'node' in req.form:
179 ctx = webutil.changectx(web.repo, req)
180 ctx = webutil.changectx(web.repo, req)
180 else:
181 else:
181 if 'rev' in req.form:
182 if 'rev' in req.form:
182 hi = req.form['rev'][0]
183 hi = req.form['rev'][0]
183 else:
184 else:
184 hi = len(web.repo) - 1
185 hi = len(web.repo) - 1
185 try:
186 try:
186 ctx = web.repo[hi]
187 ctx = web.repo[hi]
187 except error.RepoError:
188 except error.RepoError:
188 return _search(web, req, tmpl) # XXX redirect to 404 page?
189 return _search(web, req, tmpl) # XXX redirect to 404 page?
189
190
190 def changelist(limit=0, **map):
191 def changelist(limit=0, **map):
191 l = [] # build a list in forward order for efficiency
192 l = [] # build a list in forward order for efficiency
192 for i in xrange(start, end):
193 for i in xrange(start, end):
193 ctx = web.repo[i]
194 ctx = web.repo[i]
194 n = ctx.node()
195 n = ctx.node()
195 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
196 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
196 files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
197 files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
197
198
198 l.insert(0, {"parity": parity.next(),
199 l.insert(0, {"parity": parity.next(),
199 "author": ctx.user(),
200 "author": ctx.user(),
200 "parent": webutil.parents(ctx, i - 1),
201 "parent": webutil.parents(ctx, i - 1),
201 "child": webutil.children(ctx, i + 1),
202 "child": webutil.children(ctx, i + 1),
202 "changelogtag": showtags,
203 "changelogtag": showtags,
203 "desc": ctx.description(),
204 "desc": ctx.description(),
204 "date": ctx.date(),
205 "date": ctx.date(),
205 "files": files,
206 "files": files,
206 "rev": i,
207 "rev": i,
207 "node": hex(n),
208 "node": hex(n),
208 "tags": webutil.nodetagsdict(web.repo, n),
209 "tags": webutil.nodetagsdict(web.repo, n),
209 "bookmarks": webutil.nodebookmarksdict(web.repo, n),
210 "bookmarks": webutil.nodebookmarksdict(web.repo, n),
210 "inbranch": webutil.nodeinbranch(web.repo, ctx),
211 "inbranch": webutil.nodeinbranch(web.repo, ctx),
211 "branches": webutil.nodebranchdict(web.repo, ctx)
212 "branches": webutil.nodebranchdict(web.repo, ctx)
212 })
213 })
213
214
214 if limit > 0:
215 if limit > 0:
215 l = l[:limit]
216 l = l[:limit]
216
217
217 for e in l:
218 for e in l:
218 yield e
219 yield e
219
220
220 revcount = shortlog and web.maxshortchanges or web.maxchanges
221 revcount = shortlog and web.maxshortchanges or web.maxchanges
221 if 'revcount' in req.form:
222 if 'revcount' in req.form:
222 revcount = int(req.form.get('revcount', [revcount])[0])
223 revcount = int(req.form.get('revcount', [revcount])[0])
224 revcount = max(revcount, 1)
223 tmpl.defaults['sessionvars']['revcount'] = revcount
225 tmpl.defaults['sessionvars']['revcount'] = revcount
224
226
225 lessvars = copy.copy(tmpl.defaults['sessionvars'])
227 lessvars = copy.copy(tmpl.defaults['sessionvars'])
226 lessvars['revcount'] = revcount / 2
228 lessvars['revcount'] = max(revcount / 2, 1)
227 morevars = copy.copy(tmpl.defaults['sessionvars'])
229 morevars = copy.copy(tmpl.defaults['sessionvars'])
228 morevars['revcount'] = revcount * 2
230 morevars['revcount'] = revcount * 2
229
231
230 count = len(web.repo)
232 count = len(web.repo)
231 pos = ctx.rev()
233 pos = ctx.rev()
232 start = max(0, pos - revcount + 1)
234 start = max(0, pos - revcount + 1)
233 end = min(count, start + revcount)
235 end = min(count, start + revcount)
234 pos = end - 1
236 pos = end - 1
235 parity = paritygen(web.stripecount, offset=start - end)
237 parity = paritygen(web.stripecount, offset=start - end)
236
238
237 changenav = webutil.revnavgen(pos, revcount, count, web.repo.changectx)
239 changenav = webutil.revnavgen(pos, revcount, count, web.repo.changectx)
238
240
239 return tmpl(shortlog and 'shortlog' or 'changelog', changenav=changenav,
241 return tmpl(shortlog and 'shortlog' or 'changelog', changenav=changenav,
240 node=hex(ctx.node()), rev=pos, changesets=count,
242 node=hex(ctx.node()), rev=pos, changesets=count,
241 entries=lambda **x: changelist(limit=0,**x),
243 entries=lambda **x: changelist(limit=0,**x),
242 latestentry=lambda **x: changelist(limit=1,**x),
244 latestentry=lambda **x: changelist(limit=1,**x),
243 archives=web.archivelist("tip"), revcount=revcount,
245 archives=web.archivelist("tip"), revcount=revcount,
244 morevars=morevars, lessvars=lessvars)
246 morevars=morevars, lessvars=lessvars)
245
247
246 def shortlog(web, req, tmpl):
248 def shortlog(web, req, tmpl):
247 return changelog(web, req, tmpl, shortlog = True)
249 return changelog(web, req, tmpl, shortlog = True)
248
250
249 def changeset(web, req, tmpl):
251 def changeset(web, req, tmpl):
250 ctx = webutil.changectx(web.repo, req)
252 ctx = webutil.changectx(web.repo, req)
251 showtags = webutil.showtag(web.repo, tmpl, 'changesettag', ctx.node())
253 showtags = webutil.showtag(web.repo, tmpl, 'changesettag', ctx.node())
252 showbookmarks = webutil.showbookmark(web.repo, tmpl, 'changesetbookmark',
254 showbookmarks = webutil.showbookmark(web.repo, tmpl, 'changesetbookmark',
253 ctx.node())
255 ctx.node())
254 showbranch = webutil.nodebranchnodefault(ctx)
256 showbranch = webutil.nodebranchnodefault(ctx)
255
257
256 files = []
258 files = []
257 parity = paritygen(web.stripecount)
259 parity = paritygen(web.stripecount)
258 for f in ctx.files():
260 for f in ctx.files():
259 template = f in ctx and 'filenodelink' or 'filenolink'
261 template = f in ctx and 'filenodelink' or 'filenolink'
260 files.append(tmpl(template,
262 files.append(tmpl(template,
261 node=ctx.hex(), file=f,
263 node=ctx.hex(), file=f,
262 parity=parity.next()))
264 parity=parity.next()))
263
265
264 parity = paritygen(web.stripecount)
266 parity = paritygen(web.stripecount)
265 style = web.config('web', 'style', 'paper')
267 style = web.config('web', 'style', 'paper')
266 if 'style' in req.form:
268 if 'style' in req.form:
267 style = req.form['style'][0]
269 style = req.form['style'][0]
268
270
269 diffs = webutil.diffs(web.repo, tmpl, ctx, None, parity, style)
271 diffs = webutil.diffs(web.repo, tmpl, ctx, None, parity, style)
270 return tmpl('changeset',
272 return tmpl('changeset',
271 diff=diffs,
273 diff=diffs,
272 rev=ctx.rev(),
274 rev=ctx.rev(),
273 node=ctx.hex(),
275 node=ctx.hex(),
274 parent=webutil.parents(ctx),
276 parent=webutil.parents(ctx),
275 child=webutil.children(ctx),
277 child=webutil.children(ctx),
276 changesettag=showtags,
278 changesettag=showtags,
277 changesetbookmark=showbookmarks,
279 changesetbookmark=showbookmarks,
278 changesetbranch=showbranch,
280 changesetbranch=showbranch,
279 author=ctx.user(),
281 author=ctx.user(),
280 desc=ctx.description(),
282 desc=ctx.description(),
281 date=ctx.date(),
283 date=ctx.date(),
282 files=files,
284 files=files,
283 archives=web.archivelist(ctx.hex()),
285 archives=web.archivelist(ctx.hex()),
284 tags=webutil.nodetagsdict(web.repo, ctx.node()),
286 tags=webutil.nodetagsdict(web.repo, ctx.node()),
285 bookmarks=webutil.nodebookmarksdict(web.repo, ctx.node()),
287 bookmarks=webutil.nodebookmarksdict(web.repo, ctx.node()),
286 branch=webutil.nodebranchnodefault(ctx),
288 branch=webutil.nodebranchnodefault(ctx),
287 inbranch=webutil.nodeinbranch(web.repo, ctx),
289 inbranch=webutil.nodeinbranch(web.repo, ctx),
288 branches=webutil.nodebranchdict(web.repo, ctx))
290 branches=webutil.nodebranchdict(web.repo, ctx))
289
291
290 rev = changeset
292 rev = changeset
291
293
292 def manifest(web, req, tmpl):
294 def manifest(web, req, tmpl):
293 ctx = webutil.changectx(web.repo, req)
295 ctx = webutil.changectx(web.repo, req)
294 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
296 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
295 mf = ctx.manifest()
297 mf = ctx.manifest()
296 node = ctx.node()
298 node = ctx.node()
297
299
298 files = {}
300 files = {}
299 dirs = {}
301 dirs = {}
300 parity = paritygen(web.stripecount)
302 parity = paritygen(web.stripecount)
301
303
302 if path and path[-1] != "/":
304 if path and path[-1] != "/":
303 path += "/"
305 path += "/"
304 l = len(path)
306 l = len(path)
305 abspath = "/" + path
307 abspath = "/" + path
306
308
307 for f, n in mf.iteritems():
309 for f, n in mf.iteritems():
308 if f[:l] != path:
310 if f[:l] != path:
309 continue
311 continue
310 remain = f[l:]
312 remain = f[l:]
311 elements = remain.split('/')
313 elements = remain.split('/')
312 if len(elements) == 1:
314 if len(elements) == 1:
313 files[remain] = f
315 files[remain] = f
314 else:
316 else:
315 h = dirs # need to retain ref to dirs (root)
317 h = dirs # need to retain ref to dirs (root)
316 for elem in elements[0:-1]:
318 for elem in elements[0:-1]:
317 if elem not in h:
319 if elem not in h:
318 h[elem] = {}
320 h[elem] = {}
319 h = h[elem]
321 h = h[elem]
320 if len(h) > 1:
322 if len(h) > 1:
321 break
323 break
322 h[None] = None # denotes files present
324 h[None] = None # denotes files present
323
325
324 if mf and not files and not dirs:
326 if mf and not files and not dirs:
325 raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
327 raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
326
328
327 def filelist(**map):
329 def filelist(**map):
328 for f in sorted(files):
330 for f in sorted(files):
329 full = files[f]
331 full = files[f]
330
332
331 fctx = ctx.filectx(full)
333 fctx = ctx.filectx(full)
332 yield {"file": full,
334 yield {"file": full,
333 "parity": parity.next(),
335 "parity": parity.next(),
334 "basename": f,
336 "basename": f,
335 "date": fctx.date(),
337 "date": fctx.date(),
336 "size": fctx.size(),
338 "size": fctx.size(),
337 "permissions": mf.flags(full)}
339 "permissions": mf.flags(full)}
338
340
339 def dirlist(**map):
341 def dirlist(**map):
340 for d in sorted(dirs):
342 for d in sorted(dirs):
341
343
342 emptydirs = []
344 emptydirs = []
343 h = dirs[d]
345 h = dirs[d]
344 while isinstance(h, dict) and len(h) == 1:
346 while isinstance(h, dict) and len(h) == 1:
345 k, v = h.items()[0]
347 k, v = h.items()[0]
346 if v:
348 if v:
347 emptydirs.append(k)
349 emptydirs.append(k)
348 h = v
350 h = v
349
351
350 path = "%s%s" % (abspath, d)
352 path = "%s%s" % (abspath, d)
351 yield {"parity": parity.next(),
353 yield {"parity": parity.next(),
352 "path": path,
354 "path": path,
353 "emptydirs": "/".join(emptydirs),
355 "emptydirs": "/".join(emptydirs),
354 "basename": d}
356 "basename": d}
355
357
356 return tmpl("manifest",
358 return tmpl("manifest",
357 rev=ctx.rev(),
359 rev=ctx.rev(),
358 node=hex(node),
360 node=hex(node),
359 path=abspath,
361 path=abspath,
360 up=webutil.up(abspath),
362 up=webutil.up(abspath),
361 upparity=parity.next(),
363 upparity=parity.next(),
362 fentries=filelist,
364 fentries=filelist,
363 dentries=dirlist,
365 dentries=dirlist,
364 archives=web.archivelist(hex(node)),
366 archives=web.archivelist(hex(node)),
365 tags=webutil.nodetagsdict(web.repo, node),
367 tags=webutil.nodetagsdict(web.repo, node),
366 bookmarks=webutil.nodebookmarksdict(web.repo, node),
368 bookmarks=webutil.nodebookmarksdict(web.repo, node),
367 inbranch=webutil.nodeinbranch(web.repo, ctx),
369 inbranch=webutil.nodeinbranch(web.repo, ctx),
368 branches=webutil.nodebranchdict(web.repo, ctx))
370 branches=webutil.nodebranchdict(web.repo, ctx))
369
371
370 def tags(web, req, tmpl):
372 def tags(web, req, tmpl):
371 i = web.repo.tagslist()
373 i = web.repo.tagslist()
372 i.reverse()
374 i.reverse()
373 parity = paritygen(web.stripecount)
375 parity = paritygen(web.stripecount)
374
376
375 def entries(notip=False, limit=0, **map):
377 def entries(notip=False, limit=0, **map):
376 count = 0
378 count = 0
377 for k, n in i:
379 for k, n in i:
378 if notip and k == "tip":
380 if notip and k == "tip":
379 continue
381 continue
380 if limit > 0 and count >= limit:
382 if limit > 0 and count >= limit:
381 continue
383 continue
382 count = count + 1
384 count = count + 1
383 yield {"parity": parity.next(),
385 yield {"parity": parity.next(),
384 "tag": k,
386 "tag": k,
385 "date": web.repo[n].date(),
387 "date": web.repo[n].date(),
386 "node": hex(n)}
388 "node": hex(n)}
387
389
388 return tmpl("tags",
390 return tmpl("tags",
389 node=hex(web.repo.changelog.tip()),
391 node=hex(web.repo.changelog.tip()),
390 entries=lambda **x: entries(False, 0, **x),
392 entries=lambda **x: entries(False, 0, **x),
391 entriesnotip=lambda **x: entries(True, 0, **x),
393 entriesnotip=lambda **x: entries(True, 0, **x),
392 latestentry=lambda **x: entries(True, 1, **x))
394 latestentry=lambda **x: entries(True, 1, **x))
393
395
394 def bookmarks(web, req, tmpl):
396 def bookmarks(web, req, tmpl):
395 i = web.repo._bookmarks.items()
397 i = web.repo._bookmarks.items()
396 parity = paritygen(web.stripecount)
398 parity = paritygen(web.stripecount)
397
399
398 def entries(limit=0, **map):
400 def entries(limit=0, **map):
399 count = 0
401 count = 0
400 for k, n in sorted(i):
402 for k, n in sorted(i):
401 if limit > 0 and count >= limit:
403 if limit > 0 and count >= limit:
402 continue
404 continue
403 count = count + 1
405 count = count + 1
404 yield {"parity": parity.next(),
406 yield {"parity": parity.next(),
405 "bookmark": k,
407 "bookmark": k,
406 "date": web.repo[n].date(),
408 "date": web.repo[n].date(),
407 "node": hex(n)}
409 "node": hex(n)}
408
410
409 return tmpl("bookmarks",
411 return tmpl("bookmarks",
410 node=hex(web.repo.changelog.tip()),
412 node=hex(web.repo.changelog.tip()),
411 entries=lambda **x: entries(0, **x),
413 entries=lambda **x: entries(0, **x),
412 latestentry=lambda **x: entries(1, **x))
414 latestentry=lambda **x: entries(1, **x))
413
415
414 def branches(web, req, tmpl):
416 def branches(web, req, tmpl):
415 tips = (web.repo[n] for t, n in web.repo.branchtags().iteritems())
417 tips = (web.repo[n] for t, n in web.repo.branchtags().iteritems())
416 heads = web.repo.heads()
418 heads = web.repo.heads()
417 parity = paritygen(web.stripecount)
419 parity = paritygen(web.stripecount)
418 sortkey = lambda ctx: ('close' not in ctx.extra(), ctx.rev())
420 sortkey = lambda ctx: ('close' not in ctx.extra(), ctx.rev())
419
421
420 def entries(limit, **map):
422 def entries(limit, **map):
421 count = 0
423 count = 0
422 for ctx in sorted(tips, key=sortkey, reverse=True):
424 for ctx in sorted(tips, key=sortkey, reverse=True):
423 if limit > 0 and count >= limit:
425 if limit > 0 and count >= limit:
424 return
426 return
425 count += 1
427 count += 1
426 if ctx.node() not in heads:
428 if ctx.node() not in heads:
427 status = 'inactive'
429 status = 'inactive'
428 elif not web.repo.branchheads(ctx.branch()):
430 elif not web.repo.branchheads(ctx.branch()):
429 status = 'closed'
431 status = 'closed'
430 else:
432 else:
431 status = 'open'
433 status = 'open'
432 yield {'parity': parity.next(),
434 yield {'parity': parity.next(),
433 'branch': ctx.branch(),
435 'branch': ctx.branch(),
434 'status': status,
436 'status': status,
435 'node': ctx.hex(),
437 'node': ctx.hex(),
436 'date': ctx.date()}
438 'date': ctx.date()}
437
439
438 return tmpl('branches', node=hex(web.repo.changelog.tip()),
440 return tmpl('branches', node=hex(web.repo.changelog.tip()),
439 entries=lambda **x: entries(0, **x),
441 entries=lambda **x: entries(0, **x),
440 latestentry=lambda **x: entries(1, **x))
442 latestentry=lambda **x: entries(1, **x))
441
443
442 def summary(web, req, tmpl):
444 def summary(web, req, tmpl):
443 i = web.repo.tagslist()
445 i = web.repo.tagslist()
444 i.reverse()
446 i.reverse()
445
447
446 def tagentries(**map):
448 def tagentries(**map):
447 parity = paritygen(web.stripecount)
449 parity = paritygen(web.stripecount)
448 count = 0
450 count = 0
449 for k, n in i:
451 for k, n in i:
450 if k == "tip": # skip tip
452 if k == "tip": # skip tip
451 continue
453 continue
452
454
453 count += 1
455 count += 1
454 if count > 10: # limit to 10 tags
456 if count > 10: # limit to 10 tags
455 break
457 break
456
458
457 yield tmpl("tagentry",
459 yield tmpl("tagentry",
458 parity=parity.next(),
460 parity=parity.next(),
459 tag=k,
461 tag=k,
460 node=hex(n),
462 node=hex(n),
461 date=web.repo[n].date())
463 date=web.repo[n].date())
462
464
463 def bookmarks(**map):
465 def bookmarks(**map):
464 parity = paritygen(web.stripecount)
466 parity = paritygen(web.stripecount)
465 b = web.repo._bookmarks.items()
467 b = web.repo._bookmarks.items()
466 for k, n in sorted(b)[:10]: # limit to 10 bookmarks
468 for k, n in sorted(b)[:10]: # limit to 10 bookmarks
467 yield {'parity': parity.next(),
469 yield {'parity': parity.next(),
468 'bookmark': k,
470 'bookmark': k,
469 'date': web.repo[n].date(),
471 'date': web.repo[n].date(),
470 'node': hex(n)}
472 'node': hex(n)}
471
473
472 def branches(**map):
474 def branches(**map):
473 parity = paritygen(web.stripecount)
475 parity = paritygen(web.stripecount)
474
476
475 b = web.repo.branchtags()
477 b = web.repo.branchtags()
476 l = [(-web.repo.changelog.rev(n), n, t) for t, n in b.iteritems()]
478 l = [(-web.repo.changelog.rev(n), n, t) for t, n in b.iteritems()]
477 for r, n, t in sorted(l):
479 for r, n, t in sorted(l):
478 yield {'parity': parity.next(),
480 yield {'parity': parity.next(),
479 'branch': t,
481 'branch': t,
480 'node': hex(n),
482 'node': hex(n),
481 'date': web.repo[n].date()}
483 'date': web.repo[n].date()}
482
484
483 def changelist(**map):
485 def changelist(**map):
484 parity = paritygen(web.stripecount, offset=start - end)
486 parity = paritygen(web.stripecount, offset=start - end)
485 l = [] # build a list in forward order for efficiency
487 l = [] # build a list in forward order for efficiency
486 for i in xrange(start, end):
488 for i in xrange(start, end):
487 ctx = web.repo[i]
489 ctx = web.repo[i]
488 n = ctx.node()
490 n = ctx.node()
489 hn = hex(n)
491 hn = hex(n)
490
492
491 l.insert(0, tmpl(
493 l.insert(0, tmpl(
492 'shortlogentry',
494 'shortlogentry',
493 parity=parity.next(),
495 parity=parity.next(),
494 author=ctx.user(),
496 author=ctx.user(),
495 desc=ctx.description(),
497 desc=ctx.description(),
496 date=ctx.date(),
498 date=ctx.date(),
497 rev=i,
499 rev=i,
498 node=hn,
500 node=hn,
499 tags=webutil.nodetagsdict(web.repo, n),
501 tags=webutil.nodetagsdict(web.repo, n),
500 bookmarks=webutil.nodebookmarksdict(web.repo, n),
502 bookmarks=webutil.nodebookmarksdict(web.repo, n),
501 inbranch=webutil.nodeinbranch(web.repo, ctx),
503 inbranch=webutil.nodeinbranch(web.repo, ctx),
502 branches=webutil.nodebranchdict(web.repo, ctx)))
504 branches=webutil.nodebranchdict(web.repo, ctx)))
503
505
504 yield l
506 yield l
505
507
506 tip = web.repo['tip']
508 tip = web.repo['tip']
507 count = len(web.repo)
509 count = len(web.repo)
508 start = max(0, count - web.maxchanges)
510 start = max(0, count - web.maxchanges)
509 end = min(count, start + web.maxchanges)
511 end = min(count, start + web.maxchanges)
510
512
511 return tmpl("summary",
513 return tmpl("summary",
512 desc=web.config("web", "description", "unknown"),
514 desc=web.config("web", "description", "unknown"),
513 owner=get_contact(web.config) or "unknown",
515 owner=get_contact(web.config) or "unknown",
514 lastchange=tip.date(),
516 lastchange=tip.date(),
515 tags=tagentries,
517 tags=tagentries,
516 bookmarks=bookmarks,
518 bookmarks=bookmarks,
517 branches=branches,
519 branches=branches,
518 shortlog=changelist,
520 shortlog=changelist,
519 node=tip.hex(),
521 node=tip.hex(),
520 archives=web.archivelist("tip"))
522 archives=web.archivelist("tip"))
521
523
522 def filediff(web, req, tmpl):
524 def filediff(web, req, tmpl):
523 fctx, ctx = None, None
525 fctx, ctx = None, None
524 try:
526 try:
525 fctx = webutil.filectx(web.repo, req)
527 fctx = webutil.filectx(web.repo, req)
526 except LookupError:
528 except LookupError:
527 ctx = webutil.changectx(web.repo, req)
529 ctx = webutil.changectx(web.repo, req)
528 path = webutil.cleanpath(web.repo, req.form['file'][0])
530 path = webutil.cleanpath(web.repo, req.form['file'][0])
529 if path not in ctx.files():
531 if path not in ctx.files():
530 raise
532 raise
531
533
532 if fctx is not None:
534 if fctx is not None:
533 n = fctx.node()
535 n = fctx.node()
534 path = fctx.path()
536 path = fctx.path()
535 else:
537 else:
536 n = ctx.node()
538 n = ctx.node()
537 # path already defined in except clause
539 # path already defined in except clause
538
540
539 parity = paritygen(web.stripecount)
541 parity = paritygen(web.stripecount)
540 style = web.config('web', 'style', 'paper')
542 style = web.config('web', 'style', 'paper')
541 if 'style' in req.form:
543 if 'style' in req.form:
542 style = req.form['style'][0]
544 style = req.form['style'][0]
543
545
544 diffs = webutil.diffs(web.repo, tmpl, fctx or ctx, [path], parity, style)
546 diffs = webutil.diffs(web.repo, tmpl, fctx or ctx, [path], parity, style)
545 rename = fctx and webutil.renamelink(fctx) or []
547 rename = fctx and webutil.renamelink(fctx) or []
546 ctx = fctx and fctx or ctx
548 ctx = fctx and fctx or ctx
547 return tmpl("filediff",
549 return tmpl("filediff",
548 file=path,
550 file=path,
549 node=hex(n),
551 node=hex(n),
550 rev=ctx.rev(),
552 rev=ctx.rev(),
551 date=ctx.date(),
553 date=ctx.date(),
552 desc=ctx.description(),
554 desc=ctx.description(),
553 author=ctx.user(),
555 author=ctx.user(),
554 rename=rename,
556 rename=rename,
555 branch=webutil.nodebranchnodefault(ctx),
557 branch=webutil.nodebranchnodefault(ctx),
556 parent=webutil.parents(ctx),
558 parent=webutil.parents(ctx),
557 child=webutil.children(ctx),
559 child=webutil.children(ctx),
558 diff=diffs)
560 diff=diffs)
559
561
560 diff = filediff
562 diff = filediff
561
563
562 def annotate(web, req, tmpl):
564 def annotate(web, req, tmpl):
563 fctx = webutil.filectx(web.repo, req)
565 fctx = webutil.filectx(web.repo, req)
564 f = fctx.path()
566 f = fctx.path()
565 parity = paritygen(web.stripecount)
567 parity = paritygen(web.stripecount)
566
568
567 def annotate(**map):
569 def annotate(**map):
568 last = None
570 last = None
569 if binary(fctx.data()):
571 if binary(fctx.data()):
570 mt = (mimetypes.guess_type(fctx.path())[0]
572 mt = (mimetypes.guess_type(fctx.path())[0]
571 or 'application/octet-stream')
573 or 'application/octet-stream')
572 lines = enumerate([((fctx.filectx(fctx.filerev()), 1),
574 lines = enumerate([((fctx.filectx(fctx.filerev()), 1),
573 '(binary:%s)' % mt)])
575 '(binary:%s)' % mt)])
574 else:
576 else:
575 lines = enumerate(fctx.annotate(follow=True, linenumber=True))
577 lines = enumerate(fctx.annotate(follow=True, linenumber=True))
576 for lineno, ((f, targetline), l) in lines:
578 for lineno, ((f, targetline), l) in lines:
577 fnode = f.filenode()
579 fnode = f.filenode()
578
580
579 if last != fnode:
581 if last != fnode:
580 last = fnode
582 last = fnode
581
583
582 yield {"parity": parity.next(),
584 yield {"parity": parity.next(),
583 "node": hex(f.node()),
585 "node": hex(f.node()),
584 "rev": f.rev(),
586 "rev": f.rev(),
585 "author": f.user(),
587 "author": f.user(),
586 "desc": f.description(),
588 "desc": f.description(),
587 "file": f.path(),
589 "file": f.path(),
588 "targetline": targetline,
590 "targetline": targetline,
589 "line": l,
591 "line": l,
590 "lineid": "l%d" % (lineno + 1),
592 "lineid": "l%d" % (lineno + 1),
591 "linenumber": "% 6d" % (lineno + 1),
593 "linenumber": "% 6d" % (lineno + 1),
592 "revdate": f.date()}
594 "revdate": f.date()}
593
595
594 return tmpl("fileannotate",
596 return tmpl("fileannotate",
595 file=f,
597 file=f,
596 annotate=annotate,
598 annotate=annotate,
597 path=webutil.up(f),
599 path=webutil.up(f),
598 rev=fctx.rev(),
600 rev=fctx.rev(),
599 node=hex(fctx.node()),
601 node=hex(fctx.node()),
600 author=fctx.user(),
602 author=fctx.user(),
601 date=fctx.date(),
603 date=fctx.date(),
602 desc=fctx.description(),
604 desc=fctx.description(),
603 rename=webutil.renamelink(fctx),
605 rename=webutil.renamelink(fctx),
604 branch=webutil.nodebranchnodefault(fctx),
606 branch=webutil.nodebranchnodefault(fctx),
605 parent=webutil.parents(fctx),
607 parent=webutil.parents(fctx),
606 child=webutil.children(fctx),
608 child=webutil.children(fctx),
607 permissions=fctx.manifest().flags(f))
609 permissions=fctx.manifest().flags(f))
608
610
609 def filelog(web, req, tmpl):
611 def filelog(web, req, tmpl):
610
612
611 try:
613 try:
612 fctx = webutil.filectx(web.repo, req)
614 fctx = webutil.filectx(web.repo, req)
613 f = fctx.path()
615 f = fctx.path()
614 fl = fctx.filelog()
616 fl = fctx.filelog()
615 except error.LookupError:
617 except error.LookupError:
616 f = webutil.cleanpath(web.repo, req.form['file'][0])
618 f = webutil.cleanpath(web.repo, req.form['file'][0])
617 fl = web.repo.file(f)
619 fl = web.repo.file(f)
618 numrevs = len(fl)
620 numrevs = len(fl)
619 if not numrevs: # file doesn't exist at all
621 if not numrevs: # file doesn't exist at all
620 raise
622 raise
621 rev = webutil.changectx(web.repo, req).rev()
623 rev = webutil.changectx(web.repo, req).rev()
622 first = fl.linkrev(0)
624 first = fl.linkrev(0)
623 if rev < first: # current rev is from before file existed
625 if rev < first: # current rev is from before file existed
624 raise
626 raise
625 frev = numrevs - 1
627 frev = numrevs - 1
626 while fl.linkrev(frev) > rev:
628 while fl.linkrev(frev) > rev:
627 frev -= 1
629 frev -= 1
628 fctx = web.repo.filectx(f, fl.linkrev(frev))
630 fctx = web.repo.filectx(f, fl.linkrev(frev))
629
631
630 revcount = web.maxshortchanges
632 revcount = web.maxshortchanges
631 if 'revcount' in req.form:
633 if 'revcount' in req.form:
632 revcount = int(req.form.get('revcount', [revcount])[0])
634 revcount = int(req.form.get('revcount', [revcount])[0])
635 revcount = max(revcount, 1)
633 tmpl.defaults['sessionvars']['revcount'] = revcount
636 tmpl.defaults['sessionvars']['revcount'] = revcount
634
637
635 lessvars = copy.copy(tmpl.defaults['sessionvars'])
638 lessvars = copy.copy(tmpl.defaults['sessionvars'])
636 lessvars['revcount'] = revcount / 2
639 lessvars['revcount'] = max(revcount / 2, 1)
637 morevars = copy.copy(tmpl.defaults['sessionvars'])
640 morevars = copy.copy(tmpl.defaults['sessionvars'])
638 morevars['revcount'] = revcount * 2
641 morevars['revcount'] = revcount * 2
639
642
640 count = fctx.filerev() + 1
643 count = fctx.filerev() + 1
641 start = max(0, fctx.filerev() - revcount + 1) # first rev on this page
644 start = max(0, fctx.filerev() - revcount + 1) # first rev on this page
642 end = min(count, start + revcount) # last rev on this page
645 end = min(count, start + revcount) # last rev on this page
643 parity = paritygen(web.stripecount, offset=start - end)
646 parity = paritygen(web.stripecount, offset=start - end)
644
647
645 def entries(limit=0, **map):
648 def entries(limit=0, **map):
646 l = []
649 l = []
647
650
648 repo = web.repo
651 repo = web.repo
649 for i in xrange(start, end):
652 for i in xrange(start, end):
650 iterfctx = fctx.filectx(i)
653 iterfctx = fctx.filectx(i)
651
654
652 l.insert(0, {"parity": parity.next(),
655 l.insert(0, {"parity": parity.next(),
653 "filerev": i,
656 "filerev": i,
654 "file": f,
657 "file": f,
655 "node": hex(iterfctx.node()),
658 "node": hex(iterfctx.node()),
656 "author": iterfctx.user(),
659 "author": iterfctx.user(),
657 "date": iterfctx.date(),
660 "date": iterfctx.date(),
658 "rename": webutil.renamelink(iterfctx),
661 "rename": webutil.renamelink(iterfctx),
659 "parent": webutil.parents(iterfctx),
662 "parent": webutil.parents(iterfctx),
660 "child": webutil.children(iterfctx),
663 "child": webutil.children(iterfctx),
661 "desc": iterfctx.description(),
664 "desc": iterfctx.description(),
662 "tags": webutil.nodetagsdict(repo, iterfctx.node()),
665 "tags": webutil.nodetagsdict(repo, iterfctx.node()),
663 "bookmarks": webutil.nodebookmarksdict(
666 "bookmarks": webutil.nodebookmarksdict(
664 repo, iterfctx.node()),
667 repo, iterfctx.node()),
665 "branch": webutil.nodebranchnodefault(iterfctx),
668 "branch": webutil.nodebranchnodefault(iterfctx),
666 "inbranch": webutil.nodeinbranch(repo, iterfctx),
669 "inbranch": webutil.nodeinbranch(repo, iterfctx),
667 "branches": webutil.nodebranchdict(repo, iterfctx)})
670 "branches": webutil.nodebranchdict(repo, iterfctx)})
668
671
669 if limit > 0:
672 if limit > 0:
670 l = l[:limit]
673 l = l[:limit]
671
674
672 for e in l:
675 for e in l:
673 yield e
676 yield e
674
677
675 nodefunc = lambda x: fctx.filectx(fileid=x)
678 nodefunc = lambda x: fctx.filectx(fileid=x)
676 nav = webutil.revnavgen(end - 1, revcount, count, nodefunc)
679 nav = webutil.revnavgen(end - 1, revcount, count, nodefunc)
677 return tmpl("filelog", file=f, node=hex(fctx.node()), nav=nav,
680 return tmpl("filelog", file=f, node=hex(fctx.node()), nav=nav,
678 entries=lambda **x: entries(limit=0, **x),
681 entries=lambda **x: entries(limit=0, **x),
679 latestentry=lambda **x: entries(limit=1, **x),
682 latestentry=lambda **x: entries(limit=1, **x),
680 revcount=revcount, morevars=morevars, lessvars=lessvars)
683 revcount=revcount, morevars=morevars, lessvars=lessvars)
681
684
682 def archive(web, req, tmpl):
685 def archive(web, req, tmpl):
683 type_ = req.form.get('type', [None])[0]
686 type_ = req.form.get('type', [None])[0]
684 allowed = web.configlist("web", "allow_archive")
687 allowed = web.configlist("web", "allow_archive")
685 key = req.form['node'][0]
688 key = req.form['node'][0]
686
689
687 if type_ not in web.archives:
690 if type_ not in web.archives:
688 msg = 'Unsupported archive type: %s' % type_
691 msg = 'Unsupported archive type: %s' % type_
689 raise ErrorResponse(HTTP_NOT_FOUND, msg)
692 raise ErrorResponse(HTTP_NOT_FOUND, msg)
690
693
691 if not ((type_ in allowed or
694 if not ((type_ in allowed or
692 web.configbool("web", "allow" + type_, False))):
695 web.configbool("web", "allow" + type_, False))):
693 msg = 'Archive type not allowed: %s' % type_
696 msg = 'Archive type not allowed: %s' % type_
694 raise ErrorResponse(HTTP_FORBIDDEN, msg)
697 raise ErrorResponse(HTTP_FORBIDDEN, msg)
695
698
696 reponame = re.sub(r"\W+", "-", os.path.basename(web.reponame))
699 reponame = re.sub(r"\W+", "-", os.path.basename(web.reponame))
697 cnode = web.repo.lookup(key)
700 cnode = web.repo.lookup(key)
698 arch_version = key
701 arch_version = key
699 if cnode == key or key == 'tip':
702 if cnode == key or key == 'tip':
700 arch_version = short(cnode)
703 arch_version = short(cnode)
701 name = "%s-%s" % (reponame, arch_version)
704 name = "%s-%s" % (reponame, arch_version)
702 mimetype, artype, extension, encoding = web.archive_specs[type_]
705 mimetype, artype, extension, encoding = web.archive_specs[type_]
703 headers = [
706 headers = [
704 ('Content-Type', mimetype),
707 ('Content-Type', mimetype),
705 ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
708 ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
706 ]
709 ]
707 if encoding:
710 if encoding:
708 headers.append(('Content-Encoding', encoding))
711 headers.append(('Content-Encoding', encoding))
709 req.header(headers)
712 req.header(headers)
710 req.respond(HTTP_OK)
713 req.respond(HTTP_OK)
711 archival.archive(web.repo, req, cnode, artype, prefix=name)
714 archival.archive(web.repo, req, cnode, artype, prefix=name)
712 return []
715 return []
713
716
714
717
715 def static(web, req, tmpl):
718 def static(web, req, tmpl):
716 fname = req.form['file'][0]
719 fname = req.form['file'][0]
717 # a repo owner may set web.static in .hg/hgrc to get any file
720 # a repo owner may set web.static in .hg/hgrc to get any file
718 # readable by the user running the CGI script
721 # readable by the user running the CGI script
719 static = web.config("web", "static", None, untrusted=False)
722 static = web.config("web", "static", None, untrusted=False)
720 if not static:
723 if not static:
721 tp = web.templatepath or templater.templatepath()
724 tp = web.templatepath or templater.templatepath()
722 if isinstance(tp, str):
725 if isinstance(tp, str):
723 tp = [tp]
726 tp = [tp]
724 static = [os.path.join(p, 'static') for p in tp]
727 static = [os.path.join(p, 'static') for p in tp]
725 return [staticfile(static, fname, req)]
728 return [staticfile(static, fname, req)]
726
729
727 def graph(web, req, tmpl):
730 def graph(web, req, tmpl):
728
731
729 rev = webutil.changectx(web.repo, req).rev()
732 rev = webutil.changectx(web.repo, req).rev()
730 bg_height = 39
733 bg_height = 39
731 revcount = web.maxshortchanges
734 revcount = web.maxshortchanges
732 if 'revcount' in req.form:
735 if 'revcount' in req.form:
733 revcount = int(req.form.get('revcount', [revcount])[0])
736 revcount = int(req.form.get('revcount', [revcount])[0])
737 revcount = max(revcount, 1)
734 tmpl.defaults['sessionvars']['revcount'] = revcount
738 tmpl.defaults['sessionvars']['revcount'] = revcount
735
739
736 lessvars = copy.copy(tmpl.defaults['sessionvars'])
740 lessvars = copy.copy(tmpl.defaults['sessionvars'])
737 lessvars['revcount'] = revcount / 2
741 lessvars['revcount'] = max(revcount / 2, 1)
738 morevars = copy.copy(tmpl.defaults['sessionvars'])
742 morevars = copy.copy(tmpl.defaults['sessionvars'])
739 morevars['revcount'] = revcount * 2
743 morevars['revcount'] = revcount * 2
740
744
741 max_rev = len(web.repo) - 1
745 max_rev = len(web.repo) - 1
742 revcount = min(max_rev, revcount)
746 revcount = min(max_rev, revcount)
743 revnode = web.repo.changelog.node(rev)
747 revnode = web.repo.changelog.node(rev)
744 revnode_hex = hex(revnode)
748 revnode_hex = hex(revnode)
745 uprev = min(max_rev, rev + revcount)
749 uprev = min(max_rev, rev + revcount)
746 downrev = max(0, rev - revcount)
750 downrev = max(0, rev - revcount)
747 count = len(web.repo)
751 count = len(web.repo)
748 changenav = webutil.revnavgen(rev, revcount, count, web.repo.changectx)
752 changenav = webutil.revnavgen(rev, revcount, count, web.repo.changectx)
749 startrev = rev
753 startrev = rev
750 # if starting revision is less than 60 set it to uprev
754 # if starting revision is less than 60 set it to uprev
751 if rev < web.maxshortchanges:
755 if rev < web.maxshortchanges:
752 startrev = uprev
756 startrev = uprev
753
757
754 dag = graphmod.revisions(web.repo, startrev, downrev)
758 dag = graphmod.revisions(web.repo, startrev, downrev)
755 tree = list(graphmod.colored(dag))
759 tree = list(graphmod.colored(dag))
756 canvasheight = (len(tree) + 1) * bg_height - 27
760 canvasheight = (len(tree) + 1) * bg_height - 27
757 data = []
761 data = []
758 for (id, type, ctx, vtx, edges) in tree:
762 for (id, type, ctx, vtx, edges) in tree:
759 if type != graphmod.CHANGESET:
763 if type != graphmod.CHANGESET:
760 continue
764 continue
761 node = short(ctx.node())
765 node = short(ctx.node())
762 age = templatefilters.age(ctx.date())
766 age = templatefilters.age(ctx.date())
763 desc = templatefilters.firstline(ctx.description())
767 desc = templatefilters.firstline(ctx.description())
764 desc = cgi.escape(templatefilters.nonempty(desc))
768 desc = cgi.escape(templatefilters.nonempty(desc))
765 user = cgi.escape(templatefilters.person(ctx.user()))
769 user = cgi.escape(templatefilters.person(ctx.user()))
766 branch = ctx.branch()
770 branch = ctx.branch()
767 branch = branch, web.repo.branchtags().get(branch) == ctx.node()
771 branch = branch, web.repo.branchtags().get(branch) == ctx.node()
768 data.append((node, vtx, edges, desc, user, age, branch, ctx.tags(),
772 data.append((node, vtx, edges, desc, user, age, branch, ctx.tags(),
769 ctx.bookmarks()))
773 ctx.bookmarks()))
770
774
771 return tmpl('graph', rev=rev, revcount=revcount, uprev=uprev,
775 return tmpl('graph', rev=rev, revcount=revcount, uprev=uprev,
772 lessvars=lessvars, morevars=morevars, downrev=downrev,
776 lessvars=lessvars, morevars=morevars, downrev=downrev,
773 canvasheight=canvasheight, jsdata=data, bg_height=bg_height,
777 canvasheight=canvasheight, jsdata=data, bg_height=bg_height,
774 node=revnode_hex, changenav=changenav)
778 node=revnode_hex, changenav=changenav)
775
779
776 def _getdoc(e):
780 def _getdoc(e):
777 doc = e[0].__doc__
781 doc = e[0].__doc__
778 if doc:
782 if doc:
779 doc = doc.split('\n')[0]
783 doc = doc.split('\n')[0]
780 else:
784 else:
781 doc = _('(no help text available)')
785 doc = _('(no help text available)')
782 return doc
786 return doc
783
787
784 def help(web, req, tmpl):
788 def help(web, req, tmpl):
785 from mercurial import commands # avoid cycle
789 from mercurial import commands # avoid cycle
786
790
787 topicname = req.form.get('node', [None])[0]
791 topicname = req.form.get('node', [None])[0]
788 if not topicname:
792 if not topicname:
789 topic = []
793 topic = []
790
794
791 def topics(**map):
795 def topics(**map):
792 for entries, summary, _ in helpmod.helptable:
796 for entries, summary, _ in helpmod.helptable:
793 entries = sorted(entries, key=len)
797 entries = sorted(entries, key=len)
794 yield {'topic': entries[-1], 'summary': summary}
798 yield {'topic': entries[-1], 'summary': summary}
795
799
796 early, other = [], []
800 early, other = [], []
797 primary = lambda s: s.split('|')[0]
801 primary = lambda s: s.split('|')[0]
798 for c, e in commands.table.iteritems():
802 for c, e in commands.table.iteritems():
799 doc = _getdoc(e)
803 doc = _getdoc(e)
800 if 'DEPRECATED' in doc or c.startswith('debug'):
804 if 'DEPRECATED' in doc or c.startswith('debug'):
801 continue
805 continue
802 cmd = primary(c)
806 cmd = primary(c)
803 if cmd.startswith('^'):
807 if cmd.startswith('^'):
804 early.append((cmd[1:], doc))
808 early.append((cmd[1:], doc))
805 else:
809 else:
806 other.append((cmd, doc))
810 other.append((cmd, doc))
807
811
808 early.sort()
812 early.sort()
809 other.sort()
813 other.sort()
810
814
811 def earlycommands(**map):
815 def earlycommands(**map):
812 for c, doc in early:
816 for c, doc in early:
813 yield {'topic': c, 'summary': doc}
817 yield {'topic': c, 'summary': doc}
814
818
815 def othercommands(**map):
819 def othercommands(**map):
816 for c, doc in other:
820 for c, doc in other:
817 yield {'topic': c, 'summary': doc}
821 yield {'topic': c, 'summary': doc}
818
822
819 return tmpl('helptopics', topics=topics, earlycommands=earlycommands,
823 return tmpl('helptopics', topics=topics, earlycommands=earlycommands,
820 othercommands=othercommands, title='Index')
824 othercommands=othercommands, title='Index')
821
825
822 u = webutil.wsgiui()
826 u = webutil.wsgiui()
823 u.pushbuffer()
827 u.pushbuffer()
824 try:
828 try:
825 commands.help_(u, topicname)
829 commands.help_(u, topicname)
826 except error.UnknownCommand:
830 except error.UnknownCommand:
827 raise ErrorResponse(HTTP_NOT_FOUND)
831 raise ErrorResponse(HTTP_NOT_FOUND)
828 doc = u.popbuffer()
832 doc = u.popbuffer()
829 return tmpl('help', topic=topicname, doc=doc)
833 return tmpl('help', topic=topicname, doc=doc)
General Comments 0
You need to be logged in to leave comments. Login now