]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/PageType.php
Optimize PearDB _extract_version_data and _extract_page_data.
[SourceForge/phpwiki.git] / lib / PageType.php
1 <?php // -*-php-*-
2 rcs_id('$Id: PageType.php,v 1.36 2004-11-28 20:42:18 rurban Exp $');
3 /*
4  Copyright 1999,2000,2001,2002,2003,2004 $ThePhpWikiProgrammingTeam
5
6  This file is part of PhpWiki.
7
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.
12
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.
17
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
21  */
22
23 require_once('lib/CachedMarkup.php');
24
25 /** A cacheable formatted wiki page.
26  */
27 class TransformedText extends CacheableMarkup {
28     /** Constructor.
29      *
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.
35      */
36     function TransformedText($page, $text, $meta, $type_override=false) {
37         $pagetype = false;
38         if ($type_override)
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),
44                                $page->getName());
45     }
46
47     function getType() {
48         return $this->_type;
49     }
50 }
51
52 /**
53  * A page type descriptor.
54  *
55  * Encapsulate information about page types.
56  *
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 
61  * pure HTML.
62  *
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.)
66  */
67 class PageType {
68     /**
69      * Get a page type descriptor.
70      *
71      * This is a static member function.
72      *
73      * @param string $pagetype  Name of the page type.
74      * @return PageType  An object which is a subclass of PageType.
75      */
76     function GetPageType ($name=false) {
77         if (!$name)
78             $name = 'wikitext';
79         $class = "PageType_" . (string)$name;
80         if (class_exists($class))
81             return new $class;
82         trigger_error(sprintf("PageType '%s' unknown", (string)$name),
83                       E_USER_WARNING);
84         return new PageType_wikitext;
85     }
86
87     /**
88      * Get the name of this page type.
89      *
90      * @return string  Page type name.
91      */
92     function getName() {
93         if (!preg_match('/^PageType_(.+)$/i', get_class($this), $m))
94             trigger_error("Bad class name for formatter(?)", E_USER_ERROR);
95         return $m[1];
96     }
97
98     /**
99      * Transform page text.
100      *
101      * @param WikiDB_Page $page
102      * @param string $text
103      * @param hash $meta Version meta-data
104      * @return XmlContent The transformed page text.
105      */
106     function transform(&$page, &$text, $meta) {
107         $fmt_class = 'PageFormatter_' . $this->getName();
108         $formatter = new $fmt_class($page, $meta);
109         return $formatter->format($text);
110     }
111 }
112
113 class PageType_wikitext extends PageType {}
114 class PageType_html extends PageType {}
115 class PageType_pdf extends PageType {}
116
117 class PageType_wikiblog extends PageType {}
118 class PageType_comment extends PageType {}
119 class PageType_wikiforum extends PageType {}
120
121 /* To prevent from PHP5 Fatal error: Using $this when not in object context */
122 function getInterwikiMap ($pagetext = false) {
123     $map = new PageType_interwikimap($pagetext);
124     return $map;
125 }
126
127 class PageType_interwikimap extends PageType
128 {
129     function PageType_interwikimap($pagetext = false) {
130         if (!$pagetext) {
131             $dbi = $GLOBALS['request']->getDbh();
132             $page = $dbi->getPage(_("InterWikiMap"));
133             if ($page->get('locked')) {
134                 $current = $page->getCurrentRevision();
135                 $pagetext = $current->getPackedContent();
136                 $intermap = $this->_getMapFromWikiText($pagetext);
137             } else {
138                 trigger_error(_("WARNING: InterWikiMap page is unlocked, so not using those links."));
139                 $intermap = false;
140             }
141         } else {
142             $intermap = $this->_getMapFromWikiText($pagetext);
143         }
144         if (!$intermap && defined('INTERWIKI_MAP_FILE'))
145             $intermap = $this->_getMapFromFile(INTERWIKI_MAP_FILE);
146
147         $this->_map = $this->_parseMap($intermap);
148         $this->_regexp = $this->_getRegexp();
149     }
150
151     function GetMap ($pagetext = false) {
152         /*PHP5 Fatal error: Using $this when not in object context */
153         if (empty($this->_map)) {
154             $map = new PageType_interwikimap($pagetext);
155             return $map;
156         } else {
157             return $this;
158         }
159     }
160
161     function getRegexp() {
162         return $this->_regexp;
163     }
164
165     function link ($link, $linktext = false) {
166         list ($moniker, $page) = split (":", $link, 2);
167         
168         if (!isset($this->_map[$moniker])) {
169             return HTML::span(array('class' => 'bad-interwiki'),
170                               $linktext ? $linktext : $link);
171         }
172
173         $url = $this->_map[$moniker];
174         
175         // Urlencode page only if it's a query arg.
176         // FIXME: this is a somewhat broken heuristic.
177         $page_enc = strstr($url, '?') ? rawurlencode($page) : $page;
178
179         if (strstr($url, '%s'))
180             $url = sprintf($url, $page_enc);
181         else
182             $url .= $page_enc;
183
184         $link = HTML::a(array('href' => $url));
185
186         if (!$linktext) {
187             $link->pushContent(PossiblyGlueIconToText('interwiki', "$moniker:"),
188                                HTML::span(array('class' => 'wikipage'), $page));
189             $link->setAttr('class', 'interwiki');
190         }
191         else {
192             $link->pushContent(PossiblyGlueIconToText('interwiki', $linktext));
193             $link->setAttr('class', 'named-interwiki');
194         }
195         
196         return $link;
197     }
198
199
200     function _parseMap ($text) {
201         if (!preg_match_all("/^\s*(\S+)\s+(\S+)/m",
202                             $text, $matches, PREG_SET_ORDER))
203             return false;
204         foreach ($matches as $m) {
205             $map[$m[1]] = $m[2];
206         }
207         // add virtual "Upload:" moniker
208         if (empty($map['Upload'])) $map['Upload'] = getUploadDataPath();
209
210         // maybe add other monikers also (SemanticWeb link predicates?)
211         // Should they be defined in a RDF? (strict mode)
212         // Or should the SemanticWeb lib add it by itself? 
213         // (adding only a subset dependent on the context = model)
214         return $map;
215     }
216
217     function _getMapFromWikiText ($pagetext) {
218         if (preg_match('|^<verbatim>\n(.*)^</verbatim>|ms', $pagetext, $m)) {
219             return $m[1];
220         }
221         return false;
222     }
223
224     function _getMapFromFile ($filename) {
225         if (defined('WARN_NONPUBLIC_INTERWIKIMAP') and WARN_NONPUBLIC_INTERWIKIMAP) {
226             $error_html = sprintf(_("Loading InterWikiMap from external file %s."), $filename);
227             trigger_error( $error_html, E_USER_NOTICE );
228         }
229         if (!file_exists($filename)) {
230             $finder = new FileFinder();
231             $filename = $finder->findFile(INTERWIKI_MAP_FILE);
232         }
233         @$fd = fopen ($filename, "rb");
234         @$data = fread ($fd, filesize($filename));
235         @fclose ($fd);
236
237         return $data;
238     }
239
240     function _getRegexp () {
241         if (!$this->_map)
242             return '(?:(?!a)a)'; //  Never matches.
243         
244         foreach (array_keys($this->_map) as $moniker)
245             $qkeys[] = preg_quote($moniker, '/');
246         return "(?:" . join("|", $qkeys) . ")";
247     }
248 }
249
250
251 /** How to transform text.
252  */
253 class PageFormatter {
254     /** Constructor.
255      *
256      * @param WikiDB_Page $page
257      * @param hash $meta Version meta-data.
258      */
259     function PageFormatter(&$page, $meta) {
260         $this->_page = $page;
261         $this->_meta = $meta;
262         if (!empty($meta['markup']))
263             $this->_markup = $meta['markup'];
264         else
265             $this->_markup = 1; // dump used old-markup as empty. 
266         // to be able to restore it we must keep markup 1 as default.
267         // new policy: default = new markup (old crashes quite often)
268     }
269
270     function _transform(&$text) {
271         include_once('lib/BlockParser.php');
272         return TransformText($text, $this->_markup);
273     }
274
275     /** Transform the page text.
276      *
277      * @param string $text  The raw page content (e.g. wiki-text).
278      * @return XmlContent   Transformed content.
279      */
280     function format($text) {
281         trigger_error("pure virtual", E_USER_ERROR);
282     }
283 }
284
285 class PageFormatter_wikitext extends PageFormatter 
286 {
287     function format(&$text) {
288         return HTML::div(array('class' => 'wikitext'),
289                          $this->_transform($text));
290     }
291 }
292
293 class PageFormatter_interwikimap extends PageFormatter
294 {
295     function format($text) {
296         return HTML::div(array('class' => 'wikitext'),
297                          $this->_transform($this->_getHeader($text)),
298                          $this->_formatMap($text),
299                          $this->_transform($this->_getFooter($text)));
300     }
301
302     function _getHeader($text) {
303         return preg_replace('/<verbatim>.*/s', '', $text);
304     }
305
306     function _getFooter($text) {
307         return preg_replace('@.*?(</verbatim>|\Z)@s', '', $text, 1);
308     }
309     
310     function _getMap($pagetext) {
311         $map = getInterwikiMap($pagetext);
312         return $map->_map;
313     }
314     
315     function _formatMap($pagetext) {
316         $map = $this->_getMap($pagetext);
317         if (!$map)
318             return HTML::p("<No interwiki map found>"); // Shouldn't happen.
319
320         $mon_attr = array('class' => 'interwiki-moniker');
321         $url_attr = array('class' => 'interwiki-url');
322         
323         $thead = HTML::thead(HTML::tr(HTML::th($mon_attr, _("Moniker")),
324                                       HTML::th($url_attr, _("InterWiki Address"))));
325         foreach ($map as $moniker => $interurl) {
326             $rows[] = HTML::tr(HTML::td($mon_attr, new Cached_WikiLinkIfKnown($moniker)),
327                                HTML::td($url_attr, HTML::tt($interurl)));
328         }
329         
330         return HTML::table(array('class' => 'interwiki-map'),
331                            $thead,
332                            HTML::tbody(false, $rows));
333     }
334 }
335
336 class FakePageRevision {
337     function FakePageRevision($meta) {
338         $this->_meta = $meta;
339     }
340
341     function get($key) {
342         if (empty($this->_meta[$key]))
343             return false;
344         return $this->_meta[$key];
345     }
346 }
347         
348 class PageFormatter_attach extends PageFormatter
349 {
350     var $type, $prefix;
351     
352     // Display templated contents for wikiblog, comment and wikiforum
353     function format($text) {
354         if (empty($this->type))
355             trigger_error('PageFormatter_attach->format: $type missing');
356         include_once('lib/Template.php');
357         global $request;
358         $tokens['CONTENT'] = $this->_transform($text);
359         $tokens['page'] = $this->_page;
360         $tokens['rev'] = new FakePageRevision($this->_meta);
361
362         $name = new WikiPageName($this->_page->getName());
363         $tokens[$this->prefix."_PARENT"] = $name->getParent();
364
365         $meta = $this->_meta[$this->type];
366         foreach(array('ctime', 'creator', 'creator_id') as $key)
367             $tokens[$this->prefix . "_" . strtoupper($key)] = $meta[$key];
368         
369         return new Template($this->type, $request, $tokens);
370     }
371 }
372
373 class PageFormatter_wikiblog extends PageFormatter_attach {
374     var $type = 'wikiblog', $prefix = "BLOG";
375 }
376 class PageFormatter_comment extends PageFormatter_attach {
377     var $type = 'comment', $prefix = "COMMENT";
378 }
379 class PageFormatter_wikiforum extends PageFormatter_attach {
380     var $type = 'wikiforum', $prefix = "FORUM";
381 }
382
383 /** wikiabuse for htmlarea editing. not yet used.  
384  *
385  * Warning! Once a page is edited with a htmlarea like control it is
386  * stored in HTML and cannot be converted back to WikiText as long as
387  * we have no HTML => WikiText or any other interim format (WikiExchangeFormat e.g. Xml) 
388  * converter. So it has a viral effect and certain plugins will not work anymore.
389  * But a lot of wikiusers seem to like it.
390  */
391 class PageFormatter_html extends PageFormatter
392 {
393     function _transform($text) {
394         return $text;
395     }
396     function format($text) {
397         return $text;
398     }
399 }
400
401 /**
402  *  FIXME. not yet used
403  */
404 class PageFormatter_pdf extends PageFormatter
405 {
406
407     function _transform($text) {
408         include_once('lib/BlockParser.php');
409         return TransformText($text, $this->_markup);
410     }
411
412     // one page or set of pages?
413     // here we try to format only a single page
414     function format($text) {
415         include_once('lib/Template.php');
416         global $request;
417         $tokens['page']    = $this->_page;
418         $tokens['CONTENT'] = $this->_transform($text);
419         $pagename = $this->_page->getName();
420
421         // This is a XmlElement tree, which must be converted to PDF
422
423         // We can make use of several pdf extensions. This one - fpdf
424         // - is pure php and very easy, but looks quite ugly and has a
425         // terrible interface, as terrible as most of the othes. 
426         // The closest to HTML is htmldoc which needs an external cgi
427         // binary.
428         // We use a custom HTML->PDF class converter from PHPWebthings
429         // to be able to use templates for PDF.
430         require_once('lib/fpdf.php');
431         require_once('lib/pdf.php');
432
433         $pdf = new PDF();
434         $pdf->SetTitle($pagename);
435         $pdf->SetAuthor($this->_page->get('author'));
436         $pdf->SetCreator(WikiURL($pagename,false,1));
437         $pdf->AliasNbPages();
438         $pdf->AddPage();
439         //TODO: define fonts
440         $pdf->SetFont('Times','',12);
441         //$pdf->SetFont('Arial','B',16);
442
443         // PDF pagelayout from a special template
444         $template = new Template('pdf', $request, $tokens);
445         $pdf->ConvertFromHTML($template);
446
447         // specify filename, destination
448         $pdf->Output($pagename.".pdf",'I'); // I for stdin or D for download
449
450         // Output([string name [, string dest]])
451         return $pdf;
452     }
453 }
454
455 // Local Variables:
456 // mode: php
457 // tab-width: 8
458 // c-basic-offset: 4
459 // c-hanging-comment-ender-p: nil
460 // indent-tabs-mode: nil
461 // End:
462 ?>