<?php
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//				More Configuration
//

	////////	just kill requests from the libwww-perl* bot
	if( strpos($_SERVER['HTTP_USER_AGENT'],'libwww-perl') !== false ){
		header("HTTP/1.0 403 Forbidden");
		die();
	}
	
	define('WikyBlog',true);
	$processTotals['request'] = microtime();
	
	$initiateFileClass = true;
	$packageVersion = '1.7.3rc1';
	$jsNum = '109';
	$softwareServer = 'WikyBlog.com';
	$page = null;
	$pageOwner = null;
	$dbObject = null;
	$langA = null;
	$wbParser = false;
	$wbMessageBuffer = array();
	if( empty($wbDirPrefix) ){
		$wbDirPrefix = '';		//needs to be of the form: /abcd
	}
	$wbLinkPrefixStor = '';
	$wbLinkPrefix = $wbDirPrefix;
	
	if( defined('wbDebug') && wbDebug == true){
		//not good for map development, setScroll(), rsetScroll()... global variables get reset when js files get reloaded
		//
		$jsNum = date('YmdHi');
		//$jsNum = rand();message($jsNum);	
											
	}else{
		define('wbDebug',false);
	}

	
	$wbTables = array();
	$wbTables['ad_objects'] = ' `'.$wbTablePrefix.'ad_objects`';
	$wbTables['all_blog'] = ' `'.$wbTablePrefix.'all_blog`';
	$wbTables['all_files'] = ' `'.$wbTablePrefix.'all_files`';
	$wbTables['all_history'] = ' `'.$wbTablePrefix.'all_history`';
	$wbTables['all_included'] = ' `'.$wbTablePrefix.'all_included`';
	$wbTables['all_links'] = ' `'.$wbTablePrefix.'all_links`';
	$wbTables['all_search'] = ' `'.$wbTablePrefix.'all_search`';
	$wbTables['all_watch'] = ' `'.$wbTablePrefix.'all_watch`';
	$wbTables['config'] = ' `'.$wbTablePrefix.'config`';
	$wbTables['config_links'] = ' `'.$wbTablePrefix.'config_links`';
	$wbTables['config_ugroups'] = ' `'.$wbTablePrefix.'config_ugroups`';
	$wbTables['config_vars'] = ' `'.$wbTablePrefix.'config_vars`';
	$wbTables['users'] = ' `'.$wbTablePrefix.'users`';
	$wbTables['workgroup'] = ' `'.$wbTablePrefix.'workgroup`';
	
	
	$wbIndexPHP = false;
	$wbMaintain = false;
	$wbPluginDir = false;
	$wbPluginSpace = false;
	$wbNow = date('Y-m-d H:i:s');
	
	if( empty($wbAdminUser) ){
		$wbAdminUser = 'Main';
	}
	if( empty($GLOBALS['wbDefaultTitle']) ){
		$GLOBALS['wbDefaultTitle'] = 'Home';
	}

	//If Magic Quotes
	if( get_magic_quotes_gpc() ){
		fix_magic_quotes( $_COOKIE );
		fix_magic_quotes( $_ENV );
		fix_magic_quotes( $_GET );
		fix_magic_quotes( $_POST );
		fix_magic_quotes( $_REQUEST );
		fix_magic_quotes( $_SERVER );
	}
	
	//If Register Globals
	if( ini_get('register_globals') ){
		foreach($_REQUEST as $key => $value){
			$key = strtolower($key);
			if( ($key == 'globals') || $key == '_post'){
				die( 'Hack attempted.' );
			}
		}
	}
	
	////////////////////////////////////////////////////////////////////////////////////
	//
	//		php5
	//
	function php4_clone(&$object){
		if(version_compare(phpversion(), '5.0','<') ){
			return $object;
		}else{
			return clone($object);
		}
	}

	////////////////////////////////////////////////////////////////////////////////////
	//
	//
	//			PHP
	//			http://us2.php.net/manual/en/ini.php#ini.list
	
	ini_set( 'session.use_only_cookies', '1' );
	@ini_set( 'url_rewriter.tags', ''); //tells php which html tags are used to include session id
	
	ini_set( 'default_charset', 'utf-8' );
	
// 	if( !defined( 'PATH_SEPARATOR' ) ){
// 		if ( strpos( $_ENV[ 'OS' ], 'Win' ) !== false ){
// 			define( 'PATH_SEPARATOR', ';' );
// 		}else{
// 			define( 'PATH_SEPARATOR', ':' );
// 		}
// 	}
// 	ini_set( 'include_path', $includeDir.PATH_SEPARATOR.ini_get('include_path'));
	
	//	Output Buffer
	//	needed because of xmlHTTP responses
	//	if an included file accidentally sends text, even a space, the header won't be sent correctly
	//	happened with specFileManager.php, and CLASStemplate.php
	
	$wbCompress = false;
	if( !ini_get('zlib.output_compression') && isset($_SERVER['HTTP_ACCEPT_ENCODING']) ){
		if( strpos($_SERVER['HTTP_ACCEPT_ENCODING'],'gzip') !== false ){
			$wbCompress = true;
		}elseif( strpos($_SERVER['HTTP_ACCEPT_ENCODING'],'deflate') !== false ){
			$wbCompress = true;
		}
	}
	
	if( $wbCompress && function_exists('ob_gzhandler') ){
		@ob_start( 'ob_gzhandler' ); //available since 4.0.4
	}elseif(function_exists('ob_get_level') && ob_get_level() === 0){
		ob_start();
	}

	//MultiByte String Support
	//	.. define all the mb functions here, load wikiStrings if mb functions are unavailable
	//	a lot of these functions aren't available until 4.3.0!!!!
	//
	if( function_exists('mb_convert_case') ){ //only avail as of 4.3
		$wbDefaultCharset = 'UTF-8';
		mb_internal_encoding('UTF-8');
		
		//trim
		//htmlspecialchars() accepts a charset argument from 4.3.0 and on!
		function wbHtmlspecialchars($str,$style=ENT_COMPAT){
			return htmlspecialchars($str,$style,'UTF-8');
		}
		
		function wbUcwords($str){
			//$parts = wbExplode(' ',$str);
			return ucWords($str); //this doesn't seem to corrupt data
			return mb_convert_case($str, MB_CASE_TITLE);				//I don't like this function! sWho's Worse" becomes "Who'S Worse"
		}
		
		function wbStrlen($str){		return mb_strlen($str);}
		function wbStrtoupper($str){	return mb_strtoupper($str);}
		function wbStrtolower($str){	return mb_strtolower($str);}	//!! be careful... was calling this functin 470+ per request.. it was very slow!.. now 30+
																		//it made the process time go from .130 to .210 seconds !!!
		
		function wbSubstr_replace($str,$repl,$start,$length=false){
			$beg = mb_substr($str,0,$start);
			if( $length === false ){
				$end = '';
			}else{
				$end = mb_substr($str,$length+$start);
			}
			return $beg.$repl.$end;
		}
		
		//this function is called a lot for links 
		//180+ times for a page like special/main/changelog
		//but using mb functions doesn't seem to affect the process time
		function wbStr_replace($search,$replace,$string){ 
			//profile('wbStr_replace',1);
			return str_replace($search,$replace,$string);
			
			$len = mb_strlen($search);
			$result = '';
			while( ($pos=mb_strpos($string,$search))||($pos===0)){
				$result .= mb_substr($string,0,$pos).$replace;
				$string = mb_substr($string,$pos+$len);
			}
			
			return $result.$string;
		}
		
		//need to escape special characters first within the sep
		function wbExplode($sep,$str,$limit=false){
			//profile('wbExplode',1);
			//return explode($sep,$str);
			//$normal = explode($sep,$str);
			
			$result = array();
			$sepLen = mb_strlen($sep);
			$count = 1;
			while(($pos=mb_strpos($str,$sep))||($pos===0)){
				$count++;
				$result[] = mb_substr($str,0,$pos);
				$str = mb_substr($str,$pos+$sepLen);
				if( $count === $limit){
					break;
				}
			}
			$result[] = $str;
			return $result;
		}
		
		function wbSubstr($string,$start,$length=false){
			if( $length !== false ){
				return mb_substr($string,$start,$length);
			}
			return mb_substr($string,$start);
		}
		
		function wbStrpos($haystack,$needle,$offset=false){
			if( $offset !== false ){
				return mb_strpos($haystack,$needle,$offset);
			}
			return mb_strpos($haystack,$needle);
		}
		
	}else{
		includeFile('tool/Strings.php');
	}
	
	//compare case insensitive
	function wbStrcasecmp($str1,$str2){
		
		if( strcasecmp($str1,$str2) === 0){
			return true;
		}
		if( wbStrtolower($str1) == wbStrtolower($str2) ){
			return true;
		}
		return false;
	}
		
	
	
	
	//
	//	wb class
	//
	class wb{
		function get_clean(){
			if( function_exists('ob_get_clean') ){
				return ob_get_clean();
			}
			$buffer = ob_get_contents();
			ob_end_clean();
			return $buffer;
		}
	}
	
	//ini_set('display_errors','0'); 
	//	php docs say not to use this in production systems.. 
	//	what's the diff with error_reporting(0);
	error_reporting(0);
	set_error_handler('showError');

	//this the directory being used for development
	if( wbDebug == true){
		//restore_error_handler();
		
		//These are used for testing queries
		includeFile('tool/errorDebug.php');
			define('wbDebugQueryInfo',false);	//can be set to true/false
			define('wbDebugQueryMess',true);	//can be set to true/false
		
		
		error_reporting(E_ALL);
		includeFile('tool/errorLocal.php');
	}else{
		ignore_user_abort(true);
	}


	//////////////////////////////////////////////////////////////////////////////
	//	
	//		Class wbData
	//	
	class wbData{
		
		/* static */
		function getConfig($building=false){
			global $packageVersion,$wbTables,$wbIndexPHP,$wbWritable,$wbMaintain,$wbAdminUser;
			

			
			//
			// Query and Populate
			//
				//this type of usage of UNIX_TIMESTAMP() is the only way it should be used because of potential differences between php and mysql
				$query = 'SELECT `revision`, `version`, `maintained`, (UNIX_TIMESTAMP()-UNIX_TIMESTAMP(`maintained`)) as `tdiff`, `data` ';
				$query .= ' FROM '.$wbTables['config'].' ORDER BY revision DESC  LIMIT 1 OFFSET 0;'; 
				
				
				$result = @mysql_query($query); //use mysql_query here because I don't want to report any errors here
				profile('numQueries',1);
				if( $result ){
					
				
					$row = mysql_fetch_assoc($result);
					$temp = unserialize($row['data']);
					foreach($temp as $key => $value){	//merging w/ + or array_merge don't work
						$GLOBALS[$key] = $value;
					}
					
// 					//this has to be after setting all the globals if we want to be able to modify $dbInfo
// 					if( isset($temp['include']) ){
// 						wbData::adminInclude($temp['include']);
// 					}
					
					//Run the maintenance script Once a Day 
					if($row['tdiff'] > 86400 ){
						$wbMaintain = array($row['maintained'],true);
					}else{
						$wbMaintain = $row['maintained'];
					}
	
					$GLOBALS['wbConfig']['version'] = $row['version'];
				}

				
			//
			// Configuration Adjustments
			//
			
				$GLOBALS['wbConfig']['inter'] = array('http://www.wikyblog.com'=>1);
				if( !isset($GLOBALS['dbInfo']['help']) ){
					$GLOBALS['wbConfig']['helpServer'] = 'http://www.wikyblog.com';
				}
				if( isset($wbIndexPHP) && $wbIndexPHP ){
					$GLOBALS['serverName4'] = $GLOBALS['serverName3'].'/index.php';
				}else{
					$GLOBALS['serverName4'] =& $GLOBALS['serverName3'];
				}
				//Modify the dbInfo based on map key
				if( empty($GLOBALS['googleMapKeys']) ){
					unset($GLOBALS['dbInfo']['map']);
				}
				
				if( empty($GLOBALS['wbConfig']['pUser']) ){
					$GLOBALS['wbConfig']['pUser'] =& $wbAdminUser;
				}
				
		}
		
		/* static */
		function dbInfo($space,$which){
			global $dbInfo;
			$info =& $dbInfo[$space];
			
			if( !isset($info['dbTable']) ){
				trigger_error('dbTable not set for this type: '.$space);
			}
			
			if( isset($info[$which]) ){
				return $info[$which];
			}
			
			switch($which){
				case 'querySelect';
					return $info['dbTable'].'.* ';
					
				case 'queryFrom';
					return $info['dbTable'];
					
				case 'dTitle':
					$info['dTitle'] = array($space=>1,'" > "'=>0,$info['dbTable'].'.`title` '=>0);
					return $info['dTitle'];
					
				case 'uniqLink':				//I don't like this ucWords()
					$info['uniqLink'] = ' CONCAT("/'.ucWords($space).'/", '.$info['dbTable'].'.`owner`, "/", '.$info['dbTable'].'.`title` ) ';
					return $info['uniqLink'];
				
				case 'keys':
					$info['keys'] = array('owner'=>1,'title'=>1);
					return $info['keys'];
				
				case 'xmlHTTP':
					return ' "1" ';
					
				default:
					return $info['dbTable'].'.'.$which;
			}
		}
	
		//	Creates SQL statement to create a displayTitle for each row
		/* static */
		function getDTitle($space){
			global $langA,$dbInfo;
			
			$format = wbData::dbInfo($space,'dTitle');
			
			$tempA = array();
			foreach( $format as $piece => $how){
				if( $how == 0){
					$tempA[] = $piece;
				}else{
					$tempA[] = '"'.$langA[$piece].'"';
				}
			}
			return ' CONCAT( '.implode($tempA,', ').') as `dTitle` ';
		}

		
		/* static */ 
		function loadFileFunctions(){
			global $wbFTP;
			
			if( isset($wbFTP) && isset($wbFTP['use']) && $wbFTP['use'] ){
				includeFile('tool/FileSystemFTP.php');
			}else{
				includeFile('tool/FileSystem.php');
			}
		}
		
		/* static */ 
		function newObject($type,&$newObject,$isDbObject=false){
			global $dbInfo,$rootDir;
			
			$class = wbData::getClass($type,$isDbObject);
			if( !$class ){
				return false;
			}
			$newObject = new $class($type);
			$newObject->objectType = $type; //make sure objectType is set for UserGroup Restrictions
		}
		
		/* static */ 
		function getClass($type,$isDbObject=false){
			global $dbInfo,$rootDir,$wbTables,$wbPluginDir,$wbPluginSpace;
			
			$type = strtolower($type);
			if( !isset($dbInfo[$type]) ){
				trigger_error('Object type not specified');
				return false;
			}
			//restrict object creation to objects that are represented in $dbInfo
			if($isDbObject && !isset($dbInfo[$type]['dbTable'])){
				return false;
			}
			$class = $dbInfo[$type]['class'];
			if( class_exists($class) ){
				//don't need to do anything
				
			}elseif( isset($dbInfo[$type]['isPlugin']) && $dbInfo[$type]['isPlugin']){
				$file = $rootDir.'/plugins/'.$dbInfo[$type]['isPlugin'].'/class.php';
				if( !file_exists($file) ){
					die('The necessary plugin file does not exist. Space: '.$type.' :: File: /plugins/'.$dbInfo[$type]['isPlugin'].'/class.php');
				}
				require_once($file);
				
			}elseif( isset($dbInfo[$type]['dbPlugin'])){
				$query = 'SELECT `data` FROM '.$wbTables['ad_objects'].' WHERE `selector` = "class:'. wbDB::escape($class) .'"  LIMIT 1 OFFSET 0;';
				$result = wbDB::runQuery($query);
				
				if( $row = mysql_fetch_assoc($result) ){
					eval($row['data']);
				}else{
					trigger_error('Invalid dbPlugin '.$class);
				}
				
			}else{
				$file = $class.'.php';
				includeFile($file);
			}
			return $class;
		}
		
		/* static */
		function adminInclude(&$list){
			global $rootDir;
			$files = explode(',',$list);
			foreach($files as $file){
				$file = trim($file);
				if( $file == ''){
					continue;
				}
				if( $file{0} == '/'){
					$file = $rootDir.$file;
				}else{
					$file = $rootDir.'/'.$file;
				}
				
				$file = realpath($file);
				//if( !ini_get('safe_mode') && !is_file($file) ){
				if( !is_file($file) ){
					trigger_error('Could not find the admin specified file: '.$file);
					continue;
				}
				require_once($file);
			}
		}
		//example:	en,fr;q=0.7,en-us;q=0.3		... the q=0.7 defines a quality value..
		function detectLang($first=true){
			global $includeDir;
			$temp = array();
		
			$accept =& $_SERVER['HTTP_ACCEPT_LANGUAGE'];
			$tok = strtok($accept,',;');
			while( $tok !== false){
				if( strpos($tok,'q=') !== 0){
					$tok = strtolower($tok);
					$langDir = $includeDir.'/lang/'.$tok;
					if(is_dir($langDir)){
						if( $first ){
							return $tok;
						}
						$temp[] = $tok;
						
					}elseif( $pos = strpos($tok,'-')){
						$tok = substr($tok,0,$pos);
						$langDir = $includeDir.'/lang/'.$tok;
						if(is_dir($langDir)){
							if( $first ){
								return $tok;
							}
							$temp[] = $tok;
						}
					}
				}
				$tok = strtok(',;');
			}
			if( $first && (count($temp) < 1) ){
				return $GLOBALS['userLanguage'];
			}
			return $temp;
			
		}
	}
	
	
	
	


