diff -r de56132c008d -r bdac73ed481e includes/namespaces/default.php --- a/includes/namespaces/default.php Sun Mar 28 21:49:26 2010 -0400 +++ b/includes/namespaces/default.php Sun Mar 28 23:10:46 2010 -0400 @@ -21,1013 +21,1013 @@ class Namespace_Default { - /** - * Page ID - * @var string - */ - - public $page_id; - - /** - * Namespace - * @var string - */ - - public $namespace; - - /** - * Local copy of the page text - */ - - public $text_cache; - - /** - * Revision ID to send. If 0, the latest revision. - * @var int - */ - - public $revision_id = 0; - - /** - * Tracks whether the page exists - * @var bool - */ - - public $exists = false; - - /** - * Page title - * @var string - */ - - public $title = ''; - - /** - * PathManager info array ("cdata") for this page. (The one with urlname, name, namespace, delvotes, delvote_ips, protected, visible, etc.) - * @var array - */ - - public $cdata = array(); - - /** - * ACL calculation instance for this page. - * @var object(Session_ACLPageInfo) - */ - - public $perms = false; - - /** - * Protection calculation - * @var bool - */ - - public $page_protected = false; - - /** - * Wiki mode calculation - * @var bool - */ - - public $wiki_mode = false; - - /** - * Page conditions. These represent the final decision as to whether an action is allowed or not. They are set to true if ACLs permit AND if - * the action "makes sense." (e.g., you can't vote to delete a non-wikimode page.) - * @var array - */ - - public $conds = array(); - - /** - * Constructor. - */ - - public function __construct($page_id, $namespace, $revision_id = 0) - { - global $db, $session, $paths, $template, $plugins; // Common objects - - $this->page_id = sanitize_page_id($page_id); - $this->namespace = $namespace; - $this->revision_id = intval($revision_id); - - // grab the cdata - $this->build_cdata(); - - $this->page_protected = $this->cdata['really_protected'] ? true : false; - switch($this->cdata['wiki_mode']) - { - case 0: $this->wiki_mode = false; break; - case 1: $this->wiki_mode = true; break; - default: case 2: $this->wiki_mode = getConfig('wiki_mode') == 1; break; - } - } - - /** - * Build the page's cdata. - */ - - public function build_cdata() - { - global $db, $session, $paths, $template, $plugins; // Common objects - static $cdata_cache = array(); - $pathskey = $paths->get_pathskey($this->page_id, $this->namespace); - if ( isset($cdata_cache[$pathskey]) ) - { - $this->cdata = $cdata_cache[$pathskey]; - $this->exists = $cdata_cache[$pathskey]['page_exists']; - $this->title = $cdata_cache[$pathskey]['name']; - return null; - } - - $this->exists = false; - $ns_char = substr($paths->nslist['Special'], -1); - $page_name = $this->namespace == 'Article' ? dirtify_page_id($this->page_id) : "{$this->namespace}{$ns_char}" . dirtify_page_id($this->page_id); - $page_name = str_replace('_', ' ', $page_name); - $this->title = $page_name; - - $this->cdata = array( - 'name' => $page_name, - 'urlname' => $this->page_id, - 'namespace' => $this->namespace, - 'special' => 0, - 'visible' => 0, - 'comments_on' => 1, - 'protected' => 0, - 'delvotes' => 0, - 'delvote_ips' => '', - 'wiki_mode' => 2, - 'page_exists' => false, - 'page_format' => getConfig('default_page_format', 'wikitext') - ); - - if ( $data_from_db = Namespace_Default::get_cdata_from_db($this->page_id, $this->namespace) ) - { - $this->exists = true; - $this->cdata = $data_from_db; - $this->cdata['page_exists'] = true; - $this->title = $this->cdata['name']; - } - - $this->cdata = Namespace_Default::bake_cdata($this->cdata); - - $cdata_cache[$pathskey] = $this->cdata; - } - - /** - * Pulls the page's actual text from the database. - */ - - function fetch_text() - { - global $db, $session, $paths, $template, $plugins; // Common objects - - if ( !empty($this->text_cache) ) - { - return $this->text_cache; - } - - if ( $this->revision_id > 0 && is_int($this->revision_id) ) - { - - $q = $db->sql_query('SELECT page_text, char_tag, time_id FROM '.table_prefix.'logs WHERE log_type=\'page\' AND action=\'edit\' AND page_id=\'' . $this->page_id . '\' AND namespace=\'' . $this->namespace . '\' AND log_id=' . $this->revision_id . ';'); - if ( !$q ) - { - $this->send_error('Error during SQL query.', true); - } - if ( $db->numrows() < 1 ) - { - // Compatibility fix for old pages with dots in the page ID - if ( strstr($this->page_id, '.2e') ) - { - $db->free_result(); - $page_id = str_replace('.2e', '.', $this->page_id); - $q = $db->sql_query('SELECT page_text, char_tag, time_id FROM '.table_prefix.'logs WHERE log_type=\'page\' AND action=\'edit\' AND page_id=\'' . $page_id . '\' AND namespace=\'' . $this->namespace . '\' AND log_id=' . $this->revision_id . ';'); - if ( !$q ) - { - $this->send_error('Error during SQL query.', true); - } - if ( $db->numrows() < 1 ) - { - $this->page_exists = false; - return 'err_no_text_rows'; - } - } - else - { - $this->page_exists = false; - return 'err_no_text_rows'; - } - } - else - { - $row = $db->fetchrow(); - } - - $db->free_result(); - - } - else - { - $q = $db->sql_query('SELECT t.page_text, t.char_tag, l.time_id FROM '.table_prefix."page_text AS t\n" - . " LEFT JOIN " . table_prefix . "logs AS l\n" - . " ON ( l.page_id = t.page_id AND l.namespace = t.namespace )\n" - . " WHERE t.page_id='$this->page_id' AND t.namespace='$this->namespace'\n" - . " ORDER BY l.time_id DESC LIMIT 1;"); - if ( !$q ) - { - $this->send_error('Error during SQL query.', true); - } - if ( $db->numrows() < 1 ) - { - // Compatibility fix for old pages with dots in the page ID - if ( strstr($this->page_id, '.2e') ) - { - $db->free_result(); - $page_id = str_replace('.2e', '.', $this->page_id); - $q = $db->sql_query('SELECT page_text, char_tag FROM '.table_prefix.'page_text WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $this->namespace . '\';'); - if ( !$q ) - { - $this->send_error('Error during SQL query.', true); - } - if ( $db->numrows() < 1 ) - { - $this->page_exists = false; - return 'err_no_text_rows'; - } - } - else - { - $this->page_exists = false; - return 'err_no_text_rows'; - } - } - - $row = $db->fetchrow(); - $db->free_result(); - - } - - if ( !empty($row['char_tag']) ) - { - // This page text entry uses the old text-escaping format - $from = array( - "{APOS:{$row['char_tag']}}", - "{QUOT:{$row['char_tag']}}", - "{SLASH:{$row['char_tag']}}" - ); - $to = array("'", '"', '\\'); - $row['page_text'] = str_replace($from, $to, $row['page_text']); - } - - $this->text_cache = $row['page_text']; - - if ( isset($row['time_id']) ) - { - $this->revision_time = intval($row['time_id']); - } - - return $row['page_text']; - } - - /** - * Send the page. - */ - - public function send() - { - global $db, $session, $paths, $template, $plugins; // Common objects - global $output; - - $output->add_before_footer($this->display_categories()); - - if ( $this->exists ) - $this->send_from_db(); - else - { - // This is the DEPRECATED way to extend namespaces. It's left in only for compatibility with older plugins. - ob_start(); - $code = $plugins->setHook('page_not_found'); - foreach ( $code as $cmd ) - { - eval($cmd); - } - $c = ob_get_contents(); - if ( !empty($c) ) - { - ob_end_clean(); - echo $c; - } - else - { - $output->header(); - $this->error_404(); - $output->footer(); - } - } - } - - /** - * Get a redirect, if there is one. - * @return mixed Array: Page ID and namespace, associative; bool: false (no redirect) - */ - - public function get_redirect() - { - $text = $this->fetch_text(); - if ( preg_match('/^#redirect \[\[([^\]]+?)\]\]/i', $text, $match ) ) - { - list($page_id, $namespace) = RenderMan::strToPageID($match[1]); - return array( - 'page_id' => $page_id, - 'namespace' => $namespace - ); - } - return false; - } - - /** - * The "real" send-the-page function. The reason for this is so other namespaces can re-use the code - * to fetch the page from the DB while being able to install their own wrappers. - */ - - public function send_from_db($incl_inner_headers = true, $send_headers = true) - { - global $db, $session, $paths, $template, $plugins; // Common objects - global $lang; - global $output; - - $text = $this->fetch_text(); - - profiler_log("Namespace [$this->namespace, $this->page_id]: pulled text from DB"); - - $text = preg_replace('/([\s]*)__NOBREADCRUMBS__([\s]*)/', '', $text); - $text = preg_replace('/([\s]*)__NOTOC__([\s]*)/', '', $text); - $text = preg_replace('/^#redirect \[\[.+?\]\]\s*/i', '', $text); - - if ( $send_headers ) - { - $output->set_title($this->title); - $output->header(); - } - $this->do_breadcrumbs(); - - if ( $incl_inner_headers ) - { - if ( !$this->perms ) - $this->perms = $session->fetch_page_acl($this->page_id, $this->namespace); - - if ( $this->perms->get_permissions('vote_reset') && $this->cdata['delvotes'] > 0) - { - $delvote_ips = unserialize($this->cdata['delvote_ips']); - $hr = htmlspecialchars(implode(', ', $delvote_ips['u'])); - - $string_id = ( $this->cdata['delvotes'] == 1 ) ? 'delvote_lbl_votes_one' : 'delvote_lbl_votes_plural'; - $string = $lang->get($string_id, array('num_users' => $this->cdata['delvotes'])); - - echo '
- ' . $lang->get('etc_lbl_notice') . ' ' . $string . '
- ' . $lang->get('delvote_lbl_users_that_voted') . ' ' . $hr . '
- ' . $lang->get('delvote_btn_deletepage') . ' | ' . $lang->get('delvote_btn_resetvotes') . ' -
'; - } - } - - if ( $this->revision_id ) - { - echo '
- ' . $lang->get('page_msg_archived_title') . '
- ' . $lang->get('page_msg_archived_body', array( - 'archive_date' => enano_date(ED_DATE, $this->revision_time), - 'archive_time' => enano_date(ED_TIME, $this->revision_time), - 'current_link' => makeUrlNS($this->namespace, $this->page_id), - 'restore_link' => makeUrlNS($this->namespace, $this->page_id, 'do=edit&revid='.$this->revision_id), - 'restore_onclick' => 'ajaxEditor(\''.$this->revision_id.'\'); return false;', - )) . ' -
'; - $q = $db->sql_query('SELECT page_format FROM ' . table_prefix . "logs WHERE log_id = {$this->revision_id};"); - if ( !$q ) - $db->_die(); - - list($page_format) = $db->fetchrow_num(); - $db->free_result(); - } - else - { - $page_format = $this->cdata['page_format']; - } - - $code = $plugins->setHook('pageprocess_render_head'); - foreach ( $code as $cmd ) - { - eval($cmd); - } - - $prof_contentevent = profiler_log("Namespace [$this->namespace, $this->page_id]: headers and preprocessing done - about to send content"); - - if ( $incl_inner_headers ) - { - if ( $page_format === 'wikitext' ) - { - $text = '?>' . RenderMan::render($text); - } - else - { - // Page format is XHTML. This means we want to disable functionality that MCE takes care of, while still retaining - // the ability to wikilink, the ability to use images, etc. Basically, RENDER_INLINEONLY disables all behavior in - // the rendering engine/Text_Wiki that conflicts with MCE. - $text = '?>' . RenderMan::render($text, RENDER_INLINE); - } - } - else - { - $text = '?>' . $text; - $text = preg_replace('/(.*?)<\/nowiki>/s', '\\1', $text); - } - - eval ( $text ); - - profiler_log("Namespace [$this->namespace, $this->page_id]: content sent", true, $prof_contentevent); - - $code = $plugins->setHook('pageprocess_render_tail'); - foreach ( $code as $cmd ) - { - eval($cmd); - } - - if ( $incl_inner_headers ) - { - display_page_footers(); - } - - profiler_log("Namespace [$this->namespace, $this->page_id]: sent footers"); - - if ( $send_headers ) - $output->footer(); - } - - /** - * Echoes out breadcrumb data, if appropriate. - * @access private - */ - - function do_breadcrumbs() - { - global $db, $session, $paths, $template, $plugins; // Common objects - global $lang; - - if ( strpos($this->text_cache, '__NOBREADCRUMBS__') !== false ) - return false; - - $mode = getConfig('breadcrumb_mode'); - - if ( $mode == 'never' ) - // Breadcrumbs are disabled - return true; - - // Minimum depth for breadcrumb display - $threshold = ( $mode == 'always' ) ? 0 : 1; - - $breadcrumb_data = explode('/', $this->page_id); - if ( count($breadcrumb_data) > $threshold ) - { - // If we're not on a subpage of the main page, add "Home" to the list - $show_home = false; - if ( $mode == 'always' ) - { - $show_home = true; - } - echo ' - - - '; - } - } - - public function error_404() - { - global $db, $session, $paths, $template, $plugins; // Common objects - global $lang, $output; - - $userpage = $this->namespace == 'User'; - - @header('HTTP/1.1 404 Not Found'); - - $msg = ( $pp = $paths->sysmsg('Page_not_found') ) ? $pp : '{STANDARD404}'; - - $standard_404 = ''; - - if ( $userpage ) - { - $standard_404 .= '

' . $lang->get('page_msg_404_title_userpage') . '

-

' . $lang->get('page_msg_404_body_userpage'); - } - else - { - $standard_404 .= '

' . $lang->get('page_msg_404_title') . '

-

' . $lang->get('page_msg_404_body'); - } - if ( $session->get_permissions('create_page') ) - { - $standard_404 .= ' ' . $lang->get('page_msg_404_create', array( - 'create_flags' => 'href="'.makeUrlNS($this->namespace, $this->page_id, 'do=edit', true).'" onclick="ajaxEditor(); return false;"', - 'mainpage_link' => makeUrl(get_main_page(), false, true) - )); - } - else - { - $standard_404 .= ' ' . $lang->get('page_msg_404_gohome', array( - 'mainpage_link' => makeUrl(get_main_page(), false, true) - )); - } - $standard_404 .= '

'; - if ( $session->get_permissions('history_rollback') ) - { - $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;'); - if ( !$e ) - { - $db->_die('The deletion log could not be selected.'); - } - if ( $db->numrows() > 0 ) - { - $r = $db->fetchrow(); - $standard_404 .= '

' . $lang->get('page_msg_404_was_deleted', array( - 'delete_time' => enano_date(ED_DATE | ED_TIME, $r['time_id']), - 'delete_reason' => htmlspecialchars($r['edit_summary']), - 'rollback_flags' => 'href="'.makeUrl($paths->page, 'do=rollback&id='.$r['log_id']).'" onclick="ajaxRollback(\''.$r['log_id'].'\'); return false;"' - )) - . '

'; - if ( $session->user_level >= USER_LEVEL_ADMIN ) - { - $standard_404 .= '

' . $lang->get('page_msg_404_admin_opts', array( - 'detag_link' => makeUrl($paths->page, 'do=detag', true) - )) - . '

'; - } - } - $db->free_result(); - } - $standard_404 .= '

- ' . $lang->get('page_msg_404_http_response') . ' -

'; - - $parser = $template->makeParserText($msg); - $parser->assign_vars(array( - 'STANDARD404' => $standard_404 - )); - - $msg = RenderMan::render($parser->run()); - eval( '?>' . $msg ); - } - - /** - * Display the categories a page is in. If the current page is a category, its contents will also be printed. - */ - - function display_categories() - { - global $db, $session, $paths, $template, $plugins; // Common objects - global $lang; - - $html = ''; - - if ( $this->namespace == 'Category' ) - { - // Show member pages and subcategories - $q = $db->sql_query('SELECT p.urlname, p.namespace, p.name, p.namespace=\'Category\' AS is_category FROM '.table_prefix.'categories AS c - LEFT JOIN '.table_prefix.'pages AS p - ON ( p.urlname = c.page_id AND p.namespace = c.namespace ) - WHERE c.category_id=\'' . $db->escape($this->page_id) . '\' - ORDER BY is_category DESC, p.name ASC;'); - if ( !$q ) - { - $db->_die(); - } - $html .= '

