diff --git a/.htaccess b/.htaccess index 4733ffa4a86..718c3bf51d4 100644 --- a/.htaccess +++ b/.htaccess @@ -86,15 +86,15 @@ DirectoryIndex index.php # # To redirect all users to access the site WITH the 'www.' prefix, # (http://example.com/... will be redirected to http://www.example.com/...) - # adapt and uncomment the following: - # RewriteCond %{HTTP_HOST} ^example\.com$ [NC] - # RewriteRule ^(.*)$ http://www.example.com/$1 [L,R=301] + # uncomment the following: + # RewriteCond %{HTTP_HOST} !^www\. [NC] + # RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301] # # To redirect all users to access the site WITHOUT the 'www.' prefix, # (http://www.example.com/... will be redirected to http://example.com/...) - # uncomment and adapt the following: - # RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC] - # RewriteRule ^(.*)$ http://example.com/$1 [L,R=301] + # uncomment the following: + # RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] + # RewriteRule ^ http://%1%{REQUEST_URI} [L,R=301] # Modify the RewriteBase if you are using Drupal in a subdirectory or in a # VirtualDocumentRoot and the rewrite rules are not working properly. diff --git a/cron.php b/cron.php index 79fd2381b1f..7917fad2f06 100644 --- a/cron.php +++ b/cron.php @@ -5,6 +5,11 @@ * Handles incoming requests to fire off regularly-scheduled tasks (cron jobs). */ -include_once './includes/bootstrap.inc'; +/** + * Root directory of Drupal installation. + */ +define('DRUPAL_ROOT', getcwd()); + +include_once DRUPAL_ROOT . '/includes/bootstrap.inc'; drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); drupal_cron_run(); diff --git a/includes/batch.inc b/includes/batch.inc index 63629875817..b7d0ce1495f 100644 --- a/includes/batch.inc +++ b/includes/batch.inc @@ -176,7 +176,7 @@ function _batch_process() { // request, we check if it requires an additional file for functions // definitions. if ($set_changed && isset($current_set['file']) && is_file($current_set['file'])) { - include_once($current_set['file']); + include_once DRUPAL_ROOT . '/' . $current_set['file']; } $finished = 1; @@ -295,7 +295,7 @@ function _batch_finished() { if (isset($batch_set['finished'])) { // Check if the set requires an additional file for functions definitions. if (isset($batch_set['file']) && is_file($batch_set['file'])) { - include_once($batch_set['file']); + include_once DRUPAL_ROOT . '/' . $batch_set['file']; } if (function_exists($batch_set['finished'])) { $batch_set['finished']($batch_set['success'], $batch_set['results'], $batch_set['operations']); diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index 8c5b2940d81..dd20e18e53c 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -337,7 +337,7 @@ function conf_path($require_settings = TRUE, $reset = FALSE) { for ($i = count($uri) - 1; $i > 0; $i--) { for ($j = count($server); $j > 0; $j--) { $dir = implode('.', array_slice($server, -$j)) . implode('.', array_slice($uri, 0, $i)); - if (file_exists("$confdir/$dir/settings.php") || (!$require_settings && file_exists("$confdir/$dir"))) { + if (file_exists(DRUPAL_ROOT . "/$confdir/$dir/settings.php") || (!$require_settings && file_exists(DRUPAL_ROOT . "/$confdir/$dir"))) { $conf = "$confdir/$dir"; return $conf; } @@ -407,8 +407,8 @@ function conf_init() { $_SERVER['HTTP_HOST'] = ''; } - if (file_exists('./'. conf_path() .'/settings.php')) { - include_once './'. conf_path() .'/settings.php'; + if (file_exists(DRUPAL_ROOT . '/'. conf_path() .'/settings.php')) { + include_once DRUPAL_ROOT . '/'. conf_path() .'/settings.php'; } // Ignore the placeholder URL from default.settings.php. @@ -520,7 +520,7 @@ function drupal_get_filename($type, $name, $filename = NULL) { $files[$type] = array(); } - if (!empty($filename) && file_exists($filename)) { + if (!empty($filename) && file_exists(DRUPAL_ROOT . '/'. $filename)) { $files[$type][$name] = $filename; } elseif (isset($files[$type][$name])) { @@ -530,7 +530,7 @@ function drupal_get_filename($type, $name, $filename = NULL) { // the database. This is required because this function is called both // before we have a database connection (i.e. during installation) and // when a database connection fails. - elseif (db_is_active() && (($file = db_result(db_query("SELECT filename FROM {system} WHERE name = '%s' AND type = '%s'", $name, $type))) && file_exists($file))) { + elseif (db_is_active() && (($file = db_result(db_query("SELECT filename FROM {system} WHERE name = '%s' AND type = '%s'", $name, $type))) && file_exists(DRUPAL_ROOT . '/'. $file))) { $files[$type][$name] = $file; } else { @@ -541,7 +541,7 @@ function drupal_get_filename($type, $name, $filename = NULL) { $file = (($type == 'theme_engine') ? "$name.engine" : "$name.$type"); foreach (array("$config/$dir/$file", "$config/$dir/$name/$file", "$dir/$file", "$dir/$name/$file") as $file) { - if (file_exists($file)) { + if (file_exists(DRUPAL_ROOT . '/'. $file)) { $files[$type][$name] = $file; break; } @@ -561,13 +561,21 @@ function drupal_get_filename($type, $name, $filename = NULL) { * file. */ function variable_init($conf = array(), $regenerate = FALSE, $recursion_depth = 0) { - // NOTE: caching the variables improves performance by 20% when serving cached pages. + // NOTE: caching the variables improves performance by 20% when serving + // cached pages. if (!$regenerate && $cached = cache_get('variables', 'cache')) { $variables = $cached->data; } else { if (defined('MAINTENANCE_MODE') || lock_acquire('variable_cache_regenerate')) { $result = db_query('SELECT * FROM {variable}'); + // Exit here if the database went away. Do not want to pollute the cache + // with bad data. This request isn't going to end well anyway; end it + // eairly. + if ($result === FALSE) { + // This function calls exit. + _db_error_page(); + } while ($variable = db_fetch_object($result)) { $variables[$variable->name] = unserialize($variable->value); } @@ -580,15 +588,27 @@ function variable_init($conf = array(), $regenerate = FALSE, $recursion_depth = // Wait for another request that is already doing this work. lock_wait('variable_cache_regenerate'); - // Run the function again. Try a limited number of times to avoid - // infinite recursion if the database connection is invalid for + // Run the function again. Try a limited number of times to avoid + // infinite recursion if the database connection is invalid for // some reason, e.g., mysqld restart, loss of network, etc. $recursion_depth++; if ($recursion_depth < 50) { return variable_init($conf, $regenerate, $recursion_depth); } - $variables = array(); + // If the recursion_depth hit the limit, assume we aren't going to get it + // from the cache or the lock will be released any time soon. Give up and + // get variables from the database. + $result = db_query('SELECT * FROM {variable}'); + // Exit here if the database went away. Do not want to pollute the cache + // with bad data. This request isn't going to end well. + if ($result === FALSE) { + // This function calls exit. + _db_error_page(); + } + while ($variable = db_fetch_object($result)) { + $variables[$variable->name] = unserialize($variable->value); + } } } @@ -653,7 +673,7 @@ function variable_set($name, $value) { if (is_string($db_prefix) && strpos($db_prefix, 'simpletest') === 0) { cache_clear_all('variables', 'cache'); } - + variable_cache_rebuild(); } @@ -682,7 +702,7 @@ function variable_del($name) { if (is_string($db_prefix) && strpos($db_prefix, 'simpletest') === 0) { cache_clear_all('variables', 'cache'); } - + variable_cache_rebuild(); } @@ -747,7 +767,11 @@ function drupal_page_is_cacheable($force = NULL) { * The name of the bootstrap hook we wish to invoke. */ function bootstrap_invoke_all($hook) { - foreach (module_list(TRUE, TRUE) as $module) { + static $bootstrap_modules; + if (!isset($bootstrap_modules)) { + $bootstrap_modules = module_list(TRUE, TRUE); + } + foreach ($bootstrap_modules as $module) { drupal_load('module', $module); module_invoke($module, $hook); } @@ -775,7 +799,7 @@ function drupal_load($type, $name) { $filename = drupal_get_filename($type, $name); if ($filename) { - include_once "./$filename"; + include_once DRUPAL_ROOT . "/$filename"; $files[$type][$name] = TRUE; return TRUE; @@ -805,7 +829,7 @@ function drupal_set_header($name = NULL, $value = NULL, $append = FALSE) { if (!isset($name)) { return $headers; } - + // Support the Drupal 6 header API if (!isset($value)) { if (strpos($name, ':') !== FALSE) { @@ -1423,7 +1447,7 @@ function drupal_bootstrap($phase = NULL) { _drupal_bootstrap($current_phase); } } - + return $phase_index; } @@ -1453,7 +1477,7 @@ function _drupal_bootstrap($phase) { case DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE: // Allow specifying special cache handlers in settings.php, like // using memcached or files for storing cache information. - require_once variable_get('cache_inc', './includes/cache.inc'); + require_once DRUPAL_ROOT . '/' . variable_get('cache_inc', 'includes/cache.inc'); // If the page_cache_fastpath is set to TRUE in settings.php and // page_cache_fastpath (implemented in the special implementation of // cache.inc) printed the page and indicated this with a returned TRUE @@ -1477,13 +1501,13 @@ function _drupal_bootstrap($phase) { } // Initialize the default database. - require_once './includes/database.inc'; + require_once DRUPAL_ROOT . '/includes/database.inc'; db_set_active(); // Allow specifying alternate lock implementations in settings.php, like // those using APC or memcached. - require_once variable_get('lock_inc', './includes/lock.inc'); + require_once DRUPAL_ROOT . '/' . variable_get('lock_inc', 'includes/lock.inc'); lock_init(); - + // Detect if an installation is present. detect_installation_or_run_installer(); @@ -1499,7 +1523,7 @@ function _drupal_bootstrap($phase) { break; case DRUPAL_BOOTSTRAP_SESSION: - require_once variable_get('session_inc', './includes/session.inc'); + require_once DRUPAL_ROOT . '/' . variable_get('session_inc', 'includes/session.inc'); drupal_session_initialize(); break; @@ -1519,7 +1543,7 @@ function _drupal_bootstrap($phase) { // If the skipping of the bootstrap hooks is not enforced, call hook_init. if (!is_object($cache) || $cache_mode != CACHE_AGGRESSIVE) { // Load module handling. - require_once './includes/module.inc'; + require_once DRUPAL_ROOT . '/includes/module.inc'; bootstrap_invoke_all('boot'); } @@ -1534,11 +1558,11 @@ function _drupal_bootstrap($phase) { // We are done. exit; } - + if (!$cache && drupal_page_is_cacheable() && $cache_mode != CACHE_EXTERNAL) { header('X-Drupal-Cache: MISS'); } - + // If using an external cache and the page is cacheable, set headers. if ($cache_mode == CACHE_EXTERNAL && drupal_page_is_cacheable()) { drupal_page_cache_header_external(); @@ -1556,13 +1580,13 @@ function _drupal_bootstrap($phase) { break; case DRUPAL_BOOTSTRAP_PATH: - require_once './includes/path.inc'; + require_once DRUPAL_ROOT . '/includes/path.inc'; // Initialize $_GET['q'] prior to loading modules and invoking hook_init(). drupal_init_path(); break; case DRUPAL_BOOTSTRAP_FULL: - require_once './includes/common.inc'; + require_once DRUPAL_ROOT . '/includes/common.inc'; _drupal_bootstrap_full(); break; } @@ -1577,7 +1601,7 @@ function _drupal_bootstrap($phase) { * @see _drupal_maintenance_theme() */ function drupal_maintenance_theme() { - require_once './includes/theme.maintenance.inc'; + require_once DRUPAL_ROOT . '/includes/theme.maintenance.inc'; _drupal_maintenance_theme(); } @@ -1605,7 +1629,7 @@ function drupal_init_language() { $language = language_default(); } else { - include_once './includes/language.inc'; + include_once DRUPAL_ROOT . '/includes/language.inc'; $language = language_initialize(); } } @@ -1679,17 +1703,17 @@ function ip_address() { if (!isset($ip_address)) { $ip_address = $_SERVER['REMOTE_ADDR']; - + // Only use parts of the X-Forwarded-For (XFF) header that have followed a trusted route. // Specifically, identify the leftmost IP address in the XFF header that is not one of ours. // An XFF header is: X-Forwarded-For: client1, proxy1, proxy2 if (isset($_SERVER['HTTP_' . variable_get('x_forwarded_for_header', 'X_FORWARDED_FOR')]) && variable_get('reverse_proxy', 0)) { // Load trusted reverse proxy server IPs. $reverse_proxy_addresses = variable_get('reverse_proxy_addresses', array()); - + // Turn XFF header into an array. $forwarded = explode(',', $_SERVER['HTTP_' . variable_get('x_forwarded_for_header', 'X_FORWARDED_FOR')]); - + // Trim the forwarded IPs; they may have been delimited by commas and spaces. $forwarded = array_map('trim', $forwarded); @@ -1698,7 +1722,7 @@ function ip_address() { // Eliminate all trusted IPs. $untrusted = array_diff($forwarded, $reverse_proxy_addresses); - + // The right-most IP is the most specific we can trust. $ip_address = array_pop($untrusted); } @@ -1712,9 +1736,9 @@ function ip_address() { */ function drupal_session_initialize() { global $user; - + session_set_save_handler('sess_open', 'sess_close', 'sess_read', 'sess_write', 'sess_destroy_sid', 'sess_gc'); - + if (isset($_COOKIE[session_name()])) { // If a session cookie exists, initialize the session. Otherwise the // session is only started on demand in drupal_session_commit(), making @@ -1776,7 +1800,7 @@ function drupal_session_commit() { /** * Return whether a session has been started. - */ + */ function drupal_session_started($set = NULL) { static $session_started = FALSE; if (isset($set)) { @@ -1841,7 +1865,7 @@ function drupal_save_session($status = NULL) { } return $save_session; } - + /** * Returns the current bootstrap phase for this Drupal process. * @@ -1865,8 +1889,7 @@ function drupal_valid_test_ua($user_agent) { // We use the database credentials from settings.php to make the HMAC key, since // the database is not yet initialized and we can't access any Drupal variables. // The file properties add more entropy not easily accessible to others. -// $filepath = DRUPAL_ROOT . '/includes/bootstrap.inc'; - $filepath = './includes/bootstrap.inc'; + $filepath = DRUPAL_ROOT . '/includes/bootstrap.inc'; // $key = sha1(serialize($databases) . filectime($filepath) . fileinode($filepath), TRUE); $key = sha1(serialize($db_url) . filectime($filepath) . fileinode($filepath), TRUE); // The HMAC must match. @@ -1885,9 +1908,7 @@ function drupal_generate_test_ua($prefix) { // We use the database credentials to make the HMAC key, since we // check the HMAC before the database is initialized. filectime() // and fileinode() are not easily determined from remote. -// $filepath = DRUPAL_ROOT . '/includes/bootstrap.inc'; - $filepath = './includes/bootstrap.inc'; -// $key = sha1(serialize($databases) . filectime($filepath) . fileinode($filepath), TRUE); + $filepath = DRUPAL_ROOT . '/includes/bootstrap.inc'; $key = sha1(serialize($db_url) . filectime($filepath) . fileinode($filepath), TRUE); } // Generate a moderately secure HMAC based on the database credentials. @@ -1914,7 +1935,7 @@ function drupal_is_cli() { */ function drupal_session_destroy() { session_destroy(); - + // Workaround PHP 5.2 fatal error "Failed to initialize storage module". // @see http://bugs.php.net/bug.php?id=32330 session_set_save_handler('sess_open', 'sess_close', 'sess_read', 'sess_write', 'sess_destroy_sid', 'sess_gc'); diff --git a/includes/common.inc b/includes/common.inc index 7ef1e51b1ba..3ec4aefd0c4 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -1148,7 +1148,7 @@ function format_xml_elements($array) { $output = ''; foreach ($array as $key => $value) { if (is_numeric($key)) { - if ($value['key']) { + if (!empty($value['key'])) { $output .= ' <'. $value['key']; if (isset($value['attributes']) && is_array($value['attributes'])) { $output .= drupal_attributes($value['attributes']); @@ -1572,13 +1572,11 @@ function url($path = NULL, $options = array()) { * An HTML string ready for insertion in a tag. */ function drupal_attributes($attributes = array()) { - if (is_array($attributes)) { - $t = ''; - foreach ($attributes as $key => $value) { - $t .= " $key=".'"'. check_plain($value) .'"'; - } - return $t; + $t = ''; + foreach ((array) $attributes as $attribute => $data) { + $t .= " $attribute=".'"'. check_plain($data) .'"'; } + return $t; } /** @@ -1668,6 +1666,9 @@ function drupal_page_footer() { } module_invoke_all('exit'); + + // Update the hook implementation cache. + module_implements('', FALSE, FALSE, TRUE); } /** @@ -2660,7 +2661,7 @@ function drupal_valid_token($token, $value = '', $skip_anonymous = FALSE) { * failed. See xmlrpc_error(). */ function xmlrpc($url) { - require_once './includes/xmlrpc.inc'; + require_once DRUPAL_ROOT . '/includes/xmlrpc.inc'; $args = func_get_args(); return call_user_func_array('_xmlrpc', $args); } @@ -2672,16 +2673,16 @@ function _drupal_bootstrap_full() { return; } $called = 1; - require_once './includes/theme.inc'; - require_once './includes/pager.inc'; - require_once './includes/menu.inc'; - require_once './includes/tablesort.inc'; - require_once './includes/file.inc'; - require_once './includes/unicode.inc'; - require_once './includes/image.inc'; - require_once './includes/form.inc'; - require_once './includes/mail.inc'; - require_once './includes/actions.inc'; + require_once DRUPAL_ROOT . '/includes/theme.inc'; + require_once DRUPAL_ROOT . '/includes/pager.inc'; + require_once DRUPAL_ROOT . '/includes/menu.inc'; + require_once DRUPAL_ROOT . '/includes/tablesort.inc'; + require_once DRUPAL_ROOT . '/includes/file.inc'; + require_once DRUPAL_ROOT . '/includes/unicode.inc'; + require_once DRUPAL_ROOT . '/includes/image.inc'; + require_once DRUPAL_ROOT . '/includes/form.inc'; + require_once DRUPAL_ROOT . '/includes/mail.inc'; + require_once DRUPAL_ROOT . '/includes/actions.inc'; // Set the Drupal custom error handler. set_error_handler('_drupal_error_handler'); set_exception_handler('_drupal_exception_handler'); @@ -3088,10 +3089,18 @@ function element_child($key) { } /** - * Get keys of a structured array tree element that are not properties (i.e., do not begin with '#'). + * Get keys of a structured array tree element that are not properties + * (i.e., do not begin with '#'). */ function element_children($element) { - return array_filter(array_keys((array) $element), 'element_child'); + $results = array(); + foreach ((array) $element as $key => $value) { + // Inlined call to element_child() for performance reasons. + if (!isset($key[0]) || $key[0] != '#') { + $results[] = $key; + } + } + return $results; } /** diff --git a/includes/database.inc b/includes/database.inc index efb4a90fa25..ff0c422da0d 100644 --- a/includes/database.inc +++ b/includes/database.inc @@ -53,15 +53,28 @@ define('DB_ERROR', 'a515ac9c2796ca0e23adbe92c68fc9fc'); * Perform an SQL query and return success or failure. * * @param $sql - * A string containing a complete SQL query. %-substitution - * parameters are not supported. + * A string containing an SQL query. + * @param ... + * A variable number of arguments which are substituted into the query + * using printf() syntax. Instead of a variable number of query arguments, + * you may also pass a single array containing the query arguments. + * + * Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose + * in '') and %%. + * + * NOTE: using this syntax will cast NULL and FALSE values to decimal 0, + * and TRUE values to decimal 1. + * * @return * An array containing the keys: * success: a boolean indicating whether the query succeeded * query: the SQL query executed, passed through check_plain() */ function update_sql($sql) { - $result = db_query($sql, true); + $args = func_get_args(); + array_shift($args); + $result = db_query($sql, $args); + $sql = db_query_get_string($sql, $args); return array('success' => $result !== FALSE, 'query' => check_plain($sql)); } @@ -117,16 +130,18 @@ function db_prefix_tables($sql) { * @param $name * The key in the $db_url global variable from settings.php. If omitted, the * default connection will be made active. + * @param $abort + * If database connection could not be made, should we abort here or continue. * * @return * The name of the previously active database, or FALSE if none was found. */ -function db_set_active($name = 'default') { +function db_set_active($name = 'default', $abort = TRUE) { global $db_url, $db_slave_url, $db_type, $active_db, $active_slave_db; static $db_conns, $db_slave_conns, $active_name = FALSE; if (empty($db_url)) { - include_once 'includes/install.inc'; + include_once DRUPAL_ROOT . '/includes/install.inc'; install_goto('install.php'); } @@ -139,7 +154,7 @@ function db_set_active($name = 'default') { $slave_connect_url = $db_slave_url[$name][$slave_index]; } else { - $slave_connect_url = $db_slave_url[$name]; + $slave_connect_url = $db_slave_url[$name]; } } else { @@ -149,12 +164,12 @@ function db_set_active($name = 'default') { $slave_connect_url = $db_slave_url[$slave_index]; } else { - $slave_connect_url = $db_slave_url; + $slave_connect_url = $db_slave_url; } } $db_type = substr($connect_url, 0, strpos($connect_url, '://')); - $handler = "./includes/database.$db_type.inc"; + $handler = DRUPAL_ROOT . "/includes/database.$db_type.inc"; if (is_file($handler)) { include_once $handler; @@ -163,9 +178,45 @@ function db_set_active($name = 'default') { _db_error_page("The database type '". $db_type ."' is unsupported. Please use either 'mysql' or 'mysqli' for MySQL, or 'pgsql' for PostgreSQL databases."); } - $db_conns[$name] = db_connect($connect_url); + $connection = db_connect($connect_url); + // db_connect is using array return notation. + if (is_array($connection)) { + list($success, $result) = $connection; + // Connection worked + if ($success) { + $db_conns[$name] = $result; + } + // Connection failed and abort page load. + elseif ($abort) { + _db_error_page($result); + } + // Connection failed but return false. Calling function will handle error. + else { + return FALSE; + } + } + // db_connect is not using array return notation; revert to old code style. + else { + $db_conns[$name] = $connection; + } if (!empty($slave_connect_url)) { - $db_slave_conns[$name] = db_connect($slave_connect_url); + $connection = db_connect($slave_connect_url); + // db_connect is using array return notation. + if (is_array($connection)) { + list($success, $result) = $connection; + // Connection worked + if ($success) { + $db_slave_conns[$name] = $result; + } + // Connection failed, set to false. + else { + $db_slave_conns[$name] = FALSE; + } + } + // db_connect is not using array return notation; revert to old code style. + else { + $db_slave_conns[$name] = $connection; + } } } @@ -173,7 +224,7 @@ function db_set_active($name = 'default') { // Set the active connection. $active_name = $name; $active_db = $db_conns[$name]; - if (isset($db_slave_conns[$name])) { + if (!empty($db_slave_conns[$name])) { $active_slave_db = $db_slave_conns[$name]; } else { @@ -696,7 +747,7 @@ function db_ignore_slave() { // the old data. $duration = variable_get('maximum_replication_lag', 300); // Set session variable with amount of time to delay before using slave. - // This will stick around for 5 minutes by default. + // This will stick around for 5 minutes by default. // There is another $_Session variable 'not_slavesafe' for single queries. $_SESSION['ignore_slave_server'] = $_SERVER['REQUEST_TIME'] + $duration; } diff --git a/includes/database.mysql-common.inc b/includes/database.mysql-common.inc index ead7f367c94..f10f8766e2a 100644 --- a/includes/database.mysql-common.inc +++ b/includes/database.mysql-common.inc @@ -42,6 +42,41 @@ function db_query($query) { return _db_query($query); } +/** + * Builds a query, returns string. + * + * User-supplied arguments to the query should be passed in as separate + * parameters so that they can be properly escaped to avoid SQL injection + * attacks. + * + * @param $query + * A string containing an SQL query. + * @param ... + * A variable number of arguments which are substituted into the query + * using printf() syntax. Instead of a variable number of query arguments, + * you may also pass a single array containing the query arguments. + * + * Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose + * in '') and %%. + * + * NOTE: using this syntax will cast NULL and FALSE values to decimal 0, + * and TRUE values to decimal 1. + * + * @return + * String of the query fully escaped and ready to run. + */ +function db_query_get_string($query) { + $args = func_get_args(); + array_shift($args); + $query = db_prefix_tables($query); + if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax + $args = $args[0]; + } + _db_query_callback($args, TRUE); + $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query); + return $query; +} + /** * @ingroup schemaapi * @{ diff --git a/includes/database.mysql.inc b/includes/database.mysql.inc index 4ff53e1fa77..1b5ca295528 100644 --- a/includes/database.mysql.inc +++ b/includes/database.mysql.inc @@ -11,7 +11,7 @@ */ // Include functions shared between mysql and mysqli. -require_once './includes/database.mysql-common.inc'; +require_once DRUPAL_ROOT . '/includes/database.mysql-common.inc'; /** * Report database status. @@ -76,7 +76,7 @@ function db_connect($url) { $connection = @mysql_connect($url['host'], $url['user'], $url['pass'], TRUE, 2); if (!$connection || !mysql_select_db(substr($url['path'], 1))) { // Show error screen otherwise - _db_error_page(mysql_error()); + return array(FALSE, mysql_error()); } // Force MySQL to use the UTF-8 character set. Also set the collation, if a @@ -89,7 +89,7 @@ function db_connect($url) { mysql_query('SET NAMES utf8', $connection); } - return $connection; + return array(TRUE, $connection); } /** @@ -134,34 +134,71 @@ function _db_query($query, $debug = 0, $slave = FALSE) { $query = '/* '. $name .' : '. $bt[2]['function'] .' */ '. $query; } - $sent_to_slave = FALSE; - if (isset($active_slave_db) && $slave) { - $result = mysql_query($query, $active_slave_db); - $sent_to_slave = TRUE; - } - else { - $result = mysql_query($query, $active_db); - } + $retry = 0; + do { + $sent_to_slave = FALSE; + if (isset($active_slave_db) && $slave) { + $result = mysql_query($query, $active_slave_db); + $errno = mysql_errno($active_slave_db); + $sent_to_slave = TRUE; + } + else { + $result = mysql_query($query, $active_db); + $errno = mysql_errno($active_db); + } - if (variable_get('dev_query', 0)) { - $query = $bt[2]['function'] ."\n". $query; - list($usec, $sec) = explode(' ', microtime()); - $stop = (float)$usec + (float)$sec; - $diff = $stop - $timer; - $queries[] = array($query, $diff, $sent_to_slave); - } + if (variable_get('dev_query', 0)) { + $query = $bt[2]['function'] ."\n". $query; + list($usec, $sec) = explode(' ', microtime()); + $stop = (float)$usec + (float)$sec; + $diff = $stop - $timer; + $queries[] = array($query, $diff, $sent_to_slave); + } - if (!$sent_to_slave) { - $active_last_db = $active_db; - } else { - $active_last_db = $active_slave_db; - } + // Handle transient errors. + // If we can handle this error, then increment the counter, so we run again. Otherwise + // use the default case to exit the while loop and return an error as per normal. + switch($errno) { + case 1153: // Got a packet bigger than "max_allowed_packet" bytes + // If the MySQL Server rejected the query because it's too big, + // writing the query to database watchdog will fail as well + // Truncate the query ... + $retry = 2; + $query = substr($query, 0, 100); + break; + + case 1205: // Lock wait timeout exceeded; try restarting transaction + case 1213: // Deadlock found when trying to get lock; try restarting transaction + $retry++; // Increment counter. + break; + + case 1614: // Transaction branch was rolled back: deadlock was detected + case 2013: // Lost connection to MySQL server during query + $retry++; // Increment counter. + if ($sent_to_slave) { + $slave = FALSE; // Retry on master. + } + break; + + // If not an error we can handle (or not an error!) exit the loop. + // We cal deal with fall-out later, as errno is set. + default: + $retry = 2; + break; + } + + if (!$sent_to_slave) { + $active_last_db = $active_db; + } else { + $active_last_db = $active_slave_db; + } + } while ($retry < 2); if ($debug) { print '

