includes/pageprocess.php
changeset 1 fe660c52c48f
child 4 0b3a0aedfd53
equal deleted inserted replaced
0:902822492a68 1:fe660c52c48f
       
     1 <?php
       
     2 /*
       
     3  * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
       
     4  * Version 1.0 (Banshee)
       
     5  * pageprocess.php - intelligent retrieval of pages
       
     6  * Copyright (C) 2006-2007 Dan Fuhry
       
     7  *
       
     8  * This program is Free Software; you can redistribute and/or modify it under the terms of the GNU General Public License
       
     9  * as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
       
    10  *
       
    11  * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
       
    12  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
       
    13  */
       
    14 
       
    15 /**
       
    16  * Class to handle fetching page text (possibly from a cache) and formatting it.
       
    17  * @package Enano
       
    18  * @subpackage UI
       
    19  * @copyright 2007 Dan Fuhry
       
    20  * @license GNU General Public License <http://www.gnu.org/licenses/gpl.html>
       
    21  */
       
    22 
       
    23 class PageProcessor
       
    24 {
       
    25   
       
    26   /**
       
    27    * Page ID and namespace of the page handled by this instance
       
    28    * @var string
       
    29    */
       
    30   
       
    31   var $page_id;
       
    32   var $namespace;
       
    33   
       
    34   /**
       
    35    * Tracks if the page we're loading exists in the database or not.
       
    36    * @var bool
       
    37    */
       
    38   
       
    39   var $page_exists = false;
       
    40   
       
    41   /**
       
    42    * Permissions!
       
    43    * @var object
       
    44    */
       
    45   
       
    46   var $perms = null;
       
    47   
       
    48   /**
       
    49    * Switch to track if redirects are allowed. Defaults to true.
       
    50    * @var bool
       
    51    */
       
    52   
       
    53   var $allow_redir = true;
       
    54   
       
    55   /**
       
    56    * If this is set to true, this will call the header and footer funcs on $template when render() is called.
       
    57    * @var bool
       
    58    */
       
    59   
       
    60   var $send_headers = false;
       
    61   
       
    62   /**
       
    63    * Cache the fetched text so we don't fetch it from the DB twice.
       
    64    * @var string
       
    65    */
       
    66   
       
    67   var $text_cache = '';
       
    68   
       
    69   /**
       
    70    * Debugging information to track errors. You can set enable to false to disable sending debug information.
       
    71    * @var array
       
    72    */
       
    73   
       
    74   var $debug = array(
       
    75       'enable' => true,
       
    76       'works'  => false
       
    77     );
       
    78   
       
    79   /**
       
    80    * Constructor.
       
    81    * @param string The page ID (urlname) of the page
       
    82    * @param string The namespace of the page
       
    83    */
       
    84   
       
    85   function __construct( $page_id, $namespace )
       
    86   {
       
    87     global $db, $session, $paths, $template, $plugins; // Common objects
       
    88     
       
    89     // See if we can get some debug info
       
    90     if ( function_exists('debug_backtrace') && $this->debug['enable'] )
       
    91     {
       
    92       $this->debug['works'] = true;
       
    93       $this->debug['backtrace'] = enano_debug_print_backtrace(true);
       
    94     }
       
    95     
       
    96     // First things first - check page existence and permissions
       
    97     
       
    98     if ( !isset($paths->nslist[$namespace]) )
       
    99     {
       
   100       $this->send_error('The namespace "' . htmlspecialchars($namespace) . '" does not exist.');
       
   101     }
       
   102     
       
   103     $this->_setup( $page_id, $namespace );
       
   104     
       
   105   }
       
   106   
       
   107   /**
       
   108    * The main method to send the page content. Also responsible for checking permissions.
       
   109    */
       
   110   
       
   111   function send()
       
   112   {
       
   113     global $db, $session, $paths, $template, $plugins; // Common objects
       
   114     if ( !$this->perms->get_permissions('read') )
       
   115     {
       
   116       $this->err_access_denied();
       
   117       return false;
       
   118     }
       
   119     if ( $this->namespace == 'Special' || $this->namespace == 'Admin' )
       
   120     {
       
   121       if ( !$this->page_exists )
       
   122       {
       
   123         redirect( makeUrl(getConfig('main_page')), 'Can\'t find special page', 'The special or administration page you requested does not exist. You will now be transferred to the main page.', 2 );
       
   124       }
       
   125       $func_name = "page_{$this->namespace}_{$this->page_id}";
       
   126       if ( function_exists($func_name) )
       
   127       {
       
   128         return @call_user_func($func_name);
       
   129       }
       
   130       else
       
   131       {
       
   132         $title = 'Page backend not found';
       
   133         $message = "The administration page you are looking for was properly registered using the page API, but the backend function
       
   134                     (<tt>$fname</tt>) was not found. If this is a plugin page, then this is almost certainly a bug with the plugin.";
       
   135                     
       
   136         if ( $this->send_headers )
       
   137         {
       
   138           $template->tpl_strings['PAGE_NAME'] = $title;
       
   139           $template->header();
       
   140           echo "<p>$message</p>";
       
   141           $template->footer();
       
   142         }
       
   143         else
       
   144         {
       
   145           echo "<h2>$title</h2>
       
   146                 <p>$message</p>";
       
   147         }
       
   148         return false;
       
   149       }
       
   150     }
       
   151     else if ( in_array($this->namespace, array('Article', 'User', 'Project', 'Help', 'File', 'Category')) && $this->page_exists )
       
   152     {
       
   153       // Send as regular page
       
   154       $text = $this->fetch_text();
       
   155       if ( $text == 'err_no_text_rows' )
       
   156       {
       
   157         $this->err_no_rows();
       
   158         return false;
       
   159       }
       
   160       else
       
   161       {
       
   162         $this->render();
       
   163       }
       
   164     }
       
   165     else if ( ( $this->namespace == 'Template' || $this->namespace == 'System' ) && $this->page_exists )
       
   166     {
       
   167       $this->header();
       
   168       
       
   169       $text = $this->fetch_text();
       
   170       $text = preg_replace('/<noinclude>(.*?)<\/noinclude>/is', '\\1', $text);
       
   171       $text = preg_replace('/<nodisplay>(.*?)<\/nodisplay>/is', '', $text);
       
   172       
       
   173       $text = RenderMan::render( $text );
       
   174       
       
   175       echo $text;
       
   176       
       
   177       $this->footer();
       
   178       
       
   179     }
       
   180     else if ( !$this->page_exists )
       
   181     {
       
   182       // Perhaps this is hooked?
       
   183       ob_start();
       
   184       
       
   185       $code = $plugins->setHook('page_not_found');
       
   186       foreach ( $code as $cmd )
       
   187       {
       
   188         eval($cmd);
       
   189       }
       
   190       
       
   191       $ob = ob_get_contents();
       
   192       
       
   193       if ( empty($ob) )
       
   194       {
       
   195         $this->err_page_not_existent();
       
   196       }
       
   197       
       
   198     }
       
   199     
       
   200   }
       
   201   
       
   202   /**
       
   203    * Sets internal variables.
       
   204    * @access private
       
   205    */
       
   206   
       
   207   function _setup($page_id, $namespace)
       
   208   {
       
   209     global $db, $session, $paths, $template, $plugins; // Common objects
       
   210     
       
   211     $page_id_cleaned = sanitize_page_id($page_id);
       
   212     
       
   213     $this->page_id = $page_id_cleaned;
       
   214     $this->namespace = $namespace;
       
   215     
       
   216     $this->perms = $session->fetch_page_acl( $page_id, $namespace );
       
   217     
       
   218     // Exception for Admin: pages
       
   219     if ( $this->namespace == 'Admin' )
       
   220     {
       
   221       $fname = "page_Admin_{$this->page_id}";
       
   222     }
       
   223     
       
   224     // Does the page "exist"?
       
   225     if ( $paths->cpage['urlname_nons'] == $page_id && $paths->namespace == $namespace && !$paths->page_exists && ( $this->namespace == 'Admin' && !function_exists($fname) ) )
       
   226     {
       
   227       $this->page_exists = false;
       
   228     }
       
   229     else if ( !isset( $paths->pages[ $paths->nslist[$namespace] . $page_id ] ) && ( $this->namespace == 'Admin' && !function_exists($fname) ) )
       
   230     {
       
   231       $this->page_exists = false;
       
   232     }
       
   233     else
       
   234     {
       
   235       $this->page_exists = true;
       
   236     }
       
   237   }
       
   238   
       
   239   /**
       
   240    * Renders it all in one go, and echoes it out. This assumes that the text is in the DB.
       
   241    * @access private
       
   242    */
       
   243   
       
   244   function render()
       
   245   {
       
   246     $text = $this->fetch_text();
       
   247     
       
   248     $this->header();
       
   249     display_page_headers();
       
   250     echo RenderMan::render($text);
       
   251     display_page_footers();
       
   252     $this->footer();
       
   253   }
       
   254   
       
   255   /**
       
   256    * Sends the page header, dependent on, of course, whether we're supposed to.
       
   257    */
       
   258   
       
   259   function header()
       
   260   {
       
   261     global $db, $session, $paths, $template, $plugins; // Common objects
       
   262     if ( $this->send_headers )
       
   263       $template->header();
       
   264   }
       
   265   
       
   266   /**
       
   267    * Sends the page footer, dependent on, of course, whether we're supposed to.
       
   268    */
       
   269   
       
   270   function footer()
       
   271   {
       
   272     global $db, $session, $paths, $template, $plugins; // Common objects
       
   273     if ( $this->send_headers )
       
   274       $template->footer();
       
   275   }
       
   276   
       
   277   /**
       
   278    * Fetches the raw, unfiltered page text.
       
   279    * @access public
       
   280    */
       
   281   
       
   282   function fetch_text()
       
   283   {
       
   284     global $db, $session, $paths, $template, $plugins; // Common objects
       
   285     
       
   286     if ( !empty($this->text_cache) )
       
   287     {
       
   288       return $this->text_cache;
       
   289     }
       
   290     
       
   291     $q = $db->sql_query('SELECT page_text, char_tag FROM '.table_prefix.'page_text WHERE page_id=\'' . $this->page_id . '\' AND namespace=\'' . $this->namespace . '\';');
       
   292     if ( !$q )
       
   293     {
       
   294       $this->send_error('Error during SQL query.', true);
       
   295     }
       
   296     if ( $db->numrows() < 1 )
       
   297     {
       
   298       $this->page_exists = false;
       
   299       return 'err_no_text_rows';
       
   300     }
       
   301     
       
   302     $row = $db->fetchrow();
       
   303     $db->free_result();
       
   304     
       
   305     if ( !empty($row['char_tag']) )
       
   306     {
       
   307       // This page text entry uses the old text-escaping format
       
   308       $from = array(
       
   309           "{APOS:{$row['char_tag']}}",
       
   310           "{QUOT:{$row['char_tag']}}",
       
   311           "{SLASH:{$row['char_tag']}}"
       
   312         );
       
   313       $to = array("'", '"',  '\\');
       
   314       $row['page_text'] = str_replace($from, $to, $row['page_text']);
       
   315     }
       
   316     
       
   317     $this->text_cache = $row['page_text'];
       
   318     
       
   319     return $row['page_text'];
       
   320     
       
   321   }
       
   322   
       
   323   /**
       
   324    * Send the error message to the user that the access to this page is denied.
       
   325    * @access private
       
   326    */
       
   327   
       
   328   function err_access_denied()
       
   329   {
       
   330     global $db, $session, $paths, $template, $plugins; // Common objects
       
   331     
       
   332     $ob = '';
       
   333     $template->tpl_strings['PAGE_NAME'] = 'Access denied';
       
   334       
       
   335     if ( $this->send_headers )
       
   336     {
       
   337       $ob .= $template->getHeader();
       
   338     }
       
   339     
       
   340     $ob .= '<div class="error-box"><b>Access to this page is denied.</b><br />This may be because you are not logged in or you have not met certain criteria for viewing this page.</div>';
       
   341     
       
   342     if ( $this->send_headers )
       
   343     {
       
   344       $ob .= $template->getFooter();
       
   345     }
       
   346     echo $ob;
       
   347   }
       
   348   
       
   349   /**
       
   350    * Send the error message to the user complaining that there weren't any rows.
       
   351    * @access private
       
   352    */
       
   353   
       
   354   function err_no_rows()
       
   355   {
       
   356     global $db, $session, $paths, $template, $plugins; // Common objects
       
   357     
       
   358     $title = 'No text rows';
       
   359     $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>';
       
   360     if ( $this->send_headers )
       
   361     {
       
   362       $template->tpl_strings['PAGE_NAME'] = $title;
       
   363       $template->header();
       
   364       echo "<p>$message</p>";
       
   365       $template->footer();
       
   366     }
       
   367     else
       
   368     {
       
   369       echo "<h2>$title</h2>
       
   370             <p>$message</p>";
       
   371     }
       
   372   }
       
   373   
       
   374   /**
       
   375    * Tell the user the page doesn't exist, and present them with their options.
       
   376    * @access private
       
   377    */
       
   378    
       
   379   function err_page_not_existent()
       
   380   {
       
   381     global $db, $session, $paths, $template, $plugins; // Common objects
       
   382     
       
   383     $this->header();
       
   384     header('HTTP/1.1 404 Not Found');
       
   385     echo '<h3>There is no page with this title yet.</h3>
       
   386            <p>You have requested a page that doesn\'t exist yet.';
       
   387     if ( $session->get_permissions('create_page') )
       
   388     {
       
   389       echo ' You can <a href="'.makeUrlNS($this->namespace, $this->page_id, 'do=edit', true).'" onclick="ajaxEditor(); return false;">create this page</a>, or return to the <a href="'.makeUrl(getConfig('main_page')).'">homepage</a>.';
       
   390     }
       
   391     else
       
   392     {
       
   393       echo ' Return to the <a href="'.makeUrl(getConfig('main_page')).'">homepage</a>.</p>';
       
   394     }
       
   395     if ( $session->get_permissions('history_rollback') )
       
   396     {
       
   397       $e = $db->sql_query('SELECT * FROM ' . table_prefix . 'logs WHERE action=\'delete\' AND page_id=\'' . $this->page_id . '\' AND namespace=\'' . $this->namespace . '\' ORDER BY time_id DESC;');
       
   398       if ( !$e )
       
   399       {
       
   400         $db->_die('The deletion log could not be selected.');
       
   401       }
       
   402       if ( $db->numrows() > 0 )
       
   403       {
       
   404         $r = $db->fetchrow();
       
   405         echo '<p>This page also appears to have some log entries in the database - it seems that it was deleted on ' . $r['date_string'] . '. You can probably <a href="'.makeUrl($paths->page, 'do=rollback&amp;id='.$r['time_id']).'" onclick="ajaxRollback(\''.$r['time_id'].'\'); return false;">roll back</a> the deletion.</p>';
       
   406       }
       
   407       $db->free_result();
       
   408     }
       
   409     echo '<p>
       
   410             HTTP Error: 404 Not Found
       
   411           </p>';
       
   412     $this->footer();
       
   413   }
       
   414   
       
   415   /**
       
   416    * PHP 4 constructor.
       
   417    * @see PageProcessor::__construct()
       
   418    */
       
   419   
       
   420   function PageProcessor( $page_id, $namespace )
       
   421   {
       
   422     $this->__construct($page_id, $namespace);
       
   423   }
       
   424   
       
   425   /**
       
   426    * Send an error message and die
       
   427    * @var string Error message
       
   428    * @var bool If true, send DBAL's debugging information as well
       
   429    */
       
   430    
       
   431   function send_error($message, $sql = false)
       
   432   {
       
   433     global $db, $session, $paths, $template, $plugins; // Common objects
       
   434     
       
   435     $content = "<p>$message</p>";
       
   436     $template->tpl_strings['PAGE_NAME'] = 'General error in page fetcher';
       
   437     
       
   438     if ( $this->debug['works'] )
       
   439     {
       
   440       $content .= $this->debug['backtrace'];
       
   441     }
       
   442     
       
   443     header('HTTP/1.1 500 Internal Server Error');
       
   444     
       
   445     $template->header();
       
   446     echo $content;
       
   447     $template->footer();
       
   448     
       
   449     $db->close();
       
   450     
       
   451     exit;
       
   452     
       
   453   }
       
   454   
       
   455 } // class PageProcessor
       
   456 
       
   457 ?>