13 | | The following documentation is by no means a replacement for the excellent documentation you'll find in the Genshi site. |
14 | | You should go there for a more in-depth understanding of how Genshi actually works and should be used. Here we'll focus on the differences with Clearsilver. |
15 | | |
16 | | For migrating your own templates, a good way to start is to learn by example. |
17 | | Compare the Clearsilver templates found in source:trunk/templates@3831 and their corresponding Genshi ones in source:sandbox/genshi/templates@3831. |
18 | | |
19 | | Then, in the same way, compare the various web_ui.py controllers you'll find in both branches. |
20 | | |
21 | | === Changes in the template syntax === |
22 | | |
23 | | Most of the time, the porting is a straightforward operation. |
24 | | ==== expand a variable ==== |
25 | | - Clearsilver [[xml(<b><?cs var:the_variable ?></b>)]] |
26 | | - Genshi [[xml(<b>$the_variable</b>)]] |
27 | | ==== expand a simple computation ==== |
28 | | - Clearsilver [[xml(<b><?cs var:the_variable+1 ?></b>)]] |
29 | | - Genshi [[xml(<b>${the_variable+1}</b>)]] |
30 | | ==== include another template ==== |
31 | | - Clearsilver [[xml(<?cs include:the_file.cs ?>)]] |
32 | | - Genshi [[xml(<xi:include href="the_file.html"><xi:fallback/></xi:include>)]] |
33 | | ==== simple if...then (no else) ==== |
34 | | - Clearsilver [[xml(<?cs if:flag ?><b>OK</b><?cs /if ?>)]] |
35 | | - Genshi [[xml(<py:if test="flag"><b>OK</b></py:if>)]] or simply [[xml(<b py:if="flag">OK</b>)]] |
36 | | ==== if...then...else ==== |
37 | | - Clearsilver |
38 | | {{{ |
39 | | #!xml |
40 | | <?cs if:flag ?> |
41 | | <b>OK</b> |
42 | | <?cs else ?> |
43 | | <i>!!!</i> |
44 | | <?cs /if ?> |
45 | | }}} |
46 | | - Genshi |
47 | | {{{ |
48 | | #!xml |
49 | | <py:choose test="flag"> |
50 | | <py:when test="True"> |
51 | | <b>OK</b> |
52 | | </py:when> |
53 | | <py:otherwise> |
54 | | <i>!!!</i> |
55 | | </py:otherwise> |
56 | | </py:choose> |
57 | | }}} |
58 | | or simply: |
59 | | {{{ |
60 | | #!xml |
61 | | <py:choose> |
62 | | <b py:when="flag">OK</b> |
63 | | <i py:otherwise="">!!!</i> |
64 | | </py:choose> |
65 | | }}} |
66 | | The <py:choose>/<py:when>/<py:otherwise> is a bit heavy-weight for a simple if/else, but on the other hand, the construct is more general (think switch/case, or the equivalent choose/when/otherwise in XSLT). |
67 | | |
68 | | ==== iterate over a collection ==== |
69 | | - Clearsilver |
70 | | {{{ |
71 | | #!xml |
72 | | <ul><?cs |
73 | | each:element = list ?> |
74 | | <li><?cs var:element ?></li><?cs |
75 | | /each ?> |
76 | | </ul> |
77 | | }}} |
78 | | - Genshi |
79 | | {{{ |
80 | | #!xml |
81 | | <ul> |
82 | | <py:for each="element in list"> |
83 | | <li>$element</li> |
84 | | </py:for> |
85 | | </ul> |
86 | | }}} |
87 | | or simply: |
88 | | {{{ |
89 | | #!xml |
90 | | <ul> |
91 | | <li py:for="element in list">$element</li> |
92 | | </ul> |
93 | | }}} |
94 | | |
95 | | ==== define a macro ==== |
96 | | - Clearsilver |
97 | | {{{ |
98 | | #!xml |
99 | | <?cs def:entry(key, val)?> |
100 | | <dt><?cs var:key ?></dt><dd><?cs var:val ?></dd> |
101 | | <?cs /def ?> |
102 | | }}} |
103 | | - Genshi |
104 | | {{{ |
105 | | #!xml |
106 | | <py:def function="entry(key, val='--')"> |
107 | | <dt>$key</dt><dd>$val</dd> |
108 | | </py:def> |
109 | | }}} |
110 | | As you can see, with Genshi it's also easy to specify default values for the macro arguments. |
111 | | |
112 | | ==== set a variable ==== |
113 | | - Clearsilver |
114 | | {{{ |
115 | | #!xml |
116 | | <?cs set:count = len(collection) ?> |
117 | | We have <?cs if:count > 10 ?>too much<?cs else ?><?cs var:count ?><?cs /if ?> elements. |
118 | | }}} |
119 | | - Genshi |
120 | | {{{ |
121 | | #!xml |
122 | | <py:with vars="count = len(collection)"> |
123 | | We have ${count > 10 and 'too much' or count} elements. |
124 | | </py:with> |
125 | | }}} |
126 | | Note that we had to use `>` in Genshi, instead of directly `>` as in Clearsilver. |
127 | | |
128 | | ==== Examples ==== |
129 | | Let's first take a simple full-contained example from the Trac source, the simple index.cs / index.html templates. |
130 | | |
131 | | - Clearsilver [source:trunk/templates/index.cs@3725 index.cs]: |
132 | | {{{ |
133 | | #!xml |
134 | | <!DOCTYPE html |
135 | | PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" |
136 | | "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> |
137 | | <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> |
138 | | <head><title>Available Projects</title></head> |
139 | | <body><h1>Available Projects</h1><ul><?cs |
140 | | each:project = projects ?><li><?cs |
141 | | if:project.href ?> |
142 | | <a href="<?cs var:project.href ?>" title="<?cs var:project.description ?>"> |
143 | | <?cs var:project.name ?></a><?cs |
144 | | else ?> |
145 | | <small><?cs var:project.name ?>: <em>Error</em> <br /> |
146 | | (<?cs var:project.description ?>)</small><?cs |
147 | | /if ?> |
148 | | </li><?cs |
149 | | /each ?></ul></body> |
150 | | </html> |
151 | | }}} |
152 | | - Genshi [source:sandbox/genshi/templates/index.html@3728 index.html]: |
153 | | {{{ |
154 | | #!xml |
155 | | <!DOCTYPE html |
156 | | PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" |
157 | | "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> |
158 | | <html xmlns="http://www.w3.org/1999/xhtml" |
159 | | xmlns:py="http://genshi.edgewall.org/" |
160 | | xmlns:xi="http://www.w3.org/2001/XInclude"> |
161 | | |
162 | | <head><title>Available Projects</title></head> |
163 | | |
164 | | <body> |
165 | | <h1>Available Projects</h1> |
166 | | <ul> |
167 | | <li py:for="project in projects" py:choose=""> |
168 | | <a py:when="project.href" href="$project.href" title="$project.description"> |
169 | | $project.name</a> |
170 | | <py:otherwise> |
171 | | <small>$project.name: <em>Error</em> <br /> |
172 | | ($project.description)</small> |
173 | | </py:otherwise> |
174 | | </li> |
175 | | </ul> |
176 | | </body> |
177 | | </html> |
178 | | }}} |
179 | | |
180 | | Some remarks: |
181 | | - Note the possible use of multiple genshi attributes in the same element |
182 | | (in the above, the `<li>` element has a `py:for` and a `py:choose` attribute). |
183 | | - When there's only one element to output conditionally, one should use a genshi attribute |
184 | | (the `py:for="project in projets"` and the `py:when="project.href"` in the above). |
185 | | Otherwise, one should use a genshi element (here, the `<py:otherwise>`). |
186 | | - In this small example, there's no common Trac layout used (as the index is a bit special). |
187 | | For how a "normal" template looks like, see for example |
188 | | [source:sandbox/genshi/templates/diff_form.html@3831 diff_form.html], another small template. |
189 | | |
190 | | Note that a Genshi template can usually be rendered directly to have a taste of how it will look like: |
191 | | ---- |
192 | | {{{ |
193 | | #!html |
194 | | <h1>Available Projects</h1> |
195 | | <ul> |
196 | | <li py:for="project in projects" py:choose=""> |
197 | | <a py:when="project.href" href="$project.href" title="$project.description"> |
198 | | $project.name</a> |
199 | | <py:otherwise> |
200 | | <small>$project.name: <em>Error</em> <br /> |
201 | | ($project.description)</small> |
202 | | </py:otherwise> |
203 | | </li> |
204 | | </ul> |
205 | | }}} |
206 | | ---- |
207 | | This comes from an important property of Genshi templates: '''they must themselves be well-formed XML documents'''. |
208 | | |
209 | | That was not a constraint in Clearsilver, and sometimes the logic in those templates took "advantage" of that, e.g. by conditionally inserting end/start pairs of tags. Such templates are the hardest to port, because you actually have to think a bit... See for example the [source:sandbox/genshi/templates/query.html@3831 query.html] template. |
210 | | Of course, the great benefit of this constraint is that you'll end up quite naturally with well-formed content, which was far from being a trivial achievement using Clearsilver templates. Granted, you could still insert directly some non well-formed `Markup` data in your template, but again, if you use the [http://genshi.edgewall.org/wiki/Documentation/builder.html genshi.builder] ''tag'' facility for this, that's hardly a risk. |
211 | | |
212 | | |
213 | | Another example from Trac, a bit more complex. |
214 | | This illustrates how to use `<py:def>` and `<py:with>`, to convert a Clearsilver macro using `<?cs def: ?>` and `<?cs set: ?>`. |
215 | | |
216 | | - Clearsilver |
217 | | {{{ |
218 | | #!xml |
219 | | def:browser_path_links(path, file) ?><?cs |
220 | | <?cs set:first = #1 ?><?cs |
221 | | each:part = path ?><?cs |
222 | | set:last = name(part) == len(path) - #1 ?><a<?cs |
223 | | if:first ?> class="first" title="Go to root directory"<?cs |
224 | | set:first = #0 ?><?cs |
225 | | else ?> title="View <?cs var:part.name ?>"<?cs |
226 | | /if ?> href="<?cs var:part.href ?>"><?cs var:part.name ?></a><?cs |
227 | | if:!last ?><span class="sep">/</span><?cs /if ?><?cs |
228 | | /each ?><?cs |
229 | | /def ?><?cs |
230 | | }}} |
231 | | - Genshi |
232 | | {{{ |
233 | | #!xml |
234 | | <py:def function="browser_path_links(path_links)"> |
235 | | <py:for each="idx, part in enumerate(path_links)"> |
236 | | <py:with vars="first = (idx == 0); last = (idx == len(path_links) - 1)"> |
237 | | <a class="${first and 'first' or None}" |
238 | | title="${first and 'Go to root directory' or 'View ' + part.name}" |
239 | | href="$part.href">$part.name</a> |
240 | | <py:if test="not last"><span class="sep">/</span></py:if> |
241 | | </py:with> |
242 | | </py:for> |
243 | | </py:def> |
244 | | }}} |
245 | | |
246 | | |
247 | | === Changes in the controllers === |
248 | | |
249 | | ==== Implementing the `IRequestHandler` interface ==== |
250 | | Previously, all the data fed to a template had to be placed inside the `req.hdf` HDF wrapper object. |
251 | | With Genshi, the data for the template is basically a `dict`, which has to be returned by `process_request` |
252 | | at the same time as the template name. Check [source:sandbox/genshi/trac/wiki/web_ui.py@3831 trac.wiki.web_ui] for an example. |
253 | | |
254 | | ==== Generating content ==== |
255 | | When one wants to directly render a template, the Chrome component facilities should be used. |
256 | | Check the [source:sandbox/genshi/trac/web/chrome.py@3730#L422 Chrome.load_template] |
257 | | and `render_method` methods. Note however that this API is still rapidly evolving. |
258 | | |
259 | | Usage examples: |
260 | | - Implementing [source:sandbox/genshi/trac/ticket/query.py@3725#L740 Ticket Query] macro (''table'' mode) |
261 | | - Sending [source:sandbox/genshi/trac/notification.py@3725#L99 notification] e-mails |
| 13 | You can start porting your plugins [TracDev/PortingFromClearSilverToGenshi from Clearsilver to Genshi]. |