summaryrefslogtreecommitdiff
path: root/includes
diff options
context:
space:
mode:
Diffstat (limited to 'includes')
-rw-r--r--includes/ajax.inc39
-rw-r--r--includes/authorize.inc4
-rw-r--r--includes/bootstrap.inc123
-rw-r--r--includes/common.inc7
-rw-r--r--includes/database/mysql/database.inc6
-rw-r--r--includes/entity.inc105
-rw-r--r--includes/errors.inc2
-rw-r--r--includes/file.inc55
-rw-r--r--includes/form.inc13
-rw-r--r--includes/mail.inc4
-rw-r--r--includes/menu.inc62
-rw-r--r--includes/password.inc2
-rw-r--r--includes/stream_wrappers.inc6
13 files changed, 293 insertions, 135 deletions
diff --git a/includes/ajax.inc b/includes/ajax.inc
index 41c69832a..d70808efe 100644
--- a/includes/ajax.inc
+++ b/includes/ajax.inc
@@ -143,6 +143,21 @@
* - #ajax['event']: The JavaScript event to respond to. This is normally
* selected automatically for the type of form widget being used, and
* is only needed if you need to override the default behavior.
+ * - #ajax['prevent']: A JavaScript event to prevent when 'event' is triggered.
+ * Defaults to 'click' for #ajax on #type 'submit', 'button', and
+ * 'image_button'. Multiple events may be specified separated by spaces.
+ * For example, when binding #ajax behaviors to form buttons, pressing the
+ * ENTER key within a textfield triggers the 'click' event of the form's first
+ * submit button. Triggering Ajax in this situation leads to problems, like
+ * breaking autocomplete textfields. Because of that, Ajax behaviors are bound
+ * to the 'mousedown' event on form buttons by default. However, binding to
+ * 'mousedown' rather than 'click' means that it is possible to trigger a
+ * click by pressing the mouse, holding the mouse button down until the Ajax
+ * request is complete and the button is re-enabled, and then releasing the
+ * mouse button. For this case, 'prevent' can be set to 'click', so an
+ * additional event handler is bound to prevent such a click from triggering a
+ * non-Ajax form submission. This also prevents a textfield's ENTER press
+ * triggering a button's non-Ajax form submission behavior.
* - #ajax['method']: The jQuery method to use to place the new HTML.
* Defaults to 'replaceWith'. May be: 'replaceWith', 'append', 'prepend',
* 'before', 'after', or 'html'. See the
@@ -591,6 +606,7 @@ function ajax_process_form($element, &$form_state) {
* An associative array containing the properties of the element.
* Properties used:
* - #ajax['event']
+ * - #ajax['prevent']
* - #ajax['path']
* - #ajax['options']
* - #ajax['wrapper']
@@ -619,13 +635,26 @@ function ajax_pre_render_element($element) {
case 'submit':
case 'button':
case 'image_button':
- // Use the mousedown instead of the click event because form
- // submission via pressing the enter key triggers a click event on
- // submit inputs, inappropriately triggering Ajax behaviors.
+ // Pressing the ENTER key within a textfield triggers the click event of
+ // the form's first submit button. Triggering Ajax in this situation
+ // leads to problems, like breaking autocomplete textfields, so we bind
+ // to mousedown instead of click.
+ // @see http://drupal.org/node/216059
$element['#ajax']['event'] = 'mousedown';
- // Attach an additional event handler so that Ajax behaviors
- // can be triggered still via keyboard input.
+ // Retain keyboard accessibility by setting 'keypress'. This causes
+ // ajax.js to trigger 'event' when SPACE or ENTER are pressed while the
+ // button has focus.
$element['#ajax']['keypress'] = TRUE;
+ // Binding to mousedown rather than click means that it is possible to
+ // trigger a click by pressing the mouse, holding the mouse button down
+ // until the Ajax request is complete and the button is re-enabled, and
+ // then releasing the mouse button. Set 'prevent' so that ajax.js binds
+ // an additional handler to prevent such a click from triggering a
+ // non-Ajax form submission. This also prevents a textfield's ENTER
+ // press triggering this button's non-Ajax form submission behavior.
+ if (!isset($element['#ajax']['prevent'])) {
+ $element['#ajax']['prevent'] = 'click';
+ }
break;
case 'password':
diff --git a/includes/authorize.inc b/includes/authorize.inc
index 3617d7d03..852860413 100644
--- a/includes/authorize.inc
+++ b/includes/authorize.inc
@@ -193,7 +193,7 @@ function _authorize_filetransfer_connection_settings_set_defaults(&$element, $ke
function authorize_filetransfer_form_validate($form, &$form_state) {
// Only validate the form if we have collected all of the user input and are
// ready to proceed with updating or installing.
- if ($form_state['clicked_button']['#name'] != 'process_updates') {
+ if ($form_state['triggering_element']['#name'] != 'process_updates') {
return;
}
@@ -224,7 +224,7 @@ function authorize_filetransfer_form_validate($form, &$form_state) {
*/
function authorize_filetransfer_form_submit($form, &$form_state) {
global $base_url;
- switch ($form_state['clicked_button']['#name']) {
+ switch ($form_state['triggering_element']['#name']) {
case 'process_updates':
// Save the connection settings to the DB.
diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc
index f54ce92a3..e9837da30 100644
--- a/includes/bootstrap.inc
+++ b/includes/bootstrap.inc
@@ -38,94 +38,70 @@ define('CACHE_PERMANENT', 0);
define('CACHE_TEMPORARY', -1);
/**
- * Log message severity -- Emergency: system is unusable.
+ * @defgroup logging_severity_levels Logging severity levels
+ * @{
+ * Logging severity levels as defined in RFC 3164.
*
* The WATCHDOG_* constant definitions correspond to the logging severity levels
- * defined in RFC 3164, section 4.1.1: http://www.faqs.org/rfcs/rfc3164.html
- *
+ * defined in RFC 3164, section 4.1.1. PHP supplies predefined LOG_* constants
+ * for use in the syslog() function, but their values on Windows builds do not
+ * correspond to RFC 3164. The associated PHP bug report was closed with the
+ * comment, "And it's also not a bug, as Windows just have less log levels,"
+ * and "So the behavior you're seeing is perfectly normal."
+ *
+ * @see http://www.faqs.org/rfcs/rfc3164.html
+ * @see http://bugs.php.net/bug.php?id=18090
+ * @see http://php.net/manual/function.syslog.php
+ * @see http://php.net/manual/network.constants.php
* @see watchdog()
* @see watchdog_severity_levels()
*/
+
+/**
+ * Log message severity -- Emergency: system is unusable.
+ */
define('WATCHDOG_EMERGENCY', 0);
/**
* Log message severity -- Alert: action must be taken immediately.
- *
- * The WATCHDOG_* constant definitions correspond to the logging severity levels
- * defined in RFC 3164, section 4.1.1: http://www.faqs.org/rfcs/rfc3164.html
- *
- * @see watchdog()
- * @see watchdog_severity_levels()
*/
define('WATCHDOG_ALERT', 1);
/**
* Log message severity -- Critical: critical conditions.
- *
- * The WATCHDOG_* constant definitions correspond to the logging severity levels
- * defined in RFC 3164, section 4.1.1: http://www.faqs.org/rfcs/rfc3164.html
- *
- * @see watchdog()
- * @see watchdog_severity_levels()
*/
define('WATCHDOG_CRITICAL', 2);
/**
* Log message severity -- Error: error conditions.
- *
- * The WATCHDOG_* constant definitions correspond to the logging severity levels
- * defined in RFC 3164, section 4.1.1: http://www.faqs.org/rfcs/rfc3164.html
- *
- * @see watchdog()
- * @see watchdog_severity_levels()
*/
define('WATCHDOG_ERROR', 3);
/**
* Log message severity -- Warning: warning conditions.
- *
- * The WATCHDOG_* constant definitions correspond to the logging severity levels
- * defined in RFC 3164, section 4.1.1: http://www.faqs.org/rfcs/rfc3164.html
- *
- * @see watchdog()
- * @see watchdog_severity_levels()
*/
define('WATCHDOG_WARNING', 4);
/**
* Log message severity -- Notice: normal but significant condition.
- *
- * The WATCHDOG_* constant definitions correspond to the logging severity levels
- * defined in RFC 3164, section 4.1.1: http://www.faqs.org/rfcs/rfc3164.html
- *
- * @see watchdog()
- * @see watchdog_severity_levels()
*/
define('WATCHDOG_NOTICE', 5);
/**
* Log message severity -- Informational: informational messages.
- *
- * The WATCHDOG_* constant definitions correspond to the logging severity levels
- * defined in RFC 3164, section 4.1.1: http://www.faqs.org/rfcs/rfc3164.html
- *
- * @see watchdog()
- * @see watchdog_severity_levels()
*/
define('WATCHDOG_INFO', 6);
/**
* Log message severity -- Debug: debug-level messages.
- *
- * The WATCHDOG_* constant definitions correspond to the logging severity levels
- * defined in RFC 3164, section 4.1.1: http://www.faqs.org/rfcs/rfc3164.html
- *
- * @see watchdog()
- * @see watchdog_severity_levels()
*/
define('WATCHDOG_DEBUG', 7);
/**
+ * @} End of "defgroup logging_severity_levels".
+ */
+
+/**
* First bootstrap phase: initialize configuration.
*/
define('DRUPAL_BOOTSTRAP_CONFIGURATION', 0);
@@ -216,8 +192,11 @@ define('LANGUAGE_RTL', 1);
/**
* For convenience, define a short form of the request time global.
+ *
+ * REQUEST_TIME is a float with microseconds since PHP 5.4.0, but float
+ * timestamps confuses most of the PHP functions (including date_create()).
*/
-define('REQUEST_TIME', $_SERVER['REQUEST_TIME']);
+define('REQUEST_TIME', (int) $_SERVER['REQUEST_TIME']);
/**
* Flag for drupal_set_title(); text is not sanitized, so run check_plain().
@@ -311,31 +290,30 @@ function timer_stop($name) {
}
/**
- * Find the appropriate configuration directory.
+ * Finds the appropriate configuration directory.
*
- * Try finding a matching configuration directory by stripping the website's
+ * Finds a matching configuration directory by stripping the website's
* hostname from left to right and pathname from right to left. The first
- * configuration file found will be used; the remaining will ignored. If no
- * configuration file is found, return a default value '$confdir/default'.
+ * configuration file found will be used and the remaining ones will be ignored.
+ * If no configuration file is found, return a default value '$confdir/default'.
*
- * Example for a fictitious site installed at
- * http://www.drupal.org:8080/mysite/test/ the 'settings.php' is searched in
- * the following directories:
+ * With a site located at http://www.example.com:8080/mysite/test/, the file,
+ * settings.php, is searched for in the following directories:
*
- * 1. $confdir/8080.www.drupal.org.mysite.test
- * 2. $confdir/www.drupal.org.mysite.test
- * 3. $confdir/drupal.org.mysite.test
- * 4. $confdir/org.mysite.test
+ * 1. $confdir/8080.www.example.com.mysite.test
+ * 2. $confdir/www.example.com.mysite.test
+ * 3. $confdir/example.com.mysite.test
+ * 4. $confdir/com.mysite.test
*
- * 5. $confdir/8080.www.drupal.org.mysite
- * 6. $confdir/www.drupal.org.mysite
- * 7. $confdir/drupal.org.mysite
- * 8. $confdir/org.mysite
+ * 5. $confdir/8080.www.example.com.mysite
+ * 6. $confdir/www.example.com.mysite
+ * 7. $confdir/example.com.mysite
+ * 8. $confdir/com.mysite
*
- * 9. $confdir/8080.www.drupal.org
- * 10. $confdir/www.drupal.org
- * 11. $confdir/drupal.org
- * 12. $confdir/org
+ * 9. $confdir/8080.www.example.com
+ * 10. $confdir/www.example.com
+ * 11. $confdir/example.com
+ * 12. $confdir/com
*
* 13. $confdir/default
*
@@ -343,18 +321,18 @@ function timer_stop($name) {
* prior to scanning for directories. It should define an associative array
* named $sites, which maps domains to directories. It should be in the form
* of:
- *
+ * @code
* $sites = array(
* 'The url to alias' => 'A directory within the sites directory'
* );
- *
+ * @endcode
* For example:
- *
+ * @code
* $sites = array(
* 'devexample.com' => 'example.com',
* 'localhost.example' => 'example.com',
* );
- *
+ * @endcode
* The above array will cause Drupal to look for a directory named
* "example.com" in the sites directory whenever a request comes from
* "example.com", "devexample.com", or "localhost/example". That is useful
@@ -363,14 +341,15 @@ function timer_stop($name) {
* (files, system table, etc.) this will ensure the paths are correct while
* accessed on development servers.
*
- * @param $require_settings
+ * @param bool $require_settings
* Only configuration directories with an existing settings.php file
* will be recognized. Defaults to TRUE. During initial installation,
* this is set to FALSE so that Drupal can detect a matching directory,
* then create a new settings.php file in it.
- * @param reset
+ * @param bool $reset
* Force a full search for matching directories even if one had been
- * found previously.
+ * found previously. Defaults to FALSE.
+ *
* @return
* The path of the matching directory.
*/
diff --git a/includes/common.inc b/includes/common.inc
index 9b582c446..b8cd55f5c 100644
--- a/includes/common.inc
+++ b/includes/common.inc
@@ -2255,7 +2255,7 @@ function drupal_http_header_attributes(array $attributes = array()) {
* An associative array of key-value pairs to be converted to attributes.
*
* @return
- * A string ready for insertion in a tag.
+ * A string ready for insertion in a tag (starts with a space).
*
* @ingroup sanitization
*/
@@ -2291,7 +2291,9 @@ function drupal_attributes(array $attributes = array()) {
* to work in a call to drupal_attributes($options['attributes']).
* - 'html' (default FALSE): Whether $text is HTML or just plain-text. For
* example, to make an image tag into a link, this must be set to TRUE, or
- * you will see the escaped HTML image tag.
+ * you will see the escaped HTML image tag. $text is not sanitized if
+ * 'html' is TRUE. The calling function must ensure that $text is already
+ * safe.
* - 'language': An optional language object. If the path being linked to is
* internal to the site, $options['language'] is used to determine whether
* the link is "active", or pointing to the current page (the language as
@@ -7047,6 +7049,7 @@ function drupal_parse_info_format($data) {
* Array of the possible severity levels for log messages.
*
* @see watchdog()
+ * @ingroup logging_severity_levels
*/
function watchdog_severity_levels() {
return array(
diff --git a/includes/database/mysql/database.inc b/includes/database/mysql/database.inc
index bc31feaaf..157cbfa56 100644
--- a/includes/database/mysql/database.inc
+++ b/includes/database/mysql/database.inc
@@ -169,10 +169,8 @@ class DatabaseConnection_mysql extends DatabaseConnection {
// 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') {
+ // succeed for MySQL error code 1305 ("SAVEPOINT does not exist").
+ if ($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');
diff --git a/includes/entity.inc b/includes/entity.inc
index 9ee7889cf..f363c3113 100644
--- a/includes/entity.inc
+++ b/includes/entity.inc
@@ -458,6 +458,21 @@ class EntityFieldQuery {
public $fieldConditions = array();
/**
+ * List of field meta conditions (language and delta).
+ *
+ * Field conditions operate on columns specified by hook_field_schema(),
+ * the meta conditions operate on columns added by the system: delta
+ * and language. These can not be mixed with the field conditions because
+ * field columns can have any name including delta and language.
+ *
+ * @var array
+ *
+ * @see EntityFieldQuery::fieldLanguageCondition()
+ * @see EntityFieldQuery::fielDeltaCondition()
+ */
+ public $fieldMetaConditions = array();
+
+ /**
* List of property conditions.
*
* @var array
@@ -613,6 +628,90 @@ class EntityFieldQuery {
/**
* Adds a condition on field values.
*
+ * @param $type
+ * The condition array the given conditions should be added to.
+ * @param $field
+ * Either a field name or a field array.
+ * @param $column
+ * The column that should hold the value to be matched.
+ * @param $value
+ * The value to test the column value against.
+ * @param $operator
+ * The operator to be used to test the given value.
+ * @param $delta_group
+ * An arbitrary identifier: conditions in the same group must have the same
+ * $delta_group.
+ * @param $language_group
+ * An arbitrary identifier: conditions in the same group must have the same
+ * $language_group.
+ *
+ * @return EntityFieldQuery
+ * The called object.
+ *
+ * @see EntityFieldQuery::addFieldCondition
+ * @see EntityFieldQuery::deleted
+ */
+ public function fieldCondition($field, $column = NULL, $value = NULL, $operator = NULL, $delta_group = NULL, $language_group = NULL) {
+ return $this->addFieldCondition($this->fieldConditions, $field, $column, $value, $operator, $delta_group, $language_group);
+ }
+
+ /**
+ * Adds a condition on the field language column.
+ *
+ * @param $field
+ * Either a field name or a field array.
+ * @param $value
+ * The value to test the column value against.
+ * @param $operator
+ * The operator to be used to test the given value.
+ * @param $delta_group
+ * An arbitrary identifier: conditions in the same group must have the same
+ * $delta_group.
+ * @param $language_group
+ * An arbitrary identifier: conditions in the same group must have the same
+ * $language_group.
+ *
+ * @return EntityFieldQuery
+ * The called object.
+ *
+ * @see EntityFieldQuery::addFieldCondition
+ * @see EntityFieldQuery::deleted
+ */
+ public function fieldLanguageCondition($field, $value = NULL, $operator = NULL, $delta_group = NULL, $language_group = NULL) {
+ return $this->addFieldCondition($this->fieldMetaConditions, $field, 'language', $value, $operator, $delta_group, $language_group);
+ }
+
+ /**
+ * Adds a condition on the field delta column.
+ *
+ * @param $field
+ * Either a field name or a field array.
+ * @param $value
+ * The value to test the column value against.
+ * @param $operator
+ * The operator to be used to test the given value.
+ * @param $delta_group
+ * An arbitrary identifier: conditions in the same group must have the same
+ * $delta_group.
+ * @param $language_group
+ * An arbitrary identifier: conditions in the same group must have the same
+ * $language_group.
+ *
+ * @return EntityFieldQuery
+ * The called object.
+ *
+ * @see EntityFieldQuery::addFieldCondition
+ * @see EntityFieldQuery::deleted
+ */
+ public function fieldDeltaCondition($field, $value = NULL, $operator = NULL, $delta_group = NULL, $language_group = NULL) {
+ return $this->addFieldCondition($this->fieldMetaConditions, $field, 'delta', $value, $operator, $delta_group, $language_group);
+ }
+
+ /**
+ * Adds the given condition to the proper condition array.
+ *
+ * @param $conditions
+ * A reference to an array of conditions.
* @param $field
* Either a field name or a field array.
* @param $column
@@ -649,7 +748,7 @@ class EntityFieldQuery {
* @return EntityFieldQuery
* The called object.
*/
- public function fieldCondition($field, $column = NULL, $value = NULL, $operator = NULL, $delta_group = NULL, $language_group = NULL) {
+ protected function addFieldCondition(&$conditions, $field, $column = NULL, $value = NULL, $operator = NULL, $delta_group = NULL, $language_group = NULL) {
if (is_scalar($field)) {
$field_definition = field_info_field($field);
if (empty($field_definition)) {
@@ -657,11 +756,11 @@ class EntityFieldQuery {
}
$field = $field_definition;
}
- // Ensure the same index is used for fieldConditions as for fields.
+ // Ensure the same index is used for field conditions as for fields.
$index = count($this->fields);
$this->fields[$index] = $field;
if (isset($column)) {
- $this->fieldConditions[$index] = array(
+ $conditions[$index] = array(
'field' => $field,
'column' => $column,
'value' => $value,
diff --git a/includes/errors.inc b/includes/errors.inc
index be7242856..bd31bebed 100644
--- a/includes/errors.inc
+++ b/includes/errors.inc
@@ -24,6 +24,8 @@ define('ERROR_REPORTING_DISPLAY_ALL', 2);
* Map PHP error constants to watchdog severity levels.
* The error constants are documented at
* http://php.net/manual/en/errorfunc.constants.php
+ *
+ * @ingroup logging_severity_levels
*/
function drupal_error_levels() {
$types = array(
diff --git a/includes/file.inc b/includes/file.inc
index 73e75cd4f..19420ca37 100644
--- a/includes/file.inc
+++ b/includes/file.inc
@@ -755,7 +755,12 @@ function file_usage_delete(stdClass $file, $module, $type = NULL, $id = NULL, $c
*/
function file_copy(stdClass $source, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
if (!file_valid_uri($destination)) {
- watchdog('file', 'File %file (%realpath) could not be copied, because the destination %destination is invalid. This is often caused by improper use of file_copy() or a missing stream wrapper.', array('%file' => $source->uri, '%realpath' => drupal_realpath($source->uri), '%destination' => $destination));
+ if (($realpath = drupal_realpath($source->uri)) !== FALSE) {
+ watchdog('file', 'File %file (%realpath) could not be copied, because the destination %destination is invalid. This is often caused by improper use of file_copy() or a missing stream wrapper.', array('%file' => $source->uri, '%realpath' => $realpath, '%destination' => $destination));
+ }
+ else {
+ watchdog('file', 'File %file could not be copied, because the destination %destination is invalid. This is often caused by improper use of file_copy() or a missing stream wrapper.', array('%file' => $source->uri, '%destination' => $destination));
+ }
drupal_set_message(t('The specified file %file could not be copied, because the destination is invalid. More information is available in the system log.', array('%file' => $source->uri)), 'error');
return FALSE;
}
@@ -847,11 +852,15 @@ function file_unmanaged_copy($source, $destination = NULL, $replace = FILE_EXIST
$original_destination = $destination;
// Assert that the source file actually exists.
- $source = drupal_realpath($source);
if (!file_exists($source)) {
// @todo Replace drupal_set_message() calls with exceptions instead.
drupal_set_message(t('The specified file %file could not be copied, because no file by that name exists. Please check that you supplied the correct filename.', array('%file' => $original_source)), 'error');
- watchdog('file', 'File %file (%realpath) could not be copied because it does not exist.', array('%file' => $original_source, '%realpath' => drupal_realpath($original_source)));
+ if (($realpath = drupal_realpath($original_source)) !== FALSE) {
+ watchdog('file', 'File %file (%realpath) could not be copied because it does not exist.', array('%file' => $original_source, '%realpath' => $realpath));
+ }
+ else {
+ watchdog('file', 'File %file could not be copied because it does not exist.', array('%file' => $original_source));
+ }
return FALSE;
}
@@ -871,7 +880,7 @@ function file_unmanaged_copy($source, $destination = NULL, $replace = FILE_EXIST
$dirname = drupal_dirname($destination);
if (!file_prepare_directory($dirname)) {
// The destination is not valid.
- watchdog('file', 'File %file could not be copied, because the destination directory %destination is not configured correctly.', array('%file' => $original_source, '%destination' => drupal_realpath($dirname)));
+ watchdog('file', 'File %file could not be copied, because the destination directory %destination is not configured correctly.', array('%file' => $original_source, '%destination' => $dirname));
drupal_set_message(t('The specified file %file could not be copied, because the destination directory is not properly configured. This may be caused by a problem with file or directory permissions. More information is available in the system log.', array('%file' => $original_source)), 'error');
return FALSE;
}
@@ -881,12 +890,14 @@ function file_unmanaged_copy($source, $destination = NULL, $replace = FILE_EXIST
$destination = file_destination($destination, $replace);
if ($destination === FALSE) {
drupal_set_message(t('The file %file could not be copied because a file by that name already exists in the destination directory.', array('%file' => $original_source)), 'error');
- watchdog('file', 'File %file could not be copied because a file by that name already exists in the destination directory (%directory)', array('%file' => $original_source, '%destination' => drupal_realpath($destination)));
+ watchdog('file', 'File %file could not be copied because a file by that name already exists in the destination directory (%directory)', array('%file' => $original_source, '%destination' => $destination));
return FALSE;
}
// Assert that the source and destination filenames are not the same.
- if (drupal_realpath($source) == drupal_realpath($destination)) {
+ $real_source = drupal_realpath($source);
+ $real_destination = drupal_realpath($destination);
+ if ($source == $destination || ($real_source !== FALSE) && ($real_source == $real_destination)) {
drupal_set_message(t('The specified file %file was not copied because it would overwrite itself.', array('%file' => $source)), 'error');
watchdog('file', 'File %file could not be copied because it would overwrite itself.', array('%file' => $source));
return FALSE;
@@ -895,7 +906,7 @@ function file_unmanaged_copy($source, $destination = NULL, $replace = FILE_EXIST
file_ensure_htaccess();
// Perform the copy operation.
if (!@copy($source, $destination)) {
- watchdog('file', 'The specified file %file could not be copied to %destination.', array('%file' => $source, '%destination' => drupal_realpath($destination)), WATCHDOG_ERROR);
+ watchdog('file', 'The specified file %file could not be copied to %destination.', array('%file' => $source, '%destination' => $destination), WATCHDOG_ERR);
return FALSE;
}
@@ -986,7 +997,12 @@ function file_destination($destination, $replace) {
*/
function file_move(stdClass $source, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
if (!file_valid_uri($destination)) {
- watchdog('file', 'File %file (%realpath) could not be moved, because the destination %destination is invalid. This may be caused by improper use of file_move() or a missing stream wrapper.', array('%file' => $source->uri, '%realpath' => drupal_realpath($source->uri), '%destination' => $destination));
+ if (($realpath = drupal_realpath($source->uri)) !== FALSE) {
+ watchdog('file', 'File %file (%realpath) could not be moved, because the destination %destination is invalid. This may be caused by improper use of file_move() or a missing stream wrapper.', array('%file' => $source->uri, '%realpath' => $realpath, '%destination' => $destination));
+ }
+ else {
+ watchdog('file', 'File %file could not be moved, because the destination %destination is invalid. This may be caused by improper use of file_move() or a missing stream wrapper.', array('%file' => $source->uri, '%destination' => $destination));
+ }
drupal_set_message(t('The specified file %file could not be moved, because the destination is invalid. More information is available in the system log.', array('%file' => $source->uri)), 'error');
return FALSE;
}
@@ -1212,7 +1228,12 @@ function file_create_filename($basename, $directory) {
*/
function file_delete(stdClass $file, $force = FALSE) {
if (!file_valid_uri($file->uri)) {
- watchdog('file', 'File %file (%realpath) could not be deleted because it is not a valid URI. This may be caused by improper use of file_delete() or a missing stream wrapper.', array('%file' => $file->uri, '%realpath' => drupal_realpath($file->uri)));
+ if (($realpath = drupal_realpath($file->uri)) !== FALSE) {
+ watchdog('file', 'File %file (%realpath) could not be deleted because it is not a valid URI. This may be caused by improper use of file_delete() or a missing stream wrapper.', array('%file' => $file->uri, '%realpath' => $realpath));
+ }
+ else {
+ watchdog('file', 'File %file could not be deleted because it is not a valid URI. This may be caused by improper use of file_delete() or a missing stream wrapper.', array('%file' => $file->uri));
+ }
drupal_set_message(t('The specified file %file could not be deleted, because it is not a valid URI. More information is available in the system log.', array('%file' => $file->uri)), 'error');
return FALSE;
}
@@ -1256,8 +1277,6 @@ function file_delete(stdClass $file, $force = FALSE) {
* @see file_unmanaged_delete_recursive()
*/
function file_unmanaged_delete($path) {
- // Resolve streamwrapper URI to local path.
- $path = drupal_realpath($path);
if (is_dir($path)) {
watchdog('file', '%path is a directory and cannot be removed using file_unmanaged_delete().', array('%path' => $path), WATCHDOG_ERROR);
return FALSE;
@@ -1299,8 +1318,6 @@ function file_unmanaged_delete($path) {
* @see file_unmanaged_delete()
*/
function file_unmanaged_delete_recursive($path) {
- // Resolve streamwrapper URI to local path.
- $path = drupal_realpath($path);
if (is_dir($path)) {
$dir = dir($path);
while (($entry = $dir->read()) !== FALSE) {
@@ -2331,11 +2348,9 @@ function file_directory_temp() {
if (substr(PHP_OS, 0, 3) == 'WIN') {
$directories[] = 'c:\\windows\\temp';
$directories[] = 'c:\\winnt\\temp';
- $path_delimiter = '\\';
}
else {
$directories[] = '/tmp';
- $path_delimiter = '/';
}
// PHP may be able to find an alternative tmp directory.
// This function exists in PHP 5 >= 5.2.1, but Drupal
@@ -2352,8 +2367,14 @@ function file_directory_temp() {
}
if (empty($temporary_directory)) {
- // If no directory has been found default to 'files/tmp' or 'files\\tmp'.
- $temporary_directory = variable_get('file_public_path', conf_path() . '/files') . $path_delimiter . 'tmp';
+ // If no directory has been found default to 'files/tmp'.
+ $temporary_directory = variable_get('file_public_path', conf_path() . '/files') . '/tmp';
+
+ // Windows accepts paths with either slash (/) or backslash (\), but will
+ // not accept a path which contains both a slash and a backslash. Since
+ // the 'file_public_path' variable may have either format, we sanitize
+ // everything to use slash which is supported on all platforms.
+ $temporary_directory = str_replace('\\', '/', $temporary_directory);
}
// Save the path of the discovered directory.
variable_set('file_temporary_path', $temporary_directory);
diff --git a/includes/form.inc b/includes/form.inc
index 38ef41cf0..8ee0a5a52 100644
--- a/includes/form.inc
+++ b/includes/form.inc
@@ -272,7 +272,8 @@ function drupal_get_form($form_id) {
* form submission may be found in drupal_redirect_form().
*
* @return
- * The rendered form or NULL, depending upon the $form_state flags that were set.
+ * The rendered form. This function may also perform a redirect and hence may
+ * not return at all, depending upon the $form_state flags that were set.
*
* @see drupal_redirect_form()
*/
@@ -995,6 +996,8 @@ function drupal_prepare_form($form_id, &$form, &$form_state) {
$form += array('#tree' => FALSE, '#parents' => array());
if (!isset($form['#validate'])) {
+ // Ensure that modules can rely on #validate being set.
+ $form['#validate'] = array();
// Check for a handler specific to $form_id.
if (function_exists($form_id . '_validate')) {
$form['#validate'][] = $form_id . '_validate';
@@ -1007,6 +1010,8 @@ function drupal_prepare_form($form_id, &$form, &$form_state) {
}
if (!isset($form['#submit'])) {
+ // Ensure that modules can rely on #submit being set.
+ $form['#submit'] = array();
// Check for a handler specific to $form_id.
if (function_exists($form_id . '_submit')) {
$form['#submit'][] = $form_id . '_submit';
@@ -2853,9 +2858,9 @@ function form_process_date($element) {
/**
* Validates the date type to stop dates like February 30, 2006.
*/
-function date_validate($form) {
- if (!checkdate($form['#value']['month'], $form['#value']['day'], $form['#value']['year'])) {
- form_error($form, t('The specified date is invalid.'));
+function date_validate($element) {
+ if (!checkdate($element['#value']['month'], $element['#value']['day'], $element['#value']['year'])) {
+ form_error($element, t('The specified date is invalid.'));
}
}
diff --git a/includes/mail.inc b/includes/mail.inc
index d2febed39..7272df972 100644
--- a/includes/mail.inc
+++ b/includes/mail.inc
@@ -430,7 +430,7 @@ function drupal_html_to_text($string, $allowed_tags = NULL) {
$indent[] = count($lists) ? ' "' : '>';
break;
case 'li':
- $indent[] = is_numeric($lists[0]) ? ' ' . $lists[0]++ . ') ' : ' * ';
+ $indent[] = isset($lists[0]) && is_numeric($lists[0]) ? ' ' . $lists[0]++ . ') ' : ' * ';
break;
case 'dd':
$indent[] = ' ';
@@ -509,7 +509,7 @@ function drupal_html_to_text($string, $allowed_tags = NULL) {
$chunk = $casing($chunk);
}
// Format it and apply the current indentation.
- $output .= drupal_wrap_mail($chunk, implode('', $indent));
+ $output .= drupal_wrap_mail($chunk, implode('', $indent)) . MAIL_LINE_ENDINGS;
// Remove non-quotation markers from indentation.
$indent = array_map('_drupal_html_to_text_clean', $indent);
}
diff --git a/includes/menu.inc b/includes/menu.inc
index cfd35c794..5582c452e 100644
--- a/includes/menu.inc
+++ b/includes/menu.inc
@@ -431,6 +431,11 @@ function menu_get_item($path = NULL, $router_item = NULL) {
$router_items[$path] = $router_item;
}
if (!isset($router_items[$path])) {
+ // Rebuild if we know it's needed, or if the menu masks are missing which
+ // occurs rarely, likely due to a race condition of multiple rebuilds.
+ if (variable_get('menu_rebuild_needed', FALSE) || !variable_get('menu_masks', array())) {
+ menu_rebuild();
+ }
$original_map = arg(NULL, $path);
// Since there is no limit to the length of $path, use a hash to keep it
@@ -490,11 +495,6 @@ function menu_execute_active_handler($path = NULL, $deliver = TRUE) {
// Only continue if the site status is not set.
if ($page_callback_result == MENU_SITE_ONLINE) {
- // Rebuild if we know it's needed, or if the menu masks are missing which
- // occurs rarely, likely due to a race condition of multiple rebuilds.
- if (variable_get('menu_rebuild_needed', FALSE) || !variable_get('menu_masks', array())) {
- menu_rebuild();
- }
if ($router_item = menu_get_item($path)) {
if ($router_item['access']) {
if ($router_item['include_file']) {
@@ -2696,19 +2696,15 @@ function _menu_navigation_links_rebuild($menu) {
}
}
if ($menu_links) {
+ // Keep an array of processed menu links, to allow menu_link_save() to
+ // check this for parents instead of querying the database.
+ $parent_candidates = array();
// Make sure no child comes before its parent.
array_multisort($sort, SORT_NUMERIC, $menu_links);
- foreach ($menu_links as $item) {
+ foreach ($menu_links as $key => $item) {
$existing_item = db_select('menu_links')
- ->fields('menu_links', array(
- 'mlid',
- 'menu_name',
- 'plid',
- 'customized',
- 'has_children',
- 'updated',
- ))
+ ->fields('menu_links')
->condition('link_path', $item['path'])
->condition('module', 'system')
->execute()->fetchAssoc();
@@ -2727,9 +2723,14 @@ function _menu_navigation_links_rebuild($menu) {
$item['has_children'] = $existing_item['has_children'];
$item['updated'] = $existing_item['updated'];
}
- if (!$existing_item || !$existing_item['customized']) {
+ if ($existing_item && $existing_item['customized']) {
+ $parent_candidates[$existing_item['mlid']] = $existing_item;
+ }
+ else {
$item = _menu_link_build($item);
- menu_link_save($item);
+ menu_link_save($item, $existing_item, $parent_candidates);
+ $parent_candidates[$item['mlid']] = $item;
+ unset($menu_links[$key]);
}
}
}
@@ -2927,12 +2928,17 @@ function _menu_delete_item($item, $force = FALSE) {
* to insert a new link.
* - plid: (optional) The mlid of the parent.
* - router_path: (optional) The path of the relevant router item.
+ * @param $existing_item
+ * Optional, the current record from the {menu_links} table as an array.
+ * @param $parent_candidates
+ * Optional array of menu links keyed by mlid. Used by
+ * _menu_navigation_links_rebuild() only.
*
* @return
* The mlid of the saved menu link, or FALSE if the menu link could not be
* saved.
*/
-function menu_link_save(&$item) {
+function menu_link_save(&$item, $existing_item = array(), $parent_candidates = array()) {
drupal_alter('menu_link', $item);
// This is the easiest way to handle the unique internal path '<front>',
@@ -2951,15 +2957,20 @@ function menu_link_save(&$item) {
'customized' => 0,
'updated' => 0,
);
- $existing_item = FALSE;
if (isset($item['mlid'])) {
- if ($existing_item = db_query("SELECT * FROM {menu_links} WHERE mlid = :mlid", array(':mlid' => $item['mlid']))->fetchAssoc()) {
+ if (!$existing_item) {
+ $existing_item = db_query('SELECT * FROM {menu_links} WHERE mlid = :mlid', array('mlid' => $item['mlid']))->fetchAssoc();
+ }
+ if ($existing_item) {
$existing_item['options'] = unserialize($existing_item['options']);
}
}
+ else {
+ $existing_item = FALSE;
+ }
// Try to find a parent link. If found, assign it and derive its menu.
- $parent = _menu_link_find_parent($item);
+ $parent = _menu_link_find_parent($item, $parent_candidates);
if (!empty($parent['mlid'])) {
$item['plid'] = $parent['mlid'];
$item['menu_name'] = $parent['menu_name'];
@@ -3093,11 +3104,13 @@ function menu_link_save(&$item) {
*
* @param $menu_link
* A menu link.
+ * @param $parent_candidates
+ * An array of menu links keyed by mlid.
* @return
* A menu link structure of the possible parent or FALSE if no valid parent
* has been found.
*/
-function _menu_link_find_parent($menu_link) {
+function _menu_link_find_parent($menu_link, $parent_candidates = array()) {
$parent = FALSE;
// This item is explicitely top-level, skip the rest of the parenting.
@@ -3119,7 +3132,12 @@ function _menu_link_find_parent($menu_link) {
}
foreach ($candidates as $mlid) {
- $parent = db_query("SELECT * FROM {menu_links} WHERE mlid = :mlid", array(':mlid' => $mlid))->fetchAssoc();
+ if (isset($parent_candidates[$mlid])) {
+ $parent = $parent_candidates[$mlid];
+ }
+ else {
+ $parent = db_query("SELECT * FROM {menu_links} WHERE mlid = :mlid", array(':mlid' => $mlid))->fetchAssoc();
+ }
if ($parent) {
return $parent;
}
diff --git a/includes/password.inc b/includes/password.inc
index 93d34f81f..c0761da69 100644
--- a/includes/password.inc
+++ b/includes/password.inc
@@ -18,7 +18,7 @@
* increase by 1 every Drupal version in order to counteract increases in the
* speed and power of computers available to crack the hashes.
*/
-define('DRUPAL_HASH_COUNT', 14);
+define('DRUPAL_HASH_COUNT', 15);
/**
* The minimum allowed log2 number of iterations for password stretching.
diff --git a/includes/stream_wrappers.inc b/includes/stream_wrappers.inc
index 7df1f9dc6..3c88f3d8f 100644
--- a/includes/stream_wrappers.inc
+++ b/includes/stream_wrappers.inc
@@ -341,7 +341,11 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface
* Base implementation of chmod().
*/
function chmod($mode) {
- return @chmod($this->getLocalPath(), $mode);
+ $output = @chmod($this->getLocalPath(), $mode);
+ // We are modifying the underlying file here, so we have to clear the stat
+ // cache so that PHP understands that URI has changed too.
+ clearstatcache();
+ return $output;
}
/**