//
//				Configuration
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//				ERROR HANDLING
//


function showError($errno, $errmsg, $filename, $linenum, $vars){

	// for functions prepended with @ symbol to suppress errors
	if($errno === 0){
		return;
	}
	//PHP5 E_STRICT
	if( !defined('E_STRICT')){
		define('E_STRICT',2048);
	}
	
	//if we're not debugging, don't show notices
	if( error_reporting() != E_ALL){
		$excludeErrors = array(E_NOTICE,E_STRICT);
		if( in_array($errno, $excludeErrors) ){
			return;
		}
	}

	//showError2() will exists for installations matching development environment
	if( !function_exists('showError2') ){
		includeFile('tool/error.php');
	}
	showError2($errno,$errmsg,$filename,$linenum,$vars);
}

function showArray($array){
	if( is_object($array) ){
		$array = get_object_vars($array);
	}

	$text = array();
	$text[] = '<table cellspacing="0" cellpadding="7" class="tableRows" border="0">';
	if(is_array($array)){
		$odd = null;
		$odd2 = null;
		
		foreach($array as $key => $value){
			
			if($odd2==1){
				$odd = 'bgcolor="white"';
				//$odd = ' class="tableRowEven" ';
				$odd2 = 2;
			}else{
				$odd = 'bgcolor="#ddddee"';
				//$odd = ' class="tableRowOdd" ';
				$odd2 = 1;
			}
			$text[] = '<tr '.$odd.'><td>';	
 			$text[] = $key;
			$text[] = "</td><td>";
			if( !empty($value) ){
				if( is_object($value) || is_array($value) ){
					$text[] = showArray($value);
				}elseif(is_string($value)||is_numeric($value)){
					$text[] = wbHtmlspecialchars($value);
				}elseif( is_bool($value) ){
					if($value){
						$text[]= '<tt>TRUE</tt>';
					}else{
						$text[] = '<tt>FALSE</tt>';
					}
				}else{
					$text[] = '<b>--unknown value--:</b> '.gettype($value);
				}
			}
			$text[] = "</td></tr>";
		}
	}else{
		$text[] = '<tr><td>'.$array.'</td></tr>';
	}
	$text[] = "</table>";

	return "\n".implode("\n",$text)."\n";
}




//
//				ERROR
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//				MESSAGES
//


//	Buffer warnings because the $langA file(s) might be loaded after a warning is made
//		because of a user language preference...
//
//	EXAMPLE OF message()
//		$langA['test'] = 'go to %s';
//		message('test','the moon!');
//
//		RESULT: 'go to the moon!' AS A WARNING

function message(){
	global $wbMessageBuffer;
	$wbMessageBuffer[] = func_get_args();
}
function devMessage(){
	global $wbDevMessBuffer;
	$wbDevMessBuffer[] = func_get_args();
}

function returnMessages($wrap=true){
	global $wbMessageBuffer,$wbDevMessBuffer,$langA;
	$result = '';
	
	if( isAdmin(false) ){
		$wbMessageBuffer = array_merge( (array)$wbMessageBuffer, (array)$wbDevMessBuffer); //type cast for php5
	}
	
	if( empty($wbMessageBuffer) ){
		if( $wrap ){
			//return '<div class="WBmessages" name="WBmessages" style="display:none"></div>';
		}
		return '';
	}

	foreach($wbMessageBuffer as $key2 => $args){
		if( !isset($args[0]) ){
			continue;
		}
		//if first argument is in $langA, use the text in $lang
		if( isset($langA[$args[0]]) ){
			$args[0] = $langA[$args[0]];
		}
		
		if( isset($args[1]) ){
			$result .= '<li>'.call_user_func_array('sprintf',$args).'</li>';
		}else{
			$result .= '<li>'.$args[0].'</li>';
		}
		
	}
	
	$wbMessageBuffer = array();
	
	if($wrap){
		//position static so that the float won't disappear in explorer
		//return '<div class="WBmessages" style="position:static !important;"><a class="scriptOnly" style="float:right;text-decoration:none;font-weight:bold;font-size:x-small;" href="javascript:void(0)" onclick="dcr(this.parentNode);return false;">X</a><ul>'.$result.'</ul></div>';
		return '<div class="WBmessages" name="WBmessages" style="position:static !important;"><a class="scriptOnly" style="float:right;text-decoration:none;font-weight:bold;font-size:x-small;" href="javascript:void(0)" onclick="WB.DS(this.parentNode,\'none\');return false;">X</a><ul>'.$result.'</ul></div>';
	}else{
		return '<ul>'.$result.'</ul>';
	}
}


//	Used for the retrieval of $langA values
//
class wbLang{
	
	/* static */ function getFile($file='index'){
		global $rootDir,$langA,$dbInfo;
		static $init = false;
		$langFile = $rootDir.'/include/lang/'.$GLOBALS['userLanguage'].'/'.$file.'.php';
		
		//for register globals
		if( !is_array($langA)){
			$langA = array();
		}
		
		//When the lang files are in UTF8 format the "<?" opening get's messed up and turns into "?<?"
		//those extra characters in front of the "<?" are sent to the user before the <!DOCTYPE..> element
		//and screw up display .. so make sure language files are plain text ... but the strings have been made in UTF8
		//
		include_once($langFile);
		if( !$init && !empty($dbInfo) ){
			foreach($dbInfo as $space => $info){
				// if( !isset($info['dbTable']) ){
				// 	continue;
				// }
				if( isset($info['alias']) ){
					$langA[$space] = $info['alias'];
				}elseif( !isset($langA[$space]) ){
					$langA[$space] = $space;
				}
			}
		}
		$init = true;
	}
	
	/* static */ function text(){
		global $langA;
		$args = func_get_args();
		if( isset($langA[$args[0]]) ){
			$args[0] = $langA[$args[0]];
		}
		return call_user_func_array('sprintf',$args);
	}
	
	//	get a language file from the plugin directory
	//
	/* static */ function pluginFile($which='index.php',$defaultLang='en'){
		global $rootDir,$langA,$wbPluginDir;
		
		
		$file = $rootDir.'/plugins/'.$wbPluginDir.'/include/lang/'.$GLOBALS['userLanguage'].'/'.$which;
		//if( !ini_get('safe_mode') ){
			if( !is_file($file) ){
				$file = $rootDir.'/plugins/'.$wbPluginDir.'/include/lang/'.$defaultLang.'/'.$which;
				if( !is_file($file) ){
					trigger_error('The default plugin language file does not exist: '.$file);
					return;
				}
			}
		//}
		include_once($file);
	}
}
	

function profileDet($key,$line,$value){
	global $processTotals;
	if( empty($processTotals['count'][$key]) ){
		$processTotals['count'][$key]['all'] = 0;
	}
	if( empty($processTotals['count'][$key]['byLine'][$line]) ){
		$processTotals['count'][$key]['byLine'][$line] = 0;
	}
	if( empty($processTotals['count'][$key]['byValue'][$value]) ){
		$processTotals['count'][$key]['byValue'][$value] = 0;
	}
	
	$processTotals['det'][$key][][$line] = $value;
	
	$processTotals['count'][$key]['all'] += 1;
	$processTotals['count'][$key]['byLine'][$line] += 1;
	$processTotals['count'][$key]['byValue'][$value] += 1;
	
}

function profile($key,$value=1){
	global $processTotals;
	if( empty($processTotals[$key]) ){
		$processTotals[$key] = 0;
	}
	if( is_int($value) ){
		$processTotals[$key] += $value;
		return;
	}
	if( $value === 'start' ){
		$processTotals['start'][$key][] = array_sum(explode(" ", microtime()));
		return;
	}elseif($value==='end'){
		$b = array_sum(explode(" ", microtime()));//current
		$processTotals[$key] += ($b-array_pop($processTotals['start'][$key]) );
		unset($processTotals['start'][$key]);
		return;
	}
	trigger_error('Profile Error: Unkown $value');
}

function microtime_diff($a, $b, $eff = 3) {
	$a = array_sum(explode(" ", $a));
	$b = array_sum(explode(" ", $b));
	return sprintf('%0.'.$eff.'f', $b-$a);
}


function showKeywords(&$keywords,&$owner,$after='',$before='Tags: '){
	if( wbStrlen($keywords) > 0 ){
		$keywords = wbExplode(',',$keywords);
		$keys=array();
		foreach($keywords as $key){
			if( !empty($_GET['key']) && $_GET['key'] == $key){
				$keys[] = '<a>'.$key.'</a>';
				continue;
			}
			$key2 = $key;
			if(wbStrpos($key2,':')=== false){
				$key2 .= ':';
			}
			$keys[] = wbLinks::special('keywordSearch?key='.rawurlencode($key2),$key,'',$owner);
		}
		return $before.implode(', ',$keys).$after;
	}
}

function getKeywords($javascript=false){
	global $pageOwner;
	if( $javascript ){
		ob_start();
	}
	$keywords =& $pageOwner['keywords'];
	if( !is_array($keywords) ){
		return '';
	}
	
	$keys = array_keys($keywords);
	$nums = array_values($keywords);
	
	//javascript can just go through children of "WBkeywords2" and hide those with className =="WBkeywordLink2"
	echo '<ul id="WBkeywords2" onmouseover="WB.CT(ckt)" onmouseout="collapseKeywords()">';
	for($i = 0;$i<count($keys);$i++){
		
		$key = $keys[$i];
		$num =& $nums[$i];
		$dots = '';
		
		if( $pos = strpos($key,':')){
			echo '<li class="WBkeywordLink2 loadHide">';
			echo wbLinks::special('keywordSearch?key='.rawurlencode($key),ltrim(substr($key,$pos+1)).' ('.$num.')');
			echo '</li>';
			
		}else{
			
			//check the next one
			if( isset($keys[$i+1]) && (strpos($keys[$i+1],':')!== false) ){
				$dots = '...';
			}
			echo '<li class="WBkeywordHead2" onmouseover="doKeywords2(this)">';
			echo wbLinks::special('keywordSearch?key='.rawurlencode($key).':',$key.' ('.$num.') '.$dots);
			echo '</li>';
		}		
	}
	
	echo '</ul>';
	if( $javascript){
		return wb::get_clean();
	}
}


//
//		JSONResponse
//

class JSONResponse extends page{
	var $ajaxRequest = true;
	var $sessionData2 = false;
	
	function JSONResponse(){
		parent::page();
	}
	
	function send(){
		$this->head();
		$this->script();
		$this->foot();
		exit();
	}
	
	
	function head(){
		global $pageOwner,$langA;
		
		echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">';
		echo '<HTML xmlns="http://www.w3.org/1999/xhtml">';
		echo '<head>';
		echo '<meta http-equiv="content-type" content="text/html; charset=UTF-8" />';
		echo '<title>'.$this->displayTitle.'</title>';
		echo '<script type="text/javascript">/*<![CDATA[*/';
		echo "\n";
		echo 'function init(){if(window.parent.WBx)window.parent.WBx.act(JSON);}';
	}
	
