# HG changeset patch # User Dan # Date 1241496626 14400 # Node ID 323c4cd1aa3753ef16984548e6cb2374ba025898 # Parent d52dfa1f08da39aa7ffe5e7a39885e952f874af5 Made some more changes to the way namespaces are handled, for optimization purposes. This is a bit of a structural reorganization: $paths->pages is obsoleted in its entirety; calculating page existence and metadata is now the job of the Namespace_* backend class. There are many things in PageProcessor that should be reorganized, and page actions in general should really be rethought. This is probably the beginning of a long process that will be taking place over the course of the betas. diff -r d52dfa1f08da -r 323c4cd1aa37 includes/dbal.php --- a/includes/dbal.php Mon May 04 23:07:00 2009 -0400 +++ b/includes/dbal.php Tue May 05 00:10:26 2009 -0400 @@ -427,10 +427,24 @@ } function fetchrow($r = false) { - if(!$this->_conn) return false; - if(!$r) $r = $this->latest_result; - if(!$r) $this->_die('$db->fetchrow(): an invalid MySQL resource was passed.'); + if ( !$this->_conn ) + return false; + + if ( !$r ) + $r = $this->latest_result; + + if ( !$r ) + $this->_die('$db->fetchrow(): an invalid MySQL resource was passed.'); + $row = mysql_fetch_assoc($r); + /* + if ( empty($row) ) + { + $GLOBALS['do_gzip'] = false; + echo '
' . enano_debug_print_backtrace(true) . '
'; + } + */ + return integerize_array($row); } diff -r d52dfa1f08da -r 323c4cd1aa37 includes/functions.php --- a/includes/functions.php Mon May 04 23:07:00 2009 -0400 +++ b/includes/functions.php Tue May 05 00:10:26 2009 -0400 @@ -380,16 +380,7 @@ global $db, $session, $paths, $template, $plugins; // Common objects $idata = RenderMan::strToPageID($page_id); - $page_id_key = $paths->nslist[ $idata[1] ] . $idata[0]; - $page_id_key = sanitize_page_id($page_id_key); - $page_data = @$paths->pages[$page_id_key]; - $title = ( isset($page_data['name']) ) ? - ( ( $page_data['namespace'] == 'Article' || !$show_ns ) ? - '' : - $paths->nslist[ $idata[1] ] ) - . $page_data['name'] : - ( $show_ns ? $paths->nslist[$idata[1]] : '' ) . str_replace('_', ' ', dirtify_page_id( $idata[0] ) ); - return $title; + return get_page_title_ns($idata[0], $idata[1]); } /** @@ -402,19 +393,9 @@ function get_page_title_ns($page_id, $namespace) { global $db, $session, $paths, $template, $plugins; // Common objects - - $ns_prefix = ( isset($paths->nslist[ $namespace ]) ) ? $paths->nslist[ $namespace ] : $namespace . substr($paths->nslist['Special'], -1); - $page_id_key = $ns_prefix . $page_id; - if ( isPage($page_id_key) ) - { - $page_data = $paths->pages[$page_id_key]; - } - else - { - $page_data = array(); - } - $title = ( isset($page_data['name']) ) ? $page_data['name'] : $ns_prefix . str_replace('_', ' ', dirtify_page_id( $page_id ) ); - return $title; + + $ns = namespace_factory($page_id, $namespace); + return $ns->title; } /** @@ -578,32 +559,24 @@ * @return bool True if the page exists, false otherwise */ -function isPage($p) { +function isPage($p) +{ global $db, $session, $paths, $template, $plugins; // Common objects - - // Try the easy way first ;-) - if ( isset( $paths->pages[ $p ] ) ) - { - return true; - } - - // Special case for Special, Template, and Admin pages that can't have slashes in their URIs - $ns_test = RenderMan::strToPageID( $p ); - - if($ns_test[1] != 'Special' && $ns_test[1] != 'Template' && $ns_test[1] != 'Admin') - { - return false; - } - - $particles = explode('/', $p); - if ( isset ( $paths->pages[ $particles[ 0 ] ] ) ) - { - return true; - } - else - { - return false; - } + static $ispage_cache = array(); + if ( isset($ispage_cache[$p]) ) + return $ispage_cache[$p]; + + list($page_id, $namespace) = RenderMan::strToPageID($p); + $cdata = $paths->get_cdata($page_id, $namespace); + if ( !isset($cdata['page_exists']) ) + { + $class = ( class_exists($_ = "Namespace_$namespace") ) ? $_ : "Namespace_Default"; + $page = new $class($page_id, $namespace); + return $page->exists(); + } + + $ispage_cache[$p] = $cdata['page_exists']; + return $cdata['page_exists']; } /** @@ -615,6 +588,13 @@ function namespace_factory($page_id, $namespace, $revision_id = 0) { + global $db, $session, $paths, $template, $plugins; // Common objects + + static $objcache = array(); + $pathskey = $paths->get_pathskey($page_id, $namespace) . ":$revision_id"; + if ( isset($objcache[$pathskey]) ) + return $objcache[$pathskey]; + if ( !class_exists("Namespace_$namespace") ) { if ( file_exists(ENANO_ROOT . "/includes/namespaces/" . strtolower($namespace) . ".php") ) @@ -626,11 +606,13 @@ { $class = "Namespace_$namespace"; $ns = new $class($page_id, $namespace, $revision_id); + $objcache[$pathskey] = $ns; return $ns; } else { $ns = new Namespace_Default($page_id, $namespace, $revision_id); + $objcache[$pathskey] = $ns; return $ns; } } @@ -915,22 +897,7 @@ function display_page_headers() { - global $db, $session, $paths, $template, $plugins; // Common objects - global $lang; - if($session->get_permissions('vote_reset') && $paths->cpage['delvotes'] > 0) - { - $delvote_ips = unserialize($paths->cpage['delvote_ips']); - $hr = htmlspecialchars(implode(', ', $delvote_ips['u'])); - - $string_id = ( $paths->cpage['delvotes'] == 1 ) ? 'delvote_lbl_votes_one' : 'delvote_lbl_votes_plural'; - $string = $lang->get($string_id, array('num_users' => $paths->cpage['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') . ' -
'; - } + // Deprecated. } /** @@ -2362,8 +2329,11 @@ $this_page = ceil ( $start / $perpage ); $i = 0; - $paginator = generate_paginator($this_page, $num_pages, $result_url, $perpage, 0); - $out .= $paginator; + if ( $num_results > 0 ) + { + $paginator = generate_paginator($this_page, $num_pages, $result_url, $perpage, 0); + $out .= $paginator; + } $cls = 'row2'; @@ -2386,7 +2356,7 @@ { if ( isset($callers[$j]) ) { - $tmp = ( is_callable($callers[$j]) ) ? @call_user_func($callers[$j], $val, $row) : $val; + $tmp = ( is_callable($callers[$j]) ) ? call_user_func($callers[$j], $val, $row) : $val; if ( is_string($tmp) ) { @@ -2401,7 +2371,8 @@ $out .= $footer; } - $out .= $paginator; + if ( $num_results > 0 ) + $out .= $paginator; return $out; } @@ -2429,9 +2400,8 @@ $this_page = ceil ( $start / $perpage ); $paginator = generate_paginator($this_page, $num_pages, $result_url, $perpage, 0); - $out .= $paginator; - if ( $total > 1 ) + if ( $num_results > 1 ) { $out .= $paginator; } @@ -2457,7 +2427,7 @@ $out .= $footer; } - if ( $total > 1 ) + if ( $num_results > 1 ) $out .= $paginator; return $out; @@ -4617,9 +4587,11 @@ * Logs something in the profiler. * @param string Point name or message * @param bool Optional. If true (default), a backtrace will be generated and added to the profiler data. False disables this, often for security reasons. + * @param resource Optional. If specified, bases the time difference off of this event instead of the previous event/ + * @return resource Event ID */ -function profiler_log($point, $allow_backtrace = true) +function profiler_log($point, $allow_backtrace = true, $parent_event = false) { if ( !defined('ENANO_DEBUG') ) return false; @@ -4634,13 +4606,31 @@ 'point' => $point, 'time' => microtime_float(), 'backtrace' => $backtrace, - 'mem' => false + 'mem' => false, + 'parent_event' => $parent_event ); if ( function_exists('memory_get_usage') ) { $_profiler[ count($_profiler) - 1 ]['mem'] = memory_get_usage(); } - return true; + return count($_profiler) - 1; +} + +/** + * Insert a message (an event without any time data) into the profiler. + * @param string Message + */ + +function profiler_message($message) +{ + if ( !defined('ENANO_DEBUG') ) + return false; + + global $_profiler; + + $_profiler[] = array( + 'message' => $message, + ); } /** @@ -4676,6 +4666,17 @@ // if ( $time_since_last < 0.01 ) // continue; + if ( isset($entry['message']) ) + { + $html .= "\n\n Message $i\n"; + + $html .= '' . "\n"; + $html .= ' Message:' . "\n"; + $html .= ' ' . htmlspecialchars($entry['message']) . '' . "\n"; + $html .= '' . "\n"; + continue; + } + $html .= "\n\n Event $i\n"; $html .= '' . "\n"; @@ -4690,15 +4691,25 @@ $html .= ' ' . $time . 's' . "\n"; $html .= '' . "\n"; + $time_label = 'Time since last event:'; + if ( $entry['parent_event'] && is_int($entry['parent_event']) && isset($profile[$entry['parent_event']]) ) + { + $time_last = $profile[$entry['parent_event']]['time']; + $time_label = "Time since event #{$entry['parent_event']}:"; + } + $time = $entry['time'] - $time_last; if ( $time < 0.0001 ) - $time = 'Marginal'; + $time_html = 'Marginal'; else - $time = "{$time}s"; + $time_html = number_format($time, 6) . "s"; + if ( $time > 0.02 ) + $time_html = "$time_html"; + $html .= '' . "\n"; - $html .= ' Time since last event:' . "\n"; - $html .= ' ' . $time . '' . "\n"; + $html .= ' ' . $time_label . '' . "\n"; + $html .= ' ' . $time_html . '' . "\n"; $html .= '' . "\n"; if ( $entry['backtrace'] ) @@ -4903,6 +4914,7 @@ $cache->purge('page_meta'); $cache->purge('anon_sidebar'); $cache->purge('plugins'); + $cache->purge('wiki_edit_notice'); $data_files = array( 'aes_decrypt.php', diff -r d52dfa1f08da -r 323c4cd1aa37 includes/namespaces/default.php --- a/includes/namespaces/default.php Mon May 04 23:07:00 2009 -0400 +++ b/includes/namespaces/default.php Tue May 05 00:10:26 2009 -0400 @@ -64,6 +64,42 @@ 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. */ @@ -75,50 +111,65 @@ $this->namespace = $namespace; $this->revision_id = intval($revision_id); - // only do this if calling from the (very heavily feature filled) abstract - // this will still be called if you're using your own handler but not replacing the constructor - if ( __CLASS__ == 'Namespace_Default' ) + // grab the cdata + $this->build_cdata(); + + $this->page_protected = $this->cdata['really_protected'] ? true : false; + switch($this->cdata['wiki_mode']) { - $this->exists = false; - // NOTE! These should already be WELL sanitized before we reach this stage. - $q = $db->sql_query('SELECT name FROM ' . table_prefix . "pages WHERE urlname = '$this->page_id' AND namespace = '$this->namespace';"); - if ( !$q ) - $db->_die(); + 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); - if ( $db->numrows() < 1 ) - { - // we still have a chance... some older databases don't do dots in the page title right - if ( strstr(dirtify_page_id($this->page_id), '.') ) - { - $page_id = str_replace('.', '.2e', $page_id); - - $q = $db->sql_query('SELECT name FROM ' . table_prefix . "pages WHERE urlname = '$page_id' AND namespace = '$this->namespace';"); - if ( !$q ) - $db->_die(); - - if ( $db->numrows() < 1 ) - { - $this->title = $paths->nslist[$namespace] . dirtify_page_id($page_id); - } - else - { - list($this->title) = $db->fetchrow_num(); - $this->exists = true; - $this->page_id = $page_id; - } - } - else - { - $this->title = $paths->nslist[$namespace] . dirtify_page_id($page_id); - } - } - else - { - list($this->title) = $db->fetchrow_num(); - $this->exists = true; - } - $db->free_result(); + $page_name = $this->namespace == 'Article' ? dirtify_page_id($this->page_id) : "{$this->namespace}{$ns_char}" . dirtify_page_id($this->page_id); + $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; } /** @@ -287,6 +338,8 @@ $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); @@ -299,8 +352,7 @@ $oldtarget[0] = sanitize_page_id($oldtarget[0]); $url = makeUrlNS($oldtarget[1], $oldtarget[0], false, true); - $page_id_key = $paths->nslist[ $oldtarget[1] ] . $oldtarget[0]; - $page_data = $paths->pages[$page_id_key]; + $page_data = $paths->get_cdata($oldtarget[0], $oldtarget[1]); $title = ( isset($page_data['name']) ) ? $page_data['name'] : $paths->nslist[$oldtarget[1]] . htmlspecialchars( str_replace('_', ' ', dirtify_page_id( $oldtarget[0] ) ) ); if ( !isset($page_data['name']) ) { @@ -331,7 +383,6 @@ if ( $send_headers ) { - $template->init_vars($this); $output->set_title($this->title); $output->header(); } @@ -339,7 +390,23 @@ if ( $incl_inner_headers ) { - display_page_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 ) @@ -363,8 +430,7 @@ } else { - $pathskey = $paths->nslist[ $this->namespace ] . $this->page_id; - $page_format = $paths->pages[$pathskey]['page_format']; + $page_format = $this->cdata['page_format']; } if ( $redir_enabled ) @@ -378,6 +444,8 @@ 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' ) @@ -400,6 +468,8 @@ 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 ) { @@ -411,6 +481,8 @@ display_page_footers(); } + profiler_log("Namespace [$this->namespace, $this->page_id]: sent footers"); + if ( $send_headers ) $output->footer(); } @@ -709,7 +781,7 @@ $link = makeUrlNS('Category', $cid); $list[] = '' . htmlspecialchars($title) . ''; } - while ( $row = $db->fetchrow() ); + while ( $row = $db->fetchrow($q) ); $html .= implode(', ', $list); } else @@ -728,15 +800,189 @@ } 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); + + $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 */ - function exists() + 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']; + } + + // fix up deletion votes + if ( empty($cdata['delvotes']) ) + $cdata['delvotes'] = 0; + + // 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 * FROM ' . table_prefix . "pages WHERE urlname = '$page_id_db' AND namespace = '$namespace_db';"); + if ( !$q ) + $db->_die(); + + if ( $db->numrows() < 1 ) + { + $db->free_result(); + $cache[$pathskey] = false; + return false; + } + + $row = $db->fetchrow(); + $cache[$pathskey] = $row; + return $row; + } } /** @@ -755,3 +1001,7 @@ { } +class Namespace_Category extends Namespace_Default +{ +} + diff -r d52dfa1f08da -r 323c4cd1aa37 includes/namespaces/file.php --- a/includes/namespaces/file.php Mon May 04 23:07:00 2009 -0400 +++ b/includes/namespaces/file.php Tue May 05 00:10:26 2009 -0400 @@ -48,12 +48,7 @@ if ( $local_namespace != 'File' ) return null; - $selfn = $local_page_id; - if ( substr($paths->cpage['name'], 0, strlen($paths->nslist['File'])) == $paths->nslist['File']) - { - $selfn = substr($local_page_id, strlen($paths->nslist['File']), strlen($local_page_id)); - } - $selfn = $db->escape($selfn); + $selfn = $db->escape($this->page_id); $q = $db->sql_query('SELECT f.mimetype,f.time_id,f.size,l.log_id FROM ' . table_prefix . "files AS f\n" . " LEFT JOIN " . table_prefix . "logs AS l\n" . " ON ( l.time_id = f.time_id AND ( l.action = 'reupload' OR l.action IS NULL ) )\n" diff -r d52dfa1f08da -r 323c4cd1aa37 includes/namespaces/special.php --- a/includes/namespaces/special.php Mon May 04 23:07:00 2009 -0400 +++ b/includes/namespaces/special.php Tue May 05 00:10:26 2009 -0400 @@ -20,9 +20,50 @@ $this->page_id = sanitize_page_id($page_id); $this->namespace = $namespace; + $this->build_cdata(); $this->revision_id = intval($revision_id); + $this->page_protected = true; + $this->wiki_mode = 0; + } + + public function build_cdata() + { + global $db, $session, $paths, $template, $plugins; // Common objects + global $lang; + $this->exists = function_exists("page_{$this->namespace}_{$this->page_id}"); + + if ( isset($paths->pages[ $paths->get_pathskey($this->page_id, $this->namespace) ]) ) + { + $page_name = $paths->pages[ $paths->get_pathskey($this->page_id, $this->namespace) ]['name']; + } + else + { + $page_name = "{$paths->nslist[ $this->namespace ]}{$this->page_id}"; + if ( ($_ = $lang->get('specialpage_' . strtolower($this->page_id))) !== 'specialpage_' . strtolower($this->page_id) ) + { + $page_name = $_; + } + } + + $this->cdata = array( + 'name' => $page_name, + 'urlname' => $this->page_id, + 'namespace' => $this->namespace, + 'special' => 0, + 'visible' => 0, + 'comments_on' => 0, + 'protected' => 0, + 'delvotes' => 0, + 'delvote_ips' => '', + 'wiki_mode' => 2, + 'page_exists' => false, + 'page_format' => getConfig('default_page_format', 'wikitext') + ); + $this->cdata = Namespace_Default::bake_cdata($this->cdata); + + $this->title =& $this->cdata['name']; } function send() @@ -31,7 +72,7 @@ if ( $this->exists ) { - @call_user_func("page_{$this->namespace}_{$this->page_id}"); + call_user_func("page_{$this->namespace}_{$this->page_id}"); } else { @@ -57,4 +98,12 @@ echo "

