diff --git a/modules/aggregator/aggregator.css b/modules/aggregator/aggregator.css
deleted file mode 100644
index 13c58ffe70ee..000000000000
--- a/modules/aggregator/aggregator.css
+++ /dev/null
@@ -1,38 +0,0 @@
-
-#aggregator .feed-source .feed-title {
- margin-top: 0;
-}
-#aggregator .feed-source .feed-image img {
- margin-bottom: 0.75em;
-}
-#aggregator .feed-source .feed-icon {
- float: right; /* LTR */
- display: block;
-}
-#aggregator .feed-item {
- margin-bottom: 1.5em;
-}
-#aggregator .feed-item-title {
- margin-bottom: 0;
- font-size: 1.3em;
-}
-#aggregator .feed-item-meta,
-#aggregator .feed-item-body {
- margin-bottom: 0.5em;
-}
-#aggregator .feed-item-categories {
- font-size: 0.9em;
-}
-#aggregator td {
- vertical-align: bottom;
-}
-#aggregator td.categorize-item {
- white-space: nowrap;
-}
-#aggregator .categorize-item .news-item .body {
- margin-top: 0;
-}
-#aggregator .categorize-item h3 {
- margin-bottom: 1em;
- margin-top: 0;
-}
diff --git a/modules/aggregator/aggregator.info b/modules/aggregator/aggregator.info
index f147740d3e23..4d57ba853f58 100644
--- a/modules/aggregator/aggregator.info
+++ b/modules/aggregator/aggregator.info
@@ -5,4 +5,4 @@ version = VERSION
core = 8.x
files[] = aggregator.test
configure = admin/config/services/aggregator/settings
-stylesheets[all][] = aggregator.css
+stylesheets[all][] = aggregator.theme.css
diff --git a/modules/aggregator/aggregator.install b/modules/aggregator/aggregator.install
index eecd14fb27f1..a6d576a7536c 100644
--- a/modules/aggregator/aggregator.install
+++ b/modules/aggregator/aggregator.install
@@ -226,10 +226,9 @@ function aggregator_schema() {
'description' => 'The {aggregator_feed}.fid to which this item belongs.',
),
'title' => array(
- 'type' => 'varchar',
- 'length' => 255,
+ 'type' => 'text',
+ 'size' => 'normal',
'not null' => TRUE,
- 'default' => '',
'description' => 'Title of the feed item.',
),
'link' => array(
diff --git a/modules/aggregator/aggregator.module b/modules/aggregator/aggregator.module
index eafb61ed3083..f247d26a33f1 100644
--- a/modules/aggregator/aggregator.module
+++ b/modules/aggregator/aggregator.module
@@ -478,6 +478,8 @@ function aggregator_save_category($edit) {
*
* @param $edit
* An associative array describing the feed to be added/edited/deleted.
+ * @return
+ * The ID of the feed.
*/
function aggregator_save_feed($edit) {
if (!empty($edit['fid'])) {
@@ -544,6 +546,8 @@ function aggregator_save_feed($edit) {
}
}
}
+
+ return $edit['fid'];
}
/**
diff --git a/modules/aggregator/aggregator.pages.inc b/modules/aggregator/aggregator.pages.inc
index 53ecb3684e61..3ca084af005e 100644
--- a/modules/aggregator/aggregator.pages.inc
+++ b/modules/aggregator/aggregator.pages.inc
@@ -14,7 +14,7 @@
function aggregator_page_last() {
drupal_add_feed('aggregator/rss', variable_get('site_name', 'Drupal') . ' ' . t('aggregator'));
- $items = aggregator_feed_items_load('sum');
+ $items = aggregator_load_feed_items('sum');
return _aggregator_page_list($items, arg(1));
}
@@ -33,8 +33,8 @@ function aggregator_page_source($feed) {
$feed_source = theme('aggregator_feed_source', array('feed' => $feed));
// It is safe to include the fid in the query because it's loaded from the
- // database by aggregator_feed_load.
- $items = aggregator_feed_items_load('source', $feed);
+ // database by aggregator_feed_load().
+ $items = aggregator_load_feed_items('source', $feed);
return _aggregator_page_list($items, arg(3), $feed_source);
}
@@ -67,8 +67,8 @@ function aggregator_page_category($category) {
drupal_add_feed('aggregator/rss/' . $category['cid'], variable_get('site_name', 'Drupal') . ' ' . t('aggregator - @title', array('@title' => $category['title'])));
// It is safe to include the cid in the query because it's loaded from the
- // database by aggregator_category_load.
- $items = aggregator_feed_items_load('category', $category);
+ // database by aggregator_category_load().
+ $items = aggregator_load_feed_items('category', $category);
return _aggregator_page_list($items, arg(3));
}
@@ -98,7 +98,7 @@ function aggregator_page_category_form($form, $form_state, $category) {
* @return
* An array of the feed items.
*/
-function aggregator_feed_items_load($type, $data = NULL) {
+function aggregator_load_feed_items($type, $data = NULL) {
$items = array();
$range_limit = 20;
switch ($type) {
diff --git a/modules/aggregator/aggregator.theme-rtl.css b/modules/aggregator/aggregator.theme-rtl.css
new file mode 100644
index 000000000000..f02ae91b2dfb
--- /dev/null
+++ b/modules/aggregator/aggregator.theme-rtl.css
@@ -0,0 +1,3 @@
+.aggregator .feed-icon {
+ float: left;
+}
diff --git a/modules/aggregator/aggregator.theme.css b/modules/aggregator/aggregator.theme.css
new file mode 100644
index 000000000000..e2182acf7c72
--- /dev/null
+++ b/modules/aggregator/aggregator.theme.css
@@ -0,0 +1,4 @@
+.aggregator .feed-icon {
+ float: right; /* LTR */
+ display: block;
+}
diff --git a/modules/aggregator/tests/aggregator_test_rss091.xml b/modules/aggregator/tests/aggregator_test_rss091.xml
index 1fd5320d3e98..f39a2732c034 100644
--- a/modules/aggregator/tests/aggregator_test_rss091.xml
+++ b/modules/aggregator/tests/aggregator_test_rss091.xml
@@ -22,7 +22,7 @@
diff --git a/modules/block/block.admin.inc b/modules/block/block.admin.inc
index ba31b9c5b6f4..7169190da8e7 100644
--- a/modules/block/block.admin.inc
+++ b/modules/block/block.admin.inc
@@ -77,7 +77,7 @@ function block_admin_display_prepare_blocks($theme) {
*/
function block_admin_display_form($form, &$form_state, $blocks, $theme, $block_regions = NULL) {
- drupal_add_css(drupal_get_path('module', 'block') . '/block.admin.css');
+ $form['#attached']['css'] = array(drupal_get_path('module', 'block') . '/block.admin.css');
// Get a list of block regions if one was not provided.
if (!isset($block_regions)) {
@@ -276,7 +276,7 @@ function block_admin_configure($form, &$form_state, $module, $delta) {
'#maxlength' => 64,
'#description' => $block->module == 'block' ? t('The title of the block as shown to the user.') : t('Override the default title for the block. Use
to display no title, or leave blank to use the default block title.', array('!placeholder' => '<none>')),
'#default_value' => isset($block->title) ? $block->title : '',
- '#weight' => -18,
+ '#weight' => -19,
);
// Module-specific block configuration.
diff --git a/modules/block/block.module b/modules/block/block.module
index 40fc6462e9c2..d1c803979557 100644
--- a/modules/block/block.module
+++ b/modules/block/block.module
@@ -500,7 +500,7 @@ function block_custom_block_form($edit = array()) {
'#maxlength' => 64,
'#description' => t('A brief description of your block. Used on the
.', array('@overview' => url('admin/structure/block'))),
'#required' => TRUE,
- '#weight' => -19,
+ '#weight' => -18,
);
$form['body_field']['#weight'] = -17;
$form['body_field']['body'] = array(
@@ -668,7 +668,7 @@ function block_list($region) {
* Name of the module that implements the block to load.
* @param $delta
* Unique ID of the block within the context of $module. Pass NULL to return
- * an empty $block object for $module.
+ * an empty block object for $module.
*
* @return
* A block object.
@@ -827,7 +827,7 @@ function _block_render_blocks($region_blocks) {
// with node_access modules. We also preserve the submission of forms in
// blocks, by fetching from cache only if the request method is 'GET'
// (or 'HEAD').
- if (!count(module_implements('node_grants')) && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD') && ($cid = _block_get_cache_id($block)) && ($cache = cache_get($cid, 'cache_block'))) {
+ if (!count(module_implements('node_grants')) && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD') && ($cid = _block_get_cache_id($block)) && ($cache = cache('block')->get($cid))) {
$array = $cache->data;
}
else {
@@ -838,7 +838,7 @@ function _block_render_blocks($region_blocks) {
drupal_alter(array('block_view', "block_view_{$block->module}_{$block->delta}"), $array, $block);
if (isset($cid)) {
- cache_set($cid, $array, 'cache_block', CACHE_TEMPORARY);
+ cache('block')->set($cid, $array, CACHE_TEMPORARY);
}
}
@@ -912,7 +912,7 @@ function block_flush_caches() {
_block_rehash($theme->name);
}
- return array('cache_block');
+ return array('block');
}
/**
diff --git a/modules/block/block.test b/modules/block/block.test
index 03f3048b4eb6..21666871987b 100644
--- a/modules/block/block.test
+++ b/modules/block/block.test
@@ -83,7 +83,7 @@ class BlockTestCase extends DrupalWebTestCase {
$this->assertTrue(array_key_exists('subject', $data) && empty($data['subject']), t('block_block_view() provides an empty block subject, since custom blocks do not have default titles.'));
$this->assertEqual(check_markup($custom_block['body[value]'], $format), $data['content'], t('block_block_view() provides correct block content.'));
- // Check if the block can be moved to all availble regions.
+ // Check whether the block can be moved to all available regions.
$custom_block['module'] = 'block';
$custom_block['delta'] = $bid;
foreach ($this->regions as $region) {
@@ -307,7 +307,7 @@ class BlockTestCase extends DrupalWebTestCase {
// Check to see if the block was created by checking that it's in the database.
$this->assertNotNull($bid, t('Block found in database'));
- // Check if the block can be moved to all availble regions.
+ // Check whether the block can be moved to all available regions.
foreach ($this->regions as $region) {
$this->moveBlockToRegion($block, $region);
}
@@ -321,7 +321,7 @@ class BlockTestCase extends DrupalWebTestCase {
$this->assertText(t('The block settings have been updated.'), t('Block successfully move to disabled region.'));
$this->assertNoText(t($block['title']), t('Block no longer appears on page.'));
- // Confirm that the regions xpath is not availble
+ // Confirm that the region's xpath is not available.
$xpath = $this->buildXPathQuery('//div[@id=:id]/*', array(':id' => 'block-block-' . $bid));
$this->assertNoFieldByXPath($xpath, FALSE, t('Custom block found in no regions.'));
diff --git a/modules/book/book-all-books-block.tpl.php b/modules/book/book-all-books-block.tpl.php
index d22ff2ccc972..626a5f26482a 100644
--- a/modules/book/book-all-books-block.tpl.php
+++ b/modules/book/book-all-books-block.tpl.php
@@ -11,8 +11,8 @@
* render() on each to print it as an unordered list.
*/
?>
- $menu) : ?>
-
+ $menu): ?>
+
diff --git a/modules/book/book-export-html.tpl.php b/modules/book/book-export-html.tpl.php
index 180f3ae7783f..4b25a766e18c 100644
--- a/modules/book/book-export-html.tpl.php
+++ b/modules/book/book-export-html.tpl.php
@@ -40,7 +40,7 @@
*/
$div_close = '';
?>
-
+
'; ?>
diff --git a/modules/book/book-navigation.tpl.php b/modules/book/book-navigation.tpl.php
index e5883dc56ec8..5d8e9aa7fcb6 100644
--- a/modules/book/book-navigation.tpl.php
+++ b/modules/book/book-navigation.tpl.php
@@ -35,13 +35,13 @@
diff --git a/modules/book/book.module b/modules/book/book.module
index beb17214c9fa..6e74d3297efc 100644
--- a/modules/book/book.module
+++ b/modules/book/book.module
@@ -1262,12 +1262,12 @@ function book_menu_subtree_data($link) {
$cid = 'links:' . $link['menu_name'] . ':subtree-cid:' . $link['mlid'];
if (!isset($tree[$cid])) {
- $cache = cache_get($cid, 'cache_menu');
+ $cache = cache('menu')->get($cid);
if ($cache && isset($cache->data)) {
// If the cache entry exists, it will just be the cid for the actual data.
// This avoids duplication of large amounts of data.
- $cache = cache_get($cache->data, 'cache_menu');
+ $cache = cache('menu')->get($cache->data);
if ($cache && isset($cache->data)) {
$data = $cache->data;
@@ -1300,11 +1300,11 @@ function book_menu_subtree_data($link) {
$tree_cid = 'links:' . $item['menu_name'] . ':subtree-data:' . hash('sha256', serialize($data));
// Cache the data, if it is not already in the cache.
- if (!cache_get($tree_cid, 'cache_menu')) {
- cache_set($tree_cid, $data, 'cache_menu');
+ if (!cache('menu')->get($tree_cid)) {
+ cache('menu')->set($tree_cid, $data);
}
// Cache the cid of the (shared) data using the menu and item-specific cid.
- cache_set($cid, $tree_cid, 'cache_menu');
+ cache('menu')->set($cid, $tree_cid);
}
// Check access for the current user to each item in the tree.
menu_tree_check_access($data['tree'], $data['node_links']);
diff --git a/modules/color/color-rtl.css b/modules/color/color.admin-rtl.css
similarity index 100%
rename from modules/color/color-rtl.css
rename to modules/color/color.admin-rtl.css
diff --git a/modules/color/color.css b/modules/color/color.admin.css
similarity index 100%
rename from modules/color/color.css
rename to modules/color/color.admin.css
diff --git a/modules/color/color.js b/modules/color/color.js
index 43099adcabf8..3e53ce115a78 100644
--- a/modules/color/color.js
+++ b/modules/color/color.js
@@ -43,14 +43,6 @@ Drupal.behaviors.color = {
}
}
- // Fix preview background in IE6.
- if (navigator.appVersion.match(/MSIE [0-6]\./)) {
- var e = $('#preview #img')[0];
- var image = e.currentStyle.backgroundImage;
- e.style.backgroundImage = 'none';
- e.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=crop, src='" + image.substring(5, image.length - 2) + "')";
- }
-
// Set up colorScheme selector.
$('#edit-scheme', form).change(function () {
var schemes = settings.color.schemes, colorScheme = this.options[this.selectedIndex].value;
diff --git a/modules/color/color.module b/modules/color/color.module
index 09eb82b23b84..7665631ed4e8 100644
--- a/modules/color/color.module
+++ b/modules/color/color.module
@@ -189,7 +189,7 @@ function color_scheme_form($complete_form, &$form_state, $theme) {
),
// Add custom CSS.
'css' => array(
- $base . '/color.css' => array(),
+ $base . '/color.admin.css' => array(),
),
// Add custom JavaScript.
'js' => array(
diff --git a/modules/comment/comment.info b/modules/comment/comment.info
index a5837af83ab1..949ffc2a830b 100644
--- a/modules/comment/comment.info
+++ b/modules/comment/comment.info
@@ -4,6 +4,7 @@ package = Core
version = VERSION
core = 8.x
dependencies[] = text
+dependencies[] = entity
files[] = comment.module
files[] = comment.test
configure = admin/content/comment
diff --git a/modules/comment/comment.module b/modules/comment/comment.module
index 8e0c7d930394..b52204ab3aaf 100644
--- a/modules/comment/comment.module
+++ b/modules/comment/comment.module
@@ -92,7 +92,7 @@ function comment_help($path, $arg) {
* Implements hook_entity_info().
*/
function comment_entity_info() {
- $return = array(
+ $return = array(
'comment' => array(
'label' => t('Comment'),
'base table' => 'comment',
@@ -542,7 +542,7 @@ function comment_new_page_count($num_comments, $new_replies, $node) {
elseif ($flat) {
// Flat comments.
$count = $num_comments - $new_replies;
- $pageno = $count / $comments_per_page;
+ $pageno = $count / $comments_per_page;
}
else {
// Threaded comments: we build a query with a subquery to find the first
@@ -575,7 +575,7 @@ function comment_new_page_count($num_comments, $new_replies, $node) {
':thread' => $first_thread,
))->fetchField();
- $pageno = $count / $comments_per_page;
+ $pageno = $count / $comments_per_page;
}
if ($pageno >= 1) {
@@ -655,7 +655,7 @@ function comment_node_view($node, $view_mode) {
);
}
else {
- $links['comment_forbidden'] = array(
+ $links['comment-forbidden'] = array(
'title' => theme('comment_post_forbidden', array('node' => $node)),
'html' => TRUE,
);
@@ -685,7 +685,7 @@ function comment_node_view($node, $view_mode) {
}
}
else {
- $links['comment_forbidden'] = array(
+ $links['comment-forbidden'] = array(
'title' => theme('comment_post_forbidden', array('node' => $node)),
'html' => TRUE,
);
@@ -1066,8 +1066,8 @@ function comment_links($comment, $node) {
);
}
else {
- $links['comment_forbidden']['title'] = theme('comment_post_forbidden', array('node' => $node));
- $links['comment_forbidden']['html'] = TRUE;
+ $links['comment-forbidden']['title'] = theme('comment_post_forbidden', array('node' => $node));
+ $links['comment-forbidden']['html'] = TRUE;
}
}
return $links;
@@ -1439,7 +1439,7 @@ function comment_save($comment) {
$transaction = db_transaction();
try {
- $defaults = array(
+ $defaults = array(
'mail' => '',
'homepage' => '',
'name' => '',
@@ -1711,7 +1711,7 @@ function comment_num_new($nid, $timestamp = 0) {
$timestamp = ($timestamp > NODE_NEW_LIMIT ? $timestamp : NODE_NEW_LIMIT);
// Use the timestamp to retrieve the number of new comments.
- return db_query('SELECT COUNT(cid) FROM {comment} WHERE nid = :nid AND created > :timestamp AND status = :status', array(
+ return db_query('SELECT COUNT(cid) FROM {comment} WHERE nid = :nid AND created > :timestamp AND status = :status', array(
':nid' => $nid,
':timestamp' => $timestamp,
':status' => COMMENT_PUBLISHED,
@@ -2279,10 +2279,10 @@ function template_preprocess_comment(&$variables) {
// Set status to a string representation of comment->status.
if (isset($comment->in_preview)) {
- $variables['status'] = 'comment-preview';
+ $variables['status'] = 'comment-preview';
}
else {
- $variables['status'] = ($comment->status == COMMENT_NOT_PUBLISHED) ? 'comment-unpublished' : 'comment-published';
+ $variables['status'] = ($comment->status == COMMENT_NOT_PUBLISHED) ? 'comment-unpublished' : 'comment-published';
}
// Gather comment classes.
if ($comment->uid === 0) {
diff --git a/modules/dblog/dblog.css b/modules/dblog/dblog.css
index ff310e3baa20..88f4ba01b8ad 100644
--- a/modules/dblog/dblog.css
+++ b/modules/dblog/dblog.css
@@ -1,4 +1,3 @@
-
.form-item-type,
.form-item-severity {
float: left; /* LTR */
@@ -52,7 +51,9 @@ table#admin-dblog td.icon {
table#admin-dblog tr.dblog-warning td.icon {
background-image: url(../../misc/message-16-warning.png);
}
-table#admin-dblog tr.dblog-error td.icon {
+table#admin-dblog tr.dblog-error td.icon,
+table#admin-dblog tr.dblog-critical td.icon,
+table#admin-dblog tr.dblog-alert td.icon,
+table#admin-dblog tr.dblog-emerg td.icon {
background-image: url(../../misc/message-16-error.png);
}
-
diff --git a/modules/entity/entity.api.php b/modules/entity/entity.api.php
new file mode 100644
index 000000000000..9b19477163bf
--- /dev/null
+++ b/modules/entity/entity.api.php
@@ -0,0 +1,414 @@
+subject, then
+ * 'subject' should be specified here. If complex logic is required to
+ * build the label, a 'label callback' should be defined instead (see
+ * the 'label callback' section above for details).
+ * - bundle keys: An array describing how the Field API can extract the
+ * information it needs from the bundle objects for this type (e.g
+ * $vocabulary objects for terms; not applicable for nodes). This entry can
+ * be omitted if this type's bundles do not exist as standalone objects.
+ * Elements:
+ * - bundle: The name of the property that contains the name of the bundle
+ * object.
+ * - bundles: An array describing all bundles for this object type. Keys are
+ * bundles machine names, as found in the objects' 'bundle' property
+ * (defined in the 'entity keys' entry above). Elements:
+ * - label: The human-readable name of the bundle.
+ * - uri callback: Same as the 'uri callback' key documented above for the
+ * entity type, but for the bundle only. When determining the URI of an
+ * entity, if a 'uri callback' is defined for both the entity type and
+ * the bundle, the one for the bundle is used.
+ * - admin: An array of information that allows Field UI pages to attach
+ * themselves to the existing administration pages for the bundle.
+ * Elements:
+ * - path: the path of the bundle's main administration page, as defined
+ * in hook_menu(). If the path includes a placeholder for the bundle,
+ * the 'bundle argument', 'bundle helper' and 'real path' keys below
+ * are required.
+ * - bundle argument: The position of the placeholder in 'path', if any.
+ * - real path: The actual path (no placeholder) of the bundle's main
+ * administration page. This will be used to generate links.
+ * - access callback: As in hook_menu(). 'user_access' will be assumed if
+ * no value is provided.
+ * - access arguments: As in hook_menu().
+ * - view modes: An array describing the view modes for the entity type. View
+ * modes let entities be displayed differently depending on the context.
+ * For instance, a node can be displayed differently on its own page
+ * ('full' mode), on the home page or taxonomy listings ('teaser' mode), or
+ * in an RSS feed ('rss' mode). Modules taking part in the display of the
+ * entity (notably the Field API) can adjust their behavior depending on
+ * the requested view mode. An additional 'default' view mode is available
+ * for all entity types. This view mode is not intended for actual entity
+ * display, but holds default display settings. For each available view
+ * mode, administrators can configure whether it should use its own set of
+ * field display settings, or just replicate the settings of the 'default'
+ * view mode, thus reducing the amount of display configurations to keep
+ * track of. Keys of the array are view mode names. Each view mode is
+ * described by an array with the following key/value pairs:
+ * - label: The human-readable name of the view mode
+ * - custom settings: A boolean specifying whether the view mode should by
+ * default use its own custom field display settings. If FALSE, entities
+ * displayed in this view mode will reuse the 'default' display settings
+ * by default (e.g. right after the module exposing the view mode is
+ * enabled), but administrators can later use the Field UI to apply custom
+ * display settings specific to the view mode.
+ *
+ * @see entity_load()
+ * @see hook_entity_info_alter()
+ */
+function hook_entity_info() {
+ $return = array(
+ 'node' => array(
+ 'label' => t('Node'),
+ 'controller class' => 'NodeController',
+ 'base table' => 'node',
+ 'revision table' => 'node_revision',
+ 'uri callback' => 'node_uri',
+ 'fieldable' => TRUE,
+ 'translation' => array(
+ 'locale' => TRUE,
+ ),
+ 'entity keys' => array(
+ 'id' => 'nid',
+ 'revision' => 'vid',
+ 'bundle' => 'type',
+ ),
+ 'bundle keys' => array(
+ 'bundle' => 'type',
+ ),
+ 'bundles' => array(),
+ 'view modes' => array(
+ 'full' => array(
+ 'label' => t('Full content'),
+ 'custom settings' => FALSE,
+ ),
+ 'teaser' => array(
+ 'label' => t('Teaser'),
+ 'custom settings' => TRUE,
+ ),
+ 'rss' => array(
+ 'label' => t('RSS'),
+ 'custom settings' => FALSE,
+ ),
+ ),
+ ),
+ );
+
+ // Search integration is provided by node.module, so search-related
+ // view modes for nodes are defined here and not in search.module.
+ if (module_exists('search')) {
+ $return['node']['view modes'] += array(
+ 'search_index' => array(
+ 'label' => t('Search index'),
+ 'custom settings' => FALSE,
+ ),
+ 'search_result' => array(
+ 'label' => t('Search result'),
+ 'custom settings' => FALSE,
+ ),
+ );
+ }
+
+ // Bundles must provide a human readable name so we can create help and error
+ // messages, and the path to attach Field admin pages to.
+ foreach (node_type_get_names() as $type => $name) {
+ $return['node']['bundles'][$type] = array(
+ 'label' => $name,
+ 'admin' => array(
+ 'path' => 'admin/structure/types/manage/%node_type',
+ 'real path' => 'admin/structure/types/manage/' . str_replace('_', '-', $type),
+ 'bundle argument' => 4,
+ 'access arguments' => array('administer content types'),
+ ),
+ );
+ }
+
+ return $return;
+}
+
+/**
+ * Alter the entity info.
+ *
+ * Modules may implement this hook to alter the information that defines an
+ * entity. All properties that are available in hook_entity_info() can be
+ * altered here.
+ *
+ * @param $entity_info
+ * The entity info array, keyed by entity name.
+ *
+ * @see hook_entity_info()
+ */
+function hook_entity_info_alter(&$entity_info) {
+ // Set the controller class for nodes to an alternate implementation of the
+ // DrupalEntityController interface.
+ $entity_info['node']['controller class'] = 'MyCustomNodeController';
+}
+
+/**
+ * Act on entities when loaded.
+ *
+ * This is a generic load hook called for all entity types loaded via the
+ * entity API.
+ *
+ * @param $entities
+ * The entities keyed by entity ID.
+ * @param $type
+ * The type of entities being loaded (i.e. node, user, comment).
+ */
+function hook_entity_load($entities, $type) {
+ foreach ($entities as $entity) {
+ $entity->foo = mymodule_add_something($entity, $type);
+ }
+}
+
+/**
+ * Act on an entity before it is about to be created or updated.
+ *
+ * @param $entity
+ * The entity object.
+ * @param $type
+ * The type of entity being saved (i.e. node, user, comment).
+ */
+function hook_entity_presave($entity, $type) {
+ $entity->changed = REQUEST_TIME;
+}
+
+/**
+ * Act on entities when inserted.
+ *
+ * @param $entity
+ * The entity object.
+ * @param $type
+ * The type of entity being inserted (i.e. node, user, comment).
+ */
+function hook_entity_insert($entity, $type) {
+ // Insert the new entity into a fictional table of all entities.
+ $info = entity_get_info($type);
+ list($id) = entity_extract_ids($type, $entity);
+ db_insert('example_entity')
+ ->fields(array(
+ 'type' => $type,
+ 'id' => $id,
+ 'created' => REQUEST_TIME,
+ 'updated' => REQUEST_TIME,
+ ))
+ ->execute();
+}
+
+/**
+ * Act on entities when updated.
+ *
+ * @param $entity
+ * The entity object.
+ * @param $type
+ * The type of entity being updated (i.e. node, user, comment).
+ */
+function hook_entity_update($entity, $type) {
+ // Update the entity's entry in a fictional table of all entities.
+ $info = entity_get_info($type);
+ list($id) = entity_extract_ids($type, $entity);
+ db_update('example_entity')
+ ->fields(array(
+ 'updated' => REQUEST_TIME,
+ ))
+ ->condition('type', $type)
+ ->condition('id', $id)
+ ->execute();
+}
+
+/**
+ * Act on entities when deleted.
+ *
+ * @param $entity
+ * The entity object.
+ * @param $type
+ * The type of entity being deleted (i.e. node, user, comment).
+ */
+function hook_entity_delete($entity, $type) {
+ // Delete the entity's entry from a fictional table of all entities.
+ $info = entity_get_info($type);
+ list($id) = entity_extract_ids($type, $entity);
+ db_delete('example_entity')
+ ->condition('type', $type)
+ ->condition('id', $id)
+ ->execute();
+}
+
+/**
+ * Alter or execute an EntityFieldQuery.
+ *
+ * @param EntityFieldQuery $query
+ * An EntityFieldQuery. One of the most important properties to be changed is
+ * EntityFieldQuery::executeCallback. If this is set to an existing function,
+ * this function will get the query as its single argument and its result
+ * will be the returned as the result of EntityFieldQuery::execute(). This can
+ * be used to change the behavior of EntityFieldQuery entirely. For example,
+ * the default implementation can only deal with one field storage engine, but
+ * it is possible to write a module that can query across field storage
+ * engines. Also, the default implementation presumes entities are stored in
+ * SQL, but the execute callback could instead query any other entity storage,
+ * local or remote.
+ *
+ * Note the $query->altered attribute which is TRUE in case the query has
+ * already been altered once. This happens with cloned queries.
+ * If there is a pager, then such a cloned query will be executed to count
+ * all elements. This query can be detected by checking for
+ * ($query->pager && $query->count), allowing the driver to return 0 from
+ * the count query and disable the pager.
+ */
+function hook_entity_query_alter($query) {
+ $query->executeCallback = 'my_module_query_callback';
+}
+
+/**
+ * Act on entities being assembled before rendering.
+ *
+ * @param $entity
+ * The entity object.
+ * @param $type
+ * The type of entity being rendered (i.e. node, user, comment).
+ * @param $view_mode
+ * The view mode the entity is rendered in.
+ * @param $langcode
+ * The language code used for rendering.
+ *
+ * The module may add elements to $entity->content prior to rendering. The
+ * structure of $entity->content is a renderable array as expected by
+ * drupal_render().
+ *
+ * @see hook_entity_view_alter()
+ * @see hook_comment_view()
+ * @see hook_node_view()
+ * @see hook_user_view()
+ */
+function hook_entity_view($entity, $type, $view_mode, $langcode) {
+ $entity->content['my_additional_field'] = array(
+ '#markup' => $additional_field,
+ '#weight' => 10,
+ '#theme' => 'mymodule_my_additional_field',
+ );
+}
+
+/**
+ * Alter the results of ENTITY_view().
+ *
+ * This hook is called after the content has been assembled in a structured
+ * array and may be used for doing processing which requires that the complete
+ * entity content structure has been built.
+ *
+ * If a module wishes to act on the rendered HTML of the entity rather than the
+ * structured content array, it may use this hook to add a #post_render
+ * callback. Alternatively, it could also implement hook_preprocess_ENTITY().
+ * See drupal_render() and theme() for details.
+ *
+ * @param $build
+ * A renderable array representing the entity content.
+ * @param $type
+ * The type of entity being rendered (i.e. node, user, comment).
+ *
+ * @see hook_entity_view()
+ * @see hook_comment_view_alter()
+ * @see hook_node_view_alter()
+ * @see hook_taxonomy_term_view_alter()
+ * @see hook_user_view_alter()
+ */
+function hook_entity_view_alter(&$build, $type) {
+ if ($build['#view_mode'] == 'full' && isset($build['an_additional_field'])) {
+ // Change its weight.
+ $build['an_additional_field']['#weight'] = -10;
+
+ // Add a #post_render callback to act on the rendered HTML of the entity.
+ $build['#post_render'][] = 'my_module_node_post_render';
+ }
+}
+
+/**
+ * Act on entities as they are being prepared for view.
+ *
+ * Allows you to operate on multiple entities as they are being prepared for
+ * view. Only use this if attaching the data during the entity_load() phase
+ * is not appropriate, for example when attaching other 'entity' style objects.
+ *
+ * @param $entities
+ * The entities keyed by entity ID.
+ * @param $type
+ * The type of entities being loaded (i.e. node, user, comment).
+ */
+function hook_entity_prepare_view($entities, $type) {
+ // Load a specific node into the user object for later theming.
+ if ($type == 'user') {
+ $nodes = mymodule_get_user_nodes(array_keys($entities));
+ foreach ($entities as $uid => $entity) {
+ $entity->user_node = $nodes[$uid];
+ }
+ }
+}
diff --git a/modules/entity/entity.controller.inc b/modules/entity/entity.controller.inc
new file mode 100644
index 000000000000..8327bc6d0682
--- /dev/null
+++ b/modules/entity/entity.controller.inc
@@ -0,0 +1,390 @@
+ $value.
+ *
+ * @return
+ * An array of entity objects indexed by their ids.
+ */
+ public function load($ids = array(), $conditions = array());
+}
+
+/**
+ * Default implementation of DrupalEntityControllerInterface.
+ *
+ * This class can be used as-is by most simple entity types. Entity types
+ * requiring special handling can extend the class.
+ */
+class DrupalDefaultEntityController implements DrupalEntityControllerInterface {
+
+ /**
+ * Static cache of entities.
+ *
+ * @var array
+ */
+ protected $entityCache;
+
+ /**
+ * Entity type for this controller instance.
+ *
+ * @var string
+ */
+ protected $entityType;
+
+ /**
+ * Array of information about the entity.
+ *
+ * @var array
+ *
+ * @see entity_get_info()
+ */
+ protected $entityInfo;
+
+ /**
+ * Additional arguments to pass to hook_TYPE_load().
+ *
+ * Set before calling DrupalDefaultEntityController::attachLoad().
+ *
+ * @var array
+ */
+ protected $hookLoadArguments;
+
+ /**
+ * Name of the entity's ID field in the entity database table.
+ *
+ * @var string
+ */
+ protected $idKey;
+
+ /**
+ * Name of entity's revision database table field, if it supports revisions.
+ *
+ * Has the value FALSE if this entity does not use revisions.
+ *
+ * @var string
+ */
+ protected $revisionKey;
+
+ /**
+ * The table that stores revisions, if the entity supports revisions.
+ *
+ * @var string
+ */
+ protected $revisionTable;
+
+ /**
+ * Whether this entity type should use the static cache.
+ *
+ * Set by entity info.
+ *
+ * @var boolean
+ */
+ protected $cache;
+
+ /**
+ * Constructor: sets basic variables.
+ */
+ public function __construct($entityType) {
+ $this->entityType = $entityType;
+ $this->entityInfo = entity_get_info($entityType);
+ $this->entityCache = array();
+ $this->hookLoadArguments = array();
+ $this->idKey = $this->entityInfo['entity keys']['id'];
+
+ // Check if the entity type supports revisions.
+ if (!empty($this->entityInfo['entity keys']['revision'])) {
+ $this->revisionKey = $this->entityInfo['entity keys']['revision'];
+ $this->revisionTable = $this->entityInfo['revision table'];
+ }
+ else {
+ $this->revisionKey = FALSE;
+ }
+
+ // Check if the entity type supports static caching of loaded entities.
+ $this->cache = !empty($this->entityInfo['static cache']);
+ }
+
+ /**
+ * Implements DrupalEntityControllerInterface::resetCache().
+ */
+ public function resetCache(array $ids = NULL) {
+ if (isset($ids)) {
+ foreach ($ids as $id) {
+ unset($this->entityCache[$id]);
+ }
+ }
+ else {
+ $this->entityCache = array();
+ }
+ }
+
+ /**
+ * Implements DrupalEntityControllerInterface::load().
+ */
+ public function load($ids = array(), $conditions = array()) {
+ $entities = array();
+
+ // Revisions are not statically cached, and require a different query to
+ // other conditions, so separate the revision id into its own variable.
+ if ($this->revisionKey && isset($conditions[$this->revisionKey])) {
+ $revision_id = $conditions[$this->revisionKey];
+ unset($conditions[$this->revisionKey]);
+ }
+ else {
+ $revision_id = FALSE;
+ }
+
+ // Create a new variable which is either a prepared version of the $ids
+ // array for later comparison with the entity cache, or FALSE if no $ids
+ // were passed. The $ids array is reduced as items are loaded from cache,
+ // and we need to know if it's empty for this reason to avoid querying the
+ // database when all requested entities are loaded from cache.
+ $passed_ids = !empty($ids) ? array_flip($ids) : FALSE;
+ // Try to load entities from the static cache, if the entity type supports
+ // static caching.
+ if ($this->cache && !$revision_id) {
+ $entities += $this->cacheGet($ids, $conditions);
+ // If any entities were loaded, remove them from the ids still to load.
+ if ($passed_ids) {
+ $ids = array_keys(array_diff_key($passed_ids, $entities));
+ }
+ }
+
+ // Load any remaining entities from the database. This is the case if $ids
+ // is set to FALSE (so we load all entities), if there are any ids left to
+ // load, if loading a revision, or if $conditions was passed without $ids.
+ if ($ids === FALSE || $ids || $revision_id || ($conditions && !$passed_ids)) {
+ // Build the query.
+ $query = $this->buildQuery($ids, $conditions, $revision_id);
+ $queried_entities = $query
+ ->execute()
+ ->fetchAllAssoc($this->idKey);
+ }
+
+ // Pass all entities loaded from the database through $this->attachLoad(),
+ // which attaches fields (if supported by the entity type) and calls the
+ // entity type specific load callback, for example hook_node_load().
+ if (!empty($queried_entities)) {
+ $this->attachLoad($queried_entities, $revision_id);
+ $entities += $queried_entities;
+ }
+
+ if ($this->cache) {
+ // Add entities to the cache if we are not loading a revision.
+ if (!empty($queried_entities) && !$revision_id) {
+ $this->cacheSet($queried_entities);
+ }
+ }
+
+ // Ensure that the returned array is ordered the same as the original
+ // $ids array if this was passed in and remove any invalid ids.
+ if ($passed_ids) {
+ // Remove any invalid ids from the array.
+ $passed_ids = array_intersect_key($passed_ids, $entities);
+ foreach ($entities as $entity) {
+ $passed_ids[$entity->{$this->idKey}] = $entity;
+ }
+ $entities = $passed_ids;
+ }
+
+ return $entities;
+ }
+
+ /**
+ * Builds the query to load the entity.
+ *
+ * This has full revision support. For entities requiring special queries,
+ * the class can be extended, and the default query can be constructed by
+ * calling parent::buildQuery(). This is usually necessary when the object
+ * being loaded needs to be augmented with additional data from another
+ * table, such as loading node type into comments or vocabulary machine name
+ * into terms, however it can also support $conditions on different tables.
+ * See CommentController::buildQuery() or TaxonomyTermController::buildQuery()
+ * for examples.
+ *
+ * @param $ids
+ * An array of entity IDs, or FALSE to load all entities.
+ * @param $conditions
+ * An array of conditions in the form 'field' => $value.
+ * @param $revision_id
+ * The ID of the revision to load, or FALSE if this query is asking for the
+ * most current revision(s).
+ *
+ * @return SelectQuery
+ * A SelectQuery object for loading the entity.
+ */
+ protected function buildQuery($ids, $conditions = array(), $revision_id = FALSE) {
+ $query = db_select($this->entityInfo['base table'], 'base');
+
+ $query->addTag($this->entityType . '_load_multiple');
+
+ if ($revision_id) {
+ $query->join($this->revisionTable, 'revision', "revision.{$this->idKey} = base.{$this->idKey} AND revision.{$this->revisionKey} = :revisionId", array(':revisionId' => $revision_id));
+ }
+ elseif ($this->revisionKey) {
+ $query->join($this->revisionTable, 'revision', "revision.{$this->revisionKey} = base.{$this->revisionKey}");
+ }
+
+ // Add fields from the {entity} table.
+ $entity_fields = $this->entityInfo['schema_fields_sql']['base table'];
+
+ if ($this->revisionKey) {
+ // Add all fields from the {entity_revision} table.
+ $entity_revision_fields = drupal_map_assoc($this->entityInfo['schema_fields_sql']['revision table']);
+ // The id field is provided by entity, so remove it.
+ unset($entity_revision_fields[$this->idKey]);
+
+ // Remove all fields from the base table that are also fields by the same
+ // name in the revision table.
+ $entity_field_keys = array_flip($entity_fields);
+ foreach ($entity_revision_fields as $key => $name) {
+ if (isset($entity_field_keys[$name])) {
+ unset($entity_fields[$entity_field_keys[$name]]);
+ }
+ }
+ $query->fields('revision', $entity_revision_fields);
+ }
+
+ $query->fields('base', $entity_fields);
+
+ if ($ids) {
+ $query->condition("base.{$this->idKey}", $ids, 'IN');
+ }
+ if ($conditions) {
+ foreach ($conditions as $field => $value) {
+ $query->condition('base.' . $field, $value);
+ }
+ }
+ return $query;
+ }
+
+ /**
+ * Attaches data to entities upon loading.
+ *
+ * This will attach fields, if the entity is fieldable. It calls
+ * hook_entity_load() for modules which need to add data to all entities.
+ * It also calls hook_TYPE_load() on the loaded entities. For example
+ * hook_node_load() or hook_user_load(). If your hook_TYPE_load()
+ * expects special parameters apart from the queried entities, you can set
+ * $this->hookLoadArguments prior to calling the method.
+ * See NodeController::attachLoad() for an example.
+ *
+ * @param $queried_entities
+ * Associative array of query results, keyed on the entity ID.
+ * @param $revision_id
+ * ID of the revision that was loaded, or FALSE if teh most current revision
+ * was loaded.
+ */
+ protected function attachLoad(&$queried_entities, $revision_id = FALSE) {
+ // Attach fields.
+ if ($this->entityInfo['fieldable']) {
+ if ($revision_id) {
+ field_attach_load_revision($this->entityType, $queried_entities);
+ }
+ else {
+ field_attach_load($this->entityType, $queried_entities);
+ }
+ }
+
+ // Call hook_entity_load().
+ foreach (module_implements('entity_load') as $module) {
+ $function = $module . '_entity_load';
+ $function($queried_entities, $this->entityType);
+ }
+ // Call hook_TYPE_load(). The first argument for hook_TYPE_load() are
+ // always the queried entities, followed by additional arguments set in
+ // $this->hookLoadArguments.
+ $args = array_merge(array($queried_entities), $this->hookLoadArguments);
+ foreach (module_implements($this->entityInfo['load hook']) as $module) {
+ call_user_func_array($module . '_' . $this->entityInfo['load hook'], $args);
+ }
+ }
+
+ /**
+ * Gets entities from the static cache.
+ *
+ * @param $ids
+ * If not empty, return entities that match these IDs.
+ * @param $conditions
+ * If set, return entities that match all of these conditions.
+ *
+ * @return
+ * Array of entities from the entity cache.
+ */
+ protected function cacheGet($ids, $conditions = array()) {
+ $entities = array();
+ // Load any available entities from the internal cache.
+ if (!empty($this->entityCache)) {
+ if ($ids) {
+ $entities += array_intersect_key($this->entityCache, array_flip($ids));
+ }
+ // If loading entities only by conditions, fetch all available entities
+ // from the cache. Entities which don't match are removed later.
+ elseif ($conditions) {
+ $entities = $this->entityCache;
+ }
+ }
+
+ // Exclude any entities loaded from cache if they don't match $conditions.
+ // This ensures the same behavior whether loading from memory or database.
+ if ($conditions) {
+ foreach ($entities as $entity) {
+ $entity_values = (array) $entity;
+ if (array_diff_assoc($conditions, $entity_values)) {
+ unset($entities[$entity->{$this->idKey}]);
+ }
+ }
+ }
+ return $entities;
+ }
+
+ /**
+ * Stores entities in the static entity cache.
+ *
+ * @param $entities
+ * Entities to store in the cache.
+ */
+ protected function cacheSet($entities) {
+ $this->entityCache += $entities;
+ }
+}
diff --git a/modules/entity/entity.info b/modules/entity/entity.info
new file mode 100644
index 000000000000..31eb720d0a7c
--- /dev/null
+++ b/modules/entity/entity.info
@@ -0,0 +1,8 @@
+name = Entity
+description = API for managing entities like nodes and users.
+package = Core
+version = VERSION
+core = 8.x
+required = TRUE
+files[] = entity.query.inc
+files[] = entity.controller.inc
diff --git a/modules/entity/entity.module b/modules/entity/entity.module
new file mode 100644
index 000000000000..02611e3c4910
--- /dev/null
+++ b/modules/entity/entity.module
@@ -0,0 +1,442 @@
+' . t('About') . '';
+ $output .= '
' . t('The Entity module provides an API for managing entities like nodes and users, i.e. an API for loading and identifying entities. For more information, see the online handbook entry for Entity module', array('!url' => 'http://drupal.org/handbook/modules/entity')) . '
';
+ return $output;
+ }
+}
+
+/**
+ * Implements hook_modules_preenable().
+ */
+function entity_modules_preenable() {
+ entity_info_cache_clear();
+}
+
+/**
+ * Implements hook_modules_disabled().
+ */
+function entity_modules_disabled() {
+ entity_info_cache_clear();
+}
+
+/**
+ * Gets the entity info array of an entity type.
+ *
+ * @see hook_entity_info()
+ * @see hook_entity_info_alter()
+ *
+ * @param $entity_type
+ * The entity type, e.g. node, for which the info shall be returned, or NULL
+ * to return an array with info about all types.
+ */
+function entity_get_info($entity_type = NULL) {
+ global $language;
+
+ // Use the advanced drupal_static() pattern, since this is called very often.
+ static $drupal_static_fast;
+ if (!isset($drupal_static_fast)) {
+ $drupal_static_fast['entity_info'] = &drupal_static(__FUNCTION__);
+ }
+ $entity_info = &$drupal_static_fast['entity_info'];
+
+ // hook_entity_info() includes translated strings, so each language is cached
+ // separately.
+ $langcode = $language->language;
+
+ if (empty($entity_info)) {
+ if ($cache = cache()->get("entity_info:$langcode")) {
+ $entity_info = $cache->data;
+ }
+ else {
+ $entity_info = module_invoke_all('entity_info');
+ // Merge in default values.
+ foreach ($entity_info as $name => $data) {
+ $entity_info[$name] += array(
+ 'fieldable' => FALSE,
+ 'controller class' => 'DrupalDefaultEntityController',
+ 'static cache' => TRUE,
+ 'field cache' => TRUE,
+ 'load hook' => $name . '_load',
+ 'bundles' => array(),
+ 'view modes' => array(),
+ 'entity keys' => array(),
+ 'translation' => array(),
+ );
+ $entity_info[$name]['entity keys'] += array(
+ 'revision' => '',
+ 'bundle' => '',
+ );
+ foreach ($entity_info[$name]['view modes'] as $view_mode => $view_mode_info) {
+ $entity_info[$name]['view modes'][$view_mode] += array(
+ 'custom settings' => FALSE,
+ );
+ }
+ // If no bundle key is provided, assume a single bundle, named after
+ // the entity type.
+ if (empty($entity_info[$name]['entity keys']['bundle']) && empty($entity_info[$name]['bundles'])) {
+ $entity_info[$name]['bundles'] = array($name => array('label' => $entity_info[$name]['label']));
+ }
+ // Prepare entity schema fields SQL info for
+ // DrupalEntityControllerInterface::buildQuery().
+ if (isset($entity_info[$name]['base table'])) {
+ $entity_info[$name]['schema_fields_sql']['base table'] = drupal_schema_fields_sql($entity_info[$name]['base table']);
+ if (isset($entity_info[$name]['revision table'])) {
+ $entity_info[$name]['schema_fields_sql']['revision table'] = drupal_schema_fields_sql($entity_info[$name]['revision table']);
+ }
+ }
+ }
+ // Let other modules alter the entity info.
+ drupal_alter('entity_info', $entity_info);
+ cache()->set("entity_info:$langcode", $entity_info);
+ }
+ }
+
+ if (empty($entity_type)) {
+ return $entity_info;
+ }
+ elseif (isset($entity_info[$entity_type])) {
+ return $entity_info[$entity_type];
+ }
+}
+
+/**
+ * Resets the cached information about entity types.
+ */
+function entity_info_cache_clear() {
+ drupal_static_reset('entity_get_info');
+ // Clear all languages.
+ cache()->deletePrefix('entity_info:');
+}
+
+/**
+ * Helper function to extract id, vid, and bundle name from an entity.
+ *
+ * @param $entity_type
+ * The entity type; e.g. 'node' or 'user'.
+ * @param $entity
+ * The entity from which to extract values.
+ * @return
+ * A numerically indexed array (not a hash table) containing these
+ * elements:
+ * 0: primary id of the entity
+ * 1: revision id of the entity, or NULL if $entity_type is not versioned
+ * 2: bundle name of the entity
+ */
+function entity_extract_ids($entity_type, $entity) {
+ $info = entity_get_info($entity_type);
+
+ // Objects being created might not have id/vid yet.
+ $id = isset($entity->{$info['entity keys']['id']}) ? $entity->{$info['entity keys']['id']} : NULL;
+ $vid = ($info['entity keys']['revision'] && isset($entity->{$info['entity keys']['revision']})) ? $entity->{$info['entity keys']['revision']} : NULL;
+
+ if (!empty($info['entity keys']['bundle'])) {
+ // Explicitly fail for malformed entities missing the bundle property.
+ if (!isset($entity->{$info['entity keys']['bundle']}) || $entity->{$info['entity keys']['bundle']} === '') {
+ throw new EntityMalformedException(t('Missing bundle property on entity of type @entity_type.', array('@entity_type' => $entity_type)));
+ }
+ $bundle = $entity->{$info['entity keys']['bundle']};
+ }
+ else {
+ // The entity type provides no bundle key: assume a single bundle, named
+ // after the entity type.
+ $bundle = $entity_type;
+ }
+
+ return array($id, $vid, $bundle);
+}
+
+/**
+ * Helper function to assemble an object structure with initial ids.
+ *
+ * This function can be seen as reciprocal to entity_extract_ids().
+ *
+ * @param $entity_type
+ * The entity type; e.g. 'node' or 'user'.
+ * @param $ids
+ * A numerically indexed array, as returned by entity_extract_ids(),
+ * containing these elements:
+ * 0: primary id of the entity
+ * 1: revision id of the entity, or NULL if $entity_type is not versioned
+ * 2: bundle name of the entity, or NULL if $entity_type has no bundles
+ *
+ * @return
+ * An entity structure, initialized with the ids provided.
+ */
+function entity_create_stub_entity($entity_type, $ids) {
+ $entity = new stdClass();
+ $info = entity_get_info($entity_type);
+ $entity->{$info['entity keys']['id']} = $ids[0];
+ if (!empty($info['entity keys']['revision']) && isset($ids[1])) {
+ $entity->{$info['entity keys']['revision']} = $ids[1];
+ }
+ if (!empty($info['entity keys']['bundle']) && isset($ids[2])) {
+ $entity->{$info['entity keys']['bundle']} = $ids[2];
+ }
+ return $entity;
+}
+
+/**
+ * Loads entities from the database.
+ *
+ * This function should be used whenever you need to load more than one entity
+ * from the database. The entities are loaded into memory and will not require
+ * database access if loaded again during the same page request.
+ *
+ * The actual loading is done through a class that has to implement the
+ * DrupalEntityControllerInterface interface. By default,
+ * DrupalDefaultEntityController is used. Entity types can specify that a
+ * different class should be used by setting the 'controller class' key in
+ * hook_entity_info(). These classes can either implement the
+ * DrupalEntityControllerInterface interface, or, most commonly, extend the
+ * DrupalDefaultEntityController class. See node_entity_info() and the
+ * NodeController in node.module as an example.
+ *
+ * @see hook_entity_info()
+ * @see DrupalEntityControllerInterface
+ * @see DrupalDefaultEntityController
+ * @see EntityFieldQuery
+ *
+ * @param $entity_type
+ * The entity type to load, e.g. node or user.
+ * @param $ids
+ * An array of entity IDs, or FALSE to load all entities.
+ * @param $conditions
+ * (deprecated) An associative array of conditions on the base table, where
+ * the keys are the database fields and the values are the values those
+ * fields must have. Instead, it is preferable to use EntityFieldQuery to
+ * retrieve a list of entity IDs loadable by this function.
+ * @param $reset
+ * Whether to reset the internal cache for the requested entity type.
+ *
+ * @return
+ * An array of entity objects indexed by their ids.
+ *
+ * @todo Remove $conditions in Drupal 8.
+ */
+function entity_load($entity_type, $ids = FALSE, $conditions = array(), $reset = FALSE) {
+ if ($reset) {
+ entity_get_controller($entity_type)->resetCache();
+ }
+ return entity_get_controller($entity_type)->load($ids, $conditions);
+}
+
+/**
+ * Loads the unchanged, i.e. not modified, entity from the database.
+ *
+ * Unlike entity_load() this function ensures the entity is directly loaded from
+ * the database, thus bypassing any static cache. In particular, this function
+ * is useful to determine changes by comparing the entity being saved to the
+ * stored entity.
+ *
+ * @param $entity_type
+ * The entity type to load, e.g. node or user.
+ * @param $id
+ * The id of the entity to load.
+ *
+ * @return
+ * The unchanged entity, or FALSE if the entity cannot be loaded.
+ */
+function entity_load_unchanged($entity_type, $id) {
+ entity_get_controller($entity_type)->resetCache(array($id));
+ $result = entity_get_controller($entity_type)->load(array($id));
+ return reset($result);
+}
+
+/**
+ * Gets the entity controller class for an entity type.
+ */
+function entity_get_controller($entity_type) {
+ $controllers = &drupal_static(__FUNCTION__, array());
+ if (!isset($controllers[$entity_type])) {
+ $type_info = entity_get_info($entity_type);
+ $class = $type_info['controller class'];
+ $controllers[$entity_type] = new $class($entity_type);
+ }
+ return $controllers[$entity_type];
+}
+
+/**
+ * Invokes hook_entity_prepare_view().
+ *
+ * If adding a new entity similar to nodes, comments or users, you should
+ * invoke this function during the ENTITY_build_content() or
+ * ENTITY_view_multiple() phases of rendering to allow other modules to alter
+ * the objects during this phase. This is needed for situations where
+ * information needs to be loaded outside of ENTITY_load() - particularly
+ * when loading entities into one another - i.e. a user object into a node, due
+ * to the potential for unwanted side-effects such as caching and infinite
+ * recursion. By convention, entity_prepare_view() is called after
+ * field_attach_prepare_view() to allow entity level hooks to act on content
+ * loaded by field API.
+ *
+ * @see hook_entity_prepare_view()
+ *
+ * @param $entity_type
+ * The type of entity, i.e. 'node', 'user'.
+ * @param $entities
+ * The entity objects which are being prepared for view, keyed by object ID.
+ */
+function entity_prepare_view($entity_type, $entities) {
+ // To ensure hooks are only run once per entity, check for an
+ // entity_view_prepared flag and only process items without it.
+ // @todo: resolve this more generally for both entity and field level hooks.
+ $prepare = array();
+ foreach ($entities as $id => $entity) {
+ if (empty($entity->entity_view_prepared)) {
+ // Add this entity to the items to be prepared.
+ $prepare[$id] = $entity;
+
+ // Mark this item as prepared.
+ $entity->entity_view_prepared = TRUE;
+ }
+ }
+
+ if (!empty($prepare)) {
+ module_invoke_all('entity_prepare_view', $prepare, $entity_type);
+ }
+}
+
+/**
+ * Returns the uri elements of an entity.
+ *
+ * @param $entity_type
+ * The entity type; e.g. 'node' or 'user'.
+ * @param $entity
+ * The entity for which to generate a path.
+ *
+ * @return
+ * An array containing the 'path' and 'options' keys used to build the uri of
+ * the entity, and matching the signature of url(). NULL if the entity has no
+ * uri of its own.
+ */
+function entity_uri($entity_type, $entity) {
+ $info = entity_get_info($entity_type);
+ list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+
+ // A bundle-specific callback takes precedence over the generic one for the
+ // entity type.
+ if (isset($info['bundles'][$bundle]['uri callback'])) {
+ $uri_callback = $info['bundles'][$bundle]['uri callback'];
+ }
+ elseif (isset($info['uri callback'])) {
+ $uri_callback = $info['uri callback'];
+ }
+ else {
+ return NULL;
+ }
+
+ // Invoke the callback to get the URI. If there is no callback, return NULL.
+ if (isset($uri_callback) && function_exists($uri_callback)) {
+ $uri = $uri_callback($entity);
+ // Pass the entity data to url() so that alter functions do not need to
+ // lookup this entity again.
+ $uri['options']['entity_type'] = $entity_type;
+ $uri['options']['entity'] = $entity;
+ return $uri;
+ }
+}
+
+/**
+ * Returns the label of an entity.
+ *
+ * See the 'label callback' component of the hook_entity_info() return value
+ * for more information.
+ *
+ * @param $entity_type
+ * The entity type; e.g., 'node' or 'user'.
+ * @param $entity
+ * The entity for which to generate the label.
+ *
+ * @return
+ * The entity label, or FALSE if not found.
+ */
+function entity_label($entity_type, $entity) {
+ $label = FALSE;
+ $info = entity_get_info($entity_type);
+ if (isset($info['label callback']) && function_exists($info['label callback'])) {
+ $label = $info['label callback']($entity_type, $entity);
+ }
+ elseif (!empty($info['entity keys']['label']) && isset($entity->{$info['entity keys']['label']})) {
+ $label = $entity->{$info['entity keys']['label']};
+ }
+
+ return $label;
+}
+
+/**
+ * Helper function for attaching field API validation to entity forms.
+ */
+function entity_form_field_validate($entity_type, $form, &$form_state) {
+ // All field attach API functions act on an entity object, but during form
+ // validation, we don't have one. $form_state contains the entity as it was
+ // prior to processing the current form submission, and we must not update it
+ // until we have fully validated the submitted input. Therefore, for
+ // validation, act on a pseudo entity created out of the form values.
+ $pseudo_entity = (object) $form_state['values'];
+ field_attach_form_validate($entity_type, $pseudo_entity, $form, $form_state);
+}
+
+/**
+ * Helper function for copying submitted values to entity properties for simple entity forms.
+ *
+ * During the submission handling of an entity form's "Save", "Preview", and
+ * possibly other buttons, the form state's entity needs to be updated with the
+ * submitted form values. Each entity form implements its own builder function
+ * for doing this, appropriate for the particular entity and form, whereas
+ * modules may specify additional builder functions in $form['#entity_builders']
+ * for copying the form values of added form elements to entity properties.
+ * Many of the main entity builder functions can call this helper function to
+ * re-use its logic of copying $form_state['values'][PROPERTY] values to
+ * $entity->PROPERTY for all entries in $form_state['values'] that are not field
+ * data, and calling field_attach_submit() to copy field data. Apart from that
+ * this helper invokes any additional builder functions that have been specified
+ * in $form['#entity_builders'].
+ *
+ * For some entity forms (e.g., forms with complex non-field data and forms that
+ * simultaneously edit multiple entities), this behavior may be inappropriate,
+ * so the builder function for such forms needs to implement the required
+ * functionality instead of calling this function.
+ */
+function entity_form_submit_build_entity($entity_type, $entity, $form, &$form_state) {
+ $info = entity_get_info($entity_type);
+ list(, , $bundle) = entity_extract_ids($entity_type, $entity);
+
+ // Copy top-level form values that are not for fields to entity properties,
+ // without changing existing entity properties that are not being edited by
+ // this form. Copying field values must be done using field_attach_submit().
+ $values_excluding_fields = $info['fieldable'] ? array_diff_key($form_state['values'], field_info_instances($entity_type, $bundle)) : $form_state['values'];
+ foreach ($values_excluding_fields as $key => $value) {
+ $entity->$key = $value;
+ }
+
+ // Invoke all specified builders for copying form values to entity properties.
+ if (isset($form['#entity_builders'])) {
+ foreach ($form['#entity_builders'] as $function) {
+ $function($entity_type, $entity, $form, $form_state);
+ }
+ }
+
+ // Copy field values to the entity.
+ if ($info['fieldable']) {
+ field_attach_submit($entity_type, $entity, $form, $form_state);
+ }
+}
+
+/**
+ * Exception thrown when a malformed entity is passed.
+ */
+class EntityMalformedException extends Exception { }
+
diff --git a/includes/entity.inc b/modules/entity/entity.query.inc
similarity index 71%
rename from includes/entity.inc
rename to modules/entity/entity.query.inc
index 31679fdccb9a..b4e91a2f9f40 100644
--- a/includes/entity.inc
+++ b/modules/entity/entity.query.inc
@@ -1,388 +1,9 @@
$value.
- *
- * @return
- * An array of entity objects indexed by their ids. When no results are
- * found, an empty array is returned.
- */
- public function load($ids = array(), $conditions = array());
-}
-
-/**
- * Default implementation of DrupalEntityControllerInterface.
- *
- * This class can be used as-is by most simple entity types. Entity types
- * requiring special handling can extend the class.
- */
-class DrupalDefaultEntityController implements DrupalEntityControllerInterface {
-
- /**
- * Static cache of entities.
- *
- * @var array
- */
- protected $entityCache;
-
- /**
- * Entity type for this controller instance.
- *
- * @var string
- */
- protected $entityType;
-
- /**
- * Array of information about the entity.
- *
- * @var array
- *
- * @see entity_get_info()
- */
- protected $entityInfo;
-
- /**
- * Additional arguments to pass to hook_TYPE_load().
- *
- * Set before calling DrupalDefaultEntityController::attachLoad().
- *
- * @var array
- */
- protected $hookLoadArguments;
-
- /**
- * Name of the entity's ID field in the entity database table.
- *
- * @var string
- */
- protected $idKey;
-
- /**
- * Name of entity's revision database table field, if it supports revisions.
- *
- * Has the value FALSE if this entity does not use revisions.
- *
- * @var string
- */
- protected $revisionKey;
-
- /**
- * The table that stores revisions, if the entity supports revisions.
- *
- * @var string
- */
- protected $revisionTable;
-
- /**
- * Whether this entity type should use the static cache.
- *
- * Set by entity info.
- *
- * @var boolean
- */
- protected $cache;
-
- /**
- * Constructor: sets basic variables.
- */
- public function __construct($entityType) {
- $this->entityType = $entityType;
- $this->entityInfo = entity_get_info($entityType);
- $this->entityCache = array();
- $this->hookLoadArguments = array();
- $this->idKey = $this->entityInfo['entity keys']['id'];
-
- // Check if the entity type supports revisions.
- if (!empty($this->entityInfo['entity keys']['revision'])) {
- $this->revisionKey = $this->entityInfo['entity keys']['revision'];
- $this->revisionTable = $this->entityInfo['revision table'];
- }
- else {
- $this->revisionKey = FALSE;
- }
-
- // Check if the entity type supports static caching of loaded entities.
- $this->cache = !empty($this->entityInfo['static cache']);
- }
-
- /**
- * Implements DrupalEntityControllerInterface::resetCache().
- */
- public function resetCache(array $ids = NULL) {
- if (isset($ids)) {
- foreach ($ids as $id) {
- unset($this->entityCache[$id]);
- }
- }
- else {
- $this->entityCache = array();
- }
- }
-
- /**
- * Implements DrupalEntityControllerInterface::load().
- */
- public function load($ids = array(), $conditions = array()) {
- $entities = array();
-
- // Revisions are not statically cached, and require a different query to
- // other conditions, so separate the revision id into its own variable.
- if ($this->revisionKey && isset($conditions[$this->revisionKey])) {
- $revision_id = $conditions[$this->revisionKey];
- unset($conditions[$this->revisionKey]);
- }
- else {
- $revision_id = FALSE;
- }
-
- // Create a new variable which is either a prepared version of the $ids
- // array for later comparison with the entity cache, or FALSE if no $ids
- // were passed. The $ids array is reduced as items are loaded from cache,
- // and we need to know if it's empty for this reason to avoid querying the
- // database when all requested entities are loaded from cache.
- $passed_ids = !empty($ids) ? array_flip($ids) : FALSE;
- // Try to load entities from the static cache, if the entity type supports
- // static caching.
- if ($this->cache && !$revision_id) {
- $entities += $this->cacheGet($ids, $conditions);
- // If any entities were loaded, remove them from the ids still to load.
- if ($passed_ids) {
- $ids = array_keys(array_diff_key($passed_ids, $entities));
- }
- }
-
- // Load any remaining entities from the database. This is the case if $ids
- // is set to FALSE (so we load all entities), if there are any ids left to
- // load, if loading a revision, or if $conditions was passed without $ids.
- if ($ids === FALSE || $ids || $revision_id || ($conditions && !$passed_ids)) {
- // Build the query.
- $query = $this->buildQuery($ids, $conditions, $revision_id);
- $queried_entities = $query
- ->execute()
- ->fetchAllAssoc($this->idKey);
- }
-
- // Pass all entities loaded from the database through $this->attachLoad(),
- // which attaches fields (if supported by the entity type) and calls the
- // entity type specific load callback, for example hook_node_load().
- if (!empty($queried_entities)) {
- $this->attachLoad($queried_entities, $revision_id);
- $entities += $queried_entities;
- }
-
- if ($this->cache) {
- // Add entities to the cache if we are not loading a revision.
- if (!empty($queried_entities) && !$revision_id) {
- $this->cacheSet($queried_entities);
- }
- }
-
- // Ensure that the returned array is ordered the same as the original
- // $ids array if this was passed in and remove any invalid ids.
- if ($passed_ids) {
- // Remove any invalid ids from the array.
- $passed_ids = array_intersect_key($passed_ids, $entities);
- foreach ($entities as $entity) {
- $passed_ids[$entity->{$this->idKey}] = $entity;
- }
- $entities = $passed_ids;
- }
-
- return $entities;
- }
-
- /**
- * Builds the query to load the entity.
- *
- * This has full revision support. For entities requiring special queries,
- * the class can be extended, and the default query can be constructed by
- * calling parent::buildQuery(). This is usually necessary when the object
- * being loaded needs to be augmented with additional data from another
- * table, such as loading node type into comments or vocabulary machine name
- * into terms, however it can also support $conditions on different tables.
- * See CommentController::buildQuery() or TaxonomyTermController::buildQuery()
- * for examples.
- *
- * @param $ids
- * An array of entity IDs, or FALSE to load all entities.
- * @param $conditions
- * An array of conditions in the form 'field' => $value.
- * @param $revision_id
- * The ID of the revision to load, or FALSE if this query is asking for the
- * most current revision(s).
- *
- * @return SelectQuery
- * A SelectQuery object for loading the entity.
- */
- protected function buildQuery($ids, $conditions = array(), $revision_id = FALSE) {
- $query = db_select($this->entityInfo['base table'], 'base');
-
- $query->addTag($this->entityType . '_load_multiple');
-
- if ($revision_id) {
- $query->join($this->revisionTable, 'revision', "revision.{$this->idKey} = base.{$this->idKey} AND revision.{$this->revisionKey} = :revisionId", array(':revisionId' => $revision_id));
- }
- elseif ($this->revisionKey) {
- $query->join($this->revisionTable, 'revision', "revision.{$this->revisionKey} = base.{$this->revisionKey}");
- }
-
- // Add fields from the {entity} table.
- $entity_fields = $this->entityInfo['schema_fields_sql']['base table'];
-
- if ($this->revisionKey) {
- // Add all fields from the {entity_revision} table.
- $entity_revision_fields = drupal_map_assoc($this->entityInfo['schema_fields_sql']['revision table']);
- // The id field is provided by entity, so remove it.
- unset($entity_revision_fields[$this->idKey]);
-
- // Remove all fields from the base table that are also fields by the same
- // name in the revision table.
- $entity_field_keys = array_flip($entity_fields);
- foreach ($entity_revision_fields as $key => $name) {
- if (isset($entity_field_keys[$name])) {
- unset($entity_fields[$entity_field_keys[$name]]);
- }
- }
- $query->fields('revision', $entity_revision_fields);
- }
-
- $query->fields('base', $entity_fields);
-
- if ($ids) {
- $query->condition("base.{$this->idKey}", $ids, 'IN');
- }
- if ($conditions) {
- foreach ($conditions as $field => $value) {
- $query->condition('base.' . $field, $value);
- }
- }
- return $query;
- }
-
- /**
- * Attaches data to entities upon loading.
- * This will attach fields, if the entity is fieldable. It calls
- * hook_entity_load() for modules which need to add data to all entities.
- * It also calls hook_TYPE_load() on the loaded entities. For example
- * hook_node_load() or hook_user_load(). If your hook_TYPE_load()
- * expects special parameters apart from the queried entities, you can set
- * $this->hookLoadArguments prior to calling the method.
- * See NodeController::attachLoad() for an example.
- *
- * @param $queried_entities
- * Associative array of query results, keyed on the entity ID.
- * @param $revision_id
- * ID of the revision that was loaded, or FALSE if teh most current revision
- * was loaded.
- */
- protected function attachLoad(&$queried_entities, $revision_id = FALSE) {
- // Attach fields.
- if ($this->entityInfo['fieldable']) {
- if ($revision_id) {
- field_attach_load_revision($this->entityType, $queried_entities);
- }
- else {
- field_attach_load($this->entityType, $queried_entities);
- }
- }
-
- // Call hook_entity_load().
- foreach (module_implements('entity_load') as $module) {
- $function = $module . '_entity_load';
- $function($queried_entities, $this->entityType);
- }
- // Call hook_TYPE_load(). The first argument for hook_TYPE_load() are
- // always the queried entities, followed by additional arguments set in
- // $this->hookLoadArguments.
- $args = array_merge(array($queried_entities), $this->hookLoadArguments);
- foreach (module_implements($this->entityInfo['load hook']) as $module) {
- call_user_func_array($module . '_' . $this->entityInfo['load hook'], $args);
- }
- }
-
- /**
- * Gets entities from the static cache.
- *
- * @param $ids
- * If not empty, return entities that match these IDs.
- * @param $conditions
- * If set, return entities that match all of these conditions.
- *
- * @return
- * Array of entities from the entity cache.
- */
- protected function cacheGet($ids, $conditions = array()) {
- $entities = array();
- // Load any available entities from the internal cache.
- if (!empty($this->entityCache)) {
- if ($ids) {
- $entities += array_intersect_key($this->entityCache, array_flip($ids));
- }
- // If loading entities only by conditions, fetch all available entities
- // from the cache. Entities which don't match are removed later.
- elseif ($conditions) {
- $entities = $this->entityCache;
- }
- }
-
- // Exclude any entities loaded from cache if they don't match $conditions.
- // This ensures the same behavior whether loading from memory or database.
- if ($conditions) {
- foreach ($entities as $entity) {
- $entity_values = (array) $entity;
- if (array_diff_assoc($conditions, $entity_values)) {
- unset($entities[$entity->{$this->idKey}]);
- }
- }
- }
- return $entities;
- }
-
- /**
- * Stores entities in the static entity cache.
- *
- * @param $entities
- * Entities to store in the cache.
- */
- protected function cacheSet($entities) {
- $this->entityCache += $entities;
- }
-}
/**
* Exception thrown by EntityFieldQuery() on unsupported query syntax.
@@ -613,6 +234,8 @@ class EntityFieldQuery {
* literals of the same type as the column.
* - 'BETWEEN': This operator expects $value to be an array of two literals
* of the same type as the column.
+ * The operator can be omitted, and will default to 'IN' if the value is an
+ * array, or to '=' otherwise.
*
* @return EntityFieldQuery
* The called object.
@@ -729,6 +352,8 @@ class EntityFieldQuery {
* literals of the same type as the column.
* - 'BETWEEN': This operator expects $value to be an array of two literals
* of the same type as the column.
+ * The operator can be omitted, and will default to 'IN' if the value is an
+ * array, or to '=' otherwise.
* @param $delta_group
* An arbitrary identifier: conditions in the same group must have the same
* $delta_group. For example, let's presume a multivalue field which has
@@ -934,7 +559,7 @@ class EntityFieldQuery {
}
/**
- * Enable a pager for the query.
+ * Enables a pager for the query.
*
* @param $limit
* An integer specifying the number of elements per page. If passed a false
@@ -962,7 +587,7 @@ class EntityFieldQuery {
}
/**
- * Enable sortable tables for this query.
+ * Enables sortable tables for this query.
*
* @param $headers
* An EFQ Header array based on which the order clause is added to the query.
@@ -1239,7 +864,7 @@ class EntityFieldQuery {
}
/**
- * Get the total number of results and initialize a pager for the query.
+ * Gets the total number of results and initialize a pager for the query.
*
* This query can be detected by checking for ($this->pager && $this->count),
* which allows a driver to return 0 from the count query and disable
@@ -1326,7 +951,3 @@ class EntityFieldQuery {
}
-/**
- * Exception thrown when a malformed entity is passed.
- */
-class EntityMalformedException extends Exception { }
diff --git a/modules/simpletest/tests/entity_cache_test.info b/modules/entity/tests/entity_cache_test.info
similarity index 100%
rename from modules/simpletest/tests/entity_cache_test.info
rename to modules/entity/tests/entity_cache_test.info
diff --git a/modules/simpletest/tests/entity_cache_test.module b/modules/entity/tests/entity_cache_test.module
similarity index 100%
rename from modules/simpletest/tests/entity_cache_test.module
rename to modules/entity/tests/entity_cache_test.module
diff --git a/modules/simpletest/tests/entity_cache_test_dependency.info b/modules/entity/tests/entity_cache_test_dependency.info
similarity index 100%
rename from modules/simpletest/tests/entity_cache_test_dependency.info
rename to modules/entity/tests/entity_cache_test_dependency.info
diff --git a/modules/simpletest/tests/entity_cache_test_dependency.module b/modules/entity/tests/entity_cache_test_dependency.module
similarity index 100%
rename from modules/simpletest/tests/entity_cache_test_dependency.module
rename to modules/entity/tests/entity_cache_test_dependency.module
diff --git a/modules/simpletest/tests/entity_crud_hook_test.info b/modules/entity/tests/entity_crud_hook_test.info
similarity index 100%
rename from modules/simpletest/tests/entity_crud_hook_test.info
rename to modules/entity/tests/entity_crud_hook_test.info
diff --git a/modules/simpletest/tests/entity_crud_hook_test.module b/modules/entity/tests/entity_crud_hook_test.module
similarity index 100%
rename from modules/simpletest/tests/entity_crud_hook_test.module
rename to modules/entity/tests/entity_crud_hook_test.module
diff --git a/modules/simpletest/tests/entity_crud_hook_test.test b/modules/entity/tests/entity_crud_hook_test.test
similarity index 100%
rename from modules/simpletest/tests/entity_crud_hook_test.test
rename to modules/entity/tests/entity_crud_hook_test.test
diff --git a/modules/simpletest/tests/entity_query.test b/modules/entity/tests/entity_query.test
similarity index 99%
rename from modules/simpletest/tests/entity_query.test
rename to modules/entity/tests/entity_query.test
index fb95518d168e..e540a90d99f8 100644
--- a/modules/simpletest/tests/entity_query.test
+++ b/modules/entity/tests/entity_query.test
@@ -1,6 +1,5 @@
{$instance['field_name']};
}
$cid = "field:$entity_type:$id";
- cache_set($cid, $data, 'cache_field');
+ cache('field')->set($cid, $data);
}
}
}
@@ -984,7 +984,7 @@ function field_attach_update($entity_type, $entity) {
$entity_info = entity_get_info($entity_type);
if ($entity_info['field cache']) {
- cache_clear_all("field:$entity_type:$id", 'cache_field');
+ cache('field')->delete("field:$entity_type:$id");
}
}
@@ -1021,7 +1021,7 @@ function field_attach_delete($entity_type, $entity) {
$entity_info = entity_get_info($entity_type);
if ($entity_info['field cache']) {
- cache_clear_all("field:$entity_type:$id", 'cache_field');
+ cache('field')->delete("field:$entity_type:$id");
}
}
diff --git a/modules/field/field.form.inc b/modules/field/field.form.inc
index 79fc14a20e87..be3685d875ee 100644
--- a/modules/field/field.form.inc
+++ b/modules/field/field.form.inc
@@ -273,7 +273,7 @@ function theme_field_multiple_value_form($variables) {
if ($element['#cardinality'] > 1 || $element['#cardinality'] == FIELD_CARDINALITY_UNLIMITED) {
$table_id = drupal_html_id($element['#field_name'] . '_values');
$order_class = $element['#field_name'] . '-delta-order';
- $required = !empty($element['#required']) ? '
*' : '';
+ $required = !empty($element['#required']) ? theme('form_required_marker', $variables) : '';
$header = array(
array(
diff --git a/modules/field/field.info b/modules/field/field.info
index bf419e39f19d..c61c501beb88 100644
--- a/modules/field/field.info
+++ b/modules/field/field.info
@@ -7,5 +7,6 @@ files[] = field.module
files[] = field.attach.inc
files[] = tests/field.test
dependencies[] = field_sql_storage
+dependencies[] = entity
required = TRUE
stylesheets[all][] = theme/field.css
diff --git a/modules/field/field.info.inc b/modules/field/field.info.inc
index 6b172dd3417b..cc8dac5d13fc 100644
--- a/modules/field/field.info.inc
+++ b/modules/field/field.info.inc
@@ -76,12 +76,12 @@ function _field_info_collate_types($reset = FALSE) {
if ($reset) {
$info = NULL;
// Clear all languages.
- cache_clear_all('field_info_types:', 'cache_field', TRUE);
+ cache('field')->deletePrefix('field_info_types:');
return;
}
if (!isset($info)) {
- if ($cached = cache_get("field_info_types:$langcode", 'cache_field')) {
+ if ($cached = cache('field')->get("field_info_types:$langcode")) {
$info = $cached->data;
}
else {
@@ -149,7 +149,7 @@ function _field_info_collate_types($reset = FALSE) {
}
drupal_alter('field_storage_info', $info['storage types']);
- cache_set("field_info_types:$langcode", $info, 'cache_field');
+ cache('field')->set("field_info_types:$langcode", $info);
}
}
@@ -181,12 +181,12 @@ function _field_info_collate_fields($reset = FALSE) {
if ($reset) {
$info = NULL;
- cache_clear_all('field_info_fields', 'cache_field');
+ cache('field')->delete('field_info_fields');
return;
}
if (!isset($info)) {
- if ($cached = cache_get('field_info_fields', 'cache_field')) {
+ if ($cached = cache('field')->get('field_info_fields')) {
$info = $cached->data;
}
else {
@@ -238,7 +238,7 @@ function _field_info_collate_fields($reset = FALSE) {
}
}
- cache_set('field_info_fields', $info, 'cache_field');
+ cache('field')->set('field_info_fields', $info);
}
}
diff --git a/modules/field/field.module b/modules/field/field.module
index 30166bdeecfd..8969547715dd 100644
--- a/modules/field/field.module
+++ b/modules/field/field.module
@@ -421,7 +421,7 @@ function field_system_info_alter(&$info, $file, $type) {
*/
function field_flush_caches() {
field_sync_field_status();
- return array('cache_field');
+ return array('field');
}
/**
@@ -772,7 +772,7 @@ function _field_extra_fields_pre_render($elements) {
* Clear the field info and field data caches.
*/
function field_cache_clear() {
- cache_clear_all('*', 'cache_field', TRUE);
+ cache('field')->flush();
field_info_cache_clear();
}
diff --git a/modules/field/field.multilingual.inc b/modules/field/field.multilingual.inc
index 5373d9708284..d9227455cca5 100644
--- a/modules/field/field.multilingual.inc
+++ b/modules/field/field.multilingual.inc
@@ -60,9 +60,9 @@
*/
/**
- * Implements hook_multilingual_settings_changed().
+ * Implements hook_locale_language_delete().
*/
-function field_multilingual_settings_changed() {
+function field_locale_language_delete() {
field_info_cache_clear();
}
@@ -228,7 +228,7 @@ function field_valid_language($langcode, $default = TRUE) {
return $langcode;
}
global $language_content;
- return $default ? language_default('language') : $language_content->language;
+ return $default ? language_default()->language : $language_content->language;
}
/**
diff --git a/modules/field/tests/field.test b/modules/field/tests/field.test
index 669fc37cf441..b361637146e9 100644
--- a/modules/field/tests/field.test
+++ b/modules/field/tests/field.test
@@ -824,18 +824,18 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase {
$cid = "field:$entity_type:{$entity_init->ftid}";
// Check that no initial cache entry is present.
- $this->assertFalse(cache_get($cid, 'cache_field'), t('Non-cached: no initial cache entry'));
+ $this->assertFalse(cache('field')->get($cid), t('Non-cached: no initial cache entry'));
// Save, and check that no cache entry is present.
$entity = clone($entity_init);
$entity->{$this->field_name}[$langcode] = $values;
field_attach_insert($entity_type, $entity);
- $this->assertFalse(cache_get($cid, 'cache_field'), t('Non-cached: no cache entry on insert'));
+ $this->assertFalse(cache('field')->get($cid), t('Non-cached: no cache entry on insert'));
// Load, and check that no cache entry is present.
$entity = clone($entity_init);
field_attach_load($entity_type, array($entity->ftid => $entity));
- $this->assertFalse(cache_get($cid, 'cache_field'), t('Non-cached: no cache entry on load'));
+ $this->assertFalse(cache('field')->get($cid), t('Non-cached: no cache entry on load'));
// Cacheable entity type.
@@ -846,24 +846,24 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase {
field_create_instance($instance);
// Check that no initial cache entry is present.
- $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no initial cache entry'));
+ $this->assertFalse(cache('field')->get($cid), t('Cached: no initial cache entry'));
// Save, and check that no cache entry is present.
$entity = clone($entity_init);
$entity->{$this->field_name}[$langcode] = $values;
field_attach_insert($entity_type, $entity);
- $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no cache entry on insert'));
+ $this->assertFalse(cache('field')->get($cid), t('Cached: no cache entry on insert'));
// Load a single field, and check that no cache entry is present.
$entity = clone($entity_init);
field_attach_load($entity_type, array($entity->ftid => $entity), FIELD_LOAD_CURRENT, array('field_id' => $this->field_id));
- $cache = cache_get($cid, 'cache_field');
- $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no cache entry on loading a single field'));
+ $cache = cache('field')->get($cid);
+ $this->assertFalse(cache('field')->get($cid), t('Cached: no cache entry on loading a single field'));
// Load, and check that a cache entry is present with the expected values.
$entity = clone($entity_init);
field_attach_load($entity_type, array($entity->ftid => $entity));
- $cache = cache_get($cid, 'cache_field');
+ $cache = cache('field')->get($cid);
$this->assertEqual($cache->data[$this->field_name][$langcode], $values, t('Cached: correct cache entry on load'));
// Update with different values, and check that the cache entry is wiped.
@@ -871,12 +871,12 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase {
$entity = clone($entity_init);
$entity->{$this->field_name}[$langcode] = $values;
field_attach_update($entity_type, $entity);
- $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no cache entry on update'));
+ $this->assertFalse(cache('field')->get($cid), t('Cached: no cache entry on update'));
// Load, and check that a cache entry is present with the expected values.
$entity = clone($entity_init);
field_attach_load($entity_type, array($entity->ftid => $entity));
- $cache = cache_get($cid, 'cache_field');
+ $cache = cache('field')->get($cid);
$this->assertEqual($cache->data[$this->field_name][$langcode], $values, t('Cached: correct cache entry on load'));
// Create a new revision, and check that the cache entry is wiped.
@@ -885,18 +885,18 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase {
$entity = clone($entity_init);
$entity->{$this->field_name}[$langcode] = $values;
field_attach_update($entity_type, $entity);
- $cache = cache_get($cid, 'cache_field');
- $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no cache entry on new revision creation'));
+ $cache = cache('field')->get($cid);
+ $this->assertFalse(cache('field')->get($cid), t('Cached: no cache entry on new revision creation'));
// Load, and check that a cache entry is present with the expected values.
$entity = clone($entity_init);
field_attach_load($entity_type, array($entity->ftid => $entity));
- $cache = cache_get($cid, 'cache_field');
+ $cache = cache('field')->get($cid);
$this->assertEqual($cache->data[$this->field_name][$langcode], $values, t('Cached: correct cache entry on load'));
// Delete, and check that the cache entry is wiped.
field_attach_delete($entity_type, $entity);
- $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no cache entry after delete'));
+ $this->assertFalse(cache('field')->get($cid), t('Cached: no cache entry after delete'));
}
/**
diff --git a/modules/field/theme/field.tpl.php b/modules/field/theme/field.tpl.php
index e4cd85cd025b..9e76e3b9c12f 100644
--- a/modules/field/theme/field.tpl.php
+++ b/modules/field/theme/field.tpl.php
@@ -49,11 +49,11 @@ After copying this file to your theme's folder and customizing it, remove this
HTML comment.
-->
>
-
+
>:
>
- $item) : ?>
+ $item): ?>
>
diff --git a/modules/file/file.field.inc b/modules/file/file.field.inc
index 2af3cb620be7..35696dda62c0 100644
--- a/modules/file/file.field.inc
+++ b/modules/file/file.field.inc
@@ -428,7 +428,7 @@ function file_field_widget_settings_form($field, $instance) {
'bar' => t('Bar with progress meter'),
),
'#default_value' => $settings['progress_indicator'],
- '#description' => t('The throbber display does not show the status of uploads but takes up space. The progress bar is helpful for monitoring progress on large uploads.'),
+ '#description' => t('The throbber display does not show the status of uploads but takes up less space. The progress bar is helpful for monitoring progress on large uploads.'),
'#weight' => 16,
'#access' => file_progress_implementation(),
);
@@ -447,37 +447,18 @@ function file_field_widget_form(&$form, &$form_state, $field, $instance, $langco
'description' => '',
);
- // Retrieve any values set in $form_state, as will be the case during Ajax
- // rebuilds of this form.
- if (isset($form_state['values'])) {
- $path = array_merge($element['#field_parents'], array($field['field_name'], $langcode));
- $path_exists = FALSE;
- $values = drupal_array_get_nested_value($form_state['values'], $path, $path_exists);
- if ($path_exists) {
- $items = $values;
- drupal_array_set_nested_value($form_state['values'], $path, NULL);
- }
+ // Load the items for form rebuilds from the field state as they might not be
+ // in $form_state['values'] because of validation limitations. Also, they are
+ // only passed in as $items when editing existing entities.
+ $field_state = field_form_get_state($element['#field_parents'], $field['field_name'], $langcode, $form_state);
+ if (isset($field_state['items'])) {
+ $items = $field_state['items'];
}
- foreach ($items as $delta => $item) {
- $items[$delta] = array_merge($defaults, $items[$delta]);
- // Remove any items from being displayed that are not needed.
- if ($items[$delta]['fid'] == 0) {
- unset($items[$delta]);
- }
- }
-
- // Re-index deltas after removing empty items.
- $items = array_values($items);
-
- // Update order according to weight.
- $items = _field_sort_items($field, $items);
-
// Essentially we use the managed_file type, extended with some enhancements.
$element_info = element_info('managed_file');
$element += array(
'#type' => 'managed_file',
- '#default_value' => isset($items[$delta]) ? $items[$delta] : $defaults,
'#upload_location' => file_field_widget_uri($field, $instance),
'#upload_validators' => file_field_widget_upload_validators($field, $instance),
'#value_callback' => 'file_field_widget_value',
@@ -487,6 +468,8 @@ function file_field_widget_form(&$form, &$form_state, $field, $instance, $langco
);
if ($field['cardinality'] == 1) {
+ // Set the default value.
+ $element['#default_value'] = !empty($items) ? $items[0] : $defaults;
// If there's only one field, return it as delta 0.
if (empty($element['#default_value']['fid'])) {
$element['#description'] = theme('file_upload_help', array('description' => $element['#description'], 'upload_validators' => $element['#upload_validators']));
@@ -495,15 +478,15 @@ function file_field_widget_form(&$form, &$form_state, $field, $instance, $langco
}
else {
// If there are multiple values, add an element for each existing one.
- $delta = -1;
- foreach ($items as $delta => $item) {
+ foreach ($items as $item) {
$elements[$delta] = $element;
$elements[$delta]['#default_value'] = $item;
$elements[$delta]['#weight'] = $delta;
+ $delta++;
}
- // And then add one more empty row for new uploads.
- $delta++;
- if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED || $delta < $field['cardinality']) {
+ // And then add one more empty row for new uploads except when this is a
+ // programmed form as it is not necessary.
+ if (($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED || $delta < $field['cardinality']) && empty($form_state['programmed'])) {
$elements[$delta] = $element;
$elements[$delta]['#default_value'] = $defaults;
$elements[$delta]['#weight'] = $delta;
@@ -757,6 +740,32 @@ function file_field_widget_submit($form, &$form_state) {
// so nothing is lost in doing this.
$parents = array_slice($form_state['triggering_element']['#parents'], 0, -2);
drupal_array_set_nested_value($form_state['input'], $parents, NULL);
+
+ $button = $form_state['triggering_element'];
+
+ // Go one level up in the form, to the widgets container.
+ $element = drupal_array_get_nested_value($form, array_slice($button['#array_parents'], 0, -1));
+ $field_name = $element['#field_name'];
+ $langcode = $element['#language'];
+ $parents = $element['#field_parents'];
+
+ $submitted_values = drupal_array_get_nested_value($form_state['values'], array_slice($button['#array_parents'], 0, -2));
+ foreach ($submitted_values as $delta => $submitted_value) {
+ if (!$submitted_value['fid']) {
+ unset($submitted_values[$delta]);
+ }
+ }
+
+ // Re-index deltas after removing empty items.
+ $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);
+
+ // Update items.
+ $field_state = field_form_get_state($parents, $field_name, $langcode, $form_state);
+ $field_state['items'] = $submitted_values;
+ field_form_set_state($parents, $field_name, $langcode, $form_state, $field_state);
}
/**
diff --git a/modules/file/file.module b/modules/file/file.module
index 83de0f622aee..4fc9985014e7 100644
--- a/modules/file/file.module
+++ b/modules/file/file.module
@@ -643,9 +643,18 @@ function file_managed_file_save_upload($element) {
function theme_file_managed_file($variables) {
$element = $variables['element'];
+ $attributes = array();
+ if (isset($element['#id'])) {
+ $attributes['id'] = $element['#id'];
+ }
+ if (!empty($element['#attributes']['class'])) {
+ $attributes['class'] = (array) $element['#attributes']['class'];
+ }
+ $attributes['class'][] = 'form-managed-file';
+
// This wrapper is required to apply JS behaviors and CSS styling.
$output = '';
- $output .= '
';
+ $output .= '
';
$output .= drupal_render_children($element);
$output .= '
';
return $output;
diff --git a/modules/file/tests/file.test b/modules/file/tests/file.test
index 32de9dcabb97..5474774bbd4a 100644
--- a/modules/file/tests/file.test
+++ b/modules/file/tests/file.test
@@ -380,7 +380,7 @@ class FileFieldWidgetTestCase extends FileFieldTestCase {
}
/**
- * Tests upload and remove buttons, with and without Ajax, for a multi-valued File field.
+ * Tests upload and remove buttons, with and without Ajax, for multiple multi-valued File field.
*/
function testMultiValuedWidget() {
// Use 'page' instead of 'article', so that the 'article' image field does
@@ -389,77 +389,106 @@ class FileFieldWidgetTestCase extends FileFieldTestCase {
// using a custom node type.
$type_name = 'page';
$field_name = strtolower($this->randomName());
+ $field_name2 = strtolower($this->randomName());
$this->createFileField($field_name, $type_name, array('cardinality' => 3));
+ $this->createFileField($field_name2, $type_name, array('cardinality' => 3));
+
$field = field_info_field($field_name);
$instance = field_info_instance('node', $field_name, $type_name);
+ $field2 = field_info_field($field_name2);
+ $instance2 = field_info_instance('node', $field_name2, $type_name);
+
$test_file = $this->getTestFile('text');
foreach (array('nojs', 'js') as $type) {
- // Visit the node creation form, and upload 3 files. Since the field has
- // cardinality of 3, ensure the "Upload" button is displayed until after
- // the 3rd file, and after that, isn't displayed.
+ // Visit the node creation form, and upload 3 files for each field. Since
+ // the field has cardinality of 3, ensure the "Upload" button is displayed
+ // until after the 3rd file, and after that, isn't displayed. Because
+ // SimpleTest triggers the last button with a given name, so upload to the
+ // second field first.
// @todo This is only testing a non-Ajax upload, because drupalPostAJAX()
// does not yet emulate jQuery's file upload.
+ //
$this->drupalGet("node/add/$type_name");
- for ($delta = 0; $delta < 3; $delta++) {
- $edit = array('files[' . $field_name . '_' . LANGUAGE_NONE . '_' . $delta . ']' => drupal_realpath($test_file->uri));
- // If the Upload button doesn't exist, drupalPost() will automatically
- // fail with an assertion message.
- $this->drupalPost(NULL, $edit, t('Upload'));
+ foreach (array($field_name2, $field_name) as $each_field_name) {
+ for ($delta = 0; $delta < 3; $delta++) {
+ $edit = array('files[' . $each_field_name . '_' . LANGUAGE_NONE . '_' . $delta . ']' => drupal_realpath($test_file->uri));
+ // If the Upload button doesn't exist, drupalPost() will automatically
+ // fail with an assertion message.
+ $this->drupalPost(NULL, $edit, t('Upload'));
+ }
}
- $this->assertNoFieldByXpath('//input[@type="submit"]', t('Upload'), t('After uploading 3 files, the "Upload" button is no longer displayed.'));
+ $this->assertNoFieldByXpath('//input[@type="submit"]', t('Upload'), t('After uploading 3 files for each field, the "Upload" button is no longer displayed.'));
- // Test clicking each "Remove" button. For extra robustness, test them out
- // of sequential order. They are 0-indexed, and get renumbered after each
- // iteration, so array(1, 1, 0) means:
- // - First remove the 2nd file.
- // - Then remove what is then the 2nd file (was originally the 3rd file).
- // - Then remove the first file.
- $num_expected_remove_buttons = 3;
- foreach (array(1, 1, 0) as $delta) {
- // 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)));
- foreach ($buttons as $i => $button) {
- $this->assertIdentical((string) $button['name'], $field_name . '_' . LANGUAGE_NONE . '_' . $i . '_remove_button');
- }
+ $num_expected_remove_buttons = 6;
- // "Click" the remove button (emulating either a nojs or js submission).
- $button_name = $field_name . '_' . LANGUAGE_NONE . '_' . $delta . '_remove_button';
- switch ($type) {
- case 'nojs':
- // drupalPost() takes a $submit parameter that is the value of the
- // button whose click we want to emulate. Since we have multiple
- // buttons with the value "Remove", and want to control which one we
- // use, we change the value of the other ones to something else.
- // Since non-clicked buttons aren't included in the submitted POST
- // data, and since drupalPost() will result in $this being updated
- // with a newly rebuilt form, this doesn't cause problems.
- foreach ($buttons as $button) {
- if ($button['name'] != $button_name) {
- $button['value'] = 'DUMMY';
- }
+ foreach (array($field_name, $field_name2) as $current_field_name) {
+ // How many uploaded files for the current field are remaining.
+ $remaining = 3;
+ // Test clicking each "Remove" button. For extra robustness, test them out
+ // of sequential order. They are 0-indexed, and get renumbered after each
+ // iteration, so array(1, 1, 0) means:
+ // - First remove the 2nd file.
+ // - Then remove what is then the 2nd file (was originally the 3rd file).
+ // - Then remove the first file.
+ foreach (array(1,1,0) as $delta) {
+ // 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)));
+ foreach ($buttons as $i => $button) {
+ $key = $i >= $remaining ? $i - $remaining : $i;
+ $check_field_name = $field_name2;
+ if ($current_field_name == $field_name && $i < $remaining) {
+ $check_field_name = $field_name;
}
- $this->drupalPost(NULL, array(), t('Remove'));
- break;
- case 'js':
- // drupalPostAJAX() lets us target the button precisely, so we don't
- // require the workaround used above for nojs.
- $this->drupalPostAJAX(NULL, array(), array($button_name => t('Remove')));
- break;
- }
- $num_expected_remove_buttons--;
- // Ensure we have a single Upload button, and that it is numbered
- // sequentially after the Remove buttons.
- $buttons = $this->xpath('//input[@type="submit" and @value="Upload"]');
- $this->assertTrue(is_array($buttons) && count($buttons) == 1 && ((string) $buttons[0]['name'] === ($field_name . '_' . LANGUAGE_NONE . '_' . $num_expected_remove_buttons . '_upload_button')), t('After removing a file, an "Upload" button is displayed (JSMode=%type).'));
+ $this->assertIdentical((string) $button['name'], $check_field_name . '_' . LANGUAGE_NONE . '_' . $key. '_remove_button');
+ }
+
+ // "Click" the remove button (emulating either a nojs or js submission).
+ $button_name = $current_field_name . '_' . LANGUAGE_NONE . '_' . $delta . '_remove_button';
+ switch ($type) {
+ case 'nojs':
+ // drupalPost() takes a $submit parameter that is the value of the
+ // button whose click we want to emulate. Since we have multiple
+ // buttons with the value "Remove", and want to control which one we
+ // use, we change the value of the other ones to something else.
+ // Since non-clicked buttons aren't included in the submitted POST
+ // data, and since drupalPost() will result in $this being updated
+ // with a newly rebuilt form, this doesn't cause problems.
+ foreach ($buttons as $button) {
+ if ($button['name'] != $button_name) {
+ $button['value'] = 'DUMMY';
+ }
+ }
+ $this->drupalPost(NULL, array(), t('Remove'));
+ break;
+ case 'js':
+ // drupalPostAJAX() lets us target the button precisely, so we don't
+ // require the workaround used above for nojs.
+ $this->drupalPostAJAX(NULL, array(), array($button_name => t('Remove')));
+ break;
+ }
+ $num_expected_remove_buttons--;
+ $remaining--;
+
+ // Ensure an "Upload" button for the current field is displayed with the
+ // 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)));
+
+ // 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)));
+ }
}
// 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.', array('%n' => $num_expected_remove_buttons, '%type' => $type)));
+ $this->assertNoFieldByXPath('//input[@type="submit"]', t('Remove'), t('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'));
diff --git a/modules/filter/filter.module b/modules/filter/filter.module
index 9e1481207cdc..a84d6f712714 100644
--- a/modules/filter/filter.module
+++ b/modules/filter/filter.module
@@ -260,7 +260,7 @@ function filter_format_save($format) {
$return = SAVED_UPDATED;
// Clear the filter cache whenever a text format is updated.
- cache_clear_all($format->format . ':', 'cache_filter', TRUE);
+ cache('filter')->deletePrefix($format->format . ':');
}
filter_formats_reset();
@@ -290,7 +290,7 @@ function filter_format_disable($format) {
// Clear the filter cache whenever a text format is disabled.
filter_formats_reset();
- cache_clear_all($format->format . ':', 'cache_filter', TRUE);
+ cache('filter')->deletePrefix($format->format . ':');
}
/**
@@ -395,7 +395,7 @@ function filter_formats($account = NULL) {
// All available formats are cached for performance.
if (!isset($formats['all'])) {
- if ($cache = cache_get("filter_formats:{$language->language}")) {
+ if ($cache = cache()->get("filter_formats:{$language->language}")) {
$formats['all'] = $cache->data;
}
else {
@@ -407,7 +407,7 @@ function filter_formats($account = NULL) {
->execute()
->fetchAllAssoc('format');
- cache_set("filter_formats:{$language->language}", $formats['all']);
+ cache()->set("filter_formats:{$language->language}", $formats['all']);
}
}
@@ -430,8 +430,8 @@ function filter_formats($account = NULL) {
* @see filter_formats()
*/
function filter_formats_reset() {
- cache_clear_all('filter_formats', 'cache', TRUE);
- cache_clear_all('filter_list_format', 'cache', TRUE);
+ cache()->deletePrefix('filter_formats');
+ cache()->deletePrefix('filter_list_format');
drupal_static_reset('filter_list_format');
drupal_static_reset('filter_formats');
}
@@ -657,7 +657,7 @@ function filter_list_format($format_id) {
$filter_info = filter_get_filters();
if (!isset($filters['all'])) {
- if ($cache = cache_get('filter_list_format')) {
+ if ($cache = cache()->get('filter_list_format')) {
$filters['all'] = $cache->data;
}
else {
@@ -665,7 +665,7 @@ function filter_list_format($format_id) {
foreach ($result as $record) {
$filters['all'][$record->format][$record->name] = $record;
}
- cache_set('filter_list_format', $filters['all']);
+ cache()->set('filter_list_format', $filters['all']);
}
}
@@ -731,7 +731,7 @@ function check_markup($text, $format_id = NULL, $langcode = '', $cache = FALSE)
$cache_id = '';
if ($cache) {
$cache_id = $format->format . ':' . $langcode . ':' . hash('sha256', $text);
- if ($cached = cache_get($cache_id, 'cache_filter')) {
+ if ($cached = cache('filter')->get($cache_id)) {
return $cached->data;
}
}
@@ -765,7 +765,7 @@ function check_markup($text, $format_id = NULL, $langcode = '', $cache = FALSE)
// automatically flushed when the text format is updated.
// @see filter_format_save()
if ($cache) {
- cache_set($cache_id, $text, 'cache_filter');
+ cache('filter')->set($cache_id, $text);
}
return $text;
@@ -1079,7 +1079,7 @@ function filter_dom_serialize($dom_document) {
foreach ($body_node->childNodes as $child_node) {
$body_content .= $dom_document->saveXML($child_node);
}
- return preg_replace('|<([^> ]*)/>|i', '<$1 />', $body_content);
+ return $body_content;
}
/**
diff --git a/modules/forum/forum.admin.inc b/modules/forum/forum.admin.inc
index 1e6b36551591..49c71d90a0bf 100644
--- a/modules/forum/forum.admin.inc
+++ b/modules/forum/forum.admin.inc
@@ -55,7 +55,7 @@ function forum_form_forum($form, &$form_state, $edit = array()) {
$form['vid'] = array('#type' => 'hidden', '#value' => variable_get('forum_nav_vocabulary', ''));
$form['actions'] = array('#type' => 'actions');
- $form['actions']['submit' ] = array('#type' => 'submit', '#value' => t('Save'));
+ $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save'));
if ($edit['tid']) {
$form['actions']['delete'] = array('#type' => 'submit', '#value' => t('Delete'));
$form['tid'] = array('#type' => 'hidden', '#value' => $edit['tid']);
diff --git a/modules/forum/forum.module b/modules/forum/forum.module
index 5bb43925bc55..0dcea5adaa6b 100644
--- a/modules/forum/forum.module
+++ b/modules/forum/forum.module
@@ -1127,7 +1127,6 @@ function template_preprocess_forum_topic_list(&$variables) {
$variables['topics'][$id]->title = l($topic->title, "node/$topic->nid");
$variables['topics'][$id]->message = '';
}
- $topic->uid = $topic->last_comment_uid ? $topic->last_comment_uid : $topic->uid;
$variables['topics'][$id]->created = theme('forum_submitted', array('topic' => $topic));
$variables['topics'][$id]->last_reply = theme('forum_submitted', array('topic' => isset($topic->last_reply) ? $topic->last_reply : NULL));
diff --git a/modules/image/image.api.php b/modules/image/image.api.php
index acb3f9c1983f..52490c3eb747 100644
--- a/modules/image/image.api.php
+++ b/modules/image/image.api.php
@@ -110,7 +110,7 @@ function hook_image_style_delete($style) {
*/
function hook_image_style_flush($style) {
// Empty cached data that contains information about the style.
- cache_clear_all('*', 'cache_mymodule', TRUE);
+ cache('mymodule')->flush();
}
/**
diff --git a/modules/image/image.install b/modules/image/image.install
index 6f37ad00ed9e..911a445152fd 100644
--- a/modules/image/image.install
+++ b/modules/image/image.install
@@ -28,9 +28,6 @@ function image_uninstall() {
function image_schema() {
$schema = array();
- $schema['cache_image'] = drupal_get_schema_unprocessed('system', 'cache');
- $schema['cache_image']['description'] = 'Cache table used to store information about image manipulations that are in-progress.';
-
$schema['image_styles'] = array(
'description' => 'Stores configuration options for image styles.',
'fields' => array(
diff --git a/modules/image/image.module b/modules/image/image.module
index 008a36513a94..9e1c57ead92d 100644
--- a/modules/image/image.module
+++ b/modules/image/image.module
@@ -258,13 +258,6 @@ function image_system_file_system_settings_submit($form, &$form_state) {
}
}
-/**
- * Implements hook_flush_caches().
- */
-function image_flush_caches() {
- return array('cache_image');
-}
-
/**
* Implements hook_file_download().
*
@@ -492,7 +485,7 @@ function image_styles() {
// Grab from cache or build the array.
if (!isset($styles)) {
- if ($cache = cache_get('image_styles', 'cache')) {
+ if ($cache = cache()->get('image_styles')) {
$styles = $cache->data;
}
else {
@@ -534,7 +527,7 @@ function image_styles() {
}
drupal_alter('image_styles', $styles);
- cache_set('image_styles', $styles);
+ cache()->set('image_styles', $styles);
}
}
@@ -827,8 +820,8 @@ function image_style_flush($style) {
module_invoke_all('image_style_flush', $style);
// Clear image style and effect caches.
- cache_clear_all('image_styles', 'cache');
- cache_clear_all('image_effects:', 'cache', TRUE);
+ cache()->delete('image_styles');
+ cache()->deletePrefix('image_effects:');
drupal_static_reset('image_styles');
drupal_static_reset('image_effects');
@@ -838,9 +831,9 @@ function image_style_flush($style) {
// Clear page caches when flushing.
if (module_exists('block')) {
- cache_clear_all('*', 'cache_block', TRUE);
+ cache('block')->flush();
}
- cache_clear_all('*', 'cache_page', TRUE);
+ cache('page')->flush();
}
/**
@@ -952,7 +945,7 @@ function image_effect_definitions() {
$effects = &drupal_static(__FUNCTION__);
if (!isset($effects)) {
- if ($cache = cache_get("image_effects:$langcode") && !empty($cache->data)) {
+ if ($cache = cache()->get("image_effects:$langcode") && !empty($cache->data)) {
$effects = $cache->data;
}
else {
@@ -969,7 +962,7 @@ function image_effect_definitions() {
}
uasort($effects, '_image_effect_definitions_sort');
drupal_alter('image_effect_info', $effects);
- cache_set("image_effects:$langcode", $effects);
+ cache()->set("image_effects:$langcode", $effects);
}
}
diff --git a/modules/locale/locale.admin.inc b/modules/locale/locale.admin.inc
index 79ec3a1866da..cd2edf5bd1a5 100644
--- a/modules/locale/locale.admin.inc
+++ b/modules/locale/locale.admin.inc
@@ -52,7 +52,7 @@ function locale_languages_overview_form() {
'#title' => t('Default language'),
'#title_display' => 'invisible',
'#options' => $options,
- '#default_value' => language_default('language'),
+ '#default_value' => language_default()->language,
);
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save configuration'));
@@ -168,7 +168,7 @@ function locale_languages_overview_form_submit($form, &$form_state) {
drupal_set_message(t('Configuration saved.'));
// Changing the language settings impacts the interface.
- cache_clear_all('*', 'cache_page', TRUE);
+ cache('page')->flush();
module_invoke_all('multilingual_settings_changed');
$form_state['redirect'] = 'admin/config/regional/language';
@@ -280,7 +280,7 @@ function _locale_languages_common_controls(&$form, $language = NULL) {
'#required' => TRUE,
'#default_value' => @$language->language,
'#disabled' => (isset($language->language)),
- '#description' => t('
RFC 4646 compliant language identifier. Language codes typically use a country code, and optionally, a script or regional variant name.
Examples: "en", "en-US" and "zh-Hant".', array('@rfc4646' => 'http://www.ietf.org/rfc/rfc4646.txt')),
+ '#description' => t('Use language codes as
defined by the W3C for interoperability.
Examples: "en", "en-gb" and "zh-hant".', array('@w3ctags' => 'http://www.w3.org/International/articles/language-tags/')),
);
}
$form['name'] = array('#type' => 'textfield',
@@ -307,7 +307,7 @@ function _locale_languages_common_controls(&$form, $language = NULL) {
'#title' => t('Language domain'),
'#maxlength' => 128,
'#default_value' => @$language->domain,
- '#description' => t('URL
including protocol to use for this language, if your
Detection and selection settings use URL domains. For the default language, this value may be left blank.
Modifying this value may break existing URLs. Use with caution in a production environment. Example: Specifying "http://example.de" or "http://de.example.com" as language domains for German results in URLs like "http://example.de/contact" and "http://de.example.com/contact", respectively.'),
+ '#description' => t('The domain name to use for this language if URL domains are used for
Detection and selection. Leave blank for the default language.
Changing this value may break existing URLs. Example: Specifying "de.example.com" as language domain for German will result in an URL like "http://de.example.com/contact".'),
);
$form['direction'] = array('#type' => 'radios',
'#title' => t('Direction'),
@@ -331,8 +331,8 @@ function locale_languages_predefined_form_validate($form, &$form_state) {
if (!isset($form_state['values']['name'])) {
// Predefined language selection.
- include_once DRUPAL_ROOT . '/includes/iso.inc';
- $predefined = _locale_get_predefined_list();
+ include_once DRUPAL_ROOT . '/includes/standard.inc';
+ $predefined = standard_language_list();
if (!isset($predefined[$langcode])) {
form_set_error('langcode', t('Invalid language code.'));
}
@@ -355,8 +355,8 @@ function locale_languages_predefined_form_submit($form, &$form_state) {
}
else {
// Predefined language selection.
- include_once DRUPAL_ROOT . '/includes/iso.inc';
- $predefined = _locale_get_predefined_list();
+ include_once DRUPAL_ROOT . '/includes/standard.inc';
+ $predefined = standard_language_list();
locale_add_language($langcode);
drupal_set_message(t('The language %language has been created and can now be used. More information is available on the
help screen.', array('%language' => t($predefined[$langcode][0]), '@locale-help' => url('admin/help/locale'))));
}
@@ -392,7 +392,7 @@ function locale_languages_edit_form_validate($form, &$form_state) {
if (!empty($form_state['values']['domain']) && $duplicate = db_query("SELECT language FROM {languages} WHERE domain = :domain AND language <> :language", array(':domain' => $form_state['values']['domain'], ':language' => $form_state['values']['langcode']))->fetchField()) {
form_set_error('domain', t('The domain (%domain) is already tied to a language (%language).', array('%domain' => $form_state['values']['domain'], '%language' => $duplicate->language)));
}
- if (empty($form_state['values']['prefix']) && language_default('language') != $form_state['values']['langcode'] && empty($form_state['values']['domain'])) {
+ if (empty($form_state['values']['prefix']) && language_default()->language != $form_state['values']['langcode'] && empty($form_state['values']['domain'])) {
form_set_error('prefix', t('Only the default language can have both the domain and prefix empty.'));
}
if (!empty($form_state['values']['prefix']) && $duplicate = db_query("SELECT language FROM {languages} WHERE prefix = :prefix AND language <> :language", array(':prefix' => $form_state['values']['prefix'], ':language' => $form_state['values']['langcode']))->fetchField()) {
@@ -439,7 +439,7 @@ function locale_languages_delete_form($form, &$form_state, $langcode) {
drupal_goto('admin/config/regional/language');
}
- if (language_default('language') == $langcode) {
+ if (language_default()->language == $langcode) {
drupal_set_message(t('The default language cannot be deleted.'));
drupal_goto('admin/config/regional/language');
}
@@ -461,37 +461,18 @@ function locale_languages_delete_form($form, &$form_state, $langcode) {
* Process language deletion submissions.
*/
function locale_languages_delete_form_submit($form, &$form_state) {
+ $langcode = $form_state['values']['langcode'];
$languages = language_list();
- if (isset($languages[$form_state['values']['langcode']])) {
- // Remove translations first.
- db_delete('locales_target')
- ->condition('language', $form_state['values']['langcode'])
- ->execute();
- cache_clear_all('locale:' . $form_state['values']['langcode'], 'cache');
- // With no translations, this removes existing JavaScript translations file.
- _locale_rebuild_js($form_state['values']['langcode']);
- // Remove the language.
- db_delete('languages')
- ->condition('language', $form_state['values']['langcode'])
- ->execute();
- db_update('node')
- ->fields(array('language' => ''))
- ->condition('language', $form_state['values']['langcode'])
- ->execute();
- if ($languages[$form_state['values']['langcode']]->enabled) {
- variable_set('language_count', variable_get('language_count', 1) - 1);
- }
- module_invoke_all('multilingual_settings_changed');
- $variables = array('%locale' => $languages[$form_state['values']['langcode']]->name);
+ $language = $languages[$langcode];
+
+ $success = locale_language_delete($langcode);
+
+ if ($success) {
+ $variables = array('%locale' => $language->name);
drupal_set_message(t('The language %locale has been removed.', $variables));
- watchdog('locale', 'The language %locale has been removed.', $variables);
}
- // Changing the language settings impacts the interface:
- cache_clear_all('*', 'cache_page', TRUE);
-
$form_state['redirect'] = 'admin/config/regional/language';
- return;
}
/**
diff --git a/modules/locale/locale.api.php b/modules/locale/locale.api.php
index e327401a6e67..96f5b778b835 100644
--- a/modules/locale/locale.api.php
+++ b/modules/locale/locale.api.php
@@ -154,19 +154,6 @@ function hook_language_negotiation_info_alter(array &$language_providers) {
}
}
-/**
- * Allow modules to react to language settings changes.
- *
- * Every module needing to act when the number of enabled languages changes
- * should implement this. This is an "internal" hook and should not be invoked
- * elsewhere. The typical implementation would trigger some kind of rebuilding,
- * this way system components could properly react to the change of the enabled
- * languages number.
- */
-function hook_multilingual_settings_changed() {
- field_info_cache_clear();
-}
-
/**
* Perform alterations on the language fallback candidates.
*
@@ -178,6 +165,20 @@ function hook_language_fallback_candidates_alter(array &$fallback_candidates) {
$fallback_candidates = array_reverse($fallback_candidates);
}
+/**
+ * Allow modules to react before the deletion of a language.
+ *
+ * @param $language
+ * The language object of the language that is about to be deleted.
+ */
+function hook_locale_language_delete($language) {
+ // On nodes with this language, unset the language
+ db_update('node')
+ ->fields(array('language' => ''))
+ ->condition('language', $language->language)
+ ->execute();
+}
+
/**
* @} End of "addtogroup hooks".
*/
diff --git a/modules/locale/locale.bulk.inc b/modules/locale/locale.bulk.inc
index 8d6ed8f3c173..d27c80a5ad2d 100644
--- a/modules/locale/locale.bulk.inc
+++ b/modules/locale/locale.bulk.inc
@@ -68,8 +68,8 @@ function locale_translate_import_form_submit($form, &$form_state) {
$languages = language_list('language');
$langcode = $form_state['values']['langcode'];
if (!isset($languages[$langcode])) {
- include_once DRUPAL_ROOT . '/includes/iso.inc';
- $predefined = _locale_get_predefined_list();
+ include_once DRUPAL_ROOT . '/includes/standard.inc';
+ $predefined = standard_language_list();
locale_add_language($langcode);
drupal_set_message(t('The language %language has been created.', array('%language' => t($predefined[$langcode][0]))));
}
diff --git a/modules/locale/locale.module b/modules/locale/locale.module
index 0c238be47bc3..9af706c34bdb 100644
--- a/modules/locale/locale.module
+++ b/modules/locale/locale.module
@@ -293,20 +293,6 @@ function locale_language_selector_form(&$form, &$form_state, $user) {
);
}
-/**
- * Implements hook_form_FORM_ID_alter().
- */
-function locale_form_path_admin_form_alter(&$form, &$form_state) {
- $form['language'] = array(
- '#type' => 'select',
- '#title' => t('Language'),
- '#options' => array(LANGUAGE_NONE => t('All languages')) + locale_language_list('name'),
- '#default_value' => $form['language']['#value'],
- '#weight' => -10,
- '#description' => t('A path alias set for a specific language will always be used when displaying this page in that language, and takes precedence over path aliases set for
All languages.'),
- );
-}
-
/**
* Implements hook_form_FORM_ID_alter().
*/
@@ -638,7 +624,7 @@ function locale($string = NULL, $context = NULL, $langcode = NULL) {
// perspective that is a really bad idea, so we have no user
// interface for this. Be careful when turning this option off!
if (variable_get('locale_cache_strings', 1) == 1) {
- if ($cache = cache_get('locale:' . $langcode, 'cache')) {
+ if ($cache = cache()->get('locale:' . $langcode)) {
$locale_t[$langcode] = $cache->data;
}
elseif (lock_acquire('locale_cache_' . $langcode)) {
@@ -649,7 +635,7 @@ function locale($string = NULL, $context = NULL, $langcode = NULL) {
foreach ($result as $data) {
$locale_t[$langcode][$data->context][$data->source] = (empty($data->translation) ? TRUE : $data->translation);
}
- cache_set('locale:' . $langcode, $locale_t[$langcode]);
+ cache()->set('locale:' . $langcode, $locale_t[$langcode]);
lock_release('locale_cache_' . $langcode);
}
}
@@ -677,7 +663,7 @@ function locale($string = NULL, $context = NULL, $langcode = NULL) {
->fields(array('version' => VERSION))
->condition('lid', $translation->lid)
->execute();
- cache_clear_all('locale:', 'cache', TRUE);
+ cache()->deletePrefix('locale:');
}
}
else {
@@ -692,7 +678,7 @@ function locale($string = NULL, $context = NULL, $langcode = NULL) {
->execute();
$locale_t[$langcode][$context][$string] = TRUE;
// Clear locale cache so this string can be added in a later request.
- cache_clear_all('locale:', 'cache', TRUE);
+ cache()->deletePrefix('locale:');
}
}
@@ -778,6 +764,51 @@ function locale_language_list($field = 'name', $all = FALSE) {
return $list;
}
+/**
+ * Delete a language.
+ *
+ * @param $langcode
+ * Language code of the language to be deleted.
+ * @return
+ * TRUE if language is successfully deleted. Otherwise FALSE.
+ */
+function locale_language_delete($langcode) {
+ $languages = language_list();
+ if (isset($languages[$langcode])) {
+ $language = $languages[$langcode];
+
+ module_invoke_all('locale_language_delete', $language);
+
+ // Remove translations first.
+ db_delete('locales_target')
+ ->condition('language', $language->language)
+ ->execute();
+
+ // Remove the language.
+ db_delete('languages')
+ ->condition('language', $language->language)
+ ->execute();
+
+ if ($language->enabled) {
+ variable_set('language_count', variable_get('language_count', 1) - 1);
+ }
+
+ drupal_static_reset('language_list');
+ _locale_invalidate_js($language->language);
+
+ // Changing the language settings impacts the interface:
+ cache('page')->flush();
+
+ // Clearing all locale cache from database
+ cache()->delete('locale:' . $language->language);
+
+ $variables = array('%locale' => $language->name);
+ watchdog('locale', 'The language %locale has been removed.', $variables);
+ return TRUE;
+ }
+ return FALSE;
+}
+
/**
* Implements hook_modules_installed().
*/
diff --git a/modules/locale/locale.pages.inc b/modules/locale/locale.pages.inc
index 45816b905611..3e6590efe59b 100644
--- a/modules/locale/locale.pages.inc
+++ b/modules/locale/locale.pages.inc
@@ -410,7 +410,7 @@ function locale_translate_edit_form_submit($form, &$form_state) {
// Clear locale cache.
_locale_invalidate_js();
- cache_clear_all('locale:', 'cache', TRUE);
+ cache()->deletePrefix('locale:');
$form_state['redirect'] = 'admin/config/regional/translate/translate';
return;
@@ -450,7 +450,7 @@ function locale_translate_delete_form_submit($form, &$form_state) {
->execute();
// Force JavaScript translation file recreation for all languages.
_locale_invalidate_js();
- cache_clear_all('locale:', 'cache', TRUE);
+ cache()->deletePrefix('locale:');
drupal_set_message(t('The string has been removed.'));
$form_state['redirect'] = 'admin/config/regional/translate/translate';
}
diff --git a/modules/locale/locale.test b/modules/locale/locale.test
index 6dad7e08bec4..90e313df7409 100644
--- a/modules/locale/locale.test
+++ b/modules/locale/locale.test
@@ -290,9 +290,8 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase {
$this->assertRaw(t('The language %locale has been removed.', array('%locale' => $name)), t('The test language has been removed.'));
// Reload to remove $name.
$this->drupalGet($path);
- $this->assertNoText($langcode, t('Language code not found.'));
- $this->assertNoText($name, t('Name not found.'));
- $this->assertNoText($native, t('Native not found.'));
+ // Verify that language is no longer found.
+ $this->assertResponse(404, t('Language no longer found.'));
$this->drupalLogout();
// Delete the string.
@@ -1900,8 +1899,8 @@ class LocaleUILanguageNegotiationTest extends DrupalWebTestCase {
$this->assertResponse(404, "Unknown language path prefix should return 404");
// Setup for domain negotiation, first configure the language to have domain
- // URL.
- $edit = array('prefix' => '', 'domain' => "http://$language_domain");
+ // URL. We use https and a port to make sure that only the domain name is used.
+ $edit = array('prefix' => '', 'domain' => "https://$language_domain:99");
$this->drupalPost("admin/config/regional/language/edit/$language", $edit, t('Save language'));
// Set the site to use domain language negotiation.
diff --git a/modules/menu/menu.admin.inc b/modules/menu/menu.admin.inc
index cb33cbb85664..91229b497418 100644
--- a/modules/menu/menu.admin.inc
+++ b/modules/menu/menu.admin.inc
@@ -286,6 +286,7 @@ function menu_edit_item($form, &$form_state, $type, $item, $menu) {
$form['link_path'] = array(
'#type' => 'textfield',
'#title' => t('Path'),
+ '#maxlength' => 255,
'#default_value' => $path,
'#description' => t('The path for this menu link. This can be an internal Drupal path such as %add-node or an external URL such as %drupal. Enter %front to link to the front page.', array('%front' => '
', '%add-node' => 'node/add', '%drupal' => 'http://drupal.org')),
'#required' => TRUE,
diff --git a/modules/menu/menu.module b/modules/menu/menu.module
index 254079700c9e..fa5837311268 100644
--- a/modules/menu/menu.module
+++ b/modules/menu/menu.module
@@ -230,12 +230,12 @@ function menu_load($menu_name) {
function menu_load_all() {
$custom_menus = &drupal_static(__FUNCTION__);
if (!isset($custom_menus)) {
- if ($cached = cache_get('menu_custom', 'cache_menu')) {
+ if ($cached = cache('menu')->get('menu_custom')) {
$custom_menus = $cached->data;
}
else {
$custom_menus = db_query('SELECT * FROM {menu_custom}')->fetchAllAssoc('menu_name', PDO::FETCH_ASSOC);
- cache_set('menu_custom', $custom_menus, 'cache_menu');
+ cache('menu')->set('menu_custom', $custom_menus);
}
}
return $custom_menus;
diff --git a/modules/node/node.admin.inc b/modules/node/node.admin.inc
index 43e0ecd2823a..a6abb0b70670 100644
--- a/modules/node/node.admin.inc
+++ b/modules/node/node.admin.inc
@@ -121,10 +121,6 @@ function node_build_filter_query(SelectQueryInterface $query) {
foreach ($filter_data as $index => $filter) {
list($key, $value) = $filter;
switch ($key) {
- case 'term':
- $alias = $query->join('taxonomy_index', 'ti', "n.nid = %alias.nid");
- $query->condition($alias . '.tid', $value);
- break;
case 'status':
// Note: no exploitable hole as $key/$value have already been checked when submitted
list($key, $value) = explode('-', $value, 2);
diff --git a/modules/node/node.info b/modules/node/node.info
index 7f2c7ff825cf..2e410ed7a4b7 100644
--- a/modules/node/node.info
+++ b/modules/node/node.info
@@ -6,5 +6,6 @@ core = 8.x
files[] = node.module
files[] = node.test
required = TRUE
+dependencies[] = entity
configure = admin/structure/types
stylesheets[all][] = node.css
diff --git a/modules/node/node.module b/modules/node/node.module
index 20815df85d3f..30e95591726f 100644
--- a/modules/node/node.module
+++ b/modules/node/node.module
@@ -675,7 +675,7 @@ function _node_types_build($rebuild = FALSE) {
if (isset($_node_types)) {
return $_node_types;
}
- if ($cache = cache_get($cid)) {
+ if ($cache = cache()->get($cid)) {
$_node_types = $cache->data;
return $_node_types;
}
@@ -737,7 +737,7 @@ function _node_types_build($rebuild = FALSE) {
asort($_node_types->names);
- cache_set($cid, $_node_types);
+ cache()->set($cid, $_node_types);
return $_node_types;
}
@@ -746,7 +746,7 @@ function _node_types_build($rebuild = FALSE) {
* Clears the node type cache.
*/
function node_type_cache_reset() {
- cache_clear_all('node_types:', 'cache', TRUE);
+ cache()->deletePrefix('node_types:');
drupal_static_reset('_node_types_build');
}
@@ -2452,7 +2452,7 @@ function node_feed($nids = FALSE, $channel = array()) {
$nids = db_select('node', 'n')
->fields('n', array('nid', 'created'))
->condition('n.promote', 1)
- ->condition('status', 1)
+ ->condition('n.status', 1)
->orderBy('n.created', 'DESC')
->range(0, variable_get('feed_default_items', 10))
->addTag('node_access')
@@ -2833,8 +2833,6 @@ function node_search_validate($form, &$form_state) {
* TRUE if the operation may be performed, FALSE otherwise.
*/
function node_access($op, $node, $account = NULL) {
- global $user;
-
$rights = &drupal_static(__FUNCTION__, array());
if (!$node || !in_array($op, array('view', 'update', 'delete', 'create'), TRUE)) {
@@ -2844,7 +2842,7 @@ function node_access($op, $node, $account = NULL) {
}
// If no user object is supplied, the access check is for the current user.
if (empty($account)) {
- $account = $user;
+ $account = $GLOBALS['user'];
}
// $node may be either an object or a node type. Since node types cannot be
@@ -3948,3 +3946,14 @@ function node_file_download_access($field, $entity_type, $entity) {
return node_access('view', $entity);
}
}
+
+/**
+ * Implements hook_locale_language_delete().
+ */
+function node_locale_language_delete($language) {
+ // On nodes with this language, unset the language
+ db_update('node')
+ ->fields(array('language' => ''))
+ ->condition('language', $language->language)
+ ->execute();
+}
diff --git a/modules/node/node.test b/modules/node/node.test
index d004684c5b63..5de608171205 100644
--- a/modules/node/node.test
+++ b/modules/node/node.test
@@ -2261,6 +2261,7 @@ class NodeTokenReplaceTestCase extends DrupalWebTestCase {
$tests['[node:language]'] = check_plain($node->language);
$tests['[node:url]'] = url('node/' . $node->nid, $url_options);
$tests['[node:edit-url]'] = url('node/' . $node->nid . '/edit', $url_options);
+ $tests['[node:author]'] = check_plain(format_username($account));
$tests['[node:author:uid]'] = $node->uid;
$tests['[node:author:name]'] = check_plain(format_username($account));
$tests['[node:created:since]'] = format_interval(REQUEST_TIME - $node->created, 2, $language->language);
diff --git a/modules/node/node.tokens.inc b/modules/node/node.tokens.inc
index 80dbda517a01..b6bafc639077 100644
--- a/modules/node/node.tokens.inc
+++ b/modules/node/node.tokens.inc
@@ -157,8 +157,9 @@ function node_tokens($type, $tokens, array $data = array(), array $options = arr
// Default values for the chained tokens handled below.
case 'author':
- $name = ($node->uid == 0) ? variable_get('anonymous', t('Anonymous')) : $node->name;
- $replacements[$original] = $sanitize ? filter_xss($name) : $name;
+ $account = user_load($node->uid);
+ $name = format_username($account);
+ $replacements[$original] = $sanitize ? check_plain($name) : $name;
break;
case 'created':
diff --git a/modules/openid/openid.api.php b/modules/openid/openid.api.php
index 11faa71efa89..5e3d15d94cbe 100644
--- a/modules/openid/openid.api.php
+++ b/modules/openid/openid.api.php
@@ -49,8 +49,13 @@ function hook_openid_response($response, $account) {
* Allow modules to declare OpenID discovery methods.
*
* The discovery function callbacks will be called in turn with an unique
- * parameter, the claimed identifier. They have to return an array of services,
- * in the same form returned by openid_discover().
+ * parameter, the claimed identifier. They have to return an associative array
+ * with array of services and claimed identifier in the same form as returned by
+ * openid_discover(). The resulting array must contain following keys:
+ * - 'services' (required) an array of discovered services (including OpenID
+ * version, endpoint URI, etc).
+ * - 'claimed_id' (optional) new claimed identifer, found by following HTTP
+ * redirects during the services discovery.
*
* The first discovery method that succeed (return at least one services) will
* stop the discovery process.
@@ -58,6 +63,7 @@ function hook_openid_response($response, $account) {
* @return
* An associative array which keys are the name of the discovery methods and
* values are function callbacks.
+ *
* @see hook_openid_discovery_method_info_alter()
*/
function hook_openid_discovery_method_info() {
diff --git a/modules/openid/openid.js b/modules/openid/openid.js
index 1f2042746892..fdc97fa06be3 100644
--- a/modules/openid/openid.js
+++ b/modules/openid/openid.js
@@ -11,10 +11,10 @@ Drupal.behaviors.openid = {
if (cookie) {
$('#edit-openid-identifier').val(cookie);
}
- if ($('#edit-openid-identifier').val()) {
+ if ($('#edit-openid-identifier').val() || location.hash == '#openid-login') {
$('#edit-openid-identifier').addClass('openid-processed');
loginElements.hide();
- // Use .css('display', 'block') instead of .show() to Konqueror friendly.
+ // Use .css('display', 'block') instead of .show() to be Konqueror friendly.
openidElements.css('display', 'block');
}
}
diff --git a/modules/openid/openid.module b/modules/openid/openid.module
index 6d4b1d7ff78f..2bf891ab69ac 100644
--- a/modules/openid/openid.module
+++ b/modules/openid/openid.module
@@ -146,11 +146,11 @@ function _openid_user_login_form_alter(&$form, &$form_state) {
$items = array();
$items[] = array(
- 'data' => l(t('Log in using OpenID'), '#'),
+ 'data' => l(t('Log in using OpenID'), '#openid-login', array('external' => TRUE)),
'class' => array('openid-link'),
);
$items[] = array(
- 'data' => l(t('Cancel OpenID login'), '#'),
+ 'data' => l(t('Cancel OpenID login'), '#', array('external' => TRUE)),
'class' => array('user-link'),
);
@@ -256,16 +256,25 @@ function openid_login_validate($form, &$form_state) {
function openid_begin($claimed_id, $return_to = '', $form_values = array()) {
module_load_include('inc', 'openid');
+ $service = NULL;
$claimed_id = openid_normalize($claimed_id);
+ $discovery = openid_discovery($claimed_id);
- $services = openid_discovery($claimed_id);
- $service = _openid_select_service($services);
+ if (!empty($discovery['services'])) {
+ $service = _openid_select_service($discovery['services']);
+ }
- if (!$service) {
+ // Quit if the discovery result was empty or if we can't select any service.
+ if (!$discovery || !$service) {
form_set_error('openid_identifier', t('Sorry, that is not a valid OpenID. Ensure you have spelled your ID correctly.'));
return;
}
+ // Set claimed id from discovery.
+ if (!empty($discovery['claimed_id'])) {
+ $claimed_id = $discovery['claimed_id'];
+ }
+
// Store discovered information in the users' session so we don't have to rediscover.
$_SESSION['openid']['service'] = $service;
// Store the claimed id
@@ -352,11 +361,13 @@ function openid_complete($response = array()) {
// identififer to make sure that the provider is authorized to
// respond on behalf of this.
if ($response_claimed_id != $claimed_id) {
- $services = openid_discovery($response_claimed_id);
- $uris = array();
- foreach ($services as $discovered_service) {
- if (in_array('http://specs.openid.net/auth/2.0/server', $discovered_service['types']) || in_array('http://specs.openid.net/auth/2.0/signon', $discovered_service['types'])) {
- $uris[] = $discovered_service['uri'];
+ $discovery = openid_discovery($response['openid.claimed_id']);
+ if ($discovery && !empty($discovery['services'])) {
+ $uris = array();
+ foreach ($discovery['services'] as $discovered_service) {
+ if (in_array('http://specs.openid.net/auth/2.0/server', $discovered_service['types']) || in_array('http://specs.openid.net/auth/2.0/signon', $discovered_service['types'])) {
+ $uris[] = $discovered_service['uri'];
+ }
}
}
if (!in_array($service['uri'], $uris)) {
@@ -378,10 +389,21 @@ function openid_complete($response = array()) {
/**
* Perform discovery on a claimed ID to determine the OpenID provider endpoint.
*
- * @param $claimed_id The OpenID URL to perform discovery on.
+ * Discovery methods are provided by the hook_openid_discovery_method_info and
+ * could be further altered using the hook_openid_discovery_method_info_alter.
*
- * @return Array of services discovered (including OpenID version, endpoint
- * URI, etc).
+ * @param $claimed_id
+ * The OpenID URL to perform discovery on.
+ *
+ * @return
+ * The resulting discovery array from the first successful discovery method,
+ * which must contain following keys:
+ * - 'services' (required) an array of discovered services (including OpenID
+ * version, endpoint URI, etc).
+ * - 'claimed_id' (optional) new claimed identifer, found by following HTTP
+ * redirects during the services discovery.
+ * If all the discovery method fails or if no appropriate discovery method is
+ * found, FALSE is returned.
*/
function openid_discovery($claimed_id) {
module_load_include('inc', 'openid');
@@ -389,15 +411,15 @@ function openid_discovery($claimed_id) {
$methods = module_invoke_all('openid_discovery_method_info');
drupal_alter('openid_discovery_method_info', $methods);
- // Execute each method in turn.
+ // Execute each method in turn and return first successful discovery.
foreach ($methods as $method) {
- $discovered_services = $method($claimed_id);
- if (!empty($discovered_services)) {
- return $discovered_services;
+ $discovery = $method($claimed_id);
+ if (!empty($discovery)) {
+ return $discovery;
}
}
- return array();
+ return FALSE;
}
/**
@@ -421,24 +443,33 @@ function openid_openid_discovery_method_info() {
*
* @see http://openid.net/specs/openid-authentication-2_0.html#discovery
* @see hook_openid_discovery_method_info()
+ * @see openid_discovery()
+ *
+ * @return
+ * An array of discovered services and claimed identifier or NULL. See
+ * openid_discovery() for more specific information.
*/
function _openid_xri_discovery($claimed_id) {
if (_openid_is_xri($claimed_id)) {
// Resolve XRI using a proxy resolver (Extensible Resource Identifier (XRI)
// Resolution Version 2.0, section 11.2 and 14.3).
$xrds_url = variable_get('xri_proxy_resolver', 'http://xri.net/') . rawurlencode($claimed_id) . '?_xrd_r=application/xrds+xml';
- $services = _openid_xrds_discovery($xrds_url);
- foreach ($services as $i => &$service) {
- $status = $service['xrd']->children(OPENID_NS_XRD)->Status;
- if ($status && $status->attributes()->cid == 'verified') {
- $service['claimed_id'] = openid_normalize((string)$service['xrd']->children(OPENID_NS_XRD)->CanonicalID);
+ $discovery = _openid_xrds_discovery($xrds_url);
+ if (!empty($discovery['services']) && is_array($discovery['services'])) {
+ foreach ($discovery['services'] as $i => &$service) {
+ $status = $service['xrd']->children(OPENID_NS_XRD)->Status;
+ if ($status && $status->attributes()->cid == 'verified') {
+ $service['claimed_id'] = openid_normalize((string)$service['xrd']->children(OPENID_NS_XRD)->CanonicalID);
+ }
+ else {
+ // Ignore service if the Canonical ID could not be verified.
+ unset($discovery['services'][$i]);
+ }
}
- else {
- // Ignore service if CanonicalID could not be verified.
- unset($services[$i]);
+ if (!empty($discovery['services'])) {
+ return $discovery;
}
}
- return $services;
}
}
@@ -447,6 +478,11 @@ function _openid_xri_discovery($claimed_id) {
*
* @see http://openid.net/specs/openid-authentication-2_0.html#discovery
* @see hook_openid_discovery_method_info()
+ * @see openid_discovery()
+ *
+ * @return
+ * An array of discovered services and claimed identifier or NULL. See
+ * openid_discovery() for more specific information.
*/
function _openid_xrds_discovery($claimed_id) {
$services = array();
@@ -458,7 +494,18 @@ function _openid_xrds_discovery($claimed_id) {
$headers = array('Accept' => 'application/xrds+xml');
$result = drupal_http_request($xrds_url, array('headers' => $headers));
- if (!isset($result->error)) {
+ // Check for HTTP error and make sure, that we reach the target. If the
+ // maximum allowed redirects are exhausted, final destination URL isn't
+ // reached, but drupal_http_request() doesn't return any error.
+ // @todo Remove the check for 200 HTTP result code after the following issue
+ // will be fixed: http://drupal.org/node/1096890.
+ if (!isset($result->error) && $result->code == 200) {
+
+ // Replace the user-entered claimed_id if we received a redirect.
+ if (!empty($result->redirect_url)) {
+ $claimed_id = openid_normalize($result->redirect_url);
+ }
+
if (isset($result->headers['content-type']) && preg_match("/application\/xrds\+xml/", $result->headers['content-type'])) {
// Parse XML document to find URL
$services = _openid_xrds_parse($result->data);
@@ -504,7 +551,13 @@ function _openid_xrds_discovery($claimed_id) {
}
}
}
- return $services;
+
+ if (!empty($services)) {
+ return array(
+ 'services' => $services,
+ 'claimed_id' => $claimed_id,
+ );
+ }
}
/**
diff --git a/modules/openid/openid.test b/modules/openid/openid.test
index 09632ba1417c..6e2528e66af2 100644
--- a/modules/openid/openid.test
+++ b/modules/openid/openid.test
@@ -124,6 +124,28 @@ class OpenIDFunctionalTestCase extends OpenIDWebTestCase {
// OpenID Authentication 2.0, section 7.3.3:
$this->addIdentity(url('openid-test/html/openid2', array('absolute' => TRUE)), 2, 'http://example.com/html-openid2');
+
+ // OpenID Authentication 2.0, section 7.2.4:
+ // URL Identifiers MUST then be further normalized by both (1) following
+ // redirects when retrieving their content and finally (2) applying the
+ // rules in Section 6 of RFC3986 to the final destination URL. This final
+ // URL MUST be noted by the Relying Party as the Claimed Identifier and be
+ // used when requesting authentication.
+
+ // Single redirect.
+ $identity = $expected_claimed_id = url('openid-test/redirected/yadis/xrds/1', array('absolute' => TRUE));
+ $this->addRedirectedIdentity($identity, 2, 'http://example.com/xrds', $expected_claimed_id, 0);
+
+ // Exact 3 redirects (default value for the 'max_redirects' option in
+ // drupal_http_request()).
+ $identity = $expected_claimed_id = url('openid-test/redirected/yadis/xrds/2', array('absolute' => TRUE));
+ $this->addRedirectedIdentity($identity, 2, 'http://example.com/xrds', $expected_claimed_id, 2);
+
+ // Fails because there are more than 3 redirects (default value for the
+ // 'max_redirects' option in drupal_http_request()).
+ $identity = url('openid-test/redirected/yadis/xrds/3', array('absolute' => TRUE));
+ $expected_claimed_id = FALSE;
+ $this->addRedirectedIdentity($identity, 2, 'http://example.com/xrds', $expected_claimed_id, 3);
}
/**
@@ -279,6 +301,41 @@ class OpenIDFunctionalTestCase extends OpenIDWebTestCase {
$this->assertRaw(t('Successfully added %identity', array('%identity' => $claimed_id)), t('Identity %identity was added.', array('%identity' => $identity)));
}
+ /**
+ * Add OpenID identity, changed by the following redirects, to user's profile.
+ *
+ * According to OpenID Authentication 2.0, section 7.2.4, URL Identifiers MUST
+ * be further normalized by following redirects when retrieving their content
+ * and this final URL MUST be noted by the Relying Party as the Claimed
+ * Identifier and be used when requesting authentication.
+ *
+ * @param $identity
+ * The User-supplied Identifier.
+ * @param $version
+ * The protocol version used by the service.
+ * @param $local_id
+ * The expected OP-Local Identifier found during discovery.
+ * @param $claimed_id
+ * The expected Claimed Identifier returned by the OpenID Provider, or FALSE
+ * if the discovery is expected to fail.
+ * @param $redirects
+ * The number of redirects.
+ */
+ function addRedirectedIdentity($identity, $version = 2, $local_id = 'http://example.com/xrds', $claimed_id = NULL, $redirects = 0) {
+ // Set the final destination URL which is the same as the Claimed
+ // Identifier, we insert the same identifier also to the provider response,
+ // but provider could futher change the Claimed ID actually (e.g. it could
+ // add unique fragment).
+ variable_set('openid_test_redirect_url', $identity);
+ variable_set('openid_test_response', array('openid.claimed_id' => $identity));
+
+ $this->addIdentity(url('openid-test/redirect/' . $redirects, array('absolute' => TRUE)), $version, $local_id, $claimed_id);
+
+ // Clean up.
+ variable_del('openid_test_redirect_url');
+ variable_del('openid_test_response');
+ }
+
/**
* Tests that openid.signed is verified.
*/
diff --git a/modules/openid/tests/openid_test.module b/modules/openid/tests/openid_test.module
index bad1184a311a..629dcd3356a5 100644
--- a/modules/openid/tests/openid_test.module
+++ b/modules/openid/tests/openid_test.module
@@ -60,6 +60,19 @@ function openid_test_menu() {
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
+ $items['openid-test/redirect'] = array(
+ 'title' => 'OpenID Provider Redirection Point',
+ 'page callback' => 'openid_test_redirect',
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ );
+ $items['openid-test/redirected/%/%'] = array(
+ 'title' => 'OpenID Provider Final URL',
+ 'page callback' => 'openid_test_redirected_method',
+ 'page arguments' => array(2, 3),
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ );
return $items;
}
@@ -212,6 +225,28 @@ function openid_test_endpoint() {
}
}
+/**
+ * Menu callback; redirect during Normalization/Discovery.
+ */
+function openid_test_redirect($count = 0) {
+ if ($count == 0) {
+ $url = variable_get('openid_test_redirect_url', '');
+ }
+ else {
+ $url = url('openid-test/redirect/' . --$count, array('absolute' => TRUE));
+ }
+ $http_response_code = variable_get('openid_test_redirect_http_reponse_code', 301);
+ header('Location: ' . $url, TRUE, $http_response_code);
+ exit();
+}
+
+/**
+ * Menu callback; respond with appropriate callback.
+ */
+function openid_test_redirected_method($method1, $method2) {
+ return call_user_func('openid_test_' . $method1 . '_' . $method2);
+}
+
/**
* OpenID endpoint; handle "associate" requests (see OpenID Authentication 2.0,
* section 8).
diff --git a/modules/overlay/overlay-child.css b/modules/overlay/overlay-child.css
index d31952e594d5..d047535415d7 100644
--- a/modules/overlay/overlay-child.css
+++ b/modules/overlay/overlay-child.css
@@ -17,6 +17,7 @@ html.js body {
min-width: 700px;
position: relative;
padding: .2em;
+ padding-bottom: 2em;
padding-right: 26px; /* LTR */
width: 88%;
}
@@ -133,15 +134,6 @@ html.js body {
padding-top: 0.9em;
}
-/**
- * IE6 shows elements with position:fixed as position:static so replace
- * it with position:absolute;
- */
-* html #overlay-close,
-* html #overlay-close:hover {
- position: absolute;
-}
-
/**
* Disable message.
*/
diff --git a/modules/overlay/overlay-parent.js b/modules/overlay/overlay-parent.js
index f9789f14aff1..135320fe603f 100644
--- a/modules/overlay/overlay-parent.js
+++ b/modules/overlay/overlay-parent.js
@@ -429,7 +429,7 @@ Drupal.overlay.eventhandlerAlterDisplacedElements = function (event) {
var documentHeight = this.iframeWindow.document.body.clientHeight;
var documentWidth = this.iframeWindow.document.body.clientWidth;
// IE6 doesn't support maxWidth, use width instead.
- var maxWidthName = (typeof document.body.style.maxWidth == 'string') ? 'maxWidth' : 'width';
+ var maxWidthName = 'maxWidth';
if (Drupal.overlay.leftSidedScrollbarOffset === undefined && $(document.documentElement).attr('dir') === 'rtl') {
// We can't use element.clientLeft to detect whether scrollbars are placed
diff --git a/modules/path/path.admin.inc b/modules/path/path.admin.inc
index f10142b56393..c8a69639aa68 100644
--- a/modules/path/path.admin.inc
+++ b/modules/path/path.admin.inc
@@ -130,11 +130,23 @@ function path_admin_form($form, &$form_state, $path = array('source' => '', 'ali
'#required' => TRUE,
);
- // This will be a hidden value unless locale module is enabled.
- $form['language'] = array(
- '#type' => 'value',
- '#value' => $path['language']
- );
+ // A hidden value unless locale module is enabled.
+ if (module_exists('locale')) {
+ $form['language'] = array(
+ '#type' => 'select',
+ '#title' => t('Language'),
+ '#options' => array(LANGUAGE_NONE => t('All languages')) + locale_language_list('name'),
+ '#default_value' => $path['language'],
+ '#weight' => -10,
+ '#description' => t('A path alias set for a specific language will always be used when displaying this page in that language, and takes precedence over path aliases set for All languages.'),
+ );
+ }
+ else {
+ $form['language'] = array(
+ '#type' => 'value',
+ '#value' => $path['language']
+ );
+ }
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array(
diff --git a/modules/path/path.test b/modules/path/path.test
index f42ec81be08c..241f5325dc2e 100644
--- a/modules/path/path.test
+++ b/modules/path/path.test
@@ -37,14 +37,14 @@ class PathTestCase extends DrupalWebTestCase {
// Visit the system path for the node and confirm a cache entry is
// created.
- cache_clear_all('*', 'cache_path', TRUE);
+ cache('path')->flush();
$this->drupalGet($edit['source']);
- $this->assertTrue(cache_get($edit['source'], 'cache_path'), t('Cache entry was created.'));
+ $this->assertTrue(cache('path')->get($edit['source']), t('Cache entry was created.'));
// Visit the alias for the node and confirm a cache entry is created.
- cache_clear_all('*', 'cache_path', TRUE);
+ cache('path')->flush();
$this->drupalGet($edit['alias']);
- $this->assertTrue(cache_get($edit['source'], 'cache_path'), t('Cache entry was created.'));
+ $this->assertTrue(cache('path')->get($edit['source']), t('Cache entry was created.'));
}
/**
@@ -477,7 +477,7 @@ class PathMonolingualTestCase extends DrupalWebTestCase {
// Verify that French is the only language.
$this->assertFalse(drupal_multilingual(), t('Site is mono-lingual'));
- $this->assertEqual(language_default('language'), 'fr', t('French is the default language'));
+ $this->assertEqual(language_default()->language, 'fr', t('French is the default language'));
// Set language detection to URL.
$edit = array('language[enabled][locale-url]' => TRUE);
diff --git a/modules/search/search-block-form.tpl.php b/modules/search/search-block-form.tpl.php
index 78447463c6f9..da58403c2c47 100644
--- a/modules/search/search-block-form.tpl.php
+++ b/modules/search/search-block-form.tpl.php
@@ -30,7 +30,7 @@
*/
?>
- subject)) : ?>
+ subject)): ?>
diff --git a/modules/search/search-result.tpl.php b/modules/search/search-result.tpl.php
index db9f2202fc23..949452ac34cb 100644
--- a/modules/search/search-result.tpl.php
+++ b/modules/search/search-result.tpl.php
@@ -45,7 +45,7 @@
* for its existence before printing. The default keys of 'type', 'user' and
* 'date' always exist for node searches. Modules may provide other data.
* @code
- *
+ *
*
@@ -69,10 +69,10 @@
diff --git a/modules/search/search-results.tpl.php b/modules/search/search-results.tpl.php
index 4de724bec196..e35be1edcfaa 100644
--- a/modules/search/search-results.tpl.php
+++ b/modules/search/search-results.tpl.php
@@ -21,7 +21,7 @@
* @see template_preprocess_search_results()
*/
?>
-
+
diff --git a/modules/shortcut/shortcut.admin.inc b/modules/shortcut/shortcut.admin.inc
index 9735d376079a..4c8bf53afedf 100644
--- a/modules/shortcut/shortcut.admin.inc
+++ b/modules/shortcut/shortcut.admin.inc
@@ -264,6 +264,7 @@ function shortcut_set_add_form_submit($form, &$form_state) {
* @see shortcut_set_customize_submit()
*/
function shortcut_set_customize($form, &$form_state, $shortcut_set) {
+ $form['#shortcut_set_name'] = $shortcut_set->set_name;
$form['shortcuts'] = array(
'#tree' => TRUE,
'#weight' => -20,
@@ -299,7 +300,10 @@ function shortcut_set_customize($form, &$form_state, $shortcut_set) {
'js' => array(drupal_get_path('module', 'shortcut') . '/shortcut.admin.js'),
);
- $form['actions'] = array('#type' => 'actions');
+ $form['actions'] = array(
+ '#type' => 'actions',
+ '#access' => !empty($shortcut_set->links),
+ );
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Save changes'),
@@ -336,9 +340,15 @@ function shortcut_set_customize_submit($form, &$form_state) {
function theme_shortcut_set_customize($variables) {
$form = $variables['form'];
$map = array('disabled' => t('Disabled'), 'enabled' => t('Enabled'));
+ $shortcuts_by_status = array(
+ 'enabled' => element_children($form['shortcuts']['enabled']),
+ 'disabled' => element_children($form['shortcuts']['disabled']),
+ );
+ // Do not add any rows to the table if there are no shortcuts to display.
+ $statuses = empty($shortcuts_by_status['enabled']) && empty($shortcuts_by_status['disabled']) ? array() : array_keys($shortcuts_by_status);
$rows = array();
- foreach (array('enabled', 'disabled') as $status) {
+ foreach ($statuses as $status) {
drupal_add_tabledrag('shortcuts', 'match', 'sibling', 'shortcut-status-select');
drupal_add_tabledrag('shortcuts', 'order', 'sibling', 'shortcut-weight');
$rows[] = array(
@@ -349,7 +359,7 @@ function theme_shortcut_set_customize($variables) {
'class' => array('shortcut-status', 'shortcut-status-' . $status),
);
- foreach (element_children($form['shortcuts'][$status]) as $key) {
+ foreach ($shortcuts_by_status[$status] as $key) {
$shortcut = &$form['shortcuts'][$status][$key];
$row = array();
$row[] = drupal_render($shortcut['name']);
@@ -373,7 +383,7 @@ function theme_shortcut_set_customize($variables) {
'class' => array('shortcut-slot-empty'),
);
}
- $count_shortcuts = count(element_children($form['shortcuts'][$status]));
+ $count_shortcuts = count($shortcuts_by_status[$status]);
if (!empty($count_shortcuts)) {
for ($i = 0; $i < min($count_shortcuts, shortcut_max_slots()); $i++) {
$rows['empty-' . $i]['class'][] = 'shortcut-slot-hidden';
@@ -383,7 +393,7 @@ function theme_shortcut_set_customize($variables) {
}
$header = array(t('Name'), t('Weight'), t('Status'), array('data' => t('Operations'), 'colspan' => 2));
- $output = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'shortcuts')));
+ $output = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'shortcuts'), 'empty' => t('No shortcuts available. Add a shortcut.', array('@link' => url('admin/config/user-interface/shortcut/' . $form['#shortcut_set_name'] . '/add-link')))));
$output .= drupal_render($form['actions']);
$output = drupal_render_children($form) . $output;
return $output;
diff --git a/modules/simpletest/drupal_web_test_case.php b/modules/simpletest/drupal_web_test_case.php
index 5c39cfc11cd5..3ce06d55fd6f 100644
--- a/modules/simpletest/drupal_web_test_case.php
+++ b/modules/simpletest/drupal_web_test_case.php
@@ -1238,6 +1238,10 @@ class DrupalWebTestCase extends DrupalTestCase {
->condition('test_id', $this->testId)
->execute();
+ // Reset all statics and variables to perform tests in a clean environment.
+ $conf = array();
+ drupal_static_reset();
+
// Clone the current connection and replace the current prefix.
$connection_info = Database::getConnectionInfo('default');
Database::renameConnection('default', 'simpletest_original_default');
@@ -1285,10 +1289,6 @@ class DrupalWebTestCase extends DrupalTestCase {
ini_set('log_errors', 1);
ini_set('error_log', $public_files_directory . '/error.log');
- // Reset all statics and variables to perform tests in a clean environment.
- $conf = array();
- drupal_static_reset();
-
// Set the test information for use in other parts of Drupal.
$test_info = &$GLOBALS['drupal_test_info'];
$test_info['test_run_id'] = $this->databasePrefix;
@@ -1435,7 +1435,7 @@ class DrupalWebTestCase extends DrupalTestCase {
*/
protected function refreshVariables() {
global $conf;
- cache_clear_all('variables', 'cache_bootstrap');
+ cache('bootstrap')->delete('variables');
$conf = variable_initialize();
}
diff --git a/modules/simpletest/simpletest.info b/modules/simpletest/simpletest.info
index 54b020d5c573..ba817daa5c2b 100644
--- a/modules/simpletest/simpletest.info
+++ b/modules/simpletest/simpletest.info
@@ -37,3 +37,6 @@ files[] = tests/theme.test
files[] = tests/unicode.test
files[] = tests/update.test
files[] = tests/xmlrpc.test
+files[] = tests/upgrade/upgrade.test
+files[] = tests/upgrade/upgrade_bare.test
+files[] = tests/upgrade/upgrade_filled.test
diff --git a/modules/simpletest/simpletest.module b/modules/simpletest/simpletest.module
index ede9ac6ec760..80a6bc531838 100644
--- a/modules/simpletest/simpletest.module
+++ b/modules/simpletest/simpletest.module
@@ -311,7 +311,7 @@ function simpletest_test_get_all() {
if (!$groups) {
// Load test information from cache if available, otherwise retrieve the
// information from each tests getInfo() method.
- if ($cache = cache_get('simpletest', 'cache')) {
+ if ($cache = cache()->get('simpletest')) {
$groups = $cache->data;
}
else {
@@ -347,7 +347,7 @@ function simpletest_test_get_all() {
// Allow modules extending core tests to disable originals.
drupal_alter('simpletest', $groups);
- cache_set('simpletest', $groups);
+ cache()->set('simpletest', $groups);
}
}
return $groups;
@@ -421,7 +421,7 @@ function simpletest_clean_environment() {
// Detect test classes that have been added, renamed or deleted.
registry_rebuild();
- cache_clear_all('simpletest', 'cache');
+ cache()->delete('simpletest');
}
/**
diff --git a/modules/simpletest/simpletest.pages.inc b/modules/simpletest/simpletest.pages.inc
index a39e8b792b49..d2d4a91bc6c7 100644
--- a/modules/simpletest/simpletest.pages.inc
+++ b/modules/simpletest/simpletest.pages.inc
@@ -428,6 +428,9 @@ function simpletest_result_status_image($status) {
/**
* Provides settings form for SimpleTest variables.
+ *
+ * @ingroup forms
+ * @see simpletest_settings_form_validate()
*/
function simpletest_settings_form($form, &$form_state) {
$form['general'] = array(
@@ -467,16 +470,41 @@ function simpletest_settings_form($form, &$form_state) {
),
'#default_value' => variable_get('simpletest_httpauth_method', CURLAUTH_BASIC),
);
+ $username = variable_get('simpletest_httpauth_username');
+ $password = variable_get('simpletest_httpauth_password');
$form['httpauth']['simpletest_httpauth_username'] = array(
'#type' => 'textfield',
'#title' => t('Username'),
- '#default_value' => variable_get('simpletest_httpauth_username', ''),
+ '#default_value' => $username,
);
+ if ($username && $password) {
+ $form['httpauth']['simpletest_httpauth_username']['#description'] = t('Leave this blank to delete both the existing username and password.');
+ }
$form['httpauth']['simpletest_httpauth_password'] = array(
- '#type' => 'textfield',
+ '#type' => 'password',
'#title' => t('Password'),
- '#default_value' => variable_get('simpletest_httpauth_password', ''),
);
+ if ($password) {
+ $form['httpauth']['simpletest_httpauth_password']['#description'] = t('To change the password, enter the new password here.');
+ }
return system_settings_form($form);
}
+
+/**
+ * Validation handler for simpletest_settings_form().
+ */
+function simpletest_settings_form_validate($form, &$form_state) {
+ // If a username was provided but a password wasn't, preserve the existing
+ // password.
+ if (!empty($form_state['values']['simpletest_httpauth_username']) && empty($form_state['values']['simpletest_httpauth_password'])) {
+ $form_state['values']['simpletest_httpauth_password'] = variable_get('simpletest_httpauth_password', '');
+ }
+
+ // If a password was provided but a username wasn't, the credentials are
+ // incorrect, so throw an error.
+ if (empty($form_state['values']['simpletest_httpauth_username']) && !empty($form_state['values']['simpletest_httpauth_password'])) {
+ form_set_error('simpletest_httpauth_username', t('HTTP authentication credentials must include a username in addition to a password.'));
+ }
+}
+
diff --git a/modules/simpletest/tests/bootstrap.test b/modules/simpletest/tests/bootstrap.test
index 14c16a95b6d0..d1e96623aae5 100644
--- a/modules/simpletest/tests/bootstrap.test
+++ b/modules/simpletest/tests/bootstrap.test
@@ -306,7 +306,7 @@ class HookBootExitTestCase extends DrupalWebTestCase {
// Boot and exit should not fire since the page is cached.
variable_set('page_cache_invoke_hooks', FALSE);
- $this->assertTrue(cache_get(url('', array('absolute' => TRUE)), 'cache_page'), t('Page has been cached.'));
+ $this->assertTrue(cache('page')->get(url('', array('absolute' => TRUE))), t('Page has been cached.'));
$this->drupalGet('');
$this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_boot'))->fetchField(), $calls, t('hook_boot not called with agressive cache and a cached page.'));
$this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_exit'))->fetchField(), $calls, t('hook_exit not called with agressive cache and a cached page.'));
diff --git a/modules/simpletest/tests/cache.test b/modules/simpletest/tests/cache.test
index 954f575961dc..1f3240d50971 100644
--- a/modules/simpletest/tests/cache.test
+++ b/modules/simpletest/tests/cache.test
@@ -1,7 +1,7 @@
default_bin;
}
- $cache = cache_get($cid, $bin);
+ $cached = cache($bin)->get($cid);
- return isset($cache->data) && $cache->data == $var;
+ return isset($cached->data) && $cached->data == $var;
}
/**
@@ -71,8 +71,8 @@ class CacheTestCase extends DrupalWebTestCase {
$cid = $this->default_cid;
}
- $cache = cache_get($cid, $bin);
- $this->assertFalse($cache, $message);
+ $cached = cache($bin)->get($cid);
+ $this->assertFalse($cached, $message);
}
/**
@@ -85,7 +85,7 @@ class CacheTestCase extends DrupalWebTestCase {
$bin = $this->default_bin;
}
- cache_clear_all(NULL, $bin);
+ cache($bin)->expire();
}
/**
@@ -146,23 +146,24 @@ class CacheSavingCase extends CacheTestCase {
$test_object->test2 = 100;
$test_object->test3 = array('drupal1', 'drupal2' => 'drupal3', 'drupal4' => array('drupal5', 'drupal6'));
- cache_set('test_object', $test_object, 'cache');
- $cache = cache_get('test_object', 'cache');
- $this->assertTrue(isset($cache->data) && $cache->data == $test_object, t('Object is saved and restored properly.'));
+
+ cache()->set('test_object', $test_object);
+ $cached = cache()->get('test_object');
+ $this->assertTrue(isset($cached->data) && $cached->data == $test_object, t('Object is saved and restored properly.'));
}
/*
* Check or a variable is stored and restored properly.
**/
function checkVariable($var) {
- cache_set('test_var', $var, 'cache');
- $cache = cache_get('test_var', 'cache');
- $this->assertTrue(isset($cache->data) && $cache->data === $var, t('@type is saved and restored properly.', array('@type' => ucfirst(gettype($var)))));
+ cache()->set('test_var', $var);
+ $cached = cache()->get('test_var');
+ $this->assertTrue(isset($cached->data) && $cached->data === $var, t('@type is saved and restored properly.', array('@type' => ucfirst(gettype($var)))));
}
}
/**
- * Test cache_get_multiple().
+ * Test getMultiple().
*/
class CacheGetMultipleUnitTest extends CacheTestCase {
@@ -175,33 +176,34 @@ class CacheGetMultipleUnitTest extends CacheTestCase {
}
function setUp() {
- $this->default_bin = 'cache_page';
+ $this->default_bin = 'page';
parent::setUp();
}
/**
- * Test cache_get_multiple().
+ * Test getMultiple().
*/
function testCacheMultiple() {
$item1 = $this->randomName(10);
$item2 = $this->randomName(10);
- cache_set('item1', $item1, $this->default_bin);
- cache_set('item2', $item2, $this->default_bin);
+ $cache = cache($this->default_bin);
+ $cache->set('item1', $item1);
+ $cache->set('item2', $item2);
$this->assertTrue($this->checkCacheExists('item1', $item1), t('Item 1 is cached.'));
$this->assertTrue($this->checkCacheExists('item2', $item2), t('Item 2 is cached.'));
- // Fetch both records from the database with cache_get_multiple().
+ // Fetch both records from the database with getMultiple().
$item_ids = array('item1', 'item2');
- $items = cache_get_multiple($item_ids, $this->default_bin);
+ $items = $cache->getMultiple($item_ids);
$this->assertEqual($items['item1']->data, $item1, t('Item was returned from cache successfully.'));
$this->assertEqual($items['item2']->data, $item2, t('Item was returned from cache successfully.'));
// Remove one item from the cache.
- cache_clear_all('item2', $this->default_bin);
+ $cache->delete('item2');
- // Confirm that only one item is returned by cache_get_multiple().
+ // Confirm that only one item is returned by getMultiple().
$item_ids = array('item1', 'item2');
- $items = cache_get_multiple($item_ids, $this->default_bin);
+ $items = $cache->getMultiple($item_ids);
$this->assertEqual($items['item1']->data, $item1, t('Item was returned from cache successfully.'));
$this->assertFalse(isset($items['item2']), t('Item was not returned from the cache.'));
$this->assertTrue(count($items) == 1, t('Only valid cache entries returned.'));
@@ -221,7 +223,7 @@ class CacheClearCase extends CacheTestCase {
}
function setUp() {
- $this->default_bin = 'cache_page';
+ $this->default_bin = 'page';
$this->default_value = $this->randomName(10);
parent::setUp();
@@ -231,44 +233,36 @@ class CacheClearCase extends CacheTestCase {
* Test clearing using a cid.
*/
function testClearCid() {
- cache_set('test_cid_clear', $this->default_value, $this->default_bin);
+ $cache = cache($this->default_bin);
+ $cache->set('test_cid_clear', $this->default_value);
$this->assertCacheExists(t('Cache was set for clearing cid.'), $this->default_value, 'test_cid_clear');
- cache_clear_all('test_cid_clear', $this->default_bin);
+ $cache->delete('test_cid_clear');
$this->assertCacheRemoved(t('Cache was removed after clearing cid.'), 'test_cid_clear');
-
- cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
- cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
- $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
- && $this->checkCacheExists('test_cid_clear2', $this->default_value),
- t('Two caches were created for checking cid "*" with wildcard false.'));
- cache_clear_all('*', $this->default_bin);
- $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
- && $this->checkCacheExists('test_cid_clear2', $this->default_value),
- t('Two caches still exists after clearing cid "*" with wildcard false.'));
}
/**
* Test clearing using wildcard.
*/
function testClearWildcard() {
- cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
- cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
+ $cache = cache($this->default_bin);
+ $cache->set('test_cid_clear1', $this->default_value);
+ $cache->set('test_cid_clear2', $this->default_value);
$this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
&& $this->checkCacheExists('test_cid_clear2', $this->default_value),
t('Two caches were created for checking cid "*" with wildcard true.'));
- cache_clear_all('*', $this->default_bin, TRUE);
+ $cache->flush();
$this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
|| $this->checkCacheExists('test_cid_clear2', $this->default_value),
t('Two caches removed after clearing cid "*" with wildcard true.'));
- cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
- cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
+ $cache->set('test_cid_clear1', $this->default_value);
+ $cache->set('test_cid_clear2', $this->default_value);
$this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
&& $this->checkCacheExists('test_cid_clear2', $this->default_value),
t('Two caches were created for checking cid substring with wildcard true.'));
- cache_clear_all('test_', $this->default_bin, TRUE);
+ $cache->deletePrefix('test_');
$this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
|| $this->checkCacheExists('test_cid_clear2', $this->default_value),
t('Two caches removed after clearing cid substring with wildcard true.'));
@@ -279,16 +273,17 @@ class CacheClearCase extends CacheTestCase {
*/
function testClearArray() {
// Create three cache entries.
- cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
- cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
- cache_set('test_cid_clear3', $this->default_value, $this->default_bin);
+ $cache = cache($this->default_bin);
+ $cache->set('test_cid_clear1', $this->default_value);
+ $cache->set('test_cid_clear2', $this->default_value);
+ $cache->set('test_cid_clear3', $this->default_value);
$this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
&& $this->checkCacheExists('test_cid_clear2', $this->default_value)
&& $this->checkCacheExists('test_cid_clear3', $this->default_value),
t('Three cache entries were created.'));
// Clear two entries using an array.
- cache_clear_all(array('test_cid_clear1', 'test_cid_clear2'), $this->default_bin);
+ $cache->deleteMultiple(array('test_cid_clear1', 'test_cid_clear2'));
$this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
|| $this->checkCacheExists('test_cid_clear2', $this->default_value),
t('Two cache entries removed after clearing with an array.'));
@@ -299,12 +294,12 @@ class CacheClearCase extends CacheTestCase {
// Set the cache clear threshold to 2 to confirm that the full bin is cleared
// when the threshold is exceeded.
variable_set('cache_clear_threshold', 2);
- cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
- cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
+ $cache->set('test_cid_clear1', $this->default_value);
+ $cache->set('test_cid_clear2', $this->default_value);
$this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
&& $this->checkCacheExists('test_cid_clear2', $this->default_value),
t('Two cache entries were created.'));
- cache_clear_all(array('test_cid_clear1', 'test_cid_clear2', 'test_cid_clear3'), $this->default_bin);
+ $cache->deleteMultiple(array('test_cid_clear1', 'test_cid_clear2', 'test_cid_clear3'));
$this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
|| $this->checkCacheExists('test_cid_clear2', $this->default_value)
|| $this->checkCacheExists('test_cid_clear3', $this->default_value),
@@ -316,25 +311,25 @@ class CacheClearCase extends CacheTestCase {
*/
function testFlushAllCaches() {
// Create cache entries for each flushed cache bin.
- $bins = array('cache', 'cache_filter', 'cache_page', 'cache_boostrap', 'cache_path');
+ $bins = array('cache', 'filter', 'page', 'bootstrap', 'path');
$bins = array_merge(module_invoke_all('flush_caches'), $bins);
foreach ($bins as $id => $bin) {
- $id = 'test_cid_clear' . $id;
- cache_set($id, $this->default_value, $bin);
+ $cid = 'test_cid_clear' . $id;
+ cache($bin)->set($cid, $this->default_value);
}
// Remove all caches then make sure that they are cleared.
drupal_flush_all_caches();
foreach ($bins as $id => $bin) {
- $id = 'test_cid_clear' . $id;
- $this->assertFalse($this->checkCacheExists($id, $this->default_value, $bin), t('All cache entries removed from @bin.', array('@bin' => $bin)));
+ $cid = 'test_cid_clear' . $id;
+ $this->assertFalse($this->checkCacheExists($cid, $this->default_value, $bin), t('All cache entries removed from @bin.', array('@bin' => $bin)));
}
}
}
/**
- * Test cache_is_empty() function.
+ * Test isEmpty() method.
*/
class CacheIsEmptyCase extends CacheTestCase {
public static function getInfo() {
@@ -346,7 +341,7 @@ class CacheIsEmptyCase extends CacheTestCase {
}
function setUp() {
- $this->default_bin = 'cache_page';
+ $this->default_bin = 'page';
$this->default_value = $this->randomName(10);
parent::setUp();
@@ -357,15 +352,16 @@ class CacheIsEmptyCase extends CacheTestCase {
*/
function testIsEmpty() {
// Clear the cache bin.
- cache_clear_all('*', $this->default_bin);
- $this->assertTrue(cache_is_empty($this->default_bin), t('The cache bin is empty'));
+ $cache = cache($this->default_bin);
+ $cache->flush();
+ $this->assertTrue($cache->isEmpty(), t('The cache bin is empty'));
// Add some data to the cache bin.
- cache_set($this->default_cid, $this->default_value, $this->default_bin);
+ $cache->set($this->default_cid, $this->default_value);
$this->assertCacheExists(t('Cache was set.'), $this->default_value, $this->default_cid);
- $this->assertFalse(cache_is_empty($this->default_bin), t('The cache bin is not empty'));
+ $this->assertFalse($cache->isEmpty(), t('The cache bin is not empty'));
// Remove the cached data.
- cache_clear_all($this->default_cid, $this->default_bin);
+ $cache->delete($this->default_cid);
$this->assertCacheRemoved(t('Cache was removed.'), $this->default_cid);
- $this->assertTrue(cache_is_empty($this->default_bin), t('The cache bin is empty'));
+ $this->assertTrue($cache->isEmpty(), t('The cache bin is empty'));
}
}
diff --git a/modules/simpletest/tests/module.test b/modules/simpletest/tests/module.test
index f55c08af4c97..c9601c9b9ab7 100644
--- a/modules/simpletest/tests/module.test
+++ b/modules/simpletest/tests/module.test
@@ -86,17 +86,17 @@ class ModuleUnitTest extends DrupalWebTestCase {
*/
function testModuleImplements() {
// Clear the cache.
- cache_clear_all('module_implements', 'cache_bootstrap');
- $this->assertFalse(cache_get('module_implements', 'cache_bootstrap'), t('The module implements cache is empty.'));
+ cache('bootstrap')->delete('module_implements');
+ $this->assertFalse(cache('bootstrap')->get('module_implements'), t('The module implements cache is empty.'));
$this->drupalGet('');
- $this->assertTrue(cache_get('module_implements', 'cache_bootstrap'), t('The module implements cache is populated after requesting a page.'));
+ $this->assertTrue(cache('bootstrap')->get('module_implements'), t('The module implements cache is populated after requesting a page.'));
// Test again with an authenticated user.
$this->user = $this->drupalCreateUser();
$this->drupalLogin($this->user);
- cache_clear_all('module_implements', 'cache_bootstrap');
+ cache('bootstrap')->delete('module_implements');
$this->drupalGet('');
- $this->assertTrue(cache_get('module_implements', 'cache_bootstrap'), t('The module implements cache is populated after requesting a page.'));
+ $this->assertTrue(cache('bootstrap')->get('module_implements'), t('The module implements cache is populated after requesting a page.'));
// Make sure group include files are detected properly even when the file is
// already loaded when the cache is rebuilt.
diff --git a/modules/simpletest/tests/path.test b/modules/simpletest/tests/path.test
index 4701b5659b12..e1b320d270d1 100644
--- a/modules/simpletest/tests/path.test
+++ b/modules/simpletest/tests/path.test
@@ -180,7 +180,7 @@ class UrlAlterFunctionalTest extends DrupalWebTestCase {
// level and for a specific existing forum.
$this->assertUrlInboundAlter('community', 'forum');
$this->assertUrlOutboundAlter('forum', 'community');
- $forum_vid = db_query("SELECT vid FROM {taxonomy_vocabulary} WHERE module = 'forum'")->fetchField();
+ $forum_vid = variable_get('forum_nav_vocabulary');
$tid = db_insert('taxonomy_term_data')
->fields(array(
'name' => $this->randomName(),
diff --git a/modules/simpletest/tests/theme.test b/modules/simpletest/tests/theme.test
index f1e1bd58b86a..53557e361a03 100644
--- a/modules/simpletest/tests/theme.test
+++ b/modules/simpletest/tests/theme.test
@@ -354,3 +354,29 @@ class ThemeFastTestCase extends DrupalWebTestCase {
$this->assertText('registry not initialized', t('The registry was not initialized'));
}
}
+
+/**
+ * Unit tests for theme_html_tag().
+ */
+class ThemeHtmlTag extends DrupalUnitTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Theme HTML Tag',
+ 'description' => 'Tests theme_html_tag() built-in theme functions.',
+ 'group' => 'Theme',
+ );
+ }
+
+ /**
+ * Test function theme_html_tag()
+ */
+ function testThemeHtmlTag() {
+ // Test auto-closure meta tag generation
+ $tag['element'] = array('#tag' => 'meta', '#attributes' => array('name' => 'description', 'content' => 'Drupal test'));
+ $this->assertEqual(''."\n", theme_html_tag($tag), t('Test auto-closure meta tag generation.'));
+
+ // Test title tag generation
+ $tag['element'] = array('#tag' => 'title', '#value' => 'title test');
+ $this->assertEqual('title test'."\n", theme_html_tag($tag), t('Test title tag generation.'));
+ }
+}
diff --git a/modules/simpletest/tests/upgrade/drupal-7.bare.database.php.gz b/modules/simpletest/tests/upgrade/drupal-7.bare.database.php.gz
new file mode 100644
index 000000000000..4eddff6ac194
Binary files /dev/null and b/modules/simpletest/tests/upgrade/drupal-7.bare.database.php.gz differ
diff --git a/modules/simpletest/tests/upgrade/drupal-7.filled.database.php.gz b/modules/simpletest/tests/upgrade/drupal-7.filled.database.php.gz
new file mode 100644
index 000000000000..9ff7c0bc4184
Binary files /dev/null and b/modules/simpletest/tests/upgrade/drupal-7.filled.database.php.gz differ
diff --git a/modules/simpletest/tests/upgrade/upgrade.test b/modules/simpletest/tests/upgrade/upgrade.test
index 1ef8525cbbcb..e2420b00d05e 100644
--- a/modules/simpletest/tests/upgrade/upgrade.test
+++ b/modules/simpletest/tests/upgrade/upgrade.test
@@ -33,6 +33,14 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase {
protected function setUp() {
global $user, $language, $conf;
+ // We are going to set a missing zlib requirement property for usage during
+ // the performUpgrade() and tearDown() calls. Also set that the tests failed.
+ if (!function_exists('gzopen')) {
+ $this->missing_zlib_requirement = TRUE;
+ parent::setUp();
+ return;
+ }
+
// Load the Update API.
require_once DRUPAL_ROOT . '/includes/update.inc';
@@ -92,7 +100,11 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase {
$conf = array();
// Load the database from the portable PHP dump.
+ // The files can be gzipped.
foreach ($this->databaseDumpFiles as $file) {
+ if (substr($file, -3) == '.gz') {
+ $file = "compress.zlib://$file";
+ }
require $file;
}
@@ -111,17 +123,12 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase {
// Generate and set a D6-compatible session cookie.
$this->curlInitialize();
$sid = drupal_hash_base64(uniqid(mt_rand(), TRUE) . drupal_random_bytes(55));
- $session_name = update_get_d6_session_name();
- curl_setopt($this->curlHandle, CURLOPT_COOKIE, rawurlencode($session_name) . '=' . rawurlencode($sid));
+ curl_setopt($this->curlHandle, CURLOPT_COOKIE, rawurlencode(session_name()) . '=' . rawurlencode($sid));
// Force our way into the session of the child site.
drupal_save_session(TRUE);
- // A session cannot be written without the ssid column which is missing on
- // Drupal 6 sites.
- db_add_field('sessions', 'ssid', array('description' => "Secure session ID. The value is generated by Drupal's session handlers.", 'type' => 'varchar', 'length' => 128, 'not null' => TRUE, 'default' => ''));
_drupal_session_write($sid, '');
// Remove the temporarily added ssid column.
- db_drop_field('sessions', 'ssid');
drupal_save_session(FALSE);
// Restore necessary variables.
@@ -137,6 +144,11 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase {
protected function tearDown() {
global $user, $language;
+ if (!empty($this->missing_zlib_requirement)) {
+ parent::tearDown();
+ return;
+ }
+
// In case a fatal error occured that was not in the test process read the
// log to pick up any fatal errors.
simpletest_log_read($this->testId, $this->databasePrefix, get_class($this), TRUE);
@@ -204,8 +216,8 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase {
->execute();
try {
- cache_clear_all('variables', 'cache');
- cache_clear_all('variables', 'cache_bootstrap');
+ cache()->delete('variables');
+ cache('bootstrap')->delete('variables');
}
// Since cache_bootstrap won't exist in a Drupal 6 site, ignore the
// exception if the above fails.
@@ -233,6 +245,11 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase {
protected function performUpgrade($register_errors = TRUE) {
$update_url = $GLOBALS['base_url'] . '/update.php';
+ if (!empty($this->missing_zlib_requirement)) {
+ $this->fail(t('Missing zlib requirement for upgrade tests.'));
+ return FALSE;
+ }
+
// Load the first update screen.
$this->drupalGet($update_url, array('external' => TRUE));
if (!$this->assertResponse(200)) {
diff --git a/modules/simpletest/tests/upgrade/upgrade_bare.test b/modules/simpletest/tests/upgrade/upgrade_bare.test
new file mode 100644
index 000000000000..db71228575c9
--- /dev/null
+++ b/modules/simpletest/tests/upgrade/upgrade_bare.test
@@ -0,0 +1,31 @@
+ 'Bare upgrade test',
+ 'description' => 'Bare upgrade test.',
+ 'group' => 'Upgrade path',
+ );
+ }
+
+ public function setUp() {
+ // Path to the database dump.
+ $this->databaseDumpFiles = array(
+ drupal_get_path('module', 'simpletest') . '/tests/upgrade/drupal-7.bare.database.php.gz',
+ );
+ parent::setUp();
+ }
+
+ /**
+ * Test a successful upgrade.
+ */
+ public function testBareUpgrade() {
+ $this->assertTrue($this->performUpgrade(), t('The upgrade was completed successfully.'));
+ }
+}
diff --git a/modules/simpletest/tests/upgrade/upgrade_filled.test b/modules/simpletest/tests/upgrade/upgrade_filled.test
new file mode 100644
index 000000000000..9b17bda4caa4
--- /dev/null
+++ b/modules/simpletest/tests/upgrade/upgrade_filled.test
@@ -0,0 +1,31 @@
+ 'Filled upgrade test',
+ 'description' => 'Filled upgrade test.',
+ 'group' => 'Upgrade path',
+ );
+ }
+
+ public function setUp() {
+ // Path to the database dump.
+ $this->databaseDumpFiles = array(
+ drupal_get_path('module', 'simpletest') . '/tests/upgrade/drupal-7.filled.database.php.gz',
+ );
+ parent::setUp();
+ }
+
+ /**
+ * Test a successful upgrade.
+ */
+ public function testFilledUpgrade() {
+ $this->assertTrue($this->performUpgrade(), t('The upgrade was completed successfully.'));
+ }
+}
diff --git a/modules/system/system.admin.css b/modules/system/system.admin.css
index 43340b58d3fd..7299484c38d8 100644
--- a/modules/system/system.admin.css
+++ b/modules/system/system.admin.css
@@ -243,7 +243,6 @@ table.screenshot {
.exposed-filters .filters {
float: left; /* LTR */
margin-right: 1em; /* LTR */
- width: 25em; /* IE6 */
}
.exposed-filters .form-item {
margin: 0 0 0.1em 0;
diff --git a/modules/system/system.admin.inc b/modules/system/system.admin.inc
index 9dffd5999b19..d5d10b284865 100644
--- a/modules/system/system.admin.inc
+++ b/modules/system/system.admin.inc
@@ -1734,7 +1734,7 @@ function system_clear_cache_submit($form, &$form_state) {
* @ingroup forms
*/
function system_clear_page_cache_submit($form, &$form_state) {
- cache_clear_all('*', 'cache_page', TRUE);
+ cache('page')->flush();
}
/**
@@ -2182,24 +2182,45 @@ function system_site_maintenance_mode() {
* @see system_settings_form()
*/
function system_clean_url_settings($form, &$form_state) {
- global $base_url;
-
- // When accessing this form using a non-clean URL, allow a re-check to make
- // sure clean URLs can be disabled at all times.
$available = FALSE;
- if (strpos(request_uri(), '?q=') === FALSE || !empty($_SESSION['clean_url'])) {
+ $conflict = FALSE;
+
+ // If the request URI is a clean URL, clean URLs must be available.
+ // Otherwise, run a test.
+ if (strpos(request_uri(), '?q=') === FALSE && strpos(request_uri(), '&q=') === FALSE) {
$available = TRUE;
}
else {
- $request = drupal_http_request($base_url . '/admin/config/search/clean-urls/check');
+ $request = drupal_http_request($GLOBALS['base_url'] . '/admin/config/search/clean-urls/check');
+ // If the request returns HTTP 200, clean URLs are available.
if (isset($request->code) && $request->code == 200) {
$available = TRUE;
+ // If the user started the clean URL test, provide explicit feedback.
+ if (isset($form_state['input']['clean_url_test_execute'])) {
+ drupal_set_message(t('The clean URL test passed.'));
+ }
+ }
+ else {
+ // If the test failed while clean URLs are enabled, make sure clean URLs
+ // can be disabled.
+ if (variable_get('clean_url', 0)) {
+ $conflict = TRUE;
+ // Warn the user of a conflicting situation, unless after processing
+ // a submitted form.
+ if (!isset($form_state['input']['op'])) {
+ drupal_set_message(t('Clean URLs are enabled, but the clean URL test failed. Uncheck the box below to disable clean URLs.'), 'warning');
+ }
+ }
+ // If the user started the clean URL test, provide explicit feedback.
+ elseif (isset($form_state['input']['clean_url_test_execute'])) {
+ drupal_set_message(t('The clean URL test failed.'), 'warning');
+ }
}
}
- if ($available) {
- $_SESSION['clean_url'] = TRUE;
-
+ // Show the enable/disable form if clean URLs are available or if the user
+ // must be able to resolve a conflicting setting.
+ if ($available || $conflict) {
$form['clean_url'] = array(
'#type' => 'checkbox',
'#title' => t('Enable clean URLs'),
@@ -2207,18 +2228,37 @@ function system_clean_url_settings($form, &$form_state) {
'#description' => t('Use URLs like example.com/user
instead of example.com/?q=user
.'),
);
$form = system_settings_form($form);
+ if ($conflict) {
+ // $form_state['redirect'] needs to be set to the non-clean URL,
+ // otherwise the setting is not saved.
+ $form_state['redirect'] = url('', array('query' => array('q' => '/admin/config/search/clean-urls')));
+ }
}
+ // Show the clean URLs test form.
else {
drupal_add_js(drupal_get_path('module', 'system') . '/system.js');
- $form_state['redirect'] = $base_url . '/admin/config/search/clean-urls';
+ $form_state['redirect'] = url('admin/config/search/clean-urls');
$form['clean_url_description'] = array(
'#type' => 'markup',
- '#markup' => '' . t('Use URLs like example.com/user
instead of example.com/?q=user
.') . ' ' . t('If you are directed to a Page not found (404) error after testing for clean URLs, see the online handbook.', array('@handbook' => 'http://drupal.org/node/15365')) . '
',
+ '#markup' => '' . t('Use URLs like example.com/user
instead of example.com/?q=user
.'),
);
- $form['clean_url_test'] = array(
- '#type' => 'submit',
- '#value' => t('Run the clean URL test'),
+ // Explain why the user is seeing this page and what to expect after
+ // clicking the 'Run the clean URL test' button.
+ $form['clean_url_test_result'] = array(
+ '#type' => 'markup',
+ '#markup' => '
' . t('Clean URLs cannot be enabled. If you are directed to this page or to a Page not found (404) error after testing for clean URLs, see the online handbook.', array('@handbook' => 'http://drupal.org/node/15365')) . '
',
+ );
+ $form['actions'] = array(
+ '#type' => 'actions',
+ 'clean_url_test' => array(
+ '#type' => 'submit',
+ '#value' => t('Run the clean URL test'),
+ ),
+ );
+ $form['clean_url_test_execute'] = array(
+ '#type' => 'hidden',
+ '#value' => 1,
);
}
diff --git a/modules/system/system.api.php b/modules/system/system.api.php
index bcdd923220e8..297d4d9f7f60 100644
--- a/modules/system/system.api.php
+++ b/modules/system/system.api.php
@@ -59,392 +59,6 @@ function hook_hook_info_alter(&$hooks) {
$hooks['tokens']['group'] = 'mytokens';
}
-/**
- * Inform the base system and the Field API about one or more entity types.
- *
- * Inform the system about one or more entity types (i.e., object types that
- * can be loaded via entity_load() and, optionally, to which fields can be
- * attached).
- *
- * @return
- * An array whose keys are entity type names and whose values identify
- * properties of those types that the system needs to know about:
- * - label: The human-readable name of the type.
- * - controller class: The name of the class that is used to load the objects.
- * The class has to implement the DrupalEntityControllerInterface interface.
- * Leave blank to use the DrupalDefaultEntityController implementation.
- * - base table: (used by DrupalDefaultEntityController) The name of the
- * entity type's base table.
- * - revision table: The name of the entity type's revision table (if any).
- * - static cache: (used by DrupalDefaultEntityController) FALSE to disable
- * static caching of entities during a page request. Defaults to TRUE.
- * - field cache: (used by Field API loading and saving of field data) FALSE
- * to disable Field API's persistent cache of field data. Only recommended
- * if a higher level persistent cache is available for the entity type.
- * Defaults to TRUE.
- * - load hook: The name of the hook which should be invoked by
- * DrupalDefaultEntityController:attachLoad(), for example 'node_load'.
- * - uri callback: A function taking an entity as argument and returning the
- * uri elements of the entity, e.g. 'path' and 'options'. The actual entity
- * uri can be constructed by passing these elements to url().
- * - label callback: (optional) A function taking an entity type and an entity
- * as arguments and returning the label of the entity. The entity label is
- * the main string associated with an entity; for example, the title of a
- * node or the subject of a comment. If there is an entity object property
- * that defines the label, use the 'label' element of the 'entity keys'
- * return value component to provide this information (see below). If more
- * complex logic is needed to determine the label of an entity, you can
- * instead specify a callback function here, which will be called to
- * determine the entity label. See also the entity_label() function, which
- * implements this logic.
- * - fieldable: Set to TRUE if you want your entity type to accept fields
- * being attached to it.
- * - translation: An associative array of modules registered as field
- * translation handlers. Array keys are the module names, array values
- * can be any data structure the module uses to provide field translation.
- * Any empty value disallows the module to appear as a translation handler.
- * - entity keys: An array describing how the Field API can extract the
- * information it needs from the objects of the type. Elements:
- * - id: The name of the property that contains the primary id of the
- * entity. Every entity object passed to the Field API must have this
- * property and its value must be numeric.
- * - revision: The name of the property that contains the revision id of
- * the entity. The Field API assumes that all revision ids are unique
- * across all entities of a type. This entry can be omitted if the
- * entities of this type are not versionable.
- * - bundle: The name of the property that contains the bundle name for the
- * entity. The bundle name defines which set of fields are attached to
- * the entity (e.g. what nodes call "content type"). This entry can be
- * omitted if this entity type exposes a single bundle (all entities have
- * the same collection of fields). The name of this single bundle will be
- * the same as the entity type.
- * - label: The name of the property that contains the entity label. For
- * example, if the entity's label is located in $entity->subject, then
- * 'subject' should be specified here. If complex logic is required to
- * build the label, a 'label callback' should be defined instead (see
- * the 'label callback' section above for details).
- * - bundle keys: An array describing how the Field API can extract the
- * information it needs from the bundle objects for this type. This entry
- * is required if the 'path' provided in the 'bundles'/'admin' section
- * identifies the bundle using a named menu placeholder whose loader
- * callback returns an object (e.g., $vocabulary for taxonomy terms, or
- * $node_type for nodes). If the path does not include the bundle, or the
- * bundle is just a string rather than an automatically loaded object, then
- * this can be omitted. Elements:
- * - bundle: The name of the property of the bundle object that contains
- * the name of the bundle object.
- * - bundles: An array describing all bundles for this object type. Keys are
- * bundles machine names, as found in the objects' 'bundle' property
- * (defined in the 'entity keys' entry above). Elements:
- * - label: The human-readable name of the bundle.
- * - uri callback: Same as the 'uri callback' key documented above for the
- * entity type, but for the bundle only. When determining the URI of an
- * entity, if a 'uri callback' is defined for both the entity type and
- * the bundle, the one for the bundle is used.
- * - admin: An array of information that allows Field UI pages to attach
- * themselves to the existing administration pages for the bundle.
- * Elements:
- * - path: the path of the bundle's main administration page, as defined
- * in hook_menu(). If the path includes a placeholder for the bundle,
- * the 'bundle argument' and 'real path' keys below are required.
- * - bundle argument: The position of the bundle placeholder in 'path', if
- * any.
- * - real path: The actual path (no placeholder) of the bundle's main
- * administration page. This will be used to generate links.
- * - access callback: As in hook_menu(). 'user_access' will be assumed if
- * no value is provided.
- * - access arguments: As in hook_menu().
- * - view modes: An array describing the view modes for the entity type. View
- * modes let entities be displayed differently depending on the context.
- * For instance, a node can be displayed differently on its own page
- * ('full' mode), on the home page or taxonomy listings ('teaser' mode), or
- * in an RSS feed ('rss' mode). Modules taking part in the display of the
- * entity (notably the Field API) can adjust their behavior depending on
- * the requested view mode. An additional 'default' view mode is available
- * for all entity types. This view mode is not intended for actual entity
- * display, but holds default display settings. For each available view
- * mode, administrators can configure whether it should use its own set of
- * field display settings, or just replicate the settings of the 'default'
- * view mode, thus reducing the amount of display configurations to keep
- * track of. Keys of the array are view mode names. Each view mode is
- * described by an array with the following key/value pairs:
- * - label: The human-readable name of the view mode
- * - custom settings: A boolean specifying whether the view mode should by
- * default use its own custom field display settings. If FALSE, entities
- * displayed in this view mode will reuse the 'default' display settings
- * by default (e.g. right after the module exposing the view mode is
- * enabled), but administrators can later use the Field UI to apply custom
- * display settings specific to the view mode.
- *
- * @see entity_load()
- * @see hook_entity_info_alter()
- */
-function hook_entity_info() {
- $return = array(
- 'node' => array(
- 'label' => t('Node'),
- 'controller class' => 'NodeController',
- 'base table' => 'node',
- 'revision table' => 'node_revision',
- 'uri callback' => 'node_uri',
- 'fieldable' => TRUE,
- 'translation' => array(
- 'locale' => TRUE,
- ),
- 'entity keys' => array(
- 'id' => 'nid',
- 'revision' => 'vid',
- 'bundle' => 'type',
- ),
- 'bundle keys' => array(
- 'bundle' => 'type',
- ),
- 'bundles' => array(),
- 'view modes' => array(
- 'full' => array(
- 'label' => t('Full content'),
- 'custom settings' => FALSE,
- ),
- 'teaser' => array(
- 'label' => t('Teaser'),
- 'custom settings' => TRUE,
- ),
- 'rss' => array(
- 'label' => t('RSS'),
- 'custom settings' => FALSE,
- ),
- ),
- ),
- );
-
- // Search integration is provided by node.module, so search-related
- // view modes for nodes are defined here and not in search.module.
- if (module_exists('search')) {
- $return['node']['view modes'] += array(
- 'search_index' => array(
- 'label' => t('Search index'),
- 'custom settings' => FALSE,
- ),
- 'search_result' => array(
- 'label' => t('Search result'),
- 'custom settings' => FALSE,
- ),
- );
- }
-
- // Bundles must provide a human readable name so we can create help and error
- // messages, and the path to attach Field admin pages to.
- foreach (node_type_get_names() as $type => $name) {
- $return['node']['bundles'][$type] = array(
- 'label' => $name,
- 'admin' => array(
- 'path' => 'admin/structure/types/manage/%node_type',
- 'real path' => 'admin/structure/types/manage/' . str_replace('_', '-', $type),
- 'bundle argument' => 4,
- 'access arguments' => array('administer content types'),
- ),
- );
- }
-
- return $return;
-}
-
-/**
- * Alter the entity info.
- *
- * Modules may implement this hook to alter the information that defines an
- * entity. All properties that are available in hook_entity_info() can be
- * altered here.
- *
- * @param $entity_info
- * The entity info array, keyed by entity name.
- *
- * @see hook_entity_info()
- */
-function hook_entity_info_alter(&$entity_info) {
- // Set the controller class for nodes to an alternate implementation of the
- // DrupalEntityController interface.
- $entity_info['node']['controller class'] = 'MyCustomNodeController';
-}
-
-/**
- * Act on entities when loaded.
- *
- * This is a generic load hook called for all entity types loaded via the
- * entity API.
- *
- * @param $entities
- * The entities keyed by entity ID.
- * @param $type
- * The type of entities being loaded (i.e. node, user, comment).
- */
-function hook_entity_load($entities, $type) {
- foreach ($entities as $entity) {
- $entity->foo = mymodule_add_something($entity, $type);
- }
-}
-
-/**
- * Act on an entity before it is about to be created or updated.
- *
- * @param $entity
- * The entity object.
- * @param $type
- * The type of entity being saved (i.e. node, user, comment).
- */
-function hook_entity_presave($entity, $type) {
- $entity->changed = REQUEST_TIME;
-}
-
-/**
- * Act on entities when inserted.
- *
- * @param $entity
- * The entity object.
- * @param $type
- * The type of entity being inserted (i.e. node, user, comment).
- */
-function hook_entity_insert($entity, $type) {
- // Insert the new entity into a fictional table of all entities.
- $info = entity_get_info($type);
- list($id) = entity_extract_ids($type, $entity);
- db_insert('example_entity')
- ->fields(array(
- 'type' => $type,
- 'id' => $id,
- 'created' => REQUEST_TIME,
- 'updated' => REQUEST_TIME,
- ))
- ->execute();
-}
-
-/**
- * Act on entities when updated.
- *
- * @param $entity
- * The entity object.
- * @param $type
- * The type of entity being updated (i.e. node, user, comment).
- */
-function hook_entity_update($entity, $type) {
- // Update the entity's entry in a fictional table of all entities.
- $info = entity_get_info($type);
- list($id) = entity_extract_ids($type, $entity);
- db_update('example_entity')
- ->fields(array(
- 'updated' => REQUEST_TIME,
- ))
- ->condition('type', $type)
- ->condition('id', $id)
- ->execute();
-}
-
-/**
- * Act on entities when deleted.
- *
- * @param $entity
- * The entity object.
- * @param $type
- * The type of entity being deleted (i.e. node, user, comment).
- */
-function hook_entity_delete($entity, $type) {
- // Delete the entity's entry from a fictional table of all entities.
- $info = entity_get_info($type);
- list($id) = entity_extract_ids($type, $entity);
- db_delete('example_entity')
- ->condition('type', $type)
- ->condition('id', $id)
- ->execute();
-}
-
-/**
- * Alter or execute an EntityFieldQuery.
- *
- * @param EntityFieldQuery $query
- * An EntityFieldQuery. One of the most important properties to be changed is
- * EntityFieldQuery::executeCallback. If this is set to an existing function,
- * this function will get the query as its single argument and its result
- * will be the returned as the result of EntityFieldQuery::execute(). This can
- * be used to change the behavior of EntityFieldQuery entirely. For example,
- * the default implementation can only deal with one field storage engine, but
- * it is possible to write a module that can query across field storage
- * engines. Also, the default implementation presumes entities are stored in
- * SQL, but the execute callback could instead query any other entity storage,
- * local or remote.
- *
- * Note the $query->altered attribute which is TRUE in case the query has
- * already been altered once. This happens with cloned queries.
- * If there is a pager, then such a cloned query will be executed to count
- * all elements. This query can be detected by checking for
- * ($query->pager && $query->count), allowing the driver to return 0 from
- * the count query and disable the pager.
- */
-function hook_entity_query_alter($query) {
- $query->executeCallback = 'my_module_query_callback';
-}
-
-/**
- * Act on entities being assembled before rendering.
- *
- * @param $entity
- * The entity object.
- * @param $type
- * The type of entity being rendered (i.e. node, user, comment).
- * @param $view_mode
- * The view mode the entity is rendered in.
- * @param $langcode
- * The language code used for rendering.
- *
- * The module may add elements to $entity->content prior to rendering. The
- * structure of $entity->content is a renderable array as expected by
- * drupal_render().
- *
- * @see hook_entity_view_alter()
- * @see hook_comment_view()
- * @see hook_node_view()
- * @see hook_user_view()
- */
-function hook_entity_view($entity, $type, $view_mode, $langcode) {
- $entity->content['my_additional_field'] = array(
- '#markup' => $additional_field,
- '#weight' => 10,
- '#theme' => 'mymodule_my_additional_field',
- );
-}
-
-/**
- * Alter the results of ENTITY_view().
- *
- * This hook is called after the content has been assembled in a structured
- * array and may be used for doing processing which requires that the complete
- * entity content structure has been built.
- *
- * If a module wishes to act on the rendered HTML of the entity rather than the
- * structured content array, it may use this hook to add a #post_render
- * callback. Alternatively, it could also implement hook_preprocess_ENTITY().
- * See drupal_render() and theme() for details.
- *
- * @param $build
- * A renderable array representing the entity content.
- * @param $type
- * The type of entity being rendered (i.e. node, user, comment).
- *
- * @see hook_entity_view()
- * @see hook_comment_view_alter()
- * @see hook_node_view_alter()
- * @see hook_taxonomy_term_view_alter()
- * @see hook_user_view_alter()
- */
-function hook_entity_view_alter(&$build, $type) {
- if ($build['#view_mode'] == 'full' && isset($build['an_additional_field'])) {
- // Change its weight.
- $build['an_additional_field']['#weight'] = -10;
-
- // Add a #post_render callback to act on the rendered HTML of the entity.
- $build['#post_render'][] = 'my_module_node_post_render';
- }
-}
-
/**
* Define administrative paths.
*
@@ -491,30 +105,6 @@ function hook_admin_paths_alter(&$paths) {
$paths['node/add/forum'] = FALSE;
}
-/**
- * Act on entities as they are being prepared for view.
- *
- * Allows you to operate on multiple entities as they are being prepared for
- * view. Only use this if attaching the data during the entity_load() phase
- * is not appropriate, for example when attaching other 'entity' style objects.
- *
- * @param $entities
- * The entities keyed by entity ID.
- * @param $type
- * The type of entities being loaded (i.e. node, user, comment).
- * @param $langcode
- * The language to display the entity in.
- */
-function hook_entity_prepare_view($entities, $type, $langcode) {
- // Load a specific node into the user object for later theming.
- if ($type == 'user') {
- $nodes = mymodule_get_user_nodes(array_keys($entities));
- foreach ($entities as $uid => $entity) {
- $entity->user_node = $nodes[$uid];
- }
- }
-}
-
/**
* Perform periodic actions.
*
@@ -672,8 +262,8 @@ function hook_element_info_alter(&$type) {
* page logging and specialized cleanup. This hook MUST NOT print anything.
*
* Only use this hook if your code must run even for cached page views.
- * If you have code which must run once on all non cached pages, use
- * hook_init instead. Thats the usual case. If you implement this hook
+ * If you have code which must run once on all non-cached pages, use
+ * hook_init() instead. That is the usual case. If you implement this hook
* and see an error like 'Call to undefined function', it is likely that
* you are depending on the presence of a module which has not been loaded yet.
* It is not loaded because Drupal is still in bootstrap mode.
@@ -1053,24 +643,24 @@ function hook_menu_get_item_alter(&$router_item, $path, $original_map) {
* MENU_LOCAL_TASK. Example:
* @code
* // Make "Foo settings" appear on the admin Config page
- * $items['admin/config/foo'] = array(
+ * $items['admin/config/system/foo'] = array(
* 'title' => 'Foo settings',
* 'type' => MENU_NORMAL_ITEM,
* // Page callback, etc. need to be added here.
* );
- * // Make "Global settings" the main tab on the "Foo settings" page
- * $items['admin/config/foo/global'] = array(
- * 'title' => 'Global settings',
+ * // Make "Tab 1" the main tab on the "Foo settings" page
+ * $items['admin/config/system/foo/tab1'] = array(
+ * 'title' => 'Tab 1',
* 'type' => MENU_DEFAULT_LOCAL_TASK,
* // Access callback, page callback, and theme callback will be inherited
- * // from 'admin/config/foo', if not specified here to override.
+ * // from 'admin/config/system/foo', if not specified here to override.
* );
- * // Make an additional tab called "Node settings" on "Foo settings"
- * $items['admin/config/foo/node'] = array(
- * 'title' => 'Node settings',
+ * // Make an additional tab called "Tab 2" on "Foo settings"
+ * $items['admin/config/system/foo/tab2'] = array(
+ * 'title' => 'Tab 2',
* 'type' => MENU_LOCAL_TASK,
* // Page callback and theme callback will be inherited from
- * // 'admin/config/foo', if not specified here to override.
+ * // 'admin/config/system/foo', if not specified here to override.
* // Need to add access callback or access arguments.
* );
* @endcode
@@ -1744,32 +1334,36 @@ function hook_forms($form_id, $args) {
}
/**
- * Perform setup tasks. See also, hook_init.
+ * Perform setup tasks for all page requests.
*
* This hook is run at the beginning of the page request. It is typically
- * used to set up global parameters which are needed later in the request.
+ * used to set up global parameters that are needed later in the request.
*
- * Only use this hook if your code must run even for cached page views.This hook
- * is called before modules or most include files are loaded into memory.
+ * Only use this hook if your code must run even for cached page views. This
+ * hook is called before modules or most include files are loaded into memory.
* It happens while Drupal is still in bootstrap mode.
+ *
+ * @see hook_init()
*/
function hook_boot() {
- // we need user_access() in the shutdown function. make sure it gets loaded
+ // We need user_access() in the shutdown function. Make sure it gets loaded.
drupal_load('module', 'user');
drupal_register_shutdown_function('devel_shutdown');
}
/**
- * Perform setup tasks. See also, hook_boot.
+ * Perform setup tasks for non-cached page requests.
*
* This hook is run at the beginning of the page request. It is typically
- * used to set up global parameters which are needed later in the request.
- * when this hook is called, all modules are already loaded in memory.
+ * used to set up global parameters that are needed later in the request.
+ * When this hook is called, all modules are already loaded in memory.
*
* This hook is not run on cached pages.
*
* To add CSS or JS that should be present on all pages, modules should not
* implement this hook, but declare these files in their .info file.
+ *
+ * @see hook_boot()
*/
function hook_init() {
// Since this file should only be loaded on the front page, it cannot be
@@ -2377,17 +1971,41 @@ function hook_mail($key, &$message, $params) {
/**
* Add a list of cache tables to be cleared.
*
- * This hook allows your module to add cache table names to the list of cache
- * tables that will be cleared by the Clear button on the Performance page or
+ * This hook allows your module to add cache bins to the list of cache bins
+ * that will be cleared by the Clear button on the Performance page or
* whenever drupal_flush_all_caches is invoked.
*
* @return
- * An array of cache table names.
+ * An array of cache bins.
*
* @see drupal_flush_all_caches()
*/
function hook_flush_caches() {
- return array('cache_example');
+ return array('example');
+}
+
+/**
+ * Perform necessary actions before modules are installed.
+ *
+ * This function allows all modules to react prior to a module being installed.
+ *
+ * @param $modules
+ * An array of modules about to be installed.
+ */
+function hook_modules_preinstall($modules) {
+ mymodule_cache_clear();
+}
+
+/**
+ * Perform necessary actions before modules are enabled.
+ *
+ * This function allows all modules to react prior to a module being enabled.
+ *
+ * @param $module
+ * An array of modules about to be enabled.
+ */
+function hook_modules_preenable($modules) {
+ mymodule_cache_clear();
}
/**
@@ -2627,17 +2245,21 @@ function hook_file_presave($file) {
/**
* Respond to a file being added.
*
- * This hook is called before a file has been added to the database. The hook
+ * This hook is called after a file has been added to the database. The hook
* doesn't distinguish between files created as a result of a copy or those
* created by an upload.
*
* @param $file
- * The file that is about to be saved.
+ * The file that has been added.
*
* @see file_save()
*/
function hook_file_insert($file) {
-
+ // Add a message to the log, if the file is a jpg
+ $validate = file_validate_extensions($file, 'jpg');
+ if (empty($validate)) {
+ watchdog('file', 'A jpg has been added.');
+ }
}
/**
@@ -4383,7 +4005,7 @@ function hook_updater_info_alter(&$updaters) {
* The associative array of countries keyed by ISO 3166-1 country code.
*
* @see country_get_list()
- * @see _country_get_predefined_list()
+ * @see standard_country_list()
*/
function hook_countries_alter(&$countries) {
// Elbonia is now independent, so add it to the country list.
diff --git a/modules/system/system.base.css b/modules/system/system.base.css
index a6748de42ebd..4ce63cf99955 100644
--- a/modules/system/system.base.css
+++ b/modules/system/system.base.css
@@ -227,7 +227,7 @@ html.js .js-hide {
*/
.element-invisible {
position: absolute !important;
- clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
+ clip: rect(1px 1px 1px 1px); /* IE7 */
clip: rect(1px, 1px, 1px, 1px);
}
@@ -253,10 +253,6 @@ html.js .js-hide {
clear: both;
visibility: hidden;
}
-/* IE6 */
-* html .clearfix {
- height: 1%;
-}
/* IE7 */
*:first-child + html .clearfix {
min-height: 1%;
diff --git a/modules/system/system.install b/modules/system/system.install
index e039ba49da53..903d08166226 100644
--- a/modules/system/system.install
+++ b/modules/system/system.install
@@ -1645,10 +1645,10 @@ function system_update_last_removed() {
*/
/**
- * Placeholder update to set the schema version to 8000.
+ * Enable entity module.
*/
function system_update_8000() {
- // Fill in the first update to Drupal 8 when needed.
+ update_module_enable(array('entity'));
}
/**
diff --git a/modules/system/system.module b/modules/system/system.module
index 3737af1bc0f8..d7dc69cc4c54 100644
--- a/modules/system/system.module
+++ b/modules/system/system.module
@@ -1359,7 +1359,7 @@ function system_library() {
),
);
$libraries['ui.mouse'] = array(
- 'title' => 'jQuery UI: Droppable',
+ 'title' => 'jQuery UI: Mouse',
'website' => 'http://docs.jquery.com/UI/Mouse',
'version' => '1.8.7',
'js' => array(
@@ -3004,10 +3004,10 @@ function system_cron() {
}
}
- $core = array('cache', 'cache_path', 'cache_filter', 'cache_page', 'cache_form', 'cache_menu');
- $cache_tables = array_merge(module_invoke_all('flush_caches'), $core);
- foreach ($cache_tables as $table) {
- cache_clear_all(NULL, $table);
+ $core = array('cache', 'path', 'filter', 'page', 'form', 'menu');
+ $cache_bins = array_merge(module_invoke_all('flush_caches'), $core);
+ foreach ($cache_bins as $bin) {
+ cache($bin)->expire();
}
// Cleanup the batch table and the queue for failed batches.
diff --git a/modules/taxonomy/taxonomy.admin.inc b/modules/taxonomy/taxonomy.admin.inc
index a236cfed1d82..2440a283d8bc 100644
--- a/modules/taxonomy/taxonomy.admin.inc
+++ b/modules/taxonomy/taxonomy.admin.inc
@@ -173,7 +173,6 @@ function taxonomy_form_vocabulary($form, &$form_state, $edit = array()) {
if (isset($vocabulary->vid)) {
$form['actions']['delete'] = array('#type' => 'submit', '#value' => t('Delete'));
$form['vid'] = array('#type' => 'value', '#value' => $vocabulary->vid);
- $form['module'] = array('#type' => 'value', '#value' => $vocabulary->module);
}
$form['#validate'][] = 'taxonomy_form_vocabulary_validate';
diff --git a/modules/taxonomy/taxonomy.info b/modules/taxonomy/taxonomy.info
index cba3869820ff..6a13f81db0af 100644
--- a/modules/taxonomy/taxonomy.info
+++ b/modules/taxonomy/taxonomy.info
@@ -4,6 +4,7 @@ package = Core
version = VERSION
core = 8.x
dependencies[] = options
+dependencies[] = entity
files[] = taxonomy.module
files[] = taxonomy.test
configure = admin/structure/taxonomy
diff --git a/modules/taxonomy/taxonomy.install b/modules/taxonomy/taxonomy.install
index b67f18b58956..3e07259f4118 100644
--- a/modules/taxonomy/taxonomy.install
+++ b/modules/taxonomy/taxonomy.install
@@ -150,13 +150,6 @@ function taxonomy_schema() {
'size' => 'tiny',
'description' => 'The type of hierarchy allowed within the vocabulary. (0 = disabled, 1 = single, 2 = multiple)',
),
- 'module' => array(
- 'type' => 'varchar',
- 'length' => 255,
- 'not null' => TRUE,
- 'default' => '',
- 'description' => 'The module which created the vocabulary.',
- ),
'weight' => array(
'type' => 'int',
'not null' => TRUE,
@@ -246,3 +239,10 @@ function taxonomy_field_schema($field) {
),
);
}
+
+/**
+ * Remove the {taxonomy_vocabulary}.module field.
+ */
+function taxonomy_update_8000() {
+ db_drop_field('taxonomy_vocabulary', 'module');
+}
\ No newline at end of file
diff --git a/modules/taxonomy/taxonomy.module b/modules/taxonomy/taxonomy.module
index eb81870f9983..d7a5e9a1ffbe 100644
--- a/modules/taxonomy/taxonomy.module
+++ b/modules/taxonomy/taxonomy.module
@@ -417,10 +417,6 @@ function taxonomy_vocabulary_save($vocabulary) {
$vocabulary->old_machine_name = $vocabulary->original->machine_name;
}
- if (!isset($vocabulary->module)) {
- $vocabulary->module = 'taxonomy';
- }
-
module_invoke_all('taxonomy_vocabulary_presave', $vocabulary);
module_invoke_all('entity_presave', $vocabulary, 'taxonomy_vocabulary');
diff --git a/modules/toolbar/toolbar-rtl.css b/modules/toolbar/toolbar-rtl.css
index acbc98f82873..e12154724a70 100644
--- a/modules/toolbar/toolbar-rtl.css
+++ b/modules/toolbar/toolbar-rtl.css
@@ -35,7 +35,3 @@
left: 10px;
right: auto;
}
-* html #toolbar {
- left: 0;
- padding-left: 0;
-}
diff --git a/modules/toolbar/toolbar.css b/modules/toolbar/toolbar.css
index cbf3c14c8fe3..4b62cded060e 100644
--- a/modules/toolbar/toolbar.css
+++ b/modules/toolbar/toolbar.css
@@ -133,18 +133,3 @@ body.toolbar-drawer {
position: relative;
padding: 0 10px;
}
-
-/**
- * IE 6 Fix.
- *
- * IE 6 shows elements with position:fixed as position:static so we replace
- * it with position:absolute; toolbar needs its z-index to stay above overlay.
- */
-* html #toolbar {
- left: -20px;
- margin: 0;
- padding-right: 0;
- position: absolute;
- right: 0;
- width: 100%;
-}
diff --git a/modules/toolbar/toolbar.module b/modules/toolbar/toolbar.module
index 61ae648ad5f1..d25ca2d0d843 100644
--- a/modules/toolbar/toolbar.module
+++ b/modules/toolbar/toolbar.module
@@ -263,10 +263,11 @@ function toolbar_view() {
'toolbar-drawer',
'clearfix',
);
- if(_toolbar_is_collapsed()) {
+ if (_toolbar_is_collapsed()) {
$toolbar_drawer_classes[] = 'collapsed';
}
- $build['toolbar_drawer_classes'] = implode(' ', $toolbar_drawer_classes);
+ $build['toolbar_drawer']['#type'] = 'container';
+ $build['toolbar_drawer']['#attributes']['class'] = $toolbar_drawer_classes;
return $build;
}
diff --git a/modules/toolbar/toolbar.tpl.php b/modules/toolbar/toolbar.tpl.php
index 1df0cf03bf23..342fa603f1bc 100644
--- a/modules/toolbar/toolbar.tpl.php
+++ b/modules/toolbar/toolbar.tpl.php
@@ -31,7 +31,5 @@
-
-
-
+
diff --git a/modules/update/tests/update_test.module b/modules/update/tests/update_test.module
index 4e32d336afea..4acb6ef837e6 100644
--- a/modules/update/tests/update_test.module
+++ b/modules/update/tests/update_test.module
@@ -12,6 +12,12 @@ function update_test_menu() {
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
+ $items['503-error'] = array(
+ 'title' => t('503 Service unavailable'),
+ 'page callback' => 'update_callback_service_unavailable',
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ );
return $items;
}
@@ -148,3 +154,11 @@ class UpdateTestFileTransfer {
return $form;
}
}
+
+/**
+ * Return an Error 503 (Service unavailable) page.
+ */
+function update_callback_service_unavailable() {
+ drupal_add_http_header('Status', '503 Service unavailable');
+ print "503 Service Temporarily Unavailable";
+}
diff --git a/modules/update/update.fetch.inc b/modules/update/update.fetch.inc
index ff69cbb114d6..7ac0dbefbc2b 100644
--- a/modules/update/update.fetch.inc
+++ b/modules/update/update.fetch.inc
@@ -143,7 +143,7 @@ function _update_process_fetch_task($project) {
if (empty($fail[$fetch_url_base]) || $fail[$fetch_url_base] < $max_fetch_attempts) {
$xml = drupal_http_request($url);
- if (isset($xml->data)) {
+ if (!isset($xml->error) && isset($xml->data)) {
$data = $xml->data;
}
}
diff --git a/modules/update/update.test b/modules/update/update.test
index 4fb8630d76c9..a657f91de0da 100644
--- a/modules/update/update.test
+++ b/modules/update/update.test
@@ -13,7 +13,7 @@
* dummy .info file data (specified via hook_system_info_alter() in the
* update_test helper module) describing what's currently installed. Each
* test case defines a set of projects to install, their current state (via
- * the 'update_test_system_info' variable) and the desired availabile update
+ * the 'update_test_system_info' variable) and the desired available update
* data (via the 'update_test_xml_map' variable), and then performs a series
* of assertions that the report matches our expectations given the specific
* initial state and availability scenario.
@@ -32,9 +32,9 @@ class UpdateTestHelper extends DrupalWebTestCase {
*
* @see update_test_mock_page()
*/
- protected function refreshUpdateStatus($xml_map) {
+ protected function refreshUpdateStatus($xml_map, $url = 'update-test') {
// Tell update module to fetch from the URL provided by update_test module.
- variable_set('update_fetch_url', url('update-test', array('absolute' => TRUE)));
+ variable_set('update_fetch_url', url($url, array('absolute' => TRUE)));
// Save the map for update_test_mock_page() to use.
variable_set('update_test_xml_map', $xml_map);
// Manually check the update status.
@@ -215,6 +215,16 @@ class UpdateCoreTestCase extends UpdateTestHelper {
$this->assertNoText(t('There is a security update available for your version of Drupal.'));
}
+ /**
+ * Tests the update module when the update server returns 503 (Service unavailable) errors.
+ */
+ function testServiceUnavailable() {
+ $this->refreshUpdateStatus(array(), '503-error');
+ // Ensure that no "Warning: SimpleXMLElement..." parse errors are found.
+ $this->assertNoText('SimpleXMLElement');
+ $this->assertUniqueText(t('Failed to get available update data for one project.'));
+ }
+
protected function setSystemInfo7_0() {
$setting = array(
'#all' => array(
diff --git a/modules/user/user-profile-category.tpl.php b/modules/user/user-profile-category.tpl.php
index 3e295826c495..8b6cd9991a6b 100644
--- a/modules/user/user-profile-category.tpl.php
+++ b/modules/user/user-profile-category.tpl.php
@@ -25,7 +25,7 @@
*/
?>
-
+
diff --git a/modules/user/user-profile.tpl.php b/modules/user/user-profile.tpl.php
index 50e611117363..b6094294b1dc 100644
--- a/modules/user/user-profile.tpl.php
+++ b/modules/user/user-profile.tpl.php
@@ -18,11 +18,11 @@
* Available variables:
* - $user_profile: An array of profile items. Use render() to print them.
* - Field variables: for each field instance attached to the user a
- * corresponding variable is defined; e.g., $user->field_example has a
+ * corresponding variable is defined; e.g., $account->field_example has a
* variable $field_example defined. When needing to access a field's raw
* values, developers/themers are strongly encouraged to use these
* variables. Otherwise they will have to explicitly specify the desired
- * field language, e.g. $user->field_example['en'], thus overriding any
+ * field language, e.g. $account->field_example['en'], thus overriding any
* language negotiation rule that was previously applied.
*
* @see user-profile-category.tpl.php
diff --git a/modules/user/user.entity.inc b/modules/user/user.entity.inc
new file mode 100644
index 000000000000..5549c7707188
--- /dev/null
+++ b/modules/user/user.entity.inc
@@ -0,0 +1,52 @@
+ $record) {
+ $picture_fids[] = $record->picture;
+ $queried_users[$key]->data = unserialize($record->data);
+ $queried_users[$key]->roles = array();
+ if ($record->uid) {
+ $queried_users[$record->uid]->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
+ }
+ else {
+ $queried_users[$record->uid]->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user';
+ }
+ }
+
+ // Add any additional roles from the database.
+ $result = db_query('SELECT r.rid, r.name, ur.uid FROM {role} r INNER JOIN {users_roles} ur ON ur.rid = r.rid WHERE ur.uid IN (:uids)', array(':uids' => array_keys($queried_users)));
+ foreach ($result as $record) {
+ $queried_users[$record->uid]->roles[$record->rid] = $record->name;
+ }
+
+ // Add the full file objects for user pictures if enabled.
+ if (!empty($picture_fids) && variable_get('user_pictures', 1) == 1) {
+ $pictures = file_load_multiple($picture_fids);
+ foreach ($queried_users as $account) {
+ if (!empty($account->picture) && isset($pictures[$account->picture])) {
+ $account->picture = $pictures[$account->picture];
+ }
+ else {
+ $account->picture = NULL;
+ }
+ }
+ }
+ // Call the default attachLoad() method. This will add fields and call
+ // hook_user_load().
+ parent::attachLoad($queried_users, $revision_id);
+ }
+}
diff --git a/modules/user/user.info b/modules/user/user.info
index a4d18d634085..d887352760e4 100644
--- a/modules/user/user.info
+++ b/modules/user/user.info
@@ -3,7 +3,7 @@ description = Manages the user registration and login system.
package = Core
version = VERSION
core = 8.x
-files[] = user.module
+files[] = user.entity.inc
files[] = user.test
required = TRUE
configure = admin/config/people
diff --git a/modules/user/user.module b/modules/user/user.module
index db0591fd7b1c..47942584e9a8 100644
--- a/modules/user/user.module
+++ b/modules/user/user.module
@@ -287,53 +287,6 @@ function user_load_multiple($uids = array(), $conditions = array(), $reset = FAL
return entity_load('user', $uids, $conditions, $reset);
}
-/**
- * Controller class for users.
- *
- * This extends the DrupalDefaultEntityController class, adding required
- * special handling for user objects.
- */
-class UserController extends DrupalDefaultEntityController {
-
- function attachLoad(&$queried_users, $revision_id = FALSE) {
- // Build an array of user picture IDs so that these can be fetched later.
- $picture_fids = array();
- foreach ($queried_users as $key => $record) {
- $picture_fids[] = $record->picture;
- $queried_users[$key]->data = unserialize($record->data);
- $queried_users[$key]->roles = array();
- if ($record->uid) {
- $queried_users[$record->uid]->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
- }
- else {
- $queried_users[$record->uid]->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user';
- }
- }
-
- // Add any additional roles from the database.
- $result = db_query('SELECT r.rid, r.name, ur.uid FROM {role} r INNER JOIN {users_roles} ur ON ur.rid = r.rid WHERE ur.uid IN (:uids)', array(':uids' => array_keys($queried_users)));
- foreach ($result as $record) {
- $queried_users[$record->uid]->roles[$record->rid] = $record->name;
- }
-
- // Add the full file objects for user pictures if enabled.
- if (!empty($picture_fids) && variable_get('user_pictures', 1) == 1) {
- $pictures = file_load_multiple($picture_fids);
- foreach ($queried_users as $account) {
- if (!empty($account->picture) && isset($pictures[$account->picture])) {
- $account->picture = $pictures[$account->picture];
- }
- else {
- $account->picture = NULL;
- }
- }
- }
- // Call the default attachLoad() method. This will add fields and call
- // hook_user_load().
- parent::attachLoad($queried_users, $revision_id);
- }
-}
-
/**
* Loads a user object.
*
@@ -1065,6 +1018,7 @@ function user_account_form(&$form, &$form_state) {
'#access' => !empty($protected_values),
'#description' => $current_pass_description,
'#weight' => -5,
+ '#attributes' => array('autocomplete' => 'off'),
);
$form['#validate'][] = 'user_validate_current_pass';
}
diff --git a/modules/user/user.pages.inc b/modules/user/user.pages.inc
index 09bf33b6bfd7..02870e9e65a8 100644
--- a/modules/user/user.pages.inc
+++ b/modules/user/user.pages.inc
@@ -76,9 +76,11 @@ function user_pass_submit($form, &$form_state) {
$account = $form_state['values']['account'];
// Mail one time login URL and instructions using current language.
- _user_mail_notify('password_reset', $account, $language);
- watchdog('user', 'Password reset instructions mailed to %name at %email.', array('%name' => $account->name, '%email' => $account->mail));
- drupal_set_message(t('Further instructions have been sent to your e-mail address.'));
+ $mail = _user_mail_notify('password_reset', $account, $language);
+ if (!empty($mail)) {
+ watchdog('user', 'Password reset instructions mailed to %name at %email.', array('%name' => $account->name, '%email' => $account->mail));
+ drupal_set_message(t('Further instructions have been sent to your e-mail address.'));
+ }
$form_state['redirect'] = 'user';
return;
diff --git a/scripts/dump-database-d7.sh b/scripts/dump-database-d7.sh
new file mode 100644
index 000000000000..f78f998e50c5
--- /dev/null
+++ b/scripts/dump-database-d7.sh
@@ -0,0 +1,90 @@
+#!/usr/bin/env php
+ $data) {
+ // Remove descriptions to save time and code.
+ unset($data['description']);
+ foreach ($data['fields'] as &$field) {
+ unset($field['description']);
+ }
+
+ // Dump the table structure.
+ $output .= "db_create_table('" . $table . "', " . drupal_var_export($data) . ");\n";
+
+ // Don't output values for those tables.
+ if (substr($table, 0, 5) == 'cache' || $table == 'sessions' || $table == 'watchdog') {
+ $output .= "\n";
+ continue;
+ }
+
+ // Prepare the export of values.
+ $result = db_query('SELECT * FROM {'. $table .'}', array(), array('fetch' => PDO::FETCH_ASSOC));
+ $insert = '';
+ foreach ($result as $record) {
+ $insert .= '->values('. drupal_var_export($record) .")\n";
+ }
+
+ // Dump the values if there are some.
+ if ($insert) {
+ $output .= "db_insert('". $table . "')->fields(". drupal_var_export(array_keys($data['fields'])) .")\n";
+ $output .= $insert;
+ $output .= "->execute();\n";
+ }
+
+ $output .= "\n";
+}
+
+print $output;
diff --git a/scripts/generate-d7-content.sh b/scripts/generate-d7-content.sh
new file mode 100644
index 000000000000..07b715e922b4
--- /dev/null
+++ b/scripts/generate-d7-content.sh
@@ -0,0 +1,308 @@
+#!/usr/bin/env php
+fields(array('uid', 'name', 'pass', 'mail', 'status', 'created', 'access'));
+for ($i = 0; $i < 6; $i++) {
+ $name = "test user $i";
+ $pass = md5("test PassW0rd $i !(.)");
+ $mail = "test$i@example.com";
+ $now = mktime(0, 0, 0, 1, $i + 1, 2010);
+ $query->values(array(db_next_id(), $name, user_hash_password($pass), $mail, 1, $now, $now));
+}
+$query->execute();
+
+// Create vocabularies and terms
+
+$terms = array();
+
+// All possible combinations of these vocabulary properties.
+$hierarchy = array(0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2);
+$multiple = array(0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1);
+$required = array(0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1);
+
+$voc_id = 0;
+$term_id = 0;
+for ($i = 0; $i < 24; $i++) {
+ $vocabulary = new stdClass;
+ ++$voc_id;
+ $vocabulary->name = "vocabulary $voc_id (i=$i)";
+ $vocabulary->machine_name = 'vocabulary_' . $voc_id . '_' . $i;
+ $vocabulary->description = "description of ". $vocabulary->name;
+ $vocabulary->multiple = $multiple[$i % 12];
+ $vocabulary->required = $required[$i % 12];
+ $vocabulary->relations = 1;
+ $vocabulary->hierarchy = $hierarchy[$i % 12];
+ $vocabulary->weight = $i;
+ taxonomy_vocabulary_save($vocabulary);
+ $field = array(
+ 'field_name' => 'taxonomy_'. $vocabulary->machine_name,
+ 'module' => 'taxonomy',
+ 'type' => 'taxonomy_term_reference',
+ 'cardinality' => $vocabulary->multiple || $vocabulary->tags ? FIELD_CARDINALITY_UNLIMITED : 1,
+ 'settings' => array(
+ 'required' => $vocabulary->required ? TRUE : FALSE,
+ 'allowed_values' => array(
+ array(
+ 'vocabulary' => $vocabulary->machine_name,
+ 'parent' => 0,
+ ),
+ ),
+ ),
+ );
+ field_create_field($field);
+ $node_types = $i > 11 ? array('page') : array_keys(node_type_get_types());
+ foreach ($node_types as $bundle) {
+ $instance = array(
+ 'label' => $vocabulary->name,
+ 'field_name' => $field['field_name'],
+ 'bundle' => $bundle,
+ 'entity_type' => 'node',
+ 'settings' => array(),
+ 'description' => $vocabulary->help,
+ 'required' => $vocabulary->required,
+ 'widget' => array(),
+ 'display' => array(
+ 'default' => array(
+ 'type' => 'taxonomy_term_reference_link',
+ 'weight' => 10,
+ ),
+ 'teaser' => array(
+ 'type' => 'taxonomy_term_reference_link',
+ 'weight' => 10,
+ ),
+ ),
+ );
+ if ($vocabulary->tags) {
+ $instance['widget'] = array(
+ 'type' => 'taxonomy_autocomplete',
+ 'module' => 'taxonomy',
+ 'settings' => array(
+ 'size' => 60,
+ 'autocomplete_path' => 'taxonomy/autocomplete',
+ ),
+ );
+ }
+ else {
+ $instance['widget'] = array(
+ 'type' => 'select',
+ 'module' => 'options',
+ 'settings' => array(),
+ );
+ }
+ field_create_instance($instance);
+ }
+ $parents = array();
+ // Vocabularies without hierarcy get one term, single parent vocabularies get
+ // one parent and one child term. Multiple parent vocabularies get three
+ // terms: t0, t1, t2 where t0 is a parent of both t1 and t2.
+ for ($j = 0; $j < $vocabulary->hierarchy + 1; $j++) {
+ $term = new stdClass;
+ $term->vocabulary_machine_name = $vocabulary->machine_name;
+ // For multiple parent vocabularies, omit the t0-t1 relation, otherwise
+ // every parent in the vocabulary is a parent.
+ $term->parent = $vocabulary->hierarchy == 2 && i == 1 ? array() : $parents;
+ ++$term_id;
+ $term->name = "term $term_id of vocabulary $voc_id (j=$j)";
+ $term->description = 'description of ' . $term->name;
+ $term->format = 'filtered_html';
+ $term->weight = $i * 3 + $j;
+ taxonomy_term_save($term);
+ $terms[] = $term->tid;
+ $term_vocabs[$term->tid] = 'taxonomy_' . $vocabulary->machine_name;
+ $parents[] = $term->tid;
+ }
+}
+$node_id = 0;
+$revision_id = 0;
+module_load_include('inc', 'node', 'node.pages');
+for ($i = 0; $i < 24; $i++) {
+ $uid = intval($i / 8) + 3;
+ $user = user_load($uid);
+ $node = new stdClass();
+ $node->uid = $uid;
+ $node->type = $i < 12 ? 'page' : 'story';
+ $node->sticky = 0;
+ ++$node_id;
+ ++$revision_id;
+ $node->title = "node title $node_id rev $revision_id (i=$i)";
+ $node->language = LANGUAGE_NONE;
+ $body_text = str_repeat("node body ($node->type) - $i", 100);
+ $node->body[$node->language][0]['value'] = $body_text;
+ $node->body[$node->language][0]['summary'] = text_summary($body_text);
+ $node->body[$node->language][0]['format'] = 'filtered_html';
+ $node->status = intval($i / 4) % 2;
+ $node->revision = $i < 12;
+ $node->promote = $i % 2;
+ $node->created = $now + $i * 86400;
+ $node->log = "added $i node";
+ // Make every term association different a little. For nodes with revisions,
+ // make the initial revision have a different set of terms than the
+ // newest revision.
+ $items = array();
+ if ($node->revision) {
+ $node_terms = array($terms[$i], $terms[47-$i]);
+ }
+ else {
+ $node_terms = $terms;
+ unset($node_terms[$i], $node_terms[47 - $i]);
+ }
+ foreach ($node_terms as $tid) {
+ $field_name = $term_vocabs[$tid];
+ $node->{$field_name}[LANGUAGE_NONE][] = array('tid' => $tid);
+ }
+ $node->path = array('alias' => "content/$node->created");
+ node_save($node);
+ if ($node->revision) {
+ $user = user_load($uid + 3);
+ ++$revision_id;
+ $node->title .= " rev2 $revision_id";
+ $body_text = str_repeat("node revision body ($node->type) - $i", 100);
+ $node->body[$node->language][0]['value'] = $body_text;
+ $node->body[$node->language][0]['summary'] = text_summary($body_text);
+ $node->body[$node->language][0]['format'] = 'filtered_html';
+ $node->log = "added $i revision";
+ $node_terms = $terms;
+ unset($node_terms[$i], $node_terms[47 - $i]);
+ foreach ($node_terms as $tid) {
+ $field_name = $term_vocabs[$tid];
+ $node->{$field_name}[LANGUAGE_NONE][] = array('tid' => $tid);
+ }
+ node_save($node);
+ }
+}
+
+// Create poll content
+for ($i = 0; $i < 12; $i++) {
+ $uid = intval($i / 4) + 3;
+ $user = user_load($uid);
+ $node = new stdClass();
+ $node->uid = $uid;
+ $node->type = 'poll';
+ $node->sticky = 0;
+ $node->title = "poll title $i";
+ $node->language = LANGUAGE_NONE;
+ $node->status = intval($i / 2) % 2;
+ $node->revision = 1;
+ $node->promote = $i % 2;
+ $node->created = REQUEST_TIME + $i * 43200;
+ $node->runtime = 0;
+ $node->active = 1;
+ $node->log = "added $i poll";
+ $node->path = array('alias' => "content/poll/$i");
+
+ $nbchoices = ($i % 4) + 2;
+ for ($c = 0; $c < $nbchoices; $c++) {
+ $node->choice[] = array('chtext' => "Choice $c for poll $i", 'chvotes' => 0, 'weight' => 0);
+ }
+ node_save($node);
+ $path = array(
+ 'alias' => "content/poll/$i/results",
+ 'source' => "node/$node->nid/results",
+ );
+ path_save($path);
+
+ // Add some votes
+ $node = node_load($node->nid);
+ $choices = array_keys($node->choice);
+ $original_user = $GLOBALS['user'];
+ for ($v = 0; $v < ($i % 4); $v++) {
+ drupal_static_reset('ip_address');
+ $_SERVER['REMOTE_ADDR'] = "127.0.$v.1";
+ $GLOBALS['user'] = drupal_anonymous_user();// We should have already allowed anon to vote.
+ $c = $v % $nbchoices;
+ $form_state = array();
+ $form_state['values']['choice'] = $choices[$c];
+ $form_state['values']['op'] = t('Vote');
+ drupal_form_submit('poll_view_voting', $form_state, $node);
+ }
+}
+
+$uid = 6;
+$node_type = 'broken';
+$user = user_load($uid);
+$node = new stdClass();
+$node->uid = $uid;
+$node->type = 'article';
+$body_text = str_repeat("node body ($node_type) - 37", 100);
+$node->sticky = 0;
+$node->title = "node title 24";
+$node->language = LANGUAGE_NONE;
+$node->body[$node->language][0]['value'] = $body_text;
+$node->body[$node->language][0]['summary'] = text_summary($body_text);
+$node->body[$node->language][0]['format'] = 'filtered_html';
+$node->status = 1;
+$node->revision = 0;
+$node->promote = 0;
+$node->created = 1263769200;
+$node->log = "added a broken node";
+$node->path = array('alias' => "content/1263769200");
+node_save($node);
+db_update('node')
+ ->fields(array(
+ 'type' => $node_type,
+ ))
+ ->condition('nid', $node->nid)
+ ->execute();
+db_update('field_data_body')
+ ->fields(array(
+ 'bundle' => $node_type,
+ ))
+ ->condition('entity_id', $node->nid)
+ ->condition('entity_type', 'node')
+ ->execute();
+db_update('field_revision_body')
+ ->fields(array(
+ 'bundle' => $node_type,
+ ))
+ ->condition('entity_id', $node->nid)
+ ->condition('entity_type', 'node')
+ ->execute();
+db_update('field_config_instance')
+ ->fields(array(
+ 'bundle' => $node_type,
+ ))
+ ->condition('bundle', 'article')
+ ->execute();
diff --git a/scripts/run-tests.sh b/scripts/run-tests.sh
index dc4f2600fd38..63c02af73c5a 100755
--- a/scripts/run-tests.sh
+++ b/scripts/run-tests.sh
@@ -67,6 +67,8 @@ if ($args['list']) {
exit;
}
+$test_list = simpletest_script_get_test_list();
+
// Try to allocate unlimited time to run the tests.
drupal_set_time_limit(0);
@@ -485,12 +487,13 @@ function simpletest_script_reporter_init() {
echo "\n";
}
- echo "Test run started: " . format_date($_SERVER['REQUEST_TIME'], 'long') . "\n";
+ echo "Test run started:\n";
+ echo " " . format_date($_SERVER['REQUEST_TIME'], 'long') . "\n";
timer_start('run-tests');
echo "\n";
- echo "Test summary:\n";
- echo "-------------\n";
+ echo "Test summary\n";
+ echo "------------\n";
echo "\n";
}
@@ -571,7 +574,7 @@ function simpletest_script_reporter_timer_stop() {
echo "\n";
$end = timer_stop('run-tests');
echo "Test run duration: " . format_interval($end['time'] / 1000);
- echo "\n";
+ echo "\n\n";
}
/**
diff --git a/sites/default/default.settings.php b/sites/default/default.settings.php
index d5f8843b103b..ce8268253999 100755
--- a/sites/default/default.settings.php
+++ b/sites/default/default.settings.php
@@ -22,19 +22,19 @@
* http://www.drupal.org/mysite/test/, the 'settings.php'
* is searched in the following directories:
*
- * 1. sites/www.drupal.org.mysite.test
- * 2. sites/drupal.org.mysite.test
- * 3. sites/org.mysite.test
+ * - sites/www.drupal.org.mysite.test
+ * - sites/drupal.org.mysite.test
+ * - sites/org.mysite.test
*
- * 4. sites/www.drupal.org.mysite
- * 5. sites/drupal.org.mysite
- * 6. sites/org.mysite
+ * - sites/www.drupal.org.mysite
+ * - sites/drupal.org.mysite
+ * - sites/org.mysite
*
- * 7. sites/www.drupal.org
- * 8. sites/drupal.org
- * 9. sites/org
+ * - sites/www.drupal.org
+ * - sites/drupal.org
+ * - sites/org
*
- * 10. sites/default
+ * - sites/default
*
* If you are installing on a non-standard port number, prefix the
* hostname with that number. For example,
@@ -443,6 +443,42 @@ ini_set('session.cookie_lifetime', 2000000);
# 'a.b.c.d',
# );
+/**
+ * Fast 404 pages:
+ *
+ * Drupal can generate fully themed 404 pages. However, some of these responses
+ * are for images or other resource files that are not displayed to the user.
+ * This can waste bandwidth, and also generate server load.
+ *
+ * The options below return a simple, fast 404 page for URLs matching a
+ * specific pattern:
+ * - 404_fast_paths_exclude: A regular expression to match paths to exclude,
+ * such as images generated by image styles, or dynamically-resized images.
+ * If you need to add more paths, you can add '|path' to the expression.
+ * - 404_fast_paths: A regular expression to match paths that should return a
+ * simple 404 page, rather than the fully themed 404 page. If you don't have
+ * any aliases ending in htm or html you can add '|s?html?' to the expression.
+ * - 404_fast_html: The html to return for simple 404 pages.
+ *
+ * Add leading hash signs if you would like to disable this functionality.
+ */
+$conf['404_fast_paths_exclude'] = '/\/(?:styles)\//';
+$conf['404_fast_paths'] = '/\.(?:txt|png|gif|jpe?g|css|js|ico|swf|flv|cgi|bat|pl|dll|exe|asp)$/i';
+$conf['404_fast_html'] = '404 Not FoundNot Found
The requested URL "@path" was not found on this server.
';
+
+/**
+ * By default, fast 404s are returned as part of the normal page request
+ * process, which will properly serve valid pages that happen to match and will
+ * also log actual 404s to the Drupal log. Alternatively you can choose to
+ * return a 404 now by uncommenting the following line. This will reduce server
+ * load, but will cause even valid pages that happen to match the pattern to
+ * return 404s, rather than the actual page. It will also prevent the Drupal
+ * system log entry. Ensure you understand the effects of this before enabling.
+ *
+ * To enable this functionality, remove the leading hash sign below.
+ */
+# drupal_fast_404();
+
/**
* Authorized file system operations:
*
diff --git a/themes/bartik/css/ie6.css b/themes/bartik/css/ie6.css
deleted file mode 100644
index 435fab02bd8d..000000000000
--- a/themes/bartik/css/ie6.css
+++ /dev/null
@@ -1,18 +0,0 @@
-
-#content {
- overflow: hidden;
-}
-.form-item-search-block-form {
- width: 50%;
-}
-.tabs ul.primary,
-.region-header .block-menu li a,
-.comment-form .form-item {
- zoom: 1;
-}
-#block-search-form .form-item-search-block-form input {
- width: 67%;
-}
-.node-teaser {
- border-bottom: 1px solid #d3d7d9;
-}
diff --git a/themes/bartik/css/print.css b/themes/bartik/css/print.css
index 61ca3fa7d333..fbe386a43cc8 100644
--- a/themes/bartik/css/print.css
+++ b/themes/bartik/css/print.css
@@ -41,6 +41,6 @@ body {
#comments .title,
#comments form,
-.comment_forbidden {
+.comment-forbidden {
display: none;
}
diff --git a/themes/bartik/template.php b/themes/bartik/template.php
index d523a0abab57..8d2b8c4e316c 100644
--- a/themes/bartik/template.php
+++ b/themes/bartik/template.php
@@ -23,7 +23,6 @@ function bartik_preprocess_html(&$variables) {
// Add conditional stylesheets for IE
drupal_add_css(path_to_theme() . '/css/ie.css', array('group' => CSS_THEME, 'browsers' => array('IE' => 'lte IE 7', '!IE' => FALSE), 'preprocess' => FALSE));
- drupal_add_css(path_to_theme() . '/css/ie6.css', array('group' => CSS_THEME, 'browsers' => array('IE' => 'IE 6', '!IE' => FALSE), 'preprocess' => FALSE));
}
/**
diff --git a/themes/garland/comment.tpl.php b/themes/garland/comment.tpl.php
index 900afc106535..952cc0170325 100644
--- a/themes/garland/comment.tpl.php
+++ b/themes/garland/comment.tpl.php
@@ -6,7 +6,7 @@
-
+
diff --git a/themes/garland/fix-ie-rtl.css b/themes/garland/fix-ie-rtl.css
deleted file mode 100644
index fb1901b37eba..000000000000
--- a/themes/garland/fix-ie-rtl.css
+++ /dev/null
@@ -1,61 +0,0 @@
-
-body {
- /* Center layout */
- text-align: center;
-}
-
-#squeeze {
- zoom: 1;
- direction: ltr;
-}
-
-#squeeze .left-corner {
- direction: rtl
-}
-
-#header-region,
-#wrapper #container {
- /* Reset text alignment */
- text-align: right;
-}
-
-#wrapper #container #center {
- /* Reduce amount of damage done by extremely wide content */
- overflow: hidden;
-}
-
-#wrapper #container #center .right-corner .left-corner {
- /* Because of the lack of min-height, we use height as an alternative */
- height: 400px;
-}
-
-fieldset {
- /* Don't draw backgrounds on fieldsets in IE, as they look really bad. */
- background: none;
-}
-
-/* Prevent fieldsets from shifting when changing collapsed state. */
-html.js fieldset.collapsible {
- top: -1em;
-}
-
-html.js fieldset.collapsed {
- top: 0;
- margin-bottom: 1em;
-}
-
-tr.menu-disabled {
- /* Use filter to emulate CSS3 opacity */
- filter: alpha(opacity=50);
-}
-
-#header-region {
- /* Because of the lack of min-height, we use height as an alternative */
- height: 1em;
-}
-
-#attach-hide label,
-#uploadprogress div.message {
- /* Fading elements in IE causes the text to bleed unless they have a background. */
- background-color: #ffffff;
-}
diff --git a/themes/garland/fix-ie.css b/themes/garland/fix-ie.css
deleted file mode 100644
index 1a1c131bc21f..000000000000
--- a/themes/garland/fix-ie.css
+++ /dev/null
@@ -1,65 +0,0 @@
-
-body {
- /* Center layout */
- text-align: center;
-}
-
-#header-region,
-#wrapper #container {
- /* Reset text alignment */
- text-align: left; /* LTR */
-}
-
-#wrapper #container #center {
- /* Reduce amount of damage done by extremely wide content */
- overflow: hidden;
-}
-
-#wrapper #container #center .right-corner .left-corner {
- /* Because of the lack of min-height, we use height as an alternative */
- height: 400px;
-}
-
-fieldset {
- /* Don't draw backgrounds on fieldsets in IE, as they look really bad. */
- background: none;
-}
-
-div.vertical-tabs ul.vertical-tabs-list li.first {
- background-image: none;
-}
-
-ul.primary {
- /* Fix missing top margin */
- position: relative; /* LTR */
-/* top: 0.5em; */
-}
-
-/* Prevent fieldsets from shifting when changing collapsed state. */
-html.js fieldset.collapsible {
- top: -1em;
-}
-html.js fieldset.collapsed {
- top: 0;
- margin-bottom: 1em;
-}
-
-tr.menu-disabled {
- /* Use filter to emulate CSS3 opacity */
- filter: alpha(opacity=50);
-}
-
-#header-region {
- /* Because of the lack of min-height, we use height as an alternative */
- height: 1em;
-}
-
-tr.taxonomy-term-preview {
- filter: alpha(opacity=50);
-}
-
-#attach-hide label,
-#uploadprogress div.message {
- /* Fading elements in IE causes the text to bleed unless they have a background. */
- background-color: #ffffff;
-}
diff --git a/themes/garland/style.css b/themes/garland/style.css
index 4a5961c61996..079f8a36e821 100644
--- a/themes/garland/style.css
+++ b/themes/garland/style.css
@@ -868,22 +868,22 @@ ul.inline li {
/**
* Aggregator.module
*/
-#aggregator {
+.aggregator {
margin-top: 1em;
}
-#aggregator .feed-item-title {
+.aggregator .feed-item-title {
font-size: 160%;
line-height: 130%;
}
-#aggregator .feed-item {
+.aggregator .feed-item {
border-bottom: 1px solid #e9eff3;
margin: -1.5em -31px 1.75em;
padding: 1.5em 31px;
}
-#aggregator .feed-item-categories {
+.aggregator .feed-item-categories {
font-size: 0.92em;
}
-#aggregator .feed-item-meta {
+.aggregator .feed-item-meta {
font-size: 0.92em;
color: #898989;
}
diff --git a/themes/garland/template.php b/themes/garland/template.php
index 1b144f1210b9..fc09571435f3 100644
--- a/themes/garland/template.php
+++ b/themes/garland/template.php
@@ -40,8 +40,6 @@ function garland_preprocess_html(&$vars) {
if (theme_get_setting('garland_width') == 'fluid') {
$vars['classes_array'][] = 'fluid-width';
}
- // Add conditional CSS for IE6.
- drupal_add_css(path_to_theme() . '/fix-ie.css', array('group' => CSS_THEME, 'browsers' => array('IE' => 'lt IE 7', '!IE' => FALSE), 'preprocess' => FALSE));
}
/**
diff --git a/themes/seven/ie6.css b/themes/seven/ie6.css
deleted file mode 100644
index 7d1c320b84bc..000000000000
--- a/themes/seven/ie6.css
+++ /dev/null
@@ -1,17 +0,0 @@
-
-ul.menu li,
-ul.menu li a,
-ul.links li,
-ul.links li a,
-.action-links,
-#page {
- height: 1%;
-}
-#block-system-main ul.admin-list li a {
- height: 1px;
- position: relative;
- display: block;
-}
-#block-system-main ul.admin-list li div.description a {
- display: inline;
-}
diff --git a/themes/seven/reset.css b/themes/seven/reset.css
index 0bdc29aef80d..78602dea037e 100644
--- a/themes/seven/reset.css
+++ b/themes/seven/reset.css
@@ -205,13 +205,6 @@ ul.inline:after {
display: none;
clear: none;
}
-/* IE6 */
-* html .form-item,
-* html ul.links,
-* html div.admin-panel .body,
-* html .clearfix {
- height: 1%;
-}
/* IE7 */
*:first-child + html .form-item,
*:first-child + html ul.links,
diff --git a/themes/seven/template.php b/themes/seven/template.php
index 437e9a79f006..b78f5ab5f834 100644
--- a/themes/seven/template.php
+++ b/themes/seven/template.php
@@ -20,8 +20,6 @@ function seven_preprocess_html(&$vars) {
drupal_add_css(path_to_theme() . '/ie.css', array('group' => CSS_THEME, 'browsers' => array('IE' => 'lte IE 8', '!IE' => FALSE), 'weight' => 999, 'preprocess' => FALSE));
// Add conditional CSS for IE7 and below.
drupal_add_css(path_to_theme() . '/ie7.css', array('group' => CSS_THEME, 'browsers' => array('IE' => 'lte IE 7', '!IE' => FALSE), 'weight' => 999, 'preprocess' => FALSE));
- // Add conditional CSS for IE6.
- drupal_add_css(path_to_theme() . '/ie6.css', array('group' => CSS_THEME, 'browsers' => array('IE' => 'lte IE 6', '!IE' => FALSE), 'weight' => 999, 'preprocess' => FALSE));
}
/**
diff --git a/themes/seven/vertical-tabs.css b/themes/seven/vertical-tabs.css
index 4f6622251c73..f06aade8135b 100644
--- a/themes/seven/vertical-tabs.css
+++ b/themes/seven/vertical-tabs.css
@@ -87,7 +87,3 @@ div.vertical-tabs .vertical-tabs-panes legend {
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
-* html .vertical-tabs .form-type-textfield,
-* html .vertical-tabs .form-textarea-wrapper {
- width: 95%; /* IE6 */
-}
diff --git a/themes/stark/layout.css b/themes/stark/layout.css
index 43bb93f88856..7e49d74a8f7c 100644
--- a/themes/stark/layout.css
+++ b/themes/stark/layout.css
@@ -10,9 +10,7 @@
* This layout method works reasonably well, but shouldn't be used on a
* production site because it can break. For example, if an over-large image
* (one that is wider than 20% of the viewport) is in the left sidebar, the
- * image will overlap with the #content to the right. The exception to this
- * is IE6 which will just hide the navigation block completely in these
- * instances due to a positioning bug.
+ * image will overlap with the #content to the right.
*/
#content,
diff --git a/update.php b/update.php
index 105bc9d498b3..e1ad8c0e760e 100644
--- a/update.php
+++ b/update.php
@@ -231,7 +231,7 @@ function update_info_page() {
_drupal_flush_css_js();
// Flush the cache of all data for the update status module.
if (db_table_exists('cache_update')) {
- cache_clear_all('*', 'cache_update', TRUE);
+ cache('update')->flush();
}
update_task_list('info');
@@ -345,7 +345,6 @@ require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
require_once DRUPAL_ROOT . '/includes/update.inc';
require_once DRUPAL_ROOT . '/includes/common.inc';
require_once DRUPAL_ROOT . '/includes/file.inc';
-require_once DRUPAL_ROOT . '/includes/entity.inc';
require_once DRUPAL_ROOT . '/includes/unicode.inc';
update_prepare_d8_bootstrap();