cacheAllowed()) { $null = null; $bypass_data = array('shortcircuit' => true); $this->event_hook('frontend_shortcircuit', $null, $bypass_data); } } function introspect(&$propbag) { global $serendipity; $propbag->add('name', PLUGIN_EVENT_CACHESIMPLE_NAME); $propbag->add('description', PLUGIN_EVENT_CACHESIMPLE_DESC); $propbag->add('stackable', false); $propbag->add('author', 'Garvin Hicking'); $propbag->add('version', '1.1'); $propbag->add('requirements', array( 'serendipity' => '0.8', 'smarty' => '2.6.7', 'php' => '4.1.0' )); $propbag->add('event_hooks', array( 'frontend_configure' => true, 'frontend_comment' => true, 'frontend_saveComment' => true, 'backend_publish' => true, 'backend_save' => true, 'frontend_shortcircuit' => true, 'xmlrpc_deleteEntry' => true, 'xmlrpc_updateEntry' => true )); $propbag->add('groups', array('BACKEND_FEATURES')); $propbag->add('configuration', array('browser','keep_fresh')); } function introspect_config_item($name, &$propbag) { switch($name) { case 'browser': $propbag->add('type', 'boolean'); $propbag->add('name', PLUGIN_EVENT_CACHESIMPLE_BROWSER); $propbag->add('description', ''); $propbag->add('default', false); break; case 'keep_fresh': $propbag->add('type', 'boolean'); $propbag->add('name', PLUGIN_EVENT_CACHESIMPLE_KEEPFRESH); $propbag->add('description', PLUGIN_EVENT_CACHESIMPLE_KEEPFRESH_DESC); $propbag->add('default', false); break; default: return false; } return true; } function generate_content(&$title) { $title = $this->title; } function cacheHeaders($lm = null) { global $serendipity; if ($lm === null || empty($lm)) { $last_modified_ts = serendipity_serverOffsetHour(); } else { $last_modified_ts = serendipity_serverOffsetHour($lm, true); } $modified_since = !empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? gmdate('D, d M Y H:i:s \G\M\T', serendipity_serverOffsetHour(strtotime(stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE'])), true)) : false; $none_match = !empty($_SERVER['HTTP_IF_NONE_MATCH']) ? str_replace('"', '', stripslashes($_SERVER['HTTP_IF_NONE_MATCH'])) : false; $last_modified = gmdate('D, d M Y H:i:s \G\M\T', $last_modified_ts); $etag = '"' . $last_modified . '"'; serendipity_header('Last-Modified: ' . $last_modified); serendipity_header('ETag: ' . $etag); if (($none_match == $last_modified && $modified_since == $last_modified) || (!$none_match && $modified_since == $last_modified) || (!$modified_since && $none_match == $last_modified)) { header('HTTP/1.0 304 Not Modified'); header('Status: 304 Not Modified'); return true; } return false; } function debugMsg($msg) { global $serendipity; if (!$this->debug) { return false; } $this->debug_fp = @fopen($serendipity['serendipityPath'] . 'templates_c/cachesimple.log', 'a'); if (!$this->debug_fp) { return false; } if (empty($msg)) { fwrite($this->debug_fp, "\n"); } else { fwrite($this->debug_fp, '[' . date('d.m.Y H:i') . '] ' . $msg . "\n"); } fclose($this->debug_fp); } function cacheAllowed() { global $serendipity; if (in_array(basename($_SERVER['PHP_SELF']), $this->disallowed_files)) { $this->debugMsg(basename($_SERVER['REQUEST_URI']) . ' cannot be cached.'); return false; } if (count($serendipity['POST']) > 0) { $this->debugMsg('POST to ' . $_SERVER['REQUEST_URI'] . ' not cached'); return false; } if (isset($GLOBALS['css_mode'])) { // Don't cache CSS, headers() are not cached. $this->debugMsg('Not caching CSS files.'); return false; } if ((basename($_SERVER['PHP_SELF']) == 'rss.php' || (defined('PAT_FEEDS') && preg_match(PAT_FEEDS, $_SERVER['REQUEST_URI']))) && !empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) && !empty($_SERVER['HTTP_IF_NONE_MATCH'])) { $this->debugMsg('RSS Cache lives on itself to enable Conditional GET.'); return false; } return true; } function event_hook($event, &$bag, &$eventData) { global $serendipity; $hooks = array(); if (is_object($bag)) { $hooks = &$bag->get('event_hooks'); } elseif (is_array($eventData) && $eventData['shortcircuit']) { $hooks[$event] = true; } if (isset($hooks[$event])) { $this->debugMsg(''); $this->debugMsg($_SERVER['REQUEST_URI']); @include_once 'Cache/Lite.php'; if (!class_exists('Cache_Lite')) { $this->debugMsg('Cache_Lite not available.'); return false; } $options = array( 'cacheDir' => $serendipity['serendipityPath'] . 'templates_c/', 'lifeTime' => 3600, 'hashedDirectoryLevel' => 2 ); $this->cache = new Cache_Lite($options); switch($event) { case 'frontend_comment': if (isset($serendipity['GET']['adminModule']) && $serendipity['GET']['adminModule'] == 'comments') { return true; } ?> cacheAllowed()) { return true; } case 'frontend_shortcircuit': $cache_options = $serendipity['GET']; // This tries to keep individual sites with pre-filled form elements, // but also swamps your cache directory with individual cache files. $cache_options['COOKIE'] = $serendipity['COOKIE']; $cache_options['base'] = $serendipity['baseURL']; unset($cache_options['COOKIE']['author_information']); unset($cache_options['COOKIE']['karmaVote']); unset($cache_options['COOKIE']['name']); unset($cache_options['COOKIE']['url']); unset($cache_options['COOKIE']['email']); if (serendipity_db_bool($this->get_config('browser'))) { if (stristr($_SERVER['HTTP_USER_AGENT'], 'MSIE') && !stristr($_SERVER['HTTP_USER_AGENT'], 'Opera') && !stristr($_SERVER['HTTP_USER_AGENT'], 'Mac_PowerPC') && !stristr($_SERVER['HTTP_ACCEPT'], 'text/html')) { $cache_options['browser'] = 'ie'; } else { $cache_options['browser'] = 'other'; } } $this->cache_key = 'serendipity_cachesimple_' . preg_replace('@[^0-9a-z\-_]@', '_', $_SERVER['REQUEST_URI']) . crc32(serialize($cache_options)); $this->debugMsg($this->cache_key . ' cache prepared'); $this->cache->_setFileName($this->cache_key, $this->cache_group); if (serendipity_db_bool($this->get_config('keep_fresh',false))) { serendipity_header('Pragma:'); serendipity_header('Cache-Control: max-age=3'); } else { serendipity_header('Pragma:'); serendipity_header('Cache-Control: max-age=3600, must-revalidate'); serendipity_header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', time()+$options['lifeTime'])); } if (function_exists('microtime_float')) { $time_end = microtime_float(); serendipity_header('X-Serendipity-ParseTime: ' . round($time_end-$GLOBALS['time_start'],6)); } // HTTP Conditional Request. May exit with 304. if ($this->cacheHeaders(@filemtime($this->cache->_file))) { exit; } if ($content = $this->cache->get($this->cache_key, $this->cache_group)) { serendipity_header('X-Serendipity-Cache: true'); // No valid HTTP Conditional Request. Emit output. echo $content; $this->debugMsg('CACHE HIT'); exit; } else { serendipity_header('X-Serendipity-Cache: false'); $this->debugMsg('CACHE MISS'); } $serendipity['useGzip'] = false; // This is a bit wicked. Index.php already starts a ob, thus // we end it here, output the data so far, start another handler // and forward the data to it. Then we can throw our own handler // later on. // Get previous ob data of the outmost OB and erase it afterwards $prev_data = ob_get_contents(); ob_end_clean(); // Create our new outmost OB and set a callback method. ob_start(array(&$this, 'storeCache')); // Create a faked inner OB which will be the outmost OB to Serendipity ob_start(); // Echo previous data to the faked OB echo $prev_data; // "What comes around, goes around, you'll see." ;) register_shutdown_function(array(&$this, 'shutdown')); return true; break; case 'xmlrpc_updertEntry': case 'xmlrpc_deleteEntry': case 'backend_save': case 'backend_publish': $this->purgeCache(); return true; break; case 'frontend_saveComment': if (is_array($eventData) && serendipity_db_bool($eventData['allow_comments'])) { // Only purge comments if a comment could be made. Prevents purging and recreating cache when // unter spam/referrer attack. $this->purgeCache(); } return true; break; default: return false; } } else { return false; } } function &storeCache($buffer) { $this->cached = true; if (isset($GLOBALS['css_mode']) || preg_match(PAT_FEEDS, $_SERVER['REQUEST_URI'])) { // Don't cache CSS, headers() are not cached. $this->debugMsg('Not caching CSS/RSS files.'); $retval = false; return $retval; } $this->cache->save($buffer, $this->cache_key, $this->cache_group); $this->debugMsg('CACHE STORED (' . strlen($buffer) . ' bytes)'); return $buffer; } function purgeCache() { $this->debugMsg('CACHE PURGED'); $this->cache->clean($this->cache_group); } function uninstall() { global $serendipity; @include_once 'Cache/Lite.php'; $options = array( 'cacheDir' => $serendipity['serendipityPath'] . 'templates_c/', 'lifeTime' => 3600, 'hashedDirectoryLevel' => 2 ); $this->cache = new Cache_Lite($options); $this->cache->clean(); } function shutdown() { if ($this->cached === false) { $this->debugMsg('Shutdown function: Got Attention.'); $this->storeCache(ob_get_contents()); } } } /* vim: set sts=4 ts=4 expandtab : */ ?>