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