query: '. $query .'
error:'. mysql_error($active_db) .'

'; } - if (!mysql_errno($active_last_db)) { + if (!$errno) { return $result; } else { diff --git a/includes/database.mysqli.inc b/includes/database.mysqli.inc index 2ee710bdb75..0bfddf51ab2 100644 --- a/includes/database.mysqli.inc +++ b/includes/database.mysqli.inc @@ -14,7 +14,7 @@ */ // Include functions shared between mysql and mysqli. -require_once './includes/database.mysql-common.inc'; +require_once DRUPAL_ROOT . '/includes/database.mysql-common.inc'; /** * Report database status. @@ -75,7 +75,7 @@ function db_connect($url) { @mysqli_real_connect($connection, $url['host'], $url['user'], $url['pass'], substr($url['path'], 1), $url['port'], NULL, MYSQLI_CLIENT_FOUND_ROWS); if (mysqli_connect_errno() > 0) { - _db_error_page(mysqli_connect_error()); + return array(FALSE, mysqli_connect_error()); } // Force MySQL to use the UTF-8 character set. Also set the collation, if a @@ -88,7 +88,7 @@ function db_connect($url) { mysqli_query($connection, 'SET NAMES utf8'); } - return $connection; + return array(TRUE, $connection); } /** @@ -133,34 +133,72 @@ function _db_query($query, $debug = 0, $slave = FALSE) { $query = '/* '. $name .' : '. $bt[2]['function'] .' */ '. $query; } - $sent_to_slave = FALSE; - if (isset($active_slave_db) && $slave) { - $result = mysqli_query($active_slave_db, $query); - $sent_to_slave = TRUE; - } - else { - $result = mysqli_query($active_db, $query); - } + $retry = 0; + do { + $sent_to_slave = FALSE; + if (isset($active_slave_db) && $slave) { + $result = mysqli_query($active_slave_db, $query); + $errno = mysqli_errno($active_slave_db); + $sent_to_slave = TRUE; + } + else { + $result = mysqli_query($active_db, $query); + $errno = mysqli_errno($active_db); + } - if (variable_get('dev_query', 0)) { - $query = $bt[2]['function'] ."\n". $query; - list($usec, $sec) = explode(' ', microtime()); - $stop = (float)$usec + (float)$sec; - $diff = $stop - $timer; - $queries[] = array($query, $diff, $sent_to_slave); - } + if (variable_get('dev_query', 0)) { + $query = $bt[2]['function'] ."\n". $query; + list($usec, $sec) = explode(' ', microtime()); + $stop = (float)$usec + (float)$sec; + $diff = $stop - $timer; + $queries[] = array($query, $diff, $sent_to_slave); + } + + // Handle transient errors. + // If we can handle this error, then increment the counter, so we run again. Otherwise + // use the default case to exit the while loop and return an error as per normal. + switch($errno) { + case 1153: // Got a packet bigger than "max_allowed_packet" bytes + // If the MySQL Server rejected the query because it's too big, + // writing the query to database watchdog will fail as well + // Truncate the query ... + $retry = 2; + $query = substr($query, 0, 100); + break; + + case 1205: // Lock wait timeout exceeded; try restarting transaction + case 1213: // Deadlock found when trying to get lock; try restarting transaction + $retry++; // Increment counter. + break; + + case 1614: // Transaction branch was rolled back: deadlock was detected + case 2013: // Lost connection to MySQL server during query + $retry++; // Increment counter. + if ($sent_to_slave) { + $slave = FALSE; // Retry on master. + } + break; + + // If not an error we can handle (or not an error!) exit the loop. + // We cal deal with fall-out later, as errno is set. + default: + $retry = 2; + break; + } + + if (!$sent_to_slave) { + $active_last_db = $active_db; + } else { + $active_last_db = $active_slave_db; + } + } while ($retry < 2); - if (!$sent_to_slave) { - $active_last_db = $active_db; - } else { - $active_last_db = $active_slave_db; - } if ($debug) { print '

