Skip to content
Commits on Source (165)
# Drupal editor configuration normalization
# @see http://editorconfig.org/
# This is the top-most .editorconfig file; do not search in parent directories.
root = true
# All files.
[*]
end_of_line = LF
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
......@@ -3,7 +3,7 @@
#
# Protect files and directories from prying eyes.
<FilesMatch "\.(engine|inc|info|install|make|module|profile|test|po|sh|.*sql|theme|tpl(\.php)?|xtmpl)(~|\.sw[op]|\.bak|\.orig|\.save)?$|^(\..*|Entries.*|Repository|Root|Tag|Template)$|^#.*#$|\.php(~|\.sw[op]|\.bak|\.orig\.save)$">
<FilesMatch "\.(engine|inc|info|install|make|module|profile|test|po|sh|.*sql|theme|tpl(\.php)?|xtmpl)(~|\.sw[op]|\.bak|\.orig|\.save)?$|^(\..*|Entries.*|Repository|Root|Tag|Template|composer\.(json|lock))$|^#.*#$|\.php(~|\.sw[op]|\.bak|\.orig\.save)$">
Order allow,deny
</FilesMatch>
......
Drupal 7.40, xxxx-xx-xx (development version)
Drupal 7.52, 2016-11-16
-----------------------
- Fixed security issues (multiple vulnerabilities). See SA-CORE-2016-005.
Drupal 7.51, 2016-10-05
-----------------------
- The Update module now also checks for updates to a disabled theme that is
used as an admin theme.
- Exceptions thrown in dblog_watchdog() are now caught and ignored.
- Clarified the warning that appears when modules are missing or have moved.
- Log messages are now XSS filtered on display.
- Draggable tables now work on touch screen devices.
- Added a setting for allowing double underscores in CSS identifiers
(https://www.drupal.org/node/2810369).
- If a user navigates away from a page while an Ajax request is running they
will no longer get an error message saying "An Ajax HTTP request terminated
abnormally".
- The system_region_list() API function now takes an optional third parameter
which allows region name translations to be skipped when they are not needed
(API addition: https://www.drupal.org/node/2810365).
- Numerous performance improvements.
- Numerous bug fixes.
- Numerous API documentation improvements.
- Additional automated test coverage.
Drupal 7.50, 2016-07-07
-----------------------
- Added a new "administer fields" permission for trusted users, which is
required in addition to other permissions to use the field UI
(https://www.drupal.org/node/2483307).
- Added clickjacking protection to Drupal core by setting the X-Frame-Options
header to SAMEORIGIN by default (https://www.drupal.org/node/2735873).
- Added support for full UTF-8 (emojis, Asian symbols, mathematical symbols) on
MySQL and other database drivers when the site and database are configured to
allow it (https://www.drupal.org/node/2761183).
- Improved performance by avoiding a re-scan of directories when a file is
missing; instead, trigger a PHP warning (minor API change:
https://www.drupal.org/node/2581445).
- Made it possible to use any PHP callable in Ajax form callbacks, form API
form-building functions, and form API wrapper callbacks (API addition:
https://www.drupal.org/node/2761169).
- Fixed that following a password reset link while logged in leaves users unable
to change their password (minor user interface change:
https://www.drupal.org/node/2759023).
- Implemented various fixes for automated test failures on PHP 5.4+ and PHP 7.
Drupal core automated tests now pass in these environments.
- Improved support for PHP 7 by fixing various problems.
- Fixed various bugs with PHP 5.5+ imagerotate(), including when incorrect
color indices are passed in.
- Fixed a regression introduced in Drupal 7.43 that allowed files uploaded by
anonymous users to be lost after form validation errors, and that also caused
regressions with certain contributed modules.
- Fixed a regression introduced in Drupal 7.36 which caused the default value
of hidden textarea fields to be ignored.
- Fixed robots.txt to allow search engines to access CSS, JavaScript and image
files.
- Changed wording on the Update Manager settings page to clarify that the
option to check for disabled module updates also applies to uninstalled
modules (administrative-facing translatable string change).
- Changed the help text when editing menu links and configuring URL redirect
actions so that it does not reference "Drupal" or the drupal.org website
(administrative-facing translatable string change).
- Fixed the locale safety check that is used to ensure that translations are
safe to allow for tokens in the href/src attributes of translated strings.
- Fixed that URL generation only works on port 80 when using domain based
language negotation.
- Made method="get" forms work inside the administrative overlay. The fix adds
a new hidden field to these forms when they appear inside the overlay (minor
data structure change).
- Increased maxlength of menu link title input fields in the node form and
menu link form from 128 to 255 characters.
- Removed meaningless post-check=0 and pre-check=0 cache control headers from
Drupal HTTP responses.
- Added a .editorconfig file to auto-configure editors that support it.
- Added --directory option to run-tests.sh for easier test discovery of all
tests within a project.
- Made run-tests.sh exit with a failure code when there are test fails or
problems running the script.
- Fixed that cookies from previous tests are still present when a new test
starts in DrupalWebTestCase.
- Improved performance of queries on the {authmap} database table.
- Fixed handling of missing files and functions inside the registry.
- Fixed Ajax handling for tableselect form elements that use checkboxes.
- Fixed a bug which caused ip_address() to return nothing when the client IP
address and proxy IP address are the same.
- Added a new option to format_xml_elements() to allow for already encoded
values.
- Changed the {history} table's node ID field to be an unsigned integer, to
match the same field in the {node} table and to prevent errors with very
large node IDs.
- Added an explicit page callback to the "admin/people/create" menu item in the
User module (minor data structure change). Previously this automatically
inherited the page callback from the parent "admin/people" menu item, which
broke contributed modules that override the "admin/people" page.
- Numerous small bug fixes.
- Numerous API documentation improvements.
- Additional automated test coverage.
Drupal 7.44, 2016-06-15
-----------------------
- Fixed security issues (privilege escalation). See SA-CORE-2016-002.
Drupal 7.43, 2016-02-24
-----------------------
- Fixed security issues (multiple vulnerabilities). See SA-CORE-2016-001.
Drupal 7.42, 2016-02-03
-----------------------
- Stopped invoking hook_flush_caches() on every cron run, since some modules
use that hook for expensive operations that are only needed on cache clears.
- Changed the default .htaccess and web.config to block Composer-related files.
- Added static caching to module_load_include() to improve performance.
- Fixed double-encoding bugs in select field widgets provided by the Options
module. The fix deprecates the 'strip_tags' property on option widgets and
replaces it with a new 'strip_tags_and_unescape' property (minor data
structure change).
- Improved MySQL 5.7 support by changing the MySQL database driver to stop
using the ANSI SQL mode alias, which has different meanings for different
MySQL versions.
- Fixed a regression introduced in Drupal 7.39 which prevented autocomplete
functionality from working on servers that are not configured to
automatically recognize index.php.
- Updated the Archive_Tar PEAR package to the latest 1.4.0 release, to fix bugs
with tar file handling on various operating systems.
- Fixed fatal errors on node preview when a field is displayed in the node
teaser but hidden in the full node view. The fix removes a
field_attach_prepare_view() call from the node_preview() function since it is
redundant with one in the node preview theme layer.
- Improved the description of the "Trimmed" format option on text fields
(translatable string change, and minor UI and data structure change).
- Numerous small bug fixes.
- Numerous API documentation improvements.
- Additional automated test coverage.
Drupal 7.41, 2015-10-21
-----------------------
- Fixed security issues (open redirect). See SA-CORE-2015-004.
Drupal 7.40, 2015-10-14
-----------------------
- Made Drupal's code for parsing .info files run much faster and use much less
memory.
......
......@@ -23,7 +23,7 @@ Drupal requires:
- Percona Server 5.1.70 (or greater) (http://www.percona.com/). Percona
Server is a backwards-compatible replacement for MySQL.
- PostgreSQL 8.3 (or greater) (http://www.postgresql.org/).
- SQLite 3.4.2 (or greater) (http://www.sqlite.org/).
- SQLite 3.3.7 (or greater) (http://www.sqlite.org/).
For more detailed information about Drupal requirements, including a list of
PHP extensions and configurations that are required, see "System requirements"
......
......@@ -12,7 +12,9 @@ The branch maintainers for Drupal 7 are:
- Dries Buytaert 'dries' https://www.drupal.org/u/dries
- Angela Byron 'webchick' https://www.drupal.org/u/webchick
- Fabian Franz 'Fabianx' https://www.drupal.org/u/fabianx
- David Rothstein 'David_Rothstein' https://www.drupal.org/u/david_rothstein
- Stefan Ruijsenaars 'stefan.r' https://www.drupal.org/u/stefanr-0
Component maintainers
......@@ -143,7 +145,6 @@ User experience and usability
Node Access
- Moshe Weitzman 'moshe weitzman' https://www.drupal.org/u/moshe-weitzman
- Ken Rickard 'agentrickard' https://www.drupal.org/u/agentrickard
- Jess Myrbo 'xjm' https://www.drupal.org/u/xjm
Security team
......@@ -266,7 +267,6 @@ System module
- ?
Taxonomy module
- Jess Myrbo 'xjm' https://www.drupal.org/u/xjm
- Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch
- Benjamin Doherty 'bangpound' https://www.drupal.org/u/bangpound
......
......@@ -394,7 +394,7 @@ function ajax_form_callback() {
if (!empty($form_state['triggering_element'])) {
$callback = $form_state['triggering_element']['#ajax']['callback'];
}
if (!empty($callback) && function_exists($callback)) {
if (!empty($callback) && is_callable($callback)) {
$result = $callback($form, $form_state);
if (!(is_array($result) && isset($result['#type']) && $result['#type'] == 'ajax')) {
......
This diff is collapsed.
......@@ -688,6 +688,13 @@ function drupal_goto($path = '', array $options = array(), $http_response_code =
$options['fragment'] = $destination['fragment'];
}
// In some cases modules call drupal_goto(current_path()). We need to ensure
// that such a redirect is not to an external URL.
if ($path === current_path() && empty($options['external']) && url_is_external($path)) {
// Force url() to generate a non-external URL.
$options['external'] = FALSE;
}
drupal_alter('drupal_goto', $path, $options, $http_response_code);
// The 'Location' HTTP header must be absolute.
......@@ -753,7 +760,8 @@ function drupal_access_denied() {
* - headers: An array containing request headers to send as name/value pairs.
* - method: A string containing the request method. Defaults to 'GET'.
* - data: A string containing the request body, formatted as
* 'param=value&param=value&...'. Defaults to NULL.
* 'param=value&param=value&...'; to generate this, use http_build_query().
* Defaults to NULL.
* - max_redirects: An integer representing how many times a redirect
* may be followed. Defaults to 3.
* - timeout: A float representing the maximum number of seconds the function
......@@ -778,6 +786,8 @@ function drupal_access_denied() {
* HTTP header names are case-insensitive (RFC 2616, section 4.2), so for
* easy access the array keys are returned in lower case.
* - data: A string containing the response body that was received.
*
* @see http_build_query()
*/
function drupal_http_request($url, array $options = array()) {
// Allow an alternate HTTP client library to replace Drupal's default
......@@ -1760,9 +1770,15 @@ function format_rss_item($title, $link, $description, $args = array()) {
* - 'key': element name
* - 'value': element contents
* - 'attributes': associative array of element attributes
* - 'encoded': TRUE if 'value' is already encoded
*
* In both cases, 'value' can be a simple string, or it can be another array
* with the same format as $array itself for nesting.
*
* If 'encoded' is TRUE it is up to the caller to ensure that 'value' is either
* entity-encoded or CDATA-escaped. Using this option is not recommended when
* working with untrusted user input, since failing to escape the data
* correctly has security implications.
*/
function format_xml_elements($array) {
$output = '';
......@@ -1775,7 +1791,7 @@ function format_xml_elements($array) {
}
if (isset($value['value']) && $value['value'] != '') {
$output .= '>' . (is_array($value['value']) ? format_xml_elements($value['value']) : check_plain($value['value'])) . '</' . $value['key'] . ">\n";
$output .= '>' . (is_array($value['value']) ? format_xml_elements($value['value']) : (!empty($value['encoded']) ? $value['value'] : check_plain($value['value']))) . '</' . $value['key'] . ">\n";
}
else {
$output .= " />\n";
......@@ -2220,20 +2236,8 @@ function url($path = NULL, array $options = array()) {
'prefix' => ''
);
// A duplicate of the code from url_is_external() to avoid needing another
// function call, since performance inside url() is critical.
if (!isset($options['external'])) {
// Return an external link if $path contains an allowed absolute URL. Avoid
// calling drupal_strip_dangerous_protocols() if there is any slash (/),
// hash (#) or question_mark (?) before the colon (:) occurrence - if any -
// as this would clearly mean it is not a URL. If the path starts with 2
// slashes then it is always considered an external URL without an explicit
// protocol part.
$colonpos = strpos($path, ':');
$options['external'] = (strpos($path, '//') === 0)
|| ($colonpos !== FALSE
&& !preg_match('![/?#]!', substr($path, 0, $colonpos))
&& drupal_strip_dangerous_protocols($path) == $path);
$options['external'] = url_is_external($path);
}
// Preserve the original path before altering or aliasing.
......@@ -2353,12 +2357,18 @@ function url($path = NULL, array $options = array()) {
*/
function url_is_external($path) {
$colonpos = strpos($path, ':');
// Avoid calling drupal_strip_dangerous_protocols() if there is any slash (/),
// hash (#) or question_mark (?) before the colon (:) occurrence - if any - as
// this would clearly mean it is not a URL. If the path starts with 2 slashes
// then it is always considered an external URL without an explicit protocol
// part.
// Some browsers treat \ as / so normalize to forward slashes.
$path = str_replace('\\', '/', $path);
// If the path starts with 2 slashes then it is always considered an external
// URL without an explicit protocol part.
return (strpos($path, '//') === 0)
// Leading control characters may be ignored or mishandled by browsers, so
// assume such a path may lead to an external location. The \p{C} character
// class matches all UTF-8 control, unassigned, and private characters.
|| (preg_match('/^\p{C}/u', $path) !== 0)
// Avoid calling drupal_strip_dangerous_protocols() if there is any slash
// (/), hash (#) or question_mark (?) before the colon (:) occurrence - if
// any - as this would clearly mean it is not a URL.
|| ($colonpos !== FALSE
&& !preg_match('![/?#]!', substr($path, 0, $colonpos))
&& drupal_strip_dangerous_protocols($path) == $path);
......@@ -2643,6 +2653,15 @@ function drupal_deliver_html_page($page_callback_result) {
global $language;
drupal_add_http_header('Content-Language', $language->language);
// By default, do not allow the site to be rendered in an iframe on another
// domain, but provide a variable to override this. If the code running for
// this page request already set the X-Frame-Options header earlier, don't
// overwrite it here.
$frame_options = variable_get('x_frame_options', 'SAMEORIGIN');
if ($frame_options && is_null(drupal_get_http_header('X-Frame-Options'))) {
drupal_add_http_header('X-Frame-Options', $frame_options);
}
// Menu status constants are integers; page content is a string or array.
if (is_int($page_callback_result)) {
// @todo: Break these up into separate functions?
......@@ -2757,6 +2776,7 @@ function drupal_page_footer() {
_registry_check_code(REGISTRY_WRITE_LOOKUP_CACHE);
drupal_cache_system_paths();
module_implements_write_cache();
drupal_file_scan_write_cache();
system_run_automated_cron();
}
......@@ -3024,6 +3044,13 @@ function drupal_add_html_head_link($attributes, $header = FALSE) {
*/
function drupal_add_css($data = NULL, $options = NULL) {
$css = &drupal_static(__FUNCTION__, array());
$count = &drupal_static(__FUNCTION__ . '_count', 0);
// If the $css variable has been reset with drupal_static_reset(), there is
// no longer any CSS being tracked, so set the counter back to 0 also.
if (count($css) === 0) {
$count = 0;
}
// Construct the options, taking the defaults into consideration.
if (isset($options)) {
......@@ -3059,7 +3086,8 @@ function drupal_add_css($data = NULL, $options = NULL) {
}
// Always add a tiny value to the weight, to conserve the insertion order.
$options['weight'] += count($css) / 1000;
$options['weight'] += $count / 1000;
$count++;
// Add the data to the CSS array depending on the type.
switch ($options['type']) {
......@@ -3872,6 +3900,21 @@ function drupal_delete_file_if_stale($uri) {
* The cleaned identifier.
*/
function drupal_clean_css_identifier($identifier, $filter = 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['allow_css_double_underscores'] = &drupal_static(__FUNCTION__ . ':allow_css_double_underscores');
}
$allow_css_double_underscores = &$drupal_static_fast['allow_css_double_underscores'];
if (!isset($allow_css_double_underscores)) {
$allow_css_double_underscores = variable_get('allow_css_double_underscores', FALSE);
}
// Preserve BEM-style double-underscores depending on custom setting.
if ($allow_css_double_underscores) {
$filter['__'] = '__';
}
// By default, we filter using Drupal's coding standards.
$identifier = strtr($identifier, $filter);
......@@ -5323,8 +5366,8 @@ function drupal_page_set_cache() {
*
* Do not call this function from a test. Use $this->cronRun() instead.
*
* @return
* TRUE if cron ran successfully.
* @return bool
* TRUE if cron ran successfully and FALSE if cron is already running.
*/
function drupal_cron_run() {
// Allow execution to continue even if the request gets canceled.
......
......@@ -296,6 +296,20 @@ abstract class DatabaseConnection extends PDO {
*/
protected $prefixReplace = array();
/**
* List of escaped database, table, and field names, keyed by unescaped names.
*
* @var array
*/
protected $escapedNames = array();
/**
* List of escaped aliases names, keyed by unescaped aliases.
*
* @var array
*/
protected $escapedAliases = array();
function __construct($dsn, $username, $password, $driver_options = array()) {
// Initialize and prepare the connection prefix.
$this->setPrefix(isset($this->connectionOptions['prefix']) ? $this->connectionOptions['prefix'] : '');
......@@ -919,11 +933,14 @@ public function schema() {
* For some database drivers, it may also wrap the table name in
* database-specific escape characters.
*
* @return
* @return string
* The sanitized table name string.
*/
public function escapeTable($table) {
return preg_replace('/[^A-Za-z0-9_.]+/', '', $table);
if (!isset($this->escapedNames[$table])) {
$this->escapedNames[$table] = preg_replace('/[^A-Za-z0-9_.]+/', '', $table);
}
return $this->escapedNames[$table];
}
/**
......@@ -933,11 +950,14 @@ public function escapeTable($table) {
* For some database drivers, it may also wrap the field name in
* database-specific escape characters.
*
* @return
* @return string
* The sanitized field name string.
*/
public function escapeField($field) {
return preg_replace('/[^A-Za-z0-9_.]+/', '', $field);
if (!isset($this->escapedNames[$field])) {
$this->escapedNames[$field] = preg_replace('/[^A-Za-z0-9_.]+/', '', $field);
}
return $this->escapedNames[$field];
}
/**
......@@ -948,11 +968,14 @@ public function escapeField($field) {
* DatabaseConnection::escapeTable(), this doesn't allow the period (".")
* because that is not allowed in aliases.
*
* @return
* @return string
* The sanitized field name string.
*/
public function escapeAlias($field) {
return preg_replace('/[^A-Za-z0-9_]+/', '', $field);
if (!isset($this->escapedAliases[$field])) {
$this->escapedAliases[$field] = preg_replace('/[^A-Za-z0-9_]+/', '', $field);
}
return $this->escapedAliases[$field];
}
/**
......@@ -1313,6 +1336,39 @@ public function commit() {
* also larger than the $existing_id if one was passed in.
*/
abstract public function nextId($existing_id = 0);
/**
* Checks whether utf8mb4 support is configurable in settings.php.
*
* @return bool
*/
public function utf8mb4IsConfigurable() {
// Since 4 byte UTF-8 is not supported by default, there is nothing to
// configure.
return FALSE;
}
/**
* Checks whether utf8mb4 support is currently active.
*
* @return bool
*/
public function utf8mb4IsActive() {
// Since 4 byte UTF-8 is not supported by default, there is nothing to
// activate.
return FALSE;
}
/**
* Checks whether utf8mb4 support is available on the current database system.
*
* @return bool
*/
public function utf8mb4IsSupported() {
// By default we assume that the database backend may not support 4 byte
// UTF-8.
return FALSE;
}
}
/**
......
......@@ -28,6 +28,12 @@ public function __construct(array $connection_options = array()) {
$this->connectionOptions = $connection_options;
$charset = 'utf8';
// Check if the charset is overridden to utf8mb4 in settings.php.
if ($this->utf8mb4IsActive()) {
$charset = 'utf8mb4';
}
// The DSN should use either a socket or a host/port.
if (isset($connection_options['unix_socket'])) {
$dsn = 'mysql:unix_socket=' . $connection_options['unix_socket'];
......@@ -39,7 +45,7 @@ public function __construct(array $connection_options = array()) {
// Character set is added to dsn to ensure PDO uses the proper character
// set when escaping. This has security implications. See
// https://www.drupal.org/node/1201452 for further discussion.
$dsn .= ';charset=utf8';
$dsn .= ';charset=' . $charset;
$dsn .= ';dbname=' . $connection_options['database'];
// Allow PDO options to be overridden.
$connection_options += array(
......@@ -63,10 +69,10 @@ public function __construct(array $connection_options = array()) {
// certain one has been set; otherwise, MySQL defaults to 'utf8_general_ci'
// for UTF-8.
if (!empty($connection_options['collation'])) {
$this->exec('SET NAMES utf8 COLLATE ' . $connection_options['collation']);
$this->exec('SET NAMES ' . $charset . ' COLLATE ' . $connection_options['collation']);
}
else {
$this->exec('SET NAMES utf8');
$this->exec('SET NAMES ' . $charset);
}
// Set MySQL init_commands if not already defined. Default Drupal's MySQL
......@@ -81,7 +87,7 @@ public function __construct(array $connection_options = array()) {
'init_commands' => array(),
);
$connection_options['init_commands'] += array(
'sql_mode' => "SET sql_mode = 'ANSI,STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER'",
'sql_mode' => "SET sql_mode = 'REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER'",
);
// Execute initial commands.
foreach ($connection_options['init_commands'] as $sql) {
......@@ -206,6 +212,42 @@ protected function popCommittableTransactions() {
}
}
}
public function utf8mb4IsConfigurable() {
return TRUE;
}
public function utf8mb4IsActive() {
return isset($this->connectionOptions['charset']) && $this->connectionOptions['charset'] === 'utf8mb4';
}
public function utf8mb4IsSupported() {
// Ensure that the MySQL driver supports utf8mb4 encoding.
$version = $this->getAttribute(PDO::ATTR_CLIENT_VERSION);
if (strpos($version, 'mysqlnd') !== FALSE) {
// The mysqlnd driver supports utf8mb4 starting at version 5.0.9.
$version = preg_replace('/^\D+([\d.]+).*/', '$1', $version);
if (version_compare($version, '5.0.9', '<')) {
return FALSE;
}
}
else {
// The libmysqlclient driver supports utf8mb4 starting at version 5.5.3.
if (version_compare($version, '5.5.3', '<')) {
return FALSE;
}
}
// Ensure that the MySQL server supports large prefixes and utf8mb4.
try {
$this->query("CREATE TABLE {drupal_utf8mb4_test} (id VARCHAR(255), PRIMARY KEY(id(255))) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci ROW_FORMAT=DYNAMIC ENGINE=INNODB");
}
catch (Exception $e) {
return FALSE;
}
$this->query("DROP TABLE {drupal_utf8mb4_test}");
return TRUE;
}
}
......
......@@ -39,8 +39,8 @@ protected function getPrefixInfo($table = 'default', $add_prefix = TRUE) {
$info['table'] = substr($table, ++$pos);
}
else {
$db_info = Database::getConnectionInfo();
$info['database'] = $db_info[$this->connection->getTarget()]['database'];
$db_info = $this->connection->getConnectionOptions();
$info['database'] = $db_info['database'];
$info['table'] = $table;
}
return $info;
......@@ -81,7 +81,8 @@ protected function createTableSql($name, $table) {
// Provide defaults if needed.
$table += array(
'mysql_engine' => 'InnoDB',
'mysql_character_set' => 'utf8',
// Allow the default charset to be overridden in settings.php.
'mysql_character_set' => $this->connection->utf8mb4IsActive() ? 'utf8mb4' : 'utf8',
);
$sql = "CREATE TABLE {" . $name . "} (\n";
......@@ -109,6 +110,13 @@ protected function createTableSql($name, $table) {
$sql .= ' COLLATE ' . $info['collation'];
}
// The row format needs to be either DYNAMIC or COMPRESSED in order to allow
// for the innodb_large_prefix setting to take effect, see
// https://dev.mysql.com/doc/refman/5.6/en/create-table.html
if ($this->connection->utf8mb4IsActive()) {
$sql .= ' ROW_FORMAT=DYNAMIC';
}
// Add table comment.
if (!empty($table['description'])) {
$sql .= ' COMMENT ' . $this->prepareComment($table['description'], self::COMMENT_MAX_TABLE);
......
......@@ -216,6 +216,14 @@ public function nextId($existing = 0) {
return $id;
}
public function utf8mb4IsActive() {
return TRUE;
}
public function utf8mb4IsSupported() {
return TRUE;
}
}
/**
......
......@@ -92,7 +92,8 @@
* specification). Each specification is an array containing the name of
* the referenced table ('table'), and an array of column mappings
* ('columns'). Column mappings are defined by key pairs ('source_column' =>
* 'referenced_column').
* 'referenced_column'). This key is for documentation purposes only; foreign
* keys are not created in the database, nor are they enforced by Drupal.
* - 'indexes': An associative array of indexes ('indexname' =>
* specification). Each specification is an array of one or more
* key column specifiers (see below) that form an index on the
......@@ -144,6 +145,8 @@
* 'unique keys' => array(
* 'vid' => array('vid'),
* ),
* // For documentation purposes only; foreign keys are not created in the
* // database.
* 'foreign keys' => array(
* 'node_revision' => array(
* 'table' => 'node_revision',
......
......@@ -1231,6 +1231,21 @@ public function preExecute(SelectQueryInterface $query = NULL) {
// Modules may alter all queries or only those having a particular tag.
if (isset($this->alterTags)) {
// Many contrib modules assume that query tags used for access-checking
// purposes follow the pattern $entity_type . '_access'. But this is
// not the case for taxonomy terms, since core used to add term_access
// instead of taxonomy_term_access to its queries. Provide backwards
// compatibility by adding both tags here instead of attempting to fix
// all contrib modules in a coordinated effort.
// TODO:
// - Extract this mechanism into a hook as part of a public (non-security)
// issue.
// - Emit E_USER_DEPRECATED if term_access is used.
// https://www.drupal.org/node/2575081
$term_access_tags = array('term_access' => 1, 'taxonomy_term_access' => 1);
if (array_intersect_key($this->alterTags, $term_access_tags)) {
$this->alterTags += $term_access_tags;
}
$hooks = array('query');
foreach ($this->alterTags as $tag => $value) {
$hooks[] = 'query_' . $tag;
......
......@@ -378,6 +378,14 @@ public function popTransaction($name) {
}
}
public function utf8mb4IsActive() {
return TRUE;
}
public function utf8mb4IsSupported() {
return TRUE;
}
}
/**
......
......@@ -14,8 +14,6 @@ public function name() {
/**
* Minimum engine version.
*
* @todo: consider upping to 3.6.8 in Drupal 8 to get SAVEPOINT support.
*/
public function minimumVersion() {
return '3.3.7';
......
......@@ -446,7 +446,7 @@ class EntityFieldQueryException extends Exception {}
*
* This class allows finding entities based on entity properties (for example,
* node->changed), field values, and generic entity meta data (bundle,
* entity type, entity id, and revision ID). It is not possible to query across
* entity type, entity ID, and revision ID). It is not possible to query across
* multiple entity types. For example, there is no facility to find published
* nodes written by users created in the last hour, as this would require
* querying both node->status and user->created.
......@@ -688,14 +688,36 @@ public function entityCondition($name, $value, $operator = NULL) {
* @param $field
* Either a field name or a field array.
* @param $column
* The column that should hold the value to be matched.
* The column that should hold the value to be matched, defined in the
* hook_field_schema() of this field. If this is omitted then all of the
* other parameters are ignored, except $field, and this call will just be
* adding a condition that says that the field has a value, rather than
* testing the value itself.
* @param $value
* The value to test the column value against.
* The value to test the column value against. In most cases, this is a
* scalar. For more complex options, it is an array. The meaning of each
* element in the array is dependent on $operator.
* @param $operator
* The operator to be used to test the given value.
* The operator to be used to test the given value. The possible values are:
* - '=', '<>', '>', '>=', '<', '<=', 'STARTS_WITH', 'CONTAINS': These
* operators expect $value to be a literal of the same type as the
* column.
* - 'IN', 'NOT IN': These operators expect $value to be an array of
* literals of the same type as the column.
* - 'BETWEEN': This operator expects $value to be an array of two literals
* of the same type as the column.
* The operator can be omitted, and will default to 'IN' if the value is an
* array, or to '=' otherwise.
* @param $delta_group
* An arbitrary identifier: conditions in the same group must have the same
* $delta_group.
* $delta_group. For example, let's presume a multivalue field which has
* two columns, 'color' and 'shape', and for entity ID 1, there are two
* values: red/square and blue/circle. Entity ID 1 does not have values
* corresponding to 'red circle'; however if you pass 'red' and 'circle' as
* conditions, it will appear in the results -- by default queries will run
* against any combination of deltas. By passing the conditions with the
* same $delta_group it will ensure that only values attached to the same
* delta are matched, and entity 1 would then be excluded from the results.
* @param $language_group
* An arbitrary identifier: conditions in the same group must have the same
* $language_group.
......@@ -770,9 +792,11 @@ public function fieldDeltaCondition($field, $value = NULL, $operator = NULL, $de
* @param $field
* Either a field name or a field array.
* @param $column
* A column defined in the hook_field_schema() of this field. If this is
* omitted then the query will find only entities that have data in this
* field, using the entity and property conditions if there are any.
* The column that should hold the value to be matched, defined in the
* hook_field_schema() of this field. If this is omitted then all of the
* other parameters are ignored, except $field, and this call will just be
* adding a condition that says that the field has a value, rather than
* testing the value itself.
* @param $value
* The value to test the column value against. In most cases, this is a
* scalar. For more complex options, it is an array. The meaning of each
......@@ -791,10 +815,10 @@ public function fieldDeltaCondition($field, $value = NULL, $operator = NULL, $de
* @param $delta_group
* An arbitrary identifier: conditions in the same group must have the same
* $delta_group. For example, let's presume a multivalue field which has
* two columns, 'color' and 'shape', and for entity id 1, there are two
* two columns, 'color' and 'shape', and for entity ID 1, there are two
* values: red/square and blue/circle. Entity ID 1 does not have values
* corresponding to 'red circle', however if you pass 'red' and 'circle' as
* conditions, it will appear in the results - by default queries will run
* conditions, it will appear in the results -- by default queries will run
* against any combination of deltas. By passing the conditions with the
* same $delta_group it will ensure that only values attached to the same
* delta are matched, and entity 1 would then be excluded from the results.
......
......@@ -199,7 +199,16 @@ function _drupal_log_error($error, $fatal = FALSE) {
$number++;
}
watchdog('php', '%type: !message in %function (line %line of %file).', $error, $error['severity_level']);
// Log the error immediately, unless this is a non-fatal error which has been
// triggered via drupal_trigger_error_with_delayed_logging(); in that case
// trigger it in a shutdown function. Fatal errors are always triggered
// immediately since for a fatal error the page request will end here anyway.
if (!$fatal && drupal_static('_drupal_trigger_error_with_delayed_logging')) {
drupal_register_shutdown_function('watchdog', 'php', '%type: !message in %function (line %line of %file).', $error, $error['severity_level']);
}
else {
watchdog('php', '%type: !message in %function (line %line of %file).', $error, $error['severity_level']);
}
if ($fatal) {
drupal_add_http_header('Status', '500 Service unavailable (with message)');
......
......@@ -273,7 +273,9 @@ function file_default_scheme() {
* The normalized URI.
*/
function file_stream_wrapper_uri_normalize($uri) {
$scheme = file_uri_scheme($uri);
// Inline file_uri_scheme() function call for performance reasons.
$position = strpos($uri, '://');
$scheme = $position ? substr($uri, 0, $position) : FALSE;
if ($scheme && file_stream_wrapper_valid_scheme($scheme)) {
$target = file_uri_target($uri);
......@@ -1785,7 +1787,7 @@ function file_validate_is_image(stdClass $file) {
/**
* 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
* Non-image files will be ignored. If an image toolkit is available the image
* will be scaled to fit within the desired maximum dimensions.
*
* @param $file
......@@ -2022,7 +2024,7 @@ function file_download() {
*
* @see file_transfer()
* @see file_download_access()
* @see hook_file_downlaod()
* @see hook_file_download()
*/
function file_download_headers($uri) {
// Let other modules provide headers and control access to the file.
......
......@@ -105,7 +105,8 @@
* 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(),
* and search_forms().
* and search_forms(). hook_forms() can also be used to define forms in
* classes.
* @param ...
* Any additional arguments are passed on to the functions called by
* drupal_get_form(), including the unique form constructor function. For
......@@ -809,7 +810,7 @@ function drupal_retrieve_form($form_id, &$form_state) {
}
if (isset($form_definition['callback'])) {
$callback = $form_definition['callback'];
$form_state['build_info']['base_form_id'] = $callback;
$form_state['build_info']['base_form_id'] = isset($form_definition['base_form_id']) ? $form_definition['base_form_id'] : $callback;
}
// In case $form_state['wrapper_callback'] is not defined already, we also
// allow hook_forms() to define one.
......@@ -830,7 +831,7 @@ function drupal_retrieve_form($form_id, &$form_state) {
// the actual form builder function ($callback) expects. This allows for
// pre-populating a form with common elements for certain forms, such as
// back/next/save buttons in multi-step form wizards. See drupal_build_form().
if (isset($form_state['wrapper_callback']) && function_exists($form_state['wrapper_callback'])) {
if (isset($form_state['wrapper_callback']) && is_callable($form_state['wrapper_callback'])) {
$form = call_user_func_array($form_state['wrapper_callback'], $args);
// Put the prepopulated $form into $args.
$args[0] = $form;
......@@ -2571,7 +2572,7 @@ function form_type_select_value($element, $input = FALSE) {
* for this element. Return nothing to use the default.
*/
function form_type_textarea_value($element, $input = FALSE) {
if ($input !== FALSE) {
if ($input !== FALSE && $input !== NULL) {
// This should be a string, but allow other scalars since they might be
// valid input in programmatic form submissions.
return is_scalar($input) ? (string) $input : '';
......@@ -3028,7 +3029,7 @@ function form_process_password_confirm($element) {
function password_confirm_validate($element, &$element_state) {
$pass1 = trim($element['pass1']['#value']);
$pass2 = trim($element['pass2']['#value']);
if (!empty($pass1) || !empty($pass2)) {
if (strlen($pass1) > 0 || strlen($pass2) > 0) {
if (strcmp($pass1, $pass2)) {
form_error($element, t('The specified passwords do not match.'));
}
......@@ -3385,9 +3386,12 @@ function form_process_container($element, &$form_state) {
/**
* Returns HTML to wrap child elements in a container.
*
* Used for grouped form items. Can also be used as a #theme_wrapper for any
* Used for grouped form items. Can also be used as a theme wrapper for any
* renderable element, to surround it with a <div> and add attributes such as
* classes or an HTML id.
* classes or an HTML ID.
*
* See the @link forms_api_reference.html Form API reference @endlink for more
* information on the #theme_wrappers render array property.
*
* @param $variables
* An associative array containing:
......@@ -3542,6 +3546,7 @@ function form_process_tableselect($element) {
'#return_value' => $key,
'#default_value' => isset($value[$key]) ? $key : NULL,
'#attributes' => $element['#attributes'],
'#ajax' => isset($element['#ajax']) ? $element['#ajax'] : NULL,
);
}
else {
......@@ -3979,7 +3984,12 @@ function form_process_autocomplete($element) {
// browser interpreting the path plus search string as an actual file.
$current_clean_url = isset($GLOBALS['conf']['clean_url']) ? $GLOBALS['conf']['clean_url'] : NULL;
$GLOBALS['conf']['clean_url'] = 0;
$element['#autocomplete_input']['#url_value'] = url($element['#autocomplete_path'], array('absolute' => TRUE));
// Force the script path to 'index.php', in case the server is not
// configured to find it automatically. Normally it is the responsibility
// of the site to do this themselves using hook_url_outbound_alter() (see
// url()) but since this code is forcing non-clean URLs on sites that don't
// normally use them, it is done here instead.
$element['#autocomplete_input']['#url_value'] = url($element['#autocomplete_path'], array('absolute' => TRUE, 'script' => 'index.php'));
$GLOBALS['conf']['clean_url'] = $current_clean_url;
}
return $element;
......