##// END OF EJS Templates
Add #motd# to gitweb pages. Needs to pass self.motd to index template, too.
Thomas Arendsen Hein -
r3478:2896ce09 default
parent child Browse files
Show More
@@ -1,205 +1,206 b''
1 1 # hgweb/hgwebdir_mod.py - Web interface for a directory of repositories.
2 2 #
3 3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5 5 #
6 6 # This software may be used and distributed according to the terms
7 7 # of the GNU General Public License, incorporated herein by reference.
8 8
9 9 import os
10 10 from mercurial.demandload import demandload
11 11 demandload(globals(), "mimetools cStringIO")
12 12 demandload(globals(), "mercurial:ui,hg,util,templater")
13 13 demandload(globals(), "mercurial.hgweb.hgweb_mod:hgweb")
14 14 demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile,style_map")
15 15 from mercurial.i18n import gettext as _
16 16
17 17 # This is a stopgap
18 18 class hgwebdir(object):
19 19 def __init__(self, config):
20 20 def cleannames(items):
21 21 return [(name.strip(os.sep), path) for name, path in items]
22 22
23 23 self.motd = ""
24 24 self.style = ""
25 25 self.repos_sorted = ('name', False)
26 26 if isinstance(config, (list, tuple)):
27 27 self.repos = cleannames(config)
28 28 self.repos_sorted = ('', False)
29 29 elif isinstance(config, dict):
30 30 self.repos = cleannames(config.items())
31 31 self.repos.sort()
32 32 else:
33 33 cp = util.configparser()
34 34 cp.read(config)
35 35 self.repos = []
36 36 if cp.has_section('web'):
37 37 if cp.has_option('web', 'motd'):
38 38 self.motd = cp.get('web', 'motd')
39 39 if cp.has_option('web', 'style'):
40 40 self.style = cp.get('web', 'style')
41 41 if cp.has_section('paths'):
42 42 self.repos.extend(cleannames(cp.items('paths')))
43 43 if cp.has_section('collections'):
44 44 for prefix, root in cp.items('collections'):
45 45 for path in util.walkrepos(root):
46 46 repo = os.path.normpath(path)
47 47 name = repo
48 48 if name.startswith(prefix):
49 49 name = name[len(prefix):]
50 50 self.repos.append((name.lstrip(os.sep), repo))
51 51 self.repos.sort()
52 52
53 53 def run(self):
54 54 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
55 55 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
56 56 import mercurial.hgweb.wsgicgi as wsgicgi
57 57 from request import wsgiapplication
58 58 def make_web_app():
59 59 return self
60 60 wsgicgi.launch(wsgiapplication(make_web_app))
61 61
62 62 def run_wsgi(self, req):
63 63 def header(**map):
64 64 header_file = cStringIO.StringIO(''.join(tmpl("header", **map)))
65 65 msg = mimetools.Message(header_file, 0)
66 66 req.header(msg.items())
67 67 yield header_file.read()
68 68
69 69 def footer(**map):
70 70 yield tmpl("footer", motd=self.motd, **map)
71 71
72 72 url = req.env['REQUEST_URI'].split('?')[0]
73 73 if not url.endswith('/'):
74 74 url += '/'
75 75
76 76 style = self.style
77 77 if req.form.has_key('style'):
78 78 style = req.form['style'][0]
79 79 mapfile = style_map(templater.templatepath(), style)
80 80 tmpl = templater.templater(mapfile, templater.common_filters,
81 81 defaults={"header": header,
82 82 "footer": footer,
83 83 "url": url})
84 84
85 85 def archivelist(ui, nodeid, url):
86 86 allowed = ui.configlist("web", "allow_archive")
87 87 for i in [('zip', '.zip'), ('gz', '.tar.gz'), ('bz2', '.tar.bz2')]:
88 88 if i[0] in allowed or ui.configbool("web", "allow" + i[0]):
89 89 yield {"type" : i[0], "extension": i[1],
90 90 "node": nodeid, "url": url}
91 91
92 92 def entries(sortcolumn="", descending=False, **map):
93 93 def sessionvars(**map):
94 94 fields = []
95 95 if req.form.has_key('style'):
96 96 style = req.form['style'][0]
97 97 if style != get('web', 'style', ''):
98 98 fields.append(('style', style))
99 99
100 100 separator = url[-1] == '?' and ';' or '?'
101 101 for name, value in fields:
102 102 yield dict(name=name, value=value, separator=separator)
103 103 separator = ';'
104 104
105 105 rows = []
106 106 parity = 0
107 107 for name, path in self.repos:
108 108 u = ui.ui()
109 109 try:
110 110 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
111 111 except IOError:
112 112 pass
113 113 get = u.config
114 114
115 115 url = ('/'.join([req.env["REQUEST_URI"].split('?')[0], name])
116 116 .replace("//", "/")) + '/'
117 117
118 118 # update time with local timezone
119 119 try:
120 120 d = (get_mtime(path), util.makedate()[1])
121 121 except OSError:
122 122 continue
123 123
124 124 contact = (get("ui", "username") or # preferred
125 125 get("web", "contact") or # deprecated
126 126 get("web", "author", "")) # also
127 127 description = get("web", "description", "")
128 128 name = get("web", "name", name)
129 129 row = dict(contact=contact or "unknown",
130 130 contact_sort=contact.upper() or "unknown",
131 131 name=name,
132 132 name_sort=name,
133 133 url=url,
134 134 description=description or "unknown",
135 135 description_sort=description.upper() or "unknown",
136 136 lastchange=d,
137 137 lastchange_sort=d[1]-d[0],
138 138 sessionvars=sessionvars,
139 139 archives=archivelist(u, "tip", url))
140 140 if (not sortcolumn
141 141 or (sortcolumn, descending) == self.repos_sorted):
142 142 # fast path for unsorted output
143 143 row['parity'] = parity
144 144 parity = 1 - parity
145 145 yield row
146 146 else:
147 147 rows.append((row["%s_sort" % sortcolumn], row))
148 148 if rows:
149 149 rows.sort()
150 150 if descending:
151 151 rows.reverse()
152 152 for key, row in rows:
153 153 row['parity'] = parity
154 154 parity = 1 - parity
155 155 yield row
156 156
157 157 virtual = req.env.get("PATH_INFO", "").strip('/')
158 158 if virtual.startswith('static/'):
159 159 static = os.path.join(templater.templatepath(), 'static')
160 160 fname = virtual[7:]
161 161 req.write(staticfile(static, fname, req) or
162 162 tmpl('error', error='%r not found' % fname))
163 163 elif virtual:
164 164 while virtual:
165 165 real = dict(self.repos).get(virtual)
166 166 if real:
167 167 break
168 168 up = virtual.rfind('/')
169 169 if up < 0:
170 170 break
171 171 virtual = virtual[:up]
172 172 if real:
173 173 req.env['REPO_NAME'] = virtual
174 174 try:
175 175 hgweb(real).run_wsgi(req)
176 176 except IOError, inst:
177 177 req.write(tmpl("error", error=inst.strerror))
178 178 except hg.RepoError, inst:
179 179 req.write(tmpl("error", error=str(inst)))
180 180 else:
181 181 req.write(tmpl("notfound", repo=virtual))
182 182 else:
183 183 if req.form.has_key('static'):
184 184 static = os.path.join(templater.templatepath(), "static")
185 185 fname = req.form['static'][0]
186 186 req.write(staticfile(static, fname, req)
187 187 or tmpl("error", error="%r not found" % fname))
188 188 else:
189 189 sortable = ["name", "description", "contact", "lastchange"]
190 190 sortcolumn, descending = self.repos_sorted
191 191 if req.form.has_key('sort'):
192 192 sortcolumn = req.form['sort'][0]
193 193 descending = sortcolumn.startswith('-')
194 194 if descending:
195 195 sortcolumn = sortcolumn[1:]
196 196 if sortcolumn not in sortable:
197 197 sortcolumn = ""
198 198
199 199 sort = [("sort_%s" % column,
200 200 "%s%s" % ((not descending and column == sortcolumn)
201 201 and "-" or "", column))
202 202 for column in sortable]
203 203 req.write(tmpl("index", entries=entries,
204 204 sortcolumn=sortcolumn, descending=descending,
205 motd=self.motd,
205 206 **dict(sort)))
@@ -1,6 +1,8 b''
1 1 <div class="page_footer">
2 2 <div class="page_footer_text">#repo|escape#</div>
3 3 <a class="rss_logo" href="#url#rss-log">RSS</a>
4 <br />
5 #motd#
4 6 </div>
5 7 </body>
6 8 </html>
@@ -1,23 +1,24 b''
1 1 #header#
2 2 <title>Mercurial repositories index</title>
3 3 </head>
4 4 <body>
5 5
6 6 <div class="page_header">
7 7 <a href="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a>Repositories list
8 8 </div>
9 9
10 10 <table cellspacing="0">
11 11 <tr>
12 12 <td><a href="?sort=#sort_name#">Name</a></td>
13 13 <td><a href="?sort=#sort_description#">Description</a></td>
14 14 <td><a href="?sort=#sort_contact#">Contact</a></td>
15 15 <td><a href="?sort=#sort_lastchange#">Last change</a></td>
16 16 <td>&nbsp;</td>
17 17 <tr>
18 18 #entries%indexentry#
19 19 </table>
20 20 <div class="page_footer">
21 #motd#
21 22 </div>
22 23 </body>
23 24 </html>
@@ -1,50 +1,50 b''
1 1 body { font-family: sans-serif; font-size: 12px; margin:0px; border:solid #d9d8d1; border-width:1px; margin:10px; }
2 2 a { color:#0000cc; }
3 3 a:hover, a:visited, a:active { color:#880000; }
4 4 div.page_header { height:25px; padding:8px; font-size:18px; font-weight:bold; background-color:#d9d8d1; }
5 5 div.page_header a:visited { color:#0000cc; }
6 6 div.page_header a:hover { color:#880000; }
7 7 div.page_nav { padding:8px; }
8 8 div.page_nav a:visited { color:#0000cc; }
9 9 div.page_path { padding:8px; border:solid #d9d8d1; border-width:0px 0px 1px}
10 div.page_footer { height:17px; padding:4px 8px; background-color: #d9d8d1; }
10 div.page_footer { padding:4px 8px; background-color: #d9d8d1; }
11 11 div.page_footer_text { float:left; color:#555555; font-style:italic; }
12 12 div.page_body { padding:8px; }
13 13 div.title, a.title {
14 14 display:block; padding:6px 8px;
15 15 font-weight:bold; background-color:#edece6; text-decoration:none; color:#000000;
16 16 }
17 17 a.title:hover { background-color: #d9d8d1; }
18 18 div.title_text { padding:6px 0px; border: solid #d9d8d1; border-width:0px 0px 1px; }
19 19 div.log_body { padding:8px 8px 8px 150px; }
20 20 .age { white-space:nowrap; }
21 21 span.age { position:relative; float:left; width:142px; font-style:italic; }
22 22 div.log_link {
23 23 padding:0px 8px;
24 24 font-size:10px; font-family:sans-serif; font-style:normal;
25 25 position:relative; float:left; width:136px;
26 26 }
27 27 div.list_head { padding:6px 8px 4px; border:solid #d9d8d1; border-width:1px 0px 0px; font-style:italic; }
28 28 a.list { text-decoration:none; color:#000000; }
29 29 a.list:hover { text-decoration:underline; color:#880000; }
30 30 table { padding:8px 4px; }
31 31 th { padding:2px 5px; font-size:12px; text-align:left; }
32 32 tr.light:hover, .parity0:hover { background-color:#edece6; }
33 33 tr.dark, .parity1 { background-color:#f6f6f0; }
34 34 tr.dark:hover, .parity1:hover { background-color:#edece6; }
35 35 td { padding:2px 5px; font-size:12px; vertical-align:top; }
36 36 td.link { padding:2px 5px; font-family:sans-serif; font-size:10px; }
37 37 div.pre { font-family:monospace; font-size:12px; white-space:pre; }
38 38 div.diff_info { font-family:monospace; color:#000099; background-color:#edece6; font-style:italic; }
39 39 div.index_include { border:solid #d9d8d1; border-width:0px 0px 1px; padding:12px 8px; }
40 40 div.search { margin:4px 8px; position:absolute; top:56px; right:12px }
41 41 .linenr { color:#999999; text-decoration:none }
42 42 a.rss_logo {
43 43 float:right; padding:3px 0px; width:35px; line-height:10px;
44 44 border:1px solid; border-color:#fcc7a5 #7d3302 #3e1a01 #ff954e;
45 45 color:#ffffff; background-color:#ff6600;
46 46 font-weight:bold; font-family:sans-serif; font-size:10px;
47 47 text-align:center; text-decoration:none;
48 48 }
49 49 a.rss_logo:hover { background-color:#ee5500; }
50 50 pre { margin: 0; }
General Comments 0
You need to be logged in to leave comments. Login now