Skip to content
Commits on Source (12)
Drupal 7.51, xxxx-xx-xx (development version)
Drupal 7.52, 2016-11-16
-----------------------
- The update module now also checks for updates to a disabled themes that are
- Fixed security issues (multiple vulnerabilities). See SA-CORE-2016-005.
Drupal 7.51, 2016-10-05
-----------------------
- The Update module now also checks for updates to a disabled theme that is
used as an admin theme.
- Exceptions thrown in dblog_watchdog() are now caught and ignored.
- Clarified the warning that appears when modules are missing or have moved.
- If the page title is "0", it is now displayed.
- Log messages are now XSS filtered on display.
- Draggable tables do now work on touch screen devices.
- Added setting for allowing double underscores in CSS identifiers.
- 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 small bugfixes.
- Numerous bug fixes.
- Numerous API documentation improvements.
- Additional automated test coverage.
Drupal 7.50, 2016-07-07
-----------------------
......
......@@ -8,7 +8,7 @@
/**
* The current system version.
*/
define('VERSION', '7.51-dev');
define('VERSION', '7.52');
/**
* Core API compatibility.
......@@ -1072,8 +1072,8 @@ function _drupal_get_filename_perform_file_scan($type, $name) {
*/
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/2762241
if (empty($name) || ($type == 'module' && $name == 'default')) {
// @todo Remove this in https://www.drupal.org/node/2383823
if (empty($name)) {
return;
}
......
......@@ -1231,6 +1231,21 @@ public function preExecute(SelectQueryInterface $query = NULL) {
// Modules may alter all queries or only those having a particular tag.
if (isset($this->alterTags)) {
// Many contrib modules assume that query tags used for access-checking
// purposes follow the pattern $entity_type . '_access'. But this is
// not the case for taxonomy terms, since core used to add term_access
// instead of taxonomy_term_access to its queries. Provide backwards
// compatibility by adding both tags here instead of attempting to fix
// all contrib modules in a coordinated effort.
// TODO:
// - Extract this mechanism into a hook as part of a public (non-security)
// issue.
// - Emit E_USER_DEPRECATED if term_access is used.
// https://www.drupal.org/node/2575081
$term_access_tags = array('term_access' => 1, 'taxonomy_term_access' => 1);
if (array_intersect_key($this->alterTags, $term_access_tags)) {
$this->alterTags += $term_access_tags;
}
$hooks = array('query');
foreach ($this->alterTags as $tag => $value) {
$hooks[] = 'query_' . $tag;
......
......@@ -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);
......
......@@ -667,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');
......@@ -753,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);
......
......@@ -2597,10 +2597,9 @@ function template_preprocess_html(&$variables) {
}
// Construct page title.
$title = drupal_get_title();
if (strlen(trim($title))) {
if (drupal_get_title()) {
$head_title = array(
'title' => strip_tags($title),
'title' => strip_tags(drupal_get_title()),
'name' => check_plain(variable_get('site_name', 'Drupal')),
);
}
......@@ -2699,8 +2698,7 @@ function template_process_page(&$variables) {
$variables['breadcrumb'] = theme('breadcrumb', array('breadcrumb' => drupal_get_breadcrumb()));
}
if (!isset($variables['title'])) {
$title = drupal_get_title();
$variables['title'] = strlen(trim($title)) ? $title : NULL;
$variables['title'] = drupal_get_title();
}
// Generate messages last in order to capture as many as possible for the
......
......@@ -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();
......
......@@ -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);
......
......@@ -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.
*/
......
......@@ -1943,32 +1943,6 @@ class NodeTitleTestCase extends DrupalWebTestCase {
// Test node title is clickable on teaser list (/node).
$this->drupalGet('node');
$this->clickLink($node->title);
// Test edge cases.
// When node title is set to 0, the title should be '0'.
$node = $this->drupalCreateNode(array('title' => '0'));
$this->drupalGet('node/' . $node->nid);
$this->assertTitle('0' . ' | Drupal', 'Page title is equal to 0.', 'Node');
// Test that 0 appears in the template <h1>.
$xpath = '//h1[@id="page-title"]';
$this->assertEqual(trim(current($this->xpath($xpath))),
'0',
'Node title is displayed as 0.',
'Node');
// When node title is empty string, the h1 doesn't show up.
$node = $this->drupalCreateNode(array('title' => ''));
$this->drupalGet('node/' . $node->nid);
$this->assertTitle('Drupal', '', 'Node');
$xpath = '//h1[@id="page-title"]';
$this->assertIdentical(count($this->xpath($xpath)), 0);
// When node title is string with only spaces, the h1 doesn't show up.
$node = $this->drupalCreateNode(array('title' => ' '));
$this->drupalGet('node/' . $node->nid);
$this->assertTitle('Drupal', '', 'Node');
$xpath = '//h1[@id="page-title"]';
$this->assertIdentical(count($this->xpath($xpath)), 0);
}
}
......
......@@ -109,3 +109,33 @@ function taxonomy_test_get_antonym($tid) {
->execute()
->fetchField();
}
/**
* Implements hook_query_alter().
*/
function taxonomy_test_query_alter(QueryAlterableInterface $query) {
$value = variable_get(__FUNCTION__);
if (isset($value)) {
variable_set(__FUNCTION__, ++$value);
}
}
/**
* Implements hook_query_TAG_alter().
*/
function taxonomy_test_query_term_access_alter(QueryAlterableInterface $query) {
$value = variable_get(__FUNCTION__);
if (isset($value)) {
variable_set(__FUNCTION__, ++$value);
}
}
/**
* Implements hook_query_TAG_alter().
*/
function taxonomy_test_query_taxonomy_term_access_alter(QueryAlterableInterface $query) {
$value = variable_get(__FUNCTION__);
if (isset($value)) {
variable_set(__FUNCTION__, ++$value);
}
}
......@@ -86,7 +86,7 @@
<?php if ($site_name || $site_slogan): ?>
<div id="name-and-slogan">
<?php if ($site_name): ?>
<?php if (isset($title)): ?>
<?php if ($title): ?>
<div id="site-name"><strong>
<a href="<?php print $front_page; ?>" title="<?php print t('Home'); ?>" rel="home"><span><?php print $site_name; ?></span></a>
</strong></div>
......@@ -126,7 +126,7 @@
<?php if ($page['highlighted']): ?><div id="highlighted"><?php print render($page['highlighted']); ?></div><?php endif; ?>
<a id="main-content"></a>
<?php print render($title_prefix); ?>
<?php if (isset($title)): ?><h1 class="title" id="page-title"><?php print $title; ?></h1><?php endif; ?>
<?php if ($title): ?><h1 class="title" id="page-title"><?php print $title; ?></h1><?php endif; ?>
<?php print render($title_suffix); ?>
<?php if ($tabs): ?><div class="tabs"><?php print render($tabs); ?></div><?php endif; ?>
<?php print render($page['help']); ?>
......
......@@ -3270,6 +3270,21 @@ function system_update_7080() {
db_change_field('date_format_locale', 'format', 'format', $spec);
}
/**
* Remove the Drupal 6 default install profile if it is still in the database.
*/
function system_update_7081() {
// Sites which used the default install profile in Drupal 6 and then updated
// to Drupal 7.44 or earlier will still have a record of this install profile
// in the database that needs to be deleted.
db_delete('system')
->condition('filename', 'profiles/default/default.profile')
->condition('type', 'module')
->condition('status', 0)
->condition('schema_version', 0)
->execute();
}
/**
* @} End of "defgroup updates-7.x-extra".
* The next series of updates should start at 8000.
......
......@@ -2760,8 +2760,7 @@ function system_system_info_alter(&$info, $file, $type) {
*/
function system_default_region($theme) {
$regions = system_region_list($theme, REGIONS_VISIBLE, FALSE);
$region_0 = current($regions);
return isset($region_0) ? $region_0 : '';
return $regions ? reset($regions) : '';
}
/**
......@@ -2884,7 +2883,7 @@ function confirm_form($form, $question, $path, $description = NULL, $yes = NULL,
// Prepare cancel link.
if (isset($_GET['destination'])) {
$options = drupal_parse_url(urldecode($_GET['destination']));
$options = drupal_parse_url($_GET['destination']);
}
elseif (is_array($path)) {
$options = $path;
......
......@@ -1023,7 +1023,7 @@ function taxonomy_get_parents($tid) {
$query->join('taxonomy_term_hierarchy', 'h', 'h.parent = t.tid');
$query->addField('t', 'tid');
$query->condition('h.tid', $tid);
$query->addTag('term_access');
$query->addTag('taxonomy_term_access');
$query->orderBy('t.weight');
$query->orderBy('t.name');
$tids = $query->execute()->fetchCol();
......@@ -1081,7 +1081,7 @@ function taxonomy_get_children($tid, $vid = 0) {
if ($vid) {
$query->condition('t.vid', $vid);
}
$query->addTag('term_access');
$query->addTag('taxonomy_term_access');
$query->orderBy('t.weight');
$query->orderBy('t.name');
$tids = $query->execute()->fetchCol();
......@@ -1129,7 +1129,7 @@ function taxonomy_get_tree($vid, $parent = 0, $max_depth = NULL, $load_entities
$query->join('taxonomy_term_hierarchy', 'h', 'h.tid = t.tid');
$result = $query
->addTag('translatable')
->addTag('term_access')
->addTag('taxonomy_term_access')
->fields('t')
->fields('h', array('parent'))
->condition('t.vid', $vid)
......@@ -1249,7 +1249,7 @@ class TaxonomyTermController extends DrupalDefaultEntityController {
protected function buildQuery($ids, $conditions = array(), $revision_id = FALSE) {
$query = parent::buildQuery($ids, $conditions, $revision_id);
$query->addTag('translatable');
$query->addTag('term_access');
$query->addTag('taxonomy_term_access');
// When name is passed as a condition use LIKE.
if (isset($conditions['name'])) {
$query_conditions = &$query->conditions();
......
......@@ -150,7 +150,7 @@ function taxonomy_autocomplete($field_name = '', $tags_typed = '') {
$query = db_select('taxonomy_term_data', 't');
$query->addTag('translatable');
$query->addTag('term_access');
$query->addTag('taxonomy_term_access');
// Do not select already entered terms.
if (!empty($tags_typed)) {
......
......@@ -1983,3 +1983,113 @@ class TaxonomyEFQTestCase extends TaxonomyWebTestCase {
}
}
/**
* Tests that appropriate query tags are added.
*/
class TaxonomyQueryAlterTestCase extends TaxonomyWebTestCase {
public static function getInfo() {
return array(
'name' => 'Taxonomy query tags',
'description' => 'Verifies that taxonomy_term_access tags are added to queries.',
'group' => 'Taxonomy',
);
}
public function setUp() {
parent::setUp('taxonomy_test');
}
/**
* Tests that appropriate tags are added when querying the database.
*/
public function testTaxonomyQueryAlter() {
// Create a new vocabulary and add a few terms to it.
$vocabulary = $this->createVocabulary();
$terms = array();
for ($i = 0; $i < 5; $i++) {
$terms[$i] = $this->createTerm($vocabulary);
}
// Set up hierarchy. Term 2 is a child of 1.
$terms[2]->parent = array($terms[1]->tid);
taxonomy_term_save($terms[2]);
$this->setupQueryTagTestHooks();
$loaded_term = taxonomy_term_load($terms[0]->tid);
$this->assertEqual($loaded_term->tid, $terms[0]->tid, 'First term was loaded');
$this->assertQueryTagTestResult(1, 'taxonomy_term_load()');
$this->setupQueryTagTestHooks();
$loaded_terms = taxonomy_get_tree($vocabulary->vid);
$this->assertEqual(count($loaded_terms), count($terms), 'All terms were loaded');
$this->assertQueryTagTestResult(1, 'taxonomy_get_tree()');
$this->setupQueryTagTestHooks();
$loaded_terms = taxonomy_get_parents($terms[2]->tid);
$this->assertEqual(count($loaded_terms), 1, 'All parent terms were loaded');
$this->assertQueryTagTestResult(2, 'taxonomy_get_parents()');
$this->setupQueryTagTestHooks();
$loaded_terms = taxonomy_get_children($terms[1]->tid);
$this->assertEqual(count($loaded_terms), 1, 'All child terms were loaded');
$this->assertQueryTagTestResult(2, 'taxonomy_get_children()');
$this->setupQueryTagTestHooks();
$query = db_select('taxonomy_term_data', 't');
$query->addField('t', 'tid');
$query->addTag('taxonomy_term_access');
$tids = $query->execute()->fetchCol();
$this->assertEqual(count($tids), count($terms), 'All term IDs were retrieved');
$this->assertQueryTagTestResult(1, 'custom db_select() with taxonomy_term_access tag (preferred)');
$this->setupQueryTagTestHooks();
$query = db_select('taxonomy_term_data', 't');
$query->addField('t', 'tid');
$query->addTag('term_access');
$tids = $query->execute()->fetchCol();
$this->assertEqual(count($tids), count($terms), 'All term IDs were retrieved');
$this->assertQueryTagTestResult(1, 'custom db_select() with term_access tag (deprecated)');
$this->setupQueryTagTestHooks();
$query = new EntityFieldQuery();
$query->entityCondition('entity_type', 'taxonomy_term');
$query->addTag('taxonomy_term_access');
$result = $query->execute();
$this->assertEqual(count($result['taxonomy_term']), count($terms), 'All term IDs were retrieved');
$this->assertQueryTagTestResult(1, 'custom EntityFieldQuery with taxonomy_term_access tag (preferred)');
$this->setupQueryTagTestHooks();
$query = new EntityFieldQuery();
$query->entityCondition('entity_type', 'taxonomy_term');
$query->addTag('term_access');
$result = $query->execute();
$this->assertEqual(count($result['taxonomy_term']), count($terms), 'All term IDs were retrieved');
$this->assertQueryTagTestResult(1, 'custom EntityFieldQuery with term_access tag (deprecated)');
}
/**
* Sets up the hooks in the test module.
*/
protected function setupQueryTagTestHooks() {
taxonomy_terms_static_reset();
variable_set('taxonomy_test_query_alter', 0);
variable_set('taxonomy_test_query_term_access_alter', 0);
variable_set('taxonomy_test_query_taxonomy_term_access_alter', 0);
}
/**
* Verifies invocation of the hooks in the test module.
*
* @param int $expected_invocations
* The number of times the hooks are expected to have been invoked.
* @param string $method
* A string describing the invoked function which generated the query.
*/
protected function assertQueryTagTestResult($expected_invocations, $method) {
$this->assertIdentical($expected_invocations, variable_get('taxonomy_test_query_alter'), 'hook_query_alter() invoked when executing ' . $method);
$this->assertIdentical($expected_invocations, variable_get('taxonomy_test_query_term_access_alter'), 'Deprecated hook_query_term_access_alter() invoked when executing ' . $method);
$this->assertIdentical($expected_invocations, variable_get('taxonomy_test_query_taxonomy_term_access_alter'), 'Preferred hook_query_taxonomy_term_access_alter() invoked when executing ' . $method);
}
}
......@@ -99,7 +99,7 @@
<div id="name-and-slogan"<?php if ($hide_site_name && $hide_site_slogan) { print ' class="element-invisible"'; } ?>>
<?php if ($site_name): ?>
<?php if (isset($title)): ?>
<?php if ($title): ?>
<div id="site-name"<?php if ($hide_site_name) { print ' class="element-invisible"'; } ?>>
<strong>
<a href="<?php print $front_page; ?>" title="<?php print t('Home'); ?>" rel="home"><span><?php print $site_name; ?></span></a>
......@@ -187,7 +187,7 @@
<?php if ($page['highlighted']): ?><div id="highlighted"><?php print render($page['highlighted']); ?></div><?php endif; ?>
<a id="main-content"></a>
<?php print render($title_prefix); ?>
<?php if (isset($title)): ?>
<?php if ($title): ?>
<h1 class="title" id="page-title">
<?php print $title; ?>
</h1>
......
......@@ -8,7 +8,7 @@
<div id="header">
<div id="logo-floater">
<?php if ($logo || $site_title): ?>
<?php if (isset($title)): ?>
<?php if ($title): ?>
<div id="branding"><strong><a href="<?php print $front_page ?>">
<?php if ($logo): ?>
<img src="<?php print $logo ?>" alt="<?php print $site_name_and_slogan ?>" title="<?php print $site_name_and_slogan ?>" id="logo" />
......@@ -42,7 +42,7 @@
<a id="main-content"></a>
<?php if ($tabs): ?><div id="tabs-wrapper" class="clearfix"><?php endif; ?>
<?php print render($title_prefix); ?>
<?php if (isset($title)): ?>
<?php if ($title): ?>
<h1<?php print $tabs ? ' class="with-tabs"' : '' ?>><?php print $title ?></h1>
<?php endif; ?>
<?php print render($title_suffix); ?>
......
......@@ -2,7 +2,7 @@
<div id="branding" class="clearfix">
<?php print $breadcrumb; ?>
<?php print render($title_prefix); ?>
<?php if (isset($title)): ?>
<?php if ($title): ?>
<h1 class="page-title"><?php print $title; ?></h1>
<?php endif; ?>
<?php print render($title_suffix); ?>
......