Index: includes/cache.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/cache.inc,v retrieving revision 1.6 diff -u -r1.6 cache.inc --- includes/cache.inc 27 Feb 2007 12:04:39 -0000 1.6 +++ includes/cache.inc 8 Apr 2007 04:00:19 -0000 @@ -1,167 +1,167 @@ -data)) { - // If the data is permanent or we're not enforcing a minimum cache lifetime - // always return the cached data. - if ($cache->expire == CACHE_PERMANENT || !variable_get('cache_lifetime', 0)) { - $cache->data = db_decode_blob($cache->data); - } - // If enforcing a minimum cache lifetime, validate that the data is - // currently valid for this user before we return it by making sure the - // cache entry was created before the timestamp in the current session's - // cache timer. The cache variable is loaded into the $user object by - // sess_read() in session.inc. - else { - if ($user->cache > $cache->created) { - // This cache data is too old and thus not valid for us, ignore it. - return 0; - } - else { - $cache->data = db_decode_blob($cache->data); - } - } - return $cache; - } - return 0; -} - -/** - * Store data in the persistent cache. - * - * The persistent cache is split up into four database - * tables. Contributed modules can add additional tables. - * - * 'cache_page': This table stores generated pages for anonymous - * users. This is the only table affected by the page cache setting on - * the administrator panel. - * - * 'cache_menu': Stores the cachable part of the users' menus. - * - * 'cache_filter': Stores filtered pieces of content. This table is - * periodically cleared of stale entries by cron. - * - * 'cache': Generic cache storage table. - * - * The reasons for having several tables are as follows: - * - * - smaller tables allow for faster selects and inserts - * - we try to put fast changing cache items and rather static - * ones into different tables. The effect is that only the fast - * changing tables will need a lot of writes to disk. The more - * static tables will also be better cachable with MySQL's query cache - * - * @param $cid - * The cache ID of the data to store. - * @param $table - * The table $table to store the data in. Valid core values are 'cache_filter', - * 'cache_menu', 'cache_page', or 'cache'. - * @param $data - * The data to store in the cache. Complex data types must be serialized first. - * @param $expire - * One of the following values: - * - CACHE_PERMANENT: Indicates that the item should never be removed unless - * explicitly told to using cache_clear_all() with a cache ID. - * - CACHE_TEMPORARY: Indicates that the item should be removed at the next - * general cache wipe. - * - A Unix timestamp: Indicates that the item should be kept at least until - * the given time, after which it behaves like CACHE_TEMPORARY. - * @param $headers - * A string containing HTTP header information for cached pages. - */ -function cache_set($cid, $table = 'cache', $data, $expire = CACHE_PERMANENT, $headers = NULL) { - db_lock_table($table); - db_query("UPDATE {%s} SET data = %b, created = %d, expire = %d, headers = '%s' WHERE cid = '%s'", $table, $data, time(), $expire, $headers, $cid); - if (!db_affected_rows()) { - @db_query("INSERT INTO {%s} (cid, data, created, expire, headers) VALUES ('%s', %b, %d, %d, '%s')", $table, $cid, $data, time(), $expire, $headers); - } - db_unlock_tables(); -} - -/** - * - * Expire data from the cache. If called without arguments, expirable - * entries will be cleared from the cache_page table. - * - * @param $cid - * If set, the cache ID to delete. Otherwise, all cache entries that can - * expire are deleted. - * - * @param $table - * If set, the table $table to delete from. Mandatory - * argument if $cid is set. - * - * @param $wildcard - * If set to TRUE, the $cid is treated as a substring - * to match rather than a complete ID. The match is a right hand - * match. If '*' is given as $cid, the table $table will be emptied. - */ -function cache_clear_all($cid = NULL, $table = NULL, $wildcard = FALSE) { - global $user; - - if (!isset($cid) && !isset($table)) { - cache_clear_all(NULL, 'cache_page'); - return; - } - - if (empty($cid)) { - if (variable_get('cache_lifetime', 0)) { - // We store the time in the current user's $user->cache variable which - // will be saved into the sessions table by sess_write(). We then - // simulate that the cache was flushed for this user by not returning - // cached data that was cached before the timestamp. - $user->cache = time(); - - $cache_flush = variable_get('cache_flush', 0); - if ($cache_flush == 0) { - // This is the first request to clear the cache, start a timer. - variable_set('cache_flush', time()); - } - else if (time() > ($cache_flush + variable_get('cache_lifetime', 0))) { - // Clear the cache for everyone, cache_flush_delay seconds have - // passed since the first request to clear the cache. - db_query("DELETE FROM {%s} WHERE expire != %d AND expire < %d", $table, CACHE_PERMANENT, time()); - variable_set('cache_flush', 0); - } - } - else { - // No minimum cache lifetime, flush all temporary cache entries now. - db_query("DELETE FROM {%s} WHERE expire != %d AND expire < %d", $table, CACHE_PERMANENT, time()); - } - } - else { - if ($wildcard) { - if ($cid == '*') { - db_query("DELETE FROM {%s}", $table); - } - else { - db_query("DELETE FROM {%s} WHERE cid LIKE '%s%%'", $table, $cid); - } - } - else { - db_query("DELETE FROM {%s} WHERE cid = '%s'", $table, $cid); - } - } -} - +data)) { + // If the data is permanent or we're not enforcing a minimum cache lifetime + // always return the cached data. + if ($cache->expire == CACHE_PERMANENT || !variable_get('cache_lifetime', 0)) { + $cache->data = db_decode_blob($cache->data); + } + // If enforcing a minimum cache lifetime, validate that the data is + // currently valid for this user before we return it by making sure the + // cache entry was created before the timestamp in the current session's + // cache timer. The cache variable is loaded into the $user object by + // sess_read() in session.inc. + else { + if ($user->cache > $cache->created) { + // This cache data is too old and thus not valid for us, ignore it. + return 0; + } + else { + $cache->data = db_decode_blob($cache->data); + } + } + return $cache; + } + return 0; +} + +/** + * Store data in the persistent cache. + * + * The persistent cache is split up into four database + * tables. Contributed modules can add additional tables. + * + * 'cache_page': This table stores generated pages for anonymous + * users. This is the only table affected by the page cache setting on + * the administrator panel. + * + * 'cache_menu': Stores the cachable part of the users' menus. + * + * 'cache_filter': Stores filtered pieces of content. This table is + * periodically cleared of stale entries by cron. + * + * 'cache': Generic cache storage table. + * + * The reasons for having several tables are as follows: + * + * - smaller tables allow for faster selects and inserts + * - we try to put fast changing cache items and rather static + * ones into different tables. The effect is that only the fast + * changing tables will need a lot of writes to disk. The more + * static tables will also be better cachable with MySQL's query cache + * + * @param $cid + * The cache ID of the data to store. + * @param $table + * The table $table to store the data in. Valid core values are 'cache_filter', + * 'cache_menu', 'cache_page', or 'cache'. + * @param $data + * The data to store in the cache. Complex data types must be serialized first. + * @param $expire + * One of the following values: + * - CACHE_PERMANENT: Indicates that the item should never be removed unless + * explicitly told to using cache_clear_all() with a cache ID. + * - CACHE_TEMPORARY: Indicates that the item should be removed at the next + * general cache wipe. + * - A Unix timestamp: Indicates that the item should be kept at least until + * the given time, after which it behaves like CACHE_TEMPORARY. + * @param $headers + * A string containing HTTP header information for cached pages. + */ +function cache_set($cid, $table = 'cache', $data, $expire = CACHE_PERMANENT, $headers = NULL) { + db_lock_table($table); + $result = db_query("UPDATE " . $table . " SET data = %b, created = %d, expire = %d, headers = '%s' WHERE cid = '%s'", $data, time(), $expire, $headers, $cid); + if (!db_affected_rows($result)) { + @db_query("INSERT INTO " . $table . " (cid, data, created, expire, headers) VALUES ('%s', %b, %d, %d, '%s')", $cid, $data, time(), $expire, $headers); + } + db_unlock_tables(); +} + +/** + * + * Expire data from the cache. If called without arguments, expirable + * entries will be cleared from the cache_page table. + * + * @param $cid + * If set, the cache ID to delete. Otherwise, all cache entries that can + * expire are deleted. + * + * @param $table + * If set, the table $table to delete from. Mandatory + * argument if $cid is set. + * + * @param $wildcard + * If set to TRUE, the $cid is treated as a substring + * to match rather than a complete ID. The match is a right hand + * match. If '*' is given as $cid, the table $table will be emptied. + */ +function cache_clear_all($cid = NULL, $table = NULL, $wildcard = FALSE) { + global $user; + + if (!isset($cid) && !isset($table)) { + cache_clear_all(NULL, 'cache_page'); + return; + } + + if (empty($cid)) { + if (variable_get('cache_lifetime', 0)) { + // We store the time in the current user's $user->cache variable which + // will be saved into the sessions table by sess_write(). We then + // simulate that the cache was flushed for this user by not returning + // cached data that was cached before the timestamp. + $user->cache = time(); + + $cache_flush = variable_get('cache_flush', 0); + if ($cache_flush == 0) { + // This is the first request to clear the cache, start a timer. + variable_set('cache_flush', time()); + } + else if (time() > ($cache_flush + variable_get('cache_lifetime', 0))) { + // Clear the cache for everyone, cache_flush_delay seconds have + // passed since the first request to clear the cache. + db_query("DELETE FROM {%s} WHERE expire != %d AND expire < %d", $table, CACHE_PERMANENT, time()); + variable_set('cache_flush', 0); + } + } + else { + // No minimum cache lifetime, flush all temporary cache entries now. + db_query("DELETE FROM {%s} WHERE expire != %d AND expire < %d", $table, CACHE_PERMANENT, time()); + } + } + else { + if ($wildcard) { + if ($cid == '*') { + db_query("DELETE FROM {%s}", $table); + } + else { + db_query("DELETE FROM {%s} WHERE cid LIKE '%s%%'", $table, $cid); + } + } + else { + db_query("DELETE FROM {%s} WHERE cid = '%s'", $table, $cid); + } + } +} + Index: includes/database.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/database.inc,v retrieving revision 1.66 diff -u -r1.66 database.inc --- includes/database.inc 6 Apr 2007 04:39:50 -0000 1.66 +++ includes/database.inc 8 Apr 2007 04:00:19 -0000 @@ -1,5 +1,5 @@ $t('MySQL database'), - 'value' => ($phase == 'runtime') ? l($version, 'admin/logs/status/sql') : $version, - ); - - if (version_compare($version, DRUPAL_MINIMUM_MYSQL) < 0) { - $form['mysql']['severity'] = REQUIREMENT_ERROR; - $form['mysql']['description'] = $t('Your MySQL Server is too old. Drupal requires at least MySQL %version.', array('%version' => DRUPAL_MINIMUM_MYSQL)); - } - - return $form; -} - -/** - * Returns the version of the database server currently in use. - * - * @return Database server version - */ -function db_version() { - list($version) = explode('-', mysql_get_server_info()); - return $version; -} - -/** - * Initialize a database connection. - * - * Note that you can change the mysql_connect() call to mysql_pconnect() if you - * want to use persistent connections. This is not recommended on shared hosts, - * and might require additional database/webserver tuning. It can increase - * performance, however, when the overhead to connect to your database is high - * (e.g. your database and web server live on different machines). - */ -function db_connect($url) { - $url = parse_url($url); - - // Check if MySQL support is present in PHP - if (!function_exists('mysql_connect')) { - // Redirect to installer if using default DB credentials - if ($url['user'] == 'username' && $url['pass'] == 'password') { - include_once 'includes/install.inc'; - install_goto('install.php'); - } - drupal_maintenance_theme(); - drupal_set_title('PHP MySQL support not enabled'); - print theme('maintenance_page', '

We were unable to use the MySQL database because the MySQL extension for PHP is not installed. Check your PHP.ini to see how you can enable it.

-

For more help, see the Installation and upgrading handbook. If you are unsure what these terms mean you should probably contact your hosting provider.

'); - exit; - } - - // Decode url-encoded information in the db connection string - $url['user'] = urldecode($url['user']); - // Test if database url has a password. - if(isset($url['pass'])) { - $url['pass'] = urldecode($url['pass']); - } - else { - $url['pass'] = ''; - } - $url['host'] = urldecode($url['host']); - $url['path'] = urldecode($url['path']); - - // Allow for non-standard MySQL port. - if (isset($url['port'])) { - $url['host'] = $url['host'] .':'. $url['port']; - } - - // - TRUE makes mysql_connect() always open a new link, even if - // mysql_connect() was called before with the same parameters. - // This is important if you are using two databases on the same - // server. - // - 2 means CLIENT_FOUND_ROWS: return the number of found - // (matched) rows, not the number of affected rows. - $connection = @mysql_connect($url['host'], $url['user'], $url['pass'], TRUE, 2); - if (!$connection) { - // Redirect to installer if using default DB credentials - if ($url['user'] == 'username' && $url['pass'] == 'password') { - include_once 'includes/install.inc'; - install_goto('install.php'); - } - - // Show error screen otherwise - drupal_maintenance_theme(); - drupal_set_header('HTTP/1.1 503 Service Unavailable'); - drupal_set_title('Unable to connect to database server'); - print theme('maintenance_page', '

If you still have to install Drupal, proceed to the installation page.

-

If you have already finished installed Drupal, this either means that the username and password information in your settings.php file is incorrect or that we can\'t connect to the MySQL database server. This could mean your hosting provider\'s database server is down.

-

The MySQL error was: '. theme('placeholder', mysql_error()) .'.

-

Currently, the username is '. theme('placeholder', $url['user']) .' and the database server is '. theme('placeholder', $url['host']) .'.

- -

For more help, see the Installation and upgrading handbook. If you are unsure what these terms mean you should probably contact your hosting provider.

'); - exit; - } - - if (!mysql_select_db(substr($url['path'], 1))) { - drupal_maintenance_theme(); - drupal_set_title('Unable to select database'); - print theme('maintenance_page', '

We were able to connect to the MySQL database server (which means your username and password are okay) but not able to select the database.

-

The MySQL error was: '. theme('placeholder', mysql_error($connection)) .'.

-

Currently, the database is '. theme('placeholder', substr($url['path'], 1)) .'. The username is '. theme('placeholder', $url['user']) .' and the database server is '. theme('placeholder', $url['host']) .'.

- -

For more help, see the Installation and upgrading handbook. If you are unsure what these terms mean you should probably contact your hosting provider.

'); - exit; - } - - /* On MySQL 4.1 and later, force UTF-8 */ - if (version_compare(mysql_get_server_info(), '4.1.0', '>=')) { - mysql_query('SET NAMES "utf8"', $connection); - } - return $connection; -} - -/** - * Helper function for db_query(). - */ -function _db_query($query, $debug = 0) { - global $active_db, $queries; - - if (variable_get('dev_query', 0)) { - list($usec, $sec) = explode(' ', microtime()); - $timer = (float)$usec + (float)$sec; - } - - $result = mysql_query($query, $active_db); - - if (variable_get('dev_query', 0)) { - $bt = debug_backtrace(); - $query = $bt[2]['function'] . "\n" . $query; - list($usec, $sec) = explode(' ', microtime()); - $stop = (float)$usec + (float)$sec; - $diff = $stop - $timer; - $queries[] = array($query, $diff); - } - - if ($debug) { - print '

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

'; - } - - if (!mysql_errno($active_db)) { - return $result; - } - else { - trigger_error(check_plain(mysql_error($active_db) ."\nquery: ". $query), E_USER_WARNING); - return FALSE; - } -} - -/** - * Fetch one result row from the previous query as an object. - * - * @param $result - * A database query result resource, as returned from db_query(). - * @return - * An object representing the next row of the result. The attributes of this - * object are the table fields selected by the query. - */ -function db_fetch_object($result) { - if ($result) { - return mysql_fetch_object($result); - } -} - -/** - * Fetch one result row from the previous query as an array. - * - * @param $result - * A database query result resource, as returned from db_query(). - * @return - * An associative array representing the next row of the result. The keys of - * this object are the names of the table fields selected by the query, and - * the values are the field values for this result row. - */ -function db_fetch_array($result) { - if ($result) { - return mysql_fetch_array($result, MYSQL_ASSOC); - } -} - -/** - * Determine how many result rows were found by the preceding query. - * - * @param $result - * A database query result resource, as returned from db_query(). - * @return - * The number of result rows. - */ -function db_num_rows($result) { - if ($result) { - return mysql_num_rows($result); - } -} - -/** - * Return an individual result field from the previous query. - * - * Only use this function if exactly one field is being selected; otherwise, - * use db_fetch_object() or db_fetch_array(). - * - * @param $result - * A database query result resource, as returned from db_query(). - * @param $row - * The index of the row whose result is needed. - * @return - * The resulting field or FALSE. - */ -function db_result($result, $row = 0) { - if ($result && mysql_num_rows($result) > $row) { - return mysql_result($result, $row); - } - return FALSE; -} - -/** - * Determine whether the previous query caused an error. - */ -function db_error() { - global $active_db; - return mysql_errno($active_db); -} - -/** - * Return a new unique ID in the given sequence. - * - * For compatibility reasons, Drupal does not use auto-numbered fields in its - * database tables. Instead, this function is used to return a new unique ID - * of the type requested. If necessary, a new sequence with the given name - * will be created. - * - * Note that the table name should be in curly brackets to preserve compatibility - * with table prefixes. For example, db_next_id('{node}_nid'); - */ -function db_next_id($name) { - $name = db_prefix_tables($name); - db_query('LOCK TABLES {sequences} WRITE'); - $id = db_result(db_query("SELECT id FROM {sequences} WHERE name = '%s'", $name)) + 1; - db_query("REPLACE INTO {sequences} VALUES ('%s', %d)", $name, $id); - db_query('UNLOCK TABLES'); - - return $id; -} - -/** - * Determine the number of rows changed by the preceding query. - */ -function db_affected_rows() { - global $active_db; - return mysql_affected_rows($active_db); -} - -/** - * Runs a limited-range query in the active database. - * - * Use this as a substitute for db_query() when a subset of the query is to be - * returned. - * 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. The query arguments can be enclosed in one - * array instead. - * 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. - * - * @param $from - * The first result row to return. - * @param $count - * The maximum number of result rows to return. - * @return - * A database query result resource, or FALSE if the query was not executed - * correctly. - */ -function db_query_range($query) { - $args = func_get_args(); - $count = array_pop($args); - $from = array_pop($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); - $query .= ' LIMIT '. (int)$from .', '. (int)$count; - return _db_query($query); -} - -/** - * Runs a SELECT query and stores its results in a temporary table. - * - * Use this as a substitute for db_query() when the results need to stored - * in a temporary table. Temporary tables exist for the duration of the page - * request. - * 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. - * - * Note that if you need to know how many results were returned, you should do - * a SELECT COUNT(*) on the temporary table afterwards. db_num_rows() and - * db_affected_rows() do not give consistent result across different database - * types in this case. - * - * @param $query - * A string containing a normal SELECT SQL query. - * @param ... - * A variable number of arguments which are substituted into the query - * using printf() syntax. The query arguments can be enclosed in one - * array instead. - * 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. - * - * @param $table - * The name of the temporary table to select into. This name will not be - * prefixed as there is no risk of collision. - * @return - * A database query result resource, or FALSE if the query was not executed - * correctly. - */ -function db_query_temporary($query) { - $args = func_get_args(); - $tablename = array_pop($args); - array_shift($args); - - $query = preg_replace('/^SELECT/i', 'CREATE TEMPORARY TABLE '. $tablename .' Engine=HEAP SELECT', 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 _db_query($query); -} - -/** - * Returns a properly formatted Binary Large OBject value. - * - * @param $data - * Data to encode. - * @return - * Encoded data. - */ -function db_encode_blob($data) { - global $active_db; - return "'" . mysql_real_escape_string($data, $active_db) . "'"; -} - -/** - * Returns text from a Binary Large Object value. - * - * @param $data - * Data to decode. - * @return - * Decoded data. - */ -function db_decode_blob($data) { - return $data; -} - -/** - * Prepare user input for use in a database query, preventing SQL injection attacks. - */ -function db_escape_string($text) { - global $active_db; - return mysql_real_escape_string($text, $active_db); -} - -/** - * Lock a table. - */ -function db_lock_table($table) { - db_query('LOCK TABLES {'. db_escape_table($table) .'} WRITE'); -} - -/** - * Unlock all locked tables. - */ -function db_unlock_tables() { - db_query('UNLOCK TABLES'); -} - -/** - * Check if a table exists. - */ -function db_table_exists($table) { - return db_num_rows(db_query("SHOW TABLES LIKE '{" . db_escape_table($table) . "}'")); -} - -/** - * Wraps the given table.field entry with a DISTINCT(). The wrapper is added to - * the SELECT list entry of the given query and the resulting query is returned. - * This function only applies the wrapper if a DISTINCT doesn't already exist in - * the query. - * - * @param $table Table containing the field to set as DISTINCT - * @param $field Field to set as DISTINCT - * @param $query Query to apply the wrapper to - * @return SQL query with the DISTINCT wrapper surrounding the given table.field. - */ -function db_distinct_field($table, $field, $query) { - $field_to_select = 'DISTINCT('. $table .'.'. $field .')'; - // (? $t('MySQL database'), + 'value' => ($phase == 'runtime') ? l($version, 'admin/logs/status/sql') : $version, + ); + + if (version_compare($version, DRUPAL_MINIMUM_MYSQL) < 0) { + $form['mysql']['severity'] = REQUIREMENT_ERROR; + $form['mysql']['description'] = $t('Your MySQL Server is too old. Drupal requires at least MySQL %version.', array('%version' => DRUPAL_MINIMUM_MYSQL)); + } + + return $form; +} + +/** + * Returns the version of the database server currently in use. + * + * @return Database server version + */ +function db_version() { + list($version) = explode('-', mysql_get_server_info()); + return $version; +} + +/** + * Initialize a database connection. + * + * Note that you can change the mysql_connect() call to mysql_pconnect() if you + * want to use persistent connections. This is not recommended on shared hosts, + * and might require additional database/webserver tuning. It can increase + * performance, however, when the overhead to connect to your database is high + * (e.g. your database and web server live on different machines). + */ +function db_connect($url) { + $url = parse_url($url); + + // Check if MySQL support is present in PHP + if (!function_exists('mysql_connect')) { + // Redirect to installer if using default DB credentials + if ($url['user'] == 'username' && $url['pass'] == 'password') { + include_once 'includes/install.inc'; + install_goto('install.php'); + } + drupal_maintenance_theme(); + drupal_set_title('PHP MySQL support not enabled'); + print theme('maintenance_page', '

We were unable to use the MySQL database because the MySQL extension for PHP is not installed. Check your PHP.ini to see how you can enable it.

+

For more help, see the Installation and upgrading handbook. If you are unsure what these terms mean you should probably contact your hosting provider.

'); + exit; + } + + // Decode url-encoded information in the db connection string + $url['user'] = urldecode($url['user']); + // Test if database url has a password. + if(isset($url['pass'])) { + $url['pass'] = urldecode($url['pass']); + } + else { + $url['pass'] = ''; + } + $url['host'] = urldecode($url['host']); + $url['path'] = urldecode($url['path']); + + // Allow for non-standard MySQL port. + if (isset($url['port'])) { + $url['host'] = $url['host'] .':'. $url['port']; + } + + // - TRUE makes mysql_connect() always open a new link, even if + // mysql_connect() was called before with the same parameters. + // This is important if you are using two databases on the same + // server. + // - 2 means CLIENT_FOUND_ROWS: return the number of found + // (matched) rows, not the number of affected rows. + $connection = @mysql_connect($url['host'], $url['user'], $url['pass'], TRUE, 2); + if (!$connection) { + // Redirect to installer if using default DB credentials + if ($url['user'] == 'username' && $url['pass'] == 'password') { + include_once 'includes/install.inc'; + install_goto('install.php'); + } + + // Show error screen otherwise + drupal_maintenance_theme(); + drupal_set_header('HTTP/1.1 503 Service Unavailable'); + drupal_set_title('Unable to connect to database server'); + print theme('maintenance_page', '

If you still have to install Drupal, proceed to the installation page.

+

If you have already finished installed Drupal, this either means that the username and password information in your settings.php file is incorrect or that we can\'t connect to the MySQL database server. This could mean your hosting provider\'s database server is down.

+

The MySQL error was: '. theme('placeholder', mysql_error()) .'.

+

Currently, the username is '. theme('placeholder', $url['user']) .' and the database server is '. theme('placeholder', $url['host']) .'.

+ +

For more help, see the Installation and upgrading handbook. If you are unsure what these terms mean you should probably contact your hosting provider.

'); + exit; + } + + if (!mysql_select_db(substr($url['path'], 1))) { + drupal_maintenance_theme(); + drupal_set_title('Unable to select database'); + print theme('maintenance_page', '

We were able to connect to the MySQL database server (which means your username and password are okay) but not able to select the database.

+

The MySQL error was: '. theme('placeholder', mysql_error($connection)) .'.

+

Currently, the database is '. theme('placeholder', substr($url['path'], 1)) .'. The username is '. theme('placeholder', $url['user']) .' and the database server is '. theme('placeholder', $url['host']) .'.

+ +

For more help, see the Installation and upgrading handbook. If you are unsure what these terms mean you should probably contact your hosting provider.

'); + exit; + } + + /* On MySQL 4.1 and later, force UTF-8 */ + if (version_compare(mysql_get_server_info(), '4.1.0', '>=')) { + mysql_query('SET NAMES "utf8"', $connection); + } + return $connection; +} + + + +/** + * Runs a basic query in the active database. + * + * 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 + * A database query result resource, or FALSE if the query was not + * executed correctly. + */ +function db_query($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 _db_query($query); +} + +/** + * Helper function for db_query(). + */ +function _db_query($query, $debug = 0) { + global $active_db, $queries; + + if (variable_get('dev_query', 0)) { + list($usec, $sec) = explode(' ', microtime()); + $timer = (float)$usec + (float)$sec; + } + + $result = mysql_query($query, $active_db); + + if (variable_get('dev_query', 0)) { + $bt = debug_backtrace(); + $query = $bt[2]['function'] . "\n" . $query; + list($usec, $sec) = explode(' ', microtime()); + $stop = (float)$usec + (float)$sec; + $diff = $stop - $timer; + $queries[] = array($query, $diff); + } + + if ($debug) { + print '

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

'; + } + + if (!mysql_errno($active_db)) { + return $result; + } + else { + trigger_error(check_plain(mysql_error($active_db) ."\nquery: ". $query), E_USER_WARNING); + return FALSE; + } +} + + +/** + * Fetch one result row from the previous query as an object. + * + * @param $result + * A database query result resource, as returned from db_query(). + * @return + * An object representing the next row of the result. The attributes of this + * object are the table fields selected by the query. + */ +function db_fetch_object($result) { + if ($result) { + return mysql_fetch_object($result); + } +} + +/** + * Fetch one result row from the previous query as an array. + * + * @param $result + * A database query result resource, as returned from db_query(). + * @return + * An associative array representing the next row of the result. The keys of + * this object are the names of the table fields selected by the query, and + * the values are the field values for this result row. + */ +function db_fetch_array($result) { + if ($result) { + return mysql_fetch_array($result, MYSQL_ASSOC); + } +} + +/** + * Determine how many result rows were found by the preceding query. + * + * @param $result + * A database query result resource, as returned from db_query(). + * @return + * The number of result rows. + */ +/* +function db_num_rows($result) { + if ($result) { + return mysql_num_rows($result); + } +} +*/ + +/** + * Return an individual result field from the previous query. + * + * Only use this function if exactly one field is being selected; otherwise, + * use db_fetch_object() or db_fetch_array(). + * + * @param $result + * A database query result resource, as returned from db_query(). + * @param $row + * The index of the row whose result is needed. + * @return + * The resulting field or FALSE. + */ +function db_result($result, $row = 0) { + if ($result && mysql_num_rows($result) > $row) { + return mysql_result($result, $row); + } + return FALSE; +} + +/** + * Determine whether the previous query caused an error. + */ +function db_error() { + global $active_db; + return mysql_errno($active_db); +} + +/** + * Return a new unique ID in the given sequence. + * + * For compatibility reasons, Drupal does not use auto-numbered fields in its + * database tables. Instead, this function is used to return a new unique ID + * of the type requested. If necessary, a new sequence with the given name + * will be created. + * + * Note that the table name should be in curly brackets to preserve compatibility + * with table prefixes. For example, db_next_id('{node}_nid'); + */ +function db_next_id($name) { + $name = db_prefix_tables($name); + db_query('LOCK TABLES {sequences} WRITE'); + $id = db_result(db_query("SELECT id FROM {sequences} WHERE name = '%s'", $name)) + 1; + db_query("REPLACE INTO {sequences} VALUES ('%s', %d)", $name, $id); + db_query('UNLOCK TABLES'); + + return $id; +} + +/** + * Determine the number of rows changed by the preceding query. + */ +function db_affected_rows() { + global $active_db; + return mysql_affected_rows($active_db); +} + +/** + * Runs a limited-range query in the active database. + * + * Use this as a substitute for db_query() when a subset of the query is to be + * returned. + * 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. The query arguments can be enclosed in one + * array instead. + * 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. + * + * @param $from + * The first result row to return. + * @param $count + * The maximum number of result rows to return. + * @return + * A database query result resource, or FALSE if the query was not executed + * correctly. + */ +function db_query_range($query) { + $args = func_get_args(); + $count = array_pop($args); + $from = array_pop($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); + $query .= ' LIMIT '. (int)$from .', '. (int)$count; + return _db_query($query); +} + +/** + * Runs a SELECT query and stores its results in a temporary table. + * + * Use this as a substitute for db_query() when the results need to stored + * in a temporary table. Temporary tables exist for the duration of the page + * request. + * 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. + * + * Note that if you need to know how many results were returned, you should do + * a SELECT COUNT(*) on the temporary table afterwards. db_num_rows() and + * db_affected_rows() do not give consistent result across different database + * types in this case. + * + * @param $query + * A string containing a normal SELECT SQL query. + * @param ... + * A variable number of arguments which are substituted into the query + * using printf() syntax. The query arguments can be enclosed in one + * array instead. + * 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. + * + * @param $table + * The name of the temporary table to select into. This name will not be + * prefixed as there is no risk of collision. + * @return + * A database query result resource, or FALSE if the query was not executed + * correctly. + */ +function db_query_temporary($query) { + $args = func_get_args(); + $tablename = array_pop($args); + array_shift($args); + + $query = preg_replace('/^SELECT/i', 'CREATE TEMPORARY TABLE '. $tablename .' Engine=HEAP SELECT', 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 _db_query($query); +} + +/** + * Returns a properly formatted Binary Large OBject value. + * + * @param $data + * Data to encode. + * @return + * Encoded data. + */ +function db_encode_blob($data) { + global $active_db; + return "'" . mysql_real_escape_string($data, $active_db) . "'"; +} + +/** + * Returns text from a Binary Large Object value. + * + * @param $data + * Data to decode. + * @return + * Decoded data. + */ +function db_decode_blob($data) { + return $data; +} + +/** + * Prepare user input for use in a database query, preventing SQL injection attacks. + */ +function db_escape_string($text) { + global $active_db; + return mysql_real_escape_string($text, $active_db); +} + +/** + * Lock a table. + */ +function db_lock_table($table) { + db_query('LOCK TABLES {'. db_escape_table($table) .'} WRITE'); +} + +/** + * Unlock all locked tables. + */ +function db_unlock_tables() { + db_query('UNLOCK TABLES'); +} + +/** + * Check if a table exists. + */ +function db_table_exists($table) { + return db_num_rows(db_query("SHOW TABLES LIKE '{" . db_escape_table($table) . "}'")); +} + +/** + * Wraps the given table.field entry with a DISTINCT(). The wrapper is added to + * the SELECT list entry of the given query and the resulting query is returned. + * This function only applies the wrapper if a DISTINCT doesn't already exist in + * the query. + * + * @param $table Table containing the field to set as DISTINCT + * @param $field Field to set as DISTINCT + * @param $query Query to apply the wrapper to + * @return SQL query with the DISTINCT wrapper surrounding the given table.field. + */ +function db_distinct_field($table, $field, $query) { + $field_to_select = 'DISTINCT('. $table .'.'. $field .')'; + // (? $t('MySQL database'), + 'value' => ($phase == 'runtime') ? l($version, 'admin/logs/status/sql') : $version, + ); + + if (version_compare($version, DRUPAL_MINIMUM_MYSQL) < 0) { + $form['mysql']['severity'] = REQUIREMENT_ERROR; + $form['mysql']['description'] = $t('Your MySQL Server is too old. Drupal requires at least MySQL %version.', array('%version' => DRUPAL_MINIMUM_MYSQL)); + } + + return $form; +} + +/** + * Returns the version of the database server currently in use. + * + * @return Database server version + */ +function db_version() { + list($version) = explode('-', mysql_get_server_info()); + return $version; +} + +/** + * Initialize a database connection. + * + * Note that you can change the mysql_connect() call to mysql_pconnect() if you + * want to use persistent connections. This is not recommended on shared hosts, + * and might require additional database/webserver tuning. It can increase + * performance, however, when the overhead to connect to your database is high + * (e.g. your database and web server live on different machines). + */ +function db_connect($url) { + $url = parse_url($url); + + // Check if PDO support is present in PHP, and the MySQL driver + if (!class_exists('PDO') || !in_array('mysql', PDO::getAvailableDrivers())) { + // Redirect to installer if using default DB credentials + if ($url['user'] == 'username' && $url['pass'] == 'password') { + include_once 'includes/install.inc'; + install_goto('install.php'); + } + drupal_maintenance_theme(); + drupal_set_title('PHP PDO-MySQL support not enabled'); + print theme('maintenance_page', '

We were unable to use the MySQL database because the PDO extension for PHP is not installed. Check your PHP.ini to see how you can enable it.

+

For more help, see the Installation and upgrading handbook. If you are unsure what these terms mean you should probably contact your hosting provider.

'); + exit; + } + + + + // Decode url-encoded information in the db connection string + $url['user'] = urldecode($url['user']); + // Test if database url has a password. + if(isset($url['pass'])) { + $url['pass'] = urldecode($url['pass']); + } + else { + $url['pass'] = ''; + } + $url['host'] = urldecode($url['host']); + $url['path'] = urldecode($url['path']); + + // Allow for non-standard MySQL port. + if (isset($url['port'])) { + $url['host'] = $url['host'] .':'. $url['port']; + } + + // - TRUE makes mysql_connect() always open a new link, even if + // mysql_connect() was called before with the same parameters. + // This is important if you are using two databases on the same + // server. + // - 2 means CLIENT_FOUND_ROWS: return the number of found + // (matched) rows, not the number of affected rows. + + try { + $connection = new PDO('mysql:host='. $url['host'] . ';dbname=' . substr($url['path'], 1), $url['user'], $url['pass'], array( + PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => TRUE, + )); + $connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, true); + $connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + } + catch (PDOException $e) { + // Redirect to installer if using default DB credentials + if ($url['user'] == 'username' && $url['pass'] == 'password') { + include_once 'includes/install.inc'; + install_goto('install.php'); + } + + // Show error screen otherwise + drupal_maintenance_theme(); + drupal_set_header('HTTP/1.1 503 Service Unavailable'); + drupal_set_title('Unable to connect to database server'); + print theme('maintenance_page', '

If you still have to install Drupal, proceed to the installation page.

+

If you have already finished installed Drupal, this either means that the username and password information in your settings.php file is incorrect or that we can\'t connect to the MySQL database server. This could mean your hosting provider\'s database server is down.

+

The MySQL error was: '. theme('placeholder', mysql_error()) .'.

+

Currently, the username is '. theme('placeholder', $url['user']) .' and the database server is '. theme('placeholder', $url['host']) .'.

+ +

For more help, see the Installation and upgrading handbook. If you are unsure what these terms mean you should probably contact your hosting provider.

'); + exit; + + /* Old code for separate handling of db failure rather than server failure + drupal_maintenance_theme(); + drupal_set_title('Unable to select database'); + print theme('maintenance_page', '

We were able to connect to the MySQL database server (which means your username and password are okay) but not able to select the database.

+

The MySQL error was: '. theme('placeholder', mysql_error($connection)) .'.

+

Currently, the database is '. theme('placeholder', substr($url['path'], 1)) .'. The username is '. theme('placeholder', $url['user']) .' and the database server is '. theme('placeholder', $url['host']) .'.

+ +

For more help, see the Installation and upgrading handbook. If you are unsure what these terms mean you should probably contact your hosting provider.

'); + exit; + */ + + } + + // Set the connection to UTF-8 + $connection->exec('SET NAMES "utf8"'); + + return $connection; +} + + +/** + * Runs a basic query in the active database. + * + * 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 + * A database query result resource, or FALSE if the query was not + * executed correctly. + */ +function db_query($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]; + } + + $query = str_replace(array('%d', '$f', '%b', "'%s'", '%s'), '?', $query); + + // NOT NULL DEFAULT 0 and similar techniques in MySQL don't work through PDO. For now, + // fold NULL to 0. Remove this eventually and make the caller send real data + foreach ($args as $key => $value) { + if (is_null($args[$key])) { + $args[$key] = 0; + } + } + + return _db_query($query, $args); +} + +/** + * Helper function for db_query(). + */ +function _db_query($query, $args, $debug = 0) { + global $active_db, $queries; + + static $prepared = array(); + + if (variable_get('dev_query', 0)) { + list($usec, $sec) = explode(' ', microtime()); + $timer = (float)$usec + (float)$sec; + } + + // Static-cache prepared statements for speed + if (!isset($prepared[$query])) { + $prepared[$query] = $active_db->prepare($query); + } + $result = $prepared[$query]; + + // Run the query. ->execute() returns TRUE on success or FALSE on error + // There seems to be a bug with the return value of the ->execute() statement, + // so we use exceptions instead, since they seem to work. + try { + $result->execute($args); + + if (variable_get('dev_query', 0)) { + $bt = debug_backtrace(); + $query = $bt[2]['function'] . "\n" . $query; + list($usec, $sec) = explode(' ', microtime()); + $stop = (float)$usec + (float)$sec; + $diff = $stop - $timer; + $queries[] = array($query, $diff); + } + + return $result; + } + catch (Exception $e) { + if (variable_get('dev_query', 0)) { + $bt = debug_backtrace(); + $query = $bt[2]['function'] . "\n" . $query; + list($usec, $sec) = explode(' ', microtime()); + $stop = (float)$usec + (float)$sec; + $diff = $stop - $timer; + $queries[] = array($query, $diff); + } + + $error_info = $result->errorInfo(); + //debug($error_info); + //debug($result); + //debug($args); + trigger_error(check_plain($error_info[0] ."\nquery: ". $query), E_USER_WARNING); + return FALSE; + } + + if ($debug) { + print '

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

'; + } + + return FALSE; + +} + +/** + * Fetch one result row from the previous query as an object. + * + * @param $result + * A database query result resource, as returned from db_query(). + * @return + * An object representing the next row of the result. The attributes of this + * object are the table fields selected by the query. + */ +function db_fetch_object(PDOStatement $result) { + if ($result) { + return $result->fetch(PDO::FETCH_OBJ); + } +} + +/** + * Fetch one result row from the previous query as an array. + * + * @param $result + * A database query result resource, as returned from db_query(). + * @return + * An associative array representing the next row of the result. The keys of + * this object are the names of the table fields selected by the query, and + * the values are the field values for this result row. + */ +function db_fetch_array(PDOStatement $result) { + if ($result) { + return $result->fetch(PDO::FETCH_ASSOC); + } +} + +/** + * Determine how many result rows were found by the preceding query. + * + * @param $result + * A database query result resource, as returned from db_query(). + * @return + * The number of result rows. + */ + /* +function db_num_rows(PDOStatement $result) { + if ($result) { + return $result->rowCount(); + } +} +*/ + +/** + * Return an individual result field from the previous query. + * + * Only use this function if exactly one field is being selected; otherwise, + * use db_fetch_object() or db_fetch_array(). + * + * @param $result + * A database query result resource, as returned from db_query(). + * @param $row + * The index of the row whose result is needed. + * @return + * The resulting field or FALSE. + */ +function db_result($result, $row = 0) { + if ($result) { + $column = $result->fetchColumn(PDO::FETCH_ASSOC); + return $column[$row]; + } + return FALSE; +} + +/** + * Determine whether the previous query caused an error. + */ +function db_error() { + global $active_db; + return $active_db->errorCode(); +} + +/** + * Return a new unique ID in the given sequence. + * + * For compatibility reasons, Drupal does not use auto-numbered fields in its + * database tables. Instead, this function is used to return a new unique ID + * of the type requested. If necessary, a new sequence with the given name + * will be created. + * + * Note that the table name should be in curly brackets to preserve compatibility + * with table prefixes. For example, db_next_id('{node}_nid'); + */ +function db_next_id($name) { + $name = db_prefix_tables($name); + db_query('LOCK TABLES {sequences} WRITE'); + $id = db_result(db_query("SELECT id FROM {sequences} WHERE name = '%s'", $name)) + 1; + db_query("REPLACE INTO {sequences} VALUES ('%s', %d)", $name, $id); + db_query('UNLOCK TABLES'); + + return $id; +} + +/** + * Determine the number of rows changed by the preceding query. + */ +function db_affected_rows(PDOStatement $statement) { + return $statement->rowCount(); +} + + +/** + * Runs a limited-range query in the active database. + * + * Use this as a substitute for db_query() when a subset of the query is to be + * returned. + * 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. The query arguments can be enclosed in one + * array instead. + * 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. + * + * @param $from + * The first result row to return. + * @param $count + * The maximum number of result rows to return. + * @return + * A database query result resource, or FALSE if the query was not executed + * correctly. + */ +function db_query_range($query) { + $args = func_get_args(); + $count = array_pop($args); + $from = array_pop($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]; + } + $query = str_replace(array('%d', '$f', 'b', "'%s'"), '?', $query); + + $query .= ' LIMIT '. (int)$from .', '. (int)$count; + + return _db_query($query, $args); +} + +/** + * Runs a SELECT query and stores its results in a temporary table. + * + * Use this as a substitute for db_query() when the results need to stored + * in a temporary table. Temporary tables exist for the duration of the page + * request. + * 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. + * + * Note that if you need to know how many results were returned, you should do + * a SELECT COUNT(*) on the temporary table afterwards. db_num_rows() and + * db_affected_rows() do not give consistent result across different database + * types in this case. + * + * @param $query + * A string containing a normal SELECT SQL query. + * @param ... + * A variable number of arguments which are substituted into the query + * using printf() syntax. The query arguments can be enclosed in one + * array instead. + * 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. + * + * @param $table + * The name of the temporary table to select into. This name will not be + * prefixed as there is no risk of collision. + * @return + * A database query result resource, or FALSE if the query was not executed + * correctly. + */ +function db_query_temporary($query) { + $args = func_get_args(); + $tablename = array_pop($args); + array_shift($args); + + $query = preg_replace('/^SELECT/i', 'CREATE TEMPORARY TABLE '. $tablename .' Engine=HEAP SELECT', 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 _db_query($query); +} + +/** + * Returns a properly formatted Binary Large OBject value. + * + * @param $data + * Data to encode. + * @return + * Encoded data. + */ +function db_encode_blob($data) { + global $active_db; + return "'" . mysql_real_escape_string($data, $active_db) . "'"; +} + +/** + * Returns text from a Binary Large Object value. + * + * @param $data + * Data to decode. + * @return + * Decoded data. + */ +function db_decode_blob($data) { + return $data; +} + +/** + * Prepare user input for use in a database query, preventing SQL injection attacks. + */ +/*function db_escape_string($text) { + global $active_db; + return mysql_real_escape_string($text, $active_db); +}*/ + +/** + * Lock a table. + */ +function db_lock_table($table) { + db_query('LOCK TABLES {'. db_escape_table($table) .'} WRITE'); +} + +/** + * Unlock all locked tables. + */ +function db_unlock_tables() { + db_query('UNLOCK TABLES'); +} + +/** + * Check if a table exists. + */ +function db_table_exists($table) { + return db_num_rows(db_query("SHOW TABLES LIKE '{" . db_escape_table($table) . "}'")); +} + +/** + * Wraps the given table.field entry with a DISTINCT(). The wrapper is added to + * the SELECT list entry of the given query and the resulting query is returned. + * This function only applies the wrapper if a DISTINCT doesn't already exist in + * the query. + * + * @param $table Table containing the field to set as DISTINCT + * @param $field Field to set as DISTINCT + * @param $query Query to apply the wrapper to + * @return SQL query with the DISTINCT wrapper surrounding the given table.field. + */ +function db_distinct_field($table, $field, $query) { + $field_to_select = 'DISTINCT('. $table .'.'. $field .')'; + // (?custom = TRUE; $type->modified = FALSE; $type->locked = FALSE; + $type->show_in_menu = TRUE; } $form['#node_type'] = $type; // Make the type object available to implementations of hook_form_alter. @@ -139,6 +140,12 @@ '#options' => drupal_map_assoc(array(0, 10, 25, 50, 75, 100, 125, 150, 175, 200)), '#description' => t('The minimum number of words for the body field to be considered valid for this content type. This can be useful to rule out submissions that do not meet the site\'s standards, such as short test posts.') ); + $form['submission']['show_in_menu'] = array( + '#type' => 'checkbox', + '#title' => t('Show in create content menu.'), + '#description' => t('A node type that is not shown in the create content menu may only be created programmatically.'), + '#default_value' => $type->show_in_menu, + ); $form['submission']['help'] = array( '#type' => 'textarea', '#title' => t('Explanation or submission guidelines'), @@ -277,6 +284,7 @@ $type->custom = $form_values['custom']; $type->modified = TRUE; $type->locked = $form_values['locked']; + $type->show_in_menu = $form_values['show_in_menu']; if ($op == t('Reset to defaults')) { node_type_reset($type); Index: modules/node/node.module =================================================================== RCS file: /cvs/drupal/drupal/modules/node/node.module,v retrieving revision 1.797 diff -u -r1.797 node.module --- modules/node/node.module 6 Apr 2007 13:27:22 -0000 1.797 +++ modules/node/node.module 8 Apr 2007 04:00:20 -0000 @@ -333,13 +333,13 @@ } if ($is_existing) { - db_query("UPDATE {node_type} SET type = '%s', name = '%s', module = '%s', has_title = %d, title_label = '%s', has_body = %d, body_label = '%s', description = '%s', help = '%s', min_word_count = %d, custom = %d, modified = %d, locked = %d WHERE type = '%s'", $info->type, $info->name, $info->module, $info->has_title, $info->title_label, $info->has_body, $info->body_label, $info->description, $info->help, $info->min_word_count, $info->custom, $info->modified, $info->locked, $existing_type); + db_query("UPDATE {node_type} SET type = '%s', name = '%s', module = '%s', has_title = %d, title_label = '%s', has_body = %d, body_label = '%s', description = '%s', help = '%s', min_word_count = %d, custom = %d, modified = %d, locked = %d, show_in_menu = %d WHERE type = '%s'", $info->type, $info->name, $info->module, $info->has_title, $info->title_label, $info->has_body, $info->body_label, $info->description, $info->help, $info->min_word_count, $info->custom, $info->modified, $info->locked, $info->show_in_menu, $existing_type); module_invoke_all('node_type', 'update', $info); return SAVED_UPDATED; } else { - db_query("INSERT INTO {node_type} (type, name, module, has_title, title_label, has_body, body_label, description, help, min_word_count, custom, modified, locked, orig_type) VALUES ('%s', '%s', '%s', %d, '%s', %d, '%s', '%s', '%s', %d, %d, %d, %d, '%s')", $info->type, $info->name, $info->module, $info->has_title, $info->title_label, $info->has_body, $info->body_label, $info->description, $info->help, $info->min_word_count, $info->custom, $info->modified, $info->locked, $info->orig_type); + db_query("INSERT INTO {node_type} (type, name, module, has_title, title_label, has_body, body_label, description, help, min_word_count, custom, modified, locked, orig_type, show_in_menu) VALUES ('%s', '%s', '%s', %d, '%s', %d, '%s', '%s', '%s', %d, %d, %d, %d, '%s', %d)", $info->type, $info->name, $info->module, $info->has_title, $info->title_label, $info->has_body, $info->body_label, $info->description, $info->help, $info->min_word_count, $info->custom, $info->modified, $info->locked, $info->orig_type, $info->show_in_menu); module_invoke_all('node_type', 'insert', $info); return SAVED_NEW; @@ -436,6 +436,9 @@ if (!isset($info['locked'])) { $info['locked'] = TRUE; } + if (!isset($info['show_in_menu'])) { + $info['show_in_menu'] = TRUE; + } $info['orig_type'] = $info['type']; $info['is_new'] = TRUE; @@ -1168,7 +1171,7 @@ ); $items['node/add'] = array( 'title' => t('Create content'), - 'page callback' => 'node_add', + 'page callback' => 'system_admin_menu_block_page', 'access callback' => '_node_add_access', 'weight' => 1, ); @@ -1182,12 +1185,16 @@ if (function_exists($type->module .'_form')) { $name = check_plain($type->name); $type_url_str = str_replace('_', '-', $type->type); - $items['node/add/'. $type_url_str] = array( - 'title' => drupal_ucfirst($name), - 'page arguments' => array(2), - 'access callback' => 'node_access', - 'access arguments' => array('create', $type->type), - ); + if ($node->show_in_menu) { + $items['node/add/'. $type_url_str] = array( + 'title' => drupal_ucfirst($name), + 'page callback' => 'node_add', + 'page arguments' => array(2), + 'access callback' => 'node_access', + 'access arguments' => array('create', $type->type), + 'description' => $type->description, + ); + } $items['admin/content/types/'. $type_url_str] = array( 'title' => t($type->name), 'page callback' => 'drupal_get_form', @@ -2146,40 +2153,15 @@ /** * Present a node submission form or a set of links to such forms. */ -function node_add($type = NULL) { +function node_add($type) { global $user; $types = node_get_types(); - $type = isset($type) ? str_replace('-', '_', $type) : NULL; - // If a node type has been specified, validate its existence. - if (isset($types[$type]) && node_access('create', $type)) { - // Initialize settings: - $node = array('uid' => $user->uid, 'name' => $user->name, 'type' => $type); - - drupal_set_title(t('Submit @name', array('@name' => $types[$type]->name))); - $output = drupal_get_form($type .'_node_form', $node); - } - else { - // If no (valid) node type has been provided, display a node type overview. - foreach ($types as $type) { - if (function_exists($type->module .'_form') && node_access('create', $type->type)) { - $type_url_str = str_replace('_', '-', $type->type); - $title = t('Add a new @s.', array('@s' => $type->name)); - $out = '
'. l(drupal_ucfirst($type->name), "node/add/$type_url_str", array('attributes' => array('title' => $title))) .'
'; - $out .= '
'. filter_xss_admin($type->description) .'
'; - $item[$type->type] = $out; - } - } - - if (isset($item)) { - uksort($item, 'strnatcasecmp'); - $output = '
'. implode('', $item) .'
'; - } - else { - $output = t('No content types available.'); - } - } + $type = str_replace('-', '_', $type); + $node = array('uid' => $user->uid, 'name' => $user->name, 'type' => $type); + drupal_set_title(t('Submit @name', array('@name' => $types[$type]->name))); + return drupal_get_form($type .'_node_form', $node); return $output; } @@ -2383,12 +2365,18 @@ function node_page_default() { $result = pager_query(db_rewrite_sql('SELECT n.nid, n.sticky, n.created FROM {node} n WHERE n.promote = 1 AND n.status = 1 ORDER BY n.sticky DESC, n.created DESC'), variable_get('default_nodes_main', 10)); - if (db_num_rows($result)) { + // We don't have db_num_rows() anymore, so we have to pre-fetch the data to know if there is any + $records = array(); + while ($record = db_fetch_object($result)) { + $records[] = $record; + } + + if (count($records)) { $feed_url = url('rss.xml', array('absolute' => TRUE)); drupal_add_feed($feed_url, variable_get('site_name', 'Drupal') .' '. t('RSS')); $output = ''; - while ($node = db_fetch_object($result)) { + foreach ($records as $node) { $output .= node_view(node_load($node->nid), 1); } $output .= theme('pager', NULL, variable_get('default_nodes_main', 10)); Index: modules/system/system.install =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.install,v retrieving revision 1.88 diff -u -r1.88 system.install --- modules/system/system.install 30 Mar 2007 08:47:58 -0000 1.88 +++ modules/system/system.install 8 Apr 2007 04:00:21 -0000 @@ -428,6 +428,7 @@ modified tinyint NOT NULL DEFAULT '0', locked tinyint NOT NULL DEFAULT '0', orig_type varchar(255) NOT NULL default '', + show_in_menu tinyint unsigned not null default '1', PRIMARY KEY (type) ) /*!40100 DEFAULT CHARACTER SET UTF8 */ "); @@ -913,6 +914,7 @@ modified smallint NOT NULL DEFAULT '0', locked smallint NOT NULL DEFAULT '0', orig_type varchar(255) NOT NULL default '', + show_in_menu smallint unsigned not null default '1', PRIMARY KEY (type) )"); @@ -3681,6 +3683,26 @@ return $ret; } + +/** + * Add index on comments status column. + */ +function system_update_2004() { + $ret = array(); + switch ($GLOBALS['db_type']) { + case 'mysql': + case 'mysqli': + $ret[] = update_sql('ALTER TABLE {node_type} ADD COLUMN show_in_menu tinyint unsigned NOT NULL DEFAULT 1'); + break; + + case 'pgsql': + $ret[] = update_sql("ALTER TABLE {node_type} ADD COLUMN show_in_menu smallint unsigned NOT NULL DEFAULT 1"); + break; + } + return $ret; +} + + /** * Add language to url_alias table and modify indexes. */ Index: modules/user/user.module =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.module,v retrieving revision 1.765 diff -u -r1.765 user.module --- modules/user/user.module 6 Apr 2007 14:27:22 -0000 1.765 +++ modules/user/user.module 8 Apr 2007 04:00:21 -0000 @@ -102,8 +102,9 @@ } $result = db_query('SELECT * FROM {users} u WHERE '. implode(' AND ', $query), $params); - if (db_num_rows($result)) { - $user = db_fetch_object($result); + $user = db_fetch_object($result); + + if ($user) { $user = drupal_unpack($user); $user->roles = array();