<?php if (!defined('PmWiki')) exit();
/* Copyright 2006 Patrick R. Michaud (pmichaud@pobox.com),
Copyright 2007 Thomas Bley (thomas.bley@simple-groupware.de)
This file is part of PmWiki; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
by the Free Software Foundation; either version 2 of the License, or
(at your option) any later version. See pmwiki.php for full details.
*/
// TODO handle permission attribute change:
// page: remove page from cache
// group: remove group from cache
// sitewide permission change possible ?
// TODO add option to push a group into static cache
// TODO remove single page from static cache
// TODO cache expiration (daily, hourly, monthly)
// TODO handle delete page, rename page
// TODO implement: dependency check
// read authentication triggered by $NoHTMLCache < 2 ?
// INSTALLTION
/*
0) copy staticcaches.php to <pmwiki-dir>/scripts/
1) edit scrips/stdconfig.php, replace:
$pagename = ResolvePageName($pagename);
with:
$pagename = ResolvePageName($pagename);
include_once("$FarmD/scripts/staticcaches.php");
2) edit config.php, add:
$StaticPageCacheDir = 'cache.s';
$EnableStaticHTMLCache = 1; // active caching
$EnableStaticHTMLPages = '/Main\..+/'; // only cache pages in the Main group [or]
$EnableStaticHTMLPages = ''; // cache all pages
// if enabled, disable static cache for a page with (:static off:)
// cleanURLs (optional):
$EnablePathInfo = 1;
$ScriptUrl = "http://localhost/pmwiki";
3a) edit/add .htaccess, no clean urls, EnablePathInfo = 0:
# If charset needs to be UTF-8
AddCharset UTF-8 .html
# activate engine, only cache get request, make sure mod_rewrite is loaded in httpf.conf
# no clean urls, EnablePathInfo = 0
RewriteEngine On
RewriteCond %{REQUEST_METHOD} ^GET$
# absolute path to file, adapt pmwiki/ if necessary
RewriteCond %{DOCUMENT_ROOT}/pmwiki/cache.s/%{QUERY_STRING},cache.html -f
RewriteRule pmwiki.php cache.s/%{QUERY_STRING},cache.html [L]
3b) edit/add .htaccess, clean urls, EnablePathInfo = 1:
# If charset needs to be UTF-8
AddCharset UTF-8 .html
# activate engine, only cache get request, make sure mod_rewrite is loaded in httpf.conf
# clean urls, EnablePathInfo = 1
RewriteEngine On
RewriteCond %{REQUEST_METHOD} ^GET$
RewriteCond %{QUERY_STRING} ^$
# absolute path to file, adapt pmwiki/ if necessary
RewriteCond %{DOCUMENT_ROOT}/pmwiki/cache.s%{REQUEST_URI},cache.html -f
RewriteRule ^([A-Z0-9\xa0-\xff].*)$ cache.s%{REQUEST_URI},cache.html [L]
RewriteRule ^$ pmwiki.php [L]
RewriteRule ^index\.php$ pmwiki.php [L]
RewriteRule ^([A-Z0-9\xa0-\xff].*)$ pmwiki.php?n=$1 [QSA,L]
4) recache page: pmwiki.php?n=pagename&action=recache
5) static cache gui: pmwiki.php?action=sc
*/
if (!$EnableStaticHTMLCache
        || !isset($EnableStaticHTMLPages)
|| empty($StaticPageCacheDir)) {
        return;
}
        