	function script(){
		global $pageOwner,$wbMessageBuffer,$langA,$wbLinkPrefix,$wbLinkPrefixStor;
		$nl = "\n\n";
		$i = 0;
		
		echo $nl.'var fullyLoaded=false;';
		echo $nl.'var JSON=Array();';
		
		////////Before any Content! the Form
			if( !empty($this->formAction) ){
				//$this->members('form',$this->formMethod,wbLinks::getUrl($this->formAction),$wbLinkPrefixStor.toStorageUrl($this->formAction));
				//	!!formAction should not have & replaced with &amp; here
				$this->members('form',$this->formMethod,$wbLinkPrefix.str_replace('+','%2B',$this->formAction),$wbLinkPrefixStor.toStorageUrl($this->formAction));
			}
			
		////////Before any Content! set the previous tab
			if( !empty($this->prevTab) ){
				//$this->members('prev',$wbLinkPrefixStor.$this->prevTab,'');
				$this->members('prev',$this->prevTab,''); //delete uses $_GET['wb']['u'] which is a complete tab id
			}
			
		////////class content
			if( !empty($this->contentClasses) ){
				foreach($this->contentClasses as $each){
					$this->members($each->aa,$each->bb,$each->cc,$each->dd);
				}
			}
			
		////////Load Script
			if( !empty($this->scripts) ){
				$this->scripts = array_unique($this->scripts);
				foreach($this->scripts as $script){
					$this->members('script',$script,'');
				}
			}
		////////Load Style Sheets
			if( !empty($this->styles) ){
				$this->styles = array_unique($this->styles);
				foreach($this->styles as $style){
					$this->members('link',$style,'');
				}
			}
			
		////////User Menu
			if( $this->session === true){
				$this->members('addcontent','WBuserMenu','',getUserMenu(true));
			}
			
		////////Keywords
			if( $this->keywords === true){
				//only reset keywords if it's the same owner
				if( isset($_GET['wb']['o']) && ($_GET['wb']['o'] == $pageOwner['user_id'])){
					$this->members('addcontent','WBkeywords','',getKeywords(true));
				}
			}

		////////Pre-formatted XML
			if( is_array($this->contentXml) ){
				foreach($this->contentXml as $urlKey => $xml){
					$this->members('xml',$urlKey,$xml);
				}
			}
			
		////////Content Array
			foreach($this->rActions as $url => $label){
				if( empty($this->contentA[$label]) && empty($this->contentB[$url]) ){
					//echo $nl.'/* content for '.$label.' at '.$url.' was empty */ ';
					continue;
				}
				
				$key = $label;
				if( wbStrlen($label) > 18){
					$shortLabel = wbSubstr($label,0,15).'...';
				}else{
					$shortLabel = $label;
				}
				
				
				$this->members('',$shortLabel,$this->displayContentArea($key,$url),$wbLinkPrefix.$url,$wbLinkPrefixStor.toStorageUrl(wbLinks::escape($url)),$label);
				
				//navbar
				if( !empty($this->navbar) ){
					$this->members('addcontent','WBnavbar',$this->showId,$this->navbar);
					unset($this->navbar);
				}
			}
			
			if( !empty($wbMessageBuffer) ){
				$this->members('message',returnMessages(),$_GET['wb']['u']);
			}
			
			
		echo $nl.'fullyLoaded=true;';
	}
	
	// JSON[0] = {aa:'hello',bb:'hmm'};
	function members($aa,$bb,$cc=false,$dd=false,$ee=false,$ff=false){
		static $i = 0;
		echo "\n\n".'JSON['.$i.']={';
		$this->sendMember($aa,'aa');
		$this->sendMember($bb,'bb');
		$this->sendMember($cc,'cc');
		$this->sendMember($dd,'dd');
		$this->sendMember($ee,'ee');
		$this->sendMember($ff,'ff');
		echo '};';
		$i++;
	}
	
	//	this function isn't good enough
	//
	//
	function sendMember(&$txt,$str){
		static $search = array("\n","\r",'<script','</script>');
		static $repl = array('\n','\r','<"+"script','<"+"/script>');
		
		if( $txt === false){
			return;
		}
		if( $str != 'aa'){
			echo ',';
		}
		echo "\n".$str.':"';
		$txt = addcslashes($txt,'"\\');
		echo str_replace($search,$repl,$txt);
		echo '"';
	}
	
	function foot(){
		echo '/*]]>*/</script>';
		echo '</head>';
		echo '<body onload="init()">';
		//echo showArray($_GET);
		echo '</body>';
		echo '</html>';
	}
}

class ScriptResponse extends JSONResponse{
	var $interRequest = true;
	var $revType = 'interwiki';
	
	function ScriptResponse(){
		parent::page();
	}
	
	function head(){}
	function foot(){
		echo 'WBx.revtype=\'inter\';';
		echo 'WBx.act(JSON);';
	}
}

//
// 				Send Response
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// 				InterpretRequest
//
function interpretPath($path){
		global $dbInfo;

	////////	Variables
		$prefixes = array_keys($dbInfo);
		$prefixes = array_flip($prefixes);
		$resultPathArray = array('type'=>false,'type2'=>false,'owner'=>false);
		$resultPathArray['path'] = array();
		
	////////	Path
		$path = trim($path,'/\\');
		$path = str_replace('"','&quot;',$path);//all links have to be with double quotes ".."!
		$pathArray = explode('/',$path);
		$piece = array_shift($pathArray);

	////////	Edit
		if( strtolower($piece) == 'edit'){
			$piece = array_shift($pathArray);
		}
		
	////////	Prefixes
		$temp = toStorage($piece,true);
		if( isset($prefixes[$temp]) ){
			$resultPathArray['type'] = $temp;
			$piece = array_shift($pathArray);
			
			$temp = toStorage($piece,true);
			if( isset($prefixes[$temp]) ){
				$resultPathArray['type2'] = $temp;
				$piece = array_shift($pathArray);
			}
			
			
		}else{
			$resultPathArray['type'] = 'page';
		}


	////////	Owner
		if( $resultPathArray['type'] == 'help'){
			$resultPathArray['owner'] = $GLOBALS['wbAdminUser'];
			array_unshift($pathArray,$piece);
		}elseif( !empty($piece)){
			$resultPathArray['owner'] = $piece;
		}


	////////	The rest
		$resultPathArray['path'] = $pathArray;

	return $resultPathArray;
}

//
// 				InterpretRequest
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// 				FLOW CONTROL FUNCTIONS
//


function doWikiThing(){
		global $dbObject, $rootDir, $dbInfo, $page, $wbLinkPrefix, $wbLinkFormat, $wbDirPrefix, $wbLinkPrefixStor, $wbCookieDir;
		global $wbPluginSpace, $wbPluginDir;

		
		//HTTP_REFERER
		//message(showArray($_SERVER));
		
		
		
	////////	Type of Request
		$completePath = false;
		if( isset($_GET['wb']) ){
			if( isset($_GET['wb']['s'] ) ){
				$completePath = true;
				$page = new ScriptResponse();
			}else{
				$page = new JSONResponse();
			}
		}else{
			includeFile('wikiResponseNormal.php');
			$page = new wbPageNormal();
		}
			
	
	////////	Connect and get Configuration
		wbDB::connect();
		wbData::getConfig();
		
		if( defined('wbDebugAdv') ){
			includeFile('x_debug.php');
		}
		
		
		//$array['serverName3'] = 'http://'.$array['serverName2'];
		//$array['serverName3'] .= $configData['wbDirPrefix'];
		//
		
		//just making all links absolute paths
		$wbCookieDir = $wbDirPrefix;
		$wbLinkPrefix = 'http://'.$GLOBALS['serverName2'].$wbLinkPrefix;
		$wbDirPrefix = 'http://'.$GLOBALS['serverName2'].$wbDirPrefix;
		// if( $completePath ){
		// 	//This isn't working right for sites using subdirectories or subdomains
		// 	$wbLinkPrefix = $GLOBALS['serverName3'].$wbLinkPrefix;
		// 	$wbDirPrefix = $GLOBALS['serverName3'].$wbDirPrefix;
		// 	$wbLinkPrefixStor = toStorageUrl($wbLinkPrefix);
		// }
		$wbLinkFormat = 'href="'.$wbLinkPrefix.'%s" rev="%s" %s';
		
		//	Path
		if( isset($_POST['wbRedir']) || isset($_GET['wbRedir']) ){
			includeFile('tool/WBredir.php');
		}
		//	$_SERVER
		$path = $_SERVER['REDIRECT_URL'];
		$path = str_replace(array('<','>'),array('&lt;','&gt;'),$path);
		
		$len = wbStrlen($wbLinkPrefix);
		if($len > 0){
			$pos = wbStrpos($path,$wbLinkPrefix);
			if( $pos === 0 ){
				$path = wbSubstr($path,$len);
			}
		}
		
		$resultArray = interpretPath($path);
		$page->addMeta($resultArray['path']);
		

	////////	Datatypes
		
		$type = $resultArray['type'];
		if( isset($dbInfo[$type]['isPlugin']) && $dbInfo[$type]['isPlugin'] ){
			$wbPluginSpace = $type;
			$wbPluginDir = $dbInfo[$type]['isPlugin'];
		}
		$page->effectiveSpace = $type;
		wbData::newObject($type,$dbObject);  //SPEC.php overwrites $effectiveSpace for plugins
		$ownerName = $dbObject->getOwner($resultArray);
		
		
	////////	Session
	////////		- nothing requiring user verification should happen before this point ie isOwner(), hasPrivilege()... use of $pageOwner..
		includeFile('tool/session.php');
		wbSession::control($ownerName);
		$page->saltModJS();
		$page->checkLastModified();
		

	////////	Check Version
		if( ($GLOBALS['wbConfig']['version'] !== $GLOBALS['packageVersion']) ){
			includeFile('maintenance/Versions.php');
			versions::update();
		}
		
		
	////////	Get Configuration and Language
		wbLang::getFile();  //defaults to index
		
	////////	new getStep1 location
		$dbObject->getStep1($resultArray);
		$page->setTemplate(); //needs to be after getStep1 for classTemplate
		
	////////
		if( $page->status !== true ){
			includeFile('tool/SpecialContent.php');
			specialContent::fromStatus();

		}else{
			$dbObject->getStep2();
		}
		
	////////	SEND RESPONSE
		$page->prepPage();
		$page->send();
		
}




function includeFile( $file, $initiate = true ){
	global $page,$includeDir,$initiateFileClass;
	$prev = $initiateFileClass;
	$initiateFileClass = $initiate;
	require_once( $includeDir.'/'.$file );
	$initiateFileClass = $prev;
}
function rootInclude( $file ){
	global $page,$rootDir;
	require_once( $rootDir.'/'.$file );
}


function includeFileContents( $file ){
	global $page,$includeDir;
	ob_start();
	//profile('Include','start');
	require_once( $includeDir.'/'.$file );
	//profile('Include','end');
	return wb::get_clean();
}

function pluginIncludeFile($file, $initiate = true ){
	global $wbPluginDir, $rootDir, $initiateFileClass;
	$prev = $initiateFileClass;
	if( !$wbPluginDir ){
		trigger_error('wbPluginDir is not set');
		return;
	}
	$initiateFileClass = $initiate;
	require_once($rootDir.'/plugins/'.$wbPluginDir.'/'.$file);
	$initiateFileClass = $prev;
}


//
// 				FLOW CONTROL FUNCTIONS
//
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//					GET/EDIT PAGE FUNCTIONS 
//




function includeBlog($blogObj=false,$wrap=true){
	includeFile('tool/BlogContent.php');
	return BlogContent::blog($wrap);
}


function whichTemplate(&$data,$warn = false){
	global $rootDir;
	global $pageOwner;
	$filename =& $data['template'];
	
	
	if( empty($filename) || (wbStrpos($filename,'../') !== false) ){
		$data['pTemplate'] = 'graphic/blue';
		if( $warn ){
			message('INVALID_THEME');
		}
		return '';
	}
	
	$result = array();
	$result['templateNameLow'] = wbStrtolower($filename);
	$templateFile = $rootDir.'/templates/'.$result['templateNameLow'].'/template.php';

	if( !file_exists($templateFile) ){ //because a custom template could be deleted, but not the directory
	//if( !is_dir(dirname($templateFile) ) ){
		$data['pTemplate'] = 'graphic/blue';
		if( $warn ){
			message('INVALID_THEME');
		}
		return '';
	}
	
	$result['templateName'] =& $filename;
	$result['modified'] = filemtime($templateFile);
	// if( ini_get('safe_mode') ){
	// 	$result['modified'] = 0;
	// }else{
	// 	$result['modified'] = filemtime($templateFile);
	// }
	return $result;

}

//
//					GET/EDIT PAGE FUNCTIONS 
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//					Link Functions
//
//			see also browseLink2() in search.php
	
	class wbLinks{
		// local dir used by things that won't ever need /index.php prefixed
		/* static */
		function getDir($dir,$absolute=true){
			global $serverName3;
			if( $absolute ){
				return $serverName3.wbLinks::escape($dir);
			}
			return $GLOBALS['wbDirPrefix'].wbLinks::escape($dir);
		}
		
		//all other functions here will prefix with /index.php if needed
		/* static */
		function getUrl($href){
			return $GLOBALS['wbLinkPrefix'].wbLinks::escape($href);
		}
		/* static */
		function admin($specPage,$label,$attr='',$owner=false){
			return wbLinks::special($specPage,$label,$attr,$owner,'/Admin/');
		}
		/* static */
		function special($specPage,$label,$attr='',$owner=false,$special='/Special/'){
			global $pageOwner,$langA,$wbLinkFormat,$page;
			
			if($owner === false){
				$owner = $pageOwner['username'];
				$rev = $page->revType;
				
			}elseif( wbStrcasecmp($owner,$pageOwner['username'] ) ){
				$rev = $page->revType;
				
			}else{
				$rev = '';
				$attr = ' class="otheruser" '; //!! not going to work if class=".." is passed to this function
			}
				
			
			$href = $special.$owner.'/'.$specPage;
			$href = wbLinks::escape($href);
		
			if( isset($langA[$label]) ){
				$label = $langA[$label];
			}
			if( $label === false ){
				return sprintf($wbLinkFormat,$href,$rev,$attr);
			}
			return '<a '.sprintf($wbLinkFormat,$href,$rev,$attr).'>'.$label.'</a>';
		}
		
		function js($href,$label,$attr=''){
			global $wbLinkFormat,$langA;
			
			if( isset($langA[$label]) ){
				$label = $langA[$label];
			}
			$href = wbLinks::escape($href);
			$rev = 'always';
			return '<a '.sprintf($wbLinkFormat,$href,$rev,$attr).'>'.$label.'</a>';
		}
		
		/* static */
		function local($href,$label,$attr=''){
			global $wbLinkFormat,$pageOwner,$langA,$page;
			
			if( empty($href) ){
				$href = '/'.$pageOwner['username'].'/'.$GLOBALS['wbDefaultTitle'];
			}elseif( $href != '' && $href{0} != '/'){
				$href = '/'.$pageOwner['username'].'/'.$href;
			}
			$href = wbLinks::escape($href);
	
			if( $label === false ){
				return sprintf($wbLinkFormat,$href,$page->revType,$attr);
			}
			
			if( empty($label) ){
				$label = toDisplay($GLOBALS['wbDefaultTitle']);
			}elseif(isset($langA[$label]) && is_string($langA[$label])){
				$label = $langA[$label];
			}
			
			$page->addMeta($label);
			
			return '<a '.sprintf($wbLinkFormat,$href,$page->revType,$attr).'>'.$label.'</a>';
		}
		function user($name,$txt=false){
			global $wbLinkFormat,$wbDefaultTitle,$page,$pageOwner;
			
			if( wbStrcasecmp($pageOwner['username'],$name) ){
				$rev = $page->revType;
				$attrs = '';
			}else{
				$rev = '';
				$attrs = ' class="otheruser" ';
			}
				
			
			$href = '/'.$name.'/'.$wbDefaultTitle;
			if( $txt === false){
				$txt = toDisplay($name);
			}
			return '<a '.sprintf($wbLinkFormat,$href,$rev,$attrs).'>'.$txt.'</a>';
		}
			
		
		//this is only called by session.php!
		/* static */
		function getLink($href,$label,$attr=''){
			global $wbLinkFormat,$page;
			
			$page->addMeta($label);
			$href = wbLinks::escape($href);
			
			return '<a '.sprintf($wbLinkFormat,$href,$page->revType,$attr).'>'.$label.'</a>';
		}
		
		/* static */
		function help($href,$label,$attr=''){
			global $wbConfig,$serverName3,$dbInfo,$pageOwner,$langA,$page;
			
			$help = '/Help/'.$GLOBALS['userLanguage'].'/'.$href;
			
			if(isset($langA[$label]) && is_string($langA[$label])){
				$label = $langA[$label];
			}
			
			if( !isset($wbConfig['helpServer']) && !isset($dbInfo['help']) ){
				return '';
			}
			
			if( isset($dbInfo['help']) || ($serverName3 == $wbConfig['helpServer']) ){
				return wbLinks::local($help,$label);
			}
			$help = $wbConfig['helpServer'].$help;
			$rev = 'interwiki';
			return '<a href="'.$help.'" rev="'.$rev.'" title="'.$langA['help'] .'">'.$label.'</a>';
		}
		
		/* static */
		function image($image,$attr=''){
			$path = wbLinks::getDir($image);
			if( strpos($image,'.png') !== false ){
				return '<img src="'.$path.'" alt="'.$image.'" '.$attr.' style="filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src=\''.$path.'\', sizingMethod=\'scale\');" />';
			}
			return '<img src="'.$path.'" alt="'.$image.'" '.$attr.' />';
		}
		
		/* static */
		function escape(&$link){
			return str_replace(array('&','+'),array('&amp;','%2B'),$link);
		}
	}
	
	// Have to keep these two because they were built into templates...
	//
	function specialLink($specPage,$label,$attr='',$owner=false,$special='/Special/'){
		return wbLinks::special($specPage,$label,$attr,$owner,$special);
	}
	function localLink($href,$label,$attr=''){
		return wbLinks::local($href,$label,$attr);
	}





//
//					Link Functions
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//					WIKI FORMATTING


	
	function initiateParser($variables=array()){
		global $wbParser;
		if( !$wbParser ){
			includeFile('tool/myParser.php');
			$wbParser = new Parser();
		}
		$wbParser->setState($variables);
	}
	
	/* replacing wikiToHtml() */
	function outputWiki($text,$length=false,$flags=''){
		global $wbParser;
		initiateParser();
		
		$flags = str_replace(array('safe,',',safe','safe'),'',$flags);
		$moreWords = shortenWiki($text,$length);
		
		echo $wbParser->justParse($text,$flags); //have to do the whole parse() even if it's safe
		return $moreWords;
	}
	
	/* deprecated */
	function wikiToHtml(&$text,$length=false,$flags=''){
		global $wbParser;
		initiateParser();
		
		$flags = str_replace(array('safe,',',safe','safe'),'',$flags);
		
		$moreWords = shortenWiki($text,$length);
		
		$text = $wbParser->justParse($text,$flags); //have to do the whole parse() even if it's safe
		return $moreWords;
	}
	function shortenWiki(&$text,&$length){
		if( $length === false){
			return false;
		}
		$moreWords = false;
		if( wbStrlen($text) > $length ){
			$newLineSpot = wbStrpos($text,"\n",$length);
			if($newLineSpot){
				$moreWords = str_word_count($text);
				$text = wbSubstr($text,0,$newLineSpot).' ';
			}
		}

		$text = str_replace('{{TOC}}','',$text); //remove {{TOC}} .. not really the best place for this <nowiki>{{TOC}}</nowiki> will just be <nowiki></nowiki>
		return $moreWords;
	}

//
//		END WIKI FORMATTING
//
//////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//						SESSION/USER FUNCTIONS
//
	
	// check for global cookie
	function cookies(){
		return isset($_COOKIE['g']);
	}
	
	function hasPrivilege(&$objFlags,&$owner){
		global $wbConfig,$page;
		
		if( $page->disabled ){
			return false;
		}
		
		//	Used only to determine if a user can edit a page
		//	returns true or false depending on the flags
		//	this function needs to work for $dbObject object, search results (which are objects)
		if( !$owner ){
			trigger_error('Owner wasn\'t specified');
			return false;
		}
		
		
		if(isset($wbConfig['allUsers']) && $wbConfig['allUsers'] == 'Off'){
			if( !wbStrcasecmp($GLOBALS['wbAdminUser'],$owner) ){
				if( !wbStrcasecmp($GLOBALS['wbConfig']['pUser'],$owner) ){
					return false;
				}
			}
		}
		
		
		//Admins will always have accesss
		if( isAdmin(false) ){
			return true;
		}		
		
		if( $_SESSION['userlevel'] == -1){
			return false;
		}
		
		$level = 0;
		if( strpos($objFlags,'registered') !== false){
			$level = 1;
			
		}elseif( strpos($objFlags,'restricted') !== false){
			$level = 2;
			
		}elseif( strpos($objFlags,'locked') !== false){
			$level = 3;
			
		}elseif( strpos($objFlags,'admin') !== false){
			$level = 5;
		}
		
		if( $_SESSION['userlevel'] < $level ){
			return false;
		}elseif( $_SESSION['userlevel'] === $level ){
			return 1;
		}else{
			return 2;
		}
	}



//
//				SESSION/USER FUNCTIONS
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//
//				DATABASE FUNCTIONS
//
//

	
	class wbDB{
		function connect(){
			global $hostname, $dbname,$dbuname,$dbpword;
			static $connection;
			static $done = 0;
		
			if($done == 0){
				//loadExtension('mysql'); //built in to php
				
				//$connection = mysql_connect($hostname,$dbuname,$dbpword);
				$connection = @mysql_connect($hostname,$dbuname,$dbpword);
				if(!$connection){
					includeFile('admin/UnderConstruction1.php'); //file will die()
				}
		
				if(!@mysql_select_db($dbname,$connection)){
					includeFile('admin/UnderConstruction1.php');
				}
				$done = 1;
			}
			mysql_query('SET NAMES utf8;');
			return $connection;
		}
	
		// used by 
		//	session.php, toolSavePage.php
		//
		//	$table	string		the name of the table to update
		//	$up		arr/obj		[column]=value pairs to be written to $table
		//	$where	arr/obj		[column]=value pairs to select row within $table
		function dbUpdate2($table, $up, $where){
			$query = 'UPDATE '.$table.' SET ';
			$query .= wbDB::toSet($up);
			$query .= ' WHERE ';
			$query .= wbDB::toQuery($where,'AND',true);
			$query .= ' LIMIT 1';
			return wbDB::runQuery($query,true);
		}
	
		function dbInsert2($table,$array,$more=null){ 
			$query = 'INSERT INTO '.$table.' SET'; 
			$query .= wbDB::toSet($array,true);
			if( isset($more) ){
				$query .= $more;
			}
			return wbDB::runQuery($query,true);
		}
		
		//	Number of queries
		//		All Pages: (1) getConfig (2) session query
		//		special page: (2)
		//		normal page: (3) getPage (might change to 4 if we decide to do stats with each request)
		//		search page: (4) search + SQL_FOUND_ROWS
		//
		//
		function runQuery(&$query,$returnNum=false){
			($result = mysql_query( $query )) or trigger_error($query) ;
			profile('numQueries',1);
			if( $returnNum ){
				if( $result !== false ){
					$result = mysql_affected_rows();
				}
			}
		
			//	Used to optimize queries along with tool/errorDebug.php
			//	see error handling section for more info
			//	message($query);
			if(defined('wbDebugQuery') ){
				debugQueryAdd($query);				
			}
		
			return $result;
		}
	
		/// toQuery()
		//
		// 	turns arrays/objects into WHERE statements
		//
		//	$array		arr/obj		[column]=value pair
		//	$sep		string		string to place in between each [column]=value pair
		//	$nullValues 	bool		if true, empty values are not considered NULL because primary keys cannot be null
		function toQuery($array,$sep='AND',$nullValues=false){
		
			$ands = '';
			$string = '';
		
			if( is_object($array) ){
				$array = get_object_vars($array);
			}
		
			// need to keep value even if empty
			// 	- but this messess up comments when there is no prefix?
			foreach($array as $key => $value){
				if( empty($value) && !$nullValues ){
					$string .= $ands. ' ISNULL('.$key.') ';
					$ands = ' '.$sep.' ';
					continue;
				}
				if( is_numeric($value) ){
					$string .= $ands. ' '. $key .' = "'. $value .'" ';
				}else{
					$string .= $ands. ' '. $key .' = "'. wbDB::escape($value) .'" ';
				}
				$ands = ' '.$sep.' ';
			}
			return $string;
		}
		//	Better than addslashes
		function escape($string){
			// Stripslashes .. done by fix_magic_quotes()
			// if (get_magic_quotes_gpc()) {
			//     $string = stripslashes($string);
			// }
			if( is_numeric($string) ){
			    return $string;
			}
		    
			if( function_exists( 'mysql_real_escape_string') ){
				return mysql_real_escape_string($string);
			}
			return mysql_escape_string($string);
		}
		
		function remove_from_set($remove,$set){
			return ' TRIM("," FROM REPLACE( CONCAT(",", '.$set.', ",") , ",'.wbDB::escape($remove).'," , ",") )';
		}
		function add_to_set($add,$set){
			return ' IF( '.$set.' = "", "'.wbDB::escape($add).'", CONCAT_WS(",", '.$set.', "'.wbDB::escape($add).'" )) ';
		}
		
		function like($string){
			$string = wbDB::escape($string);
			return str_replace(array('%','_'),array('\%','\_'),$string);
		}		
	
		//this is used for inserts and updates
		// -> updates require use of null values
		function toSet($array,$nullValues=false,$table=false){
			if( $table ){
				$table = ' '.$table.'.';
			}else{
				$table = ' ';
			}
			$comma = '';
			$string = '';
			if( is_object($array) ){
				$array = get_object_vars($array);
			}
		
			foreach($array as $key => $value){
				if( is_int($value) || is_float($value) ){
					//don't need to do anything, numbers are safe
					
				}elseif( is_array($value) ){
					$value = $value[0];
					
				}elseif( empty($value) && !$nullValues ){
					$string .= $comma.$table.'`'. $key .'` = NULL ';
					$comma = ' , ';
					continue;
				}else{
					$value = '"'.wbDB::escape($value).'" ';
				}
				$string .= $comma.$table.'`'. $key .'` = '. $value;
				$comma = ' , ';
			}
			return $string;
		}		
	}


