Idea for simple template system that use wiki macros within html source and separated python logic. This template is major part of MoinViewControllerArchitecture

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:

Benefits:

Template format

There are two options for this kind of template:

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:

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:

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:

   1 def macro_user_bookmarks(self):
   2     return [Page(item).link_to() for item in request.user.bookmarks()]
   3 def macro_show_bookmarks(self):
   4     return request.user.show_bookmarks

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


CategoryFeatureRequest

MoinMoin: WikiTemplateLanguage (last edited 2007-10-29 19:11:46 by localhost)