$EditFunctions[] = 'scUpdateCache';
$HandleActions['sc'] = 'scListCacheFiles';
$HandleActions['sc_clear'] = 'scRemoveCacheFiles';
$HandleActions['sc_recache'] = 'scRecacheFiles';
$HandleActions['recache'] = 'scRemoveCacheFile';
Markup('static','directives','/\\(:static\\s(.*?):\\)/ei',"scCacheConfig('$1')");
if (count($_POST) > 0
|| count($_GET) > 1
|| (count($_GET) == 1 && empty($_GET['n']))) {
        return;
}
if ($EnableStaticHTMLPages and !preg_match($EnableStaticHTMLPages,$pagename)) return;
$LastStaticModTime = 0;
foreach ($WikiLibDirs as $dir) {
$page = $dir->pagefile($pagename);
if ($page and file_exists($page)) {
$LastStaticModTime = filemtime($page);
break;
}
}
if ($LastStaticModTime == 0) return;
$StaticPageCacheFile = scBuildCacheFile($pagename);
if (!file_exists($StaticPageCacheFile) || @filemtime($StaticPageCacheFile) < $LastStaticModTime) {
$HandleActions['browse'] = 'scHandleStaticBrowse';
}
function scCacheConfig($params) {
// (:static on/off dependency regexp:)
$params = explode(' ',$params);
if ($params[0]=='off') $EnableStaticHTMLCache = 0;
// echo $params[1];
// TODO handle dependencies
}
function scBuildCacheFile($pagename) {
global $StaticPageCacheDir, $EnablePathInfo, $ScriptUrl;
if ($pagename!='') {
if ($EnablePathInfo) {
         $url = parse_url($ScriptUrl);
         $pagename = $url['path'].'/'.str_replace('.','/',$pagename);
        } else {
         $pagename = "n=$pagename";
        }
}
return "$StaticPageCacheDir/$pagename,cache.html";
}
function scGetPage($cachefile) {
global $EnablePathInfo;
if ($EnablePathInfo) {
        preg_match('!/([^/]+)/([^/]+)$!',$cachefile,$match);
        $fpagename = "$match[1].$match[2]";
} else {
$fpagename = basename($cachefile);
}
return str_replace(array('n=',',cache.html'),'',$fpagename);
}
function scRemoveCacheFile($pagename, $redirect=0) {
global $DefaultGroup, $DefaultName;
RetrieveAuthPage($pagename, 'edit', true, READPAGE_CURRENT);
@unlink(scBuildCacheFile($pagename));
if ($pagename == "$DefaultGroup.$DefaultName") {
        @unlink(scBuildCacheFile(''));
}
if ($redirect) Redirect($pagename);
}
function scPutCacheFile($pagename,$out) {
global $DefaultGroup, $DefaultName;
$StaticPageCacheFile = scBuildCacheFile($pagename);
mkdirp(dirname($StaticPageCacheFile));
if ($fp = @fopen("$StaticPageCacheFile,new", 'x')) {
fwrite($fp,'<!-- static-cached -->'.$out);
        fclose($fp);
rename("$StaticPageCacheFile,new", $StaticPageCacheFile);
        if ($pagename == "$DefaultGroup.$DefaultName") {
         copy($StaticPageCacheFile,scBuildCacheFile(''));
        }
}
}
function scGetCacheFiles($dir='') {
global $StaticPageCacheDir;
if ($dir=='') $dir = $StaticPageCacheDir;
$files = array();
if ($dfp = @opendir($dir)) {
while ( ($pagefile = readdir($dfp)) !== false) {
if ($pagefile{0} == '.') continue;
         if (is_dir("$dir/$pagefile")) {
         $files = array_merge($files, scGetCacheFiles("$dir/$pagefile"));
         } else {
         $files[] = "$dir/$pagefile";
         }
        }
}
return $files;
}
function scRemoveCacheFiles($pagename, $auth='read') {
global $SiteAdminGroup;
RetrieveAuthPage($SiteAdminGroup, 'read', true, READPAGE_CURRENT);
foreach (scGetCacheFiles() as $file) @unlink($file);
scListCacheFiles($pagename);
}
function scRecacheFiles($pagename, $redirect=0) {
global $SiteAdminGroup;
$substring = '';
if (!empty($_REQUEST['substring'])) $substring = $_REQUEST['substring'];
$output = array();
RetrieveAuthPage($SiteAdminGroup, 'read', true, READPAGE_CURRENT);
foreach (scGetCacheFiles() as $file) {
$fpagename = scGetPage($file);
if (!$fpagename) continue;
        if ($substring and !strpos('#'.$fpagename,$substring)) continue;
        $output[] = "Recache: $fpagename";
        scRemoveCacheFile($fpagename);
        @set_time_limit(15);
        scHandleStaticBrowse($fpagename,'read',0);
}
echo implode('<br>',$output).'<br><br>';
scListCacheFiles($pagename);
}
function scGetUrl($pagename) {
$url = FmtPageName('$PageUrl',$pagename);
if (!strpos($url,'?')) $url .= '?';
return $url;
}
function scListCacheFiles($pagename, $auth='read') {
global $StaticPageCacheDir, $TimeFmt;
$pageurl = scGetUrl($pagename);
echo "<a href='$pageurl'>Continue</a> - ";
echo "<a href='$pageurl&action=sc'>Refresh</a> - ";
echo "<a href='$pageurl&action=sc_clear'>Clear static cache</a><br><br>";
echo '<table style="width:75%;">';
echo '<tr style="font-weight:bold;"><td>Page</td><td>Last modified / size</td><td>Operation</td></tr>';
echo "<tr><td colspan='2'></td><td><a href='$pageurl&action=sc_recache'>Recache all in list</a></td></tr>";
$groups = array();
foreach (scGetCacheFiles() as $file) {
$lastmod = strftime($TimeFmt, filemtime($file));
        $size = number_format(filesize($file)/1024)." Kb";
        $fpagename = scGetPage($file);
        $fgroup = substr($fpagename,0,strpos($fpagename,'.'));
        if (!$fgroup) continue;
        $groups[$fgroup] = 0;
$fpageurl = scGetUrl($fpagename);
        echo "<tr><td><a href='$fpageurl'>$fpagename</a></td><td>$lastmod ($size)</td>";
        echo "<td><a href='$fpageurl&action=sc_recache&substring=$fpagename'>Recache page</a></td>";
}
foreach ($groups as $fgroup=>$none) {
echo "<tr><td>$fgroup</td><td></td><td><a href='$pageurl&action=sc_recache&substring=$fgroup.'>Recache members in list</a></td></tr>";
}
echo '</table>';
}
function scUpdateCache($pagename, &$page, &$new) {
global $IsPagePosted;
if ($IsPagePosted) {
scRemoveCacheFile($pagename);
        // TODO remove dependencies, hanle delete page, rename page
}
}
function scHandleStaticBrowse($pagename, $auth='read', $output=1) {
global $EnableStaticHTMLCache, $NoHTMLCache;
ob_start();
HandleBrowse($pagename,$auth);
$out = ob_get_contents();
if ($output) ob_end_flush(); else ob_end_clean();
scRemoveCacheFile($pagename);
if ($EnableStaticHTMLCache and $NoHTMLCache < 2 and $out > '') {
scPutCacheFile($pagename,$out);
}
}