' . $lang->get('onpage_cat_heading_subcategories') . '

'; - $html .= '
'; - $html .= ''; - $html .= ''; - $ticker = 0; - $counter = 0; - $switched = false; - $class = 'row1'; - while ( $row = $db->fetchrow($q) ) - { - if ( $row['is_category'] == 0 && !$switched ) - { - if ( $counter > 0 ) - { - // Fill-in - while ( $ticker < 3 ) - { - $ticker++; - $html .= ''; - } - } - else - { - $html .= ''; - } - $html .= '
' . $lang->get('onpage_cat_msg_no_subcategories') . '
' . "\n\n"; - $html .= '

' . $lang->get('onpage_cat_heading_pages') . '

'; - $html .= '
'; - $html .= ''; - $html .= ''; - $counter = 0; - $ticker = -1; - $switched = true; - } - $counter++; - $ticker++; - if ( $ticker == 3 ) - { - $html .= ''; - $ticker = 0; - $class = ( $class == 'row3' ) ? 'row1' : 'row3'; - } - $html .= ""; - } - if ( !$switched ) - { - if ( $counter > 0 ) - { - // Fill-in - while ( $ticker < 2 ) - { - $ticker++; - $html .= ''; - } - } - else - { - $html .= ''; - } - $html .= '
"; // " to workaround stupid jEdit bug - - $link = makeUrlNS($row['namespace'], sanitize_page_id($row['urlname'])); - $html .= 'nslist[$row['namespace']] . sanitize_page_id($row['urlname']); - if ( !isPage( $key ) ) - { - $html .= ' class="wikilink-nonexistent"'; - } - $html .= '>'; - $title = get_page_title_ns($row['urlname'], $row['namespace']); - $html .= htmlspecialchars($title); - $html .= ''; - - $html .= "' . $lang->get('onpage_cat_msg_no_subcategories') . '
' . "\n\n"; - $html .= '

