2 rcs_id('$Id: PageType.php,v 1.31 2004-09-22 13:46:25 rurban Exp $');
4 Copyright 1999,2000,2001,2002,2003,2004 $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
19 along with PhpWiki; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 hash $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 TransformedText($page, &$text, $meta, $type_override=false) {
39 $pagetype = $type_override;
40 elseif (isset($meta['pagetype']))
41 $pagetype = $meta['pagetype'];
42 $this->_type = PageType::GetPageType($pagetype);
43 $this->CacheableMarkup($this->_type->transform($page, $text, $meta),
53 * A page type descriptor.
55 * Encapsulate information about page types.
57 * Currently the only information encapsulated is how to format
58 * the specific page type. In the future or capabilities may be
59 * added, e.g. the abilities to edit different page types (differently.)
60 * e.g. Support for the javascript htmlarea editor, which can only edit
63 * IMPORTANT NOTE: Since the whole PageType class gets stored (serialized)
64 * as of the cached marked-up page, it is important that the PageType classes
65 * not have large amounts of class data. (No class data is even better.)
69 * Get a page type descriptor.
71 * This is a static member function.
73 * @param string $pagetype Name of the page type.
74 * @return PageType An object which is a subclass of PageType.
76 function GetPageType ($name=false) {
79 $class = "PageType_" . (string)$name;
80 if (class_exists($class))
82 trigger_error(sprintf("PageType '%s' unknown", (string)$name),
84 return new PageType_wikitext;
88 * Get the name of this page type.
90 * @return string Page type name.
93 if (!preg_match('/^PageType_(.+)$/i', get_class($this), $m))
94 trigger_error("Bad class name for formatter(?)", E_USER_ERROR);
99 * Transform page text.
101 * @param WikiDB_Page $page
102 * @param string $text
103 * @param hash $meta Version meta-data
104 * @return XmlContent The transformed page text.
106 function transform(&$page, &$text, $meta) {
107 $fmt_class = 'PageFormatter_' . $this->getName();
108 $formatter = new $fmt_class($page, $meta);
109 return $formatter->format($text);
113 class PageType_wikitext extends PageType {}
114 class PageType_html extends PageType {}
115 class PageType_pdf extends PageType {}
117 class PageType_wikiblog extends PageType {}
118 class PageType_comment extends PageType {}
119 class PageType_wikiforum extends PageType {}
121 /* To prevent from PHP5 Fatal error: Using $this when not in object context */
122 function getInterwikiMap () {
123 $map = new PageType_interwikimap();
127 class PageType_interwikimap extends PageType
129 function PageType_interwikimap() {
131 $dbi = $request->getDbh();
132 $intermap = $this->_getMapFromWikiPage($dbi->getPage(_("InterWikiMap")));
133 if (!$intermap && defined('INTERWIKI_MAP_FILE'))
134 $intermap = $this->_getMapFromFile(INTERWIKI_MAP_FILE);
136 $this->_map = $this->_parseMap($intermap);
137 $this->_regexp = $this->_getRegexp();
140 function GetMap ($request = false) {
141 /*PHP5 Fatal error: Using $this when not in object context */
142 if (empty($this->_map)) {
143 $map = new PageType_interwikimap();
150 function getRegexp() {
151 return $this->_regexp;
154 function link ($link, $linktext = false) {
156 list ($moniker, $page) = split (":", $link, 2);
158 if (!isset($this->_map[$moniker])) {
159 return HTML::span(array('class' => 'bad-interwiki'),
160 $linktext ? $linktext : $link);
163 $url = $this->_map[$moniker];
165 // Urlencode page only if it's a query arg.
166 // FIXME: this is a somewhat broken heuristic.
167 $page_enc = strstr($url, '?') ? rawurlencode($page) : $page;
169 if (strstr($url, '%s'))
170 $url = sprintf($url, $page_enc);
174 $link = HTML::a(array('href' => $url));
177 $link->pushContent(PossiblyGlueIconToText('interwiki', "$moniker:"),
178 HTML::span(array('class' => 'wikipage'), $page));
179 $link->setAttr('class', 'interwiki');
182 $link->pushContent(PossiblyGlueIconToText('interwiki', $linktext));
183 $link->setAttr('class', 'named-interwiki');
190 function _parseMap ($text) {
191 if (!preg_match_all("/^\s*(\S+)\s+(\S+)/m",
192 $text, $matches, PREG_SET_ORDER))
194 foreach ($matches as $m) {
197 // add virtual "Upload:" moniker
198 if (empty($map['Upload'])) $map['Upload'] = getUploadDataPath();
200 // maybe add other monikers also (SemanticWeb link predicates?)
201 // Should they be defined in a RDF? (strict mode)
202 // Or should the SemanticWeb lib add it by itself?
203 // (adding only a subset dependent on the context = model)
207 function _getMapFromWikiPage ($page) {
208 if (! $page->get('locked'))
211 $current = $page->getCurrentRevision();
213 if (preg_match('|^<verbatim>\n(.*)^</verbatim>|ms',
214 $current->getPackedContent(), $m)) {
220 function _getMapFromFile ($filename) {
221 if (defined('WARN_NONPUBLIC_INTERWIKIMAP') and WARN_NONPUBLIC_INTERWIKIMAP) {
222 $error_html = sprintf(_("Loading InterWikiMap from external file %s."), $filename);
223 trigger_error( $error_html, E_USER_NOTICE );
225 if (!file_exists($filename)) {
226 $finder = new FileFinder();
227 $filename = $finder->findFile(INTERWIKI_MAP_FILE);
229 @$fd = fopen ($filename, "rb");
230 @$data = fread ($fd, filesize($filename));
236 function _getRegexp () {
238 return '(?:(?!a)a)'; // Never matches.
240 foreach (array_keys($this->_map) as $moniker)
241 $qkeys[] = preg_quote($moniker, '/');
242 return "(?:" . join("|", $qkeys) . ")";
247 /** How to transform text.
249 class PageFormatter {
252 * @param WikiDB_Page $page
253 * @param hash $meta Version meta-data.
255 function PageFormatter(&$page, $meta) {
256 $this->_page = $page;
257 $this->_meta = $meta;
258 if (!empty($meta['markup']))
259 $this->_markup = $meta['markup'];
261 $this->_markup = 2; // new policy: default = new markup (old crashes quite often)
264 function _transform(&$text) {
265 include_once('lib/BlockParser.php');
266 return TransformText($text, $this->_markup);
269 /** Transform the page text.
271 * @param string $text The raw page content (e.g. wiki-text).
272 * @return XmlContent Transformed content.
274 function format($text) {
275 trigger_error("pure virtual", E_USER_ERROR);
279 class PageFormatter_wikitext extends PageFormatter
281 function format(&$text) {
282 return HTML::div(array('class' => 'wikitext'),
283 $this->_transform($text));
287 class PageFormatter_interwikimap extends PageFormatter
289 function format($text) {
290 return HTML::div(array('class' => 'wikitext'),
291 $this->_transform($this->_getHeader($text)),
293 $this->_transform($this->_getFooter($text)));
296 function _getHeader($text) {
297 return preg_replace('/<verbatim>.*/s', '', $text);
300 function _getFooter($text) {
301 return preg_replace('@.*?(</verbatim>|\Z)@s', '', $text, 1);
305 $map = PageType_interwikimap::getMap();
309 function _formatMap() {
310 $map = $this->_getMap();
312 return HTML::p("<No map found>"); // Shouldn't happen.
315 $dbi = $request->getDbh();
317 $mon_attr = array('class' => 'interwiki-moniker');
318 $url_attr = array('class' => 'interwiki-url');
320 $thead = HTML::thead(HTML::tr(HTML::th($mon_attr, _("Moniker")),
321 HTML::th($url_attr, _("InterWiki Address"))));
322 foreach ($map as $moniker => $interurl) {
323 $rows[] = HTML::tr(HTML::td($mon_attr, new Cached_WikiLinkIfKnown($moniker)),
324 HTML::td($url_attr, HTML::tt($interurl)));
327 return HTML::table(array('class' => 'interwiki-map'),
329 HTML::tbody(false, $rows));
333 class FakePageRevision {
334 function FakePageRevision($meta) {
335 $this->_meta = $meta;
339 if (empty($this->_meta[$key]))
341 return $this->_meta[$key];
345 class PageFormatter_attach extends PageFormatter
349 // Display templated contents for wikiblog, comment and wikiforum
350 function format($text) {
351 if (empty($this->type))
352 trigger_error('PageFormatter_attach->format: $type missing');
353 include_once('lib/Template.php');
355 $tokens['CONTENT'] = $this->_transform($text);
356 $tokens['page'] = $this->_page;
357 $tokens['rev'] = new FakePageRevision($this->_meta);
359 $name = new WikiPageName($this->_page->getName());
360 $tokens[$this->prefix."_PARENT"] = $name->getParent();
362 $meta = $this->_meta[$this->type];
363 foreach(array('ctime', 'creator', 'creator_id') as $key)
364 $tokens[$this->prefix . "_" . strtoupper($key)] = $meta[$key];
366 return new Template($this->type, $request, $tokens);
370 class PageFormatter_wikiblog extends PageFormatter_attach {
371 var $type = 'wikiblog', $prefix = "BLOG";
373 class PageFormatter_comment extends PageFormatter_attach {
374 var $type = 'comment', $prefix = "COMMENT";
376 class PageFormatter_wikiforum extends PageFormatter_attach {
377 var $type = 'wikiforum', $prefix = "FORUM";
380 /** wikiabuse for htmlarea editing. not yet used.
382 * Warning! Once a page is edited with a htmlarea like control it is
383 * stored in HTML and cannot be converted back to WikiText as long as
384 * we have no HTML => WikiText or any other interim format (WikiExchangeFormat e.g. Xml)
385 * converter. So it has a viral effect and certain plugins will not work anymore.
386 * But a lot of wikiusers seem to like it.
388 class PageFormatter_html extends PageFormatter
390 function _transform($text) {
393 function format($text) {
399 * FIXME. not yet used
401 class PageFormatter_pdf extends PageFormatter
404 function _transform($text) {
405 include_once('lib/BlockParser.php');
406 return TransformText($text, $this->_markup);
409 // one page or set of pages?
410 // here we try to format only a single page
411 function format($text) {
412 include_once('lib/Template.php');
414 $tokens['page'] = $this->_page;
415 $tokens['CONTENT'] = $this->_transform($text);
416 $pagename = $this->_page->getName();
418 // This is a XmlElement tree, which must be converted to PDF
420 // We can make use of several pdf extensions. This one - fpdf
421 // - is pure php and very easy, but looks quite ugly and has a
422 // terrible interface, as terrible as most of the othes.
423 // The closest to HTML is htmldoc which needs an external cgi
425 // We use a custom HTML->PDF class converter from PHPWebthings
426 // to be able to use templates for PDF.
427 require_once('lib/fpdf.php');
428 require_once('lib/pdf.php');
431 $pdf->SetTitle($pagename);
432 $pdf->SetAuthor($this->_page->get('author'));
433 $pdf->SetCreator(WikiURL($pagename,false,1));
434 $pdf->AliasNbPages();
437 $pdf->SetFont('Times','',12);
438 //$pdf->SetFont('Arial','B',16);
440 // PDF pagelayout from a special template
441 $template = new Template('pdf', $request, $tokens);
442 $pdf->ConvertFromHTML($template);
444 // specify filename, destination
445 $pdf->Output($pagename.".pdf",'I'); // I for stdin or D for download
447 // Output([string name [, string dest]])
456 // c-hanging-comment-ender-p: nil
457 // indent-tabs-mode: nil