]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/WikiPluginCached.php
Fixes to support new $basepage argument of WikiPlugin::run().
[SourceForge/phpwiki.git] / lib / WikiPluginCached.php
1 <?php rcs_id('$Id: WikiPluginCached.php,v 1.4 2003-02-21 23:01:09 dairiki Exp $');
2 /*
3  Copyright (C) 2002 Johannes Große (Johannes Gro&szlig;e)
4
5  This file is (not yet) part of PhpWiki.
6
7  PhpWiki is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation; either version 2 of the License, or
10  (at your option) any later version.
11
12  PhpWiki is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  GNU General Public License for more details.
16
17  You should have received a copy of the GNU General Public License
18  along with PhpWiki; if not, write to the Free Software
19  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */ 
21
22 /**
23  * You should set up the options in plugincache-config.php
24  * $ pear install http://pear.php.net/get/Cache
25  * This file belongs to WikiPluginCached.
26  * @author  Johannes Große
27  * @version 0.8
28  */
29
30 require_once "lib/WikiPlugin.php";
31 require_once "lib/plugincache-config.php";
32 // Try the installed pear class first. It might be newer. Not yet.
33 // @require_once('Cache.php');
34 // if (!class_exists('Cache'))
35      require_once('lib/pear/Cache.php'); // We have to create your own copy here.
36
37 define('PLUGIN_CACHED_HTML',0);
38 define('PLUGIN_CACHED_IMG_INLINE',1);
39 define('PLUGIN_CACHED_IMG_ONDEMAND',2);
40 define('PLUGIN_CACHED_MAP',3);
41
42 /**
43  * An extension of the WikiPlugin class to allow image output and      
44  * cacheing.                                                         
45  * There are several abstract functions to be overloaded. 
46  * Have a look at the example files
47  * <ul><li>plugin/TexToPng.php</li>
48  *     <li>plugin/CacheTest.php (extremely simple example)</li>
49  *     <li>plugin/RecentChanges.php</li>
50  *     <li>plugin/VisualWiki.php</li></ul>
51  *
52  * @author  Johannes Große
53  * @version 0.8
54  */                                                                
55 class WikiPluginCached extends WikiPlugin
56 {   
57     /** 
58      * Produces URL and id number from plugin arguments which later on,
59      * will allow to find a cached image or to reconstruct the complete 
60      * plugin call to recreate the image.
61      * 
62      * @param cache    object the cache object used to store the images
63      * @param argarray array  all parameters (including those set to 
64      *                        default values) of the plugin call to be
65      *                        prepared
66      * @access private
67      * @return array(id,url)  
68      */
69     function genUrl($cache,$argarray) {
70         $cacheparams = $GLOBALS['CacheParams'];
71
72         $plugincall = serialize( array( 
73             'pluginname' => $this->getName(),
74             'arguments'  => $argarray ) ); 
75         $id = $cache->generateId( $plugincall );
76
77         $url = $cacheparams['cacheurl']; // FIXME: SERVER_URL ?
78         if (($lastchar = substr($url,-1)) == '/') {
79             $url = substr($url, 0, -1);
80         }
81         if (strlen($plugincall)>$cacheparams['maxarglen']) {
82             // we can't send the data as URL so we just send the id  
83
84             if (!$request->getSessionVar('imagecache'.$id)) {
85                 $request->setSessionVar('imagecache'.$id, $plugincall);
86             } 
87             $plugincall = false; // not needed anymore
88         }
89
90         if ($lastchar=='?') {
91             // this indicates that a direct call of the image creation
92             // script is wished ($url is assumed to link to the script)
93             $url .= "id=$id" . ($plugincall ? '&args='.rawurlencode($plugincall) : '');
94         } else {
95             // we are supposed to use the indirect 404 ErrorDocument method
96             // ($url is assumed to be the url of the image in 
97             //  cache_dir and the image creation script is referred to in the 
98             //  ErrorDocument 404 directive.)
99             $url .= '/' . $cacheparams['filename_prefix'] . $id . '.img' 
100                     . ($plugincall ? '?args='.rawurlencode($plugincall) : '');
101         }
102         return array($id, $url);
103     } // genUrl
104
105     /** 
106      * Replaces the abstract run method of WikiPlugin to implement
107      * a cache check which can avoid redundant runs. 
108      * <b>Do not override this method in a subclass. Instead you may
109      * rename your run method to getHtml, getImage or getMap.
110      * Have a close look on the arguments and required return values,
111      * however. </b>  
112      * 
113      * @access protected
114      * @param  dbi       WikiDB  database abstraction class
115      * @param  argstr    string  plugin arguments in the call from PhpWiki
116      * @param  request   Request ???
117      * @param  string    basepage Pagename to use to interpret links [/relative] page names.
118      * @return           string  HTML output to be printed to browser
119      *
120      * @see #getHtml
121      * @see #getImage
122      * @see #getMap
123      */
124     function run($dbi, $argstr, $request, $basepage) {
125         $cache = WikiPluginCached::newCache();
126         $cacheparams = $GLOBALS['CacheParams'];
127
128         $sortedargs = $this->getArgs($argstr, $request);
129         if (is_array($sortedargs) )
130             ksort($sortedargs);
131
132         list($id,$url) = $this->genUrl($cache, $sortedargs);
133
134         // ---------- html and img gen. -----------------
135         if ($this->getPluginType() == PLUGIN_CACHED_IMG_ONDEMAND) {
136             return $this->embedImg($url, $dbi, $sortedargs, $request);
137         }
138
139         $do_save = false;
140         $content = $cache->get($id, 'imagecache');
141         switch($this->getPluginType()) {
142             case PLUGIN_CACHED_HTML:
143                 if (!$content || !$content['html']) {
144                     $this->resetError();
145                     $content['html'] = $this->getHtml($dbi,$sortedargs,$request,$basepage);
146                     if ($errortext = $this->getError()) {
147                         WikiPluginCached::printError($errortext,'html');
148                         return HTML();
149                     }
150                     $do_save = true;
151                 } 
152                 break;
153             case PLUGIN_CACHED_IMG_INLINE:
154                 if ($cacheparams['usecache']&&(!$content || !$content['image'])) {
155                     $do_save = WikiPluginCached::produceImage($content, $this, $dbi, $sortedargs, $request, 'html');
156                     $content['html'] = $do_save?$this->embedImg($url, $dbi, $sortedargs, $request) : false;
157                 }
158                 break;
159             case PLUGIN_CACHED_MAP:
160                 if (!$content || !$content['image'] || !$content['html'] ) {
161                     $do_save = WikiPluginCached::produceImage($content, $this, $dbi, $sortedargs, $request, 'html');
162                     $content['html'] = $do_save?WikiPluginCached::embedMap($id,
163                         $url,$content['html'],$dbi,$sortedargs,$request):false;
164                 }
165                 break;
166         }
167         if ($do_save) {
168             $expire = $this->getExpire($dbi,$sortedargs,$request);
169             $cache->save($id, $content, $expire,'imagecache');
170         }
171         if ($content['html'])
172             return $content['html'];
173         return HTML();
174     } // run
175
176
177     /* --------------------- virtual or abstract functions ----------- */
178
179     /**
180      * Sets the type of the plugin to html, image or map 
181      * prodcution
182      *
183      * @access protected 
184      * @return int determines the plugin to produce either html, 
185      *             an image or an image map; uses on of the 
186      *             following predefined values
187      *             <ul> 
188      *             <li>PLUGIN_CACHED_HTML</li>
189      *             <li>PLUGIN_CACHED_IMG_INLINE</li>
190      *             <li>PLUGIN_CACHED_IMG_ONDEMAND</li>
191      *             <li>PLUGIN_CACHED_MAP</li>
192      *             </ul>    
193      */
194     function getPluginType() {
195         return PLUGIN_CACHED_IMG_ONDEMAND;
196     }
197
198
199
200     /** 
201      * Creates an image handle from the given user arguments. 
202      * This method is only called if the return value of 
203      * <code>getPluginType</code> is set to 
204      * PLUGIN_CACHED_IMG_INLINE or PLUGIN_CACHED_IMG_ONDEMAND.
205      *
206      * @access protected pure virtual
207      * @param  dbi       WikiDB       database abstraction class
208      * @param  argarray  array        complete (!) arguments to produce 
209      *                                image. It is not necessary to call 
210      *                                WikiPlugin->getArgs anymore.
211      * @param  request   Request      ??? 
212      * @return           imagehandle  image handle if successful
213      *                                false if an error occured
214      */
215     function getImage($dbi,$argarray,$request) {
216         trigger_error('WikiPluginCached::getImage: pure virtual function in file ' 
217                       . __FILE__ . ' line ' . __LINE__, E_USER_ERROR);
218         return false;
219     }
220
221     /** 
222      * Sets the life time of a cache entry in seconds. 
223      * Expired entries are not used anymore.
224      * During a garbage collection each expired entry is
225      * removed. If removing all expired entries is not
226      * sufficient, the expire time is ignored and removing
227      * is determined by the last "touch" of the entry.
228      * 
229      * @access protected virtual
230      * @param  dbi       WikiDB       database abstraction class
231      * @param  argarray  array        complete (!) arguments. It is
232      *                                not necessary to call 
233      *                                WikiPlugin->getArgs anymore.
234      * @param  request   Request      ??? 
235      * @return           string       format: '+seconds'
236      *                                '0' never expires
237      */
238     function getExpire($dbi,$argarray,$request) {
239         return '0'; // persist forever
240     }
241
242     /** 
243      * Decides the image type of an image output. 
244      * Always used unless plugin type is PLUGIN_CACHED_HTML.
245      * 
246      * @access protected virtual
247      * @param  dbi       WikiDB       database abstraction class
248      * @param  argarray  array        complete (!) arguments. It is
249      *                                not necessary to call 
250      *                                WikiPlugin->getArgs anymore.
251      * @param  request   Request      ??? 
252      * @return           string       'png', 'jpeg' or 'gif'
253      */    
254     function getImageType($dbi,$argarray,$request) {
255         if (in_array($argarray['imgtype'],$GLOBAL['CacheParams']['imgtypes']))
256             return $argarray['imgtype'];
257         else
258             return 'png';
259     }
260
261
262     /** 
263      * Produces the alt text for an image.
264      * <code> &lt;img src=... alt="getAlt(...)"&gt; </code> 
265      *
266      * @access protected virtual
267      * @param  dbi       WikiDB       database abstraction class
268      * @param  argarray  array        complete (!) arguments. It is
269      *                                not necessary to call 
270      *                                WikiPlugin->getArgs anymore.
271      * @param  request   Request      ??? 
272      * @return           string       "alt" description of the image
273      */
274     function getAlt($dbi,$argarray,$request) {
275         return '<?plugin '.$this->getName().' '.$this->glueArgs($argarray).'?>';
276     }
277
278     /** 
279      * Creates HTML output to be cached.  
280      * This method is only called if the plugin_type is set to 
281      * PLUGIN_CACHED_HTML.
282      *
283      * @access protected pure virtual
284      * @param  dbi       WikiDB       database abstraction class
285      * @param  argarray  array        complete (!) arguments to produce 
286      *                                image. It is not necessary to call 
287      *                                WikiPlugin->getArgs anymore.
288      * @param  request   Request      ??? 
289      * @param  string    $basepage    Pagename to use to interpret links [/relative] page names.
290      * @return           string       html to be printed in place of the plugin command
291      *                                false if an error occured
292      */
293     function getHtml($dbi, $argarray, $request, $basepage) {
294         trigger_error('WikiPluginCached::getHtml: pure virtual function in file ' 
295                       . __FILE__ . ' line ' . __LINE__, E_USER_ERROR);
296     }
297
298
299     /** 
300      * Creates HTML output to be cached.  
301      * This method is only called if the plugin_type is set to 
302      * PLUGIN_CACHED_HTML.
303      *
304      * @access protected pure virtual
305      * @param  dbi       WikiDB       database abstraction class
306      * @param  argarray  array        complete (!) arguments to produce 
307      *                                image. It is not necessary to call 
308      *                                WikiPlugin->getArgs anymore.
309      * @param  request   Request      ??? 
310      * @return array(html,handle)     html for the map interior (to be specific,
311      *                                only &lt;area;&gt; tags defining hot spots)
312      *                                handle is an imagehandle to the corresponding
313      *                                image.
314      *                                array(false,false) if an error occured
315      */
316     function getMap($dbi, $argarray, $request) {
317         trigger_error('WikiPluginCached::getHtml: pure virtual function in file ' 
318                       . __FILE__ . ' line ' . __LINE__, E_USER_ERROR);
319     }
320
321     /* --------------------- produce Html ----------------------------- */
322
323     /** 
324      * Creates an HTML map hyperlinked to the image specified
325      * by url and containing the hotspots given by map.
326      *
327      * @access private
328      * @param  id       string  unique id for the plugin call
329      * @param  url      string  url pointing to the image part of the map
330      * @param  map      string  &lt;area&gt; tags defining active
331      *                          regions in the map
332      * @param  dbi      WikiDB  database abstraction class
333      * @param  argarray array   complete (!) arguments to produce 
334      *                          image. It is not necessary to call 
335      *                          WikiPlugin->getArgs anymore.
336      * @param  request  Request ??? 
337      * @return          string  html output
338      */
339     function embedMap($id,$url,$map,$dbi,$argarray,$request) {
340         // id is not unique if the same map is produced twice
341         $key = substr($id,0,8).substr(microtime(),0,6);
342         return HTML(HTML::map(array( 'name' => $key ), $map ),
343                     HTML::img( array(
344                    'src'    => $url, 
345                    //  'alt'    => htmlspecialchars($this->getAlt($dbi,$argarray,$request)) 
346                    'usemap' => '#'.$key ))
347                );
348     }
349
350     /** 
351      * Creates an HTML &lt;img&gt; tag hyperlinking to the specified
352      * url and produces an alternative text for non-graphical
353      * browsers.
354      *
355      * @access private
356      * @param  url      string  url pointing to the image part of the map
357      * @param  map      string  &lt;area&gt; tags defining active
358      *                          regions in the map
359      * @param  dbi      WikiDB  database abstraction class
360      * @param  argarray array   complete (!) arguments to produce 
361      *                          image. It is not necessary to call 
362      *                          WikiPlugin->getArgs anymore.
363      * @param  request  Request ??? 
364      * @return          string  html output
365      */
366     function embedImg($url,$dbi,$argarray,$request) {
367         return HTML::img( array( 
368             'src' => $url,
369             'alt' => htmlspecialchars($this->getAlt($dbi,$argarray,$request)) ) );         
370     }
371
372
373 // --------------------------------------------------------------------------
374 // ---------------------- static member functions ---------------------------
375 // --------------------------------------------------------------------------
376
377     /** 
378      * Creates one static PEAR Cache object and returns copies afterwards.
379      * FIXME: There should be references returned
380      *
381      * @access static protected
382      * @return Cache  copy of the cache object
383      */
384     function newCache() {
385         static $staticcache;
386   
387         $cacheparams = $GLOBALS['CacheParams'];
388
389         if (!is_object($staticcache)) {
390          /*  $pearFinder = new PearFileFinder;
391             $pearFinder->includeOnce('Cache.php');*/
392
393             $staticcache = new Cache($cacheparams['database'],$cacheparams); 
394             $staticcache->gc_maxlifetime = $cacheparams['maxlifetime'];
395  
396             if (!$cacheparams['usecache']) {
397                 $staticcache->setCaching(false);
398             }
399         }
400         return $staticcache; // FIXME: use references ?
401     }
402
403     /** 
404      * Determines whether a needed image type may is available 
405      * from the GD library and gives an alternative otherwise.
406      *
407      * @access  public static
408      * @param   wish   string one of 'png', 'gif', 'jpeg', 'jpg'
409      * @return         string the image type to be used ('png', 'gif', 'jpeg')
410      *                        'html' in case of an error
411      */
412
413     function decideImgType($wish) {
414         if ($wish=='html') return $wish;                 
415         if ($wish=='jpg') { $wish = 'jpeg'; }
416
417         $supportedtypes = array();
418         // Todo: swf, pdf, ...
419         $imagetypes = array(  
420             'png'   => IMG_PNG,
421             'gif'   => IMG_GIF,                             
422             'jpeg'  => IMG_JPEG,
423             'wbmp'  => IMG_WBMP,
424             'xpm'   => IMG_XPM,
425             /* // these do work but not with the ImageType bitmask
426             'gd'    => IMG_GD,
427             'gd2'   => IMG_GD,
428             'xbm'   => IMG_XBM,
429             */
430             );
431
432         $presenttypes = ImageTypes();
433         foreach($imagetypes as $imgtype => $bitmask)
434             if ( $presenttypes && $bitmask )
435                 array_push($supportedtypes, $imgtype);        
436
437         if (in_array($wish, $supportedtypes)) 
438             return $wish;
439         elseif (!empty($supportedtypes))
440             return reset($supportedtypes);
441         else
442             return 'html';
443         
444     } // decideImgType
445
446
447     /** 
448      * Writes an image into a file or to the browser.
449      * Note that there is no check if the image can 
450      * be written.
451      *
452      * @access  public static
453      * @param   imgtype   string 'png', 'gif' or 'jpeg'
454      * @param   imghandle string image handle containing the image
455      * @param   imgfile   string file name of the image to be produced
456      * @return  void
457      * @see     decideImageType
458      */
459     function writeImage($imgtype, $imghandle, $imgfile=false) {
460         if ($imgtype != 'html') {
461             $func = "Image" . strtoupper($imgtype);    
462             if ($imgfile) {
463                 $func($imghandle,$imgfile);
464             } else {
465                 $func($imghandle);
466             }
467         }
468     } // writeImage
469
470
471     /** 
472      * Sends HTTP Header for some predefined file types.
473      * There is no parameter check.
474      *
475      * @access  public static
476      * @param   doctype string 'gif', 'png', 'jpeg', 'html'
477      * @return  void 
478      */
479     function writeHeader($doctype) {
480         $IMAGEHEADER = array( 
481             'gif'  => 'Content-type: image/gif',
482             'png'  => 'Content-type: image/png',
483             'jpeg' => 'Content-type: image/jpeg',
484             'xbm'  => 'Content-type: image/xbm',
485             'xpm'  => 'Content-type: image/xpm',
486             'gd'   => 'Content-type: image/gd',
487             'gd2'  => 'Content-type: image/gd2',
488             'wbmp' => 'Content-type: image/vnd.wap.wbmp', // wireless bitmaps for PDA's and such.
489             'html' => 'Content-type: text/html' );
490         // Todo: swf
491        Header($IMAGEHEADER[$doctype]);
492     } // writeHeader
493
494
495     /** 
496      * Converts argument array to a string of format option="value". 
497      * This should only be used for displaying plugin options for 
498      * the quoting of arguments is not safe, yet.
499      *
500      * @access public static
501      * @param  argarray array   contains all arguments to be converted
502      * @return          string  concated arguments
503      */
504     function glueArgs($argarray) {
505         if (!empty($argarray)) {
506             $argstr = '';
507             while (list($key,$value)=each($argarray)) {
508                 $argstr .= $key. '=' . '"' . $value . '" ';  
509             // FIXME FIXME: How are values quoted? Can a value contain " ?
510             }
511             return substr($argstr,0,strlen($argstr)-1);
512         }
513         return '';
514     } // glueArgs
515
516     // ---------------------- FetchImageFromCache ------------------------------
517
518     /** 
519      * Extracts the cache entry id from the url and the plugin call
520      * parameters if available.
521      *
522      * @access private static
523      * @param  id           string   return value. Image is stored under this id.
524      * @param  plugincall   string   return value. Only returned if present in url.
525      *                               Contains all parameters to reconstruct
526      *                               plugin call.
527      * @param  cache        Cache    PEAR Cache object
528      * @param  request      Request  ???
529      * @param  errorformat  string   format which should be used to
530      *                               output errors ('html', 'png', 'gif', 'jpeg')
531      * @return boolean               false if an error occurs, true otherwise.
532      *                               Param id and param plugincall are
533      *                               also return values.
534      */
535     function checkCall1(&$id, &$plugincall,$cache,$request, $errorformat) {
536         $id=$request->getArg('id');
537         $plugincall=rawurldecode($request->getArg('args')); 
538
539         if (!$id) {
540            if (!$plugincall) {
541                 // This should never happen, so do not gettextify.
542                 $errortext = "Neither 'args' nor 'id' given. Cannot proceed without parameters.";
543                 WikiPluginCached::printError($errorformat, $errortext);
544                 return false;
545             } else {
546                 $id = $cache->generateId( $plugincall );
547             }
548         }   
549         return true;     
550     } // checkCall1
551
552
553     /** 
554      * Extracts the parameters necessary to reconstruct the plugin
555      * call needed to produce the requested image. 
556      *
557      * @access static private  
558      * @param  plugincall string   reference to serialized array containing both 
559      *                             name and parameters of the plugin call
560      * @param  request    Request  ???
561      * @return            boolean  false if an error occurs, true otherwise.
562      *                 
563      */
564     function checkCall2(&$plugincall,$request) {
565         // if plugincall wasn't sent by URL, it must have been
566         // stored in a session var instead and we can retreive it from there
567         if (!$plugincall) {
568             if (!$plugincall=$request->getSessionVar('imagecache'.$id)) {
569                 // I think this is the only error which may occur
570                 // without having written bad code. So gettextify it.
571                 $errortext = sprintf(
572                     gettext ("There is no image creation data available to id '%s'. Please reload referring page." ),
573                     $id );  
574                 WikiPluginCached::printError($errorformat, $errortext);
575                 return false; 
576             }       
577         }
578         $plugincall = unserialize($plugincall);
579         return true;
580     } // checkCall2
581
582
583     /** 
584      * Creates an image or image map depending on the plugin type. 
585      * @access static private 
586      * @param  content array             reference to created array which overwrite the keys
587      *                                   'image', 'imagetype' and possibly 'html'
588      * @param  plugin  WikiPluginCached  plugin which is called to create image or map
589      * @param  dbi     WikiDB            handle to database
590      * @param  argarray array            Contains all arguments needed by plugin
591      * @param  request Request           ????
592      * @param  errorformat string        outputs errors in 'png', 'gif', 'jpg' or 'html'
593      * @return boolean                   error status; true=ok; false=error
594      */
595     function produceImage(&$content, $plugin, $dbi, $argarray, $request, $errorformat) {
596         $plugin->resetError();
597         $content['html'] = $imagehandle = false;
598         if ($plugin->getPluginType() == PLUGIN_CACHED_MAP ) {
599             list($imagehandle,$content['html']) = $plugin->getMap($dbi, $argarray, $request);
600         } else {
601             $imagehandle = $plugin->getImage($dbi, $argarray, $request);
602         }
603
604         $content['imagetype'] 
605             = WikiPluginCached::decideImgType($plugin->getImageType($dbi, $argarray, $request));
606         $errortext = $plugin->getError();
607
608         if (!$imagehandle||$errortext) {
609             if (!$errortext) {
610                 $errortext = "'<?plugin ".$plugin->getName(). ' '
611                     . WikiPluginCached::glueArgs($argarray)." ?>' returned no image, " 
612                     . " although no error was reported.";
613             }
614             WikiPluginCached::printError($errorformat, $errortext);
615             return false; 
616         }
617
618         // image handle -> image data        
619         $cacheparams = $GLOBALS['CacheParams'];
620         $tmpfile = tempnam($cacheparams['cache_dir'],$cacheparams['filename_prefix']);
621         WikiPluginCached::writeImage($content['imagetype'], $imagehandle, $tmpfile);             
622         ImageDestroy($imagehandle);
623         if (file_exists($tmpfile)) {
624             $fp = fopen($tmpfile,'rb');
625             $content['image'] = fread($fp,filesize($tmpfile));
626             fclose($fp);
627             unlink($tmpfile);
628             if ($content['image'])
629                 return true;
630         }
631         return false;
632     } // produceImage
633
634
635     /** 
636      * Main function for obtaining images from cache or generating on-the-fly
637      * from parameters sent by url or session vars.
638      *
639      * @access static public
640      * @param  dbi     WikiDB            handle to database
641      * @param  request Request           ???
642      * @param  errorformat string        outputs errors in 'png', 'gif', 'jpeg' or 'html'
643      */
644     function fetchImageFromCache($dbi,$request,$errorformat='png') {
645         $cache   = WikiPluginCached::newCache();      
646         $errorformat = WikiPluginCached::decideImgType($errorformat);
647
648         if (!WikiPluginCached::checkCall1($id,$plugincall,$cache,$request,$errorformat)) return false;
649
650         // check cache 
651         $content = $cache->get($id,'imagecache');
652
653         if ($content && $content['image']) {
654             WikiPluginCached::writeHeader($content['imagetype']);
655             print $content['image']; 
656             return true;
657         } 
658
659         // produce image, now. At first, we need plugincall parameters
660         if (!WikiPluginCached::checkCall2($plugincall,$request)) return false;
661
662         $pluginname = $plugincall['pluginname'];
663         $argarray   = $plugincall['arguments'];
664
665         $loader = new WikiPluginLoader;
666         $plugin = $loader->getPlugin($pluginname);
667
668         // cache empty, but image maps have to be created _inline_
669         // so ask user to reload wiki page instead
670         $cacheparams = $GLOBALS['CacheParams'];
671         if (($plugin->getPluginType() == PLUGIN_CACHED_MAP) && $cacheparams['force_syncmap']) {
672             $errortext = gettext('Image map expired. Reload wiki page to recreate its html part.');
673             WikiPluginCached::printError($errorformat, $errortext);
674         }
675
676         
677         if (!WikiPluginCached::produceImage(&$content, $plugin, $dbi, $argarray, $request, $errorformat) ) return false;
678
679         $expire = $plugin->getExpire($dbi,$argarray,$request);
680
681         if ($content['image']) {
682             $cache->save($id, $content, $expire,'imagecache');
683             WikiPluginCached::writeHeader($content['imagetype']); 
684             print $content['image'];
685             return true;
686         }
687
688         $errortext = "Could not create image file from imagehandle.";
689         WikiPluginCached::printError($errorformat, $errortext);
690         return false; 
691     } // FetchImageFromCache
692
693     // -------------------- error handling ---------------------------- 
694
695     /** 
696      * Resets buffer containing all error messages. This is allways
697      * done before invoking any abstract creation routines like
698      * <code>getImage</code>.
699      *
700      * @access private
701      * @return void
702      */
703     function resetError() {
704         $this->_errortext = '';
705     }
706        
707     /** 
708      * Returns all accumulated error messages. 
709      *
710      * @access protected
711      * @return string error messages printed with <code>complain</code>.
712      */
713     function getError() {
714         return $this->_errortext;
715     }
716
717     /** 
718      * Collects the error messages in a string for later output 
719      * by WikiPluginCached. This should be used for any errors
720      * that occur during data (html,image,map) creation.
721      * 
722      * @access protected
723      * @param  addtext string errormessage to be printed (separate 
724      *                        multiple lines with '\n')
725      * @return void
726      */
727     function complain($addtext) {
728         $this->_errortext .= $addtext;
729     }
730
731     /** 
732      * Outputs the error as image if possible or as html text 
733      * if wished or html header has already been sent.
734      *
735      * @access static protected
736      * @param  imgtype string 'png', 'gif', 'jpeg' or 'html'
737      * @param  errortext string guess what?
738      * @return void
739      */
740     function printError($imgtype, $errortext) {
741        $imgtype = WikiPluginCached::decideImgType($imgtype);
742
743        $talkedallready = ob_get_contents() || headers_sent();
744        if (($imgtype=='html') || $talkedallready) {
745            trigger_error($errortext, E_USER_WARNING);
746        } else {
747            $red = array(255,0,0);
748            $grey = array(221,221,221);
749            $im = WikiPluginCached::text2img($errortext, 2, $red, $grey);
750            if (!$im) { 
751                trigger_error($errortext, E_USER_WARNING);
752                return;
753            }
754            WikiPluginCached::writeHeader($imgtype);
755            WikiPluginCached::writeImage($imgtype, $im); 
756            ImageDestroy($im);
757        }
758     } // printError
759
760
761     /** 
762      * Basic text to image converter for error handling which allows
763      * multiple line output.
764      * It will only output the first 25 lines of 80 characters. Both 
765      * values may be smaller if the chosen font is to big for there
766      * is further restriction to 600 pixel in width and 350 in height.
767      * 
768      * @access static public
769      * @param  txt     string  multi line text to be converted
770      * @param  fontnr  integer number (1-5) telling gd which internal font to use;
771      *                         I recommend font 2 for errors and 4 for help texts.
772      * @param  textcol array   text color as a list of the rgb components; array(red,green,blue)
773      * @param  bgcol   array   background color; array(red,green,blue)
774      * @return string          image handle for gd routines
775      */
776     function text2img($txt,$fontnr,$textcol,$bgcol) {
777         // basic (!) output for error handling
778
779         // check parameters
780         if ($fontnr<1 || $fontnr>5) {
781             $fontnr = 2;
782         }
783         if (!is_array($textcol) || !is_array($bgcol)) {
784                 $textcol = array(0,0,0);
785                 $bgcol = array(255,255,255);
786         }
787         foreach( array_merge($textcol,$bgcol) as $component) {
788             if ($component<0 || $component > 255) {
789                 $textcol = array(0,0,0);
790                 $bgcol = array(255,255,255);
791                 break;
792             }
793         }
794
795         // prepare Parameters 
796         
797         // set maximum values
798         $IMAGESIZE  = array(
799             'cols'   => 80,
800             'rows'   => 25,
801             'width'  => 600,
802             'height' => 350 );
803
804         $charx    = ImageFontWidth($fontnr);
805         $chary    = ImageFontHeight($fontnr);
806         $marginx  = $charx;
807         $marginy  = floor($chary/2);
808
809         $IMAGESIZE['cols'] = min($IMAGESIZE['cols'], floor(($IMAGESIZE['width']  - 2*$marginx )/$charx));
810         $IMAGESIZE['rows'] = min($IMAGESIZE['rows'], floor(($IMAGESIZE['height'] - 2*$marginy )/$chary));
811
812         // split lines
813         $y = 0;
814         $wx = 0;
815         do {
816             $len = strlen($txt);
817             $npos = strpos($txt, "\n");
818
819             if ($npos===false) {
820                 $breaklen = min($IMAGESIZE['cols'],$len);
821             } else {
822                 $breaklen = min($npos+1, $IMAGESIZE['cols']);
823             }
824             $lines[$y] = chop(substr($txt, 0, $breaklen));
825             $wx = max($wx,strlen($lines[$y++]));
826             $txt = substr($txt, $breaklen); 
827         } while ($txt && ($y < $IMAGESIZE['rows']));
828
829         // recalculate image size
830         $IMAGESIZE['rows'] = $y;
831         $IMAGESIZE['cols'] = $wx;
832  
833         $IMAGESIZE['width']  = $IMAGESIZE['cols'] * $charx + 2*$marginx;
834         $IMAGESIZE['height'] = $IMAGESIZE['rows'] * $chary + 2*$marginy;
835
836         // create blank image
837         $im = @ImageCreate($IMAGESIZE['width'],$IMAGESIZE['height']);
838
839         $col = ImageColorAllocate($im, $textcol[0], $textcol[1], $textcol[2]); 
840         $bg  = ImageColorAllocate($im, $bgcol[0], $bgcol[1], $bgcol[2]); 
841
842         ImageFilledRectangle($im, 0, 0, $IMAGESIZE['width']-1, $IMAGESIZE['height']-1, $bg);
843     
844         // write text lines
845         foreach($lines as $nr => $textstr) {
846             ImageString( $im, $fontnr, $marginx, $marginy+$nr*$chary, 
847                          $textstr, $col );
848         }
849         return $im;
850     } // text2img
851
852 } // WikiPluginCached
853
854
855 // For emacs users
856 // Local Variables:
857 // mode: php
858 // tab-width: 4
859 // c-basic-offset: 4
860 // c-hanging-comment-ender-p: nil
861 // indent-tabs-mode: nil
862 // End:
863 ?>