' . $lang->get('onpage_cat_heading_pages') . '

'; - $html .= '
'; - $html .= ''; - $html .= ''; - $counter = 0; - $ticker = 0; - $switched = true; - } - if ( $counter > 0 ) - { - // Fill-in - while ( $ticker < 2 ) - { - $ticker++; - $html .= ''; - } - } - else - { - $html .= ''; - } - $html .= '
' . $lang->get('onpage_cat_msg_no_pages') . '
' . "\n\n"; - } - - if ( $this->namespace != 'Special' && $this->namespace != 'Admin' ) - { - $html .= '
'; - $html .= '
'; - $html .= '(' . $lang->get('tags_catbox_link') . ')'; - $html .= '
'; - $html .= '
' . $lang->get('catedit_catbox_lbl_categories') . ' '; - - $q = $db->sql_query('SELECT category_id FROM ' . table_prefix . "categories WHERE page_id = '$this->page_id' AND namespace = '$this->namespace';"); - if ( !$q ) - $db->_die(); - - if ( $row = $db->fetchrow() ) - { - $list = array(); - do - { - $cid = sanitize_page_id($row['category_id']); - $title = get_page_title_ns($cid, 'Category'); - $link = makeUrlNS('Category', $cid); - $list[] = '' . htmlspecialchars($title) . ''; - } - while ( $row = $db->fetchrow($q) ); - $html .= implode(', ', $list); - } - else - { - $html .= $lang->get('catedit_catbox_lbl_uncategorized'); - } - - $can_edit = ( $session->get_permissions('edit_cat') && ( !$paths->page_protected || $session->get_permissions('even_when_protected') ) ); - if ( $can_edit ) - { - $edit_link = '' . $lang->get('catedit_catbox_link_edit') . ''; - $html .= ' [ ' . $edit_link . ' ]'; - } - - $html .= '
'; - } - return $html; - } - - /** - * Pull in switches as to whether a specific toolbar button should be used or not. This sets things up according to the current page being displayed. - * @return array Associative - */ - - function set_conds() - { - global $db, $session, $paths, $template, $plugins; // Common objects - - if ( !$this->perms ) - $this->perms = $session->fetch_page_acl($this->page_id, $this->namespace); - - if ( !$this->perms ) - { - // We're trying to send a page WAY too early (session hasn't been started yet), such as for a redirect. Send a default set of conds because - // there's NO way to get permissions to determine anything otherwise. Yes, starting $session here might be dangerous. - $this->conds = array( - 'article' => true, - 'comments' => false, - 'edit' => false, - 'viewsource' => false, - 'history' => false, - 'rename' => false, - 'delvote' => false, - 'resetvotes' => false, - 'delete' => false, - 'printable' => false, - 'protect' => false, - 'setwikimode' => false, - 'clearlogs' => false, - 'password' => false, - 'acledit' => false, - 'adminpage' => false - ); - return $this->conds; - } - - // die('have perms:
' . print_r($this->perms, true) . "\n---------------------------------\nBacktrace:\n" . enano_debug_print_backtrace(true));
-    
-    $enforce_protection = ( $this->page_protected && ( ( $session->check_acl_scope('even_when_protected', $this->namespace) && !$this->perms->get_permissions('even_when_protected') ) || !$session->check_acl_scope('even_when_protected', $this->namespace) ) );
-    
-    $conds = array();
-    
-    // Article: always show
-    $conds['article'] = true;
-    
-    // Discussion: Show if comments are enabled on the site, and if comments are on for this page.
-    $conds['comments'] = $this->perms->get_permissions('read') && getConfig('enable_comments', '1')=='1' && $this->cdata['comments_on'] == 1;
-    
-    // Edit: Show if we have permission to edit the page, and if we don't have protection in effect
-    $conds['edit'] = $this->perms->get_permissions('read') && $session->check_acl_scope('edit_page', $this->namespace) && $this->perms->get_permissions('edit_page') && !$enforce_protection;
-    
-    // View source: Show if we have permission to view source and either ACLs prohibit editing or protection is in effect
-    $conds['viewsource'] = $session->check_acl_scope('view_source', $this->namespace) && $this->perms->get_permissions('view_source') && ( !$this->perms->get_permissions('edit_page') || $enforce_protection ) && $this->namespace != 'API';
-    
-    // History: Show if we have permission to see history and if the page exists
-    $conds['history'] = $session->check_acl_scope('history_view', $this->namespace) && $this->exists && $this->perms->get_permissions('history_view');
-    
-    // Rename: Show if the page exists, if we have permission to rename, and if protection isn't in effect
-    $conds['rename'] = $session->check_acl_scope('rename', $this->namespace) && $this->exists && $this->perms->get_permissions('rename') && !$enforce_protection;
-    
-    // Vote-to-delete: Show if we have Wiki Mode on, if we have permission to vote for deletion, and if the page exists (can't vote to delete a nonexistent page)
-    $conds['delvote'] = $this->wiki_mode && $session->check_acl_scope('vote_delete', $this->namespace) && $this->perms->get_permissions('vote_delete') && $this->exists;
-    
-    // Reset votes: Show if we have Wiki Mode on, if we have permission to reset votes, if the page exists, and if there's at least one vote
-    $conds['resetvotes'] = $session->check_acl_scope('vote_reset', $this->namespace) && $this->wiki_mode && $this->exists && $this->perms->get_permissions('vote_reset') && $this->cdata['delvotes'] > 0;
-    
-    // Delete page: Show if the page exists and if we have permission to delete it
-    $conds['delete'] = $session->check_acl_scope('delete_page', $this->namespace) && $this->exists && $this->perms->get_permissions('delete_page');
-    
-    // Printable view: Show if the page exists
-    $conds['printable'] = $this->exists;
-    
-    // Protect: Show if we have Wiki Mode on, if the page exists, and if we have permission to protect the page.
-    $conds['protect'] = $session->check_acl_scope('protect', $this->namespace) && $this->wiki_mode && $this->exists && $this->perms->get_permissions('protect');
-    
-    // Set Wiki Mode: Show if the page exists and if we have permission to set wiki mode
-    $conds['setwikimode'] = $session->check_acl_scope('set_wiki_mode', $this->namespace) && $this->exists && $this->perms->get_permissions('set_wiki_mode');
-    
-    // Clear logs: Show if we have permission to clear logs
-    $conds['clearlogs'] = $session->check_acl_scope('clear_logs', $this->namespace) && $this->perms->get_permissions('clear_logs');
-    
-    // Set password: a little bit complicated. If there's a password, check for password_reset; else, check for password_set.
-    $conds['password'] = empty($this->cdata['password']) ?
-                           $session->check_acl_scope('password_set', $this->namespace) && $this->perms->get_permissions('password_set') :
-                           $session->check_acl_scope('password_reset', $this->namespace) && $this->perms->get_permissions('password_reset');
-    
-    // Edit ACLs: Show if this is a non-Enano page that's calling the Enano API and (a) if we have permissions to edit ACLs or (b) we're an admin AND ACL_ALWAYS_ALLOW_ADMIN_EDIT_ACL is on
-    $conds['acledit'] = $this->namespace != 'API' && $session->check_acl_scope('edit_acl', $this->namespace) && ( $this->perms->get_permissions('edit_acl') || ( defined('ACL_ALWAYS_ALLOW_ADMIN_EDIT_ACL') &&  $session->user_level >= USER_LEVEL_ADMIN ) );
-    
-    // Admin page: Show if the page exists and if we're an admin
-    $conds['adminpage'] = $session->user_level >= USER_LEVEL_ADMIN && $this->exists;
-    
-    // Allow plugins to change stuff
-    $code = $plugins->setHook('page_conds_set');
-    foreach ( $code as $cmd )
-    {
-      eval($cmd);
-    }
-    
-    $this->conds = $conds;
-  }
-  
-  /**
-   * Return page conditions
-   * @return array
-   */
-  
-  public function get_conds()
-  {
-    if ( empty($this->conds) )
-      $this->set_conds();
-    
-    return $this->conds;
-  }
-  
-  /**
-   * Just tell us if the current page exists or not.
-   * @return bool
-   */
-   
-  public function exists()
-  {
-    return $this->exists;
-  }
-  
-  /**
-   * Return cdata
-   * @return array
-   */
-  
-  public function get_cdata()
-  {
-    return $this->cdata;
-  }
-  
-  /**
-   * Bake, or finalize the processing of, a cdata array.
-   * @static
-   * @access public
-   */
-  
-  public static function bake_cdata($cdata)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    // urlname_nons is the actual page_id.
-    $cdata['urlname_nons'] = $cdata['urlname'];
-    if ( isset($paths->nslist[ $cdata['namespace'] ]) )
-    {
-      $cdata['urlname'] = $paths->nslist[ $cdata['namespace'] ] . $cdata['urlname'];
-    }
-    else
-    {
-      $ns_char = substr($paths->nslist['Special'], -1);
-      $cdata['urlname'] = $cdata['namespace'] . $ns_char . $cdata['urlname'];
-    }
-    
-    // add missing keys
-    $defaults = array(
-      'special' => 0,
-      'visible' => 0,
-      'comments_on' => 1,
-      'protected' => 0,
-      'delvotes' => 0,
-      'delvote_ips' => serialize(array()),
-      'wiki_mode' => 2,
-      'page_format' => getConfig('default_page_format', 'wikitext')
-    );
-    foreach ( $defaults as $key => $value )
-    {
-      if ( !isset($cdata[$key]) )
-        $cdata[$key] = $value;
-    }
-    
-    // fix up deletion votes
-    if ( empty($cdata['delvotes']) )
-      $cdata['delvotes'] = 0;
-    
-    // fix up deletion vote IP list
-    if ( empty($cdata['delvote_ips']) )
-      $cdata['delvote_ips'] = serialize(array());
-    
-    // calculate wiki mode
-    $cdata['really_wiki_mode'] = ( $cdata['wiki_mode'] == 1 || ( $cdata['wiki_mode'] == 2 && getConfig('wiki_mode', 0) == 1 ) );
-    
-    // calculate protection
-    $cdata['really_protected'] = ( $cdata['protected'] > 0 );
-    if ( $cdata['protected'] == 2 )
-    {
-      $cdata['really_protected'] = !$session->user_logged_in || ( $session->user_logged_in && $session->reg_time + 86400*4 > time() );
-    }
-    
-    return $cdata;
-  }
-  
-  /**
-   * Grabs raw (unbaked) cdata from the database, caching if possible.
-   * @param string Page ID
-   * @param string Namespace.
-   * @static
-   */
-  
-  public static function get_cdata_from_db($page_id, $namespace)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    static $cache = array();
-    
-    $pathskey = $paths->get_pathskey($page_id, $namespace);
-    if ( isset($cache[$pathskey]) )
-      return $cache[$pathskey];
-    
-    $page_id_db = $db->escape($page_id);
-    $namespace_db = $db->escape($namespace);
-    
-    $q = $db->sql_query('SELECT p.*'
-                      . '    FROM ' . table_prefix . "pages AS p\n"
-                      . "  WHERE p.urlname = '$page_id_db' AND p.namespace = '$namespace_db'\n"
-                      . "    GROUP BY p.urlname, p.name, p.namespace, p.page_order, p.special, p.visible, p.protected, p.wiki_mode, p.comments_on, p.delvotes, p.delvote_ips, p.page_format, p.password;");
-    
-    if ( !$q )
-      $db->_die();
-    
-    if ( $db->numrows() < 1 )
-    {
-      $db->free_result();
-      $cache[$pathskey] = false;
-      return false;
-    }
-    
-    $row = $db->fetchrow();
-    
-    // Get comment counts
-    // FIXME: Apparently there's a bit of recursion in here. Fetching permissions depends on this cdata function.
-    // Perhaps we should eliminate session's dependency on cdata? (What is it used for?)
-    $q = $db->sql_query('SELECT approved FROM ' . table_prefix . "comments WHERE page_id = '$page_id_db' AND namespace = '$namespace_db';");
-    // yay parallel assignment
-    $row['comments_approved'] = $row['comments_unapproved'] = $row['comments_spam'] = 0;
-    while ( $commentrow = $db->fetchrow() )
-      switch($commentrow['approved'])
-      {
-        case COMMENT_APPROVED:
-        default:
-          $row['comments_approved']++;
-          break;
-        case COMMENT_UNAPPROVED:
-          $row['comments_unapproved']++;
-          break;
-        case COMMENT_SPAM:
-          $row['comments_spam']++;
-          break;
-      }
-    
-    $cache[$pathskey] = $row;
-    return $row;
-  }
+	/**
+ 	* Page ID
+ 	* @var string
+ 	*/
+	
+	public $page_id;
+	
+	/**
+ 	* Namespace
+ 	* @var string
+ 	*/
+	
+	public $namespace;
+	
+	/**
+ 	* Local copy of the page text
+ 	*/
+	
+	public $text_cache;
+	
+	/**
+ 	* Revision ID to send. If 0, the latest revision.
+ 	* @var int
+ 	*/
+	
+	public $revision_id = 0;
+	
+	/**
+ 	* Tracks whether the page exists
+ 	* @var bool
+ 	*/
+	
+	public $exists = false;
+	
+	/**
+ 	* Page title
+ 	* @var string
+ 	*/
+	
+	public $title = '';
+	
+	/**
+ 	* PathManager info array ("cdata") for this page. (The one with urlname, name, namespace, delvotes, delvote_ips, protected, visible, etc.)
+ 	* @var array
+ 	*/
+	
+	public $cdata = array();
+	
+	/**
+ 	* ACL calculation instance for this page.
+ 	* @var object(Session_ACLPageInfo)
+ 	*/
+	
+	public $perms = false;
+	
+	/**
+ 	* Protection calculation
+ 	* @var bool
+ 	*/
+	
+	public $page_protected = false;
+	
+	/**
+ 	* Wiki mode calculation
+ 	* @var bool
+ 	*/
+	
+	public $wiki_mode = false;
+	
+	/**
+ 	* Page conditions. These represent the final decision as to whether an action is allowed or not. They are set to true if ACLs permit AND if
+ 	* the action "makes sense." (e.g., you can't vote to delete a non-wikimode page.)
+ 	* @var array
+ 	*/
+	
+	public $conds = array();
+	
+	/**
+ 	* Constructor.
+ 	*/
+	
+	public function __construct($page_id, $namespace, $revision_id = 0)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		$this->page_id = sanitize_page_id($page_id);
+		$this->namespace = $namespace;
+		$this->revision_id = intval($revision_id);
+		
+		// grab the cdata
+		$this->build_cdata();
+		
+		$this->page_protected = $this->cdata['really_protected'] ? true : false;
+		switch($this->cdata['wiki_mode'])
+		{
+			case 0: $this->wiki_mode = false; break;
+			case 1: $this->wiki_mode = true; break;
+			default: case 2: $this->wiki_mode = getConfig('wiki_mode') == 1; break;
+		}
+	}
+	
+	/**
+ 	* Build the page's cdata.
+ 	*/
+	
+	public function build_cdata()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		static $cdata_cache = array();
+		$pathskey = $paths->get_pathskey($this->page_id, $this->namespace);
+		if ( isset($cdata_cache[$pathskey]) )
+		{
+			$this->cdata = $cdata_cache[$pathskey];
+			$this->exists = $cdata_cache[$pathskey]['page_exists'];
+			$this->title = $cdata_cache[$pathskey]['name'];
+			return null;
+		}
+		
+		$this->exists = false;
+		$ns_char = substr($paths->nslist['Special'], -1);
+		$page_name = $this->namespace == 'Article' ? dirtify_page_id($this->page_id) : "{$this->namespace}{$ns_char}" . dirtify_page_id($this->page_id);
+		$page_name = str_replace('_', ' ', $page_name);
+		$this->title = $page_name;
+		
+		$this->cdata = array(
+			'name' => $page_name,
+			'urlname' => $this->page_id,
+			'namespace' => $this->namespace,
+			'special' => 0,
+			'visible' => 0,
+			'comments_on' => 1,
+			'protected' => 0,
+			'delvotes' => 0,
+			'delvote_ips' => '',
+			'wiki_mode' => 2,
+			'page_exists' => false,
+			'page_format' => getConfig('default_page_format', 'wikitext')
+		);
+		
+		if ( $data_from_db = Namespace_Default::get_cdata_from_db($this->page_id, $this->namespace) )
+		{
+			$this->exists = true;
+			$this->cdata = $data_from_db;
+			$this->cdata['page_exists'] = true;
+			$this->title = $this->cdata['name'];
+		}
+				
+		$this->cdata = Namespace_Default::bake_cdata($this->cdata);
+		
+		$cdata_cache[$pathskey] = $this->cdata;
+	}
+	
+	/**
+ 	* Pulls the page's actual text from the database.
+ 	*/
+	
+	function fetch_text()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		if ( !empty($this->text_cache) )
+		{
+			return $this->text_cache;
+		}
+		
+		if ( $this->revision_id > 0 && is_int($this->revision_id) )
+		{
+		
+			$q = $db->sql_query('SELECT page_text, char_tag, time_id FROM '.table_prefix.'logs WHERE log_type=\'page\' AND action=\'edit\' AND page_id=\'' . $this->page_id . '\' AND namespace=\'' . $this->namespace . '\' AND log_id=' . $this->revision_id . ';');
+			if ( !$q )
+			{
+				$this->send_error('Error during SQL query.', true);
+			}
+			if ( $db->numrows() < 1 )
+			{
+				// Compatibility fix for old pages with dots in the page ID
+				if ( strstr($this->page_id, '.2e') )
+				{
+					$db->free_result();
+					$page_id = str_replace('.2e', '.', $this->page_id);
+					$q = $db->sql_query('SELECT page_text, char_tag, time_id FROM '.table_prefix.'logs WHERE log_type=\'page\' AND action=\'edit\' AND page_id=\'' . $page_id . '\' AND namespace=\'' . $this->namespace . '\' AND log_id=' . $this->revision_id . ';');
+					if ( !$q )
+					{
+						$this->send_error('Error during SQL query.', true);
+					}
+					if ( $db->numrows() < 1 )
+					{
+						$this->page_exists = false;
+						return 'err_no_text_rows';
+					}
+				}
+				else
+				{
+					$this->page_exists = false;
+					return 'err_no_text_rows';
+				}
+			}
+			else
+			{
+				$row = $db->fetchrow();
+			}
+			
+			$db->free_result();
+			
+		}
+		else
+		{
+			$q = $db->sql_query('SELECT t.page_text, t.char_tag, l.time_id FROM '.table_prefix."page_text AS t\n"
+												. "  LEFT JOIN " . table_prefix . "logs AS l\n"
+												. "    ON ( l.page_id = t.page_id AND l.namespace = t.namespace )\n"
+												. "  WHERE t.page_id='$this->page_id' AND t.namespace='$this->namespace'\n"
+												. "  ORDER BY l.time_id DESC LIMIT 1;");
+			if ( !$q )
+			{
+				$this->send_error('Error during SQL query.', true);
+			}
+			if ( $db->numrows() < 1 )
+			{
+				// Compatibility fix for old pages with dots in the page ID
+				if ( strstr($this->page_id, '.2e') )
+				{
+					$db->free_result();
+					$page_id = str_replace('.2e', '.', $this->page_id);
+					$q = $db->sql_query('SELECT page_text, char_tag FROM '.table_prefix.'page_text WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $this->namespace . '\';');
+					if ( !$q )
+					{
+						$this->send_error('Error during SQL query.', true);
+					}
+					if ( $db->numrows() < 1 )
+					{
+						$this->page_exists = false;
+						return 'err_no_text_rows';
+					}
+				}
+				else
+				{
+					$this->page_exists = false;
+					return 'err_no_text_rows';
+				}
+			}
+			
+			$row = $db->fetchrow();
+			$db->free_result();
+			
+		}
+		
+		if ( !empty($row['char_tag']) )
+		{
+			// This page text entry uses the old text-escaping format
+			$from = array(
+					"{APOS:{$row['char_tag']}}",
+					"{QUOT:{$row['char_tag']}}",
+					"{SLASH:{$row['char_tag']}}"
+				);
+			$to = array("'", '"',  '\\');
+			$row['page_text'] = str_replace($from, $to, $row['page_text']);
+		}
+		
+		$this->text_cache = $row['page_text'];
+		
+		if ( isset($row['time_id']) )
+		{
+			$this->revision_time = intval($row['time_id']);
+		}
+		
+		return $row['page_text'];
+	}
+	
+	/**
+ 	* Send the page.
+ 	*/
+	
+	public function send()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $output;
+		
+		$output->add_before_footer($this->display_categories());
+		
+		if ( $this->exists )
+			$this->send_from_db();
+		else
+		{
+			// This is the DEPRECATED way to extend namespaces. It's left in only for compatibility with older plugins.
+			ob_start();
+			$code = $plugins->setHook('page_not_found');
+			foreach ( $code as $cmd )
+			{
+				eval($cmd);
+			}
+			$c = ob_get_contents();
+			if ( !empty($c) )
+			{
+				ob_end_clean();
+				echo $c;
+			}
+			else
+			{
+				$output->header();
+				$this->error_404();
+				$output->footer();
+			}
+		}
+	}
+	
+	/**
+ 	* Get a redirect, if there is one.
+ 	* @return mixed Array: Page ID and namespace, associative; bool: false (no redirect)
+ 	*/
+	
+	public function get_redirect()
+	{
+		$text = $this->fetch_text();
+		if ( preg_match('/^#redirect \[\[([^\]]+?)\]\]/i', $text, $match ) )
+		{
+			list($page_id, $namespace) = RenderMan::strToPageID($match[1]);
+			return array(
+					'page_id' => $page_id,
+					'namespace' => $namespace
+				);
+		}
+		return false;
+	}
+ 	
+	/**
+ 	* The "real" send-the-page function. The reason for this is so other namespaces can re-use the code
+ 	* to fetch the page from the DB while being able to install their own wrappers.
+ 	*/
+	
+	public function send_from_db($incl_inner_headers = true, $send_headers = true)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		global $output;
+		
+		$text = $this->fetch_text();
+		
+		profiler_log("Namespace [$this->namespace, $this->page_id]: pulled text from DB");
+		
+		$text = preg_replace('/([\s]*)__NOBREADCRUMBS__([\s]*)/', '', $text);
+		$text = preg_replace('/([\s]*)__NOTOC__([\s]*)/', '', $text);
+		$text = preg_replace('/^#redirect \[\[.+?\]\]\s*/i', '', $text);
+		
+		if ( $send_headers )
+		{
+			$output->set_title($this->title);
+			$output->header();
+		}
+		$this->do_breadcrumbs();
+		
+		if ( $incl_inner_headers )
+		{
+			if ( !$this->perms )
+				$this->perms = $session->fetch_page_acl($this->page_id, $this->namespace);
+			
+			if ( $this->perms->get_permissions('vote_reset') && $this->cdata['delvotes'] > 0)
+			{
+				$delvote_ips = unserialize($this->cdata['delvote_ips']);
+				$hr = htmlspecialchars(implode(', ', $delvote_ips['u']));
+				
+				$string_id = ( $this->cdata['delvotes'] == 1 ) ? 'delvote_lbl_votes_one' : 'delvote_lbl_votes_plural';
+				$string = $lang->get($string_id, array('num_users' => $this->cdata['delvotes']));
+				
+				echo '
+ ' . $lang->get('etc_lbl_notice') . ' ' . $string . '
+ ' . $lang->get('delvote_lbl_users_that_voted') . ' ' . $hr . '
+ ' . $lang->get('delvote_btn_deletepage') . ' | ' . $lang->get('delvote_btn_resetvotes') . ' +
'; + } + } + + if ( $this->revision_id ) + { + echo '
+ ' . $lang->get('page_msg_archived_title') . '
+ ' . $lang->get('page_msg_archived_body', array( + 'archive_date' => enano_date(ED_DATE, $this->revision_time), + 'archive_time' => enano_date(ED_TIME, $this->revision_time), + 'current_link' => makeUrlNS($this->namespace, $this->page_id), + 'restore_link' => makeUrlNS($this->namespace, $this->page_id, 'do=edit&revid='.$this->revision_id), + 'restore_onclick' => 'ajaxEditor(\''.$this->revision_id.'\'); return false;', + )) . ' +
'; + $q = $db->sql_query('SELECT page_format FROM ' . table_prefix . "logs WHERE log_id = {$this->revision_id};"); + if ( !$q ) + $db->_die(); + + list($page_format) = $db->fetchrow_num(); + $db->free_result(); + } + else + { + $page_format = $this->cdata['page_format']; + } + + $code = $plugins->setHook('pageprocess_render_head'); + foreach ( $code as $cmd ) + { + eval($cmd); + } + + $prof_contentevent = profiler_log("Namespace [$this->namespace, $this->page_id]: headers and preprocessing done - about to send content"); + + if ( $incl_inner_headers ) + { + if ( $page_format === 'wikitext' ) + { + $text = '?>' . RenderMan::render($text); + } + else + { + // Page format is XHTML. This means we want to disable functionality that MCE takes care of, while still retaining + // the ability to wikilink, the ability to use images, etc. Basically, RENDER_INLINEONLY disables all behavior in + // the rendering engine/Text_Wiki that conflicts with MCE. + $text = '?>' . RenderMan::render($text, RENDER_INLINE); + } + } + else + { + $text = '?>' . $text; + $text = preg_replace('/(.*?)<\/nowiki>/s', '\\1', $text); + } + + eval ( $text ); + + profiler_log("Namespace [$this->namespace, $this->page_id]: content sent", true, $prof_contentevent); + + $code = $plugins->setHook('pageprocess_render_tail'); + foreach ( $code as $cmd ) + { + eval($cmd); + } + + if ( $incl_inner_headers ) + { + display_page_footers(); + } + + profiler_log("Namespace [$this->namespace, $this->page_id]: sent footers"); + + if ( $send_headers ) + $output->footer(); + } + + /** + * Echoes out breadcrumb data, if appropriate. + * @access private + */ + + function do_breadcrumbs() + { + global $db, $session, $paths, $template, $plugins; // Common objects + global $lang; + + if ( strpos($this->text_cache, '__NOBREADCRUMBS__') !== false ) + return false; + + $mode = getConfig('breadcrumb_mode'); + + if ( $mode == 'never' ) + // Breadcrumbs are disabled + return true; + + // Minimum depth for breadcrumb display + $threshold = ( $mode == 'always' ) ? 0 : 1; + + $breadcrumb_data = explode('/', $this->page_id); + if ( count($breadcrumb_data) > $threshold ) + { + // If we're not on a subpage of the main page, add "Home" to the list + $show_home = false; + if ( $mode == 'always' ) + { + $show_home = true; + } + echo ' + + + '; + } + } + + public function error_404() + { + global $db, $session, $paths, $template, $plugins; // Common objects + global $lang, $output; + + $userpage = $this->namespace == 'User'; + + @header('HTTP/1.1 404 Not Found'); + + $msg = ( $pp = $paths->sysmsg('Page_not_found') ) ? $pp : '{STANDARD404}'; + + $standard_404 = ''; + + if ( $userpage ) + { + $standard_404 .= '

' . $lang->get('page_msg_404_title_userpage') . '

+

' . $lang->get('page_msg_404_body_userpage'); + } + else + { + $standard_404 .= '

' . $lang->get('page_msg_404_title') . '

+

' . $lang->get('page_msg_404_body'); + } + if ( $session->get_permissions('create_page') ) + { + $standard_404 .= ' ' . $lang->get('page_msg_404_create', array( + 'create_flags' => 'href="'.makeUrlNS($this->namespace, $this->page_id, 'do=edit', true).'" onclick="ajaxEditor(); return false;"', + 'mainpage_link' => makeUrl(get_main_page(), false, true) + )); + } + else + { + $standard_404 .= ' ' . $lang->get('page_msg_404_gohome', array( + 'mainpage_link' => makeUrl(get_main_page(), false, true) + )); + } + $standard_404 .= '

'; + if ( $session->get_permissions('history_rollback') ) + { + $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;'); + if ( !$e ) + { + $db->_die('The deletion log could not be selected.'); + } + if ( $db->numrows() > 0 ) + { + $r = $db->fetchrow(); + $standard_404 .= '

' . $lang->get('page_msg_404_was_deleted', array( + 'delete_time' => enano_date(ED_DATE | ED_TIME, $r['time_id']), + 'delete_reason' => htmlspecialchars($r['edit_summary']), + 'rollback_flags' => 'href="'.makeUrl($paths->page, 'do=rollback&id='.$r['log_id']).'" onclick="ajaxRollback(\''.$r['log_id'].'\'); return false;"' + )) + . '

'; + if ( $session->user_level >= USER_LEVEL_ADMIN ) + { + $standard_404 .= '

' . $lang->get('page_msg_404_admin_opts', array( + 'detag_link' => makeUrl($paths->page, 'do=detag', true) + )) + . '

'; + } + } + $db->free_result(); + } + $standard_404 .= '

+ ' . $lang->get('page_msg_404_http_response') . ' +

'; + + $parser = $template->makeParserText($msg); + $parser->assign_vars(array( + 'STANDARD404' => $standard_404 + )); + + $msg = RenderMan::render($parser->run()); + eval( '?>' . $msg ); + } + + /** + * Display the categories a page is in. If the current page is a category, its contents will also be printed. + */ + + function display_categories() + { + global $db, $session, $paths, $template, $plugins; // Common objects + global $lang; + + $html = ''; + + if ( $this->namespace == 'Category' ) + { + // Show member pages and subcategories + $q = $db->sql_query('SELECT p.urlname, p.namespace, p.name, p.namespace=\'Category\' AS is_category FROM '.table_prefix.'categories AS c + LEFT JOIN '.table_prefix.'pages AS p + ON ( p.urlname = c.page_id AND p.namespace = c.namespace ) + WHERE c.category_id=\'' . $db->escape($this->page_id) . '\' + ORDER BY is_category DESC, p.name ASC;'); + if ( !$q ) + { + $db->_die(); + } + $html .= '

