]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/Template.php
More infiltration of new object-based HTML generation.
[SourceForge/phpwiki.git] / lib / Template.php
1 <?php rcs_id('$Id: Template.php,v 1.24 2002-01-21 06:55:47 dairiki Exp $');
2
3 require_once("lib/ErrorManager.php");
4 require_once("lib/WikiPlugin.php");
5
6 //FIXME: This is a mess and needs to be refactored.
7 //  (In other words: this is all in a state of flux, so don't count on any
8 //   of this being the same tomorrow...)
9
10 class Template
11 {
12     function Template($tmpl) {
13         //$this->_tmpl = $this->_munge_input($tmpl);
14         $this->_tmpl = $tmpl;
15         $this->_vars = array();
16     }
17
18     function _munge_input($template) {
19         // Expand "< ?plugin-* ...? >"
20         preg_match_all('/<\?plugin.*?\?>/s', $template, $m);
21         global $dbi, $request;  // FIXME: no globals?
22         $pluginLoader = new WikiPluginLoader;
23         foreach (array_unique($m[0]) as $plugin_pi) {
24             $orig[] = '/' . preg_quote($plugin_pi, '/') . '/s';
25             // Plugin args like 'description=_("Get backlinks")' get
26             // gettexted.
27             // FIXME: move this to WikiPlugin.php.
28             $translated_pi = preg_replace('/(\s\w+=)_\("((?:[^"\\\\]|\\.)*)"\)/xse',
29                                           '"\1\"" . gettext("\2") . "\""',
30                                           $plugin_pi);
31             $repl[] = $pluginLoader->expandPI($translated_pi, $dbi, $request);
32         }
33
34         // Convert < ?= expr ? > to < ?php $this->_print(expr); ? >
35         $orig[] = '/<\?=(.*?)\?>/s';
36         $repl[] = '<?php $this->_print(\1);?>';
37         
38         // Convert tag attributes like foo=_("String") to foo="String" (with gettext mapping).
39         $orig[] = '/( < \w [^>]* \w=)_\("((?:[^"\\\\]|\\.)*)"\)/xse';
40         $repl[] = '"\1\"" . htmlspecialchars(gettext("\2")) . "\""';
41         
42         return preg_replace($orig, $repl, $template);
43
44         //$ret = preg_replace($orig, $repl, $template);
45         //echo QElement('pre', $ret);
46         //return $ret;
47     }
48
49     function _getReplacement($varname, $index = false) {
50         // FIXME: report missing vars.
51
52         echo "GET: $varname<br>\n";
53         
54         $vars = &$this->_vars;
55         if (!isset($vars[$varname]))
56             return false;
57
58         $value = $vars[$varname];
59         if ($index !== false)
60             @$value = $value[$index];
61
62         if (!is_string($value))
63             $value = $this->_toString($value);
64
65         // Quote '?' to avoid inadvertently inserting "<? php", "? >", or similar...
66         return str_replace('?', '&#63;', $value);
67     }
68     
69     function _print ($val) {
70         $string_val = '';
71
72         if (is_array($val)) {
73             $n = 0;
74             foreach ($val as $item) {
75                 if ($n++)
76                     echo "\n";
77                 $this->_print($item);
78             }
79         }
80         elseif (isa($val, 'Template')) {
81             // Expand sub-template with defaults from this template.
82             $val->printExpansion($this->_vars);
83         }
84         else
85             printXML($val);
86     }
87     
88     /**
89      * Substitute HTML replacement text for tokens in template. 
90      *
91      * Constructs a new WikiTemplate based upon the named template.
92      *
93      * @access public
94      *
95      * @param $token string Name of token to substitute for.
96      *
97      * @param $replacement string Replacement HTML text.
98      */
99     function replace($varname, $value) {
100         $this->_vars[$varname] = $value;
101     }
102
103     /**
104      * Substitute text for tokens in template. 
105      *
106      * FIXME: this is now identical to Template::replace();
107      * @access public
108      *
109      * @param $token string Name of token to substitute for.
110      *
111      * @param $replacement string Replacement text.
112      * The replacement text is run through htmlspecialchars()
113      * to escape any special characters.
114      */
115     function qreplace($varname, $value) {
116         //$this->_vars[$varname] = htmlspecialchars($value);
117         $this->_vars[$varname] = $value;
118     }
119     
120
121     /**
122      * Include/remove conditional text in template.
123      *
124      * @access public
125      *
126      * @param $token string Conditional token name.
127      * The text within any matching if blocks (or single line ifs) will
128      * be included in the template expansion, while the text in matching
129      * negated if blocks will be excluded. 
130      */
131     /*
132     function setConditional($token, $value = true) {
133         $this->_iftoken[$token] = $value;
134     }
135     */
136     
137     function printExpansion ($defaults = false) {
138         $vars = &$this->_vars;
139         if ($defaults !== false) {
140             $save_vars = $vars;
141             if (!is_array($defaults)) {
142                 if (!isset($vars['CONTENT']))
143                     $vars['CONTENT'] = $defaults;
144             }
145             else {
146                 foreach ($defaults as $key => $val)
147                     if (!isset($vars[$key]))
148                         $vars[$key] = $val;
149             }
150         }
151         extract($vars);
152         
153         //$this->_dump_template();
154
155         global $ErrorManager;
156         $ErrorManager->pushErrorHandler(new WikiMethodCb($this, '_errorHandler'));
157
158         eval('?>' . $this->_munge_input($this->_tmpl));
159
160         $ErrorManager->popErrorHandler();
161
162         if (isset($save_vars))
163             $vars = $save_vars;
164     }
165
166     function getExpansion ($defaults = false) {
167         ob_start();
168         $this->printExpansion($defaults);
169         $html = ob_get_contents();
170         ob_end_clean();
171         return $html;
172     }
173
174     // Debugging:
175     function _dump_template () {
176         $lines = explode("\n", $this->_munge_input($this->_tmpl));
177         echo "<pre>\n";
178         $n = 1;
179         foreach ($lines as $line)
180             printf("%4d  %s\n", $n++, htmlspecialchars($line));
181         echo "</pre>\n";
182     }
183
184     function _errorHandler($error) {
185         if (!preg_match('/: eval\(\)\'d code$/', $error->errfile))
186             return false;
187
188         // Hack alert: Ignore 'undefined variable' messages for variables
189         //  whose names are ALL_CAPS.
190         if (preg_match('/Undefined variable:\s*[_A-Z]+\s*$/', $error->errstr))
191             return true;
192         
193         $error->errfile = "In template";
194         $lines = explode("\n", $this->_tmpl);
195         if (isset($lines[$error->errline - 1]))
196             $error->errstr .= ":\n\t" . $lines[$error->errline - 1];
197         return $error;
198     }
199 };
200
201 class TemplateFile
202 extends Template
203 {
204     function TemplateFile($filename) {
205         $this->_template_file = $filename;
206         $fp = fopen($filename, "rb");
207         $data = fread($fp, filesize($filename));
208         fclose($fp);
209         $this->Template($data);
210     }
211
212 }
213
214 class WikiTemplate
215 extends TemplateFile
216 {
217     /**
218      * Constructor.
219      *
220      * Constructs a new WikiTemplate based upon the named template.
221      *
222      * @access public
223      *
224      * @param $template string Which template.
225      */
226     function WikiTemplate($template, $page_revision = false) {
227         global $Theme;
228         $this->TemplateFile($Theme->findTemplate($template));
229         $this->_template_name = $template;
230         $this->setGlobalTokens();
231         if ($page_revision)
232             $this->setPageRevisionTokens($page_revision);
233     }
234
235     
236     function setPageTokens(&$page) {
237         /*
238         if ($page->get('locked'))
239             $this->setConditional('LOCK');
240         // HACK: note that EDITABLE may also be set in setWikiUserTokens.
241         if (!$page->get('locked'))
242             $this->setConditional('EDITABLE');
243         */
244         
245         $pagename = $page->getName();
246
247         $this->replace('page', $page);
248         $this->qreplace('PAGE', $pagename);
249         $this->qreplace('PAGEURL', rawurlencode($pagename));
250         $this->qreplace('SPLIT_PAGE', split_pagename($pagename));
251         $this->qreplace('BROWSE_PAGE', WikiURL($pagename));
252
253         // FIXME: this is a bit of dangerous hackage.
254         $this->qreplace('ACTION', WikiURL($pagename, array('action' => '')));
255
256         // FIXME:?
257         //$this->replace_callback('HITS', array($page, 'getHitCount'));
258         //$this->replace_callback('RELATEDPAGES', array($page, 'getHitCount'));
259         //_dotoken('RELATEDPAGES', LinkRelatedPages($dbi, $name), $page);
260     }
261
262     function setPageRevisionTokens(&$revision) {
263         $page = & $revision->getPage();
264         
265         $current = & $page->getCurrentRevision();
266         $previous = & $page->getRevisionBefore($revision->getVersion());
267
268         $this->replace('IS_CURRENT',
269                        $current->getVersion() == $revision->getVersion());
270         
271         global $datetimeformat;
272         
273         //$this->qreplace('LASTMODIFIED',
274         //              strftime($datetimeformat, $revision->get('mtime')));
275
276         $this->qreplace('LASTAUTHOR', $revision->get('author'));
277         $this->qreplace('VERSION', $revision->getVersion());
278         $this->qreplace('CURRENT_VERSION', $current->getVersion());
279
280         $this->replace('revision', $revision);
281         
282         $this->setPageTokens($page);
283     }
284
285     function setWikiUserTokens(&$user) {
286         $this->replace('user', $user);
287         $this->qreplace('USERID', $user->getId());
288
289         $prefs = $user->getPreferences();
290         $this->qreplace('EDIT_AREA_WIDTH', $prefs['edit_area.width']);
291         $this->qreplace('EDIT_AREA_HEIGHT', $prefs['edit_area.height']);
292     }
293
294     function setGlobalTokens () {
295         global $user, $RCS_IDS, $Theme, $request;
296         
297         // FIXME: This a a bit of dangerous hackage.
298         $this->replace('Theme', $Theme);
299         $this->qreplace('BROWSE', WikiURL(''));
300         $this->qreplace('WIKI_NAME', WIKI_NAME);
301
302         if (isset($user))
303             $this->setWikiUserTokens($user);
304         if (isset($RCS_IDS))
305             $this->qreplace('RCS_IDS', $RCS_IDS);
306
307         require_once('lib/ButtonFactory.php');
308         $this->replace('ButtonFactory', new ButtonFactory);
309
310         $query_args = $request->getArgs();
311         unset($query_args['login']);
312         $this->replace('query_args', $query_args);
313     }
314 };
315
316
317 /**
318  * Generate page contents using a template.
319  *
320  * This is a convenience function for backwards compatibility with the old
321  * GeneratePage().
322  *
323  * @param $template string name of the template (see config.php for list of names)
324  *
325  * @param $content string html content to put into the page
326  *
327  * @param $title string page title
328  *
329  * @param $page_revision object Current WikiDB_PageRevision, if available.
330  *
331  * @return string HTML expansion of template.
332  */
333 function GeneratePage($template, $content, $title, $page_revision = false) {
334     // require_once("lib/template.php");
335     // FIXME: More hackage.  Really GeneratePage should go away, at some point.
336     assert($template == 'MESSAGE');
337     $t = new WikiTemplate('top');
338     $t->qreplace('TITLE', $title);
339     $t->qreplace('HEADER', $title);
340     if ($page_revision)
341         $t->setPageRevisionTokens($page_revision);
342     $t->replace('CONTENT', $content);
343     return $t->getExpansion();
344 }
345
346 // Local Variables:
347 // mode: php
348 // tab-width: 8
349 // c-basic-offset: 4
350 // c-hanging-comment-ender-p: nil
351 // indent-tabs-mode: nil
352 // End:   
353 ?>