Idea for simple template system that use wiki macros within html source and separated python logic. This template is major part of MoinViewControllerArchitecture
Contents
Description
This is the basic idea:
wiki markup -> text_python -> [processing request] -> final html
Note that I'm NOT talking about pages. I'm talking about the user interface parts and internal html stuff that we send again and again for every request. The current code that does this is found in the theme package, but also in request, Page, some macros.
Each view in the wiki will be defined as a wiki template. The wiki engine will render these templates to text_python format, then in runtime, complied templates will render the final html, entering the dynamic parts, like page name, page content etc.
here a simple example:
## Login View <html> <head> <title>Welcome to [[WikiName]]</title> [[css_stylesheet(common)]] [[css_stylesheet(screen)]] [[css_stylesheet(print)]] </head> <body> <h1>Welcome to [[WikiName]]!</h1> ## here comes a form with user name and password boxes... <p>If you have any difficulties please contact [[WikiAdmin]]</p> </body> </html>
After you save this such page, it is rendered by the text_python formatter, to something like this:
request.write("""<html> <head> <title>Welcome to %(wiki_name)s</title> """ % request.cfg And so on ...
This Python code, when run with request object and other model object it needs access to, will fetch the dynamic data from the model.
Summary:
- No logic inside the template
- Replacement markup are wiki macros, which we have a parser for
- Macros return either static html or the code needed to fetch data at runtime
Benefits:
- We don't have to learn a new template language
- its very easy to add new macros, since we already have a plug in system, just put one in the plugin directory.
- Template compiled to the very fast text_python, all template parsing is done once.
Template format
There are two options for this kind of template:
- Use wiki markup to describe the template
- Use pure html and only some features of wiki markup, like macro expanding
I think the second option is better for a template, since we want to control every little detail of the html code, and in the same time, we don't want to mix logic and model related stuff in the template. So we can use a very simple parser that format the text as is, and call each macro found in the text.
Or we use both methods, stuff that make more sense in pure html will be written using pass-through processor, and other text will use the standard parser formatter.
Validating templates
One problem I see how to validate templates, since <ol>items</ol> is not valid. In order to check the template html code, you will have to render it. We can add a validate method to the controller base class, that render a test page with the template, and validate it through the web. This can be standard test on our unit tests, that validates all templates automatically each time you run make test.
Macros
Macros are used to insert code that fetch data from other code, the wiki or the user. Data can be:
- Static - like data in some other source. Let a macro duplicate this kind of data
- Dynamic, like the user name that change for every request.
The templates can come uncompiled as part of the code. On the first call to each template, it will be compiled and cached. Static wiki data will will be added to the template, and will never be fetched again, unless the wiki data has changed. Dynamic data will be fetch by the template code for every request.
Macros can be custom macros defined in the controller class or standard macros that we reuse on any template.
content
This is the main macro used to insert content into the wiki view. This macro will call the current action, which write the content for that specific action. Every request will have a current action, either the implicit action "viewpage" or explicit like "search"
<!-- above: html code for standard user interface --> [[content]] <!-- bellow: more html code -->
Examples:
- viewpage action will write the content of the requested page
- search action will write the search results
- create action will write a page creation content, with list of similar pages etc
- edit action will write a page editor - text area with page editor toolbar
title
Will return the current action title. For example, a search action will return 'Search for "Bugs"'
toolbar
Will return the current action toolbar, which is different for view page, edit page or search results. (No more raise NoMoinMoinFooter)
include
Will exec other templates, so we can reuse parts. Each template will have ts own controller class.
HTML widgets
For the html designer, the best option would be to just write the html. But when a part of the page does not appear always, like user page trail, you must use some custom template language like DTML or PHP and like, and you get a lot of mess.
Instead we can use several widgets that we use again and again, like a list or a table. Each can have arguments so you can control the basic display from the template itself. The goal is to let the html designer work without getting into the Python code, with a simple syntax.
The html designer will say: I want here a list with the user page trail, with id="pagetrail", I will design this later in css. The programmer will just write the pagetrail method to return a list of pages, and macro_ol will format this list and write the results.
Maybe something like this:
[[ol data="pagetrail" id="pagetrail"]]
This will call the standard macro-ol of the controller class, with keyword arguments id, class and data. macro_ol will get the data from self.pagetrail and add the id attribute to the ol tag.
I'm using html like syntax, since it will be easier to html designers, its cleaner than the function call syntax used in moin and it seems to be very easy to parse.
This system can be very easy to explain - when you want dynamic data, use this_syntax. Tell the programmer what data you want using the "data" attribute, and specify any html attributes you want, so you can design the object later with CSS. This can be enough for most cases, since you can get any child element by #id tag or .class tag.
HTML widget internals
An html widget can be constructed as template itself:
<!-- Expandable List with a title template The div is the list title, when you click on the title, the list collapse or expand. --> <div class="expandable-list" onClick="toggleList(this)"[[attributes]]>[[title]]</div> <ol style="display:[[display]];"> [[items]] </ol>
The controller for this template:
1 class ExpandableList(Controller):
2 def __init__(self, title, data, default, display, **attr):
3 self._title = title
4 self._data = data
5 self._default = defalt
6 self._display = display
7 self._attr = attr
8 def macro_attributes(self):
9 return [' %s="%s"' % (k, v) for k, v in self._attr.items()]
10 def macro_title(self):
11 return self._title
12 def macro_items(self):
13 items = ['<li>%s</li>' % item for item in self._data()]
14 If not items:
15 return "<li class="hint">self._default</li>"
16 return '\n'.join(items)
17 def macro_display(self):
18 if callable(self._display):
19 if self._display():
20 return ''
21 else:
22 return 'none'
23 return self._display
And here is a user bookmarks list on the main template:
[[expandable-list title="Bookmarks" data="user_bookmarks" default="Add bookmarks here" display="show_bookmarks" id="bookmarks" ]]
And the code on the main controller:
And the final html:
<div class="expandable-list" onClick="toggleList(this)" id="bookmarks">Bookmarks</div> <ol style="display:none;"> <li><a href="/ThisPage">ThisPage</a></li> <li><a href="/ThatPage">ThatPage</a></li> </ol>
Then html designer can design both a new theme or the theme building blocks, without touching the code.
Maybe its an overkill to write this using a template, but its means that every building block of the system is a text_python fragment, and we can include it inside any other text_python fragment, or add together different fragments. And its a very consistent way to work.
Implementation
Here is a simple template to play with:
<html> <head> <title>[[title]]</title> </head> <body> <h1>[[title]]</h1> <p>This is a very simple test template. The next fragment will come from a macro.</p> <ul id="filelist"> [[filelist]] </ul> <address>[[author]]</address> </body> </html>
Using macro syntax
Download the template module template.py and the test template test.txt and play.
Using Python string replacement syntax
Looking at the above I was reminded of a string template class I was experimenting with recently, which took advantage of python's native %(name)s string substitution in a slightly novel way. Here's an adaptation of the above code, which may be faster then the implementation.
Download and play with template2.py. Use the same test.txt - embed a %(recursive)s in there somewhere to excercise the recursive macro, whose utility I'm still not sure of.
The question is, is [[macro]] format in these templates useful, or does it just confuse matters with normal wiki macros unneccessarily?
-- DoranMoppert
Its indeed a clever way to replace strings, but I'm not sure its the best way to do templates, as you don't have enough control as the template writer like macros with variables. The second argument for macros, is to create a system that works as a wiki page, so we can mix templates with page content, both defined in similar way, and both working together.
About confusion with standard wiki macros, I don't see the big problem. Template macros will not be available to the wiki, wiki macros will be available to both. We can use simple naming conventions to prevent confusion.
-- NirSoffer 2004-08-10 19:53:49