' . $lang->get('onpage_cat_heading_subcategories') . '

'; + $html .= '
'; + $html .= ''; + $html .= ''; + $ticker = 0; + $counter = 0; + $switched = false; + $class = 'row1'; + while ( $row = $db->fetchrow($q) ) + { + if ( $row['is_category'] == 0 && !$switched ) + { + if ( $counter > 0 ) + { + // Fill-in + while ( $ticker < 3 ) + { + $ticker++; + $html .= ''; + } + } + else + { + $html .= ''; + } + $html .= '
' . $lang->get('onpage_cat_msg_no_subcategories') . '
' . "\n\n"; + $html .= '

' . $lang->get('onpage_cat_heading_pages') . '

'; + $html .= '
'; + $html .= ''; + $html .= ''; + $counter = 0; + $ticker = -1; + $switched = true; + } + $counter++; + $ticker++; + if ( $ticker == 3 ) + { + $html .= ''; + $ticker = 0; + $class = ( $class == 'row3' ) ? 'row1' : 'row3'; + } + $html .= ""; + } + if ( !$switched ) + { + if ( $counter > 0 ) + { + // Fill-in + while ( $ticker < 2 ) + { + $ticker++; + $html .= ''; + } + } + else + { + $html .= ''; + } + $html .= '
"; // " to workaround stupid jEdit bug + + $link = makeUrlNS($row['namespace'], sanitize_page_id($row['urlname'])); + $html .= 'nslist[$row['namespace']] . sanitize_page_id($row['urlname']); + if ( !isPage( $key ) ) + { + $html .= ' class="wikilink-nonexistent"'; + } + $html .= '>'; + $title = get_page_title_ns($row['urlname'], $row['namespace']); + $html .= htmlspecialchars($title); + $html .= ''; + + $html .= "' . $lang->get('onpage_cat_msg_no_subcategories') . '
' . "\n\n"; + $html .= '

