]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/Template.php
Beginnings of yet another refactor of the templates:
[SourceForge/phpwiki.git] / lib / Template.php
1 <?php rcs_id('$Id: Template.php,v 1.20 2002-01-15 23:40:25 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 ${VAR} to < ?php echo "$VAR"; ? >
35         //$orig[] = '/\${(\w[\w\d]*)}/';
36         //$repl[] = '< ?php echo "$\1"; ? >';
37         //$orig[] = '/\${(\w[\w\d]*)}/e';
38         //$repl[] = '$this->_getReplacement("\1")';
39         $orig[] = '/\${(\w[\w\d]*)}/';
40         $repl[] = '<?php echo $this->_toString($\1); ?>';
41
42         // Convert $VAR[ind] to < ?php echo "$VAR[ind]"; ? >
43         $orig[] = '/\$(\w[\w\d]*)\[([\w\d]+)\]/';
44         $repl[] = '<?php echo $this->_toString($\1["\2"]); ?>';
45         //$orig[] = '/\$(\w[\w\d]*)\[([\w\d]+)\]/e';
46         //$repl[] = '$this->_getReplacement("\1", "\2")';
47
48         // Convert $_("String") to < ?php echo htmlspecialchars(gettext("String")); ? >
49         $orig[] = '/\$_\(("(?:[^"\\\\]|\\.)*")\)/xs';
50         $repl[] = "<?php echo htmlspecialchars(gettext(\\1)); ?>";
51         
52         // Convert tag attributes like foo=_("String") to foo="String" (with gettext mapping).
53         $orig[] = '/( < \w [^>]* \w=)_\("((?:[^"\\\\]|\\.)*)"\)/xse';
54         $repl[] = '"\1\"" . htmlspecialchars(gettext("\2")) . "\""';
55         
56         return preg_replace($orig, $repl, $template);
57
58         $ret = preg_replace($orig, $repl, $template);
59         echo QElement('pre', $ret);
60         return $ret;
61     }
62
63     function _getReplacement($varname, $index = false) {
64         // FIXME: report missing vars.
65
66         echo "GET: $varname<br>\n";
67         
68         $vars = &$this->_vars;
69         if (!isset($vars[$varname]))
70             return false;
71
72         $value = $vars[$varname];
73         if ($index !== false)
74             @$value = $value[$index];
75
76         if (!is_string($value))
77             $value = $this->_toString($value);
78
79         // Quote '?' to avoid inadvertently inserting "<? php", "? >", or similar...
80         return str_replace('?', '&#63;', $value);
81     }
82     
83     function _toString ($val) {
84         $string_val = '';
85
86         if (is_array($val)) {
87             foreach ($val as $key => $item) {
88                 $val[$key] = $this->_toString($item);
89             }
90             return join("\n", $val);
91         }
92         elseif (is_object($val)) {
93             if (method_exists($val, 'ashtml'))
94                 return $val->asHTML();
95             elseif (method_exists($val, 'asstring'))
96                 return htmlspecialchars($val->asString());
97         }
98         /*
99         elseif (is_object($val) && method_exists($val, 'asString')) {
100             return $val->asString();
101         }
102         */
103         return (string) $val;
104     }
105     
106     /**
107      * Substitute HTML replacement text for tokens in template. 
108      *
109      * Constructs a new WikiTemplate based upon the named template.
110      *
111      * @access public
112      *
113      * @param $token string Name of token to substitute for.
114      *
115      * @param $replacement string Replacement HTML text.
116      */
117     function replace($varname, $value) {
118         $this->_vars[$varname] = $value;
119     }
120
121     /**
122      * Substitute text for tokens in template. 
123      *
124      * @access public
125      *
126      * @param $token string Name of token to substitute for.
127      *
128      * @param $replacement string Replacement text.
129      * The replacement text is run through htmlspecialchars()
130      * to escape any special characters.
131      */
132     function qreplace($varname, $value) {
133         $this->_vars[$varname] = htmlspecialchars($value);
134     }
135     
136
137     /**
138      * Include/remove conditional text in template.
139      *
140      * @access public
141      *
142      * @param $token string Conditional token name.
143      * The text within any matching if blocks (or single line ifs) will
144      * be included in the template expansion, while the text in matching
145      * negated if blocks will be excluded. 
146      */
147     /*
148     function setConditional($token, $value = true) {
149         $this->_iftoken[$token] = $value;
150     }
151     */
152     
153     function getExpansion($varhash = false) {
154         $savevars = $this->_vars;
155         if (is_array($varhash)) {
156             foreach ($varhash as $key => $val)
157                 $this->_vars[$key] = $val;
158         }
159         extract($this->_vars);
160         if (isset($this->_iftoken))
161             $_iftoken = $this->_iftoken;
162         
163         ob_start();
164
165         //$this->_dump_template();
166
167         global $ErrorManager;
168         $ErrorManager->pushErrorHandler(new WikiMethodCb($this, '_errorHandler'));
169         eval('?>' . $this->_munge_input($this->_tmpl));
170         $ErrorManager->popErrorHandler();
171
172         $html = ob_get_contents();
173         ob_end_clean();
174
175         return $html;
176     }
177
178     function printExpansion($args = false) {
179         echo $this->getExpansion($args);
180     }
181
182     // Debugging:
183     function _dump_template () {
184         $lines = explode("\n", $this->_munge_input($this->_tmpl));
185         echo "<pre>\n";
186         $n = 1;
187         foreach ($lines as $line)
188             printf("%4d  %s\n", $n++, htmlspecialchars($line));
189         echo "</pre>\n";
190     }
191
192     function _errorHandler($error) {
193         if (!preg_match('/: eval\(\)\'d code$/', $error->errfile))
194             return false;
195
196         // Hack alert: Ignore 'undefined variable' messages for variables
197         //  whose names are ALL_CAPS.
198         if (preg_match('/Undefined variable:\s*[_A-Z]+\s*$/', $error->errstr))
199             return true;
200         
201         $error->errfile = "In template";
202         $lines = explode("\n", $this->_tmpl);
203         if (isset($lines[$error->errline - 1]))
204             $error->errstr .= ":\n\t" . $lines[$error->errline - 1];
205         return $error;
206     }
207 };
208
209 class TemplateFile
210 extends Template
211 {
212     function TemplateFile($filename) {
213         $this->_template_file = $filename;
214         $fp = fopen($filename, "rb");
215         $data = fread($fp, filesize($filename));
216         fclose($fp);
217         $this->Template($data);
218     }
219 }
220
221 class WikiTemplate
222 extends TemplateFile
223 {
224     /**
225      * Constructor.
226      *
227      * Constructs a new WikiTemplate based upon the named template.
228      *
229      * @access public
230      *
231      * @param $template string Which template.
232      */
233     function WikiTemplate($template, $page_revision = false) {
234         global $templates;
235
236         $this->TemplateFile(FindFile($templates[$template]));
237
238         $this->_template_name = $template;
239
240         $this->setGlobalTokens();
241         if ($page_revision)
242             $this->setPageRevisionTokens($page_revision);
243     }
244     
245
246     function setPageTokens(&$page) {
247         /*
248         if ($page->get('locked'))
249             $this->setConditional('LOCK');
250         // HACK: note that EDITABLE may also be set in setWikiUserTokens.
251         if (!$page->get('locked'))
252             $this->setConditional('EDITABLE');
253         */
254         
255         $pagename = $page->getName();
256
257         $this->replace('page', $page);
258         $this->qreplace('CHARSET', CHARSET);
259         $this->qreplace('PAGE', $pagename);
260         $this->qreplace('PAGEURL', rawurlencode($pagename));
261         $this->qreplace('SPLIT_PAGE', split_pagename($pagename));
262         $this->qreplace('BROWSE_PAGE', WikiURL($pagename));
263
264         // FIXME: this is a bit of dangerous hackage.
265         $this->qreplace('ACTION', WikiURL($pagename, array('action' => '')));
266
267         // FIXME:?
268         //$this->replace_callback('HITS', array($page, 'getHitCount'));
269         //$this->replace_callback('RELATEDPAGES', array($page, 'getHitCount'));
270         //_dotoken('RELATEDPAGES', LinkRelatedPages($dbi, $name), $page);
271     }
272
273     function setPageRevisionTokens(&$revision) {
274         $page = & $revision->getPage();
275         
276         $current = & $page->getCurrentRevision();
277         $previous = & $page->getRevisionBefore($revision->getVersion());
278
279         $this->replace('IS_CURRENT',
280                        $current->getVersion() == $revision->getVersion());
281         
282         global $datetimeformat;
283         
284         $this->qreplace('LASTMODIFIED',
285                         strftime($datetimeformat, $revision->get('mtime')));
286
287         $this->qreplace('LASTAUTHOR', $revision->get('author'));
288         $this->qreplace('VERSION', $revision->getVersion());
289         $this->qreplace('CURRENT_VERSION', $current->getVersion());
290
291         $this->replace('revision', $revision);
292         
293         $this->setPageTokens($page);
294     }
295
296     function setWikiUserTokens(&$user) {
297         /*
298         if ( $user->is_admin() ) {
299             $this->setConditional('ADMIN');
300             $this->setConditional('EDITABLE');
301         }
302         if ( ! $user->is_authenticated() )
303             $this->setConditional('ANONYMOUS');
304         */
305         $this->replace('user', $user);
306         $this->qreplace('USERID', $user->id());
307
308         $prefs = $user->getPreferences();
309         $this->qreplace('EDIT_AREA_WIDTH', $prefs['edit_area.width']);
310         $this->qreplace('EDIT_AREA_HEIGHT', $prefs['edit_area.height']);
311     }
312
313     function setGlobalTokens () {
314         global $user, $logo, $RCS_IDS;
315         
316         // FIXME: This a a bit of dangerous hackage.
317         $this->qreplace('BROWSE', WikiURL(''));
318         $this->replace('CSS', WikiTemplate::__css_links());
319         $this->qreplace('WIKI_NAME', WIKI_NAME);
320
321         if (isset($user))
322             $this->setWikiUserTokens($user);
323         $this->qreplace('LOGO', DataURL($logo));
324         if (isset($RCS_IDS))
325             $this->qreplace('RCS_IDS', $RCS_IDS);
326
327         $this->qreplace('BASE_URL',
328                         // FIXME:
329                         //WikiURL($GLOBALS['pagename'], false, 'absolute_url')
330                         BaseURL()
331                         );
332
333         require_once('lib/ButtonFactory.php');
334         $this->replace('ButtonFactory', new ButtonFactory);
335     }
336
337     
338     function __css_links () {
339         global $CSS_URLS, $CSS_DEFAULT;
340         
341         $html = array();
342         foreach  ($CSS_URLS as $key => $val) {
343             $link = array('rel'     => 'stylesheet',
344                           'title'   => _($key),
345                           'href'    => DataURL($val),
346                           'type'    => 'text/css',
347                           'charset' => CHARSET);
348             // FIXME: why the charset?  Is a style-sheet really ever going to
349             // be other than US-ASCII?
350
351             // The next line is also used by xgettext to localise the word
352             // "Printer" used in the stylesheet's 'title' (see above).
353             if ($key == _("Printer")) {
354                 $link['media'] = 'print, screen';
355             }
356
357             if ($key != $CSS_DEFAULT) {
358                 $link['rel'] = 'alternate stylesheet';
359                 $html[] = Element('link', $link);
360             }
361             else {
362                 // Default CSS should be listed first, otherwise some
363                 // browsers (incl. Galeon) don't seem to treat it as
364                 // default (regardless of whether rel="stylesheet" or
365                 // rel="alternate stylesheet".)
366                 array_unshift($html, Element('link', $link));
367             }
368         }
369         return join("\n", $html);
370     }
371 };
372
373
374 /**
375  * Generate page contents using a template.
376  *
377  * This is a convenience function for backwards compatibility with the old
378  * GeneratePage().
379  *
380  * @param $template string name of the template (see config.php for list of names)
381  *
382  * @param $content string html content to put into the page
383  *
384  * @param $title string page title
385  *
386  * @param $page_revision object Current WikiDB_PageRevision, if available.
387  *
388  * @return string HTML expansion of template.
389  */
390 function GeneratePage($template, $content, $title, $page_revision = false) {
391     // require_once("lib/template.php");
392     $t = new WikiTemplate($template);
393     if ($page_revision)
394         $t->setPageRevisionTokens($page_revision);
395     $t->replace('CONTENT', $content);
396     $t->replace('TITLE', $title);
397     return $t->getExpansion();
398 }
399
400 // Local Variables:
401 // mode: php
402 // tab-width: 8
403 // c-basic-offset: 4
404 // c-hanging-comment-ender-p: nil
405 // indent-tabs-mode: nil
406 // End:   
407 ?>