WeakWiki


lib\engine.library.php



/********************************************************************************
WeakWiki (WeakWiki Engine library: engine.library.php)
Copyright (C) 2010 Alexander Lang

This program is free software; 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 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this
program; if not, see .

Project Home: http://weakwiki.robnet.wmweb.at/
Contact:      robbiblubber@robnet.wmweb.at
********************************************************************************/



require_once('./lib/parser.library.php');
require_once('./lib/uc.library.php');
require_once($GLOBALS['M_HISTORY']);



/** Current Version.															*/
define('_VERSION', '0.16.0');


/** Flags a document creation.													*/
define('WIKI_NEW',  2);

/** Flags a document change.													*/
define('WIKI_EDIT', 0);


/** Flags a read operation.														*/
define('OP_READ',    'r');

/** Flags a write operation (edit).												*/
define('OP_WRITE',   'w');

/** Flags a write operation (edit).												*/
define('OP_EDIT',    'w');

/** Flags a history operation.													*/
define('OP_HISTORY', 'h');

/** Flags a source operation.													*/
define('OP_SOURCE',  's');

/** Flags aa administrative operation.											*/
define('OP_ADMIN',  'a');


/** Default permissions for unknown users.										*/
define('PERM_DEFAULT_UNKNOWN', 'rhs');

/** Default permissions for reader level users.									*/
define('PERM_DEFAULT_READ',    'rhs');

/** Default permissions for editor level users.									*/
define('PERM_DEFAULT_EDIT',   'rwhs');

/** Default permissions for superusers.											*/
define('PERM_DEFAULT_ADMIN', 'rwxhs');


/** Error constant for no error.												*/
define('E_OK',             0);

/** Error constant for empty wiki title.										*/
define('E_TITLE_EMPTY',    1);

/** Error constant for already existing documents.								*/
define('E_ALREADY_EXISTS', 2);


/** Flags a default search.														*/
define('SEARCH_DEFAULT',    0);

/** Flags a full text search.													*/
define('SEARCH_FULLTEXT', 128);



/** This class represents a document.											*/
class Document
{
	//////////////////////////////////////////////////////////////////////////////
	// protected members														//
	//////////////////////////////////////////////////////////////////////////////
	
	/** Document title.															*/
	protected $title;
	
	/** Document text.															*/
	protected $text;
	
	
	/** Permissions.															*/
	protected $perm = null;
		
	
	//////////////////////////////////////////////////////////////////////////////
	// constructor																//
	//////////////////////////////////////////////////////////////////////////////
	
	/** Creates an instance of this class.
		@param $title		Title.
		@param $texr		Text.												*/
	public function __construct($title = "", $text = "")
	{
		$this->title = $title;
		$this->text  = $text;
	}
	
	
	//////////////////////////////////////////////////////////////////////////////
	// public methods															//
	//////////////////////////////////////////////////////////////////////////////
	
