]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/PageType.php
Connect the rest of PhpWiki to the IniConfig system. Also the keyword regular expres...
[SourceForge/phpwiki.git] / lib / PageType.php
1 <?php // -*-php-*-
2 rcs_id('$Id: PageType.php,v 1.27 2004-04-19 23:13:03 zorloc 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 = $meta['pagetype'];
38         if ($type_override)
39             $pagetype = $type_override;
40         $this->_type = PageType::GetPageType($pagetype);
41         $this->CacheableMarkup($this->_type->transform($page, $text, $meta),
42                                $page->getName());
43     }
44
45     function getType() {
46         return $this->_type;
47     }
48 }
49
50 /**
51  * A page type descriptor.
52  *
53  * Encapsulate information about page types.
54  *
55  * Currently the only information encapsulated is how to format
56  * the specific page type.  In the future or capabilities may be
57  * added, e.g. the abilities to edit different page types (differently.)
58  * e.g. Support for the javascript htmlarea editor, which can only edit 
59  * pure HTML.
60  *
61  * IMPORTANT NOTE: Since the whole PageType class gets stored (serialized)
62  * as of the cached marked-up page, it is important that the PageType classes
63  * not have large amounts of class data.  (No class data is even better.)
64  */
65 class PageType {
66     /**
67      * Get a page type descriptor.
68      *
69      * This is a static member function.
70      *
71      * @param string $pagetype  Name of the page type.
72      * @return PageType  An object which is a subclass of PageType.
73      */
74     function GetPageType ($name=false) {
75         if (!$name)
76             $name = 'wikitext';
77         if ($name) {
78             $class = "PageType_" . (string)$name;
79             if (class_exists($class))
80                 return new $class;
81             trigger_error(sprintf("PageType '%s' unknown", (string)$name),
82                           E_USER_WARNING);
83         }
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 () {
123     $map = new PageType_interwikimap();
124     return $map;
125 }
126
127 class PageType_interwikimap extends PageType
128 {
129     function PageType_interwikimap() {
130         global $request;
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);
135
136         $this->_map = $this->_parseMap($intermap);
137         $this->_regexp = $this->_getRegexp();
138     }
139
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();
144             return $map;
145         } else {
146             return $this;
147         }
148     }
149
150     function getRegexp() {
151         return $this->_regexp;
152     }
153
154     function link ($link, $linktext = false) {
155
156         list ($moniker, $page) = split (":", $link, 2);
157         
158         if (!isset($this->_map[$moniker])) {
159             return HTML::span(array('class' => 'bad-interwiki'),
160                               $linktext ? $linktext : $link);
161         }
162
163         $url = $this->_map[$moniker];
164         
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;
168
169         if (strstr($url, '%s'))
170             $url = sprintf($url, $page_enc);
171         else
172             $url .= $page_enc;
173
174         $link = HTML::a(array('href' => $url));
175
176         if (!$linktext) {
177             $link->pushContent(PossiblyGlueIconToText('interwiki', "$moniker:"),
178                                HTML::span(array('class' => 'wikipage'), $page));
179             $link->setAttr('class', 'interwiki');
180         }
181         else {
182             $link->pushContent(PossiblyGlueIconToText('interwiki', $linktext));
183             $link->setAttr('class', 'named-interwiki');
184         }
185         
186         return $link;
187     }
188
189
190     function _parseMap ($text) {
191         if (!preg_match_all("/^\s*(\S+)\s+(\S+)/m",
192                             $text, $matches, PREG_SET_ORDER))
193             return false;
194         foreach ($matches as $m) {
195             $map[$m[1]] = $m[2];
196         }
197         if (empty($map['Upload']))
198             $map['Upload'] = SERVER_URL . ((substr(DATA_PATH,0,1)=='/') ? '' : "/") . DATA_PATH . '/uploads/';
199         return $map;
200     }
201
202     function _getMapFromWikiPage ($page) {
203         if (! $page->get('locked'))
204             return false;
205         
206         $current = $page->getCurrentRevision();
207         
208         if (preg_match('|^<verbatim>\n(.*)^</verbatim>|ms',
209                        $current->getPackedContent(), $m)) {
210             return $m[1];
211         }
212         return false;
213     }
214
215     // Fixme!
216     function _getMapFromFile ($filename) {
217         if (defined('WARN_NONPUBLIC_INTERWIKIMAP') and WARN_NONPUBLIC_INTERWIKIMAP) {
218             $error_html = sprintf(_("Loading InterWikiMap from external file %s."), $filename);
219             trigger_error( $error_html, E_USER_NOTICE );
220         }
221         if (!file_exists($filename)) {
222             $finder = new FileFinder();
223             $filename = $finder->findFile(INTERWIKI_MAP_FILE);
224         }
225         @$fd = fopen ($filename, "rb");
226         @$data = fread ($fd, filesize($filename));
227         @fclose ($fd);
228
229         return $data;
230     }
231
232     function _getRegexp () {
233         if (!$this->_map)
234             return '(?:(?!a)a)'; //  Never matches.
235         
236         foreach (array_keys($this->_map) as $moniker)
237             $qkeys[] = preg_quote($moniker, '/');
238         return "(?:" . join("|", $qkeys) . ")";
239     }
240 }
241
242
243 /** How to transform text.
244  */
245 class PageFormatter {
246     /** Constructor.
247      *
248      * @param WikiDB_Page $page
249      * @param hash $meta Version meta-data.
250      */
251     function PageFormatter($page, $meta) {
252         $this->_page = $page;
253         $this->_meta = $meta;
254         if (!empty($meta['markup']))
255             $this->_markup = $meta['markup'];
256         else
257             $this->_markup = 1;
258     }
259
260     function _transform($text) {
261         include_once('lib/BlockParser.php');
262         return TransformText($text, $this->_markup);
263     }
264
265     /** Transform the page text.
266      *
267      * @param string $text  The raw page content (e.g. wiki-text).
268      * @return XmlContent   Transformed content.
269      */
270     function format($text) {
271         trigger_error("pure virtual", E_USER_ERROR);
272     }
273 }
274
275 class PageFormatter_wikitext extends PageFormatter 
276 {
277     function format($text) {
278         return HTML::div(array('class' => 'wikitext'),
279                          $this->_transform($text));
280     }
281 }
282
283 class PageFormatter_interwikimap extends PageFormatter
284 {
285     function format($text) {
286         return HTML::div(array('class' => 'wikitext'),
287                          $this->_transform($this->_getHeader($text)),
288                          $this->_formatMap(),
289                          $this->_transform($this->_getFooter($text)));
290     }
291
292     function _getHeader($text) {
293         return preg_replace('/<verbatim>.*/s', '', $text);
294     }
295
296     function _getFooter($text) {
297         return preg_replace('@.*?(</verbatim>|\Z)@s', '', $text, 1);
298     }
299     
300     function _getMap() {
301         $map = PageType_interwikimap::getMap();
302         return $map->_map;
303     }
304     
305     function _formatMap() {
306         $map = $this->_getMap();
307         if (!$map)
308             return HTML::p("<No map found>"); // Shouldn't happen.
309
310         global $request;
311         $dbi = $request->getDbh();
312
313         $mon_attr = array('class' => 'interwiki-moniker');
314         $url_attr = array('class' => 'interwiki-url');
315         
316         $thead = HTML::thead(HTML::tr(HTML::th($mon_attr, _("Moniker")),
317                                       HTML::th($url_attr, _("InterWiki Address"))));
318         foreach ($map as $moniker => $interurl) {
319             $rows[] = HTML::tr(HTML::td($mon_attr, new Cached_WikiLinkIfKnown($moniker)),
320                                HTML::td($url_attr, HTML::tt($interurl)));
321         }
322         
323         return HTML::table(array('class' => 'interwiki-map'),
324                            $thead,
325                            HTML::tbody(false, $rows));
326     }
327 }
328
329 class FakePageRevision {
330     function FakePageRevision($meta) {
331         $this->_meta = $meta;
332     }
333
334     function get($key) {
335         if (empty($this->_meta[$key]))
336             return false;
337         return $this->_meta[$key];
338     }
339 }
340         
341 class PageFormatter_attach extends PageFormatter
342 {
343     var $type, $prefix;
344     
345     // Display templated contents for wikiblog, comment and wikiforum
346     function format($text) {
347         if (empty($this->type))
348             trigger_error('PageFormatter_attach->format: $type missing');
349         include_once('lib/Template.php');
350         global $request;
351         $tokens['CONTENT'] = $this->_transform($text);
352         $tokens['page'] = $this->_page;
353         $tokens['rev'] = new FakePageRevision($this->_meta);
354
355         $name = new WikiPageName($this->_page->getName());
356         $tokens[$this->prefix."_PARENT"] = $name->getParent();
357
358         $meta = $this->_meta[$this->type];
359         foreach(array('ctime', 'creator', 'creator_id') as $key)
360             $tokens[$this->prefix . "_" . strtoupper($key)] = $meta[$key];
361         
362         return new Template($this->type, $request, $tokens);
363     }
364 }
365
366 class PageFormatter_wikiblog extends PageFormatter_attach {
367     var $type = 'wikiblog', $prefix = "BLOG";
368 }
369 class PageFormatter_comment extends PageFormatter_attach {
370     var $type = 'comment', $prefix = "COMMENT";
371 }
372 class PageFormatter_wikiforum extends PageFormatter_attach {
373     var $type = 'wikiforum', $prefix = "FORUM";
374 }
375
376 /** wikiabuse for htmlarea editing. not yet used.  
377  *
378  * Warning! Once a page is edited with a htmlarea like control it is
379  * stored in HTML and cannot be converted back to WikiText as long as
380  * we have no HTML => WikiText or any other interim format (WikiExchangeFormat e.g. Xml) 
381  * converter. So it has a viral effect and certain plugins will not work anymore.
382  * But a lot of wikiusers seem to like it.
383  */
384 class PageFormatter_html extends PageFormatter
385 {
386     function _transform($text) {
387         return $text;
388     }
389     function format($text) {
390         return $text;
391     }
392 }
393
394 /**
395  *  FIXME. not yet used
396  */
397 class PageFormatter_pdf extends PageFormatter
398 {
399
400     function _transform($text) {
401         include_once('lib/BlockParser.php');
402         return TransformText($text, $this->_markup);
403     }
404
405     // one page or set of pages?
406     // here we try to format only a single page
407     function format($text) {
408         include_once('lib/Template.php');
409         global $request;
410         $tokens['page']    = $this->_page;
411         $tokens['CONTENT'] = $this->_transform($text);
412         $pagename = $this->_page->getName();
413
414         // This is a XmlElement tree, which must be converted to PDF
415
416         // We can make use of several pdf extensions. This one - fpdf
417         // - is pure php and very easy, but looks quite ugly and has a
418         // terrible interface, as terrible as most of the othes. 
419         // The closest to HTML is htmldoc which needs an external cgi
420         // binary.
421         // We use a custom HTML->PDF class converter from PHPWebthings
422         // to be able to use templates for PDF.
423         require_once('lib/fpdf.php');
424         require_once('lib/pdf.php');
425
426         $pdf = new PDF();
427         $pdf->SetTitle($pagename);
428         $pdf->SetAuthor($this->_page->get('author'));
429         $pdf->SetCreator(WikiUrl($pagename,false,1));
430         $pdf->AliasNbPages();
431         $pdf->AddPage();
432         //TODO: define fonts
433         $pdf->SetFont('Times','',12);
434         //$pdf->SetFont('Arial','B',16);
435
436         // PDF pagelayout from a special template
437         $template = new Template('pdf', $request, $tokens);
438         $pdf->ConvertFromHTML($template);
439
440         // specify filename, destination
441         $pdf->Output($pagename.".pdf",'I'); // I for stdin or D for download
442
443         // Output([string name [, string dest]])
444         return $pdf;
445     }
446 }
447
448 // Local Variables:
449 // mode: php
450 // tab-width: 8
451 // c-basic-offset: 4
452 // c-hanging-comment-ender-p: nil
453 // indent-tabs-mode: nil
454 // End:
455 ?>