]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/CachedMarkup.php
include [all] Include and file path should be devided with single space. File path...
[SourceForge/phpwiki.git] / lib / CachedMarkup.php
1 <?php
2
3 /* Copyright (C) 2002 Geoffrey T. Dairiki <dairiki@dairiki.org>
4  * Copyright (C) 2004-2010 $ThePhpWikiProgrammingTeam
5  * Copyright (C) 2008-2009 Marc-Etienne Vargenau, Alcatel-Lucent
6  *
7  * This file is part of PhpWiki.
8  *
9  * PhpWiki is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * PhpWiki is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with PhpWiki; if not, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  */
23
24 require_once 'lib/Units.php';
25
26 class CacheableMarkup extends XmlContent {
27
28     function CacheableMarkup($content, $basepage) {
29         $this->_basepage = $basepage;
30         $this->_buf = '';
31         $this->_content = array();
32         $this->_append($content);
33         if ($this->_buf != '')
34             $this->_content[] = $this->_buf;
35         unset($this->_buf);
36     }
37
38     function pack() {
39         // FusionForge hack
40         // This causes a strange bug when a comment containing
41         // a single quote is entered in the Summary box:
42         // - the history is wrong (user and comment missing)
43         // - the table of contents plugin no longer works
44         global $WikiTheme;
45         if (isa($WikiTheme, 'WikiTheme_fusionforge')) {
46             return serialize($this);
47         }
48
49         if (function_exists('gzcompress'))
50             return gzcompress(serialize($this), 9);
51         return serialize($this);
52
53         // FIXME: probably should implement some sort of "compression"
54         //   when no gzcompress is available.
55     }
56
57     function unpack($packed) {
58         if (!$packed)
59             return false;
60
61         // ZLIB format has a five bit checksum in it's header.
62         // Lets check for sanity.
63         if (((ord($packed[0]) * 256 + ord($packed[1])) % 31 == 0)
64              and (substr($packed,0,2) == "\037\213")
65                   or (substr($packed,0,2) == "x\332"))   // 120, 218
66         {
67             if (function_exists('gzuncompress')) {
68                 // Looks like ZLIB.
69                 $data = gzuncompress($packed);
70                 return unserialize($data);
71             } else {
72                 // user our php lib. TESTME
73                 include_once 'ziplib.php';
74                 $zip = new ZipReader($packed);
75                 list(,$data,$attrib) = $zip->readFile();
76                 return unserialize($data);
77             }
78         }
79         if (substr($packed,0,2) == "O:") {
80             // Looks like a serialized object
81             return unserialize($packed);
82         }
83         if (preg_match("/^\w+$/", $packed))
84             return $packed;
85         // happened with _BackendInfo problem also.
86         trigger_error("Can't unpack bad cached markup. Probably php_zlib extension not loaded.",
87                       E_USER_WARNING);
88         return false;
89     }
90
91     /** Get names of wikipages linked to.
92      *
93      * @return array of hashes { linkto=>pagename, relation=>pagename }
94      */
95     function getWikiPageLinks() {
96         $links = array();
97         foreach ($this->_content as $item) {
98             if (!isa($item, 'Cached_DynamicContent'))
99                 continue;
100             if (!($item_links = $item->getWikiPageLinks($this->_basepage)))
101                 continue;
102             $links = array_merge($links, $item_links);
103         }
104         // array_unique has a bug with hashes!
105         // set_links checks for duplicates, array_merge does not
106         //return array_unique($links);
107         return $links;
108     }
109
110     /** Get link info.
111      *
112      * This is here to support the XML-RPC listLinks() method.
113      *
114      * @return array
115      * Returns an array of hashes.
116      */
117     function getLinkInfo() {
118         $link = array();
119         foreach ($this->_content as $link) {
120             if (! isa($link, 'Cached_Link'))
121                 continue;
122             $info = $link->getLinkInfo($this->_basepage);
123             $links[$info->href] = $info;
124         }
125         return array_values($links);
126     }
127
128     function _append($item) {
129         if (is_array($item)) {
130             foreach ($item as $subitem)
131                 $this->_append($subitem);
132         }
133         elseif (!is_object($item)) {
134             $this->_buf .= $this->_quote((string) $item);
135         }
136         elseif (isa($item, 'Cached_DynamicContent')) {
137             if ($this->_buf) {
138                 $this->_content[] = $this->_buf;
139                 $this->_buf = '';
140             }
141             $this->_content[] = $item;
142         }
143         elseif (isa($item, 'XmlElement')) {
144             if ($item->isEmpty()) {
145                 $this->_buf .= $item->emptyTag();
146             }
147             else {
148                 $this->_buf .= $item->startTag();
149                 foreach ($item->getContent() as $subitem)
150                     $this->_append($subitem);
151                 $this->_buf .= "</$item->_tag>";
152
153                 if (!$this->getDescription() and $item->getTag() == 'p') {
154                     // performance: when is this really needed?
155                     $this->_glean_description($item->asString());
156                 }
157             }
158             if (!$item->isInlineElement())
159             $this->_buf .= "\n";
160         }
161         elseif (isa($item, 'XmlContent')) {
162             foreach ($item->getContent() as $item)
163                 $this->_append($item);
164         }
165         elseif (method_exists($item, 'asXML')) {
166             $this->_buf .= $item->asXML();
167         }
168         elseif (method_exists($item, 'asString')) {
169             $this->_buf .= $this->_quote($item->asString());
170         }
171         else {
172             $this->_buf .= sprintf("==Object(%s)==", get_class($item));
173         }
174     }
175
176     function _glean_description($text) {
177         static $two_sentences;
178         if (!$two_sentences) {
179             $two_sentences = "[.?!][\")]*\s+[\"(]*[[:upper:])]"
180                            . ".*"
181                            . "[.?!][\")]*\s*[\"(]*([[:upper:])]|$)";
182         }
183
184         if (!isset($this->_description) and preg_match("/$two_sentences/sx", $text))
185             $this->_description = preg_replace("/\s*\n\s*/", " ", trim($text));
186     }
187
188     /**
189      * Guess a short description of the page.
190      *
191      * Algorithm:
192      *
193      * This algorithm was suggested on MeatballWiki by
194      * Alex Schroeder <kensanata@yahoo.com>.
195      *
196      * Use the first paragraph in the page which contains at least two
197      * sentences.
198      *
199      * @see http://www.usemod.com/cgi-bin/mb.pl?MeatballWikiSuggestions
200      *
201      * @return string
202      */
203     function getDescription () {
204         return isset($this->_description) ? $this->_description : '';
205     }
206
207     function asXML () {
208         $xml = '';
209         $basepage = $this->_basepage;
210
211         foreach ($this->_content as $item) {
212             if (is_string($item)) {
213                 $xml .= $item;
214             }
215             elseif (is_subclass_of($item,
216                                    check_php_version(5)
217                                      ? 'Cached_DynamicContent'
218                                      : 'cached_dynamiccontent'))
219             {
220                 $val = $item->expand($basepage, $this);
221                 $xml .= $val->asXML();
222             }
223             else {
224                 $xml .= $item->asXML();
225             }
226         }
227         return $xml;
228     }
229
230     function printXML () {
231         $basepage = $this->_basepage;
232         // _content might be changed from a plugin (CreateToc)
233         for ($i=0; $i < count($this->_content); $i++) {
234             $item = $this->_content[$i];
235             if (is_string($item)) {
236                 print $item;
237             }
238             elseif (is_subclass_of($item,
239                                    check_php_version(5)
240                                      ? 'Cached_DynamicContent'
241                                      : 'cached_dynamiccontent'))
242             {      // give the content the chance to know about itself or even
243                 // to change itself
244                 $val = $item->expand($basepage, $this);
245                 if ($val) $val->printXML();
246                 else trigger_error('empty item ' . print_r($item, true));
247             }
248             else {
249                 $item->printXML();
250             }
251         }
252     }
253 }
254
255 /**
256  * The base class for all dynamic content.
257  *
258  * Dynamic content is anything that can change even when the original
259  * wiki-text from which it was parsed is unchanged.
260  */
261 class Cached_DynamicContent {
262
263     function cache(&$cache) {
264         $cache[] = $this;
265     }
266
267     function expand($basepage, &$obj) {
268         trigger_error("Pure virtual", E_USER_ERROR);
269     }
270
271     function getWikiPageLinks($basepage) {
272         return false;
273     }
274 }
275
276 class XmlRpc_LinkInfo {
277     function XmlRpc_LinkInfo($page, $type, $href, $relation = '') {
278         $this->page = $page;
279         $this->type = $type;
280         $this->href = $href;
281         $this->relation = $relation;
282         //$this->pageref = str_replace("/RPC2.php", "/index.php", $href);
283     }
284 }
285
286 class Cached_Link extends Cached_DynamicContent {
287
288     function isInlineElement() {
289         return true;
290     }
291
292     /** Get link info (for XML-RPC support)
293      *
294      * This is here to support the XML-RPC listLinks method.
295      * (See http://www.ecyrd.com/JSPWiki/Wiki.jsp?page=WikiRPCInterface)
296      */
297     function getLinkInfo($basepage) {
298         return new XmlRpc_LinkInfo($this->_getName($basepage),
299                                    $this->_getType(),
300                                    $this->_getURL($basepage),
301                                    $this->_getRelation($basepage));
302     }
303
304     function _getURL($basepage) {
305         return $this->_url;
306     }
307     function __getRelation($basepage) {
308         return $this->_relation;
309     }
310 }
311 /*
312  * Defer interwiki inline links. img src=upload:xx.png
313  * LinkImage($url, $alt = false)
314  */
315 class Cached_InlinedImage extends Cached_DynamicContent {
316     function isInlineElement() {
317         return true;
318     }
319     function _getURL($basepage) {
320         return $this->_url;
321     }
322     // TODO: fix interwiki inline links in case of static dumps
323     function expand($basepage, &$markup) {
324         global $WikiTheme;
325         $this->_basepage = $basepage;
326         $label = isset($this->_label) ? $this->_label : false;
327         if ($WikiTheme->DUMP_MODE) {
328             // In case of static dumps we need to check if we should
329             // inline the image or not: external: keep link, internal: copy locally
330             return LinkImage($label);
331         } else {
332             return LinkImage($label);
333         }
334     }
335 }
336
337 class Cached_WikiLink extends Cached_Link {
338
339     function Cached_WikiLink ($page, $label = false, $anchor = false) {
340         $this->_page = $page;
341         /* ":DontStoreLink" */
342         if (substr($this->_page,0,1) == ':') {
343             $this->_page = substr($this->_page,1);
344             $this->_nolink = true;
345         }
346         if ($anchor)
347             $this->_anchor = $anchor;
348         if ($label and $label != $page)
349             $this->_label = $label;
350         $this->_basepage = false;
351     }
352
353     function _getType() {
354         return 'internal';
355     }
356
357     function getPagename($basepage) {
358         $page = new WikiPageName($this->_page, $basepage);
359         if ($page->isValid()) return $page->name;
360         else return false;
361     }
362
363     function getWikiPageLinks($basepage) {
364         if ($basepage == '') return false;
365         if (isset($this->_nolink)) return false;
366         if ($link = $this->getPagename($basepage))
367             return array(array('linkto' => $link));
368         else
369             return false;
370     }
371
372     function _getName($basepage) {
373         return $this->getPagename($basepage);
374     }
375
376     function _getURL($basepage) {
377         return WikiURL($this->getPagename($basepage));
378         //return WikiURL($this->getPagename($basepage), false, 'abs_url');
379     }
380
381     function expand($basepage, &$markup) {
382         global $WikiTheme;
383         $this->_basepage = $basepage;
384         $label = isset($this->_label) ? $this->_label : false;
385         $anchor = isset($this->_anchor) ? (string)$this->_anchor : '';
386         $page = new WikiPageName($this->_page, $basepage, $anchor);
387         if ($WikiTheme->DUMP_MODE and !empty($WikiTheme->VALID_LINKS)) {
388             if (!in_array($this->_page, $WikiTheme->VALID_LINKS))
389                 return HTML($label ? $label : $page->getName());
390         }
391         if ($page->isValid()) return WikiLink($page, 'auto', $label);
392             else return HTML($label);
393     }
394
395     function asXML() {
396         global $WikiTheme;
397         $label = isset($this->_label) ? $this->_label : false;
398         $anchor = isset($this->_anchor) ? (string)$this->_anchor : '';
399         //TODO: need basepage for subpages like /Remove (within CreateTOC)
400         $page = new WikiPageName($this->_page, $this->_basepage, $anchor);
401         if ($WikiTheme->DUMP_MODE and $WikiTheme->VALID_LINKS) {
402             if (!in_array($this->_page, $WikiTheme->VALID_LINKS))
403                 return $label ? $label : $page->getName();
404         }
405         $link = WikiLink($page, 'auto', $label);
406         return $link->asXML();
407     }
408
409     function asString() {
410         if (isset($this->_label))
411             return $this->_label;
412         return $this->_page;
413     }
414 }
415
416 class Cached_WikiLinkIfKnown extends Cached_WikiLink
417 {
418     function Cached_WikiLinkIfKnown ($moniker) {
419         $this->_page = $moniker;
420     }
421
422     function expand($basepage, &$markup) {
423         global $WikiTheme;
424         if ($WikiTheme->DUMP_MODE and $WikiTheme->VALID_LINKS) {
425             if (!in_array($this->_page, $WikiTheme->VALID_LINKS))
426                 return HTML($label ? $label : $page->getName());
427         }
428         return WikiLink($this->_page, 'if_known');
429     }
430 }
431
432 class Cached_SpellCheck extends Cached_WikiLink
433 {
434     function Cached_SpellCheck ($word, $suggs) {
435         $this->_page = $word;
436         $this->suggestions = $suggs;
437     }
438
439     function expand($basepage, &$markup) {
440         $link = HTML::a(array('class' => 'spell-wrong',
441                   'title' => 'SpellCheck: '.join(', ', $this->suggestions),
442                   'name' => $this->_page),
443             $this->_page);
444         return $link;
445     }
446 }
447
448 class Cached_PhpwikiURL extends Cached_DynamicContent
449 {
450     function Cached_PhpwikiURL ($url, $label) {
451     $this->_url = $url;
452         if ($label)
453             $this->_label = $label;
454     }
455
456     function isInlineElement() {
457         return true;
458     }
459
460     function expand($basepage, &$markup) {
461     global $WikiTheme;
462         $label = isset($this->_label) ? $this->_label : false;
463         if ($WikiTheme->DUMP_MODE and $WikiTheme->VALID_LINKS) {
464             if (!in_array($this->_page, $WikiTheme->VALID_LINKS))
465                 return HTML($label ? $label : $page->getName());
466         }
467         return LinkPhpwikiURL($this->_url, $label, $basepage);
468     }
469
470     function asXML() {
471         $label = isset($this->_label) ? $this->_label : false;
472         $link = LinkPhpwikiURL($this->_url, $label);
473         return $link->asXML();
474     }
475
476     function asString() {
477         if (isset($this->_label))
478             return $this->_label;
479         return $this->_url;
480     }
481 }
482
483 /*
484  * Relations (::) are named links to pages.
485  * Attributes (:=) are named metadata per page, "named links to numbers with units".
486  * We don't want to exhaust the linktable with numbers,
487  * since this would create empty pages per each value,
488  * so we don't store the attributes as full relationlink.
489  * But we do store the attribute name as relation with an empty pagename
490  * to denote that this is an attribute,
491  * and to enable a fast listRelations mode=attributes
492  */
493 class Cached_SemanticLink extends Cached_WikiLink {
494
495     function Cached_SemanticLink ($url, $label=false) {
496     $this->_url = $url;
497         if ($label && $label != $url)
498             $this->_label = $label;
499         $this->_expandurl($this->_url);
500     }
501
502     function isInlineElement() {
503         return true;
504     }
505
506     function getPagename($basepage) {
507         if (!isset($this->_page)) return false;
508         $page = new WikiPageName($this->_page, $basepage);
509         if ($page->isValid()) return $page->name;
510         else return false;
511     }
512
513     /* Add relation to the link table.
514      * attributes have the _relation, but not the _page set.
515      */
516     function getWikiPageLinks($basepage) {
517         if ($basepage == '') return false;
518         if (!isset($this->_page) and isset($this->_attribute)) {
519             // An attribute: we store it in the basepage now, to fill the cache for page->save
520             // TODO: side-effect free query
521             $page = $GLOBALS['request']->getPage($basepage);
522             $page->setAttribute($this->_relation, $this->_attribute);
523             $this->_page = $basepage;
524             return array(array('linkto' => '', 'relation' => $this->_relation));
525         }
526         if ($link = $this->getPagename($basepage))
527             return array(array('linkto' => $link, 'relation' => $this->_relation));
528         else
529             return false;
530     }
531
532     function _expandurl($url) {
533         $m = array();
534         if (!preg_match('/^ ([^:]+) (:[:=]) (.+) $/x', $url, $m)) {
535             return HTML::span(array('class' => 'error'), _("BAD semantic relation link"));
536         }
537         $this->_relation = urldecode($m[1]);
538         $is_attribute = ($m[2] == ':=');
539         if ($is_attribute) {
540             $this->_attribute = urldecode($m[3]);
541             // since this stored in the markup cache, we are extra sensible
542             // not to store false empty stuff.
543             $units = new Units();
544             if (!DISABLE_UNITS and !$units->errcode) {
545                 $this->_attribute_base = $units->Definition($this->_attribute);
546                 $this->_unit = $units->baseunit($this->_attribute);
547             }
548         } else {
549             $this->_page = urldecode($m[3]);
550         }
551         return $m;
552     }
553
554     function _expand($url, $label = false) {
555         global $WikiTheme;
556         $m = $this->_expandurl($url);
557         $class = 'wiki';
558         // do not link to the attribute value, but to the attribute
559         $is_attribute = ($m[2] == ':=');
560         if ($WikiTheme->DUMP_MODE and $WikiTheme->VALID_LINKS) {
561             if (isset($this->_page) and !in_array($this->_page, $WikiTheme->VALID_LINKS))
562                 return HTML($label ? $label : ($is_attribute ? $this->_relation : $this->_page));
563         }
564         if ($is_attribute)
565             $title = isset($this->_attribute_base)
566             ? sprintf(_("Attribute %s, base value: %s"), $this->_relation, $this->_attribute_base)
567             : sprintf(_("Attribute %s, value: %s"), $this->_relation, $this->_attribute);
568             if ($label) {
569                 return HTML::span(
570                     HTML::a(array('href'  => WikiURL($is_attribute ? $this->_relation : $this->_page),
571                    'class' => "wiki ".($is_attribute ? "attribute" : "relation"),
572                    'title' => $is_attribute
573                    ? $title
574                    : sprintf(_("Relation %s to page %s"), $this->_relation, $this->_page)),
575              $label)
576          );
577             } elseif ($is_attribute) {
578                 return HTML::span
579         (
580          HTML::a(array('href'  => WikiURL($this->_relation),
581                    'class' => "wiki attribute",
582                    'title' => $title),
583              $url)
584          );
585         } else {
586             return HTML::span
587         (
588          HTML::a(array('href'  => WikiURL($this->_relation),
589                    'class' => "wiki relation"),
590              $this->_relation),
591          HTML::span(array('class'=>'relation-symbol'), $m[2]),
592          HTML::a(array('href'  => WikiURL($this->_page),
593                    'class' => "wiki"),
594              $this->_page)
595          );
596         }
597     }
598
599     function expand($basepage, &$markup) {
600         $label = isset($this->_label) ? $this->_label : false;
601         return $this->_expand($this->_url, $label);
602     }
603
604     function asXML() {
605         $label = isset($this->_label) ? $this->_label : false;
606         $link = $this->_expand($this->_url, $label);
607         return $link->asXML();
608     }
609
610     function asString() {
611         if (isset($this->_label))
612             return $this->_label;
613         return $this->_url;
614     }
615 }
616
617 /**
618  * Highlight found search engine terms
619  */
620 class Cached_SearchHighlight extends Cached_DynamicContent
621 {
622     function Cached_SearchHighlight ($word, $engine) {
623         $this->_word = $word;
624         $this->engine = $engine;
625     }
626
627     function expand($basepage, &$markup) {
628         return HTML::span(array('class' => 'search-term',
629                                 'title' => _("Found by ") . $this->engine),
630                           $this->_word);
631     }
632 }
633
634 class Cached_ExternalLink extends Cached_Link {
635
636     function Cached_ExternalLink($url, $label=false) {
637         $this->_url = $url;
638         if ($label && $label != $url)
639             $this->_label = $label;
640     }
641
642     function _getType() {
643         return 'external';
644     }
645
646     function _getName($basepage) {
647         $label = isset($this->_label) ? $this->_label : false;
648         return ($label and is_string($label)) ? $label : $this->_url;
649     }
650
651     function expand($basepage, &$markup) {
652         global $request;
653
654         $label = isset($this->_label) ? $this->_label : false;
655         $link = LinkURL($this->_url, $label);
656
657         if (GOOGLE_LINKS_NOFOLLOW) {
658             // Ignores nofollow when the user who saved the page was authenticated.
659             $page = $request->getPage($basepage);
660             $current = $page->getCurrentRevision(false);
661             if (!$current->get('author_id'))
662                 $link->setAttr('rel', 'nofollow');
663         }
664         return $link;
665     }
666
667     function asString() {
668         if (isset($this->_label) and is_string($this->_label))
669             return $this->_label;
670         return $this->_url;
671     }
672 }
673
674 class Cached_InterwikiLink extends Cached_ExternalLink {
675
676     function Cached_InterwikiLink($link, $label=false) {
677         $this->_link = $link;
678         if ($label)
679             $this->_label = $label;
680     }
681
682     function getPagename($basepage) {
683         list ($moniker, $page) = explode (":", $this->_link, 2);
684         $page = new WikiPageName($page, $basepage);
685         if ($page->isValid()) return $page->name;
686         else return false;
687     }
688
689     function getWikiPageLinks($basepage) {
690         if ($basepage == '') return false;
691         /* ":DontStoreLink" */
692         if (substr($this->_link,0,1) == ':') return false;
693         /* store only links to valid pagenames */
694         $dbi = $GLOBALS['request']->getDbh();
695         if ($link = $this->getPagename($basepage) and $dbi->isWikiPage($link)) {
696             return array(array('linkto' => $link));
697         } else {
698             return false; // dont store external links
699         }
700     }
701
702     function _getName($basepage) {
703         $label = isset($this->_label) ? $this->_label : false;
704         return ($label and is_string($label)) ? $label : $this->_link;
705     }
706
707     /* there may be internal interwiki links also */
708     function _getType() {
709         return $this->getPagename(false) ? 'internal' : 'external';
710     }
711
712     function _getURL($basepage) {
713         $link = $this->expand($basepage, $this);
714         return $link->getAttr('href');
715     }
716
717     function expand($basepage, &$markup) {
718         global $WikiTheme;
719         $intermap = getInterwikiMap();
720         $label = isset($this->_label) ? $this->_label : false;
721         //FIXME: check Upload: inlined images
722         if ($WikiTheme->DUMP_MODE and !empty($WikiTheme->VALID_LINKS)) {
723             if (!in_array($this->_link, $WikiTheme->VALID_LINKS))
724             return HTML($label ? $label : $this->_link);
725         }
726         return $intermap->link($this->_link, $label);
727     }
728
729     function asString() {
730         if (isset($this->_label))
731             return $this->_label;
732         return $this->_link;
733     }
734 }
735
736 // Needed to put UserPages to backlinks. Special method to markup userpages with icons
737 // Thanks to PhpWiki:DanFr for finding this bug.
738 // Fixed since 1.3.8, prev. versions had no userpages in backlinks
739 class Cached_UserLink extends Cached_WikiLink {
740     function expand($basepage, &$markup) {
741         $label = isset($this->_label) ? $this->_label : false;
742         $anchor = isset($this->_anchor) ? (string)$this->_anchor : '';
743         $page = new WikiPageName($this->_page, $basepage, $anchor);
744         $link = WikiLink($page, 'auto', $label);
745         // $link = HTML::a(array('href' => $PageName));
746         $link->setContent(PossiblyGlueIconToText('wikiuser', $this->_page));
747         $link->setAttr('class', 'wikiuser');
748         return $link;
749     }
750 }
751
752 /**
753  * 1.3.13: Previously stored was only _pi.
754  * A fresh generated cache has now ->name and ->args also.
755  * main::isActionPage only checks the raw content.
756  */
757 class Cached_PluginInvocation extends Cached_DynamicContent {
758
759     function Cached_PluginInvocation ($pi) {
760         $this->_pi = $pi;
761         $loader = $this->_getLoader();
762         if (is_array($plugin_cmdline = $loader->parsePI($pi)) and $plugin_cmdline[1]) {
763             $this->pi_name = $plugin_cmdline[0]; // plugin, plugin-form, plugin-list
764             $this->name = $plugin_cmdline[1]->getName();
765             $this->args = $plugin_cmdline[2];
766         }
767     }
768
769     function setTightness($top, $bottom) {
770     }
771
772     function isInlineElement() {
773         return false;
774     }
775
776     function expand($basepage, &$markup) {
777         $loader = $this->_getLoader();
778         $xml = $loader->expandPI($this->_pi, $GLOBALS['request'], $markup, $basepage);
779         return $xml;
780     }
781
782     function asString() {
783         return $this->_pi;
784     }
785
786     function getWikiPageLinks($basepage) {
787         $loader = $this->_getLoader();
788
789         return $loader->getWikiPageLinks($this->_pi, $basepage);
790     }
791
792     function & _getLoader() {
793         static $loader = false;
794
795         if (!$loader) {
796             include_once 'lib/WikiPlugin.php';
797             $loader = new WikiPluginLoader;
798         }
799         return $loader;
800     }
801 }
802
803 // Local Variables:
804 // mode: php
805 // tab-width: 8
806 // c-basic-offset: 4
807 // c-hanging-comment-ender-p: nil
808 // indent-tabs-mode: nil
809 // End: