<?php if (!defined('PmWiki')) exit();

/*	=== Sisterly ===
 *	Copyright 2008 Eemeli Aro <eemeli@gmail.com>
 *
 *	Lets wikis on a wiki farm refer to each other
 *
 *	Developed and tested using the PmWiki 2.2.0-beta series.
 *
 *	To install, add the following to your configuration file:

		$SisterFmt = array(
			'Name' => array(
				'dirfmt' => '[...]/wiki.d/{$FullName}',
				'scripturl' => 'http://[...]',
				'attachurlprefix' => 'http://[...]',
				'passwdread' => '' ),
			[...]
		);
		include_once("$FarmD/cookbook/sisterly.php");

 *	where the contents of $SisterFmt match you farm layout.
 *
 *	For more information, please see the online documentation at
 *		http://www.pmwiki.org/wiki/Cookbook/Sisterly
 *
 *	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['Sisterly']['Version'] = '2008-10-14';

if (empty($SisterFmt)) return;

$Sisters = new SisterStore($SisterFmt);
$WikiLibDirs[] = &$Sisters;

SDVA( $SearchPatterns['farm'], $SearchPatterns['default'] );
SDV( $SearchPatterns['farm']['farm'], '');
# $SearchPatterns['default']['farm'] = '';
# $SearchPatterns['farm']['farm'] = "/^\w[\w-]*:/";

$FmtPV['$Wiki'] = 'preg_replace("/^(?:([^:]+):)?.*$/","$1",$group)';
$FmtPV['$PageUrl'] = '$GLOBALS["Sisters"]->pageurl("$group","$name")';

Markup( '{sister$var}', '<{$var}', '/\\{((?:\w[\w-]*):!?[-\\w.\\/\\x80-\\xff]*)(\\$:?\\w+)\\}/e', "PRR(PVSE(PageVar(\$pagename, '$2', '$1')))" );
Markup( 'Attach:sister','<urllink', "/\\bAttach:(".implode('|',array_keys($SisterFmt))."):/ie", '$GLOBALS["Sisters"]->get("$1","attachurlprefix")' );

## fixes for included sister pages
if (IsEnabled($EnableSisterFixIncluded,1)) SDVA( $QualifyPatterns, array(
	'/(\\[\\[(?>[^\\]]+?->)?\\s*)([^:]+?([|#?].*?)?\\]\\])/e' => "preg_match('/^\w[-\w]*:/',\$group,\$sm) ? PSS('$1').\$sm[0].PSS('$2') : PSS('$0')",
	'!\\bAttach:([^:.]+/)?([^:./]+\\.)!e' => 'preg_match("/^(\w[-\w]*):(.*)$/",$group,$sm) ? $GLOBALS["Sisters"]->get($sm[1],"attachurlprefix").( "$1">"" ? "$1" : $sm[2]."/" ).PSS("$2") : PSS("$0")',
));

$MakePageNameFunction = 'MakeSisterPageName';
function MakeSisterPageName( $basepage, $str ) {
	global $PageNameChars, $PagePathFmt, $MakePageNamePatterns;
	SDV($PageNameChars,'-[:alnum:]');
	SDV($MakePageNamePatterns, array(
		"/'/" => '',			   # strip single-quotes
		"/[^$PageNameChars]+/" => ' ',         # convert everything else to space
		'/((^|[^-\\w])\\w)/e' => "strtoupper('$1')",
		'/ /' => ''));
	$str = preg_replace('/[#?].*$/', '', $str);
	if (!preg_match( "/^(?:(\w[\w-]*):)?(.*)/", $str, $sm )) return '';
	$m = preg_split( '/[.\\/]/', $sm[2] );
	if ( count($m)<1 || count($m)>2 || $m[0]=='' ) return '';
	if (empty($sm[1])) $site = '';
	else $site = preg_replace( array_keys($MakePageNamePatterns), array_values($MakePageNamePatterns), $sm[1] ) . ':';
	##  handle "Group.Name" conversions
	if (@$m[1] > '') {
		$group = preg_replace( array_keys($MakePageNamePatterns), array_values($MakePageNamePatterns), $m[0] );
		$name = preg_replace( array_keys($MakePageNamePatterns), array_values($MakePageNamePatterns), $m[1] );
		return "$site$group.$name";
	}
	$name = preg_replace( array_keys($MakePageNamePatterns), array_values($MakePageNamePatterns), $m[0] );
	$isgrouphome = count($m) > 1;
	foreach((array)$PagePathFmt as $pg) {
		if ($isgrouphome && strncmp($pg, '$1.', 3) !== 0) continue;
		$pn = $site . FmtPageName(str_replace('$1', $name, $pg), $basepage);
		if (PageExists($pn)) return $pn;
	}
	if ($isgrouphome) {
		foreach((array)$PagePathFmt as $pg) if (strncmp($pg, '$1.', 3) == 0)
			return $site . FmtPageName(str_replace('$1', $name, $pg), $basepage);
		return "$site$name.$name";
	}
	return $site . preg_replace('#[^/.]+$#', $name, $basepage);
}

class SisterStore extends PageStore {
	var $iswrite = FALSE;
	var $fmt;
	var $attr;

	function SisterStore( $sfmt, $a=NULL ) {
		$this->fmt = $sfmt;
		$this->attr = (array)$a;
		$GLOBALS['PageExistsCache'] = array();
	}

	## enables case-insensitive access
	function get($sn,$prop) {
		foreach( array_keys($this->fmt) as $s ) if (!strcasecmp($s,$sn)) return $this->fmt[$s][$prop];
		return '';
	}

	## used in $FmtPV['$PageUrl']
	function pageurl($group,$name) {
		global $ScriptUrl, $EnablePathInfo;
		if ( preg_match( '/^(\w[\w-]*):(.+)$/', $group, $m ) && ( $surl = $this->get($m[1],'scripturl') ) )
			$group = $m[2];
		else
			$surl = $ScriptUrl;
		# return PUE( ( ($group=='Main') && ($name=='HomePage') ) ? "$surl/" : ( ($name==$group) ? "$surl/$group" : "$surl/$group/$name" ) );
		return PUE( $EnablePathInfo ? "$surl/$group/$name" : "$surl?n=$group.$name" );
	}

	function pagefile($pagename) {
		if ( preg_match( '#^(\w[\w-]*):([^./]+)[./](.+)$#', $pagename, $sm ) && ( $sdf = $this->get($sm[1],'dirfmt') ) ) {
			$dfmt = str_replace(
				array( '{$FullName}',     '{$Group}' ),
				array( $sm[2].'.'.$sm[3], $sm[2]     ),
				$sdf
			);
			# return FmtPageName( $dfmt, $pagename );
			return $dfmt;
		}
		return FALSE;
	}

	function read($pagename, $since=0) {
		$page = parent::read($pagename,$since);
		if ($page) {
			$page['passwdattr'] = '@lock';
			$page['passwdedit'] = '@lock';
			$page['passwdupload'] = '@lock';
		}
		if ( empty($page['passwdread']) && preg_match('#^(\w[\w-]*):.*[./]GroupAttributes$#i',$pagename,$sm) )
			$page['passwdread'] = $this->get($sm[1],'passwdread');
		return $page;
	}

	## inherited: function exists($pagename)

	function write($pagename,$page) { Abort("Cannot write page to $pagename on sister site...changes not saved"); }
	function delete($pagename) { Abort("Cannot delete page $pagename on sister site...changes not saved"); }

	function ls($pats=NULL) {
		global $GroupPattern, $NamePattern;
		StopWatch("SisterStore::ls begin");
		$pats=(array)$pats;
		if (!isset($pats['farm'])) return array();
		array_push( $pats, "/^\w[\w-]*:$GroupPattern\.$NamePattern$/" );
		$out = array();
		foreach( array_keys($this->fmt) as $sister ) {
			$dir = $this->pagefile($sister.':$Group.$Name');
			$maxslash = substr_count($dir, '/');
			$dirlist = array(preg_replace('!/*[^/]*\\$.*$!','',$dir));
			while (count($dirlist)>0) {
				$dir = array_shift($dirlist);
				$dfp = @opendir($dir); if (!$dfp) { continue; }
				$dirslash = substr_count($dir, '/') + 1;
				$o = array();
				while ( ($pagefile = readdir($dfp)) !== false) {
					if ($pagefile{0} == '.') continue;
					if ($dirslash < $maxslash && is_dir("$dir/$pagefile"))
						{ array_push($dirlist,"$dir/$pagefile"); continue; }
					if ($dirslash == $maxslash) $o[] = "$sister:$pagefile";
				}
				closedir($dfp);
				StopWatch("SisterStore::ls merge $sister");
				$out = array_merge($out, MatchPageNames($o, $pats));
			}
		}
		StopWatch("SisterStore::ls end");
		return $out;
	}
}