4 * Copyright 1999,2000,2001,2002,2003,2004,2005,2006 $ThePhpWikiProgrammingTeam
6 * This file is part of PhpWiki.
8 * PhpWiki is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * PhpWiki is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with PhpWiki; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 require_once 'lib/CachedMarkup.php';
25 /** A cacheable formatted wiki page.
27 class TransformedText extends CacheableMarkup
30 * @param WikiDB_Page $page
31 * @param string $text The packed page revision content.
32 * @param array $meta The version meta-data.
33 * @param string $type_override For markup of page using a different
34 * pagetype than that specified in its version meta-data.
36 function __construct($page, $text, $meta, $type_override = '')
40 $pagetype = $type_override;
41 elseif (isset($meta['pagetype']))
42 $pagetype = $meta['pagetype'];
43 $this->_type = PageType::GetPageType($pagetype);
44 parent::__construct($this->_type->transform($page, $text, $meta),
55 * A page type descriptor.
57 * Encapsulate information about page types.
59 * Currently the only information encapsulated is how to format
60 * the specific page type. In the future or capabilities may be
61 * added, e.g. the abilities to edit different page types (differently.)
62 * e.g. Support for the javascript htmlarea editor, which can only edit
65 * IMPORTANT NOTE: Since the whole PageType class gets stored (serialized)
66 * as of the cached marked-up page, it is important that the PageType classes
67 * not have large amounts of class data. (No class data is even better.)
69 abstract class PageType
72 * Get a page type descriptor.
74 * @param string $name Name of the page type.
75 * @return PageType An object which is a subclass of PageType.
77 static function GetPageType($name = '')
81 $class = "PageType_" . (string)$name;
82 if (class_exists($class))
84 trigger_error(sprintf("PageType ā%sā unknown", (string)$name),
86 return new PageType_wikitext;
90 * Get the name of this page type.
92 * @return string Page type name.
96 if (!preg_match('/^PageType_(.+)$/i', get_class($this), $m))
97 trigger_error("Bad class name for formatter(?)", E_USER_ERROR);
102 * Transform page text.
104 * @param WikiDB_Page $page
105 * @param string $text
106 * @param array $meta Version meta-data
107 * @return XmlContent The transformed page text.
109 function transform(&$page, &$text, $meta)
111 $fmt_class = 'PageFormatter_' . $this->getName();
112 $formatter = new $fmt_class($page, $meta);
113 return $formatter->format($text);
117 class PageType_wikitext extends PageType
121 class PageType_html extends PageType
125 class PageType_wikiblog extends PageType
129 class PageType_comment extends PageType
133 class PageType_wikiforum extends PageType
137 class PageType_MediaWiki extends PageType
141 /* To prevent from PHP5 Fatal error: Using $this when not in object context */
142 function getInterwikiMap($pagetext = false, $force = false)
145 if (empty($map) or $force)
146 $map = new PageType_interwikimap($pagetext);
150 class PageType_interwikimap extends PageType
152 function __construct($pagetext = false)
155 * @var WikiRequest $request
160 $dbi = $request->getDbh();
161 $page = $dbi->getPage(__("InterWikiMap"));
162 if ($page->get('locked')) {
163 $current = $page->getCurrentRevision();
164 $pagetext = $current->getPackedContent();
165 $intermap = $this->_getMapFromWikiText($pagetext);
166 } elseif ($page->exists()) {
167 trigger_error(_("WARNING: InterWikiMap page is unlocked, so not using those links."));
172 $intermap = $this->_getMapFromWikiText($pagetext);
174 if (!$intermap && defined('INTERWIKI_MAP_FILE'))
175 $intermap = $this->_getMapFromFile(INTERWIKI_MAP_FILE);
177 $this->_map = $this->_parseMap($intermap);
178 $this->_regexp = $this->_getRegexp();
181 function GetMap($pagetext = false)
183 /*PHP5 Fatal error: Using $this when not in object context */
184 if (empty($this->_map)) {
185 $map = new PageType_interwikimap($pagetext);
194 return $this->_regexp;
197 function link($link, $linktext = false)
200 list ($moniker, $page) = explode(":", $link, 2);
202 if (!isset($this->_map[$moniker])) {
203 return HTML::span(array('class' => 'bad-interwiki'),
204 $linktext ? $linktext : $link);
207 $url = $this->_map[$moniker];
208 // localize Upload:links for WIKIDUMP
209 if (!empty($WikiTheme->DUMP_MODE) and $moniker == 'Upload') {
211 include_once 'lib/config.php';
212 $url = getUploadFilePath();
213 // calculate to a relative local path to /uploads for PDF images.
214 $doc_root = $request->get("DOCUMENT_ROOT");
215 $ldir = NormalizeLocalFileName($url);
216 $wikiroot = NormalizeLocalFileName('');
218 $ldir = strtolower($ldir);
219 $doc_root = strtolower($doc_root);
220 $wikiroot = strtolower($wikiroot);
222 if (string_starts_with($ldir, $doc_root)) {
223 $link_prefix = substr($url, strlen($doc_root));
224 } elseif (string_starts_with($ldir, $wikiroot)) {
225 $link_prefix = NormalizeWebFileName(substr($url, strlen($wikiroot)));
229 // Urlencode page only if it's a query arg.
230 // FIXME: this is a somewhat broken heuristic.
231 if ($moniker == 'Upload') {
233 $page = rawurldecode($page);
235 $page_enc = strstr($url, '?') ? rawurlencode($page) : $page;
237 if (strstr($url, '%s'))
238 $url = sprintf($url, $page_enc);
242 // Encode spaces in '[[Help:Reini Urban]]'
243 // but not in '[[Upload:logo.jpg size=40x25 align=center]]'
244 if ($moniker != 'Upload') {
245 $url = str_replace(' ', '%20', $url);
248 $link = HTML::a(array('href' => $url));
251 $link->pushContent(PossiblyGlueIconToText('interwiki', "$moniker:"),
252 HTML::span(array('class' => 'wikipage'), $page));
253 $link->setAttr('class', 'interwiki');
255 $link->pushContent(PossiblyGlueIconToText('interwiki', $linktext));
256 $link->setAttr('class', 'named-interwiki');
262 private function _parseMap($text)
265 * @var WikiRequest $request
269 if (!preg_match_all("/^\s*(\S+)\s+(.+)$/m",
270 $text, $matches, PREG_SET_ORDER)
274 foreach ($matches as $m) {
278 // Add virtual monikers: "Upload:" "Talk:" "User:", ":"
279 // and expand special variables %u, %b, %d
281 // Upload: Should be expanded later to user-specific upload dirs.
282 // In the Upload plugin, not here: Upload:ReiniUrban/uploaded-file.png
283 if (empty($map['Upload'])) {
284 $map['Upload'] = getUploadDataPath();
286 // User:ReiniUrban => ReiniUrban or Users/ReiniUrban
287 // Can be easily overriden by a customized InterWikiMap:
289 if (empty($map["User"])) {
292 // Talk:UserName => UserName/Discussion
293 // Talk:PageName => PageName/Discussion as default, which might be overridden
294 if (empty($map["Talk"])) {
295 $pagename = $request->getArg('pagename');
296 // against PageName/Discussion/Discussion
297 if (string_ends_with($pagename, '/' . _("Discussion")))
300 $map["Talk"] = "%s" . '/' . _("Discussion");
303 foreach (array('Upload', 'User', 'Talk') as $special) {
304 // Expand special variables:
307 // %d => iso8601 DateTime
308 // %s is expanded later to the pagename
309 if (strstr($map[$special], '%u'))
310 $map[$special] = str_replace($map[$special],
312 $request->_user->_userid);
313 if (strstr($map[$special], '%b'))
314 $map[$special] = str_replace($map[$special],
317 if (strstr($map[$special], '%d'))
318 $map[$special] = str_replace($map[$special],
320 // such as 2003-01-11T14:03:02+00:00
327 private function _getMapFromWikiText($pagetext)
329 if (preg_match('|^<verbatim>\n(.*)^</verbatim>|ms', $pagetext, $m)) {
335 private function _getMapFromFile($filename)
337 if (defined('WARN_NONPUBLIC_INTERWIKIMAP') and WARN_NONPUBLIC_INTERWIKIMAP) {
338 $error_html = sprintf(_("Loading InterWikiMap from external file %s."),
340 trigger_error($error_html, E_USER_NOTICE);
342 if (!file_exists($filename)) {
343 $finder = new FileFinder();
344 $filename = $finder->findFile(INTERWIKI_MAP_FILE);
346 @$fd = fopen($filename, "rb");
347 @$data = fread($fd, filesize($filename));
353 private function _getRegexp()
356 return '(?:(?!a)a)'; // Never matches.
359 foreach (array_keys($this->_map) as $moniker)
360 $qkeys[] = preg_quote($moniker, '/');
361 return "(?:" . join("|", $qkeys) . ")";
365 /** How to transform text.
367 abstract class PageFormatter
370 * @param WikiDB_Page $page
371 * @param array $meta Version meta-data hash.
373 function __construct(&$page, $meta)
375 $this->_page = $page;
376 $this->_meta = $meta;
379 function _transform($text)
381 include_once 'lib/BlockParser.php';
382 return TransformText($text);
385 /** Transform the page text.
387 * @param string $text The raw page content (e.g. wiki-text).
388 * @return XmlContent Transformed content.
390 abstract function format($text);
393 class PageFormatter_wikitext extends PageFormatter
395 function format($text)
397 return HTML::div(array('class' => 'wikitext'),
398 $this->_transform($text));
402 class PageFormatter_interwikimap extends PageFormatter
404 function format($text)
406 return HTML::div(array('class' => 'wikitext'),
407 $this->_transform($this->_getHeader($text)),
408 $this->_formatMap($text),
409 $this->_transform($this->_getFooter($text)));
412 protected function _getHeader($text)
414 return preg_replace('/<verbatim>.*/s', '', $text);
417 protected function _getFooter($text)
419 return preg_replace('@.*?(</verbatim>|\Z)@s', '', $text, 1);
422 protected function _getMap($pagetext)
424 $map = getInterwikiMap($pagetext, 'force');
428 protected function _formatMap($pagetext)
430 $map = $this->_getMap($pagetext);
432 return HTML::p("No interwiki map found"); // Shouldn't happen.
434 $mon_attr = array('class' => 'interwiki-moniker');
435 $url_attr = array('class' => 'interwiki-url');
437 $thead = HTML::thead(HTML::tr(HTML::th($mon_attr, _("Moniker")),
438 HTML::th($url_attr, _("InterWiki Address"))));
440 foreach ($map as $moniker => $interurl) {
441 $rows[] = HTML::tr(HTML::td($mon_attr, new Cached_WikiLinkIfKnown($moniker)),
442 HTML::td($url_attr, HTML::samp($interurl)));
445 return HTML::table(array('class' => 'interwiki-map'),
447 HTML::tbody(false, $rows));
451 class FakePageRevision
453 function __construct($meta)
455 $this->_meta = $meta;
460 if (empty($this->_meta[$key]))
462 return $this->_meta[$key];
466 // abstract base class
467 class PageFormatter_attach extends PageFormatter
469 public $type, $prefix;
471 // Display templated contents for wikiblog, comment and wikiforum
472 function format($text)
474 if (empty($this->type))
475 trigger_error('PageFormatter_attach->format: $type missing');
476 include_once 'lib/Template.php';
478 $tokens['CONTENT'] = $this->_transform($text);
479 $tokens['page'] = $this->_page;
480 $tokens['rev'] = new FakePageRevision($this->_meta);
482 $name = new WikiPageName($this->_page->getName());
483 $tokens[$this->prefix . "_PARENT"] = $name->getParent();
485 $meta = $this->_meta[$this->type];
486 foreach (array('ctime', 'creator', 'creator_id') as $key)
487 $tokens[$this->prefix . "_" . strtoupper($key)] = $meta[$key];
489 return new Template($this->type, $request, $tokens);
493 class PageFormatter_wikiblog extends PageFormatter_attach
495 public $type = 'wikiblog', $prefix = "BLOG";
498 class PageFormatter_comment extends PageFormatter_attach
500 public $type = 'comment', $prefix = "COMMENT";
503 class PageFormatter_wikiforum extends PageFormatter_attach
505 public $type = 'wikiforum', $prefix = "FORUM";
508 /** wikiabuse for htmlarea editing. not yet used.
510 * Warning! Once a page is edited with a htmlarea like control it is
511 * stored in HTML and cannot be converted back to WikiText as long as
512 * we have no HTML => WikiText or any other interim format (WikiExchangeFormat e.g. XML)
513 * converter. See lib/HtmlParser.php for ongoing work on that.
514 * So it has a viral effect and certain plugins will not work anymore.
515 * But a lot of wikiusers seem to like it.
517 class PageFormatter_html extends PageFormatter
519 function _transform($text)
524 function format($text)
530 class PageFormatter_MediaWiki extends PageFormatter
532 function _transform($text)
534 include_once 'lib/BlockParser.php';
535 // Expand leading tabs.
536 $text = expand_tabs($text);
538 $input = new BlockParser_Input($text);
539 $output = $this->ParsedBlock($input);
540 return new XmlContent($output->getContent());
543 function format($text)
545 return HTML::div(array('class' => 'wikitext'),
546 $this->_transform($text));
554 // c-hanging-comment-ender-p: nil
555 // indent-tabs-mode: nil