query: '. $query .'
error:'. mysqli_error($active_db) .'

'; } - if (!mysqli_errno($active_last_db)) { + if (!$errno) { return $result; } else { @@ -336,7 +374,7 @@ function db_query_range_slave($query) { * * Use this as a substitute for db_query() when the results need to be stored * in a temporary table. - * + * * User-supplied arguments to the query should be passed in as separate parameters * so that they can be properly escaped to avoid SQL injection attacks. * diff --git a/includes/database.pgsql.inc b/includes/database.pgsql.inc index 161de08f513..085015486df 100644 --- a/includes/database.pgsql.inc +++ b/includes/database.pgsql.inc @@ -77,15 +77,15 @@ function db_connect($url) { $connection = @pg_connect($conn_string); if (!$connection) { - require_once './includes/unicode.inc'; - _db_error_page(decode_entities($php_errormsg)); + require_once DRUPAL_ROOT . '/includes/unicode.inc'; + return array(FALSE, decode_entities($php_errormsg)); } // Restore error tracking setting ini_set('track_errors', $track_errors_previous); pg_query($connection, "set client_encoding=\"UTF8\""); - return $connection; + return array(TRUE, $connection); } /** @@ -124,6 +124,41 @@ function db_query($query) { return _db_query($query); } +/** + * Builds a query, returns string. + * + * User-supplied arguments to the query should be passed in as separate + * parameters so that they can be properly escaped to avoid SQL injection + * attacks. + * + * @param $query + * A string containing an SQL query. + * @param ... + * A variable number of arguments which are substituted into the query + * using printf() syntax. Instead of a variable number of query arguments, + * you may also pass a single array containing the query arguments. + * + * Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose + * in '') and %%. + * + * NOTE: using this syntax will cast NULL and FALSE values to decimal 0, + * and TRUE values to decimal 1. + * + * @return + * String of the query fully escaped and ready to run. + */ +function db_query_get_string($query) { + $args = func_get_args(); + array_shift($args); + $query = db_prefix_tables($query); + if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax + $args = $args[0]; + } + _db_query_callback($args, TRUE); + $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query); + return $query; +} + /** * Helper function for db_query(). */ diff --git a/includes/file.inc b/includes/file.inc index 4a3e3fc16c5..ced6adcfea0 100644 --- a/includes/file.inc +++ b/includes/file.inc @@ -311,7 +311,34 @@ function file_copy(&$source, $dest = 0, $replace = FILE_EXISTS_RENAME) { return FALSE; } - if (!@copy($source, $dest)) { + // Perform the replace operation. Since there could be multiple processes + // writing to the same file, the best option is to create a temporary file in + // the same directory and then rename it to the destination. A temporary file + // is needed if the directory is mounted on a separate machine; thus ensuring + // the rename command stays local. + $result = FALSE; + if ($replace == FILE_EXISTS_REPLACE) { + // Get a temporary filename in the destination directory. + $temporary_file = tempnam(dirname($dest), 'file'); + // Place contents in the temporary file. + if ($temporary_file && @copy($source, $temporary_file)) { + if (!$result = @rename($temporary_file, $dest)) { + // Unlink and try again for windows. Rename on windows does not replace + // the file if it already exists. + @unlink($dest); + $result = @rename($temporary_file, $dest); + } + // Remove temporary_file if rename failed. + if (!$result) { + @unlink($temporary_file); + } + } + } + // Perform the copy operation. + else { + $result = @copy($source, $dest); + } + if ($result === FALSE) { drupal_set_message(t('The selected file %file could not be copied.', array('%file' => $source)), 'error'); return 0; } @@ -520,7 +547,7 @@ function file_create_filename($basename, $directory) { */ function file_delete($path) { if (is_file($path)) { - return unlink($path); + return @unlink($path); } } diff --git a/includes/form.inc b/includes/form.inc index 11445b457bd..25baf686f98 100644 --- a/includes/form.inc +++ b/includes/form.inc @@ -302,7 +302,7 @@ function drupal_execute($form_id, &$form_state) { // Make sure $form_state is passed around by reference. $args[1] = &$form_state; - + $form = call_user_func_array('drupal_retrieve_form', $args); $form['#post'] = $form_state['values']; @@ -773,8 +773,8 @@ function form_execute_handlers($type, &$form, &$form_state) { foreach ($handlers as $function) { if (function_exists($function)) { - // Check to see if a previous _submit handler has set a batch, but - // make sure we do not react to a batch that is already being processed + // Check to see if a previous _submit handler has set a batch, but + // make sure we do not react to a batch that is already being processed // (for instance if a batch operation performs a drupal_execute()). if ($type == 'submit' && ($batch =& batch_get()) && !isset($batch['current_set'])) { // Some previous _submit handler has set a batch. We store the call @@ -900,7 +900,7 @@ function form_builder($form_id, $form, &$form_state) { // Recurse through all child elements. $count = 0; foreach (element_children($form) as $key) { - $form[$key]['#post'] = $form['#post']; + $form[$key]['#post'] = isset($form['#post']) ? $form['#post'] : NULL; $form[$key]['#programmed'] = $form['#programmed']; // Don't squash an existing tree value. if (!isset($form[$key]['#tree'])) { @@ -1342,6 +1342,9 @@ function form_set_value($form_item, $value, &$form_state) { function _form_set_value(&$form_values, $form_item, $parents, $value) { $parent = array_shift($parents); if (empty($parents)) { + if (!is_array($form_values)) { + $form_values = array(); + } $form_values[$parent] = $value; } else { @@ -2127,7 +2130,6 @@ function theme_textarea($element) { * * @ingroup themeable */ - function theme_markup($element) { return (isset($element['#value']) ? $element['#value'] : '') . (isset($element['#children']) ? $element['#children'] : ''); } @@ -2551,7 +2553,7 @@ function batch_process($redirect = NULL, $url = NULL) { else { // Non-progressive execution: bypass the whole progressbar workflow // and execute the batch in one pass. - require_once './includes/batch.inc'; + require_once DRUPAL_ROOT . '/includes/batch.inc'; _batch_process(); } } diff --git a/includes/image.inc b/includes/image.inc index 1580d317a96..808982666fc 100644 --- a/includes/image.inc +++ b/includes/image.inc @@ -40,7 +40,7 @@ function image_get_available_toolkits() { $output = array(); foreach ($toolkits as $file => $toolkit) { - include_once "./$file"; + include_once DRUPAL_ROOT . "/$file"; $function = str_replace('.', '_', $toolkit->name) .'_info'; if (function_exists($function)) { $info = $function(); @@ -62,7 +62,7 @@ function image_get_toolkit() { if (!$toolkit) { $toolkit = variable_get('image_toolkit', 'gd'); - $toolkit_file = './includes/image.'. $toolkit .'.inc'; + $toolkit_file = DRUPAL_ROOT . '/includes/image.'. $toolkit .'.inc'; if (isset($toolkit) && file_exists($toolkit_file)) { include_once $toolkit_file; } diff --git a/includes/install.inc b/includes/install.inc index e70f87ccbaa..4ceae8cd839 100644 --- a/includes/install.inc +++ b/includes/install.inc @@ -34,25 +34,41 @@ function drupal_load_updates() { * @param $module * A module name. * @return - * If the module has updates, an array of available updates sorted by version. + * If the module has updates, an array of available updates sorted by version. * Otherwise, FALSE. */ function drupal_get_schema_versions($module) { - $updates = array(); - $functions = get_defined_functions(); - foreach ($functions['user'] as $function) { - if (strpos($function, $module .'_update_') === 0) { - $version = substr($function, strlen($module .'_update_')); - if (is_numeric($version)) { - $updates[] = $version; + static $updates; + if (!isset($updates[$module])) { + $updates = array(); + + foreach (module_list() as $loaded_module) { + $updates[$loaded_module] = array(); + } + + // Prepare regular expression to match all possible defined hook_update_N(). + $regexp = '/^(?P.+)_update_(?P\d+)$/'; + $functions = get_defined_functions(); + // Narrow this down to functions ending with an integer, since all + // hook_update_N() functions end this way, and there are other + // possible functions which match '_update_'. We use preg_grep() here + // instead of foreaching through all defined functions, since the loop + // through all PHP functions can take significant page execution time + // and this function is called on every administrative page via + // system_requirements(). + foreach (preg_grep('/_\d+$/', $functions['user']) as $function) { + // If this function is a module update function, add it to the list of + // module updates. + if (preg_match($regexp, $function, $matches)) { + $updates[$matches['module']][] = $matches['version']; } } + // Ensure that updates are applied in numerical order. + foreach ($updates as &$module_updates) { + sort($module_updates, SORT_NUMERIC); + } } - if (count($updates) == 0) { - return FALSE; - } - sort($updates, SORT_NUMERIC); - return $updates; + return empty($updates[$module]) ? FALSE : $updates[$module]; } /** @@ -152,8 +168,8 @@ function drupal_detect_database_types() { $databases = array(); foreach (array('mysql', 'mysqli', 'pgsql') as $type) { - if (file_exists('./includes/install.'. $type .'.inc')) { - include_once './includes/install.'. $type .'.inc'; + if (file_exists(DRUPAL_ROOT . '/includes/install.'. $type .'.inc')) { + include_once DRUPAL_ROOT . '/includes/install.'. $type .'.inc'; $function = $type .'_is_available'; if ($function()) { $databases[$type] = $type; @@ -172,8 +188,8 @@ function drupal_detect_database_types() { * An array of settings that need to be updated. */ function drupal_rewrite_settings($settings = array(), $prefix = '') { - $default_settings = './sites/default/default.settings.php'; - $settings_file = './'. conf_path(FALSE, TRUE) .'/'. $prefix .'settings.php'; + $default_settings = DRUPAL_ROOT . '/sites/default/default.settings.php'; + $settings_file = DRUPAL_ROOT . '/' . conf_path(FALSE, TRUE) .'/'. $prefix .'settings.php'; // Build list of setting names and insert the values into the global namespace. $keys = array(); @@ -267,10 +283,10 @@ function drupal_get_install_files($module_list = array()) { * The list of modules to install. */ function drupal_verify_profile($profile, $locale) { - include_once './includes/file.inc'; - include_once './includes/common.inc'; + include_once DRUPAL_ROOT . '/includes/file.inc'; + include_once DRUPAL_ROOT . '/includes/common.inc'; - $profile_file = "./profiles/$profile/$profile.profile"; + $profile_file = DRUPAL_ROOT . "/profiles/$profile/$profile.profile"; if (!isset($profile) || !file_exists($profile_file)) { install_no_profile_error(); @@ -355,7 +371,7 @@ function _drupal_install_module($module) { */ function drupal_install_system() { $system_path = dirname(drupal_get_filename('module', 'system', NULL)); - require_once './'. $system_path .'/system.install'; + require_once DRUPAL_ROOT . '/' . $system_path . '/system.install'; module_invoke('system', 'install'); $system_versions = drupal_get_schema_versions('system'); $system_version = $system_versions ? max($system_versions) : SCHEMA_INSTALLED; @@ -630,16 +646,16 @@ function st($string, $args = array()) { if (!isset($locale_strings)) { $locale_strings = array(); - $filename = './profiles/'. $profile .'/translations/'. $install_locale .'.po'; + $filename = DRUPAL_ROOT . '/profiles/'. $profile .'/translations/'. $install_locale .'.po'; if (file_exists($filename)) { - require_once './includes/locale.inc'; + require_once DRUPAL_ROOT . '/includes/locale.inc'; $file = (object) array('filepath' => $filename); _locale_import_read_po('mem-store', $file); $locale_strings = _locale_import_one_string('mem-report'); } } - require_once './includes/theme.inc'; + require_once DRUPAL_ROOT . '/includes/theme.inc'; // Transform arguments before inserting them foreach ($args as $key => $value) { switch ($key[0]) { @@ -666,9 +682,9 @@ function st($string, $args = array()) { * Name of profile to check. */ function drupal_check_profile($profile) { - include_once './includes/file.inc'; + include_once DRUPAL_ROOT . '/includes/file.inc'; - $profile_file = "./profiles/$profile/$profile.profile"; + $profile_file = DRUPAL_ROOT . "/profiles/$profile/$profile.profile"; if (!isset($profile) || !file_exists($profile_file)) { install_no_profile_error(); @@ -714,7 +730,7 @@ function drupal_check_module($module) { // Include install file $install = drupal_get_install_files(array($module)); if (isset($install[$module])) { - require_once $install[$module]->filename; + require_once DRUPAL_ROOT . '/' . $install->filename; // Check requirements $requirements = module_invoke($module, 'requirements', 'install'); diff --git a/includes/locale.inc b/includes/locale.inc index 82723701197..3412ecb92b4 100644 --- a/includes/locale.inc +++ b/includes/locale.inc @@ -289,7 +289,7 @@ function _locale_languages_common_controls(&$form, $language = NULL) { function locale_languages_predefined_form_validate($form, &$form_state) { $langcode = $form_state['values']['langcode']; - if ($duplicate = db_result(db_query("SELECT COUNT(*) FROM {languages} WHERE language = '%s'", $langcode)) != 0) { + if ($duplicate = db_result(db_query_range("SELECT 1 FROM {languages} WHERE language = '%s'", $langcode, 0, 1)) != 0) { form_set_error('langcode', t('The language %language (%code) already exists.', array('%language' => $form_state['values']['name'], '%code' => $langcode))); } @@ -2602,7 +2602,7 @@ function _locale_batch_build($files, $finished = NULL, $components = array()) { 'title' => $t('Importing interface translations'), 'init_message' => $t('Starting import'), 'error_message' => $t('Error importing interface translations'), - 'file' => './includes/locale.inc', + 'file' => DRUPAL_ROOT . '/includes/locale.inc', // This is not a batch API construct, but data passed along to the // installer, so we know what did we import already. '#components' => $components, diff --git a/includes/mail.inc b/includes/mail.inc index 92540e2ea5f..9a9f7596fdf 100644 --- a/includes/mail.inc +++ b/includes/mail.inc @@ -172,7 +172,7 @@ function drupal_mail($module, $key, $to, $language, $params = array(), $from = N function drupal_mail_send($message) { // Allow for a custom mail backend. if (variable_get('smtp_library', '') && file_exists(variable_get('smtp_library', ''))) { - include_once './'. variable_get('smtp_library', ''); + include_once DRUPAL_ROOT . '/'. variable_get('smtp_library', ''); return drupal_mail_wrapper($message); } else { diff --git a/includes/menu.inc b/includes/menu.inc index 0d377d0d387..e11de864856 100644 --- a/includes/menu.inc +++ b/includes/menu.inc @@ -206,7 +206,16 @@ function menu_get_ancestors($parts) { $ancestors = array(); $length = $number_parts - 1; $end = (1 << $number_parts) - 1; - $masks = variable_get('menu_masks', array()); + $masks = variable_get('menu_masks', NULL); + // If the optimized menu_masks array is not available, try every character + // position to see if that matches up with a wildcard, slash, or the original + // value in order to get the correct $ancestors and $placeholders returned. + // Do not use this as the default value of the menu_masks variable to avoid + // building such a big array. Currently the maximum value for $end is 511 + // (2^9) - 1. 9 comes from the MENU_MAX_PARTS constant. + if (!$masks) { + $masks = range($end, 1); + } // Only examine patterns that actually exist as router items (the masks). foreach ($masks as $i) { if ($i > $end) { @@ -345,7 +354,7 @@ function menu_execute_active_handler($path = NULL) { if ($router_item = menu_get_item($path)) { if ($router_item['access']) { if ($router_item['file']) { - require_once($router_item['file']); + require_once DRUPAL_ROOT . '/' . $router_item['file']; } return call_user_func_array($router_item['page_callback'], $router_item['page_arguments']); } @@ -581,7 +590,7 @@ function _menu_translate(&$router_item, $map, $to_arg = FALSE) { $router_item['href'] = implode('/', $link_map); $router_item['options'] = array(); _menu_check_access($router_item, $map); - + // For performance, don't localize an item the user can't access. if ($router_item['access']) { _menu_item_localize($router_item, $map); @@ -656,7 +665,11 @@ function _menu_link_translate(&$item) { } // menu_tree_check_access() may set this ahead of time for links to nodes. if (!isset($item['access'])) { - if (!_menu_load_objects($item, $map)) { + // Loading a views object is expensive so we have a special case for it. + if (isset($item['access_callback']) && $item['access_callback'] == 'views_access' && $item['access_arguments'] == 'a:1:{i:0;b:1;}' && $item['title_callback'] == 't' && empty($item['title_arguments'])) { + $item['access'] = TRUE; + } + elseif (!_menu_load_objects($item, $map)) { // An error occurred loading an object. $item['access'] = FALSE; return FALSE; @@ -1025,6 +1038,11 @@ function _menu_tree_check_access(&$tree) { $new_tree = array(); foreach ($tree as $key => $v) { $item = &$tree[$key]['link']; + // Do not load hidden menu items if not in active breadcrumb trail and + // user can't administer the menu. + if (!empty($item['hidden']) && empty($item['in_active_trail']) && !user_access('administer menu')) { + continue; + } _menu_link_translate($item); if ($item['access']) { if ($tree[$key]['below']) { @@ -1695,7 +1713,7 @@ function menu_cache_clear_all() { function menu_rebuild() { if (!lock_acquire('menu_rebuild')) { // Wait for another request that is already doing this work. - // We choose to block here since otherwise the router item may not + // We choose to block here since otherwise the router item may not // be avaiable in menu_execute_active_handler() resulting in a 404. lock_wait('menu_rebuild'); return FALSE; @@ -1709,7 +1727,7 @@ function menu_rebuild() { // Clear the menu, page and block caches. menu_cache_clear_all(); _menu_clear_page_cache(); - + if (defined('MAINTENANCE_MODE')) { variable_set('menu_rebuild_needed', TRUE); } @@ -1717,9 +1735,9 @@ function menu_rebuild() { variable_del('menu_rebuild_needed'); } lock_release('menu_rebuild'); - + db_query('COMMIT'); - + return TRUE; } @@ -1904,7 +1922,7 @@ function _menu_delete_item($item, $force = FALSE) { * - router_path: The path of the relevant router item. * * @return - * The mlid of the saved menu link, or FALSE if the menu link could not be + * The mlid of the saved menu link, or FALSE if the menu link could not be * saved. */ function menu_link_save(&$item) { @@ -2519,7 +2537,7 @@ function _menu_site_is_offline() { return $_GET['q'] != 'user' && $_GET['q'] != 'user/login'; } // Logged in users are unprivileged here, so they are logged out. - require_once drupal_get_path('module', 'user') .'/user.pages.inc'; + require_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'user') .'/user.pages.inc'; user_logout(); } } diff --git a/includes/module.inc b/includes/module.inc index 79583675bd5..402da24bc7c 100644 --- a/includes/module.inc +++ b/includes/module.inc @@ -250,22 +250,22 @@ function module_exists($module) { */ function module_load_install($module) { // Make sure the installation API is available - include_once './includes/install.inc'; + include_once DRUPAL_ROOT . '/includes/install.inc'; module_load_include('install', $module); } /** * Load a module include file. - * + * * Examples: * @code * // Load node.admin.inc from the node module. * module_load_include('inc', 'node', 'node.admin'); * // Load content_types.inc from the node module. - * module_load_include('inc', 'node', 'content_types'); + * module_load_include('inc', 'node', 'content_types'); * @endcode - * + * * Do not use this function to load an install file. Use module_load_install() * instead. * @@ -274,7 +274,7 @@ function module_load_install($module) { * @param $module * The module to which the include file belongs. * @param $name - * Optionally, specify the base file name (without the $type extension). + * Optionally, specify the base file name (without the $type extension). * If not set, $module is used. */ function module_load_include($type, $module, $name = NULL) { @@ -282,7 +282,7 @@ function module_load_include($type, $module, $name = NULL) { $name = $module; } - $file = './'. drupal_get_path('module', $module) ."/$name.$type"; + $file = DRUPAL_ROOT . '/'. drupal_get_path('module', $module) ."/$name.$type"; if (is_file($file)) { require_once $file; @@ -420,22 +420,64 @@ function module_hook($module, $hook) { * @param $sort * By default, modules are ordered by weight and filename, settings this option * to TRUE, module list will be ordered by module name. - * @param $refresh + * @param $reset * For internal use only: Whether to force the stored list of hook * implementations to be regenerated (such as after enabling a new module, * before processing hook_enable). + * @param $write_cache + * For internal use only: Update the persistent cache of hook implementations. * @return * An array with the names of the modules which are implementing this hook. */ -function module_implements($hook, $sort = FALSE, $refresh = FALSE) { +function module_implements($hook, $sort = FALSE, $reset = FALSE, $write_cache = FALSE) { static $implementations; - - if ($refresh) { + static $verified; + + // We maintain a persistent cache of hook implementations in addition to the + // static cache to avoid looping through every module and every hook on each + // request. Benchmarks show that the benefit of this caching outweighs the + // additional database hit even when using the default database caching + // backend and only a small number of modules are enabled. The cost of the + // cache_get() is more or less constant and reduced further when non-database + // caching backends are used, so there will be more significant gains when a + // large number of modules are installed or hooks invoked, since this can + // quickly lead to module_hook() being called several thousand times + // per request. + if ($reset) { $implementations = array(); + $verified = array(); + cache_set('module_implements', array()); return; } + if ($write_cache) { + // Check whether we should write the cache. We do not want to cache hooks + // which are only invoked on HTTP POST requests since these do not need to + // be optimized as tightly, and not doing so keeps the cache entry smaller. + if (isset($implementations['#write_cache']) && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD')) { + unset($implementations['#write_cache']); + cache_set('module_implements', $implementations); + } + return; + } + + // Fetch implementations from cache. + if (empty($implementations)) { + if (function_exists('cache_get')) { + $cache = cache_get('module_implements'); + } + if (empty($cache)) { + $implementations = array(); + } + else { + $implementations = $cache->data; + } + } + if (!isset($implementations[$hook])) { + // The hook is not cached, so ensure that whether or not it has + // implementations, that the cache is updated at the end of the request. + $implementations['#write_cache'] = TRUE; $implementations[$hook] = array(); $list = module_list(FALSE, TRUE, $sort); foreach ($list as $module) { @@ -444,6 +486,20 @@ function module_implements($hook, $sort = FALSE, $refresh = FALSE) { } } } + elseif (empty($verified[$hook])) { + foreach ($implementations[$hook] as $key => $module) { + // It is possible that a module removed a hook implementation without the + // implementations cache being rebuilt yet, so we check module_hook() on + // each request to avoid undefined function errors. + if (!module_hook($module, $hook)) { + // Clear out the stale implementation from the cache and force a cache + // refresh to forget about no longer existing hook implementations. + unset($implementations[$hook][$key]); + $implementations['#write_cache'] = TRUE; + } + } + $verified[$hook] = TRUE; + } // The explicit cast forces a copy to be made. This is needed because // $implementations[$hook] is only a reference to an element of diff --git a/includes/theme.inc b/includes/theme.inc index 0b25f710e54..fb6ff58907d 100644 --- a/includes/theme.inc +++ b/includes/theme.inc @@ -164,7 +164,7 @@ function _init_theme($theme, $base_theme = array(), $registry_callback = '_theme // Initialize the theme. if (isset($theme->engine)) { // Include the engine. - include_once './'. $theme->owner; + include_once DRUPAL_ROOT . '/'. $theme->owner; $theme_engine = $theme->engine; if (function_exists($theme_engine .'_init')) { @@ -179,12 +179,12 @@ function _init_theme($theme, $base_theme = array(), $registry_callback = '_theme foreach ($base_theme as $base) { // Include the theme file or the engine. if (!empty($base->owner)) { - include_once './'. $base->owner; + include_once DRUPAL_ROOT . '/'. $base->owner; } } // and our theme gets one too. if (!empty($theme->owner)) { - include_once './'. $theme->owner; + include_once DRUPAL_ROOT . '/'. $theme->owner; } } @@ -298,23 +298,23 @@ function _theme_process_registry(&$cache, $name, $type, $theme, $path) { // files can prevent them from getting registered. if (isset($info['file']) && !isset($info['path'])) { // First, check to see if the fully qualified file exists. - $filename = './'. $path .'/'. $info['file']; - if (file_exists($filename)) { - require_once $filename; + $filename = $path .'/'. $info['file']; + if (file_exists(DRUPAL_ROOT . '/' . $filename)) { + require_once DRUPAL_ROOT . '/' . $filename; $result[$hook]['include files'][] = $filename; } else { - $filename = './'. $info['file']; - if (file_exists($filename)) { - require_once $filename; + $filename = $info['file']; + if (file_exists(DRUPAL_ROOT . '/' . $filename)) { + require_once DRUPAL_ROOT . '/' . $filename; $result[$hook]['include files'][] = $filename; } } } elseif (isset($info['file']) && isset($info['path'])) { - $filename = './'. $info['path'] .'/'. $info['file']; - if (file_exists($filename)) { - require_once $filename; + $filename = $info['path'] .'/'. $info['file']; + if (file_exists(DRUPAL_ROOT . '/' . $filename)) { + require_once DRUPAL_ROOT . '/' . $filename; $result[$hook]['include files'][] = $filename; } } @@ -482,7 +482,7 @@ function list_themes($refresh = FALSE) { if (db_is_active() && !defined('MAINTENANCE_MODE')) { $result = db_query("SELECT * FROM {system} WHERE type = '%s'", 'theme'); while ($theme = db_fetch_object($result)) { - if (file_exists($theme->filename)) { + if (file_exists(DRUPAL_ROOT . '/' . $theme->filename)) { $theme->info = unserialize($theme->info); $themes[] = $theme; } @@ -500,7 +500,7 @@ function list_themes($refresh = FALSE) { } } foreach ($theme->info['scripts'] as $script => $path) { - if (file_exists($path)) { + if (file_exists(DRUPAL_ROOT . '/' . $path)) { $theme->scripts[$script] = $path; } } @@ -606,7 +606,7 @@ function list_themes($refresh = FALSE) { * implementations for named objects. * @param ... * Additional arguments to pass along to the theme function. - * + * * @return * An HTML string that generates the themed output. */ @@ -642,7 +642,7 @@ function theme() { // Include a file if the theme function or preprocess function is held elsewhere. if (!empty($info['include files'])) { foreach ($info['include files'] as $include_file) { - include_once($include_file); + include_once DRUPAL_ROOT . '/' . $include_file; } } @@ -658,8 +658,8 @@ function theme() { // Statically cache files we've already tried to include so we don't // run unnecessary file_exists calls. $included_files[$include_file] = TRUE; - if (file_exists('./'. $include_file)) { - include_once('./'. $include_file); + if (file_exists(DRUPAL_ROOT . '/' . $include_file)) { + include_once DRUPAL_ROOT . '/' . $include_file; } } } @@ -765,8 +765,8 @@ function drupal_discover_template($paths, $suggestions, $extension = '.tpl.php') if (!empty($suggestion)) { $suggestion = str_replace(array("/", "\\", "\0"), '', $suggestion); foreach ($paths as $path) { - if (file_exists($file = $path .'/'. $suggestion . $extension)) { - return $file; + if (file_exists(DRUPAL_ROOT . '/' . $path .'/'. $suggestion . $extension)) { + return $path .'/'. $suggestion . $extension; } } } @@ -1076,7 +1076,7 @@ function theme_get_setting($setting_name, $refresh = FALSE) { function theme_render_template($template_file, $variables) { extract($variables, EXTR_SKIP); // Extract the variables to a local namespace ob_start(); // Start output buffering - include "./$template_file"; // Include the template file + include DRUPAL_ROOT . '/' . "$template_file"; // Include the template file $contents = ob_get_contents(); // Get the contents of the buffer ob_end_clean(); // End buffering and discard return $contents; // Return the contents @@ -1279,7 +1279,7 @@ function theme_links($links, $attributes = array('class' => 'links')) { * @param $getsize * If set to TRUE, the image's dimension are fetched and added as width/height attributes. * Defaults to TRUE. Must be set to FALSE if $path is a full URL. - * + * * @return * A string containing the image tag. */ diff --git a/includes/theme.maintenance.inc b/includes/theme.maintenance.inc index 2725057d8b2..60c54daa36c 100644 --- a/includes/theme.maintenance.inc +++ b/includes/theme.maintenance.inc @@ -21,13 +21,13 @@ function _drupal_maintenance_theme() { return; } - require_once './includes/path.inc'; - require_once './includes/theme.inc'; - require_once './includes/common.inc'; - require_once './includes/unicode.inc'; - require_once './includes/file.inc'; - require_once './includes/module.inc'; - require_once './includes/database.inc'; + require_once DRUPAL_ROOT . '/includes/path.inc'; + require_once DRUPAL_ROOT . '/includes/theme.inc'; + require_once DRUPAL_ROOT . '/includes/common.inc'; + require_once DRUPAL_ROOT . '/includes/unicode.inc'; + require_once DRUPAL_ROOT . '/includes/file.inc'; + require_once DRUPAL_ROOT . '/includes/module.inc'; + require_once DRUPAL_ROOT . '/includes/database.inc'; unicode_check(); // Install and update pages are treated differently to prevent theming overrides. diff --git a/includes/unicode.inc b/includes/unicode.inc index b1ce75760b9..9a1ba799c6b 100644 --- a/includes/unicode.inc +++ b/includes/unicode.inc @@ -336,7 +336,7 @@ function _mime_header_decode($matches) { function decode_entities($text, $exclude = array()) { static $html_entities; if (!isset($html_entities)) { - include_once './includes/unicode.entities.inc'; + include_once DRUPAL_ROOT . '/includes/unicode.entities.inc'; } // Flip the exclude list so that we can do quick lookups later. diff --git a/index.php b/index.php index cc504603123..58af9149327 100644 --- a/index.php +++ b/index.php @@ -11,7 +11,12 @@ * See COPYRIGHT.txt and LICENSE.txt. */ -require_once './includes/bootstrap.inc'; +/** + * Root directory of Drupal installation. + */ +define('DRUPAL_ROOT', getcwd()); + +require_once DRUPAL_ROOT . '/includes/bootstrap.inc'; drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); $return = menu_execute_active_handler(); diff --git a/install.php b/install.php index beb3fcb4f18..a44bbbe0d9e 100644 --- a/install.php +++ b/install.php @@ -1,6 +1,11 @@ filename; + require_once DRUPAL_ROOT . '/' . $profile->filename; return $profile->name; } elseif (sizeof($profiles) > 1) { @@ -476,7 +480,7 @@ function install_select_profile_form(&$form_state, $profile_files) { $names = array(); foreach ($profile_files as $profile) { - include_once($profile->filename); + include_once DRUPAL_ROOT . '/' . $profile->filename; // Load profile details and store them for later retrieval. $function = $profile->name .'_profile_details'; @@ -491,7 +495,7 @@ function install_select_profile_form(&$form_state, $profile_files) { $names[$profile->name] = $name; } - // Display radio buttons alphabetically by human-readable name. + // Display radio buttons alphabetically by human-readable name. natcasesort($names); foreach ($names as $profile => $name) { $form['profile'][$name] = array( @@ -514,7 +518,7 @@ function install_select_profile_form(&$form_state, $profile_files) { * Find all .po files for the current profile. */ function install_find_locales($profilename) { - $locales = file_scan_directory('./profiles/'. $profilename .'/translations', '\.po$', array('.', '..', 'CVS'), 0, FALSE); + $locales = file_scan_directory(DRUPAL_ROOT . '/profiles/'. $profilename .'/translations', '\.po$', array('.', '..', 'CVS'), 0, FALSE); array_unshift($locales, (object) array('name' => 'en')); return $locales; } @@ -526,8 +530,8 @@ function install_find_locales($profilename) { * The selected language. */ function install_select_locale($profilename) { - include_once './includes/file.inc'; - include_once './includes/form.inc'; + include_once DRUPAL_ROOT . '/includes/file.inc'; + include_once DRUPAL_ROOT . '/includes/form.inc'; // Find all available locales. $locales = install_find_locales($profilename); @@ -593,7 +597,7 @@ function install_select_locale($profilename) { * Form API array definition for language selection. */ function install_select_locale_form(&$form_state, $locales) { - include_once './includes/locale.inc'; + include_once DRUPAL_ROOT . '/includes/locale.inc'; $languages = _locale_get_predefined_list(); foreach ($locales as $locale) { // Try to use verbose locale name @@ -687,14 +691,14 @@ function install_tasks($profile, $task) { // to the same address, until the batch finished callback is invoked // and the task advances to 'locale-initial-import'. if ($task == 'profile-install-batch') { - include_once 'includes/batch.inc'; + include_once DRUPAL_ROOT . '/includes/batch.inc'; $output = _batch_page(); } // Import interface translations for the enabled modules. if ($task == 'locale-initial-import') { if (!empty($install_locale) && ($install_locale != 'en')) { - include_once 'includes/locale.inc'; + include_once DRUPAL_ROOT . '/includes/locale.inc'; // Enable installation language as default site language. locale_add_language($install_locale, NULL, NULL, NULL, NULL, NULL, 1, TRUE); // Collect files to import for this language. @@ -713,8 +717,8 @@ function install_tasks($profile, $task) { $task = 'configure'; } if ($task == 'locale-initial-batch') { - include_once 'includes/batch.inc'; - include_once 'includes/locale.inc'; + include_once DRUPAL_ROOT . '/includes/batch.inc'; + include_once DRUPAL_ROOT . '/includes/locale.inc'; $output = _batch_page(); } @@ -733,7 +737,7 @@ function install_tasks($profile, $task) { drupal_set_title(st('Configure site')); // Warn about settings.php permissions risk - $settings_dir = './'. conf_path(); + $settings_dir = DRUPAL_ROOT . '/'. conf_path(); $settings_file = $settings_dir .'/settings.php'; if (!drupal_verify_install_file($settings_file, FILE_EXIST|FILE_READABLE|FILE_NOT_WRITABLE) || !drupal_verify_install_file($settings_dir, FILE_NOT_WRITABLE, 'dir')) { drupal_set_message(st('All necessary changes to %dir and %file have been made, so you should remove write permissions to them now in order to avoid security risks. If you are unsure how to do so, please consult the on-line handbook.', array('%dir' => $settings_dir, '%file' => $settings_file, '@handbook_url' => 'http://drupal.org/getting-started')), 'error'); @@ -787,7 +791,7 @@ function install_tasks($profile, $task) { // control and proceed with importing the remaining translations. if ($task == 'profile-finished') { if (!empty($install_locale) && ($install_locale != 'en')) { - include_once 'includes/locale.inc'; + include_once DRUPAL_ROOT . '/includes/locale.inc'; // Collect files to import for this language. Skip components // already covered in the initial batch set. $batch = locale_batch_by_language($install_locale, '_install_locale_remaining_batch_finished', variable_get('install_locale_batch_components', array())); @@ -805,8 +809,8 @@ function install_tasks($profile, $task) { $task = 'finished'; } if ($task == 'locale-remaining-batch') { - include_once 'includes/batch.inc'; - include_once 'includes/locale.inc'; + include_once DRUPAL_ROOT . '/includes/batch.inc'; + include_once DRUPAL_ROOT . '/includes/locale.inc'; $output = _batch_page(); } @@ -821,17 +825,14 @@ function install_tasks($profile, $task) { // The end of the install process. Remember profile used. if ($task == 'done') { - // Rebuild menu to get content type links registered by the profile, - // and possibly any other menu items created through the tasks. - menu_rebuild(); + // Flush all caches to ensure that any full bootstraps during the installer + // do not leave stale cached data, and that any content types or other items + // registered by the install profile are registered correctly. + drupal_flush_all_caches(); // Register actions declared by any modules. actions_synchronize(); - // Randomize query-strings on css/js files, to hide the fact that - // this is a new install, not upgraded yet. - _drupal_flush_css_js(); - variable_set('install_profile', $profile); } @@ -902,7 +903,7 @@ function install_check_requirements($profile, $verify) { // If Drupal is not set up already, we need to create a settings file. if (!$verify) { $writable = FALSE; - $conf_path = './'. conf_path(FALSE, TRUE); + $conf_path = DRUPAL_ROOT . '/'. conf_path(FALSE, TRUE); $settings_file = $conf_path .'/settings.php'; $file = $conf_path; $exists = FALSE; @@ -957,7 +958,7 @@ function install_check_requirements($profile, $verify) { drupal_set_message($message, 'warning'); } } - } + } } /** diff --git a/modules/book/book.install b/modules/book/book.install index e3172f8b725..94d24bc1b00 100644 --- a/modules/book/book.install +++ b/modules/book/book.install @@ -106,7 +106,7 @@ function book_update_6000() { } // Determine whether there are any existing nodes in the book hierarchy. - if (db_result(db_query("SELECT COUNT(*) FROM {book}"))) { + if (db_result(db_query_range("SELECT 1 FROM {book}", 0, 1))) { // Temporary table for the old book hierarchy; we'll discard revision info. $schema['book_temp'] = array( 'fields' => array( diff --git a/modules/book/book.module b/modules/book/book.module index 56f839a33c6..65f17784cff 100644 --- a/modules/book/book.module +++ b/modules/book/book.module @@ -251,11 +251,14 @@ function theme_book_title_link($link) { * * This list may be used for generating a list of all the books, or for building * the options for a form select. + * + * @param $reset + * Reset the cache of stored books. */ -function book_get_books() { +function book_get_books($reset = FALSE) { static $all_books; - if (!isset($all_books)) { + if ($reset || !isset($all_books)) { $all_books = array(); $result = db_query("SELECT DISTINCT(bid) FROM {book}"); $nids = array(); @@ -463,12 +466,16 @@ function _book_update_outline(&$node) { if ($new) { // Insert new. db_query("INSERT INTO {book} (nid, mlid, bid) VALUES (%d, %d, %d)", $node->nid, $node->book['mlid'], $node->book['bid']); + // Reset the cache of stored books. + book_get_books(TRUE); } else { if ($node->book['bid'] != db_result(db_query("SELECT bid FROM {book} WHERE nid = %d", $node->nid))) { // Update the bid for this page and all children. book_update_bid($node->book); } + // Reset the cache of stored books. + book_get_books(TRUE); } return TRUE; } @@ -712,6 +719,8 @@ function book_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) { } menu_link_delete($node->book['mlid']); db_query('DELETE FROM {book} WHERE mlid = %d', $node->book['mlid']); + // Reset the cache of stored books. + book_get_books(TRUE); } break; case 'prepare': diff --git a/modules/color/color.module b/modules/color/color.module index 63fad7af91f..a8f620f3fd6 100644 --- a/modules/color/color.module +++ b/modules/color/color.module @@ -121,7 +121,7 @@ function _color_page_alter(&$vars) { */ function color_get_info($theme) { $path = drupal_get_path('theme', $theme); - $file = $path .'/color/color.inc'; + $file = DRUPAL_ROOT . '/' . $path .'/color/color.inc'; if ($path && file_exists($file)) { include $file; return $info; diff --git a/modules/dblog/dblog.admin.inc b/modules/dblog/dblog.admin.inc index 853a2ce14c8..3448bd00026 100644 --- a/modules/dblog/dblog.admin.inc +++ b/modules/dblog/dblog.admin.inc @@ -191,8 +191,10 @@ function dblog_build_filter_query() { foreach ($_SESSION['dblog_overview_filter'] as $key => $filter) { $filter_where = array(); foreach ($filter as $value) { - $filter_where[] = $filters[$key]['where']; - $args[] = $value; + if (isset($filters[$key]['where'])) { + $filter_where[] = $filters[$key]['where']; + $args[] = $value; + } } if (!empty($filter_where)) { $where[] = '('. implode(' OR ', $filter_where) .')'; diff --git a/modules/filter/filter.module b/modules/filter/filter.module index b95517914ee..2ed560d26da 100644 --- a/modules/filter/filter.module +++ b/modules/filter/filter.module @@ -911,7 +911,7 @@ function _filter_autop($text) { else { // Opening or closing tag? $open = ($chunk[1] != '/'); - list($tag) = split('[ >]', substr($chunk, 2 - $open), 2); + list($tag) = preg_split('/[ >]/', substr($chunk, 2 - $open), 2); if (!$ignore) { if ($open) { $ignore = TRUE; diff --git a/modules/locale/locale.module b/modules/locale/locale.module index 092eb4842ab..776525372be 100644 --- a/modules/locale/locale.module +++ b/modules/locale/locale.module @@ -175,7 +175,7 @@ function locale_menu() { function locale_inc_callback() { $args = func_get_args(); $function = array_shift($args); - include_once './includes/locale.inc'; + include_once DRUPAL_ROOT . '/includes/locale.inc'; return call_user_func_array($function, $args); } @@ -342,59 +342,68 @@ function locale($string = NULL, $langcode = NULL, $reset = FALSE) { $langcode = isset($langcode) ? $langcode : $language->language; - // Store database cached translations in a static var. - if (!isset($locale_t[$langcode])) { - $locale_t[$langcode] = array(); - // Disabling the usage of string caching allows a module to watch for - // the exact list of strings used on a page. From a performance - // perspective that is a really bad idea, so we have no user - // interface for this. Be careful when turning this option off! - if (variable_get('locale_cache_strings', 1) == 1) { - if ($cache = cache_get('locale:'. $langcode, 'cache')) { - $locale_t[$langcode] = $cache->data; - } - elseif (lock_acquire('locale_cache_' . $langcode)) { - // Refresh database stored cache of translations for given language. - // We only store short strings used in current version, to improve - // performance and consume less memory. - $result = db_query("SELECT s.source, t.translation, t.language FROM {locales_source} s LEFT JOIN {locales_target} t ON s.lid = t.lid AND t.language = '%s' WHERE s.textgroup = 'default' AND s.version = '%s' AND LENGTH(s.source) < 75", $langcode, VERSION); - while ($data = db_fetch_object($result)) { - $locale_t[$langcode][$data->source] = (empty($data->translation) ? TRUE : $data->translation); - } - cache_set('locale:'. $langcode, $locale_t[$langcode]); - lock_release('locale_cache_' . $langcode); - } - } - } + // Strings are cached by langcode and user roles, using instances of the + // LocaleLookup class to handle string lookup and caching. + if (!isset($locale_t[$langcode]) && isset($language)) { + $locale_t[$langcode] = new LocaleLookup($langcode); + } - // If we have the translation cached, skip checking the database - if (!isset($locale_t[$langcode][$string])) { + return ($locale_t[$langcode][$string] === TRUE ? $string : $locale_t[$langcode][$string]); +} - // We do not have this translation cached, so get it from the DB. - $translation = db_fetch_object(db_query("SELECT s.lid, t.translation, s.version FROM {locales_source} s LEFT JOIN {locales_target} t ON s.lid = t.lid AND t.language = '%s' WHERE s.source = '%s' AND s.textgroup = 'default'", $langcode, $string)); - if ($translation) { - // We have the source string at least. - // Cache translation string or TRUE if no translation exists. - $locale_t[$langcode][$string] = (empty($translation->translation) ? TRUE : $translation->translation); +/** + * Extends CacheArray to allow for dynamic building of the locale cache. + */ +class LocaleLookup extends DrupalCacheArray { + + /** + * A language code. + * @var string + */ + protected $langcode; + + /** + * Constructs a LocaleCache object. + */ + public function __construct($langcode) { + $this->langcode = $langcode; + + // Add the current user's role IDs to the cache key, this ensures that, for + // example, strings for admin menu items and settings forms are not cached + // for anonymous users. + $rids = implode(':', array_keys($GLOBALS['user']->roles)); + parent::__construct("locale:$langcode:$rids", 'cache'); + } + /** + * Overrides DrupalCacheArray::resolveCacheMiss(). + */ + protected function resolveCacheMiss($offset) { + $translation = db_fetch_object(db_query("SELECT s.lid, t.translation, s.version FROM {locales_source} s LEFT JOIN {locales_target} t ON s.lid = t.lid AND t.language = '%s' WHERE s.source = '%s' AND s.textgroup = 'default'", $this->langcode, $offset)); + if ($translation) { if ($translation->version != VERSION) { - // This is the first use of this string under current Drupal version. Save version - // and clear cache, to include the string into caching next time. Saved version is - // also a string-history information for later pruning of the tables. + // This is the first use of this string under current Drupal version. + // Update the {locales_source} table to indicate the string is current. db_query("UPDATE {locales_source} SET version = '%s' WHERE lid = %d", VERSION, $translation->lid); - cache_clear_all('locale:', 'cache', TRUE); } + $value = !empty($translation->translation) ? $translation->translation : TRUE; } else { - // We don't have the source string, cache this as untranslated. + // We don't have the source string, update the {locales_source} table to + // indicate the string is not translated. db_query("INSERT INTO {locales_source} (location, source, textgroup, version) VALUES ('%s', '%s', 'default', '%s')", request_uri(), $string, VERSION); - $locale_t[$langcode][$string] = TRUE; - // Clear locale cache so this string can be added in a later request. - cache_clear_all('locale:', 'cache', TRUE); + $value = TRUE; } + $this->storage[$offset] = $value; + // Disabling the usage of string caching allows a module to watch for + // the exact list of strings used on a page. From a performance + // perspective that is a really bad idea, so we have no user + // interface for this. Be careful when turning this option off! + if (variable_get('locale_cache_strings', 1)) { + $this->persist($offset); + } + return $value; } - - return ($locale_t[$langcode][$string] === TRUE ? $string : $locale_t[$langcode][$string]); } /** @@ -479,7 +488,7 @@ function locale_language_list($field = 'name', $all = FALSE) { * translations for. */ function locale_system_update($components) { - include_once 'includes/locale.inc'; + include_once DRUPAL_ROOT . '/includes/locale.inc'; if ($batch = locale_batch_by_component($components)) { batch_set($batch); } diff --git a/modules/menu/menu.admin.inc b/modules/menu/menu.admin.inc index a62f246e196..84b1b1da21f 100644 --- a/modules/menu/menu.admin.inc +++ b/modules/menu/menu.admin.inc @@ -475,7 +475,7 @@ function menu_delete_menu_confirm_submit($form, &$form_state) { $menu = $form['#menu']; $form_state['redirect'] = 'admin/build/menu'; // System-defined menus may not be deleted - only menus defined by this module. - if (in_array($menu['menu_name'], menu_list_system_menus()) || !db_result(db_query("SELECT COUNT(*) FROM {menu_custom} WHERE menu_name = '%s'", $menu['menu_name']))) { + if (in_array($menu['menu_name'], menu_list_system_menus()) || !db_result(db_query_range("SELECT 1 FROM {menu_custom} WHERE menu_name = '%s'", $menu['menu_name'], 0, 1))) { return; } // Reset all the menu links defined by the system via hook_menu. diff --git a/modules/node/node.admin.inc b/modules/node/node.admin.inc index 41f473032ce..74e0607f83d 100644 --- a/modules/node/node.admin.inc +++ b/modules/node/node.admin.inc @@ -455,7 +455,7 @@ function node_admin_nodes() { $result = pager_query(db_rewrite_sql('SELECT n.*, u.name FROM {node} n '. $filter['join'] .' INNER JOIN {users} u ON n.uid = u.uid '. $filter['where'] .' ORDER BY n.changed DESC'), 50, 0, NULL, $filter['args']); // Enable language column if locale is enabled or if we have any node with language - $count = db_result(db_query("SELECT COUNT(*) FROM {node} n WHERE language != ''")); + $count = db_result(db_query_range("SELECT 1 FROM {node} n WHERE language != ''", 0, 1)); $multilanguage = (module_exists('locale') || $count); $form['options'] = array( diff --git a/modules/node/node.module b/modules/node/node.module index d0ca2142fa4..8e53e1b7a4f 100644 --- a/modules/node/node.module +++ b/modules/node/node.module @@ -490,7 +490,7 @@ function node_types_rebuild() { function node_type_save($info) { $is_existing = FALSE; $existing_type = !empty($info->old_type) ? $info->old_type : $info->type; - $is_existing = db_result(db_query("SELECT COUNT(*) FROM {node_type} WHERE type = '%s'", $existing_type)); + $is_existing = db_result(db_query_range("SELECT 1 FROM {node_type} WHERE type = '%s'", $existing_type, 0, 1)); if (!isset($info->help)) { $info->help = ''; } @@ -2021,30 +2021,47 @@ function node_search_validate($form, &$form_state) { */ function node_access($op, $node, $account = NULL) { global $user; + static $rights; if (!$node || !in_array($op, array('view', 'update', 'delete', 'create'), TRUE)) { // If there was no node to check against, or the $op was not one of the // supported ones, we return access denied. return FALSE; } + // If no user object is supplied, the access check is for the current user. + if (empty($account)) { + $account = $user; + } + + // Cache ID contains user account, op, and NID or node type if op is create. + $cid = $account->uid . $op; // Convert the node to an object if necessary: if ($op != 'create') { $node = (object)$node; + $cid .= $node->nid; } - // If no user object is supplied, the access check is for the current user. - if (empty($account)) { - $account = $user; + // Checking create condition; $node is a string referencing a node type. + else { + $cid .= is_object($node) ? $node->type : $node; + } + + // Return cached value if it exists. + if (isset($rights[$cid])) { + return $rights[$cid]; } // If the node is in a restricted format, disallow editing. if ($op == 'update' && !filter_access($node->format)) { + $rights[$cid] = FALSE; return FALSE; } if (user_access('administer nodes', $account)) { + $rights[$cid] = TRUE; return TRUE; } if (!user_access('access content', $account)) { + $rights[$cid] = FALSE; return FALSE; } @@ -2056,6 +2073,7 @@ function node_access($op, $node, $account = NULL) { } $access = module_invoke($module, 'access', $op, $node, $account); if (!is_null($access)) { + $rights[$cid] = $access; return $access; } @@ -2076,11 +2094,13 @@ function node_access($op, $node, $account = NULL) { $sql = "SELECT 1 FROM {node_access} WHERE (nid = 0 OR nid = %d) $grants_sql AND grant_$op >= 1"; $result = db_query_range($sql, $node->nid, 0, 1); - return (bool) db_result($result); + $rights[$cid] = (bool) db_result($result); + return $rights[$cid]; } // Let authors view their own nodes. if ($op == 'view' && $account->uid == $node->uid && $account->uid != 0) { + $rights[$cid] = TRUE; return TRUE; } @@ -2186,9 +2206,7 @@ function node_access_view_all_nodes() { $grants_sql = 'AND ('. implode(' OR ', $grants) .')'; } - $sql = "SELECT COUNT(*) FROM {node_access} WHERE nid = 0 $grants_sql AND grant_view >= 1"; - $result = db_query($sql); - $access = db_result($result); + $access = db_result(db_query_range("SELECT 1 FROM {node_access} WHERE nid = 0 $grants_sql AND grant_view >= 1", 0, 1)); } return $access; @@ -2746,7 +2764,7 @@ function node_assign_owner_action_form($context) { } function node_assign_owner_action_validate($form, $form_state) { - $count = db_result(db_query("SELECT COUNT(*) FROM {users} WHERE name = '%s'", $form_state['values']['owner_name'])); + $count = db_result(db_query_range("SELECT 1 FROM {users} WHERE name = '%s'", $form_state['values']['owner_name'], 0, 1)); if (intval($count) != 1) { form_set_error('owner_name', t('Please enter a valid username.')); } diff --git a/modules/path/path.admin.inc b/modules/path/path.admin.inc index dba0d030037..9c3f7b87936 100644 --- a/modules/path/path.admin.inc +++ b/modules/path/path.admin.inc @@ -14,7 +14,7 @@ function path_admin_overview($keys = NULL) { // Add the filter form above the overview table. $output = drupal_get_form('path_admin_filter_form', $keys); // Enable language column if locale is enabled or if we have any alias with language - $count = db_result(db_query("SELECT COUNT(*) FROM {url_alias} WHERE language != ''")); + $count = db_result(db_query_range("SELECT 1 FROM {url_alias} WHERE language != ''", 0, 1)); $multilanguage = (module_exists('locale') || $count); if ($keys) { @@ -134,7 +134,7 @@ function path_admin_form_validate($form, &$form_state) { // Language is only set if locale module is enabled, otherwise save for all languages. $language = isset($form_state['values']['language']) ? $form_state['values']['language'] : ''; - if (db_result(db_query("SELECT COUNT(dst) FROM {url_alias} WHERE pid != %d AND dst = '%s' AND language = '%s'", $pid, $dst, $language))) { + if (db_result(db_query_range("SELECT 1 FROM {url_alias} WHERE pid != %d AND dst = '%s' AND language = '%s'", $pid, $dst, $language, 0, 1))) { form_set_error('dst', t('The alias %alias is already in use in this language.', array('%alias' => $dst))); } $item = menu_get_item($src); diff --git a/modules/path/path.module b/modules/path/path.module index 953feb25337..5790c12c138 100644 --- a/modules/path/path.module +++ b/modules/path/path.module @@ -180,7 +180,7 @@ function path_nodeapi(&$node, $op, $arg = NULL) { case 'validate': if (isset($node->path)) { $node->path = trim($node->path); - if (db_result(db_query("SELECT COUNT(dst) FROM {url_alias} WHERE dst = '%s' AND src != '%s' AND language = '%s'", $node->path, "node/$node->nid", $language))) { + if (db_result(db_query_range("SELECT 1 FROM {url_alias} WHERE dst = '%s' AND src != '%s' AND language = '%s'", $node->path, "node/$node->nid", $language, 0, 1))) { form_set_error('path', t('The path is already in use.')); } } diff --git a/modules/ping/ping.module b/modules/ping/ping.module index 6553960d204..12b137028ed 100644 --- a/modules/ping/ping.module +++ b/modules/ping/ping.module @@ -29,7 +29,7 @@ function ping_cron() { if (variable_get('site_name', 0)) { $cron_last = variable_get('cron_last', time()); // Query changed first since usually changed >= created. - if (db_result(db_query('SELECT COUNT(*) FROM {node} WHERE status = 1 AND changed > %d', $cron_last)) || db_result(db_query('SELECT COUNT(*) FROM {node} WHERE status = 1 AND created > %d', $cron_last))) { + if (db_result(db_query_range('SELECT 1 FROM {node} WHERE status = 1 AND changed > %d', $cron_last, 0, 1)) || db_result(db_query_range('SELECT 1 FROM {node} WHERE status = 1 AND created > %d', $cron_last, 0, 1))) { _ping_notify(variable_get('site_name', ''), $base_url); } } diff --git a/modules/poll/poll.module b/modules/poll/poll.module index 7bc5587d399..edf0246165b 100644 --- a/modules/poll/poll.module +++ b/modules/poll/poll.module @@ -320,7 +320,7 @@ function _poll_choice_form($delta, $value = '', $votes = 0) { * Menu callback for AHAH additions. */ function poll_choice_js() { - include_once 'modules/node/node.pages.inc'; + include_once DRUPAL_ROOT . '/modules/node/node.pages.inc'; $form_state = array('storage' => NULL, 'submitted' => FALSE); $form_build_id = $_POST['form_build_id']; // Get the form from the cache. diff --git a/modules/profile/profile.module b/modules/profile/profile.module index e8c053904b5..61d4b303bd3 100644 --- a/modules/profile/profile.module +++ b/modules/profile/profile.module @@ -470,7 +470,7 @@ function profile_category_access($account, $category) { return TRUE; } else { - return user_edit_access($account) && db_result(db_query("SELECT COUNT(*) FROM {profile_fields} WHERE category = '%s' AND visibility <> %d", $category, PROFILE_HIDDEN)); + return user_edit_access($account) && db_result(db_query_range("SELECT 1 FROM {profile_fields} WHERE category = '%s' AND visibility <> %d", $category, PROFILE_HIDDEN, 0, 1)); } } diff --git a/modules/profile/profile.pages.inc b/modules/profile/profile.pages.inc index da1b8c5834d..b3c23db3a30 100644 --- a/modules/profile/profile.pages.inc +++ b/modules/profile/profile.pages.inc @@ -108,7 +108,7 @@ function profile_browse() { */ function profile_autocomplete($field, $string) { $matches = array(); - if (db_result(db_query("SELECT COUNT(*) FROM {profile_fields} WHERE fid = %d AND autocomplete = 1", $field))) { + if (db_result(db_query_range("SELECT 1 FROM {profile_fields} WHERE fid = %d AND autocomplete = 1", $field, 0, 1))) { $result = db_query_range("SELECT value FROM {profile_values} WHERE fid = %d AND value LIKE '%s%%' GROUP BY value ORDER BY value ASC", $field, $string, 0, 10); while ($data = db_fetch_object($result)) { $matches[$data->value] = check_plain($data->value); diff --git a/modules/simpletest/drupal_web_test_case.php b/modules/simpletest/drupal_web_test_case.php index 6789a8d3e7d..aadf73220b6 100644 --- a/modules/simpletest/drupal_web_test_case.php +++ b/modules/simpletest/drupal_web_test_case.php @@ -1109,8 +1109,7 @@ protected function setUp() { ini_set('log_errors', 1); ini_set('error_log', $directory . '/error.log'); -// include_once DRUPAL_ROOT . '/includes/install.inc'; - include_once './includes/install.inc'; + include_once DRUPAL_ROOT . '/includes/install.inc'; drupal_install_system(); // $this->preloadRegistry(); diff --git a/modules/simpletest/run-tests.sh b/modules/simpletest/run-tests.sh old mode 100755 new mode 100644 diff --git a/modules/system/system.admin.inc b/modules/system/system.admin.inc index 3086139bdf7..44ea1b87fe1 100644 --- a/modules/system/system.admin.inc +++ b/modules/system/system.admin.inc @@ -525,7 +525,7 @@ function system_theme_settings(&$form_state, $key = '') { // Process the theme and all its base themes. foreach ($theme_keys as $theme) { // Include the theme-settings.php file. - $filename = './'. str_replace("/$theme.info", '', $themes[$theme]->filename) .'/theme-settings.php'; + $filename = DRUPAL_ROOT . '/'. str_replace("/$theme.info", '', $themes[$theme]->filename) .'/theme-settings.php'; if (file_exists($filename)) { require_once $filename; } @@ -875,7 +875,7 @@ function system_modules_confirm_form($modules, $storage) { * Submit callback; handles modules form submission. */ function system_modules_submit($form, &$form_state) { - include_once './includes/install.inc'; + include_once DRUPAL_ROOT . '/includes/install.inc'; $new_modules = array(); // If we are coming from the confirm form... @@ -1027,7 +1027,7 @@ function system_module_build_dependencies($modules, $form_values) { */ function system_modules_uninstall($form_state = NULL) { // Make sure the install API is available. - include_once './includes/install.inc'; + include_once DRUPAL_ROOT . '/includes/install.inc'; // Display the confirm form if any modules have been submitted. if (isset($form_state) && $confirm_form = system_modules_uninstall_confirm_form($form_state['storage'])) { @@ -1128,7 +1128,7 @@ function system_modules_uninstall_validate($form, &$form_state) { */ function system_modules_uninstall_submit($form, &$form_state) { // Make sure the install API is available. - include_once './includes/install.inc'; + include_once DRUPAL_ROOT . '/includes/install.inc'; if (!empty($form['#confirmed'])) { // Call the uninstall routine for each selected module. @@ -1729,7 +1729,7 @@ function system_clean_url_settings() { */ function system_status($check = FALSE) { // Load .install files - include_once './includes/install.inc'; + include_once DRUPAL_ROOT . '/includes/install.inc'; drupal_load_updates(); // Check run-time requirements and status information. @@ -1837,7 +1837,7 @@ function system_sql() { * Default page callback for batches. */ function system_batch_page() { - require_once './includes/batch.inc'; + require_once DRUPAL_ROOT . '/includes/batch.inc'; $output = _batch_page(); if ($output === FALSE) { drupal_access_denied(); diff --git a/modules/system/system.install b/modules/system/system.install index 4dc6a2b6113..d0e84fb4436 100644 --- a/modules/system/system.install +++ b/modules/system/system.install @@ -274,7 +274,7 @@ function system_requirements($phase) { } // Test Unicode library - include_once './includes/unicode.inc'; + include_once DRUPAL_ROOT . '/includes/unicode.inc'; $requirements = array_merge($requirements, unicode_requirements()); if ($phase == 'runtime') { diff --git a/modules/system/system.module b/modules/system/system.module index 1df0a9160c5..9fa852eac75 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -654,7 +654,7 @@ function system_admin_menu_block($item) { function system_admin_theme_submit($form, &$form_state) { // If we're changing themes, make sure the theme has its blocks initialized. if ($form_state['values']['admin_theme'] && $form_state['values']['admin_theme'] != variable_get('admin_theme', '0')) { - $result = db_result(db_query("SELECT COUNT(*) FROM {blocks} WHERE theme = '%s'", $form_state['values']['admin_theme'])); + $result = db_result(db_query_range("SELECT 1 FROM {blocks} WHERE theme = '%s'", $form_state['values']['admin_theme'], 0, 1)); if (!$result) { system_initialize_theme_blocks($form_state['values']['admin_theme']); } @@ -702,7 +702,7 @@ function system_theme_select_form($description = '', $default_value = '', $weigh $screenshot = NULL; $theme_key = $info->name; while ($theme_key) { - if (file_exists($themes[$theme_key]->info['screenshot'])) { + if (file_exists(DRUPAL_ROOT . '/' . $themes[$theme_key]->info['screenshot'])) { $screenshot = $themes[$theme_key]->info['screenshot']; break; } @@ -885,7 +885,7 @@ function _system_theme_data() { } if (empty($themes[$key]->info['engine'])) { $filename = dirname($themes[$key]->filename) .'/'. $themes[$key]->name .'.theme'; - if (file_exists($filename)) { + if (file_exists(DRUPAL_ROOT . '/' . $filename)) { $themes[$key]->owner = $filename; $themes[$key]->prefix = $key; } @@ -1044,9 +1044,16 @@ function system_find_base_theme($themes, $key, $used_keys = array()) { */ function system_region_list($theme_key) { static $list = array(); + global $theme, $theme_info; if (!array_key_exists($theme_key, $list)) { - $info = unserialize(db_result(db_query("SELECT info FROM {system} WHERE type = 'theme' AND name = '%s'", $theme_key))); + if ($theme == $theme_key) { + $info = $theme_info->info; + } + else { + $info = unserialize(db_result(db_query("SELECT info FROM {system} WHERE type = 'theme' AND name = '%s'", $theme_key))); + } + $list[$theme_key] = array_map('t', $info['regions']); } @@ -1079,7 +1086,7 @@ function system_default_region($theme) { */ function system_initialize_theme_blocks($theme) { // Initialize theme's blocks if none already registered. - if (!(db_result(db_query("SELECT COUNT(*) FROM {blocks} WHERE theme = '%s'", $theme)))) { + if (!(db_result(db_query_range("SELECT 1 FROM {blocks} WHERE theme = '%s'", $theme, 0, 1)))) { $default_theme = variable_get('theme_default', 'garland'); $regions = system_region_list($theme); $result = db_query("SELECT * FROM {blocks} WHERE theme = '%s'", $default_theme); diff --git a/modules/taxonomy/taxonomy.module b/modules/taxonomy/taxonomy.module index 38b74e19f35..b34b57601a2 100644 --- a/modules/taxonomy/taxonomy.module +++ b/modules/taxonomy/taxonomy.module @@ -200,6 +200,19 @@ function taxonomy_save_vocabulary(&$edit) { $edit['module'] = 'taxonomy'; } + $return = module_invoke_all('taxonomy_vocabulary_presave', $edit); + if (is_array($return) && !empty($return)) { + $update = TRUE; + foreach ($return as $key => $value) { + if (is_numeric($key) && $value == FALSE) { + $update = FALSE; + } + } + if ($update) { + $edit = $return; + } + } + if (!empty($edit['vid']) && !empty($edit['name'])) { drupal_write_record('vocabulary', $edit, 'vid'); db_query("DELETE FROM {vocabulary_node_types} WHERE vid = %d", $edit['vid']); @@ -309,6 +322,19 @@ function taxonomy_save_term(&$form_values) { 'weight' => 0 ); + $return = module_invoke_all('taxonomy_term_presave', $form_values); + if (is_array($return) && !empty($return)) { + $update = TRUE; + foreach ($return as $key => $value) { + if (is_numeric($key) && $value == FALSE) { + $update = FALSE; + } + } + if ($update) { + $form_values = $return; + } + } + if (!empty($form_values['tid']) && $form_values['name']) { drupal_write_record('term_data', $form_values, 'tid'); $hook = 'update'; @@ -471,10 +497,19 @@ function taxonomy_form_all($free_tags = 0) { * If set, return only those vocabularies associated with this node type. */ function taxonomy_get_vocabularies($type = NULL) { + static $vocabularies_cache = array(); + static $vocabularies_null; + if ($type) { + if (isset($vocabularies_cache[$type])) { + return $vocabularies_cache[$type]; + } $result = db_query(db_rewrite_sql("SELECT v.vid, v.*, n.type FROM {vocabulary} v LEFT JOIN {vocabulary_node_types} n ON v.vid = n.vid WHERE n.type = '%s' ORDER BY v.weight, v.name", 'v', 'vid'), $type); } else { + if (is_array($vocabularies_null)) { + return $vocabularies_null; + } $result = db_query(db_rewrite_sql('SELECT v.*, n.type FROM {vocabulary} v LEFT JOIN {vocabulary_node_types} n ON v.vid = n.vid ORDER BY v.weight, v.name', 'v', 'vid')); } @@ -494,7 +529,15 @@ function taxonomy_get_vocabularies($type = NULL) { $vocabularies[$voc->vid] = $voc; } + if ($type) { + $vocabularies_cache[$type] = $vocabularies; + } + else { + $vocabularies_null = $vocabularies; + } + return $vocabularies; + } /** @@ -1049,20 +1092,7 @@ function taxonomy_vocabulary_load($vid, $reset = FALSE) { } if (!isset($vocabularies[$vid])) { - // Initialize so if this vocabulary does not exist, we have - // that cached, and we will not try to load this later. - $vocabularies[$vid] = FALSE; - // Try to load the data and fill up the object. - $result = db_query('SELECT v.*, n.type FROM {vocabulary} v LEFT JOIN {vocabulary_node_types} n ON v.vid = n.vid WHERE v.vid = %d', $vid); - $node_types = array(); - while ($voc = db_fetch_object($result)) { - if (!empty($voc->type)) { - $node_types[$voc->type] = $voc->type; - } - unset($voc->type); - $voc->nodes = $node_types; - $vocabularies[$vid] = $voc; - } + $vocabularies = taxonomy_get_vocabularies(); } // Return FALSE if this vocabulary does not exist. diff --git a/modules/trigger/trigger.module b/modules/trigger/trigger.module index 263a2a125ba..70762799a25 100644 --- a/modules/trigger/trigger.module +++ b/modules/trigger/trigger.module @@ -142,12 +142,16 @@ function trigger_access_check($module) { * An array of action IDs. */ function _trigger_get_hook_aids($hook, $op = '') { - $aids = array(); - $result = db_query("SELECT aa.aid, a.type FROM {trigger_assignments} aa LEFT JOIN {actions} a ON aa.aid = a.aid WHERE aa.hook = '%s' AND aa.op = '%s' ORDER BY weight", $hook, $op); - while ($action = db_fetch_object($result)) { - $aids[$action->aid]['type'] = $action->type; + static $hooks; + if (!isset($hooks[$hook][$op])) { + $aids = array(); + $result = db_query("SELECT aa.aid, a.type FROM {trigger_assignments} aa LEFT JOIN {actions} a ON aa.aid = a.aid WHERE aa.hook = '%s' AND aa.op = '%s' ORDER BY weight", $hook, $op); + while ($action = db_fetch_object($result)) { + $aids[$action->aid]['type'] = $action->type; + } + $hooks[$hook][$op] = $aids; } - return $aids; + return $hooks[$hook][$op]; } /** diff --git a/modules/update/update.fetch.inc b/modules/update/update.fetch.inc index d1e381277ac..e25a504c4cd 100644 --- a/modules/update/update.fetch.inc +++ b/modules/update/update.fetch.inc @@ -148,7 +148,7 @@ function _update_get_fetch_url_base($project) { * @see update_requirements() */ function _update_cron_notify() { - include_once './includes/install.inc'; + include_once DRUPAL_ROOT . '/includes/install.inc'; $status = update_requirements('runtime'); $params = array(); $notify_all = (variable_get('update_notification_threshold', 'all') == 'all'); diff --git a/modules/update/update.module b/modules/update/update.module index fd45ed5df79..b4129a5adaf 100644 --- a/modules/update/update.module +++ b/modules/update/update.module @@ -71,7 +71,7 @@ function update_help($path, $arg) { return $output; case 'admin/build/themes': case 'admin/build/modules': - include_once './includes/install.inc'; + include_once DRUPAL_ROOT . '/includes/install.inc'; $status = update_requirements('runtime'); foreach (array('core', 'contrib') as $report_type) { $type = 'update_'. $report_type; @@ -103,7 +103,7 @@ function update_help($path, $arg) { // update missing, print an error message about it. if (arg(0) == 'admin' && strpos($path, '#') === FALSE && user_access('administer site configuration')) { - include_once './includes/install.inc'; + include_once DRUPAL_ROOT . '/includes/install.inc'; $status = update_requirements('runtime'); foreach (array('core', 'contrib') as $report_type) { $type = 'update_'. $report_type; diff --git a/modules/user/user.admin.inc b/modules/user/user.admin.inc index c84fdbcf66f..6ad978487bf 100644 --- a/modules/user/user.admin.inc +++ b/modules/user/user.admin.inc @@ -683,12 +683,12 @@ function user_admin_role() { function user_admin_role_validate($form, &$form_state) { if ($form_state['values']['name']) { if ($form_state['values']['op'] == t('Save role')) { - if (db_result(db_query("SELECT COUNT(*) FROM {role} WHERE name = '%s' AND rid != %d", $form_state['values']['name'], $form_state['values']['rid']))) { + if (db_result(db_query_range("SELECT 1 FROM {role} WHERE name = '%s' AND rid != %d", $form_state['values']['name'], $form_state['values']['rid'], 0, 1))) { form_set_error('name', t('The role name %name already exists. Please choose another role name.', array('%name' => $form_state['values']['name']))); } } else if ($form_state['values']['op'] == t('Add role')) { - if (db_result(db_query("SELECT COUNT(*) FROM {role} WHERE name = '%s'", $form_state['values']['name']))) { + if (db_result(db_query_range("SELECT 1 FROM {role} WHERE name = '%s'", $form_state['values']['name'], 0, 1))) { form_set_error('name', t('The role name %name already exists. Please choose another role name.', array('%name' => $form_state['values']['name']))); } } diff --git a/modules/user/user.module b/modules/user/user.module index 4e6364722f8..cafc94b3252 100644 --- a/modules/user/user.module +++ b/modules/user/user.module @@ -1616,7 +1616,7 @@ function _user_edit_validate($uid, &$edit) { if ($error = user_validate_name($edit['name'])) { form_set_error('name', $error); } - else if (db_result(db_query("SELECT COUNT(*) FROM {users} WHERE uid != %d AND name = '%s'", $uid, $edit['name'])) > 0) { + else if (db_result(db_query_range("SELECT 1 FROM {users} WHERE uid != %d AND name = '%s'", $uid, $edit['name'], 0, 1)) > 0) { form_set_error('name', t('The name %name is already taken.', array('%name' => $edit['name']))); } else if (drupal_is_denied('user', $edit['name'])) { @@ -1628,7 +1628,7 @@ function _user_edit_validate($uid, &$edit) { if ($error = user_validate_mail($edit['mail'])) { form_set_error('mail', $error); } - else if (db_result(db_query("SELECT COUNT(*) FROM {users} WHERE uid != %d AND mail = '%s'", $uid, $edit['mail'])) > 0) { + else if (db_result(db_query_range("SELECT 1 FROM {users} WHERE uid != %d AND mail = '%s'", $uid, $edit['mail'], 0, 1)) > 0) { form_set_error('mail', t('The e-mail address %email is already registered. Have you forgotten your password?', array('%email' => $edit['mail'], '@password' => url('user/password')))); } else if (drupal_is_denied('mail', $edit['mail'])) { diff --git a/themes/engines/phptemplate/phptemplate.engine b/themes/engines/phptemplate/phptemplate.engine index 214e1a41e5f..f9b36490ada 100644 --- a/themes/engines/phptemplate/phptemplate.engine +++ b/themes/engines/phptemplate/phptemplate.engine @@ -11,7 +11,7 @@ function phptemplate_init($template) { $file = dirname($template->filename) .'/template.php'; if (file_exists($file)) { - include_once "./$file"; + include_once DRUPAL_ROOT . "/$file"; } } diff --git a/update.php b/update.php index e32547369da..e4ef7cdff31 100644 --- a/update.php +++ b/update.php @@ -12,6 +12,11 @@ * be sure to open settings.php again, and change it back to its original state! */ +/** + * Root directory of Drupal installation. + */ +define('DRUPAL_ROOT', getcwd()); + /** * Global flag to identify update.php run, and so avoid various unwanted * operations, such as hook_init() and hook_exit() invokes, css/js preprocessing @@ -590,24 +595,26 @@ function update_check_requirements() { // Our custom error handler is not yet installed, so we just suppress them. ini_set('display_errors', FALSE); -require_once './includes/bootstrap.inc'; +require_once DRUPAL_ROOT . '/includes/bootstrap.inc'; // We only load DRUPAL_BOOTSTRAP_CONFIGURATION for the update requirements // check to avoid reaching the PHP memory limit. $op = isset($_REQUEST['op']) ? $_REQUEST['op'] : ''; if (empty($op)) { // Minimum load of components. - drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION); + drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE); - require_once './includes/install.inc'; - require_once './includes/file.inc'; - require_once './modules/system/system.install'; + require_once DRUPAL_ROOT . '/includes/install.inc'; + require_once DRUPAL_ROOT . '/includes/file.inc'; + require_once DRUPAL_ROOT . '/modules/system/system.install'; // Load module basics. - include_once './includes/module.inc'; + include_once DRUPAL_ROOT . '/includes/module.inc'; $module_list['system']['filename'] = 'modules/system/system.module'; $module_list['filter']['filename'] = 'modules/filter/filter.module'; module_list(TRUE, FALSE, FALSE, $module_list); + module_implements('', FALSE, TRUE); + drupal_load('module', 'system'); drupal_load('module', 'filter'); @@ -646,8 +653,8 @@ function update_check_requirements() { if (!empty($update_free_access) || $user->uid == 1) { drupal_session_start(); - include_once './includes/install.inc'; - include_once './includes/batch.inc'; + include_once DRUPAL_ROOT . '/includes/install.inc'; + include_once DRUPAL_ROOT . '/includes/batch.inc'; drupal_load_updates(); update_fix_d6_requirements(); diff --git a/xmlrpc.php b/xmlrpc.php index 0f0300b184b..e50f276f8ce 100644 --- a/xmlrpc.php +++ b/xmlrpc.php @@ -1,13 +1,18 @@