annotate mercurial/hgweb/common.py @ 7695:deec6628e62b

Also find correct column width of wide characters. Use unicodedata.east_asian_width() to determine wide/full width characters if available. Otherwise, return character count as before.
author Shun-ichi GOTO <shunichi.goto@gmail.com>
date Wed, 21 Jan 2009 20:29:47 +0900
parents bd522d09d5e3
children a3d7f99c23c0
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
2391
d351a3be3371 Fixing up comment headers for split up code.
Eric Hopper <hopper@omnifarious.org>
parents: 2356
diff changeset
1 # hgweb/common.py - Utility functions needed by hgweb_mod and hgwebdir_mod
131
c9d51742471c moving hgweb to mercurial subdir
jake@edge2.net
parents:
diff changeset
2 #
238
3b92f8fe47ae hgweb.py: kill #! line, clean up copyright notice
mpm@selenic.com
parents: 222
diff changeset
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
2859
345bac2bc4ec update copyrights.
Vadim Gelfer <vadim.gelfer@gmail.com>
parents: 2514
diff changeset
4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
131
c9d51742471c moving hgweb to mercurial subdir
jake@edge2.net
parents:
diff changeset
5 #
c9d51742471c moving hgweb to mercurial subdir
jake@edge2.net
parents:
diff changeset
6 # This software may be used and distributed according to the terms
c9d51742471c moving hgweb to mercurial subdir
jake@edge2.net
parents:
diff changeset
7 # of the GNU General Public License, incorporated herein by reference.
c9d51742471c moving hgweb to mercurial subdir
jake@edge2.net
parents:
diff changeset
8
5561
22713dce19f6 hgweb: return meaningful HTTP status codes instead of nonsense
Bryan O'Sullivan <bos@serpentine.com>
parents: 4462
diff changeset
9 import errno, mimetypes, os
22713dce19f6 hgweb: return meaningful HTTP status codes instead of nonsense
Bryan O'Sullivan <bos@serpentine.com>
parents: 4462
diff changeset
10
5993
948a41e77902 hgweb: explicit response status
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5930
diff changeset
11 HTTP_OK = 200
948a41e77902 hgweb: explicit response status
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5930
diff changeset
12 HTTP_BAD_REQUEST = 400
6926
57b954d8d003 hgweb: raise ErrorResponses to communicate protocol errors
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6924
diff changeset
13 HTTP_UNAUTHORIZED = 401
7029
b84d27386285 hgweb: Respond with HTTP 403 for disabled archive types instead of 404
Rocco Rutte <pdmef@gmx.net>
parents: 6926
diff changeset
14 HTTP_FORBIDDEN = 403
5993
948a41e77902 hgweb: explicit response status
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5930
diff changeset
15 HTTP_NOT_FOUND = 404
6926
57b954d8d003 hgweb: raise ErrorResponses to communicate protocol errors
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6924
diff changeset
16 HTTP_METHOD_NOT_ALLOWED = 405
5993
948a41e77902 hgweb: explicit response status
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5930
diff changeset
17 HTTP_SERVER_ERROR = 500
948a41e77902 hgweb: explicit response status
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5930
diff changeset
18
5561
22713dce19f6 hgweb: return meaningful HTTP status codes instead of nonsense
Bryan O'Sullivan <bos@serpentine.com>
parents: 4462
diff changeset
19 class ErrorResponse(Exception):
22713dce19f6 hgweb: return meaningful HTTP status codes instead of nonsense
Bryan O'Sullivan <bos@serpentine.com>
parents: 4462
diff changeset
20 def __init__(self, code, message=None):
22713dce19f6 hgweb: return meaningful HTTP status codes instead of nonsense
Bryan O'Sullivan <bos@serpentine.com>
parents: 4462
diff changeset
21 Exception.__init__(self)
22713dce19f6 hgweb: return meaningful HTTP status codes instead of nonsense
Bryan O'Sullivan <bos@serpentine.com>
parents: 4462
diff changeset
22 self.code = code
6924
e8332c8108ff hgweb: generate error message only if nothing is passed
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 6123
diff changeset
23 if message is not None:
5563
d61fea133f2d hgweb: fix breaking tests on Python < 2.5
Bryan O'Sullivan <bos@serpentine.com>
parents: 5561
diff changeset
24 self.message = message
5561
22713dce19f6 hgweb: return meaningful HTTP status codes instead of nonsense
Bryan O'Sullivan <bos@serpentine.com>
parents: 4462
diff changeset
25 else:
5563
d61fea133f2d hgweb: fix breaking tests on Python < 2.5
Bryan O'Sullivan <bos@serpentine.com>
parents: 5561
diff changeset
26 self.message = _statusmessage(code)
d61fea133f2d hgweb: fix breaking tests on Python < 2.5
Bryan O'Sullivan <bos@serpentine.com>
parents: 5561
diff changeset
27
d61fea133f2d hgweb: fix breaking tests on Python < 2.5
Bryan O'Sullivan <bos@serpentine.com>
parents: 5561
diff changeset
28 def _statusmessage(code):
d61fea133f2d hgweb: fix breaking tests on Python < 2.5
Bryan O'Sullivan <bos@serpentine.com>
parents: 5561
diff changeset
29 from BaseHTTPServer import BaseHTTPRequestHandler
d61fea133f2d hgweb: fix breaking tests on Python < 2.5
Bryan O'Sullivan <bos@serpentine.com>
parents: 5561
diff changeset
30 responses = BaseHTTPRequestHandler.responses
d61fea133f2d hgweb: fix breaking tests on Python < 2.5
Bryan O'Sullivan <bos@serpentine.com>
parents: 5561
diff changeset
31 return responses.get(code, ('Error', 'Unknown error'))[0]
5760
0145f9afb0e7 Removed tabs and trailing whitespace in python files
Thomas Arendsen Hein <thomas@intevation.de>
parents: 5563
diff changeset
32
5563
d61fea133f2d hgweb: fix breaking tests on Python < 2.5
Bryan O'Sullivan <bos@serpentine.com>
parents: 5561
diff changeset
33 def statusmessage(code):
d61fea133f2d hgweb: fix breaking tests on Python < 2.5
Bryan O'Sullivan <bos@serpentine.com>
parents: 5561
diff changeset
34 return '%d %s' % (code, _statusmessage(code))
131
c9d51742471c moving hgweb to mercurial subdir
jake@edge2.net
parents:
diff changeset
35
1418
68f81ba07b2a Make hgweb work when the repository is empty (no 00changelog.i)
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents: 1416
diff changeset
36 def get_mtime(repo_path):
3853
c0b449154a90 switch to the .hg/store layout, fix the tests
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents: 3276
diff changeset
37 store_path = os.path.join(repo_path, ".hg")
c0b449154a90 switch to the .hg/store layout, fix the tests
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents: 3276
diff changeset
38 if not os.path.isdir(os.path.join(store_path, "data")):
c0b449154a90 switch to the .hg/store layout, fix the tests
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents: 3276
diff changeset
39 store_path = os.path.join(store_path, "store")
c0b449154a90 switch to the .hg/store layout, fix the tests
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents: 3276
diff changeset
40 cl_path = os.path.join(store_path, "00changelog.i")
c0b449154a90 switch to the .hg/store layout, fix the tests
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents: 3276
diff changeset
41 if os.path.exists(cl_path):
1418
68f81ba07b2a Make hgweb work when the repository is empty (no 00changelog.i)
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents: 1416
diff changeset
42 return os.stat(cl_path).st_mtime
68f81ba07b2a Make hgweb work when the repository is empty (no 00changelog.i)
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents: 1416
diff changeset
43 else:
3853
c0b449154a90 switch to the .hg/store layout, fix the tests
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents: 3276
diff changeset
44 return os.stat(store_path).st_mtime
1418
68f81ba07b2a Make hgweb work when the repository is empty (no 00changelog.i)
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents: 1416
diff changeset
45
2514
419c42223bee Really fix http headers for web UI and issue 254.
Eric Hopper <hopper@omnifarious.org>
parents: 2391
diff changeset
46 def staticfile(directory, fname, req):
5930
c301f15c965a send conservatively capitalized HTTP headers
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5779
diff changeset
47 """return a file inside directory with guessed Content-Type header
1825
a9343f9d7365 Make hgweb.staticfile() more secure and portable.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 1796
diff changeset
48
a9343f9d7365 Make hgweb.staticfile() more secure and portable.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 1796
diff changeset
49 fname always uses '/' as directory separator and isn't allowed to
a9343f9d7365 Make hgweb.staticfile() more secure and portable.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 1796
diff changeset
50 contain unusual path components.
5930
c301f15c965a send conservatively capitalized HTTP headers
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5779
diff changeset
51 Content-Type is guessed using the mimetypes module.
1825
a9343f9d7365 Make hgweb.staticfile() more secure and portable.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 1796
diff changeset
52 Return an empty string if fname is illegal or file not found.
1793
83c6d8355909 Allow serving static files from hgwebdir to fix CSS and favicon.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 1792
diff changeset
53
1825
a9343f9d7365 Make hgweb.staticfile() more secure and portable.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 1796
diff changeset
54 """
a9343f9d7365 Make hgweb.staticfile() more secure and portable.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 1796
diff changeset
55 parts = fname.split('/')
a9343f9d7365 Make hgweb.staticfile() more secure and portable.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 1796
diff changeset
56 for part in parts:
a9343f9d7365 Make hgweb.staticfile() more secure and portable.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 1796
diff changeset
57 if (part in ('', os.curdir, os.pardir) or
a9343f9d7365 Make hgweb.staticfile() more secure and portable.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 1796
diff changeset
58 os.sep in part or os.altsep is not None and os.altsep in part):
a9343f9d7365 Make hgweb.staticfile() more secure and portable.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 1796
diff changeset
59 return ""
7288
9c399c53469d Allow per-file shadowing of static directory in templatepath
Brendan Cully <brendan@kublai.com>
parents: 7107
diff changeset
60 fpath = os.path.join(*parts)
9c399c53469d Allow per-file shadowing of static directory in templatepath
Brendan Cully <brendan@kublai.com>
parents: 7107
diff changeset
61 if isinstance(directory, str):
9c399c53469d Allow per-file shadowing of static directory in templatepath
Brendan Cully <brendan@kublai.com>
parents: 7107
diff changeset
62 directory = [directory]
9c399c53469d Allow per-file shadowing of static directory in templatepath
Brendan Cully <brendan@kublai.com>
parents: 7107
diff changeset
63 for d in directory:
9c399c53469d Allow per-file shadowing of static directory in templatepath
Brendan Cully <brendan@kublai.com>
parents: 7107
diff changeset
64 path = os.path.join(d, fpath)
9c399c53469d Allow per-file shadowing of static directory in templatepath
Brendan Cully <brendan@kublai.com>
parents: 7107
diff changeset
65 if os.path.exists(path):
9c399c53469d Allow per-file shadowing of static directory in templatepath
Brendan Cully <brendan@kublai.com>
parents: 7107
diff changeset
66 break
1793
83c6d8355909 Allow serving static files from hgwebdir to fix CSS and favicon.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 1792
diff changeset
67 try:
1825
a9343f9d7365 Make hgweb.staticfile() more secure and portable.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 1796
diff changeset
68 os.stat(path)
a9343f9d7365 Make hgweb.staticfile() more secure and portable.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 1796
diff changeset
69 ct = mimetypes.guess_type(path)[0] or "text/plain"
5993
948a41e77902 hgweb: explicit response status
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5930
diff changeset
70 req.respond(HTTP_OK, ct, length = os.path.getsize(path))
3244
f045b049a704 Fix static file serving over HTTP on Windows.
Wojciech Milkowski <wmilkowski@interia.pl>
parents: 2859
diff changeset
71 return file(path, 'rb').read()
5561
22713dce19f6 hgweb: return meaningful HTTP status codes instead of nonsense
Bryan O'Sullivan <bos@serpentine.com>
parents: 4462
diff changeset
72 except TypeError:
5993
948a41e77902 hgweb: explicit response status
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5930
diff changeset
73 raise ErrorResponse(HTTP_SERVER_ERROR, 'illegal file name')
5561
22713dce19f6 hgweb: return meaningful HTTP status codes instead of nonsense
Bryan O'Sullivan <bos@serpentine.com>
parents: 4462
diff changeset
74 except OSError, err:
22713dce19f6 hgweb: return meaningful HTTP status codes instead of nonsense
Bryan O'Sullivan <bos@serpentine.com>
parents: 4462
diff changeset
75 if err.errno == errno.ENOENT:
5993
948a41e77902 hgweb: explicit response status
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5930
diff changeset
76 raise ErrorResponse(HTTP_NOT_FOUND)
5561
22713dce19f6 hgweb: return meaningful HTTP status codes instead of nonsense
Bryan O'Sullivan <bos@serpentine.com>
parents: 4462
diff changeset
77 else:
5993
948a41e77902 hgweb: explicit response status
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 5930
diff changeset
78 raise ErrorResponse(HTTP_SERVER_ERROR, err.strerror)
3276
db9d2a624521 hgweb: Search templates in templatepath/style/map, too, using a common function.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 3244
diff changeset
79
db9d2a624521 hgweb: Search templates in templatepath/style/map, too, using a common function.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 3244
diff changeset
80 def style_map(templatepath, style):
db9d2a624521 hgweb: Search templates in templatepath/style/map, too, using a common function.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 3244
diff changeset
81 """Return path to mapfile for a given style.
db9d2a624521 hgweb: Search templates in templatepath/style/map, too, using a common function.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 3244
diff changeset
82
db9d2a624521 hgweb: Search templates in templatepath/style/map, too, using a common function.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 3244
diff changeset
83 Searches mapfile in the following locations:
db9d2a624521 hgweb: Search templates in templatepath/style/map, too, using a common function.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 3244
diff changeset
84 1. templatepath/style/map
db9d2a624521 hgweb: Search templates in templatepath/style/map, too, using a common function.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 3244
diff changeset
85 2. templatepath/map-style
db9d2a624521 hgweb: Search templates in templatepath/style/map, too, using a common function.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 3244
diff changeset
86 3. templatepath/map
db9d2a624521 hgweb: Search templates in templatepath/style/map, too, using a common function.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 3244
diff changeset
87 """
db9d2a624521 hgweb: Search templates in templatepath/style/map, too, using a common function.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 3244
diff changeset
88 locations = style and [os.path.join(style, "map"), "map-"+style] or []
db9d2a624521 hgweb: Search templates in templatepath/style/map, too, using a common function.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 3244
diff changeset
89 locations.append("map")
7107
125c8fedcbe0 Allow hgweb to search for templates in more than one path.
Brendan Cully <brendan@kublai.com>
parents: 7029
diff changeset
90 if isinstance(templatepath, str):
125c8fedcbe0 Allow hgweb to search for templates in more than one path.
Brendan Cully <brendan@kublai.com>
parents: 7029
diff changeset
91 templatepath = [templatepath]
125c8fedcbe0 Allow hgweb to search for templates in more than one path.
Brendan Cully <brendan@kublai.com>
parents: 7029
diff changeset
92 for path in templatepath:
125c8fedcbe0 Allow hgweb to search for templates in more than one path.
Brendan Cully <brendan@kublai.com>
parents: 7029
diff changeset
93 for location in locations:
125c8fedcbe0 Allow hgweb to search for templates in more than one path.
Brendan Cully <brendan@kublai.com>
parents: 7029
diff changeset
94 mapfile = os.path.join(path, location)
125c8fedcbe0 Allow hgweb to search for templates in more than one path.
Brendan Cully <brendan@kublai.com>
parents: 7029
diff changeset
95 if os.path.isfile(mapfile):
125c8fedcbe0 Allow hgweb to search for templates in more than one path.
Brendan Cully <brendan@kublai.com>
parents: 7029
diff changeset
96 return mapfile
3276
db9d2a624521 hgweb: Search templates in templatepath/style/map, too, using a common function.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 3244
diff changeset
97 raise RuntimeError("No hgweb templates found in %r" % templatepath)
db9d2a624521 hgweb: Search templates in templatepath/style/map, too, using a common function.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 3244
diff changeset
98
4462
12e4d9524951 hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4038
diff changeset
99 def paritygen(stripecount, offset=0):
12e4d9524951 hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4038
diff changeset
100 """count parity of horizontal stripes for easier reading"""
12e4d9524951 hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4038
diff changeset
101 if stripecount and offset:
12e4d9524951 hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4038
diff changeset
102 # account for offset, e.g. due to building the list in reverse
12e4d9524951 hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4038
diff changeset
103 count = (stripecount + offset) % stripecount
12e4d9524951 hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4038
diff changeset
104 parity = (stripecount + offset) / stripecount & 1
12e4d9524951 hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4038
diff changeset
105 else:
12e4d9524951 hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4038
diff changeset
106 count = 0
12e4d9524951 hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4038
diff changeset
107 parity = 0
12e4d9524951 hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4038
diff changeset
108 while True:
12e4d9524951 hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4038
diff changeset
109 yield parity
12e4d9524951 hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4038
diff changeset
110 count += 1
12e4d9524951 hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4038
diff changeset
111 if stripecount and count >= stripecount:
12e4d9524951 hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4038
diff changeset
112 parity = 1 - parity
12e4d9524951 hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4038
diff changeset
113 count = 0
12e4d9524951 hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents: 4038
diff changeset
114
5779
e9f68860d5ed Don't let ui.username override web.contact (issue900)
Thomas Arendsen Hein <thomas@intevation.de>
parents: 5760
diff changeset
115 def get_contact(config):
e9f68860d5ed Don't let ui.username override web.contact (issue900)
Thomas Arendsen Hein <thomas@intevation.de>
parents: 5760
diff changeset
116 """Return repo contact information or empty string.
e9f68860d5ed Don't let ui.username override web.contact (issue900)
Thomas Arendsen Hein <thomas@intevation.de>
parents: 5760
diff changeset
117
e9f68860d5ed Don't let ui.username override web.contact (issue900)
Thomas Arendsen Hein <thomas@intevation.de>
parents: 5760
diff changeset
118 web.contact is the primary source, but if that is not set, try
e9f68860d5ed Don't let ui.username override web.contact (issue900)
Thomas Arendsen Hein <thomas@intevation.de>
parents: 5760
diff changeset
119 ui.username or $EMAIL as a fallback to display something useful.
e9f68860d5ed Don't let ui.username override web.contact (issue900)
Thomas Arendsen Hein <thomas@intevation.de>
parents: 5760
diff changeset
120 """
e9f68860d5ed Don't let ui.username override web.contact (issue900)
Thomas Arendsen Hein <thomas@intevation.de>
parents: 5760
diff changeset
121 return (config("web", "contact") or
e9f68860d5ed Don't let ui.username override web.contact (issue900)
Thomas Arendsen Hein <thomas@intevation.de>
parents: 5760
diff changeset
122 config("ui", "username") or
e9f68860d5ed Don't let ui.username override web.contact (issue900)
Thomas Arendsen Hein <thomas@intevation.de>
parents: 5760
diff changeset
123 os.environ.get("EMAIL") or "")