Patchwork hgweb: expose list of per-repo labels to templates

login
register
mail settings
Submitter Gregory Szorc
Date July 1, 2016, 2:09 a.m.
Message ID <4b7c13a0b961b3ab838d.1467338997@ubuntu-vm-main>
Download mbox | patch
Permalink /patch/15681/
State Accepted
Delegated to: Yuya Nishihara
Headers show

Comments

Gregory Szorc - July 1, 2016, 2:09 a.m.
# HG changeset patch
# User Gregory Szorc <gregory.szorc@gmail.com>
# Date 1467338393 25200
#      Thu Jun 30 18:59:53 2016 -0700
# Node ID 4b7c13a0b961b3ab838dc6c8a5b127f85df07b36
# Parent  7dce56174916e09be39c690278942b4f7567b3f6
hgweb: expose list of per-repo labels to templates

hgweb currently offers limited functionality for "classifying"
repositories. This patch aims to change that.

The web.labels config option list is introduced. It's values
are exposed to the "index" and "summary" templates. Custom
templates can use template features like ifcontains() to e.g.
look for the presence of a specific label and engage specific
behavior. For example, a site operator may wish to assign a
"defunct" label to a repository so the repository is prominently
marked as dead in repository indexes.
Yuya Nishihara - July 3, 2016, 7:19 a.m.
On Thu, 30 Jun 2016 19:09:57 -0700, Gregory Szorc wrote:
> # HG changeset patch
> # User Gregory Szorc <gregory.szorc@gmail.com>
> # Date 1467338393 25200
> #      Thu Jun 30 18:59:53 2016 -0700
> # Node ID 4b7c13a0b961b3ab838dc6c8a5b127f85df07b36
> # Parent  7dce56174916e09be39c690278942b4f7567b3f6
> hgweb: expose list of per-repo labels to templates
> 
> hgweb currently offers limited functionality for "classifying"
> repositories. This patch aims to change that.
> 
> The web.labels config option list is introduced. It's values
> are exposed to the "index" and "summary" templates. Custom
> templates can use template features like ifcontains() to e.g.
> look for the presence of a specific label and engage specific
> behavior. For example, a site operator may wish to assign a
> "defunct" label to a repository so the repository is prominently
> marked as dead in repository indexes.

I agree this will be useful. Queued, thanks.

Patch

diff --git a/mercurial/help/config.txt b/mercurial/help/config.txt
--- a/mercurial/help/config.txt
+++ b/mercurial/help/config.txt
@@ -2020,16 +2020,24 @@  The full set of options is:
 
 ``hidden``
     Whether to hide the repository in the hgwebdir index.
     (default: False)
 
 ``ipv6``
     Whether to use IPv6. (default: False)
 
+``labels``
+    List of string *labels* associated with the repository.
+
+    Labels are exposed as a template keyword and can be used to customize
+    output. e.g. the ``index`` template can group or filter repositories
+    by labels and the ``summary`` template can display additional content
+    if a specific label is present.
+
 ``logoimg``
     File name of the logo image that some templates display on each page.
     The file name is relative to ``staticurl``. That is, the full path to
     the logo image is "staticurl/logoimg".
     If unset, ``hglogo.png`` will be used.
 
 ``logourl``
     Base URL to use for logos. If unset, ``https://mercurial-scm.org/``
diff --git a/mercurial/hgweb/hgwebdir_mod.py b/mercurial/hgweb/hgwebdir_mod.py
--- a/mercurial/hgweb/hgwebdir_mod.py
+++ b/mercurial/hgweb/hgwebdir_mod.py
@@ -361,17 +361,19 @@  class hgwebdir(object):
                            'name': name + '/',
                            'name_sort': name,
                            'url': url,
                            'description': "",
                            'description_sort': "",
                            'lastchange': d,
                            'lastchange_sort': d[1]-d[0],
                            'archives': [],