//
//	all_search
//

	class allSearch{
		/* static */
		function remove(&$object){
			global $wbTables;
			if( !isset($GLOBALS['wbConfig']['search']) || $GLOBALS['wbConfig']['search'] === false ){
				return;
			}
		
			$query = 'DELETE FROM '.$wbTables['all_search'].' WHERE `file_id`='.$object->file_id;
			if(!wbDB::runQuery($query)){
				trigger_error('Deleting from the all_search table failed');
			}
		}
	
		/* static */
		function update(&$object){
			global $dbInfo,$wbTables;
			//	GETTING THE SEARCHABLE TEXT	::	SEE adminSearchOptions.php
			//		1)	strip_tags()
			//		2)	MySQL: A ``word'' is any sequence of characters consisting of letters, digits, `'', and `_'
			//
			
			if( !isset($GLOBALS['wbConfig']['search']) || $GLOBALS['wbConfig']['search'] === false ){
				return;
			}
			if( !isset($object->dbInfo['searchTitle']) || $object->dbInfo['searchTitle'] === false){
				return; //this type not added to all_search
			}
			
			$searchData['all_title'] = '';
			$searchData['all_content'] = '';
			$searchData['file_id'] = $object->file_id;
			
			//set all_title
			$temp = array();
			foreach($object->dbInfo['searchTitle'] as $objKey){
				$temp[] = toDisplay($object->$objKey);
			}
			$searchData['all_title'] .= implode('/',$temp);
			$searchData['all_title'] = str_replace('_',' ',$searchData['all_title']);
			
			//set all_content
			if( isset($object->dbInfo['searchContent']) ){
				foreach($object->dbInfo['searchContent'] as $objKey){
					if( $objKey == 'keywords' ){
						continue;
					}
					$searchData['all_content'] .= $object->$objKey;
				}
				if( isset($object->keywords) ){
					$searchData['all_content'] .= $object->keywords;
				}
				$searchData['all_content'] = allSearch::cleanText($searchData['all_content']);
			}
			
			$query = 'REPLACE INTO '.$wbTables['all_search'].' SET ';
			$query .= wbDB::toSet($searchData,true);
			if( !wbDB::runQuery($query) ){
				trigger_error('all_search REPLACE query failed');
			}
		}
		/* static */
		function cleanText(&$text){
			//CDATA sections need to be handled differently.. 
			//	<![CDATA[this is a &lt;tag&gt; that needs to be stripped]]>
			//
			
			if( strpos($text,'<![CDATA[') ){
				$text = str_replace(array('<![CDATA[',']]>'),'',$text);
				$trans_tbl = get_html_translation_table(HTML_ENTITIES);
				$trans_tbl = array_flip($trans_tbl);
				$text = strtr($text, $trans_tbl);
			}
			
			//strip markup
			//	This doesn't do wiki links, tables and other portions of the syntax
			$text = strip_tags($text);
			$text = str_replace(array("'''''","''''","'''",'=====','====','===',),'',$text);
			do{
				$len = strlen($text);
				$text = str_replace(array("\n*","\n#","\n:","\n;"),"\n",$text);
			}while(strlen($text) != $len);
			
			return $text;
		}
	}



//
//				DATABASE FUNCTIONS
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//				PAGE ACTION FUNCTIONS 
//

//	isOwner() should be one thing, and 
//	ownerPrivs() another!
//	$allowAdmin is used 

function isOwner($warn=false,$strict=false,$allowAdmin=true){
	global $pageOwner;
	static $warned = false;

	////////	ownerPrivs
		if( ($_SESSION['userlevel'] === 3) && !$strict ){
			return true;
			
	////////	ownerStrict
		}elseif( $_SESSION['userlevel'] === 4 ){
			return true;
		}
		
	////////	Administrators (almost) always have access
		if( isAdmin(false) ){
			
			//if a user is given both owner privs on an account, and admin privs for the site, they won't actually be given these privs
			if( wbStrcasecmp($_SESSION['username'],$pageOwner['username']) ){
				return true;
			}
			if( $allowAdmin ){
				return true;
			}
		}
		
	////////	Warn?
		if( $warn && !$warned){
			message('NOT_OWNER');
			$warned = true;
		}

		return false;
}

	
	function isAdmin($strict=true){
		
		if( !isset($_SESSION) || !isset($_SESSION['userlevel']) ){
			return false;
		}
		
		if( !isset($_SESSION['admin'])|| ($_SESSION['admin'] !== true) ){
			return false;
		}
		
		if( $strict === false ){
			return true;
		}
		
		if( !isset($_SESSION['adminConfirmed']) ){
			return false;
		}else{
			$diff = mktime()-$_SESSION['adminConfirmed'];
			if( $diff > 1200 ){
				$_SESSION['adminConfirmed'] = mktime();
			}
		}
			
		return true;
	}

//
//				PAGE ACTION FUNCTIONS
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//				DATA TYPE FUNCTIONS
//


function stringToArray(&$string){
	$array = array();
	return explode("\n",str_replace(array("\r\n","\r"),"\n",$string));
}


function toDisplay($text,$len=null){
	$text = wbStr_replace('_',' ',$text);
	$text = wbStr_replace('/',' > ',$text);
	
	if( isset($len) ){
		if( wbStrlen($text) > ($len+5) ){
			$text = wbSubstr($text,0,$len).'...';
		}
	}
	return $text;
}

function toStorage($text,$full=false){
	$text = wbStr_replace(' ','_',$text);
	if( $full){
		return wbStrtolower($text);
	}
	return $text;
}

//	This gives us the values used to tie <div id="..."> to <a rev="...">
//		/!&@#
//

function toStorageUrl($rev){
	$rev = trim($rev);
	
	if($pos = wbStrpos($rev,'?')){
		$rev = toStorage(wbSubstr($rev,0,$pos),true).wbSubstr($rev,$pos);
	}else{
		$rev = toStorage($rev,true);
	}
	if( strpos($rev,'/edit') === 0){
		$rev = substr($rev,5);
	}
	$rev = base64_encode($rev); //33% larger, uses (A-Za-z0-9+/) characters with = as suffix
	return str_replace(array('/','='),array('.','_'),$rev); //make it valid xhmtl
	//return str_replace(array('/','='),'',$rev); //make it valid xhmtl
}
function fromStorageUrl($rev){
	$rev = str_replace(array('.','_'),array('/','='),$rev);
	return base64_decode($rev);
}

//
//	date functions
//
	function formatToArray($format){
		$array = array();
		$i = 0;
		$len= wbStrlen($format);
		while($i < $len){
			$char = wbSubstr($format,$i,1);
			if( $char == '\\'){
				$i++;
				$array[] = $char .wbSubstr($format,$i,1);
				$i++;
				continue;
			}
			$array[] = $char;
			$i++;
		}
		return $array;
	}
	
	function dbFromDate($timestamp,$which=1,$adjust=true){
		global $langA;
		
		if( ($timestamp == '0000-00-00 00:00:00') || ($timestamp == '00000000000000')){
			return 0;
		}
			
		
		//
		//	Get the unix timestamp
		//
			$unix = strtotime($timestamp);
			//try mktime from Chris Weiss 
			if( $unix == -1 && strlen($timestamp)==14){
				$unix = mktime( substr($timestamp,8,2),
				substr($timestamp,10,2),
				substr($timestamp,12,2),
				substr($timestamp,4,2),
				substr($timestamp,6,2),
				substr($timestamp,0,4));
			}

		//
		//	Adjust unix
		//	
			if( $which === '6'){ //UTC
				$unix += date('Z');
				
			}elseif( $adjust && isset($_SESSION['timezone']) ){
				$timeArray = explode(':',$_SESSION['timezone']);
				$hrs = 3600*(int)$timeArray[0];
				$mins = 60*(int)$timeArray[1];
				$unix += $hrs+$mins;
			}
		
			if( $unix == -1){
				trigger_error('dbFromDate() error: '.$timestamp);
				return;
			}
			if( ($which == 4) || $which == 'unix'){
				return $unix;
			}
			
		//
		//	Get the format
		//
					
			//DlFMaA
		
			$formats = array();
			$formats['1'] = array('l',', ','F',' ','j',', ','Y');										// 'l, F j, Y';
			$formats['2'] = array('m','/','d','/','Y');													//'m/d/Y';
			$formats['3'] = array('j',' ','M',' ','Y',', ','H',':','i');								//'j M Y, H:i';
			$formats['5'] = array('D',', ','j',' ','M',' ','Y',' ','H',':','i',':','s',' ','O');		// 'D, j M Y H:i:s O'; -0700
			$formats['6'] = array('Y','-','m','-','d','\T','H',':','i',':','s','\Z');					//'Y-m-j\TH:i:s\Z';
			$formats['7'] = array('H',':','i',' ','D',', ','j',' ','M',' ','Y');						// 'H:i D, j M Y'; EST
			$formats['8'] = array('D',', ','j',' ','M',' ','Y',' ','H',':','i',':','s',' ','T'); 		// 'D, j M Y H:i:s T'; EST
			$formats['9'] = array('Y','-','m','-','d',' ','H',':','i',':','s');							// 'Y-m-d H:i:s'; MySQL format
			$formats['l,_F_j,_Y'] = array('l',',_','F','_','j',',_','Y');
			
			if( ctype_alpha($which) || is_array($which) ){
				$format =& $which;
			}elseif( isset($formats[$which])){
				$format = $formats[$which];
			}else{
				$format = formatToArray($which);
			}
			
		//
		//	Get formatted 
		//	
			$result = '';
			foreach($format as $char){
				if( wbStrpos($char,'\\') === 0){
					$result .= wbSubstr($char,1);
					continue;
				}
				if( !ctype_alpha($char) ){
					$result .= $char;
					continue;
				}
				
				switch($char){
					case 'D':
						$char = $langA['date_D'][date('w',$unix)];
					break;
					case 'l':
						$char = $langA['date_l'][date('w',$unix)];
					break;
					case 'F':
						$char = $langA['date_F'][date('n',$unix)];
					break;
					case 'M':
						$char = $langA['date_M'][date('n',$unix)];
					break;
					case 'a':
						$char = $langA['date_a'][date('a',$unix)];
					break;
					case 'A':
						$char = $langA['date_A'][date('A',$unix)];
					break;
					default:
						$char = date($char,$unix);
					break;
				}
				$result .= $char;
			}
		return $result;
	}


// changes post value into values appropriate for insertion into the database
//	- called by setFromPost() functions ... which is only called from toolEditPage.php and toolSavePage.php

function newLines(&$arg){ 
	//	newlines... 
	if( is_string($arg) ){
		$arg = str_replace(array("\r\n","\r"),"\n",$arg);
		//$arg = wbStr_replace("\r\n","\n",$arg);
		//$arg = wbStr_replace("\r","\n",$arg); //somehow adds extra newlines?

	}elseif( is_array($arg) ){
		foreach($arg as $key => $value){
			newLines($arg[$key]);
		}
	}
	
}

function fix_magic_quotes( &$arr ) {
	foreach( $arr as $key => $val ) {
		if( is_array( $val ) ) {
			fix_magic_quotes( $arr[$key] );
		} else {
			$arr[$key] = stripslashes( $val );
		}
	}
}


function globalFromPost($textField='content'){
	global $wbNow;
	newLines($GLOBALS['_POST']);

	//	1)	keywords
	if( isset($_POST['keywords']) && is_string($_POST['keywords']) ){
		$_POST['keywords'] = wbHtmlspecialchars($_POST['keywords']);

		//	remove parentheses
		if( wbStrpos($_POST['keywords'],'(') !== false ){
			$pattern = '#\([^\)]+\)#';
			$repl = '';
			$_POST['keywords'] = preg_replace($pattern, $repl, $_POST['keywords'] );
		}

		$t = wbExplode(',',$_POST['keywords']);
		array_walk($t, 'trim_array');
		$t = array_unique($t);
		if( in_array('',$t) ){
			unset( $t[array_search('',$t)] );
		}
		$_POST['keywords'] = $t; // an array, but it'll be changed to a string in step two..
	}


	//	2)	ARRAYS TO COMMA LISTS
	//!!!!!		-- this should only be done with the POST values that need to be changed!! $_POST['keywords'], ['flags']????
	//
	if( isset($_POST['keywords']) && is_array($_POST['keywords']) ){
		$_POST['keywords'] = implode(',',$_POST['keywords']);
	}
	if( isset($_POST['flags']) && is_array($_POST['flags']) ){
		$_POST['flags'] = implode(',',$_POST['flags']);
	}

	//	3) Tildes
	$replace = array();
	if( isset($_SESSION['sig']) ){
		$signature =& $_SESSION['sig'];
		
	}elseif( isset($_SESSION['username']) ){
		$signature = '--[[/'.$_SESSION['username'].'/'.$GLOBALS['wbDefaultTitle'].'|'.$_SESSION['username'].']]';
		//$signature = '--[[/'.$_SESSION['username'].'|'.$_SESSION['username'].']]';
		
	}else{
		$signature =& $_SERVER["REMOTE_ADDR"];
	}

	//	3-a)	Date
	$search['timestamp'] = '~~~~~';
	$replace['timestamp'] = dbFromDate($wbNow,'7');

	//	3-c)	Signature and Date
	$signature_time = $signature .' '.$replace['timestamp'];
	$search[] = '~~~~';	//signature and date
	$replace[] = $signature_time;


	//	3-b)	Signature
	$search[] = '~~~';	// signature
	$replace[] = $signature;

	if( isset($_POST[$textField]) ){
		foreach($search as $sKey => $sValue){
			$_POST[$textField] = wbStr_replace($sValue,$replace[$sKey],$_POST[$textField]);
		}
	}
}


//	used by fromPost() get rid of extra white spaces in array values
function trim_array(&$value,$key,$chars=" \t\n\r\0\x0B"){
	$value = trim($value,$chars);
	if( empty($value) ){
		unset($value);
	}
}

//
//				DATA TYPE FUNCTIONS
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// 				 Class Page
//					extended by JSONResponse and wbPageNormal
//

class page{

	// these reflect display elements
	var $displayTitle = ''; 	// as shown to user
	var $contentA = array();	// ContentAreas by label
	var $contentB = array();	// ContentAreas by url
	var $contentXml;			// xml content

	var $showId;				//url Id
	
	var $contentClasses = array();		//for class content

	var $footer;
	var $keywords;				//set to true if tags need to be resent
	var $session; 				//set to true when the user menu need to be resent
	
	var $navbar = ''; 			// the navigation bar
	var $rActions = array(); 	// tabs
	var $cLinks = array();		// Content Links

	//	html elements
	var $htmlHead = array(); //!! version 1.7.2 - deprecated
	var $HeadArray = array();
	var $metaDescription = null;
	var $metaKeywords = array();

	// form variables
	var $autoForm = false; //auto set the formAction
	var $formAction;
	var $formMethod = 'post';
	var $formEnc = 'application/x-www-form-urlencoded';
	
	// template
 	var $css2 = false;
 	var $scripts = array();
 	var $styles = array();

	// Navigation
	var $pathArray;
	var $isBlog;
	var $ajaxRequest = false;
	var $interRequest = false;
	var $revType = 'local';

	// Command to be taken on page
	var $userCmd;
	var $cmdArg;
	
	//headers
	var $lastMod = array();
	var $lastModSalts = array();
	var $lastModTime;
	
	var $sessionData2 = true;
	
	//new
	var $nofollow = false;
	var $disabled = false;
	var $status = true;
	var $images = array();
	var $effectiveSpace;

