1 <?php rcs_id('$Id: WikiPluginCached.php,v 1.14 2004-09-25 16:26:08 rurban Exp $');
3 Copyright (C) 2002 Johannes Große (Johannes Große)
4 Copyright (C) 2004 Reini Urban
6 This file is part of PhpWiki.
8 PhpWiki is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 PhpWiki is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with PhpWiki; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * You should set up the options in config/config.ini at Part seven:
25 * $ pear install http://pear.php.net/get/Cache
26 * This file belongs to WikiPluginCached.
29 require_once "lib/WikiPlugin.php";
30 // require_once "lib/plugincache-config.php"; // replaced by config.ini settings!
32 // Try the system pear class. See newCache()
33 @require_once('Cache.php');
35 define('PLUGIN_CACHED_HTML', 0); // cached html (extensive calculation)
36 define('PLUGIN_CACHED_IMG_INLINE', 1); // gd images
37 define('PLUGIN_CACHED_IMG_ONDEMAND', 2); // don't cache
38 define('PLUGIN_CACHED_MAP', 4); // area maps
39 define('PLUGIN_CACHED_STATIC', 8); // make it available via /uploads/, not via /getimg.php?id=
42 * An extension of the WikiPlugin class to allow image output and
44 * There are several abstract functions to be overloaded.
45 * Have a look at the example files
46 * <ul><li>plugin/TexToPng.php</li>
47 * <li>plugin/CacheTest.php (extremely simple example)</li>
48 * <li>plugin/RecentChangesCached.php</li>
49 * <li>plugin/VisualWiki.php</li>
50 * <li>plugin/Ploticus.php</li>
53 * @author Johannes Große, Reini Urban
55 class WikiPluginCached extends WikiPlugin
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.
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
68 * @return array(id,url)
70 * TODO: check if args is needed at all (on lost cache)
72 function genUrl($cache, $argarray) {
74 //$cacheparams = $GLOBALS['CacheParams'];
76 $plugincall = serialize( array(
77 'pluginname' => $this->getName(),
78 'arguments' => $argarray ) );
79 $id = $cache->generateId( $plugincall );
80 $plugincall_arg = rawurlencode($plugincall);
81 //$plugincall_arg = md5($plugincall); // will not work if plugin has to recreate content and cache is lost
83 $url = DATA_PATH . '/getimg.php?';
84 if (($lastchar = substr($url,-1)) == '/') {
85 $url = substr($url, 0, -1);
87 if (strlen($plugincall_arg) > PLUGIN_CACHED_MAXARGLEN) {
88 // we can't send the data as URL so we just send the id
89 if (!$request->getSessionVar('imagecache'.$id)) {
90 $request->setSessionVar('imagecache'.$id, $plugincall);
92 $plugincall_arg = false; // not needed anymore
95 if ($lastchar == '?') {
96 // this indicates that a direct call of the image creation
97 // script is wished ($url is assumed to link to the script)
98 $url .= "id=$id" . ($plugincall_arg ? '&args='.$plugincall_arg : '');
100 // Not yet supported.
101 // We are supposed to use the indirect 404 ErrorDocument method
102 // ($url is assumed to be the url of the image in
103 // cache_dir and the image creation script is referred to in the
104 // ErrorDocument 404 directive.)
105 $url .= '/' . PLUGIN_CACHED_FILENAME_PREFIX . $id . '.img'
106 . ($plugincall_arg ? '?args='.$plugincall_arg : '');
108 if ($request->getArg("start_debug"))
109 $url .= "&start_debug=1";
110 return array($id, $url);
114 * Replaces the abstract run method of WikiPlugin to implement
115 * a cache check which can avoid redundant runs.
116 * <b>Do not override this method in a subclass. Instead you may
117 * rename your run method to getHtml, getImage or getMap.
118 * Have a close look on the arguments and required return values,
122 * @param dbi WikiDB database abstraction class
123 * @param argstr string plugin arguments in the call from PhpWiki
124 * @param request Request ???
125 * @param string basepage Pagename to use to interpret links [/relative] page names.
126 * @return string HTML output to be printed to browser
132 function run ($dbi, $argstr, &$request, $basepage) {
133 $cache = $this->newCache();
134 //$cacheparams = $GLOBALS['CacheParams'];
136 $sortedargs = $this->getArgs($argstr, $request);
137 if (is_array($sortedargs) )
139 $this->_args =& $sortedargs;
140 $this->_type = $this->getPluginType();
141 if ($this->_type & PLUGIN_CACHED_STATIC
142 or $request->getArg('action') == 'pdf') // htmldoc doesn't grok subrequests
144 $this->_type = $this->_type & ~PLUGIN_CACHED_STATIC;
145 $this->_static = true;
148 // ---------- embed static image, no getimg.php? url -----------------
149 if (0 and $this->_static) {
150 //$content = $cache->get($id, 'imagecache');
152 if ($this->produceImage($content, $this, $dbi, $sortedargs, $request, 'html')) {
153 // save the image in uploads
154 return $this->embedImg($content['url'], $dbi, $sortedargs, $request);
156 // copy the cached image into uploads if older
161 list($id, $url) = $this->genUrl($cache, $sortedargs);
162 // ---------- don't check cache: html and img gen. -----------------
163 // override global PLUGIN_CACHED_USECACHE for a plugin
164 if ($this->getPluginType() & PLUGIN_CACHED_IMG_ONDEMAND) {
165 if ($this->_static and $this->produceImage($content, $this, $dbi, $sortedargs, $request, 'html'))
166 $url = $content['url'];
167 return $this->embedImg($url, $dbi, $sortedargs, $request);
171 $content = $cache->get($id, 'imagecache');
172 switch ($this->_type) {
173 case PLUGIN_CACHED_HTML:
174 if (!$content || !$content['html']) {
176 $content['html'] = $this->getHtml($dbi, $sortedargs, $request, $basepage);
177 if ($errortext = $this->getError()) {
178 $this->printError($errortext, 'html');
184 case PLUGIN_CACHED_IMG_INLINE:
185 if (PLUGIN_CACHED_USECACHE && (!$content || !$content['image'])) { // new
186 $do_save = $this->produceImage($content, $this, $dbi, $sortedargs, $request, 'html');
187 if ($this->_static) $url = $content['url'];
188 $content['html'] = $do_save ? $this->embedImg($url, $dbi, $sortedargs, $request) : false;
189 } elseif (!empty($content['url']) && $this->_static) { // already in cache
190 $content['html'] = $this->embedImg($content['url'], $dbi, $sortedargs, $request);
191 } elseif (!empty($content['image']) && $this->_static) { // copy from cache to upload
192 $do_save = $this->produceImage($content, $this, $dbi, $sortedargs, $request, 'html');
193 $url = $content['url'];
194 $content['html'] = $do_save ? $this->embedImg($url, $dbi, $sortedargs, $request) : false;
197 case PLUGIN_CACHED_MAP:
198 if (!$content || !$content['image'] || !$content['html'] ) {
199 $do_save = $this->produceImage($content, $this, $dbi, $sortedargs, $request, 'html');
200 if ($this->_static) $url = $content['url'];
201 $content['html'] = $do_save
202 ? $this->embedMap($id, $url, $content['html'], $dbi, $sortedargs, $request)
208 $expire = $this->getExpire($dbi, $sortedargs, $request);
209 $content['args'] = $sortedargs;
210 $cache->save($id, $content, $expire, 'imagecache');
212 if ($content['html'])
213 return $content['html'];
218 /* --------------------- virtual or abstract functions ----------- */
221 * Sets the type of the plugin to html, image or map
225 * @return int determines the plugin to produce either html,
226 * an image or an image map; uses on of the
227 * following predefined values
229 * <li>PLUGIN_CACHED_HTML</li>
230 * <li>PLUGIN_CACHED_IMG_INLINE</li>
231 * <li>PLUGIN_CACHED_IMG_ONDEMAND</li>
232 * <li>PLUGIN_CACHED_MAP</li>
235 function getPluginType() {
236 return PLUGIN_CACHED_IMG_ONDEMAND;
240 * Creates an image handle from the given user arguments.
241 * This method is only called if the return value of
242 * <code>getPluginType</code> is set to
243 * PLUGIN_CACHED_IMG_INLINE or PLUGIN_CACHED_IMG_ONDEMAND.
245 * @access protected pure virtual
246 * @param dbi WikiDB database abstraction class
247 * @param argarray array complete (!) arguments to produce
248 * image. It is not necessary to call
249 * WikiPlugin->getArgs anymore.
250 * @param request Request ???
251 * @return imagehandle image handle if successful
252 * false if an error occured
254 function getImage($dbi,$argarray,$request) {
255 trigger_error('WikiPluginCached::getImage: pure virtual function in file '
256 . __FILE__ . ' line ' . __LINE__, E_USER_ERROR);
261 * Sets the life time of a cache entry in seconds.
262 * Expired entries are not used anymore.
263 * During a garbage collection each expired entry is
264 * removed. If removing all expired entries is not
265 * sufficient, the expire time is ignored and removing
266 * is determined by the last "touch" of the entry.
268 * @access protected virtual
269 * @param dbi WikiDB database abstraction class
270 * @param argarray array complete (!) arguments. It is
271 * not necessary to call
272 * WikiPlugin->getArgs anymore.
273 * @param request Request ???
274 * @return string format: '+seconds'
277 function getExpire($dbi,$argarray,$request) {
278 return '0'; // persist forever
282 * Decides the image type of an image output.
283 * Always used unless plugin type is PLUGIN_CACHED_HTML.
285 * @access protected virtual
286 * @param dbi WikiDB database abstraction class
287 * @param argarray array complete (!) arguments. It is
288 * not necessary to call
289 * WikiPlugin->getArgs anymore.
290 * @param request Request ???
291 * @return string 'png', 'jpeg' or 'gif'
293 function getImageType(&$dbi, $argarray, &$request) {
294 if (in_array($argarray['imgtype'], preg_split('/\s*:\s*/', PLUGIN_CACHED_IMGTYPES)))
295 return $argarray['imgtype'];
301 * Produces the alt text for an image.
302 * <code> <img src=... alt="getAlt(...)"> </code>
304 * @access protected virtual
305 * @param dbi WikiDB database abstraction class
306 * @param argarray array complete (!) arguments. It is
307 * not necessary to call
308 * WikiPlugin->getArgs anymore.
309 * @param request Request ???
310 * @return string "alt" description of the image
312 function getAlt($dbi,$argarray,$request) {
313 return '<?plugin '.$this->getName().' '.$this->glueArgs($argarray).'?>';
317 * Creates HTML output to be cached.
318 * This method is only called if the plugin_type is set to
319 * PLUGIN_CACHED_HTML.
321 * @access protected pure virtual
322 * @param dbi WikiDB database abstraction class
323 * @param argarray array complete (!) arguments to produce
324 * image. It is not necessary to call
325 * WikiPlugin->getArgs anymore.
326 * @param request Request ???
327 * @param string $basepage Pagename to use to interpret links [/relative] page names.
328 * @return string html to be printed in place of the plugin command
329 * false if an error occured
331 function getHtml($dbi, $argarray, $request, $basepage) {
332 trigger_error('WikiPluginCached::getHtml: pure virtual function in file '
333 . __FILE__ . ' line ' . __LINE__, E_USER_ERROR);
337 * Creates HTML output to be cached.
338 * This method is only called if the plugin_type is set to
339 * PLUGIN_CACHED_HTML.
341 * @access protected pure virtual
342 * @param dbi WikiDB database abstraction class
343 * @param argarray array complete (!) arguments to produce
344 * image. It is not necessary to call
345 * WikiPlugin->getArgs anymore.
346 * @param request Request ???
347 * @return array(html,handle) html for the map interior (to be specific,
348 * only <area;> tags defining hot spots)
349 * handle is an imagehandle to the corresponding
351 * array(false,false) if an error occured
353 function getMap($dbi, $argarray, $request) {
354 trigger_error('WikiPluginCached::getHtml: pure virtual function in file '
355 . __FILE__ . ' line ' . __LINE__, E_USER_ERROR);
358 /* --------------------- produce Html ----------------------------- */
361 * Creates an HTML map hyperlinked to the image specified
362 * by url and containing the hotspots given by map.
365 * @param id string unique id for the plugin call
366 * @param url string url pointing to the image part of the map
367 * @param map string <area> tags defining active
369 * @param dbi WikiDB database abstraction class
370 * @param argarray array complete (!) arguments to produce
371 * image. It is not necessary to call
372 * WikiPlugin->getArgs anymore.
373 * @param request Request ???
374 * @return string html output
376 function embedMap($id,$url,$map,&$dbi,$argarray,&$request) {
377 // id is not unique if the same map is produced twice
378 $key = substr($id,0,8).substr(microtime(),0,6);
379 return HTML(HTML::map(array( 'name' => $key ), $map ),
382 // 'alt' => htmlspecialchars($this->getAlt($dbi,$argarray,$request))
383 'usemap' => '#'.$key ))
388 * Creates an HTML <img> tag hyperlinking to the specified
389 * url and produces an alternative text for non-graphical
393 * @param url string url pointing to the image part of the map
394 * @param map string <area> tags defining active
396 * @param dbi WikiDB database abstraction class
397 * @param argarray array complete (!) arguments to produce
398 * image. It is not necessary to call
399 * WikiPlugin->getArgs anymore.
400 * @param request Request ???
401 * @return string html output
403 function embedImg($url, $dbi, $argarray, $request) {
404 return HTML::img( array(
406 'alt' => htmlspecialchars($this->getAlt($dbi, $argarray, $request)) ) );
410 // --------------------------------------------------------------------------
411 // ---------------------- static member functions ---------------------------
412 // --------------------------------------------------------------------------
415 * Creates one static PEAR Cache object and returns copies afterwards.
416 * FIXME: There should be references returned
418 * @access static protected
419 * @return Cache copy of the cache object
421 function newCache() {
424 if (!is_object($staticcache)) {
425 if (!class_exists('Cache')) {
426 // uuh, pear not in include_path! should print a warning.
427 // search some possible pear paths.
428 $pearFinder = new PearFileFinder;
429 if ($lib = $pearFinder->findFile('Cache.php', 'missing_ok'))
431 else // fall back to our own copy
432 require_once('lib/pear/Cache.php');
434 $cacheparams = array();
435 foreach (explode(':','database:cache_dir:filename_prefix:highwater:lowwater'
436 .':maxlifetime:maxarglen:usecache:force_syncmap') as $key) {
437 $cacheparams[$key] = constant('PLUGIN_CACHED_'.strtoupper($key));
439 $cacheparams['imgtypes'] = preg_split('/\s*:\s*/', PLUGIN_CACHED_IMGTYPES);
440 $staticcache = new Cache(PLUGIN_CACHED_DATABASE, $cacheparams);
441 $staticcache->gc_maxlifetime = PLUGIN_CACHED_MAXLIFETIME;
443 if (! PLUGIN_CACHED_USECACHE ) {
444 $staticcache->setCaching(false);
447 return $staticcache; // FIXME: use references ?
451 * Determines whether a needed image type may is available
452 * from the GD library and gives an alternative otherwise.
454 * @access public static
455 * @param wish string one of 'png', 'gif', 'jpeg', 'jpg'
456 * @return string the image type to be used ('png', 'gif', 'jpeg')
457 * 'html' in case of an error
460 function decideImgType($wish) {
461 if ($wish=='html') return $wish;
462 if ($wish=='jpg') { $wish = 'jpeg'; }
464 $supportedtypes = array();
465 // Todo: swf, pdf, ...
472 /* // these do work but not with the ImageType bitmask
479 $presenttypes = ImageTypes();
480 foreach($imagetypes as $imgtype => $bitmask)
481 if ( $presenttypes && $bitmask )
482 array_push($supportedtypes, $imgtype);
484 if (in_array($wish, $supportedtypes))
486 elseif (!empty($supportedtypes))
487 return reset($supportedtypes);
495 * Writes an image into a file or to the browser.
496 * Note that there is no check if the image can
499 * @access public static
500 * @param imgtype string 'png', 'gif' or 'jpeg'
501 * @param imghandle string image handle containing the image
502 * @param imgfile string file name of the image to be produced
504 * @see decideImageType
506 function writeImage($imgtype, $imghandle, $imgfile=false) {
507 if ($imgtype != 'html') {
508 $func = "Image" . strtoupper($imgtype);
510 $func($imghandle,$imgfile);
519 * Sends HTTP Header for some predefined file types.
520 * There is no parameter check.
522 * @access public static
523 * @param doctype string 'gif', 'png', 'jpeg', 'html'
526 function writeHeader($doctype) {
527 static $IMAGEHEADER = array(
528 'gif' => 'Content-type: image/gif',
529 'png' => 'Content-type: image/png',
530 'jpeg' => 'Content-type: image/jpeg',
531 'xbm' => 'Content-type: image/xbm',
532 'xpm' => 'Content-type: image/xpm',
533 'gd' => 'Content-type: image/gd',
534 'gd2' => 'Content-type: image/gd2',
535 'wbmp' => 'Content-type: image/vnd.wap.wbmp', // wireless bitmaps for PDA's and such.
536 'html' => 'Content-type: text/html' );
537 // Todo: swf, pdf, svg, svgz
538 Header($IMAGEHEADER[$doctype]);
543 * Converts argument array to a string of format option="value".
544 * This should only be used for displaying plugin options for
545 * the quoting of arguments is not safe, yet.
547 * @access public static
548 * @param argarray array contains all arguments to be converted
549 * @return string concated arguments
551 function glueArgs($argarray) {
552 if (!empty($argarray)) {
554 while (list($key,$value)=each($argarray)) {
555 $argstr .= $key. '=' . '"' . $value . '" ';
556 // FIXME: How are values quoted? Can a value contain '"'?
557 // TODO: rawurlencode(value)
559 return substr($argstr, 0, strlen($argstr)-1);
564 // ---------------------- FetchImageFromCache ------------------------------
567 * Extracts the cache entry id from the url and the plugin call
568 * parameters if available.
570 * @access private static
571 * @param id string return value. Image is stored under this id.
572 * @param plugincall string return value. Only returned if present in url.
573 * Contains all parameters to reconstruct
575 * @param cache Cache PEAR Cache object
576 * @param request Request ???
577 * @param errorformat string format which should be used to
578 * output errors ('html', 'png', 'gif', 'jpeg')
579 * @return boolean false if an error occurs, true otherwise.
580 * Param id and param plugincall are
581 * also return values.
583 function checkCall1(&$id, &$plugincall, $cache, $request, $errorformat) {
584 $id = $request->getArg('id');
585 $plugincall = rawurldecode($request->getArg('args'));
589 // This should never happen, so do not gettextify.
590 $errortext = "Neither 'args' nor 'id' given. Cannot proceed without parameters.";
591 $this->printError($errorformat, $errortext);
594 $id = $cache->generateId( $plugincall );
602 * Extracts the parameters necessary to reconstruct the plugin
603 * call needed to produce the requested image.
605 * @access static private
606 * @param plugincall string reference to serialized array containing both
607 * name and parameters of the plugin call
608 * @param request Request ???
609 * @return boolean false if an error occurs, true otherwise.
612 function checkCall2(&$plugincall, $request) {
613 // if plugincall wasn't sent by URL, it must have been
614 // stored in a session var instead and we can retreive it from there
616 if (!$plugincall=$request->getSessionVar('imagecache'.$id)) {
617 // I think this is the only error which may occur
618 // without having written bad code. So gettextify it.
619 $errortext = sprintf(
620 gettext ("There is no image creation data available to id '%s'. Please reload referring page." ),
622 $this->printError($errorformat, $errortext);
626 $plugincall = unserialize($plugincall);
632 * Creates an image or image map depending on the plugin type.
633 * @access static private
634 * @param content array reference to created array which overwrite the keys
635 * 'image', 'imagetype' and possibly 'html'
636 * @param plugin WikiPluginCached plugin which is called to create image or map
637 * @param dbi WikiDB handle to database
638 * @param argarray array Contains all arguments needed by plugin
639 * @param request Request ????
640 * @param errorformat string outputs errors in 'png', 'gif', 'jpg' or 'html'
641 * @return boolean error status; true=ok; false=error
643 function produceImage(&$content, $plugin, $dbi, $argarray, $request, $errorformat) {
644 $plugin->resetError();
645 $content['html'] = $imagehandle = false;
646 if ($plugin->getPluginType() & PLUGIN_CACHED_MAP ) {
647 list($imagehandle,$content['html']) = $plugin->getMap($dbi, $argarray, $request);
649 $imagehandle = $plugin->getImage($dbi, $argarray, $request);
652 $content['imagetype']
653 = $this->decideImgType($plugin->getImageType($dbi, $argarray, $request));
654 $errortext = $plugin->getError();
656 if (!$imagehandle||$errortext) {
658 $errortext = "'<?plugin ".$plugin->getName(). ' '
659 . $this->glueArgs($argarray)." ?>' returned no image, "
660 . " although no error was reported.";
662 $this->printError($errorformat, $errortext);
666 // image handle -> image data
667 if ($this->_static) {
668 $ext = "." . $content['imagetype'];
669 $tmpfile = tempnam(getUploadFilePath(), PLUGIN_CACHED_FILENAME_PREFIX . $ext);
670 if (!strstr(basename($tmpfile), $ext)) {
674 $tmpfile = getUploadFilePath() . basename($tmpfile);
676 $tmpfile = $this->tempnam();
678 $this->writeImage($content['imagetype'], $imagehandle, $tmpfile);
679 ImageDestroy($imagehandle);
681 if (file_exists($tmpfile)) {
682 $fp = fopen($tmpfile,'rb');
683 $content['image'] = fread($fp, filesize($tmpfile));
685 if ($this->_static) {
686 // on static it is in "uploads/" but in wikicached also
687 $content['file'] = $tmpfile;
688 $content['url'] = getUploadDataPath() . basename($tmpfile);
692 if ($content['image'])
698 function tempnam($prefix = false) {
699 return tempnam(isWindows() ? str_replace('/', "\\", PLUGIN_CACHED_CACHE_DIR) : PLUGIN_CACHED_CACHE_DIR,
700 $prefix ? $prefix : PLUGIN_CACHED_FILENAME_PREFIX);
704 * Main function for obtaining images from cache or generating on-the-fly
705 * from parameters sent by url or session vars.
707 * @access static public
708 * @param dbi WikiDB handle to database
709 * @param request Request ???
710 * @param errorformat string outputs errors in 'png', 'gif', 'jpeg' or 'html'
712 function fetchImageFromCache($dbi, $request, $errorformat='png') {
713 $cache = $this->newCache();
714 $errorformat = $this->decideImgType($errorformat);
716 if (!$this->checkCall1($id, $plugincall, $cache, $request, $errorformat)) return false;
718 $content = $cache->get($id, 'imagecache');
720 if (!empty($content['image'])) {
721 $this->writeHeader($content['imagetype']);
722 print $content['image'];
725 if (!empty($content['html'])) {
726 print $content['html'];
730 if (!empty($content['file']) && !empty($content['url']) && file_exists($content['file'])) {
731 print $this->embedImg($content['url'], $dbi, array(), $request);
735 // re-produce image. At first, we need the plugincall parameters.
736 // Cached args with matching id override given args to shorten getimg.php?id=md5
737 if (!empty($content['args']))
738 $plugincall['arguments'] = $content['args'];
739 if (!$this->checkCall2($plugincall, $request)) return false;
741 $pluginname = $plugincall['pluginname'];
742 $argarray = $plugincall['arguments'];
744 $loader = new WikiPluginLoader;
745 $plugin = $loader->getPlugin($pluginname);
747 // cache empty, but image maps have to be created _inline_
748 // so ask user to reload wiki page instead
749 if (($plugin->getPluginType() & PLUGIN_CACHED_MAP) && PLUGIN_CACHED_FORCE_SYNCMAP) {
750 $errortext = _("Image map expired. Reload wiki page to recreate its html part.");
751 $this->printError($errorformat, $errortext);
754 if (!$this->produceImage($content, $plugin, $dbi, $argarray,
755 $request, $errorformat))
758 $expire = $plugin->getExpire($dbi, $argarray, $request);
760 if ($content['image']) {
761 $cache->save($id, $content, $expire, 'imagecache');
762 $this->writeHeader($content['imagetype']);
763 print $content['image'];
767 $errortext = "Could not create image file from imagehandle.";
768 $this->printError($errorformat, $errortext);
770 } // FetchImageFromCache
772 // -------------------- error handling ----------------------------
775 * Resets buffer containing all error messages. This is allways
776 * done before invoking any abstract creation routines like
777 * <code>getImage</code>.
782 function resetError() {
783 $this->_errortext = '';
787 * Returns all accumulated error messages.
790 * @return string error messages printed with <code>complain</code>.
792 function getError() {
793 return $this->_errortext;
797 * Collects the error messages in a string for later output
798 * by WikiPluginCached. This should be used for any errors
799 * that occur during data (html,image,map) creation.
802 * @param addtext string errormessage to be printed (separate
803 * multiple lines with '\n')
806 function complain($addtext) {
807 $this->_errortext .= $addtext;
811 * Outputs the error as image if possible or as html text
812 * if wished or html header has already been sent.
814 * @access static protected
815 * @param imgtype string 'png', 'gif', 'jpeg' or 'html'
816 * @param errortext string guess what?
819 function printError($imgtype, $errortext) {
820 $imgtype = $this->decideImgType($imgtype);
822 $talkedallready = ob_get_contents() || headers_sent();
823 if (($imgtype=='html') || $talkedallready) {
824 if (is_object($errortext))
825 $errortext = $errortext->asXml();
826 trigger_error($errortext, E_USER_WARNING);
828 $red = array(255,0,0);
829 $grey = array(221,221,221);
830 if (is_object($errortext))
831 $errortext = $errortext->asString();
832 $im = $this->text2img($errortext, 2, $red, $grey);
834 trigger_error($errortext, E_USER_WARNING);
837 $this->writeHeader($imgtype);
838 $this->writeImage($imgtype, $im);
845 * Basic text to image converter for error handling which allows
846 * multiple line output.
847 * It will only output the first 25 lines of 80 characters. Both
848 * values may be smaller if the chosen font is to big for there
849 * is further restriction to 600 pixel in width and 350 in height.
851 * @access static public
852 * @param txt string multi line text to be converted
853 * @param fontnr integer number (1-5) telling gd which internal font to use;
854 * I recommend font 2 for errors and 4 for help texts.
855 * @param textcol array text color as a list of the rgb components; array(red,green,blue)
856 * @param bgcol array background color; array(red,green,blue)
857 * @return string image handle for gd routines
859 function text2img($txt,$fontnr,$textcol,$bgcol) {
860 // basic (!) output for error handling
863 if ($fontnr<1 || $fontnr>5) {
866 if (!is_array($textcol) || !is_array($bgcol)) {
867 $textcol = array(0,0,0);
868 $bgcol = array(255,255,255);
870 foreach( array_merge($textcol,$bgcol) as $component) {
871 if ($component<0 || $component > 255) {
872 $textcol = array(0,0,0);
873 $bgcol = array(255,255,255);
878 // prepare Parameters
880 // set maximum values
887 if (function_exists('ImageFontWidth')) {
888 $charx = ImageFontWidth($fontnr);
889 $chary = ImageFontHeight($fontnr);
891 $charx = 10; $chary = 10;
894 $marginy = floor($chary/2);
896 $IMAGESIZE['cols'] = min($IMAGESIZE['cols'], floor(($IMAGESIZE['width'] - 2*$marginx )/$charx));
897 $IMAGESIZE['rows'] = min($IMAGESIZE['rows'], floor(($IMAGESIZE['height'] - 2*$marginy )/$chary));
904 $npos = strpos($txt, "\n");
907 $breaklen = min($IMAGESIZE['cols'],$len);
909 $breaklen = min($npos+1, $IMAGESIZE['cols']);
911 $lines[$y] = chop(substr($txt, 0, $breaklen));
912 $wx = max($wx,strlen($lines[$y++]));
913 $txt = substr($txt, $breaklen);
914 } while ($txt && ($y < $IMAGESIZE['rows']));
916 // recalculate image size
917 $IMAGESIZE['rows'] = $y;
918 $IMAGESIZE['cols'] = $wx;
920 $IMAGESIZE['width'] = $IMAGESIZE['cols'] * $charx + 2*$marginx;
921 $IMAGESIZE['height'] = $IMAGESIZE['rows'] * $chary + 2*$marginy;
923 // create blank image
924 $im = @ImageCreate($IMAGESIZE['width'],$IMAGESIZE['height']);
926 $col = ImageColorAllocate($im, $textcol[0], $textcol[1], $textcol[2]);
927 $bg = ImageColorAllocate($im, $bgcol[0], $bgcol[1], $bgcol[2]);
929 ImageFilledRectangle($im, 0, 0, $IMAGESIZE['width']-1, $IMAGESIZE['height']-1, $bg);
932 foreach($lines as $nr => $textstr) {
933 ImageString( $im, $fontnr, $marginx, $marginy+$nr*$chary,
939 function newFilterThroughCmd($input, $commandLine) {
940 $descriptorspec = array(
941 0 => array("pipe", "r"), // stdin is a pipe that the child will read from
942 1 => array("pipe", "w"), // stdout is a pipe that the child will write to
943 2 => array("pipe", "w"), // stdout is a pipe that the child will write to
946 $process = proc_open("$commandLine", $descriptorspec, $pipes);
947 if (is_resource($process)) {
948 // $pipes now looks like this:
949 // 0 => writeable handle connected to child stdin
950 // 1 => readable handle connected to child stdout
951 // 2 => readable handle connected to child stderr
952 fwrite($pipes[0], $input);
955 while(!feof($pipes[1])) {
956 $buf .= fgets($pipes[1], 1024);
960 while(!feof($pipes[2])) {
961 $stderr .= fgets($pipes[2], 1024);
964 // It is important that you close any pipes before calling
965 // proc_close in order to avoid a deadlock
966 $return_value = proc_close($process);
967 if (empty($buf)) printXML($this->error($stderr));
972 /* PHP versions < 4.3
973 * TODO: via temp file looks more promising
975 function OldFilterThroughCmd($input, $commandLine) {
976 $input = str_replace ("\\", "\\\\", $input);
977 $input = str_replace ("\"", "\\\"", $input);
978 $input = str_replace ("\$", "\\\$", $input);
979 $input = str_replace ("`", "\`", $input);
980 $input = str_replace ("'", "\'", $input);
981 //$input = str_replace (";", "\;", $input);
983 $pipe = popen("echo \"$input\"|$commandLine", 'r');
985 print "pipe failed.";
989 while (!feof($pipe)) {
990 $output .= fread($pipe, 1024);
996 // run "echo $source | $commandLine" and return result
997 function filterThroughCmd($source, $commandLine) {
998 if (check_php_version(4,3,0))
999 return $this->newFilterThroughCmd($source, $commandLine);
1001 return $this->oldFilterThroughCmd($source, $commandLine);
1004 } // WikiPluginCached
1007 // $Log: not supported by cvs2svn $
1008 // Revision 1.13 2004/09/22 13:46:25 rurban
1009 // centralize upload paths.
1010 // major WikiPluginCached feature enhancement:
1011 // support _STATIC pages in uploads/ instead of dynamic getimg.php? subrequests.
1012 // mainly for debugging, cache problems and action=pdf
1014 // Revision 1.12 2004/09/07 13:26:31 rurban
1015 // new WikiPluginCached option debug=static and some more sf.net defaults for VisualWiki
1017 // Revision 1.11 2004/09/06 09:12:46 rurban
1018 // improve pear handling with silent fallback to ours
1025 // c-basic-offset: 4
1026 // c-hanging-comment-ender-p: nil
1027 // indent-tabs-mode: nil