• plugin/TexToPng.php
  • *
  • plugin/CacheTest.php (extremely simple example)
  • *
  • plugin/RecentChangesCached.php
  • *
  • plugin/VisualWiki.php
  • *
  • plugin/Ploticus.php
  • * * * @author Johannes Große, Reini Urban */ class WikiPluginCached extends WikiPlugin { public $_static; /** * Produces URL and id number from plugin arguments which later on, * will allow to find a cached image or to reconstruct the complete * plugin call to recreate the image. * * @param cache object the cache object used to store the images * @param argarray array all parameters (including those set to * default values) of the plugin call to be * prepared * @access private * @return array(id,url) * * TODO: check if args is needed at all (on lost cache) */ function genUrl($cache, $argarray) { global $request; //$cacheparams = $GLOBALS['CacheParams']; $plugincall = serialize(array( 'pluginname' => $this->getName(), 'arguments' => $argarray)); $id = $cache->generateId($plugincall); $plugincall_arg = rawurlencode($plugincall); //$plugincall_arg = md5($plugincall); // will not work if plugin has to recreate content and cache is lost $url = DATA_PATH . '/getimg.php?'; if (($lastchar = substr($url, -1)) == '/') { $url = substr($url, 0, -1); } if (strlen($plugincall_arg) > PLUGIN_CACHED_MAXARGLEN) { // we can't send the data as URL so we just send the id if (!$request->getSessionVar('imagecache' . $id)) { $request->setSessionVar('imagecache' . $id, $plugincall); } $plugincall_arg = false; // not needed anymore } if ($lastchar == '?') { // this indicates that a direct call of the image creation // script is wished ($url is assumed to link to the script) $url .= "id=$id" . ($plugincall_arg ? '&args=' . $plugincall_arg : ''); } else { // Not yet supported. // We are supposed to use the indirect 404 ErrorDocument method // ($url is assumed to be the url of the image in // cache_dir and the image creation script is referred to in the // ErrorDocument 404 directive.) $url .= '/' . PLUGIN_CACHED_FILENAME_PREFIX . $id . '.img' . ($plugincall_arg ? '?args=' . $plugincall_arg : ''); } if ($request->getArg("start_debug") and (DEBUG & _DEBUG_REMOTE)) $url .= "&start_debug=1"; return array($id, $url); } // genUrl /** * Replaces the abstract run method of WikiPlugin to implement * a cache check which can avoid redundant runs. * Do not override this method in a subclass. Instead you may * rename your run method to getHtml, getImage or getMap. * Have a close look on the arguments and required return values, * however. * * @access protected * @param WikiDB $dbi database abstraction class * @param string $argstr plugin arguments in the call from PhpWiki * @param Request $request * @param string $basepage Pagename to use to interpret links [/relative] page names. * @return string HTML output to be printed to browser * * @see #getHtml * @see #getImage * @see #getMap */ function run($dbi, $argstr, &$request, $basepage) { $cache = $this->newCache(); //$cacheparams = $GLOBALS['CacheParams']; $sortedargs = $this->getArgs($argstr, $request); if (is_array($sortedargs)) ksort($sortedargs); $this->_args =& $sortedargs; $this->_type = $this->getPluginType(); $this->_static = false; if ($this->_type & PLUGIN_CACHED_STATIC or $request->getArg('action') == 'pdf' ) // htmldoc doesn't grok subrequests { $this->_type = $this->_type & ~PLUGIN_CACHED_STATIC; $this->_static = true; } // ---------- embed static image, no getimg.php? url ----------------- if (0 and $this->_static) { //$content = $cache->get($id, 'imagecache'); $content = array(); if ($this->produceImage($content, $this, $dbi, $sortedargs, $request, 'html')) { // save the image in uploads return $this->embedImg($content['url'], $dbi, $sortedargs, $request); } else { // copy the cached image into uploads if older return HTML(); } } list($id, $url) = $this->genUrl($cache, $sortedargs); // ---------- don't check cache: html and img gen. ----------------- // override global PLUGIN_CACHED_USECACHE for a plugin if ($this->getPluginType() & PLUGIN_CACHED_IMG_ONDEMAND) { if ($this->_static and $this->produceImage($content, $this, $dbi, $sortedargs, $request, 'html')) $url = $content['url']; return $this->embedImg($url, $dbi, $sortedargs, $request); } $do_save = false; $content = $cache->get($id, 'imagecache'); switch ($this->_type) { case PLUGIN_CACHED_HTML: if (!$content || !$content['html']) { $this->resetError(); $content['html'] = $this->getHtml($dbi, $sortedargs, $request, $basepage); if ($errortext = $this->getError()) { $this->printError($errortext, 'html'); return HTML(); } $do_save = true; } break; case PLUGIN_CACHED_IMG_INLINE: if (PLUGIN_CACHED_USECACHE && (!$content || !$content['image'])) { // new $do_save = $this->produceImage($content, $this, $dbi, $sortedargs, $request, 'html'); if ($this->_static) $url = $content['url']; $content['html'] = $do_save ? $this->embedImg($url, $dbi, $sortedargs, $request) : false; } elseif (!empty($content['url']) && $this->_static) { // already in cache $content['html'] = $this->embedImg($content['url'], $dbi, $sortedargs, $request); } elseif (!empty($content['image']) && $this->_static) { // copy from cache to upload $do_save = $this->produceImage($content, $this, $dbi, $sortedargs, $request, 'html'); $url = $content['url']; $content['html'] = $do_save ? $this->embedImg($url, $dbi, $sortedargs, $request) : false; } break; case PLUGIN_CACHED_MAP: if (!$content || !$content['image'] || !$content['html']) { $do_save = $this->produceImage($content, $this, $dbi, $sortedargs, $request, 'html'); if ($this->_static) $url = $content['url']; $content['html'] = $do_save ? $this->embedMap($id, $url, $content['html'], $dbi, $sortedargs, $request) : false; } break; case PLUGIN_CACHED_SVG: if (!$content || !$content['html']) { $do_save = $this->produceImage($content, $this, $dbi, $sortedargs, $request, 'html'); if ($this->_static) $url = $content['url']; $args = array(); //width+height => object args if (!empty($sortedargs['width'])) $args['width'] = $sortedargs['width']; if (!empty($sortedargs['height'])) $args['height'] = $sortedargs['height']; $content['html'] = $do_save ? $this->embedObject($url, 'image/svg+xml', $args, HTML::embed(array_merge( array('src' => $url, 'type' => 'image/svg+xml'), $args))) : false; } break; case PLUGIN_CACHED_SVG_PNG: if (!$content || !$content['html']) { $do_save_svg = $this->produceImage($content, $this, $dbi, $sortedargs, $request, 'html'); if ($this->_static) $url = $content['url']; // hack alert! somehow we should know which argument will produce the secondary image (PNG) $args = $sortedargs; $args[$this->pngArg()] = $content['imagetype']; // default type: PNG or GIF $do_save = $this->produceImage($pngcontent, $this, $dbi, $args, $request, $content['imagetype']); $args = array(); //width+height => object args if (!empty($sortedargs['width'])) $args['width'] = $sortedargs['width']; if (!empty($sortedargs['height'])) $args['height'] = $sortedargs['height']; $content['html'] = $do_save_svg ? $this->embedObject($url, 'image/svg+xml', $args, $this->embedImg($pngcontent['url'], $dbi, $sortedargs, $request)) : false; } break; } if ($do_save) { $content['args'] = md5($this->_pi); $expire = $this->getExpire($dbi, $content['args'], $request); $cache->save($id, $content, $expire, 'imagecache'); } if ($content['html']) return $content['html']; return HTML(); } // run /* --------------------- virtual or abstract functions ----------- */ /** * Sets the type of the plugin to html, image or map * production * * @access protected * @return int determines the plugin to produce either html, * an image or an image map; uses on of the * following predefined values * */ function getPluginType() { return PLUGIN_CACHED_IMG_ONDEMAND; } /** * Creates an image handle from the given user arguments. * This method is only called if the return value of * getPluginType is set to * PLUGIN_CACHED_IMG_INLINE or PLUGIN_CACHED_IMG_ONDEMAND. * * @access protected pure virtual * @param dbi WikiDB database abstraction class * @param argarray array complete (!) arguments to produce * image. It is not necessary to call * WikiPlugin->getArgs anymore. * @param request Request ??? * @return imagehandle image handle if successful * false if an error occured */ function getImage($dbi, $argarray, $request) { trigger_error('WikiPluginCached::getImage: pure virtual function in file ' . __FILE__ . ' line ' . __LINE__, E_USER_ERROR); return false; } /** * Sets the life time of a cache entry in seconds. * Expired entries are not used anymore. * During a garbage collection each expired entry is * removed. If removing all expired entries is not * sufficient, the expire time is ignored and removing * is determined by the last "touch" of the entry. * * @access protected virtual * @param dbi WikiDB database abstraction class * @param argarray array complete (!) arguments. It is * not necessary to call * WikiPlugin->getArgs anymore. * @param request Request ??? * @return string format: '+seconds' * '0' never expires */ function getExpire($dbi, $argarray, $request) { return '0'; // persist forever } /** * Decides the image type of an image output. * Always used unless plugin type is PLUGIN_CACHED_HTML. * * @access protected virtual * @param dbi WikiDB database abstraction class * @param argarray array complete (!) arguments. It is * not necessary to call * WikiPlugin->getArgs anymore. * @param request Request ??? * @return string 'png', 'jpeg' or 'gif' */ function getImageType(&$dbi, $argarray, &$request) { if (in_array($argarray['imgtype'], preg_split('/\s*:\s*/', PLUGIN_CACHED_IMGTYPES))) return $argarray['imgtype']; else return 'png'; } /** * Produces the alt text for an image. * <img src=... alt="getAlt(...)"> * * @access protected virtual * @param dbi WikiDB database abstraction class * @param argarray array complete (!) arguments. It is * not necessary to call * WikiPlugin->getArgs anymore. * @param request Request ??? * @return string "alt" description of the image */ function getAlt($dbi, $argarray, $request) { return 'getName() . ' ' . $this->glueArgs($argarray) . '?>'; } /** * Creates HTML output to be cached. * This method is only called if the plugin_type is set to * PLUGIN_CACHED_HTML. * * @access protected pure virtual * @param dbi WikiDB database abstraction class * @param argarray array complete (!) arguments to produce * image. It is not necessary to call * WikiPlugin->getArgs anymore. * @param request Request ??? * @param string $basepage Pagename to use to interpret links [/relative] page names. * @return string html to be printed in place of the plugin command * false if an error occured */ function getHtml($dbi, $argarray, $request, $basepage) { trigger_error('WikiPluginCached::getHtml: pure virtual function in file ' . __FILE__ . ' line ' . __LINE__, E_USER_ERROR); } /** * Creates HTML output to be cached. * This method is only called if the plugin_type is set to * PLUGIN_CACHED_HTML. * * @access protected pure virtual * @param dbi WikiDB database abstraction class * @param argarray array complete (!) arguments to produce * image. It is not necessary to call * WikiPlugin->getArgs anymore. * @param request Request ??? * @return array(html,handle) html for the map interior (to be specific, * only <area;> tags defining hot spots) * handle is an imagehandle to the corresponding * image. * array(false,false) if an error occured */ function getMap($dbi, $argarray, $request) { trigger_error('WikiPluginCached::getHtml: pure virtual function in file ' . __FILE__ . ' line ' . __LINE__, E_USER_ERROR); } /* --------------------- produce Html ----------------------------- */ /** * Creates an HTML map hyperlinked to the image specified * by url and containing the hotspots given by map. * * @access private * @param id string unique id for the plugin call * @param url string url pointing to the image part of the map * @param map string <area> tags defining active * regions in the map * @param dbi WikiDB database abstraction class * @param argarray array complete (!) arguments to produce * image. It is not necessary to call * WikiPlugin->getArgs anymore. * @param request Request ??? * @return string html output */ function embedMap($id, $url, $map, &$dbi, $argarray, &$request) { // id is not unique if the same map is produced twice $key = substr($id, 0, 8) . substr(microtime(), 0, 6); return HTML(HTML::map(array('name' => $key), $map), HTML::img(array( 'src' => $url, // 'alt' => htmlspecialchars($this->getAlt($dbi,$argarray,$request)) 'usemap' => '#' . $key)) ); } /** * Creates an HTML <img> tag hyperlinking to the specified * url and produces an alternative text for non-graphical * browsers. * * @access private * @param url string url pointing to the image part of the map * @param map string <area> tags defining active * regions in the map * @param dbi WikiDB database abstraction class * @param argarray array complete (!) arguments to produce * image. It is not necessary to call * WikiPlugin->getArgs anymore. * @param request Request ??? * @return string html output */ function embedImg($url, $dbi, $argarray, $request) { return HTML::img(array( 'src' => $url, 'alt' => htmlspecialchars($this->getAlt($dbi, $argarray, $request)))); } /** * svg?, swf, ... Example Audio File * See http://www.protocol7.com/svg-wiki/?EmbedingSvgInHTML

    Alternate Content like

    */ // how to handle alternate images? always provide alternate static images? function embedObject($url, $type, $args = false, $params = false) { if (!$args) $args = array(); $object = HTML::object(array_merge($args, array('src' => $url, 'type' => $type))); if ($params) $object->pushContent($params); return $object; } // -------------------------------------------------------------------------- // ---------------------- static member functions --------------------------- // -------------------------------------------------------------------------- /** * Creates one static PEAR Cache object and returns copies afterwards. * FIXME: There should be references returned * * @access static protected * @return Cache copy of the cache object */ function newCache() { static $staticcache; if (!is_object($staticcache)) { if (!class_exists('Cache')) { // uuh, pear not in include_path! should print a warning. // search some possible pear paths. $pearFinder = new PearFileFinder; if ($lib = $pearFinder->findFile('Cache.php', 'missing_ok')) require_once($lib); else // fall back to our own copy require_once 'lib/pear/Cache.php'; } $cacheparams = array(); foreach (explode(':', 'database:cache_dir:filename_prefix:highwater:lowwater' . ':maxlifetime:maxarglen:usecache:force_syncmap') as $key) { $cacheparams[$key] = constant('PLUGIN_CACHED_' . strtoupper($key)); } $cacheparams['imgtypes'] = preg_split('/\s*:\s*/', PLUGIN_CACHED_IMGTYPES); $staticcache = new Cache(PLUGIN_CACHED_DATABASE, $cacheparams); $staticcache->gc_maxlifetime = PLUGIN_CACHED_MAXLIFETIME; if (!PLUGIN_CACHED_USECACHE) { $staticcache->setCaching(false); } } return $staticcache; // FIXME: use references ? } /** * Determines whether a needed image type may is available * from the GD library and gives an alternative otherwise. * * @access public static * @param wish string one of 'png', 'gif', 'jpeg', 'jpg' * @return string the image type to be used ('png', 'gif', 'jpeg') * 'html' in case of an error */ function decideImgType($wish) { if ($wish == 'html') return $wish; if ($wish == 'jpg') { $wish = 'jpeg'; } $supportedtypes = array(); // Todo: swf, pdf, ... $imagetypes = array( 'png' => IMG_PNG, 'gif' => IMG_GIF, 'jpeg' => IMG_JPEG, 'wbmp' => IMG_WBMP, 'xpm' => IMG_XPM, /* // these do work but not with the ImageType bitmask 'gd' => IMG_GD, 'gd2' => IMG_GD, 'xbm' => IMG_XBM, */ ); if (function_exists('ImageTypes')) { $presenttypes = ImageTypes(); foreach ($imagetypes as $imgtype => $bitmask) if ($presenttypes && $bitmask) array_push($supportedtypes, $imgtype); } else { foreach ($imagetypes as $imgtype => $bitmask) if (function_exists("Image" . $imgtype)) array_push($supportedtypes, $imgtype); } if (in_array($wish, $supportedtypes)) return $wish; elseif (!empty($supportedtypes)) return reset($supportedtypes); else return 'html'; } // decideImgType /** * Writes an image into a file or to the browser. * Note that there is no check if the image can * be written. * * @access public static * @param imgtype string 'png', 'gif' or 'jpeg' * @param imghandle string image handle containing the image * @param imgfile string file name of the image to be produced * @return void * @see decideImageType */ function writeImage($imgtype, $imghandle, $imgfile = false) { if ($imgtype != 'html') { $func = "Image" . strtoupper($imgtype); if ($imgfile) { $func($imghandle, $imgfile); } else { $func($imghandle); } } } // writeImage /** * Sends HTTP Header for some predefined file types. * There is no parameter check. * * @access public static * @param doctype string 'gif', 'png', 'jpeg', 'html' * @return void */ function writeHeader($doctype) { static $IMAGEHEADER = array( 'gif' => 'Content-type: image/gif', 'png' => 'Content-type: image/png', 'jpeg' => 'Content-type: image/jpeg', 'xbm' => 'Content-type: image/xbm', 'xpm' => 'Content-type: image/xpm', 'gd' => 'Content-type: image/gd', 'gd2' => 'Content-type: image/gd2', 'wbmp' => 'Content-type: image/vnd.wap.wbmp', // wireless bitmaps for PDA's and such. 'html' => 'Content-type: text/html'); // Todo: swf, pdf, svg, svgz Header($IMAGEHEADER[$doctype]); } /** * Converts argument array to a string of format option="value". * This should only be used for displaying plugin options for * the quoting of arguments is not safe, yet. * * @access public static * @param argarray array contains all arguments to be converted * @return string concated arguments */ function glueArgs($argarray) { if (!empty($argarray)) { $argstr = ''; while (list($key, $value) = each($argarray)) { $argstr .= $key . '=' . '"' . $value . '" '; // FIXME: How are values quoted? Can a value contain '"'? // TODO: rawurlencode(value) } return substr($argstr, 0, strlen($argstr) - 1); } return ''; } // glueArgs // ---------------------- FetchImageFromCache ------------------------------ /** * Extracts the cache entry id from the url and the plugin call * parameters if available. * * @access private static * @param id string return value. Image is stored under this id. * @param plugincall string return value. Only returned if present in url. * Contains all parameters to reconstruct * plugin call. * @param cache Cache PEAR Cache object * @param request Request ??? * @param errorformat string format which should be used to * output errors ('html', 'png', 'gif', 'jpeg') * @return boolean false if an error occurs, true otherwise. * Param id and param plugincall are * also return values. */ function checkCall1(&$id, &$plugincall, $cache, $request, $errorformat) { $id = $request->getArg('id'); $plugincall = rawurldecode($request->getArg('args')); if (!$id) { if (!$plugincall) { // This should never happen, so do not gettextify. $errortext = "Neither 'args' nor 'id' given. Cannot proceed without parameters."; $this->printError($errorformat, $errortext); return false; } else { $id = $cache->generateId($plugincall); } } return true; } // checkCall1 /** * Extracts the parameters necessary to reconstruct the plugin * call needed to produce the requested image. * * @access static private * @param plugincall string reference to serialized array containing both * name and parameters of the plugin call * @param request Request ??? * @return boolean false if an error occurs, true otherwise. * */ function checkCall2(&$plugincall, $request) { // if plugincall wasn't sent by URL, it must have been // stored in a session var instead and we can retreive it from there if (!$plugincall) { if (!$plugincall = $request->getSessionVar('imagecache' . $id)) { // I think this is the only error which may occur // without having written bad code. So gettextify it. $errortext = sprintf( gettext("There is no image creation data available to id “%s”. Please reload referring page."), $id); $this->printError($errorformat, $errortext); return false; } } $plugincall = unserialize($plugincall); return true; } // checkCall2 /** * Creates an image or image map depending on the plugin type. * @access static private * @param content array reference to created array which overwrite the keys * 'image', 'imagetype' and possibly 'html' * @param plugin WikiPluginCached plugin which is called to create image or map * @param dbi WikiDB handle to database * @param argarray array Contains all arguments needed by plugin * @param request Request ???? * @param errorformat string outputs errors in 'png', 'gif', 'jpg' or 'html' * @return boolean error status; true=ok; false=error */ function produceImage(&$content, $plugin, $dbi, $argarray, $request, $errorformat) { $plugin->resetError(); $content['html'] = $imagehandle = false; if ($plugin->getPluginType() == PLUGIN_CACHED_MAP) { list($imagehandle, $content['html']) = $plugin->getMap($dbi, $argarray, $request); } else { $imagehandle = $plugin->getImage($dbi, $argarray, $request); } $content['imagetype'] = $this->decideImgType($plugin->getImageType($dbi, $argarray, $request)); $errortext = $plugin->getError(); if (!$imagehandle || $errortext) { if (!$errortext) { $errortext = "'getName() . ' ' . $this->glueArgs($argarray) . " ?>' returned no image, " . " although no error was reported."; } $this->printError($errorformat, $errortext); return false; } // image handle -> image data if (!empty($this->_static)) { $ext = "." . $content['imagetype']; if (is_string($imagehandle) and file_exists($imagehandle)) { if (preg_match("/.(\w+)$/", $imagehandle, $m)) { $ext = "." . $m[1]; } } $tmpfile = tempnam(getUploadFilePath(), PLUGIN_CACHED_FILENAME_PREFIX . $ext); if (!strstr(basename($tmpfile), $ext)) { unlink($tmpfile); $tmpfile .= $ext; } $tmpfile = getUploadFilePath() . basename($tmpfile); if (is_string($imagehandle) and file_exists($imagehandle)) { rename($imagehandle, $tmpfile); } } else { $tmpfile = $this->tempnam(); } if (is_resource($imagehandle)) { $this->writeImage($content['imagetype'], $imagehandle, $tmpfile); ImageDestroy($imagehandle); sleep(0.2); } elseif (is_string($imagehandle)) { $content['file'] = getUploadFilePath() . basename($tmpfile); $content['url'] = getUploadDataPath() . basename($tmpfile); return true; } if (file_exists($tmpfile)) { $fp = fopen($tmpfile, 'rb'); $content['image'] = fread($fp, filesize($tmpfile)); fclose($fp); if (!empty($this->_static)) { // on static it is in "uploads/" but in wikicached also $content['file'] = $tmpfile; $content['url'] = getUploadDataPath() . basename($tmpfile); return true; } unlink($tmpfile); if ($content['image']) return true; } return false; } function staticUrl($tmpfile) { $content['file'] = $tmpfile; $content['url'] = getUploadDataPath() . basename($tmpfile); return $content; } function tempnam($prefix = "") { if (preg_match("/^(.+)\.(\w{2,4})$/", $prefix, $m)) { $prefix = $m[1]; $ext = "." . $m[2]; } else { $ext = isWindows() ? ".tmp" : ""; } $temp = tempnam(isWindows() ? str_replace('/', "\\", PLUGIN_CACHED_CACHE_DIR) : PLUGIN_CACHED_CACHE_DIR, $prefix ? $prefix : PLUGIN_CACHED_FILENAME_PREFIX); if (isWindows()) { if ($ext != ".tmp") unlink($temp); $temp = preg_replace("/\.tmp$/", $ext, $temp); } else { $temp .= $ext; } return $temp; } /** * Main function for obtaining images from cache or generating on-the-fly * from parameters sent by url or session vars. * * @access static public * @param dbi WikiDB handle to database * @param request Request ??? * @param errorformat string outputs errors in 'png', 'gif', 'jpeg' or 'html' */ function fetchImageFromCache($dbi, $request, $errorformat = 'png') { $cache = $this->newCache(); $errorformat = $this->decideImgType($errorformat); // get id if (!$this->checkCall1($id, $plugincall, $cache, $request, $errorformat)) return false; // check cache $content = $cache->get($id, 'imagecache'); if (!empty($content['image'])) { $this->writeHeader($content['imagetype']); print $content['image']; return true; } if (!empty($content['html'])) { print $content['html']; return true; } // static version? if (!empty($content['file']) && !empty($content['url']) && file_exists($content['file'])) { print $this->embedImg($content['url'], $dbi, array(), $request); return true; } // re-produce image. At first, we need the plugincall parameters. // Cached args with matching id override given args to shorten getimg.php?id=md5 if (!empty($content['args'])) $plugincall['arguments'] = $content['args']; if (!$this->checkCall2($plugincall, $request)) return false; $pluginname = $plugincall['pluginname']; $argarray = $plugincall['arguments']; $loader = new WikiPluginLoader(); $plugin = $loader->getPlugin($pluginname); // cache empty, but image maps have to be created _inline_ // so ask user to reload wiki page instead if (($plugin->getPluginType() & PLUGIN_CACHED_MAP) && PLUGIN_CACHED_FORCE_SYNCMAP) { $errortext = _("Image map expired. Reload wiki page to recreate its html part."); $this->printError($errorformat, $errortext); } if (!$this->produceImage($content, $plugin, $dbi, $argarray, $request, $errorformat) ) return false; $expire = $plugin->getExpire($dbi, $argarray, $request); if ($content['image']) { $cache->save($id, $content, $expire, 'imagecache'); $this->writeHeader($content['imagetype']); print $content['image']; return true; } $errortext = "Could not create image file from imagehandle."; $this->printError($errorformat, $errortext); return false; } // FetchImageFromCache // -------------------- error handling ---------------------------- /** * Resets buffer containing all error messages. This is allways * done before invoking any abstract creation routines like * getImage. * * @access private * @return void */ function resetError() { $this->_errortext = ''; } /** * Returns all accumulated error messages. * * @access protected * @return string error messages printed with complain. */ function getError() { return $this->_errortext; } /** * Collects the error messages in a string for later output * by WikiPluginCached. This should be used for any errors * that occur during data (html,image,map) creation. * * @access protected * @param addtext string errormessage to be printed (separate * multiple lines with '\n') * @return void */ function complain($addtext) { $this->_errortext .= $addtext; } /** * Outputs the error as image if possible or as html text * if wished or html header has already been sent. * * @access static protected * @param imgtype string 'png', 'gif', 'jpeg' or 'html' * @param errortext string guess what? * @return void */ function printError($imgtype, $errortext) { $imgtype = $this->decideImgType($imgtype); $talkedallready = ob_get_contents() || headers_sent(); if (($imgtype == 'html') || $talkedallready) { if (is_object($errortext)) $errortext = $errortext->asXml(); trigger_error($errortext, E_USER_WARNING); } else { $red = array(255, 0, 0); $grey = array(221, 221, 221); if (is_object($errortext)) $errortext = $errortext->asString(); $im = $this->text2img($errortext, 2, $red, $grey); if (!$im) { trigger_error($errortext, E_USER_WARNING); return; } $this->writeHeader($imgtype); $this->writeImage($imgtype, $im); ImageDestroy($im); } } // printError /** * Basic text to image converter for error handling which allows * multiple line output. * It will only output the first 25 lines of 80 characters. Both * values may be smaller if the chosen font is to big for there * is further restriction to 600 pixel in width and 350 in height. * * @access static public * @param txt string multi line text to be converted * @param fontnr integer number (1-5) telling gd which internal font to use; * I recommend font 2 for errors and 4 for help texts. * @param textcol array text color as a list of the rgb components; array(red,green,blue) * @param bgcol array background color; array(red,green,blue) * @return string image handle for gd routines */ function text2img($txt, $fontnr, $textcol, $bgcol) { // basic (!) output for error handling // check parameters if ($fontnr < 1 || $fontnr > 5) { $fontnr = 2; } if (!is_array($textcol) || !is_array($bgcol)) { $textcol = array(0, 0, 0); $bgcol = array(255, 255, 255); } foreach (array_merge($textcol, $bgcol) as $component) { if ($component < 0 || $component > 255) { $textcol = array(0, 0, 0); $bgcol = array(255, 255, 255); break; } } // prepare Parameters // set maximum values $IMAGESIZE = array( 'cols' => 80, 'rows' => 25, 'width' => 600, 'height' => 350); if (function_exists('ImageFontWidth')) { $charx = ImageFontWidth($fontnr); $chary = ImageFontHeight($fontnr); } else { $charx = 10; $chary = 10; } $marginx = $charx; $marginy = floor($chary / 2); $IMAGESIZE['cols'] = min($IMAGESIZE['cols'], floor(($IMAGESIZE['width'] - 2 * $marginx) / $charx)); $IMAGESIZE['rows'] = min($IMAGESIZE['rows'], floor(($IMAGESIZE['height'] - 2 * $marginy) / $chary)); // split lines $y = 0; $wx = 0; do { $len = strlen($txt); $npos = strpos($txt, "\n"); if ($npos === false) { $breaklen = min($IMAGESIZE['cols'], $len); } else { $breaklen = min($npos + 1, $IMAGESIZE['cols']); } $lines[$y] = chop(substr($txt, 0, $breaklen)); $wx = max($wx, strlen($lines[$y++])); $txt = substr($txt, $breaklen); } while ($txt && ($y < $IMAGESIZE['rows'])); // recalculate image size $IMAGESIZE['rows'] = $y; $IMAGESIZE['cols'] = $wx; $IMAGESIZE['width'] = $IMAGESIZE['cols'] * $charx + 2 * $marginx; $IMAGESIZE['height'] = $IMAGESIZE['rows'] * $chary + 2 * $marginy; // create blank image $im = @ImageCreate($IMAGESIZE['width'], $IMAGESIZE['height']); $col = ImageColorAllocate($im, $textcol[0], $textcol[1], $textcol[2]); $bg = ImageColorAllocate($im, $bgcol[0], $bgcol[1], $bgcol[2]); ImageFilledRectangle($im, 0, 0, $IMAGESIZE['width'] - 1, $IMAGESIZE['height'] - 1, $bg); // write text lines foreach ($lines as $nr => $textstr) { ImageString($im, $fontnr, $marginx, $marginy + $nr * $chary, $textstr, $col); } return $im; } // text2img function newFilterThroughCmd($input, $commandLine) { $descriptorspec = array( 0 => array("pipe", "r"), // stdin is a pipe that the child will read from 1 => array("pipe", "w"), // stdout is a pipe that the child will write to 2 => array("pipe", "w"), // stdout is a pipe that the child will write to ); $process = proc_open("$commandLine", $descriptorspec, $pipes); if (is_resource($process)) { // $pipes now looks like this: // 0 => writeable handle connected to child stdin // 1 => readable handle connected to child stdout // 2 => readable handle connected to child stderr fwrite($pipes[0], $input); fclose($pipes[0]); $buf = ""; while (!feof($pipes[1])) { $buf .= fgets($pipes[1], 1024); } fclose($pipes[1]); $stderr = ''; while (!feof($pipes[2])) { $stderr .= fgets($pipes[2], 1024); } fclose($pipes[2]); // It is important that you close any pipes before calling // proc_close in order to avoid a deadlock proc_close($process); if (empty($buf)) printXML($this->error($stderr)); return $buf; } return ''; } // run "echo $source | $commandLine" and return result function filterThroughCmd($source, $commandLine) { return $this->newFilterThroughCmd($source, $commandLine); } /** * Execute system command and wait until the outfile $until exists. * * @param cmd string command to be invoked * @param until string expected output filename * @return boolean error status; true=ok; false=error */ function execute($cmd, $until = false) { // cmd must redirect stderr to stdout though! $errstr = exec($cmd); //, $outarr, $returnval); // normally 127 //$errstr = join('',$outarr); $ok = empty($errstr); if (!$ok) { trigger_error("\n" . $cmd . " failed: $errstr", E_USER_WARNING); } elseif ($GLOBALS['request']->getArg('debug')) trigger_error("\n" . $cmd . ": success\n", E_USER_NOTICE); if (!isWindows()) { if ($until) { $loop = 100000; while (!file_exists($until) and $loop > 0) { $loop -= 100; usleep(100); } } else { usleep(5000); } } if ($until) return file_exists($until); return $ok; } } // Local Variables: // mode: php // tab-width: 8 // c-basic-offset: 4 // c-hanging-comment-ender-p: nil // indent-tabs-mode: nil // End: