diff options
Diffstat (limited to 'includes')
-rw-r--r-- | includes/batch.inc | 18 | ||||
-rw-r--r-- | includes/bootstrap.inc | 13 | ||||
-rw-r--r-- | includes/common.inc | 15 | ||||
-rw-r--r-- | includes/database/database.inc | 51 | ||||
-rw-r--r-- | includes/database/mysql/database.inc | 52 | ||||
-rw-r--r-- | includes/database/select.inc | 2 | ||||
-rw-r--r-- | includes/database/sqlite/database.inc | 14 | ||||
-rw-r--r-- | includes/entity.inc | 15 | ||||
-rw-r--r-- | includes/errors.inc | 6 | ||||
-rw-r--r-- | includes/file.inc | 22 | ||||
-rw-r--r-- | includes/form.inc | 34 | ||||
-rw-r--r-- | includes/install.inc | 2 | ||||
-rw-r--r-- | includes/locale.inc | 2 |
13 files changed, 180 insertions, 66 deletions
diff --git a/includes/batch.inc b/includes/batch.inc index 7011abfbd..727c62560 100644 --- a/includes/batch.inc +++ b/includes/batch.inc @@ -339,6 +339,8 @@ function _batch_process() { $progress_message = $old_set['progress_message']; } + // Total progress is the number of operations that have fully run plus the + // completion level of the current operation. $current = $total - $remaining + $finished; $percentage = _batch_api_percentage($total, $current); $elapsed = isset($current_set['elapsed']) ? $current_set['elapsed'] : 0; @@ -373,7 +375,10 @@ function _batch_process() { * @param $total * The total number of operations. * @param $current - * The number of the current operation. + * The number of the current operation. This may be a floating point number + * rather than an integer in the case of a multi-step operation that is not + * yet complete; in that case, the fractional part of $current represents the + * fraction of the operation that has been completed. * @return * The properly formatted percentage, as a string. We output percentages * using the correct number of decimal places so that we never print "100%" @@ -390,7 +395,16 @@ function _batch_api_percentage($total, $current) { // We add a new digit at 200, 2000, etc. (since, for example, 199/200 // would round up to 100% if we didn't). $decimal_places = max(0, floor(log10($total / 2.0)) - 1); - $percentage = sprintf('%01.' . $decimal_places . 'f', round($current / $total * 100, $decimal_places)); + do { + // Calculate the percentage to the specified number of decimal places. + $percentage = sprintf('%01.' . $decimal_places . 'f', round($current / $total * 100, $decimal_places)); + // When $current is an integer, the above calculation will always be + // correct. However, if $current is a floating point number (in the case + // of a multi-step batch operation that is not yet complete), $percentage + // may be erroneously rounded up to 100%. To prevent that, we add one + // more decimal place and try again. + $decimal_places++; + } while ($percentage == '100'); } return $percentage; } diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index 830894f52..c53286406 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -1122,13 +1122,12 @@ function drupal_serve_page_from_cache(stdClass $cache) { } } - // If a cache is served from a HTTP proxy without hitting the web server, - // the boot and exit hooks cannot be fired, so only allow caching in - // proxies if boot hooks are disabled. If the client send a session cookie, - // do not bother caching the page in a public proxy, because the cached copy - // will only be served to that particular user due to Vary: Cookie, unless - // the Vary header has been replaced or unset in hook_boot() (see below). - $max_age = !variable_get('page_cache_invoke_hooks', TRUE) && (!isset($_COOKIE[session_name()]) || isset($hook_boot_headers['vary'])) ? variable_get('page_cache_maximum_age', 0) : 0; + // If the client sent a session cookie, a cached copy will only be served + // to that one particular client due to Vary: Cookie. Thus, do not set + // max-age > 0, allowing the page to be cached by external proxies, when a + // session cookie is present unless the Vary header has been replaced or + // unset in hook_boot(). + $max_age = !isset($_COOKIE[session_name()]) || isset($hook_boot_headers['vary']) ? variable_get('page_cache_maximum_age', 0) : 0; $default_headers['Cache-Control'] = 'public, max-age=' . $max_age; // Entity tag should change if the output changes. diff --git a/includes/common.inc b/includes/common.inc index 5dadb4d16..9b582c446 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -752,7 +752,8 @@ function drupal_access_denied() { * received. * - redirect_code: If redirected, an integer containing the initial response * status code. - * - redirect_url: If redirected, a string containing the redirection location. + * - redirect_url: If redirected, a string containing the URL of the redirect + * target. * - error: If an error occurred, the error message. Otherwise not set. * - headers: An array containing the response headers as name/value pairs. * HTTP header names are case-insensitive (RFC 2616, section 4.2), so for @@ -1008,7 +1009,9 @@ function drupal_http_request($url, array $options = array()) { $result = drupal_http_request($location, $options); $result->redirect_code = $code; } - $result->redirect_url = $location; + if (!isset($result->redirect_url)) { + $result->redirect_url = $location; + } break; default: $result->error = $status_message; @@ -2822,6 +2825,8 @@ function drupal_add_html_head_link($attributes, $header = FALSE) { * * @return * An array of queued cascading stylesheets. + * + * @see drupal_get_css() */ function drupal_add_css($data = NULL, $options = NULL) { $css = &drupal_static(__FUNCTION__, array()); @@ -2902,8 +2907,11 @@ function drupal_add_css($data = NULL, $options = NULL) { * (optional) If set to TRUE, this function skips calling drupal_alter() on * $css, useful when the calling function passes a $css array that has already * been altered. + * * @return * A string of XHTML CSS tags. + * + * @see drupal_add_css() */ function drupal_get_css($css = NULL, $skip_alter = FALSE) { if (!isset($css)) { @@ -7431,7 +7439,8 @@ function entity_create_stub_entity($entity_type, $ids) { * Whether to reset the internal cache for the requested entity type. * * @return - * An array of entity objects indexed by their ids. + * An array of entity objects indexed by their ids. When no results are + * found, an empty array is returned. * * @todo Remove $conditions in Drupal 8. */ diff --git a/includes/database/database.inc b/includes/database/database.inc index 4cc1a33d7..e08f9074f 100644 --- a/includes/database/database.inc +++ b/includes/database/database.inc @@ -273,20 +273,25 @@ abstract class DatabaseConnection extends PDO { protected $schema = NULL; /** - * The default prefix used by this database connection. + * The prefixes used by this database connection. * - * Separated from the other prefixes for performance reasons. + * @var array + */ + protected $prefixes = array(); + + /** + * List of search values for use in prefixTables(). * - * @var string + * @var array */ - protected $defaultPrefix = ''; + protected $prefixSearch = array(); /** - * The non-default prefixes used by this database connection. + * List of replacement values for use in prefixTables(). * * @var array */ - protected $prefixes = array(); + protected $prefixReplace = array(); function __construct($dsn, $username, $password, $driver_options = array()) { // Initialize and prepare the connection prefix. @@ -375,7 +380,7 @@ abstract class DatabaseConnection extends PDO { } /** - * Preprocess the prefixes used by this database connection. + * Set the list of prefixes used by this database connection. * * @param $prefix * The prefixes, in any of the multiple forms documented in @@ -383,14 +388,27 @@ abstract class DatabaseConnection extends PDO { */ protected function setPrefix($prefix) { if (is_array($prefix)) { - $this->defaultPrefix = isset($prefix['default']) ? $prefix['default'] : ''; - unset($prefix['default']); - $this->prefixes = $prefix; + $this->prefixes = $prefix + array('default' => ''); } else { - $this->defaultPrefix = $prefix; - $this->prefixes = array(); + $this->prefixes = array('default' => $prefix); } + + // Set up variables for use in prefixTables(). Replace table-specific + // prefixes first. + $this->prefixSearch = array(); + $this->prefixReplace = array(); + foreach ($this->prefixes as $key => $val) { + if ($key != 'default') { + $this->prefixSearch[] = '{' . $key . '}'; + $this->prefixReplace[] = $val . $key; + } + } + // Then replace remaining tables with the default prefix. + $this->prefixSearch[] = '{'; + $this->prefixReplace[] = $this->prefixes['default']; + $this->prefixSearch[] = '}'; + $this->prefixReplace[] = ''; } /** @@ -408,12 +426,7 @@ abstract class DatabaseConnection extends PDO { * The properly-prefixed string. */ public function prefixTables($sql) { - // Replace specific table prefixes first. - foreach ($this->prefixes as $key => $val) { - $sql = strtr($sql, array('{' . $key . '}' => $val . $key)); - } - // Then replace remaining tables with the default prefix. - return strtr($sql, array('{' => $this->defaultPrefix , '}' => '')); + return str_replace($this->prefixSearch, $this->prefixReplace, $sql); } /** @@ -427,7 +440,7 @@ abstract class DatabaseConnection extends PDO { return $this->prefixes[$table]; } else { - return $this->defaultPrefix; + return $this->prefixes['default']; } } diff --git a/includes/database/mysql/database.inc b/includes/database/mysql/database.inc index 262cc6051..bc31feaaf 100644 --- a/includes/database/mysql/database.inc +++ b/includes/database/mysql/database.inc @@ -133,6 +133,58 @@ class DatabaseConnection_mysql extends DatabaseConnection { catch (PDOException $e) { } } + + /** + * Overridden to work around issues to MySQL not supporting transactional DDL. + */ + public function popTransaction($name) { + if (!$this->supportsTransactions()) { + return; + } + if (!$this->inTransaction()) { + throw new DatabaseTransactionNoActiveException(); + } + + // Commit everything since SAVEPOINT $name. + while ($savepoint = array_pop($this->transactionLayers)) { + if ($savepoint != $name) { + continue; + } + + // If there are no more layers left then we should commit. + if (empty($this->transactionLayers)) { + if (!PDO::commit()) { + throw new DatabaseTransactionCommitFailedException(); + } + } + else { + // Attempt to release this savepoint in the standard way. + try { + $this->query('RELEASE SAVEPOINT ' . $name); + } + catch (PDOException $e) { + // However, in MySQL (InnoDB), savepoints are automatically committed + // when tables are altered or created (DDL transactions are not + // supported). This can cause exceptions due to trying to release + // savepoints which no longer exist. + // + // To avoid exceptions when no actual error has occurred, we silently + // succeed for PDOExceptions with SQLSTATE 42000 ("Syntax error or + // access rule violation") and MySQL error code 1305 ("SAVEPOINT does + // not exist"). + if ($e->getCode() == '42000' && $e->errorInfo[1] == '1305') { + // If one SAVEPOINT was released automatically, then all were. + // Therefore, we keep just the topmost transaction. + $this->transactionLayers = array('drupal_transaction'); + } + else { + throw $e; + } + } + break; + } + } + } } diff --git a/includes/database/select.inc b/includes/database/select.inc index 716c2fc3d..53be20adc 100644 --- a/includes/database/select.inc +++ b/includes/database/select.inc @@ -414,7 +414,7 @@ interface SelectQueryInterface extends QueryConditionInterface, QueryAlterableIn * @param $start * The first record from the result set to return. If NULL, removes any * range directives that are set. - * @param $limit + * @param $length * The number of records to return from the result set. * @return SelectQueryInterface * The called object. diff --git a/includes/database/sqlite/database.inc b/includes/database/sqlite/database.inc index cf3b9551f..0fc0b5528 100644 --- a/includes/database/sqlite/database.inc +++ b/includes/database/sqlite/database.inc @@ -71,12 +71,8 @@ class DatabaseConnection_sqlite extends DatabaseConnection { )); // Attach one database for each registered prefix. - $prefixes = &$this->prefixes; - if (!empty($this->defaultPrefix)) { - // Add in the default prefix, which is also attached. - $prefixes[] = &$this->defaultPrefix; - } - foreach ($this->prefixes as $table => &$prefix) { + $prefixes = $this->prefixes; + foreach ($prefixes as $table => &$prefix) { // Empty prefix means query the main database -- no need to attach anything. if (!empty($prefix)) { // Only attach the database once. @@ -90,6 +86,8 @@ class DatabaseConnection_sqlite extends DatabaseConnection { $prefix .= '.'; } } + // Regenerate the prefixes replacement table. + $this->setPrefix($prefixes); // Detect support for SAVEPOINT. $version = $this->query('SELECT sqlite_version()')->fetchField(); @@ -240,7 +238,9 @@ class DatabaseConnection_sqlite extends DatabaseConnection { // Generate a new temporary table name and protect it from prefixing. // SQLite requires that temporary tables to be non-qualified. $tablename = $this->generateTemporaryTableName(); - $this->prefixes[$tablename] = ''; + $prefixes = $this->prefixes; + $prefixes[$tablename] = ''; + $this->setPrefix($prefixes); $this->query(preg_replace('/^SELECT/i', 'CREATE TEMPORARY TABLE ' . $tablename . ' AS SELECT', $query), $args, $options); return $tablename; diff --git a/includes/entity.inc b/includes/entity.inc index a3cdf7417..9ee7889cf 100644 --- a/includes/entity.inc +++ b/includes/entity.inc @@ -39,7 +39,8 @@ interface DrupalEntityControllerInterface { * An array of conditions in the form 'field' => $value. * * @return - * An array of entity objects indexed by their ids. + * An array of entity objects indexed by their ids. When no results are + * found, an empty array is returned. */ public function load($ids = array(), $conditions = array()); } @@ -650,7 +651,11 @@ class EntityFieldQuery { */ public function fieldCondition($field, $column = NULL, $value = NULL, $operator = NULL, $delta_group = NULL, $language_group = NULL) { if (is_scalar($field)) { - $field = field_info_field($field); + $field_definition = field_info_field($field); + if (empty($field_definition)) { + throw new EntityFieldQueryException(t('Unknown field: @field_name', array('@field_name' => $field))); + } + $field = $field_definition; } // Ensure the same index is used for fieldConditions as for fields. $index = count($this->fields); @@ -752,7 +757,11 @@ class EntityFieldQuery { */ public function fieldOrderBy($field, $column, $direction = 'ASC') { if (is_scalar($field)) { - $field = field_info_field($field); + $field_definition = field_info_field($field); + if (empty($field_definition)) { + throw new EntityFieldQueryException(t('Unknown field: @field_name', array('@field_name' => $field))); + } + $field = $field_definition; } // Save the index used for the new field, for later use in field storage. $index = count($this->fields); diff --git a/includes/errors.inc b/includes/errors.inc index 3a97b6daa..be7242856 100644 --- a/includes/errors.inc +++ b/includes/errors.inc @@ -172,9 +172,9 @@ function error_displayable($error = NULL) { * Log a PHP error or exception, display an error page in fatal cases. * * @param $error - * An array with the following keys: %type, !message, %function, %file, %line. - * All the parameters are plain-text, exception message, which needs to be - * a safe HTML string. + * 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. * @param $fatal * TRUE if the error is fatal. */ diff --git a/includes/file.inc b/includes/file.inc index 6dc7f88b3..73e75cd4f 100644 --- a/includes/file.inc +++ b/includes/file.inc @@ -737,8 +737,7 @@ function file_usage_delete(stdClass $file, $module, $type = NULL, $id = NULL, $c * A file object. * @param $destination * A string containing the destination that $source should be copied to. - * This must be a stream wrapper URI. If this value is omitted, Drupal's - * default files scheme will be used, usually "public://". + * This must be a stream wrapper URI. * @param $replace * Replace behavior when the destination file already exists: * - FILE_EXISTS_REPLACE - Replace the existing file. If a managed file with @@ -967,8 +966,7 @@ function file_destination($destination, $replace) { * A file object. * @param $destination * A string containing the destination that $source should be moved to. - * This must be a stream wrapper URI. If this value is omitted, Drupal's - * default files scheme will be used, usually "public://". + * This must be a stream wrapper URI. * @param $replace * Replace behavior when the destination file already exists: * - FILE_EXISTS_REPLACE - Replace the existing file. If a managed file with @@ -1755,9 +1753,9 @@ function file_validate_image_resolution(stdClass $file, $maximum_dimensions = 0, * @param $data * A string containing the contents of the file. * @param $destination - * A string containing the destination URI. - * This must be a stream wrapper URI. If this value is omitted, Drupal's - * default files scheme will be used, usually "public://". + * A string containing the destination URI. 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://". * @param $replace * Replace behavior when the destination file already exists: * - FILE_EXISTS_REPLACE - Replace the existing file. If a managed file with @@ -1823,10 +1821,9 @@ function file_save_data($data, $destination = NULL, $replace = FILE_EXISTS_RENAM * @param $data * A string containing the contents of the file. * @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 is saved using Drupal's - * default files scheme, usually "public://". + * 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://". * @param $replace * Replace behavior when the destination file already exists: * - FILE_EXISTS_REPLACE - Replace the existing file. @@ -2153,8 +2150,7 @@ function drupal_unlink($uri, $context = NULL) { * @see http://drupal.org/node/515192 * * @param $uri - * A string containing the URI to verify. If this value is omitted, - * Drupal's public files directory will be used [public://]. + * A string containing the URI to verify. * * @return * The absolute pathname, or FALSE on failure. diff --git a/includes/form.inc b/includes/form.inc index a337b03d1..38ef41cf0 100644 --- a/includes/form.inc +++ b/includes/form.inc @@ -104,8 +104,10 @@ * function via $form_state. This is commonly used for wizard-style * multi-step forms, add-more buttons, and the like. For further information * see drupal_build_form(). - * - 'redirect': a URL that will be used to redirect the form on submission. - * See drupal_redirect_form() for complete information. + * - 'redirect': $form_state['redirect'] is used to redirect the form on + * submission. It may either be a string containing the destination URL, or + * an array of arguments compatible with drupal_goto(). See + * drupal_redirect_form() for complete information. * - 'storage': $form_state['storage'] is not a special key, and no specific * support is provided for it in the Form API, but by tradition it was * the location where application-specific data was stored for communication @@ -1128,10 +1130,28 @@ function drupal_validate_form($form_id, &$form, &$form_state) { * Redirects the user to a URL after a form has been processed. * * After a form was executed, the data in $form_state controls whether the form - * is redirected. By default, we redirect to a new destination page. The path of - * the destination page can be set in $form_state['redirect']. If that is not - * set, the user is redirected to the current page to display a fresh, - * unpopulated copy of the form. + * is redirected. By default, we redirect to a new destination page. The path + * of the destination page can be set in $form_state['redirect'], as either a + * string containing the destination or an array of arguments compatible with + * drupal_goto(). If that is not set, the user is redirected to the current + * page to display a fresh, unpopulated copy of the form. + * + * For example, to redirect to 'node': + * @code + * $form_state['redirect'] = 'node'; + * @endcode + * Or to redirect to 'node/123?foo=bar#baz': + * @code + * $form_state['redirect'] = array( + * 'node/123', + * array( + * 'query' => array( + * 'foo' => 'bar', + * ), + * 'fragment' => 'baz', + * ), + * ); + * @endcode * * There are several triggers that may prevent a redirection though: * - If $form_state['redirect'] is FALSE, a form builder function or form @@ -4074,6 +4094,8 @@ function _form_set_class(&$element, $class = array()) { * Sample 'finished' callback: * @code * function batch_test_finished($success, $results, $operations) { + * // The 'success' parameter means no fatal PHP errors were detected. All + * // other error management should be handled using 'results'. * if ($success) { * $message = format_plural(count($results), 'One post processed.', '@count posts processed.'); * } diff --git a/includes/install.inc b/includes/install.inc index d22f4f9cb..089cdee8f 100644 --- a/includes/install.inc +++ b/includes/install.inc @@ -570,7 +570,7 @@ abstract class DatabaseTasks { } /** - * @class Exception class used to throw error if the DatabaseInstaller fails. + * Exception thrown if the database installer fails. */ class DatabaseTaskException extends Exception { } diff --git a/includes/locale.inc b/includes/locale.inc index 578b1b3c6..6154cf3c3 100644 --- a/includes/locale.inc +++ b/includes/locale.inc @@ -1863,7 +1863,7 @@ function _locale_rebuild_js($langcode = NULL) { // Construct the array for JavaScript translations. // Only add strings with a translation to the translations array. - $result = db_query("SELECT s.lid, s.source, t.translation FROM {locales_source} s INNER JOIN {locales_target} t ON s.lid = t.lid AND t.language = :language WHERE s.location LIKE '%.js%' AND s.textgroup = :textgroup AND t.translation IS NOT NULL", array(':language' => $language->language, ':textgroup' => 'default')); + $result = db_query("SELECT s.lid, s.source, t.translation FROM {locales_source} s INNER JOIN {locales_target} t ON s.lid = t.lid AND t.language = :language WHERE s.location LIKE '%.js%' AND s.textgroup = :textgroup", array(':language' => $language->language, ':textgroup' => 'default')); $translations = array(); foreach ($result as $data) { |