$message

"; $output->footer(); } + + function set_conds() + { + parent::set_conds(); + + $this->conds['printable'] = false; + $this->conds['adminpage'] = false; + } } diff -r d52dfa1f08da -r 323c4cd1aa37 includes/namespaces/user.php --- a/includes/namespaces/user.php Mon May 04 23:07:00 2009 -0400 +++ b/includes/namespaces/user.php Tue May 05 00:10:26 2009 -0400 @@ -58,13 +58,7 @@ $page_urlname = dirtify_page_id($this->page_id); if ( $this->page_id == $paths->page_id && $this->namespace == $paths->namespace ) - { - $page_name = ( isset($paths->cpage['name']) ) ? $paths->cpage['name'] : $this->page_id; - } - else - { - $page_name = ( isset($paths->pages[$this->page_id]) ) ? $paths->pages[$this->page_id]['name'] : $this->page_id; - } + $page_name = $this->cdata['name']; $target_username = strtr($page_urlname, Array( @@ -80,13 +74,8 @@ { $page_name = $lang->get('userpage_page_title', array('username' => $target_username)); } - else - { - // User has a custom title for their userpage - $page_name = $paths->pages[ $paths->nslist[$this->namespace] . $this->page_id ]['name']; - } - $template->tpl_strings['PAGE_NAME'] = htmlspecialchars($page_name); + $output->set_title($page_name); $q = $db->sql_query('SELECT u.username, u.user_id AS authoritative_uid, u.real_name, u.email, u.reg_time, u.user_has_avatar, u.avatar_type, x.*, COUNT(c.comment_id) AS n_comments FROM '.table_prefix.'users u @@ -275,7 +264,7 @@ $parser->assign_bool(array( 'page_exists' => true )); - $page_title = htmlspecialchars($paths->pages[ $c_page_id ]['name']); + $page_title = get_page_title($c_page_id); } else { diff -r d52dfa1f08da -r 323c4cd1aa37 includes/pageprocess.php --- a/includes/pageprocess.php Mon May 04 23:07:00 2009 -0400 +++ b/includes/pageprocess.php Tue May 05 00:10:26 2009 -0400 @@ -181,10 +181,6 @@ if ( !$this->perms->get_permissions('read') ) { - if ( $this->send_headers ) - { - $template->init_vars($this); - } // Permission denied to read page. Is this one of our core pages that must always be allowed? // NOTE: Not even the administration panel will work if ACLs deny access to it. if ( $this->namespace == 'Special' && in_array($this->page_id, array('Login', 'Logout', 'LangExportJSON', 'CSS')) ) @@ -218,10 +214,6 @@ $admin_fail = false; if ( $this->namespace == 'Admin' && strstr($this->page_id, '/') ) { - if ( $this->send_headers ) - { - $template->init_vars($this); - } $this->page_id = substr($this->page_id, 0, strpos($this->page_id, '/')); $funcname = "page_{$this->namespace}_{$this->page_id}"; if ( function_exists($funcname) ) @@ -231,21 +223,19 @@ } if ( isPage($pathskey) ) { - if ( $this->send_headers ) - { - $template->init_vars($this); - } - if ( $paths->pages[$pathskey]['special'] == 1 ) + $cdata = $this->ns->get_cdata(); + + if ( $cdata['special'] == 1 ) { $this->send_headers = false; $strict_no_headers = true; $GLOBALS['output'] = new Output_Naked(); } - if ( isset($paths->pages[$pathskey]['password']) ) + if ( isset($cdata['password']) ) { - if ( $paths->pages[$pathskey]['password'] != '' && $paths->pages[$pathskey]['password'] != sha1('') ) + if ( $cdata['password'] != '' && $cdata['password'] != sha1('') ) { - $password =& $paths->pages[$pathskey]['password']; + $password =& $cdata['password']; if ( $this->password != $password ) { $this->err_wrong_password(); @@ -253,7 +243,7 @@ } } } - if ( isset($paths->pages[$pathskey]['require_admin']) && $paths->pages[$pathskey]['require_admin'] ) + if ( isset($cdata['require_admin']) && $cdata['require_admin'] ) { if ( $session->auth_level < USER_LEVEL_ADMIN ) { @@ -288,11 +278,6 @@ // We are all done. Ship off the page. - if ( $this->send_headers ) - { - $template->init_vars($this); - } - $this->ns->send(); } @@ -325,15 +310,12 @@ { return ''; } - $pathskey = $paths->nslist[ $this->namespace ] . $this->page_id; - if ( isPage($pathskey) ) + $cdata = $this->ns->get_cdata(); + if ( isset($cdata['password']) ) { - if ( isset($paths->pages[$pathskey]['password']) ) + if ( $cdata['password'] != sha1('') && $cdata['password'] !== $this->password && !empty($cdata['password']) ) { - if ( $paths->pages[$pathskey]['password'] != sha1('') && $paths->pages[$pathskey]['password'] !== $this->password && !empty($paths->pages[$pathskey]['password']) ) - { - return false; - } + return false; } } return $this->fetch_text(); @@ -462,9 +444,9 @@ } // Set page_format - $pathskey = $paths->nslist[ $this->namespace ] . $this->page_id; // Using @ due to warning thrown when saving new page - if ( @$paths->pages[ $pathskey ]['page_format'] !== $page_format ) + $cdata = $this->ns->get_cdata(); + if ( @$cdata['page_format'] !== $page_format ) { // Note: no SQL injection to worry about here. Everything that goes into this is sanitized already, barring some rogue plugin. // (and if there's a rogue plugin running, we have bigger things to worry about anyway.) @@ -906,15 +888,7 @@ } // Retrieve page metadata - $pathskey = $paths->nslist[ $this->namespace ] . $this->page_id; - if ( !isPage($pathskey) ) - { - return array( - 'success' => false, - 'error' => 'page_metadata_not_found' - ); - } - $metadata =& $paths->pages[$pathskey]; + $metadata = $this->ns->get_cdata(); // Log the action $username = $db->escape($session->username); @@ -1000,8 +974,7 @@ foreach ( $stack as $oldtarget ) { $url = makeUrlNS($oldtarget[1], $oldtarget[0], 'redirect=no', true); - $page_id_key = $paths->nslist[ $oldtarget[1] ] . $oldtarget[0]; - $page_data = $paths->pages[$page_id_key]; + $page_data = $this->ns->get_cdata(); $title = ( isset($page_data['name']) ) ? $page_data['name'] : $paths->nslist[$oldtarget[1]] . htmlspecialchars( str_replace('_', ' ', dirtify_page_id( $oldtarget[0] ) ) ); $a = '' . $title . ''; $output->add_after_header('' . $lang->get('page_msg_redirected_from', array('from' => $a)) . '
'); @@ -1125,14 +1098,13 @@ foreach ( $stack as $oldtarget ) { $url = makeUrlNS($oldtarget[1], $oldtarget[0], 'redirect=no', true); - $page_id_key = $paths->nslist[ $oldtarget[1] ] . $oldtarget[0]; - $page_data = $paths->pages[$page_id_key]; + $old_page = namespace_factory($oldtarget[0], $oldtarget[1]); + $page_data = $old_page->get_cdata(); $title = ( isset($page_data['name']) ) ? $page_data['name'] : $paths->nslist[$oldtarget[1]] . htmlspecialchars( str_replace('_', ' ', dirtify_page_id( $oldtarget[0] ) ) ); $a = '' . $title . ''; $url = makeUrlNS($this->namespace, $this->page_id, 'redirect=no', true); - $page_id_key = $paths->nslist[ $this->namespace ] . $this->page_id; - $page_data = $paths->pages[$page_id_key]; + $page_data = $this->ns->get_cdata(); $title = ( isset($page_data['name']) ) ? $page_data['name'] : $paths->nslist[$this->namespace] . htmlspecialchars( str_replace('_', ' ', dirtify_page_id( $this->page_id ) ) ); $b = '' . $title . ''; diff -r d52dfa1f08da -r 323c4cd1aa37 includes/pageutils.php --- a/includes/pageutils.php Mon May 04 23:07:00 2009 -0400 +++ b/includes/pageutils.php Tue May 05 00:10:26 2009 -0400 @@ -50,14 +50,18 @@ public static function getsource($page, $password = false) { global $db, $session, $paths, $template, $plugins; // Common objects - if(!isPage($page)) + if ( !isPage($page) ) { return ''; } - if(strlen($paths->pages[$page]['password']) == 40) + list($page_id, $namespace) = RenderMan::strToPageID($page); + $ns = namespace_factory($page_id, $namespace); + $cdata = $ns->get_cdata(); + + if ( strlen($cdata['password']) == 40 ) { - if(!$password || ( $password != $paths->pages[$page]['password'])) + if(!$password || ( $password != $cdata['password'])) { return 'invalid_password'; } @@ -111,65 +115,10 @@ public static function savepage($page_id, $namespace, $message, $summary = 'No edit summary given', $minor = false) { global $db, $session, $paths, $template, $plugins; // Common objects - $uid = sha1(microtime()); - $pname = $paths->nslist[$namespace] . $page_id; - if(!$session->get_permissions('edit_page')) - return 'Access to edit pages is denied.'; - - if(!isPage($pname)) - { - $create = PageUtils::createPage($page_id, $namespace); - if ( $create != 'good' ) - return 'The page did not exist, and I was not able to create it. The reported error was: ' . $create; - $paths->page_exists = true; - } - - // Check page protection - - $is_protected = false; - $page_data =& $paths->pages[$pname]; - // Is the protection semi? - if ( $page_data['protected'] == 2 ) - { - $is_protected = true; - // Page is semi-protected. Has the user been here for at least 4 days? - // 345600 seconds = 4 days - if ( $session->user_logged_in && ( $session->reg_time + 345600 ) <= time() ) - $is_protected = false; - } - // Is the protection full? - else if ( $page_data['protected'] == 1 ) - { - $is_protected = true; - } - - // If it's protected and we DON'T have even_when_protected rights, bail out - if ( $is_protected && !$session->get_permissions('even_when_protected') ) - { - return 'You don\'t have the necessary permissions to edit this page.'; - } - - // We're skipping the wiki mode check here because by default edit_page pemissions are AUTH_WIKIMODE. - // The exception here is the user's own userpage, which is overridden at the time of account creation. - // At that point it's set to AUTH_ALLOW, but obviously only for the user's own userpage. - - // Strip potentially harmful tags and PHP from the message, dependent upon permissions settings - $message = RenderMan::preprocess_text($message, false, false); - - $msg = $db->escape($message); - - $minor = $minor ? ENANO_SQL_BOOLEAN_TRUE : ENANO_SQL_BOOLEAN_FALSE; - $q='INSERT INTO ' . table_prefix.'logs(log_type,action,time_id,date_string,page_id,namespace,page_text,char_tag,author,edit_summary,minor_edit) VALUES(\'page\', \'edit\', '.time().', \''.enano_date('d M Y h:i a').'\', \'' . $paths->page_id . '\', \'' . $paths->namespace . '\', ' . ENANO_SQL_MULTISTRING_PRFIX . '\'' . $msg . '\', \'' . $uid . '\', \'' . $session->username . '\', \'' . $db->escape(htmlspecialchars($summary)) . '\', ' . $minor . ');'; - if(!$db->sql_query($q)) $db->_die('The history (log) entry could not be inserted into the logs table.'); - - $q = 'UPDATE ' . table_prefix.'page_text SET page_text=' . ENANO_SQL_MULTISTRING_PRFIX . '\'' . $msg . '\',char_tag=\'' . $uid . '\' WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\';'; - $e = $db->sql_query($q); - if(!$e) $db->_die('Enano was unable to save the page contents. Your changes have been lost :\'(.'); - - $paths->rebuild_page_index($page_id, $namespace); - - return 'good'; + $page = new PageProcessor($page_id, $namespace); + $cdata = $page->ns->get_cdata(); + return $page->update_page($message, $summary, $minor, $cdata['page_format']); } /** @@ -278,47 +227,8 @@ { global $db, $session, $paths, $template, $plugins; // Common objects - $pname = $paths->nslist[$namespace] . $page_id; - $wiki = ( ( $paths->pages[$pname]['wiki_mode'] == 2 && getConfig('wiki_mode') == '1') || $paths->pages[$pname]['wiki_mode'] == 1) ? true : false; - $prot = ( ( $paths->pages[$pname]['protected'] == 2 && $session->user_logged_in && $session->reg_time + 60*60*24*4 < time() ) || $paths->pages[$pname]['protected'] == 1) ? true : false; - - if ( !$session->get_permissions('protect') ) - { - return('Insufficient access rights'); - } - if ( !$wiki ) - { - return('Page protection only has an effect when Wiki Mode is enabled.'); - } - if ( !preg_match('#^([0-9]+){1}$#', (string)$level) ) - { - return('Invalid $level parameter.'); - } - - switch($level) - { - case 0: - $q = 'INSERT INTO ' . table_prefix.'logs(time_id,date_string,log_type,action,author,page_id,namespace,edit_summary) VALUES('.time().', \''.enano_date('d M Y h:i a').'\', \'page\', \'unprot\', \'' . $session->username . '\', \'' . $page_id . '\', \'' . $namespace . '\', \'' . $db->escape(htmlspecialchars($reason)) . '\');'; - break; - case 1: - $q = 'INSERT INTO ' . table_prefix.'logs(time_id,date_string,log_type,action,author,page_id,namespace,edit_summary) VALUES('.time().', \''.enano_date('d M Y h:i a').'\', \'page\', \'prot\', \'' . $session->username . '\', \'' . $page_id . '\', \'' . $namespace . '\', \'' . $db->escape(htmlspecialchars($reason)) . '\');'; - break; - case 2: - $q = 'INSERT INTO ' . table_prefix.'logs(time_id,date_string,log_type,action,author,page_id,namespace,edit_summary) VALUES('.time().', \''.enano_date('d M Y h:i a').'\', \'page\', \'semiprot\', \'' . $session->username . '\', \'' . $page_id . '\', \'' . $namespace . '\', \'' . $db->escape(htmlspecialchars($reason)) . '\');'; - break; - default: - return 'PageUtils::protect(): Invalid value for $level'; - break; - } - if(!$db->sql_query($q)) $db->_die('The log entry for the page protection could not be inserted.'); - - $q = $db->sql_query('UPDATE ' . table_prefix.'pages SET protected=' . $level . ' WHERE urlname=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\';'); - if ( !$q ) - { - $db->_die('The pages table was not updated.'); - } - - return('good'); + $page = new PageProcessor($page_id, $namespace); + return $page->protect_page($level, $reason); } /** @@ -339,7 +249,9 @@ ob_start(); - $pname = $paths->nslist[$namespace] . $page_id; + $pname = $paths->get_pathskey($page_id, $namespace); + $ns = namespace_factory($page_id, $namespace); + $cdata = $ns->get_cdata(); if ( !isPage($pname) ) { @@ -348,15 +260,15 @@ if ( isPage($pname['password']) ) { - $password_exists = ( !empty($paths->pages[$pname]['password']) && $paths->pages[$pname]['password'] !== sha1('') ); - if ( $password_exists && $password !== $paths->pages[$pname]['password'] ) + $password_exists = ( !empty($cdata['password']) && $cdata['password'] !== sha1('') ); + if ( $password_exists && $password !== $cdata['password'] ) { return '

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

