-
-
- ',
+ 'allowed_html' => '
-
-
-
',
'filter_html_help' => 1,
'filter_html_nofollow' => 0,
);
@@ -1175,6 +1209,10 @@
$f = _filter_html('
', $filter);
$this->assertNoNormalized($f, 'onerror', 'HTML filter should remove empty on* attributes on default.');
+
+ // Custom tags are supported and should be allowed through.
+ $f = _filter_html(' ', $filter);
+ $this->assertNormalized($f, 'test-element', 'HTML filter should allow custom elements.');
}
/**
@@ -1260,6 +1298,7 @@
// Create a e-mail that is too long.
$long_email = str_repeat('a', 254) . '@example.com';
$too_long_email = str_repeat('b', 255) . '@example.com';
+ $email_with_plus_sign = 'one+two@example.com';
// Filter selection/pattern matching.
@@ -1273,12 +1312,13 @@
),
// MAILTO URLs.
'
-person@example.com or mailto:person2@example.com or ' . $long_email . ' but not ' . $too_long_email . '
+person@example.com or mailto:person2@example.com or ' . $email_with_plus_sign . ' or ' . $long_email . ' but not ' . $too_long_email . '
' => array(
'person@example.com' => TRUE,
'mailto:person2@example.com' => TRUE,
'' . $long_email . '' => TRUE,
'' . $too_long_email . '' => FALSE,
+ '' . $email_with_plus_sign . '' => TRUE,
),
// URI parts and special characters.
'
@@ -1970,3 +2010,26 @@
}
}
}
+
+/**
+ * Tests DOMDocument serialization.
+ */
+class FilterDOMSerializeTestCase extends DrupalWebTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Serialization',
+ 'description' => 'Test serialization of DOMDocument objects.',
+ 'group' => 'Filter',
+ );
+ }
+
+ /**
+ * Tests empty DOMDocument object.
+ */
+ function testFilterEmptyDOMSerialization() {
+ $document = new DOMDocument();
+ $result = filter_dom_serialize($document);
+ $this->assertEqual('', $result);
+ }
+}
diff -Naur drupal-7.25/modules/forum/forum.info drupal-7.66/modules/forum/forum.info
--- drupal-7.25/modules/forum/forum.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/forum/forum.info 2019-04-17 22:39:36.000000000 +0200
@@ -9,8 +9,7 @@
configure = admin/structure/forum
stylesheets[all][] = forum.css
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/forum/forum.module drupal-7.66/modules/forum/forum.module
--- drupal-7.25/modules/forum/forum.module 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/forum/forum.module 2019-04-17 22:20:46.000000000 +0200
@@ -263,10 +263,10 @@
* Implements hook_node_view().
*/
function forum_node_view($node, $view_mode) {
- $vid = variable_get('forum_nav_vocabulary', 0);
- $vocabulary = taxonomy_vocabulary_load($vid);
if (_forum_node_check_node_type($node)) {
if ($view_mode == 'full' && node_is_page($node)) {
+ $vid = variable_get('forum_nav_vocabulary', 0);
+ $vocabulary = taxonomy_vocabulary_load($vid);
// Breadcrumb navigation
$breadcrumb[] = l(t('Home'), NULL);
$breadcrumb[] = l($vocabulary->name, 'forum');
diff -Naur drupal-7.25/modules/help/help.api.php drupal-7.66/modules/help/help.api.php
--- drupal-7.25/modules/help/help.api.php 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/help/help.api.php 1970-01-01 01:00:00.000000000 +0100
@@ -1,63 +0,0 @@
-' . t('Blocks are boxes of content rendered into an area, or region, of a web page. The default theme Bartik, for example, implements the regions "Sidebar first", "Sidebar second", "Featured", "Content", "Header", "Footer", etc., and a block may appear in any one of these areas. The blocks administration page provides a drag-and-drop interface for assigning a block to a region, and for controlling the order of blocks within regions.', array('@blocks' => url('admin/structure/block'))) . '';
-
- // Help for another path in the block module
- case 'admin/structure/block':
- return '' . t('This page provides a drag-and-drop interface for assigning a block to a region, and for controlling the order of blocks within regions. Since not all themes implement the same regions, or display regions in the same way, blocks are positioned on a per-theme basis. Remember that your changes will not be saved until you click the Save blocks button at the bottom of the page.') . '
';
- }
-}
-
-/**
- * @} End of "addtogroup hooks".
- */
diff -Naur drupal-7.25/modules/help/help.info drupal-7.66/modules/help/help.info
--- drupal-7.25/modules/help/help.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/help/help.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
core = 7.x
files[] = help.test
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/image/image.admin.inc drupal-7.66/modules/image/image.admin.inc
--- drupal-7.25/modules/image/image.admin.inc 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/image/image.admin.inc 2019-04-17 22:20:46.000000000 +0200
@@ -592,15 +592,15 @@
'#type' => 'radios',
'#title' => t('Anchor'),
'#options' => array(
- 'left-top' => t('Top') . ' ' . t('Left'),
- 'center-top' => t('Top') . ' ' . t('Center'),
- 'right-top' => t('Top') . ' ' . t('Right'),
- 'left-center' => t('Center') . ' ' . t('Left'),
+ 'left-top' => t('Top left'),
+ 'center-top' => t('Top center'),
+ 'right-top' => t('Top right'),
+ 'left-center' => t('Center left'),
'center-center' => t('Center'),
- 'right-center' => t('Center') . ' ' . t('Right'),
- 'left-bottom' => t('Bottom') . ' ' . t('Left'),
- 'center-bottom' => t('Bottom') . ' ' . t('Center'),
- 'right-bottom' => t('Bottom') . ' ' . t('Right'),
+ 'right-center' => t('Center right'),
+ 'left-bottom' => t('Bottom left'),
+ 'center-bottom' => t('Bottom center'),
+ 'right-bottom' => t('Bottom right'),
),
'#theme' => 'image_anchor',
'#default_value' => $data['anchor'],
@@ -736,7 +736,8 @@
if (!isset($form[$key]['#access']) || $form[$key]['#access']) {
$rows[] = array(
'data' => $row,
- 'class' => !empty($form[$key]['weight']['#access']) || $key == 'new' ? array('draggable') : array(),
+ // Use a strict (===) comparison since $key can be 0.
+ 'class' => !empty($form[$key]['weight']['#access']) || $key === 'new' ? array('draggable') : array(),
);
}
}
diff -Naur drupal-7.25/modules/image/image.info drupal-7.66/modules/image/image.info
--- drupal-7.25/modules/image/image.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/image/image.info 2019-04-17 22:39:36.000000000 +0200
@@ -7,8 +7,7 @@
files[] = image.test
configure = admin/config/media/image-styles
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/image/image.module drupal-7.66/modules/image/image.module
--- drupal-7.25/modules/image/image.module 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/image/image.module 2019-04-17 22:20:46.000000000 +0200
@@ -64,7 +64,7 @@
$effect = image_effect_definition_load($arg[7]);
return isset($effect['help']) ? ('' . $effect['help'] . '
') : NULL;
case 'admin/config/media/image-styles/edit/%/effects/%':
- $effect = ($arg[5] == 'add') ? image_effect_definition_load($arg[6]) : image_effect_load($arg[6], $arg[4]);
+ $effect = ($arg[5] == 'add') ? image_effect_definition_load($arg[6]) : image_effect_load($arg[7], $arg[5]);
return isset($effect['help']) ? ('' . $effect['help'] . '
') : NULL;
}
}
@@ -801,6 +801,8 @@
*
* @param $style
* The image style
+ * @param $scheme
+ * The file scheme, for example 'public' for public files.
*/
function image_style_deliver($style, $scheme) {
$args = func_get_args();
@@ -833,8 +835,8 @@
file_download($scheme, file_uri_target($derivative_uri));
}
else {
- $headers = module_invoke_all('file_download', $image_uri);
- if (in_array(-1, $headers) || empty($headers)) {
+ $headers = file_download_headers($image_uri);
+ if (empty($headers)) {
return MENU_ACCESS_DENIED;
}
if (count($headers)) {
@@ -845,6 +847,12 @@
}
}
+ // Confirm that the original source image exists before trying to process it.
+ if (!is_file($image_uri)) {
+ watchdog('image', 'Source image at %source_image_path not found while trying to generate derivative image at %derivative_path.', array('%source_image_path' => $image_uri, '%derivative_path' => $derivative_uri));
+ return MENU_NOT_FOUND;
+ }
+
// Don't start generating the image if the derivative already exists or if
// generation is in progress in another thread.
$lock_name = 'image_style_deliver:' . $style['name'] . ':' . drupal_hash_base64($image_uri);
@@ -854,6 +862,7 @@
// Tell client to retry again in 3 seconds. Currently no browsers are known
// to support Retry-After.
drupal_add_http_header('Status', '503 Service Unavailable');
+ drupal_add_http_header('Content-Type', 'text/html; charset=utf-8');
drupal_add_http_header('Retry-After', 3);
print t('Image generation in progress. Try again shortly.');
drupal_exit();
@@ -875,6 +884,7 @@
else {
watchdog('image', 'Unable to generate the derived image located at %path.', array('%path' => $derivative_uri));
drupal_add_http_header('Status', '500 Internal Server Error');
+ drupal_add_http_header('Content-Type', 'text/html; charset=utf-8');
print t('Error generating image.');
drupal_exit();
}
@@ -1019,7 +1029,15 @@
// The token query is added even if the 'image_allow_insecure_derivatives'
// variable is TRUE, so that the emitted links remain valid if it is changed
// back to the default FALSE.
- $token_query = array(IMAGE_DERIVATIVE_TOKEN => image_style_path_token($style_name, $original_uri));
+ // However, sites which need to prevent the token query from being emitted at
+ // all can additionally set the 'image_suppress_itok_output' variable to TRUE
+ // to achieve that (if both are set, the security token will neither be
+ // emitted in the image derivative URL nor checked for in
+ // image_style_deliver()).
+ $token_query = array();
+ if (!variable_get('image_suppress_itok_output', FALSE)) {
+ $token_query = array(IMAGE_DERIVATIVE_TOKEN => image_style_path_token($style_name, $original_uri));
+ }
// If not using clean URLs, the image derivative callback is only available
// with the query string. If the file does not exist, use url() to ensure
@@ -1031,8 +1049,12 @@
}
$file_url = file_create_url($uri);
- // Append the query string with the token.
- return $file_url . (strpos($file_url, '?') !== FALSE ? '&' : '?') . drupal_http_build_query($token_query);
+ // Append the query string with the token, if necessary.
+ if ($token_query) {
+ $file_url .= (strpos($file_url, '?') !== FALSE ? '&' : '?') . drupal_http_build_query($token_query);
+ }
+
+ return $file_url;
}
/**
diff -Naur drupal-7.25/modules/image/image.test drupal-7.66/modules/image/image.test
--- drupal-7.25/modules/image/image.test 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/image/image.test 2019-04-17 22:20:46.000000000 +0200
@@ -32,7 +32,7 @@
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);
}
@@ -78,6 +78,24 @@
}
/**
+ * Create a random style.
+ *
+ * @return array
+ * A list containing the details of the generated image style.
+ */
+ function createRandomStyle() {
+ $style_name = strtolower($this->randomName(10));
+ $style_label = $this->randomString();
+ image_style_save(array('name' => $style_name, 'label' => $style_label));
+ $style_path = 'admin/config/media/image-styles/edit/' . $style_name;
+ return array(
+ 'name' => $style_name,
+ 'label' => $style_label,
+ 'path' => $style_path,
+ );
+ }
+
+ /**
* Upload an image to a node.
*
* @param $image
@@ -174,6 +192,32 @@
}
/**
+ * Test that an invalid source image returns a 404.
+ */
+ function testImageStyleUrlForMissingSourceImage() {
+ $non_existent_uri = 'public://foo.png';
+ $generated_url = image_style_url($this->style_name, $non_existent_uri);
+ $this->drupalGet($generated_url);
+ $this->assertResponse(404, 'Accessing an image style URL with a source image that does not exist provides a 404 error response.');
+ }
+
+ /**
+ * Test that we do not pass an array to drupal_add_http_header.
+ */
+ function testImageContentTypeHeaders() {
+ $files = $this->drupalGetTestFiles('image');
+ $file = array_shift($files);
+ // Copy the test file to private folder.
+ $private_file = file_copy($file, 'private://', FILE_EXISTS_RENAME);
+ // Tell image_module_test module to return the headers we want to test.
+ variable_set('image_module_test_invalid_headers', $private_file->uri);
+ // Invoke image_style_deliver so it will try to set headers.
+ $generated_url = image_style_url($this->style_name, $private_file->uri);
+ $this->drupalGet($generated_url);
+ variable_del('image_module_test_invalid_headers');
+ }
+
+ /**
* Test image_style_url().
*/
function _testImageStyleUrlAndPath($scheme, $clean_url = TRUE, $extra_slash = FALSE) {
@@ -241,7 +285,7 @@
$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
@@ -320,6 +364,15 @@
$this->drupalGet($nested_url);
$this->assertResponse(200, 'Image was accessible when a correct token was provided in the URL.');
+ // Suppress the security token in the URL, then get the URL of a file. Check
+ // that the security token is not present in the URL but that the image is
+ // still accessible.
+ variable_set('image_suppress_itok_output', TRUE);
+ $generate_url = image_style_url($this->style_name, $original_uri);
+ $this->assertIdentical(strpos($generate_url, IMAGE_DERIVATIVE_TOKEN . '='), FALSE, 'The security token does not appear in the image style URL.');
+ $this->drupalGet($generate_url);
+ $this->assertResponse(200, 'Image was accessible at the URL with a missing token.');
+
// Check that requesting a nonexistent image does not create any new
// directories in the file system.
$directory = $scheme . '://styles/' . $this->style_name . '/' . $scheme . '/' . $this->randomName();
@@ -451,6 +504,58 @@
}
/**
+ * Tests the administrative user interface.
+ */
+class ImageAdminUiTestCase extends ImageFieldTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Administrative user interface',
+ 'description' => 'Tests the forms used in the administrative user interface.',
+ 'group' => 'Image',
+ );
+ }
+
+ function setUp() {
+ parent::setUp(array('image'));
+ }
+
+ /**
+ * Test if the help text is available on the add effect form.
+ */
+ function testAddEffectHelpText() {
+ // Create a random image style.
+ $style = $this->createRandomStyle();
+
+ // Open the add effect form and check for the help text.
+ $this->drupalGet($style['path'] . '/add/image_crop');
+ $this->assertText(t('Cropping will remove portions of an image to make it the specified dimensions.'), 'The image style effect help text was displayed on the add effect page.');
+ }
+
+ /**
+ * Test if the help text is available on the edit effect form.
+ */
+ function testEditEffectHelpText() {
+ // Create a random image style.
+ $random_style = $this->createRandomStyle();
+
+ // Add the crop effect to the image style.
+ $edit = array();
+ $edit['data[width]'] = 20;
+ $edit['data[height]'] = 20;
+ $this->drupalPost($random_style['path'] . '/add/image_crop', $edit, t('Add effect'));
+
+ // Open the edit effect form and check for the help text.
+ drupal_static_reset('image_styles');
+ $style = image_style_load($random_style['name']);
+
+ foreach ($style['effects'] as $ieid => $effect) {
+ $this->drupalGet($random_style['path'] . '/effects/' . $ieid);
+ $this->assertText(t('Cropping will remove portions of an image to make it the specified dimensions.'), 'The image style effect help text was displayed on the edit effect page.');
+ }
+ }
+}
+
+/**
* Tests creation, deletion, and editing of image styles and effects.
*/
class ImageAdminStylesUnitTest extends ImageFieldTestCase {
diff -Naur drupal-7.25/modules/image/tests/image_module_test.info drupal-7.66/modules/image/tests/image_module_test.info
--- drupal-7.25/modules/image/tests/image_module_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/image/tests/image_module_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -6,8 +6,7 @@
files[] = image_module_test.module
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/image/tests/image_module_test.module drupal-7.66/modules/image/tests/image_module_test.module
--- drupal-7.25/modules/image/tests/image_module_test.module 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/image/tests/image_module_test.module 2019-04-17 22:20:46.000000000 +0200
@@ -9,6 +9,9 @@
if (variable_get('image_module_test_file_download', FALSE) == $uri) {
return array('X-Image-Owned-By' => 'image_module_test');
}
+ if (variable_get('image_module_test_invalid_headers', FALSE) == $uri) {
+ return array('Content-Type' => 'image/png');
+ }
}
/**
diff -Naur drupal-7.25/modules/locale/locale.admin.inc drupal-7.66/modules/locale/locale.admin.inc
--- drupal-7.25/modules/locale/locale.admin.inc 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/locale/locale.admin.inc 2019-04-17 22:20:46.000000000 +0200
@@ -1139,11 +1139,11 @@
'#value' => $source->location
);
- // Include default form controls with empty values for all languages.
- // This ensures that the languages are always in the same order in forms.
+ // Include both translated and not yet translated target languages in the
+ // list. The source language is English for built-in strings and the default
+ // language for other strings.
$languages = language_list();
$default = language_default();
- // We don't need the default language value, that value is in $source.
$omit = $source->textgroup == 'default' ? 'en' : $default->language;
unset($languages[($omit)]);
$form['translations'] = array('#tree' => TRUE);
@@ -1194,7 +1194,7 @@
$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,
diff -Naur drupal-7.25/modules/locale/locale.info drupal-7.66/modules/locale/locale.info
--- drupal-7.25/modules/locale/locale.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/locale/locale.info 2019-04-17 22:39:36.000000000 +0200
@@ -6,8 +6,7 @@
files[] = locale.test
configure = admin/config/regional/language
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/locale/locale.test drupal-7.66/modules/locale/locale.test
--- drupal-7.25/modules/locale/locale.test 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/locale/locale.test 2019-04-17 22:20:46.000000000 +0200
@@ -393,6 +393,16 @@
// 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;
@@ -809,7 +819,7 @@
* Additional options to pass to the translation import form.
*/
function importPoFile($contents, array $options = array()) {
- $name = tempnam('temporary://', "po_") . '.po';
+ $name = drupal_tempnam('temporary://', "po_") . '.po';
file_put_contents($name, $contents);
$options['files[file]'] = $name;
$this->drupalPost('admin/config/regional/translate/import', $options, t('Import'));
@@ -1103,7 +1113,7 @@
* Additional options to pass to the translation import form.
*/
function importPoFile($contents, array $options = array()) {
- $name = tempnam('temporary://', "po_") . '.po';
+ $name = drupal_tempnam('temporary://', "po_") . '.po';
file_put_contents($name, $contents);
$options['files[file]'] = $name;
$this->drupalPost('admin/config/regional/translate/import', $options, t('Import'));
@@ -1202,7 +1212,7 @@
* Helper function that returns a .po file with context.
*/
function getPoFileWithContext() {
- // Croatian (code hr) is one the the languages that have a different
+ // Croatian (code hr) is one of the languages that have a different
// form for the full name and the abbreviated name for the month May.
return <<< EOF
msgid ""
@@ -1330,7 +1340,7 @@
function testExportTranslation() {
// First import some known translations.
// This will also automatically enable the 'fr' language.
- $name = tempnam('temporary://', "po_") . '.po';
+ $name = drupal_tempnam('temporary://', "po_") . '.po';
file_put_contents($name, $this->getPoFile());
$this->drupalPost('admin/config/regional/translate/import', array(
'langcode' => 'fr',
@@ -2237,6 +2247,37 @@
$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 @@
$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;
+ }
}
/**
@@ -3085,11 +3188,7 @@
foreach (language_types_info() as $type => $info) {
if (isset($info['fixed'])) {
$negotiation = variable_get("language_negotiation_$type", array());
- $equal = count($info['fixed']) == count($negotiation);
- while ($equal && list($id) = each($negotiation)) {
- list(, $info_id) = each($info['fixed']);
- $equal = $info_id == $id;
- }
+ $equal = array_keys($negotiation) === array_values($info['fixed']);
$this->assertTrue($equal, format_string('language negotiation for %type is properly set up', array('%type' => $type)));
}
}
@@ -3141,3 +3240,46 @@
$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 world!';
+ $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 !';
+ $result = locale_string_is_safe($string);
+ $this->assertFalse($result);
+
+ // Check a translatable string which includes a token in an href attribute.
+ $string = 'Hi user';
+ $result = locale_string_is_safe($string);
+ $this->assertTrue($result);
+ }
+}
diff -Naur drupal-7.25/modules/locale/tests/locale_test.info drupal-7.66/modules/locale/tests/locale_test.info
--- drupal-7.25/modules/locale/tests/locale_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/locale/tests/locale_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
version = VERSION
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/menu/menu.admin.inc drupal-7.66/modules/menu/menu.admin.inc
--- drupal-7.25/modules/menu/menu.admin.inc 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/menu/menu.admin.inc 2019-04-17 22:20:46.000000000 +0200
@@ -281,6 +281,7 @@
$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 @@
'#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' => '', '%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' => '', '%add-node' => 'node/add', '%example' => 'http://example.com')),
'#required' => TRUE,
);
$form['actions']['delete'] = array(
diff -Naur drupal-7.25/modules/menu/menu.info drupal-7.66/modules/menu/menu.info
--- drupal-7.25/modules/menu/menu.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/menu/menu.info 2019-04-17 22:39:36.000000000 +0200
@@ -6,8 +6,7 @@
files[] = menu.test
configure = admin/structure/menu
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/menu/menu.module drupal-7.66/modules/menu/menu.module
--- drupal-7.25/modules/menu/menu.module 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/menu/menu.module 2019-04-17 22:20:46.000000000 +0200
@@ -69,7 +69,7 @@
'title' => 'Parent menu items',
'page callback' => 'menu_parent_options_js',
'type' => MENU_CALLBACK,
- 'access arguments' => array(TRUE),
+ 'access arguments' => array('administer menu'),
);
$items['admin/structure/menu/list'] = array(
'title' => 'List menus',
@@ -674,6 +674,7 @@
$form['menu']['link']['link_title'] = array(
'#type' => 'textfield',
'#title' => t('Menu link title'),
+ '#maxlength' => 255,
'#default_value' => $link['link_title'],
);
diff -Naur drupal-7.25/modules/menu/menu.test drupal-7.66/modules/menu/menu.test
--- drupal-7.25/modules/menu/menu.test 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/menu/menu.test 2019-04-17 22:20:46.000000000 +0200
@@ -72,6 +72,17 @@
$saved_item = menu_link_load($item['mlid']);
$this->assertEqual($description, $saved_item['options']['attributes']['title'], 'Saving an existing link updates the description (title attribute)');
$this->resetMenuLink($item, $old_title);
+
+ // Test that the page title is correct when a local task appears in a
+ // top-level menu item. See https://www.drupal.org/node/1973262.
+ $item = $this->addMenuLink(0, 'user/register', 'user-menu');
+ $this->drupalGet('user/password');
+ $this->assertNoTitle('Home | Drupal');
+ $this->drupalLogout();
+ $this->drupalGet('user/register');
+ $this->assertTitle($item['link_title'] . ' | Drupal');
+ $this->drupalGet('user');
+ $this->assertNoTitle('Home | Drupal');
}
/**
@@ -514,6 +525,23 @@
}
/**
+ * Test administrative users other than user 1 can access the menu parents AJAX callback.
+ */
+ public function testMenuParentsJsAccess() {
+ $admin = $this->drupalCreateUser(array('administer menu'));
+ $this->drupalLogin($admin);
+ // Just check access to the callback overall, the POST data is irrelevant.
+ $this->drupalGetAJAX('admin/structure/menu/parents');
+ $this->assertResponse(200);
+
+ // Do standard user tests.
+ // Login the user.
+ $this->drupalLogin($this->std_user);
+ $this->drupalGetAJAX('admin/structure/menu/parents');
+ $this->assertResponse(403);
+ }
+
+ /**
* Get standard menu link.
*/
private function getStandardMenuLink() {
@@ -620,7 +648,12 @@
);
$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('//', '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(
@@ -656,6 +689,10 @@
$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('//', 'Menu link title field has correct maxlength in node edit form.');
+
// Edit the node and remove the menu link.
$edit = array(
'menu[enabled]' => FALSE,
diff -Naur drupal-7.25/modules/node/content_types.inc drupal-7.66/modules/node/content_types.inc
--- drupal-7.25/modules/node/content_types.inc 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/node/content_types.inc 2019-04-17 22:20:46.000000000 +0200
@@ -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();
diff -Naur drupal-7.25/modules/node/node.admin.inc drupal-7.66/modules/node/node.admin.inc
--- drupal-7.25/modules/node/node.admin.inc 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/node/node.admin.inc 2019-04-17 22:20:46.000000000 +0200
@@ -329,6 +329,8 @@
}
/**
+ * Implements callback_batch_operation().
+ *
* Executes a batch operation for node_mass_update().
*
* @param array $nodes
@@ -367,7 +369,9 @@
}
/**
- * Menu callback: Reports the status of batch operation for node_mass_update().
+ * Implements callback_batch_finished().
+ *
+ * Reports the status of batch operation for node_mass_update().
*
* @param bool $success
* A boolean indicating whether the batch mass update operation successfully
@@ -471,6 +475,7 @@
$header['operations'] = array('data' => t('Operations'));
$query = db_select('node', 'n')->extend('PagerDefault')->extend('TableSort');
+ $query->addTag('node_admin_filter');
node_build_filter_query($query);
if (!user_access('bypass node access')) {
@@ -503,14 +508,17 @@
$options = array();
foreach ($nodes as $node) {
$langcode = entity_language('node', $node);
- $l_options = $langcode != LANGUAGE_NONE && isset($languages[$langcode]) ? array('language' => $languages[$langcode]) : array();
+ $uri = entity_uri('node', $node);
+ if ($langcode != LANGUAGE_NONE && isset($languages[$langcode])) {
+ $uri['options']['language'] = $languages[$langcode];
+ }
$options[$node->nid] = array(
'title' => array(
'data' => array(
'#type' => 'link',
'#title' => $node->title,
- '#href' => 'node/' . $node->nid,
- '#options' => $l_options,
+ '#href' => $uri['path'],
+ '#options' => $uri['options'],
'#suffix' => ' ' . theme('mark', array('type' => node_mark($node->nid, $node->changed))),
),
),
diff -Naur drupal-7.25/modules/node/node.api.php drupal-7.66/modules/node/node.api.php
--- drupal-7.25/modules/node/node.api.php 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/node/node.api.php 2019-04-17 22:20:46.000000000 +0200
@@ -17,11 +17,14 @@
* During node operations (create, update, view, delete, etc.), there are
* several sets of hooks that get invoked to allow modules to modify the base
* node operation:
- * - Node-type-specific hooks: These hooks are only invoked on the primary
- * module, using the "base" return component of hook_node_info() as the
- * function prefix. For example, poll.module defines the base for the Poll
- * content type as "poll", so during creation of a poll node, hook_insert() is
- * only invoked by calling poll_insert().
+ * - Node-type-specific hooks: When defining a node type, hook_node_info()
+ * returns a 'base' component. Node-type-specific hooks are named
+ * base_hookname() instead of mymodule_hookname() (in a module called
+ * 'mymodule' for example). Only the node type's corresponding implementation
+ * is invoked. For example, poll_node_info() in poll.module defines the base
+ * for the 'poll' node type as 'poll'. So when a poll node is created,
+ * hook_insert() is invoked on poll_insert() only.
+ * Hooks that are node-type-specific are noted below.
* - All-module hooks: This set of hooks is invoked on all implementing modules,
* to allow other modules to modify what the primary node module is doing. For
* example, hook_node_insert() is invoked on all modules when creating a poll
@@ -195,7 +198,7 @@
if (user_access('access private content', $account)) {
$grants['example'] = array(1);
}
- $grants['example_owner'] = array($account->uid);
+ $grants['example_author'] = array($account->uid);
return $grants;
}
@@ -885,11 +888,10 @@
* name as the key. Each sub-array has up to 10 attributes. Possible
* attributes:
* - name: (required) The human-readable name of the node type.
- * - base: (required) The base string used to construct callbacks
- * corresponding to this node type (for example, if base is defined as
- * example_foo, then example_foo_insert will be called when inserting a node
- * of that type). This string is usually the name of the module, but not
- * always.
+ * - base: (required) The base name for implementations of node-type-specific
+ * hooks that respond to this node type. Base is usually the name of the
+ * module or 'node_content', but not always. See
+ * @link node_api_hooks Node API hooks @endlink for more information.
* - description: (required) A brief description of the node type.
* - help: (optional) Help information shown to the user when creating a node
* of this type.
@@ -948,7 +950,7 @@
* 'recent', or 'comments'. The values should be arrays themselves, with the
* following keys available:
* - title: (required) The human readable name of the ranking mechanism.
- * - join: (optional) The part of a query string to join to any additional
+ * - join: (optional) An array with information to join any additional
* necessary table. This is not necessary if the table required is already
* joined to by the base query, such as for the {node} table. Other tables
* should use the full table name as an alias to avoid naming collisions.
@@ -972,7 +974,12 @@
'title' => t('Average vote'),
// Note that we use i.sid, the search index's search item id, rather than
// n.nid.
- 'join' => 'LEFT JOIN {vote_node_data} vote_node_data ON vote_node_data.nid = i.sid',
+ 'join' => array(
+ 'type' => 'LEFT',
+ 'table' => 'vote_node_data',
+ 'alias' => 'vote_node_data',
+ 'on' => 'vote_node_data.nid = i.sid',
+ ),
// The highest possible score should be 1, and the lowest possible score,
// always 0, should be 0.
'score' => 'vote_node_data.average / CAST(%f AS DECIMAL)',
@@ -1030,12 +1037,23 @@
/**
* Respond to node deletion.
*
- * This hook is invoked only on the module that defines the node's content type
- * (use hook_node_delete() to respond to all node deletions).
- *
- * This hook is invoked from node_delete_multiple() after the node has been
- * removed from the node table in the database, before hook_node_delete() is
- * invoked, and before field_attach_delete() is called.
+ * This is a node-type-specific hook, which is invoked only for the node type
+ * being affected. See
+ * @link node_api_hooks Node API hooks @endlink for more information.
+ *
+ * Use hook_node_delete() to respond to node deletion of all node types.
+ *
+ * This hook is invoked from node_delete_multiple() before hook_node_delete()
+ * is invoked and before field_attach_delete() is called.
+ *
+ * Note that when this hook is invoked, the changes have not yet been written
+ * to the database, because a database transaction is still in progress. The
+ * transaction is not finalized until the delete operation is entirely
+ * completed and node_delete_multiple() goes out of scope. You should not rely
+ * on data in the database at this time as it is not updated yet. You should
+ * also note that any write/update database queries executed from this hook are
+ * also not committed immediately. Check node_delete_multiple() and
+ * db_transaction() for more info.
*
* @param $node
* The node that is being deleted.
@@ -1051,8 +1069,11 @@
/**
* Act on a node object about to be shown on the add/edit form.
*
- * This hook is invoked only on the module that defines the node's content type
- * (use hook_node_prepare() to act on all node preparations).
+ * This is a node-type-specific hook, which is invoked only for the node type
+ * being affected. See
+ * @link node_api_hooks Node API hooks @endlink for more information.
+ *
+ * Use hook_node_prepare() to respond to node preparation of all node types.
*
* This hook is invoked from node_object_prepare() before the general
* hook_node_prepare() is invoked.
@@ -1063,26 +1084,21 @@
* @ingroup node_api_hooks
*/
function hook_prepare($node) {
- if ($file = file_check_upload($field_name)) {
- $file = file_save_upload($field_name, _image_filename($file->filename, NULL, TRUE));
- if ($file) {
- if (!image_get_info($file->uri)) {
- form_set_error($field_name, t('Uploaded file is not a valid image'));
- return;
- }
- }
- else {
- return;
- }
- $node->images['_original'] = $file->uri;
- _image_build_derivatives($node, TRUE);
- $node->new_file = TRUE;
+ if (!isset($node->mymodule_value)) {
+ $node->mymodule_value = 'foo';
}
}
/**
* Display a node editing form.
*
+ * This is a node-type-specific hook, which is invoked only for the node type
+ * being affected. See
+ * @link node_api_hooks Node API hooks @endlink for more information.
+ *
+ * Use hook_form_BASE_FORM_ID_alter(), with base form ID 'node_form', to alter
+ * node forms for all node types.
+ *
* This hook, implemented by node modules, is called to retrieve the form
* that is displayed to create or edit a node. This form is displayed at path
* node/add/[node type] or node/[node ID]/edit.
@@ -1138,8 +1154,11 @@
/**
* Respond to creation of a new node.
*
- * This hook is invoked only on the module that defines the node's content type
- * (use hook_node_insert() to act on all node insertions).
+ * This is a node-type-specific hook, which is invoked only for the node type
+ * being affected. See
+ * @link node_api_hooks Node API hooks @endlink for more information.
+ *
+ * Use hook_node_insert() to respond to node insertion of all node types.
*
* This hook is invoked from node_save() after the node is inserted into the
* node table in the database, before field_attach_insert() is called, and
@@ -1162,8 +1181,11 @@
/**
* Act on nodes being loaded from the database.
*
- * This hook is invoked only on the module that defines the node's content type
- * (use hook_node_load() to respond to all node loads).
+ * This is a node-type-specific hook, which is invoked only for the node type
+ * being affected. See
+ * @link node_api_hooks Node API hooks @endlink for more information.
+ *
+ * Use hook_node_load() to respond to node load of all node types.
*
* This hook is invoked during node loading, which is handled by entity_load(),
* via classes NodeController and DrupalDefaultEntityController. After the node
@@ -1196,8 +1218,11 @@
/**
* Respond to updates to a node.
*
- * This hook is invoked only on the module that defines the node's content type
- * (use hook_node_update() to act on all node updates).
+ * This is a node-type-specific hook, which is invoked only for the node type
+ * being affected. See
+ * @link node_api_hooks Node API hooks @endlink for more information.
+ *
+ * Use hook_node_update() to respond to node update of all node types.
*
* This hook is invoked from node_save() after the node is updated in the
* node table in the database, before field_attach_update() is called, and
@@ -1218,8 +1243,11 @@
/**
* Perform node validation before a node is created or updated.
*
- * This hook is invoked only on the module that defines the node's content type
- * (use hook_node_validate() to act on all node validations).
+ * This is a node-type-specific hook, which is invoked only for the node type
+ * being affected. See
+ * @link node_api_hooks Node API hooks @endlink for more information.
+ *
+ * Use hook_node_validate() to respond to node validation of all node types.
*
* This hook is invoked from node_validate(), after a user has finished
* editing the node and is previewing or submitting it. It is invoked at the end
@@ -1252,8 +1280,11 @@
/**
* Display a node.
*
- * This hook is invoked only on the module that defines the node's content type
- * (use hook_node_view() to act on all node views).
+ * This is a node-type-specific hook, which is invoked only for the node type
+ * being affected. See
+ * @link node_api_hooks Node API hooks @endlink for more information.
+ *
+ * Use hook_node_view() to respond to node view of all node types.
*
* This hook is invoked during node viewing after the node is fully loaded, so
* that the node type module can define a custom method for display, or add to
@@ -1263,6 +1294,10 @@
* The node to be displayed, as returned by node_load().
* @param $view_mode
* View mode, e.g. 'full', 'teaser', ...
+ * @param $langcode
+ * (optional) A language code to use for rendering. Defaults to the global
+ * content language of the current request.
+ *
* @return
* The passed $node parameter should be modified as necessary and returned so
* it can be properly presented. Nodes are prepared for display by assembling
@@ -1276,7 +1311,7 @@
*
* @ingroup node_api_hooks
*/
-function hook_view($node, $view_mode) {
+function hook_view($node, $view_mode, $langcode = NULL) {
if ($view_mode == 'full' && node_is_page($node)) {
$breadcrumb = array();
$breadcrumb[] = l(t('Home'), NULL);
diff -Naur drupal-7.25/modules/node/node.info drupal-7.66/modules/node/node.info
--- drupal-7.25/modules/node/node.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/node/node.info 2019-04-17 22:39:36.000000000 +0200
@@ -9,8 +9,7 @@
configure = admin/structure/types
stylesheets[all][] = node.css
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/node/node.install drupal-7.66/modules/node/node.install
--- drupal-7.25/modules/node/node.install 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/node/node.install 2019-04-17 22:20:46.000000000 +0200
@@ -410,6 +410,7 @@
'nid' => array(
'description' => 'The {node}.nid that was read.',
'type' => 'int',
+ 'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
@@ -934,5 +935,32 @@
}
/**
+ * Enable node types that may have been erroneously disabled in Drupal 7.36.
+ */
+function node_update_7015() {
+ db_update('node_type')
+ ->fields(array('disabled' => 0))
+ ->condition('base', 'node_content')
+ ->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".
*/
diff -Naur drupal-7.25/modules/node/node.module drupal-7.66/modules/node/node.module
--- drupal-7.25/modules/node/node.module 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/node/node.module 2019-04-17 22:20:46.000000000 +0200
@@ -210,7 +210,7 @@
'custom settings' => FALSE,
),
'search_result' => array(
- 'label' => t('Search result'),
+ 'label' => t('Search result highlighting input'),
'custom settings' => FALSE,
),
);
@@ -506,7 +506,8 @@
* - custom: TRUE or FALSE indicating whether this type is defined by a module
* (FALSE) or by a user (TRUE) via Add Content Type.
* - modified: TRUE or FALSE indicating whether this type has been modified by
- * an administrator. Currently not used in any way.
+ * an administrator. When modifying an existing node type, set to TRUE, or
+ * the change will be ignored on node_types_rebuild().
* - locked: TRUE or FALSE indicating whether the administrator can change the
* machine name of this type.
* - disabled: TRUE or FALSE indicating whether this type has been disabled.
@@ -1397,12 +1398,7 @@
$node->content = array();
// Allow modules to change the view mode.
- $context = array(
- 'entity_type' => 'node',
- 'entity' => $node,
- 'langcode' => $langcode,
- );
- drupal_alter('entity_view_mode', $view_mode, $context);
+ $view_mode = key(entity_view_mode_prepare('node', array($node->nid => $node), $view_mode, $langcode));
// The 'view' hook can be implemented to overwrite the default function
// to display nodes.
@@ -1585,9 +1581,7 @@
),
'access content overview' => array(
'title' => t('Access the content overview page'),
- 'description' => user_access('access content overview')
- ? t('Get an overview of all content.', array('@url' => url('admin/content')))
- : t('Get an overview of all content.'),
+ 'description' => t('Get an overview of all content.', array('@url' => url('admin/content'))),
),
'access content' => array(
'title' => t('View published content'),
@@ -1615,7 +1609,7 @@
}
/**
- * Gathers the rankings from the the hook_ranking() implementations.
+ * Gathers the rankings from the hook_ranking() implementations.
*
* @param $query
* A query object that has been extended with the Search DB Extender.
@@ -1683,7 +1677,7 @@
);
$form['content_ranking']['#theme'] = 'node_search_admin';
$form['content_ranking']['info'] = array(
- '#value' => '' . t('The following numbers control which properties the content search should favor when ordering the results. Higher numbers mean more influence, zero means the property is ignored. Changing these numbers does not require the search index to be rebuilt. Changes take effect immediately.') . ''
+ '#markup' => '' . t('Influence is a numeric multiplier used in ordering search results. A higher number means the corresponding factor has more influence on search results; zero means the factor is ignored. Changing these numbers does not require the search index to be rebuilt. Changes take effect immediately.') . '
'
);
// Note: reversed to reflect that higher number = higher ranking.
@@ -1870,7 +1864,7 @@
$output = drupal_render($form['info']);
- $header = array(t('Factor'), t('Weight'));
+ $header = array(t('Factor'), t('Influence'));
foreach (element_children($form['factors']) as $key) {
$row = array();
$row[] = $form['factors'][$key]['#title'];
@@ -2604,9 +2598,10 @@
$node->link = url("node/$node->nid", array('absolute' => TRUE));
$node->rss_namespaces = array();
+ $account = user_load($node->uid);
$node->rss_elements = array(
array('key' => 'pubDate', 'value' => gmdate('r', $node->created)),
- array('key' => 'dc:creator', 'value' => $node->name),
+ array('key' => 'dc:creator', 'value' => format_username($account)),
array('key' => 'guid', 'value' => $node->nid . ' at ' . $base_url, 'attributes' => array('isPermaLink' => 'false'))
);
@@ -2664,15 +2659,26 @@
* An array in the format expected by drupal_render().
*/
function node_view_multiple($nodes, $view_mode = 'teaser', $weight = 0, $langcode = NULL) {
- field_attach_prepare_view('node', $nodes, $view_mode, $langcode);
- entity_prepare_view('node', $nodes, $langcode);
$build = array();
+ $entities_by_view_mode = entity_view_mode_prepare('node', $nodes, $view_mode, $langcode);
+ foreach ($entities_by_view_mode as $entity_view_mode => $entities) {
+ field_attach_prepare_view('node', $entities, $entity_view_mode, $langcode);
+ entity_prepare_view('node', $entities, $langcode);
+
+ foreach ($entities as $entity) {
+ $build['nodes'][$entity->nid] = node_view($entity, $entity_view_mode, $langcode);
+ }
+ }
+
foreach ($nodes as $node) {
- $build['nodes'][$node->nid] = node_view($node, $view_mode, $langcode);
$build['nodes'][$node->nid]['#weight'] = $weight;
$weight++;
}
+ // Sort here, to preserve the input order of the entities that were passed to
+ // this function.
+ uasort($build['nodes'], 'element_sort');
$build['nodes']['#sorted'] = TRUE;
+
return $build;
}
@@ -2947,7 +2953,10 @@
* system. When adding a node listing to your module, be sure to use a dynamic
* query created by db_select() and add a tag of "node_access". This will allow
* modules dealing with node access to ensure only nodes to which the user has
- * access are retrieved, through the use of hook_query_TAG_alter().
+ * access are retrieved, through the use of hook_query_TAG_alter(). Tagging a
+ * query with "node_access" does not check the published/unpublished status of
+ * nodes, so the base query is responsible for ensuring that unpublished nodes
+ * are not displayed to inappropriate users.
*
* Note: Even a single module returning NODE_ACCESS_DENY from hook_node_access()
* will block access to the node. Therefore, implementers should take care to
@@ -3290,6 +3299,17 @@
/**
* Helper for node access functions.
*
+ * Queries tagged with 'node_access' that are not against the {node} table
+ * should add the base table as metadata. For example:
+ * @code
+ * $query
+ * ->addTag('node_access')
+ * ->addMetaData('base_table', 'taxonomy_index');
+ * @endcode
+ * If the query is not against the {node} table, an attempt is made to guess
+ * the table, but is not recommended to rely on this as it is deprecated and not
+ * allowed in Drupal 8. It is always safer to provide the table.
+ *
* @param $query
* The query to add conditions to.
* @param $type
@@ -3618,7 +3638,8 @@
// Try to allocate enough time to rebuild node grants
drupal_set_time_limit(240);
- $nids = db_query("SELECT nid FROM {node}")->fetchCol();
+ // Rebuild newest nodes first so that recent content becomes available quickly.
+ $nids = db_query("SELECT nid FROM {node} ORDER BY nid DESC")->fetchCol();
foreach ($nids as $nid) {
$node = node_load($nid, NULL, TRUE);
// To preserve database integrity, only acquire grants if the node
@@ -3651,6 +3672,8 @@
}
/**
+ * Implements callback_batch_operation().
+ *
* Performs batch operation for node_access_rebuild().
*
* This is a multistep operation: we go through all nodes by packs of 20. The
@@ -3665,7 +3688,7 @@
// Initiate multistep processing.
$context['sandbox']['progress'] = 0;
$context['sandbox']['current_node'] = 0;
- $context['sandbox']['max'] = db_query('SELECT COUNT(DISTINCT nid) FROM {node}')->fetchField();
+ $context['sandbox']['max'] = db_query('SELECT COUNT(nid) FROM {node}')->fetchField();
}
// Process the next 20 nodes.
@@ -3689,6 +3712,8 @@
}
/**
+ * Implements callback_batch_finished().
+ *
* Performs post-processing for node_access_rebuild().
*
* @param bool $success
diff -Naur drupal-7.25/modules/node/node.pages.inc drupal-7.66/modules/node/node.pages.inc
--- drupal-7.25/modules/node/node.pages.inc 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/node/node.pages.inc 2019-04-17 22:20:46.000000000 +0200
@@ -371,35 +371,37 @@
* @see node_form_build_preview()
*/
function node_preview($node) {
- if (node_access('create', $node) || node_access('update', $node)) {
- _field_invoke_multiple('load', 'node', array($node->nid => $node));
+ // Clone the node before previewing it to prevent the node itself from being
+ // modified.
+ $cloned_node = clone $node;
+ if (node_access('create', $cloned_node) || node_access('update', $cloned_node)) {
+ _field_invoke_multiple('load', 'node', array($cloned_node->nid => $cloned_node));
// Load the user's name when needed.
- if (isset($node->name)) {
+ if (isset($cloned_node->name)) {
// The use of isset() is mandatory in the context of user IDs, because
// user ID 0 denotes the anonymous user.
- if ($user = user_load_by_name($node->name)) {
- $node->uid = $user->uid;
- $node->picture = $user->picture;
+ if ($user = user_load_by_name($cloned_node->name)) {
+ $cloned_node->uid = $user->uid;
+ $cloned_node->picture = $user->picture;
}
else {
- $node->uid = 0; // anonymous user
+ $cloned_node->uid = 0; // anonymous user
}
}
- elseif ($node->uid) {
- $user = user_load($node->uid);
- $node->name = $user->name;
- $node->picture = $user->picture;
+ elseif ($cloned_node->uid) {
+ $user = user_load($cloned_node->uid);
+ $cloned_node->name = $user->name;
+ $cloned_node->picture = $user->picture;
}
- $node->changed = REQUEST_TIME;
- $nodes = array($node->nid => $node);
- field_attach_prepare_view('node', $nodes, 'full');
+ $cloned_node->changed = REQUEST_TIME;
+ $nodes = array($cloned_node->nid => $cloned_node);
// Display a preview of the node.
if (!form_get_errors()) {
- $node->in_preview = TRUE;
- $output = theme('node_preview', array('node' => $node));
- unset($node->in_preview);
+ $cloned_node->in_preview = TRUE;
+ $output = theme('node_preview', array('node' => $cloned_node));
+ unset($cloned_node->in_preview);
}
drupal_set_title(t('Preview'), PASS_THROUGH);
diff -Naur drupal-7.25/modules/node/node.test drupal-7.66/modules/node/node.test
--- drupal-7.25/modules/node/node.test 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/node/node.test 2019-04-17 22:20:46.000000000 +0200
@@ -457,10 +457,70 @@
}
function setUp() {
- parent::setUp();
+ parent::setUp(array('taxonomy', 'node'));
$web_user = $this->drupalCreateUser(array('edit own page content', 'create page content'));
$this->drupalLogin($web_user);
+
+ // Add a vocabulary so we can test different view modes.
+ $vocabulary = (object) array(
+ 'name' => $this->randomName(),
+ 'description' => $this->randomName(),
+ 'machine_name' => drupal_strtolower($this->randomName()),
+ 'help' => '',
+ 'nodes' => array('page' => 'page'),
+ );
+ taxonomy_vocabulary_save($vocabulary);
+
+ $this->vocabulary = $vocabulary;
+
+ // Add a term to the vocabulary.
+ $term = (object) array(
+ 'name' => $this->randomName(),
+ 'description' => $this->randomName(),
+ // Use the first available text format.
+ 'format' => db_query_range('SELECT format FROM {filter_format}', 0, 1)->fetchField(),
+ 'vid' => $this->vocabulary->vid,
+ 'vocabulary_machine_name' => $vocabulary->machine_name,
+ );
+ taxonomy_term_save($term);
+
+ $this->term = $term;
+
+ // Set up a field and instance.
+ $this->field_name = drupal_strtolower($this->randomName());
+ $this->field = array(
+ 'field_name' => $this->field_name,
+ 'type' => 'taxonomy_term_reference',
+ 'settings' => array(
+ 'allowed_values' => array(
+ array(
+ 'vocabulary' => $this->vocabulary->machine_name,
+ 'parent' => '0',
+ ),
+ ),
+ )
+ );
+
+ field_create_field($this->field);
+ $this->instance = array(
+ 'field_name' => $this->field_name,
+ 'entity_type' => 'node',
+ 'bundle' => 'page',
+ 'widget' => array(
+ 'type' => 'options_select',
+ ),
+ // Hide on full display but render on teaser.
+ 'display' => array(
+ 'default' => array(
+ 'type' => 'hidden',
+ ),
+ 'teaser' => array(
+ 'type' => 'taxonomy_term_reference_link',
+ ),
+ ),
+ );
+ field_create_instance($this->instance);
}
/**
@@ -470,21 +530,26 @@
$langcode = LANGUAGE_NONE;
$title_key = "title";
$body_key = "body[$langcode][0][value]";
+ $term_key = "{$this->field_name}[$langcode]";
// Fill in node creation form and preview node.
$edit = array();
$edit[$title_key] = $this->randomName(8);
$edit[$body_key] = $this->randomName(16);
+ $edit[$term_key] = $this->term->tid;
$this->drupalPost('node/add/page', $edit, t('Preview'));
- // Check that the preview is displaying the title and body.
+ // Check that the preview is displaying the title, body, and term.
$this->assertTitle(t('Preview | Drupal'), 'Basic page title is preview.');
$this->assertText($edit[$title_key], 'Title displayed.');
$this->assertText($edit[$body_key], 'Body displayed.');
+ $this->assertText($this->term->name, 'Term displayed.');
- // Check that the title and body fields are displayed with the correct values.
+ // Check that the title, body, and term fields are displayed with the
+ // correct values.
$this->assertFieldByName($title_key, $edit[$title_key], 'Title field displayed.');
$this->assertFieldByName($body_key, $edit[$body_key], 'Body field displayed.');
+ $this->assertFieldByName($term_key, $edit[$term_key], 'Term field displayed.');
}
/**
@@ -494,6 +559,7 @@
$langcode = LANGUAGE_NONE;
$title_key = "title";
$body_key = "body[$langcode][0][value]";
+ $term_key = "{$this->field_name}[$langcode]";
// Force revision on "Basic page" content.
variable_set('node_options_page', array('status', 'revision'));
@@ -501,17 +567,21 @@
$edit = array();
$edit[$title_key] = $this->randomName(8);
$edit[$body_key] = $this->randomName(16);
+ $edit[$term_key] = $this->term->tid;
$edit['log'] = $this->randomName(32);
$this->drupalPost('node/add/page', $edit, t('Preview'));
- // Check that the preview is displaying the title and body.
+ // Check that the preview is displaying the title, body, and term.
$this->assertTitle(t('Preview | Drupal'), 'Basic page title is preview.');
$this->assertText($edit[$title_key], 'Title displayed.');
$this->assertText($edit[$body_key], 'Body displayed.');
+ $this->assertText($this->term->name, 'Term displayed.');
- // Check that the title and body fields are displayed with the correct values.
+ // Check that the title, body, and term fields are displayed with the
+ // correct values.
$this->assertFieldByName($title_key, $edit[$title_key], 'Title field displayed.');
$this->assertFieldByName($body_key, $edit[$body_key], 'Body field displayed.');
+ $this->assertFieldByName($term_key, $edit[$term_key], 'Term field displayed.');
// Check that the log field has the correct value.
$this->assertFieldByName('log', $edit['log'], 'Log field displayed.');
@@ -571,6 +641,8 @@
);
try {
+ // An exception is generated by node_test_exception_node_insert() if the
+ // title is 'testing_transaction_exception'.
node_save((object) $edit);
$this->fail(t('Expected exception has not been thrown.'));
}
@@ -1374,7 +1446,7 @@
* @see node_test_node_insert()
*/
function testNodeSaveOnInsert() {
- // node_test_node_insert() tiggers a save on insert if the title equals
+ // node_test_node_insert() triggers a save on insert if the title equals
// 'new'.
$node = $this->drupalCreateNode(array('title' => 'new'));
$this->assertEqual($node->title, 'Node ' . $node->nid, 'Node saved on node insert.');
@@ -1446,7 +1518,7 @@
* 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');
@@ -2442,6 +2514,35 @@
$output = token_replace($input, array('node' => $node), array('language' => $language, 'sanitize' => FALSE));
$this->assertEqual($output, $expected, format_string('Unsanitized node token %token replaced.', array('%token' => $input)));
}
+
+ // Repeat for a node without a summary.
+ $settings['body'] = array(LANGUAGE_NONE => array(array('value' => $this->randomName(32), 'summary' => '')));
+ $node = $this->drupalCreateNode($settings);
+
+ // Load node (without summary) so that the body and summary fields are
+ // structured properly.
+ $node = node_load($node->nid);
+ $instance = field_info_instance('node', 'body', $node->type);
+
+ // Generate and test sanitized token - use full body as expected value.
+ $tests = array();
+ $tests['[node:summary]'] = _text_sanitize($instance, $langcode, $node->body[$langcode][0], 'value');
+
+ // Test to make sure that we generated something for each token.
+ $this->assertFalse(in_array(0, array_map('strlen', $tests)), 'No empty tokens generated for node without a summary.');
+
+ foreach ($tests as $input => $expected) {
+ $output = token_replace($input, array('node' => $node), array('language' => $language));
+ $this->assertEqual($output, $expected, format_string('Sanitized node token %token replaced for node without a summary.', array('%token' => $input)));
+ }
+
+ // Generate and test unsanitized tokens.
+ $tests['[node:summary]'] = $node->body[$langcode][0]['value'];
+
+ foreach ($tests as $input => $expected) {
+ $output = token_replace($input, array('node' => $node), array('language' => $language, 'sanitize' => FALSE));
+ $this->assertEqual($output, $expected, format_string('Unsanitized node token %token replaced for node without a summary.', array('%token' => $input)));
+ }
}
}
@@ -2667,8 +2768,8 @@
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');
@@ -2751,8 +2852,8 @@
$edit = array();
$langcode = LANGUAGE_NONE;
$edit["title"] = $this->randomName(8);
- $edit["body[$langcode][0][value]"] = t('Data that should appear only in the body for the node.');
- $edit["body[$langcode][0][summary]"] = t('Extra data that should appear only in the teaser for the node.');
+ $edit["body[$langcode][0][value]"] = 'Data that should appear only in the body for the node.';
+ $edit["body[$langcode][0][summary]"] = 'Extra data that should appear only in the teaser for the node.';
$this->drupalPost('node/add/page', $edit, t('Save'));
$node = $this->drupalGetNodeByTitle($edit["title"]);
@@ -2770,6 +2871,45 @@
$build = node_view($node);
$this->assertEqual($build['#view_mode'], 'teaser', 'The view mode has correctly been set to teaser.');
}
+
+ /**
+ * Tests fields that were previously hidden when the view mode is changed.
+ */
+ function testNodeViewModeChangeHiddenField() {
+ // Hide the tags field on the default display
+ $instance = field_info_instance('node', 'field_tags', 'article');
+ $instance['display']['default']['type'] = 'hidden';
+ field_update_instance($instance);
+
+ $web_user = $this->drupalCreateUser(array('create article content', 'edit own article content'));
+ $this->drupalLogin($web_user);
+
+ // Create a node.
+ $edit = array();
+ $langcode = LANGUAGE_NONE;
+ $edit["title"] = $this->randomName(8);
+ $edit["body[$langcode][0][value]"] = 'Data that should appear only in the body for the node.';
+ $edit["body[$langcode][0][summary]"] = 'Extra data that should appear only in the teaser for the node.';
+ $edit["field_tags[$langcode]"] = 'Extra tag';
+ $this->drupalPost('node/add/article', $edit, t('Save'));
+
+ $node = $this->drupalGetNodeByTitle($edit["title"]);
+
+ // Set the flag to alter the view mode and view the node.
+ variable_set('node_test_change_view_mode', 'teaser');
+ $this->drupalGet('node/' . $node->nid);
+
+ // Check that teaser mode is viewed.
+ $this->assertText('Extra data that should appear only in the teaser for the node.', 'Teaser text present');
+ // Make sure body text is not present.
+ $this->assertNoText('Data that should appear only in the body for the node.', 'Body text not present');
+ // Make sure tags are present.
+ $this->assertText('Extra tag', 'Taxonomy term present');
+
+ // Test that the correct build mode has been set.
+ $build = node_view($node);
+ $this->assertEqual($build['#view_mode'], 'teaser', 'The view mode has correctly been set to teaser.');
+ }
}
/**
@@ -2846,3 +2986,36 @@
$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.');
+ }
+
+}
diff -Naur drupal-7.25/modules/node/node.tokens.inc drupal-7.66/modules/node/node.tokens.inc
--- drupal-7.25/modules/node/node.tokens.inc 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/node/node.tokens.inc 2019-04-17 22:20:46.000000000 +0200
@@ -136,10 +136,29 @@
case 'body':
case 'summary':
if ($items = field_get_items('node', $node, 'body', $language_code)) {
- $column = ($name == 'body') ? 'value' : 'summary';
$instance = field_info_instance('node', 'body', $node->type);
$field_langcode = field_language('node', $node, 'body', $language_code);
- $replacements[$original] = $sanitize ? _text_sanitize($instance, $field_langcode, $items[0], $column) : $items[0][$column];
+ // If the summary was requested and is not empty, use it.
+ if ($name == 'summary' && !empty($items[0]['summary'])) {
+ $output = $sanitize ? _text_sanitize($instance, $field_langcode, $items[0], 'summary') : $items[0]['summary'];
+ }
+ // Attempt to provide a suitable version of the 'body' field.
+ else {
+ $output = $sanitize ? _text_sanitize($instance, $field_langcode, $items[0], 'value') : $items[0]['value'];
+ // A summary was requested.
+ if ($name == 'summary') {
+ if (isset($instance['display']['teaser']['settings']['trim_length'])) {
+ $trim_length = $instance['display']['teaser']['settings']['trim_length'];
+ }
+ else {
+ // Use default value.
+ $trim_length = NULL;
+ }
+ // Generate an optionally trimmed summary of the body field.
+ $output = text_summary($output, $instance['settings']['text_processing'] ? $items[0]['format'] : NULL, $trim_length);
+ }
+ }
+ $replacements[$original] = $output;
}
break;
diff -Naur drupal-7.25/modules/node/tests/node_access_test.info drupal-7.66/modules/node/tests/node_access_test.info
--- drupal-7.25/modules/node/tests/node_access_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/node/tests/node_access_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
core = 7.x
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/node/tests/node_access_test.module drupal-7.66/modules/node/tests/node_access_test.module
--- drupal-7.25/modules/node/tests/node_access_test.module 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/node/tests/node_access_test.module 2019-04-17 22:20:46.000000000 +0200
@@ -211,7 +211,7 @@
}
/**
- * Implements hook_nodeapi_update().
+ * Implements hook_node_update().
*/
function node_access_test_node_update($node) {
_node_access_test_node_write($node);
diff -Naur drupal-7.25/modules/node/tests/node_test.info drupal-7.66/modules/node/tests/node_test.info
--- drupal-7.25/modules/node/tests/node_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/node/tests/node_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
core = 7.x
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/node/tests/node_test_exception.info drupal-7.66/modules/node/tests/node_test_exception.info
--- drupal-7.25/modules/node/tests/node_test_exception.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/node/tests/node_test_exception.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
core = 7.x
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/openid/openid.inc drupal-7.66/modules/openid/openid.inc
--- drupal-7.25/modules/openid/openid.inc 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/openid/openid.inc 2019-04-17 22:20:46.000000000 +0200
@@ -158,6 +158,11 @@
return array();
}
+ // Also stop parsing if there is an unreasonably large number of tags.
+ if ($dom->getElementsByTagName('*')->length > variable_get('openid_xrds_maximum_tag_count', 30000)) {
+ return array();
+ }
+
// Parse the DOM document for the information we need.
if ($xml = simplexml_import_dom($dom)) {
foreach ($xml->children(OPENID_NS_XRD)->XRD as $xrd) {
diff -Naur drupal-7.25/modules/openid/openid.info drupal-7.66/modules/openid/openid.info
--- drupal-7.25/modules/openid/openid.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/openid/openid.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
core = 7.x
files[] = openid.test
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/openid/openid.install drupal-7.66/modules/openid/openid.install
--- drupal-7.25/modules/openid/openid.install 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/openid/openid.install 2019-04-17 22:20:46.000000000 +0200
@@ -15,13 +15,14 @@
'idp_endpoint_uri' => array(
'type' => 'varchar',
'length' => 255,
- 'description' => 'URI of the OpenID Provider endpoint.',
+ 'not null' => TRUE,
+ 'description' => 'Primary Key: URI of the OpenID Provider endpoint.',
),
'assoc_handle' => array(
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
- 'description' => 'Primary Key: Used to refer to this association in subsequent messages.',
+ 'description' => 'Used to refer to this association in subsequent messages.',
),
'assoc_type' => array(
'type' => 'varchar',
@@ -51,7 +52,10 @@
'description' => 'The lifetime, in seconds, of this association.',
),
),
- 'primary key' => array('assoc_handle'),
+ 'primary key' => array('idp_endpoint_uri'),
+ 'unique keys' => array(
+ 'assoc_handle' => array('assoc_handle'),
+ ),
);
$schema['openid_nonce'] = array(
@@ -158,3 +162,69 @@
/**
* @} End of "addtogroup updates-6.x-to-7.x".
*/
+
+/**
+ * @addtogroup updates-7.x-extra
+ * @{
+ */
+
+/**
+ * Bind associations to their providers.
+ */
+function openid_update_7000() {
+ db_drop_table('openid_association');
+
+ $schema = array(
+ 'description' => 'Stores temporary shared key association information for OpenID authentication.',
+ 'fields' => array(
+ 'idp_endpoint_uri' => array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'description' => 'Primary Key: URI of the OpenID Provider endpoint.',
+ ),
+ 'assoc_handle' => array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'description' => 'Used to refer to this association in subsequent messages.',
+ ),
+ 'assoc_type' => array(
+ 'type' => 'varchar',
+ 'length' => 32,
+ 'description' => 'The signature algorithm used: one of HMAC-SHA1 or HMAC-SHA256.',
+ ),
+ 'session_type' => array(
+ 'type' => 'varchar',
+ 'length' => 32,
+ 'description' => 'Valid association session types: "no-encryption", "DH-SHA1", and "DH-SHA256".',
+ ),
+ 'mac_key' => array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'description' => 'The MAC key (shared secret) for this association.',
+ ),
+ 'created' => array(
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'UNIX timestamp for when the association was created.',
+ ),
+ 'expires_in' => array(
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'The lifetime, in seconds, of this association.',
+ ),
+ ),
+ 'primary key' => array('idp_endpoint_uri'),
+ 'unique keys' => array(
+ 'assoc_handle' => array('assoc_handle'),
+ ),
+ );
+ db_create_table('openid_association', $schema);
+}
+
+/**
+ * @} End of "addtogroup updates-7.x-extra".
+ */
diff -Naur drupal-7.25/modules/openid/openid.module drupal-7.66/modules/openid/openid.module
--- drupal-7.25/modules/openid/openid.module 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/openid/openid.module 2019-04-17 22:20:46.000000000 +0200
@@ -365,14 +365,20 @@
// to the OpenID Provider, we need to do discovery on the returned
// identififer to make sure that the provider is authorized to
// respond on behalf of this.
- if ($response_claimed_id != $claimed_id) {
+ if ($response_claimed_id != $claimed_id || $response_claimed_id != $response['openid.identity']) {
$discovery = openid_discovery($response['openid.claimed_id']);
+ $uris = array();
if ($discovery && !empty($discovery['services'])) {
- $uris = array();
foreach ($discovery['services'] as $discovered_service) {
- if (in_array('http://specs.openid.net/auth/2.0/server', $discovered_service['types']) || in_array('http://specs.openid.net/auth/2.0/signon', $discovered_service['types'])) {
- $uris[] = $discovered_service['uri'];
+ if (!in_array('http://specs.openid.net/auth/2.0/server', $discovered_service['types']) && !in_array('http://specs.openid.net/auth/2.0/signon', $discovered_service['types'])) {
+ continue;
}
+ // The OP-Local Identifier (if different than the Claimed
+ // Identifier) must be present in the XRDS document.
+ if ($response_claimed_id != $response['openid.identity'] && (!isset($discovered_service['identity']) || $discovered_service['identity'] != $response['openid.identity'])) {
+ continue;
+ }
+ $uris[] = $discovered_service['uri'];
}
}
if (!in_array($service['uri'], $uris)) {
@@ -839,7 +845,7 @@
// direct verification: ignore the openid.assoc_handle, even if present.
// See http://openid.net/specs/openid-authentication-2_0.html#rfc.section.11.4.1
if (!empty($response['openid.assoc_handle']) && empty($response['openid.invalidate_handle'])) {
- $association = db_query("SELECT * FROM {openid_association} WHERE assoc_handle = :assoc_handle", array(':assoc_handle' => $response['openid.assoc_handle']))->fetchObject();
+ $association = db_query("SELECT * FROM {openid_association} WHERE idp_endpoint_uri = :endpoint AND assoc_handle = :assoc_handle", array(':endpoint' => $service['uri'], ':assoc_handle' => $response['openid.assoc_handle']))->fetchObject();
}
if ($association && isset($association->session_type)) {
@@ -871,6 +877,7 @@
// database to avoid reusing it again on a subsequent authentication request.
// See http://openid.net/specs/openid-authentication-2_0.html#rfc.section.11.4.2.2
db_delete('openid_association')
+ ->condition('idp_endpoint_uri', $service['uri'])
->condition('assoc_handle', $response['invalidate_handle'])
->execute();
}
diff -Naur drupal-7.25/modules/openid/openid.test drupal-7.66/modules/openid/openid.test
--- drupal-7.25/modules/openid/openid.test 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/openid/openid.test 2019-04-17 22:20:46.000000000 +0200
@@ -94,7 +94,7 @@
$identity = url('openid-test/yadis/xrds/dummy-user', array('absolute' => TRUE, 'fragment' => $this->randomName()));
// Tell openid_test.module to respond with this identifier. If the fragment
// part is present in the identifier, it should be retained.
- variable_set('openid_test_response', array('openid.claimed_id' => $identity));
+ variable_set('openid_test_response', array('openid.claimed_id' => $identity, 'openid.identity' => openid_normalize($identity)));
$this->addIdentity(url('openid-test/yadis/xrds/server', array('absolute' => TRUE)), 2, 'http://specs.openid.net/auth/2.0/identifier_select', $identity);
variable_set('openid_test_response', array());
@@ -680,11 +680,11 @@
* 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.');
}
/**
diff -Naur drupal-7.25/modules/openid/tests/openid_test.info drupal-7.66/modules/openid/tests/openid_test.info
--- drupal-7.25/modules/openid/tests/openid_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/openid/tests/openid_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -6,8 +6,7 @@
dependencies[] = openid
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/openid/tests/openid_test.module drupal-7.66/modules/openid/tests/openid_test.module
--- drupal-7.25/modules/openid/tests/openid_test.module 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/openid/tests/openid_test.module 2019-04-17 22:20:46.000000000 +0200
@@ -150,6 +150,7 @@
http://specs.openid.net/auth/2.0/server
' . url('openid-test/endpoint', array('absolute' => TRUE)) . '
+ ' . url('openid-test/yadis/xrds/server', array('absolute' => TRUE)) . '
';
}
elseif (arg(3) == 'delegate') {
diff -Naur drupal-7.25/modules/overlay/overlay-parent.js drupal-7.66/modules/overlay/overlay-parent.js
--- drupal-7.25/modules/overlay/overlay-parent.js 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/overlay/overlay-parent.js 2019-04-17 22:20:46.000000000 +0200
@@ -350,7 +350,7 @@
* TRUE if the URL represents an administrative link, FALSE otherwise.
*/
Drupal.overlay.isAdminLink = function (url) {
- if (Drupal.overlay.isExternalLink(url)) {
+ if (!Drupal.urlIsLocal(url)) {
return false;
}
@@ -378,6 +378,8 @@
/**
* Determine whether a link is external to the site.
*
+ * Deprecated. Use Drupal.urlIsLocal() instead.
+ *
* @param url
* The URL to be tested.
*
@@ -385,8 +387,28 @@
* TRUE if the URL is external to the site, FALSE otherwise.
*/
Drupal.overlay.isExternalLink = function (url) {
- var re = RegExp('^((f|ht)tps?:)?//(?!' + window.location.host + ')');
- return re.test(url);
+ return !Drupal.urlIsLocal(url);
+};
+
+/**
+ * Constructs an internal URL (relative to this site) from the provided path.
+ *
+ * For example, if the provided path is 'admin' and the site is installed at
+ * http://example.com/drupal, this function will return '/drupal/admin'.
+ *
+ * @param path
+ * The internal path, without any leading slash.
+ *
+ * @return
+ * The internal URL derived from the provided path, or null if a valid
+ * internal path cannot be constructed (for example, if an attempt to create
+ * an external link is detected).
+ */
+Drupal.overlay.getInternalUrl = function (path) {
+ var url = Drupal.settings.basePath + path;
+ if (Drupal.urlIsLocal(url)) {
+ return url;
+ }
};
/**
@@ -577,7 +599,7 @@
// If the link contains the overlay-restore class and the overlay-context
// state is set, also update the parent window's location.
var parentLocation = ($target.hasClass('overlay-restore') && typeof $.bbq.getState('overlay-context') == 'string')
- ? Drupal.settings.basePath + $.bbq.getState('overlay-context')
+ ? this.getInternalUrl($.bbq.getState('overlay-context'))
: null;
href = this.fragmentizeLink($target.get(0), parentLocation);
// Only override default behavior when left-clicking and user is not
@@ -657,11 +679,15 @@
}
// Get the overlay URL from the current URL fragment.
+ var internalUrl = null;
var state = $.bbq.getState('overlay');
if (state) {
+ internalUrl = this.getInternalUrl(state);
+ }
+ if (internalUrl) {
// Append render variable, so the server side can choose the right
// rendering and add child frame code to the page if needed.
- var url = $.param.querystring(Drupal.settings.basePath + state, { render: 'overlay' });
+ var url = $.param.querystring(internalUrl, { render: 'overlay' });
this.open(url);
this.resetActiveClass(this.getPath(Drupal.settings.basePath + state));
diff -Naur drupal-7.25/modules/overlay/overlay.info drupal-7.66/modules/overlay/overlay.info
--- drupal-7.25/modules/overlay/overlay.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/overlay/overlay.info 2019-04-17 22:39:36.000000000 +0200
@@ -4,8 +4,7 @@
version = VERSION
core = 7.x
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/overlay/overlay.module drupal-7.66/modules/overlay/overlay.module
--- drupal-7.25/modules/overlay/overlay.module 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/overlay/overlay.module 2019-04-17 22:20:46.000000000 +0200
@@ -79,6 +79,20 @@
}
/**
+ * 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().
*/
function overlay_form_user_profile_form_alter(&$form, &$form_state) {
diff -Naur drupal-7.25/modules/path/path.info drupal-7.66/modules/path/path.info
--- drupal-7.25/modules/path/path.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/path/path.info 2019-04-17 22:39:36.000000000 +0200
@@ -6,8 +6,7 @@
files[] = path.test
configure = admin/config/search/path
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/path/path.module drupal-7.66/modules/path/path.module
--- drupal-7.25/modules/path/path.module 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/path/path.module 2019-04-17 22:20:46.000000000 +0200
@@ -185,7 +185,7 @@
* Implements hook_node_insert().
*/
function path_node_insert($node) {
- if (isset($node->path)) {
+ if (isset($node->path) && isset($node->path['alias'])) {
$path = $node->path;
$path['alias'] = trim($path['alias']);
// Only save a non-empty alias.
@@ -205,9 +205,9 @@
function path_node_update($node) {
if (isset($node->path)) {
$path = $node->path;
- $path['alias'] = trim($path['alias']);
+ $path['alias'] = isset($path['alias']) ? trim($path['alias']) : '';
// Delete old alias if user erased it.
- if (!empty($path['pid']) && empty($path['alias'])) {
+ if (!empty($path['pid']) && !$path['alias']) {
path_delete($path['pid']);
}
path_node_insert($node);
diff -Naur drupal-7.25/modules/path/path.test drupal-7.66/modules/path/path.test
--- drupal-7.25/modules/path/path.test 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/path/path.test 2019-04-17 22:20:46.000000000 +0200
@@ -21,7 +21,7 @@
parent::setUp('path');
// Create test user and login.
- $web_user = $this->drupalCreateUser(array('create page content', 'edit own page content', 'administer url aliases', 'create url aliases'));
+ $web_user = $this->drupalCreateUser(array('create page content', 'edit own page content', 'administer url aliases', 'create url aliases', 'access content overview'));
$this->drupalLogin($web_user);
}
@@ -160,6 +160,34 @@
$this->drupalGet($edit['path[alias]']);
$this->assertNoText($node1->title, 'Alias was successfully deleted.');
$this->assertResponse(404);
+
+ // Create third test node.
+ $node3 = $this->drupalCreateNode();
+
+ // Create an invalid alias with a leading slash and verify that the slash
+ // is removed when the link is generated. This ensures that URL aliases
+ // cannot be used to inject external URLs.
+ // @todo The user interface should either display an error message or
+ // automatically trim these invalid aliases, rather than allowing them to
+ // be silently created, at which point the functional aspects of this
+ // test will need to be moved elsewhere and switch to using a
+ // programmatically-created alias instead.
+ $alias = $this->randomName(8);
+ $edit = array('path[alias]' => '/' . $alias);
+ $this->drupalPost('node/' . $node3->nid . '/edit', $edit, t('Save'));
+ $this->drupalGet('admin/content');
+ // This checks the link href before clicking it, rather than using
+ // DrupalWebTestCase::assertUrl() after clicking it, because the test
+ // browser does not always preserve the correct number of slashes in the
+ // URL when it visits internal links; using DrupalWebTestCase::assertUrl()
+ // would actually make the test pass unconditionally on the testbot (or
+ // anywhere else where Drupal is installed in a subdirectory).
+ $link_xpath = $this->xpath('//a[normalize-space(text())=:label]', array(':label' => $node3->title));
+ $link_href = (string) $link_xpath[0]['href'];
+ $link_prefix = base_path() . (variable_get('clean_url', 0) ? '' : '?q=');
+ $this->assertEqual($link_href, $link_prefix . $alias);
+ $this->clickLink($node3->title);
+ $this->assertResponse(404);
}
/**
diff -Naur drupal-7.25/modules/php/php.info drupal-7.66/modules/php/php.info
--- drupal-7.25/modules/php/php.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/php/php.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
core = 7.x
files[] = php.test
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/poll/poll.info drupal-7.66/modules/poll/poll.info
--- drupal-7.25/modules/poll/poll.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/poll/poll.info 2019-04-17 22:39:36.000000000 +0200
@@ -6,8 +6,7 @@
files[] = poll.test
stylesheets[all][] = poll.css
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/poll/poll.module drupal-7.66/modules/poll/poll.module
--- drupal-7.25/modules/poll/poll.module 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/poll/poll.module 2019-04-17 22:20:46.000000000 +0200
@@ -191,7 +191,6 @@
'base' => 'poll',
'description' => t('A poll is a question with a set of possible responses. A poll, once created, automatically provides a simple running count of the number of votes received for each response.'),
'title_label' => t('Question'),
- 'has_body' => FALSE,
)
);
}
@@ -249,6 +248,7 @@
'#title' => check_plain($type->title_label),
'#required' => TRUE,
'#default_value' => $node->title,
+ '#maxlength' => 255,
'#weight' => -5,
);
@@ -631,9 +631,6 @@
* The node object to load.
*/
function poll_block_latest_poll_view($node) {
- global $user;
- $output = '';
-
// This is necessary for shared objects because PHP doesn't copy objects, but
// passes them by reference. So when the objects are cached it can result in
// the wrong output being displayed on subsequent calls. The cloning and
@@ -674,9 +671,6 @@
* Implements hook_view().
*/
function poll_view($node, $view_mode) {
- global $user;
- $output = '';
-
if (!empty($node->allowvotes) && empty($node->show_results)) {
$node->content['poll_view_voting'] = drupal_get_form('poll_view_voting', $node);
}
@@ -694,7 +688,7 @@
function poll_teaser($node) {
$teaser = NULL;
if (is_array($node->choice)) {
- foreach ($node->choice as $k => $choice) {
+ foreach ($node->choice as $choice) {
if ($choice['chtext'] != '') {
$teaser .= '* ' . check_plain($choice['chtext']) . "\n";
}
@@ -720,7 +714,6 @@
'#type' => 'radios',
'#title' => t('Choices'),
'#title_display' => 'invisible',
- '#default_value' => -1,
'#options' => $list,
);
}
@@ -748,7 +741,7 @@
* Validation function for processing votes
*/
function poll_view_voting_validate($form, &$form_state) {
- if ($form_state['values']['choice'] == -1) {
+ if (empty($form_state['values']['choice'])) {
form_set_error( 'choice', t('Your vote could not be recorded because you did not select any of the choices.'));
}
}
@@ -925,7 +918,6 @@
*
* @see poll-bar.tpl.php
* @see poll-bar--block.tpl.php
- * @see theme_poll_bar()
*/
function template_preprocess_poll_bar(&$variables) {
if ($variables['block']) {
diff -Naur drupal-7.25/modules/poll/poll.test drupal-7.66/modules/poll/poll.test
--- drupal-7.25/modules/poll/poll.test 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/poll/poll.test 2019-04-17 22:20:46.000000000 +0200
@@ -315,6 +315,11 @@
$this->drupalLogin($vote_user);
+ // Record a vote without selecting any choice.
+ $edit = array();
+ $this->drupalPost('node/' . $poll_nid, $edit, t('Vote'));
+ $this->assertText(t('Your vote could not be recorded because you did not select any of the choices.'), 'Found the empty poll submission error message.');
+
// Record a vote for the first choice.
$edit = array(
'choice' => '1',
diff -Naur drupal-7.25/modules/profile/profile.info drupal-7.66/modules/profile/profile.info
--- drupal-7.25/modules/profile/profile.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/profile/profile.info 2019-04-17 22:39:36.000000000 +0200
@@ -11,8 +11,7 @@
; See user_system_info_alter().
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/profile/profile.test drupal-7.66/modules/profile/profile.test
--- drupal-7.25/modules/profile/profile.test 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/profile/profile.test 2019-04-17 22:20:46.000000000 +0200
@@ -339,12 +339,22 @@
$this->setProfileField($field, $field['value']);
// Set some html for what we want to see in the page output later.
- $autocomplete_html = '';
- $field_html = '';
+ // Autocomplete always uses non-clean URLs.
+ $current_clean_url = isset($GLOBALS['conf']['clean_url']) ? $GLOBALS['conf']['clean_url'] : NULL;
+ $GLOBALS['conf']['clean_url'] = 0;
+ $autocomplete_url = url('profile/autocomplete/' . $field['fid'], array('absolute' => TRUE, 'script' => 'index.php'));
+ $GLOBALS['conf']['clean_url'] = $current_clean_url;
+ $autocomplete_id = drupal_html_id('edit-' . $field['form_name'] . '-autocomplete');
+ $autocomplete_html = '';
// Check that autocompletion html is found on the user's profile edit page.
$this->drupalGet('user/' . $this->admin_user->uid . '/edit/' . $category);
$this->assertRaw($autocomplete_html, 'Autocomplete found.');
+ $this->assertFieldByXPath(
+ '//input[@type="text" and @name="' . $field['form_name'] . '" and contains(@class, "form-autocomplete")]',
+ '',
+ 'Text input field found'
+ );
$this->assertRaw('misc/autocomplete.js', 'Autocomplete JavaScript found.');
$this->assertRaw('class="form-text form-autocomplete"', 'Autocomplete form element class found.');
diff -Naur drupal-7.25/modules/rdf/rdf.info drupal-7.66/modules/rdf/rdf.info
--- drupal-7.25/modules/rdf/rdf.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/rdf/rdf.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
core = 7.x
files[] = rdf.test
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/rdf/rdf.module drupal-7.66/modules/rdf/rdf.module
--- drupal-7.25/modules/rdf/rdf.module 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/rdf/rdf.module 2019-04-17 22:20:46.000000000 +0200
@@ -190,17 +190,33 @@
* An RDF mapping structure or an empty array if no record was found.
*/
function _rdf_mapping_load($type, $bundle) {
- $mapping = db_select('rdf_mapping')
- ->fields(NULL, array('mapping'))
+ $mappings = _rdf_mapping_load_multiple($type, array($bundle));
+ return $mappings ? reset($mappings) : array();
+}
+
+/**
+ * Helper function to retrieve a set of RDF mappings from the database.
+ *
+ * @param $type
+ * The entity type of the mappings.
+ * @param $bundles
+ * The bundles the mappings refer to.
+ *
+ * @return
+ * An array of RDF mapping structures, or an empty array.
+ */
+function _rdf_mapping_load_multiple($type, array $bundles) {
+ $mappings = db_select('rdf_mapping')
+ ->fields(NULL, array('bundle', 'mapping'))
->condition('type', $type)
- ->condition('bundle', $bundle)
+ ->condition('bundle', $bundles)
->execute()
- ->fetchField();
+ ->fetchAllKeyed();
- if (!$mapping) {
- return array();
+ foreach ($mappings as $bundle => $mapping) {
+ $mappings[$bundle] = unserialize($mapping);
}
- return unserialize($mapping);
+ return $mappings;
}
/**
@@ -368,10 +384,13 @@
function rdf_entity_info_alter(&$entity_info) {
// Loop through each entity type and its bundles.
foreach ($entity_info as $entity_type => $entity_type_info) {
- if (isset($entity_type_info['bundles'])) {
- foreach ($entity_type_info['bundles'] as $bundle => $bundle_info) {
- if ($mapping = _rdf_mapping_load($entity_type, $bundle)) {
- $entity_info[$entity_type]['bundles'][$bundle]['rdf_mapping'] = $mapping;
+ if (!empty($entity_type_info['bundles'])) {
+ $bundles = array_keys($entity_type_info['bundles']);
+ $mappings = _rdf_mapping_load_multiple($entity_type, $bundles);
+
+ foreach ($bundles as $bundle) {
+ if (isset($mappings[$bundle])) {
+ $entity_info[$entity_type]['bundles'][$bundle]['rdf_mapping'] = $mappings[$bundle];
}
else {
// If no mapping was found in the database, assign the default RDF
@@ -471,27 +490,17 @@
$variables['attributes_array']['about'] = empty($variables['node_url']) ? NULL: $variables['node_url'];
$variables['attributes_array']['typeof'] = empty($variables['node']->rdf_mapping['rdftype']) ? NULL : $variables['node']->rdf_mapping['rdftype'];
- // Adds RDFa markup to the title of the node. Because the RDFa markup is
- // added to the tag which might contain HTML code, we specify an empty
- // datatype to ensure the value of the title read by the RDFa parsers is a
- // literal.
- $variables['title_attributes_array']['property'] = empty($variables['node']->rdf_mapping['title']['predicates']) ? NULL : $variables['node']->rdf_mapping['title']['predicates'];
- $variables['title_attributes_array']['datatype'] = '';
-
- // In full node mode, the title is not displayed by node.tpl.php so it is
- // added in the tag of the HTML page.
- if ($variables['page']) {
- $element = array(
- '#tag' => 'meta',
- '#attributes' => array(
- 'content' => $variables['node']->title,
- 'about' => $variables['node_url'],
+ // Adds RDFa markup about the title of the node to the title_suffix.
+ if (!empty($variables['node']->rdf_mapping['title']['predicates'])) {
+ $variables['title_suffix']['rdf_meta_title'] = array(
+ '#theme' => 'rdf_metadata',
+ '#metadata' => array(
+ array(
+ 'property' => $variables['node']->rdf_mapping['title']['predicates'],
+ 'content' => $variables['node']->title,
+ ),
),
);
- if (!empty($variables['node']->rdf_mapping['title']['predicates'])) {
- $element['#attributes']['property'] = $variables['node']->rdf_mapping['title']['predicates'];
- }
- drupal_add_html_head($element, 'rdf_node_title');
}
// Adds RDFa markup for the date.
@@ -511,35 +520,20 @@
}
// Adds RDFa markup annotating the number of comments a node has.
- if (isset($variables['node']->comment_count) && !empty($variables['node']->rdf_mapping['comment_count']['predicates'])) {
- // Annotates the 'x comments' link in teaser view.
- if (isset($variables['content']['links']['comment']['#links']['comment-comments'])) {
- $comment_count_attributes['property'] = $variables['node']->rdf_mapping['comment_count']['predicates'];
- $comment_count_attributes['content'] = $variables['node']->comment_count;
- $comment_count_attributes['datatype'] = $variables['node']->rdf_mapping['comment_count']['datatype'];
- // According to RDFa parsing rule number 4, a new subject URI is created
- // from the href attribute if no rel/rev attribute is present. To get the
- // original node URL from the about attribute of the parent container we
- // set an empty rel attribute which triggers rule number 5. See
- // http://www.w3.org/TR/rdfa-syntax/#sec_5.5.
- $comment_count_attributes['rel'] = '';
- $variables['content']['links']['comment']['#links']['comment-comments']['attributes'] += $comment_count_attributes;
- }
- // In full node view, the number of comments is not displayed by
- // node.tpl.php so it is expressed in RDFa in the tag of the HTML
- // page.
- if ($variables['page'] && user_access('access comments')) {
- $element = array(
- '#tag' => 'meta',
- '#attributes' => array(
- 'about' => $variables['node_url'],
+ if (isset($variables['node']->comment_count) &&
+ !empty($variables['node']->rdf_mapping['comment_count']['predicates']) &&
+ user_access('access comments')) {
+ // Adds RDFa markup for the comment count near the node title as metadata.
+ $variables['title_suffix']['rdf_meta_comment_count'] = array(
+ '#theme' => 'rdf_metadata',
+ '#metadata' => array(
+ array(
'property' => $variables['node']->rdf_mapping['comment_count']['predicates'],
'content' => $variables['node']->comment_count,
'datatype' => $variables['node']->rdf_mapping['comment_count']['datatype'],
),
- );
- drupal_add_html_head($element, 'rdf_node_comment_count');
- }
+ ),
+ );
}
}
@@ -865,9 +859,9 @@
$output = '';
foreach ($variables['metadata'] as $attributes) {
// Add a class so that developers viewing the HTML source can see why there
- // are empty tags in the document. The class can also be used to set
- // a CSS display:none rule in a theme where empty spans affect display.
+ // are empty tags in the document.
$attributes['class'][] = 'rdf-meta';
+ $attributes['class'][] = 'element-hidden';
// The XHTML+RDFa doctype allows either or syntax to
// be used, but for maximum browser compatibility, W3C recommends the
// former when serving pages using the text/html media type, see
diff -Naur drupal-7.25/modules/rdf/rdf.test drupal-7.66/modules/rdf/rdf.test
--- drupal-7.25/modules/rdf/rdf.test 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/rdf/rdf.test 2019-04-17 22:20:46.000000000 +0200
@@ -301,7 +301,7 @@
// Ensure the default bundle mapping for node is used. These attributes come
// from the node default bundle definition.
- $blog_title = $this->xpath("//meta[@property='dc:title' and @content='$node->title']");
+ $blog_title = $this->xpath("//div[@about='$url']/span[@property='dc:title' and @content='$node->title']");
$blog_meta = $this->xpath("//div[(@about='$url') and (@typeof='sioct:Weblog')]//span[contains(@property, 'dc:date') and contains(@property, 'dc:created') and @datatype='xsd:dateTime' and @content='$isoDate']");
$this->assertTrue(!empty($blog_title), 'Property dc:title is present in meta tag.');
$this->assertTrue(!empty($blog_meta), 'RDF type is present on post. Properties dc:date and dc:created are present on post date.');
@@ -324,7 +324,7 @@
$this->drupalGet('node/' . $node->nid);
// Ensure the mapping defined in rdf_module.test is used.
- $test_bundle_title = $this->xpath('//meta[@property="dc:title" and @content="' . $node->title . '"]');
+ $test_bundle_title = $this->xpath("//div[@about='$url']/span[@property='dc:title' and @content=\"$node->title\"]");
$test_bundle_meta = $this->xpath("//div[(@about='$url') and contains(@typeof, 'foo:mapping_install1') and contains(@typeof, 'bar:mapping_install2')]//span[contains(@property, 'dc:date') and contains(@property, 'dc:created') and @datatype='xsd:dateTime' and @content='$isoDate']");
$this->assertTrue(!empty($test_bundle_title), 'Property dc:title is present in meta tag.');
$this->assertTrue(!empty($test_bundle_meta), 'RDF type is present on post. Properties dc:date and dc:created are present on post date.');
@@ -343,7 +343,7 @@
// Ensure the default bundle mapping for node is used. These attributes come
// from the node default bundle definition.
- $random_bundle_title = $this->xpath("//meta[@property='dc:title' and @content='$node->title']");
+ $random_bundle_title = $this->xpath("//div[@about='$url']/span[@property='dc:title' and @content='$node->title']");
$random_bundle_meta = $this->xpath("//div[(@about='$url') and contains(@typeof, 'sioc:Item') and contains(@typeof, 'foaf:Document')]//span[contains(@property, 'dc:date') and contains(@property, 'dc:created') and @datatype='xsd:dateTime' and @content='$isoDate']");
$this->assertTrue(!empty($random_bundle_title), 'Property dc:title is present in meta tag.');
$this->assertTrue(!empty($random_bundle_meta), 'RDF type is present on post. Properties dc:date and dc:created are present on post date.');
@@ -461,15 +461,13 @@
// Tests number of comments in teaser view.
$this->drupalGet('node');
- $comment_count_teaser = $this->xpath('//div[contains(@typeof, "sioc:Item")]//li[contains(@class, "comment-comments")]/a[contains(@property, "sioc:num_replies") and contains(@content, "2") and @datatype="xsd:integer"]');
+ $node_url = url('node/' . $this->node1->nid);
+ $comment_count_teaser = $this->xpath('//div[@about=:node-url]/span[@property="sioc:num_replies" and @content="2" and @datatype="xsd:integer"]', array(':node-url' => $node_url));
$this->assertTrue(!empty($comment_count_teaser), 'RDFa markup for the number of comments found on teaser view.');
- $comment_count_link = $this->xpath('//div[@about=:url]//a[contains(@property, "sioc:num_replies") and @rel=""]', array(':url' => url("node/{$this->node1->nid}")));
- $this->assertTrue(!empty($comment_count_link), 'Empty rel attribute found in comment count link.');
// Tests number of comments in full node view.
$this->drupalGet('node/' . $this->node1->nid);
- $node_url = url('node/' . $this->node1->nid);
- $comment_count_teaser = $this->xpath('/html/head/meta[@about=:node-url and @property="sioc:num_replies" and @content="2" and @datatype="xsd:integer"]', array(':node-url' => $node_url));
+ $comment_count_teaser = $this->xpath('//div[@about=:node-url]/span[@property="sioc:num_replies" and @content="2" and @datatype="xsd:integer"]', array(':node-url' => $node_url));
$this->assertTrue(!empty($comment_count_teaser), 'RDFa markup for the number of comments found on full node view.');
}
diff -Naur drupal-7.25/modules/rdf/tests/rdf_test.info drupal-7.66/modules/rdf/tests/rdf_test.info
--- drupal-7.25/modules/rdf/tests/rdf_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/rdf/tests/rdf_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -4,9 +4,9 @@
version = VERSION
core = 7.x
hidden = TRUE
+dependencies[] = blog
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/search/search.admin.inc drupal-7.66/modules/search/search.admin.inc
--- drupal-7.25/modules/search/search.admin.inc 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/search/search.admin.inc 2019-04-17 22:20:46.000000000 +0200
@@ -125,6 +125,16 @@
'#options' => $module_options,
'#description' => t('Choose which search module is the default.')
);
+ $form['logging'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Logging')
+ );
+ $form['logging']['search_logging'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Log searches'),
+ '#default_value' => variable_get('search_logging', 1),
+ '#description' => t('If checked, all searches will be logged. Uncheck to skip logging. Logging may affect performance.'),
+ );
$form['#validate'][] = 'search_admin_settings_validate';
$form['#submit'][] = 'search_admin_settings_submit';
diff -Naur drupal-7.25/modules/search/search.api.php drupal-7.66/modules/search/search.api.php
--- drupal-7.25/modules/search/search.api.php 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/search/search.api.php 2019-04-17 22:20:46.000000000 +0200
@@ -30,8 +30,9 @@
*
* @return
* Array with optional keys:
- * - title: Title for the tab on the search page for this module. Defaults
- * to the module name if not given.
+ * - title: Title for the tab on the search page for this module. Title must
+ * be untranslated. Outside of this return array, pass the title through the
+ * t() function to register it as a translatable string.
* - path: Path component after 'search/' for searching with this module.
* Defaults to the module name if not given.
* - conditions_callback: An implementation of callback_search_conditions().
@@ -39,6 +40,9 @@
* @ingroup search
*/
function hook_search_info() {
+ // Make the title translatable.
+ t('Content');
+
return array(
'title' => 'Content',
'path' => 'node',
diff -Naur drupal-7.25/modules/search/search.extender.inc drupal-7.66/modules/search/search.extender.inc
--- drupal-7.25/modules/search/search.extender.inc 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/search/search.extender.inc 2019-04-17 22:20:46.000000000 +0200
@@ -149,6 +149,17 @@
$this->searchExpression = $expression;
$this->type = $module;
+ // Add a search_* tag. This needs to be added before any preExecute methods
+ // for decorated queries are called, as $this->prepared will be set to TRUE
+ // and tags added in the execute method will never get used. For example,
+ // if $query is extended by 'SearchQuery' then 'PagerDefault', the
+ // search-specific tag will be added too late (when preExecute() has
+ // already been called from the PagerDefault extender), and as a
+ // consequence will not be available to hook_query_alter() implementations,
+ // nor will the correct hook_query_TAG_alter() implementations get invoked.
+ // See node_search_execute().
+ $this->addTag('search_' . $module);
+
return $this;
}
@@ -398,10 +409,10 @@
* used. However, if at least one call to addScore() has taken place, the
* keyword relevance score is not automatically added.
*
- * Also note that if you call orderBy() directly on the query, search scores
- * will not automatically be used to order search results. Your orderBy()
- * expression can reference 'calculated_score', which will be the total
- * calculated score value.
+ * Note that you must use this method to add ordering to your searches, and
+ * not call orderBy() directly, when using the SearchQuery extender. This is
+ * because of the two-pass system the SearchQuery class uses to normalize
+ * scores.
*
* @param $score
* The score expression, which should evaluate to a number between 0 and 1.
@@ -494,9 +505,8 @@
$this->orderBy('calculated_score', 'DESC');
}
- // Add tag and useful metadata.
+ // Add useful metadata.
$this
- ->addTag('search_' . $this->type)
->addMetaData('normalize', $this->normalize)
->fields('i', array('type', 'sid'));
diff -Naur drupal-7.25/modules/search/search.info drupal-7.66/modules/search/search.info
--- drupal-7.25/modules/search/search.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/search/search.info 2019-04-17 22:39:36.000000000 +0200
@@ -8,8 +8,7 @@
configure = admin/config/search/settings
stylesheets[all][] = search.css
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/search/search.install drupal-7.66/modules/search/search.install
--- drupal-7.25/modules/search/search.install 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/search/search.install 2019-04-17 22:20:46.000000000 +0200
@@ -12,6 +12,7 @@
variable_del('minimum_word_size');
variable_del('overlap_cjk');
variable_del('search_cron_limit');
+ variable_del('search_logging');
}
/**
diff -Naur drupal-7.25/modules/search/search.pages.inc drupal-7.66/modules/search/search.pages.inc
--- drupal-7.25/modules/search/search.pages.inc 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/search/search.pages.inc 2019-04-17 22:20:46.000000000 +0200
@@ -49,7 +49,7 @@
// which will get us back to this page callback. In other words, the search
// form submits with POST but redirects to GET. This way we can keep
// the search query URL clean as a whistle.
- if (empty($_POST['form_id']) || $_POST['form_id'] != 'search_form') {
+ if (empty($_POST['form_id']) || ($_POST['form_id'] != 'search_form' && $_POST['form_id'] != 'search_block_form')) {
$conditions = NULL;
if (isset($info['conditions_callback']) && function_exists($info['conditions_callback'])) {
// Build an optional array of more search conditions.
@@ -57,9 +57,10 @@
}
// Only search if there are keywords or non-empty conditions.
if ($keys || !empty($conditions)) {
- // Log the search keys.
- watchdog('search', 'Searched %type for %keys.', array('%keys' => $keys, '%type' => $info['title']), WATCHDOG_NOTICE, l(t('results'), 'search/' . $info['path'] . '/' . $keys));
-
+ if (variable_get('search_logging', TRUE)) {
+ // Log the search keys.
+ watchdog('search', 'Searched %type for %keys.', array('%keys' => $keys, '%type' => $info['title']), WATCHDOG_NOTICE, l(t('results'), 'search/' . $info['path'] . '/' . $keys));
+ }
// Collect the search results.
$results = search_data($keys, $info['module'], $conditions);
}
diff -Naur drupal-7.25/modules/search/search.test drupal-7.66/modules/search/search.test
--- drupal-7.25/modules/search/search.test 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/search/search.test 2019-04-17 22:20:46.000000000 +0200
@@ -11,6 +11,9 @@
define('SEARCH_TYPE_2', '_test2_');
define('SEARCH_TYPE_JPN', '_test3_');
+/**
+ * Indexes content and queries it.
+ */
class SearchMatchTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
@@ -307,6 +310,9 @@
}
}
+/**
+ * Indexes content and tests the advanced search form.
+ */
class SearchAdvancedSearchForm extends DrupalWebTestCase {
protected $node;
@@ -370,6 +376,9 @@
}
}
+/**
+ * Indexes content and tests ranking factors.
+ */
class SearchRankingTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
@@ -580,6 +589,9 @@
}
}
+/**
+ * Tests the rendering of the search block.
+ */
class SearchBlockTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
@@ -654,6 +666,24 @@
url('search/node/', array('absolute' => TRUE)),
'Redirected to correct url.'
);
+
+ // Test that after entering a too-short keyword in the form, you can then
+ // search again with a longer keyword. First test using the block form.
+ $terms = array('search_block_form' => 'a');
+ $this->drupalPost('node', $terms, t('Search'));
+ $this->assertText('You must include at least one positive keyword with 3 characters or more');
+ $terms = array('search_block_form' => 'foo');
+ $this->drupalPost(NULL, $terms, t('Search'));
+ $this->assertNoText('You must include at least one positive keyword with 3 characters or more');
+ $this->assertText('Your search yielded no results');
+
+ // Same test again, using the search page form for the second search this time.
+ $terms = array('search_block_form' => 'a');
+ $this->drupalPost('node', $terms, t('Search'));
+ $terms = array('keys' => 'foo');
+ $this->drupalPost(NULL, $terms, t('Search'));
+ $this->assertNoText('You must include at least one positive keyword with 3 characters or more');
+ $this->assertText('Your search yielded no results');
}
}
@@ -727,7 +757,7 @@
public static function getInfo() {
return array(
'name' => 'Comment Search tests',
- 'description' => 'Verify text formats and filters used elsewhere.',
+ 'description' => 'Test integration searching comments.',
'group' => 'Search',
);
}
@@ -1423,7 +1453,7 @@
parent::setUp('search', 'search_extra_type');
// Login as a user that can create and search content.
- $this->search_user = $this->drupalCreateUser(array('search content', 'administer search', 'administer nodes', 'bypass node access', 'access user profiles', 'administer users', 'administer blocks'));
+ $this->search_user = $this->drupalCreateUser(array('search content', 'administer search', 'administer nodes', 'bypass node access', 'access user profiles', 'administer users', 'administer blocks', 'access site reports'));
$this->drupalLogin($this->search_user);
// Add a single piece of content and index it.
@@ -1472,6 +1502,19 @@
);
$this->drupalPost('admin/config/search/settings', $edit, t('Save configuration'));
$this->assertNoText(t('The configuration options have been saved.'), 'Form does not save with an invalid word length.');
+
+ // Test logging setting. It should be on by default.
+ $text = $this->randomName(5);
+ $this->drupalPost('search/node', array('keys' => $text), t('Search'));
+ $this->drupalGet('admin/reports/dblog');
+ $this->assertLink('Searched Content for ' . $text . '.', 0, 'Search was logged');
+
+ // Turn off logging.
+ variable_set('search_logging', FALSE);
+ $text = $this->randomName(5);
+ $this->drupalPost('search/node', array('keys' => $text), t('Search'));
+ $this->drupalGet('admin/reports/dblog');
+ $this->assertNoLink('Searched Content for ' . $text . '.', 'Search was not logged');
}
/**
@@ -2017,10 +2060,11 @@
}
/**
- * Tests that search returns results with punctuation in the search phrase.
+ * Tests that search works with punctuation and HTML entities.
*/
function testPhraseSearchPunctuation() {
$node = $this->drupalCreateNode(array('body' => array(LANGUAGE_NONE => array(array('value' => "The bunny's ears were fuzzy.")))));
+ $node2 = $this->drupalCreateNode(array('body' => array(LANGUAGE_NONE => array(array('value' => 'Dignissim Aliquam & Quieligo meus natu quae quia te. Damnum© erat— neo pneum. Facilisi feugiat ibidem ratis.')))));
// Update the search index.
module_invoke_all('update_index');
@@ -2033,6 +2077,58 @@
$edit = array('keys' => '"bunny\'s"');
$this->drupalPost('search/node', $edit, t('Search'));
$this->assertText($node->title);
+
+ // Search for "&" and verify entities are not broken up in the output.
+ $edit = array('keys' => '&');
+ $this->drupalPost('search/node', $edit, t('Search'));
+ $this->assertNoRaw('&');
+ $this->assertText('You must include at least one positive keyword');
+
+ $edit = array('keys' => '&');
+ $this->drupalPost('search/node', $edit, t('Search'));
+ $this->assertNoRaw('&');
+ $this->assertText('You must include at least one positive keyword');
+ }
+}
+
+/**
+ * Tests node search with query tags.
+ */
+class SearchNodeTagTest extends DrupalWebTestCase {
+ public $test_user;
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Node search query tags',
+ 'description' => 'Tests Node search tags functionality.',
+ 'group' => 'Search',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('search', 'search_node_tags');
+ node_access_rebuild();
+
+ // Create a test user and log in.
+ $this->test_user = $this->drupalCreateUser(array('search content'));
+ $this->drupalLogin($this->test_user);
+ }
+
+ /**
+ * Tests that the correct tags are available and hooks invoked.
+ */
+ function testNodeSearchQueryTags() {
+ $this->drupalCreateNode(array('body' => array(LANGUAGE_NONE => array(array('value' => 'testing testing testing.')))));
+
+ // Update the search index.
+ module_invoke_all('update_index');
+ search_update_totals();
+
+ $edit = array('keys' => 'testing');
+ $this->drupalPost('search/node', $edit, t('Search'));
+
+ $this->assertTrue(variable_get('search_node_tags_test_query_tag', FALSE), 'hook_query_alter() was invoked and the query contained the "search_node" tag.');
+ $this->assertTrue(variable_get('search_node_tags_test_query_tag_hook', FALSE), 'hook_query_search_node_alter() was invoked.');
}
}
diff -Naur drupal-7.25/modules/search/tests/search_embedded_form.info drupal-7.66/modules/search/tests/search_embedded_form.info
--- drupal-7.25/modules/search/tests/search_embedded_form.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/search/tests/search_embedded_form.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
core = 7.x
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/search/tests/search_extra_type.info drupal-7.66/modules/search/tests/search_extra_type.info
--- drupal-7.25/modules/search/tests/search_extra_type.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/search/tests/search_extra_type.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
core = 7.x
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/search/tests/search_node_tags.info drupal-7.66/modules/search/tests/search_node_tags.info
--- drupal-7.25/modules/search/tests/search_node_tags.info 1970-01-01 01:00:00.000000000 +0100
+++ drupal-7.66/modules/search/tests/search_node_tags.info 2019-04-17 22:39:36.000000000 +0200
@@ -0,0 +1,11 @@
+name = "Test search node tags"
+description = "Support module for Node search tags testing."
+package = Testing
+version = VERSION
+core = 7.x
+hidden = TRUE
+
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
+project = "drupal"
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/search/tests/search_node_tags.module drupal-7.66/modules/search/tests/search_node_tags.module
--- drupal-7.25/modules/search/tests/search_node_tags.module 1970-01-01 01:00:00.000000000 +0100
+++ drupal-7.66/modules/search/tests/search_node_tags.module 2019-04-17 22:20:46.000000000 +0200
@@ -0,0 +1,23 @@
+hasTag('search_node')) {
+ variable_set('search_node_tags_test_query_tag', TRUE);
+ }
+}
+
+/**
+ * Implements hook_query_TAG_alter().
+ */
+function search_node_tags_query_search_node_alter(QueryAlterableInterface $query) {
+ variable_set('search_node_tags_test_query_tag_hook', TRUE);
+}
diff -Naur drupal-7.25/modules/shortcut/shortcut.info drupal-7.66/modules/shortcut/shortcut.info
--- drupal-7.25/modules/shortcut/shortcut.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/shortcut/shortcut.info 2019-04-17 22:39:36.000000000 +0200
@@ -6,8 +6,7 @@
files[] = shortcut.test
configure = admin/config/user-interface/shortcut
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/drupal_web_test_case.php drupal-7.66/modules/simpletest/drupal_web_test_case.php
--- drupal-7.25/modules/simpletest/drupal_web_test_case.php 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/simpletest/drupal_web_test_case.php 2019-04-17 22:20:46.000000000 +0200
@@ -40,6 +40,13 @@
protected $originalFileDirectory = NULL;
/**
+ * URL to the verbose output file directory.
+ *
+ * @var string
+ */
+ protected $verboseDirectoryUrl;
+
+ /**
* Time limit for the test.
*/
protected $timeLimit = 500;
@@ -143,15 +150,7 @@
);
// Store assertion for display after the test has completed.
- try {
- $connection = Database::getConnection('default', 'simpletest_original_default');
- }
- catch (DatabaseConnectionNotDefinedException $e) {
- // If the test was not set up, the simpletest_original_default
- // connection does not exist.
- $connection = Database::getConnection('default', 'default');
- }
- $connection
+ self::getDatabaseConnection()
->insert('simpletest')
->fields($assertion)
->execute();
@@ -167,6 +166,25 @@
}
/**
+ * Returns the database connection to the site running Simpletest.
+ *
+ * @return DatabaseConnection
+ * The database connection to use for inserting assertions.
+ */
+ public static function getDatabaseConnection() {
+ try {
+ $connection = Database::getConnection('default', 'simpletest_original_default');
+ }
+ catch (DatabaseConnectionNotDefinedException $e) {
+ // If the test was not set up, the simpletest_original_default
+ // connection does not exist.
+ $connection = Database::getConnection('default', 'default');
+ }
+
+ return $connection;
+ }
+
+ /**
* Store an assertion from outside the testing context.
*
* This is useful for inserting assertions that can only be recorded after
@@ -205,7 +223,8 @@
'file' => $caller['file'],
);
- return db_insert('simpletest')
+ return self::getDatabaseConnection()
+ ->insert('simpletest')
->fields($assertion)
->execute();
}
@@ -221,7 +240,8 @@
* @see DrupalTestCase::insertAssert()
*/
public static function deleteAssert($message_id) {
- return (bool) db_delete('simpletest')
+ return (bool) self::getDatabaseConnection()
+ ->delete('simpletest')
->condition('message_id', $message_id)
->execute();
}
@@ -435,10 +455,10 @@
}
/**
- * Logs verbose message in a text file.
+ * Logs a verbose message in a text file.
*
- * The a link to the vebose message will be placed in the test results via
- * as a passing assertion with the text '[verbose message]'.
+ * The link to the verbose message will be placed in the test results as a
+ * passing assertion with the text '[verbose message]'.
*
* @param $message
* The verbose message to be stored.
@@ -448,8 +468,11 @@
protected function verbose($message) {
if ($id = simpletest_verbose($message)) {
$class_safe = str_replace('\\', '_', get_class($this));
- $url = file_create_url($this->originalFileDirectory . '/simpletest/verbose/' . $class_safe . '-' . $id . '.html');
- $this->error(l(t('Verbose message'), $url, array('attributes' => array('target' => '_blank'))), 'User notice');
+ $url = $this->verboseDirectoryUrl . '/' . $class_safe . '-' . $id . '.html';
+ // Not using l() to avoid invoking the theme system, so that unit tests
+ // can use verbose() as well.
+ $link = '' . t('Verbose message') . '';
+ $this->error($link, 'User notice');
}
}
@@ -706,10 +729,17 @@
* method.
*/
protected function setUp() {
- global $conf;
+ global $conf, $language;
// Store necessary current values before switching to the test environment.
$this->originalFileDirectory = variable_get('file_public_path', conf_path() . '/files');
+ $this->verboseDirectoryUrl = file_create_url($this->originalFileDirectory . '/simpletest/verbose');
+
+ // Set up English language.
+ $this->originalLanguage = $language;
+ $this->originalLanguageDefault = variable_get('language_default');
+ unset($conf['language_default']);
+ $language = language_default();
// Reset all statics so that test is performed with a clean environment.
drupal_static_reset();
@@ -751,7 +781,7 @@
}
protected function tearDown() {
- global $conf;
+ global $conf, $language;
// Get back to the original connection.
Database::removeConnection('default');
@@ -762,6 +792,12 @@
if (isset($this->originalModuleList)) {
module_list(TRUE, FALSE, FALSE, $this->originalModuleList);
}
+
+ // Reset language.
+ $language = $this->originalLanguage;
+ if ($this->originalLanguageDefault) {
+ $GLOBALS['conf']['language_default'] = $this->originalLanguageDefault;
+ }
}
}
@@ -841,6 +877,13 @@
protected $cookieFile = NULL;
/**
+ * The cookies of the page currently loaded in the internal browser.
+ *
+ * @var array
+ */
+ protected $cookies = array();
+
+ /**
* Additional cURL options.
*
* DrupalWebTestCase itself never sets this but always obeys what is set.
@@ -929,7 +972,6 @@
protected function drupalCreateNode($settings = array()) {
// Populate defaults array.
$settings += array(
- 'body' => array(LANGUAGE_NONE => array(array())),
'title' => $this->randomName(8),
'comment' => 2,
'changed' => REQUEST_TIME,
@@ -944,6 +986,12 @@
'language' => LANGUAGE_NONE,
);
+ // Add the body after the language is defined so that it may be set
+ // properly.
+ $settings += array(
+ 'body' => array($settings['language'] => array(array())),
+ );
+
// Use the original node's created time for existing nodes.
if (isset($settings['created']) && !isset($settings['date'])) {
$settings['date'] = format_date($settings['created'], 'custom', 'Y-m-d H:i:s O');
@@ -1002,9 +1050,7 @@
'description' => '',
'help' => '',
'title_label' => 'Title',
- 'body_label' => 'Body',
'has_title' => 1,
- 'has_body' => 1,
);
// Imposed values for a custom type.
$forced = array(
@@ -1054,7 +1100,7 @@
$lines = array(16, 256, 1024, 2048, 20480);
$count = 0;
foreach ($lines as $line) {
- simpletest_generate_file('text-' . $count++, 64, $line);
+ simpletest_generate_file('text-' . $count++, 64, $line, 'text');
}
// Copy other test files from simpletest.
@@ -1145,7 +1191,7 @@
}
/**
- * Internal helper function; Create a role with specified permissions.
+ * Creates a role with specified permissions.
*
* @param $permissions
* Array of permission names to assign to role.
@@ -1351,12 +1397,14 @@
* @see DrupalWebTestCase::tearDown()
*/
protected function prepareEnvironment() {
- global $user, $language, $conf;
+ global $user, $language, $language_url, $conf;
// Store necessary current values before switching to prefixed database.
$this->originalLanguage = $language;
+ $this->originalLanguageUrl = $language_url;
$this->originalLanguageDefault = variable_get('language_default');
$this->originalFileDirectory = variable_get('file_public_path', conf_path() . '/files');
+ $this->verboseDirectoryUrl = file_create_url($this->originalFileDirectory . '/simpletest/verbose');
$this->originalProfile = drupal_get_profile();
$this->originalCleanUrl = variable_get('clean_url', 0);
$this->originalUser = $user;
@@ -1364,7 +1412,7 @@
// Set to English to prevent exceptions from utf8_truncate() from t()
// during install if the current language is not 'en'.
// The following array/object conversion is copied from language_default().
- $language = (object) array('language' => 'en', 'name' => 'English', 'native' => 'English', 'direction' => 0, 'enabled' => 1, 'plurals' => 0, 'formula' => '', 'domain' => '', 'prefix' => '', 'weight' => 0, 'javascript' => '');
+ $language_url = $language = (object) array('language' => 'en', 'name' => 'English', 'native' => 'English', 'direction' => 0, 'enabled' => 1, 'plurals' => 0, 'formula' => '', 'domain' => '', 'prefix' => '', 'weight' => 0, 'javascript' => '');
// Save and clean the shutdown callbacks array because it is static cached
// and will be changed by the test run. Otherwise it will contain callbacks
@@ -1422,7 +1470,7 @@
* @see DrupalWebTestCase::prepareEnvironment()
*/
protected function setUp() {
- global $user, $language, $conf;
+ global $user, $language, $language_url, $conf;
// Create the database prefix for this test.
$this->prepareDatabasePrefix();
@@ -1519,7 +1567,7 @@
// Set up English language.
unset($conf['language_default']);
- $language = language_default();
+ $language_url = $language = language_default();
// Use the test mail class instead of the default mail handler class.
variable_set('mail_system', array('default-system' => 'TestingMailSystem'));
@@ -1613,7 +1661,7 @@
* and reset the database prefix.
*/
protected function tearDown() {
- global $user, $language;
+ global $user, $language, $language_url;
// In case a fatal error occurred that was not in the test process read the
// log to pick up any fatal errors.
@@ -1678,12 +1726,15 @@
// Reset language.
$language = $this->originalLanguage;
+ $language_url = $this->originalLanguageUrl;
if ($this->originalLanguageDefault) {
$GLOBALS['conf']['language_default'] = $this->originalLanguageDefault;
}
- // Close the CURL handler.
+ // Close the CURL handler and reset the cookies array so test classes
+ // containing multiple tests are not polluted.
$this->curlClose();
+ $this->cookies = array();
}
/**
@@ -1756,14 +1807,24 @@
protected function curlExec($curl_options, $redirect = FALSE) {
$this->curlInitialize();
- // cURL incorrectly handles URLs with a fragment by including the
- // fragment in the request to the server, causing some web servers
- // to reject the request citing "400 - Bad Request". To prevent
- // this, we strip the fragment from the request.
- // TODO: Remove this for Drupal 8, since fixed in curl 7.20.0.
- if (!empty($curl_options[CURLOPT_URL]) && strpos($curl_options[CURLOPT_URL], '#')) {
- $original_url = $curl_options[CURLOPT_URL];
- $curl_options[CURLOPT_URL] = strtok($curl_options[CURLOPT_URL], '#');
+ if (!empty($curl_options[CURLOPT_URL])) {
+ // Forward XDebug activation if present.
+ if (isset($_COOKIE['XDEBUG_SESSION'])) {
+ $options = drupal_parse_url($curl_options[CURLOPT_URL]);
+ $options += array('query' => array());
+ $options['query'] += array('XDEBUG_SESSION_START' => $_COOKIE['XDEBUG_SESSION']);
+ $curl_options[CURLOPT_URL] = url($options['path'], $options);
+ }
+
+ // cURL incorrectly handles URLs with a fragment by including the
+ // fragment in the request to the server, causing some web servers
+ // to reject the request citing "400 - Bad Request". To prevent
+ // this, we strip the fragment from the request.
+ // TODO: Remove this for Drupal 8, since fixed in curl 7.20.0.
+ if (strpos($curl_options[CURLOPT_URL], '#')) {
+ $original_url = $curl_options[CURLOPT_URL];
+ $curl_options[CURLOPT_URL] = strtok($curl_options[CURLOPT_URL], '#');
+ }
}
$url = empty($curl_options[CURLOPT_URL]) ? curl_getinfo($this->curlHandle, CURLINFO_EFFECTIVE_URL) : $curl_options[CURLOPT_URL];
@@ -2198,6 +2259,7 @@
// Submit the POST request.
$return = drupal_json_decode($this->drupalPost(NULL, $edit, array('path' => $ajax_path, 'triggering_element' => $triggering_element), $options, $headers, $form_html_id, $extra_post));
+ $this->assertIdentical($this->drupalGetHeader('X-Drupal-Ajax-Token'), '1', 'Ajax response header found.');
// Change the page content by applying the returned commands.
if (!empty($ajax_settings) && !empty($return)) {
@@ -2234,8 +2296,13 @@
if ($wrapperNode) {
// ajax.js adds an enclosing DIV to work around a Safari bug.
$newDom = new DOMDocument();
+ // DOM can load HTML soup. But, HTML soup can throw warnings,
+ // suppress them.
$newDom->loadHTML('' . $command['data'] . '');
- $newNode = $dom->importNode($newDom->documentElement->firstChild->firstChild, TRUE);
+ // Suppress warnings thrown when duplicate HTML IDs are
+ // encountered. This probably means we are replacing an element
+ // with the same ID.
+ $newNode = @$dom->importNode($newDom->documentElement->firstChild->firstChild, TRUE);
$method = isset($command['method']) ? $command['method'] : $ajax_settings['method'];
// The "method" is a jQuery DOM manipulation function. Emulate
// each one using PHP's DOMNode API.
@@ -2269,6 +2336,13 @@
}
break;
+ case 'updateBuildId':
+ $buildId = $xpath->query('//input[@name="form_build_id" and @value="' . $command['old'] . '"]')->item(0);
+ if ($buildId) {
+ $buildId->setAttribute('value', $command['new']);
+ }
+ break;
+
// @todo Add suitable implementations for these commands in order to
// have full test coverage of what ajax.js can do.
case 'remove':
@@ -2281,12 +2355,22 @@
break;
case 'restripe':
break;
+ case 'add_css':
+ break;
}
}
$content = $dom->saveHTML();
}
$this->drupalSetContent($content);
$this->drupalSetSettings($drupal_settings);
+
+ $verbose = 'AJAX POST request to: ' . $path;
+ $verbose .= '
AJAX callback path: ' . $ajax_path;
+ $verbose .= '
Ending URL: ' . $this->getUrl();
+ $verbose .= '
' . $this->content;
+
+ $this->verbose($verbose);
+
return $return;
}
@@ -2540,6 +2624,11 @@
*
* @param $xpath
* The xpath string to use in the search.
+ * @param array $arguments
+ * An array of arguments with keys in the form ':name' matching the
+ * placeholders in the query. The values may be either strings or numeric
+ * values.
+ *
* @return
* The return value of the xpath search. For details on the xpath string
* format and return values see the SimpleXML documentation,
@@ -2609,8 +2698,6 @@
*
* @param $label
* Text between the anchor tags.
- * @param $index
- * Link position counting from zero.
* @param $message
* Message to display.
* @param $group
@@ -2669,28 +2756,26 @@
*
* Will click the first link found with this link text by default, or a later
* one if an index is given. Match is case sensitive with normalized space.
- * The label is translated label. There is an assert for successful click.
+ * The label is translated label.
+ *
+ * If the link is discovered and clicked, the test passes. Fail otherwise.
*
* @param $label
* Text between the anchor tags.
* @param $index
* Link position counting from zero.
* @return
- * Page on success, or FALSE on failure.
+ * Page contents on success, or FALSE on failure.
*/
protected function clickLink($label, $index = 0) {
$url_before = $this->getUrl();
$urls = $this->xpath('//a[normalize-space(text())=:label]', array(':label' => $label));
-
if (isset($urls[$index])) {
$url_target = $this->getAbsoluteUrl($urls[$index]['href']);
- }
-
- $this->assertTrue(isset($urls[$index]), t('Clicked link %label (@url_target) from @url_before', array('%label' => $label, '@url_target' => $url_target, '@url_before' => $url_before)), t('Browser'));
-
- if (isset($url_target)) {
+ $this->pass(t('Clicked link %label (@url_target) from @url_before', array('%label' => $label, '@url_target' => $url_target, '@url_before' => $url_before)), 'Browser');
return $this->drupalGet($url_target);
}
+ $this->fail(t('Link %label does not exist on @url_before', array('%label' => $label, '@url_before' => $url_before)), 'Browser');
return FALSE;
}
@@ -2715,7 +2800,7 @@
$path = substr($path, $length);
}
// Ensure that we have an absolute path.
- if ($path[0] !== '/') {
+ if (empty($path) || $path[0] !== '/') {
$path = '/' . $path;
}
// Finally, prepend the $base_url.
@@ -2927,7 +3012,7 @@
if (!$message) {
$message = t('Raw "@raw" found', array('@raw' => $raw));
}
- return $this->assert(strpos($this->drupalGetContent(), $raw) !== FALSE, $message, $group);
+ return $this->assert(strpos($this->drupalGetContent(), (string) $raw) !== FALSE, $message, $group);
}
/**
@@ -2947,7 +3032,7 @@
if (!$message) {
$message = t('Raw "@raw" not found', array('@raw' => $raw));
}
- return $this->assert(strpos($this->drupalGetContent(), $raw) === FALSE, $message, $group);
+ return $this->assert(strpos($this->drupalGetContent(), (string) $raw) === FALSE, $message, $group);
}
/**
@@ -3174,7 +3259,7 @@
* @param $callback
* The name of the theme function to invoke; e.g. 'links' for theme_links().
* @param $variables
- * An array of variables to pass to the theme function.
+ * (optional) An array of variables to pass to the theme function.
* @param $expected
* The expected themed output string.
* @param $message
@@ -3210,7 +3295,9 @@
* @param $xpath
* XPath used to find the field.
* @param $value
- * (optional) Value of the field to assert.
+ * (optional) Value of the field to assert. You may pass in NULL (default)
+ * to skip checking the actual value, while still checking that the field
+ * exists.
* @param $message
* (optional) Message to display.
* @param $group
@@ -3278,12 +3365,14 @@
}
/**
- * Asserts that a field does not exist in the current page by the given XPath.
+ * Asserts that a field doesn't exist or its value doesn't match, by XPath.
*
* @param $xpath
* XPath used to find the field.
* @param $value
- * (optional) Value of the field to assert.
+ * (optional) Value for the field, to assert that the field's value on the
+ * page doesn't match it. You may pass in NULL to skip checking the
+ * value, while still checking that the field doesn't exist.
* @param $message
* (optional) Message to display.
* @param $group
@@ -3316,7 +3405,9 @@
* @param $name
* Name of field to assert.
* @param $value
- * Value of the field to assert.
+ * (optional) Value of the field to assert. You may pass in NULL (default)
+ * to skip checking the actual value, while still checking that the field
+ * exists.
* @param $message
* Message to display.
* @param $group
@@ -3347,9 +3438,12 @@
* @param $name
* Name of field to assert.
* @param $value
- * Value of the field to assert.
+ * (optional) Value for the field, to assert that the field's value on the
+ * page doesn't match it. You may pass in NULL to skip checking the
+ * value, while still checking that the field doesn't exist. However, the
+ * default value ('') asserts that the field value is not an empty string.
* @param $message
- * Message to display.
+ * (optional) Message to display.
* @param $group
* The group this message belongs to.
* @return
@@ -3360,14 +3454,17 @@
}
/**
- * Asserts that a field exists in the current page with the given id and value.
+ * Asserts that a field exists in the current page with the given ID and value.
*
* @param $id
- * Id of field to assert.
+ * ID of field to assert.
* @param $value
- * Value of the field to assert.
+ * (optional) Value for the field to assert. You may pass in NULL to skip
+ * checking the value, while still checking that the field exists.
+ * However, the default value ('') asserts that the field value is an empty
+ * string.
* @param $message
- * Message to display.
+ * (optional) Message to display.
* @param $group
* The group this message belongs to.
* @return
@@ -3378,14 +3475,17 @@
}
/**
- * Asserts that a field does not exist with the given id and value.
+ * Asserts that a field does not exist with the given ID and value.
*
* @param $id
- * Id of field to assert.
+ * ID of field to assert.
* @param $value
- * Value of the field to assert.
+ * (optional) Value for the field, to assert that the field's value on the
+ * page doesn't match it. You may pass in NULL to skip checking the value,
+ * while still checking that the field doesn't exist. However, the default
+ * value ('') asserts that the field value is not an empty string.
* @param $message
- * Message to display.
+ * (optional) Message to display.
* @param $group
* The group this message belongs to.
* @return
@@ -3399,9 +3499,9 @@
* Asserts that a checkbox field in the current page is checked.
*
* @param $id
- * Id of field to assert.
+ * ID of field to assert.
* @param $message
- * Message to display.
+ * (optional) Message to display.
* @return
* TRUE on pass, FALSE on fail.
*/
@@ -3414,9 +3514,9 @@
* Asserts that a checkbox field in the current page is not checked.
*
* @param $id
- * Id of field to assert.
+ * ID of field to assert.
* @param $message
- * Message to display.
+ * (optional) Message to display.
* @return
* TRUE on pass, FALSE on fail.
*/
@@ -3429,11 +3529,11 @@
* Asserts that a select option in the current page is checked.
*
* @param $id
- * Id of select field to assert.
+ * ID of select field to assert.
* @param $option
* Option to assert.
* @param $message
- * Message to display.
+ * (optional) Message to display.
* @return
* TRUE on pass, FALSE on fail.
*
@@ -3448,11 +3548,11 @@
* Asserts that a select option in the current page is not checked.
*
* @param $id
- * Id of select field to assert.
+ * ID of select field to assert.
* @param $option
* Option to assert.
* @param $message
- * Message to display.
+ * (optional) Message to display.
* @return
* TRUE on pass, FALSE on fail.
*/
@@ -3462,12 +3562,12 @@
}
/**
- * Asserts that a field exists with the given name or id.
+ * Asserts that a field exists with the given name or ID.
*
* @param $field
- * Name or id of field to assert.
+ * Name or ID of field to assert.
* @param $message
- * Message to display.
+ * (optional) Message to display.
* @param $group
* The group this message belongs to.
* @return
@@ -3478,12 +3578,12 @@
}
/**
- * Asserts that a field does not exist with the given name or id.
+ * Asserts that a field does not exist with the given name or ID.
*
* @param $field
- * Name or id of field to assert.
+ * Name or ID of field to assert.
* @param $message
- * Message to display.
+ * (optional) Message to display.
* @param $group
* The group this message belongs to.
* @return
diff -Naur drupal-7.25/modules/simpletest/files/css_test_files/css_input_with_import.css drupal-7.66/modules/simpletest/files/css_test_files/css_input_with_import.css
--- drupal-7.25/modules/simpletest/files/css_test_files/css_input_with_import.css 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/simpletest/files/css_test_files/css_input_with_import.css 2019-04-17 22:20:46.000000000 +0200
@@ -1,5 +1,7 @@
+@import url("http://example.com/style.css");
+@import url("//example.com/style.css");
@import "import1.css";
@import "import2.css";
diff -Naur drupal-7.25/modules/simpletest/files/css_test_files/css_input_with_import.css.optimized.css drupal-7.66/modules/simpletest/files/css_test_files/css_input_with_import.css.optimized.css
--- drupal-7.25/modules/simpletest/files/css_test_files/css_input_with_import.css.optimized.css 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/simpletest/files/css_test_files/css_input_with_import.css.optimized.css 2019-04-17 22:20:46.000000000 +0200
@@ -1,4 +1,4 @@
-ul,select{font:1em/160% Verdana,sans-serif;color:#494949;}.ui-icon{background-image:url(images/icon.png);}
+@import url("http://example.com/style.css");@import url("//example.com/style.css");ul,select{font:1em/160% Verdana,sans-serif;color:#494949;}.ui-icon{background-image:url(images/icon.png);}.data .double-quote{background-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");}.data .single-quote{background-image:url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACAWGBwYFCAcGhwkIiAmMFA0MCwsMGJGSjpQdGZ6eHJmcG6AkLicgIiuim5woNqirr7EztDOfJri8uDI8LjKzsb/2wBDASIkJDAqMF40NF7GhHCExsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsb/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AKAAH//Z');}.data .no-quote{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAEAQAAAAAo/mtHAAAAIElEQVQIHWMRnWHwcRNLN8NZ7QYWwT8PlBlYsgqVBRsAankIMw5MtnoAAAAASUVORK5CYII=);}
p,select{font:1em/160% Verdana,sans-serif;color:#494949;}
body{margin:0;padding:0;background:#edf5fa;font:76%/170% Verdana,sans-serif;color:#494949;}.this .is .a .test{font:1em/100% Verdana,sans-serif;color:#494949;}.this
.is
diff -Naur drupal-7.25/modules/simpletest/files/css_test_files/css_input_with_import.css.unoptimized.css drupal-7.66/modules/simpletest/files/css_test_files/css_input_with_import.css.unoptimized.css
--- drupal-7.25/modules/simpletest/files/css_test_files/css_input_with_import.css.unoptimized.css 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/simpletest/files/css_test_files/css_input_with_import.css.unoptimized.css 2019-04-17 22:20:46.000000000 +0200
@@ -1,5 +1,7 @@
+@import url("http://example.com/style.css");
+@import url("//example.com/style.css");
ul, select {
font: 1em/160% Verdana, sans-serif;
@@ -7,6 +9,21 @@
}
.ui-icon{background-image: url(images/icon.png);}
+/* Test data URI images with different quote styles. */
+.data .double-quote {
+ /* http://stackoverflow.com/a/13139830/11023 */
+ background-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");
+}
+
+.data .single-quote {
+ background-image: url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACAWGBwYFCAcGhwkIiAmMFA0MCwsMGJGSjpQdGZ6eHJmcG6AkLicgIiuim5woNqirr7EztDOfJri8uDI8LjKzsb/2wBDASIkJDAqMF40NF7GhHCExsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsb/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AKAAH//Z');
+}
+
+.data .no-quote {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAEAQAAAAAo/mtHAAAAIElEQVQIHWMRnWHwcRNLN8NZ7QYWwT8PlBlYsgqVBRsAankIMw5MtnoAAAAASUVORK5CYII=);
+}
+
+
p, select {
font: 1em/160% Verdana, sans-serif;
color: #494949;
diff -Naur drupal-7.25/modules/simpletest/files/css_test_files/css_subfolder/css_input_with_import.css.optimized.css drupal-7.66/modules/simpletest/files/css_test_files/css_subfolder/css_input_with_import.css.optimized.css
--- drupal-7.25/modules/simpletest/files/css_test_files/css_subfolder/css_input_with_import.css.optimized.css 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/simpletest/files/css_test_files/css_subfolder/css_input_with_import.css.optimized.css 2019-04-17 22:20:46.000000000 +0200
@@ -1,4 +1,4 @@
-ul,select{font:1em/160% Verdana,sans-serif;color:#494949;}.ui-icon{background-image:url(../images/icon.png);}
+ul,select{font:1em/160% Verdana,sans-serif;color:#494949;}.ui-icon{background-image:url(../images/icon.png);}.data .double-quote{background-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");}.data .single-quote{background-image:url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACAWGBwYFCAcGhwkIiAmMFA0MCwsMGJGSjpQdGZ6eHJmcG6AkLicgIiuim5woNqirr7EztDOfJri8uDI8LjKzsb/2wBDASIkJDAqMF40NF7GhHCExsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsb/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AKAAH//Z');}.data .no-quote{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAEAQAAAAAo/mtHAAAAIElEQVQIHWMRnWHwcRNLN8NZ7QYWwT8PlBlYsgqVBRsAankIMw5MtnoAAAAASUVORK5CYII=);}
p,select{font:1em/160% Verdana,sans-serif;color:#494949;}
body{margin:0;padding:0;background:#edf5fa;font:76%/170% Verdana,sans-serif;color:#494949;}.this .is .a .test{font:1em/100% Verdana,sans-serif;color:#494949;}.this
.is
diff -Naur drupal-7.25/modules/simpletest/files/css_test_files/css_subfolder/css_input_with_import.css.unoptimized.css drupal-7.66/modules/simpletest/files/css_test_files/css_subfolder/css_input_with_import.css.unoptimized.css
--- drupal-7.25/modules/simpletest/files/css_test_files/css_subfolder/css_input_with_import.css.unoptimized.css 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/simpletest/files/css_test_files/css_subfolder/css_input_with_import.css.unoptimized.css 2019-04-17 22:20:46.000000000 +0200
@@ -7,6 +7,21 @@
}
.ui-icon{background-image: url(../images/icon.png);}
+/* Test data URI images with different quote styles. */
+.data .double-quote {
+ /* http://stackoverflow.com/a/13139830/11023 */
+ background-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");
+}
+
+.data .single-quote {
+ background-image: url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACAWGBwYFCAcGhwkIiAmMFA0MCwsMGJGSjpQdGZ6eHJmcG6AkLicgIiuim5woNqirr7EztDOfJri8uDI8LjKzsb/2wBDASIkJDAqMF40NF7GhHCExsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsb/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AKAAH//Z');
+}
+
+.data .no-quote {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAEAQAAAAAo/mtHAAAAIElEQVQIHWMRnWHwcRNLN8NZ7QYWwT8PlBlYsgqVBRsAankIMw5MtnoAAAAASUVORK5CYII=);
+}
+
+
p, select {
font: 1em/160% Verdana, sans-serif;
color: #494949;
diff -Naur drupal-7.25/modules/simpletest/files/css_test_files/import1.css drupal-7.66/modules/simpletest/files/css_test_files/import1.css
--- drupal-7.25/modules/simpletest/files/css_test_files/import1.css 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/simpletest/files/css_test_files/import1.css 2019-04-17 22:20:46.000000000 +0200
@@ -3,4 +3,18 @@
font: 1em/160% Verdana, sans-serif;
color: #494949;
}
-.ui-icon{background-image: url(images/icon.png);}
\ No newline at end of file
+.ui-icon{background-image: url(images/icon.png);}
+
+/* Test data URI images with different quote styles. */
+.data .double-quote {
+ /* http://stackoverflow.com/a/13139830/11023 */
+ background-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");
+}
+
+.data .single-quote {
+ background-image: url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACAWGBwYFCAcGhwkIiAmMFA0MCwsMGJGSjpQdGZ6eHJmcG6AkLicgIiuim5woNqirr7EztDOfJri8uDI8LjKzsb/2wBDASIkJDAqMF40NF7GhHCExsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsb/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AKAAH//Z');
+}
+
+.data .no-quote {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAEAQAAAAAo/mtHAAAAIElEQVQIHWMRnWHwcRNLN8NZ7QYWwT8PlBlYsgqVBRsAankIMw5MtnoAAAAASUVORK5CYII=);
+}
diff -Naur drupal-7.25/modules/simpletest/files/image-test-no-transparency.gif drupal-7.66/modules/simpletest/files/image-test-no-transparency.gif
--- drupal-7.25/modules/simpletest/files/image-test-no-transparency.gif 1970-01-01 01:00:00.000000000 +0100
+++ drupal-7.66/modules/simpletest/files/image-test-no-transparency.gif 2019-04-17 22:20:46.000000000 +0200
@@ -0,0 +1 @@
+GIF89a( , ( 8 *L(C.HCV"ČC1b(S\YŌI%B0Oh N2w^icΠk|x̤<:$@*V5 Q ]fulح_{U&[iϲ-nBsݚ=7l^,+wHx
8bƅ?6 ;
\ No newline at end of file
diff -Naur drupal-7.25/modules/simpletest/files/image-test-transparent-out-of-range.gif drupal-7.66/modules/simpletest/files/image-test-transparent-out-of-range.gif
--- drupal-7.25/modules/simpletest/files/image-test-transparent-out-of-range.gif 1970-01-01 01:00:00.000000000 +0100
+++ drupal-7.66/modules/simpletest/files/image-test-transparent-out-of-range.gif 2019-04-17 22:20:46.000000000 +0200
@@ -0,0 +1,2 @@
+GIF89a( ! , ( |80
lݵҍԇd
+gp,E
Tl
{ Q1|ĩ^ޮ`L0er^`7G{vnoyiV40zk*$ar ;
\ No newline at end of file
diff -Naur drupal-7.25/modules/simpletest/files/phar-1.phar drupal-7.66/modules/simpletest/files/phar-1.phar
--- drupal-7.25/modules/simpletest/files/phar-1.phar 1970-01-01 01:00:00.000000000 +0100
+++ drupal-7.66/modules/simpletest/files/phar-1.phar 2019-04-17 22:20:46.000000000 +0200
@@ -0,0 +1,301 @@
+ 2,
+'c' => 'text/plain',
+'cc' => 'text/plain',
+'cpp' => 'text/plain',
+'c++' => 'text/plain',
+'dtd' => 'text/plain',
+'h' => 'text/plain',
+'log' => 'text/plain',
+'rng' => 'text/plain',
+'txt' => 'text/plain',
+'xsd' => 'text/plain',
+'php' => 1,
+'inc' => 1,
+'avi' => 'video/avi',
+'bmp' => 'image/bmp',
+'css' => 'text/css',
+'gif' => 'image/gif',
+'htm' => 'text/html',
+'html' => 'text/html',
+'htmls' => 'text/html',
+'ico' => 'image/x-ico',
+'jpe' => 'image/jpeg',
+'jpg' => 'image/jpeg',
+'jpeg' => 'image/jpeg',
+'js' => 'application/x-javascript',
+'midi' => 'audio/midi',
+'mid' => 'audio/midi',
+'mod' => 'audio/mod',
+'mov' => 'movie/quicktime',
+'mp3' => 'audio/mp3',
+'mpg' => 'video/mpeg',
+'mpeg' => 'video/mpeg',
+'pdf' => 'application/pdf',
+'png' => 'image/png',
+'swf' => 'application/shockwave-flash',
+'tif' => 'image/tiff',
+'tiff' => 'image/tiff',
+'wav' => 'audio/wav',
+'xbm' => 'image/xbm',
+'xml' => 'text/xml',
+);
+
+header("Cache-Control: no-cache, must-revalidate");
+header("Pragma: no-cache");
+
+$basename = basename(__FILE__);
+if (!strpos($_SERVER['REQUEST_URI'], $basename)) {
+chdir(Extract_Phar::$temp);
+include $web;
+return;
+}
+$pt = substr($_SERVER['REQUEST_URI'], strpos($_SERVER['REQUEST_URI'], $basename) + strlen($basename));
+if (!$pt || $pt == '/') {
+$pt = $web;
+header('HTTP/1.1 301 Moved Permanently');
+header('Location: ' . $_SERVER['REQUEST_URI'] . '/' . $pt);
+exit;
+}
+$a = realpath(Extract_Phar::$temp . DIRECTORY_SEPARATOR . $pt);
+if (!$a || strlen(dirname($a)) < strlen(Extract_Phar::$temp)) {
+header('HTTP/1.0 404 Not Found');
+echo "\n \n File Not Found\n \n \n 404 - File Not Found
\n \n";
+exit;
+}
+$b = pathinfo($a);
+if (!isset($b['extension'])) {
+header('Content-Type: text/plain');
+header('Content-Length: ' . filesize($a));
+readfile($a);
+exit;
+}
+if (isset($mimes[$b['extension']])) {
+if ($mimes[$b['extension']] === 1) {
+include $a;
+exit;
+}
+if ($mimes[$b['extension']] === 2) {
+highlight_file($a);
+exit;
+}
+header('Content-Type: ' .$mimes[$b['extension']]);
+header('Content-Length: ' . filesize($a));
+readfile($a);
+exit;
+}
+}
+
+class Extract_Phar
+{
+static $temp;
+static $origdir;
+const GZ = 0x1000;
+const BZ2 = 0x2000;
+const MASK = 0x3000;
+const START = 'index.php';
+const LEN = 6643;
+
+static function go($return = false)
+{
+$fp = fopen(__FILE__, 'rb');
+fseek($fp, self::LEN);
+$L = unpack('V', $a = fread($fp, 4));
+$m = '';
+
+do {
+$read = 8192;
+if ($L[1] - strlen($m) < 8192) {
+$read = $L[1] - strlen($m);
+}
+$last = fread($fp, $read);
+$m .= $last;
+} while (strlen($last) && strlen($m) < $L[1]);
+
+if (strlen($m) < $L[1]) {
+die('ERROR: manifest length read was "' .
+strlen($m) .'" should be "' .
+$L[1] . '"');
+}
+
+$info = self::_unpack($m);
+$f = $info['c'];
+
+if ($f & self::GZ) {
+if (!function_exists('gzinflate')) {
+die('Error: zlib extension is not enabled -' .
+' gzinflate() function needed for zlib-compressed .phars');
+}
+}
+
+if ($f & self::BZ2) {
+if (!function_exists('bzdecompress')) {
+die('Error: bzip2 extension is not enabled -' .
+' bzdecompress() function needed for bz2-compressed .phars');
+}
+}
+
+$temp = self::tmpdir();
+
+if (!$temp || !is_writable($temp)) {
+$sessionpath = session_save_path();
+if (strpos ($sessionpath, ";") !== false)
+$sessionpath = substr ($sessionpath, strpos ($sessionpath, ";")+1);
+if (!file_exists($sessionpath) || !is_dir($sessionpath)) {
+die('Could not locate temporary directory to extract phar');
+}
+$temp = $sessionpath;
+}
+
+$temp .= '/pharextract/'.basename(__FILE__, '.phar');
+self::$temp = $temp;
+self::$origdir = getcwd();
+@mkdir($temp, 0777, true);
+$temp = realpath($temp);
+
+if (!file_exists($temp . DIRECTORY_SEPARATOR . md5_file(__FILE__))) {
+self::_removeTmpFiles($temp, getcwd());
+@mkdir($temp, 0777, true);
+@file_put_contents($temp . '/' . md5_file(__FILE__), '');
+
+foreach ($info['m'] as $path => $file) {
+$a = !file_exists(dirname($temp . '/' . $path));
+@mkdir(dirname($temp . '/' . $path), 0777, true);
+clearstatcache();
+
+if ($path[strlen($path) - 1] == '/') {
+@mkdir($temp . '/' . $path, 0777);
+} else {
+file_put_contents($temp . '/' . $path, self::extractFile($path, $file, $fp));
+@chmod($temp . '/' . $path, 0666);
+}
+}
+}
+
+chdir($temp);
+
+if (!$return) {
+include self::START;
+}
+}
+
+static function tmpdir()
+{
+if (strpos(PHP_OS, 'WIN') !== false) {
+if ($var = getenv('TMP') ? getenv('TMP') : getenv('TEMP')) {
+return $var;
+}
+if (is_dir('/temp') || mkdir('/temp')) {
+return realpath('/temp');
+}
+return false;
+}
+if ($var = getenv('TMPDIR')) {
+return $var;
+}
+return realpath('/tmp');
+}
+
+static function _unpack($m)
+{
+$info = unpack('V', substr($m, 0, 4));
+ $l = unpack('V', substr($m, 10, 4));
+$m = substr($m, 14 + $l[1]);
+$s = unpack('V', substr($m, 0, 4));
+$o = 0;
+$start = 4 + $s[1];
+$ret['c'] = 0;
+
+for ($i = 0; $i < $info[1]; $i++) {
+ $len = unpack('V', substr($m, $start, 4));
+$start += 4;
+ $savepath = substr($m, $start, $len[1]);
+$start += $len[1];
+ $ret['m'][$savepath] = array_values(unpack('Va/Vb/Vc/Vd/Ve/Vf', substr($m, $start, 24)));
+$ret['m'][$savepath][3] = sprintf('%u', $ret['m'][$savepath][3]
+& 0xffffffff);
+$ret['m'][$savepath][7] = $o;
+$o += $ret['m'][$savepath][2];
+$start += 24 + $ret['m'][$savepath][5];
+$ret['c'] |= $ret['m'][$savepath][4] & self::MASK;
+}
+return $ret;
+}
+
+static function extractFile($path, $entry, $fp)
+{
+$data = '';
+$c = $entry[2];
+
+while ($c) {
+if ($c < 8192) {
+$data .= @fread($fp, $c);
+$c = 0;
+} else {
+$c -= 8192;
+$data .= @fread($fp, 8192);
+}
+}
+
+if ($entry[4] & self::GZ) {
+$data = gzinflate($data);
+} elseif ($entry[4] & self::BZ2) {
+$data = bzdecompress($data);
+}
+
+if (strlen($data) != $entry[0]) {
+die("Invalid internal .phar file (size error " . strlen($data) . " != " .
+$stat[7] . ")");
+}
+
+if ($entry[3] != sprintf("%u", crc32($data) & 0xffffffff)) {
+die("Invalid internal .phar file (checksum error)");
+}
+
+return $data;
+}
+
+static function _removeTmpFiles($temp, $origdir)
+{
+chdir($temp);
+
+foreach (glob('*') as $f) {
+if (file_exists($f)) {
+is_dir($f) ? @rmdir($f) : @unlink($f);
+if (file_exists($f) && is_dir($f)) {
+self::_removeTmpFiles($f, getcwd());
+}
+}
+}
+
+@rmdir($temp);
+clearstatcache();
+chdir($origdir);
+}
+}
+
+Extract_Phar::go();
+__HALT_COMPILER(); ?>7 index.php 8![ u $elapsed)));
@@ -328,25 +331,32 @@
// Also discover PSR-0 test classes, if the PHP version allows it.
if (version_compare(PHP_VERSION, '5.3') > 0) {
- // Select all PSR-0 classes in the Tests namespace of all modules.
+ // Select all PSR-0 and PSR-4 classes in the Tests namespace of all
+ // modules.
$system_list = db_query("SELECT name, filename FROM {system}")->fetchAllKeyed();
foreach ($system_list as $name => $filename) {
- // Build directory in which the test files would reside.
- $tests_dir = DRUPAL_ROOT . '/' . dirname($filename) . '/lib/Drupal/' . $name . '/Tests';
- // Scan it for test files if it exists.
- if (is_dir($tests_dir)) {
- $files = file_scan_directory($tests_dir, '/.*\.php/');
- if (!empty($files)) {
- $basedir = DRUPAL_ROOT . '/' . dirname($filename) . '/lib/';
- foreach ($files as $file) {
- // Convert the file name into the namespaced class name.
- $replacements = array(
- '/' => '\\',
- $basedir => '',
- '.php' => '',
- );
- $classes[] = strtr($file->uri, $replacements);
+ $module_dir = DRUPAL_ROOT . '/' . dirname($filename);
+ // Search both the 'lib/Drupal/mymodule' directory (for PSR-0 classes)
+ // and the 'src' directory (for PSR-4 classes).
+ foreach(array('lib/Drupal/' . $name, 'src') as $subdir) {
+ // Build directory in which the test files would reside.
+ $tests_dir = $module_dir . '/' . $subdir . '/Tests';
+ // Scan it for test files if it exists.
+ if (is_dir($tests_dir)) {
+ $files = file_scan_directory($tests_dir, '/.*\.php/');
+ if (!empty($files)) {
+ foreach ($files as $file) {
+ // Convert the file name into the namespaced class name.
+ $replacements = array(
+ '/' => '\\',
+ $module_dir . '/' => '',
+ 'lib/' => '',
+ 'src/' => 'Drupal\\' . $name . '\\',
+ '.php' => '',
+ );
+ $classes[] = strtr($file->uri, $replacements);
+ }
}
}
}
@@ -364,7 +374,10 @@
// If this test class requires a non-existing module, skip it.
if (!empty($info['dependencies'])) {
foreach ($info['dependencies'] as $module) {
- if (!drupal_get_filename('module', $module)) {
+ // Pass FALSE as fourth argument so no error gets created for
+ // the missing file.
+ $found_module = drupal_get_filename('module', $module, NULL, FALSE);
+ if (!$found_module) {
continue 2;
}
}
@@ -406,17 +419,20 @@
// Only register PSR-0 class loading if we are on PHP 5.3 or higher.
if (version_compare(PHP_VERSION, '5.3') > 0) {
- spl_autoload_register('_simpletest_autoload_psr0');
+ spl_autoload_register('_simpletest_autoload_psr4_psr0');
}
}
/**
- * Autoload callback to find PSR-0 test classes.
+ * Autoload callback to find PSR-4 and PSR-0 test classes.
+ *
+ * Looks in the 'src/Tests' and in the 'lib/Drupal/mymodule/Tests' directory of
+ * modules for the class.
*
* This will only work on classes where the namespace is of the pattern
* "Drupal\$extension\Tests\.."
*/
-function _simpletest_autoload_psr0($class) {
+function _simpletest_autoload_psr4_psr0($class) {
// Static cache for extension paths.
// This cache is lazily filled as soon as it is needed.
@@ -446,14 +462,26 @@
$namespace = substr($class, 0, $nspos);
$classname = substr($class, $nspos + 1);
- // Build the filepath where we expect the class to be defined.
- $path = dirname($extensions[$extension]) . '/lib/' .
- str_replace('\\', '/', $namespace) . '/' .
+ // Try the PSR-4 location first, and the PSR-0 location as a fallback.
+ // Build the PSR-4 filepath where we expect the class to be defined.
+ $psr4_path = dirname($extensions[$extension]) . '/src/' .
+ str_replace('\\', '/', substr($namespace, strlen('Drupal\\' . $extension . '\\'))) . '/' .
str_replace('_', '/', $classname) . '.php';
// Include the file, if it does exist.
- if (file_exists($path)) {
- include $path;
+ if (file_exists($psr4_path)) {
+ include $psr4_path;
+ }
+ else {
+ // Build the PSR-0 filepath where we expect the class to be defined.
+ $psr0_path = dirname($extensions[$extension]) . '/lib/' .
+ str_replace('\\', '/', $namespace) . '/' .
+ str_replace('_', '/', $classname) . '.php';
+
+ // Include the file, if it does exist.
+ if (file_exists($psr0_path)) {
+ include $psr0_path;
+ }
}
}
}
@@ -487,25 +515,25 @@
* Generate test file.
*/
function simpletest_generate_file($filename, $width, $lines, $type = 'binary-text') {
- $size = $width * $lines - $lines;
-
- // Generate random text
$text = '';
- for ($i = 0; $i < $size; $i++) {
- switch ($type) {
- case 'text':
- $text .= chr(rand(32, 126));
- break;
- case 'binary':
- $text .= chr(rand(0, 31));
- break;
- case 'binary-text':
- default:
- $text .= rand(0, 1);
- break;
+ for ($i = 0; $i < $lines; $i++) {
+ // Generate $width - 1 characters to leave space for the "\n" character.
+ for ($j = 0; $j < $width - 1; $j++) {
+ switch ($type) {
+ case 'text':
+ $text .= chr(rand(32, 126));
+ break;
+ case 'binary':
+ $text .= chr(rand(0, 31));
+ break;
+ case 'binary-text':
+ default:
+ $text .= rand(0, 1);
+ break;
+ }
}
+ $text .= "\n";
}
- $text = wordwrap($text, $width - 1, "\n", TRUE) . "\n"; // Add \n for symmetrical file.
// Create filename.
file_put_contents('public://' . $filename . '.txt', $text);
diff -Naur drupal-7.25/modules/simpletest/simpletest.test drupal-7.66/modules/simpletest/simpletest.test
--- drupal-7.25/modules/simpletest/simpletest.test 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/simpletest/simpletest.test 2019-04-17 22:20:46.000000000 +0200
@@ -322,6 +322,14 @@
* Test internal testing framework browser.
*/
class SimpleTestBrowserTestCase extends DrupalWebTestCase {
+
+ /**
+ * A flag indicating whether a cookie has been set in a test.
+ *
+ * @var bool
+ */
+ protected static $cookieSet = FALSE;
+
public static function getInfo() {
return array(
'name' => 'SimpleTest browser',
@@ -380,6 +388,46 @@
$urls = $this->xpath('//a[text()=:text]', array(':text' => 'A second "even more weird" link, in memory of George O\'Malley'));
$this->assertEqual($urls[0]['href'], 'link2', 'Match with mixed single and double quotes.');
}
+
+ /**
+ * Tests that cookies set during a request are available for testing.
+ */
+ public function testCookies() {
+ // Check that the $this->cookies property is populated when a user logs in.
+ $user = $this->drupalCreateUser();
+ $edit = array('name' => $user->name, 'pass' => $user->pass_raw);
+ $this->drupalPost('', $edit, t('Log in'));
+ $this->assertEqual(count($this->cookies), 1, 'A cookie is set when the user logs in.');
+
+ // Check that the name and value of the cookie match the request data.
+ $cookie_header = $this->drupalGetHeader('set-cookie', TRUE);
+
+ // The name and value are located at the start of the string, separated by
+ // an equals sign and ending in a semicolon.
+ preg_match('/^([^=]+)=([^;]+)/', $cookie_header, $matches);
+ $name = $matches[1];
+ $value = $matches[2];
+
+ $this->assertTrue(array_key_exists($name, $this->cookies), 'The cookie name is correct.');
+ $this->assertEqual($value, $this->cookies[$name]['value'], 'The cookie value is correct.');
+
+ // Set a flag indicating that a cookie has been set in this test.
+ // @see SimpleTestBrowserTestCase::testCookieDoesNotBleed().
+ self::$cookieSet = TRUE;
+ }
+
+ /**
+ * Tests that the cookies from a previous test do not bleed into a new test.
+ *
+ * @see SimpleTestBrowserTestCase::testCookies().
+ */
+ public function testCookieDoesNotBleed() {
+ // In order for this test to be effective it should always run after the
+ // testCookies() test.
+ $this->assertTrue(self::$cookieSet, 'Tests have been executed in the expected order.');
+ $this->assertEqual(count($this->cookies), 0, 'No cookies are present at the start of a new test.');
+ }
+
}
class SimpleTestMailCaptureTestCase extends DrupalWebTestCase {
@@ -703,7 +751,9 @@
$classes_all = simpletest_test_get_all();
foreach (array(
'Drupal\\simpletest\\Tests\\PSR0WebTest',
+ 'Drupal\\simpletest\\Tests\\PSR4WebTest',
'Drupal\\psr_0_test\\Tests\\ExampleTest',
+ 'Drupal\\psr_4_test\\Tests\\ExampleTest',
) as $class) {
$this->assert(!empty($classes_all['SimpleTest'][$class]), t('Class @class must be discovered by simpletest_test_get_all().', array('@class' => $class)));
}
@@ -726,15 +776,20 @@
// Don't expect PSR-0 tests to be discovered on older PHP versions.
return;
}
- // This one is provided by simpletest itself via PSR-0.
+ // These are provided by simpletest itself via PSR-0 and PSR-4.
$this->assertText('PSR0 web test');
+ $this->assertText('PSR4 web test');
$this->assertText('PSR0 example test: PSR-0 in disabled modules.');
+ $this->assertText('PSR4 example test: PSR-4 in disabled modules.');
$this->assertText('PSR0 example test: PSR-0 in nested subfolders.');
+ $this->assertText('PSR4 example test: PSR-4 in nested subfolders.');
// Test each test individually.
foreach (array(
'Drupal\\psr_0_test\\Tests\\ExampleTest',
'Drupal\\psr_0_test\\Tests\\Nested\\NestedExampleTest',
+ 'Drupal\\psr_4_test\\Tests\\ExampleTest',
+ 'Drupal\\psr_4_test\\Tests\\Nested\\NestedExampleTest',
) as $class) {
$this->drupalGet('admin/config/development/testing');
$edit = array($class => TRUE);
diff -Naur drupal-7.25/modules/simpletest/src/Tests/PSR4WebTest.php drupal-7.66/modules/simpletest/src/Tests/PSR4WebTest.php
--- drupal-7.25/modules/simpletest/src/Tests/PSR4WebTest.php 1970-01-01 01:00:00.000000000 +0100
+++ drupal-7.66/modules/simpletest/src/Tests/PSR4WebTest.php 2019-04-17 22:20:46.000000000 +0200
@@ -0,0 +1,18 @@
+ 'PSR4 web test',
+ 'description' => 'We want to assert that this PSR-4 test case is being discovered.',
+ 'group' => 'SimpleTest',
+ );
+ }
+
+ function testArithmetics() {
+ $this->assert(1 + 1 == 2, '1 + 1 == 2');
+ }
+}
diff -Naur drupal-7.25/modules/simpletest/tests/actions_loop_test.info drupal-7.66/modules/simpletest/tests/actions_loop_test.info
--- drupal-7.25/modules/simpletest/tests/actions_loop_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/actions_loop_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
core = 7.x
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/ajax.test drupal-7.66/modules/simpletest/tests/ajax.test
--- drupal-7.25/modules/simpletest/tests/ajax.test 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/ajax.test 2019-04-17 22:20:46.000000000 +0200
@@ -293,7 +293,7 @@
$this->assertCommand($commands, $expected, "'changed' AJAX command (with asterisk) issued with correct selector");
// Tests the 'css' command.
- $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("Set the the '#box' div to be blue.")));
+ $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("Set the '#box' div to be blue.")));
$expected = array(
'command' => 'css',
'selector' => '#css_div',
@@ -368,6 +368,14 @@
'settings' => array('ajax_forms_test' => array('foo' => 42)),
);
$this->assertCommand($commands, $expected, "'settings' AJAX command issued with correct data");
+
+ // Tests the 'add_css' command.
+ $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'add_css' command")));
+ $expected = array(
+ 'command' => 'add_css',
+ 'data' => 'my/file.css',
+ );
+ $this->assertCommand($commands, $expected, "'add_css' AJAX command issued with correct data");
}
}
@@ -498,6 +506,85 @@
}
/**
+ * Test Ajax forms when page caching for anonymous users is turned on.
+ */
+class AJAXFormPageCacheTestCase extends AJAXTestCase {
+ protected $profile = 'testing';
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'AJAX forms on cached pages',
+ 'description' => 'Tests that AJAX forms work properly for anonymous users on cached pages.',
+ 'group' => 'AJAX',
+ );
+ }
+
+ public function setUp() {
+ parent::setUp();
+
+ variable_set('cache', TRUE);
+ }
+
+ /**
+ * Return the build id of the current form.
+ */
+ protected function getFormBuildId() {
+ $build_id_fields = $this->xpath('//input[@name="form_build_id"]');
+ $this->assertEqual(count($build_id_fields), 1, 'One form build id field on the page');
+ return (string) $build_id_fields[0]['value'];
+ }
+
+ /**
+ * Create a simple form, then POST to system/ajax to change to it.
+ */
+ public function testSimpleAJAXFormValue() {
+ $this->drupalGet('ajax_forms_test_get_form');
+ $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Page was not cached.');
+ $build_id_initial = $this->getFormBuildId();
+
+ $edit = array('select' => 'green');
+ $commands = $this->drupalPostAJAX(NULL, $edit, 'select');
+ $build_id_first_ajax = $this->getFormBuildId();
+ $this->assertNotEqual($build_id_initial, $build_id_first_ajax, 'Build id is changed in the simpletest-DOM on first AJAX submission');
+ $expected = array(
+ 'command' => 'updateBuildId',
+ 'old' => $build_id_initial,
+ 'new' => $build_id_first_ajax,
+ );
+ $this->assertCommand($commands, $expected, 'Build id change command issued on first AJAX submission');
+
+ $edit = array('select' => 'red');
+ $commands = $this->drupalPostAJAX(NULL, $edit, 'select');
+ $build_id_second_ajax = $this->getFormBuildId();
+ $this->assertEqual($build_id_first_ajax, $build_id_second_ajax, 'Build id remains the same on subsequent AJAX submissions');
+
+ // Repeat the test sequence but this time with a page loaded from the cache.
+ $this->drupalGet('ajax_forms_test_get_form');
+ $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
+ $build_id_from_cache_initial = $this->getFormBuildId();
+ $this->assertEqual($build_id_initial, $build_id_from_cache_initial, 'Build id is the same as on the first request');
+
+ $edit = array('select' => 'green');
+ $commands = $this->drupalPostAJAX(NULL, $edit, 'select');
+ $build_id_from_cache_first_ajax = $this->getFormBuildId();
+ $this->assertNotEqual($build_id_from_cache_initial, $build_id_from_cache_first_ajax, 'Build id is changed in the simpletest-DOM on first AJAX submission');
+ $this->assertNotEqual($build_id_first_ajax, $build_id_from_cache_first_ajax, 'Build id from first user is not reused');
+ $expected = array(
+ 'command' => 'updateBuildId',
+ 'old' => $build_id_from_cache_initial,
+ 'new' => $build_id_from_cache_first_ajax,
+ );
+ $this->assertCommand($commands, $expected, 'Build id change command issued on first AJAX submission');
+
+ $edit = array('select' => 'red');
+ $commands = $this->drupalPostAJAX(NULL, $edit, 'select');
+ $build_id_from_cache_second_ajax = $this->getFormBuildId();
+ $this->assertEqual($build_id_from_cache_first_ajax, $build_id_from_cache_second_ajax, 'Build id remains the same on subsequent AJAX submissions');
+ }
+}
+
+
+/**
* Miscellaneous Ajax tests using ajax_test module.
*/
class AJAXElementValidation extends AJAXTestCase {
diff -Naur drupal-7.25/modules/simpletest/tests/ajax_forms_test.info drupal-7.66/modules/simpletest/tests/ajax_forms_test.info
--- drupal-7.25/modules/simpletest/tests/ajax_forms_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/ajax_forms_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
version = VERSION
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/ajax_forms_test.module drupal-7.66/modules/simpletest/tests/ajax_forms_test.module
--- drupal-7.25/modules/simpletest/tests/ajax_forms_test.module 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/ajax_forms_test.module 2019-04-17 22:20:46.000000000 +0200
@@ -157,7 +157,7 @@
// Shows the Ajax 'css' command.
$form['css_command_example'] = array(
- '#value' => t("Set the the '#box' div to be blue."),
+ '#value' => t("Set the '#box' div to be blue."),
'#type' => 'submit',
'#ajax' => array(
'callback' => 'ajax_forms_test_advanced_commands_css_callback',
@@ -254,6 +254,15 @@
),
);
+ // Shows the Ajax 'add_css' command.
+ $form['add_css_command_example'] = array(
+ '#type' => 'submit',
+ '#value' => t("AJAX 'add_css' command"),
+ '#ajax' => array(
+ 'callback' => 'ajax_forms_test_advanced_commands_add_css_callback',
+ ),
+ );
+
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
@@ -407,6 +416,15 @@
}
/**
+ * Ajax callback for 'add_css'.
+ */
+function ajax_forms_test_advanced_commands_add_css_callback($form, $form_state) {
+ $commands = array();
+ $commands[] = ajax_command_add_css('my/file.css');
+ return array('#type' => 'ajax', '#commands' => $commands);
+}
+
+/**
* This form and its related submit and callback functions demonstrate
* not validating another form element when a single Ajax element is triggered.
*
diff -Naur drupal-7.25/modules/simpletest/tests/ajax_test.info drupal-7.66/modules/simpletest/tests/ajax_test.info
--- drupal-7.25/modules/simpletest/tests/ajax_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/ajax_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
core = 7.x
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/batch_test.callbacks.inc drupal-7.66/modules/simpletest/tests/batch_test.callbacks.inc
--- drupal-7.25/modules/simpletest/tests/batch_test.callbacks.inc 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/batch_test.callbacks.inc 2019-04-17 22:20:46.000000000 +0200
@@ -7,6 +7,8 @@
*/
/**
+ * Implements callback_batch_operation().
+ *
* Simple batch operation.
*/
function _batch_test_callback_1($id, $sleep, &$context) {
@@ -20,6 +22,8 @@
}
/**
+ * Implements callback_batch_operation().
+ *
* Multistep batch operation.
*/
function _batch_test_callback_2($start, $total, $sleep, &$context) {
@@ -53,6 +57,8 @@
}
/**
+ * Implements callback_batch_operation().
+ *
* Simple batch operation.
*/
function _batch_test_callback_5($id, $sleep, &$context) {
@@ -68,6 +74,8 @@
}
/**
+ * Implements callback_batch_operation().
+ *
* Batch operation setting up its own batch.
*/
function _batch_test_nested_batch_callback() {
@@ -76,6 +84,8 @@
}
/**
+ * Implements callback_batch_finished().
+ *
* Common 'finished' callbacks for batches 1 to 4.
*/
function _batch_test_finished_helper($batch_id, $success, $results, $operations) {
@@ -99,6 +109,8 @@
}
/**
+ * Implements callback_batch_finished().
+ *
* 'finished' callback for batch 0.
*/
function _batch_test_finished_0($success, $results, $operations) {
@@ -106,6 +118,8 @@
}
/**
+ * Implements callback_batch_finished().
+ *
* 'finished' callback for batch 1.
*/
function _batch_test_finished_1($success, $results, $operations) {
@@ -113,6 +127,8 @@
}
/**
+ * Implements callback_batch_finished().
+ *
* 'finished' callback for batch 2.
*/
function _batch_test_finished_2($success, $results, $operations) {
@@ -120,6 +136,8 @@
}
/**
+ * Implements callback_batch_finished().
+ *
* 'finished' callback for batch 3.
*/
function _batch_test_finished_3($success, $results, $operations) {
@@ -127,6 +145,8 @@
}
/**
+ * Implements callback_batch_finished().
+ *
* 'finished' callback for batch 4.
*/
function _batch_test_finished_4($success, $results, $operations) {
@@ -134,6 +154,8 @@
}
/**
+ * Implements callback_batch_finished().
+ *
* 'finished' callback for batch 5.
*/
function _batch_test_finished_5($success, $results, $operations) {
diff -Naur drupal-7.25/modules/simpletest/tests/batch_test.info drupal-7.66/modules/simpletest/tests/batch_test.info
--- drupal-7.25/modules/simpletest/tests/batch_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/batch_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
core = 7.x
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/boot.test drupal-7.66/modules/simpletest/tests/boot.test
--- drupal-7.25/modules/simpletest/tests/boot.test 1970-01-01 01:00:00.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/boot.test 2019-04-17 22:20:46.000000000 +0200
@@ -0,0 +1,38 @@
+ 'Early bootstrap test',
+ 'description' => 'Confirm that calling module_implements() during early bootstrap does not pollute the module_implements() cache.',
+ 'group' => 'System',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('boot_test_1', 'boot_test_2');
+ }
+
+ /**
+ * Test hook_boot() on both regular and "early exit" pages.
+ */
+ public function testHookBoot() {
+ $paths = array('', 'early_exit');
+ foreach ($paths as $path) {
+ // Empty the module_implements() caches.
+ module_implements(NULL, FALSE, TRUE);
+ // Do a request to the front page, which will call module_implements()
+ // during hook_boot().
+ $this->drupalGet($path);
+ // Reset the static cache so we get implementation data from the persistent
+ // cache.
+ drupal_static_reset();
+ // Make sure we get a full list of all modules implementing hook_help().
+ $modules = module_implements('help');
+ $this->assertTrue(in_array('boot_test_2', $modules));
+ }
+ }
+}
diff -Naur drupal-7.25/modules/simpletest/tests/boot_test_1.info drupal-7.66/modules/simpletest/tests/boot_test_1.info
--- drupal-7.25/modules/simpletest/tests/boot_test_1.info 1970-01-01 01:00:00.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/boot_test_1.info 2019-04-17 22:39:36.000000000 +0200
@@ -0,0 +1,11 @@
+name = Early bootstrap tests
+description = A support module for hook_boot testing.
+core = 7.x
+package = Testing
+version = VERSION
+hidden = TRUE
+
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
+project = "drupal"
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/boot_test_1.module drupal-7.66/modules/simpletest/tests/boot_test_1.module
--- drupal-7.25/modules/simpletest/tests/boot_test_1.module 1970-01-01 01:00:00.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/boot_test_1.module 2019-04-17 22:20:46.000000000 +0200
@@ -0,0 +1,21 @@
+proxy_ip;
+ $_SERVER['HTTP_X_FORWARDED_FOR'] = $this->proxy_ip;
+ drupal_static_reset('ip_address');
+ $this->assertTrue(
+ ip_address() == $this->proxy_ip,
+ 'Visiting from trusted proxy got proxy IP address.'
+ );
+
// Multi-tier architecture with comma separated values in header.
$_SERVER['REMOTE_ADDR'] = $this->proxy_ip;
$_SERVER['HTTP_X_FORWARDED_FOR'] = implode(', ', array($this->untrusted_ip, $this->forwarded_ip, $this->proxy2_ip));
@@ -93,6 +102,11 @@
$this->assertFalse(drupal_valid_http_host('security\\.drupal.org:80'), 'HTTP_HOST with \\ is invalid');
$this->assertFalse(drupal_valid_http_host('security<.drupal.org:80'), 'HTTP_HOST with < is invalid');
$this->assertFalse(drupal_valid_http_host('security..drupal.org:80'), 'HTTP_HOST with .. is invalid');
+ // Verifies that host names are shorter than 1000 characters.
+ $this->assertFalse(drupal_valid_http_host(str_repeat('x', 1001)), 'HTTP_HOST with more than 1000 characters is invalid.');
+ $this->assertFalse(drupal_valid_http_host(str_repeat('.', 101)), 'HTTP_HOST with more than 100 subdomains is invalid.');
+ $this->assertFalse(drupal_valid_http_host(str_repeat(':', 101)), 'HTTP_HOST with more than 100 portseparators is invalid.');
+
// IPv6 loopback address
$this->assertTrue(drupal_valid_http_host('[::1]:80'), 'HTTP_HOST containing IPv6 loopback is valid');
}
@@ -139,7 +153,7 @@
$this->assertResponse(200, 'Conditional request without If-None-Match returned 200 OK.');
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
- $this->drupalGet('', array(), array('If-Modified-Since: ' . gmdate(DATE_RFC1123, strtotime($last_modified) + 1), 'If-None-Match: ' . $etag));
+ $this->drupalGet('', array(), array('If-Modified-Since: ' . gmdate(DATE_RFC7231, strtotime($last_modified) + 1), 'If-None-Match: ' . $etag));
$this->assertResponse(200, 'Conditional request with new a If-Modified-Since date newer than Last-Modified returned 200 OK.');
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
@@ -147,7 +161,9 @@
$this->drupalLogin($user);
$this->drupalGet('', array(), array('If-Modified-Since: ' . $last_modified, 'If-None-Match: ' . $etag));
$this->assertResponse(200, 'Conditional request returned 200 OK for authenticated user.');
- $this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), 'Absense of Page was not cached.');
+ $this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), 'Absence of Page was not cached.');
+ $this->assertFalse($this->drupalGetHeader('ETag'), 'ETag HTTP headers are not present for logged in users.');
+ $this->assertFalse($this->drupalGetHeader('Last-Modified'), 'Last-Modified HTTP headers are not present for logged in users.');
}
/**
@@ -184,7 +200,7 @@
$this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar')));
$this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), 'Caching was bypassed.');
$this->assertTrue(strpos($this->drupalGetHeader('Vary'), 'Cookie') === FALSE, 'Vary: Cookie header was not sent.');
- $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'no-cache, must-revalidate, post-check=0, pre-check=0', 'Cache-Control header was sent.');
+ $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'no-cache, must-revalidate', 'Cache-Control header was sent.');
$this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', 'Expires header was sent.');
$this->assertEqual($this->drupalGetHeader('Foo'), 'bar', 'Custom header was sent.');
@@ -282,6 +298,39 @@
}
/**
+ * Tests the auto-loading behavior of the code registry.
+ */
+class BootstrapAutoloadTestCase extends DrupalWebTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Code registry',
+ 'description' => 'Test that the code registry functions correctly.',
+ 'group' => 'Bootstrap',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('drupal_autoload_test');
+ }
+
+ /**
+ * Tests that autoloader name matching is not case sensitive.
+ */
+ function testAutoloadCase() {
+ // Test interface autoloader.
+ $this->assertTrue(drupal_autoload_interface('drupalautoloadtestinterface'), 'drupal_autoload_interface() recognizes DrupalAutoloadTestInterface in lower case.');
+ // Test class autoloader.
+ $this->assertTrue(drupal_autoload_class('drupalautoloadtestclass'), 'drupal_autoload_class() recognizes DrupalAutoloadTestClass in lower case.');
+ // Test trait autoloader.
+ if (version_compare(PHP_VERSION, '5.4') >= 0) {
+ $this->assertTrue(drupal_autoload_trait('drupalautoloadtesttrait'), 'drupal_autoload_trait() recognizes DrupalAutoloadTestTrait in lower case.');
+ }
+ }
+
+}
+
+/**
* Test hook_boot() and hook_exit().
*/
class HookBootExitTestCase extends DrupalWebTestCase {
@@ -339,13 +388,20 @@
public static function getInfo() {
return array(
- 'name' => 'Get filename test',
- 'description' => 'Test that drupal_get_filename() works correctly when the file is not found in the database.',
+ 'name' => 'Get filename test (without the system table)',
+ 'description' => 'Test that drupal_get_filename() works correctly when the database is not available.',
'group' => 'Bootstrap',
);
}
/**
+ * The last file-related error message triggered by the filename test.
+ *
+ * Used by BootstrapGetFilenameTestCase::testDrupalGetFilename().
+ */
+ protected $getFilenameTestTriggeredError;
+
+ /**
* Test that drupal_get_filename() works correctly when the file is not found in the database.
*/
function testDrupalGetFilename() {
@@ -374,6 +430,203 @@
// automatically check there for 'script' files, just as it does for (e.g.)
// 'module' files in modules.
$this->assertIdentical(drupal_get_filename('script', 'test'), 'scripts/test.script', t('Retrieve test script location.'));
+
+ // When searching for a module that does not exist, drupal_get_filename()
+ // should return NULL and trigger an appropriate error message.
+ $this->getFilenameTestTriggeredError = NULL;
+ set_error_handler(array($this, 'fileNotFoundErrorHandler'));
+ $non_existing_module = $this->randomName();
+ $this->assertNull(drupal_get_filename('module', $non_existing_module), 'Searching for a module that does not exist returns NULL.');
+ $this->assertTrue(strpos($this->getFilenameTestTriggeredError, format_string('The following module is missing from the file system: %name', array('%name' => $non_existing_module))) === 0, 'Searching for an item that does not exist triggers the correct error.');
+ restore_error_handler();
+
+ // Check that the result is stored in the file system scan cache.
+ $file_scans = _drupal_file_scan_cache();
+ $this->assertIdentical($file_scans['module'][$non_existing_module], FALSE, 'Searching for a module that does not exist creates a record in the missing and moved files static variable.');
+
+ // Performing the search again in the same request still should not find
+ // the file, but the error message should not be repeated (therefore we do
+ // not override the error handler here).
+ $this->assertNull(drupal_get_filename('module', $non_existing_module), 'Searching for a module that does not exist returns NULL during the second search.');
+ }
+
+ /**
+ * Skips handling of "file not found" errors.
+ */
+ public function fileNotFoundErrorHandler($error_level, $message, $filename, $line, $context) {
+ // Skip error handling if this is a "file not found" error.
+ if (strpos($message, 'is missing from the file system:') !== FALSE || strpos($message, 'has moved within the file system:') !== FALSE) {
+ $this->getFilenameTestTriggeredError = $message;
+ return;
+ }
+ _drupal_error_handler($error_level, $message, $filename, $line, $context);
+ }
+}
+
+/**
+ * Test drupal_get_filename() in the context of a full Drupal installation.
+ */
+class BootstrapGetFilenameWebTestCase extends DrupalWebTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Get filename test (full installation)',
+ 'description' => 'Test that drupal_get_filename() works correctly in the context of a full Drupal installation.',
+ 'group' => 'Bootstrap',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('system_test');
+ }
+
+ /**
+ * The last file-related error message triggered by the filename test.
+ *
+ * Used by BootstrapGetFilenameWebTestCase::testDrupalGetFilename().
+ */
+ protected $getFilenameTestTriggeredError;
+
+ /**
+ * Test that drupal_get_filename() works correctly with a full Drupal site.
+ */
+ function testDrupalGetFilename() {
+ // Search for a module that exists in the file system and the {system}
+ // table and make sure that it is found.
+ $this->assertIdentical(drupal_get_filename('module', 'node'), 'modules/node/node.module', 'Module found at expected location.');
+
+ // Search for a module that does not exist in either the file system or the
+ // {system} table. Make sure that an appropriate error is triggered and
+ // that the module winds up in the static and persistent cache.
+ $this->getFilenameTestTriggeredError = NULL;
+ set_error_handler(array($this, 'fileNotFoundErrorHandler'));
+ $non_existing_module = $this->randomName();
+ $this->assertNull(drupal_get_filename('module', $non_existing_module), 'Searching for a module that does not exist returns NULL.');
+ $this->assertTrue(strpos($this->getFilenameTestTriggeredError, format_string('The following module is missing from the file system: %name', array('%name' => $non_existing_module))) === 0, 'Searching for a module that does not exist triggers the correct error.');
+ restore_error_handler();
+ $file_scans = _drupal_file_scan_cache();
+ $this->assertIdentical($file_scans['module'][$non_existing_module], FALSE, 'Searching for a module that does not exist creates a record in the missing and moved files static variable.');
+ drupal_file_scan_write_cache();
+ $cache = cache_get('_drupal_file_scan_cache', 'cache_bootstrap');
+ $this->assertIdentical($cache->data['module'][$non_existing_module], FALSE, 'Searching for a module that does not exist creates a record in the missing and moved files persistent cache.');
+
+ // Simulate moving a module to a location that does not match the location
+ // in the {system} table and perform similar tests as above.
+ db_update('system')
+ ->fields(array('filename' => 'modules/simpletest/tests/fake_location/module_test.module'))
+ ->condition('name', 'module_test')
+ ->condition('type', 'module')
+ ->execute();
+ $this->getFilenameTestTriggeredError = NULL;
+ set_error_handler(array($this, 'fileNotFoundErrorHandler'));
+ $this->assertIdentical(drupal_get_filename('module', 'module_test'), 'modules/simpletest/tests/module_test.module', 'Searching for a module that has moved finds the module at its new location.');
+ $this->assertTrue(strpos($this->getFilenameTestTriggeredError, format_string('The following module has moved within the file system: %name', array('%name' => 'module_test'))) === 0, 'Searching for a module that has moved triggers the correct error.');
+ restore_error_handler();
+ $file_scans = _drupal_file_scan_cache();
+ $this->assertIdentical($file_scans['module']['module_test'], 'modules/simpletest/tests/module_test.module', 'Searching for a module that has moved creates a record in the missing and moved files static variable.');
+ drupal_file_scan_write_cache();
+ $cache = cache_get('_drupal_file_scan_cache', 'cache_bootstrap');
+ $this->assertIdentical($cache->data['module']['module_test'], 'modules/simpletest/tests/module_test.module', 'Searching for a module that has moved creates a record in the missing and moved files persistent cache.');
+
+ // Simulate a module that exists in the {system} table but does not exist
+ // in the file system and perform similar tests as above.
+ $non_existing_module = $this->randomName();
+ db_update('system')
+ ->fields(array('name' => $non_existing_module))
+ ->condition('name', 'module_test')
+ ->condition('type', 'module')
+ ->execute();
+ $this->getFilenameTestTriggeredError = NULL;
+ set_error_handler(array($this, 'fileNotFoundErrorHandler'));
+ $this->assertNull(drupal_get_filename('module', $non_existing_module), 'Searching for a module that exists in the system table but not in the file system returns NULL.');
+ $this->assertTrue(strpos($this->getFilenameTestTriggeredError, format_string('The following module is missing from the file system: %name', array('%name' => $non_existing_module))) === 0, 'Searching for a module that exists in the system table but not in the file system triggers the correct error.');
+ restore_error_handler();
+ $file_scans = _drupal_file_scan_cache();
+ $this->assertIdentical($file_scans['module'][$non_existing_module], FALSE, 'Searching for a module that exists in the system table but not in the file system creates a record in the missing and moved files static variable.');
+ drupal_file_scan_write_cache();
+ $cache = cache_get('_drupal_file_scan_cache', 'cache_bootstrap');
+ $this->assertIdentical($cache->data['module'][$non_existing_module], FALSE, 'Searching for a module that exists in the system table but not in the file system creates a record in the missing and moved files persistent cache.');
+
+ // Simulate a module that exists in the file system but not in the {system}
+ // table and perform similar tests as above.
+ db_delete('system')
+ ->condition('name', 'common_test')
+ ->condition('type', 'module')
+ ->execute();
+ system_list_reset();
+ $this->getFilenameTestTriggeredError = NULL;
+ set_error_handler(array($this, 'fileNotFoundErrorHandler'));
+ $this->assertIdentical(drupal_get_filename('module', 'common_test'), 'modules/simpletest/tests/common_test.module', 'Searching for a module that does not exist in the system table finds the module at its actual location.');
+ $this->assertTrue(strpos($this->getFilenameTestTriggeredError, format_string('The following module has moved within the file system: %name', array('%name' => 'common_test'))) === 0, 'Searching for a module that does not exist in the system table triggers the correct error.');
+ restore_error_handler();
+ $file_scans = _drupal_file_scan_cache();
+ $this->assertIdentical($file_scans['module']['common_test'], 'modules/simpletest/tests/common_test.module', 'Searching for a module that does not exist in the system table creates a record in the missing and moved files static variable.');
+ drupal_file_scan_write_cache();
+ $cache = cache_get('_drupal_file_scan_cache', 'cache_bootstrap');
+ $this->assertIdentical($cache->data['module']['common_test'], 'modules/simpletest/tests/common_test.module', 'Searching for a module that does not exist in the system table creates a record in the missing and moved files persistent cache.');
+ }
+
+ /**
+ * Skips handling of "file not found" errors.
+ */
+ public function fileNotFoundErrorHandler($error_level, $message, $filename, $line, $context) {
+ // Skip error handling if this is a "file not found" error.
+ if (strpos($message, 'is missing from the file system:') !== FALSE || strpos($message, 'has moved within the file system:') !== FALSE) {
+ $this->getFilenameTestTriggeredError = $message;
+ return;
+ }
+ _drupal_error_handler($error_level, $message, $filename, $line, $context);
+ }
+
+ /**
+ * Test that watchdog messages about missing files are correctly recorded.
+ */
+ public function testWatchdog() {
+ // Search for a module that does not exist in either the file system or the
+ // {system} table. Make sure that an appropriate warning is recorded in the
+ // logs.
+ $non_existing_module = $this->randomName();
+ $query_parameters = array(
+ ':type' => 'php',
+ ':severity' => WATCHDOG_WARNING,
+ );
+ $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND severity = :severity', $query_parameters)->fetchField(), 0, 'No warning message appears in the logs before searching for a module that does not exist.');
+ // Trigger the drupal_get_filename() call. This must be done via a request
+ // to a separate URL since the watchdog() will happen in a shutdown
+ // function, and so that SimpleTest can be told to ignore (and not fail as
+ // a result of) the expected PHP warnings generated during this process.
+ variable_set('system_test_drupal_get_filename_test_module_name', $non_existing_module);
+ $this->drupalGet('system-test/drupal-get-filename');
+ $message_variables = db_query('SELECT variables FROM {watchdog} WHERE type = :type AND severity = :severity', $query_parameters)->fetchCol();
+ $this->assertEqual(count($message_variables), 1, 'A single warning message appears in the logs after searching for a module that does not exist.');
+ $variables = reset($message_variables);
+ $variables = unserialize($variables);
+ $this->assertTrue(isset($variables['!message']) && strpos($variables['!message'], format_string('The following module is missing from the file system: %name', array('%name' => $non_existing_module))) !== FALSE, 'The warning message that appears in the logs after searching for a module that does not exist contains the expected text.');
+ }
+
+ /**
+ * Test that drupal_get_filename() does not break recursive rebuilds.
+ */
+ public function testRecursiveRebuilds() {
+ // Ensure that the drupal_get_filename() call due to a missing module does
+ // not break the data returned by an attempted recursive rebuild. The code
+ // path which is tested is as follows:
+ // - Call drupal_get_schema().
+ // - Within a hook_schema() implementation, trigger a drupal_get_filename()
+ // search for a nonexistent module.
+ // - In the watchdog() call that results from that, trigger
+ // drupal_get_schema() again.
+ // Without some kind of recursion protection, this could cause the second
+ // drupal_get_schema() call to return incomplete results. This test ensures
+ // that does not happen.
+ $non_existing_module = $this->randomName();
+ variable_set('system_test_drupal_get_filename_test_module_name', $non_existing_module);
+ $this->drupalGet('system-test/drupal-get-filename-with-schema-rebuild');
+ $original_drupal_get_schema_tables = variable_get('system_test_drupal_get_filename_with_schema_rebuild_original_tables');
+ $final_drupal_get_schema_tables = variable_get('system_test_drupal_get_filename_with_schema_rebuild_final_tables');
+ $this->assertTrue(!empty($original_drupal_get_schema_tables));
+ $this->assertTrue(!empty($final_drupal_get_schema_tables));
+ $this->assertEqual($original_drupal_get_schema_tables, $final_drupal_get_schema_tables);
}
}
@@ -476,16 +729,12 @@
* Tests that the drupal_check_memory_limit() function works as expected.
*/
function testCheckMemoryLimit() {
- $memory_limit = ini_get('memory_limit');
// Test that a very reasonable amount of memory is available.
$this->assertTrue(drupal_check_memory_limit('30MB'), '30MB of memory tested available.');
- // Get the available memory and multiply it by two to make it unreasonably
- // high.
- $twice_avail_memory = ($memory_limit * 2) . 'MB';
-
+ // Test an unlimited memory limit.
// The function should always return true if the memory limit is set to -1.
- $this->assertTrue(drupal_check_memory_limit($twice_avail_memory, -1), 'drupal_check_memory_limit() returns TRUE when a limit of -1 (none) is supplied');
+ $this->assertTrue(drupal_check_memory_limit('9999999999YB', -1), 'drupal_check_memory_limit() returns TRUE when a limit of -1 (none) is supplied');
// Test that even though we have 30MB of memory available - the function
// returns FALSE when given an upper limit for how much memory can be used.
@@ -541,3 +790,85 @@
}
}
}
+
+/**
+ * Tests for $_GET['destination'] and $_REQUEST['destination'] validation.
+ */
+class BootstrapDestinationTestCase extends DrupalWebTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'URL destination validation',
+ 'description' => 'Test that $_GET[\'destination\'] and $_REQUEST[\'destination\'] cannot contain external URLs.',
+ 'group' => 'Bootstrap',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('system_test');
+ }
+
+ /**
+ * Tests that $_GET/$_REQUEST['destination'] only contain internal URLs.
+ *
+ * @see _drupal_bootstrap_variables()
+ * @see system_test_get_destination()
+ * @see system_test_request_destination()
+ */
+ public function testDestination() {
+ $test_cases = array(
+ array(
+ 'input' => 'node',
+ 'output' => 'node',
+ 'message' => "Standard internal example node path is present in the 'destination' parameter.",
+ ),
+ array(
+ 'input' => '/example.com',
+ 'output' => '/example.com',
+ 'message' => 'Internal path with one leading slash is allowed.',
+ ),
+ array(
+ 'input' => '//example.com/test',
+ 'output' => '',
+ 'message' => 'External URL without scheme is not allowed.',
+ ),
+ array(
+ 'input' => 'example:test',
+ 'output' => 'example:test',
+ 'message' => 'Internal URL using a colon is allowed.',
+ ),
+ array(
+ 'input' => 'http://example.com',
+ 'output' => '',
+ 'message' => 'External URL is not allowed.',
+ ),
+ array(
+ 'input' => 'javascript:alert(0)',
+ 'output' => 'javascript:alert(0)',
+ 'message' => 'Javascript URL is allowed because it is treated as an internal URL.',
+ ),
+ );
+ foreach ($test_cases as $test_case) {
+ // Test $_GET['destination'].
+ $this->drupalGet('system-test/get-destination', array('query' => array('destination' => $test_case['input'])));
+ $this->assertIdentical($test_case['output'], $this->drupalGetContent(), $test_case['message']);
+ // Test $_REQUEST['destination']. There's no form to submit to, so
+ // drupalPost() won't work here; this just tests a direct $_POST request
+ // instead.
+ $curl_parameters = array(
+ CURLOPT_URL => $this->getAbsoluteUrl('system-test/request-destination'),
+ CURLOPT_POST => TRUE,
+ CURLOPT_POSTFIELDS => 'destination=' . urlencode($test_case['input']),
+ CURLOPT_HTTPHEADER => array(),
+ );
+ $post_output = $this->curlExec($curl_parameters);
+ $this->assertIdentical($test_case['output'], $post_output, $test_case['message']);
+ }
+
+ // Make sure that 404 pages do not populate $_GET['destination'] with
+ // external URLs.
+ variable_set('site_404', 'system-test/get-destination');
+ $this->drupalGet('http://example.com', array('external' => FALSE));
+ $this->assertIdentical('', $this->drupalGetContent(), 'External URL is not allowed on 404 pages.');
+ }
+}
diff -Naur drupal-7.25/modules/simpletest/tests/common.test drupal-7.66/modules/simpletest/tests/common.test
--- drupal-7.25/modules/simpletest/tests/common.test 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/common.test 2019-04-17 22:20:46.000000000 +0200
@@ -76,7 +76,7 @@
class CommonURLUnitTest extends DrupalWebTestCase {
public static function getInfo() {
return array(
- 'name' => 'URL generation tests',
+ 'name' => 'URL generation unit tests',
'description' => 'Confirm that url(), drupal_get_query_parameters(), drupal_http_build_query(), and l() work correctly with various input.',
'group' => 'System',
);
@@ -169,7 +169,7 @@
$this->assertEqual(drupal_http_build_query(array('a' => ' //+%20@۞')), 'a=%20%26%23//%2B%2520%40%DB%9E', 'Value was properly encoded.');
$this->assertEqual(drupal_http_build_query(array(' //+%20@۞' => 'a')), '%20%26%23%2F%2F%2B%2520%40%DB%9E=a', 'Key was properly encoded.');
$this->assertEqual(drupal_http_build_query(array('a' => '1', 'b' => '2', 'c' => '3')), 'a=1&b=2&c=3', 'Multiple values were properly concatenated.');
- $this->assertEqual(drupal_http_build_query(array('a' => array('b' => '2', 'c' => '3'), 'd' => 'foo')), 'a[b]=2&a[c]=3&d=foo', 'Nested array was properly encoded.');
+ $this->assertEqual(drupal_http_build_query(array('a' => array('b' => '2', 'c' => '3'), 'd' => 'foo')), 'a%5Bb%5D=2&a%5Bc%5D=3&d=foo', 'Nested array was properly encoded.');
}
/**
@@ -209,7 +209,16 @@
// Test that drupal can recognize an absolute URL. Used to prevent attack vectors.
$this->assertTrue(url_is_external($url), 'Correctly identified an external URL.');
+ // External URL without an explicit protocol.
+ $url = '//drupal.org/foo/bar?foo=bar&bar=baz&baz#foo';
+ $this->assertTrue(url_is_external($url), 'Correctly identified an external URL without a protocol part.');
+
+ // Internal URL starting with a slash.
+ $url = '/drupal.org';
+ $this->assertFalse(url_is_external($url), 'Correctly identified an internal URL with a leading slash.');
+
// Test the parsing of absolute URLs.
+ $url = 'http://drupal.org/foo/bar?foo=bar&bar=baz&baz#foo';
$result = array(
'path' => 'http://drupal.org/foo/bar',
'query' => array('foo' => 'bar', 'bar' => 'baz', 'baz' => ''),
@@ -349,6 +358,108 @@
$query = array($this->randomName(5) => $this->randomName(5));
$result = url($url, array('query' => $query));
$this->assertEqual($url . '&' . http_build_query($query, '', '&'), $result, 'External URL query string can be extended with a custom query string in $options.');
+
+ // Verify that an internal URL does not result in an external URL without
+ // protocol part.
+ $url = '/drupal.org';
+ $result = url($url);
+ $this->assertTrue(strpos($result, '//') === FALSE, 'Internal URL does not turn into an external URL.');
+
+ // Verify that an external URL without protocol part is recognized as such.
+ $url = '//drupal.org';
+ $result = url($url);
+ $this->assertEqual($url, $result, 'External URL without protocol is not altered.');
+ }
+}
+
+/**
+ * Web tests for URL generation functions.
+ */
+class CommonURLWebTest extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'URL generation web tests',
+ 'description' => 'Confirm that URL-generating functions work correctly on specific site paths.',
+ 'group' => 'System',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('common_test');
+ }
+
+ /**
+ * Tests the url() function on internal paths which mimic external URLs.
+ */
+ function testInternalPathMimicsExternal() {
+ // Ensure that calling url(current_path()) on "/http://example.com" (an
+ // internal path which mimics an external URL) always links to the internal
+ // path, not the external URL. This helps protect against external URL link
+ // injection vulnerabilities.
+ variable_set('common_test_link_to_current_path', TRUE);
+ $this->drupalGet('/http://example.com');
+ $this->clickLink('link which should point to the current path');
+ $this->assertUrl('/http://example.com');
+ $this->assertText('link which should point to the current path');
+ }
+}
+
+/**
+ * Tests url_is_external().
+ */
+class UrlIsExternalUnitTest extends DrupalUnitTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'External URL checking',
+ 'description' => 'Performs tests on url_is_external().',
+ 'group' => 'System',
+ );
+ }
+
+ /**
+ * Tests if each URL is external or not.
+ */
+ function testUrlIsExternal() {
+ foreach ($this->examples() as $path => $expected) {
+ $this->assertIdentical(url_is_external($path), $expected, $path);
+ }
+ }
+
+ /**
+ * Provides data for testUrlIsExternal().
+ *
+ * @return array
+ * An array of test data, keyed by a path, with the expected value where
+ * TRUE is external, and FALSE is not external.
+ */
+ protected function examples() {
+ return array(
+ // Simple external URLs.
+ 'http://example.com' => TRUE,
+ 'https://example.com' => TRUE,
+ 'http://drupal.org/foo/bar?foo=bar&bar=baz&baz#foo' => TRUE,
+ '//drupal.org' => TRUE,
+ // Some browsers ignore or strip leading control characters.
+ "\x00//www.example.com" => TRUE,
+ "\x08//www.example.com" => TRUE,
+ "\x1F//www.example.com" => TRUE,
+ "\n//www.example.com" => TRUE,
+ // JSON supports decoding directly from UTF-8 code points.
+ json_decode('"\u00AD"') . "//www.example.com" => TRUE,
+ json_decode('"\u200E"') . "//www.example.com" => TRUE,
+ json_decode('"\uE0020"') . "//www.example.com" => TRUE,
+ json_decode('"\uE000"') . "//www.example.com" => TRUE,
+ // Backslashes should be normalized to forward.
+ '\\\\example.com' => TRUE,
+ // Local URLs.
+ 'node' => FALSE,
+ '/system/ajax' => FALSE,
+ '?q=foo:bar' => FALSE,
+ 'node/edit:me' => FALSE,
+ '/drupal.org' => FALSE,
+ '' => FALSE,
+ );
}
}
@@ -661,6 +772,10 @@
drupal_add_css($css);
$styles = drupal_get_css();
$this->assertTrue(strpos($styles, $css) > 0, 'Rendered CSS includes the added stylesheet.');
+ // Verify that newlines are properly added inside style tags.
+ $query_string = variable_get('css_js_query_string', '0');
+ $css_processed = "";
+ $this->assertEqual(trim($styles), $css_processed, 'Rendered CSS includes newlines inside style tags for JavaScript use.');
}
/**
@@ -864,6 +979,31 @@
// Verify that invalid characters (including non-breaking space) are stripped from the identifier.
$this->assertIdentical(drupal_clean_css_identifier('invalid !"#$%&\'()*+,./:;<=>?@[\\]^`{|}~ identifier', array()), 'invalididentifier', 'Strip invalid characters.');
+
+ // Verify that double underscores are replaced in the identifier by default.
+ $identifier = 'css__identifier__with__double__underscores';
+ $expected = 'css--identifier--with--double--underscores';
+ $this->assertIdentical(drupal_clean_css_identifier($identifier), $expected, 'Verify double underscores are replaced with double hyphens by default.');
+
+ // Verify that double underscores are preserved in the identifier if the
+ // variable allow_css_double_underscores is set to TRUE.
+ $this->setAllowCSSDoubleUnderscores(TRUE);
+ $this->assertIdentical(drupal_clean_css_identifier($identifier), $identifier, 'Verify double underscores are preserved if the allow_css_double_underscores set to TRUE.');
+
+ // To avoid affecting other test cases, set the variable
+ // allow_css_double_underscores to FALSE which is the default value.
+ $this->setAllowCSSDoubleUnderscores(FALSE);
+ }
+
+ /**
+ * Set the variable allow_css_double_underscores and reset the cache.
+ *
+ * @param $value bool
+ * A new value to be set to allow_css_double_underscores.
+ */
+ function setAllowCSSDoubleUnderscores($value) {
+ $GLOBALS['conf']['allow_css_double_underscores'] = $value;
+ drupal_static_reset('drupal_clean_css_identifier:allow_css_double_underscores');
}
/**
@@ -914,9 +1054,11 @@
* Tests basic CSS loading with and without optimization via drupal_load_stylesheet().
*
* Known tests:
- * - Retain white-space in selectors. (http://drupal.org/node/472820)
- * - Proper URLs in imported files. (http://drupal.org/node/265719)
- * - Retain pseudo-selectors. (http://drupal.org/node/460448)
+ * - Retain white-space in selectors. (https://drupal.org/node/472820)
+ * - Proper URLs in imported files. (https://drupal.org/node/265719)
+ * - Retain pseudo-selectors. (https://drupal.org/node/460448)
+ * - Don't adjust data URIs. (https://drupal.org/node/2142441)
+ * - Files imported from external URLs. (https://drupal.org/node/2014851)
*/
function testLoadCssBasic() {
// Array of files to test living in 'simpletest/files/css_test_files/'.
@@ -1022,8 +1164,8 @@
$result = drupal_http_request($auth);
$this->drupalSetContent($result->data);
- $this->assertRaw($username, '$_SERVER["PHP_AUTH_USER"] is passed correctly.');
- $this->assertRaw($password, '$_SERVER["PHP_AUTH_PW"] is passed correctly.');
+ $this->assertRaw($username, 'Username is passed correctly.');
+ $this->assertRaw($password, 'Password is passed correctly.');
}
function testDrupalHTTPRequestRedirect() {
@@ -1083,6 +1225,74 @@
}
/**
+ * Tests parsing of the HTTP response status line.
+ */
+class DrupalHTTPResponseStatusLineTest extends DrupalUnitTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Drupal HTTP request response status parsing',
+ 'description' => 'Perform unit tests on _drupal_parse_response_status().',
+ 'group' => 'System',
+ );
+ }
+
+ /**
+ * Tests parsing HTTP response status line.
+ */
+ public function testStatusLine() {
+ // Grab the big array of test data from statusLineData().
+ $data = $this->statusLineData();
+ foreach($data as $test_case) {
+ $test_data = array_shift($test_case);
+ $expected = array_shift($test_case);
+
+ $outcome = _drupal_parse_response_status($test_data);
+
+ foreach(array_keys($expected) as $key) {
+ $this->assertIdentical($outcome[$key], $expected[$key]);
+ }
+ }
+ }
+
+ /**
+ * Data provider for testStatusLine().
+ *
+ * @return array
+ * Test data.
+ */
+ protected function statusLineData() {
+ return array(
+ array(
+ 'HTTP/1.1 200 OK',
+ array(
+ 'http_version' => 'HTTP/1.1',
+ 'response_code' => '200',
+ 'reason_phrase' => 'OK',
+ ),
+ ),
+ // Data set with no reason phrase.
+ array(
+ 'HTTP/1.1 200',
+ array(
+ 'http_version' => 'HTTP/1.1',
+ 'response_code' => '200',
+ 'reason_phrase' => '',
+ ),
+ ),
+ // Arbitrary strings.
+ array(
+ 'version code multi word explanation',
+ array(
+ 'http_version' => 'version',
+ 'response_code' => 'code',
+ 'reason_phrase' => 'multi word explanation',
+ ),
+ ),
+ );
+ }
+}
+
+/**
* Testing drupal_add_region_content and drupal_get_region_content.
*/
class DrupalSetContentTestCase extends DrupalWebTestCase {
@@ -1101,7 +1311,7 @@
function testRegions() {
global $theme_key;
- $block_regions = array_keys(system_region_list($theme_key));
+ $block_regions = system_region_list($theme_key, REGIONS_ALL, FALSE);
$delimiter = $this->randomName(32);
$values = array();
// Set some random content for each region available.
@@ -1162,6 +1372,15 @@
$this->assertText('drupal_goto', 'Drupal goto redirect succeeded.');
$this->assertEqual($this->getUrl(), url('common-test/drupal_goto', array('query' => array('foo' => '123'), 'absolute' => TRUE)), 'Drupal goto redirected to expected URL.');
+ // Test that calling drupal_goto() on the current path is not dangerous.
+ variable_set('common_test_redirect_current_path', TRUE);
+ $this->drupalGet('', array('query' => array('q' => 'http://www.example.com/')));
+ $headers = $this->drupalGetHeaders(TRUE);
+ list(, $status) = explode(' ', $headers[0][':status'], 3);
+ $this->assertEqual($status, 302, 'Expected response code was sent.');
+ $this->assertNotEqual($this->getUrl(), 'http://www.example.com/', 'Drupal goto did not redirect to external URL.');
+ $this->assertTrue(strpos($this->getUrl(), url('', array('absolute' => TRUE))) === 0, 'Drupal redirected to itself.');
+ variable_del('common_test_redirect_current_path');
// Test that drupal_goto() respects ?destination=xxx. Use an complicated URL
// to test that the path is encoded and decoded properly.
$destination = 'common-test/drupal_goto/destination?foo=%2525&bar=123';
@@ -1348,6 +1567,127 @@
}
/**
+ * Test the 'javascript_always_use_jquery' variable.
+ */
+ function testJavaScriptAlwaysUseJQuery() {
+ // The default front page of the site should use jQuery and other standard
+ // scripts and settings.
+ $this->drupalGet('');
+ $this->assertRaw('misc/jquery.js', 'Default behavior: The front page of the site includes jquery.js.');
+ $this->assertRaw('misc/drupal.js', 'Default behavior: The front page of the site includes drupal.js.');
+ $this->assertRaw('Drupal.settings', 'Default behavior: The front page of the site includes Drupal settings.');
+ $this->assertRaw('basePath', 'Default behavior: The front page of the site includes the basePath Drupal setting.');
+
+ // The default front page should not use jQuery and other standard scripts
+ // and settings when the 'javascript_always_use_jquery' variable is set to
+ // FALSE.
+ variable_set('javascript_always_use_jquery', FALSE);
+ $this->drupalGet('');
+ $this->assertNoRaw('misc/jquery.js', 'When "javascript_always_use_jquery" is FALSE: The front page of the site does not include jquery.js.');
+ $this->assertNoRaw('misc/drupal.js', 'When "javascript_always_use_jquery" is FALSE: The front page of the site does not include drupal.js.');
+ $this->assertNoRaw('Drupal.settings', 'When "javascript_always_use_jquery" is FALSE: The front page of the site does not include Drupal settings.');
+ $this->assertNoRaw('basePath', 'When "javascript_always_use_jquery" is FALSE: The front page of the site does not include the basePath Drupal setting.');
+ variable_del('javascript_always_use_jquery');
+
+ // When only settings have been added via drupal_add_js(), drupal_get_js()
+ // should still return jQuery and other standard scripts and settings.
+ $this->resetStaticVariables();
+ drupal_add_js(array('testJavaScriptSetting' => 'test'), 'setting');
+ $javascript = drupal_get_js();
+ $this->assertTrue(strpos($javascript, 'misc/jquery.js') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when only settings have been added includes jquery.js.');
+ $this->assertTrue(strpos($javascript, 'misc/drupal.js') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when only settings have been added includes drupal.js.');
+ $this->assertTrue(strpos($javascript, 'Drupal.settings') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when only settings have been added includes Drupal.settings.');
+ $this->assertTrue(strpos($javascript, 'basePath') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when only settings have been added includes the basePath Drupal setting.');
+ $this->assertTrue(strpos($javascript, 'testJavaScriptSetting') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when only settings have been added includes the added Drupal settings.');
+
+ // When only settings have been added via drupal_add_js() and the
+ // 'javascript_always_use_jquery' variable is set to FALSE, drupal_get_js()
+ // should not return jQuery and other standard scripts and settings, nor
+ // should it return the requested settings (since they cannot actually be
+ // addded to the page without jQuery).
+ $this->resetStaticVariables();
+ variable_set('javascript_always_use_jquery', FALSE);
+ drupal_add_js(array('testJavaScriptSetting' => 'test'), 'setting');
+ $javascript = drupal_get_js();
+ $this->assertTrue(strpos($javascript, 'misc/jquery.js') === FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when only settings have been added does not include jquery.js.');
+ $this->assertTrue(strpos($javascript, 'misc/drupal.js') === FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when only settings have been added does not include drupal.js.');
+ $this->assertTrue(strpos($javascript, 'Drupal.settings') === FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when only settings have been added does not include Drupal.settings.');
+ $this->assertTrue(strpos($javascript, 'basePath') === FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when only settings have been added does not include the basePath Drupal setting.');
+ $this->assertTrue(strpos($javascript, 'testJavaScriptSetting') === FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when only settings have been added does not include the added Drupal settings.');
+ variable_del('javascript_always_use_jquery');
+
+ // When a regular file has been added via drupal_add_js(), drupal_get_js()
+ // should return jQuery and other standard scripts and settings.
+ $this->resetStaticVariables();
+ drupal_add_js('misc/collapse.js');
+ $javascript = drupal_get_js();
+ $this->assertTrue(strpos($javascript, 'misc/jquery.js') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when a custom JavaScript file has been added includes jquery.js.');
+ $this->assertTrue(strpos($javascript, 'misc/drupal.js') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when a custom JavaScript file has been added includes drupal.js.');
+ $this->assertTrue(strpos($javascript, 'Drupal.settings') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when a custom JavaScript file has been added includes Drupal.settings.');
+ $this->assertTrue(strpos($javascript, 'basePath') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when a custom JavaScript file has been added includes the basePath Drupal setting.');
+ $this->assertTrue(strpos($javascript, 'misc/collapse.js') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when a custom JavaScript file has been added includes the custom file.');
+
+ // When a regular file has been added via drupal_add_js() and the
+ // 'javascript_always_use_jquery' variable is set to FALSE, drupal_get_js()
+ // should still return jQuery and other standard scripts and settings
+ // (since the file is assumed to require jQuery by default).
+ $this->resetStaticVariables();
+ variable_set('javascript_always_use_jquery', FALSE);
+ drupal_add_js('misc/collapse.js');
+ $javascript = drupal_get_js();
+ $this->assertTrue(strpos($javascript, 'misc/jquery.js') !== FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when a custom JavaScript file has been added includes jquery.js.');
+ $this->assertTrue(strpos($javascript, 'misc/drupal.js') !== FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when a custom JavaScript file has been added includes drupal.js.');
+ $this->assertTrue(strpos($javascript, 'Drupal.settings') !== FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when a custom JavaScript file has been added includes Drupal.settings.');
+ $this->assertTrue(strpos($javascript, 'basePath') !== FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when a custom JavaScript file has been added includes the basePath Drupal setting.');
+ $this->assertTrue(strpos($javascript, 'misc/collapse.js') !== FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when a custom JavaScript file has been added includes the custom file.');
+ variable_del('javascript_always_use_jquery');
+
+ // When a file that does not require jQuery has been added via
+ // drupal_add_js(), drupal_get_js() should still return jQuery and other
+ // standard scripts and settings by default.
+ $this->resetStaticVariables();
+ drupal_add_js('misc/collapse.js', array('requires_jquery' => FALSE));
+ $javascript = drupal_get_js();
+ $this->assertTrue(strpos($javascript, 'misc/jquery.js') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when a custom JavaScript file that does not require jQuery has been added includes jquery.js.');
+ $this->assertTrue(strpos($javascript, 'misc/drupal.js') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when a custom JavaScript file that does not require jQuery has been added includes drupal.js.');
+ $this->assertTrue(strpos($javascript, 'Drupal.settings') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when a custom JavaScript file that does not require jQuery has been added includes Drupal.settings.');
+ $this->assertTrue(strpos($javascript, 'basePath') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when a custom JavaScript file that does not require jQuery has been added includes the basePath Drupal setting.');
+ $this->assertTrue(strpos($javascript, 'misc/collapse.js') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when a custom JavaScript file that does not require jQuery has been added includes the custom file.');
+
+ // When a file that does not require jQuery has been added via
+ // drupal_add_js() and the 'javascript_always_use_jquery' variable is set
+ // to FALSE, drupal_get_js() should not return jQuery and other standard
+ // scripts and setting, but it should still return the requested file.
+ $this->resetStaticVariables();
+ variable_set('javascript_always_use_jquery', FALSE);
+ drupal_add_js('misc/collapse.js', array('requires_jquery' => FALSE));
+ $javascript = drupal_get_js();
+ $this->assertTrue(strpos($javascript, 'misc/jquery.js') === FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when a custom JavaScript file that does not require jQuery has been added does not include jquery.js.');
+ $this->assertTrue(strpos($javascript, 'misc/drupal.js') === FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when a custom JavaScript file that does not require jQuery has been added does not include drupal.js.');
+ $this->assertTrue(strpos($javascript, 'Drupal.settings') === FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when a custom JavaScript file that does not require jQuery has been added does not include Drupal.settings.');
+ $this->assertTrue(strpos($javascript, 'basePath') === FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when a custom JavaScript file that does not require jQuery has been added does not include the basePath Drupal setting.');
+ $this->assertTrue(strpos($javascript, 'misc/collapse.js') !== FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when a custom JavaScript file that does not require jQuery has been added includes the custom file.');
+ variable_del('javascript_always_use_jquery');
+
+ // When 'javascript_always_use_jquery' is set to FALSE and a file that does
+ // not require jQuery is added, followed by one that does, drupal_get_js()
+ // should return jQuery and other standard scripts and settings, in
+ // addition to both of the requested files.
+ $this->resetStaticVariables();
+ variable_set('javascript_always_use_jquery', FALSE);
+ drupal_add_js('misc/collapse.js', array('requires_jquery' => FALSE));
+ drupal_add_js('misc/ajax.js');
+ $javascript = drupal_get_js();
+ $this->assertTrue(strpos($javascript, 'misc/jquery.js') !== FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when at least one custom JavaScript file that requires jQuery has been added includes jquery.js.');
+ $this->assertTrue(strpos($javascript, 'misc/drupal.js') !== FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when at least one custom JavaScript file that requires jQuery has been added includes drupal.js.');
+ $this->assertTrue(strpos($javascript, 'Drupal.settings') !== FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when at least one custom JavaScript file that requires jQuery has been added includes Drupal.settings.');
+ $this->assertTrue(strpos($javascript, 'basePath') !== FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when at least one custom JavaScript file that requires jQuery has been added includes the basePath Drupal setting.');
+ $this->assertTrue(strpos($javascript, 'misc/collapse.js') !== FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when at least one custom JavaScript file that requires jQuery has been added includes the first custom file.');
+ $this->assertTrue(strpos($javascript, 'misc/ajax.js') !== FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when at least one custom JavaScript file that requires jQuery has been added includes the second custom file.');
+ variable_del('javascript_always_use_jquery');
+ }
+
+ /**
* Test drupal_add_js() sets preproccess to false when cache is set to false.
*/
function testNoCache() {
@@ -1575,6 +1915,15 @@
$query_string = variable_get('css_js_query_string', '0');
$this->assertRaw(drupal_get_path('module', 'node') . '/node.js?' . $query_string, 'Query string was appended correctly to js.');
}
+
+ /**
+ * Resets static variables related to adding JavaScript to a page.
+ */
+ function resetStaticVariables() {
+ drupal_static_reset('drupal_add_js');
+ drupal_static_reset('drupal_add_library');
+ drupal_static_reset('drupal_get_library');
+ }
}
/**
@@ -1893,7 +2242,7 @@
}
/**
- * Tests caching of an empty render item.
+ * Tests caching of render items.
*/
function testDrupalRenderCache() {
// Force a request via GET.
@@ -1919,6 +2268,59 @@
drupal_render($element);
$this->assertFalse(isset($element['#printed']), 'Cache hit');
+ // Test that user 1 does not share the cache with other users who have the
+ // same roles, even when DRUPAL_CACHE_PER_ROLE is used.
+ $user1 = user_load(1);
+ $first_authenticated_user = $this->drupalCreateUser();
+ $second_authenticated_user = $this->drupalCreateUser();
+ $user1->roles = array_intersect_key($user1->roles, array(DRUPAL_AUTHENTICATED_RID => TRUE));
+ user_save($user1);
+ // Load all the accounts again, to make sure we have complete account
+ // objects.
+ $user1 = user_load(1);
+ $first_authenticated_user = user_load($first_authenticated_user->uid);
+ $second_authenticated_user = user_load($second_authenticated_user->uid);
+ $this->assertEqual($user1->roles, $first_authenticated_user->roles, 'User 1 has the same roles as an authenticated user.');
+ // Impersonate user 1 and render content that only user 1 should have
+ // permission to see.
+ $original_user = $GLOBALS['user'];
+ $original_session_state = drupal_save_session();
+ drupal_save_session(FALSE);
+ $GLOBALS['user'] = $user1;
+ $test_element = array(
+ '#cache' => array(
+ 'keys' => array('test'),
+ 'granularity' => DRUPAL_CACHE_PER_ROLE,
+ ),
+ );
+ $element = $test_element;
+ $element['#markup'] = 'content for user 1';
+ $output = drupal_render($element);
+ $this->assertEqual($output, 'content for user 1');
+ // Verify the cache is working by rendering the same element but with
+ // different markup passed in; the result should be the same.
+ $element = $test_element;
+ $element['#markup'] = 'should not be used';
+ $output = drupal_render($element);
+ $this->assertEqual($output, 'content for user 1');
+ // Verify that the first authenticated user does not see the same content
+ // as user 1.
+ $GLOBALS['user'] = $first_authenticated_user;
+ $element = $test_element;
+ $element['#markup'] = 'content for authenticated users';
+ $output = drupal_render($element);
+ $this->assertEqual($output, 'content for authenticated users');
+ // Verify that the second authenticated user shares the cache with the
+ // first authenticated user.
+ $GLOBALS['user'] = $second_authenticated_user;
+ $element = $test_element;
+ $element['#markup'] = 'should not be used';
+ $output = drupal_render($element);
+ $this->assertEqual($output, 'content for authenticated users');
+ // Restore the original logged-in user.
+ $GLOBALS['user'] = $original_user;
+ drupal_save_session($original_session_state);
+
// Restore the previous request method.
$_SERVER['REQUEST_METHOD'] = $request_method;
}
@@ -2785,3 +3187,28 @@
$this->assertIdentical(drupal_array_diff_assoc_recursive($this->array1, $this->array2), $expected);
}
}
+
+/**
+ * Tests the functionality of drupal_get_query_array().
+ */
+class DrupalGetQueryArrayTestCase extends DrupalWebTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Query parsing using drupal_get_query_array()',
+ 'description' => 'Tests that drupal_get_query_array() correctly parses query parameters.',
+ 'group' => 'System',
+ );
+ }
+
+ /**
+ * Tests that drupal_get_query_array() correctly explodes query parameters.
+ */
+ public function testDrupalGetQueryArray() {
+ $url = "http://my.site.com/somepath?foo=/content/folder[@name='foo']/folder[@name='bar']";
+ $parsed = parse_url($url);
+ $result = drupal_get_query_array($parsed['query']);
+ $this->assertEqual($result['foo'], "/content/folder[@name='foo']/folder[@name='bar']", 'drupal_get_query_array() should only explode parameters on the first equals sign.');
+ }
+
+}
diff -Naur drupal-7.25/modules/simpletest/tests/common_test.info drupal-7.66/modules/simpletest/tests/common_test.info
--- drupal-7.25/modules/simpletest/tests/common_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/common_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -7,8 +7,7 @@
stylesheets[print][] = common_test.print.css
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/common_test.module drupal-7.66/modules/simpletest/tests/common_test.module
--- drupal-7.25/modules/simpletest/tests/common_test.module 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/common_test.module 2019-04-17 22:20:46.000000000 +0200
@@ -93,6 +93,18 @@
}
/**
+ * Implements hook_init().
+ */
+function common_test_init() {
+ if (variable_get('common_test_redirect_current_path', FALSE)) {
+ drupal_goto(current_path());
+ }
+ if (variable_get('common_test_link_to_current_path', FALSE)) {
+ drupal_set_message(l('link which should point to the current path', current_path()));
+ }
+}
+
+/**
* Print destination query parameter.
*/
function common_test_destination() {
diff -Naur drupal-7.25/modules/simpletest/tests/common_test_cron_helper.info drupal-7.66/modules/simpletest/tests/common_test_cron_helper.info
--- drupal-7.25/modules/simpletest/tests/common_test_cron_helper.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/common_test_cron_helper.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
core = 7.x
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/database_test.info drupal-7.66/modules/simpletest/tests/database_test.info
--- drupal-7.25/modules/simpletest/tests/database_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/database_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
version = VERSION
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/database_test.test drupal-7.66/modules/simpletest/tests/database_test.test
--- drupal-7.25/modules/simpletest/tests/database_test.test 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/database_test.test 2019-04-17 22:20:46.000000000 +0200
@@ -238,7 +238,7 @@
// Open the default target so we have an object to compare.
$db1 = Database::getConnection('default', 'default');
- // Try to close the the default connection, then open a new one.
+ // Try to close the default connection, then open a new one.
Database::closeConnection('default', 'default');
$db2 = Database::getConnection('default', 'default');
@@ -1414,10 +1414,47 @@
}
$query = (string)$query;
- $expected = "/* Testing query comments SELECT nid FROM {node}; -- */ SELECT test.name AS name, test.age AS age\nFROM \n{test} test";
+ $expected = "/* Testing query comments * / SELECT nid FROM {node}; -- */ SELECT test.name AS name, test.age AS age\nFROM \n{test} test";
$this->assertEqual($num_records, 4, 'Returned the correct number of rows.');
$this->assertEqual($query, $expected, 'The flattened query contains the sanitised comment string.');
+
+ $connection = Database::getConnection();
+ foreach ($this->makeCommentsProvider() as $test_set) {
+ list($expected, $comments) = $test_set;
+ $this->assertEqual($expected, $connection->makeComment($comments));
+ }
+ }
+
+ /**
+ * Provides expected and input values for testVulnerableComment().
+ */
+ function makeCommentsProvider() {
+ return array(
+ array(
+ '/* */ ',
+ array(''),
+ ),
+ // Try and close the comment early.
+ array(
+ '/* Exploit * / DROP TABLE node; -- */ ',
+ array('Exploit */ DROP TABLE node; --'),
+ ),
+ // Variations on comment closing.
+ array(
+ '/* Exploit * / * / DROP TABLE node; -- */ ',
+ array('Exploit */*/ DROP TABLE node; --'),
+ ),
+ array(
+ '/* Exploit * * // DROP TABLE node; -- */ ',
+ array('Exploit **// DROP TABLE node; --'),
+ ),
+ // Try closing the comment in the second string which is appended.
+ array(
+ '/* Exploit * / DROP TABLE node; --; Another try * / DROP TABLE node; -- */ ',
+ array('Exploit */ DROP TABLE node; --', 'Another try */ DROP TABLE node; --'),
+ ),
+ );
}
/**
@@ -1947,6 +1984,15 @@
$this->assertEqual($num_records, 4, 'Returned the correct number of rows.');
}
+
+ /**
+ * Tests that the sort direction is sanitized properly.
+ */
+ function testOrderByEscaping() {
+ $query = db_select('test')->orderBy('name', 'invalid direction');
+ $order_bys = $query->getOrderBy();
+ $this->assertEqual($order_bys['name'], 'ASC', 'Invalid order by direction is converted to ASC.');
+ }
}
/**
@@ -2622,6 +2668,52 @@
}
/**
+ * Confirm that an extended query has a "tag" added to it.
+ */
+ function testExtenderHasTag() {
+ $query = db_select('test')
+ ->extend('SelectQueryExtender');
+ $query->addField('test', 'name');
+ $query->addField('test', 'age', 'age');
+
+ $query->addTag('test');
+
+ $this->assertTrue($query->hasTag('test'), 'hasTag() returned true.');
+ $this->assertFalse($query->hasTag('other'), 'hasTag() returned false.');
+ }
+
+ /**
+ * Test extended query tagging "has all of these tags" functionality.
+ */
+ function testExtenderHasAllTags() {
+ $query = db_select('test')
+ ->extend('SelectQueryExtender');
+ $query->addField('test', 'name');
+ $query->addField('test', 'age', 'age');
+
+ $query->addTag('test');
+ $query->addTag('other');
+
+ $this->assertTrue($query->hasAllTags('test', 'other'), 'hasAllTags() returned true.');
+ $this->assertFalse($query->hasAllTags('test', 'stuff'), 'hasAllTags() returned false.');
+ }
+
+ /**
+ * Test extended query tagging "has at least one of these tags" functionality.
+ */
+ function testExtenderHasAnyTag() {
+ $query = db_select('test')
+ ->extend('SelectQueryExtender');
+ $query->addField('test', 'name');
+ $query->addField('test', 'age', 'age');
+
+ $query->addTag('test');
+
+ $this->assertTrue($query->hasAnyTag('test', 'other'), 'hasAnyTag() returned true.');
+ $this->assertFalse($query->hasAnyTag('other', 'stuff'), 'hasAnyTag() returned false.');
+ }
+
+ /**
* Test that we can attach meta data to a query object.
*
* This is how we pass additional context to alter hooks.
@@ -3091,6 +3183,15 @@
$this->assertEqual($this->countTableRows($table_name_system), $this->countTableRows("system"), 'A temporary table was created successfully in this request.');
$this->assertEqual($this->countTableRows($table_name_users), $this->countTableRows("users"), 'A second temporary table was created successfully in this request.');
+
+ // Check that leading whitespace and comments do not cause problems
+ // in the modified query.
+ $sql = "
+ -- Let's select some rows into a temporary table
+ SELECT name FROM {test}
+ ";
+ $table_name_test = db_query_temporary($sql, array());
+ $this->assertEqual($this->countTableRows($table_name_test), $this->countTableRows('test'), 'Leading white space and comments do not interfere with temporary table creation.');
}
}
@@ -3329,6 +3430,34 @@
$this->assertEqual(count($names), 3, 'Correct number of names returned');
}
+
+ /**
+ * Test SQL injection via database query array arguments.
+ */
+ public function testArrayArgumentsSQLInjection() {
+ // Attempt SQL injection and verify that it does not work.
+ $condition = array(
+ "1 ;INSERT INTO {test} (name) VALUES ('test12345678'); -- " => '',
+ '1' => '',
+ );
+ try {
+ db_query("SELECT * FROM {test} WHERE name = :name", array(':name' => $condition))->fetchObject();
+ $this->fail('SQL injection attempt via array arguments should result in a PDOException.');
+ }
+ catch (PDOException $e) {
+ $this->pass('SQL injection attempt via array arguments should result in a PDOException.');
+ }
+
+ // Test that the insert query that was used in the SQL injection attempt did
+ // not result in a row being inserted in the database.
+ $result = db_select('test')
+ ->condition('name', 'test12345678')
+ ->countQuery()
+ ->execute()
+ ->fetchField();
+ $this->assertFalse($result, 'SQL injection attempt did not result in a row being inserted in the database table.');
+ }
+
}
/**
@@ -3362,12 +3491,14 @@
}
/**
- * Helper method for transaction unit test. This "outer layer" transaction
- * starts and then encapsulates the "inner layer" transaction. This nesting
- * is used to evaluate whether the the database transaction API properly
- * supports nesting. By "properly supports," we mean the outer transaction
- * continues to exist regardless of what functions are called and whether
- * those functions start their own transactions.
+ * Helper method for transaction unit test.
+ *
+ * This "outer layer" transaction starts and then encapsulates the
+ * "inner layer" transaction. This nesting is used to evaluate whether the
+ * database transaction API properly supports nesting. By "properly supports,"
+ * we mean the outer transaction continues to exist regardless of what
+ * functions are called and whether those functions start their own
+ * transactions.
*
* In contrast, a typical database would commit the outer transaction, start
* a new transaction for the inner layer, commit the inner layer transaction,
diff -Naur drupal-7.25/modules/simpletest/tests/drupal_autoload_test/drupal_autoload_test.info drupal-7.66/modules/simpletest/tests/drupal_autoload_test/drupal_autoload_test.info
--- drupal-7.25/modules/simpletest/tests/drupal_autoload_test/drupal_autoload_test.info 1970-01-01 01:00:00.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/drupal_autoload_test/drupal_autoload_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -0,0 +1,13 @@
+name = "Drupal code registry test"
+description = "Support module for testing the code registry."
+files[] = drupal_autoload_test_interface.inc
+files[] = drupal_autoload_test_class.inc
+package = Testing
+version = VERSION
+core = 7.x
+hidden = TRUE
+
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
+project = "drupal"
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/drupal_autoload_test/drupal_autoload_test.module drupal-7.66/modules/simpletest/tests/drupal_autoload_test/drupal_autoload_test.module
--- drupal-7.25/modules/simpletest/tests/drupal_autoload_test/drupal_autoload_test.module 1970-01-01 01:00:00.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/drupal_autoload_test/drupal_autoload_test.module 2019-04-17 22:20:46.000000000 +0200
@@ -0,0 +1,22 @@
+name == 'drupal_autoload_test' && version_compare(PHP_VERSION, '5.4') >= 0) {
+ $files["$module->dir/drupal_autoload_test_trait.sh"] = array(
+ 'module' => $module->name,
+ 'weight' => $module->weight,
+ );
+ }
+ }
+}
diff -Naur drupal-7.25/modules/simpletest/tests/drupal_autoload_test/drupal_autoload_test_class.inc drupal-7.66/modules/simpletest/tests/drupal_autoload_test/drupal_autoload_test_class.inc
--- drupal-7.25/modules/simpletest/tests/drupal_autoload_test/drupal_autoload_test_class.inc 1970-01-01 01:00:00.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/drupal_autoload_test/drupal_autoload_test_class.inc 2019-04-17 22:20:46.000000000 +0200
@@ -0,0 +1,11 @@
+ 'Entity loading',
+ 'description' => 'Tests the entity_load() function.',
+ 'group' => 'Entity API',
+ );
+ }
+
+ /**
+ * Tests the functionality for loading entities matching certain conditions.
+ */
+ public function testEntityLoadConditions() {
+ // Create a few nodes. One of them is given an edge-case title of "Array",
+ // because loading entities by an array of conditions is subject to PHP
+ // array-to-string conversion issues and we want to test those.
+ $node_1 = $this->drupalCreateNode(array('title' => 'Array'));
+ $node_2 = $this->drupalCreateNode(array('title' => 'Node 2'));
+ $node_3 = $this->drupalCreateNode(array('title' => 'Node 3'));
+
+ // Load all entities so that they are statically cached.
+ $all_nodes = entity_load('node', FALSE);
+
+ // Check that the first node can be loaded by title.
+ $nodes_loaded = entity_load('node', FALSE, array('title' => 'Array'));
+ $this->assertEqual(array_keys($nodes_loaded), array($node_1->nid));
+
+ // Check that the second and third nodes can be loaded by title using an
+ // array of conditions, and that the first node is not loaded from the
+ // cache along with them.
+ $nodes_loaded = entity_load('node', FALSE, array('title' => array('Node 2', 'Node 3')));
+ ksort($nodes_loaded);
+ $this->assertEqual(array_keys($nodes_loaded), array($node_2->nid, $node_3->nid));
+ $this->assertIdentical($nodes_loaded[$node_2->nid], $all_nodes[$node_2->nid], 'Loaded node 2 is identical to cached node.');
+ $this->assertIdentical($nodes_loaded[$node_3->nid], $all_nodes[$node_3->nid], 'Loaded node 3 is identical to cached node.');
+ }
+}
diff -Naur drupal-7.25/modules/simpletest/tests/entity_crud_hook_test.info drupal-7.66/modules/simpletest/tests/entity_crud_hook_test.info
--- drupal-7.25/modules/simpletest/tests/entity_crud_hook_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/entity_crud_hook_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
version = VERSION
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/entity_query_access_test.info drupal-7.66/modules/simpletest/tests/entity_query_access_test.info
--- drupal-7.25/modules/simpletest/tests/entity_query_access_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/entity_query_access_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
core = 7.x
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/error_test.info drupal-7.66/modules/simpletest/tests/error_test.info
--- drupal-7.25/modules/simpletest/tests/error_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/error_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
core = 7.x
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/file.test drupal-7.66/modules/simpletest/tests/file.test
--- drupal-7.25/modules/simpletest/tests/file.test 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/file.test 2019-04-17 22:20:46.000000000 +0200
@@ -480,21 +480,6 @@
* Test file_validate_size().
*/
function testFileValidateSize() {
- global $user;
- $original_user = $user;
- drupal_save_session(FALSE);
-
- // Run these test as uid = 1.
- $user = user_load(1);
-
- $file = new stdClass();
- $file->filesize = 999999;
- $errors = file_validate_size($file, 1, 1);
- $this->assertEqual(count($errors), 0, 'No size limits enforced on uid=1.', 'File');
-
- // Run these tests as a regular user.
- $user = $this->drupalCreateUser();
-
// Create a file with a size of 1000 bytes, and quotas of only 1 byte.
$file = new stdClass();
$file->filesize = 1000;
@@ -506,9 +491,6 @@
$this->assertEqual(count($errors), 1, 'Error for the user being over their limit.', 'File');
$errors = file_validate_size($file, 1, 1);
$this->assertEqual(count($errors), 2, 'Errors for both the file and their limit.', 'File');
-
- $user = $original_user;
- drupal_save_session(TRUE);
}
}
@@ -975,6 +957,15 @@
$path = file_create_filename($basename, $directory);
$this->assertEqual($path, $expected, format_string('Creating a new filepath from %original equals %new.', array('%new' => $path, '%original' => $original)), 'File');
+ try {
+ $filename = "a\xFFtest\x80€.txt";
+ file_create_filename($filename, $directory);
+ $this->fail('Expected exception not thrown');
+ }
+ catch (RuntimeException $e) {
+ $this->assertEqual("Invalid filename '$filename'", $e->getMessage());
+ }
+
// @TODO: Finally we copy a file into a directory several times, to ensure a properly iterating filename suffix.
}
@@ -1007,6 +998,14 @@
$this->assertNotEqual($path, $destination, 'A new filepath destination is created when filepath destination already exists with FILE_EXISTS_RENAME.', 'File');
$path = file_destination($destination, FILE_EXISTS_ERROR);
$this->assertEqual($path, FALSE, 'An error is returned when filepath destination already exists with FILE_EXISTS_ERROR.', 'File');
+
+ try {
+ file_destination("core/misc/a\xFFtest\x80€.txt", FILE_EXISTS_REPLACE);
+ $this->fail('Expected exception not thrown');
+ }
+ catch (RuntimeException $e) {
+ $this->assertEqual("Invalid filename 'a\xFFtest\x80€.txt'", $e->getMessage());
+ }
}
/**
@@ -2564,6 +2563,7 @@
parent::setUp();
$this->bad_extension = 'php';
$this->name = $this->randomName() . '.' . $this->bad_extension . '.txt';
+ $this->name_with_uc_ext = $this->randomName() . '.' . strtoupper($this->bad_extension) . '.txt';
}
/**
@@ -2601,9 +2601,13 @@
* White listed extensions are ignored by file_munge_filename().
*/
function testMungeIgnoreWhitelisted() {
- // Declare our extension as whitelisted.
- $munged_name = file_munge_filename($this->name, $this->bad_extension);
- $this->assertIdentical($munged_name, $this->name, format_string('The new filename (%munged) matches the original (%original) once the extension has been whitelisted.', array('%munged' => $munged_name, '%original' => $this->name)));
+ // Declare our extension as whitelisted. The declared extensions should
+ // be case insensitive so test using one with a different case.
+ $munged_name = file_munge_filename($this->name_with_uc_ext, $this->bad_extension);
+ $this->assertIdentical($munged_name, $this->name_with_uc_ext, format_string('The new filename (%munged) matches the original (%original) once the extension has been whitelisted.', array('%munged' => $munged_name, '%original' => $this->name_with_uc_ext)));
+ // The allowed extensions should also be normalized.
+ $munged_name = file_munge_filename($this->name, strtoupper($this->bad_extension));
+ $this->assertIdentical($munged_name, $this->name, format_string('The new filename (%munged) matches the original (%original) also when the whitelisted extension is in uppercase.', array('%munged' => $munged_name, '%original' => $this->name)));
}
/**
@@ -2779,4 +2783,64 @@
$this->assertTrue(file_stream_wrapper_valid_scheme(file_uri_scheme('public://asdf')), 'Got a valid stream scheme from public://asdf');
$this->assertFalse(file_stream_wrapper_valid_scheme(file_uri_scheme('foo://asdf')), 'Did not get a valid stream scheme from foo://asdf');
}
+
+ /**
+ * Tests that phar stream wrapper is registered as expected.
+ *
+ * @see file_get_stream_wrappers()
+ */
+ public function testPharStreamWrapperRegistration() {
+ if (!class_exists('Phar', FALSE)) {
+ $this->assertFalse(in_array('phar', stream_get_wrappers(), TRUE), 'PHP is compiled without phar support. Therefore, no phar stream wrapper is registered.');
+ }
+ elseif (version_compare(PHP_VERSION, '5.3.3', '<')) {
+ $this->assertFalse(in_array('phar', stream_get_wrappers(), TRUE), 'The PHP version is <5.3.3. The built-in phar stream wrapper has been unregistered and not replaced.');
+ }
+ else {
+ $this->assertTrue(in_array('phar', stream_get_wrappers(), TRUE), 'A phar stream wrapper is registered.');
+ $this->assertFalse(file_stream_wrapper_valid_scheme('phar'), 'The phar scheme is not a valid scheme for Drupal File API usage.');
+ }
+
+ // Ensure that calling file_get_stream_wrappers() multiple times, both
+ // without and with a drupal_static_reset() in between, does not create
+ // errors due to the PharStreamWrapperManager singleton.
+ file_get_stream_wrappers();
+ file_get_stream_wrappers();
+ drupal_static_reset('file_get_stream_wrappers');
+ file_get_stream_wrappers();
+ }
+
+ /**
+ * Tests that only valid phar files can be used.
+ */
+ public function testPharFile() {
+ if (!in_array('phar', stream_get_wrappers(), TRUE)) {
+ $this->pass('There is no phar stream wrapper registered.');
+ // Nothing else in this test is relevant when there's no phar stream
+ // wrapper. testPharStreamWrapperRegistration() is sufficient for testing
+ // the conditions of when the stream wrapper should or should not be
+ // registered.
+ return;
+ }
+
+ $base = dirname(dirname(__FILE__)) . '/files';
+
+ // Ensure that file operations via the phar:// stream wrapper work for phar
+ // files with the .phar extension.
+ $this->assertFalse(file_exists("phar://$base/phar-1.phar/no-such-file.php"));
+ $this->assertTrue(file_exists("phar://$base/phar-1.phar/index.php"));
+ $file_contents = file_get_contents("phar://$base/phar-1.phar/index.php");
+ $expected_hash = 'c7e7904ea573c5ebea3ef00bb08c1f86af1a45961fbfbeb1892ff4a98fd73ad5';
+ $this->assertIdentical($expected_hash, hash('sha256', $file_contents));
+
+ // Ensure that file operations via the phar:// stream wrapper throw an
+ // exception for files without the .phar extension.
+ try {
+ file_exists("phar://$base/image-2.jpg/index.php");
+ $this->fail('Expected exception failed to be thrown when accessing an invalid phar file.');
+ }
+ catch (Exception $e) {
+ $this->assertEqual(get_class($e), 'TYPO3\PharStreamWrapper\Exception', 'Expected exception thrown when accessing an invalid phar file.');
+ }
+ }
}
diff -Naur drupal-7.25/modules/simpletest/tests/file_test.info drupal-7.66/modules/simpletest/tests/file_test.info
--- drupal-7.25/modules/simpletest/tests/file_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/file_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -6,8 +6,7 @@
files[] = file_test.module
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/filter_test.info drupal-7.66/modules/simpletest/tests/filter_test.info
--- drupal-7.25/modules/simpletest/tests/filter_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/filter_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
core = 7.x
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/form.test drupal-7.66/modules/simpletest/tests/form.test
--- drupal-7.25/modules/simpletest/tests/form.test 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/form.test 2019-04-17 22:20:46.000000000 +0200
@@ -470,6 +470,64 @@
$this->drupalPost(NULL, array('checkboxes[one]' => TRUE, 'checkboxes[two]' => TRUE), t('Submit'));
$this->assertText('An illegal choice has been detected.', 'Input forgery was detected.');
}
+
+ /**
+ * Tests that submitted values are converted to scalar strings for textfields.
+ */
+ public function testTextfieldStringValue() {
+ // Check multivalued submissions.
+ $multivalue = array('evil' => 'multivalue', 'not so' => 'good');
+ $this->checkFormValue('textfield', $multivalue, '');
+ $this->checkFormValue('password', $multivalue, '');
+ $this->checkFormValue('textarea', $multivalue, '');
+ $this->checkFormValue('machine_name', $multivalue, '');
+ $this->checkFormValue('password_confirm', $multivalue, array('pass1' => '', 'pass2' => ''));
+ // Check integer submissions.
+ $integer = 5;
+ $string = '5';
+ $this->checkFormValue('textfield', $integer, $string);
+ $this->checkFormValue('password', $integer, $string);
+ $this->checkFormValue('textarea', $integer, $string);
+ $this->checkFormValue('machine_name', $integer, $string);
+ $this->checkFormValue('password_confirm', array('pass1' => $integer, 'pass2' => $integer), array('pass1' => $string, 'pass2' => $string));
+ // Check that invalid array keys are ignored for password confirm elements.
+ $this->checkFormValue('password_confirm', array('pass1' => 'test', 'pass2' => 'test', 'extra' => 'invalid'), array('pass1' => 'test', 'pass2' => 'test'));
+ }
+
+ /**
+ * Checks that a given form input value is sanitized to the expected result.
+ *
+ * @param string $element_type
+ * The form element type. Example: textfield.
+ * @param mixed $input_value
+ * The submitted user input value for the form element.
+ * @param mixed $expected_value
+ * The sanitized result value in the form state after calling
+ * form_builder().
+ */
+ protected function checkFormValue($element_type, $input_value, $expected_value) {
+ $form_id = $this->randomName();
+ $form = array();
+ $form_state = form_state_defaults();
+ $form['op'] = array('#type' => 'submit', '#value' => t('Submit'));
+ $form[$element_type] = array(
+ '#type' => $element_type,
+ '#title' => 'test',
+ );
+
+ $form_state['input'][$element_type] = $input_value;
+ $form_state['input']['form_id'] = $form_id;
+ $form_state['method'] = 'post';
+ $form_state['values'] = array();
+ drupal_prepare_form($form_id, $form, $form_state);
+
+ // This is the main function we want to test: it is responsible for
+ // populating user supplied $form_state['input'] to sanitized
+ // $form_state['values'].
+ form_builder($form_id, $form, $form_state);
+
+ $this->assertIdentical($form_state['values'][$element_type], $expected_value, format_string('Form submission for the "@element_type" element type has been correctly sanitized.', array('@element_type' => $element_type)));
+ }
}
/**
@@ -633,6 +691,14 @@
}
/**
+ * Tests that a form with a disabled CSRF token can be validated.
+ */
+ function testDisabledToken() {
+ $this->drupalPost('form-test/validate-no-token', array(), 'Save');
+ $this->assertText('The form_test_validate_no_token form has been submitted successfully.');
+ }
+
+ /**
* Tests partial form validation through #limit_validation_errors.
*/
function testValidateLimitErrors() {
@@ -936,6 +1002,26 @@
$this->assertTrue(isset($errors['tableselect']), 'Option checker disallows invalid values for radio buttons.');
}
+ /**
+ * Test presence of ajax functionality
+ */
+ function testAjax() {
+ $rows = array('row1', 'row2', 'row3');
+ // Test checkboxes (#multiple == TRUE).
+ foreach ($rows as $row) {
+ $element = 'tableselect[' . $row . ']';
+ $edit = array($element => TRUE);
+ $result = $this->drupalPostAJAX('form_test/tableselect/multiple-true', $edit, $element);
+ $this->assertFalse(empty($result), t('Ajax triggers on checkbox for @row.', array('@row' => $row)));
+ }
+ // Test radios (#multiple == FALSE).
+ $element = 'tableselect';
+ foreach ($rows as $row) {
+ $edit = array($element => $row);
+ $result = $this->drupalPostAjax('form_test/tableselect/multiple-false', $edit, $element);
+ $this->assertFalse(empty($result), t('Ajax triggers on radio for @row.', array('@row' => $row)));
+ }
+ }
/**
* Helper function for the option check test to submit a form while collecting errors.
@@ -1156,6 +1242,235 @@
$this->assertText('State persisted.');
}
}
+
+ /**
+ * Verify that the form build-id remains the same when validation errors
+ * occur on a mutable form.
+ */
+ function testMutableForm() {
+ // Request the form with 'cache' query parameter to enable form caching.
+ $this->drupalGet('form_test/form-storage', array('query' => array('cache' => 1)));
+ $buildIdFields = $this->xpath('//input[@name="form_build_id"]');
+ $this->assertEqual(count($buildIdFields), 1, 'One form build id field on the page');
+ $buildId = (string) $buildIdFields[0]['value'];
+
+ // Trigger validation error by submitting an empty title.
+ $edit = array('title' => '');
+ $this->drupalPost(NULL, $edit, 'Continue submit');
+
+ // Verify that the build-id did not change.
+ $this->assertFieldByName('form_build_id', $buildId, 'Build id remains the same when form validation fails');
+ }
+
+ /**
+ * Verifies that form build-id is regenerated when loading an immutable form
+ * from the cache.
+ */
+ function testImmutableForm() {
+ // Request the form with 'cache' query parameter to enable form caching.
+ $this->drupalGet('form_test/form-storage', array('query' => array('cache' => 1, 'immutable' => 1)));
+ $buildIdFields = $this->xpath('//input[@name="form_build_id"]');
+ $this->assertEqual(count($buildIdFields), 1, 'One form build id field on the page');
+ $buildId = (string) $buildIdFields[0]['value'];
+
+ // Trigger validation error by submitting an empty title.
+ $edit = array('title' => '');
+ $this->drupalPost(NULL, $edit, 'Continue submit');
+
+ // Verify that the build-id did change.
+ $this->assertNoFieldByName('form_build_id', $buildId, 'Build id changes when form validation fails');
+
+ // Retrieve the new build-id.
+ $buildIdFields = $this->xpath('//input[@name="form_build_id"]');
+ $this->assertEqual(count($buildIdFields), 1, 'One form build id field on the page');
+ $buildId = (string) $buildIdFields[0]['value'];
+
+ // Trigger validation error by again submitting an empty title.
+ $edit = array('title' => '');
+ $this->drupalPost(NULL, $edit, 'Continue submit');
+
+ // Verify that the build-id does not change the second time.
+ $this->assertFieldByName('form_build_id', $buildId, 'Build id remains the same when form validation fails subsequently');
+ }
+
+ /**
+ * Verify that existing contrib code cannot overwrite immutable form state.
+ */
+ public function testImmutableFormLegacyProtection() {
+ $this->drupalGet('form_test/form-storage', array('query' => array('cache' => 1, 'immutable' => 1)));
+ $build_id_fields = $this->xpath('//input[@name="form_build_id"]');
+ $this->assertEqual(count($build_id_fields), 1, 'One form build id field on the page');
+ $build_id = (string) $build_id_fields[0]['value'];
+
+ // Try to poison the form cache.
+ $original = $this->drupalGetAJAX('form_test/form-storage-legacy/' . $build_id);
+ $this->assertEqual($original['form']['#build_id_old'], $build_id, 'Original build_id was recorded');
+ $this->assertNotEqual($original['form']['#build_id'], $build_id, 'New build_id was generated');
+
+ // Assert that a watchdog message was logged by form_set_cache.
+ $status = (bool) db_query_range('SELECT 1 FROM {watchdog} WHERE message = :message', 0, 1, array(':message' => 'Form build-id mismatch detected while attempting to store a form in the cache.'));
+ $this->assert($status, 'A watchdog message was logged by form_set_cache');
+
+ // Ensure that the form state was not poisoned by the preceeding call.
+ $original = $this->drupalGetAJAX('form_test/form-storage-legacy/' . $build_id);
+ $this->assertEqual($original['form']['#build_id_old'], $build_id, 'Original build_id was recorded');
+ $this->assertNotEqual($original['form']['#build_id'], $build_id, 'New build_id was generated');
+ $this->assert(empty($original['form']['#poisoned']), 'Original form structure was preserved');
+ $this->assert(empty($original['form_state']['poisoned']), 'Original form state was preserved');
+ }
+}
+
+/**
+ * Test the form storage when page caching for anonymous users is turned on.
+ */
+class FormsFormStoragePageCacheTestCase extends DrupalWebTestCase {
+ protected $profile = 'testing';
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Forms using form storage on cached pages',
+ 'description' => 'Tests a form using form storage and makes sure validation and caching works when page caching for anonymous users is turned on.',
+ 'group' => 'Form API',
+ );
+ }
+
+ public function setUp() {
+ parent::setUp('form_test');
+
+ variable_set('cache', TRUE);
+ }
+
+ /**
+ * Return the build id of the current form.
+ */
+ protected function getFormBuildId() {
+ $build_id_fields = $this->xpath('//input[@name="form_build_id"]');
+ $this->assertEqual(count($build_id_fields), 1, 'One form build id field on the page');
+ return (string) $build_id_fields[0]['value'];
+ }
+
+ /**
+ * Build-id is regenerated when validating cached form.
+ */
+ public function testValidateFormStorageOnCachedPage() {
+ $this->drupalGet('form_test/form-storage-page-cache');
+ $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Page was not cached.');
+ $this->assertText('No old build id', 'No old build id on the page');
+ $build_id_initial = $this->getFormBuildId();
+
+ // Trigger validation error by submitting an empty title.
+ $edit = array('title' => '');
+ $this->drupalPost(NULL, $edit, 'Save');
+ $this->assertText($build_id_initial, 'Old build id on the page');
+ $build_id_first_validation = $this->getFormBuildId();
+ $this->assertNotEqual($build_id_initial, $build_id_first_validation, 'Build id changes when form validation fails');
+
+ // Trigger validation error by again submitting an empty title.
+ $edit = array('title' => '');
+ $this->drupalPost(NULL, $edit, 'Save');
+ $this->assertText('No old build id', 'No old build id on the page');
+ $build_id_second_validation = $this->getFormBuildId();
+ $this->assertEqual($build_id_first_validation, $build_id_second_validation, 'Build id remains the same when form validation fails subsequently');
+
+ // Repeat the test sequence but this time with a page loaded from the cache.
+ $this->drupalGet('form_test/form-storage-page-cache');
+ $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
+ $this->assertText('No old build id', 'No old build id on the page');
+ $build_id_from_cache_initial = $this->getFormBuildId();
+ $this->assertEqual($build_id_initial, $build_id_from_cache_initial, 'Build id is the same as on the first request');
+
+ // Trigger validation error by submitting an empty title.
+ $edit = array('title' => '');
+ $this->drupalPost(NULL, $edit, 'Save');
+ $this->assertText($build_id_initial, 'Old build id is initial build id');
+ $build_id_from_cache_first_validation = $this->getFormBuildId();
+ $this->assertNotEqual($build_id_initial, $build_id_from_cache_first_validation, 'Build id changes when form validation fails');
+ $this->assertNotEqual($build_id_first_validation, $build_id_from_cache_first_validation, 'Build id from first user is not reused');
+
+ // Trigger validation error by again submitting an empty title.
+ $edit = array('title' => '');
+ $this->drupalPost(NULL, $edit, 'Save');
+ $this->assertText('No old build id', 'No old build id on the page');
+ $build_id_from_cache_second_validation = $this->getFormBuildId();
+ $this->assertEqual($build_id_from_cache_first_validation, $build_id_from_cache_second_validation, 'Build id remains the same when form validation fails subsequently');
+ }
+
+ /**
+ * Build-id is regenerated when rebuilding cached form.
+ */
+ public function testRebuildFormStorageOnCachedPage() {
+ $this->drupalGet('form_test/form-storage-page-cache');
+ $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Page was not cached.');
+ $this->assertText('No old build id', 'No old build id on the page');
+ $build_id_initial = $this->getFormBuildId();
+
+ // Trigger rebuild, should regenerate build id.
+ $edit = array('title' => 'something');
+ $this->drupalPost(NULL, $edit, 'Rebuild');
+ $this->assertText($build_id_initial, 'Initial build id as old build id on the page');
+ $build_id_first_rebuild = $this->getFormBuildId();
+ $this->assertNotEqual($build_id_initial, $build_id_first_rebuild, 'Build id changes on first rebuild.');
+
+ // Trigger subsequent rebuild, should regenerate the build id again.
+ $edit = array('title' => 'something');
+ $this->drupalPost(NULL, $edit, 'Rebuild');
+ $this->assertText($build_id_first_rebuild, 'First build id as old build id on the page');
+ $build_id_second_rebuild = $this->getFormBuildId();
+ $this->assertNotEqual($build_id_first_rebuild, $build_id_second_rebuild, 'Build id changes on second rebuild.');
+ }
+}
+
+/**
+ * Test cache_form.
+ */
+class FormsFormCacheTestCase extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Form caching',
+ 'description' => 'Tests storage and retrieval of forms from cache.',
+ 'group' => 'Form API',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('form_test');
+ }
+
+ /**
+ * Tests storing and retrieving the form from cache.
+ */
+ function testCacheForm() {
+ $form = drupal_get_form('form_test_cache_form');
+ $form_state = array('foo' => 'bar', 'build_info' => array('baz'));
+ form_set_cache($form['#build_id'], $form, $form_state);
+
+ $cached_form_state = array();
+ $cached_form = form_get_cache($form['#build_id'], $cached_form_state);
+
+ $this->assertEqual($cached_form['#build_id'], $form['#build_id'], 'Form retrieved from cache_form successfully.');
+ $this->assertEqual($cached_form_state['foo'], 'bar', 'Data retrieved from cache_form successfully.');
+ }
+
+ /**
+ * Tests changing form_cache_expiration.
+ */
+ function testCacheFormCustomExpiration() {
+ variable_set('form_cache_expiration', -1 * (24 * 60 * 60));
+
+ $form = drupal_get_form('form_test_cache_form');
+ $form_state = array('foo' => 'bar', 'build_info' => array('baz'));
+ form_set_cache($form['#build_id'], $form, $form_state);
+
+ // Clear expired entries from cache_form, which should include the entry we
+ // just stored. Without this, the form will still be retrieved from cache.
+ cache_clear_all(NULL, 'cache_form');
+
+ $cached_form_state = array();
+ $cached_form = form_get_cache($form['#build_id'], $cached_form_state);
+
+ $this->assertNull($cached_form, 'Expired form was not returned from cache.');
+ $this->assertTrue(empty($cached_form_state), 'No data retrieved from cache for expired form.');
+ }
}
/**
@@ -1486,6 +1801,16 @@
$this->submitForm(array('textfield' => 'dummy value', 'checkboxes' => array(1 => NULL, 2 => 2)), TRUE);
$this->submitForm(array('textfield' => 'dummy value', 'checkboxes' => array(1 => NULL, 2 => NULL)), TRUE);
+ // Test that a programmatic form submission can successfully submit values
+ // even for fields where the #access property is FALSE.
+ $this->submitForm(array('textfield' => 'dummy value', 'textfield_no_access' => 'test value'), TRUE);
+ // Test that #access is respected for programmatic form submissions when
+ // requested to do so.
+ $submitted_values = array('textfield' => 'dummy value', 'textfield_no_access' => 'test value');
+ $expected_values = array('textfield' => 'dummy value', 'textfield_no_access' => 'default value');
+ $form_state = array('programmed_bypass_access_check' => FALSE);
+ $this->submitForm($submitted_values, TRUE, $expected_values, $form_state);
+
// Test that a programmatic form submission can correctly click a button
// that limits validation errors based on user input. Since we do not
// submit any values for "textfield" here and the textfield is required, we
@@ -1508,10 +1833,18 @@
* @param $valid_input
* A boolean indicating whether or not the form submission is expected to
* be valid.
+ * @param $expected_values
+ * (Optional) An array of field values that are expected to be stored by
+ * the form submit handler. If not set, the submitted $values are assumed
+ * to also be the expected stored values.
+ * @param $form_state
+ * (Optional) A keyed array containing the state of the form, to be sent in
+ * the call to drupal_form_submit(). The $values parameter is added to
+ * $form_state['values'] by default, if it is not already set.
*/
- private function submitForm($values, $valid_input) {
+ private function submitForm($values, $valid_input, $expected_values = NULL, $form_state = array()) {
// Programmatically submit the given values.
- $form_state = array('values' => $values);
+ $form_state += array('values' => $values);
drupal_form_submit('form_test_programmatic_form', $form_state);
// Check that the form returns an error when expected, and vice versa.
@@ -1528,7 +1861,10 @@
// By fetching the values from $form_state['storage'] we ensure that the
// submission handler was properly executed.
$stored_values = $form_state['storage']['programmatic_form_submit'];
- foreach ($values as $key => $value) {
+ if (!isset($expected_values)) {
+ $expected_values = $values;
+ }
+ foreach ($expected_values as $key => $value) {
$this->assertTrue(isset($stored_values[$key]) && $stored_values[$key] == $value, format_string('Submission handler correctly executed: %stored_key is %stored_value', array('%stored_key' => $key, '%stored_value' => print_r($value, TRUE))));
}
}
@@ -1844,3 +2180,36 @@
$this->assertNoDuplicateIds('There are no duplicate IDs');
}
}
+
+/**
+ * Tests for form textarea.
+ */
+class FormTextareaTestCase extends DrupalUnitTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Form textarea',
+ 'description' => 'Tests form textarea related functions.',
+ 'group' => 'Form API',
+ );
+ }
+
+ /**
+ * Tests that textarea value is properly set.
+ */
+ public function testValueCallback() {
+ $element = array();
+ $form_state = array();
+ $test_cases = array(
+ array(NULL, FALSE),
+ array(NULL, NULL),
+ array('', array('test')),
+ array('test', 'test'),
+ array('123', 123),
+ );
+ foreach ($test_cases as $test_case) {
+ list($expected, $input) = $test_case;
+ $this->assertIdentical($expected, form_type_textarea_value($element, $input, $form_state));
+ }
+ }
+}
diff -Naur drupal-7.25/modules/simpletest/tests/form_test.info drupal-7.66/modules/simpletest/tests/form_test.info
--- drupal-7.25/modules/simpletest/tests/form_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/form_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
core = 7.x
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/form_test.module drupal-7.66/modules/simpletest/tests/form_test.module
--- drupal-7.25/modules/simpletest/tests/form_test.module 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/form_test.module 2019-04-17 22:20:46.000000000 +0200
@@ -37,6 +37,13 @@
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
+ $items['form-test/validate-no-token'] = array(
+ 'title' => 'Form validation without a CSRF token',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('form_test_validate_no_token'),
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ );
$items['form-test/limit-validation-errors'] = array(
'title' => 'Form validation with some error suppression',
'page callback' => 'drupal_get_form',
@@ -90,6 +97,21 @@
'type' => MENU_CALLBACK,
);
+ $items['form_test/form-storage-legacy'] = array(
+ 'title' => 'Emulate legacy AHAH-style ajax callback',
+ 'page callback' => 'form_test_storage_legacy_handler',
+ 'access arguments' => array('access content'),
+ 'type' => MENU_CALLBACK,
+ );
+
+ $items['form_test/form-storage-page-cache'] = array(
+ 'title' => 'Form storage with page cache test',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('form_test_storage_page_cache_form'),
+ 'access arguments' => array('access content'),
+ 'type' => MENU_CALLBACK,
+ );
+
$items['form_test/wrapper-callback'] = array(
'title' => 'Form wrapper callback test',
'page callback' => 'form_test_wrapper_callback',
@@ -440,6 +462,27 @@
}
/**
+ * Form builder for testing submission of a form without a CSRF token.
+ */
+function form_test_validate_no_token($form, &$form_state) {
+ $form['submit'] = array(
+ '#type' => 'submit',
+ '#value' => 'Save',
+ );
+
+ $form['#token'] = FALSE;
+
+ return $form;
+}
+
+/**
+ * Form submission handler for form_test_validate_no_token().
+ */
+function form_test_validate_no_token_submit($form, &$form_state) {
+ drupal_set_message('The form_test_validate_no_token form has been submitted successfully.');
+}
+
+/**
* Builds a simple form with a button triggering partial validation.
*/
function form_test_limit_validation_errors_form($form, &$form_state) {
@@ -574,11 +617,17 @@
$form['tableselect'] = $element_properties;
$form['tableselect'] += array(
+ '#prefix' => '',
+ '#suffix' => '',
'#type' => 'tableselect',
'#header' => $header,
'#options' => $options,
'#multiple' => FALSE,
'#empty' => t('Empty text.'),
+ '#ajax' => array(
+ 'callback' => '_form_test_tableselect_ajax_callback',
+ 'wrapper' => 'tableselect-wrapper',
+ ),
);
$form['submit'] = array(
@@ -683,6 +732,13 @@
}
/**
+* Ajax callback that returns the form element.
+*/
+function _form_test_tableselect_ajax_callback($form, &$form_state) {
+ return $form['tableselect'];
+}
+
+/**
* A multistep form for testing the form storage.
*
* It uses two steps for editing a virtual "thing". Any changes to it are saved
@@ -746,10 +802,37 @@
$form_state['cache'] = TRUE;
}
+ if (isset($_REQUEST['immutable'])) {
+ $form_state['build_info']['immutable'] = TRUE;
+ }
+
return $form;
}
/**
+ * Emulate legacy AHAH-style ajax callback.
+ *
+ * Drupal 6 AHAH callbacks used to operate directly on forms retrieved using
+ * form_get_cache and stored using form_set_cache after manipulation. This
+ * callback helps testing whether form_set_cache prevents resaving of immutable
+ * forms.
+ */
+function form_test_storage_legacy_handler($form_build_id) {
+ $form_state = array();
+ $form = form_get_cache($form_build_id, $form_state);
+
+ drupal_json_output(array(
+ 'form' => $form,
+ 'form_state' => $form_state,
+ ));
+
+ $form['#poisoned'] = TRUE;
+ $form_state['poisoned'] = TRUE;
+
+ form_set_cache($form_build_id, $form, $form_state);
+}
+
+/**
* Form element validation handler for 'value' element in form_test_storage_form().
*
* Tests updating of cached form storage during validation.
@@ -786,6 +869,74 @@
}
/**
+ * A simple form for testing form storage when page caching is enabled.
+ */
+function form_test_storage_page_cache_form($form, &$form_state) {
+ $form['title'] = array(
+ '#type' => 'textfield',
+ '#title' => 'Title',
+ '#required' => TRUE,
+ );
+
+ $form['test_build_id_old'] = array(
+ '#type' => 'item',
+ '#title' => 'Old build id',
+ '#markup' => 'No old build id',
+ );
+
+ $form['submit'] = array(
+ '#type' => 'submit',
+ '#value' => 'Save',
+ );
+
+ $form['rebuild'] = array(
+ '#type' => 'submit',
+ '#value' => 'Rebuild',
+ '#submit' => array('form_test_storage_page_cache_rebuild'),
+ );
+
+ $form['#after_build'] = array('form_test_storage_page_cache_old_build_id');
+ $form_state['cache'] = TRUE;
+
+ return $form;
+}
+
+/**
+ * Form element #after_build callback: output the old form build-id.
+ */
+function form_test_storage_page_cache_old_build_id($form) {
+ if (isset($form['#build_id_old'])) {
+ $form['test_build_id_old']['#markup'] = check_plain($form['#build_id_old']);
+ }
+ return $form;
+}
+
+/**
+ * Form submit callback: Rebuild the form and continue.
+ */
+function form_test_storage_page_cache_rebuild($form, &$form_state) {
+ $form_state['rebuild'] = TRUE;
+}
+
+/**
+ * A simple form for testing form caching.
+ */
+function form_test_cache_form($form, &$form_state) {
+ $form['title'] = array(
+ '#type' => 'textfield',
+ '#title' => 'Title',
+ '#required' => TRUE,
+ );
+
+ $form['submit'] = array(
+ '#type' => 'submit',
+ '#value' => 'Save',
+ );
+
+ return $form;
+}
+
+/**
* A form for testing form labels and required marks.
*/
function form_label_test_form() {
@@ -1548,6 +1699,15 @@
'#default_value' => array(1, 2),
);
+ // This is used to test that programmatic form submissions can bypass #access
+ // restrictions.
+ $form['textfield_no_access'] = array(
+ '#type' => 'textfield',
+ '#title' => 'Textfield no access',
+ '#default_value' => 'default value',
+ '#access' => FALSE,
+ );
+
$form['field_to_validate'] = array(
'#type' => 'radios',
'#title' => 'Field to validate (in the case of limited validation)',
diff -Naur drupal-7.25/modules/simpletest/tests/image.test drupal-7.66/modules/simpletest/tests/image.test
--- drupal-7.25/modules/simpletest/tests/image.test 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/image.test 2019-04-17 22:20:46.000000000 +0200
@@ -207,9 +207,11 @@
protected $green = array(0, 255, 0, 0);
protected $blue = array(0, 0, 255, 0);
protected $yellow = array(255, 255, 0, 0);
- protected $fuchsia = array(255, 0, 255, 0); // Used as background colors.
- protected $transparent = array(0, 0, 0, 127);
protected $white = array(255, 255, 255, 0);
+ protected $transparent = array(0, 0, 0, 127);
+ // Used as rotate background colors.
+ protected $fuchsia = array(255, 0, 255, 0);
+ protected $rotate_transparent = array(255, 255, 255, 127);
protected $width = 40;
protected $height = 20;
@@ -261,6 +263,7 @@
*/
function testManipulations() {
// If GD isn't available don't bother testing this.
+ module_load_include('inc', 'system', 'image.gd');
if (!function_exists('image_gd_check_settings') || !image_gd_check_settings()) {
$this->pass(t('Image manipulations for the GD toolkit were skipped because the GD toolkit is not available.'));
return;
@@ -274,6 +277,7 @@
$files = array(
'image-test.png',
'image-test.gif',
+ 'image-test-no-transparency.gif',
'image-test.jpg',
);
@@ -331,15 +335,10 @@
);
// Systems using non-bundled GD2 don't have imagerotate. Test if available.
- if (function_exists('imagerotate')) {
+ // @todo Remove the version check once https://www.drupal.org/node/2918570
+ // is resolved.
+ if (function_exists('imagerotate') && (version_compare(PHP_VERSION, '7.0.26', '<') || (version_compare(PHP_VERSION, '7.1', '>=') && version_compare(PHP_VERSION, '7.1.12', '<')))) {
$operations += array(
- 'rotate_5' => array(
- 'function' => 'rotate',
- 'arguments' => array(5, 0xFF00FF), // Fuchsia background.
- 'width' => 42,
- 'height' => 24,
- 'corners' => array_fill(0, 4, $this->fuchsia),
- ),
'rotate_90' => array(
'function' => 'rotate',
'arguments' => array(90, 0xFF00FF), // Fuchsia background.
@@ -347,13 +346,6 @@
'height' => 40,
'corners' => array($this->fuchsia, $this->red, $this->green, $this->blue),
),
- 'rotate_transparent_5' => array(
- 'function' => 'rotate',
- 'arguments' => array(5),
- 'width' => 42,
- 'height' => 24,
- 'corners' => array_fill(0, 4, $this->transparent),
- ),
'rotate_transparent_90' => array(
'function' => 'rotate',
'arguments' => array(90),
@@ -362,6 +354,49 @@
'corners' => array($this->transparent, $this->red, $this->green, $this->blue),
),
);
+ // As of PHP version 5.5, GD uses a different algorithm to rotate images
+ // than version 5.4 and below, resulting in different dimensions.
+ // See https://bugs.php.net/bug.php?id=65148.
+ // For the 40x20 test images, the dimensions resulting from rotation will
+ // be 1 pixel smaller in both width and height in PHP 5.5 and above.
+ // @todo: The PHP bug was fixed in PHP 7.0.26 and 7.1.12. Change the code
+ // below to reflect that in https://www.drupal.org/node/2918570.
+ if (version_compare(PHP_VERSION, '5.5', '>=')) {
+ $operations += array(
+ 'rotate_5' => array(
+ 'function' => 'rotate',
+ 'arguments' => array(5, 0xFF00FF), // Fuchsia background.
+ 'width' => 41,
+ 'height' => 23,
+ 'corners' => array_fill(0, 4, $this->fuchsia),
+ ),
+ 'rotate_transparent_5' => array(
+ 'function' => 'rotate',
+ 'arguments' => array(5),
+ 'width' => 41,
+ 'height' => 23,
+ 'corners' => array_fill(0, 4, $this->rotate_transparent),
+ ),
+ );
+ }
+ else {
+ $operations += array(
+ 'rotate_5' => array(
+ 'function' => 'rotate',
+ 'arguments' => array(5, 0xFF00FF), // Fuchsia background.
+ 'width' => 42,
+ 'height' => 24,
+ 'corners' => array_fill(0, 4, $this->fuchsia),
+ ),
+ 'rotate_transparent_5' => array(
+ 'function' => 'rotate',
+ 'arguments' => array(5),
+ 'width' => 42,
+ 'height' => 24,
+ 'corners' => array_fill(0, 4, $this->rotate_transparent),
+ ),
+ );
+ }
}
// Systems using non-bundled GD2 don't have imagefilter. Test if available.
@@ -379,7 +414,7 @@
array_fill(0, 3, 76) + array(3 => 0),
array_fill(0, 3, 149) + array(3 => 0),
array_fill(0, 3, 29) + array(3 => 0),
- array_fill(0, 3, 0) + array(3 => 127)
+ array_fill(0, 3, 225) + array(3 => 127)
),
),
);
@@ -394,11 +429,14 @@
continue 2;
}
- // Transparent GIFs and the imagefilter function don't work together.
- // There is a todo in image.gd.inc to correct this.
+ // All images should be converted to truecolor when loaded.
+ $image_truecolor = imageistruecolor($image->resource);
+ $this->assertTrue($image_truecolor, format_string('Image %file after load is a truecolor image.', array('%file' => $file)));
+
if ($image->info['extension'] == 'gif') {
if ($op == 'desaturate') {
- $values['corners'][3] = $this->white;
+ // Transparent GIFs and the imagefilter function don't work together.
+ $values['corners'][3][3] = 0;
}
}
@@ -426,6 +464,11 @@
}
// Now check each of the corners to ensure color correctness.
foreach ($values['corners'] as $key => $corner) {
+ // The test gif that does not have transparency has yellow where the
+ // others have transparent.
+ if ($file === 'image-test-no-transparency.gif' && $corner === $this->transparent) {
+ $corner = $this->yellow;
+ }
// Get the location of the corner.
switch ($key) {
case 0:
@@ -451,7 +494,8 @@
$directory = file_default_scheme() . '://imagetests';
file_prepare_directory($directory, FILE_CREATE_DIRECTORY);
- image_save($image, $directory . '/' . $op . '.' . $image->info['extension']);
+ $file_path = $directory . '/' . $op . '.' . $image->info['extension'];
+ image_save($image, $file_path);
$this->assertTrue($correct_dimensions_real, format_string('Image %file after %action action has proper dimensions.', array('%file' => $file, '%action' => $op)));
$this->assertTrue($correct_dimensions_object, format_string('Image %file object after %action action is reporting the proper height and width values.', array('%file' => $file, '%action' => $op)));
@@ -460,8 +504,37 @@
$this->assertTrue($correct_colors, format_string('Image %file object after %action action has the correct color placement.', array('%file' => $file, '%action' => $op)));
}
}
+
+ // Check that saved image reloads without raising PHP errors.
+ $image_reloaded = image_load($file_path);
}
+ }
+ /**
+ * Tests loading an image whose transparent color index is out of range.
+ */
+ function testTransparentColorOutOfRange() {
+ // This image was generated by taking an initial image with a palette size
+ // of 6 colors, and setting the transparent color index to 6 (one higher
+ // than the largest allowed index), as follows:
+ // @code
+ // $image = imagecreatefromgif('modules/simpletest/files/image-test.gif');
+ // imagecolortransparent($image, 6);
+ // imagegif($image, 'modules/simpletest/files/image-test-transparent-out-of-range.gif');
+ // @endcode
+ // This allows us to test that an image with an out-of-range color index
+ // can be loaded correctly.
+ $file = 'image-test-transparent-out-of-range.gif';
+ $image = image_load(drupal_get_path('module', 'simpletest') . '/files/' . $file);
+
+ if (!$image) {
+ $this->fail(format_string('Could not load image %file.', array('%file' => $file)));
+ }
+ else {
+ // All images should be converted to truecolor when loaded.
+ $image_truecolor = imageistruecolor($image->resource);
+ $this->assertTrue($image_truecolor, format_string('Image %file after load is a truecolor image.', array('%file' => $file)));
+ }
}
}
diff -Naur drupal-7.25/modules/simpletest/tests/image_test.info drupal-7.66/modules/simpletest/tests/image_test.info
--- drupal-7.25/modules/simpletest/tests/image_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/image_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
core = 7.x
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/mail.test drupal-7.66/modules/simpletest/tests/mail.test
--- drupal-7.25/modules/simpletest/tests/mail.test 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/mail.test 2019-04-17 22:20:46.000000000 +0200
@@ -441,7 +441,7 @@
* is 1000 characters."
*/
function testVeryLongLineWrap() {
- $input = 'Drupal
' . str_repeat('x', 2100) . '>
Drupal';
+ $input = 'Drupal
' . str_repeat('x', 2100) . '
Drupal';
$output = drupal_html_to_text($input);
// This awkward construct comes from includes/mail.inc lines 8-13.
$eol = variable_get('mail_line_endings', MAIL_LINE_ENDINGS);
@@ -455,7 +455,6 @@
$maximum_line_length = max($maximum_line_length, strlen($line . $eol));
}
$verbose = 'Maximum line length found was ' . $maximum_line_length . ' octets.';
- // @todo This should assert that $maximum_line_length <= 1000.
- $this->pass($verbose);
+ $this->assertTrue($maximum_line_length <= 1000, $verbose);
}
}
diff -Naur drupal-7.25/modules/simpletest/tests/menu_test.info drupal-7.66/modules/simpletest/tests/menu_test.info
--- drupal-7.25/modules/simpletest/tests/menu_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/menu_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
core = 7.x
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/module.test drupal-7.66/modules/simpletest/tests/module.test
--- drupal-7.25/modules/simpletest/tests/module.test 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/module.test 2019-04-17 22:20:46.000000000 +0200
@@ -302,3 +302,45 @@
$this->assertEqual(0, $count, 'Permissions were all removed.');
}
}
+
+class ModuleImplementsAlterTestCase extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Module implements alter',
+ 'description' => 'Tests hook_module_implements_alter().',
+ 'group' => 'Module',
+ );
+ }
+
+ /**
+ * Tests hook_module_implements_alter() adding an implementation.
+ */
+ function testModuleImplementsAlter() {
+ module_enable(array('module_test'), FALSE);
+ $this->assertTrue(module_exists('module_test'), 'Test module is enabled.');
+
+ // Assert that module_test.module is now included.
+ $this->assertTrue(function_exists('module_test_permission'),
+ 'The file module_test.module was successfully included.');
+
+ $modules = module_implements('permission');
+ $this->assertTrue(in_array('module_test', $modules), 'module_test implements hook_permission.');
+
+ $modules = module_implements('module_implements_alter');
+ $this->assertTrue(in_array('module_test', $modules), 'module_test implements hook_module_implements_alter().');
+
+ // Assert that module_test.implementations.inc is not included yet.
+ $this->assertFalse(function_exists('module_test_altered_test_hook'),
+ 'The file module_test.implementations.inc is not included yet.');
+
+ // Assert that module_test_module_implements_alter(*, 'altered_test_hook')
+ // has added an implementation
+ $this->assertTrue(in_array('module_test', module_implements('altered_test_hook')),
+ 'module_test implements hook_altered_test_hook().');
+
+ // Assert that module_test.implementations.inc was included as part of the process.
+ $this->assertTrue(function_exists('module_test_altered_test_hook'),
+ 'The file module_test.implementations.inc was included.');
+ }
+
+}
diff -Naur drupal-7.25/modules/simpletest/tests/module_test.implementations.inc drupal-7.66/modules/simpletest/tests/module_test.implementations.inc
--- drupal-7.25/modules/simpletest/tests/module_test.implementations.inc 1970-01-01 01:00:00.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/module_test.implementations.inc 2019-04-17 22:20:46.000000000 +0200
@@ -0,0 +1,10 @@
+assertFalse(user_needs_new_hash($account), 'Re-hashed password does not need a new hash.');
$this->assertTrue(user_check_password($password, $account), 'Password check succeeds with re-hashed password.');
}
+
+ /**
+ * Verifies that passwords longer than 512 bytes are not hashed.
+ */
+ public function testLongPassword() {
+ $password = str_repeat('x', 512);
+ $result = user_hash_password($password);
+ $this->assertFalse(empty($result), '512 byte long password is allowed.');
+ $password = str_repeat('x', 513);
+ $result = user_hash_password($password);
+ $this->assertFalse($result, '513 byte long password is not allowed.');
+ // Check a string of 3-byte UTF-8 characters.
+ $password = str_repeat('€', 170);
+ $result = user_hash_password($password);
+ $this->assertFalse(empty($result), '510 byte long password is allowed.');
+ $password .= 'xx';
+ $this->assertFalse(empty($result), '512 byte long password is allowed.');
+ $password = str_repeat('€', 171);
+ $result = user_hash_password($password);
+ $this->assertFalse($result, '513 byte long password is not allowed.');
+ }
}
diff -Naur drupal-7.25/modules/simpletest/tests/path_test.info drupal-7.66/modules/simpletest/tests/path_test.info
--- drupal-7.25/modules/simpletest/tests/path_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/path_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
core = 7.x
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/psr_0_test/psr_0_test.info drupal-7.66/modules/simpletest/tests/psr_0_test/psr_0_test.info
--- drupal-7.25/modules/simpletest/tests/psr_0_test/psr_0_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/psr_0_test/psr_0_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
hidden = TRUE
package = Testing
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/psr_4_test/psr_4_test.info drupal-7.66/modules/simpletest/tests/psr_4_test/psr_4_test.info
--- drupal-7.25/modules/simpletest/tests/psr_4_test/psr_4_test.info 1970-01-01 01:00:00.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/psr_4_test/psr_4_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -0,0 +1,11 @@
+name = PSR-4 Test cases
+description = Test classes to be discovered by simpletest.
+core = 7.x
+
+hidden = TRUE
+package = Testing
+
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
+project = "drupal"
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/psr_4_test/psr_4_test.module drupal-7.66/modules/simpletest/tests/psr_4_test/psr_4_test.module
--- drupal-7.25/modules/simpletest/tests/psr_4_test/psr_4_test.module 1970-01-01 01:00:00.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/psr_4_test/psr_4_test.module 2019-04-17 22:20:46.000000000 +0200
@@ -0,0 +1 @@
+ 'PSR4 example test: PSR-4 in disabled modules.',
+ 'description' => 'We want to assert that this test case is being discovered.',
+ 'group' => 'SimpleTest',
+ );
+ }
+
+ function testArithmetics() {
+ $this->assert(1 + 1 == 2, '1 + 1 == 2');
+ }
+}
diff -Naur drupal-7.25/modules/simpletest/tests/psr_4_test/src/Tests/Nested/NestedExampleTest.php drupal-7.66/modules/simpletest/tests/psr_4_test/src/Tests/Nested/NestedExampleTest.php
--- drupal-7.25/modules/simpletest/tests/psr_4_test/src/Tests/Nested/NestedExampleTest.php 1970-01-01 01:00:00.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/psr_4_test/src/Tests/Nested/NestedExampleTest.php 2019-04-17 22:20:46.000000000 +0200
@@ -0,0 +1,18 @@
+ 'PSR4 example test: PSR-4 in nested subfolders.',
+ 'description' => 'We want to assert that this PSR-4 test case is being discovered.',
+ 'group' => 'SimpleTest',
+ );
+ }
+
+ function testArithmetics() {
+ $this->assert(1 + 1 == 2, '1 + 1 == 2');
+ }
+}
diff -Naur drupal-7.25/modules/simpletest/tests/requirements1_test.info drupal-7.66/modules/simpletest/tests/requirements1_test.info
--- drupal-7.25/modules/simpletest/tests/requirements1_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/requirements1_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
core = 7.x
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/requirements2_test.info drupal-7.66/modules/simpletest/tests/requirements2_test.info
--- drupal-7.25/modules/simpletest/tests/requirements2_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/requirements2_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -7,8 +7,7 @@
core = 7.x
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/session.test drupal-7.66/modules/simpletest/tests/session.test
--- drupal-7.25/modules/simpletest/tests/session.test 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/session.test 2019-04-17 22:20:46.000000000 +0200
@@ -478,6 +478,56 @@
}
/**
+ * Tests that empty session IDs do not cause unrelated sessions to load.
+ */
+ public function testEmptySessionId() {
+ global $is_https;
+
+ if ($is_https) {
+ $secure_session_name = session_name();
+ }
+ else {
+ $secure_session_name = 'S' . session_name();
+ }
+
+ // Enable mixed mode for HTTP and HTTPS.
+ variable_set('https', TRUE);
+
+ $admin_user = $this->drupalCreateUser(array('access administration pages'));
+ $standard_user = $this->drupalCreateUser(array('access content'));
+
+ // First log in as the admin user on HTTP.
+ // We cannot use $this->drupalLogin() here because we need to use the
+ // special http.php URLs.
+ $edit = array(
+ 'name' => $admin_user->name,
+ 'pass' => $admin_user->pass_raw
+ );
+ $this->drupalGet('user');
+ $form = $this->xpath('//form[@id="user-login"]');
+ $form[0]['action'] = $this->httpUrl('user');
+ $this->drupalPost(NULL, $edit, t('Log in'));
+
+ $this->curlClose();
+
+ // Now start a session for the standard user on HTTPS.
+ $edit = array(
+ 'name' => $standard_user->name,
+ 'pass' => $standard_user->pass_raw
+ );
+ $this->drupalGet('user');
+ $form = $this->xpath('//form[@id="user-login"]');
+ $form[0]['action'] = $this->httpsUrl('user');
+ $this->drupalPost(NULL, $edit, t('Log in'));
+
+ // Make the secure session cookie blank.
+ curl_setopt($this->curlHandle, CURLOPT_COOKIE, "$secure_session_name=");
+ $this->drupalGet($this->httpsUrl('user'));
+ $this->assertNoText($admin_user->name, 'User is not logged in as admin');
+ $this->assertNoText($standard_user->name, "The user's own name is not displayed because the invalid session cookie has logged them out.");
+ }
+
+ /**
* Test that there exists a session with two specific session IDs.
*
* @param $sid
diff -Naur drupal-7.25/modules/simpletest/tests/session_test.info drupal-7.66/modules/simpletest/tests/session_test.info
--- drupal-7.25/modules/simpletest/tests/session_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/session_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
core = 7.x
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/system_dependencies_test.info drupal-7.66/modules/simpletest/tests/system_dependencies_test.info
--- drupal-7.25/modules/simpletest/tests/system_dependencies_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/system_dependencies_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -6,8 +6,7 @@
hidden = TRUE
dependencies[] = _missing_dependency
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/system_incompatible_core_version_dependencies_test.info drupal-7.66/modules/simpletest/tests/system_incompatible_core_version_dependencies_test.info
--- drupal-7.25/modules/simpletest/tests/system_incompatible_core_version_dependencies_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/system_incompatible_core_version_dependencies_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -6,8 +6,7 @@
hidden = TRUE
dependencies[] = system_incompatible_core_version_test
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/system_incompatible_core_version_test.info drupal-7.66/modules/simpletest/tests/system_incompatible_core_version_test.info
--- drupal-7.25/modules/simpletest/tests/system_incompatible_core_version_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/system_incompatible_core_version_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
core = 5.x
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/system_incompatible_module_version_dependencies_test.info drupal-7.66/modules/simpletest/tests/system_incompatible_module_version_dependencies_test.info
--- drupal-7.25/modules/simpletest/tests/system_incompatible_module_version_dependencies_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/system_incompatible_module_version_dependencies_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -7,8 +7,7 @@
; system_incompatible_module_version_test declares version 1.0
dependencies[] = system_incompatible_module_version_test (>2.0)
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/system_incompatible_module_version_test.info drupal-7.66/modules/simpletest/tests/system_incompatible_module_version_test.info
--- drupal-7.25/modules/simpletest/tests/system_incompatible_module_version_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/system_incompatible_module_version_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
core = 7.x
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/system_project_namespace_test.info drupal-7.66/modules/simpletest/tests/system_project_namespace_test.info
--- drupal-7.25/modules/simpletest/tests/system_project_namespace_test.info 1970-01-01 01:00:00.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/system_project_namespace_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -0,0 +1,12 @@
+name = "System project namespace test"
+description = "Support module for testing project namespace dependencies."
+package = Testing
+version = VERSION
+core = 7.x
+hidden = TRUE
+dependencies[] = drupal:filter
+
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
+project = "drupal"
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/system_project_namespace_test.module drupal-7.66/modules/simpletest/tests/system_project_namespace_test.module
--- drupal-7.25/modules/simpletest/tests/system_project_namespace_test.module 1970-01-01 01:00:00.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/system_project_namespace_test.module 2019-04-17 22:20:46.000000000 +0200
@@ -0,0 +1 @@
+ MENU_CALLBACK,
);
+ $items['system-test/drupal-set-message'] = array(
+ 'title' => 'Set messages with drupal_set_message()',
+ 'page callback' => 'system_test_drupal_set_message',
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ );
+
$items['system-test/main-content-handling'] = array(
'title' => 'Test main content handling',
'page callback' => 'system_test_main_content_fallback',
@@ -106,6 +113,34 @@
'type' => MENU_CALLBACK,
);
+ $items['system-test/get-destination'] = array(
+ 'title' => 'Test $_GET[\'destination\']',
+ 'page callback' => 'system_test_get_destination',
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ );
+
+ $items['system-test/request-destination'] = array(
+ 'title' => 'Test $_REQUEST[\'destination\']',
+ 'page callback' => 'system_test_request_destination',
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ );
+
+ $items['system-test/drupal-get-filename'] = array(
+ 'title' => 'Test drupal_get_filename()',
+ 'page callback' => 'system_test_drupal_get_filename',
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ );
+
+ $items['system-test/drupal-get-filename-with-schema-rebuild'] = array(
+ 'title' => 'Test drupal_get_filename() with a schema rebuild',
+ 'page callback' => 'system_test_drupal_get_filename_with_schema_rebuild',
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ );
+
return $items;
}
@@ -114,8 +149,23 @@
}
function system_test_basic_auth_page() {
- $output = t('$_SERVER[\'PHP_AUTH_USER\'] is @username.', array('@username' => $_SERVER['PHP_AUTH_USER']));
- $output .= t('$_SERVER[\'PHP_AUTH_PW\'] is @password.', array('@password' => $_SERVER['PHP_AUTH_PW']));
+ // The Authorization HTTP header is forwarded via Drupal's .htaccess file even
+ // for PHP CGI SAPIs.
+ if (isset($_SERVER['HTTP_AUTHORIZATION'])) {
+ $authorization_header = $_SERVER['HTTP_AUTHORIZATION'];
+ }
+ // If using CGI on Apache with mod_rewrite, the forwarded HTTP header appears
+ // in the redirected HTTP headers. See
+ // https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpFoundation/ServerBag.php#L61
+ elseif (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) {
+ $authorization_header = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
+ }
+ // Resemble PHP_AUTH_USER and PHP_AUTH_PW for a Basic authentication from
+ // the HTTP_AUTHORIZATION header. See
+ // http://www.php.net/manual/features.http-auth.php
+ list($user, $pw) = explode(':', base64_decode(substr($authorization_header, 6)));
+ $output = t('Username is @username.', array('@username' => $user));
+ $output .= t('Password is @password.', array('@password' => $pw));
return $output;
}
@@ -260,6 +310,9 @@
}
}
+ if ($file->name == 'system_project_namespace_test') {
+ $info['hidden'] = FALSE;
+ }
// Make the system_dependencies_test visible by default.
if ($file->name == 'system_dependencies_test') {
$info['hidden'] = FALSE;
@@ -405,3 +458,114 @@
system_authorized_init('system_test_authorize_run', drupal_get_path('module', 'system_test') . '/system_test.module', array(), $page_title);
drupal_goto($authorize_url);
}
+
+/**
+ * Sets two messages and removes the first one before the messages are displayed.
+ */
+function system_test_drupal_set_message() {
+ // Set two messages.
+ drupal_set_message('First message (removed).');
+ drupal_set_message('Second message (not removed).');
+
+ // Remove the first.
+ unset($_SESSION['messages']['status'][0]);
+
+ return '';
+}
+
+/**
+ * Page callback to print out $_GET['destination'] for testing.
+ */
+function system_test_get_destination() {
+ if (isset($_GET['destination'])) {
+ print $_GET['destination'];
+ }
+ // No need to render the whole page, we are just interested in this bit of
+ // information.
+ exit;
+}
+
+/**
+ * Page callback to print out $_REQUEST['destination'] for testing.
+ */
+function system_test_request_destination() {
+ if (isset($_REQUEST['destination'])) {
+ print $_REQUEST['destination'];
+ }
+ // No need to render the whole page, we are just interested in this bit of
+ // information.
+ exit;
+}
+
+/**
+ * Page callback to run drupal_get_filename() on a particular module.
+ */
+function system_test_drupal_get_filename() {
+ // Prevent SimpleTest from failing as a result of the expected PHP warnings
+ // this function causes. Any warnings will be recorded in the database logs
+ // for examination by the tests.
+ define('SIMPLETEST_COLLECT_ERRORS', FALSE);
+
+ $module_name = variable_get('system_test_drupal_get_filename_test_module_name');
+ drupal_get_filename('module', $module_name);
+
+ return '';
+}
+
+/**
+ * Page callback to run drupal_get_filename() and do a schema rebuild.
+ */
+function system_test_drupal_get_filename_with_schema_rebuild() {
+ // Prevent SimpleTest from failing as a result of the expected PHP warnings
+ // this function causes.
+ define('SIMPLETEST_COLLECT_ERRORS', FALSE);
+
+ // Record the original database tables from drupal_get_schema().
+ variable_set('system_test_drupal_get_filename_with_schema_rebuild_original_tables', array_keys(drupal_get_schema(NULL, TRUE)));
+
+ // Trigger system_test_schema() and system_test_watchdog() to perform an
+ // attempted recursive rebuild when drupal_get_schema() is called. See
+ // BootstrapGetFilenameWebTestCase::testRecursiveRebuilds().
+ variable_set('system_test_drupal_get_filename_attempt_recursive_rebuild', TRUE);
+ drupal_get_schema(NULL, TRUE);
+
+ return '';
+}
+
+/**
+ * Implements hook_watchdog().
+ */
+function system_test_watchdog($log_entry) {
+ // If an attempted recursive schema rebuild has been triggered by
+ // system_test_drupal_get_filename_with_schema_rebuild(), perform the rebuild
+ // in response to the missing file message triggered by system_test_schema().
+ if (!variable_get('system_test_drupal_get_filename_attempt_recursive_rebuild')) {
+ return;
+ }
+ if ($log_entry['type'] != 'php' || $log_entry['severity'] != WATCHDOG_WARNING) {
+ return;
+ }
+ $module_name = variable_get('system_test_drupal_get_filename_test_module_name');
+ if (!isset($log_entry['variables']['!message']) || strpos($log_entry['variables']['!message'], format_string('The following module is missing from the file system: %name', array('%name' => $module_name))) === FALSE) {
+ return;
+ }
+ variable_set('system_test_drupal_get_filename_with_schema_rebuild_final_tables', array_keys(drupal_get_schema()));
+}
+
+/**
+ * Implements hook_module_implements_alter().
+ */
+function system_test_module_implements_alter(&$implementations, $hook) {
+ // For BootstrapGetFilenameWebTestCase::testRecursiveRebuilds() to work
+ // correctly, this module's hook_schema() implementation cannot be either the
+ // first implementation (since that would trigger a potential recursive
+ // rebuild before anything is in the drupal_get_schema() cache) or the last
+ // implementation (since that would trigger a potential recursive rebuild
+ // after the cache is already complete). So put it somewhere in the middle.
+ if ($hook == 'schema') {
+ $group = $implementations['system_test'];
+ unset($implementations['system_test']);
+ $count = count($implementations);
+ $implementations = array_merge(array_slice($implementations, 0, $count / 2, TRUE), array('system_test' => $group), array_slice($implementations, $count / 2, NULL, TRUE));
+ }
+}
diff -Naur drupal-7.25/modules/simpletest/tests/taxonomy_test.info drupal-7.66/modules/simpletest/tests/taxonomy_test.info
--- drupal-7.25/modules/simpletest/tests/taxonomy_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/taxonomy_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -6,8 +6,7 @@
hidden = TRUE
dependencies[] = taxonomy
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/taxonomy_test.module drupal-7.66/modules/simpletest/tests/taxonomy_test.module
--- drupal-7.25/modules/simpletest/tests/taxonomy_test.module 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/taxonomy_test.module 2019-04-17 22:20:46.000000000 +0200
@@ -109,3 +109,33 @@
->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);
+ }
+}
diff -Naur drupal-7.25/modules/simpletest/tests/theme.test drupal-7.66/modules/simpletest/tests/theme.test
--- drupal-7.25/modules/simpletest/tests/theme.test 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/theme.test 2019-04-17 22:20:46.000000000 +0200
@@ -155,6 +155,15 @@
$this->assertNotEqual(theme_get_setting('subtheme_override', 'test_basetheme'), theme_get_setting('subtheme_override', 'test_subtheme'), 'Base theme\'s default settings values can be overridden by subtheme.');
$this->assertIdentical(theme_get_setting('basetheme_only', 'test_subtheme'), 'base theme value', 'Base theme\'s default settings values are inherited by subtheme.');
}
+
+ /**
+ * Test the drupal_add_region_content() function.
+ */
+ function testDrupalAddRegionContent() {
+ $this->drupalGet('theme-test/drupal-add-region-content');
+ $this->assertText('Hello');
+ $this->assertText('World');
+ }
}
/**
@@ -425,28 +434,100 @@
}
/**
- * Unit tests for theme_html_tag().
+ * Tests the markup of core render element types passed to drupal_render().
*/
-class ThemeHtmlTag extends DrupalUnitTestCase {
+class RenderElementTypesTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
- 'name' => 'Theme HTML Tag',
- 'description' => 'Tests theme_html_tag() built-in theme functions.',
+ 'name' => 'Render element types',
+ 'description' => 'Tests the markup of core render element types passed to drupal_render().',
'group' => 'Theme',
);
}
/**
- * Test function theme_html_tag()
+ * Asserts that an array of elements is rendered properly.
+ *
+ * @param array $elements
+ * An array of associative arrays describing render elements and their
+ * expected markup. Each item in $elements must contain the following:
+ * - 'name': This human readable description will be displayed on the test
+ * results page.
+ * - 'value': This is the render element to test.
+ * - 'expected': This is the expected markup for the element in 'value'.
+ */
+ function assertElements($elements) {
+ foreach($elements as $element) {
+ $this->assertIdentical(drupal_render($element['value']), $element['expected'], '"' . $element['name'] . '" input rendered correctly by drupal_render().');
+ }
+ }
+
+ /**
+ * Tests system #type 'container'.
+ */
+ function testContainer() {
+ $elements = array(
+ // Basic container with no attributes.
+ array(
+ 'name' => "#type 'container' with no HTML attributes",
+ 'value' => array(
+ '#type' => 'container',
+ 'child' => array(
+ '#markup' => 'foo',
+ ),
+ ),
+ 'expected' => 'foo',
+ ),
+ // Container with a class.
+ array(
+ 'name' => "#type 'container' with a class HTML attribute",
+ 'value' => array(
+ '#type' => 'container',
+ 'child' => array(
+ '#markup' => 'foo',
+ ),
+ '#attributes' => array(
+ 'class' => 'bar',
+ ),
+ ),
+ 'expected' => ' ',
+ ),
+ );
+
+ $this->assertElements($elements);
+ }
+
+ /**
+ * Tests system #type 'html_tag'.
*/
- function testThemeHtmlTag() {
- // Test auto-closure meta tag generation
- $tag['element'] = array('#tag' => 'meta', '#attributes' => array('name' => 'description', 'content' => 'Drupal test'));
- $this->assertEqual(''."\n", theme_html_tag($tag), 'Test auto-closure meta tag generation.');
-
- // Test title tag generation
- $tag['element'] = array('#tag' => 'title', '#value' => 'title test');
- $this->assertEqual('title test '."\n", theme_html_tag($tag), 'Test title tag generation.');
+ function testHtmlTag() {
+ $elements = array(
+ // Test auto-closure meta tag generation.
+ array(
+ 'name' => "#type 'html_tag' auto-closure meta tag generation",
+ 'value' => array(
+ '#type' => 'html_tag',
+ '#tag' => 'meta',
+ '#attributes' => array(
+ 'name' => 'description',
+ 'content' => 'Drupal test',
+ ),
+ ),
+ 'expected' => '' . "\n",
+ ),
+ // Test title tag generation.
+ array(
+ 'name' => "#type 'html_tag' title tag generation",
+ 'value' => array(
+ '#type' => 'html_tag',
+ '#tag' => 'title',
+ '#value' => 'title test',
+ ),
+ 'expected' => 'title test ' . "\n",
+ ),
+ );
+
+ $this->assertElements($elements);
}
}
@@ -500,3 +581,99 @@
$this->assertTrue($registry['theme_test_template_test_2'], 'Offset was returned correctly from the theme registry');
}
}
+
+/**
+ * Tests for theme debug markup.
+ */
+class ThemeDebugMarkupTestCase extends DrupalWebTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Theme debug markup',
+ 'description' => 'Tests theme debug markup output.',
+ 'group' => 'Theme',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('theme_test', 'node');
+ theme_enable(array('test_theme'));
+ }
+
+ /**
+ * Tests debug markup added to template output.
+ */
+ function testDebugOutput() {
+ variable_set('theme_default', 'test_theme');
+ // Enable the debug output.
+ variable_set('theme_debug', TRUE);
+
+ $registry = theme_get_registry();
+ $extension = '.tpl.php';
+ // Populate array of templates.
+ $templates = drupal_find_theme_templates($registry, $extension, drupal_get_path('theme', 'test_theme'));
+ $templates += drupal_find_theme_templates($registry, $extension, drupal_get_path('module', 'node'));
+
+ // Create a node and test different features of the debug markup.
+ $node = $this->drupalCreateNode();
+ $this->drupalGet('node/' . $node->nid);
+ $this->assertRaw('', 'Theme debug markup found in theme output when debug is enabled.');
+ $this->assertRaw("CALL: theme('node')", 'Theme call information found.');
+ $this->assertRaw('x node--1' . $extension . PHP_EOL . ' * node--page' . $extension . PHP_EOL . ' * node' . $extension, 'Suggested template files found in order and node ID specific template shown as current template.');
+ $template_filename = $templates['node__1']['path'] . '/' . $templates['node__1']['template'] . $extension;
+ $this->assertRaw("BEGIN OUTPUT from '$template_filename'", 'Full path to current template file found.');
+
+ // Create another node and make sure the template suggestions shown in the
+ // debug markup are correct.
+ $node2 = $this->drupalCreateNode();
+ $this->drupalGet('node/' . $node2->nid);
+ $this->assertRaw('* node--2' . $extension . PHP_EOL . ' * node--page' . $extension . PHP_EOL . ' x node' . $extension, 'Suggested template files found in order and base template shown as current template.');
+
+ // Create another node and make sure the template suggestions shown in the
+ // debug markup are correct.
+ $node3 = $this->drupalCreateNode();
+ $build = array('#theme' => 'node__foo__bar');
+ $build += node_view($node3);
+ $output = drupal_render($build);
+ $this->assertTrue(strpos($output, "CALL: theme('node__foo__bar')") !== FALSE, 'Theme call information found.');
+ $this->assertTrue(strpos($output, '* node--foo--bar' . $extension . PHP_EOL . ' * node--foo' . $extension . PHP_EOL . ' * node--3' . $extension . PHP_EOL . ' * node--page' . $extension . PHP_EOL . ' x node' . $extension) !== FALSE, 'Suggested template files found in order and base template shown as current template.');
+
+ // Disable theme debug.
+ variable_set('theme_debug', FALSE);
+
+ $this->drupalGet('node/' . $node->nid);
+ $this->assertNoRaw('', 'Theme debug markup not found in theme output when debug is disabled.');
+ }
+
+}
+
+/**
+ * Tests module-provided theme engines.
+ */
+class ModuleProvidedThemeEngineTestCase extends DrupalWebTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Theme engine test',
+ 'description' => 'Tests module-provided theme engines.',
+ 'group' => 'Theme',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('theme_test');
+ theme_enable(array('test_theme', 'test_theme_nyan_cat'));
+ }
+
+ /**
+ * Ensures that the module provided theme engine is found and used by core.
+ */
+ function testEngineIsFoundAndWorking() {
+ variable_set('theme_default', 'test_theme_nyan_cat');
+ variable_set('admin_theme', 'test_theme_nyan_cat');
+
+ $this->drupalGet('theme-test/engine-info-test');
+ $this->assertText('Miaou');
+ }
+
+}
diff -Naur drupal-7.25/modules/simpletest/tests/theme_test.info drupal-7.66/modules/simpletest/tests/theme_test.info
--- drupal-7.25/modules/simpletest/tests/theme_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/theme_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
core = 7.x
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/theme_test.module drupal-7.66/modules/simpletest/tests/theme_test.module
--- drupal-7.25/modules/simpletest/tests/theme_test.module 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/theme_test.module 2019-04-17 22:20:46.000000000 +0200
@@ -27,10 +27,19 @@
$themes['test_theme'] = drupal_get_path('module', 'theme_test') . '/themes/test_theme/test_theme.info';
$themes['test_basetheme'] = drupal_get_path('module', 'theme_test') . '/themes/test_basetheme/test_basetheme.info';
$themes['test_subtheme'] = drupal_get_path('module', 'theme_test') . '/themes/test_subtheme/test_subtheme.info';
+ $themes['test_theme_nyan_cat'] = drupal_get_path('module', 'theme_test') . '/themes/test_theme_nyan_cat/test_theme_nyan_cat.info';
return $themes;
}
/**
+ * Implements hook_system_theme_engine_info().
+ */
+function theme_test_system_theme_engine_info() {
+ $theme_engines['nyan_cat'] = drupal_get_path('module', 'theme_test') . '/themes/engines/nyan_cat/nyan_cat.engine';
+ return $theme_engines;
+}
+
+/**
* Implements hook_menu().
*/
function theme_test_menu() {
@@ -53,6 +62,17 @@
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
+ $items['theme-test/drupal-add-region-content'] = array(
+ 'page callback' => '_theme_test_drupal_add_region_content',
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ );
+ $items['theme-test/engine-info-test'] = array(
+ 'description' => "Serves a simple page rendered using a Nyan Cat theme engine template.",
+ 'page callback' => '_theme_test_engine_info_test',
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ );
return $items;
}
@@ -127,6 +147,23 @@
}
/**
+ * Page callback, calls drupal_add_region_content.
+ */
+function _theme_test_drupal_add_region_content() {
+ drupal_add_region_content('content', 'World');
+ return 'Hello';
+}
+
+/**
+ * Serves a simple page renderered using a Nyan Cat theme engine template.
+ */
+function _theme_test_engine_info_test() {
+ return array(
+ '#markup' => theme('theme_test_template_test'),
+ );
+}
+
+/**
* Theme function for testing theme('theme_test_foo').
*/
function theme_theme_test_foo($variables) {
diff -Naur drupal-7.25/modules/simpletest/tests/themes/engines/nyan_cat/nyan_cat.engine drupal-7.66/modules/simpletest/tests/themes/engines/nyan_cat/nyan_cat.engine
--- drupal-7.25/modules/simpletest/tests/themes/engines/nyan_cat/nyan_cat.engine 1970-01-01 01:00:00.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/themes/engines/nyan_cat/nyan_cat.engine 2019-04-17 22:20:46.000000000 +0200
@@ -0,0 +1,53 @@
+filename) . '/template.theme';
+ if (file_exists($file)) {
+ include_once DRUPAL_ROOT . '/' . $file;
+ }
+}
+
+/**
+ * Implements hook_theme().
+ */
+function nyan_cat_theme($existing, $type, $theme, $path) {
+ $templates = drupal_find_theme_functions($existing, array($theme));
+ $templates += drupal_find_theme_templates($existing, '.nyan-cat.html', $path);
+ return $templates;
+}
+
+/**
+ * Implements hook_extension().
+ */
+function nyan_cat_extension() {
+ return '.nyan-cat.html';
+}
+
+/**
+ * Implements hook_render_template().
+ *
+ * @param string $template_file
+ * The filename of the template to render.
+ * @param mixed[] $variables
+ * A keyed array of variables that will appear in the output.
+ *
+ * @return string
+ * The output generated by the template.
+ */
+function nyan_cat_render_template($template_file, $variables) {
+ $output = str_replace('div', 'nyancat', file_get_contents(DRUPAL_ROOT . '/' . $template_file));
+ foreach ($variables as $key => $variable) {
+ if (strpos($output, '9' . $key) !== FALSE) {
+ $output = str_replace('9' . $key, $variable, $output);
+ }
+ }
+ return $output;
+}
diff -Naur drupal-7.25/modules/simpletest/tests/themes/test_basetheme/test_basetheme.info drupal-7.66/modules/simpletest/tests/themes/test_basetheme/test_basetheme.info
--- drupal-7.25/modules/simpletest/tests/themes/test_basetheme/test_basetheme.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/themes/test_basetheme/test_basetheme.info 2019-04-17 22:39:36.000000000 +0200
@@ -6,8 +6,7 @@
settings[basetheme_only] = base theme value
settings[subtheme_override] = base theme value
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/themes/test_subtheme/test_subtheme.info drupal-7.66/modules/simpletest/tests/themes/test_subtheme/test_subtheme.info
--- drupal-7.25/modules/simpletest/tests/themes/test_subtheme/test_subtheme.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/themes/test_subtheme/test_subtheme.info 2019-04-17 22:39:36.000000000 +0200
@@ -6,8 +6,7 @@
settings[subtheme_override] = subtheme value
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/themes/test_theme/templates/node--1.tpl.php drupal-7.66/modules/simpletest/tests/themes/test_theme/templates/node--1.tpl.php
--- drupal-7.25/modules/simpletest/tests/themes/test_theme/templates/node--1.tpl.php 1970-01-01 01:00:00.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/themes/test_theme/templates/node--1.tpl.php 2019-04-17 22:20:46.000000000 +0200
@@ -0,0 +1,2 @@
+
+Node Content Dummy
diff -Naur drupal-7.25/modules/simpletest/tests/themes/test_theme/test_theme.info drupal-7.66/modules/simpletest/tests/themes/test_theme/test_theme.info
--- drupal-7.25/modules/simpletest/tests/themes/test_theme/test_theme.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/themes/test_theme/test_theme.info 2019-04-17 22:39:36.000000000 +0200
@@ -17,8 +17,7 @@
settings[theme_test_setting] = default value
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/themes/test_theme/theme-settings.php drupal-7.66/modules/simpletest/tests/themes/test_theme/theme-settings.php
--- drupal-7.25/modules/simpletest/tests/themes/test_theme/theme-settings.php 1970-01-01 01:00:00.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/themes/test_theme/theme-settings.php 2019-04-17 22:20:46.000000000 +0200
@@ -0,0 +1,32 @@
+ 'checkbox',
+ '#title' => 'Test theme checkbox',
+ '#default_value' => theme_get_setting('test_theme_checkbox'),
+ );
+
+ // Force the form to be cached so we can test that this file is properly
+ // loaded and the custom submit handler is properly called even on a cached
+ // form build.
+ $form_state['cache'] = TRUE;
+ $form['#submit'][] = 'test_theme_form_system_theme_settings_submit';
+}
+
+/**
+ * Form submission handler for the test theme settings form.
+ *
+ * @see test_theme_form_system_theme_settings_alter()
+ */
+function test_theme_form_system_theme_settings_submit($form, &$form_state) {
+ drupal_set_message('The test theme setting was saved.');
+}
diff -Naur drupal-7.25/modules/simpletest/tests/themes/test_theme_nyan_cat/templates/theme_test_template_test.nyan-cat.html drupal-7.66/modules/simpletest/tests/themes/test_theme_nyan_cat/templates/theme_test_template_test.nyan-cat.html
--- drupal-7.25/modules/simpletest/tests/themes/test_theme_nyan_cat/templates/theme_test_template_test.nyan-cat.html 1970-01-01 01:00:00.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/themes/test_theme_nyan_cat/templates/theme_test_template_test.nyan-cat.html 2019-04-17 22:20:46.000000000 +0200
@@ -0,0 +1 @@
+Miaou
\ No newline at end of file
diff -Naur drupal-7.25/modules/simpletest/tests/themes/test_theme_nyan_cat/test_theme_nyan_cat.info drupal-7.66/modules/simpletest/tests/themes/test_theme_nyan_cat/test_theme_nyan_cat.info
--- drupal-7.25/modules/simpletest/tests/themes/test_theme_nyan_cat/test_theme_nyan_cat.info 1970-01-01 01:00:00.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/themes/test_theme_nyan_cat/test_theme_nyan_cat.info 2019-04-17 22:39:36.000000000 +0200
@@ -0,0 +1,10 @@
+name = Nyan cat engine based test theme
+description = Theme for testing the module-provided theme engines.
+core = 7.x
+hidden = TRUE
+engine = nyan_cat
+
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
+project = "drupal"
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/update_script_test.info drupal-7.66/modules/simpletest/tests/update_script_test.info
--- drupal-7.25/modules/simpletest/tests/update_script_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/update_script_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
core = 7.x
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/update_script_test.install drupal-7.66/modules/simpletest/tests/update_script_test.install
--- drupal-7.25/modules/simpletest/tests/update_script_test.install 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/update_script_test.install 2019-04-17 22:20:46.000000000 +0200
@@ -31,6 +31,19 @@
'severity' => REQUIREMENT_ERROR,
);
break;
+ case REQUIREMENT_INFO:
+ $requirements['update_script_test_stop'] = array(
+ 'title' => 'Update script test stop',
+ 'value' => 'Error',
+ 'description' => 'This is a requirements error provided by the update_script_test module to stop the page redirect for the info.',
+ 'severity' => REQUIREMENT_ERROR,
+ );
+ $requirements['update_script_test'] = array(
+ 'title' => 'Update script test',
+ 'description' => 'This is a requirements info provided by the update_script_test module.',
+ 'severity' => REQUIREMENT_INFO,
+ );
+ break;
}
}
diff -Naur drupal-7.25/modules/simpletest/tests/update_test_1.info drupal-7.66/modules/simpletest/tests/update_test_1.info
--- drupal-7.25/modules/simpletest/tests/update_test_1.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/update_test_1.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
core = 7.x
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/update_test_2.info drupal-7.66/modules/simpletest/tests/update_test_2.info
--- drupal-7.25/modules/simpletest/tests/update_test_2.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/update_test_2.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
core = 7.x
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/update_test_3.info drupal-7.66/modules/simpletest/tests/update_test_3.info
--- drupal-7.25/modules/simpletest/tests/update_test_3.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/update_test_3.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
core = 7.x
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/upgrade/drupal-6.filled.database.php drupal-7.66/modules/simpletest/tests/upgrade/drupal-6.filled.database.php
--- drupal-7.25/modules/simpletest/tests/upgrade/drupal-6.filled.database.php 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/upgrade/drupal-6.filled.database.php 2019-04-17 22:20:46.000000000 +0200
@@ -19919,7 +19919,7 @@
'vid' => '1',
'name' => 'vocabulary 1 (i=0)',
'description' => 'description of vocabulary 1 (i=0)',
- 'help' => '',
+ 'help' => 'help for vocabulary 1 (i=0)',
'relations' => '1',
'hierarchy' => '0',
'multiple' => '0',
@@ -19932,7 +19932,7 @@
'vid' => '2',
'name' => 'vocabulary 2 (i=1)',
'description' => 'description of vocabulary 2 (i=1)',
- 'help' => '',
+ 'help' => 'help for vocabulary 2 (i=1)',
'relations' => '1',
'hierarchy' => '1',
'multiple' => '1',
@@ -19945,7 +19945,7 @@
'vid' => '3',
'name' => 'vocabulary 3 (i=2)',
'description' => 'description of vocabulary 3 (i=2)',
- 'help' => '',
+ 'help' => 'help for vocabulary 3 (i=2)',
'relations' => '1',
'hierarchy' => '2',
'multiple' => '0',
@@ -19958,7 +19958,7 @@
'vid' => '4',
'name' => 'vocabulary 4 (i=3)',
'description' => 'description of vocabulary 4 (i=3)',
- 'help' => '',
+ 'help' => 'help for vocabulary 4 (i=3)',
'relations' => '1',
'hierarchy' => '0',
'multiple' => '1',
@@ -19971,7 +19971,7 @@
'vid' => '5',
'name' => 'vocabulary 5 (i=4)',
'description' => 'description of vocabulary 5 (i=4)',
- 'help' => '',
+ 'help' => 'help for vocabulary 5 (i=4)',
'relations' => '1',
'hierarchy' => '1',
'multiple' => '0',
@@ -19984,7 +19984,7 @@
'vid' => '6',
'name' => 'vocabulary 6 (i=5)',
'description' => 'description of vocabulary 6 (i=5)',
- 'help' => '',
+ 'help' => 'help for vocabulary 6 (i=5)',
'relations' => '1',
'hierarchy' => '2',
'multiple' => '1',
@@ -19997,7 +19997,7 @@
'vid' => '7',
'name' => 'vocabulary 7 (i=6)',
'description' => 'description of vocabulary 7 (i=6)',
- 'help' => '',
+ 'help' => 'help for vocabulary 7 (i=6)',
'relations' => '1',
'hierarchy' => '0',
'multiple' => '0',
@@ -20010,7 +20010,7 @@
'vid' => '8',
'name' => 'vocabulary 8 (i=7)',
'description' => 'description of vocabulary 8 (i=7)',
- 'help' => '',
+ 'help' => 'help for vocabulary 8 (i=7)',
'relations' => '1',
'hierarchy' => '1',
'multiple' => '1',
@@ -20023,7 +20023,7 @@
'vid' => '9',
'name' => 'vocabulary 9 (i=8)',
'description' => 'description of vocabulary 9 (i=8)',
- 'help' => '',
+ 'help' => 'help for vocabulary 9 (i=8)',
'relations' => '1',
'hierarchy' => '2',
'multiple' => '0',
@@ -20036,7 +20036,7 @@
'vid' => '10',
'name' => 'vocabulary 10 (i=9)',
'description' => 'description of vocabulary 10 (i=9)',
- 'help' => '',
+ 'help' => 'help for vocabulary 10 (i=9)',
'relations' => '1',
'hierarchy' => '0',
'multiple' => '1',
@@ -20049,7 +20049,7 @@
'vid' => '11',
'name' => 'vocabulary 11 (i=10)',
'description' => 'description of vocabulary 11 (i=10)',
- 'help' => '',
+ 'help' => 'help for vocabulary 11 (i=10)',
'relations' => '1',
'hierarchy' => '1',
'multiple' => '0',
@@ -20062,7 +20062,7 @@
'vid' => '12',
'name' => 'vocabulary 12 (i=11)',
'description' => 'description of vocabulary 12 (i=11)',
- 'help' => '',
+ 'help' => 'help for vocabulary 12 (i=11)',
'relations' => '1',
'hierarchy' => '2',
'multiple' => '1',
@@ -20075,7 +20075,7 @@
'vid' => '13',
'name' => 'vocabulary 13 (i=12)',
'description' => 'description of vocabulary 13 (i=12)',
- 'help' => '',
+ 'help' => 'help for vocabulary 13 (i=12)',
'relations' => '1',
'hierarchy' => '0',
'multiple' => '0',
@@ -20088,7 +20088,7 @@
'vid' => '14',
'name' => 'vocabulary 14 (i=13)',
'description' => 'description of vocabulary 14 (i=13)',
- 'help' => '',
+ 'help' => 'help for vocabulary 14 (i=13)',
'relations' => '1',
'hierarchy' => '1',
'multiple' => '1',
@@ -20101,7 +20101,7 @@
'vid' => '15',
'name' => 'vocabulary 15 (i=14)',
'description' => 'description of vocabulary 15 (i=14)',
- 'help' => '',
+ 'help' => 'help for vocabulary 15 (i=14)',
'relations' => '1',
'hierarchy' => '2',
'multiple' => '0',
@@ -20114,7 +20114,7 @@
'vid' => '16',
'name' => 'vocabulary 16 (i=15)',
'description' => 'description of vocabulary 16 (i=15)',
- 'help' => '',
+ 'help' => 'help for vocabulary 16 (i=15)',
'relations' => '1',
'hierarchy' => '0',
'multiple' => '1',
@@ -20127,7 +20127,7 @@
'vid' => '17',
'name' => 'vocabulary 17 (i=16)',
'description' => 'description of vocabulary 17 (i=16)',
- 'help' => '',
+ 'help' => 'help for vocabulary 17 (i=16)',
'relations' => '1',
'hierarchy' => '1',
'multiple' => '0',
@@ -20140,7 +20140,7 @@
'vid' => '18',
'name' => 'vocabulary 18 (i=17)',
'description' => 'description of vocabulary 18 (i=17)',
- 'help' => '',
+ 'help' => 'help for vocabulary 18 (i=17)',
'relations' => '1',
'hierarchy' => '2',
'multiple' => '1',
@@ -20153,7 +20153,7 @@
'vid' => '19',
'name' => 'vocabulary 19 (i=18)',
'description' => 'description of vocabulary 19 (i=18)',
- 'help' => '',
+ 'help' => 'help for vocabulary 19 (i=18)',
'relations' => '1',
'hierarchy' => '0',
'multiple' => '0',
@@ -20166,7 +20166,7 @@
'vid' => '20',
'name' => 'vocabulary 20 (i=19)',
'description' => 'description of vocabulary 20 (i=19)',
- 'help' => '',
+ 'help' => 'help for vocabulary 20 (i=19)',
'relations' => '1',
'hierarchy' => '1',
'multiple' => '1',
@@ -20179,7 +20179,7 @@
'vid' => '21',
'name' => 'vocabulary 21 (i=20)',
'description' => 'description of vocabulary 21 (i=20)',
- 'help' => '',
+ 'help' => 'help for vocabulary 21 (i=20)',
'relations' => '1',
'hierarchy' => '2',
'multiple' => '0',
@@ -20192,7 +20192,7 @@
'vid' => '22',
'name' => 'vocabulary 22 (i=21)',
'description' => 'description of vocabulary 22 (i=21)',
- 'help' => '',
+ 'help' => 'help for vocabulary 22 (i=21)',
'relations' => '1',
'hierarchy' => '0',
'multiple' => '1',
@@ -20205,7 +20205,7 @@
'vid' => '23',
'name' => 'vocabulary 23 (i=22)',
'description' => 'description of vocabulary 23 (i=22)',
- 'help' => '',
+ 'help' => 'help for vocabulary 23 (i=22)',
'relations' => '1',
'hierarchy' => '1',
'multiple' => '0',
@@ -20218,7 +20218,7 @@
'vid' => '24',
'name' => 'vocabulary 24 (i=23)',
'description' => 'description of vocabulary 24 (i=23)',
- 'help' => '',
+ 'help' => 'help for vocabulary 24 (i=23)',
'relations' => '1',
'hierarchy' => '2',
'multiple' => '1',
diff -Naur drupal-7.25/modules/simpletest/tests/upgrade/drupal-6.upload.database.php drupal-7.66/modules/simpletest/tests/upgrade/drupal-6.upload.database.php
--- drupal-7.25/modules/simpletest/tests/upgrade/drupal-6.upload.database.php 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/upgrade/drupal-6.upload.database.php 2019-04-17 22:20:46.000000000 +0200
@@ -127,6 +127,38 @@
'status' => '1',
'timestamp' => '1285708958',
))
+// On some Drupal 6 sites, more than one file can have the same filepath. See
+// https://www.drupal.org/node/1260938.
+->values(array(
+ 'fid' => '12',
+ 'uid' => '1',
+ 'filename' => 'duplicate-name.png',
+ 'filepath' => 'sites/default/files/duplicate-name.png',
+ 'filemime' => 'image/png',
+ 'filesize' => '314',
+ 'status' => '1',
+ 'timestamp' => '1285708958',
+))
+->values(array(
+ 'fid' => '13',
+ 'uid' => '1',
+ 'filename' => 'duplicate-name.png',
+ 'filepath' => 'sites/default/files/duplicate-name.png',
+ 'filemime' => 'image/png',
+ 'filesize' => '315',
+ 'status' => '1',
+ 'timestamp' => '1285708958',
+))
+->values(array(
+ 'fid' => '14',
+ 'uid' => '1',
+ 'filename' => 'duplicate-name.png',
+ 'filepath' => 'sites/default/files/duplicate-name.png',
+ 'filemime' => 'image/png',
+ 'filesize' => '316',
+ 'status' => '1',
+ 'timestamp' => '1285708958',
+))
->execute();
db_insert('node')->fields(array(
@@ -197,6 +229,23 @@
'tnid' => '0',
'translate' => '0',
))
+->values(array(
+ 'nid' => '41',
+ 'vid' => '55',
+ 'type' => 'page',
+ 'language' => '',
+ 'title' => 'node title 41 revision 55',
+ 'uid' => '1',
+ 'status' => '1',
+ 'created' => '1285709012',
+ 'changed' => '1285709012',
+ 'comment' => '0',
+ 'promote' => '0',
+ 'moderate' => '0',
+ 'sticky' => '0',
+ 'tnid' => '0',
+ 'translate' => '0',
+))
->execute();
db_insert('node_revisions')->fields(array(
@@ -254,6 +303,28 @@
'timestamp' => '1285709012',
'format' => '1',
))
+->values(array(
+ 'nid' => '41',
+ 'vid' => '54',
+ 'uid' => '1',
+ 'title' => 'node title 41 revision 54',
+ 'body' => "Attachments:\r\nduplicate-name.png",
+ 'teaser' => "Attachments:\r\nduplicate-name.png",
+ 'log' => '',
+ 'timestamp' => '1285709012',
+ 'format' => '1',
+))
+->values(array(
+ 'nid' => '41',
+ 'vid' => '55',
+ 'uid' => '1',
+ 'title' => 'node title 41 revision 55',
+ 'body' => "Attachments:\r\nduplicate-name.png\r\nduplicate-name.png",
+ 'teaser' => "Attachments:\r\nduplicate-name.png\r\nduplicate-name.png",
+ 'log' => '',
+ 'timestamp' => '1285709012',
+ 'format' => '1',
+))
->execute();
db_create_table('upload', array(
@@ -415,6 +486,30 @@
'list' => '1',
'weight' => '0',
))
+->values(array(
+ 'fid' => '12',
+ 'nid' => '41',
+ 'vid' => '54',
+ 'description' => 'duplicate-name.png',
+ 'list' => '1',
+ 'weight' => '0',
+))
+->values(array(
+ 'fid' => '13',
+ 'nid' => '41',
+ 'vid' => '55',
+ 'description' => 'first description',
+ 'list' => '0',
+ 'weight' => '0',
+))
+->values(array(
+ 'fid' => '14',
+ 'nid' => '41',
+ 'vid' => '55',
+ 'description' => 'second description',
+ 'list' => '1',
+ 'weight' => '0',
+))
->execute();
// Add series of entries for invalid node vids to the {upload} table.
@@ -431,7 +526,7 @@
->values(array(
'fid' => $i,
'nid' => '40',
- 'vid' => 24 + $i,
+ 'vid' => 26 + $i,
'description' => 'crazy-basename.png',
'list' => '1',
'weight' => '0',
@@ -440,7 +535,7 @@
->values(array(
'fid' => 2,
'nid' => '40',
- 'vid' => 24 + $i + 1,
+ 'vid' => 26 + $i + 1,
'description' => 'crazy-basename.png',
'list' => '1',
'weight' => '0',
diff -Naur drupal-7.25/modules/simpletest/tests/upgrade/upgrade.taxonomy.test drupal-7.66/modules/simpletest/tests/upgrade/upgrade.taxonomy.test
--- drupal-7.25/modules/simpletest/tests/upgrade/upgrade.taxonomy.test 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/upgrade/upgrade.taxonomy.test 2019-04-17 22:20:46.000000000 +0200
@@ -56,6 +56,11 @@
$this->assertFalse(db_table_exists('taxonomy_vocabulary_node_type'), 'taxonomy_vocabulary_node_type has been removed.');
$this->assertFalse(db_table_exists('taxonomy_term_node'), 'taxonomy_term_node has been removed.');
+ // Check that taxonomy_index has not stored nids of unpublished nodes.
+ $nids = db_query('SELECT nid from {node} WHERE status = :status', array(':status' => NODE_NOT_PUBLISHED))->fetchCol();
+ $indexed_nids = db_query('SELECT DISTINCT nid from {taxonomy_index}')->fetchCol();
+ $this->assertFalse(array_intersect($nids, $indexed_nids), 'No unpublished nid present in taxonomy_index');
+
// Check that the node type 'page' has been associated to a taxonomy
// reference field for each vocabulary.
$voc_keys = array();
@@ -69,9 +74,10 @@
$this->assertEqual($voc_keys, $inst_keys, 'Node type page has instances for every vocabulary.');
// Ensure instance variables are getting through.
- foreach ($instances as $instance) {
- $this->assertTrue(isset($instance['required']), 'The required setting was preserved during the upgrade path.');
- $this->assertTrue($instance['description'], 'The description was preserved during the upgrade path');
+ foreach (array_unique($instances) as $instance) {
+ $field_instance = field_info_instance('node', $instance, 'page');
+ $this->assertTrue(isset($field_instance['required']), 'The required setting was preserved during the upgrade path.');
+ $this->assertTrue($field_instance['description'], 'The description was preserved during the upgrade path');
}
// Node type 'story' was not explicitly in $vocabulary->nodes but
diff -Naur drupal-7.25/modules/simpletest/tests/upgrade/upgrade.upload.test drupal-7.66/modules/simpletest/tests/upgrade/upgrade.upload.test
--- drupal-7.25/modules/simpletest/tests/upgrade/upgrade.upload.test 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/upgrade/upgrade.upload.test 2019-04-17 22:20:46.000000000 +0200
@@ -64,12 +64,35 @@
}
$this->assertIdentical($filenames, $recorded_filenames, 'The uploaded files are present in the same order after the upgrade.');
}
+
// Test for the file with repeating basename to only have the streaming
// path replaced.
$node = node_load(40, 53);
$repeated_basename_file = $node->upload[LANGUAGE_NONE][4];
$this->assertEqual($repeated_basename_file['uri'], 'private://drupal-6/file/directory/path/crazy-basename.png', "The file with the repeated basename path only had the stream portion replaced");
+ // Ensure that filepaths are deduplicated.
+ $node0 = node_load(41, 54);
+ $node1 = node_load(41, 55);
+ // Ensure that both revisions point to the same file ID.
+ $items0 = field_get_items('node', $node0, 'upload');
+ $this->assertEqual(count($items0), 1);
+ $items1 = field_get_items('node', $node1, 'upload');
+ $this->assertEqual(count($items1), 2);
+ $this->assertEqual($items0[0]['fid'], $items1[0]['fid']);
+ $this->assertEqual($items0[0]['fid'], $items1[1]['fid']);
+ // The revision with more than one reference to the same file should retain
+ // the original settings for each reference.
+ $this->assertEqual($items1[0]['description'], 'first description');
+ $this->assertEqual($items1[0]['display'], 0);
+ $this->assertEqual($items1[1]['description'], 'second description');
+ $this->assertEqual($items1[1]['display'], 1);
+ // Ensure that the latest version of the files are used.
+ $this->assertEqual($items1[0]['filesize'], 316);
+ $this->assertEqual($items1[1]['filesize'], 316);
+ // No duplicate files should remain on the Drupal 7 site.
+ $this->assertEqual(0, db_query("SELECT COUNT(*) FROM {file_managed} GROUP BY uri HAVING COUNT(fid) > 1")->fetchField());
+
// Make sure the file settings were properly migrated.
$d6_file_directory_temp = '/drupal-6/file/directory/temp';
$d6_file_directory_path = '/drupal-6/file/directory/path';
diff -Naur drupal-7.25/modules/simpletest/tests/url_alter_test.info drupal-7.66/modules/simpletest/tests/url_alter_test.info
--- drupal-7.25/modules/simpletest/tests/url_alter_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/url_alter_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
version = VERSION
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/simpletest/tests/xmlrpc.test drupal-7.66/modules/simpletest/tests/xmlrpc.test
--- drupal-7.25/modules/simpletest/tests/xmlrpc.test 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/xmlrpc.test 2019-04-17 22:20:46.000000000 +0200
@@ -211,6 +211,11 @@
* Make sure that XML-RPC can transfer large messages.
*/
function testSizedMessages() {
+ // These tests can produce up to 128 x 160 words in the XML-RPC message
+ // (see xmlrpc_test_message_sized_in_kb()) with 4 tags used to represent
+ // each. Set a large enough tag limit to allow this to be tested.
+ variable_set('xmlrpc_message_maximum_tag_count', 100000);
+
$xml_url = url(NULL, array('absolute' => TRUE)) . 'xmlrpc.php';
$sizes = array(8, 80, 160);
foreach ($sizes as $size) {
@@ -241,4 +246,38 @@
$this->assertEqual($removed, 'system.methodSignature', 'Hiding builting system.methodSignature with hook_xmlrpc_alter works');
}
+ /**
+ * Test limits on system.multicall that can prevent brute-force attacks.
+ */
+ function testMulticallLimit() {
+ $url = url(NULL, array('absolute' => TRUE)) . 'xmlrpc.php';
+ $multicall_args = array();
+ $num_method_calls = 10;
+ for ($i = 0; $i < $num_method_calls; $i++) {
+ $struct = array('i' => $i);
+ $multicall_args[] = array('methodName' => 'validator1.echoStructTest', 'params' => array($struct));
+ }
+ // Test limits of 1, 5, 9, 13.
+ for ($limit = 1; $limit < $num_method_calls + 4; $limit += 4) {
+ variable_set('xmlrpc_multicall_duplicate_method_limit', $limit);
+ $results = xmlrpc($url, array('system.multicall' => array($multicall_args)));
+ $this->assertEqual($num_method_calls, count($results));
+ for ($i = 0; $i < min($limit, $num_method_calls); $i++) {
+ $x = array_shift($results);
+ $this->assertTrue(empty($x->is_error), "Result $i is not an error");
+ $this->assertEqual($multicall_args[$i]['params'][0], $x);
+ }
+ for (; $i < $num_method_calls; $i++) {
+ $x = array_shift($results);
+ $this->assertFalse(empty($x->is_error), "Result $i is an error");
+ $this->assertEqual(-156579, $x->code);
+ }
+ }
+ variable_set('xmlrpc_multicall_duplicate_method_limit', -1);
+ $results = xmlrpc($url, array('system.multicall' => array($multicall_args)));
+ $this->assertEqual($num_method_calls, count($results));
+ foreach ($results as $i => $x) {
+ $this->assertTrue(empty($x->is_error), "Result $i is not an error");
+ }
+ }
}
diff -Naur drupal-7.25/modules/simpletest/tests/xmlrpc_test.info drupal-7.66/modules/simpletest/tests/xmlrpc_test.info
--- drupal-7.25/modules/simpletest/tests/xmlrpc_test.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/simpletest/tests/xmlrpc_test.info 2019-04-17 22:39:36.000000000 +0200
@@ -5,8 +5,7 @@
core = 7.x
hidden = TRUE
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/statistics/statistics.admin.inc drupal-7.66/modules/statistics/statistics.admin.inc
--- drupal-7.25/modules/statistics/statistics.admin.inc 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/statistics/statistics.admin.inc 2019-04-17 22:20:46.000000000 +0200
@@ -59,7 +59,7 @@
* statistics settings form, but is dependent on cron running.
*
* @return
- * A render array containing information about the the top pages.
+ * A render array containing information about the top pages.
*/
function statistics_top_pages() {
$header = array(
@@ -137,7 +137,8 @@
->groupBy('u.name')
->groupBy('bl.iid')
->limit(30)
- ->orderByHeader($header);
+ ->orderByHeader($header)
+ ->orderBy('a.hostname');
$uniques_query = db_select('accesslog')->distinct();
$uniques_query->fields('accesslog', array('uid', 'hostname'));
diff -Naur drupal-7.25/modules/statistics/statistics.info drupal-7.66/modules/statistics/statistics.info
--- drupal-7.25/modules/statistics/statistics.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/statistics/statistics.info 2019-04-17 22:39:36.000000000 +0200
@@ -6,8 +6,7 @@
files[] = statistics.test
configure = admin/config/system/statistics
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/statistics/statistics.module drupal-7.66/modules/statistics/statistics.module
--- drupal-7.25/modules/statistics/statistics.module 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/statistics/statistics.module 2019-04-17 22:20:46.000000000 +0200
@@ -118,10 +118,9 @@
// Attach Ajax node count statistics if configured.
if (variable_get('statistics_count_content_views', 0) && variable_get('statistics_count_content_views_ajax', 0)) {
if (!empty($node->nid) && $view_mode == 'full' && node_is_page($node) && empty($node->in_preview)) {
- $node->content['#attached']['js'] = array(
- drupal_get_path('module', 'statistics') . '/statistics.js' => array(
- 'scope' => 'footer'
- ),
+ $statistics = drupal_get_path('module', 'statistics') . '/statistics.js';
+ $node->content['#attached']['js'][$statistics] = array(
+ 'scope' => 'footer',
);
$settings = array('data' => array('nid' => $node->nid), 'url' => url(drupal_get_path('module', 'statistics') . '/statistics.php'));
$node->content['#attached']['js'][] = array(
@@ -246,7 +245,7 @@
* Implements hook_cron().
*/
function statistics_cron() {
- $statistics_timestamp = variable_get('statistics_day_timestamp', '');
+ $statistics_timestamp = variable_get('statistics_day_timestamp', 0);
if ((REQUEST_TIME - $statistics_timestamp) >= 86400) {
// Reset day counts.
diff -Naur drupal-7.25/modules/statistics/statistics.php drupal-7.66/modules/statistics/statistics.php
--- drupal-7.25/modules/statistics/statistics.php 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/statistics/statistics.php 2019-04-17 22:20:46.000000000 +0200
@@ -15,17 +15,19 @@
include_once DRUPAL_ROOT . '/includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_VARIABLES);
if (variable_get('statistics_count_content_views', 0) && variable_get('statistics_count_content_views_ajax', 0)) {
- $nid = $_POST['nid'];
- if (is_numeric($nid)) {
- db_merge('node_counter')
- ->key(array('nid' => $nid))
- ->fields(array(
- 'daycount' => 1,
- 'totalcount' => 1,
- 'timestamp' => REQUEST_TIME,
- ))
- ->expression('daycount', 'daycount + 1')
- ->expression('totalcount', 'totalcount + 1')
- ->execute();
+ if (isset($_POST['nid'])) {
+ $nid = $_POST['nid'];
+ if (is_numeric($nid)) {
+ db_merge('node_counter')
+ ->key(array('nid' => $nid))
+ ->fields(array(
+ 'daycount' => 1,
+ 'totalcount' => 1,
+ 'timestamp' => REQUEST_TIME,
+ ))
+ ->expression('daycount', 'daycount + 1')
+ ->expression('totalcount', 'totalcount + 1')
+ ->execute();
+ }
}
}
diff -Naur drupal-7.25/modules/statistics/statistics.test drupal-7.66/modules/statistics/statistics.test
--- drupal-7.25/modules/statistics/statistics.test 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/statistics/statistics.test 2019-04-17 22:20:46.000000000 +0200
@@ -35,7 +35,7 @@
'title' => 'test',
'path' => 'node/1',
'url' => 'http://example.com',
- 'hostname' => '192.168.1.1',
+ 'hostname' => '1.2.3.3',
'uid' => 0,
'sid' => 10,
'timer' => 10,
@@ -268,7 +268,7 @@
*/
function testIPAddressBlocking() {
// IP address for testing.
- $test_ip_address = '192.168.1.1';
+ $test_ip_address = '1.2.3.3';
// Verify the IP address from accesslog appears on the top visitors page
// and that a 'block IP address' link is displayed.
@@ -414,7 +414,7 @@
$timestamp = time();
$this->drupalPost(NULL, NULL, t('Cancel account'));
// Confirm account cancellation request.
- $this->drupalGet("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login));
+ $this->drupalGet("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login, $account->uid));
$this->assertFalse(user_load($account->uid, TRUE), 'User is not found in the database.');
$this->drupalGet('admin/reports/visitors');
diff -Naur drupal-7.25/modules/syslog/syslog.info drupal-7.66/modules/syslog/syslog.info
--- drupal-7.25/modules/syslog/syslog.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/syslog/syslog.info 2019-04-17 22:39:36.000000000 +0200
@@ -6,8 +6,7 @@
files[] = syslog.test
configure = admin/config/development/logging
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/system/form.api.php drupal-7.66/modules/system/form.api.php
--- drupal-7.25/modules/system/form.api.php 1970-01-01 01:00:00.000000000 +0100
+++ drupal-7.66/modules/system/form.api.php 2019-04-17 22:20:46.000000000 +0200
@@ -0,0 +1,126 @@
+fetchField();
+ }
+
+ // For this example, we decide that we can safely process
+ // 5 nodes at a time without a timeout.
+ $limit = 5;
+
+ // With each pass through the callback, retrieve the next group of nids.
+ $result = db_query_range("SELECT nid FROM {node} WHERE nid > %d ORDER BY nid ASC", $context['sandbox']['current_node'], 0, $limit);
+ while ($row = db_fetch_array($result)) {
+
+ // Here we actually perform our processing on the current node.
+ $node = node_load($row['nid'], NULL, TRUE);
+ $node->value1 = $options1;
+ $node->value2 = $options2;
+ node_save($node);
+
+ // Store some result for post-processing in the finished callback.
+ $context['results'][] = check_plain($node->title);
+
+ // Update our progress information.
+ $context['sandbox']['progress']++;
+ $context['sandbox']['current_node'] = $node->nid;
+ $context['message'] = t('Now processing %node', array('%node' => $node->title));
+ }
+
+ // Inform the batch engine that we are not finished,
+ // and provide an estimation of the completion level we reached.
+ if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
+ $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
+ }
+}
+
+/**
+ * Complete a batch process.
+ *
+ * Callback for batch_set().
+ *
+ * This callback may be specified in a batch to perform clean-up operations, or
+ * to analyze the results of the batch operations.
+ *
+ * @param $success
+ * A boolean indicating whether the batch has completed successfully.
+ * @param $results
+ * The value set in $context['results'] by callback_batch_operation().
+ * @param $operations
+ * If $success is FALSE, contains the operations that remained unprocessed.
+ */
+function callback_batch_finished($success, $results, $operations) {
+ if ($success) {
+ // Here we do something meaningful with the results.
+ $message = t("!count items were processed.", array(
+ '!count' => count($results),
+ ));
+ $message .= theme('item_list', array('items' => $results));
+ drupal_set_message($message);
+ }
+ else {
+ // An error occurred.
+ // $operations contains the operations that remained unprocessed.
+ $error_operation = reset($operations);
+ $message = t('An error occurred while processing %error_operation with arguments: @arguments', array(
+ '%error_operation' => $error_operation[0],
+ '@arguments' => print_r($error_operation[1], TRUE)
+ ));
+ drupal_set_message($message, 'error');
+ }
+}
diff -Naur drupal-7.25/modules/system/image.gd.inc drupal-7.66/modules/system/image.gd.inc
--- drupal-7.25/modules/system/image.gd.inc 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/system/image.gd.inc 2019-04-17 22:20:46.000000000 +0200
@@ -56,13 +56,8 @@
* A boolean indicating if the GD toolkit is available on this machine.
*/
function image_gd_check_settings() {
- if ($check = get_extension_funcs('gd')) {
- if (in_array('imagegd2', $check)) {
- // GD2 support is available.
- return TRUE;
- }
- }
- return FALSE;
+ // GD2 support is available.
+ return function_exists('imagegd2');
}
/**
@@ -121,38 +116,62 @@
return FALSE;
}
- $width = $image->info['width'];
- $height = $image->info['height'];
+ // PHP 5.5 GD bug: https://bugs.php.net/bug.php?id=65148: To prevent buggy
+ // behavior on negative multiples of 90 degrees we convert any negative
+ // angle to a positive one between 0 and 360 degrees.
+ $degrees -= floor($degrees / 360) * 360;
- // Convert the hexadecimal background value to a color index value.
+ // Convert the hexadecimal background value to a RGBA array.
if (isset($background)) {
- $rgb = array();
- for ($i = 16; $i >= 0; $i -= 8) {
- $rgb[] = (($background >> $i) & 0xFF);
- }
- $background = imagecolorallocatealpha($image->resource, $rgb[0], $rgb[1], $rgb[2], 0);
+ $background = array(
+ 'red' => $background >> 16 & 0xFF,
+ 'green' => $background >> 8 & 0xFF,
+ 'blue' => $background & 0xFF,
+ 'alpha' => 0,
+ );
}
- // Set the background color as transparent if $background is NULL.
else {
- // Get the current transparent color.
- $background = imagecolortransparent($image->resource);
-
- // If no transparent colors, use white.
- if ($background == 0) {
- $background = imagecolorallocatealpha($image->resource, 255, 255, 255, 0);
- }
+ // Background color is not specified: use transparent white as background.
+ $background = array(
+ 'red' => 255,
+ 'green' => 255,
+ 'blue' => 255,
+ 'alpha' => 127
+ );
}
+ // Store the color index for the background as that is what GD uses.
+ $background_idx = imagecolorallocatealpha($image->resource, $background['red'], $background['green'], $background['blue'], $background['alpha']);
+
// Images are assigned a new color palette when rotating, removing any
// transparency flags. For GIF images, keep a record of the transparent color.
if ($image->info['extension'] == 'gif') {
- $transparent_index = imagecolortransparent($image->resource);
- if ($transparent_index != 0) {
- $transparent_gif_color = imagecolorsforindex($image->resource, $transparent_index);
+ // GIF does not work with a transparency channel, but can define 1 color
+ // in its palette to act as transparent.
+
+ // Get the current transparent color, if any.
+ $gif_transparent_id = imagecolortransparent($image->resource);
+ if ($gif_transparent_id !== -1) {
+ // The gif already has a transparent color set: remember it to set it on
+ // the rotated image as well.
+ $transparent_gif_color = imagecolorsforindex($image->resource, $gif_transparent_id);
+
+ if ($background['alpha'] >= 127) {
+ // We want a transparent background: use the color already set to act
+ // as transparent, as background.
+ $background_idx = $gif_transparent_id;
+ }
+ }
+ else {
+ // The gif does not currently have a transparent color set.
+ if ($background['alpha'] >= 127) {
+ // But as the background is transparent, it should get one.
+ $transparent_gif_color = $background;
+ }
}
}
- $image->resource = imagerotate($image->resource, 360 - $degrees, $background);
+ $image->resource = imagerotate($image->resource, 360 - $degrees, $background_idx);
// GIFs need to reassign the transparent color after performing the rotate.
if (isset($transparent_gif_color)) {
@@ -234,7 +253,24 @@
function image_gd_load(stdClass $image) {
$extension = str_replace('jpg', 'jpeg', $image->info['extension']);
$function = 'imagecreatefrom' . $extension;
- return (function_exists($function) && $image->resource = $function($image->source));
+ if (function_exists($function) && $image->resource = $function($image->source)) {
+ if (imageistruecolor($image->resource)) {
+ return TRUE;
+ }
+ else {
+ // Convert indexed images to truecolor, copying the image to a new
+ // truecolor resource, so that filters work correctly and don't result
+ // in unnecessary dither.
+ $resource = image_gd_create_tmp($image, $image->info['width'], $image->info['height']);
+ if ($resource) {
+ imagecopy($resource, $image->resource, 0, 0, 0, 0, imagesx($resource), imagesy($resource));
+ imagedestroy($image->resource);
+ $image->resource = $resource;
+ }
+ }
+ return (bool) $image->resource;
+ }
+ return FALSE;
}
/**
@@ -302,17 +338,31 @@
$res = imagecreatetruecolor($width, $height);
if ($image->info['extension'] == 'gif') {
- // Grab transparent color index from image resource.
+ // Find out if a transparent color is set, will return -1 if no
+ // transparent color has been defined in the image.
$transparent = imagecolortransparent($image->resource);
if ($transparent >= 0) {
- // The original must have a transparent color, allocate to the new image.
- $transparent_color = imagecolorsforindex($image->resource, $transparent);
- $transparent = imagecolorallocate($res, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']);
-
- // Flood with our new transparent color.
- imagefill($res, 0, 0, $transparent);
- imagecolortransparent($res, $transparent);
+ // Find out the number of colors in the image palette. It will be 0 for
+ // truecolor images.
+ $palette_size = imagecolorstotal($image->resource);
+ if ($palette_size == 0 || $transparent < $palette_size) {
+ // Set the transparent color in the new resource, either if it is a
+ // truecolor image or if the transparent color is part of the palette.
+ // Since the index of the transparency color is a property of the
+ // image rather than of the palette, it is possible that an image
+ // could be created with this index set outside the palette size (see
+ // http://stackoverflow.com/a/3898007).
+ $transparent_color = imagecolorsforindex($image->resource, $transparent);
+ $transparent = imagecolorallocate($res, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']);
+
+ // Flood with our new transparent color.
+ imagefill($res, 0, 0, $transparent);
+ imagecolortransparent($res, $transparent);
+ }
+ else {
+ imagefill($res, 0, 0, imagecolorallocate($res, 255, 255, 255));
+ }
}
}
elseif ($image->info['extension'] == 'png') {
@@ -346,7 +396,7 @@
*/
function image_gd_get_info(stdClass $image) {
$details = FALSE;
- $data = getimagesize($image->source);
+ $data = @getimagesize($image->source);
if (isset($data) && is_array($data)) {
$extensions = array('1' => 'gif', '2' => 'jpg', '3' => 'png');
diff -Naur drupal-7.25/modules/system/system.admin.inc drupal-7.66/modules/system/system.admin.inc
--- drupal-7.25/modules/system/system.admin.inc 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/system/system.admin.inc 2019-04-17 22:20:46.000000000 +0200
@@ -572,9 +572,10 @@
// Process the theme and all its base themes.
foreach ($theme_keys as $theme) {
// Include the theme-settings.php file.
- $filename = DRUPAL_ROOT . '/' . str_replace("/$theme.info", '', $themes[$theme]->filename) . '/theme-settings.php';
- if (file_exists($filename)) {
- require_once $filename;
+ $theme_settings_path = drupal_get_path('theme', $theme) . '/theme-settings.php';
+ if (file_exists(DRUPAL_ROOT . '/' . $theme_settings_path)) {
+ require_once DRUPAL_ROOT . '/' . $theme_settings_path;
+ $form_state['build_info']['files'][] = $theme_settings_path;
}
// Call theme-specific settings.
@@ -640,13 +641,13 @@
// If the user provided a path for a logo or favicon file, make sure a file
// exists at that path.
- if ($form_state['values']['logo_path']) {
+ if (!empty($form_state['values']['logo_path'])) {
$path = _system_theme_settings_validate_path($form_state['values']['logo_path']);
if (!$path) {
form_set_error('logo_path', t('The custom logo path is invalid.'));
}
}
- if ($form_state['values']['favicon_path']) {
+ if (!empty($form_state['values']['favicon_path'])) {
$path = _system_theme_settings_validate_path($form_state['values']['favicon_path']);
if (!$path) {
form_set_error('favicon_path', t('The custom favicon path is invalid.'));
@@ -703,14 +704,16 @@
// If the user uploaded a new logo or favicon, save it to a permanent location
// and use it in place of the default theme-provided file.
- if ($file = $values['logo_upload']) {
+ if (!empty($values['logo_upload'])) {
+ $file = $values['logo_upload'];
unset($values['logo_upload']);
$filename = file_unmanaged_copy($file->uri);
$values['default_logo'] = 0;
$values['logo_path'] = $filename;
$values['toggle_logo'] = 1;
}
- if ($file = $values['favicon_upload']) {
+ if (!empty($values['favicon_upload'])) {
+ $file = $values['favicon_upload'];
unset($values['favicon_upload']);
$filename = file_unmanaged_copy($file->uri);
$values['default_favicon'] = 0;
@@ -950,7 +953,11 @@
}
/**
- * Array sorting callback; sorts modules or themes by their name.
+ * Sorts themes by their names, with the default theme listed first.
+ *
+ * Callback for uasort() within system_themes_page().
+ *
+ * @see system_sort_modules_by_info_name().
*/
function system_sort_themes($a, $b) {
if ($a->is_default) {
@@ -995,22 +1002,28 @@
$status_short = '';
$status_long = '';
+ // Initialize empty arrays of long and short reasons explaining why the
+ // module is incompatible.
+ // Add each reason as a separate element in both the arrays.
+ $reasons_short = array();
+ $reasons_long = array();
+
// Check the core compatibility.
if (!isset($info['core']) || $info['core'] != DRUPAL_CORE_COMPATIBILITY) {
$compatible = FALSE;
- $status_short .= t('Incompatible with this version of Drupal core.');
- $status_long .= t('This version is not compatible with Drupal !core_version and should be replaced.', array('!core_version' => DRUPAL_CORE_COMPATIBILITY));
+ $reasons_short[] = t('Incompatible with this version of Drupal core.');
+ $reasons_long[] = t('This version is not compatible with Drupal !core_version and should be replaced.', array('!core_version' => DRUPAL_CORE_COMPATIBILITY));
}
// Ensure this module is compatible with the currently installed version of PHP.
if (version_compare(phpversion(), $info['php']) < 0) {
$compatible = FALSE;
- $status_short .= t('Incompatible with this version of PHP');
+ $reasons_short[] = t('Incompatible with this version of PHP');
$php_required = $info['php'];
if (substr_count($info['php'], '.') < 2) {
$php_required .= '.*';
}
- $status_long .= t('This module requires PHP version @php_required and is incompatible with PHP version !php_version.', array('@php_required' => $php_required, '!php_version' => phpversion()));
+ $reasons_long[] = t('This module requires PHP version @php_required and is incompatible with PHP version !php_version.', array('@php_required' => $php_required, '!php_version' => phpversion()));
}
// If this module is compatible, present a checkbox indicating
@@ -1026,6 +1039,8 @@
}
}
else {
+ $status_short = implode(' ', $reasons_short);
+ $status_long = implode(' ', $reasons_long);
$form['enable'] = array(
'#markup' => theme('image', array('path' => 'misc/watchdog-error.png', 'alt' => $status_short, 'title' => $status_short)),
);
@@ -1798,7 +1813,7 @@
'#title' => t('Private file system path'),
'#default_value' => variable_get('file_private_path', ''),
'#maxlength' => 255,
- '#description' => t('An existing local file system path for storing private files. It should be writable by Drupal and not accessible over the web. See the online handbook for more information about securing private files.', array('@handbook' => 'http://drupal.org/documentation/modules/file')),
+ '#description' => t('An existing local file system path for storing private files. It should be writable by Drupal and not accessible over the web. See the online handbook for more information about securing private files.', array('@handbook' => 'https://www.drupal.org/docs/7/core/modules/file/overview')),
'#after_build' => array('system_check_directory'),
);
@@ -1842,7 +1857,7 @@
if (count($toolkits_available) == 0) {
variable_del('image_toolkit');
$form['image_toolkit_help'] = array(
- '#markup' => t("No image toolkits were detected. Drupal includes support for PHP's built-in image processing functions but they were not detected on this system. You should consult your system administrator to have them enabled, or try using a third party toolkit.", array('gd-link' => url('http://php.net/gd'))),
+ '#markup' => t("No image toolkits were detected. Drupal includes support for PHP's built-in image processing functions but they were not detected on this system. You should consult your system administrator to have them enabled, or try using a third party toolkit.", array('!gd-link' => url('http://php.net/gd'))),
);
return $form;
}
@@ -2188,6 +2203,11 @@
* Return the date for a given format string via Ajax.
*/
function system_date_time_lookup() {
+ // This callback is protected with a CSRF token because user input from the
+ // query string is reflected in the output.
+ if (!isset($_GET['token']) || !drupal_valid_token($_GET['token'], 'admin/config/regional/date-time/formats/lookup')) {
+ return MENU_ACCESS_DENIED;
+ }
$result = format_date(REQUEST_TIME, 'custom', $_GET['format']);
drupal_json_output($result);
}
@@ -2546,9 +2566,21 @@
/**
* Returns HTML for the status report.
*
+ * This theme function is dependent on install.inc being loaded, because
+ * that's where the constants are defined.
+ *
* @param $variables
* An associative array containing:
- * - requirements: An array of requirements.
+ * - requirements: An array of requirements/status items. Each requirement
+ * is an associative array containing the following elements:
+ * - title: The name of the requirement.
+ * - value: (optional) The current value (version, time, level, etc).
+ * - description: (optional) The description of the requirement.
+ * - severity: (optional) The requirement's result/severity level, one of:
+ * - REQUIREMENT_INFO: Status information.
+ * - REQUIREMENT_OK: The requirement is satisfied.
+ * - REQUIREMENT_WARNING: The requirement failed with a warning.
+ * - REQUIREMENT_ERROR: The requirement failed with an error.
*
* @ingroup themeable
*/
@@ -2578,6 +2610,8 @@
if (empty($requirement['#type'])) {
$severity = $severities[isset($requirement['severity']) ? (int) $requirement['severity'] : REQUIREMENT_OK];
$severity['icon'] = '' . $severity['title'] . '';
+ // The requirement's 'value' key is optional, provide a default value.
+ $requirement['value'] = isset($requirement['value']) ? $requirement['value'] : '';
// Output table row(s)
if (!empty($requirement['description'])) {
@@ -2632,8 +2666,8 @@
}
$row[] = array('data' => $description, 'class' => array('description'));
// Display links (such as help or permissions) in their own columns.
- foreach (array('help', 'permissions', 'configure') as $key) {
- $row[] = array('data' => drupal_render($module['links'][$key]), 'class' => array($key));
+ foreach (array('help', 'permissions', 'configure') as $link_type) {
+ $row[] = array('data' => drupal_render($module['links'][$link_type]), 'class' => array($link_type));
}
$rows[] = $row;
}
@@ -2861,13 +2895,14 @@
* Allow users to add additional date formats.
*/
function system_configure_date_formats_form($form, &$form_state, $dfid = 0) {
+ $ajax_path = 'admin/config/regional/date-time/formats/lookup';
$js_settings = array(
'type' => 'setting',
'data' => array(
'dateTime' => array(
'date-format' => array(
'text' => t('Displayed as'),
- 'lookup' => url('admin/config/regional/date-time/formats/lookup'),
+ 'lookup' => url($ajax_path, array('query' => array('token' => drupal_get_token($ajax_path)))),
),
),
),
diff -Naur drupal-7.25/modules/system/system.api.php drupal-7.66/modules/system/system.api.php
--- drupal-7.25/modules/system/system.api.php 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/system/system.api.php 2019-04-17 22:20:46.000000000 +0200
@@ -113,21 +113,21 @@
* translation handlers. Array keys are the module names, array values
* can be any data structure the module uses to provide field translation.
* Any empty value disallows the module to appear as a translation handler.
- * - entity keys: An array describing how the Field API can extract the
- * information it needs from the objects of the type. Elements:
+ * - entity keys: (optional) An array describing how the Field API can extract
+ * the information it needs from the objects of the type. Elements:
* - id: The name of the property that contains the primary id of the
* entity. Every entity object passed to the Field API must have this
* property and its value must be numeric.
* - revision: The name of the property that contains the revision id of
* the entity. The Field API assumes that all revision ids are unique
* across all entities of a type. This entry can be omitted if the
- * entities of this type are not versionable.
+ * entities of this type are not versionable. Defaults to an empty string.
* - bundle: The name of the property that contains the bundle name for the
* entity. The bundle name defines which set of fields are attached to
* the entity (e.g. what nodes call "content type"). This entry can be
* omitted if this entity type exposes a single bundle (all entities have
* the same collection of fields). The name of this single bundle will be
- * the same as the entity type.
+ * the same as the entity type. Defaults to an empty string.
* - label: The name of the property that contains the entity label. For
* example, if the entity's label is located in $entity->subject, then
* 'subject' should be specified here. If complex logic is required to
@@ -247,7 +247,7 @@
'custom settings' => FALSE,
),
'search_result' => array(
- 'label' => t('Search result'),
+ 'label' => t('Search result highlighting input'),
'custom settings' => FALSE,
),
);
@@ -606,8 +606,8 @@
* @return
* An associative array where the key is the queue name and the value is
* again an associative array. Possible keys are:
- * - 'worker callback': The name of the function to call. It will be called
- * with one argument, the item created via DrupalQueue::createItem().
+ * - 'worker callback': The name of an implementation of
+ * callback_queue_worker().
* - 'time': (optional) How much time Drupal should spend on calling this
* worker in seconds. Defaults to 15.
* - 'skip on cron': (optional) Set to TRUE to avoid being processed during
@@ -875,7 +875,7 @@
*
* @see ajax_render()
*/
-function hook_ajax_render_alter($commands) {
+function hook_ajax_render_alter(&$commands) {
// Inject any new status messages into the content area.
$commands[] = ajax_command_prepend('#block-system-main .content', theme('status_messages'));
}
@@ -1797,6 +1797,8 @@
* the $form_id input matched your module's format for dynamically-generated
* form IDs, and if so, act appropriately.
*
+ * Third, forms defined in classes can be defined this way.
+ *
* @param $form_id
* The unique string identifying the desired form.
* @param $args
@@ -1807,19 +1809,22 @@
* @return
* An associative array whose keys define form_ids and whose values are an
* associative array defining the following keys:
- * - callback: The name of the form builder function to invoke. This will be
- * used for the base form ID, for example, to target a base form using
- * hook_form_BASE_FORM_ID_alter().
+ * - callback: The callable returning the form array. If it is the name of
+ * the form builder function then this will be used for the base
+ * form ID, for example, to target a base form using
+ * hook_form_BASE_FORM_ID_alter(). Otherwise use the base_form_id key to
+ * define the base form ID.
* - callback arguments: (optional) Additional arguments to pass to the
* function defined in 'callback', which are prepended to $args.
- * - wrapper_callback: (optional) The name of a form builder function to
- * invoke before the form builder defined in 'callback' is invoked. This
- * wrapper callback may prepopulate the $form array with form elements,
- * which will then be already contained in the $form that is passed on to
- * the form builder defined in 'callback'. For example, a wrapper callback
- * could setup wizard-alike form buttons that are the same for a variety of
- * forms that belong to the wizard, which all share the same wrapper
- * callback.
+ * - base_form_id: The base form ID can be specified explicitly. This is
+ * required when callback is not the name of a function.
+ * - wrapper_callback: (optional) Any callable to invoke before the form
+ * builder defined in 'callback' is invoked. This wrapper callback may
+ * prepopulate the $form array with form elements, which will then be
+ * already contained in the $form that is passed on to the form builder
+ * defined in 'callback'. For example, a wrapper callback could setup
+ * wizard-like form buttons that are the same for a variety of forms that
+ * belong to the wizard, which all share the same wrapper callback.
*/
function hook_forms($form_id, $args) {
// Simply reroute the (non-existing) $form_id 'mymodule_first_form' to
@@ -1843,6 +1848,15 @@
'wrapper_callback' => 'mymodule_main_form_wrapper',
);
+ // Build a form with a static class callback.
+ $forms['mymodule_class_generated_form'] = array(
+ // This will call: MyClass::generateMainForm().
+ 'callback' => array('MyClass', 'generateMainForm'),
+ // The base_form_id is required when the callback is a static function in
+ // a class. This can also be used to keep newer code backwards compatible.
+ 'base_form_id' => 'mymodule_main_form',
+ );
+
return $forms;
}
@@ -1874,8 +1888,8 @@
*
* This hook is not run on cached pages.
*
- * To add CSS or JS that should be present on all pages, modules should not
- * implement this hook, but declare these files in their .info file.
+ * To add CSS or JS files that should be present on all pages, modules should
+ * not implement this hook, but declare these files in their .info file.
*
* @see hook_boot()
*/
@@ -1890,9 +1904,8 @@
/**
* Define image toolkits provided by this module.
*
- * The file which includes each toolkit's functions must be declared as part of
- * the files array in the module .info file so that the registry will find and
- * parse it.
+ * The file which includes each toolkit's functions must be included in this
+ * hook.
*
* The toolkit's functions must be named image_toolkitname_operation().
* where the operation may be:
@@ -2038,6 +2051,22 @@
}
/**
+ * Return additional theme engines provided by modules.
+ *
+ * This hook is invoked from _system_rebuild_theme_data() and allows modules to
+ * register additional theme engines outside of the regular 'themes/engines'
+ * directories of a Drupal installation.
+ *
+ * @return
+ * An associative array. Each key is the system name of a theme engine and
+ * each value is the corresponding path to the theme engine's .engine file.
+ */
+function hook_system_theme_engine_info() {
+ $theme_engines['izumi'] = drupal_get_path('module', 'mymodule') . '/izumi/izumi.engine';
+ return $theme_engines;
+}
+
+/**
* Alter the information parsed from module and theme .info files
*
* This hook is invoked in _system_rebuild_module_data() and in
@@ -2110,6 +2139,61 @@
}
/**
+ * Provide online user help.
+ *
+ * By implementing hook_help(), a module can make documentation available to
+ * the user for the module as a whole, or for specific paths. Help for
+ * developers should usually be provided via function header comments in the
+ * code, or in special API example files.
+ *
+ * The page-specific help information provided by this hook appears as a system
+ * help block on that page. The module overview help information is displayed
+ * by the Help module. It can be accessed from the page at admin/help or from
+ * the Modules page.
+ *
+ * For detailed usage examples of:
+ * - Module overview help, see node_help(). Module overview help should follow
+ * @link https://drupal.org/node/632280 the standard help template. @endlink
+ * - Page-specific help with simple paths, see dashboard_help().
+ * - Page-specific help using wildcards in path and $arg, see node_help()
+ * and block_help().
+ *
+ * @param $path
+ * The router menu path, as defined in hook_menu(), for the help that is
+ * being requested; e.g., 'admin/people' or 'user/register'. If the router
+ * path includes a wildcard, then this will appear in $path as %, even if it
+ * is a named %autoloader wildcard in the hook_menu() implementation; for
+ * example, node pages would have $path equal to 'node/%' or 'node/%/view'.
+ * For the help page for the module as a whole, $path will have the value
+ * 'admin/help#module_name', where 'module_name" is the machine name of your
+ * module.
+ * @param $arg
+ * An array that corresponds to the return value of the arg() function, for
+ * modules that want to provide help that is specific to certain values
+ * of wildcards in $path. For example, you could provide help for the path
+ * 'user/1' by looking for the path 'user/%' and $arg[1] == '1'. This given
+ * array should always be used rather than directly invoking arg(), because
+ * your hook implementation may be called for other purposes besides building
+ * the current page's help. Note that depending on which module is invoking
+ * hook_help, $arg may contain only empty strings. Regardless, $arg[0] to
+ * $arg[11] will always be set.
+ *
+ * @return
+ * A localized string containing the help text.
+ */
+function hook_help($path, $arg) {
+ switch ($path) {
+ // Main module help for the block module
+ case 'admin/help#block':
+ return '' . t('Blocks are boxes of content rendered into an area, or region, of a web page. The default theme Bartik, for example, implements the regions "Sidebar first", "Sidebar second", "Featured", "Content", "Header", "Footer", etc., and a block may appear in any one of these areas. The blocks administration page provides a drag-and-drop interface for assigning a block to a region, and for controlling the order of blocks within regions.', array('@blocks' => url('admin/structure/block'))) . '
';
+
+ // Help for another path in the block module
+ case 'admin/structure/block':
+ return '' . t('This page provides a drag-and-drop interface for assigning a block to a region, and for controlling the order of blocks within regions. Since not all themes implement the same regions, or display regions in the same way, blocks are positioned on a per-theme basis. Remember that your changes will not be saved until you click the Save blocks button at the bottom of the page.') . '
';
+ }
+}
+
+/**
* Register a module (or theme's) theme implementations.
*
* The implementations declared by this hook have two purposes: either they
@@ -2578,6 +2662,8 @@
* module_enable() for a detailed description of the order in which install and
* enable hooks are invoked.
*
+ * This hook should be implemented in a .module file, not in an .install file.
+ *
* @param $modules
* An array of the modules that were installed.
*
@@ -3119,7 +3205,9 @@
* creation and alteration of the supported database engines.
*
* See the Schema API Handbook at http://drupal.org/node/146843 for details on
- * schema definition structures.
+ * schema definition structures. Note that foreign key definitions are for
+ * documentation purposes only; foreign keys are not created in the database,
+ * nor are they enforced by Drupal.
*
* @return array
* A schema definition structure array. For each element of the
@@ -3171,6 +3259,8 @@
'nid_vid' => array('nid', 'vid'),
'vid' => array('vid'),
),
+ // For documentation purposes only; foreign keys are not created in the
+ // database.
'foreign keys' => array(
'node_revision' => array(
'table' => 'node_revision',
@@ -3371,24 +3461,31 @@
* hooks. See @link update_api Update versions of API functions @endlink for
* details.
*
- * If your update task is potentially time-consuming, you'll need to implement a
- * multipass update to avoid PHP timeouts. Multipass updates use the $sandbox
- * parameter provided by the batch API (normally, $context['sandbox']) to store
- * information between successive calls, and the $sandbox['#finished'] value
- * to provide feedback regarding completion level.
+ * The $sandbox parameter should be used when a multipass update is needed, in
+ * circumstances where running the whole update at once could cause PHP to
+ * timeout. Each pass is run in a way that avoids PHP timeouts, provided each
+ * pass remains under the timeout limit. To signify that an update requires
+ * at least one more pass, set $sandbox['#finished'] to a number less than 1
+ * (you need to do this each pass). The value of $sandbox['#finished'] will be
+ * unset between passes but all other data in $sandbox will be preserved. The
+ * system will stop iterating this update when $sandbox['#finished'] is left
+ * unset or set to a number higher than 1. It is recommended that
+ * $sandbox['#finished'] is initially set to 0, and then updated each pass to a
+ * number between 0 and 1 that represents the overall % completed for this
+ * update, finishing with 1.
*
- * See the batch operations page for more information on how to use the
- * @link http://drupal.org/node/180528 Batch API. @endlink
+ * See the @link batch Batch operations topic @endlink for more information on
+ * how to use the Batch API.
*
- * @param $sandbox
+ * @param array $sandbox
* Stores information for multipass updates. See above for more information.
*
- * @throws DrupalUpdateException, PDOException
+ * @throws DrupalUpdateException|PDOException
* In case of error, update hooks should throw an instance of DrupalUpdateException
* with a meaningful message for the user. If a database query fails for whatever
* reason, it will throw a PDOException.
*
- * @return
+ * @return string|null
* Optionally, update hooks may return a translated string that will be
* displayed to the user after the update has completed. If no message is
* returned, no message will be presented to the user.
@@ -3632,8 +3729,9 @@
*
* Any tasks you define here will be run, in order, after the installer has
* finished the site configuration step but before it has moved on to the
- * final import of languages and the end of the installation. You can have any
- * number of custom tasks to perform during this phase.
+ * final import of languages and the end of the installation. This is invoked
+ * by install_tasks(). You can have any number of custom tasks to perform
+ * during this phase.
*
* Each task you define here corresponds to a callback function which you must
* separately define and which is called when your task is run. This function
@@ -3726,6 +3824,8 @@
*
* @see install_state_defaults()
* @see batch_set()
+ * @see hook_install_tasks_alter()
+ * @see install_tasks()
*/
function hook_install_tasks(&$install_state) {
// Here, we define a variable to allow tasks to indicate that a particular,
@@ -3828,6 +3928,8 @@
/**
* Alter the full list of installation tasks.
*
+ * This hook is invoked on the install profile in install_tasks().
+ *
* @param $tasks
* An array of all available installation tasks, including those provided by
* Drupal core. You can modify this array to change or replace any part of
@@ -3835,6 +3937,9 @@
* is selected.
* @param $install_state
* An array of information about the current installation state.
+ *
+ * @see hook_install_tasks()
+ * @see install_tasks()
*/
function hook_install_tasks_alter(&$tasks, $install_state) {
// Replace the "Choose language" installation task provided by Drupal core
@@ -4722,6 +4827,28 @@
*/
/**
+ * Work on a single queue item.
+ *
+ * Callback for hook_cron_queue_info().
+ *
+ * @param $queue_item_data
+ * The data that was passed to DrupalQueueInterface::createItem() when the
+ * item was queued.
+ *
+ * @throws Exception
+ * The worker callback may throw an exception to indicate there was a problem.
+ * The cron process will log the exception, and leave the item in the queue to
+ * be processed again later.
+ *
+ * @see drupal_cron_run()
+ */
+function callback_queue_worker($queue_item_data) {
+ $node = node_load($queue_item_data);
+ $node->title = 'Updated title';
+ node_save($node);
+}
+
+/**
* Return the URI for an entity.
*
* Callback for hook_entity_info().
diff -Naur drupal-7.25/modules/system/system.base-rtl.css drupal-7.66/modules/system/system.base-rtl.css
--- drupal-7.25/modules/system/system.base-rtl.css 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/system/system.base-rtl.css 2019-04-17 22:20:46.000000000 +0200
@@ -9,10 +9,10 @@
*/
/* Animated throbber */
html.js input.form-autocomplete {
- background-position: 0% 2px;
+ background-position: 0% center;
}
html.js input.throbbing {
- background-position: 0% -18px;
+ background-position: 0% center;
}
/**
diff -Naur drupal-7.25/modules/system/system.base.css drupal-7.66/modules/system/system.base.css
--- drupal-7.25/modules/system/system.base.css 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/system/system.base.css 2019-04-17 22:20:46.000000000 +0200
@@ -31,12 +31,13 @@
}
/* Animated throbber */
html.js input.form-autocomplete {
- background-image: url(../../misc/throbber.gif);
- background-position: 100% 2px; /* LTR */
+ background-image: url(../../misc/throbber-inactive.png);
+ background-position: 100% center; /* LTR */
background-repeat: no-repeat;
}
html.js input.throbbing {
- background-position: 100% -18px; /* LTR */
+ background-image: url(../../misc/throbber-active.gif);
+ background-position: 100% center; /* LTR */
}
/**
@@ -164,7 +165,7 @@
display: inline-block;
}
.ajax-progress .throbber {
- background: transparent url(../../misc/throbber.gif) no-repeat 0px -18px;
+ background: transparent url(../../misc/throbber-active.gif) no-repeat 0px center;
float: left; /* LTR */
height: 15px;
margin: 2px;
diff -Naur drupal-7.25/modules/system/system.info drupal-7.66/modules/system/system.info
--- drupal-7.25/modules/system/system.info 2014-01-03 01:38:26.000000000 +0100
+++ drupal-7.66/modules/system/system.info 2019-04-17 22:39:36.000000000 +0200
@@ -12,8 +12,7 @@
required = TRUE
configure = admin/config/system
-; Information added by Drupal.org packaging script on 2014-01-03
-version = "7.25"
+; Information added by Drupal.org packaging script on 2019-04-17
+version = "7.66"
project = "drupal"
-datestamp = "1388709506"
-
+datestamp = "1555533576"
diff -Naur drupal-7.25/modules/system/system.install drupal-7.66/modules/system/system.install
--- drupal-7.25/modules/system/system.install 2014-01-03 01:28:46.000000000 +0100
+++ drupal-7.66/modules/system/system.install 2019-04-17 22:20:46.000000000 +0200
@@ -160,7 +160,7 @@
if (empty($drivers)) {
$database_ok = FALSE;
$pdo_message = $t('Your web server does not appear to support any common PDO database extensions. Check with your hosting provider to see if they support PDO (PHP Data Objects) and offer any databases that Drupal supports.', array(
- '@drupal-databases' => 'http://drupal.org/node/270#database',
+ '@drupal-databases' => 'https://www.drupal.org/requirements/database',
));
}
// Make sure the native PDO extension is available, not the older PEAR
@@ -196,6 +196,12 @@
);
}
+ // Test database-specific multi-byte UTF-8 related requirements.
+ $charset_requirements = _system_check_db_utf8mb4_requirements($phase);
+ if (!empty($charset_requirements)) {
+ $requirements['database_charset'] = $charset_requirements;
+ }
+
// Test PHP memory_limit
$memory_limit = ini_get('memory_limit');
$requirements['php_memory_limit'] = array(
@@ -518,6 +524,75 @@
}
/**
+ * Checks whether the requirements for multi-byte UTF-8 support are met.
+ *
+ * @param string $phase
+ * The hook_requirements() stage.
+ *
+ * @return array
+ * A requirements array with the result of the charset check.
+ */
+function _system_check_db_utf8mb4_requirements($phase) {
+ global $install_state;
+ // In the requirements check of the installer, skip the utf8mb4 check unless
+ // the database connection info ha