	function page(){
		
		if( !empty($_POST['cmd']) ){
			$this->userCmd = $_POST['cmd'];
		}elseif( !empty($_GET['cmd']) ){
			$this->userCmd = $_GET['cmd'];
		}

		if( is_array($this->userCmd) ){
			reset($this->userCmd);//key() doesn't work right for all php versions
			while( is_array($this->userCmd) ){
				$this->cmdArg[] = key($this->userCmd);
				$this->userCmd = current($this->userCmd);
			}
		}

		if( !empty($this->userCmd) ){
			$this->userCmd = wbStrtolower($this->userCmd);
		}
		$this->userCmd = wbStr_replace('_',' ',$this->userCmd);
	}
	
	
	
	function regLink($label,$url=false){
		if($url===false){
			$this->cLinks[] = $label;
		}else{
			$this->cLinks[$url] = $label;
		}
		return $url;
	}
	
	//find a link in $page->cLinks[]
	function findLink($label){
		$return = array_search($label,$this->cLinks,true);
		if( is_null($return) ){
			$return = false;
		}
		return $return;
	}

	function pageTabs(&$linkArray,&$object){
		global $langA;
		
		$editable =& $object->editable;
		$flags =& $object->flags;
		$watching =& $object->watching;
		
		
		foreach($linkArray as $key => $value){

			
			////	Edit/View Source
				if( $value == $langA['edit'] ){
					if( !$editable ){
						$linkArray[$key] = $langA['view_source'];
					}
					
					
			/// Talk/Comments
				}elseif( $value == $langA['talk'] ){
					if( strpos($flags,'nocomments') !== false ){
						unset($linkArray[$key]);
						continue;
					}
					$linkArray[$key] = $object->talkLabel;
					
					// }elseif( ($value == $langA['talk']) && (strpos($flags,'nocomments') !== false) ){
					// 	unset($linkArray[$key]);
					// 	continue;
					
			/// Options
				}elseif( $value == $langA['options'] ){
					
					if( !isOwner() || !$editable ){
						unset($linkArray[$key]);
						continue;
					}
					
			/// Watch
				}elseif( ($value == $langA['watch']) && ($watching === '1') ){
					unset($linkArray[$key]);
					continue;
					
				}elseif( ($value == $langA['unwatch']) && ($watching === '0') ){
					unset($linkArray[$key]);
					continue;
				}
		}
	}
	
	
	function isBlog(){
		global $dbObject,$pageOwner;
		
		if( isset($this->isBlog) ){
			return $this->isBlog;
		}
		
		//must be the home page .. therefore using 'page' object
		if( !isset($dbObject->objectType) || ($dbObject->objectType != 'page') ){ 
			$this->isBlog = false;
			return false;
		}
		
		if( !empty($this->userCmd) ){
			$this->isBlog = false;
			return false;
		}
		
		if( !isset($dbObject->title) ){
			//don't know for sure yet
			return false;
		}
		
		if( !wbStrcasecmp($dbObject->title,$GLOBALS['wbDefaultTitle']) ){
			$this->isBlog = false;
			return false;
		}
		
		if( isset($pageOwner) ){
			if( isset($pageOwner['isBlog']) && ($pageOwner['isBlog'] =='On') ){
				$this->isBlog = true;
				return true;
			}
			$this->isBlog = false;
			return false;
		}
		return true; //could be
	}
	
	function addMeta(){} //redifined by wikiResponseNormal
	
