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