updated core to 7.73

This commit is contained in:
2020-09-24 17:04:08 +02:00
parent 084d28842a
commit 7d87153100
524 changed files with 25194 additions and 7745 deletions
+1 -1
View File
@@ -189,7 +189,7 @@ function hook_aggregator_process($feed) {
*
* @ingroup aggregator
*/
function hook_aggregator_process_info($feed) {
function hook_aggregator_process_info() {
return array(
'title' => t('Default processor'),
'description' => t('Creates lightweight records of feed items.'),
+1 -1
View File
@@ -27,7 +27,7 @@ function aggregator_aggregator_fetch($feed) {
$headers['If-None-Match'] = $feed->etag;
}
if ($feed->modified) {
$headers['If-Modified-Since'] = gmdate(DATE_RFC1123, $feed->modified);
$headers['If-Modified-Since'] = gmdate(DATE_RFC7231, $feed->modified);
}
// Request feed.
+3 -4
View File
@@ -7,8 +7,7 @@ files[] = aggregator.test
configure = admin/config/services/aggregator/settings
stylesheets[all][] = aggregator.css
; Information added by drupal.org packaging script on 2013-04-03
version = "7.22"
; Information added by Drupal.org packaging script on 2020-09-16
version = "7.73"
project = "drupal"
datestamp = "1365027012"
datestamp = "1600272641"
+10
View File
@@ -260,6 +260,7 @@ function aggregator_schema() {
'primary key' => array('iid'),
'indexes' => array(
'fid' => array('fid'),
'timestamp' => array('timestamp'),
),
'foreign keys' => array(
'aggregator_feed' => array(
@@ -325,6 +326,15 @@ function aggregator_update_7003() {
db_add_index('aggregator_feed', 'url', array(array('url', 255)));
}
/**
* Add index on timestamp.
*/
function aggregator_update_7004() {
if (!db_index_exists('aggregator_item', 'timestamp')) {
db_add_index('aggregator_item', 'timestamp', array('timestamp'));
}
}
/**
* @} End of "addtogroup updates-7.x-extra"
*/
+8
View File
@@ -455,6 +455,14 @@ function aggregator_save_category($edit) {
db_delete('aggregator_category')
->condition('cid', $edit['cid'])
->execute();
// Remove category from feeds.
db_delete('aggregator_category_feed')
->condition('cid', $edit['cid'])
->execute();
// Remove category from feed items.
db_delete('aggregator_category_item')
->condition('cid', $edit['cid'])
->execute();
// Make sure there is no active block for this category.
if (module_exists('block')) {
db_delete('block')
+1 -1
View File
@@ -72,7 +72,7 @@ function aggregator_aggregator_remove($feed) {
*/
function aggregator_form_aggregator_admin_form_alter(&$form, $form_state) {
if (in_array('aggregator', variable_get('aggregator_processors', array('aggregator')))) {
$info = module_invoke('aggregator', 'aggregator_process', 'info');
$info = module_invoke('aggregator', 'aggregator_process_info');
$items = drupal_map_assoc(array(3, 5, 10, 15, 20, 25), '_aggregator_items');
$period = drupal_map_assoc(array(3600, 10800, 21600, 32400, 43200, 86400, 172800, 259200, 604800, 1209600, 2419200, 4838400, 9676800), 'format_interval');
$period[AGGREGATOR_CLEAR_NEVER] = t('Never');
+53 -2
View File
@@ -288,6 +288,10 @@ EOF;
return $GLOBALS['base_url'] . '/' . drupal_get_path('module', 'aggregator') . '/tests/aggregator_test_atom.xml';
}
function getHtmlEntitiesSample() {
return $GLOBALS['base_url'] . '/' . drupal_get_path('module', 'aggregator') . '/tests/aggregator_test_title_entities.xml';
}
/**
* Creates sample article nodes.
*
@@ -414,7 +418,7 @@ class CategorizeFeedTestCase extends AggregatorTestCase {
}
/**
* Creates a feed and makes sure you can add more than one category to it.
* Creates a feed and makes sure you can add/delete categories to it.
*/
function testCategorizeFeed() {
@@ -444,7 +448,31 @@ class CategorizeFeedTestCase extends AggregatorTestCase {
// Assert the feed has two categories.
$this->getFeedCategories($db_feed);
$this->assertEqual(count($db_feed->categories), 2, 'Feed has 2 categories');
// Use aggregator_save_feed() to delete a category.
$category = reset($categories);
aggregator_save_category(array('cid' => $category->cid));
// Assert that category is deleted.
$db_category = db_query("SELECT COUNT(*) FROM {aggregator_category} WHERE cid = :cid", array(':cid' => $category->cid))->fetchField();
$this->assertFalse($db_category, format_string('The category %title has been deleted.', array('%title' => $category->title)));
// Assert that category has been removed from feed.
$categorized_feeds = db_query("SELECT COUNT(*) FROM {aggregator_category_feed} WHERE cid = :cid", array(':cid' => $category->cid))->fetchField();
$this->assertFalse($categorized_feeds, format_string('The category %title has been removed from feed %feed_title.', array('%title' => $category->title, '%feed_title' => $feed['title'])));
// Assert that no broken links (associated with the deleted category)
// appear on one of the other category pages.
$this->createSampleNodes();
$this->drupalGet('admin/config/services/aggregator');
$this->clickLink('update items');
$categories = $this->getCategories();
$category = reset($categories);
$this->drupalGet('aggregator/categories/' . $category->cid);
global $base_path;
$this->assertNoRaw('<a href="' . $base_path . 'aggregator/categories/"></a>,');
}
}
/**
@@ -681,9 +709,21 @@ class CategorizeFeedItemTestCase extends AggregatorTestCase {
}
}
// Delete category from feed items when category is deleted.
$cid = reset($feed->categories);
$categories = $this->getCategories();
$category_title = $categories[$cid]->title;
// Delete category.
aggregator_save_category(array('cid' => $cid));
// Assert category has been removed from feed items.
$categorized_count = db_query("SELECT COUNT(*) FROM {aggregator_category_item} WHERE cid = :cid", array(':cid' => $cid))->fetchField();
$this->assertFalse($categorized_count, format_string('The category %title has been removed from feed items.', array('%title' => $category_title)));
// Delete feed.
$this->deleteFeed($feed);
}
}
/**
@@ -931,7 +971,7 @@ class AggregatorRenderingTestCase extends AggregatorTestCase {
// up.
$feed->block = 0;
aggregator_save_feed((array) $feed);
// It is nescessary to flush the cache after saving the number of items.
// It is necessary to flush the cache after saving the number of items.
drupal_flush_all_caches();
// Check that the block is no longer displayed.
$this->drupalGet('node');
@@ -1016,4 +1056,15 @@ class FeedParserTestCase extends AggregatorTestCase {
$this->assertText('Some text.');
$this->assertEqual('urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a', db_query('SELECT guid FROM {aggregator_item} WHERE link = :link', array(':link' => 'http://example.org/2003/12/13/atom03'))->fetchField(), 'Atom entry id element is parsed correctly.');
}
/**
* Tests a feed that uses HTML entities in item titles.
*/
function testHtmlEntitiesSample() {
$feed = $this->createFeed($this->getHtmlEntitiesSample());
aggregator_refresh($feed);
$this->drupalGet('aggregator/sources/' . $feed->fid);
$this->assertResponse(200, format_string('Feed %name exists.', array('%name' => $feed->title)));
$this->assertRaw("Quote&quot; Amp&amp;");
}
}
@@ -5,8 +5,7 @@ version = VERSION
core = 7.x
hidden = TRUE
; Information added by drupal.org packaging script on 2013-04-03
version = "7.22"
; Information added by Drupal.org packaging script on 2020-09-16
version = "7.73"
project = "drupal"
datestamp = "1365027012"
datestamp = "1600272641"
@@ -32,7 +32,7 @@ function aggregator_test_feed($use_last_modified = FALSE, $use_etag = FALSE) {
// Send appropriate response. We respond with a 304 not modified on either
// etag or on last modified.
if ($use_last_modified) {
drupal_add_http_header('Last-Modified', gmdate(DATE_RFC1123, $last_modified));
drupal_add_http_header('Last-Modified', gmdate(DATE_RFC7231, $last_modified));
}
if ($use_etag) {
drupal_add_http_header('ETag', $etag);
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<rss version="0.91">
<channel>
<title>Example with Entities</title>
<link>http://example.com</link>
<description>Example RSS Feed With HTML Entities in Title</description>
<language>en-us</language>
<item>
<title>Quote&quot; Amp&amp;</title>
<link>http://example.com/example-turns-one</link>
<description>Some text.</description>
</item>
</channel>
</rss>
+1 -1
View File
@@ -272,7 +272,7 @@ function block_admin_configure($form, &$form_state, $module, $delta) {
$form['settings']['title'] = array(
'#type' => 'textfield',
'#title' => t('Block title'),
'#maxlength' => 64,
'#maxlength' => 255,
'#description' => $block->module == 'block' ? t('The title of the block as shown to the user.') : t('Override the default title for the block. Use <em>!placeholder</em> to display no title, or leave blank to use the default block title.', array('!placeholder' => '&lt;none&gt;')),
'#default_value' => isset($block->title) ? $block->title : '',
'#weight' => -19,
+42 -13
View File
@@ -87,13 +87,13 @@
* and any value provided can be modified by a user on the block
* configuration screen.
* - pages: (optional) See 'visibility' above. A string that contains one or
* more page paths separated by '\n', '\r', or '\r\n' when 'visibility' is
* set to BLOCK_VISIBILITY_NOTLISTED or BLOCK_VISIBILITY_LISTED, or custom
* PHP code when 'visibility' is set to BLOCK_VISIBILITY_PHP. Paths may use
* '*' as a wildcard (matching any number of characters); '<front>'
* designates the site's front page. For BLOCK_VISIBILITY_PHP, the PHP
* code's return value should be TRUE if the block is to be made visible or
* FALSE if the block should not be visible.
* more page paths separated by "\n", "\r", or "\r\n" when 'visibility' is
* set to BLOCK_VISIBILITY_NOTLISTED or BLOCK_VISIBILITY_LISTED (example:
* "<front>\nnode/1"), or custom PHP code when 'visibility' is set to
* BLOCK_VISIBILITY_PHP. Paths may use '*' as a wildcard (matching any
* number of characters); '<front>' designates the site's front page. For
* BLOCK_VISIBILITY_PHP, the PHP code's return value should be TRUE if the
* block is to be made visible or FALSE if the block should not be visible.
*
* For a detailed usage example, see block_example.module.
*
@@ -200,11 +200,13 @@ function hook_block_save($delta = '', $edit = array()) {
* within the module, defined in hook_block_info().
*
* @return
* An array containing the following elements:
* Either an empty array so the block will not be shown or an array containing
* the following elements:
* - subject: The default localized title of the block. If the block does not
* have a default title, this should be set to NULL.
* - content: The content of the block's body. This may be a renderable array
* (preferable) or a string containing rendered HTML content.
* (preferable) or a string containing rendered HTML content. If the content
* is empty the block will not be shown.
*
* For a detailed usage example, see block_example.module.
*
@@ -253,8 +255,9 @@ function hook_block_view($delta = '') {
* specific block.
*
* @param $data
* An array of data, as returned from the hook_block_view() implementation of
* the module that defined the block:
* The data as returned from the hook_block_view() implementation of the
* module that defined the block. This could be an empty array or NULL value
* (if the block is empty) or an array containing:
* - subject: The default localized title of the block.
* - content: Either a string or a renderable array representing the content
* of the block. You should check that the content is an array before trying
@@ -287,8 +290,9 @@ function hook_block_view_alter(&$data, $block) {
* specific block, rather than implementing hook_block_view_alter().
*
* @param $data
* An array of data, as returned from the hook_block_view() implementation of
* the module that defined the block:
* The data as returned from the hook_block_view() implementation of the
* module that defined the block. This could be an empty array or NULL value
* (if the block is empty) or an array containing:
* - subject: The localized title of the block.
* - content: Either a string or a renderable array representing the content
* of the block. You should check that the content is an array before trying
@@ -359,6 +363,31 @@ function hook_block_list_alter(&$blocks) {
}
}
/**
* Act on block cache ID (cid) parts before the cid is generated.
*
* This hook allows you to add, remove or modify the custom keys used to
* generate a block cache ID (by default, these keys are set to the block
* module and delta). These keys will be combined with the standard ones
* provided by drupal_render_cid_parts() to generate the final block cache ID.
*
* To change the cache granularity used by drupal_render_cid_parts(), this hook
* cannot be used; instead, set the 'cache' key in the block's definition in
* hook_block_info().
*
* @params $cid_parts
* An array of elements used to build the cid.
* @param $block
* The block object being acted on.
*
* @see _block_get_cache_id()
*/
function hook_block_cid_parts_alter(&$cid_parts, $block) {
global $user;
// This example shows how to cache a block based on the user's timezone.
$cid_parts[] = $user->timezone;
}
/**
* @} End of "addtogroup hooks".
*/
+3 -4
View File
@@ -6,8 +6,7 @@ core = 7.x
files[] = block.test
configure = admin/structure/block
; Information added by drupal.org packaging script on 2013-04-03
version = "7.22"
; Information added by Drupal.org packaging script on 2020-09-16
version = "7.73"
project = "drupal"
datestamp = "1365027012"
datestamp = "1600272641"
+17 -1
View File
@@ -79,7 +79,7 @@ function block_schema() {
),
'title' => array(
'type' => 'varchar',
'length' => 64,
'length' => 255,
'not null' => TRUE,
'default' => '',
'description' => 'Custom title for the block. (Empty string will use block default title, <none> will remove the title, text will cause block to use specified title.)',
@@ -472,6 +472,22 @@ function block_update_7008() {
db_drop_field('block', 'throttle');
}
/**
* Increase {block}.title length to 255 characters.
*/
function block_update_7009() {
db_change_field('block', 'title', 'title',
array(
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
'description' => 'Custom title for the block. (Empty string will use block default title, <none> will remove the title, text will cause block to use specified title.)',
'translatable' => TRUE,
)
);
}
/**
* @} End of "addtogroup updates-7.x-extra".
*/
+3 -3
View File
@@ -24,7 +24,7 @@ Drupal.behaviors.blockSettingsSummary = {
$('fieldset#edit-node-type', context).drupalSetSummary(function (context) {
var vals = [];
$('input[type="checkbox"]:checked', context).each(function () {
vals.push($.trim($(this).next('label').text()));
vals.push($.trim($(this).next('label').html()));
});
if (!vals.length) {
vals.push(Drupal.t('Not restricted'));
@@ -35,7 +35,7 @@ Drupal.behaviors.blockSettingsSummary = {
$('fieldset#edit-role', context).drupalSetSummary(function (context) {
var vals = [];
$('input[type="checkbox"]:checked', context).each(function () {
vals.push($.trim($(this).next('label').text()));
vals.push($.trim($(this).next('label').html()));
});
if (!vals.length) {
vals.push(Drupal.t('Not restricted'));
@@ -49,7 +49,7 @@ Drupal.behaviors.blockSettingsSummary = {
return Drupal.t('Not customizable');
}
else {
return $radio.next('label').text();
return $radio.next('label').html();
}
});
}
+111 -50
View File
@@ -16,7 +16,7 @@ define('BLOCK_REGION_NONE', -1);
define('BLOCK_CUSTOM_FIXED', 0);
/**
* Shows this block by default, but lets individual users hide it.
* Shows this block by default, but lets individual users hide it.
*/
define('BLOCK_CUSTOM_ENABLED', 1);
@@ -59,6 +59,7 @@ function block_help($path, $arg) {
$output .= '<dd>' . t('Users with the <em>Administer blocks</em> permission can <a href="@block-add">add custom blocks</a>, which are then listed on the <a href="@blocks">Blocks administration page</a>. Once created, custom blocks behave just like default and module-generated blocks.', array('@blocks' => url('admin/structure/block'), '@block-add' => url('admin/structure/block/add'))) . '</dd>';
$output .= '</dl>';
return $output;
case 'admin/structure/block/add':
return '<p>' . t('Use this page to create a new custom block.') . '</p>';
}
@@ -66,7 +67,7 @@ function block_help($path, $arg) {
$demo_theme = !empty($arg[4]) ? $arg[4] : variable_get('theme_default', 'bartik');
$themes = list_themes();
$output = '<p>' . 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 <em>Save blocks</em> button at the bottom of the page. Click the <em>configure</em> link next to each block to configure its specific title and visibility settings.') . '</p>';
$output .= '<p>' . l(t('Demonstrate block regions (@theme)', array('@theme' => $themes[$demo_theme]->info['name'])), 'admin/structure/block/demo/' . $demo_theme) . '</p>';
$output .= '<p>' . l(t('Demonstrate block regions (!theme)', array('!theme' => $themes[$demo_theme]->info['name'])), 'admin/structure/block/demo/' . $demo_theme) . '</p>';
return $output;
}
}
@@ -143,7 +144,7 @@ function block_menu() {
);
foreach (list_themes() as $key => $theme) {
$items['admin/structure/block/list/' . $key] = array(
'title' => check_plain($theme->info['name']),
'title' => $theme->info['name'],
'page arguments' => array($key),
'type' => $key == $default_theme ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK,
'weight' => $key == $default_theme ? -10 : 0,
@@ -162,7 +163,7 @@ function block_menu() {
);
}
$items['admin/structure/block/demo/' . $key] = array(
'title' => check_plain($theme->info['name']),
'title' => $theme->info['name'],
'page callback' => 'block_admin_demo',
'page arguments' => array($key),
'type' => MENU_CALLBACK,
@@ -189,6 +190,7 @@ function _block_themes_access($theme) {
* @param $theme
* The theme whose blocks are being configured. If not set, the default theme
* is assumed.
*
* @return
* The theme that should be used for the block configuration page, or NULL
* to indicate that the default theme should be used.
@@ -261,7 +263,7 @@ function block_page_build(&$page) {
$all_regions = system_region_list($theme);
$item = menu_get_item();
if ($item['path'] != 'admin/structure/block/demo/' . $theme) {
if ($item === FALSE || $item['path'] != 'admin/structure/block/demo/' . $theme) {
// Load all region content assigned via blocks.
foreach (array_keys($all_regions) as $region) {
// Assign blocks to region.
@@ -281,10 +283,8 @@ function block_page_build(&$page) {
}
else {
// Append region description if we are rendering the regions demo page.
$item = menu_get_item();
if ($item['path'] == 'admin/structure/block/demo/' . $theme) {
$visible_regions = array_keys(system_region_list($theme, REGIONS_VISIBLE));
foreach ($visible_regions as $region) {
foreach (system_region_list($theme, REGIONS_VISIBLE, FALSE) as $region) {
$description = '<div class="block-region">' . $all_regions[$region] . '</div>';
$page[$region]['block_description'] = array(
'#markup' => $description,
@@ -343,14 +343,17 @@ function _block_get_renderable_array($list = array()) {
// to perform contextual actions on the help block, and the links needlessly
// draw attention on it.
if ($key != 'system_main' && $key != 'system_help') {
$build[$key]['#contextual_links']['block'] = array('admin/structure/block/manage', array($block->module, $block->delta));
$build[$key]['#contextual_links']['block'] = array(
'admin/structure/block/manage',
array($block->module, $block->delta),
);
}
$build[$key] += array(
'#block' => $block,
'#weight' => ++$weight,
);
$build[$key]['#theme_wrappers'][] ='block';
$build[$key]['#theme_wrappers'][] = 'block';
}
$build['#sorted'] = TRUE;
return $build;
@@ -386,59 +389,62 @@ function _block_rehash($theme = NULL) {
// Gather the blocks defined by modules.
foreach (module_implements('block_info') as $module) {
$module_blocks = module_invoke($module, 'block_info');
$delta_list = array();
foreach ($module_blocks as $delta => $block) {
// Compile a condition to retrieve this block from the database.
$condition = db_and()
->condition('module', $module)
->condition('delta', $delta);
$or->condition($condition);
// Add identifiers.
$delta_list[] = $delta;
$block['module'] = $module;
$block['delta'] = $delta;
$block['theme'] = $theme;
$block['delta'] = $delta;
$block['theme'] = $theme;
$current_blocks[$module][$delta] = $block;
}
if (!empty($delta_list)) {
$condition = db_and()->condition('module', $module)->condition('delta', $delta_list);
$or->condition($condition);
}
}
// Save the blocks defined in code for alter context.
$code_blocks = $current_blocks;
$database_blocks = db_select('block', 'b')
$database_blocks = db_select('block', 'b', array('fetch' => PDO::FETCH_ASSOC))
->fields('b')
->condition($or)
->condition('theme', $theme)
->execute();
$original_database_blocks = array();
foreach ($database_blocks as $block) {
// Preserve info which is not in the database.
$block->info = $current_blocks[$block->module][$block->delta]['info'];
$module = $block['module'];
$delta = $block['delta'];
$original_database_blocks[$module][$delta] = $block;
// The cache mode can only by set from hook_block_info(), so that has
// precedence over the database's value.
if (isset($current_blocks[$block->module][$block->delta]['cache'])) {
$block->cache = $current_blocks[$block->module][$block->delta]['cache'];
if (isset($current_blocks[$module][$delta]['cache'])) {
$block['cache'] = $current_blocks[$module][$delta]['cache'];
}
// Preserve info which is not in the database.
$block['info'] = $current_blocks[$module][$delta]['info'];
// Blocks stored in the database override the blocks defined in code.
$current_blocks[$block->module][$block->delta] = get_object_vars($block);
$current_blocks[$module][$delta] = $block;
// Preserve this block.
$bids[$block->bid] = $block->bid;
$bids[$block['bid']] = $block['bid'];
}
drupal_alter('block_info', $current_blocks, $theme, $code_blocks);
foreach ($current_blocks as $module => $module_blocks) {
foreach ($module_blocks as $delta => $block) {
if (!isset($block['pages'])) {
// {block}.pages is type 'text', so it cannot have a
// default value, and not null, so we need to provide
// value if the module did not.
$block['pages'] = '';
}
// Make sure weight is set.
if (!isset($block['weight'])) {
$block['weight'] = 0;
}
// Make sure certain attributes are set.
$block += array(
'pages' => '',
'weight' => 0,
'status' => 0,
);
// Check for active blocks in regions that are not available.
if (!empty($block['region']) && $block['region'] != BLOCK_REGION_NONE && !isset($regions[$block['region']]) && $block['status'] == 1) {
drupal_set_message(t('The block %info was assigned to the invalid region %region and has been disabled.', array('%info' => $block['info'], '%region' => $block['region'])), 'warning');
// Disabled modules are moved into the BLOCK_REGION_NONE later so no
// need to move the block to another region.
$block['status'] = 0;
}
// Set region to none if not enabled and make sure status is set.
// Set region to none if not enabled.
if (empty($block['status'])) {
$block['status'] = 0;
$block['region'] = BLOCK_REGION_NONE;
@@ -456,7 +462,15 @@ function _block_rehash($theme = NULL) {
else {
$primary_keys = array();
}
drupal_write_record('block', $block, $primary_keys);
// If the block is new or differs from the original database block, save
// it. To determine whether there was a change it is enough to examine
// the values for the keys in the original database record as that
// contained every database field.
if (!$primary_keys || array_diff_assoc($original_database_blocks[$module][$delta], $block)) {
drupal_write_record('block', $block, $primary_keys);
// Make it possible to test this.
$block['saved'] = TRUE;
}
// Add to the list of blocks we return.
$blocks[] = $block;
}
@@ -632,7 +646,8 @@ function block_theme_initialize($theme) {
$regions = system_region_list($theme, REGIONS_VISIBLE);
$result = db_query("SELECT * FROM {block} WHERE theme = :theme", array(':theme' => $default_theme), array('fetch' => PDO::FETCH_ASSOC));
foreach ($result as $block) {
// If the region isn't supported by the theme, assign the block to the theme's default region.
// If the region isn't supported by the theme, assign the block to the
// theme's default region.
if ($block['status'] && !isset($regions[$block['region']])) {
$block['region'] = system_default_region($theme);
}
@@ -680,6 +695,9 @@ function block_list($region) {
/**
* Loads a block object from the database.
*
* This function returns the first block matching the module and delta
* parameters, so it should not be used for theme-specific functionality.
*
* @param $module
* Name of the module that implements the block to load.
* @param $delta
@@ -797,17 +815,18 @@ function block_block_list_alter(&$blocks) {
// with different case. Ex: /Page, /page, /PAGE.
$pages = drupal_strtolower($block->pages);
if ($block->visibility < BLOCK_VISIBILITY_PHP) {
// Convert the Drupal path to lowercase
// Convert the Drupal path to lowercase.
$path = drupal_strtolower(drupal_get_path_alias($_GET['q']));
// Compare the lowercase internal and lowercase path alias (if any).
$page_match = drupal_match_path($path, $pages);
if ($path != $_GET['q']) {
$page_match = $page_match || drupal_match_path($_GET['q'], $pages);
}
// When $block->visibility has a value of 0 (BLOCK_VISIBILITY_NOTLISTED),
// the block is displayed on all pages except those listed in $block->pages.
// When set to 1 (BLOCK_VISIBILITY_LISTED), it is displayed only on those
// pages listed in $block->pages.
// When $block->visibility has a value of 0
// (BLOCK_VISIBILITY_NOTLISTED), the block is displayed on all pages
// except those listed in $block->pages. When set to 1
// (BLOCK_VISIBILITY_LISTED), it is displayed only on those pages
// listed in $block->pages.
$page_match = !($block->visibility xor $page_match);
}
elseif (module_exists('php')) {
@@ -830,32 +849,71 @@ function block_block_list_alter(&$blocks) {
* Render the content and subject for a set of blocks.
*
* @param $region_blocks
* An array of block objects such as returned for one region by _block_load_blocks().
* An array of block objects such as returned for one region by
* _block_load_blocks().
*
* @return
* An array of visible blocks as expected by drupal_render().
*/
function _block_render_blocks($region_blocks) {
// Block caching is not compatible with node access modules. We also
// preserve the submission of forms in blocks, by fetching from cache only
$cacheable = TRUE;
// We preserve the submission of forms in blocks, by fetching from cache only
// if the request method is 'GET' (or 'HEAD').
$cacheable = !count(module_implements('node_grants')) && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD');
if ($_SERVER['REQUEST_METHOD'] != 'GET' && $_SERVER['REQUEST_METHOD'] != 'HEAD') {
$cacheable = FALSE;
}
// Block caching is not usually compatible with node access modules, so by
// default it is disabled when node access modules exist. However, it can be
// allowed by using the variable 'block_cache_bypass_node_grants'.
elseif (!variable_get('block_cache_bypass_node_grants', FALSE) && count(module_implements('node_grants'))) {
$cacheable = FALSE;
}
// Proceed to loop over all blocks in order to compute their respective cache
// identifiers; this allows us to do one single cache_get_multiple() call
// instead of doing one cache_get() call per block.
$cached_blocks = array();
$cids = array();
if ($cacheable) {
foreach ($region_blocks as $key => $block) {
if (!isset($block->content)) {
if (($cid = _block_get_cache_id($block))) {
$cids[$key] = $cid;
}
}
}
if ($cids) {
// We cannot pass $cids in directly because cache_get_multiple() will
// modify it, and we need to use it later on in this function.
$cid_values = array_values($cids);
$cached_blocks = cache_get_multiple($cid_values, 'cache_block');
}
}
foreach ($region_blocks as $key => $block) {
// Render the block content if it has not been created already.
if (!isset($block->content)) {
// Erase the block from the static array - we'll put it back if it has
// content.
unset($region_blocks[$key]);
// Try fetching the block from cache.
if ($cacheable && ($cid = _block_get_cache_id($block)) && ($cache = cache_get($cid, 'cache_block'))) {
$array = $cache->data;
$cid = empty($cids[$key]) ? NULL : $cids[$key];
// Try fetching the block from the previously loaded cache entries.
if (isset($cached_blocks[$cid])) {
$array = $cached_blocks[$cid]->data;
}
else {
$array = module_invoke($block->module, 'block_view', $block->delta);
// Valid PHP function names cannot contain hyphens.
$delta = str_replace('-', '_', $block->delta);
// Allow modules to modify the block before it is viewed, via either
// hook_block_view_alter() or hook_block_view_MODULE_DELTA_alter().
drupal_alter(array('block_view', "block_view_{$block->module}_{$block->delta}"), $array, $block);
drupal_alter(array('block_view', "block_view_{$block->module}_{$delta}"), $array, $block);
if (isset($cid)) {
cache_set($cid, $array, 'cache_block', CACHE_TEMPORARY);
@@ -900,6 +958,8 @@ function _block_render_blocks($region_blocks) {
* Theme and language contexts are automatically differentiated.
*
* @param $block
* The block to get the cache_id from.
*
* @return
* The string used as cache_id for the block.
*/
@@ -914,6 +974,7 @@ function _block_get_cache_id($block) {
// Start with common sub-patterns: block identification, theme, language.
$cid_parts[] = $block->module;
$cid_parts[] = $block->delta;
drupal_alter('block_cid_parts', $cid_parts, $block);
$cid_parts = array_merge($cid_parts, drupal_render_cid_parts($block->cache));
return implode(':', $cid_parts);
@@ -1013,7 +1074,7 @@ function block_menu_delete($menu) {
* Implements hook_form_FORM_ID_alter().
*/
function block_form_system_performance_settings_alter(&$form, &$form_state) {
$disabled = count(module_implements('node_grants'));
$disabled = (!variable_get('block_cache_bypass_node_grants', FALSE) && count(module_implements('node_grants')));
$form['caching']['block_cache'] = array(
'#type' => 'checkbox',
'#title' => t('Cache blocks'),
+123 -3
View File
@@ -75,7 +75,7 @@ class BlockTestCase extends DrupalWebTestCase {
$bid = db_query("SELECT bid FROM {block_custom} WHERE info = :info", array(':info' => $custom_block['info']))->fetchField();
// Check to see if the custom block was created by checking that it's in the database.
$this->assertNotNull($bid, 'Custom block found in database');
$this->assertTrue($bid, 'Custom block found in database');
// Check that block_block_view() returns the correct title and content.
$data = block_block_view($bid);
@@ -193,7 +193,7 @@ class BlockTestCase extends DrupalWebTestCase {
}
/**
* Test block visibility when using "pages" restriction but leaving
* Test block visibility when using "pages" restriction but leaving
* "pages" textarea empty
*/
function testBlockVisibilityListedEmpty() {
@@ -305,7 +305,7 @@ class BlockTestCase extends DrupalWebTestCase {
))->fetchField();
// Check to see if the block was created by checking that it's in the database.
$this->assertNotNull($bid, 'Block found in database');
$this->assertTrue($bid, 'Block found in database');
// Check whether the block can be moved to all available regions.
foreach ($this->regions as $region) {
@@ -752,6 +752,48 @@ class BlockTemplateSuggestionsUnitTest extends DrupalUnitTestCase {
}
}
/**
* Tests for hook_block_view_MODULE_DELTA_alter().
*/
class BlockViewModuleDeltaAlterWebTest extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Block view module delta alter',
'description' => 'Test the hook_block_view_MODULE_DELTA_alter() hook.',
'group' => 'Block',
);
}
public function setUp() {
parent::setUp(array('block_test'));
}
/**
* Tests that the alter hook is called, even if the delta contains a hyphen.
*/
public function testBlockViewModuleDeltaAlter() {
$block = new stdClass;
$block->module = 'block_test';
$block->delta = 'test_underscore';
$block->title = '';
$render_array = _block_render_blocks(array('region' => $block));
$render = array_pop($render_array);
$test_underscore = $render->content['#markup'];
$this->assertEqual($test_underscore, 'hook_block_view_MODULE_DELTA_alter', 'Found expected altered block content for delta with underscore');
$block = new stdClass;
$block->module = 'block_test';
$block->delta = 'test-hyphen';
$block->title = '';
$render_array = _block_render_blocks(array('region' => $block));
$render = array_pop($render_array);
$test_hyphen = $render->content['#markup'];
$this->assertEqual($test_hyphen, 'hook_block_view_MODULE_DELTA_alter', 'Hyphens (-) in block delta were replaced by underscore (_)');
}
}
/**
* Tests that hidden regions do not inherit blocks when a theme is enabled.
*/
@@ -857,3 +899,81 @@ class BlockInvalidRegionTestCase extends DrupalWebTestCase {
$this->assertNoRaw($warning_message, 'Disabled block in the invalid region will not trigger the warning.');
}
}
/**
* Tests that block rehashing works correctly.
*/
class BlockHashTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Block rehash',
'description' => 'Checks _block_rehash() functionality.',
'group' => 'Block',
);
}
function setUp() {
parent::setUp(array('block'));
}
/**
* Tests that block rehashing does not write to the database too often.
*/
function testBlockRehash() {
// No hook_block_info_alter(), no save.
$this->doRehash();
module_enable(array('block_test'), FALSE);
// Save the new blocks, check that the new blocks exist by checking weight.
_block_rehash();
$this->assertWeight(0);
// Now hook_block_info_alter() exists but no blocks are saved on a second
// rehash.
$this->doRehash();
$this->assertWeight(0);
// Now hook_block_info_alter() exists and is changing one block which
// should be saved.
$GLOBALS['conf']['block_test_info_alter'] = 1;
$this->doRehash(TRUE);
$this->assertWeight(10000);
// Now hook_block_info_alter() exists but already changed the block's
// weight before, so it should not be saved again.
$this->doRehash();
$this->assertWeight(10000);
}
/**
* Performs a block rehash and checks several related assertions.
*
* @param $alter_active
* Set to TRUE if the block_test module's hook_block_info_alter()
* implementation is expected to make a change that results in an existing
* block needing to be resaved to the database. Defaults to FALSE.
*/
function doRehash($alter_active = FALSE) {
$saves = 0;
foreach (_block_rehash() as $block) {
$module = $block['module'];
$delta = $block['delta'];
if ($alter_active && $module == 'block_test' && $delta == 'test_html_id') {
$this->assertFalse(empty($block['saved']), "$module $delta saved");
$saves++;
}
else {
$this->assertTrue(empty($block['saved']), "$module $delta not saved");
}
}
$this->assertEqual($alter_active, $saves);
}
/**
* Asserts that the block_test module's block has a given weight.
*
* @param $weight
* The expected weight.
*/
function assertWeight($weight) {
$db_weight = db_query('SELECT weight FROM {block} WHERE module = :module AND delta = :delta', array(':module' => 'block_test', ':delta' => 'test_html_id'))->fetchField();
// By casting to string the assert fails on FALSE.
$this->assertIdentical((string) $db_weight, (string) $weight);
}
}
+3 -4
View File
@@ -5,8 +5,7 @@ version = VERSION
core = 7.x
hidden = TRUE
; Information added by drupal.org packaging script on 2013-04-03
version = "7.22"
; Information added by Drupal.org packaging script on 2020-09-16
version = "7.73"
project = "drupal"
datestamp = "1365027012"
datestamp = "1600272641"
+31
View File
@@ -22,6 +22,14 @@ function block_test_block_info() {
'cache' => variable_get('block_test_caching', DRUPAL_CACHE_PER_ROLE),
);
$blocks['test_underscore'] = array(
'info' => t('Test underscore'),
);
$blocks['test-hyphen'] = array(
'info' => t('Test hyphen'),
);
$blocks['test_html_id'] = array(
'info' => t('Test block html id'),
);
@@ -34,3 +42,26 @@ function block_test_block_info() {
function block_test_block_view($delta = 0) {
return array('content' => variable_get('block_test_content', ''));
}
/**
* Implements hook_block_view_MODULE_DELTA_alter().
*/
function block_test_block_view_block_test_test_underscore_alter(&$data, $block) {
$data['content'] = 'hook_block_view_MODULE_DELTA_alter';
}
/**
* Implements hook_block_view_MODULE_DELTA_alter().
*/
function block_test_block_view_block_test_test_hyphen_alter(&$data, $block) {
$data['content'] = 'hook_block_view_MODULE_DELTA_alter';
}
/**
* Implements hook_block_info_alter().
*/
function block_test_block_info_alter(&$blocks) {
if (variable_get('block_test_info_alter')) {
$blocks['block_test']['test_html_id']['weight'] = 10000;
}
}
@@ -13,8 +13,7 @@ regions[footer] = Footer
regions[highlighted] = Highlighted
regions[help] = Help
; Information added by drupal.org packaging script on 2013-04-03
version = "7.22"
; Information added by Drupal.org packaging script on 2020-09-16
version = "7.73"
project = "drupal"
datestamp = "1365027012"
datestamp = "1600272641"
+3 -4
View File
@@ -5,8 +5,7 @@ version = VERSION
core = 7.x
files[] = blog.test
; Information added by drupal.org packaging script on 2013-04-03
version = "7.22"
; Information added by Drupal.org packaging script on 2020-09-16
version = "7.73"
project = "drupal"
datestamp = "1365027012"
datestamp = "1600272641"
+1 -1
View File
@@ -152,7 +152,7 @@ function blog_menu_local_tasks_alter(&$data, $router_item, $root_path) {
}
}
// Provide a helper action link to the author on the 'blog/%' page.
elseif ($root_path == 'blog/%' && $router_item['page_arguments'][0]->uid == $user->uid) {
elseif ($root_path == 'blog/%' && isset($router_item['page_arguments'][0]->uid) && $router_item['page_arguments'][0]->uid == $user->uid) {
$data['actions']['output']['blog'] = array(
'#theme' => 'menu_local_action',
);
+20 -20
View File
@@ -42,8 +42,8 @@ class BlogTestCase extends DrupalWebTestCase {
$this->drupalGet('blog/' . $this->big_user->uid);
$this->assertResponse(200);
$this->assertTitle(t("@name's blog", array('@name' => format_username($this->big_user))) . ' | Drupal', t('Blog title was displayed'));
$this->assertText(t('You are not allowed to post a new blog entry.'), t('No new entries can be posted without the right permission'));
$this->assertTitle(t("@name's blog", array('@name' => format_username($this->big_user))) . ' | Drupal', 'Blog title was displayed');
$this->assertText(t('You are not allowed to post a new blog entry.'), 'No new entries can be posted without the right permission');
}
/**
@@ -54,8 +54,8 @@ class BlogTestCase extends DrupalWebTestCase {
$this->drupalGet('blog/' . $this->own_user->uid);
$this->assertResponse(200);
$this->assertTitle(t("@name's blog", array('@name' => format_username($this->own_user))) . ' | Drupal', t('Blog title was displayed'));
$this->assertText(t('@author has not created any blog entries.', array('@author' => format_username($this->own_user))), t('Users blog displayed with no entries'));
$this->assertTitle(t("@name's blog", array('@name' => format_username($this->own_user))) . ' | Drupal', 'Blog title was displayed');
$this->assertText(t('@author has not created any blog entries.', array('@author' => format_username($this->own_user))), 'Users blog displayed with no entries');
}
/**
@@ -73,7 +73,7 @@ class BlogTestCase extends DrupalWebTestCase {
$edit = array();
$edit['blog_block_count'] = 5;
$this->drupalPost('admin/structure/block/manage/blog/recent/configure', $edit, t('Save block'));
$this->assertEqual(variable_get('blog_block_count', 10), 5, t('Number of recent blog posts changed.'));
$this->assertEqual(variable_get('blog_block_count', 10), 5, 'Number of recent blog posts changed.');
// Do basic tests for each user.
$this->doBasicTests($this->any_user, TRUE);
@@ -132,31 +132,31 @@ class BlogTestCase extends DrupalWebTestCase {
$this->drupalGet('admin/help/blog');
$this->assertResponse($response2);
if ($response2 == 200) {
$this->assertTitle(t('Blog | Drupal'), t('Blog help node was displayed'));
$this->assertText(t('Blog'), t('Blog help node was displayed'));
$this->assertTitle(t('Blog | Drupal'), 'Blog help node was displayed');
$this->assertText(t('Blog'), 'Blog help node was displayed');
}
// Verify the blog block was displayed.
$this->drupalGet('');
$this->assertResponse(200);
$this->assertText(t('Recent blog posts'), t('Blog block was displayed'));
$this->assertText(t('Recent blog posts'), 'Blog block was displayed');
// View blog node.
$this->drupalGet('node/' . $node->nid);
$this->assertResponse(200);
$this->assertTitle($node->title . ' | Drupal', t('Blog node was displayed'));
$this->assertTitle($node->title . ' | Drupal', 'Blog node was displayed');
$breadcrumb = array(
l(t('Home'), NULL),
l(t('Blogs'), 'blog'),
l(t("!name's blog", array('!name' => format_username($node_user))), 'blog/' . $node_user->uid),
);
$this->assertRaw(theme('breadcrumb', array('breadcrumb' => $breadcrumb)), t('Breadcrumbs were displayed'));
$this->assertRaw(theme('breadcrumb', array('breadcrumb' => $breadcrumb)), 'Breadcrumbs were displayed');
// View blog edit node.
$this->drupalGet('node/' . $node->nid . '/edit');
$this->assertResponse($response);
if ($response == 200) {
$this->assertTitle('Edit Blog entry ' . $node->title . ' | Drupal', t('Blog edit node was displayed'));
$this->assertTitle('Edit Blog entry ' . $node->title . ' | Drupal', 'Blog edit node was displayed');
}
if ($response == 200) {
@@ -166,12 +166,12 @@ class BlogTestCase extends DrupalWebTestCase {
$edit["title"] = 'node/' . $node->nid;
$edit["body[$langcode][0][value]"] = $this->randomName(256);
$this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
$this->assertRaw(t('Blog entry %title has been updated.', array('%title' => $edit["title"])), t('Blog node was edited'));
$this->assertRaw(t('Blog entry %title has been updated.', array('%title' => $edit["title"])), 'Blog node was edited');
// Delete blog node.
$this->drupalPost('node/' . $node->nid . '/delete', array(), t('Delete'));
$this->assertResponse($response);
$this->assertRaw(t('Blog entry %title has been deleted.', array('%title' => $edit["title"])), t('Blog node was deleted'));
$this->assertRaw(t('Blog entry %title has been deleted.', array('%title' => $edit["title"])), 'Blog node was deleted');
}
}
@@ -185,29 +185,29 @@ class BlogTestCase extends DrupalWebTestCase {
// Confirm blog entries link exists on the user page.
$this->drupalGet('user/' . $user->uid);
$this->assertResponse(200);
$this->assertText(t('View recent blog entries'), t('View recent blog entries link was displayed'));
$this->assertText(t('View recent blog entries'), 'View recent blog entries link was displayed');
// Confirm the recent blog entries link goes to the user's blog page.
$this->clickLink('View recent blog entries');
$this->assertTitle(t("@name's blog | Drupal", array('@name' => format_username($user))), t('View recent blog entries link target was correct'));
$this->assertTitle(t("@name's blog | Drupal", array('@name' => format_username($user))), 'View recent blog entries link target was correct');
// Confirm a blog page was displayed.
$this->drupalGet('blog');
$this->assertResponse(200);
$this->assertTitle('Blogs | Drupal', t('Blog page was displayed'));
$this->assertText(t('Home'), t('Breadcrumbs were displayed'));
$this->assertTitle('Blogs | Drupal', 'Blog page was displayed');
$this->assertText(t('Home'), 'Breadcrumbs were displayed');
$this->assertLink(t('Create new blog entry'));
// Confirm a blog page was displayed per user.
$this->drupalGet('blog/' . $user->uid);
$this->assertTitle(t("@name's blog | Drupal", array('@name' => format_username($user))), t('User blog node was displayed'));
$this->assertTitle(t("@name's blog | Drupal", array('@name' => format_username($user))), 'User blog node was displayed');
// Confirm a blog feed was displayed.
$this->drupalGet('blog/feed');
$this->assertTitle(t('Drupal blogs'), t('Blog feed was displayed'));
$this->assertTitle(t('Drupal blogs'), 'Blog feed was displayed');
// Confirm a blog feed was displayed per user.
$this->drupalGet('blog/' . $user->uid . '/feed');
$this->assertTitle(t("@name's blog", array('@name' => format_username($user))), t('User blog feed was displayed'));
$this->assertTitle(t("@name's blog", array('@name' => format_username($user))), 'User blog feed was displayed');
}
}
+3 -4
View File
@@ -7,8 +7,7 @@ files[] = book.test
configure = admin/content/book/settings
stylesheets[all][] = book.css
; Information added by drupal.org packaging script on 2013-04-03
version = "7.22"
; Information added by Drupal.org packaging script on 2020-09-16
version = "7.73"
project = "drupal"
datestamp = "1365027012"
datestamp = "1600272641"
+8 -5
View File
@@ -768,11 +768,13 @@ function book_prev($book_link) {
return NULL;
}
$flat = book_get_flat_menu($book_link);
// Assigning the array to $flat resets the array pointer for use with each().
reset($flat);
$curr = NULL;
do {
$prev = $curr;
list($key, $curr) = each($flat);
$curr = current($flat);
$key = key($flat);
next($flat);
} while ($key && $key != $book_link['mlid']);
if ($key == $book_link['mlid']) {
@@ -806,9 +808,10 @@ function book_prev($book_link) {
*/
function book_next($book_link) {
$flat = book_get_flat_menu($book_link);
// Assigning the array to $flat resets the array pointer for use with each().
reset($flat);
do {
list($key, $curr) = each($flat);
$key = key($flat);
next($flat);
}
while ($key && $key != $book_link['mlid']);
@@ -1375,7 +1378,7 @@ function book_link_load($mlid) {
* A fully loaded menu link.
*
* @return
* An subtree of menu links in an array, in the order they should be rendered.
* A subtree of menu links in an array, in the order they should be rendered.
*/
function book_menu_subtree_data($link) {
$tree = &drupal_static(__FUNCTION__, array());
+3 -4
View File
@@ -5,8 +5,7 @@ version = VERSION
core = 7.x
files[] = color.test
; Information added by drupal.org packaging script on 2013-04-03
version = "7.22"
; Information added by Drupal.org packaging script on 2020-09-16
version = "7.73"
project = "drupal"
datestamp = "1365027012"
datestamp = "1600272641"
+55 -6
View File
@@ -240,6 +240,7 @@ function color_scheme_form($complete_form, &$form_state, $theme) {
$form['palette'][$name] = array(
'#type' => 'textfield',
'#title' => check_plain($names[$name]),
'#value_callback' => 'color_palette_color_value',
'#default_value' => $value,
'#size' => 8,
);
@@ -294,6 +295,52 @@ function theme_color_scheme_form($variables) {
return $output;
}
/**
* Determines the value for a palette color field.
*
* @param $element
* The form element whose value is being populated.
* @param $input
* The incoming input to populate the form element. If this is FALSE,
* the element's default value should be returned.
* @param $form_state
* A keyed array containing the current state of the form.
*
* @return
* The data that will appear in the $form_state['values'] collection for this
* element. Return nothing to use the default.
*/
function color_palette_color_value($element, $input = FALSE, $form_state = array()) {
// If we suspect a possible cross-site request forgery attack, only accept
// hexadecimal CSS color strings from user input, to avoid problems when this
// value is used in the JavaScript preview.
if ($input !== FALSE) {
// Start with the provided value for this textfield, and validate that if
// necessary, falling back on the default value.
$value = form_type_textfield_value($element, $input, $form_state);
if (!$value || !isset($form_state['complete form']['#token']) || color_valid_hexadecimal_string($value) || drupal_valid_token($form_state['values']['form_token'], $form_state['complete form']['#token'])) {
return $value;
}
else {
return $element['#default_value'];
}
}
}
/**
* Determines if a hexadecimal CSS color string is valid.
*
* @param $color
* The string to check.
*
* @return
* TRUE if the string is a valid hexadecimal CSS color string, or FALSE if it
* isn't.
*/
function color_valid_hexadecimal_string($color) {
return preg_match('/^#([a-f0-9]{3}){1,2}$/iD', $color);
}
/**
* Form validation handler for color_scheme_form().
*
@@ -302,7 +349,7 @@ function theme_color_scheme_form($variables) {
function color_scheme_form_validate($form, &$form_state) {
// Only accept hexadecimal CSS color strings to avoid XSS upon use.
foreach ($form_state['values']['palette'] as $key => $color) {
if (!preg_match('/^#([a-f0-9]{3}){1,2}$/iD', $color)) {
if (!color_valid_hexadecimal_string($color)) {
form_set_error('palette][' . $key, t('%name must be a valid hexadecimal CSS color value.', array('%name' => $form['color']['palette'][$key]['#title'])));
}
}
@@ -346,9 +393,10 @@ function color_scheme_form_submit($form, &$form_state) {
// memory_get_usage(), therefore we won't inadvertently reject a color
// scheme change based on a faulty memory calculation.
$usage = memory_get_usage(TRUE);
$limit = parse_size(ini_get('memory_limit'));
if ($usage + $required > $limit) {
drupal_set_message(t('There is not enough memory available to PHP to change this theme\'s color scheme. You need at least %size more. Check the <a href="@url">PHP documentation</a> for more information.', array('%size' => format_size($usage + $required - $limit), '@url' => 'http://www.php.net/manual/ini.core.php#ini.sect.resource-limits')), 'error');
$memory_limit = ini_get('memory_limit');
$size = parse_size($memory_limit);
if (!drupal_check_memory_limit($usage + $required, $memory_limit)) {
drupal_set_message(t('There is not enough memory available to PHP to change this theme\'s color scheme. You need at least %size more. Check the <a href="@url">PHP documentation</a> for more information.', array('%size' => format_size($usage + $required - $size), '@url' => 'http://www.php.net/manual/ini.core.php#ini.sect.resource-limits')), 'error');
return;
}
}
@@ -686,8 +734,9 @@ function _color_blend($img, $hex1, $hex2, $alpha) {
* Converts a hex color into an RGB triplet.
*/
function _color_unpack($hex, $normalize = FALSE) {
if (strlen($hex) == 4) {
$hex = $hex[1] . $hex[1] . $hex[2] . $hex[2] . $hex[3] . $hex[3];
$hex = substr($hex, 1);
if (strlen($hex) == 3) {
$hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
}
$c = hexdec($hex);
for ($i = 16; $i >= 0; $i -= 8) {
+1 -1
View File
@@ -122,7 +122,7 @@ class ColorTestCase extends DrupalWebTestCase {
$edit['palette[bg]'] = $color;
$this->drupalPost($settings_path, $edit, t('Save configuration'));
if($is_valid) {
if ($is_valid) {
$this->assertText('The configuration options have been saved.');
}
else {
+3 -4
View File
@@ -9,8 +9,7 @@ files[] = comment.test
configure = admin/content/comment
stylesheets[all][] = comment.css
; Information added by drupal.org packaging script on 2013-04-03
version = "7.22"
; Information added by Drupal.org packaging script on 2020-09-16
version = "7.73"
project = "drupal"
datestamp = "1365027012"
datestamp = "1600272641"
-3
View File
@@ -9,9 +9,6 @@
* Implements hook_uninstall().
*/
function comment_uninstall() {
// Delete comment_body field.
field_delete_field('comment_body');
// Remove variables.
variable_del('comment_block_count');
$node_types = array_keys(node_type_get_types());
+24 -17
View File
@@ -152,7 +152,7 @@ function comment_node_type_load($name) {
}
/**
* Entity URI callback.
* Implements callback_entity_info_uri().
*/
function comment_uri($comment) {
return array(
@@ -490,7 +490,7 @@ function comment_permalink($cid) {
// Return the node view, this will show the correct comment in context.
return menu_execute_active_handler('node/' . $node->nid, FALSE);
}
drupal_not_found();
return MENU_NOT_FOUND;
}
/**
@@ -993,12 +993,7 @@ function comment_build_content($comment, $node, $view_mode = 'full', $langcode =
$comment->content = array();
// Allow modules to change the view mode.
$context = array(
'entity_type' => 'comment',
'entity' => $comment,
'langcode' => $langcode,
);
drupal_alter('entity_view_mode', $view_mode, $context);
$view_mode = key(entity_view_mode_prepare('comment', array($comment->cid => $comment), $view_mode, $langcode));
// Build fields content.
field_attach_prepare_view('comment', array($comment->cid => $comment), $view_mode, $langcode);
@@ -1108,17 +1103,26 @@ function comment_links($comment, $node) {
* An array in the format expected by drupal_render().
*/
function comment_view_multiple($comments, $node, $view_mode = 'full', $weight = 0, $langcode = NULL) {
field_attach_prepare_view('comment', $comments, $view_mode, $langcode);
entity_prepare_view('comment', $comments, $langcode);
$build = array();
$entities_by_view_mode = entity_view_mode_prepare('comment', $comments, $view_mode, $langcode);
foreach ($entities_by_view_mode as $entity_view_mode => $entities) {
field_attach_prepare_view('comment', $entities, $entity_view_mode, $langcode);
entity_prepare_view('comment', $entities, $langcode);
foreach ($entities as $entity) {
$build[$entity->cid] = comment_view($entity, $node, $entity_view_mode, $langcode);
}
}
$build = array(
'#sorted' => TRUE,
);
foreach ($comments as $comment) {
$build[$comment->cid] = comment_view($comment, $node, $view_mode, $langcode);
$build[$comment->cid]['#weight'] = $weight;
$weight++;
}
// Sort here, to preserve the input order of the entities that were passed to
// this function.
uasort($build, 'element_sort');
$build['#sorted'] = TRUE;
return $build;
}
@@ -2304,7 +2308,7 @@ function template_preprocess_comment(&$variables) {
$variables['signature'] = $comment->signature;
$uri = entity_uri('comment', $comment);
$uri['options'] += array('attributes' => array('class' => 'permalink', 'rel' => 'bookmark'));
$uri['options'] += array('attributes' => array('class' => array('permalink'), 'rel' => 'bookmark'));
$variables['title'] = l($comment->subject, $uri['path'], $uri['options']);
$variables['permalink'] = l(t('Permalink'), $uri['path'], $uri['options']);
@@ -2603,7 +2607,7 @@ function comment_unpublish_action($comment, $context = array()) {
/**
* Unpublishes a comment if it contains certain keywords.
*
* @param $comment
* @param object $comment
* Comment object to modify.
* @param array $context
* Array with components:
@@ -2615,10 +2619,13 @@ function comment_unpublish_action($comment, $context = array()) {
* @see comment_unpublish_by_keyword_action_submit()
*/
function comment_unpublish_by_keyword_action($comment, $context) {
$node = node_load($comment->nid);
$build = comment_view($comment, $node);
$text = drupal_render($build);
foreach ($context['keywords'] as $keyword) {
$text = drupal_render($comment);
if (strpos($text, $keyword) !== FALSE) {
$comment->status = COMMENT_NOT_PUBLISHED;
comment_save($comment);
watchdog('action', 'Unpublished comment %subject.', array('%subject' => $comment->subject));
break;
}
+115 -19
View File
@@ -6,14 +6,22 @@
*/
class CommentHelperCase extends DrupalWebTestCase {
protected $super_user;
protected $admin_user;
protected $web_user;
protected $node;
function setUp() {
parent::setUp('comment', 'search');
$modules = func_get_args();
if (isset($modules[0]) && is_array($modules[0])) {
$modules = $modules[0];
}
$modules[] = 'comment';
parent::setUp($modules);
// Create users and test node.
$this->admin_user = $this->drupalCreateUser(array('administer content types', 'administer comments', 'administer blocks'));
$this->super_user = $this->drupalCreateUser(array('access administration pages', 'administer modules'));
$this->admin_user = $this->drupalCreateUser(array('administer content types', 'administer comments', 'administer blocks', 'administer actions', 'administer fields'));
$this->web_user = $this->drupalCreateUser(array('access comments', 'post comments', 'create article content', 'edit own comments'));
$this->node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1, 'uid' => $this->web_user->uid));
}
@@ -155,7 +163,7 @@ class CommentHelperCase extends DrupalWebTestCase {
$mode_text = 'required';
break;
}
$this->setCommentSettings('comment_preview', $mode, 'Comment preview ' . $mode_text . '.');
$this->setCommentSettings('comment_preview', $mode, format_string('Comment preview @mode_text.', array('@mode_text' => $mode_text)));
}
/**
@@ -175,7 +183,7 @@ class CommentHelperCase extends DrupalWebTestCase {
* Anonymous level.
*/
function setCommentAnonymous($level) {
$this->setCommentSettings('comment_anonymous', $level, 'Anonymous commenting set to level ' . $level . '.');
$this->setCommentSettings('comment_anonymous', $level, format_string('Anonymous commenting set to level @level.', array('@level' => $level)));
}
/**
@@ -185,7 +193,7 @@ class CommentHelperCase extends DrupalWebTestCase {
* Comments per page value.
*/
function setCommentsPerPage($number) {
$this->setCommentSettings('comment_default_per_page', $number, 'Number of comments per page set to ' . $number . '.');
$this->setCommentSettings('comment_default_per_page', $number, format_string('Number of comments per page set to @number.', array('@number' => $number)));
}
/**
@@ -201,7 +209,7 @@ class CommentHelperCase extends DrupalWebTestCase {
function setCommentSettings($name, $value, $message) {
variable_set($name . '_article', $value);
// Display status message.
$this->assertTrue(TRUE, $message);
$this->pass($message);
}
/**
@@ -273,7 +281,7 @@ class CommentInterfaceTest extends CommentHelperCase {
$this->setCommentPreview(DRUPAL_DISABLED);
$this->setCommentForm(TRUE);
$this->setCommentSubject(FALSE);
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, t('Comment paging changed.'));
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, 'Comment paging changed.');
$this->drupalLogout();
// Post comment #1 without subject or preview.
@@ -583,7 +591,7 @@ class CommentInterfaceTest extends CommentHelperCase {
$this->setCommentPreview(DRUPAL_DISABLED);
$this->setCommentForm(TRUE);
$this->setCommentSubject(FALSE);
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, t('Comment paging changed.'));
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, 'Comment paging changed.');
$this->drupalLogout();
// Creates a second user to post comments.
@@ -954,7 +962,7 @@ class CommentPreviewTest extends CommentHelperCase {
$this->setCommentPreview(DRUPAL_OPTIONAL);
$this->setCommentForm(TRUE);
$this->setCommentSubject(TRUE);
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, t('Comment paging changed.'));
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, 'Comment paging changed.');
$this->drupalLogout();
// Login as web user and add a signature and a user picture.
@@ -1000,7 +1008,7 @@ class CommentPreviewTest extends CommentHelperCase {
$this->setCommentPreview(DRUPAL_OPTIONAL);
$this->setCommentForm(TRUE);
$this->setCommentSubject(TRUE);
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, t('Comment paging changed.'));
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, 'Comment paging changed.');
$edit = array();
$edit['subject'] = $this->randomName(8);
@@ -1238,7 +1246,7 @@ class CommentPagerTest extends CommentHelperCase {
$comments[] = $this->postComment($node, $this->randomName(), $this->randomName(), TRUE);
$comments[] = $this->postComment($node, $this->randomName(), $this->randomName(), TRUE);
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_FLAT, t('Comment paging changed.'));
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_FLAT, 'Comment paging changed.');
// Set comments to one per page so that we are able to test paging without
// needing to insert large numbers of comments.
@@ -1279,7 +1287,7 @@ class CommentPagerTest extends CommentHelperCase {
// If we switch to threaded mode, the replies on the oldest comment
// should be bumped to the first page and comment 6 should be bumped
// to the second page.
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, t('Switched to threaded mode.'));
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, 'Switched to threaded mode.');
$this->drupalGet('node/' . $node->nid, array('query' => array('page' => 0)));
$this->assertTrue($this->commentExists($reply, TRUE), 'In threaded mode, reply appears on page 1.');
$this->assertFalse($this->commentExists($comments[1]), 'In threaded mode, comment 2 has been bumped off of page 1.');
@@ -1339,7 +1347,7 @@ class CommentPagerTest extends CommentHelperCase {
// - 2
// - 5
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_FLAT, t('Comment paging changed.'));
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_FLAT, 'Comment paging changed.');
$expected_order = array(
0,
@@ -1353,7 +1361,7 @@ class CommentPagerTest extends CommentHelperCase {
$this->drupalGet('node/' . $node->nid);
$this->assertCommentOrder($comments, $expected_order);
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, t('Switched to threaded mode.'));
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, 'Switched to threaded mode.');
$expected_order = array(
0,
@@ -1435,7 +1443,7 @@ class CommentPagerTest extends CommentHelperCase {
// - 2
// - 5
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_FLAT, t('Comment paging changed.'));
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_FLAT, 'Comment paging changed.');
$expected_pages = array(
1 => 5, // Page of comment 5
@@ -1453,7 +1461,7 @@ class CommentPagerTest extends CommentHelperCase {
$this->assertIdentical($expected_page, $returned_page, format_string('Flat mode, @new replies: expected page @expected, returned page @returned.', array('@new' => $new_replies, '@expected' => $expected_page, '@returned' => $returned_page)));
}
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, t('Switched to threaded mode.'));
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, 'Switched to threaded mode.');
$expected_pages = array(
1 => 5, // Page of comment 5
@@ -1490,7 +1498,7 @@ class CommentNodeAccessTest extends CommentHelperCase {
}
function setUp() {
DrupalWebTestCase::setUp('comment', 'search', 'node_access_test');
parent::setUp('search', 'node_access_test');
node_access_rebuild();
// Create users and test node.
@@ -1509,7 +1517,7 @@ class CommentNodeAccessTest extends CommentHelperCase {
$this->setCommentPreview(DRUPAL_DISABLED);
$this->setCommentForm(TRUE);
$this->setCommentSubject(TRUE);
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, t('Comment paging changed.'));
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, 'Comment paging changed.');
$this->drupalLogout();
// Post comment.
@@ -1973,6 +1981,41 @@ class CommentActionsTestCase extends CommentHelperCase {
$this->clearWatchdog();
}
/**
* Tests the unpublish comment by keyword action.
*/
public function testCommentUnpublishByKeyword() {
$this->drupalLogin($this->admin_user);
$callback = 'comment_unpublish_by_keyword_action';
$hash = drupal_hash_base64($callback);
$comment_text = $keywords = $this->randomName();
$edit = array(
'actions_label' => $callback,
'keywords' => $keywords,
);
$this->drupalPost("admin/config/system/actions/configure/$hash", $edit, t('Save'));
$action = db_query("SELECT aid, type, callback, parameters, label FROM {actions} WHERE callback = :callback", array(':callback' => $callback))->fetchObject();
$this->assertTrue($action, 'The action could be loaded.');
$comment = $this->postComment($this->node, $comment_text, $this->randomName());
// Load the full comment so that status is available.
$comment = comment_load($comment->id);
$this->assertTrue($comment->status == COMMENT_PUBLISHED, 'The comment status was set to published.');
comment_unpublish_by_keyword_action($comment, array('keywords' => array($keywords)));
// We need to make sure that the comment has been saved with status
// unpublished.
$this->assertEqual(comment_load($comment->cid)->status, COMMENT_NOT_PUBLISHED, 'Comment was unpublished.');
$this->assertWatchdogMessage('Unpublished comment %subject.', array('%subject' => $comment->subject), 'Found watchdog message.');
$this->clearWatchdog();
}
/**
* Verify that a watchdog message has been entered.
*
@@ -2126,7 +2169,7 @@ class CommentThreadingTestCase extends CommentHelperCase {
$this->setCommentPreview(DRUPAL_DISABLED);
$this->setCommentForm(TRUE);
$this->setCommentSubject(TRUE);
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, t('Comment paging changed.'));
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, 'Comment paging changed.');
$this->drupalLogout();
// Create a node.
@@ -2223,3 +2266,56 @@ class CommentNodeChangesTestCase extends CommentHelperCase {
$this->assertFalse(comment_load($comment->id), 'The comment could not be loaded after the node was deleted.');
}
}
/**
* Tests uninstalling the comment module.
*/
class CommentUninstallTestCase extends CommentHelperCase {
public static function getInfo() {
return array(
'name' => 'Comment module uninstallation',
'description' => 'Tests that the comments module can be properly uninstalled.',
'group' => 'Comment',
);
}
function testCommentUninstall() {
$this->drupalLogin($this->super_user);
// Disable comment module.
$edit['modules[Core][comment][enable]'] = FALSE;
$this->drupalPost('admin/modules', $edit, t('Save configuration'));
$this->assertText(t('The configuration options have been saved.'), 'Comment module was disabled.');
// Uninstall comment module.
$edit = array('uninstall[comment]' => 'comment');
$this->drupalPost('admin/modules/uninstall', $edit, t('Uninstall'));
$this->drupalPost(NULL, NULL, t('Uninstall'));
$this->assertText(t('The selected modules have been uninstalled.'), 'Comment module was uninstalled.');
// Run cron and clear field cache so that comment fields and instances
// marked for deletion are actually removed.
$this->cronRun();
field_cache_clear();
// Verify that comment fields have been removed.
$all_fields = array_keys(field_info_field_map());
$this->assertFalse(in_array('comment_body', $all_fields), 'Comment fields were removed by uninstall.');
// Verify that comment field instances have been removed (or at least marked
// for deletion).
// N.B. field_read_instances does an INNER JOIN on field_config so if the
// comment_body row has been removed successfully from there no instances
// will be returned, but that does not guarantee that no rows are left over
// in the field_config_instance table.
$count = db_select('field_config_instance', 'fci')
->condition('entity_type', 'comment')
->condition('field_name', 'comment_body')
->condition('deleted', 0)
->countQuery()
->execute()
->fetchField();
$this->assertTrue($count == 0, 'Comment field instances were removed by uninstall.');
}
}
+3 -4
View File
@@ -6,8 +6,7 @@ core = 7.x
files[] = contact.test
configure = admin/structure/contact
; Information added by drupal.org packaging script on 2013-04-03
version = "7.22"
; Information added by Drupal.org packaging script on 2020-09-16
version = "7.73"
project = "drupal"
datestamp = "1365027012"
datestamp = "1600272641"
+8 -1
View File
@@ -234,7 +234,14 @@ function contact_form_user_profile_form_alter(&$form, &$form_state) {
* Implements hook_user_presave().
*/
function contact_user_presave(&$edit, $account, $category) {
$edit['data']['contact'] = isset($edit['contact']) ? $edit['contact'] : variable_get('contact_default_status', 1);
if (isset($edit['contact'])) {
// Set new value.
$edit['data']['contact'] = $edit['contact'];
}
elseif (!isset($account->data['contact'])) {
// Use default if none has been set.
$edit['data']['contact'] = variable_get('contact_default_status', 1);
}
}
/**
+2 -2
View File
@@ -134,7 +134,7 @@ function contact_site_form_submit($form, &$form_state) {
global $user, $language;
$values = $form_state['values'];
$values['sender'] = $user;
$values['sender'] = clone $user;
$values['sender']->name = $values['name'];
$values['sender']->mail = $values['mail'];
$values['category'] = contact_load($values['cid']);
@@ -270,7 +270,7 @@ function contact_personal_form_submit($form, &$form_state) {
global $user, $language;
$values = $form_state['values'];
$values['sender'] = $user;
$values['sender'] = clone $user;
$values['sender']->name = $values['name'];
$values['sender']->mail = $values['mail'];
+55 -33
View File
@@ -36,7 +36,7 @@ class ContactSitewideTestCase extends DrupalWebTestCase {
$edit = array();
$edit['contact_default_status'] = TRUE;
$this->drupalPost('admin/config/people/accounts', $edit, t('Save configuration'));
$this->assertText(t('The configuration options have been saved.'), t('Setting successfully saved.'));
$this->assertText(t('The configuration options have been saved.'), 'Setting successfully saved.');
// Delete old categories to ensure that new categories are used.
$this->deleteCategories();
@@ -56,21 +56,21 @@ class ContactSitewideTestCase extends DrupalWebTestCase {
$invalid_recipients = array('invalid', 'invalid@', 'invalid@site.', '@site.', '@site.com');
foreach ($invalid_recipients as $invalid_recipient) {
$this->addCategory($this->randomName(16), $invalid_recipient, '', FALSE);
$this->assertRaw(t('%recipient is an invalid e-mail address.', array('%recipient' => $invalid_recipient)), t('Caught invalid recipient (' . $invalid_recipient . ').'));
$this->assertRaw(t('%recipient is an invalid e-mail address.', array('%recipient' => $invalid_recipient)), format_string('Caught invalid recipient (@invalid_recipient).', array('@invalid_recipient' => $invalid_recipient)));
}
// Test validation of empty category and recipients fields.
$this->addCategory($category = '', '', '', TRUE);
$this->assertText(t('Category field is required.'), t('Caught empty category field'));
$this->assertText(t('Recipients field is required.'), t('Caught empty recipients field.'));
$this->assertText(t('Category field is required.'), 'Caught empty category field');
$this->assertText(t('Recipients field is required.'), 'Caught empty recipients field.');
// Create first valid category.
$recipients = array('simpletest@example.com', 'simpletest2@example.com', 'simpletest3@example.com');
$this->addCategory($category = $this->randomName(16), implode(',', array($recipients[0])), '', TRUE);
$this->assertRaw(t('Category %category has been saved.', array('%category' => $category)), t('Category successfully saved.'));
$this->assertRaw(t('Category %category has been saved.', array('%category' => $category)), 'Category successfully saved.');
// Make sure the newly created category is included in the list of categories.
$this->assertNoUniqueText($category, t('New category included in categories list.'));
$this->assertNoUniqueText($category, 'New category included in categories list.');
// Test update contact form category.
$categories = $this->getCategories();
@@ -80,80 +80,80 @@ class ContactSitewideTestCase extends DrupalWebTestCase {
$this->assertEqual($category_array['recipients'], $recipients_str);
$this->assertEqual($category_array['reply'], $reply);
$this->assertFalse($category_array['selected']);
$this->assertRaw(t('Category %category has been saved.', array('%category' => $category)), t('Category successfully saved.'));
$this->assertRaw(t('Category %category has been saved.', array('%category' => $category)), 'Category successfully saved.');
// Ensure that the contact form is shown without a category selection input.
user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, array('access site-wide contact form'));
$this->drupalLogout();
$this->drupalGet('contact');
$this->assertText(t('Your e-mail address'), t('Contact form is shown when there is one category.'));
$this->assertNoText(t('Category'), t('When there is only one category, the category selection element is hidden.'));
$this->assertText(t('Your e-mail address'), 'Contact form is shown when there is one category.');
$this->assertNoText(t('Category'), 'When there is only one category, the category selection element is hidden.');
$this->drupalLogin($admin_user);
// Add more categories.
$this->addCategory($category = $this->randomName(16), implode(',', array($recipients[0], $recipients[1])), '', FALSE);
$this->assertRaw(t('Category %category has been saved.', array('%category' => $category)), t('Category successfully saved.'));
$this->assertRaw(t('Category %category has been saved.', array('%category' => $category)), 'Category successfully saved.');
$this->addCategory($category = $this->randomName(16), implode(',', array($recipients[0], $recipients[1], $recipients[2])), '', FALSE);
$this->assertRaw(t('Category %category has been saved.', array('%category' => $category)), t('Category successfully saved.'));
$this->assertRaw(t('Category %category has been saved.', array('%category' => $category)), 'Category successfully saved.');
// Try adding a category that already exists.
$this->addCategory($category, '', '', FALSE);
$this->assertNoRaw(t('Category %category has been saved.', array('%category' => $category)), t('Category not saved.'));
$this->assertRaw(t('A contact form with category %category already exists.', array('%category' => $category)), t('Duplicate category error found.'));
$this->assertNoRaw(t('Category %category has been saved.', array('%category' => $category)), 'Category not saved.');
$this->assertRaw(t('A contact form with category %category already exists.', array('%category' => $category)), 'Duplicate category error found.');
// Clear flood table in preparation for flood test and allow other checks to complete.
db_delete('flood')->execute();
$num_records_after = db_query("SELECT COUNT(*) FROM {flood}")->fetchField();
$this->assertIdentical($num_records_after, '0', t('Flood table emptied.'));
$this->assertIdentical($num_records_after, '0', 'Flood table emptied.');
$this->drupalLogout();
// Check to see that anonymous user cannot see contact page without permission.
user_role_revoke_permissions(DRUPAL_ANONYMOUS_RID, array('access site-wide contact form'));
$this->drupalGet('contact');
$this->assertResponse(403, t('Access denied to anonymous user without permission.'));
$this->assertResponse(403, 'Access denied to anonymous user without permission.');
// Give anonymous user permission and see that page is viewable.
user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, array('access site-wide contact form'));
$this->drupalGet('contact');
$this->assertResponse(200, t('Access granted to anonymous user with permission.'));
$this->assertResponse(200, 'Access granted to anonymous user with permission.');
// Submit contact form with invalid values.
$this->submitContact('', $recipients[0], $this->randomName(16), $categories[0], $this->randomName(64));
$this->assertText(t('Your name field is required.'), t('Name required.'));
$this->assertText(t('Your name field is required.'), 'Name required.');
$this->submitContact($this->randomName(16), '', $this->randomName(16), $categories[0], $this->randomName(64));
$this->assertText(t('Your e-mail address field is required.'), t('E-mail required.'));
$this->assertText(t('Your e-mail address field is required.'), 'E-mail required.');
$this->submitContact($this->randomName(16), $invalid_recipients[0], $this->randomName(16), $categories[0], $this->randomName(64));
$this->assertText(t('You must enter a valid e-mail address.'), t('Valid e-mail required.'));
$this->assertText(t('You must enter a valid e-mail address.'), 'Valid e-mail required.');
$this->submitContact($this->randomName(16), $recipients[0], '', $categories[0], $this->randomName(64));
$this->assertText(t('Subject field is required.'), t('Subject required.'));
$this->assertText(t('Subject field is required.'), 'Subject required.');
$this->submitContact($this->randomName(16), $recipients[0], $this->randomName(16), $categories[0], '');
$this->assertText(t('Message field is required.'), t('Message required.'));
$this->assertText(t('Message field is required.'), 'Message required.');
// Test contact form with no default category selected.
db_update('contact')
->fields(array('selected' => 0))
->execute();
$this->drupalGet('contact');
$this->assertRaw(t('- Please choose -'), t('Without selected categories the visitor is asked to chose a category.'));
$this->assertRaw(t('- Please choose -'), 'Without selected categories the visitor is asked to chose a category.');
// Submit contact form with invalid category id (cid 0).
$this->submitContact($this->randomName(16), $recipients[0], $this->randomName(16), 0, '');
$this->assertText(t('You must select a valid category.'), t('Valid category required.'));
$this->assertText(t('You must select a valid category.'), 'Valid category required.');
// Submit contact form with correct values and check flood interval.
for ($i = 0; $i < $flood_limit; $i++) {
$this->submitContact($this->randomName(16), $recipients[0], $this->randomName(16), $categories[0], $this->randomName(64));
$this->assertText(t('Your message has been sent.'), t('Message sent.'));
$this->assertText(t('Your message has been sent.'), 'Message sent.');
}
// Submit contact form one over limit.
$this->drupalGet('contact');
$this->assertResponse(403, t('Access denied to anonymous user after reaching message treshold.'));
$this->assertRaw(t('You cannot send more than %number messages in @interval. Try again later.', array('%number' => variable_get('contact_threshold_limit', 3), '@interval' => format_interval(600))), t('Message threshold reached.'));
$this->assertResponse(403, 'Access denied to anonymous user after reaching message treshold.');
$this->assertRaw(t('You cannot send more than %number messages in @interval. Try again later.', array('%number' => variable_get('contact_threshold_limit', 3), '@interval' => format_interval(600))), 'Message threshold reached.');
// Delete created categories.
$this->drupalLogin($admin_user);
@@ -182,8 +182,8 @@ class ContactSitewideTestCase extends DrupalWebTestCase {
// We are testing the auto-reply, so there should be one e-mail going to the sender.
$captured_emails = $this->drupalGetMails(array('id' => 'contact_page_autoreply', 'to' => $email, 'from' => 'foo@example.com'));
$this->assertEqual(count($captured_emails), 1, t('Auto-reply e-mail was sent to the sender for category "foo".'), t('Contact'));
$this->assertEqual($captured_emails[0]['body'], drupal_html_to_text($foo_autoreply), t('Auto-reply e-mail body is correct for category "foo".'), t('Contact'));
$this->assertEqual(count($captured_emails), 1, 'Auto-reply e-mail was sent to the sender for category "foo".', 'Contact');
$this->assertEqual($captured_emails[0]['body'], drupal_html_to_text($foo_autoreply), 'Auto-reply e-mail body is correct for category "foo".', 'Contact');
// Test the auto-reply for category 'bar'.
$email = $this->randomName(32) . '@example.com';
@@ -191,14 +191,14 @@ class ContactSitewideTestCase extends DrupalWebTestCase {
// Auto-reply for category 'bar' should result in one auto-reply e-mail to the sender.
$captured_emails = $this->drupalGetMails(array('id' => 'contact_page_autoreply', 'to' => $email, 'from' => 'bar@example.com'));
$this->assertEqual(count($captured_emails), 1, t('Auto-reply e-mail was sent to the sender for category "bar".'), t('Contact'));
$this->assertEqual($captured_emails[0]['body'], drupal_html_to_text($bar_autoreply), t('Auto-reply e-mail body is correct for category "bar".'), t('Contact'));
$this->assertEqual(count($captured_emails), 1, 'Auto-reply e-mail was sent to the sender for category "bar".', 'Contact');
$this->assertEqual($captured_emails[0]['body'], drupal_html_to_text($bar_autoreply), 'Auto-reply e-mail body is correct for category "bar".', 'Contact');
// Verify that no auto-reply is sent when the auto-reply field is left blank.
$email = $this->randomName(32) . '@example.com';
$this->submitContact($this->randomName(16), $email, $this->randomString(64), 4, $this->randomString(128));
$captured_emails = $this->drupalGetMails(array('id' => 'contact_page_autoreply', 'to' => $email, 'from' => 'no_autoreply@example.com'));
$this->assertEqual(count($captured_emails), 0, t('No auto-reply e-mail was sent to the sender for category "no-autoreply".'), t('Contact'));
$this->assertEqual(count($captured_emails), 0, 'No auto-reply e-mail was sent to the sender for category "no-autoreply".', 'Contact');
}
/**
@@ -279,7 +279,7 @@ class ContactSitewideTestCase extends DrupalWebTestCase {
foreach ($categories as $category) {
$category_name = db_query("SELECT category FROM {contact} WHERE cid = :cid", array(':cid' => $category))->fetchField();
$this->drupalPost('admin/structure/contact/delete/' . $category, array(), t('Delete'));
$this->assertRaw(t('Category %category has been deleted.', array('%category' => $category_name)), t('Category deleted successfully.'));
$this->assertRaw(t('Category %category has been deleted.', array('%category' => $category_name)), 'Category deleted successfully.');
}
}
@@ -346,6 +346,28 @@ class ContactPersonalTestCase extends DrupalWebTestCase {
$this->drupalGet('user/' . $this->contact_user->uid . '/contact');
$this->assertResponse(200);
// Test that users can disable their contact form.
$this->drupalLogin($this->contact_user);
$edit = array('contact' => FALSE);
$this->drupalPost('user/' . $this->contact_user->uid . '/edit', $edit, 'Save');
$this->drupalLogout();
$this->drupalGet('user/' . $this->contact_user->uid . '/contact');
$this->assertResponse(403);
// Test that user's contact status stays disabled when saving.
$contact_user_temp = user_load($this->contact_user->uid, TRUE);
user_save($contact_user_temp);
$this->drupalGet('user/' . $this->contact_user->uid . '/contact');
$this->assertResponse(403);
// Test that users can enable their contact form.
$this->drupalLogin($this->contact_user);
$edit = array('contact' => TRUE);
$this->drupalPost('user/' . $this->contact_user->uid . '/edit', $edit, 'Save');
$this->drupalLogout();
$this->drupalGet('user/' . $this->contact_user->uid . '/contact');
$this->assertResponse(200);
// Revoke the personal contact permission for the anonymous user.
user_role_revoke_permissions(DRUPAL_ANONYMOUS_RID, array('access user contact forms'));
$this->drupalGet('user/' . $this->contact_user->uid . '/contact');
@@ -355,7 +377,7 @@ class ContactPersonalTestCase extends DrupalWebTestCase {
$this->drupalLogin($this->admin_user);
$edit = array('contact_default_status' => FALSE);
$this->drupalPost('admin/config/people/accounts', $edit, t('Save configuration'));
$this->assertText(t('The configuration options have been saved.'), t('Setting successfully saved.'));
$this->assertText(t('The configuration options have been saved.'), 'Setting successfully saved.');
$this->drupalLogout();
// Re-create our contacted user with personal contact forms disabled by
+1 -1
View File
@@ -77,7 +77,7 @@ div.contextual-links-wrapper ul.contextual-links {
-webkit-border-top-left-radius: 4px; /* LTR */
border-radius: 4px 0 4px 4px; /* LTR */
}
.contextual-links-region:hover a.contextual-links-trigger,
a.contextual-links-trigger-active,
div.contextual-links-active a.contextual-links-trigger,
div.contextual-links-active ul.contextual-links {
display: block;
+3 -4
View File
@@ -5,8 +5,7 @@ version = VERSION
core = 7.x
files[] = contextual.test
; Information added by drupal.org packaging script on 2013-04-03
version = "7.22"
; Information added by Drupal.org packaging script on 2020-09-16
version = "7.73"
project = "drupal"
datestamp = "1365027012"
datestamp = "1600272641"
+4
View File
@@ -30,6 +30,10 @@ Drupal.behaviors.contextualLinks = {
);
// Hide the contextual links when user clicks a link or rolls out of the .contextual-links-region.
$region.bind('mouseleave click', Drupal.contextualLinks.mouseleave);
$region.hover(
function() { $trigger.addClass('contextual-links-trigger-active'); },
function() { $trigger.removeClass('contextual-links-trigger-active'); }
);
// Prepend the trigger.
$wrapper.prepend($trigger);
});
+1 -1
View File
@@ -32,7 +32,7 @@ function hook_dashboard_regions() {
* An array containing all dashboard regions, in the format provided by
* hook_dashboard_regions().
*/
function hook_dashboard_regions_alter($regions) {
function hook_dashboard_regions_alter(&$regions) {
// Remove the sidebar region defined by the core dashboard module.
unset($regions['dashboard_sidebar']);
}
+3 -4
View File
@@ -7,8 +7,7 @@ files[] = dashboard.test
dependencies[] = block
configure = admin/dashboard/customize
; Information added by drupal.org packaging script on 2013-04-03
version = "7.22"
; Information added by Drupal.org packaging script on 2020-09-16
version = "7.73"
project = "drupal"
datestamp = "1365027012"
datestamp = "1600272641"
+5 -5
View File
@@ -9,7 +9,7 @@
* Implements Drupal.behaviors for the Dashboard module.
*/
Drupal.behaviors.dashboard = {
attach: function (context, settings) {
attach: function (context, settings) {
$('#dashboard', context).once(function () {
$(this).prepend('<div class="customize clearfix"><ul class="action-links"><li><a href="#">' + Drupal.t('Customize dashboard') + '</a></li></ul><div class="canvas"></div></div>');
$('.customize .action-links a', this).click(Drupal.behaviors.dashboard.enterCustomizeMode);
@@ -32,13 +32,13 @@ Drupal.behaviors.dashboard = {
empty_text = Drupal.settings.dashboard.emptyRegionTextInactive;
}
// We need a placeholder.
if ($('.placeholder', this).length == 0) {
$(this).append('<div class="placeholder"></div>');
if ($('.dashboard-placeholder', this).length == 0) {
$(this).append('<div class="dashboard-placeholder"></div>');
}
$('.placeholder', this).html(empty_text);
$('.dashboard-placeholder', this).html(empty_text);
}
else {
$('.placeholder', this).remove();
$('.dashboard-placeholder', this).remove();
}
});
},
+19 -19
View File
@@ -49,15 +49,15 @@ class DashboardBlocksTestCase extends DrupalWebTestCase {
// Ensure admin access.
$this->drupalGet('admin/dashboard');
$this->assertResponse(200, t('Admin has access to the dashboard.'));
$this->assertRaw($custom_block['title'], t('Admin has access to a dashboard block.'));
$this->assertResponse(200, 'Admin has access to the dashboard.');
$this->assertRaw($custom_block['title'], 'Admin has access to a dashboard block.');
// Ensure non-admin access is denied.
$normal_user = $this->drupalCreateUser();
$this->drupalLogin($normal_user);
$this->drupalGet('admin/dashboard');
$this->assertResponse(403, t('Non-admin has no access to the dashboard.'));
$this->assertNoText($custom_block['title'], t('Non-admin has no access to a dashboard block.'));
$this->assertResponse(403, 'Non-admin has no access to the dashboard.');
$this->assertNoText($custom_block['title'], 'Non-admin has no access to a dashboard block.');
}
/**
@@ -70,7 +70,7 @@ class DashboardBlocksTestCase extends DrupalWebTestCase {
$this->drupalGet('admin/dashboard/configure');
foreach ($dashboard_regions as $region => $description) {
$elements = $this->xpath('//option[@value=:region]', array(':region' => $region));
$this->assertTrue(!empty($elements), t('%region is an available choice on the dashboard block configuration page.', array('%region' => $region)));
$this->assertTrue(!empty($elements), format_string('%region is an available choice on the dashboard block configuration page.', array('%region' => $region)));
}
// Ensure blocks cannot be placed in dashboard regions on the standard
@@ -78,7 +78,7 @@ class DashboardBlocksTestCase extends DrupalWebTestCase {
$this->drupalGet('admin/structure/block');
foreach ($dashboard_regions as $region => $description) {
$elements = $this->xpath('//option[@value=:region]', array(':region' => $region));
$this->assertTrue(empty($elements), t('%region is not an available choice on the block configuration page.', array('%region' => $region)));
$this->assertTrue(empty($elements), format_string('%region is not an available choice on the block configuration page.', array('%region' => $region)));
}
}
@@ -94,24 +94,24 @@ class DashboardBlocksTestCase extends DrupalWebTestCase {
$custom_block['regions[stark]'] = 'dashboard_main';
$this->drupalPost('admin/structure/block/add', $custom_block, t('Save block'));
$this->drupalGet('admin/dashboard');
$this->assertRaw($custom_block['title'], t('Block appears on the dashboard.'));
$this->assertRaw($custom_block['title'], 'Block appears on the dashboard.');
$edit = array();
$edit['modules[Core][dashboard][enable]'] = FALSE;
$this->drupalPost('admin/modules', $edit, t('Save configuration'));
$this->assertText(t('The configuration options have been saved.'), t('Modules status has been updated.'));
$this->assertNoRaw('assigned to the invalid region', t('Dashboard blocks gracefully disabled.'));
$this->assertText(t('The configuration options have been saved.'), 'Modules status has been updated.');
$this->assertNoRaw('assigned to the invalid region', 'Dashboard blocks gracefully disabled.');
module_list(TRUE);
$this->assertFalse(module_exists('dashboard'), t('Dashboard disabled.'));
$this->assertFalse(module_exists('dashboard'), 'Dashboard disabled.');
$edit['modules[Core][dashboard][enable]'] = 'dashboard';
$this->drupalPost('admin/modules', $edit, t('Save configuration'));
$this->assertText(t('The configuration options have been saved.'), t('Modules status has been updated.'));
$this->assertText(t('The configuration options have been saved.'), 'Modules status has been updated.');
module_list(TRUE);
$this->assertTrue(module_exists('dashboard'), t('Dashboard enabled.'));
$this->assertTrue(module_exists('dashboard'), 'Dashboard enabled.');
$this->drupalGet('admin/dashboard');
$this->assertRaw($custom_block['title'], t('Block still appears on the dashboard.'));
$this->assertRaw($custom_block['title'], 'Block still appears on the dashboard.');
}
/**
@@ -121,21 +121,21 @@ class DashboardBlocksTestCase extends DrupalWebTestCase {
// Test "Recent comments", which should be available (defined as
// "administrative") but not enabled.
$this->drupalGet('admin/dashboard');
$this->assertNoText(t('Recent comments'), t('"Recent comments" not on dashboard.'));
$this->assertNoText(t('Recent comments'), '"Recent comments" not on dashboard.');
$this->drupalGet('admin/dashboard/drawer');
$this->assertText(t('Recent comments'), t('Drawer of disabled blocks includes a block defined as "administrative".'));
$this->assertNoText(t('Syndicate'), t('Drawer of disabled blocks excludes a block not defined as "administrative".'));
$this->assertText(t('Recent comments'), 'Drawer of disabled blocks includes a block defined as "administrative".');
$this->assertNoText(t('Syndicate'), 'Drawer of disabled blocks excludes a block not defined as "administrative".');
$this->drupalGet('admin/dashboard/configure');
$elements = $this->xpath('//select[@id=:id]//option[@selected="selected"]', array(':id' => 'edit-blocks-comment-recent-region'));
$this->assertTrue($elements[0]['value'] == 'dashboard_inactive', t('A block defined as "administrative" defaults to dashboard_inactive.'));
$this->assertTrue($elements[0]['value'] == 'dashboard_inactive', 'A block defined as "administrative" defaults to dashboard_inactive.');
// Now enable the block on the dashboard.
$values = array();
$values['blocks[comment_recent][region]'] = 'dashboard_main';
$this->drupalPost('admin/dashboard/configure', $values, t('Save blocks'));
$this->drupalGet('admin/dashboard');
$this->assertText(t('Recent comments'), t('"Recent comments" was placed on dashboard.'));
$this->assertText(t('Recent comments'), '"Recent comments" was placed on dashboard.');
$this->drupalGet('admin/dashboard/drawer');
$this->assertNoText(t('Recent comments'), t('Drawer of disabled blocks excludes enabled blocks.'));
$this->assertNoText(t('Recent comments'), 'Drawer of disabled blocks excludes enabled blocks.');
}
}
+9 -2
View File
@@ -294,11 +294,18 @@ function theme_dblog_message($variables) {
else {
$output = t($event->message, unserialize($event->variables));
}
// If the output is expected to be a link, strip all the tags and
// special characters by using filter_xss() without any allowed tags.
// If not, use filter_xss_admin() to allow some tags.
if ($variables['link'] && isset($event->wid)) {
// Truncate message to 56 chars.
// Truncate message to 56 chars after stripping all the tags.
$output = truncate_utf8(filter_xss($output, array()), 56, TRUE, TRUE);
$output = l($output, 'admin/reports/event/' . $event->wid, array('html' => TRUE));
}
else {
// Prevent XSS in log detail pages.
$output = filter_xss_admin($output);
}
}
return $output;
}
@@ -413,6 +420,6 @@ function dblog_clear_log_form($form) {
*/
function dblog_clear_log_submit() {
$_SESSION['dblog_overview_filter'] = array();
db_delete('watchdog')->execute();
db_truncate('watchdog')->execute();
drupal_set_message(t('Database log cleared.'));
}
+3 -4
View File
@@ -5,8 +5,7 @@ version = VERSION
core = 7.x
files[] = dblog.test
; Information added by drupal.org packaging script on 2013-04-03
version = "7.22"
; Information added by Drupal.org packaging script on 2020-09-16
version = "7.73"
project = "drupal"
datestamp = "1365027012"
datestamp = "1600272641"
+9
View File
@@ -154,6 +154,15 @@ function dblog_update_7002() {
db_add_index('watchdog', 'severity', array('severity'));
}
/**
* Account for possible legacy systems where dblog was not installed.
*/
function dblog_update_7003() {
if (!db_table_exists('watchdog')) {
db_create_table('watchdog', drupal_get_schema_unprocessed('dblog', 'watchdog'));
}
}
/**
* @} End of "addtogroup updates-7.x-extra".
*/
+24 -14
View File
@@ -144,20 +144,30 @@ function _dblog_get_message_types() {
* Note: Some values may be truncated to meet database column size restrictions.
*/
function dblog_watchdog(array $log_entry) {
Database::getConnection('default', 'default')->insert('watchdog')
->fields(array(
'uid' => $log_entry['uid'],
'type' => substr($log_entry['type'], 0, 64),
'message' => $log_entry['message'],
'variables' => serialize($log_entry['variables']),
'severity' => $log_entry['severity'],
'link' => substr($log_entry['link'], 0, 255),
'location' => $log_entry['request_uri'],
'referer' => $log_entry['referer'],
'hostname' => substr($log_entry['ip'], 0, 128),
'timestamp' => $log_entry['timestamp'],
))
->execute();
if (!function_exists('drupal_substr')) {
require_once DRUPAL_ROOT . '/includes/unicode.inc';
}
try {
Database::getConnection('default', 'default')->insert('watchdog')
->fields(array(
'uid' => $log_entry['uid'],
'type' => drupal_substr($log_entry['type'], 0, 64),
'message' => $log_entry['message'],
'variables' => serialize($log_entry['variables']),
'severity' => $log_entry['severity'],
'link' => drupal_substr($log_entry['link'], 0, 255),
'location' => $log_entry['request_uri'],
'referer' => $log_entry['referer'],
'hostname' => drupal_substr($log_entry['ip'], 0, 128),
'timestamp' => $log_entry['timestamp'],
))
->execute();
}
catch (Exception $e) {
// Exception is ignored so that watchdog does not break pages during the
// installation process or is not able to create the watchdog table during
// installation.
}
}
/**
+88 -29
View File
@@ -11,7 +11,7 @@
class DBLogTestCase extends DrupalWebTestCase {
/**
* A user with some relevent administrative permissions.
* A user with some relevant administrative permissions.
*
* @var object
*/
@@ -79,10 +79,10 @@ class DBLogTestCase extends DrupalWebTestCase {
// Check row limit variable.
$current_limit = variable_get('dblog_row_limit', 1000);
$this->assertTrue($current_limit == $row_limit, t('[Cache] Row limit variable of @count equals row limit of @limit', array('@count' => $current_limit, '@limit' => $row_limit)));
$this->assertTrue($current_limit == $row_limit, format_string('[Cache] Row limit variable of @count equals row limit of @limit', array('@count' => $current_limit, '@limit' => $row_limit)));
// Verify dblog row limit equals specified row limit.
$current_limit = unserialize(db_query("SELECT value FROM {variable} WHERE name = :dblog_limit", array(':dblog_limit' => 'dblog_row_limit'))->fetchField());
$this->assertTrue($current_limit == $row_limit, t('[Variable table] Row limit variable of @count equals row limit of @limit', array('@count' => $current_limit, '@limit' => $row_limit)));
$this->assertTrue($current_limit == $row_limit, format_string('[Variable table] Row limit variable of @count equals row limit of @limit', array('@count' => $current_limit, '@limit' => $row_limit)));
}
/**
@@ -96,14 +96,14 @@ class DBLogTestCase extends DrupalWebTestCase {
$this->generateLogEntries($row_limit + 10);
// Verify that the database log row count exceeds the row limit.
$count = db_query('SELECT COUNT(wid) FROM {watchdog}')->fetchField();
$this->assertTrue($count > $row_limit, t('Dblog row count of @count exceeds row limit of @limit', array('@count' => $count, '@limit' => $row_limit)));
$this->assertTrue($count > $row_limit, format_string('Dblog row count of @count exceeds row limit of @limit', array('@count' => $count, '@limit' => $row_limit)));
// Run a cron job.
$this->cronRun();
// Verify that the database log row count equals the row limit plus one
// because cron adds a record after it runs.
$count = db_query('SELECT COUNT(wid) FROM {watchdog}')->fetchField();
$this->assertTrue($count == $row_limit + 1, t('Dblog row count of @count equals row limit of @limit plus one', array('@count' => $count, '@limit' => $row_limit)));
$this->assertTrue($count == $row_limit + 1, format_string('Dblog row count of @count equals row limit of @limit plus one', array('@count' => $count, '@limit' => $row_limit)));
}
/**
@@ -119,13 +119,18 @@ class DBLogTestCase extends DrupalWebTestCase {
private function generateLogEntries($count, $type = 'custom', $severity = WATCHDOG_NOTICE) {
global $base_root;
// This long URL makes it just a little bit harder to pass the link part of
// the test with a mix of English words and a repeating series of random
// percent-encoded Chinese characters.
$link = urldecode('/content/xo%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A%E9%85%B1%E5%87%89%E6%8B%8C%E7%B4%A0%E9%B8%A1%E7%85%A7%E7%83%A7%E9%B8%A1%E9%BB%84%E7%8E%AB%E7%91%B0-%E7%A7%91%E5%B7%9E%E7%9A%84%E5%B0%8F%E4%B9%9D%E5%AF%A8%E6%B2%9F%E7%BB%9D%E7%BE%8E%E9%AB%98%E5%B1%B1%E6%B9%96%E6%B3%8A-lake-isabelle');
// Prepare the fields to be logged
$log = array(
'type' => $type,
'message' => 'Log entry added to test the dblog row limit.',
'variables' => array(),
'severity' => $severity,
'link' => NULL,
'link' => $link,
'user' => $this->big_user,
'uid' => isset($this->big_user->uid) ? $this->big_user->uid : 0,
'request_uri' => $base_root . request_uri(),
@@ -153,35 +158,35 @@ class DBLogTestCase extends DrupalWebTestCase {
$this->drupalGet('admin/help/dblog');
$this->assertResponse($response);
if ($response == 200) {
$this->assertText(t('Database logging'), t('DBLog help was displayed'));
$this->assertText(t('Database logging'), 'DBLog help was displayed');
}
// View the database log report page.
$this->drupalGet('admin/reports/dblog');
$this->assertResponse($response);
if ($response == 200) {
$this->assertText(t('Recent log messages'), t('DBLog report was displayed'));
$this->assertText(t('Recent log messages'), 'DBLog report was displayed');
}
// View the database log page-not-found report page.
$this->drupalGet('admin/reports/page-not-found');
$this->assertResponse($response);
if ($response == 200) {
$this->assertText(t('Top ' . $quote . 'page not found' . $quote . ' errors'), t('DBLog page-not-found report was displayed'));
$this->assertText(t('Top ' . $quote . 'page not found' . $quote . ' errors'), 'DBLog page-not-found report was displayed');
}
// View the database log access-denied report page.
$this->drupalGet('admin/reports/access-denied');
$this->assertResponse($response);
if ($response == 200) {
$this->assertText(t('Top ' . $quote . 'access denied' . $quote . ' errors'), t('DBLog access-denied report was displayed'));
$this->assertText(t('Top ' . $quote . 'access denied' . $quote . ' errors'), 'DBLog access-denied report was displayed');
}
// View the database log event page.
$this->drupalGet('admin/reports/event/1');
$this->assertResponse($response);
if ($response == 200) {
$this->assertText(t('Details'), t('DBLog event node was displayed'));
$this->assertText(t('Details'), 'DBLog event node was displayed');
}
}
@@ -220,7 +225,7 @@ class DBLogTestCase extends DrupalWebTestCase {
$this->assertResponse(200);
// Retrieve the user object.
$user = user_load_by_name($name);
$this->assertTrue($user != NULL, t('User @name was loaded', array('@name' => $name)));
$this->assertTrue($user != NULL, format_string('User @name was loaded', array('@name' => $name)));
// pass_raw property is needed by drupalLogin.
$user->pass_raw = $pass;
// Login user.
@@ -233,7 +238,7 @@ class DBLogTestCase extends DrupalWebTestCase {
$ids[] = $row->wid;
}
$count_before = (isset($ids)) ? count($ids) : 0;
$this->assertTrue($count_before > 0, t('DBLog contains @count records for @name', array('@count' => $count_before, '@name' => $user->name)));
$this->assertTrue($count_before > 0, format_string('DBLog contains @count records for @name', array('@count' => $count_before, '@name' => $user->name)));
// Login the admin user.
$this->drupalLogin($this->big_user);
@@ -249,11 +254,11 @@ class DBLogTestCase extends DrupalWebTestCase {
// Add user.
// Default display includes name and email address; if too long, the email
// address is replaced by three periods.
$this->assertLogMessage(t('New user: %name (%email).', array('%name' => $name, '%email' => $user->mail)), t('DBLog event was recorded: [add user]'));
$this->assertLogMessage(t('New user: %name (%email).', array('%name' => $name, '%email' => $user->mail)), 'DBLog event was recorded: [add user]');
// Login user.
$this->assertLogMessage(t('Session opened for %name.', array('%name' => $name)), t('DBLog event was recorded: [login user]'));
$this->assertLogMessage(t('Session opened for %name.', array('%name' => $name)), 'DBLog event was recorded: [login user]');
// Logout user.
$this->assertLogMessage(t('Session closed for %name.', array('%name' => $name)), t('DBLog event was recorded: [logout user]'));
$this->assertLogMessage(t('Session closed for %name.', array('%name' => $name)), 'DBLog event was recorded: [logout user]');
// Delete user.
$message = t('Deleted user: %name %email.', array('%name' => $name, '%email' => '<' . $user->mail . '>'));
$message_text = truncate_utf8(filter_xss($message, array()), 56, TRUE, TRUE);
@@ -268,12 +273,12 @@ class DBLogTestCase extends DrupalWebTestCase {
$link = drupal_substr($value, strpos($value, 'admin/reports/event/'));
$this->drupalGet($link);
// Check for full message text on the details page.
$this->assertRaw($message, t('DBLog event details was found: [delete user]'));
$this->assertRaw($message, 'DBLog event details was found: [delete user]');
break;
}
}
}
$this->assertTrue($link, t('DBLog event was recorded: [delete user]'));
$this->assertTrue($link, 'DBLog event was recorded: [delete user]');
// Visit random URL (to generate page not found event).
$not_found_url = $this->randomName(60);
$this->drupalGet($not_found_url);
@@ -282,7 +287,7 @@ class DBLogTestCase extends DrupalWebTestCase {
$this->drupalGet('admin/reports/page-not-found');
$this->assertResponse(200);
// Check that full-length URL displayed.
$this->assertText($not_found_url, t('DBLog event was recorded: [page not found]'));
$this->assertText($not_found_url, 'DBLog event was recorded: [page not found]');
}
/**
@@ -307,7 +312,7 @@ class DBLogTestCase extends DrupalWebTestCase {
$this->assertResponse(200);
// Retrieve the node object.
$node = $this->drupalGetNodeByTitle($title);
$this->assertTrue($node != NULL, t('Node @title was loaded', array('@title' => $title)));
$this->assertTrue($node != NULL, format_string('Node @title was loaded', array('@title' => $title)));
// Edit the node.
$edit = $this->getContentUpdate($type);
$this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
@@ -330,23 +335,23 @@ class DBLogTestCase extends DrupalWebTestCase {
// Verify that node events were recorded.
// Was node content added?
$this->assertLogMessage(t('@type: added %title.', array('@type' => $type, '%title' => $title)), t('DBLog event was recorded: [content added]'));
$this->assertLogMessage(t('@type: added %title.', array('@type' => $type, '%title' => $title)), 'DBLog event was recorded: [content added]');
// Was node content updated?
$this->assertLogMessage(t('@type: updated %title.', array('@type' => $type, '%title' => $title)), t('DBLog event was recorded: [content updated]'));
$this->assertLogMessage(t('@type: updated %title.', array('@type' => $type, '%title' => $title)), 'DBLog event was recorded: [content updated]');
// Was node content deleted?
$this->assertLogMessage(t('@type: deleted %title.', array('@type' => $type, '%title' => $title)), t('DBLog event was recorded: [content deleted]'));
$this->assertLogMessage(t('@type: deleted %title.', array('@type' => $type, '%title' => $title)), 'DBLog event was recorded: [content deleted]');
// View the database log access-denied report page.
$this->drupalGet('admin/reports/access-denied');
$this->assertResponse(200);
// Verify that the 'access denied' event was recorded.
$this->assertText(t('admin/reports/dblog'), t('DBLog event was recorded: [access denied]'));
$this->assertText(t('admin/reports/dblog'), 'DBLog event was recorded: [access denied]');
// View the database log page-not-found report page.
$this->drupalGet('admin/reports/page-not-found');
$this->assertResponse(200);
// Verify that the 'page not found' event was recorded.
$this->assertText(t('node/@nid', array('@nid' => $node->nid)), t('DBLog event was recorded: [page not found]'));
$this->assertText(t('node/@nid', array('@nid' => $node->nid)), 'DBLog event was recorded: [page not found]');
}
/**
@@ -433,14 +438,14 @@ class DBLogTestCase extends DrupalWebTestCase {
// Add a watchdog entry.
dblog_watchdog($log);
// Make sure the table count has actually been incremented.
$this->assertEqual($count + 1, db_query('SELECT COUNT(*) FROM {watchdog}')->fetchField(), t('dblog_watchdog() added an entry to the dblog :count', array(':count' => $count)));
$this->assertEqual($count + 1, db_query('SELECT COUNT(*) FROM {watchdog}')->fetchField(), format_string('dblog_watchdog() added an entry to the dblog :count', array(':count' => $count)));
// Login the admin user.
$this->drupalLogin($this->big_user);
// Post in order to clear the database table.
$this->drupalPost('admin/reports/dblog', array(), t('Clear log messages'));
// Count the rows in watchdog that previously related to the deleted user.
$count = db_query('SELECT COUNT(*) FROM {watchdog}')->fetchField();
$this->assertEqual($count, 0, t('DBLog contains :count records after a clear.', array(':count' => $count)));
$this->assertEqual($count, 0, format_string('DBLog contains :count records after a clear.', array(':count' => $count)));
}
/**
@@ -512,7 +517,34 @@ class DBLogTestCase extends DrupalWebTestCase {
// Clear all logs and make sure the confirmation message is found.
$this->drupalPost('admin/reports/dblog', array(), t('Clear log messages'));
$this->assertText(t('Database log cleared.'), t('Confirmation message found'));
$this->assertText(t('Database log cleared.'), 'Confirmation message found');
}
/**
* Verifies that exceptions are caught in dblog_watchdog().
*/
protected function testDBLogException() {
$log = array(
'type' => 'custom',
'message' => 'Log entry added to test watchdog handling of Exceptions.',
'variables' => array(),
'severity' => WATCHDOG_NOTICE,
'link' => NULL,
'user' => $this->big_user,
'uid' => isset($this->big_user->uid) ? $this->big_user->uid : 0,
'request_uri' => request_uri(),
'referer' => $_SERVER['HTTP_REFERER'],
'ip' => ip_address(),
'timestamp' => REQUEST_TIME,
);
// Remove watchdog table temporarily to simulate it missing during
// installation.
db_query("DROP TABLE {watchdog}");
// Add a watchdog entry.
// This should not throw an Exception, but fail silently.
dblog_watchdog($log);
}
/**
@@ -633,5 +665,32 @@ class DBLogTestCase extends DrupalWebTestCase {
// Document Object Model (DOM).
$this->assertLink(html_entity_decode($message_text), 0, $message);
}
}
/**
* Make sure HTML tags are filtered out in the log detail page.
*/
public function testLogMessageSanitized() {
$this->drupalLogin($this->big_user);
// Make sure dangerous HTML tags are filtered out in log detail page.
$log = array(
'uid' => 0,
'type' => 'custom',
'message' => "<script>alert('foo');</script> <strong>Lorem ipsum</strong>",
'variables' => NULL,
'severity' => WATCHDOG_NOTICE,
'link' => 'foo/bar',
'request_uri' => 'http://example.com?dblog=1',
'referer' => 'http://example.org?dblog=2',
'ip' => '0.0.1.0',
'timestamp' => REQUEST_TIME,
);
dblog_watchdog($log);
$wid = db_query('SELECT MAX(wid) FROM {watchdog}')->fetchField();
$this->drupalGet('admin/reports/event/' . $wid);
$this->assertResponse(200);
$this->assertNoRaw("<script>alert('foo');</script>");
$this->assertRaw("alert('foo'); <strong>Lorem ipsum</strong>");
}
}
+90 -16
View File
@@ -1,4 +1,8 @@
<?php
/**
* @file
* Hooks provided by the Field module.
*/
/**
* @addtogroup hooks
@@ -23,14 +27,22 @@
* @see hook_field_extra_fields_alter()
*
* @return
* A nested array of 'pseudo-field' components. Each list is nested within
* the following keys: entity type, bundle name, context (either 'form' or
* A nested array of 'pseudo-field' elements. Each list is nested within the
* following keys: entity type, bundle name, context (either 'form' or
* 'display'). The keys are the name of the elements as appearing in the
* renderable array (either the entity form or the displayed entity). The
* value is an associative array:
* - label: The human readable name of the component.
* - description: A short description of the component contents.
* - label: The human readable name of the element.
* - description: A short description of the element contents.
* - weight: The default weight of the element.
* - edit: (optional) String containing markup (normally a link) used as the
* element's 'edit' operation in the administration interface. Only for
* 'form' context.
* - delete: (optional) String containing markup (normally a link) used as the
* element's 'delete' operation in the administration interface. Only for
* 'form' context.
*
* @ingroup field_types
*/
function hook_field_extra_fields() {
$extra['node']['poll'] = array(
@@ -70,6 +82,8 @@ function hook_field_extra_fields() {
* The associative array of 'pseudo-field' components.
*
* @see hook_field_extra_fields()
*
* @ingroup field_types
*/
function hook_field_extra_fields_alter(&$info) {
// Force node title to always be at the top of the list by default.
@@ -107,6 +121,9 @@ function hook_field_extra_fields_alter(&$info) {
/**
* Define Field API field types.
*
* Along with this hook, you also need to implement other hooks. See
* @link field_types Field Types API @endlink for more information.
*
* @return
* An array whose keys are field type names and whose values are arrays
* describing the field type, with the following key/value pairs:
@@ -193,8 +210,11 @@ function hook_field_info_alter(&$info) {
/**
* Define the Field API schema for a field structure.
*
* This hook MUST be defined in .install for it to be detected during
* installation and upgrade.
* This is invoked when a field is created, in order to obtain the database
* schema from the module that defines the field's type.
*
* This hook must be defined in the module's .install file for it to be detected
* during installation and upgrade.
*
* @param $field
* A field structure.
@@ -644,6 +664,8 @@ function hook_field_delete_revision($entity_type, $entity, $field, $instance, $l
* The source entity from which field values are being copied.
* @param $source_langcode
* The source language from which field values are being copied.
*
* @ingroup field_language
*/
function hook_field_prepare_translation($entity_type, $entity, $field, $instance, $langcode, &$items, $source_entity, $source_langcode) {
// If the translating user is not permitted to use the assigned text format,
@@ -1238,7 +1260,7 @@ function hook_field_formatter_view($entity_type, $entity, $field, $instance, $la
*/
/**
* @ingroup field_attach
* @addtogroup field_attach
* @{
*/
@@ -1300,9 +1322,33 @@ function hook_field_attach_load($entity_type, $entities, $age, $options) {
* This hook is invoked after the field module has performed the operation.
*
* See field_attach_validate() for details and arguments.
*
* @param $entity_type
* The type of $entity; e.g., 'node' or 'user'.
* @param $entity
* The entity with fields to validate.
* @param array $errors
* The array of errors (keyed by field name, language code, and delta) that
* have already been reported for the entity. The function should add its
* errors to this array. Each error is an associative array with the following
* keys and values:
* - error: An error code (should be a string prefixed with the module name).
* - message: The human readable message to be displayed.
*/
function hook_field_attach_validate($entity_type, $entity, &$errors) {
// @todo Needs function body.
// Make sure any images in article nodes have an alt text.
if ($entity_type == 'node' && $entity->type == 'article' && !empty($entity->field_image)) {
foreach ($entity->field_image as $langcode => $items) {
foreach ($items as $delta => $item) {
if (!empty($item['fid']) && empty($item['alt'])) {
$errors['field_image'][$langcode][$delta][] = array(
'error' => 'field_example_invalid',
'message' => t('All images in articles need to have an alternative text set.'),
);
}
}
}
}
}
/**
@@ -1504,6 +1550,8 @@ function hook_field_attach_prepare_translation_alter(&$entity, $context) {
* - entity_type: The type of the entity to be displayed.
* - entity: The entity with fields to render.
* - langcode: The language code $entity has to be displayed in.
*
* @ingroup field_language
*/
function hook_field_language_alter(&$display_language, $context) {
// Do not apply core language fallback rules if they are disabled or if Locale
@@ -1525,6 +1573,8 @@ function hook_field_language_alter(&$display_language, $context) {
* An associative array containing:
* - entity_type: The type of the entity the field is attached to.
* - field: A field data structure.
*
* @ingroup field_language
*/
function hook_field_available_languages_alter(&$languages, $context) {
// Add an unavailable language.
@@ -1575,7 +1625,7 @@ function hook_field_attach_rename_bundle($entity_type, $bundle_old, $bundle_new)
* @param $entity_type
* The type of entity; for example, 'node' or 'user'.
* @param $bundle
* The bundle that was just deleted.
* The name of the bundle that was just deleted.
* @param $instances
* An array of all instances that existed for the bundle before it was
* deleted.
@@ -1590,7 +1640,7 @@ function hook_field_attach_delete_bundle($entity_type, $bundle, $instances) {
}
/**
* @} End of "defgroup field_attach".
* @} End of "addtogroup field_attach".
*/
/**
@@ -1847,7 +1897,7 @@ function hook_field_storage_write($entity_type, $entity, $op, $fields) {
$items = (array) $entity->{$field_name}[$langcode];
$delta_count = 0;
foreach ($items as $delta => $item) {
// We now know we have someting to insert.
// We now know we have something to insert.
$do_insert = TRUE;
$record = array(
'entity_type' => $entity_type,
@@ -2250,6 +2300,10 @@ function hook_field_storage_pre_update($entity_type, $entity, &$skip_fields) {
}
}
/**
* @} End of "addtogroup field_storage
*/
/**
* Returns the maximum weight for the entity components handled by the module.
*
@@ -2263,9 +2317,12 @@ function hook_field_storage_pre_update($entity_type, $entity, &$skip_fields) {
* @param $context
* The context for which the maximum weight is requested. Either 'form', or
* the name of a view mode.
*
* @return
* The maximum weight of the entity's components, or NULL if no components
* were found.
*
* @ingroup field_info
*/
function hook_field_info_max_weight($entity_type, $bundle, $context) {
$weights = array();
@@ -2277,6 +2334,11 @@ function hook_field_info_max_weight($entity_type, $bundle, $context) {
return $weights ? max($weights) : NULL;
}
/**
* @addtogroup field_types
* @{
*/
/**
* Alters the display settings of a field before it gets displayed.
*
@@ -2343,6 +2405,10 @@ function hook_field_display_ENTITY_TYPE_alter(&$display, $context) {
}
}
/**
* @} End of "addtogroup field_types
*/
/**
* Alters the display settings of pseudo-fields before an entity is displayed.
*
@@ -2358,6 +2424,8 @@ function hook_field_display_ENTITY_TYPE_alter(&$display, $context) {
* - entity_type: The entity type; e.g., 'node' or 'user'.
* - bundle: The bundle name.
* - view_mode: The view mode, e.g. 'full', 'teaser'...
*
* @ingroup field_types
*/
function hook_field_extra_fields_display_alter(&$displays, $context) {
if ($context['entity_type'] == 'taxonomy_term' && $context['view_mode'] == 'full') {
@@ -2387,6 +2455,8 @@ function hook_field_extra_fields_display_alter(&$displays, $context) {
* - instance: The instance of the field.
*
* @see hook_field_widget_properties_alter()
*
* @ingroup field_widget
*/
function hook_field_widget_properties_ENTITY_TYPE_alter(&$widget, $context) {
// Change a widget's type according to the time of day.
@@ -2397,10 +2467,6 @@ function hook_field_widget_properties_ENTITY_TYPE_alter(&$widget, $context) {
}
}
/**
* @} End of "addtogroup field_storage".
*/
/**
* @addtogroup field_crud
* @{
@@ -2508,7 +2574,7 @@ function hook_field_delete_field($field) {
*
* @param $instance
* The instance as it is post-update.
* @param $prior_$instance
* @param $prior_instance
* The instance as it was pre-update.
*/
function hook_field_update_instance($instance, $prior_instance) {
@@ -2596,6 +2662,8 @@ function hook_field_purge_instance($instance) {
*
* @param $field
* The field being purged.
*
* @ingroup field_storage
*/
function hook_field_storage_purge_field($field) {
$table_name = _field_sql_storage_tablename($field);
@@ -2613,6 +2681,8 @@ function hook_field_storage_purge_field($field) {
*
* @param $instance
* The instance being purged.
*
* @ingroup field_storage
*/
function hook_field_storage_purge_field_instance($instance) {
db_delete('my_module_field_instance_info')
@@ -2634,6 +2704,8 @@ function hook_field_storage_purge_field_instance($instance) {
* The (possibly deleted) field whose data is being purged.
* @param $instance
* The deleted field instance whose data is being purged.
*
* @ingroup field_storage
*/
function hook_field_storage_purge($entity_type, $entity, $field, $instance) {
list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
@@ -2673,6 +2745,8 @@ function hook_field_storage_purge($entity_type, $entity, $field, $instance) {
*
* @return
* TRUE if the operation is allowed, and FALSE if the operation is denied.
*
* @ingroup field_types
*/
function hook_field_access($op, $field, $entity_type, $entity, $account) {
if ($field['field_name'] == 'field_of_interest' && $op == 'edit') {
+7 -1
View File
@@ -318,7 +318,7 @@ function _field_invoke_multiple($op, $entity_type, $entities, &$a = NULL, &$b =
// Unless a language suggestion is provided we iterate on all the
// available languages.
$available_languages = field_available_languages($entity_type, $field);
$language = !empty($options['language'][$id]) ? $options['language'][$id] : $options['language'];
$language = is_array($options['language']) && !empty($options['language'][$id]) ? $options['language'][$id] : $options['language'];
$languages = _field_language_suggestion($available_languages, $language, $field_name);
foreach ($languages as $langcode) {
$grouped_items[$field_id][$langcode][$id] = isset($entity->{$field_name}[$langcode]) ? $entity->{$field_name}[$langcode] : array();
@@ -976,6 +976,12 @@ function field_attach_insert($entity_type, $entity) {
/**
* Save field data for an existing entity.
*
* When calling this function outside an entity save operation be sure to
* clear caches for the entity:
* @code
* entity_get_controller($entity_type)->resetCache(array($entity_id))
* @endcode
*
* @param $entity_type
* The type of $entity; e.g. 'node' or 'user'.
* @param $entity
+26 -11
View File
@@ -60,11 +60,11 @@ function field_create_field($field) {
}
// Field type is required.
if (empty($field['type'])) {
throw new FieldException('Attempt to create a field with no type.');
throw new FieldException(format_string('Attempt to create field @field_name with no type.', array('@field_name' => $field['field_name'])));
}
// Field name cannot contain invalid characters.
if (!preg_match('/^[_a-z]+[_a-z0-9]*$/', $field['field_name'])) {
throw new FieldException('Attempt to create a field with invalid characters. Only lowercase alphanumeric characters and underscores are allowed, and only lowercase letters and underscore are allowed as the first character');
throw new FieldException(format_string('Attempt to create a field @field_name with invalid characters. Only lowercase alphanumeric characters and underscores are allowed, and only lowercase letters and underscore are allowed as the first character', array('@field_name' => $field['field_name'])));
}
// Field name cannot be longer than 32 characters. We use drupal_strlen()
@@ -189,7 +189,7 @@ function field_create_field($field) {
}
// Clear caches
field_cache_clear(TRUE);
field_cache_clear();
// Invoke external hooks after the cache is cleared for API consistency.
module_invoke_all('field_create_field', $field);
@@ -244,9 +244,11 @@ function field_update_field($field) {
// $prior_field may no longer be right.
module_load_install($field['module']);
$schema = (array) module_invoke($field['module'], 'field_schema', $field);
$schema += array('columns' => array(), 'indexes' => array());
$schema += array('columns' => array(), 'indexes' => array(), 'foreign keys' => array());
// 'columns' are hardcoded in the field type.
$field['columns'] = $schema['columns'];
// 'foreign keys' are hardcoded in the field type.
$field['foreign keys'] = $schema['foreign keys'];
// 'indexes' can be both hardcoded in the field type, and specified in the
// incoming $field definition.
$field += array(
@@ -286,7 +288,7 @@ function field_update_field($field) {
drupal_write_record('field_config', $field, $primary_key);
// Clear caches
field_cache_clear(TRUE);
field_cache_clear();
// Invoke external hooks after the cache is cleared for API consistency.
module_invoke_all('field_update_field', $field, $prior_field, $has_data);
@@ -428,7 +430,7 @@ function field_delete_field($field_name) {
->execute();
// Clear the cache.
field_cache_clear(TRUE);
field_cache_clear();
module_invoke_all('field_delete_field', $field);
}
@@ -522,17 +524,30 @@ function field_create_instance($instance) {
* Updates an instance of a field.
*
* @param $instance
* An associative array representing an instance structure. The required
* keys and values are:
* An associative array representing an instance structure. The following
* required array elements specify which field instance is being updated:
* - entity_type: The type of the entity the field is attached to.
* - bundle: The bundle this field belongs to.
* - field_name: The name of an existing field.
* Read-only_id properties are assigned automatically. Any other
* properties specified in $instance overwrite the existing values for
* the instance.
* The other array elements represent properties of the instance, and all
* properties must be specified or their default values will be used (except
* internal-use properties, which are assigned automatically). To avoid
* losing the previously stored properties of the instance when making a
* change, first load the instance with field_info_instance(), then override
* the values you want to override, and finally save using this function.
* Example:
* @code
* // Fetch an instance info array.
* $instance_info = field_info_instance($entity_type, $field_name, $bundle_name);
* // Change a single property in the instance definition.
* $instance_info['required'] = TRUE;
* // Write the changed definition back.
* field_update_instance($instance_info);
* @endcode
*
* @throws FieldException
*
* @see field_info_instance()
* @see field_create_instance()
*/
function field_update_instance($instance) {
+3 -4
View File
@@ -11,8 +11,7 @@ dependencies[] = field_sql_storage
required = TRUE
stylesheets[all][] = theme/field.css
; Information added by drupal.org packaging script on 2013-04-03
version = "7.22"
; Information added by Drupal.org packaging script on 2020-09-16
version = "7.73"
project = "drupal"
datestamp = "1365027012"
datestamp = "1600272641"
+24 -6
View File
@@ -146,7 +146,10 @@ class FieldInfo {
// Save in "static" and persistent caches.
$this->fieldMap = $map;
cache_set('field_info:field_map', $map, 'cache_field');
if (lock_acquire('field_info:field_map')) {
cache_set('field_info:field_map', $map, 'cache_field');
lock_release('field_info:field_map');
}
return $map;
}
@@ -174,7 +177,10 @@ class FieldInfo {
}
// Store in persistent cache.
cache_set('field_info:fields', $this->fieldsById, 'cache_field');
if (lock_acquire('field_info:fields')) {
cache_set('field_info:fields', $this->fieldsById, 'cache_field');
lock_release('field_info:fields');
}
}
// Fill the name/ID map.
@@ -231,7 +237,10 @@ class FieldInfo {
}
// Store in persistent cache.
cache_set('field_info:instances', $this->bundleInstances, 'cache_field');
if (lock_acquire('field_info:instances')) {
cache_set('field_info:instances', $this->bundleInstances, 'cache_field');
lock_release('field_info:instances');
}
}
$this->loadedAllInstances = TRUE;
@@ -419,7 +428,11 @@ class FieldInfo {
foreach ($instances as $instance) {
$cache['fields'][] = $this->fieldsById[$instance['field_id']];
}
cache_set("field_info:bundle:$entity_type:$bundle", $cache, 'cache_field');
if (lock_acquire("field_info:bundle:$entity_type:$bundle")) {
cache_set("field_info:bundle:$entity_type:$bundle", $cache, 'cache_field');
lock_release("field_info:bundle:$entity_type:$bundle");
}
return $instances;
}
@@ -460,7 +473,10 @@ class FieldInfo {
// Store in the 'static' and persistent caches.
$this->bundleExtraFields[$entity_type][$bundle] = $info;
cache_set("field_info:bundle_extra:$entity_type:$bundle", $info, 'cache_field');
if (lock_acquire("field_info:bundle_extra:$entity_type:$bundle")) {
cache_set("field_info:bundle_extra:$entity_type:$bundle", $info, 'cache_field');
lock_release("field_info:bundle_extra:$entity_type:$bundle");
}
return $this->bundleExtraFields[$entity_type][$bundle];
}
@@ -596,10 +612,12 @@ class FieldInfo {
// Fill in default values.
$display += array(
'label' => 'above',
'type' => $field_type_info['default_formatter'],
'settings' => array(),
'weight' => 0,
);
if (empty($display['type'])) {
$display['type'] = $field_type_info['default_formatter'];
}
if ($display['type'] != 'hidden') {
$formatter_type_info = field_info_formatter_types($display['type']);
// Fall back to default formatter if formatter type is not available.
+16 -1
View File
@@ -223,7 +223,11 @@ function _field_info_collate_types($reset = FALSE) {
}
drupal_alter('field_storage_info', $info['storage types']);
cache_set("field_info_types:$langcode", $info, 'cache_field');
// Set the cache if we can acquire a lock.
if (lock_acquire("field_info_types:$langcode")) {
cache_set("field_info_types:$langcode", $info, 'cache_field');
lock_release("field_info_types:$langcode");
}
}
}
@@ -449,6 +453,17 @@ function field_info_bundles($entity_type = NULL) {
* - type: The field type.
* - bundles: The bundles in which the field appears, as an array with entity
* types as keys and the array of bundle names as values.
* Example:
* @code
* array(
* 'body' => array(
* 'bundles' => array(
* 'node' => array('page', 'article'),
* ),
* 'type' => 'text_with_summary',
* ),
* );
* @endcode
*/
function field_info_field_map() {
$cache = _field_info_field_cache();
+23 -1
View File
@@ -128,7 +128,7 @@ function field_schema() {
'not null' => TRUE,
'default' => ''
),
'entity_type' => array(
'entity_type' => array(
'type' => 'varchar',
'length' => 32,
'not null' => TRUE,
@@ -162,6 +162,7 @@ function field_schema() {
),
);
$schema['cache_field'] = drupal_get_schema_unprocessed('system', 'cache');
$schema['cache_field']['description'] = 'Cache table for the Field module to store already built field information.';
return $schema;
}
@@ -466,6 +467,27 @@ function field_update_7003() {
// Empty update to force a rebuild of the registry.
}
/**
* Grant the new "administer fields" permission to trusted users.
*/
function field_update_7004() {
// Assign the permission to anyone that already has a trusted core permission
// that would have previously let them administer fields on an entity type.
$rids = array();
$permissions = array(
'administer site configuration',
'administer content types',
'administer users',
);
foreach ($permissions as $permission) {
$rids = array_merge($rids, array_keys(user_roles(FALSE, $permission)));
}
$rids = array_unique($rids);
foreach ($rids as $rid) {
_update_7000_user_role_grant_permissions($rid, array('administer fields'), 'field');
}
}
/**
* @} End of "addtogroup updates-7.x-extra".
*/
+31 -16
View File
@@ -316,6 +316,21 @@ function field_help($path, $arg) {
}
}
/**
* Implements hook_permission().
*/
function field_permission() {
return array(
'administer fields' => array(
'title' => t('Administer fields'),
'description' => t('Additional permissions are required based on what the fields are attached to (for example, <a href="@url">administer content types</a> to manage fields attached to content).', array(
'@url' => '#module-node',
)),
'restrict access' => TRUE,
),
);
}
/**
* Implements hook_theme().
*/
@@ -342,17 +357,6 @@ function field_cron() {
field_purge_batch($limit);
}
/**
* Implements hook_modules_uninstalled().
*/
function field_modules_uninstalled($modules) {
module_load_include('inc', 'field', 'field.crud');
foreach ($modules as $module) {
// TODO D7: field_module_delete is not yet implemented
// field_module_delete($module);
}
}
/**
* Implements hook_system_info_alter().
*
@@ -819,9 +823,9 @@ function field_view_value($entity_type, $entity, $field_name, $item, $display =
*
* This function can be used by third-party modules that need to output an
* isolated field.
* - Do not use inside node (or other entities) templates, use
* - Do not use inside node (or any other entity) templates; use
* render($content[FIELD_NAME]) instead.
* - Do not use to display all fields in an entity, use
* - Do not use to display all fields in an entity; use
* field_attach_prepare_view() and field_attach_view() instead.
* - The field_view_value() function can be used to output a single formatted
* field value, without label or wrapping field markup.
@@ -905,6 +909,7 @@ function field_view_field($entity_type, $entity, $field_name, $display = array()
'entity' => $entity,
'view_mode' => '_custom',
'display' => $display,
'language' => $langcode,
);
drupal_alter('field_attach_view', $result, $context);
@@ -947,20 +952,30 @@ function field_get_items($entity_type, $entity, $field_name, $langcode = NULL) {
*/
function field_has_data($field) {
$query = new EntityFieldQuery();
return (bool) $query
->fieldCondition($field)
$query = $query->fieldCondition($field)
->range(0, 1)
->count()
// Neutralize the 'entity_field_access' query tag added by
// field_sql_storage_field_storage_query(). The result cannot depend on the
// access grants of the current user.
->addTag('DANGEROUS_ACCESS_CHECK_OPT_OUT')
->addTag('DANGEROUS_ACCESS_CHECK_OPT_OUT');
return (bool) $query
->execute() || (bool) $query
->age(FIELD_LOAD_REVISION)
->execute();
}
/**
* Determine whether the user has access to a given field.
*
* This function does not determine whether access is granted to the entity
* itself, only the specific field. Callers are responsible for ensuring that
* entity access is also respected. For example, when checking field access for
* nodes, check node_access() before checking field_access(), and when checking
* field access for entities using the Entity API contributed module,
* check entity_access() before checking field_access().
*
* @param $op
* The operation to be performed. Possible values:
* - 'edit'
@@ -7,8 +7,7 @@ dependencies[] = field
files[] = field_sql_storage.test
required = TRUE
; Information added by drupal.org packaging script on 2013-04-03
version = "7.22"
; Information added by Drupal.org packaging script on 2020-09-16
version = "7.73"
project = "drupal"
datestamp = "1365027012"
datestamp = "1600272641"
@@ -64,6 +64,49 @@ function _field_sql_storage_revision_tablename($field) {
}
}
/**
* Generates a table alias for a field data table.
*
* The table alias is unique for each unique combination of field name
* (represented by $tablename), delta_group and language_group.
*
* @param $tablename
* The name of the data table for this field.
* @param $field_key
* The numeric key of this field in this query.
* @param $query
* The EntityFieldQuery that is executed.
*
* @return
* A string containing the generated table alias.
*/
function _field_sql_storage_tablealias($tablename, $field_key, EntityFieldQuery $query) {
// No conditions present: use a unique alias.
if (empty($query->fieldConditions[$field_key])) {
return $tablename . $field_key;
}
// Find the delta and language condition values and append them to the alias.
$condition = $query->fieldConditions[$field_key];
$alias = $tablename;
$has_group_conditions = FALSE;
foreach (array('delta', 'language') as $column) {
if (isset($condition[$column . '_group'])) {
$alias .= '_' . $column . '_' . $condition[$column . '_group'];
$has_group_conditions = TRUE;
}
}
// Return the alias when it has delta/language group conditions.
if ($has_group_conditions) {
return $alias;
}
// Return a unique alias in other cases.
return $tablename . $field_key;
}
/**
* Generate a column name for a field data table.
*
@@ -180,7 +223,17 @@ function _field_sql_storage_schema($field) {
foreach ($field['indexes'] as $index_name => $columns) {
$real_name = _field_sql_storage_indexname($field['field_name'], $index_name);
foreach ($columns as $column_name) {
$current['indexes'][$real_name][] = _field_sql_storage_columnname($field['field_name'], $column_name);
// Indexes can be specified as either a column name or an array with
// column name and length. Allow for either case.
if (is_array($column_name)) {
$current['indexes'][$real_name][] = array(
_field_sql_storage_columnname($field['field_name'], $column_name[0]),
$column_name[1],
);
}
else {
$current['indexes'][$real_name][] = _field_sql_storage_columnname($field['field_name'], $column_name);
}
}
}
@@ -188,7 +241,7 @@ function _field_sql_storage_schema($field) {
foreach ($field['foreign keys'] as $specifier => $specification) {
$real_name = _field_sql_storage_indexname($field['field_name'], $specifier);
$current['foreign keys'][$real_name]['table'] = $specification['table'];
foreach ($specification['columns'] as $column => $referenced) {
foreach ($specification['columns'] as $column_name => $referenced) {
$sql_storage_column = _field_sql_storage_columnname($field['field_name'], $column_name);
$current['foreign keys'][$real_name]['columns'][$sql_storage_column] = $referenced;
}
@@ -289,7 +342,17 @@ function field_sql_storage_field_storage_update_field($field, $prior_field, $has
$real_name = _field_sql_storage_indexname($field['field_name'], $name);
$real_columns = array();
foreach ($columns as $column_name) {
$real_columns[] = _field_sql_storage_columnname($field['field_name'], $column_name);
// Indexes can be specified as either a column name or an array with
// column name and length. Allow for either case.
if (is_array($column_name)) {
$real_columns[] = array(
_field_sql_storage_columnname($field['field_name'], $column_name[0]),
$column_name[1],
);
}
else {
$real_columns[] = _field_sql_storage_columnname($field['field_name'], $column_name);
}
}
db_add_index($table, $real_name, $real_columns);
db_add_index($revision_table, $real_name, $real_columns);
@@ -422,7 +485,7 @@ function field_sql_storage_field_storage_write($entity_type, $entity, $op, $fiel
$items = (array) $entity->{$field_name}[$langcode];
$delta_count = 0;
foreach ($items as $delta => $item) {
// We now know we have someting to insert.
// We now know we have something to insert.
$do_insert = TRUE;
$record = array(
'entity_type' => $entity_type,
@@ -504,17 +567,21 @@ function field_sql_storage_field_storage_query(EntityFieldQuery $query) {
$id_key = 'revision_id';
}
$table_aliases = array();
$query_tables = NULL;
// Add tables for the fields used.
foreach ($query->fields as $key => $field) {
$tablename = $tablename_function($field);
// Every field needs a new table.
$table_alias = $tablename . $key;
$table_alias = _field_sql_storage_tablealias($tablename, $key, $query);
$table_aliases[$key] = $table_alias;
if ($key) {
$select_query->join($tablename, $table_alias, "$table_alias.entity_type = $field_base_table.entity_type AND $table_alias.$id_key = $field_base_table.$id_key");
if (!isset($query_tables[$table_alias])) {
$select_query->join($tablename, $table_alias, "$table_alias.entity_type = $field_base_table.entity_type AND $table_alias.$id_key = $field_base_table.$id_key");
}
}
else {
$select_query = db_select($tablename, $table_alias);
// Store a reference to the list of joined tables.
$query_tables =& $select_query->getTables();
// Allow queries internal to the Field API to opt out of the access
// check, for situations where the query's results should not depend on
// the access grants for the current user.
@@ -126,7 +126,7 @@ class FieldSqlStorageTestCase extends DrupalWebTestCase {
$rows = db_select($this->table, 't')->fields('t')->execute()->fetchAllAssoc('delta', PDO::FETCH_ASSOC);
foreach ($values as $delta => $value) {
if ($delta < $this->field['cardinality']) {
$this->assertEqual($rows[$delta][$this->field_name . '_value'], $value['value'], t("Value $delta is inserted correctly"));
$this->assertEqual($rows[$delta][$this->field_name . '_value'], $value['value'], format_string("Value %delta is inserted correctly", array('%delta' => $delta)));
}
else {
$this->assertFalse(array_key_exists($delta, $rows), "No extraneous value gets inserted.");
@@ -145,7 +145,7 @@ class FieldSqlStorageTestCase extends DrupalWebTestCase {
$rows = db_select($this->table, 't')->fields('t')->execute()->fetchAllAssoc('delta', PDO::FETCH_ASSOC);
foreach ($values as $delta => $value) {
if ($delta < $this->field['cardinality']) {
$this->assertEqual($rows[$delta][$this->field_name . '_value'], $value['value'], t("Value $delta is updated correctly"));
$this->assertEqual($rows[$delta][$this->field_name . '_value'], $value['value'], format_string("Value %delta is updated correctly", array('%delta' => $delta)));
}
else {
$this->assertFalse(array_key_exists($delta, $rows), "No extraneous value gets updated.");
@@ -175,7 +175,7 @@ class FieldSqlStorageTestCase extends DrupalWebTestCase {
$rows = db_select($this->table, 't')->fields('t')->execute()->fetchAllAssoc('delta', PDO::FETCH_ASSOC);
foreach ($values as $delta => $value) {
if ($delta < $this->field['cardinality']) {
$this->assertEqual($rows[$delta][$this->field_name . '_value'], $value['value'], t("Update with no field_name entry leaves value $delta untouched"));
$this->assertEqual($rows[$delta][$this->field_name . '_value'], $value['value'], format_string("Update with no field_name entry leaves value %delta untouched", array('%delta' => $delta)));
}
}
@@ -183,7 +183,7 @@ class FieldSqlStorageTestCase extends DrupalWebTestCase {
$entity->{$this->field_name} = NULL;
field_attach_update($entity_type, $entity);
$rows = db_select($this->table, 't')->fields('t')->execute()->fetchAllAssoc('delta', PDO::FETCH_ASSOC);
$this->assertEqual(count($rows), 0, t("Update with an empty field_name entry empties the field."));
$this->assertEqual(count($rows), 0, "Update with an empty field_name entry empties the field.");
}
/**
@@ -326,7 +326,7 @@ class FieldSqlStorageTestCase extends DrupalWebTestCase {
// Ensure that the field tables are still there.
foreach (_field_sql_storage_schema($prior_field) as $table_name => $table_info) {
$this->assertTrue(db_table_exists($table_name), t('Table %table exists.', array('%table' => $table_name)));
$this->assertTrue(db_table_exists($table_name), format_string('Table %table exists.', array('%table' => $table_name)));
}
}
@@ -345,8 +345,8 @@ class FieldSqlStorageTestCase extends DrupalWebTestCase {
// Verify the indexes we will create do not exist yet.
foreach ($tables as $table) {
$this->assertFalse(Database::getConnection()->schema()->indexExists($table, 'value'), t("No index named value exists in $table"));
$this->assertFalse(Database::getConnection()->schema()->indexExists($table, 'value_format'), t("No index named value_format exists in $table"));
$this->assertFalse(Database::getConnection()->schema()->indexExists($table, 'value'), format_string("No index named value exists in %table", array('%table' => $table)));
$this->assertFalse(Database::getConnection()->schema()->indexExists($table, 'value_format'), format_string("No index named value_format exists in %table", array('%table' => $table)));
}
// Add data so the table cannot be dropped.
@@ -355,24 +355,24 @@ class FieldSqlStorageTestCase extends DrupalWebTestCase {
field_attach_insert('test_entity', $entity);
// Add an index
$field = array('field_name' => $field_name, 'indexes' => array('value' => array('value')));
$field = array('field_name' => $field_name, 'indexes' => array('value' => array(array('value', 255))));
field_update_field($field);
foreach ($tables as $table) {
$this->assertTrue(Database::getConnection()->schema()->indexExists($table, "{$field_name}_value"), t("Index on value created in $table"));
$this->assertTrue(Database::getConnection()->schema()->indexExists($table, "{$field_name}_value"), format_string("Index on value created in %table", array('%table' => $table)));
}
// Add a different index, removing the existing custom one.
$field = array('field_name' => $field_name, 'indexes' => array('value_format' => array('value', 'format')));
$field = array('field_name' => $field_name, 'indexes' => array('value_format' => array(array('value', 127), array('format', 127))));
field_update_field($field);
foreach ($tables as $table) {
$this->assertTrue(Database::getConnection()->schema()->indexExists($table, "{$field_name}_value_format"), t("Index on value_format created in $table"));
$this->assertFalse(Database::getConnection()->schema()->indexExists($table, "{$field_name}_value"), t("Index on value removed in $table"));
$this->assertTrue(Database::getConnection()->schema()->indexExists($table, "{$field_name}_value_format"), format_string("Index on value_format created in %table", array('%table' => $table)));
$this->assertFalse(Database::getConnection()->schema()->indexExists($table, "{$field_name}_value"), format_string("Index on value removed in %table", array('%table' => $table)));
}
// Verify that the tables were not dropped.
$entity = field_test_create_stub_entity(0, 0, $instance['bundle']);
field_attach_load('test_entity', array(0 => $entity));
$this->assertEqual($entity->{$field_name}[LANGUAGE_NONE][0]['value'], 'field data', t("Index changes performed without dropping the tables"));
$this->assertEqual($entity->{$field_name}[LANGUAGE_NONE][0]['value'], 'field data', "Index changes performed without dropping the tables");
}
/**
@@ -387,19 +387,19 @@ class FieldSqlStorageTestCase extends DrupalWebTestCase {
$instance = field_info_instance($this->instance['entity_type'], $this->instance['field_name'], $this->instance['bundle']);
// The storage details are indexed by a storage engine type.
$this->assertTrue(array_key_exists('sql', $field['storage']['details']), t('The storage type is SQL.'));
$this->assertTrue(array_key_exists('sql', $field['storage']['details']), 'The storage type is SQL.');
// The SQL details are indexed by table name.
$details = $field['storage']['details']['sql'];
$this->assertTrue(array_key_exists($current, $details[FIELD_LOAD_CURRENT]), t('Table name is available in the instance array.'));
$this->assertTrue(array_key_exists($revision, $details[FIELD_LOAD_REVISION]), t('Revision table name is available in the instance array.'));
$this->assertTrue(array_key_exists($current, $details[FIELD_LOAD_CURRENT]), 'Table name is available in the instance array.');
$this->assertTrue(array_key_exists($revision, $details[FIELD_LOAD_REVISION]), 'Revision table name is available in the instance array.');
// Test current and revision storage details together because the columns
// are the same.
foreach ((array) $this->field['columns'] as $column_name => $attributes) {
$storage_column_name = _field_sql_storage_columnname($this->field['field_name'], $column_name);
$this->assertEqual($details[FIELD_LOAD_CURRENT][$current][$column_name], $storage_column_name, t('Column name %value matches the definition in %bin.', array('%value' => $column_name, '%bin' => $current)));
$this->assertEqual($details[FIELD_LOAD_REVISION][$revision][$column_name], $storage_column_name, t('Column name %value matches the definition in %bin.', array('%value' => $column_name, '%bin' => $revision)));
$this->assertEqual($details[FIELD_LOAD_CURRENT][$current][$column_name], $storage_column_name, format_string('Column name %value matches the definition in %bin.', array('%value' => $column_name, '%bin' => $current)));
$this->assertEqual($details[FIELD_LOAD_REVISION][$revision][$column_name], $storage_column_name, format_string('Column name %value matches the definition in %bin.', array('%value' => $column_name, '%bin' => $revision)));
}
}
@@ -407,21 +407,180 @@ class FieldSqlStorageTestCase extends DrupalWebTestCase {
* Test foreign key support.
*/
function testFieldSqlStorageForeignKeys() {
// Create a decimal field.
// Create a 'shape' field, with a configurable foreign key (see
// field_test_field_schema()).
$field_name = 'testfield';
$field = array('field_name' => $field_name, 'type' => 'text');
$field = field_create_field($field);
// Retrieve the field and instance with field_info and verify the foreign
// keys are in place.
$foreign_key_name = 'shape';
$field = array('field_name' => $field_name, 'type' => 'shape', 'settings' => array('foreign_key_name' => $foreign_key_name));
field_create_field($field);
// Retrieve the field definition and check that the foreign key is in place.
$field = field_info_field($field_name);
$this->assertEqual($field['foreign keys']['format']['table'], 'filter_format', t('Foreign key table name preserved through CRUD'));
$this->assertEqual($field['foreign keys']['format']['columns']['format'], 'format', t('Foreign key column name preserved through CRUD'));
$this->assertEqual($field['foreign keys'][$foreign_key_name]['table'], $foreign_key_name, 'Foreign key table name preserved through CRUD');
$this->assertEqual($field['foreign keys'][$foreign_key_name]['columns'][$foreign_key_name], 'id', 'Foreign key column name preserved through CRUD');
// Update the field settings, it should update the foreign key definition
// too.
$foreign_key_name = 'color';
$field['settings']['foreign_key_name'] = $foreign_key_name;
field_update_field($field);
// Retrieve the field definition and check that the foreign key is in place.
$field = field_info_field($field_name);
$this->assertEqual($field['foreign keys'][$foreign_key_name]['table'], $foreign_key_name, 'Foreign key table name modified after update');
$this->assertEqual($field['foreign keys'][$foreign_key_name]['columns'][$foreign_key_name], 'id', 'Foreign key column name modified after update');
// Now grab the SQL schema and verify that too.
$schema = drupal_get_schema(_field_sql_storage_tablename($field));
$this->assertEqual(count($schema['foreign keys']), 1, t("There is 1 foreign key in the schema"));
$schema = drupal_get_schema(_field_sql_storage_tablename($field), TRUE);
$this->assertEqual(count($schema['foreign keys']), 1, 'There is 1 foreign key in the schema');
$foreign_key = reset($schema['foreign keys']);
$filter_column = _field_sql_storage_columnname($field['field_name'], 'format');
$this->assertEqual($foreign_key['table'], 'filter_format', t('Foreign key table name preserved in the schema'));
$this->assertEqual($foreign_key['columns'][$filter_column], 'format', t('Foreign key column name preserved in the schema'));
$foreign_key_column = _field_sql_storage_columnname($field['field_name'], $foreign_key_name);
$this->assertEqual($foreign_key['table'], $foreign_key_name, 'Foreign key table name preserved in the schema');
$this->assertEqual($foreign_key['columns'][$foreign_key_column], 'id', 'Foreign key column name preserved in the schema');
}
/**
* Test handling multiple conditions on one column of a field.
*
* Tests both the result and the complexity of the query.
*/
function testFieldSqlStorageMultipleConditionsSameColumn() {
$entity = field_test_create_stub_entity(NULL, NULL);
$entity->{$this->field_name}[LANGUAGE_NONE][0] = array('value' => 1);
field_test_entity_save($entity);
$entity = field_test_create_stub_entity(NULL, NULL);
$entity->{$this->field_name}[LANGUAGE_NONE][0] = array('value' => 2);
field_test_entity_save($entity);
$entity = field_test_create_stub_entity(NULL, NULL);
$entity->{$this->field_name}[LANGUAGE_NONE][0] = array('value' => 3);
field_test_entity_save($entity);
$query = new EntityFieldQuery();
// This tag causes field_test_query_store_global_test_query_alter() to be
// invoked so that the query can be tested.
$query->addTag('store_global_test_query');
$query->entityCondition('entity_type', 'test_entity');
$query->entityCondition('bundle', 'test_bundle');
$query->fieldCondition($this->field_name, 'value', 1, '<>', 0, LANGUAGE_NONE);
$query->fieldCondition($this->field_name, 'value', 2, '<>', 0, LANGUAGE_NONE);
$result = field_sql_storage_field_storage_query($query);
// Test the results.
$this->assertEqual(1, count($result), format_string('One result should be returned, got @count', array('@count' => count($result))));
// Test the complexity of the query.
$query = $GLOBALS['test_query'];
$this->assertNotNull($query, 'Precondition: the query should be available');
$tables = $query->getTables();
$this->assertEqual(1, count($tables), 'The query contains just one table.');
// Clean up.
unset($GLOBALS['test_query']);
}
/**
* Test handling multiple conditions on multiple columns of one field.
*
* Tests both the result and the complexity of the query.
*/
function testFieldSqlStorageMultipleConditionsDifferentColumns() {
// Create the multi-column shape field
$field_name = strtolower($this->randomName());
$field = array('field_name' => $field_name, 'type' => 'shape', 'cardinality' => 4);
$field = field_create_field($field);
$instance = array(
'field_name' => $field_name,
'entity_type' => 'test_entity',
'bundle' => 'test_bundle'
);
$instance = field_create_instance($instance);
$entity = field_test_create_stub_entity(NULL, NULL);
$entity->{$field_name}[LANGUAGE_NONE][0] = array('shape' => 'A', 'color' => 'X');
field_test_entity_save($entity);
$entity = field_test_create_stub_entity(NULL, NULL);
$entity->{$field_name}[LANGUAGE_NONE][0] = array('shape' => 'B', 'color' => 'X');
field_test_entity_save($entity);
$entity = field_test_create_stub_entity(NULL, NULL);
$entity->{$field_name}[LANGUAGE_NONE][0] = array('shape' => 'A', 'color' => 'Y');
field_test_entity_save($entity);
$query = new EntityFieldQuery();
// This tag causes field_test_query_store_global_test_query_alter() to be
// invoked so that the query can be tested.
$query->addTag('store_global_test_query');
$query->entityCondition('entity_type', 'test_entity');
$query->entityCondition('bundle', 'test_bundle');
$query->fieldCondition($field_name, 'shape', 'B', '=', 'something', LANGUAGE_NONE);
$query->fieldCondition($field_name, 'color', 'X', '=', 'something', LANGUAGE_NONE);
$result = field_sql_storage_field_storage_query($query);
// Test the results.
$this->assertEqual(1, count($result), format_string('One result should be returned, got @count', array('@count' => count($result))));
// Test the complexity of the query.
$query = $GLOBALS['test_query'];
$this->assertNotNull($query, 'Precondition: the query should be available');
$tables = $query->getTables();
$this->assertEqual(1, count($tables), 'The query contains just one table.');
// Clean up.
unset($GLOBALS['test_query']);
}
/**
* Test handling multiple conditions on multiple columns of one field for multiple languages.
*
* Tests both the result and the complexity of the query.
*/
function testFieldSqlStorageMultipleConditionsDifferentColumnsMultipleLanguages() {
field_test_entity_info_translatable('test_entity', TRUE);
// Create the multi-column shape field
$field_name = strtolower($this->randomName());
$field = array('field_name' => $field_name, 'type' => 'shape', 'cardinality' => 4, 'translatable' => TRUE);
$field = field_create_field($field);
$instance = array(
'field_name' => $field_name,
'entity_type' => 'test_entity',
'bundle' => 'test_bundle',
'settings' => array(
// Prevent warning from field_test_field_load().
'test_hook_field_load' => FALSE,
),
);
$instance = field_create_instance($instance);
$entity = field_test_create_stub_entity(NULL, NULL);
$entity->{$field_name}[LANGUAGE_NONE][0] = array('shape' => 'A', 'color' => 'X');
$entity->{$field_name}['en'][0] = array('shape' => 'B', 'color' => 'Y');
field_test_entity_save($entity);
$entity = field_test_entity_test_load($entity->ftid);
$query = new EntityFieldQuery();
// This tag causes field_test_query_store_global_test_query_alter() to be
// invoked so that the query can be tested.
$query->addTag('store_global_test_query');
$query->entityCondition('entity_type', 'test_entity');
$query->entityCondition('bundle', 'test_bundle');
$query->fieldCondition($field_name, 'color', 'X', '=', NULL, LANGUAGE_NONE);
$query->fieldCondition($field_name, 'shape', 'B', '=', NULL, 'en');
$result = field_sql_storage_field_storage_query($query);
// Test the results.
$this->assertEqual(1, count($result), format_string('One result should be returned, got @count', array('@count' => count($result))));
// Test the complexity of the query.
$query = $GLOBALS['test_query'];
$this->assertNotNull($query, 'Precondition: the query should be available');
$tables = $query->getTables();
$this->assertEqual(2, count($tables), 'The query contains two tables.');
// Clean up.
unset($GLOBALS['test_query']);
}
}
+3 -4
View File
@@ -7,8 +7,7 @@ dependencies[] = field
dependencies[] = options
files[] = tests/list.test
; Information added by drupal.org packaging script on 2013-04-03
version = "7.22"
; Information added by Drupal.org packaging script on 2020-09-16
version = "7.73"
project = "drupal"
datestamp = "1365027012"
datestamp = "1600272641"
+8 -1
View File
@@ -61,7 +61,7 @@ function list_update_7001() {
// Additionally, float keys need to be disambiguated ('.5' is '0.5').
if ($field['type'] == 'list_number' && !empty($allowed_values)) {
$keys = array_map(create_function('$a', 'return (string) (float) $a;'), array_keys($allowed_values));
$keys = array_map('_list_update_7001_float_string_cast', array_keys($allowed_values));
$allowed_values = array_combine($keys, array_values($allowed_values));
}
@@ -88,6 +88,13 @@ function list_update_7001() {
}
}
/**
* Helper callback function to cast the array element.
*/
function _list_update_7001_float_string_cast($element) {
return (string) (float) $element;
}
/**
* Helper function for list_update_7001: extract allowed values from a string.
*
+52 -52
View File
@@ -51,9 +51,9 @@ class ListFieldTestCase extends FieldTestCase {
// All three options appear.
$entity = field_test_create_stub_entity();
$form = drupal_get_form('field_test_entity_form', $entity);
$this->assertTrue(!empty($form[$this->field_name][$langcode][1]), t('Option 1 exists'));
$this->assertTrue(!empty($form[$this->field_name][$langcode][2]), t('Option 2 exists'));
$this->assertTrue(!empty($form[$this->field_name][$langcode][3]), t('Option 3 exists'));
$this->assertTrue(!empty($form[$this->field_name][$langcode][1]), 'Option 1 exists');
$this->assertTrue(!empty($form[$this->field_name][$langcode][2]), 'Option 2 exists');
$this->assertTrue(!empty($form[$this->field_name][$langcode][3]), 'Option 3 exists');
// Use one of the values in an actual entity, and check that this value
// cannot be removed from the list.
@@ -77,19 +77,19 @@ class ListFieldTestCase extends FieldTestCase {
field_update_field($this->field);
$entity = field_test_create_stub_entity();
$form = drupal_get_form('field_test_entity_form', $entity);
$this->assertTrue(empty($form[$this->field_name][$langcode][1]), t('Option 1 does not exist'));
$this->assertTrue(!empty($form[$this->field_name][$langcode][2]), t('Option 2 exists'));
$this->assertTrue(empty($form[$this->field_name][$langcode][3]), t('Option 3 does not exist'));
$this->assertTrue(empty($form[$this->field_name][$langcode][1]), 'Option 1 does not exist');
$this->assertTrue(!empty($form[$this->field_name][$langcode][2]), 'Option 2 exists');
$this->assertTrue(empty($form[$this->field_name][$langcode][3]), 'Option 3 does not exist');
// Completely new options appear.
$this->field['settings']['allowed_values'] = array(10 => 'Update', 20 => 'Twenty');
field_update_field($this->field);
$form = drupal_get_form('field_test_entity_form', $entity);
$this->assertTrue(empty($form[$this->field_name][$langcode][1]), t('Option 1 does not exist'));
$this->assertTrue(empty($form[$this->field_name][$langcode][2]), t('Option 2 does not exist'));
$this->assertTrue(empty($form[$this->field_name][$langcode][3]), t('Option 3 does not exist'));
$this->assertTrue(!empty($form[$this->field_name][$langcode][10]), t('Option 10 exists'));
$this->assertTrue(!empty($form[$this->field_name][$langcode][20]), t('Option 20 exists'));
$this->assertTrue(empty($form[$this->field_name][$langcode][1]), 'Option 1 does not exist');
$this->assertTrue(empty($form[$this->field_name][$langcode][2]), 'Option 2 does not exist');
$this->assertTrue(empty($form[$this->field_name][$langcode][3]), 'Option 3 does not exist');
$this->assertTrue(!empty($form[$this->field_name][$langcode][10]), 'Option 10 exists');
$this->assertTrue(!empty($form[$this->field_name][$langcode][20]), 'Option 20 exists');
// Options are reset when a new field with the same name is created.
field_delete_field($this->field_name);
@@ -107,9 +107,9 @@ class ListFieldTestCase extends FieldTestCase {
$this->instance = field_create_instance($this->instance);
$entity = field_test_create_stub_entity();
$form = drupal_get_form('field_test_entity_form', $entity);
$this->assertTrue(!empty($form[$this->field_name][$langcode][1]), t('Option 1 exists'));
$this->assertTrue(!empty($form[$this->field_name][$langcode][2]), t('Option 2 exists'));
$this->assertTrue(!empty($form[$this->field_name][$langcode][3]), t('Option 3 exists'));
$this->assertTrue(!empty($form[$this->field_name][$langcode][1]), 'Option 1 exists');
$this->assertTrue(!empty($form[$this->field_name][$langcode][2]), 'Option 2 exists');
$this->assertTrue(!empty($form[$this->field_name][$langcode][3]), 'Option 3 exists');
}
}
@@ -212,7 +212,7 @@ class ListFieldUITestCase extends FieldTestCase {
parent::setUp('field_test', 'field_ui');
// Create test user.
$admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer taxonomy'));
$admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer taxonomy', 'administer fields'));
$this->drupalLogin($admin_user);
// Create content type, with underscores.
@@ -233,20 +233,20 @@ class ListFieldUITestCase extends FieldTestCase {
// Flat list of textual values.
$string = "Zero\nOne";
$array = array('0' => 'Zero', '1' => 'One');
$this->assertAllowedValuesInput($string, $array, t('Unkeyed lists are accepted.'));
$this->assertAllowedValuesInput($string, $array, 'Unkeyed lists are accepted.');
// Explicit integer keys.
$string = "0|Zero\n2|Two";
$array = array('0' => 'Zero', '2' => 'Two');
$this->assertAllowedValuesInput($string, $array, t('Integer keys are accepted.'));
$this->assertAllowedValuesInput($string, $array, 'Integer keys are accepted.');
// Check that values can be added and removed.
$string = "0|Zero\n1|One";
$array = array('0' => 'Zero', '1' => 'One');
$this->assertAllowedValuesInput($string, $array, t('Values can be added and removed.'));
$this->assertAllowedValuesInput($string, $array, 'Values can be added and removed.');
// Non-integer keys.
$this->assertAllowedValuesInput("1.1|One", 'keys must be integers', t('Non integer keys are rejected.'));
$this->assertAllowedValuesInput("abc|abc", 'keys must be integers', t('Non integer keys are rejected.'));
$this->assertAllowedValuesInput("1.1|One", 'keys must be integers', 'Non integer keys are rejected.');
$this->assertAllowedValuesInput("abc|abc", 'keys must be integers', 'Non integer keys are rejected.');
// Mixed list of keyed and unkeyed values.
$this->assertAllowedValuesInput("Zero\n1|One", 'invalid input', t('Mixed lists are rejected.'));
$this->assertAllowedValuesInput("Zero\n1|One", 'invalid input', 'Mixed lists are rejected.');
// Create a node with actual data for the field.
$settings = array(
@@ -256,22 +256,22 @@ class ListFieldUITestCase extends FieldTestCase {
$node = $this->drupalCreateNode($settings);
// Check that a flat list of values is rejected once the field has data.
$this->assertAllowedValuesInput( "Zero\nOne", 'invalid input', t('Unkeyed lists are rejected once the field has data.'));
$this->assertAllowedValuesInput( "Zero\nOne", 'invalid input', 'Unkeyed lists are rejected once the field has data.');
// Check that values can be added but values in use cannot be removed.
$string = "0|Zero\n1|One\n2|Two";
$array = array('0' => 'Zero', '1' => 'One', '2' => 'Two');
$this->assertAllowedValuesInput($string, $array, t('Values can be added.'));
$this->assertAllowedValuesInput($string, $array, 'Values can be added.');
$string = "0|Zero\n1|One";
$array = array('0' => 'Zero', '1' => 'One');
$this->assertAllowedValuesInput($string, $array, t('Values not in use can be removed.'));
$this->assertAllowedValuesInput("0|Zero", 'some values are being removed while currently in use', t('Values in use cannot be removed.'));
$this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
$this->assertAllowedValuesInput("0|Zero", 'some values are being removed while currently in use', 'Values in use cannot be removed.');
// Delete the node, remove the value.
node_delete($node->nid);
$string = "0|Zero";
$array = array('0' => 'Zero');
$this->assertAllowedValuesInput($string, $array, t('Values not in use can be removed.'));
$this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
}
/**
@@ -284,19 +284,19 @@ class ListFieldUITestCase extends FieldTestCase {
// Flat list of textual values.
$string = "Zero\nOne";
$array = array('0' => 'Zero', '1' => 'One');
$this->assertAllowedValuesInput($string, $array, t('Unkeyed lists are accepted.'));
$this->assertAllowedValuesInput($string, $array, 'Unkeyed lists are accepted.');
// Explicit numeric keys.
$string = "0|Zero\n.5|Point five";
$array = array('0' => 'Zero', '0.5' => 'Point five');
$this->assertAllowedValuesInput($string, $array, t('Integer keys are accepted.'));
$this->assertAllowedValuesInput($string, $array, 'Integer keys are accepted.');
// Check that values can be added and removed.
$string = "0|Zero\n.5|Point five\n1.0|One";
$array = array('0' => 'Zero', '0.5' => 'Point five', '1' => 'One');
$this->assertAllowedValuesInput($string, $array, t('Values can be added and removed.'));
$this->assertAllowedValuesInput($string, $array, 'Values can be added and removed.');
// Non-numeric keys.
$this->assertAllowedValuesInput("abc|abc\n", 'each key must be a valid integer or decimal', t('Non numeric keys are rejected.'));
$this->assertAllowedValuesInput("abc|abc\n", 'each key must be a valid integer or decimal', 'Non numeric keys are rejected.');
// Mixed list of keyed and unkeyed values.
$this->assertAllowedValuesInput("Zero\n1|One\n", 'invalid input', t('Mixed lists are rejected.'));
$this->assertAllowedValuesInput("Zero\n1|One\n", 'invalid input', 'Mixed lists are rejected.');
// Create a node with actual data for the field.
$settings = array(
@@ -306,22 +306,22 @@ class ListFieldUITestCase extends FieldTestCase {
$node = $this->drupalCreateNode($settings);
// Check that a flat list of values is rejected once the field has data.
$this->assertAllowedValuesInput("Zero\nOne", 'invalid input', t('Unkeyed lists are rejected once the field has data.'));
$this->assertAllowedValuesInput("Zero\nOne", 'invalid input', 'Unkeyed lists are rejected once the field has data.');
// Check that values can be added but values in use cannot be removed.
$string = "0|Zero\n.5|Point five\n2|Two";
$array = array('0' => 'Zero', '0.5' => 'Point five', '2' => 'Two');
$this->assertAllowedValuesInput($string, $array, t('Values can be added.'));
$this->assertAllowedValuesInput($string, $array, 'Values can be added.');
$string = "0|Zero\n.5|Point five";
$array = array('0' => 'Zero', '0.5' => 'Point five');
$this->assertAllowedValuesInput($string, $array, t('Values not in use can be removed.'));
$this->assertAllowedValuesInput("0|Zero", 'some values are being removed while currently in use', t('Values in use cannot be removed.'));
$this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
$this->assertAllowedValuesInput("0|Zero", 'some values are being removed while currently in use', 'Values in use cannot be removed.');
// Delete the node, remove the value.
node_delete($node->nid);
$string = "0|Zero";
$array = array('0' => 'Zero');
$this->assertAllowedValuesInput($string, $array, t('Values not in use can be removed.'));
$this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
}
/**
@@ -334,21 +334,21 @@ class ListFieldUITestCase extends FieldTestCase {
// Flat list of textual values.
$string = "Zero\nOne";
$array = array('Zero' => 'Zero', 'One' => 'One');
$this->assertAllowedValuesInput($string, $array, t('Unkeyed lists are accepted.'));
$this->assertAllowedValuesInput($string, $array, 'Unkeyed lists are accepted.');
// Explicit keys.
$string = "zero|Zero\none|One";
$array = array('zero' => 'Zero', 'one' => 'One');
$this->assertAllowedValuesInput($string, $array, t('Explicit keys are accepted.'));
$this->assertAllowedValuesInput($string, $array, 'Explicit keys are accepted.');
// Check that values can be added and removed.
$string = "zero|Zero\ntwo|Two";
$array = array('zero' => 'Zero', 'two' => 'Two');
$this->assertAllowedValuesInput($string, $array, t('Values can be added and removed.'));
$this->assertAllowedValuesInput($string, $array, 'Values can be added and removed.');
// Mixed list of keyed and unkeyed values.
$string = "zero|Zero\nOne\n";
$array = array('zero' => 'Zero', 'One' => 'One');
$this->assertAllowedValuesInput($string, $array, t('Mixed lists are accepted.'));
$this->assertAllowedValuesInput($string, $array, 'Mixed lists are accepted.');
// Overly long keys.
$this->assertAllowedValuesInput("zero|Zero\n" . $this->randomName(256) . "|One", 'each key must be a string at most 255 characters long', t('Overly long keys are rejected.'));
$this->assertAllowedValuesInput("zero|Zero\n" . $this->randomName(256) . "|One", 'each key must be a string at most 255 characters long', 'Overly long keys are rejected.');
// Create a node with actual data for the field.
$settings = array(
@@ -361,22 +361,22 @@ class ListFieldUITestCase extends FieldTestCase {
// data.
$string = "Zero\nOne";
$array = array('Zero' => 'Zero', 'One' => 'One');
$this->assertAllowedValuesInput($string, $array, t('Unkeyed lists are still accepted once the field has data.'));
$this->assertAllowedValuesInput($string, $array, 'Unkeyed lists are still accepted once the field has data.');
// Check that values can be added but values in use cannot be removed.
$string = "Zero\nOne\nTwo";
$array = array('Zero' => 'Zero', 'One' => 'One', 'Two' => 'Two');
$this->assertAllowedValuesInput($string, $array, t('Values can be added.'));
$this->assertAllowedValuesInput($string, $array, 'Values can be added.');
$string = "Zero\nOne";
$array = array('Zero' => 'Zero', 'One' => 'One');
$this->assertAllowedValuesInput($string, $array, t('Values not in use can be removed.'));
$this->assertAllowedValuesInput("Zero", 'some values are being removed while currently in use', t('Values in use cannot be removed.'));
$this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
$this->assertAllowedValuesInput("Zero", 'some values are being removed while currently in use', 'Values in use cannot be removed.');
// Delete the node, remove the value.
node_delete($node->nid);
$string = "Zero";
$array = array('Zero' => 'Zero');
$this->assertAllowedValuesInput($string, $array, t('Values not in use can be removed.'));
$this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
}
/**
@@ -395,15 +395,15 @@ class ListFieldUITestCase extends FieldTestCase {
'off' => $off,
);
$this->drupalPost($this->admin_path, $edit, t('Save settings'));
$this->assertText("Saved field_list_boolean configuration.", t("The 'On' and 'Off' form fields work for boolean fields."));
$this->assertText("Saved field_list_boolean configuration.", "The 'On' and 'Off' form fields work for boolean fields.");
// Test the allowed_values on the field settings form.
$this->drupalGet($this->admin_path);
$this->assertFieldByName('on', $on, t("The 'On' value is stored correctly."));
$this->assertFieldByName('off', $off, t("The 'Off' value is stored correctly."));
$this->assertFieldByName('on', $on, "The 'On' value is stored correctly.");
$this->assertFieldByName('off', $off, "The 'Off' value is stored correctly.");
$field = field_info_field($this->field_name);
$this->assertEqual($field['settings']['allowed_values'], $allowed_values, t('The allowed value is correct'));
$this->assertFalse(isset($field['settings']['on']), t('The on value is not saved into settings'));
$this->assertFalse(isset($field['settings']['off']), t('The off value is not saved into settings'));
$this->assertEqual($field['settings']['allowed_values'], $allowed_values, 'The allowed value is correct');
$this->assertFalse(isset($field['settings']['on']), 'The on value is not saved into settings');
$this->assertFalse(isset($field['settings']['off']), 'The off value is not saved into settings');
}
/**
@@ -5,8 +5,7 @@ package = Testing
version = VERSION
hidden = TRUE
; Information added by drupal.org packaging script on 2013-04-03
version = "7.22"
; Information added by Drupal.org packaging script on 2020-09-16
version = "7.73"
project = "drupal"
datestamp = "1365027012"
datestamp = "1600272641"
+3 -4
View File
@@ -6,8 +6,7 @@ core = 7.x
dependencies[] = field
files[] = number.test
; Information added by drupal.org packaging script on 2013-04-03
version = "7.22"
; Information added by Drupal.org packaging script on 2020-09-16
version = "7.73"
project = "drupal"
datestamp = "1365027012"
datestamp = "1600272641"
+13 -2
View File
@@ -164,6 +164,15 @@ function number_field_presave($entity_type, $entity, $field, $instance, $langcod
}
}
}
if ($field['type'] == 'number_float') {
// Remove the decimal point from float values with decimal
// point but no decimal numbers.
foreach ($items as $delta => $item) {
if (isset($item['value'])) {
$items[$delta]['value'] = floatval($item['value']);
}
}
}
}
/**
@@ -188,7 +197,7 @@ function number_field_formatter_info() {
'label' => t('Default'),
'field types' => array('number_integer'),
'settings' => array(
'thousand_separator' => ' ',
'thousand_separator' => '',
// The 'decimal_separator' and 'scale' settings are not configurable
// through the UI, and will therefore keep their default values. They
// are only present so that the 'number_integer' and 'number_decimal'
@@ -202,7 +211,7 @@ function number_field_formatter_info() {
'label' => t('Default'),
'field types' => array('number_decimal', 'number_float'),
'settings' => array(
'thousand_separator' => ' ',
'thousand_separator' => '',
'decimal_separator' => '.',
'scale' => 2,
'prefix_suffix' => TRUE,
@@ -222,6 +231,8 @@ function number_field_formatter_settings_form($field, $instance, $view_mode, $fo
$display = $instance['display'][$view_mode];
$settings = $display['settings'];
$element = array();
if ($display['type'] == 'number_decimal' || $display['type'] == 'number_integer') {
$options = array(
'' => t('<none>'),
+51 -5
View File
@@ -23,7 +23,7 @@ class NumberFieldTestCase extends DrupalWebTestCase {
function setUp() {
parent::setUp('field_test');
$this->web_user = $this->drupalCreateUser(array('access field_test content', 'administer field_test content', 'administer content types'));
$this->web_user = $this->drupalCreateUser(array('access field_test content', 'administer field_test content', 'administer content types', 'administer fields'));
$this->drupalLogin($this->web_user);
}
@@ -58,7 +58,7 @@ class NumberFieldTestCase extends DrupalWebTestCase {
// Display creation form.
$this->drupalGet('test-entity/add/test-bundle');
$langcode = LANGUAGE_NONE;
$this->assertFieldByName("{$this->field['field_name']}[$langcode][0][value]", '', t('Widget is displayed'));
$this->assertFieldByName("{$this->field['field_name']}[$langcode][0][value]", '', 'Widget is displayed');
// Submit a signed decimal value within the allowed precision and scale.
$value = '-1234.5678';
@@ -68,8 +68,8 @@ class NumberFieldTestCase extends DrupalWebTestCase {
$this->drupalPost(NULL, $edit, t('Save'));
preg_match('|test-entity/manage/(\d+)/edit|', $this->url, $match);
$id = $match[1];
$this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), t('Entity was created'));
$this->assertRaw(round($value, 2), t('Value is displayed.'));
$this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), 'Entity was created');
$this->assertRaw($value, 'Value is displayed.');
// Try to create entries with more than one decimal separator; assert fail.
$wrong_entries = array(
@@ -89,7 +89,7 @@ class NumberFieldTestCase extends DrupalWebTestCase {
$this->assertText(
t('There should only be one decimal separator (@separator)',
array('@separator' => $this->field['settings']['decimal_separator'])),
t('Correctly failed to save decimal value with more than one decimal point.')
'Correctly failed to save decimal value with more than one decimal point.'
);
}
@@ -152,4 +152,50 @@ class NumberFieldTestCase extends DrupalWebTestCase {
);
$this->drupalPost(NULL, $edit, t('Save'));
}
/**
* Test number_float field.
*/
function testNumberFloatField() {
$this->field = array(
'field_name' => drupal_strtolower($this->randomName()),
'type' => 'number_float',
'settings' => array(
'precision' => 8, 'scale' => 4, 'decimal_separator' => '.',
)
);
field_create_field($this->field);
$this->instance = array(
'field_name' => $this->field['field_name'],
'entity_type' => 'test_entity',
'bundle' => 'test_bundle',
'widget' => array(
'type' => 'number',
),
'display' => array(
'default' => array(
'type' => 'number_decimal',
),
),
);
field_create_instance($this->instance);
$langcode = LANGUAGE_NONE;
$value = array(
'9.' => '9',
'.' => '0',
'123.55' => '123.55',
'.55' => '0.55',
'-0.55' => '-0.55',
);
foreach($value as $key => $value) {
$edit = array(
"{$this->field['field_name']}[$langcode][0][value]" => $key,
);
$this->drupalPost('test-entity/add/test-bundle', $edit, t('Save'));
$this->assertNoText("PDOException");
$this->assertRaw($value, 'Correct value is displayed.');
}
}
}
+3 -4
View File
@@ -6,8 +6,7 @@ core = 7.x
dependencies[] = field
files[] = options.test
; Information added by drupal.org packaging script on 2013-04-03
version = "7.22"
; Information added by Drupal.org packaging script on 2020-09-16
version = "7.73"
project = "drupal"
datestamp = "1365027012"
datestamp = "1600272641"
+9 -1
View File
@@ -185,6 +185,7 @@ function _options_properties($type, $multiple, $required, $has_value) {
$base = array(
'filter_xss' => FALSE,
'strip_tags' => FALSE,
'strip_tags_and_unescape' => FALSE,
'empty_option' => FALSE,
'optgroups' => FALSE,
);
@@ -195,7 +196,7 @@ function _options_properties($type, $multiple, $required, $has_value) {
case 'select':
$properties = array(
// Select boxes do not support any HTML tag.
'strip_tags' => TRUE,
'strip_tags_and_unescape' => TRUE,
'optgroups' => TRUE,
);
if ($multiple) {
@@ -271,9 +272,16 @@ function _options_prepare_options(&$options, $properties) {
_options_prepare_options($options[$value], $properties);
}
else {
// The 'strip_tags' option is deprecated. Use 'strip_tags_and_unescape'
// when plain text is required (and where the output will be run through
// check_plain() before being inserted back into HTML) or 'filter_xss'
// when HTML is required.
if ($properties['strip_tags']) {
$options[$value] = strip_tags($label);
}
if ($properties['strip_tags_and_unescape']) {
$options[$value] = decode_entities(strip_tags($label));
}
if ($properties['filter_xss']) {
$options[$value] = field_filter_xss($label);
}
+48 -31
View File
@@ -1,7 +1,7 @@
<?php
/**
* @file
* @file
* Tests for options.module.
*/
@@ -23,8 +23,15 @@ class OptionsWidgetsTestCase extends FieldTestCase {
'type' => 'list_integer',
'cardinality' => 1,
'settings' => array(
// Make sure that 0 works as an option.
'allowed_values' => array(0 => 'Zero', 1 => 'One', 2 => 'Some <script>dangerous</script> & unescaped <strong>markup</strong>'),
'allowed_values' => array(
// Make sure that 0 works as an option.
0 => 'Zero',
1 => 'One',
// Make sure that option text is properly sanitized.
2 => 'Some <script>dangerous</script> & unescaped <strong>markup</strong>',
// Make sure that HTML entities in option text are not double-encoded.
3 => 'Some HTML encoded markup with &lt; &amp; &gt;',
),
),
);
$this->card_1 = field_create_field($this->card_1);
@@ -35,8 +42,13 @@ class OptionsWidgetsTestCase extends FieldTestCase {
'type' => 'list_integer',
'cardinality' => 2,
'settings' => array(
// Make sure that 0 works as an option.
'allowed_values' => array(0 => 'Zero', 1 => 'One', 2 => 'Some <script>dangerous</script> & unescaped <strong>markup</strong>'),
'allowed_values' => array(
// Make sure that 0 works as an option.
0 => 'Zero',
1 => 'One',
// Make sure that option text is properly sanitized.
2 => 'Some <script>dangerous</script> & unescaped <strong>markup</strong>',
),
),
);
$this->card_2 = field_create_field($this->card_2);
@@ -47,14 +59,18 @@ class OptionsWidgetsTestCase extends FieldTestCase {
'type' => 'list_boolean',
'cardinality' => 1,
'settings' => array(
// Make sure that 0 works as a 'on' value'.
'allowed_values' => array(1 => 'Zero', 0 => 'Some <script>dangerous</script> & unescaped <strong>markup</strong>'),
'allowed_values' => array(
// Make sure that 1 works as a 'on' value'.
1 => 'Zero',
// Make sure that option text is properly sanitized.
0 => 'Some <script>dangerous</script> & unescaped <strong>markup</strong>',
),
),
);
$this->bool = field_create_field($this->bool);
// Create a web user.
$this->web_user = $this->drupalCreateUser(array('access field_test content', 'administer field_test content'));
$this->web_user = $this->drupalCreateUser(array('access field_test content', 'administer field_test content', 'administer fields'));
$this->drupalLogin($this->web_user);
}
@@ -85,7 +101,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
$this->assertNoFieldChecked("edit-card-1-$langcode-0");
$this->assertNoFieldChecked("edit-card-1-$langcode-1");
$this->assertNoFieldChecked("edit-card-1-$langcode-2");
$this->assertRaw('Some dangerous &amp; unescaped <strong>markup</strong>', t('Option text was properly filtered.'));
$this->assertRaw('Some dangerous &amp; unescaped <strong>markup</strong>', 'Option text was properly filtered.');
// Select first option.
$edit = array("card_1[$langcode]" => 0);
@@ -139,7 +155,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
$this->assertNoFieldChecked("edit-card-2-$langcode-0");
$this->assertNoFieldChecked("edit-card-2-$langcode-1");
$this->assertNoFieldChecked("edit-card-2-$langcode-2");
$this->assertRaw('Some dangerous &amp; unescaped <strong>markup</strong>', t('Option text was properly filtered.'));
$this->assertRaw('Some dangerous &amp; unescaped <strong>markup</strong>', 'Option text was properly filtered.');
// Submit form: select first and third options.
$edit = array(
@@ -178,7 +194,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
"card_2[$langcode][2]" => TRUE,
);
$this->drupalPost(NULL, $edit, t('Save'));
$this->assertText('this field cannot hold more than 2 values', t('Validation error was displayed.'));
$this->assertText('this field cannot hold more than 2 values', 'Validation error was displayed.');
// Submit form: uncheck all options.
$edit = array(
@@ -225,19 +241,20 @@ class OptionsWidgetsTestCase extends FieldTestCase {
// Display form.
$this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
// A required field without any value has a "none" option.
$this->assertTrue($this->xpath('//select[@id=:id]//option[@value="_none" and text()=:label]', array(':id' => 'edit-card-1-' . $langcode, ':label' => t('- Select a value -'))), t('A required select list has a "Select a value" choice.'));
$this->assertTrue($this->xpath('//select[@id=:id]//option[@value="_none" and text()=:label]', array(':id' => 'edit-card-1-' . $langcode, ':label' => t('- Select a value -'))), 'A required select list has a "Select a value" choice.');
// With no field data, nothing is selected.
$this->assertNoOptionSelected("edit-card-1-$langcode", '_none');
$this->assertNoOptionSelected("edit-card-1-$langcode", 0);
$this->assertNoOptionSelected("edit-card-1-$langcode", 1);
$this->assertNoOptionSelected("edit-card-1-$langcode", 2);
$this->assertRaw('Some dangerous &amp; unescaped markup', t('Option text was properly filtered.'));
$this->assertRaw('Some dangerous &amp; unescaped markup', 'Option text was properly filtered.');
$this->assertRaw('Some HTML encoded markup with &lt; &amp; &gt;', 'HTML entities in option text were properly handled and not double-encoded');
// Submit form: select invalid 'none' option.
$edit = array("card_1[$langcode]" => '_none');
$this->drupalPost(NULL, $edit, t('Save'));
$this->assertRaw(t('!title field is required.', array('!title' => $instance['field_name'])), t('Cannot save a required field when selecting "none" from the select list.'));
$this->assertRaw(t('!title field is required.', array('!title' => $instance['field_name'])), 'Cannot save a required field when selecting "none" from the select list.');
// Submit form: select first option.
$edit = array("card_1[$langcode]" => 0);
@@ -247,7 +264,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
// Display form: check that the right options are selected.
$this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
// A required field with a value has no 'none' option.
$this->assertFalse($this->xpath('//select[@id=:id]//option[@value="_none"]', array(':id' => 'edit-card-1-' . $langcode)), t('A required select list with an actual value has no "none" choice.'));
$this->assertFalse($this->xpath('//select[@id=:id]//option[@value="_none"]', array(':id' => 'edit-card-1-' . $langcode)), 'A required select list with an actual value has no "none" choice.');
$this->assertOptionSelected("edit-card-1-$langcode", 0);
$this->assertNoOptionSelected("edit-card-1-$langcode", 1);
$this->assertNoOptionSelected("edit-card-1-$langcode", 2);
@@ -259,7 +276,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
// Display form.
$this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
// A non-required field has a 'none' option.
$this->assertTrue($this->xpath('//select[@id=:id]//option[@value="_none" and text()=:label]', array(':id' => 'edit-card-1-' . $langcode, ':label' => t('- None -'))), t('A non-required select list has a "None" choice.'));
$this->assertTrue($this->xpath('//select[@id=:id]//option[@value="_none" and text()=:label]', array(':id' => 'edit-card-1-' . $langcode, ':label' => t('- None -'))), 'A non-required select list has a "None" choice.');
// Submit form: Unselect the option.
$edit = array("card_1[$langcode]" => '_none');
$this->drupalPost('test-entity/manage/' . $entity->ftid . '/edit', $edit, t('Save'));
@@ -276,8 +293,8 @@ class OptionsWidgetsTestCase extends FieldTestCase {
$this->assertNoOptionSelected("edit-card-1-$langcode", 0);
$this->assertNoOptionSelected("edit-card-1-$langcode", 1);
$this->assertNoOptionSelected("edit-card-1-$langcode", 2);
$this->assertRaw('Some dangerous &amp; unescaped markup', t('Option text was properly filtered.'));
$this->assertRaw('Group 1', t('Option groups are displayed.'));
$this->assertRaw('Some dangerous &amp; unescaped markup', 'Option text was properly filtered.');
$this->assertRaw('Group 1', 'Option groups are displayed.');
// Submit form: select first option.
$edit = array("card_1[$langcode]" => 0);
@@ -323,7 +340,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
$this->assertNoOptionSelected("edit-card-2-$langcode", 0);
$this->assertNoOptionSelected("edit-card-2-$langcode", 1);
$this->assertNoOptionSelected("edit-card-2-$langcode", 2);
$this->assertRaw('Some dangerous &amp; unescaped markup', t('Option text was properly filtered.'));
$this->assertRaw('Some dangerous &amp; unescaped markup', 'Option text was properly filtered.');
// Submit form: select first and third options.
$edit = array("card_2[$langcode][]" => array(0 => 0, 2 => 2));
@@ -350,7 +367,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
// Submit form: select the three options while the field accepts only 2.
$edit = array("card_2[$langcode][]" => array(0 => 0, 1 => 1, 2 => 2));
$this->drupalPost(NULL, $edit, t('Save'));
$this->assertText('this field cannot hold more than 2 values', t('Validation error was displayed.'));
$this->assertText('this field cannot hold more than 2 values', 'Validation error was displayed.');
// Submit form: uncheck all options.
$edit = array("card_2[$langcode][]" => array());
@@ -359,7 +376,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
// Test the 'None' option.
// Check that the 'none' option has no efect if actual options are selected
// Check that the 'none' option has no effect if actual options are selected
// as well.
$edit = array("card_2[$langcode][]" => array('_none' => '_none', 0 => 0));
$this->drupalPost('test-entity/manage/' . $entity->ftid . '/edit', $edit, t('Save'));
@@ -374,7 +391,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
$instance['required'] = TRUE;
field_update_instance($instance);
$this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
$this->assertFalse($this->xpath('//select[@id=:id]//option[@value=""]', array(':id' => 'edit-card-2-' . $langcode)), t('A required select list does not have an empty key.'));
$this->assertFalse($this->xpath('//select[@id=:id]//option[@value=""]', array(':id' => 'edit-card-2-' . $langcode)), 'A required select list does not have an empty key.');
// We do not have to test that a required select list with one option is
// auto-selected because the browser does it for us.
@@ -393,8 +410,8 @@ class OptionsWidgetsTestCase extends FieldTestCase {
$this->assertNoOptionSelected("edit-card-2-$langcode", 0);
$this->assertNoOptionSelected("edit-card-2-$langcode", 1);
$this->assertNoOptionSelected("edit-card-2-$langcode", 2);
$this->assertRaw('Some dangerous &amp; unescaped markup', t('Option text was properly filtered.'));
$this->assertRaw('Group 1', t('Option groups are displayed.'));
$this->assertRaw('Some dangerous &amp; unescaped markup', 'Option text was properly filtered.');
$this->assertRaw('Group 1', 'Option groups are displayed.');
// Submit form: select first option.
$edit = array("card_2[$langcode][]" => array(0 => 0));
@@ -438,7 +455,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
// Display form: with no field data, option is unchecked.
$this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
$this->assertNoFieldChecked("edit-bool-$langcode");
$this->assertRaw('Some dangerous &amp; unescaped <strong>markup</strong>', t('Option text was properly filtered.'));
$this->assertRaw('Some dangerous &amp; unescaped <strong>markup</strong>', 'Option text was properly filtered.');
// Submit form: check the option.
$edit = array("bool[$langcode]" => TRUE);
@@ -459,7 +476,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
$this->assertNoFieldChecked("edit-bool-$langcode");
// Create admin user.
$admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer taxonomy'));
$admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer taxonomy', 'administer fields'));
$this->drupalLogin($admin_user);
// Create a test field instance.
@@ -483,13 +500,13 @@ class OptionsWidgetsTestCase extends FieldTestCase {
$this->assertText(
'Use field label instead of the "On value" as label ',
t('Display setting checkbox available.')
'Display setting checkbox available.'
);
$this->assertFieldByXPath(
'*//label[@for="edit-' . $this->bool['field_name'] . '-und" and text()="MyOnValue "]',
TRUE,
t('Default case shows "On value"')
'Default case shows "On value"'
);
// Enable setting
@@ -502,16 +519,16 @@ class OptionsWidgetsTestCase extends FieldTestCase {
$this->drupalGet($fieldEditUrl);
$this->assertText(
'Use field label instead of the "On value" as label ',
t('Display setting checkbox is available')
'Display setting checkbox is available'
);
$this->assertFieldChecked(
'edit-instance-widget-settings-display-label',
t('Display settings checkbox checked')
'Display settings checkbox checked'
);
$this->assertFieldByXPath(
'*//label[@for="edit-' . $this->bool['field_name'] . '-und" and text()="' . $this->bool['field_name'] . ' "]',
TRUE,
t('Display label changes label of the checkbox')
'Display label changes label of the checkbox'
);
}
}
+3 -4
View File
@@ -7,8 +7,7 @@ dependencies[] = field
files[] = text.test
required = TRUE
; Information added by drupal.org packaging script on 2013-04-03
version = "7.22"
; Information added by Drupal.org packaging script on 2020-09-16
version = "7.73"
project = "drupal"
datestamp = "1365027012"
datestamp = "1600272641"
+17 -13
View File
@@ -12,9 +12,9 @@ Drupal.behaviors.textSummary = {
$summaries.once('text-summary-wrapper').each(function(index) {
var $summary = $(this);
var $summaryLabel = $summary.find('label');
var $summaryLabel = $summary.find('label').first();
var $full = $widget.find('.text-full').eq(index).closest('.form-item');
var $fullLabel = $full.find('label');
var $fullLabel = $full.find('label').first();
// Create a placeholder label when the field cardinality is
// unlimited or greater than 1.
@@ -23,24 +23,28 @@ Drupal.behaviors.textSummary = {
}
// Setup the edit/hide summary link.
var $link = $('<span class="field-edit-link">(<a class="link-edit-summary" href="#">' + Drupal.t('Hide summary') + '</a>)</span>').toggle(
function () {
var $link = $('<span class="field-edit-link">(<a class="link-edit-summary" href="#">' + Drupal.t('Hide summary') + '</a>)</span>');
var $a = $link.find('a');
var toggleClick = true;
$link.bind('click', function (e) {
if (toggleClick) {
$summary.hide();
$(this).find('a').html(Drupal.t('Edit summary')).end().appendTo($fullLabel);
return false;
},
function () {
$summary.show();
$(this).find('a').html(Drupal.t('Hide summary')).end().appendTo($summaryLabel);
return false;
$a.html(Drupal.t('Edit summary'));
$link.appendTo($fullLabel);
}
).appendTo($summaryLabel);
else {
$summary.show();
$a.html(Drupal.t('Hide summary'));
$link.appendTo($summaryLabel);
}
toggleClick = !toggleClick;
return false;
}).appendTo($summaryLabel);
// If no summary is set, hide the summary field.
if ($(this).find('.text-summary').val() == '') {
$link.click();
}
return;
});
});
}
+4 -2
View File
@@ -223,11 +223,13 @@ function text_field_formatter_settings_form($field, $instance, $view_mode, $form
if (strpos($display['type'], '_trimmed') !== FALSE) {
$element['trim_length'] = array(
'#title' => t('Trim length'),
'#title' => t('Trimmed limit'),
'#type' => 'textfield',
'#field_suffix' => t('characters'),
'#size' => 10,
'#default_value' => $settings['trim_length'],
'#element_validate' => array('element_validate_integer_positive'),
'#description' => t('If the summary is not set, the trimmed %label field will be shorter than this character limit.', array('%label' => $instance['label'])),
'#required' => TRUE,
);
}
@@ -245,7 +247,7 @@ function text_field_formatter_settings_summary($field, $instance, $view_mode) {
$summary = '';
if (strpos($display['type'], '_trimmed') !== FALSE) {
$summary = t('Trim length') . ': ' . $settings['trim_length'];
$summary = t('Trimmed limit: @trim_length characters', array('@trim_length' => $settings['trim_length']));
}
return $summary;
+21 -20
View File
@@ -110,8 +110,8 @@ class TextFieldTestCase extends DrupalWebTestCase {
// Display creation form.
$this->drupalGet('test-entity/add/test-bundle');
$this->assertFieldByName("{$this->field_name}[$langcode][0][value]", '', t('Widget is displayed'));
$this->assertNoFieldByName("{$this->field_name}[$langcode][0][format]", '1', t('Format selector is not displayed'));
$this->assertFieldByName("{$this->field_name}[$langcode][0][value]", '', 'Widget is displayed');
$this->assertNoFieldByName("{$this->field_name}[$langcode][0][format]", '1', 'Format selector is not displayed');
// Submit with some value.
$value = $this->randomName();
@@ -121,7 +121,7 @@ class TextFieldTestCase extends DrupalWebTestCase {
$this->drupalPost(NULL, $edit, t('Save'));
preg_match('|test-entity/manage/(\d+)/edit|', $this->url, $match);
$id = $match[1];
$this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), t('Entity was created'));
$this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), 'Entity was created');
// Display the entity.
$entity = field_test_entity_test_load($id);
@@ -179,8 +179,8 @@ class TextFieldTestCase extends DrupalWebTestCase {
// Display the creation form. Since the user only has access to one format,
// no format selector will be displayed.
$this->drupalGet('test-entity/add/test-bundle');
$this->assertFieldByName("{$this->field_name}[$langcode][0][value]", '', t('Widget is displayed'));
$this->assertNoFieldByName("{$this->field_name}[$langcode][0][format]", '', t('Format selector is not displayed'));
$this->assertFieldByName("{$this->field_name}[$langcode][0][value]", '', 'Widget is displayed');
$this->assertNoFieldByName("{$this->field_name}[$langcode][0][format]", '', 'Format selector is not displayed');
// Submit with data that should be filtered.
$value = '<em>' . $this->randomName() . '</em>';
@@ -190,14 +190,14 @@ class TextFieldTestCase extends DrupalWebTestCase {
$this->drupalPost(NULL, $edit, t('Save'));
preg_match('|test-entity/manage/(\d+)/edit|', $this->url, $match);
$id = $match[1];
$this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), t('Entity was created'));
$this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), 'Entity was created');
// Display the entity.
$entity = field_test_entity_test_load($id);
$entity->content = field_attach_view($entity_type, $entity, 'full');
$this->content = drupal_render($entity->content);
$this->assertNoRaw($value, t('HTML tags are not displayed.'));
$this->assertRaw(check_plain($value), t('Escaped HTML is displayed correctly.'));
$this->assertNoRaw($value, 'HTML tags are not displayed.');
$this->assertRaw(check_plain($value), 'Escaped HTML is displayed correctly.');
// Create a new text format that does not escape HTML, and grant the user
// access to it.
@@ -219,21 +219,21 @@ class TextFieldTestCase extends DrupalWebTestCase {
// Display edition form.
// We should now have a 'text format' selector.
$this->drupalGet('test-entity/manage/' . $id . '/edit');
$this->assertFieldByName("{$this->field_name}[$langcode][0][value]", NULL, t('Widget is displayed'));
$this->assertFieldByName("{$this->field_name}[$langcode][0][format]", NULL, t('Format selector is displayed'));
$this->assertFieldByName("{$this->field_name}[$langcode][0][value]", NULL, 'Widget is displayed');
$this->assertFieldByName("{$this->field_name}[$langcode][0][format]", NULL, 'Format selector is displayed');
// Edit and change the text format to the new one that was created.
$edit = array(
"{$this->field_name}[$langcode][0][format]" => $format_id,
);
$this->drupalPost(NULL, $edit, t('Save'));
$this->assertRaw(t('test_entity @id has been updated.', array('@id' => $id)), t('Entity was updated'));
$this->assertRaw(t('test_entity @id has been updated.', array('@id' => $id)), 'Entity was updated');
// Display the entity.
$entity = field_test_entity_test_load($id);
$entity->content = field_attach_view($entity_type, $entity, 'full');
$this->content = drupal_render($entity->content);
$this->assertRaw($value, t('Value is displayed unfiltered'));
$this->assertRaw($value, 'Value is displayed unfiltered');
}
}
@@ -383,7 +383,7 @@ class TextSummaryTestCase extends DrupalWebTestCase {
*/
function callTextSummary($text, $expected, $format = NULL, $size = NULL) {
$summary = text_summary($text, $format, $size);
$this->assertIdentical($summary, $expected, t('Generated summary "@summary" matches expected "@expected".', array('@summary' => $summary, '@expected' => $expected)));
$this->assertIdentical($summary, $expected, format_string('Generated summary "@summary" matches expected "@expected".', array('@summary' => $summary, '@expected' => $expected)));
}
/**
@@ -401,7 +401,7 @@ class TextSummaryTestCase extends DrupalWebTestCase {
$this->drupalPost('node/add/article', $edit, t('Save'));
$node = $this->drupalGetNodeByTitle($edit['title']);
$this->assertIdentical($node->body['und'][0]['summary'], $summary, t('Article with with summary and no body has been submitted.'));
$this->assertIdentical($node->body['und'][0]['summary'], $summary, 'Article with with summary and no body has been submitted.');
}
}
@@ -424,6 +424,7 @@ class TextTranslationTestCase extends DrupalWebTestCase {
'administer content types',
'access administration pages',
'bypass node access',
'administer fields',
filter_permission_name($full_html_format),
));
$this->translator = $this->drupalCreateUser(array('create article content', 'edit own article content', 'translate content'));
@@ -436,7 +437,7 @@ class TextTranslationTestCase extends DrupalWebTestCase {
// Set "Article" content type to use multilingual support with translation.
$edit = array('language_content_type' => 2);
$this->drupalPost('admin/structure/types/manage/article', $edit, t('Save content type'));
$this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Article')), t('Article content type has been updated.'));
$this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Article')), 'Article content type has been updated.');
}
/**
@@ -464,7 +465,7 @@ class TextTranslationTestCase extends DrupalWebTestCase {
$node = $this->drupalGetNodeByTitle($edit['title']);
$this->drupalGet("node/$node->nid/translate");
$this->clickLink(t('add translation'));
$this->assertFieldByXPath("//textarea[@name='body[$langcode][0][value]']", $body, t('The textfield widget is populated.'));
$this->assertFieldByXPath("//textarea[@name='body[$langcode][0][value]']", $body, 'The textfield widget is populated.');
}
/**
@@ -476,7 +477,7 @@ class TextTranslationTestCase extends DrupalWebTestCase {
$edit = array('field[cardinality]' => -1);
$this->drupalPost('admin/structure/types/manage/article/fields/body', $edit, t('Save settings'));
$this->drupalGet('node/add/article');
$this->assertFieldByXPath("//input[@name='body_add_more']", t('Add another item'), t('Body field cardinality set to multiple.'));
$this->assertFieldByXPath("//input[@name='body_add_more']", t('Add another item'), 'Body field cardinality set to multiple.');
$body = array(
$this->randomName(),
@@ -501,7 +502,7 @@ class TextTranslationTestCase extends DrupalWebTestCase {
"body[$langcode][$delta][format]" => array_shift($formats),
);
$this->drupalPost('node/1/edit', $edit, t('Save'));
$this->assertText($body[$delta], t('The body field with delta @delta has been saved.', array('@delta' => $delta)));
$this->assertText($body[$delta], format_string('The body field with delta @delta has been saved.', array('@delta' => $delta)));
}
// Login as translator.
@@ -511,7 +512,7 @@ class TextTranslationTestCase extends DrupalWebTestCase {
$node = $this->drupalGetNodeByTitle($title);
$this->drupalGet("node/$node->nid/translate");
$this->clickLink(t('add translation'));
$this->assertNoText($body[0], t('The body field with delta @delta is hidden.', array('@delta' => 0)));
$this->assertText($body[1], t('The body field with delta @delta is shown.', array('@delta' => 1)));
$this->assertNoText($body[0], format_string('The body field with delta @delta is hidden.', array('@delta' => 0)));
$this->assertText($body[1], format_string('The body field with delta @delta is shown.', array('@delta' => 1)));
}
}
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -28,7 +28,9 @@ function field_test_field_info() {
'shape' => array(
'label' => t('Shape'),
'description' => t('Another dummy field type.'),
'settings' => array(),
'settings' => array(
'foreign_key_name' => 'shape',
),
'instance_settings' => array(),
'default_widget' => 'test_field_widget',
'default_formatter' => 'field_test_default',
+3 -4
View File
@@ -6,8 +6,7 @@ files[] = field_test.entity.inc
version = VERSION
hidden = TRUE
; Information added by drupal.org packaging script on 2013-04-03
version = "7.22"
; Information added by Drupal.org packaging script on 2020-09-16
version = "7.73"
project = "drupal"
datestamp = "1365027012"
datestamp = "1600272641"
+15 -3
View File
@@ -60,7 +60,7 @@ function field_test_schema() {
'description' => 'The base table for test entities with a bundle key.',
'fields' => array(
'ftid' => array(
'description' => 'The primary indentifier for a test_entity_bundle_key.',
'description' => 'The primary identifier for a test_entity_bundle_key.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
@@ -79,7 +79,7 @@ function field_test_schema() {
'description' => 'The base table for test entities with a bundle.',
'fields' => array(
'ftid' => array(
'description' => 'The primary indentifier for a test_entity_bundle.',
'description' => 'The primary identifier for a test_entity_bundle.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
@@ -132,6 +132,18 @@ function field_test_field_schema($field) {
);
}
else {
$foreign_keys = array();
// The 'foreign keys' key is not always used in tests.
if (!empty($field['settings']['foreign_key_name'])) {
$foreign_keys['foreign keys'] = array(
// This is a dummy foreign key definition, references a table that
// doesn't exist, but that's not a problem.
$field['settings']['foreign_key_name'] => array(
'table' => $field['settings']['foreign_key_name'],
'columns' => array($field['settings']['foreign_key_name'] => 'id'),
),
);
}
return array(
'columns' => array(
'shape' => array(
@@ -145,6 +157,6 @@ function field_test_field_schema($field) {
'not null' => FALSE,
),
),
);
) + $foreign_keys;
}
}
+16 -4
View File
@@ -204,10 +204,7 @@ function field_test_dummy_field_storage_query(EntityFieldQuery $query) {
}
/**
* Entity label callback.
*
* @param $entity
* The entity object.
* Implements callback_entity_info_label().
*
* @return
* The label of the entity prefixed with "label callback".
@@ -223,6 +220,10 @@ function field_test_field_attach_view_alter(&$output, $context) {
if (!empty($context['display']['settings']['alter'])) {
$output['test_field'][] = array('#markup' => 'field_test_field_attach_view_alter');
}
if (isset($output['test_field'])) {
$output['test_field'][] = array('#markup' => 'field language is ' . $context['language']);
}
}
/**
@@ -270,3 +271,14 @@ function field_test_query_efq_table_prefixing_test_alter(&$query) {
// exception if the EFQ does not properly prefix the base table.
$query->join('test_entity','te2','%alias.ftid = test_entity.ftid');
}
/**
* Implements hook_query_TAG_alter() for tag 'store_global_test_query'.
*/
function field_test_query_store_global_test_query_alter($query) {
// Save the query in a global variable so that it can be examined by tests.
// This can be used by any test which needs to check a query, but see
// FieldSqlStorageTestCase::testFieldSqlStorageMultipleConditionsSameColumn()
// for an example.
$GLOBALS['test_query'] = $query;
}
+3 -3
View File
@@ -455,13 +455,13 @@ function field_test_field_attach_rename_bundle($bundle_old, $bundle_new) {
function field_test_field_attach_delete_bundle($entity_type, $bundle, $instances) {
$data = _field_test_storage_data();
foreach ($instances as $field_name => $instance) {
$field = field_info_field($field_name);
foreach ($instances as $instance) {
$field = field_info_field_by_id($instance['field_id']);
if ($field['storage']['type'] == 'field_test_storage') {
$field_data = &$data[$field['id']];
foreach (array('current', 'revisions') as $sub_table) {
foreach ($field_data[$sub_table] as &$row) {
if ($row->bundle == $bundle_old) {
if ($row->bundle == $bundle) {
$row->deleted = TRUE;
}
}
+5 -3
View File
@@ -4,8 +4,10 @@
* @file field.tpl.php
* Default template implementation to display the value of a field.
*
* This file is not used and is here as a starting point for customization only.
* @see theme_field()
* This file is not used by Drupal core, which uses theme functions instead for
* performance reasons. The markup is the same, though, so if you want to use
* template files rather than functions to extend field theming, copy this to
* your custom theme. See theme_field() for a discussion of performance.
*
* Available variables:
* - $items: An array of field values. Use render() to output them.
@@ -45,7 +47,7 @@
*/
?>
<!--
THIS FILE IS NOT USED AND IS HERE AS A STARTING POINT FOR CUSTOMIZATION ONLY.
This file is not used by Drupal core, which uses theme functions instead.
See http://api.drupal.org/api/function/theme_field/7 for details.
After copying this file to your theme's folder and customizing it, remove this
HTML comment.
+12 -4
View File
@@ -936,7 +936,7 @@ function field_ui_display_overview_form($form, &$form_state, $entity_type, $bund
$field_label_options = array(
'above' => t('Above'),
'inline' => t('Inline'),
'hidden' => t('<Hidden>'),
'hidden' => '<' . t('Hidden') . '>',
);
$extra_visibility_options = array(
'visible' => t('Visible'),
@@ -992,7 +992,7 @@ function field_ui_display_overview_form($form, &$form_state, $entity_type, $bund
);
$formatter_options = field_ui_formatter_options($field['type']);
$formatter_options['hidden'] = t('<Hidden>');
$formatter_options['hidden'] = '<' . t('Hidden') . '>';
$table[$name]['format'] = array(
'type' => array(
'#type' => 'select',
@@ -1026,6 +1026,10 @@ function field_ui_display_overview_form($form, &$form_state, $entity_type, $bund
$instance['display'][$view_mode]['type'] = $formatter_type;
$formatter = field_info_formatter_types($formatter_type);
// For hidden fields, $formatter will be NULL, but we expect an array later.
// To maintain BC, but avoid PHP 7.4 Notices, ensure $formatter is an array
// with a 'module' element.
$formatter['module'] = isset($formatter['module']) ? $formatter['module'] : '';
$instance['display'][$view_mode]['module'] = $formatter['module'];
$instance['display'][$view_mode]['settings'] = $settings;
@@ -1558,8 +1562,8 @@ function field_ui_existing_field_options($entity_type, $bundle) {
/**
* Form constructor for the field settings edit page.
*
* @see field_ui_settings_form_submit()
* @ingroups forms
* @see field_ui_field_settings_form_submit()
* @ingroup forms
*/
function field_ui_field_settings_form($form, &$form_state, $instance) {
$bundle = $instance['bundle'];
@@ -2105,6 +2109,10 @@ function field_ui_next_destination($entity_type, $bundle) {
$destinations = !empty($_REQUEST['destinations']) ? $_REQUEST['destinations'] : array();
if (!empty($destinations)) {
unset($_REQUEST['destinations']);
}
// Remove any external URLs.
$destinations = array_diff($destinations, array_filter($destinations, 'url_is_external'));
if ($destinations) {
return field_ui_get_destinations($destinations);
}
$admin_path = _field_ui_bundle_admin_path($entity_type, $bundle);
+3
View File
@@ -134,6 +134,9 @@ function hook_field_widget_settings_form($field, $instance) {
/**
* Specify the form elements for a formatter's settings.
*
* This hook is only invoked if hook_field_formatter_settings_summary()
* returns a non-empty value.
*
* @param $field
* The field structure being configured.
* @param $instance
+3 -4
View File
@@ -6,8 +6,7 @@ core = 7.x
dependencies[] = field
files[] = field_ui.test
; Information added by drupal.org packaging script on 2013-04-03
version = "7.22"
; Information added by Drupal.org packaging script on 2020-09-16
version = "7.73"
project = "drupal"
datestamp = "1365027012"
datestamp = "1600272641"
+2 -2
View File
@@ -168,7 +168,7 @@ Drupal.fieldUIOverview = {
var dragObject = this;
var row = dragObject.rowObject.element;
var rowHandler = $(row).data('fieldUIRowHandler');
if (rowHandler !== undefined) {
if (typeof rowHandler !== 'undefined') {
var regionRow = $(row).prevAll('tr.region-message').get(0);
var region = regionRow.className.replace(/([^ ]+[ ]+)*region-([^ ]+)-message([ ]+[^ ]+)*/, '$2');
@@ -319,7 +319,7 @@ Drupal.fieldUIDisplayOverview.field.prototype = {
if (currentValue == 'hidden') {
// Restore the formatter back to the default formatter. Pseudo-fields do
// not have default formatters, we just return to 'visible' for those.
var value = (this.defaultFormatter != undefined) ? this.defaultFormatter : 'visible';
var value = (typeof this.defaultFormatter !== 'undefined') ? this.defaultFormatter : this.$formatSelect.find('option').val();
}
break;
+28 -3
View File
@@ -106,9 +106,19 @@ function field_ui_menu() {
$access = array_intersect_key($bundle_info['admin'], drupal_map_assoc(array('access callback', 'access arguments')));
$access += array(
'access callback' => 'user_access',
'access arguments' => array('administer site configuration'),
'access arguments' => array('administer fields'),
);
// Add the "administer fields" permission on top of the access
// restriction because the field UI should only be accessible to
// trusted users.
if ($access['access callback'] != 'user_access' || $access['access arguments'] != array('administer fields')) {
$access = array(
'access callback' => 'field_ui_admin_access',
'access arguments' => array($access['access callback'], $access['access arguments']),
);
}
$items["$path/fields"] = array(
'title' => 'Manage fields',
'page callback' => 'drupal_get_form',
@@ -255,6 +265,12 @@ function field_ui_menu_title($instance) {
* Menu access callback for the 'view mode display settings' pages.
*/
function _field_ui_view_mode_menu_access($entity_type, $bundle, $view_mode, $access_callback) {
// It's good practice to call func_get_args() at the beginning of a function
// to avoid problems with function parameters being modified later. The
// behavior of func_get_args() changed in PHP7.
// @see https://www.php.net/manual/en/migration70.incompatible.php#migration70.incompatible.other.func-parameter-modified
$all_args = func_get_args();
// First, determine visibility according to the 'use custom display'
// setting for the view mode.
$bundle = field_extract_bundle($entity_type, $bundle);
@@ -265,7 +281,6 @@ function _field_ui_view_mode_menu_access($entity_type, $bundle, $view_mode, $acc
// part of _menu_check_access().
if ($visibility) {
// Grab the variable 'access arguments' part.
$all_args = func_get_args();
$args = array_slice($all_args, 4);
$callback = empty($access_callback) ? 0 : trim($access_callback);
if (is_numeric($callback)) {
@@ -318,7 +333,7 @@ function field_ui_field_attach_create_bundle($entity_type, $bundle) {
}
/**
* Determines the adminstration path for a bundle.
* Determines the administration path for a bundle.
*/
function _field_ui_bundle_admin_path($entity_type, $bundle_name) {
$bundles = field_info_bundles($entity_type);
@@ -392,3 +407,13 @@ function field_ui_form_node_type_form_submit($form, &$form_state) {
$form_state['redirect'] = _field_ui_bundle_admin_path('node', $form_state['values']['type']) .'/fields';
}
}
/**
* Access callback to determine if a user is allowed to use the field UI.
*
* Only grant access if the user has both the "administer fields" permission and
* is granted access by the entity specific restrictions.
*/
function field_ui_admin_access($access_callback, $access_arguments) {
return user_access('administer fields') && call_user_func_array($access_callback, $access_arguments);
}
+61 -48
View File
@@ -22,7 +22,7 @@ class FieldUITestCase extends DrupalWebTestCase {
parent::setUp($modules);
// Create test user.
$admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer taxonomy'));
$admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer taxonomy', 'administer fields'));
$this->drupalLogin($admin_user);
// Create content type, with underscores.
@@ -59,18 +59,18 @@ class FieldUITestCase extends DrupalWebTestCase {
// First step : 'Add new field' on the 'Manage fields' page.
$this->drupalPost("$bundle_path/fields", $initial_edit, t('Save'));
$this->assertRaw(t('These settings apply to the %label field everywhere it is used.', array('%label' => $label)), t('Field settings page was displayed.'));
$this->assertRaw(t('These settings apply to the %label field everywhere it is used.', array('%label' => $label)), 'Field settings page was displayed.');
// Second step : 'Field settings' form.
$this->drupalPost(NULL, $field_edit, t('Save field settings'));
$this->assertRaw(t('Updated field %label field settings.', array('%label' => $label)), t('Redirected to instance and widget settings page.'));
$this->assertRaw(t('Updated field %label field settings.', array('%label' => $label)), 'Redirected to instance and widget settings page.');
// Third step : 'Instance settings' form.
$this->drupalPost(NULL, $instance_edit, t('Save settings'));
$this->assertRaw(t('Saved %label configuration.', array('%label' => $label)), t('Redirected to "Manage fields" page.'));
$this->assertRaw(t('Saved %label configuration.', array('%label' => $label)), 'Redirected to "Manage fields" page.');
// Check that the field appears in the overview form.
$this->assertFieldByXPath('//table[@id="field-overview"]//td[1]', $label, t('Field was created and appears in the overview page.'));
$this->assertFieldByXPath('//table[@id="field-overview"]//td[1]', $label, 'Field was created and appears in the overview page.');
}
/**
@@ -98,10 +98,10 @@ class FieldUITestCase extends DrupalWebTestCase {
// Second step : 'Instance settings' form.
$this->drupalPost(NULL, $instance_edit, t('Save settings'));
$this->assertRaw(t('Saved %label configuration.', array('%label' => $label)), t('Redirected to "Manage fields" page.'));
$this->assertRaw(t('Saved %label configuration.', array('%label' => $label)), 'Redirected to "Manage fields" page.');
// Check that the field appears in the overview form.
$this->assertFieldByXPath('//table[@id="field-overview"]//td[1]', $label, t('Field was created and appears in the overview page.'));
$this->assertFieldByXPath('//table[@id="field-overview"]//td[1]', $label, 'Field was created and appears in the overview page.');
}
/**
@@ -119,14 +119,14 @@ class FieldUITestCase extends DrupalWebTestCase {
function fieldUIDeleteField($bundle_path, $field_name, $label, $bundle_label) {
// Display confirmation form.
$this->drupalGet("$bundle_path/fields/$field_name/delete");
$this->assertRaw(t('Are you sure you want to delete the field %label', array('%label' => $label)), t('Delete confirmation was found.'));
$this->assertRaw(t('Are you sure you want to delete the field %label', array('%label' => $label)), 'Delete confirmation was found.');
// Submit confirmation form.
$this->drupalPost(NULL, array(), t('Delete'));
$this->assertRaw(t('The field %label has been deleted from the %type content type.', array('%label' => $label, '%type' => $bundle_label)), t('Delete message was found.'));
$this->assertRaw(t('The field %label has been deleted from the %type content type.', array('%label' => $label, '%type' => $bundle_label)), 'Delete message was found.');
// Check that the field does not appear in the overview form.
$this->assertNoFieldByXPath('//table[@id="field-overview"]//span[@class="label-field"]', $label, t('Field does not appear in the overview page.'));
$this->assertNoFieldByXPath('//table[@id="field-overview"]//span[@class="label-field"]', $label, 'Field does not appear in the overview page.');
}
}
@@ -179,13 +179,13 @@ class FieldUIManageFieldsTestCase extends FieldUITestCase {
);
foreach ($table_headers as $table_header) {
// We check that the label appear in the table headings.
$this->assertRaw($table_header . '</th>', t('%table_header table header was found.', array('%table_header' => $table_header)));
$this->assertRaw($table_header . '</th>', format_string('%table_header table header was found.', array('%table_header' => $table_header)));
}
// "Add new field" and "Add existing field" aren't a table heading so just
// test the text.
foreach (array('Add new field', 'Add existing field') as $element) {
$this->assertText($element, t('"@element" was found.', array('@element' => $element)));
$this->assertText($element, format_string('"@element" was found.', array('@element' => $element)));
}
}
@@ -208,7 +208,7 @@ class FieldUIManageFieldsTestCase extends FieldUITestCase {
// should also appear in the 'taxonomy term' entity.
$vocabulary = taxonomy_vocabulary_load(1);
$this->drupalGet('admin/structure/taxonomy/' . $vocabulary->machine_name . '/fields');
$this->assertTrue($this->xpath('//select[@name="fields[_add_existing_field][field_name]"]//option[@value="' . $this->field_name . '"]'), t('Existing field was found in account settings.'));
$this->assertTrue($this->xpath('//select[@name="fields[_add_existing_field][field_name]"]//option[@value="' . $this->field_name . '"]'), 'Existing field was found in account settings.');
}
/**
@@ -231,7 +231,7 @@ class FieldUIManageFieldsTestCase extends FieldUITestCase {
$this->assertFieldSettings($this->type, $this->field_name, $string);
// Assert redirection back to the "manage fields" page.
$this->assertText(t('Saved @label configuration.', array('@label' => $this->field_label)), t('Redirected to "Manage fields" page.'));
$this->assertText(t('Saved @label configuration.', array('@label' => $this->field_label)), 'Redirected to "Manage fields" page.');
}
/**
@@ -240,12 +240,12 @@ class FieldUIManageFieldsTestCase extends FieldUITestCase {
function addExistingField() {
// Check "Add existing field" appears.
$this->drupalGet('admin/structure/types/manage/page/fields');
$this->assertRaw(t('Add existing field'), t('"Add existing field" was found.'));
$this->assertRaw(t('Add existing field'), '"Add existing field" was found.');
// Check that the list of options respects entity type restrictions on
// fields. The 'comment' field is restricted to the 'comment' entity type
// and should not appear in the list.
$this->assertFalse($this->xpath('//select[@id="edit-add-existing-field-field-name"]//option[@value="comment"]'), t('The list of options respects entity type restrictions.'));
$this->assertFalse($this->xpath('//select[@id="edit-add-existing-field-field-name"]//option[@value="comment"]'), 'The list of options respects entity type restrictions.');
// Add a new field based on an existing field.
$edit = array(
@@ -272,12 +272,12 @@ class FieldUIManageFieldsTestCase extends FieldUITestCase {
field_info_cache_clear();
// Assert field settings.
$field = field_info_field($field_name);
$this->assertTrue($field['settings']['test_field_setting'] == $string, t('Field settings were found.'));
$this->assertTrue($field['settings']['test_field_setting'] == $string, 'Field settings were found.');
// Assert instance and widget settings.
$instance = field_info_instance($entity_type, $field_name, $bundle);
$this->assertTrue($instance['settings']['test_instance_setting'] == $string, t('Field instance settings were found.'));
$this->assertTrue($instance['widget']['settings']['test_widget_setting'] == $string, t('Field widget settings were found.'));
$this->assertTrue($instance['settings']['test_instance_setting'] == $string, 'Field instance settings were found.');
$this->assertTrue($instance['widget']['settings']['test_widget_setting'] == $string, 'Field widget settings were found.');
}
/**
@@ -303,31 +303,31 @@ class FieldUIManageFieldsTestCase extends FieldUITestCase {
$element_id = "edit-$field_name-$langcode-0-value";
$element_name = "{$field_name}[$langcode][0][value]";
$this->drupalGet($admin_path);
$this->assertFieldById($element_id, '', t('The default value widget was empty.'));
$this->assertFieldById($element_id, '', 'The default value widget was empty.');
// Check that invalid default values are rejected.
$edit = array($element_name => '-1');
$this->drupalPost($admin_path, $edit, t('Save settings'));
$this->assertText("$field_name does not accept the value -1", t('Form vaildation failed.'));
$this->assertText("$field_name does not accept the value -1", 'Form vaildation failed.');
// Check that the default value is saved.
$edit = array($element_name => '1');
$this->drupalPost($admin_path, $edit, t('Save settings'));
$this->assertText("Saved $field_name configuration", t('The form was successfully submitted.'));
$this->assertText("Saved $field_name configuration", 'The form was successfully submitted.');
$instance = field_info_instance('node', $field_name, $this->type);
$this->assertEqual($instance['default_value'], array(array('value' => 1)), t('The default value was correctly saved.'));
$this->assertEqual($instance['default_value'], array(array('value' => 1)), 'The default value was correctly saved.');
// Check that the default value shows up in the form
$this->drupalGet($admin_path);
$this->assertFieldById($element_id, '1', t('The default value widget was displayed with the correct value.'));
$this->assertFieldById($element_id, '1', 'The default value widget was displayed with the correct value.');
// Check that the default value can be emptied.
$edit = array($element_name => '');
$this->drupalPost(NULL, $edit, t('Save settings'));
$this->assertText("Saved $field_name configuration", t('The form was successfully submitted.'));
$this->assertText("Saved $field_name configuration", 'The form was successfully submitted.');
field_info_cache_clear();
$instance = field_info_instance('node', $field_name, $this->type);
$this->assertEqual($instance['default_value'], NULL, t('The default value was correctly saved.'));
$this->assertEqual($instance['default_value'], NULL, 'The default value was correctly saved.');
}
/**
@@ -362,9 +362,9 @@ class FieldUIManageFieldsTestCase extends FieldUITestCase {
// Reset the fields info.
field_info_cache_clear();
// Check that the field instance was deleted.
$this->assertNull(field_info_instance('node', $this->field_name, $this->type), t('Field instance was deleted.'));
$this->assertNull(field_info_instance('node', $this->field_name, $this->type), 'Field instance was deleted.');
// Check that the field was not deleted
$this->assertNotNull(field_info_field($this->field_name), t('Field was not deleted.'));
$this->assertNotNull(field_info_field($this->field_name), 'Field was not deleted.');
// Delete the second instance.
$this->fieldUIDeleteField($bundle_path2, $this->field_name, $this->field_label, $type_name2);
@@ -372,9 +372,9 @@ class FieldUIManageFieldsTestCase extends FieldUITestCase {
// Reset the fields info.
field_info_cache_clear();
// Check that the field instance was deleted.
$this->assertNull(field_info_instance('node', $this->field_name, $type_name2), t('Field instance was deleted.'));
$this->assertNull(field_info_instance('node', $this->field_name, $type_name2), 'Field instance was deleted.');
// Check that the field was deleted too.
$this->assertNull(field_info_field($this->field_name), t('Field was deleted.'));
$this->assertNull(field_info_field($this->field_name), 'Field was deleted.');
}
/**
@@ -385,7 +385,7 @@ class FieldUIManageFieldsTestCase extends FieldUITestCase {
// Check that the field type is not available in the 'add new field' row.
$this->drupalGet($bundle_path);
$this->assertFalse($this->xpath('//select[@id="edit-add-new-field-type"]//option[@value="hidden_test_field"]'), t("The 'add new field' select respects field types 'no_ui' property."));
$this->assertFalse($this->xpath('//select[@id="edit-add-new-field-type"]//option[@value="hidden_test_field"]'), "The 'add new field' select respects field types 'no_ui' property.");
// Create a field and an instance programmatically.
$field_name = 'hidden_test_field';
@@ -398,18 +398,18 @@ class FieldUIManageFieldsTestCase extends FieldUITestCase {
'widget' => array('type' => 'test_field_widget'),
);
field_create_instance($instance);
$this->assertTrue(field_read_instance('node', $field_name, $this->type), t('An instance of the field %field was created programmatically.', array('%field' => $field_name)));
$this->assertTrue(field_read_instance('node', $field_name, $this->type), format_string('An instance of the field %field was created programmatically.', array('%field' => $field_name)));
// Check that the newly added instance appears on the 'Manage Fields'
// screen.
$this->drupalGet($bundle_path);
$this->assertFieldByXPath('//table[@id="field-overview"]//td[1]', $instance['label'], t('Field was created and appears in the overview page.'));
$this->assertFieldByXPath('//table[@id="field-overview"]//td[1]', $instance['label'], 'Field was created and appears in the overview page.');
// Check that the instance does not appear in the 'add existing field' row
// on other bundles.
$bundle_path = 'admin/structure/types/manage/article/fields/';
$this->drupalGet($bundle_path);
$this->assertFalse($this->xpath('//select[@id="edit-add-existing-field-field-name"]//option[@value=:field_name]', array(':field_name' => $field_name)), t("The 'add existing field' select respects field types 'no_ui' property."));
$this->assertFalse($this->xpath('//select[@id="edit-add-existing-field-field-name"]//option[@value=:field_name]', array(':field_name' => $field_name)), "The 'add existing field' select respects field types 'no_ui' property.");
}
/**
@@ -445,6 +445,19 @@ class FieldUIManageFieldsTestCase extends FieldUITestCase {
$this->assertText(t('The machine-readable name is already in use. It must be unique.'));
$this->assertUrl($url, array(), 'Stayed on the same page.');
}
/**
* Tests that external URLs in the 'destinations' query parameter are blocked.
*/
function testExternalDestinations() {
$path = 'admin/structure/types/manage/article/fields/field_tags/field-settings';
$options = array(
'query' => array('destinations' => array('http://example.com')),
);
$this->drupalPost($path, NULL, t('Save field settings'), $options);
$this->assertUrl('admin/structure/types/manage/article/fields', array(), 'Stayed on the same site.');
}
}
/**
@@ -488,8 +501,8 @@ class FieldUIManageDisplayTestCase extends FieldUITestCase {
// Display the "Manage display" screen and check that the expected formatter is
// selected.
$this->drupalGet($manage_display);
$this->assertFieldByName('fields[field_test][type]', $format, t('The expected formatter is selected.'));
$this->assertText("$setting_name: $setting_value", t('The expected summary is displayed.'));
$this->assertFieldByName('fields[field_test][type]', $format, 'The expected formatter is selected.');
$this->assertText("$setting_name: $setting_value", 'The expected summary is displayed.');
// Change the formatter and check that the summary is updated.
$edit = array('fields[field_test][type]' => 'field_test_multiple', 'refresh_rows' => 'field_test');
@@ -498,8 +511,8 @@ class FieldUIManageDisplayTestCase extends FieldUITestCase {
$default_settings = field_info_formatter_settings($format);
$setting_name = key($default_settings);
$setting_value = $default_settings[$setting_name];
$this->assertFieldByName('fields[field_test][type]', $format, t('The expected formatter is selected.'));
$this->assertText("$setting_name: $setting_value", t('The expected summary is displayed.'));
$this->assertFieldByName('fields[field_test][type]', $format, 'The expected formatter is selected.');
$this->assertText("$setting_name: $setting_value", 'The expected summary is displayed.');
// Submit the form and check that the instance is updated.
$this->drupalPost(NULL, array(), t('Save'));
@@ -507,8 +520,8 @@ class FieldUIManageDisplayTestCase extends FieldUITestCase {
$instance = field_info_instance('node', 'field_test', $this->type);
$current_format = $instance['display']['default']['type'];
$current_setting_value = $instance['display']['default']['settings'][$setting_name];
$this->assertEqual($current_format, $format, t('The formatter was updated.'));
$this->assertEqual($current_setting_value, $setting_value, t('The setting was updated.'));
$this->assertEqual($current_format, $format, 'The formatter was updated.');
$this->assertEqual($current_setting_value, $setting_value, 'The setting was updated.');
}
/**
@@ -540,8 +553,8 @@ class FieldUIManageDisplayTestCase extends FieldUITestCase {
// Check that the field is displayed with the default formatter in 'rss'
// mode (uses 'default'), and hidden in 'teaser' mode (uses custom settings).
$this->assertNodeViewText($node, 'rss', $output['field_test_default'], t("The field is displayed as expected in view modes that use 'default' settings."));
$this->assertNodeViewNoText($node, 'teaser', $value, t("The field is hidden in view modes that use custom settings."));
$this->assertNodeViewText($node, 'rss', $output['field_test_default'], "The field is displayed as expected in view modes that use 'default' settings.");
$this->assertNodeViewNoText($node, 'teaser', $value, "The field is hidden in view modes that use custom settings.");
// Change fomatter for 'default' mode, check that the field is displayed
// accordingly in 'rss' mode.
@@ -549,14 +562,14 @@ class FieldUIManageDisplayTestCase extends FieldUITestCase {
'fields[field_test][type]' => 'field_test_with_prepare_view',
);
$this->drupalPost('admin/structure/types/manage/' . $this->hyphen_type . '/display', $edit, t('Save'));
$this->assertNodeViewText($node, 'rss', $output['field_test_with_prepare_view'], t("The field is displayed as expected in view modes that use 'default' settings."));
$this->assertNodeViewText($node, 'rss', $output['field_test_with_prepare_view'], "The field is displayed as expected in view modes that use 'default' settings.");
// Specialize the 'rss' mode, check that the field is displayed the same.
$edit = array(
"view_modes_custom[rss]" => TRUE,
);
$this->drupalPost('admin/structure/types/manage/' . $this->hyphen_type . '/display', $edit, t('Save'));
$this->assertNodeViewText($node, 'rss', $output['field_test_with_prepare_view'], t("The field is displayed as expected in newly specialized 'rss' mode."));
$this->assertNodeViewText($node, 'rss', $output['field_test_with_prepare_view'], "The field is displayed as expected in newly specialized 'rss' mode.");
// Set the field to 'hidden' in the view mode, check that the field is
// hidden.
@@ -564,7 +577,7 @@ class FieldUIManageDisplayTestCase extends FieldUITestCase {
'fields[field_test][type]' => 'hidden',
);
$this->drupalPost('admin/structure/types/manage/' . $this->hyphen_type . '/display/rss', $edit, t('Save'));
$this->assertNodeViewNoText($node, 'rss', $value, t("The field is hidden in 'rss' mode."));
$this->assertNodeViewNoText($node, 'rss', $value, "The field is hidden in 'rss' mode.");
// Set the view mode back to 'default', check that the field is displayed
// accordingly.
@@ -572,7 +585,7 @@ class FieldUIManageDisplayTestCase extends FieldUITestCase {
"view_modes_custom[rss]" => FALSE,
);
$this->drupalPost('admin/structure/types/manage/' . $this->hyphen_type . '/display', $edit, t('Save'));
$this->assertNodeViewText($node, 'rss', $output['field_test_with_prepare_view'], t("The field is displayed as expected when 'rss' mode is set back to 'default' settings."));
$this->assertNodeViewText($node, 'rss', $output['field_test_with_prepare_view'], "The field is displayed as expected when 'rss' mode is set back to 'default' settings.");
// Specialize the view mode again.
$edit = array(
@@ -580,7 +593,7 @@ class FieldUIManageDisplayTestCase extends FieldUITestCase {
);
$this->drupalPost('admin/structure/types/manage/' . $this->hyphen_type . '/display', $edit, t('Save'));
// Check that the previous settings for the view mode have been kept.
$this->assertNodeViewNoText($node, 'rss', $value, t("The previous settings are kept when 'rss' mode is specialized again."));
$this->assertNodeViewNoText($node, 'rss', $value, "The previous settings are kept when 'rss' mode is specialized again.");
}
/**
@@ -682,7 +695,7 @@ class FieldUIAlterTestCase extends DrupalWebTestCase {
parent::setUp(array('field_test'));
// Create test user.
$admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer users'));
$admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer users', 'administer fields'));
$this->drupalLogin($admin_user);
}
+21 -6
View File
@@ -92,6 +92,7 @@ function file_field_instance_settings_form($field, $instance) {
'#description' => t('Separate extensions with a space or comma and do not include the leading dot.'),
'#element_validate' => array('_file_generic_settings_extensions'),
'#weight' => 1,
'#maxlength' => 256,
// By making this field required, we prevent a potential security issue
// that would allow files of any type to be uploaded.
'#required' => TRUE,
@@ -186,7 +187,7 @@ function file_field_load($entity_type, $entities, $field, $instances, $langcode,
$items[$id][$delta] = NULL;
}
else {
$items[$id][$delta] = array_merge($item, (array) $files[$item['fid']]);
$items[$id][$delta] = array_merge((array) $files[$item['fid']], $item);
}
}
}
@@ -215,8 +216,16 @@ function file_field_presave($entity_type, $entity, $field, $instance, $langcode,
// Make sure that each file which will be saved with this object has a
// permanent status, so that it will not be removed when temporary files are
// cleaned up.
foreach ($items as $item) {
foreach ($items as $delta => $item) {
if (empty($item['fid'])) {
unset($items[$delta]);
continue;
}
$file = file_load($item['fid']);
if (empty($file)) {
unset($items[$delta]);
continue;
}
if (!$file->status) {
$file->status = FILE_STATUS_PERMANENT;
file_save($file);
@@ -243,6 +252,12 @@ function file_field_insert($entity_type, $entity, $field, $instance, $langcode,
* Checks for files that have been removed from the object.
*/
function file_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) {
// Check whether the field is defined on the object.
if (!isset($entity->{$field['field_name']})) {
// We cannot check for removed files if the field is not defined.
return;
}
list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
// On new revisions, all files are considered to be a new usage and no
@@ -584,7 +599,7 @@ function file_field_widget_value($element, $input = FALSE, $form_state) {
// If the display field is present make sure its unchecked value is saved.
$field = field_widget_field($element, $form_state);
if (empty($input['display'])) {
$input['display'] = $field['settings']['display_field'] ? 0 : 1;
$input['display'] = !empty($field['settings']['display_field']) ? 0 : 1;
}
}
@@ -617,7 +632,7 @@ function file_field_widget_process($element, &$form_state, $form) {
$element['#theme'] = 'file_widget';
// Add the display field if enabled.
if (!empty($field['settings']['display_field']) && $item['fid']) {
if (!empty($field['settings']['display_field'])) {
$element['display'] = array(
'#type' => empty($item['fid']) ? 'hidden' : 'checkbox',
'#title' => t('Include file in display'),
@@ -760,7 +775,7 @@ function file_field_widget_submit($form, &$form_state) {
$langcode = $element['#language'];
$parents = $element['#field_parents'];
$submitted_values = drupal_array_get_nested_value($form_state['values'], array_slice($button['#array_parents'], 0, -2));
$submitted_values = drupal_array_get_nested_value($form_state['values'], array_slice($button['#parents'], 0, -2));
foreach ($submitted_values as $delta => $submitted_value) {
if (!$submitted_value['fid']) {
unset($submitted_values[$delta]);
@@ -771,7 +786,7 @@ function file_field_widget_submit($form, &$form_state) {
$submitted_values = array_values($submitted_values);
// Update form_state values.
drupal_array_set_nested_value($form_state['values'], array_slice($button['#array_parents'], 0, -2), $submitted_values);
drupal_array_set_nested_value($form_state['values'], array_slice($button['#parents'], 0, -2), $submitted_values);
// Update items.
$field_state = field_form_get_state($parents, $field_name, $langcode, $form_state);
+3 -4
View File
@@ -6,8 +6,7 @@ core = 7.x
dependencies[] = field
files[] = tests/file.test
; Information added by drupal.org packaging script on 2013-04-03
version = "7.22"
; Information added by Drupal.org packaging script on 2020-09-16
version = "7.73"
project = "drupal"
datestamp = "1365027012"
datestamp = "1600272641"
+1 -1
View File
@@ -83,7 +83,7 @@ Drupal.file = Drupal.file || {
'%filename': this.value.replace('C:\\fakepath\\', ''),
'%extensions': extensionPattern.replace(/\|/g, ', ')
});
$(this).closest('div.form-managed-file').prepend('<div class="messages error file-upload-js-error">' + error + '</div>');
$(this).closest('div.form-managed-file').prepend('<div class="messages error file-upload-js-error" aria-live="polite">' + error + '</div>');
this.value = '';
return false;
}
+115 -19
View File
@@ -92,7 +92,7 @@ function file_theme() {
'variables' => array('file' => NULL, 'icon_directory' => NULL),
),
'file_icon' => array(
'variables' => array('file' => NULL, 'icon_directory' => NULL),
'variables' => array('file' => NULL, 'icon_directory' => NULL, 'alt' => ''),
),
'file_managed_file' => array(
'render element' => 'element',
@@ -140,14 +140,15 @@ function file_file_download($uri, $field_type = 'file') {
}
// Find out which (if any) fields of this type contain the file.
$references = file_get_file_references($file, NULL, FIELD_LOAD_CURRENT, $field_type);
$references = file_get_file_references($file, NULL, FIELD_LOAD_CURRENT, $field_type, FALSE);
// Stop processing if there are no references in order to avoid returning
// headers for files controlled by other modules. Make an exception for
// temporary files where the host entity has not yet been saved (for example,
// an image preview on a node/add form) in which case, allow download by the
// file's owner.
if (empty($references) && ($file->status == FILE_STATUS_PERMANENT || $file->uid != $user->uid)) {
// file's owner. For anonymous file owners, only the browser session that
// uploaded the file should be granted access.
if (empty($references) && ($file->status == FILE_STATUS_PERMANENT || $file->uid != $user->uid || (!$user->uid && empty($_SESSION['anonymous_allowed_file_ids'][$file->fid])))) {
return;
}
@@ -238,6 +239,9 @@ function file_ajax_upload() {
$form_parents = func_get_args();
$form_build_id = (string) array_pop($form_parents);
// Sanitize form parents before using them.
$form_parents = array_filter($form_parents, 'element_child');
if (empty($_POST['form_build_id']) || $form_build_id != $_POST['form_build_id']) {
// Invalid request.
drupal_set_message(t('An unrecoverable error occurred. The uploaded file likely exceeded the maximum file size (@size) that this server supports.', array('@size' => format_size(file_upload_max_size()))), 'error');
@@ -246,7 +250,7 @@ function file_ajax_upload() {
return array('#type' => 'ajax', '#commands' => $commands);
}
list($form, $form_state) = ajax_get_form();
list($form, $form_state, $form_id, $form_build_id, $commands) = ajax_get_form();
if (!$form) {
// Invalid form_build_id.
@@ -280,11 +284,11 @@ function file_ajax_upload() {
$form['#suffix'] .= '<span class="ajax-new-content"></span>';
}
$output = theme('status_messages') . drupal_render($form);
$form['#prefix'] .= theme('status_messages');
$output = drupal_render($form);
$js = drupal_add_js();
$settings = call_user_func_array('array_merge_recursive', $js['settings']['data']);
$settings = drupal_array_merge_deep_array($js['settings']['data']);
$commands = array();
$commands[] = ajax_command_replace(NULL, $output, $settings);
return array('#type' => 'ajax', '#commands' => $commands);
}
@@ -358,6 +362,10 @@ function file_file_delete($file) {
* support for a default value.
*/
function file_managed_file_process($element, &$form_state, $form) {
// Append the '-upload' to the #id so the field label's 'for' attribute
// corresponds with the file element.
$original_id = $element['#id'];
$element['#id'] .= '-upload';
$fid = isset($element['#value']['fid']) ? $element['#value']['fid'] : 0;
// Set some default element properties.
@@ -367,7 +375,7 @@ function file_managed_file_process($element, &$form_state, $form) {
$ajax_settings = array(
'path' => 'file/ajax/' . implode('/', $element['#array_parents']) . '/' . $form['form_build_id']['#value'],
'wrapper' => $element['#id'] . '-ajax-wrapper',
'wrapper' => $original_id . '-ajax-wrapper',
'effect' => 'fade',
'progress' => array(
'type' => $element['#progress_indicator'],
@@ -454,6 +462,17 @@ function file_managed_file_process($element, &$form_state, $form) {
'#markup' => theme('file_link', array('file' => $element['#file'])) . ' ',
'#weight' => -10,
);
// Anonymous users who have uploaded a temporary file need a
// non-session-based token added so file_managed_file_value() can check
// that they have permission to use this file on subsequent submissions of
// the same form (for example, after an Ajax upload or form validation
// error).
if (!$GLOBALS['user']->uid && $element['#file']->status != FILE_STATUS_PERMANENT) {
$element['fid_token'] = array(
'#type' => 'hidden',
'#value' => drupal_hmac_base64('file-' . $fid, drupal_get_private_key() . drupal_get_hash_salt()),
);
}
}
// Add the extension list to the page as JavaScript settings.
@@ -462,13 +481,13 @@ function file_managed_file_process($element, &$form_state, $form) {
$element['upload']['#attached']['js'] = array(
array(
'type' => 'setting',
'data' => array('file' => array('elements' => array('#' . $element['#id'] . '-upload' => $extension_list)))
'data' => array('file' => array('elements' => array('#' . $element['#id'] => $extension_list)))
)
);
}
// Prefix and suffix used for Ajax replacement.
$element['#prefix'] = '<div id="' . $element['#id'] . '-ajax-wrapper">';
$element['#prefix'] = '<div id="' . $original_id . '-ajax-wrapper">';
$element['#suffix'] = '</div>';
return $element;
@@ -479,6 +498,7 @@ function file_managed_file_process($element, &$form_state, $form) {
*/
function file_managed_file_value(&$element, $input = FALSE, $form_state = NULL) {
$fid = 0;
$force_default = FALSE;
// Find the current value of this field from the form state.
$form_state_fid = $form_state['values'];
@@ -511,15 +531,51 @@ function file_managed_file_value(&$element, $input = FALSE, $form_state = NULL)
$callback($element, $input, $form_state);
}
}
// Load file if the FID has changed to confirm it exists.
if (isset($input['fid']) && $file = file_load($input['fid'])) {
$fid = $file->fid;
// If a FID was submitted, load the file (and check access if it's not a
// public file) to confirm it exists and that the current user has access
// to it.
if (isset($input['fid']) && ($file = file_load($input['fid']))) {
// By default the public:// file scheme provided by Drupal core is the
// only one that allows files to be publicly accessible to everyone, so
// it is the only one for which the file access checks are bypassed.
// Other modules which provide publicly accessible streams of their own
// in hook_stream_wrappers() can add the corresponding scheme to the
// 'file_public_schema' variable to bypass file access checks for those
// as well. This should only be done for schemes that are completely
// publicly accessible, with no download restrictions; for security
// reasons all other schemes must go through the file_download_access()
// check.
if (!in_array(file_uri_scheme($file->uri), variable_get('file_public_schema', array('public'))) && !file_download_access($file->uri)) {
$force_default = TRUE;
}
// Temporary files that belong to other users should never be allowed.
elseif ($file->status != FILE_STATUS_PERMANENT) {
if ($GLOBALS['user']->uid && $file->uid != $GLOBALS['user']->uid) {
$force_default = TRUE;
}
// Since file ownership can't be determined for anonymous users, they
// are not allowed to reuse temporary files at all. But they do need
// to be able to reuse their own files from earlier submissions of
// the same form, so to allow that, check for the token added by
// file_managed_file_process().
elseif (!$GLOBALS['user']->uid) {
$token = drupal_array_get_nested_value($form_state['input'], array_merge($element['#parents'], array('fid_token')));
if ($token !== drupal_hmac_base64('file-' . $file->fid, drupal_get_private_key() . drupal_get_hash_salt())) {
$force_default = TRUE;
}
}
}
// If all checks pass, allow the file to be changed.
if (!$force_default) {
$fid = $file->fid;
}
}
}
}
// If there is no input, set the default value.
else {
// If there is no input or if the default value was requested above, use the
// default value.
if ($input === FALSE || $force_default) {
if ($element['#extended']) {
$default_fid = isset($element['#default_value']['fid']) ? $element['#default_value']['fid'] : 0;
$return = isset($element['#default_value']) ? $element['#default_value'] : array('fid' => 0);
@@ -725,7 +781,32 @@ function theme_file_link($variables) {
$icon_directory = $variables['icon_directory'];
$url = file_create_url($file->uri);
$icon = theme('file_icon', array('file' => $file, 'icon_directory' => $icon_directory));
// Human-readable names, for use as text-alternatives to icons.
$mime_name = array(
'application/msword' => t('Microsoft Office document icon'),
'application/vnd.ms-excel' => t('Office spreadsheet icon'),
'application/vnd.ms-powerpoint' => t('Office presentation icon'),
'application/pdf' => t('PDF icon'),
'video/quicktime' => t('Movie icon'),
'audio/mpeg' => t('Audio icon'),
'audio/wav' => t('Audio icon'),
'image/jpeg' => t('Image icon'),
'image/png' => t('Image icon'),
'image/gif' => t('Image icon'),
'application/zip' => t('Package icon'),
'text/html' => t('HTML icon'),
'text/plain' => t('Plain text icon'),
'application/octet-stream' => t('Binary Data'),
);
$mimetype = file_get_mimetype($file->uri);
$icon = theme('file_icon', array(
'file' => $file,
'icon_directory' => $icon_directory,
'alt' => !empty($mime_name[$mimetype]) ? $mime_name[$mimetype] : t('File'),
));
// Set options as per anchor format described at
// http://microformats.org/wiki/file-format-examples
@@ -755,16 +836,19 @@ function theme_file_link($variables) {
* - file: A file object for which to make an icon.
* - icon_directory: (optional) A path to a directory of icons to be used for
* files. Defaults to the value of the "file_icon_directory" variable.
* - alt: (optional) The alternative text to represent the icon in text-based
* browsers. Defaults to an empty string.
*
* @ingroup themeable
*/
function theme_file_icon($variables) {
$file = $variables['file'];
$alt = $variables['alt'];
$icon_directory = $variables['icon_directory'];
$mime = check_plain($file->filemime);
$icon_url = file_icon_url($file, $icon_directory);
return '<img class="file-icon" alt="" title="' . $mime . '" src="' . $icon_url . '" />';
return '<img class="file-icon" alt="' . check_plain($alt) . '" title="' . $mime . '" src="' . $icon_url . '" />';
}
/**
@@ -986,11 +1070,18 @@ function file_icon_map($file) {
* @param $field_type
* (optional) The name of a field type. If given, limits the reference check
* to fields of the given type.
* @param $check_access
* (optional) A boolean that specifies whether the permissions of the current
* user should be checked when retrieving references. If FALSE, all
* references to the file are returned. If TRUE, only references from
* entities that the current user has access to are returned. Defaults to
* TRUE for backwards compatibility reasons, but FALSE is recommended for
* most situations.
*
* @return
* An integer value.
*/
function file_get_file_references($file, $field = NULL, $age = FIELD_LOAD_REVISION, $field_type = 'file') {
function file_get_file_references($file, $field = NULL, $age = FIELD_LOAD_REVISION, $field_type = 'file', $check_access = TRUE) {
$references = drupal_static(__FUNCTION__, array());
$fields = isset($field) ? array($field['field_name'] => $field) : field_info_fields();
@@ -1001,6 +1092,11 @@ function file_get_file_references($file, $field = NULL, $age = FIELD_LOAD_REVISI
$query
->fieldCondition($file_field, 'fid', $file->fid)
->age($age);
if (!$check_access) {
// Neutralize the 'entity_field_access' query tag added by
// field_sql_storage_field_storage_query().
$query->addTag('DANGEROUS_ACCESS_CHECK_OPT_OUT');
}
$references[$field_name] = $query->execute();
}
}
File diff suppressed because it is too large Load Diff
+3 -4
View File
@@ -5,8 +5,7 @@ version = VERSION
core = 7.x
hidden = TRUE
; Information added by drupal.org packaging script on 2013-04-03
version = "7.22"
; Information added by Drupal.org packaging script on 2020-09-16
version = "7.73"
project = "drupal"
datestamp = "1365027012"
datestamp = "1600272641"
@@ -67,3 +67,18 @@ function file_module_test_form_submit($form, &$form_state) {
}
drupal_set_message(t('The file id is %fid.', array('%fid' => $fid)));
}
/**
* Implements hook_file_download().
*/
function file_module_test_file_download($uri) {
if (variable_get('file_module_test_grant_download_access')) {
// Mimic what file_get_content_headers() would do if we had a full $file
// object to pass to it.
return array(
'Content-Type' => mime_header_encode(file_get_mimetype($uri)),
'Content-Length' => filesize($uri),
'Cache-Control' => 'private',
);
}
}
+26 -26
View File
@@ -57,20 +57,20 @@
* - description: Additional administrative information about the filter's
* behavior, if needed for clarification.
* - settings callback: The name of a function that returns configuration form
* elements for the filter. See hook_filter_FILTER_settings() for details.
* elements for the filter. See callback_filter_settings() for details.
* - default settings: An associative array containing default settings for
* the filter, to be applied when the filter has not been configured yet.
* - prepare callback: The name of a function that escapes the content before
* the actual filtering happens. See hook_filter_FILTER_prepare() for
* the actual filtering happens. See callback_filter_prepare() for
* details.
* - process callback: (required) The name the function that performs the
* actual filtering. See hook_filter_FILTER_process() for details.
* actual filtering. See callback_filter_process() for details.
* - cache (default TRUE): Specifies whether the filtered text can be cached.
* Note that setting this to FALSE makes the entire text format not
* cacheable, which may have an impact on the site's overall performance.
* See filter_format_allowcache() for details.
* - tips callback: The name of a function that returns end-user-facing filter
* usage guidelines for the filter. See hook_filter_FILTER_tips() for
* usage guidelines for the filter. See callback_filter_tips() for
* details.
* - weight: A default weight for the filter in new text formats.
*
@@ -122,11 +122,9 @@ function hook_filter_info_alter(&$info) {
*/
/**
* Settings callback for hook_filter_info().
* Provide a settings form for filter settings.
*
* Note: This is not really a hook. The function name is manually specified via
* 'settings callback' in hook_filter_info(), with this recommended callback
* name pattern. It is called from filter_admin_format_form().
* Callback for hook_filter_info().
*
* This callback function is used to provide a settings form for filter
* settings, for filters that need settings on a per-text-format basis. This
@@ -158,8 +156,10 @@ function hook_filter_info_alter(&$info) {
* @return
* An array of form elements defining settings for the filter. Array keys
* should match the array keys in $filter->settings and $defaults.
*
* @ingroup callbacks
*/
function hook_filter_FILTER_settings($form, &$form_state, $filter, $format, $defaults, $filters) {
function callback_filter_settings($form, &$form_state, $filter, $format, $defaults, $filters) {
$filter->settings += $defaults;
$elements = array();
@@ -172,11 +172,9 @@ function hook_filter_FILTER_settings($form, &$form_state, $filter, $format, $def
}
/**
* Prepare callback for hook_filter_info().
* Provide prepared text with special characters escaped.
*
* Note: This is not really a hook. The function name is manually specified via
* 'prepare callback' in hook_filter_info(), with this recommended callback
* name pattern. It is called from check_markup().
* Callback for hook_filter_info().
*
* See hook_filter_info() for a description of the filtering process. Filters
* should not use the 'prepare callback' step for anything other than escaping,
@@ -199,19 +197,19 @@ function hook_filter_FILTER_settings($form, &$form_state, $filter, $format, $def
*
* @return
* The prepared, escaped text.
*
* @ingroup callbacks
*/
function hook_filter_FILTER_prepare($text, $filter, $format, $langcode, $cache, $cache_id) {
function callback_filter_prepare($text, $filter, $format, $langcode, $cache, $cache_id) {
// Escape <code> and </code> tags.
$text = preg_replace('|<code>(.+?)</code>|se', "[codefilter_code]$1[/codefilter_code]", $text);
$text = preg_replace('|<code>(.+?)</code>|s', "[codefilter_code]$1[/codefilter_code]", $text);
return $text;
}
/**
* Process callback for hook_filter_info().
* Provide text filtered to conform to the supplied format.
*
* Note: This is not really a hook. The function name is manually specified via
* 'process callback' in hook_filter_info(), with this recommended callback
* name pattern. It is called from check_markup().
* Callback for hook_filter_info().
*
* See hook_filter_info() for a description of the filtering process. This step
* is where the filter actually transforms the text.
@@ -232,19 +230,19 @@ function hook_filter_FILTER_prepare($text, $filter, $format, $langcode, $cache,
*
* @return
* The filtered text.
*
* @ingroup callbacks
*/
function hook_filter_FILTER_process($text, $filter, $format, $langcode, $cache, $cache_id) {
$text = preg_replace('|\[codefilter_code\](.+?)\[/codefilter_code\]|se', "<pre>$1</pre>", $text);
function callback_filter_process($text, $filter, $format, $langcode, $cache, $cache_id) {
$text = preg_replace('|\[codefilter_code\](.+?)\[/codefilter_code\]|s', "<pre>$1</pre>", $text);
return $text;
}
/**
* Tips callback for hook_filter_info().
* Return help text for a filter.
*
* Note: This is not really a hook. The function name is manually specified via
* 'tips callback' in hook_filter_info(), with this recommended callback
* name pattern. It is called from _filter_tips().
* Callback for hook_filter_info().
*
* A filter's tips should be informative and to the point. Short tips are
* preferably one-liners.
@@ -260,8 +258,10 @@ function hook_filter_FILTER_process($text, $filter, $format, $langcode, $cache,
*
* @return
* Translated text to display as a tip.
*
* @ingroup callbacks
*/
function hook_filter_FILTER_tips($filter, $format, $long) {
function callback_filter_tips($filter, $format, $long) {
if ($long) {
return t('Lines and paragraphs are automatically recognized. The &lt;br /&gt; line break, &lt;p&gt; paragraph and &lt;/p&gt; close paragraph tags are inserted automatically. If paragraphs are not recognized simply add a couple blank lines.');
}
+3 -4
View File
@@ -7,8 +7,7 @@ files[] = filter.test
required = TRUE
configure = admin/config/content/formats
; Information added by drupal.org packaging script on 2013-04-03
version = "7.22"
; Information added by Drupal.org packaging script on 2020-09-16
version = "7.73"
project = "drupal"
datestamp = "1365027012"
datestamp = "1600272641"
+56 -25
View File
@@ -93,6 +93,14 @@ function filter_menu() {
'type' => MENU_SUGGESTED_ITEM,
'file' => 'filter.pages.inc',
);
$items['filter/tips/%filter_format'] = array(
'title' => 'Compose tips',
'page callback' => 'filter_tips_long',
'page arguments' => array(2),
'access callback' => 'filter_access',
'access arguments' => array(2),
'file' => 'filter.pages.inc',
);
$items['admin/config/content/formats'] = array(
'title' => 'Text formats',
'description' => 'Configure how content input by users is filtered, including allowed HTML tags. Also allows enabling of module-provided filters.',
@@ -340,6 +348,7 @@ function filter_admin_format_title($format) {
function filter_permission() {
$perms['administer filters'] = array(
'title' => t('Administer text formats and filters'),
'description' => t('Define how text is handled by combining filters into <a href="@url">text formats</a>.', array('@url' => url('admin/config/content/formats'))),
'restrict access' => TRUE,
);
@@ -348,9 +357,7 @@ function filter_permission() {
foreach (filter_formats() as $format) {
$permission = filter_permission_name($format);
if (!empty($permission)) {
// Only link to the text format configuration page if the user who is
// viewing this will have access to that page.
$format_name_replacement = user_access('administer filters') ? l($format->name, 'admin/config/content/formats/' . $format->format) : drupal_placeholder($format->name);
$format_name_replacement = l($format->name, 'admin/config/content/formats/' . $format->format);
$perms[$permission] = array(
'title' => t("Use the !text_format text format", array('!text_format' => $format_name_replacement,)),
'description' => drupal_placeholder(t('Warning: This permission may have security implications depending on how the text format is configured.')),
@@ -739,8 +746,8 @@ function filter_list_format($format_id) {
* @param $text
* The text to be filtered.
* @param $format_id
* (optional) The format ID of the text to be filtered. If no format is
* assigned, the fallback format will be used. Defaults to NULL.
* (optional) The machine name of the filter format to be used to filter the
* text. Defaults to the fallback format. See filter_fallback_format().
* @param $langcode
* (optional) The language code of the text to be filtered, e.g. 'en' for
* English. This allows filters to be language aware so language specific
@@ -1120,18 +1127,23 @@ function filter_dom_serialize($dom_document) {
$body_node = $dom_document->getElementsByTagName('body')->item(0);
$body_content = '';
foreach ($body_node->getElementsByTagName('script') as $node) {
filter_dom_serialize_escape_cdata_element($dom_document, $node);
}
if ($body_node !== NULL) {
foreach ($body_node->getElementsByTagName('script') as $node) {
filter_dom_serialize_escape_cdata_element($dom_document, $node);
}
foreach ($body_node->getElementsByTagName('style') as $node) {
filter_dom_serialize_escape_cdata_element($dom_document, $node, '/*', '*/');
}
foreach ($body_node->getElementsByTagName('style') as $node) {
filter_dom_serialize_escape_cdata_element($dom_document, $node, '/*', '*/');
}
foreach ($body_node->childNodes as $child_node) {
$body_content .= $dom_document->saveXML($child_node);
foreach ($body_node->childNodes as $child_node) {
$body_content .= $dom_document->saveXML($child_node);
}
return preg_replace('|<([^> ]*)/>|i', '<$1 />', $body_content);
}
else {
return $body_content;
}
return preg_replace('|<([^> ]*)/>|i', '<$1 />', $body_content);
}
/**
@@ -1256,10 +1268,9 @@ function filter_filter_info() {
}
/**
* Filter settings callback for the HTML content filter.
* Implements callback_filter_settings().
*
* See hook_filter_FILTER_settings() for documentation of parameters and return
* value.
* Filter settings callback for the HTML content filter.
*/
function _filter_html_settings($form, &$form_state, $filter, $format, $defaults) {
$filter->settings += $defaults;
@@ -1285,6 +1296,8 @@ function _filter_html_settings($form, &$form_state, $filter, $format, $defaults)
}
/**
* Implements callback_filter_process().
*
* Provides filtering of input into accepted HTML.
*/
function _filter_html($text, $filter) {
@@ -1304,7 +1317,9 @@ function _filter_html($text, $filter) {
}
/**
* Filter tips callback: Provides help for the HTML filter.
* Implements callback_filter_tips().
*
* Provides help for the HTML filter.
*
* @see filter_filter_info()
*/
@@ -1404,7 +1419,9 @@ function _filter_html_tips($filter, $format, $long = FALSE) {
}
/**
* Filter URL settings callback: Provides settings for the URL filter.
* Implements callback_filter_settings().
*
* Provides settings for the URL filter.
*
* @see filter_filter_info()
*/
@@ -1425,6 +1442,8 @@ function _filter_url_settings($form, &$form_state, $filter, $format, $defaults)
}
/**
* Implements callback_filter_process().
*
* Converts text into hyperlinks automatically.
*
* This filter identifies and makes clickable three types of "links".
@@ -1454,7 +1473,7 @@ function _filter_url($text, $filter) {
// we cannot cleanly differ between protocols here without hard-coding MAILTO,
// so '//' is optional for all protocols.
// @see filter_xss_bad_protocol()
$protocols = variable_get('filter_allowed_protocols', array('http', 'https', 'ftp', 'news', 'nntp', 'telnet', 'mailto', 'irc', 'ssh', 'sftp', 'webcal', 'rtsp'));
$protocols = variable_get('filter_allowed_protocols', array('ftp', 'http', 'https', 'irc', 'mailto', 'news', 'nntp', 'rtsp', 'sftp', 'ssh', 'tel', 'telnet', 'webcal'));
$protocols = implode(':(?://)?|', $protocols) . ':(?://)?';
// Prepare domain name pattern.
@@ -1478,7 +1497,7 @@ function _filter_url($text, $filter) {
$tasks['_filter_url_parse_full_links'] = $pattern;
// Match e-mail addresses.
$url_pattern = "[A-Za-z0-9._-]{1,254}@(?:$domain)";
$url_pattern = "[A-Za-z0-9._+-]{1,254}@(?:$domain)";
$pattern = "`($url_pattern)`";
$tasks['_filter_url_parse_email_links'] = $pattern;
@@ -1619,7 +1638,7 @@ function _filter_url_escape_comments($match, $escape = NULL) {
// Replace all HTML coments with a '<!-- [hash] -->' placeholder.
if ($mode) {
$content = $match[1];
$hash = md5($content);
$hash = hash('sha256', $content);
$comments[$hash] = $content;
return "<!-- $hash -->";
}
@@ -1650,7 +1669,9 @@ function _filter_url_trim($text, $length = NULL) {
}
/**
* Filter tips callback: Provides help for the URL filter.
* Implements callback_filter_tips().
*
* Provides help for the URL filter.
*
* @see filter_filter_info()
*/
@@ -1659,6 +1680,8 @@ function _filter_url_tips($filter, $format, $long = FALSE) {
}
/**
* Implements callback_filter_process().
*
* Scans the input and makes sure that HTML tags are properly closed.
*/
function _filter_htmlcorrector($text) {
@@ -1666,6 +1689,8 @@ function _filter_htmlcorrector($text) {
}
/**
* Implements callback_filter_process().
*
* Converts line breaks into <p> and <br> in an intelligent fashion.
*
* Based on: http://photomatt.net/scripts/autop
@@ -1733,7 +1758,9 @@ function _filter_autop($text) {
}
/**
* Filter tips callback: Provides help for the auto-paragraph filter.
* Implements callback_filter_tips().
*
* Provides help for the auto-paragraph filter.
*
* @see filter_filter_info()
*/
@@ -1747,6 +1774,8 @@ function _filter_autop_tips($filter, $format, $long = FALSE) {
}
/**
* Implements callback_filter_process().
*
* Escapes all HTML tags, so they will be visible instead of being effective.
*/
function _filter_html_escape($text) {
@@ -1754,7 +1783,9 @@ function _filter_html_escape($text) {
}
/**
* Filter tips callback: Provides help for the HTML escaping filter.
* Implements callback_filter_tips().
*
* Provides help for the HTML escaping filter.
*
* @see filter_filter_info()
*/
+4 -5
View File
@@ -14,10 +14,9 @@
* @see filter_menu()
* @see theme_filter_tips()
*/
function filter_tips_long() {
$format_id = arg(2);
if ($format_id) {
$output = theme('filter_tips', array('tips' => _filter_tips($format_id, TRUE), 'long' => TRUE));
function filter_tips_long($format = NULL) {
if (!empty($format)) {
$output = theme('filter_tips', array('tips' => _filter_tips($format->format, TRUE), 'long' => TRUE));
}
else {
$output = theme('filter_tips', array('tips' => _filter_tips(-1, TRUE), 'long' => TRUE));
@@ -68,7 +67,7 @@ function theme_filter_tips($variables) {
foreach ($tips as $name => $tiplist) {
if ($multiple) {
$output .= '<div class="filter-type filter-' . drupal_html_class($name) . '">';
$output .= '<h3>' . $name . '</h3>';
$output .= '<h3>' . check_plain($name) . '</h3>';
}
if (count($tiplist) > 0) {

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