	//////////////////////////////////////////////////////////////////////
	//
	//		HTTP/1.0 304 Not Modified
	//
	function setLastModified($timestamp){
		if( isset( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ){
			$this->lastMod[] = dbFromDate($timestamp,'unix');
		}
	}
	function saltMod(){}
	function saltModJS(){}
	function setTemplate(){}
	
	//
	//	Send a 304 response if the file(s)/preferences/js/template?jsNum have not changed
	//
	function checkLastModified($last=false){
		global $jsNum;
		
		if( !$last && $this->isBlog()){
			return;
		}
		
		if( defined('wbCache') && !wbCache){
			return;
		}
		if( count($this->lastMod) < 1 ){
			return;
		}
		$sum = array_sum($this->lastMod);
		$sum += array_sum($this->lastModSalts);
		
		$count = count($this->lastMod) + count($this->lastModSalts);
		$this->lastModTime = round(($sum-$jsNum)/$count);
		$this->lastModTime -= 86400; //subtract 24 hours
		
		if( !isset( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ){
			return;
		}
		
		$time = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
		
		// IE sends sizes after the date like this: Wed, 20 Aug 2003 06:51:19 GMT; length=5202
		// but we just strip the GMT and everything after... cause it's not really GMT
		$pos = strpos($time,'GMT');
		if( $pos !== false){
			$time = substr($time,0,$pos);
		}
		$time = strtotime($time);
		
		// must be the same modified time.. 
		//	the modified time for search pages could change negatively if a file is deleted..
		//
		if( $this->lastModTime != $time){
			return;
		}
		header( "HTTP/1.0 304 Not Modified" );
		if( function_exists('ob_get_level') ){
			while( ob_get_level() > 0){
				ob_end_clean();
			}
		}
		exit();
	}
	
	//////////////////////////////////////////////////////////////////////
	//
	//		Prepare Page
	//
	function prepPage(){
		global $dbObject,$pageOwner,$jsNum;
		
		//message('SERVER: '.showArray($_SERVER));
		//message('REQUEST: '.$_SERVER['REQUEST_URI']);
		//message('POST: '.showArray($_POST));
		//message('FILES: '.showArray($_FILES));
		//message('GET: '.showArray($_GET));
		//message('owner: '.showArray($pageOwner));
		
		$this->checkLastModified(true);
		//message('not 304 '.rand());
		
		////////	Headers
			//http://www.mnot.net/cache_docs/ has a good reference
			header('Cache-Control: private, must-revalidate, max-age=0, s-maxage=0, proxy-revalidate, no-cache' );
			if( isset($this->lastModTime) ){
				header('Last-Modified: '. date('D, j M Y G:i:s ',$this->lastModTime).' GMT');
				header('Expires: ' . date( 'D, d M Y H:i:s',$this->lastModTime) . ' GMT' );
			}
			
		////////	Check for debug info
			if( defined('wbDebugQuery') ){
				debugQueryAll();
			}
			
			//!!version 1.5.6
			if( isset($dbObject->links) && is_array($dbObject->links) ){
				foreach($dbObject->links as $label => $url){
					$this->cLinks[$url] = $label;
				}
			}
			
			
		/// ContentA to rActions
		
			foreach($this->contentA as $contentLabel => $value){
				if( empty($value) ){
					unset($this->contentA[$contentLabel]);
					continue;
				}
				
				//Get the newest link for this label
				//	the strict version of array_keys is only available in php5!
				//
				$url = $this->findLink($contentLabel);
				if( !$url ){
					$url = $_SERVER['REDIRECT_URL'].'#WBnone'.$_SERVER["REQUEST_METHOD"].$contentLabel;
				}
				
				
				//$url = urldecode($url);
				//$url = str_replace(array('&lt;','&gt;','<','>'),'',$url);
				
				if($pos = wbStrpos($url,'?')){
					$url = wbStr_replace(' ','_',wbSubstr($url,0,$pos)).wbSubstr($url,$pos);
				}else{
					$url = wbStr_replace(' ','_',wbSubstr($url,0,$pos));
				}
				$this->rActions[$url] = $contentLabel;
				if( !isset($this->showId) ){
					$this->showId = $url;
				}
			}
			
		/// ContentB to rActions
			foreach($this->contentB as $url => $value){
				if( empty($value) ){
					unset($this->contentB[$url]);
					continue;
				}
				
				//Get the Label
				if( empty($this->cLinks[$url]) ){
					$label = substr(strrchr($url,'/'),1);
					$label = toDisplay($label);
				}else{
					$label = $this->cLinks[$url];
				}
				$this->rActions[$url] = $label;
				
				if( !isset($this->showId) ){
					$this->showId = $url;
				}
			}
			
			$this->rActions = array_unique($this->rActions);

			//don't want to replace spaces at this point 
			//... or do I... unless all actions have spaces replaced
			
			$formUrl = $this->showId;
			$this->showId = wbLinks::escape($this->showId);

			$this->showId = toStorageUrl($this->showId);
			
			if( $this->autoForm ){
				$this->formAction = $formUrl;
			}elseif( !empty($this->formAction) ){
				//$this->formAction = str_replace('&','&amp;',$this->formAction); //done by getUrl(
			}
	}
	
	
	function displayContentArea($contentLabel,$urlKey){
		global $dbObject,$langA,$wbConfig,$serverName3,$dbInfo,$pageOwner;
		
		ob_start();
		
		
		//BoxInfo	
		$help = false;
		echo '<div class="WBboxInfo">';
		foreach($this->cLinks as $value => $lbl){
			if( is_numeric($value) ){
				echo $lbl;
				continue;
			}
			if(strpos($value,'#WBnone') !== false){
				continue;
			}
			if( $lbl == '?' ){
				if( $pageOwner['ihelp'] == 'On' ){
					$help = $value;
					$help = '/Help/'.$GLOBALS['userLanguage'].'/'.$value;
				}
				continue;
			}
			if( strpos($value,'javascript:') ===0){
				echo '<a href="javascript:void(0)" onclick="'.wbSubstr($value,11).';return false;">'.$lbl.'</a>';
				continue;
			}
			echo wbLinks::local($value,$lbl);
		}
		
		if( $help ){
			//help link
			if( isset($wbConfig['helpServer']) && ($serverName3 !== $wbConfig['helpServer'])){
				$help = $wbConfig['helpServer'].$help;
				echo '<a href="'.$help.'" rev="interwiki" title="'.$langA['help'] .'">?</a>';
			}else{
				echo wbLinks::local($help,'?',' title="'.$langA['help'] .'"');
			}
				
			
		}
		echo '</div>';
		
		
		//Output Content
		echo '<div class="WBcontentArea2">';
		echo '<h1 class="WBcontentTitle">';
		
		if( !$this->interRequest ){
			if( isset($_GET['wb']['o']) && ($_GET['wb']['o'] != $pageOwner['user_id'])){
				echo toDisplay($pageOwner['username']).' > ';
			}
		}
		if( !empty($this->displayTitle) ){
			echo $this->displayTitle;
		}else{
			echo $contentLabel;
		}
		echo '</h1>';
		echo returnMessages();
		
		
		if( isset($this->contentA[$contentLabel]) ){
			echo $this->contentA[$contentLabel];
			
		}elseif( isset($this->contentB[$urlKey]) ){
			echo $this->contentB[$urlKey];
		}
		
		echo '</div>';
		
		return wb::get_clean();
	}
	function getLinks(){} //called by templates


}// end class page

//the values here relate directly to XmlResponse
// developped initially for CLASSmap.php and map editing
class content{
	var $aa = '';
	var $bb = '';
	var $cc = '';
	var $dd = '';
	var $ee;
	var $ff;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
//
//
//		CLASSES FOR DIFFERENT PAGE TYPES
// 			- these are used with $dbObject;
// 			- represents what user sees and inputs
//			- so the edate should be in the user format?? mm/dd/yyyy
//
//		There are a number of functions that are called before the $_SESSION variable is set
//			which means things like $langA, $pageOwner, and isOwner won't work correclty!!
//
//			getStep1()
//			 -> setKeys()
//			 ->	setVariables()
// AA: Base class

class dbPage{
	var $objectType = null; //use this to define 'page', 'comment', etc for use with dbInfo..

	//	required database values
	var $file_id;
	var $owner;
	var $title;
	var $content;
	var $flags;
	var $keywords;
	var $owner_id;

	//var $subject;

	//	tracking database values
	var $username;
	var $ip;
	var $summary;
	var $modified;
	var $posted;
	var $created;
	var $revision;
	var $distanceToCurrent=0;
	var $hitcounter=0;
	var $info; //used for redirect url.. possibly duplicate url later
	var $watching;

	//	for script interaction
	var $editable = true;		//has to default to true
	var $validData = true;		//has to default to true
	var $exists = false;
	var $editLabel;
	var $talkLabel;
	var $includes = array();	//content includes

	//	linking
	var $uniqLink;
	var $uniqStorage;
	var $links = array(); //relative,edit,talk,history,options,help(false to turn off)
	
	var $inLinks = array();

	//	for data handling
	var $dbInfo; //tablenames and query constants

	var $dbValues;		//	Values used by toDB()... used for insert/update
	var $userValues;	//	Values supplied by users
	var $commands = array('append'=>false,'lock'=>true,'hide'=>true,'delete'=>true,'comments'=>true); //determines which options can be modified
	var $pathLen = array('title'=>40); //set maximum lengths for the path components, number of characters
	var $size,$comments;
	var $requirePOST = true;
	var $checkOwner = true;
	var $CanHaveChildren = true;
	var $usingId = false;
	
	function setDbInfo(){
		global $dbInfo;
		$this->dbInfo =& $dbInfo[$this->objectType];
		$this->dbInfo['keys'] = wbData::dbInfo($this->objectType,'keys');
	}

	function setVariables(&$array,$constraint=null){
		if( is_object($array) ){
			$array = get_object_vars($array);
		}
		if( $constraint == null){
			$constraint = get_class_vars(get_class($this));
		}
		foreach($array as $key => $value){
			if( array_key_exists($key,$constraint) ){
				$this->$key = $value;
			}
		}
		return $constraint;
	}
	//sets the object keys based on the request url
	// the keys are generally updated by the database values... 
	// giving them the appropriate cases
	function setKeys(&$pathArray){
		if( empty($pathArray['owner']) ){
			$this->owner = $GLOBALS['wbConfig']['pUser'];
		}else{
			$this->owner = $pathArray['owner'];
		}
		
		if( isset($pathArray['path'][0]) && is_numeric($pathArray['path'][0]) ){
			$this->file_id = $pathArray['path'][0];
		}
		
		$this->title = toStorage( implode('/',$pathArray['path']) );
		if( empty($this->title) ){
			$this->title = $GLOBALS['wbDefaultTitle']; //must keep to some extent
		}
		
	}

	//checks keys
	// used to truncates lengths of keys according to lengths in $pathLen
	function checkKeys(){
		global $page;
		
		$truncated = false;
		foreach($this->pathLen as $key => $value){
			if( !isset($this->$key) ){
				message('INCOMPLETE_PATH');
				$this->validData = false && $this->validData;
				break;
			}
			$temp = trim($this->$key);
			if( empty($temp) ){
				message('INCOMPLETE_PATH');
				$this->validData = false && $this->validData;
				break;
			}
			
			if( ($value > 0) && (wbStrlen($this->$key) > $value) ){
				$this->$key = wbSubstr($this->$key,0,$value);
				$truncated = true;
			}
		}
		if($truncated){
			$page->userCmd = 'nothing';
			$this->setUniqLink();
			message('LONG_PATH');
		}
	}
	
	function getOwner(&$pathArray){
		if( empty($pathArray['owner']) ){
			$this->owner = $GLOBALS['wbConfig']['pUser'];
		}else{
			$this->owner = $pathArray['owner'];
		}
 		
		return $this->owner;
	}

	function getStep1(&$pathArray){
		$this->setKeys($pathArray);
		$this->setFromDB();
	}
	
	//	Database Functions
	//
	//
	function setFromDB(){
		global $wbTables,$page,$langA;
		$this->exists = false;
		//$this->uniqLink =& $this->info;
		
		///	Query
			$query = 'SELECT ';
			$query .= wbData::dbInfo($this->objectType,'querySelect');
			$query .= ', '.wbData::dbInfo($this->objectType,'uniqLink').' as uniqLink ';
			$query .= ', '.$this->dbInfo['dbTable'].'.`file_id` as `file_id` ';
			$query .= ', '.$wbTables['all_files'].'.* ';
			if( !empty($_SESSION['username']) ){
				$query .= ', IF( '.$wbTables['all_watch'].'.`file_id` IS NOT NULL, 1 , 0 ) as `watching` ';
			}
			$query .= ' FROM '.wbData::dbInfo($this->objectType,'queryFrom');
			$query .= ' INNER JOIN '.$wbTables['all_files'];//join to all_files needs to be last!
			$query .= ' ON '.$wbTables['all_files'].'.`file_id` = '.$this->dbInfo['dbTable'].'.`file_id` ';
			if( !empty($_SESSION['username']) ){
				$query .= ' LEFT JOIN '.$wbTables['all_watch'].' ON '.$wbTables['all_watch'].'.`user_id` = "'.wbDB::escape($_SESSION['user_id']).'" ';
				$query .= ' AND '.$wbTables['all_watch'].'.`file_id` = '.$wbTables['all_files'].'.`file_id` ';
			}
			$query .= ' WHERE ';
			$query .= $this->where($this->dbInfo['dbTable']);
			
			$query .= ' LIMIT 1 OFFSET 0';
			
			$result = wbDB::runQuery($query);
			$num = mysql_num_rows($result);
			
		///	New Page
			if($num !== 1){
				$this->setUniqLink();
				return;
			}
			$this->exists = true;
			$row = mysql_fetch_object($result);
			
		///	Redirect
			if( (strpos($row->flags,'redirect') !== false) && !empty($row->info) && empty($page->userCmd)){
				end($this->dbInfo['keys']);
				$key = key($this->dbInfo['keys']);
				
				$link = wbLinks::local($row->uniqLink.'?cmd=showOriginal',toDisplay($this->$key));
				message('REDIRECTED',$link);
				$temp = interpretPath($row->info);
				$this->getStep1($temp);
				//$_GET['redir'] = 'no'; //don't do multiple redirects
				return;
			}
			
		///	Else
			$this->setVariables($row);
			
		///	Set $dbObject->info
		//	$this->info = $this->uniqLink; //should not do this because they could be different if the file was renamed!
			if( empty($this->info) ){
				//message('should fill info '.$this->info . ' and '.$this->uniqLink);
				$this->FillInfo();
			}
			

			$this->uniqStorage = toStorage($this->uniqLink,true);
			$this->modified = dbFromDate($this->modified,9,false);
			
		/// Last Modified .. cannot check here because it could be a blog page!
			$page->setLastModified($this->modified);
			
		/// IF FLAG5.. update all_blog
			if( (strpos($this->flags,'flag5') !== false) ){
				includeFile('tool/BlogContent.php');
				BlogContent::saveFromObject($this);
			}
			
	}
	
	function FillInfo(){
		global $wbTables;
		
		$query = 'UPDATE '.$wbTables['all_files'];
		$query .= ' SET ';
		$query .= ' `modified` = `modified` ';
		$query .= ' , `info` = "'.wbDB::escape($this->uniqLink).'" ';
		$query .= ' WHERE `file_id` = "'.(int)$this->file_id.'" ';
		$result = wbDB::runQuery($query);
		//message($query);
		
	}
	
	// 	I would need a similar function for the historyTable...
	//	except that here I can specify the $table..
	function where($table,$useId=true){ 
		$temp = array();
		
		//if file_id is set
		if( is_numeric($this->file_id) && $useId ){
			return $table.'.`file_id` = "'.(int)$this->file_id.'" ';
		}

		foreach($this->dbInfo['keys'] as $key => $value){
			$temp[$table.'.`'.$key.'`'] = $this->$key; 
		}
		return wbDB::toQuery($temp,'AND',true);
	}
	
	
	//	addHit()
	//	increments the hit counter
	function addHit(){
		if( $this->exists){
			$query = 'UPDATE LOW_PRIORITY '.$this->dbInfo['dbTable'];
			$query .= ' SET `hitcounter`=`hitcounter`+1 WHERE ';
			$query .= ' `file_id`= "'.$this->file_id.'" ';;
			$result = wbDB::runQuery($query);
		}
	}		

	//used by classes like the CLASSteplate to get the data stored on file
	//needed here because this function will be called from toolBatchObject()
	//function setFileLocations(){} // should not be set by all classes!.. functions should check for it's existence
	function setFromDisk(){} 

	//Be careful if you redefine this function for a new object type!
	
	function getStep2(){
		global $page,$langA;
		$this->revision = 'current';
		$doActions = true;
		
		if( !$this->exists ){
			$doActions = $this->newPage();
			
		}elseif( $this->editable ){
			$this->editable = hasPrivilege($this->flags,$this->owner);
		}
		
		if( !$this->editable ){
			$this->editLabel = $langA['view_source'];
		}else{
			$this->editLabel = $langA['edit'];
		}
		
		$this->setLinks();
		
		//update comments
		//!!version 1.6.1
		// the 65535 value is also used by the GuestMaintenance plugin
		if( $this->comments == '65535' ){
			includeFile('tool/FileComments.php');
			fileComments::updateAll($this);
		}
		
		// I like the flexibility of using $this->editable, but unless users change the "Editable" option to something
		//	other than "global" a hidden flag means nothing!
		if( (strpos($this->flags,'deleted') !== false) && ($page->userCmd !== 'restore') ){
			$page->lastModSalts[] = 883638000;
			
			if( $page->userCmd !== 'delete'){
				message('DELETED_FILE');
				$doActions = false;
			}

			
		}elseif( (strpos($this->flags,'hidden') !== false) ){
			$page->lastModSalts[] = 883638005;
			
			if( !isOwner()){
				message('HIDDEN_FILE');
				$doActions = false;
			}else{
				message('HIDDEN_FILE2');
			}
		}elseif( strpos($this->flags,'relvisible') !== false ){
			if( !$this->editable ){
				$doActions = false;
				message('HIDDEN_FILE');
			}else{
				message('HIDDEN_FILE2');
			}
		}
		
		if( $doActions !== false ){
			$this->doActions();
		}
		
		$this->setTalkLabel();
		$page->pageTabs($page->cLinks,$this);
	}

	function doActions(){
		global $page, $pageOwner,$langA;
		
		//In case there's a mistake with $langA, these actions won't be empty
		$langA += array('compare'=>'compare','save'=>'save','preview'=>'preview','changes'=>'changes','save_options'=>'save_options');
		
		//
		//	Unchecked Flag
		//
			if( (strpos($this->flags,'notchecked') !== false) && isOwner(false,true,false) ){
				switch($page->userCmd){
					case 'save':
					case wbStrtolower($langA['save']):
					case 'difference':
					case 'edit':
					case '':
						includeFile('tool/RemoveFlag.php');
						removeFlag::flagChecked($this);
					break;
					
				}
			}
			
		//
		//	Page Actions
		//
			if( !empty($page->userCmd) ){
				
				switch( $page->userCmd ){
					case 'stop':
					case 'donothing':
					return;
					
					case 'children':
						if( $this->CanHaveChildren ){
							includeFile('tool/Children.php');
							return;
						}
					break;
			
					case 'restore':
					case 'delete':
					case wbStrtolower($langA['confirm_delete']):
						includeFile('tool/Delete.php');
					return;
					
					case 'copy':
					case wbStrtolower($langA['copy']):
					case 'rename':
						includeFile('tool/Rename.php');
					return;
					
					case 'changes':
					case wbStrtolower($langA['changes']):
					case 'difference':
					case 'show':
					case 'compare':
					case 'compare versions':
					case 'editrevision':
					case 'revision':
					//case 'revertedit':
					//case 'deleted':
					case wbStrtolower($langA['compare']);
						includeFile('tool/History1.php');
					return;
					case 'revertedit':
						includeFile('tool/History1.php');
					break;
			
					case 'history':
						includeFile('search/History.php');
					return;
			
					//case 'append':
					//case '+':
					case 'save': //maps don't use $langA['save'], save via ajax also uses 'save'
					case wbStrtolower($langA['save']):
						includeFile('tool/SavePage.php');
					return;
			
					case 'viewerrors':
						includeFile('tool/Errors.php');
					return;
					
					case 'append';
					case 'appenddata':
					case 'preview':
					case 'view source':
					case 'edit':
					case wbStrtolower($langA['preview']):
						includeFile('tool/EditPage.php');
						initEdit::init();
					return;
					
					case 'share':
						includeFile('tool/ShareFiles.php');
					return;
					
					case 'unwatch':
					case 'watch':
						includeFile('tool/FileWatch.php');
					break;
			
					case 'unblog';
					case 'blogthis':
					case 'repost':
					case 'options':
					case 'get options':
					case 'defaultoptions':
					case wbStrtolower($langA['save_options']):
						$this->options(true);
					return;
					
					case 'showoriginal': //used by redirect
					default:
						// the counter is incremented after the template is sent, so we increment the query count here
						register_shutdown_function(array(&$this, 'addHit'));
						profile('numQueries',1);
					break;
				}
			}else{
				register_shutdown_function(array(&$this, 'addHit'));
				profile('numQueries',1);
			}

		//
		// 	Redirect Message
		//		
			if( strpos($this->flags,'redirect') !== false){
				if( ($page->userCmd != 'save') && ($page->userCmd != wbStrtolower($langA['save'])) ){
					$link = wbLinks::local($this->info,wbStr_replace('_',' ',$this->info));
					message('REDIRECT_TO',$link);
				}
			}
			
			
		//
		// 	Display
		//
			$field = $this->outputObj();
		
		//
		// 	display the rest
		//
			if( $page->isBlog() ){
				$page->contentB[$field] .= '<p class="WBblogSeparator"><br/> </p>';
				$page->contentB[$field] .= includeBlog();
			}else{
				$this->includeComments($field);
			}
			
			
			$this->addFooter($field);
	}

	function setLinks($edit=true){
		global $langA,$page;
		
		
		$key = end(array_flip($this->dbInfo['keys']));
		$label = $this->{$key};
		$temp = explode('/',$label);
		
		$link = substr($this->uniqLink,0,-strlen($label));
		//$page->prevTab = toStorageUrl($this->uniqLink);
		
		
		foreach($temp as $i => $value){
			if( empty($value) ){
				continue;
			}
			$link = $link.$value;
			$page->regLink($this->getOutputField($value),$link);
			
			if( ($i+1) != count($temp)){
				$page->regLink('>');
				$link .= '/';
			}
		}
		
		if( !$page->interRequest ){
			if( $edit === true ){
				$page->regLink($this->editLabel,'/Edit'.$this->uniqLink.'?cmd=edit');
			}elseif( $edit ){
				$page->regLink($this->editLabel,$edit);
			}
		}
		
		//because using the uniqLink wouldn't be right for a comment
		if( $this->commands['comments'] ){
			//$page->regLink($this->talkLabel,'/Talk'.$this->uniqLink);
			$page->regLink($langA['talk'],'/Talk'.$this->uniqLink);
		}
		
		if( $this->exists ){
			
			if( !$page->interRequest ){
				$page->regLink($langA['history'],'/Edit'.$this->uniqLink.'?cmd=history');
			}
			$page->regLink($langA['diff'],'/Edit'.$this->uniqLink.'?cmd=difference');
			
			if( !empty($_SESSION['username']) ){
				$page->regLink($langA['watch'],'/Edit'.$this->uniqLink.'?cmd=watch');
				$page->regLink($langA['unwatch'],'/Edit'.$this->uniqLink.'?cmd=unwatch');
			}
		}
		
		if( !$page->interRequest ){
			$page->regLink($langA['options'],'/Edit'.$this->uniqLink.'?cmd=options');
		}
	}

	
	function setTalkLabel(){
		global $dbInfo,$wbTables,$langA;
		if( $this->comments > 0 ){
			$this->talkLabel = $langA['talk'].' ('.$this->comments.')';
		}else{
			$this->talkLabel = $langA['talk'];
		}
	}
	
	function includeComments($field){
		global $langA, $page, $wbTables;
		
		
		if( isset($this->commands['comments']) && ($this->commands['comments'] == false) ){
			return;
		}
		if( strpos($this->flags,'nocomments') !== false){
			return;
		}
		if( strpos($this->flags,'flag4') === false){
			return;
		}
		
		$prefix = '';
		if( $this->objectType !== 'page' ){
			$prefix = $this->objectType.'/';
		}
		
		ob_start();
		echo '<div style="margin:1em 0 1em 0;" class="recentCommentsList">';
		echo '<h2>'.$langA['comments'].'</h2>';
		
		$query = 'SELECT ';
		$query .= wbData::dbInfo('comment','uniqLink').' as `uniqLink` ';
		$query .= ', `subject` ';
		// $query .= ', '.wbData::dbInfo('comment','querySelect');
		// $query .= ', `modified`, `created`, `flags` ';
		$query .= ' FROM ';
		$query .= wbData::dbInfo('comment','queryFrom');
		$query .= ' INNER JOIN ';
		$query .= $wbTables['all_files']; //join to all_files needs to be last in case 'queryFrom' has more than one table
		$query .= ' ON '.$wbTables['all_files'].'.`file_id` = '.wbData::dbInfo('comment','dbTable').'.`file_id` ';
		$query .= ' WHERE ';
		$query .= ' `owner` = "'.wbDB::escape($this->owner).'" ';
		$query .= ' AND `title` = "'.wbDB::escape($this->title).'" ';
		$query .= ' AND `prefix` = "'.wbDB::escape($prefix).'" ';
		$query .= ' AND '.$wbTables['all_files'].'.`visible` = 1 ';
		$query .= ' AND !FIND_IN_SET("redirect", '.$wbTables['all_files'].'.`flags`) ';		
		$query .= ' LIMIT 5 OFFSET 0 ';
		$result = wbDB::runQuery($query);
		if( mysql_num_rows($result) < 1 ){
			$this->talkLabel = $langA['talk'];
			
		}else{
		
			echo '<ul>';
			while($row = mysql_fetch_assoc($result) ){
				echo '<li>';
				echo wbLinks::local($row['uniqLink'],toDisplay($row['subject']));
				echo '</li>';
			}
			echo '</ul>';
		}
		
		echo '<span>';
			if( $this->comments > 5 ){
				echo wbLinks::local('/Talk'.$this->uniqLink,'read_more');
				echo ' - ';
			}
			echo wbLinks::local('/Talk'.$this->uniqLink,$langA['talk']);
		echo '</span>';
		echo '</div>';
		
		$page->contentB[$field] .= wb::get_clean();
	}
	
	function setFromPost(){
		globalFromPost();
		$this->setVariables( $_POST , $this->userValues );
	}
	
	// Sets $this->validData, $this->flags = 'safe'
	//
	function checkData($warn=true){
		global $wbParser,$langA;
		initiateParser();
		

		if( '' == trim($this->content) ){
			message('EMPTY_CONTENT');
			$this->validData = false;
			return;
		}
		
		$wbParser->parse($this->content,true,$this);
		$this->getParserInfo();
		if( $wbParser && $wbParser->errors && $warn ){
			$img = '<img src="'.wbLinks::getDir('/imgs/icons/bullet_error.gif').'" border="0" width="16" height="16" style="vertical-align:middle" alt="'.$langA['syntax_warning'].'" /> ';
			$link = wbLinks::local('/Edit'.$this->uniqLink.'?cmd=viewErrors',false);
			message($img.wbLang::text('SYNTAX_WARNING',$link));
		}
	}
	
	function getParserInfo(){
		global $wbParser;
		if( !is_object($wbParser) ){
			return;
		}
		
		//
		//	Safely Parsed
		//		
			$this->isUnSafe($wbParser->foundUnsafe);
			
		//
		//	Parsed with Errors
		//	
		
			if( $wbParser->foundError ){
				$this->flags .= ',flag3';
			}else{
				$this->flags = str_replace('flag3','',$this->flags);
			}
	}
	
	
	//send	false	safe
	//		true	not safe
	//flags will get cleaned later
	function isUnSafe($bool){
		if( $bool ){
			$this->flags = str_replace('safe','',$this->flags);
		}else{
			$this->flags .= ',safe';
			return;
		}
		
		if( isAdmin(false)
			&& (strpos($this->flags,'admin') === false)
			){
				$link = wbLinks::local($this->uniqLink.'?cmd=options',false);
				message('MAKE_ADMIN',$link);
		}	
		
	}
	

	///////////////////////////////////////
	//
	//		Exporting Values
	
	// reduces object/array to array with values corresponding to keys of the constraint.
	// pass by reference?
	function onlyTheseKeys($constraint){
		$temp = array();
		foreach($constraint as $key => $value){
			$temp[$key] = $this->$key;
		}
		return $temp;
	}

	//	Used for rename only at the moment
	function keyArray(){
		return $this->onlyTheseKeys($this->dbInfo['keys']);
	}

	//	For saving values to database
	function toDB(){
		$temp = $this->onlyTheseKeys($this->dbValues);
		$temp['summary'] = wbHtmlspecialchars($_POST['summary']);
		return $temp;
	}
	
// 	//	For comparing versions
// 	//	no longer used by /tool/SavePage.php
// 	function toUserValues(){
// 		return $this->onlyTheseKeys($this->userValues);
// 	}
	
	//used by CLASStemplate
	function translateToUser($text){
		return $text;
	}
	
	function editValues(){
		global $page,$jsNum;
		$page->css2 = true;
		$page->scripts[] = '/include/js/editing.js?'.$jsNum;
		$page->scripts[] = '/include/js/editingAll.js?'.$jsNum;
		
		$uri = '/Edit'.$this->uniqLink.'?cmd=edit';
		$page->contentB[$uri] = toEditArea($this->content,1,'content');
		$page->contentB[$uri] .= saveBar();
		return $uri;
	}
	
	function newPage(){
		global $pageOwner,$dbInfo;

		if( is_numeric($this->file_id) ){
			message('INCOMPLETE_PATH');
			$this->validData = false;
			return;
		}
		
		$this->checkKeys();
		
		//see SPECdefaultOptions and wiki.php
		if( isset($pageOwner['flags'][$this->dbInfo['class']]) ){
			$this->flags = $pageOwner['flags'][$this->dbInfo['class']]; 
		}
		$this->flags .= ',default';
		
		if( $this->editable ){
			$this->editable = hasPrivilege($this->flags,$this->owner);
		}
	}
	
	//a generic function for setting the uniqLink when it's a new page
	//only needed by newPage() and toolBatchObject.php
	function setUniqLink(){
		if( $this->objectType === 'page'){
			$this->uniqLink = '';	//very important for version conflict detection
		}else{
			$this->uniqLink = '/'.toDisplay($this->objectType);
		}
		
		end($this->dbInfo['keys']);
		$lastKey = key($this->dbInfo['keys']);

		foreach($this->dbInfo['keys'] as $key => $temp){
			if( empty($this->$key) ){
				continue;
			}
			$this->uniqLink .= '/'.$this->$key;
		}
		$this->uniqStorage = toStorage($this->uniqLink,true);
		$this->checkLink();
	}

		
	//
	// Check the url
	//
	function checkLink(){
		if( !$this->validData ){
			return;
		}
		$chars = array('<','>','%3e','%3c','&lt;','&gt;');
		foreach($chars as $char){
			if( strpos($this->uniqStorage,$char) !== false ){
				message(wbLang::text('INVALID_LINK'));
				$this->validData = false;
				return;
			}
		}
	}



	//	Get the field
	//		true|false|string
	function getOutputField($field=true){
		
		if( $field === true ){
			$keys = array_flip($this->dbInfo['keys']);
			$field = $this->{end($keys)};
			$pos = strrpos($field,'/');
			if( $pos !== false){
				$field = substr($field,$pos+1);
			}
		}
		return toDisplay($field);
	}
	
	function abbrevOutputHeader(&$row){
		global $langA;
		$links = array();
		$links[$langA['file']] = $row->uniqLink;
		
		if( strpos($row->flags,'nocomments') !== false ){
			$links[$langA['talk']] = '/Talk'.$links[$langA['file']];
		}
		
		echo '<h2 class="heading underline">';
		echo wbLinks::local($row->uniqLink, toDisplay($row->title) );
		echo '</h2>';
		echo ' <div class="fileInfo">';
			echo '<span>';
			$temp = Array();
			foreach( $links as $key => $value){
				if( is_string($value) ){
					$temp[] = wbLinks::local($value,$key);
				}
			}
			echo implode(' :: ',$temp);
			echo '</span>';
			
			echo '<span>'.$langA['modified'].': '.dbFromDate($row->modified,3).'</span>';
			echo '<span>'.$langA['created'].': '. dbFromDate($row->created,3).'</span>';
		echo ' </div>';
	}
	


	function outputObj($link=true){
		global $page,$wbParser;
		
		$page->metaDescription = $page->displayTitle.'.';
		initiateParser();
		
		if( $link === true ){
			$link = $this->uniqLink;
		}
		
		
		$page->contentB[$link] = $wbParser->justParse($this->content,$this->flags,$this->file_id);
		return $link;
	}
	
	function addFooter($field){
		global $page,$langA,$pageOwner;
		if( empty($field) ){
			return;
		}
		
		$footer = array();
		
		//Keywords
		if( !empty($this->keywords) ){
			$footer[] = showKeywords($this->keywords,$this->owner);
		}
		
		//Last Modified
		if( !empty($this->modified) ){
			if( empty($this->username) ){
				$user =& $this->ip;
			}else{
				$user = wbLinks::user($this->username);
				if( isset($_SESSION['username']) && $this->username != $_SESSION['username']){
					$user .= wbLinks::special('Permissions?guest='.$this->username,'+/-','title="'.$langA['SET_USER_PERMISSIONS'].$this->username.'"',$_SESSION['username']);
					//shouldn't wrap the +/- links in a span, otherwise the css won't be quite right
				}

			}
			$footer[] = wbLang::text('LAST_MODIFIED',dbFromDate($this->modified,7),$user);
		}
		if( !empty($this->hitcounter) ){
			$footer[] = wbLang::text('accessed_times',number_format($this->hitcounter));
		}
		
		if( $this->CanHaveChildren ){
			$footer[] = wbLinks::local('/Edit'.$this->uniqLink.'?cmd=children','Children');
		}
		
		$footer[] = wbLinks::special('WhatLinksHere?to='.rawurlencode($this->uniqLink),'what_links_here');
		
		
		if( !isset($pageOwner['share']) || $pageOwner['share'] == 'On'){
			$i = '<img src="'.wbLinks::getDir('/imgs/social/shareAll.gif').'" border="0" width="16" height="16" style="vertical-align:middle" alt="share" /> ';
			$footer[] = wbLinks::local('/Edit'.$this->uniqLink.'?cmd=share',$i.$langA['share']);
		}
		
		//License
		if( isset($pageOwner['licenseTxt']) ){
			$footer[] = $pageOwner['licenseTxt'];
		}
		
        if( isset($pageOwner['pageFooters']) && is_array($pageOwner['pageFooters']) ){
            $footer = array_merge( (array)$footer, (array)$pageOwner['pageFooters']);
        }		
		
		
		if( count($footer) > 0){
			$t =& $page->contentB[$field];
			$t .= '<div class="WBfileFooter"><span>';
			$t .= implode('</span> <span>',$footer);
			$t .= '</span></div>';
		}
		
	}//end addFooter()

	
	
	/* static */
	function options($initiate=true){
		includeFile('tool/Options.php',$initiate);
	}
	
	/* 
	// Not required, but can be used
	function afterSave(){}
	*/

}


//	A: Normal Page
class CLASSpage extends dbPage{
	var $objectType = 'page'; //this is used to select the correct array from $dbInfo

	var $dbValues = array('owner'=>1,'title'=>1,'content'=>1);
	var $userValues = array('content'=>1,'keywords'=>1);

	function CLASSpage(){
		$this->setDbInfo();
	}

	function newPage(){
		global $page, $langA,$pageOwner;
		parent::newPage();

		//	STANDARD EDIT MESSAGE
		if( $this->editable ){
			$this->content = wbLang::text('DEFAULT_CONTENT',$this->uniqLink);
			if( empty($page->userCmd) && cookies() ){
				$page->userCmd = 'edit';
			}
		}else{
			$this->content = $langA['DEFAULT_CONTENT_PROTECTED'];
		}
		$temp = strtolower($this->title);
		if( strpos($temp,'template:') === 0){
			if( empty($this->flags) ){
				$this->flags = 'template';
			}else{
				$this->flags .= ',template';
			}
		}
	}

	function setLinks(){
		global $page,$pageOwner;
		
		parent::setLinks();
		$page->displayTitle = toDisplay($this->title);
		$page->regLink('?','Pages?en=Pages');
		
		if( !empty($pageOwner['homeTitle']) && wbStrtolower($this->title) == wbStrtolower($GLOBALS['wbDefaultTitle']) ){
			$page->displayTitle = $pageOwner['homeTitle'];
		}
		
	}

	//		Trying to use something besides "Home"
	//	
	function getOutputField($field=true){
		global $pageOwner;
		$field = parent::getOutputField($field);
		
		if( !empty($pageOwner['homeTitle']) && wbStrtolower($field) == wbStrtolower($GLOBALS['wbDefaultTitle'])){
			return parent::getOutputField($pageOwner['homeTitle']);
		}
		return $field;
	}
	
	function abbrevOutput(&$row){
		global $langA,$page,$pageOwner;
		
		parent::abbrevOutputHeader($row);
		CLASSpage::abbrevOutputContent($row);
	}
	
	function abbrevOutputContent(&$row){
		global $langA,$page,$pageOwner;
		
		$length = 400;
		if( !empty($pageOwner['blen']) && is_numeric($pageOwner['blen']) ){
			$length = $pageOwner['blen'];
		}
		
		echo '<div class="desc">';
			$bool = outputWiki($row->content,$length,$row->flags);
			if($bool == true){
				echo '... '.wbLinks::local($row->uniqLink,$langA['read_more']).' <span class="sm">('.$bool.$langA['words'].')</span>';
			}
		echo '</div>';
		echo '<br />';
	}	
	
}

//
//				CLASSES
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//				<---	End of File
//
// 				LINE #	1265 as of 2005-April-24
// 						1408 as of 2005-May-7
// 						1500 as of 2005-May-22
// 						1805 as of 2005-Aug-15
// 						2424 as of 2005-Aug-18
// 						2605 as of 2005-Sep-12 ...ooh getting smaller
//						2214 as of 2005-Sep-19
//						1847 as of 2005-Dec-30
//						2062 as of 2006-Feb-10
//						2155 as of 2006-Mar-9
//						2401 as of 2006-Apr-4
//						2645 as of 2006-May-26
//						2766 as of 2006-Jun-07
//						2884 as of 2006-Aug-02
//						3452 as of 2007-Aug-13
//						3581 as of 2008-Jan-29
