comparison mercurial/hgweb/hgwebdir_mod.py @ 16239:287f76b3f502

hgweb: support multi-level repository indexes by enabling descend and collapse The descend option in hgweb can be used to display all reachable repositories within a directory hierarchy if set to True. However, all reachable repositories, regardless of their depth below the root of the hierarchy, are then listed at the same level - expanded - in the hgweb interface. This patch adds support for showing only each level of a directory hierarchy, with subrepositories being shown alongside their parent repositories only at the appropriate level (because there is no way to navigate to subrepositories from within repositories), and the contents of directories hidden - collapsed - behind a link for each directory. To enable this multi-level navigation, a new option called collapse must be set to True when the descend option is set to True.
author Paul Boddie <paul@boddie.org.uk>
date Sat, 18 Feb 2012 20:10:19 +0100
parents a31b8e03af28
children d94c470c3deb
comparison
equal deleted inserted replaced
16238:e8eecfe37d4e 16239:287f76b3f502
243 return archives 243 return archives
244 244
245 def rawentries(subdir="", **map): 245 def rawentries(subdir="", **map):
246 246
247 descend = self.ui.configbool('web', 'descend', True) 247 descend = self.ui.configbool('web', 'descend', True)
248 collapse = self.ui.configbool('web', 'collapse', False)
249 seenrepos = set()
250 seendirs = set()
248 for name, path in self.repos: 251 for name, path in self.repos:
249 252
250 if not name.startswith(subdir): 253 if not name.startswith(subdir):
251 continue 254 continue
252 name = name[len(subdir):] 255 name = name[len(subdir):]
253 if not descend and '/' in name: 256 directory = False
254 continue 257
255 258 if '/' in name:
256 u = self.ui.copy() 259 if not descend:
257 try: 260 continue
258 u.readconfig(os.path.join(path, '.hg', 'hgrc')) 261
259 except Exception, e: 262 nameparts = name.split('/')
260 u.warn(_('error reading %s/.hg/hgrc: %s\n') % (path, e)) 263 rootname = nameparts[0]
261 continue 264
262 def get(section, name, default=None): 265 if not collapse:
263 return u.config(section, name, default, untrusted=True) 266 pass
264 267 elif rootname in seendirs:
265 if u.configbool("web", "hidden", untrusted=True): 268 continue
266 continue 269 elif rootname in seenrepos:
267 270 pass
268 if not self.read_allowed(u, req): 271 else:
269 continue 272 directory = True
273 name = rootname
274
275 # redefine the path to refer to the directory
276 discarded = '/'.join(nameparts[1:])
277
278 # remove name parts plus accompanying slash
279 path = path[:-len(discarded) - 1]
270 280
271 parts = [name] 281 parts = [name]
272 if 'PATH_INFO' in req.env: 282 if 'PATH_INFO' in req.env:
273 parts.insert(0, req.env['PATH_INFO'].rstrip('/')) 283 parts.insert(0, req.env['PATH_INFO'].rstrip('/'))
274 if req.env['SCRIPT_NAME']: 284 if req.env['SCRIPT_NAME']:
275 parts.insert(0, req.env['SCRIPT_NAME']) 285 parts.insert(0, req.env['SCRIPT_NAME'])
276 url = re.sub(r'/+', '/', '/'.join(parts) + '/') 286 url = re.sub(r'/+', '/', '/'.join(parts) + '/')
287
288 # show either a directory entry or a repository
289 if directory:
290 # get the directory's time information
291 try:
292 d = (get_mtime(path), util.makedate()[1])
293 except OSError:
294 continue
295
296 row = dict(contact="",
297 contact_sort="",
298 name=name,
299 name_sort=name,
300 url=url,
301 description="",
302 description_sort="",
303 lastchange=d,
304 lastchange_sort=d[1]-d[0],
305 archives=[])
306
307 seendirs.add(name)
308 yield row
309 continue
310
311 u = self.ui.copy()
312 try:
313 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
314 except Exception, e:
315 u.warn(_('error reading %s/.hg/hgrc: %s\n') % (path, e))
316 continue
317 def get(section, name, default=None):
318 return u.config(section, name, default, untrusted=True)
319
320 if u.configbool("web", "hidden", untrusted=True):
321 continue
322
323 if not self.read_allowed(u, req):
324 continue
277 325
278 # update time with local timezone 326 # update time with local timezone
279 try: 327 try:
280 r = hg.repository(self.ui, path) 328 r = hg.repository(self.ui, path)
281 except IOError: 329 except IOError:
300 description=description or "unknown", 348 description=description or "unknown",
301 description_sort=description.upper() or "unknown", 349 description_sort=description.upper() or "unknown",
302 lastchange=d, 350 lastchange=d,
303 lastchange_sort=d[1]-d[0], 351 lastchange_sort=d[1]-d[0],
304 archives=archivelist(u, "tip", url)) 352 archives=archivelist(u, "tip", url))
353
354 seenrepos.add(name)
305 yield row 355 yield row
306 356
307 sortdefault = None, False 357 sortdefault = None, False
308 def entries(sortcolumn="", descending=False, subdir="", **map): 358 def entries(sortcolumn="", descending=False, subdir="", **map):
309 rows = rawentries(subdir=subdir, **map) 359 rows = rawentries(subdir=subdir, **map)