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