-                           'isdirectory': True}
+                           'isdirectory': True,
+                           'labels': [],
+                           }
 
                     seendirs.add(name)
                     yield row
                     continue
 
                 u = self.ui.copy()
                 try:
                     u.readconfig(os.path.join(path, '.hg', 'hgrc'))
@@ -411,16 +413,17 @@  class hgwebdir(object):
                        'name_sort': name,
                        'url': url,
                        'description': description or "unknown",
                        'description_sort': description.upper() or "unknown",
                        'lastchange': d,
                        'lastchange_sort': d[1]-d[0],
                        'archives': archivelist(u, "tip", url),
                        'isdirectory': None,
+                       'labels': u.configlist('web', 'labels', untrusted=True),
                        }
 
                 yield row
 
         sortdefault = None, False
         def entries(sortcolumn="", descending=False, subdir="", **map):
             rows = rawentries(subdir=subdir, **map)
 
diff --git a/mercurial/hgweb/webcommands.py b/mercurial/hgweb/webcommands.py
--- a/mercurial/hgweb/webcommands.py
+++ b/mercurial/hgweb/webcommands.py
@@ -720,17 +720,18 @@  def summary(web, req, tmpl):
                 owner=get_contact(web.config) or "unknown",
                 lastchange=tip.date(),
                 tags=tagentries,
                 bookmarks=bookmarks,
                 branches=webutil.branchentries(web.repo, web.stripecount, 10),
                 shortlog=changelist,
                 node=tip.hex(),
                 symrev='tip',
