updated drupal core to 7.51

This commit is contained in:
Bachir Soussi Chiadmi 2016-11-01 18:21:48 +01:00
parent 2ed8b48636
commit 1f780c974e
227 changed files with 2960 additions and 762 deletions

View File

@ -1,4 +1,102 @@
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.

View File

@ -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

View File

@ -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')) {

View File

@ -8,7 +8,7 @@
/**
* The current system version.
*/
define('VERSION', '7.43');
define('VERSION', '7.51');
/**
* Core API compatibility.
@ -828,14 +828,21 @@ function drupal_settings_initialize() {
* @param $filename
* The filename of the item if it is to be set explicitly rather
* than by consulting the database.
* @param bool $trigger_error
* Whether to trigger an error when a file is missing or has unexpectedly
* moved. This defaults to TRUE, but can be set to FALSE by calling code that
* merely wants to check whether an item exists in the filesystem.
*
* @return
* The filename of the requested item or NULL if the item is not found.
*/
function drupal_get_filename($type, $name, $filename = NULL) {
function drupal_get_filename($type, $name, $filename = NULL, $trigger_error = TRUE) {
// The $files static variable will hold the locations of all requested files.
// We can be sure that any file listed in this static variable actually
// exists as all additions have gone through a file_exists() check.
// The location of files will not change during the request, so do not use
// drupal_static().
static $files = array(), $dirs = array();
static $files = array();
// Profiles are a special case: they have a fixed location and naming.
if ($type == 'profile') {
@ -847,59 +854,41 @@ function drupal_get_filename($type, $name, $filename = NULL) {
}
if (!empty($filename) && file_exists($filename)) {
// Prime the static cache with the provided filename.
$files[$type][$name] = $filename;
}
elseif (isset($files[$type][$name])) {
// nothing
// This item had already been found earlier in the request, either through
// priming of the static cache (for example, in system_list()), through a
// lookup in the {system} table, or through a file scan (cached or not). Do
// nothing.
}
// Verify that we have an active database connection, before querying
// the database. This is required because this function is called both
// before we have a database connection (i.e. during installation) and
// when a database connection fails.
else {
// Look for the filename listed in the {system} table. Verify that we have
// an active database connection before doing so, since this function is
// called both before we have a database connection (i.e. during
// installation) and when a database connection fails.
$database_unavailable = TRUE;
try {
if (function_exists('db_query')) {
$file = db_query("SELECT filename FROM {system} WHERE name = :name AND type = :type", array(':name' => $name, ':type' => $type))->fetchField();
if ($file !== FALSE && file_exists(DRUPAL_ROOT . '/' . $file)) {
$files[$type][$name] = $file;
}
$database_unavailable = FALSE;
}
}
catch (Exception $e) {
// The database table may not exist because Drupal is not yet installed,
// or the database might be down. We have a fallback for this case so we
// hide the error completely.
// the database might be down, or we may have done a non-database cache
// flush while $conf['page_cache_without_database'] = TRUE and
// $conf['page_cache_invoke_hooks'] = TRUE. We have a fallback for these
// cases so we hide the error completely.
}
// Fallback to searching the filesystem if the database could not find the
// file or the file returned by the database is not found.
// Fall back to searching the filesystem if the database could not find the
// file or the file does not exist at the path returned by the database.
if (!isset($files[$type][$name])) {
// We have a consistent directory naming: modules, themes...
$dir = $type . 's';
if ($type == 'theme_engine') {
$dir = 'themes/engines';
$extension = 'engine';
}
elseif ($type == 'theme') {
$extension = 'info';
}
else {
$extension = $type;
}
if (!isset($dirs[$dir][$extension])) {
$dirs[$dir][$extension] = TRUE;
if (!function_exists('drupal_system_listing')) {
require_once DRUPAL_ROOT . '/includes/common.inc';
}
// Scan the appropriate directories for all files with the requested
// extension, not just the file we are currently looking for. This
// prevents unnecessary scans from being repeated when this function is
// called more than once in the same page request.
$matches = drupal_system_listing("/^" . DRUPAL_PHP_FUNCTION_PATTERN . "\.$extension$/", $dir, 'name', 0);
foreach ($matches as $matched_name => $file) {
$files[$type][$matched_name] = $file->uri;
}
}
$files[$type][$name] = _drupal_get_filename_fallback($type, $name, $trigger_error, $database_unavailable);
}
}
@ -908,6 +897,256 @@ function drupal_get_filename($type, $name, $filename = NULL) {
}
}
/**
* Performs a cached file system scan as a fallback when searching for a file.
*
* This function looks for the requested file by triggering a file scan,
* caching the new location if the file has moved and caching the miss
* if the file is missing. If a file had been marked as missing in a previous
* file scan, or if it has been marked as moved and is still in the last known
* location, no new file scan will be performed.
*
* @param string $type
* The type of the item (theme, theme_engine, module, profile).
* @param string $name
* The name of the item for which the filename is requested.
* @param bool $trigger_error
* Whether to trigger an error when a file is missing or has unexpectedly
* moved.
* @param bool $database_unavailable
* Whether this function is being called because the Drupal database could
* not be queried for the file's location.
*
* @return
* The filename of the requested item or NULL if the item is not found.
*
* @see drupal_get_filename()
*/
function _drupal_get_filename_fallback($type, $name, $trigger_error, $database_unavailable) {
$file_scans = &_drupal_file_scan_cache();
$filename = NULL;
// If the cache indicates that the item is missing, or we can verify that the
// item exists in the location the cache says it exists in, use that.
if (isset($file_scans[$type][$name]) && ($file_scans[$type][$name] === FALSE || file_exists($file_scans[$type][$name]))) {
$filename = $file_scans[$type][$name];
}
// Otherwise, perform a new file scan to find the item.
else {
$filename = _drupal_get_filename_perform_file_scan($type, $name);
// Update the static cache, and mark the persistent cache for updating at
// the end of the page request. See drupal_file_scan_write_cache().
$file_scans[$type][$name] = $filename;
$file_scans['#write_cache'] = TRUE;
}
// If requested, trigger a user-level warning about the missing or
// unexpectedly moved file. If the database was unavailable, do not trigger a
// warning in the latter case, though, since if the {system} table could not
// be queried there is no way to know if the location found here was
// "unexpected" or not.
if ($trigger_error) {
$error_type = $filename === FALSE ? 'missing' : 'moved';
if ($error_type == 'missing' || !$database_unavailable) {
_drupal_get_filename_fallback_trigger_error($type, $name, $error_type);
}
}
// The cache stores FALSE for files that aren't found (to be able to
// distinguish them from files that have not yet been searched for), but
// drupal_get_filename() expects NULL for these instead, so convert to NULL
// before returning.
if ($filename === FALSE) {
$filename = NULL;
}
return $filename;
}
/**
* Returns the current list of cached file system scan results.
*
* @return
* An associative array tracking the most recent file scan results for all
* files that have had scans performed. The keys are the type and name of the
* item that was searched for, and the values can be either:
* - Boolean FALSE if the item was not found in the file system.
* - A string pointing to the location where the item was found.
*/
function &_drupal_file_scan_cache() {
$file_scans = &drupal_static(__FUNCTION__, array());
// The file scan results are stored in a persistent cache (in addition to the
// static cache) but because this function can be called before the
// persistent cache is available, we must merge any items that were found
// earlier in the page request into the results from the persistent cache.
if (!isset($file_scans['#cache_merge_done'])) {
try {
if (function_exists('cache_get')) {
$cache = cache_get('_drupal_file_scan_cache', 'cache_bootstrap');
if (!empty($cache->data)) {
// File scan results from the current request should take precedence
// over the results from the persistent cache, since they are newer.
$file_scans = drupal_array_merge_deep($cache->data, $file_scans);
}
// Set a flag to indicate that the persistent cache does not need to be
// merged again.
$file_scans['#cache_merge_done'] = TRUE;
}
}
catch (Exception $e) {
// Hide the error.
}
}
return $file_scans;
}
/**
* Performs a file system scan to search for a system resource.
*
* @param $type
* The type of the item (theme, theme_engine, module, profile).
* @param $name
* The name of the item for which the filename is requested.
*
* @return
* The filename of the requested item or FALSE if the item is not found.
*
* @see drupal_get_filename()
* @see _drupal_get_filename_fallback()
*/
function _drupal_get_filename_perform_file_scan($type, $name) {
// The location of files will not change during the request, so do not use
// drupal_static().
static $dirs = array(), $files = array();
// We have a consistent directory naming: modules, themes...
$dir = $type . 's';
if ($type == 'theme_engine') {
$dir = 'themes/engines';
$extension = 'engine';
}
elseif ($type == 'theme') {
$extension = 'info';
}
else {
$extension = $type;
}
// Check if we had already scanned this directory/extension combination.
if (!isset($dirs[$dir][$extension])) {
// Log that we have now scanned this directory/extension combination
// into a static variable so as to prevent unnecessary file scans.
$dirs[$dir][$extension] = TRUE;
if (!function_exists('drupal_system_listing')) {
require_once DRUPAL_ROOT . '/includes/common.inc';
}
// Scan the appropriate directories for all files with the requested
// extension, not just the file we are currently looking for. This
// prevents unnecessary scans from being repeated when this function is
// called more than once in the same page request.
$matches = drupal_system_listing("/^" . DRUPAL_PHP_FUNCTION_PATTERN . "\.$extension$/", $dir, 'name', 0);
foreach ($matches as $matched_name => $file) {
// Log the locations found in the file scan into a static variable.
$files[$type][$matched_name] = $file->uri;
}
}
// Return the results of the file system scan, or FALSE to indicate the file
// was not found.
return isset($files[$type][$name]) ? $files[$type][$name] : FALSE;
}
/**
* Triggers a user-level warning for missing or unexpectedly moved files.
*
* @param $type
* The type of the item (theme, theme_engine, module, profile).
* @param $name
* The name of the item for which the filename is requested.
* @param $error_type
* The type of the error ('missing' or 'moved').
*
* @see drupal_get_filename()
* @see _drupal_get_filename_fallback()
*/
function _drupal_get_filename_fallback_trigger_error($type, $name, $error_type) {
// Hide messages due to known bugs that will appear on a lot of sites.
// @todo Remove this in https://www.drupal.org/node/2383823
if (empty($name)) {
return;
}
// Make sure we only show any missing or moved file errors only once per
// request.
static $errors_triggered = array();
if (empty($errors_triggered[$type][$name][$error_type])) {
// Use _drupal_trigger_error_with_delayed_logging() here since these are
// triggered during low-level operations that cannot necessarily be
// interrupted by a watchdog() call.
if ($error_type == 'missing') {
_drupal_trigger_error_with_delayed_logging(format_string('The following @type is missing from the file system: %name. For information about how to fix this, see <a href="@documentation">the documentation page</a>.', array('@type' => $type, '%name' => $name, '@documentation' => 'https://www.drupal.org/node/2487215')), E_USER_WARNING);
}
elseif ($error_type == 'moved') {
_drupal_trigger_error_with_delayed_logging(format_string('The following @type has moved within the file system: %name. In order to fix this, clear caches or put the @type back in its original location. For more information, see <a href="@documentation">the documentation page</a>.', array('@type' => $type, '%name' => $name, '@documentation' => 'https://www.drupal.org/node/2487215')), E_USER_WARNING);
}
$errors_triggered[$type][$name][$error_type] = TRUE;
}
}
/**
* Invokes trigger_error() with logging delayed until the end of the request.
*
* This is an alternative to PHP's trigger_error() function which can be used
* during low-level Drupal core operations that need to avoid being interrupted
* by a watchdog() call.
*
* Normally, Drupal's error handler calls watchdog() in response to a
* trigger_error() call. However, this invokes hook_watchdog() which can run
* arbitrary code. If the trigger_error() happens in the middle of an
* operation such as a rebuild operation which should not be interrupted by
* arbitrary code, that could potentially break or trigger the rebuild again.
* This function protects against that by delaying the watchdog() call until
* the end of the current page request.
*
* This is an internal function which should only be called by low-level Drupal
* core functions. It may be removed in a future Drupal 7 release.
*
* @param string $error_msg
* The error message to trigger. As with trigger_error() itself, this is
* limited to 1024 bytes; additional characters beyond that will be removed.
* @param int $error_type
* (optional) The type of error. This should be one of the E_USER family of
* constants. As with trigger_error() itself, this defaults to E_USER_NOTICE
* if not provided.
*
* @see _drupal_log_error()
*/
function _drupal_trigger_error_with_delayed_logging($error_msg, $error_type = E_USER_NOTICE) {
$delay_logging = &drupal_static(__FUNCTION__, FALSE);
$delay_logging = TRUE;
trigger_error($error_msg, $error_type);
$delay_logging = FALSE;
}
/**
* Writes the file scan cache to the persistent cache.
*
* This cache stores all files marked as missing or moved after a file scan
* to prevent unnecessary file scans in subsequent requests. This cache is
* cleared in system_list_reset() (i.e. after a module/theme rebuild).
*/
function drupal_file_scan_write_cache() {
// Only write to the persistent cache if requested, and if we know that any
// data previously in the cache was successfully loaded and merged in by
// _drupal_file_scan_cache().
$file_scans = &_drupal_file_scan_cache();
if (isset($file_scans['#write_cache']) && isset($file_scans['#cache_merge_done'])) {
unset($file_scans['#write_cache']);
cache_set('_drupal_file_scan_cache', $file_scans, 'cache_bootstrap');
}
}
/**
* Loads the persistent variable table.
*
@ -1261,7 +1500,7 @@ function drupal_page_header() {
$default_headers = array(
'Expires' => 'Sun, 19 Nov 1978 05:00:00 GMT',
'Cache-Control' => 'no-cache, must-revalidate, post-check=0, pre-check=0',
'Cache-Control' => 'no-cache, must-revalidate',
// Prevent browsers from sniffing a response and picking a MIME type
// different from the declared content-type, since that can lead to
// XSS and other vulnerabilities.
@ -1439,6 +1678,23 @@ function drupal_unpack($obj, $field = 'data') {
* available to code that needs localization. See st() and get_t() for
* alternatives.
*
* @section sec_context String context
* Matching source strings are normally only translated once, and the same
* translation is used everywhere that has a matching string. However, in some
* cases, a certain English source string needs to have multiple translations.
* One example of this is the string "May", which could be used as either a
* full month name or a 3-letter abbreviated month. In other languages where
* the month name for May has more than 3 letters, you would need to provide
* two different translations (one for the full name and one abbreviated), and
* the correct form would need to be chosen, depending on how "May" is being
* used. To facilitate this, the "May" string should be provided with two
* different contexts in the $options parameter when calling t(). For example:
* @code
* t('May', array(), array('context' => 'Long month name')
* t('May', array(), array('context' => 'Abbreviated month name')
* @endcode
* See https://localize.drupal.org/node/2109 for more information.
*
* @param $string
* A string containing the English string to translate.
* @param $args
@ -1449,8 +1705,9 @@ function drupal_unpack($obj, $field = 'data') {
* An associative array of additional options, with the following elements:
* - 'langcode' (defaults to the current language): The language code to
* translate to a language other than what is used to display the page.
* - 'context' (defaults to the empty context): The context the source string
* belongs to.
* - 'context' (defaults to the empty context): A string giving the context
* that the source string belongs to. See @ref sec_context above for more
* information.
*
* @return
* The translated string.
@ -2945,8 +3202,15 @@ function ip_address() {
// Eliminate all trusted IPs.
$untrusted = array_diff($forwarded, $reverse_proxy_addresses);
// The right-most IP is the most specific we can trust.
$ip_address = array_pop($untrusted);
if (!empty($untrusted)) {
// The right-most IP is the most specific we can trust.
$ip_address = array_pop($untrusted);
}
else {
// All IP addresses in the forwarded array are configured proxy IPs
// (and thus trusted). We take the leftmost IP.
$ip_address = array_shift($forwarded);
}
}
}
}
@ -3187,7 +3451,7 @@ function _registry_check_code($type, $name = NULL) {
$cache_key = $type[0] . $name;
if (isset($lookup_cache[$cache_key])) {
if ($lookup_cache[$cache_key]) {
require_once DRUPAL_ROOT . '/' . $lookup_cache[$cache_key];
include_once DRUPAL_ROOT . '/' . $lookup_cache[$cache_key];
}
return (bool) $lookup_cache[$cache_key];
}
@ -3212,7 +3476,7 @@ function _registry_check_code($type, $name = NULL) {
$lookup_cache[$cache_key] = $file;
if ($file) {
require_once DRUPAL_ROOT . '/' . $file;
include_once DRUPAL_ROOT . '/' . $file;
return TRUE;
}
else {

View File

@ -760,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
@ -785,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
@ -1767,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 = '';
@ -1782,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";
@ -2644,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?
@ -2758,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();
}
@ -3025,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)) {
@ -3060,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']) {
@ -3873,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);

View File

@ -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 @@ abstract class DatabaseConnection extends PDO {
* 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 @@ abstract class DatabaseConnection extends PDO {
* 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 @@ abstract class DatabaseConnection extends PDO {
* 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 @@ abstract class DatabaseConnection extends PDO {
* 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;
}
}
/**

View File

@ -28,6 +28,12 @@ class DatabaseConnection_mysql extends DatabaseConnection {
$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 @@ class DatabaseConnection_mysql extends DatabaseConnection {
// 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 @@ class DatabaseConnection_mysql extends DatabaseConnection {
// 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
@ -206,6 +212,42 @@ class DatabaseConnection_mysql extends DatabaseConnection {
}
}
}
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;
}
}

View File

@ -39,8 +39,8 @@ class DatabaseSchema_mysql extends DatabaseSchema {
$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 @@ class DatabaseSchema_mysql extends DatabaseSchema {
// 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 @@ class DatabaseSchema_mysql extends DatabaseSchema {
$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);

View File

@ -216,6 +216,14 @@ class DatabaseConnection_pgsql extends DatabaseConnection {
return $id;
}
public function utf8mb4IsActive() {
return TRUE;
}
public function utf8mb4IsSupported() {
return TRUE;
}
}
/**

View File

@ -378,6 +378,14 @@ class DatabaseConnection_sqlite extends DatabaseConnection {
}
}
public function utf8mb4IsActive() {
return TRUE;
}
public function utf8mb4IsSupported() {
return TRUE;
}
}
/**

View File

@ -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 @@ class EntityFieldQuery {
* @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 @@ class EntityFieldQuery {
* @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 @@ class EntityFieldQuery {
* @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.

View File

@ -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)');

View File

@ -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);
@ -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.

View 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.'));
}
@ -3545,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 {

View File

@ -809,6 +809,13 @@ function install_system_module(&$install_state) {
variable_set('install_profile_modules', array_diff($modules, array('system')));
$install_state['database_tables_exist'] = TRUE;
// Prevent the hook_requirements() check from telling us to convert the
// database to utf8mb4.
$connection = Database::getConnection();
if ($connection->utf8mb4IsConfigurable() && $connection->utf8mb4IsActive()) {
variable_set('drupal_all_databases_are_utf8mb4', TRUE);
}
}
/**

View File

@ -435,6 +435,13 @@ function locale_language_url_rewrite_url(&$path, &$options) {
switch (variable_get('locale_language_negotiation_url_part', LOCALE_LANGUAGE_NEGOTIATION_URL_PREFIX)) {
case LOCALE_LANGUAGE_NEGOTIATION_URL_DOMAIN:
if ($options['language']->domain) {
// Save the original base URL. If it contains a port, we need to
// retain it below.
if (!empty($options['base_url'])) {
// The colon in the URL scheme messes up the port checking below.
$normalized_base_url = str_replace(array('https://', 'http://'), '', $options['base_url']);
}
// Ask for an absolute URL with our modified base_url.
global $is_https;
$url_scheme = ($is_https) ? 'https://' : 'http://';
@ -449,6 +456,19 @@ function locale_language_url_rewrite_url(&$path, &$options) {
// Apply the appropriate protocol to the URL.
$options['base_url'] = $url_scheme . $host;
// In case either the original base URL or the HTTP host contains a
// port, retain it.
$http_host = $_SERVER['HTTP_HOST'];
if (isset($normalized_base_url) && strpos($normalized_base_url, ':') !== FALSE) {
list($host, $port) = explode(':', $normalized_base_url);
$options['base_url'] .= ':' . $port;
}
elseif (strpos($http_host, ':') !== FALSE) {
list($host, $port) = explode(':', $http_host);
$options['base_url'] .= ':' . $port;
}
if (isset($options['https']) && variable_get('https', FALSE)) {
if ($options['https'] === TRUE) {
$options['base_url'] = str_replace('http://', 'https://', $options['base_url']);
@ -523,6 +543,22 @@ function locale_language_url_rewrite_session(&$path, &$options) {
* possible attack vector (img).
*/
function locale_string_is_safe($string) {
// Some strings have tokens in them. For tokens in the first part of href or
// src HTML attributes, filter_xss() removes part of the token, the part
// before the first colon. filter_xss() assumes it could be an attempt to
// inject javascript. When filter_xss() removes part of tokens, it causes the
// string to not be translatable when it should be translatable. See
// LocaleStringIsSafeTest::testLocaleStringIsSafe().
//
// We can recognize tokens since they are wrapped with brackets and are only
// composed of alphanumeric characters, colon, underscore, and dashes. We can
// be sure these strings are safe to strip out before the string is checked in
// filter_xss() because no dangerous javascript will match that pattern.
//
// @todo Do not strip out the token. Fix filter_xss() to not incorrectly
// alter the string. https://www.drupal.org/node/2372127
$string = preg_replace('/\[[a-z0-9_-]+(:[a-z0-9_-]+)+\]/i', '', $string);
return decode_entities($string) == decode_entities(filter_xss($string, array('a', 'abbr', 'acronym', 'address', 'b', 'bdo', 'big', 'blockquote', 'br', 'caption', 'cite', 'code', 'col', 'colgroup', 'dd', 'del', 'dfn', 'dl', 'dt', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'ins', 'kbd', 'li', 'ol', 'p', 'pre', 'q', 'samp', 'small', 'span', 'strong', 'sub', 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', 'tt', 'ul', 'var')));
}
@ -631,9 +667,6 @@ function locale_add_language($langcode, $name = NULL, $native = NULL, $direction
* translations).
*/
function _locale_import_po($file, $langcode, $mode, $group = NULL) {
// Try to allocate enough time to parse and import the data.
drupal_set_time_limit(240);
// Check if we have the language already in the database.
if (!db_query("SELECT COUNT(language) FROM {languages} WHERE language = :language", array(':language' => $langcode))->fetchField()) {
drupal_set_message(t('The language selected for import is not supported.'), 'error');
@ -717,6 +750,12 @@ function _locale_import_read_po($op, $file, $mode = NULL, $lang = NULL, $group =
$lineno = 0;
while (!feof($fd)) {
// Refresh the time limit every 10 parsed rows to ensure there is always
// enough time to import the data for large PO files.
if (!($lineno % 10)) {
drupal_set_time_limit(30);
}
// A line should not be longer than 10 * 1024.
$line = fgets($fd, 10 * 1024);

View File

@ -2419,7 +2419,7 @@ function menu_set_active_trail($new_trail = NULL) {
// argument placeholders (%). Such links are not contained in regular
// menu trees, and have only been loaded for the additional
// translation that happens here, so as to be able to display them in
// the breadcumb for the current page.
// the breadcrumb for the current page.
// @see _menu_tree_check_access()
// @see _menu_link_translate()
if (strpos($link['href'], '%') !== FALSE) {

View File

@ -227,6 +227,10 @@ function system_list_reset() {
drupal_static_reset('list_themes');
cache_clear_all('bootstrap_modules', 'cache_bootstrap');
cache_clear_all('system_list', 'cache_bootstrap');
// Clean up the bootstrap file scan cache.
drupal_static_reset('_drupal_file_scan_cache');
cache_clear_all('_drupal_file_scan_cache', 'cache_bootstrap');
}
/**
@ -936,7 +940,9 @@ function module_invoke($module, $hook) {
*
* @return
* An array of return values of the hook implementations. If modules return
* arrays from their implementations, those are merged into one array.
* arrays from their implementations, those are merged into one array
* recursively. Note: integer keys in arrays will be lost, as the merge is
* done using array_merge_recursive().
*
* @see drupal_alter()
*/

View File

@ -163,7 +163,7 @@ function _drupal_session_write($sid, $value) {
try {
if (!drupal_save_session()) {
// We don't have anything to do if we are not allowed to save the session.
return;
return TRUE;
}
// Check whether $_SESSION has been changed in this request.
@ -425,7 +425,7 @@ function _drupal_session_destroy($sid) {
// Nothing to do if we are not allowed to change the session.
if (!drupal_save_session()) {
return;
return TRUE;
}
// Delete session data.
@ -446,6 +446,8 @@ function _drupal_session_destroy($sid) {
elseif (variable_get('https', FALSE)) {
_drupal_session_delete_cookie('S' . session_name(), TRUE);
}
return TRUE;
}
/**

View File

@ -1248,6 +1248,7 @@ function path_to_theme() {
function drupal_find_theme_functions($cache, $prefixes) {
$implementations = array();
$functions = get_defined_functions();
$theme_functions = preg_grep('/^(' . implode(')|(', $prefixes) . ')_/', $functions['user']);
foreach ($cache as $hook => $info) {
foreach ($prefixes as $prefix) {
@ -1264,7 +1265,7 @@ function drupal_find_theme_functions($cache, $prefixes) {
// intermediary suggestion.
$pattern = isset($info['pattern']) ? $info['pattern'] : ($hook . '__');
if (!isset($info['base hook']) && !empty($pattern)) {
$matches = preg_grep('/^' . $prefix . '_' . $pattern . '/', $functions['user']);
$matches = preg_grep('/^' . $prefix . '_' . $pattern . '/', $theme_functions);
if ($matches) {
foreach ($matches as $match) {
$new_hook = substr($match, strlen($prefix) + 1);
@ -2638,7 +2639,7 @@ function template_preprocess_page(&$variables) {
// Move some variables to the top level for themer convenience and template cleanliness.
$variables['show_messages'] = $variables['page']['#show_messages'];
foreach (system_region_list($GLOBALS['theme']) as $region_key => $region_name) {
foreach (system_region_list($GLOBALS['theme'], REGIONS_ALL, FALSE) as $region_key) {
if (!isset($variables['page'][$region_key])) {
$variables['page'][$region_key] = array();
}

View File

@ -795,6 +795,14 @@ function update_fix_d7_requirements() {
function update_fix_d7_install_profile() {
$profile = drupal_get_profile();
// 'Default' profile has been renamed to 'Standard' in D7.
// We change the profile here to prevent a broken record in the system table.
// See system_update_7049().
if ($profile == 'default') {
$profile = 'standard';
variable_set('install_profile', $profile);
}
$results = db_select('system', 's')
->fields('s', array('name', 'schema_version'))
->condition('name', $profile)

View File

@ -476,7 +476,7 @@ Drupal.ajax.prototype.getEffect = function (response) {
* Handler for the form redirection error.
*/
Drupal.ajax.prototype.error = function (xmlhttprequest, uri, customMessage) {
alert(Drupal.ajaxError(xmlhttprequest, uri, customMessage));
Drupal.displayAjaxError(Drupal.ajaxError(xmlhttprequest, uri, customMessage));
// Remove the progress element.
if (this.progress.element) {
$(this.progress.element).remove();

View File

@ -310,7 +310,7 @@ Drupal.ACDB.prototype.search = function (searchString) {
}
},
error: function (xmlhttp) {
alert(Drupal.ajaxError(xmlhttp, db.uri));
Drupal.displayAjaxError(Drupal.ajaxError(xmlhttp, db.uri));
}
});
}, this.delay);

View File

@ -413,6 +413,29 @@ Drupal.getSelection = function (element) {
return { 'start': element.selectionStart, 'end': element.selectionEnd };
};
/**
* Add a global variable which determines if the window is being unloaded.
*
* This is primarily used by Drupal.displayAjaxError().
*/
Drupal.beforeUnloadCalled = false;
$(window).bind('beforeunload pagehide', function () {
Drupal.beforeUnloadCalled = true;
});
/**
* Displays a JavaScript error from an Ajax response when appropriate to do so.
*/
Drupal.displayAjaxError = function (message) {
// Skip displaying the message if the user deliberately aborted (for example,
// by reloading the page or navigating to a different page) while the Ajax
// request was still ongoing. See, for example, the discussion at
// http://stackoverflow.com/questions/699941/handle-ajax-error-when-a-user-clicks-refresh.
if (!Drupal.beforeUnloadCalled) {
alert(message);
}
};
/**
* Build an error message from an Ajax response.
*/

View File

@ -106,8 +106,10 @@ Drupal.tableDrag = function (table, tableSettings) {
// Add mouse bindings to the document. The self variable is passed along
// as event handlers do not have direct access to the tableDrag object.
$(document).bind('mousemove', function (event) { return self.dragRow(event, self); });
$(document).bind('mouseup', function (event) { return self.dropRow(event, self); });
$(document).bind('mousemove pointermove', function (event) { return self.dragRow(event, self); });
$(document).bind('mouseup pointerup', function (event) { return self.dropRow(event, self); });
$(document).bind('touchmove', function (event) { return self.dragRow(event.originalEvent.touches[0], self); });
$(document).bind('touchend', function (event) { return self.dropRow(event.originalEvent.touches[0], self); });
};
/**
@ -274,7 +276,10 @@ Drupal.tableDrag.prototype.makeDraggable = function (item) {
});
// Add the mousedown action for the handle.
handle.mousedown(function (event) {
handle.bind('mousedown touchstart pointerdown', function (event) {
if (event.originalEvent.type == "touchstart") {
event = event.originalEvent.touches[0];
}
// Create a new dragObject recording the event information.
self.dragObject = {};
self.dragObject.initMouseOffset = self.getMouseOffset(item, event);

View File

@ -7,8 +7,8 @@ files[] = aggregator.test
configure = admin/config/services/aggregator/settings
stylesheets[all][] = aggregator.css
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -72,7 +72,7 @@ function aggregator_aggregator_remove($feed) {
*/
function aggregator_form_aggregator_admin_form_alter(&$form, $form_state) {
if (in_array('aggregator', variable_get('aggregator_processors', array('aggregator')))) {
$info = module_invoke('aggregator', 'aggregator_process', 'info');
$info = module_invoke('aggregator', 'aggregator_process_info');
$items = drupal_map_assoc(array(3, 5, 10, 15, 20, 25), '_aggregator_items');
$period = drupal_map_assoc(array(3600, 10800, 21600, 32400, 43200, 86400, 172800, 259200, 604800, 1209600, 2419200, 4838400, 9676800), 'format_interval');
$period[AGGREGATOR_CLEAR_NEVER] = t('Never');

View File

@ -5,8 +5,8 @@ version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -6,8 +6,8 @@ core = 7.x
files[] = block.test
configure = admin/structure/block
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -285,8 +285,7 @@ function block_page_build(&$page) {
// Append region description if we are rendering the regions demo page.
$item = menu_get_item();
if ($item['path'] == 'admin/structure/block/demo/' . $theme) {
$visible_regions = array_keys(system_region_list($theme, REGIONS_VISIBLE));
foreach ($visible_regions as $region) {
foreach (system_region_list($theme, REGIONS_VISIBLE, FALSE) as $region) {
$description = '<div class="block-region">' . $all_regions[$region] . '</div>';
$page[$region]['block_description'] = array(
'#markup' => $description,

View File

@ -5,8 +5,8 @@ version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -13,8 +13,8 @@ regions[footer] = Footer
regions[highlighted] = Highlighted
regions[help] = Help
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -5,8 +5,8 @@ version = VERSION
core = 7.x
files[] = blog.test
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -152,7 +152,7 @@ function blog_menu_local_tasks_alter(&$data, $router_item, $root_path) {
}
}
// Provide a helper action link to the author on the 'blog/%' page.
elseif ($root_path == 'blog/%' && $router_item['page_arguments'][0]->uid == $user->uid) {
elseif ($root_path == 'blog/%' && isset($router_item['page_arguments'][0]->uid) && $router_item['page_arguments'][0]->uid == $user->uid) {
$data['actions']['output']['blog'] = array(
'#theme' => 'menu_local_action',
);

View File

@ -7,8 +7,8 @@ files[] = book.test
configure = admin/content/book/settings
stylesheets[all][] = book.css
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -5,8 +5,8 @@ version = VERSION
core = 7.x
files[] = color.test
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -9,8 +9,8 @@ files[] = comment.test
configure = admin/content/comment
stylesheets[all][] = comment.css
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -13,7 +13,7 @@ class CommentHelperCase extends DrupalWebTestCase {
function setUp() {
parent::setUp('comment', 'search');
// Create users and test node.
$this->admin_user = $this->drupalCreateUser(array('administer content types', 'administer comments', 'administer blocks', 'administer actions'));
$this->admin_user = $this->drupalCreateUser(array('administer content types', 'administer comments', 'administer blocks', 'administer actions', 'administer fields'));
$this->web_user = $this->drupalCreateUser(array('access comments', 'post comments', 'create article content', 'edit own comments'));
$this->node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1, 'uid' => $this->web_user->uid));
}

View File

@ -6,8 +6,8 @@ core = 7.x
files[] = contact.test
configure = admin/structure/contact
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -5,8 +5,8 @@ version = VERSION
core = 7.x
files[] = contextual.test
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -7,8 +7,8 @@ files[] = dashboard.test
dependencies[] = block
configure = admin/dashboard/customize
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -294,11 +294,18 @@ function theme_dblog_message($variables) {
else {
$output = t($event->message, unserialize($event->variables));
}
// If the output is expected to be a link, strip all the tags and
// special characters by using filter_xss() without any allowed tags.
// If not, use filter_xss_admin() to allow some tags.
if ($variables['link'] && isset($event->wid)) {
// Truncate message to 56 chars.
// Truncate message to 56 chars after stripping all the tags.
$output = truncate_utf8(filter_xss($output, array()), 56, TRUE, TRUE);
$output = l($output, 'admin/reports/event/' . $event->wid, array('html' => TRUE));
}
else {
// Prevent XSS in log detail pages.
$output = filter_xss_admin($output);
}
}
return $output;
}

View File

@ -5,8 +5,8 @@ version = VERSION
core = 7.x
files[] = dblog.test
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -154,6 +154,15 @@ function dblog_update_7002() {
db_add_index('watchdog', 'severity', array('severity'));
}
/**
* Account for possible legacy systems where dblog was not installed.
*/
function dblog_update_7003() {
if (!db_table_exists('watchdog')) {
db_create_table('watchdog', drupal_get_schema_unprocessed('dblog', 'watchdog'));
}
}
/**
* @} End of "addtogroup updates-7.x-extra".
*/

View File

@ -147,20 +147,27 @@ function dblog_watchdog(array $log_entry) {
if (!function_exists('drupal_substr')) {
require_once DRUPAL_ROOT . '/includes/unicode.inc';
}
Database::getConnection('default', 'default')->insert('watchdog')
->fields(array(
'uid' => $log_entry['uid'],
'type' => drupal_substr($log_entry['type'], 0, 64),
'message' => $log_entry['message'],
'variables' => serialize($log_entry['variables']),
'severity' => $log_entry['severity'],
'link' => drupal_substr($log_entry['link'], 0, 255),
'location' => $log_entry['request_uri'],
'referer' => $log_entry['referer'],
'hostname' => drupal_substr($log_entry['ip'], 0, 128),
'timestamp' => $log_entry['timestamp'],
))
->execute();
try {
Database::getConnection('default', 'default')->insert('watchdog')
->fields(array(
'uid' => $log_entry['uid'],
'type' => drupal_substr($log_entry['type'], 0, 64),
'message' => $log_entry['message'],
'variables' => serialize($log_entry['variables']),
'severity' => $log_entry['severity'],
'link' => drupal_substr($log_entry['link'], 0, 255),
'location' => $log_entry['request_uri'],
'referer' => $log_entry['referer'],
'hostname' => drupal_substr($log_entry['ip'], 0, 128),
'timestamp' => $log_entry['timestamp'],
))
->execute();
}
catch (Exception $e) {
// Exception is ignored so that watchdog does not break pages during the
// installation process or is not able to create the watchdog table during
// installation.
}
}
/**

View File

@ -119,7 +119,9 @@ class DBLogTestCase extends DrupalWebTestCase {
private function generateLogEntries($count, $type = 'custom', $severity = WATCHDOG_NOTICE) {
global $base_root;
// Make it just a little bit harder to pass the link part of the test.
// This long URL makes it just a little bit harder to pass the link part of
// the test with a mix of English words and a repeating series of random
// percent-encoded Chinese characters.
$link = urldecode('/content/xo%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A-lake-isabelle');
// Prepare the fields to be logged
@ -518,6 +520,33 @@ class DBLogTestCase extends DrupalWebTestCase {
$this->assertText(t('Database log cleared.'), 'Confirmation message found');
}
/**
* Verifies that exceptions are caught in dblog_watchdog().
*/
protected function testDBLogException() {
$log = array(
'type' => 'custom',
'message' => 'Log entry added to test watchdog handling of Exceptions.',
'variables' => array(),
'severity' => WATCHDOG_NOTICE,
'link' => NULL,
'user' => $this->big_user,
'uid' => isset($this->big_user->uid) ? $this->big_user->uid : 0,
'request_uri' => request_uri(),
'referer' => $_SERVER['HTTP_REFERER'],
'ip' => ip_address(),
'timestamp' => REQUEST_TIME,
);
// Remove watchdog table temporarily to simulate it missing during
// installation.
db_query("DROP TABLE {watchdog}");
// Add a watchdog entry.
// This should not throw an Exception, but fail silently.
dblog_watchdog($log);
}
/**
* Gets the database log event information from the browser page.
*
@ -636,4 +665,32 @@ class DBLogTestCase extends DrupalWebTestCase {
// Document Object Model (DOM).
$this->assertLink(html_entity_decode($message_text), 0, $message);
}
/**
* Make sure HTML tags are filtered out in the log detail page.
*/
public function testLogMessageSanitized() {
$this->drupalLogin($this->big_user);
// Make sure dangerous HTML tags are filtered out in log detail page.
$log = array(
'uid' => 0,
'type' => 'custom',
'message' => "<script>alert('foo');</script> <strong>Lorem ipsum</strong>",
'variables' => NULL,
'severity' => WATCHDOG_NOTICE,
'link' => 'foo/bar',
'request_uri' => 'http://example.com?dblog=1',
'referer' => 'http://example.org?dblog=2',
'ip' => '0.0.1.0',
'timestamp' => REQUEST_TIME,
);
dblog_watchdog($log);
$wid = db_query('SELECT MAX(wid) FROM {watchdog}')->fetchField();
$this->drupalGet('admin/reports/event/' . $wid);
$this->assertResponse(200);
$this->assertNoRaw("<script>alert('foo');</script>");
$this->assertRaw("alert('foo'); <strong>Lorem ipsum</strong>");
}
}

View File

@ -189,7 +189,7 @@ function field_create_field($field) {
}
// Clear caches
field_cache_clear(TRUE);
field_cache_clear();
// Invoke external hooks after the cache is cleared for API consistency.
module_invoke_all('field_create_field', $field);
@ -288,7 +288,7 @@ function field_update_field($field) {
drupal_write_record('field_config', $field, $primary_key);
// Clear caches
field_cache_clear(TRUE);
field_cache_clear();
// Invoke external hooks after the cache is cleared for API consistency.
module_invoke_all('field_update_field', $field, $prior_field, $has_data);
@ -430,7 +430,7 @@ function field_delete_field($field_name) {
->execute();
// Clear the cache.
field_cache_clear(TRUE);
field_cache_clear();
module_invoke_all('field_delete_field', $field);
}

View File

@ -11,8 +11,8 @@ dependencies[] = field_sql_storage
required = TRUE
stylesheets[all][] = theme/field.css
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -467,6 +467,27 @@ function field_update_7003() {
// Empty update to force a rebuild of the registry.
}
/**
* Grant the new "administer fields" permission to trusted users.
*/
function field_update_7004() {
// Assign the permission to anyone that already has a trusted core permission
// that would have previously let them administer fields on an entity type.
$rids = array();
$permissions = array(
'administer site configuration',
'administer content types',
'administer users',
);
foreach ($permissions as $permission) {
$rids = array_merge($rids, array_keys(user_roles(FALSE, $permission)));
}
$rids = array_unique($rids);
foreach ($rids as $rid) {
_update_7000_user_role_grant_permissions($rid, array('administer fields'), 'field');
}
}
/**
* @} End of "addtogroup updates-7.x-extra".
*/

View File

@ -316,6 +316,21 @@ function field_help($path, $arg) {
}
}
/**
* Implements hook_permission().
*/
function field_permission() {
return array(
'administer fields' => array(
'title' => t('Administer fields'),
'description' => t('Additional permissions are required based on what the fields are attached to (for example, <a href="@url">administer content types</a> to manage fields attached to content).', array(
'@url' => '#module-node',
)),
'restrict access' => TRUE,
),
);
}
/**
* Implements hook_theme().
*/

View File

@ -7,8 +7,8 @@ dependencies[] = field
files[] = field_sql_storage.test
required = TRUE
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -7,8 +7,8 @@ dependencies[] = field
dependencies[] = options
files[] = tests/list.test
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -212,7 +212,7 @@ class ListFieldUITestCase extends FieldTestCase {
parent::setUp('field_test', 'field_ui');
// Create test user.
$admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer taxonomy'));
$admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer taxonomy', 'administer fields'));
$this->drupalLogin($admin_user);
// Create content type, with underscores.

View File

@ -5,8 +5,8 @@ package = Testing
version = VERSION
hidden = TRUE
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -6,8 +6,8 @@ core = 7.x
dependencies[] = field
files[] = number.test
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -222,6 +222,8 @@ function number_field_formatter_settings_form($field, $instance, $view_mode, $fo
$display = $instance['display'][$view_mode];
$settings = $display['settings'];
$element = array();
if ($display['type'] == 'number_decimal' || $display['type'] == 'number_integer') {
$options = array(
'' => t('<none>'),

View File

@ -23,7 +23,7 @@ class NumberFieldTestCase extends DrupalWebTestCase {
function setUp() {
parent::setUp('field_test');
$this->web_user = $this->drupalCreateUser(array('access field_test content', 'administer field_test content', 'administer content types'));
$this->web_user = $this->drupalCreateUser(array('access field_test content', 'administer field_test content', 'administer content types', 'administer fields'));
$this->drupalLogin($this->web_user);
}

View File

@ -6,8 +6,8 @@ core = 7.x
dependencies[] = field
files[] = options.test
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -54,7 +54,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
$this->bool = field_create_field($this->bool);
// Create a web user.
$this->web_user = $this->drupalCreateUser(array('access field_test content', 'administer field_test content'));
$this->web_user = $this->drupalCreateUser(array('access field_test content', 'administer field_test content', 'administer fields'));
$this->drupalLogin($this->web_user);
}
@ -460,7 +460,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
$this->assertNoFieldChecked("edit-bool-$langcode");
// Create admin user.
$admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer taxonomy'));
$admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer taxonomy', 'administer fields'));
$this->drupalLogin($admin_user);
// Create a test field instance.

View File

@ -7,8 +7,8 @@ dependencies[] = field
files[] = text.test
required = TRUE
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -424,6 +424,7 @@ class TextTranslationTestCase extends DrupalWebTestCase {
'administer content types',
'access administration pages',
'bypass node access',
'administer fields',
filter_permission_name($full_html_format),
));
$this->translator = $this->drupalCreateUser(array('create article content', 'edit own article content', 'translate content'));

View File

@ -6,8 +6,8 @@ files[] = field_test.entity.inc
version = VERSION
hidden = TRUE
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -6,8 +6,8 @@ core = 7.x
dependencies[] = field
files[] = field_ui.test
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -106,9 +106,19 @@ function field_ui_menu() {
$access = array_intersect_key($bundle_info['admin'], drupal_map_assoc(array('access callback', 'access arguments')));
$access += array(
'access callback' => 'user_access',
'access arguments' => array('administer site configuration'),
'access arguments' => array('administer fields'),
);
// Add the "administer fields" permission on top of the access
// restriction because the field UI should only be accessible to
// trusted users.
if ($access['access callback'] != 'user_access' || $access['access arguments'] != array('administer fields')) {
$access = array(
'access callback' => 'field_ui_admin_access',
'access arguments' => array($access['access callback'], $access['access arguments']),
);
}
$items["$path/fields"] = array(
'title' => 'Manage fields',
'page callback' => 'drupal_get_form',
@ -392,3 +402,13 @@ function field_ui_form_node_type_form_submit($form, &$form_state) {
$form_state['redirect'] = _field_ui_bundle_admin_path('node', $form_state['values']['type']) .'/fields';
}
}
/**
* Access callback to determine if a user is allowed to use the field UI.
*
* Only grant access if the user has both the "administer fields" permission and
* is granted access by the entity specific restrictions.
*/
function field_ui_admin_access($access_callback, $access_arguments) {
return user_access('administer fields') && call_user_func_array($access_callback, $access_arguments);
}

View File

@ -22,7 +22,7 @@ class FieldUITestCase extends DrupalWebTestCase {
parent::setUp($modules);
// Create test user.
$admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer taxonomy'));
$admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer taxonomy', 'administer fields'));
$this->drupalLogin($admin_user);
// Create content type, with underscores.
@ -695,7 +695,7 @@ class FieldUIAlterTestCase extends DrupalWebTestCase {
parent::setUp(array('field_test'));
// Create test user.
$admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer users'));
$admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer users', 'administer fields'));
$this->drupalLogin($admin_user);
}

View File

@ -6,8 +6,8 @@ core = 7.x
dependencies[] = field
files[] = tests/file.test
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -457,6 +457,17 @@ function file_managed_file_process($element, &$form_state, $form) {
'#markup' => theme('file_link', array('file' => $element['#file'])) . ' ',
'#weight' => -10,
);
// Anonymous users who have uploaded a temporary file need a
// non-session-based token added so file_managed_file_value() can check
// that they have permission to use this file on subsequent submissions of
// the same form (for example, after an Ajax upload or form validation
// error).
if (!$GLOBALS['user']->uid && $element['#file']->status != FILE_STATUS_PERMANENT) {
$element['fid_token'] = array(
'#type' => 'hidden',
'#value' => drupal_hmac_base64('file-' . $fid, drupal_get_private_key() . drupal_get_hash_salt()),
);
}
}
// Add the extension list to the page as JavaScript settings.
@ -533,13 +544,24 @@ function file_managed_file_value(&$element, $input = FALSE, $form_state = NULL)
$force_default = TRUE;
}
// Temporary files that belong to other users should never be allowed.
// Since file ownership can't be determined for anonymous users, they
// are not allowed to reuse temporary files at all.
elseif ($file->status != FILE_STATUS_PERMANENT && (!$GLOBALS['user']->uid || $file->uid != $GLOBALS['user']->uid)) {
$force_default = TRUE;
elseif ($file->status != FILE_STATUS_PERMANENT) {
if ($GLOBALS['user']->uid && $file->uid != $GLOBALS['user']->uid) {
$force_default = TRUE;
}
// Since file ownership can't be determined for anonymous users, they
// are not allowed to reuse temporary files at all. But they do need
// to be able to reuse their own files from earlier submissions of
// the same form, so to allow that, check for the token added by
// file_managed_file_process().
elseif (!$GLOBALS['user']->uid) {
$token = drupal_array_get_nested_value($form_state['input'], array_merge($element['#parents'], array('fid_token')));
if ($token !== drupal_hmac_base64('file-' . $file->fid, drupal_get_private_key() . drupal_get_hash_salt())) {
$force_default = TRUE;
}
}
}
// If all checks pass, allow the file to be changed.
else {
if (!$force_default) {
$fid = $file->fid;
}
}

View File

@ -22,7 +22,7 @@ class FileFieldTestCase extends DrupalWebTestCase {
$modules[] = 'file';
$modules[] = 'file_module_test';
parent::setUp($modules);
$this->admin_user = $this->drupalCreateUser(array('access content', 'access administration pages', 'administer site configuration', 'administer users', 'administer permissions', 'administer content types', 'administer nodes', 'bypass node access'));
$this->admin_user = $this->drupalCreateUser(array('access content', 'access administration pages', 'administer site configuration', 'administer users', 'administer permissions', 'administer content types', 'administer nodes', 'bypass node access', 'administer fields'));
$this->drupalLogin($this->admin_user);
}
@ -1503,3 +1503,178 @@ class FilePrivateTestCase extends FileFieldTestCase {
$this->assertResponse(403, 'Confirmed that access is denied for the file without view field access permission after attempting to attach it to a new node.');
}
}
/**
* Confirm that file field submissions work correctly for anonymous visitors.
*/
class FileFieldAnonymousSubmission extends FileFieldTestCase {
public static function getInfo() {
return array(
'name' => 'File form anonymous submission',
'description' => 'Test anonymous form submission.',
'group' => 'File',
);
}
function setUp() {
parent::setUp();
// Allow node submissions by anonymous users.
user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, array(
'create article content',
'access content',
));
}
/**
* Tests the basic node submission for an anonymous visitor.
*/
function testAnonymousNode() {
$bundle_label = 'Article';
$node_title = 'Test page';
// Load the node form.
$this->drupalGet('node/add/article');
$this->assertResponse(200, 'Loaded the article node form.');
$this->assertText(strip_tags(t('Create @name', array('@name' => $bundle_label))));
$edit = array(
'title' => $node_title,
'body[und][0][value]' => 'Test article',
'body[und][0][format]' => 'filtered_html',
);
$this->drupalPost(NULL, $edit, t('Save'));
$this->assertResponse(200);
$t_args = array('@type' => $bundle_label, '%title' => $node_title);
$this->assertText(strip_tags(t('@type %title has been created.', $t_args)), 'The node was created.');
$matches = array();
if (preg_match('@node/(\d+)$@', $this->getUrl(), $matches)) {
$nid = end($matches);
$this->assertNotEqual($nid, 0, 'The node ID was extracted from the URL.');
$node = node_load($nid);
$this->assertNotEqual($node, NULL, 'The node was loaded successfully.');
}
}
/**
* Tests file submission for an anonymous visitor.
*/
function testAnonymousNodeWithFile() {
$bundle_label = 'Article';
$node_title = 'Test page';
// Load the node form.
$this->drupalGet('node/add/article');
$this->assertResponse(200, 'Loaded the article node form.');
$this->assertText(strip_tags(t('Create @name', array('@name' => $bundle_label))));
// Generate an image file.
$image = $this->getTestImage();
// Submit the form.
$edit = array(
'title' => $node_title,
'body[und][0][value]' => 'Test article',
'body[und][0][format]' => 'filtered_html',
'files[field_image_und_0]' => drupal_realpath($image->uri),
);
$this->drupalPost(NULL, $edit, t('Save'));
$this->assertResponse(200);
$t_args = array('@type' => $bundle_label, '%title' => $node_title);
$this->assertText(strip_tags(t('@type %title has been created.', $t_args)), 'The node was created.');
$matches = array();
if (preg_match('@node/(\d+)$@', $this->getUrl(), $matches)) {
$nid = end($matches);
$this->assertNotEqual($nid, 0, 'The node ID was extracted from the URL.');
$node = node_load($nid);
$this->assertNotEqual($node, NULL, 'The node was loaded successfully.');
$this->assertEqual($node->field_image[LANGUAGE_NONE][0]['filename'], $image->filename, 'The image was uploaded successfully.');
}
}
/**
* Tests file submission for an anonymous visitor with a missing node title.
*/
function testAnonymousNodeWithFileWithoutTitle() {
$this->drupalLogout();
$this->_testNodeWithFileWithoutTitle();
}
/**
* Tests file submission for an authenticated user with a missing node title.
*/
function testAuthenticatedNodeWithFileWithoutTitle() {
$admin_user = $this->drupalCreateUser(array(
'bypass node access',
'access content overview',
'administer nodes',
));
$this->drupalLogin($admin_user);
$this->_testNodeWithFileWithoutTitle();
}
/**
* Helper method to test file submissions with missing node titles.
*/
protected function _testNodeWithFileWithoutTitle() {
$bundle_label = 'Article';
$node_title = 'Test page';
// Load the node form.
$this->drupalGet('node/add/article');
$this->assertResponse(200, 'Loaded the article node form.');
$this->assertText(strip_tags(t('Create @name', array('@name' => $bundle_label))));
// Generate an image file.
$image = $this->getTestImage();
// Submit the form but exclude the title field.
$edit = array(
'body[und][0][value]' => 'Test article',
'body[und][0][format]' => 'filtered_html',
'files[field_image_und_0]' => drupal_realpath($image->uri),
);
$this->drupalPost(NULL, $edit, t('Save'));
$this->assertResponse(200);
$t_args = array('@type' => $bundle_label, '%title' => $node_title);
$this->assertNoText(strip_tags(t('@type %title has been created.', $t_args)), 'The node was created.');
$this->assertText(t('!name field is required.', array('!name' => t('Title'))));
// Submit the form again but this time with the missing title field. This
// should still work.
$edit = array(
'title' => $node_title,
);
$this->drupalPost(NULL, $edit, t('Save'));
// Confirm the final submission actually worked.
$t_args = array('@type' => $bundle_label, '%title' => $node_title);
$this->assertText(strip_tags(t('@type %title has been created.', $t_args)), 'The node was created.');
$matches = array();
if (preg_match('@node/(\d+)$@', $this->getUrl(), $matches)) {
$nid = end($matches);
$this->assertNotEqual($nid, 0, 'The node ID was extracted from the URL.');
$node = node_load($nid);
$this->assertNotEqual($node, NULL, 'The node was loaded successfully.');
$this->assertEqual($node->field_image[LANGUAGE_NONE][0]['filename'], $image->filename, 'The image was uploaded successfully.');
}
}
/**
* Generates a test image.
*
* @return stdClass
* A file object.
*/
function getTestImage() {
// Get a file to upload.
$file = current($this->drupalGetTestFiles('image'));
// Add a filesize property to files as would be read by file_load().
$file->filesize = filesize($file->uri);
return $file;
}
}

View File

@ -5,8 +5,8 @@ version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -7,8 +7,8 @@ files[] = filter.test
required = TRUE
configure = admin/config/content/formats
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -1120,8 +1120,12 @@ class FilterUnitTestCase extends DrupalUnitTestCase {
$f = filter_xss("<img src=\"jav\0a\0\0cript:alert(0)\">", array('img'));
$this->assertNoNormalized($f, 'cript', 'HTML scheme clearing evasion -- embedded nulls.');
$f = filter_xss('<img src=" &#14; javascript:alert(0)">', array('img'));
$this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing evasion -- spaces and metacharacters before scheme.');
// @todo This dataset currently fails under 5.4 because of
// https://www.drupal.org/node/1210798. Restore after it's fixed.
if (version_compare(PHP_VERSION, '5.4.0', '<')) {
$f = filter_xss('<img src=" &#14; javascript:alert(0)">', array('img'));
$this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing evasion -- spaces and metacharacters before scheme.');
}
$f = filter_xss('<img src="vbscript:msgbox(0)">', array('img'));
$this->assertNoNormalized($f, 'vbscript', 'HTML scheme clearing evasion -- another scheme.');

View File

@ -9,8 +9,8 @@ files[] = forum.test
configure = admin/structure/forum
stylesheets[all][] = forum.css
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -5,8 +5,8 @@ version = VERSION
core = 7.x
files[] = help.test
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -7,8 +7,8 @@ dependencies[] = file
files[] = image.test
configure = admin/config/media/image-styles
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -32,7 +32,7 @@ class ImageFieldTestCase extends DrupalWebTestCase {
function setUp() {
parent::setUp('image');
$this->admin_user = $this->drupalCreateUser(array('access content', 'access administration pages', 'administer site configuration', 'administer content types', 'administer nodes', 'create article content', 'edit any article content', 'delete any article content', 'administer image styles'));
$this->admin_user = $this->drupalCreateUser(array('access content', 'access administration pages', 'administer site configuration', 'administer content types', 'administer nodes', 'create article content', 'edit any article content', 'delete any article content', 'administer image styles', 'administer fields'));
$this->drupalLogin($this->admin_user);
}
@ -285,7 +285,7 @@ class ImageStylesPathAndUrlTestCase extends DrupalWebTestCase {
$this->assertEqual($this->drupalGetHeader('Content-Length'), $generated_image_info['file_size'], 'Expected Content-Length was reported.');
if ($scheme == 'private') {
$this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', 'Expires header was sent.');
$this->assertEqual($this->drupalGetHeader('Cache-Control'), 'no-cache, must-revalidate, post-check=0, pre-check=0', 'Cache-Control header was set to prevent caching.');
$this->assertEqual($this->drupalGetHeader('Cache-Control'), 'no-cache, must-revalidate', 'Cache-Control header was set to prevent caching.');
$this->assertEqual($this->drupalGetHeader('X-Image-Owned-By'), 'image_module_test', 'Expected custom header has been added.');
// Make sure that a second request to the already existing derivate works

View File

@ -6,8 +6,8 @@ core = 7.x
files[] = image_module_test.module
hidden = TRUE
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -1194,7 +1194,7 @@ function locale_translate_edit_form_submit($form, &$form_state) {
$translation = db_query("SELECT translation FROM {locales_target} WHERE lid = :lid AND language = :language", array(':lid' => $lid, ':language' => $key))->fetchField();
if (!empty($value)) {
// Only update or insert if we have a value to use.
if (!empty($translation)) {
if (is_string($translation)) {
db_update('locales_target')
->fields(array(
'translation' => $value,

View File

@ -6,8 +6,8 @@ core = 7.x
files[] = locale.test
configure = admin/config/regional/language
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -393,6 +393,16 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase {
// The indicator should not be here.
$this->assertNoRaw($language_indicator, 'String is translated.');
// Verify that a translation set which has an empty target string can be
// updated without any database error.
db_update('locales_target')
->fields(array('translation' => ''))
->condition('language', $langcode, '=')
->condition('lid', $lid, '=')
->execute();
$this->drupalPost('admin/config/regional/translate/edit/' . $lid, $edit, t('Save translations'));
$this->assertText(t('The string has been saved.'), 'The string has been saved.');
// Try to edit a non-existent string and ensure we're redirected correctly.
// Assuming we don't have 999,999 strings already.
$random_lid = 999999;
@ -2237,6 +2247,37 @@ class LocaleContentFunctionalTest extends DrupalWebTestCase {
$this->drupalLogout();
}
/**
* Verifies that nodes may be created with different languages.
*/
function testNodeCreationWithLanguage() {
// Create an admin user and log them in.
$perms = array(
// Standard node permissions.
'create page content',
'administer content types',
'administer nodes',
'bypass node access',
// Locale.
'administer languages',
);
$web_user = $this->drupalCreateUser($perms);
$this->drupalLogin($web_user);
// Create some test nodes using different langcodes.
foreach (array(LANGUAGE_NONE, 'en', 'fr') as $langcode) {
$node_args = array(
'type' => 'page',
'promote' => 1,
'language' => $langcode,
);
$node = $this->drupalCreateNode($node_args);
$node_reloaded = node_load($node->nid, NULL, TRUE);
$this->assertEqual($node_reloaded->language, $langcode, format_string('The language code of the node was successfully set to @langcode.', array('@langcode' => $langcode)));
}
}
}
/**
@ -2629,6 +2670,68 @@ class LocaleUrlRewritingTest extends DrupalWebTestCase {
$this->drupalGet("$prefix/$path");
$this->assertResponse(404, $message2);
}
/**
* Check URL rewriting when using a domain name and a non-standard port.
*/
function testDomainNameNegotiationPort() {
$language_domain = 'example.fr';
$edit = array(
'locale_language_negotiation_url_part' => 1,
);
$this->drupalPost('admin/config/regional/language/configure/url', $edit, t('Save configuration'));
$edit = array(
'prefix' => '',
'domain' => $language_domain
);
$this->drupalPost('admin/config/regional/language/edit/fr', $edit, t('Save language'));
// Enable domain configuration.
variable_set('locale_language_negotiation_url_part', LOCALE_LANGUAGE_NEGOTIATION_URL_DOMAIN);
// Reset static caching.
drupal_static_reset('language_list');
drupal_static_reset('language_url_outbound_alter');
drupal_static_reset('language_url_rewrite_url');
// In case index.php is part of the URLs, we need to adapt the asserted
// URLs as well.
$index_php = strpos(url('', array('absolute' => TRUE)), 'index.php') !== FALSE;
// Remember current HTTP_HOST.
$http_host = $_SERVER['HTTP_HOST'];
// Fake a different port.
$_SERVER['HTTP_HOST'] .= ':88';
// Create an absolute French link.
$languages = language_list();
$language = $languages['fr'];
$url = url('', array(
'absolute' => TRUE,
'language' => $language
));
$expected = 'http://example.fr:88/';
$expected .= $index_php ? 'index.php/' : '';
$this->assertEqual($url, $expected, 'The right port is used.');
// If we set the port explicitly in url(), it should not be overriden.
$url = url('', array(
'absolute' => TRUE,
'language' => $language,
'base_url' => $GLOBALS['base_url'] . ':90',
));
$expected = 'http://example.fr:90/';
$expected .= $index_php ? 'index.php/' : '';
$this->assertEqual($url, $expected, 'A given port is not overriden.');
// Restore HTTP_HOST.
$_SERVER['HTTP_HOST'] = $http_host;
}
}
/**
@ -3141,3 +3244,46 @@ class LocaleCSSAlterTest extends DrupalWebTestCase {
$this->assertRaw('@import url("' . $base_url . '/modules/system/system.messages.css' . $query_string . '");' . "\n" . '@import url("' . $base_url . '/modules/system/system.messages-rtl.css' . $query_string . '");' . "\n", 'CSS: system.messages-rtl.css is added directly after system.messages.css.');
}
}
/**
* Tests locale translation safe string handling.
*/
class LocaleStringIsSafeTest extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Test if a string is safe',
'description' => 'Tests locale translation safe string handling.',
'group' => 'Locale',
);
}
function setUp() {
parent::setUp('locale');
}
/**
* Tests for locale_string_is_safe().
*/
public function testLocaleStringIsSafe() {
// Check a translatable string without HTML.
$string = 'Hello world!';
$result = locale_string_is_safe($string);
$this->assertTrue($result);
// Check a translatable string which includes trustable HTML.
$string = 'Hello <strong>world</strong>!';
$result = locale_string_is_safe($string);
$this->assertTrue($result);
// Check an untranslatable string which includes untrustable HTML (according
// to the locale_string_is_safe() function definition).
$string = 'Hello <img src="world.png" alt="world" />!';
$result = locale_string_is_safe($string);
$this->assertFalse($result);
// Check a translatable string which includes a token in an href attribute.
$string = 'Hi <a href="[current-user:url]">user</a>';
$result = locale_string_is_safe($string);
$this->assertTrue($result);
}
}

View File

@ -5,8 +5,8 @@ package = Testing
version = VERSION
hidden = TRUE
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -281,6 +281,7 @@ function menu_edit_item($form, &$form_state, $type, $item, $menu) {
$form['link_title'] = array(
'#type' => 'textfield',
'#title' => t('Menu link title'),
'#maxlength' => 255,
'#default_value' => $item['link_title'],
'#description' => t('The text to be used for this link in the menu.'),
'#required' => TRUE,
@ -305,7 +306,7 @@ function menu_edit_item($form, &$form_state, $type, $item, $menu) {
'#title' => t('Path'),
'#maxlength' => 255,
'#default_value' => $path,
'#description' => t('The path for this menu link. This can be an internal Drupal path such as %add-node or an external URL such as %drupal. Enter %front to link to the front page.', array('%front' => '<front>', '%add-node' => 'node/add', '%drupal' => 'http://drupal.org')),
'#description' => t('The path for this menu link. This can be an internal path such as %add-node or an external URL such as %example. Enter %front to link to the front page.', array('%front' => '<front>', '%add-node' => 'node/add', '%example' => 'http://example.com')),
'#required' => TRUE,
);
$form['actions']['delete'] = array(

View File

@ -6,8 +6,8 @@ core = 7.x
files[] = menu.test
configure = admin/structure/menu
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -674,6 +674,7 @@ function menu_form_node_form_alter(&$form, $form_state) {
$form['menu']['link']['link_title'] = array(
'#type' => 'textfield',
'#title' => t('Menu link title'),
'#maxlength' => 255,
'#default_value' => $link['link_title'],
);

View File

@ -648,7 +648,12 @@ class MenuNodeTestCase extends DrupalWebTestCase {
);
$this->drupalPost('admin/structure/types/manage/page', $edit, t('Save content type'));
// Create a node.
// Verify that the menu link title on the node add form has the correct
// maxlength.
$this->drupalGet('node/add/page');
$this->assertPattern('/<input .* id="edit-menu-link-title" .* maxlength="255" .* \/>/', 'Menu link title field has correct maxlength in node add form.');
// Create a node with menu link disabled.
$node_title = $this->randomName();
$language = LANGUAGE_NONE;
$edit = array(
@ -684,6 +689,10 @@ class MenuNodeTestCase extends DrupalWebTestCase {
$this->drupalGet('node/' . $node->nid . '/edit');
$this->assertOptionSelected('edit-menu-weight', 17, 'Menu weight correct in edit form');
// Verify that the menu link title on the node edit form has the correct
// maxlength.
$this->assertPattern('/<input .* id="edit-menu-link-title" .* maxlength="255" .* \/>/', 'Menu link title field has correct maxlength in node edit form.');
// Edit the node and remove the menu link.
$edit = array(
'menu[enabled]' => FALSE,

View File

@ -11,7 +11,7 @@
function node_overview_types() {
$types = node_type_get_types();
$names = node_type_get_names();
$field_ui = module_exists('field_ui');
$field_ui = module_exists('field_ui') && user_access('administer fields');
$header = array(t('Name'), array('data' => t('Operations'), 'colspan' => $field_ui ? '4' : '2'));
$rows = array();

View File

@ -9,8 +9,8 @@ required = TRUE
configure = admin/structure/types
stylesheets[all][] = node.css
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -410,6 +410,7 @@ function node_schema() {
'nid' => array(
'description' => 'The {node}.nid that was read.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
@ -943,6 +944,23 @@ function node_update_7015() {
->execute();
}
/**
* Change {history}.nid to an unsigned int in order to match {node}.nid.
*/
function node_update_7016() {
db_drop_primary_key('history');
db_drop_index('history', 'nid');
db_change_field('history', 'nid', 'nid', array(
'description' => 'The {node}.nid that was read.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
));
db_add_primary_key('history', array('uid', 'nid'));
db_add_index('history', 'nid', array('nid'));
}
/**
* @} End of "addtogroup updates-7.x-extra".
*/

View File

@ -1518,7 +1518,7 @@ class NodeTypeTestCase extends DrupalWebTestCase {
* Tests editing a node type using the UI.
*/
function testNodeTypeEditing() {
$web_user = $this->drupalCreateUser(array('bypass node access', 'administer content types'));
$web_user = $this->drupalCreateUser(array('bypass node access', 'administer content types', 'administer fields'));
$this->drupalLogin($web_user);
$instance = field_info_instance('node', 'body', 'page');
@ -2768,8 +2768,8 @@ class NodeAccessFieldTestCase extends NodeWebTestCase {
node_access_rebuild();
// Create some users.
$this->admin_user = $this->drupalCreateUser(array('access content', 'bypass node access'));
$this->content_admin_user = $this->drupalCreateUser(array('access content', 'administer content types'));
$this->admin_user = $this->drupalCreateUser(array('access content', 'bypass node access', 'administer fields'));
$this->content_admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer fields'));
// Add a custom field to the page content type.
$this->field_name = drupal_strtolower($this->randomName() . '_field_name');
@ -2986,3 +2986,36 @@ class NodePageCacheTest extends NodeWebTestCase {
$this->assertResponse(404);
}
}
/**
* Tests that multi-byte UTF-8 characters are stored and retrieved correctly.
*/
class NodeMultiByteUtf8Test extends NodeWebTestCase {
public static function getInfo() {
return array(
'name' => 'Multi-byte UTF-8',
'description' => 'Test that multi-byte UTF-8 characters are stored and retrieved correctly.',
'group' => 'Node',
);
}
/**
* Tests that multi-byte UTF-8 characters are stored and retrieved correctly.
*/
public function testMultiByteUtf8() {
$connection = Database::getConnection();
// On MySQL, this test will only run if 'charset' is set to 'utf8mb4' in
// settings.php.
if (!($connection->utf8mb4IsSupported() && $connection->utf8mb4IsActive())) {
return;
}
$title = '🐙';
$this->assertTrue(drupal_strlen($title, 'utf-8') < strlen($title), 'Title has multi-byte characters.');
$node = $this->drupalCreateNode(array('title' => $title));
$this->drupalGet('node/' . $node->nid);
$result = $this->xpath('//h1[@id="page-title"]');
$this->assertEqual(trim((string) $result[0]), $title, 'The passed title was returned.');
}
}

View File

@ -5,8 +5,8 @@ version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -5,8 +5,8 @@ version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -5,8 +5,8 @@ version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -5,8 +5,8 @@ package = Core
core = 7.x
files[] = openid.test
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -680,11 +680,11 @@ class OpenIDTestCase extends DrupalWebTestCase {
* Test _openid_dh_XXX_to_XXX() functions.
*/
function testConversion() {
$this->assertEqual(_openid_dh_long_to_base64('12345678901234567890123456789012345678901234567890'), 'CHJ/Y2mq+DyhUCZ0evjH8ZbOPwrS', '_openid_dh_long_to_base64() returned expected result.');
$this->assertEqual(_openid_dh_base64_to_long('BsH/g8Nrpn2dtBSdu/sr1y8hxwyx'), '09876543210987654321098765432109876543210987654321', '_openid_dh_base64_to_long() returned expected result.');
$this->assertIdentical(_openid_dh_long_to_base64('12345678901234567890123456789012345678901234567890'), 'CHJ/Y2mq+DyhUCZ0evjH8ZbOPwrS', '_openid_dh_long_to_base64() returned expected result.');
$this->assertIdentical(_openid_dh_base64_to_long('BsH/g8Nrpn2dtBSdu/sr1y8hxwyx'), '9876543210987654321098765432109876543210987654321', '_openid_dh_base64_to_long() returned expected result.');
$this->assertEqual(_openid_dh_long_to_binary('12345678901234567890123456789012345678901234567890'), "\x08r\x7fci\xaa\xf8<\xa1P&tz\xf8\xc7\xf1\x96\xce?\x0a\xd2", '_openid_dh_long_to_binary() returned expected result.');
$this->assertEqual(_openid_dh_binary_to_long("\x06\xc1\xff\x83\xc3k\xa6}\x9d\xb4\x14\x9d\xbb\xfb+\xd7/!\xc7\x0c\xb1"), '09876543210987654321098765432109876543210987654321', '_openid_dh_binary_to_long() returned expected result.');
$this->assertIdentical(_openid_dh_long_to_binary('12345678901234567890123456789012345678901234567890'), "\x08r\x7fci\xaa\xf8<\xa1P&tz\xf8\xc7\xf1\x96\xce?\x0a\xd2", '_openid_dh_long_to_binary() returned expected result.');
$this->assertIdentical(_openid_dh_binary_to_long("\x06\xc1\xff\x83\xc3k\xa6}\x9d\xb4\x14\x9d\xbb\xfb+\xd7/!\xc7\x0c\xb1"), '9876543210987654321098765432109876543210987654321', '_openid_dh_binary_to_long() returned expected result.');
}
/**

View File

@ -6,8 +6,8 @@ core = 7.x
dependencies[] = openid
hidden = TRUE
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -4,8 +4,8 @@ package = Core
version = VERSION
core = 7.x
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -78,6 +78,20 @@ function overlay_theme() {
);
}
/**
* Implements hook_form_alter().
*/
function overlay_form_alter(&$form, &$form_state) {
// Add a hidden element to prevent dropping out of the overlay when a form is
// submitted inside the overlay using a GET method.
if (isset($form['#method']) && $form['#method'] == 'get' && isset($_REQUEST['render']) && $_REQUEST['render'] == 'overlay' && !isset($form['render'])) {
$form['render'] = array(
'#type' => 'hidden',
'#value' => 'overlay',
);
}
}
/**
* Implements hook_form_FORM_ID_alter().
*/

View File

@ -6,8 +6,8 @@ core = 7.x
files[] = path.test
configure = admin/config/search/path
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -5,8 +5,8 @@ version = VERSION
core = 7.x
files[] = php.test
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -6,8 +6,8 @@ core = 7.x
files[] = poll.test
stylesheets[all][] = poll.css
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

View File

@ -11,8 +11,8 @@ configure = admin/config/people/profile
; See user_system_info_alter().
hidden = TRUE
; Information added by Drupal.org packaging script on 2016-02-24
version = "7.43"
; Information added by Drupal.org packaging script on 2016-10-05
version = "7.51"
project = "drupal"
datestamp = "1456343506"
datestamp = "1475694174"

Some files were not shown because too many files have changed in this diff Show More