Edgewall Software

Changes between Version 5 and Version 6 of TracDev/Proposals/ThemePlugins


Ignore:
Timestamp:
Apr 14, 2007, 11:22:31 PM (17 years ago)
Author:
Armin Ronacher
Comment:

update

Legend:

Unmodified
Added
Removed
Modified
  • TracDev/Proposals/ThemePlugins

    v5 v6  
    77The current way is overriding `htdocs_location` in the trac config to bypass the shipped css and js files and inject new ones. Using the `site.html` file (talking of trac 0.11) you can then override the way the template is rendered. Because of the great Genshi API with XPATH support nearly everything is overrideable. But trac upgrades are a pain in the ass and distributing themes does not work.
    88
     9'''Solution'''
     10 * generic css classes (see below)
     11 * prefixing every css class with #x-trac
     12 * shipping two themes `trac` and `custom`, first one is the default trac theme, the latter a instance based theme, also see below
     13 * map theme providers to names using entrypoints
     14 * specify the used theme in the config with `[trac]\ntheme=trac`
     15
    916== Generic CSS Classes ==
    1017
     
    1522To create a list of generic classes an analysis of the current css files and genshi templates would be required.
    1623
    17 == Theme Provider ==
     24Also *all* css rules must be prefixed with `#x-trac` in order to avoid name collisions with existing templates of project webpages.
     25
     26== Example Theme Provider ==
     27This is an example theme provider for a distributable theme.
     28
    1829{{{
    1930#!python
     
    3849}}}
    3950
     51== Custom Theme Provider ==
     52This is the included the trac distribution in order to load templates from the instance folder.
     53
     54{{{
     55#!python
     56class CustomTheme(Component):
     57    implements(IThemeProvider)
     58
     59    # IThemeProvider
     60    def get_theme_htdocs_id(self):
     61        return 'site'
     62
     63    def get_theme_site_template(self):
     64        return 'site.html'
     65}}}
     66
    4067There will also be a `UserTheme` component class which uses the `site.html` from the trac instance folder and the template/htdocs folder in the instance. It's one of the both default themes:
    4168 * `trac` - the trac default theme
    4269 * `custom` - a special theme that forwards the htdocs/template lookups to the instance folders and uses the normal `site.html` as template.
    4370
    44 == Themes ==
    45 A theme looks like the theme defined above. So this could be the `mytheme/site.html`:
    46 
     71== Templates ==
     72Chances in the templates are quite small. The `layout.html` should look like this:
     73{{{
     74#!html
     75<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     76  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
     77<html xmlns="http://www.w3.org/1999/xhtml"
     78      xmlns:xi="http://www.w3.org/2001/XInclude"
     79      xmlns:py="http://genshi.edgewall.org/"
     80      py:strip="">
     81
     82  <head py:match="head">
     83    <title py:with="title = list(select('title/text()'))">
     84      <py:if test="title">${title} –</py:if>
     85      ${' – '.join(filter(None, [project.name, 'Trac']))}
     86    </title>
     87    <py:if test="chrome.links">
     88      <py:for each="rel, links in chrome.links.items()">
     89        <link rel="${rel}" py:for="link in links" py:attrs="link" />
     90      </py:for>
     91    </py:if>
     92    <py:if test="'SEARCH_VIEW' in perm" id="search">
     93      <link type="application/opensearchdescription+xml" rel="search"
     94            href="${href.search('opensearch')}" title="Search $project.name"/>
     95    </py:if>
     96    <script py:for="script in chrome.scripts"
     97            type="${script.type}" src="${script.href}"></script>
     98    ${Markup('&lt;!--[if lt IE 7]&gt;')}
     99    <script type="text/javascript" src="${href.chrome('common/js/ie_pre7_hacks.js')}"></script>
     100    ${Markup('&lt;![endif]--&gt;')}
     101    ${select("*[local-name() != 'title']")}
     102  </head>
     103
     104  <!-- navigation helper -->
     105  <div py:def="navigation(category)" id="${category}" class="nav">
     106    <ul py:if="chrome.nav[category]">
     107      <li py:for="idx, item in  enumerate(chrome.nav[category])"
     108          class="${classes(first_last(idx, chrome.nav[category]), active=item.active)}">${item.label}</li>
     109    </ul>
     110  </div>
     111
     112  <body py:match="body">
     113
     114    <!-- search form -->
     115    <form py:if="'SEARCH_VIEW' in perm" id="search"
     116          action="${href.search()}" method="get"><div>
     117      <label for="proj-search">Search:</label>
     118      <input type="text" id="proj-search" name="q" size="18" accesskey="f" value="" />
     119      <input type="submit" value="Search" />
     120      <input type="hidden" name="wiki" value="on" />
     121      <input type="hidden" name="changeset" value="on" />
     122      <input type="hidden" name="ticket" value="on" />
     123    </div></form>
     124
     125    <!-- meta navigation -->
     126    ${navigation('metanav')}
     127
     128    <!-- main navigation -->
     129    ${navigation('mainnav')}
     130
     131    <!-- trac body -->
     132    <div id="body">
     133      ${select('*|text()')}
     134
     135      <script type="text/javascript" py:if="chrome.late_links">
     136        <py:for each="link in chrome.late_links.get('stylesheet')">
     137          $.loadStyleSheet("${link.href}", "${link.title}");
     138        </py:for>
     139      </script>
     140      <script py:for="script in chrome.late_scripts"
     141              type="${script.type}" src="${script.href}"></script>
     142    </div>
     143
     144    <!-- alternative links -->
     145    <div id="altlinks" py:if="'alternate' in chrome.links">
     146      <h3>Download in other formats:</h3>
     147      <ul>
     148        <li py:for="idx, link in enumerate(chrome.links.alternate)"
     149            class="${first_last(idx, chrome.links.alternate)}">
     150          <a rel="nofollow" href="${link.href}" class="${link['class']}"
     151             py:content="link.title" />
     152        </li>
     153      </ul>
     154    </div>
     155
     156    <!-- footer -->
     157    <div id="footer"><hr/>
     158      <a id="tracpowered" href="http://trac.edgewall.org/"><img
     159        src="${href.chrome('common/trac_logo_mini.png')}" height="30"
     160        width="107" alt="Trac Powered"/></a>
     161      <p class="left">
     162        Powered by <a href="${href.about()}"><strong>Trac ${trac.version}</strong></a><br />
     163        By <a href="http://www.edgewall.org/">Edgewall Software</a>.
     164      </p>
     165      <p class="right">${chrome.footer}</p>
     166    </div>
     167  </body>
     168
     169  <xi:include href="${theme.get_theme_site_template()}"><xi:fallback /></xi:include>
     170</html>
     171}}}
     172
     173All the data elements moved into little div/ul boxes which are quite flat. The included filename is retrieved from the theme provider.
     174
     175== Default Theme ==
     176Here the default theme for the new structure.
     177
     178
     179{{{
     180#!python
     181class TracTheme(Component):
     182    implements(IThemeProvider)
     183
     184    # IThemeProvider
     185    def get_theme_htdocs_id(self):
     186        return 'common'
     187
     188    def get_theme_site_template(self):
     189        return 'trac_site.html'
     190}}}
     191
     192And here the `trac_site.html`. It's called this way so that we can load `site.html` from the instance folder. All other themes have to create a folder for their themes with an unique name.
    47193{{{
    48194#!text/html
     
    51197      xmlns:xi="http://www.w3.org/2001/XInclude" py:strip="">
    52198  <head py:match="head">
    53     <!-- load the default header stuff including the layout and plugin css files -->
    54199    ${select('*')}
    55     <!-- include theme css files -->
    56     <link rel="stylesheet" href="${theme.get_chrome_url('style/style.css')}" />
    57     <link rel="stylesheet" href="${theme.get_chrome_url('style/print.css')}" media="print" />
     200    <link rel="stylesheet" href="${theme.get_chrome_url('trac_style/css/style.css')}" />
     201    <link rel="stylesheet" href="${theme.get_chrome_url('trac_style/css/print.css')}" media="print" />
    58202  </head>
    59203  <body py:match="body">
    60     <!-- normal design used by the project webpage -->
    61     <div id="header">
    62       <h1>Project Trac</h1>
    63     </div>
    64     <div id="navigation">
    65       <li><a href="/">Index</a></li>
    66       <li><a href="/downloads">Downloads</a></li>
    67       <li><a href="/wiki/">Wiki</a></li>
    68       <li class="active"><a href="/trac/">Development</a></li>
    69     </div>
    70 
    71     <!-- embbed the trac -->
    72     <div id="body">
    73       <div id="x-trac">
    74         <div class="sidebar">
    75           <!-- combine all three navigation bars into one -->
    76           <ul class="navigation">
    77             <!-- include main navigation -->
    78             ${select('ul[@id="mainnav"]/*')}
    79             <!-- include meta nav -->
    80             ${select('ul[@id="metanav"]/*')}
    81             <!-- include ctx nav -->
    82             ${select('ul[@id="ctxtnav"]/*')}
    83           </ul>
    84             <li>Search
    85               ${select('div[@id="banner"]/form[@id="search"]')}
    86             </li>
    87           </ul>
    88           <!-- search box -->
    89           ${select('form[@id="search"]')}
     204    <div id="x-trac">
     205      <div id="banner">
     206        <div id="header" py:choose="">
     207          <a py:when="chrome.logo.src" id="logo" href="${chrome.logo.link}"><img
     208            src="${chrome.logo.src}" alt="${chrome.logo.alt}" /></a>
     209          <h1 py:otherwise=""><a href="${chrome.logo.link}">${project.name}</a></h1>
    90210        </div>
    91         <div class="trac-body">
    92           ${select('div[@id="main"]/*')}
    93         </div>
     211        ${select('ul[@id="metanav"]')}
    94212      </div>
    95     </div>
    96 
    97     <!-- project webpage footer -->
    98     <div id="footer">
    99       <a href="http://www.example.org/">Project</a> is open source software licensed
    100       under the <a href="http://www.gnu.org/copyleft/gpl.html">GPL</a>,
    101       development page powered by <a href="http://trac.edgewall.org/">Trac</a>.
     213      ${select('ul[@id="mainnav"]')}
     214
     215      <div id="main">
     216        ${select('div[@id="body"]/*')}
     217        ${select('div[@id="altlinks"]')}
     218      </div>
     219
     220      ${select('div[@id="footer"]')}
    102221    </div>
    103222  </body>