##// END OF EJS Templates
Allow serving static files from hgwebdir to fix CSS and favicon.
Thomas Arendsen Hein -
r1793:83c6d835 default
parent child Browse files
Show More
@@ -1,1179 +1,1188 b''
1 # hgweb.py - web interface to a mercurial repository
1 # hgweb.py - web interface to a mercurial repository
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 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005 Matt Mackall <mpm@selenic.com>
5 #
5 #
6 # This software may be used and distributed according to the terms
6 # This software may be used and distributed according to the terms
7 # of the GNU General Public License, incorporated herein by reference.
7 # of the GNU General Public License, incorporated herein by reference.
8
8
9 import os, cgi, sys, urllib
9 import os, cgi, sys, urllib
10 import mimetypes
10 import mimetypes
11 from demandload import demandload
11 from demandload import demandload
12 demandload(globals(), "mdiff time re socket zlib errno ui hg ConfigParser")
12 demandload(globals(), "mdiff time re socket zlib errno ui hg ConfigParser")
13 demandload(globals(), "zipfile tempfile StringIO tarfile BaseHTTPServer util")
13 demandload(globals(), "zipfile tempfile StringIO tarfile BaseHTTPServer util")
14 demandload(globals(), "mimetypes")
14 demandload(globals(), "mimetypes")
15 from node import *
15 from node import *
16 from i18n import gettext as _
16 from i18n import gettext as _
17
17
18 def templatepath():
18 def templatepath():
19 for f in "templates", "../templates":
19 for f in "templates", "../templates":
20 p = os.path.join(os.path.dirname(__file__), f)
20 p = os.path.join(os.path.dirname(__file__), f)
21 if os.path.isdir(p):
21 if os.path.isdir(p):
22 return p
22 return p
23
23
24 def age(x):
24 def age(x):
25 def plural(t, c):
25 def plural(t, c):
26 if c == 1:
26 if c == 1:
27 return t
27 return t
28 return t + "s"
28 return t + "s"
29 def fmt(t, c):
29 def fmt(t, c):
30 return "%d %s" % (c, plural(t, c))
30 return "%d %s" % (c, plural(t, c))
31
31
32 now = time.time()
32 now = time.time()
33 then = x[0]
33 then = x[0]
34 delta = max(1, int(now - then))
34 delta = max(1, int(now - then))
35
35
36 scales = [["second", 1],
36 scales = [["second", 1],
37 ["minute", 60],
37 ["minute", 60],
38 ["hour", 3600],
38 ["hour", 3600],
39 ["day", 3600 * 24],
39 ["day", 3600 * 24],
40 ["week", 3600 * 24 * 7],
40 ["week", 3600 * 24 * 7],
41 ["month", 3600 * 24 * 30],
41 ["month", 3600 * 24 * 30],
42 ["year", 3600 * 24 * 365]]
42 ["year", 3600 * 24 * 365]]
43
43
44 scales.reverse()
44 scales.reverse()
45
45
46 for t, s in scales:
46 for t, s in scales:
47 n = delta / s
47 n = delta / s
48 if n >= 2 or s == 1:
48 if n >= 2 or s == 1:
49 return fmt(t, n)
49 return fmt(t, n)
50
50
51 def nl2br(text):
51 def nl2br(text):
52 return text.replace('\n', '<br/>\n')
52 return text.replace('\n', '<br/>\n')
53
53
54 def obfuscate(text):
54 def obfuscate(text):
55 return ''.join(['&#%d;' % ord(c) for c in text])
55 return ''.join(['&#%d;' % ord(c) for c in text])
56
56
57 def up(p):
57 def up(p):
58 if p[0] != "/":
58 if p[0] != "/":
59 p = "/" + p
59 p = "/" + p
60 if p[-1] == "/":
60 if p[-1] == "/":
61 p = p[:-1]
61 p = p[:-1]
62 up = os.path.dirname(p)
62 up = os.path.dirname(p)
63 if up == "/":
63 if up == "/":
64 return "/"
64 return "/"
65 return up + "/"
65 return up + "/"
66
66
67 def get_mtime(repo_path):
67 def get_mtime(repo_path):
68 hg_path = os.path.join(repo_path, ".hg")
68 hg_path = os.path.join(repo_path, ".hg")
69 cl_path = os.path.join(hg_path, "00changelog.i")
69 cl_path = os.path.join(hg_path, "00changelog.i")
70 if os.path.exists(os.path.join(cl_path)):
70 if os.path.exists(os.path.join(cl_path)):
71 return os.stat(cl_path).st_mtime
71 return os.stat(cl_path).st_mtime
72 else:
72 else:
73 return os.stat(hg_path).st_mtime
73 return os.stat(hg_path).st_mtime
74
74
75 def staticfile(directory, fname):
76 fname = os.path.realpath(os.path.join(directory, fname))
77
78 try:
79 # the static dir should be a substring in the real
80 # file path, if it is not, we have something strange
81 # going on => security breach attempt?
82 #
83 # This will either:
84 # 1) find the `static' path at index 0 = success
85 # 2) find the `static' path at other index = error
86 # 3) not find the `static' path = ValueError generated
87 if fname.index(directory) != 0:
88 # generate ValueError manually
89 raise ValueError()
90
91 os.stat(fname)
92
93 ct = mimetypes.guess_type(fname)[0] or "text/plain"
94 return "Content-type: %s\n\n%s" % (ct, file(fname).read())
95 except ValueError:
96 # security breach attempt
97 return ""
98 except OSError, e:
99 if e.errno == errno.ENOENT:
100 return ""
101
75 class hgrequest(object):
102 class hgrequest(object):
76 def __init__(self, inp=None, out=None, env=None):
103 def __init__(self, inp=None, out=None, env=None):
77 self.inp = inp or sys.stdin
104 self.inp = inp or sys.stdin
78 self.out = out or sys.stdout
105 self.out = out or sys.stdout
79 self.env = env or os.environ
106 self.env = env or os.environ
80 self.form = cgi.parse(self.inp, self.env, keep_blank_values=1)
107 self.form = cgi.parse(self.inp, self.env, keep_blank_values=1)
81
108
82 def write(self, *things):
109 def write(self, *things):
83 for thing in things:
110 for thing in things:
84 if hasattr(thing, "__iter__"):
111 if hasattr(thing, "__iter__"):
85 for part in thing:
112 for part in thing:
86 self.write(part)
113 self.write(part)
87 else:
114 else:
88 try:
115 try:
89 self.out.write(str(thing))
116 self.out.write(str(thing))
90 except socket.error, inst:
117 except socket.error, inst:
91 if inst[0] != errno.ECONNRESET:
118 if inst[0] != errno.ECONNRESET:
92 raise
119 raise
93
120
94 def header(self, headers=[('Content-type','text/html')]):
121 def header(self, headers=[('Content-type','text/html')]):
95 for header in headers:
122 for header in headers:
96 self.out.write("%s: %s\r\n" % header)
123 self.out.write("%s: %s\r\n" % header)
97 self.out.write("\r\n")
124 self.out.write("\r\n")
98
125
99 def httphdr(self, type, file="", size=0):
126 def httphdr(self, type, file="", size=0):
100
127
101 headers = [('Content-type', type)]
128 headers = [('Content-type', type)]
102 if file:
129 if file:
103 headers.append(('Content-disposition', 'attachment; filename=%s' % file))
130 headers.append(('Content-disposition', 'attachment; filename=%s' % file))
104 if size > 0:
131 if size > 0:
105 headers.append(('Content-length', str(size)))
132 headers.append(('Content-length', str(size)))
106 self.header(headers)
133 self.header(headers)
107
134
108 class templater(object):
135 class templater(object):
109 def __init__(self, mapfile, filters={}, defaults={}):
136 def __init__(self, mapfile, filters={}, defaults={}):
110 self.cache = {}
137 self.cache = {}
111 self.map = {}
138 self.map = {}
112 self.base = os.path.dirname(mapfile)
139 self.base = os.path.dirname(mapfile)
113 self.filters = filters
140 self.filters = filters
114 self.defaults = defaults
141 self.defaults = defaults
115
142
116 for l in file(mapfile):
143 for l in file(mapfile):
117 m = re.match(r'(\S+)\s*=\s*"(.*)"$', l)
144 m = re.match(r'(\S+)\s*=\s*"(.*)"$', l)
118 if m:
145 if m:
119 self.cache[m.group(1)] = m.group(2)
146 self.cache[m.group(1)] = m.group(2)
120 else:
147 else:
121 m = re.match(r'(\S+)\s*=\s*(\S+)', l)
148 m = re.match(r'(\S+)\s*=\s*(\S+)', l)
122 if m:
149 if m:
123 self.map[m.group(1)] = os.path.join(self.base, m.group(2))
150 self.map[m.group(1)] = os.path.join(self.base, m.group(2))
124 else:
151 else:
125 raise LookupError(_("unknown map entry '%s'") % l)
152 raise LookupError(_("unknown map entry '%s'") % l)
126
153
127 def __call__(self, t, **map):
154 def __call__(self, t, **map):
128 m = self.defaults.copy()
155 m = self.defaults.copy()
129 m.update(map)
156 m.update(map)
130 try:
157 try:
131 tmpl = self.cache[t]
158 tmpl = self.cache[t]
132 except KeyError:
159 except KeyError:
133 tmpl = self.cache[t] = file(self.map[t]).read()
160 tmpl = self.cache[t] = file(self.map[t]).read()
134 return self.template(tmpl, self.filters, **m)
161 return self.template(tmpl, self.filters, **m)
135
162
136 def template(self, tmpl, filters={}, **map):
163 def template(self, tmpl, filters={}, **map):
137 while tmpl:
164 while tmpl:
138 m = re.search(r"#([a-zA-Z0-9]+)((%[a-zA-Z0-9]+)*)((\|[a-zA-Z0-9]+)*)#", tmpl)
165 m = re.search(r"#([a-zA-Z0-9]+)((%[a-zA-Z0-9]+)*)((\|[a-zA-Z0-9]+)*)#", tmpl)
139 if m:
166 if m:
140 yield tmpl[:m.start(0)]
167 yield tmpl[:m.start(0)]
141 v = map.get(m.group(1), "")
168 v = map.get(m.group(1), "")
142 v = callable(v) and v(**map) or v
169 v = callable(v) and v(**map) or v
143
170
144 format = m.group(2)
171 format = m.group(2)
145 fl = m.group(4)
172 fl = m.group(4)
146
173
147 if format:
174 if format:
148 q = v.__iter__
175 q = v.__iter__
149 for i in q():
176 for i in q():
150 lm = map.copy()
177 lm = map.copy()
151 lm.update(i)
178 lm.update(i)
152 yield self(format[1:], **lm)
179 yield self(format[1:], **lm)
153
180
154 v = ""
181 v = ""
155
182
156 elif fl:
183 elif fl:
157 for f in fl.split("|")[1:]:
184 for f in fl.split("|")[1:]:
158 v = filters[f](v)
185 v = filters[f](v)
159
186
160 yield v
187 yield v
161 tmpl = tmpl[m.end(0):]
188 tmpl = tmpl[m.end(0):]
162 else:
189 else:
163 yield tmpl
190 yield tmpl
164 return
191 return
165
192
166 common_filters = {
193 common_filters = {
167 "escape": lambda x: cgi.escape(x, True),
194 "escape": lambda x: cgi.escape(x, True),
168 "urlescape": urllib.quote,
195 "urlescape": urllib.quote,
169 "strip": lambda x: x.strip(),
196 "strip": lambda x: x.strip(),
170 "age": age,
197 "age": age,
171 "date": lambda x: util.datestr(x),
198 "date": lambda x: util.datestr(x),
172 "addbreaks": nl2br,
199 "addbreaks": nl2br,
173 "obfuscate": obfuscate,
200 "obfuscate": obfuscate,
174 "short": (lambda x: x[:12]),
201 "short": (lambda x: x[:12]),
175 "firstline": (lambda x: x.splitlines(1)[0]),
202 "firstline": (lambda x: x.splitlines(1)[0]),
176 "permissions": (lambda x: x and "-rwxr-xr-x" or "-rw-r--r--"),
203 "permissions": (lambda x: x and "-rwxr-xr-x" or "-rw-r--r--"),
177 "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S"),
204 "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S"),
178 }
205 }
179
206
180 class hgweb(object):
207 class hgweb(object):
181 def __init__(self, repo, name=None):
208 def __init__(self, repo, name=None):
182 if type(repo) == type(""):
209 if type(repo) == type(""):
183 self.repo = hg.repository(ui.ui(), repo)
210 self.repo = hg.repository(ui.ui(), repo)
184 else:
211 else:
185 self.repo = repo
212 self.repo = repo
186
213
187 self.mtime = -1
214 self.mtime = -1
188 self.reponame = name
215 self.reponame = name
189 self.archives = 'zip', 'gz', 'bz2'
216 self.archives = 'zip', 'gz', 'bz2'
190
217
191 def refresh(self):
218 def refresh(self):
192 mtime = get_mtime(self.repo.root)
219 mtime = get_mtime(self.repo.root)
193 if mtime != self.mtime:
220 if mtime != self.mtime:
194 self.mtime = mtime
221 self.mtime = mtime
195 self.repo = hg.repository(self.repo.ui, self.repo.root)
222 self.repo = hg.repository(self.repo.ui, self.repo.root)
196 self.maxchanges = int(self.repo.ui.config("web", "maxchanges", 10))
223 self.maxchanges = int(self.repo.ui.config("web", "maxchanges", 10))
197 self.maxfiles = int(self.repo.ui.config("web", "maxfiles", 10))
224 self.maxfiles = int(self.repo.ui.config("web", "maxfiles", 10))
198 self.allowpull = self.repo.ui.configbool("web", "allowpull", True)
225 self.allowpull = self.repo.ui.configbool("web", "allowpull", True)
199
226
200 def archivelist(self, nodeid):
227 def archivelist(self, nodeid):
201 for i in self.archives:
228 for i in self.archives:
202 if self.repo.ui.configbool("web", "allow" + i, False):
229 if self.repo.ui.configbool("web", "allow" + i, False):
203 yield {"type" : i, "node" : nodeid}
230 yield {"type" : i, "node" : nodeid}
204
231
205 def listfiles(self, files, mf):
232 def listfiles(self, files, mf):
206 for f in files[:self.maxfiles]:
233 for f in files[:self.maxfiles]:
207 yield self.t("filenodelink", node=hex(mf[f]), file=f)
234 yield self.t("filenodelink", node=hex(mf[f]), file=f)
208 if len(files) > self.maxfiles:
235 if len(files) > self.maxfiles:
209 yield self.t("fileellipses")
236 yield self.t("fileellipses")
210
237
211 def listfilediffs(self, files, changeset):
238 def listfilediffs(self, files, changeset):
212 for f in files[:self.maxfiles]:
239 for f in files[:self.maxfiles]:
213 yield self.t("filedifflink", node=hex(changeset), file=f)
240 yield self.t("filedifflink", node=hex(changeset), file=f)
214 if len(files) > self.maxfiles:
241 if len(files) > self.maxfiles:
215 yield self.t("fileellipses")
242 yield self.t("fileellipses")
216
243
217 def siblings(self, siblings=[], rev=None, hiderev=None, **args):
244 def siblings(self, siblings=[], rev=None, hiderev=None, **args):
218 if not rev:
245 if not rev:
219 rev = lambda x: ""
246 rev = lambda x: ""
220 siblings = [s for s in siblings if s != nullid]
247 siblings = [s for s in siblings if s != nullid]
221 if len(siblings) == 1 and rev(siblings[0]) == hiderev:
248 if len(siblings) == 1 and rev(siblings[0]) == hiderev:
222 return
249 return
223 for s in siblings:
250 for s in siblings:
224 yield dict(node=hex(s), rev=rev(s), **args)
251 yield dict(node=hex(s), rev=rev(s), **args)
225
252
226 def renamelink(self, fl, node):
253 def renamelink(self, fl, node):
227 r = fl.renamed(node)
254 r = fl.renamed(node)
228 if r:
255 if r:
229 return [dict(file=r[0], node=hex(r[1]))]
256 return [dict(file=r[0], node=hex(r[1]))]
230 return []
257 return []
231
258
232 def showtag(self, t1, node=nullid, **args):
259 def showtag(self, t1, node=nullid, **args):
233 for t in self.repo.nodetags(node):
260 for t in self.repo.nodetags(node):
234 yield self.t(t1, tag=t, **args)
261 yield self.t(t1, tag=t, **args)
235
262
236 def diff(self, node1, node2, files):
263 def diff(self, node1, node2, files):
237 def filterfiles(filters, files):
264 def filterfiles(filters, files):
238 l = [x for x in files if x in filters]
265 l = [x for x in files if x in filters]
239
266
240 for t in filters:
267 for t in filters:
241 if t and t[-1] != os.sep:
268 if t and t[-1] != os.sep:
242 t += os.sep
269 t += os.sep
243 l += [x for x in files if x.startswith(t)]
270 l += [x for x in files if x.startswith(t)]
244 return l
271 return l
245
272
246 parity = [0]
273 parity = [0]
247 def diffblock(diff, f, fn):
274 def diffblock(diff, f, fn):
248 yield self.t("diffblock",
275 yield self.t("diffblock",
249 lines=prettyprintlines(diff),
276 lines=prettyprintlines(diff),
250 parity=parity[0],
277 parity=parity[0],
251 file=f,
278 file=f,
252 filenode=hex(fn or nullid))
279 filenode=hex(fn or nullid))
253 parity[0] = 1 - parity[0]
280 parity[0] = 1 - parity[0]
254
281
255 def prettyprintlines(diff):
282 def prettyprintlines(diff):
256 for l in diff.splitlines(1):
283 for l in diff.splitlines(1):
257 if l.startswith('+'):
284 if l.startswith('+'):
258 yield self.t("difflineplus", line=l)
285 yield self.t("difflineplus", line=l)
259 elif l.startswith('-'):
286 elif l.startswith('-'):
260 yield self.t("difflineminus", line=l)
287 yield self.t("difflineminus", line=l)
261 elif l.startswith('@'):
288 elif l.startswith('@'):
262 yield self.t("difflineat", line=l)
289 yield self.t("difflineat", line=l)
263 else:
290 else:
264 yield self.t("diffline", line=l)
291 yield self.t("diffline", line=l)
265
292
266 r = self.repo
293 r = self.repo
267 cl = r.changelog
294 cl = r.changelog
268 mf = r.manifest
295 mf = r.manifest
269 change1 = cl.read(node1)
296 change1 = cl.read(node1)
270 change2 = cl.read(node2)
297 change2 = cl.read(node2)
271 mmap1 = mf.read(change1[0])
298 mmap1 = mf.read(change1[0])
272 mmap2 = mf.read(change2[0])
299 mmap2 = mf.read(change2[0])
273 date1 = util.datestr(change1[2])
300 date1 = util.datestr(change1[2])
274 date2 = util.datestr(change2[2])
301 date2 = util.datestr(change2[2])
275
302
276 modified, added, removed, deleted, unknown = r.changes(node1, node2)
303 modified, added, removed, deleted, unknown = r.changes(node1, node2)
277 if files:
304 if files:
278 modified, added, removed = map(lambda x: filterfiles(files, x),
305 modified, added, removed = map(lambda x: filterfiles(files, x),
279 (modified, added, removed))
306 (modified, added, removed))
280
307
281 diffopts = self.repo.ui.diffopts()
308 diffopts = self.repo.ui.diffopts()
282 showfunc = diffopts['showfunc']
309 showfunc = diffopts['showfunc']
283 ignorews = diffopts['ignorews']
310 ignorews = diffopts['ignorews']
284 for f in modified:
311 for f in modified:
285 to = r.file(f).read(mmap1[f])
312 to = r.file(f).read(mmap1[f])
286 tn = r.file(f).read(mmap2[f])
313 tn = r.file(f).read(mmap2[f])
287 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
314 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
288 showfunc=showfunc, ignorews=ignorews), f, tn)
315 showfunc=showfunc, ignorews=ignorews), f, tn)
289 for f in added:
316 for f in added:
290 to = None
317 to = None
291 tn = r.file(f).read(mmap2[f])
318 tn = r.file(f).read(mmap2[f])
292 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
319 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
293 showfunc=showfunc, ignorews=ignorews), f, tn)
320 showfunc=showfunc, ignorews=ignorews), f, tn)
294 for f in removed:
321 for f in removed:
295 to = r.file(f).read(mmap1[f])
322 to = r.file(f).read(mmap1[f])
296 tn = None
323 tn = None
297 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
324 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
298 showfunc=showfunc, ignorews=ignorews), f, tn)
325 showfunc=showfunc, ignorews=ignorews), f, tn)
299
326
300 def changelog(self, pos):
327 def changelog(self, pos):
301 def changenav(**map):
328 def changenav(**map):
302 def seq(factor, maxchanges=None):
329 def seq(factor, maxchanges=None):
303 if maxchanges:
330 if maxchanges:
304 yield maxchanges
331 yield maxchanges
305 if maxchanges >= 20 and maxchanges <= 40:
332 if maxchanges >= 20 and maxchanges <= 40:
306 yield 50
333 yield 50
307 else:
334 else:
308 yield 1 * factor
335 yield 1 * factor
309 yield 3 * factor
336 yield 3 * factor
310 for f in seq(factor * 10):
337 for f in seq(factor * 10):
311 yield f
338 yield f
312
339
313 l = []
340 l = []
314 last = 0
341 last = 0
315 for f in seq(1, self.maxchanges):
342 for f in seq(1, self.maxchanges):
316 if f < self.maxchanges or f <= last:
343 if f < self.maxchanges or f <= last:
317 continue
344 continue
318 if f > count:
345 if f > count:
319 break
346 break
320 last = f
347 last = f
321 r = "%d" % f
348 r = "%d" % f
322 if pos + f < count:
349 if pos + f < count:
323 l.append(("+" + r, pos + f))
350 l.append(("+" + r, pos + f))
324 if pos - f >= 0:
351 if pos - f >= 0:
325 l.insert(0, ("-" + r, pos - f))
352 l.insert(0, ("-" + r, pos - f))
326
353
327 yield {"rev": 0, "label": "(0)"}
354 yield {"rev": 0, "label": "(0)"}
328
355
329 for label, rev in l:
356 for label, rev in l:
330 yield {"label": label, "rev": rev}
357 yield {"label": label, "rev": rev}
331
358
332 yield {"label": "tip", "rev": "tip"}
359 yield {"label": "tip", "rev": "tip"}
333
360
334 def changelist(**map):
361 def changelist(**map):
335 parity = (start - end) & 1
362 parity = (start - end) & 1
336 cl = self.repo.changelog
363 cl = self.repo.changelog
337 l = [] # build a list in forward order for efficiency
364 l = [] # build a list in forward order for efficiency
338 for i in range(start, end):
365 for i in range(start, end):
339 n = cl.node(i)
366 n = cl.node(i)
340 changes = cl.read(n)
367 changes = cl.read(n)
341 hn = hex(n)
368 hn = hex(n)
342
369
343 l.insert(0, {"parity": parity,
370 l.insert(0, {"parity": parity,
344 "author": changes[1],
371 "author": changes[1],
345 "parent": self.siblings(cl.parents(n), cl.rev,
372 "parent": self.siblings(cl.parents(n), cl.rev,
346 cl.rev(n) - 1),
373 cl.rev(n) - 1),
347 "child": self.siblings(cl.children(n), cl.rev,
374 "child": self.siblings(cl.children(n), cl.rev,
348 cl.rev(n) + 1),
375 cl.rev(n) + 1),
349 "changelogtag": self.showtag("changelogtag",n),
376 "changelogtag": self.showtag("changelogtag",n),
350 "manifest": hex(changes[0]),
377 "manifest": hex(changes[0]),
351 "desc": changes[4],
378 "desc": changes[4],
352 "date": changes[2],
379 "date": changes[2],
353 "files": self.listfilediffs(changes[3], n),
380 "files": self.listfilediffs(changes[3], n),
354 "rev": i,
381 "rev": i,
355 "node": hn})
382 "node": hn})
356 parity = 1 - parity
383 parity = 1 - parity
357
384
358 for e in l:
385 for e in l:
359 yield e
386 yield e
360
387
361 cl = self.repo.changelog
388 cl = self.repo.changelog
362 mf = cl.read(cl.tip())[0]
389 mf = cl.read(cl.tip())[0]
363 count = cl.count()
390 count = cl.count()
364 start = max(0, pos - self.maxchanges + 1)
391 start = max(0, pos - self.maxchanges + 1)
365 end = min(count, start + self.maxchanges)
392 end = min(count, start + self.maxchanges)
366 pos = end - 1
393 pos = end - 1
367
394
368 yield self.t('changelog',
395 yield self.t('changelog',
369 changenav=changenav,
396 changenav=changenav,
370 manifest=hex(mf),
397 manifest=hex(mf),
371 rev=pos, changesets=count, entries=changelist)
398 rev=pos, changesets=count, entries=changelist)
372
399
373 def search(self, query):
400 def search(self, query):
374
401
375 def changelist(**map):
402 def changelist(**map):
376 cl = self.repo.changelog
403 cl = self.repo.changelog
377 count = 0
404 count = 0
378 qw = query.lower().split()
405 qw = query.lower().split()
379
406
380 def revgen():
407 def revgen():
381 for i in range(cl.count() - 1, 0, -100):
408 for i in range(cl.count() - 1, 0, -100):
382 l = []
409 l = []
383 for j in range(max(0, i - 100), i):
410 for j in range(max(0, i - 100), i):
384 n = cl.node(j)
411 n = cl.node(j)
385 changes = cl.read(n)
412 changes = cl.read(n)
386 l.append((n, j, changes))
413 l.append((n, j, changes))
387 l.reverse()
414 l.reverse()
388 for e in l:
415 for e in l:
389 yield e
416 yield e
390
417
391 for n, i, changes in revgen():
418 for n, i, changes in revgen():
392 miss = 0
419 miss = 0
393 for q in qw:
420 for q in qw:
394 if not (q in changes[1].lower() or
421 if not (q in changes[1].lower() or
395 q in changes[4].lower() or
422 q in changes[4].lower() or
396 q in " ".join(changes[3][:20]).lower()):
423 q in " ".join(changes[3][:20]).lower()):
397 miss = 1
424 miss = 1
398 break
425 break
399 if miss:
426 if miss:
400 continue
427 continue
401
428
402 count += 1
429 count += 1
403 hn = hex(n)
430 hn = hex(n)
404
431
405 yield self.t('searchentry',
432 yield self.t('searchentry',
406 parity=count & 1,
433 parity=count & 1,
407 author=changes[1],
434 author=changes[1],
408 parent=self.siblings(cl.parents(n), cl.rev),
435 parent=self.siblings(cl.parents(n), cl.rev),
409 child=self.siblings(cl.children(n), cl.rev),
436 child=self.siblings(cl.children(n), cl.rev),
410 changelogtag=self.showtag("changelogtag",n),
437 changelogtag=self.showtag("changelogtag",n),
411 manifest=hex(changes[0]),
438 manifest=hex(changes[0]),
412 desc=changes[4],
439 desc=changes[4],
413 date=changes[2],
440 date=changes[2],
414 files=self.listfilediffs(changes[3], n),
441 files=self.listfilediffs(changes[3], n),
415 rev=i,
442 rev=i,
416 node=hn)
443 node=hn)
417
444
418 if count >= self.maxchanges:
445 if count >= self.maxchanges:
419 break
446 break
420
447
421 cl = self.repo.changelog
448 cl = self.repo.changelog
422 mf = cl.read(cl.tip())[0]
449 mf = cl.read(cl.tip())[0]
423
450
424 yield self.t('search',
451 yield self.t('search',
425 query=query,
452 query=query,
426 manifest=hex(mf),
453 manifest=hex(mf),
427 entries=changelist)
454 entries=changelist)
428
455
429 def changeset(self, nodeid):
456 def changeset(self, nodeid):
430 cl = self.repo.changelog
457 cl = self.repo.changelog
431 n = self.repo.lookup(nodeid)
458 n = self.repo.lookup(nodeid)
432 nodeid = hex(n)
459 nodeid = hex(n)
433 changes = cl.read(n)
460 changes = cl.read(n)
434 p1 = cl.parents(n)[0]
461 p1 = cl.parents(n)[0]
435
462
436 files = []
463 files = []
437 mf = self.repo.manifest.read(changes[0])
464 mf = self.repo.manifest.read(changes[0])
438 for f in changes[3]:
465 for f in changes[3]:
439 files.append(self.t("filenodelink",
466 files.append(self.t("filenodelink",
440 filenode=hex(mf.get(f, nullid)), file=f))
467 filenode=hex(mf.get(f, nullid)), file=f))
441
468
442 def diff(**map):
469 def diff(**map):
443 yield self.diff(p1, n, None)
470 yield self.diff(p1, n, None)
444
471
445 yield self.t('changeset',
472 yield self.t('changeset',
446 diff=diff,
473 diff=diff,
447 rev=cl.rev(n),
474 rev=cl.rev(n),
448 node=nodeid,
475 node=nodeid,
449 parent=self.siblings(cl.parents(n), cl.rev),
476 parent=self.siblings(cl.parents(n), cl.rev),
450 child=self.siblings(cl.children(n), cl.rev),
477 child=self.siblings(cl.children(n), cl.rev),
451 changesettag=self.showtag("changesettag",n),
478 changesettag=self.showtag("changesettag",n),
452 manifest=hex(changes[0]),
479 manifest=hex(changes[0]),
453 author=changes[1],
480 author=changes[1],
454 desc=changes[4],
481 desc=changes[4],
455 date=changes[2],
482 date=changes[2],
456 files=files,
483 files=files,
457 archives=self.archivelist(nodeid))
484 archives=self.archivelist(nodeid))
458
485
459 def filelog(self, f, filenode):
486 def filelog(self, f, filenode):
460 cl = self.repo.changelog
487 cl = self.repo.changelog
461 fl = self.repo.file(f)
488 fl = self.repo.file(f)
462 filenode = hex(fl.lookup(filenode))
489 filenode = hex(fl.lookup(filenode))
463 count = fl.count()
490 count = fl.count()
464
491
465 def entries(**map):
492 def entries(**map):
466 l = []
493 l = []
467 parity = (count - 1) & 1
494 parity = (count - 1) & 1
468
495
469 for i in range(count):
496 for i in range(count):
470 n = fl.node(i)
497 n = fl.node(i)
471 lr = fl.linkrev(n)
498 lr = fl.linkrev(n)
472 cn = cl.node(lr)
499 cn = cl.node(lr)
473 cs = cl.read(cl.node(lr))
500 cs = cl.read(cl.node(lr))
474
501
475 l.insert(0, {"parity": parity,
502 l.insert(0, {"parity": parity,
476 "filenode": hex(n),
503 "filenode": hex(n),
477 "filerev": i,
504 "filerev": i,
478 "file": f,
505 "file": f,
479 "node": hex(cn),
506 "node": hex(cn),
480 "author": cs[1],
507 "author": cs[1],
481 "date": cs[2],
508 "date": cs[2],
482 "rename": self.renamelink(fl, n),
509 "rename": self.renamelink(fl, n),
483 "parent": self.siblings(fl.parents(n),
510 "parent": self.siblings(fl.parents(n),
484 fl.rev, file=f),
511 fl.rev, file=f),
485 "child": self.siblings(fl.children(n),
512 "child": self.siblings(fl.children(n),
486 fl.rev, file=f),
513 fl.rev, file=f),
487 "desc": cs[4]})
514 "desc": cs[4]})
488 parity = 1 - parity
515 parity = 1 - parity
489
516
490 for e in l:
517 for e in l:
491 yield e
518 yield e
492
519
493 yield self.t("filelog", file=f, filenode=filenode, entries=entries)
520 yield self.t("filelog", file=f, filenode=filenode, entries=entries)
494
521
495 def filerevision(self, f, node):
522 def filerevision(self, f, node):
496 fl = self.repo.file(f)
523 fl = self.repo.file(f)
497 n = fl.lookup(node)
524 n = fl.lookup(node)
498 node = hex(n)
525 node = hex(n)
499 text = fl.read(n)
526 text = fl.read(n)
500 changerev = fl.linkrev(n)
527 changerev = fl.linkrev(n)
501 cl = self.repo.changelog
528 cl = self.repo.changelog
502 cn = cl.node(changerev)
529 cn = cl.node(changerev)
503 cs = cl.read(cn)
530 cs = cl.read(cn)
504 mfn = cs[0]
531 mfn = cs[0]
505
532
506 mt = mimetypes.guess_type(f)[0]
533 mt = mimetypes.guess_type(f)[0]
507 rawtext = text
534 rawtext = text
508 if util.binary(text):
535 if util.binary(text):
509 text = "(binary:%s)" % mt
536 text = "(binary:%s)" % mt
510
537
511 def lines():
538 def lines():
512 for l, t in enumerate(text.splitlines(1)):
539 for l, t in enumerate(text.splitlines(1)):
513 yield {"line": t,
540 yield {"line": t,
514 "linenumber": "% 6d" % (l + 1),
541 "linenumber": "% 6d" % (l + 1),
515 "parity": l & 1}
542 "parity": l & 1}
516
543
517 yield self.t("filerevision",
544 yield self.t("filerevision",
518 file=f,
545 file=f,
519 filenode=node,
546 filenode=node,
520 path=up(f),
547 path=up(f),
521 text=lines(),
548 text=lines(),
522 raw=rawtext,
549 raw=rawtext,
523 mimetype=mt,
550 mimetype=mt,
524 rev=changerev,
551 rev=changerev,
525 node=hex(cn),
552 node=hex(cn),
526 manifest=hex(mfn),
553 manifest=hex(mfn),
527 author=cs[1],
554 author=cs[1],
528 date=cs[2],
555 date=cs[2],
529 parent=self.siblings(fl.parents(n), fl.rev, file=f),
556 parent=self.siblings(fl.parents(n), fl.rev, file=f),
530 child=self.siblings(fl.children(n), fl.rev, file=f),
557 child=self.siblings(fl.children(n), fl.rev, file=f),
531 rename=self.renamelink(fl, n),
558 rename=self.renamelink(fl, n),
532 permissions=self.repo.manifest.readflags(mfn)[f])
559 permissions=self.repo.manifest.readflags(mfn)[f])
533
560
534 def fileannotate(self, f, node):
561 def fileannotate(self, f, node):
535 bcache = {}
562 bcache = {}
536 ncache = {}
563 ncache = {}
537 fl = self.repo.file(f)
564 fl = self.repo.file(f)
538 n = fl.lookup(node)
565 n = fl.lookup(node)
539 node = hex(n)
566 node = hex(n)
540 changerev = fl.linkrev(n)
567 changerev = fl.linkrev(n)
541
568
542 cl = self.repo.changelog
569 cl = self.repo.changelog
543 cn = cl.node(changerev)
570 cn = cl.node(changerev)
544 cs = cl.read(cn)
571 cs = cl.read(cn)
545 mfn = cs[0]
572 mfn = cs[0]
546
573
547 def annotate(**map):
574 def annotate(**map):
548 parity = 1
575 parity = 1
549 last = None
576 last = None
550 for r, l in fl.annotate(n):
577 for r, l in fl.annotate(n):
551 try:
578 try:
552 cnode = ncache[r]
579 cnode = ncache[r]
553 except KeyError:
580 except KeyError:
554 cnode = ncache[r] = self.repo.changelog.node(r)
581 cnode = ncache[r] = self.repo.changelog.node(r)
555
582
556 try:
583 try:
557 name = bcache[r]
584 name = bcache[r]
558 except KeyError:
585 except KeyError:
559 cl = self.repo.changelog.read(cnode)
586 cl = self.repo.changelog.read(cnode)
560 bcache[r] = name = self.repo.ui.shortuser(cl[1])
587 bcache[r] = name = self.repo.ui.shortuser(cl[1])
561
588
562 if last != cnode:
589 if last != cnode:
563 parity = 1 - parity
590 parity = 1 - parity
564 last = cnode
591 last = cnode
565
592
566 yield {"parity": parity,
593 yield {"parity": parity,
567 "node": hex(cnode),
594 "node": hex(cnode),
568 "rev": r,
595 "rev": r,
569 "author": name,
596 "author": name,
570 "file": f,
597 "file": f,
571 "line": l}
598 "line": l}
572
599
573 yield self.t("fileannotate",
600 yield self.t("fileannotate",
574 file=f,
601 file=f,
575 filenode=node,
602 filenode=node,
576 annotate=annotate,
603 annotate=annotate,
577 path=up(f),
604 path=up(f),
578 rev=changerev,
605 rev=changerev,
579 node=hex(cn),
606 node=hex(cn),
580 manifest=hex(mfn),
607 manifest=hex(mfn),
581 author=cs[1],
608 author=cs[1],
582 date=cs[2],
609 date=cs[2],
583 rename=self.renamelink(fl, n),
610 rename=self.renamelink(fl, n),
584 parent=self.siblings(fl.parents(n), fl.rev, file=f),
611 parent=self.siblings(fl.parents(n), fl.rev, file=f),
585 child=self.siblings(fl.children(n), fl.rev, file=f),
612 child=self.siblings(fl.children(n), fl.rev, file=f),
586 permissions=self.repo.manifest.readflags(mfn)[f])
613 permissions=self.repo.manifest.readflags(mfn)[f])
587
614
588 def manifest(self, mnode, path):
615 def manifest(self, mnode, path):
589 man = self.repo.manifest
616 man = self.repo.manifest
590 mn = man.lookup(mnode)
617 mn = man.lookup(mnode)
591 mnode = hex(mn)
618 mnode = hex(mn)
592 mf = man.read(mn)
619 mf = man.read(mn)
593 rev = man.rev(mn)
620 rev = man.rev(mn)
594 node = self.repo.changelog.node(rev)
621 node = self.repo.changelog.node(rev)
595 mff = man.readflags(mn)
622 mff = man.readflags(mn)
596
623
597 files = {}
624 files = {}
598
625
599 p = path[1:]
626 p = path[1:]
600 if p and p[-1] != "/":
627 if p and p[-1] != "/":
601 p += "/"
628 p += "/"
602 l = len(p)
629 l = len(p)
603
630
604 for f,n in mf.items():
631 for f,n in mf.items():
605 if f[:l] != p:
632 if f[:l] != p:
606 continue
633 continue
607 remain = f[l:]
634 remain = f[l:]
608 if "/" in remain:
635 if "/" in remain:
609 short = remain[:remain.find("/") + 1] # bleah
636 short = remain[:remain.find("/") + 1] # bleah
610 files[short] = (f, None)
637 files[short] = (f, None)
611 else:
638 else:
612 short = os.path.basename(remain)
639 short = os.path.basename(remain)
613 files[short] = (f, n)
640 files[short] = (f, n)
614
641
615 def filelist(**map):
642 def filelist(**map):
616 parity = 0
643 parity = 0
617 fl = files.keys()
644 fl = files.keys()
618 fl.sort()
645 fl.sort()
619 for f in fl:
646 for f in fl:
620 full, fnode = files[f]
647 full, fnode = files[f]
621 if not fnode:
648 if not fnode:
622 continue
649 continue
623
650
624 yield {"file": full,
651 yield {"file": full,
625 "manifest": mnode,
652 "manifest": mnode,
626 "filenode": hex(fnode),
653 "filenode": hex(fnode),
627 "parity": parity,
654 "parity": parity,
628 "basename": f,
655 "basename": f,
629 "permissions": mff[full]}
656 "permissions": mff[full]}
630 parity = 1 - parity
657 parity = 1 - parity
631
658
632 def dirlist(**map):
659 def dirlist(**map):
633 parity = 0
660 parity = 0
634 fl = files.keys()
661 fl = files.keys()
635 fl.sort()
662 fl.sort()
636 for f in fl:
663 for f in fl:
637 full, fnode = files[f]
664 full, fnode = files[f]
638 if fnode:
665 if fnode:
639 continue
666 continue
640
667
641 yield {"parity": parity,
668 yield {"parity": parity,
642 "path": os.path.join(path, f),
669 "path": os.path.join(path, f),
643 "manifest": mnode,
670 "manifest": mnode,
644 "basename": f[:-1]}
671 "basename": f[:-1]}
645 parity = 1 - parity
672 parity = 1 - parity
646
673
647 yield self.t("manifest",
674 yield self.t("manifest",
648 manifest=mnode,
675 manifest=mnode,
649 rev=rev,
676 rev=rev,
650 node=hex(node),
677 node=hex(node),
651 path=path,
678 path=path,
652 up=up(path),
679 up=up(path),
653 fentries=filelist,
680 fentries=filelist,
654 dentries=dirlist,
681 dentries=dirlist,
655 archives=self.archivelist(hex(node)))
682 archives=self.archivelist(hex(node)))
656
683
657 def tags(self):
684 def tags(self):
658 cl = self.repo.changelog
685 cl = self.repo.changelog
659 mf = cl.read(cl.tip())[0]
686 mf = cl.read(cl.tip())[0]
660
687
661 i = self.repo.tagslist()
688 i = self.repo.tagslist()
662 i.reverse()
689 i.reverse()
663
690
664 def entries(notip=False, **map):
691 def entries(notip=False, **map):
665 parity = 0
692 parity = 0
666 for k,n in i:
693 for k,n in i:
667 if notip and k == "tip": continue
694 if notip and k == "tip": continue
668 yield {"parity": parity,
695 yield {"parity": parity,
669 "tag": k,
696 "tag": k,
670 "tagmanifest": hex(cl.read(n)[0]),
697 "tagmanifest": hex(cl.read(n)[0]),
671 "date": cl.read(n)[2],
698 "date": cl.read(n)[2],
672 "node": hex(n)}
699 "node": hex(n)}
673 parity = 1 - parity
700 parity = 1 - parity
674
701
675 yield self.t("tags",
702 yield self.t("tags",
676 manifest=hex(mf),
703 manifest=hex(mf),
677 entries=lambda **x: entries(False, **x),
704 entries=lambda **x: entries(False, **x),
678 entriesnotip=lambda **x: entries(True, **x))
705 entriesnotip=lambda **x: entries(True, **x))
679
706
680 def summary(self):
707 def summary(self):
681 cl = self.repo.changelog
708 cl = self.repo.changelog
682 mf = cl.read(cl.tip())[0]
709 mf = cl.read(cl.tip())[0]
683
710
684 i = self.repo.tagslist()
711 i = self.repo.tagslist()
685 i.reverse()
712 i.reverse()
686
713
687 def tagentries(**map):
714 def tagentries(**map):
688 parity = 0
715 parity = 0
689 count = 0
716 count = 0
690 for k,n in i:
717 for k,n in i:
691 if k == "tip": # skip tip
718 if k == "tip": # skip tip
692 continue;
719 continue;
693
720
694 count += 1
721 count += 1
695 if count > 10: # limit to 10 tags
722 if count > 10: # limit to 10 tags
696 break;
723 break;
697
724
698 c = cl.read(n)
725 c = cl.read(n)
699 m = c[0]
726 m = c[0]
700 t = c[2]
727 t = c[2]
701
728
702 yield self.t("tagentry",
729 yield self.t("tagentry",
703 parity = parity,
730 parity = parity,
704 tag = k,
731 tag = k,
705 node = hex(n),
732 node = hex(n),
706 date = t,
733 date = t,
707 tagmanifest = hex(m))
734 tagmanifest = hex(m))
708 parity = 1 - parity
735 parity = 1 - parity
709
736
710 def changelist(**map):
737 def changelist(**map):
711 parity = 0
738 parity = 0
712 cl = self.repo.changelog
739 cl = self.repo.changelog
713 l = [] # build a list in forward order for efficiency
740 l = [] # build a list in forward order for efficiency
714 for i in range(start, end):
741 for i in range(start, end):
715 n = cl.node(i)
742 n = cl.node(i)
716 changes = cl.read(n)
743 changes = cl.read(n)
717 hn = hex(n)
744 hn = hex(n)
718 t = changes[2]
745 t = changes[2]
719
746
720 l.insert(0, self.t(
747 l.insert(0, self.t(
721 'shortlogentry',
748 'shortlogentry',
722 parity = parity,
749 parity = parity,
723 author = changes[1],
750 author = changes[1],
724 manifest = hex(changes[0]),
751 manifest = hex(changes[0]),
725 desc = changes[4],
752 desc = changes[4],
726 date = t,
753 date = t,
727 rev = i,
754 rev = i,
728 node = hn))
755 node = hn))
729 parity = 1 - parity
756 parity = 1 - parity
730
757
731 yield l
758 yield l
732
759
733 cl = self.repo.changelog
760 cl = self.repo.changelog
734 mf = cl.read(cl.tip())[0]
761 mf = cl.read(cl.tip())[0]
735 count = cl.count()
762 count = cl.count()
736 start = max(0, count - self.maxchanges)
763 start = max(0, count - self.maxchanges)
737 end = min(count, start + self.maxchanges)
764 end = min(count, start + self.maxchanges)
738 pos = end - 1
765 pos = end - 1
739
766
740 yield self.t("summary",
767 yield self.t("summary",
741 desc = self.repo.ui.config("web", "description", "unknown"),
768 desc = self.repo.ui.config("web", "description", "unknown"),
742 owner = (self.repo.ui.config("ui", "username") or # preferred
769 owner = (self.repo.ui.config("ui", "username") or # preferred
743 self.repo.ui.config("web", "contact") or # deprecated
770 self.repo.ui.config("web", "contact") or # deprecated
744 self.repo.ui.config("web", "author", "unknown")), # also
771 self.repo.ui.config("web", "author", "unknown")), # also
745 lastchange = (0, 0), # FIXME
772 lastchange = (0, 0), # FIXME
746 manifest = hex(mf),
773 manifest = hex(mf),
747 tags = tagentries,
774 tags = tagentries,
748 shortlog = changelist)
775 shortlog = changelist)
749
776
750 def filediff(self, file, changeset):
777 def filediff(self, file, changeset):
751 cl = self.repo.changelog
778 cl = self.repo.changelog
752 n = self.repo.lookup(changeset)
779 n = self.repo.lookup(changeset)
753 changeset = hex(n)
780 changeset = hex(n)
754 p1 = cl.parents(n)[0]
781 p1 = cl.parents(n)[0]
755 cs = cl.read(n)
782 cs = cl.read(n)
756 mf = self.repo.manifest.read(cs[0])
783 mf = self.repo.manifest.read(cs[0])
757
784
758 def diff(**map):
785 def diff(**map):
759 yield self.diff(p1, n, file)
786 yield self.diff(p1, n, file)
760
787
761 yield self.t("filediff",
788 yield self.t("filediff",
762 file=file,
789 file=file,
763 filenode=hex(mf.get(file, nullid)),
790 filenode=hex(mf.get(file, nullid)),
764 node=changeset,
791 node=changeset,
765 rev=self.repo.changelog.rev(n),
792 rev=self.repo.changelog.rev(n),
766 parent=self.siblings(cl.parents(n), cl.rev),
793 parent=self.siblings(cl.parents(n), cl.rev),
767 child=self.siblings(cl.children(n), cl.rev),
794 child=self.siblings(cl.children(n), cl.rev),
768 diff=diff)
795 diff=diff)
769
796
770 def archive(self, req, cnode, type):
797 def archive(self, req, cnode, type):
771 cs = self.repo.changelog.read(cnode)
798 cs = self.repo.changelog.read(cnode)
772 mnode = cs[0]
799 mnode = cs[0]
773 mf = self.repo.manifest.read(mnode)
800 mf = self.repo.manifest.read(mnode)
774 rev = self.repo.manifest.rev(mnode)
801 rev = self.repo.manifest.rev(mnode)
775 reponame = re.sub(r"\W+", "-", self.reponame)
802 reponame = re.sub(r"\W+", "-", self.reponame)
776 name = "%s-%s/" % (reponame, short(cnode))
803 name = "%s-%s/" % (reponame, short(cnode))
777
804
778 files = mf.keys()
805 files = mf.keys()
779 files.sort()
806 files.sort()
780
807
781 if type == 'zip':
808 if type == 'zip':
782 tmp = tempfile.mkstemp()[1]
809 tmp = tempfile.mkstemp()[1]
783 try:
810 try:
784 zf = zipfile.ZipFile(tmp, "w", zipfile.ZIP_DEFLATED)
811 zf = zipfile.ZipFile(tmp, "w", zipfile.ZIP_DEFLATED)
785
812
786 for f in files:
813 for f in files:
787 zf.writestr(name + f, self.repo.file(f).read(mf[f]))
814 zf.writestr(name + f, self.repo.file(f).read(mf[f]))
788 zf.close()
815 zf.close()
789
816
790 f = open(tmp, 'r')
817 f = open(tmp, 'r')
791 req.httphdr('application/zip', name[:-1] + '.zip',
818 req.httphdr('application/zip', name[:-1] + '.zip',
792 os.path.getsize(tmp))
819 os.path.getsize(tmp))
793 req.write(f.read())
820 req.write(f.read())
794 f.close()
821 f.close()
795 finally:
822 finally:
796 os.unlink(tmp)
823 os.unlink(tmp)
797
824
798 else:
825 else:
799 tf = tarfile.TarFile.open(mode='w|' + type, fileobj=req.out)
826 tf = tarfile.TarFile.open(mode='w|' + type, fileobj=req.out)
800 mff = self.repo.manifest.readflags(mnode)
827 mff = self.repo.manifest.readflags(mnode)
801 mtime = int(time.time())
828 mtime = int(time.time())
802
829
803 if type == "gz":
830 if type == "gz":
804 encoding = "gzip"
831 encoding = "gzip"
805 else:
832 else:
806 encoding = "x-bzip2"
833 encoding = "x-bzip2"
807 req.header([('Content-type', 'application/x-tar'),
834 req.header([('Content-type', 'application/x-tar'),
808 ('Content-disposition', 'attachment; filename=%s%s%s' %
835 ('Content-disposition', 'attachment; filename=%s%s%s' %
809 (name[:-1], '.tar.', type)),
836 (name[:-1], '.tar.', type)),
810 ('Content-encoding', encoding)])
837 ('Content-encoding', encoding)])
811 for fname in files:
838 for fname in files:
812 rcont = self.repo.file(fname).read(mf[fname])
839 rcont = self.repo.file(fname).read(mf[fname])
813 finfo = tarfile.TarInfo(name + fname)
840 finfo = tarfile.TarInfo(name + fname)
814 finfo.mtime = mtime
841 finfo.mtime = mtime
815 finfo.size = len(rcont)
842 finfo.size = len(rcont)
816 finfo.mode = mff[fname] and 0755 or 0644
843 finfo.mode = mff[fname] and 0755 or 0644
817 tf.addfile(finfo, StringIO.StringIO(rcont))
844 tf.addfile(finfo, StringIO.StringIO(rcont))
818 tf.close()
845 tf.close()
819
846
820 # add tags to things
847 # add tags to things
821 # tags -> list of changesets corresponding to tags
848 # tags -> list of changesets corresponding to tags
822 # find tag, changeset, file
849 # find tag, changeset, file
823
850
824 def run(self, req=hgrequest()):
851 def run(self, req=hgrequest()):
825 def clean(path):
852 def clean(path):
826 p = os.path.normpath(path)
853 p = os.path.normpath(path)
827 if p[:2] == "..":
854 if p[:2] == "..":
828 raise "suspicious path"
855 raise "suspicious path"
829 return p
856 return p
830
857
831 def header(**map):
858 def header(**map):
832 yield self.t("header", **map)
859 yield self.t("header", **map)
833
860
834 def footer(**map):
861 def footer(**map):
835 yield self.t("footer", **map)
862 yield self.t("footer", **map)
836
863
837 def expand_form(form):
864 def expand_form(form):
838 shortcuts = {
865 shortcuts = {
839 'cl': [('cmd', ['changelog']), ('rev', None)],
866 'cl': [('cmd', ['changelog']), ('rev', None)],
840 'cs': [('cmd', ['changeset']), ('node', None)],
867 'cs': [('cmd', ['changeset']), ('node', None)],
841 'f': [('cmd', ['file']), ('filenode', None)],
868 'f': [('cmd', ['file']), ('filenode', None)],
842 'fl': [('cmd', ['filelog']), ('filenode', None)],
869 'fl': [('cmd', ['filelog']), ('filenode', None)],
843 'fd': [('cmd', ['filediff']), ('node', None)],
870 'fd': [('cmd', ['filediff']), ('node', None)],
844 'fa': [('cmd', ['annotate']), ('filenode', None)],
871 'fa': [('cmd', ['annotate']), ('filenode', None)],
845 'mf': [('cmd', ['manifest']), ('manifest', None)],
872 'mf': [('cmd', ['manifest']), ('manifest', None)],
846 'ca': [('cmd', ['archive']), ('node', None)],
873 'ca': [('cmd', ['archive']), ('node', None)],
847 'tags': [('cmd', ['tags'])],
874 'tags': [('cmd', ['tags'])],
848 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
875 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
849 'static': [('cmd', ['static']), ('file', None)]
876 'static': [('cmd', ['static']), ('file', None)]
850 }
877 }
851
878
852 for k in shortcuts.iterkeys():
879 for k in shortcuts.iterkeys():
853 if form.has_key(k):
880 if form.has_key(k):
854 for name, value in shortcuts[k]:
881 for name, value in shortcuts[k]:
855 if value is None:
882 if value is None:
856 value = form[k]
883 value = form[k]
857 form[name] = value
884 form[name] = value
858 del form[k]
885 del form[k]
859
886
860 self.refresh()
887 self.refresh()
861
888
862 expand_form(req.form)
889 expand_form(req.form)
863
890
864 t = self.repo.ui.config("web", "templates", templatepath())
891 t = self.repo.ui.config("web", "templates", templatepath())
865 static = self.repo.ui.config("web", "static", os.path.join(t,"static"))
892 static = self.repo.ui.config("web", "static", os.path.join(t,"static"))
866 m = os.path.join(t, "map")
893 m = os.path.join(t, "map")
867 style = self.repo.ui.config("web", "style", "")
894 style = self.repo.ui.config("web", "style", "")
868 if req.form.has_key('style'):
895 if req.form.has_key('style'):
869 style = req.form['style'][0]
896 style = req.form['style'][0]
870 if style:
897 if style:
871 b = os.path.basename("map-" + style)
898 b = os.path.basename("map-" + style)
872 p = os.path.join(t, b)
899 p = os.path.join(t, b)
873 if os.path.isfile(p):
900 if os.path.isfile(p):
874 m = p
901 m = p
875
902
876 port = req.env["SERVER_PORT"]
903 port = req.env["SERVER_PORT"]
877 port = port != "80" and (":" + port) or ""
904 port = port != "80" and (":" + port) or ""
878 uri = req.env["REQUEST_URI"]
905 uri = req.env["REQUEST_URI"]
879 if "?" in uri:
906 if "?" in uri:
880 uri = uri.split("?")[0]
907 uri = uri.split("?")[0]
881 url = "http://%s%s%s" % (req.env["SERVER_NAME"], port, uri)
908 url = "http://%s%s%s" % (req.env["SERVER_NAME"], port, uri)
882 if not self.reponame:
909 if not self.reponame:
883 self.reponame = (self.repo.ui.config("web", "name")
910 self.reponame = (self.repo.ui.config("web", "name")
884 or uri.strip('/') or self.repo.root)
911 or uri.strip('/') or self.repo.root)
885
912
886 self.t = templater(m, common_filters,
913 self.t = templater(m, common_filters,
887 {"url": url,
914 {"url": url,
888 "repo": self.reponame,
915 "repo": self.reponame,
889 "header": header,
916 "header": header,
890 "footer": footer,
917 "footer": footer,
891 })
918 })
892
919
893 if not req.form.has_key('cmd'):
920 if not req.form.has_key('cmd'):
894 req.form['cmd'] = [self.t.cache['default'],]
921 req.form['cmd'] = [self.t.cache['default'],]
895
922
896 if req.form['cmd'][0] == 'changelog':
923 if req.form['cmd'][0] == 'changelog':
897 c = self.repo.changelog.count() - 1
924 c = self.repo.changelog.count() - 1
898 hi = c
925 hi = c
899 if req.form.has_key('rev'):
926 if req.form.has_key('rev'):
900 hi = req.form['rev'][0]
927 hi = req.form['rev'][0]
901 try:
928 try:
902 hi = self.repo.changelog.rev(self.repo.lookup(hi))
929 hi = self.repo.changelog.rev(self.repo.lookup(hi))
903 except hg.RepoError:
930 except hg.RepoError:
904 req.write(self.search(hi))
931 req.write(self.search(hi))
905 return
932 return
906
933
907 req.write(self.changelog(hi))
934 req.write(self.changelog(hi))
908
935
909 elif req.form['cmd'][0] == 'changeset':
936 elif req.form['cmd'][0] == 'changeset':
910 req.write(self.changeset(req.form['node'][0]))
937 req.write(self.changeset(req.form['node'][0]))
911
938
912 elif req.form['cmd'][0] == 'manifest':
939 elif req.form['cmd'][0] == 'manifest':
913 req.write(self.manifest(req.form['manifest'][0],
940 req.write(self.manifest(req.form['manifest'][0],
914 clean(req.form['path'][0])))
941 clean(req.form['path'][0])))
915
942
916 elif req.form['cmd'][0] == 'tags':
943 elif req.form['cmd'][0] == 'tags':
917 req.write(self.tags())
944 req.write(self.tags())
918
945
919 elif req.form['cmd'][0] == 'summary':
946 elif req.form['cmd'][0] == 'summary':
920 req.write(self.summary())
947 req.write(self.summary())
921
948
922 elif req.form['cmd'][0] == 'filediff':
949 elif req.form['cmd'][0] == 'filediff':
923 req.write(self.filediff(clean(req.form['file'][0]),
950 req.write(self.filediff(clean(req.form['file'][0]),
924 req.form['node'][0]))
951 req.form['node'][0]))
925
952
926 elif req.form['cmd'][0] == 'file':
953 elif req.form['cmd'][0] == 'file':
927 req.write(self.filerevision(clean(req.form['file'][0]),
954 req.write(self.filerevision(clean(req.form['file'][0]),
928 req.form['filenode'][0]))
955 req.form['filenode'][0]))
929
956
930 elif req.form['cmd'][0] == 'annotate':
957 elif req.form['cmd'][0] == 'annotate':
931 req.write(self.fileannotate(clean(req.form['file'][0]),
958 req.write(self.fileannotate(clean(req.form['file'][0]),
932 req.form['filenode'][0]))
959 req.form['filenode'][0]))
933
960
934 elif req.form['cmd'][0] == 'filelog':
961 elif req.form['cmd'][0] == 'filelog':
935 req.write(self.filelog(clean(req.form['file'][0]),
962 req.write(self.filelog(clean(req.form['file'][0]),
936 req.form['filenode'][0]))
963 req.form['filenode'][0]))
937
964
938 elif req.form['cmd'][0] == 'heads':
965 elif req.form['cmd'][0] == 'heads':
939 req.httphdr("application/mercurial-0.1")
966 req.httphdr("application/mercurial-0.1")
940 h = self.repo.heads()
967 h = self.repo.heads()
941 req.write(" ".join(map(hex, h)) + "\n")
968 req.write(" ".join(map(hex, h)) + "\n")
942
969
943 elif req.form['cmd'][0] == 'branches':
970 elif req.form['cmd'][0] == 'branches':
944 req.httphdr("application/mercurial-0.1")
971 req.httphdr("application/mercurial-0.1")
945 nodes = []
972 nodes = []
946 if req.form.has_key('nodes'):
973 if req.form.has_key('nodes'):
947 nodes = map(bin, req.form['nodes'][0].split(" "))
974 nodes = map(bin, req.form['nodes'][0].split(" "))
948 for b in self.repo.branches(nodes):
975 for b in self.repo.branches(nodes):
949 req.write(" ".join(map(hex, b)) + "\n")
976 req.write(" ".join(map(hex, b)) + "\n")
950
977
951 elif req.form['cmd'][0] == 'between':
978 elif req.form['cmd'][0] == 'between':
952 req.httphdr("application/mercurial-0.1")
979 req.httphdr("application/mercurial-0.1")
953 nodes = []
980 nodes = []
954 if req.form.has_key('pairs'):
981 if req.form.has_key('pairs'):
955 pairs = [map(bin, p.split("-"))
982 pairs = [map(bin, p.split("-"))
956 for p in req.form['pairs'][0].split(" ")]
983 for p in req.form['pairs'][0].split(" ")]
957 for b in self.repo.between(pairs):
984 for b in self.repo.between(pairs):
958 req.write(" ".join(map(hex, b)) + "\n")
985 req.write(" ".join(map(hex, b)) + "\n")
959
986
960 elif req.form['cmd'][0] == 'changegroup':
987 elif req.form['cmd'][0] == 'changegroup':
961 req.httphdr("application/mercurial-0.1")
988 req.httphdr("application/mercurial-0.1")
962 nodes = []
989 nodes = []
963 if not self.allowpull:
990 if not self.allowpull:
964 return
991 return
965
992
966 if req.form.has_key('roots'):
993 if req.form.has_key('roots'):
967 nodes = map(bin, req.form['roots'][0].split(" "))
994 nodes = map(bin, req.form['roots'][0].split(" "))
968
995
969 z = zlib.compressobj()
996 z = zlib.compressobj()
970 f = self.repo.changegroup(nodes, 'serve')
997 f = self.repo.changegroup(nodes, 'serve')
971 while 1:
998 while 1:
972 chunk = f.read(4096)
999 chunk = f.read(4096)
973 if not chunk:
1000 if not chunk:
974 break
1001 break
975 req.write(z.compress(chunk))
1002 req.write(z.compress(chunk))
976
1003
977 req.write(z.flush())
1004 req.write(z.flush())
978
1005
979 elif req.form['cmd'][0] == 'archive':
1006 elif req.form['cmd'][0] == 'archive':
980 changeset = self.repo.lookup(req.form['node'][0])
1007 changeset = self.repo.lookup(req.form['node'][0])
981 type = req.form['type'][0]
1008 type = req.form['type'][0]
982 if (type in self.archives and
1009 if (type in self.archives and
983 self.repo.ui.configbool("web", "allow" + type, False)):
1010 self.repo.ui.configbool("web", "allow" + type, False)):
984 self.archive(req, changeset, type)
1011 self.archive(req, changeset, type)
985 return
1012 return
986
1013
987 req.write(self.t("error"))
1014 req.write(self.t("error"))
988
1015
989 elif req.form['cmd'][0] == 'static':
1016 elif req.form['cmd'][0] == 'static':
990 fname = req.form['file'][0]
1017 fname = req.form['file'][0]
991
1018 req.write(staticfile(static, fname)
992 fname = os.path.realpath(os.path.join(static, fname))
1019 or tmpl("error", error="%r not found" % fname))
993
994 try:
995 # the static dir should be a substring in the real
996 # file path, if it is not, we have something strange
997 # going on => security breach attempt?
998 #
999 # This will either:
1000 # 1) find the `static' path at index 0 = success
1001 # 2) find the `static' path at other index = error
1002 # 3) not find the `static' path = ValueError generated
1003 if fname.index(static) != 0:
1004 # generate ValueError manually
1005 raise ValueError()
1006
1007 os.stat(fname)
1008
1009 ct = mimetypes.guess_type(fname)[0] or "text/plain"
1010 req.write("Content-type: %s\n\n" % ct, file(fname).read())
1011 except ValueError:
1012 # security breach attempt
1013 req.write(self.t("error"))
1014 except OSError, e:
1015 if e.errno == errno.ENOENT:
1016 req.write(self.t("error"))
1017
1020
1018 else:
1021 else:
1019 req.write(self.t("error"))
1022 req.write(self.t("error"))
1020
1023
1021 def create_server(repo):
1024 def create_server(repo):
1022
1025
1023 def openlog(opt, default):
1026 def openlog(opt, default):
1024 if opt and opt != '-':
1027 if opt and opt != '-':
1025 return open(opt, 'w')
1028 return open(opt, 'w')
1026 return default
1029 return default
1027
1030
1028 address = repo.ui.config("web", "address", "")
1031 address = repo.ui.config("web", "address", "")
1029 port = int(repo.ui.config("web", "port", 8000))
1032 port = int(repo.ui.config("web", "port", 8000))
1030 use_ipv6 = repo.ui.configbool("web", "ipv6")
1033 use_ipv6 = repo.ui.configbool("web", "ipv6")
1031 accesslog = openlog(repo.ui.config("web", "accesslog", "-"), sys.stdout)
1034 accesslog = openlog(repo.ui.config("web", "accesslog", "-"), sys.stdout)
1032 errorlog = openlog(repo.ui.config("web", "errorlog", "-"), sys.stderr)
1035 errorlog = openlog(repo.ui.config("web", "errorlog", "-"), sys.stderr)
1033
1036
1034 class IPv6HTTPServer(BaseHTTPServer.HTTPServer):
1037 class IPv6HTTPServer(BaseHTTPServer.HTTPServer):
1035 address_family = getattr(socket, 'AF_INET6', None)
1038 address_family = getattr(socket, 'AF_INET6', None)
1036
1039
1037 def __init__(self, *args, **kwargs):
1040 def __init__(self, *args, **kwargs):
1038 if self.address_family is None:
1041 if self.address_family is None:
1039 raise hg.RepoError(_('IPv6 not available on this system'))
1042 raise hg.RepoError(_('IPv6 not available on this system'))
1040 BaseHTTPServer.HTTPServer.__init__(self, *args, **kwargs)
1043 BaseHTTPServer.HTTPServer.__init__(self, *args, **kwargs)
1041
1044
1042 class hgwebhandler(BaseHTTPServer.BaseHTTPRequestHandler):
1045 class hgwebhandler(BaseHTTPServer.BaseHTTPRequestHandler):
1043 def log_error(self, format, *args):
1046 def log_error(self, format, *args):
1044 errorlog.write("%s - - [%s] %s\n" % (self.address_string(),
1047 errorlog.write("%s - - [%s] %s\n" % (self.address_string(),
1045 self.log_date_time_string(),
1048 self.log_date_time_string(),
1046 format % args))
1049 format % args))
1047
1050
1048 def log_message(self, format, *args):
1051 def log_message(self, format, *args):
1049 accesslog.write("%s - - [%s] %s\n" % (self.address_string(),
1052 accesslog.write("%s - - [%s] %s\n" % (self.address_string(),
1050 self.log_date_time_string(),
1053 self.log_date_time_string(),
1051 format % args))
1054 format % args))
1052
1055
1053 def do_POST(self):
1056 def do_POST(self):
1054 try:
1057 try:
1055 self.do_hgweb()
1058 self.do_hgweb()
1056 except socket.error, inst:
1059 except socket.error, inst:
1057 if inst[0] != errno.EPIPE:
1060 if inst[0] != errno.EPIPE:
1058 raise
1061 raise
1059
1062
1060 def do_GET(self):
1063 def do_GET(self):
1061 self.do_POST()
1064 self.do_POST()
1062
1065
1063 def do_hgweb(self):
1066 def do_hgweb(self):
1064 query = ""
1067 query = ""
1065 p = self.path.find("?")
1068 p = self.path.find("?")
1066 if p:
1069 if p:
1067 query = self.path[p + 1:]
1070 query = self.path[p + 1:]
1068 query = query.replace('+', ' ')
1071 query = query.replace('+', ' ')
1069
1072
1070 env = {}
1073 env = {}
1071 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
1074 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
1072 env['REQUEST_METHOD'] = self.command
1075 env['REQUEST_METHOD'] = self.command
1073 env['SERVER_NAME'] = self.server.server_name
1076 env['SERVER_NAME'] = self.server.server_name
1074 env['SERVER_PORT'] = str(self.server.server_port)
1077 env['SERVER_PORT'] = str(self.server.server_port)
1075 env['REQUEST_URI'] = "/"
1078 env['REQUEST_URI'] = "/"
1076 if query:
1079 if query:
1077 env['QUERY_STRING'] = query
1080 env['QUERY_STRING'] = query
1078 host = self.address_string()
1081 host = self.address_string()
1079 if host != self.client_address[0]:
1082 if host != self.client_address[0]:
1080 env['REMOTE_HOST'] = host
1083 env['REMOTE_HOST'] = host
1081 env['REMOTE_ADDR'] = self.client_address[0]
1084 env['REMOTE_ADDR'] = self.client_address[0]
1082
1085
1083 if self.headers.typeheader is None:
1086 if self.headers.typeheader is None:
1084 env['CONTENT_TYPE'] = self.headers.type
1087 env['CONTENT_TYPE'] = self.headers.type
1085 else:
1088 else:
1086 env['CONTENT_TYPE'] = self.headers.typeheader
1089 env['CONTENT_TYPE'] = self.headers.typeheader
1087 length = self.headers.getheader('content-length')
1090 length = self.headers.getheader('content-length')
1088 if length:
1091 if length:
1089 env['CONTENT_LENGTH'] = length
1092 env['CONTENT_LENGTH'] = length
1090 accept = []
1093 accept = []
1091 for line in self.headers.getallmatchingheaders('accept'):
1094 for line in self.headers.getallmatchingheaders('accept'):
1092 if line[:1] in "\t\n\r ":
1095 if line[:1] in "\t\n\r ":
1093 accept.append(line.strip())
1096 accept.append(line.strip())
1094 else:
1097 else:
1095 accept = accept + line[7:].split(',')
1098 accept = accept + line[7:].split(',')
1096 env['HTTP_ACCEPT'] = ','.join(accept)
1099 env['HTTP_ACCEPT'] = ','.join(accept)
1097
1100
1098 req = hgrequest(self.rfile, self.wfile, env)
1101 req = hgrequest(self.rfile, self.wfile, env)
1099 self.send_response(200, "Script output follows")
1102 self.send_response(200, "Script output follows")
1100 hg.run(req)
1103 hg.run(req)
1101
1104
1102 hg = hgweb(repo)
1105 hg = hgweb(repo)
1103 if use_ipv6:
1106 if use_ipv6:
1104 return IPv6HTTPServer((address, port), hgwebhandler)
1107 return IPv6HTTPServer((address, port), hgwebhandler)
1105 else:
1108 else:
1106 return BaseHTTPServer.HTTPServer((address, port), hgwebhandler)
1109 return BaseHTTPServer.HTTPServer((address, port), hgwebhandler)
1107
1110
1108 # This is a stopgap
1111 # This is a stopgap
1109 class hgwebdir(object):
1112 class hgwebdir(object):
1110 def __init__(self, config):
1113 def __init__(self, config):
1111 def cleannames(items):
1114 def cleannames(items):
1112 return [(name.strip('/'), path) for name, path in items]
1115 return [(name.strip('/'), path) for name, path in items]
1113
1116
1114 if type(config) == type([]):
1117 if type(config) == type([]):
1115 self.repos = cleannames(config)
1118 self.repos = cleannames(config)
1116 elif type(config) == type({}):
1119 elif type(config) == type({}):
1117 self.repos = cleannames(config.items())
1120 self.repos = cleannames(config.items())
1118 self.repos.sort()
1121 self.repos.sort()
1119 else:
1122 else:
1120 cp = ConfigParser.SafeConfigParser()
1123 cp = ConfigParser.SafeConfigParser()
1121 cp.read(config)
1124 cp.read(config)
1122 self.repos = cleannames(cp.items("paths"))
1125 self.repos = cleannames(cp.items("paths"))
1123 self.repos.sort()
1126 self.repos.sort()
1124
1127
1125 def run(self, req=hgrequest()):
1128 def run(self, req=hgrequest()):
1126 def header(**map):
1129 def header(**map):
1127 yield tmpl("header", **map)
1130 yield tmpl("header", **map)
1128
1131
1129 def footer(**map):
1132 def footer(**map):
1130 yield tmpl("footer", **map)
1133 yield tmpl("footer", **map)
1131
1134
1132 m = os.path.join(templatepath(), "map")
1135 m = os.path.join(templatepath(), "map")
1133 tmpl = templater(m, common_filters,
1136 tmpl = templater(m, common_filters,
1134 {"header": header, "footer": footer})
1137 {"header": header, "footer": footer})
1135
1138
1136 def entries(**map):
1139 def entries(**map):
1137 parity = 0
1140 parity = 0
1138 for name, path in self.repos:
1141 for name, path in self.repos:
1139 u = ui.ui()
1142 u = ui.ui()
1140 try:
1143 try:
1141 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
1144 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
1142 except IOError:
1145 except IOError:
1143 pass
1146 pass
1144 get = u.config
1147 get = u.config
1145
1148
1146 url = ('/'.join([req.env["REQUEST_URI"].split('?')[0], name])
1149 url = ('/'.join([req.env["REQUEST_URI"].split('?')[0], name])
1147 .replace("//", "/"))
1150 .replace("//", "/"))
1148
1151
1149 # update time with local timezone
1152 # update time with local timezone
1150 try:
1153 try:
1151 d = (get_mtime(path), util.makedate()[1])
1154 d = (get_mtime(path), util.makedate()[1])
1152 except OSError:
1155 except OSError:
1153 continue
1156 continue
1154
1157
1155 yield dict(contact=(get("ui", "username") or # preferred
1158 yield dict(contact=(get("ui", "username") or # preferred
1156 get("web", "contact") or # deprecated
1159 get("web", "contact") or # deprecated
1157 get("web", "author", "unknown")), # also
1160 get("web", "author", "unknown")), # also
1158 name=get("web", "name", name),
1161 name=get("web", "name", name),
1159 url=url,
1162 url=url,
1160 parity=parity,
1163 parity=parity,
1161 shortdesc=get("web", "description", "unknown"),
1164 shortdesc=get("web", "description", "unknown"),
1162 lastupdate=d)
1165 lastupdate=d)
1163
1166
1164 parity = 1 - parity
1167 parity = 1 - parity
1165
1168
1166 virtual = req.env.get("PATH_INFO", "").strip('/')
1169 virtual = req.env.get("PATH_INFO", "").strip('/')
1167 if virtual:
1170 if virtual:
1168 real = dict(self.repos).get(virtual)
1171 real = dict(self.repos).get(virtual)
1169 if real:
1172 if real:
1170 try:
1173 try:
1171 hgweb(real).run(req)
1174 hgweb(real).run(req)
1172 except IOError, inst:
1175 except IOError, inst:
1173 req.write(tmpl("error", error=inst.strerror))
1176 req.write(tmpl("error", error=inst.strerror))
1174 except hg.RepoError, inst:
1177 except hg.RepoError, inst:
1175 req.write(tmpl("error", error=str(inst)))
1178 req.write(tmpl("error", error=str(inst)))
1176 else:
1179 else:
1177 req.write(tmpl("notfound", repo=virtual))
1180 req.write(tmpl("notfound", repo=virtual))
1178 else:
1181 else:
1179 req.write(tmpl("index", entries=entries))
1182 if req.form.has_key('static'):
1183 static = os.path.join(templatepath(), "static")
1184 fname = req.form['static'][0]
1185 req.write(staticfile(static, fname)
1186 or tmpl("error", error="%r not found" % fname))
1187 else:
1188 req.write(tmpl("index", entries=entries))
General Comments 0
You need to be logged in to leave comments. Login now