' . $lang->get('onpage_cat_heading_pages') . '

'; + $html .= '
'; + $html .= ''; + $html .= ''; + $counter = 0; + $ticker = 0; + $switched = true; + } + if ( $counter > 0 ) + { + // Fill-in + while ( $ticker < 2 ) + { + $ticker++; + $html .= ''; + } + } + else + { + $html .= ''; + } + $html .= '
' . $lang->get('onpage_cat_msg_no_pages') . '
' . "\n\n"; + } + + if ( $this->namespace != 'Special' && $this->namespace != 'Admin' ) + { + $html .= '
'; + $html .= '
'; + $html .= '(' . $lang->get('tags_catbox_link') . ')'; + $html .= '
'; + $html .= '
' . $lang->get('catedit_catbox_lbl_categories') . ' '; + + $q = $db->sql_query('SELECT category_id FROM ' . table_prefix . "categories WHERE page_id = '$this->page_id' AND namespace = '$this->namespace';"); + if ( !$q ) + $db->_die(); + + if ( $row = $db->fetchrow() ) + { + $list = array(); + do + { + $cid = sanitize_page_id($row['category_id']); + $title = get_page_title_ns($cid, 'Category'); + $link = makeUrlNS('Category', $cid); + $list[] = '' . htmlspecialchars($title) . ''; + } + while ( $row = $db->fetchrow($q) ); + $html .= implode(', ', $list); + } + else + { + $html .= $lang->get('catedit_catbox_lbl_uncategorized'); + } + + $can_edit = ( $session->get_permissions('edit_cat') && ( !$paths->page_protected || $session->get_permissions('even_when_protected') ) ); + if ( $can_edit ) + { + $edit_link = '' . $lang->get('catedit_catbox_link_edit') . ''; + $html .= ' [ ' . $edit_link . ' ]'; + } + + $html .= '
'; + } + return $html; + } + + /** + * Pull in switches as to whether a specific toolbar button should be used or not. This sets things up according to the current page being displayed. + * @return array Associative + */ + + function set_conds() + { + global $db, $session, $paths, $template, $plugins; // Common objects + + if ( !$this->perms ) + $this->perms = $session->fetch_page_acl($this->page_id, $this->namespace); + + if ( !$this->perms ) + { + // We're trying to send a page WAY too early (session hasn't been started yet), such as for a redirect. Send a default set of conds because + // there's NO way to get permissions to determine anything otherwise. Yes, starting $session here might be dangerous. + $this->conds = array( + 'article' => true, + 'comments' => false, + 'edit' => false, + 'viewsource' => false, + 'history' => false, + 'rename' => false, + 'delvote' => false, + 'resetvotes' => false, + 'delete' => false, + 'printable' => false, + 'protect' => false, + 'setwikimode' => false, + 'clearlogs' => false, + 'password' => false, + 'acledit' => false, + 'adminpage' => false + ); + return $this->conds; + } + + // die('have perms:
' . print_r($this->perms, true) . "\n---------------------------------\nBacktrace:\n" . enano_debug_print_backtrace(true));
+		
+		$enforce_protection = ( $this->page_protected && ( ( $session->check_acl_scope('even_when_protected', $this->namespace) && !$this->perms->get_permissions('even_when_protected') ) || !$session->check_acl_scope('even_when_protected', $this->namespace) ) );
+		
+		$conds = array();
+		
+		// Article: always show
+		$conds['article'] = true;
+		
+		// Discussion: Show if comments are enabled on the site, and if comments are on for this page.
+		$conds['comments'] = $this->perms->get_permissions('read') && getConfig('enable_comments', '1')=='1' && $this->cdata['comments_on'] == 1;
+		
+		// Edit: Show if we have permission to edit the page, and if we don't have protection in effect
+		$conds['edit'] = $this->perms->get_permissions('read') && $session->check_acl_scope('edit_page', $this->namespace) && $this->perms->get_permissions('edit_page') && !$enforce_protection;
+		
+		// View source: Show if we have permission to view source and either ACLs prohibit editing or protection is in effect
+		$conds['viewsource'] = $session->check_acl_scope('view_source', $this->namespace) && $this->perms->get_permissions('view_source') && ( !$this->perms->get_permissions('edit_page') || $enforce_protection ) && $this->namespace != 'API';
+		
+		// History: Show if we have permission to see history and if the page exists
+		$conds['history'] = $session->check_acl_scope('history_view', $this->namespace) && $this->exists && $this->perms->get_permissions('history_view');
+		
+		// Rename: Show if the page exists, if we have permission to rename, and if protection isn't in effect
+		$conds['rename'] = $session->check_acl_scope('rename', $this->namespace) && $this->exists && $this->perms->get_permissions('rename') && !$enforce_protection;
+		
+		// Vote-to-delete: Show if we have Wiki Mode on, if we have permission to vote for deletion, and if the page exists (can't vote to delete a nonexistent page)
+		$conds['delvote'] = $this->wiki_mode && $session->check_acl_scope('vote_delete', $this->namespace) && $this->perms->get_permissions('vote_delete') && $this->exists;
+		
+		// Reset votes: Show if we have Wiki Mode on, if we have permission to reset votes, if the page exists, and if there's at least one vote
+		$conds['resetvotes'] = $session->check_acl_scope('vote_reset', $this->namespace) && $this->wiki_mode && $this->exists && $this->perms->get_permissions('vote_reset') && $this->cdata['delvotes'] > 0;
+		
+		// Delete page: Show if the page exists and if we have permission to delete it
+		$conds['delete'] = $session->check_acl_scope('delete_page', $this->namespace) && $this->exists && $this->perms->get_permissions('delete_page');
+		
+		// Printable view: Show if the page exists
+		$conds['printable'] = $this->exists;
+		
+		// Protect: Show if we have Wiki Mode on, if the page exists, and if we have permission to protect the page.
+		$conds['protect'] = $session->check_acl_scope('protect', $this->namespace) && $this->wiki_mode && $this->exists && $this->perms->get_permissions('protect');
+		
+		// Set Wiki Mode: Show if the page exists and if we have permission to set wiki mode
+		$conds['setwikimode'] = $session->check_acl_scope('set_wiki_mode', $this->namespace) && $this->exists && $this->perms->get_permissions('set_wiki_mode');
+		
+		// Clear logs: Show if we have permission to clear logs
+		$conds['clearlogs'] = $session->check_acl_scope('clear_logs', $this->namespace) && $this->perms->get_permissions('clear_logs');
+		
+		// Set password: a little bit complicated. If there's a password, check for password_reset; else, check for password_set.
+		$conds['password'] = empty($this->cdata['password']) ?
+ 													$session->check_acl_scope('password_set', $this->namespace) && $this->perms->get_permissions('password_set') :
+ 													$session->check_acl_scope('password_reset', $this->namespace) && $this->perms->get_permissions('password_reset');
+		
+		// Edit ACLs: Show if this is a non-Enano page that's calling the Enano API and (a) if we have permissions to edit ACLs or (b) we're an admin AND ACL_ALWAYS_ALLOW_ADMIN_EDIT_ACL is on
+		$conds['acledit'] = $this->namespace != 'API' && $session->check_acl_scope('edit_acl', $this->namespace) && ( $this->perms->get_permissions('edit_acl') || ( defined('ACL_ALWAYS_ALLOW_ADMIN_EDIT_ACL') &&  $session->user_level >= USER_LEVEL_ADMIN ) );
+		
+		// Admin page: Show if the page exists and if we're an admin
+		$conds['adminpage'] = $session->user_level >= USER_LEVEL_ADMIN && $this->exists;
+		
+		// Allow plugins to change stuff
+		$code = $plugins->setHook('page_conds_set');
+		foreach ( $code as $cmd )
+		{
+			eval($cmd);
+		}
+		
+		$this->conds = $conds;
+	}
+	
+	/**
+ 	* Return page conditions
+ 	* @return array
+ 	*/
+	
+	public function get_conds()
+	{
+		if ( empty($this->conds) )
+			$this->set_conds();
+		
+		return $this->conds;
+	}
+	
+	/**
+ 	* Just tell us if the current page exists or not.
+ 	* @return bool
+ 	*/
+ 	
+	public function exists()
+	{
+		return $this->exists;
+	}
+	
+	/**
+ 	* Return cdata
+ 	* @return array
+ 	*/
+	
+	public function get_cdata()
+	{
+		return $this->cdata;
+	}
+	
+	/**
+ 	* Bake, or finalize the processing of, a cdata array.
+ 	* @static
+ 	* @access public
+ 	*/
+	
+	public static function bake_cdata($cdata)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		// urlname_nons is the actual page_id.
+		$cdata['urlname_nons'] = $cdata['urlname'];
+		if ( isset($paths->nslist[ $cdata['namespace'] ]) )
+		{
+			$cdata['urlname'] = $paths->nslist[ $cdata['namespace'] ] . $cdata['urlname'];
+		}
+		else
+		{
+			$ns_char = substr($paths->nslist['Special'], -1);
+			$cdata['urlname'] = $cdata['namespace'] . $ns_char . $cdata['urlname'];
+		}
+		
+		// add missing keys
+		$defaults = array(
+			'special' => 0,
+			'visible' => 0,
+			'comments_on' => 1,
+			'protected' => 0,
+			'delvotes' => 0,
+			'delvote_ips' => serialize(array()),
+			'wiki_mode' => 2,
+			'page_format' => getConfig('default_page_format', 'wikitext')
+		);
+		foreach ( $defaults as $key => $value )
+		{
+			if ( !isset($cdata[$key]) )
+				$cdata[$key] = $value;
+		}
+		
+		// fix up deletion votes
+		if ( empty($cdata['delvotes']) )
+			$cdata['delvotes'] = 0;
+		
+		// fix up deletion vote IP list
+		if ( empty($cdata['delvote_ips']) )
+			$cdata['delvote_ips'] = serialize(array());
+		
+		// calculate wiki mode
+		$cdata['really_wiki_mode'] = ( $cdata['wiki_mode'] == 1 || ( $cdata['wiki_mode'] == 2 && getConfig('wiki_mode', 0) == 1 ) );
+		
+		// calculate protection
+		$cdata['really_protected'] = ( $cdata['protected'] > 0 );
+		if ( $cdata['protected'] == 2 )
+		{
+			$cdata['really_protected'] = !$session->user_logged_in || ( $session->user_logged_in && $session->reg_time + 86400*4 > time() );
+		}
+		
+		return $cdata;
+	}
+	
+	/**
+ 	* Grabs raw (unbaked) cdata from the database, caching if possible.
+ 	* @param string Page ID
+ 	* @param string Namespace.
+ 	* @static
+ 	*/
+	
+	public static function get_cdata_from_db($page_id, $namespace)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		static $cache = array();
+		
+		$pathskey = $paths->get_pathskey($page_id, $namespace);
+		if ( isset($cache[$pathskey]) )
+			return $cache[$pathskey];
+		
+		$page_id_db = $db->escape($page_id);
+		$namespace_db = $db->escape($namespace);
+		
+		$q = $db->sql_query('SELECT p.*'
+											. '    FROM ' . table_prefix . "pages AS p\n"
+											. "  WHERE p.urlname = '$page_id_db' AND p.namespace = '$namespace_db'\n"
+											. "    GROUP BY p.urlname, p.name, p.namespace, p.page_order, p.special, p.visible, p.protected, p.wiki_mode, p.comments_on, p.delvotes, p.delvote_ips, p.page_format, p.password;");
+		
+		if ( !$q )
+			$db->_die();
+		
+		if ( $db->numrows() < 1 )
+		{
+			$db->free_result();
+			$cache[$pathskey] = false;
+			return false;
+		}
+		
+		$row = $db->fetchrow();
+		
+		// Get comment counts
+		// FIXME: Apparently there's a bit of recursion in here. Fetching permissions depends on this cdata function.
+		// Perhaps we should eliminate session's dependency on cdata? (What is it used for?)
+		$q = $db->sql_query('SELECT approved FROM ' . table_prefix . "comments WHERE page_id = '$page_id_db' AND namespace = '$namespace_db';");
+		// yay parallel assignment
+		$row['comments_approved'] = $row['comments_unapproved'] = $row['comments_spam'] = 0;
+		while ( $commentrow = $db->fetchrow() )
+			switch($commentrow['approved'])
+			{
+				case COMMENT_APPROVED:
+				default:
+					$row['comments_approved']++;
+					break;
+				case COMMENT_UNAPPROVED:
+					$row['comments_unapproved']++;
+					break;
+				case COMMENT_SPAM:
+					$row['comments_spam']++;
+					break;
+			}
+		
+		$cache[$pathskey] = $row;
+		return $row;
+	}
 }
 
 /**