* * A PageStore alternative which doesn't mangle page contents when viewed outside PmWiki * * Developed and tested using PmWiki 2.2.0 * * To install, add the following to your configuration file: include_once("$FarmD/cookbook/pagetopstore.php"); $WikiDir = new PageTopStore( 'wiki.d/{$FullName}', 'wikitop.d/{$FullName}' ); * For more information, please see the online documentation at * http://www.pmwiki.org/wiki/Cookbook/PageTopStore * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License, * Version 2, as published by the Free Software Foundation. * http://www.gnu.org/copyleft/gpl.html */ $RecipeInfo['PageTopStore']['Version'] = '2009-01-22'; SDV( $HandleActions['filltop'], 'HandleFillTop' ); function HandleFillTop( $pagename, $auth = 'attr' ) { global $ScriptUrl; if (empty( $_GET['ps'] )) Abort( "No PageStore given!
usage: $ScriptUrl?action=filltop&ps=YourPageStoreVariableNameHere
use ps=WikiDir for default" ); $psn = $_GET['ps']; if (empty( $GLOBALS[$psn]->topfmt )) Abort( "$psn at {$GLOBALS[$psn]->dirfmt} isn't a valid PageTopStore!" ); $page = RetrieveAuthPage( $pagename, $auth, true, READPAGE_CURRENT ); $GLOBALS[$psn]->fill(); } class PageTopStore extends PageStore { var $dirfmt; var $topfmt; var $iswrite; var $attr; function PageTopStore( $d='$WorkDir/$FullName', $t='wikitop.d/{$FullName}', $w=0, $a=NULL ) { $this->dirfmt = $d; $this->topfmt = $t; $this->iswrite = $w; $this->attr = (array)$a; $GLOBALS['PageExistsCache'] = array(); } ## inherited: function pagefile( $pagename ) function pagetopfile( $pagename ) { global $FarmD; $tfmt = $this->topfmt; if ($pagename > '') { $pagename = str_replace('/', '.', $pagename); ## optimizations for standard locations if ( $tfmt == 'wikitop.d/{$FullName}' ) return "wikitop.d/$pagename"; if ( $tfmt == 'wikitop.d/{$Group}/{$FullName}' ) return preg_replace( '/([^.]+).*/', 'wikitop.d/$1/$0', $pagename ); } return FmtPageName( $tfmt, $pagename ); } function modtop( $pagename ) { global $Now, $EnablePost, $IsPagePosted; $pagefile = $this->pagefile($pagename); if (empty($pagefile)) return; $topfile = $this->pagetopfile($pagename); if (empty($topfile)) return; if ( !file_exists($pagefile) ) { if ( file_exists($topfile) ) { $page = $this->read( $pagename, READPAGE_CURRENT ); Lock(2); $prev_Now = $Now; $prev_EnablePost = $EnablePost; $prev_IsPagePosted = $IsPagePosted; $Now = min( $Now, max( $page['time'], filemtime($topfile) ) ); $EnablePost = 1; $IsPagePosted = TRUE; $blank_page = array( 'ctime' => $Now, 'time' => $Now ); UpdatePage( $pagename, $blank_page, $page ); $Now = $prev_Now; $EnablePost = $prev_EnablePost; $IsPagePosted = $prev_IsPagePosted; Lock(0); } return; } if ( !file_exists($topfile) ) { $page = parent::read( $pagename, READPAGE_CURRENT ); $this->writetop( $pagename, $page ); return; } if ( filemtime($topfile) > filemtime($pagefile) ) { $page_was = parent::read( $pagename, 0 ); $page_top = array_merge( $page_was, $this->read( $pagename, READPAGE_CURRENT ) ); if (strcmp( trim( @$page_top['text'] ), trim( @$page_was['text'] ) )) { Lock(2); $prev_Now = $Now; $prev_EnablePost = $EnablePost; $prev_IsPagePosted = $IsPagePosted; $Now = min( $Now, max( $page_was['time'], $page_top['time'], filemtime($topfile) ) ); $EnablePost = 1; $IsPagePosted = TRUE; UpdatePage( $pagename, $page_was, $page_top ); $Now = $prev_Now; $EnablePost = $prev_EnablePost; $IsPagePosted = $prev_IsPagePosted; Lock(0); } } } function writetop( $pagename, &$page ) { global $Now, $Version; $topfile = $this->pagetopfile($pagename); $dir = dirname($topfile); mkdirp($dir); if ( !file_exists("$dir/.htaccess") && ( $fp = @fopen( "$dir/.htaccess", 'w' ) ) ) { fwrite( $fp, "Order Deny,Allow\nDeny from all\n" ); fclose($fp); } $st = FALSE; if ( $topfile && ( $fp = fopen( "$topfile,new", 'w' ) ) ) { $r0 = array( '%', "\n", '<' ); $r1 = array( '%25', '%0a', '%3c' ); $x = "version=$Version fmt=pagetop\n"; $st = true && fputs( $fp, $x ); $tz = strlen($x); //if (empty($page)) $page = array( 'ctime' => $Now, 'time' => $Now ); foreach( $page as $k => $v ) if ( ( $k > '' ) && ( $k[0] != '=' ) && ( $k != 'version' ) && ( $k != 'text' ) && ( $k != 'newline' ) && ( strpos($k,':') == FALSE ) ) { $x = str_replace( $r0, $r1, "$k=$v" ) . "\n"; $st = $st && fputs( $fp, $x ); $tz += strlen($x); } $st = $st && fputs( $fp, "\n" . $page['text'] . "\n" ); $tz += 2 + strlen($page['text']); $st = fclose($fp) && $st; $st = $st && ( filesize("$topfile,new") > $tz * 0.95 ); if (file_exists( $topfile )) $st = $st && unlink($topfile); $st = $st && rename( "$topfile,new", $topfile ); } if ($st) { fixperms($topfile); touch( $topfile, $page['time'] ); } else Abort("Cannot write page $pagename top to ($topfile)..."); } function fill() { global $ScriptUrl; print( "PageTopStore::fill()
\nFilling PageTopStore at {$this->topfmt} from PageStore at {$this->dirfmt}...\n" );
		foreach( @parent::ls() as $pagename ) {
			print("  $pagename\n"); flush();
			$page = parent::read( $pagename, READPAGE_CURRENT );
			$this->writetop( $pagename, $page );
		}
		print("\nall done.\n
\n

Return to $ScriptUrl

\n"); } function read( $pagename, $since=0 ) { global $EnablePageTopStoreAutofill; if ( $since != READPAGE_CURRENT ) { if (IsEnabled( $EnablePageTopStoreAutofill, TRUE )) $this->modtop( $pagename ); return parent::read( $pagename, $since ); } $urlencoded = FALSE; $topfile = $this->pagetopfile($pagename); if ( $topfile && ( $ft = @fopen($topfile,'r') ) ) { $page = $this->attr; while ( !feof($ft) ) { ## headers $line = fgets( $ft, 4096 ); while ( ( substr( $line, -1, 1 ) != "\n" ) && !feof($ft) ) $line .= fgets( $ft, 4096 ); $line = rtrim($line); if (!$line) break; ## empty line indicates end of headers if ($urlencoded) $line = urldecode(str_replace( '+', '%2b', $line )); @list($k,$v) = explode( '=', $line, 2 ); if (!$k) continue; if ( $k == 'version' ) $urlencoded = ( strpos( $v, 'urlencoded=1' ) !== FALSE ); $page[$k] = $v; } $page['text'] = trim(stream_get_contents( $ft )); fclose($ft); return $page; } $page = parent::read( $pagename, READPAGE_CURRENT ); if ( IsEnabled( $EnablePageTopStoreAutofill, TRUE ) && !empty($page) && $topfile && !file_exists($topfile) ) $this->writetop( $pagename, $page ); return $page; } function write( $pagename, $page ) { global $PCache; parent::write( $pagename, $page ); $text = $page['text']; $page = $PCache[$pagename]; if (empty( $page )) Abort("Page $pagename is blank?"); $page['text'] = $text; $this->writetop( $pagename, $page ); } function exists( $pagename ) { if (!$pagename) return false; $pagefile = $this->pagefile($pagename); $topfile = $this->pagetopfile($pagename); return ( ( $pagefile && file_exists($pagefile) ) || ( $topfile && file_exists($topfile) ) ); } function delete($pagename) { global $Now; $pagefile = $this->pagefile($pagename); @rename( $pagefile, "$pagefile,del-$Now" ); @unlink( $this->pagetopfile($pagename) ); } ## inherited: function ls($pats=NULL) }