security update core+modules
This commit is contained in:
@@ -1,3 +1,6 @@
|
||||
/**
|
||||
* Right-to-Left styles for theme in the Aggregator module.
|
||||
*/
|
||||
|
||||
#aggregator .feed-source .feed-icon {
|
||||
float: left;
|
||||
|
@@ -2,11 +2,11 @@
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Admin page callbacks for the aggregator module.
|
||||
* Administration page callbacks for the Aggregator module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Menu callback; displays the aggregator administration page.
|
||||
* Page callback: Displays the Aggregator module administration page.
|
||||
*/
|
||||
function aggregator_admin_overview() {
|
||||
return aggregator_view();
|
||||
@@ -16,7 +16,7 @@ function aggregator_admin_overview() {
|
||||
* Displays the aggregator administration page.
|
||||
*
|
||||
* @return
|
||||
* The page HTML.
|
||||
* A HTML-formatted string with administration page content.
|
||||
*/
|
||||
function aggregator_view() {
|
||||
$result = db_query('SELECT f.fid, f.title, f.url, f.refresh, f.checked, f.link, f.description, f.hash, f.etag, f.modified, f.image, f.block, COUNT(i.iid) AS items FROM {aggregator_feed} f LEFT JOIN {aggregator_item} i ON f.fid = i.fid GROUP BY f.fid, f.title, f.url, f.refresh, f.checked, f.link, f.description, f.hash, f.etag, f.modified, f.image, f.block ORDER BY f.title');
|
||||
@@ -56,8 +56,8 @@ function aggregator_view() {
|
||||
* Form constructor for adding and editing feed sources.
|
||||
*
|
||||
* @param $feed
|
||||
* If editing a feed, the feed to edit as a PHP stdClass value; if adding a
|
||||
* new feed, NULL.
|
||||
* (optional) If editing a feed, the feed to edit as a PHP stdClass value; if
|
||||
* adding a new feed, NULL. Defaults to NULL.
|
||||
*
|
||||
* @ingroup forms
|
||||
* @see aggregator_form_feed_validate()
|
||||
@@ -165,6 +165,7 @@ function aggregator_form_feed_validate($form, &$form_state) {
|
||||
* Form submission handler for aggregator_form_feed().
|
||||
*
|
||||
* @see aggregator_form_feed_validate()
|
||||
*
|
||||
* @todo Add delete confirmation dialog.
|
||||
*/
|
||||
function aggregator_form_feed_submit($form, &$form_state) {
|
||||
@@ -398,7 +399,7 @@ function _aggregator_parse_opml($opml) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu callback; refreshes a feed, then redirects to the overview page.
|
||||
* Page callback: Refreshes a feed, then redirects to the overview page.
|
||||
*
|
||||
* @param $feed
|
||||
* An object describing the feed to be refreshed.
|
||||
@@ -590,6 +591,7 @@ function aggregator_form_category_validate($form, &$form_state) {
|
||||
* Form submission handler for aggregator_form_category().
|
||||
*
|
||||
* @see aggregator_form_category_validate()
|
||||
*
|
||||
* @todo Add delete confirmation dialog.
|
||||
*/
|
||||
function aggregator_form_category_submit($form, &$form_state) {
|
||||
|
@@ -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,3 +1,6 @@
|
||||
/**
|
||||
* Styles for theme in the Aggregator module.
|
||||
*/
|
||||
|
||||
#aggregator .feed-source .feed-title {
|
||||
margin-top: 0;
|
||||
|
@@ -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.
|
||||
|
@@ -6,3 +6,9 @@ core = 7.x
|
||||
files[] = aggregator.test
|
||||
configure = admin/config/services/aggregator/settings
|
||||
stylesheets[all][] = aggregator.css
|
||||
|
||||
; Information added by Drupal.org packaging script on 2015-04-02
|
||||
version = "7.36"
|
||||
project = "drupal"
|
||||
datestamp = "1427943826"
|
||||
|
||||
|
@@ -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"
|
||||
*/
|
||||
|
@@ -266,13 +266,13 @@ function aggregator_menu() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Title callback: Returns a title for aggregatory category pages.
|
||||
* Title callback: Returns a title for aggregator category pages.
|
||||
*
|
||||
* @param $category
|
||||
* An aggregator category.
|
||||
*
|
||||
* @return
|
||||
* An aggregator category title.
|
||||
* A string with the aggregator category title.
|
||||
*/
|
||||
function _aggregator_category_title($category) {
|
||||
return $category['title'];
|
||||
@@ -723,7 +723,7 @@ function theme_aggregator_block_item($variables) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely renders HTML content, as allowed.
|
||||
* Renders the HTML content safely, as allowed.
|
||||
*
|
||||
* @param $value
|
||||
* The content to be filtered.
|
||||
@@ -739,7 +739,7 @@ function aggregator_filter_xss($value) {
|
||||
* Checks and sanitizes the aggregator configuration.
|
||||
*
|
||||
* Goes through all fetchers, parsers and processors and checks whether they
|
||||
* are available. If one is missing resets to standard configuration.
|
||||
* are available. If one is missing, resets to standard configuration.
|
||||
*
|
||||
* @return
|
||||
* TRUE if this function resets the configuration; FALSE if not.
|
||||
@@ -775,7 +775,7 @@ function aggregator_sanitize_configuration() {
|
||||
* Items count.
|
||||
*
|
||||
* @return
|
||||
* Plural-formatted "@count items"
|
||||
* A string that is plural-formatted as "@count items".
|
||||
*/
|
||||
function _aggregator_items($count) {
|
||||
return format_plural($count, '1 item', '@count items');
|
||||
|
@@ -2,14 +2,14 @@
|
||||
|
||||
/**
|
||||
* @file
|
||||
* User page callbacks for the aggregator module.
|
||||
* User page callbacks for the Aggregator module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Menu callback; displays the most recent items gathered from any feed.
|
||||
* Page callback: Displays the most recent items gathered from any feed.
|
||||
*
|
||||
* @return
|
||||
* The items HTML.
|
||||
* The rendered list of items for the feed.
|
||||
*/
|
||||
function aggregator_page_last() {
|
||||
drupal_add_feed('aggregator/rss', variable_get('site_name', 'Drupal') . ' ' . t('aggregator'));
|
||||
@@ -20,13 +20,15 @@ function aggregator_page_last() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu callback; displays all the items captured from a particular feed.
|
||||
* Page callback: Displays all the items captured from the particular feed.
|
||||
*
|
||||
* @param $feed
|
||||
* The feed for which to display all items.
|
||||
*
|
||||
* @return
|
||||
* The rendered list of items for a feed.
|
||||
*
|
||||
* @see aggregator_menu()
|
||||
*/
|
||||
function aggregator_page_source($feed) {
|
||||
drupal_set_title($feed->title);
|
||||
@@ -40,13 +42,13 @@ function aggregator_page_source($feed) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu callback; displays a form with all items captured from a feed.
|
||||
* Page callback: Displays a form with all items captured from a feed.
|
||||
*
|
||||
* @param $feed
|
||||
* The feed for which to list all the aggregated items.
|
||||
* The feed for which to list all of the aggregated items.
|
||||
*
|
||||
* @return
|
||||
* The rendered list of items for a feed.
|
||||
* The rendered list of items for the feed.
|
||||
*
|
||||
* @see aggregator_page_source()
|
||||
*/
|
||||
@@ -55,13 +57,13 @@ function aggregator_page_source_form($form, $form_state, $feed) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu callback; displays all the items aggregated in a particular category.
|
||||
* Page callback: Displays all the items aggregated in a particular category.
|
||||
*
|
||||
* @param $category
|
||||
* The category for which to list all the aggregated items.
|
||||
* The category for which to list all of the aggregated items.
|
||||
*
|
||||
* @return
|
||||
* The rendered list of items for a category.
|
||||
* The rendered list of items for the feed.
|
||||
*/
|
||||
function aggregator_page_category($category) {
|
||||
drupal_add_feed('aggregator/rss/' . $category['cid'], variable_get('site_name', 'Drupal') . ' ' . t('aggregator - @title', array('@title' => $category['title'])));
|
||||
@@ -74,13 +76,13 @@ function aggregator_page_category($category) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu callback; displays a form containing items aggregated in a category.
|
||||
* Page callback: Displays a form containing items aggregated in a category.
|
||||
*
|
||||
* @param $category
|
||||
* The category for which to list all the aggregated items.
|
||||
* The category for which to list all of the aggregated items.
|
||||
*
|
||||
* @return
|
||||
* The rendered list of items for a category.
|
||||
* The rendered list of items for the feed.
|
||||
*
|
||||
* @see aggregator_page_category()
|
||||
*/
|
||||
@@ -166,7 +168,7 @@ function aggregator_feed_items_load($type, $data = NULL) {
|
||||
* The feed source URL.
|
||||
*
|
||||
* @return
|
||||
* The rendered list of items for a feed.
|
||||
* The rendered list of items for the feed.
|
||||
*/
|
||||
function _aggregator_page_list($items, $op, $feed_source = '') {
|
||||
if (user_access('administer news feeds') && ($op == 'categorize')) {
|
||||
@@ -191,10 +193,14 @@ function _aggregator_page_list($items, $op, $feed_source = '') {
|
||||
* @param $items
|
||||
* An array of the feed items.
|
||||
* @param $feed_source
|
||||
* The feed source URL.
|
||||
* (optional) The feed source URL. Defaults to an empty string.
|
||||
*
|
||||
* @return array
|
||||
* An array of FAPI elements.
|
||||
*
|
||||
* @ingroup forms
|
||||
* @see aggregator_categorize_items_submit()
|
||||
* @see theme_aggregator_categorize_items()
|
||||
* @ingroup forms
|
||||
*/
|
||||
function aggregator_categorize_items($items, $feed_source = '') {
|
||||
$form['#submit'][] = 'aggregator_categorize_items_submit';
|
||||
@@ -334,7 +340,12 @@ function template_preprocess_aggregator_item(&$variables) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu callback; displays all the feeds used by the aggregator.
|
||||
* Page callback: Displays all the feeds used by the aggregator.
|
||||
*
|
||||
* @return
|
||||
* An HTML-formatted string.
|
||||
*
|
||||
* @see aggregator_menu()
|
||||
*/
|
||||
function aggregator_page_sources() {
|
||||
$result = db_query('SELECT f.fid, f.title, f.description, f.image, MAX(i.timestamp) AS last FROM {aggregator_feed} f LEFT JOIN {aggregator_item} i ON f.fid = i.fid GROUP BY f.fid, f.title, f.description, f.image ORDER BY last DESC, f.title');
|
||||
@@ -358,7 +369,12 @@ function aggregator_page_sources() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu callback; displays all the categories used by the aggregator.
|
||||
* Page callback: Displays all the categories used by the Aggregator module.
|
||||
*
|
||||
* @return string
|
||||
* An HTML formatted string.
|
||||
*
|
||||
* @see aggregator_menu()
|
||||
*/
|
||||
function aggregator_page_categories() {
|
||||
$result = db_query('SELECT c.cid, c.title, c.description FROM {aggregator_category} c LEFT JOIN {aggregator_category_item} ci ON c.cid = ci.cid LEFT JOIN {aggregator_item} i ON ci.iid = i.iid GROUP BY c.cid, c.title, c.description');
|
||||
@@ -380,7 +396,10 @@ function aggregator_page_categories() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu callback; generate an RSS 0.92 feed of aggregator items or categories.
|
||||
* Page callback: Generates an RSS 0.92 feed of aggregator items or categories.
|
||||
*
|
||||
* @return string
|
||||
* An HTML formatted string.
|
||||
*/
|
||||
function aggregator_page_rss() {
|
||||
$result = NULL;
|
||||
@@ -448,12 +467,14 @@ function theme_aggregator_page_rss($variables) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu callback; generates an OPML representation of all feeds.
|
||||
* Page callback: Generates an OPML representation of all feeds.
|
||||
*
|
||||
* @param $cid
|
||||
* If set, feeds are exported only from a category with this ID. Otherwise, all feeds are exported.
|
||||
* (optional) If set, feeds are exported only from a category with this ID.
|
||||
* Otherwise, all feeds are exported. Defaults to NULL.
|
||||
*
|
||||
* @return
|
||||
* The output XML.
|
||||
* An OPML formatted string.
|
||||
*/
|
||||
function aggregator_page_opml($cid = NULL) {
|
||||
if ($cid) {
|
||||
@@ -468,14 +489,12 @@ function aggregator_page_opml($cid = NULL) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the OPML page for a feed.
|
||||
* Prints the OPML page for the feed.
|
||||
*
|
||||
* @param $variables
|
||||
* An associative array containing:
|
||||
* - feeds: An array of the feeds to theme.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @ingroup themeable
|
||||
*/
|
||||
function theme_aggregator_page_opml($variables) {
|
||||
|
@@ -131,6 +131,12 @@ function aggregator_form_aggregator_admin_form_alter(&$form, $form_state) {
|
||||
*
|
||||
* Callback for drupal_map_assoc() within
|
||||
* aggregator_form_aggregator_admin_form_alter().
|
||||
*
|
||||
* @param $length
|
||||
* The desired length of teaser text, in bytes.
|
||||
*
|
||||
* @return
|
||||
* A translated string explaining the teaser string length.
|
||||
*/
|
||||
function _aggregator_characters($length) {
|
||||
return ($length == 0) ? t('Unlimited') : format_plural($length, '1 character', '@count characters');
|
||||
|
@@ -5,6 +5,9 @@
|
||||
* Tests for aggregator.module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Defines a base class for testing the Aggregator module.
|
||||
*/
|
||||
class AggregatorTestCase extends DrupalWebTestCase {
|
||||
function setUp() {
|
||||
parent::setUp('aggregator', 'aggregator_test');
|
||||
@@ -13,10 +16,15 @@ class AggregatorTestCase extends DrupalWebTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an aggregator feed (simulate form submission on admin/config/services/aggregator/add/feed).
|
||||
* Creates an aggregator feed.
|
||||
*
|
||||
* This method simulates the form submission on path
|
||||
* admin/config/services/aggregator/add/feed.
|
||||
*
|
||||
* @param $feed_url
|
||||
* If given, feed will be created with this URL, otherwise /rss.xml will be used.
|
||||
* (optional) If given, feed will be created with this URL, otherwise
|
||||
* /rss.xml will be used. Defaults to NULL.
|
||||
*
|
||||
* @return $feed
|
||||
* Full feed object if possible.
|
||||
*
|
||||
@@ -33,7 +41,7 @@ class AggregatorTestCase extends DrupalWebTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an aggregator feed.
|
||||
* Deletes an aggregator feed.
|
||||
*
|
||||
* @param $feed
|
||||
* Feed object representing the feed.
|
||||
@@ -44,10 +52,11 @@ class AggregatorTestCase extends DrupalWebTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a randomly generated feed edit array.
|
||||
* Returns a randomly generated feed edit array.
|
||||
*
|
||||
* @param $feed_url
|
||||
* If given, feed will be created with this URL, otherwise /rss.xml will be used.
|
||||
* (optional) If given, feed will be created with this URL, otherwise
|
||||
* /rss.xml will be used. Defaults to NULL.
|
||||
* @return
|
||||
* A feed array.
|
||||
*/
|
||||
@@ -68,7 +77,7 @@ class AggregatorTestCase extends DrupalWebTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the count of the randomly created feed array.
|
||||
* Returns the count of the randomly created feed array.
|
||||
*
|
||||
* @return
|
||||
* Number of feed items on default feed created by createFeed().
|
||||
@@ -80,10 +89,13 @@ class AggregatorTestCase extends DrupalWebTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Update feed items (simulate click to admin/config/services/aggregator/update/$fid).
|
||||
* Updates the feed items.
|
||||
*
|
||||
* This method simulates a click to
|
||||
* admin/config/services/aggregator/update/$fid.
|
||||
*
|
||||
* @param $feed
|
||||
* Feed object representing the feed.
|
||||
* Feed object representing the feed, passed by reference.
|
||||
* @param $expected_count
|
||||
* Expected number of feed items.
|
||||
*/
|
||||
@@ -112,7 +124,7 @@ class AggregatorTestCase extends DrupalWebTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm item removal from a feed.
|
||||
* Confirms an item removal from a feed.
|
||||
*
|
||||
* @param $feed
|
||||
* Feed object representing the feed.
|
||||
@@ -123,7 +135,7 @@ class AggregatorTestCase extends DrupalWebTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add and remove feed items and ensure that the count is zero.
|
||||
* Adds and removes feed items and ensure that the count is zero.
|
||||
*
|
||||
* @param $feed
|
||||
* Feed object representing the feed.
|
||||
@@ -140,7 +152,7 @@ class AggregatorTestCase extends DrupalWebTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Pull feed categories from aggregator_category_feed table.
|
||||
* Pulls feed categories from {aggregator_category_feed} table.
|
||||
*
|
||||
* @param $feed
|
||||
* Feed object representing the feed.
|
||||
@@ -154,7 +166,11 @@ class AggregatorTestCase extends DrupalWebTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Pull categories from aggregator_category table.
|
||||
* Pulls categories from {aggregator_category} table.
|
||||
*
|
||||
* @return
|
||||
* An associative array keyed by category ID and values are set to the
|
||||
* category names.
|
||||
*/
|
||||
function getCategories() {
|
||||
$categories = array();
|
||||
@@ -165,14 +181,14 @@ class AggregatorTestCase extends DrupalWebTestCase {
|
||||
return $categories;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the feed name and URL is unique.
|
||||
* Checks whether the feed name and URL are unique.
|
||||
*
|
||||
* @param $feed_name
|
||||
* String containing the feed name to check.
|
||||
* @param $feed_url
|
||||
* String containing the feed URL to check.
|
||||
*
|
||||
* @return
|
||||
* TRUE if feed is unique.
|
||||
*/
|
||||
@@ -182,10 +198,11 @@ class AggregatorTestCase extends DrupalWebTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a valid OPML file from an array of feeds.
|
||||
* Creates a valid OPML file from an array of feeds.
|
||||
*
|
||||
* @param $feeds
|
||||
* An array of feeds.
|
||||
*
|
||||
* @return
|
||||
* Path to valid OPML file.
|
||||
*/
|
||||
@@ -223,7 +240,7 @@ EOF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an invalid OPML file.
|
||||
* Creates an invalid OPML file.
|
||||
*
|
||||
* @return
|
||||
* Path to invalid OPML file.
|
||||
@@ -240,7 +257,7 @@ EOF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a valid but empty OPML file.
|
||||
* Creates a valid but empty OPML file.
|
||||
*
|
||||
* @return
|
||||
* Path to empty OPML file.
|
||||
@@ -271,11 +288,15 @@ 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.
|
||||
*
|
||||
* @param $count
|
||||
* (optional) The number of nodes to generate.
|
||||
* (optional) The number of nodes to generate. Defaults to five.
|
||||
*/
|
||||
function createSampleNodes($count = 5) {
|
||||
$langcode = LANGUAGE_NONE;
|
||||
@@ -290,7 +311,7 @@ EOF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests aggregator configuration settings.
|
||||
* Tests functionality of the configuration settings in the Aggregator module.
|
||||
*/
|
||||
class AggregatorConfigurationTestCase extends AggregatorTestCase {
|
||||
public static function getInfo() {
|
||||
@@ -321,6 +342,9 @@ class AggregatorConfigurationTestCase extends AggregatorTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests adding aggregator feeds.
|
||||
*/
|
||||
class AddFeedTestCase extends AggregatorTestCase {
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
@@ -331,7 +355,7 @@ class AddFeedTestCase extends AggregatorTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a feed, ensure that it is unique, check the source, and delete the feed.
|
||||
* Creates and ensures that a feed is unique, checks source, and deletes feed.
|
||||
*/
|
||||
function testAddFeed() {
|
||||
$feed = $this->createFeed();
|
||||
@@ -381,6 +405,9 @@ class AddFeedTestCase extends AggregatorTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the categorize feed functionality in the Aggregator module.
|
||||
*/
|
||||
class CategorizeFeedTestCase extends AggregatorTestCase {
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
@@ -391,7 +418,7 @@ class CategorizeFeedTestCase extends AggregatorTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a feed and make sure you can add more than one category to it.
|
||||
* Creates a feed and makes sure you can add more than one category to it.
|
||||
*/
|
||||
function testCategorizeFeed() {
|
||||
|
||||
@@ -424,6 +451,9 @@ class CategorizeFeedTestCase extends AggregatorTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests functionality of updating the feed in the Aggregator module.
|
||||
*/
|
||||
class UpdateFeedTestCase extends AggregatorTestCase {
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
@@ -434,7 +464,7 @@ class UpdateFeedTestCase extends AggregatorTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a feed and attempt to update it.
|
||||
* Creates a feed and attempts to update it.
|
||||
*/
|
||||
function testUpdateFeed() {
|
||||
$remamining_fields = array('title', 'url', '');
|
||||
@@ -466,6 +496,9 @@ class UpdateFeedTestCase extends AggregatorTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests functionality for removing feeds in the Aggregator module.
|
||||
*/
|
||||
class RemoveFeedTestCase extends AggregatorTestCase {
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
@@ -476,7 +509,7 @@ class RemoveFeedTestCase extends AggregatorTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a feed and ensure that all it services are removed.
|
||||
* Removes a feed and ensures that all of its services are removed.
|
||||
*/
|
||||
function testRemoveFeed() {
|
||||
$feed = $this->createFeed();
|
||||
@@ -494,6 +527,9 @@ class RemoveFeedTestCase extends AggregatorTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests functionality of updating a feed item in the Aggregator module.
|
||||
*/
|
||||
class UpdateFeedItemTestCase extends AggregatorTestCase {
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
@@ -504,7 +540,7 @@ class UpdateFeedItemTestCase extends AggregatorTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test running "update items" from the 'admin/config/services/aggregator' page.
|
||||
* Tests running "update items" from 'admin/config/services/aggregator' page.
|
||||
*/
|
||||
function testUpdateFeedItem() {
|
||||
$this->createSampleNodes();
|
||||
@@ -564,7 +600,7 @@ class RemoveFeedItemTestCase extends AggregatorTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test running "remove items" from the 'admin/config/services/aggregator' page.
|
||||
* Tests running "remove items" from 'admin/config/services/aggregator' page.
|
||||
*/
|
||||
function testRemoveFeedItem() {
|
||||
// Create a bunch of test feeds.
|
||||
@@ -592,6 +628,9 @@ class RemoveFeedItemTestCase extends AggregatorTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests categorization functionality in the Aggregator module.
|
||||
*/
|
||||
class CategorizeFeedItemTestCase extends AggregatorTestCase {
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
@@ -602,6 +641,8 @@ class CategorizeFeedItemTestCase extends AggregatorTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that children of a feed inherit a defined category.
|
||||
*
|
||||
* If a feed has a category, make sure that the children inherit that
|
||||
* categorization.
|
||||
*/
|
||||
@@ -649,6 +690,9 @@ class CategorizeFeedItemTestCase extends AggregatorTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests importing feeds from OPML functionality for the Aggregator module.
|
||||
*/
|
||||
class ImportOPMLTestCase extends AggregatorTestCase {
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
@@ -659,7 +703,7 @@ class ImportOPMLTestCase extends AggregatorTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Open OPML import form.
|
||||
* Opens OPML import form.
|
||||
*/
|
||||
function openImportForm() {
|
||||
db_delete('aggregator_category')->execute();
|
||||
@@ -681,7 +725,7 @@ class ImportOPMLTestCase extends AggregatorTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit form filled with invalid fields.
|
||||
* Submits form filled with invalid fields.
|
||||
*/
|
||||
function validateImportFormFields() {
|
||||
$before = db_query('SELECT COUNT(*) FROM {aggregator_feed}')->fetchField();
|
||||
@@ -707,7 +751,7 @@ class ImportOPMLTestCase extends AggregatorTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit form with invalid, empty and valid OPML files.
|
||||
* Submits form with invalid, empty, and valid OPML files.
|
||||
*/
|
||||
function submitImportForm() {
|
||||
$before = db_query('SELECT COUNT(*) FROM {aggregator_feed}')->fetchField();
|
||||
@@ -766,6 +810,9 @@ class ImportOPMLTestCase extends AggregatorTestCase {
|
||||
$this->assertTrue($category, 'Categories are correct.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the import of an OPML file.
|
||||
*/
|
||||
function testOPMLImport() {
|
||||
$this->openImportForm();
|
||||
$this->validateImportFormFields();
|
||||
@@ -773,6 +820,9 @@ class ImportOPMLTestCase extends AggregatorTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests functionality of the cron process in the Aggregator module.
|
||||
*/
|
||||
class AggregatorCronTestCase extends AggregatorTestCase {
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
@@ -783,7 +833,7 @@ class AggregatorCronTestCase extends AggregatorTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add feeds update them on cron.
|
||||
* Adds feeds and updates them via cron process.
|
||||
*/
|
||||
public function testCron() {
|
||||
// Create feed and test basic updating on cron.
|
||||
@@ -819,6 +869,9 @@ class AggregatorCronTestCase extends AggregatorTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests rendering functionality in the Aggregator module.
|
||||
*/
|
||||
class AggregatorRenderingTestCase extends AggregatorTestCase {
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
@@ -829,9 +882,9 @@ class AggregatorRenderingTestCase extends AggregatorTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a feed block to the page and checks its links.
|
||||
* Adds a feed block to the page and checks its links.
|
||||
*
|
||||
* TODO: Test the category block as well.
|
||||
* @todo Test the category block as well.
|
||||
*/
|
||||
public function testBlockLinks() {
|
||||
// Create feed.
|
||||
@@ -882,7 +935,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');
|
||||
@@ -890,7 +943,7 @@ class AggregatorRenderingTestCase extends AggregatorTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a feed and check that feed's page.
|
||||
* Creates a feed and checks that feed's page.
|
||||
*/
|
||||
public function testFeedPage() {
|
||||
// Increase the number of items published in the rss.xml feed so we have
|
||||
@@ -913,7 +966,7 @@ class AggregatorRenderingTestCase extends AggregatorTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for feed parsing.
|
||||
* Tests feed parsing in the Aggregator module.
|
||||
*/
|
||||
class FeedParserTestCase extends AggregatorTestCase {
|
||||
public static function getInfo() {
|
||||
@@ -933,7 +986,7 @@ class FeedParserTestCase extends AggregatorTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a feed that uses the RSS 0.91 format.
|
||||
* Tests a feed that uses the RSS 0.91 format.
|
||||
*/
|
||||
function testRSS091Sample() {
|
||||
$feed = $this->createFeed($this->getRSS091Sample());
|
||||
@@ -955,7 +1008,7 @@ class FeedParserTestCase extends AggregatorTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a feed that uses the Atom format.
|
||||
* Tests a feed that uses the Atom format.
|
||||
*/
|
||||
function testAtomSample() {
|
||||
$feed = $this->createFeed($this->getAtomSample());
|
||||
@@ -967,4 +1020,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" Amp&");
|
||||
}
|
||||
}
|
||||
|
@@ -4,3 +4,9 @@ package = Testing
|
||||
version = VERSION
|
||||
core = 7.x
|
||||
hidden = TRUE
|
||||
|
||||
; Information added by Drupal.org packaging script on 2015-04-02
|
||||
version = "7.36"
|
||||
project = "drupal"
|
||||
datestamp = "1427943826"
|
||||
|
||||
|
@@ -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);
|
||||
|
14
modules/aggregator/tests/aggregator_test_title_entities.xml
Normal file
14
modules/aggregator/tests/aggregator_test_title_entities.xml
Normal file
@@ -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" Amp&</title>
|
||||
<link>http://example.com/example-turns-one</link>
|
||||
<description>Some text.</description>
|
||||
</item>
|
||||
</channel>
|
||||
</rss>
|
@@ -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' => '<none>')),
|
||||
'#default_value' => isset($block->title) ? $block->title : '',
|
||||
'#weight' => -19,
|
||||
|
@@ -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
|
||||
|
@@ -5,3 +5,9 @@ version = VERSION
|
||||
core = 7.x
|
||||
files[] = block.test
|
||||
configure = admin/structure/block
|
||||
|
||||
; Information added by Drupal.org packaging script on 2015-04-02
|
||||
version = "7.36"
|
||||
project = "drupal"
|
||||
datestamp = "1427943826"
|
||||
|
||||
|
@@ -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".
|
||||
*/
|
||||
|
@@ -66,7 +66,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 +143,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 +162,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,
|
||||
@@ -401,23 +401,27 @@ function _block_rehash($theme = NULL) {
|
||||
}
|
||||
// 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) {
|
||||
@@ -456,7 +460,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;
|
||||
}
|
||||
@@ -680,6 +692,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
|
||||
@@ -740,7 +755,7 @@ function _block_load_blocks() {
|
||||
/**
|
||||
* Implements hook_block_list_alter().
|
||||
*
|
||||
* Checks the page, user role, and user-specific visibilty settings.
|
||||
* Checks the page, user role, and user-specific visibility settings.
|
||||
* Removes the block if the visibility conditions are not met.
|
||||
*/
|
||||
function block_block_list_alter(&$blocks) {
|
||||
@@ -836,26 +851,64 @@ function block_block_list_alter(&$blocks) {
|
||||
* 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);
|
||||
@@ -1013,7 +1066,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'),
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -4,3 +4,9 @@ package = Testing
|
||||
version = VERSION
|
||||
core = 7.x
|
||||
hidden = TRUE
|
||||
|
||||
; Information added by Drupal.org packaging script on 2015-04-02
|
||||
version = "7.36"
|
||||
project = "drupal"
|
||||
datestamp = "1427943826"
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -12,3 +12,9 @@ regions[header] = Header
|
||||
regions[footer] = Footer
|
||||
regions[highlighted] = Highlighted
|
||||
regions[help] = Help
|
||||
|
||||
; Information added by Drupal.org packaging script on 2015-04-02
|
||||
version = "7.36"
|
||||
project = "drupal"
|
||||
datestamp = "1427943826"
|
||||
|
||||
|
@@ -4,3 +4,9 @@ package = Core
|
||||
version = VERSION
|
||||
core = 7.x
|
||||
files[] = blog.test
|
||||
|
||||
; Information added by Drupal.org packaging script on 2015-04-02
|
||||
version = "7.36"
|
||||
project = "drupal"
|
||||
datestamp = "1427943826"
|
||||
|
||||
|
@@ -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');
|
||||
}
|
||||
}
|
||||
|
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @file
|
||||
* Right-to-Left styling for the Book module.
|
||||
*/
|
||||
|
||||
.book-navigation .menu {
|
||||
padding: 1em 3em 0 0;
|
||||
|
@@ -2,11 +2,16 @@
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Admin page callbacks for the book module.
|
||||
* Administration page callbacks for the Book module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns an administrative overview of all books.
|
||||
*
|
||||
* @return string
|
||||
* A HTML-formatted string with the administrative page content.
|
||||
*
|
||||
* @see book_menu()
|
||||
*/
|
||||
function book_admin_overview() {
|
||||
$rows = array();
|
||||
@@ -53,6 +58,8 @@ function book_admin_settings() {
|
||||
|
||||
/**
|
||||
* Form validation handler for book_admin_settings().
|
||||
*
|
||||
* @see book_admin_settings_submit()
|
||||
*/
|
||||
function book_admin_settings_validate($form, &$form_state) {
|
||||
$child_type = $form_state['values']['book_child_type'];
|
||||
@@ -149,7 +156,7 @@ function book_admin_edit_submit($form, &$form_state) {
|
||||
* @param $node
|
||||
* The node of the top-level page in the book.
|
||||
* @param $form
|
||||
* The form that is being modified.
|
||||
* The form that is being modified, passed by reference.
|
||||
*
|
||||
* @see book_admin_edit()
|
||||
*/
|
||||
@@ -184,10 +191,10 @@ function _book_admin_table($node, &$form) {
|
||||
* @param $tree
|
||||
* A subtree of the book menu hierarchy.
|
||||
* @param $form
|
||||
* The form that is being modified.
|
||||
* The form that is being modified, passed by reference.
|
||||
*
|
||||
* @return
|
||||
* The form that is being modified.
|
||||
* The modified form array.
|
||||
*
|
||||
* @see book_admin_edit()
|
||||
*/
|
||||
|
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @file
|
||||
* Styling for the Book module.
|
||||
*/
|
||||
|
||||
.book-navigation .menu {
|
||||
border-top: 1px solid #888;
|
||||
|
@@ -6,3 +6,9 @@ core = 7.x
|
||||
files[] = book.test
|
||||
configure = admin/content/book/settings
|
||||
stylesheets[all][] = book.css
|
||||
|
||||
; Information added by Drupal.org packaging script on 2015-04-02
|
||||
version = "7.36"
|
||||
project = "drupal"
|
||||
datestamp = "1427943826"
|
||||
|
||||
|
@@ -3,7 +3,6 @@
|
||||
* Javascript behaviors for the Book module.
|
||||
*/
|
||||
|
||||
|
||||
(function ($) {
|
||||
|
||||
Drupal.behaviors.bookFieldsetSummaries = {
|
||||
|
@@ -221,6 +221,9 @@ function _book_outline_remove_access($node) {
|
||||
*
|
||||
* A node can be removed from a book if it is actually in a book and it either
|
||||
* is not a top-level page or is a top-level page with no children.
|
||||
*
|
||||
* @param $node
|
||||
* The node to remove from the outline.
|
||||
*/
|
||||
function _book_node_is_removable($node) {
|
||||
return (!empty($node->book['bid']) && (($node->book['bid'] != $node->nid) || !$node->book['has_children']));
|
||||
@@ -734,7 +737,7 @@ function book_get_flat_menu($book_link) {
|
||||
* @param $tree
|
||||
* A tree of menu links in an array.
|
||||
* @param $flat
|
||||
* A flat array of the menu links from $tree.
|
||||
* A flat array of the menu links from $tree, passed by reference.
|
||||
*
|
||||
* @see book_get_flat_menu().
|
||||
*/
|
||||
@@ -1062,8 +1065,9 @@ function _book_link_defaults($nid) {
|
||||
* to the structured data but can also simply iterate over all elements and
|
||||
* render them (as in the default template).
|
||||
*
|
||||
* The $variables array contains the following elements:
|
||||
* - book_menus
|
||||
* @param $variables
|
||||
* An associative array containing the following key:
|
||||
* - book_menus
|
||||
*
|
||||
* @see book-all-books-block.tpl.php
|
||||
*/
|
||||
@@ -1079,8 +1083,9 @@ function template_preprocess_book_all_books_block(&$variables) {
|
||||
/**
|
||||
* Processes variables for book-navigation.tpl.php.
|
||||
*
|
||||
* The $variables array contains the following elements:
|
||||
* - book_link
|
||||
* @param $variables
|
||||
* An associative array containing the following key:
|
||||
* - book_link
|
||||
*
|
||||
* @see book-navigation.tpl.php
|
||||
*/
|
||||
@@ -1151,8 +1156,9 @@ function template_preprocess_book_navigation(&$variables) {
|
||||
* Reference to the table of contents array. This is modified in place, so the
|
||||
* function does not have a return value.
|
||||
* @param $exclude
|
||||
* Optional array of menu link ID values. Any link whose menu link ID is in
|
||||
* this array will be excluded (along with its children).
|
||||
* (optional) An array of menu link ID values. Any link whose menu link ID is
|
||||
* in this array will be excluded (along with its children). Defaults to an
|
||||
* empty array.
|
||||
* @param $depth_limit
|
||||
* Any link deeper than this value will be excluded (along with its children).
|
||||
*/
|
||||
@@ -1198,10 +1204,11 @@ function book_toc($bid, $depth_limit, $exclude = array()) {
|
||||
/**
|
||||
* Processes variables for book-export-html.tpl.php.
|
||||
*
|
||||
* The $variables array contains the following elements:
|
||||
* - title
|
||||
* - contents
|
||||
* - depth
|
||||
* @param $variables
|
||||
* An associative array containing the following keys:
|
||||
* - title
|
||||
* - contents
|
||||
* - depth
|
||||
*
|
||||
* @see book-export-html.tpl.php
|
||||
*/
|
||||
@@ -1261,7 +1268,8 @@ function book_export_traverse($tree, $visit_func) {
|
||||
* @param $node
|
||||
* The node that will be output.
|
||||
* @param $children
|
||||
* All the rendered child nodes within the current node.
|
||||
* (optional) All the rendered child nodes within the current node. Defaults
|
||||
* to an empty string.
|
||||
*
|
||||
* @return
|
||||
* The HTML generated for the given node.
|
||||
@@ -1280,9 +1288,10 @@ function book_node_export($node, $children = '') {
|
||||
/**
|
||||
* Processes variables for book-node-export-html.tpl.php.
|
||||
*
|
||||
* The $variables array contains the following elements:
|
||||
* - node
|
||||
* - children
|
||||
* @param $variables
|
||||
* An associative array containing the following keys:
|
||||
* - node
|
||||
* - children
|
||||
*
|
||||
* @see book-node-export-html.tpl.php
|
||||
*/
|
||||
@@ -1294,6 +1303,12 @@ function template_preprocess_book_node_export_html(&$variables) {
|
||||
|
||||
/**
|
||||
* Determine if a given node type is in the list of types allowed for books.
|
||||
*
|
||||
* @param $type
|
||||
* A node type.
|
||||
*
|
||||
* @return
|
||||
* A Boolean TRUE if the node type can be included in books; otherwise, FALSE.
|
||||
*/
|
||||
function book_type_is_allowed($type) {
|
||||
return in_array($type, variable_get('book_allowed_types', array('book')));
|
||||
@@ -1336,7 +1351,7 @@ function book_node_type_update($type) {
|
||||
*
|
||||
* @return
|
||||
* A menu link, with the link translated for rendering and data added from the
|
||||
* {book} table.
|
||||
* {book} table. FALSE if there is an error.
|
||||
*/
|
||||
function book_link_load($mlid) {
|
||||
if ($item = db_query("SELECT * FROM {menu_links} ml INNER JOIN {book} b ON b.mlid = ml.mlid LEFT JOIN {menu_router} m ON m.path = ml.router_path WHERE ml.mlid = :mlid", array(
|
||||
@@ -1360,7 +1375,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());
|
||||
|
@@ -7,6 +7,11 @@
|
||||
|
||||
/**
|
||||
* Menu callback: Prints a listing of all books.
|
||||
*
|
||||
* @return string
|
||||
* A HTML-formatted string with the listing of all books content.
|
||||
*
|
||||
* @see book_menu()
|
||||
*/
|
||||
function book_render() {
|
||||
$book_list = array();
|
||||
@@ -36,6 +41,8 @@ function book_render() {
|
||||
* @return
|
||||
* A string representing the node and its children in the book hierarchy in a
|
||||
* format determined by the $type parameter.
|
||||
*
|
||||
* @see book_menu()
|
||||
*/
|
||||
function book_export($type, $nid) {
|
||||
// Check that the node exists and that the current user has access to it.
|
||||
@@ -79,7 +86,6 @@ function book_export($type, $nid) {
|
||||
*/
|
||||
function book_export_html($nid) {
|
||||
if (user_access('access printer-friendly version')) {
|
||||
$export_data = array();
|
||||
$node = node_load($nid);
|
||||
if (isset($node->book)) {
|
||||
$tree = book_menu_subtree_data($node->book);
|
||||
@@ -100,6 +106,11 @@ function book_export_html($nid) {
|
||||
*
|
||||
* @param $node
|
||||
* The book node for which to show the outline.
|
||||
*
|
||||
* @return string
|
||||
* A HTML-formatted string with the outline form for a single node.
|
||||
*
|
||||
* @see book_menu()
|
||||
*/
|
||||
function book_outline($node) {
|
||||
drupal_set_title($node->title);
|
||||
|
@@ -5,14 +5,37 @@
|
||||
* Tests for book.module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tests the functionality of the Book module.
|
||||
*/
|
||||
class BookTestCase extends DrupalWebTestCase {
|
||||
|
||||
/**
|
||||
* A book node.
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $book;
|
||||
// $book_author is a user with permission to create and edit books.
|
||||
|
||||
/**
|
||||
* A user with permission to create and edit books.
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $book_author;
|
||||
// $web_user is a user with permission to view a book
|
||||
// and access the printer-friendly version.
|
||||
|
||||
/**
|
||||
* A user with permission to view a book and access printer-friendly version.
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $web_user;
|
||||
// $admin_user is a user with permission to create and edit books and to administer blocks.
|
||||
|
||||
/**
|
||||
* A user with permission to create and edit books and to administer blocks.
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $admin_user;
|
||||
|
||||
public static function getInfo() {
|
||||
@@ -36,7 +59,7 @@ class BookTestCase extends DrupalWebTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new book with a page hierarchy.
|
||||
* Creates a new book with a page hierarchy.
|
||||
*/
|
||||
function createBook() {
|
||||
// Create new book.
|
||||
@@ -67,7 +90,7 @@ class BookTestCase extends DrupalWebTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test book functionality through node interfaces.
|
||||
* Tests book functionality through node interfaces.
|
||||
*/
|
||||
function testBook() {
|
||||
// Create new book.
|
||||
@@ -106,18 +129,20 @@ class BookTestCase extends DrupalWebTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the outline of sub-pages; previous, up, and next; and printer friendly version.
|
||||
* Checks the outline of sub-pages; previous, up, and next.
|
||||
*
|
||||
* Also checks the printer friendly version of the outline.
|
||||
*
|
||||
* @param $node
|
||||
* Node to check.
|
||||
* @param $nodes
|
||||
* Nodes that should be in outline.
|
||||
* @param $previous
|
||||
* Previous link node.
|
||||
* (optional) Previous link node. Defaults to FALSE.
|
||||
* @param $up
|
||||
* Up link node.
|
||||
* (optional) Up link node. Defaults to FALSE.
|
||||
* @param $next
|
||||
* Next link node.
|
||||
* (optional) Next link node. Defaults to FALSE.
|
||||
* @param $breadcrumb
|
||||
* The nodes that should be displayed in the breadcrumb.
|
||||
*/
|
||||
@@ -129,23 +154,23 @@ class BookTestCase extends DrupalWebTestCase {
|
||||
|
||||
// Check outline structure.
|
||||
if ($nodes !== NULL) {
|
||||
$this->assertPattern($this->generateOutlinePattern($nodes), t('Node ' . $number . ' outline confirmed.'));
|
||||
$this->assertPattern($this->generateOutlinePattern($nodes), format_string('Node %number outline confirmed.', array('%number' => $number)));
|
||||
}
|
||||
else {
|
||||
$this->pass(t('Node ' . $number . ' doesn\'t have outline.'));
|
||||
$this->pass(format_string('Node %number does not have outline.', array('%number' => $number)));
|
||||
}
|
||||
|
||||
// Check previous, up, and next links.
|
||||
if ($previous) {
|
||||
$this->assertRaw(l('‹ ' . $previous->title, 'node/' . $previous->nid, array('attributes' => array('class' => array('page-previous'), 'title' => t('Go to previous page')))), t('Previous page link found.'));
|
||||
$this->assertRaw(l('‹ ' . $previous->title, 'node/' . $previous->nid, array('attributes' => array('class' => array('page-previous'), 'title' => t('Go to previous page')))), 'Previous page link found.');
|
||||
}
|
||||
|
||||
if ($up) {
|
||||
$this->assertRaw(l('up', 'node/' . $up->nid, array('attributes' => array('class' => array('page-up'), 'title' => t('Go to parent page')))), t('Up page link found.'));
|
||||
$this->assertRaw(l('up', 'node/' . $up->nid, array('attributes' => array('class' => array('page-up'), 'title' => t('Go to parent page')))), 'Up page link found.');
|
||||
}
|
||||
|
||||
if ($next) {
|
||||
$this->assertRaw(l($next->title . ' ›', 'node/' . $next->nid, array('attributes' => array('class' => array('page-next'), 'title' => t('Go to next page')))), t('Next page link found.'));
|
||||
$this->assertRaw(l($next->title . ' ›', 'node/' . $next->nid, array('attributes' => array('class' => array('page-next'), 'title' => t('Go to next page')))), 'Next page link found.');
|
||||
}
|
||||
|
||||
// Compute the expected breadcrumb.
|
||||
@@ -163,20 +188,24 @@ class BookTestCase extends DrupalWebTestCase {
|
||||
}
|
||||
|
||||
// Compare expected and got breadcrumbs.
|
||||
$this->assertIdentical($expected_breadcrumb, $got_breadcrumb, t('The breadcrumb is correctly displayed on the page.'));
|
||||
$this->assertIdentical($expected_breadcrumb, $got_breadcrumb, 'The breadcrumb is correctly displayed on the page.');
|
||||
|
||||
// Check printer friendly version.
|
||||
$this->drupalGet('book/export/html/' . $node->nid);
|
||||
$this->assertText($node->title, t('Printer friendly title found.'));
|
||||
$this->assertRaw(check_markup($node->body[LANGUAGE_NONE][0]['value'], $node->body[LANGUAGE_NONE][0]['format']), t('Printer friendly body found.'));
|
||||
$this->assertText($node->title, 'Printer friendly title found.');
|
||||
$this->assertRaw(check_markup($node->body[LANGUAGE_NONE][0]['value'], $node->body[LANGUAGE_NONE][0]['format']), 'Printer friendly body found.');
|
||||
|
||||
$number++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a regular expression to check for the sub-nodes in the outline.
|
||||
* Creates a regular expression to check for the sub-nodes in the outline.
|
||||
*
|
||||
* @param array $nodes Nodes to check in outline.
|
||||
* @param array $nodes
|
||||
* An array of nodes to check in outline.
|
||||
*
|
||||
* @return
|
||||
* A regular expression that locates sub-nodes of the outline.
|
||||
*/
|
||||
function generateOutlinePattern($nodes) {
|
||||
$outline = '';
|
||||
@@ -188,10 +217,12 @@ class BookTestCase extends DrupalWebTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create book node.
|
||||
* Creates a book node.
|
||||
*
|
||||
* @param integer $book_nid Book node id or set to 'new' to create new book.
|
||||
* @param integer $parent Parent book reference id.
|
||||
* @param $book_nid
|
||||
* A book node ID or set to 'new' to create a new book.
|
||||
* @param $parent
|
||||
* (optional) Parent book reference ID. Defaults to NULL.
|
||||
*/
|
||||
function createBookNode($book_nid, $parent = NULL) {
|
||||
// $number does not use drupal_static as it should not be reset
|
||||
@@ -216,7 +247,7 @@ class BookTestCase extends DrupalWebTestCase {
|
||||
|
||||
// Check to make sure the book node was created.
|
||||
$node = $this->drupalGetNodeByTitle($edit['title']);
|
||||
$this->assertNotNull(($node === FALSE ? NULL : $node), t('Book node found in database.'));
|
||||
$this->assertNotNull(($node === FALSE ? NULL : $node), 'Book node found in database.');
|
||||
$number++;
|
||||
|
||||
return $node;
|
||||
@@ -236,28 +267,28 @@ class BookTestCase extends DrupalWebTestCase {
|
||||
|
||||
// Make sure each part of the book is there.
|
||||
foreach ($nodes as $node) {
|
||||
$this->assertText($node->title, t('Node title found in printer friendly version.'));
|
||||
$this->assertRaw(check_markup($node->body[LANGUAGE_NONE][0]['value'], $node->body[LANGUAGE_NONE][0]['format']), t('Node body found in printer friendly version.'));
|
||||
$this->assertText($node->title, 'Node title found in printer friendly version.');
|
||||
$this->assertRaw(check_markup($node->body[LANGUAGE_NONE][0]['value'], $node->body[LANGUAGE_NONE][0]['format']), 'Node body found in printer friendly version.');
|
||||
}
|
||||
|
||||
// Make sure we can't export an unsupported format.
|
||||
$this->drupalGet('book/export/foobar/' . $this->book->nid);
|
||||
$this->assertResponse('404', t('Unsupported export format returned "not found".'));
|
||||
$this->assertResponse('404', 'Unsupported export format returned "not found".');
|
||||
|
||||
// Make sure we get a 404 on a not existing book node.
|
||||
$this->drupalGet('book/export/html/123');
|
||||
$this->assertResponse('404', t('Not existing book node returned "not found".'));
|
||||
$this->assertResponse('404', 'Not existing book node returned "not found".');
|
||||
|
||||
// Make sure an anonymous user cannot view printer-friendly version.
|
||||
$this->drupalLogout();
|
||||
|
||||
// Load the book and verify there is no printer-friendly version link.
|
||||
$this->drupalGet('node/' . $this->book->nid);
|
||||
$this->assertNoLink(t('Printer-friendly version'), t('Anonymous user is not shown link to printer-friendly version.'));
|
||||
$this->assertNoLink(t('Printer-friendly version'), 'Anonymous user is not shown link to printer-friendly version.');
|
||||
|
||||
// Try getting the URL directly, and verify it fails.
|
||||
$this->drupalGet('book/export/html/' . $this->book->nid);
|
||||
$this->assertResponse('403', t('Anonymous user properly forbidden.'));
|
||||
$this->assertResponse('403', 'Anonymous user properly forbidden.');
|
||||
|
||||
// Now grant anonymous users permission to view the printer-friendly
|
||||
// version and verify that node access restrictions still prevent them from
|
||||
@@ -276,30 +307,30 @@ class BookTestCase extends DrupalWebTestCase {
|
||||
// Set block title to confirm that the interface is available.
|
||||
$block_title = $this->randomName(16);
|
||||
$this->drupalPost('admin/structure/block/manage/book/navigation/configure', array('title' => $block_title), t('Save block'));
|
||||
$this->assertText(t('The block configuration has been saved.'), t('Block configuration set.'));
|
||||
$this->assertText(t('The block configuration has been saved.'), 'Block configuration set.');
|
||||
|
||||
// Set the block to a region to confirm block is available.
|
||||
$edit = array();
|
||||
$edit['blocks[book_navigation][region]'] = 'footer';
|
||||
$this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
|
||||
$this->assertText(t('The block settings have been updated.'), t('Block successfully move to footer region.'));
|
||||
$this->assertText(t('The block settings have been updated.'), 'Block successfully move to footer region.');
|
||||
|
||||
// Give anonymous users the permission 'node test view'.
|
||||
$edit = array();
|
||||
$edit[DRUPAL_ANONYMOUS_RID . '[node test view]'] = TRUE;
|
||||
$this->drupalPost('admin/people/permissions/' . DRUPAL_ANONYMOUS_RID, $edit, t('Save permissions'));
|
||||
$this->assertText(t('The changes have been saved.'), t("Permission 'node test view' successfully assigned to anonymous users."));
|
||||
$this->assertText(t('The changes have been saved.'), "Permission 'node test view' successfully assigned to anonymous users.");
|
||||
|
||||
// Test correct display of the block.
|
||||
$nodes = $this->createBook();
|
||||
$this->drupalGet('<front>');
|
||||
$this->assertText($block_title, t('Book navigation block is displayed.'));
|
||||
$this->assertText($this->book->title, t('Link to book root (@title) is displayed.', array('@title' => $nodes[0]->title)));
|
||||
$this->assertNoText($nodes[0]->title, t('No links to individual book pages are displayed.'));
|
||||
$this->assertText($block_title, 'Book navigation block is displayed.');
|
||||
$this->assertText($this->book->title, format_string('Link to book root (@title) is displayed.', array('@title' => $nodes[0]->title)));
|
||||
$this->assertNoText($nodes[0]->title, 'No links to individual book pages are displayed.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the book navigation block when an access module is enabled.
|
||||
* Tests the book navigation block when an access module is enabled.
|
||||
*/
|
||||
function testNavigationBlockOnAccessModuleEnabled() {
|
||||
$this->drupalLogin($this->admin_user);
|
||||
@@ -312,19 +343,19 @@ class BookTestCase extends DrupalWebTestCase {
|
||||
// Set block display to 'Show block only on book pages'.
|
||||
$edit['book_block_mode'] = 'book pages';
|
||||
$this->drupalPost('admin/structure/block/manage/book/navigation/configure', $edit, t('Save block'));
|
||||
$this->assertText(t('The block configuration has been saved.'), t('Block configuration set.'));
|
||||
$this->assertText(t('The block configuration has been saved.'), 'Block configuration set.');
|
||||
|
||||
// Set the block to a region to confirm block is available.
|
||||
$edit = array();
|
||||
$edit['blocks[book_navigation][region]'] = 'footer';
|
||||
$this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
|
||||
$this->assertText(t('The block settings have been updated.'), t('Block successfully move to footer region.'));
|
||||
$this->assertText(t('The block settings have been updated.'), 'Block successfully move to footer region.');
|
||||
|
||||
// Give anonymous users the permission 'node test view'.
|
||||
$edit = array();
|
||||
$edit[DRUPAL_ANONYMOUS_RID . '[node test view]'] = TRUE;
|
||||
$this->drupalPost('admin/people/permissions/' . DRUPAL_ANONYMOUS_RID, $edit, t('Save permissions'));
|
||||
$this->assertText(t('The changes have been saved.'), t('Permission \'node test view\' successfully assigned to anonymous users.'));
|
||||
$this->assertText(t('The changes have been saved.'), "Permission 'node test view' successfully assigned to anonymous users.");
|
||||
|
||||
// Create a book.
|
||||
$this->createBook();
|
||||
@@ -332,12 +363,12 @@ class BookTestCase extends DrupalWebTestCase {
|
||||
// Test correct display of the block to registered users.
|
||||
$this->drupalLogin($this->web_user);
|
||||
$this->drupalGet('node/' . $this->book->nid);
|
||||
$this->assertText($block_title, t('Book navigation block is displayed to registered users.'));
|
||||
$this->assertText($block_title, 'Book navigation block is displayed to registered users.');
|
||||
$this->drupalLogout();
|
||||
|
||||
// Test correct display of the block to anonymous users.
|
||||
$this->drupalGet('node/' . $this->book->nid);
|
||||
$this->assertText($block_title, t('Book navigation block is displayed to anonymous users.'));
|
||||
$this->assertText($block_title, 'Book navigation block is displayed to anonymous users.');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -350,10 +381,10 @@ class BookTestCase extends DrupalWebTestCase {
|
||||
|
||||
// Test access to delete top-level and child book nodes.
|
||||
$this->drupalGet('node/' . $this->book->nid . '/outline/remove');
|
||||
$this->assertResponse('403', t('Deleting top-level book node properly forbidden.'));
|
||||
$this->assertResponse('403', 'Deleting top-level book node properly forbidden.');
|
||||
$this->drupalPost('node/' . $nodes[4]->nid . '/outline/remove', $edit, t('Remove'));
|
||||
$node4 = node_load($nodes[4]->nid, NULL, TRUE);
|
||||
$this->assertTrue(empty($node4->book), t('Deleting child book node properly allowed.'));
|
||||
$this->assertTrue(empty($node4->book), 'Deleting child book node properly allowed.');
|
||||
|
||||
// Delete all child book nodes and retest top-level node deletion.
|
||||
foreach ($nodes as $node) {
|
||||
@@ -362,6 +393,6 @@ class BookTestCase extends DrupalWebTestCase {
|
||||
node_delete_multiple($nids);
|
||||
$this->drupalPost('node/' . $this->book->nid . '/outline/remove', $edit, t('Remove'));
|
||||
$node = node_load($this->book->nid, NULL, TRUE);
|
||||
$this->assertTrue(empty($node->book), t('Deleting childless top-level book node properly allowed.'));
|
||||
$this->assertTrue(empty($node->book), 'Deleting childless top-level book node properly allowed.');
|
||||
}
|
||||
}
|
||||
|
@@ -4,3 +4,9 @@ package = Core
|
||||
version = VERSION
|
||||
core = 7.x
|
||||
files[] = color.test
|
||||
|
||||
; Information added by Drupal.org packaging script on 2015-04-02
|
||||
version = "7.36"
|
||||
project = "drupal"
|
||||
datestamp = "1427943826"
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -98,13 +98,14 @@ function comment_admin_overview($form, &$form_state, $arg) {
|
||||
// Remove the first node title from the node_titles array and attach to
|
||||
// the comment.
|
||||
$comment->node_title = array_shift($node_titles);
|
||||
$comment_body = field_get_items('comment', $comment, 'comment_body');
|
||||
$options[$comment->cid] = array(
|
||||
'subject' => array(
|
||||
'data' => array(
|
||||
'#type' => 'link',
|
||||
'#title' => $comment->subject,
|
||||
'#href' => 'comment/' . $comment->cid,
|
||||
'#options' => array('attributes' => array('title' => truncate_utf8($comment->comment_body[LANGUAGE_NONE][0]['value'], 128)), 'fragment' => 'comment-' . $comment->cid),
|
||||
'#options' => array('attributes' => array('title' => truncate_utf8($comment_body[0]['value'], 128)), 'fragment' => 'comment-' . $comment->cid),
|
||||
),
|
||||
),
|
||||
'author' => theme('username', array('account' => $comment)),
|
||||
|
@@ -8,3 +8,9 @@ files[] = comment.module
|
||||
files[] = comment.test
|
||||
configure = admin/content/comment
|
||||
stylesheets[all][] = comment.css
|
||||
|
||||
; Information added by Drupal.org packaging script on 2015-04-02
|
||||
version = "7.36"
|
||||
project = "drupal"
|
||||
datestamp = "1427943826"
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
@@ -2041,7 +2045,8 @@ function comment_form($form, &$form_state, $comment) {
|
||||
|
||||
// Attach fields.
|
||||
$comment->node_type = 'comment_node_' . $node->type;
|
||||
field_attach_form('comment', $comment, $form, $form_state);
|
||||
$langcode = entity_language('comment', $comment);
|
||||
field_attach_form('comment', $comment, $form, $form_state, $langcode);
|
||||
|
||||
return $form;
|
||||
}
|
||||
@@ -2066,7 +2071,8 @@ function comment_preview($comment) {
|
||||
$node = node_load($comment->nid);
|
||||
|
||||
if (!form_get_errors()) {
|
||||
$comment->format = $comment->comment_body[LANGUAGE_NONE][0]['format'];
|
||||
$comment_body = field_get_items('comment', $comment, 'comment_body');
|
||||
$comment->format = $comment_body[0]['format'];
|
||||
// Attach the user and time information.
|
||||
if (!empty($comment->name)) {
|
||||
$account = user_load_by_name($comment->name);
|
||||
@@ -2190,7 +2196,9 @@ function comment_submit($comment) {
|
||||
// 1) Filter it into HTML
|
||||
// 2) Strip out all HTML tags
|
||||
// 3) Convert entities back to plain-text.
|
||||
$comment_body = $comment->comment_body[LANGUAGE_NONE][0];
|
||||
$field = field_info_field('comment_body');
|
||||
$langcode = field_is_translatable('comment', $field) ? entity_language('comment', $comment) : LANGUAGE_NONE;
|
||||
$comment_body = $comment->comment_body[$langcode][0];
|
||||
if (isset($comment_body['format'])) {
|
||||
$comment_text = check_markup($comment_body['value'], $comment_body['format']);
|
||||
}
|
||||
@@ -2284,15 +2292,23 @@ function template_preprocess_comment(&$variables) {
|
||||
$variables['comment'] = $comment;
|
||||
$variables['node'] = $node;
|
||||
$variables['author'] = theme('username', array('account' => $comment));
|
||||
|
||||
$variables['created'] = format_date($comment->created);
|
||||
$variables['changed'] = format_date($comment->changed);
|
||||
|
||||
// Avoid calling format_date() twice on the same timestamp.
|
||||
if ($comment->changed == $comment->created) {
|
||||
$variables['changed'] = $variables['created'];
|
||||
}
|
||||
else {
|
||||
$variables['changed'] = format_date($comment->changed);
|
||||
}
|
||||
|
||||
$variables['new'] = !empty($comment->new) ? t('new') : '';
|
||||
$variables['picture'] = theme_get_setting('toggle_comment_user_picture') ? theme('user_picture', array('account' => $comment)) : '';
|
||||
$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']);
|
||||
@@ -2591,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:
|
||||
@@ -2603,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;
|
||||
}
|
||||
|
@@ -13,7 +13,7 @@ class CommentHelperCase extends DrupalWebTestCase {
|
||||
function setUp() {
|
||||
parent::setUp('comment', 'search');
|
||||
// Create users and test node.
|
||||
$this->admin_user = $this->drupalCreateUser(array('administer content types', 'administer comments', 'administer blocks'));
|
||||
$this->admin_user = $this->drupalCreateUser(array('administer content types', 'administer comments', 'administer blocks', 'administer actions'));
|
||||
$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 +155,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 +175,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 +185,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 +201,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 +273,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 +583,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 +954,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 +1000,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 +1238,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 +1279,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 +1339,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 +1353,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 +1435,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 +1453,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
|
||||
@@ -1509,7 +1509,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 +1973,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 +2161,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.
|
||||
|
@@ -5,3 +5,9 @@ version = VERSION
|
||||
core = 7.x
|
||||
files[] = contact.test
|
||||
configure = admin/structure/contact
|
||||
|
||||
; Information added by Drupal.org packaging script on 2015-04-02
|
||||
version = "7.36"
|
||||
project = "drupal"
|
||||
datestamp = "1427943826"
|
||||
|
||||
|
@@ -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'];
|
||||
|
||||
|
@@ -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.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,7 +355,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
|
||||
|
@@ -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;
|
||||
|
@@ -4,3 +4,9 @@ package = Core
|
||||
version = VERSION
|
||||
core = 7.x
|
||||
files[] = contextual.test
|
||||
|
||||
; Information added by Drupal.org packaging script on 2015-04-02
|
||||
version = "7.36"
|
||||
project = "drupal"
|
||||
datestamp = "1427943826"
|
||||
|
||||
|
@@ -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);
|
||||
});
|
||||
|
@@ -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']);
|
||||
}
|
||||
|
@@ -6,3 +6,9 @@ version = VERSION
|
||||
files[] = dashboard.test
|
||||
dependencies[] = block
|
||||
configure = admin/dashboard/customize
|
||||
|
||||
; Information added by Drupal.org packaging script on 2015-04-02
|
||||
version = "7.36"
|
||||
project = "drupal"
|
||||
datestamp = "1427943826"
|
||||
|
||||
|
@@ -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();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@@ -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.');
|
||||
}
|
||||
}
|
||||
|
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @file
|
||||
* Right-to-Left styling for the Database Logging module.
|
||||
*/
|
||||
|
||||
.form-item-type,
|
||||
.form-item-severity {
|
||||
|
@@ -2,14 +2,19 @@
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Administrative page callbacks for the dblog module.
|
||||
* Administrative page callbacks for the Database Logging module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Menu callback; displays a listing of log messages.
|
||||
* Page callback: Displays a listing of database log messages.
|
||||
*
|
||||
* Messages are truncated at 56 chars. Full-length message could be viewed at
|
||||
* the message details page.
|
||||
* Messages are truncated at 56 chars. Full-length messages can be viewed on the
|
||||
* message details page.
|
||||
*
|
||||
* @see dblog_clear_log_form()
|
||||
* @see dblog_event()
|
||||
* @see dblog_filter_form()
|
||||
* @see dblog_menu()
|
||||
*
|
||||
* @ingroup logging_severity_levels
|
||||
*/
|
||||
@@ -81,12 +86,18 @@ function dblog_overview() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu callback; generic function to display a page of the most frequent events.
|
||||
* Page callback: Shows the most frequent log messages of a given event type.
|
||||
*
|
||||
* Messages are not truncated because events from this page have no detail view.
|
||||
* Messages are not truncated on this page because events detailed herein do not
|
||||
* have links to a detailed view.
|
||||
*
|
||||
* @param $type
|
||||
* type of dblog events to display.
|
||||
* @param string $type
|
||||
* Type of database log events to display (e.g., 'search').
|
||||
*
|
||||
* @return array
|
||||
* A build array in the format expected by drupal_render().
|
||||
*
|
||||
* @see dblog_menu()
|
||||
*/
|
||||
function dblog_top($type) {
|
||||
|
||||
@@ -127,7 +138,16 @@ function dblog_top($type) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu callback; displays details about a log message.
|
||||
* Page callback: Displays details about a specific database log message.
|
||||
*
|
||||
* @param int $id
|
||||
* Unique ID of the database log message.
|
||||
*
|
||||
* @return array|string
|
||||
* If the ID is located in the Database Logging table, a build array in the
|
||||
* format expected by drupal_render(); otherwise, an empty string.
|
||||
*
|
||||
* @see dblog_menu()
|
||||
*/
|
||||
function dblog_event($id) {
|
||||
$severity = watchdog_severity_levels();
|
||||
@@ -184,7 +204,10 @@ function dblog_event($id) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Build query for dblog administration filters based on session.
|
||||
* Builds a query for database log administration filters based on session.
|
||||
*
|
||||
* @return array
|
||||
* An associative array with keys 'where' and 'args'.
|
||||
*/
|
||||
function dblog_build_filter_query() {
|
||||
if (empty($_SESSION['dblog_overview_filter'])) {
|
||||
@@ -213,9 +236,16 @@ function dblog_build_filter_query() {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* List dblog administration filters that can be applied.
|
||||
* Creates a list of database log administration filters that can be applied.
|
||||
*
|
||||
* @return array
|
||||
* Associative array of filters. The top-level keys are used as the form
|
||||
* element names for the filters, and the values are arrays with the following
|
||||
* elements:
|
||||
* - title: Title of the filter.
|
||||
* - where: The filter condition.
|
||||
* - options: Array of options for the select list for the filter.
|
||||
*/
|
||||
function dblog_filters() {
|
||||
$filters = array();
|
||||
@@ -244,7 +274,7 @@ function dblog_filters() {
|
||||
/**
|
||||
* Returns HTML for a log message.
|
||||
*
|
||||
* @param $variables
|
||||
* @param array $variables
|
||||
* An associative array containing:
|
||||
* - event: An object with at least the message and variables properties.
|
||||
* - link: (optional) Format message as link, event->wid is required.
|
||||
@@ -274,11 +304,13 @@ function theme_dblog_message($variables) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return form for dblog administration filters.
|
||||
* Form constructor for the database logging filter form.
|
||||
*
|
||||
* @see dblog_filter_form_validate()
|
||||
* @see dblog_filter_form_submit()
|
||||
* @see dblog_overview()
|
||||
*
|
||||
* @ingroup forms
|
||||
* @see dblog_filter_form_submit()
|
||||
* @see dblog_filter_form_validate()
|
||||
*/
|
||||
function dblog_filter_form($form) {
|
||||
$filters = dblog_filters();
|
||||
@@ -316,12 +348,13 @@ function dblog_filter_form($form) {
|
||||
'#value' => t('Reset')
|
||||
);
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate result from dblog administration filter form.
|
||||
* Form validation handler for dblog_filter_form().
|
||||
*
|
||||
* @see dblog_filter_form_submit()
|
||||
*/
|
||||
function dblog_filter_form_validate($form, &$form_state) {
|
||||
if ($form_state['values']['op'] == t('Filter') && empty($form_state['values']['type']) && empty($form_state['values']['severity'])) {
|
||||
@@ -330,7 +363,9 @@ function dblog_filter_form_validate($form, &$form_state) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Process result from dblog administration filter form.
|
||||
* Form submission handler for dblog_filter_form().
|
||||
*
|
||||
* @see dblog_filter_form_validate()
|
||||
*/
|
||||
function dblog_filter_form_submit($form, &$form_state) {
|
||||
$op = $form_state['values']['op'];
|
||||
@@ -351,10 +386,10 @@ function dblog_filter_form_submit($form, &$form_state) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return form for dblog clear button.
|
||||
* Form constructor for the form that clears out the log.
|
||||
*
|
||||
* @ingroup forms
|
||||
* @see dblog_clear_log_submit()
|
||||
* @ingroup forms
|
||||
*/
|
||||
function dblog_clear_log_form($form) {
|
||||
$form['dblog_clear'] = array(
|
||||
@@ -374,7 +409,7 @@ function dblog_clear_log_form($form) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit callback: clear database with log messages.
|
||||
* Form submission handler for dblog_clear_log_form().
|
||||
*/
|
||||
function dblog_clear_log_submit() {
|
||||
$_SESSION['dblog_overview_filter'] = array();
|
||||
|
@@ -1,3 +1,8 @@
|
||||
/**
|
||||
* @file
|
||||
* Admin styles for the Database Logging module.
|
||||
*/
|
||||
|
||||
.form-item-type,
|
||||
.form-item-severity {
|
||||
float: left; /* LTR */
|
||||
|
@@ -4,3 +4,9 @@ package = Core
|
||||
version = VERSION
|
||||
core = 7.x
|
||||
files[] = dblog.test
|
||||
|
||||
; Information added by Drupal.org packaging script on 2015-04-02
|
||||
version = "7.36"
|
||||
project = "drupal"
|
||||
datestamp = "1427943826"
|
||||
|
||||
|
@@ -85,6 +85,7 @@ function dblog_schema() {
|
||||
'indexes' => array(
|
||||
'type' => array('type'),
|
||||
'uid' => array('uid'),
|
||||
'severity' => array('severity'),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -140,3 +141,19 @@ function dblog_update_7001() {
|
||||
/**
|
||||
* @} End of "addtogroup updates-6.x-to-7.x".
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup updates-7.x-extra
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Add an index to the severity column in the watchdog database table.
|
||||
*/
|
||||
function dblog_update_7002() {
|
||||
db_add_index('watchdog', 'severity', array('severity'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup updates-7.x-extra".
|
||||
*/
|
||||
|
@@ -4,9 +4,9 @@
|
||||
* @file
|
||||
* System monitoring and logging for administrators.
|
||||
*
|
||||
* The dblog module monitors your site and keeps a list of
|
||||
* recorded events containing usage and performance data, errors,
|
||||
* warnings, and similar operational information.
|
||||
* The Database Logging module monitors your site and keeps a list of recorded
|
||||
* events containing usage and performance data, errors, warnings, and similar
|
||||
* operational information.
|
||||
*
|
||||
* @see watchdog()
|
||||
*/
|
||||
@@ -96,7 +96,7 @@ function dblog_init() {
|
||||
/**
|
||||
* Implements hook_cron().
|
||||
*
|
||||
* Remove expired log messages.
|
||||
* Controls the size of the log table, paring it to 'dblog_row_limit' messages.
|
||||
*/
|
||||
function dblog_cron() {
|
||||
// Cleanup the watchdog table.
|
||||
@@ -121,6 +121,12 @@ function dblog_cron() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gathers a list of uniquely defined database log message types.
|
||||
*
|
||||
* @return array
|
||||
* List of uniquely defined database log message types.
|
||||
*/
|
||||
function _dblog_get_message_types() {
|
||||
$types = array();
|
||||
|
||||
@@ -135,7 +141,7 @@ function _dblog_get_message_types() {
|
||||
/**
|
||||
* Implements hook_watchdog().
|
||||
*
|
||||
* Note some values may be truncated for database column size restrictions.
|
||||
* Note: Some values may be truncated to meet database column size restrictions.
|
||||
*/
|
||||
function dblog_watchdog(array $log_entry) {
|
||||
Database::getConnection('default', 'default')->insert('watchdog')
|
||||
@@ -155,7 +161,7 @@ function dblog_watchdog(array $log_entry) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter().
|
||||
* Implements hook_form_FORM_ID_alter() for system_logging_settings().
|
||||
*/
|
||||
function dblog_form_system_logging_settings_alter(&$form, $form_state) {
|
||||
$form['dblog_row_limit'] = array(
|
||||
|
@@ -5,8 +5,23 @@
|
||||
* Tests for dblog.module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tests logging messages to the database.
|
||||
*/
|
||||
class DBLogTestCase extends DrupalWebTestCase {
|
||||
|
||||
/**
|
||||
* A user with some relevant administrative permissions.
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $big_user;
|
||||
|
||||
/**
|
||||
* A user without any permissions.
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $any_user;
|
||||
|
||||
public static function getInfo() {
|
||||
@@ -28,7 +43,11 @@ class DBLogTestCase extends DrupalWebTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Login users, create dblog events, and test dblog functionality through the admin and user interfaces.
|
||||
* Tests Database Logging module functionality through interfaces.
|
||||
*
|
||||
* First logs in users, then creates database log events, and finally tests
|
||||
* Database Logging module functionality through both the admin and user
|
||||
* interfaces.
|
||||
*/
|
||||
function testDBLog() {
|
||||
// Login the admin user.
|
||||
@@ -46,12 +65,13 @@ class DBLogTestCase extends DrupalWebTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify setting of the dblog row limit.
|
||||
* Verifies setting of the database log row limit.
|
||||
*
|
||||
* @param integer $count Log row limit.
|
||||
* @param int $row_limit
|
||||
* The row limit.
|
||||
*/
|
||||
private function verifyRowLimit($row_limit) {
|
||||
// Change the dblog row limit.
|
||||
// Change the database log row limit.
|
||||
$edit = array();
|
||||
$edit['dblog_row_limit'] = $row_limit;
|
||||
$this->drupalPost('admin/config/development/logging', $edit, t('Save configuration'));
|
||||
@@ -59,40 +79,42 @@ 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)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify cron applies the dblog row limit.
|
||||
* Verifies that cron correctly applies the database log row limit.
|
||||
*
|
||||
* @param integer $count Log row limit.
|
||||
* @param int $row_limit
|
||||
* The row limit.
|
||||
*/
|
||||
private function verifyCron($row_limit) {
|
||||
// Generate additional log entries.
|
||||
$this->generateLogEntries($row_limit + 10);
|
||||
// Verify dblog row count exceeds row limit.
|
||||
// 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 cron job.
|
||||
// Run a cron job.
|
||||
$this->cronRun();
|
||||
// Verify dblog row count equals row limit plus one because cron adds a record after it runs.
|
||||
// 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)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate dblog entries.
|
||||
* Generates a number of random database log events.
|
||||
*
|
||||
* @param integer $count
|
||||
* Number of log entries to generate.
|
||||
* @param $type
|
||||
* The type of watchdog entry.
|
||||
* @param $severity
|
||||
* The severity of the watchdog entry.
|
||||
* @param int $count
|
||||
* Number of watchdog entries to generate.
|
||||
* @param string $type
|
||||
* (optional) The type of watchdog entry. Defaults to 'custom'.
|
||||
* @param int $severity
|
||||
* (optional) The severity of the watchdog entry. Defaults to WATCHDOG_NOTICE.
|
||||
*/
|
||||
private function generateLogEntries($count, $type = 'custom', $severity = WATCHDOG_NOTICE) {
|
||||
global $base_root;
|
||||
@@ -119,51 +141,52 @@ class DBLogTestCase extends DrupalWebTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the logged in user has the desired access to the various dblog nodes.
|
||||
* Confirms that database log reports are displayed at the correct paths.
|
||||
*
|
||||
* @param integer $response HTTP response code.
|
||||
* @param int $response
|
||||
* (optional) HTTP response code. Defaults to 200.
|
||||
*/
|
||||
private function verifyReports($response = 200) {
|
||||
$quote = ''';
|
||||
|
||||
// View dblog help node.
|
||||
// View the database log help page.
|
||||
$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 dblog report node.
|
||||
// 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 dblog page-not-found report node.
|
||||
// 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 dblog access-denied report node.
|
||||
// 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 dblog event node.
|
||||
// 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');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify events.
|
||||
* Generates and then verifies various types of events.
|
||||
*/
|
||||
private function verifyEvents() {
|
||||
// Invoke events.
|
||||
@@ -179,14 +202,14 @@ class DBLogTestCase extends DrupalWebTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate and verify user events.
|
||||
*
|
||||
* Generates and then verifies some user events.
|
||||
*/
|
||||
private function doUser() {
|
||||
// Set user variables.
|
||||
$name = $this->randomName();
|
||||
$pass = user_password();
|
||||
// Add user using form to generate add user event (which is not triggered by drupalCreateUser).
|
||||
// Add a user using the form to generate an add user event (which is not
|
||||
// triggered by drupalCreateUser).
|
||||
$edit = array();
|
||||
$edit['name'] = $name;
|
||||
$edit['mail'] = $name . '@example.com';
|
||||
@@ -195,44 +218,46 @@ class DBLogTestCase extends DrupalWebTestCase {
|
||||
$edit['status'] = 1;
|
||||
$this->drupalPost('admin/people/create', $edit, t('Create new account'));
|
||||
$this->assertResponse(200);
|
||||
// Retrieve user object.
|
||||
// Retrieve the user object.
|
||||
$user = user_load_by_name($name);
|
||||
$this->assertTrue($user != NULL, t('User @name was loaded', array('@name' => $name)));
|
||||
$user->pass_raw = $pass; // Needed by drupalLogin.
|
||||
$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.
|
||||
$this->drupalLogin($user);
|
||||
// Logout user.
|
||||
$this->drupalLogout();
|
||||
// Fetch row ids in watchdog that relate to the user.
|
||||
// Fetch the row IDs in watchdog that relate to the user.
|
||||
$result = db_query('SELECT wid FROM {watchdog} WHERE uid = :uid', array(':uid' => $user->uid));
|
||||
foreach ($result as $row) {
|
||||
$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);
|
||||
// Delete user.
|
||||
// Delete the user created at the start of this test.
|
||||
// We need to POST here to invoke batch_process() in the internal browser.
|
||||
$this->drupalPost('user/' . $user->uid . '/cancel', array('user_cancel_method' => 'user_cancel_reassign'), t('Cancel account'));
|
||||
|
||||
// View the dblog report.
|
||||
// View the database log report.
|
||||
$this->drupalGet('admin/reports/dblog');
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Verify events were recorded.
|
||||
// Verify that the expected events were recorded.
|
||||
// Add user.
|
||||
// Default display includes name and email address; if too long then email 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]'));
|
||||
// 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)), '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);
|
||||
// Verify full message on details page.
|
||||
// Verify that the full message displays on the details page.
|
||||
$link = FALSE;
|
||||
if ($links = $this->xpath('//a[text()="' . html_entity_decode($message_text) . '"]')) {
|
||||
// Found link with the message text.
|
||||
@@ -243,27 +268,28 @@ 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);
|
||||
$this->assertResponse(404);
|
||||
// View dblog page-not-found report page.
|
||||
// View the database log page-not-found report page.
|
||||
$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]');
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate and verify node events.
|
||||
* Generates and then verifies some node events.
|
||||
*
|
||||
* @param string $type Content type.
|
||||
* @param string $type
|
||||
* A node type (e.g., 'article', 'page' or 'poll').
|
||||
*/
|
||||
private function doNode($type) {
|
||||
// Create user.
|
||||
@@ -272,61 +298,65 @@ class DBLogTestCase extends DrupalWebTestCase {
|
||||
// Login user.
|
||||
$this->drupalLogin($user);
|
||||
|
||||
// Create node using form to generate add content event (which is not triggered by drupalCreateNode).
|
||||
// Create a node using the form in order to generate an add content event
|
||||
// (which is not triggered by drupalCreateNode).
|
||||
$edit = $this->getContent($type);
|
||||
$langcode = LANGUAGE_NONE;
|
||||
$title = $edit["title"];
|
||||
$this->drupalPost('node/add/' . $type, $edit, t('Save'));
|
||||
$this->assertResponse(200);
|
||||
// Retrieve node object.
|
||||
// Retrieve the node object.
|
||||
$node = $this->drupalGetNodeByTitle($title);
|
||||
$this->assertTrue($node != NULL, t('Node @title was loaded', array('@title' => $title)));
|
||||
// Edit node.
|
||||
$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'));
|
||||
$this->assertResponse(200);
|
||||
// Delete node.
|
||||
// Delete the node.
|
||||
$this->drupalPost('node/' . $node->nid . '/delete', array(), t('Delete'));
|
||||
$this->assertResponse(200);
|
||||
// View node (to generate page not found event).
|
||||
// View the node (to generate page not found event).
|
||||
$this->drupalGet('node/' . $node->nid);
|
||||
$this->assertResponse(404);
|
||||
// View the dblog report (to generate access denied event).
|
||||
// View the database log report (to generate access denied event).
|
||||
$this->drupalGet('admin/reports/dblog');
|
||||
$this->assertResponse(403);
|
||||
|
||||
// Login the admin user.
|
||||
$this->drupalLogin($this->big_user);
|
||||
// View the dblog report.
|
||||
// View the database log report.
|
||||
$this->drupalGet('admin/reports/dblog');
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Verify events were recorded.
|
||||
// Content added.
|
||||
$this->assertLogMessage(t('@type: added %title.', array('@type' => $type, '%title' => $title)), t('DBLog event was recorded: [content added]'));
|
||||
// Content updated.
|
||||
$this->assertLogMessage(t('@type: updated %title.', array('@type' => $type, '%title' => $title)), t('DBLog event was recorded: [content updated]'));
|
||||
// Content deleted.
|
||||
$this->assertLogMessage(t('@type: deleted %title.', array('@type' => $type, '%title' => $title)), t('DBLog event was recorded: [content deleted]'));
|
||||
// Verify that node events were recorded.
|
||||
// Was node 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)), 'DBLog event was recorded: [content updated]');
|
||||
// Was node content deleted?
|
||||
$this->assertLogMessage(t('@type: deleted %title.', array('@type' => $type, '%title' => $title)), 'DBLog event was recorded: [content deleted]');
|
||||
|
||||
// View dblog access-denied report node.
|
||||
// View the database log access-denied report page.
|
||||
$this->drupalGet('admin/reports/access-denied');
|
||||
$this->assertResponse(200);
|
||||
// Access denied.
|
||||
$this->assertText(t('admin/reports/dblog'), t('DBLog event was recorded: [access denied]'));
|
||||
// Verify that the 'access denied' event was recorded.
|
||||
$this->assertText(t('admin/reports/dblog'), 'DBLog event was recorded: [access denied]');
|
||||
|
||||
// View dblog page-not-found report node.
|
||||
// View the database log page-not-found report page.
|
||||
$this->drupalGet('admin/reports/page-not-found');
|
||||
$this->assertResponse(200);
|
||||
// Page not found.
|
||||
$this->assertText(t('node/@nid', array('@nid' => $node->nid)), t('DBLog event was recorded: [page not found]'));
|
||||
// Verify that the 'page not found' event was recorded.
|
||||
$this->assertText(t('node/@nid', array('@nid' => $node->nid)), 'DBLog event was recorded: [page not found]');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create content based on content type.
|
||||
* Creates random content based on node content type.
|
||||
*
|
||||
* @param string $type Content type.
|
||||
* @return array Content.
|
||||
* @param string $type
|
||||
* Node content type (e.g., 'article').
|
||||
*
|
||||
* @return array
|
||||
* Random content needed by various node types.
|
||||
*/
|
||||
private function getContent($type) {
|
||||
$langcode = LANGUAGE_NONE;
|
||||
@@ -350,10 +380,13 @@ class DBLogTestCase extends DrupalWebTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create content update based on content type.
|
||||
* Creates random content as an update based on node content type.
|
||||
*
|
||||
* @param string $type Content type.
|
||||
* @return array Content.
|
||||
* @param string $type
|
||||
* Node content type (e.g., 'article').
|
||||
*
|
||||
* @return array
|
||||
* Random content needed by various node types.
|
||||
*/
|
||||
private function getContentUpdate($type) {
|
||||
switch ($type) {
|
||||
@@ -375,11 +408,14 @@ class DBLogTestCase extends DrupalWebTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Login an admin user, create dblog event, and test clearing dblog functionality through the admin interface.
|
||||
* Tests the addition and clearing of log events through the admin interface.
|
||||
*
|
||||
* Logs in the admin user, creates a database log event, and tests the
|
||||
* functionality of clearing the database log through the admin interface.
|
||||
*/
|
||||
protected function testDBLogAddAndClear() {
|
||||
global $base_root;
|
||||
// Get a count of how many watchdog entries there are.
|
||||
// Get a count of how many watchdog entries already exist.
|
||||
$count = db_query('SELECT COUNT(*) FROM {watchdog}')->fetchField();
|
||||
$log = array(
|
||||
'type' => 'custom',
|
||||
@@ -396,27 +432,27 @@ class DBLogTestCase extends DrupalWebTestCase {
|
||||
);
|
||||
// Add a watchdog entry.
|
||||
dblog_watchdog($log);
|
||||
// Make sure the table count has actually 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)));
|
||||
// Make sure the table count has actually been incremented.
|
||||
$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);
|
||||
// Now post to clear the db table.
|
||||
// Post in order to clear the database table.
|
||||
$this->drupalPost('admin/reports/dblog', array(), t('Clear log messages'));
|
||||
// Count rows in watchdog that previously related to the deleted user.
|
||||
// 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)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the dblog filter on admin/reports/dblog.
|
||||
* Tests the database log filter functionality at admin/reports/dblog.
|
||||
*/
|
||||
protected function testFilter() {
|
||||
$this->drupalLogin($this->big_user);
|
||||
|
||||
// Clear log to ensure that only generated entries are found.
|
||||
// Clear the log to ensure that only generated entries will be found.
|
||||
db_delete('watchdog')->execute();
|
||||
|
||||
// Generate watchdog entries.
|
||||
// Generate 9 random watchdog entries.
|
||||
$type_names = array();
|
||||
$types = array();
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
@@ -432,10 +468,10 @@ class DBLogTestCase extends DrupalWebTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
// View the dblog.
|
||||
// View the database log page.
|
||||
$this->drupalGet('admin/reports/dblog');
|
||||
|
||||
// Confirm all the entries are displayed.
|
||||
// Confirm that all the entries are displayed.
|
||||
$count = $this->getTypeCount($types);
|
||||
foreach ($types as $key => $type) {
|
||||
$this->assertEqual($count[$key], $type['count'], 'Count matched');
|
||||
@@ -461,8 +497,8 @@ class DBLogTestCase extends DrupalWebTestCase {
|
||||
$this->assertEqual(array_sum($count), $type_count, 'Count matched');
|
||||
}
|
||||
|
||||
// Set filter to match each of the three type attributes and confirm the
|
||||
// number of entries displayed.
|
||||
// Set the filter to match each of the two filter-type attributes and
|
||||
// confirm the correct number of entries are displayed.
|
||||
foreach ($types as $key => $type) {
|
||||
$edit = array(
|
||||
'type[]' => array($type['type']),
|
||||
@@ -476,14 +512,18 @@ 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');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the log entry information form the page.
|
||||
* Gets the database log event information from the browser page.
|
||||
*
|
||||
* @return
|
||||
* List of entries and their information.
|
||||
* @return array
|
||||
* List of log events where each event is an array with following keys:
|
||||
* - severity: (int) A database log severity constant.
|
||||
* - type: (string) The type of database log event.
|
||||
* - message: (string) The message for this database log event.
|
||||
* - user: (string) The user associated with this database log event.
|
||||
*/
|
||||
protected function getLogEntries() {
|
||||
$entries = array();
|
||||
@@ -502,11 +542,12 @@ class DBLogTestCase extends DrupalWebTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the count of entries per type.
|
||||
* Gets the count of database log entries by database log event type.
|
||||
*
|
||||
* @param $types
|
||||
* @param array $types
|
||||
* The type information to compare against.
|
||||
* @return
|
||||
*
|
||||
* @return array
|
||||
* The count of each type keyed by the key of the $types array.
|
||||
*/
|
||||
protected function getTypeCount(array $types) {
|
||||
@@ -524,11 +565,12 @@ class DBLogTestCase extends DrupalWebTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the watchdog severity constant corresponding to the CSS class.
|
||||
* Gets the watchdog severity constant corresponding to the CSS class.
|
||||
*
|
||||
* @param $class
|
||||
* @param string $class
|
||||
* CSS class attribute.
|
||||
* @return
|
||||
*
|
||||
* @return int|null
|
||||
* The watchdog severity constant or NULL if not found.
|
||||
*
|
||||
* @ingroup logging_severity_levels
|
||||
@@ -557,11 +599,12 @@ class DBLogTestCase extends DrupalWebTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the text contained by the element.
|
||||
* Extracts the text contained by the XHTML element.
|
||||
*
|
||||
* @param $element
|
||||
* @param SimpleXMLElement $element
|
||||
* Element to extract text from.
|
||||
* @return
|
||||
*
|
||||
* @return string
|
||||
* Extracted text.
|
||||
*/
|
||||
protected function asText(SimpleXMLElement $element) {
|
||||
@@ -572,21 +615,22 @@ class DBLogTestCase extends DrupalWebTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert messages appear on the log overview screen.
|
||||
* Confirms that a log message appears on the database log overview screen.
|
||||
*
|
||||
* This function should be used only for admin/reports/dblog page, because it
|
||||
* check for the message link text truncated to 56 characters. Other dblog
|
||||
* pages have no detail links so contains a full message text.
|
||||
* This function should only be used for the admin/reports/dblog page, because
|
||||
* it checks for the message link text truncated to 56 characters. Other log
|
||||
* pages have no detail links so they contain the full message text.
|
||||
*
|
||||
* @param $log_message
|
||||
* The message to check.
|
||||
* @param $message
|
||||
* @param string $log_message
|
||||
* The database log message to check.
|
||||
* @param string $message
|
||||
* The message to pass to simpletest.
|
||||
*/
|
||||
protected function assertLogMessage($log_message, $message) {
|
||||
$message_text = truncate_utf8(filter_xss($log_message, array()), 56, TRUE, TRUE);
|
||||
// After filter_xss() HTML entities should be converted to their characters
|
||||
// because assertLink() uses this string in xpath() to query DOM.
|
||||
// After filter_xss(), HTML entities should be converted to their character
|
||||
// equivalents because assertLink() uses this string in xpath() to query the
|
||||
// Document Object Model (DOM).
|
||||
$this->assertLink(html_entity_decode($message_text), 0, $message);
|
||||
}
|
||||
}
|
||||
|
@@ -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,
|
||||
@@ -873,7 +895,7 @@ function hook_field_widget_form(&$form, &$form_state, $field, $instance, $langco
|
||||
'#type' => $instance['widget']['type'],
|
||||
'#default_value' => isset($items[$delta]) ? $items[$delta] : '',
|
||||
);
|
||||
return $element;
|
||||
return array('value' => $element);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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".
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -1735,11 +1785,14 @@ function hook_field_storage_details_alter(&$details, $field) {
|
||||
* loaded.
|
||||
*/
|
||||
function hook_field_storage_load($entity_type, $entities, $age, $fields, $options) {
|
||||
$field_info = field_info_field_by_ids();
|
||||
$load_current = $age == FIELD_LOAD_CURRENT;
|
||||
|
||||
foreach ($fields as $field_id => $ids) {
|
||||
$field = $field_info[$field_id];
|
||||
// By the time this hook runs, the relevant field definitions have been
|
||||
// populated and cached in FieldInfo, so calling field_info_field_by_id()
|
||||
// on each field individually is more efficient than loading all fields in
|
||||
// memory upfront with field_info_field_by_ids().
|
||||
$field = field_info_field_by_id($field_id);
|
||||
$field_name = $field['field_name'];
|
||||
$table = $load_current ? _field_sql_storage_tablename($field) : _field_sql_storage_revision_tablename($field);
|
||||
|
||||
@@ -1844,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,
|
||||
@@ -2247,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.
|
||||
*
|
||||
@@ -2260,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();
|
||||
@@ -2274,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.
|
||||
*
|
||||
@@ -2340,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.
|
||||
*
|
||||
@@ -2355,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') {
|
||||
@@ -2384,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.
|
||||
@@ -2394,10 +2467,6 @@ function hook_field_widget_properties_ENTITY_TYPE_alter(&$widget, $context) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup field_storage".
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup field_crud
|
||||
* @{
|
||||
@@ -2505,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) {
|
||||
@@ -2593,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);
|
||||
@@ -2610,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')
|
||||
@@ -2631,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);
|
||||
@@ -2670,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') {
|
||||
|
@@ -283,7 +283,6 @@ function _field_invoke_multiple($op, $entity_type, $entities, &$a = NULL, &$b =
|
||||
'language' => NULL,
|
||||
);
|
||||
$options += $default_options;
|
||||
$field_info = field_info_field_by_ids();
|
||||
|
||||
$fields = array();
|
||||
$grouped_instances = array();
|
||||
@@ -307,7 +306,7 @@ function _field_invoke_multiple($op, $entity_type, $entities, &$a = NULL, &$b =
|
||||
foreach ($instances as $instance) {
|
||||
$field_id = $instance['field_id'];
|
||||
$field_name = $instance['field_name'];
|
||||
$field = $field_info[$field_id];
|
||||
$field = field_info_field_by_id($field_id);
|
||||
$function = $options['default'] ? 'field_default_' . $op : $field['module'] . '_field_' . $op;
|
||||
if (function_exists($function)) {
|
||||
// Add the field to the list of fields to invoke the hook on.
|
||||
@@ -319,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();
|
||||
@@ -555,16 +554,23 @@ function _field_invoke_get_instances($entity_type, $bundle, $options) {
|
||||
* @param $langcode
|
||||
* The language the field values are going to be entered, if no language
|
||||
* is provided the default site language will be used.
|
||||
* @param array $options
|
||||
* An associative array of additional options. See _field_invoke() for
|
||||
* details.
|
||||
*
|
||||
* @see field_form_get_state()
|
||||
* @see field_form_set_state()
|
||||
*/
|
||||
function field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode = NULL) {
|
||||
function field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode = NULL, $options = array()) {
|
||||
// Validate $options since this is a new parameter added after Drupal 7 was
|
||||
// released.
|
||||
$options = is_array($options) ? $options : array();
|
||||
|
||||
// Set #parents to 'top-level' by default.
|
||||
$form += array('#parents' => array());
|
||||
|
||||
// If no language is provided use the default site language.
|
||||
$options = array('language' => field_valid_language($langcode));
|
||||
$options['language'] = field_valid_language($langcode);
|
||||
$form += (array) _field_invoke_default('form', $entity_type, $entity, $form, $form_state, $options);
|
||||
|
||||
// Add custom weight handling.
|
||||
@@ -614,7 +620,6 @@ function field_attach_form($entity_type, $entity, &$form, &$form_state, $langcod
|
||||
* non-deleted fields are operated on.
|
||||
*/
|
||||
function field_attach_load($entity_type, $entities, $age = FIELD_LOAD_CURRENT, $options = array()) {
|
||||
$field_info = field_info_field_by_ids();
|
||||
$load_current = $age == FIELD_LOAD_CURRENT;
|
||||
|
||||
// Merge default options.
|
||||
@@ -692,7 +697,7 @@ function field_attach_load($entity_type, $entities, $age = FIELD_LOAD_CURRENT, $
|
||||
}
|
||||
// Collect the storage backend if the field has not been loaded yet.
|
||||
if (!isset($skip_fields[$field_id])) {
|
||||
$field = $field_info[$field_id];
|
||||
$field = field_info_field_by_id($field_id);
|
||||
$storages[$field['storage']['type']][$field_id][] = $load_current ? $id : $vid;
|
||||
}
|
||||
}
|
||||
@@ -709,7 +714,7 @@ function field_attach_load($entity_type, $entities, $age = FIELD_LOAD_CURRENT, $
|
||||
_field_invoke_multiple('load', $entity_type, $queried_entities, $age, $null, $options);
|
||||
|
||||
// Invoke hook_field_attach_load(): let other modules act on loading the
|
||||
// entitiy.
|
||||
// entity.
|
||||
module_invoke_all('field_attach_load', $entity_type, $queried_entities, $age, $options);
|
||||
|
||||
// Build cache data.
|
||||
@@ -769,13 +774,21 @@ function field_attach_load_revision($entity_type, $entities, $options = array())
|
||||
* If validation errors are found, a FieldValidationException is thrown. The
|
||||
* 'errors' property contains the array of errors, keyed by field name,
|
||||
* language and delta.
|
||||
* @param array $options
|
||||
* An associative array of additional options. See _field_invoke() for
|
||||
* details.
|
||||
*/
|
||||
function field_attach_validate($entity_type, $entity) {
|
||||
function field_attach_validate($entity_type, $entity, $options = array()) {
|
||||
// Validate $options since this is a new parameter added after Drupal 7 was
|
||||
// released.
|
||||
$options = is_array($options) ? $options : array();
|
||||
|
||||
$errors = array();
|
||||
// Check generic, field-type-agnostic errors first.
|
||||
_field_invoke_default('validate', $entity_type, $entity, $errors);
|
||||
$null = NULL;
|
||||
_field_invoke_default('validate', $entity_type, $entity, $errors, $null, $options);
|
||||
// Check field-type specific errors.
|
||||
_field_invoke('validate', $entity_type, $entity, $errors);
|
||||
_field_invoke('validate', $entity_type, $entity, $errors, $null, $options);
|
||||
|
||||
// Let other modules validate the entity.
|
||||
// Avoid module_invoke_all() to let $errors be taken by reference.
|
||||
@@ -817,14 +830,21 @@ function field_attach_validate($entity_type, $entity) {
|
||||
* full form structure, or a sub-element of a larger form.
|
||||
* @param $form_state
|
||||
* An associative array containing the current state of the form.
|
||||
* @param array $options
|
||||
* An associative array of additional options. See _field_invoke() for
|
||||
* details.
|
||||
*/
|
||||
function field_attach_form_validate($entity_type, $entity, $form, &$form_state) {
|
||||
function field_attach_form_validate($entity_type, $entity, $form, &$form_state, $options = array()) {
|
||||
// Validate $options since this is a new parameter added after Drupal 7 was
|
||||
// released.
|
||||
$options = is_array($options) ? $options : array();
|
||||
|
||||
// Extract field values from submitted values.
|
||||
_field_invoke_default('extract_form_values', $entity_type, $entity, $form, $form_state);
|
||||
|
||||
// Perform field_level validation.
|
||||
try {
|
||||
field_attach_validate($entity_type, $entity);
|
||||
field_attach_validate($entity_type, $entity, $options);
|
||||
}
|
||||
catch (FieldValidationException $e) {
|
||||
// Pass field-level validation errors back to widgets for accurate error
|
||||
@@ -836,7 +856,7 @@ function field_attach_form_validate($entity_type, $entity, $form, &$form_state)
|
||||
field_form_set_state($form['#parents'], $field_name, $langcode, $form_state, $field_state);
|
||||
}
|
||||
}
|
||||
_field_invoke_default('form_errors', $entity_type, $entity, $form, $form_state);
|
||||
_field_invoke_default('form_errors', $entity_type, $entity, $form, $form_state, $options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -857,12 +877,19 @@ function field_attach_form_validate($entity_type, $entity, $form, &$form_state)
|
||||
* full form structure, or a sub-element of a larger form.
|
||||
* @param $form_state
|
||||
* An associative array containing the current state of the form.
|
||||
* @param array $options
|
||||
* An associative array of additional options. See _field_invoke() for
|
||||
* details.
|
||||
*/
|
||||
function field_attach_submit($entity_type, $entity, $form, &$form_state) {
|
||||
// Extract field values from submitted values.
|
||||
_field_invoke_default('extract_form_values', $entity_type, $entity, $form, $form_state);
|
||||
function field_attach_submit($entity_type, $entity, $form, &$form_state, $options = array()) {
|
||||
// Validate $options since this is a new parameter added after Drupal 7 was
|
||||
// released.
|
||||
$options = is_array($options) ? $options : array();
|
||||
|
||||
_field_invoke_default('submit', $entity_type, $entity, $form, $form_state);
|
||||
// Extract field values from submitted values.
|
||||
_field_invoke_default('extract_form_values', $entity_type, $entity, $form, $form_state, $options);
|
||||
|
||||
_field_invoke_default('submit', $entity_type, $entity, $form, $form_state, $options);
|
||||
|
||||
// Let other modules act on submitting the entity.
|
||||
// Avoid module_invoke_all() to let $form_state be taken by reference.
|
||||
@@ -949,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
|
||||
@@ -1093,9 +1126,16 @@ function field_attach_delete_revision($entity_type, $entity) {
|
||||
* @param $langcode
|
||||
* (Optional) The language the field values are to be shown in. If no language
|
||||
* is provided the current language is used.
|
||||
* @param array $options
|
||||
* An associative array of additional options. See _field_invoke() for
|
||||
* details.
|
||||
*/
|
||||
function field_attach_prepare_view($entity_type, $entities, $view_mode, $langcode = NULL) {
|
||||
$options = array('language' => array());
|
||||
function field_attach_prepare_view($entity_type, $entities, $view_mode, $langcode = NULL, $options = array()) {
|
||||
// Validate $options since this is a new parameter added after Drupal 7 was
|
||||
// released.
|
||||
$options = is_array($options) ? $options : array();
|
||||
|
||||
$options['language'] = array();
|
||||
|
||||
// To ensure hooks are only run once per entity, only process items without
|
||||
// the _field_view_prepared flag.
|
||||
@@ -1167,14 +1207,21 @@ function field_attach_prepare_view($entity_type, $entities, $view_mode, $langcod
|
||||
* @param $langcode
|
||||
* The language the field values are to be shown in. If no language is
|
||||
* provided the current language is used.
|
||||
* @param array $options
|
||||
* An associative array of additional options. See _field_invoke() for
|
||||
* details.
|
||||
* @return
|
||||
* A renderable array for the field values.
|
||||
*/
|
||||
function field_attach_view($entity_type, $entity, $view_mode, $langcode = NULL) {
|
||||
function field_attach_view($entity_type, $entity, $view_mode, $langcode = NULL, $options = array()) {
|
||||
// Validate $options since this is a new parameter added after Drupal 7 was
|
||||
// released.
|
||||
$options = is_array($options) ? $options : array();
|
||||
|
||||
// Determine the actual language to display for each field, given the
|
||||
// languages available in the field data.
|
||||
$display_language = field_language($entity_type, $entity, NULL, $langcode);
|
||||
$options = array('language' => $display_language);
|
||||
$options['language'] = $display_language;
|
||||
|
||||
// Invoke field_default_view().
|
||||
$null = NULL;
|
||||
|
@@ -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()
|
||||
@@ -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(
|
||||
@@ -319,7 +321,11 @@ function field_read_field($field_name, $include_additional = array()) {
|
||||
* Reads in fields that match an array of conditions.
|
||||
*
|
||||
* @param array $params
|
||||
* An array of conditions to match against.
|
||||
* An array of conditions to match against. Keys are columns from the
|
||||
* 'field_config' table, values are conditions to match. Additionally,
|
||||
* conditions on the 'entity_type' and 'bundle' columns from the
|
||||
* 'field_config_instance' table are supported (select fields having an
|
||||
* instance on a given bundle).
|
||||
* @param array $include_additional
|
||||
* The default behavior of this function is to not return fields that
|
||||
* are inactive or have been deleted. Setting
|
||||
@@ -337,8 +343,21 @@ function field_read_fields($params = array(), $include_additional = array()) {
|
||||
|
||||
// Turn the conditions into a query.
|
||||
foreach ($params as $key => $value) {
|
||||
// Allow filtering on the 'entity_type' and 'bundle' columns of the
|
||||
// field_config_instance table.
|
||||
if ($key == 'entity_type' || $key == 'bundle') {
|
||||
if (empty($fci_join)) {
|
||||
$fci_join = $query->join('field_config_instance', 'fci', 'fc.id = fci.field_id');
|
||||
}
|
||||
$key = 'fci.' . $key;
|
||||
}
|
||||
else {
|
||||
$key = 'fc.' . $key;
|
||||
}
|
||||
|
||||
$query->condition($key, $value);
|
||||
}
|
||||
|
||||
if (!isset($include_additional['include_inactive']) || !$include_additional['include_inactive']) {
|
||||
$query
|
||||
->condition('fc.active', 1)
|
||||
@@ -505,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) {
|
||||
|
@@ -5,7 +5,14 @@ version = VERSION
|
||||
core = 7.x
|
||||
files[] = field.module
|
||||
files[] = field.attach.inc
|
||||
files[] = field.info.class.inc
|
||||
files[] = tests/field.test
|
||||
dependencies[] = field_sql_storage
|
||||
required = TRUE
|
||||
stylesheets[all][] = theme/field.css
|
||||
|
||||
; Information added by Drupal.org packaging script on 2015-04-02
|
||||
version = "7.36"
|
||||
project = "drupal"
|
||||
datestamp = "1427943826"
|
||||
|
||||
|
684
modules/field/field.info.class.inc
Normal file
684
modules/field/field.info.class.inc
Normal file
@@ -0,0 +1,684 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @file
|
||||
* Definition of the FieldInfo class.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides field and instance definitions for the current runtime environment.
|
||||
*
|
||||
* A FieldInfo object is created and statically persisted through the request
|
||||
* by the _field_info_field_cache() function. The object properties act as a
|
||||
* "static cache" of fields and instances definitions.
|
||||
*
|
||||
* The preferred way to access definitions is through the getBundleInstances()
|
||||
* method, which keeps cache entries per bundle, storing both fields and
|
||||
* instances for a given bundle. Fields used in multiple bundles are duplicated
|
||||
* in several cache entries, and are merged into a single list in the memory
|
||||
* cache. Cache entries are loaded for bundles as a whole, optimizing memory
|
||||
* and CPU usage for the most common pattern of iterating over all instances of
|
||||
* a bundle rather than accessing a single instance.
|
||||
*
|
||||
* The getFields() and getInstances() methods, which return all existing field
|
||||
* and instance definitions, are kept mainly for backwards compatibility, and
|
||||
* should be avoided when possible, since they load and persist in memory a
|
||||
* potentially large array of information. In many cases, the lightweight
|
||||
* getFieldMap() method should be preferred.
|
||||
*/
|
||||
class FieldInfo {
|
||||
|
||||
/**
|
||||
* Lightweight map of fields across entity types and bundles.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fieldMap;
|
||||
|
||||
/**
|
||||
* List of $field structures keyed by ID. Includes deleted fields.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fieldsById = array();
|
||||
|
||||
/**
|
||||
* Mapping of field names to the ID of the corresponding non-deleted field.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fieldIdsByName = array();
|
||||
|
||||
/**
|
||||
* Whether $fieldsById contains all field definitions or a subset.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $loadedAllFields = FALSE;
|
||||
|
||||
/**
|
||||
* Separately tracks requested field names or IDs that do not exist.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $unknownFields = array();
|
||||
|
||||
/**
|
||||
* Instance definitions by bundle.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $bundleInstances = array();
|
||||
|
||||
/**
|
||||
* Whether $bundleInstances contains all instances definitions or a subset.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $loadedAllInstances = FALSE;
|
||||
|
||||
/**
|
||||
* Separately tracks requested bundles that are empty (or do not exist).
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $emptyBundles = array();
|
||||
|
||||
/**
|
||||
* Extra fields by bundle.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $bundleExtraFields = array();
|
||||
|
||||
/**
|
||||
* Clears the "static" and persistent caches.
|
||||
*/
|
||||
public function flush() {
|
||||
$this->fieldMap = NULL;
|
||||
|
||||
$this->fieldsById = array();
|
||||
$this->fieldIdsByName = array();
|
||||
$this->loadedAllFields = FALSE;
|
||||
$this->unknownFields = array();
|
||||
|
||||
$this->bundleInstances = array();
|
||||
$this->loadedAllInstances = FALSE;
|
||||
$this->emptyBundles = array();
|
||||
|
||||
$this->bundleExtraFields = array();
|
||||
|
||||
cache_clear_all('field_info:', 'cache_field', TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects a lightweight map of fields across bundles.
|
||||
*
|
||||
* @return
|
||||
* An array keyed by field name. Each value is an array with two entries:
|
||||
* - 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.
|
||||
*/
|
||||
public function getFieldMap() {
|
||||
// Read from the "static" cache.
|
||||
if ($this->fieldMap !== NULL) {
|
||||
return $this->fieldMap;
|
||||
}
|
||||
|
||||
// Read from persistent cache.
|
||||
if ($cached = cache_get('field_info:field_map', 'cache_field')) {
|
||||
$map = $cached->data;
|
||||
|
||||
// Save in "static" cache.
|
||||
$this->fieldMap = $map;
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
$map = array();
|
||||
|
||||
$query = db_query('SELECT fc.type, fci.field_name, fci.entity_type, fci.bundle FROM {field_config_instance} fci INNER JOIN {field_config} fc ON fc.id = fci.field_id WHERE fc.active = 1 AND fc.storage_active = 1 AND fc.deleted = 0 AND fci.deleted = 0');
|
||||
foreach ($query as $row) {
|
||||
$map[$row->field_name]['bundles'][$row->entity_type][] = $row->bundle;
|
||||
$map[$row->field_name]['type'] = $row->type;
|
||||
}
|
||||
|
||||
// Save in "static" and persistent caches.
|
||||
$this->fieldMap = $map;
|
||||
if (lock_acquire('field_info:field_map')) {
|
||||
cache_set('field_info:field_map', $map, 'cache_field');
|
||||
lock_release('field_info:field_map');
|
||||
}
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all active fields, including deleted ones.
|
||||
*
|
||||
* @return
|
||||
* An array of field definitions, keyed by field ID.
|
||||
*/
|
||||
public function getFields() {
|
||||
// Read from the "static" cache.
|
||||
if ($this->loadedAllFields) {
|
||||
return $this->fieldsById;
|
||||
}
|
||||
|
||||
// Read from persistent cache.
|
||||
if ($cached = cache_get('field_info:fields', 'cache_field')) {
|
||||
$this->fieldsById = $cached->data;
|
||||
}
|
||||
else {
|
||||
// Collect and prepare fields.
|
||||
foreach (field_read_fields(array(), array('include_deleted' => TRUE)) as $field) {
|
||||
$this->fieldsById[$field['id']] = $this->prepareField($field);
|
||||
}
|
||||
|
||||
// Store in persistent cache.
|
||||
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.
|
||||
foreach ($this->fieldsById as $field) {
|
||||
if (!$field['deleted']) {
|
||||
$this->fieldIdsByName[$field['field_name']] = $field['id'];
|
||||
}
|
||||
}
|
||||
|
||||
$this->loadedAllFields = TRUE;
|
||||
|
||||
return $this->fieldsById;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all active, non-deleted instances definitions.
|
||||
*
|
||||
* @param $entity_type
|
||||
* (optional) The entity type.
|
||||
*
|
||||
* @return
|
||||
* If $entity_type is not set, all instances keyed by entity type and bundle
|
||||
* name. If $entity_type is set, all instances for that entity type, keyed
|
||||
* by bundle name.
|
||||
*/
|
||||
public function getInstances($entity_type = NULL) {
|
||||
// If the full list is not present in "static" cache yet.
|
||||
if (!$this->loadedAllInstances) {
|
||||
|
||||
// Read from persistent cache.
|
||||
if ($cached = cache_get('field_info:instances', 'cache_field')) {
|
||||
$this->bundleInstances = $cached->data;
|
||||
}
|
||||
else {
|
||||
// Collect and prepare instances.
|
||||
|
||||
// We also need to populate the static field cache, since it will not
|
||||
// be set by subsequent getBundleInstances() calls.
|
||||
$this->getFields();
|
||||
|
||||
// Initialize empty arrays for all existing entity types and bundles.
|
||||
// This is not strictly needed, but is done to preserve the behavior of
|
||||
// field_info_instances() before http://drupal.org/node/1915646.
|
||||
foreach (field_info_bundles() as $existing_entity_type => $bundles) {
|
||||
foreach ($bundles as $bundle => $bundle_info) {
|
||||
$this->bundleInstances[$existing_entity_type][$bundle] = array();
|
||||
}
|
||||
}
|
||||
|
||||
foreach (field_read_instances() as $instance) {
|
||||
$field = $this->getField($instance['field_name']);
|
||||
$instance = $this->prepareInstance($instance, $field['type']);
|
||||
$this->bundleInstances[$instance['entity_type']][$instance['bundle']][$instance['field_name']] = $instance;
|
||||
}
|
||||
|
||||
// Store in persistent cache.
|
||||
if (lock_acquire('field_info:instances')) {
|
||||
cache_set('field_info:instances', $this->bundleInstances, 'cache_field');
|
||||
lock_release('field_info:instances');
|
||||
}
|
||||
}
|
||||
|
||||
$this->loadedAllInstances = TRUE;
|
||||
}
|
||||
|
||||
if (isset($entity_type)) {
|
||||
return isset($this->bundleInstances[$entity_type]) ? $this->bundleInstances[$entity_type] : array();
|
||||
}
|
||||
else {
|
||||
return $this->bundleInstances;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a field definition from a field name.
|
||||
*
|
||||
* This method only retrieves active, non-deleted fields.
|
||||
*
|
||||
* @param $field_name
|
||||
* The field name.
|
||||
*
|
||||
* @return
|
||||
* The field definition, or NULL if no field was found.
|
||||
*/
|
||||
public function getField($field_name) {
|
||||
// Read from the "static" cache.
|
||||
if (isset($this->fieldIdsByName[$field_name])) {
|
||||
$field_id = $this->fieldIdsByName[$field_name];
|
||||
return $this->fieldsById[$field_id];
|
||||
}
|
||||
if (isset($this->unknownFields[$field_name])) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not check the (large) persistent cache, but read the definition.
|
||||
|
||||
// Cache miss: read from definition.
|
||||
if ($field = field_read_field($field_name)) {
|
||||
$field = $this->prepareField($field);
|
||||
|
||||
// Save in the "static" cache.
|
||||
$this->fieldsById[$field['id']] = $field;
|
||||
$this->fieldIdsByName[$field['field_name']] = $field['id'];
|
||||
|
||||
return $field;
|
||||
}
|
||||
else {
|
||||
$this->unknownFields[$field_name] = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a field definition from a field ID.
|
||||
*
|
||||
* This method only retrieves active fields, deleted or not.
|
||||
*
|
||||
* @param $field_id
|
||||
* The field ID.
|
||||
*
|
||||
* @return
|
||||
* The field definition, or NULL if no field was found.
|
||||
*/
|
||||
public function getFieldById($field_id) {
|
||||
// Read from the "static" cache.
|
||||
if (isset($this->fieldsById[$field_id])) {
|
||||
return $this->fieldsById[$field_id];
|
||||
}
|
||||
if (isset($this->unknownFields[$field_id])) {
|
||||
return;
|
||||
}
|
||||
|
||||
// No persistent cache, fields are only persistently cached as part of a
|
||||
// bundle.
|
||||
|
||||
// Cache miss: read from definition.
|
||||
if ($fields = field_read_fields(array('id' => $field_id), array('include_deleted' => TRUE))) {
|
||||
$field = current($fields);
|
||||
$field = $this->prepareField($field);
|
||||
|
||||
// Store in the static cache.
|
||||
$this->fieldsById[$field['id']] = $field;
|
||||
if (!$field['deleted']) {
|
||||
$this->fieldIdsByName[$field['field_name']] = $field['id'];
|
||||
}
|
||||
|
||||
return $field;
|
||||
}
|
||||
else {
|
||||
$this->unknownFields[$field_id] = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the instances for a bundle.
|
||||
*
|
||||
* The function also populates the corresponding field definitions in the
|
||||
* "static" cache.
|
||||
*
|
||||
* @param $entity_type
|
||||
* The entity type.
|
||||
* @param $bundle
|
||||
* The bundle name.
|
||||
*
|
||||
* @return
|
||||
* The array of instance definitions, keyed by field name.
|
||||
*/
|
||||
public function getBundleInstances($entity_type, $bundle) {
|
||||
// Read from the "static" cache.
|
||||
if (isset($this->bundleInstances[$entity_type][$bundle])) {
|
||||
return $this->bundleInstances[$entity_type][$bundle];
|
||||
}
|
||||
if (isset($this->emptyBundles[$entity_type][$bundle])) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// Read from the persistent cache.
|
||||
if ($cached = cache_get("field_info:bundle:$entity_type:$bundle", 'cache_field')) {
|
||||
$info = $cached->data;
|
||||
|
||||
// Extract the field definitions and save them in the "static" cache.
|
||||
foreach ($info['fields'] as $field) {
|
||||
if (!isset($this->fieldsById[$field['id']])) {
|
||||
$this->fieldsById[$field['id']] = $field;
|
||||
if (!$field['deleted']) {
|
||||
$this->fieldIdsByName[$field['field_name']] = $field['id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
unset($info['fields']);
|
||||
|
||||
// Store the instance definitions in the "static" cache'. Empty (or
|
||||
// non-existent) bundles are stored separately, so that they do not
|
||||
// pollute the global list returned by getInstances().
|
||||
if ($info['instances']) {
|
||||
$this->bundleInstances[$entity_type][$bundle] = $info['instances'];
|
||||
}
|
||||
else {
|
||||
$this->emptyBundles[$entity_type][$bundle] = TRUE;
|
||||
}
|
||||
|
||||
return $info['instances'];
|
||||
}
|
||||
|
||||
// Cache miss: collect from the definitions.
|
||||
|
||||
$instances = array();
|
||||
|
||||
// Collect the fields in the bundle.
|
||||
$params = array('entity_type' => $entity_type, 'bundle' => $bundle);
|
||||
$fields = field_read_fields($params);
|
||||
|
||||
// This iterates on non-deleted instances, so deleted fields are kept out of
|
||||
// the persistent caches.
|
||||
foreach (field_read_instances($params) as $instance) {
|
||||
$field = $fields[$instance['field_name']];
|
||||
|
||||
$instance = $this->prepareInstance($instance, $field['type']);
|
||||
$instances[$field['field_name']] = $instance;
|
||||
|
||||
// If the field is not in our global "static" list yet, add it.
|
||||
if (!isset($this->fieldsById[$field['id']])) {
|
||||
$field = $this->prepareField($field);
|
||||
|
||||
$this->fieldsById[$field['id']] = $field;
|
||||
$this->fieldIdsByName[$field['field_name']] = $field['id'];
|
||||
}
|
||||
}
|
||||
|
||||
// Store in the 'static' cache'. Empty (or non-existent) bundles are stored
|
||||
// separately, so that they do not pollute the global list returned by
|
||||
// getInstances().
|
||||
if ($instances) {
|
||||
$this->bundleInstances[$entity_type][$bundle] = $instances;
|
||||
}
|
||||
else {
|
||||
$this->emptyBundles[$entity_type][$bundle] = TRUE;
|
||||
}
|
||||
|
||||
// The persistent cache additionally contains the definitions of the fields
|
||||
// involved in the bundle.
|
||||
$cache = array(
|
||||
'instances' => $instances,
|
||||
'fields' => array()
|
||||
);
|
||||
foreach ($instances as $instance) {
|
||||
$cache['fields'][] = $this->fieldsById[$instance['field_id']];
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the "extra fields" for a bundle.
|
||||
*
|
||||
* @param $entity_type
|
||||
* The entity type.
|
||||
* @param $bundle
|
||||
* The bundle name.
|
||||
*
|
||||
* @return
|
||||
* The array of extra fields.
|
||||
*/
|
||||
public function getBundleExtraFields($entity_type, $bundle) {
|
||||
// Read from the "static" cache.
|
||||
if (isset($this->bundleExtraFields[$entity_type][$bundle])) {
|
||||
return $this->bundleExtraFields[$entity_type][$bundle];
|
||||
}
|
||||
|
||||
// Read from the persistent cache.
|
||||
if ($cached = cache_get("field_info:bundle_extra:$entity_type:$bundle", 'cache_field')) {
|
||||
$this->bundleExtraFields[$entity_type][$bundle] = $cached->data;
|
||||
return $this->bundleExtraFields[$entity_type][$bundle];
|
||||
}
|
||||
|
||||
// Cache miss: read from hook_field_extra_fields(). Note: given the current
|
||||
// shape of the hook, we have no other way than collecting extra fields on
|
||||
// all bundles.
|
||||
$info = array();
|
||||
$extra = module_invoke_all('field_extra_fields');
|
||||
drupal_alter('field_extra_fields', $extra);
|
||||
// Merge in saved settings.
|
||||
if (isset($extra[$entity_type][$bundle])) {
|
||||
$info = $this->prepareExtraFields($extra[$entity_type][$bundle], $entity_type, $bundle);
|
||||
}
|
||||
|
||||
// Store in the 'static' and persistent caches.
|
||||
$this->bundleExtraFields[$entity_type][$bundle] = $info;
|
||||
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];
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a field definition for the current run-time context.
|
||||
*
|
||||
* @param $field
|
||||
* The raw field structure as read from the database.
|
||||
*
|
||||
* @return
|
||||
* The field definition completed for the current runtime context.
|
||||
*/
|
||||
public function prepareField($field) {
|
||||
// Make sure all expected field settings are present.
|
||||
$field['settings'] += field_info_field_settings($field['type']);
|
||||
$field['storage']['settings'] += field_info_storage_settings($field['storage']['type']);
|
||||
|
||||
// Add storage details.
|
||||
$details = (array) module_invoke($field['storage']['module'], 'field_storage_details', $field);
|
||||
drupal_alter('field_storage_details', $details, $field);
|
||||
$field['storage']['details'] = $details;
|
||||
|
||||
// Populate the list of bundles using the field.
|
||||
$field['bundles'] = array();
|
||||
if (!$field['deleted']) {
|
||||
$map = $this->getFieldMap();
|
||||
if (isset($map[$field['field_name']])) {
|
||||
$field['bundles'] = $map[$field['field_name']]['bundles'];
|
||||
}
|
||||
}
|
||||
|
||||
return $field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares an instance definition for the current run-time context.
|
||||
*
|
||||
* @param $instance
|
||||
* The raw instance structure as read from the database.
|
||||
* @param $field_type
|
||||
* The field type.
|
||||
*
|
||||
* @return
|
||||
* The field instance array completed for the current runtime context.
|
||||
*/
|
||||
public function prepareInstance($instance, $field_type) {
|
||||
// Make sure all expected instance settings are present.
|
||||
$instance['settings'] += field_info_instance_settings($field_type);
|
||||
|
||||
// Set a default value for the instance.
|
||||
if (field_behaviors_widget('default value', $instance) == FIELD_BEHAVIOR_DEFAULT && !isset($instance['default_value'])) {
|
||||
$instance['default_value'] = NULL;
|
||||
}
|
||||
|
||||
// Prepare widget settings.
|
||||
$instance['widget'] = $this->prepareInstanceWidget($instance['widget'], $field_type);
|
||||
|
||||
// Prepare display settings.
|
||||
foreach ($instance['display'] as $view_mode => $display) {
|
||||
$instance['display'][$view_mode] = $this->prepareInstanceDisplay($display, $field_type);
|
||||
}
|
||||
|
||||
// Fall back to 'hidden' for view modes configured to use custom display
|
||||
// settings, and for which the instance has no explicit settings.
|
||||
$entity_info = entity_get_info($instance['entity_type']);
|
||||
$view_modes = array_merge(array('default'), array_keys($entity_info['view modes']));
|
||||
$view_mode_settings = field_view_mode_settings($instance['entity_type'], $instance['bundle']);
|
||||
foreach ($view_modes as $view_mode) {
|
||||
if ($view_mode == 'default' || !empty($view_mode_settings[$view_mode]['custom_settings'])) {
|
||||
if (!isset($instance['display'][$view_mode])) {
|
||||
$instance['display'][$view_mode] = array(
|
||||
'type' => 'hidden',
|
||||
'label' => 'above',
|
||||
'settings' => array(),
|
||||
'weight' => 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares widget properties for the current run-time context.
|
||||
*
|
||||
* @param $widget
|
||||
* Widget specifications as found in $instance['widget'].
|
||||
* @param $field_type
|
||||
* The field type.
|
||||
*
|
||||
* @return
|
||||
* The widget properties completed for the current runtime context.
|
||||
*/
|
||||
public function prepareInstanceWidget($widget, $field_type) {
|
||||
$field_type_info = field_info_field_types($field_type);
|
||||
|
||||
// Fill in default values.
|
||||
$widget += array(
|
||||
'type' => $field_type_info['default_widget'],
|
||||
'settings' => array(),
|
||||
'weight' => 0,
|
||||
);
|
||||
|
||||
$widget_type_info = field_info_widget_types($widget['type']);
|
||||
// Fall back to default formatter if formatter type is not available.
|
||||
if (!$widget_type_info) {
|
||||
$widget['type'] = $field_type_info['default_widget'];
|
||||
$widget_type_info = field_info_widget_types($widget['type']);
|
||||
}
|
||||
$widget['module'] = $widget_type_info['module'];
|
||||
// Fill in default settings for the widget.
|
||||
$widget['settings'] += field_info_widget_settings($widget['type']);
|
||||
|
||||
return $widget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts display specifications to the current run-time context.
|
||||
*
|
||||
* @param $display
|
||||
* Display specifications as found in $instance['display']['a_view_mode'].
|
||||
* @param $field_type
|
||||
* The field type.
|
||||
*
|
||||
* @return
|
||||
* The display properties completed for the current runtime context.
|
||||
*/
|
||||
public function prepareInstanceDisplay($display, $field_type) {
|
||||
$field_type_info = field_info_field_types($field_type);
|
||||
|
||||
// Fill in default values.
|
||||
$display += array(
|
||||
'label' => 'above',
|
||||
'type' => $field_type_info['default_formatter'],
|
||||
'settings' => array(),
|
||||
'weight' => 0,
|
||||
);
|
||||
if ($display['type'] != 'hidden') {
|
||||
$formatter_type_info = field_info_formatter_types($display['type']);
|
||||
// Fall back to default formatter if formatter type is not available.
|
||||
if (!$formatter_type_info) {
|
||||
$display['type'] = $field_type_info['default_formatter'];
|
||||
$formatter_type_info = field_info_formatter_types($display['type']);
|
||||
}
|
||||
$display['module'] = $formatter_type_info['module'];
|
||||
// Fill in default settings for the formatter.
|
||||
$display['settings'] += field_info_formatter_settings($display['type']);
|
||||
}
|
||||
|
||||
return $display;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares 'extra fields' for the current run-time context.
|
||||
*
|
||||
* @param $extra_fields
|
||||
* The array of extra fields, as collected in hook_field_extra_fields().
|
||||
* @param $entity_type
|
||||
* The entity type.
|
||||
* @param $bundle
|
||||
* The bundle name.
|
||||
*
|
||||
* @return
|
||||
* The list of extra fields completed for the current runtime context.
|
||||
*/
|
||||
public function prepareExtraFields($extra_fields, $entity_type, $bundle) {
|
||||
$entity_type_info = entity_get_info($entity_type);
|
||||
$bundle_settings = field_bundle_settings($entity_type, $bundle);
|
||||
$extra_fields += array('form' => array(), 'display' => array());
|
||||
|
||||
$result = array();
|
||||
// Extra fields in forms.
|
||||
foreach ($extra_fields['form'] as $name => $field_data) {
|
||||
$settings = isset($bundle_settings['extra_fields']['form'][$name]) ? $bundle_settings['extra_fields']['form'][$name] : array();
|
||||
if (isset($settings['weight'])) {
|
||||
$field_data['weight'] = $settings['weight'];
|
||||
}
|
||||
$result['form'][$name] = $field_data;
|
||||
}
|
||||
|
||||
// Extra fields in displayed entities.
|
||||
$data = $extra_fields['display'];
|
||||
foreach ($extra_fields['display'] as $name => $field_data) {
|
||||
$settings = isset($bundle_settings['extra_fields']['display'][$name]) ? $bundle_settings['extra_fields']['display'][$name] : array();
|
||||
$view_modes = array_merge(array('default'), array_keys($entity_type_info['view modes']));
|
||||
foreach ($view_modes as $view_mode) {
|
||||
if (isset($settings[$view_mode])) {
|
||||
$field_data['display'][$view_mode] = $settings[$view_mode];
|
||||
}
|
||||
else {
|
||||
$field_data['display'][$view_mode] = array(
|
||||
'weight' => $field_data['weight'],
|
||||
'visible' => TRUE,
|
||||
);
|
||||
}
|
||||
}
|
||||
unset($field_data['weight']);
|
||||
$result['display'][$name] = $field_data;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
@@ -5,6 +5,32 @@
|
||||
* Field Info API, providing information about available fields and field types.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Retrieves the FieldInfo object for the current request.
|
||||
*
|
||||
* @return FieldInfo
|
||||
* An instance of the FieldInfo class.
|
||||
*/
|
||||
function _field_info_field_cache() {
|
||||
// Use the advanced drupal_static() pattern, since this is called very often.
|
||||
static $drupal_static_fast;
|
||||
|
||||
if (!isset($drupal_static_fast)) {
|
||||
$drupal_static_fast['field_info_field_cache'] = &drupal_static(__FUNCTION__);
|
||||
}
|
||||
$field_info = &$drupal_static_fast['field_info_field_cache'];
|
||||
|
||||
if (!isset($field_info)) {
|
||||
// @todo The registry should save the need for an explicit include, but not
|
||||
// a couple upgrade tests (DisabledNodeTypeTestCase,
|
||||
// FilterFormatUpgradePathTestCase...) break in a strange way without it.
|
||||
include_once dirname(__FILE__) . '/field.info.class.inc';
|
||||
$field_info = new FieldInfo();
|
||||
}
|
||||
|
||||
return $field_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* @defgroup field_info Field Info API
|
||||
* @{
|
||||
@@ -34,7 +60,50 @@ function field_info_cache_clear() {
|
||||
entity_info_cache_clear();
|
||||
|
||||
_field_info_collate_types(TRUE);
|
||||
_field_info_collate_fields(TRUE);
|
||||
_field_info_field_cache()->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Collates all information on existing fields and instances.
|
||||
*
|
||||
* Deprecated. This function is kept to ensure backwards compatibility, but has
|
||||
* a serious performance impact, and should be absolutely avoided.
|
||||
* See http://drupal.org/node/1915646.
|
||||
*
|
||||
* Use the regular field_info_*() API functions to access the information, or
|
||||
* field_info_cache_clear() to clear the cached data.
|
||||
*/
|
||||
function _field_info_collate_fields($reset = FALSE) {
|
||||
if ($reset) {
|
||||
_field_info_field_cache()->flush();
|
||||
return;
|
||||
}
|
||||
|
||||
$cache = _field_info_field_cache();
|
||||
|
||||
// Collect fields, and build the array of IDs keyed by field_name.
|
||||
$fields = $cache->getFields();
|
||||
$field_ids = array();
|
||||
foreach ($fields as $id => $field) {
|
||||
if (!$field['deleted']) {
|
||||
$field_ids[$field['field_name']] = $id;
|
||||
}
|
||||
}
|
||||
|
||||
// Collect extra fields for all entity types.
|
||||
$extra_fields = array();
|
||||
foreach (field_info_bundles() as $entity_type => $bundles) {
|
||||
foreach ($bundles as $bundle => $info) {
|
||||
$extra_fields[$entity_type][$bundle] = $cache->getBundleExtraFields($entity_type, $bundle);
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
'fields' => $fields,
|
||||
'field_ids' => $field_ids,
|
||||
'instances' => $cache->getInstances(),
|
||||
'extra_fields' => $extra_fields,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,96 +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');
|
||||
}
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collates all information on existing fields and instances.
|
||||
*
|
||||
* @param $reset
|
||||
* If TRUE, clear the cache. The information will be rebuilt from the
|
||||
* database next time it is needed. Defaults to FALSE.
|
||||
*
|
||||
* @return
|
||||
* If $reset is TRUE, nothing.
|
||||
* If $reset is FALSE, an array containing the following elements:
|
||||
* - fields: Array of existing fields, keyed by field ID. This element
|
||||
* lists deleted and non-deleted fields, but not inactive ones.
|
||||
* Each field has an additional element, 'bundles', which is an array
|
||||
* of all non-deleted instances of that field.
|
||||
* - field_ids: Array of field IDs, keyed by field name. This element
|
||||
* only lists non-deleted, active fields.
|
||||
* - instances: Array of existing instances, keyed by entity type, bundle
|
||||
* name and field name. This element only lists non-deleted instances
|
||||
* whose field is active.
|
||||
*/
|
||||
function _field_info_collate_fields($reset = FALSE) {
|
||||
static $info;
|
||||
|
||||
if ($reset) {
|
||||
$info = NULL;
|
||||
cache_clear_all('field_info_fields', 'cache_field');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isset($info)) {
|
||||
if ($cached = cache_get('field_info_fields', 'cache_field')) {
|
||||
$info = $cached->data;
|
||||
}
|
||||
else {
|
||||
$definitions = array(
|
||||
'field_ids' => field_read_fields(array(), array('include_deleted' => 1)),
|
||||
'instances' => field_read_instances(),
|
||||
);
|
||||
|
||||
// Populate 'fields' with all fields, keyed by ID.
|
||||
$info['fields'] = array();
|
||||
foreach ($definitions['field_ids'] as $key => $field) {
|
||||
$info['fields'][$key] = $definitions['field_ids'][$key] = _field_info_prepare_field($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");
|
||||
}
|
||||
|
||||
// Build an array of field IDs for non-deleted fields, keyed by name.
|
||||
$info['field_ids'] = array();
|
||||
foreach ($info['fields'] as $key => $field) {
|
||||
if (!$field['deleted']) {
|
||||
$info['field_ids'][$field['field_name']] = $key;
|
||||
}
|
||||
}
|
||||
|
||||
// Populate 'instances'. Only non-deleted instances are considered.
|
||||
$info['instances'] = array();
|
||||
foreach (field_info_bundles() as $entity_type => $bundles) {
|
||||
foreach ($bundles as $bundle => $bundle_info) {
|
||||
$info['instances'][$entity_type][$bundle] = array();
|
||||
}
|
||||
}
|
||||
foreach ($definitions['instances'] as $instance) {
|
||||
$field = $info['fields'][$instance['field_id']];
|
||||
$instance = _field_info_prepare_instance($instance, $field);
|
||||
$info['instances'][$instance['entity_type']][$instance['bundle']][$instance['field_name']] = $instance;
|
||||
// Enrich field definitions with the list of bundles where they have
|
||||
// instances. NOTE: Deleted fields in $info['field_ids'] are not
|
||||
// enriched because all of their instances are deleted, too, and
|
||||
// are thus not in $definitions['instances'].
|
||||
$info['fields'][$instance['field_id']]['bundles'][$instance['entity_type']][] = $instance['bundle'];
|
||||
}
|
||||
|
||||
// Populate 'extra_fields'.
|
||||
$extra = module_invoke_all('field_extra_fields');
|
||||
drupal_alter('field_extra_fields', $extra);
|
||||
// Merge in saved settings.
|
||||
foreach ($extra as $entity_type => $bundles) {
|
||||
foreach ($bundles as $bundle => $extra_fields) {
|
||||
$extra_fields = _field_info_prepare_extra_fields($extra_fields, $entity_type, $bundle);
|
||||
$info['extra_fields'][$entity_type][$bundle] = $extra_fields;
|
||||
}
|
||||
}
|
||||
|
||||
cache_set('field_info_fields', $info, 'cache_field');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,190 +237,66 @@ function _field_info_collate_fields($reset = FALSE) {
|
||||
/**
|
||||
* Prepares a field definition for the current run-time context.
|
||||
*
|
||||
* Since the field was last saved or updated, new field settings can be
|
||||
* expected.
|
||||
* The functionality has moved to the FieldInfo class. This function is kept as
|
||||
* a backwards-compatibility layer. See http://drupal.org/node/1915646.
|
||||
*
|
||||
* @param $field
|
||||
* The raw field structure as read from the database.
|
||||
* @see FieldInfo::prepareField()
|
||||
*/
|
||||
function _field_info_prepare_field($field) {
|
||||
// Make sure all expected field settings are present.
|
||||
$field['settings'] += field_info_field_settings($field['type']);
|
||||
$field['storage']['settings'] += field_info_storage_settings($field['storage']['type']);
|
||||
|
||||
// Add storage details.
|
||||
$details = (array) module_invoke($field['storage']['module'], 'field_storage_details', $field);
|
||||
drupal_alter('field_storage_details', $details, $field, $instance);
|
||||
$field['storage']['details'] = $details;
|
||||
|
||||
// Initialize the 'bundles' list.
|
||||
$field['bundles'] = array();
|
||||
|
||||
return $field;
|
||||
$cache = _field_info_field_cache();
|
||||
return $cache->prepareField($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares an instance definition for the current run-time context.
|
||||
*
|
||||
* Since the instance was last saved or updated, a number of things might have
|
||||
* changed: widgets or formatters disabled, new settings expected, new view
|
||||
* modes added...
|
||||
* The functionality has moved to the FieldInfo class. This function is kept as
|
||||
* a backwards-compatibility layer. See http://drupal.org/node/1915646.
|
||||
*
|
||||
* @param $instance
|
||||
* The raw instance structure as read from the database.
|
||||
* @param $field
|
||||
* The field structure for the instance.
|
||||
*
|
||||
* @return
|
||||
* Field instance array.
|
||||
* @see FieldInfo::prepareInstance()
|
||||
*/
|
||||
function _field_info_prepare_instance($instance, $field) {
|
||||
// Make sure all expected instance settings are present.
|
||||
$instance['settings'] += field_info_instance_settings($field['type']);
|
||||
|
||||
// Set a default value for the instance.
|
||||
if (field_behaviors_widget('default value', $instance) == FIELD_BEHAVIOR_DEFAULT && !isset($instance['default_value'])) {
|
||||
$instance['default_value'] = NULL;
|
||||
}
|
||||
|
||||
$instance['widget'] = _field_info_prepare_instance_widget($field, $instance['widget']);
|
||||
|
||||
foreach ($instance['display'] as $view_mode => $display) {
|
||||
$instance['display'][$view_mode] = _field_info_prepare_instance_display($field, $display);
|
||||
}
|
||||
|
||||
// Fallback to 'hidden' for view modes configured to use custom display
|
||||
// settings, and for which the instance has no explicit settings.
|
||||
$entity_info = entity_get_info($instance['entity_type']);
|
||||
$view_modes = array_merge(array('default'), array_keys($entity_info['view modes']));
|
||||
$view_mode_settings = field_view_mode_settings($instance['entity_type'], $instance['bundle']);
|
||||
foreach ($view_modes as $view_mode) {
|
||||
if ($view_mode == 'default' || !empty($view_mode_settings[$view_mode]['custom_settings'])) {
|
||||
if (!isset($instance['display'][$view_mode])) {
|
||||
$instance['display'][$view_mode] = array(
|
||||
'type' => 'hidden',
|
||||
'label' => 'above',
|
||||
'settings' => array(),
|
||||
'weight' => 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $instance;
|
||||
$cache = _field_info_field_cache();
|
||||
return $cache->prepareInstance($instance, $field['type']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts display specifications to the current run-time context.
|
||||
*
|
||||
* @param $field
|
||||
* The field structure for the instance.
|
||||
* @param $display
|
||||
* Display specifications as found in
|
||||
* $instance['display']['some_view_mode'].
|
||||
* The functionality has moved to the FieldInfo class. This function is kept as
|
||||
* a backwards-compatibility layer. See http://drupal.org/node/1915646.
|
||||
*
|
||||
* @see FieldInfo::prepareInstanceDisplay()
|
||||
*/
|
||||
function _field_info_prepare_instance_display($field, $display) {
|
||||
$field_type = field_info_field_types($field['type']);
|
||||
|
||||
// Fill in default values.
|
||||
$display += array(
|
||||
'label' => 'above',
|
||||
'type' => $field_type['default_formatter'],
|
||||
'settings' => array(),
|
||||
'weight' => 0,
|
||||
);
|
||||
if ($display['type'] != 'hidden') {
|
||||
$formatter_type = field_info_formatter_types($display['type']);
|
||||
// Fallback to default formatter if formatter type is not available.
|
||||
if (!$formatter_type) {
|
||||
$display['type'] = $field_type['default_formatter'];
|
||||
$formatter_type = field_info_formatter_types($display['type']);
|
||||
}
|
||||
$display['module'] = $formatter_type['module'];
|
||||
// Fill in default settings for the formatter.
|
||||
$display['settings'] += field_info_formatter_settings($display['type']);
|
||||
}
|
||||
|
||||
return $display;
|
||||
$cache = _field_info_field_cache();
|
||||
return $cache->prepareInstanceDisplay($display, $field['type']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares widget specifications for the current run-time context.
|
||||
*
|
||||
* @param $field
|
||||
* The field structure for the instance.
|
||||
* @param $widget
|
||||
* Widget specifications as found in $instance['widget'].
|
||||
* The functionality has moved to the FieldInfo class. This function is kept as
|
||||
* a backwards-compatibility layer. See http://drupal.org/node/1915646.
|
||||
*
|
||||
* @see FieldInfo::prepareInstanceWidget()
|
||||
*/
|
||||
function _field_info_prepare_instance_widget($field, $widget) {
|
||||
$field_type = field_info_field_types($field['type']);
|
||||
|
||||
// Fill in default values.
|
||||
$widget += array(
|
||||
'type' => $field_type['default_widget'],
|
||||
'settings' => array(),
|
||||
'weight' => 0,
|
||||
);
|
||||
|
||||
$widget_type = field_info_widget_types($widget['type']);
|
||||
// Fallback to default formatter if formatter type is not available.
|
||||
if (!$widget_type) {
|
||||
$widget['type'] = $field_type['default_widget'];
|
||||
$widget_type = field_info_widget_types($widget['type']);
|
||||
}
|
||||
$widget['module'] = $widget_type['module'];
|
||||
// Fill in default settings for the widget.
|
||||
$widget['settings'] += field_info_widget_settings($widget['type']);
|
||||
|
||||
return $widget;
|
||||
$cache = _field_info_field_cache();
|
||||
return $cache->prepareInstanceWidget($widget, $field['type']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares 'extra fields' for the current run-time context.
|
||||
*
|
||||
* @param $extra_fields
|
||||
* The array of extra fields, as collected in hook_field_extra_fields().
|
||||
* @param $entity_type
|
||||
* The entity type.
|
||||
* @param $bundle
|
||||
* The bundle name.
|
||||
* The functionality has moved to the FieldInfo class. This function is kept as
|
||||
* a backwards-compatibility layer. See http://drupal.org/node/1915646.
|
||||
*
|
||||
* @see FieldInfo::prepareExtraFields()
|
||||
*/
|
||||
function _field_info_prepare_extra_fields($extra_fields, $entity_type, $bundle) {
|
||||
$entity_type_info = entity_get_info($entity_type);
|
||||
$bundle_settings = field_bundle_settings($entity_type, $bundle);
|
||||
$extra_fields += array('form' => array(), 'display' => array());
|
||||
|
||||
$result = array();
|
||||
// Extra fields in forms.
|
||||
foreach ($extra_fields['form'] as $name => $field_data) {
|
||||
$settings = isset($bundle_settings['extra_fields']['form'][$name]) ? $bundle_settings['extra_fields']['form'][$name] : array();
|
||||
if (isset($settings['weight'])) {
|
||||
$field_data['weight'] = $settings['weight'];
|
||||
}
|
||||
$result['form'][$name] = $field_data;
|
||||
}
|
||||
|
||||
// Extra fields in displayed entities.
|
||||
$data = $extra_fields['display'];
|
||||
foreach ($extra_fields['display'] as $name => $field_data) {
|
||||
$settings = isset($bundle_settings['extra_fields']['display'][$name]) ? $bundle_settings['extra_fields']['display'][$name] : array();
|
||||
$view_modes = array_merge(array('default'), array_keys($entity_type_info['view modes']));
|
||||
foreach ($view_modes as $view_mode) {
|
||||
if (isset($settings[$view_mode])) {
|
||||
$field_data['display'][$view_mode] = $settings[$view_mode];
|
||||
}
|
||||
else {
|
||||
$field_data['display'][$view_mode] = array(
|
||||
'weight' => $field_data['weight'],
|
||||
'visible' => TRUE,
|
||||
);
|
||||
}
|
||||
}
|
||||
unset($field_data['weight']);
|
||||
$result['display'][$name] = $field_data;
|
||||
}
|
||||
|
||||
return $result;
|
||||
$cache = _field_info_field_cache();
|
||||
return $cache->prepareExtraFields($extra_fields, $entity_type, $bundle);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -583,22 +443,62 @@ function field_info_bundles($entity_type = NULL) {
|
||||
return $bundles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a lightweight map of fields across bundles.
|
||||
*
|
||||
* The function only returns active, non deleted fields.
|
||||
*
|
||||
* @return
|
||||
* An array keyed by field name. Each value is an array with two entries:
|
||||
* - 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();
|
||||
return $cache->getFieldMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all field definitions.
|
||||
*
|
||||
* Use of this function should be avoided when possible, since it loads and
|
||||
* statically caches a potentially large array of information. Use
|
||||
* field_info_field_map() instead.
|
||||
*
|
||||
* When iterating over the fields present in a given bundle after a call to
|
||||
* field_info_instances($entity_type, $bundle), it is recommended to use
|
||||
* field_info_field() on each individual field instead.
|
||||
*
|
||||
* @return
|
||||
* An array of field definitions, keyed by field name. Each field has an
|
||||
* additional property, 'bundles', which is an array of all the bundles to
|
||||
* which this field belongs keyed by entity type.
|
||||
*
|
||||
* @see field_info_field_map()
|
||||
*/
|
||||
function field_info_fields() {
|
||||
$cache = _field_info_field_cache();
|
||||
$info = $cache->getFields();
|
||||
|
||||
$fields = array();
|
||||
$info = _field_info_collate_fields();
|
||||
foreach ($info['fields'] as $key => $field) {
|
||||
foreach ($info as $key => $field) {
|
||||
if (!$field['deleted']) {
|
||||
$fields[$field['field_name']] = $field;
|
||||
}
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
@@ -620,10 +520,8 @@ function field_info_fields() {
|
||||
* @see field_info_field_by_id()
|
||||
*/
|
||||
function field_info_field($field_name) {
|
||||
$info = _field_info_collate_fields();
|
||||
if (isset($info['field_ids'][$field_name])) {
|
||||
return $info['fields'][$info['field_ids'][$field_name]];
|
||||
}
|
||||
$cache = _field_info_field_cache();
|
||||
return $cache->getField($field_name);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -641,17 +539,19 @@ function field_info_field($field_name) {
|
||||
* @see field_info_field()
|
||||
*/
|
||||
function field_info_field_by_id($field_id) {
|
||||
$info = _field_info_collate_fields();
|
||||
if (isset($info['fields'][$field_id])) {
|
||||
return $info['fields'][$field_id];
|
||||
}
|
||||
$cache = _field_info_field_cache();
|
||||
return $cache->getFieldById($field_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the same data as field_info_field_by_id() for every field.
|
||||
*
|
||||
* This function is typically used when handling all fields of some entities
|
||||
* to avoid thousands of calls to field_info_field_by_id().
|
||||
* Use of this function should be avoided when possible, since it loads and
|
||||
* statically caches a potentially large array of information.
|
||||
*
|
||||
* When iterating over the fields present in a given bundle after a call to
|
||||
* field_info_instances($entity_type, $bundle), it is recommended to use
|
||||
* field_info_field() on each individual field instead.
|
||||
*
|
||||
* @return
|
||||
* An array, each key is a field ID and the values are field arrays as
|
||||
@@ -662,41 +562,57 @@ function field_info_field_by_id($field_id) {
|
||||
* @see field_info_field_by_id()
|
||||
*/
|
||||
function field_info_field_by_ids() {
|
||||
$info = _field_info_collate_fields();
|
||||
return $info['fields'];
|
||||
$cache = _field_info_field_cache();
|
||||
return $cache->getFields();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves information about field instances.
|
||||
*
|
||||
* Use of this function to retrieve instances across separate bundles (i.e.
|
||||
* when the $bundle parameter is NULL) should be avoided when possible, since
|
||||
* it loads and statically caches a potentially large array of information. Use
|
||||
* field_info_field_map() instead.
|
||||
*
|
||||
* When retrieving the instances of a specific bundle (i.e. when both
|
||||
* $entity_type and $bundle_name are provided), the function also populates a
|
||||
* static cache with the corresponding field definitions, allowing fast
|
||||
* retrieval of field_info_field() later in the request.
|
||||
*
|
||||
* @param $entity_type
|
||||
* The entity type for which to return instances.
|
||||
* (optional) The entity type for which to return instances.
|
||||
* @param $bundle_name
|
||||
* The bundle name for which to return instances.
|
||||
* (optional) The bundle name for which to return instances. If $entity_type
|
||||
* is NULL, the $bundle_name parameter is ignored.
|
||||
*
|
||||
* @return
|
||||
* If $entity_type is not set, return all instances keyed by entity type and
|
||||
* bundle name. If $entity_type is set, return all instances for that entity
|
||||
* type, keyed by bundle name. If $entity_type and $bundle_name are set, return
|
||||
* all instances for that bundle.
|
||||
*
|
||||
* @see field_info_field_map()
|
||||
*/
|
||||
function field_info_instances($entity_type = NULL, $bundle_name = NULL) {
|
||||
$info = _field_info_collate_fields();
|
||||
$cache = _field_info_field_cache();
|
||||
|
||||
if (isset($entity_type) && isset($bundle_name)) {
|
||||
return isset($info['instances'][$entity_type][$bundle_name]) ? $info['instances'][$entity_type][$bundle_name] : array();
|
||||
if (!isset($entity_type)) {
|
||||
return $cache->getInstances();
|
||||
}
|
||||
elseif (isset($entity_type)) {
|
||||
return isset($info['instances'][$entity_type]) ? $info['instances'][$entity_type] : array();
|
||||
}
|
||||
else {
|
||||
return $info['instances'];
|
||||
if (!isset($bundle_name)) {
|
||||
return $cache->getInstances($entity_type);
|
||||
}
|
||||
|
||||
return $cache->getBundleInstances($entity_type, $bundle_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of instance data for a specific field and bundle.
|
||||
*
|
||||
* The function populates a static cache with all fields and instances used in
|
||||
* the bundle, allowing fast retrieval of field_info_field() or
|
||||
* field_info_instance() later in the request.
|
||||
*
|
||||
* @param $entity_type
|
||||
* The entity type for the instance.
|
||||
* @param $field_name
|
||||
@@ -709,9 +625,10 @@ function field_info_instances($entity_type = NULL, $bundle_name = NULL) {
|
||||
* NULL if the instance does not exist.
|
||||
*/
|
||||
function field_info_instance($entity_type, $field_name, $bundle_name) {
|
||||
$info = _field_info_collate_fields();
|
||||
if (isset($info['instances'][$entity_type][$bundle_name][$field_name])) {
|
||||
return $info['instances'][$entity_type][$bundle_name][$field_name];
|
||||
$cache = _field_info_field_cache();
|
||||
$info = $cache->getBundleInstances($entity_type, $bundle_name);
|
||||
if (isset($info[$field_name])) {
|
||||
return $info[$field_name];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -769,11 +686,10 @@ function field_info_instance($entity_type, $field_name, $bundle_name) {
|
||||
* The array of pseudo-field elements in the bundle.
|
||||
*/
|
||||
function field_info_extra_fields($entity_type, $bundle, $context) {
|
||||
$info = _field_info_collate_fields();
|
||||
if (isset($info['extra_fields'][$entity_type][$bundle][$context])) {
|
||||
return $info['extra_fields'][$entity_type][$bundle][$context];
|
||||
}
|
||||
return array();
|
||||
$cache = _field_info_field_cache();
|
||||
$info = $cache->getBundleExtraFields($entity_type, $bundle);
|
||||
|
||||
return isset($info[$context]) ? $info[$context] : array();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -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;
|
||||
}
|
||||
@@ -459,6 +460,13 @@ function field_update_7002() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the FieldInfo class to the class registry.
|
||||
*/
|
||||
function field_update_7003() {
|
||||
// Empty update to force a rebuild of the registry.
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup updates-7.x-extra".
|
||||
*/
|
||||
|
@@ -342,17 +342,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 +808,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.
|
||||
@@ -873,7 +862,8 @@ function field_view_field($entity_type, $entity, $field_name, $display = array()
|
||||
if ($field = field_info_field($field_name)) {
|
||||
if (is_array($display)) {
|
||||
// When using custom display settings, fill in default values.
|
||||
$display = _field_info_prepare_instance_display($field, $display);
|
||||
$cache = _field_info_field_cache();
|
||||
$display = $cache->prepareInstanceDisplay($display, $field["type"]);
|
||||
}
|
||||
|
||||
// Hook invocations are done through the _field_invoke() functions in
|
||||
@@ -904,6 +894,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);
|
||||
|
||||
@@ -946,20 +937,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'
|
||||
@@ -1197,7 +1198,7 @@ function _element_validate_integer($element, &$form_state) {
|
||||
* Use element_validate_integer_positive() instead.
|
||||
*
|
||||
* @deprecated
|
||||
* @see element_validate_number_positive()
|
||||
* @see element_validate_integer_positive()
|
||||
*/
|
||||
function _element_validate_integer_positive($element, &$form_state) {
|
||||
element_validate_integer_positive($element, $form_state);
|
||||
|
@@ -6,3 +6,9 @@ core = 7.x
|
||||
dependencies[] = field
|
||||
files[] = field_sql_storage.test
|
||||
required = TRUE
|
||||
|
||||
; Information added by Drupal.org packaging script on 2015-04-02
|
||||
version = "7.36"
|
||||
project = "drupal"
|
||||
datestamp = "1427943826"
|
||||
|
||||
|
@@ -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.
|
||||
*
|
||||
@@ -188,7 +231,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;
|
||||
}
|
||||
@@ -324,11 +367,14 @@ function field_sql_storage_field_storage_delete_field($field) {
|
||||
* Implements hook_field_storage_load().
|
||||
*/
|
||||
function field_sql_storage_field_storage_load($entity_type, $entities, $age, $fields, $options) {
|
||||
$field_info = field_info_field_by_ids();
|
||||
$load_current = $age == FIELD_LOAD_CURRENT;
|
||||
|
||||
foreach ($fields as $field_id => $ids) {
|
||||
$field = $field_info[$field_id];
|
||||
// By the time this hook runs, the relevant field definitions have been
|
||||
// populated and cached in FieldInfo, so calling field_info_field_by_id()
|
||||
// on each field individually is more efficient than loading all fields in
|
||||
// memory upfront with field_info_field_by_ids().
|
||||
$field = field_info_field_by_id($field_id);
|
||||
$field_name = $field['field_name'];
|
||||
$table = $load_current ? _field_sql_storage_tablename($field) : _field_sql_storage_revision_tablename($field);
|
||||
|
||||
@@ -419,7 +465,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,
|
||||
@@ -501,17 +547,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.
|
||||
@@ -358,21 +358,21 @@ class FieldSqlStorageTestCase extends DrupalWebTestCase {
|
||||
$field = array('field_name' => $field_name, 'indexes' => array('value' => array('value')));
|
||||
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_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']);
|
||||
}
|
||||
}
|
||||
|
@@ -6,3 +6,9 @@ core = 7.x
|
||||
dependencies[] = field
|
||||
dependencies[] = options
|
||||
files[] = tests/list.test
|
||||
|
||||
; Information added by Drupal.org packaging script on 2015-04-02
|
||||
version = "7.36"
|
||||
project = "drupal"
|
||||
datestamp = "1427943826"
|
||||
|
||||
|
@@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -4,3 +4,9 @@ core = 7.x
|
||||
package = Testing
|
||||
version = VERSION
|
||||
hidden = TRUE
|
||||
|
||||
; Information added by Drupal.org packaging script on 2015-04-02
|
||||
version = "7.36"
|
||||
project = "drupal"
|
||||
datestamp = "1427943826"
|
||||
|
||||
|
@@ -5,3 +5,9 @@ version = VERSION
|
||||
core = 7.x
|
||||
dependencies[] = field
|
||||
files[] = number.test
|
||||
|
||||
; Information added by Drupal.org packaging script on 2015-04-02
|
||||
version = "7.36"
|
||||
project = "drupal"
|
||||
datestamp = "1427943826"
|
||||
|
||||
|
@@ -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(round($value, 2), '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.'
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -5,3 +5,9 @@ version = VERSION
|
||||
core = 7.x
|
||||
dependencies[] = field
|
||||
files[] = options.test
|
||||
|
||||
; Information added by Drupal.org packaging script on 2015-04-02
|
||||
version = "7.36"
|
||||
project = "drupal"
|
||||
datestamp = "1427943826"
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @file
|
||||
* Tests for options.module.
|
||||
*/
|
||||
|
||||
@@ -85,7 +85,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 & unescaped <strong>markup</strong>', t('Option text was properly filtered.'));
|
||||
$this->assertRaw('Some dangerous & unescaped <strong>markup</strong>', 'Option text was properly filtered.');
|
||||
|
||||
// Select first option.
|
||||
$edit = array("card_1[$langcode]" => 0);
|
||||
@@ -139,7 +139,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 & unescaped <strong>markup</strong>', t('Option text was properly filtered.'));
|
||||
$this->assertRaw('Some dangerous & unescaped <strong>markup</strong>', 'Option text was properly filtered.');
|
||||
|
||||
// Submit form: select first and third options.
|
||||
$edit = array(
|
||||
@@ -178,7 +178,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 +225,19 @@ 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 & unescaped markup', t('Option text was properly filtered.'));
|
||||
$this->assertRaw('Some dangerous & unescaped markup', 'Option text was properly filtered.');
|
||||
|
||||
// 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 +247,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 +259,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 +276,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 & unescaped markup', t('Option text was properly filtered.'));
|
||||
$this->assertRaw('Group 1', t('Option groups are displayed.'));
|
||||
$this->assertRaw('Some dangerous & 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 +323,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 & unescaped markup', t('Option text was properly filtered.'));
|
||||
$this->assertRaw('Some dangerous & 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 +350,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 +359,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 +374,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 +393,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 & unescaped markup', t('Option text was properly filtered.'));
|
||||
$this->assertRaw('Group 1', t('Option groups are displayed.'));
|
||||
$this->assertRaw('Some dangerous & 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 +438,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 & unescaped <strong>markup</strong>', t('Option text was properly filtered.'));
|
||||
$this->assertRaw('Some dangerous & unescaped <strong>markup</strong>', 'Option text was properly filtered.');
|
||||
|
||||
// Submit form: check the option.
|
||||
$edit = array("bool[$langcode]" => TRUE);
|
||||
@@ -483,13 +483,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 +502,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'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -6,3 +6,9 @@ core = 7.x
|
||||
dependencies[] = field
|
||||
files[] = text.test
|
||||
required = TRUE
|
||||
|
||||
; Information added by Drupal.org packaging script on 2015-04-02
|
||||
version = "7.36"
|
||||
project = "drupal"
|
||||
datestamp = "1427943826"
|
||||
|
||||
|
@@ -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;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@@ -245,7 +245,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('Trim length') . ': ' . check_plain($settings['trim_length']);
|
||||
}
|
||||
|
||||
return $summary;
|
||||
|
@@ -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.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -436,7 +436,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 +464,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 +476,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 +501,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 +511,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
@@ -9,6 +9,12 @@
|
||||
* Implements hook_entity_info().
|
||||
*/
|
||||
function field_test_entity_info() {
|
||||
// If requested, clear the field cache while this hook is being called. See
|
||||
// testFieldInfoCache().
|
||||
if (variable_get('field_test_clear_info_cache_in_hook_entity_info', FALSE)) {
|
||||
field_info_cache_clear();
|
||||
}
|
||||
|
||||
$bundles = variable_get('field_test_bundles', array('test_bundle' => array('label' => 'Test Bundle')));
|
||||
$test_entity_modes = array(
|
||||
'full' => array(
|
||||
|
@@ -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',
|
||||
|
@@ -5,3 +5,9 @@ package = Testing
|
||||
files[] = field_test.entity.inc
|
||||
version = VERSION
|
||||
hidden = TRUE
|
||||
|
||||
; Information added by Drupal.org packaging script on 2015-04-02
|
||||
version = "7.36"
|
||||
project = "drupal"
|
||||
datestamp = "1427943826"
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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',
|
||||
@@ -1534,7 +1534,7 @@ function field_ui_existing_field_options($entity_type, $bundle) {
|
||||
// - locked fields,
|
||||
// - fields already in the current bundle,
|
||||
// - fields that cannot be added to the entity type,
|
||||
// - fields that that shoud not be added via user interface.
|
||||
// - fields that should not be added via user interface.
|
||||
|
||||
if (empty($field['locked'])
|
||||
&& !field_info_instance($entity_type, $field['field_name'], $bundle)
|
||||
@@ -1544,7 +1544,7 @@ function field_ui_existing_field_options($entity_type, $bundle) {
|
||||
'type' => $field['type'],
|
||||
'type_label' => $field_types[$field['type']]['label'],
|
||||
'field' => $field['field_name'],
|
||||
'label' => t($instance['label']),
|
||||
'label' => $instance['label'],
|
||||
'widget_type' => $instance['widget']['type'],
|
||||
);
|
||||
}
|
||||
@@ -1558,8 +1558,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'];
|
||||
|
@@ -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
|
||||
|
@@ -5,3 +5,9 @@ version = VERSION
|
||||
core = 7.x
|
||||
dependencies[] = field
|
||||
files[] = field_ui.test
|
||||
|
||||
; Information added by Drupal.org packaging script on 2015-04-02
|
||||
version = "7.36"
|
||||
project = "drupal"
|
||||
datestamp = "1427943826"
|
||||
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -318,7 +318,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);
|
||||
@@ -332,23 +332,30 @@ function _field_ui_bundle_admin_path($entity_type, $bundle_name) {
|
||||
* Identifies inactive fields within a bundle.
|
||||
*/
|
||||
function field_ui_inactive_instances($entity_type, $bundle_name = NULL) {
|
||||
if (!empty($bundle_name)) {
|
||||
$inactive = array($bundle_name => array());
|
||||
$params = array('bundle' => $bundle_name);
|
||||
$params = array('entity_type' => $entity_type);
|
||||
|
||||
if (empty($bundle_name)) {
|
||||
$active = field_info_instances($entity_type);
|
||||
$inactive = array();
|
||||
}
|
||||
else {
|
||||
$inactive = array();
|
||||
$params = array();
|
||||
// Restrict to the specified bundle. For consistency with the case where
|
||||
// $bundle_name is NULL, the $active and $inactive arrays are keyed by
|
||||
// bundle name first.
|
||||
$params['bundle'] = $bundle_name;
|
||||
$active = array($bundle_name => field_info_instances($entity_type, $bundle_name));
|
||||
$inactive = array($bundle_name => array());
|
||||
}
|
||||
$params['entity_type'] = $entity_type;
|
||||
|
||||
$active_instances = field_info_instances($entity_type);
|
||||
// Iterate on existing definitions, and spot those that do not appear in the
|
||||
// $active list collected earlier.
|
||||
$all_instances = field_read_instances($params, array('include_inactive' => TRUE));
|
||||
foreach ($all_instances as $instance) {
|
||||
if (!isset($active_instances[$instance['bundle']][$instance['field_name']])) {
|
||||
if (!isset($active[$instance['bundle']][$instance['field_name']])) {
|
||||
$inactive[$instance['bundle']][$instance['field_name']] = $instance;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($bundle_name)) {
|
||||
return $inactive[$bundle_name];
|
||||
}
|
||||
|
@@ -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(
|
||||
@@ -269,15 +269,15 @@ class FieldUIManageFieldsTestCase extends FieldUITestCase {
|
||||
*/
|
||||
function assertFieldSettings($bundle, $field_name, $string = 'dummy test string', $entity_type = 'node') {
|
||||
// Reset the fields info.
|
||||
_field_info_collate_fields(TRUE);
|
||||
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.');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -360,21 +360,21 @@ class FieldUIManageFieldsTestCase extends FieldUITestCase {
|
||||
$this->fieldUIDeleteField($bundle_path1, $this->field_name, $this->field_label, $this->type);
|
||||
|
||||
// Reset the fields info.
|
||||
_field_info_collate_fields(TRUE);
|
||||
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);
|
||||
|
||||
// Reset the fields info.
|
||||
_field_info_collate_fields(TRUE);
|
||||
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.");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -488,8 +488,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 +498,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 +507,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 +540,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 +549,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 +564,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 +572,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 +580,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.");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -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
|
||||
@@ -261,8 +276,16 @@ function file_field_update($entity_type, $entity, $field, $instance, $langcode,
|
||||
$current_fids[] = $item['fid'];
|
||||
}
|
||||
|
||||
// Compare the original field values with the ones that are being saved.
|
||||
$original = $entity->original;
|
||||
// Compare the original field values with the ones that are being saved. Use
|
||||
// $entity->original to check this when possible, but if it isn't available,
|
||||
// create a bare-bones entity and load its previous values instead.
|
||||
if (isset($entity->original)) {
|
||||
$original = $entity->original;
|
||||
}
|
||||
else {
|
||||
$original = entity_create_stub_entity($entity_type, array($id, $vid, $bundle));
|
||||
field_attach_load($entity_type, array($id => $original), FIELD_LOAD_CURRENT, array('field_id' => $field['id']));
|
||||
}
|
||||
$original_fids = array();
|
||||
if (!empty($original->{$field['field_name']}[$langcode])) {
|
||||
foreach ($original->{$field['field_name']}[$langcode] as $original_item) {
|
||||
@@ -752,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]);
|
||||
@@ -763,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);
|
||||
|
@@ -5,3 +5,9 @@ version = VERSION
|
||||
core = 7.x
|
||||
dependencies[] = field
|
||||
files[] = tests/file.test
|
||||
|
||||
; Information added by Drupal.org packaging script on 2015-04-02
|
||||
version = "7.36"
|
||||
project = "drupal"
|
||||
datestamp = "1427943826"
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -246,7 +246,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.
|
||||
@@ -284,7 +284,6 @@ function file_ajax_upload() {
|
||||
$js = drupal_add_js();
|
||||
$settings = call_user_func_array('array_merge_recursive', $js['settings']['data']);
|
||||
|
||||
$commands = array();
|
||||
$commands[] = ajax_command_replace(NULL, $output, $settings);
|
||||
return array('#type' => 'ajax', '#commands' => $commands);
|
||||
}
|
||||
@@ -358,6 +357,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 +370,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'],
|
||||
@@ -462,13 +465,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 +482,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 +515,35 @@ 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)) {
|
||||
$fid = $file->fid;
|
||||
}
|
||||
// If the current user doesn't have access, don't let the file be
|
||||
// changed.
|
||||
else {
|
||||
$force_default = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
@@ -139,7 +139,7 @@ class FileFieldTestCase extends DrupalWebTestCase {
|
||||
// Save at least one revision to better simulate a real site.
|
||||
$this->drupalCreateNode(get_object_vars($node));
|
||||
$node = node_load($nid, NULL, TRUE);
|
||||
$this->assertNotEqual($nid, $node->vid, t('Node revision exists.'));
|
||||
$this->assertNotEqual($nid, $node->vid, 'Node revision exists.');
|
||||
}
|
||||
|
||||
// Attach a file to the node.
|
||||
@@ -180,7 +180,7 @@ class FileFieldTestCase extends DrupalWebTestCase {
|
||||
* Asserts that a file exists physically on disk.
|
||||
*/
|
||||
function assertFileExists($file, $message = NULL) {
|
||||
$message = isset($message) ? $message : t('File %file exists on the disk.', array('%file' => $file->uri));
|
||||
$message = isset($message) ? $message : format_string('File %file exists on the disk.', array('%file' => $file->uri));
|
||||
$this->assertTrue(is_file($file->uri), $message);
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ class FileFieldTestCase extends DrupalWebTestCase {
|
||||
function assertFileEntryExists($file, $message = NULL) {
|
||||
entity_get_controller('file')->resetCache();
|
||||
$db_file = file_load($file->fid);
|
||||
$message = isset($message) ? $message : t('File %file exists in database at the correct path.', array('%file' => $file->uri));
|
||||
$message = isset($message) ? $message : format_string('File %file exists in database at the correct path.', array('%file' => $file->uri));
|
||||
$this->assertEqual($db_file->uri, $file->uri, $message);
|
||||
}
|
||||
|
||||
@@ -198,7 +198,7 @@ class FileFieldTestCase extends DrupalWebTestCase {
|
||||
* Asserts that a file does not exist on disk.
|
||||
*/
|
||||
function assertFileNotExists($file, $message = NULL) {
|
||||
$message = isset($message) ? $message : t('File %file exists on the disk.', array('%file' => $file->uri));
|
||||
$message = isset($message) ? $message : format_string('File %file exists on the disk.', array('%file' => $file->uri));
|
||||
$this->assertFalse(is_file($file->uri), $message);
|
||||
}
|
||||
|
||||
@@ -207,7 +207,7 @@ class FileFieldTestCase extends DrupalWebTestCase {
|
||||
*/
|
||||
function assertFileEntryNotExists($file, $message) {
|
||||
entity_get_controller('file')->resetCache();
|
||||
$message = isset($message) ? $message : t('File %file exists in database at the correct path.', array('%file' => $file->uri));
|
||||
$message = isset($message) ? $message : format_string('File %file exists in database at the correct path.', array('%file' => $file->uri));
|
||||
$this->assertFalse(file_load($file->fid), $message);
|
||||
}
|
||||
|
||||
@@ -215,11 +215,133 @@ class FileFieldTestCase extends DrupalWebTestCase {
|
||||
* Asserts that a file's status is set to permanent in the database.
|
||||
*/
|
||||
function assertFileIsPermanent($file, $message = NULL) {
|
||||
$message = isset($message) ? $message : t('File %file is permanent.', array('%file' => $file->uri));
|
||||
$message = isset($message) ? $message : format_string('File %file is permanent.', array('%file' => $file->uri));
|
||||
$this->assertTrue($file->status == FILE_STATUS_PERMANENT, $message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests adding a file to a non-node entity.
|
||||
*/
|
||||
class FileTaxonomyTermTestCase extends DrupalWebTestCase {
|
||||
protected $admin_user;
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Taxonomy term file test',
|
||||
'description' => 'Tests adding a file to a non-node entity.',
|
||||
'group' => 'File',
|
||||
);
|
||||
}
|
||||
|
||||
public function setUp() {
|
||||
$modules[] = 'file';
|
||||
$modules[] = 'taxonomy';
|
||||
parent::setUp($modules);
|
||||
$this->admin_user = $this->drupalCreateUser(array('access content', 'access administration pages', 'administer site configuration', 'administer taxonomy'));
|
||||
$this->drupalLogin($this->admin_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a file field and attaches it to the "Tags" taxonomy vocabulary.
|
||||
*
|
||||
* @param $name
|
||||
* The field name of the file field to create.
|
||||
* @param $uri_scheme
|
||||
* The URI scheme to use for the file field (for example, "private" to
|
||||
* create a field that stores private files or "public" to create a field
|
||||
* that stores public files).
|
||||
*/
|
||||
protected function createAttachFileField($name, $uri_scheme) {
|
||||
$field = array(
|
||||
'field_name' => $name,
|
||||
'type' => 'file',
|
||||
'settings' => array(
|
||||
'uri_scheme' => $uri_scheme,
|
||||
),
|
||||
'cardinality' => 1,
|
||||
);
|
||||
field_create_field($field);
|
||||
// Attach an instance of it.
|
||||
$instance = array(
|
||||
'field_name' => $name,
|
||||
'label' => 'File',
|
||||
'entity_type' => 'taxonomy_term',
|
||||
'bundle' => 'tags',
|
||||
'required' => FALSE,
|
||||
'settings' => array(),
|
||||
'widget' => array(
|
||||
'type' => 'file_generic',
|
||||
'settings' => array(),
|
||||
),
|
||||
);
|
||||
field_create_instance($instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a public file can be attached to a taxonomy term.
|
||||
*
|
||||
* This is a regression test for https://www.drupal.org/node/2305017.
|
||||
*/
|
||||
public function testTermFilePublic() {
|
||||
$this->_testTermFile('public');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a private file can be attached to a taxonomy term.
|
||||
*
|
||||
* This is a regression test for https://www.drupal.org/node/2305017.
|
||||
*/
|
||||
public function testTermFilePrivate() {
|
||||
$this->_testTermFile('private');
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs tests for attaching a file field to a taxonomy term.
|
||||
*
|
||||
* @param $uri_scheme
|
||||
* The URI scheme to use for the file field, either "public" or "private".
|
||||
*/
|
||||
protected function _testTermFile($uri_scheme) {
|
||||
$field_name = strtolower($this->randomName());
|
||||
$this->createAttachFileField($field_name, $uri_scheme);
|
||||
// Get a file to upload.
|
||||
$file = current($this->drupalGetTestFiles('text'));
|
||||
// Add a filesize property to files as would be read by file_load().
|
||||
$file->filesize = filesize($file->uri);
|
||||
$langcode = LANGUAGE_NONE;
|
||||
$edit = array(
|
||||
"name" => $this->randomName(),
|
||||
);
|
||||
// Attach a file to the term.
|
||||
$edit['files[' . $field_name . '_' . $langcode . '_0]'] = drupal_realpath($file->uri);
|
||||
$this->drupalPost("admin/structure/taxonomy/tags/add", $edit, t('Save'));
|
||||
// Find the term ID we just created.
|
||||
$tid = db_query_range('SELECT tid FROM {taxonomy_term_data} ORDER BY tid DESC', 0, 1)->fetchField();
|
||||
$terms = entity_load('taxonomy_term', array($tid));
|
||||
$term = $terms[$tid];
|
||||
$fid = $term->{$field_name}[LANGUAGE_NONE][0]['fid'];
|
||||
// Check that the uploaded file is present on the edit form.
|
||||
$this->drupalGet("taxonomy/term/$tid/edit");
|
||||
$file_input_name = $field_name . '[' . LANGUAGE_NONE . '][0][fid]';
|
||||
$this->assertFieldByXpath('//input[@type="hidden" and @name="' . $file_input_name . '"]', $fid, 'File is attached on edit form.');
|
||||
// Edit the term and change name without changing the file.
|
||||
$edit = array(
|
||||
"name" => $this->randomName(),
|
||||
);
|
||||
$this->drupalPost("taxonomy/term/$tid/edit", $edit, t('Save'));
|
||||
// Check that the uploaded file is still present on the edit form.
|
||||
$this->drupalGet("taxonomy/term/$tid/edit");
|
||||
$file_input_name = $field_name . '[' . LANGUAGE_NONE . '][0][fid]';
|
||||
$this->assertFieldByXpath('//input[@type="hidden" and @name="' . $file_input_name . '"]', $fid, 'File is attached on edit form.');
|
||||
// Load term while resetting the cache.
|
||||
$terms = entity_load('taxonomy_term', array($tid), array(), TRUE);
|
||||
$term = $terms[$tid];
|
||||
$this->assertTrue(!empty($term->{$field_name}[LANGUAGE_NONE]), 'Term has attached files.');
|
||||
$this->assertEqual($term->{$field_name}[LANGUAGE_NONE][0]['fid'], $fid, 'Same File ID is attached to the term.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the 'managed_file' element type.
|
||||
*
|
||||
@@ -253,19 +375,19 @@ class FileManagedFileElementTestCase extends FileFieldTestCase {
|
||||
|
||||
// Submit without a file.
|
||||
$this->drupalPost($path, array(), t('Save'));
|
||||
$this->assertRaw(t('The file id is %fid.', array('%fid' => 0)), t('Submitted without a file.'));
|
||||
$this->assertRaw(t('The file id is %fid.', array('%fid' => 0)), 'Submitted without a file.');
|
||||
|
||||
// Submit a new file, without using the Upload button.
|
||||
$last_fid_prior = $this->getLastFileId();
|
||||
$edit = array('files[' . $input_base_name . ']' => drupal_realpath($test_file->uri));
|
||||
$this->drupalPost($path, $edit, t('Save'));
|
||||
$last_fid = $this->getLastFileId();
|
||||
$this->assertTrue($last_fid > $last_fid_prior, t('New file got saved.'));
|
||||
$this->assertRaw(t('The file id is %fid.', array('%fid' => $last_fid)), t('Submit handler has correct file info.'));
|
||||
$this->assertTrue($last_fid > $last_fid_prior, 'New file got saved.');
|
||||
$this->assertRaw(t('The file id is %fid.', array('%fid' => $last_fid)), 'Submit handler has correct file info.');
|
||||
|
||||
// Submit no new input, but with a default file.
|
||||
$this->drupalPost($path . '/' . $last_fid, array(), t('Save'));
|
||||
$this->assertRaw(t('The file id is %fid.', array('%fid' => $last_fid)), t('Empty submission did not change an existing file.'));
|
||||
$this->assertRaw(t('The file id is %fid.', array('%fid' => $last_fid)), 'Empty submission did not change an existing file.');
|
||||
|
||||
// Now, test the Upload and Remove buttons, with and without Ajax.
|
||||
foreach (array(FALSE, TRUE) as $ajax) {
|
||||
@@ -280,9 +402,9 @@ class FileManagedFileElementTestCase extends FileFieldTestCase {
|
||||
$this->drupalPost(NULL, $edit, t('Upload'));
|
||||
}
|
||||
$last_fid = $this->getLastFileId();
|
||||
$this->assertTrue($last_fid > $last_fid_prior, t('New file got uploaded.'));
|
||||
$this->assertTrue($last_fid > $last_fid_prior, 'New file got uploaded.');
|
||||
$this->drupalPost(NULL, array(), t('Save'));
|
||||
$this->assertRaw(t('The file id is %fid.', array('%fid' => $last_fid)), t('Submit handler has correct file info.'));
|
||||
$this->assertRaw(t('The file id is %fid.', array('%fid' => $last_fid)), 'Submit handler has correct file info.');
|
||||
|
||||
// Remove, then Submit.
|
||||
$this->drupalGet($path . '/' . $last_fid);
|
||||
@@ -293,7 +415,7 @@ class FileManagedFileElementTestCase extends FileFieldTestCase {
|
||||
$this->drupalPost(NULL, array(), t('Remove'));
|
||||
}
|
||||
$this->drupalPost(NULL, array(), t('Save'));
|
||||
$this->assertRaw(t('The file id is %fid.', array('%fid' => 0)), t('Submission after file removal was successful.'));
|
||||
$this->assertRaw(t('The file id is %fid.', array('%fid' => 0)), 'Submission after file removal was successful.');
|
||||
|
||||
// Upload, then Remove, then Submit.
|
||||
$this->drupalGet($path);
|
||||
@@ -307,7 +429,7 @@ class FileManagedFileElementTestCase extends FileFieldTestCase {
|
||||
$this->drupalPost(NULL, array(), t('Remove'));
|
||||
}
|
||||
$this->drupalPost(NULL, array(), t('Save'));
|
||||
$this->assertRaw(t('The file id is %fid.', array('%fid' => 0)), t('Submission after file upload and removal was successful.'));
|
||||
$this->assertRaw(t('The file id is %fid.', array('%fid' => 0)), 'Submission after file upload and removal was successful.');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -350,16 +472,25 @@ class FileFieldWidgetTestCase extends FileFieldTestCase {
|
||||
$nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
|
||||
$node = node_load($nid, NULL, TRUE);
|
||||
$node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
|
||||
$this->assertFileExists($node_file, t('New file saved to disk on node creation.'));
|
||||
$this->assertFileExists($node_file, 'New file saved to disk on node creation.');
|
||||
|
||||
// Test that running field_attach_update() leaves the file intact.
|
||||
$field = new stdClass();
|
||||
$field->type = $type_name;
|
||||
$field->nid = $nid;
|
||||
field_attach_update('node', $field);
|
||||
$node = node_load($nid);
|
||||
$node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
|
||||
$this->assertFileExists($node_file, 'New file still saved to disk on field update.');
|
||||
|
||||
// Ensure the file can be downloaded.
|
||||
$this->drupalGet(file_create_url($node_file->uri));
|
||||
$this->assertResponse(200, t('Confirmed that the generated URL is correct by downloading the shipped file.'));
|
||||
$this->assertResponse(200, 'Confirmed that the generated URL is correct by downloading the shipped file.');
|
||||
|
||||
// Ensure the edit page has a remove button instead of an upload button.
|
||||
$this->drupalGet("node/$nid/edit");
|
||||
$this->assertNoFieldByXPath('//input[@type="submit"]', t('Upload'), t('Node with file does not display the "Upload" button.'));
|
||||
$this->assertFieldByXpath('//input[@type="submit"]', t('Remove'), t('Node with file displays the "Remove" button.'));
|
||||
$this->assertNoFieldByXPath('//input[@type="submit"]', t('Upload'), 'Node with file does not display the "Upload" button.');
|
||||
$this->assertFieldByXpath('//input[@type="submit"]', t('Remove'), 'Node with file displays the "Remove" button.');
|
||||
|
||||
// "Click" the remove button (emulating either a nojs or js submission).
|
||||
switch ($type) {
|
||||
@@ -373,13 +504,13 @@ class FileFieldWidgetTestCase extends FileFieldTestCase {
|
||||
}
|
||||
|
||||
// Ensure the page now has an upload button instead of a remove button.
|
||||
$this->assertNoFieldByXPath('//input[@type="submit"]', t('Remove'), t('After clicking the "Remove" button, it is no longer displayed.'));
|
||||
$this->assertFieldByXpath('//input[@type="submit"]', t('Upload'), t('After clicking the "Remove" button, the "Upload" button is displayed.'));
|
||||
$this->assertNoFieldByXPath('//input[@type="submit"]', t('Remove'), 'After clicking the "Remove" button, it is no longer displayed.');
|
||||
$this->assertFieldByXpath('//input[@type="submit"]', t('Upload'), 'After clicking the "Remove" button, the "Upload" button is displayed.');
|
||||
|
||||
// Save the node and ensure it does not have the file.
|
||||
$this->drupalPost(NULL, array(), t('Save'));
|
||||
$node = node_load($nid, NULL, TRUE);
|
||||
$this->assertTrue(empty($node->{$field_name}[LANGUAGE_NONE][0]['fid']), t('File was successfully removed from the node.'));
|
||||
$this->assertTrue(empty($node->{$field_name}[LANGUAGE_NONE][0]['fid']), 'File was successfully removed from the node.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,7 +554,7 @@ class FileFieldWidgetTestCase extends FileFieldTestCase {
|
||||
$this->drupalPost(NULL, $edit, t('Upload'));
|
||||
}
|
||||
}
|
||||
$this->assertNoFieldByXpath('//input[@type="submit"]', t('Upload'), t('After uploading 3 files for each field, the "Upload" button is no longer displayed.'));
|
||||
$this->assertNoFieldByXpath('//input[@type="submit"]', t('Upload'), 'After uploading 3 files for each field, the "Upload" button is no longer displayed.');
|
||||
|
||||
$num_expected_remove_buttons = 6;
|
||||
|
||||
@@ -440,7 +571,7 @@ class FileFieldWidgetTestCase extends FileFieldTestCase {
|
||||
// Ensure we have the expected number of Remove buttons, and that they
|
||||
// are numbered sequentially.
|
||||
$buttons = $this->xpath('//input[@type="submit" and @value="Remove"]');
|
||||
$this->assertTrue(is_array($buttons) && count($buttons) === $num_expected_remove_buttons, t('There are %n "Remove" buttons displayed (JSMode=%type).', array('%n' => $num_expected_remove_buttons, '%type' => $type)));
|
||||
$this->assertTrue(is_array($buttons) && count($buttons) === $num_expected_remove_buttons, format_string('There are %n "Remove" buttons displayed (JSMode=%type).', array('%n' => $num_expected_remove_buttons, '%type' => $type)));
|
||||
foreach ($buttons as $i => $button) {
|
||||
$key = $i >= $remaining ? $i - $remaining : $i;
|
||||
$check_field_name = $field_name2;
|
||||
@@ -482,17 +613,17 @@ class FileFieldWidgetTestCase extends FileFieldTestCase {
|
||||
// correct name.
|
||||
$upload_button_name = $current_field_name . '_' . LANGUAGE_NONE . '_' . $remaining . '_upload_button';
|
||||
$buttons = $this->xpath('//input[@type="submit" and @value="Upload" and @name=:name]', array(':name' => $upload_button_name));
|
||||
$this->assertTrue(is_array($buttons) && count($buttons) == 1, t('The upload button is displayed with the correct name (JSMode=%type).', array('%type' => $type)));
|
||||
$this->assertTrue(is_array($buttons) && count($buttons) == 1, format_string('The upload button is displayed with the correct name (JSMode=%type).', array('%type' => $type)));
|
||||
|
||||
// Ensure only at most one button per field is displayed.
|
||||
$buttons = $this->xpath('//input[@type="submit" and @value="Upload"]');
|
||||
$expected = $current_field_name == $field_name ? 1 : 2;
|
||||
$this->assertTrue(is_array($buttons) && count($buttons) == $expected, t('After removing a file, only one "Upload" button for each possible field is displayed (JSMode=%type).', array('%type' => $type)));
|
||||
$this->assertTrue(is_array($buttons) && count($buttons) == $expected, format_string('After removing a file, only one "Upload" button for each possible field is displayed (JSMode=%type).', array('%type' => $type)));
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the page now has no Remove buttons.
|
||||
$this->assertNoFieldByXPath('//input[@type="submit"]', t('Remove'), t('After removing all files, there is no "Remove" button displayed (JSMode=%type).', array('%type' => $type)));
|
||||
$this->assertNoFieldByXPath('//input[@type="submit"]', t('Remove'), format_string('After removing all files, there is no "Remove" button displayed (JSMode=%type).', array('%type' => $type)));
|
||||
|
||||
// Save the node and ensure it does not have any files.
|
||||
$this->drupalPost(NULL, array('title' => $this->randomName()), t('Save'));
|
||||
@@ -500,7 +631,7 @@ class FileFieldWidgetTestCase extends FileFieldTestCase {
|
||||
preg_match('/node\/([0-9]+)/', $this->getUrl(), $matches);
|
||||
$nid = $matches[1];
|
||||
$node = node_load($nid, NULL, TRUE);
|
||||
$this->assertTrue(empty($node->{$field_name}[LANGUAGE_NONE][0]['fid']), t('Node was successfully saved without any files.'));
|
||||
$this->assertTrue(empty($node->{$field_name}[LANGUAGE_NONE][0]['fid']), 'Node was successfully saved without any files.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -526,21 +657,21 @@ class FileFieldWidgetTestCase extends FileFieldTestCase {
|
||||
$nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
|
||||
$node = node_load($nid, NULL, TRUE);
|
||||
$node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
|
||||
$this->assertFileExists($node_file, t('New file saved to disk on node creation.'));
|
||||
$this->assertFileExists($node_file, 'New file saved to disk on node creation.');
|
||||
|
||||
// Ensure the private file is available to the user who uploaded it.
|
||||
$this->drupalGet(file_create_url($node_file->uri));
|
||||
$this->assertResponse(200, t('Confirmed that the generated URL is correct by downloading the shipped file.'));
|
||||
$this->assertResponse(200, 'Confirmed that the generated URL is correct by downloading the shipped file.');
|
||||
|
||||
// Ensure we can't change 'uri_scheme' field settings while there are some
|
||||
// entities with uploaded files.
|
||||
$this->drupalGet("admin/structure/types/manage/$type_name/fields/$field_name");
|
||||
$this->assertFieldByXpath('//input[@id="edit-field-settings-uri-scheme-public" and @disabled="disabled"]', 'public', t('Upload destination setting disabled.'));
|
||||
$this->assertFieldByXpath('//input[@id="edit-field-settings-uri-scheme-public" and @disabled="disabled"]', 'public', 'Upload destination setting disabled.');
|
||||
|
||||
// Delete node and confirm that setting could be changed.
|
||||
node_delete($nid);
|
||||
$this->drupalGet("admin/structure/types/manage/$type_name/fields/$field_name");
|
||||
$this->assertFieldByXpath('//input[@id="edit-field-settings-uri-scheme-public" and not(@disabled)]', 'public', t('Upload destination setting enabled.'));
|
||||
$this->assertFieldByXpath('//input[@id="edit-field-settings-uri-scheme-public" and not(@disabled)]', 'public', 'Upload destination setting enabled.');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -592,17 +723,17 @@ class FileFieldWidgetTestCase extends FileFieldTestCase {
|
||||
|
||||
$comment = comment_load($cid);
|
||||
$comment_file = (object) $comment->{'field_' . $name}[LANGUAGE_NONE][0];
|
||||
$this->assertFileExists($comment_file, t('New file saved to disk on node creation.'));
|
||||
$this->assertFileExists($comment_file, 'New file saved to disk on node creation.');
|
||||
// Test authenticated file download.
|
||||
$url = file_create_url($comment_file->uri);
|
||||
$this->assertNotEqual($url, NULL, t('Confirmed that the URL is valid'));
|
||||
$this->assertNotEqual($url, NULL, 'Confirmed that the URL is valid');
|
||||
$this->drupalGet(file_create_url($comment_file->uri));
|
||||
$this->assertResponse(200, t('Confirmed that the generated URL is correct by downloading the shipped file.'));
|
||||
$this->assertResponse(200, 'Confirmed that the generated URL is correct by downloading the shipped file.');
|
||||
|
||||
// Test anonymous file download.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet(file_create_url($comment_file->uri));
|
||||
$this->assertResponse(403, t('Confirmed that access is denied for the file without the needed permission.'));
|
||||
$this->assertResponse(403, 'Confirmed that access is denied for the file without the needed permission.');
|
||||
|
||||
// Unpublishes node.
|
||||
$this->drupalLogin($this->admin_user);
|
||||
@@ -614,7 +745,7 @@ class FileFieldWidgetTestCase extends FileFieldTestCase {
|
||||
// Ensures normal user can no longer download the file.
|
||||
$this->drupalLogin($user);
|
||||
$this->drupalGet(file_create_url($comment_file->uri));
|
||||
$this->assertResponse(403, t('Confirmed that access is denied for the file without the needed permission.'));
|
||||
$this->assertResponse(403, 'Confirmed that access is denied for the file without the needed permission.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -661,25 +792,25 @@ class FileFieldRevisionTestCase extends FileFieldTestCase {
|
||||
$node = node_load($nid, NULL, TRUE);
|
||||
$node_file_r1 = (object) $node->{$field_name}[LANGUAGE_NONE][0];
|
||||
$node_vid_r1 = $node->vid;
|
||||
$this->assertFileExists($node_file_r1, t('New file saved to disk on node creation.'));
|
||||
$this->assertFileEntryExists($node_file_r1, t('File entry exists in database on node creation.'));
|
||||
$this->assertFileIsPermanent($node_file_r1, t('File is permanent.'));
|
||||
$this->assertFileExists($node_file_r1, 'New file saved to disk on node creation.');
|
||||
$this->assertFileEntryExists($node_file_r1, 'File entry exists in database on node creation.');
|
||||
$this->assertFileIsPermanent($node_file_r1, 'File is permanent.');
|
||||
|
||||
// Upload another file to the same node in a new revision.
|
||||
$this->replaceNodeFile($test_file, $field_name, $nid);
|
||||
$node = node_load($nid, NULL, TRUE);
|
||||
$node_file_r2 = (object) $node->{$field_name}[LANGUAGE_NONE][0];
|
||||
$node_vid_r2 = $node->vid;
|
||||
$this->assertFileExists($node_file_r2, t('Replacement file exists on disk after creating new revision.'));
|
||||
$this->assertFileEntryExists($node_file_r2, t('Replacement file entry exists in database after creating new revision.'));
|
||||
$this->assertFileIsPermanent($node_file_r2, t('Replacement file is permanent.'));
|
||||
$this->assertFileExists($node_file_r2, 'Replacement file exists on disk after creating new revision.');
|
||||
$this->assertFileEntryExists($node_file_r2, 'Replacement file entry exists in database after creating new revision.');
|
||||
$this->assertFileIsPermanent($node_file_r2, 'Replacement file is permanent.');
|
||||
|
||||
// Check that the original file is still in place on the first revision.
|
||||
$node = node_load($nid, $node_vid_r1, TRUE);
|
||||
$this->assertEqual($node_file_r1, (object) $node->{$field_name}[LANGUAGE_NONE][0], t('Original file still in place after replacing file in new revision.'));
|
||||
$this->assertFileExists($node_file_r1, t('Original file still in place after replacing file in new revision.'));
|
||||
$this->assertFileEntryExists($node_file_r1, t('Original file entry still in place after replacing file in new revision'));
|
||||
$this->assertFileIsPermanent($node_file_r1, t('Original file is still permanent.'));
|
||||
$this->assertEqual($node_file_r1, (object) $node->{$field_name}[LANGUAGE_NONE][0], 'Original file still in place after replacing file in new revision.');
|
||||
$this->assertFileExists($node_file_r1, 'Original file still in place after replacing file in new revision.');
|
||||
$this->assertFileEntryExists($node_file_r1, 'Original file entry still in place after replacing file in new revision');
|
||||
$this->assertFileIsPermanent($node_file_r1, 'Original file is still permanent.');
|
||||
|
||||
// Save a new version of the node without any changes.
|
||||
// Check that the file is still the same as the previous revision.
|
||||
@@ -687,23 +818,23 @@ class FileFieldRevisionTestCase extends FileFieldTestCase {
|
||||
$node = node_load($nid, NULL, TRUE);
|
||||
$node_file_r3 = (object) $node->{$field_name}[LANGUAGE_NONE][0];
|
||||
$node_vid_r3 = $node->vid;
|
||||
$this->assertEqual($node_file_r2, $node_file_r3, t('Previous revision file still in place after creating a new revision without a new file.'));
|
||||
$this->assertFileIsPermanent($node_file_r3, t('New revision file is permanent.'));
|
||||
$this->assertEqual($node_file_r2, $node_file_r3, 'Previous revision file still in place after creating a new revision without a new file.');
|
||||
$this->assertFileIsPermanent($node_file_r3, 'New revision file is permanent.');
|
||||
|
||||
// Revert to the first revision and check that the original file is active.
|
||||
$this->drupalPost('node/' . $nid . '/revisions/' . $node_vid_r1 . '/revert', array(), t('Revert'));
|
||||
$node = node_load($nid, NULL, TRUE);
|
||||
$node_file_r4 = (object) $node->{$field_name}[LANGUAGE_NONE][0];
|
||||
$node_vid_r4 = $node->vid;
|
||||
$this->assertEqual($node_file_r1, $node_file_r4, t('Original revision file still in place after reverting to the original revision.'));
|
||||
$this->assertFileIsPermanent($node_file_r4, t('Original revision file still permanent after reverting to the original revision.'));
|
||||
$this->assertEqual($node_file_r1, $node_file_r4, 'Original revision file still in place after reverting to the original revision.');
|
||||
$this->assertFileIsPermanent($node_file_r4, 'Original revision file still permanent after reverting to the original revision.');
|
||||
|
||||
// Delete the second revision and check that the file is kept (since it is
|
||||
// still being used by the third revision).
|
||||
$this->drupalPost('node/' . $nid . '/revisions/' . $node_vid_r2 . '/delete', array(), t('Delete'));
|
||||
$this->assertFileExists($node_file_r3, t('Second file is still available after deleting second revision, since it is being used by the third revision.'));
|
||||
$this->assertFileEntryExists($node_file_r3, t('Second file entry is still available after deleting second revision, since it is being used by the third revision.'));
|
||||
$this->assertFileIsPermanent($node_file_r3, t('Second file entry is still permanent after deleting second revision, since it is being used by the third revision.'));
|
||||
$this->assertFileExists($node_file_r3, 'Second file is still available after deleting second revision, since it is being used by the third revision.');
|
||||
$this->assertFileEntryExists($node_file_r3, 'Second file entry is still available after deleting second revision, since it is being used by the third revision.');
|
||||
$this->assertFileIsPermanent($node_file_r3, 'Second file entry is still permanent after deleting second revision, since it is being used by the third revision.');
|
||||
|
||||
// Attach the second file to a user.
|
||||
$user = $this->drupalCreateUser();
|
||||
@@ -714,9 +845,9 @@ class FileFieldRevisionTestCase extends FileFieldTestCase {
|
||||
|
||||
// Delete the third revision and check that the file is not deleted yet.
|
||||
$this->drupalPost('node/' . $nid . '/revisions/' . $node_vid_r3 . '/delete', array(), t('Delete'));
|
||||
$this->assertFileExists($node_file_r3, t('Second file is still available after deleting third revision, since it is being used by the user.'));
|
||||
$this->assertFileEntryExists($node_file_r3, t('Second file entry is still available after deleting third revision, since it is being used by the user.'));
|
||||
$this->assertFileIsPermanent($node_file_r3, t('Second file entry is still permanent after deleting third revision, since it is being used by the user.'));
|
||||
$this->assertFileExists($node_file_r3, 'Second file is still available after deleting third revision, since it is being used by the user.');
|
||||
$this->assertFileEntryExists($node_file_r3, 'Second file entry is still available after deleting third revision, since it is being used by the user.');
|
||||
$this->assertFileIsPermanent($node_file_r3, 'Second file entry is still permanent after deleting third revision, since it is being used by the user.');
|
||||
|
||||
// Delete the user and check that the file is also deleted.
|
||||
user_delete($user->uid);
|
||||
@@ -724,13 +855,13 @@ class FileFieldRevisionTestCase extends FileFieldTestCase {
|
||||
// not be necessary here. The file really is deleted, but stream wrappers
|
||||
// doesn't seem to think so unless we clear the PHP file stat() cache.
|
||||
clearstatcache();
|
||||
$this->assertFileNotExists($node_file_r3, t('Second file is now deleted after deleting third revision, since it is no longer being used by any other nodes.'));
|
||||
$this->assertFileEntryNotExists($node_file_r3, t('Second file entry is now deleted after deleting third revision, since it is no longer being used by any other nodes.'));
|
||||
$this->assertFileNotExists($node_file_r3, 'Second file is now deleted after deleting third revision, since it is no longer being used by any other nodes.');
|
||||
$this->assertFileEntryNotExists($node_file_r3, 'Second file entry is now deleted after deleting third revision, since it is no longer being used by any other nodes.');
|
||||
|
||||
// Delete the entire node and check that the original file is deleted.
|
||||
$this->drupalPost('node/' . $nid . '/delete', array(), t('Delete'));
|
||||
$this->assertFileNotExists($node_file_r1, t('Original file is deleted after deleting the entire node with two revisions remaining.'));
|
||||
$this->assertFileEntryNotExists($node_file_r1, t('Original file entry is deleted after deleting the entire node with two revisions remaining.'));
|
||||
$this->assertFileNotExists($node_file_r1, 'Original file is deleted after deleting the entire node with two revisions remaining.');
|
||||
$this->assertFileEntryNotExists($node_file_r1, 'Original file entry is deleted after deleting the entire node with two revisions remaining.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -755,6 +886,7 @@ class FileFieldDisplayTestCase extends FileFieldTestCase {
|
||||
$field_settings = array(
|
||||
'display_field' => '1',
|
||||
'display_default' => '1',
|
||||
'cardinality' => FIELD_CARDINALITY_UNLIMITED,
|
||||
);
|
||||
$instance_settings = array(
|
||||
'description_field' => '1',
|
||||
@@ -774,7 +906,7 @@ class FileFieldDisplayTestCase extends FileFieldTestCase {
|
||||
);
|
||||
$this->drupalPost("admin/structure/types/manage/$type_name/display", $edit, t('Save'));
|
||||
$this->drupalGet('node/' . $node->nid);
|
||||
$this->assertNoText($field_name, t('Field label is hidden when no file attached for formatter %formatter', array('%formatter' => $formatter)));
|
||||
$this->assertNoText($field_name, format_string('Field label is hidden when no file attached for formatter %formatter', array('%formatter' => $formatter)));
|
||||
}
|
||||
|
||||
$test_file = $this->getTestFile('text');
|
||||
@@ -787,14 +919,25 @@ class FileFieldDisplayTestCase extends FileFieldTestCase {
|
||||
$node = node_load($nid, NULL, TRUE);
|
||||
$node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
|
||||
$default_output = theme('file_link', array('file' => $node_file));
|
||||
$this->assertRaw($default_output, t('Default formatter displaying correctly on full node view.'));
|
||||
$this->assertRaw($default_output, 'Default formatter displaying correctly on full node view.');
|
||||
|
||||
// Turn the "display" option off and check that the file is no longer displayed.
|
||||
$edit = array($field_name . '[' . LANGUAGE_NONE . '][0][display]' => FALSE);
|
||||
$this->drupalPost('node/' . $nid . '/edit', $edit, t('Save'));
|
||||
|
||||
$this->assertNoRaw($default_output, t('Field is hidden when "display" option is unchecked.'));
|
||||
$this->assertNoRaw($default_output, 'Field is hidden when "display" option is unchecked.');
|
||||
|
||||
// Test that fields appear as expected during the preview.
|
||||
// Add a second file.
|
||||
$name = 'files[' . $field_name . '_' . LANGUAGE_NONE . '_1]';
|
||||
$edit[$name] = drupal_realpath($test_file->uri);
|
||||
|
||||
// Uncheck the display checkboxes and go to the preview.
|
||||
$edit[$field_name . '[' . LANGUAGE_NONE . '][0][display]'] = FALSE;
|
||||
$edit[$field_name . '[' . LANGUAGE_NONE . '][1][display]'] = FALSE;
|
||||
$this->drupalPost('node/' . $nid . '/edit', $edit, t('Preview'));
|
||||
$this->assertRaw($field_name . '[' . LANGUAGE_NONE . '][0][display]', 'First file appears as expected.');
|
||||
$this->assertRaw($field_name . '[' . LANGUAGE_NONE . '][1][display]', 'Second file appears as expected.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -829,17 +972,17 @@ class FileFieldValidateTestCase extends FileFieldTestCase {
|
||||
$langcode = LANGUAGE_NONE;
|
||||
$edit = array("title" => $this->randomName());
|
||||
$this->drupalPost('node/add/' . $type_name, $edit, t('Save'));
|
||||
$this->assertRaw(t('!title field is required.', array('!title' => $instance['label'])), t('Node save failed when required file field was empty.'));
|
||||
$this->assertRaw(t('!title field is required.', array('!title' => $instance['label'])), 'Node save failed when required file field was empty.');
|
||||
|
||||
// Create a new node with the uploaded file.
|
||||
$nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
|
||||
$this->assertTrue($nid !== FALSE, t('uploadNodeFile(@test_file, @field_name, @type_name) succeeded', array('@test_file' => $test_file->uri, '@field_name' => $field_name, '@type_name' => $type_name)));
|
||||
$this->assertTrue($nid !== FALSE, format_string('uploadNodeFile(@test_file, @field_name, @type_name) succeeded', array('@test_file' => $test_file->uri, '@field_name' => $field_name, '@type_name' => $type_name)));
|
||||
|
||||
$node = node_load($nid, NULL, TRUE);
|
||||
|
||||
$node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
|
||||
$this->assertFileExists($node_file, t('File exists after uploading to the required field.'));
|
||||
$this->assertFileEntryExists($node_file, t('File entry exists after uploading to the required field.'));
|
||||
$this->assertFileExists($node_file, 'File exists after uploading to the required field.');
|
||||
$this->assertFileEntryExists($node_file, 'File entry exists after uploading to the required field.');
|
||||
|
||||
// Try again with a multiple value field.
|
||||
field_delete_field($field_name);
|
||||
@@ -848,14 +991,14 @@ class FileFieldValidateTestCase extends FileFieldTestCase {
|
||||
// Try to post a new node without uploading a file in the multivalue field.
|
||||
$edit = array('title' => $this->randomName());
|
||||
$this->drupalPost('node/add/' . $type_name, $edit, t('Save'));
|
||||
$this->assertRaw(t('!title field is required.', array('!title' => $instance['label'])), t('Node save failed when required multiple value file field was empty.'));
|
||||
$this->assertRaw(t('!title field is required.', array('!title' => $instance['label'])), 'Node save failed when required multiple value file field was empty.');
|
||||
|
||||
// Create a new node with the uploaded file into the multivalue field.
|
||||
$nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
|
||||
$node = node_load($nid, NULL, TRUE);
|
||||
$node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
|
||||
$this->assertFileExists($node_file, t('File exists after uploading to the required multiple value field.'));
|
||||
$this->assertFileEntryExists($node_file, t('File entry exists after uploading to the required multipel value field.'));
|
||||
$this->assertFileExists($node_file, 'File exists after uploading to the required multiple value field.');
|
||||
$this->assertFileEntryExists($node_file, 'File entry exists after uploading to the required multipel value field.');
|
||||
|
||||
// Remove our file field.
|
||||
field_delete_field($field_name);
|
||||
@@ -890,13 +1033,13 @@ class FileFieldValidateTestCase extends FileFieldTestCase {
|
||||
$nid = $this->uploadNodeFile($small_file, $field_name, $type_name);
|
||||
$node = node_load($nid, NULL, TRUE);
|
||||
$node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
|
||||
$this->assertFileExists($node_file, t('File exists after uploading a file (%filesize) under the max limit (%maxsize).', array('%filesize' => format_size($small_file->filesize), '%maxsize' => $max_filesize)));
|
||||
$this->assertFileEntryExists($node_file, t('File entry exists after uploading a file (%filesize) under the max limit (%maxsize).', array('%filesize' => format_size($small_file->filesize), '%maxsize' => $max_filesize)));
|
||||
$this->assertFileExists($node_file, format_string('File exists after uploading a file (%filesize) under the max limit (%maxsize).', array('%filesize' => format_size($small_file->filesize), '%maxsize' => $max_filesize)));
|
||||
$this->assertFileEntryExists($node_file, format_string('File entry exists after uploading a file (%filesize) under the max limit (%maxsize).', array('%filesize' => format_size($small_file->filesize), '%maxsize' => $max_filesize)));
|
||||
|
||||
// Check that uploading the large file fails (1M limit).
|
||||
$nid = $this->uploadNodeFile($large_file, $field_name, $type_name);
|
||||
$error_message = t('The file is %filesize exceeding the maximum file size of %maxsize.', array('%filesize' => format_size($large_file->filesize), '%maxsize' => format_size($file_limit)));
|
||||
$this->assertRaw($error_message, t('Node save failed when file (%filesize) exceeded the max upload size (%maxsize).', array('%filesize' => format_size($large_file->filesize), '%maxsize' => $max_filesize)));
|
||||
$this->assertRaw($error_message, format_string('Node save failed when file (%filesize) exceeded the max upload size (%maxsize).', array('%filesize' => format_size($large_file->filesize), '%maxsize' => $max_filesize)));
|
||||
}
|
||||
|
||||
// Turn off the max filesize.
|
||||
@@ -906,8 +1049,8 @@ class FileFieldValidateTestCase extends FileFieldTestCase {
|
||||
$nid = $this->uploadNodeFile($large_file, $field_name, $type_name);
|
||||
$node = node_load($nid, NULL, TRUE);
|
||||
$node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
|
||||
$this->assertFileExists($node_file, t('File exists after uploading a file (%filesize) with no max limit.', array('%filesize' => format_size($large_file->filesize))));
|
||||
$this->assertFileEntryExists($node_file, t('File entry exists after uploading a file (%filesize) with no max limit.', array('%filesize' => format_size($large_file->filesize))));
|
||||
$this->assertFileExists($node_file, format_string('File exists after uploading a file (%filesize) with no max limit.', array('%filesize' => format_size($large_file->filesize))));
|
||||
$this->assertFileEntryExists($node_file, format_string('File entry exists after uploading a file (%filesize) with no max limit.', array('%filesize' => format_size($large_file->filesize))));
|
||||
|
||||
// Remove our file field.
|
||||
field_delete_field($field_name);
|
||||
@@ -933,8 +1076,8 @@ class FileFieldValidateTestCase extends FileFieldTestCase {
|
||||
$nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
|
||||
$node = node_load($nid, NULL, TRUE);
|
||||
$node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
|
||||
$this->assertFileExists($node_file, t('File exists after uploading a file with no extension checking.'));
|
||||
$this->assertFileEntryExists($node_file, t('File entry exists after uploading a file with no extension checking.'));
|
||||
$this->assertFileExists($node_file, 'File exists after uploading a file with no extension checking.');
|
||||
$this->assertFileEntryExists($node_file, 'File entry exists after uploading a file with no extension checking.');
|
||||
|
||||
// Enable extension checking for text files.
|
||||
$this->updateFileField($field_name, $type_name, array('file_extensions' => 'txt'));
|
||||
@@ -942,7 +1085,7 @@ class FileFieldValidateTestCase extends FileFieldTestCase {
|
||||
// Check that the file with the wrong extension cannot be uploaded.
|
||||
$nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
|
||||
$error_message = t('Only files with the following extensions are allowed: %files-allowed.', array('%files-allowed' => 'txt'));
|
||||
$this->assertRaw($error_message, t('Node save failed when file uploaded with the wrong extension.'));
|
||||
$this->assertRaw($error_message, 'Node save failed when file uploaded with the wrong extension.');
|
||||
|
||||
// Enable extension checking for text and image files.
|
||||
$this->updateFileField($field_name, $type_name, array('file_extensions' => "txt $test_file_extension"));
|
||||
@@ -951,8 +1094,8 @@ class FileFieldValidateTestCase extends FileFieldTestCase {
|
||||
$nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
|
||||
$node = node_load($nid, NULL, TRUE);
|
||||
$node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
|
||||
$this->assertFileExists($node_file, t('File exists after uploading a file with extension checking.'));
|
||||
$this->assertFileEntryExists($node_file, t('File entry exists after uploading a file with extension checking.'));
|
||||
$this->assertFileExists($node_file, 'File exists after uploading a file with extension checking.');
|
||||
$this->assertFileEntryExists($node_file, 'File entry exists after uploading a file with extension checking.');
|
||||
|
||||
// Remove our file field.
|
||||
field_delete_field($field_name);
|
||||
@@ -986,7 +1129,7 @@ class FileFieldPathTestCase extends FileFieldTestCase {
|
||||
// Check that the file was uploaded to the file root.
|
||||
$node = node_load($nid, NULL, TRUE);
|
||||
$node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
|
||||
$this->assertPathMatch('public://' . $test_file->filename, $node_file->uri, t('The file %file was uploaded to the correct path.', array('%file' => $node_file->uri)));
|
||||
$this->assertPathMatch('public://' . $test_file->filename, $node_file->uri, format_string('The file %file was uploaded to the correct path.', array('%file' => $node_file->uri)));
|
||||
|
||||
// Change the path to contain multiple subdirectories.
|
||||
$field = $this->updateFileField($field_name, $type_name, array('file_directory' => 'foo/bar/baz'));
|
||||
@@ -997,7 +1140,7 @@ class FileFieldPathTestCase extends FileFieldTestCase {
|
||||
// Check that the file was uploaded into the subdirectory.
|
||||
$node = node_load($nid, NULL, TRUE);
|
||||
$node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
|
||||
$this->assertPathMatch('public://foo/bar/baz/' . $test_file->filename, $node_file->uri, t('The file %file was uploaded to the correct path.', array('%file' => $node_file->uri)));
|
||||
$this->assertPathMatch('public://foo/bar/baz/' . $test_file->filename, $node_file->uri, format_string('The file %file was uploaded to the correct path.', array('%file' => $node_file->uri)));
|
||||
|
||||
// Check the path when used with tokens.
|
||||
// Change the path to contain multiple token directories.
|
||||
@@ -1013,7 +1156,7 @@ class FileFieldPathTestCase extends FileFieldTestCase {
|
||||
// the user running the test case.
|
||||
$data = array('user' => $this->admin_user);
|
||||
$subdirectory = token_replace('[user:uid]/[user:name]', $data);
|
||||
$this->assertPathMatch('public://' . $subdirectory . '/' . $test_file->filename, $node_file->uri, t('The file %file was uploaded to the correct path with token replacements.', array('%file' => $node_file->uri)));
|
||||
$this->assertPathMatch('public://' . $subdirectory . '/' . $test_file->filename, $node_file->uri, format_string('The file %file was uploaded to the correct path with token replacements.', array('%file' => $node_file->uri)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1094,11 +1237,11 @@ class FileTokenReplaceTestCase extends FileFieldTestCase {
|
||||
$tests['[file:owner:uid]'] = $file->uid;
|
||||
|
||||
// Test to make sure that we generated something for each token.
|
||||
$this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.'));
|
||||
$this->assertFalse(in_array(0, array_map('strlen', $tests)), 'No empty tokens generated.');
|
||||
|
||||
foreach ($tests as $input => $expected) {
|
||||
$output = token_replace($input, array('file' => $file), array('language' => $language));
|
||||
$this->assertEqual($output, $expected, t('Sanitized file token %token replaced.', array('%token' => $input)));
|
||||
$this->assertEqual($output, $expected, format_string('Sanitized file token %token replaced.', array('%token' => $input)));
|
||||
}
|
||||
|
||||
// Generate and test unsanitized tokens.
|
||||
@@ -1109,7 +1252,7 @@ class FileTokenReplaceTestCase extends FileFieldTestCase {
|
||||
|
||||
foreach ($tests as $input => $expected) {
|
||||
$output = token_replace($input, array('file' => $file), array('language' => $language, 'sanitize' => FALSE));
|
||||
$this->assertEqual($output, $expected, t('Unsanitized file token %token replaced.', array('%token' => $input)));
|
||||
$this->assertEqual($output, $expected, format_string('Unsanitized file token %token replaced.', array('%token' => $input)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1154,10 +1297,10 @@ class FilePrivateTestCase extends FileFieldTestCase {
|
||||
$node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
|
||||
// Ensure the file can be downloaded.
|
||||
$this->drupalGet(file_create_url($node_file->uri));
|
||||
$this->assertResponse(200, t('Confirmed that the generated URL is correct by downloading the shipped file.'));
|
||||
$this->assertResponse(200, 'Confirmed that the generated URL is correct by downloading the shipped file.');
|
||||
$this->drupalLogOut();
|
||||
$this->drupalGet(file_create_url($node_file->uri));
|
||||
$this->assertResponse(403, t('Confirmed that access is denied for the file without the needed permission.'));
|
||||
$this->assertResponse(403, 'Confirmed that access is denied for the file without the needed permission.');
|
||||
|
||||
// Test with the field that should deny access through field access.
|
||||
$this->drupalLogin($this->admin_user);
|
||||
@@ -1166,6 +1309,19 @@ class FilePrivateTestCase extends FileFieldTestCase {
|
||||
$node_file = (object) $node->{$no_access_field_name}[LANGUAGE_NONE][0];
|
||||
// Ensure the file cannot be downloaded.
|
||||
$this->drupalGet(file_create_url($node_file->uri));
|
||||
$this->assertResponse(403, t('Confirmed that access is denied for the file without view field access permission.'));
|
||||
$this->assertResponse(403, 'Confirmed that access is denied for the file without view field access permission.');
|
||||
|
||||
// Attempt to reuse the existing file when creating a new node, and confirm
|
||||
// that access is still denied.
|
||||
$edit = array();
|
||||
$edit['title'] = $this->randomName(8);
|
||||
$edit[$field_name . '[' . LANGUAGE_NONE . '][0][fid]'] = $node_file->fid;
|
||||
$this->drupalPost('node/add/page', $edit, t('Save'));
|
||||
$new_node = $this->drupalGetNodeByTitle($edit['title']);
|
||||
$this->assertTrue(!empty($new_node), 'Node was created.');
|
||||
$this->assertUrl('node/' . $new_node->nid);
|
||||
$this->assertNoRaw($node_file->filename, 'File without view field access permission does not appear after attempting to attach it to a new node.');
|
||||
$this->drupalGet(file_create_url($node_file->uri));
|
||||
$this->assertResponse(403, 'Confirmed that access is denied for the file without view field access permission after attempting to attach it to a new node.');
|
||||
}
|
||||
}
|
||||
|
@@ -4,3 +4,9 @@ package = Core
|
||||
version = VERSION
|
||||
core = 7.x
|
||||
hidden = TRUE
|
||||
|
||||
; Information added by Drupal.org packaging script on 2015-04-02
|
||||
version = "7.36"
|
||||
project = "drupal"
|
||||
datestamp = "1427943826"
|
||||
|
||||
|
@@ -2,13 +2,14 @@
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Admin page callbacks for the filter module.
|
||||
* Administrative page callbacks for the Filter module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Menu callback; Displays a list of all text formats and allows them to be rearranged.
|
||||
* Page callback: Form constructor for a form to list and reorder text formats.
|
||||
*
|
||||
* @ingroup forms
|
||||
* @see filter_menu()
|
||||
* @see filter_admin_overview_submit()
|
||||
*/
|
||||
function filter_admin_overview($form) {
|
||||
@@ -45,6 +46,9 @@ function filter_admin_overview($form) {
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for filter_admin_overview().
|
||||
*/
|
||||
function filter_admin_overview_submit($form, &$form_state) {
|
||||
foreach ($form_state['values']['formats'] as $id => $data) {
|
||||
if (is_array($data) && isset($data['weight'])) {
|
||||
@@ -95,7 +99,26 @@ function theme_filter_admin_overview($variables) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu callback; Display a text format form.
|
||||
* Page callback: Displays the text format add/edit form.
|
||||
*
|
||||
* @param object|null $format
|
||||
* (optional) An object representing a format, with the following properties:
|
||||
* - format: A machine-readable name representing the ID of the text format
|
||||
* to save. If this corresponds to an existing text format, that format
|
||||
* will be updated; otherwise, a new format will be created.
|
||||
* - name: The title of the text format.
|
||||
* - cache: (optional) An integer indicating whether the text format is
|
||||
* cacheable (1) or not (0). Defaults to 1.
|
||||
* - status: (optional) An integer indicating whether the text format is
|
||||
* enabled (1) or not (0). Defaults to 1.
|
||||
* - weight: (optional) The weight of the text format, which controls its
|
||||
* placement in text format lists. If omitted, the weight is set to 0.
|
||||
* Defaults to NULL.
|
||||
*
|
||||
* @return
|
||||
* A form array.
|
||||
*
|
||||
* @see filter_menu()
|
||||
*/
|
||||
function filter_admin_format_page($format = NULL) {
|
||||
if (!isset($format->name)) {
|
||||
@@ -109,11 +132,24 @@ function filter_admin_format_page($format = NULL) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a text format form.
|
||||
* Form constructor for the text format add/edit form.
|
||||
*
|
||||
* @param $format
|
||||
* A format object having the properties:
|
||||
* - format: A machine-readable name representing the ID of the text format to
|
||||
* save. If this corresponds to an existing text format, that format will be
|
||||
* updated; otherwise, a new format will be created.
|
||||
* - name: The title of the text format.
|
||||
* - cache: An integer indicating whether the text format is cacheable (1) or
|
||||
* not (0). Defaults to 1.
|
||||
* - status: (optional) An integer indicating whether the text format is
|
||||
* enabled (1) or not (0). Defaults to 1.
|
||||
* - weight: (optional) The weight of the text format, which controls its
|
||||
* placement in text format lists. If omitted, the weight is set to 0.
|
||||
*
|
||||
* @ingroup forms
|
||||
* @see filter_admin_format_form_validate()
|
||||
* @see filter_admin_format_form_submit()
|
||||
* @ingroup forms
|
||||
*/
|
||||
function filter_admin_format_form($form, &$form_state, $format) {
|
||||
$is_fallback = ($format->format == filter_fallback_format());
|
||||
@@ -287,7 +323,9 @@ function theme_filter_admin_format_filter_order($variables) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate text format form submissions.
|
||||
* Form validation handler for filter_admin_format_form().
|
||||
*
|
||||
* @see filter_admin_format_form_submit()
|
||||
*/
|
||||
function filter_admin_format_form_validate($form, &$form_state) {
|
||||
$format_format = trim($form_state['values']['format']);
|
||||
@@ -304,7 +342,9 @@ function filter_admin_format_form_validate($form, &$form_state) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Process text format form submissions.
|
||||
* Form submission handler for filter_admin_format_form().
|
||||
*
|
||||
* @see filter_admin_format_form_validate()
|
||||
*/
|
||||
function filter_admin_format_form_submit($form, &$form_state) {
|
||||
// Remove unnecessary values.
|
||||
@@ -336,10 +376,14 @@ function filter_admin_format_form_submit($form, &$form_state) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu callback; confirm deletion of a format.
|
||||
* Form constructor for the text format deletion confirmation form.
|
||||
*
|
||||
* @ingroup forms
|
||||
* @param $format
|
||||
* An object representing a text format.
|
||||
*
|
||||
* @see filter_menu()
|
||||
* @see filter_admin_disable_submit()
|
||||
* @ingroup forms
|
||||
*/
|
||||
function filter_admin_disable($form, &$form_state, $format) {
|
||||
$form['#format'] = $format;
|
||||
@@ -353,7 +397,7 @@ function filter_admin_disable($form, &$form_state, $format) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Process filter disable form submission.
|
||||
* Form submission handler for filter_admin_disable().
|
||||
*/
|
||||
function filter_admin_disable_submit($form, &$form_state) {
|
||||
$format = $form['#format'];
|
||||
@@ -362,4 +406,3 @@ function filter_admin_disable_submit($form, &$form_state) {
|
||||
|
||||
$form_state['redirect'] = 'admin/config/content/formats';
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
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) {
|
||||
function callback_filter_process($text, $filter, $format, $langcode, $cache, $cache_id) {
|
||||
$text = preg_replace('|\[codefilter_code\](.+?)\[/codefilter_code\]|se', "<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 <br /> line break, <p> paragraph and </p> close paragraph tags are inserted automatically. If paragraphs are not recognized simply add a couple blank lines.');
|
||||
}
|
||||
|
@@ -6,3 +6,9 @@ core = 7.x
|
||||
files[] = filter.test
|
||||
required = TRUE
|
||||
configure = admin/config/content/formats
|
||||
|
||||
; Information added by Drupal.org packaging script on 2015-04-02
|
||||
version = "7.36"
|
||||
project = "drupal"
|
||||
datestamp = "1427943826"
|
||||
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the filter module.
|
||||
* Install, update, and uninstall functions for the Filter module.
|
||||
*/
|
||||
|
||||
/**
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user