<?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);
  }
}