'; } } - $wiki = ( ( $paths->pages[$pname]['wiki_mode'] == 2 && getConfig('wiki_mode') == '1') || $paths->pages[$pname]['wiki_mode'] == 1) ? true : false; - $prot = ( ( $paths->pages[$pname]['protected'] == 2 && $session->user_logged_in && $session->reg_time + 60*60*24*4 < time() ) || $paths->pages[$pname]['protected'] == 1) ? true : false; + $wiki = ( ( $cdata['wiki_mode'] == 2 && getConfig('wiki_mode') == '1') || $cdata['wiki_mode'] == 1) ? true : false; + $prot = ( ( $cdata['protected'] == 2 && $session->user_logged_in && $session->reg_time + 60*60*24*4 < time() ) || $cdata['protected'] == 1) ? true : false; $q = 'SELECT log_id,time_id,date_string,page_id,namespace,author,edit_summary,minor_edit FROM ' . table_prefix.'logs WHERE log_type=\'page\' AND action=\'edit\' AND page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\' AND is_draft != 1 ORDER BY time_id DESC;'; if(!$db->sql_query($q)) $db->_die('The history data for the page "' . $paths->cpage['name'] . '" could not be selected.'); @@ -1055,40 +967,8 @@ global $db, $session, $paths, $template, $plugins; // Common objects global $lang; - $pname = $paths->nslist[$namespace] . $page_id; - - $prot = ( ( $paths->pages[$pname]['protected'] == 2 && $session->user_logged_in && $session->reg_time + 60*60*24*4 < time() ) || $paths->pages[$pname]['protected'] == 1) ? true : false; - $wiki = ( ( $paths->pages[$pname]['wiki_mode'] == 2 && getConfig('wiki_mode') == '1') || $paths->pages[$pname]['wiki_mode'] == 1) ? true : false; - - if( empty($name)) - { - return($lang->get('ajax_rename_too_short')); - } - if( ( $session->get_permissions('rename') && ( ( $prot && $session->get_permissions('even_when_protected') ) || !$prot ) ) && ( $paths->namespace != 'Special' && $paths->namespace != 'Admin' )) - { - $e = $db->sql_query('INSERT INTO ' . table_prefix.'logs(time_id,date_string,log_type,action,page_id,namespace,author,edit_summary) VALUES('.time().', \''.enano_date('d M Y h:i a').'\', \'page\', \'rename\', \'' . $db->escape($paths->page_id) . '\', \'' . $paths->namespace . '\', \'' . $db->escape($session->username) . '\', \'' . $db->escape($paths->cpage['name']) . '\')'); - if ( !$e ) - { - $db->_die('The page title could not be updated.'); - } - $e = $db->sql_query('UPDATE ' . table_prefix.'pages SET name=\'' . $db->escape($name) . '\' WHERE urlname=\'' . $db->escape($page_id) . '\' AND namespace=\'' . $db->escape($namespace) . '\';'); - if ( !$e ) - { - $db->_die('The page title could not be updated.'); - } - else - { - $subst = array( - 'page_name_old' => $paths->pages[$pname]['name'], - 'page_name_new' => $name - ); - return $lang->get('ajax_rename_success', $subst); - } - } - else - { - return($lang->get('etc_access_denied')); - } + $page = new PageProcessor($page_id, $namespace); + return $page->rename_page($name); } /** @@ -1120,7 +1000,7 @@ // If the page exists, make a backup of it in case it gets spammed/vandalized // If not, the admin's probably deleting a trash page - if ( isset($paths->pages[ $paths->nslist[$namespace] . $page_id ]) ) + if ( isPage($paths->get_pathskey($page_id, $namespace)) ) { $e = $db->sql_query('SELECT page_text,char_tag FROM ' . table_prefix.'page_text WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\';'); if(!$e) $db->_die('The current page text could not be selected; as a result, creating the backup of the page failed. Please make a backup copy of the page by clicking Edit this page and then clicking Save Changes.'); @@ -1256,8 +1136,11 @@ return 'The page does not exist.'; } - $cv =& $paths->pages[$pname]['delvotes']; - $ips = $paths->pages[$pname]['delvote_ips']; + $ns = namespace_factory($page_id, $namespace); + $cdata = $ns->get_cdata(); + + $cv =& $cdata['delvotes']; + $ips =& $cdata['delvote_ips']; if ( empty($ips) ) { @@ -1424,10 +1307,15 @@ $cat_current[] = $r; } $db->free_result(); - $cat_all = Array(); - foreach ( $paths->pages as $i => $_ ) + + $cat_all = array(); + $q = $db->sql_query('SELECT * FROM ' . table_prefix . 'pages WHERE namespace = \'Category\';'); + if ( !$q ) + $db->_die(); + + while ( $row = $db->fetchrow() ) { - if($paths->pages[$i]['namespace']=='Category') $cat_all[] = $paths->pages[$i]; + $cat_all[] = Namespace_Default::bake_cdata($row); } // Make $cat_all an associative array, like $paths->pages @@ -1498,12 +1386,17 @@ if(!$session->get_permissions('edit_cat')) return('Insufficient privileges to change category information'); $page_perms = $session->fetch_page_acl($page_id, $namespace); - $page_data =& $paths->pages[$paths->nslist[$namespace].$page_id]; + $ns = namespace_factory($page_id, $namespace); + $page_data = $ns->get_cdata(); - $cat_all = Array(); - foreach ( $paths->pages as $i => $_ ) + $cat_all = array(); + $q = $db->sql_query('SELECT * FROM ' . table_prefix . 'pages WHERE namespace = \'Category\';'); + if ( !$q ) + $db->_die(); + + while ( $row = $db->fetchrow() ) { - if($paths->pages[$i]['namespace']=='Category') $cat_all[] = $paths->pages[$i]; + $cat_all[] = Namespace_Default::bake_cdata($row); } // Make $cat_all an associative array, like $paths->pages @@ -1597,13 +1490,16 @@ global $db, $session, $paths, $template, $plugins; // Common objects global $lang, $cache; // Determine permissions - if($paths->pages[$paths->nslist[$namespace].$page_id]['password'] != '') + $ns = namespace_factory($page_id, $namespace); + $cdata = $ns->get_cdata(); + if ( $cdata['password'] != '' ) $a = $session->get_permissions('password_reset'); else $a = $session->get_permissions('password_set'); - if(!$a) + if ( !$a ) return $lang->get('etc_access_denied'); - if(!isset($pass)) return('Password was not set on URL'); + if ( !isset($pass) ) + return('Password was not set on URL'); $p = $pass; if ( !preg_match('#([0-9a-f]){40,40}#', $p) ) { @@ -1617,7 +1513,6 @@ { die('PageUtils::setpass(): Error during update query: '.$db->get_error()."\n\nSQL Backtrace:\n".$db->sql_backtrace()); } - $cache->purge('page_meta'); // Is the new password blank? if ( $p == '' ) { diff -r d52dfa1f08da -r 323c4cd1aa37 includes/paths.php --- a/includes/paths.php Mon May 04 23:07:00 2009 -0400 +++ b/includes/paths.php Tue May 05 00:10:26 2009 -0400 @@ -163,30 +163,6 @@ eval($cmd); } - if ( $page_cache = $cache->fetch('page_meta') ) - { - $this->pages = array_merge($this->pages, $page_cache); - } - else - { - $e = $db->sql_query('SELECT name,urlname,namespace,special,visible,comments_on,protected,delvotes,' . "\n" - . ' delvote_ips,wiki_mode,password,page_format FROM '.table_prefix.'pages ORDER BY name;'); - - if( !$e ) - { - $db->_die('The error seems to have occured while selecting the page information. File: includes/paths.php; line: '.__LINE__); - } - while($r = $db->fetchrow()) - { - $r = $this->calculate_metadata_from_row($r); - - $this->pages[$r['urlname']] = $r; - $this->pages[] =& $this->pages[$r['urlname']]; - } - - $this->update_metadata_cache(); - } - $db->free_result(); if ( defined('ENANO_INTERFACE_INDEX') || defined('ENANO_INTERFACE_AJAX') || defined('IN_ENANO_UPGRADE') ) { $title = $this->parse_url(false); @@ -391,6 +367,39 @@ $session->init_permissions(); } + /** + * Fetch cdata (metadata) for a page. + * @param string Page ID + * @param string Namespace + * @return array + */ + + function get_cdata($page_id, $namespace) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + $pathskey = $this->get_pathskey($page_id, $namespace); + if ( isset($this->pages[$pathskey]) ) + return $this->pages[$pathskey]; + + $page = namespace_factory($page_id, $namespace); + $cdata = $page->get_cdata(); + + $this->pages[$pathskey] = $cdata; + return $cdata; + } + + /** + * For a given page ID and namespace, generate a flat string that can be used to access $paths->pages. + * @param string Page ID + * @param string Namespace + */ + + function get_pathskey($page_id, $namespace) + { + return ( isset($this->nslist[$namespace]) ) ? "{$this->nslist[$namespace]}{$page_id}" : "{$namespace}:{$page_id}"; + } + function add_page($flags) { global $lang; @@ -404,10 +413,9 @@ } $flags['require_admin'] = ( $flags['namespace'] === 'Admin' ); + $flags['page_exists'] = true; - $pages_len = sizeof($this->pages) / 2; - $this->pages[$pages_len] = $flags; - $this->pages[$flags['urlname']] =& $this->pages[$pages_len]; + $this->pages[$flags['urlname']] = $flags; } function main_page() @@ -564,7 +572,7 @@ $title = $this->parse_url(false); list(, $ns) = RenderMan::strToPageID($title); $title = substr($title, strlen($this->nslist[$ns])); - $regex = '/^' . str_replace('/', '\\/', preg_quote($this->nslist[$this->namespace])) . '\\/?/'; + $regex = '/^' . str_replace('/', '\\/', preg_quote($this->nslist[$ns])) . '\\/?/'; $title = preg_replace($regex, '', $title); $title = explode('/', $title); $id = $id + 1; @@ -604,42 +612,14 @@ } /** - * Updates the cache containing all page metadata. + * Deprecated. */ function update_metadata_cache() { global $db, $session, $paths, $template, $plugins; // Common objects - if ( getConfig('cache_thumbs') != '1' ) - return false; - - $e = $db->sql_unbuffered_query('SELECT name,urlname,namespace,special,visible,comments_on,protected,delvotes,' . "\n" - . ' delvote_ips,wiki_mode,password,page_format FROM '.table_prefix.'pages ORDER BY name;'); - if ( !$e ) - $db->_die(); - - $md_array = array(); - - while ( $row = $db->fetchrow() ) - { - $row = $this->calculate_metadata_from_row($row); - $md_array[$row['urlname']] = $row; - } - - // import cache functions - global $cache; - - // store data (TTL 20 minutes) - try - { - $cache->store('page_meta', $md_array, 20); - } - catch ( Exception $e ) - { - } - - return true; + return false; } /** @@ -650,34 +630,7 @@ function calculate_metadata_from_row($r) { - $r['urlname_nons'] = $r['urlname']; - if ( isset($this->nslist[$r['namespace']]) ) - { - $r['urlname'] = $this->nslist[$r['namespace']] . $r['urlname']; // Applies the User:/File:/etc prefixes to the URL names - } - else - { - $ns_char = substr($this->nslist['Special'], -1); - $r['urlname'] = $r['namespace'] . $ns_char . $r['urlname']; - } - - if ( $r['delvotes'] == null) - { - $r['delvotes'] = 0; - } - if ( $r['protected'] == 0 || $r['protected'] == 1 ) - { - $r['really_protected'] = (int)$r['protected']; - } - else if ( $r['protected'] == 2 && getConfig('wiki_mode') == '1') - { - $r['really_protected'] = 1; - } - else if ( $r['protected'] == 2 && getConfig('wiki_mode') == '0' ) - { - $r['really_protected'] = 0; - } - return $r; + return Namespace_Default::bake_cdata($r); } /** @@ -838,7 +791,6 @@ $db->_die(); $sha1_blank = sha1(''); - $query_func = ( ENANO_DBLAYER == 'MYSQL' ) ? 'mysql_query' : 'pg_query'; // // Index $pages_in_batch pages at a time @@ -1082,65 +1034,6 @@ } /** - * Creates an instance of the Searcher class, including index info - * @return object - */ - - function makeSearcher($match_case = false) - { - global $db, $session, $paths, $template, $plugins; // Common objects - $search = new Searcher(); - $q = $db->sql_query('SELECT word,page_names FROM '.table_prefix.'search_index;'); - if(!$q) - { - echo $db->get_error(); - return false; - } - $idx = Array(); - while($row = $db->fetchrow($q)) - { - $row['word'] = rtrim($row['word'], "\0"); - $idx[$row['word']] = $row['page_names']; - } - $db->free_result(); - $search->index = $idx; - if($match_case) - $search->match_case = true; - return $search; - } - - /** - * Creates an associative array filled with the values of all the page titles - * @return array - */ - - function get_page_titles() - { - $texts = Array(); - for ( $i = 0; $i < sizeof($this->pages) / 2; $i++ ) - { - $texts[$this->pages[$i]['urlname']] = $this->pages[$i]['name']; - } - return $texts; - } - - /** - * Creates an instance of the Searcher class, including index info for page titles - * @return object - */ - - function makeTitleSearcher($match_case = false) - { - global $db, $session, $paths, $template, $plugins; // Common objects - $search = new Searcher(); - $texts = $this->get_page_titles(); - $search->buildIndex($texts); - if($match_case) - $search->match_case = true; - return $search; - } - - /** * Returns a list of groups that a given page is a member of. * @param string Page ID * @param string Namespace diff -r d52dfa1f08da -r 323c4cd1aa37 includes/plugins.php --- a/includes/plugins.php Mon May 04 23:07:00 2009 -0400 +++ b/includes/plugins.php Tue May 05 00:10:26 2009 -0400 @@ -96,6 +96,7 @@ { if ( in_array($plugin, $this->system_plugins) ) continue; + if ( $this->loaded_plugins[$plugin]['status'] & PLUGIN_OUTOFDATE ) { // it's out of date, don't load diff -r d52dfa1f08da -r 323c4cd1aa37 includes/render.php --- a/includes/render.php Mon May 04 23:07:00 2009 -0400 +++ b/includes/render.php Tue May 05 00:10:26 2009 -0400 @@ -55,7 +55,7 @@ public static function getTemplate($id, $parms) { global $db, $session, $paths, $template, $plugins; // Common objects - if(!isset($paths->pages[$paths->nslist['Template'].$id])) + if ( !isPage($paths->get_pathskey($id, 'Template')) ) { return '[['.$paths->nslist['Template'].$id.']]'; } @@ -65,7 +65,8 @@ } else { - $text = RenderMan::getPage($id, 'Template', 0, true, true, 0); + $page = new PageProcessor($id, 'Template'); + $text = $page->fetch_text(); $paths->template_cache[$id] = $text; } @@ -95,7 +96,7 @@ { global $db, $session, $paths, $template, $plugins; // Common objects $fetch_ns = 'Template'; - if(!isset($paths->pages[$paths->nslist['Template'].$id])) + if ( !isPage($paths->get_pathskey($id, 'Template')) ) { // Transclusion of another page // 1.1.5: Now You, Too, Can Be A Template, Even If You're Just A Plain Old Article! (TM) diff -r d52dfa1f08da -r 323c4cd1aa37 includes/search.php --- a/includes/search.php Mon May 04 23:07:00 2009 -0400 +++ b/includes/search.php Tue May 05 00:10:26 2009 -0400 @@ -251,16 +251,15 @@ $inc = 1; // Is this search term present in the page's title? If so, give extra points - preg_match("/^ns=$ns_list;pid=(.+)$/", $match, $piecesparts); - $pathskey = $paths->nslist[ $piecesparts[1] ] . sanitize_page_id($piecesparts[2]); - if ( isPage($pathskey) ) + preg_match("/^ns=$ns_list;pid=(.+)$/", $pages, $piecesparts); + $title = get_page_title_ns($piecesparts[2], $piecesparts[1]); + + $test_func = ( $case_sensitive ) ? 'strstr' : 'stristr'; + if ( $test_func($title, $row['word']) || $test_func($piecesparts[2], $row['word']) ) { - $test_func = ( $case_sensitive ) ? 'strstr' : 'stristr'; - if ( $test_func($paths->pages[$pathskey]['name'], $row['word']) || $test_func($paths->pages[$pathskey]['urlname_nons'], $row['word']) ) - { - $inc = 1.5; - } + $inc = 1.5; } + if ( isset($scores[$match]) ) { $scores[$match] = $scores[$match] + $inc; @@ -392,20 +391,15 @@ $inc = 1; // Is this search term present in the page's title? If so, give extra points - preg_match("/^ns=$ns_list;pid=(.+)$/", $id, $piecesparts); - $pathskey = $paths->nslist[ $piecesparts[1] ] . sanitize_page_id($piecesparts[2]); - if ( isPage($pathskey) ) + preg_match("/^ns=$ns_list;pid=(.+)$/", $pages, $piecesparts); + $title = get_page_title_ns($piecesparts[2], $piecesparts[1]); + + $test_func = ( $case_sensitive ) ? 'strstr' : 'stristr'; + if ( $test_func($title, $row['word']) || $test_func($piecesparts[2], $row['word']) ) { - $test_func = ( $case_sensitive ) ? 'strstr' : 'stristr'; - foreach ( array_merge($query_phrase['any'], $query_phrase['req']) as $term ) - { - if ( $test_func($paths->pages[$pathskey]['name'], $term) || $test_func($paths->pages[$pathskey]['urlname_nons'], $term) ) - { - $inc = 1.5; - break; - } - } + $inc = 1.5; } + if ( isset($scores[$id]) ) { $scores[$id] = $scores[$id] + $inc; diff -r d52dfa1f08da -r 323c4cd1aa37 includes/sessions.php --- a/includes/sessions.php Mon May 04 23:07:00 2009 -0400 +++ b/includes/sessions.php Tue May 05 00:10:26 2009 -0400 @@ -670,7 +670,7 @@ $captcha_good = true; } } - if ( $policy != 'disable' && !$captcha_good ) + if ( $lockout_data['lockout_policy'] != 'disable' && !$captcha_good ) { if ( $lockout_data['lockout_fails'] >= $lockout_data['lockout_threshold'] ) { @@ -2929,12 +2929,6 @@ return $objcache[$namespace][$page_id]; } - //if ( !isset( $paths->pages[$paths->nslist[$namespace] . $page_id] ) ) - //{ - // // Page does not exist - // return false; - //} - $objcache[$namespace][$page_id] = new Session_ACLPageInfo( $page_id, $namespace, $this->acl_types, $this->acl_descs, $this->acl_deps, $this->acl_base_cache ); $object =& $objcache[$namespace][$page_id]; @@ -4384,12 +4378,11 @@ $this->namespace = $namespace; $pathskey = $paths->nslist[$this->namespace].sanitize_page_id($this->page_id); - $ppwm = 2; - if ( isset($paths->pages[$pathskey]) ) - { - if ( isset($paths->pages[$pathskey]['wiki_mode']) ) - $ppwm = $paths->pages[$pathskey]['wiki_mode']; - } + $ns = namespace_factory($this->page_id, $this->namespace); + $cdata = $ns->get_cdata(); + $ppwm = $cdata['wiki_mode']; + unset($ns, $cdata); + if ( $ppwm == 1 && ( $session->user_logged_in || getConfig('wiki_mode_require_login') != '1' ) ) $this->wiki_mode = true; else if ( $ppwm == 1 && !$session->user_logged_in && getConfig('wiki_mode_require_login') == '1' ) diff -r d52dfa1f08da -r 323c4cd1aa37 includes/stats.php --- a/includes/stats.php Mon May 04 23:07:00 2009 -0400 +++ b/includes/stats.php Tue May 05 00:10:26 2009 -0400 @@ -65,25 +65,9 @@ while ( $row = $db->fetchrow() ) { - if ( isset($paths->pages[ $paths->nslist[ $row['namespace'] ] . $row['page_id'] ]) ) - { - $page_data =& $paths->pages[ $paths->nslist[ $row['namespace'] ] . $row['page_id'] ]; - $title = $page_data['name']; - $page_id = $page_data['urlname']; - } - else if ( !isset($paths->nslist[$row['namespace']]) ) - { - $title = $row['namespace'] . ':' . $row['page_id']; - $page_id = sanitize_page_id($title); - } - else - { - $title = dirtify_page_id( $paths->nslist[$row['namespace']] . $row['page_id'] ); - $title = str_replace('_', ' ', $title); - $page_id = sanitize_page_id($title); - } + $title = get_page_title_ns($row['page_id'], $row['namespace']); $data[] = array( - 'page_urlname' => $page_id, + 'page_urlname' => $paths->get_pathskey($row['page_id'], $row['namespace']), 'page_title' => $title, 'num_hits' => $row['num_hits'] ); diff -r d52dfa1f08da -r 323c4cd1aa37 includes/template.php --- a/includes/template.php Mon May 04 23:07:00 2009 -0400 +++ b/includes/template.php Tue May 05 00:10:26 2009 -0400 @@ -14,12 +14,27 @@ class template { - var $tpl_strings, $tpl_bool, $vars_assign_history, $theme, $style, $no_headers, $additional_headers, $sidebar_extra, $sidebar_widgets, $toolbar_menu, $theme_list, $named_theme_list, $default_theme, $default_style, $plugin_blocks, $plugin_blocks_content, $namespace_string, $style_list, $theme_loaded, $initted_to_page_id, $initted_to_namespace; + var $tpl_strings, $tpl_bool, $vars_assign_history, $theme, $style, $no_headers, $additional_headers, $sidebar_extra, $sidebar_widgets, $toolbar_menu, $theme_list, $named_theme_list, $default_theme, $default_style, $plugin_blocks, $plugin_blocks_content, $namespace_string, $style_list, $theme_loaded; + + var $theme_initted = false; + var $page_initted = false; + var $elements = false; + var $page_id = false; + var $namespace = false; - var $initted_to_theme = array( - 'theme' => false, - 'style' => false - ); + /** + * Page action conditions + * @var array + */ + + var $conds = array(); + + /** + * The PageProcessor for the current page + * @var object + */ + + var $page = false; /** * The list of themes that are critical for Enano operation. This doesn't include oxygen which @@ -287,7 +302,7 @@ function sidebar_widget($t, $h, $use_normal_section = false) { global $db, $session, $paths, $template, $plugins; // Common objects - if(!defined('ENANO_TEMPLATE_LOADED')) + if ( !$this->theme_loaded ) { $this->load_theme($session->theme, $session->style); } @@ -365,141 +380,65 @@ } /** - * Initializes all variables related to on-page content. This includes sidebars and what have you. - * @param object Optional PageProcessor object to use for passing metadata and permissions on. If omitted, uses information from $paths and $session. - * @param bool If true, re-inits even if already initted with this page_id and namespace + * Change the theme we're supposed to display. + * @param string Theme name + * @param string Style name; optional + */ + + function set_theme($theme = false, $style = false) + { + $this->theme_initted = false; + $this->load_theme($theme, $style); + } + + /** + * Change the page we're supposed to generate for + * @param mixed Page ID *or* PageProcessor. If a PageProcessor, pulls permission info and such from that; if not, starts a PageProcessor. YOU SHOULD USE A PageProcessor WHENEVER POSSIBLE! It improves efficiency. + * @param string Namespace; not required if including a PageProcessor. */ - function init_vars($page = false, $force_init = false) + function set_page($page_id_or_pp, $namespace = false) { - global $db, $session, $paths, $template, $plugins; // Common objects - global $email; - global $lang; - - if(!$this->theme || !$this->style) + if ( is_object($page_id_or_pp) && get_class($page_id_or_pp) === 'PageProcessor' ) { - $this->load_theme(); - } - - if ( defined('ENANO_TEMPLATE_LOADED') ) - { - // trigger_error("\$template->init_vars() called more than once", E_USER_WARNING); - // die_semicritical('Illegal call', '

