summaryrefslogtreecommitdiff
path: root/includes
diff options
context:
space:
mode:
Diffstat (limited to 'includes')
-rw-r--r--includes/bootstrap.inc66
-rw-r--r--includes/cache.inc50
-rw-r--r--includes/common.inc66
-rw-r--r--includes/database/database.inc3
-rw-r--r--includes/database/mysql/database.inc2
-rw-r--r--includes/database/mysql/schema.inc11
-rw-r--r--includes/database/pgsql/database.inc2
-rw-r--r--includes/database/pgsql/schema.inc4
-rw-r--r--includes/database/query.inc2
-rw-r--r--includes/database/schema.inc7
-rw-r--r--includes/database/sqlite/database.inc8
-rw-r--r--includes/date.inc2
-rw-r--r--includes/entity.inc26
-rw-r--r--includes/errors.inc22
-rw-r--r--includes/file.inc106
-rw-r--r--includes/form.inc328
-rw-r--r--includes/graph.inc4
-rw-r--r--includes/language.inc14
-rw-r--r--includes/locale.inc34
-rw-r--r--includes/menu.inc36
-rw-r--r--includes/module.inc4
-rw-r--r--includes/pager.inc8
-rw-r--r--includes/path.inc30
-rw-r--r--includes/registry.inc34
-rw-r--r--includes/session.inc40
-rw-r--r--includes/theme.inc35
-rw-r--r--includes/unicode.inc2
-rw-r--r--includes/update.inc10
-rw-r--r--includes/updater.inc2
-rw-r--r--includes/utility.inc2
30 files changed, 618 insertions, 342 deletions
diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc
index 763aad6d7..50e8253bb 100644
--- a/includes/bootstrap.inc
+++ b/includes/bootstrap.inc
@@ -68,32 +68,32 @@ define('WATCHDOG_EMERGENCY', 0);
define('WATCHDOG_ALERT', 1);
/**
- * Log message severity -- Critical: critical conditions.
+ * Log message severity -- Critical conditions.
*/
define('WATCHDOG_CRITICAL', 2);
/**
- * Log message severity -- Error: error conditions.
+ * Log message severity -- Error conditions.
*/
define('WATCHDOG_ERROR', 3);
/**
- * Log message severity -- Warning: warning conditions.
+ * Log message severity -- Warning conditions.
*/
define('WATCHDOG_WARNING', 4);
/**
- * Log message severity -- Notice: normal but significant condition.
+ * Log message severity -- Normal but significant conditions.
*/
define('WATCHDOG_NOTICE', 5);
/**
- * Log message severity -- Informational: informational messages.
+ * Log message severity -- Informational messages.
*/
define('WATCHDOG_INFO', 6);
/**
- * Log message severity -- Debug: debug-level messages.
+ * Log message severity -- Debug-level messages.
*/
define('WATCHDOG_DEBUG', 7);
@@ -279,7 +279,7 @@ define('DRUPAL_PHP_FUNCTION_PATTERN', '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*'
* means that assigning an offset via arrayAccess will only apply while the
* object is in scope and will not be written back to the persistent cache.
* This follows a similar pattern to static vs. persistent caching in
- * procedural code. Extending classes may wish to alter this behaviour, for
+ * procedural code. Extending classes may wish to alter this behavior, for
* example by overriding offsetSet() and adding an automatic call to persist().
*
* @see SchemaCache
@@ -966,7 +966,7 @@ function variable_initialize($conf = array()) {
* The default value to use if this variable has never been set.
*
* @return
- * The value of the variable.
+ * The value of the variable. Unserialization is taken care of as necessary.
*
* @see variable_del()
* @see variable_set()
@@ -1691,8 +1691,16 @@ function watchdog_exception($type, Exception $exception, $message = NULL, $varia
* NULL if message is already translated or not possible to
* translate.
* @param $severity
- * The severity of the message, as per RFC 3164. Possible values are
- * WATCHDOG_ERROR, WATCHDOG_WARNING, etc.
+ * The severity of the message; one of the following values as defined in
+ * @link http://www.faqs.org/rfcs/rfc3164.html RFC 3164: @endlink
+ * - WATCHDOG_EMERGENCY: Emergency, system is unusable.
+ * - WATCHDOG_ALERT: Alert, action must be taken immediately.
+ * - WATCHDOG_CRITICAL: Critical conditions.
+ * - WATCHDOG_ERROR: Error conditions.
+ * - WATCHDOG_WARNING: Warning conditions.
+ * - WATCHDOG_NOTICE: (default) Normal but significant conditions.
+ * - WATCHDOG_INFO: Informational messages.
+ * - WATCHDOG_DEBUG: Debug-level messages.
* @param $link
* A link to associate with the message.
*
@@ -1709,6 +1717,9 @@ function watchdog($type, $message, $variables = array(), $severity = WATCHDOG_NO
if (!$in_error_state && function_exists('module_implements')) {
$in_error_state = TRUE;
+ // The user object may not exist in all conditions, so 0 is substituted if needed.
+ $user_uid = isset($user->uid) ? $user->uid : 0;
+
// Prepare the fields to be logged
$log_entry = array(
'type' => $type,
@@ -1717,6 +1728,7 @@ function watchdog($type, $message, $variables = array(), $severity = WATCHDOG_NO
'severity' => $severity,
'link' => $link,
'user' => $user,
+ 'uid' => $user_uid,
'request_uri' => $base_root . request_uri(),
'referer' => isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '',
'ip' => ip_address(),
@@ -1913,7 +1925,7 @@ function drupal_block_denied($ip) {
*/
function drupal_random_bytes($count) {
// $random_state does not use drupal_static as it stores random bytes.
- static $random_state, $bytes;
+ static $random_state, $bytes, $php_compatible;
// Initialize on the first call. The contents of $_SERVER includes a mix of
// user-specific and system information that varies a little with each page.
if (!isset($random_state)) {
@@ -1925,6 +1937,11 @@ function drupal_random_bytes($count) {
$bytes = '';
}
if (strlen($bytes) < $count) {
+ // PHP versions prior 5.3.4 experienced openssl_random_pseudo_bytes()
+ // locking on Windows and rendered it unusable.
+ if (!isset($php_compatible)) {
+ $php_compatible = version_compare(PHP_VERSION, '5.3.4', '>=');
+ }
// /dev/urandom is available on many *nix systems and is considered the
// best commonly available pseudo-random source.
if ($fh = @fopen('/dev/urandom', 'rb')) {
@@ -1934,6 +1951,11 @@ function drupal_random_bytes($count) {
$bytes .= fread($fh, max(4096, $count));
fclose($fh);
}
+ // openssl_random_pseudo_bytes() will find entropy in a system-dependent
+ // way.
+ elseif ($php_compatible && function_exists('openssl_random_pseudo_bytes')) {
+ $bytes .= openssl_random_pseudo_bytes($count - strlen($bytes));
+ }
// If /dev/urandom is not available or returns no bytes, this loop will
// generate a good set of pseudo-random bytes on any system.
// Note that it may be important that our $random_state is passed
@@ -2428,7 +2450,8 @@ function drupal_valid_test_ua() {
}
}
- return FALSE;
+ $test_prefix = FALSE;
+ return $test_prefix;
}
/**
@@ -3069,11 +3092,30 @@ function registry_rebuild() {
* to be called, because it is already known that the list of files in the
* {system} table matches those in the file system.
*
+ * @return
+ * TRUE if the registry was rebuilt, FALSE if another thread was rebuilding
+ * in parallel and the current thread just waited for completion.
+ *
* @see registry_rebuild()
*/
function registry_update() {
+ // install_system_module() calls module_enable() which calls into this
+ // function during initial system installation, so the lock system is neither
+ // loaded nor does its storage exist yet.
+ $in_installer = drupal_installation_attempted();
+ if (!$in_installer && !lock_acquire(__FUNCTION__)) {
+ // Another request got the lock, wait for it to finish.
+ lock_wait(__FUNCTION__);
+ return FALSE;
+ }
+
require_once DRUPAL_ROOT . '/includes/registry.inc';
_registry_update();
+
+ if (!$in_installer) {
+ lock_release(__FUNCTION__);
+ }
+ return TRUE;
}
/**
diff --git a/includes/cache.inc b/includes/cache.inc
index eb7f09040..a19d3c38c 100644
--- a/includes/cache.inc
+++ b/includes/cache.inc
@@ -379,11 +379,31 @@ class DrupalDatabaseCache implements DrupalCacheInterface {
* The bin being requested.
*/
protected function garbageCollection() {
- global $user;
+ $cache_lifetime = variable_get('cache_lifetime', 0);
- // Garbage collection necessary when enforcing a minimum cache lifetime.
+ // Clean-up the per-user cache expiration session data, so that the session
+ // handler can properly clean-up the session data for anonymous users.
+ if (isset($_SESSION['cache_expiration'])) {
+ $expire = REQUEST_TIME - $cache_lifetime;
+ foreach ($_SESSION['cache_expiration'] as $bin => $timestamp) {
+ if ($timestamp < $expire) {
+ unset($_SESSION['cache_expiration'][$bin]);
+ }
+ }
+ if (!$_SESSION['cache_expiration']) {
+ unset($_SESSION['cache_expiration']);
+ }
+ }
+
+ // Garbage collection of temporary items is only necessary when enforcing
+ // a minimum cache lifetime.
+ if (!$cache_lifetime) {
+ return;
+ }
+ // When cache lifetime is in force, avoid running garbage collection too
+ // often since this will remove temporary cache items indiscriminately.
$cache_flush = variable_get('cache_flush_' . $this->bin, 0);
- if ($cache_flush && ($cache_flush + variable_get('cache_lifetime', 0) <= REQUEST_TIME)) {
+ if ($cache_flush && ($cache_flush + $cache_lifetime <= REQUEST_TIME)) {
// Reset the variable immediately to prevent a meltdown in heavy load situations.
variable_set('cache_flush_' . $this->bin, 0);
// Time to flush old cache data
@@ -413,17 +433,16 @@ class DrupalDatabaseCache implements DrupalCacheInterface {
if (!isset($cache->data)) {
return FALSE;
}
- // 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 _drupal_session_read()
- // in session.inc. 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) && $user->cache > $cache->created) {
- // This cache data is too old and thus not valid for us, ignore it.
+ // If the cached data is temporary and subject to a per-user minimum
+ // lifetime, compare the cache entry timestamp with the user session
+ // cache_expiration timestamp. If the cache entry is too old, ignore it.
+ if ($cache->expire != CACHE_PERMANENT && variable_get('cache_lifetime', 0) && isset($_SESSION['cache_expiration'][$this->bin]) && $_SESSION['cache_expiration'][$this->bin] > $cache->created) {
+ // Ignore cache data that is too old and thus not valid for this user.
return FALSE;
}
+ // If the data is permanent or not subject to a minimum cache lifetime,
+ // unserialize and return the cached data.
if ($cache->serialized) {
$cache->data = unserialize($cache->data);
}
@@ -468,11 +487,10 @@ class DrupalDatabaseCache implements DrupalCacheInterface {
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 bin by _drupal_session_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 = REQUEST_TIME;
+ // We store the time in the current user's session. We then simulate
+ // that the cache was flushed for this user by not returning cached
+ // data that was cached before the timestamp.
+ $_SESSION['cache_expiration'][$this->bin] = REQUEST_TIME;
$cache_flush = variable_get('cache_flush_' . $this->bin, 0);
if ($cache_flush == 0) {
diff --git a/includes/common.inc b/includes/common.inc
index 43e211813..f3c936e95 100644
--- a/includes/common.inc
+++ b/includes/common.inc
@@ -532,8 +532,8 @@ function drupal_get_destination() {
* Parses a system URL string into an associative array suitable for url().
*
* This function should only be used for URLs that have been generated by the
- * system, resp. url(). It should not be used for URLs that come from external
- * sources, or URLs that link to external resources.
+ * system, such as via url(). It should not be used for URLs that come from
+ * external sources, or URLs that link to external resources.
*
* The returned array contains a 'path' that may be passed separately to url().
* For example:
@@ -1734,7 +1734,8 @@ function format_plural($count, $singular, $plural, array $args = array(), array
// Get the plural index through the gettext formula.
$index = (function_exists('locale_get_plural')) ? locale_get_plural($count, isset($options['langcode']) ? $options['langcode'] : NULL) : -1;
- // Backwards compatibility.
+ // If the index cannot be computed, use the plural as a fallback (which
+ // allows for most flexiblity with the replaceable @count value).
if ($index < 0) {
return t($plural, $args, $options);
}
@@ -2821,7 +2822,7 @@ function drupal_add_html_head_link($attributes, $header = FALSE) {
* - 'group': A number identifying the group in which to add the stylesheet.
* Available constants are:
* - CSS_SYSTEM: Any system-layer CSS.
- * - CSS_DEFAULT: Any module-layer CSS.
+ * - CSS_DEFAULT: (default) Any module-layer CSS.
* - CSS_THEME: Any theme-layer CSS.
* The group number serves as a weight: the markup for loading a stylesheet
* within a lower weight group is output to the page before the markup for
@@ -2969,6 +2970,18 @@ function drupal_get_css($css = NULL, $skip_alter = FALSE) {
// Sort CSS items, so that they appear in the correct order.
uasort($css, 'drupal_sort_css_js');
+ // Provide the page with information about the individual CSS files used,
+ // information not otherwise available when CSS aggregation is enabled. The
+ // setting is attached later in this function, but is set here, so that CSS
+ // files removed below are still considered "used" and prevented from being
+ // added in a later AJAX request.
+ // Skip if no files were added to the page or jQuery.extend() will overwrite
+ // the Drupal.settings.ajaxPageState.css object with an empty array.
+ if (!empty($css)) {
+ // Cast the array to an object to be on the safe side even if not empty.
+ $setting['ajaxPageState']['css'] = (object) array_fill_keys(array_keys($css), 1);
+ }
+
// Remove the overridden CSS files. Later CSS files override former ones.
$previous_item = array();
foreach ($css as $key => $item) {
@@ -2989,10 +3002,9 @@ function drupal_get_css($css = NULL, $skip_alter = FALSE) {
'#items' => $css,
);
- // Provide the page with information about the individual CSS files used,
- // information not otherwise available when CSS aggregation is enabled.
- $setting['ajaxPageState']['css'] = array_fill_keys(array_keys($css), 1);
- $styles['#attached']['js'][] = array('type' => 'setting', 'data' => $setting);
+ if (!empty($setting)) {
+ $styles['#attached']['js'][] = array('type' => 'setting', 'data' => $setting);
+ }
return drupal_render($styles);
}
@@ -3443,7 +3455,13 @@ function drupal_build_css_cache($css) {
$data = '';
$uri = '';
$map = variable_get('drupal_css_cache_files', array());
- $key = hash('sha256', serialize($css));
+ // Create a new array so that only the file names are used to create the hash.
+ // This prevents new aggregates from being created unnecessarily.
+ $css_data = array();
+ foreach ($css as $css_file) {
+ $css_data[] = $css_file['data'];
+ }
+ $key = hash('sha256', serialize($css_data));
if (isset($map[$key])) {
$uri = $map[$key];
}
@@ -4804,7 +4822,13 @@ function drupal_build_js_cache($files) {
$contents = '';
$uri = '';
$map = variable_get('drupal_js_cache_files', array());
- $key = hash('sha256', serialize($files));
+ // Create a new array so that only the file names are used to create the hash.
+ // This prevents new aggregates from being created unnecessarily.
+ $js_data = array();
+ foreach ($files as $file) {
+ $js_data[] = $file['data'];
+ }
+ $key = hash('sha256', serialize($js_data));
if (isset($map[$key])) {
$uri = $map[$key];
}
@@ -5219,8 +5243,6 @@ function drupal_cron_cleanup() {
function drupal_system_listing($mask, $directory, $key = 'name', $min_depth = 1) {
$config = conf_path();
- $profile = drupal_get_profile();
-
$searchdir = array($directory);
$files = array();
@@ -5228,8 +5250,24 @@ function drupal_system_listing($mask, $directory, $key = 'name', $min_depth = 1)
// themes as organized by a distribution. It is pristine in the same way
// that /modules is pristine for core; users should avoid changing anything
// there in favor of sites/all or sites/<domain> directories.
- if (file_exists("profiles/$profile/$directory")) {
- $searchdir[] = "profiles/$profile/$directory";
+ $profiles = array();
+ $profile = drupal_get_profile();
+ // For SimpleTest to be able to test modules packaged together with a
+ // distribution we need to include the profile of the parent site (in which
+ // test runs are triggered).
+ if (drupal_valid_test_ua()) {
+ $testing_profile = variable_get('simpletest_parent_profile', FALSE);
+ if ($testing_profile && $testing_profile != $profile) {
+ $profiles[] = $testing_profile;
+ }
+ }
+ // In case both profile directories contain the same extension, the actual
+ // profile always has precedence.
+ $profiles[] = $profile;
+ foreach ($profiles as $profile) {
+ if (file_exists("profiles/$profile/$directory")) {
+ $searchdir[] = "profiles/$profile/$directory";
+ }
}
// Always search sites/all/* as well as the global directories.
diff --git a/includes/database/database.inc b/includes/database/database.inc
index 6e40b2765..6efe298d2 100644
--- a/includes/database/database.inc
+++ b/includes/database/database.inc
@@ -1430,9 +1430,6 @@ abstract class Database {
/**
* Gets the connection object for the specified database key and target.
*
- * Note: do not use the setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE) on the
- * returned object because of http://bugs.php.net/bug.php?id=43139.
- *
* @param $target
* The database target name.
* @param $key
diff --git a/includes/database/mysql/database.inc b/includes/database/mysql/database.inc
index e024a7f39..7278a2bc8 100644
--- a/includes/database/mysql/database.inc
+++ b/includes/database/mysql/database.inc
@@ -46,8 +46,6 @@ class DatabaseConnection_mysql extends DatabaseConnection {
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => TRUE,
// Because MySQL's prepared statements skip the query cache, because it's dumb.
PDO::ATTR_EMULATE_PREPARES => TRUE,
- // Force column names to lower case.
- PDO::ATTR_CASE => PDO::CASE_LOWER,
);
parent::__construct($dsn, $connection_options['username'], $connection_options['password'], $connection_options['pdo']);
diff --git a/includes/database/mysql/schema.inc b/includes/database/mysql/schema.inc
index 4e88fa169..d6aea4d94 100644
--- a/includes/database/mysql/schema.inc
+++ b/includes/database/mysql/schema.inc
@@ -131,8 +131,13 @@ class DatabaseSchema_mysql extends DatabaseSchema {
protected function createFieldSql($name, $spec) {
$sql = "`" . $name . "` " . $spec['mysql_type'];
- if (in_array($spec['mysql_type'], array('VARCHAR', 'CHAR', 'TINYTEXT', 'MEDIUMTEXT', 'LONGTEXT', 'TEXT')) && isset($spec['length'])) {
- $sql .= '(' . $spec['length'] . ')';
+ if (in_array($spec['mysql_type'], array('VARCHAR', 'CHAR', 'TINYTEXT', 'MEDIUMTEXT', 'LONGTEXT', 'TEXT'))) {
+ if (isset($spec['length'])) {
+ $sql .= '(' . $spec['length'] . ')';
+ }
+ if (!empty($spec['binary'])) {
+ $sql .= ' BINARY';
+ }
}
elseif (isset($spec['precision']) && isset($spec['scale'])) {
$sql .= '(' . $spec['precision'] . ', ' . $spec['scale'] . ')';
@@ -381,7 +386,7 @@ class DatabaseSchema_mysql extends DatabaseSchema {
// Returns one row for each column in the index. Result is string or FALSE.
// Details at http://dev.mysql.com/doc/refman/5.0/en/show-index.html
$row = $this->connection->query('SHOW INDEX FROM {' . $table . "} WHERE key_name = '$name'")->fetchAssoc();
- return isset($row['key_name']);
+ return isset($row['Key_name']);
}
public function addPrimaryKey($table, $fields) {
diff --git a/includes/database/pgsql/database.inc b/includes/database/pgsql/database.inc
index d42a1cc3c..d80b47551 100644
--- a/includes/database/pgsql/database.inc
+++ b/includes/database/pgsql/database.inc
@@ -62,8 +62,6 @@ class DatabaseConnection_pgsql extends DatabaseConnection {
PDO::ATTR_EMULATE_PREPARES => TRUE,
// Convert numeric values to strings when fetching.
PDO::ATTR_STRINGIFY_FETCHES => TRUE,
- // Force column names to lower case.
- PDO::ATTR_CASE => PDO::CASE_LOWER,
);
parent::__construct($dsn, $connection_options['username'], $connection_options['password'], $connection_options['pdo']);
diff --git a/includes/database/pgsql/schema.inc b/includes/database/pgsql/schema.inc
index 9ed8a2620..49adbf907 100644
--- a/includes/database/pgsql/schema.inc
+++ b/includes/database/pgsql/schema.inc
@@ -328,9 +328,9 @@ class DatabaseSchema_pgsql extends DatabaseSchema {
// rename them when renaming the table.
$indexes = $this->connection->query('SELECT indexname FROM pg_indexes WHERE schemaname = :schema AND tablename = :table', array(':schema' => $old_schema, ':table' => $old_table_name));
foreach ($indexes as $index) {
- if (preg_match('/^' . preg_quote($old_full_name) . '_(.*)_idx$/', $index->indexname, $matches)) {
+ if (preg_match('/^' . preg_quote($old_full_name) . '_(.*)$/', $index->indexname, $matches)) {
$index_name = $matches[1];
- $this->connection->query('ALTER INDEX ' . $index->indexname . ' RENAME TO {' . $new_name . '}_' . $index_name . '_idx');
+ $this->connection->query('ALTER INDEX ' . $index->indexname . ' RENAME TO {' . $new_name . '}_' . $index_name);
}
}
diff --git a/includes/database/query.inc b/includes/database/query.inc
index 6020b0ea5..750aea7a0 100644
--- a/includes/database/query.inc
+++ b/includes/database/query.inc
@@ -1898,7 +1898,7 @@ class DatabaseCondition implements QueryConditionInterface, Countable {
function __clone() {
$this->changed = TRUE;
foreach ($this->conditions as $key => $condition) {
- if ($condition['field'] instanceOf QueryConditionInterface) {
+ if ($key !== '#conjunction' && $condition['field'] instanceOf QueryConditionInterface) {
$this->conditions[$key]['field'] = clone($condition['field']);
}
}
diff --git a/includes/database/schema.inc b/includes/database/schema.inc
index e2a1c4caa..d3943b29b 100644
--- a/includes/database/schema.inc
+++ b/includes/database/schema.inc
@@ -76,8 +76,13 @@ require_once dirname(__FILE__) . '/query.inc';
* the precision (total number of significant digits) and scale
* (decimal digits right of the decimal point). Both values are
* mandatory. Ignored for other field types.
+ * - 'binary': A boolean indicating that MySQL should force 'char',
+ * 'varchar' or 'text' fields to use case-sensitive binary collation.
+ * This has no effect on other database types for which case sensitivity
+ * is already the default behavior.
* All parameters apart from 'type' are optional except that type
- * 'numeric' columns must specify 'precision' and 'scale'.
+ * 'numeric' columns must specify 'precision' and 'scale', and type
+ * 'varchar' must specify the 'length' parameter.
* - 'primary key': An array of one or more key column specifiers (see below)
* that form the primary key.
* - 'unique keys': An associative array of unique keys ('keyname' =>
diff --git a/includes/database/sqlite/database.inc b/includes/database/sqlite/database.inc
index b4e41b527..ea91e9143 100644
--- a/includes/database/sqlite/database.inc
+++ b/includes/database/sqlite/database.inc
@@ -37,7 +37,7 @@ class DatabaseConnection_sqlite extends DatabaseConnection {
/**
* All databases attached to the current database. This is used to allow
* prefixes to be safely handled without locking the table
- *
+ *
* @var array
*/
protected $attachedDatabases = array();
@@ -46,10 +46,10 @@ class DatabaseConnection_sqlite extends DatabaseConnection {
* Whether or not a table has been dropped this request: the destructor will
* only try to get rid of unnecessary databases if there is potential of them
* being empty.
- *
+ *
* This variable is set to public because DatabaseSchema_sqlite needs to
* access it. However, it should not be manually set.
- *
+ *
* @var boolean
*/
var $tableDropped = FALSE;
@@ -68,8 +68,6 @@ class DatabaseConnection_sqlite extends DatabaseConnection {
'pdo' => array(),
);
$connection_options['pdo'] += array(
- // Force column names to lower case.
- PDO::ATTR_CASE => PDO::CASE_LOWER,
// Convert numeric values to strings when fetching.
PDO::ATTR_STRINGIFY_FETCHES => TRUE,
);
diff --git a/includes/date.inc b/includes/date.inc
index 27634f9e3..01ab131b3 100644
--- a/includes/date.inc
+++ b/includes/date.inc
@@ -2,7 +2,7 @@
/**
* @file
- * Initialize the list of date formats and their locales.
+ * Initializes the list of date formats and their locales.
*/
/**
diff --git a/includes/entity.inc b/includes/entity.inc
index 07ee06162..ae7807794 100644
--- a/includes/entity.inc
+++ b/includes/entity.inc
@@ -468,7 +468,7 @@ class EntityFieldQuery {
* @var array
*
* @see EntityFieldQuery::fieldLanguageCondition()
- * @see EntityFieldQuery::fielDeltaCondition()
+ * @see EntityFieldQuery::fieldDeltaCondition()
*/
public $fieldMetaConditions = array();
@@ -1106,12 +1106,13 @@ class EntityFieldQuery {
* contains everything necessary for processing.
*
* @return
- * Either a number if count() was called or an array of associative
- * arrays of stub entities. The outer array keys are entity types, and the
- * inner array keys are the relevant ID. (In most this cases this will be
- * the entity ID. The only exception is when age=FIELD_LOAD_REVISION is used
- * and field conditions or sorts are present -- in this case, the key will
- * be the revision ID.) The inner array values are always stub entities, as
+ * Either a number if count() was called or an array of associative arrays
+ * of stub entities. The outer array keys are entity types, and the inner
+ * array keys are the relevant ID. (In most cases this will be the entity
+ * ID. The only exception is when age=FIELD_LOAD_REVISION is used and field
+ * conditions or sorts are present -- in this case, the key will be the
+ * revision ID.) The entity type will only exist in the outer array if
+ * results were found. The inner array values are always stub entities, as
* returned by entity_create_stub_entity(). To traverse the returned array:
* @code
* foreach ($query->execute() as $entity_type => $entities) {
@@ -1121,7 +1122,9 @@ class EntityFieldQuery {
* the entities found:
* @code
* $result = $query->execute();
- * $entities = entity_load($my_type, array_keys($result[$my_type]));
+ * if (!empty($result[$my_type])) {
+ * $entities = entity_load($my_type, array_keys($result[$my_type]));
+ * }
* @endcode
*/
public function execute() {
@@ -1260,12 +1263,11 @@ class EntityFieldQuery {
/**
* Get the total number of results and initialize a pager for the query.
*
- * This query can be detected by checking for ($this->pager && $this->count),
- * which allows a driver to return 0 from the count query and disable
- * the pager.
+ * The pager can be disabled by either setting the pager limit to 0, or by
+ * setting this query to be a count query.
*/
function initializePager() {
- if ($this->pager && !$this->count) {
+ if ($this->pager && !empty($this->pager['limit']) && !$this->count) {
$page = pager_find_page($this->pager['element']);
$count_query = clone $this;
$this->pager['total'] = $count_query->count()->execute();
diff --git a/includes/errors.inc b/includes/errors.inc
index cb708d860..f62bf06a5 100644
--- a/includes/errors.inc
+++ b/includes/errors.inc
@@ -2,7 +2,7 @@
/**
* @file
- * Functions for error handling
+ * Functions for error handling.
*/
/**
@@ -21,7 +21,8 @@ define('ERROR_REPORTING_DISPLAY_SOME', 1);
define('ERROR_REPORTING_DISPLAY_ALL', 2);
/**
- * Map PHP error constants to watchdog severity levels.
+ * Maps PHP error constants to watchdog severity levels.
+ *
* The error constants are documented at
* http://php.net/manual/en/errorfunc.constants.php
*
@@ -52,7 +53,7 @@ function drupal_error_levels() {
}
/**
- * Custom PHP error handler.
+ * Provides custom PHP error handling.
*
* @param $error_level
* The level of the error raised.
@@ -63,7 +64,8 @@ function drupal_error_levels() {
* @param $line
* The line number the error was raised at.
* @param $context
- * An array that points to the active symbol table at the point the error occurred.
+ * An array that points to the active symbol table at the point the error
+ * occurred.
*/
function _drupal_error_handler_real($error_level, $message, $filename, $line, $context) {
if ($error_level & error_reporting()) {
@@ -90,10 +92,11 @@ function _drupal_error_handler_real($error_level, $message, $filename, $line, $c
}
/**
- * Decode an exception, especially to retrive the correct caller.
+ * Decodes an exception and retrieves the correct caller.
*
* @param $exception
* The exception object that was thrown.
+ *
* @return
* An error in the format expected by _drupal_log_error().
*/
@@ -136,7 +139,7 @@ function _drupal_decode_exception($exception) {
}
/**
- * Render an error message for an exception without any possibility of a further exception occurring.
+ * Renders an exception error message without further exceptions.
*
* @param $exception
* The exception object that was thrown.
@@ -171,12 +174,12 @@ function error_displayable($error = NULL) {
}
/**
- * Log a PHP error or exception, display an error page in fatal cases.
+ * Logs a PHP error or exception and displays an error page in fatal cases.
*
* @param $error
* An array with the following keys: %type, !message, %function, %file, %line
- * and severity_level. All the parameters are plain-text, with the exception of
- * !message, which needs to be a safe HTML string.
+ * and severity_level. All the parameters are plain-text, with the exception
+ * of !message, which needs to be a safe HTML string.
* @param $fatal
* TRUE if the error is fatal.
*/
@@ -263,6 +266,7 @@ function _drupal_log_error($error, $fatal = FALSE) {
*
* @param $backtrace
* A standard PHP backtrace.
+ *
* @return
* An associative array with keys 'file', 'line' and 'function'.
*/
diff --git a/includes/file.inc b/includes/file.inc
index 676b32f15..c5e5cf07d 100644
--- a/includes/file.inc
+++ b/includes/file.inc
@@ -70,11 +70,7 @@ define('FILE_EXISTS_ERROR', 2);
define('FILE_STATUS_PERMANENT', 1);
/**
- * Methods to manage a registry of stream wrappers.
- */
-
-/**
- * Drupal stream wrapper registry.
+ * Provides Drupal stream wrapper registry.
*
* A stream wrapper is an abstraction of a file system that allows Drupal to
* use the same set of methods to access both local files and remote resources.
@@ -206,7 +202,7 @@ function file_uri_scheme($uri) {
}
/**
- * Check that the scheme of a stream URI is valid.
+ * Checks that the scheme of a stream URI is valid.
*
* Confirms that there is a registered stream handler for the provided scheme
* and that it is callable. This is useful if you want to confirm a valid
@@ -232,7 +228,7 @@ function file_stream_wrapper_valid_scheme($scheme) {
/**
- * Returns the part of an URI after the schema.
+ * Returns the part of a URI after the schema.
*
* @param $uri
* A stream, referenced as "scheme://target".
@@ -252,7 +248,7 @@ function file_uri_target($uri) {
}
/**
- * Get the default file stream implementation.
+ * Gets the default file stream implementation.
*
* @return
* 'public', 'private' or any other file scheme defined as the default.
@@ -322,7 +318,7 @@ function file_stream_wrapper_get_instance_by_uri($uri) {
}
/**
- * Returns a reference to the stream wrapper class responsible for a given scheme.
+ * Returns a reference to the stream wrapper class responsible for a scheme.
*
* This helper method returns a stream instance using a scheme. That is, the
* passed string does not contain a "://". For example, "public" is a scheme
@@ -357,7 +353,6 @@ function file_stream_wrapper_get_instance_by_scheme($scheme) {
* Creates a web-accessible URL for a stream to an external or local file.
*
* Compatibility: normal paths and stream wrappers.
- * @see http://drupal.org/node/515192
*
* There are two kinds of local files:
* - "managed files", i.e. those stored by a Drupal-compatible stream wrapper.
@@ -375,6 +370,8 @@ function file_stream_wrapper_get_instance_by_scheme($scheme) {
* If the provided string already contains a preceding 'http', 'https', or
* '/', nothing is done and the same string is returned. If a stream wrapper
* could not be found to generate an external URL, then FALSE is returned.
+ *
+ * @see http://drupal.org/node/515192
*/
function file_create_url($uri) {
// Allow the URI to be altered, e.g. to serve a file from a CDN or static
@@ -417,7 +414,7 @@ function file_create_url($uri) {
}
/**
- * Check that the directory exists and is writable.
+ * Checks that the directory exists and is writable.
*
* Directories need to have execute permissions to be considered a directory by
* FTP servers, etc.
@@ -459,7 +456,7 @@ function file_prepare_directory(&$directory, $options = FILE_MODIFY_PERMISSIONS)
}
/**
- * If missing, create a .htaccess file in each Drupal files directory.
+ * Creates a .htaccess file in each Drupal files directory if it is missing.
*/
function file_ensure_htaccess() {
file_create_htaccess('public://', FALSE);
@@ -470,7 +467,7 @@ function file_ensure_htaccess() {
}
/**
- * Creates an .htaccess file in the given directory.
+ * Creates a .htaccess file in the given directory.
*
* @param $directory
* The directory.
@@ -526,25 +523,25 @@ function file_create_htaccess($directory, $private = TRUE) {
* @return
* An array of file objects, indexed by fid.
*
+ * @todo Remove $conditions in Drupal 8.
+ *
* @see hook_file_load()
* @see file_load()
* @see entity_load()
* @see EntityFieldQuery
- *
- * @todo Remove $conditions in Drupal 8.
*/
function file_load_multiple($fids = array(), $conditions = array()) {
return entity_load('file', $fids, $conditions);
}
/**
- * Load a file object from the database.
+ * Loads a single file object from the database.
*
* @param $fid
* A file ID.
*
* @return
- * A file object.
+ * An object representing the file, or FALSE if the file was not found.
*
* @see hook_file_load()
* @see file_load_multiple()
@@ -555,7 +552,7 @@ function file_load($fid) {
}
/**
- * Save a file object to the database.
+ * Saves a file object to the database.
*
* If the $file->fid is not set a new record will be added.
*
@@ -797,7 +794,7 @@ function file_copy(stdClass $source, $destination = NULL, $replace = FILE_EXISTS
}
/**
- * Determine whether the URI has a valid scheme for file API operations.
+ * Determines whether the URI has a valid scheme for file API operations.
*
* There must be a scheme and it must be a Drupal-provided scheme like
* 'public', 'private', 'temporary', or an extension provided with
@@ -926,7 +923,7 @@ function file_unmanaged_copy($source, $destination = NULL, $replace = FILE_EXIST
}
/**
- * Given a relative path, construct a URI into Drupal's default files location.
+ * Constructs a URI to Drupal's default files location given a relative path.
*/
function file_build_uri($path) {
$uri = file_default_scheme() . '://' . $path;
@@ -934,8 +931,7 @@ function file_build_uri($path) {
}
/**
- * Determines the destination path for a file depending on how replacement of
- * existing files should be handled.
+ * Determines the destination path for a file.
*
* @param $destination
* A string specifying the desired final URI or filepath.
@@ -972,7 +968,7 @@ function file_destination($destination, $replace) {
}
/**
- * Move a file to a new location and update the file's database entry.
+ * Moves a file to a new location and update the file's database entry.
*
* Moving a file is performed by copying the file to the new location and then
* deleting the original.
@@ -1052,8 +1048,7 @@ function file_move(stdClass $source, $destination = NULL, $replace = FILE_EXISTS
}
/**
- * Move a file to a new location without calling any hooks or making any
- * changes to the database.
+ * Moves a file to a new location without database changes or hook invocation.
*
* @param $source
* A string specifying the filepath or URI of the original file.
@@ -1082,7 +1077,7 @@ function file_unmanaged_move($source, $destination = NULL, $replace = FILE_EXIST
}
/**
- * Modify a filename as needed for security purposes.
+ * Modifies a filename as needed for security purposes.
*
* Munging a file name prevents unknown file extensions from masking exploit
* files. When web servers such as Apache decide how to process a URL request,
@@ -1146,7 +1141,7 @@ function file_munge_filename($filename, $extensions, $alerts = TRUE) {
}
/**
- * Undo the effect of file_munge_filename().
+ * Undoes the effect of file_munge_filename().
*
* @param $filename
* String with the filename to be unmunged.
@@ -1159,7 +1154,7 @@ function file_unmunge_filename($filename) {
}
/**
- * Create a full file path from a directory and filename.
+ * Creates a full file path from a directory and filename.
*
* If a file with the specified name already exists, an alternative will be
* used.
@@ -1214,7 +1209,7 @@ function file_create_filename($basename, $directory) {
}
/**
- * Delete a file and its database record.
+ * Deletes a file and its database record.
*
* If the $force parameter is not TRUE, file_usage_list() will be called to
* determine if the file is being used by any modules. If the file is being
@@ -1269,8 +1264,7 @@ function file_delete(stdClass $file, $force = FALSE) {
}
/**
- * Delete a file without calling any hooks or making any changes to the
- * database.
+ * Deletes a file without database changes or hook invocations.
*
* This function should be used when the file to be deleted does not have an
* entry recorded in the files table.
@@ -1306,7 +1300,7 @@ function file_unmanaged_delete($path) {
}
/**
- * Recursively delete all files and directories in the specified filepath.
+ * Deletes all files and directories in the specified filepath recursively.
*
* If the specified path is a directory then the function will call itself
* recursively to process the contents. Once the contents have been removed the
@@ -1344,7 +1338,7 @@ function file_unmanaged_delete_recursive($path) {
}
/**
- * Determine total disk space used by a single user or the whole filesystem.
+ * Determines total disk space used by a single user or the whole filesystem.
*
* @param $uid
* Optional. A user id, specifying NULL returns the total space used by all
@@ -1581,7 +1575,6 @@ function file_save_upload($source, $validators = array(), $destination = FALSE,
* or open_basedir are enabled, so this function fills that gap.
*
* Compatibility: normal paths and stream wrappers.
- * @see http://drupal.org/node/515192
*
* @param $filename
* The filename of the uploaded file.
@@ -1592,6 +1585,7 @@ function file_save_upload($source, $validators = array(), $destination = FALSE,
* TRUE on success, or FALSE on failure.
*
* @see move_uploaded_file()
+ * @see http://drupal.org/node/515192
* @ingroup php_wrappers
*/
function drupal_move_uploaded_file($filename, $uri) {
@@ -1612,7 +1606,7 @@ function drupal_move_uploaded_file($filename, $uri) {
}
/**
- * Check that a file meets the criteria specified by the validators.
+ * Checks that a file meets the criteria specified by the validators.
*
* After executing the validator callbacks specified hook_file_validate() will
* also be called to allow other modules to report errors about the file.
@@ -1647,10 +1641,11 @@ function file_validate(stdClass &$file, $validators = array()) {
}
/**
- * Check for files with names longer than we can store in the database.
+ * Checks for files with names longer than we can store in the database.
*
* @param $file
* A Drupal file object.
+ *
* @return
* An array. If the file name is too long, it will contain an error message.
*/
@@ -1667,7 +1662,7 @@ function file_validate_name_length(stdClass $file) {
}
/**
- * Check that the filename ends with an allowed extension.
+ * Checks that the filename ends with an allowed extension.
*
* @param $file
* A Drupal file object.
@@ -1691,7 +1686,7 @@ function file_validate_extensions(stdClass $file, $extensions) {
}
/**
- * Check that the file's size is below certain limits.
+ * Checks that the file's size is below certain limits.
*
* This check is not enforced for the user #1.
*
@@ -1730,7 +1725,7 @@ function file_validate_size(stdClass $file, $file_limit = 0, $user_limit = 0) {
}
/**
- * Check that the file is recognized by image_get_info() as an image.
+ * Checks that the file is recognized by image_get_info() as an image.
*
* @param $file
* A Drupal file object.
@@ -1752,7 +1747,7 @@ function file_validate_is_image(stdClass $file) {
}
/**
- * Verify that image dimensions are within the specified maximum and minimum.
+ * Verifies that image dimensions are within the specified maximum and minimum.
*
* Non-image files will be ignored. If a image toolkit is available the image
* will be scaled to fit within the desired maximum dimensions.
@@ -1810,7 +1805,7 @@ function file_validate_image_resolution(stdClass $file, $maximum_dimensions = 0,
}
/**
- * Save a string to the specified destination and create a database file entry.
+ * Saves a file to the specified destination and creates a database entry.
*
* @param $data
* A string containing the contents of the file.
@@ -1874,7 +1869,7 @@ function file_save_data($data, $destination = NULL, $replace = FILE_EXISTS_RENAM
}
/**
- * Save a string to the specified destination without invoking file API.
+ * Saves a string to the specified destination without invoking file API.
*
* This function is identical to file_save_data() except the file will not be
* saved to the {file_managed} table and none of the file_* hooks will be
@@ -1885,7 +1880,8 @@ function file_save_data($data, $destination = NULL, $replace = FILE_EXISTS_RENAM
* @param $destination
* A string containing the destination location. This must be a stream wrapper
* URI. If no value is provided, a randomized name will be generated and the
- * file will be saved using Drupal's default files scheme, usually "public://".
+ * file will be saved using Drupal's default files scheme, usually
+ * "public://".
* @param $replace
* Replace behavior when the destination file already exists:
* - FILE_EXISTS_REPLACE - Replace the existing file.
@@ -1911,7 +1907,7 @@ function file_unmanaged_save_data($data, $destination = NULL, $replace = FILE_EX
}
/**
- * Transfer file using HTTP to client.
+ * Transfers a file to the client using HTTP.
*
* Pipes a file through Drupal to the client.
*
@@ -1953,7 +1949,7 @@ function file_transfer($uri, $headers) {
* exists but no modules responded drupal_access_denied() will be returned.
* If the file does not exist drupal_not_found() will be returned.
*
- * @see hook_file_download()
+ * @see system_menu()
*/
function file_download() {
// Merge remainder of arguments from GET['q'], into relative file path.
@@ -2068,7 +2064,7 @@ function file_scan_directory($dir, $mask, $options = array(), $depth = 0) {
}
/**
- * Determine the maximum file upload size by querying the PHP settings.
+ * Determines the maximum file upload size by querying the PHP settings.
*
* @return
* A file size limit in bytes based on the PHP upload_max_filesize and
@@ -2092,7 +2088,7 @@ function file_upload_max_size() {
}
/**
- * Determine an Internet Media Type, or MIME type from a filename.
+ * Determines an Internet Media Type or MIME type from a filename.
*
* @param $uri
* A string containing the URI, path, or filename.
@@ -2121,7 +2117,7 @@ function file_get_mimetype($uri, $mapping = NULL) {
}
/**
- * Set the permissions on a file or directory.
+ * Sets the permissions on a file or directory.
*
* This function will use the 'file_chmod_directory' and 'file_chmod_file'
* variables for the default modes for directories and uploaded/generated
@@ -2227,10 +2223,11 @@ function drupal_unlink($uri, $context = NULL) {
* The absolute local filesystem path (with no symbolic links), or FALSE on
* failure.
*
+ * @todo This function is deprecated, and should be removed wherever possible.
+ *
* @see DrupalStreamWrapperInterface::realpath()
* @see http://php.net/manual/function.realpath.php
* @ingroup php_wrappers
- * @todo: This function is deprecated, and should be removed wherever possible.
*/
function drupal_realpath($uri) {
// If this URI is a stream, pass it off to the appropriate stream wrapper.
@@ -2257,7 +2254,6 @@ function drupal_realpath($uri) {
* PHP's dirname() as a fallback.
*
* Compatibility: normal paths and stream wrappers.
- * @see http://drupal.org/node/515192
*
* @param $uri
* A URI or path.
@@ -2266,6 +2262,7 @@ function drupal_realpath($uri) {
* A string containing the directory name.
*
* @see dirname()
+ * @see http://drupal.org/node/515192
* @ingroup php_wrappers
*/
function drupal_dirname($uri) {
@@ -2315,7 +2312,6 @@ function drupal_basename($uri, $suffix = NULL) {
* is not provided, this function will make sure that Drupal's is used.
*
* Compatibility: normal paths and stream wrappers.
- * @see http://drupal.org/node/515192
*
* @param $uri
* A URI or pathname.
@@ -2330,6 +2326,7 @@ function drupal_basename($uri, $suffix = NULL) {
* Boolean TRUE on success, or FALSE on failure.
*
* @see mkdir()
+ * @see http://drupal.org/node/515192
* @ingroup php_wrappers
*/
function drupal_mkdir($uri, $mode = NULL, $recursive = FALSE, $context = NULL) {
@@ -2346,7 +2343,7 @@ function drupal_mkdir($uri, $mode = NULL, $recursive = FALSE, $context = NULL) {
}
/**
- * Remove a directory.
+ * Removes a directory.
*
* PHP's rmdir() is broken on Windows, as it can fail to remove a directory
* when it has a read-only flag set.
@@ -2383,7 +2380,6 @@ function drupal_rmdir($uri, $context = NULL) {
* given a filepath.
*
* Compatibility: normal paths and stream wrappers.
- * @see http://drupal.org/node/515192
*
* @param $directory
* The directory where the temporary filename will be created.
@@ -2395,6 +2391,7 @@ function drupal_rmdir($uri, $context = NULL) {
* The new temporary filename, or FALSE on failure.
*
* @see tempnam()
+ * @see http://drupal.org/node/515192
* @ingroup php_wrappers
*/
function drupal_tempnam($directory, $prefix) {
@@ -2417,7 +2414,7 @@ function drupal_tempnam($directory, $prefix) {
}
/**
- * Get the path of system-appropriate temporary directory.
+ * Gets the path of system-appropriate temporary directory.
*/
function file_directory_temp() {
$temporary_directory = variable_get('file_temporary_path', NULL);
@@ -2474,6 +2471,7 @@ function file_directory_temp() {
*
* @param $file
* A file object.
+ *
* @return
* An associative array of headers, as expected by file_transfer().
*/
diff --git a/includes/form.inc b/includes/form.inc
index 3d5f6f22e..3ef32ab41 100644
--- a/includes/form.inc
+++ b/includes/form.inc
@@ -1,4 +1,8 @@
<?php
+ /**
+ * @file
+ * Functions for form and batch generation and processing.
+ */
/**
* @defgroup forms Form builder functions
@@ -90,7 +94,11 @@
*/
/**
- * Wrapper for drupal_build_form() for use when $form_state is not needed.
+ * Returns a renderable form array for a given form ID.
+ *
+ * This function should be used instead of drupal_build_form() when $form_state
+ * is not needed (i.e., when initially rendering the form) and is often
+ * used as a menu callback.
*
* @param $form_id
* The unique string identifying the desired form. If a function with that
@@ -98,7 +106,7 @@
* generate the same form (or very similar forms) using different $form_ids
* can implement hook_forms(), which maps different $form_id values to the
* proper form constructor function. Examples may be found in node_forms(),
- * search_forms(), and user_forms().
+ * and search_forms().
* @param ...
* Any additional arguments are passed on to the functions called by
* drupal_get_form(), including the unique form constructor function. For
@@ -124,7 +132,7 @@ function drupal_get_form($form_id) {
}
/**
- * Build and process a form based on a form id.
+ * Builds and process a form based on a form id.
*
* The form may also be retrieved from the cache if the form was built in a
* previous page-load. The form is then passed on for processing, validation
@@ -136,7 +144,7 @@ function drupal_get_form($form_id) {
* generate the same form (or very similar forms) using different $form_ids
* can implement hook_forms(), which maps different $form_id values to the
* proper form constructor function. Examples may be found in node_forms(),
- * search_forms(), and user_forms().
+ * and search_forms().
* @param $form_state
* An array which stores information about the form. This is passed as a
* reference so that the caller can use it to examine what in the form changed
@@ -207,8 +215,8 @@ function drupal_get_form($form_id) {
* validation functions and submit functions use this array for nearly all
* their decision making. (Note that
* @link http://api.drupal.org/api/drupal/developer--topics--forms_api_reference.html/7#tree #tree @endlink
- * determines whether the values are a flat array or an array whose structure
- * parallels the $form array.)
+ * determines whether the values are a flat array or an array whose
+ * structure parallels the $form array.)
* - input: The array of values as they were submitted by the user. These are
* raw and unvalidated, so should not be used without a thorough
* understanding of security implications. In almost all cases, code should
@@ -377,7 +385,7 @@ function drupal_build_form($form_id, &$form_state) {
}
/**
- * Retrieve default values for the $form_state array.
+ * Retrieves default values for the $form_state array.
*/
function form_state_defaults() {
return array(
@@ -422,7 +430,7 @@ function form_state_defaults() {
* Modules that need to generate the same form (or very similar forms)
* using different $form_ids can implement hook_forms(), which maps
* different $form_id values to the proper form constructor function. Examples
- * may be found in node_forms(), search_forms(), and user_forms().
+ * may be found in node_forms() and search_forms().
* @param $form_state
* A keyed array containing the current state of the form.
* @param $old_form
@@ -483,7 +491,7 @@ function drupal_rebuild_form($form_id, &$form_state, $old_form = NULL) {
}
/**
- * Fetch a form from cache.
+ * Fetches a form from cache.
*/
function form_get_cache($form_build_id, &$form_state) {
if ($cached = cache_get('form_' . $form_build_id, 'cache_form')) {
@@ -514,7 +522,7 @@ function form_get_cache($form_build_id, &$form_state) {
}
/**
- * Store a form in the cache.
+ * Stores a form in the cache.
*/
function form_set_cache($form_build_id, $form, $form_state) {
// 6 hours cache life time for forms should be plenty.
@@ -564,7 +572,7 @@ function form_state_keys_no_cache() {
}
/**
- * Loads an include file and makes sure it is loaded whenever the form is processed.
+ * Ensures an include file is loaded loaded whenever the form is processed.
*
* Example:
* @code
@@ -628,7 +636,7 @@ function form_load_include(&$form_state, $type, $module, $name = NULL) {
* Modules that need to generate the same form (or very similar forms)
* using different $form_ids can implement hook_forms(), which maps
* different $form_id values to the proper form constructor function. Examples
- * may be found in node_forms(), search_forms(), and user_forms().
+ * may be found in node_forms() and search_forms().
* @param $form_state
* A keyed array containing the current state of the form. Most important is
* the $form_state['values'] collection, a tree of data used to simulate the
@@ -930,9 +938,10 @@ function drupal_process_form($form_id, &$form, &$form_state) {
}
/**
- * Prepares a structured form array by adding required elements,
- * executing any hook_form_alter functions, and optionally inserting
- * a validation token to prevent tampering.
+ * Prepares a structured form array.
+ *
+ * Adds required elements, executes any hook_form_alter functions, and
+ * optionally inserts a validation token to prevent tampering.
*
* @param $form_id
* A unique string identifying the form for validation, submission,
@@ -1060,8 +1069,7 @@ function drupal_prepare_form($form_id, &$form, &$form_state) {
/**
- * Validates user-submitted form data from the $form_state using
- * the validate functions defined in a structured form array.
+ * Validates user-submitted form data in the $form_state array.
*
* @param $form_id
* A unique string identifying the form for validation, submission,
@@ -1235,9 +1243,11 @@ function drupal_redirect_form($form_state) {
}
/**
- * Performs validation on form elements. First ensures required fields are
- * completed, #maxlength is not exceeded, and selected options were in the
- * list of options given to the user. Then calls user-defined validators.
+ * Performs validation on form elements.
+ *
+ * First ensures required fields are completed, #maxlength is not exceeded, and
+ * selected options were in the list of options given to the user. Then calls
+ * user-defined validators.
*
* @param $elements
* An associative array containing the structure of the form.
@@ -1389,9 +1399,10 @@ function _form_validate(&$elements, &$form_state, $form_id = NULL) {
}
/**
- * A helper function used to execute custom validation and submission
- * handlers for a given form. Button-specific handlers are checked
- * first. If none exist, the function falls back to form-level handlers.
+ * Executes custom validation and submission handlers for a given form.
+ *
+ * Button-specific handlers are checked first. If none exist, the function
+ * falls back to form-level handlers.
*
* @param $type
* The type of handler to execute. 'validate' or 'submit' are the
@@ -1513,8 +1524,6 @@ function form_execute_handlers($type, &$form, &$form_state) {
* doing anything with that data that requires it to be valid, PHP errors
* would be triggered if the input processing and validation steps were fully
* skipped.
- * @see http://drupal.org/node/370537
- * @see http://drupal.org/node/763376
*
* @param $name
* The name of the form element. If the #parents property of your form
@@ -1530,6 +1539,9 @@ function form_execute_handlers($type, &$form, &$form_state) {
* @return
* Return value is for internal use only. To get a list of errors, use
* form_get_errors() or form_get_error().
+ *
+ * @see http://drupal.org/node/370537
+ * @see http://drupal.org/node/763376
*/
function form_set_error($name = NULL, $message = '', $limit_validation_errors = NULL) {
$form = &drupal_static(__FUNCTION__, array());
@@ -1575,14 +1587,14 @@ function form_set_error($name = NULL, $message = '', $limit_validation_errors =
}
/**
- * Clear all errors against all form elements made by form_set_error().
+ * Clears all errors against all form elements made by form_set_error().
*/
function form_clear_error() {
drupal_static_reset('form_set_error');
}
/**
- * Return an associative array of all errors.
+ * Returns an associative array of all errors.
*/
function form_get_errors() {
$form = form_set_error();
@@ -1610,16 +1622,18 @@ function form_get_error($element) {
}
/**
- * Flag an element as having an error.
+ * Flags an element as having an error.
*/
function form_error(&$element, $message = '') {
form_set_error(implode('][', $element['#parents']), $message);
}
/**
- * Walk through the structured form array, adding any required properties to
- * each element and mapping the incoming input data to the proper elements.
- * Also, execute any #process handlers attached to a specific element.
+ * Builds and processes all elements in the structured form array.
+ *
+ * Adds any required properties to each element, maps the incoming input data
+ * to the proper elements, and executes any #process handlers attached to a
+ * specific element.
*
* This is one of the three primary functions that recursively iterates a form
* array. This one does it for completing the form building process. The other
@@ -1891,8 +1905,7 @@ function form_builder($form_id, &$element, &$form_state) {
}
/**
- * Populate the #value and #name properties of input elements so they
- * can be processed and rendered.
+ * Adds the #name and #value properties of an input element before rendering.
*/
function _form_builder_handle_input_element($form_id, &$element, &$form_state) {
if (!isset($element['#name'])) {
@@ -2031,7 +2044,7 @@ function _form_builder_handle_input_element($form_id, &$element, &$form_state) {
}
/**
- * Helper function to handle the convoluted logic of button click detection.
+ * Detects if an element triggered the form submission via Ajax.
*
* This detects button or non-button controls that trigger a form submission via
* Ajax or some other scriptable environment. These environments can set the
@@ -2051,7 +2064,7 @@ function _form_element_triggered_scripted_submission($element, &$form_state) {
}
/**
- * Helper function to handle the convoluted logic of button click detection.
+ * Determines if a given button triggered the form submission.
*
* This detects button controls that trigger a form submission by being clicked
* and having the click processed by the browser rather than being captured by
@@ -2146,7 +2159,7 @@ function form_state_values_clean(&$form_state) {
}
/**
- * Helper function to determine the value for an image button form element.
+ * Determines the value for an image button form element.
*
* @param $form
* The form element whose value is being populated.
@@ -2155,6 +2168,7 @@ function form_state_values_clean(&$form_state) {
* the element's default value should be returned.
* @param $form_state
* A keyed array containing the current state of the form.
+ *
* @return
* The data that will appear in the $form_state['values'] collection
* for this element. Return nothing to use the default.
@@ -2193,13 +2207,14 @@ function form_type_image_button_value($form, $input, $form_state) {
}
/**
- * Helper function to determine the value for a checkbox form element.
+ * Determines the value for a checkbox form element.
*
* @param $form
* The form element whose value is being populated.
-* @param $input
+ * @param $input
* The incoming input to populate the form element. If this is FALSE,
* the element's default value should be returned.
+ *
* @return
* The data that will appear in the $element_state['values'] collection
* for this element. Return nothing to use the default.
@@ -2233,13 +2248,14 @@ function form_type_checkbox_value($element, $input = FALSE) {
}
/**
- * Helper function to determine the value for a checkboxes form element.
+ * Determines the value for a checkboxes form element.
*
* @param $element
* The form element whose value is being populated.
* @param $input
* The incoming input to populate the form element. If this is FALSE,
* the element's default value should be returned.
+ *
* @return
* The data that will appear in the $element_state['values'] collection
* for this element. Return nothing to use the default.
@@ -2273,13 +2289,14 @@ function form_type_checkboxes_value($element, $input = FALSE) {
}
/**
- * Helper function to determine the value for a tableselect form element.
+ * Determines the value for a tableselect form element.
*
* @param $element
* The form element whose value is being populated.
* @param $input
* The incoming input to populate the form element. If this is FALSE,
* the element's default value should be returned.
+ *
* @return
* The data that will appear in the $element_state['values'] collection
* for this element. Return nothing to use the default.
@@ -2308,14 +2325,56 @@ function form_type_tableselect_value($element, $input = FALSE) {
}
/**
- * Helper function to determine the value for a password_confirm form
- * element.
+ * Form value callback: Determines the value for a #type radios form element.
+ *
+ * @param $element
+ * The form element whose value is being populated.
+ * @param $input
+ * (optional) The incoming input to populate the form element. If FALSE, the
+ * element's default value is returned. Defaults to FALSE.
+ *
+ * @return
+ * The data that will appear in the $element_state['values'] collection for
+ * this element.
+ */
+function form_type_radios_value(&$element, $input = FALSE) {
+ if ($input !== FALSE) {
+ // There may not be a submitted value for multiple radio buttons, if none of
+ // the options was checked by default. If there is no submitted input value
+ // for this element (NULL), _form_builder_handle_input_element()
+ // automatically attempts to use the #default_value (if set) or an empty
+ // string (''). However, an empty string would fail validation in
+ // _form_validate(), in case it is not contained in the list of allowed
+ // values in #options.
+ if (!isset($input)) {
+ // Signify a garbage value to disable the #default_value handling and take
+ // over NULL as #value.
+ $element['#has_garbage_value'] = TRUE;
+ // There was a user submission so validation is a must. If this element is
+ // #required, then an appropriate error message will be output. While an
+ // optional #type 'radios' does not necessarily make sense from a user
+ // interaction perspective, there may be use-cases for that and it is not
+ // the job of Form API to artificially limit possibilities.
+ $element['#needs_validation'] = TRUE;
+ }
+ // The value stays the same, but the flags above will ensure it is
+ // processed properly.
+ return $input;
+ }
+ elseif (isset($element['#default_value'])) {
+ return $element['#default_value'];
+ }
+}
+
+/**
+ * Determines the value for a password_confirm form element.
*
* @param $element
* The form element whose value is being populated.
* @param $input
* The incoming input to populate the form element. If this is FALSE,
* the element's default value should be returned.
+ *
* @return
* The data that will appear in the $element_state['values'] collection
* for this element. Return nothing to use the default.
@@ -2328,13 +2387,14 @@ function form_type_password_confirm_value($element, $input = FALSE) {
}
/**
- * Helper function to determine the value for a select form element.
+ * Determines the value for a select form element.
*
* @param $element
* The form element whose value is being populated.
* @param $input
* The incoming input to populate the form element. If this is FALSE,
* the element's default value should be returned.
+ *
* @return
* The data that will appear in the $element_state['values'] collection
* for this element. Return nothing to use the default.
@@ -2368,13 +2428,14 @@ function form_type_select_value($element, $input = FALSE) {
}
/**
- * Helper function to determine the value for a textfield form element.
+ * Determines the value for a textfield form element.
*
* @param $element
* The form element whose value is being populated.
* @param $input
* The incoming input to populate the form element. If this is FALSE,
* the element's default value should be returned.
+ *
* @return
* The data that will appear in the $element_state['values'] collection
* for this element. Return nothing to use the default.
@@ -2388,13 +2449,14 @@ function form_type_textfield_value($element, $input = FALSE) {
}
/**
- * Helper function to determine the value for form's token value.
+ * Determines the value for form's token value.
*
* @param $element
* The form element whose value is being populated.
* @param $input
* The incoming input to populate the form element. If this is FALSE,
* the element's default value should be returned.
+ *
* @return
* The data that will appear in the $element_state['values'] collection
* for this element. Return nothing to use the default.
@@ -2406,7 +2468,7 @@ function form_type_token_value($element, $input = FALSE) {
}
/**
- * Changes submitted form values in $form_state.
+ * Changes submitted form values during form validation.
*
* Use this function to change the submitted value of a form element in a form
* validation function, so that the changed value persists in $form_state
@@ -2458,11 +2520,9 @@ function form_options_flatten($array) {
}
/**
- * Helper function for form_options_flatten().
+ * Iterates over an array and returns a flat array with duplicate keys removed.
*
- * Iterates over arrays which may share common values and produces a flat
- * array that has removed duplicate keys. Also handles cases where objects
- * are passed as array values.
+ * This function also handles cases where objects are passed as array values.
*/
function _form_options_flatten($array) {
$return = &drupal_static(__FUNCTION__);
@@ -2572,7 +2632,7 @@ function theme_select($variables) {
}
/**
- * Converts a select form element's options array into an HTML.
+ * Converts a select form element's options array into HTML.
*
* @param $element
* An associative array containing the properties of the element.
@@ -2580,6 +2640,7 @@ function theme_select($variables) {
* Mixed: Either an associative array of items to list as choices, or an
* object with an 'option' member that is an associative array. This
* parameter is only used internally and should not be passed.
+ *
* @return
* An HTML string of options for the select form element.
*/
@@ -2616,8 +2677,7 @@ function form_select_options($element, $choices = NULL) {
}
/**
- * Traverses a select element's #option array looking for any values
- * that hold the given key. Returns an array of indexes that match.
+ * Returns the indexes of a select element's options matching a given key.
*
* This function is useful if you need to modify the options that are
* already in a form element; for example, to remove choices which are
@@ -2643,6 +2703,7 @@ function form_select_options($element, $choices = NULL) {
* The select element to search.
* @param $key
* The key to look for.
+ *
* @return
* An array of indexes that match the given $key. Array will be
* empty if no elements were found. FALSE if optgroups were found.
@@ -2747,6 +2808,9 @@ function theme_radios($variables) {
if (!empty($element['#attributes']['class'])) {
$attributes['class'] .= ' ' . implode(' ', $element['#attributes']['class']);
}
+ if (isset($element['#attributes']['title'])) {
+ $attributes['title'] = $element['#attributes']['title'];
+ }
return '<div' . drupal_attributes($attributes) . '>' . (!empty($element['#children']) ? $element['#children'] : '') . '</div>';
}
@@ -2779,7 +2843,7 @@ function form_process_password_confirm($element) {
}
/**
- * Validate password_confirm element.
+ * Validates a password_confirm element.
*/
function password_confirm_validate($element, &$element_state) {
$pass1 = trim($element['pass1']['#value']);
@@ -2830,7 +2894,7 @@ function theme_date($variables) {
}
/**
- * Roll out a single date element.
+ * Expands a date element into year, month, and day select elements.
*/
function form_process_date($element) {
// Default to current date
@@ -2886,7 +2950,7 @@ function form_process_date($element) {
}
/**
- * Validates the date type to stop dates like February 30, 2006.
+ * Validates the date type to prevent invalid dates (e.g., February 30, 2006).
*/
function date_validate($element) {
if (!checkdate($element['#value']['month'], $element['#value']['day'], $element['#value']['year'])) {
@@ -2916,7 +2980,7 @@ function map_month($month) {
}
/**
- * If no default value is set for weight select boxes, use 0.
+ * Sets the value for a weight element, with zero as a default.
*/
function weight_value(&$form) {
if (isset($form['#default_value'])) {
@@ -2928,8 +2992,7 @@ function weight_value(&$form) {
}
/**
- * Roll out a single radios element to a list of radios,
- * using the options array as index.
+ * Expands a radios element into individual radio elements.
*/
function form_process_radios($element) {
if (count($element['#options']) > 0) {
@@ -2950,7 +3013,9 @@ function form_process_radios($element) {
// The key is sanitized in drupal_attributes() during output from the
// theme function.
'#return_value' => $key,
- '#default_value' => isset($element['#default_value']) ? $element['#default_value'] : NULL,
+ // Use default or FALSE. A value of FALSE means that the radio button is
+ // not 'checked'.
+ '#default_value' => isset($element['#default_value']) ? $element['#default_value'] : FALSE,
'#attributes' => $element['#attributes'],
'#parents' => $element['#parents'],
'#id' => drupal_html_id('edit-' . implode('-', $parents_for_id)),
@@ -2969,7 +3034,7 @@ function form_process_radios($element) {
* An associative array containing:
* - element: An associative array containing the properties of the element.
* Properties used: #title, #value, #return_value, #description, #required,
- * #attributes.
+ * #attributes, #checked.
*
* @ingroup themeable
*/
@@ -3008,15 +3073,19 @@ function theme_checkboxes($variables) {
if (!empty($element['#attributes']['class'])) {
$attributes['class'] = array_merge($attributes['class'], $element['#attributes']['class']);
}
+ if (isset($element['#attributes']['title'])) {
+ $attributes['title'] = $element['#attributes']['title'];
+ }
return '<div' . drupal_attributes($attributes) . '>' . (!empty($element['#children']) ? $element['#children'] : '') . '</div>';
}
/**
- * Add form_element theming to an element if title or description is set.
+ * Adds form element theming to an element if its title or description is set.
*
* This is used as a pre render function for checkboxes and radios.
*/
function form_pre_render_conditional_form_element($element) {
+ $t = get_t();
// Set the element's title attribute to show #title as a tooltip, if needed.
if (isset($element['#title']) && $element['#title_display'] == 'attribute') {
$element['#attributes']['title'] = $element['#title'];
@@ -3059,6 +3128,9 @@ function form_process_checkbox($element, $form_state) {
return $element;
}
+/**
+ * Processes a checkboxes form element.
+ */
function form_process_checkboxes($element) {
$value = is_array($element['#value']) ? $element['#value'] : array();
$element['#tree'] = TRUE;
@@ -3120,6 +3192,7 @@ function form_process_actions($element, &$form_state) {
* container.
* @param $form_state
* The $form_state array for the form this element belongs to.
+ *
* @return
* The processed element.
*/
@@ -3230,11 +3303,12 @@ function theme_tableselect($variables) {
}
/**
- * Create the correct amount of checkbox or radio elements to populate the table.
+ * Creates checkbox or radio elements to populate a tableselect table.
*
* @param $element
* An associative array containing the properties and children of the
* tableselect element.
+ *
* @return
* The processed element.
*/
@@ -3244,7 +3318,7 @@ function form_process_tableselect($element) {
$value = is_array($element['#value']) ? $element['#value'] : array();
}
else {
- // Advanced selection behaviour make no sense for radios.
+ // Advanced selection behavior makes no sense for radios.
$element['#js_select'] = FALSE;
}
@@ -3328,6 +3402,9 @@ function form_process_tableselect($element) {
* different character, 'replace_pattern' needs to be set accordingly.
* - error: (optional) A custom form error message string to show, if the
* machine name contains disallowed characters.
+ * - standalone: (optional) Whether the live preview should stay in its own
+ * form element rather than in the suffix of the source element. Defaults
+ * to FALSE.
* - #maxlength: (optional) Should be set to the maximum allowed length of the
* machine name. Defaults to 64.
* - #disabled: (optional) Should be set to TRUE in case an existing machine
@@ -3339,6 +3416,9 @@ function form_process_machine_name($element, &$form_state) {
'#title' => t('Machine-readable name'),
'#description' => t('A unique machine-readable name. Can only contain lowercase letters, numbers, and underscores.'),
'#machine_name' => array(),
+ '#field_prefix' => '',
+ '#field_suffix' => '',
+ '#suffix' => '',
);
// A form element that only wants to set one #machine_name property (usually
// 'source' only) would leave all other properties undefined, if the defaults
@@ -3349,6 +3429,9 @@ function form_process_machine_name($element, &$form_state) {
'label' => t('Machine name'),
'replace_pattern' => '[^a-z0-9_]+',
'replace' => '_',
+ 'standalone' => FALSE,
+ 'field_prefix' => $element['#field_prefix'],
+ 'field_suffix' => $element['#field_suffix'],
);
// By default, machine names are restricted to Latin alphanumeric characters.
@@ -3364,7 +3447,7 @@ function form_process_machine_name($element, &$form_state) {
}
// Retrieve the form element containing the human-readable name from the
- // complete form in $form_state. By reference, because we need to append
+ // complete form in $form_state. By reference, because we may need to append
// a #field_suffix that will hold the live preview.
$key_exists = NULL;
$source = drupal_array_get_nested_value($form_state['complete form'], $element['#machine_name']['source'], $key_exists);
@@ -3372,16 +3455,21 @@ function form_process_machine_name($element, &$form_state) {
return $element;
}
- // Append a field suffix to the source form element, which will contain
- // the live preview of the machine name.
$suffix_id = $source['#id'] . '-machine-name-suffix';
- $source += array('#field_suffix' => '');
- $source['#field_suffix'] .= ' <small id="' . $suffix_id . '">&nbsp;</small>';
+ $element['#machine_name']['suffix'] = '#' . $suffix_id;
- $parents = array_merge($element['#machine_name']['source'], array('#field_suffix'));
- drupal_array_set_nested_value($form_state['complete form'], $parents, $source['#field_suffix']);
+ if ($element['#machine_name']['standalone']) {
+ $element['#suffix'] .= ' <small id="' . $suffix_id . '">&nbsp;</small>';
+ }
+ else {
+ // Append a field suffix to the source form element, which will contain
+ // the live preview of the machine name.
+ $source += array('#field_suffix' => '');
+ $source['#field_suffix'] .= ' <small id="' . $suffix_id . '">&nbsp;</small>';
- $element['#machine_name']['suffix'] = '#' . $suffix_id;
+ $parents = array_merge($element['#machine_name']['source'], array('#field_suffix'));
+ drupal_array_set_nested_value($form_state['complete form'], $parents, $source['#field_suffix']);
+ }
$js_settings = array(
'type' => 'setting',
@@ -3398,7 +3486,7 @@ function form_process_machine_name($element, &$form_state) {
}
/**
- * Form element validation handler for #type 'machine_name'.
+ * Form element validation handler for machine_name elements.
*
* Note that #maxlength is validated by _form_validate() already.
*/
@@ -3436,8 +3524,7 @@ function form_validate_machine_name(&$element, &$form_state) {
}
/**
- * Adds fieldsets to the specified group or adds group members to this
- * fieldset.
+ * Arranges fieldsets into groups.
*
* @param $element
* An associative array containing the properties and children of the
@@ -3445,6 +3532,7 @@ function form_validate_machine_name(&$element, &$form_state) {
* child elements are taken over into $form_state.
* @param $form_state
* The $form_state array for the form this fieldset belongs to.
+ *
* @return
* The processed element.
*/
@@ -3548,6 +3636,7 @@ function form_pre_render_fieldset($element) {
* fieldset.
* @param $form_state
* The $form_state array for the form this vertical tab widget belongs to.
+ *
* @return
* The processed element.
*/
@@ -3582,8 +3671,8 @@ function form_process_vertical_tabs($element, &$form_state) {
*
* @param $variables
* An associative array containing:
- * - element: An associative array containing the properties and children of the
- * fieldset. Properties used: #children.
+ * - element: An associative array containing the properties and children of
+ * the fieldset. Properties used: #children.
*
* @ingroup themeable
*/
@@ -3792,7 +3881,7 @@ function theme_password($variables) {
}
/**
- * Expand weight elements into selects.
+ * Expands a weight element into a select element.
*/
function form_process_weight($element) {
$element['#is_weight'] = TRUE;
@@ -3976,7 +4065,8 @@ function theme_form_required_marker($variables) {
*
* Form element labels include the #title and a #required marker. The label is
* associated with the element itself by the element #id. Labels may appear
- * before or after elements, depending on theme_form_element() and #title_display.
+ * before or after elements, depending on theme_form_element() and
+ * #title_display.
*
* This function will not be called for elements with no labels, depending on
* #title_display. For elements that have an empty #title and are not required,
@@ -4055,7 +4145,7 @@ function _form_set_class(&$element, $class = array()) {
}
/**
- * Helper form element validator: integer.
+ * Form element validation handler for integer elements.
*/
function element_validate_integer($element, &$form_state) {
$value = $element['#value'];
@@ -4065,7 +4155,7 @@ function element_validate_integer($element, &$form_state) {
}
/**
- * Helper form element validator: integer > 0.
+ * Form element validation handler for integer elements that must be positive.
*/
function element_validate_integer_positive($element, &$form_state) {
$value = $element['#value'];
@@ -4075,7 +4165,7 @@ function element_validate_integer_positive($element, &$form_state) {
}
/**
- * Helper form element validator: number.
+ * Form element validation handler for number elements.
*/
function element_validate_number($element, &$form_state) {
$value = $element['#value'];
@@ -4091,7 +4181,7 @@ function element_validate_number($element, &$form_state) {
/**
* @defgroup batch Batch operations
* @{
- * Create and process batch operations.
+ * Creates and processes batch operations.
*
* Functions allowing forms processing to be spread out over several page
* requests, thus ensuring that the processing does not get interrupted
@@ -4200,13 +4290,24 @@ function element_validate_number($element, &$form_state) {
*/
/**
- * Opens a new batch.
- *
- * @param $batch
- * An array defining the batch. The following keys can be used -- only
- * 'operations' is required, and batch_init() provides default values for
- * the messages.
- * - 'operations': Array of function calls to be performed.
+ * Adds a new batch.
+ *
+ * Batch operations are added as new batch sets. Batch sets are used to spread
+ * processing (primarily, but not exclusively, forms processing) over several
+ * page requests. This helps to ensure that the processing is not interrupted
+ * due to PHP timeouts, while users are still able to receive feedback on the
+ * progress of the ongoing operations. Combining related operations into
+ * distinct batch sets provides clean code independence for each batch set,
+ * ensuring that two or more batches, submitted independently, can be processed
+ * without mutual interference. Each batch set may specify its own set of
+ * operations and results, produce its own UI messages, and trigger its own
+ * 'finished' callback. Batch sets are processed sequentially, with the progress
+ * bar starting afresh for each new set.
+ *
+ * @param $batch_definition
+ * An associative array defining the batch, with the following elements (all
+ * are optional except as noted):
+ * - operations: (required) Array of function calls to be performed.
* Example:
* @code
* array(
@@ -4214,35 +4315,26 @@ function element_validate_number($element, &$form_state) {
* array('my_function_2', array($arg2_1, $arg2_2)),
* )
* @endcode
- * - 'title': Title for the progress page. Only safe strings should be passed.
- * Defaults to t('Processing').
- * - 'init_message': Message displayed while the processing is initialized.
+ * - title: A safe, translated string to use as the title for the progress
+ * page. Defaults to t('Processing').
+ * - init_message: Message displayed while the processing is initialized.
* Defaults to t('Initializing.').
- * - 'progress_message': Message displayed while processing the batch.
- * Available placeholders are @current, @remaining, @total, @percentage,
- * @estimate and @elapsed. Defaults to t('Completed @current of @total.').
- * - 'error_message': Message displayed if an error occurred while processing
+ * - progress_message: Message displayed while processing the batch. Available
+ * placeholders are @current, @remaining, @total, @percentage, @estimate and
+ * @elapsed. Defaults to t('Completed @current of @total.').
+ * - error_message: Message displayed if an error occurred while processing
* the batch. Defaults to t('An error has occurred.').
- * - 'finished': Name of a function to be executed after the batch has
- * completed. This should be used to perform any result massaging that
- * may be needed, and possibly save data in $_SESSION for display after
- * final page redirection.
- * - 'file': Path to the file containing the definitions of the
- * 'operations' and 'finished' functions, for instance if they don't
- * reside in the main .module file. The path should be relative to
- * base_path(), and thus should be built using drupal_get_path().
- * - 'css': Array of paths to CSS files to be used on the progress page.
- * - 'url_options': options passed to url() when constructing redirect
- * URLs for the batch.
- *
- * Operations are added as new batch sets. Batch sets are used to ensure
- * clean code independence, ensuring that several batches submitted by
- * different parts of the code (core / contrib modules) can be processed
- * correctly while not interfering or having to cope with each other. Each
- * batch set gets to specify his own UI messages, operates on its own set
- * of operations and results, and triggers its own 'finished' callback.
- * Batch sets are processed sequentially, with the progress bar starting
- * fresh for every new set.
+ * - finished: Name of a function to be executed after the batch has
+ * completed. This should be used to perform any result massaging that may
+ * be needed, and possibly save data in $_SESSION for display after final
+ * page redirection.
+ * - file: Path to the file containing the definitions of the 'operations' and
+ * 'finished' functions, for instance if they don't reside in the main
+ * .module file. The path should be relative to base_path(), and thus should
+ * be built using drupal_get_path().
+ * - css: Array of paths to CSS files to be used on the progress page.
+ * - url_options: options passed to url() when constructing redirect URLs for
+ * the batch.
*/
function batch_set($batch_definition) {
if ($batch_definition) {
@@ -4422,6 +4514,7 @@ function &batch_get() {
* The batch array.
* @param $set_id
* The id of the set to process.
+ *
* @return
* The name and class of the queue are added by reference to the batch set.
*/
@@ -4451,6 +4544,7 @@ function _batch_populate_queue(&$batch, $set_id) {
*
* @param $batch_set
* The batch set.
+ *
* @return
* The queue object.
*/
diff --git a/includes/graph.inc b/includes/graph.inc
index 7fcc57a45..9ef86a145 100644
--- a/includes/graph.inc
+++ b/includes/graph.inc
@@ -7,7 +7,7 @@
/**
- * Perform a depth first sort on a directed acyclic graph.
+ * Performs a depth-first sort on a directed acyclic graph.
*
* @param $graph
* A three dimensional associated array, with the first keys being the names
@@ -72,7 +72,7 @@ function drupal_depth_first_search(&$graph) {
}
/**
- * Helper function to perform a depth first sort.
+ * Performs a depth-first sort on a graph.
*
* @param $graph
* A three dimensional associated graph array.
diff --git a/includes/language.inc b/includes/language.inc
index b7057f2a1..20909f5a6 100644
--- a/includes/language.inc
+++ b/includes/language.inc
@@ -349,7 +349,10 @@ function language_provider_invoke($provider_id, $provider = NULL) {
$results[$provider_id] = isset($languages[$langcode]) ? $languages[$langcode] : FALSE;
}
- return $results[$provider_id];
+ // Since objects are resources we need to return a clone to prevent the
+ // provider cache to be unintentionally altered. The same providers might be
+ // used with different language types based on configuration.
+ return !empty($results[$provider_id]) ? clone($results[$provider_id]) : $results[$provider_id];
}
/**
@@ -380,15 +383,18 @@ function language_initialize($type) {
// first valid language found.
$negotiation = variable_get("language_negotiation_$type", array());
- foreach ($negotiation as $id => $provider) {
- $language = language_provider_invoke($id, $provider);
+ foreach ($negotiation as $provider_id => $provider) {
+ $language = language_provider_invoke($provider_id, $provider);
if ($language) {
+ $language->provider = $provider_id;
return $language;
}
}
// If no other language was found use the default one.
- return language_default();
+ $language = language_default();
+ $language->provider = LANGUAGE_NEGOTIATION_DEFAULT;
+ return $language;
}
/**
diff --git a/includes/locale.inc b/includes/locale.inc
index 0db4d4a07..7fb8d6424 100644
--- a/includes/locale.inc
+++ b/includes/locale.inc
@@ -141,7 +141,7 @@ function locale_language_from_browser($languages) {
// language-range = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" )
// Samples: "hu, en-us;q=0.66, en;q=0.33", "hu,en-us;q=0.5"
$browser_langcodes = array();
- if (preg_match_all('@([a-zA-Z-]+|\*)(?:;q=([0-9.]+))?(?:$|\s*,\s*)@', trim($_SERVER['HTTP_ACCEPT_LANGUAGE']), $matches, PREG_SET_ORDER)) {
+ if (preg_match_all('@(?<=[, ]|^)([a-zA-Z-]+|\*)(?:;q=([0-9.]+))?(?:$|\s*,\s*)@', trim($_SERVER['HTTP_ACCEPT_LANGUAGE']), $matches, PREG_SET_ORDER)) {
foreach ($matches as $match) {
// We can safely use strtolower() here, tags are ASCII.
// RFC2616 mandates that the decimal part is no more than three digits,
@@ -430,8 +430,27 @@ function locale_language_url_rewrite_url(&$path, &$options) {
case LOCALE_LANGUAGE_NEGOTIATION_URL_DOMAIN:
if ($options['language']->domain) {
// Ask for an absolute URL with our modified base_url.
+ global $is_https;
+ $url_scheme = ($is_https) ? 'https://' : 'http://';
$options['absolute'] = TRUE;
- $options['base_url'] = $options['language']->domain;
+
+ // Take the domain without ports or protocols so we can apply the
+ // protocol needed. The setting might include a protocol.
+ // This is changed in Drupal 8 but we need to keep backwards
+ // compatibility for Drupal 7.
+ $host = 'http://' . str_replace(array('http://', 'https://'), '', $options['language']->domain);
+ $host = parse_url($host, PHP_URL_HOST);
+
+ // Apply the appropriate protocol to the URL.
+ $options['base_url'] = $url_scheme . $host;
+ if (isset($options['https']) && variable_get('https', FALSE)) {
+ if ($options['https'] === TRUE) {
+ $options['base_url'] = str_replace('http://', 'https://', $options['base_url']);
+ }
+ elseif ($options['https'] === FALSE) {
+ $options['base_url'] = str_replace('https://', 'http://', $options['base_url']);
+ }
+ }
}
break;
@@ -978,7 +997,7 @@ function _locale_import_one_string($op, $value = NULL, $mode = NULL, $lang = NUL
// data untouched or if we don't have an existing plural formula.
$header = _locale_import_parse_header($value['msgstr']);
- // Get the plural formula and update in database.
+ // Get and store the plural formula if available.
if (isset($header["Plural-Forms"]) && $p = _locale_import_parse_plural_forms($header["Plural-Forms"], $file->uri)) {
list($nplurals, $plural) = $p;
db_update('languages')
@@ -989,15 +1008,6 @@ function _locale_import_one_string($op, $value = NULL, $mode = NULL, $lang = NUL
->condition('language', $lang)
->execute();
}
- else {
- db_update('languages')
- ->fields(array(
- 'plurals' => 0,
- 'formula' => '',
- ))
- ->condition('language', $lang)
- ->execute();
- }
}
$header_done = TRUE;
}
diff --git a/includes/menu.inc b/includes/menu.inc
index 25a87af12..b25a374ac 100644
--- a/includes/menu.inc
+++ b/includes/menu.inc
@@ -321,7 +321,14 @@ 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');
+ // If the optimized menu_masks array is not available use brute force 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.
+ if (!$masks) {
+ $masks = range(511, 1);
+ }
// Only examine patterns that actually exist as router items (the masks).
foreach ($masks as $i) {
if ($i > $end) {
@@ -452,18 +459,10 @@ function menu_get_item($path = NULL, $router_item = NULL) {
}
$original_map = arg(NULL, $path);
- // Since there is no limit to the length of $path, use a hash to keep it
- // short yet unique.
- $cid = 'menu_item:' . hash('sha256', $path);
- if ($cached = cache_get($cid, 'cache_menu')) {
- $router_item = $cached->data;
- }
- else {
- $parts = array_slice($original_map, 0, MENU_MAX_PARTS);
- $ancestors = menu_get_ancestors($parts);
- $router_item = db_query_range('SELECT * FROM {menu_router} WHERE path IN (:ancestors) ORDER BY fit DESC', 0, 1, array(':ancestors' => $ancestors))->fetchAssoc();
- cache_set($cid, $router_item, 'cache_menu');
- }
+ $parts = array_slice($original_map, 0, MENU_MAX_PARTS);
+ $ancestors = menu_get_ancestors($parts);
+ $router_item = db_query_range('SELECT * FROM {menu_router} WHERE path IN (:ancestors) ORDER BY fit DESC', 0, 1, array(':ancestors' => $ancestors))->fetchAssoc();
+
if ($router_item) {
// Allow modules to alter the router item before it is translated and
// checked for access.
@@ -2152,7 +2151,7 @@ function menu_contextual_links($module, $parent_path, $args) {
$links = array();
// Performance: In case a previous invocation for the same parent path did not
// return any links, we immediately return here.
- if (isset($path_empty[$parent_path])) {
+ if (isset($path_empty[$parent_path]) && strpos($parent_path, '%') !== FALSE) {
return $links;
}
// Construct the item-specific parent path.
@@ -2321,6 +2320,9 @@ function menu_get_active_menu_names() {
*/
function menu_set_active_item($path) {
$_GET['q'] = $path;
+ // Since the active item has changed, the active menu trail may also be out
+ // of date.
+ drupal_static_reset('menu_set_active_trail');
}
/**
@@ -2406,7 +2408,7 @@ function menu_set_active_trail($new_trail = NULL) {
// appending either the preferred link or the menu router item for the
// current page. Exclude it if we are on the front page.
$last = end($trail);
- if ($last['href'] != $preferred_link['href'] && !drupal_is_front_page()) {
+ if ($preferred_link && $last['href'] != $preferred_link['href'] && !drupal_is_front_page()) {
$trail[] = $preferred_link;
}
}
@@ -3149,10 +3151,10 @@ function menu_link_save(&$item, $existing_item = array(), $parent_candidates = a
}
// If every value in $existing_item is the same in the $item, there is no
// reason to run the update queries or clear the caches. We use
- // array_intersect_assoc() with the $item as the first parameter because
+ // array_intersect_key() with the $item as the first parameter because
// $item may have additional keys left over from building a router entry.
// The intersect removes the extra keys, allowing a meaningful comparison.
- if (!$existing_item || (array_intersect_assoc($item, $existing_item)) != $existing_item) {
+ if (!$existing_item || (array_intersect_key($item, $existing_item) != $existing_item)) {
db_update('menu_links')
->fields(array(
'menu_name' => $item['menu_name'],
diff --git a/includes/module.inc b/includes/module.inc
index 77e35b7b0..500bc5ebc 100644
--- a/includes/module.inc
+++ b/includes/module.inc
@@ -425,6 +425,8 @@ function module_enable($module_list, $enable_dependencies = TRUE) {
registry_update();
// Refresh the schema to include it.
drupal_get_schema(NULL, TRUE);
+ // Update the theme registry to include it.
+ drupal_theme_rebuild();
// Clear entity cache.
entity_info_cache_clear();
@@ -546,6 +548,8 @@ function module_disable($module_list, $disable_dependents = TRUE) {
// Update the registry to remove the newly-disabled module.
registry_update();
_system_update_bootstrap_status();
+ // Update the theme registry to remove the newly-disabled module.
+ drupal_theme_rebuild();
}
// If there remains no more node_access module, rebuilding will be
diff --git a/includes/pager.inc b/includes/pager.inc
index a5d3e6be0..c060d0e7c 100644
--- a/includes/pager.inc
+++ b/includes/pager.inc
@@ -630,7 +630,13 @@ function theme_pager_link($variables) {
}
}
- return l($text, $_GET['q'], array('attributes' => $attributes, 'query' => $query));
+ // @todo l() cannot be used here, since it adds an 'active' class based on the
+ // path only (which is always the current path for pager links). Apparently,
+ // none of the pager links is active at any time - but it should still be
+ // possible to use l() here.
+ // @see http://drupal.org/node/1410574
+ $attributes['href'] = url($_GET['q'], array('query' => $query));
+ return '<a' . drupal_attributes($attributes) . '>' . check_plain($text) . '</a>';
}
/**
diff --git a/includes/path.inc b/includes/path.inc
index ed5b639fb..411a7a71c 100644
--- a/includes/path.inc
+++ b/includes/path.inc
@@ -431,21 +431,27 @@ function path_load($conditions) {
* - language: (optional) The language of the alias.
*/
function path_save(&$path) {
- $path += array('pid' => NULL, 'language' => LANGUAGE_NONE);
+ $path += array('language' => LANGUAGE_NONE);
- // Insert or update the alias.
- $status = drupal_write_record('url_alias', $path, (!empty($path['pid']) ? 'pid' : array()));
+ // Load the stored alias, if any.
+ if (!empty($path['pid']) && !isset($path['original'])) {
+ $path['original'] = path_load($path['pid']);
+ }
- // Verify that a record was written.
- if ($status) {
- if ($status === SAVED_NEW) {
- module_invoke_all('path_insert', $path);
- }
- else {
- module_invoke_all('path_update', $path);
- }
- drupal_clear_path_cache($path['source']);
+ if (empty($path['pid'])) {
+ drupal_write_record('url_alias', $path);
+ module_invoke_all('path_insert', $path);
+ }
+ else {
+ drupal_write_record('url_alias', $path, array('pid'));
+ module_invoke_all('path_update', $path);
}
+
+ // Clear internal properties.
+ unset($path['original']);
+
+ // Clear the static alias cache.
+ drupal_clear_path_cache($path['source']);
}
/**
diff --git a/includes/registry.inc b/includes/registry.inc
index c7b8702c4..f6c81eb1a 100644
--- a/includes/registry.inc
+++ b/includes/registry.inc
@@ -131,10 +131,6 @@ function _registry_parse_files($files) {
if (file_exists($filename)) {
$hash = hash_file('sha256', $filename);
if (empty($file['hash']) || $file['hash'] != $hash) {
- // Delete registry entries for this file, so we can insert the new resources.
- db_delete('registry')
- ->condition('filename', $filename)
- ->execute();
$file['hash'] = $hash;
$parsed_files[$filename] = $file;
}
@@ -156,9 +152,9 @@ function _registry_parse_files($files) {
* Parse a file and save its function and class listings.
*
* @param $filename
- * Name of the file we are going to parse.
+ * Name of the file we are going to parse.
* @param $contents
- * Contents of the file we are going to parse as a string.
+ * Contents of the file we are going to parse as a string.
* @param $module
* (optional) Name of the module this file belongs to.
* @param $weight
@@ -166,17 +162,25 @@ function _registry_parse_files($files) {
*/
function _registry_parse_file($filename, $contents, $module = '', $weight = 0) {
if (preg_match_all('/^\s*(?:abstract|final)?\s*(class|interface)\s+([a-zA-Z0-9_]+)/m', $contents, $matches)) {
- $query = db_insert('registry')->fields(array('name', 'type', 'filename', 'module', 'weight'));
foreach ($matches[2] as $key => $name) {
- $query->values(array(
- 'name' => $name,
- 'type' => $matches[1][$key],
- 'filename' => $filename,
- 'module' => $module,
- 'weight' => $weight,
- ));
+ db_merge('registry')
+ ->key(array(
+ 'name' => $name,
+ 'type' => $matches[1][$key],
+ ))
+ ->fields(array(
+ 'filename' => $filename,
+ 'module' => $module,
+ 'weight' => $weight,
+ ))
+ ->execute();
}
- $query->execute();
+ // Delete any resources for this file where the name is not in the list
+ // we just merged in.
+ db_delete('registry')
+ ->condition('filename', $filename)
+ ->condition('name', $matches[2], 'NOT IN')
+ ->execute();
}
}
diff --git a/includes/session.inc b/includes/session.inc
index fd04de875..8f1bcafc4 100644
--- a/includes/session.inc
+++ b/includes/session.inc
@@ -172,7 +172,7 @@ function _drupal_session_write($sid, $value) {
// For performance reasons, do not update the sessions table, unless
// $_SESSION has changed or more than 180 has passed since the last update.
- if ($is_changed || REQUEST_TIME - $user->timestamp > variable_get('session_write_interval', 180)) {
+ if ($is_changed || !isset($user->timestamp) || REQUEST_TIME - $user->timestamp > variable_get('session_write_interval', 180)) {
// Either ssid or sid or both will be added from $key below.
$fields = array(
'uid' => $user->uid,
@@ -199,6 +199,9 @@ function _drupal_session_write($sid, $value) {
}
}
}
+ elseif (variable_get('https', FALSE)) {
+ unset($key['ssid']);
+ }
db_merge('sessions')
->key($key)
@@ -255,11 +258,17 @@ function drupal_session_initialize() {
// we lazily start sessions at the end of this request, and some
// processes (like drupal_get_token()) needs to know the future
// session ID in advance.
+ $GLOBALS['lazy_session'] = TRUE;
$user = drupal_anonymous_user();
// Less random sessions (which are much faster to generate) are used for
// anonymous users than are generated in drupal_session_regenerate() when
// a user becomes authenticated.
session_id(drupal_hash_base64(uniqid(mt_rand(), TRUE)));
+ if ($is_https && variable_get('https', FALSE)) {
+ $insecure_session_name = substr(session_name(), 1);
+ $session_id = drupal_hash_base64(uniqid(mt_rand(), TRUE));
+ $_COOKIE[$insecure_session_name] = $session_id;
+ }
}
date_default_timezone_set(drupal_get_user_timezone());
}
@@ -291,7 +300,7 @@ function drupal_session_start() {
* If an anonymous user already have an empty session, destroy it.
*/
function drupal_session_commit() {
- global $user;
+ global $user, $is_https;
if (!drupal_save_session()) {
// We don't have anything to do if we are not allowed to save the session.
@@ -310,6 +319,12 @@ function drupal_session_commit() {
// started.
if (!drupal_session_started()) {
drupal_session_start();
+ if ($is_https && variable_get('https', FALSE)) {
+ $insecure_session_name = substr(session_name(), 1);
+ $params = session_get_cookie_params();
+ $expire = $params['lifetime'] ? REQUEST_TIME + $params['lifetime'] : 0;
+ setcookie($insecure_session_name, $_COOKIE[$insecure_session_name], $expire, $params['path'], $params['domain'], FALSE, $params['httponly']);
+ }
}
// Write the session data.
session_write_close();
@@ -336,7 +351,7 @@ function drupal_session_regenerate() {
global $user, $is_https;
if ($is_https && variable_get('https', FALSE)) {
$insecure_session_name = substr(session_name(), 1);
- if (isset($_COOKIE[$insecure_session_name])) {
+ if (!isset($GLOBALS['lazy_session']) && isset($_COOKIE[$insecure_session_name])) {
$old_insecure_session_id = $_COOKIE[$insecure_session_name];
}
$params = session_get_cookie_params();
@@ -416,7 +431,10 @@ function _drupal_session_destroy($sid) {
// Unset the session cookies.
_drupal_session_delete_cookie(session_name());
if ($is_https) {
- _drupal_session_delete_cookie(substr(session_name(), 1), TRUE);
+ _drupal_session_delete_cookie(substr(session_name(), 1), FALSE);
+ }
+ elseif (variable_get('https', FALSE)) {
+ _drupal_session_delete_cookie('S' . session_name(), TRUE);
}
}
@@ -425,13 +443,17 @@ function _drupal_session_destroy($sid) {
*
* @param $name
* Name of session cookie to delete.
- * @param $force_insecure
- * Force cookie to be insecure.
+ * @param boolean $secure
+ * Force the secure value of the cookie.
*/
-function _drupal_session_delete_cookie($name, $force_insecure = FALSE) {
- if (isset($_COOKIE[$name])) {
+function _drupal_session_delete_cookie($name, $secure = NULL) {
+ global $is_https;
+ if (isset($_COOKIE[$name]) || (!$is_https && $secure === TRUE)) {
$params = session_get_cookie_params();
- setcookie($name, '', REQUEST_TIME - 3600, $params['path'], $params['domain'], !$force_insecure && $params['secure'], $params['httponly']);
+ if ($secure !== NULL) {
+ $params['secure'] = $secure;
+ }
+ setcookie($name, '', REQUEST_TIME - 3600, $params['path'], $params['domain'], $params['secure'], $params['httponly']);
unset($_COOKIE[$name]);
}
}
diff --git a/includes/theme.inc b/includes/theme.inc
index da4200e56..51e1075ca 100644
--- a/includes/theme.inc
+++ b/includes/theme.inc
@@ -252,7 +252,20 @@ function _drupal_theme_initialize($theme, $base_theme = array(), $registry_callb
* class.
*/
function theme_get_registry($complete = TRUE) {
- static $theme_registry = array();
+ // Use the advanced drupal_static() pattern, since this is called very often.
+ static $drupal_static_fast;
+ if (!isset($drupal_static_fast)) {
+ $drupal_static_fast['registry'] = &drupal_static('theme_get_registry');
+ }
+ $theme_registry = &$drupal_static_fast['registry'];
+
+ // Initialize the theme, if this is called early in the bootstrap, or after
+ // static variables have been reset.
+ if (!is_array($theme_registry)) {
+ drupal_theme_initialize();
+ $theme_registry = array();
+ }
+
$key = (int) $complete;
if (!isset($theme_registry[$key])) {
@@ -335,6 +348,7 @@ function _theme_save_registry($theme, $registry) {
* to add more theme hooks.
*/
function drupal_theme_rebuild() {
+ drupal_static_reset('theme_get_registry');
cache_clear_all('theme_registry', 'cache', TRUE);
}
@@ -899,8 +913,6 @@ function list_themes($refresh = FALSE) {
* @see themeable
*/
function theme($hook, $variables = array()) {
- static $hooks = NULL;
-
// If called before all modules are loaded, we do not necessarily have a full
// theme registry to work with, and therefore cannot process the theme
// request properly. See also _theme_load_registry().
@@ -908,10 +920,7 @@ function theme($hook, $variables = array()) {
throw new Exception(t('theme() may not be called until all modules are loaded.'));
}
- if (!isset($hooks)) {
- drupal_theme_initialize();
- $hooks = theme_get_registry(FALSE);
- }
+ $hooks = theme_get_registry(FALSE);
// If an array of hook candidates were passed, use the first one that has an
// implementation.
@@ -991,6 +1000,13 @@ function theme($hook, $variables = array()) {
if (isset($info['base hook'])) {
$base_hook = $info['base hook'];
$base_hook_info = $hooks[$base_hook];
+ // Include files required by the base hook, since its variable processors
+ // might reside there.
+ if (!empty($base_hook_info['includes'])) {
+ foreach ($base_hook_info['includes'] as $include_file) {
+ include_once DRUPAL_ROOT . '/' . $include_file;
+ }
+ }
if (isset($base_hook_info['preprocess functions']) || isset($base_hook_info['process functions'])) {
$variables['theme_hook_suggestion'] = $hook;
$hook = $base_hook;
@@ -1960,8 +1976,11 @@ function theme_item_list($variables) {
$type = $variables['type'];
$attributes = $variables['attributes'];
+ // Only output the list container and title, if there are any list items.
+ // Check to see whether the block title exists before adding a header.
+ // Empty headers are not semantic and present accessibility challenges.
$output = '<div class="item-list">';
- if (isset($title)) {
+ if (isset($title) && $title !== '') {
$output .= '<h3>' . $title . '</h3>';
}
diff --git a/includes/unicode.inc b/includes/unicode.inc
index 9dde2ca70..cd9cd9bf0 100644
--- a/includes/unicode.inc
+++ b/includes/unicode.inc
@@ -73,7 +73,7 @@ define('PREG_CLASS_UNICODE_WORD_BOUNDARY',
'\x{A836}-\x{A839}\x{A874}-\x{A877}\x{A8CE}-\x{A8CF}\x{A8F8}-\x{A8FA}' .
'\x{A92E}-\x{A92F}\x{A95F}\x{A9C1}-\x{A9CD}\x{A9DE}-\x{A9DF}' .
'\x{AA5C}-\x{AA5F}\x{AA77}-\x{AA79}\x{AADE}-\x{AADF}\x{ABEB}' .
- '\x{D800}-\x{F8FF}\x{FB29}\x{FD3E}-\x{FD3F}\x{FDFC}-\x{FDFD}' .
+ '\x{E000}-\x{F8FF}\x{FB29}\x{FD3E}-\x{FD3F}\x{FDFC}-\x{FDFD}' .
'\x{FE10}-\x{FE19}\x{FE30}-\x{FE6B}\x{FEFF}-\x{FF0F}\x{FF1A}-\x{FF20}' .
'\x{FF3B}-\x{FF40}\x{FF5B}-\x{FF65}\x{FFE0}-\x{FFFD}');
diff --git a/includes/update.inc b/includes/update.inc
index 41f33f402..6588fac00 100644
--- a/includes/update.inc
+++ b/includes/update.inc
@@ -410,15 +410,18 @@ function update_fix_d7_block_deltas(&$sandbox, $renamed_deltas, $moved_deltas) {
// Initialize batch update information.
$sandbox['progress'] = 0;
$sandbox['last_user_processed'] = -1;
- $sandbox['max'] = db_query("SELECT COUNT(*) FROM {users} WHERE data IS NOT NULL")->fetchField();
+ $sandbox['max'] = db_query("SELECT COUNT(*) FROM {users} WHERE data LIKE :block", array(
+ ':block' => '%' . db_like(serialize('block')) . '%',
+ ))
+ ->fetchField();
}
// Now do the batch update of the user-specific block visibility settings.
$limit = 100;
$result = db_select('users', 'u')
->fields('u', array('uid', 'data'))
->condition('uid', $sandbox['last_user_processed'], '>')
+ ->condition('data', '%' . db_like(serialize('block')) . '%', 'LIKE')
->orderBy('uid', 'ASC')
- ->where('data IS NOT NULL')
->range(0, $limit)
->execute();
foreach ($result as $row) {
@@ -815,9 +818,6 @@ function update_fix_d7_install_profile() {
'files' => array(),
);
- // The install profile is always required.
- $file->info['required'] = TRUE;
-
$values = array(
'filename' => $filename,
'name' => $profile,
diff --git a/includes/updater.inc b/includes/updater.inc
index 9801629b1..d4f99e974 100644
--- a/includes/updater.inc
+++ b/includes/updater.inc
@@ -330,7 +330,7 @@ class Updater {
}
catch (FileTransferException $e) {
$message = t($e->getMessage(), $e->arguments);
- $throw_message = t('Unable to create %directory due to the following: %reason', array('%directory' => $install_location, '%reason' => $message));
+ $throw_message = t('Unable to create %directory due to the following: %reason', array('%directory' => $directory, '%reason' => $message));
throw new UpdaterException($throw_message);
}
}
diff --git a/includes/utility.inc b/includes/utility.inc
index d195bff74..5019852c7 100644
--- a/includes/utility.inc
+++ b/includes/utility.inc
@@ -51,7 +51,7 @@ function drupal_var_export($var, $prefix = '') {
// magic method __set_state() leaving the export broken. This
// workaround avoids this by casting the object as an array for
// export and casting it back to an object when evaluated.
- $output .= '(object) ' . drupal_var_export((array) $var, $prefix);
+ $output = '(object) ' . drupal_var_export((array) $var, $prefix);
}
else {
$output = var_export($var, TRUE);