	/** Gets the Document's title.
		@return			Title.													*/
	public function getTitle()
	{
		return $this->title;
	}
	
	
	/** Gets the Document's text.
		@return			Text.													*/
	public function getText()
	{
		if($this->text == "")
		{											// try to read text from file
			if($this->title != "")
			{
				if($this->exists())
				{
					$this->text = file_get_contents($this->getFileName());
				}
			}
		}
		
		return $this->text;
	}
	
	
	/** Gets the Document's text.
		@return			Text.													*/
	public function getSource()
	{
		return formatSource($this->getText());
	}
	
	
	/** Returns the parsed wiki text.
		@return			Parsed Document text.									*/
	public function getHTML()
	{		 
		if(X_PREPARSE)
		{
			if(file_exists($this->getParsedFile()))
			{
				return file_get_contents($this->getParsedFile());
			}			
		}
		
		$rval = wikiParse($this->getText());
		
		if(X_PREPARSE)
		{
			if((strpos($rval, "<<@lock:") === false) && (strpos($rval, "<<@dynamic>>") === false))
			{
				file_put_contents($this->getParsedFile(), $rval);
			}
		}
		
		return $rval;
	}
	
	
	/** Writes the parsed wiki text.
		@param $prefix	Text to precede the Document's text.
		@param $postfix	Text to succeed the  Document's text.					*/
	public function write($prefix = "", $postfix = "")
	{
		echo($this->getHTML());
	}
	
	
	/** Deletes the document.													*/
	function delete()
	{
		_writeHistory($this->title, 'wiki entry deleted');
		jwrite('wiki entry deleted', $this->title);
		unlink($this->getFileName());
		
		if(file_exists($this->getParsedFile()))
		{
			unlink($this->getParsedFile());
		}
	}
	
	
	/** Updates the document.
		@param $title		New title.
		@param $text		New text.
		@return				Returns E_OK if update was successful.
							Otherwise an error code.							*/
	function update($title, $text)
	{
		if(trim($title) == "")
		{											// no title
			return E_TITLE_EMPTY;
		}
		
		$exists = $this->exists();					// check existence
		$nexists = file_exists(_PATH_CONTENT . toTitle($title) . '.wiki');
		
		if(($this->title != $title) || (!$exists))
		{											// new or renamed Documents
			if($nexists)							// may not overwrite existing
			{										// Documents
				return E_ALREADY_EXISTS;
			}
		}
		
		if(($this->title != $title) && $exists)
		{											// Document is being renamed
													// delete old file
			unlink($this->getFileName());
			_renameHistory($this->title, $title);
			
			if(file_exists($this->getParsedFile()))
			{
				unlink($this->getParsedFile());
			}
		}
		
		if(!$exists)
		{											// write log and history
			_createHistory($title);
			jwrite('wiki entry created', $title);
		}
		else
		{
			_writeHistory($title, 'wiki entry edited');
			jwrite('wiki entry edited', $title);
		}
		
		$this->title = $title;						// update members
		$this->text  = $text;
		
		file_put_contents($this->getFileName(), $text);
		
		if(file_exists($this->getParsedFile()))
		{
			unlink($this->getParsedFile());
		}
			
		return E_OK;
	}
	
	
	/** Returns, if the Document exists.
		@return			TRUE, if the Document exists, otherwise FALSE.			*/
	public function exists()
	{
		return file_exists($this->getFileName());
	}
	
	
	/** Checks if an operation is allowed for the current user.
		@param $op		Operation string:
							OP_READ, OP_EDIT (= OP_WRITE), OP_HISTORY, OP_READ.
		@return			TRUE if operation is permittet, otherwise FALSE.		*/
	public function request($op)
	{
		$u = currentUser();
		$this->readPermissions();
		
		if($u == null)	{ return (strpos($this->perm["." . UL_UNKNOWN], $op) !== false); }
		
		return (strpos($this->perm["." . $u->getLevel()], $op) !== false);
	}
	
	
	/** Returns if the current user is allowed to read the document.
		@return			TRUE if the document may be read, otherwise FALSE.		*/
	public function readable()
	{
		return $this->request(OP_READ);
	}
	
	
	/** Returns if the current user is allowed to edit the document.
		@return			TRUE if the document may be edited, otherwise FALSE.	*/
	public function editable()
	{
		return $this->request(OP_WRITE);
	}
	
	
	/** Gets the permissions for a user group.
		@param $ul		User level:
							UL_UNKNOWN, UL_READ, UL_EDIT, UL_ADMIN.
		@return			Permission set.											*/
	public function getPermissions($ul)
	{
		$this->readPermissions();
		
		return new DocumentPermissions($this->perm["." . $ul]);
	}
	
	
	//////////////////////////////////////////////////////////////////////////////
	// protected methods														//
	//////////////////////////////////////////////////////////////////////////////
	
	/** Evaluates the Document's permissions.									*/
	protected function readPermissions()
	{
		if($this->perm != null) return;
		
		$this->perm = Array
		(
			"." . UL_UNKNOWN => PERM_DEFAULT_UNKNOWN,
			"." . UL_READ    => PERM_DEFAULT_READ,
			"." . UL_EDIT    => PERM_DEFAULT_EDIT,
			"." . UL_ADMIN   => PERM_DEFAULT_ADMIN
		);
		
		$t = $this->getText();
		
		if(strpos($t, "<<@lock:") === false) return;
		
		$p = Array();
		
		$i = 0;
		while(true)
		{
			$c = _getArea($t, "<<@lock:", ">>", $i);
					
			if($i < 0) break;
			
			array_push($p, substr($c, 8, strlen($c) - 10));
		}
		
		foreach($p as $i)
		{
			$i = str_replace("->", "=", str_replace(",", ";", str_replace(" ", "", $i)));
			
			foreach(explode(";", $i) as $j)
			{
				$c = explode("=", strtolower($j));
				
				switch(substr($c[0], 0, 1))
				{
					case "u":
						$this->perm["." . UL_UNKNOWN] = $c[1];
						break;
					case "r":
						$this->perm["." . UL_READ] = $c[1];
						break;
					case "e":
						$this->perm["." . UL_EDIT] = $c[1];
						break;
				}
			}
		}
	}
	
	
	/** Gets the documents source file name.
		@return			File name.												*/
	protected function getFileName()
	{
		return _PATH_CONTENT . toTitle($this->title) . ".wiki";
	}
	
	
	/** Gets the preparsed file name.
		@return			File name.												*/
	protected function getParsedFile()
	{
		return _PATH_PREPARSED . toTitle($this->title) . ".html";
	}
}


/** This class holds permissions on an Document for a user group.				*/
class DocumentPermissions
{
	//////////////////////////////////////////////////////////////////////////////
	// protected members														//
	//////////////////////////////////////////////////////////////////////////////
	
	/** String representation of user group permissions							*/
	protected $perm;
	
	
	//////////////////////////////////////////////////////////////////////////////
	// constructor																//
	//////////////////////////////////////////////////////////////////////////////
	
	/** Creates an instance of this class.
		@param $p		Permission string.										*/
	public function __construct($p)
	{
		$this->perm = $p;
	}
	
	
	//////////////////////////////////////////////////////////////////////////////
	// public methods															//
	//////////////////////////////////////////////////////////////////////////////
	
	/** Returns, if an Document may be read.
		@return			TRUE, if read operations are permitted.					*/ 
	public function allowRead()
	{
		return (strpos($this->perm, "r") !== false);
	}
	
	
	/** Returns, if an Document may be edited.
		@return			TRUE, if edit operations are permitted.					*/ 
	public function allowEdit()
	{
		return (strpos($this->perm, "w") !== false);
	}
	
	
	/** Returns, if history of an Document may be viewed.
		@return			TRUE, if history operations are permitted.				*/ 
	public function allowHistory()
	{
		return (strpos($this->perm, "h") !== false);
	}
	
	
	/** Returns, if source of an Document may be viewed.
		@return			TRUE, if source operations are permitted.				*/ 
	public function allowSource()
	{
		return (strpos($this->perm, "s") !== false);
	}
}


/** This class provides search capabilities.									*/
class Search
{
	//////////////////////////////////////////////////////////////////////////////
	// protected members														//
	//////////////////////////////////////////////////////////////////////////////
	
	/** Search results.															*/
	protected $results;
	
	/** Query string.															*/
	protected $query;
	
	/** Search mode.															*/
	protected $mode;
	
	
	
	//////////////////////////////////////////////////////////////////////////////
	// constructor																//
	//////////////////////////////////////////////////////////////////////////////
	
	public function __construct()
	{
		$results = Array();
		$query   = "";
		$mode    = SEARCH_DEFAULT;
	}
	
	
	
	//////////////////////////////////////////////////////////////////////////////
	// public methods															//
	//////////////////////////////////////////////////////////////////////////////
	
