8045
Comment: Added more information about the fragments.
|
15535
Added some notes about functions.
|
Deletions are marked like this. | Additions are marked like this. |
Line 14: | Line 14: |
The hgweb engine looks up themes in the `templates` directory, typically residing within the `mercurial` package when installed, although some operating system distributions may link `mercurial/templates` to another location (such as `/usr/share/mercurial/templates`). A theme may have its own directory with a file named `map`, or it may place a file in the `templates` directory with a name of the form `map-<theme>`, and this map file will look something like this: | The hgweb engine looks up themes in the `templates` directory, typically residing within the `mercurial` package when installed, although some operating system distributions may link `mercurial/templates` to another location (such as `/usr/share/mercurial/templates`). It is possible to set the location of the templates directory using the `templates` setting in the `web` section of an `hgrc` file such as `/etc/mercurial/hgrc` or the `.hgrc` file in the Web server user's home directory. A theme may have its own directory with a file named `map`, or it may place a file in the `templates` directory with a name of the form `map-<theme>`, and this map file will look something like this: |
Line 25: | Line 27: |
naventry = '<a href="{url}log/{node|short}{sessionvars%urlparameter}">{label|escape}</a> ' | naventry = '< a href="{url}log/{node|short}{sessionvars%urlparameter}">{label|escape}</a> ' |
Line 29: | Line 31: |
This maps a template name to either a file (for example, the content for `header` is found in the `header.tmpl` file) or a simple quoted string (such as `naventry`). The latter is used for small templates where a separate file would be awkward. | This maps a template name to either a file (for example, the content for `header` is found in the `header.tmpl` file) or a simple quoted string (such as the content for `naventry` in the above example). The latter is used for small templates where a separate file would be awkward. |
Line 35: | Line 37: |
Each template is simply a file or string with a number of tags of the form `{variable}` that get replaced with the appropriate text when the template gets processed. For example, here's the template for the tags page in the former default theme: | Each template is simply a file or string with a number of tags of the form `{variable}` that get replaced with the appropriate text when the template gets processed. For example, here's a simplified version of the template for the tags page in the `spartan` theme: |
Line 39: | Line 41: |
<title>{repo}: tags</title> | <title>{repo|escape}: tags</title> |
Line 44: | Line 46: |
<a href="?cmd=changelog;rev={rev}">changelog</a> <a href="?cmd=manifest;manifest={manifest};path=/">manifest</a> |
< a href="{url}log{sessionvars%urlparameter}">changelog</a> < a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a> < a href="{url}graph{sessionvars%urlparameter}">graph</a> < a href="{url}branches{sessionvars%urlparameter}">branches</a> < a href="{url}file/{node|short}/{sessionvars%urlparameter}">files</a> |
Line 57: | Line 62: |
There are four main kinds of template operations in the above example: * The ''inclusion'' of a fragment `{header}` which brings in text from another location * The ''substitution'' of a variable `{url}` which replaces the variable name with a proper value * The substitution of a variable which is then processed or ''filtered'', so that `{repo|escape}` replaces the variable name `repo` with a value that has been processed by the `escape` function * The ''interpolation'' `{entries%tagentry}` of values in a collection `entries` using a fragment `tagentry` which brings in text that will be processed according to each set of values Here, we define a ''fragment'' as a template defined in the map file that contributes to another template by providing a region of the final document, page or response. |
|
Line 66: | Line 80: |
|| bookmarks || bookmarks || per-repository (introduced in recent versions) || | |
Line 74: | Line 89: |
|| help || command/topic reference || || | |
Line 82: | Line 98: |
The `mercurial.hgweb.webcommands` module contains a list of the supported commands in its `__all__` attribute; the templates above correspond to many of these commands. |
|
Line 90: | Line 108: |
|| diffblock || lines in a diff || (lines, parity) || || filedifflink || link to diff || (node, file) || |
|| diffblock || lines in a diff || (`lines`, `parity`) || || filedifflink || link to diff || (`node`, `file`) || |
Line 93: | Line 111: |
|| filenodelink || link to file || (node, file, parity) || | || filenolink || reference to file || (`node`, `file`, `parity`) || || filenodelink || link to file || (`node`, `file`, `parity`) || |
Line 95: | Line 114: |
|| tagentry || tag details (summary) || (node, tag, date, parity) || == Substitutions == Although the above definition of a template would imply that arbitrary substitutions can be defined, in practice only a selection of variables are defined. In the above example, `{repo}` causes the predefined variable of that name to yield its value such that it is substituted into the output. Similarly, `{header}` and `{footer}` respectively cause the contents of the header and footer definitions to be substituted into the output. It is not currently possible to define arbitrary substitutions in the map file and then reference them in the unconditional form described above. For example, defining the following in the map file... |
|| tagentry || tag details (summary) || (`node`, `tag`, `date`, `parity`) || ==== Data Types ==== The following data types or structures are available to various templates and fragments: || '''Data Type''' || '''Members''' || || annotate (fileannotate) || `parity`, `rev`, `node`, `author`, `desc`, `file`, `targetline`, `line`, `lineid`, `linenumber` || || archives (index) || `node`, `url`, `type`, `extension` || || bookmarks (summary) || `parity`, `node`, `date`, `bookmark` || || branches (summary) || `parity`, `node`, `date`, `branch` || || changeset || `rev`, `node`, `author`, `desc`, `date`, `tags`, `branches`, `inbranch`, `parent`, `child`, `files`, `diff`, `branch`, `changesettag`, `changesetbranch`, `archives` || || entries (changelog) || `parity`, `rev`, `node`, `author`, `desc`, `date`, `tags`, `branches`, `inbranch`, `parent`, `child`, `files`, `changelogtag` || || entries (filelog) || `parity`, `filerev`, `node`, `author`, `desc`, `date`, `tags`, `branches`, `inbranch`, `parent`, `child`, `branch`, `rename`, `file` || || entries (index) || `parity`, `contact`, `contact_sort`, `name`, `name_sort`, `url`, `description`, `description_sort`, `lastchange`, `lastchange_sort`, `archives` || || entries (search) || ''template of entries (changelog)'' || || fileannotate || `rev`, `node`, `author`, `desc`, `date`, `parent`, `child`, `branch`, `rename`, `file`, `annotate`, `permissions` || || filediff || `rev`, `node`, `author`, `desc`, `date`, `parent`, `child`, `branch`, `rename`, `file`, `diff` || || manifest || `rev`, `node`, `tags`, `branches`, `inbranch`, `archives`, `path`, `up`, `upparity`, `dentries`, `fentries` || || shortlog (summary) || ''template of shortlogentry'' || || shortlogentry || `parity`, `rev`, `node`, `author`, `desc`, `date`, `tags`, `branches`, `inbranch` || Source: `mercurial.hgweb.webcommands`; in addition, the `mercurial.hgweb.hgwebdir_mod` module provides the `hgwebdir.makeindex` method which defines the data used to present the navigable index of repositories. Note that some data types yield collections of templates, not values. Such templates are thus directly substituted into other templates. See also the selection of tag and branch representations below. ==== Tags and Branches ==== || '''Data Type Member''' || '''Description''' || || bookmarks (''except summary'') || list containing dictionaries mapping `"name"` to each bookmark name || || branches (''except summary'') || list containing at most one dictionary mapping `"name"` to the branch name || || inbranch || list containing at most one dictionary mapping `"name"` to the branch name || || tag || tag name || || tags || list containing dictionaries mapping `"name"` to each tag name || == Inclusion and Substitutions == Although the above definition of a template (`{variable}` gets replaced with some text) would imply that arbitrary substitutions can be defined, in practice some limitations apply: * Only fragments listed above as providing ''special substitution without data'' can be incorporated into a template or fragment using a simple inclusion or substitution * For example: `{header}` * See the note below about the limitations of inclusion * Thus, all other fragments must be used in ''interpolation'' operations, as described below * Only a selection of variables is defined for each template or fragment * For example: `{url}` * See the fragments and data types tables above for some guidance See the filters section below for a description of how filters can be applied to substitutions. === Limitations of Inclusion === It is not currently possible to define arbitrary substitutions in the map file and then reference them as if they were simple variables (like `{url}` or `{repo|escape}`). For example, defining the following in the map file... |
Line 111: | Line 175: |
Consider the {{{{entries%tagentry}}}} line in the above example. The `entries` variable is actually a list of variable mappings, and the `%` syntax instructs the template engine to apply the `tagentry` format to each of them. Note that the `tagentry` definition is successfully referenced here, and its contents applied to each entry in the `entries` collection, precisely because an interpolation is being performed. Thus, the limitation described above (where an unconditional `{tagentry}` reference would fail) does not apply here. |
Consider the `{entries%tagentry}` line in the following example: {{{ <ul id="tagEntries"> {entries%tagentry} </ul> }}} The `entries` variable is actually a list of variable mappings, and the `%` syntax instructs the template engine to apply the `tagentry` format to each of them. The members in each variable mapping are given above in the fragments and data types tables. Note that the `tagentry` template (or fragment) is successfully referenced here, and its contents used to format each entry in the `entries` collection, precisely because an interpolation is being performed. Thus, the limitation described above (where an unconditional `{tagentry}` reference would fail) does not apply here. |
Line 117: | Line 189: |
There is also a set of filters that can be applied to replacements, for example: | There is also a set of filters that can be applied when substituting values into templates, for example: |
Line 132: | Line 204: |
* `escape`: escape HTML * `age`: print a date in 'x days ago' format (e.g. {{{{date|age} }}}gives {{{15 hours ago}}}) * `date`: print a date in default format (e.g. {{{{date|date} }}}gives {{{Mon Mar 29 15:16:05 2010 -0500}}}) * `rfc822date`: print a date in rfc822 format (e.g. {{{{date|rfc822date} }}}gives {{{Mon, 29 Mar 2010 15:16:05 -0500}}}) * `rfc3339date`: print a date in rfc3339 format (e.g. {{{{date|rfc3339date} }}}gives {{{2010-03-29T15:16:05-05:00}}}) * `localdate`: print a date in the local timezone (e.g. {{{{date|localdate|rfc822date} }}}gives {{{Mon, 29 Mar 2010 21:16:05 +0100}}}) * `shortdate`: print a date in short format (e.g. {{{{date|shortdate} }}}gives {{{2010-03-29}}}) * `addbreaks`: insert <br> tags for newlines * `obfuscate`: disguise email addresses * `short`: print node ids in short form * `firstline`: print the first line of a multiline string * `permissions`: convert a permission code to ls-style formatting |
|| '''Name''' || '''Description''' || '''Example''' || '''Example Output''' || || `addbreaks` || insert `<br>` tags for newlines || || || || `escape` || escape text as HTML || || || || `obfuscate` || disguise email addresses || || || || `person` || Remove email address from user name|| || || || `urlparameter` || escape text for use in URL parameters || || || || `xmlescape` || escape text as XML || || || ||<-4> '''Dates/Times''' || || `age` || print a date in `x days ago` format || `{date|age}` || `15 hours ago` || || `date` || print a date in default format || `{date|date}` || `Mon Mar 29 15:16:05 2010 -0500` || || `localdate` || print a date in the local timezone || `{date|localdate|rfc822date}` || `Mon, 29 Mar 2010 21:16:05 +0100` || || `isodate` || print a date in ISO 8601 format || `{date|isodate}` || `2010-03-29 15:16 -0500` || || `rfc822date` || print a date in rfc822 format || `{date|rfc822date}` || `Mon, 29 Mar 2010 15:16:05 -0500` || || `rfc3339date` || print a date in rfc3339 format || `{date|rfc3339date}` || `2010-03-29T15:16:05-05:00` || || `shortdate` || print a date in short format || `{date|shortdate}` || `2010-03-29` || ||<-4> '''Formatting''' || || `firstline` || print the first line of a multiline string || || || || `permissions` || convert a permission code to `ls`-style formatting || || || || `short` || print node ids in short form || || || == Functions == Functions are a bit like filters but are used when parameterisation of an operation is required. For example, here's the `strip` function in use, removing line endings from a file's contents: {{{ <span id="{lineid}">{strip(line|escape, '\r\n')}</span><a href="#{lineid}"></a> }}} In contrast, here's the `strip` filter being used on a description, removing all leading and trailing whitespace: {{{ <div class="description">{desc|strip|escape|websub|nonempty}</div> }}} Given the potential for parameterisation, some more complicated functions are supported. For example, the `if` function can be used to decide which text shall be substituted according to some condition: {{{ {if(isdirectory, '', '<a href="{url|urlescape}atom-log" title="subscribe to repository atom feed">Atom</a>')} }}} In the above example, used in the context of an index entry appearing in the summary view, if the entry has the `isdirectory` property, an empty string is substituted into the resulting output, otherwise a hyperlink to an Atom feed for the repository is substituted. {{{#!wiki note Functions were introduced in Mercurial 2.4 and are therefore not available to themes running on older versions. }}} |
Theming
The hgweb interface is completely themable. All output is generated from templates, nothing is hardcoded. Here's how it works:
Note that internally in the code, the term "style" may be used, but for the sake of avoiding confusion with other meanings of this word, the term "theme" is used in this document to describe the resources that change the hgweb interface's appearance.
Contents
The Map File
The hgweb engine looks up themes in the templates directory, typically residing within the mercurial package when installed, although some operating system distributions may link mercurial/templates to another location (such as /usr/share/mercurial/templates). It is possible to set the location of the templates directory using the templates setting in the web section of an hgrc file such as /etc/mercurial/hgrc or the .hgrc file in the Web server user's home directory.
A theme may have its own directory with a file named map, or it may place a file in the templates directory with a name of the form map-<theme>, and this map file will look something like this:
default = 'shortlog' mimetype = 'text/html; charset={encoding}' header = header.tmpl footer = footer.tmpl search = search.tmpl changelog = changelog.tmpl shortlog = shortlog.tmpl shortlogentry = shortlogentry.tmpl naventry = '< a href="{url}log/{node|short}{sessionvars%urlparameter}">{label|escape}</a> ' ...
This maps a template name to either a file (for example, the content for header is found in the header.tmpl file) or a simple quoted string (such as the content for naventry in the above example). The latter is used for small templates where a separate file would be awkward.
A theme is simply a separate map file and possibly some additional template files. Mercurial comes with a few themes already, including the default HTML style (called paper), an XML style for Atom and RSS feeds, and a raw theme that allows getting patches and source files as plain text, as well as a gitweb-lookalike theme. In version 1.5 there are also coal, monoblue and spartan themes.
Templates
Each template is simply a file or string with a number of tags of the form {variable} that get replaced with the appropriate text when the template gets processed. For example, here's a simplified version of the template for the tags page in the spartan theme:
{header} <title>{repo|escape}: tags</title> </head> <body> <div class="buttons"> < a href="{url}log{sessionvars%urlparameter}">changelog</a> < a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a> < a href="{url}graph{sessionvars%urlparameter}">graph</a> < a href="{url}branches{sessionvars%urlparameter}">branches</a> < a href="{url}file/{node|short}/{sessionvars%urlparameter}">files</a> </div> <h2>tags:</h2> <ul id="tagEntries"> {entries%tagentry} </ul> {footer}
There are four main kinds of template operations in the above example:
The inclusion of a fragment {header} which brings in text from another location
The substitution of a variable {url} which replaces the variable name with a proper value
The substitution of a variable which is then processed or filtered, so that {repo|escape} replaces the variable name repo with a value that has been processed by the escape function
The interpolation {entries%tagentry} of values in a collection entries using a fragment tagentry which brings in text that will be processed according to each set of values
Here, we define a fragment as a template defined in the map file that contributes to another template by providing a region of the final document, page or response.
Essential Templates
To have a functioning theme, the following templates appear to be necessary:
Page-Level Templates
These templates provide entire pages:
Template |
Information |
View Type |
bookmarks |
bookmarks |
per-repository (introduced in recent versions) |
branches |
named branches |
per-repository (introduced in recent versions) |
changelog |
history |
per-repository |
changeset |
log entry view |
|
error |
error page |
|
fileannotate |
file editors/contributors |
per-file |
filediff |
file changes |
per-file |
filerevision |
file view |
per-file |
graph |
graphical history |
per-repository (introduced in recent versions) |
help |
command/topic reference |
|
index |
list of repositories |
|
manifest |
files/browse |
per-repository |
notfound |
resource not found page |
|
search |
search results page |
|
shortlog |
history |
per-repository |
summary |
summary/dashboard page |
per-repository |
tags |
tag definitions |
per-repository |
The mercurial.hgweb.webcommands module contains a list of the supported commands in its __all__ attribute; the templates above correspond to many of these commands.
Fragments
These templates are used by the page-level templates to complete displayed pages:
Fragment |
Purpose |
Data Type |
header |
page header |
special substitution without data |
footer |
page footer |
special substitution without data |
mimetype |
content/media type |
special substitution without data |
diffblock |
lines in a diff |
(lines, parity) |
filedifflink |
link to diff |
(node, file) |
fileellipses |
... or similar |
special substitution without data |
filenolink |
reference to file |
(node, file, parity) |
filenodelink |
link to file |
(node, file, parity) |
searchentry |
search result |
shortlogentry |
tagentry |
tag details (summary) |
(node, tag, date, parity) |
Data Types
The following data types or structures are available to various templates and fragments:
Data Type |
Members |
annotate (fileannotate) |
parity, rev, node, author, desc, file, targetline, line, lineid, linenumber |
archives (index) |
node, url, type, extension |
bookmarks (summary) |
parity, node, date, bookmark |
branches (summary) |
parity, node, date, branch |
changeset |
rev, node, author, desc, date, tags, branches, inbranch, parent, child, files, diff, branch, changesettag, changesetbranch, archives |
entries (changelog) |
parity, rev, node, author, desc, date, tags, branches, inbranch, parent, child, files, changelogtag |
entries (filelog) |
parity, filerev, node, author, desc, date, tags, branches, inbranch, parent, child, branch, rename, file |
entries (index) |
parity, contact, contact_sort, name, name_sort, url, description, description_sort, lastchange, lastchange_sort, archives |
entries (search) |
template of entries (changelog) |
fileannotate |
rev, node, author, desc, date, parent, child, branch, rename, file, annotate, permissions |
filediff |
rev, node, author, desc, date, parent, child, branch, rename, file, diff |
manifest |
rev, node, tags, branches, inbranch, archives, path, up, upparity, dentries, fentries |
shortlog (summary) |
template of shortlogentry |
shortlogentry |
parity, rev, node, author, desc, date, tags, branches, inbranch |
Source: mercurial.hgweb.webcommands; in addition, the mercurial.hgweb.hgwebdir_mod module provides the hgwebdir.makeindex method which defines the data used to present the navigable index of repositories.
Note that some data types yield collections of templates, not values. Such templates are thus directly substituted into other templates. See also the selection of tag and branch representations below.
Tags and Branches
Data Type Member |
Description |
bookmarks (except summary) |
list containing dictionaries mapping "name" to each bookmark name |
branches (except summary) |
list containing at most one dictionary mapping "name" to the branch name |
inbranch |
list containing at most one dictionary mapping "name" to the branch name |
tag |
tag name |
tags |
list containing dictionaries mapping "name" to each tag name |
Inclusion and Substitutions
Although the above definition of a template ({variable} gets replaced with some text) would imply that arbitrary substitutions can be defined, in practice some limitations apply:
Only fragments listed above as providing special substitution without data can be incorporated into a template or fragment using a simple inclusion or substitution
For example: {header}
- See the note below about the limitations of inclusion
Thus, all other fragments must be used in interpolation operations, as described below
- Only a selection of variables is defined for each template or fragment
For example: {url}
- See the fragments and data types tables above for some guidance
See the filters section below for a description of how filters can be applied to substitutions.
Limitations of Inclusion
It is not currently possible to define arbitrary substitutions in the map file and then reference them as if they were simple variables (like {url} or {repo|escape}). For example, defining the following in the map file...
logo = '<img src="/images/mercurial.png" alt="Mercurial logo" />'
...and then inserting the reference {logo} in a template will not cause the substitution to occur.
Interpolations
Consider the {entries%tagentry} line in the following example:
<ul id="tagEntries"> {entries%tagentry} </ul>
The entries variable is actually a list of variable mappings, and the % syntax instructs the template engine to apply the tagentry format to each of them. The members in each variable mapping are given above in the fragments and data types tables.
Note that the tagentry template (or fragment) is successfully referenced here, and its contents used to format each entry in the entries collection, precisely because an interpolation is being performed. Thus, the limitation described above (where an unconditional {tagentry} reference would fail) does not apply here.
Filters
There is also a set of filters that can be applied when substituting values into templates, for example:
<title>{repo|escape}: changeset {node|short}</title>
This applies the escape filter to the repo variable and the short filter to the node variable. Multiple filters can be applied to a single variable:
<h2>changeset: {repo|escape|firstline}</h2>
The available filters include:
Name |
Description |
Example |
Example Output |
addbreaks |
insert <br> tags for newlines |
|
|
escape |
escape text as HTML |
|
|
obfuscate |
disguise email addresses |
|
|
person |
Remove email address from user name |
|
|
urlparameter |
escape text for use in URL parameters |
|
|
xmlescape |
escape text as XML |
|
|
Dates/Times |
|||
age |
print a date in x days ago format |
{date|age} |
15 hours ago |
date |
print a date in default format |
{date|date} |
Mon Mar 29 15:16:05 2010 -0500 |
localdate |
print a date in the local timezone |
{date|localdate|rfc822date} |
Mon, 29 Mar 2010 21:16:05 +0100 |
isodate |
print a date in ISO 8601 format |
{date|isodate} |
2010-03-29 15:16 -0500 |
rfc822date |
print a date in rfc822 format |
{date|rfc822date} |
Mon, 29 Mar 2010 15:16:05 -0500 |
rfc3339date |
print a date in rfc3339 format |
{date|rfc3339date} |
2010-03-29T15:16:05-05:00 |
shortdate |
print a date in short format |
{date|shortdate} |
2010-03-29 |
Formatting |
|||
firstline |
print the first line of a multiline string |
|
|
permissions |
convert a permission code to ls-style formatting |
|
|
short |
print node ids in short form |
|
|
Functions
Functions are a bit like filters but are used when parameterisation of an operation is required. For example, here's the strip function in use, removing line endings from a file's contents:
<span id="{lineid}">{strip(line|escape, '\r\n')}</span><a href="#{lineid}"></a>
In contrast, here's the strip filter being used on a description, removing all leading and trailing whitespace:
<div class="description">{desc|strip|escape|websub|nonempty}</div>
Given the potential for parameterisation, some more complicated functions are supported. For example, the if function can be used to decide which text shall be substituted according to some condition:
{if(isdirectory, '', '<a href="{url|urlescape}atom-log" title="subscribe to repository atom feed">Atom</a>')}
In the above example, used in the context of an index entry appearing in the summary view, if the entry has the isdirectory property, an empty string is substituted into the resulting output, otherwise a hyperlink to an Atom feed for the repository is substituted.
Functions were introduced in Mercurial 2.4 and are therefore not available to themes running on older versions.
Creating Your Own Theme
Now that you know how the templates work, creating your own theme is simply a matter of copying the stock map file to map-mytheme, modifying it, and copying any template files you modify.
Selecting a Theme
Themes (known also as styles) can be switched by appending the style=mytheme URL parameter, preceding it with ? or & as appropriate. This should allow you to navigate around the Web interface using the specified theme, although some operations may reset the theme.
To switch the default theme for your repository viewer, add the following to your .hg/hgrc file:
[web] style = mytheme