$template->init_vars() was called multiple times, this is not supposed to happen. Exiting with fatal error.

'); - } - else - { - @define('ENANO_TEMPLATE_LOADED', ''); + $this->page_initted = false; + $page =& $page_id_or_pp; + $this->page = $page; + $this->page_id = $page->page_id; + $this->namespace = $page->namespace; } - - if ( is_object($page) && ( @get_class($page) == 'PageProcessor' || preg_match('/^Namespace_/', @get_class($page)) ) ) + else if ( is_string($page_id_or_pp) ) { - $page_append = substr($paths->fullpage, strlen($paths->page)); - if ( isset($paths->nslist[$page->namespace]) ) - { - $local_page = $paths->nslist[$page->namespace] . $page->page_id; - } - else - { - $local_page = $page->namespace . substr($paths->nslist['Special'], -1) . $page->page_id . $page_append; - } - $local_fullpage = $local_page . $page_append; - $local_page_id =& $page->page_id; - $local_namespace =& $page->namespace; - $local_page_exists = $page->exists(); - $perms =& $page->perms; - if ( !is_object($perms) ) - { - unset($perms); - $perms = $session->fetch_page_acl($local_page_id, $local_namespace); - } + if ( !is_string($namespace) ) + return false; + + if ( $page_id_or_pp === $this->page_id && $namespace === $this->namespace ) + return true; + + $this->page_initted = false; + $this->page = false; + $this->page_id = sanitize_page_id($page_id_or_pp); + $this->namespace = $namespace; } else { - $local_page =& $paths->page; - $local_page_id =& $paths->page_id; - $local_fullpage =& $paths->fullpage; - $local_namespace =& $paths->namespace; - $local_page_exists =& $paths->page_exists; - $local_page_protected =& $paths->page_protected; - $perms =& $session; - } - - if ( $local_page_id === $this->initted_to_page_id && $local_namespace === $this->initted_to_namespace && $this->theme === $this->initted_to_theme['theme'] && $this->style === $this->initted_to_theme['style'] && !$force_init ) - { - // we're already initted with this page. - return true; - } - - profiler_log("template: starting var init"); - - $this->initted_to_page_id = $local_page_id; - $this->initted_to_namespace = $local_namespace; - $this->initted_to_theme = array( - 'theme' => $this->theme, - 'style' => $this->style - ); - - require(ENANO_ROOT . "/themes/{$this->theme}/theme.cfg"); - - if ( $local_page_exists && isPage($local_page) ) - { - $local_cdata =& $paths->pages[$local_page]; + return false; } - else - { - // if the page doesn't exist but we're trying to load it, it was requested manually and $paths->cpage should match it. - if ( $paths->page_id == $local_page_id ) - { - // load metadata from cpage - $local_cdata =& $paths->cpage; - } - else - { - // generate our own failsafe metadata - $local_cdata = array( - 'urlname' => $local_page, - 'urlname_nons' => $local_page_id, - 'namespace' => $local_namespace, - 'name' => get_page_title_ns($local_page_id, $local_namespace), - 'comments_on' => 0, - 'protected' => 0, - 'wiki_mode' => 2, - 'delvotes' => 0, - 'delvote_ips' => serialize(array()) - ); - } - } + return true; + } + + /** + * Global, only-called-once init. Goes to all themes. + */ + + function init_global_vars() + { + global $db, $session, $paths, $template, $plugins; // Common objects + global $lang; + global $email; - // calculate protection - if ( !isset($local_page_protected) ) - { - if ( $local_cdata['protected'] == 0 ) - { - $local_page_protected = false; - } - else if ( $local_cdata['protected'] == 1 ) - { - $local_page_protected = true; - } - else if ( $local_cdata['protected'] == 2 ) - { - if ( - ( !$session->user_logged_in || // Is the user logged in? - ( $session->user_logged_in && $session->reg_time + ( 4 * 86400 ) >= time() ) ) // If so, have they been registered for 4 days? - && !$perms->get_permissions('even_when_protected') ) // And of course, is there an ACL that overrides semi-protection? - { - $local_page_protected = true; - } - else - { - $local_page_protected = false; - } - } - } - - $tplvars = $this->extract_vars('elements.tpl'); - - if(isset($_SERVER['HTTP_USER_AGENT']) && strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE')) + // IE PNG fixing code + if ( isset($_SERVER['HTTP_USER_AGENT']) && strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE') ) { $this->add_header('