	/** Returns the query string.
		@return				Query string.										*/
	public function getQueryString()
	{
		return $this->query;
	}
	
	
	/** Sets the query string.
		@param value		Query string.										*/
	public function setQueryString($value)
	{
		$this->query = $value;
	}
	
	
	/** Returns the query mode.
		@return				Mode.												*/
	public function getMode()
	{
		return $this->mode;
	}
	
	
	/** Sets the query mode.
		@param value		Mode.												*/
	public function setMode($value)
	{
		$this->mode = $value;
	}
	
	
	/** Returns the query results.
		@return				Result as array of entry titles.					*/
	public function getResults()
	{
		return $this->results;
	}
	
	
	/** Returns if the query returned results.
		@return				TRUE if results have been returned, FALSE if the
							result set is empty.								*/
	public function hasResults()
	{
		return (count($this->results) > 0);
	}
	
	
	/** Returns if a direct hit exists.
		@param $query		Query string.
		@return				TRUE if a direct hit exists, otherwise FALSE.		*/
	public function hasDirectHit($query)
	{
		return file_exists(_PATH_CONTENT . toTitle($query) . '.wiki');
	}
	
	
	/** Returns the direct hit (if exists).
		@param $query		Query string.
		@return				Direct hit document. NULL, if no direct hit exists.	*/
	public function getDirectHit($query)
	{
		if(hasDirectHit($query)) { return new Document($query); }
		return null;
	}
	
	
	/** Searches the wiki for matches with the given query string.
		@param $query		Query string.
		@param $mode		Mode.
		@return				Results.											*/
	public function find($query = "", $mode = SEARCH_DEFAULT)
	{
		$this->results = Array();
		
		switch($mode)
		{
			case SEARCH_FULLTEXT:
				$query = strtolower($query);
				
				$d = opendir(trim(_PATH_CONTENT, "/"));
									
				while(true)
				{
					$i = readdir($d);
							
					if($i === false) break;
					
					if(strlen($i) > 5)
					{
						if(substr($i, strlen($i) - 5) == ".wiki")
						{
							$c = strtolower(file_get_contents(_PATH_CONTENT . $i));
							if(strpos($c, $query) !== false)
							{
								array_push($this->results, fromTitle(substr($i, 0, strlen($i) - 5)));
							}
						}
					}
				}
				break;
			case SEARCH_DEFAULT:
				$query = toTitle($query);
				
				$d = opendir(trim(_PATH_CONTENT, "/"));
				
				while(true)
				{
					$i = readdir($d);
							
					if($i === false) break;
					
					if(strlen($i) > 5)
					{
						if(substr($i, strlen($i) - 5) == ".wiki")
						{
							if(strpos(substr($i, 0, strlen($i) - 5), $query) !== false)
							{
								array_push($this->results, fromTitle(substr($i, 0, strlen($i) - 5)));
							}
						}
					}
				}
				break;
		}
		
		sort($this->results);
		
		return $this->results;
	}
}


/** Returns a link to collapse/expand the side bar.
	@param $mode			Mode. SIDE_COLLAPSE or SIDE_EXPAND.
	@return					Link.
function linkSide($mode)
{
	return _WIKI_COMMAND . '?action=' . $mode . '&' . str_replace('action=', 'paction=', str_replace('?', '&', $_SERVER['QUERY_STRING']));
}


/** Returns a link to the referring page of a side bar collapse/expand.
	@return					Link.
function referSide()
{
	return '?' . trim(str_replace('action=' . SIDE_EXPAND, '', str_replace('action=' . SIDE_COLLAPSE, '', str_replace('?', '&', $_SERVER['QUERY_STRING']))), '&');
}


/** Writes a system log entry.
	@param $text		Text.
	@param $document	Document (optional).
	@param $uid			User ID (optional).										*/
function jwrite($text, $document = "", $uid = "")
{	
	if(($u = currentUser()) != null) { $uid = $u->getUID(); }
	if(strlen($document) > 0)        { $document = "[[" . $document . "]]"; }
	
	file_put_contents("./history/current.log", "|" . date("Y-m-d H:i:s") . "|" . $uid . "|" . $text . "|" . $document . "|\n", FILE_APPEND | LOCK_EX);
}


/** Formats input string as a file system-complient wiki title.
	@param $title	Input string.
	@return			Formatted string.											*/
function toTitle($title)
{
	$drop = Array("/", "\\", "\"", "'", ",", ";", "?", "*");
	
	$rval = str_replace(" ", "_", strtolower($title));
	
	foreach($drop as $i) { $rval = str_replace($i, "", $rval); }
	return $rval;
}


/** Formats system-complient wiki title into textual wiki title.
	@param $title	Input string.
	@return			Formatted string.											*/
function fromTitle($title)
{
	return ucwords(str_replace("_", " ", $title));
}


/** Formats source code.
	@param $text		Source.													*/
function formatSource($text)
{
	$text = str_replace("&", "&", $text);
	$text = str_replace("<", "<", $text);
	$text = str_replace(">", ">", $text);
	$text = str_replace("\r\n", "\n", $text);
	$text = str_replace("\t", "    ", $text);
	$text = str_replace(" ", " ", $text);
	$text = str_replace("\n", "
\n", $text); return $text; } /** Uncompresses a zlib-compressed string. @param $data String. */ function _gzdecode($data) { $g = tempnam(_PATH_HISTORY, 'ff'); @file_put_contents($g, $data); ob_start(); readgzfile($g); $d = ob_get_clean(); unlink($g); return $d; } ?>

WeakWiki