-                archives=web.archivelist("tip"))
+                archives=web.archivelist("tip"),
+                labels=web.configlist('web', 'labels'))
 
 @webcommand('filediff')
 def filediff(web, req, tmpl):
     """
     /diff/{revision}/{path}
     -----------------------
 
     Show how a file changed in a particular commit.
diff --git a/mercurial/templates/json/map b/mercurial/templates/json/map
--- a/mercurial/templates/json/map
+++ b/mercurial/templates/json/map
@@ -104,17 +104,18 @@  branchentry = '\{
 shortlogentry = '{changelistentry}'
 summary = '\{
   "node": {node|json},
   "lastchange": {lastchange|json},
   "bookmarks": [{join(bookmarks%bookmarkentry, ", ")}],
   "branches": [{join(branches%branchentry, ", ")}],
   "shortlog": [{join(shortlog%shortlogentry, ", ")}],
   "tags": [{join(tags%tagentry, ", ")}],
-  "archives": [{join(archives%archiveentry, ", ")}]
+  "archives": [{join(archives%archiveentry, ", ")}],
+  "labels": {labels|json}
   }'
 archiveentry = '\{
   "node": {node|json},
   "extension": {extension|json},
   "type": {type|json}
   }'
 filediff = '\{
   "path": {file|json},
@@ -214,10 +215,11 @@  filenodelink = ''
 filenolink = ''
 index = '\{
   "entries": [{join(entries%indexentry, ", ")}]
   }'
 indexentry = '\{
   "name": {name|utf8|json},
   "description": {description|utf8|json},
   "contact": {contact|utf8|json},
-  "lastchange": {lastchange|json}
+  "lastchange": {lastchange|json},
+  "labels": {labels|json}
   }'
diff --git a/tests/test-hgweb-json.t b/tests/test-hgweb-json.t
--- a/tests/test-hgweb-json.t
+++ b/tests/test-hgweb-json.t
@@ -834,16 +834,17 @@  summary/ shows a summary of repository s
         "date": [
           0.0,
           0
         ],
         "node": "ed66c30e87eb65337c05a4229efaa5f1d5285a90",
         "status": "inactive"
       }
     ],
+    "labels": [],
     "lastchange": [
       0.0,
       0
     ],
     "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7",
     "shortlog": [
       {
         "bookmarks": [],
diff --git a/tests/test-hgwebdir.t b/tests/test-hgwebdir.t
--- a/tests/test-hgwebdir.t
+++ b/tests/test-hgwebdir.t
@@ -57,16 +57,17 @@  create a subdirectory containing reposit
   $ hg --cwd f/f2 ci -Amf2 -d '4 0'
   adding f2
   $ echo 'f2 = f2' > f/.hgsub
   $ hg -R f ci -Am 'add subrepo' -d'4 0'
   adding .hgsub
   $ cat >> f/.hg/hgrc << EOF
   > [web]
   > name = fancy name for repo f
+  > labels = foo, bar
   > EOF
   $ cd ..
 
 create repository without .hg/store
 
   $ hg init nostore
   $ rm -R nostore/.hg/store
   $ root=`pwd`
@@ -103,22 +104,24 @@  should succeed
   $ get-with-headers.py localhost:$HGPORT '?style=json'
   200 Script output follows
   
   {
   "entries": [{
   "name": "a",
   "description": "unknown",
   "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
-  "lastchange": [*, *] (glob)
+  "lastchange": [*, *], (glob)
+  "labels": []
   }, {
   "name": "b",
   "description": "unknown",
   "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
-  "lastchange": [*, *] (glob)
+  "lastchange": [*, *], (glob)
+  "labels": []
   }]
   } (no-eol)
 
   $ get-with-headers.py localhost:$HGPORT 'a/file/tip/a?style=raw'
   200 Script output follows
   
   a
   $ get-with-headers.py localhost:$HGPORT 'b/file/tip/b?style=raw'
@@ -196,16 +199,228 @@  should succeed, slashy names
   /starstar/webdir/c/
   /starstar/webdir/notrepo/e/
   /starstar/webdir/notrepo/e/e2/
   /starstar/webdir/notrepo/f/
   /starstar/webdir/notrepo/f/f2/
   /astar/
   /astar/.hg/patches/
   
+
+  $ get-with-headers.py localhost:$HGPORT1 '?style=json'
+  200 Script output follows
+  
+  {
+  "entries": [{
+  "name": "t/a",
+  "description": "unknown",
+  "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
+  "lastchange": [*, *], (glob)
+  "labels": []
+  }, {
+  "name": "b",
+  "description": "unknown",
+  "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
+  "lastchange": [*, *], (glob)
+  "labels": []
+  }, {
+  "name": "coll/a",
+  "description": "unknown",
+  "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
+  "lastchange": [*, *], (glob)
+  "labels": []
+  }, {
+  "name": "coll/a/.hg/patches",
+  "description": "unknown",
+  "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
+  "lastchange": [*, *], (glob)
+  "labels": []
+  }, {
+  "name": "coll/b",
+  "description": "unknown",
+  "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
+  "lastchange": [*, *], (glob)
+  "labels": []
+  }, {
+  "name": "coll/c",
+  "description": "unknown",
+  "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
+  "lastchange": [*, *], (glob)
+  "labels": []
+  }, {
+  "name": "coll/notrepo/e",
+  "description": "unknown",
+  "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
+  "lastchange": [*, *], (glob)
+  "labels": []
+  }, {
+  "name": "fancy name for repo f",
+  "description": "unknown",
+  "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
+  "lastchange": [*, *], (glob)
+  "labels": ["foo", "bar"]
+  }, {
+  "name": "rcoll/a",
+  "description": "unknown",
+  "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
+  "lastchange": [*, *], (glob)
+  "labels": []
+  }, {
+  "name": "rcoll/a/.hg/patches",
+  "description": "unknown",
+  "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
+  "lastchange": [*, *], (glob)
+  "labels": []
+  }, {
+  "name": "rcoll/b",
+  "description": "unknown",
+  "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
+  "lastchange": [*, *], (glob)
+  "labels": []
+  }, {
+  "name": "rcoll/b/d",
+  "description": "unknown",
+  "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
+  "lastchange": [*, *], (glob)
+  "labels": []
+  }, {
+  "name": "rcoll/c",
+  "description": "unknown",
+  "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
+  "lastchange": [*, *], (glob)
+  "labels": []
+  }, {
+  "name": "rcoll/notrepo/e",
+  "description": "unknown",
+  "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
+  "lastchange": [*, *], (glob)
+  "labels": []
+  }, {
+  "name": "rcoll/notrepo/e/e2",
+  "description": "unknown",
+  "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
+  "lastchange": [*, *], (glob)
+  "labels": []
+  }, {
+  "name": "fancy name for repo f",
+  "description": "unknown",
+  "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
+  "lastchange": [*, *], (glob)
+  "labels": ["foo", "bar"]
+  }, {
+  "name": "rcoll/notrepo/f/f2",
+  "description": "unknown",
+  "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
+  "lastchange": [*, *], (glob)
+  "labels": []
+  }, {
+  "name": "star/webdir/a",
+  "description": "unknown",
+  "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
+  "lastchange": [*, *], (glob)
+  "labels": []
+  }, {
+  "name": "star/webdir/a/.hg/patches",
+  "description": "unknown",
+  "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
+  "lastchange": [*, *], (glob)
+  "labels": []
+  }, {
+  "name": "star/webdir/b",
+  "description": "unknown",
+  "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
+  "lastchange": [*, *], (glob)
+  "labels": []
+  }, {
+  "name": "star/webdir/c",
+  "description": "unknown",
+  "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
+  "lastchange": [*, *], (glob)
+  "labels": []
+  }, {
+  "name": "star/webdir/notrepo/e",
+  "description": "unknown",
+  "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
+  "lastchange": [*, *], (glob)
+  "labels": []
+  }, {
+  "name": "fancy name for repo f",
+  "description": "unknown",
+  "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
+  "lastchange": [*, *], (glob)
+  "labels": ["foo", "bar"]
+  }, {
+  "name": "starstar/webdir/a",
+  "description": "unknown",
+  "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
+  "lastchange": [*, *], (glob)
+  "labels": []
+  }, {
+  "name": "starstar/webdir/a/.hg/patches",
+  "description": "unknown",
+  "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
+  "lastchange": [*, *], (glob)
+  "labels": []
+  }, {
+  "name": "starstar/webdir/b",
+  "description": "unknown",
+  "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
+  "lastchange": [*, *], (glob)
+  "labels": []
+  }, {
+  "name": "starstar/webdir/b/d",
+  "description": "unknown",
+  "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
+  "lastchange": [*, *], (glob)
+  "labels": []
+  }, {
+  "name": "starstar/webdir/c",
+  "description": "unknown",
+  "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
+  "lastchange": [*, *], (glob)
+  "labels": []
+  }, {
+  "name": "starstar/webdir/notrepo/e",
+  "description": "unknown",
+  "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
+  "lastchange": [*, *], (glob)
+  "labels": []
+  }, {
+  "name": "starstar/webdir/notrepo/e/e2",
+  "description": "unknown",
+  "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
+  "lastchange": [*, *], (glob)
+  "labels": []
+  }, {
+  "name": "fancy name for repo f",
+  "description": "unknown",
+  "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
+  "lastchange": [*, *], (glob)
+  "labels": ["foo", "bar"]
+  }, {
+  "name": "starstar/webdir/notrepo/f/f2",
+  "description": "unknown",
+  "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
+  "lastchange": [*, *], (glob)
+  "labels": []
+  }, {
+  "name": "astar",
+  "description": "unknown",
+  "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
+  "lastchange": [*, *], (glob)
+  "labels": []
+  }, {
+  "name": "astar/.hg/patches",
+  "description": "unknown",
+  "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
+  "lastchange": [*, *], (glob)
+  "labels": []
+  }]
+  } (no-eol)
+
   $ get-with-headers.py localhost:$HGPORT1 '?style=paper'
   200 Script output follows
   
   <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
   <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
   <head>
   <link rel="icon" href="/static/hgicon.png" type="image/png" />
   <meta name="robots" content="index, nofollow" />