| 1 | = Theme Plugins = |
| 2 | |
| 3 | This proposal describes a way trac could get a working theme support. The main idea is that every project which uses trac should have it's own theme. Or at least each major trac installation. |
| 4 | |
| 5 | The 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. |
| 6 | |
| 7 | == Generic CSS Classes == |
| 8 | |
| 9 | One of the first things which should be done is cleaning up the shipped css files. They should use more generic classes and documented classes. Each plugin developer should be able to use them too in his plugins. |
| 10 | |
| 11 | Also the trac core would only ship layout css files without color definitions. The default theme would then add the color definitions for the elements (links, browser etc). Currently the only way to change the trac colors is copying the default css files and replace all the color hex values with own ones. |
| 12 | |
| 13 | To create a list of generic classes an analysis of the current css files and genshi templates would be required. |
| 14 | |
| 15 | == A CSS Preprocessor == |
| 16 | |
| 17 | CSS does not allow variables and constants unfortunately. Because of that a CSS preprocessor is the only way to use a value twice in a file. If a request in a chrome folder goes to `foo.css` and the `foo.css` file does not exist the chrome system would look for a `foo.css.tmpl`. If that file exists trac loads it as genshi text template and executes it. |
| 18 | |
| 19 | This template would have access to the current theme provider described below. |
| 20 | |
| 21 | == A Theme Provider == |
| 22 | |
| 23 | The most important part of a theme would be the theme provider. Because trac supports plugins which can add their own css files it's pretty hard to keep a global style without overriding the css files of the plugins too. |
| 24 | |
| 25 | {{{ |
| 26 | #!python |
| 27 | class IThemeProvider(Interface): |
| 28 | |
| 29 | def get_theme_info(): |
| 30 | """ |
| 31 | Return the theme information for the current theme. |
| 32 | Has to return a dict with the following keys: |
| 33 | |
| 34 | `author` |
| 35 | the author of the theme in the form ``name <email>`` |
| 36 | `url` |
| 37 | url for the theme. |
| 38 | `name` |
| 39 | name of the theme. |
| 40 | """ |
| 41 | |
| 42 | def get_theme_color(name): |
| 43 | """ |
| 44 | Return the hexadecimal value for a given name. The actual color |
| 45 | names should be specified on a wiki page in the trac.edgewall.org |
| 46 | wiki so that plugin developers could get informations about the |
| 47 | allowed colors. |
| 48 | |
| 49 | If a name is not overriden by the theme it has to return a |
| 50 | default color. (for example ``#000000``) |
| 51 | """ |
| 52 | }}} |
| 53 | |
| 54 | == site.html == |
| 55 | At the moment trac instances have a `htdocs/` and a `template/` folder. Both will stay, but the `site.html` in the template folder goes. |
| 56 | |
| 57 | The `site.html` file will be part of a theme. If a theme implements not only the `IThemeProvider` but also the `ITemplateProvider` it can add a `htdocs` and `template` folder to the searchpath. In the latter would now be the `site.html` file. |
| 58 | |
| 59 | As long as the `layout.html` file does not change you can override nearly everything in the `site.html` file by using XPATH: |
| 60 | |
| 61 | {{{ |
| 62 | #!text/html |
| 63 | <html xmlns="http://www.w3.org/1999/xhtml" |
| 64 | xmlns:py="http://genshi.edgewall.org/" |
| 65 | xmlns:xi="http://www.w3.org/2001/XInclude" py:strip=""> |
| 66 | <head py:match="head"> |
| 67 | <!-- load the default header stuff including the layout and plugin css files --> |
| 68 | ${select('*')} |
| 69 | <!-- load own css files. not sure if get_css_url would be |
| 70 | possible, maybe use the env stuff --> |
| 71 | <link rel="stylesheet" href="${theme.get_css_url('style.css')}" /> |
| 72 | <link rel="stylesheet" href="${theme.get_css_url('print.css')}" media="print" /> |
| 73 | </head> |
| 74 | <body py:match="body"> |
| 75 | <!-- here a very complex example --> |
| 76 | <div id="project-content"> |
| 77 | <div id="project-header"> |
| 78 | <h1>Project Trac</h1> |
| 79 | </div> |
| 80 | <div id="project-sidebar"> |
| 81 | <!-- combine all three navigation bars into one --> |
| 82 | <ul><li><ul> |
| 83 | <!-- include main navigation --> |
| 84 | ${select('div[@id="mainnav"]/ul/*')} |
| 85 | <li><a href="http://www.example.org/">Project Webpage</a></ul></li> |
| 86 | <li>User<ul> |
| 87 | ${select('div[@id="metanav"]/ul/*')} |
| 88 | </ul></li> |
| 89 | <!-- this is a bit hackish, there should be a nicer way for it --> |
| 90 | <li py:if="unicode(select('div[@id=\'main\']/div[@id=\'ctxtnav\']/ul/*'))"> |
| 91 | Actions<ul> |
| 92 | ${select('div[@id="main"]/div[@id="ctxtnav"]/ul/*')} |
| 93 | </ul></li> |
| 94 | <li>Search |
| 95 | ${select('div[@id="banner"]/form[@id="search"]')} |
| 96 | </li> |
| 97 | </ul> |
| 98 | </div> |
| 99 | <div id="project-body"> |
| 100 | <!-- include trac body output --> |
| 101 | ${select('div[@id="main"]/*[@id!="ctxtnav"]')} |
| 102 | </div> |
| 103 | <div id="project-footer"> |
| 104 | <a href="http://www.example.org/">Project</a> is open source software licensed |
| 105 | under the <a href="http://www.gnu.org/copyleft/gpl.html">GPL</a>, |
| 106 | development page powered by <a href="http://trac.edgewall.org/">Trac</a>. |
| 107 | </div> |
| 108 | </div> |
| 109 | </body> |
| 110 | </html> |
| 111 | }}} |
| 112 | |
| 113 | That of course requires that the `layout.html` and css classes/ids don't change after a trac update. |