1 <?php rcs_id('$Id: Template.php,v 1.20 2002-01-15 23:40:25 dairiki Exp $');
3 require_once("lib/ErrorManager.php");
4 require_once("lib/WikiPlugin.php");
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...)
12 function Template($tmpl) {
13 //$this->_tmpl = $this->_munge_input($tmpl);
15 $this->_vars = array();
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
27 // FIXME: move this to WikiPlugin.php.
28 $translated_pi = preg_replace('/(\s\w+=)_\("((?:[^"\\\\]|\\.)*)"\)/xse',
29 '"\1\"" . gettext("\2") . "\""',
31 $repl[] = $pluginLoader->expandPI($translated_pi, $dbi, $request);
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); ?>';
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")';
48 // Convert $_("String") to < ?php echo htmlspecialchars(gettext("String")); ? >
49 $orig[] = '/\$_\(("(?:[^"\\\\]|\\.)*")\)/xs';
50 $repl[] = "<?php echo htmlspecialchars(gettext(\\1)); ?>";
52 // Convert tag attributes like foo=_("String") to foo="String" (with gettext mapping).
53 $orig[] = '/( < \w [^>]* \w=)_\("((?:[^"\\\\]|\\.)*)"\)/xse';
54 $repl[] = '"\1\"" . htmlspecialchars(gettext("\2")) . "\""';
56 return preg_replace($orig, $repl, $template);
58 $ret = preg_replace($orig, $repl, $template);
59 echo QElement('pre', $ret);
63 function _getReplacement($varname, $index = false) {
64 // FIXME: report missing vars.
66 echo "GET: $varname<br>\n";
68 $vars = &$this->_vars;
69 if (!isset($vars[$varname]))
72 $value = $vars[$varname];
74 @$value = $value[$index];
76 if (!is_string($value))
77 $value = $this->_toString($value);
79 // Quote '?' to avoid inadvertently inserting "<? php", "? >", or similar...
80 return str_replace('?', '?', $value);
83 function _toString ($val) {
87 foreach ($val as $key => $item) {
88 $val[$key] = $this->_toString($item);
90 return join("\n", $val);
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());
99 elseif (is_object($val) && method_exists($val, 'asString')) {
100 return $val->asString();
103 return (string) $val;
107 * Substitute HTML replacement text for tokens in template.
109 * Constructs a new WikiTemplate based upon the named template.
113 * @param $token string Name of token to substitute for.
115 * @param $replacement string Replacement HTML text.
117 function replace($varname, $value) {
118 $this->_vars[$varname] = $value;
122 * Substitute text for tokens in template.
126 * @param $token string Name of token to substitute for.
128 * @param $replacement string Replacement text.
129 * The replacement text is run through htmlspecialchars()
130 * to escape any special characters.
132 function qreplace($varname, $value) {
133 $this->_vars[$varname] = htmlspecialchars($value);
138 * Include/remove conditional text in template.
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.
148 function setConditional($token, $value = true) {
149 $this->_iftoken[$token] = $value;
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;
159 extract($this->_vars);
160 if (isset($this->_iftoken))
161 $_iftoken = $this->_iftoken;
165 //$this->_dump_template();
167 global $ErrorManager;
168 $ErrorManager->pushErrorHandler(new WikiMethodCb($this, '_errorHandler'));
169 eval('?>' . $this->_munge_input($this->_tmpl));
170 $ErrorManager->popErrorHandler();
172 $html = ob_get_contents();
178 function printExpansion($args = false) {
179 echo $this->getExpansion($args);
183 function _dump_template () {
184 $lines = explode("\n", $this->_munge_input($this->_tmpl));
187 foreach ($lines as $line)
188 printf("%4d %s\n", $n++, htmlspecialchars($line));
192 function _errorHandler($error) {
193 if (!preg_match('/: eval\(\)\'d code$/', $error->errfile))
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))
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];
212 function TemplateFile($filename) {
213 $this->_template_file = $filename;
214 $fp = fopen($filename, "rb");
215 $data = fread($fp, filesize($filename));
217 $this->Template($data);
227 * Constructs a new WikiTemplate based upon the named template.
231 * @param $template string Which template.
233 function WikiTemplate($template, $page_revision = false) {
236 $this->TemplateFile(FindFile($templates[$template]));
238 $this->_template_name = $template;
240 $this->setGlobalTokens();
242 $this->setPageRevisionTokens($page_revision);
246 function setPageTokens(&$page) {
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');
255 $pagename = $page->getName();
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));
264 // FIXME: this is a bit of dangerous hackage.
265 $this->qreplace('ACTION', WikiURL($pagename, array('action' => '')));
268 //$this->replace_callback('HITS', array($page, 'getHitCount'));
269 //$this->replace_callback('RELATEDPAGES', array($page, 'getHitCount'));
270 //_dotoken('RELATEDPAGES', LinkRelatedPages($dbi, $name), $page);
273 function setPageRevisionTokens(&$revision) {
274 $page = & $revision->getPage();
276 $current = & $page->getCurrentRevision();
277 $previous = & $page->getRevisionBefore($revision->getVersion());
279 $this->replace('IS_CURRENT',
280 $current->getVersion() == $revision->getVersion());
282 global $datetimeformat;
284 $this->qreplace('LASTMODIFIED',
285 strftime($datetimeformat, $revision->get('mtime')));
287 $this->qreplace('LASTAUTHOR', $revision->get('author'));
288 $this->qreplace('VERSION', $revision->getVersion());
289 $this->qreplace('CURRENT_VERSION', $current->getVersion());
291 $this->replace('revision', $revision);
293 $this->setPageTokens($page);
296 function setWikiUserTokens(&$user) {
298 if ( $user->is_admin() ) {
299 $this->setConditional('ADMIN');
300 $this->setConditional('EDITABLE');
302 if ( ! $user->is_authenticated() )
303 $this->setConditional('ANONYMOUS');
305 $this->replace('user', $user);
306 $this->qreplace('USERID', $user->id());
308 $prefs = $user->getPreferences();
309 $this->qreplace('EDIT_AREA_WIDTH', $prefs['edit_area.width']);
310 $this->qreplace('EDIT_AREA_HEIGHT', $prefs['edit_area.height']);
313 function setGlobalTokens () {
314 global $user, $logo, $RCS_IDS;
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);
322 $this->setWikiUserTokens($user);
323 $this->qreplace('LOGO', DataURL($logo));
325 $this->qreplace('RCS_IDS', $RCS_IDS);
327 $this->qreplace('BASE_URL',
329 //WikiURL($GLOBALS['pagename'], false, 'absolute_url')
333 require_once('lib/ButtonFactory.php');
334 $this->replace('ButtonFactory', new ButtonFactory);
338 function __css_links () {
339 global $CSS_URLS, $CSS_DEFAULT;
342 foreach ($CSS_URLS as $key => $val) {
343 $link = array('rel' => 'stylesheet',
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?
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';
357 if ($key != $CSS_DEFAULT) {
358 $link['rel'] = 'alternate stylesheet';
359 $html[] = Element('link', $link);
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));
369 return join("\n", $html);
375 * Generate page contents using a template.
377 * This is a convenience function for backwards compatibility with the old
380 * @param $template string name of the template (see config.php for list of names)
382 * @param $content string html content to put into the page
384 * @param $title string page title
386 * @param $page_revision object Current WikiDB_PageRevision, if available.
388 * @return string HTML expansion of template.
390 function GeneratePage($template, $content, $title, $page_revision = false) {
391 // require_once("lib/template.php");
392 $t = new WikiTemplate($template);
394 $t->setPageRevisionTokens($page_revision);
395 $t->replace('CONTENT', $content);
396 $t->replace('TITLE', $title);
397 return $t->getExpansion();
404 // c-hanging-comment-ender-p: nil
405 // indent-tabs-mode: nil