includes/pageprocess.php
changeset 1227 bdac73ed481e
parent 1216 4125e19d3b27
child 1252 e34c23a35dc9
equal deleted inserted replaced
1226:de56132c008d 1227:bdac73ed481e
    21  * @license GNU General Public License <http://www.gnu.org/licenses/gpl-2.0.html>
    21  * @license GNU General Public License <http://www.gnu.org/licenses/gpl-2.0.html>
    22  */
    22  */
    23 
    23 
    24 class PageProcessor
    24 class PageProcessor
    25 {
    25 {
    26   
    26 	
    27   /**
    27 	/**
    28    * Page ID and namespace of the page handled by this instance
    28  	* Page ID and namespace of the page handled by this instance
    29    * @var string
    29  	* @var string
    30    */
    30  	*/
    31   
    31 	
    32   var $page_id;
    32 	var $page_id;
    33   var $namespace;
    33 	var $namespace;
    34   
    34 	
    35   /**
    35 	/**
    36    * The instance of the namespace processor for the namespace we're doing.
    36  	* The instance of the namespace processor for the namespace we're doing.
    37    * @var object
    37  	* @var object
    38    */
    38  	*/
    39   
    39 	
    40   var $ns;
    40 	var $ns;
    41   
    41 	
    42   /**
    42 	/**
    43    * The title of the page sent to the template parser
    43  	* The title of the page sent to the template parser
    44    * @var string
    44  	* @var string
    45    */
    45  	*/
    46   
    46 	
    47   var $title = '';
    47 	var $title = '';
    48   
    48 	
    49   /**
    49 	/**
    50    * The information about the page(s) we were redirected from
    50  	* The information about the page(s) we were redirected from
    51    * @var array
    51  	* @var array
    52    */
    52  	*/
    53   
    53 	
    54   var $redirect_stack = array();
    54 	var $redirect_stack = array();
    55   
    55 	
    56   /**
    56 	/**
    57    * The revision ID (history entry) to send. If set to 0 (the default) then the most recent revision will be sent.
    57  	* The revision ID (history entry) to send. If set to 0 (the default) then the most recent revision will be sent.
    58    * @var int
    58  	* @var int
    59    */
    59  	*/
    60   
    60 	
    61   var $revision_id = 0;
    61 	var $revision_id = 0;
    62   
    62 	
    63   /**
    63 	/**
    64    * The time this revision was saved, as a UNIX timestamp
    64  	* The time this revision was saved, as a UNIX timestamp
    65    * @var int
    65  	* @var int
    66    */
    66  	*/
    67   
    67 	
    68   var $revision_time = 0;
    68 	var $revision_time = 0;
    69   
    69 	
    70   /**
    70 	/**
    71    * Unsanitized page ID.
    71  	* Unsanitized page ID.
    72    * @var string
    72  	* @var string
    73    */
    73  	*/
    74   
    74 	
    75   var $page_id_unclean;
    75 	var $page_id_unclean;
    76   
    76 	
    77   /**
    77 	/**
    78    * Tracks if the page we're loading exists in the database or not.
    78  	* Tracks if the page we're loading exists in the database or not.
    79    * @var bool
    79  	* @var bool
    80    */
    80  	*/
    81   
    81 	
    82   var $page_exists = false;
    82 	var $page_exists = false;
    83   
    83 	
    84   /**
    84 	/**
    85    * Permissions!
    85  	* Permissions!
    86    * @var object
    86  	* @var object
    87    */
    87  	*/
    88   
    88 	
    89   var $perms = null;
    89 	var $perms = null;
    90   
    90 	
    91   /**
    91 	/**
    92    * The SHA1 hash of the user-inputted password for the page
    92  	* The SHA1 hash of the user-inputted password for the page
    93    * @var string
    93  	* @var string
    94    */
    94  	*/
    95    
    95  	
    96   var $password = '';
    96 	var $password = '';
    97   
    97 	
    98   /**
    98 	/**
    99    * Switch to track if redirects are allowed. Defaults to true.
    99  	* Switch to track if redirects are allowed. Defaults to true.
   100    * @var bool
   100  	* @var bool
   101    */
   101  	*/
   102   
   102 	
   103   var $allow_redir = true;
   103 	var $allow_redir = true;
   104   
   104 	
   105   /**
   105 	/**
   106    * Holds any error message from redirection code. Defaults to false (no error).
   106  	* Holds any error message from redirection code. Defaults to false (no error).
   107    * @var mixed
   107  	* @var mixed
   108    */
   108  	*/
   109    
   109  	
   110   var $redir_error = false;
   110 	var $redir_error = false;
   111   
   111 	
   112   /**
   112 	/**
   113    * If this is set to true, this will call the header and footer funcs on $template when render() is called.
   113  	* If this is set to true, this will call the header and footer funcs on $template when render() is called.
   114    * @var bool
   114  	* @var bool
   115    */
   115  	*/
   116   
   116 	
   117   var $send_headers = false;
   117 	var $send_headers = false;
   118   
   118 	
   119   /**
   119 	/**
   120    * Cache the fetched text so we don't fetch it from the DB twice.
   120  	* Cache the fetched text so we don't fetch it from the DB twice.
   121    * @var string
   121  	* @var string
   122    */
   122  	*/
   123   
   123 	
   124   var $text_cache = '';
   124 	var $text_cache = '';
   125   
   125 	
   126   /**
   126 	/**
   127    * Debugging information to track errors. You can set enable to false to disable sending debug information.
   127  	* Debugging information to track errors. You can set enable to false to disable sending debug information.
   128    * @var array
   128  	* @var array
   129    */
   129  	*/
   130   
   130 	
   131   var $debug = array(
   131 	var $debug = array(
   132       'enable' => false,
   132 			'enable' => false,
   133       'works'  => false
   133 			'works'  => false
   134     );
   134 		);
   135   
   135 	
   136   /**
   136 	/**
   137    * The list of errors raised in the class.
   137  	* The list of errors raised in the class.
   138    * @var array
   138  	* @var array
   139    */
   139  	*/
   140   
   140 	
   141   var $_errors = array();
   141 	var $_errors = array();
   142   
   142 	
   143   /**
   143 	/**
   144    * Constructor.
   144  	* Constructor.
   145    * @param string The page ID (urlname) of the page
   145  	* @param string The page ID (urlname) of the page
   146    * @param string The namespace of the page
   146  	* @param string The namespace of the page
   147    * @param int Optional. The revision ID to send.
   147  	* @param int Optional. The revision ID to send.
   148    */
   148  	*/
   149   
   149 	
   150   function __construct( $page_id, $namespace, $revision_id = 0 )
   150 	function __construct( $page_id, $namespace, $revision_id = 0 )
   151   {
   151 	{
   152     global $db, $session, $paths, $template, $plugins; // Common objects
   152 		global $db, $session, $paths, $template, $plugins; // Common objects
   153     
   153 		
   154     profiler_log("PageProcessor [{$namespace}:{$page_id}]: Started constructor");
   154 		profiler_log("PageProcessor [{$namespace}:{$page_id}]: Started constructor");
   155     
   155 		
   156     // See if we can get some debug info
   156 		// See if we can get some debug info
   157     if ( function_exists('debug_backtrace') && $this->debug['enable'] )
   157 		if ( function_exists('debug_backtrace') && $this->debug['enable'] )
   158     {
   158 		{
   159       $this->debug['works'] = true;
   159 			$this->debug['works'] = true;
   160       $this->debug['backtrace'] = enano_debug_print_backtrace(true);
   160 			$this->debug['backtrace'] = enano_debug_print_backtrace(true);
   161     }
   161 		}
   162     
   162 		
   163     // First things first - check page existence and permissions
   163 		// First things first - check page existence and permissions
   164     
   164 		
   165     if ( !isset($paths->nslist[$namespace]) )
   165 		if ( !isset($paths->nslist[$namespace]) )
   166     {
   166 		{
   167       $this->send_error('The namespace "' . htmlspecialchars($namespace) . '" does not exist.');
   167 			$this->send_error('The namespace "' . htmlspecialchars($namespace) . '" does not exist.');
   168     }
   168 		}
   169     
   169 		
   170     if ( !is_int($revision_id) )
   170 		if ( !is_int($revision_id) )
   171       $revision_id = 0;
   171 			$revision_id = 0;
   172     
   172 		
   173     $this->_setup( $page_id, $namespace, $revision_id );
   173 		$this->_setup( $page_id, $namespace, $revision_id );
   174   }
   174 	}
   175   
   175 	
   176   /**
   176 	/**
   177    * The main method to send the page content. Also responsible for checking permissions and calling the statistics counter.
   177  	* The main method to send the page content. Also responsible for checking permissions and calling the statistics counter.
   178    * @param bool If true, the stat counter is called. Defaults to false.
   178  	* @param bool If true, the stat counter is called. Defaults to false.
   179    */
   179  	*/
   180   
   180 	
   181   function send( $do_stats = false )
   181 	function send( $do_stats = false )
   182   {
   182 	{
   183     global $db, $session, $paths, $template, $plugins; // Common objects
   183 		global $db, $session, $paths, $template, $plugins; // Common objects
   184     global $lang, $output;
   184 		global $lang, $output;
   185     
   185 		
   186     profiler_log('PageProcessor: send() called');
   186 		profiler_log('PageProcessor: send() called');
   187     
   187 		
   188     if ( !$this->perms->get_permissions('read') )
   188 		if ( !$this->perms->get_permissions('read') )
   189     {
   189 		{
   190       // Permission denied to read page. Is this one of our core pages that must always be allowed?
   190 			// Permission denied to read page. Is this one of our core pages that must always be allowed?
   191       // NOTE: Not even the administration panel will work if ACLs deny access to it.
   191 			// NOTE: Not even the administration panel will work if ACLs deny access to it.
   192       if ( $this->namespace == 'Special' && in_array($this->page_id, array('Login', 'Logout', 'LangExportJSON', 'CSS')) )
   192 			if ( $this->namespace == 'Special' && in_array($this->page_id, array('Login', 'Logout', 'LangExportJSON', 'CSS')) )
   193       {
   193 			{
   194         // Do nothing; allow execution to continue
   194 				// Do nothing; allow execution to continue
   195       }
   195 			}
   196       else
   196 			else
   197       {
   197 			{
   198         // Page isn't whitelisted, behave as normal
   198 				// Page isn't whitelisted, behave as normal
   199         $this->err_access_denied();
   199 				$this->err_access_denied();
   200         return false;
   200 				return false;
   201       }
   201 			}
   202     }
   202 		}
   203     if ( $this->revision_id > 0 && !$this->perms->get_permissions('history_view') )
   203 		if ( $this->revision_id > 0 && !$this->perms->get_permissions('history_view') )
   204     {
   204 		{
   205       $this->err_access_denied();
   205 			$this->err_access_denied();
   206       return false;
   206 			return false;
   207     }
   207 		}
   208     
   208 		
   209     // Is there a custom function registered for handling this namespace?
   209 		// Is there a custom function registered for handling this namespace?
   210     // DEPRECATED (even though it only saw its way into one alpha release.)
   210 		// DEPRECATED (even though it only saw its way into one alpha release.)
   211     if ( $proc = $paths->get_namespace_processor($this->namespace) )
   211 		if ( $proc = $paths->get_namespace_processor($this->namespace) )
   212     {
   212 		{
   213       // yes, just call that
   213 			// yes, just call that
   214       // this is protected aggressively by the PathManager against overriding critical namespaces
   214 			// this is protected aggressively by the PathManager against overriding critical namespaces
   215       return call_user_func($proc, $this);
   215 			return call_user_func($proc, $this);
   216     }
   216 		}
   217     
   217 		
   218     $pathskey = $paths->nslist[ $this->namespace ] . $this->page_id;
   218 		$pathskey = $paths->nslist[ $this->namespace ] . $this->page_id;
   219     $strict_no_headers = false;
   219 		$strict_no_headers = false;
   220     $admin_fail = false;
   220 		$admin_fail = false;
   221     if ( $this->namespace == 'Admin' && strstr($this->page_id, '/') )
   221 		if ( $this->namespace == 'Admin' && strstr($this->page_id, '/') )
   222     {
   222 		{
   223       $this->page_id = substr($this->page_id, 0, strpos($this->page_id, '/'));
   223 			$this->page_id = substr($this->page_id, 0, strpos($this->page_id, '/'));
   224       $funcname = "page_{$this->namespace}_{$this->page_id}";
   224 			$funcname = "page_{$this->namespace}_{$this->page_id}";
   225       if ( function_exists($funcname) )
   225 			if ( function_exists($funcname) )
   226       {
   226 			{
   227         $this->page_exists = true;
   227 				$this->page_exists = true;
   228       }
   228 			}
   229     }
   229 		}
   230     if ( isPage($pathskey) )
   230 		if ( isPage($pathskey) )
   231     {
   231 		{
   232       $cdata = $this->ns->get_cdata();
   232 			$cdata = $this->ns->get_cdata();
   233       
   233 			
   234       if ( $cdata['special'] == 1 )
   234 			if ( $cdata['special'] == 1 )
   235       {
   235 			{
   236         $this->send_headers = false;
   236 				$this->send_headers = false;
   237         $strict_no_headers = true;
   237 				$strict_no_headers = true;
   238         $GLOBALS['output'] = new Output_Naked();
   238 				$GLOBALS['output'] = new Output_Naked();
   239       }
   239 			}
   240       if ( isset($cdata['password']) )
   240 			if ( isset($cdata['password']) )
   241       {
   241 			{
   242         if ( $cdata['password'] != '' && $cdata['password'] != sha1('') )
   242 				if ( $cdata['password'] != '' && $cdata['password'] != sha1('') )
   243         {
   243 				{
   244           $password =& $cdata['password'];
   244 					$password =& $cdata['password'];
   245           if ( $this->password != $password )
   245 					if ( $this->password != $password )
   246           {
   246 					{
   247             $this->err_wrong_password();
   247 						$this->err_wrong_password();
   248             return false;
   248 						return false;
   249           }
   249 					}
   250         }
   250 				}
   251       }
   251 			}
   252       if ( isset($cdata['require_admin']) && $cdata['require_admin'] )
   252 			if ( isset($cdata['require_admin']) && $cdata['require_admin'] )
   253       {
   253 			{
   254         if ( $session->auth_level < USER_LEVEL_ADMIN )
   254 				if ( $session->auth_level < USER_LEVEL_ADMIN )
   255         {
   255 				{
   256           $admin_fail = true;
   256 					$admin_fail = true;
   257         }
   257 				}
   258       }
   258 			}
   259     }
   259 		}
   260     else if ( $this->namespace === $paths->namespace && $this->page_id == $paths->page_id )
   260 		else if ( $this->namespace === $paths->namespace && $this->page_id == $paths->page_id )
   261     {
   261 		{
   262       if ( isset($paths->cpage['require_admin']) && $paths->cpage['require_admin'] )
   262 			if ( isset($paths->cpage['require_admin']) && $paths->cpage['require_admin'] )
   263       {
   263 			{
   264         if ( $session->auth_level < USER_LEVEL_ADMIN )
   264 				if ( $session->auth_level < USER_LEVEL_ADMIN )
   265         {
   265 				{
   266           $admin_fail = true;
   266 					$admin_fail = true;
   267         }
   267 				}
   268       }
   268 			}
   269     }
   269 		}
   270     if ( $admin_fail )
   270 		if ( $admin_fail )
   271     {
   271 		{
   272       header('Content-type: text/javascript');
   272 			header('Content-type: text/javascript');
   273       echo enano_json_encode(array(
   273 			echo enano_json_encode(array(
   274           'mode' => 'error',
   274 					'mode' => 'error',
   275           'error' => 'need_auth_to_admin'
   275 					'error' => 'need_auth_to_admin'
   276         ));
   276 				));
   277       return true;
   277 			return true;
   278     }
   278 		}
   279     if ( $this->page_exists && $this->namespace != 'Special' && $this->namespace != 'Admin' && $do_stats )
   279 		if ( $this->page_exists && $this->namespace != 'Special' && $this->namespace != 'Admin' && $do_stats )
   280     {
   280 		{
   281       require_once(ENANO_ROOT.'/includes/stats.php');
   281 			require_once(ENANO_ROOT.'/includes/stats.php');
   282       doStats($this->page_id, $this->namespace);
   282 			doStats($this->page_id, $this->namespace);
   283     }
   283 		}
   284     
   284 		
   285     // We are all done. Ship off the page.
   285 		// We are all done. Ship off the page.
   286     
   286 		
   287     if ( !$this->allow_redir )
   287 		if ( !$this->allow_redir )
   288     {
   288 		{
   289       if ( method_exists($this->ns, 'get_redirect') )
   289 			if ( method_exists($this->ns, 'get_redirect') )
   290       {
   290 			{
   291         if ( $result = $this->ns->get_redirect() )
   291 				if ( $result = $this->ns->get_redirect() )
   292           display_redirect_notice($result['page_id'], $result['namespace']);
   292 					display_redirect_notice($result['page_id'], $result['namespace']);
   293       }
   293 			}
   294     }
   294 		}
   295     else
   295 		else
   296     {
   296 		{
   297       $this->process_redirects();
   297 			$this->process_redirects();
   298       
   298 			
   299       if ( count($this->redirect_stack) > 0 )
   299 			if ( count($this->redirect_stack) > 0 )
   300       {
   300 			{
   301         $stack = array_reverse($this->redirect_stack);
   301 				$stack = array_reverse($this->redirect_stack);
   302         foreach ( $stack as $stackel )
   302 				foreach ( $stack as $stackel )
   303         {
   303 				{
   304           $url = makeUrlNS($stackel['old_namespace'], $stackel['old_page_id'], 'redirect=no', true);
   304 					$url = makeUrlNS($stackel['old_namespace'], $stackel['old_page_id'], 'redirect=no', true);
   305           $page_data = $this->ns->get_cdata();
   305 					$page_data = $this->ns->get_cdata();
   306           $title = $stackel['old_title'];
   306 					$title = $stackel['old_title'];
   307           $a = '<a href="' . $url . '">' . htmlspecialchars($title) . '</a>';
   307 					$a = '<a href="' . $url . '">' . htmlspecialchars($title) . '</a>';
   308           $output->add_after_header('<small>' . $lang->get('page_msg_redirected_from', array('from' => $a)) . '<br /></small>');
   308 					$output->add_after_header('<small>' . $lang->get('page_msg_redirected_from', array('from' => $a)) . '<br /></small>');
   309         }
   309 				}
   310         $template->set_page($this);
   310 				$template->set_page($this);
   311       }
   311 			}
   312       
   312 			
   313       if ( $this->redir_error )
   313 			if ( $this->redir_error )
   314       {
   314 			{
   315         $output->add_after_header('<div class="usermessage"><b>' . $this->redir_error . '</b></div>');
   315 				$output->add_after_header('<div class="usermessage"><b>' . $this->redir_error . '</b></div>');
   316         $result = $this->ns->get_redirect();
   316 				$result = $this->ns->get_redirect();
   317         display_redirect_notice($result['page_id'], $result['namespace']);
   317 				display_redirect_notice($result['page_id'], $result['namespace']);
   318       }
   318 			}
   319     }
   319 		}
   320     
   320 		
   321     $this->ns->send();
   321 		$this->ns->send();
   322   }
   322 	}
   323   
   323 	
   324   /**
   324 	/**
   325    * Sends the page through by fetching it from the database.
   325  	* Sends the page through by fetching it from the database.
   326    */
   326  	*/
   327    
   327  	
   328   function send_from_db($strict_no_headers = false)
   328 	function send_from_db($strict_no_headers = false)
   329   {
   329 	{
   330     global $db, $session, $paths, $template, $plugins; // Common objects
   330 		global $db, $session, $paths, $template, $plugins; // Common objects
   331     global $lang;
   331 		global $lang;
   332     
   332 		
   333     $this->ns->send_from_db();
   333 		$this->ns->send_from_db();
   334   }
   334 	}
   335   
   335 	
   336   /**
   336 	/**
   337    * Fetches the wikitext or HTML source for the page.
   337  	* Fetches the wikitext or HTML source for the page.
   338    * @return string
   338  	* @return string
   339    */
   339  	*/
   340   
   340 	
   341   function fetch_source()
   341 	function fetch_source()
   342   {
   342 	{
   343     global $db, $session, $paths, $template, $plugins; // Common objects
   343 		global $db, $session, $paths, $template, $plugins; // Common objects
   344     
   344 		
   345     if ( !$this->perms->get_permissions('view_source') )
   345 		if ( !$this->perms->get_permissions('view_source') )
   346     {
   346 		{
   347       return false;
   347 			return false;
   348     }
   348 		}
   349     if ( !$this->page_exists )
   349 		if ( !$this->page_exists )
   350     {
   350 		{
   351       return '';
   351 			return '';
   352     }
   352 		}
   353     $cdata = $this->ns->get_cdata();
   353 		$cdata = $this->ns->get_cdata();
   354     if ( isset($cdata['password']) )
   354 		if ( isset($cdata['password']) )
   355     {
   355 		{
   356       if ( $cdata['password'] != sha1('') && $cdata['password'] !== $this->password && !empty($cdata['password']) )
   356 			if ( $cdata['password'] != sha1('') && $cdata['password'] !== $this->password && !empty($cdata['password']) )
   357       {
   357 			{
   358         return false;
   358 				return false;
   359       }
   359 			}
   360     }
   360 		}
   361     return $this->fetch_text();
   361 		return $this->fetch_text();
   362   }
   362 	}
   363   
   363 	
   364   /**
   364 	/**
   365    * Updates (saves/changes/edits) the content of the page.
   365  	* Updates (saves/changes/edits) the content of the page.
   366    * @param string The new text for the page
   366  	* @param string The new text for the page
   367    * @param string A summary of edits made to the page.
   367  	* @param string A summary of edits made to the page.
   368    * @param bool If true, the edit is marked as a minor revision
   368  	* @param bool If true, the edit is marked as a minor revision
   369    * @param string Page format - wikitext or xhtml. REQUIRED, and new in 1.1.6.
   369  	* @param string Page format - wikitext or xhtml. REQUIRED, and new in 1.1.6.
   370    * @return bool True on success, false on failure. When returning false, it will push errors to the PageProcessor error stack; read with $page->pop_error()
   370  	* @return bool True on success, false on failure. When returning false, it will push errors to the PageProcessor error stack; read with $page->pop_error()
   371    */
   371  	*/
   372   
   372 	
   373   function update_page($text, $edit_summary = false, $minor_edit = false, $page_format)
   373 	function update_page($text, $edit_summary = false, $minor_edit = false, $page_format)
   374   {
   374 	{
   375     global $db, $session, $paths, $template, $plugins; // Common objects
   375 		global $db, $session, $paths, $template, $plugins; // Common objects
   376     global $lang;
   376 		global $lang;
   377     
   377 		
   378     // Create the page if it doesn't exist
   378 		// Create the page if it doesn't exist
   379     if ( !$this->page_exists )
   379 		if ( !$this->page_exists )
   380     {
   380 		{
   381       if ( !$this->create_page() )
   381 			if ( !$this->create_page() )
   382       {
   382 			{
   383         return false;
   383 				return false;
   384       }
   384 			}
   385     }
   385 		}
   386       
   386 			
   387     //
   387 		//
   388     // Validation
   388 		// Validation
   389     //
   389 		//
   390     
   390 		
   391     $page_id = $db->escape($this->page_id);
   391 		$page_id = $db->escape($this->page_id);
   392     $namespace = $db->escape($this->namespace);
   392 		$namespace = $db->escape($this->namespace);
   393     
   393 		
   394     $q = $db->sql_query('SELECT protected FROM ' . table_prefix . "pages WHERE urlname='$page_id' AND namespace='$namespace';");
   394 		$q = $db->sql_query('SELECT protected FROM ' . table_prefix . "pages WHERE urlname='$page_id' AND namespace='$namespace';");
   395     if ( !$q )
   395 		if ( !$q )
   396       $db->_die('PageProcess updating page content');
   396 			$db->_die('PageProcess updating page content');
   397     if ( $db->numrows() < 1 )
   397 		if ( $db->numrows() < 1 )
   398     {
   398 		{
   399       $this->raise_error($lang->get('editor_err_no_rows'));
   399 			$this->raise_error($lang->get('editor_err_no_rows'));
   400       return false;
   400 			return false;
   401     }
   401 		}
   402     
   402 		
   403     // Do we have permission to edit the page?
   403 		// Do we have permission to edit the page?
   404     if ( !$this->perms->get_permissions('edit_page') )
   404 		if ( !$this->perms->get_permissions('edit_page') )
   405     {
   405 		{
   406       $this->raise_error($lang->get('editor_err_no_permission'));
   406 			$this->raise_error($lang->get('editor_err_no_permission'));
   407       return false;
   407 			return false;
   408     }
   408 		}
   409     
   409 		
   410     list($protection) = $db->fetchrow_num();
   410 		list($protection) = $db->fetchrow_num();
   411     $db->free_result();
   411 		$db->free_result();
   412     
   412 		
   413     if ( $protection == 1 )
   413 		if ( $protection == 1 )
   414     {
   414 		{
   415       // The page is protected - do we have permission to edit protected pages?
   415 			// The page is protected - do we have permission to edit protected pages?
   416       if ( !$this->perms->get_permissions('even_when_protected') )
   416 			if ( !$this->perms->get_permissions('even_when_protected') )
   417       {
   417 			{
   418         $this->raise_error($lang->get('editor_err_page_protected'));
   418 				$this->raise_error($lang->get('editor_err_page_protected'));
   419         return false;
   419 				return false;
   420       }
   420 			}
   421     }
   421 		}
   422     else if ( $protection == 2 )
   422 		else if ( $protection == 2 )
   423     {
   423 		{
   424       // The page is semi-protected.
   424 			// The page is semi-protected.
   425       if (
   425 			if (
   426            ( !$session->user_logged_in || // Is the user logged in?
   426  					( !$session->user_logged_in || // Is the user logged in?
   427              ( $session->user_logged_in && $session->reg_time + ( 4 * 86400 ) >= time() ) ) // If so, have they been registered for 4 days?
   427  						( $session->user_logged_in && $session->reg_time + ( 4 * 86400 ) >= time() ) ) // If so, have they been registered for 4 days?
   428            && !$this->perms->get_permissions('even_when_protected') ) // And of course, is there an ACL that overrides semi-protection?
   428  					&& !$this->perms->get_permissions('even_when_protected') ) // And of course, is there an ACL that overrides semi-protection?
   429       {
   429 			{
   430         $this->raise_error($lang->get('editor_err_page_protected'));
   430 				$this->raise_error($lang->get('editor_err_page_protected'));
   431         return false;
   431 				return false;
   432       }
   432 			}
   433     }
   433 		}
   434     
   434 		
   435     // Spam check
   435 		// Spam check
   436     if ( !spamalyze($text) )
   436 		if ( !spamalyze($text) )
   437     {
   437 		{
   438       $this->raise_error($lang->get('editor_err_spamcheck_failed'));
   438 			$this->raise_error($lang->get('editor_err_spamcheck_failed'));
   439       return false;
   439 			return false;
   440     }
   440 		}
   441     
   441 		
   442     // Page format check
   442 		// Page format check
   443     if ( !in_array($page_format, array('xhtml', 'wikitext')) )
   443 		if ( !in_array($page_format, array('xhtml', 'wikitext')) )
   444     {
   444 		{
   445       $this->raise_error("format \"$page_format\" not one of [xhtml, wikitext]");
   445 			$this->raise_error("format \"$page_format\" not one of [xhtml, wikitext]");
   446       return false;
   446 			return false;
   447     }
   447 		}
   448     
   448 		
   449     //
   449 		//
   450     // Protection validated; update page content
   450 		// Protection validated; update page content
   451     //
   451 		//
   452     
   452 		
   453     $text_undb = RenderMan::preprocess_text($text, false, false);
   453 		$text_undb = RenderMan::preprocess_text($text, false, false);
   454     $text = $db->escape($text_undb);
   454 		$text = $db->escape($text_undb);
   455     $author = $db->escape($session->username);
   455 		$author = $db->escape($session->username);
   456     $time = time();
   456 		$time = time();
   457     $edit_summary = ( strval($edit_summary) === $edit_summary ) ? $db->escape($edit_summary) : '';
   457 		$edit_summary = ( strval($edit_summary) === $edit_summary ) ? $db->escape($edit_summary) : '';
   458     $minor_edit = ( $minor_edit ) ? '1' : '0';
   458 		$minor_edit = ( $minor_edit ) ? '1' : '0';
   459     $date_string = enano_date(ED_DATE | ED_TIME);
   459 		$date_string = enano_date(ED_DATE | ED_TIME);
   460     
   460 		
   461     // Insert log entry
   461 		// Insert log entry
   462     $sql = 'INSERT INTO ' . table_prefix . "logs ( time_id, date_string, log_type, action, page_id, namespace, author, author_uid, page_text, edit_summary, minor_edit, page_format )\n"
   462 		$sql = 'INSERT INTO ' . table_prefix . "logs ( time_id, date_string, log_type, action, page_id, namespace, author, author_uid, page_text, edit_summary, minor_edit, page_format )\n"
   463          . "  VALUES ( $time, '$date_string', 'page', 'edit', '{$this->page_id}', '{$this->namespace}', '$author', $session->user_id, '$text', '$edit_summary', $minor_edit, '$page_format' );";
   463  				. "  VALUES ( $time, '$date_string', 'page', 'edit', '{$this->page_id}', '{$this->namespace}', '$author', $session->user_id, '$text', '$edit_summary', $minor_edit, '$page_format' );";
   464     if ( !$db->sql_query($sql) )
   464 		if ( !$db->sql_query($sql) )
   465     {
   465 		{
   466       $this->raise_error($db->get_error());
   466 			$this->raise_error($db->get_error());
   467       return false;
   467 			return false;
   468     }
   468 		}
   469     
   469 		
   470     // Update the master text entry
   470 		// Update the master text entry
   471     $sql = 'UPDATE ' . table_prefix . "page_text SET page_text = '$text' WHERE page_id = '{$this->page_id}' AND namespace = '{$this->namespace}';";
   471 		$sql = 'UPDATE ' . table_prefix . "page_text SET page_text = '$text' WHERE page_id = '{$this->page_id}' AND namespace = '{$this->namespace}';";
   472     if ( !$db->sql_query($sql) )
   472 		if ( !$db->sql_query($sql) )
   473     {
   473 		{
   474       $this->raise_error($db->get_error());
   474 			$this->raise_error($db->get_error());
   475       return false;
   475 			return false;
   476     }
   476 		}
   477     
   477 		
   478     // If there's an identical draft copy, delete it
   478 		// If there's an identical draft copy, delete it
   479     $sql = 'DELETE FROM ' . table_prefix . "logs WHERE is_draft = 1 AND page_id = '{$this->page_id}' AND namespace = '{$this->namespace}' AND page_text = '{$text}';";
   479 		$sql = 'DELETE FROM ' . table_prefix . "logs WHERE is_draft = 1 AND page_id = '{$this->page_id}' AND namespace = '{$this->namespace}' AND page_text = '{$text}';";
   480     if ( !$db->sql_query($sql) )
   480 		if ( !$db->sql_query($sql) )
   481     {
   481 		{
   482       $this->raise_error($db->get_error());
   482 			$this->raise_error($db->get_error());
   483       return false;
   483 			return false;
   484     }
   484 		}
   485     
   485 		
   486     // Set page_format
   486 		// Set page_format
   487     // Using @ due to warning thrown when saving new page
   487 		// Using @ due to warning thrown when saving new page
   488     $cdata = $this->ns->get_cdata();
   488 		$cdata = $this->ns->get_cdata();
   489     if ( @$cdata['page_format'] !== $page_format )
   489 		if ( @$cdata['page_format'] !== $page_format )
   490     {
   490 		{
   491       // Note: no SQL injection to worry about here. Everything that goes into this is sanitized already, barring some rogue plugin.
   491 			// Note: no SQL injection to worry about here. Everything that goes into this is sanitized already, barring some rogue plugin.
   492       // (and if there's a rogue plugin running, we have bigger things to worry about anyway.)
   492 			// (and if there's a rogue plugin running, we have bigger things to worry about anyway.)
   493       if ( !$db->sql_query('UPDATE ' . table_prefix . "pages SET page_format = '$page_format' WHERE urlname = '$this->page_id' AND namespace = '$this->namespace';") )
   493 			if ( !$db->sql_query('UPDATE ' . table_prefix . "pages SET page_format = '$page_format' WHERE urlname = '$this->page_id' AND namespace = '$this->namespace';") )
   494       {
   494 			{
   495         $this->raise_error($db->get_error());
   495 				$this->raise_error($db->get_error());
   496         return false;
   496 				return false;
   497       }
   497 			}
   498       $paths->update_metadata_cache();
   498 			$paths->update_metadata_cache();
   499     }
   499 		}
   500     
   500 		
   501     // Rebuild the search index
   501 		// Rebuild the search index
   502     $paths->rebuild_page_index($this->page_id, $this->namespace);
   502 		$paths->rebuild_page_index($this->page_id, $this->namespace);
   503     
   503 		
   504     $this->text_cache = $text_undb;
   504 		$this->text_cache = $text_undb;
   505     
   505 		
   506     return true;
   506 		return true;
   507     
   507 		
   508   }
   508 	}
   509   
   509 	
   510   /**
   510 	/**
   511    * Creates the page if it doesn't already exist.
   511  	* Creates the page if it doesn't already exist.
   512    * @param string Optional page title.
   512  	* @param string Optional page title.
   513    * @param bool Visibility (allow indexing) flag
   513  	* @param bool Visibility (allow indexing) flag
   514    * @return bool True on success, false on failure.
   514  	* @return bool True on success, false on failure.
   515    */
   515  	*/
   516   
   516 	
   517   function create_page($title = false, $visible = true)
   517 	function create_page($title = false, $visible = true)
   518   {
   518 	{
   519     global $db, $session, $paths, $template, $plugins; // Common objects
   519 		global $db, $session, $paths, $template, $plugins; // Common objects
   520     global $lang;
   520 		global $lang;
   521     
   521 		
   522     // Do we have permission to create the page?
   522 		// Do we have permission to create the page?
   523     if ( !$this->perms->get_permissions('create_page') )
   523 		if ( !$this->perms->get_permissions('create_page') )
   524     {
   524 		{
   525       $this->raise_error($lang->get('pagetools_create_err_no_permission'));
   525 			$this->raise_error($lang->get('pagetools_create_err_no_permission'));
   526       return false;
   526 			return false;
   527     }
   527 		}
   528     
   528 		
   529     // Does it already exist?
   529 		// Does it already exist?
   530     if ( $this->page_exists )
   530 		if ( $this->page_exists )
   531     {
   531 		{
   532       $this->raise_error($lang->get('pagetools_create_err_already_exists'));
   532 			$this->raise_error($lang->get('pagetools_create_err_already_exists'));
   533       return false;
   533 			return false;
   534     }
   534 		}
   535     
   535 		
   536     // It's not in there. Perform validation.
   536 		// It's not in there. Perform validation.
   537     
   537 		
   538     // We can't create special, admin, or external pages.
   538 		// We can't create special, admin, or external pages.
   539     if ( $this->namespace == 'Special' || $this->namespace == 'Admin' || $this->namespace == 'API' )
   539 		if ( $this->namespace == 'Special' || $this->namespace == 'Admin' || $this->namespace == 'API' )
   540     {
   540 		{
   541       $this->raise_error($lang->get('pagetools_create_err_nodb_namespace'));
   541 			$this->raise_error($lang->get('pagetools_create_err_nodb_namespace'));
   542       return false;
   542 			return false;
   543     }
   543 		}
   544     
   544 		
   545     // Guess the proper title
   545 		// Guess the proper title
   546     $name = ( !empty($title) ) ? $title : str_replace('_', ' ', dirtify_page_id($this->page_id));
   546 		$name = ( !empty($title) ) ? $title : str_replace('_', ' ', dirtify_page_id($this->page_id));
   547     
   547 		
   548     // Check for the restricted Project: prefix
   548 		// Check for the restricted Project: prefix
   549     if ( substr($this->page_id, 0, 8) == 'Project:' )
   549 		if ( substr($this->page_id, 0, 8) == 'Project:' )
   550     {
   550 		{
   551       $this->raise_error($lang->get('pagetools_create_err_reserved_prefix'));
   551 			$this->raise_error($lang->get('pagetools_create_err_reserved_prefix'));
   552       return false;
   552 			return false;
   553     }
   553 		}
   554     
   554 		
   555     // Validation successful - insert the page
   555 		// Validation successful - insert the page
   556     
   556 		
   557     $metadata = array(
   557 		$metadata = array(
   558         'urlname' => $this->page_id,
   558 				'urlname' => $this->page_id,
   559         'namespace' => $this->namespace,
   559 				'namespace' => $this->namespace,
   560         'name' => $name,
   560 				'name' => $name,
   561         'special' => 0,
   561 				'special' => 0,
   562         'visible' => $visible ? 1 : 0,
   562 				'visible' => $visible ? 1 : 0,
   563         'comments_on' => 1,
   563 				'comments_on' => 1,
   564         'protected' => ( $this->namespace == 'System' ? 1 : 0 ),
   564 				'protected' => ( $this->namespace == 'System' ? 1 : 0 ),
   565         'delvotes' => 0,
   565 				'delvotes' => 0,
   566         'delvote_ips' => serialize(array()),
   566 				'delvote_ips' => serialize(array()),
   567         'wiki_mode' => 2
   567 				'wiki_mode' => 2
   568       );
   568 			);
   569     
   569 		
   570     $paths->add_page($metadata);
   570 		$paths->add_page($metadata);
   571     
   571 		
   572     $page_id = $db->escape($this->page_id);
   572 		$page_id = $db->escape($this->page_id);
   573     $namespace = $db->escape($this->namespace);
   573 		$namespace = $db->escape($this->namespace);
   574     $name = $db->escape($name);
   574 		$name = $db->escape($name);
   575     $protect = ( $this->namespace == 'System' ) ? '1' : '0';
   575 		$protect = ( $this->namespace == 'System' ) ? '1' : '0';
   576     $blank_array = $db->escape(serialize(array()));
   576 		$blank_array = $db->escape(serialize(array()));
   577     
   577 		
   578     // Query 1: Metadata entry
   578 		// Query 1: Metadata entry
   579     $q = $db->sql_query('INSERT INTO ' . table_prefix . "pages(name, urlname, namespace, visible, protected, delvotes, delvote_ips, wiki_mode)\n"
   579 		$q = $db->sql_query('INSERT INTO ' . table_prefix . "pages(name, urlname, namespace, visible, protected, delvotes, delvote_ips, wiki_mode)\n"
   580                       . "  VALUES ( '$name', '$page_id', '$namespace', {$metadata['visible']}, $protect, 0, '$blank_array', 2 );");
   580 											. "  VALUES ( '$name', '$page_id', '$namespace', {$metadata['visible']}, $protect, 0, '$blank_array', 2 );");
   581     if ( !$q )
   581 		if ( !$q )
   582       $db->_die('PageProcessor page creation - metadata stage');
   582 			$db->_die('PageProcessor page creation - metadata stage');
   583     
   583 		
   584     // Query 2: Text insertion
   584 		// Query 2: Text insertion
   585     $q = $db->sql_query('INSERT INTO ' . table_prefix . "page_text(page_id, namespace, page_text)\n"
   585 		$q = $db->sql_query('INSERT INTO ' . table_prefix . "page_text(page_id, namespace, page_text)\n"
   586                         . "VALUES ( '$page_id', '$namespace', '' );");
   586 												. "VALUES ( '$page_id', '$namespace', '' );");
   587     if ( !$q )
   587 		if ( !$q )
   588       $db->_die('PageProcessor page creation - text stage');
   588 			$db->_die('PageProcessor page creation - text stage');
   589     
   589 		
   590     // Query 3: Log entry
   590 		// Query 3: Log entry
   591     $db->sql_query('INSERT INTO ' . table_prefix."logs(time_id, date_string, log_type, action, author, author_uid, page_id, namespace)\n"
   591 		$db->sql_query('INSERT INTO ' . table_prefix."logs(time_id, date_string, log_type, action, author, author_uid, page_id, namespace)\n"
   592                    . "  VALUES ( " . time() . ", 'DEPRECATED', 'page', 'create', \n"
   592  									. "  VALUES ( " . time() . ", 'DEPRECATED', 'page', 'create', \n"
   593                    . "          '" . $db->escape($session->username) . "', $session->user_id, '" . $db->escape($this->page_id) . "', '" . $this->namespace . "');");
   593  									. "          '" . $db->escape($session->username) . "', $session->user_id, '" . $db->escape($this->page_id) . "', '" . $this->namespace . "');");
   594     if ( !$q )
   594 		if ( !$q )
   595       $db->_die('PageProcessor page creation - logging stage');
   595 			$db->_die('PageProcessor page creation - logging stage');
   596     
   596 		
   597     // Update the cache
   597 		// Update the cache
   598     $paths->update_metadata_cache();
   598 		$paths->update_metadata_cache();
   599     
   599 		
   600     // Make sure that when/if we save the page later in this instance it doesn't get re-created
   600 		// Make sure that when/if we save the page later in this instance it doesn't get re-created
   601     $this->page_exists = true;
   601 		$this->page_exists = true;
   602     
   602 		
   603     // Page created. We're good!
   603 		// Page created. We're good!
   604     return true;
   604 		return true;
   605   }
   605 	}
   606   
   606 	
   607   /**
   607 	/**
   608    * Rolls back a non-edit action in the logs
   608  	* Rolls back a non-edit action in the logs
   609    * @param int Log entry (log_id) to roll back
   609  	* @param int Log entry (log_id) to roll back
   610    * @return array Standard Enano error/success protocol
   610  	* @return array Standard Enano error/success protocol
   611    */
   611  	*/
   612   
   612 	
   613   function rollback_log_entry($log_id)
   613 	function rollback_log_entry($log_id)
   614   {
   614 	{
   615     global $db, $session, $paths, $template, $plugins; // Common objects
   615 		global $db, $session, $paths, $template, $plugins; // Common objects
   616     global $cache;
   616 		global $cache;
   617     
   617 		
   618     // Verify permissions
   618 		// Verify permissions
   619     if ( !$this->perms->get_permissions('history_rollback') )
   619 		if ( !$this->perms->get_permissions('history_rollback') )
   620     {
   620 		{
   621       return array(
   621 			return array(
   622         'success' => false,
   622 				'success' => false,
   623         'error' => 'access_denied'
   623 				'error' => 'access_denied'
   624         );
   624 				);
   625     }
   625 		}
   626     
   626 		
   627     // Check input
   627 		// Check input
   628     $log_id = intval($log_id);
   628 		$log_id = intval($log_id);
   629     if ( empty($log_id) )
   629 		if ( empty($log_id) )
   630     {
   630 		{
   631       return array(
   631 			return array(
   632         'success' => false,
   632 				'success' => false,
   633         'error' => 'invalid_parameter'
   633 				'error' => 'invalid_parameter'
   634         );
   634 				);
   635     }
   635 		}
   636     
   636 		
   637     // Fetch the log entry
   637 		// Fetch the log entry
   638     $q = $db->sql_query('SELECT * FROM ' . table_prefix . "logs WHERE log_type = 'page' AND page_id='{$this->page_id}' AND namespace='{$this->namespace}' AND log_id = $log_id;");
   638 		$q = $db->sql_query('SELECT * FROM ' . table_prefix . "logs WHERE log_type = 'page' AND page_id='{$this->page_id}' AND namespace='{$this->namespace}' AND log_id = $log_id;");
   639     if ( !$q )
   639 		if ( !$q )
   640       $db->_die();
   640 			$db->_die();
   641     
   641 		
   642     // Is this even a valid log entry for this context?
   642 		// Is this even a valid log entry for this context?
   643     if ( $db->numrows() < 1 )
   643 		if ( $db->numrows() < 1 )
   644     {
   644 		{
   645       return array(
   645 			return array(
   646         'success' => false,
   646 				'success' => false,
   647         'error' => 'entry_not_found'
   647 				'error' => 'entry_not_found'
   648         );
   648 				);
   649     }
   649 		}
   650     
   650 		
   651     // All good, fetch and free the result
   651 		// All good, fetch and free the result
   652     $log_entry = $db->fetchrow();
   652 		$log_entry = $db->fetchrow();
   653     $db->free_result();
   653 		$db->free_result();
   654     
   654 		
   655     $dateline = enano_date(ED_DATE | ED_TIME, $log_entry['time_id']);
   655 		$dateline = enano_date(ED_DATE | ED_TIME, $log_entry['time_id']);
   656     
   656 		
   657     // Let's see, what do we have here...
   657 		// Let's see, what do we have here...
   658     switch ( $log_entry['action'] )
   658 		switch ( $log_entry['action'] )
   659     {
   659 		{
   660       case 'rename':
   660 			case 'rename':
   661         // Page was renamed, let the rename method handle this
   661 				// Page was renamed, let the rename method handle this
   662         return array_merge($this->rename_page($log_entry['edit_summary']), array('dateline' => $dateline, 'action' => $log_entry['action']));
   662 				return array_merge($this->rename_page($log_entry['edit_summary']), array('dateline' => $dateline, 'action' => $log_entry['action']));
   663         break;
   663 				break;
   664       case 'prot':
   664 			case 'prot':
   665       case 'unprot':
   665 			case 'unprot':
   666       case 'semiprot':
   666 			case 'semiprot':
   667         return array_merge($this->protect_page(intval($log_entry['page_text']), '__REVERSION__'), array('dateline' => $dateline, 'action' => $log_entry['action']));
   667 				return array_merge($this->protect_page(intval($log_entry['page_text']), '__REVERSION__'), array('dateline' => $dateline, 'action' => $log_entry['action']));
   668         break;
   668 				break;
   669       case 'delete':
   669 			case 'delete':
   670         
   670 				
   671         // Raising a previously dead page has implications...
   671 				// Raising a previously dead page has implications...
   672         
   672 				
   673         // FIXME: l10n
   673 				// FIXME: l10n
   674         // rollback_extra is required because usually only moderators can undo page deletion AND restore the content.
   674 				// rollback_extra is required because usually only moderators can undo page deletion AND restore the content.
   675         // potential flaw here - once recreated, can past revisions be restored by users without rollback_extra? should
   675 				// potential flaw here - once recreated, can past revisions be restored by users without rollback_extra? should
   676         // probably modify editor routine to deny revert access if the timestamp < timestamp of last deletion if any.
   676 				// probably modify editor routine to deny revert access if the timestamp < timestamp of last deletion if any.
   677         if ( !$this->perms->get_permissions('history_rollback_extra') )
   677 				if ( !$this->perms->get_permissions('history_rollback_extra') )
   678           return 'Administrative privileges are required for page undeletion.';
   678 					return 'Administrative privileges are required for page undeletion.';
   679         
   679 				
   680         // Rolling back the deletion of a page that was since created?
   680 				// Rolling back the deletion of a page that was since created?
   681         $pathskey = $paths->nslist[ $this->namespace ] . $this->page_id;
   681 				$pathskey = $paths->nslist[ $this->namespace ] . $this->page_id;
   682         if ( isPage($pathskey) )
   682 				if ( isPage($pathskey) )
   683           return array(
   683 					return array(
   684               'success' => false,
   684 							'success' => false,
   685               // This is a clean Christian in-joke.
   685 							// This is a clean Christian in-joke.
   686               'error' => 'seeking_living_among_dead'
   686 							'error' => 'seeking_living_among_dead'
   687             );
   687 						);
   688         
   688 				
   689         // Generate a crappy page name
   689 				// Generate a crappy page name
   690         $name = $db->escape( str_replace('_', ' ', dirtify_page_id($this->page_id)) );
   690 				$name = $db->escape( str_replace('_', ' ', dirtify_page_id($this->page_id)) );
   691         
   691 				
   692         // Stage 1 - re-insert page
   692 				// Stage 1 - re-insert page
   693         $e = $db->sql_query('INSERT INTO ' . table_prefix.'pages(name,urlname,namespace) VALUES( \'' . $name . '\', \'' . $this->page_id . '\',\'' . $this->namespace . '\' )');
   693 				$e = $db->sql_query('INSERT INTO ' . table_prefix.'pages(name,urlname,namespace) VALUES( \'' . $name . '\', \'' . $this->page_id . '\',\'' . $this->namespace . '\' )');
   694         if ( !$e )
   694 				if ( !$e )
   695           $db->die_json();
   695 					$db->die_json();
   696         
   696 				
   697         // Select the latest published revision
   697 				// Select the latest published revision
   698         $q = $db->sql_query('SELECT page_text FROM ' . table_prefix . "logs WHERE\n"
   698 				$q = $db->sql_query('SELECT page_text FROM ' . table_prefix . "logs WHERE\n"
   699                           . "      log_type  = 'page'\n"
   699 													. "      log_type  = 'page'\n"
   700                           . "  AND action    = 'edit'\n"
   700 													. "  AND action    = 'edit'\n"
   701                           . "  AND page_id   = '$this->page_id'\n"
   701 													. "  AND page_id   = '$this->page_id'\n"
   702                           . "  AND namespace = '$this->namespace'\n"
   702 													. "  AND namespace = '$this->namespace'\n"
   703                           . "  AND is_draft != 1\n"
   703 													. "  AND is_draft != 1\n"
   704                           . "ORDER BY time_id DESC LIMIT 1;");
   704 													. "ORDER BY time_id DESC LIMIT 1;");
   705         if ( !$q )
   705 				if ( !$q )
   706           $db->die_json();
   706 					$db->die_json();
   707         list($page_text) = $db->fetchrow_num();
   707 				list($page_text) = $db->fetchrow_num();
   708         $db->free_result($q);
   708 				$db->free_result($q);
   709         
   709 				
   710         // Apply the latest revision as the current page text
   710 				// Apply the latest revision as the current page text
   711         $page_text = $db->escape($page_text);
   711 				$page_text = $db->escape($page_text);
   712         $e = $db->sql_query('INSERT INTO ' . table_prefix."page_text(page_id, namespace, page_text) VALUES\n"
   712 				$e = $db->sql_query('INSERT INTO ' . table_prefix."page_text(page_id, namespace, page_text) VALUES\n"
   713                           . "  ( '$this->page_id', '$this->namespace', '$page_text' );");
   713 													. "  ( '$this->page_id', '$this->namespace', '$page_text' );");
   714         if ( !$e )
   714 				if ( !$e )
   715           $db->die_json();
   715 					$db->die_json();
   716         
   716 				
   717         $cache->purge('page_meta');
   717 				$cache->purge('page_meta');
   718         
   718 				
   719         return array(
   719 				return array(
   720             'success' => true,
   720 						'success' => true,
   721             'dateline' => $dateline,
   721 						'dateline' => $dateline,
   722             'action' => $log_entry['action']
   722 						'action' => $log_entry['action']
   723           );
   723 					);
   724         
   724 				
   725         break;
   725 				break;
   726       case 'reupload':
   726 			case 'reupload':
   727         
   727 				
   728         // given a log id and some revision info, restore the old file.
   728 				// given a log id and some revision info, restore the old file.
   729         // get the timestamp of the file before this one
   729 				// get the timestamp of the file before this one
   730         $q = $db->sql_query('SELECT time_id, file_key, file_extension, filename, size, mimetype FROM ' . table_prefix . "files WHERE time_id < {$log_entry['time_id']} ORDER BY time_id DESC LIMIT 1;");
   730 				$q = $db->sql_query('SELECT time_id, file_key, file_extension, filename, size, mimetype FROM ' . table_prefix . "files WHERE time_id < {$log_entry['time_id']} ORDER BY time_id DESC LIMIT 1;");
   731         if ( !$q )
   731 				if ( !$q )
   732           $db->_die();
   732 					$db->_die();
   733         
   733 				
   734         $row = $db->fetchrow();
   734 				$row = $db->fetchrow();
   735         $db->free_result();
   735 				$db->free_result();
   736         
   736 				
   737         // If the file hasn't been renamed to the new format (omitting timestamp), do that now.
   737 				// If the file hasn't been renamed to the new format (omitting timestamp), do that now.
   738         $fname = ENANO_ROOT . "/files/{$row['file_key']}_{$row['time_id']}{$row['file_extension']}";
   738 				$fname = ENANO_ROOT . "/files/{$row['file_key']}_{$row['time_id']}{$row['file_extension']}";
   739         if ( @file_exists($fname) )
   739 				if ( @file_exists($fname) )
   740         {
   740 				{
   741           // it's stored in the old format - rename
   741 					// it's stored in the old format - rename
   742           $fname_new = ENANO_ROOT . "/files/{$row['file_key']}{$row['file_extension']}";
   742 					$fname_new = ENANO_ROOT . "/files/{$row['file_key']}{$row['file_extension']}";
   743           if ( !@rename($fname, $fname_new) )
   743 					if ( !@rename($fname, $fname_new) )
   744           {
   744 					{
   745             return array(
   745 						return array(
   746               'success' => false,
   746 							'success' => false,
   747               'error' => 'rb_file_rename_failed',
   747 							'error' => 'rb_file_rename_failed',
   748               'action' => $log_entry['action']
   748 							'action' => $log_entry['action']
   749               );
   749 							);
   750           }
   750 					}
   751         }
   751 				}
   752         
   752 				
   753         // Insert a new file entry
   753 				// Insert a new file entry
   754         $time = time();
   754 				$time = time();
   755         $filename = $db->escape($row['filename']);
   755 				$filename = $db->escape($row['filename']);
   756         $mimetype = $db->escape($row['mimetype']);
   756 				$mimetype = $db->escape($row['mimetype']);
   757         $ext = $db->escape($row['file_extension']);
   757 				$ext = $db->escape($row['file_extension']);
   758         $key = $db->escape($row['file_key']);
   758 				$key = $db->escape($row['file_key']);
   759         
   759 				
   760         $q = $db->sql_query('INSERT INTO ' . table_prefix . "files ( time_id, page_id, filename, size, mimetype, file_extension, file_key ) VALUES\n"
   760 				$q = $db->sql_query('INSERT INTO ' . table_prefix . "files ( time_id, page_id, filename, size, mimetype, file_extension, file_key ) VALUES\n"
   761               . "  ( $time, '$this->page_id', '$filename', {$row['size']}, '$mimetype', '$ext', '$key' );");
   761 							. "  ( $time, '$this->page_id', '$filename', {$row['size']}, '$mimetype', '$ext', '$key' );");
   762         if ( !$q )
   762 				if ( !$q )
   763           $db->die_json();
   763 					$db->die_json();
   764         
   764 				
   765         // add reupload log entry
   765 				// add reupload log entry
   766         $username = $db->escape($session->username);
   766 				$username = $db->escape($session->username);
   767         $q = $db->sql_query('INSERT INTO ' . table_prefix . "logs ( log_type, action, time_id, page_id, namespace, author, author_uid, edit_summary ) VALUES\n"
   767 				$q = $db->sql_query('INSERT INTO ' . table_prefix . "logs ( log_type, action, time_id, page_id, namespace, author, author_uid, edit_summary ) VALUES\n"
   768                           . "  ( 'page', 'reupload', $time, '$this->page_id', '$this->namespace', '$username', $session->user_id, '__ROLLBACK__' )");
   768 													. "  ( 'page', 'reupload', $time, '$this->page_id', '$this->namespace', '$username', $session->user_id, '__ROLLBACK__' )");
   769         if ( !$q )
   769 				if ( !$q )
   770           $db->die_json();
   770 					$db->die_json();
   771         
   771 				
   772         return array(
   772 				return array(
   773             'success' => true,
   773 						'success' => true,
   774             'dateline' => $dateline,
   774 						'dateline' => $dateline,
   775             'action' => $log_entry['action']
   775 						'action' => $log_entry['action']
   776           );
   776 					);
   777         
   777 				
   778         break;
   778 				break;
   779       case 'votereset':
   779 			case 'votereset':
   780         if ( !$this->perms->get_permissions('history_rollback_extra') )
   780 				if ( !$this->perms->get_permissions('history_rollback_extra') )
   781           return 'Denied!';
   781 					return 'Denied!';
   782         
   782 				
   783         // pull existing vote data
   783 				// pull existing vote data
   784         $q = $db->sql_query('SELECT delvotes, delvote_ips FROM ' . table_prefix . "pages WHERE urlname = '$this->page_id' AND namespace = '$this->namespace';");
   784 				$q = $db->sql_query('SELECT delvotes, delvote_ips FROM ' . table_prefix . "pages WHERE urlname = '$this->page_id' AND namespace = '$this->namespace';");
   785         if ( !$q )
   785 				if ( !$q )
   786           $db->_die();
   786 					$db->_die();
   787         
   787 				
   788         if ( $db->numrows() < 1 )
   788 				if ( $db->numrows() < 1 )
   789           return array(
   789 					return array(
   790               'success' => false,
   790 							'success' => false,
   791               'error' => 'page_not_exist',
   791 							'error' => 'page_not_exist',
   792               'action' => $log_entry['action']
   792 							'action' => $log_entry['action']
   793             );
   793 						);
   794           
   794 					
   795         list($curr_delvotes, $curr_delvote_ips) = $db->fetchrow_num();
   795 				list($curr_delvotes, $curr_delvote_ips) = $db->fetchrow_num();
   796         $db->free_result();
   796 				$db->free_result();
   797         
   797 				
   798         // merge with existing votes
   798 				// merge with existing votes
   799         $old_delvote_ips = unserialize($log_entry['page_text']);
   799 				$old_delvote_ips = unserialize($log_entry['page_text']);
   800         $new_delvote_ips = unserialize($curr_delvote_ips);
   800 				$new_delvote_ips = unserialize($curr_delvote_ips);
   801         $new_delvote_ips['u'] = array_unique(array_merge($new_delvote_ips['u'], $old_delvote_ips['u']));
   801 				$new_delvote_ips['u'] = array_unique(array_merge($new_delvote_ips['u'], $old_delvote_ips['u']));
   802         $new_delvote_ips['ip'] = array_unique(array_merge($new_delvote_ips['ip'], $old_delvote_ips['ip']));
   802 				$new_delvote_ips['ip'] = array_unique(array_merge($new_delvote_ips['ip'], $old_delvote_ips['ip']));
   803         $new_delvotes = count($new_delvote_ips['ip']);
   803 				$new_delvotes = count($new_delvote_ips['ip']);
   804         $new_delvote_ips = $db->escape(serialize($new_delvote_ips));
   804 				$new_delvote_ips = $db->escape(serialize($new_delvote_ips));
   805         
   805 				
   806         // update pages table
   806 				// update pages table
   807         $q = $db->sql_query('UPDATE ' . table_prefix . "pages SET delvotes = $new_delvotes, delvote_ips = '$new_delvote_ips' WHERE urlname = '$this->page_id' AND namespace = '$this->namespace';");
   807 				$q = $db->sql_query('UPDATE ' . table_prefix . "pages SET delvotes = $new_delvotes, delvote_ips = '$new_delvote_ips' WHERE urlname = '$this->page_id' AND namespace = '$this->namespace';");
   808         
   808 				
   809         $cache->purge('page_meta');
   809 				$cache->purge('page_meta');
   810         
   810 				
   811         return array(
   811 				return array(
   812             'success' => true,
   812 						'success' => true,
   813             'dateline' => $dateline,
   813 						'dateline' => $dateline,
   814             'action' => $log_entry['action']
   814 						'action' => $log_entry['action']
   815           );
   815 					);
   816         break;
   816 				break;
   817       default:
   817 			default:
   818         
   818 				
   819         return array(
   819 				return array(
   820             'success' => false,
   820 						'success' => false,
   821             'error' => 'rb_action_not_supported',
   821 						'error' => 'rb_action_not_supported',
   822             'action' => $log_entry['action']
   822 						'action' => $log_entry['action']
   823           );
   823 					);
   824         
   824 				
   825         break;
   825 				break;
   826     }
   826 		}
   827   }
   827 	}
   828   
   828 	
   829   /**
   829 	/**
   830    * Renames the page
   830  	* Renames the page
   831    * @param string New name
   831  	* @param string New name
   832    * @return array Standard Enano error/success protocol
   832  	* @return array Standard Enano error/success protocol
   833    */
   833  	*/
   834   
   834 	
   835   function rename_page($new_name)
   835 	function rename_page($new_name)
   836   {
   836 	{
   837     global $db, $session, $paths, $template, $plugins; // Common objects
   837 		global $db, $session, $paths, $template, $plugins; // Common objects
   838     
   838 		
   839     // Check permissions
   839 		// Check permissions
   840     if ( !$this->perms->get_permissions('rename') )
   840 		if ( !$this->perms->get_permissions('rename') )
   841     {
   841 		{
   842       return array(
   842 			return array(
   843         'success' => false,
   843 				'success' => false,
   844         'error' => 'access_denied'
   844 				'error' => 'access_denied'
   845         );
   845 				);
   846     }
   846 		}
   847     
   847 		
   848     // If this is the same as the current name, return success
   848 		// If this is the same as the current name, return success
   849     $page_name = get_page_title_ns($this->page_id, $this->namespace);
   849 		$page_name = get_page_title_ns($this->page_id, $this->namespace);
   850     if ( $page_name === $new_name )
   850 		if ( $page_name === $new_name )
   851     {
   851 		{
   852       return array(
   852 			return array(
   853         'success' => true
   853 				'success' => true
   854         );
   854 				);
   855     }
   855 		}
   856     
   856 		
   857     // Make sure the name is valid
   857 		// Make sure the name is valid
   858     $new_name = trim($new_name);
   858 		$new_name = trim($new_name);
   859     if ( empty($new_name) )
   859 		if ( empty($new_name) )
   860     {
   860 		{
   861       return array(
   861 			return array(
   862         'success' => false,
   862 				'success' => false,
   863         'error' => 'invalid_parameter'
   863 				'error' => 'invalid_parameter'
   864         );
   864 				);
   865     }
   865 		}
   866     
   866 		
   867     // Log the action
   867 		// Log the action
   868     $username = $db->escape($session->username);
   868 		$username = $db->escape($session->username);
   869     $page_name = $db->escape($page_name);
   869 		$page_name = $db->escape($page_name);
   870     $time = time();
   870 		$time = time();
   871     
   871 		
   872     $q = $db->sql_query('INSERT INTO ' . table_prefix . "logs ( log_type, action, page_id, namespace, author, author_uid, edit_summary, time_id, date_string ) VALUES\n"
   872 		$q = $db->sql_query('INSERT INTO ' . table_prefix . "logs ( log_type, action, page_id, namespace, author, author_uid, edit_summary, time_id, date_string ) VALUES\n"
   873                       . "  ( 'page', 'rename', '{$this->page_id}', '{$this->namespace}', '$username', $session->user_id, '$page_name', '$time', 'DATE_STRING COLUMN OBSOLETE, USE time_id' );");
   873 											. "  ( 'page', 'rename', '{$this->page_id}', '{$this->namespace}', '$username', $session->user_id, '$page_name', '$time', 'DATE_STRING COLUMN OBSOLETE, USE time_id' );");
   874     if ( !$q )
   874 		if ( !$q )
   875       $db->_die();
   875 			$db->_die();
   876     
   876 		
   877     // Not much to do but to rename it now
   877 		// Not much to do but to rename it now
   878     $new_name = $db->escape($new_name);
   878 		$new_name = $db->escape($new_name);
   879     $q = $db->sql_query('UPDATE ' . table_prefix . "pages SET name = '$new_name' WHERE urlname = '{$this->page_id}' AND namespace = '{$this->namespace}';");
   879 		$q = $db->sql_query('UPDATE ' . table_prefix . "pages SET name = '$new_name' WHERE urlname = '{$this->page_id}' AND namespace = '{$this->namespace}';");
   880     if ( !$q )
   880 		if ( !$q )
   881       $db->_die();
   881 			$db->_die();
   882     
   882 		
   883     // Update the cache
   883 		// Update the cache
   884     $paths->update_metadata_cache();
   884 		$paths->update_metadata_cache();
   885     
   885 		
   886     return array(
   886 		return array(
   887       'success' => true
   887 			'success' => true
   888       );
   888 			);
   889   }
   889 	}
   890   
   890 	
   891   /**
   891 	/**
   892    * Sets the protection level of the page
   892  	* Sets the protection level of the page
   893    * @param int Protection level, one of PROTECT_{FULL,SEMI,NONE}
   893  	* @param int Protection level, one of PROTECT_{FULL,SEMI,NONE}
   894    * @param string Reason for protection - required
   894  	* @param string Reason for protection - required
   895    */
   895  	*/
   896   
   896 	
   897   function protect_page($protection_level, $reason)
   897 	function protect_page($protection_level, $reason)
   898   {
   898 	{
   899     global $db, $session, $paths, $template, $plugins; // Common objects
   899 		global $db, $session, $paths, $template, $plugins; // Common objects
   900     global $cache;
   900 		global $cache;
   901     
   901 		
   902     // Validate permissions
   902 		// Validate permissions
   903     if ( !$this->perms->get_permissions('protect') )
   903 		if ( !$this->perms->get_permissions('protect') )
   904     {
   904 		{
   905       return array(
   905 			return array(
   906         'success' => false,
   906 				'success' => false,
   907         'error' => 'access_denied'
   907 				'error' => 'access_denied'
   908         );
   908 				);
   909     }
   909 		}
   910     
   910 		
   911     // Validate re-auth
   911 		// Validate re-auth
   912     if ( !$session->sid_super )
   912 		if ( !$session->sid_super )
   913     {
   913 		{
   914       return array(
   914 			return array(
   915         'success' => false,
   915 				'success' => false,
   916         'error' => 'access_denied_need_reauth'
   916 				'error' => 'access_denied_need_reauth'
   917         );
   917 				);
   918     }
   918 		}
   919     
   919 		
   920     // Validate input
   920 		// Validate input
   921     $reason = trim($reason);
   921 		$reason = trim($reason);
   922     if ( !in_array($protection_level, array(PROTECT_NONE, PROTECT_FULL, PROTECT_SEMI)) || empty($reason) )
   922 		if ( !in_array($protection_level, array(PROTECT_NONE, PROTECT_FULL, PROTECT_SEMI)) || empty($reason) )
   923     {
   923 		{
   924       return array(
   924 			return array(
   925         'success' => false,
   925 				'success' => false,
   926         'error' => 'invalid_parameter'
   926 				'error' => 'invalid_parameter'
   927         );
   927 				);
   928     }
   928 		}
   929     
   929 		
   930     // Retrieve page metadata
   930 		// Retrieve page metadata
   931     $metadata = $this->ns->get_cdata();
   931 		$metadata = $this->ns->get_cdata();
   932     
   932 		
   933     // Log the action
   933 		// Log the action
   934     $username = $db->escape($session->username);
   934 		$username = $db->escape($session->username);
   935     $time = time();
   935 		$time = time();
   936     $existing_protection = intval($metadata['protected']);
   936 		$existing_protection = intval($metadata['protected']);
   937     $reason = $db->escape($reason);
   937 		$reason = $db->escape($reason);
   938     
   938 		
   939     if ( $existing_protection == $protection_level )
   939 		if ( $existing_protection == $protection_level )
   940     {
   940 		{
   941       return array(
   941 			return array(
   942         'success' => false,
   942 				'success' => false,
   943         'error' => 'protection_already_there'
   943 				'error' => 'protection_already_there'
   944         );
   944 				);
   945     }
   945 		}
   946     
   946 		
   947     $action = '[ insanity ]';
   947 		$action = '[ insanity ]';
   948     switch($protection_level)
   948 		switch($protection_level)
   949     {
   949 		{
   950       case PROTECT_FULL: $action = 'prot'; break;
   950 			case PROTECT_FULL: $action = 'prot'; break;
   951       case PROTECT_NONE: $action = 'unprot'; break;
   951 			case PROTECT_NONE: $action = 'unprot'; break;
   952       case PROTECT_SEMI: $action = 'semiprot'; break;
   952 			case PROTECT_SEMI: $action = 'semiprot'; break;
   953     }
   953 		}
   954     
   954 		
   955     $sql = 'INSERT INTO ' . table_prefix . "logs ( log_type, action, page_id, namespace, author, author_uid, edit_summary, time_id, page_text, date_string ) VALUES\n"
   955 		$sql = 'INSERT INTO ' . table_prefix . "logs ( log_type, action, page_id, namespace, author, author_uid, edit_summary, time_id, page_text, date_string ) VALUES\n"
   956          . "  ( 'page', '$action', '{$this->page_id}', '{$this->namespace}', '$username', $author_uid, '$reason', '$time', '$existing_protection', 'DATE_STRING COLUMN OBSOLETE, USE time_id' );";
   956  				. "  ( 'page', '$action', '{$this->page_id}', '{$this->namespace}', '$username', $author_uid, '$reason', '$time', '$existing_protection', 'DATE_STRING COLUMN OBSOLETE, USE time_id' );";
   957     if ( !$db->sql_query($sql) )
   957 		if ( !$db->sql_query($sql) )
   958     {
   958 		{
   959       $db->die_json();
   959 			$db->die_json();
   960     }
   960 		}
   961     
   961 		
   962     // Perform the actual protection
   962 		// Perform the actual protection
   963     $q = $db->sql_query('UPDATE ' . table_prefix . "pages SET protected = $protection_level WHERE urlname = '{$this->page_id}' AND namespace = '{$this->namespace}';");
   963 		$q = $db->sql_query('UPDATE ' . table_prefix . "pages SET protected = $protection_level WHERE urlname = '{$this->page_id}' AND namespace = '{$this->namespace}';");
   964     if ( !$q )
   964 		if ( !$q )
   965       $db->die_json();
   965 			$db->die_json();
   966     
   966 		
   967     $cache->purge('page_meta');
   967 		$cache->purge('page_meta');
   968     
   968 		
   969     return array(
   969 		return array(
   970       'success' => true
   970 			'success' => true
   971       );
   971 			);
   972   }
   972 	}
   973   
   973 	
   974   /**
   974 	/**
   975    * Sets internal variables.
   975  	* Sets internal variables.
   976    * @access private
   976  	* @access private
   977    */
   977  	*/
   978   
   978 	
   979   function _setup($page_id, $namespace, $revision_id)
   979 	function _setup($page_id, $namespace, $revision_id)
   980   {
   980 	{
   981     global $db, $session, $paths, $template, $plugins; // Common objects
   981 		global $db, $session, $paths, $template, $plugins; // Common objects
   982     
   982 		
   983     $page_id_cleaned = sanitize_page_id($page_id);
   983 		$page_id_cleaned = sanitize_page_id($page_id);
   984     
   984 		
   985     $this->revision_id = $revision_id;
   985 		$this->revision_id = $revision_id;
   986     $this->page_id_unclean = dirtify_page_id($page_id);
   986 		$this->page_id_unclean = dirtify_page_id($page_id);
   987     
   987 		
   988     // resolve namespace
   988 		// resolve namespace
   989     $this->ns = namespace_factory($page_id, $namespace, $this->revision_id);
   989 		$this->ns = namespace_factory($page_id, $namespace, $this->revision_id);
   990     $this->page_id =& $this->ns->page_id;
   990 		$this->page_id =& $this->ns->page_id;
   991     $this->namespace =& $this->ns->namespace;
   991 		$this->namespace =& $this->ns->namespace;
   992     
   992 		
   993     $this->perms = $session->fetch_page_acl( $page_id, $namespace );
   993 		$this->perms = $session->fetch_page_acl( $page_id, $namespace );
   994     
   994 		
   995     $this->page_exists = $this->ns->exists();
   995 		$this->page_exists = $this->ns->exists();
   996     $this->title = get_page_title_ns($this->page_id, $this->namespace);
   996 		$this->title = get_page_title_ns($this->page_id, $this->namespace);
   997     
   997 		
   998     profiler_log("PageProcessor [{$this->namespace}:{$this->page_id}]: Ran _setup()");
   998 		profiler_log("PageProcessor [{$this->namespace}:{$this->page_id}]: Ran _setup()");
   999   }
   999 	}
  1000   
  1000 	
  1001   /**
  1001 	/**
  1002    * Processes any redirects.
  1002  	* Processes any redirects.
  1003    * @access private
  1003  	* @access private
  1004    */
  1004  	*/
  1005   
  1005 	
  1006   function process_redirects()
  1006 	function process_redirects()
  1007   {
  1007 	{
  1008     global $db, $session, $paths, $template, $plugins; // Common objects
  1008 		global $db, $session, $paths, $template, $plugins; // Common objects
  1009     global $output, $lang;
  1009 		global $output, $lang;
  1010     
  1010 		
  1011     $this->redirect_stack = array();
  1011 		$this->redirect_stack = array();
  1012     
  1012 		
  1013     if ( !method_exists($this->ns, 'get_redirect') )
  1013 		if ( !method_exists($this->ns, 'get_redirect') )
  1014       return true;
  1014 			return true;
  1015     
  1015 		
  1016     if ( !$this->allow_redir )
  1016 		if ( !$this->allow_redir )
  1017       return true;
  1017 			return true;
  1018     
  1018 		
  1019     $redirect_count = 0;
  1019 		$redirect_count = 0;
  1020     
  1020 		
  1021     while ( $result = $this->ns->get_redirect() )
  1021 		while ( $result = $this->ns->get_redirect() )
  1022     {
  1022 		{
  1023       if ( $result['namespace'] == 'Special' || $result['namespace'] == 'Admin' )
  1023 			if ( $result['namespace'] == 'Special' || $result['namespace'] == 'Admin' )
  1024       {
  1024 			{
  1025         // Can't redirect to special/admin page
  1025 				// Can't redirect to special/admin page
  1026         $this->redir_error = $lang->get('page_err_redirect_to_special');
  1026 				$this->redir_error = $lang->get('page_err_redirect_to_special');
  1027         break;
  1027 				break;
  1028       }
  1028 			}
  1029       if ( $redirect_count == 3 )
  1029 			if ( $redirect_count == 3 )
  1030       {
  1030 			{
  1031         // max of 3 internal redirects exceeded
  1031 				// max of 3 internal redirects exceeded
  1032         $this->redir_error = $lang->get('page_err_redirects_exceeded');
  1032 				$this->redir_error = $lang->get('page_err_redirects_exceeded');
  1033         break;
  1033 				break;
  1034       }
  1034 			}
  1035       
  1035 			
  1036       $loop = false;
  1036 			$loop = false;
  1037       foreach ( $this->redirect_stack as $stackel )
  1037 			foreach ( $this->redirect_stack as $stackel )
  1038       {
  1038 			{
  1039         if ( $result['page_id'] == $stackel['old_page_id'] && $result['namespace'] == $stackel['old_namespace'] )
  1039 				if ( $result['page_id'] == $stackel['old_page_id'] && $result['namespace'] == $stackel['old_namespace'] )
  1040         {
  1040 				{
  1041           $loop = true;
  1041 					$loop = true;
  1042           break;
  1042 					break;
  1043         }
  1043 				}
  1044       }
  1044 			}
  1045       
  1045 			
  1046       if ( $loop )
  1046 			if ( $loop )
  1047       {
  1047 			{
  1048         // redirect loop
  1048 				// redirect loop
  1049         $this->redir_error = $lang->get('page_err_redirect_infinite_loop');
  1049 				$this->redir_error = $lang->get('page_err_redirect_infinite_loop');
  1050         break;
  1050 				break;
  1051       }
  1051 			}
  1052       $new_ns = namespace_factory($result['page_id'], $result['namespace']);
  1052 			$new_ns = namespace_factory($result['page_id'], $result['namespace']);
  1053       if ( !$new_ns->exists() )
  1053 			if ( !$new_ns->exists() )
  1054       {
  1054 			{
  1055         // new page doesn't exist
  1055 				// new page doesn't exist
  1056         $this->redir_error = $lang->get('page_err_redirect_to_nonexistent');
  1056 				$this->redir_error = $lang->get('page_err_redirect_to_nonexistent');
  1057         break;
  1057 				break;
  1058       }
  1058 			}
  1059       
  1059 			
  1060       // build stack entry
  1060 			// build stack entry
  1061       $stackel = array(
  1061 			$stackel = array(
  1062           'page_id' => $result['page_id'],
  1062 					'page_id' => $result['page_id'],
  1063           'namespace' => $result['namespace'],
  1063 					'namespace' => $result['namespace'],
  1064           'old_page_id' => $this->page_id,
  1064 					'old_page_id' => $this->page_id,
  1065           'old_namespace' => $this->namespace,
  1065 					'old_namespace' => $this->namespace,
  1066           'old_title' => $this->ns->title
  1066 					'old_title' => $this->ns->title
  1067         );
  1067 				);
  1068       
  1068 			
  1069       // replace everything (perform the actual redirect)
  1069 			// replace everything (perform the actual redirect)
  1070       $this->ns = $new_ns;
  1070 			$this->ns = $new_ns;
  1071       
  1071 			
  1072       $this->page_id =& $this->ns->page_id;
  1072 			$this->page_id =& $this->ns->page_id;
  1073       $this->namespace =& $this->ns->namespace;
  1073 			$this->namespace =& $this->ns->namespace;
  1074       
  1074 			
  1075       $this->redirect_stack[] = $stackel;
  1075 			$this->redirect_stack[] = $stackel;
  1076       
  1076 			
  1077       $redirect_count++;
  1077 			$redirect_count++;
  1078     }
  1078 		}
  1079   }
  1079 	}
  1080     
  1080 		
  1081   /**
  1081 	/**
  1082    * Sends the page header, dependent on, of course, whether we're supposed to.
  1082  	* Sends the page header, dependent on, of course, whether we're supposed to.
  1083    */
  1083  	*/
  1084   
  1084 	
  1085   function header()
  1085 	function header()
  1086   {
  1086 	{
  1087     global $db, $session, $paths, $template, $plugins; // Common objects
  1087 		global $db, $session, $paths, $template, $plugins; // Common objects
  1088     if ( $this->send_headers )
  1088 		if ( $this->send_headers )
  1089       $template->header();
  1089 			$template->header();
  1090   }
  1090 	}
  1091   
  1091 	
  1092   /**
  1092 	/**
  1093    * Sends the page footer, dependent on, of course, whether we're supposed to.
  1093  	* Sends the page footer, dependent on, of course, whether we're supposed to.
  1094    */
  1094  	*/
  1095   
  1095 	
  1096   function footer()
  1096 	function footer()
  1097   {
  1097 	{
  1098     global $db, $session, $paths, $template, $plugins; // Common objects
  1098 		global $db, $session, $paths, $template, $plugins; // Common objects
  1099     if ( $this->send_headers )
  1099 		if ( $this->send_headers )
  1100       $template->footer();
  1100 			$template->footer();
  1101   }
  1101 	}
  1102   
  1102 	
  1103   /**
  1103 	/**
  1104    * Fetches the raw, unfiltered page text.
  1104  	* Fetches the raw, unfiltered page text.
  1105    * @access public
  1105  	* @access public
  1106    */
  1106  	*/
  1107   
  1107 	
  1108   function fetch_text()
  1108 	function fetch_text()
  1109   {
  1109 	{
  1110     return $this->ns->fetch_text();
  1110 		return $this->ns->fetch_text();
  1111   }
  1111 	}
  1112   
  1112 	
  1113   /**
  1113 	/**
  1114    * Tells us if the page exists.
  1114  	* Tells us if the page exists.
  1115    * @return bool
  1115  	* @return bool
  1116    */
  1116  	*/
  1117   
  1117 	
  1118   function exists()
  1118 	function exists()
  1119   {
  1119 	{
  1120     return $this->ns->exists();
  1120 		return $this->ns->exists();
  1121   }
  1121 	}
  1122   
  1122 	
  1123   /**
  1123 	/**
  1124    * Send the error message to the user that the access to this page is denied.
  1124  	* Send the error message to the user that the access to this page is denied.
  1125    * @access private
  1125  	* @access private
  1126    */
  1126  	*/
  1127   
  1127 	
  1128   function err_access_denied()
  1128 	function err_access_denied()
  1129   {
  1129 	{
  1130     global $db, $session, $paths, $template, $plugins; // Common objects
  1130 		global $db, $session, $paths, $template, $plugins; // Common objects
  1131     global $lang;
  1131 		global $lang;
  1132     global $email;
  1132 		global $email;
  1133     
  1133 		
  1134     // Log it for crying out loud
  1134 		// Log it for crying out loud
  1135     $q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,author_uid,edit_summary,page_text) VALUES(\'security\', \'illegal_page\', '.time().', \'DEPRECATED\', \''.$db->escape($session->username).'\', ' . $session->user_id . ', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', \'' . $db->escape(serialize(array($this->page_id, $this->namespace))) . '\')');
  1135 		$q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,author_uid,edit_summary,page_text) VALUES(\'security\', \'illegal_page\', '.time().', \'DEPRECATED\', \''.$db->escape($session->username).'\', ' . $session->user_id . ', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', \'' . $db->escape(serialize(array($this->page_id, $this->namespace))) . '\')');
  1136     
  1136 		
  1137     $ob = '';
  1137 		$ob = '';
  1138     //$template->tpl_strings['PAGE_NAME'] = 'Access denied';
  1138 		//$template->tpl_strings['PAGE_NAME'] = 'Access denied';
  1139     $template->tpl_strings['PAGE_NAME'] = htmlspecialchars( $this->title );
  1139 		$template->tpl_strings['PAGE_NAME'] = htmlspecialchars( $this->title );
  1140       
  1140 			
  1141     if ( $this->send_headers )
  1141 		if ( $this->send_headers )
  1142     {
  1142 		{
  1143       $ob .= $template->getHeader();
  1143 			$ob .= $template->getHeader();
  1144     }
  1144 		}
  1145     
  1145 		
  1146     if ( count($this->redirect_stack) > 0 )
  1146 		if ( count($this->redirect_stack) > 0 )
  1147     {
  1147 		{
  1148       $stack = array_reverse($this->redirect_stack);
  1148 			$stack = array_reverse($this->redirect_stack);
  1149       foreach ( $stack as $oldtarget )
  1149 			foreach ( $stack as $oldtarget )
  1150       {
  1150 			{
  1151         $url = makeUrlNS($oldtarget[1], $oldtarget[0], 'redirect=no', true);
  1151 				$url = makeUrlNS($oldtarget[1], $oldtarget[0], 'redirect=no', true);
  1152         $old_page = namespace_factory($oldtarget[0], $oldtarget[1]);
  1152 				$old_page = namespace_factory($oldtarget[0], $oldtarget[1]);
  1153         $page_data = $old_page->get_cdata();
  1153 				$page_data = $old_page->get_cdata();
  1154         $title = ( isset($page_data['name']) ) ? $page_data['name'] : $paths->nslist[$oldtarget[1]] . htmlspecialchars( str_replace('_', ' ', dirtify_page_id( $oldtarget[0] ) ) );
  1154 				$title = ( isset($page_data['name']) ) ? $page_data['name'] : $paths->nslist[$oldtarget[1]] . htmlspecialchars( str_replace('_', ' ', dirtify_page_id( $oldtarget[0] ) ) );
  1155         $a = '<a href="' . $url . '">' . $title . '</a>';
  1155 				$a = '<a href="' . $url . '">' . $title . '</a>';
  1156         
  1156 				
  1157         $url = makeUrlNS($this->namespace, $this->page_id, 'redirect=no', true);
  1157 				$url = makeUrlNS($this->namespace, $this->page_id, 'redirect=no', true);
  1158         $page_data = $this->ns->get_cdata();
  1158 				$page_data = $this->ns->get_cdata();
  1159         $title = ( isset($page_data['name']) ) ? $page_data['name'] : $paths->nslist[$this->namespace] . htmlspecialchars( str_replace('_', ' ', dirtify_page_id( $this->page_id ) ) );
  1159 				$title = ( isset($page_data['name']) ) ? $page_data['name'] : $paths->nslist[$this->namespace] . htmlspecialchars( str_replace('_', ' ', dirtify_page_id( $this->page_id ) ) );
  1160         $b = '<a href="' . $url . '">' . $title . '</a>';
  1160 				$b = '<a href="' . $url . '">' . $title . '</a>';
  1161         
  1161 				
  1162         $ob .= '<small>' . $lang->get('page_msg_redirected_from_to', array('from' => $a, 'to' => $b)) . '<br /></small>';
  1162 				$ob .= '<small>' . $lang->get('page_msg_redirected_from_to', array('from' => $a, 'to' => $b)) . '<br /></small>';
  1163       }
  1163 			}
  1164     }
  1164 		}
  1165     
  1165 		
  1166     $email_link = $email->encryptEmail(getConfig('contact_email'), '', '', $lang->get('page_err_access_denied_siteadmin'));
  1166 		$email_link = $email->encryptEmail(getConfig('contact_email'), '', '', $lang->get('page_err_access_denied_siteadmin'));
  1167     
  1167 		
  1168     $ob .= "<h3>" . $lang->get('page_err_access_denied_title') . "</h3>";
  1168 		$ob .= "<h3>" . $lang->get('page_err_access_denied_title') . "</h3>";
  1169     $ob .= "<p>" . $lang->get('page_err_access_denied_body', array('site_administration' => $email_link)) . "</p>";
  1169 		$ob .= "<p>" . $lang->get('page_err_access_denied_body', array('site_administration' => $email_link)) . "</p>";
  1170     
  1170 		
  1171     if ( $this->send_headers )
  1171 		if ( $this->send_headers )
  1172     {
  1172 		{
  1173       $ob .= $template->getFooter();
  1173 			$ob .= $template->getFooter();
  1174     }
  1174 		}
  1175     echo $ob;
  1175 		echo $ob;
  1176   }
  1176 	}
  1177   
  1177 	
  1178   /**
  1178 	/**
  1179    * Inform the user of an incorrect or absent password
  1179  	* Inform the user of an incorrect or absent password
  1180    * @access private
  1180  	* @access private
  1181    */
  1181  	*/
  1182    
  1182  	
  1183   function err_wrong_password()
  1183 	function err_wrong_password()
  1184   {
  1184 	{
  1185     global $db, $session, $paths, $template, $plugins; // Common objects
  1185 		global $db, $session, $paths, $template, $plugins; // Common objects
  1186     global $lang;
  1186 		global $lang;
  1187     
  1187 		
  1188     $title = $lang->get('page_msg_passrequired_title');
  1188 		$title = $lang->get('page_msg_passrequired_title');
  1189     $message = ( empty($this->password) ) ?
  1189 		$message = ( empty($this->password) ) ?
  1190                  '<p>' . $lang->get('page_msg_passrequired') . '</p>' :
  1190  								'<p>' . $lang->get('page_msg_passrequired') . '</p>' :
  1191                  '<p>' . $lang->get('page_msg_pass_wrong') . '</p>';
  1191  								'<p>' . $lang->get('page_msg_pass_wrong') . '</p>';
  1192     $message .= '<form action="' . makeUrlNS($this->namespace, $this->page_id) . '" method="post">
  1192 		$message .= '<form action="' . makeUrlNS($this->namespace, $this->page_id) . '" method="post">
  1193                    <p>
  1193  									<p>
  1194                      <label>' . $lang->get('page_lbl_password') . ' <input name="pagepass" type="password" /></label>&nbsp;&nbsp;<input type="submit" value="' . $lang->get('page_btn_password_submit') . '" />
  1194  										<label>' . $lang->get('page_lbl_password') . ' <input name="pagepass" type="password" /></label>&nbsp;&nbsp;<input type="submit" value="' . $lang->get('page_btn_password_submit') . '" />
  1195                    </p>
  1195  									</p>
  1196                  </form>';
  1196  								</form>';
  1197     if ( $this->send_headers )
  1197 		if ( $this->send_headers )
  1198     {
  1198 		{
  1199       $template->tpl_strings['PAGE_NAME'] = $title;
  1199 			$template->tpl_strings['PAGE_NAME'] = $title;
  1200       $template->header();
  1200 			$template->header();
  1201       echo "$message";
  1201 			echo "$message";
  1202       $template->footer();
  1202 			$template->footer();
  1203     }
  1203 		}
  1204     else
  1204 		else
  1205     {
  1205 		{
  1206       echo "<h2>$title</h2>
  1206 			echo "<h2>$title</h2>
  1207             $message";
  1207 						$message";
  1208     }
  1208 		}
  1209   }
  1209 	}
  1210   
  1210 	
  1211   /**
  1211 	/**
  1212    * Send the error message to the user complaining that there weren't any rows.
  1212  	* Send the error message to the user complaining that there weren't any rows.
  1213    * @access private
  1213  	* @access private
  1214    */
  1214  	*/
  1215   
  1215 	
  1216   function err_no_rows()
  1216 	function err_no_rows()
  1217   {
  1217 	{
  1218     global $db, $session, $paths, $template, $plugins; // Common objects
  1218 		global $db, $session, $paths, $template, $plugins; // Common objects
  1219     
  1219 		
  1220     $title = 'No text rows';
  1220 		$title = 'No text rows';
  1221     $message = 'While the page\'s existence was verified, there were no rows in the database that matched the query for the text. This may indicate a bug with the software; ask the webmaster for more information. The offending query was:<pre>' . $db->latest_query . '</pre>';
  1221 		$message = 'While the page\'s existence was verified, there were no rows in the database that matched the query for the text. This may indicate a bug with the software; ask the webmaster for more information. The offending query was:<pre>' . $db->latest_query . '</pre>';
  1222     if ( $this->send_headers )
  1222 		if ( $this->send_headers )
  1223     {
  1223 		{
  1224       $template->tpl_strings['PAGE_NAME'] = $title;
  1224 			$template->tpl_strings['PAGE_NAME'] = $title;
  1225       $template->header();
  1225 			$template->header();
  1226       echo "<p>$message</p>";
  1226 			echo "<p>$message</p>";
  1227       $template->footer();
  1227 			$template->footer();
  1228     }
  1228 		}
  1229     else
  1229 		else
  1230     {
  1230 		{
  1231       echo "<h2>$title</h2>
  1231 			echo "<h2>$title</h2>
  1232             <p>$message</p>";
  1232 						<p>$message</p>";
  1233     }
  1233 		}
  1234   }
  1234 	}
  1235   
  1235 	
  1236   /**
  1236 	/**
  1237    * Send an error message and die. For debugging or critical technical errors only - nothing that would under normal circumstances be shown to the user.
  1237  	* Send an error message and die. For debugging or critical technical errors only - nothing that would under normal circumstances be shown to the user.
  1238    * @param string Error message
  1238  	* @param string Error message
  1239    * @param bool If true, send DBAL's debugging information as well
  1239  	* @param bool If true, send DBAL's debugging information as well
  1240    */
  1240  	*/
  1241    
  1241  	
  1242   function send_error($message, $sql = false)
  1242 	function send_error($message, $sql = false)
  1243   {
  1243 	{
  1244     global $db, $session, $paths, $template, $plugins; // Common objects
  1244 		global $db, $session, $paths, $template, $plugins; // Common objects
  1245     global $lang;
  1245 		global $lang;
  1246     
  1246 		
  1247     $content = "<p>$message</p>";
  1247 		$content = "<p>$message</p>";
  1248     $template->tpl_strings['PAGE_NAME'] = $lang->get('page_msg_general_error');
  1248 		$template->tpl_strings['PAGE_NAME'] = $lang->get('page_msg_general_error');
  1249     
  1249 		
  1250     if ( $this->debug['works'] )
  1250 		if ( $this->debug['works'] )
  1251     {
  1251 		{
  1252       $content .= $this->debug['backtrace'];
  1252 			$content .= $this->debug['backtrace'];
  1253     }
  1253 		}
  1254     
  1254 		
  1255     header('HTTP/1.1 500 Internal Server Error');
  1255 		header('HTTP/1.1 500 Internal Server Error');
  1256     
  1256 		
  1257     $template->header();
  1257 		$template->header();
  1258     echo $content;
  1258 		echo $content;
  1259     $template->footer();
  1259 		$template->footer();
  1260     
  1260 		
  1261     $db->close();
  1261 		$db->close();
  1262     
  1262 		
  1263     exit;
  1263 		exit;
  1264     
  1264 		
  1265   }
  1265 	}
  1266   
  1266 	
  1267   /**
  1267 	/**
  1268    * Raises an error.
  1268  	* Raises an error.
  1269    * @param string Error string
  1269  	* @param string Error string
  1270    */
  1270  	*/
  1271    
  1271  	
  1272   function raise_error($string)
  1272 	function raise_error($string)
  1273   {
  1273 	{
  1274     if ( !is_string($string) )
  1274 		if ( !is_string($string) )
  1275       return false;
  1275 			return false;
  1276     $this->_errors[] = $string;
  1276 		$this->_errors[] = $string;
  1277   }
  1277 	}
  1278   
  1278 	
  1279   /**
  1279 	/**
  1280    * Retrieves the latest error from the error stack and returns it ('pops' the error stack)
  1280  	* Retrieves the latest error from the error stack and returns it ('pops' the error stack)
  1281    * @return string
  1281  	* @return string
  1282    */
  1282  	*/
  1283   
  1283 	
  1284   function pop_error()
  1284 	function pop_error()
  1285   {
  1285 	{
  1286     if ( count($this->_errors) < 1 )
  1286 		if ( count($this->_errors) < 1 )
  1287       return false;
  1287 			return false;
  1288     return array_pop($this->_errors);
  1288 		return array_pop($this->_errors);
  1289   }
  1289 	}
  1290   
  1290 	
  1291 } // class PageProcessor
  1291 } // class PageProcessor
  1292 
  1292 
  1293 ?>
  1293 ?>