##// END OF EJS Templates
hgwebdir: do not show RSS and Atom links for plain directories...
Angel Ezquerra -
r18046:40374059 default
parent child Browse files
Show More
@@ -1,451 +1,452
1 # hgweb/hgwebdir_mod.py - Web interface for a directory of repositories.
1 # hgweb/hgwebdir_mod.py - Web interface for a directory of repositories.
2 #
2 #
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 import os, re, time
9 import os, re, time
10 from mercurial.i18n import _
10 from mercurial.i18n import _
11 from mercurial import ui, hg, scmutil, util, templater
11 from mercurial import ui, hg, scmutil, util, templater
12 from mercurial import error, encoding
12 from mercurial import error, encoding
13 from common import ErrorResponse, get_mtime, staticfile, paritygen, \
13 from common import ErrorResponse, get_mtime, staticfile, paritygen, \
14 get_contact, HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
14 get_contact, HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
15 from hgweb_mod import hgweb
15 from hgweb_mod import hgweb
16 from request import wsgirequest
16 from request import wsgirequest
17 import webutil
17 import webutil
18
18
19 def cleannames(items):
19 def cleannames(items):
20 return [(util.pconvert(name).strip('/'), path) for name, path in items]
20 return [(util.pconvert(name).strip('/'), path) for name, path in items]
21
21
22 def findrepos(paths):
22 def findrepos(paths):
23 repos = []
23 repos = []
24 for prefix, root in cleannames(paths):
24 for prefix, root in cleannames(paths):
25 roothead, roottail = os.path.split(root)
25 roothead, roottail = os.path.split(root)
26 # "foo = /bar/*" or "foo = /bar/**" lets every repo /bar/N in or below
26 # "foo = /bar/*" or "foo = /bar/**" lets every repo /bar/N in or below
27 # /bar/ be served as as foo/N .
27 # /bar/ be served as as foo/N .
28 # '*' will not search inside dirs with .hg (except .hg/patches),
28 # '*' will not search inside dirs with .hg (except .hg/patches),
29 # '**' will search inside dirs with .hg (and thus also find subrepos).
29 # '**' will search inside dirs with .hg (and thus also find subrepos).
30 try:
30 try:
31 recurse = {'*': False, '**': True}[roottail]
31 recurse = {'*': False, '**': True}[roottail]
32 except KeyError:
32 except KeyError:
33 repos.append((prefix, root))
33 repos.append((prefix, root))
34 continue
34 continue
35 roothead = os.path.normpath(os.path.abspath(roothead))
35 roothead = os.path.normpath(os.path.abspath(roothead))
36 paths = scmutil.walkrepos(roothead, followsym=True, recurse=recurse)
36 paths = scmutil.walkrepos(roothead, followsym=True, recurse=recurse)
37 repos.extend(urlrepos(prefix, roothead, paths))
37 repos.extend(urlrepos(prefix, roothead, paths))
38 return repos
38 return repos
39
39
40 def urlrepos(prefix, roothead, paths):
40 def urlrepos(prefix, roothead, paths):
41 """yield url paths and filesystem paths from a list of repo paths
41 """yield url paths and filesystem paths from a list of repo paths
42
42
43 >>> conv = lambda seq: [(v, util.pconvert(p)) for v,p in seq]
43 >>> conv = lambda seq: [(v, util.pconvert(p)) for v,p in seq]
44 >>> conv(urlrepos('hg', '/opt', ['/opt/r', '/opt/r/r', '/opt']))
44 >>> conv(urlrepos('hg', '/opt', ['/opt/r', '/opt/r/r', '/opt']))
45 [('hg/r', '/opt/r'), ('hg/r/r', '/opt/r/r'), ('hg', '/opt')]
45 [('hg/r', '/opt/r'), ('hg/r/r', '/opt/r/r'), ('hg', '/opt')]
46 >>> conv(urlrepos('', '/opt', ['/opt/r', '/opt/r/r', '/opt']))
46 >>> conv(urlrepos('', '/opt', ['/opt/r', '/opt/r/r', '/opt']))
47 [('r', '/opt/r'), ('r/r', '/opt/r/r'), ('', '/opt')]
47 [('r', '/opt/r'), ('r/r', '/opt/r/r'), ('', '/opt')]
48 """
48 """
49 for path in paths:
49 for path in paths:
50 path = os.path.normpath(path)
50 path = os.path.normpath(path)
51 yield (prefix + '/' +
51 yield (prefix + '/' +
52 util.pconvert(path[len(roothead):]).lstrip('/')).strip('/'), path
52 util.pconvert(path[len(roothead):]).lstrip('/')).strip('/'), path
53
53
54 def geturlcgivars(baseurl, port):
54 def geturlcgivars(baseurl, port):
55 """
55 """
56 Extract CGI variables from baseurl
56 Extract CGI variables from baseurl
57
57
58 >>> geturlcgivars("http://host.org/base", "80")
58 >>> geturlcgivars("http://host.org/base", "80")
59 ('host.org', '80', '/base')
59 ('host.org', '80', '/base')
60 >>> geturlcgivars("http://host.org:8000/base", "80")
60 >>> geturlcgivars("http://host.org:8000/base", "80")
61 ('host.org', '8000', '/base')
61 ('host.org', '8000', '/base')
62 >>> geturlcgivars('/base', 8000)
62 >>> geturlcgivars('/base', 8000)
63 ('', '8000', '/base')
63 ('', '8000', '/base')
64 >>> geturlcgivars("base", '8000')
64 >>> geturlcgivars("base", '8000')
65 ('', '8000', '/base')
65 ('', '8000', '/base')
66 >>> geturlcgivars("http://host", '8000')
66 >>> geturlcgivars("http://host", '8000')
67 ('host', '8000', '/')
67 ('host', '8000', '/')
68 >>> geturlcgivars("http://host/", '8000')
68 >>> geturlcgivars("http://host/", '8000')
69 ('host', '8000', '/')
69 ('host', '8000', '/')
70 """
70 """
71 u = util.url(baseurl)
71 u = util.url(baseurl)
72 name = u.host or ''
72 name = u.host or ''
73 if u.port:
73 if u.port:
74 port = u.port
74 port = u.port
75 path = u.path or ""
75 path = u.path or ""
76 if not path.startswith('/'):
76 if not path.startswith('/'):
77 path = '/' + path
77 path = '/' + path
78
78
79 return name, str(port), path
79 return name, str(port), path
80
80
81 class hgwebdir(object):
81 class hgwebdir(object):
82 refreshinterval = 20
82 refreshinterval = 20
83
83
84 def __init__(self, conf, baseui=None):
84 def __init__(self, conf, baseui=None):
85 self.conf = conf
85 self.conf = conf
86 self.baseui = baseui
86 self.baseui = baseui
87 self.lastrefresh = 0
87 self.lastrefresh = 0
88 self.motd = None
88 self.motd = None
89 self.refresh()
89 self.refresh()
90
90
91 def refresh(self):
91 def refresh(self):
92 if self.lastrefresh + self.refreshinterval > time.time():
92 if self.lastrefresh + self.refreshinterval > time.time():
93 return
93 return
94
94
95 if self.baseui:
95 if self.baseui:
96 u = self.baseui.copy()
96 u = self.baseui.copy()
97 else:
97 else:
98 u = ui.ui()
98 u = ui.ui()
99 u.setconfig('ui', 'report_untrusted', 'off')
99 u.setconfig('ui', 'report_untrusted', 'off')
100 u.setconfig('ui', 'nontty', 'true')
100 u.setconfig('ui', 'nontty', 'true')
101
101
102 if not isinstance(self.conf, (dict, list, tuple)):
102 if not isinstance(self.conf, (dict, list, tuple)):
103 map = {'paths': 'hgweb-paths'}
103 map = {'paths': 'hgweb-paths'}
104 if not os.path.exists(self.conf):
104 if not os.path.exists(self.conf):
105 raise util.Abort(_('config file %s not found!') % self.conf)
105 raise util.Abort(_('config file %s not found!') % self.conf)
106 u.readconfig(self.conf, remap=map, trust=True)
106 u.readconfig(self.conf, remap=map, trust=True)
107 paths = []
107 paths = []
108 for name, ignored in u.configitems('hgweb-paths'):
108 for name, ignored in u.configitems('hgweb-paths'):
109 for path in u.configlist('hgweb-paths', name):
109 for path in u.configlist('hgweb-paths', name):
110 paths.append((name, path))
110 paths.append((name, path))
111 elif isinstance(self.conf, (list, tuple)):
111 elif isinstance(self.conf, (list, tuple)):
112 paths = self.conf
112 paths = self.conf
113 elif isinstance(self.conf, dict):
113 elif isinstance(self.conf, dict):
114 paths = self.conf.items()
114 paths = self.conf.items()
115
115
116 repos = findrepos(paths)
116 repos = findrepos(paths)
117 for prefix, root in u.configitems('collections'):
117 for prefix, root in u.configitems('collections'):
118 prefix = util.pconvert(prefix)
118 prefix = util.pconvert(prefix)
119 for path in scmutil.walkrepos(root, followsym=True):
119 for path in scmutil.walkrepos(root, followsym=True):
120 repo = os.path.normpath(path)
120 repo = os.path.normpath(path)
121 name = util.pconvert(repo)
121 name = util.pconvert(repo)
122 if name.startswith(prefix):
122 if name.startswith(prefix):
123 name = name[len(prefix):]
123 name = name[len(prefix):]
124 repos.append((name.lstrip('/'), repo))
124 repos.append((name.lstrip('/'), repo))
125
125
126 self.repos = repos
126 self.repos = repos
127 self.ui = u
127 self.ui = u
128 encoding.encoding = self.ui.config('web', 'encoding',
128 encoding.encoding = self.ui.config('web', 'encoding',
129 encoding.encoding)
129 encoding.encoding)
130 self.style = self.ui.config('web', 'style', 'paper')
130 self.style = self.ui.config('web', 'style', 'paper')
131 self.templatepath = self.ui.config('web', 'templates', None)
131 self.templatepath = self.ui.config('web', 'templates', None)
132 self.stripecount = self.ui.config('web', 'stripes', 1)
132 self.stripecount = self.ui.config('web', 'stripes', 1)
133 if self.stripecount:
133 if self.stripecount:
134 self.stripecount = int(self.stripecount)
134 self.stripecount = int(self.stripecount)
135 self._baseurl = self.ui.config('web', 'baseurl')
135 self._baseurl = self.ui.config('web', 'baseurl')
136 self.lastrefresh = time.time()
136 self.lastrefresh = time.time()
137
137
138 def run(self):
138 def run(self):
139 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
139 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
140 raise RuntimeError("This function is only intended to be "
140 raise RuntimeError("This function is only intended to be "
141 "called while running as a CGI script.")
141 "called while running as a CGI script.")
142 import mercurial.hgweb.wsgicgi as wsgicgi
142 import mercurial.hgweb.wsgicgi as wsgicgi
143 wsgicgi.launch(self)
143 wsgicgi.launch(self)
144
144
145 def __call__(self, env, respond):
145 def __call__(self, env, respond):
146 req = wsgirequest(env, respond)
146 req = wsgirequest(env, respond)
147 return self.run_wsgi(req)
147 return self.run_wsgi(req)
148
148
149 def read_allowed(self, ui, req):
149 def read_allowed(self, ui, req):
150 """Check allow_read and deny_read config options of a repo's ui object
150 """Check allow_read and deny_read config options of a repo's ui object
151 to determine user permissions. By default, with neither option set (or
151 to determine user permissions. By default, with neither option set (or
152 both empty), allow all users to read the repo. There are two ways a
152 both empty), allow all users to read the repo. There are two ways a
153 user can be denied read access: (1) deny_read is not empty, and the
153 user can be denied read access: (1) deny_read is not empty, and the
154 user is unauthenticated or deny_read contains user (or *), and (2)
154 user is unauthenticated or deny_read contains user (or *), and (2)
155 allow_read is not empty and the user is not in allow_read. Return True
155 allow_read is not empty and the user is not in allow_read. Return True
156 if user is allowed to read the repo, else return False."""
156 if user is allowed to read the repo, else return False."""
157
157
158 user = req.env.get('REMOTE_USER')
158 user = req.env.get('REMOTE_USER')
159
159
160 deny_read = ui.configlist('web', 'deny_read', untrusted=True)
160 deny_read = ui.configlist('web', 'deny_read', untrusted=True)
161 if deny_read and (not user or deny_read == ['*'] or user in deny_read):
161 if deny_read and (not user or deny_read == ['*'] or user in deny_read):
162 return False
162 return False
163
163
164 allow_read = ui.configlist('web', 'allow_read', untrusted=True)
164 allow_read = ui.configlist('web', 'allow_read', untrusted=True)
165 # by default, allow reading if no allow_read option has been set
165 # by default, allow reading if no allow_read option has been set
166 if (not allow_read) or (allow_read == ['*']) or (user in allow_read):
166 if (not allow_read) or (allow_read == ['*']) or (user in allow_read):
167 return True
167 return True
168
168
169 return False
169 return False
170
170
171 def run_wsgi(self, req):
171 def run_wsgi(self, req):
172 try:
172 try:
173 try:
173 try:
174 self.refresh()
174 self.refresh()
175
175
176 virtual = req.env.get("PATH_INFO", "").strip('/')
176 virtual = req.env.get("PATH_INFO", "").strip('/')
177 tmpl = self.templater(req)
177 tmpl = self.templater(req)
178 ctype = tmpl('mimetype', encoding=encoding.encoding)
178 ctype = tmpl('mimetype', encoding=encoding.encoding)
179 ctype = templater.stringify(ctype)
179 ctype = templater.stringify(ctype)
180
180
181 # a static file
181 # a static file
182 if virtual.startswith('static/') or 'static' in req.form:
182 if virtual.startswith('static/') or 'static' in req.form:
183 if virtual.startswith('static/'):
183 if virtual.startswith('static/'):
184 fname = virtual[7:]
184 fname = virtual[7:]
185 else:
185 else:
186 fname = req.form['static'][0]
186 fname = req.form['static'][0]
187 static = templater.templatepath('static')
187 static = templater.templatepath('static')
188 return (staticfile(static, fname, req),)
188 return (staticfile(static, fname, req),)
189
189
190 # top-level index
190 # top-level index
191 elif not virtual:
191 elif not virtual:
192 req.respond(HTTP_OK, ctype)
192 req.respond(HTTP_OK, ctype)
193 return self.makeindex(req, tmpl)
193 return self.makeindex(req, tmpl)
194
194
195 # nested indexes and hgwebs
195 # nested indexes and hgwebs
196
196
197 repos = dict(self.repos)
197 repos = dict(self.repos)
198 virtualrepo = virtual
198 virtualrepo = virtual
199 while virtualrepo:
199 while virtualrepo:
200 real = repos.get(virtualrepo)
200 real = repos.get(virtualrepo)
201 if real:
201 if real:
202 req.env['REPO_NAME'] = virtualrepo
202 req.env['REPO_NAME'] = virtualrepo
203 try:
203 try:
204 repo = hg.repository(self.ui, real)
204 repo = hg.repository(self.ui, real)
205 return hgweb(repo).run_wsgi(req)
205 return hgweb(repo).run_wsgi(req)
206 except IOError, inst:
206 except IOError, inst:
207 msg = inst.strerror
207 msg = inst.strerror
208 raise ErrorResponse(HTTP_SERVER_ERROR, msg)
208 raise ErrorResponse(HTTP_SERVER_ERROR, msg)
209 except error.RepoError, inst:
209 except error.RepoError, inst:
210 raise ErrorResponse(HTTP_SERVER_ERROR, str(inst))
210 raise ErrorResponse(HTTP_SERVER_ERROR, str(inst))
211
211
212 up = virtualrepo.rfind('/')
212 up = virtualrepo.rfind('/')
213 if up < 0:
213 if up < 0:
214 break
214 break
215 virtualrepo = virtualrepo[:up]
215 virtualrepo = virtualrepo[:up]
216
216
217 # browse subdirectories
217 # browse subdirectories
218 subdir = virtual + '/'
218 subdir = virtual + '/'
219 if [r for r in repos if r.startswith(subdir)]:
219 if [r for r in repos if r.startswith(subdir)]:
220 req.respond(HTTP_OK, ctype)
220 req.respond(HTTP_OK, ctype)
221 return self.makeindex(req, tmpl, subdir)
221 return self.makeindex(req, tmpl, subdir)
222
222
223 # prefixes not found
223 # prefixes not found
224 req.respond(HTTP_NOT_FOUND, ctype)
224 req.respond(HTTP_NOT_FOUND, ctype)
225 return tmpl("notfound", repo=virtual)
225 return tmpl("notfound", repo=virtual)
226
226
227 except ErrorResponse, err:
227 except ErrorResponse, err:
228 req.respond(err, ctype)
228 req.respond(err, ctype)
229 return tmpl('error', error=err.message or '')
229 return tmpl('error', error=err.message or '')
230 finally:
230 finally:
231 tmpl = None
231 tmpl = None
232
232
233 def makeindex(self, req, tmpl, subdir=""):
233 def makeindex(self, req, tmpl, subdir=""):
234
234
235 def archivelist(ui, nodeid, url):
235 def archivelist(ui, nodeid, url):
236 allowed = ui.configlist("web", "allow_archive", untrusted=True)
236 allowed = ui.configlist("web", "allow_archive", untrusted=True)
237 archives = []
237 archives = []
238 for i in [('zip', '.zip'), ('gz', '.tar.gz'), ('bz2', '.tar.bz2')]:
238 for i in [('zip', '.zip'), ('gz', '.tar.gz'), ('bz2', '.tar.bz2')]:
239 if i[0] in allowed or ui.configbool("web", "allow" + i[0],
239 if i[0] in allowed or ui.configbool("web", "allow" + i[0],
240 untrusted=True):
240 untrusted=True):
241 archives.append({"type" : i[0], "extension": i[1],
241 archives.append({"type" : i[0], "extension": i[1],
242 "node": nodeid, "url": url})
242 "node": nodeid, "url": url})
243 return archives
243 return archives
244
244
245 def rawentries(subdir="", **map):
245 def rawentries(subdir="", **map):
246
246
247 descend = self.ui.configbool('web', 'descend', True)
247 descend = self.ui.configbool('web', 'descend', True)
248 collapse = self.ui.configbool('web', 'collapse', False)
248 collapse = self.ui.configbool('web', 'collapse', False)
249 seenrepos = set()
249 seenrepos = set()
250 seendirs = set()
250 seendirs = set()
251 for name, path in self.repos:
251 for name, path in self.repos:
252
252
253 if not name.startswith(subdir):
253 if not name.startswith(subdir):
254 continue
254 continue
255 name = name[len(subdir):]
255 name = name[len(subdir):]
256 directory = False
256 directory = False
257
257
258 if '/' in name:
258 if '/' in name:
259 if not descend:
259 if not descend:
260 continue
260 continue
261
261
262 nameparts = name.split('/')
262 nameparts = name.split('/')
263 rootname = nameparts[0]
263 rootname = nameparts[0]
264
264
265 if not collapse:
265 if not collapse:
266 pass
266 pass
267 elif rootname in seendirs:
267 elif rootname in seendirs:
268 continue
268 continue
269 elif rootname in seenrepos:
269 elif rootname in seenrepos:
270 pass
270 pass
271 else:
271 else:
272 directory = True
272 directory = True
273 name = rootname
273 name = rootname
274
274
275 # redefine the path to refer to the directory
275 # redefine the path to refer to the directory
276 discarded = '/'.join(nameparts[1:])
276 discarded = '/'.join(nameparts[1:])
277
277
278 # remove name parts plus accompanying slash
278 # remove name parts plus accompanying slash
279 path = path[:-len(discarded) - 1]
279 path = path[:-len(discarded) - 1]
280
280
281 parts = [name]
281 parts = [name]
282 if 'PATH_INFO' in req.env:
282 if 'PATH_INFO' in req.env:
283 parts.insert(0, req.env['PATH_INFO'].rstrip('/'))
283 parts.insert(0, req.env['PATH_INFO'].rstrip('/'))
284 if req.env['SCRIPT_NAME']:
284 if req.env['SCRIPT_NAME']:
285 parts.insert(0, req.env['SCRIPT_NAME'])
285 parts.insert(0, req.env['SCRIPT_NAME'])
286 url = re.sub(r'/+', '/', '/'.join(parts) + '/')
286 url = re.sub(r'/+', '/', '/'.join(parts) + '/')
287
287
288 # show either a directory entry or a repository
288 # show either a directory entry or a repository
289 if directory:
289 if directory:
290 # get the directory's time information
290 # get the directory's time information
291 try:
291 try:
292 d = (get_mtime(path), util.makedate()[1])
292 d = (get_mtime(path), util.makedate()[1])
293 except OSError:
293 except OSError:
294 continue
294 continue
295
295
296 # add '/' to the name to make it obvious that
296 # add '/' to the name to make it obvious that
297 # the entry is a directory, not a regular repository
297 # the entry is a directory, not a regular repository
298 row = dict(contact="",
298 row = dict(contact="",
299 contact_sort="",
299 contact_sort="",
300 name=name + '/',
300 name=name + '/',
301 name_sort=name,
301 name_sort=name,
302 url=url,
302 url=url,
303 description="",
303 description="",
304 description_sort="",
304 description_sort="",
305 lastchange=d,
305 lastchange=d,
306 lastchange_sort=d[1]-d[0],
306 lastchange_sort=d[1]-d[0],
307 archives=[])
307 archives=[],
308 isdirectory=True)
308
309
309 seendirs.add(name)
310 seendirs.add(name)
310 yield row
311 yield row
311 continue
312 continue
312
313
313 u = self.ui.copy()
314 u = self.ui.copy()
314 try:
315 try:
315 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
316 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
316 except Exception, e:
317 except Exception, e:
317 u.warn(_('error reading %s/.hg/hgrc: %s\n') % (path, e))
318 u.warn(_('error reading %s/.hg/hgrc: %s\n') % (path, e))
318 continue
319 continue
319 def get(section, name, default=None):
320 def get(section, name, default=None):
320 return u.config(section, name, default, untrusted=True)
321 return u.config(section, name, default, untrusted=True)
321
322
322 if u.configbool("web", "hidden", untrusted=True):
323 if u.configbool("web", "hidden", untrusted=True):
323 continue
324 continue
324
325
325 if not self.read_allowed(u, req):
326 if not self.read_allowed(u, req):
326 continue
327 continue
327
328
328 # update time with local timezone
329 # update time with local timezone
329 try:
330 try:
330 r = hg.repository(self.ui, path)
331 r = hg.repository(self.ui, path)
331 except IOError:
332 except IOError:
332 u.warn(_('error accessing repository at %s\n') % path)
333 u.warn(_('error accessing repository at %s\n') % path)
333 continue
334 continue
334 except error.RepoError:
335 except error.RepoError:
335 u.warn(_('error accessing repository at %s\n') % path)
336 u.warn(_('error accessing repository at %s\n') % path)
336 continue
337 continue
337 try:
338 try:
338 d = (get_mtime(r.spath), util.makedate()[1])
339 d = (get_mtime(r.spath), util.makedate()[1])
339 except OSError:
340 except OSError:
340 continue
341 continue
341
342
342 contact = get_contact(get)
343 contact = get_contact(get)
343 description = get("web", "description", "")
344 description = get("web", "description", "")
344 name = get("web", "name", name)
345 name = get("web", "name", name)
345 row = dict(contact=contact or "unknown",
346 row = dict(contact=contact or "unknown",
346 contact_sort=contact.upper() or "unknown",
347 contact_sort=contact.upper() or "unknown",
347 name=name,
348 name=name,
348 name_sort=name,
349 name_sort=name,
349 url=url,
350 url=url,
350 description=description or "unknown",
351 description=description or "unknown",
351 description_sort=description.upper() or "unknown",
352 description_sort=description.upper() or "unknown",
352 lastchange=d,
353 lastchange=d,
353 lastchange_sort=d[1]-d[0],
354 lastchange_sort=d[1]-d[0],
354 archives=archivelist(u, "tip", url))
355 archives=archivelist(u, "tip", url))
355
356
356 seenrepos.add(name)
357 seenrepos.add(name)
357 yield row
358 yield row
358
359
359 sortdefault = None, False
360 sortdefault = None, False
360 def entries(sortcolumn="", descending=False, subdir="", **map):
361 def entries(sortcolumn="", descending=False, subdir="", **map):
361 rows = rawentries(subdir=subdir, **map)
362 rows = rawentries(subdir=subdir, **map)
362
363
363 if sortcolumn and sortdefault != (sortcolumn, descending):
364 if sortcolumn and sortdefault != (sortcolumn, descending):
364 sortkey = '%s_sort' % sortcolumn
365 sortkey = '%s_sort' % sortcolumn
365 rows = sorted(rows, key=lambda x: x[sortkey],
366 rows = sorted(rows, key=lambda x: x[sortkey],
366 reverse=descending)
367 reverse=descending)
367 for row, parity in zip(rows, paritygen(self.stripecount)):
368 for row, parity in zip(rows, paritygen(self.stripecount)):
368 row['parity'] = parity
369 row['parity'] = parity
369 yield row
370 yield row
370
371
371 self.refresh()
372 self.refresh()
372 sortable = ["name", "description", "contact", "lastchange"]
373 sortable = ["name", "description", "contact", "lastchange"]
373 sortcolumn, descending = sortdefault
374 sortcolumn, descending = sortdefault
374 if 'sort' in req.form:
375 if 'sort' in req.form:
375 sortcolumn = req.form['sort'][0]
376 sortcolumn = req.form['sort'][0]
376 descending = sortcolumn.startswith('-')
377 descending = sortcolumn.startswith('-')
377 if descending:
378 if descending:
378 sortcolumn = sortcolumn[1:]
379 sortcolumn = sortcolumn[1:]
379 if sortcolumn not in sortable:
380 if sortcolumn not in sortable:
380 sortcolumn = ""
381 sortcolumn = ""
381
382
382 sort = [("sort_%s" % column,
383 sort = [("sort_%s" % column,
383 "%s%s" % ((not descending and column == sortcolumn)
384 "%s%s" % ((not descending and column == sortcolumn)
384 and "-" or "", column))
385 and "-" or "", column))
385 for column in sortable]
386 for column in sortable]
386
387
387 self.refresh()
388 self.refresh()
388 self.updatereqenv(req.env)
389 self.updatereqenv(req.env)
389
390
390 return tmpl("index", entries=entries, subdir=subdir,
391 return tmpl("index", entries=entries, subdir=subdir,
391 sortcolumn=sortcolumn, descending=descending,
392 sortcolumn=sortcolumn, descending=descending,
392 **dict(sort))
393 **dict(sort))
393
394
394 def templater(self, req):
395 def templater(self, req):
395
396
396 def header(**map):
397 def header(**map):
397 yield tmpl('header', encoding=encoding.encoding, **map)
398 yield tmpl('header', encoding=encoding.encoding, **map)
398
399
399 def footer(**map):
400 def footer(**map):
400 yield tmpl("footer", **map)
401 yield tmpl("footer", **map)
401
402
402 def motd(**map):
403 def motd(**map):
403 if self.motd is not None:
404 if self.motd is not None:
404 yield self.motd
405 yield self.motd
405 else:
406 else:
406 yield config('web', 'motd', '')
407 yield config('web', 'motd', '')
407
408
408 def config(section, name, default=None, untrusted=True):
409 def config(section, name, default=None, untrusted=True):
409 return self.ui.config(section, name, default, untrusted)
410 return self.ui.config(section, name, default, untrusted)
410
411
411 self.updatereqenv(req.env)
412 self.updatereqenv(req.env)
412
413
413 url = req.env.get('SCRIPT_NAME', '')
414 url = req.env.get('SCRIPT_NAME', '')
414 if not url.endswith('/'):
415 if not url.endswith('/'):
415 url += '/'
416 url += '/'
416
417
417 vars = {}
418 vars = {}
418 styles = (
419 styles = (
419 req.form.get('style', [None])[0],
420 req.form.get('style', [None])[0],
420 config('web', 'style'),
421 config('web', 'style'),
421 'paper'
422 'paper'
422 )
423 )
423 style, mapfile = templater.stylemap(styles, self.templatepath)
424 style, mapfile = templater.stylemap(styles, self.templatepath)
424 if style == styles[0]:
425 if style == styles[0]:
425 vars['style'] = style
426 vars['style'] = style
426
427
427 start = url[-1] == '?' and '&' or '?'
428 start = url[-1] == '?' and '&' or '?'
428 sessionvars = webutil.sessionvars(vars, start)
429 sessionvars = webutil.sessionvars(vars, start)
429 logourl = config('web', 'logourl', 'http://mercurial.selenic.com/')
430 logourl = config('web', 'logourl', 'http://mercurial.selenic.com/')
430 logoimg = config('web', 'logoimg', 'hglogo.png')
431 logoimg = config('web', 'logoimg', 'hglogo.png')
431 staticurl = config('web', 'staticurl') or url + 'static/'
432 staticurl = config('web', 'staticurl') or url + 'static/'
432 if not staticurl.endswith('/'):
433 if not staticurl.endswith('/'):
433 staticurl += '/'
434 staticurl += '/'
434
435
435 tmpl = templater.templater(mapfile,
436 tmpl = templater.templater(mapfile,
436 defaults={"header": header,
437 defaults={"header": header,
437 "footer": footer,
438 "footer": footer,
438 "motd": motd,
439 "motd": motd,
439 "url": url,
440 "url": url,
440 "logourl": logourl,
441 "logourl": logourl,
441 "logoimg": logoimg,
442 "logoimg": logoimg,
442 "staticurl": staticurl,
443 "staticurl": staticurl,
443 "sessionvars": sessionvars})
444 "sessionvars": sessionvars})
444 return tmpl
445 return tmpl
445
446
446 def updatereqenv(self, env):
447 def updatereqenv(self, env):
447 if self._baseurl is not None:
448 if self._baseurl is not None:
448 name, port, path = geturlcgivars(self._baseurl, env['SERVER_PORT'])
449 name, port, path = geturlcgivars(self._baseurl, env['SERVER_PORT'])
449 env['SERVER_NAME'] = name
450 env['SERVER_NAME'] = name
450 env['SERVER_PORT'] = port
451 env['SERVER_PORT'] = port
451 env['SCRIPT_NAME'] = path
452 env['SCRIPT_NAME'] = path
@@ -1,302 +1,307
1 default = 'summary'
1 default = 'summary'
2 mimetype = 'text/html; charset={encoding}'
2 mimetype = 'text/html; charset={encoding}'
3 header = header.tmpl
3 header = header.tmpl
4 footer = footer.tmpl
4 footer = footer.tmpl
5 search = search.tmpl
5 search = search.tmpl
6 changelog = changelog.tmpl
6 changelog = changelog.tmpl
7 summary = summary.tmpl
7 summary = summary.tmpl
8 error = error.tmpl
8 error = error.tmpl
9 notfound = notfound.tmpl
9 notfound = notfound.tmpl
10
10
11 help = help.tmpl
11 help = help.tmpl
12 helptopics = helptopics.tmpl
12 helptopics = helptopics.tmpl
13
13
14 helpentry = '<tr><td><a href="{url}help/{topic|escape}{sessionvars%urlparameter}">{topic|escape}</a></td><td>{summary|escape}</td></tr>'
14 helpentry = '<tr><td><a href="{url}help/{topic|escape}{sessionvars%urlparameter}">{topic|escape}</a></td><td>{summary|escape}</td></tr>'
15
15
16 naventry = '<a href="{url}log/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
16 naventry = '<a href="{url}log/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
17 navshortentry = '<a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
17 navshortentry = '<a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
18 navgraphentry = '<a href="{url}graph/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
18 navgraphentry = '<a href="{url}graph/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
19 filenaventry = '<a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{label|escape}</a> '
19 filenaventry = '<a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{label|escape}</a> '
20 filedifflink = '<a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a> '
20 filedifflink = '<a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a> '
21 filenodelink = '
21 filenodelink = '
22 <tr class="parity{parity}">
22 <tr class="parity{parity}">
23 <td><a class="list" href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a></td>
23 <td><a class="list" href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a></td>
24 <td></td>
24 <td></td>
25 <td class="link">
25 <td class="link">
26 <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> |
26 <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> |
27 <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a> |
27 <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a> |
28 <a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
28 <a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
29 <a href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a> |
29 <a href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a> |
30 <a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a>
30 <a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a>
31 </td>
31 </td>
32 </tr>'
32 </tr>'
33 filenolink = '
33 filenolink = '
34 <tr class="parity{parity}">
34 <tr class="parity{parity}">
35 <td><a class="list" href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a></td>
35 <td><a class="list" href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a></td>
36 <td></td>
36 <td></td>
37 <td class="link">
37 <td class="link">
38 file |
38 file |
39 annotate |
39 annotate |
40 <a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
40 <a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
41 <a href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a> |
41 <a href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a> |
42 <a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a>
42 <a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a>
43 </td>
43 </td>
44 </tr>'
44 </tr>'
45
45
46 nav = '{before%naventry} {after%naventry}'
46 nav = '{before%naventry} {after%naventry}'
47 navshort = '{before%navshortentry}{after%navshortentry}'
47 navshort = '{before%navshortentry}{after%navshortentry}'
48 navgraph = '{before%navgraphentry}{after%navgraphentry}'
48 navgraph = '{before%navgraphentry}{after%navgraphentry}'
49 filenav = '{before%filenaventry}{after%filenaventry}'
49 filenav = '{before%filenaventry}{after%filenaventry}'
50
50
51 fileellipses = '...'
51 fileellipses = '...'
52 changelogentry = changelogentry.tmpl
52 changelogentry = changelogentry.tmpl
53 searchentry = changelogentry.tmpl
53 searchentry = changelogentry.tmpl
54 changeset = changeset.tmpl
54 changeset = changeset.tmpl
55 manifest = manifest.tmpl
55 manifest = manifest.tmpl
56 direntry = '
56 direntry = '
57 <tr class="parity{parity}">
57 <tr class="parity{parity}">
58 <td style="font-family:monospace">drwxr-xr-x</td>
58 <td style="font-family:monospace">drwxr-xr-x</td>
59 <td style="font-family:monospace"></td>
59 <td style="font-family:monospace"></td>
60 <td style="font-family:monospace"></td>
60 <td style="font-family:monospace"></td>
61 <td>
61 <td>
62 <a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">{basename|escape}</a>
62 <a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">{basename|escape}</a>
63 <a href="{url}file/{node|short}{path|urlescape}/{emptydirs|urlescape}{sessionvars%urlparameter}">{emptydirs|escape}</a>
63 <a href="{url}file/{node|short}{path|urlescape}/{emptydirs|urlescape}{sessionvars%urlparameter}">{emptydirs|escape}</a>
64 </td>
64 </td>
65 <td class="link">
65 <td class="link">
66 <a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">files</a>
66 <a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">files</a>
67 </td>
67 </td>
68 </tr>'
68 </tr>'
69 fileentry = '
69 fileentry = '
70 <tr class="parity{parity}">
70 <tr class="parity{parity}">
71 <td style="font-family:monospace">{permissions|permissions}</td>
71 <td style="font-family:monospace">{permissions|permissions}</td>
72 <td style="font-family:monospace" align=right>{date|isodate}</td>
72 <td style="font-family:monospace" align=right>{date|isodate}</td>
73 <td style="font-family:monospace" align=right>{size}</td>
73 <td style="font-family:monospace" align=right>{size}</td>
74 <td class="list">
74 <td class="list">
75 <a class="list" href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{basename|escape}</a>
75 <a class="list" href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{basename|escape}</a>
76 </td>
76 </td>
77 <td class="link">
77 <td class="link">
78 <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> |
78 <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> |
79 <a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a> |
79 <a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a> |
80 <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a>
80 <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a>
81 </td>
81 </td>
82 </tr>'
82 </tr>'
83 filerevision = filerevision.tmpl
83 filerevision = filerevision.tmpl
84 fileannotate = fileannotate.tmpl
84 fileannotate = fileannotate.tmpl
85 filediff = filediff.tmpl
85 filediff = filediff.tmpl
86 filecomparison = filecomparison.tmpl
86 filecomparison = filecomparison.tmpl
87 filelog = filelog.tmpl
87 filelog = filelog.tmpl
88 fileline = '
88 fileline = '
89 <div style="font-family:monospace" class="parity{parity}">
89 <div style="font-family:monospace" class="parity{parity}">
90 <pre><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</pre>
90 <pre><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</pre>
91 </div>'
91 </div>'
92 annotateline = '
92 annotateline = '
93 <tr style="font-family:monospace" class="parity{parity}">
93 <tr style="font-family:monospace" class="parity{parity}">
94 <td class="linenr" style="text-align: right;">
94 <td class="linenr" style="text-align: right;">
95 <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#l{targetline}"
95 <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#l{targetline}"
96 title="{node|short}: {desc|escape|firstline}">{author|user}@{rev}</a>
96 title="{node|short}: {desc|escape|firstline}">{author|user}@{rev}</a>
97 </td>
97 </td>
98 <td><pre><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a></pre></td>
98 <td><pre><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a></pre></td>
99 <td><pre>{line|escape}</pre></td>
99 <td><pre>{line|escape}</pre></td>
100 </tr>'
100 </tr>'
101 difflineplus = '<span style="color:#008800;"><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>'
101 difflineplus = '<span style="color:#008800;"><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>'
102 difflineminus = '<span style="color:#cc0000;"><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>'
102 difflineminus = '<span style="color:#cc0000;"><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>'
103 difflineat = '<span style="color:#990099;"><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>'
103 difflineat = '<span style="color:#990099;"><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>'
104 diffline = '<span><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>'
104 diffline = '<span><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>'
105
105
106 comparisonblock ='
106 comparisonblock ='
107 <tbody class="block">
107 <tbody class="block">
108 {lines}
108 {lines}
109 </tbody>'
109 </tbody>'
110 comparisonline = '
110 comparisonline = '
111 <tr style="font-family:monospace">
111 <tr style="font-family:monospace">
112 <td class="{type}"><pre><a class="linenr" href="#{lineid}" id="{lineid}">{leftlinenumber}</a> {leftline|escape}</pre></td>
112 <td class="{type}"><pre><a class="linenr" href="#{lineid}" id="{lineid}">{leftlinenumber}</a> {leftline|escape}</pre></td>
113 <td class="{type}"><pre><a class="linenr" href="#{lineid}" id="{lineid}">{rightlinenumber}</a> {rightline|escape}</pre></td>
113 <td class="{type}"><pre><a class="linenr" href="#{lineid}" id="{lineid}">{rightlinenumber}</a> {rightline|escape}</pre></td>
114 </tr>'
114 </tr>'
115
115
116 changelogparent = '
116 changelogparent = '
117 <tr>
117 <tr>
118 <th class="parent">parent {rev}:</th>
118 <th class="parent">parent {rev}:</th>
119 <td class="parent">
119 <td class="parent">
120 <a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>
120 <a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>
121 </td>
121 </td>
122 </tr>'
122 </tr>'
123 changesetbranch = '<tr><td>branch</td><td>{name}</td></tr>'
123 changesetbranch = '<tr><td>branch</td><td>{name}</td></tr>'
124 changesetparent = '
124 changesetparent = '
125 <tr>
125 <tr>
126 <td>parent {rev}</td>
126 <td>parent {rev}</td>
127 <td style="font-family:monospace">
127 <td style="font-family:monospace">
128 <a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>
128 <a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>
129 </td>
129 </td>
130 </tr>'
130 </tr>'
131 filerevbranch = '<tr><td>branch</td><td>{name}</td></tr>'
131 filerevbranch = '<tr><td>branch</td><td>{name}</td></tr>'
132 filerevparent = '
132 filerevparent = '
133 <tr>
133 <tr>
134 <td>parent {rev}</td>
134 <td>parent {rev}</td>
135 <td style="font-family:monospace">
135 <td style="font-family:monospace">
136 <a class="list" href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
136 <a class="list" href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
137 {rename%filerename}{node|short}
137 {rename%filerename}{node|short}
138 </a>
138 </a>
139 </td>
139 </td>
140 </tr>'
140 </tr>'
141 filerename = '{file|escape}@'
141 filerename = '{file|escape}@'
142 filelogrename = '| <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">base</a>'
142 filelogrename = '| <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">base</a>'
143 fileannotateparent = '
143 fileannotateparent = '
144 <tr>
144 <tr>
145 <td>parent {rev}</td>
145 <td>parent {rev}</td>
146 <td style="font-family:monospace">
146 <td style="font-family:monospace">
147 <a class="list" href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
147 <a class="list" href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
148 {rename%filerename}{node|short}
148 {rename%filerename}{node|short}
149 </a>
149 </a>
150 </td>
150 </td>
151 </tr>'
151 </tr>'
152 changelogchild = '
152 changelogchild = '
153 <tr>
153 <tr>
154 <th class="child">child {rev}:</th>
154 <th class="child">child {rev}:</th>
155 <td class="child"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
155 <td class="child"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
156 </tr>'
156 </tr>'
157 changesetchild = '
157 changesetchild = '
158 <tr>
158 <tr>
159 <td>child {rev}</td>
159 <td>child {rev}</td>
160 <td style="font-family:monospace">
160 <td style="font-family:monospace">
161 <a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>
161 <a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>
162 </td>
162 </td>
163 </tr>'
163 </tr>'
164 filerevchild = '
164 filerevchild = '
165 <tr>
165 <tr>
166 <td>child {rev}</td>
166 <td>child {rev}</td>
167 <td style="font-family:monospace">
167 <td style="font-family:monospace">
168 <a class="list" href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td>
168 <a class="list" href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td>
169 </tr>'
169 </tr>'
170 fileannotatechild = '
170 fileannotatechild = '
171 <tr>
171 <tr>
172 <td>child {rev}</td>
172 <td>child {rev}</td>
173 <td style="font-family:monospace">
173 <td style="font-family:monospace">
174 <a class="list" href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td>
174 <a class="list" href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td>
175 </tr>'
175 </tr>'
176 tags = tags.tmpl
176 tags = tags.tmpl
177 tagentry = '
177 tagentry = '
178 <tr class="parity{parity}">
178 <tr class="parity{parity}">
179 <td class="age"><i class="age">{date|rfc822date}</i></td>
179 <td class="age"><i class="age">{date|rfc822date}</i></td>
180 <td><a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}"><b>{tag|escape}</b></a></td>
180 <td><a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}"><b>{tag|escape}</b></a></td>
181 <td class="link">
181 <td class="link">
182 <a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
182 <a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
183 <a href="{url}log/{node|short}{sessionvars%urlparameter}">changelog</a> |
183 <a href="{url}log/{node|short}{sessionvars%urlparameter}">changelog</a> |
184 <a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>
184 <a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>
185 </td>
185 </td>
186 </tr>'
186 </tr>'
187 bookmarks = bookmarks.tmpl
187 bookmarks = bookmarks.tmpl
188 bookmarkentry = '
188 bookmarkentry = '
189 <tr class="parity{parity}">
189 <tr class="parity{parity}">
190 <td class="age"><i class="age">{date|rfc822date}</i></td>
190 <td class="age"><i class="age">{date|rfc822date}</i></td>
191 <td><a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}"><b>{bookmark|escape}</b></a></td>
191 <td><a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}"><b>{bookmark|escape}</b></a></td>
192 <td class="link">
192 <td class="link">
193 <a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
193 <a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
194 <a href="{url}log/{node|short}{sessionvars%urlparameter}">changelog</a> |
194 <a href="{url}log/{node|short}{sessionvars%urlparameter}">changelog</a> |
195 <a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>
195 <a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>
196 </td>
196 </td>
197 </tr>'
197 </tr>'
198 branches = branches.tmpl
198 branches = branches.tmpl
199 branchentry = '
199 branchentry = '
200 <tr class="parity{parity}">
200 <tr class="parity{parity}">
201 <td class="age"><i class="age">{date|rfc822date}</i></td>
201 <td class="age"><i class="age">{date|rfc822date}</i></td>
202 <td><a class="list" href="{url}shortlog/{node|short}{sessionvars%urlparameter}"><b>{node|short}</b></a></td>
202 <td><a class="list" href="{url}shortlog/{node|short}{sessionvars%urlparameter}"><b>{node|short}</b></a></td>
203 <td class="{status}">{branch|escape}</td>
203 <td class="{status}">{branch|escape}</td>
204 <td class="link">
204 <td class="link">
205 <a href="{url}changeset/{node|short}{sessionvars%urlparameter}">changeset</a> |
205 <a href="{url}changeset/{node|short}{sessionvars%urlparameter}">changeset</a> |
206 <a href="{url}log/{node|short}{sessionvars%urlparameter}">changelog</a> |
206 <a href="{url}log/{node|short}{sessionvars%urlparameter}">changelog</a> |
207 <a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>
207 <a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>
208 </td>
208 </td>
209 </tr>'
209 </tr>'
210 diffblock = '<pre>{lines}</pre>'
210 diffblock = '<pre>{lines}</pre>'
211 filediffparent = '
211 filediffparent = '
212 <tr>
212 <tr>
213 <td>parent {rev}</td>
213 <td>parent {rev}</td>
214 <td style="font-family:monospace">
214 <td style="font-family:monospace">
215 <a class="list" href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
215 <a class="list" href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
216 {node|short}
216 {node|short}
217 </a>
217 </a>
218 </td>
218 </td>
219 </tr>'
219 </tr>'
220 filecompparent = '
220 filecompparent = '
221 <tr>
221 <tr>
222 <td>parent {rev}</td>
222 <td>parent {rev}</td>
223 <td style="font-family:monospace">
223 <td style="font-family:monospace">
224 <a class="list" href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
224 <a class="list" href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
225 {node|short}
225 {node|short}
226 </a>
226 </a>
227 </td>
227 </td>
228 </tr>'
228 </tr>'
229 filelogparent = '
229 filelogparent = '
230 <tr>
230 <tr>
231 <td align="right">parent {rev}:&nbsp;</td>
231 <td align="right">parent {rev}:&nbsp;</td>
232 <td><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td>
232 <td><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td>
233 </tr>'
233 </tr>'
234 filediffchild = '
234 filediffchild = '
235 <tr>
235 <tr>
236 <td>child {rev}</td>
236 <td>child {rev}</td>
237 <td style="font-family:monospace">
237 <td style="font-family:monospace">
238 <a class="list" href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a>
238 <a class="list" href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a>
239 </td>
239 </td>
240 </tr>'
240 </tr>'
241 filecompchild = '
241 filecompchild = '
242 <tr>
242 <tr>
243 <td>child {rev}</td>
243 <td>child {rev}</td>
244 <td style="font-family:monospace">
244 <td style="font-family:monospace">
245 <a class="list" href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a>
245 <a class="list" href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a>
246 </td>
246 </td>
247 </tr>'
247 </tr>'
248 filelogchild = '
248 filelogchild = '
249 <tr>
249 <tr>
250 <td align="right">child {rev}:&nbsp;</td>
250 <td align="right">child {rev}:&nbsp;</td>
251 <td><a href="{url}file{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td>
251 <td><a href="{url}file{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td>
252 </tr>'
252 </tr>'
253 shortlog = shortlog.tmpl
253 shortlog = shortlog.tmpl
254 graph = graph.tmpl
254 graph = graph.tmpl
255 tagtag = '<span class="tagtag" title="{name}">{name}</span> '
255 tagtag = '<span class="tagtag" title="{name}">{name}</span> '
256 branchtag = '<span class="branchtag" title="{name}">{name}</span> '
256 branchtag = '<span class="branchtag" title="{name}">{name}</span> '
257 inbranchtag = '<span class="inbranchtag" title="{name}">{name}</span> '
257 inbranchtag = '<span class="inbranchtag" title="{name}">{name}</span> '
258 bookmarktag = '<span class="bookmarktag" title="{name}">{name}</span> '
258 bookmarktag = '<span class="bookmarktag" title="{name}">{name}</span> '
259 shortlogentry = '
259 shortlogentry = '
260 <tr class="parity{parity}">
260 <tr class="parity{parity}">
261 <td class="age"><i class="age">{date|rfc822date}</i></td>
261 <td class="age"><i class="age">{date|rfc822date}</i></td>
262 <td><i>{author|person}</i></td>
262 <td><i>{author|person}</i></td>
263 <td>
263 <td>
264 <a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}">
264 <a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}">
265 <b>{desc|strip|firstline|escape|nonempty}</b>
265 <b>{desc|strip|firstline|escape|nonempty}</b>
266 <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}{bookmarks%bookmarktag}</span>
266 <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}{bookmarks%bookmarktag}</span>
267 </a>
267 </a>
268 </td>
268 </td>
269 <td class="link" nowrap>
269 <td class="link" nowrap>
270 <a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
270 <a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
271 <a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>
271 <a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>
272 </td>
272 </td>
273 </tr>'
273 </tr>'
274 filelogentry = '
274 filelogentry = '
275 <tr class="parity{parity}">
275 <tr class="parity{parity}">
276 <td class="age"><i class="age">{date|rfc822date}</i></td>
276 <td class="age"><i class="age">{date|rfc822date}</i></td>
277 <td>
277 <td>
278 <a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}">
278 <a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}">
279 <b>{desc|strip|firstline|escape|nonempty}</b>
279 <b>{desc|strip|firstline|escape|nonempty}</b>
280 </a>
280 </a>
281 </td>
281 </td>
282 <td class="link">
282 <td class="link">
283 <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a>&nbsp;|&nbsp;<a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a>&nbsp;|&nbsp;<a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a> {rename%filelogrename}</td>
283 <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a>&nbsp;|&nbsp;<a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a>&nbsp;|&nbsp;<a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a> {rename%filelogrename}</td>
284 </tr>'
284 </tr>'
285 archiveentry = ' | <a href="{url}archive/{node|short}{extension}">{type|escape}</a> '
285 archiveentry = ' | <a href="{url}archive/{node|short}{extension}">{type|escape}</a> '
286 indexentry = '
286 indexentry = '
287 <tr class="parity{parity}">
287 <tr class="parity{parity}">
288 <td>
288 <td>
289 <a class="list" href="{url}{sessionvars%urlparameter}">
289 <a class="list" href="{url}{sessionvars%urlparameter}">
290 <b>{name|escape}</b>
290 <b>{name|escape}</b>
291 </a>
291 </a>
292 </td>
292 </td>
293 <td>{description}</td>
293 <td>{description}</td>
294 <td>{contact|obfuscate}</td>
294 <td>{contact|obfuscate}</td>
295 <td class="age">{lastchange|rfc822date}</td>
295 <td class="age">{lastchange|rfc822date}</td>
296 <td class="indexlinks">{archives%indexarchiveentry}</td>
296 <td class="indexlinks">{archives%indexarchiveentry}</td>
297 <td><div class="rss_logo"><a href="{url}rss-log">RSS</a> <a href="{url}atom-log">Atom</a></div></td>
297 <td>{if(isdirectory, '',
298 '<div class="rss_logo">
299 <a href="{url}rss-log">RSS</a> <a href="{url}atom-log">Atom</a>
300 </div>'
301 )}
302 </td>
298 </tr>\n'
303 </tr>\n'
299 indexarchiveentry = ' <a href="{url}archive/{node|short}{extension}">{type|escape}</a> '
304 indexarchiveentry = ' <a href="{url}archive/{node|short}{extension}">{type|escape}</a> '
300 index = index.tmpl
305 index = index.tmpl
301 urlparameter = '{separator}{name}={value|urlescape}'
306 urlparameter = '{separator}{name}={value|urlescape}'
302 hiddenformentry = '<input type="hidden" name="{name}" value="{value|escape}" />'
307 hiddenformentry = '<input type="hidden" name="{name}" value="{value|escape}" />'
@@ -1,260 +1,261
1 default = 'summary'
1 default = 'summary'
2 mimetype = 'text/html; charset={encoding}'
2 mimetype = 'text/html; charset={encoding}'
3 header = header.tmpl
3 header = header.tmpl
4 footer = footer.tmpl
4 footer = footer.tmpl
5 search = search.tmpl
5 search = search.tmpl
6 changelog = changelog.tmpl
6 changelog = changelog.tmpl
7 summary = summary.tmpl
7 summary = summary.tmpl
8 error = error.tmpl
8 error = error.tmpl
9 notfound = notfound.tmpl
9 notfound = notfound.tmpl
10
10
11 help = help.tmpl
11 help = help.tmpl
12 helptopics = helptopics.tmpl
12 helptopics = helptopics.tmpl
13
13
14 helpentry = '<tr><td><a href="{url}help/{topic|escape}{sessionvars%urlparameter}">{topic|escape}</a></td><td>{summary|escape}</td></tr>'
14 helpentry = '<tr><td><a href="{url}help/{topic|escape}{sessionvars%urlparameter}">{topic|escape}</a></td><td>{summary|escape}</td></tr>'
15
15
16 naventry = '<a href="{url}log/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
16 naventry = '<a href="{url}log/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
17 navshortentry = '<a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
17 navshortentry = '<a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
18 navgraphentry = '<a href="{url}graph/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
18 navgraphentry = '<a href="{url}graph/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
19 filenaventry = '<a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{label|escape}</a>'
19 filenaventry = '<a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{label|escape}</a>'
20 filedifflink = '<a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a> '
20 filedifflink = '<a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a> '
21 filenodelink = '
21 filenodelink = '
22 <tr class="parity{parity}">
22 <tr class="parity{parity}">
23 <td><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a></td>
23 <td><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a></td>
24 <td></td>
24 <td></td>
25 <td>
25 <td>
26 <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> |
26 <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> |
27 <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a> |
27 <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a> |
28 <a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
28 <a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
29 <a href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a> |
29 <a href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a> |
30 <a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a>
30 <a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a>
31 </td>
31 </td>
32 </tr>'
32 </tr>'
33 filenolink = '
33 filenolink = '
34 <tr class="parity{parity}">
34 <tr class="parity{parity}">
35 <td><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a></td>
35 <td><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a></td>
36 <td></td>
36 <td></td>
37 <td>
37 <td>
38 file |
38 file |
39 annotate |
39 annotate |
40 <a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
40 <a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
41 <a href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a> |
41 <a href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a> |
42 <a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a>
42 <a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a>
43 </td>
43 </td>
44 </tr>'
44 </tr>'
45
45
46 nav = '{before%naventry} {after%naventry}'
46 nav = '{before%naventry} {after%naventry}'
47 navshort = '{before%navshortentry}{after%navshortentry}'
47 navshort = '{before%navshortentry}{after%navshortentry}'
48 navgraph = '{before%navgraphentry}{after%navgraphentry}'
48 navgraph = '{before%navgraphentry}{after%navgraphentry}'
49 filenav = '{before%filenaventry}{after%filenaventry}'
49 filenav = '{before%filenaventry}{after%filenaventry}'
50
50
51 fileellipses = '...'
51 fileellipses = '...'
52 changelogentry = changelogentry.tmpl
52 changelogentry = changelogentry.tmpl
53 searchentry = changelogentry.tmpl
53 searchentry = changelogentry.tmpl
54 changeset = changeset.tmpl
54 changeset = changeset.tmpl
55 manifest = manifest.tmpl
55 manifest = manifest.tmpl
56 direntry = '
56 direntry = '
57 <tr class="parity{parity}">
57 <tr class="parity{parity}">
58 <td>drwxr-xr-x</td>
58 <td>drwxr-xr-x</td>
59 <td></td>
59 <td></td>
60 <td></td>
60 <td></td>
61 <td><a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">{basename|escape}</a></td>
61 <td><a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">{basename|escape}</a></td>
62 <td><a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">files</a></td>
62 <td><a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">files</a></td>
63 </tr>'
63 </tr>'
64 fileentry = '
64 fileentry = '
65 <tr class="parity{parity}">
65 <tr class="parity{parity}">
66 <td>{permissions|permissions}</td>
66 <td>{permissions|permissions}</td>
67 <td>{date|isodate}</td>
67 <td>{date|isodate}</td>
68 <td>{size}</td>
68 <td>{size}</td>
69 <td><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{basename|escape}</a></td>
69 <td><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{basename|escape}</a></td>
70 <td>
70 <td>
71 <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> |
71 <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> |
72 <a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a> |
72 <a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a> |
73 <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a>
73 <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a>
74 </td>
74 </td>
75 </tr>'
75 </tr>'
76 filerevision = filerevision.tmpl
76 filerevision = filerevision.tmpl
77 fileannotate = fileannotate.tmpl
77 fileannotate = fileannotate.tmpl
78 filediff = filediff.tmpl
78 filediff = filediff.tmpl
79 filecomparison = filecomparison.tmpl
79 filecomparison = filecomparison.tmpl
80 filelog = filelog.tmpl
80 filelog = filelog.tmpl
81 fileline = '
81 fileline = '
82 <div style="font-family:monospace" class="parity{parity}">
82 <div style="font-family:monospace" class="parity{parity}">
83 <pre><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</pre>
83 <pre><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</pre>
84 </div>'
84 </div>'
85 annotateline = '
85 annotateline = '
86 <tr class="parity{parity}">
86 <tr class="parity{parity}">
87 <td class="linenr">
87 <td class="linenr">
88 <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#l{targetline}"
88 <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#l{targetline}"
89 title="{node|short}: {desc|escape|firstline}">{author|user}@{rev}</a>
89 title="{node|short}: {desc|escape|firstline}">{author|user}@{rev}</a>
90 </td>
90 </td>
91 <td class="lineno">
91 <td class="lineno">
92 <a href="#{lineid}" id="{lineid}">{linenumber}</a>
92 <a href="#{lineid}" id="{lineid}">{linenumber}</a>
93 </td>
93 </td>
94 <td class="source">{line|escape}</td>
94 <td class="source">{line|escape}</td>
95 </tr>'
95 </tr>'
96 difflineplus = '<span style="color:#008800;"><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>'
96 difflineplus = '<span style="color:#008800;"><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>'
97 difflineminus = '<span style="color:#cc0000;"><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>'
97 difflineminus = '<span style="color:#cc0000;"><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>'
98 difflineat = '<span style="color:#990099;"><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>'
98 difflineat = '<span style="color:#990099;"><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>'
99 diffline = '<span><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>'
99 diffline = '<span><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>'
100
100
101 comparisonblock ='
101 comparisonblock ='
102 <tbody class="block">
102 <tbody class="block">
103 {lines}
103 {lines}
104 </tbody>'
104 </tbody>'
105 comparisonline = '
105 comparisonline = '
106 <tr>
106 <tr>
107 <td class="source {type}"><a class="linenr" href="#{lineid}" id="{lineid}">{leftlinenumber}</a> {leftline|escape}</td>
107 <td class="source {type}"><a class="linenr" href="#{lineid}" id="{lineid}">{leftlinenumber}</a> {leftline|escape}</td>
108 <td class="source {type}"><a class="linenr" href="#{lineid}" id="{lineid}">{rightlinenumber}</a> {rightline|escape}</td>
108 <td class="source {type}"><a class="linenr" href="#{lineid}" id="{lineid}">{rightlinenumber}</a> {rightline|escape}</td>
109 </tr>'
109 </tr>'
110
110
111 changelogparent = '
111 changelogparent = '
112 <tr>
112 <tr>
113 <th class="parent">parent {rev}:</th>
113 <th class="parent">parent {rev}:</th>
114 <td class="parent">
114 <td class="parent">
115 <a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>
115 <a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>
116 </td>
116 </td>
117 </tr>'
117 </tr>'
118 changesetbranch = '<dt>branch</dt><dd>{name}</dd>'
118 changesetbranch = '<dt>branch</dt><dd>{name}</dd>'
119 changesetparent = '
119 changesetparent = '
120 <dt>parent {rev}</dt>
120 <dt>parent {rev}</dt>
121 <dd><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></dd>'
121 <dd><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></dd>'
122 filerevbranch = '<dt>branch</dt><dd>{name}</dd>'
122 filerevbranch = '<dt>branch</dt><dd>{name}</dd>'
123 filerevparent = '
123 filerevparent = '
124 <dt>parent {rev}</dt>
124 <dt>parent {rev}</dt>
125 <dd>
125 <dd>
126 <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
126 <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
127 {rename%filerename}{node|short}
127 {rename%filerename}{node|short}
128 </a>
128 </a>
129 </dd>'
129 </dd>'
130 filerename = '{file|escape}@'
130 filerename = '{file|escape}@'
131 filelogrename = '| <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">base</a>'
131 filelogrename = '| <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">base</a>'
132 fileannotateparent = '
132 fileannotateparent = '
133 <dt>parent {rev}</dt>
133 <dt>parent {rev}</dt>
134 <dd>
134 <dd>
135 <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
135 <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
136 {rename%filerename}{node|short}
136 {rename%filerename}{node|short}
137 </a>
137 </a>
138 </dd>'
138 </dd>'
139 changelogchild = '
139 changelogchild = '
140 <dt>child {rev}:</dt>
140 <dt>child {rev}:</dt>
141 <dd><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></dd>'
141 <dd><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></dd>'
142 changesetchild = '
142 changesetchild = '
143 <dt>child {rev}</dt>
143 <dt>child {rev}</dt>
144 <dd><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></dd>'
144 <dd><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></dd>'
145 filerevchild = '
145 filerevchild = '
146 <dt>child {rev}</dt>
146 <dt>child {rev}</dt>
147 <dd>
147 <dd>
148 <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a>
148 <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a>
149 </dd>'
149 </dd>'
150 fileannotatechild = '
150 fileannotatechild = '
151 <dt>child {rev}</dt>
151 <dt>child {rev}</dt>
152 <dd>
152 <dd>
153 <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a>
153 <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a>
154 </dd>'
154 </dd>'
155 tags = tags.tmpl
155 tags = tags.tmpl
156 tagentry = '
156 tagentry = '
157 <tr class="parity{parity}">
157 <tr class="parity{parity}">
158 <td class="nowrap age">{date|rfc822date}</td>
158 <td class="nowrap age">{date|rfc822date}</td>
159 <td><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{tag|escape}</a></td>
159 <td><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{tag|escape}</a></td>
160 <td class="nowrap">
160 <td class="nowrap">
161 <a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
161 <a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
162 <a href="{url}log/{node|short}{sessionvars%urlparameter}">changelog</a> |
162 <a href="{url}log/{node|short}{sessionvars%urlparameter}">changelog</a> |
163 <a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>
163 <a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>
164 </td>
164 </td>
165 </tr>'
165 </tr>'
166 bookmarks = bookmarks.tmpl
166 bookmarks = bookmarks.tmpl
167 bookmarkentry = '
167 bookmarkentry = '
168 <tr class="parity{parity}">
168 <tr class="parity{parity}">
169 <td class="nowrap date">{date|rfc822date}</td>
169 <td class="nowrap date">{date|rfc822date}</td>
170 <td><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{bookmark|escape}</a></td>
170 <td><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{bookmark|escape}</a></td>
171 <td class="nowrap">
171 <td class="nowrap">
172 <a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
172 <a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
173 <a href="{url}log/{node|short}{sessionvars%urlparameter}">changelog</a> |
173 <a href="{url}log/{node|short}{sessionvars%urlparameter}">changelog</a> |
174 <a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>
174 <a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>
175 </td>
175 </td>
176 </tr>'
176 </tr>'
177 branches = branches.tmpl
177 branches = branches.tmpl
178 branchentry = '
178 branchentry = '
179 <tr class="parity{parity}">
179 <tr class="parity{parity}">
180 <td class="nowrap age">{date|rfc822date}</td>
180 <td class="nowrap age">{date|rfc822date}</td>
181 <td><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
181 <td><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
182 <td class="{status}">{branch|escape}</td>
182 <td class="{status}">{branch|escape}</td>
183 <td class="nowrap">
183 <td class="nowrap">
184 <a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
184 <a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
185 <a href="{url}log/{node|short}{sessionvars%urlparameter}">changelog</a> |
185 <a href="{url}log/{node|short}{sessionvars%urlparameter}">changelog</a> |
186 <a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>
186 <a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>
187 </td>
187 </td>
188 </tr>'
188 </tr>'
189 diffblock = '<pre>{lines}</pre>'
189 diffblock = '<pre>{lines}</pre>'
190 filediffparent = '
190 filediffparent = '
191 <dt>parent {rev}</dt>
191 <dt>parent {rev}</dt>
192 <dd><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></dd>'
192 <dd><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></dd>'
193 filecompparent = '
193 filecompparent = '
194 <dt>parent {rev}</dt>
194 <dt>parent {rev}</dt>
195 <dd><a href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></dd>'
195 <dd><a href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></dd>'
196 filelogparent = '
196 filelogparent = '
197 <tr>
197 <tr>
198 <td align="right">parent {rev}:&nbsp;</td>
198 <td align="right">parent {rev}:&nbsp;</td>
199 <td><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td>
199 <td><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td>
200 </tr>'
200 </tr>'
201 filediffchild = '
201 filediffchild = '
202 <dt>child {rev}</dt>
202 <dt>child {rev}</dt>
203 <dd><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></dd>'
203 <dd><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></dd>'
204 filecompchild = '
204 filecompchild = '
205 <dt>child {rev}</dt>
205 <dt>child {rev}</dt>
206 <dd><a href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></dd>'
206 <dd><a href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></dd>'
207 filelogchild = '
207 filelogchild = '
208 <tr>
208 <tr>
209 <td align="right">child {rev}:&nbsp;</td>
209 <td align="right">child {rev}:&nbsp;</td>
210 <td><a href="{url}file{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td>
210 <td><a href="{url}file{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td>
211 </tr>'
211 </tr>'
212 shortlog = shortlog.tmpl
212 shortlog = shortlog.tmpl
213 tagtag = '<span class="tagtag" title="{name}">{name}</span> '
213 tagtag = '<span class="tagtag" title="{name}">{name}</span> '
214 branchtag = '<span class="branchtag" title="{name}">{name}</span> '
214 branchtag = '<span class="branchtag" title="{name}">{name}</span> '
215 inbranchtag = '<span class="inbranchtag" title="{name}">{name}</span> '
215 inbranchtag = '<span class="inbranchtag" title="{name}">{name}</span> '
216 bookmarktag = '<span class="bookmarktag" title="{name}">{name}</span> '
216 bookmarktag = '<span class="bookmarktag" title="{name}">{name}</span> '
217 shortlogentry = '
217 shortlogentry = '
218 <tr class="parity{parity}">
218 <tr class="parity{parity}">
219 <td class="nowrap age">{date|rfc822date}</td>
219 <td class="nowrap age">{date|rfc822date}</td>
220 <td>{author|person}</td>
220 <td>{author|person}</td>
221 <td>
221 <td>
222 <a href="{url}rev/{node|short}{sessionvars%urlparameter}">
222 <a href="{url}rev/{node|short}{sessionvars%urlparameter}">
223 {desc|strip|firstline|escape|nonempty}
223 {desc|strip|firstline|escape|nonempty}
224 <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}{bookmarks%bookmarktag}</span>
224 <span class="logtags">{inbranch%inbranchtag}{branches%branchtag}{tags%tagtag}{bookmarks%bookmarktag}</span>
225 </a>
225 </a>
226 </td>
226 </td>
227 <td class="nowrap">
227 <td class="nowrap">
228 <a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
228 <a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
229 <a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>
229 <a href="{url}file/{node|short}{sessionvars%urlparameter}">files</a>
230 </td>
230 </td>
231 </tr>'
231 </tr>'
232 filelogentry = '
232 filelogentry = '
233 <tr class="parity{parity}">
233 <tr class="parity{parity}">
234 <td class="nowrap age">{date|rfc822date}</td>
234 <td class="nowrap age">{date|rfc822date}</td>
235 <td><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{desc|strip|firstline|escape|nonempty}</a></td>
235 <td><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{desc|strip|firstline|escape|nonempty}</a></td>
236 <td class="nowrap">
236 <td class="nowrap">
237 <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a>&nbsp;|&nbsp;<a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a>&nbsp;|&nbsp;<a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a>
237 <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a>&nbsp;|&nbsp;<a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a>&nbsp;|&nbsp;<a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a>
238 {rename%filelogrename}
238 {rename%filelogrename}
239 </td>
239 </td>
240 </tr>'
240 </tr>'
241 archiveentry = '<li><a href="{url}archive/{node|short}{extension}">{type|escape}</a></li>'
241 archiveentry = '<li><a href="{url}archive/{node|short}{extension}">{type|escape}</a></li>'
242 indexentry = '
242 indexentry = '
243 <tr class="parity{parity}">
243 <tr class="parity{parity}">
244 <td><a href="{url}{sessionvars%urlparameter}">{name|escape}</a></td>
244 <td><a href="{url}{sessionvars%urlparameter}">{name|escape}</a></td>
245 <td>{description}</td>
245 <td>{description}</td>
246 <td>{contact|obfuscate}</td>
246 <td>{contact|obfuscate}</td>
247 <td class="age">{lastchange|rfc822date}</td>
247 <td class="age">{lastchange|rfc822date}</td>
248 <td class="indexlinks">{archives%indexarchiveentry}</td>
248 <td class="indexlinks">{archives%indexarchiveentry}</td>
249 <td>
249 <td>
250 <div class="rss_logo">
250 {if(isdirectory, '',
251 <a href="{url}rss-log">RSS</a>
251 '<div class="rss_logo">
252 <a href="{url}atom-log">Atom</a>
252 <a href="{url}rss-log">RSS</a> <a href="{url}atom-log">Atom</a>
253 </div>
253 </div>'
254 )}
254 </td>
255 </td>
255 </tr>\n'
256 </tr>\n'
256 indexarchiveentry = '<a href="{url}archive/{node|short}{extension}">{type|escape}</a> '
257 indexarchiveentry = '<a href="{url}archive/{node|short}{extension}">{type|escape}</a> '
257 index = index.tmpl
258 index = index.tmpl
258 urlparameter = '{separator}{name}={value|urlescape}'
259 urlparameter = '{separator}{name}={value|urlescape}'
259 hiddenformentry = '<input type="hidden" name="{name}" value="{value|escape}" />'
260 hiddenformentry = '<input type="hidden" name="{name}" value="{value|escape}" />'
260 graph = graph.tmpl
261 graph = graph.tmpl
General Comments 0
You need to be logged in to leave comments. Login now