{% endif %}
diff --git a/core/modules/system/templates/status-messages.html.twig b/core/modules/system/templates/status-messages.html.twig
index 73bd9b744..6ad802e14 100644
--- a/core/modules/system/templates/status-messages.html.twig
+++ b/core/modules/system/templates/status-messages.html.twig
@@ -15,8 +15,6 @@
* Available variables:
* - message_list: List of messages to be displayed, grouped by type.
* - status_headings: List of all status types.
- * - display: (optional) May have a value of 'status' or 'error' when only
- * displaying messages of that specific type.
* - attributes: HTML attributes for the element, including:
* - class: HTML classes.
*
diff --git a/core/modules/system/tests/fixtures/update/drupal-8.block-context-manager-2354889.php b/core/modules/system/tests/fixtures/update/drupal-8.block-context-manager-2354889.php
index cb3567689..c19c6bb66 100644
--- a/core/modules/system/tests/fixtures/update/drupal-8.block-context-manager-2354889.php
+++ b/core/modules/system/tests/fixtures/update/drupal-8.block-context-manager-2354889.php
@@ -47,7 +47,7 @@ $existing_blocks = unserialize($existing_blocks);
$connection->update('key_value')
->fields([
- 'value' => serialize(array_merge($existing_blocks, ['block.block.testfor2354889', 'block.block.secondtestfor2354889', 'block.block.thirdtestfor2354889']))
+ 'value' => serialize(array_merge($existing_blocks, ['block.block.testfor2354889', 'block.block.secondtestfor2354889', 'block.block.thirdtestfor2354889'])),
])
->condition('collection', 'config.entity.key_store.block')
->condition('name', 'theme:bartik')
diff --git a/core/modules/system/tests/fixtures/update/drupal-8.language-enabled.php b/core/modules/system/tests/fixtures/update/drupal-8.language-enabled.php
index 05bcf8e98..477d95e2c 100644
Binary files a/core/modules/system/tests/fixtures/update/drupal-8.language-enabled.php and b/core/modules/system/tests/fixtures/update/drupal-8.language-enabled.php differ
diff --git a/core/modules/system/tests/fixtures/update/drupal-8.local-actions-tasks-into-blocks-507488.php b/core/modules/system/tests/fixtures/update/drupal-8.local-actions-tasks-into-blocks-507488.php
index 6cf44b174..0fa97e44f 100644
--- a/core/modules/system/tests/fixtures/update/drupal-8.local-actions-tasks-into-blocks-507488.php
+++ b/core/modules/system/tests/fixtures/update/drupal-8.local-actions-tasks-into-blocks-507488.php
@@ -40,7 +40,7 @@ $existing_blocks = unserialize($existing_blocks);
$connection->update('key_value')
->fields([
- 'value' => serialize(array_merge($existing_blocks, ['block.block.seven_local_actions']))
+ 'value' => serialize(array_merge($existing_blocks, ['block.block.seven_local_actions'])),
])
->condition('collection', 'config.entity.key_store.block')
->condition('name', 'theme:seven')
@@ -55,7 +55,7 @@ $extensions = $connection->select('config')
$extensions = unserialize($extensions);
$connection->update('config')
->fields([
- 'data' => serialize(array_merge_recursive($extensions, ['theme' => ['test_theme' => 0]]))
+ 'data' => serialize(array_merge_recursive($extensions, ['theme' => ['test_theme' => 0]])),
])
->condition('name', 'core.extension')
->execute();
diff --git a/core/modules/system/tests/fixtures/update/drupal-8.page-title-into-block-2476947.php b/core/modules/system/tests/fixtures/update/drupal-8.page-title-into-block-2476947.php
index 766e9a603..b51dbe8a0 100644
--- a/core/modules/system/tests/fixtures/update/drupal-8.page-title-into-block-2476947.php
+++ b/core/modules/system/tests/fixtures/update/drupal-8.page-title-into-block-2476947.php
@@ -40,7 +40,7 @@ $existing_blocks = unserialize($existing_blocks);
$connection->update('key_value')
->fields([
- 'value' => serialize(array_merge($existing_blocks, ['block.block.bartik_page_title']))
+ 'value' => serialize(array_merge($existing_blocks, ['block.block.bartik_page_title'])),
])
->condition('collection', 'config.entity.key_store.block')
->condition('name', 'theme:bartik')
@@ -55,7 +55,7 @@ $extensions = $connection->select('config')
$extensions = unserialize($extensions);
$connection->update('config')
->fields([
- 'data' => serialize(array_merge_recursive($extensions, ['theme' => ['test_theme' => 0]]))
+ 'data' => serialize(array_merge_recursive($extensions, ['theme' => ['test_theme' => 0]])),
])
->condition('name', 'core.extension')
->execute();
diff --git a/core/modules/system/tests/fixtures/update/drupal-8.seven-secondary-local-tasks-block-2569529.php b/core/modules/system/tests/fixtures/update/drupal-8.seven-secondary-local-tasks-block-2569529.php
index 63582dc38..97c1b7bd9 100644
--- a/core/modules/system/tests/fixtures/update/drupal-8.seven-secondary-local-tasks-block-2569529.php
+++ b/core/modules/system/tests/fixtures/update/drupal-8.seven-secondary-local-tasks-block-2569529.php
@@ -40,7 +40,7 @@ $existing_blocks = unserialize($existing_blocks);
$connection->update('key_value')
->fields([
- 'value' => serialize(array_merge($existing_blocks, ['block.block.seven_secondary_local_tasks']))
+ 'value' => serialize(array_merge($existing_blocks, ['block.block.seven_secondary_local_tasks'])),
])
->condition('collection', 'config.entity.key_store.block')
->condition('name', 'theme:seven')
diff --git a/core/modules/system/tests/fixtures/update/drupal-8.site-branding-into-block-2005546.php b/core/modules/system/tests/fixtures/update/drupal-8.site-branding-into-block-2005546.php
index cd593723c..050dd3827 100644
--- a/core/modules/system/tests/fixtures/update/drupal-8.site-branding-into-block-2005546.php
+++ b/core/modules/system/tests/fixtures/update/drupal-8.site-branding-into-block-2005546.php
@@ -40,7 +40,7 @@ $existing_blocks = unserialize($existing_blocks);
$connection->update('key_value')
->fields([
- 'value' => serialize(array_merge($existing_blocks, ['block.block.bartik_branding']))
+ 'value' => serialize(array_merge($existing_blocks, ['block.block.bartik_branding'])),
])
->condition('collection', 'config.entity.key_store.block')
->condition('name', 'theme:bartik')
@@ -55,7 +55,7 @@ $extensions = $connection->select('config')
$extensions = unserialize($extensions);
$connection->update('config')
->fields([
- 'data' => serialize(array_merge_recursive($extensions, ['theme' => ['test_theme' => 0]]))
+ 'data' => serialize(array_merge_recursive($extensions, ['theme' => ['test_theme' => 0]])),
])
->condition('name', 'core.extension')
->execute();
diff --git a/core/modules/system/tests/fixtures/update/drupal-8.stable-base-theme-2575421.php b/core/modules/system/tests/fixtures/update/drupal-8.stable-base-theme-2575421.php
index 7b658b417..537ecf1ba 100644
--- a/core/modules/system/tests/fixtures/update/drupal-8.stable-base-theme-2575421.php
+++ b/core/modules/system/tests/fixtures/update/drupal-8.stable-base-theme-2575421.php
@@ -19,7 +19,7 @@ $extensions = $connection->select('config')
$extensions = unserialize($extensions);
$connection->update('config')
->fields([
- 'data' => serialize(array_merge_recursive($extensions, ['theme' => ['test_stable' => 0]]))
+ 'data' => serialize(array_merge_recursive($extensions, ['theme' => ['test_stable' => 0]])),
])
->condition('name', 'core.extension')
->execute();
diff --git a/core/modules/system/tests/fixtures/update/drupal-8.update-test-block-disabled-2513534.php b/core/modules/system/tests/fixtures/update/drupal-8.update-test-block-disabled-2513534.php
index fa394473d..cf5f9dfeb 100644
--- a/core/modules/system/tests/fixtures/update/drupal-8.update-test-block-disabled-2513534.php
+++ b/core/modules/system/tests/fixtures/update/drupal-8.update-test-block-disabled-2513534.php
@@ -43,7 +43,7 @@ $existing_blocks = unserialize($existing_blocks);
$connection->update('key_value')
->fields([
- 'value' => serialize(array_merge($existing_blocks, ['block.block.testfor2513534', 'block.block.secondtestfor2513534']))
+ 'value' => serialize(array_merge($existing_blocks, ['block.block.testfor2513534', 'block.block.secondtestfor2513534'])),
])
->condition('collection', 'config.entity.key_store.block')
->condition('name', 'theme:bartik')
diff --git a/core/modules/system/tests/fixtures/update/drupal-8.views-taxonomy-parent-2543726.php b/core/modules/system/tests/fixtures/update/drupal-8.views-taxonomy-parent-2543726.php
new file mode 100644
index 000000000..70e0ad496
--- /dev/null
+++ b/core/modules/system/tests/fixtures/update/drupal-8.views-taxonomy-parent-2543726.php
@@ -0,0 +1,63 @@
+insert('config')
+ ->fields(['collection', 'name', 'data'])
+ ->values([
+ 'collection' => '',
+ 'name' => "views.view.test_taxonomy_parent",
+ 'data' => serialize($view_config),
+ ])
+ ->execute();
+
+$uuid = new Php();
+
+// The root tid.
+$tids = [0];
+
+for ($i = 0; $i < 4; $i++) {
+ $name = $this->randomString();
+
+ $tid = $connection->insert('taxonomy_term_data')
+ ->fields(['vid', 'uuid', 'langcode'])
+ ->values(['vid' => 'tags', 'uuid' => $uuid->generate(), 'langcode' => 'en'])
+ ->execute();
+
+ $connection->insert('taxonomy_term_field_data')
+ ->fields(['tid', 'vid', 'langcode', 'name', 'weight', 'changed', 'default_langcode'])
+ ->values(['tid' => $tid, 'vid' => 'tags', 'langcode' => 'en', 'name' => $name, 'weight' => 0, 'changed' => REQUEST_TIME, 'default_langcode' => 1])
+ ->execute();
+
+ $tids[] = $tid;
+}
+
+$hierarchy = [
+ // Term with tid 1 has terms with tids 2 and 3 as parents.
+ 1 => [2, 3],
+ 2 => [3, 0],
+ 3 => [0],
+];
+
+$query = $connection->insert('taxonomy_term_hierarchy')->fields(['tid', 'parent']);
+
+foreach ($hierarchy as $tid => $parents) {
+ foreach ($parents as $parent) {
+ $query->values(['tid' => $tids[$tid], 'parent' => $tids[$parent]]);
+ }
+}
+
+$query->execute();
diff --git a/core/modules/system/tests/fixtures/update/drupal-8.views-taxonomy-parent-2543726.yml b/core/modules/system/tests/fixtures/update/drupal-8.views-taxonomy-parent-2543726.yml
new file mode 100644
index 000000000..de8f63d7c
--- /dev/null
+++ b/core/modules/system/tests/fixtures/update/drupal-8.views-taxonomy-parent-2543726.yml
@@ -0,0 +1,222 @@
+langcode: en
+status: true
+dependencies:
+ module:
+ - taxonomy
+ - user
+id: test_taxonomy_parent
+label: test_taxonomy_parent
+module: views
+description: ''
+tag: ''
+base_table: taxonomy_term_data
+base_field: tid
+core: 8.x
+display:
+ default:
+ display_plugin: default
+ id: default
+ display_title: Master
+ position: 0
+ display_options:
+ access:
+ type: perm
+ options:
+ perm: 'access content'
+ cache:
+ type: tag
+ options: { }
+ query:
+ type: views_query
+ options:
+ disable_sql_rewrite: false
+ distinct: false
+ replica: false
+ query_comment: ''
+ query_tags: { }
+ exposed_form:
+ type: basic
+ options:
+ submit_button: Apply
+ reset_button: false
+ reset_button_label: Reset
+ exposed_sorts_label: 'Sort by'
+ expose_sort_order: true
+ sort_asc_label: Asc
+ sort_desc_label: Desc
+ pager:
+ type: full
+ options:
+ items_per_page: 10
+ offset: 0
+ id: 0
+ total_pages: null
+ expose:
+ items_per_page: false
+ items_per_page_label: 'Items per page'
+ items_per_page_options: '5, 10, 25, 50'
+ items_per_page_options_all: false
+ items_per_page_options_all_label: '- All -'
+ offset: false
+ offset_label: Offset
+ tags:
+ previous: '‹ Previous'
+ next: 'Next ›'
+ first: '« First'
+ last: 'Last »'
+ quantity: 9
+ style:
+ type: default
+ options:
+ grouping: { }
+ row_class: ''
+ default_row_class: true
+ uses_fields: false
+ row:
+ type: fields
+ options:
+ inline: { }
+ separator: ''
+ hide_empty: false
+ default_field_elements: true
+ fields:
+ name:
+ id: name
+ table: taxonomy_term_data
+ field: name
+ label: ''
+ alter:
+ alter_text: false
+ make_link: false
+ absolute: false
+ trim: false
+ word_boundary: false
+ ellipsis: false
+ strip_tags: false
+ html: false
+ hide_empty: false
+ empty_zero: false
+ relationship: none
+ group_type: group
+ admin_label: ''
+ exclude: false
+ element_type: ''
+ element_class: ''
+ element_label_type: ''
+ element_label_class: ''
+ element_label_colon: true
+ element_wrapper_type: ''
+ element_wrapper_class: ''
+ element_default_classes: true
+ empty: ''
+ hide_alter_empty: true
+ plugin_id: field
+ type: string
+ settings:
+ link_to_entity: true
+ entity_type: taxonomy_term
+ entity_field: name
+ filters:
+ parent:
+ id: parent
+ table: taxonomy_term_hierarchy
+ field: parent
+ relationship: field_tags
+ group_type: group
+ admin_label: ''
+ operator: '='
+ value:
+ min: ''
+ max: ''
+ value: ''
+ group: 1
+ exposed: true
+ expose:
+ operator_id: parent_op
+ label: 'Parent term'
+ description: ''
+ use_operator: false
+ operator: parent_op
+ identifier: parent
+ required: false
+ remember: false
+ multiple: false
+ remember_roles:
+ authenticated: authenticated
+ anonymous: '0'
+ administrator: '0'
+ placeholder: ''
+ min_placeholder: ''
+ max_placeholder: ''
+ is_grouped: false
+ group_info:
+ label: ''
+ description: ''
+ identifier: ''
+ optional: true
+ widget: select
+ multiple: false
+ remember: false
+ default_group: All
+ default_group_multiple: { }
+ group_items: { }
+ plugin_id: numeric
+ parent_1:
+ id: parent_1
+ table: taxonomy_term_hierarchy
+ field: parent
+ relationship: field_tags
+ group_type: group
+ admin_label: ''
+ operator: '='
+ value:
+ min: ''
+ max: ''
+ value: ''
+ group: 1
+ exposed: true
+ expose:
+ operator_id: parent_1_op
+ label: 'Parent term'
+ description: ''
+ use_operator: false
+ operator: parent_1_op
+ identifier: parent_1
+ required: false
+ remember: false
+ multiple: false
+ remember_roles:
+ authenticated: authenticated
+ anonymous: '0'
+ administrator: '0'
+ placeholder: ''
+ min_placeholder: ''
+ max_placeholder: ''
+ is_grouped: false
+ group_info:
+ label: ''
+ description: ''
+ identifier: ''
+ optional: true
+ widget: select
+ multiple: false
+ remember: false
+ default_group: All
+ default_group_multiple: { }
+ group_items: { }
+ plugin_id: numeric
+ sorts: { }
+ header: { }
+ footer: { }
+ empty: { }
+ relationships:
+ parent:
+ id: parent
+ table: taxonomy_term__parent
+ field: parent_target_id
+ relationship: none
+ group_type: group
+ admin_label: Parent
+ required: true
+ plugin_id: standard
+ arguments: { }
diff --git a/core/modules/system/tests/modules/ajax_forms_test/ajax_forms_test.module b/core/modules/system/tests/modules/ajax_forms_test/ajax_forms_test.module
index 262572be9..a93372d06 100644
--- a/core/modules/system/tests/modules/ajax_forms_test/ajax_forms_test.module
+++ b/core/modules/system/tests/modules/ajax_forms_test/ajax_forms_test.module
@@ -180,8 +180,8 @@ function ajax_forms_test_advanced_commands_add_css_callback($form, FormStateInte
* Ajax form callback: Selects the 'drivertext' element of the validation form.
*/
function ajax_forms_test_validation_form_callback($form, FormStateInterface $form_state) {
- drupal_set_message("ajax_forms_test_validation_form_callback invoked");
- drupal_set_message(t("Callback: drivertext=%drivertext, spare_required_field=%spare_required_field", ['%drivertext' => $form_state->getValue('drivertext'), '%spare_required_field' => $form_state->getValue('spare_required_field')]));
+ \Drupal::messenger()->addStatus("ajax_forms_test_validation_form_callback invoked");
+ \Drupal::messenger()->addStatus(t("Callback: drivertext=%drivertext, spare_required_field=%spare_required_field", ['%drivertext' => $form_state->getValue('drivertext'), '%spare_required_field' => $form_state->getValue('spare_required_field')]));
return ['#markup' => '
ajax_forms_test_validation_form_callback at ' . date('c') . '
'];
}
@@ -189,8 +189,8 @@ function ajax_forms_test_validation_form_callback($form, FormStateInterface $for
* Ajax form callback: Selects the 'drivernumber' element of the validation form.
*/
function ajax_forms_test_validation_number_form_callback($form, FormStateInterface $form_state) {
- drupal_set_message("ajax_forms_test_validation_number_form_callback invoked");
- drupal_set_message(t("Callback: drivernumber=%drivernumber, spare_required_field=%spare_required_field", ['%drivernumber' => $form_state->getValue('drivernumber'), '%spare_required_field' => $form_state->getValue('spare_required_field')]));
+ \Drupal::messenger()->addStatus("ajax_forms_test_validation_number_form_callback invoked");
+ \Drupal::messenger()->addStatus(t("Callback: drivernumber=%drivernumber, spare_required_field=%spare_required_field", ['%drivernumber' => $form_state->getValue('drivernumber'), '%spare_required_field' => $form_state->getValue('spare_required_field')]));
return ['#markup' => '
ajax_forms_test_validation_number_form_callback at ' . date('c') . '
'];
}
diff --git a/core/modules/system/tests/modules/ajax_forms_test/ajax_forms_test.routing.yml b/core/modules/system/tests/modules/ajax_forms_test/ajax_forms_test.routing.yml
index 57801a3e2..7b1a82678 100644
--- a/core/modules/system/tests/modules/ajax_forms_test/ajax_forms_test.routing.yml
+++ b/core/modules/system/tests/modules/ajax_forms_test/ajax_forms_test.routing.yml
@@ -45,4 +45,3 @@ ajax_forms_test.ajax_element_form:
_form: '\Drupal\ajax_forms_test\Form\AjaxFormsTestAjaxElementsForm'
requirements:
_access: 'TRUE'
-
diff --git a/core/modules/system/tests/modules/ajax_forms_test/src/Callbacks.php b/core/modules/system/tests/modules/ajax_forms_test/src/Callbacks.php
index 9b35ef769..71350305f 100644
--- a/core/modules/system/tests/modules/ajax_forms_test/src/Callbacks.php
+++ b/core/modules/system/tests/modules/ajax_forms_test/src/Callbacks.php
@@ -27,7 +27,8 @@ class Callbacks {
*/
public function dateCallback($form, FormStateInterface $form_state) {
$response = new AjaxResponse();
- $response->addCommand(new HtmlCommand('#ajax_date_value', $form_state->getValue('date')));
+ $date = $form_state->getValue('date');
+ $response->addCommand(new HtmlCommand('#ajax_date_value', sprintf('
%s
', $date)));
$response->addCommand(new DataCommand('#ajax_date_value', 'form_state_value_date', $form_state->getValue('date')));
return $response;
}
@@ -39,7 +40,7 @@ class Callbacks {
$datetime = $form_state->getValue('datetime')['date'] . ' ' . $form_state->getValue('datetime')['time'];
$response = new AjaxResponse();
- $response->addCommand(new HtmlCommand('#ajax_datetime_value', $datetime));
+ $response->addCommand(new HtmlCommand('#ajax_datetime_value', sprintf('
%s
', $datetime)));
$response->addCommand(new DataCommand('#ajax_datetime_value', 'form_state_value_datetime', $datetime));
return $response;
}
diff --git a/core/modules/system/tests/modules/ajax_forms_test/src/Form/AjaxFormsTestValidationForm.php b/core/modules/system/tests/modules/ajax_forms_test/src/Form/AjaxFormsTestValidationForm.php
index d908e3809..ef6ddaf73 100644
--- a/core/modules/system/tests/modules/ajax_forms_test/src/Form/AjaxFormsTestValidationForm.php
+++ b/core/modules/system/tests/modules/ajax_forms_test/src/Form/AjaxFormsTestValidationForm.php
@@ -67,7 +67,7 @@ class AjaxFormsTestValidationForm extends FormBase {
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
- drupal_set_message($this->t("Validation form submitted"));
+ $this->messenger()->addStatus($this->t("Validation form submitted"));
}
}
diff --git a/core/modules/system/tests/modules/ajax_forms_test/src/Plugin/Block/AjaxFormBlock.php b/core/modules/system/tests/modules/ajax_forms_test/src/Plugin/Block/AjaxFormBlock.php
index aa50cc465..65e1372d9 100644
--- a/core/modules/system/tests/modules/ajax_forms_test/src/Plugin/Block/AjaxFormBlock.php
+++ b/core/modules/system/tests/modules/ajax_forms_test/src/Plugin/Block/AjaxFormBlock.php
@@ -6,6 +6,7 @@ use Drupal\Core\Block\BlockBase;
use Drupal\Core\Form\FormBuilderInterface;
use Drupal\Core\Form\FormInterface;
use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -27,6 +28,13 @@ class AjaxFormBlock extends BlockBase implements FormInterface, ContainerFactory
*/
protected $formBuilder;
+ /**
+ * The messenger.
+ *
+ * @var \Drupal\Core\Messenger\MessengerInterface
+ */
+ protected $messenger;
+
/**
* Constructs a new AjaxFormBlock.
*
@@ -38,10 +46,13 @@ class AjaxFormBlock extends BlockBase implements FormInterface, ContainerFactory
* The plugin implementation definition.
* @param \Drupal\Core\Form\FormBuilderInterface $form_builder
* The form builder.
+ * @param \Drupal\Core\Messenger\MessengerInterface $messenger
+ * The messenger.
*/
- public function __construct(array $configuration, $plugin_id, $plugin_definition, FormBuilderInterface $form_builder) {
+ public function __construct(array $configuration, $plugin_id, $plugin_definition, FormBuilderInterface $form_builder, MessengerInterface $messenger) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->formBuilder = $form_builder;
+ $this->messenger = $messenger;
}
/**
@@ -52,7 +63,8 @@ class AjaxFormBlock extends BlockBase implements FormInterface, ContainerFactory
$configuration,
$plugin_id,
$plugin_definition,
- $container->get('form_builder')
+ $container->get('form_builder'),
+ $container->get('messenger')
);
}
@@ -129,7 +141,7 @@ class AjaxFormBlock extends BlockBase implements FormInterface, ContainerFactory
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
- drupal_set_message('Submission successful.');
+ $this->messenger->addStatus('Submission successful.');
}
}
diff --git a/core/modules/system/tests/modules/ajax_test/ajax_test.info.yml b/core/modules/system/tests/modules/ajax_test/ajax_test.info.yml
index f1517488a..dbd359664 100644
--- a/core/modules/system/tests/modules/ajax_test/ajax_test.info.yml
+++ b/core/modules/system/tests/modules/ajax_test/ajax_test.info.yml
@@ -5,4 +5,4 @@ package: Testing
version: VERSION
core: 8.x
dependencies:
- - contact
+ - drupal:contact
diff --git a/core/modules/system/tests/modules/ajax_test/ajax_test.libraries.yml b/core/modules/system/tests/modules/ajax_test/ajax_test.libraries.yml
index f1c73064b..772a05f73 100644
--- a/core/modules/system/tests/modules/ajax_test/ajax_test.libraries.yml
+++ b/core/modules/system/tests/modules/ajax_test/ajax_test.libraries.yml
@@ -1,3 +1,8 @@
+ajax_insert:
+ js:
+ js/insert-ajax.js: {}
+ dependencies:
+ - core/drupal.ajax
order:
drupalSettings:
ajax: test
diff --git a/core/modules/system/tests/modules/ajax_test/ajax_test.routing.yml b/core/modules/system/tests/modules/ajax_test/ajax_test.routing.yml
index e8d06c0a9..875b7caa9 100644
--- a/core/modules/system/tests/modules/ajax_test/ajax_test.routing.yml
+++ b/core/modules/system/tests/modules/ajax_test/ajax_test.routing.yml
@@ -6,6 +6,14 @@ ajax_test.dialog_contents:
requirements:
_access: 'TRUE'
+ajax_test.ajax_render_types:
+ path: '/ajax-test/dialog-contents-types/{type}'
+ defaults:
+ _title: 'AJAX Dialog contents routing'
+ _controller: '\Drupal\ajax_test\Controller\AjaxTestController::renderTypes'
+ requirements:
+ _access: 'TRUE'
+
ajax_test.dialog_form:
path: '/ajax-test/dialog-form'
defaults:
@@ -21,6 +29,20 @@ ajax_test.dialog:
requirements:
_access: 'TRUE'
+ajax_test.insert_links_block_wrapper:
+ path: '/ajax-test/insert-block-wrapper'
+ defaults:
+ _controller: '\Drupal\ajax_test\Controller\AjaxTestController::insertLinksBlockWrapper'
+ requirements:
+ _access: 'TRUE'
+
+ajax_test.insert_links_inline_wrapper:
+ path: '/ajax-test/insert-inline-wrapper'
+ defaults:
+ _controller: '\Drupal\ajax_test\Controller\AjaxTestController::insertLinksInlineWrapper'
+ requirements:
+ _access: 'TRUE'
+
ajax_test.dialog_close:
path: '/ajax-test/dialog-close'
defaults:
diff --git a/core/modules/system/tests/modules/ajax_test/js/insert-ajax.es6.js b/core/modules/system/tests/modules/ajax_test/js/insert-ajax.es6.js
new file mode 100644
index 000000000..6ca363a50
--- /dev/null
+++ b/core/modules/system/tests/modules/ajax_test/js/insert-ajax.es6.js
@@ -0,0 +1,45 @@
+/**
+ * @file
+ * Drupal behavior to attach click event handlers to ajax-insert and
+ * ajax-insert-inline links for testing ajax requests.
+ */
+
+(function($, window, Drupal) {
+ Drupal.behaviors.insertTest = {
+ attach(context) {
+ $('.ajax-insert')
+ .once('ajax-insert')
+ .on('click', event => {
+ event.preventDefault();
+ const ajaxSettings = {
+ url: event.currentTarget.getAttribute('href'),
+ wrapper: 'ajax-target',
+ base: false,
+ element: false,
+ method: event.currentTarget.getAttribute('data-method'),
+ effect: event.currentTarget.getAttribute('data-effect'),
+ };
+ const myAjaxObject = Drupal.ajax(ajaxSettings);
+ myAjaxObject.execute();
+ });
+
+ $('.ajax-insert-inline')
+ .once('ajax-insert')
+ .on('click', event => {
+ event.preventDefault();
+ const ajaxSettings = {
+ url: event.currentTarget.getAttribute('href'),
+ wrapper: 'ajax-target-inline',
+ base: false,
+ element: false,
+ method: event.currentTarget.getAttribute('data-method'),
+ effect: event.currentTarget.getAttribute('data-effect'),
+ };
+ const myAjaxObject = Drupal.ajax(ajaxSettings);
+ myAjaxObject.execute();
+ });
+
+ $(context).addClass('processed');
+ },
+ };
+})(jQuery, window, Drupal);
diff --git a/core/modules/system/tests/modules/ajax_test/js/insert-ajax.js b/core/modules/system/tests/modules/ajax_test/js/insert-ajax.js
new file mode 100644
index 000000000..e28fcd298
--- /dev/null
+++ b/core/modules/system/tests/modules/ajax_test/js/insert-ajax.js
@@ -0,0 +1,42 @@
+/**
+* DO NOT EDIT THIS FILE.
+* See the following change record for more information,
+* https://www.drupal.org/node/2815083
+* @preserve
+**/
+
+(function ($, window, Drupal) {
+ Drupal.behaviors.insertTest = {
+ attach: function attach(context) {
+ $('.ajax-insert').once('ajax-insert').on('click', function (event) {
+ event.preventDefault();
+ var ajaxSettings = {
+ url: event.currentTarget.getAttribute('href'),
+ wrapper: 'ajax-target',
+ base: false,
+ element: false,
+ method: event.currentTarget.getAttribute('data-method'),
+ effect: event.currentTarget.getAttribute('data-effect')
+ };
+ var myAjaxObject = Drupal.ajax(ajaxSettings);
+ myAjaxObject.execute();
+ });
+
+ $('.ajax-insert-inline').once('ajax-insert').on('click', function (event) {
+ event.preventDefault();
+ var ajaxSettings = {
+ url: event.currentTarget.getAttribute('href'),
+ wrapper: 'ajax-target-inline',
+ base: false,
+ element: false,
+ method: event.currentTarget.getAttribute('data-method'),
+ effect: event.currentTarget.getAttribute('data-effect')
+ };
+ var myAjaxObject = Drupal.ajax(ajaxSettings);
+ myAjaxObject.execute();
+ });
+
+ $(context).addClass('processed');
+ }
+ };
+})(jQuery, window, Drupal);
\ No newline at end of file
diff --git a/core/modules/system/tests/modules/ajax_test/src/Controller/AjaxTestController.php b/core/modules/system/tests/modules/ajax_test/src/Controller/AjaxTestController.php
index 5ba65e801..1897719f6 100644
--- a/core/modules/system/tests/modules/ajax_test/src/Controller/AjaxTestController.php
+++ b/core/modules/system/tests/modules/ajax_test/src/Controller/AjaxTestController.php
@@ -42,6 +42,101 @@ class AjaxTestController {
return $content;
}
+ /**
+ * Example content for testing the wrapper of the response.
+ *
+ * @param string $type
+ * Type of response.
+ *
+ * @return array
+ * Renderable array of AJAX response contents.
+ */
+ public function renderTypes($type) {
+ return [
+ '#title' => '
AJAX Dialog & contents',
+ 'content' => [
+ '#type' => 'inline_template',
+ '#template' => $this->getRenderTypes()[$type]['render'],
+ ],
+ ];
+ }
+
+ /**
+ * Returns a render array of links that directly Drupal.ajax().
+ *
+ * @return array
+ * Renderable array of AJAX response contents.
+ */
+ public function insertLinksBlockWrapper() {
+ $methods = [
+ 'html',
+ 'replaceWith',
+ ];
+
+ $build['links'] = [
+ 'ajax_target' => [
+ '#markup' => '
',
+ ],
+ 'links' => [
+ '#theme' => 'links',
+ '#attached' => ['library' => ['ajax_test/ajax_insert']],
+ ],
+ ];
+ foreach ($methods as $method) {
+ foreach ($this->getRenderTypes() as $type => $item) {
+ $class = 'ajax-insert';
+ $build['links']['links']['#links']["$method-$type"] = [
+ 'title' => "Link $method $type",
+ 'url' => Url::fromRoute('ajax_test.ajax_render_types', ['type' => $type]),
+ 'attributes' => [
+ 'class' => [$class],
+ 'data-method' => $method,
+ 'data-effect' => $item['effect'],
+ ],
+ ];
+ }
+ }
+ return $build;
+ }
+
+ /**
+ * Returns a render array of links that directly Drupal.ajax().
+ *
+ * @return array
+ * Renderable array of AJAX response contents.
+ */
+ public function insertLinksInlineWrapper() {
+ $methods = [
+ 'html',
+ 'replaceWith',
+ ];
+
+ $build['links'] = [
+ 'ajax_target' => [
+ '#markup' => '
Target inline
',
+ ],
+ 'links' => [
+ '#theme' => 'links',
+ '#attached' => ['library' => ['ajax_test/ajax_insert']],
+ ],
+ ];
+ foreach ($methods as $method) {
+ foreach ($this->getRenderTypes() as $type => $item) {
+ $class = 'ajax-insert-inline';
+ $build['links']['links']['#links']["$method-$type"] = [
+ 'title' => "Link $method $type",
+ 'url' => Url::fromRoute('ajax_test.ajax_render_types', ['type' => $type]),
+ 'attributes' => [
+ 'class' => [$class],
+ 'data-method' => $method,
+ 'data-effect' => $item['effect'],
+ ],
+ ];
+ }
+ }
+ return $build;
+ }
+
/**
* Returns a render array that will be rendered by AjaxRenderer.
*
@@ -139,7 +234,7 @@ class AjaxTestController {
'data-dialog-type' => 'modal',
'data-dialog-options' => json_encode([
'width' => 400,
- ])
+ ]),
],
],
'link3' => [
@@ -151,7 +246,7 @@ class AjaxTestController {
'data-dialog-options' => json_encode([
'target' => 'ajax-test-dialog-wrapper-1',
'width' => 800,
- ])
+ ]),
],
],
'link4' => [
@@ -179,7 +274,7 @@ class AjaxTestController {
'data-dialog-options' => json_encode([
'width' => 800,
'height' => 500,
- ])
+ ]),
],
],
'link7' => [
@@ -190,7 +285,7 @@ class AjaxTestController {
'data-dialog-type' => 'dialog',
'data-dialog-options' => json_encode([
'width' => 800,
- ])
+ ]),
],
],
'link8' => [
@@ -222,4 +317,41 @@ class AjaxTestController {
return $response;
}
+ /**
+ * Render types.
+ *
+ * @return array
+ * Render types.
+ */
+ protected function getRenderTypes() {
+ $render_single_root = [
+ 'pre-wrapped-div' => '
pre-wrapped
',
+ 'pre-wrapped-span' => '
pre-wrapped',
+ 'pre-wrapped-whitespace' => '
pre-wrapped-whitespace
' . "\r\n",
+ 'not-wrapped' => 'not-wrapped',
+ 'comment-string-not-wrapped' => 'comment-string-not-wrapped',
+ 'comment-not-wrapped' => '',
+ 'svg' => '
',
+ 'empty' => '',
+ ];
+ $render_multiple_root = [
+ 'mixed' => ' foo foo bar
additional not wrapped strings,
final string
',
+ 'top-level-only' => '
element #1
element #2
',
+ 'top-level-only-pre-whitespace' => '
element #1
element #2
',
+ 'top-level-only-middle-whitespace-span' => '
element #1 element #2',
+ 'top-level-only-middle-whitespace-div' => '
element #1
element #2
',
+ ];
+
+ $render_info = [];
+ foreach ($render_single_root as $key => $render) {
+ $render_info[$key] = ['render' => $render, 'effect' => 'fade'];
+ }
+ foreach ($render_multiple_root as $key => $render) {
+ $render_info[$key] = ['render' => $render, 'effect' => 'none'];
+ $render_info["$key--effect"] = ['render' => $render, 'effect' => 'fade'];
+ }
+
+ return $render_info;
+ }
+
}
diff --git a/core/modules/system/tests/modules/ajax_test/src/Form/AjaxTestDialogForm.php b/core/modules/system/tests/modules/ajax_test/src/Form/AjaxTestDialogForm.php
index daedcf170..a0b606067 100644
--- a/core/modules/system/tests/modules/ajax_test/src/Form/AjaxTestDialogForm.php
+++ b/core/modules/system/tests/modules/ajax_test/src/Form/AjaxTestDialogForm.php
@@ -31,7 +31,7 @@ class AjaxTestDialogForm extends FormBase {
// to have a dummy field we can set in WebTestBase::drupalPostForm() else it won't
// submit anything.
$form['textfield'] = [
- '#type' => 'hidden'
+ '#type' => 'hidden',
];
$form['button1'] = [
'#type' => 'submit',
@@ -67,7 +67,6 @@ class AjaxTestDialogForm extends FormBase {
$form_state->setRedirect('ajax_test.dialog_contents');
}
-
/**
* AJAX callback handler for AjaxTestDialogForm.
*/
@@ -82,7 +81,6 @@ class AjaxTestDialogForm extends FormBase {
return $this->dialog(FALSE);
}
-
/**
* Util to render dialog in ajax callback.
*
diff --git a/core/modules/system/tests/modules/batch_test/batch_test.callbacks.inc b/core/modules/system/tests/modules/batch_test/batch_test.callbacks.inc
index 9116c85fe..eb1832185 100644
--- a/core/modules/system/tests/modules/batch_test/batch_test.callbacks.inc
+++ b/core/modules/system/tests/modules/batch_test/batch_test.callbacks.inc
@@ -116,7 +116,7 @@ function _batch_test_finished_helper($batch_id, $success, $results, $operations)
],
];
- drupal_set_message(\Drupal::service('renderer')->renderPlain($error_message));
+ \Drupal::messenger()->addStatus(\Drupal::service('renderer')->renderPlain($error_message));
}
/**
diff --git a/core/modules/system/tests/modules/batch_test/src/Controller/BatchTestController.php b/core/modules/system/tests/modules/batch_test/src/Controller/BatchTestController.php
index 1d34f8a45..9a5951e6d 100644
--- a/core/modules/system/tests/modules/batch_test/src/Controller/BatchTestController.php
+++ b/core/modules/system/tests/modules/batch_test/src/Controller/BatchTestController.php
@@ -19,7 +19,7 @@ class BatchTestController {
return [
'success' => [
'#markup' => 'Redirection successful.',
- ]
+ ],
];
}
@@ -103,7 +103,7 @@ class BatchTestController {
return [
'success' => [
'#markup' => 'Got out of a programmatic batched form.',
- ]
+ ],
];
}
diff --git a/core/modules/system/tests/modules/common_test_cron_helper/common_test_cron_helper.module b/core/modules/system/tests/modules/common_test_cron_helper/common_test_cron_helper.module
index ee280a3b0..31373cb59 100644
--- a/core/modules/system/tests/modules/common_test_cron_helper/common_test_cron_helper.module
+++ b/core/modules/system/tests/modules/common_test_cron_helper/common_test_cron_helper.module
@@ -8,8 +8,8 @@
/**
* Implements hook_cron().
*
- * common_test_cron() throws an exception, but the execution should reach this
- * function as well.
+ * Function common_test_cron() throws an exception, but the execution should
+ * reach this function as well.
*
* @see common_test_cron()
*/
diff --git a/core/modules/system/tests/modules/condition_test/src/FormController.php b/core/modules/system/tests/modules/condition_test/src/FormController.php
index da475ae0c..19dc9505b 100644
--- a/core/modules/system/tests/modules/condition_test/src/FormController.php
+++ b/core/modules/system/tests/modules/condition_test/src/FormController.php
@@ -60,13 +60,13 @@ class FormController implements FormInterface {
$this->condition->submitConfigurationForm($form, $form_state);
$config = $this->condition->getConfig();
foreach ($config['bundles'] as $bundle) {
- drupal_set_message('Bundle: ' . $bundle);
+ \Drupal::messenger()->addStatus('Bundle: ' . $bundle);
}
$article = Node::load(1);
$this->condition->setContextValue('node', $article);
if ($this->condition->execute()) {
- drupal_set_message(t('Executed successfully.'));
+ \Drupal::messenger()->addStatus(t('Executed successfully.'));
}
}
diff --git a/core/modules/system/tests/modules/database_test/database_test.install b/core/modules/system/tests/modules/database_test/database_test.install
index b4e3ca563..ab952c746 100644
--- a/core/modules/system/tests/modules/database_test/database_test.install
+++ b/core/modules/system/tests/modules/database_test/database_test.install
@@ -47,7 +47,7 @@ function database_test_schema() {
],
'primary key' => ['id'],
'unique keys' => [
- 'name' => ['name']
+ 'name' => ['name'],
],
'indexes' => [
'ages' => ['age'],
@@ -148,7 +148,7 @@ function database_test_schema() {
],
'blob2' => [
'description' => 'A second BLOB field.',
- 'type' => 'blob'
+ 'type' => 'blob',
],
],
'primary key' => ['id'],
@@ -212,7 +212,7 @@ function database_test_schema() {
],
'primary key' => ['id'],
'unique keys' => [
- 'name' => ['name']
+ 'name' => ['name'],
],
'indexes' => [
'ages' => ['age'],
@@ -242,7 +242,7 @@ function database_test_schema() {
],
'primary key' => ['id'],
'unique keys' => [
- 'name' => ['name']
+ 'name' => ['name'],
],
];
@@ -287,6 +287,10 @@ function database_test_schema() {
'description' => 'A column with preserved name.',
'type' => 'text',
],
+ 'function' => [
+ 'description' => 'A column with reserved name in MySQL 8.',
+ 'type' => 'text',
+ ],
],
'primary key' => ['id'],
];
diff --git a/core/modules/system/tests/modules/database_test/database_test.module b/core/modules/system/tests/modules/database_test/database_test.module
index 1eaa103fa..210998444 100644
--- a/core/modules/system/tests/modules/database_test/database_test.module
+++ b/core/modules/system/tests/modules/database_test/database_test.module
@@ -38,7 +38,6 @@ function database_test_query_alter(AlterableInterface $query) {
}
}
-
/**
* Implements hook_query_TAG_alter().
*
diff --git a/core/modules/system/tests/modules/default_format_test/default_format_test.info.yml b/core/modules/system/tests/modules/default_format_test/default_format_test.info.yml
new file mode 100644
index 000000000..0a02a0688
--- /dev/null
+++ b/core/modules/system/tests/modules/default_format_test/default_format_test.info.yml
@@ -0,0 +1,6 @@
+name: 'Default format test'
+type: module
+description: 'Support module for testing default route format.'
+package: Testing
+version: VERSION
+core: 8.x
diff --git a/core/modules/system/tests/modules/default_format_test/default_format_test.routing.yml b/core/modules/system/tests/modules/default_format_test/default_format_test.routing.yml
new file mode 100644
index 000000000..368dcb194
--- /dev/null
+++ b/core/modules/system/tests/modules/default_format_test/default_format_test.routing.yml
@@ -0,0 +1,28 @@
+default_format_test.machine:
+ path: '/default_format_test/machine'
+ defaults:
+ # Same controller + method!
+ _controller: '\Drupal\default_format_test\DefaultFormatTestController::content'
+ requirements:
+ _access: 'TRUE'
+ _format: 'json'
+
+default_format_test.human:
+ path: '/default_format_test/human'
+ defaults:
+ # Same controller + method!
+ _controller: '\Drupal\default_format_test\DefaultFormatTestController::content'
+ requirements:
+ _access: 'TRUE'
+ _format: 'html'
+
+# Route definition identical to default_format_test.machine, only different name.
+# @see \Drupal\FunctionalTests\Routing\DefaultFormatTest::testMultiple
+default_format_test.machine.alias:
+ path: '/default_format_test/machine'
+ defaults:
+ # Same controller + method!
+ _controller: '\Drupal\default_format_test\DefaultFormatTestController::content'
+ requirements:
+ _access: 'TRUE'
+ _format: 'json'
diff --git a/core/modules/system/tests/modules/default_format_test/src/DefaultFormatTestController.php b/core/modules/system/tests/modules/default_format_test/src/DefaultFormatTestController.php
new file mode 100644
index 000000000..6e52eea34
--- /dev/null
+++ b/core/modules/system/tests/modules/default_format_test/src/DefaultFormatTestController.php
@@ -0,0 +1,15 @@
+getRequestFormat();
+ return new CacheableResponse('format:' . $format, 200, ['Content-Type' => $request->getMimeType($format)]);
+ }
+
+}
diff --git a/core/modules/system/tests/modules/dialog_renderer_test/src/Render/MainContent/WideModalRenderer.php b/core/modules/system/tests/modules/dialog_renderer_test/src/Render/MainContent/WideModalRenderer.php
index 2fd982818..4b3aee92d 100644
--- a/core/modules/system/tests/modules/dialog_renderer_test/src/Render/MainContent/WideModalRenderer.php
+++ b/core/modules/system/tests/modules/dialog_renderer_test/src/Render/MainContent/WideModalRenderer.php
@@ -38,7 +38,6 @@ class WideModalRenderer extends ModalRenderer {
$this->mode = $mode;
}
-
/**
* {@inheritdoc}
*/
diff --git a/core/modules/system/tests/modules/early_rendering_controller_test/src/EarlyRenderingTestController.php b/core/modules/system/tests/modules/early_rendering_controller_test/src/EarlyRenderingTestController.php
index 9ba11b773..2a6b83965 100644
--- a/core/modules/system/tests/modules/early_rendering_controller_test/src/EarlyRenderingTestController.php
+++ b/core/modules/system/tests/modules/early_rendering_controller_test/src/EarlyRenderingTestController.php
@@ -62,7 +62,7 @@ class EarlyRenderingTestController extends ControllerBase {
'#pre_render' => [function () {
$elements = $this->earlyRenderContent();
return $elements;
- }
+ },
],
];
}
diff --git a/core/modules/system/tests/modules/entity_reference_test/entity_reference_test.info.yml b/core/modules/system/tests/modules/entity_reference_test/entity_reference_test.info.yml
index 62830f4fc..d617a0f6d 100644
--- a/core/modules/system/tests/modules/entity_reference_test/entity_reference_test.info.yml
+++ b/core/modules/system/tests/modules/entity_reference_test/entity_reference_test.info.yml
@@ -5,7 +5,7 @@ core: 8.x
package: Testing
version: VERSION
dependencies:
- - node
- - user
- - views
- - entity_test
+ - drupal:node
+ - drupal:user
+ - drupal:views
+ - drupal:entity_test
diff --git a/core/modules/system/tests/modules/entity_reference_test_views/entity_reference_test_views.info.yml b/core/modules/system/tests/modules/entity_reference_test_views/entity_reference_test_views.info.yml
index 19678f88f..0209b78e4 100644
--- a/core/modules/system/tests/modules/entity_reference_test_views/entity_reference_test_views.info.yml
+++ b/core/modules/system/tests/modules/entity_reference_test_views/entity_reference_test_views.info.yml
@@ -5,4 +5,4 @@ package: Testing
version: VERSION
core: 8.x
dependencies:
- - views
+ - drupal:views
diff --git a/core/modules/system/tests/modules/entity_schema_test/entity_schema_test.info.yml b/core/modules/system/tests/modules/entity_schema_test/entity_schema_test.info.yml
index 646717ddc..b1d814255 100644
--- a/core/modules/system/tests/modules/entity_schema_test/entity_schema_test.info.yml
+++ b/core/modules/system/tests/modules/entity_schema_test/entity_schema_test.info.yml
@@ -5,4 +5,4 @@ package: Testing
version: VERSION
core: 8.x
dependencies:
- - entity_test
+ - drupal:entity_test
diff --git a/core/modules/system/tests/modules/entity_test/entity_test.info.yml b/core/modules/system/tests/modules/entity_test/entity_test.info.yml
index bd4382e4d..192d6ded8 100644
--- a/core/modules/system/tests/modules/entity_test/entity_test.info.yml
+++ b/core/modules/system/tests/modules/entity_test/entity_test.info.yml
@@ -5,5 +5,5 @@ package: Testing
version: VERSION
core: 8.x
dependencies:
- - field
- - text
+ - drupal:field
+ - drupal:text
diff --git a/core/modules/system/tests/modules/entity_test/entity_test.module b/core/modules/system/tests/modules/entity_test/entity_test.module
index 8b4f2ba82..66a42e293 100644
--- a/core/modules/system/tests/modules/entity_test/entity_test.module
+++ b/core/modules/system/tests/modules/entity_test/entity_test.module
@@ -101,6 +101,9 @@ function entity_test_entity_type_alter(array &$entity_types) {
if (!$state->get('entity_test_new')) {
unset($entity_types['entity_test_new']);
}
+
+ $entity_test_definition = $entity_types['entity_test'];
+ $entity_test_definition->set('entity_keys', $state->get('entity_test.entity_keys', []) + $entity_test_definition->getKeys());
}
/**
@@ -303,7 +306,7 @@ function entity_test_entity_extra_field_info() {
'description' => t('An extra field on the display side, hidden by default.'),
'visible' => FALSE,
],
- ]
+ ],
];
return $extra;
@@ -677,6 +680,23 @@ function entity_test_entity_test_mul_langcode_key_translation_delete(EntityInter
_entity_test_record_hooks('entity_test_mul_langcode_key_translation_delete', $translation->language()->getId());
}
+/**
+ * Implements hook_entity_revision_create().
+ */
+function entity_test_entity_revision_create(EntityInterface $new_revision, EntityInterface $entity, $keep_untranslatable_fields) {
+ _entity_test_record_hooks('entity_revision_create', ['new_revision' => $new_revision, 'entity' => $entity, 'keep_untranslatable_fields' => $keep_untranslatable_fields]);
+}
+
+/**
+ * Implements hook_ENTITY_TYPE_revision_create() for 'entity_test_mulrev'.
+ */
+function entity_test_entity_test_mulrev_revision_create(EntityInterface $new_revision, EntityInterface $entity, $keep_untranslatable_fields) {
+ if ($new_revision->get('name')->value == 'revision_create_test_it') {
+ $new_revision->set('name', 'revision_create_test_it_altered');
+ }
+ _entity_test_record_hooks('entity_test_mulrev_revision_create', ['new_revision' => $new_revision, 'entity' => $entity, 'keep_untranslatable_fields' => $keep_untranslatable_fields]);
+}
+
/**
* Field default value callback.
*
diff --git a/core/modules/system/tests/modules/entity_test/src/Controller/EntityTestController.php b/core/modules/system/tests/modules/entity_test/src/Controller/EntityTestController.php
index a69348849..fa69e783d 100644
--- a/core/modules/system/tests/modules/entity_test/src/Controller/EntityTestController.php
+++ b/core/modules/system/tests/modules/entity_test/src/Controller/EntityTestController.php
@@ -95,7 +95,6 @@ class EntityTestController extends ControllerBase {
];
}
-
/**
* Empty list of entities of the given entity type.
*
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTest.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTest.php
index 9f8c5b052..771d81a9a 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTest.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTest.php
@@ -109,7 +109,7 @@ class EntityTest extends ContentEntityBase implements EntityOwnerInterface {
],
]);
- return $fields;
+ return $fields + \Drupal::state()->get($entity_type->id() . '.additional_base_field_definitions', []);
}
/**
@@ -164,4 +164,15 @@ class EntityTest extends ContentEntityBase implements EntityOwnerInterface {
return $this->get('name')->value;
}
+ /**
+ * {@inheritdoc}
+ */
+ public function getEntityKey($key) {
+ // Typically this protected method is used internally by entity classes and
+ // exposed publicly through more specific getter methods. So that test cases
+ // are able to set and access entity keys dynamically, update the visibility
+ // of this method to public.
+ return parent::getEntityKey($key);
+ }
+
}
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestComputedField.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestComputedField.php
index c873ccc00..187effe50 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestComputedField.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestComputedField.php
@@ -4,6 +4,7 @@ namespace Drupal\entity_test\Entity;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
+use Drupal\entity_test\Plugin\Field\ComputedReferenceTestFieldItemList;
use Drupal\entity_test\Plugin\Field\ComputedTestFieldItemList;
/**
@@ -39,6 +40,12 @@ class EntityTestComputedField extends EntityTest {
->setComputed(TRUE)
->setClass(ComputedTestFieldItemList::class);
+ $fields['computed_reference_field'] = BaseFieldDefinition::create('entity_reference')
+ ->setLabel('Computed Reference Field Test')
+ ->setComputed(TRUE)
+ ->setSetting('target_type', 'entity_test')
+ ->setClass(ComputedReferenceTestFieldItemList::class);
+
return $fields;
}
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMapField.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMapField.php
new file mode 100644
index 000000000..eb72fa55f
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMapField.php
@@ -0,0 +1,39 @@
+setLabel(t('Data'))
+ ->setDescription(t('A serialized array of additional data.'));
+
+ return $fields;
+ }
+
+}
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMul.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMul.php
index 50ae3771a..0c1ca9a53 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMul.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMul.php
@@ -50,7 +50,7 @@ class EntityTestMul extends EntityTest {
* {@inheritdoc}
*/
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
- return parent::baseFieldDefinitions($entity_type) + \Drupal::state()->get($entity_type->id() . '.additional_base_field_definitions', []);
+ return parent::baseFieldDefinitions($entity_type);
}
}
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRev.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRev.php
index b4493e31f..eeafa14d1 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRev.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRev.php
@@ -54,7 +54,7 @@ class EntityTestMulRev extends EntityTestRev {
* {@inheritdoc}
*/
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
- $fields = parent::baseFieldDefinitions($entity_type) + \Drupal::state()->get($entity_type->id() . '.additional_base_field_definitions', []);
+ $fields = parent::baseFieldDefinitions($entity_type);
$fields['non_mul_field'] = BaseFieldDefinition::create('string')
->setLabel(t('Non translatable'))
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRevPub.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRevPub.php
index 862958e92..8fa69a32b 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRevPub.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRevPub.php
@@ -17,7 +17,8 @@ use Drupal\Core\Entity\EntityTypeInterface;
* "access" = "Drupal\entity_test\EntityTestAccessControlHandler",
* "form" = {
* "default" = "Drupal\entity_test\EntityTestForm",
- * "delete" = "Drupal\entity_test\EntityTestDeleteForm"
+ * "delete" = "Drupal\entity_test\EntityTestDeleteForm",
+ * "delete-multiple-confirm" = "Drupal\Core\Entity\Form\DeleteMultipleForm"
* },
* "translation" = "Drupal\content_translation\ContentTranslationHandler",
* "views_data" = "Drupal\views\EntityViewsData",
@@ -45,6 +46,7 @@ use Drupal\Core\Entity\EntityTypeInterface;
* "add-form" = "/entity_test_mulrevpub/add",
* "canonical" = "/entity_test_mulrevpub/manage/{entity_test_mulrevpub}",
* "delete-form" = "/entity_test/delete/entity_test_mulrevpub/{entity_test_mulrevpub}",
+ * "delete-multiple-form" = "/entity_test/delete",
* "edit-form" = "/entity_test_mulrevpub/manage/{entity_test_mulrevpub}/edit",
* "revision" = "/entity_test_mulrevpub/{entity_test_mulrevpub}/revision/{entity_test_mulrevpub_revision}/view",
* }
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestRev.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestRev.php
index 5aa3ae13d..e99505eed 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestRev.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestRev.php
@@ -16,7 +16,8 @@ use Drupal\Core\Field\BaseFieldDefinition;
* "view_builder" = "Drupal\entity_test\EntityTestViewBuilder",
* "form" = {
* "default" = "Drupal\entity_test\EntityTestForm",
- * "delete" = "Drupal\entity_test\EntityTestDeleteForm"
+ * "delete" = "Drupal\entity_test\EntityTestDeleteForm",
+ * "delete-multiple-confirm" = "Drupal\Core\Entity\Form\DeleteMultipleForm"
* },
* "view_builder" = "Drupal\entity_test\EntityTestViewBuilder",
* "translation" = "Drupal\content_translation\ContentTranslationHandler",
@@ -41,6 +42,7 @@ use Drupal\Core\Field\BaseFieldDefinition;
* "add-form" = "/entity_test_rev/add",
* "canonical" = "/entity_test_rev/manage/{entity_test_rev}",
* "delete-form" = "/entity_test/delete/entity_test_rev/{entity_test_rev}",
+ * "delete-multiple-form" = "/entity_test_rev/delete_multiple",
* "edit-form" = "/entity_test_rev/manage/{entity_test_rev}/edit",
* "revision" = "/entity_test_rev/{entity_test_rev}/revision/{entity_test_rev_revision}/view",
* }
@@ -65,7 +67,7 @@ class EntityTestRev extends EntityTest {
->setCardinality(1)
->setReadOnly(TRUE);
- return $fields + \Drupal::state()->get($entity_type->id() . '.additional_base_field_definitions', []);
+ return $fields;
}
}
diff --git a/core/modules/system/tests/modules/entity_test/src/EntityTestForm.php b/core/modules/system/tests/modules/entity_test/src/EntityTestForm.php
index 75ff8ad9b..d471d6058 100644
--- a/core/modules/system/tests/modules/entity_test/src/EntityTestForm.php
+++ b/core/modules/system/tests/modules/entity_test/src/EntityTestForm.php
@@ -65,7 +65,7 @@ class EntityTestForm extends ContentEntityForm {
else {
$message = t('%entity_type @id has been updated.', ['@id' => $entity->id(), '%entity_type' => $entity->getEntityTypeId()]);
}
- drupal_set_message($message);
+ $this->messenger()->addStatus($message);
if ($entity->id()) {
$entity_type = $entity->getEntityTypeId();
@@ -76,7 +76,7 @@ class EntityTestForm extends ContentEntityForm {
}
else {
// Error on save.
- drupal_set_message(t('The entity could not be saved.'), 'error');
+ $this->messenger()->addError($this->t('The entity could not be saved.'));
$form_state->setRebuild();
}
}
diff --git a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/ComputedReferenceTestFieldItemList.php b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/ComputedReferenceTestFieldItemList.php
new file mode 100644
index 000000000..d28a014f1
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/ComputedReferenceTestFieldItemList.php
@@ -0,0 +1,24 @@
+get('entity_test_reference_computed_target_ids', []) as $delta => $id) {
+ $this->list[$delta] = $this->createItem($delta, $id);
+ }
+ }
+
+}
diff --git a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldWidget/ShapeOnlyColorEditableWidget.php b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldWidget/ShapeOnlyColorEditableWidget.php
index ae5dd3039..6283bc3ce 100644
--- a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldWidget/ShapeOnlyColorEditableWidget.php
+++ b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldWidget/ShapeOnlyColorEditableWidget.php
@@ -25,7 +25,7 @@ class ShapeOnlyColorEditableWidget extends WidgetBase {
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
$element['shape'] = [
'#type' => 'hidden',
- '#value' => $items[$delta]->shape
+ '#value' => $items[$delta]->shape,
];
$element['color'] = [
diff --git a/core/modules/hal/tests/src/Functional/EntityResource/EntityTestBundle/EntityTestBundleHalJsonAnonTest.php b/core/modules/system/tests/modules/entity_test/tests/src/Functional/Hal/EntityTestBundleHalJsonAnonTest.php
similarity index 76%
rename from core/modules/hal/tests/src/Functional/EntityResource/EntityTestBundle/EntityTestBundleHalJsonAnonTest.php
rename to core/modules/system/tests/modules/entity_test/tests/src/Functional/Hal/EntityTestBundleHalJsonAnonTest.php
index 9e6bcb306..77988aa03 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/EntityTestBundle/EntityTestBundleHalJsonAnonTest.php
+++ b/core/modules/system/tests/modules/entity_test/tests/src/Functional/Hal/EntityTestBundleHalJsonAnonTest.php
@@ -1,10 +1,10 @@
[
- ['value' => $author->uuid()]
+ ['value' => $author->uuid()],
],
'lang' => 'en',
],
diff --git a/core/modules/hal/tests/src/Functional/EntityResource/EntityTest/EntityTestHalJsonBasicAuthTest.php b/core/modules/system/tests/modules/entity_test/tests/src/Functional/Hal/EntityTestHalJsonBasicAuthTest.php
similarity index 84%
rename from core/modules/hal/tests/src/Functional/EntityResource/EntityTest/EntityTestHalJsonBasicAuthTest.php
rename to core/modules/system/tests/modules/entity_test/tests/src/Functional/Hal/EntityTestHalJsonBasicAuthTest.php
index 65ad0db63..18fad4bee 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/EntityTest/EntityTestHalJsonBasicAuthTest.php
+++ b/core/modules/system/tests/modules/entity_test/tests/src/Functional/Hal/EntityTestHalJsonBasicAuthTest.php
@@ -1,6 +1,6 @@
applyHalFieldNormalization($default_normalization);
+
+ $author = User::load(0);
+ return $normalization + [
+ '_links' => [
+ 'self' => [
+ 'href' => '',
+ ],
+ 'type' => [
+ 'href' => $this->baseUrl . '/rest/type/entity_test_map_field/entity_test_map_field',
+ ],
+ $this->baseUrl . '/rest/relation/entity_test_map_field/entity_test_map_field/user_id' => [
+ [
+ 'href' => $this->baseUrl . '/user/0?_format=hal_json',
+ 'lang' => 'en',
+ ],
+ ],
+ ],
+ '_embedded' => [
+ $this->baseUrl . '/rest/relation/entity_test_map_field/entity_test_map_field/user_id' => [
+ [
+ '_links' => [
+ 'self' => [
+ 'href' => $this->baseUrl . '/user/0?_format=hal_json',
+ ],
+ 'type' => [
+ 'href' => $this->baseUrl . '/rest/type/user/user',
+ ],
+ ],
+ 'uuid' => [
+ [
+ 'value' => $author->uuid(),
+ ],
+ ],
+ 'lang' => 'en',
+ ],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getNormalizedPostEntity() {
+ return parent::getNormalizedPostEntity() + [
+ '_links' => [
+ 'type' => [
+ 'href' => $this->baseUrl . '/rest/type/entity_test_map_field/entity_test_map_field',
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedCacheContexts() {
+ return [
+ 'url.site',
+ 'user.permissions',
+ ];
+ }
+
+}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityTestBundle/EntityTestBundleJsonAnonTest.php b/core/modules/system/tests/modules/entity_test/tests/src/Functional/Rest/EntityTestBundleJsonAnonTest.php
similarity index 82%
rename from core/modules/rest/tests/src/Functional/EntityResource/EntityTestBundle/EntityTestBundleJsonAnonTest.php
rename to core/modules/system/tests/modules/entity_test/tests/src/Functional/Rest/EntityTestBundleJsonAnonTest.php
index 1df26f386..08eb70047 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/EntityTestBundle/EntityTestBundleJsonAnonTest.php
+++ b/core/modules/system/tests/modules/entity_test/tests/src/Functional/Rest/EntityTestBundleJsonAnonTest.php
@@ -1,6 +1,6 @@
grantPermissionsToTestedRole(['administer entity_test_bundle content']);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function createEntity() {
+ $entity_test_bundle = EntityTestBundle::create([
+ 'id' => 'camelids',
+ 'label' => 'Camelids',
+ 'description' => 'Camelids are large, strictly herbivorous animals with slender necks and long legs.',
+ ]);
+ $entity_test_bundle->save();
+
+ return $entity_test_bundle;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedNormalizedEntity() {
+ return [
+ 'dependencies' => [],
+ 'description' => 'Camelids are large, strictly herbivorous animals with slender necks and long legs.',
+ 'id' => 'camelids',
+ 'label' => 'Camelids',
+ 'langcode' => 'en',
+ 'status' => TRUE,
+ 'uuid' => $this->entity->uuid(),
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getNormalizedPostEntity() {
+ // @todo Update in https://www.drupal.org/node/2300677.
+ }
+
+}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityTestBundle/EntityTestBundleXmlAnonTest.php b/core/modules/system/tests/modules/entity_test/tests/src/Functional/Rest/EntityTestBundleXmlAnonTest.php
similarity index 86%
rename from core/modules/rest/tests/src/Functional/EntityResource/EntityTestBundle/EntityTestBundleXmlAnonTest.php
rename to core/modules/system/tests/modules/entity_test/tests/src/Functional/Rest/EntityTestBundleXmlAnonTest.php
index 2b9927485..e88314b42 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/EntityTestBundle/EntityTestBundleXmlAnonTest.php
+++ b/core/modules/system/tests/modules/entity_test/tests/src/Functional/Rest/EntityTestBundleXmlAnonTest.php
@@ -1,6 +1,6 @@
grantPermissionsToTestedRole(['view test entity']);
+ break;
+ case 'POST':
+ $this->grantPermissionsToTestedRole([
+ 'administer entity_test content',
+ 'administer entity_test_with_bundle content',
+ 'create entity_test entity_test_with_bundle entities',
+ ]);
+ break;
+ case 'PATCH':
+ case 'DELETE':
+ $this->grantPermissionsToTestedRole(['administer entity_test content']);
+ break;
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function createEntity() {
+ $entity_test_label = EntityTestLabel::create([
+ 'name' => 'label_llama',
+ ]);
+ $entity_test_label->setOwnerId(0);
+ $entity_test_label->save();
+ return $entity_test_label;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedNormalizedEntity() {
+ $author = User::load(0);
+ $normalization = [
+ 'uuid' => [
+ [
+ 'value' => $this->entity->uuid(),
+ ],
+ ],
+ 'id' => [
+ [
+ 'value' => (int) $this->entity->id(),
+ ],
+ ],
+ 'langcode' => [
+ [
+ 'value' => 'en',
+ ],
+ ],
+ 'type' => [
+ [
+ 'value' => 'entity_test_label',
+ ],
+ ],
+ 'name' => [
+ [
+ 'value' => 'label_llama',
+ ],
+ ],
+ 'created' => [
+ $this->formatExpectedTimestampItemValues((int) $this->entity->get('created')->value),
+ ],
+ 'user_id' => [
+ [
+ 'target_id' => (int) $author->id(),
+ 'target_type' => 'user',
+ 'target_uuid' => $author->uuid(),
+ 'url' => $author->toUrl()->toString(),
+ ],
+ ],
+ ];
+
+ return $normalization;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getNormalizedPostEntity() {
+ return [
+ 'type' => [
+ [
+ 'value' => 'entity_test_label',
+ ],
+ ],
+ 'name' => [
+ [
+ 'value' => 'label_llama',
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedCacheContexts() {
+ return ['user.permissions'];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedUnauthorizedAccessMessage($method) {
+ if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
+ return parent::getExpectedUnauthorizedAccessMessage($method);
+ }
+
+ switch ($method) {
+ case 'GET':
+ return "The 'view test entity' permission is required.";
+ case 'POST':
+ return "The following permissions are required: 'administer entity_test content' OR 'administer entity_test_with_bundle content' OR 'create entity_test_label entity_test_with_bundle entities'.";
+ case 'PATCH':
+ case 'DELETE':
+ return "The 'administer entity_test content' permission is required.";
+ default:
+ return parent::getExpectedUnauthorizedAccessMessage($method);
+ }
+ }
+
+}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityTestLabel/EntityTestLabelXmlAnonTest.php b/core/modules/system/tests/modules/entity_test/tests/src/Functional/Rest/EntityTestLabelXmlAnonTest.php
similarity index 87%
rename from core/modules/rest/tests/src/Functional/EntityResource/EntityTestLabel/EntityTestLabelXmlAnonTest.php
rename to core/modules/system/tests/modules/entity_test/tests/src/Functional/Rest/EntityTestLabelXmlAnonTest.php
index ef2005ab7..ac1bd8685 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/EntityTestLabel/EntityTestLabelXmlAnonTest.php
+++ b/core/modules/system/tests/modules/entity_test/tests/src/Functional/Rest/EntityTestLabelXmlAnonTest.php
@@ -1,6 +1,6 @@
'value',
+ 'key2' => 'no, val you',
+ 'π' => 3.14159,
+ TRUE => 42,
+ 'nested' => [
+ 'bird' => 'robin',
+ 'doll' => 'Russian',
+ ],
+ ];
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUpAuthorization($method) {
+ $this->grantPermissionsToTestedRole(['administer entity_test content']);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function createEntity() {
+ $entity = EntityTestMapField::create([
+ 'name' => 'Llama',
+ 'type' => 'entity_test_map_field',
+ 'data' => [
+ static::$mapValue,
+ ],
+ ]);
+ $entity->setOwnerId(0);
+ $entity->save();
+ return $entity;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedNormalizedEntity() {
+ $author = User::load(0);
+ return [
+ 'uuid' => [
+ [
+ 'value' => $this->entity->uuid(),
+ ],
+ ],
+ 'id' => [
+ [
+ 'value' => 1,
+ ],
+ ],
+ 'name' => [
+ [
+ 'value' => 'Llama',
+ ],
+ ],
+ 'langcode' => [
+ [
+ 'value' => 'en',
+ ],
+ ],
+ 'created' => [
+ $this->formatExpectedTimestampItemValues((int) $this->entity->get('created')->value),
+ ],
+ 'user_id' => [
+ [
+ 'target_id' => (int) $author->id(),
+ 'target_type' => 'user',
+ 'target_uuid' => $author->uuid(),
+ 'url' => $author->toUrl()->toString(),
+ ],
+ ],
+ 'data' => [
+ static::$mapValue,
+ ],
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getNormalizedPostEntity() {
+ return [
+ 'name' => [
+ [
+ 'value' => 'Dramallama',
+ ],
+ ],
+ 'data' => [
+ 0 => static::$mapValue,
+ ],
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedUnauthorizedAccessMessage($method) {
+ if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
+ return parent::getExpectedUnauthorizedAccessMessage($method);
+ }
+
+ return "The 'administer entity_test content' permission is required.";
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedCacheContexts() {
+ return ['user.permissions'];
+ }
+
+}
diff --git a/core/modules/system/tests/modules/entity_test/tests/src/Functional/Rest/EntityTestResourceTestBase.php b/core/modules/system/tests/modules/entity_test/tests/src/Functional/Rest/EntityTestResourceTestBase.php
new file mode 100644
index 000000000..2210f97d6
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_test/tests/src/Functional/Rest/EntityTestResourceTestBase.php
@@ -0,0 +1,163 @@
+grantPermissionsToTestedRole(['view test entity']);
+ break;
+ case 'POST':
+ $this->grantPermissionsToTestedRole(['create entity_test entity_test_with_bundle entities']);
+ break;
+ case 'PATCH':
+ case 'DELETE':
+ $this->grantPermissionsToTestedRole(['administer entity_test content']);
+ break;
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function createEntity() {
+ // Set flag so that internal field 'internal_string_field' is created.
+ // @see entity_test_entity_base_field_info()
+ $this->container->get('state')->set('entity_test.internal_field', TRUE);
+ \Drupal::entityDefinitionUpdateManager()->applyUpdates();
+
+ $entity_test = EntityTest::create([
+ 'name' => 'Llama',
+ 'type' => 'entity_test',
+ // Set a value for the internal field to confirm that it will not be
+ // returned in normalization.
+ // @see entity_test_entity_base_field_info().
+ 'internal_string_field' => [
+ 'value' => 'This value shall not be internal!',
+ ],
+ ]);
+ $entity_test->setOwnerId(0);
+ $entity_test->save();
+
+ return $entity_test;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedNormalizedEntity() {
+ $author = User::load(0);
+ $normalization = [
+ 'uuid' => [
+ [
+ 'value' => $this->entity->uuid(),
+ ],
+ ],
+ 'id' => [
+ [
+ 'value' => 1,
+ ],
+ ],
+ 'langcode' => [
+ [
+ 'value' => 'en',
+ ],
+ ],
+ 'type' => [
+ [
+ 'value' => 'entity_test',
+ ],
+ ],
+ 'name' => [
+ [
+ 'value' => 'Llama',
+ ],
+ ],
+ 'created' => [
+ $this->formatExpectedTimestampItemValues((int) $this->entity->get('created')->value),
+ ],
+ 'user_id' => [
+ [
+ 'target_id' => (int) $author->id(),
+ 'target_type' => 'user',
+ 'target_uuid' => $author->uuid(),
+ 'url' => $author->toUrl()->toString(),
+ ],
+ ],
+ 'field_test_text' => [],
+ ];
+
+ return $normalization;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getNormalizedPostEntity() {
+ return [
+ 'type' => [
+ [
+ 'value' => 'entity_test',
+ ],
+ ],
+ 'name' => [
+ [
+ 'value' => 'Dramallama',
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedUnauthorizedAccessMessage($method) {
+ if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
+ return parent::getExpectedUnauthorizedAccessMessage($method);
+ }
+
+ switch ($method) {
+ case 'GET':
+ return "The 'view test entity' permission is required.";
+ case 'POST':
+ return "The following permissions are required: 'administer entity_test content' OR 'administer entity_test_with_bundle content' OR 'create entity_test entity_test_with_bundle entities'.";
+ default:
+ return parent::getExpectedUnauthorizedAccessMessage($method);
+ }
+ }
+
+}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestTextItemNormalizerTest.php b/core/modules/system/tests/modules/entity_test/tests/src/Functional/Rest/EntityTestTextItemNormalizerTest.php
similarity index 98%
rename from core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestTextItemNormalizerTest.php
rename to core/modules/system/tests/modules/entity_test/tests/src/Functional/Rest/EntityTestTextItemNormalizerTest.php
index cd8654df4..3ae0044ae 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestTextItemNormalizerTest.php
+++ b/core/modules/system/tests/modules/entity_test/tests/src/Functional/Rest/EntityTestTextItemNormalizerTest.php
@@ -1,6 +1,6 @@
$i,
'name' => $i,
'test_single_property' => $i . ' - test single property',
diff --git a/core/modules/system/tests/modules/entity_test_update/src/Entity/EntityTestUpdate.php b/core/modules/system/tests/modules/entity_test_update/src/Entity/EntityTestUpdate.php
index 286e8e62b..631a89e54 100644
--- a/core/modules/system/tests/modules/entity_test_update/src/Entity/EntityTestUpdate.php
+++ b/core/modules/system/tests/modules/entity_test_update/src/Entity/EntityTestUpdate.php
@@ -13,7 +13,8 @@ use Drupal\Core\StringTranslation\TranslatableMarkup;
*
* This entity type starts out non-revisionable and non-translatable, but during
* an update test it can be made revisionable and translatable using the helper
- * methods from \Drupal\system\Tests\Entity\EntityDefinitionTestTrait.
+ * methods from
+ * \Drupal\Tests\system\Functional\Entity\Traits\EntityDefinitionTestTrait.
*
* @ContentEntityType(
* id = "entity_test_update",
diff --git a/core/modules/system/tests/modules/entity_test_update/src/EntityTestUpdateStorageSchema.php b/core/modules/system/tests/modules/entity_test_update/src/EntityTestUpdateStorageSchema.php
index 2b75e8309..f04466fbc 100644
--- a/core/modules/system/tests/modules/entity_test_update/src/EntityTestUpdateStorageSchema.php
+++ b/core/modules/system/tests/modules/entity_test_update/src/EntityTestUpdateStorageSchema.php
@@ -18,7 +18,7 @@ class EntityTestUpdateStorageSchema extends SqlContentEntityStorageSchema {
$schema = parent::getEntitySchema($entity_type, $reset);
if ($entity_type->id() == 'entity_test_update') {
- $schema[$entity_type->getBaseTable()]['indexes'] += \Drupal::state()->get('entity_test_update.additional_entity_indexes', []);
+ $schema[$this->storage->getBaseTable()]['indexes'] += \Drupal::state()->get('entity_test_update.additional_entity_indexes', []);
}
return $schema;
}
diff --git a/core/modules/system/tests/modules/entity_test_update/update/entity_rev_pub_updates_8400.inc b/core/modules/system/tests/modules/entity_test_update/update/entity_rev_pub_updates_8400.inc
index 0a4ed4fa1..7f2659716 100644
--- a/core/modules/system/tests/modules/entity_test_update/update/entity_rev_pub_updates_8400.inc
+++ b/core/modules/system/tests/modules/entity_test_update/update/entity_rev_pub_updates_8400.inc
@@ -8,41 +8,17 @@
use Drupal\Core\Field\BaseFieldDefinition;
/**
- * Implements hook_update_dependencies().
- */
-function entity_test_update_update_dependencies() {
- // The update function that adds the status field must run after
- // content_translation_update_8400() which fixes NULL values for the
- // 'content_translation_status' field.
- $dependencies['entity_test_update'][8400] = [
- 'content_translation' => 8400,
- ];
-
- return $dependencies;
-}
-
-/**
- * Add the 'published' and revisionable metadata fields to entity_test_update.
+ * Add the 'published' entity key to entity_test_update.
*/
function entity_test_update_update_8400() {
$definition_update_manager = \Drupal::entityDefinitionUpdateManager();
- // Add the published entity key and revisionable metadata fields to the
- // entity_test_update entity type.
+ // Add the published entity key to the entity_test_update entity type.
$entity_type = $definition_update_manager->getEntityType('entity_test_update');
$entity_keys = $entity_type->getKeys();
$entity_keys['published'] = 'status';
$entity_type->set('entity_keys', $entity_keys);
-
- $revision_metadata_keys = [
- 'revision_user' => 'revision_user',
- 'revision_created' => 'revision_created',
- 'revision_log_message' => 'revision_log_message',
- 'revision_default' => 'revision_default',
- ];
- $entity_type->set('revision_metadata_keys', $revision_metadata_keys);
-
$definition_update_manager->updateEntityType($entity_type);
// Add the status field.
@@ -55,41 +31,13 @@ function entity_test_update_update_8400() {
$has_content_translation_status_field = \Drupal::moduleHandler()->moduleExists('content_translation') && $definition_update_manager->getFieldStorageDefinition('content_translation_status', 'entity_test_update');
if ($has_content_translation_status_field) {
- $status->setInitialValueFromField('content_translation_status');
+ $status->setInitialValueFromField('content_translation_status', TRUE);
}
else {
$status->setInitialValue(TRUE);
}
$definition_update_manager->installFieldStorageDefinition('status', 'entity_test_update', 'entity_test_update', $status);
- // Add the revision metadata fields.
- $revision_created = BaseFieldDefinition::create('created')
- ->setLabel(t('Revision create time'))
- ->setDescription(t('The time that the current revision was created.'))
- ->setRevisionable(TRUE);
- $definition_update_manager->installFieldStorageDefinition('revision_created', 'entity_test_update', 'entity_test_update', $revision_created);
-
- $revision_user = BaseFieldDefinition::create('entity_reference')
- ->setLabel(t('Revision user'))
- ->setDescription(t('The user ID of the author of the current revision.'))
- ->setSetting('target_type', 'user')
- ->setRevisionable(TRUE);
- $definition_update_manager->installFieldStorageDefinition('revision_user', 'entity_test_update', 'entity_test_update', $revision_user);
-
- $revision_log_message = BaseFieldDefinition::create('string_long')
- ->setLabel(t('Revision log message'))
- ->setDescription(t('Briefly describe the changes you have made.'))
- ->setRevisionable(TRUE)
- ->setDefaultValue('')
- ->setDisplayOptions('form', [
- 'type' => 'string_textarea',
- 'weight' => 25,
- 'settings' => [
- 'rows' => 4,
- ],
- ]);
- $definition_update_manager->installFieldStorageDefinition('revision_log_message', 'entity_test_update', 'entity_test_update', $revision_log_message);
-
// Uninstall the 'content_translation_status' field if needed.
$database = \Drupal::database();
if ($has_content_translation_status_field) {
diff --git a/core/modules/system/tests/modules/error_test/src/Controller/ErrorTestController.php b/core/modules/system/tests/modules/error_test/src/Controller/ErrorTestController.php
index dd526b863..c64da6dc2 100644
--- a/core/modules/system/tests/modules/error_test/src/Controller/ErrorTestController.php
+++ b/core/modules/system/tests/modules/error_test/src/Controller/ErrorTestController.php
@@ -14,7 +14,7 @@ class ErrorTestController extends ControllerBase {
/**
* The database connection.
*
- * @var \Drupal\Core\Database\Connection;
+ * @var \Drupal\Core\Database\Connection
*/
protected $database;
@@ -88,7 +88,7 @@ class ErrorTestController extends ControllerBase {
'#post_render' => [
function () {
throw new \Exception('This is an exception that occurs during rendering');
- }
+ },
],
];
}
diff --git a/core/modules/system/tests/modules/experimental_module_dependency_test/experimental_module_dependency_test.info.yml b/core/modules/system/tests/modules/experimental_module_dependency_test/experimental_module_dependency_test.info.yml
index 665417fc4..f84f616c4 100644
--- a/core/modules/system/tests/modules/experimental_module_dependency_test/experimental_module_dependency_test.info.yml
+++ b/core/modules/system/tests/modules/experimental_module_dependency_test/experimental_module_dependency_test.info.yml
@@ -3,6 +3,6 @@ type: module
description: 'Module with a dependency in the experimental package.'
package: Testing
dependencies:
- - experimental_module_test
+ - drupal:experimental_module_test
version: VERSION
core: 8.x
diff --git a/core/modules/system/tests/modules/form_test/form_test.info.yml b/core/modules/system/tests/modules/form_test/form_test.info.yml
index 39d45eaaf..fdf1076ef 100644
--- a/core/modules/system/tests/modules/form_test/form_test.info.yml
+++ b/core/modules/system/tests/modules/form_test/form_test.info.yml
@@ -4,3 +4,6 @@ description: 'Support module for Form API tests.'
package: Testing
version: VERSION
core: 8.x
+dependencies:
+ - file
+ - filter
diff --git a/core/modules/system/tests/modules/form_test/form_test.module b/core/modules/system/tests/modules/form_test/form_test.module
index e3a5e3f28..14e011877 100644
--- a/core/modules/system/tests/modules/form_test/form_test.module
+++ b/core/modules/system/tests/modules/form_test/form_test.module
@@ -11,7 +11,7 @@ use Drupal\Core\Form\FormStateInterface;
* Implements hook_form_FORM_ID_alter() on behalf of block.module.
*/
function block_form_form_test_alter_form_alter(&$form, FormStateInterface $form_state) {
- drupal_set_message('block_form_form_test_alter_form_alter() executed.');
+ \Drupal::messenger()->addStatus('block_form_form_test_alter_form_alter() executed.');
}
/**
@@ -19,7 +19,7 @@ function block_form_form_test_alter_form_alter(&$form, FormStateInterface $form_
*/
function form_test_form_alter(&$form, FormStateInterface $form_state, $form_id) {
if ($form_id == 'form_test_alter_form') {
- drupal_set_message('form_test_form_alter() executed.');
+ \Drupal::messenger()->addStatus('form_test_form_alter() executed.');
}
}
@@ -27,14 +27,14 @@ function form_test_form_alter(&$form, FormStateInterface $form_state, $form_id)
* Implements hook_form_FORM_ID_alter().
*/
function form_test_form_form_test_alter_form_alter(&$form, FormStateInterface $form_state) {
- drupal_set_message('form_test_form_form_test_alter_form_alter() executed.');
+ \Drupal::messenger()->addStatus('form_test_form_form_test_alter_form_alter() executed.');
}
/**
* Implements hook_form_FORM_ID_alter() on behalf of system.module.
*/
function system_form_form_test_alter_form_alter(&$form, FormStateInterface $form_state) {
- drupal_set_message('system_form_form_test_alter_form_alter() executed.');
+ \Drupal::messenger()->addStatus('system_form_form_test_alter_form_alter() executed.');
}
/**
@@ -90,7 +90,7 @@ function form_test_form_user_register_form_alter(&$form, FormStateInterface $for
* Submit callback that just lets the form rebuild.
*/
function form_test_user_register_form_rebuild($form, FormStateInterface $form_state) {
- drupal_set_message('Form rebuilt.');
+ \Drupal::messenger()->addStatus('Form rebuilt.');
$form_state->setRebuild();
}
diff --git a/core/modules/system/tests/modules/form_test/form_test.routing.yml b/core/modules/system/tests/modules/form_test/form_test.routing.yml
index 9e178cd51..f2de12647 100644
--- a/core/modules/system/tests/modules/form_test/form_test.routing.yml
+++ b/core/modules/system/tests/modules/form_test/form_test.routing.yml
@@ -498,6 +498,14 @@ form_test.get_form:
requirements:
_access: 'TRUE'
+form_test.machine_name_validation:
+ path: '/form-test/form-test-machine-name-validation'
+ defaults:
+ _form: '\Drupal\form_test\Form\FormTestMachineNameValidationForm'
+ _title: 'Form machine name validation test'
+ requirements:
+ _access: 'TRUE'
+
form_test.optional_container:
path: '/form-test/optional-container'
defaults:
diff --git a/core/modules/system/tests/modules/form_test/src/Callbacks.php b/core/modules/system/tests/modules/form_test/src/Callbacks.php
index 0310a37e5..4af6f2e70 100644
--- a/core/modules/system/tests/modules/form_test/src/Callbacks.php
+++ b/core/modules/system/tests/modules/form_test/src/Callbacks.php
@@ -38,7 +38,7 @@ class Callbacks {
if ($triggered) {
// Output the element's value from $form_state.
- drupal_set_message(t('@label value: @value', ['@label' => $element['#title'], '@value' => $form_state->getValue('name')]));
+ \Drupal::messenger()->addStatus(t('@label value: @value', ['@label' => $element['#title'], '@value' => $form_state->getValue('name')]));
// Trigger a form validation error to see our changes.
$form_state->setErrorByName('');
diff --git a/core/modules/system/tests/modules/form_test/src/ConfirmFormTestForm.php b/core/modules/system/tests/modules/form_test/src/ConfirmFormTestForm.php
index c5d4cef94..089661fe4 100644
--- a/core/modules/system/tests/modules/form_test/src/ConfirmFormTestForm.php
+++ b/core/modules/system/tests/modules/form_test/src/ConfirmFormTestForm.php
@@ -68,7 +68,7 @@ class ConfirmFormTestForm extends ConfirmFormBase {
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
- drupal_set_message($this->t('The ConfirmFormTestForm::submitForm() method was used for this form.'));
+ $this->messenger()->addStatus($this->t('The ConfirmFormTestForm::submitForm() method was used for this form.'));
$form_state->setRedirect('
');
}
diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestClickedButtonForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestClickedButtonForm.php
index e95244c47..b54770804 100644
--- a/core/modules/system/tests/modules/form_test/src/Form/FormTestClickedButtonForm.php
+++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestClickedButtonForm.php
@@ -83,10 +83,10 @@ class FormTestClickedButtonForm extends FormBase {
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
if ($triggering_element = $form_state->getTriggeringElement()) {
- drupal_set_message(t('The clicked button is %name.', ['%name' => $triggering_element['#name']]));
+ $this->messenger()->addStatus(t('The clicked button is %name.', ['%name' => $triggering_element['#name']]));
}
else {
- drupal_set_message('There is no clicked button.');
+ $this->messenger()->addStatus('There is no clicked button.');
}
}
@@ -94,7 +94,7 @@ class FormTestClickedButtonForm extends FormBase {
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
- drupal_set_message('Submit handler for form_test_clicked_button executed.');
+ $this->messenger()->addStatus('Submit handler for form_test_clicked_button executed.');
}
}
diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestGetForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestGetForm.php
index a032771e9..bf9ce60dc 100644
--- a/core/modules/system/tests/modules/form_test/src/Form/FormTestGetForm.php
+++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestGetForm.php
@@ -35,7 +35,7 @@ class FormTestGetForm extends FormBase {
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
- drupal_set_message('The form_test_get_form form has been submitted successfully.');
+ $this->messenger()->addStatus('The form_test_get_form form has been submitted successfully.');
}
}
diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestGroupDetailsForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestGroupDetailsForm.php
index 12a05e12d..32722fdb0 100644
--- a/core/modules/system/tests/modules/form_test/src/Form/FormTestGroupDetailsForm.php
+++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestGroupDetailsForm.php
@@ -39,6 +39,13 @@ class FormTestGroupDetailsForm extends FormBase {
'#type' => 'textfield',
'#title' => 'Nest in details element',
];
+ $form['summary_attributes'] = [
+ '#type' => 'details',
+ '#title' => 'Details element with summary attributes',
+ '#summary_attributes' => [
+ 'data-summary-attribute' => 'test',
+ ],
+ ];
return $form;
}
diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestLabelForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestLabelForm.php
index 88de23008..a22bf7d09 100644
--- a/core/modules/system/tests/modules/form_test/src/Form/FormTestLabelForm.php
+++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestLabelForm.php
@@ -12,6 +12,40 @@ use Drupal\Core\Form\FormStateInterface;
*/
class FormTestLabelForm extends FormBase {
+ /**
+ * An array of elements that render a title.
+ *
+ * @var array
+ */
+ public static $typesWithTitle = [
+ 'checkbox',
+ 'checkboxes',
+ 'color',
+ 'date',
+ 'datelist',
+ 'datetime',
+ 'details',
+ 'email',
+ 'fieldset',
+ 'file',
+ 'item',
+ 'managed_file',
+ 'number',
+ 'password',
+ 'password_confirm',
+ 'radio',
+ 'radios',
+ 'range',
+ 'search',
+ 'select',
+ 'tel',
+ 'textarea',
+ 'textfield',
+ 'text_format',
+ 'url',
+ 'weight',
+ ];
+
/**
* {@inheritdoc}
*/
@@ -125,6 +159,21 @@ class FormTestLabelForm extends FormBase {
],
'#required' => TRUE,
];
+
+ foreach (static::$typesWithTitle as $type) {
+ $form['form_' . $type . '_title_no_xss'] = [
+ '#type' => $type,
+ '#title' => "$type is XSS filtered!",
+ ];
+ // Add keys that are required for some elements to be processed correctly.
+ if (in_array($type, ['checkboxes', 'radios'], TRUE)) {
+ $form['form_' . $type . '_title_no_xss']['#options'] = [];
+ }
+ if ($type === 'datetime') {
+ $form['form_' . $type . '_title_no_xss']['#default_value'] = NULL;
+ }
+ }
+
return $form;
}
diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestLimitValidationErrorsForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestLimitValidationErrorsForm.php
index 883602217..8f6531f5c 100644
--- a/core/modules/system/tests/modules/form_test/src/Form/FormTestLimitValidationErrorsForm.php
+++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestLimitValidationErrorsForm.php
@@ -104,7 +104,7 @@ class FormTestLimitValidationErrorsForm extends FormBase {
// The title has not been validated, thus its value - in case of the test case
// an empty string - may not be set.
if (!$form_state->hasValue('title') && $form_state->hasValue('test')) {
- drupal_set_message('Only validated values appear in the form values.');
+ $this->messenger()->addStatus('Only validated values appear in the form values.');
}
}
diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestMachineNameForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestMachineNameForm.php
index ee287f4ce..121cd31ae 100644
--- a/core/modules/system/tests/modules/form_test/src/Form/FormTestMachineNameForm.php
+++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestMachineNameForm.php
@@ -33,7 +33,7 @@ class FormTestMachineNameForm extends FormBase {
'#title' => 'Machine name 1',
'#description' => 'A machine name.',
'#machine_name' => [
- 'source' => ['machine_name_1_label']
+ 'source' => ['machine_name_1_label'],
],
];
$form['machine_name_2_label'] = [
@@ -45,7 +45,7 @@ class FormTestMachineNameForm extends FormBase {
'#title' => 'Machine name 2',
'#description' => 'Another machine name.',
'#machine_name' => [
- 'source' => ['machine_name_2_label']
+ 'source' => ['machine_name_2_label'],
],
];
$form['submit'] = [
diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestMachineNameValidationForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestMachineNameValidationForm.php
new file mode 100644
index 000000000..ff2dcaac5
--- /dev/null
+++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestMachineNameValidationForm.php
@@ -0,0 +1,141 @@
+ 'textfield',
+ '#default_value' => $form_state->getValue('name'),
+ '#maxlength' => 50,
+ '#required' => TRUE,
+ '#title' => 'Name',
+ ];
+
+ // The default value simulates how an entity form works, which has default
+ // values based on an entity, which is updated in an afterBuild callback.
+ // During validation and after build, limit_validation_errors is not
+ // in effect, which means that getValue('id') does return a value, while it
+ // does not during the submit callback. Therefore, this test sets the value
+ // in ::buildAjaxSnackConfigureFormValidate() and then uses that as the
+ // default value, so that the default value and the value are identical.
+ $form['id'] = [
+ '#type' => 'machine_name',
+ '#default_value' => $form_state->get('id'),
+ '#maxlength' => 50,
+ '#required' => TRUE,
+ '#machine_name' => [
+ 'exists' => [$this, 'load'],
+ 'source' => ['name'],
+ ],
+ ];
+
+ // Test support for multiple machine names on the form. Although this has
+ // the default value duplicate it should not generate an error because it
+ // is the default value.
+ $form['id2'] = [
+ '#type' => 'machine_name',
+ '#default_value' => 'duplicate',
+ '#maxlength' => 50,
+ '#required' => TRUE,
+ '#machine_name' => [
+ 'exists' => [$this, 'load'],
+ 'source' => ['name'],
+ ],
+ ];
+
+ $form['snack'] = [
+ '#type' => 'select',
+ '#title' => $this->t('Select a snack'),
+ '#options' => [
+ 'apple' => 'apple',
+ 'pear' => 'pear',
+ 'potato' => 'potato',
+ ],
+ '#required' => TRUE,
+ '#ajax' => [
+ 'callback' => '::buildAjaxSnackConfigureForm',
+ 'wrapper' => 'snack-config-form',
+ 'method' => 'replace',
+ 'effect' => 'fade',
+ ],
+ ];
+ $form['snack_configs'] = [
+ '#type' => 'container',
+ '#attributes' => [
+ 'id' => 'snack-config-form',
+ ],
+ '#tree' => TRUE,
+ ];
+
+ $form['submit'] = [
+ '#type' => 'submit',
+ '#value' => 'Save',
+ ];
+ return $form;
+ }
+
+ /**
+ * Validate callback that forces a form rebuild.
+ */
+ public function buildAjaxSnackConfigureFormValidate(array $form, FormStateInterface $form_state) {
+ $form_state->set('id', $form_state->getValue('id'));
+ }
+
+ /**
+ * Submit callback that forces a form rebuild.
+ */
+ public function buildAjaxSnackConfigureFormSubmit(array $form, FormStateInterface $form_state) {
+ $form_state->setRebuild();
+ }
+
+ /**
+ * Handles changes to the selected snack configuration.
+ */
+ public function buildAjaxSnackConfigureForm(array $form, FormStateInterface $form_state) {
+ return $form['snack_configs'];
+ }
+
+ /**
+ * Loading stub for machine name
+ *
+ * @param $machine_name
+ * @return bool
+ */
+ public function load($machine_name) {
+ if (strpos($machine_name, 'duplicate') !== FALSE) {
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function submitForm(array &$form, FormStateInterface $form_state) {
+ $this->messenger()->addStatus('The form_test_machine_name_validation_form form has been submitted successfully.');
+ }
+
+}
diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestRebuildPreserveValuesForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestRebuildPreserveValuesForm.php
index 662de8f7d..b1054abf0 100644
--- a/core/modules/system/tests/modules/form_test/src/Form/FormTestRebuildPreserveValuesForm.php
+++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestRebuildPreserveValuesForm.php
@@ -94,7 +94,7 @@ class FormTestRebuildPreserveValuesForm extends FormBase {
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
// Finish the workflow. Do not rebuild.
- drupal_set_message(t('Form values: %values', ['%values' => var_export($form_state->getValues(), TRUE)]));
+ $this->messenger()->addStatus($this->t('Form values: %values', ['%values' => var_export($form_state->getValues(), TRUE)]));
}
}
diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestStatePersistForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestStatePersistForm.php
index af7f51cf8..5f34bba4d 100644
--- a/core/modules/system/tests/modules/form_test/src/Form/FormTestStatePersistForm.php
+++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestStatePersistForm.php
@@ -42,7 +42,7 @@ class FormTestStatePersistForm extends FormBase {
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
- drupal_set_message($form_state->get('value'));
+ $this->messenger()->addStatus($form_state->get('value'));
$form_state->setRebuild();
}
diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestStorageForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestStorageForm.php
index f81658d53..5cc09b728 100644
--- a/core/modules/system/tests/modules/form_test/src/Form/FormTestStorageForm.php
+++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestStorageForm.php
@@ -50,7 +50,7 @@ class FormTestStorageForm extends FormBase {
}
// Count how often the form is constructed.
$_SESSION['constructions']++;
- drupal_set_message("Form constructions: " . $_SESSION['constructions']);
+ $this->messenger()->addStatus("Form constructions: " . $_SESSION['constructions']);
$form['title'] = [
'#type' => 'textfield',
@@ -136,10 +136,10 @@ class FormTestStorageForm extends FormBase {
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
- drupal_set_message("Title: " . Html::escape($form_state->getValue('title')));
- drupal_set_message("Form constructions: " . $_SESSION['constructions']);
+ $this->messenger()->addStatus("Title: " . Html::escape($form_state->getValue('title')));
+ $this->messenger()->addStatus("Form constructions: " . $_SESSION['constructions']);
if ($form_state->has(['thing', 'changed'])) {
- drupal_set_message("The thing has been changed.");
+ $this->messenger()->addStatus("The thing has been changed.");
}
$form_state->setRedirect('');
}
diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestTableSelectMultipleFalseForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestTableSelectMultipleFalseForm.php
index d79b235e0..31db2ebf3 100644
--- a/core/modules/system/tests/modules/form_test/src/Form/FormTestTableSelectMultipleFalseForm.php
+++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestTableSelectMultipleFalseForm.php
@@ -29,7 +29,7 @@ class FormTestTableSelectMultipleFalseForm extends FormTestTableSelectFormBase {
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
- drupal_set_message(t('Submitted: @value', ['@value' => $form_state->getValue('tableselect')]));
+ $this->messenger()->addStatus($this->t('Submitted: @value', ['@value' => $form_state->getValue('tableselect')]));
}
}
diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestTableSelectMultipleTrueForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestTableSelectMultipleTrueForm.php
index 1d46d7cc6..abfa38035 100644
--- a/core/modules/system/tests/modules/form_test/src/Form/FormTestTableSelectMultipleTrueForm.php
+++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestTableSelectMultipleTrueForm.php
@@ -31,7 +31,7 @@ class FormTestTableSelectMultipleTrueForm extends FormTestTableSelectFormBase {
public function submitForm(array &$form, FormStateInterface $form_state) {
$selected = $form_state->getValue('tableselect');
foreach ($selected as $key => $value) {
- drupal_set_message(t('Submitted: @key = @value', ['@key' => $key, '@value' => $value]));
+ $this->messenger()->addStatus($this->t('Submitted: @key = @value', ['@key' => $key, '@value' => $value]));
}
}
diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestValidateForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestValidateForm.php
index 7123aceef..a662e8505 100644
--- a/core/modules/system/tests/modules/form_test/src/Form/FormTestValidateForm.php
+++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestValidateForm.php
@@ -59,7 +59,7 @@ class FormTestValidateForm extends FormBase {
// Alter the submitted value in $form_state.
$form_state->setValueForElement($form['name'], 'value changed by setValueForElement() in #validate');
// Output the element's value from $form_state.
- drupal_set_message(t('@label value: @value', ['@label' => $form['name']['#title'], '@value' => $form_state->getValue('name')]));
+ $this->messenger()->addStatus($this->t('@label value: @value', ['@label' => $form['name']['#title'], '@value' => $form_state->getValue('name')]));
// Trigger a form validation error to see our changes.
$form_state->setErrorByName('');
diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestValidateNoToken.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestValidateNoToken.php
index c4b568edd..7a5e7ff03 100644
--- a/core/modules/system/tests/modules/form_test/src/Form/FormTestValidateNoToken.php
+++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestValidateNoToken.php
@@ -35,7 +35,7 @@ class FormTestValidateNoToken extends FormBase {
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
- drupal_set_message('The form_test_validate_no_token form has been submitted successfully.');
+ $this->messenger()->addStatus('The form_test_validate_no_token form has been submitted successfully.');
}
}
diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestValidateRequiredForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestValidateRequiredForm.php
index 13aec600a..91be7b1e7 100644
--- a/core/modules/system/tests/modules/form_test/src/Form/FormTestValidateRequiredForm.php
+++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestValidateRequiredForm.php
@@ -84,7 +84,7 @@ class FormTestValidateRequiredForm extends FormBase {
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
- drupal_set_message('The form_test_validate_required_form form was submitted successfully.');
+ $this->messenger()->addStatus('The form_test_validate_required_form form was submitted successfully.');
}
}
diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestValidateRequiredNoTitleForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestValidateRequiredNoTitleForm.php
index 1829ab256..fd1c25123 100644
--- a/core/modules/system/tests/modules/form_test/src/Form/FormTestValidateRequiredNoTitleForm.php
+++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestValidateRequiredNoTitleForm.php
@@ -36,7 +36,7 @@ class FormTestValidateRequiredNoTitleForm extends FormBase {
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
- drupal_set_message('The form_test_validate_required_form_no_title form was submitted successfully.');
+ $this->messenger()->addStatus('The form_test_validate_required_form_no_title form was submitted successfully.');
}
}
diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestVerticalTabsAccessForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestVerticalTabsAccessForm.php
index 1b343fccc..c2d062a64 100644
--- a/core/modules/system/tests/modules/form_test/src/Form/FormTestVerticalTabsAccessForm.php
+++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestVerticalTabsAccessForm.php
@@ -130,7 +130,7 @@ class FormTestVerticalTabsAccessForm extends FormBase {
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
- drupal_set_message(t('The form submitted correctly.'));
+ $this->messenger()->addStatus(t('The form submitted correctly.'));
}
}
diff --git a/core/modules/system/tests/modules/form_test/src/FormTestArgumentsObject.php b/core/modules/system/tests/modules/form_test/src/FormTestArgumentsObject.php
index 0d86a2d7a..8667ca9a6 100644
--- a/core/modules/system/tests/modules/form_test/src/FormTestArgumentsObject.php
+++ b/core/modules/system/tests/modules/form_test/src/FormTestArgumentsObject.php
@@ -50,14 +50,14 @@ class FormTestArgumentsObject extends ConfigFormBase {
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
- drupal_set_message($this->t('The FormTestArgumentsObject::validateForm() method was used for this form.'));
+ $this->messenger()->addStatus($this->t('The FormTestArgumentsObject::validateForm() method was used for this form.'));
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
- drupal_set_message($this->t('The FormTestArgumentsObject::submitForm() method was used for this form.'));
+ $this->messenger()->addStatus($this->t('The FormTestArgumentsObject::submitForm() method was used for this form.'));
$this->config('form_test.object')
->set('bananas', $form_state->getValue('bananas'))
->save();
diff --git a/core/modules/system/tests/modules/form_test/src/FormTestControllerObject.php b/core/modules/system/tests/modules/form_test/src/FormTestControllerObject.php
index 40d474d7a..428e9cef3 100644
--- a/core/modules/system/tests/modules/form_test/src/FormTestControllerObject.php
+++ b/core/modules/system/tests/modules/form_test/src/FormTestControllerObject.php
@@ -32,7 +32,7 @@ class FormTestControllerObject extends ConfigFormBase {
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
- drupal_set_message(t('The FormTestControllerObject::create() method was used for this form.'));
+ \Drupal::messenger()->addStatus(t('The FormTestControllerObject::create() method was used for this form.'));
return new static(
$container->get('config.factory')
);
@@ -64,14 +64,14 @@ class FormTestControllerObject extends ConfigFormBase {
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
- drupal_set_message($this->t('The FormTestControllerObject::validateForm() method was used for this form.'));
+ $this->messenger()->addStatus($this->t('The FormTestControllerObject::validateForm() method was used for this form.'));
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
- drupal_set_message($this->t('The FormTestControllerObject::submitForm() method was used for this form.'));
+ $this->messenger()->addStatus($this->t('The FormTestControllerObject::submitForm() method was used for this form.'));
$this->config('form_test.object')
->set('bananas', $form_state->getValue('bananas'))
->save();
diff --git a/core/modules/system/tests/modules/form_test/src/FormTestObject.php b/core/modules/system/tests/modules/form_test/src/FormTestObject.php
index d7fd119c7..3db579e94 100644
--- a/core/modules/system/tests/modules/form_test/src/FormTestObject.php
+++ b/core/modules/system/tests/modules/form_test/src/FormTestObject.php
@@ -57,14 +57,14 @@ class FormTestObject extends ConfigFormBase {
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
- drupal_set_message($this->t('The FormTestObject::validateForm() method was used for this form.'));
+ $this->messenger()->addStatus($this->t('The FormTestObject::validateForm() method was used for this form.'));
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
- drupal_set_message($this->t('The FormTestObject::submitForm() method was used for this form.'));
+ $this->messenger()->addStatus($this->t('The FormTestObject::submitForm() method was used for this form.'));
$this->config('form_test.object')
->set('bananas', $form_state->getValue('bananas'))
->save();
diff --git a/core/modules/system/tests/modules/form_test/src/FormTestServiceObject.php b/core/modules/system/tests/modules/form_test/src/FormTestServiceObject.php
index c1d855555..f4a89bf9f 100644
--- a/core/modules/system/tests/modules/form_test/src/FormTestServiceObject.php
+++ b/core/modules/system/tests/modules/form_test/src/FormTestServiceObject.php
@@ -50,14 +50,14 @@ class FormTestServiceObject extends ConfigFormBase {
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
- drupal_set_message($this->t('The FormTestServiceObject::validateForm() method was used for this form.'));
+ $this->messenger()->addStatus($this->t('The FormTestServiceObject::validateForm() method was used for this form.'));
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
- drupal_set_message($this->t('The FormTestServiceObject::submitForm() method was used for this form.'));
+ $this->messenger()->addStatus($this->t('The FormTestServiceObject::submitForm() method was used for this form.'));
$this->config('form_test.object', FALSE)
->set('bananas', $form_state->getValue('bananas'))
->save();
diff --git a/core/modules/system/tests/modules/hold_test/hold_test.info.yml b/core/modules/system/tests/modules/hold_test/hold_test.info.yml
new file mode 100644
index 000000000..f76775142
--- /dev/null
+++ b/core/modules/system/tests/modules/hold_test/hold_test.info.yml
@@ -0,0 +1,6 @@
+name: Hold test
+type: module
+description: 'Support testing with request/response hold.'
+package: Testing
+version: VERSION
+core: 8.x
diff --git a/core/modules/system/tests/modules/hold_test/hold_test.install b/core/modules/system/tests/modules/hold_test/hold_test.install
new file mode 100644
index 000000000..183463deb
--- /dev/null
+++ b/core/modules/system/tests/modules/hold_test/hold_test.install
@@ -0,0 +1,14 @@
+hold(static::HOLD_REQUEST);
+ }
+
+ /**
+ * Response hold.
+ */
+ public function onRespond() {
+ $this->hold(static::HOLD_RESPONSE);
+ }
+
+ /**
+ * Hold process by type.
+ *
+ * @param string $type
+ * Type of hold.
+ */
+ protected function hold($type) {
+ $path = \Drupal::root() . "/sites/default/files/simpletest/hold_test_$type.txt";
+ do {
+ $status = (bool) file_get_contents($path);
+ } while ($status && (NULL === usleep(100000)));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function getSubscribedEvents() {
+ $events[KernelEvents::REQUEST][] = ['onRequest'];
+ $events[KernelEvents::RESPONSE][] = ['onRespond'];
+ return $events;
+ }
+
+}
diff --git a/core/modules/system/tests/modules/image_test/src/Plugin/ImageToolkit/TestToolkit.php b/core/modules/system/tests/modules/image_test/src/Plugin/ImageToolkit/TestToolkit.php
index a0f508041..48e072240 100644
--- a/core/modules/system/tests/modules/image_test/src/Plugin/ImageToolkit/TestToolkit.php
+++ b/core/modules/system/tests/modules/image_test/src/Plugin/ImageToolkit/TestToolkit.php
@@ -2,7 +2,6 @@
namespace Drupal\image_test\Plugin\ImageToolkit;
-use Drupal\Component\Utility\Unicode;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\ImageToolkit\ImageToolkitBase;
@@ -238,7 +237,7 @@ class TestToolkit extends ImageToolkitBase {
public static function getSupportedExtensions() {
$extensions = [];
foreach (static::supportedTypes() as $image_type) {
- $extensions[] = Unicode::strtolower(image_type_to_extension($image_type, FALSE));
+ $extensions[] = mb_strtolower(image_type_to_extension($image_type, FALSE));
}
return $extensions;
}
diff --git a/core/modules/system/tests/modules/js_ajax_test/js/js_ajax_test.ajax.es6.js b/core/modules/system/tests/modules/js_ajax_test/js/js_ajax_test.ajax.es6.js
index 627d2e38c..116bad83f 100644
--- a/core/modules/system/tests/modules/js_ajax_test/js/js_ajax_test.ajax.es6.js
+++ b/core/modules/system/tests/modules/js_ajax_test/js/js_ajax_test.ajax.es6.js
@@ -2,7 +2,7 @@
* @file
* Testing behavior for JSWebAssertTest.
*/
-(function ($, Drupal) {
+(function($, Drupal) {
/**
* Test Ajax command.
*
@@ -13,7 +13,7 @@
* @param {string} response.selector
* A jQuery selector string.
*/
- Drupal.AjaxCommands.prototype.jsAjaxTestCommand = function (ajax, response) {
+ Drupal.AjaxCommands.prototype.jsAjaxTestCommand = function(ajax, response) {
const $domElement = $(response.selector);
ajax.element_settings.cat = 'catbro';
@@ -22,6 +22,8 @@
elementSettings: ajax.elementSettings.cat || {},
};
- $domElement.html(`${JSON.stringify(data)}
`);
+ $domElement.html(
+ `${JSON.stringify(data)}
`,
+ );
};
-}(jQuery, Drupal));
+})(jQuery, Drupal);
diff --git a/core/modules/system/tests/modules/js_webassert_test/js/js_webassert_test.wait_for_ajax_request.es6.js b/core/modules/system/tests/modules/js_webassert_test/js/js_webassert_test.wait_for_ajax_request.es6.js
index d2e1fea2c..afed63107 100644
--- a/core/modules/system/tests/modules/js_webassert_test/js/js_webassert_test.wait_for_ajax_request.es6.js
+++ b/core/modules/system/tests/modules/js_webassert_test/js/js_webassert_test.wait_for_ajax_request.es6.js
@@ -3,7 +3,7 @@
* Testing behavior for JSWebAssertTest.
*/
-(function ($, Drupal, drupalSettings) {
+(function($, Drupal, drupalSettings) {
/**
* @type {Drupal~behavior}
*
@@ -12,7 +12,9 @@
*/
Drupal.behaviors.js_webassert_test_wait_for_ajax_request = {
attach(context) {
- $('input[name="test_assert_wait_on_ajax_input"]').val('js_webassert_test');
+ $('input[name="test_assert_wait_on_ajax_input"]').val(
+ 'js_webassert_test',
+ );
},
};
-}(jQuery, Drupal, drupalSettings));
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/modules/system/tests/modules/js_webassert_test/js/js_webassert_test.wait_for_element.es6.js b/core/modules/system/tests/modules/js_webassert_test/js/js_webassert_test.wait_for_element.es6.js
index 10d911b9a..cd59bc73e 100644
--- a/core/modules/system/tests/modules/js_webassert_test/js/js_webassert_test.wait_for_element.es6.js
+++ b/core/modules/system/tests/modules/js_webassert_test/js/js_webassert_test.wait_for_element.es6.js
@@ -3,7 +3,7 @@
* Testing behavior for JSWebAssertTest.
*/
-(function ($, Drupal, drupalSettings) {
+(function($, Drupal, drupalSettings) {
/**
* @type {Drupal~behavior}
*
@@ -15,4 +15,4 @@
$('#js_webassert_test_element_invisible').show();
},
};
-}(jQuery, Drupal, drupalSettings));
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/modules/system/tests/modules/js_webassert_test/src/Form/JsWebAssertTestForm.php b/core/modules/system/tests/modules/js_webassert_test/src/Form/JsWebAssertTestForm.php
index d46db774d..3197aa5be 100644
--- a/core/modules/system/tests/modules/js_webassert_test/src/Form/JsWebAssertTestForm.php
+++ b/core/modules/system/tests/modules/js_webassert_test/src/Form/JsWebAssertTestForm.php
@@ -135,10 +135,11 @@ class JsWebAssertTestForm extends FormBase {
$form['added_link'] = [
'#title' => 'Added link',
'#type' => 'link',
- '#url' => Url::fromRoute('js_webassert_test.js_webassert_test_form')
+ '#url' => Url::fromRoute('js_webassert_test.js_webassert_test_form'),
];
return $form;
}
+
/**
* Ajax callback for the "Add field" button.
*/
@@ -179,7 +180,6 @@ class JsWebAssertTestForm extends FormBase {
return $form;
}
-
/**
* Ajax callback for the "Test waitForElementVisible" button.
*/
diff --git a/core/modules/system/tests/modules/keyvalue_test/keyvalue_test.info.yml b/core/modules/system/tests/modules/keyvalue_test/keyvalue_test.info.yml
index 01319ed01..1da31bcc5 100644
--- a/core/modules/system/tests/modules/keyvalue_test/keyvalue_test.info.yml
+++ b/core/modules/system/tests/modules/keyvalue_test/keyvalue_test.info.yml
@@ -6,4 +6,4 @@ package: Testing
version: VERSION
hidden: true
dependencies:
- - entity_test
+ - drupal:entity_test
diff --git a/core/modules/system/tests/modules/menu_test/menu_test.info.yml b/core/modules/system/tests/modules/menu_test/menu_test.info.yml
index dbba9b866..3d082e325 100644
--- a/core/modules/system/tests/modules/menu_test/menu_test.info.yml
+++ b/core/modules/system/tests/modules/menu_test/menu_test.info.yml
@@ -5,5 +5,5 @@ package: Testing
version: VERSION
core: 8.x
dependencies:
- - test_page_test
- - menu_ui
+ - drupal:test_page_test
+ - drupal:menu_ui
diff --git a/core/modules/system/tests/modules/menu_test/src/Controller/MenuTestController.php b/core/modules/system/tests/modules/menu_test/src/Controller/MenuTestController.php
index fe6566cdd..8472b99a1 100644
--- a/core/modules/system/tests/modules/menu_test/src/Controller/MenuTestController.php
+++ b/core/modules/system/tests/modules/menu_test/src/Controller/MenuTestController.php
@@ -71,7 +71,6 @@ class MenuTestController extends ControllerBase {
return ['#markup' => 'This is the menuTestCallback content.'];
}
-
/**
* A title callback method for test routes.
*
diff --git a/core/modules/system/tests/modules/menu_test/src/TestControllers.php b/core/modules/system/tests/modules/menu_test/src/TestControllers.php
index 31fb341af..dafaab0e5 100644
--- a/core/modules/system/tests/modules/menu_test/src/TestControllers.php
+++ b/core/modules/system/tests/modules/menu_test/src/TestControllers.php
@@ -2,7 +2,7 @@
namespace Drupal\menu_test;
-use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Component\Render\FormattableMarkup;
/**
* Controllers for testing the menu integration routing system.
@@ -38,7 +38,7 @@ class TestControllers {
$_SESSION['menu_test'] = 0;
}
$_SESSION['menu_test']++;
- return ['#markup' => SafeMarkup::format('Session menu_test is @count', ['@count' => $_SESSION['menu_test']])];
+ return ['#markup' => new FormattableMarkup('Session menu_test is @count', ['@count' => $_SESSION['menu_test']])];
}
/**
@@ -59,7 +59,7 @@ class TestControllers {
*/
public function testDefaults($placeholder = NULL) {
if ($placeholder) {
- return ['#markup' => SafeMarkup::format("Sometimes there is a placeholder: '@placeholder'.", ['@placeholder' => $placeholder])];
+ return ['#markup' => new FormattableMarkup("Sometimes there is a placeholder: '@placeholder'.", ['@placeholder' => $placeholder])];
}
else {
return ['#markup' => 'Sometimes there is no placeholder.'];
@@ -77,9 +77,9 @@ class TestControllers {
'#contextual_links' => [
'menu_test_menu' => [
'route_parameters' => ['bar' => 1],
- ]
- ]
- ]
+ ],
+ ],
+ ],
];
}
diff --git a/core/modules/system/tests/modules/module_autoload_test/src/SomeClass.php b/core/modules/system/tests/modules/module_autoload_test/src/SomeClass.php
index 6cd97a0cb..11993b71f 100644
--- a/core/modules/system/tests/modules/module_autoload_test/src/SomeClass.php
+++ b/core/modules/system/tests/modules/module_autoload_test/src/SomeClass.php
@@ -3,6 +3,7 @@
namespace Drupal\module_autoload_test;
class SomeClass {
+
public function testMethod() {
return 'Drupal\\module_autoload_test\\SomeClass::testMethod() was invoked.';
}
diff --git a/core/modules/system/tests/modules/module_handler_test_multiple/module_handler_test_multiple_child/module_handler_test_multiple_child.info.yml b/core/modules/system/tests/modules/module_handler_test_multiple/module_handler_test_multiple_child/module_handler_test_multiple_child.info.yml
index b4b07f9c0..794d624a6 100644
--- a/core/modules/system/tests/modules/module_handler_test_multiple/module_handler_test_multiple_child/module_handler_test_multiple_child.info.yml
+++ b/core/modules/system/tests/modules/module_handler_test_multiple/module_handler_test_multiple_child/module_handler_test_multiple_child.info.yml
@@ -6,4 +6,4 @@ version: VERSION
core: 8.x
hidden: true
dependencies:
- - module_handler_test_multiple
+ - drupal:module_handler_test_multiple
diff --git a/core/modules/system/tests/modules/module_install_class_loader_test2/module_install_class_loader_test2.info.yml b/core/modules/system/tests/modules/module_install_class_loader_test2/module_install_class_loader_test2.info.yml
index 5154c9bf0..b2a677c5c 100644
--- a/core/modules/system/tests/modules/module_install_class_loader_test2/module_install_class_loader_test2.info.yml
+++ b/core/modules/system/tests/modules/module_install_class_loader_test2/module_install_class_loader_test2.info.yml
@@ -5,4 +5,4 @@ package: Testing
core: 8.x
version: VERSION
dependencies:
- - module_install_class_loader_test1
+ - drupal:module_install_class_loader_test1
diff --git a/core/modules/system/tests/modules/module_installer_config_test/config/schema/module_installer_config_test.schema.yml b/core/modules/system/tests/modules/module_installer_config_test/config/schema/module_installer_config_test.schema.yml
index cb39936d7..7674b136d 100644
--- a/core/modules/system/tests/modules/module_installer_config_test/config/schema/module_installer_config_test.schema.yml
+++ b/core/modules/system/tests/modules/module_installer_config_test/config/schema/module_installer_config_test.schema.yml
@@ -7,4 +7,3 @@ module_installer_config_test.type.*:
name:
type: label
label: 'Name'
-
diff --git a/core/modules/system/tests/modules/module_installer_config_test/module_installer_config_test.info.yml b/core/modules/system/tests/modules/module_installer_config_test/module_installer_config_test.info.yml
index 78f3d88e0..4c600b68b 100644
--- a/core/modules/system/tests/modules/module_installer_config_test/module_installer_config_test.info.yml
+++ b/core/modules/system/tests/modules/module_installer_config_test/module_installer_config_test.info.yml
@@ -4,4 +4,3 @@ type: module
package: Testing
core: 8.x
version: VERSION
-
diff --git a/core/modules/system/tests/modules/off_canvas_test/src/Controller/TestController.php b/core/modules/system/tests/modules/off_canvas_test/src/Controller/TestController.php
index ea310fa0b..fbfa5a7bd 100644
--- a/core/modules/system/tests/modules/off_canvas_test/src/Controller/TestController.php
+++ b/core/modules/system/tests/modules/off_canvas_test/src/Controller/TestController.php
@@ -45,17 +45,22 @@ class TestController {
public function linksDisplay() {
return [
'off_canvas_link_1' => [
- '#title' => 'Click Me 1!',
+ '#title' => 'Open side panel 1',
'#type' => 'link',
'#url' => Url::fromRoute('off_canvas_test.thing1'),
'#attributes' => [
'class' => ['use-ajax'],
'data-dialog-type' => 'dialog',
'data-dialog-renderer' => 'off_canvas',
+ 'data-dialog-options' => Json::encode([
+ 'classes' => [
+ "ui-dialog" => "ui-corner-all side-1",
+ ],
+ ]),
],
],
'off_canvas_link_2' => [
- '#title' => 'Click Me 2!',
+ '#title' => 'Open side panel 2',
'#type' => 'link',
'#url' => Url::fromRoute('off_canvas_test.thing2'),
'#attributes' => [
@@ -64,6 +69,43 @@ class TestController {
'data-dialog-renderer' => 'off_canvas',
'data-dialog-options' => Json::encode([
'width' => 555,
+ 'classes' => [
+ "ui-dialog" => "ui-corner-all side-2",
+ ],
+ ]),
+ ],
+ ],
+ 'off_canvas_top_link_1' => [
+ '#title' => 'Open top panel 1',
+ '#type' => 'link',
+ '#url' => Url::fromRoute('off_canvas_test.thing1'),
+ '#attributes' => [
+ 'class' => ['use-ajax'],
+ 'data-dialog-type' => 'dialog',
+ 'data-dialog-renderer' => 'off_canvas_top',
+ 'data-dialog-options' => Json::encode([
+ 'width' => 555,
+ 'classes' => [
+ "ui-dialog" => "ui-corner-all top-1",
+ ],
+ ]),
+ ],
+
+ ],
+ 'off_canvas_top_link_2' => [
+ '#title' => 'Open top panel 2',
+ '#type' => 'link',
+ '#url' => Url::fromRoute('off_canvas_test.thing2'),
+ '#attributes' => [
+ 'class' => ['use-ajax'],
+ 'data-dialog-type' => 'dialog',
+ 'data-dialog-renderer' => 'off_canvas_top',
+ 'data-dialog-options' => Json::encode([
+ 'height' => 421,
+ 'classes' => [
+ "ui-dialog" => "ui-corner-all top-2",
+ ],
+
]),
],
],
diff --git a/core/modules/system/tests/modules/pager_test/src/Controller/PagerTestController.php b/core/modules/system/tests/modules/pager_test/src/Controller/PagerTestController.php
index 1964f740f..4f1fdad64 100644
--- a/core/modules/system/tests/modules/pager_test/src/Controller/PagerTestController.php
+++ b/core/modules/system/tests/modules/pager_test/src/Controller/PagerTestController.php
@@ -69,7 +69,7 @@ class PagerTestController extends ControllerBase {
],
'#pre_render' => [
'Drupal\pager_test\Controller\PagerTestController::showPagerCacheContext',
- ]
+ ],
];
return $build;
@@ -118,7 +118,7 @@ class PagerTestController extends ControllerBase {
* #pre_render callback for #type => pager that shows the pager cache context.
*/
public static function showPagerCacheContext(array $pager) {
- drupal_set_message(\Drupal::service('cache_contexts_manager')->convertTokensToKeys(['url.query_args.pagers:' . $pager['#element']])->getKeys()[0]);
+ \Drupal::messenger()->addStatus(\Drupal::service('cache_contexts_manager')->convertTokensToKeys(['url.query_args.pagers:' . $pager['#element']])->getKeys()[0]);
return $pager;
}
diff --git a/core/modules/system/tests/modules/plugin_test/src/Plugin/MockBlockManager.php b/core/modules/system/tests/modules/plugin_test/src/Plugin/MockBlockManager.php
index 443ab5d04..de9fc3364 100644
--- a/core/modules/system/tests/modules/plugin_test/src/Plugin/MockBlockManager.php
+++ b/core/modules/system/tests/modules/plugin_test/src/Plugin/MockBlockManager.php
@@ -7,11 +7,13 @@ use Drupal\Component\Plugin\Discovery\StaticDiscovery;
use Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator;
use Drupal\Component\Plugin\Factory\ReflectionFactory;
use Drupal\Core\Plugin\Context\ContextDefinition;
+use Drupal\Core\Plugin\Context\EntityContextDefinition;
/**
* Defines a plugin manager used by Plugin API derivative unit tests.
*/
class MockBlockManager extends PluginManagerBase {
+
public function __construct() {
// Create the object that can be used to return definitions for all the
@@ -139,7 +141,11 @@ class MockBlockManager extends PluginManagerBase {
// a TranslatableMarkup and we will do assertEqual() checks on arrays that
// include ContextDefinition objects, and var_export() has problems
// printing TranslatableMarkup objects.
- return new ContextDefinition($data_type, (string) $label, $required);
+ $class = ContextDefinition::class;
+ if (strpos($data_type, 'entity:') === 0) {
+ $class = EntityContextDefinition::class;
+ }
+ return new $class($data_type, (string) $label, $required);
}
}
diff --git a/core/modules/system/tests/modules/plugin_test/src/Plugin/TestPluginManager.php b/core/modules/system/tests/modules/plugin_test/src/Plugin/TestPluginManager.php
index ec464a5b7..d044bafcc 100644
--- a/core/modules/system/tests/modules/plugin_test/src/Plugin/TestPluginManager.php
+++ b/core/modules/system/tests/modules/plugin_test/src/Plugin/TestPluginManager.php
@@ -10,6 +10,7 @@ use Drupal\Component\Plugin\Factory\DefaultFactory;
* Defines a plugin manager used by Plugin API unit tests.
*/
class TestPluginManager extends PluginManagerBase {
+
public function __construct() {
// Create the object that can be used to return definitions for all the
diff --git a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/mock_block/MockMenuBlockDeriver.php b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/mock_block/MockMenuBlockDeriver.php
index d5f6d6c29..b8aedac36 100644
--- a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/mock_block/MockMenuBlockDeriver.php
+++ b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/mock_block/MockMenuBlockDeriver.php
@@ -45,7 +45,7 @@ class MockMenuBlockDeriver implements DeriverInterface {
// Instead of the derivative label, the specific label will be used.
'label' => t('Derivative label'),
// This setting will be merged in.
- 'setting' => 'default'
+ 'setting' => 'default',
] + $base_plugin_definition,
];
diff --git a/core/modules/system/tests/modules/render_attached_test/render_attached_test.info.yml b/core/modules/system/tests/modules/render_attached_test/render_attached_test.info.yml
index 111fb9625..7662e3077 100644
--- a/core/modules/system/tests/modules/render_attached_test/render_attached_test.info.yml
+++ b/core/modules/system/tests/modules/render_attached_test/render_attached_test.info.yml
@@ -5,4 +5,4 @@ package: Testing
version: VERSION
core: 8.x
dependencies:
- - block
+ - drupal:block
diff --git a/core/modules/system/tests/modules/render_placeholder_message_test/src/RenderPlaceholderMessageTestController.php b/core/modules/system/tests/modules/render_placeholder_message_test/src/RenderPlaceholderMessageTestController.php
index 4a4d15d89..ddb76e6dc 100644
--- a/core/modules/system/tests/modules/render_placeholder_message_test/src/RenderPlaceholderMessageTestController.php
+++ b/core/modules/system/tests/modules/render_placeholder_message_test/src/RenderPlaceholderMessageTestController.php
@@ -91,7 +91,7 @@ class RenderPlaceholderMessageTestController implements ContainerAwareInterface
*/
public static function setAndLogMessage($message) {
// Set message.
- drupal_set_message($message);
+ \Drupal::messenger()->addStatus($message);
// Print which message is expected.
return ['#markup' => 'Message: ' . $message . '
'];
diff --git a/core/modules/system/tests/modules/requirements2_test/requirements2_test.info.yml b/core/modules/system/tests/modules/requirements2_test/requirements2_test.info.yml
index 670df8091..4c1a367db 100644
--- a/core/modules/system/tests/modules/requirements2_test/requirements2_test.info.yml
+++ b/core/modules/system/tests/modules/requirements2_test/requirements2_test.info.yml
@@ -2,8 +2,8 @@ name: 'Requirements 2 Test'
type: module
description: 'Tests that a module is not installed when the one it depends on fails hook_requirements(''install).'
dependencies:
- - requirements1_test
- - comment
+ - drupal:requirements1_test
+ - drupal:comment
package: Testing
version: VERSION
core: 8.x
diff --git a/core/modules/system/tests/modules/service_provider_test/src/TestClass.php b/core/modules/system/tests/modules/service_provider_test/src/TestClass.php
index d521e1fdb..c4dad8595 100644
--- a/core/modules/system/tests/modules/service_provider_test/src/TestClass.php
+++ b/core/modules/system/tests/modules/service_provider_test/src/TestClass.php
@@ -36,7 +36,7 @@ class TestClass implements EventSubscriberInterface, DestructableInterface, Cont
* A simple kernel listener method.
*/
public function onKernelRequestTest(GetResponseEvent $event) {
- drupal_set_message(t('The service_provider_test event subscriber fired!'));
+ \Drupal::messenger()->addStatus(t('The service_provider_test event subscriber fired!'));
}
/**
diff --git a/core/modules/system/tests/modules/session_test/src/Controller/SessionTestController.php b/core/modules/system/tests/modules/session_test/src/Controller/SessionTestController.php
index 76a546294..1094522ea 100644
--- a/core/modules/system/tests/modules/session_test/src/Controller/SessionTestController.php
+++ b/core/modules/system/tests/modules/session_test/src/Controller/SessionTestController.php
@@ -110,7 +110,7 @@ class SessionTestController extends ControllerBase {
* A notification message.
*/
public function setMessage() {
- drupal_set_message($this->t('This is a dummy message.'));
+ $this->messenger()->addStatus($this->t('This is a dummy message.'));
return new Response($this->t('A message was set.'));
// Do not return anything, so the current request does not result in a themed
// page with messages. The message will be displayed in the following request
diff --git a/core/modules/system/tests/modules/session_test/src/Form/SessionTestForm.php b/core/modules/system/tests/modules/session_test/src/Form/SessionTestForm.php
index c09237ac6..b324c9729 100644
--- a/core/modules/system/tests/modules/session_test/src/Form/SessionTestForm.php
+++ b/core/modules/system/tests/modules/session_test/src/Form/SessionTestForm.php
@@ -2,7 +2,7 @@
namespace Drupal\session_test\Form;
-use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
@@ -43,7 +43,7 @@ class SessionTestForm extends FormBase {
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
- drupal_set_message(SafeMarkup::format('Ok: @input', ['@input' => $form_state->getValue('input')]));
+ $this->messenger()->addStatus(new FormattableMarkup('Ok: @input', ['@input' => $form_state->getValue('input')]));
}
}
diff --git a/core/modules/system/tests/modules/system_dependencies_test/system_dependencies_test.info.yml b/core/modules/system/tests/modules/system_dependencies_test/system_dependencies_test.info.yml
index e99d48e4a..fae988033 100644
--- a/core/modules/system/tests/modules/system_dependencies_test/system_dependencies_test.info.yml
+++ b/core/modules/system/tests/modules/system_dependencies_test/system_dependencies_test.info.yml
@@ -5,4 +5,4 @@ package: Testing
version: VERSION
core: 8.x
dependencies:
- - _missing_dependency
+ - drupal:_missing_dependency
diff --git a/core/modules/system/tests/modules/system_incompatible_core_version_dependencies_test/system_incompatible_core_version_dependencies_test.info.yml b/core/modules/system/tests/modules/system_incompatible_core_version_dependencies_test/system_incompatible_core_version_dependencies_test.info.yml
index 148bd3b71..449980e7d 100644
--- a/core/modules/system/tests/modules/system_incompatible_core_version_dependencies_test/system_incompatible_core_version_dependencies_test.info.yml
+++ b/core/modules/system/tests/modules/system_incompatible_core_version_dependencies_test/system_incompatible_core_version_dependencies_test.info.yml
@@ -5,4 +5,4 @@ package: Testing
version: VERSION
core: 8.x
dependencies:
- - system_incompatible_core_version_test
+ - drupal:system_incompatible_core_version_test
diff --git a/core/modules/system/tests/modules/system_incompatible_module_version_dependencies_test/system_incompatible_module_version_dependencies_test.info.yml b/core/modules/system/tests/modules/system_incompatible_module_version_dependencies_test/system_incompatible_module_version_dependencies_test.info.yml
index 55e004b0d..cafa61c9b 100644
--- a/core/modules/system/tests/modules/system_incompatible_module_version_dependencies_test/system_incompatible_module_version_dependencies_test.info.yml
+++ b/core/modules/system/tests/modules/system_incompatible_module_version_dependencies_test/system_incompatible_module_version_dependencies_test.info.yml
@@ -5,4 +5,4 @@ package: Testing
version: VERSION
core: 8.x
dependencies:
- - 'system_incompatible_module_version_test (>2.0)'
+ - 'drupal:system_incompatible_module_version_test (>2.0)'
diff --git a/core/modules/system/tests/modules/system_module_test/system_module_test.install b/core/modules/system/tests/modules/system_module_test/system_module_test.install
new file mode 100644
index 000000000..d1bcde1d7
--- /dev/null
+++ b/core/modules/system/tests/modules/system_module_test/system_module_test.install
@@ -0,0 +1,15 @@
+markup! (not removed).'));
+ $this->messenger->addStatus('First message (removed).');
+ $this->messenger->addStatus($this->t('Second message with markup! (not removed).'));
$messages = $this->messenger->deleteByType('status');
// Remove the first.
unset($messages[0]);
@@ -120,27 +120,27 @@ class SystemTestController extends ControllerBase {
}
// Duplicate message check.
- drupal_set_message('Non Duplicated message', 'status', FALSE);
- drupal_set_message('Non Duplicated message', 'status', FALSE);
+ $this->messenger->addStatus('Non Duplicated message');
+ $this->messenger->addStatus('Non Duplicated message');
- drupal_set_message('Duplicated message', 'status', TRUE);
- drupal_set_message('Duplicated message', 'status', TRUE);
+ $this->messenger->addStatus('Duplicated message', TRUE);
+ $this->messenger->addStatus('Duplicated message', TRUE);
// Add a Markup message.
- drupal_set_message(Markup::create('Markup with markup!'));
+ $this->messenger->addStatus(Markup::create('Markup with markup!'));
// Test duplicate Markup messages.
- drupal_set_message(Markup::create('Markup with markup!'));
+ $this->messenger->addStatus(Markup::create('Markup with markup!'));
// Ensure that multiple Markup messages work.
- drupal_set_message(Markup::create('Markup2 with markup!'));
+ $this->messenger->addStatus(Markup::create('Markup2 with markup!'));
// Test mixing of types.
- drupal_set_message(Markup::create('Non duplicate Markup / string.'));
- drupal_set_message('Non duplicate Markup / string.');
- drupal_set_message(Markup::create('Duplicate Markup / string.'), 'status', TRUE);
- drupal_set_message('Duplicate Markup / string.', 'status', TRUE);
+ $this->messenger->addStatus(Markup::create('Non duplicate Markup / string.'));
+ $this->messenger->addStatus('Non duplicate Markup / string.');
+ $this->messenger->addStatus(Markup::create('Duplicate Markup / string.'), TRUE);
+ $this->messenger->addStatus('Duplicate Markup / string.', TRUE);
// Test auto-escape of non safe strings.
- drupal_set_message('Thismarkup will be escaped.');
+ $this->messenger->addStatus('Thismarkup will be escaped.');
return [];
}
diff --git a/core/modules/system/tests/modules/system_test/system_test.module b/core/modules/system/tests/modules/system_test/system_test.module
index 81739086c..affc20ea4 100644
--- a/core/modules/system/tests/modules/system_test/system_test.module
+++ b/core/modules/system/tests/modules/system_test/system_test.module
@@ -27,7 +27,7 @@ function system_test_help($route_name, RouteMatchInterface $route_match) {
function system_test_modules_installed($modules) {
if (\Drupal::state()->get('system_test.verbose_module_hooks')) {
foreach ($modules as $module) {
- drupal_set_message(t('hook_modules_installed fired for @module', ['@module' => $module]));
+ \Drupal::messenger()->addStatus(t('hook_modules_installed fired for @module', ['@module' => $module]));
}
}
}
@@ -38,7 +38,7 @@ function system_test_modules_installed($modules) {
function system_test_modules_uninstalled($modules) {
if (\Drupal::state()->get('system_test.verbose_module_hooks')) {
foreach ($modules as $module) {
- drupal_set_message(t('hook_modules_uninstalled fired for @module', ['@module' => $module]));
+ \Drupal::messenger()->addStatus(t('hook_modules_uninstalled fired for @module', ['@module' => $module]));
}
}
}
@@ -90,7 +90,7 @@ function system_test_page_attachments(array &$page) {
// \Drupal::service('path.matcher')->isFrontPage().
$frontpage = \Drupal::state()->get('system_test.front_page_output') ?: 0;
if ($frontpage && \Drupal::service('path.matcher')->isFrontPage()) {
- drupal_set_message(t('On front page.'));
+ \Drupal::messenger()->addStatus(t('On front page.'));
}
}
diff --git a/core/modules/system/tests/modules/system_test/system_test.routing.yml b/core/modules/system/tests/modules/system_test/system_test.routing.yml
index b7cba071e..b47174faa 100644
--- a/core/modules/system/tests/modules/system_test/system_test.routing.yml
+++ b/core/modules/system/tests/modules/system_test/system_test.routing.yml
@@ -13,11 +13,11 @@ system_test.main_content_handling:
requirements:
_access: 'TRUE'
-system_test.drupal_set_message:
- path: '/system-test/drupal-set-message'
+system_test.messenger_service:
+ path: '/system-test/messenger-service'
defaults:
- _title: 'Set messages with drupal_set_message()'
- _controller: '\Drupal\system_test\Controller\SystemTestController::drupalSetMessageTest'
+ _title: 'Set messages with Messenger service'
+ _controller: '\Drupal\system_test\Controller\SystemTestController::messengerServiceTest'
requirements:
_access: 'TRUE'
diff --git a/core/modules/system/tests/modules/test_page_test/src/Controller/Test.php b/core/modules/system/tests/modules/test_page_test/src/Controller/Test.php
index e754eaf0c..8867b8ba2 100644
--- a/core/modules/system/tests/modules/test_page_test/src/Controller/Test.php
+++ b/core/modules/system/tests/modules/test_page_test/src/Controller/Test.php
@@ -15,7 +15,8 @@ class Test {
* Renders a page with a title.
*
* @return array
- * A render array as expected by drupal_render()
+ * A render array as expected by
+ * \Drupal\Core\Render\RendererInterface::render().
*/
public function renderTitle() {
$build = [];
@@ -29,7 +30,8 @@ class Test {
* Renders a page.
*
* @return array
- * A render array as expected by drupal_render().
+ * A render array as expected by
+ * \Drupal\Core\Render\RendererInterface::render().
*/
public function staticTitle() {
$build = [];
@@ -66,7 +68,8 @@ class Test {
* Returns a generic page render array for title tests.
*
* @return array
- * A render array as expected by drupal_render()
+ * A render array as expected by
+ * \Drupal\Core\Render\RendererInterface::render().
*/
public function renderPage() {
return [
@@ -95,7 +98,8 @@ class Test {
* Renders a page with encoded markup.
*
* @return array
- * A render array as expected by drupal_render()
+ * A render array as expected by
+ * \Drupal\Core\Render\RendererInterface::render().
*/
public function renderEncodedMarkup() {
return ['#plain_text' => 'Bad html '];
@@ -105,7 +109,8 @@ class Test {
* Renders a page with pipe character in link test.
*
* @return array
- * A render array as expected by drupal_render()
+ * A render array as expected by
+ * \Drupal\Core\Render\RendererInterface::render().
*/
public function renderPipeInLink() {
return ['#markup' => 'foo|bar|baz'];
diff --git a/core/modules/system/tests/modules/theme_suggestions_test/theme_suggestions_test.module b/core/modules/system/tests/modules/theme_suggestions_test/theme_suggestions_test.module
index 50fb86d2b..cbc5b3802 100644
--- a/core/modules/system/tests/modules/theme_suggestions_test/theme_suggestions_test.module
+++ b/core/modules/system/tests/modules/theme_suggestions_test/theme_suggestions_test.module
@@ -21,7 +21,7 @@ function theme_suggestions_test_theme() {
* Implements hook_theme_suggestions_alter().
*/
function theme_suggestions_test_theme_suggestions_alter(array &$suggestions, array $variables, $hook) {
- drupal_set_message(__FUNCTION__ . '() executed.');
+ \Drupal::messenger()->addStatus(__FUNCTION__ . '() executed.');
if ($hook == 'theme_test_general_suggestions') {
$suggestions[] = $hook . '__module_override';
}
@@ -31,7 +31,7 @@ function theme_suggestions_test_theme_suggestions_alter(array &$suggestions, arr
* Implements hook_theme_suggestions_HOOK_alter().
*/
function theme_suggestions_test_theme_suggestions_theme_test_suggestions_alter(array &$suggestions, array $variables) {
- drupal_set_message(__FUNCTION__ . '() executed.');
+ \Drupal::messenger()->addStatus(__FUNCTION__ . '() executed.');
$suggestions[] = 'theme_test_suggestions__' . 'module_override';
}
diff --git a/core/modules/system/tests/modules/theme_test/src/ThemeTestController.php b/core/modules/system/tests/modules/theme_test/src/ThemeTestController.php
index f1ed8f3a9..c9a939000 100644
--- a/core/modules/system/tests/modules/theme_test/src/ThemeTestController.php
+++ b/core/modules/system/tests/modules/theme_test/src/ThemeTestController.php
@@ -119,7 +119,6 @@ class ThemeTestController extends ControllerBase {
return ['#theme' => 'theme_test_function_suggestions'];
}
-
/**
* Menu callback for testing includes with suggestion alter hooks.
*/
diff --git a/core/modules/system/tests/modules/theme_test/theme_test.module b/core/modules/system/tests/modules/theme_test/theme_test.module
index cd8aba7da..901449696 100644
--- a/core/modules/system/tests/modules/theme_test/theme_test.module
+++ b/core/modules/system/tests/modules/theme_test/theme_test.module
@@ -178,14 +178,14 @@ function theme_test_theme_suggestions_theme_test_suggestion_provided(array $vari
* Implements hook_theme_suggestions_alter().
*/
function theme_test_theme_suggestions_alter(array &$suggestions, array $variables, $hook) {
- drupal_set_message(__FUNCTION__ . '() executed.');
+ \Drupal::messenger()->addStatus(__FUNCTION__ . '() executed for ' . $hook . '.');
}
/**
* Implements hook_theme_suggestions_HOOK_alter().
*/
function theme_test_theme_suggestions_theme_test_suggestions_alter(array &$suggestions, array $variables) {
- drupal_set_message(__FUNCTION__ . '() executed.');
+ \Drupal::messenger()->addStatus(__FUNCTION__ . '() executed.');
}
/**
diff --git a/core/modules/system/tests/modules/token_test/token_test.info.yml b/core/modules/system/tests/modules/token_test/token_test.info.yml
index e243f95d1..8f3bc473f 100644
--- a/core/modules/system/tests/modules/token_test/token_test.info.yml
+++ b/core/modules/system/tests/modules/token_test/token_test.info.yml
@@ -4,5 +4,5 @@ core: 8.x
package: Testing
version: VERSION
dependencies:
- - user
- - node
+ - drupal:user
+ - drupal:node
diff --git a/core/modules/system/tests/modules/twig_extension_test/src/TwigExtensionTestController.php b/core/modules/system/tests/modules/twig_extension_test/src/TwigExtensionTestController.php
index c15b16d8b..c51d3c898 100644
--- a/core/modules/system/tests/modules/twig_extension_test/src/TwigExtensionTestController.php
+++ b/core/modules/system/tests/modules/twig_extension_test/src/TwigExtensionTestController.php
@@ -20,8 +20,8 @@ class TwigExtensionTestController {
'#safe_join_items' => [
'will be escaped',
$this->t('will be markup'),
- ['#markup' => 'will be rendered']
- ]
+ ['#markup' => 'will be rendered'],
+ ],
];
}
diff --git a/core/modules/system/tests/modules/twig_theme_test/src/TwigThemeTestController.php b/core/modules/system/tests/modules/twig_theme_test/src/TwigThemeTestController.php
index 8e1f69e79..1e995c58f 100644
--- a/core/modules/system/tests/modules/twig_theme_test/src/TwigThemeTestController.php
+++ b/core/modules/system/tests/modules/twig_theme_test/src/TwigThemeTestController.php
@@ -100,7 +100,7 @@ class TwigThemeTestController {
public function renderable() {
return [
'#theme' => 'twig_theme_test_renderable',
- '#renderable' => new ExampleRenderable()
+ '#renderable' => new ExampleRenderable(),
];
}
diff --git a/core/modules/system/tests/modules/update_script_test/config/schema/update_script_test.schema.yml b/core/modules/system/tests/modules/update_script_test/config/schema/update_script_test.schema.yml
index cc4031d29..20eefc0c7 100644
--- a/core/modules/system/tests/modules/update_script_test/config/schema/update_script_test.schema.yml
+++ b/core/modules/system/tests/modules/update_script_test/config/schema/update_script_test.schema.yml
@@ -7,4 +7,3 @@ update_script_test.settings:
requirement_type:
type: integer
label: 'Requirement type'
-
diff --git a/core/modules/system/tests/modules/update_script_test/update_script_test.module b/core/modules/system/tests/modules/update_script_test/update_script_test.module
index 986dc31ab..096f91251 100644
--- a/core/modules/system/tests/modules/update_script_test/update_script_test.module
+++ b/core/modules/system/tests/modules/update_script_test/update_script_test.module
@@ -14,5 +14,5 @@
* @see UpdateScriptFunctionalTest::testRequirements()
*/
function update_script_test_cache_flush() {
- drupal_set_message(t('hook_cache_flush() invoked for update_script_test.module.'));
+ \Drupal::messenger()->addStatus(t('hook_cache_flush() invoked for update_script_test.module.'));
}
diff --git a/core/modules/system/tests/modules/update_script_test/update_script_test.services.yml b/core/modules/system/tests/modules/update_script_test/update_script_test.services.yml
index 82446eda3..7598774a7 100644
--- a/core/modules/system/tests/modules/update_script_test/update_script_test.services.yml
+++ b/core/modules/system/tests/modules/update_script_test/update_script_test.services.yml
@@ -4,4 +4,3 @@ services:
arguments: ['@state']
tags:
- { name: path_processor_inbound, priority: 1000 }
-
diff --git a/core/modules/system/tests/modules/update_test_schema/update_test_schema.install b/core/modules/system/tests/modules/update_test_schema/update_test_schema.install
index 972f90176..eef6b255a 100644
--- a/core/modules/system/tests/modules/update_test_schema/update_test_schema.install
+++ b/core/modules/system/tests/modules/update_test_schema/update_test_schema.install
@@ -31,6 +31,7 @@ function update_test_schema_schema() {
$schema_version = \Drupal::state()->get('update_test_schema_version', 8000);
if ($schema_version >= 8001) {
+
/**
* Schema version 8001.
*/
@@ -45,4 +46,5 @@ if ($schema_version >= 8001) {
// Add a column.
db_add_index('update_test_schema_table', 'test', ['a'], $table);
}
+
}
diff --git a/core/modules/system/tests/src/Functional/Ajax/OffCanvasDialogTest.php b/core/modules/system/tests/src/Functional/Ajax/OffCanvasDialogTest.php
index 222ebc45d..c78672212 100644
--- a/core/modules/system/tests/src/Functional/Ajax/OffCanvasDialogTest.php
+++ b/core/modules/system/tests/src/Functional/Ajax/OffCanvasDialogTest.php
@@ -23,8 +23,10 @@ class OffCanvasDialogTest extends BrowserTestBase {
/**
* Test sending AJAX requests to open and manipulate off-canvas dialog.
+ *
+ * @dataProvider dialogPosition
*/
- public function testDialog() {
+ public function testDialog($position) {
// Ensure the elements render without notices or exceptions.
$this->drupalGet('ajax-test/dialog');
@@ -45,8 +47,9 @@ class OffCanvasDialogTest extends BrowserTestBase {
'resizable' => 'w',
'draggable' => FALSE,
'drupalAutoButtons' => FALSE,
+ 'drupalOffCanvasPosition' => $position ?: 'side',
'buttons' => [],
- 'dialogClass' => 'ui-dialog-off-canvas',
+ 'dialogClass' => 'ui-dialog-off-canvas ui-dialog-position-' . ($position ?: 'side'),
'width' => 300,
],
'effect' => 'fade',
@@ -54,9 +57,23 @@ class OffCanvasDialogTest extends BrowserTestBase {
];
// Emulate going to the JS version of the page and check the JSON response.
- $ajax_result = $this->drupalGet('ajax-test/dialog-contents', ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_dialog.off_canvas']]);
+ $wrapper_format = $position && ($position !== 'side') ? 'drupal_dialog.off_canvas_' . $position : 'drupal_dialog.off_canvas';
+ $ajax_result = $this->drupalGet('ajax-test/dialog-contents', ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => $wrapper_format]]);
$ajax_result = Json::decode($ajax_result);
- $this->assertEqual($off_canvas_expected_response, $ajax_result[3], 'off-canvas dialog JSON response matches.');
+ $this->assertEquals($off_canvas_expected_response, $ajax_result[3], 'off-canvas dialog JSON response matches.');
+ }
+
+ /**
+ * The data provider for potential dialog positions.
+ *
+ * @return array
+ */
+ public static function dialogPosition() {
+ return [
+ [NULL],
+ ['side'],
+ ['top'],
+ ];
}
}
diff --git a/core/modules/system/tests/src/Functional/Batch/ProcessingTest.php b/core/modules/system/tests/src/Functional/Batch/ProcessingTest.php
index 2d7c7ae01..97dc6e7a5 100644
--- a/core/modules/system/tests/src/Functional/Batch/ProcessingTest.php
+++ b/core/modules/system/tests/src/Functional/Batch/ProcessingTest.php
@@ -175,7 +175,6 @@ class ProcessingTest extends BrowserTestBase {
$this->assertText('Redirection successful.', 'Redirection after batch execution is correct.');
}
-
/**
* Triggers a pass if the texts were found in order in the raw content.
*
diff --git a/core/modules/system/tests/src/Functional/Bootstrap/DrupalSetMessageTest.php b/core/modules/system/tests/src/Functional/Bootstrap/DrupalMessengerServiceTest.php
similarity index 73%
rename from core/modules/system/tests/src/Functional/Bootstrap/DrupalSetMessageTest.php
rename to core/modules/system/tests/src/Functional/Bootstrap/DrupalMessengerServiceTest.php
index 976e8bebf..51404ef18 100644
--- a/core/modules/system/tests/src/Functional/Bootstrap/DrupalSetMessageTest.php
+++ b/core/modules/system/tests/src/Functional/Bootstrap/DrupalMessengerServiceTest.php
@@ -2,14 +2,15 @@
namespace Drupal\Tests\system\Functional\Bootstrap;
+use Drupal\Core\Url;
use Drupal\Tests\BrowserTestBase;
/**
- * Tests drupal_set_message() and related functions.
+ * Tests the Messenger service.
*
* @group Bootstrap
*/
-class DrupalSetMessageTest extends BrowserTestBase {
+class DrupalMessengerServiceTest extends BrowserTestBase {
/**
* Modules to enable.
@@ -19,12 +20,12 @@ class DrupalSetMessageTest extends BrowserTestBase {
public static $modules = ['system_test'];
/**
- * Tests drupal_set_message().
+ * Tests Messenger service.
*/
- public function testDrupalSetMessage() {
- // The page at system-test/drupal-set-message sets two messages and then
- // removes the first before it is displayed.
- $this->drupalGet('system-test/drupal-set-message');
+ public function testDrupalMessengerService() {
+ // The page at system_test.messenger_service route sets two messages and
+ // then removes the first before it is displayed.
+ $this->drupalGet(Url::fromRoute('system_test.messenger_service'));
$this->assertNoText('First message (removed).');
$this->assertRaw(t('Second message with markup! (not removed).'));
diff --git a/core/modules/system/tests/src/Functional/Cache/PageCacheTagsTestBase.php b/core/modules/system/tests/src/Functional/Cache/PageCacheTagsTestBase.php
index 69afeb745..e70789916 100644
--- a/core/modules/system/tests/src/Functional/Cache/PageCacheTagsTestBase.php
+++ b/core/modules/system/tests/src/Functional/Cache/PageCacheTagsTestBase.php
@@ -4,7 +4,7 @@ namespace Drupal\Tests\system\Functional\Cache;
use Drupal\Core\Url;
use Drupal\Tests\BrowserTestBase;
-use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Component\Render\FormattableMarkup;
/**
* Provides helper methods for page cache tags tests.
@@ -44,7 +44,7 @@ abstract class PageCacheTagsTestBase extends BrowserTestBase {
*/
protected function verifyPageCache(Url $url, $hit_or_miss, $tags = FALSE) {
$this->drupalGet($url);
- $message = SafeMarkup::format('Page cache @hit_or_miss for %path.', ['@hit_or_miss' => $hit_or_miss, '%path' => $url->toString()]);
+ $message = new FormattableMarkup('Page cache @hit_or_miss for %path.', ['@hit_or_miss' => $hit_or_miss, '%path' => $url->toString()]);
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), $hit_or_miss, $message);
if ($hit_or_miss === 'HIT' && is_array($tags)) {
diff --git a/core/modules/system/src/Tests/Common/AlterTest.php b/core/modules/system/tests/src/Functional/Common/AlterTest.php
similarity index 95%
rename from core/modules/system/src/Tests/Common/AlterTest.php
rename to core/modules/system/tests/src/Functional/Common/AlterTest.php
index 863bd54de..e529e9d1c 100644
--- a/core/modules/system/src/Tests/Common/AlterTest.php
+++ b/core/modules/system/tests/src/Functional/Common/AlterTest.php
@@ -1,15 +1,15 @@
alter().
*
* @group Common
*/
-class AlterTest extends WebTestBase {
+class AlterTest extends BrowserTestBase {
/**
* Modules to enable.
diff --git a/core/modules/system/src/Tests/Common/EarlyRenderingControllerTest.php b/core/modules/system/tests/src/Functional/Common/EarlyRenderingControllerTest.php
similarity index 84%
rename from core/modules/system/src/Tests/Common/EarlyRenderingControllerTest.php
rename to core/modules/system/tests/src/Functional/Common/EarlyRenderingControllerTest.php
index 1afdd4eeb..9d6c0af5d 100644
--- a/core/modules/system/src/Tests/Common/EarlyRenderingControllerTest.php
+++ b/core/modules/system/tests/src/Functional/Common/EarlyRenderingControllerTest.php
@@ -1,16 +1,16 @@
drupalGet(Url::fromRoute('early_rendering_controller_test.response'));
$this->assertResponse(200);
$this->assertRaw('Hello world!');
- $this->assertNoCacheTag('foo');
+ $this->assertSession()->responseHeaderNotContains('X-Drupal-Cache-Tags', 'foo');
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.response.early'));
$this->assertResponse(200);
$this->assertRaw('Hello world!');
- $this->assertNoCacheTag('foo');
+ $this->assertSession()->responseHeaderNotContains('X-Drupal-Cache-Tags', 'foo');
// Response object with attachments: non-early & early.
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.response-with-attachments'));
$this->assertResponse(200);
$this->assertRaw('Hello world!');
- $this->assertNoCacheTag('foo');
+ $this->assertSession()->responseHeaderNotContains('X-Drupal-Cache-Tags', 'foo');
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.response-with-attachments.early'));
$this->assertResponse(500);
$this->assertRaw('The controller result claims to be providing relevant cache metadata, but leaked metadata was detected. Please ensure you are not rendering content too early. Returned object class: Drupal\early_rendering_controller_test\AttachmentsTestResponse.');
@@ -69,7 +69,7 @@ class EarlyRenderingControllerTest extends WebTestBase {
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.cacheable-response'));
$this->assertResponse(200);
$this->assertRaw('Hello world!');
- $this->assertNoCacheTag('foo');
+ $this->assertSession()->responseHeaderNotContains('X-Drupal-Cache-Tags', 'foo');
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.cacheable-response.early'));
$this->assertResponse(500);
$this->assertRaw('The controller result claims to be providing relevant cache metadata, but leaked metadata was detected. Please ensure you are not rendering content too early. Returned object class: Drupal\early_rendering_controller_test\CacheableTestResponse.');
@@ -78,17 +78,17 @@ class EarlyRenderingControllerTest extends WebTestBase {
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.domain-object'));
$this->assertResponse(200);
$this->assertRaw('TestDomainObject');
- $this->assertNoCacheTag('foo');
+ $this->assertSession()->responseHeaderNotContains('X-Drupal-Cache-Tags', 'foo');
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.domain-object.early'));
$this->assertResponse(200);
$this->assertRaw('TestDomainObject');
- $this->assertNoCacheTag('foo');
+ $this->assertSession()->responseHeaderNotContains('X-Drupal-Cache-Tags', 'foo');
// Basic domain object with attachments: non-early & early.
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.domain-object-with-attachments'));
$this->assertResponse(200);
$this->assertRaw('AttachmentsTestDomainObject');
- $this->assertNoCacheTag('foo');
+ $this->assertSession()->responseHeaderNotContains('X-Drupal-Cache-Tags', 'foo');
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.domain-object-with-attachments.early'));
$this->assertResponse(500);
$this->assertRaw('The controller result claims to be providing relevant cache metadata, but leaked metadata was detected. Please ensure you are not rendering content too early. Returned object class: Drupal\early_rendering_controller_test\AttachmentsTestDomainObject.');
@@ -97,14 +97,14 @@ class EarlyRenderingControllerTest extends WebTestBase {
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.cacheable-domain-object'));
$this->assertResponse(200);
$this->assertRaw('CacheableTestDomainObject');
- $this->assertNoCacheTag('foo');
+ $this->assertSession()->responseHeaderNotContains('X-Drupal-Cache-Tags', 'foo');
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.cacheable-domain-object.early'));
$this->assertResponse(500);
$this->assertRaw('The controller result claims to be providing relevant cache metadata, but leaked metadata was detected. Please ensure you are not rendering content too early. Returned object class: Drupal\early_rendering_controller_test\CacheableTestDomainObject.');
// The exceptions are expected. Do not interpret them as a test failure.
// Not using File API; a potential error must trigger a PHP warning.
- unlink(\Drupal::root() . '/' . $this->siteDirectory . '/error.log');
+ unlink($this->root . '/' . $this->siteDirectory . '/error.log');
}
}
diff --git a/core/modules/system/src/Tests/Common/FormatDateTest.php b/core/modules/system/tests/src/Functional/Common/FormatDateTest.php
similarity index 55%
rename from core/modules/system/src/Tests/Common/FormatDateTest.php
rename to core/modules/system/tests/src/Functional/Common/FormatDateTest.php
index c174e3432..062d6f412 100644
--- a/core/modules/system/src/Tests/Common/FormatDateTest.php
+++ b/core/modules/system/tests/src/Functional/Common/FormatDateTest.php
@@ -1,51 +1,15 @@
config('system.date')
- ->set('timezone.user.configurable', 1)
- ->save();
- $formats = $this->container->get('entity.manager')
- ->getStorage('date_format')
- ->loadMultiple(['long', 'medium', 'short']);
- $formats['long']->setPattern('l, j. F Y - G:i')->save();
- $formats['medium']->setPattern('j. F Y - G:i')->save();
- $formats['short']->setPattern('Y M j - g:ia')->save();
- $this->refreshVariables();
-
- $this->settingsSet('locale_custom_strings_' . self::LANGCODE, [
- '' => ['Sunday' => 'domingo'],
- 'Long month name' => ['March' => 'marzo'],
- ]);
-
- ConfigurableLanguage::createFromLangcode(static::LANGCODE)->save();
- $this->resetAll();
- }
+class FormatDateTest extends BrowserTestBase {
/**
* Tests admin-defined formats in format_date().
diff --git a/core/modules/system/src/Tests/Common/NoJavaScriptAnonymousTest.php b/core/modules/system/tests/src/Functional/Common/NoJavaScriptAnonymousTest.php
similarity index 92%
rename from core/modules/system/src/Tests/Common/NoJavaScriptAnonymousTest.php
rename to core/modules/system/tests/src/Functional/Common/NoJavaScriptAnonymousTest.php
index e671f8721..2be4d79ad 100644
--- a/core/modules/system/src/Tests/Common/NoJavaScriptAnonymousTest.php
+++ b/core/modules/system/tests/src/Functional/Common/NoJavaScriptAnonymousTest.php
@@ -1,9 +1,9 @@
drupalGet('common-test/type-link-active-class');
+ $this->assertIdentical(0, strpos($this->getSession()->getPage()->getContent(), "\nassertIdentical('text/html; charset=UTF-8', $this->drupalGetHeader('Content-Type'));
+ $this->assertTitle('Test active link class | Drupal');
+ $this->assertCacheContext('url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT);
+
+ $this->drupalGet('common-test/type-link-active-class', ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'json']]);
+ $this->assertIdentical('application/json', $this->drupalGetHeader('Content-Type'));
+ $json = Json::decode($this->getSession()->getPage()->getContent());
+ $this->assertEqual(['content', 'title'], array_keys($json));
+ $this->assertIdentical('Test active link class', $json['title']);
+ $this->assertCacheContext('url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT);
+ }
+
+}
diff --git a/core/modules/system/src/Tests/Common/UrlTest.php b/core/modules/system/tests/src/Functional/Common/UrlTest.php
similarity index 96%
rename from core/modules/system/src/Tests/Common/UrlTest.php
rename to core/modules/system/tests/src/Functional/Common/UrlTest.php
index 1092e1a03..8e4d7b121 100644
--- a/core/modules/system/src/Tests/Common/UrlTest.php
+++ b/core/modules/system/tests/src/Functional/Common/UrlTest.php
@@ -1,13 +1,13 @@
randomMachineName(5) => $this->randomMachineName(5)];
+ $query = ['awesome' => 'drupal'];
$result = Url::fromUri($url, ['query' => $query])->toString();
- $this->assertEqual($url . '?' . http_build_query($query, '', '&'), $result, 'External URL can be extended with a query string in $options.');
+ $this->assertSame('https://www.drupal.org/?awesome=drupal', $result);
// Verify query string can be extended in an external URL.
$url = $test_url . '?drupal=awesome';
- $query = [$this->randomMachineName(5) => $this->randomMachineName(5)];
+ $query = ['awesome' => 'drupal'];
$result = Url::fromUri($url, ['query' => $query])->toString();
- $this->assertEqual($url . '&' . http_build_query($query, '', '&'), $result);
+ $this->assertEqual('https://www.drupal.org/?awesome=drupal&drupal=awesome', $result);
}
}
diff --git a/core/modules/system/tests/src/Functional/CsrfRequestHeaderTest.php b/core/modules/system/tests/src/Functional/CsrfRequestHeaderTest.php
index c6d7b35da..0bb9016c4 100644
--- a/core/modules/system/tests/src/Functional/CsrfRequestHeaderTest.php
+++ b/core/modules/system/tests/src/Functional/CsrfRequestHeaderTest.php
@@ -4,7 +4,6 @@ namespace Drupal\Tests\system\Functional;
use Drupal\Core\Url;
use Drupal\Tests\BrowserTestBase;
-use GuzzleHttp\Cookie\CookieJar;
/**
* Tests protecting routes by requiring CSRF token in the request header.
@@ -27,7 +26,7 @@ class CsrfRequestHeaderTest extends BrowserTestBase {
* uses the deprecated _access_rest_csrf.
*/
public function testRouteAccess() {
- $client = \Drupal::httpClient();
+ $client = $this->getHttpClient();
$csrf_token_paths = ['deprecated/session/token', 'session/token'];
// Test using the both the current path and a test path that returns
// a token using the deprecated 'rest' value.
@@ -44,11 +43,6 @@ class CsrfRequestHeaderTest extends BrowserTestBase {
$url = Url::fromRoute($route_name)
->setAbsolute(TRUE)
->toString();
- $domain = parse_url($url, PHP_URL_HOST);
-
- $session_id = $this->getSession()->getCookie($this->getSessionName());
- /** @var \GuzzleHttp\Cookie\CookieJar $cookies */
- $cookies = CookieJar::fromArray([$this->getSessionName() => $session_id], $domain);
$post_options = [
'headers' => ['Accept' => 'text/plain'],
'http_errors' => FALSE,
@@ -60,7 +54,7 @@ class CsrfRequestHeaderTest extends BrowserTestBase {
// Add cookies to POST options so that all other requests are for the
// authenticated user.
- $post_options['cookies'] = $cookies;
+ $post_options['cookies'] = $this->getSessionCookies();
// Test that access is denied with no token in header.
$result = $client->post($url, $post_options);
diff --git a/core/modules/system/tests/src/Functional/Database/TemporaryQueryTest.php b/core/modules/system/tests/src/Functional/Database/TemporaryQueryTest.php
index 522658579..e7d08502f 100644
--- a/core/modules/system/tests/src/Functional/Database/TemporaryQueryTest.php
+++ b/core/modules/system/tests/src/Functional/Database/TemporaryQueryTest.php
@@ -2,6 +2,8 @@
namespace Drupal\Tests\system\Functional\Database;
+use Drupal\Core\Database\Database;
+
/**
* Tests the temporary query functionality.
*
@@ -29,7 +31,7 @@ class TemporaryQueryTest extends DatabaseTestBase {
$data = json_decode($this->getSession()->getPage()->getContent());
if ($data) {
$this->assertEqual($this->countTableRows('test'), $data->row_count, 'The temporary table contains the correct amount of rows.');
- $this->assertFalse(db_table_exists($data->table_name), 'The temporary table is, indeed, temporary.');
+ $this->assertFalse(Database::getConnection()->schema()->tableExists($data->table_name), 'The temporary table is, indeed, temporary.');
}
else {
$this->fail('The creation of the temporary table failed.');
diff --git a/core/modules/system/tests/src/Functional/Datetime/DrupalDateTimeTest.php b/core/modules/system/tests/src/Functional/Datetime/DrupalDateTimeTest.php
index d2114fdf8..488fa6ef4 100644
--- a/core/modules/system/tests/src/Functional/Datetime/DrupalDateTimeTest.php
+++ b/core/modules/system/tests/src/Functional/Datetime/DrupalDateTimeTest.php
@@ -33,7 +33,7 @@ class DrupalDateTimeTest extends BrowserTestBase {
$options = [
'query' => [
'date' => 'Tue+Sep+17+2013+21%3A35%3A31+GMT%2B0100+(BST)#',
- ]
+ ],
];
// Query the AJAX Timezone Callback with a long-format date.
$response = $this->drupalGet('system/timezone/BST/3600/1', $options);
diff --git a/core/modules/system/tests/src/Functional/Entity/ConfigEntityImportTest.php b/core/modules/system/tests/src/Functional/Entity/ConfigEntityImportTest.php
index 6c5a9d64c..88d9bb5d6 100644
--- a/core/modules/system/tests/src/Functional/Entity/ConfigEntityImportTest.php
+++ b/core/modules/system/tests/src/Functional/Entity/ConfigEntityImportTest.php
@@ -21,7 +21,7 @@ class ConfigEntityImportTest extends BrowserTestBase {
*
* @var array
*/
- public static $modules = ['action', 'block', 'filter', 'image', 'search', 'search_extra_type'];
+ public static $modules = ['action', 'block', 'filter', 'image', 'search', 'search_extra_type', 'config_test'];
/**
* {@inheritdoc}
@@ -41,7 +41,9 @@ class ConfigEntityImportTest extends BrowserTestBase {
$this->doFilterFormatUpdate();
$this->doImageStyleUpdate();
$this->doSearchPageUpdate();
+ $this->doThirdPartySettingsUpdate();
}
+
/**
* Tests updating a action during import.
*/
@@ -171,6 +173,34 @@ class ConfigEntityImportTest extends BrowserTestBase {
$this->assertConfigUpdateImport($name, $original_data, $custom_data);
}
+ /**
+ * Tests updating of third party settings.
+ */
+ protected function doThirdPartySettingsUpdate() {
+ // Create a test action with a known label.
+ $name = 'system.action.third_party_settings_test';
+
+ /** @var \Drupal\config_test\Entity\ConfigTest $entity */
+ $entity = Action::create([
+ 'id' => 'third_party_settings_test',
+ 'plugin' => 'action_message_action',
+ ]);
+ $entity->save();
+
+ $this->assertIdentical([], $entity->getThirdPartyProviders());
+ // Get a copy of the configuration before the third party setting is added.
+ $no_third_part_setting_config = $this->container->get('config.storage')->read($name);
+
+ // Add a third party setting.
+ $entity->setThirdPartySetting('config_test', 'integer', 1);
+ $entity->save();
+ $this->assertIdentical(1, $entity->getThirdPartySetting('config_test', 'integer'));
+ $has_third_part_setting_config = $this->container->get('config.storage')->read($name);
+
+ // Ensure configuration imports can completely remove third party settings.
+ $this->assertConfigUpdateImport($name, $has_third_part_setting_config, $no_third_part_setting_config);
+ }
+
/**
* Tests that a single set of plugin config stays in sync.
*
diff --git a/core/modules/system/tests/src/Functional/Entity/EntityCacheTagsTestBase.php b/core/modules/system/tests/src/Functional/Entity/EntityCacheTagsTestBase.php
index aa9452a4b..447afbfa7 100644
--- a/core/modules/system/tests/src/Functional/Entity/EntityCacheTagsTestBase.php
+++ b/core/modules/system/tests/src/Functional/Entity/EntityCacheTagsTestBase.php
@@ -10,7 +10,7 @@ use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Url;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\field\Entity\FieldConfig;
-use Drupal\system\Tests\Cache\PageCacheTagsTestBase;
+use Drupal\Tests\system\Functional\Cache\PageCacheTagsTestBase;
use Drupal\user\Entity\Role;
use Drupal\user\RoleInterface;
@@ -153,7 +153,7 @@ abstract class EntityCacheTagsTestBase extends PageCacheTagsTestBase {
* @return string[]
* An array of the additional cache contexts.
*
- * @see \Drupal\system\Tests\Entity\EntityCacheTagsTestBase::createEntity()
+ * @see \Drupal\Tests\system\Functional\Entity\EntityCacheTagsTestBase::createEntity()
*/
protected function getAdditionalCacheContextsForEntity(EntityInterface $entity) {
return [];
@@ -167,7 +167,7 @@ abstract class EntityCacheTagsTestBase extends PageCacheTagsTestBase {
* @return array
* An array of the additional cache tags.
*
- * @see \Drupal\system\Tests\Entity\EntityCacheTagsTestBase::createEntity()
+ * @see \Drupal\Tests\system\Functional\Entity\EntityCacheTagsTestBase::createEntity()
*/
protected function getAdditionalCacheTagsForEntity(EntityInterface $entity) {
return [];
diff --git a/core/modules/system/src/Tests/Entity/EntityFormTest.php b/core/modules/system/tests/src/Functional/Entity/EntityFormTest.php
similarity index 98%
rename from core/modules/system/src/Tests/Entity/EntityFormTest.php
rename to core/modules/system/tests/src/Functional/Entity/EntityFormTest.php
index 2ebc35061..4f0b7f0f7 100644
--- a/core/modules/system/src/Tests/Entity/EntityFormTest.php
+++ b/core/modules/system/tests/src/Functional/Entity/EntityFormTest.php
@@ -1,16 +1,16 @@
drupalCreateContentType(['type' => 'article', 'name' => 'Article']);
+ $this->installSchema('system', 'sequences');
+ $this->installSchema('comment', ['comment_entity_statistics']);
+
+ $this->installEntitySchema('comment');
+ $this->installEntitySchema('node');
+ $this->installEntitySchema('taxonomy_term');
+ $this->installEntitySchema('user');
+
+ $this->installConfig(['comment', 'field', 'node', 'taxonomy', 'user']);
+
+ // Create the anonymous and the admin users.
+ $anonymous_user = User::create([
+ 'uid' => 0,
+ 'name' => '',
+ ]);
+ $anonymous_user->save();
+ $admin_user = User::create([
+ 'uid' => 1,
+ 'name' => 'admin',
+ 'status' => 1,
+ ]);
+ $admin_user->save();
}
/**
@@ -110,8 +139,8 @@ class EntityReferenceSelectionAccessTest extends BrowserTestBase {
}
// Test as a non-admin.
- $normal_user = $this->drupalCreateUser(['access content']);
- \Drupal::currentUser()->setAccount($normal_user);
+ $normal_user = $this->createUser(['access content']);
+ $this->setCurrentUser($normal_user);
$referenceable_tests = [
[
'arguments' => [
@@ -162,8 +191,8 @@ class EntityReferenceSelectionAccessTest extends BrowserTestBase {
$this->assertReferenceable($selection_options, $referenceable_tests, 'Node handler');
// Test as an admin.
- $admin_user = $this->drupalCreateUser(['access content', 'bypass node access']);
- \Drupal::currentUser()->setAccount($admin_user);
+ $content_admin = $this->createUser(['access content', 'bypass node access']);
+ $this->setCurrentUser($content_admin);
$referenceable_tests = [
[
'arguments' => [
@@ -239,7 +268,7 @@ class EntityReferenceSelectionAccessTest extends BrowserTestBase {
}
// Test as a non-admin.
- \Drupal::currentUser()->setAccount($users['non_admin']);
+ $this->setCurrentUser($users['non_admin']);
$referenceable_tests = [
[
'arguments' => [
@@ -278,7 +307,7 @@ class EntityReferenceSelectionAccessTest extends BrowserTestBase {
];
$this->assertReferenceable($selection_options, $referenceable_tests, 'User handler');
- \Drupal::currentUser()->setAccount($users['admin']);
+ $this->setCurrentUser($users['admin']);
$referenceable_tests = [
[
'arguments' => [
@@ -361,6 +390,7 @@ class EntityReferenceSelectionAccessTest extends BrowserTestBase {
];
// Build a set of test data.
+ $this->createContentType(['type' => 'article', 'name' => 'Article']);
$node_values = [
'published' => [
'type' => 'article',
@@ -431,8 +461,8 @@ class EntityReferenceSelectionAccessTest extends BrowserTestBase {
}
// Test as a non-admin.
- $normal_user = $this->drupalCreateUser(['access content', 'access comments']);
- \Drupal::currentUser()->setAccount($normal_user);
+ $normal_user = $this->createUser(['access content', 'access comments']);
+ $this->setCurrentUser($normal_user);
$referenceable_tests = [
[
'arguments' => [
@@ -470,8 +500,8 @@ class EntityReferenceSelectionAccessTest extends BrowserTestBase {
$this->assertReferenceable($selection_options, $referenceable_tests, 'Comment handler');
// Test as a comment admin.
- $admin_user = $this->drupalCreateUser(['access content', 'access comments', 'administer comments']);
- \Drupal::currentUser()->setAccount($admin_user);
+ $admin_user = $this->createUser(['access content', 'access comments', 'administer comments']);
+ $this->setCurrentUser($admin_user);
$referenceable_tests = [
[
'arguments' => [
@@ -488,8 +518,8 @@ class EntityReferenceSelectionAccessTest extends BrowserTestBase {
$this->assertReferenceable($selection_options, $referenceable_tests, 'Comment handler (comment admin)');
// Test as a node and comment admin.
- $admin_user = $this->drupalCreateUser(['access content', 'access comments', 'administer comments', 'bypass node access']);
- \Drupal::currentUser()->setAccount($admin_user);
+ $admin_user = $this->createUser(['access content', 'access comments', 'administer comments', 'bypass node access']);
+ $this->setCurrentUser($admin_user);
$referenceable_tests = [
[
'arguments' => [
@@ -507,4 +537,148 @@ class EntityReferenceSelectionAccessTest extends BrowserTestBase {
$this->assertReferenceable($selection_options, $referenceable_tests, 'Comment handler (comment + node admin)');
}
+ /**
+ * Test the term-specific overrides of the selection handler.
+ */
+ public function testTermHandler() {
+ // Create a 'Tags' vocabulary.
+ Vocabulary::create([
+ 'name' => 'Tags',
+ 'description' => $this->randomMachineName(),
+ 'vid' => 'tags',
+ ])->save();
+
+ $selection_options = [
+ 'target_type' => 'taxonomy_term',
+ 'handler' => 'default',
+ 'target_bundles' => NULL,
+ ];
+
+ // Build a set of test data.
+ $term_values = [
+ 'published1' => [
+ 'vid' => 'tags',
+ 'status' => 1,
+ 'name' => 'Term published1',
+ ],
+ 'published2' => [
+ 'vid' => 'tags',
+ 'status' => 1,
+ 'name' => 'Term published2',
+ ],
+ 'unpublished' => [
+ 'vid' => 'tags',
+ 'status' => 0,
+ 'name' => 'Term unpublished',
+ ],
+ 'published3' => [
+ 'vid' => 'tags',
+ 'status' => 1,
+ 'name' => 'Term published3',
+ 'parent' => 'unpublished',
+ ],
+ 'published4' => [
+ 'vid' => 'tags',
+ 'status' => 1,
+ 'name' => 'Term published4',
+ 'parent' => 'published3',
+ ],
+ ];
+
+ $terms = [];
+ $term_labels = [];
+ foreach ($term_values as $key => $values) {
+ $term = Term::create($values);
+ if (isset($values['parent'])) {
+ $term->parent->entity = $terms[$values['parent']];
+ }
+ $term->save();
+ $terms[$key] = $term;
+ $term_labels[$key] = Html::escape($term->label());
+ }
+
+ // Test as a non-admin.
+ $normal_user = $this->createUser(['access content']);
+ $this->setCurrentUser($normal_user);
+ $referenceable_tests = [
+ [
+ 'arguments' => [
+ [NULL, 'CONTAINS'],
+ ],
+ 'result' => [
+ 'tags' => [
+ $terms['published1']->id() => $term_labels['published1'],
+ $terms['published2']->id() => $term_labels['published2'],
+ ],
+ ],
+ ],
+ [
+ 'arguments' => [
+ ['published1', 'CONTAINS'],
+ ['Published1', 'CONTAINS'],
+ ],
+ 'result' => [
+ 'tags' => [
+ $terms['published1']->id() => $term_labels['published1'],
+ ],
+ ],
+ ],
+ [
+ 'arguments' => [
+ ['published2', 'CONTAINS'],
+ ['Published2', 'CONTAINS'],
+ ],
+ 'result' => [
+ 'tags' => [
+ $terms['published2']->id() => $term_labels['published2'],
+ ],
+ ],
+ ],
+ [
+ 'arguments' => [
+ ['invalid term', 'CONTAINS'],
+ ],
+ 'result' => [],
+ ],
+ [
+ 'arguments' => [
+ ['Term unpublished', 'CONTAINS'],
+ ],
+ 'result' => [],
+ ],
+ ];
+ $this->assertReferenceable($selection_options, $referenceable_tests, 'Term handler');
+
+ // Test as an admin.
+ $admin_user = $this->createUser(['access content', 'administer taxonomy']);
+ $this->setCurrentUser($admin_user);
+ $referenceable_tests = [
+ [
+ 'arguments' => [
+ [NULL, 'CONTAINS'],
+ ],
+ 'result' => [
+ 'tags' => [
+ $terms['published1']->id() => $term_labels['published1'],
+ $terms['published2']->id() => $term_labels['published2'],
+ $terms['unpublished']->id() => $term_labels['unpublished'],
+ $terms['published3']->id() => '-' . $term_labels['published3'],
+ $terms['published4']->id() => '--' . $term_labels['published4'],
+ ],
+ ],
+ ],
+ [
+ 'arguments' => [
+ ['Term unpublished', 'CONTAINS'],
+ ],
+ 'result' => [
+ 'tags' => [
+ $terms['unpublished']->id() => $term_labels['unpublished'],
+ ],
+ ],
+ ],
+ ];
+ $this->assertReferenceable($selection_options, $referenceable_tests, 'Term handler (admin)');
+ }
+
}
diff --git a/core/modules/system/src/Tests/Entity/EntityTranslationFormTest.php b/core/modules/system/tests/src/Functional/Entity/EntityTranslationFormTest.php
similarity index 97%
rename from core/modules/system/src/Tests/Entity/EntityTranslationFormTest.php
rename to core/modules/system/tests/src/Functional/Entity/EntityTranslationFormTest.php
index f5134249a..e008e71e1 100644
--- a/core/modules/system/src/Tests/Entity/EntityTranslationFormTest.php
+++ b/core/modules/system/tests/src/Functional/Entity/EntityTranslationFormTest.php
@@ -1,18 +1,18 @@
_view"
+ * - ":"
+ */
+ public function testEntityUri() {
+ $entity_url = $this->entity->urlInfo();
+ $entity_type = $this->entity->getEntityTypeId();
+
+ // Selects the view mode that will be used.
+ $view_mode = $this->selectViewMode($entity_type);
+
+ // The default cache contexts for rendered entities.
+ $entity_cache_contexts = $this->getDefaultCacheContexts();
+
+ // Generate the standardized entity cache tags.
+ $cache_tag = $this->entity->getCacheTags();
+ $view_cache_tag = \Drupal::entityManager()->getViewBuilder($entity_type)->getCacheTags();
+ $render_cache_tag = 'rendered';
+
+ $this->pass("Test entity.", 'Debug');
+ $this->verifyPageCache($entity_url, 'MISS');
+
+ // Verify a cache hit, but also the presence of the correct cache tags.
+ $this->verifyPageCache($entity_url, 'HIT');
+
+ // Also verify the existence of an entity render cache entry, if this entity
+ // type supports render caching.
+ if (\Drupal::entityManager()->getDefinition($entity_type)->isRenderCacheable()) {
+ $cache_keys = ['entity_view', $entity_type, $this->entity->id(), $view_mode];
+ $cid = $this->createCacheId($cache_keys, $entity_cache_contexts);
+ $redirected_cid = NULL;
+ $additional_cache_contexts = $this->getAdditionalCacheContextsForEntity($this->entity);
+ if (count($additional_cache_contexts)) {
+ $redirected_cid = $this->createCacheId($cache_keys, Cache::mergeContexts($entity_cache_contexts, $additional_cache_contexts));
+ }
+ $expected_cache_tags = Cache::mergeTags($cache_tag, $view_cache_tag);
+ $expected_cache_tags = Cache::mergeTags($expected_cache_tags, $this->getAdditionalCacheTagsForEntity($this->entity));
+ $expected_cache_tags = Cache::mergeTags($expected_cache_tags, [$render_cache_tag]);
+ $this->verifyRenderCache($cid, $expected_cache_tags, $redirected_cid);
+ }
+
+ // Verify that after modifying the entity, there is a cache miss.
+ $this->pass("Test modification of entity.", 'Debug');
+ $this->entity->save();
+ $this->verifyPageCache($entity_url, 'MISS');
+
+ // Verify a cache hit.
+ $this->verifyPageCache($entity_url, 'HIT');
+
+ // Verify that after modifying the entity's display, there is a cache miss.
+ $this->pass("Test modification of entity's '$view_mode' display.", 'Debug');
+ $entity_display = entity_get_display($entity_type, $this->entity->bundle(), $view_mode);
+ $entity_display->save();
+ $this->verifyPageCache($entity_url, 'MISS');
+
+ // Verify a cache hit.
+ $this->verifyPageCache($entity_url, 'HIT');
+
+ if ($bundle_entity_type_id = $this->entity->getEntityType()->getBundleEntityType()) {
+ // Verify that after modifying the corresponding bundle entity, there is a
+ // cache miss.
+ $this->pass("Test modification of entity's bundle entity.", 'Debug');
+ $bundle_entity = $this->container->get('entity_type.manager')
+ ->getStorage($bundle_entity_type_id)
+ ->load($this->entity->bundle());
+ $bundle_entity->save();
+ $this->verifyPageCache($entity_url, 'MISS');
+
+ // Verify a cache hit.
+ $this->verifyPageCache($entity_url, 'HIT');
+ }
+
+ if ($this->entity->getEntityType()->get('field_ui_base_route')) {
+ // Verify that after modifying a configurable field on the entity, there
+ // is a cache miss.
+ $this->pass("Test modification of entity's configurable field.", 'Debug');
+ $field_storage_name = $this->entity->getEntityTypeId() . '.configurable_field';
+ $field_storage = FieldStorageConfig::load($field_storage_name);
+ $field_storage->save();
+ $this->verifyPageCache($entity_url, 'MISS');
+
+ // Verify a cache hit.
+ $this->verifyPageCache($entity_url, 'HIT');
+
+ // Verify that after modifying a configurable field on the entity, there
+ // is a cache miss.
+ $this->pass("Test modification of entity's configurable field.", 'Debug');
+ $field_name = $this->entity->getEntityTypeId() . '.' . $this->entity->bundle() . '.configurable_field';
+ $field = FieldConfig::load($field_name);
+ $field->save();
+ $this->verifyPageCache($entity_url, 'MISS');
+
+ // Verify a cache hit.
+ $this->verifyPageCache($entity_url, 'HIT');
+ }
+
+ // Verify that after invalidating the entity's cache tag directly, there is
+ // a cache miss.
+ $this->pass("Test invalidation of entity's cache tag.", 'Debug');
+ Cache::invalidateTags($this->entity->getCacheTagsToInvalidate());
+ $this->verifyPageCache($entity_url, 'MISS');
+
+ // Verify a cache hit.
+ $this->verifyPageCache($entity_url, 'HIT');
+
+ // Verify that after invalidating the generic entity type's view cache tag
+ // directly, there is a cache miss.
+ $this->pass("Test invalidation of entity's 'view' cache tag.", 'Debug');
+ Cache::invalidateTags($view_cache_tag);
+ $this->verifyPageCache($entity_url, 'MISS');
+
+ // Verify a cache hit.
+ $this->verifyPageCache($entity_url, 'HIT');
+
+ // Verify that after deleting the entity, there is a cache miss.
+ $this->pass('Test deletion of entity.', 'Debug');
+ $this->entity->delete();
+ $this->verifyPageCache($entity_url, 'MISS');
+ $this->assertResponse(404);
+ }
+
+ /**
+ * Gets the default cache contexts for rendered entities.
+ *
+ * @return array
+ * The default cache contexts for rendered entities.
+ */
+ protected function getDefaultCacheContexts() {
+ return ['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions'];
+ }
+
+}
diff --git a/core/modules/system/tests/src/Functional/Entity/Traits/EntityDefinitionTestTrait.php b/core/modules/system/tests/src/Functional/Entity/Traits/EntityDefinitionTestTrait.php
new file mode 100644
index 000000000..e873e096f
--- /dev/null
+++ b/core/modules/system/tests/src/Functional/Entity/Traits/EntityDefinitionTestTrait.php
@@ -0,0 +1,299 @@
+state->set('entity_test_new', TRUE);
+ $this->entityManager->clearCachedDefinitions();
+ $this->entityDefinitionUpdateManager->applyUpdates();
+ }
+
+ /**
+ * Resets the entity type definition.
+ */
+ protected function resetEntityType() {
+ $this->state->set('entity_test_update.entity_type', NULL);
+ $this->entityManager->clearCachedDefinitions();
+ $this->entityDefinitionUpdateManager->applyUpdates();
+ }
+
+ /**
+ * Updates the 'entity_test_update' entity type to revisionable.
+ */
+ protected function updateEntityTypeToRevisionable() {
+ $entity_type = clone $this->entityManager->getDefinition('entity_test_update');
+
+ $keys = $entity_type->getKeys();
+ $keys['revision'] = 'revision_id';
+ $entity_type->set('entity_keys', $keys);
+ $entity_type->set('revision_table', 'entity_test_update_revision');
+
+ $this->state->set('entity_test_update.entity_type', $entity_type);
+ }
+
+ /**
+ * Updates the 'entity_test_update' entity type not revisionable.
+ */
+ protected function updateEntityTypeToNotRevisionable() {
+ $entity_type = clone $this->entityManager->getDefinition('entity_test_update');
+
+ $keys = $entity_type->getKeys();
+ unset($keys['revision']);
+ $entity_type->set('entity_keys', $keys);
+ $entity_type->set('revision_table', NULL);
+
+ $this->state->set('entity_test_update.entity_type', $entity_type);
+ }
+
+ /**
+ * Updates the 'entity_test_update' entity type to translatable.
+ */
+ protected function updateEntityTypeToTranslatable() {
+ $entity_type = clone $this->entityManager->getDefinition('entity_test_update');
+
+ $entity_type->set('translatable', TRUE);
+ $entity_type->set('data_table', 'entity_test_update_data');
+
+ if ($entity_type->isRevisionable()) {
+ $entity_type->set('revision_data_table', 'entity_test_update_revision_data');
+ }
+
+ $this->state->set('entity_test_update.entity_type', $entity_type);
+ }
+
+ /**
+ * Updates the 'entity_test_update' entity type to not translatable.
+ */
+ protected function updateEntityTypeToNotTranslatable() {
+ $entity_type = clone $this->entityManager->getDefinition('entity_test_update');
+
+ $entity_type->set('translatable', FALSE);
+ $entity_type->set('data_table', NULL);
+
+ if ($entity_type->isRevisionable()) {
+ $entity_type->set('revision_data_table', NULL);
+ }
+
+ $this->state->set('entity_test_update.entity_type', $entity_type);
+ }
+
+ /**
+ * Updates the 'entity_test_update' entity type to revisionable and
+ * translatable.
+ */
+ protected function updateEntityTypeToRevisionableAndTranslatable() {
+ $entity_type = clone $this->entityManager->getDefinition('entity_test_update');
+
+ $keys = $entity_type->getKeys();
+ $keys['revision'] = 'revision_id';
+ $entity_type->set('entity_keys', $keys);
+ $entity_type->set('translatable', TRUE);
+ $entity_type->set('data_table', 'entity_test_update_data');
+ $entity_type->set('revision_table', 'entity_test_update_revision');
+ $entity_type->set('revision_data_table', 'entity_test_update_revision_data');
+
+ $this->state->set('entity_test_update.entity_type', $entity_type);
+ }
+
+ /**
+ * Adds a new base field to the 'entity_test_update' entity type.
+ *
+ * @param string $type
+ * (optional) The field type for the new field. Defaults to 'string'.
+ * @param string $entity_type_id
+ * (optional) The entity type ID the base field should be attached to.
+ * Defaults to 'entity_test_update'.
+ * @param bool $is_revisionable
+ * (optional) If the base field should be revisionable or not. Defaults to
+ * FALSE.
+ */
+ protected function addBaseField($type = 'string', $entity_type_id = 'entity_test_update', $is_revisionable = FALSE) {
+ $definitions['new_base_field'] = BaseFieldDefinition::create($type)
+ ->setName('new_base_field')
+ ->setRevisionable($is_revisionable)
+ ->setLabel(t('A new base field'));
+ $this->state->set($entity_type_id . '.additional_base_field_definitions', $definitions);
+ }
+
+ /**
+ * Adds a long-named base field to the 'entity_test_update' entity type.
+ */
+ protected function addLongNameBaseField() {
+ $key = 'entity_test_update.additional_base_field_definitions';
+ $definitions = $this->state->get($key, []);
+ $definitions['new_long_named_entity_reference_base_field'] = BaseFieldDefinition::create('entity_reference')
+ ->setName('new_long_named_entity_reference_base_field')
+ ->setLabel(t('A new long-named base field'))
+ ->setSetting('target_type', 'user')
+ ->setSetting('handler', 'default');
+ $this->state->set($key, $definitions);
+ }
+
+ /**
+ * Adds a new revisionable base field to the 'entity_test_update' entity type.
+ *
+ * @param string $type
+ * (optional) The field type for the new field. Defaults to 'string'.
+ */
+ protected function addRevisionableBaseField($type = 'string') {
+ $definitions['new_base_field'] = BaseFieldDefinition::create($type)
+ ->setName('new_base_field')
+ ->setLabel(t('A new revisionable base field'))
+ ->setRevisionable(TRUE);
+ $this->state->set('entity_test_update.additional_base_field_definitions', $definitions);
+ }
+
+ /**
+ * Modifies the new base field from 'string' to 'text'.
+ */
+ protected function modifyBaseField() {
+ $this->addBaseField('text');
+ }
+
+ /**
+ * Promotes a field to an entity key.
+ */
+ protected function makeBaseFieldEntityKey() {
+ $entity_type = clone $this->entityManager->getDefinition('entity_test_update');
+ $entity_keys = $entity_type->getKeys();
+ $entity_keys['new_base_field'] = 'new_base_field';
+ $entity_type->set('entity_keys', $entity_keys);
+ $this->state->set('entity_test_update.entity_type', $entity_type);
+ }
+
+ /**
+ * Removes the new base field from the 'entity_test_update' entity type.
+ *
+ * @param string $entity_type_id
+ * (optional) The entity type ID the base field should be attached to.
+ */
+ protected function removeBaseField($entity_type_id = 'entity_test_update') {
+ $this->state->delete($entity_type_id . '.additional_base_field_definitions');
+ }
+
+ /**
+ * Adds a single-field index to the base field.
+ */
+ protected function addBaseFieldIndex() {
+ $this->state->set('entity_test_update.additional_field_index.entity_test_update.new_base_field', TRUE);
+ }
+
+ /**
+ * Removes the index added in addBaseFieldIndex().
+ */
+ protected function removeBaseFieldIndex() {
+ $this->state->delete('entity_test_update.additional_field_index.entity_test_update.new_base_field');
+ }
+
+ /**
+ * Adds a new bundle field to the 'entity_test_update' entity type.
+ *
+ * @param string $type
+ * (optional) The field type for the new field. Defaults to 'string'.
+ */
+ protected function addBundleField($type = 'string') {
+ $definitions['new_bundle_field'] = FieldStorageDefinition::create($type)
+ ->setName('new_bundle_field')
+ ->setLabel(t('A new bundle field'))
+ ->setTargetEntityTypeId('entity_test_update');
+ $this->state->set('entity_test_update.additional_field_storage_definitions', $definitions);
+ $this->state->set('entity_test_update.additional_bundle_field_definitions.test_bundle', $definitions);
+ }
+
+ /**
+ * Modifies the new bundle field from 'string' to 'text'.
+ */
+ protected function modifyBundleField() {
+ $this->addBundleField('text');
+ }
+
+ /**
+ * Removes the new bundle field from the 'entity_test_update' entity type.
+ */
+ protected function removeBundleField() {
+ $this->state->delete('entity_test_update.additional_field_storage_definitions');
+ $this->state->delete('entity_test_update.additional_bundle_field_definitions.test_bundle');
+ }
+
+ /**
+ * Adds an index to the 'entity_test_update' entity type's base table.
+ *
+ * @see \Drupal\entity_test\EntityTestStorageSchema::getEntitySchema()
+ */
+ protected function addEntityIndex() {
+ $indexes = [
+ 'entity_test_update__new_index' => ['name', 'test_single_property'],
+ ];
+ $this->state->set('entity_test_update.additional_entity_indexes', $indexes);
+ }
+
+ /**
+ * Removes the index added in addEntityIndex().
+ */
+ protected function removeEntityIndex() {
+ $this->state->delete('entity_test_update.additional_entity_indexes');
+ }
+
+ /**
+ * Renames the base table to 'entity_test_update_new'.
+ */
+ protected function renameBaseTable() {
+ $entity_type = clone $this->entityManager->getDefinition('entity_test_update');
+
+ $entity_type->set('base_table', 'entity_test_update_new');
+
+ $this->state->set('entity_test_update.entity_type', $entity_type);
+ }
+
+ /**
+ * Renames the data table to 'entity_test_update_data_new'.
+ */
+ protected function renameDataTable() {
+ $entity_type = clone $this->entityManager->getDefinition('entity_test_update');
+
+ $entity_type->set('data_table', 'entity_test_update_data_new');
+
+ $this->state->set('entity_test_update.entity_type', $entity_type);
+ }
+
+ /**
+ * Renames the revision table to 'entity_test_update_revision_new'.
+ */
+ protected function renameRevisionBaseTable() {
+ $entity_type = clone $this->entityManager->getDefinition('entity_test_update');
+
+ $entity_type->set('revision_table', 'entity_test_update_revision_new');
+
+ $this->state->set('entity_test_update.entity_type', $entity_type);
+ }
+
+ /**
+ * Renames the revision data table to 'entity_test_update_revision_data_new'.
+ */
+ protected function renameRevisionDataTable() {
+ $entity_type = clone $this->entityManager->getDefinition('entity_test_update');
+
+ $entity_type->set('revision_data_table', 'entity_test_update_revision_data_new');
+
+ $this->state->set('entity_test_update.entity_type', $entity_type);
+ }
+
+ /**
+ * Removes the entity type.
+ */
+ protected function deleteEntityType() {
+ $this->state->set('entity_test_update.entity_type', 'null');
+ }
+
+}
diff --git a/core/modules/system/tests/src/Functional/Entity/Update/LangcodeToAsciiUpdateFilledTest.php b/core/modules/system/tests/src/Functional/Entity/Update/LangcodeToAsciiUpdateFilledTest.php
index 7e95ed779..e26fb8802 100644
--- a/core/modules/system/tests/src/Functional/Entity/Update/LangcodeToAsciiUpdateFilledTest.php
+++ b/core/modules/system/tests/src/Functional/Entity/Update/LangcodeToAsciiUpdateFilledTest.php
@@ -6,6 +6,7 @@ namespace Drupal\Tests\system\Functional\Entity\Update;
* Runs LangcodeToAsciiUpdateTest with a dump filled with content.
*
* @group Entity
+ * @group legacy
*/
class LangcodeToAsciiUpdateFilledTest extends LangcodeToAsciiUpdateTest {
diff --git a/core/modules/system/tests/src/Functional/Entity/Update/LangcodeToAsciiUpdateTest.php b/core/modules/system/tests/src/Functional/Entity/Update/LangcodeToAsciiUpdateTest.php
index d1a3cabb0..653a43723 100644
--- a/core/modules/system/tests/src/Functional/Entity/Update/LangcodeToAsciiUpdateTest.php
+++ b/core/modules/system/tests/src/Functional/Entity/Update/LangcodeToAsciiUpdateTest.php
@@ -9,6 +9,7 @@ use Drupal\FunctionalTests\Update\UpdatePathTestBase;
* Tests that the entity langcode fields have been updated to varchar_ascii.
*
* @group Entity
+ * @group legacy
*/
class LangcodeToAsciiUpdateTest extends UpdatePathTestBase {
@@ -39,7 +40,8 @@ class LangcodeToAsciiUpdateTest extends UpdatePathTestBase {
];
foreach ($tables as $table => $columns) {
foreach ($columns as $column) {
- $this->assertEqual('utf8mb4_general_ci', $this->getColumnCollation($table, $column), 'Found correct starting collation for ' . $table . '.' . $column);
+ // Depending on MYSQL versions you get different collations.
+ $this->assertContains($this->getColumnCollation($table, $column), ['utf8mb4_0900_ai_ci', 'utf8mb4_general_ci'], 'Found correct starting collation for ' . $table . '.' . $column);
}
}
diff --git a/core/modules/system/tests/src/Functional/Entity/Update/MoveRevisionMetadataFieldsUpdateTest.php b/core/modules/system/tests/src/Functional/Entity/Update/MoveRevisionMetadataFieldsUpdateTest.php
index b4888db09..62d9a3e3f 100644
--- a/core/modules/system/tests/src/Functional/Entity/Update/MoveRevisionMetadataFieldsUpdateTest.php
+++ b/core/modules/system/tests/src/Functional/Entity/Update/MoveRevisionMetadataFieldsUpdateTest.php
@@ -10,6 +10,7 @@ use Drupal\views\Entity\View;
* Tests the upgrade path for moving the revision metadata fields.
*
* @group Update
+ * @group legacy
*/
class MoveRevisionMetadataFieldsUpdateTest extends UpdatePathTestBase {
@@ -141,7 +142,7 @@ class MoveRevisionMetadataFieldsUpdateTest extends UpdatePathTestBase {
$revision_metadata_keys = [
'revision_user' => 'revision_user',
'revision_created' => 'revision_created',
- 'revision_log_message' => 'revision_log_message'
+ 'revision_log_message' => 'revision_log_message',
];
$this->assertEquals($revision_metadata_keys, $entity_type->getRevisionMetadataKeys(TRUE));
@@ -167,7 +168,7 @@ class MoveRevisionMetadataFieldsUpdateTest extends UpdatePathTestBase {
$definition = [
'id' => 'entity_test_mul_revlog',
'revision_metadata_keys' => [
- 'revision_default' => 'revision_default'
+ 'revision_default' => 'revision_default',
],
];
$entity_type = new ContentEntityType($definition);
diff --git a/core/modules/system/tests/src/Functional/Entity/Update/SqlContentEntityStorageSchemaConverterNonTranslatableTest.php b/core/modules/system/tests/src/Functional/Entity/Update/SqlContentEntityStorageSchemaConverterNonTranslatableTest.php
new file mode 100644
index 000000000..32dd5aa44
--- /dev/null
+++ b/core/modules/system/tests/src/Functional/Entity/Update/SqlContentEntityStorageSchemaConverterNonTranslatableTest.php
@@ -0,0 +1,24 @@
+databaseDumpFiles = [
+ __DIR__ . '/../../../../fixtures/update/drupal-8.0.0-rc1-filled.standard.entity_test_update.php.gz',
+ __DIR__ . '/../../../../fixtures/update/drupal-8.entity-test-schema-converter-enabled.php',
+ ];
+ }
+
+}
diff --git a/core/modules/system/tests/src/Functional/Entity/Update/SqlContentEntityStorageSchemaConverterTest.php b/core/modules/system/tests/src/Functional/Entity/Update/SqlContentEntityStorageSchemaConverterTestBase.php
similarity index 56%
rename from core/modules/system/tests/src/Functional/Entity/Update/SqlContentEntityStorageSchemaConverterTest.php
rename to core/modules/system/tests/src/Functional/Entity/Update/SqlContentEntityStorageSchemaConverterTestBase.php
index 625ee190e..4a26f1091 100644
--- a/core/modules/system/tests/src/Functional/Entity/Update/SqlContentEntityStorageSchemaConverterTest.php
+++ b/core/modules/system/tests/src/Functional/Entity/Update/SqlContentEntityStorageSchemaConverterTestBase.php
@@ -4,15 +4,12 @@ namespace Drupal\Tests\system\Functional\Entity\Update;
use Drupal\Core\Entity\Sql\TemporaryTableMapping;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
-use Drupal\system\Tests\Entity\EntityDefinitionTestTrait;
+use Drupal\Tests\system\Functional\Entity\Traits\EntityDefinitionTestTrait;
/**
- * Tests updating an entity type with existing data to be revisionable.
- *
- * @group Entity
- * @group Update
+ * Defines a class for testing the conversion of entity types to revisionable.
*/
-class SqlContentEntityStorageSchemaConverterTest extends UpdatePathTestBase {
+abstract class SqlContentEntityStorageSchemaConverterTestBase extends UpdatePathTestBase {
use EntityDefinitionTestTrait;
@@ -64,16 +61,6 @@ class SqlContentEntityStorageSchemaConverterTest extends UpdatePathTestBase {
$this->state = \Drupal::state();
}
- /**
- * {@inheritdoc}
- */
- protected function setDatabaseDumpFiles() {
- $this->databaseDumpFiles = [
- __DIR__ . '/../../../../fixtures/update/drupal-8.0.0-rc1-filled.standard.entity_test_update_mul.php.gz',
- __DIR__ . '/../../../../fixtures/update/drupal-8.entity-test-schema-converter-enabled.php',
- ];
- }
-
/**
* Tests the conversion of an entity type to revisionable.
*/
@@ -83,17 +70,24 @@ class SqlContentEntityStorageSchemaConverterTest extends UpdatePathTestBase {
$entity_test_update = $this->lastInstalledSchemaRepository->getLastInstalledDefinition('entity_test_update');
$this->assertFalse($entity_test_update->isRevisionable());
- // Make the entity type revisionable and translatable and run the updates.
- $this->updateEntityTypeToRevisionableAndTranslatable();
+ $translatable = $entity_test_update->isTranslatable();
+
+ // Make the entity type revisionable and run the updates.
+ if ($translatable) {
+ $this->updateEntityTypeToRevisionableAndTranslatable();
+ }
+ else {
+ $this->updateEntityTypeToRevisionable();
+ }
$this->runUpdates();
/** @var \Drupal\Core\Entity\EntityTypeInterface $entity_test_update */
$entity_test_update = $this->lastInstalledSchemaRepository->getLastInstalledDefinition('entity_test_update');
- $this->assertTrue($entity_test_update->isRevisionable());
-
$field_storage_definitions = $this->lastInstalledSchemaRepository->getLastInstalledFieldStorageDefinitions('entity_test_update');
- $this->assertTrue(isset($field_storage_definitions['revision_translation_affected']));
+
+ $this->assertTrue($entity_test_update->isRevisionable());
+ $this->assertEquals($translatable, isset($field_storage_definitions['revision_translation_affected']));
/** @var \Drupal\Core\Entity\Sql\SqlEntityStorageInterface $storage */
$storage = \Drupal::entityTypeManager()->getStorage('entity_test_update');
@@ -107,10 +101,6 @@ class SqlContentEntityStorageSchemaConverterTest extends UpdatePathTestBase {
$this->assertEqual($i, $revision->id());
$this->assertEqual($i, $revision->getRevisionId());
- // Check that the correct initial value was provided for the
- // 'revision_translation_affected' field.
- $this->assertTrue($revision->revision_translation_affected->value);
-
$this->assertEqual($i . ' - test single property', $revision->test_single_property->value);
$this->assertEqual($i . ' - test multiple properties - value1', $revision->test_multiple_properties->value1);
@@ -131,7 +121,16 @@ class SqlContentEntityStorageSchemaConverterTest extends UpdatePathTestBase {
$this->assertEqual($i . ' - test entity base field info', $revision->test_entity_base_field_info->value);
- // Do the same checks for translated field values.
+ // Do the same checks for translated field values if the entity type is
+ // translatable.
+ if (!$translatable) {
+ continue;
+ }
+
+ // Check that the correct initial value was provided for the
+ // 'revision_translation_affected' field.
+ $this->assertTrue($revision->revision_translation_affected->value);
+
$translation = $revision->getTranslation('ro');
$this->assertEqual($i . ' - test single property - ro', $translation->test_single_property->value);
@@ -168,102 +167,4 @@ class SqlContentEntityStorageSchemaConverterTest extends UpdatePathTestBase {
}
}
- /**
- * Tests that a failed "make revisionable" update preserves the existing data.
- */
- public function testMakeRevisionableErrorHandling() {
- $original_entity_type = $this->lastInstalledSchemaRepository->getLastInstalledDefinition('entity_test_update');
- $original_storage_definitions = $this->lastInstalledSchemaRepository->getLastInstalledFieldStorageDefinitions('entity_test_update');
-
- $original_entity_schema_data = $this->installedStorageSchema->get('entity_test_update.entity_schema_data', []);
- foreach ($original_storage_definitions as $storage_definition) {
- $original_field_schema_data[$storage_definition->getName()] = $this->installedStorageSchema->get('entity_test_update.field_schema_data.' . $storage_definition->getName(), []);
- }
-
- // Check that entity type is not revisionable prior to running the update
- // process.
- $this->assertFalse($original_entity_type->isRevisionable());
-
- // Make the update throw an exception during the entity save process.
- \Drupal::state()->set('entity_test_update.throw_exception', TRUE);
-
- // Since the update process is interrupted by the exception thrown above,
- // we can not do the full post update testing offered by UpdatePathTestBase.
- $this->checkFailedUpdates = FALSE;
-
- // Make the entity type revisionable and run the updates.
- $this->updateEntityTypeToRevisionableAndTranslatable();
-
- $this->runUpdates();
-
- // Check that the update failed.
- $this->assertRaw('' . t('Failed:') . '');
-
- // Check that the last installed entity type definition is kept as
- // non-revisionable.
- $new_entity_type = $this->lastInstalledSchemaRepository->getLastInstalledDefinition('entity_test_update');
- $this->assertFalse($new_entity_type->isRevisionable(), 'The entity type is kept unchanged.');
-
- // Check that the last installed field storage definitions did not change by
- // looking at the 'langcode' field, which is updated automatically.
- $new_storage_definitions = $this->lastInstalledSchemaRepository->getLastInstalledFieldStorageDefinitions('entity_test_update');
- $langcode_key = $original_entity_type->getKey('langcode');
- $this->assertEqual($original_storage_definitions[$langcode_key]->isRevisionable(), $new_storage_definitions[$langcode_key]->isRevisionable(), "The 'langcode' field is kept unchanged.");
-
- /** @var \Drupal\Core\Entity\Sql\SqlEntityStorageInterface $storage */
- $storage = \Drupal::entityTypeManager()->getStorage('entity_test_update');
-
- // Check that installed storage schema did not change.
- $new_entity_schema_data = $this->installedStorageSchema->get('entity_test_update.entity_schema_data', []);
- $this->assertEqual($original_entity_schema_data, $new_entity_schema_data);
-
- foreach ($new_storage_definitions as $storage_definition) {
- $new_field_schema_data[$storage_definition->getName()] = $this->installedStorageSchema->get('entity_test_update.field_schema_data.' . $storage_definition->getName(), []);
- }
- $this->assertEqual($original_field_schema_data, $new_field_schema_data);
-
- // Check that temporary tables have been removed.
- $schema = \Drupal::database()->schema();
- foreach ($storage->getTableMapping()->getTableNames() as $table_name) {
- $this->assertFalse($schema->tableExists(TemporaryTableMapping::getTempTableName($table_name)));
- }
-
- // Check that the original tables still exist and their data is intact.
- $this->assertTrue($schema->tableExists('entity_test_update'));
- $this->assertTrue($schema->tableExists('entity_test_update_data'));
-
- $base_table_count = \Drupal::database()->select('entity_test_update')
- ->countQuery()
- ->execute()
- ->fetchField();
- $this->assertEqual($base_table_count, 102);
-
- $data_table_count = \Drupal::database()->select('entity_test_update_data')
- ->countQuery()
- ->execute()
- ->fetchField();
- // There are two records for each entity, one for English and one for
- // Romanian.
- $this->assertEqual($data_table_count, 204);
-
- $base_table_row = \Drupal::database()->select('entity_test_update')
- ->fields('entity_test_update')
- ->condition('id', 1, '=')
- ->condition('langcode', 'en', '=')
- ->execute()
- ->fetchAllAssoc('id');
- $this->assertEqual('843e9ac7-3351-4cc1-a202-2dbffffae21c', $base_table_row[1]->uuid);
-
- $data_table_row = \Drupal::database()->select('entity_test_update_data')
- ->fields('entity_test_update_data')
- ->condition('id', 1, '=')
- ->condition('langcode', 'en', '=')
- ->execute()
- ->fetchAllAssoc('id');
- $this->assertEqual('1 - test single property', $data_table_row[1]->test_single_property);
- $this->assertEqual('1 - test multiple properties - value1', $data_table_row[1]->test_multiple_properties__value1);
- $this->assertEqual('1 - test multiple properties - value2', $data_table_row[1]->test_multiple_properties__value2);
- $this->assertEqual('1 - test entity base field info', $data_table_row[1]->test_entity_base_field_info);
- }
-
}
diff --git a/core/modules/system/tests/src/Functional/Entity/Update/SqlContentEntityStorageSchemaConverterTranslatableTest.php b/core/modules/system/tests/src/Functional/Entity/Update/SqlContentEntityStorageSchemaConverterTranslatableTest.php
new file mode 100644
index 000000000..853ab6d4b
--- /dev/null
+++ b/core/modules/system/tests/src/Functional/Entity/Update/SqlContentEntityStorageSchemaConverterTranslatableTest.php
@@ -0,0 +1,124 @@
+databaseDumpFiles = [
+ __DIR__ . '/../../../../fixtures/update/drupal-8.0.0-rc1-filled.standard.entity_test_update_mul.php.gz',
+ __DIR__ . '/../../../../fixtures/update/drupal-8.entity-test-schema-converter-enabled.php',
+ ];
+ }
+
+ /**
+ * Tests that a failed "make revisionable" update preserves the existing data.
+ */
+ public function testMakeRevisionableErrorHandling() {
+ $original_entity_type = $this->lastInstalledSchemaRepository->getLastInstalledDefinition('entity_test_update');
+ $original_storage_definitions = $this->lastInstalledSchemaRepository->getLastInstalledFieldStorageDefinitions('entity_test_update');
+
+ $original_entity_schema_data = $this->installedStorageSchema->get('entity_test_update.entity_schema_data', []);
+ foreach ($original_storage_definitions as $storage_definition) {
+ $original_field_schema_data[$storage_definition->getName()] = $this->installedStorageSchema->get('entity_test_update.field_schema_data.' . $storage_definition->getName(), []);
+ }
+
+ // Check that entity type is not revisionable prior to running the update
+ // process.
+ $this->assertFalse($original_entity_type->isRevisionable());
+
+ // Make the update throw an exception during the entity save process.
+ \Drupal::state()->set('entity_test_update.throw_exception', TRUE);
+
+ // Since the update process is interrupted by the exception thrown above,
+ // we can not do the full post update testing offered by UpdatePathTestBase.
+ $this->checkFailedUpdates = FALSE;
+
+ // Make the entity type revisionable and run the updates.
+ $this->updateEntityTypeToRevisionableAndTranslatable();
+
+ $this->runUpdates();
+
+ // Check that the update failed.
+ $this->assertRaw('' . t('Failed:') . '');
+
+ // Check that the last installed entity type definition is kept as
+ // non-revisionable.
+ $new_entity_type = $this->lastInstalledSchemaRepository->getLastInstalledDefinition('entity_test_update');
+ $this->assertFalse($new_entity_type->isRevisionable(), 'The entity type is kept unchanged.');
+
+ // Check that the last installed field storage definitions did not change by
+ // looking at the 'langcode' field, which is updated automatically.
+ $new_storage_definitions = $this->lastInstalledSchemaRepository->getLastInstalledFieldStorageDefinitions('entity_test_update');
+ $langcode_key = $original_entity_type->getKey('langcode');
+ $this->assertEqual($original_storage_definitions[$langcode_key]->isRevisionable(), $new_storage_definitions[$langcode_key]->isRevisionable(), "The 'langcode' field is kept unchanged.");
+
+ /** @var \Drupal\Core\Entity\Sql\SqlEntityStorageInterface $storage */
+ $storage = \Drupal::entityTypeManager()->getStorage('entity_test_update');
+
+ // Check that installed storage schema did not change.
+ $new_entity_schema_data = $this->installedStorageSchema->get('entity_test_update.entity_schema_data', []);
+ $this->assertEqual($original_entity_schema_data, $new_entity_schema_data);
+
+ foreach ($new_storage_definitions as $storage_definition) {
+ $new_field_schema_data[$storage_definition->getName()] = $this->installedStorageSchema->get('entity_test_update.field_schema_data.' . $storage_definition->getName(), []);
+ }
+ $this->assertEqual($original_field_schema_data, $new_field_schema_data);
+
+ // Check that temporary tables have been removed.
+ $schema = \Drupal::database()->schema();
+ foreach ($storage->getTableMapping()->getTableNames() as $table_name) {
+ $this->assertFalse($schema->tableExists(TemporaryTableMapping::getTempTableName($table_name)));
+ }
+
+ // Check that the original tables still exist and their data is intact.
+ $this->assertTrue($schema->tableExists('entity_test_update'));
+ $this->assertTrue($schema->tableExists('entity_test_update_data'));
+
+ $base_table_count = \Drupal::database()->select('entity_test_update')
+ ->countQuery()
+ ->execute()
+ ->fetchField();
+ $this->assertEqual($base_table_count, 102);
+
+ $data_table_count = \Drupal::database()->select('entity_test_update_data')
+ ->countQuery()
+ ->execute()
+ ->fetchField();
+ // There are two records for each entity, one for English and one for
+ // Romanian.
+ $this->assertEqual($data_table_count, 204);
+
+ $base_table_row = \Drupal::database()->select('entity_test_update')
+ ->fields('entity_test_update')
+ ->condition('id', 1, '=')
+ ->condition('langcode', 'en', '=')
+ ->execute()
+ ->fetchAllAssoc('id');
+ $this->assertEqual('843e9ac7-3351-4cc1-a202-2dbffffae21c', $base_table_row[1]->uuid);
+
+ $data_table_row = \Drupal::database()->select('entity_test_update_data')
+ ->fields('entity_test_update_data')
+ ->condition('id', 1, '=')
+ ->condition('langcode', 'en', '=')
+ ->execute()
+ ->fetchAllAssoc('id');
+ $this->assertEqual('1 - test single property', $data_table_row[1]->test_single_property);
+ $this->assertEqual('1 - test multiple properties - value1', $data_table_row[1]->test_multiple_properties__value1);
+ $this->assertEqual('1 - test multiple properties - value2', $data_table_row[1]->test_multiple_properties__value2);
+ $this->assertEqual('1 - test entity base field info', $data_table_row[1]->test_entity_base_field_info);
+ }
+
+}
diff --git a/core/modules/system/tests/src/Functional/Entity/Update/SqlContentEntityStorageSchemaIndexFilledTest.php b/core/modules/system/tests/src/Functional/Entity/Update/SqlContentEntityStorageSchemaIndexFilledTest.php
index d3c6912f9..ee0e5b51a 100644
--- a/core/modules/system/tests/src/Functional/Entity/Update/SqlContentEntityStorageSchemaIndexFilledTest.php
+++ b/core/modules/system/tests/src/Functional/Entity/Update/SqlContentEntityStorageSchemaIndexFilledTest.php
@@ -6,6 +6,7 @@ namespace Drupal\Tests\system\Functional\Entity\Update;
* Runs SqlContentEntityStorageSchemaIndexTest with a dump filled with content.
*
* @group Entity
+ * @group legacy
*/
class SqlContentEntityStorageSchemaIndexFilledTest extends SqlContentEntityStorageSchemaIndexTest {
diff --git a/core/modules/system/tests/src/Functional/Entity/Update/SqlContentEntityStorageSchemaIndexTest.php b/core/modules/system/tests/src/Functional/Entity/Update/SqlContentEntityStorageSchemaIndexTest.php
index 395423033..408493f90 100644
--- a/core/modules/system/tests/src/Functional/Entity/Update/SqlContentEntityStorageSchemaIndexTest.php
+++ b/core/modules/system/tests/src/Functional/Entity/Update/SqlContentEntityStorageSchemaIndexTest.php
@@ -8,6 +8,7 @@ use Drupal\FunctionalTests\Update\UpdatePathTestBase;
* Tests that a newly-added index is properly created during database updates.
*
* @group Entity
+ * @group legacy
*/
class SqlContentEntityStorageSchemaIndexTest extends UpdatePathTestBase {
diff --git a/core/modules/system/tests/src/Functional/Entity/Update/UpdateApiEntityDefinitionUpdateTest.php b/core/modules/system/tests/src/Functional/Entity/Update/UpdateApiEntityDefinitionUpdateTest.php
index f3285ae5d..1c3c6e3e0 100644
--- a/core/modules/system/tests/src/Functional/Entity/Update/UpdateApiEntityDefinitionUpdateTest.php
+++ b/core/modules/system/tests/src/Functional/Entity/Update/UpdateApiEntityDefinitionUpdateTest.php
@@ -34,7 +34,6 @@ class UpdateApiEntityDefinitionUpdateTest extends BrowserTestBase {
*/
protected $updatesManager;
-
/**
* {@inheritdoc}
*/
diff --git a/core/modules/system/tests/src/Functional/FileTransfer/FileTransferTest.php b/core/modules/system/tests/src/Functional/FileTransfer/FileTransferTest.php
index 04b1f6636..a15a92a6d 100644
--- a/core/modules/system/tests/src/Functional/FileTransfer/FileTransferTest.php
+++ b/core/modules/system/tests/src/Functional/FileTransfer/FileTransferTest.php
@@ -20,7 +20,7 @@ class FileTransferTest extends BrowserTestBase {
protected function setUp() {
parent::setUp();
- $this->testConnection = TestFileTransfer::factory(\Drupal::root(), ['hostname' => $this->hostname, 'username' => $this->username, 'password' => $this->password, 'port' => $this->port]);
+ $this->testConnection = TestFileTransfer::factory($this->root, ['hostname' => $this->hostname, 'username' => $this->username, 'password' => $this->password, 'port' => $this->port]);
}
public function _getFakeModuleFiles() {
@@ -28,11 +28,11 @@ class FileTransferTest extends BrowserTestBase {
'fake.module',
'fake.info.yml',
'theme' => [
- 'fake.html.twig'
+ 'fake.html.twig',
],
'inc' => [
- 'fake.inc'
- ]
+ 'fake.inc',
+ ],
];
return $files;
}
@@ -82,7 +82,7 @@ class FileTransferTest extends BrowserTestBase {
$gotit = TRUE;
try {
- $this->testConnection->copyDirectory($source, \Drupal::root() . '/' . PublicStream::basePath());
+ $this->testConnection->copyDirectory($source, $this->root . '/' . PublicStream::basePath());
}
catch (FileTransferException $e) {
$gotit = FALSE;
diff --git a/core/modules/system/tests/src/Functional/Form/ConfirmFormTest.php b/core/modules/system/tests/src/Functional/Form/ConfirmFormTest.php
index c65546a48..ad5a74b69 100644
--- a/core/modules/system/tests/src/Functional/Form/ConfirmFormTest.php
+++ b/core/modules/system/tests/src/Functional/Form/ConfirmFormTest.php
@@ -2,7 +2,7 @@
namespace Drupal\Tests\system\Functional\Form;
-use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Url;
use Drupal\Tests\BrowserTestBase;
@@ -79,7 +79,7 @@ class ConfirmFormTest extends BrowserTestBase {
*/
public function assertCancelLinkUrl(Url $url, $message = '', $group = 'Other') {
$links = $this->xpath('//a[@href=:url]', [':url' => $url->toString()]);
- $message = ($message ? $message : SafeMarkup::format('Cancel link with URL %url found.', ['%url' => $url->toString()]));
+ $message = ($message ? $message : new FormattableMarkup('Cancel link with URL %url found.', ['%url' => $url->toString()]));
return $this->assertTrue(isset($links[0]), $message, $group);
}
diff --git a/core/modules/system/tests/src/Functional/Form/ElementTest.php b/core/modules/system/tests/src/Functional/Form/ElementTest.php
index 57da8daf2..6eefa484b 100644
--- a/core/modules/system/tests/src/Functional/Form/ElementTest.php
+++ b/core/modules/system/tests/src/Functional/Form/ElementTest.php
@@ -223,4 +223,12 @@ class ElementTest extends BrowserTestBase {
$this->assertText('I am an error on the details element.');
}
+ /**
+ * Tests summary attributes of details.
+ */
+ public function testDetailsSummaryAttributes() {
+ $this->drupalGet('form-test/group-details');
+ $this->assertTrue($this->cssSelect('summary[data-summary-attribute="test"]'));
+ }
+
}
diff --git a/core/modules/system/tests/src/Functional/Form/ElementsLabelsTest.php b/core/modules/system/tests/src/Functional/Form/ElementsLabelsTest.php
index 6747ed63f..91f3ec4a5 100644
--- a/core/modules/system/tests/src/Functional/Form/ElementsLabelsTest.php
+++ b/core/modules/system/tests/src/Functional/Form/ElementsLabelsTest.php
@@ -2,6 +2,7 @@
namespace Drupal\Tests\system\Functional\Form;
+use Drupal\form_test\Form\FormTestLabelForm;
use Drupal\Tests\BrowserTestBase;
/**
@@ -96,6 +97,17 @@ class ElementsLabelsTest extends BrowserTestBase {
$this->assertTrue(!empty($elements), "Title/Label not displayed when 'visually-hidden' attribute is set in radios.");
}
+ /**
+ * Tests XSS-protection of element labels.
+ */
+ public function testTitleEscaping() {
+ $this->drupalGet('form_test/form-labels');
+ foreach (FormTestLabelForm::$typesWithTitle as $type) {
+ $this->assertSession()->responseContains("$type alert('XSS') is XSS filtered!");
+ $this->assertSession()->responseNotContains("$type is XSS filtered!");
+ }
+ }
+
/**
* Tests different display options for form element descriptions.
*/
diff --git a/core/modules/system/tests/src/Functional/Form/ElementsVerticalTabsTest.php b/core/modules/system/tests/src/Functional/Form/ElementsVerticalTabsTest.php
index 07f25fef6..34ca1c9cf 100644
--- a/core/modules/system/tests/src/Functional/Form/ElementsVerticalTabsTest.php
+++ b/core/modules/system/tests/src/Functional/Form/ElementsVerticalTabsTest.php
@@ -2,7 +2,7 @@
namespace Drupal\Tests\system\Functional\Form;
-use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Component\Render\FormattableMarkup;
use Drupal\Component\Serialization\Json;
use Drupal\Tests\BrowserTestBase;
@@ -89,7 +89,7 @@ class ElementsVerticalTabsTest extends BrowserTestBase {
*/
public function testDefaultTabCleaned() {
$values = Json::decode($this->drupalPostForm('form_test/form-state-values-clean', [], t('Submit')));
- $this->assertFalse(isset($values['vertical_tabs__active_tab']), SafeMarkup::format('%element was removed.', ['%element' => 'vertical_tabs__active_tab']));
+ $this->assertFalse(isset($values['vertical_tabs__active_tab']), new FormattableMarkup('%element was removed.', ['%element' => 'vertical_tabs__active_tab']));
}
}
diff --git a/core/modules/system/tests/src/Functional/Form/FormObjectTest.php b/core/modules/system/tests/src/Functional/Form/FormObjectTest.php
index 34ebb2e79..e2c00f5d1 100644
--- a/core/modules/system/tests/src/Functional/Form/FormObjectTest.php
+++ b/core/modules/system/tests/src/Functional/Form/FormObjectTest.php
@@ -2,15 +2,14 @@
namespace Drupal\Tests\system\Functional\Form;
-use Drupal\system\Tests\System\SystemConfigFormTestBase;
-use Drupal\form_test\FormTestObject;
+use Drupal\Tests\BrowserTestBase;
/**
* Tests building a form from an object.
*
* @group Form
*/
-class FormObjectTest extends SystemConfigFormTestBase {
+class FormObjectTest extends BrowserTestBase {
/**
* Modules to enable.
@@ -19,19 +18,6 @@ class FormObjectTest extends SystemConfigFormTestBase {
*/
public static $modules = ['form_test'];
- protected function setUp() {
- parent::setUp();
-
- $this->form = new FormTestObject($this->container->get('config.factory'));
- $this->values = [
- 'bananas' => [
- '#value' => $this->randomString(10),
- '#config_name' => 'form_test.object',
- '#config_key' => 'bananas',
- ],
- ];
- }
-
/**
* Tests using an object as the form callback.
*
diff --git a/core/modules/system/tests/src/Functional/Form/FormTest.php b/core/modules/system/tests/src/Functional/Form/FormTest.php
index 87c763245..849944b0f 100644
--- a/core/modules/system/tests/src/Functional/Form/FormTest.php
+++ b/core/modules/system/tests/src/Functional/Form/FormTest.php
@@ -4,7 +4,7 @@ namespace Drupal\Tests\system\Functional\Form;
use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\Html;
-use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Form\FormState;
use Drupal\Core\Render\Element;
use Drupal\Core\Url;
@@ -151,7 +151,7 @@ class FormTest extends BrowserTestBase {
}
}
// Clear the expected form error messages so they don't appear as exceptions.
- drupal_get_messages();
+ \Drupal::messenger()->deleteAll();
}
/**
@@ -426,7 +426,7 @@ class FormTest extends BrowserTestBase {
'multiple_no_default_required[]' => 'three',
];
$this->drupalPostForm(NULL, $edit, 'Submit');
- $values = Json::decode($this->getRawContent());
+ $values = Json::decode($this->getSession()->getPage()->getContent());
// Verify expected values.
$expected = [
@@ -622,7 +622,7 @@ class FormTest extends BrowserTestBase {
// the disabled container.
$actual_count = count($disabled_elements);
$expected_count = 42;
- $this->assertEqual($actual_count, $expected_count, SafeMarkup::format('Found @actual elements with disabled property (expected @expected).', [
+ $this->assertEqual($actual_count, $expected_count, new FormattableMarkup('Found @actual elements with disabled property (expected @expected).', [
'@actual' => count($disabled_elements),
'@expected' => $expected_count,
]));
diff --git a/core/modules/system/tests/src/Functional/Form/LanguageSelectElementTest.php b/core/modules/system/tests/src/Functional/Form/LanguageSelectElementTest.php
index 1bfa19f48..0bd9bb8e6 100644
--- a/core/modules/system/tests/src/Functional/Form/LanguageSelectElementTest.php
+++ b/core/modules/system/tests/src/Functional/Form/LanguageSelectElementTest.php
@@ -45,7 +45,7 @@ class LanguageSelectElementTest extends BrowserTestBase {
'edit-languages-all' => LanguageInterface::STATE_ALL,
'edit-languages-configurable' => LanguageInterface::STATE_CONFIGURABLE,
'edit-languages-locked' => LanguageInterface::STATE_LOCKED,
- 'edit-languages-config-and-locked' => LanguageInterface::STATE_CONFIGURABLE | LanguageInterface::STATE_LOCKED
+ 'edit-languages-config-and-locked' => LanguageInterface::STATE_CONFIGURABLE | LanguageInterface::STATE_LOCKED,
];
foreach ($ids as $id => $flags) {
$this->assertField($id, format_string('The @id field was found on the page.', ['@id' => $id]));
@@ -83,7 +83,7 @@ class LanguageSelectElementTest extends BrowserTestBase {
// field elements.
$edit = [];
$this->drupalPostForm(NULL, $edit, t('Submit'));
- $values = Json::decode($this->getRawContent());
+ $values = Json::decode($this->getSession()->getPage()->getContent());
$this->assertEqual($values['languages_all'], 'xx');
$this->assertEqual($values['languages_configurable'], 'en');
$this->assertEqual($values['languages_locked'], LanguageInterface::LANGCODE_NOT_SPECIFIED);
diff --git a/core/modules/system/tests/src/Functional/Form/StateValuesCleanTest.php b/core/modules/system/tests/src/Functional/Form/StateValuesCleanTest.php
index 626648724..cae0835b5 100644
--- a/core/modules/system/tests/src/Functional/Form/StateValuesCleanTest.php
+++ b/core/modules/system/tests/src/Functional/Form/StateValuesCleanTest.php
@@ -3,7 +3,7 @@
namespace Drupal\Tests\system\Functional\Form;
use Drupal\Component\Serialization\Json;
-use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Component\Render\FormattableMarkup;
use Drupal\Tests\BrowserTestBase;
/**
@@ -47,7 +47,7 @@ class StateValuesCleanTest extends BrowserTestBase {
$this->assertFalse(isset($values['baz']['baz']), format_string('%element was removed.', ['%element' => 'baz']));
// Verify values manually added for cleaning were removed.
- $this->assertFalse(isset($values['wine']), SafeMarkup::format('%element was removed.', ['%element' => 'wine']));
+ $this->assertFalse(isset($values['wine']), new FormattableMarkup('%element was removed.', ['%element' => 'wine']));
// Verify that nested form value still exists.
$this->assertTrue(isset($values['baz']['beer']), 'Nested form value still exists.');
diff --git a/core/modules/hal/tests/src/Functional/EntityResource/Action/ActionHalJsonAnonTest.php b/core/modules/system/tests/src/Functional/Hal/ActionHalJsonAnonTest.php
similarity index 74%
rename from core/modules/hal/tests/src/Functional/EntityResource/Action/ActionHalJsonAnonTest.php
rename to core/modules/system/tests/src/Functional/Hal/ActionHalJsonAnonTest.php
index 0eae86630..eb19bac6f 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/Action/ActionHalJsonAnonTest.php
+++ b/core/modules/system/tests/src/Functional/Hal/ActionHalJsonAnonTest.php
@@ -1,9 +1,9 @@
verbose($this->stringToHtml($output));
}
- $output_upper = Unicode::strtoupper($output);
- $upper_input = Unicode::strtoupper($input);
+ $output_upper = mb_strtoupper($output);
+ $upper_input = mb_strtoupper($input);
$upper_output = MailFormatHelper::htmlToText($upper_input);
$pass = $this->assertEqual(
$upper_output,
@@ -347,8 +347,8 @@ class HtmlToTextTest extends BrowserTestBase {
$maximum_line_length = 0;
foreach (explode($eol, $output) as $line) {
- // We must use strlen() rather than Unicode::strlen() in order to count
- // octets rather than characters.
+ // We must use strlen() rather than mb_strlen() in order to count octets
+ // rather than characters.
$maximum_line_length = max($maximum_line_length, strlen($line . $eol));
}
$verbose = 'Maximum line length found was ' . $maximum_line_length . ' octets.';
diff --git a/core/modules/system/tests/src/Functional/Menu/LocalTasksTest.php b/core/modules/system/tests/src/Functional/Menu/LocalTasksTest.php
index 12a157d58..1a4933677 100644
--- a/core/modules/system/tests/src/Functional/Menu/LocalTasksTest.php
+++ b/core/modules/system/tests/src/Functional/Menu/LocalTasksTest.php
@@ -18,7 +18,7 @@ class LocalTasksTest extends BrowserTestBase {
*
* @var string[]
*/
- public static $modules = ['block', 'menu_test', 'entity_test'];
+ public static $modules = ['block', 'menu_test', 'entity_test', 'node'];
/**
* The local tasks block under testing.
@@ -253,4 +253,29 @@ class LocalTasksTest extends BrowserTestBase {
$this->assertLocalTasks($sub_tasks, 1);
}
+ /**
+ * Test that local tasks blocks cache is invalidated correctly.
+ */
+ public function testLocalTaskBlockCache() {
+ $this->drupalLogin($this->rootUser);
+ $this->drupalCreateContentType(['type' => 'page']);
+
+ $this->drupalGet('/admin/structure/types/manage/page');
+
+ // Only the Edit task. The block avoids showing a single tab.
+ $this->assertNoLocalTasks();
+
+ // Field UI adds the usual Manage fields etc tabs.
+ \Drupal::service('module_installer')->install(['field_ui']);
+
+ $this->drupalGet('/admin/structure/types/manage/page');
+
+ $this->assertLocalTasks([
+ ['entity.node_type.edit_form', ['node_type' => 'page']],
+ ['entity.node.field_ui_fields', ['node_type' => 'page']],
+ ['entity.entity_form_display.node.default', ['node_type' => 'page']],
+ ['entity.entity_view_display.node.default', ['node_type' => 'page']],
+ ]);
+ }
+
}
diff --git a/core/modules/system/tests/src/Functional/Module/DependencyTest.php b/core/modules/system/tests/src/Functional/Module/DependencyTest.php
index 0fc78caa9..d2938721a 100644
--- a/core/modules/system/tests/src/Functional/Module/DependencyTest.php
+++ b/core/modules/system/tests/src/Functional/Module/DependencyTest.php
@@ -10,6 +10,7 @@ use Drupal\Component\Utility\Unicode;
* @group Module
*/
class DependencyTest extends ModuleTestBase {
+
/**
* Checks functionality of project namespaces for dependencies.
*/
diff --git a/core/modules/system/tests/src/Functional/Module/HookRequirementsTest.php b/core/modules/system/tests/src/Functional/Module/HookRequirementsTest.php
index a873d9652..2ada3a285 100644
--- a/core/modules/system/tests/src/Functional/Module/HookRequirementsTest.php
+++ b/core/modules/system/tests/src/Functional/Module/HookRequirementsTest.php
@@ -8,6 +8,7 @@ namespace Drupal\Tests\system\Functional\Module;
* @group Module
*/
class HookRequirementsTest extends ModuleTestBase {
+
/**
* Assert that a module cannot be installed if it fails hook_requirements().
*/
diff --git a/core/modules/system/tests/src/Functional/Module/InstallUninstallTest.php b/core/modules/system/tests/src/Functional/Module/InstallUninstallTest.php
index 4d8f30d65..9ca440780 100644
--- a/core/modules/system/tests/src/Functional/Module/InstallUninstallTest.php
+++ b/core/modules/system/tests/src/Functional/Module/InstallUninstallTest.php
@@ -4,6 +4,7 @@ namespace Drupal\Tests\system\Functional\Module;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Logger\RfcLogLevel;
+use Drupal\workspaces\Entity\Workspace;
/**
* Install/uninstall core module and confirm table creation/deletion.
@@ -22,7 +23,8 @@ class InstallUninstallTest extends ModuleTestBase {
*/
public function testInstallUninstall() {
// Set a variable so that the hook implementations in system_test.module
- // will display messages via drupal_set_message().
+ // will display messages via
+ // \Drupal\Core\Messenger\MessengerInterface::addStatus().
$this->container->get('state')->set('system_test.verbose_module_hooks', TRUE);
// Install and uninstall module_test to ensure hook_preinstall_module and
@@ -147,6 +149,12 @@ class InstallUninstallTest extends ModuleTestBase {
$this->preUninstallForum();
}
+ // Delete all workspaces before uninstall.
+ if ($name == 'workspaces') {
+ $workspaces = Workspace::loadMultiple();
+ \Drupal::entityTypeManager()->getStorage('workspace')->delete($workspaces);
+ }
+
$now_installed_list = \Drupal::moduleHandler()->getModuleList();
$added_modules = array_diff(array_keys($now_installed_list), array_keys($was_installed_list));
while ($added_modules) {
diff --git a/core/modules/system/tests/src/Functional/Module/ModuleTestBase.php b/core/modules/system/tests/src/Functional/Module/ModuleTestBase.php
index abdbe0391..6cc726326 100644
--- a/core/modules/system/tests/src/Functional/Module/ModuleTestBase.php
+++ b/core/modules/system/tests/src/Functional/Module/ModuleTestBase.php
@@ -56,8 +56,9 @@ abstract class ModuleTestBase extends BrowserTestBase {
public function assertModuleTablesExist($module) {
$tables = array_keys(drupal_get_module_schema($module));
$tables_exist = TRUE;
+ $schema = Database::getConnection()->schema();
foreach ($tables as $table) {
- if (!db_table_exists($table)) {
+ if (!$schema->tableExists($table)) {
$tables_exist = FALSE;
}
}
@@ -73,8 +74,9 @@ abstract class ModuleTestBase extends BrowserTestBase {
public function assertModuleTablesDoNotExist($module) {
$tables = array_keys(drupal_get_module_schema($module));
$tables_exist = FALSE;
+ $schema = Database::getConnection()->schema();
foreach ($tables as $table) {
- if (db_table_exists($table)) {
+ if ($schema->tableExists($table)) {
$tables_exist = TRUE;
}
}
@@ -109,16 +111,18 @@ abstract class ModuleTestBase extends BrowserTestBase {
}
$this->assertTrue($all_names);
+ $module_config_dependencies = \Drupal::service('config.manager')->findConfigEntityDependents('module', [$module]);
// Look up each default configuration object name in the active
// configuration, and if it exists, remove it from the stack.
- // Only default config that belongs to $module is guaranteed to exist; any
- // other default config depends on whether other modules are enabled. Thus,
- // list all default config once more, but filtered by $module.
- $names = $module_file_storage->listAll($module . '.');
+ $names = $module_file_storage->listAll();
foreach ($names as $key => $name) {
if ($this->config($name)->get()) {
unset($names[$key]);
}
+ // All configuration in a module's config/install directory should depend
+ // on the module as it must be removed on uninstall or the module will not
+ // be re-installable.
+ $this->assertTrue(strpos($name, $module . '.') === 0 || isset($module_config_dependencies[$name]), "Configuration $name provided by $module in its config/install directory does not depend on it.");
}
// Verify that all configuration has been installed (which means that $names
// is empty).
diff --git a/core/modules/system/tests/src/Functional/Module/PrepareUninstallTest.php b/core/modules/system/tests/src/Functional/Module/PrepareUninstallTest.php
index 86d06e35e..5edaf42af 100644
--- a/core/modules/system/tests/src/Functional/Module/PrepareUninstallTest.php
+++ b/core/modules/system/tests/src/Functional/Module/PrepareUninstallTest.php
@@ -2,7 +2,6 @@
namespace Drupal\Tests\system\Functional\Module;
-use Drupal\Component\Utility\Unicode;
use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\taxonomy\Functional\TaxonomyTestTrait;
@@ -78,15 +77,15 @@ class PrepareUninstallTest extends BrowserTestBase {
$this->assertText($this->terms[$term_count - $i]->label());
}
$term_count = $term_count - 10;
- $this->assertText("And $term_count more taxonomy term entities.");
+ $this->assertText("And $term_count more taxonomy terms.");
$this->assertText('This action cannot be undone.');
$this->assertText('Make a backup of your database if you want to be able to restore these items.');
- $this->drupalPostForm(NULL, [], t('Delete all taxonomy term entities'));
+ $this->drupalPostForm(NULL, [], t('Delete all taxonomy terms'));
// Check that we are redirected to the uninstall page and data has been
// removed.
$this->assertUrl('admin/modules/uninstall', []);
- $this->assertText('All taxonomy term entities have been deleted.');
+ $this->assertText('All taxonomy terms have been deleted.');
// Check that there is no more data to be deleted, Taxonomy is ready to be
// uninstalled.
@@ -162,14 +161,14 @@ class PrepareUninstallTest extends BrowserTestBase {
$storage = $this->container->get('entity.manager')
->getStorage('entity_test_no_label');
$storage->create([
- 'id' => Unicode::strtolower($this->randomMachineName()),
+ 'id' => mb_strtolower($this->randomMachineName()),
'name' => $this->randomMachineName(),
])->save();
$this->drupalGet('admin/modules/uninstall/entity/entity_test_no_label');
$this->assertText('This will delete 1 entity test without label.');
$this->assertFieldByXPath($button_xpath, NULL, 'Button with value "Delete all entity test without label entities" found');
$storage->create([
- 'id' => Unicode::strtolower($this->randomMachineName()),
+ 'id' => mb_strtolower($this->randomMachineName()),
'name' => $this->randomMachineName(),
])->save();
$this->drupalGet('admin/modules/uninstall/entity/entity_test_no_label');
diff --git a/core/modules/system/tests/src/Functional/Module/RequiredTest.php b/core/modules/system/tests/src/Functional/Module/RequiredTest.php
deleted file mode 100644
index 00c7cbe70..000000000
--- a/core/modules/system/tests/src/Functional/Module/RequiredTest.php
+++ /dev/null
@@ -1,32 +0,0 @@
-drupalGet('admin/modules');
- foreach ($module_info as $module => $info) {
- // Check to make sure the checkbox for each required module is disabled
- // and checked (or absent from the page if the module is also hidden).
- if (!empty($info['required'])) {
- $field_name = 'modules[' . $module . '][enable]';
- if (empty($info['hidden'])) {
- $this->assertFieldByXPath("//input[@name='$field_name' and @disabled='disabled' and @checked='checked']", '', format_string('Field @name was disabled and checked.', ['@name' => $field_name]));
- }
- else {
- $this->assertNoFieldByName($field_name);
- }
- }
- }
- }
-
-}
diff --git a/core/modules/system/tests/src/Functional/Module/UninstallTest.php b/core/modules/system/tests/src/Functional/Module/UninstallTest.php
index 2b17b8fcf..98b96ee91 100644
--- a/core/modules/system/tests/src/Functional/Module/UninstallTest.php
+++ b/core/modules/system/tests/src/Functional/Module/UninstallTest.php
@@ -3,7 +3,7 @@
namespace Drupal\Tests\system\Functional\Module;
use Drupal\Core\Cache\Cache;
-use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Entity\EntityMalformedException;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
@@ -57,6 +57,17 @@ class UninstallTest extends BrowserTestBase {
$this->drupalGet('admin/modules/uninstall');
$this->assertTitle(t('Uninstall') . ' | Drupal');
+ foreach (\Drupal::service('extension.list.module')->getAllInstalledInfo() as $module => $info) {
+ $field_name = "uninstall[$module]";
+ if (!empty($info['required'])) {
+ // A required module should not be listed on the uninstall page.
+ $this->assertSession()->fieldNotExists($field_name);
+ }
+ else {
+ $this->assertSession()->fieldExists($field_name);
+ }
+ }
+
// Be sure labels are rendered properly.
// @see regression https://www.drupal.org/node/2512106
$this->assertRaw('');
@@ -103,7 +114,7 @@ class UninstallTest extends BrowserTestBase {
// cleared during the uninstall.
\Drupal::cache()->set('uninstall_test', 'test_uninstall_page', Cache::PERMANENT);
$cached = \Drupal::cache()->get('uninstall_test');
- $this->assertEqual($cached->data, 'test_uninstall_page', SafeMarkup::format('Cache entry found: @bin', ['@bin' => $cached->data]));
+ $this->assertEqual($cached->data, 'test_uninstall_page', new FormattableMarkup('Cache entry found: @bin', ['@bin' => $cached->data]));
$this->drupalPostForm(NULL, NULL, t('Uninstall'));
$this->assertText(t('The selected modules have been uninstalled.'), 'Modules status has been updated.');
diff --git a/core/modules/system/tests/src/Functional/Render/AjaxPageStateTest.php b/core/modules/system/tests/src/Functional/Render/AjaxPageStateTest.php
index 6f5dc7da6..586b1c93b 100644
--- a/core/modules/system/tests/src/Functional/Render/AjaxPageStateTest.php
+++ b/core/modules/system/tests/src/Functional/Render/AjaxPageStateTest.php
@@ -67,9 +67,9 @@ class AjaxPageStateTest extends BrowserTestBase {
"query" =>
[
'ajax_page_state' => [
- 'libraries' => 'core/html5shiv'
- ]
- ]
+ 'libraries' => 'core/html5shiv',
+ ],
+ ],
]
);
$this->assertNoRaw(
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Action/ActionJsonAnonTest.php b/core/modules/system/tests/src/Functional/Rest/ActionJsonAnonTest.php
similarity index 84%
rename from core/modules/rest/tests/src/Functional/EntityResource/Action/ActionJsonAnonTest.php
rename to core/modules/system/tests/src/Functional/Rest/ActionJsonAnonTest.php
index 4d55c25b9..07a7a5b3d 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/Action/ActionJsonAnonTest.php
+++ b/core/modules/system/tests/src/Functional/Rest/ActionJsonAnonTest.php
@@ -1,6 +1,6 @@
grantPermissionsToTestedRole(['administer actions']);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function createEntity() {
+ $action = Action::create([
+ 'id' => 'user_add_role_action.' . RoleInterface::ANONYMOUS_ID,
+ 'type' => 'user',
+ 'label' => t('Add the anonymous role to the selected users'),
+ 'configuration' => [
+ 'rid' => RoleInterface::ANONYMOUS_ID,
+ ],
+ 'plugin' => 'user_add_role_action',
+ ]);
+ $action->save();
+
+ return $action;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedNormalizedEntity() {
+ return [
+ 'configuration' => [
+ 'rid' => 'anonymous',
+ ],
+ 'dependencies' => [
+ 'config' => ['user.role.anonymous'],
+ 'module' => ['user'],
+ ],
+ 'id' => 'user_add_role_action.anonymous',
+ 'label' => 'Add the anonymous role to the selected users',
+ 'langcode' => 'en',
+ 'plugin' => 'user_add_role_action',
+ 'status' => TRUE,
+ 'type' => 'user',
+ 'uuid' => $this->entity->uuid(),
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedCacheContexts() {
+ return [
+ 'user.permissions',
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getNormalizedPostEntity() {
+ // @todo Update in https://www.drupal.org/node/2300677.
+ }
+
+}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Action/ActionXmlAnonTest.php b/core/modules/system/tests/src/Functional/Rest/ActionXmlAnonTest.php
similarity index 88%
rename from core/modules/rest/tests/src/Functional/EntityResource/Action/ActionXmlAnonTest.php
rename to core/modules/system/tests/src/Functional/Rest/ActionXmlAnonTest.php
index 3bce75ebd..e45af980e 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/Action/ActionXmlAnonTest.php
+++ b/core/modules/system/tests/src/Functional/Rest/ActionXmlAnonTest.php
@@ -1,6 +1,6 @@
grantPermissionsToTestedRole(['administer menu']);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function createEntity() {
+ $menu = Menu::create([
+ 'id' => 'menu',
+ 'label' => 'Menu',
+ 'description' => 'Menu',
+ ]);
+ $menu->save();
+
+ return $menu;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedNormalizedEntity() {
+ return [
+ 'dependencies' => [],
+ 'description' => 'Menu',
+ 'id' => 'menu',
+ 'label' => 'Menu',
+ 'langcode' => 'en',
+ 'locked' => FALSE,
+ 'status' => TRUE,
+ 'uuid' => $this->entity->uuid(),
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getNormalizedPostEntity() {
+ // @todo Update in https://www.drupal.org/node/2300677.
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedCacheContexts() {
+ return [
+ 'user.permissions',
+ ];
+ }
+
+}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Menu/MenuXmlAnonTest.php b/core/modules/system/tests/src/Functional/Rest/MenuXmlAnonTest.php
similarity index 88%
rename from core/modules/rest/tests/src/Functional/EntityResource/Menu/MenuXmlAnonTest.php
rename to core/modules/system/tests/src/Functional/Rest/MenuXmlAnonTest.php
index 22584501c..e1c23b5f7 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/Menu/MenuXmlAnonTest.php
+++ b/core/modules/system/tests/src/Functional/Rest/MenuXmlAnonTest.php
@@ -1,6 +1,6 @@
assertTrue(\Drupal::hasService('service_provider_test_class'), 'The service_provider_test_class service has been registered to the DIC');
- // The event subscriber method in the test class calls drupal_set_message()
- // with a message saying it has fired. This will fire on every page request
- // so it should show up on the front page.
+ // The event subscriber method in the test class calls
+ // \Drupal\Core\Messenger\MessengerInterface::addStatus() with a message
+ // saying it has fired. This will fire on every page request so it should
+ // show up on the front page.
$this->drupalGet('');
$this->assertText(t('The service_provider_test event subscriber fired!'), 'The service_provider_test event subscriber fired');
}
diff --git a/core/modules/system/src/Tests/System/AccessDeniedTest.php b/core/modules/system/tests/src/Functional/System/AccessDeniedTest.php
similarity index 86%
rename from core/modules/system/src/Tests/System/AccessDeniedTest.php
rename to core/modules/system/tests/src/Functional/System/AccessDeniedTest.php
index ba1d59967..6490910be 100644
--- a/core/modules/system/src/Tests/System/AccessDeniedTest.php
+++ b/core/modules/system/tests/src/Functional/System/AccessDeniedTest.php
@@ -1,9 +1,10 @@
drupalLogin($this->createUser([]));
$this->drupalGet('admin', ['query' => ['foo' => 'bar']]);
- $this->assertEqual($this->drupalSettings['path']['currentPath'], 'admin');
- $this->assertEqual($this->drupalSettings['path']['currentPathIsAdmin'], TRUE);
- $this->assertEqual($this->drupalSettings['path']['currentQuery'], ['foo' => 'bar']);
+
+ $settings = $this->getDrupalSettings();
+ $this->assertEqual($settings['path']['currentPath'], 'admin');
+ $this->assertEqual($settings['path']['currentPathIsAdmin'], TRUE);
+ $this->assertEqual($settings['path']['currentQuery'], ['foo' => 'bar']);
$this->drupalLogin($this->adminUser);
@@ -56,7 +61,7 @@ class AccessDeniedTest extends WebTestBase {
'site_403' => 'user/' . $this->adminUser->id(),
];
$this->drupalPostForm('admin/config/system/site-information', $edit, t('Save configuration'));
- $this->assertRaw(SafeMarkup::format("The path '%path' has to start with a slash.", ['%path' => $edit['site_403']]));
+ $this->assertRaw(new FormattableMarkup("The path '%path' has to start with a slash.", ['%path' => $edit['site_403']]));
// Use a custom 403 page.
$edit = [
diff --git a/core/modules/system/tests/src/Functional/System/AdminMetaTagTest.php b/core/modules/system/tests/src/Functional/System/AdminMetaTagTest.php
index dbd82cda0..6fcc4b848 100644
--- a/core/modules/system/tests/src/Functional/System/AdminMetaTagTest.php
+++ b/core/modules/system/tests/src/Functional/System/AdminMetaTagTest.php
@@ -10,6 +10,7 @@ use Drupal\Tests\BrowserTestBase;
* @group system
*/
class AdminMetaTagTest extends BrowserTestBase {
+
/**
* Verify that the meta tag HTML is generated correctly.
*/
diff --git a/core/modules/system/src/Tests/System/AdminTest.php b/core/modules/system/tests/src/Functional/System/AdminTest.php
similarity index 87%
rename from core/modules/system/src/Tests/System/AdminTest.php
rename to core/modules/system/tests/src/Functional/System/AdminTest.php
index 7921e1c4c..7a0b2a3ae 100644
--- a/core/modules/system/src/Tests/System/AdminTest.php
+++ b/core/modules/system/tests/src/Functional/System/AdminTest.php
@@ -1,16 +1,16 @@
getSession();
+
// The front page defaults to 'user/login', which redirects to 'user/{user}'
// for authenticated users. We cannot use '', since this does not
// match the redirected url.
@@ -152,20 +154,20 @@ class AdminTest extends WebTestBase {
$this->drupalGet('admin/compact/on');
$this->assertResponse(200, 'A valid page is returned after turning on compact mode.');
$this->assertUrl($frontpage_url, [], 'The user is redirected to the front page after turning on compact mode.');
- $this->assertTrue($this->cookies['Drupal.visitor.admin_compact_mode']['value'], 'Compact mode turns on.');
+ $this->assertTrue($session->getCookie('Drupal.visitor.admin_compact_mode'), 'Compact mode turns on.');
$this->drupalGet('admin/compact/on');
- $this->assertTrue($this->cookies['Drupal.visitor.admin_compact_mode']['value'], 'Compact mode remains on after a repeat call.');
+ $this->assertTrue($session->getCookie('Drupal.visitor.admin_compact_mode'), 'Compact mode remains on after a repeat call.');
$this->drupalGet('');
- $this->assertTrue($this->cookies['Drupal.visitor.admin_compact_mode']['value'], 'Compact mode persists on new requests.');
+ $this->assertTrue($session->getCookie('Drupal.visitor.admin_compact_mode'), 'Compact mode persists on new requests.');
$this->drupalGet('admin/compact/off');
$this->assertResponse(200, 'A valid page is returned after turning off compact mode.');
$this->assertUrl($frontpage_url, [], 'The user is redirected to the front page after turning off compact mode.');
- $this->assertEqual($this->cookies['Drupal.visitor.admin_compact_mode']['value'], 'deleted', 'Compact mode turns off.');
+ $this->assertNull($session->getCookie('Drupal.visitor.admin_compact_mode'), 'Compact mode turns off.');
$this->drupalGet('admin/compact/off');
- $this->assertEqual($this->cookies['Drupal.visitor.admin_compact_mode']['value'], 'deleted', 'Compact mode remains off after a repeat call.');
+ $this->assertNull($session->getCookie('Drupal.visitor.admin_compact_mode'), 'Compact mode remains off after a repeat call.');
$this->drupalGet('');
- $this->assertTrue($this->cookies['Drupal.visitor.admin_compact_mode']['value'], 'Compact mode persists on new requests.');
+ $this->assertNull($session->getCookie('Drupal.visitor.admin_compact_mode'), 'Compact mode persists off new requests.');
}
}
diff --git a/core/modules/system/src/Tests/System/CronRunTest.php b/core/modules/system/tests/src/Functional/System/CronRunTest.php
similarity index 96%
rename from core/modules/system/src/Tests/System/CronRunTest.php
rename to core/modules/system/tests/src/Functional/System/CronRunTest.php
index ec4e649bf..12b86c83e 100644
--- a/core/modules/system/src/Tests/System/CronRunTest.php
+++ b/core/modules/system/tests/src/Functional/System/CronRunTest.php
@@ -1,15 +1,18 @@
assertText(t('The machine-readable name is already in use. It must be unique.'), 'It is not possible to create a date format with the machine name "fallback". It is a built-in format that already exists.');
// Create a date format with a machine name distinct from the previous two.
- $id = Unicode::strtolower($this->randomMachineName(16));
+ $id = mb_strtolower($this->randomMachineName(16));
$edit = [
'label' => $this->randomMachineName(16),
'id' => $id,
diff --git a/core/modules/system/src/Tests/System/DefaultMobileMetaTagsTest.php b/core/modules/system/tests/src/Functional/System/DefaultMobileMetaTagsTest.php
similarity index 66%
rename from core/modules/system/src/Tests/System/DefaultMobileMetaTagsTest.php
rename to core/modules/system/tests/src/Functional/System/DefaultMobileMetaTagsTest.php
index 3edfb7bb7..922839f99 100644
--- a/core/modules/system/src/Tests/System/DefaultMobileMetaTagsTest.php
+++ b/core/modules/system/tests/src/Functional/System/DefaultMobileMetaTagsTest.php
@@ -1,16 +1,17 @@
drupalGet('');
foreach ($this->defaultMetaTags as $name => $metatag) {
- $this->assertRaw($metatag, SafeMarkup::format('Default Mobile meta tag "@name" displayed properly.', ['@name' => $name]), 'System');
+ $this->assertRaw($metatag, new FormattableMarkup('Default Mobile meta tag "@name" displayed properly.', ['@name' => $name]), 'System');
}
}
@@ -42,7 +43,7 @@ class DefaultMobileMetaTagsTest extends WebTestBase {
\Drupal::service('module_installer')->install(['system_module_test']);
$this->drupalGet('');
foreach ($this->defaultMetaTags as $name => $metatag) {
- $this->assertNoRaw($metatag, SafeMarkup::format('Default Mobile meta tag "@name" removed properly.', ['@name' => $name]), 'System');
+ $this->assertNoRaw($metatag, new FormattableMarkup('Default Mobile meta tag "@name" removed properly.', ['@name' => $name]), 'System');
}
}
diff --git a/core/modules/system/src/Tests/System/FrontPageTest.php b/core/modules/system/tests/src/Functional/System/FrontPageTest.php
similarity index 90%
rename from core/modules/system/src/Tests/System/FrontPageTest.php
rename to core/modules/system/tests/src/Functional/System/FrontPageTest.php
index e24e90c31..7f9fc8c1f 100644
--- a/core/modules/system/src/Tests/System/FrontPageTest.php
+++ b/core/modules/system/tests/src/Functional/System/FrontPageTest.php
@@ -1,16 +1,16 @@
$this->nodePath];
$this->drupalPostForm('admin/config/system/site-information', $edit, t('Save configuration'));
- $this->assertRaw(SafeMarkup::format("The path '%path' has to start with a slash.", ['%path' => $edit['site_frontpage']]));
+ $this->assertRaw(new FormattableMarkup("The path '%path' has to start with a slash.", ['%path' => $edit['site_frontpage']]));
// Change the front page to a valid path.
$edit['site_frontpage'] = '/' . $this->nodePath;
diff --git a/core/modules/system/src/Tests/System/HtaccessTest.php b/core/modules/system/tests/src/Functional/System/HtaccessTest.php
similarity index 86%
rename from core/modules/system/src/Tests/System/HtaccessTest.php
rename to core/modules/system/tests/src/Functional/System/HtaccessTest.php
index fab7a37a3..79c4766c4 100644
--- a/core/modules/system/src/Tests/System/HtaccessTest.php
+++ b/core/modules/system/tests/src/Functional/System/HtaccessTest.php
@@ -1,15 +1,15 @@
drupalCreateNode([
'title' => 'This is a node',
'type' => $type->id(),
- 'path' => '/test.php'
+ 'path' => '/test.php',
]);
$node->save();
$this->drupalGet('test.php');
@@ -130,15 +130,11 @@ class HtaccessTest extends WebTestBase {
* Path to file. Without leading slash.
* @param int $response_code
* The expected response code. For example: 200, 403 or 404.
- *
- * @return bool
- * TRUE if the assertion succeeded, FALSE otherwise.
*/
protected function assertFileAccess($path, $response_code) {
- $result = $this->assertTrue(file_exists(\Drupal::root() . '/' . $path), "The file $path exists.");
+ $this->assertTrue(file_exists(\Drupal::root() . '/' . $path), "The file $path exists.");
$this->drupalGet($path);
- $result = $result && $this->assertResponse($response_code, "Response code to $path is $response_code.");
- return $result;
+ $this->assertResponse($response_code, "Response code to $path is $response_code.");
}
/**
@@ -147,7 +143,10 @@ class HtaccessTest extends WebTestBase {
public function testSvgzContentEncoding() {
$this->drupalGet('core/modules/system/tests/logo.svgz');
$this->assertResponse(200);
- $header = $this->drupalGetHeader('Content-Encoding');
+
+ // Use x-encoded-content-encoding because of Content-Encoding responses
+ // (gzip, deflate, etc.) are automatically decoded by Guzzle.
+ $header = $this->drupalGetHeader('x-encoded-content-encoding');
$this->assertEqual($header, 'gzip');
}
diff --git a/core/modules/system/tests/src/Functional/System/IndexPhpTest.php b/core/modules/system/tests/src/Functional/System/IndexPhpTest.php
index 2bf098931..4675fed0f 100644
--- a/core/modules/system/tests/src/Functional/System/IndexPhpTest.php
+++ b/core/modules/system/tests/src/Functional/System/IndexPhpTest.php
@@ -10,6 +10,7 @@ use Drupal\Tests\BrowserTestBase;
* @group system
*/
class IndexPhpTest extends BrowserTestBase {
+
protected function setUp() {
parent::setUp();
}
diff --git a/core/modules/system/src/Tests/System/PageNotFoundTest.php b/core/modules/system/tests/src/Functional/System/PageNotFoundTest.php
similarity index 85%
rename from core/modules/system/src/Tests/System/PageNotFoundTest.php
rename to core/modules/system/tests/src/Functional/System/PageNotFoundTest.php
index b1a88d827..ab9b54b49 100644
--- a/core/modules/system/src/Tests/System/PageNotFoundTest.php
+++ b/core/modules/system/tests/src/Functional/System/PageNotFoundTest.php
@@ -1,9 +1,10 @@
'user/' . $this->adminUser->id(),
];
$this->drupalPostForm('admin/config/system/site-information', $edit, t('Save configuration'));
- $this->assertRaw(SafeMarkup::format("The path '%path' has to start with a slash.", ['%path' => $edit['site_404']]));
+ $this->assertRaw(new FormattableMarkup("The path '%path' has to start with a slash.", ['%path' => $edit['site_404']]));
// Use a custom 404 page.
$edit = [
diff --git a/core/modules/system/src/Tests/System/PageTitleTest.php b/core/modules/system/tests/src/Functional/System/PageTitleTest.php
similarity index 83%
rename from core/modules/system/src/Tests/System/PageTitleTest.php
rename to core/modules/system/tests/src/Functional/System/PageTitleTest.php
index 44e33cb6e..8073827fb 100644
--- a/core/modules/system/src/Tests/System/PageTitleTest.php
+++ b/core/modules/system/tests/src/Functional/System/PageTitleTest.php
@@ -1,17 +1,18 @@
assertTitle('Foo | Drupal');
$result = $this->xpath('//h1[@class="page-title"]');
- $this->assertEqual('Foo', (string) $result[0]);
+ $this->assertEqual('Foo', $result[0]->getText());
// Test forms
$this->drupalGet('form-test/object-builder');
$this->assertTitle('Test dynamic title | Drupal');
$result = $this->xpath('//h1[@class="page-title"]');
- $this->assertEqual('Test dynamic title', (string) $result[0]);
+ $this->assertEqual('Test dynamic title', $result[0]->getText());
// Set some custom translated strings.
- $this->addCustomTranslations('en', [
- '' => ['Static title' => 'Static title translated'],
+ $settings_key = 'locale_custom_strings_en';
+
+ // Update in-memory settings directly.
+ $settings = Settings::getAll();
+ $settings[$settings_key] = ['' => ['Static title' => 'Static title translated']];
+ new Settings($settings);
+
+ // Rewrites the settings.php.
+ $this->writeSettings([
+ 'settings' => [
+ $settings_key => (object) [
+ 'value' => $settings[$settings_key],
+ 'required' => TRUE,
+ ],
+ ],
]);
- $this->writeCustomTranslations();
// Ensure that the title got translated.
$this->drupalGet('test-page-static-title');
$this->assertTitle('Static title translated | Drupal');
$result = $this->xpath('//h1[@class="page-title"]');
- $this->assertEqual('Static title translated', (string) $result[0]);
+ $this->assertEqual('Static title translated', $result[0]->getText());
// Test the dynamic '_title_callback' route option.
$this->drupalGet('test-page-dynamic-title');
$this->assertTitle('Dynamic title | Drupal');
$result = $this->xpath('//h1[@class="page-title"]');
- $this->assertEqual('Dynamic title', (string) $result[0]);
+ $this->assertEqual('Dynamic title', $result[0]->getText());
// Ensure that titles are cacheable and are escaped normally if the
// controller does not escape them.
diff --git a/core/modules/system/tests/src/Functional/System/RetrieveFileTest.php b/core/modules/system/tests/src/Functional/System/RetrieveFileTest.php
index ef12a51ad..cf64f079a 100644
--- a/core/modules/system/tests/src/Functional/System/RetrieveFileTest.php
+++ b/core/modules/system/tests/src/Functional/System/RetrieveFileTest.php
@@ -10,6 +10,7 @@ use Drupal\Tests\BrowserTestBase;
* @group system
*/
class RetrieveFileTest extends BrowserTestBase {
+
/**
* Invokes system_retrieve_file() in several scenarios.
*/
diff --git a/core/modules/system/src/Tests/System/ShutdownFunctionsTest.php b/core/modules/system/tests/src/Functional/System/ShutdownFunctionsTest.php
similarity index 84%
rename from core/modules/system/src/Tests/System/ShutdownFunctionsTest.php
rename to core/modules/system/tests/src/Functional/System/ShutdownFunctionsTest.php
index 50fae0362..f5d55be78 100644
--- a/core/modules/system/src/Tests/System/ShutdownFunctionsTest.php
+++ b/core/modules/system/tests/src/Functional/System/ShutdownFunctionsTest.php
@@ -1,15 +1,15 @@
getRawContent(), 'The function fastcgi_finish_request exists when serving the request.');
+ $server_using_fastcgi = strpos($this->getSession()->getPage()->getContent(), 'The function fastcgi_finish_request exists when serving the request.');
if ($server_using_fastcgi) {
// We need to wait to ensure that the shutdown functions have fired.
sleep(1);
diff --git a/core/modules/system/src/Tests/System/SiteMaintenanceTest.php b/core/modules/system/tests/src/Functional/System/SiteMaintenanceTest.php
similarity index 94%
rename from core/modules/system/src/Tests/System/SiteMaintenanceTest.php
rename to core/modules/system/tests/src/Functional/System/SiteMaintenanceTest.php
index e269a6c73..389b3f756 100644
--- a/core/modules/system/src/Tests/System/SiteMaintenanceTest.php
+++ b/core/modules/system/tests/src/Functional/System/SiteMaintenanceTest.php
@@ -1,16 +1,21 @@
drupalLogout();
$this->drupalGet('');
- $this->assertEqual('Site under maintenance', $this->cssSelect('main h1')[0]);
+ $this->assertEqual('Site under maintenance', $this->cssSelect('main h1')[0]->getText());
$this->assertText($offline_message);
$this->drupalGet('node');
- $this->assertEqual('Site under maintenance', $this->cssSelect('main h1')[0]);
+ $this->assertEqual('Site under maintenance', $this->cssSelect('main h1')[0]->getText());
$this->assertText($offline_message);
$this->drupalGet('user/register');
- $this->assertEqual('Site under maintenance', $this->cssSelect('main h1')[0]);
+ $this->assertEqual('Site under maintenance', $this->cssSelect('main h1')[0]->getText());
$this->assertText($offline_message);
// Verify that user is able to log in.
@@ -110,7 +115,7 @@ class SiteMaintenanceTest extends WebTestBase {
// Logout and verify that custom site offline message is displayed.
$this->drupalLogout();
$this->drupalGet('');
- $this->assertEqual('Site under maintenance', $this->cssSelect('main h1')[0]);
+ $this->assertEqual('Site under maintenance', $this->cssSelect('main h1')[0]->getText());
$this->assertRaw($offline_message, 'Found the site offline message.');
// Verify that custom site offline message is not displayed on user/password.
@@ -137,7 +142,7 @@ class SiteMaintenanceTest extends WebTestBase {
// Logout and verify that offline message is displayed in Bartik.
$this->drupalLogout();
$this->drupalGet('');
- $this->assertEqual('Site under maintenance', $this->cssSelect('main h1')[0]);
+ $this->assertEqual('Site under maintenance', $this->cssSelect('main h1')[0]->getText());
}
/**
diff --git a/core/modules/system/tests/src/Functional/System/SitesDirectoryHardeningTest.php b/core/modules/system/tests/src/Functional/System/SitesDirectoryHardeningTest.php
index fbe97f4da..f08a18d8b 100644
--- a/core/modules/system/tests/src/Functional/System/SitesDirectoryHardeningTest.php
+++ b/core/modules/system/tests/src/Functional/System/SitesDirectoryHardeningTest.php
@@ -49,13 +49,16 @@ class SitesDirectoryHardeningTest extends BrowserTestBase {
$settings = Settings::getAll();
$settings['skip_permissions_hardening'] = TRUE;
new Settings($settings);
- $this->assertTrue(Settings::get('skip_permissions_hardening'), 'Able to set hardening to true');
+ $this->assertTrue(Settings::get('skip_permissions_hardening'), 'Able to set skip permissions hardening to true.');
$this->makeWritable($site_path);
// Manually trigger the requirements check.
$requirements = $this->checkSystemRequirements();
$this->assertEqual(REQUIREMENT_WARNING, $requirements['configuration_files']['severity'], 'Warning severity is properly set.');
- $this->assertEqual($this->t('Protection disabled'), (string) $requirements['configuration_files']['description']['#context']['configuration_error_list']['#items'][0], 'Description is properly set.');
+ $this->assertEquals('Protection disabled', (string) $requirements['configuration_files']['value']);
+ $description = strip_tags(\Drupal::service('renderer')->renderPlain($requirements['configuration_files']['description']));
+ $this->assertContains('settings.php is not protected from modifications and poses a security risk.', $description);
+ $this->assertContains('services.yml is not protected from modifications and poses a security risk.', $description);
$this->assertTrue(is_writable($site_path), 'Site directory remains writable when automatically fixing permissions is disabled.');
$this->assertTrue(is_writable($settings_file), 'settings.php remains writable when automatically fixing permissions is disabled.');
@@ -66,7 +69,8 @@ class SitesDirectoryHardeningTest extends BrowserTestBase {
new Settings($settings);
// Manually trigger the requirements check.
- $this->checkSystemRequirements();
+ $requirements = $this->checkSystemRequirements();
+ $this->assertEquals('Protected', (string) $requirements['configuration_files']['value']);
$this->assertFalse(is_writable($site_path), 'Site directory is protected when automatically fixing permissions is enabled.');
$this->assertFalse(is_writable($settings_file), 'settings.php is protected when automatically fixing permissions is enabled.');
diff --git a/core/modules/system/tests/src/Functional/System/StatusTest.php b/core/modules/system/tests/src/Functional/System/StatusTest.php
index 1ec4fb5aa..c902a3731 100644
--- a/core/modules/system/tests/src/Functional/System/StatusTest.php
+++ b/core/modules/system/tests/src/Functional/System/StatusTest.php
@@ -27,7 +27,7 @@ class StatusTest extends BrowserTestBase {
// Unset the sync directory in settings.php to trigger $config_directories
// error.
- $settings['config_directories'] = [
+ $settings['config_directories'] = [
CONFIG_SYNC_DIRECTORY => (object) [
'value' => '',
'required' => TRUE,
diff --git a/core/modules/system/src/Tests/System/ThemeTest.php b/core/modules/system/tests/src/Functional/System/ThemeTest.php
similarity index 92%
rename from core/modules/system/src/Tests/System/ThemeTest.php
rename to core/modules/system/tests/src/Functional/System/ThemeTest.php
index 78fac8ead..d1d7880c8 100644
--- a/core/modules/system/src/Tests/System/ThemeTest.php
+++ b/core/modules/system/tests/src/Functional/System/ThemeTest.php
@@ -1,9 +1,10 @@
assertEqual((string) $elements[0], $implicit_public_file);
- $this->assertEqual((string) $elements[1], $explicit_file);
- $this->assertEqual((string) $elements[2], $local_file);
+ $this->assertEqual($elements[0]->getText(), $implicit_public_file);
+ $this->assertEqual($elements[1]->getText(), $explicit_file);
+ $this->assertEqual($elements[2]->getText(), $local_file);
// Verify the actual 'src' attribute of the logo being output in a site
// branding block.
@@ -129,7 +134,7 @@ class ThemeTest extends WebTestBase {
':rel' => 'home',
]
);
- $this->assertEqual((string) $elements[0]['src'], $expected['src']);
+ $this->assertEqual($elements[0]->getAttribute('src'), $expected['src']);
}
$unsupported_paths = [
// Stream wrapper URI to non-existing file.
@@ -173,7 +178,7 @@ class ThemeTest extends WebTestBase {
$this->drupalPostForm('admin/appearance/settings', $edit, t('Save configuration'));
$fields = $this->xpath($this->constructFieldXpath('name', 'logo_path'));
- $uploaded_filename = 'public://' . $fields[0]['value'];
+ $uploaded_filename = 'public://' . $fields[0]->getValue();
$this->drupalPlaceBlock('system_branding_block', ['region' => 'header']);
$this->drupalGet('');
@@ -181,7 +186,7 @@ class ThemeTest extends WebTestBase {
':rel' => 'home',
]
);
- $this->assertEqual($elements[0]['src'], file_url_transform_relative(file_create_url($uploaded_filename)));
+ $this->assertEqual($elements[0]->getAttribute('src'), file_url_transform_relative(file_create_url($uploaded_filename)));
$this->container->get('theme_handler')->install(['bartik']);
@@ -425,7 +430,21 @@ class ThemeTest extends WebTestBase {
$version = $themes['bartik']->info['version'];
// Confirm Bartik is indicated as the default theme.
- $this->assertTextPattern('/Bartik ' . preg_quote($version) . '\s{2,}\(default theme\)/');
+ $out = $this->getSession()->getPage()->getContent();
+ $this->assertTrue((bool) preg_match('/Bartik ' . preg_quote($version) . '\s{2,}\(default theme\)/', $out));
+ }
+
+ /**
+ * Test the theme settings form when logo and favicon features are disabled.
+ */
+ public function testThemeSettingsNoLogoNoFavicon() {
+ // Install theme with no logo and no favicon feature.
+ $this->container->get('theme_handler')->install(['test_theme_settings_features']);
+ // Visit this theme's settings page.
+ $this->drupalGet('admin/appearance/settings/test_theme_settings_features');
+ $edit = [];
+ $this->drupalPostForm('admin/appearance/settings/test_theme_settings_features', $edit, t('Save configuration'));
+ $this->assertText('The configuration options have been saved.');
}
}
diff --git a/core/modules/system/src/Tests/System/TokenReplaceWebTest.php b/core/modules/system/tests/src/Functional/System/TokenReplaceWebTest.php
similarity index 84%
rename from core/modules/system/src/Tests/System/TokenReplaceWebTest.php
rename to core/modules/system/tests/src/Functional/System/TokenReplaceWebTest.php
index 21fe23ac1..450f50704 100644
--- a/core/modules/system/src/Tests/System/TokenReplaceWebTest.php
+++ b/core/modules/system/tests/src/Functional/System/TokenReplaceWebTest.php
@@ -1,17 +1,17 @@
assertCacheContext('url.site');
// Make sure we got something.
- $content = $this->getRawContent();
+ $content = $this->getSession()->getPage()->getContent();
$this->assertFalse(empty($content), 'Page content is not empty');
foreach ($expected as $string) {
$this->assertRaw('' . $string . '
');
@@ -94,7 +97,7 @@ class EngineTwigTest extends WebTestBase {
// tests for *which* cacheability metadata is bubbled live elsewhere.)
$this->assertCacheContext('url.site');
- $content = $this->getRawContent();
+ $content = $this->getSession()->getPage()->getContent();
$this->assertFalse(empty($content), 'Page content is not empty');
foreach ($expected as $string) {
$this->assertRaw('' . $string . '
');
@@ -113,7 +116,7 @@ class EngineTwigTest extends WebTestBase {
'rendered url: ' . Url::fromRoute('user.register')->toString(),
];
- $content = $this->getRawContent();
+ $content = $this->getSession()->getPage()->getContent();
$this->assertFalse(empty($content), 'Page content is not empty');
foreach ($expected as $string) {
$this->assertRaw('' . $string . '
');
diff --git a/core/modules/system/src/Tests/Theme/EntityFilteringThemeTest.php b/core/modules/system/tests/src/Functional/Theme/EntityFilteringThemeTest.php
similarity index 96%
rename from core/modules/system/src/Tests/Theme/EntityFilteringThemeTest.php
rename to core/modules/system/tests/src/Functional/Theme/EntityFilteringThemeTest.php
index c6ae012d9..6e4bd0a0b 100644
--- a/core/modules/system/src/Tests/Theme/EntityFilteringThemeTest.php
+++ b/core/modules/system/tests/src/Functional/Theme/EntityFilteringThemeTest.php
@@ -1,15 +1,15 @@
drupalGet('');
- $attributes = $this->xpath('/html[@theme_test_html_attribute="theme test html attribute value"]');
- $this->assertTrue(count($attributes) == 1, "Attribute set in the 'html' element via hook_preprocess_HOOK() found.");
- $attributes = $this->xpath('/html/body[@theme_test_body_attribute="theme test body attribute value"]');
+ $this->assertSession()->responseContains('');
+ $attributes = $this->xpath('/body[@theme_test_body_attribute="theme test body attribute value"]');
$this->assertTrue(count($attributes) == 1, "Attribute set in the 'body' element via hook_preprocess_HOOK() found.");
}
diff --git a/core/modules/system/src/Tests/Theme/ThemeSuggestionsAlterTest.php b/core/modules/system/tests/src/Functional/Theme/ThemeSuggestionsAlterTest.php
similarity index 88%
rename from core/modules/system/src/Tests/Theme/ThemeSuggestionsAlterTest.php
rename to core/modules/system/tests/src/Functional/Theme/ThemeSuggestionsAlterTest.php
index c01a94fec..36e8eb2d6 100644
--- a/core/modules/system/src/Tests/Theme/ThemeSuggestionsAlterTest.php
+++ b/core/modules/system/tests/src/Functional/Theme/ThemeSuggestionsAlterTest.php
@@ -1,16 +1,16 @@
resetAll();
$this->drupalGet('theme-test/specific-suggestion-alter');
$this->assertText('Template overridden based on suggestion alter hook determined by the base hook.');
- $this->assertTrue(strpos($this->getRawContent(), 'theme_test_specific_suggestions__variant') < strpos($this->getRawContent(), 'theme_test_specific_suggestions__variant__foo'), 'Specific theme call is added to the suggestions array before the suggestions alter hook.');
+ $raw_content = $this->getSession()->getPage()->getContent();
+ $this->assertTrue(strpos($raw_content, 'theme_test_specific_suggestions__variant') < strpos($raw_content, 'theme_test_specific_suggestions__variant__foo'), 'Specific theme call is added to the suggestions array before the suggestions alter hook.');
}
/**
@@ -154,7 +155,7 @@ class ThemeSuggestionsAlterTest extends WebTestBase {
/**
* Tests execution order of theme suggestion alter hooks.
*
- * hook_theme_suggestions_alter() should fire before
+ * Hook hook_theme_suggestions_alter() should fire before
* hook_theme_suggestions_HOOK_alter() within an extension (module or theme).
*/
public function testExecutionOrder() {
@@ -166,20 +167,24 @@ class ThemeSuggestionsAlterTest extends WebTestBase {
$this->resetAll();
// Send two requests so that we get all the messages we've set via
- // drupal_set_message().
+ // \Drupal\Core\Messenger\MessengerInterface::addStatus().
$this->drupalGet('theme-test/suggestion-alter');
// Ensure that the order is first by extension, then for a given extension,
// the hook-specific one after the generic one.
- $expected = [
+ $expected_order = [
'theme_suggestions_test_theme_suggestions_alter() executed.',
'theme_suggestions_test_theme_suggestions_theme_test_suggestions_alter() executed.',
- 'theme_test_theme_suggestions_alter() executed.',
+ 'theme_test_theme_suggestions_alter() executed for theme_test_suggestions.',
'theme_test_theme_suggestions_theme_test_suggestions_alter() executed.',
'test_theme_theme_suggestions_alter() executed.',
'test_theme_theme_suggestions_theme_test_suggestions_alter() executed.',
];
- $content = preg_replace('/\s+/', ' ', Xss::filter($this->content, []));
- $this->assert(strpos($content, implode(' ', $expected)) !== FALSE, 'Suggestion alter hooks executed in the expected order.');
+ $content = preg_replace('/\s+/', ' ', Xss::filter($this->getSession()->getPage()->getContent(), []));
+ $order = 0;
+ foreach ($expected_order as $expected_string) {
+ $this->assertGreaterThan($order, strpos($content, $expected_string));
+ $order = strpos($content, $expected_string);
+ }
}
}
diff --git a/core/modules/system/tests/src/Functional/Theme/ThemeTest.php b/core/modules/system/tests/src/Functional/Theme/ThemeTest.php
new file mode 100644
index 000000000..3d534d8e7
--- /dev/null
+++ b/core/modules/system/tests/src/Functional/Theme/ThemeTest.php
@@ -0,0 +1,182 @@
+install(['test_theme']);
+ }
+
+ /**
+ * Ensures preprocess functions run even for suggestion implementations.
+ *
+ * The theme hook used by this test has its base preprocess function in a
+ * separate file, so this test also ensures that that file is correctly loaded
+ * when needed.
+ */
+ public function testPreprocessForSuggestions() {
+ // Test with both an unprimed and primed theme registry.
+ drupal_theme_rebuild();
+ for ($i = 0; $i < 2; $i++) {
+ $this->drupalGet('theme-test/suggestion');
+ $this->assertText('Theme hook implementor=test_theme_theme_test__suggestion(). Foo=template_preprocess_theme_test', 'Theme hook suggestion ran with data available from a preprocess function for the base hook.');
+ }
+ }
+
+ /**
+ * Tests the priority of some theme negotiators.
+ */
+ public function testNegotiatorPriorities() {
+ $this->drupalGet('theme-test/priority');
+
+ // Ensure that the custom theme negotiator was not able to set the theme.
+ $this->assertNoText('Theme hook implementor=test_theme_theme_test__suggestion(). Foo=template_preprocess_theme_test', 'Theme hook suggestion ran with data available from a preprocess function for the base hook.');
+ }
+
+ /**
+ * Ensures that non-HTML requests never initialize themes.
+ */
+ public function testThemeOnNonHtmlRequest() {
+ $this->drupalGet('theme-test/non-html');
+ $json = Json::decode($this->getSession()->getPage()->getContent());
+ $this->assertFalse($json['theme_initialized']);
+ }
+
+ /**
+ * Ensure page-front template suggestion is added when on front page.
+ */
+ public function testFrontPageThemeSuggestion() {
+ // Set the current route to user.login because theme_get_suggestions() will
+ // query it to see if we are on the front page.
+ $request = Request::create('/user/login');
+ $request->attributes->set(RouteObjectInterface::ROUTE_NAME, 'user.login');
+ $request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, new Route('/user/login'));
+ \Drupal::requestStack()->push($request);
+ $this->config('system.site')->set('page.front', '/user/login')->save();
+ $suggestions = theme_get_suggestions(['user', 'login'], 'page');
+ // Set it back to not annoy the batch runner.
+ \Drupal::requestStack()->pop();
+ $this->assertTrue(in_array('page__front', $suggestions), 'Front page template was suggested.');
+ }
+
+ /**
+ * Ensures a theme's .info.yml file is able to override a module CSS file from being added to the page.
+ *
+ * @see test_theme.info.yml
+ */
+ public function testCSSOverride() {
+ // Reuse the same page as in testPreprocessForSuggestions(). We're testing
+ // what is output to the HTML HEAD based on what is in a theme's .info.yml
+ // file, so it doesn't matter what page we get, as long as it is themed with
+ // the test theme. First we test with CSS aggregation disabled.
+ $config = $this->config('system.performance');
+ $config->set('css.preprocess', 0);
+ $config->save();
+ $this->drupalGet('theme-test/suggestion');
+ $this->assertNoText('js.module.css', 'The theme\'s .info.yml file is able to override a module CSS file from being added to the page.');
+
+ // Also test with aggregation enabled, simply ensuring no PHP errors are
+ // triggered during drupal_build_css_cache() when a source file doesn't
+ // exist. Then allow remaining tests to continue with aggregation disabled
+ // by default.
+ $config->set('css.preprocess', 1);
+ $config->save();
+ $this->drupalGet('theme-test/suggestion');
+ $config->set('css.preprocess', 0);
+ $config->save();
+ }
+
+ /**
+ * Ensures a themes template is overridable based on the 'template' filename.
+ */
+ public function testTemplateOverride() {
+ $this->config('system.theme')
+ ->set('default', 'test_theme')
+ ->save();
+ $this->drupalGet('theme-test/template-test');
+ $this->assertText('Success: Template overridden.', 'Template overridden by defined \'template\' filename.');
+ }
+
+ /**
+ * Ensures a theme template can override a theme function.
+ */
+ public function testFunctionOverride() {
+ $this->drupalGet('theme-test/function-template-overridden');
+ $this->assertText('Success: Template overrides theme function.', 'Theme function overridden by test_theme template.');
+ }
+
+ /**
+ * Tests that the page variable is not prematurely flattened.
+ *
+ * Some modules check the page array in template_preprocess_html(), so we
+ * ensure that it has not been rendered prematurely.
+ */
+ public function testPreprocessHtml() {
+ $this->drupalGet('');
+ $attributes = $this->xpath('/body[@theme_test_page_variable="Page variable is an array."]');
+ $this->assertTrue(count($attributes) == 1, 'In template_preprocess_html(), the page variable is still an array (not rendered yet).');
+ $this->assertText('theme test page bottom markup', 'Modules are able to set the page bottom region.');
+ }
+
+ /**
+ * Tests that region attributes can be manipulated via preprocess functions.
+ */
+ public function testRegionClass() {
+ \Drupal::service('module_installer')->install(['block', 'theme_region_test']);
+
+ // Place a block.
+ $this->drupalPlaceBlock('system_main_block');
+ $this->drupalGet('');
+ $elements = $this->cssSelect(".region-sidebar-first.new_class");
+ $this->assertEqual(count($elements), 1, 'New class found.');
+ }
+
+ /**
+ * Ensures suggestion preprocess functions run for default implementations.
+ *
+ * The theme hook used by this test has its base preprocess function in a
+ * separate file, so this test also ensures that that file is correctly loaded
+ * when needed.
+ */
+ public function testSuggestionPreprocessForDefaults() {
+ $this->config('system.theme')->set('default', 'test_theme')->save();
+ // Test with both an unprimed and primed theme registry.
+ drupal_theme_rebuild();
+ for ($i = 0; $i < 2; $i++) {
+ $this->drupalGet('theme-test/preprocess-suggestions');
+ $items = $this->cssSelect('.suggestion');
+ $expected_values = [
+ 'Suggestion',
+ 'Kitten',
+ 'Monkey',
+ 'Kitten',
+ 'Flamingo',
+ ];
+ foreach ($expected_values as $key => $value) {
+ $this->assertEqual((string) $value, $items[$key]->getText());
+ }
+ }
+ }
+
+}
diff --git a/core/modules/system/src/Tests/Theme/TwigDebugMarkupTest.php b/core/modules/system/tests/src/Functional/Theme/TwigDebugMarkupTest.php
similarity index 93%
rename from core/modules/system/src/Tests/Theme/TwigDebugMarkupTest.php
rename to core/modules/system/tests/src/Functional/Theme/TwigDebugMarkupTest.php
index 457fbad8b..9fe89fd58 100644
--- a/core/modules/system/src/Tests/Theme/TwigDebugMarkupTest.php
+++ b/core/modules/system/tests/src/Functional/Theme/TwigDebugMarkupTest.php
@@ -1,15 +1,16 @@
renderRoot($build);
$this->assertTrue(strpos($output, '') !== FALSE, 'Twig debug markup found in theme output when debug is enabled.');
- $this->setRawContent($output);
$this->assertTrue(strpos($output, "THEME HOOK: 'node'") !== FALSE, 'Theme call information found.');
$this->assertTrue(strpos($output, '* node--1--full' . $extension . PHP_EOL . ' x node--1' . $extension . PHP_EOL . ' * node--page--full' . $extension . PHP_EOL . ' * node--page' . $extension . PHP_EOL . ' * node--full' . $extension . PHP_EOL . ' * node' . $extension) !== FALSE, 'Suggested template files found in order and node ID specific template shown as current template.');
- $this->assertEscaped('node--');
+ $this->assertContains(Html::escape('node--'), (string) $output);
$template_filename = $templates['node__1']['path'] . '/' . $templates['node__1']['template'] . $extension;
$this->assertTrue(strpos($output, "BEGIN OUTPUT from '$template_filename'") !== FALSE, 'Full path to current template file found.');
diff --git a/core/modules/system/src/Tests/Theme/TwigTransTest.php b/core/modules/system/tests/src/Functional/Theme/TwigTransTest.php
similarity index 97%
rename from core/modules/system/src/Tests/Theme/TwigTransTest.php
rename to core/modules/system/tests/src/Functional/Theme/TwigTransTest.php
index b73cdaf0c..aeafe7aeb 100644
--- a/core/modules/system/src/Tests/Theme/TwigTransTest.php
+++ b/core/modules/system/tests/src/Functional/Theme/TwigTransTest.php
@@ -1,16 +1,16 @@
drupalLogin($this->adminUser);
diff --git a/core/modules/system/tests/src/Functional/Update/AutomatedCronUpdateWithAutomatedCronTest.php b/core/modules/system/tests/src/Functional/Update/AutomatedCronUpdateWithAutomatedCronTest.php
index 50926c935..e967f1701 100644
--- a/core/modules/system/tests/src/Functional/Update/AutomatedCronUpdateWithAutomatedCronTest.php
+++ b/core/modules/system/tests/src/Functional/Update/AutomatedCronUpdateWithAutomatedCronTest.php
@@ -8,6 +8,7 @@ use Drupal\FunctionalTests\Update\UpdatePathTestBase;
* Ensures that the automated cron module is installed on update.
*
* @group Update
+ * @group legacy
*/
class AutomatedCronUpdateWithAutomatedCronTest extends UpdatePathTestBase {
diff --git a/core/modules/system/tests/src/Functional/Update/AutomatedCronUpdateWithoutAutomatedCronTest.php b/core/modules/system/tests/src/Functional/Update/AutomatedCronUpdateWithoutAutomatedCronTest.php
index d4ac71188..c131c7059 100644
--- a/core/modules/system/tests/src/Functional/Update/AutomatedCronUpdateWithoutAutomatedCronTest.php
+++ b/core/modules/system/tests/src/Functional/Update/AutomatedCronUpdateWithoutAutomatedCronTest.php
@@ -8,6 +8,7 @@ use Drupal\FunctionalTests\Update\UpdatePathTestBase;
* Ensures that the automated cron module is not installed on update.
*
* @group Update
+ * @group legacy
*/
class AutomatedCronUpdateWithoutAutomatedCronTest extends UpdatePathTestBase {
diff --git a/core/modules/system/tests/src/Functional/Update/ConfigOverridesUpdateTest.php b/core/modules/system/tests/src/Functional/Update/ConfigOverridesUpdateTest.php
index bab7d4941..e18195da1 100644
--- a/core/modules/system/tests/src/Functional/Update/ConfigOverridesUpdateTest.php
+++ b/core/modules/system/tests/src/Functional/Update/ConfigOverridesUpdateTest.php
@@ -10,6 +10,7 @@ use Drupal\FunctionalTests\Update\UpdatePathTestBase;
* @see system_update_8200()
*
* @group Update
+ * @group legacy
*/
class ConfigOverridesUpdateTest extends UpdatePathTestBase {
diff --git a/core/modules/system/tests/src/Functional/Update/DependencyHookInvocationTest.php b/core/modules/system/tests/src/Functional/Update/DependencyHookInvocationTest.php
index 8ff7b883d..203d89ef2 100644
--- a/core/modules/system/tests/src/Functional/Update/DependencyHookInvocationTest.php
+++ b/core/modules/system/tests/src/Functional/Update/DependencyHookInvocationTest.php
@@ -21,7 +21,7 @@ class DependencyHookInvocationTest extends BrowserTestBase {
protected function setUp() {
parent::setUp();
- require_once \Drupal::root() . '/core/includes/update.inc';
+ require_once $this->root . '/core/includes/update.inc';
}
/**
diff --git a/core/modules/system/tests/src/Functional/Update/DependencyMissingTest.php b/core/modules/system/tests/src/Functional/Update/DependencyMissingTest.php
index 6ae4dc40c..e11999a46 100644
--- a/core/modules/system/tests/src/Functional/Update/DependencyMissingTest.php
+++ b/core/modules/system/tests/src/Functional/Update/DependencyMissingTest.php
@@ -22,7 +22,7 @@ class DependencyMissingTest extends BrowserTestBase {
// Only install update_test_2.module, even though its updates have a
// dependency on update_test_3.module.
parent::setUp();
- require_once \Drupal::root() . '/core/includes/update.inc';
+ require_once $this->root . '/core/includes/update.inc';
}
public function testMissingUpdate() {
diff --git a/core/modules/system/tests/src/Functional/Update/DependencyOrderingTest.php b/core/modules/system/tests/src/Functional/Update/DependencyOrderingTest.php
index 8cafaaddb..0f168aa78 100644
--- a/core/modules/system/tests/src/Functional/Update/DependencyOrderingTest.php
+++ b/core/modules/system/tests/src/Functional/Update/DependencyOrderingTest.php
@@ -20,7 +20,7 @@ class DependencyOrderingTest extends BrowserTestBase {
protected function setUp() {
parent::setUp();
- require_once \Drupal::root() . '/core/includes/update.inc';
+ require_once $this->root . '/core/includes/update.inc';
}
/**
diff --git a/core/modules/system/tests/src/Functional/Update/EntityUpdateAddRevisionDefaultTest.php b/core/modules/system/tests/src/Functional/Update/EntityUpdateAddRevisionDefaultTest.php
index e226540b6..aa8ba0046 100644
--- a/core/modules/system/tests/src/Functional/Update/EntityUpdateAddRevisionDefaultTest.php
+++ b/core/modules/system/tests/src/Functional/Update/EntityUpdateAddRevisionDefaultTest.php
@@ -3,7 +3,7 @@
namespace Drupal\Tests\system\Functional\Update;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
-use Drupal\system\Tests\Entity\EntityDefinitionTestTrait;
+use Drupal\Tests\system\Functional\Entity\Traits\EntityDefinitionTestTrait;
/**
* Tests the upgrade path for adding the 'revision_default' field.
@@ -11,6 +11,7 @@ use Drupal\system\Tests\Entity\EntityDefinitionTestTrait;
* @see https://www.drupal.org/project/drupal/issues/2891215
*
* @group Update
+ * @group legacy
*/
class EntityUpdateAddRevisionDefaultTest extends UpdatePathTestBase {
diff --git a/core/modules/system/tests/src/Functional/Update/EntityUpdateAddRevisionTranslationAffectedTest.php b/core/modules/system/tests/src/Functional/Update/EntityUpdateAddRevisionTranslationAffectedTest.php
index f7dcd0c41..6553fcb06 100644
--- a/core/modules/system/tests/src/Functional/Update/EntityUpdateAddRevisionTranslationAffectedTest.php
+++ b/core/modules/system/tests/src/Functional/Update/EntityUpdateAddRevisionTranslationAffectedTest.php
@@ -3,7 +3,7 @@
namespace Drupal\Tests\system\Functional\Update;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
-use Drupal\system\Tests\Entity\EntityDefinitionTestTrait;
+use Drupal\Tests\system\Functional\Entity\Traits\EntityDefinitionTestTrait;
/**
* Tests the upgrade path for adding the 'revision_translation_affected' field.
@@ -11,6 +11,7 @@ use Drupal\system\Tests\Entity\EntityDefinitionTestTrait;
* @see https://www.drupal.org/node/2896845
*
* @group Update
+ * @group legacy
*/
class EntityUpdateAddRevisionTranslationAffectedTest extends UpdatePathTestBase {
diff --git a/core/modules/system/tests/src/Functional/Update/EntityUpdateInitialTest.php b/core/modules/system/tests/src/Functional/Update/EntityUpdateInitialTest.php
index d4d79f96b..7a85744b2 100644
--- a/core/modules/system/tests/src/Functional/Update/EntityUpdateInitialTest.php
+++ b/core/modules/system/tests/src/Functional/Update/EntityUpdateInitialTest.php
@@ -10,6 +10,7 @@ use Drupal\FunctionalTests\Update\UpdatePathTestBase;
* @see https://www.drupal.org/project/drupal/issues/2925550
*
* @group Update
+ * @group legacy
*/
class EntityUpdateInitialTest extends UpdatePathTestBase {
diff --git a/core/modules/system/tests/src/Functional/Update/EntityUpdateToPublishableTest.php b/core/modules/system/tests/src/Functional/Update/EntityUpdateToPublishableTest.php
new file mode 100644
index 000000000..e887fd561
--- /dev/null
+++ b/core/modules/system/tests/src/Functional/Update/EntityUpdateToPublishableTest.php
@@ -0,0 +1,170 @@
+entityTypeManager = \Drupal::entityTypeManager();
+ $this->entityDefinitionUpdateManager = \Drupal::entityDefinitionUpdateManager();
+ $this->lastInstalledSchemaRepository = \Drupal::service('entity.last_installed_schema.repository');
+ $this->installedStorageSchema = \Drupal::keyValue('entity.storage_schema.sql');
+ $this->state = \Drupal::state();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setDatabaseDumpFiles() {
+ $this->databaseDumpFiles = [
+ __DIR__ . '/../../../fixtures/update/drupal-8.0.0-rc1-filled.standard.entity_test_update_mul.php.gz',
+];
+ }
+
+ /**
+ * Tests the conversion of an entity type to be publishable.
+ *
+ * @see entity_test_update_update_8400()
+ */
+ public function testConvertToPublishable() {
+ // Check that entity type is not publishable prior to running the update
+ // process.
+ $entity_test_update = $this->lastInstalledSchemaRepository->getLastInstalledDefinition('entity_test_update');
+ $this->assertFalse($entity_test_update->getKey('published'));
+
+ // Make the entity type translatable and publishable.
+ $this->updateEntityTypeDefinition();
+
+ $this->enableUpdates('entity_test_update', 'entity_rev_pub_updates', 8400);
+ $this->runUpdates();
+
+ /** @var \Drupal\Core\Entity\EntityTypeInterface $entity_test_update */
+ $entity_test_update = $this->lastInstalledSchemaRepository->getLastInstalledDefinition('entity_test_update');
+ $this->assertEquals('status', $entity_test_update->getKey('published'));
+
+ /** @var \Drupal\Core\Entity\Sql\SqlEntityStorageInterface $storage */
+ $storage = \Drupal::entityTypeManager()->getStorage('entity_test_update');
+ $this->assertCount(102, $storage->loadMultiple(), 'All test entities were found.');
+
+ // The test entity with ID 50 was created before Content Translation was
+ // enabled, which means it didn't have a 'content_translation_status' field.
+ // content_translation_update_8400() added values for that field which
+ // should now be reflected in the entity's 'status' field.
+ /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
+ $entity = $storage->load(50);
+ $this->assertEquals(1, $entity->status->value);
+
+ $translation = $entity->getTranslation('ro');
+ $this->assertEquals(1, $translation->status->value);
+
+ // The test entity with ID 100 was created with Content Translation enabled
+ // and it should have the same values as entity 50.
+ $entity = $storage->load(100);
+ $this->assertEquals(1, $entity->status->value);
+
+ $translation = $entity->getTranslation('ro');
+ $this->assertEquals(1, $translation->status->value);
+
+ // The test entity 101 had 'content_translation_status' set to 0 for the
+ // English (source) language.
+ $entity = $storage->load(101);
+ $this->assertEquals(0, $entity->status->value);
+
+ $translation = $entity->getTranslation('ro');
+ $this->assertEquals(1, $translation->status->value);
+
+ // The test entity 102 had 'content_translation_status' set to 0 for the
+ // Romanian language.
+ $entity = $storage->load(102);
+ $this->assertEquals(1, $entity->status->value);
+
+ $translation = $entity->getTranslation('ro');
+ $this->assertEquals(0, $translation->status->value);
+ }
+
+ /**
+ * Updates the 'entity_test_update' entity type to translatable and
+ * publishable.
+ */
+ protected function updateEntityTypeDefinition() {
+ $entity_type = clone $this->entityTypeManager->getDefinition('entity_test_update');
+
+ $keys = $entity_type->getKeys();
+ $keys['published'] = 'status';
+ $entity_type->set('entity_keys', $keys);
+
+ $entity_type->set('translatable', TRUE);
+ $entity_type->set('data_table', 'entity_test_update_data');
+
+ $this->state->set('entity_test_update.entity_type', $entity_type);
+
+ // Add the status field to the entity type.
+ $status = BaseFieldDefinition::create('boolean')
+ ->setLabel(t('Publishing status'))
+ ->setDescription(t('A boolean indicating the published state.'))
+ ->setRevisionable(TRUE)
+ ->setTranslatable(TRUE)
+ ->setRequired(TRUE)
+ ->setDefaultValue(TRUE);
+
+ $this->state->set('entity_test_update.additional_base_field_definitions', [
+ 'status' => $status,
+ ]);
+
+ $this->entityTypeManager->clearCachedDefinitions();
+ }
+
+}
diff --git a/core/modules/system/tests/src/Functional/Update/EntityUpdateToRevisionableAndPublishableTest.php b/core/modules/system/tests/src/Functional/Update/EntityUpdateToRevisionableAndPublishableTest.php
deleted file mode 100644
index 2312dc6f0..000000000
--- a/core/modules/system/tests/src/Functional/Update/EntityUpdateToRevisionableAndPublishableTest.php
+++ /dev/null
@@ -1,219 +0,0 @@
-entityTypeManager = \Drupal::entityTypeManager();
- $this->entityDefinitionUpdateManager = \Drupal::entityDefinitionUpdateManager();
- $this->lastInstalledSchemaRepository = \Drupal::service('entity.last_installed_schema.repository');
- $this->installedStorageSchema = \Drupal::keyValue('entity.storage_schema.sql');
- $this->state = \Drupal::state();
- }
-
- /**
- * {@inheritdoc}
- */
- protected function setDatabaseDumpFiles() {
- $this->databaseDumpFiles = [
- __DIR__ . '/../../../fixtures/update/drupal-8.0.0-rc1-filled.standard.entity_test_update_mul.php.gz',
- __DIR__ . '/../../../fixtures/update/drupal-8.entity-test-schema-converter-enabled.php',
- ];
- }
-
- /**
- * Tests the conversion of an entity type to revisionable and publishable.
- *
- * @see entity_test_update_update_8400()
- */
- public function testConvertToRevisionableAndPublishable() {
- // Check that entity type is not revisionable nor publishable prior to
- // running the update process.
- $entity_test_update = $this->lastInstalledSchemaRepository->getLastInstalledDefinition('entity_test_update');
- $this->assertFalse($entity_test_update->isRevisionable());
- $this->assertFalse($entity_test_update->getKey('published'));
-
- // Make the entity type revisionable, translatable and publishable.
- $this->updateEntityTypeDefinition();
-
- $this->enableUpdates('entity_test_update', 'entity_rev_pub_updates', 8400);
- $this->runUpdates();
-
- /** @var \Drupal\Core\Entity\EntityTypeInterface $entity_test_update */
- $entity_test_update = $this->lastInstalledSchemaRepository->getLastInstalledDefinition('entity_test_update');
- $this->assertTrue($entity_test_update->isRevisionable());
- $this->assertEqual('status', $entity_test_update->getKey('published'));
-
- /** @var \Drupal\Core\Entity\Sql\SqlEntityStorageInterface $storage */
- $storage = \Drupal::entityTypeManager()->getStorage('entity_test_update');
- $this->assertEqual(count($storage->loadMultiple()), 102, 'All test entities were found.');
-
- // The conversion to revisionable is already tested by
- // \Drupal\system\Tests\Entity\Update\SqlContentEntityStorageSchemaConverterTest::testMakeRevisionable()
- // so we only need to check that some special cases are handled.
- // All the checks implemented here are taking into consideration the special
- // conditions in which the test database was created.
- // @see _entity_test_update_create_test_entities()
-
- // The test entity with ID 50 was created before Content Translation was
- // enabled, which means it didn't have a 'content_translation_status' field.
- // content_translation_update_8400() added values for that field which
- // should now be reflected in the entity's 'status' field.
- /** @var \Drupal\Core\Entity\ContentEntityInterface $revision */
- $revision = $storage->loadRevision(50);
- $this->assertEqual(1, $revision->status->value);
-
- $translation = $revision->getTranslation('ro');
- $this->assertEqual(1, $translation->status->value);
-
- // The test entity with ID 100 was created with Content Translation enabled
- // and it should have the same values as entity 50.
- $revision = $storage->loadRevision(100);
- $this->assertEqual(1, $revision->status->value);
-
- $translation = $revision->getTranslation('ro');
- $this->assertEqual(1, $translation->status->value);
-
- // The test entity 101 had 'content_translation_status' set to 0 for the
- // English (source) language.
- $revision = $storage->loadRevision(101);
- $this->assertEqual(0, $revision->status->value);
-
- $translation = $revision->getTranslation('ro');
- $this->assertEqual(1, $translation->status->value);
-
- // The test entity 102 had 'content_translation_status' set to 0 for the
- // Romanian language.
- $revision = $storage->loadRevision(102);
- $this->assertEqual(1, $revision->status->value);
-
- $translation = $revision->getTranslation('ro');
- $this->assertEqual(0, $translation->status->value);
- }
-
- /**
- * Updates the 'entity_test_update' entity type to revisionable,
- * translatable, publishable and adds revision metadata keys.
- */
- protected function updateEntityTypeDefinition() {
- $entity_type = clone $this->entityTypeManager->getDefinition('entity_test_update');
-
- $keys = $entity_type->getKeys();
- $keys['revision'] = 'revision_id';
- $keys['published'] = 'status';
- $entity_type->set('entity_keys', $keys);
-
- $revision_metadata_keys = [
- 'revision_user' => 'revision_user',
- 'revision_created' => 'revision_created',
- 'revision_log_message' => 'revision_log_message',
- 'revision_default' => 'revision_default',
- ];
- $entity_type->set('revision_metadata_keys', $revision_metadata_keys);
-
- $entity_type->set('translatable', TRUE);
- $entity_type->set('data_table', 'entity_test_update_data');
- $entity_type->set('revision_table', 'entity_test_update_revision');
- $entity_type->set('revision_data_table', 'entity_test_update_revision_data');
-
- $this->state->set('entity_test_update.entity_type', $entity_type);
-
- // Also add the status and revision metadata base fields to the entity type.
- $status = BaseFieldDefinition::create('boolean')
- ->setLabel(t('Publishing status'))
- ->setDescription(t('A boolean indicating the published state.'))
- ->setRevisionable(TRUE)
- ->setTranslatable(TRUE)
- ->setRequired(TRUE)
- ->setDefaultValue(TRUE);
-
- $revision_created = BaseFieldDefinition::create('created')
- ->setLabel(t('Revision create time'))
- ->setDescription(t('The time that the current revision was created.'))
- ->setRevisionable(TRUE);
-
- $revision_user = BaseFieldDefinition::create('entity_reference')
- ->setLabel(t('Revision user'))
- ->setDescription(t('The user ID of the author of the current revision.'))
- ->setSetting('target_type', 'user')
- ->setRevisionable(TRUE);
-
- $revision_log_message = BaseFieldDefinition::create('string_long')
- ->setLabel(t('Revision log message'))
- ->setDescription(t('Briefly describe the changes you have made.'))
- ->setRevisionable(TRUE)
- ->setDefaultValue('')
- ->setDisplayOptions('form', [
- 'type' => 'string_textarea',
- 'weight' => 25,
- 'settings' => [
- 'rows' => 4,
- ],
- ]);
-
- $this->state->set('entity_test_update.additional_base_field_definitions', [
- 'status' => $status,
- 'revision_created' => $revision_created,
- 'revision_user' => $revision_user,
- 'revision_log_message' => $revision_log_message,
- ]);
-
- $this->entityTypeManager->clearCachedDefinitions();
- }
-
-}
diff --git a/core/modules/system/tests/src/Functional/Update/FieldSchemaDataUninstallUpdateTest.php b/core/modules/system/tests/src/Functional/Update/FieldSchemaDataUninstallUpdateTest.php
index 782e3410f..1beb62b73 100644
--- a/core/modules/system/tests/src/Functional/Update/FieldSchemaDataUninstallUpdateTest.php
+++ b/core/modules/system/tests/src/Functional/Update/FieldSchemaDataUninstallUpdateTest.php
@@ -10,6 +10,7 @@ use Drupal\FunctionalTests\Update\UpdatePathTestBase;
* @see https://www.drupal.org/node/2573667
*
* @group Update
+ * @group legacy
*/
class FieldSchemaDataUninstallUpdateTest extends UpdatePathTestBase {
diff --git a/core/modules/system/tests/src/Functional/Update/FilterHtmlUpdateTest.php b/core/modules/system/tests/src/Functional/Update/FilterHtmlUpdateTest.php
index 09e40aa98..01edaab42 100644
--- a/core/modules/system/tests/src/Functional/Update/FilterHtmlUpdateTest.php
+++ b/core/modules/system/tests/src/Functional/Update/FilterHtmlUpdateTest.php
@@ -9,6 +9,7 @@ use Drupal\FunctionalTests\Update\UpdatePathTestBase;
* Tests that the allowed html configutations are updated with attributes.
*
* @group Entity
+ * @group legacy
*/
class FilterHtmlUpdateTest extends UpdatePathTestBase {
diff --git a/core/modules/system/tests/src/Functional/Update/InstallProfileSystemInstall8300Test.php b/core/modules/system/tests/src/Functional/Update/InstallProfileSystemInstall8300Test.php
index 623f88047..0a1d03ff3 100644
--- a/core/modules/system/tests/src/Functional/Update/InstallProfileSystemInstall8300Test.php
+++ b/core/modules/system/tests/src/Functional/Update/InstallProfileSystemInstall8300Test.php
@@ -9,6 +9,7 @@ use Drupal\FunctionalTests\Update\UpdatePathTestBase;
* Tests system_update_8300().
*
* @group Update
+ * @group legacy
*/
class InstallProfileSystemInstall8300Test extends UpdatePathTestBase {
@@ -23,6 +24,8 @@ class InstallProfileSystemInstall8300Test extends UpdatePathTestBase {
/**
* Ensures that the system_update_8300() runs as expected.
+ *
+ * @expectedDeprecation To access the install profile in Drupal 8 use \Drupal::installProfile() or inject the install_profile container parameter into your service. See https://www.drupal.org/node/2538996
*/
public function testUpdate() {
// Ensure the BC layers work and settings.php and configuration is in the
diff --git a/core/modules/system/tests/src/Functional/Update/InvalidUpdateHookTest.php b/core/modules/system/tests/src/Functional/Update/InvalidUpdateHookTest.php
index 44d4832dd..85d041e2a 100644
--- a/core/modules/system/tests/src/Functional/Update/InvalidUpdateHookTest.php
+++ b/core/modules/system/tests/src/Functional/Update/InvalidUpdateHookTest.php
@@ -35,7 +35,7 @@ class InvalidUpdateHookTest extends BrowserTestBase {
protected function setUp() {
parent::setUp();
- require_once \Drupal::root() . '/core/includes/update.inc';
+ require_once $this->root . '/core/includes/update.inc';
$this->updateUrl = $GLOBALS['base_url'] . '/update.php';
$this->updateUser = $this->drupalCreateUser(['administer software updates']);
diff --git a/core/modules/system/tests/src/Functional/Update/LocalActionsAndTasksConvertedIntoBlocksUpdateTest.php b/core/modules/system/tests/src/Functional/Update/LocalActionsAndTasksConvertedIntoBlocksUpdateTest.php
index a54bb6d1d..cfa822104 100644
--- a/core/modules/system/tests/src/Functional/Update/LocalActionsAndTasksConvertedIntoBlocksUpdateTest.php
+++ b/core/modules/system/tests/src/Functional/Update/LocalActionsAndTasksConvertedIntoBlocksUpdateTest.php
@@ -11,6 +11,7 @@ use Drupal\node\Entity\Node;
* @see https://www.drupal.org/node/507488
*
* @group system
+ * @group legacy
*/
class LocalActionsAndTasksConvertedIntoBlocksUpdateTest extends UpdatePathTestBase {
diff --git a/core/modules/system/tests/src/Functional/Update/MenuTreeSerializationTitleFilledTest.php b/core/modules/system/tests/src/Functional/Update/MenuTreeSerializationTitleFilledTest.php
index 5bf07d849..58429e3cb 100644
--- a/core/modules/system/tests/src/Functional/Update/MenuTreeSerializationTitleFilledTest.php
+++ b/core/modules/system/tests/src/Functional/Update/MenuTreeSerializationTitleFilledTest.php
@@ -6,6 +6,7 @@ namespace Drupal\Tests\system\Functional\Update;
* Runs MenuTreeSerializationTitleTest with a dump filled with content.
*
* @group Update
+ * @group legacy
*/
class MenuTreeSerializationTitleFilledTest extends MenuTreeSerializationTitleTest {
diff --git a/core/modules/system/tests/src/Functional/Update/MenuTreeSerializationTitleTest.php b/core/modules/system/tests/src/Functional/Update/MenuTreeSerializationTitleTest.php
index 0df71482b..1a3fa84f4 100644
--- a/core/modules/system/tests/src/Functional/Update/MenuTreeSerializationTitleTest.php
+++ b/core/modules/system/tests/src/Functional/Update/MenuTreeSerializationTitleTest.php
@@ -9,6 +9,7 @@ use Drupal\FunctionalTests\Update\UpdatePathTestBase;
* Tests system_update_8001().
*
* @group Update
+ * @group legacy
*/
class MenuTreeSerializationTitleTest extends UpdatePathTestBase {
diff --git a/core/modules/system/tests/src/Functional/Update/PageTitleConvertedIntoBlockUpdateTest.php b/core/modules/system/tests/src/Functional/Update/PageTitleConvertedIntoBlockUpdateTest.php
index 2f7306a7c..c9c264c96 100644
--- a/core/modules/system/tests/src/Functional/Update/PageTitleConvertedIntoBlockUpdateTest.php
+++ b/core/modules/system/tests/src/Functional/Update/PageTitleConvertedIntoBlockUpdateTest.php
@@ -11,6 +11,7 @@ use Drupal\node\Entity\Node;
* @see https://www.drupal.org/node/2476947
*
* @group system
+ * @group legacy
*/
class PageTitleConvertedIntoBlockUpdateTest extends UpdatePathTestBase {
diff --git a/core/modules/system/tests/src/Functional/Update/RecalculatedDependencyTest.php b/core/modules/system/tests/src/Functional/Update/RecalculatedDependencyTest.php
index 6eb51a2a5..2cda26e9a 100644
--- a/core/modules/system/tests/src/Functional/Update/RecalculatedDependencyTest.php
+++ b/core/modules/system/tests/src/Functional/Update/RecalculatedDependencyTest.php
@@ -8,6 +8,7 @@ use Drupal\FunctionalTests\Update\UpdatePathTestBase;
* Tests system_post_update_recalculate_dependencies_for_installed_config_entities().
*
* @group Update
+ * @group legacy
*/
class RecalculatedDependencyTest extends UpdatePathTestBase {
@@ -58,7 +59,7 @@ class RecalculatedDependencyTest extends UpdatePathTestBase {
$this->assertEqual([
'field.storage.node.field_tags',
'node.type.article',
- 'taxonomy.vocabulary.tags'
+ 'taxonomy.vocabulary.tags',
], $data['dependencies']['config']);
$data = \Drupal::config('field.field.user.user.user_picture')->get();
diff --git a/core/modules/system/tests/src/Functional/Update/RemoveResponseGzipFromSystemPerformanceConfigurationTest.php b/core/modules/system/tests/src/Functional/Update/RemoveResponseGzipFromSystemPerformanceConfigurationTest.php
index b2ccfe679..f7f772b13 100644
--- a/core/modules/system/tests/src/Functional/Update/RemoveResponseGzipFromSystemPerformanceConfigurationTest.php
+++ b/core/modules/system/tests/src/Functional/Update/RemoveResponseGzipFromSystemPerformanceConfigurationTest.php
@@ -8,6 +8,7 @@ use Drupal\FunctionalTests\Update\UpdatePathTestBase;
* Ensures that response.gzip is removed from system.performance configuration.
*
* @group Update
+ * @group legacy
*/
class RemoveResponseGzipFromSystemPerformanceConfigurationTest extends UpdatePathTestBase {
diff --git a/core/modules/system/tests/src/Functional/Update/RouterIndexOptimizationFilledTest.php b/core/modules/system/tests/src/Functional/Update/RouterIndexOptimizationFilledTest.php
index afb48108f..93c9c8e23 100644
--- a/core/modules/system/tests/src/Functional/Update/RouterIndexOptimizationFilledTest.php
+++ b/core/modules/system/tests/src/Functional/Update/RouterIndexOptimizationFilledTest.php
@@ -6,6 +6,7 @@ namespace Drupal\Tests\system\Functional\Update;
* Runs RouterIndexOptimizationTest with a dump filled with content.
*
* @group Update
+ * @group legacy
*/
class RouterIndexOptimizationFilledTest extends RouterIndexOptimizationTest {
diff --git a/core/modules/system/tests/src/Functional/Update/RouterIndexOptimizationTest.php b/core/modules/system/tests/src/Functional/Update/RouterIndexOptimizationTest.php
index a3bfad256..f4672b4cd 100644
--- a/core/modules/system/tests/src/Functional/Update/RouterIndexOptimizationTest.php
+++ b/core/modules/system/tests/src/Functional/Update/RouterIndexOptimizationTest.php
@@ -8,8 +8,10 @@ use Drupal\FunctionalTests\Update\UpdatePathTestBase;
* Tests system_update_8002().
*
* @group Update
+ * @group legacy
*/
class RouterIndexOptimizationTest extends UpdatePathTestBase {
+
/**
* {@inheritdoc}
*/
diff --git a/core/modules/system/tests/src/Functional/Update/SevenSecondaryLocalTasksConvertedIntoBlockUpdateTest.php b/core/modules/system/tests/src/Functional/Update/SevenSecondaryLocalTasksConvertedIntoBlockUpdateTest.php
index 341b37be1..172897497 100644
--- a/core/modules/system/tests/src/Functional/Update/SevenSecondaryLocalTasksConvertedIntoBlockUpdateTest.php
+++ b/core/modules/system/tests/src/Functional/Update/SevenSecondaryLocalTasksConvertedIntoBlockUpdateTest.php
@@ -10,6 +10,7 @@ use Drupal\FunctionalTests\Update\UpdatePathTestBase;
* @see https://www.drupal.org/node/2569529
*
* @group system
+ * @group legacy
*/
class SevenSecondaryLocalTasksConvertedIntoBlockUpdateTest extends UpdatePathTestBase {
diff --git a/core/modules/system/tests/src/Functional/Update/SiteBrandingConvertedIntoBlockUpdateTest.php b/core/modules/system/tests/src/Functional/Update/SiteBrandingConvertedIntoBlockUpdateTest.php
index d904e10ed..72f3a2782 100644
--- a/core/modules/system/tests/src/Functional/Update/SiteBrandingConvertedIntoBlockUpdateTest.php
+++ b/core/modules/system/tests/src/Functional/Update/SiteBrandingConvertedIntoBlockUpdateTest.php
@@ -8,6 +8,7 @@ use Drupal\FunctionalTests\Update\UpdatePathTestBase;
* Tests the upgrade path for page site variables being converted into a block.
*
* @group system
+ * @group legacy
*/
class SiteBrandingConvertedIntoBlockUpdateTest extends UpdatePathTestBase {
diff --git a/core/modules/system/tests/src/Functional/Update/StableBaseThemeUpdateTest.php b/core/modules/system/tests/src/Functional/Update/StableBaseThemeUpdateTest.php
index 326bbbb9a..2bfc59d21 100644
--- a/core/modules/system/tests/src/Functional/Update/StableBaseThemeUpdateTest.php
+++ b/core/modules/system/tests/src/Functional/Update/StableBaseThemeUpdateTest.php
@@ -10,6 +10,7 @@ use Drupal\FunctionalTests\Update\UpdatePathTestBase;
* @see https://www.drupal.org/node/2575421
*
* @group system
+ * @group legacy
*/
class StableBaseThemeUpdateTest extends UpdatePathTestBase {
diff --git a/core/modules/system/tests/src/Functional/Update/UpdateActionsWithEntityPluginsTest.php b/core/modules/system/tests/src/Functional/Update/UpdateActionsWithEntityPluginsTest.php
index 4094d0031..764c92c5f 100644
--- a/core/modules/system/tests/src/Functional/Update/UpdateActionsWithEntityPluginsTest.php
+++ b/core/modules/system/tests/src/Functional/Update/UpdateActionsWithEntityPluginsTest.php
@@ -9,6 +9,7 @@ use Drupal\system\Entity\Action;
* Tests upgrading comment and node actions to generic entity ones.
*
* @group Update
+ * @group legacy
*/
class UpdateActionsWithEntityPluginsTest extends UpdatePathTestBase {
@@ -53,4 +54,33 @@ class UpdateActionsWithEntityPluginsTest extends UpdatePathTestBase {
}
}
+ /**
+ * Tests upgrading comment and node delete actions to generic entity ones.
+ *
+ * @see system_post_update_change_delete_action_plugins()
+ */
+ public function testUpdateDeleteActionsWithEntityPlugins() {
+ // comment_delete_actions is not part of the dump files.
+ $array = [
+ 'node_delete_action' => ['node_delete_action', 'entity:delete_action:node'],
+ ];
+
+ foreach ($array as $key => list($before, $after)) {
+ /** @var \Drupal\system\Entity\Action $action */
+ $action = Action::load($key);
+ $this->assertSame($before, $action->getPlugin()->getPluginId());
+ }
+
+ $this->runUpdates();
+
+ foreach ($array as $key => list($before, $after)) {
+ /** @var \Drupal\system\Entity\Action $action */
+ $action = Action::load($key);
+ $this->assertSame($after, $action->getPlugin()->getPluginId());
+
+ // Check that the type the action is based on will be a module dependency.
+ $this->assertArraySubset(['module' => [$action->getPluginDefinition()['type']]], $action->getDependencies());
+ }
+ }
+
}
diff --git a/core/modules/system/tests/src/Functional/Update/UpdateEntityDisplayTest.php b/core/modules/system/tests/src/Functional/Update/UpdateEntityDisplayTest.php
index 698d33109..187c7ac91 100644
--- a/core/modules/system/tests/src/Functional/Update/UpdateEntityDisplayTest.php
+++ b/core/modules/system/tests/src/Functional/Update/UpdateEntityDisplayTest.php
@@ -7,9 +7,10 @@ use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
/**
- * Tests system_post_update_add_region_to_entity_displays().
+ * Tests updates for entity displays.
*
* @group Update
+ * @group legacy
*/
class UpdateEntityDisplayTest extends UpdatePathTestBase {
@@ -24,8 +25,10 @@ class UpdateEntityDisplayTest extends UpdatePathTestBase {
/**
* Tests that entity displays are updated with regions for their fields.
+ *
+ * @see system_post_update_add_region_to_entity_displays()
*/
- public function testUpdate() {
+ public function testRegionUpdate() {
// No region key appears pre-update.
$entity_form_display = EntityFormDisplay::load('node.article.default');
$options = $entity_form_display->getComponent('body');
@@ -47,4 +50,22 @@ class UpdateEntityDisplayTest extends UpdatePathTestBase {
$this->assertIdentical('content', $options['region']);
}
+ /**
+ * Tests that entity displays are updated to properly store extra fields.
+ *
+ * @see system_post_update_extra_fields()
+ */
+ public function testExtraFieldsUpdate() {
+ $assertion = function ($expected_keys) {
+ $entity_view_display = EntityViewDisplay::load('node.article.default');
+ $this->assertEquals($expected_keys, array_keys($entity_view_display->getComponent('links')));
+ };
+
+ // Before the update extra fields are missing additional configuration.
+ $assertion(['weight', 'region']);
+ $this->runUpdates();
+ // After the update the additional configuration is present.
+ $assertion(['weight', 'region', 'settings', 'third_party_settings']);
+ }
+
}
diff --git a/core/modules/system/tests/src/Functional/Update/UpdatePathRC1TestBaseFilledTest.php b/core/modules/system/tests/src/Functional/Update/UpdatePathRC1TestBaseFilledTest.php
index 7573fc4a9..45c3d8477 100644
--- a/core/modules/system/tests/src/Functional/Update/UpdatePathRC1TestBaseFilledTest.php
+++ b/core/modules/system/tests/src/Functional/Update/UpdatePathRC1TestBaseFilledTest.php
@@ -10,6 +10,7 @@ use Drupal\user\Entity\User;
* Runs UpdatePathTestBaseTest with a RC1 dump filled with content.
*
* @group Update
+ * @group legacy
*/
class UpdatePathRC1TestBaseFilledTest extends UpdatePathRC1TestBaseTest {
diff --git a/core/modules/system/tests/src/Functional/Update/UpdatePathRC1TestBaseTest.php b/core/modules/system/tests/src/Functional/Update/UpdatePathRC1TestBaseTest.php
index 4077a07d2..da53ba6e6 100644
--- a/core/modules/system/tests/src/Functional/Update/UpdatePathRC1TestBaseTest.php
+++ b/core/modules/system/tests/src/Functional/Update/UpdatePathRC1TestBaseTest.php
@@ -9,6 +9,7 @@ use Drupal\FunctionalTests\Update\UpdatePathTestBase;
* Tests the update path base class with the RC1 database dump.
*
* @group Update
+ * @group legacy
*/
class UpdatePathRC1TestBaseTest extends UpdatePathTestBase {
diff --git a/core/modules/system/tests/src/Functional/Update/UpdatePathTestBaseFilledTest.php b/core/modules/system/tests/src/Functional/Update/UpdatePathTestBaseFilledTest.php
index 2ed58fbab..84b44df54 100644
--- a/core/modules/system/tests/src/Functional/Update/UpdatePathTestBaseFilledTest.php
+++ b/core/modules/system/tests/src/Functional/Update/UpdatePathTestBaseFilledTest.php
@@ -11,6 +11,7 @@ use Drupal\user\Entity\User;
* Runs UpdatePathTestBaseTest with a dump filled with content.
*
* @group Update
+ * @group legacy
*/
class UpdatePathTestBaseFilledTest extends UpdatePathTestBaseTest {
diff --git a/core/modules/system/tests/src/Functional/Update/UpdatePathTestJavaScriptTest.php b/core/modules/system/tests/src/Functional/Update/UpdatePathTestJavaScriptTest.php
index e30915e76..d5bcb1682 100644
--- a/core/modules/system/tests/src/Functional/Update/UpdatePathTestJavaScriptTest.php
+++ b/core/modules/system/tests/src/Functional/Update/UpdatePathTestJavaScriptTest.php
@@ -8,6 +8,7 @@ use Drupal\FunctionalTests\Update\UpdatePathTestBase;
* Tests the presence of JavaScript at update.php.
*
* @group Update
+ * @group legacy
*/
class UpdatePathTestJavaScriptTest extends UpdatePathTestBase {
diff --git a/core/modules/system/tests/src/Functional/Update/UpdatePathWithBrokenRoutingFilledTest.php b/core/modules/system/tests/src/Functional/Update/UpdatePathWithBrokenRoutingFilledTest.php
index 0e81ad479..4eaec54e3 100644
--- a/core/modules/system/tests/src/Functional/Update/UpdatePathWithBrokenRoutingFilledTest.php
+++ b/core/modules/system/tests/src/Functional/Update/UpdatePathWithBrokenRoutingFilledTest.php
@@ -6,6 +6,7 @@ namespace Drupal\Tests\system\Functional\Update;
* Runs UpdatePathWithBrokenRoutingTest with a dump filled with content.
*
* @group Update
+ * @group legacy
*/
class UpdatePathWithBrokenRoutingFilledTest extends UpdatePathWithBrokenRoutingTest {
diff --git a/core/modules/system/tests/src/Functional/Update/UpdatePathWithBrokenRoutingTest.php b/core/modules/system/tests/src/Functional/Update/UpdatePathWithBrokenRoutingTest.php
index 1c8493d94..c54804712 100644
--- a/core/modules/system/tests/src/Functional/Update/UpdatePathWithBrokenRoutingTest.php
+++ b/core/modules/system/tests/src/Functional/Update/UpdatePathWithBrokenRoutingTest.php
@@ -8,6 +8,7 @@ use Drupal\FunctionalTests\Update\UpdatePathTestBase;
* Tests the update path with a broken router.
*
* @group Update
+ * @group legacy
*/
class UpdatePathWithBrokenRoutingTest extends UpdatePathTestBase {
diff --git a/core/modules/system/tests/src/Functional/Update/UpdatePostUpdateFailingTest.php b/core/modules/system/tests/src/Functional/Update/UpdatePostUpdateFailingTest.php
index 03b5d7b0e..a01476534 100644
--- a/core/modules/system/tests/src/Functional/Update/UpdatePostUpdateFailingTest.php
+++ b/core/modules/system/tests/src/Functional/Update/UpdatePostUpdateFailingTest.php
@@ -8,6 +8,7 @@ use Drupal\FunctionalTests\Update\UpdatePathTestBase;
* Tests hook_post_update() when there are failing update hooks.
*
* @group Update
+ * @group legacy
*/
class UpdatePostUpdateFailingTest extends UpdatePathTestBase {
diff --git a/core/modules/system/tests/src/Functional/Update/UpdatePostUpdateTest.php b/core/modules/system/tests/src/Functional/Update/UpdatePostUpdateTest.php
index 1bba498d0..fd9e01170 100644
--- a/core/modules/system/tests/src/Functional/Update/UpdatePostUpdateTest.php
+++ b/core/modules/system/tests/src/Functional/Update/UpdatePostUpdateTest.php
@@ -9,6 +9,7 @@ use Drupal\FunctionalTests\Update\UpdatePathTestBase;
* Tests hook_post_update().
*
* @group Update
+ * @group legacy
*/
class UpdatePostUpdateTest extends UpdatePathTestBase {
diff --git a/core/modules/system/tests/src/Functional/Update/UpdateSchemaTest.php b/core/modules/system/tests/src/Functional/Update/UpdateSchemaTest.php
index a42cbcb3c..1c2edee06 100644
--- a/core/modules/system/tests/src/Functional/Update/UpdateSchemaTest.php
+++ b/core/modules/system/tests/src/Functional/Update/UpdateSchemaTest.php
@@ -35,7 +35,7 @@ class UpdateSchemaTest extends BrowserTestBase {
protected function setUp() {
parent::setUp();
- require_once \Drupal::root() . '/core/includes/update.inc';
+ require_once $this->root . '/core/includes/update.inc';
$this->user = $this->drupalCreateUser(['administer software updates', 'access site in maintenance mode']);
$this->updateUrl = Url::fromRoute('system.db_update');
}
diff --git a/core/modules/system/tests/src/Functional/Update/UpdatesWith7xTest.php b/core/modules/system/tests/src/Functional/Update/UpdatesWith7xTest.php
index 2452264e3..4f536ea5a 100644
--- a/core/modules/system/tests/src/Functional/Update/UpdatesWith7xTest.php
+++ b/core/modules/system/tests/src/Functional/Update/UpdatesWith7xTest.php
@@ -31,7 +31,7 @@ class UpdatesWith7xTest extends BrowserTestBase {
protected function setUp() {
parent::setUp();
- require_once \Drupal::root() . '/core/includes/update.inc';
+ require_once $this->root . '/core/includes/update.inc';
$this->updateUrl = $GLOBALS['base_url'] . '/update.php';
$this->updateUser = $this->drupalCreateUser(['administer software updates']);
}
diff --git a/core/modules/system/tests/src/FunctionalJavascript/ModalRendererTest.php b/core/modules/system/tests/src/FunctionalJavascript/ModalRendererTest.php
index 7c0aec022..3597ba46d 100644
--- a/core/modules/system/tests/src/FunctionalJavascript/ModalRendererTest.php
+++ b/core/modules/system/tests/src/FunctionalJavascript/ModalRendererTest.php
@@ -2,14 +2,14 @@
namespace Drupal\Tests\system\FunctionalJavascript;
-use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
+use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
/**
* Tests that dialog links use different renderer services.
*
* @group system
*/
-class ModalRendererTest extends JavascriptTestBase {
+class ModalRendererTest extends WebDriverTestBase {
/**
* {@inheritdoc}
diff --git a/core/modules/system/tests/src/FunctionalJavascript/OffCanvasTest.php b/core/modules/system/tests/src/FunctionalJavascript/OffCanvasTest.php
index 567919c3e..c42d777c1 100644
--- a/core/modules/system/tests/src/FunctionalJavascript/OffCanvasTest.php
+++ b/core/modules/system/tests/src/FunctionalJavascript/OffCanvasTest.php
@@ -5,10 +5,19 @@ namespace Drupal\Tests\system\FunctionalJavascript;
/**
* Tests the off-canvas dialog functionality.
*
- * @group settings_tray
+ * @group system
*/
class OffCanvasTest extends OffCanvasTestBase {
+ /**
+ * Stores to the class that should be on the last dialog.
+ *
+ * @var string
+ *
+ * @see \Drupal\off_canvas_test\Controller\TestController::linksDisplay.
+ */
+ protected $lastDialogClass;
+
/**
* {@inheritdoc}
*/
@@ -18,60 +27,83 @@ class OffCanvasTest extends OffCanvasTestBase {
/**
* Tests that non-contextual links will work with the off-canvas dialog.
+ *
+ * @dataProvider themeDataProvider
*/
- public function testOffCanvasLinks() {
- // Test the same functionality on multiple themes.
- foreach ($this->getTestThemes() as $theme) {
- $this->enableTheme($theme);
- $this->drupalGet('/off-canvas-test-links');
+ public function testOffCanvasLinks($theme) {
+ $this->enableTheme($theme);
+ $this->drupalGet('/off-canvas-test-links');
- $page = $this->getSession()->getPage();
- $web_assert = $this->assertSession();
+ $page = $this->getSession()->getPage();
+ $web_assert = $this->assertSession();
- // Make sure off-canvas dialog is on page when first loaded.
- $web_assert->elementNotExists('css', '#drupal-off-canvas');
+ // Make sure off-canvas dialog is on page when first loaded.
+ $web_assert->elementNotExists('css', '#drupal-off-canvas');
- // Check opening and closing with two separate links.
- // Make sure tray updates to new content.
- // Check the first link again to make sure the empty title class is
- // removed.
- foreach (['1', '2', '1'] as $link_index) {
- // Click the first test like that should open the page.
- $page->clickLink("Click Me $link_index!");
+ // Check opening and closing with two separate links.
+ // Make sure tray updates to new content.
+ // Check the first link again to make sure the empty title class is
+ // removed.
+ foreach (['1', '2', '1'] as $link_index) {
+ $this->assertOffCanvasDialog($link_index, 'side');
+ $header_text = $this->getOffCanvasDialog()->find('css', '.ui-dialog-title')->getText();
+ if ($link_index == '2') {
+ // Check no title behavior.
+ $web_assert->elementExists('css', '.ui-dialog-empty-title');
+ $this->assertEquals(' ', $header_text);
+
+ $style = $page->find('css', '.ui-dialog-off-canvas')->getAttribute('style');
+ $this->assertTrue(strstr($style, 'width: 555px;') !== FALSE, 'Dialog width respected.');
+ $page->clickLink("Open side panel 1");
$this->waitForOffCanvasToOpen();
-
- // Check that the canvas is not on the page.
- $web_assert->elementExists('css', '#drupal-off-canvas');
- // Check that response text is on page.
- $web_assert->pageTextContains("Thing $link_index says hello");
- $off_canvas_tray = $this->getOffCanvasDialog();
-
- // Check that tray is visible.
- $this->assertEquals(TRUE, $off_canvas_tray->isVisible());
- $header_text = $off_canvas_tray->find('css', '.ui-dialog-title')->getText();
-
- $tray_text = $off_canvas_tray->findById('drupal-off-canvas')->getText();
- $this->assertEquals("Thing $link_index says hello", $tray_text);
-
- if ($link_index == '2') {
- // Check no title behavior.
- $web_assert->elementExists('css', '.ui-dialog-empty-title');
- $this->assertEquals("\xc2\xa0", $header_text);
-
- $style = $page->find('css', '.ui-dialog-off-canvas')->getAttribute('style');
- $this->assertTrue(strstr($style, 'width: 555px;') !== FALSE, 'Dialog width respected.');
- $page->clickLink("Click Me 1!");
- $this->waitForOffCanvasToOpen();
- $style = $page->find('css', '.ui-dialog-off-canvas')->getAttribute('style');
- $this->assertTrue(strstr($style, 'width: 555px;') === FALSE, 'Dialog width reset to default.');
- }
- else {
- // Check that header is correct.
- $this->assertEquals("Thing $link_index", $header_text);
- $web_assert->elementNotExists('css', '.ui-dialog-empty-title');
- }
+ $style = $page->find('css', '.ui-dialog-off-canvas')->getAttribute('style');
+ $this->assertTrue(strstr($style, 'width: 555px;') === FALSE, 'Dialog width reset to default.');
+ }
+ else {
+ // Check that header is correct.
+ $this->assertEquals("Thing $link_index", $header_text);
+ $web_assert->elementNotExists('css', '.ui-dialog-empty-title');
}
}
+
+ // Test the off_canvas_top tray.
+ foreach ([1, 2] as $link_index) {
+ $this->assertOffCanvasDialog($link_index, 'top');
+
+ $style = $page->find('css', '.ui-dialog-off-canvas')->getAttribute('style');
+ if ($link_index === 1) {
+ $this->assertTrue((bool) strstr($style, 'height: auto;'));
+ }
+ else {
+ $this->assertTrue((bool) strstr($style, 'height: 421px;'));
+ }
+ }
+
+ // Ensure an off-canvas link opened from inside the off-canvas dialog will
+ // work.
+ $this->drupalGet('/off-canvas-test-links');
+ $page->clickLink('Display more links!');
+ $this->waitForOffCanvasToOpen();
+ $web_assert->linkExists('Off_canvas link!');
+ // Click off-canvas link inside off-canvas dialog
+ $page->clickLink('Off_canvas link!');
+ /* @var \Behat\Mink\Element\NodeElement $dialog */
+ $this->waitForOffCanvasToOpen();
+ $web_assert->elementTextContains('css', '.ui-dialog[aria-describedby="drupal-off-canvas"]', 'Thing 2 says hello');
+
+ // Ensure an off-canvas link opened from inside the off-canvas dialog will
+ // work after another dialog has been opened.
+ $this->drupalGet('/off-canvas-test-links');
+ $page->clickLink("Open side panel 1");
+ $this->waitForOffCanvasToOpen();
+ $page->clickLink('Display more links!');
+ $this->waitForOffCanvasToOpen();
+ $web_assert->linkExists('Off_canvas link!');
+ // Click off-canvas link inside off-canvas dialog
+ $page->clickLink('Off_canvas link!');
+ /* @var \Behat\Mink\Element\NodeElement $dialog */
+ $this->waitForOffCanvasToOpen();
+ $web_assert->elementTextContains('css', '.ui-dialog[aria-describedby="drupal-off-canvas"]', 'Thing 2 says hello');
}
/**
@@ -91,20 +123,53 @@ class OffCanvasTest extends OffCanvasTestBase {
$this->getSession()->resizeWindow($narrow_width_breakpoint + $offset, $height);
$this->drupalGet('/off-canvas-test-links');
$this->assertFalse($page->find('css', '.dialog-off-canvas-main-canvas')->hasAttribute('style'), 'Body not padded on wide page load.');
- $page->clickLink("Click Me 1!");
+ $page->clickLink("Open side panel 1");
$this->waitForOffCanvasToOpen();
// Check that the main canvas is padded when page is not narrow width and
// tray is open.
+ $page->waitFor(10, function ($page) {
+ return $page->find('css', '.dialog-off-canvas-main-canvas')->hasAttribute('style');
+ });
$web_assert->elementAttributeContains('css', '.dialog-off-canvas-main-canvas', 'style', 'padding-right');
// Testing at the narrower width.
$this->getSession()->resizeWindow($narrow_width_breakpoint - $offset, $height);
$this->drupalGet('/off-canvas-test-links');
$this->assertFalse($page->find('css', '.dialog-off-canvas-main-canvas')->hasAttribute('style'), 'Body not padded on narrow page load.');
- $page->clickLink("Click Me 1!");
+ $page->clickLink("Open side panel 1");
$this->waitForOffCanvasToOpen();
$this->assertFalse($page->find('css', '.dialog-off-canvas-main-canvas')->hasAttribute('style'), 'Body not padded on narrow page with tray open.');
}
}
+ /**
+ * @param int $link_index
+ * The index of the link to test.
+ * @param string $position
+ * The position of the dialog to test.
+ */
+ protected function assertOffCanvasDialog($link_index, $position) {
+ $page = $this->getSession()->getPage();
+ $web_assert = $this->assertSession();
+ $link_text = "Open $position panel $link_index";
+
+ // Click the first test like that should open the page.
+ $page->clickLink($link_text);
+ if ($this->lastDialogClass) {
+ $this->waitForNoElement('.' . $this->lastDialogClass);
+ }
+ $this->waitForOffCanvasToOpen($position);
+ $this->lastDialogClass = "$position-$link_index";
+
+ // Check that response text is on page.
+ $web_assert->pageTextContains("Thing $link_index says hello");
+ $off_canvas_tray = $this->getOffCanvasDialog();
+
+ // Check that tray is visible.
+ $this->assertEquals(TRUE, $off_canvas_tray->isVisible());
+
+ $tray_text = $off_canvas_tray->findById('drupal-off-canvas')->getText();
+ $this->assertEquals("Thing $link_index says hello", $tray_text);
+ }
+
}
diff --git a/core/modules/system/tests/src/FunctionalJavascript/OffCanvasTestBase.php b/core/modules/system/tests/src/FunctionalJavascript/OffCanvasTestBase.php
index d3f446cf6..b2e6c2b65 100644
--- a/core/modules/system/tests/src/FunctionalJavascript/OffCanvasTestBase.php
+++ b/core/modules/system/tests/src/FunctionalJavascript/OffCanvasTestBase.php
@@ -2,12 +2,12 @@
namespace Drupal\Tests\system\FunctionalJavascript;
-use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
+use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
/**
* Base class contains common test functionality for the Off-canvas dialog.
*/
-abstract class OffCanvasTestBase extends JavascriptTestBase {
+abstract class OffCanvasTestBase extends WebDriverTestBase {
/**
* {@inheritdoc}
@@ -60,14 +60,21 @@ abstract class OffCanvasTestBase extends JavascriptTestBase {
/**
* Waits for off-canvas dialog to open.
+ *
+ * @param string $position
+ * The position of the dialog.
+ *
+ * @throws \Behat\Mink\Exception\ElementNotFoundException
*/
- protected function waitForOffCanvasToOpen() {
+ protected function waitForOffCanvasToOpen($position = 'side') {
$web_assert = $this->assertSession();
// Wait just slightly longer than the off-canvas dialog CSS animation.
// @see core/misc/dialog/off-canvas.motion.css
$this->getSession()->wait(800);
$web_assert->assertWaitOnAjaxRequest();
$this->assertElementVisibleAfterWait('css', '#drupal-off-canvas');
+ // Check that the canvas is positioned on the side.
+ $web_assert->elementExists('css', '.ui-dialog-position-' . $position);
}
/**
@@ -99,8 +106,22 @@ abstract class OffCanvasTestBase extends JavascriptTestBase {
* @todo Remove in https://www.drupal.org/node/2892440.
*/
protected function waitForNoElement($selector, $timeout = 10000) {
- $condition = "(typeof jQuery !== 'undefined' && jQuery('$selector').length === 0)";
- $this->assertJsCondition($condition, $timeout);
+
+ $start = microtime(TRUE);
+ $end = $start + ($timeout / 1000);
+ $page = $this->getSession()->getPage();
+
+ do {
+ $result = $page->find('css', $selector);
+
+ if (empty($result)) {
+ return;
+ }
+
+ usleep(100000);
+ } while (microtime(TRUE) < $end);
+
+ $this->assertEmpty($result, 'Element was not on the page after wait.');
}
/**
@@ -125,7 +146,22 @@ abstract class OffCanvasTestBase extends JavascriptTestBase {
* (Optional) Timeout in milliseconds, defaults to 10000.
*/
protected function assertElementVisibleAfterWait($selector, $locator, $timeout = 10000) {
+ $this->assertSession()->assertWaitOnAjaxRequest();
$this->assertNotEmpty($this->assertSession()->waitForElementVisible($selector, $locator, $timeout));
}
+ /**
+ * Dataprovider that returns theme name as the sole argument.
+ */
+ public function themeDataProvider() {
+ $themes = $this->getTestThemes();
+ $data = [];
+ foreach ($themes as $theme) {
+ $data[$theme] = [
+ $theme,
+ ];
+ }
+ return $data;
+ }
+
}
diff --git a/core/modules/system/tests/src/FunctionalJavascript/ThemeFormSettingsTest.php b/core/modules/system/tests/src/FunctionalJavascript/ThemeFormSettingsTest.php
index 8e4729fe3..4f3e9f1d4 100644
--- a/core/modules/system/tests/src/FunctionalJavascript/ThemeFormSettingsTest.php
+++ b/core/modules/system/tests/src/FunctionalJavascript/ThemeFormSettingsTest.php
@@ -3,7 +3,7 @@
namespace Drupal\Tests\system\FunctionalJavascript;
use Drupal\file\Entity\File;
-use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
+use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
use Drupal\Tests\TestFileCreationTrait;
/**
@@ -11,7 +11,7 @@ use Drupal\Tests\TestFileCreationTrait;
*
* @group system
*/
-class ThemeFormSettingsTest extends JavascriptTestBase {
+class ThemeFormSettingsTest extends WebDriverTestBase {
use TestFileCreationTrait;
diff --git a/core/modules/system/tests/src/Kernel/Block/SystemMenuBlockTest.php b/core/modules/system/tests/src/Kernel/Block/SystemMenuBlockTest.php
index 663bff6ba..7a6d6685e 100644
--- a/core/modules/system/tests/src/Kernel/Block/SystemMenuBlockTest.php
+++ b/core/modules/system/tests/src/Kernel/Block/SystemMenuBlockTest.php
@@ -162,13 +162,13 @@ class SystemMenuBlockTest extends KernelTestBase {
$dependencies = $block->calculateDependencies()->getDependencies();
$expected = [
'config' => [
- 'system.menu.' . $this->menu->id()
+ 'system.menu.' . $this->menu->id(),
],
'module' => [
- 'system'
+ 'system',
],
'theme' => [
- 'stark'
+ 'stark',
],
];
$this->assertIdentical($expected, $dependencies);
@@ -247,7 +247,7 @@ class SystemMenuBlockTest extends KernelTestBase {
'test.example2' => [
'test.example3' => [
'test.example4' => [],
- ]
+ ],
],
'test.example5' => [
'test.example7' => [],
diff --git a/core/modules/system/src/Tests/Common/AddFeedTest.php b/core/modules/system/tests/src/Kernel/Common/AddFeedTest.php
similarity index 94%
rename from core/modules/system/src/Tests/Common/AddFeedTest.php
rename to core/modules/system/tests/src/Kernel/Common/AddFeedTest.php
index 40ff215df..1060b30e4 100644
--- a/core/modules/system/src/Tests/Common/AddFeedTest.php
+++ b/core/modules/system/tests/src/Kernel/Common/AddFeedTest.php
@@ -1,16 +1,21 @@
drupalGet('common-test/type-link-active-class');
- $this->assertIdentical(0, strpos($this->getRawContent(), "\nassertIdentical('text/html; charset=UTF-8', $this->drupalGetHeader('Content-Type'));
- $this->assertTitle('Test active link class | Drupal');
- $this->assertCacheContext('url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT);
-
- $this->drupalGet('common-test/type-link-active-class', ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'json']]);
- $this->assertIdentical('application/json', $this->drupalGetHeader('Content-Type'));
- $json = Json::decode($this->getRawContent());
- $this->assertEqual(['content', 'title'], array_keys($json));
- $this->assertIdentical('Test active link class', $json['title']);
- $this->assertCacheContext('url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT);
- }
+ protected static $modules = ['common_test', 'system'];
/**
* Tests rendering form elements without passing through
@@ -152,7 +130,7 @@ class RenderWebTest extends WebTestBase {
'#title' => $this->randomMachineName(),
'#markup' => $this->randomMachineName(),
];
- $this->assertRenderedElement($element, '//details/div/div[contains(@class, :class) and contains(., :markup)]', [
+ $this->assertRenderedElement($element, '//details/div[contains(@class, :class) and contains(., :markup)]', [
':class' => 'js-form-type-item',
':markup' => $element['item']['#markup'],
]);
@@ -162,9 +140,7 @@ class RenderWebTest extends WebTestBase {
* Tests that elements are rendered properly.
*/
protected function assertRenderedElement(array $element, $xpath, array $xpath_args = []) {
- $original_element = $element;
- $this->setRawContent(drupal_render_root($element));
- $this->verbose('
' . $this->getRawContent());
+ $this->render($element);
// @see \Drupal\simpletest\WebTestBase::xpath()
$xpath = $this->buildXPathQuery($xpath, $xpath_args);
diff --git a/core/modules/system/tests/src/Kernel/Common/SystemListingTest.php b/core/modules/system/tests/src/Kernel/Common/SystemListingTest.php
index b0412b433..1a3f373f3 100644
--- a/core/modules/system/tests/src/Kernel/Common/SystemListingTest.php
+++ b/core/modules/system/tests/src/Kernel/Common/SystemListingTest.php
@@ -11,6 +11,7 @@ use Drupal\KernelTests\KernelTestBase;
* @group Common
*/
class SystemListingTest extends KernelTestBase {
+
/**
* Tests that files in different directories take precedence as expected.
*/
@@ -32,13 +33,13 @@ class SystemListingTest extends KernelTestBase {
foreach ($expected_directories as $module => $directories) {
foreach ($directories as $directory) {
$filename = "$directory/$module/$module.info.yml";
- $this->assertTrue(file_exists(\Drupal::root() . '/' . $filename), format_string('@filename exists.', ['@filename' => $filename]));
+ $this->assertTrue(file_exists($this->root . '/' . $filename), format_string('@filename exists.', ['@filename' => $filename]));
}
}
// Now scan the directories and check that the files take precedence as
// expected.
- $listing = new ExtensionDiscovery(\Drupal::root());
+ $listing = new ExtensionDiscovery($this->root);
$listing->setProfileDirectories(['core/profiles/testing']);
$files = $listing->scan('module');
foreach ($expected_directories as $module => $directories) {
@@ -55,7 +56,7 @@ class SystemListingTest extends KernelTestBase {
* Tests that directories matching file_scan_ignore_directories are ignored
*/
public function testFileScanIgnoreDirectory() {
- $listing = new ExtensionDiscovery(\Drupal::root(), FALSE);
+ $listing = new ExtensionDiscovery($this->root, FALSE);
$listing->setProfileDirectories(['core/profiles/testing']);
$files = $listing->scan('module');
$this->assertArrayHasKey('drupal_system_listing_compatible_test', $files);
@@ -67,7 +68,7 @@ class SystemListingTest extends KernelTestBase {
$reflected_property->setValue($reflected_class, []);
$this->setSetting('file_scan_ignore_directories', ['drupal_system_listing_compatible_test']);
- $listing = new ExtensionDiscovery(\Drupal::root(), FALSE);
+ $listing = new ExtensionDiscovery($this->root, FALSE);
$listing->setProfileDirectories(['core/profiles/testing']);
$files = $listing->scan('module');
$this->assertArrayNotHasKey('drupal_system_listing_compatible_test', $files);
diff --git a/core/modules/system/tests/src/Kernel/Entity/EntityReferenceSelectionReferenceableTest.php b/core/modules/system/tests/src/Kernel/Entity/EntityReferenceSelectionReferenceableTest.php
index f7fa5d87b..058f83bb9 100644
--- a/core/modules/system/tests/src/Kernel/Entity/EntityReferenceSelectionReferenceableTest.php
+++ b/core/modules/system/tests/src/Kernel/Entity/EntityReferenceSelectionReferenceableTest.php
@@ -3,7 +3,6 @@
namespace Drupal\Tests\system\Kernel\Entity;
use Drupal\Component\Utility\Html;
-use Drupal\Component\Utility\Unicode;
use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait;
use Drupal\field\Entity\FieldConfig;
use Drupal\node\Entity\NodeType;
@@ -35,7 +34,7 @@ class EntityReferenceSelectionReferenceableTest extends KernelTestBase {
/**
* The selection handler.
*
- * @var \Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface.
+ * @var \Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface
*/
protected $selectionHandler;
@@ -58,24 +57,24 @@ class EntityReferenceSelectionReferenceableTest extends KernelTestBase {
// Create a new node-type.
NodeType::create([
- 'type' => $node_type = Unicode::strtolower($this->randomMachineName()),
+ 'type' => $node_type = mb_strtolower($this->randomMachineName()),
'name' => $this->randomString(),
])->save();
// Create an entity reference field targeting 'entity_test_no_label'
// entities.
- $field_name = Unicode::strtolower($this->randomMachineName());
+ $field_name = mb_strtolower($this->randomMachineName());
$this->createEntityReferenceField('node', $node_type, $field_name, $this->randomString(), 'entity_test_no_label');
$field_config = FieldConfig::loadByName('node', $node_type, $field_name);
$this->selectionHandler = $this->container->get('plugin.manager.entity_reference_selection')->getSelectionHandler($field_config);
// Generate a bundle name to be used with 'entity_test_no_label'.
- $this->bundle = Unicode::strtolower($this->randomMachineName());
+ $this->bundle = mb_strtolower($this->randomMachineName());
// Create 6 entities to be referenced by the field.
foreach (static::$labels as $name) {
$storage->create([
- 'id' => Unicode::strtolower($this->randomMachineName()),
+ 'id' => mb_strtolower($this->randomMachineName()),
'name' => $name,
'type' => $this->bundle,
])->save();
diff --git a/core/modules/system/tests/src/Kernel/Extension/ModuleHandlerTest.php b/core/modules/system/tests/src/Kernel/Extension/ModuleHandlerTest.php
index d7c265035..708d517a3 100644
--- a/core/modules/system/tests/src/Kernel/Extension/ModuleHandlerTest.php
+++ b/core/modules/system/tests/src/Kernel/Extension/ModuleHandlerTest.php
@@ -105,7 +105,7 @@ class ModuleHandlerTest extends KernelTestBase {
// Color will depend on Config, which depends on a non-existing module Foo.
// Nothing should be installed.
\Drupal::state()->set('module_test.dependency', 'missing dependency');
- drupal_static_reset('system_rebuild_module_data');
+ \Drupal::service('extension.list.module')->reset();
try {
$result = $this->moduleInstaller()->install(['color']);
@@ -120,7 +120,7 @@ class ModuleHandlerTest extends KernelTestBase {
// Fix the missing dependency.
// Color module depends on Config. Config depends on Help module.
\Drupal::state()->set('module_test.dependency', 'dependency');
- drupal_static_reset('system_rebuild_module_data');
+ \Drupal::service('extension.list.module')->reset();
$result = $this->moduleInstaller()->install(['color']);
$this->assertTrue($result, 'ModuleInstaller::install() returns the correct value.');
@@ -152,7 +152,7 @@ class ModuleHandlerTest extends KernelTestBase {
// dependency on a specific version of Help module in its info file. Make
// sure that Drupal\Core\Extension\ModuleInstaller::install() still works.
\Drupal::state()->set('module_test.dependency', 'version dependency');
- drupal_static_reset('system_rebuild_module_data');
+ \Drupal::service('extension.list.module')->reset();
$result = $this->moduleInstaller()->install(['color']);
$this->assertTrue($result, 'ModuleInstaller::install() returns the correct value.');
@@ -170,21 +170,26 @@ class ModuleHandlerTest extends KernelTestBase {
/**
* Tests uninstalling a module that is a "dependency" of a profile.
+ *
+ * Note this test does not trigger the deprecation error because of static
+ * caching in \Drupal\Core\Extension\InfoParser::parse().
+ *
+ * @group legacy
*/
- public function testUninstallProfileDependency() {
- $profile = 'testing';
- $dependency = 'page_cache';
- $this->setSetting('install_profile', $profile);
+ public function testUninstallProfileDependencyBC() {
+ $profile = 'testing_install_profile_dependencies_bc';
+ $dependency = 'dblog';
+ $this->setInstallProfile($profile);
// Prime the drupal_get_filename() static cache with the location of the
- // minimal profile as it is not the currently active profile and we don't
+ // testing profile as it is not the currently active profile and we don't
// yet have any cached way to retrieve its location.
// @todo Remove as part of https://www.drupal.org/node/2186491
drupal_get_filename('profile', $profile, 'core/profiles/' . $profile . '/' . $profile . '.info.yml');
$this->enableModules(['module_test', $profile]);
- drupal_static_reset('system_rebuild_module_data');
- $data = system_rebuild_module_data();
- $this->assertTrue(isset($data[$profile]->requires[$dependency]));
+ $data = \Drupal::service('extension.list.module')->reset()->getList();
+ $this->assertFalse(isset($data[$profile]->requires[$dependency]));
+ $this->assertContains($dependency, $data[$profile]->info['install']);
$this->moduleInstaller()->install([$dependency]);
$this->assertTrue($this->moduleHandler()->moduleExists($dependency));
@@ -201,6 +206,76 @@ class ModuleHandlerTest extends KernelTestBase {
$this->assertFalse(in_array($profile, $uninstalled_modules), 'The installation profile is not in the list of uninstalled modules.');
}
+ /**
+ * Tests uninstalling a module installed by a profile.
+ */
+ public function testUninstallProfileDependency() {
+ $profile = 'testing_install_profile_dependencies';
+ $dependency = 'dblog';
+ $non_dependency = 'ban';
+ $this->setInstallProfile($profile);
+ // Prime the drupal_get_filename() static cache with the location of the
+ // testing_install_profile_dependencies profile as it is not the currently
+ // active profile and we don't yet have any cached way to retrieve its
+ // location.
+ // @todo Remove as part of https://www.drupal.org/node/2186491
+ drupal_get_filename('profile', $profile, 'core/profiles/' . $profile . '/' . $profile . '.info.yml');
+ $this->enableModules(['module_test', $profile]);
+
+ $data = \Drupal::service('extension.list.module')->reset()->getList();
+ $this->assertArrayHasKey($dependency, $data[$profile]->requires);
+ $this->assertArrayNotHasKey($non_dependency, $data[$profile]->requires);
+
+ $this->moduleInstaller()->install([$dependency, $non_dependency]);
+ $this->assertTrue($this->moduleHandler()->moduleExists($dependency));
+
+ // Uninstall the profile module that is not a dependent.
+ $result = $this->moduleInstaller()->uninstall([$non_dependency]);
+ $this->assertTrue($result, 'ModuleInstaller::uninstall() returns TRUE.');
+ $this->assertFalse($this->moduleHandler()->moduleExists($non_dependency));
+ $this->assertEquals(drupal_get_installed_schema_version($non_dependency), SCHEMA_UNINSTALLED, "$dependency module was uninstalled.");
+
+ // Verify that the installation profile itself was not uninstalled.
+ $uninstalled_modules = \Drupal::state()->get('module_test.uninstall_order') ?: [];
+ $this->assertContains($non_dependency, $uninstalled_modules, "$dependency module is in the list of uninstalled modules.");
+ $this->assertNotContains($profile, $uninstalled_modules, 'The installation profile is not in the list of uninstalled modules.');
+
+ // Try uninstalling the required module.
+ $this->setExpectedException(ModuleUninstallValidatorException::class, 'The following reasons prevent the modules from being uninstalled: The Testing install profile dependencies module is required');
+ $this->moduleInstaller()->uninstall([$dependency]);
+ }
+
+ /**
+ * Tests that a profile can supply only real dependencies
+ */
+ public function testProfileAllDependencies() {
+ $profile = 'testing_install_profile_all_dependencies';
+ $dependencies = ['dblog', 'ban'];
+
+ $this->setInstallProfile($profile);
+ // Prime the drupal_get_filename() static cache with the location of the
+ // testing_install_profile_dependencies profile as it is not the currently
+ // active profile and we don't yet have any cached way to retrieve its
+ // location.
+ // @todo Remove as part of https://www.drupal.org/node/2186491
+ drupal_get_filename('profile', $profile, 'core/profiles/' . $profile . '/' . $profile . '.info.yml');
+ $this->enableModules(['module_test', $profile]);
+
+ $data = \Drupal::service('extension.list.module')->reset()->getList();
+ foreach ($dependencies as $dependency) {
+ $this->assertArrayHasKey($dependency, $data[$profile]->requires);
+ }
+
+ $this->moduleInstaller()->install($dependencies);
+ foreach ($dependencies as $dependency) {
+ $this->assertTrue($this->moduleHandler()->moduleExists($dependency));
+ }
+
+ // Try uninstalling the dependencies.
+ $this->setExpectedException(ModuleUninstallValidatorException::class, 'The following reasons prevent the modules from being uninstalled: The Testing install profile all dependencies module is required');
+ $this->moduleInstaller()->uninstall($dependencies);
+ }
+
/**
* Tests uninstalling a module that has content.
*/
@@ -221,7 +296,7 @@ class ModuleHandlerTest extends KernelTestBase {
// entity_test will depend on help. This way help can not be uninstalled
// when there is test content preventing entity_test from being uninstalled.
\Drupal::state()->set('module_test.dependency', 'dependency');
- drupal_static_reset('system_rebuild_module_data');
+ \Drupal::service('extension.list.module')->reset();
// Create an entity so that the modules can not be disabled.
$entity = EntityTest::create(['name' => $this->randomString()]);
diff --git a/core/modules/system/tests/src/Kernel/Form/FormObjectTest.php b/core/modules/system/tests/src/Kernel/Form/FormObjectTest.php
new file mode 100644
index 000000000..36cbb50ca
--- /dev/null
+++ b/core/modules/system/tests/src/Kernel/Form/FormObjectTest.php
@@ -0,0 +1,35 @@
+form = new FormTestObject($this->container->get('config.factory'));
+ $this->values = [
+ 'bananas' => [
+ '#value' => $this->randomString(10),
+ '#config_name' => 'form_test.object',
+ '#config_key' => 'bananas',
+ ],
+ ];
+ }
+
+}
diff --git a/core/modules/system/tests/src/Kernel/Form/ProgrammaticTest.php b/core/modules/system/tests/src/Kernel/Form/ProgrammaticTest.php
index 03d22afe5..28d056cec 100644
--- a/core/modules/system/tests/src/Kernel/Form/ProgrammaticTest.php
+++ b/core/modules/system/tests/src/Kernel/Form/ProgrammaticTest.php
@@ -95,7 +95,7 @@ class ProgrammaticTest extends KernelTestBase {
public function testProgrammaticAccessBypass() {
$form_state = (new FormState())->setValues([
'textfield' => 'dummy value',
- 'field_restricted' => 'dummy value'
+ 'field_restricted' => 'dummy value',
]);
// Programmatically submit the form with a value for the restricted field.
diff --git a/core/modules/system/tests/src/Kernel/Plugin/migrate/source/ExtensionTest.php b/core/modules/system/tests/src/Kernel/Plugin/migrate/source/ExtensionTest.php
new file mode 100644
index 000000000..c6576019a
--- /dev/null
+++ b/core/modules/system/tests/src/Kernel/Plugin/migrate/source/ExtensionTest.php
@@ -0,0 +1,77 @@
+ 'sites/all/modules/i18n/i18nmenu/i18nmenu.module',
+ 'name' => 'i18nmenu',
+ 'type' => 'module',
+ 'owner' => '',
+ 'status' => '1',
+ 'throttle' => '0',
+ 'bootstrap' => '0',
+ 'schema_version' => '0',
+ 'weight' => '0',
+ 'info' => 'a:10:{s:4:"name";s:16:"Menu translation";s:11:"description";s:40:"Supports translatable custom menu items.";s:12:"dependencies";a:4:{i:0;s:4:"i18n";i:1;s:4:"menu";i:2;s:10:"i18nblocks";i:3;s:11:"i18nstrings";}s:7:"package";s:13:"Multilanguage";s:4:"core";s:3:"6.x";s:7:"version";s:8:"6.x-1.10";s:7:"project";s:4:"i18n";s:9:"datestamp";s:10:"1318336004";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";},',
+ ],
+ [
+ 'filename' => 'sites/all/modules/variable/variable.module ',
+ 'name' => 'variable',
+ 'type' => 'module',
+ 'owner' => '',
+ 'status' => '1',
+ 'throttle' => '0',
+ 'bootstrap' => '0',
+ 'schema_version' => '-1',
+ 'weight' => '0',
+ 'info' => 'a:9:{s:4:"name";s:12:"Variable API";s:11:"description";s:12:"Variable API";s:4:"core";s:3:"6.x";s:7:"version";s:14:"6.x-1.0-alpha1";s:7:"project";s:8:"variable";s:9:"datestamp";s:10:"1414059742";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}',
+ ],
+ ];
+
+ $info = unserialize('a:9:{s:4:"name";s:12:"Variable API";s:11:"description";s:12:"Variable API";s:4:"core";s:3:"6.x";s:7:"version";s:14:"6.x-1.0-alpha1";s:7:"project";s:8:"variable";s:9:"datestamp";s:10:"1414059742";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}');
+ $test[0]['expected_results'] = [
+ [
+ 'filename' => 'sites/all/modules/variable/variable.module ',
+ 'name' => 'variable',
+ 'type' => 'module',
+ 'owner' => '',
+ 'status' => '1',
+ 'throttle' => '0',
+ 'bootstrap' => '0',
+ 'schema_version' => '-1',
+ 'weight' => '0',
+ 'info' => $info,
+ ],
+ ];
+
+ $test[0]['expected_count'] = NULL;
+ $test[0]['configuration'] = [
+ 'name' => 'variable',
+ ];
+
+ return $test;
+ }
+
+}
diff --git a/core/modules/system/tests/src/Kernel/Render/ClassyTest.php b/core/modules/system/tests/src/Kernel/Render/ClassyTest.php
index 8dbb01e5d..8d1cb8548 100644
--- a/core/modules/system/tests/src/Kernel/Render/ClassyTest.php
+++ b/core/modules/system/tests/src/Kernel/Render/ClassyTest.php
@@ -37,8 +37,8 @@ class ClassyTest extends KernelTestBase {
* Test the classy theme.
*/
public function testClassyTheme() {
- drupal_set_message('An error occurred', 'error');
- drupal_set_message('But then something nice happened');
+ \Drupal::messenger()->addError('An error occurred');
+ \Drupal::messenger()->addStatus('But then something nice happened');
$messages = [
'#type' => 'status_messages',
];
diff --git a/core/modules/system/tests/src/Kernel/Scripts/DbCommandBaseTest.php b/core/modules/system/tests/src/Kernel/Scripts/DbCommandBaseTest.php
index cb7f5eb7c..3bee38d1a 100644
--- a/core/modules/system/tests/src/Kernel/Scripts/DbCommandBaseTest.php
+++ b/core/modules/system/tests/src/Kernel/Scripts/DbCommandBaseTest.php
@@ -35,7 +35,7 @@ class DbCommandBaseTest extends KernelTestBase {
Database::addConnectionInfo('magic_db', 'default', Database::getConnectionInfo('default')['default']);
$command_tester->execute([
- '--database' => 'magic_db'
+ '--database' => 'magic_db',
]);
$this->assertEquals('magic_db', $command->getDatabaseConnection($command_tester->getInput())->getKey(),
'Special db key is returned');
@@ -48,7 +48,7 @@ class DbCommandBaseTest extends KernelTestBase {
$command = new DbCommandBaseTester();
$command_tester = new CommandTester($command);
$command_tester->execute([
- '--database' => 'dne'
+ '--database' => 'dne',
]);
$this->setExpectedException(ConnectionNotDefinedException::class);
$command->getDatabaseConnection($command_tester->getInput());
@@ -58,18 +58,16 @@ class DbCommandBaseTest extends KernelTestBase {
* Test supplying database connection as a url.
*/
public function testSpecifyDbUrl() {
- $connection_info = Database::getConnectionInfo('default')['default'];
-
$command = new DbCommandBaseTester();
$command_tester = new CommandTester($command);
$command_tester->execute([
- '-db-url' => $connection_info['driver'] . '://' . $connection_info['username'] . ':' . $connection_info['password'] . '@' . $connection_info['host'] . '/' . $connection_info['database']
+ '-db-url' => Database::getConnectionInfoAsUrl(),
]);
$this->assertEquals('db-tools', $command->getDatabaseConnection($command_tester->getInput())->getKey());
Database::removeConnection('db-tools');
$command_tester->execute([
- '--database-url' => $connection_info['driver'] . '://' . $connection_info['username'] . ':' . $connection_info['password'] . '@' . $connection_info['host'] . '/' . $connection_info['database']
+ '--database-url' => Database::getConnectionInfoAsUrl(),
]);
$this->assertEquals('db-tools', $command->getDatabaseConnection($command_tester->getInput())->getKey());
}
@@ -91,9 +89,8 @@ class DbCommandBaseTest extends KernelTestBase {
]);
$this->assertEquals('extra', $command->getDatabaseConnection($command_tester->getInput())->tablePrefix());
- $connection_info = Database::getConnectionInfo('default')['default'];
$command_tester->execute([
- '-db-url' => $connection_info['driver'] . '://' . $connection_info['username'] . ':' . $connection_info['password'] . '@' . $connection_info['host'] . '/' . $connection_info['database'],
+ '-db-url' => Database::getConnectionInfoAsUrl(),
'--prefix' => 'extra2',
]);
$this->assertEquals('extra2', $command->getDatabaseConnection($command_tester->getInput())->tablePrefix());
diff --git a/core/modules/system/src/Tests/System/FloodTest.php b/core/modules/system/tests/src/Kernel/System/FloodTest.php
similarity index 86%
rename from core/modules/system/src/Tests/System/FloodTest.php
rename to core/modules/system/tests/src/Kernel/System/FloodTest.php
index bba303244..ba7bd5192 100644
--- a/core/modules/system/src/Tests/System/FloodTest.php
+++ b/core/modules/system/tests/src/Kernel/System/FloodTest.php
@@ -1,30 +1,22 @@
container->get('request_stack')->push($request);
- }
+ protected static $modules = ['system'];
/**
* Test flood control mechanism clean-up.
@@ -33,6 +25,7 @@ class FloodTest extends WebTestBase {
$threshold = 1;
$window_expired = -1;
$name = 'flood_test_cleanup';
+ $cron = $this->container->get('cron');
$flood = \Drupal::flood();
$this->assertTrue($flood->isAllowed($name, $threshold));
@@ -41,7 +34,7 @@ class FloodTest extends WebTestBase {
// Verify event is not allowed.
$this->assertFalse($flood->isAllowed($name, $threshold));
// Run cron and verify event is now allowed.
- $this->cronRun();
+ $cron->run();
$this->assertTrue($flood->isAllowed($name, $threshold));
// Register unexpired event.
@@ -49,7 +42,7 @@ class FloodTest extends WebTestBase {
// Verify event is not allowed.
$this->assertFalse($flood->isAllowed($name, $threshold));
// Run cron and verify event is still not allowed.
- $this->cronRun();
+ $cron->run();
$this->assertFalse($flood->isAllowed($name, $threshold));
}
diff --git a/core/modules/system/tests/src/Kernel/System/SystemGetInfoTest.php b/core/modules/system/tests/src/Kernel/System/SystemGetInfoTest.php
new file mode 100644
index 000000000..cd42fa487
--- /dev/null
+++ b/core/modules/system/tests/src/Kernel/System/SystemGetInfoTest.php
@@ -0,0 +1,44 @@
+assertSame('System', $system_module_info['name']);
+ $this->assertSame(['system' => $system_module_info], system_get_info('module'));
+
+ // The User module is not installed so system_get_info() should return
+ // an empty array.
+ $this->assertSame([], system_get_info('module', 'user'));
+
+ // Install the User module and check system_get_info() returns the correct
+ // information.
+ $this->container->get('module_installer')->install(['user']);
+ $user_module_info = system_get_info('module', 'user');
+ $this->assertSame('User', $user_module_info['name']);
+ $this->assertSame(['system' => $system_module_info, 'user' => $user_module_info], system_get_info('module'));
+
+ // Test theme info. There are no themes installed yet.
+ $this->assertSame([], system_get_info('theme', 'stable'));
+ $this->assertSame([], system_get_info('theme'));
+ $this->container->get('theme_installer')->install(['stable']);
+ $stable_theme_info = system_get_info('theme', 'stable');
+ $this->assertSame('Stable', $stable_theme_info['name']);
+ $this->assertSame(['stable' => $stable_theme_info], system_get_info('theme'));
+ }
+
+}
diff --git a/core/modules/system/src/Tests/Theme/FunctionsTest.php b/core/modules/system/tests/src/Kernel/Theme/FunctionsTest.php
similarity index 96%
rename from core/modules/system/src/Tests/Theme/FunctionsTest.php
rename to core/modules/system/tests/src/Kernel/Theme/FunctionsTest.php
index 825834859..9b93dff3f 100644
--- a/core/modules/system/src/Tests/Theme/FunctionsTest.php
+++ b/core/modules/system/tests/src/Kernel/Theme/FunctionsTest.php
@@ -1,27 +1,38 @@
container->get('theme_handler')->install(['classy']);
+ $this->config('system.theme')->set('default', 'classy')->save();
+ }
/**
* Tests item-list.html.twig.
@@ -194,7 +205,7 @@ class FunctionsTest extends WebTestBase {
'title' => 'Plain "text"',
],
'html text' => [
- 'title' => SafeMarkup::format('@text', ['@text' => 'potentially unsafe text that be escaped']),
+ 'title' => new FormattableMarkup('@text', ['@text' => 'potentially unsafe text that be escaped']),
],
'front page' => [
'title' => 'Front page',
@@ -209,7 +220,7 @@ class FunctionsTest extends WebTestBase {
'url' => Url::fromRoute('router_test.1'),
'query' => [
'key' => 'value',
- ]
+ ],
],
];
@@ -315,7 +326,7 @@ class FunctionsTest extends WebTestBase {
'title' => 'Plain "text"',
],
[
- 'title' => SafeMarkup::format('@text', ['@text' => 'potentially unsafe text that be escaped']),
+ 'title' => new FormattableMarkup('@text', ['@text' => 'potentially unsafe text that be escaped']),
],
[
'title' => 'Front page',
@@ -330,7 +341,7 @@ class FunctionsTest extends WebTestBase {
'url' => Url::fromRoute('router_test.1'),
'query' => [
'key' => 'value',
- ]
+ ],
],
];
diff --git a/core/modules/system/src/Tests/Theme/ThemeTest.php b/core/modules/system/tests/src/Kernel/Theme/ThemeTest.php
similarity index 50%
rename from core/modules/system/src/Tests/Theme/ThemeTest.php
rename to core/modules/system/tests/src/Kernel/Theme/ThemeTest.php
index 64f330e94..b9f09ec03 100644
--- a/core/modules/system/src/Tests/Theme/ThemeTest.php
+++ b/core/modules/system/tests/src/Kernel/Theme/ThemeTest.php
@@ -1,29 +1,26 @@
install(['test_theme']);
@@ -103,105 +100,6 @@ class ThemeTest extends WebTestBase {
$this->assertEqual($suggestions, $result, 'Found expected page suggestions for paths containing hyphens.');
}
- /**
- * Ensures preprocess functions run even for suggestion implementations.
- *
- * The theme hook used by this test has its base preprocess function in a
- * separate file, so this test also ensures that that file is correctly loaded
- * when needed.
- */
- public function testPreprocessForSuggestions() {
- // Test with both an unprimed and primed theme registry.
- drupal_theme_rebuild();
- for ($i = 0; $i < 2; $i++) {
- $this->drupalGet('theme-test/suggestion');
- $this->assertText('Theme hook implementor=test_theme_theme_test__suggestion(). Foo=template_preprocess_theme_test', 'Theme hook suggestion ran with data available from a preprocess function for the base hook.');
- }
- }
-
- /**
- * Tests the priority of some theme negotiators.
- */
- public function testNegotiatorPriorities() {
- $this->drupalGet('theme-test/priority');
-
- // Ensure that the custom theme negotiator was not able to set the theme.
-
- $this->assertNoText('Theme hook implementor=test_theme_theme_test__suggestion(). Foo=template_preprocess_theme_test', 'Theme hook suggestion ran with data available from a preprocess function for the base hook.');
- }
-
- /**
- * Ensures that non-HTML requests never initialize themes.
- */
- public function testThemeOnNonHtmlRequest() {
- $this->drupalGet('theme-test/non-html');
- $json = Json::decode($this->getRawContent());
- $this->assertFalse($json['theme_initialized']);
- }
-
- /**
- * Ensure page-front template suggestion is added when on front page.
- */
- public function testFrontPageThemeSuggestion() {
- // Set the current route to user.login because theme_get_suggestions() will
- // query it to see if we are on the front page.
- $request = Request::create('/user/login');
- $request->attributes->set(RouteObjectInterface::ROUTE_NAME, 'user.login');
- $request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, new Route('/user/login'));
- \Drupal::requestStack()->push($request);
- $this->config('system.site')->set('page.front', '/user/login')->save();
- $suggestions = theme_get_suggestions(['user', 'login'], 'page');
- // Set it back to not annoy the batch runner.
- \Drupal::requestStack()->pop();
- $this->assertTrue(in_array('page__front', $suggestions), 'Front page template was suggested.');
- }
-
- /**
- * Ensures a theme's .info.yml file is able to override a module CSS file from being added to the page.
- *
- * @see test_theme.info.yml
- */
- public function testCSSOverride() {
- // Reuse the same page as in testPreprocessForSuggestions(). We're testing
- // what is output to the HTML HEAD based on what is in a theme's .info.yml
- // file, so it doesn't matter what page we get, as long as it is themed with
- // the test theme. First we test with CSS aggregation disabled.
- $config = $this->config('system.performance');
- $config->set('css.preprocess', 0);
- $config->save();
- $this->drupalGet('theme-test/suggestion');
- $this->assertNoText('js.module.css', 'The theme\'s .info.yml file is able to override a module CSS file from being added to the page.');
-
- // Also test with aggregation enabled, simply ensuring no PHP errors are
- // triggered during drupal_build_css_cache() when a source file doesn't
- // exist. Then allow remaining tests to continue with aggregation disabled
- // by default.
- $config->set('css.preprocess', 1);
- $config->save();
- $this->drupalGet('theme-test/suggestion');
- $config->set('css.preprocess', 0);
- $config->save();
- }
-
- /**
- * Ensures a themes template is overridable based on the 'template' filename.
- */
- public function testTemplateOverride() {
- $this->config('system.theme')
- ->set('default', 'test_theme')
- ->save();
- $this->drupalGet('theme-test/template-test');
- $this->assertText('Success: Template overridden.', 'Template overridden by defined \'template\' filename.');
- }
-
- /**
- * Ensures a theme template can override a theme function.
- */
- public function testFunctionOverride() {
- $this->drupalGet('theme-test/function-template-overridden');
- $this->assertText('Success: Template overrides theme function.', 'Theme function overridden by test_theme template.');
- }
-
/**
* Test the listInfo() function.
*/
@@ -264,57 +162,4 @@ class ThemeTest extends WebTestBase {
$this->assertEqual($templates['node__1']['template'], 'node--1', 'Template node--1.tpl.twig was found in test_theme.');
}
- /**
- * Tests that the page variable is not prematurely flattened.
- *
- * Some modules check the page array in template_preprocess_html(), so we
- * ensure that it has not been rendered prematurely.
- */
- public function testPreprocessHtml() {
- $this->drupalGet('');
- $attributes = $this->xpath('/html/body[@theme_test_page_variable="Page variable is an array."]');
- $this->assertTrue(count($attributes) == 1, 'In template_preprocess_html(), the page variable is still an array (not rendered yet).');
- $this->assertText('theme test page bottom markup', 'Modules are able to set the page bottom region.');
- }
-
- /**
- * Tests that region attributes can be manipulated via preprocess functions.
- */
- public function testRegionClass() {
- \Drupal::service('module_installer')->install(['block', 'theme_region_test']);
-
- // Place a block.
- $this->drupalPlaceBlock('system_main_block');
- $this->drupalGet('');
- $elements = $this->cssSelect(".region-sidebar-first.new_class");
- $this->assertEqual(count($elements), 1, 'New class found.');
- }
-
- /**
- * Ensures suggestion preprocess functions run for default implementations.
- *
- * The theme hook used by this test has its base preprocess function in a
- * separate file, so this test also ensures that that file is correctly loaded
- * when needed.
- */
- public function testSuggestionPreprocessForDefaults() {
- $this->config('system.theme')->set('default', 'test_theme')->save();
- // Test with both an unprimed and primed theme registry.
- drupal_theme_rebuild();
- for ($i = 0; $i < 2; $i++) {
- $this->drupalGet('theme-test/preprocess-suggestions');
- $items = $this->cssSelect('.suggestion');
- $expected_values = [
- 'Suggestion',
- 'Kitten',
- 'Monkey',
- 'Kitten',
- 'Flamingo',
- ];
- foreach ($expected_values as $key => $value) {
- $this->assertEqual((string) $value, $items[$key]);
- }
- }
- }
-
}
diff --git a/core/modules/system/src/Tests/Theme/TwigFilterTest.php b/core/modules/system/tests/src/Kernel/Theme/TwigFilterTest.php
similarity index 97%
rename from core/modules/system/src/Tests/Theme/TwigFilterTest.php
rename to core/modules/system/tests/src/Kernel/Theme/TwigFilterTest.php
index 653e4fa96..c1c26dd03 100644
--- a/core/modules/system/src/Tests/Theme/TwigFilterTest.php
+++ b/core/modules/system/tests/src/Kernel/Theme/TwigFilterTest.php
@@ -1,15 +1,15 @@
'menu__mock',
'#items' => [
// To be filled when generating test cases, using $get_built_element().
- ]
+ ],
];
$get_built_element = function (MenuLinkTreeElement $element) {
@@ -248,10 +248,10 @@ class MenuLinkTreeTest extends UnitTestCase {
new MenuLinkTreeElement($multi_level_root_a, TRUE, 0, FALSE, [
new MenuLinkTreeElement($multi_level_parent_c, TRUE, 0, FALSE, [
new MenuLinkTreeElement($links[0], FALSE, 0, FALSE, []),
- ])
+ ]),
]),
new MenuLinkTreeElement($multi_level_root_b, TRUE, 0, FALSE, [
- new MenuLinkTreeElement($links[1], FALSE, 1, FALSE, [])
+ new MenuLinkTreeElement($links[1], FALSE, 1, FALSE, []),
]),
];
$tree[0]->subtree[0]->subtree[0]->access = $access;
diff --git a/core/modules/system/tests/themes/test_theme/templates/views-view--test-preprocess--display-2.html.twig b/core/modules/system/tests/themes/test_theme/templates/views-view--test-preprocess--display-2.html.twig
new file mode 100644
index 000000000..629141033
--- /dev/null
+++ b/core/modules/system/tests/themes/test_theme/templates/views-view--test-preprocess--display-2.html.twig
@@ -0,0 +1,2 @@
+css_class: {{ css_class }}
+attributes: {{- attributes -}}
diff --git a/core/modules/system/tests/themes/test_theme/test_theme.info.yml b/core/modules/system/tests/themes/test_theme/test_theme.info.yml
index 1c9745f1c..b73a45d7c 100644
--- a/core/modules/system/tests/themes/test_theme/test_theme.info.yml
+++ b/core/modules/system/tests/themes/test_theme/test_theme.info.yml
@@ -14,6 +14,7 @@ description: 'Theme for testing the theme system'
version: VERSION
base theme: classy
core: 8.x
+logo: images/logo2.svg
stylesheets-remove:
- '@system/css/js.module.css'
libraries:
diff --git a/core/modules/system/tests/themes/test_theme/test_theme.theme b/core/modules/system/tests/themes/test_theme/test_theme.theme
index aa399b66c..423390d22 100644
--- a/core/modules/system/tests/themes/test_theme/test_theme.theme
+++ b/core/modules/system/tests/themes/test_theme/test_theme.theme
@@ -53,7 +53,7 @@ function test_theme_theme_test_alter_alter(&$data) {
* Implements hook_theme_suggestions_alter().
*/
function test_theme_theme_suggestions_alter(array &$suggestions, array $variables, $hook) {
- drupal_set_message(__FUNCTION__ . '() executed.');
+ \Drupal::messenger()->addStatus(__FUNCTION__ . '() executed.');
// Theme alter hooks run after module alter hooks, so add this theme
// suggestion to the beginning of the array so that the suggestion added by
// the theme_suggestions_test module can be picked up when that module is
@@ -67,7 +67,7 @@ function test_theme_theme_suggestions_alter(array &$suggestions, array $variable
* Implements hook_theme_suggestions_HOOK_alter().
*/
function test_theme_theme_suggestions_theme_test_suggestions_alter(array &$suggestions, array $variables) {
- drupal_set_message(__FUNCTION__ . '() executed.');
+ \Drupal::messenger()->addStatus(__FUNCTION__ . '() executed.');
// Theme alter hooks run after module alter hooks, so add this theme
// suggestion to the beginning of the array so that the suggestion added by
// the theme_suggestions_test module can be picked up when that module is
diff --git a/core/modules/system/tests/themes/test_theme_libraries_empty/test_theme_libraries_empty.info.yml b/core/modules/system/tests/themes/test_theme_libraries_empty/test_theme_libraries_empty.info.yml
index bfe8a93f2..d3853b14e 100644
--- a/core/modules/system/tests/themes/test_theme_libraries_empty/test_theme_libraries_empty.info.yml
+++ b/core/modules/system/tests/themes/test_theme_libraries_empty/test_theme_libraries_empty.info.yml
@@ -5,4 +5,3 @@ version: VERSION
base theme: classy
core: 8.x
libraries:
-
diff --git a/core/modules/system/tests/themes/test_theme_settings_features/config/schema/test_theme_settings_features.schema.yml b/core/modules/system/tests/themes/test_theme_settings_features/config/schema/test_theme_settings_features.schema.yml
new file mode 100644
index 000000000..4d07483e0
--- /dev/null
+++ b/core/modules/system/tests/themes/test_theme_settings_features/config/schema/test_theme_settings_features.schema.yml
@@ -0,0 +1,3 @@
+test_theme_settings_features.settings:
+ type: theme_settings
+ label: 'Test theme settings'
diff --git a/core/modules/system/tests/themes/test_theme_settings_features/test_theme_settings_features.info.yml b/core/modules/system/tests/themes/test_theme_settings_features/test_theme_settings_features.info.yml
new file mode 100644
index 000000000..73ed7e5a4
--- /dev/null
+++ b/core/modules/system/tests/themes/test_theme_settings_features/test_theme_settings_features.info.yml
@@ -0,0 +1,8 @@
+name: Test features
+type: theme
+core: 8.x
+description: 'Test theme to test theme settings with limited features.'
+features:
+ - node_user_picture
+ - comment_user_picture
+ - comment_user_verification
diff --git a/core/modules/taxonomy/migrations/d6_term_node_revision.yml b/core/modules/taxonomy/migrations/d6_term_node_revision.yml
index 2edf86054..9487984f9 100644
--- a/core/modules/taxonomy/migrations/d6_term_node_revision.yml
+++ b/core/modules/taxonomy/migrations/d6_term_node_revision.yml
@@ -11,7 +11,7 @@ process:
vid:
-
plugin: migration_lookup
- migration: d6_node
+ migration: d6_node_revision
source: vid
-
plugin: skip_on_empty
diff --git a/core/modules/taxonomy/src/Entity/Term.php b/core/modules/taxonomy/src/Entity/Term.php
index b8eac0acc..8fc3cedb0 100644
--- a/core/modules/taxonomy/src/Entity/Term.php
+++ b/core/modules/taxonomy/src/Entity/Term.php
@@ -4,10 +4,12 @@ namespace Drupal\taxonomy\Entity;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityChangedTrait;
+use Drupal\Core\Entity\EntityPublishedTrait;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\taxonomy\TermInterface;
+use Drupal\user\StatusItem;
/**
* Defines the taxonomy term entity.
@@ -15,6 +17,13 @@ use Drupal\taxonomy\TermInterface;
* @ContentEntityType(
* id = "taxonomy_term",
* label = @Translation("Taxonomy term"),
+ * label_collection = @Translation("Taxonomy terms"),
+ * label_singular = @Translation("taxonomy term"),
+ * label_plural = @Translation("taxonomy terms"),
+ * label_count = @PluralTranslation(
+ * singular = "@count taxonomy term",
+ * plural = "@count taxonomy terms",
+ * ),
* bundle_label = @Translation("Vocabulary"),
* handlers = {
* "storage" = "Drupal\taxonomy\TermStorage",
@@ -38,7 +47,8 @@ use Drupal\taxonomy\TermInterface;
* "bundle" = "vid",
* "label" = "name",
* "langcode" = "langcode",
- * "uuid" = "uuid"
+ * "uuid" = "uuid",
+ * "published" = "status",
* },
* bundle_entity_type = "taxonomy_vocabulary",
* field_ui_base_route = "entity.taxonomy_vocabulary.overview_form",
@@ -55,6 +65,7 @@ use Drupal\taxonomy\TermInterface;
class Term extends ContentEntityBase implements TermInterface {
use EntityChangedTrait;
+ use EntityPublishedTrait;
/**
* {@inheritdoc}
@@ -64,22 +75,28 @@ class Term extends ContentEntityBase implements TermInterface {
// See if any of the term's children are about to be become orphans.
$orphans = [];
- foreach (array_keys($entities) as $tid) {
- if ($children = $storage->loadChildren($tid)) {
+ /** @var \Drupal\taxonomy\TermInterface $term */
+ foreach ($entities as $tid => $term) {
+ if ($children = $storage->getChildren($term)) {
+ /** @var \Drupal\taxonomy\TermInterface $child */
foreach ($children as $child) {
+ $parent = $child->get('parent');
+ // Update child parents item list.
+ $parent->filter(function ($item) use ($tid) {
+ return $item->target_id != $tid;
+ });
+
// If the term has multiple parents, we don't delete it.
- $parents = $storage->loadParents($child->id());
- if (empty($parents)) {
+ if ($parent->count()) {
+ $child->save();
+ }
+ else {
$orphans[] = $child;
}
}
}
}
- // Delete term hierarchy information after looking up orphans but before
- // deleting them so that their children/parent information is consistent.
- $storage->deleteTermHierarchy(array_keys($entities));
-
if (!empty($orphans)) {
$storage->delete($orphans);
}
@@ -88,14 +105,11 @@ class Term extends ContentEntityBase implements TermInterface {
/**
* {@inheritdoc}
*/
- public function postSave(EntityStorageInterface $storage, $update = TRUE) {
- parent::postSave($storage, $update);
-
- // Only change the parents if a value is set, keep the existing values if
- // not.
- if (isset($this->parent->target_id)) {
- $storage->deleteTermHierarchy([$this->id()]);
- $storage->updateTermHierarchy($this);
+ public function preSave(EntityStorageInterface $storage) {
+ parent::preSave($storage);
+ // Terms with no parents are mandatory children of .
+ if (!$this->get('parent')->count()) {
+ $this->parent->target_id = 0;
}
}
@@ -106,6 +120,12 @@ class Term extends ContentEntityBase implements TermInterface {
/** @var \Drupal\Core\Field\BaseFieldDefinition[] $fields */
$fields = parent::baseFieldDefinitions($entity_type);
+ // Add the published field.
+ $fields += static::publishedBaseFieldDefinitions($entity_type);
+ // @todo Remove the usage of StatusItem in
+ // https://www.drupal.org/project/drupal/issues/2936864.
+ $fields['status']->getItemDefinition()->setClass(StatusItem::class);
+
$fields['tid']->setLabel(t('Term ID'))
->setDescription(t('The term ID.'));
@@ -156,8 +176,7 @@ class Term extends ContentEntityBase implements TermInterface {
->setLabel(t('Term Parents'))
->setDescription(t('The parents of this term.'))
->setSetting('target_type', 'taxonomy_term')
- ->setCardinality(BaseFieldDefinition::CARDINALITY_UNLIMITED)
- ->setCustomStorage(TRUE);
+ ->setCardinality(BaseFieldDefinition::CARDINALITY_UNLIMITED);
$fields['changed'] = BaseFieldDefinition::create('changed')
->setLabel(t('Changed'))
@@ -167,6 +186,16 @@ class Term extends ContentEntityBase implements TermInterface {
return $fields;
}
+ /**
+ * {@inheritdoc}
+ */
+ public static function bundleFieldDefinitions(EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) {
+ // Only terms in the same bundle can be a parent.
+ $fields['parent'] = clone $base_field_definitions['parent'];
+ $fields['parent']->setSetting('handler_settings', ['target_bundles' => [$bundle => $bundle]]);
+ return $fields;
+ }
+
/**
* {@inheritdoc}
*/
@@ -235,17 +264,4 @@ class Term extends ContentEntityBase implements TermInterface {
return $this->bundle();
}
- /**
- * {@inheritdoc}
- */
- protected function getFieldsToSkipFromTranslationChangesCheck() {
- // @todo the current implementation of the parent field makes it impossible
- // for ::hasTranslationChanges() to correctly check the field for changes,
- // so it is currently skipped from the comparision and has to be fixed by
- // https://www.drupal.org/node/2843060.
- $fields = parent::getFieldsToSkipFromTranslationChangesCheck();
- $fields[] = 'parent';
- return $fields;
- }
-
}
diff --git a/core/modules/taxonomy/src/Form/OverviewTerms.php b/core/modules/taxonomy/src/Form/OverviewTerms.php
index df80a4703..f9e0d5959 100644
--- a/core/modules/taxonomy/src/Form/OverviewTerms.php
+++ b/core/modules/taxonomy/src/Form/OverviewTerms.php
@@ -241,6 +241,11 @@ class OverviewTerms extends FormBase {
$form['terms'] = [
'#type' => 'table',
'#empty' => $empty,
+ '#header' => [
+ 'term' => $this->t('Name'),
+ 'operations' => $this->t('Operations'),
+ 'weight' => $this->t('Weight'),
+ ],
'#attributes' => [
'id' => 'taxonomy',
],
@@ -251,6 +256,11 @@ class OverviewTerms extends FormBase {
// all terms.
$change_weight_access = AccessResult::allowed();
foreach ($current_page as $key => $term) {
+ $form['terms'][$key] = [
+ 'term' => [],
+ 'operations' => [],
+ 'weight' => [],
+ ];
/** @var $term \Drupal\Core\Entity\EntityInterface */
$term = $this->entityManager->getTranslationFromContext($term);
$form['terms'][$key]['#term'] = $term;
@@ -344,11 +354,8 @@ class OverviewTerms extends FormBase {
$row_position++;
}
- $form['terms']['#header'] = [$this->t('Name')];
-
$this->renderer->addCacheableDependency($form['terms'], $change_weight_access);
if ($change_weight_access->isAllowed()) {
- $form['terms']['#header'][] = $this->t('Weight');
if ($parent_fields) {
$form['terms']['#tabledrag'][] = [
'action' => 'match',
@@ -377,8 +384,6 @@ class OverviewTerms extends FormBase {
];
}
- $form['terms']['#header'][] = $this->t('Operations');
-
if (($taxonomy_vocabulary->getHierarchy() !== VocabularyInterface::HIERARCHY_MULTIPLE && count($tree) > 1) && $change_weight_access->isAllowed()) {
$form['actions'] = ['#type' => 'actions', '#tree' => FALSE];
$form['actions']['submit'] = [
@@ -492,7 +497,7 @@ class OverviewTerms extends FormBase {
$vocabulary->setHierarchy($hierarchy);
$vocabulary->save();
}
- drupal_set_message($this->t('The configuration options have been saved.'));
+ $this->messenger()->addStatus($this->t('The configuration options have been saved.'));
}
/**
diff --git a/core/modules/taxonomy/src/Form/VocabularyResetForm.php b/core/modules/taxonomy/src/Form/VocabularyResetForm.php
index 439d994cf..c7ea78f7c 100644
--- a/core/modules/taxonomy/src/Form/VocabularyResetForm.php
+++ b/core/modules/taxonomy/src/Form/VocabularyResetForm.php
@@ -82,7 +82,7 @@ class VocabularyResetForm extends EntityConfirmFormBase {
parent::submitForm($form, $form_state);
$this->termStorage->resetWeights($this->entity->id());
- drupal_set_message($this->t('Reset vocabulary %name to alphabetical order.', ['%name' => $this->entity->label()]));
+ $this->messenger()->addStatus($this->t('Reset vocabulary %name to alphabetical order.', ['%name' => $this->entity->label()]));
$this->logger('taxonomy')->notice('Reset vocabulary %name to alphabetical order.', ['%name' => $this->entity->label()]);
$form_state->setRedirectUrl($this->getCancelUrl());
}
diff --git a/core/modules/taxonomy/src/Plugin/EntityReferenceSelection/TermSelection.php b/core/modules/taxonomy/src/Plugin/EntityReferenceSelection/TermSelection.php
index 9d97a55f1..059225029 100644
--- a/core/modules/taxonomy/src/Plugin/EntityReferenceSelection/TermSelection.php
+++ b/core/modules/taxonomy/src/Plugin/EntityReferenceSelection/TermSelection.php
@@ -28,7 +28,7 @@ class TermSelection extends DefaultSelection {
'sort' => [
'field' => 'name',
'direction' => 'asc',
- ]
+ ],
] + parent::defaultConfiguration();
}
@@ -59,10 +59,17 @@ class TermSelection extends DefaultSelection {
$bundles = $this->entityManager->getBundleInfo('taxonomy_term');
$bundle_names = $this->getConfiguration()['target_bundles'] ?: array_keys($bundles);
+ $has_admin_access = $this->currentUser->hasPermission('administer taxonomy');
+ $unpublished_terms = [];
foreach ($bundle_names as $bundle) {
if ($vocabulary = Vocabulary::load($bundle)) {
+ /** @var \Drupal\taxonomy\TermInterface[] $terms */
if ($terms = $this->entityManager->getStorage('taxonomy_term')->loadTree($vocabulary->id(), 0, NULL, TRUE)) {
foreach ($terms as $term) {
+ if (!$has_admin_access && (!$term->isPublished() || in_array($term->parent->target_id, $unpublished_terms))) {
+ $unpublished_terms[] = $term->id();
+ continue;
+ }
$options[$vocabulary->id()][$term->id()] = str_repeat('-', $term->depth) . Html::escape($this->entityManager->getTranslationFromContext($term)->label());
}
}
@@ -72,4 +79,63 @@ class TermSelection extends DefaultSelection {
return $options;
}
+ /**
+ * {@inheritdoc}
+ */
+ public function countReferenceableEntities($match = NULL, $match_operator = 'CONTAINS') {
+ if ($match) {
+ return parent::countReferenceableEntities($match, $match_operator);
+ }
+
+ $total = 0;
+ $referenceable_entities = $this->getReferenceableEntities($match, $match_operator, 0);
+ foreach ($referenceable_entities as $bundle => $entities) {
+ $total += count($entities);
+ }
+ return $total;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS') {
+ $query = parent::buildEntityQuery($match, $match_operator);
+
+ // Adding the 'taxonomy_term_access' tag is sadly insufficient for terms:
+ // core requires us to also know about the concept of 'published' and
+ // 'unpublished'.
+ if (!$this->currentUser->hasPermission('administer taxonomy')) {
+ $query->condition('status', 1);
+ }
+ return $query;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function createNewEntity($entity_type_id, $bundle, $label, $uid) {
+ $term = parent::createNewEntity($entity_type_id, $bundle, $label, $uid);
+
+ // In order to create a referenceable term, it needs to published.
+ /** @var \Drupal\taxonomy\TermInterface $term */
+ $term->setPublished();
+
+ return $term;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function validateReferenceableNewEntities(array $entities) {
+ $entities = parent::validateReferenceableNewEntities($entities);
+ // Mirror the conditions checked in buildEntityQuery().
+ if (!$this->currentUser->hasPermission('administer taxonomy')) {
+ $entities = array_filter($entities, function ($term) {
+ /** @var \Drupal\taxonomy\TermInterface $term */
+ return $term->isPublished();
+ });
+ }
+ return $entities;
+ }
+
}
diff --git a/core/modules/taxonomy/src/Plugin/migrate/D7TaxonomyTermDeriver.php b/core/modules/taxonomy/src/Plugin/migrate/D7TaxonomyTermDeriver.php
index 561a9afe8..566cf3b3d 100644
--- a/core/modules/taxonomy/src/Plugin/migrate/D7TaxonomyTermDeriver.php
+++ b/core/modules/taxonomy/src/Plugin/migrate/D7TaxonomyTermDeriver.php
@@ -137,7 +137,7 @@ class D7TaxonomyTermDeriver extends DeriverBase implements ContainerDeriverInter
$this->fieldPluginCache[$field_type] = $this->fieldPluginManager->createInstance($plugin_id, ['core' => 7], $migration);
}
$this->fieldPluginCache[$field_type]
- ->processFieldValues($migration, $field_name, $info);
+ ->defineValueProcessPipeline($migration, $field_name, $info);
}
catch (PluginNotFoundException $ex) {
try {
diff --git a/core/modules/taxonomy/src/Plugin/migrate/field/TaxonomyTermReference.php b/core/modules/taxonomy/src/Plugin/migrate/field/TaxonomyTermReference.php
index bea9a3f95..1fa80d2cd 100644
--- a/core/modules/taxonomy/src/Plugin/migrate/field/TaxonomyTermReference.php
+++ b/core/modules/taxonomy/src/Plugin/migrate/field/TaxonomyTermReference.php
@@ -30,7 +30,7 @@ class TaxonomyTermReference extends FieldPluginBase {
/**
* {@inheritdoc}
*/
- public function processFieldValues(MigrationInterface $migration, $field_name, $data) {
+ public function defineValueProcessPipeline(MigrationInterface $migration, $field_name, $data) {
$process = [
'plugin' => 'sub_process',
'source' => $field_name,
diff --git a/core/modules/taxonomy/src/Plugin/migrate/source/d7/Term.php b/core/modules/taxonomy/src/Plugin/migrate/source/d7/Term.php
index 93b0fc4aa..658f4422b 100644
--- a/core/modules/taxonomy/src/Plugin/migrate/source/d7/Term.php
+++ b/core/modules/taxonomy/src/Plugin/migrate/source/d7/Term.php
@@ -75,6 +75,20 @@ class Term extends FieldableEntity {
$current_tid = $row->getSourceProperty('tid');
$row->setSourceProperty('is_container', in_array($current_tid, $forum_container_tids));
+ // If the term name or term description were replaced by real fields using
+ // the Drupal 7 Title module, use the fields value instead of the term name
+ // or term description.
+ if ($this->moduleExists('title')) {
+ $name_field = $row->getSourceProperty('name_field');
+ if (isset($name_field[0]['value'])) {
+ $row->setSourceProperty('name', $name_field[0]['value']);
+ }
+ $description_field = $row->getSourceProperty('description_field');
+ if (isset($description_field[0]['value'])) {
+ $row->setSourceProperty('description', $description_field[0]['value']);
+ }
+ }
+
return parent::prepareRow($row);
}
diff --git a/core/modules/taxonomy/src/Plugin/migrate/source/d7/Vocabulary.php b/core/modules/taxonomy/src/Plugin/migrate/source/d7/Vocabulary.php
index 185990185..82048a3c2 100644
--- a/core/modules/taxonomy/src/Plugin/migrate/source/d7/Vocabulary.php
+++ b/core/modules/taxonomy/src/Plugin/migrate/source/d7/Vocabulary.php
@@ -43,7 +43,7 @@ class Vocabulary extends DrupalSqlBase {
'hierarchy' => $this->t('The type of hierarchy allowed within the vocabulary. (0 = disabled, 1 = single, 2 = multiple)'),
'module' => $this->t('Module responsible for the vocabulary.'),
'weight' => $this->t('The weight of the vocabulary in relation to other vocabularies.'),
- 'machine_name' => $this->t('Unique machine name of the vocabulary.')
+ 'machine_name' => $this->t('Unique machine name of the vocabulary.'),
];
}
diff --git a/core/modules/taxonomy/src/Plugin/views/argument/IndexTidDepth.php b/core/modules/taxonomy/src/Plugin/views/argument/IndexTidDepth.php
index 02b599542..8eeb1f9b6 100644
--- a/core/modules/taxonomy/src/Plugin/views/argument/IndexTidDepth.php
+++ b/core/modules/taxonomy/src/Plugin/views/argument/IndexTidDepth.php
@@ -111,18 +111,19 @@ class IndexTidDepth extends ArgumentPluginBase implements ContainerFactoryPlugin
$last = "tn";
if ($this->options['depth'] > 0) {
- $subquery->leftJoin('taxonomy_term_hierarchy', 'th', "th.tid = tn.tid");
+ $subquery->leftJoin('taxonomy_term__parent', 'th', "th.entity_id = tn.tid");
$last = "th";
foreach (range(1, abs($this->options['depth'])) as $count) {
- $subquery->leftJoin('taxonomy_term_hierarchy', "th$count", "$last.parent = th$count.tid");
- $where->condition("th$count.tid", $tids, $operator);
+ $subquery->leftJoin('taxonomy_term__parent', "th$count", "$last.parent_target_id = th$count.entity_id");
+ $where->condition("th$count.entity_id", $tids, $operator);
$last = "th$count";
}
}
elseif ($this->options['depth'] < 0) {
foreach (range(1, abs($this->options['depth'])) as $count) {
- $subquery->leftJoin('taxonomy_term_hierarchy', "th$count", "$last.tid = th$count.parent");
- $where->condition("th$count.tid", $tids, $operator);
+ $field = $count == 1 ? 'tid' : 'entity_id';
+ $subquery->leftJoin('taxonomy_term__parent', "th$count", "$last.$field = th$count.parent_target_id");
+ $where->condition("th$count.entity_id", $tids, $operator);
$last = "th$count";
}
}
diff --git a/core/modules/taxonomy/src/Plugin/views/argument_default/Tid.php b/core/modules/taxonomy/src/Plugin/views/argument_default/Tid.php
index ee5a2602d..6d8545b0e 100644
--- a/core/modules/taxonomy/src/Plugin/views/argument_default/Tid.php
+++ b/core/modules/taxonomy/src/Plugin/views/argument_default/Tid.php
@@ -34,7 +34,7 @@ class Tid extends ArgumentDefaultPluginBase implements CacheableDependencyInterf
/**
* The vocabulary storage.
*
- * @var \Drupal\taxonomy\VocabularyStorageInterface.
+ * @var \Drupal\taxonomy\VocabularyStorageInterface
*/
protected $vocabularyStorage;
diff --git a/core/modules/taxonomy/src/Plugin/views/field/TaxonomyIndexTid.php b/core/modules/taxonomy/src/Plugin/views/field/TaxonomyIndexTid.php
index 367565c38..45f0095db 100644
--- a/core/modules/taxonomy/src/Plugin/views/field/TaxonomyIndexTid.php
+++ b/core/modules/taxonomy/src/Plugin/views/field/TaxonomyIndexTid.php
@@ -21,7 +21,7 @@ class TaxonomyIndexTid extends PrerenderList {
/**
* The vocabulary storage.
*
- * @var \Drupal\taxonomy\VocabularyStorageInterface.
+ * @var \Drupal\taxonomy\VocabularyStorageInterface
*/
protected $vocabularyStorage;
diff --git a/core/modules/taxonomy/src/Plugin/views/field/TermName.php b/core/modules/taxonomy/src/Plugin/views/field/TermName.php
index 0a777e9c1..eefc3e3d3 100644
--- a/core/modules/taxonomy/src/Plugin/views/field/TermName.php
+++ b/core/modules/taxonomy/src/Plugin/views/field/TermName.php
@@ -31,7 +31,6 @@ class TermName extends EntityField {
return $items;
}
-
/**
* {@inheritdoc}
*/
diff --git a/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTidDepth.php b/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTidDepth.php
index 802786e46..ebb5b51f2 100644
--- a/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTidDepth.php
+++ b/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTidDepth.php
@@ -77,18 +77,19 @@ class TaxonomyIndexTidDepth extends TaxonomyIndexTid {
$last = "tn";
if ($this->options['depth'] > 0) {
- $subquery->leftJoin('taxonomy_term_hierarchy', 'th', "th.tid = tn.tid");
+ $subquery->leftJoin('taxonomy_term__parent', 'th', "th.entity_id = tn.tid");
$last = "th";
foreach (range(1, abs($this->options['depth'])) as $count) {
- $subquery->leftJoin('taxonomy_term_hierarchy', "th$count", "$last.parent = th$count.tid");
- $where->condition("th$count.tid", $this->value, $operator);
+ $subquery->leftJoin('taxonomy_term__parent', "th$count", "$last.parent_target_id = th$count.entity_id");
+ $where->condition("th$count.entity_id", $this->value, $operator);
$last = "th$count";
}
}
elseif ($this->options['depth'] < 0) {
foreach (range(1, abs($this->options['depth'])) as $count) {
- $subquery->leftJoin('taxonomy_term_hierarchy', "th$count", "$last.tid = th$count.parent");
- $where->condition("th$count.tid", $this->value, $operator);
+ $field = $count == 1 ? 'tid' : 'entity_id';
+ $subquery->leftJoin('taxonomy_term__parent', "th$count", "$last.$field = th$count.parent_target_id");
+ $where->condition("th$count.entity_id", $this->value, $operator);
$last = "th$count";
}
}
diff --git a/core/modules/taxonomy/src/TermAccessControlHandler.php b/core/modules/taxonomy/src/TermAccessControlHandler.php
index 1d4846366..b25dca462 100644
--- a/core/modules/taxonomy/src/TermAccessControlHandler.php
+++ b/core/modules/taxonomy/src/TermAccessControlHandler.php
@@ -18,19 +18,37 @@ class TermAccessControlHandler extends EntityAccessControlHandler {
* {@inheritdoc}
*/
protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
+ if ($account->hasPermission('administer taxonomy')) {
+ return AccessResult::allowed()->cachePerPermissions();
+ }
+
switch ($operation) {
case 'view':
- return AccessResult::allowedIfHasPermission($account, 'access content');
+ $access_result = AccessResult::allowedIf($account->hasPermission('access content') && $entity->isPublished())
+ ->cachePerPermissions()
+ ->addCacheableDependency($entity);
+ if (!$access_result->isAllowed()) {
+ $access_result->setReason("The 'access content' permission is required and the taxonomy term must be published.");
+ }
+ return $access_result;
case 'update':
- return AccessResult::allowedIfHasPermissions($account, ["edit terms in {$entity->bundle()}", 'administer taxonomy'], 'OR');
+ if ($account->hasPermission("edit terms in {$entity->bundle()}")) {
+ return AccessResult::allowed()->cachePerPermissions();
+ }
+
+ return AccessResult::neutral()->setReason("The following permissions are required: 'edit terms in {$entity->bundle()}' OR 'administer taxonomy'.");
case 'delete':
- return AccessResult::allowedIfHasPermissions($account, ["delete terms in {$entity->bundle()}", 'administer taxonomy'], 'OR');
+ if ($account->hasPermission("delete terms in {$entity->bundle()}")) {
+ return AccessResult::allowed()->cachePerPermissions();
+ }
+
+ return AccessResult::neutral()->setReason("The following permissions are required: 'delete terms in {$entity->bundle()}' OR 'administer taxonomy'.");
default:
// No opinion.
- return AccessResult::neutral();
+ return AccessResult::neutral()->cachePerPermissions();
}
}
diff --git a/core/modules/taxonomy/src/TermForm.php b/core/modules/taxonomy/src/TermForm.php
index 6de48e0af..95909bd3b 100644
--- a/core/modules/taxonomy/src/TermForm.php
+++ b/core/modules/taxonomy/src/TermForm.php
@@ -132,11 +132,11 @@ class TermForm extends ContentEntityForm {
$view_link = $term->link($term->getName());
switch ($result) {
case SAVED_NEW:
- drupal_set_message($this->t('Created new term %term.', ['%term' => $view_link]));
+ $this->messenger()->addStatus($this->t('Created new term %term.', ['%term' => $view_link]));
$this->logger('taxonomy')->notice('Created new term %term.', ['%term' => $term->getName(), 'link' => $edit_link]);
break;
case SAVED_UPDATED:
- drupal_set_message($this->t('Updated term %term.', ['%term' => $view_link]));
+ $this->messenger()->addStatus($this->t('Updated term %term.', ['%term' => $view_link]));
$this->logger('taxonomy')->notice('Updated term %term.', ['%term' => $term->getName(), 'link' => $edit_link]);
break;
}
diff --git a/core/modules/taxonomy/src/TermInterface.php b/core/modules/taxonomy/src/TermInterface.php
index 3087a56ec..2dc26fb65 100644
--- a/core/modules/taxonomy/src/TermInterface.php
+++ b/core/modules/taxonomy/src/TermInterface.php
@@ -4,11 +4,12 @@ namespace Drupal\taxonomy;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityChangedInterface;
+use Drupal\Core\Entity\EntityPublishedInterface;
/**
* Provides an interface defining a taxonomy term entity.
*/
-interface TermInterface extends ContentEntityInterface, EntityChangedInterface {
+interface TermInterface extends ContentEntityInterface, EntityChangedInterface, EntityPublishedInterface {
/**
* Gets the term's description.
@@ -57,7 +58,7 @@ interface TermInterface extends ContentEntityInterface, EntityChangedInterface {
/**
* Sets the name of the term.
*
- * @param int $name
+ * @param string $name
* The term's name.
*
* @return $this
diff --git a/core/modules/taxonomy/src/TermStorage.php b/core/modules/taxonomy/src/TermStorage.php
index 0a00c13de..2d47722fa 100644
--- a/core/modules/taxonomy/src/TermStorage.php
+++ b/core/modules/taxonomy/src/TermStorage.php
@@ -2,35 +2,14 @@
namespace Drupal\taxonomy;
-use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
/**
* Defines a Controller class for taxonomy terms.
*/
class TermStorage extends SqlContentEntityStorage implements TermStorageInterface {
- /**
- * Array of loaded parents keyed by child term ID.
- *
- * @var array
- */
- protected $parents = [];
-
- /**
- * Array of all loaded term ancestry keyed by ancestor term ID.
- *
- * @var array
- */
- protected $parentsAll = [];
-
- /**
- * Array of child terms keyed by parent term ID.
- *
- * @var array
- */
- protected $children = [];
-
/**
* Array of term parents keyed by vocabulary ID and child term ID.
*
@@ -59,6 +38,14 @@ class TermStorage extends SqlContentEntityStorage implements TermStorageInterfac
*/
protected $trees = [];
+ /**
+ * Array of all loaded term ancestry keyed by ancestor term ID, keyed by term
+ * ID.
+ *
+ * @var \Drupal\taxonomy\TermInterface[][]
+ */
+ protected $ancestors;
+
/**
* {@inheritdoc}
*
@@ -80,9 +67,7 @@ class TermStorage extends SqlContentEntityStorage implements TermStorageInterfac
*/
public function resetCache(array $ids = NULL) {
drupal_static_reset('taxonomy_term_count_nodes');
- $this->parents = [];
- $this->parentsAll = [];
- $this->children = [];
+ $this->ancestors = [];
$this->treeChildren = [];
$this->treeParents = [];
$this->treeTerms = [];
@@ -93,100 +78,125 @@ class TermStorage extends SqlContentEntityStorage implements TermStorageInterfac
/**
* {@inheritdoc}
*/
- public function deleteTermHierarchy($tids) {
- $this->database->delete('taxonomy_term_hierarchy')
- ->condition('tid', $tids, 'IN')
- ->execute();
- }
+ public function deleteTermHierarchy($tids) {}
/**
* {@inheritdoc}
*/
- public function updateTermHierarchy(EntityInterface $term) {
- $query = $this->database->insert('taxonomy_term_hierarchy')
- ->fields(['tid', 'parent']);
-
- foreach ($term->parent as $parent) {
- $query->values([
- 'tid' => $term->id(),
- 'parent' => (int) $parent->target_id,
- ]);
- }
- $query->execute();
- }
+ public function updateTermHierarchy(EntityInterface $term) {}
/**
* {@inheritdoc}
*/
public function loadParents($tid) {
- if (!isset($this->parents[$tid])) {
- $parents = [];
- $query = $this->database->select('taxonomy_term_field_data', 't');
- $query->join('taxonomy_term_hierarchy', 'h', 'h.parent = t.tid');
- $query->addField('t', 'tid');
- $query->condition('h.tid', $tid);
- $query->condition('t.default_langcode', 1);
- $query->addTag('taxonomy_term_access');
- $query->orderBy('t.weight');
- $query->orderBy('t.name');
- if ($ids = $query->execute()->fetchCol()) {
- $parents = $this->loadMultiple($ids);
+ $terms = [];
+ /** @var \Drupal\taxonomy\TermInterface $term */
+ if ($tid && $term = $this->load($tid)) {
+ foreach ($this->getParents($term) as $id => $parent) {
+ // This method currently doesn't return the parent.
+ // @see https://www.drupal.org/node/2019905
+ if (!empty($id)) {
+ $terms[$id] = $parent;
+ }
}
- $this->parents[$tid] = $parents;
}
- return $this->parents[$tid];
+
+ return $terms;
+ }
+
+ /**
+ * Returns a list of parents of this term.
+ *
+ * @return \Drupal\taxonomy\TermInterface[]
+ * The parent taxonomy term entities keyed by term ID. If this term has a
+ * parent, that item is keyed with 0 and will have NULL as value.
+ *
+ * @internal
+ * @todo Refactor away when TreeInterface is introduced.
+ */
+ protected function getParents(TermInterface $term) {
+ $parents = $ids = [];
+ // Cannot use $this->get('parent')->referencedEntities() here because that
+ // strips out the '0' reference.
+ foreach ($term->get('parent') as $item) {
+ if ($item->target_id == 0) {
+ // The parent.
+ $parents[0] = NULL;
+ continue;
+ }
+ $ids[] = $item->target_id;
+ }
+
+ // @todo Better way to do this? AND handle the NULL/0 parent?
+ // Querying the terms again so that the same access checks are run when
+ // getParents() is called as in Drupal version prior to 8.3.
+ $loaded_parents = [];
+
+ if ($ids) {
+ $query = \Drupal::entityQuery('taxonomy_term')
+ ->condition('tid', $ids, 'IN');
+
+ $loaded_parents = static::loadMultiple($query->execute());
+ }
+
+ return $parents + $loaded_parents;
}
/**
* {@inheritdoc}
*/
public function loadAllParents($tid) {
- if (!isset($this->parentsAll[$tid])) {
- $parents = [];
- if ($term = $this->load($tid)) {
- $parents[$term->id()] = $term;
- $terms_to_search[] = $term->id();
+ /** @var \Drupal\taxonomy\TermInterface $term */
+ return (!empty($tid) && $term = $this->load($tid)) ? $this->getAncestors($term) : [];
+ }
- while ($tid = array_shift($terms_to_search)) {
- if ($new_parents = $this->loadParents($tid)) {
- foreach ($new_parents as $new_parent) {
- if (!isset($parents[$new_parent->id()])) {
- $parents[$new_parent->id()] = $new_parent;
- $terms_to_search[] = $new_parent->id();
- }
- }
+ /**
+ * Returns all ancestors of this term.
+ *
+ * @return \Drupal\taxonomy\TermInterface[]
+ * A list of ancestor taxonomy term entities keyed by term ID.
+ *
+ * @internal
+ * @todo Refactor away when TreeInterface is introduced.
+ */
+ protected function getAncestors(TermInterface $term) {
+ if (!isset($this->ancestors[$term->id()])) {
+ $this->ancestors[$term->id()] = [$term->id() => $term];
+ $search[] = $term->id();
+
+ while ($tid = array_shift($search)) {
+ foreach ($this->getParents(static::load($tid)) as $id => $parent) {
+ if ($parent && !isset($this->ancestors[$term->id()][$id])) {
+ $this->ancestors[$term->id()][$id] = $parent;
+ $search[] = $id;
}
}
}
-
- $this->parentsAll[$tid] = $parents;
}
- return $this->parentsAll[$tid];
+ return $this->ancestors[$term->id()];
}
/**
* {@inheritdoc}
*/
public function loadChildren($tid, $vid = NULL) {
- if (!isset($this->children[$tid])) {
- $children = [];
- $query = $this->database->select('taxonomy_term_field_data', 't');
- $query->join('taxonomy_term_hierarchy', 'h', 'h.tid = t.tid');
- $query->addField('t', 'tid');
- $query->condition('h.parent', $tid);
- if ($vid) {
- $query->condition('t.vid', $vid);
- }
- $query->condition('t.default_langcode', 1);
- $query->addTag('taxonomy_term_access');
- $query->orderBy('t.weight');
- $query->orderBy('t.name');
- if ($ids = $query->execute()->fetchCol()) {
- $children = $this->loadMultiple($ids);
- }
- $this->children[$tid] = $children;
- }
- return $this->children[$tid];
+ /** @var \Drupal\taxonomy\TermInterface $term */
+ return (!empty($tid) && $term = $this->load($tid)) ? $this->getChildren($term) : [];
+ }
+
+ /**
+ * Returns all children terms of this term.
+ *
+ * @return \Drupal\taxonomy\TermInterface[]
+ * A list of children taxonomy term entities keyed by term ID.
+ *
+ * @internal
+ * @todo Refactor away when TreeInterface is introduced.
+ */
+ public function getChildren(TermInterface $term) {
+ $query = \Drupal::entityQuery('taxonomy_term')
+ ->condition('parent', $term->id());
+ return static::loadMultiple($query->execute());
}
/**
@@ -201,12 +211,12 @@ class TermStorage extends SqlContentEntityStorage implements TermStorageInterfac
$this->treeChildren[$vid] = [];
$this->treeParents[$vid] = [];
$this->treeTerms[$vid] = [];
- $query = $this->database->select('taxonomy_term_field_data', 't');
- $query->join('taxonomy_term_hierarchy', 'h', 'h.tid = t.tid');
+ $query = $this->database->select($this->getDataTable(), 't');
+ $query->join('taxonomy_term__parent', 'p', 't.tid = p.entity_id');
+ $query->addExpression('parent_target_id', 'parent');
$result = $query
->addTag('taxonomy_term_access')
->fields('t')
- ->fields('h', ['parent'])
->condition('t.vid', $vid)
->condition('t.default_langcode', 1)
->orderBy('t.weight')
@@ -254,7 +264,9 @@ class TermStorage extends SqlContentEntityStorage implements TermStorageInterfac
$term = clone $term;
}
$term->depth = $depth;
- unset($term->parent);
+ if (!$load_entities) {
+ unset($term->parent);
+ }
$tid = $load_entities ? $term->id() : $term->tid;
$term->parents = $this->treeParents[$vid][$tid];
$tree[] = $term;
@@ -293,7 +305,7 @@ class TermStorage extends SqlContentEntityStorage implements TermStorageInterfac
public function nodeCount($vid) {
$query = $this->database->select('taxonomy_index', 'ti');
$query->addExpression('COUNT(DISTINCT ti.nid)');
- $query->leftJoin('taxonomy_term_data', 'td', 'ti.tid = td.tid');
+ $query->leftJoin($this->getBaseTable(), 'td', 'ti.tid = td.tid');
$query->condition('td.vid', $vid);
$query->addTag('vocabulary_node_count');
return $query->execute()->fetchField();
@@ -303,7 +315,7 @@ class TermStorage extends SqlContentEntityStorage implements TermStorageInterfac
* {@inheritdoc}
*/
public function resetWeights($vid) {
- $this->database->update('taxonomy_term_field_data')
+ $this->database->update($this->getDataTable())
->fields(['weight' => 0])
->condition('vid', $vid)
->execute();
@@ -313,7 +325,7 @@ class TermStorage extends SqlContentEntityStorage implements TermStorageInterfac
* {@inheritdoc}
*/
public function getNodeTerms(array $nids, array $vocabs = [], $langcode = NULL) {
- $query = db_select('taxonomy_term_field_data', 'td');
+ $query = db_select($this->getDataTable(), 'td');
$query->innerJoin('taxonomy_index', 'tn', 'td.tid = tn.tid');
$query->fields('td', ['tid']);
$query->addField('tn', 'nid', 'node_nid');
@@ -351,7 +363,7 @@ class TermStorage extends SqlContentEntityStorage implements TermStorageInterfac
public function __sleep() {
$vars = parent::__sleep();
// Do not serialize static cache.
- unset($vars['parents'], $vars['parentsAll'], $vars['children'], $vars['treeChildren'], $vars['treeParents'], $vars['treeTerms'], $vars['trees']);
+ unset($vars['ancestors'], $vars['treeChildren'], $vars['treeParents'], $vars['treeTerms'], $vars['trees']);
return $vars;
}
@@ -361,9 +373,7 @@ class TermStorage extends SqlContentEntityStorage implements TermStorageInterfac
public function __wakeup() {
parent::__wakeup();
// Initialize static caches.
- $this->parents = [];
- $this->parentsAll = [];
- $this->children = [];
+ $this->ancestors = [];
$this->treeChildren = [];
$this->treeParents = [];
$this->treeTerms = [];
diff --git a/core/modules/taxonomy/src/TermStorageInterface.php b/core/modules/taxonomy/src/TermStorageInterface.php
index 4ab2d2dea..4d7b5cc1d 100644
--- a/core/modules/taxonomy/src/TermStorageInterface.php
+++ b/core/modules/taxonomy/src/TermStorageInterface.php
@@ -15,6 +15,10 @@ interface TermStorageInterface extends ContentEntityStorageInterface {
*
* @param array $tids
* Array of terms that need to be removed from hierarchy.
+ *
+ * @todo Remove this method in Drupal 9.0.x. Now the parent references are
+ * automatically cleared when deleting a taxonomy term.
+ * https://www.drupal.org/node/2785693
*/
public function deleteTermHierarchy($tids);
@@ -23,6 +27,10 @@ interface TermStorageInterface extends ContentEntityStorageInterface {
*
* @param \Drupal\Core\Entity\EntityInterface $term
* Term entity that needs to be added to term hierarchy information.
+ *
+ * @todo remove this method Drupal 9.0.x. Now the parent references are
+ * automatically updates when when a taxonomy term is added/updated.
+ * https://www.drupal.org/node/2785693
*/
public function updateTermHierarchy(EntityInterface $term);
diff --git a/core/modules/taxonomy/src/TermStorageSchema.php b/core/modules/taxonomy/src/TermStorageSchema.php
index 5bcb088db..4351b60a2 100644
--- a/core/modules/taxonomy/src/TermStorageSchema.php
+++ b/core/modules/taxonomy/src/TermStorageSchema.php
@@ -17,40 +17,12 @@ class TermStorageSchema extends SqlContentEntityStorageSchema {
protected function getEntitySchema(ContentEntityTypeInterface $entity_type, $reset = FALSE) {
$schema = parent::getEntitySchema($entity_type, $reset = FALSE);
- $schema['taxonomy_term_field_data']['indexes'] += [
- 'taxonomy_term__tree' => ['vid', 'weight', 'name'],
- 'taxonomy_term__vid_name' => ['vid', 'name'],
- ];
-
- $schema['taxonomy_term_hierarchy'] = [
- 'description' => 'Stores the hierarchical relationship between terms.',
- 'fields' => [
- 'tid' => [
- 'type' => 'int',
- 'unsigned' => TRUE,
- 'not null' => TRUE,
- 'default' => 0,
- 'description' => 'Primary Key: The {taxonomy_term_data}.tid of the term.',
- ],
- 'parent' => [
- 'type' => 'int',
- 'unsigned' => TRUE,
- 'not null' => TRUE,
- 'default' => 0,
- 'description' => "Primary Key: The {taxonomy_term_data}.tid of the term's parent. 0 indicates no parent.",
- ],
- ],
- 'indexes' => [
- 'parent' => ['parent'],
- ],
- 'foreign keys' => [
- 'taxonomy_term_data' => [
- 'table' => 'taxonomy_term_data',
- 'columns' => ['tid' => 'tid'],
- ],
- ],
- 'primary key' => ['tid', 'parent'],
- ];
+ if ($data_table = $this->storage->getDataTable()) {
+ $schema[$data_table]['indexes'] += [
+ 'taxonomy_term__tree' => ['vid', 'weight', 'name'],
+ 'taxonomy_term__vid_name' => ['vid', 'name'],
+ ];
+ }
$schema['taxonomy_index'] = [
'description' => 'Maintains denormalized information about node/term relationships.',
diff --git a/core/modules/taxonomy/src/TermViewsData.php b/core/modules/taxonomy/src/TermViewsData.php
index 5e98bfe9b..73e8cbc00 100644
--- a/core/modules/taxonomy/src/TermViewsData.php
+++ b/core/modules/taxonomy/src/TermViewsData.php
@@ -36,7 +36,7 @@ class TermViewsData extends EntityViewsData {
$data['taxonomy_term_field_data']['tid']['filter']['id'] = 'taxonomy_index_tid';
$data['taxonomy_term_field_data']['tid']['filter']['title'] = $this->t('Term');
$data['taxonomy_term_field_data']['tid']['filter']['help'] = $this->t('Taxonomy term chosen from autocomplete or select widget.');
- $data['taxonomy_term_field_data']['tid']['filter']['hierarchy table'] = 'taxonomy_term_hierarchy';
+ $data['taxonomy_term_field_data']['tid']['filter']['hierarchy table'] = 'taxonomy_term__parent';
$data['taxonomy_term_field_data']['tid']['filter']['numeric'] = TRUE;
$data['taxonomy_term_field_data']['tid_raw'] = [
@@ -61,12 +61,12 @@ class TermViewsData extends EntityViewsData {
'argument field' => 'tid',
'base' => 'node_field_data',
'field' => 'nid',
- 'relationship' => 'node_field_data:term_node_tid'
+ 'relationship' => 'node_field_data:term_node_tid',
],
];
$data['taxonomy_term_field_data']['vid']['help'] = $this->t('Filter the results of "Taxonomy: Term" to a particular vocabulary.');
- unset($data['taxonomy_term_field_data']['vid']['field']);
+ $data['taxonomy_term_field_data']['vid']['field']['help'] = t('The vocabulary name.');
$data['taxonomy_term_field_data']['vid']['argument']['id'] = 'vocabulary_vid';
unset($data['taxonomy_term_field_data']['vid']['sort']);
@@ -133,7 +133,7 @@ class TermViewsData extends EntityViewsData {
],
];
- $data['taxonomy_index']['table']['group'] = $this->t('Taxonomy term');
+ $data['taxonomy_index']['table']['group'] = $this->t('Taxonomy term');
$data['taxonomy_index']['table']['join'] = [
'taxonomy_term_field_data' => [
@@ -146,8 +146,8 @@ class TermViewsData extends EntityViewsData {
'left_field' => 'nid',
'field' => 'nid',
],
- 'taxonomy_term_hierarchy' => [
- 'left_field' => 'tid',
+ 'taxonomy_term__parent' => [
+ 'left_field' => 'entity_id',
'field' => 'tid',
],
];
@@ -181,7 +181,7 @@ class TermViewsData extends EntityViewsData {
'filter' => [
'title' => $this->t('Has taxonomy term'),
'id' => 'taxonomy_index_tid',
- 'hierarchy table' => 'taxonomy_term_hierarchy',
+ 'hierarchy table' => 'taxonomy_term__parent',
'numeric' => TRUE,
'skip base' => 'taxonomy_term_field_data',
'allow empty' => TRUE,
@@ -216,47 +216,22 @@ class TermViewsData extends EntityViewsData {
'title' => $this->t('Post date'),
'help' => $this->t('The date the content related to a term was posted.'),
'sort' => [
- 'id' => 'date'
+ 'id' => 'date',
],
'filter' => [
'id' => 'date',
],
];
- $data['taxonomy_term_hierarchy']['table']['group'] = $this->t('Taxonomy term');
- $data['taxonomy_term_hierarchy']['table']['provider'] = 'taxonomy';
-
- $data['taxonomy_term_hierarchy']['table']['join'] = [
- 'taxonomy_term_hierarchy' => [
- // Link to self through left.parent = right.tid (going down in depth).
- 'left_field' => 'tid',
- 'field' => 'parent',
- ],
- 'taxonomy_term_field_data' => [
- // Link directly to taxonomy_term_field_data via tid.
- 'left_field' => 'tid',
- 'field' => 'tid',
- ],
+ // Link to self through left.parent = right.tid (going down in depth).
+ $data['taxonomy_term__parent']['table']['join']['taxonomy_term__parent'] = [
+ 'left_field' => 'entity_id',
+ 'field' => 'parent_target_id',
];
- $data['taxonomy_term_hierarchy']['parent'] = [
- 'title' => $this->t('Parent term'),
- 'help' => $this->t('The parent term of the term. This can produce duplicate entries if you are using a vocabulary that allows multiple parents.'),
- 'relationship' => [
- 'base' => 'taxonomy_term_field_data',
- 'field' => 'parent',
- 'label' => $this->t('Parent'),
- 'id' => 'standard',
- ],
- 'filter' => [
- 'help' => $this->t('Filter the results of "Taxonomy: Term" by the parent pid.'),
- 'id' => 'numeric',
- ],
- 'argument' => [
- 'help' => $this->t('The parent term of the term.'),
- 'id' => 'taxonomy',
- ],
- ];
+ $data['taxonomy_term__parent']['parent_target_id']['help'] = $this->t('The parent term of the term. This can produce duplicate entries if you are using a vocabulary that allows multiple parents.');
+ $data['taxonomy_term__parent']['parent_target_id']['relationship']['label'] = $this->t('Parent');
+ $data['taxonomy_term__parent']['parent_target_id']['argument']['id'] = 'taxonomy';
return $data;
}
diff --git a/core/modules/taxonomy/src/Tests/RssTest.php b/core/modules/taxonomy/src/Tests/RssTest.php
index 81a397d69..c4f48846b 100644
--- a/core/modules/taxonomy/src/Tests/RssTest.php
+++ b/core/modules/taxonomy/src/Tests/RssTest.php
@@ -123,7 +123,7 @@ class RssTest extends TaxonomyTestBase {
$this->drupalGet('taxonomy/term/all/feed');
$this->assertRaw($raw_xml, "Raw text '$raw_xml' is found.");
// Unpublish the article and check that it is not shown in the feed.
- $node->setPublished(FALSE)->save();
+ $node->setUnpublished()->save();
$this->drupalGet('taxonomy/term/all/feed');
$this->assertNoRaw($raw_xml);
}
diff --git a/core/modules/taxonomy/src/Tests/TaxonomyTestTrait.php b/core/modules/taxonomy/src/Tests/TaxonomyTestTrait.php
index 8c4a66727..a1b4a5521 100644
--- a/core/modules/taxonomy/src/Tests/TaxonomyTestTrait.php
+++ b/core/modules/taxonomy/src/Tests/TaxonomyTestTrait.php
@@ -4,7 +4,6 @@ namespace Drupal\taxonomy\Tests;
@trigger_error(__NAMESPACE__ . '\TaxonomyTestTrait is deprecated in Drupal 8.4.0 and will be removed before Drupal 9.0.0. Instead, use \Drupal\Tests\taxonomy\Functional\TaxonomyTestTrait', E_USER_DEPRECATED);
-use Drupal\Component\Utility\Unicode;
use Drupal\Core\Language\LanguageInterface;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\taxonomy\Entity\Term;
@@ -25,7 +24,7 @@ trait TaxonomyTestTrait {
$vocabulary = Vocabulary::create([
'name' => $this->randomMachineName(),
'description' => $this->randomMachineName(),
- 'vid' => Unicode::strtolower($this->randomMachineName()),
+ 'vid' => mb_strtolower($this->randomMachineName()),
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
'weight' => mt_rand(0, 10),
]);
diff --git a/core/modules/taxonomy/src/Tests/TaxonomyTranslationTestTrait.php b/core/modules/taxonomy/src/Tests/TaxonomyTranslationTestTrait.php
index 9b952ebbe..6f2f11987 100644
--- a/core/modules/taxonomy/src/Tests/TaxonomyTranslationTestTrait.php
+++ b/core/modules/taxonomy/src/Tests/TaxonomyTranslationTestTrait.php
@@ -22,7 +22,7 @@ trait TaxonomyTranslationTestTrait {
/**
* The vocabulary.
*
- * @var \Drupal\taxonomy\Entity\Vocabulary;
+ * @var \Drupal\taxonomy\Entity\Vocabulary
*/
protected $vocabulary;
diff --git a/core/modules/taxonomy/src/Tests/TermAutocompleteTest.php b/core/modules/taxonomy/src/Tests/TermAutocompleteTest.php
index 7ff519df9..ce28b1285 100644
--- a/core/modules/taxonomy/src/Tests/TermAutocompleteTest.php
+++ b/core/modules/taxonomy/src/Tests/TermAutocompleteTest.php
@@ -2,7 +2,6 @@
namespace Drupal\taxonomy\Tests;
-use Drupal\Component\Utility\Unicode;
use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
@@ -86,7 +85,7 @@ class TermAutocompleteTest extends TaxonomyTestBase {
// Create a taxonomy_term_reference field on the article Content Type that
// uses a taxonomy_autocomplete widget.
- $this->fieldName = Unicode::strtolower($this->randomMachineName());
+ $this->fieldName = mb_strtolower($this->randomMachineName());
FieldStorageConfig::create([
'field_name' => $this->fieldName,
'entity_type' => 'node',
@@ -189,7 +188,7 @@ class TermAutocompleteTest extends TaxonomyTestBase {
foreach ($expectedResults as $termName) {
$expected[] = [
'value' => $termName . ' (' . $this->termIds[$termName] . ')',
- 'label' => $termName
+ 'label' => $termName,
];
}
diff --git a/core/modules/taxonomy/src/Tests/TermTest.php b/core/modules/taxonomy/src/Tests/TermTest.php
index 42f92b1b4..54309c423 100644
--- a/core/modules/taxonomy/src/Tests/TermTest.php
+++ b/core/modules/taxonomy/src/Tests/TermTest.php
@@ -3,7 +3,6 @@
namespace Drupal\taxonomy\Tests;
use Drupal\Component\Utility\Tags;
-use Drupal\Component\Utility\Unicode;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\field\Entity\FieldConfig;
use Drupal\taxonomy\Entity\Term;
@@ -73,6 +72,15 @@ class TermTest extends TaxonomyTestBase {
->save();
}
+ /**
+ * The "parent" field must restrict references to the same vocabulary.
+ */
+ public function testParentHandlerSettings() {
+ $vocabulary_fields = \Drupal::service('entity_field.manager')->getFieldDefinitions('taxonomy_term', $this->vocabulary->id());
+ $parent_target_bundles = $vocabulary_fields['parent']->getSetting('handler_settings')['target_bundles'];
+ $this->assertIdentical([$this->vocabulary->id() => $this->vocabulary->id()], $parent_target_bundles);
+ }
+
/**
* Test terms in a single and multiple hierarchy.
*/
@@ -490,7 +498,7 @@ class TermTest extends TaxonomyTestBase {
$this->assertFalse($terms, 'No term loaded with an invalid name.');
// Try to load the term using a substring of the name.
- $terms = taxonomy_term_load_multiple_by_name(Unicode::substr($term->getName(), 2), 'No term loaded with a substring of the name.');
+ $terms = taxonomy_term_load_multiple_by_name(mb_substr($term->getName(), 2), 'No term loaded with a substring of the name.');
$this->assertFalse($terms);
// Create a new term in a different vocabulary with the same name.
diff --git a/core/modules/taxonomy/src/VocabularyForm.php b/core/modules/taxonomy/src/VocabularyForm.php
index 23e911b9a..61b3638ec 100644
--- a/core/modules/taxonomy/src/VocabularyForm.php
+++ b/core/modules/taxonomy/src/VocabularyForm.php
@@ -19,7 +19,7 @@ class VocabularyForm extends BundleEntityFormBase {
/**
* The vocabulary storage.
*
- * @var \Drupal\taxonomy\VocabularyStorageInterface.
+ * @var \Drupal\taxonomy\VocabularyStorageInterface
*/
protected $vocabularyStorage;
@@ -89,7 +89,7 @@ class VocabularyForm extends BundleEntityFormBase {
if ($this->moduleHandler->moduleExists('language')) {
$form['default_terms_language'] = [
'#type' => 'details',
- '#title' => $this->t('Terms language'),
+ '#title' => $this->t('Term language'),
'#open' => TRUE,
];
$form['default_terms_language']['default_language'] = [
@@ -125,13 +125,13 @@ class VocabularyForm extends BundleEntityFormBase {
$edit_link = $this->entity->link($this->t('Edit'));
switch ($status) {
case SAVED_NEW:
- drupal_set_message($this->t('Created new vocabulary %name.', ['%name' => $vocabulary->label()]));
+ $this->messenger()->addStatus($this->t('Created new vocabulary %name.', ['%name' => $vocabulary->label()]));
$this->logger('taxonomy')->notice('Created new vocabulary %name.', ['%name' => $vocabulary->label(), 'link' => $edit_link]);
$form_state->setRedirectUrl($vocabulary->urlInfo('overview-form'));
break;
case SAVED_UPDATED:
- drupal_set_message($this->t('Updated vocabulary %name.', ['%name' => $vocabulary->label()]));
+ $this->messenger()->addStatus($this->t('Updated vocabulary %name.', ['%name' => $vocabulary->label()]));
$this->logger('taxonomy')->notice('Updated vocabulary %name.', ['%name' => $vocabulary->label(), 'link' => $edit_link]);
$form_state->setRedirectUrl($vocabulary->urlInfo('collection'));
break;
diff --git a/core/modules/taxonomy/src/VocabularyListBuilder.php b/core/modules/taxonomy/src/VocabularyListBuilder.php
index e7c021b4d..b6f6921db 100644
--- a/core/modules/taxonomy/src/VocabularyListBuilder.php
+++ b/core/modules/taxonomy/src/VocabularyListBuilder.php
@@ -7,6 +7,7 @@ use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;
@@ -45,6 +46,13 @@ class VocabularyListBuilder extends DraggableListBuilder {
*/
protected $renderer;
+ /**
+ * The messenger.
+ *
+ * @var \Drupal\Core\Messenger\MessengerInterface
+ */
+ protected $messenger;
+
/**
* Constructs a new VocabularyListBuilder object.
*
@@ -56,13 +64,20 @@ class VocabularyListBuilder extends DraggableListBuilder {
* The entity manager service.
* @param \Drupal\Core\Render\RendererInterface $renderer
* The renderer service.
+ * @param \Drupal\Core\Messenger\MessengerInterface $messenger
+ * The messenger.
*/
- public function __construct(EntityTypeInterface $entity_type, AccountInterface $current_user, EntityTypeManagerInterface $entity_type_manager, RendererInterface $renderer = NULL) {
+ public function __construct(EntityTypeInterface $entity_type,
+ AccountInterface $current_user,
+ EntityTypeManagerInterface $entity_type_manager,
+ RendererInterface $renderer = NULL,
+ MessengerInterface $messenger) {
parent::__construct($entity_type, $entity_type_manager->getStorage($entity_type->id()));
$this->currentUser = $current_user;
$this->entityTypeManager = $entity_type_manager;
$this->renderer = $renderer;
+ $this->messenger = $messenger;
}
/**
@@ -73,7 +88,8 @@ class VocabularyListBuilder extends DraggableListBuilder {
$entity_type,
$container->get('current_user'),
$container->get('entity_type.manager'),
- $container->get('renderer')
+ $container->get('renderer'),
+ $container->get('messenger')
);
}
@@ -123,7 +139,7 @@ class VocabularyListBuilder extends DraggableListBuilder {
$header['label'] = t('Vocabulary name');
$header['description'] = t('Description');
- if ($this->currentUser->hasPermission('administer vocabularies')) {
+ if ($this->currentUser->hasPermission('administer vocabularies') && !empty($this->weightKey)) {
$header['weight'] = t('Weight');
}
@@ -161,7 +177,7 @@ class VocabularyListBuilder extends DraggableListBuilder {
$this->renderer->addCacheableDependency($build['table'], $create_access);
if ($create_access->isAllowed()) {
$build['table']['#empty'] = t('No vocabularies available. Add vocabulary.', [
- ':link' => Url::fromRoute('entity.taxonomy_vocabulary.add_form')->toString()
+ ':link' => Url::fromRoute('entity.taxonomy_vocabulary.add_form')->toString(),
]);
}
else {
@@ -189,7 +205,7 @@ class VocabularyListBuilder extends DraggableListBuilder {
public function submitForm(array &$form, FormStateInterface $form_state) {
parent::submitForm($form, $form_state);
- drupal_set_message(t('The configuration options have been saved.'));
+ $this->messenger->addStatus($this->t('The configuration options have been saved.'));
}
}
diff --git a/core/modules/taxonomy/src/VocabularyStorage.php b/core/modules/taxonomy/src/VocabularyStorage.php
index bdbf8938f..ce90189b5 100644
--- a/core/modules/taxonomy/src/VocabularyStorage.php
+++ b/core/modules/taxonomy/src/VocabularyStorage.php
@@ -21,7 +21,12 @@ class VocabularyStorage extends ConfigEntityStorage implements VocabularyStorage
* {@inheritdoc}
*/
public function getToplevelTids($vids) {
- return db_query('SELECT t.tid FROM {taxonomy_term_data} t INNER JOIN {taxonomy_term_hierarchy} th ON th.tid = t.tid WHERE t.vid IN ( :vids[] ) AND th.parent = 0', [':vids[]' => $vids])->fetchCol();
+ $tids = \Drupal::entityQuery('taxonomy_term')
+ ->condition('vid', $vids, 'IN')
+ ->condition('parent.target_id', 0)
+ ->execute();
+
+ return array_values($tids);
}
}
diff --git a/core/modules/taxonomy/taxonomy.es6.js b/core/modules/taxonomy/taxonomy.es6.js
index ad5972908..71225d11a 100644
--- a/core/modules/taxonomy/taxonomy.es6.js
+++ b/core/modules/taxonomy/taxonomy.es6.js
@@ -3,7 +3,7 @@
* Taxonomy behaviors.
*/
-(function ($, Drupal) {
+(function($, Drupal) {
/**
* Move a block in the blocks table from one region to another.
*
@@ -25,10 +25,16 @@
const rows = $table.find('tr').length;
// When a row is swapped, keep previous and next page classes set.
- tableDrag.row.prototype.onSwap = function (swappedRow) {
- $table.find('tr.taxonomy-term-preview').removeClass('taxonomy-term-preview');
- $table.find('tr.taxonomy-term-divider-top').removeClass('taxonomy-term-divider-top');
- $table.find('tr.taxonomy-term-divider-bottom').removeClass('taxonomy-term-divider-bottom');
+ tableDrag.row.prototype.onSwap = function(swappedRow) {
+ $table
+ .find('tr.taxonomy-term-preview')
+ .removeClass('taxonomy-term-preview');
+ $table
+ .find('tr.taxonomy-term-divider-top')
+ .removeClass('taxonomy-term-divider-top');
+ $table
+ .find('tr.taxonomy-term-divider-bottom')
+ .removeClass('taxonomy-term-divider-bottom');
const tableBody = $table[0].tBodies[0];
if (backStep) {
@@ -43,10 +49,14 @@
for (let k = rows - forwardStep - 1; k < rows - 1; k++) {
$(tableBody.rows[k]).addClass('taxonomy-term-preview');
}
- $(tableBody.rows[rows - forwardStep - 2]).addClass('taxonomy-term-divider-top');
- $(tableBody.rows[rows - forwardStep - 1]).addClass('taxonomy-term-divider-bottom');
+ $(tableBody.rows[rows - forwardStep - 2]).addClass(
+ 'taxonomy-term-divider-top',
+ );
+ $(tableBody.rows[rows - forwardStep - 1]).addClass(
+ 'taxonomy-term-divider-bottom',
+ );
}
};
},
};
-}(jQuery, Drupal));
+})(jQuery, Drupal);
diff --git a/core/modules/taxonomy/taxonomy.info.yml b/core/modules/taxonomy/taxonomy.info.yml
index ceca3dd91..8eccaf52f 100644
--- a/core/modules/taxonomy/taxonomy.info.yml
+++ b/core/modules/taxonomy/taxonomy.info.yml
@@ -5,6 +5,6 @@ package: Core
version: VERSION
core: 8.x
dependencies:
- - node
- - text
+ - drupal:node
+ - drupal:text
configure: entity.taxonomy_vocabulary.collection
diff --git a/core/modules/taxonomy/taxonomy.install b/core/modules/taxonomy/taxonomy.install
new file mode 100644
index 000000000..bbcb8349a
--- /dev/null
+++ b/core/modules/taxonomy/taxonomy.install
@@ -0,0 +1,183 @@
+getFieldStorageDefinition('parent', 'taxonomy_term');
+ $field_storage_definition->setCustomStorage(FALSE);
+ $definition_update_manager->updateFieldStorageDefinition($field_storage_definition);
+}
+
+/**
+ * Copy hierarchy from {taxonomy_term_hierarchy} to {taxonomy_term__parent}.
+ */
+function taxonomy_update_8502(&$sandbox) {
+ $database = \Drupal::database();
+
+ if (!isset($sandbox['current'])) {
+ // Set batch ops sandbox.
+ $sandbox['current'] = 0;
+ $sandbox['tid'] = -1;
+ $sandbox['delta'] = 0;
+ $sandbox['limit'] = Settings::get('entity_update_batch_size', 50);
+ $sandbox['max'] = $database->select('taxonomy_term_hierarchy')
+ ->countQuery()
+ ->execute()
+ ->fetchField();
+ }
+
+ // Save the hierarchy.
+ $select = $database->select('taxonomy_term_hierarchy', 'h');
+ $select->join('taxonomy_term_data', 'd', 'h.tid = d.tid');
+ $hierarchy = $select
+ ->fields('h', ['tid', 'parent'])
+ ->fields('d', ['vid', 'langcode'])
+ ->range($sandbox['current'], $sandbox['limit'])
+ ->orderBy('tid', 'ASC')
+ ->orderBy('parent', 'ASC')
+ ->execute()
+ ->fetchAll();
+
+ // Restore data.
+ $insert = $database->insert('taxonomy_term__parent')
+ ->fields(['bundle', 'entity_id', 'revision_id', 'langcode', 'delta', 'parent_target_id']);
+
+ foreach ($hierarchy as $row) {
+ if ($row->tid !== $sandbox['tid']) {
+ $sandbox['delta'] = 0;
+ $sandbox['tid'] = $row->tid;
+ }
+
+ $insert->values([
+ 'bundle' => $row->vid,
+ 'entity_id' => $row->tid,
+ 'revision_id' => $row->tid,
+ 'langcode' => $row->langcode,
+ 'delta' => $sandbox['delta'],
+ 'parent_target_id' => $row->parent,
+ ]);
+
+ $sandbox['delta']++;
+ $sandbox['current']++;
+ }
+
+ $insert->execute();
+
+ $sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['current'] / $sandbox['max']);
+
+ if ($sandbox['#finished'] >= 1) {
+ // Update the entity type because the 'taxonomy_term_hierarchy' table is no
+ // longer part of its shared tables schema.
+ $definition_update_manager = \Drupal::entityDefinitionUpdateManager();
+ $definition_update_manager->updateEntityType($definition_update_manager->getEntityType('taxonomy_term'));
+
+ // \Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::onEntityTypeUpdate()
+ // only deletes *known* entity tables (i.e. the base, data and revision
+ // tables), so we have to drop it manually.
+ $database->schema()->dropTable('taxonomy_term_hierarchy');
+
+ return t('Taxonomy term hierarchy has been converted to default entity reference storage.');
+ }
+}
+
+/**
+ * Update views to use {taxonomy_term__parent} in relationships.
+ */
+function taxonomy_update_8503() {
+ $config_factory = \Drupal::configFactory();
+
+ foreach ($config_factory->listAll('views.view.') as $id) {
+ $view = $config_factory->getEditable($id);
+
+ foreach (array_keys($view->get('display')) as $display_id) {
+ $changed = FALSE;
+
+ foreach (['relationships', 'filters', 'arguments'] as $handler_type) {
+ $base_path = "display.$display_id.display_options.$handler_type";
+ $handlers = $view->get($base_path);
+
+ if (!$handlers) {
+ continue;
+ }
+
+ foreach ($handlers as $handler_key => $handler_config) {
+ $table_path = "$base_path.$handler_key.table";
+ $field_path = "$base_path.$handler_key.field";
+ $table = $view->get($table_path);
+ $field = $view->get($field_path);
+
+ if (($table && ($table === 'taxonomy_term_hierarchy')) && ($field && ($field === 'parent'))) {
+ $view->set($table_path, 'taxonomy_term__parent');
+ $view->set($field_path, 'parent_target_id');
+
+ $changed = TRUE;
+ }
+ }
+ }
+
+ if ($changed) {
+ $view->save(TRUE);
+ }
+ }
+ }
+}
+
+/**
+ * Add the publishing status fields to taxonomy terms.
+ */
+function taxonomy_update_8601() {
+ $definition_update_manager = \Drupal::entityDefinitionUpdateManager();
+ $entity_type = $definition_update_manager->getEntityType('taxonomy_term');
+
+ // Bail out early if a field named 'status' is already installed.
+ if ($definition_update_manager->getFieldStorageDefinition('status', 'taxonomy_term')) {
+ $message = \Drupal::state()->get('taxonomy_update_8601_skip_message', t('The publishing status field has not been added to taxonomy terms. See this page for more information on how to install it.', [
+ ':link' => 'https://www.drupal.org/node/2985366',
+ ]));
+ return $message;
+ }
+
+ // Add the 'published' entity key to the taxonomy_term entity type.
+ $entity_keys = $entity_type->getKeys();
+ $entity_keys['published'] = 'status';
+ $entity_type->set('entity_keys', $entity_keys);
+
+ $definition_update_manager->updateEntityType($entity_type);
+
+ // Add the status field.
+ $status = BaseFieldDefinition::create('boolean')
+ ->setLabel(t('Publishing status'))
+ ->setDescription(t('A boolean indicating the published state.'))
+ ->setRevisionable(TRUE)
+ ->setTranslatable(TRUE)
+ ->setDefaultValue(TRUE);
+
+ $has_content_translation_status_field = $definition_update_manager->getFieldStorageDefinition('content_translation_status', 'taxonomy_term');
+ if ($has_content_translation_status_field) {
+ $status->setInitialValueFromField('content_translation_status', TRUE);
+ }
+ else {
+ $status->setInitialValue(TRUE);
+ }
+ $definition_update_manager->installFieldStorageDefinition('status', 'taxonomy_term', 'taxonomy_term', $status);
+
+ // Uninstall the 'content_translation_status' field if needed.
+ if ($has_content_translation_status_field) {
+ $content_translation_status = $definition_update_manager->getFieldStorageDefinition('content_translation_status', 'taxonomy_term');
+ $definition_update_manager->uninstallFieldStorageDefinition($content_translation_status);
+ }
+
+ return t('The publishing status field has been added to taxonomy terms.');
+}
diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module
index be8532a2d..179487d3f 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -213,7 +213,8 @@ function taxonomy_check_vocabulary_hierarchy(VocabularyInterface $vocabulary, $c
* content language of the current request.
*
* @return array
- * A $page element suitable for use by drupal_render().
+ * A $page element suitable for use by
+ * \Drupal\Core\Render\RendererInterface::render().
*/
function taxonomy_term_view(Term $term, $view_mode = 'full', $langcode = NULL) {
return entity_view($term, $view_mode, $langcode);
@@ -231,7 +232,8 @@ function taxonomy_term_view(Term $term, $view_mode = 'full', $langcode = NULL) {
* content language of the current request.
*
* @return array
- * An array in the format expected by drupal_render().
+ * An array in the format expected by
+ * \Drupal\Core\Render\RendererInterface::render().
*/
function taxonomy_term_view_multiple(array $terms, $view_mode = 'full', $langcode = NULL) {
return entity_view_multiple($terms, $view_mode, $langcode);
diff --git a/core/modules/taxonomy/taxonomy.post_update.php b/core/modules/taxonomy/taxonomy.post_update.php
new file mode 100644
index 000000000..12a596278
--- /dev/null
+++ b/core/modules/taxonomy/taxonomy.post_update.php
@@ -0,0 +1,127 @@
+getEntityType('taxonomy_term');
+ $published_key = $entity_type->getKey('published');
+
+ $status_filter = [
+ 'id' => 'status',
+ 'table' => 'taxonomy_term_field_data',
+ 'field' => $published_key,
+ 'relationship' => 'none',
+ 'group_type' => 'group',
+ 'admin_label' => '',
+ 'operator' => '=',
+ 'value' => '1',
+ 'group' => 1,
+ 'exposed' => FALSE,
+ 'expose' => [
+ 'operator_id' => '',
+ 'label' => '',
+ 'description' => '',
+ 'use_operator' => FALSE,
+ 'operator' => '',
+ 'identifier' => '',
+ 'required' => FALSE,
+ 'remember' => FALSE,
+ 'multiple' => FALSE,
+ 'remember_roles' => [
+ 'authenticated' => 'authenticated',
+ 'anonymous' => '0',
+ 'administrator' => '0',
+ ],
+ ],
+ 'is_grouped' => FALSE,
+ 'group_info' => [
+ 'label' => '',
+ 'description' => '',
+ 'identifier' => '',
+ 'optional' => TRUE,
+ 'widget' => 'select',
+ 'multiple' => FALSE,
+ 'remember' => FALSE,
+ 'default_group' => 'All',
+ 'default_group_multiple' => [],
+ 'group_items' => [],
+ ],
+ 'entity_type' => 'taxonomy_term',
+ 'entity_field' => $published_key,
+ 'plugin_id' => 'boolean',
+ ];
+
+ \Drupal::classResolver(ConfigEntityUpdater::class)->update($sandbox, 'view', function ($view) use ($published_key, $status_filter) {
+ /** @var \Drupal\views\ViewEntityInterface $view */
+ // Only alter taxonomy term views.
+ if ($view->get('base_table') !== 'taxonomy_term_field_data') {
+ return FALSE;
+ }
+
+ $displays = $view->get('display');
+ foreach ($displays as $display_name => &$display) {
+ // Update any existing 'content_translation_status fields.
+ $fields = isset($display['display_options']['fields']) ? $display['display_options']['fields'] : [];
+ foreach ($fields as $id => $field) {
+ if (isset($field['field']) && $field['field'] == 'content_translation_status') {
+ $fields[$id]['field'] = $published_key;
+ }
+ }
+ $display['display_options']['fields'] = $fields;
+
+ // Update any existing 'content_translation_status sorts.
+ $sorts = isset($display['display_options']['sorts']) ? $display['display_options']['sorts'] : [];
+ foreach ($sorts as $id => $sort) {
+ if (isset($sort['field']) && $sort['field'] == 'content_translation_status') {
+ $sorts[$id]['field'] = $published_key;
+ }
+ }
+ $display['display_options']['sorts'] = $sorts;
+
+ // Update any existing 'content_translation_status' filters or add a new
+ // one if necessary.
+ $filters = isset($display['display_options']['filters']) ? $display['display_options']['filters'] : [];
+ $has_status_filter = FALSE;
+ foreach ($filters as $id => $filter) {
+ if (isset($filter['field']) && $filter['field'] == 'content_translation_status') {
+ $filters[$id]['field'] = $published_key;
+ $has_status_filter = TRUE;
+ }
+ }
+
+ if (!$has_status_filter) {
+ $status_filter['id'] = ViewExecutable::generateHandlerId($published_key, $filters);
+ $filters[$status_filter['id']] = $status_filter;
+ }
+ $display['display_options']['filters'] = $filters;
+ }
+ $view->set('display', $displays);
+
+ return TRUE;
+ });
+}
diff --git a/core/modules/taxonomy/tests/fixtures/update/drupal-8.views-taxonomy-term-publishing-status-2981887.php b/core/modules/taxonomy/tests/fixtures/update/drupal-8.views-taxonomy-term-publishing-status-2981887.php
new file mode 100644
index 000000000..13374dbb7
--- /dev/null
+++ b/core/modules/taxonomy/tests/fixtures/update/drupal-8.views-taxonomy-term-publishing-status-2981887.php
@@ -0,0 +1,32 @@
+insert('config')
+ ->fields(['collection', 'name', 'data'])
+ ->values([
+ 'collection' => '',
+ 'name' => 'views.view.test_taxonomy_term_view_with_content_translation_status',
+ 'data' => serialize($view_with_cts_config),
+ ])
+ ->values([
+ 'collection' => '',
+ 'name' => 'views.view.test_taxonomy_term_view_without_content_translation_status',
+ 'data' => serialize($view_without_cts_config),
+ ])
+ ->execute();
diff --git a/core/modules/taxonomy/tests/fixtures/update/views.view.test_taxonomy_term_view_with_content_translation_status.yml b/core/modules/taxonomy/tests/fixtures/update/views.view.test_taxonomy_term_view_with_content_translation_status.yml
new file mode 100644
index 000000000..f4cc45b12
--- /dev/null
+++ b/core/modules/taxonomy/tests/fixtures/update/views.view.test_taxonomy_term_view_with_content_translation_status.yml
@@ -0,0 +1,250 @@
+langcode: en
+status: true
+dependencies:
+ module:
+ - taxonomy
+ - user
+id: test_taxonomy_term_view_with_content_translation_status
+label: 'Test taxonomy term view with content translation status'
+module: views
+description: ''
+tag: ''
+base_table: taxonomy_term_field_data
+base_field: tid
+core: 8.x
+display:
+ default:
+ display_plugin: default
+ id: default
+ display_title: Master
+ position: 0
+ display_options:
+ access:
+ type: perm
+ options:
+ perm: 'access content'
+ cache:
+ type: tag
+ options: { }
+ query:
+ type: views_query
+ options:
+ disable_sql_rewrite: false
+ distinct: false
+ replica: false
+ query_comment: ''
+ query_tags: { }
+ exposed_form:
+ type: basic
+ options:
+ submit_button: Apply
+ reset_button: false
+ reset_button_label: Reset
+ exposed_sorts_label: 'Sort by'
+ expose_sort_order: true
+ sort_asc_label: Asc
+ sort_desc_label: Desc
+ pager:
+ type: none
+ options:
+ offset: 0
+ style:
+ type: default
+ options:
+ grouping: { }
+ row_class: ''
+ default_row_class: true
+ uses_fields: false
+ row:
+ type: fields
+ options:
+ inline: { }
+ separator: ''
+ hide_empty: false
+ default_field_elements: true
+ fields:
+ name:
+ id: name
+ table: taxonomy_term_field_data
+ field: name
+ entity_type: taxonomy_term
+ entity_field: name
+ label: ''
+ alter:
+ alter_text: false
+ make_link: false
+ absolute: false
+ trim: false
+ word_boundary: false
+ ellipsis: false
+ strip_tags: false
+ html: false
+ hide_empty: false
+ empty_zero: false
+ type: string
+ settings:
+ link_to_entity: true
+ plugin_id: term_name
+ relationship: none
+ group_type: group
+ admin_label: ''
+ exclude: false
+ element_type: ''
+ element_class: ''
+ element_label_type: ''
+ element_label_class: ''
+ element_label_colon: true
+ element_wrapper_type: ''
+ element_wrapper_class: ''
+ element_default_classes: true
+ empty: ''
+ hide_alter_empty: true
+ click_sort_column: value
+ group_column: value
+ group_columns: { }
+ group_rows: true
+ delta_limit: 0
+ delta_offset: 0
+ delta_reversed: false
+ delta_first_last: false
+ multi_type: separator
+ separator: ', '
+ field_api_classes: false
+ convert_spaces: false
+ content_translation_status:
+ id: content_translation_status
+ table: taxonomy_term_field_data
+ field: content_translation_status
+ relationship: none
+ group_type: group
+ admin_label: ''
+ label: ''
+ exclude: false
+ alter:
+ alter_text: false
+ text: ''
+ make_link: false
+ path: ''
+ absolute: false
+ external: false
+ replace_spaces: false
+ path_case: none
+ trim_whitespace: false
+ alt: ''
+ rel: ''
+ link_class: ''
+ prefix: ''
+ suffix: ''
+ target: ''
+ nl2br: false
+ max_length: 0
+ word_boundary: true
+ ellipsis: true
+ more_link: false
+ more_link_text: ''
+ more_link_path: ''
+ strip_tags: false
+ trim: false
+ preserve_tags: ''
+ html: false
+ element_type: ''
+ element_class: ''
+ element_label_type: ''
+ element_label_class: ''
+ element_label_colon: false
+ element_wrapper_type: ''
+ element_wrapper_class: ''
+ element_default_classes: true
+ empty: ''
+ hide_empty: false
+ empty_zero: false
+ hide_alter_empty: true
+ click_sort_column: value
+ type: boolean
+ settings:
+ format: true-false
+ format_custom_true: ''
+ format_custom_false: ''
+ group_column: value
+ group_columns: { }
+ group_rows: true
+ delta_limit: 0
+ delta_offset: 0
+ delta_reversed: false
+ delta_first_last: false
+ multi_type: separator
+ separator: ', '
+ field_api_classes: false
+ entity_type: taxonomy_term
+ entity_field: content_translation_status
+ plugin_id: field
+ filters:
+ content_translation_status:
+ id: content_translation_status
+ table: taxonomy_term_field_data
+ field: content_translation_status
+ relationship: none
+ group_type: group
+ admin_label: ''
+ operator: '='
+ value: All
+ group: 1
+ exposed: true
+ expose:
+ operator_id: ''
+ label: 'Translation status'
+ description: ''
+ use_operator: false
+ operator: content_translation_status_op
+ identifier: content_translation_status
+ required: false
+ remember: false
+ multiple: false
+ remember_roles:
+ authenticated: authenticated
+ anonymous: '0'
+ administrator: '0'
+ is_grouped: false
+ group_info:
+ label: ''
+ description: ''
+ identifier: ''
+ optional: true
+ widget: select
+ multiple: false
+ remember: false
+ default_group: All
+ default_group_multiple: { }
+ group_items: { }
+ entity_type: taxonomy_term
+ entity_field: content_translation_status
+ plugin_id: boolean
+ sorts:
+ content_translation_status:
+ id: content_translation_status
+ table: taxonomy_term_field_data
+ field: content_translation_status
+ relationship: none
+ group_type: group
+ admin_label: ''
+ order: ASC
+ exposed: false
+ expose:
+ label: ''
+ entity_type: taxonomy_term
+ entity_field: content_translation_status
+ plugin_id: standard
+ header: { }
+ footer: { }
+ empty: { }
+ relationships: { }
+ arguments: { }
+ display_extenders: { }
+ cache_metadata:
+ max-age: -1
+ contexts:
+ - 'languages:language_content'
+ - 'languages:language_interface'
+ - url
+ - user.permissions
+ tags: { }
diff --git a/core/modules/taxonomy/tests/fixtures/update/views.view.test_taxonomy_term_view_without_content_translation_status.yml b/core/modules/taxonomy/tests/fixtures/update/views.view.test_taxonomy_term_view_without_content_translation_status.yml
new file mode 100644
index 000000000..53eb09c9b
--- /dev/null
+++ b/core/modules/taxonomy/tests/fixtures/update/views.view.test_taxonomy_term_view_without_content_translation_status.yml
@@ -0,0 +1,128 @@
+langcode: en
+status: true
+dependencies:
+ module:
+ - taxonomy
+ - user
+id: test_taxonomy_term_view_without_content_translation_status
+label: 'Test taxonomy term view without content translation status'
+module: views
+description: ''
+tag: ''
+base_table: taxonomy_term_field_data
+base_field: tid
+core: 8.x
+display:
+ default:
+ display_plugin: default
+ id: default
+ display_title: Master
+ position: 0
+ display_options:
+ access:
+ type: perm
+ options:
+ perm: 'access content'
+ cache:
+ type: tag
+ options: { }
+ query:
+ type: views_query
+ options:
+ disable_sql_rewrite: false
+ distinct: false
+ replica: false
+ query_comment: ''
+ query_tags: { }
+ exposed_form:
+ type: basic
+ options:
+ submit_button: Apply
+ reset_button: false
+ reset_button_label: Reset
+ exposed_sorts_label: 'Sort by'
+ expose_sort_order: true
+ sort_asc_label: Asc
+ sort_desc_label: Desc
+ pager:
+ type: none
+ options:
+ offset: 0
+ style:
+ type: default
+ options:
+ grouping: { }
+ row_class: ''
+ default_row_class: true
+ uses_fields: false
+ row:
+ type: fields
+ options:
+ inline: { }
+ separator: ''
+ hide_empty: false
+ default_field_elements: true
+ fields:
+ name:
+ id: name
+ table: taxonomy_term_field_data
+ field: name
+ entity_type: taxonomy_term
+ entity_field: name
+ label: ''
+ alter:
+ alter_text: false
+ make_link: false
+ absolute: false
+ trim: false
+ word_boundary: false
+ ellipsis: false
+ strip_tags: false
+ html: false
+ hide_empty: false
+ empty_zero: false
+ type: string
+ settings:
+ link_to_entity: true
+ plugin_id: term_name
+ relationship: none
+ group_type: group
+ admin_label: ''
+ exclude: false
+ element_type: ''
+ element_class: ''
+ element_label_type: ''
+ element_label_class: ''
+ element_label_colon: true
+ element_wrapper_type: ''
+ element_wrapper_class: ''
+ element_default_classes: true
+ empty: ''
+ hide_alter_empty: true
+ click_sort_column: value
+ group_column: value
+ group_columns: { }
+ group_rows: true
+ delta_limit: 0
+ delta_offset: 0
+ delta_reversed: false
+ delta_first_last: false
+ multi_type: separator
+ separator: ', '
+ field_api_classes: false
+ convert_spaces: false
+ filters: { }
+ sorts: { }
+ header: { }
+ footer: { }
+ empty: { }
+ relationships: { }
+ arguments: { }
+ display_extenders: { }
+ cache_metadata:
+ max-age: -1
+ contexts:
+ - 'languages:language_content'
+ - 'languages:language_interface'
+ - user.permissions
+ tags: { }
diff --git a/core/modules/taxonomy/tests/modules/taxonomy_crud/taxonomy_crud.info.yml b/core/modules/taxonomy/tests/modules/taxonomy_crud/taxonomy_crud.info.yml
index dd07a1819..dcfeb6194 100644
--- a/core/modules/taxonomy/tests/modules/taxonomy_crud/taxonomy_crud.info.yml
+++ b/core/modules/taxonomy/tests/modules/taxonomy_crud/taxonomy_crud.info.yml
@@ -5,4 +5,4 @@ package: Testing
version: VERSION
core: 8.x
dependencies:
- - taxonomy
+ - drupal:taxonomy
diff --git a/core/modules/taxonomy/tests/modules/taxonomy_term_stub_test/taxonomy_term_stub_test.info.yml b/core/modules/taxonomy/tests/modules/taxonomy_term_stub_test/taxonomy_term_stub_test.info.yml
index fac03815a..e857e9a7f 100644
--- a/core/modules/taxonomy/tests/modules/taxonomy_term_stub_test/taxonomy_term_stub_test.info.yml
+++ b/core/modules/taxonomy/tests/modules/taxonomy_term_stub_test/taxonomy_term_stub_test.info.yml
@@ -5,5 +5,5 @@ package: Testing
version: VERSION
core: 8.x
dependencies:
- - taxonomy
- - migrate
+ - drupal:taxonomy
+ - drupal:migrate
diff --git a/core/modules/taxonomy/tests/modules/taxonomy_test/taxonomy_test.info.yml b/core/modules/taxonomy/tests/modules/taxonomy_test/taxonomy_test.info.yml
index 5bc019776..0cf7097c7 100644
--- a/core/modules/taxonomy/tests/modules/taxonomy_test/taxonomy_test.info.yml
+++ b/core/modules/taxonomy/tests/modules/taxonomy_test/taxonomy_test.info.yml
@@ -5,4 +5,4 @@ package: Testing
version: VERSION
core: 8.x
dependencies:
- - taxonomy
+ - drupal:taxonomy
diff --git a/core/modules/taxonomy/tests/modules/taxonomy_test_views/taxonomy_test_views.info.yml b/core/modules/taxonomy/tests/modules/taxonomy_test_views/taxonomy_test_views.info.yml
index be8790605..288410883 100644
--- a/core/modules/taxonomy/tests/modules/taxonomy_test_views/taxonomy_test_views.info.yml
+++ b/core/modules/taxonomy/tests/modules/taxonomy_test_views/taxonomy_test_views.info.yml
@@ -5,5 +5,5 @@ package: Testing
version: VERSION
core: 8.x
dependencies:
- - taxonomy
- - views
+ - drupal:taxonomy
+ - drupal:views
diff --git a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_argument_taxonomy_vocabulary.yml b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_argument_taxonomy_vocabulary.yml
index a90e618f4..a34b579d2 100644
--- a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_argument_taxonomy_vocabulary.yml
+++ b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_argument_taxonomy_vocabulary.yml
@@ -171,4 +171,3 @@ display:
- url
- user.permissions
tags: { }
-
diff --git a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_parent.yml b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_parent.yml
index 256f61899..65df6a80e 100644
--- a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_parent.yml
+++ b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_parent.yml
@@ -124,8 +124,8 @@ display:
relationships:
parent:
id: parent
- table: taxonomy_term_hierarchy
- field: parent
+ table: taxonomy_term__parent
+ field: parent_target_id
relationship: none
group_type: group
admin_label: Parent
diff --git a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_term_relationship.yml b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_term_relationship.yml
index 80295b6ea..664ae27cf 100644
--- a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_term_relationship.yml
+++ b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_term_relationship.yml
@@ -186,8 +186,8 @@ display:
plugin_id: standard
parent:
id: parent
- table: taxonomy_term_hierarchy
- field: parent
+ table: taxonomy_term__parent
+ field: parent_target_id
relationship: none
group_type: group
admin_label: Parent
diff --git a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_vid_field.yml b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_vid_field.yml
new file mode 100644
index 000000000..ec5ae2dac
--- /dev/null
+++ b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_vid_field.yml
@@ -0,0 +1,163 @@
+langcode: en
+status: true
+dependencies:
+ module:
+ - taxonomy
+ - user
+id: test_taxonomy_vid_field
+label: test_taxonomy_vid_field
+module: views
+description: ''
+tag: ''
+base_table: taxonomy_term_field_data
+base_field: tid
+core: 8.x
+display:
+ default:
+ display_plugin: default
+ id: default
+ display_title: Master
+ position: 0
+ display_options:
+ access:
+ type: perm
+ options:
+ perm: 'access content'
+ cache:
+ type: tag
+ options: { }
+ query:
+ type: views_query
+ options:
+ disable_sql_rewrite: false
+ distinct: false
+ replica: false
+ query_comment: ''
+ query_tags: { }
+ exposed_form:
+ type: basic
+ options:
+ submit_button: Apply
+ reset_button: false
+ reset_button_label: Reset
+ exposed_sorts_label: 'Sort by'
+ expose_sort_order: true
+ sort_asc_label: Asc
+ sort_desc_label: Desc
+ pager:
+ type: full
+ options:
+ items_per_page: 10
+ offset: 0
+ id: 0
+ total_pages: null
+ expose:
+ items_per_page: false
+ items_per_page_label: 'Items per page'
+ items_per_page_options: '5, 10, 25, 50'
+ items_per_page_options_all: false
+ items_per_page_options_all_label: '- All -'
+ offset: false
+ offset_label: Offset
+ tags:
+ previous: '‹ Previous'
+ next: 'Next ›'
+ first: '« First'
+ last: 'Last »'
+ quantity: 9
+ style:
+ type: default
+ options:
+ grouping: { }
+ row_class: ''
+ default_row_class: true
+ uses_fields: false
+ row:
+ type: fields
+ options:
+ inline: { }
+ separator: ''
+ hide_empty: false
+ default_field_elements: true
+ fields:
+ vid:
+ id: vid
+ table: taxonomy_term_field_data
+ field: vid
+ relationship: none
+ group_type: group
+ admin_label: ''
+ label: ''
+ exclude: false
+ alter:
+ alter_text: false
+ text: ''
+ make_link: false
+ path: ''
+ absolute: false
+ external: false
+ replace_spaces: false
+ path_case: none
+ trim_whitespace: false
+ alt: ''
+ rel: ''
+ link_class: ''
+ prefix: ''
+ suffix: ''
+ target: ''
+ nl2br: false
+ max_length: 0
+ word_boundary: true
+ ellipsis: true
+ more_link: false
+ more_link_text: ''
+ more_link_path: ''
+ strip_tags: false
+ trim: false
+ preserve_tags: ''
+ html: false
+ element_type: ''
+ element_class: ''
+ element_label_type: ''
+ element_label_class: ''
+ element_label_colon: false
+ element_wrapper_type: ''
+ element_wrapper_class: ''
+ element_default_classes: true
+ empty: ''
+ hide_empty: false
+ empty_zero: false
+ hide_alter_empty: true
+ click_sort_column: target_id
+ type: entity_reference_label
+ settings:
+ link: false
+ group_column: target_id
+ group_columns: { }
+ group_rows: true
+ delta_limit: 0
+ delta_offset: 0
+ delta_reversed: false
+ delta_first_last: false
+ multi_type: separator
+ separator: ', '
+ field_api_classes: false
+ entity_type: taxonomy_term
+ entity_field: vid
+ plugin_id: field
+ filters: { }
+ sorts: { }
+ header: { }
+ footer: { }
+ empty: { }
+ relationships: { }
+ arguments: { }
+ display_extenders: { }
+ cache_metadata:
+ max-age: -1
+ contexts:
+ - 'languages:language_content'
+ - 'languages:language_interface'
+ - url.query_args
+ - user.permissions
+ tags: { }
diff --git a/core/modules/taxonomy/tests/modules/vocabulary_serialization_test/vocabulary_serialization_test.info.yml b/core/modules/taxonomy/tests/modules/vocabulary_serialization_test/vocabulary_serialization_test.info.yml
index be28f20a2..ef00b57b2 100644
--- a/core/modules/taxonomy/tests/modules/vocabulary_serialization_test/vocabulary_serialization_test.info.yml
+++ b/core/modules/taxonomy/tests/modules/vocabulary_serialization_test/vocabulary_serialization_test.info.yml
@@ -4,4 +4,4 @@ package: Testing
version: VERSION
core: 8.x
dependencies:
- - taxonomy
+ - drupal:taxonomy
diff --git a/core/modules/taxonomy/tests/src/Functional/Hal/TermHalJsonAnonTest.php b/core/modules/taxonomy/tests/src/Functional/Hal/TermHalJsonAnonTest.php
new file mode 100644
index 000000000..ee3effc57
--- /dev/null
+++ b/core/modules/taxonomy/tests/src/Functional/Hal/TermHalJsonAnonTest.php
@@ -0,0 +1,178 @@
+applyHalFieldNormalization($default_normalization);
+
+ // We test with multiple parent terms, and combinations thereof.
+ // @see ::createEntity()
+ // @see ::testGet()
+ // @see ::testGetTermWithParent()
+ // @see ::providerTestGetTermWithParent()
+ // @see ::testGetTermWithParent()
+ $parent_term_ids = [];
+ for ($i = 0; $i < $this->entity->get('parent')->count(); $i++) {
+ $parent_term_ids[$i] = (int) $this->entity->get('parent')[$i]->target_id;
+ }
+
+ $expected_parent_normalization_links = FALSE;
+ $expected_parent_normalization_embedded = FALSE;
+ switch ($parent_term_ids) {
+ case [0]:
+ $expected_parent_normalization_links = [
+ NULL,
+ ];
+ $expected_parent_normalization_embedded = [
+ NULL,
+ ];
+ break;
+ case [2]:
+ $expected_parent_normalization_links = [
+ [
+ 'href' => $this->baseUrl . '/taxonomy/term/2?_format=hal_json',
+ ],
+ ];
+ $expected_parent_normalization_embedded = [
+ [
+ '_links' => [
+ 'self' => [
+ 'href' => $this->baseUrl . '/taxonomy/term/2?_format=hal_json',
+ ],
+ 'type' => [
+ 'href' => $this->baseUrl . '/rest/type/taxonomy_term/camelids',
+ ],
+ ],
+ 'uuid' => [
+ ['value' => Term::load(2)->uuid()],
+ ],
+ ],
+ ];
+ break;
+ case [0, 2]:
+ $expected_parent_normalization_links = [
+ NULL,
+ [
+ 'href' => $this->baseUrl . '/taxonomy/term/2?_format=hal_json',
+ ],
+ ];
+ $expected_parent_normalization_embedded = [
+ NULL,
+ [
+ '_links' => [
+ 'self' => [
+ 'href' => $this->baseUrl . '/taxonomy/term/2?_format=hal_json',
+ ],
+ 'type' => [
+ 'href' => $this->baseUrl . '/rest/type/taxonomy_term/camelids',
+ ],
+ ],
+ 'uuid' => [
+ ['value' => Term::load(2)->uuid()],
+ ],
+ ],
+ ];
+ break;
+ case [3, 2]:
+ $expected_parent_normalization_links = [
+ [
+ 'href' => $this->baseUrl . '/taxonomy/term/3?_format=hal_json',
+ ],
+ [
+ 'href' => $this->baseUrl . '/taxonomy/term/2?_format=hal_json',
+ ],
+ ];
+ $expected_parent_normalization_embedded = [
+ [
+ '_links' => [
+ 'self' => [
+ 'href' => $this->baseUrl . '/taxonomy/term/3?_format=hal_json',
+ ],
+ 'type' => [
+ 'href' => $this->baseUrl . '/rest/type/taxonomy_term/camelids',
+ ],
+ ],
+ 'uuid' => [
+ ['value' => Term::load(3)->uuid()],
+ ],
+ ],
+ [
+ '_links' => [
+ 'self' => [
+ 'href' => $this->baseUrl . '/taxonomy/term/2?_format=hal_json',
+ ],
+ 'type' => [
+ 'href' => $this->baseUrl . '/rest/type/taxonomy_term/camelids',
+ ],
+ ],
+ 'uuid' => [
+ ['value' => Term::load(2)->uuid()],
+ ],
+ ],
+ ];
+ break;
+ }
+
+ return $normalization + [
+ '_links' => [
+ 'self' => [
+ 'href' => $this->baseUrl . '/llama?_format=hal_json',
+ ],
+ 'type' => [
+ 'href' => $this->baseUrl . '/rest/type/taxonomy_term/camelids',
+ ],
+ $this->baseUrl . '/rest/relation/taxonomy_term/camelids/parent' => $expected_parent_normalization_links,
+ ],
+ '_embedded' => [
+ $this->baseUrl . '/rest/relation/taxonomy_term/camelids/parent' => $expected_parent_normalization_embedded,
+ ],
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getNormalizedPostEntity() {
+ return parent::getNormalizedPostEntity() + [
+ '_links' => [
+ 'type' => [
+ 'href' => $this->baseUrl . '/rest/type/taxonomy_term/camelids',
+ ],
+ ],
+ ];
+ }
+
+}
diff --git a/core/modules/hal/tests/src/Functional/EntityResource/Term/TermHalJsonBasicAuthTest.php b/core/modules/taxonomy/tests/src/Functional/Hal/TermHalJsonBasicAuthTest.php
similarity index 85%
rename from core/modules/hal/tests/src/Functional/EntityResource/Term/TermHalJsonBasicAuthTest.php
rename to core/modules/taxonomy/tests/src/Functional/Hal/TermHalJsonBasicAuthTest.php
index 58633389f..576615607 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/Term/TermHalJsonBasicAuthTest.php
+++ b/core/modules/taxonomy/tests/src/Functional/Hal/TermHalJsonBasicAuthTest.php
@@ -1,6 +1,6 @@
NULL,
+ ];
+
+ /**
+ * @var \Drupal\taxonomy\TermInterface
+ */
+ protected $entity;
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUpAuthorization($method) {
+ switch ($method) {
+ case 'GET':
+ $this->grantPermissionsToTestedRole(['access content']);
+ break;
+
+ case 'POST':
+ $this->grantPermissionsToTestedRole(['create terms in camelids']);
+ break;
+
+ case 'PATCH':
+ // Grant the 'create url aliases' permission to test the case when
+ // the path field is accessible, see
+ // \Drupal\Tests\rest\Functional\EntityResource\Node\NodeResourceTestBase
+ // for a negative test.
+ $this->grantPermissionsToTestedRole(['edit terms in camelids', 'create url aliases']);
+ break;
+
+ case 'DELETE':
+ $this->grantPermissionsToTestedRole(['delete terms in camelids']);
+ break;
+
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function createEntity() {
+ $vocabulary = Vocabulary::load('camelids');
+ if (!$vocabulary) {
+ // Create a "Camelids" vocabulary.
+ $vocabulary = Vocabulary::create([
+ 'name' => 'Camelids',
+ 'vid' => 'camelids',
+ ]);
+ $vocabulary->save();
+ }
+
+ // Create a "Llama" taxonomy term.
+ $term = Term::create(['vid' => $vocabulary->id()])
+ ->setName('Llama')
+ ->setDescription("It is a little known fact that llamas cannot count higher than seven.")
+ ->setChangedTime(123456789)
+ ->set('path', '/llama');
+ $term->save();
+
+ return $term;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedNormalizedEntity() {
+ // We test with multiple parent terms, and combinations thereof.
+ // @see ::createEntity()
+ // @see ::testGet()
+ // @see ::testGetTermWithParent()
+ // @see ::providerTestGetTermWithParent()
+ $parent_term_ids = [];
+ for ($i = 0; $i < $this->entity->get('parent')->count(); $i++) {
+ $parent_term_ids[$i] = (int) $this->entity->get('parent')[$i]->target_id;
+ }
+
+ $expected_parent_normalization = FALSE;
+ switch ($parent_term_ids) {
+ case [0]:
+ $expected_parent_normalization = [
+ [
+ 'target_id' => NULL,
+ ],
+ ];
+ break;
+ case [2]:
+ $expected_parent_normalization = [
+ [
+ 'target_id' => 2,
+ 'target_type' => 'taxonomy_term',
+ 'target_uuid' => Term::load(2)->uuid(),
+ 'url' => base_path() . 'taxonomy/term/2',
+ ],
+ ];
+ break;
+ case [0, 2]:
+ $expected_parent_normalization = [
+ [
+ 'target_id' => NULL,
+ ],
+ [
+ 'target_id' => 2,
+ 'target_type' => 'taxonomy_term',
+ 'target_uuid' => Term::load(2)->uuid(),
+ 'url' => base_path() . 'taxonomy/term/2',
+ ],
+ ];
+ break;
+ case [3, 2]:
+ $expected_parent_normalization = [
+ [
+ 'target_id' => 3,
+ 'target_type' => 'taxonomy_term',
+ 'target_uuid' => Term::load(3)->uuid(),
+ 'url' => base_path() . 'taxonomy/term/3',
+ ],
+ [
+ 'target_id' => 2,
+ 'target_type' => 'taxonomy_term',
+ 'target_uuid' => Term::load(2)->uuid(),
+ 'url' => base_path() . 'taxonomy/term/2',
+ ],
+ ];
+ break;
+ }
+
+ return [
+ 'tid' => [
+ ['value' => 1],
+ ],
+ 'uuid' => [
+ ['value' => $this->entity->uuid()],
+ ],
+ 'vid' => [
+ [
+ 'target_id' => 'camelids',
+ 'target_type' => 'taxonomy_vocabulary',
+ 'target_uuid' => Vocabulary::load('camelids')->uuid(),
+ ],
+ ],
+ 'name' => [
+ ['value' => 'Llama'],
+ ],
+ 'description' => [
+ [
+ 'value' => 'It is a little known fact that llamas cannot count higher than seven.',
+ 'format' => NULL,
+ 'processed' => "It is a little known fact that llamas cannot count higher than seven.
\n",
+ ],
+ ],
+ 'parent' => $expected_parent_normalization,
+ 'weight' => [
+ ['value' => 0],
+ ],
+ 'langcode' => [
+ [
+ 'value' => 'en',
+ ],
+ ],
+ 'changed' => [
+ $this->formatExpectedTimestampItemValues($this->entity->getChangedTime()),
+ ],
+ 'default_langcode' => [
+ [
+ 'value' => TRUE,
+ ],
+ ],
+ 'path' => [
+ [
+ 'alias' => '/llama',
+ 'pid' => 1,
+ 'langcode' => 'en',
+ ],
+ ],
+ 'status' => [
+ [
+ 'value' => TRUE,
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getNormalizedPostEntity() {
+ return [
+ 'vid' => [
+ [
+ 'target_id' => 'camelids',
+ ],
+ ],
+ 'name' => [
+ [
+ 'value' => 'Dramallama',
+ ],
+ ],
+ 'description' => [
+ [
+ 'value' => 'Dramallamas are the coolest camelids.',
+ 'format' => NULL,
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedUnauthorizedAccessMessage($method) {
+ if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
+ return parent::getExpectedUnauthorizedAccessMessage($method);
+ }
+
+ switch ($method) {
+ case 'GET':
+ return "The 'access content' permission is required and the taxonomy term must be published.";
+ case 'POST':
+ return "The following permissions are required: 'create terms in camelids' OR 'administer taxonomy'.";
+ case 'PATCH':
+ return "The following permissions are required: 'edit terms in camelids' OR 'administer taxonomy'.";
+ case 'DELETE':
+ return "The following permissions are required: 'delete terms in camelids' OR 'administer taxonomy'.";
+ default:
+ return parent::getExpectedUnauthorizedAccessMessage($method);
+ }
+ }
+
+ /**
+ * Tests PATCHing a term's path.
+ *
+ * For a negative test, see the similar test coverage for Node.
+ *
+ * @see \Drupal\Tests\rest\Functional\EntityResource\Node\NodeResourceTestBase::testPatchPath()
+ */
+ public function testPatchPath() {
+ $this->initAuthentication();
+ $this->provisionEntityResource();
+ $this->setUpAuthorization('GET');
+ $this->setUpAuthorization('PATCH');
+
+ $url = $this->getEntityResourceUrl()->setOption('query', ['_format' => static::$format]);
+
+ // GET term's current normalization.
+ $response = $this->request('GET', $url, $this->getAuthenticationRequestOptions('GET'));
+ $normalization = $this->serializer->decode((string) $response->getBody(), static::$format);
+
+ // Change term's path alias.
+ $normalization['path'][0]['alias'] .= 's-rule-the-world';
+
+ // Create term PATCH request.
+ $request_options = [];
+ $request_options[RequestOptions::HEADERS]['Content-Type'] = static::$mimeType;
+ $request_options = array_merge_recursive($request_options, $this->getAuthenticationRequestOptions('PATCH'));
+ $request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
+
+ // PATCH request: 200.
+ $response = $this->request('PATCH', $url, $request_options);
+ $this->assertResourceResponse(200, FALSE, $response);
+ $updated_normalization = $this->serializer->decode((string) $response->getBody(), static::$format);
+ $this->assertSame($normalization['path'], $updated_normalization['path']);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedCacheTags() {
+ return Cache::mergeTags(parent::getExpectedCacheTags(), ['config:filter.format.plain_text', 'config:filter.settings']);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedCacheContexts() {
+ return Cache::mergeContexts(['url.site'], $this->container->getParameter('renderer.config')['required_cache_contexts']);
+ }
+
+ /**
+ * Tests GETting a term with a parent term other than the default (0).
+ *
+ * @see ::getExpectedNormalizedEntity()
+ *
+ * @dataProvider providerTestGetTermWithParent
+ */
+ public function testGetTermWithParent(array $parent_term_ids) {
+ // Create all possible parent terms.
+ Term::create(['vid' => Vocabulary::load('camelids')->id()])
+ ->setName('Lamoids')
+ ->save();
+ Term::create(['vid' => Vocabulary::load('camelids')->id()])
+ ->setName('Wimoids')
+ ->save();
+
+ // Modify the entity under test to use the provided parent terms.
+ $this->entity->set('parent', $parent_term_ids)->save();
+
+ $this->initAuthentication();
+ $url = $this->getEntityResourceUrl();
+ $url->setOption('query', ['_format' => static::$format]);
+ $request_options = $this->getAuthenticationRequestOptions('GET');
+ $this->provisionEntityResource();
+ $this->setUpAuthorization('GET');
+ $response = $this->request('GET', $url, $request_options);
+ $expected = $this->getExpectedNormalizedEntity();
+ static::recursiveKSort($expected);
+ $actual = $this->serializer->decode((string) $response->getBody(), static::$format);
+ static::recursiveKSort($actual);
+ $this->assertSame($expected, $actual);
+ }
+
+ public function providerTestGetTermWithParent() {
+ return [
+ 'root parent: [0] (= no parent)' => [
+ [0],
+ ],
+ 'non-root parent: [2]' => [
+ [2],
+ ],
+ 'multiple parents: [0,2] (root + non-root parent)' => [
+ [0, 2],
+ ],
+ 'multiple parents: [3,2] (both non-root parents)' => [
+ [3, 2],
+ ],
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedUnauthorizedAccessCacheability() {
+ // @see \Drupal\taxonomy\TermAccessControlHandler::checkAccess()
+ return parent::getExpectedUnauthorizedAccessCacheability()
+ ->addCacheTags(['taxonomy_term:1']);
+ }
+
+}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Term/TermXmlAnonTest.php b/core/modules/taxonomy/tests/src/Functional/Rest/TermXmlAnonTest.php
similarity index 91%
rename from core/modules/rest/tests/src/Functional/EntityResource/Term/TermXmlAnonTest.php
rename to core/modules/taxonomy/tests/src/Functional/Rest/TermXmlAnonTest.php
index 8f47ae672..69abeaf21 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/Term/TermXmlAnonTest.php
+++ b/core/modules/taxonomy/tests/src/Functional/Rest/TermXmlAnonTest.php
@@ -1,6 +1,6 @@
grantPermissionsToTestedRole(['administer taxonomy']);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function createEntity() {
+ $vocabulary = Vocabulary::create([
+ 'name' => 'Llama',
+ 'vid' => 'llama',
+ ]);
+ $vocabulary->save();
+
+ return $vocabulary;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedNormalizedEntity() {
+ return [
+ 'uuid' => $this->entity->uuid(),
+ 'vid' => 'llama',
+ 'langcode' => 'en',
+ 'status' => TRUE,
+ 'dependencies' => [],
+ 'name' => 'Llama',
+ 'description' => NULL,
+ 'hierarchy' => 0,
+ 'weight' => 0,
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getNormalizedPostEntity() {
+ // @todo Update in https://www.drupal.org/node/2300677.
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedUnauthorizedAccessMessage($method) {
+ if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
+ return parent::getExpectedUnauthorizedAccessMessage($method);
+ }
+
+ if ($method === 'GET') {
+ return "The following permissions are required: 'access taxonomy overview' OR 'administer taxonomy'.";
+ }
+ return parent::getExpectedUnauthorizedAccessMessage($method);
+ }
+
+}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Vocabulary/VocabularyXmlAnonTest.php b/core/modules/taxonomy/tests/src/Functional/Rest/VocabularyXmlAnonTest.php
similarity index 87%
rename from core/modules/rest/tests/src/Functional/EntityResource/Vocabulary/VocabularyXmlAnonTest.php
rename to core/modules/taxonomy/tests/src/Functional/Rest/VocabularyXmlAnonTest.php
index 3e2a300f5..ac8da3d74 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/Vocabulary/VocabularyXmlAnonTest.php
+++ b/core/modules/taxonomy/tests/src/Functional/Rest/VocabularyXmlAnonTest.php
@@ -1,6 +1,6 @@
setupQueryTagTestHooks();
$loaded_terms = $term_storage->loadParents($terms[2]->id());
$this->assertEqual(count($loaded_terms), 1, 'All parent terms were loaded');
- $this->assertQueryTagTestResult(2, 1, 'TermStorage::loadParents()');
+ $this->assertQueryTagTestResult(3, 1, 'TermStorage::loadParents()');
$this->setupQueryTagTestHooks();
$loaded_terms = $term_storage->loadChildren($terms[1]->id());
$this->assertEqual(count($loaded_terms), 1, 'All child terms were loaded');
- $this->assertQueryTagTestResult(2, 1, 'TermStorage::loadChildren()');
+ $this->assertQueryTagTestResult(3, 1, 'TermStorage::loadChildren()');
$this->setupQueryTagTestHooks();
$query = db_select('taxonomy_term_data', 't');
diff --git a/core/modules/taxonomy/tests/src/Functional/TaxonomyTestTrait.php b/core/modules/taxonomy/tests/src/Functional/TaxonomyTestTrait.php
index db445e962..8dcdf1700 100644
--- a/core/modules/taxonomy/tests/src/Functional/TaxonomyTestTrait.php
+++ b/core/modules/taxonomy/tests/src/Functional/TaxonomyTestTrait.php
@@ -2,7 +2,6 @@
namespace Drupal\Tests\taxonomy\Functional;
-use Drupal\Component\Utility\Unicode;
use Drupal\Core\Language\LanguageInterface;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\taxonomy\Entity\Term;
@@ -20,7 +19,7 @@ trait TaxonomyTestTrait {
$vocabulary = Vocabulary::create([
'name' => $this->randomMachineName(),
'description' => $this->randomMachineName(),
- 'vid' => Unicode::strtolower($this->randomMachineName()),
+ 'vid' => mb_strtolower($this->randomMachineName()),
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
'weight' => mt_rand(0, 10),
]);
diff --git a/core/modules/taxonomy/tests/src/Functional/TaxonomyTranslationTestTrait.php b/core/modules/taxonomy/tests/src/Functional/TaxonomyTranslationTestTrait.php
index a7fd54b02..a817e1235 100644
--- a/core/modules/taxonomy/tests/src/Functional/TaxonomyTranslationTestTrait.php
+++ b/core/modules/taxonomy/tests/src/Functional/TaxonomyTranslationTestTrait.php
@@ -17,7 +17,7 @@ trait TaxonomyTranslationTestTrait {
/**
* The vocabulary.
*
- * @var \Drupal\taxonomy\Entity\Vocabulary;
+ * @var \Drupal\taxonomy\Entity\Vocabulary
*/
protected $vocabulary;
diff --git a/core/modules/taxonomy/tests/src/Functional/TermAccessTest.php b/core/modules/taxonomy/tests/src/Functional/TermAccessTest.php
new file mode 100644
index 000000000..4836fcd65
--- /dev/null
+++ b/core/modules/taxonomy/tests/src/Functional/TermAccessTest.php
@@ -0,0 +1,124 @@
+assertSession();
+
+ $vocabulary = $this->createVocabulary();
+
+ // Create two terms.
+ $published_term = Term::create([
+ 'vid' => $vocabulary->id(),
+ 'name' => 'Published term',
+ 'status' => 1,
+ ]);
+ $published_term->save();
+ $unpublished_term = Term::create([
+ 'vid' => $vocabulary->id(),
+ 'name' => 'Unpublished term',
+ 'status' => 0,
+ ]);
+ $unpublished_term->save();
+
+ // Start off logged in as admin.
+ $this->drupalLogin($this->drupalCreateUser(['administer taxonomy']));
+
+ // Test the 'administer taxonomy' permission.
+ $this->drupalGet('taxonomy/term/' . $published_term->id());
+ $assert_session->statusCodeEquals(200);
+ $this->assertTermAccess($published_term, 'view', TRUE);
+ $this->drupalGet('taxonomy/term/' . $unpublished_term->id());
+ $assert_session->statusCodeEquals(200);
+ $this->assertTermAccess($unpublished_term, 'view', TRUE);
+
+ $this->drupalGet('taxonomy/term/' . $published_term->id() . '/edit');
+ $assert_session->statusCodeEquals(200);
+ $this->assertTermAccess($published_term, 'update', TRUE);
+ $this->drupalGet('taxonomy/term/' . $unpublished_term->id() . '/edit');
+ $assert_session->statusCodeEquals(200);
+ $this->assertTermAccess($unpublished_term, 'update', TRUE);
+
+ $this->drupalGet('taxonomy/term/' . $published_term->id() . '/delete');
+ $assert_session->statusCodeEquals(200);
+ $this->assertTermAccess($published_term, 'delete', TRUE);
+ $this->drupalGet('taxonomy/term/' . $unpublished_term->id() . '/delete');
+ $assert_session->statusCodeEquals(200);
+ $this->assertTermAccess($unpublished_term, 'delete', TRUE);
+
+ // Test the 'access content' permission.
+ $this->drupalLogin($this->drupalCreateUser(['access content']));
+
+ $this->drupalGet('taxonomy/term/' . $published_term->id());
+ $assert_session->statusCodeEquals(200);
+ $this->assertTermAccess($published_term, 'view', TRUE);
+
+ $this->drupalGet('taxonomy/term/' . $unpublished_term->id());
+ $assert_session->statusCodeEquals(403);
+ $this->assertTermAccess($unpublished_term, 'view', FALSE, "The 'access content' permission is required and the taxonomy term must be published.");
+
+ $this->drupalGet('taxonomy/term/' . $published_term->id() . '/edit');
+ $assert_session->statusCodeEquals(403);
+ $this->assertTermAccess($published_term, 'update', FALSE, "The following permissions are required: 'edit terms in {$vocabulary->id()}' OR 'administer taxonomy'.");
+ $this->drupalGet('taxonomy/term/' . $unpublished_term->id() . '/edit');
+ $assert_session->statusCodeEquals(403);
+ $this->assertTermAccess($unpublished_term, 'update', FALSE, "The following permissions are required: 'edit terms in {$vocabulary->id()}' OR 'administer taxonomy'.");
+
+ $this->drupalGet('taxonomy/term/' . $published_term->id() . '/delete');
+ $assert_session->statusCodeEquals(403);
+ $this->assertTermAccess($published_term, 'delete', FALSE, "The following permissions are required: 'delete terms in {$vocabulary->id()}' OR 'administer taxonomy'.");
+ $this->drupalGet('taxonomy/term/' . $unpublished_term->id() . '/delete');
+ $assert_session->statusCodeEquals(403);
+ $this->assertTermAccess($unpublished_term, 'delete', FALSE, "The following permissions are required: 'delete terms in {$vocabulary->id()}' OR 'administer taxonomy'.");
+
+ // Install the Views module and repeat the checks for the 'view' permission.
+ \Drupal::service('module_installer')->install(['views'], TRUE);
+ $this->rebuildContainer();
+
+ $this->drupalGet('taxonomy/term/' . $published_term->id());
+ $assert_session->statusCodeEquals(200);
+
+ // @todo Change this assertion to expect a 403 status code when
+ // https://www.drupal.org/project/drupal/issues/2983070 is fixed.
+ $this->drupalGet('taxonomy/term/' . $unpublished_term->id());
+ $assert_session->statusCodeEquals(404);
+ }
+
+ /**
+ * Checks access on taxonomy term.
+ *
+ * @param \Drupal\taxonomy\TermInterface $term
+ * A taxonomy term entity.
+ * @param $access_operation
+ * The entity operation, e.g. 'view', 'edit', 'delete', etc.
+ * @param bool $access_allowed
+ * Whether the current use has access to the given operation or not.
+ * @param string $access_reason
+ * (optional) The reason of the access result.
+ */
+ protected function assertTermAccess(TermInterface $term, $access_operation, $access_allowed, $access_reason = '') {
+ $access_result = $term->access($access_operation, NULL, TRUE);
+ $this->assertSame($access_allowed, $access_result->isAllowed());
+
+ if ($access_reason) {
+ $this->assertSame($access_reason, $access_result->getReason());
+ }
+ }
+
+}
diff --git a/core/modules/taxonomy/tests/src/Functional/TermCacheTagsTest.php b/core/modules/taxonomy/tests/src/Functional/TermCacheTagsTest.php
index 35b7d6bd6..378d02c75 100644
--- a/core/modules/taxonomy/tests/src/Functional/TermCacheTagsTest.php
+++ b/core/modules/taxonomy/tests/src/Functional/TermCacheTagsTest.php
@@ -2,9 +2,9 @@
namespace Drupal\Tests\taxonomy\Functional;
-use Drupal\system\Tests\Entity\EntityWithUriCacheTagsTestBase;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\taxonomy\Entity\Term;
+use Drupal\Tests\system\Functional\Entity\EntityWithUriCacheTagsTestBase;
/**
* Tests the Taxonomy term entity's cache tags.
diff --git a/core/modules/taxonomy/tests/src/Functional/TermIndexTest.php b/core/modules/taxonomy/tests/src/Functional/TermIndexTest.php
index d4fe8f225..83ab04d5e 100644
--- a/core/modules/taxonomy/tests/src/Functional/TermIndexTest.php
+++ b/core/modules/taxonomy/tests/src/Functional/TermIndexTest.php
@@ -2,7 +2,6 @@
namespace Drupal\Tests\taxonomy\Functional;
-use Drupal\Component\Utility\Unicode;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
/**
@@ -49,7 +48,7 @@ class TermIndexTest extends TaxonomyTestBase {
// Create a vocabulary and add two term reference fields to article nodes.
$this->vocabulary = $this->createVocabulary();
- $this->fieldName1 = Unicode::strtolower($this->randomMachineName());
+ $this->fieldName1 = mb_strtolower($this->randomMachineName());
$handler_settings = [
'target_bundles' => [
$this->vocabulary->id() => $this->vocabulary->id(),
@@ -69,7 +68,7 @@ class TermIndexTest extends TaxonomyTestBase {
])
->save();
- $this->fieldName2 = Unicode::strtolower($this->randomMachineName());
+ $this->fieldName2 = mb_strtolower($this->randomMachineName());
$this->createEntityReferenceField('node', 'article', $this->fieldName2, NULL, 'taxonomy_term', 'default', $handler_settings, FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
entity_get_form_display('node', 'article', 'default')
diff --git a/core/modules/taxonomy/tests/src/Functional/Update/TaxonomyParentUpdateTest.php b/core/modules/taxonomy/tests/src/Functional/Update/TaxonomyParentUpdateTest.php
new file mode 100644
index 000000000..c47e3e486
--- /dev/null
+++ b/core/modules/taxonomy/tests/src/Functional/Update/TaxonomyParentUpdateTest.php
@@ -0,0 +1,101 @@
+db = $this->container->get('database');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setDatabaseDumpFiles() {
+ $this->databaseDumpFiles = [
+ __DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8-rc1.bare.standard.php.gz',
+ __DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.views-taxonomy-parent-2543726.php',
+ ];
+ }
+
+ /**
+ * Tests taxonomy term parents update.
+ *
+ * @see taxonomy_update_8501()
+ * @see taxonomy_update_8502()
+ * @see taxonomy_update_8503()
+ */
+ public function testTaxonomyUpdateParents() {
+ // Force the update hook to only run one term per batch.
+ drupal_rewrite_settings([
+ 'settings' => [
+ 'entity_update_batch_size' => (object) [
+ 'value' => 1,
+ 'required' => TRUE,
+ ],
+ ],
+ ]);
+
+ // Run updates.
+ $this->runUpdates();
+
+ /** @var \Drupal\taxonomy\TermInterface $term */
+ $term = Term::load(1);
+ $parents = [2, 3];
+ $this->assertCount(2, $term->parent);
+ $this->assertTrue(in_array($term->parent[0]->entity->id(), $parents));
+ $this->assertTrue(in_array($term->parent[1]->entity->id(), $parents));
+
+ $term = Term::load(2);
+ $parents = [0, 3];
+ $this->assertCount(2, $term->parent);
+ $this->assertTrue(in_array($term->parent[0]->target_id, $parents));
+ $this->assertTrue(in_array($term->parent[1]->target_id, $parents));
+
+ $term = Term::load(3);
+ $this->assertCount(1, $term->parent);
+ // Target ID is returned as string.
+ $this->assertSame(0, (int) $term->get('parent')[0]->target_id);
+
+ // Test if the view has been converted to use the {taxonomy_term__parent}
+ // table instead of the {taxonomy_term_hierarchy} table.
+ $view = $this->config("views.view.test_taxonomy_parent");
+
+ $relationship_base_path = 'display.default.display_options.relationships.parent';
+ $this->assertSame('taxonomy_term__parent', $view->get("$relationship_base_path.table"));
+ $this->assertSame('parent_target_id', $view->get("$relationship_base_path.field"));
+
+ $filters_base_path_1 = 'display.default.display_options.filters.parent';
+ $this->assertSame('taxonomy_term__parent', $view->get("$filters_base_path_1.table"));
+ $this->assertSame('parent_target_id', $view->get("$filters_base_path_1.field"));
+
+ $filters_base_path_2 = 'display.default.display_options.filters.parent';
+ $this->assertSame('taxonomy_term__parent', $view->get("$filters_base_path_2.table"));
+ $this->assertSame('parent_target_id', $view->get("$filters_base_path_2.field"));
+
+ // The {taxonomy_term_hierarchy} table has been removed.
+ $this->assertFalse($this->db->schema()->tableExists('taxonomy_term_hierarchy'));
+ }
+
+}
diff --git a/core/modules/taxonomy/tests/src/Functional/Update/TaxonomyTermUpdatePathTest.php b/core/modules/taxonomy/tests/src/Functional/Update/TaxonomyTermUpdatePathTest.php
new file mode 100644
index 000000000..55f14915a
--- /dev/null
+++ b/core/modules/taxonomy/tests/src/Functional/Update/TaxonomyTermUpdatePathTest.php
@@ -0,0 +1,139 @@
+databaseDumpFiles = [
+ __DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.filled.standard.php.gz',
+ __DIR__ . '/../../../fixtures/update/drupal-8.views-taxonomy-term-publishing-status-2981887.php',
+ ];
+ }
+
+ /**
+ * Tests the conversion of taxonomy terms to be publishable.
+ *
+ * @see taxonomy_update_8601()
+ */
+ public function testPublishable() {
+ $this->runUpdates();
+
+ // Log in as user 1.
+ $account = User::load(1);
+ $account->passRaw = 'drupal';
+ $this->drupalLogin($account);
+
+ // Make sure our vocabulary exists.
+ $this->drupalGet('admin/structure/taxonomy/manage/test_vocabulary/overview');
+
+ // Make sure our terms exist.
+ $assert_session = $this->assertSession();
+ $assert_session->pageTextContains('Test root term');
+ $assert_session->pageTextContains('Test child term');
+
+ $this->drupalGet('taxonomy/term/3');
+ $assert_session->statusCodeEquals('200');
+
+ // Make sure the terms are still translated.
+ $this->drupalGet('taxonomy/term/2/translations');
+ $assert_session->linkExists('Test root term - Spanish');
+
+ $storage = \Drupal::entityTypeManager()->getStorage('taxonomy_term');
+
+ // Check that the 'content_translation_status' field has been updated
+ // correctly.
+ /** @var \Drupal\taxonomy\TermInterface $term */
+ $term = $storage->load(2);
+ $translation = $term->getTranslation('es');
+ $this->assertTrue($translation->isPublished());
+
+ // Check that taxonomy terms can be created, saved and then loaded.
+ $term = $storage->create([
+ 'name' => 'Test term',
+ 'vid' => 'tags',
+ ]);
+ $term->save();
+
+ $term = $storage->loadUnchanged($term->id());
+
+ $this->assertEquals('Test term', $term->label());
+ $this->assertEquals('tags', $term->bundle());
+ $this->assertTrue($term->isPublished());
+
+ // Check that the term can be unpublished.
+ $term->setUnpublished();
+ $term->save();
+ $term = $storage->loadUnchanged($term->id());
+ $this->assertFalse($term->isPublished());
+
+ // Test the update does not run when a status field already exists.
+ module_load_install('taxonomy');
+ $this->assertEquals('The publishing status field has not been added to taxonomy terms. See this page for more information on how to install it.', (string) taxonomy_update_8601());
+ // Test the message can be overridden.
+ \Drupal::state()->set('taxonomy_update_8601_skip_message', 'Another message');
+ $this->assertEquals('Another message', (string) taxonomy_update_8601());
+ }
+
+ /**
+ * Tests handling of the publishing status in taxonomy term views updates.
+ *
+ * @see taxonomy_post_update_handle_publishing_status_addition_in_views()
+ */
+ public function testPublishingStatusUpdateForTaxonomyTermViews() {
+ // Check that the test view was previously using the
+ // 'content_translation_status' field.
+ $config = \Drupal::config('views.view.test_taxonomy_term_view_with_content_translation_status');
+ $display_options = $config->get('display.default.display_options');
+ $this->assertEquals('content_translation_status', $display_options['fields']['content_translation_status']['field']);
+ $this->assertEquals('content_translation_status', $display_options['filters']['content_translation_status']['field']);
+ $this->assertEquals('content_translation_status', $display_options['sorts']['content_translation_status']['field']);
+
+ // Check a test view without any filter.
+ $config = \Drupal::config('views.view.test_taxonomy_term_view_without_content_translation_status');
+ $display_options = $config->get('display.default.display_options');
+ $this->assertEmpty($display_options['filters']);
+
+ $this->runUpdates();
+
+ // Check that a view which had a field, filter and a sort on the
+ // 'content_translation_status' field has been updated to use the new
+ // 'status' field.
+ $view = View::load('test_taxonomy_term_view_with_content_translation_status');
+ foreach ($view->get('display') as $display) {
+ $this->assertEquals('status', $display['display_options']['fields']['content_translation_status']['field']);
+ $this->assertEquals('status', $display['display_options']['sorts']['content_translation_status']['field']);
+ $this->assertEquals('status', $display['display_options']['filters']['content_translation_status']['field']);
+ }
+
+ // Check that a view without any filters has been updated to include a
+ // filter for the 'status' field.
+ $view = View::load('test_taxonomy_term_view_without_content_translation_status');
+ foreach ($view->get('display') as $display) {
+ $this->assertNotEmpty($display['display_options']['filters']);
+ $this->assertEquals('status', $display['display_options']['filters']['status']['field']);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function replaceUser1() {
+ // Do not replace the user from our dump.
+ }
+
+}
diff --git a/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyDefaultArgumentTest.php b/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyDefaultArgumentTest.php
index 82646c03c..d2eb21625 100644
--- a/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyDefaultArgumentTest.php
+++ b/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyDefaultArgumentTest.php
@@ -2,11 +2,6 @@
namespace Drupal\Tests\taxonomy\Functional\Views;
-use Drupal\field\Entity\FieldConfig;
-use Drupal\views\Views;
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpKernel\HttpKernelInterface;
-
/**
* Tests the representative node relationship for terms.
*
@@ -21,73 +16,6 @@ class TaxonomyDefaultArgumentTest extends TaxonomyTestBase {
*/
public static $testViews = ['taxonomy_default_argument_test'];
- /**
- * Tests the relationship.
- */
- public function testNodePath() {
- $view = Views::getView('taxonomy_default_argument_test');
-
- $request = Request::create($this->nodes[0]->url());
- $request->server->set('SCRIPT_NAME', $GLOBALS['base_path'] . 'index.php');
- $request->server->set('SCRIPT_FILENAME', 'index.php');
-
- $response = $this->container->get('http_kernel')
- ->handle($request, HttpKernelInterface::SUB_REQUEST);
- $view->setRequest($request);
- $view->setResponse($response);
-
- $view->initHandlers();
- $expected = implode(',', [$this->term1->id(), $this->term2->id()]);
- $this->assertEqual($expected, $view->argument['tid']->getDefaultArgument());
- $view->destroy();
- }
-
- public function testNodePathWithViewSelection() {
- // Change the term entity reference field to use a view as selection plugin.
- \Drupal::service('module_installer')->install(['entity_reference_test']);
-
- $field_name = 'field_' . $this->vocabulary->id();
- $field = FieldConfig::loadByName('node', 'article', $field_name);
- $field->setSetting('handler', 'views');
- $field->setSetting('handler_settings', [
- 'view' => [
- 'view_name' => 'test_entity_reference',
- 'display_name' => 'entity_reference_1',
- ],
- ]);
- $field->save();
-
- $view = Views::getView('taxonomy_default_argument_test');
-
- $request = Request::create($this->nodes[0]->url());
- $request->server->set('SCRIPT_NAME', $GLOBALS['base_path'] . 'index.php');
- $request->server->set('SCRIPT_FILENAME', 'index.php');
-
- $response = $this->container->get('http_kernel')->handle($request, HttpKernelInterface::SUB_REQUEST);
- $view->setRequest($request);
- $view->setResponse($response);
-
- $view->initHandlers();
- $expected = implode(',', [$this->term1->id(), $this->term2->id()]);
- $this->assertEqual($expected, $view->argument['tid']->getDefaultArgument());
- }
-
- public function testTermPath() {
- $view = Views::getView('taxonomy_default_argument_test');
-
- $request = Request::create($this->term1->url());
- $request->server->set('SCRIPT_NAME', $GLOBALS['base_path'] . 'index.php');
- $request->server->set('SCRIPT_FILENAME', 'index.php');
-
- $response = $this->container->get('http_kernel')->handle($request, HttpKernelInterface::SUB_REQUEST);
- $view->setRequest($request);
- $view->setResponse($response);
- $view->initHandlers();
-
- $expected = $this->term1->id();
- $this->assertEqual($expected, $view->argument['tid']->getDefaultArgument());
- }
-
/**
* Tests escaping of page title when the taxonomy plugin provides it.
*/
diff --git a/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyIndexTidUiTest.php b/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyIndexTidUiTest.php
index 4de73705d..992e5bee1 100644
--- a/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyIndexTidUiTest.php
+++ b/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyIndexTidUiTest.php
@@ -199,7 +199,7 @@ class TaxonomyIndexTidUiTest extends UITestBase {
// Select 'Term' and 'Vocabulary' as filters.
$edit = [
'name[taxonomy_term_field_data.tid]' => TRUE,
- 'name[taxonomy_term_field_data.vid]' => TRUE
+ 'name[taxonomy_term_field_data.vid]' => TRUE,
];
$this->drupalPostForm('admin/structure/views/nojs/add-handler/test_taxonomy_term_name/default/filter', $edit, 'Add and configure filter criteria');
// Select 'Empty Vocabulary' and 'Autocomplete' from the list of options.
diff --git a/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyRelationshipTest.php b/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyRelationshipTest.php
index 03be3e15b..329996490 100644
--- a/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyRelationshipTest.php
+++ b/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyRelationshipTest.php
@@ -61,24 +61,24 @@ class TaxonomyRelationshipTest extends TaxonomyTestBase {
$this->assertEqual($views_data['table']['join']['taxonomy_term_field_data']['field'], 'tid');
$this->assertEqual($views_data['table']['join']['node_field_data']['left_field'], 'nid');
$this->assertEqual($views_data['table']['join']['node_field_data']['field'], 'nid');
- $this->assertEqual($views_data['table']['join']['taxonomy_term_hierarchy']['left_field'], 'tid');
- $this->assertEqual($views_data['table']['join']['taxonomy_term_hierarchy']['field'], 'tid');
+ $this->assertEqual($views_data['table']['join']['taxonomy_term__parent']['left_field'], 'entity_id');
+ $this->assertEqual($views_data['table']['join']['taxonomy_term__parent']['field'], 'tid');
- // Check the generated views data of taxonomy_term_hierarchy.
- $views_data = Views::viewsData()->get('taxonomy_term_hierarchy');
+ // Check the generated views data of taxonomy_term__parent.
+ $views_data = Views::viewsData()->get('taxonomy_term__parent');
// Check the table join data.
- $this->assertEqual($views_data['table']['join']['taxonomy_term_hierarchy']['left_field'], 'tid');
- $this->assertEqual($views_data['table']['join']['taxonomy_term_hierarchy']['field'], 'parent');
+ $this->assertEqual($views_data['table']['join']['taxonomy_term__parent']['left_field'], 'entity_id');
+ $this->assertEqual($views_data['table']['join']['taxonomy_term__parent']['field'], 'parent_target_id');
$this->assertEqual($views_data['table']['join']['taxonomy_term_field_data']['left_field'], 'tid');
- $this->assertEqual($views_data['table']['join']['taxonomy_term_field_data']['field'], 'tid');
+ $this->assertEqual($views_data['table']['join']['taxonomy_term_field_data']['field'], 'entity_id');
// Check the parent relationship data.
- $this->assertEqual($views_data['parent']['relationship']['base'], 'taxonomy_term_field_data');
- $this->assertEqual($views_data['parent']['relationship']['field'], 'parent');
- $this->assertEqual($views_data['parent']['relationship']['label'], t('Parent'));
- $this->assertEqual($views_data['parent']['relationship']['id'], 'standard');
+ $this->assertEqual($views_data['parent_target_id']['relationship']['base'], 'taxonomy_term_field_data');
+ $this->assertEqual($views_data['parent_target_id']['relationship']['base field'], 'tid');
+ $this->assertEqual($views_data['parent_target_id']['relationship']['label'], t('Parent'));
+ $this->assertEqual($views_data['parent_target_id']['relationship']['id'], 'standard');
// Check the parent filter and argument data.
- $this->assertEqual($views_data['parent']['filter']['id'], 'numeric');
- $this->assertEqual($views_data['parent']['argument']['id'], 'taxonomy');
+ $this->assertEqual($views_data['parent_target_id']['filter']['id'], 'numeric');
+ $this->assertEqual($views_data['parent_target_id']['argument']['id'], 'taxonomy');
// Check an actual test view.
$view = Views::getView('test_taxonomy_term_relationship');
@@ -95,7 +95,7 @@ class TaxonomyRelationshipTest extends TaxonomyTestBase {
if (!$index) {
$this->assertTrue($row->_relationship_entities['parent'] instanceof TermInterface);
$this->assertEqual($row->_relationship_entities['parent']->id(), $this->term2->id());
- $this->assertEqual($row->taxonomy_term_field_data_taxonomy_term_hierarchy_tid, $this->term2->id());
+ $this->assertEqual($row->taxonomy_term_field_data_taxonomy_term__parent_tid, $this->term2->id());
}
$this->assertTrue($row->_relationship_entities['nid'] instanceof NodeInterface);
$this->assertEqual($row->_relationship_entities['nid']->id(), $this->nodes[$index]->id());
diff --git a/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyTermViewTest.php b/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyTermViewTest.php
index f69c49c71..fc0cddce4 100644
--- a/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyTermViewTest.php
+++ b/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyTermViewTest.php
@@ -2,7 +2,6 @@
namespace Drupal\Tests\taxonomy\Functional\Views;
-use Drupal\Component\Utility\Unicode;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\node\Entity\Node;
@@ -50,7 +49,7 @@ class TaxonomyTermViewTest extends TaxonomyTestBase {
// Create a vocabulary and add two term reference fields to article nodes.
- $this->fieldName1 = Unicode::strtolower($this->randomMachineName());
+ $this->fieldName1 = mb_strtolower($this->randomMachineName());
$handler_settings = [
'target_bundles' => [
diff --git a/core/modules/taxonomy/tests/src/Functional/VocabularyCrudTest.php b/core/modules/taxonomy/tests/src/Functional/VocabularyCrudTest.php
index 2ac2d9de6..1fb01630b 100644
--- a/core/modules/taxonomy/tests/src/Functional/VocabularyCrudTest.php
+++ b/core/modules/taxonomy/tests/src/Functional/VocabularyCrudTest.php
@@ -2,7 +2,6 @@
namespace Drupal\Tests\taxonomy\Functional;
-use Drupal\Component\Utility\Unicode;
use Drupal\field\Entity\FieldConfig;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\field\Entity\FieldStorageConfig;
@@ -144,12 +143,12 @@ class VocabularyCrudTest extends TaxonomyTestBase {
public function testUninstallReinstall() {
// Field storages and fields attached to taxonomy term bundles should be
// removed when the module is uninstalled.
- $field_name = Unicode::strtolower($this->randomMachineName() . '_field_name');
+ $field_name = mb_strtolower($this->randomMachineName() . '_field_name');
$storage_definition = [
'field_name' => $field_name,
'entity_type' => 'taxonomy_term',
'type' => 'text',
- 'cardinality' => 4
+ 'cardinality' => 4,
];
FieldStorageConfig::create($storage_definition)->save();
$field_definition = [
@@ -165,7 +164,7 @@ class VocabularyCrudTest extends TaxonomyTestBase {
// installed for testing below.
$this->vocabulary->unsetThirdPartySetting('taxonomy_crud', 'foo');
- require_once \Drupal::root() . '/core/includes/install.inc';
+ require_once $this->root . '/core/includes/install.inc';
$this->container->get('module_installer')->uninstall(['taxonomy']);
$this->container->get('module_installer')->install(['taxonomy']);
diff --git a/core/modules/taxonomy/tests/src/Functional/VocabularyLanguageTest.php b/core/modules/taxonomy/tests/src/Functional/VocabularyLanguageTest.php
index f25df8a1f..c94d64e28 100644
--- a/core/modules/taxonomy/tests/src/Functional/VocabularyLanguageTest.php
+++ b/core/modules/taxonomy/tests/src/Functional/VocabularyLanguageTest.php
@@ -2,7 +2,6 @@
namespace Drupal\Tests\taxonomy\Functional;
-use Drupal\Component\Utility\Unicode;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\language\Entity\ContentLanguageSettings;
@@ -43,7 +42,7 @@ class VocabularyLanguageTest extends TaxonomyTestBase {
$this->assertField('edit-langcode', 'The language selector field was found on the page.');
// Create the vocabulary.
- $vid = Unicode::strtolower($this->randomMachineName());
+ $vid = mb_strtolower($this->randomMachineName());
$edit['name'] = $this->randomMachineName();
$edit['description'] = $this->randomMachineName();
$edit['langcode'] = 'aa';
@@ -72,7 +71,7 @@ class VocabularyLanguageTest extends TaxonomyTestBase {
// the terms are saved.
$edit = [
'name' => $this->randomMachineName(),
- 'vid' => Unicode::strtolower($this->randomMachineName()),
+ 'vid' => mb_strtolower($this->randomMachineName()),
'default_language[langcode]' => 'bb',
'default_language[language_alterable]' => TRUE,
];
diff --git a/core/modules/taxonomy/tests/src/Functional/VocabularyPermissionsTest.php b/core/modules/taxonomy/tests/src/Functional/VocabularyPermissionsTest.php
index 989398e62..bb94a96da 100644
--- a/core/modules/taxonomy/tests/src/Functional/VocabularyPermissionsTest.php
+++ b/core/modules/taxonomy/tests/src/Functional/VocabularyPermissionsTest.php
@@ -83,6 +83,7 @@ class VocabularyPermissionsTest extends TaxonomyTestBase {
$assert_session->linkExists('Add term');
$assert_session->buttonExists('Save');
$assert_session->pageTextContains('Weight');
+ $assert_session->fieldExists('Weight');
$assert_session->pageTextContains($edit_help_text);
// Visit vocabulary overview without terms. 'Add term' should be shown.
@@ -108,7 +109,8 @@ class VocabularyPermissionsTest extends TaxonomyTestBase {
$assert_session->linkNotExists('Edit');
$assert_session->linkNotExists('Delete');
$assert_session->buttonNotExists('Save');
- $assert_session->pageTextNotContains('Weight');
+ $assert_session->pageTextContains('Weight');
+ $assert_session->fieldNotExists('Weight');
$assert_session->linkNotExists('Add term');
$assert_session->pageTextContains($no_edit_help_text);
@@ -132,6 +134,7 @@ class VocabularyPermissionsTest extends TaxonomyTestBase {
$assert_session->linkNotExists('Delete');
$assert_session->buttonExists('Save');
$assert_session->pageTextContains('Weight');
+ $assert_session->fieldExists('Weight');
$assert_session->linkNotExists('Add term');
$assert_session->pageTextContains($edit_help_text);
@@ -154,7 +157,8 @@ class VocabularyPermissionsTest extends TaxonomyTestBase {
$assert_session->linkExists('Delete');
$assert_session->linkNotExists('Add term');
$assert_session->buttonNotExists('Save');
- $assert_session->pageTextNotContains('Weight');
+ $assert_session->pageTextContains('Weight');
+ $assert_session->fieldNotExists('Weight');
$assert_session->pageTextContains($no_edit_help_text);
// Visit vocabulary overview without terms. 'Add term' should not be shown.
@@ -179,6 +183,7 @@ class VocabularyPermissionsTest extends TaxonomyTestBase {
$assert_session->linkNotExists('Add term');
$assert_session->buttonExists('Save');
$assert_session->pageTextContains('Weight');
+ $assert_session->fieldExists('Weight');
$assert_session->pageTextContains($edit_help_text);
// Visit vocabulary overview without terms. 'Add term' should not be shown.
@@ -201,7 +206,8 @@ class VocabularyPermissionsTest extends TaxonomyTestBase {
$assert_session->linkNotExists('Delete');
$assert_session->linkExists('Add term');
$assert_session->buttonNotExists('Save');
- $assert_session->pageTextNotContains('Weight');
+ $assert_session->pageTextContains('Weight');
+ $assert_session->fieldNotExists('Weight');
$assert_session->pageTextContains($no_edit_help_text);
// Visit vocabulary overview without terms. 'Add term' should not be shown.
diff --git a/core/modules/taxonomy/tests/src/Functional/VocabularyTranslationTest.php b/core/modules/taxonomy/tests/src/Functional/VocabularyTranslationTest.php
index 2d312578e..aa98ac333 100644
--- a/core/modules/taxonomy/tests/src/Functional/VocabularyTranslationTest.php
+++ b/core/modules/taxonomy/tests/src/Functional/VocabularyTranslationTest.php
@@ -2,8 +2,6 @@
namespace Drupal\Tests\taxonomy\Functional;
-use Drupal\Component\Utility\Unicode;
-
/**
* Tests content translation for vocabularies.
*
@@ -39,7 +37,7 @@ class VocabularyTranslationTest extends TaxonomyTestBase {
$this->assertField('edit-default-language-content-translation', 'The content translation checkbox is present on the page.');
// Create the vocabulary.
- $vid = Unicode::strtolower($this->randomMachineName());
+ $vid = mb_strtolower($this->randomMachineName());
$edit['name'] = $this->randomMachineName();
$edit['description'] = $this->randomMachineName();
$edit['langcode'] = 'en';
diff --git a/core/modules/taxonomy/tests/src/Functional/VocabularyUiTest.php b/core/modules/taxonomy/tests/src/Functional/VocabularyUiTest.php
index e39a0d855..2620cb8fc 100644
--- a/core/modules/taxonomy/tests/src/Functional/VocabularyUiTest.php
+++ b/core/modules/taxonomy/tests/src/Functional/VocabularyUiTest.php
@@ -2,8 +2,6 @@
namespace Drupal\Tests\taxonomy\Functional;
-use Drupal\Component\Utility\Unicode;
-
use Drupal\Core\Url;
use Drupal\taxonomy\Entity\Vocabulary;
@@ -39,7 +37,7 @@ class VocabularyUiTest extends TaxonomyTestBase {
// Create a new vocabulary.
$this->clickLink(t('Add vocabulary'));
$edit = [];
- $vid = Unicode::strtolower($this->randomMachineName());
+ $vid = mb_strtolower($this->randomMachineName());
$edit['name'] = $this->randomMachineName();
$edit['description'] = $this->randomMachineName();
$edit['vid'] = $vid;
@@ -131,7 +129,7 @@ class VocabularyUiTest extends TaxonomyTestBase {
*/
public function testTaxonomyAdminDeletingVocabulary() {
// Create a vocabulary.
- $vid = Unicode::strtolower($this->randomMachineName());
+ $vid = mb_strtolower($this->randomMachineName());
$edit = [
'name' => $this->randomMachineName(),
'vid' => $vid,
diff --git a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTaxonomyTermTest.php b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTaxonomyTermTest.php
index 14fba563b..75c79241e 100644
--- a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTaxonomyTermTest.php
+++ b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTaxonomyTermTest.php
@@ -92,7 +92,7 @@ class MigrateTaxonomyTermTest extends MigrateDrupal6TestBase {
$this->assertSame($values['vid'], $term->vid->target_id);
$this->assertSame((string) $values['weight'], $term->weight->value);
if ($values['parent'] === [0]) {
- $this->assertNull($term->parent->target_id);
+ $this->assertSame(0, (int) $term->parent->target_id);
}
else {
$parents = [];
diff --git a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTaxonomyVocabularyTest.php b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTaxonomyVocabularyTest.php
index 79e188b9e..1584c5cc7 100644
--- a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTaxonomyVocabularyTest.php
+++ b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTaxonomyVocabularyTest.php
@@ -32,7 +32,7 @@ class MigrateTaxonomyVocabularyTest extends MigrateDrupal6TestBase {
for ($i = 0; $i < 3; $i++) {
$j = $i + 1;
$vocabulary = Vocabulary::load("vocabulary_{$j}_i_{$i}_");
- $this->assertSame($this->getMigration('d6_taxonomy_vocabulary')->getIdMap()->lookupDestinationID([$j]), [$vocabulary->id()]);
+ $this->assertSame($this->getMigration('d6_taxonomy_vocabulary')->getIdMap()->lookupDestinationId([$j]), [$vocabulary->id()]);
$this->assertSame("vocabulary $j (i=$i)", $vocabulary->label());
$this->assertSame("description of vocabulary $j (i=$i)", $vocabulary->getDescription());
$this->assertSame($i, $vocabulary->getHierarchy());
diff --git a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTaxonomyVocabularyTranslationTest.php b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTaxonomyVocabularyTranslationTest.php
index 3993abb98..9d7176c49 100644
--- a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTaxonomyVocabularyTranslationTest.php
+++ b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTaxonomyVocabularyTranslationTest.php
@@ -14,7 +14,13 @@ class MigrateTaxonomyVocabularyTranslationTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
- public static $modules = ['language', 'taxonomy'];
+ public static $modules = [
+ 'config_translation',
+ 'language',
+ 'taxonomy',
+ // Required for translation migrations.
+ 'migrate_drupal_multilingual',
+ ];
/**
* {@inheritdoc}
diff --git a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTermNodeRevisionTest.php b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTermNodeRevisionTest.php
index 614725d96..25544486a 100644
--- a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTermNodeRevisionTest.php
+++ b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTermNodeRevisionTest.php
@@ -31,7 +31,7 @@ class MigrateTermNodeRevisionTest extends MigrateDrupal6TestBase {
* Tests the Drupal 6 term-node revision association to Drupal 8 migration.
*/
public function testTermRevisionNode() {
- $node = \Drupal::entityManager()->getStorage('node')->loadRevision(2);
+ $node = \Drupal::entityManager()->getStorage('node')->loadRevision(2001);
$this->assertSame(2, count($node->field_vocabulary_3_i_2_));
$this->assertSame('4', $node->field_vocabulary_3_i_2_[0]->target_id);
$this->assertSame('5', $node->field_vocabulary_3_i_2_[1]->target_id);
diff --git a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateVocabularyEntityDisplayTest.php b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateVocabularyEntityDisplayTest.php
index 5b8a63887..6b6ee945c 100644
--- a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateVocabularyEntityDisplayTest.php
+++ b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateVocabularyEntityDisplayTest.php
@@ -45,7 +45,7 @@ class MigrateVocabularyEntityDisplayTest extends MigrateDrupal6TestBase {
$this->assertSame('entity_reference_label', $component['type']);
$this->assertSame(20, $component['weight']);
// Test the Id map.
- $this->assertSame(['node', 'article', 'default', 'field_tags'], $this->getMigration('d6_vocabulary_entity_display')->getIdMap()->lookupDestinationID([4, 'article']));
+ $this->assertSame(['node', 'article', 'default', 'field_tags'], $this->getMigration('d6_vocabulary_entity_display')->getIdMap()->lookupDestinationId([4, 'article']));
// Tests that a vocabulary named like a D8 base field will be migrated and
// prefixed with 'field_' to avoid conflicts.
diff --git a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateVocabularyEntityFormDisplayTest.php b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateVocabularyEntityFormDisplayTest.php
index 2c96e5176..7cfc306a9 100644
--- a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateVocabularyEntityFormDisplayTest.php
+++ b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateVocabularyEntityFormDisplayTest.php
@@ -45,7 +45,7 @@ class MigrateVocabularyEntityFormDisplayTest extends MigrateDrupal6TestBase {
$this->assertSame('options_select', $component['type']);
$this->assertSame(20, $component['weight']);
// Test the Id map.
- $this->assertSame(['node', 'article', 'default', 'field_tags'], $this->getMigration('d6_vocabulary_entity_form_display')->getIdMap()->lookupDestinationID([4, 'article']));
+ $this->assertSame(['node', 'article', 'default', 'field_tags'], $this->getMigration('d6_vocabulary_entity_form_display')->getIdMap()->lookupDestinationId([4, 'article']));
// Test the term widget tags setting.
$entity_form_display = EntityFormDisplay::load('node.story.default');
diff --git a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateVocabularyFieldInstanceTest.php b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateVocabularyFieldInstanceTest.php
index 26cd7f581..77cb3e906 100644
--- a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateVocabularyFieldInstanceTest.php
+++ b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateVocabularyFieldInstanceTest.php
@@ -58,7 +58,7 @@ class MigrateVocabularyFieldInstanceTest extends MigrateDrupal6TestBase {
$this->assertSame(['field_tags'], $settings['handler_settings']['target_bundles'], 'The target_bundles handler setting is correct.');
$this->assertSame(TRUE, $settings['handler_settings']['auto_create'], 'The "auto_create" setting is correct.');
- $this->assertSame(['node', 'article', 'field_tags'], $this->getMigration('d6_vocabulary_field_instance')->getIdMap()->lookupDestinationID([4, 'article']));
+ $this->assertSame(['node', 'article', 'field_tags'], $this->getMigration('d6_vocabulary_field_instance')->getIdMap()->lookupDestinationId([4, 'article']));
// Test the the field vocabulary_1_i_0_.
$field_id = 'node.story.field_vocabulary_1_i_0_';
diff --git a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateVocabularyFieldTest.php b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateVocabularyFieldTest.php
index d294ec481..fa5c7f3a1 100644
--- a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateVocabularyFieldTest.php
+++ b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateVocabularyFieldTest.php
@@ -39,7 +39,7 @@ class MigrateVocabularyFieldTest extends MigrateDrupal6TestBase {
$this->assertSame('taxonomy_term', $settings['target_type'], "Target type is correct.");
$this->assertSame(1, $field_storage->getCardinality(), "Field cardinality in 1.");
- $this->assertSame(['node', 'field_tags'], $this->getMigration('d6_vocabulary_field')->getIdMap()->lookupDestinationID([4]), "Test IdMap");
+ $this->assertSame(['node', 'field_tags'], $this->getMigration('d6_vocabulary_field')->getIdMap()->lookupDestinationId([4]), "Test IdMap");
// Tests that a vocabulary named like a D8 base field will be migrated and
// prefixed with 'field_' to avoid conflicts.
diff --git a/core/modules/taxonomy/tests/src/Kernel/Migrate/d7/MigrateTaxonomyTermTest.php b/core/modules/taxonomy/tests/src/Kernel/Migrate/d7/MigrateTaxonomyTermTest.php
index 474969dde..e4246594a 100644
--- a/core/modules/taxonomy/tests/src/Kernel/Migrate/d7/MigrateTaxonomyTermTest.php
+++ b/core/modules/taxonomy/tests/src/Kernel/Migrate/d7/MigrateTaxonomyTermTest.php
@@ -47,7 +47,7 @@ class MigrateTaxonomyTermTest extends MigrateDrupal7TestBase {
'd7_field',
'd7_taxonomy_vocabulary',
'd7_field_instance',
- 'd7_taxonomy_term'
+ 'd7_taxonomy_term',
]);
}
@@ -104,7 +104,11 @@ class MigrateTaxonomyTermTest extends MigrateDrupal7TestBase {
*/
public function testTaxonomyTerms() {
$this->assertEntity(1, 'General discussion', 'forums', '', NULL, 2);
- $this->assertEntity(2, 'Term1', 'test_vocabulary', 'The first term.', 'filtered_html', 0, [], NULL, 3);
+
+ // Tests that terms that used the Drupal 7 Title module and that have their
+ // name and description replaced by real fields are correctly migrated.
+ $this->assertEntity(2, 'Term1 (This is a real field!)', 'test_vocabulary', 'The first term. (This is a real field!)', 'filtered_html', 0, [], NULL, 3);
+
$this->assertEntity(3, 'Term2', 'test_vocabulary', 'The second term.', 'filtered_html');
$this->assertEntity(4, 'Term3', 'test_vocabulary', 'The third term.', 'full_html', 0, [3], 6);
$this->assertEntity(5, 'Custom Forum', 'forums', 'Where the cool kids are.', NULL, 3);
diff --git a/core/modules/taxonomy/tests/src/Kernel/Plugin/migrate/source/d7/TermSourceWithVocabularyFilterTest.php b/core/modules/taxonomy/tests/src/Kernel/Plugin/migrate/source/d7/TermSourceWithVocabularyFilterTest.php
index e03a6ab56..671bb554e 100644
--- a/core/modules/taxonomy/tests/src/Kernel/Plugin/migrate/source/d7/TermSourceWithVocabularyFilterTest.php
+++ b/core/modules/taxonomy/tests/src/Kernel/Plugin/migrate/source/d7/TermSourceWithVocabularyFilterTest.php
@@ -27,16 +27,16 @@ class TermSourceWithVocabularyFilterTest extends TermTest {
[
'tid' => 1,
'vid' => 5,
- 'name' => 'name value 1',
- 'description' => 'description value 1',
+ 'name' => 'name value 1 (name_field)',
+ 'description' => 'description value 1 (description_field)',
'weight' => 0,
'parent' => [0],
],
[
'tid' => 4,
'vid' => 5,
- 'name' => 'name value 4',
- 'description' => 'description value 4',
+ 'name' => 'name value 4 (name_field)',
+ 'description' => 'description value 4 (description_field)',
'weight' => 1,
'parent' => [1],
],
diff --git a/core/modules/taxonomy/tests/src/Kernel/Plugin/migrate/source/d7/TermTest.php b/core/modules/taxonomy/tests/src/Kernel/Plugin/migrate/source/d7/TermTest.php
index dd22672a0..ef002cfec 100644
--- a/core/modules/taxonomy/tests/src/Kernel/Plugin/migrate/source/d7/TermTest.php
+++ b/core/modules/taxonomy/tests/src/Kernel/Plugin/migrate/source/d7/TermTest.php
@@ -126,19 +126,57 @@ class TermTest extends MigrateSqlSourceTestBase {
'machine_name' => 'categories',
],
];
+ $tests[0]['source_data']['field_config'] = [
+ [
+ 'id' => '3',
+ 'translatable' => '0',
+ ],
+ [
+ 'id' => '4',
+ 'translatable' => '1',
+ ],
+ [
+ 'id' => '5',
+ 'translatable' => '1',
+ ],
+ ];
$tests[0]['source_data']['field_config_instance'] = [
[
+ 'id' => '2',
+ 'field_id' => 3,
'field_name' => 'field_term_field',
'entity_type' => 'taxonomy_term',
'bundle' => 'tags',
+ 'data' => 'a:0:{}',
'deleted' => 0,
],
[
+ 'id' => '3',
+ 'field_id' => 3,
'field_name' => 'field_term_field',
'entity_type' => 'taxonomy_term',
'bundle' => 'categories',
+ 'data' => 'a:0:{}',
'deleted' => 0,
],
+ [
+ 'id' => '4',
+ 'field_id' => '4',
+ 'field_name' => 'name_field',
+ 'entity_type' => 'taxonomy_term',
+ 'bundle' => 'tags',
+ 'data' => 'a:0:{}',
+ 'deleted' => '0',
+ ],
+ [
+ 'id' => '5',
+ 'field_id' => '5',
+ 'field_name' => 'description_field',
+ 'entity_type' => 'taxonomy_term',
+ 'bundle' => 'tags',
+ 'data' => 'a:0:{}',
+ 'deleted' => '0',
+ ],
];
$tests[0]['source_data']['field_data_field_term_field'] = [
[
@@ -156,6 +194,61 @@ class TermTest extends MigrateSqlSourceTestBase {
'delta' => 0,
],
];
+ $tests[0]['source_data']['field_data_name_field'] = [
+ [
+ 'entity_type' => 'taxonomy_term',
+ 'bundle' => 'tags',
+ 'deleted' => '0',
+ 'entity_id' => '1',
+ 'revision_id' => '1',
+ 'language' => 'und',
+ 'delta' => '0',
+ 'name_field_value' => 'name value 1 (name_field)',
+ 'name_field_format' => NULL,
+ ],
+ [
+ 'entity_type' => 'taxonomy_term',
+ 'bundle' => 'tags',
+ 'deleted' => '0',
+ 'entity_id' => '4',
+ 'revision_id' => '4',
+ 'language' => 'und',
+ 'delta' => '0',
+ 'name_field_value' => 'name value 4 (name_field)',
+ 'name_field_format' => NULL,
+ ],
+ ];
+ $tests[0]['source_data']['field_data_description_field'] = [
+ [
+ 'entity_type' => 'taxonomy_term',
+ 'bundle' => 'tags',
+ 'deleted' => '0',
+ 'entity_id' => '1',
+ 'revision_id' => '1',
+ 'language' => 'und',
+ 'delta' => '0',
+ 'description_field_value' => 'description value 1 (description_field)',
+ 'description_field_format' => NULL,
+ ],
+ [
+ 'entity_type' => 'taxonomy_term',
+ 'bundle' => 'tags',
+ 'deleted' => '0',
+ 'entity_id' => '4',
+ 'revision_id' => '4',
+ 'language' => 'und',
+ 'delta' => '0',
+ 'description_field_value' => 'description value 4 (description_field)',
+ 'description_field_format' => NULL,
+ ],
+ ];
+ $tests[0]['source_data']['system'] = [
+ [
+ 'name' => 'title',
+ 'type' => 'module',
+ 'status' => 1,
+ ],
+ ];
$tests[0]['source_data']['variable'] = [
[
'name' => 'forum_containers',
@@ -168,8 +261,8 @@ class TermTest extends MigrateSqlSourceTestBase {
[
'tid' => 1,
'vid' => 5,
- 'name' => 'name value 1',
- 'description' => 'description value 1',
+ 'name' => 'name value 1 (name_field)',
+ 'description' => 'description value 1 (description_field)',
'weight' => 0,
'parent' => [0],
],
@@ -192,8 +285,8 @@ class TermTest extends MigrateSqlSourceTestBase {
[
'tid' => 4,
'vid' => 5,
- 'name' => 'name value 4',
- 'description' => 'description value 4',
+ 'name' => 'name value 4 (name_field)',
+ 'description' => 'description value 4 (description_field)',
'weight' => 1,
'parent' => [1],
],
@@ -233,16 +326,16 @@ class TermTest extends MigrateSqlSourceTestBase {
[
'tid' => 1,
'vid' => 5,
- 'name' => 'name value 1',
- 'description' => 'description value 1',
+ 'name' => 'name value 1 (name_field)',
+ 'description' => 'description value 1 (description_field)',
'weight' => 0,
'parent' => [0],
],
[
'tid' => 4,
'vid' => 5,
- 'name' => 'name value 4',
- 'description' => 'description value 4',
+ 'name' => 'name value 4 (name_field)',
+ 'description' => 'description value 4 (description_field)',
'weight' => 1,
'parent' => [1],
],
diff --git a/core/modules/taxonomy/tests/src/Kernel/Views/TaxonomyDefaultArgumentTest.php b/core/modules/taxonomy/tests/src/Kernel/Views/TaxonomyDefaultArgumentTest.php
new file mode 100644
index 000000000..3b9ba26d8
--- /dev/null
+++ b/core/modules/taxonomy/tests/src/Kernel/Views/TaxonomyDefaultArgumentTest.php
@@ -0,0 +1,93 @@
+server->set('SCRIPT_NAME', $GLOBALS['base_path'] . 'index.php');
+ $request->server->set('SCRIPT_FILENAME', 'index.php');
+
+ $response = $this->container->get('http_kernel')
+ ->handle($request, HttpKernelInterface::SUB_REQUEST);
+
+ $view->setRequest($request);
+ $view->setResponse($response);
+ $view->initHandlers();
+
+ return $view;
+ }
+
+ /**
+ * Tests the relationship.
+ */
+ public function testNodePath() {
+ $view = $this->initViewWithRequest($this->nodes[0]->url());
+
+ $expected = implode(',', [$this->term1->id(), $this->term2->id()]);
+ $this->assertEqual($expected, $view->argument['tid']->getDefaultArgument());
+ $view->destroy();
+ }
+
+ public function testNodePathWithViewSelection() {
+ // Change the term entity reference field to use a view as selection plugin.
+ \Drupal::service('module_installer')->install(['entity_reference_test']);
+
+ $field_name = 'field_' . $this->vocabulary->id();
+ $field = FieldConfig::loadByName('node', 'article', $field_name);
+ $field->setSetting('handler', 'views');
+ $field->setSetting('handler_settings', [
+ 'view' => [
+ 'view_name' => 'test_entity_reference',
+ 'display_name' => 'entity_reference_1',
+ ],
+ ]);
+ $field->save();
+
+ $view = $this->initViewWithRequest($this->nodes[0]->url());
+
+ $expected = implode(',', [$this->term1->id(), $this->term2->id()]);
+ $this->assertEqual($expected, $view->argument['tid']->getDefaultArgument());
+ }
+
+ public function testTermPath() {
+ $view = $this->initViewWithRequest($this->term1->url());
+
+ $expected = $this->term1->id();
+ $this->assertEqual($expected, $view->argument['tid']->getDefaultArgument());
+ }
+
+}
diff --git a/core/modules/taxonomy/tests/src/Kernel/Views/TaxonomyFieldVidTest.php b/core/modules/taxonomy/tests/src/Kernel/Views/TaxonomyFieldVidTest.php
new file mode 100644
index 000000000..2fde283e3
--- /dev/null
+++ b/core/modules/taxonomy/tests/src/Kernel/Views/TaxonomyFieldVidTest.php
@@ -0,0 +1,92 @@
+installEntitySchema('taxonomy_term');
+ $this->installEntitySchema('user');
+ $this->installConfig(['filter']);
+
+ /** @var \Drupal\taxonomy\Entity\Vocabulary $vocabulary */
+ $vocabulary = $this->createVocabulary();
+ $this->term1 = $this->createTerm($vocabulary);
+
+ // Create user 1 and set is as the logged in user, so that the logged in
+ // user has the correct permissions to view the vocabulary name.
+ $this->adminUser = User::create(['name' => $this->randomString()]);
+ $this->adminUser->save();
+ $this->container->get('current_user')->setAccount($this->adminUser);
+
+ ViewTestData::createTestViews(get_class($this), ['taxonomy_test_views']);
+ }
+
+ /**
+ * Tests the field handling for the Vocabulary ID.
+ */
+ public function testViewsHandlerVidField() {
+ /** @var \Drupal\Core\Render\RendererInterface $renderer */
+ $renderer = \Drupal::service('renderer');
+
+ $view = Views::getView('test_taxonomy_vid_field');
+ $this->executeView($view);
+
+ $actual = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+ return $view->field['vid']->advancedRender($view->result[0]);
+ });
+ $vocabulary = Vocabulary::load($this->term1->bundle());
+ $expected = $vocabulary->get('name');
+
+ $this->assertEquals($expected, $actual);
+ }
+
+}
diff --git a/core/modules/taxonomy/tests/src/Kernel/Views/TaxonomyTestBase.php b/core/modules/taxonomy/tests/src/Kernel/Views/TaxonomyTestBase.php
new file mode 100644
index 000000000..a9f4c474b
--- /dev/null
+++ b/core/modules/taxonomy/tests/src/Kernel/Views/TaxonomyTestBase.php
@@ -0,0 +1,184 @@
+installConfig(['node', 'filter']);
+ $this->installEntitySchema('user');
+ $this->installEntitySchema('taxonomy_term');
+ $this->mockStandardInstall();
+
+ if ($import_test_views) {
+ ViewTestData::createTestViews(get_class($this), ['taxonomy_test_views']);
+ }
+
+ $this->term1 = $this->createTerm();
+ $this->term2 = $this->createTerm();
+
+ $node = [];
+ $node['type'] = 'article';
+ $node['field_views_testing_tags'][]['target_id'] = $this->term1->id();
+ $node['field_views_testing_tags'][]['target_id'] = $this->term2->id();
+ $this->nodes[] = $this->drupalCreateNode($node);
+ $this->nodes[] = $this->drupalCreateNode($node);
+ }
+
+ /**
+ * Provides a workaround for the inability to use the standard profile.
+ *
+ * @see https://www.drupal.org/node/1708692
+ */
+ protected function mockStandardInstall() {
+ $this->drupalCreateContentType([
+ 'type' => 'article',
+ ]);
+
+ // Create the vocabulary for the tag field.
+ $this->vocabulary = Vocabulary::create([
+ 'name' => 'Views testing tags',
+ 'vid' => 'views_testing_tags',
+ ]);
+ $this->vocabulary->save();
+ $field_name = 'field_' . $this->vocabulary->id();
+
+ $handler_settings = [
+ 'target_bundles' => [
+ $this->vocabulary->id() => $this->vocabulary->id(),
+ ],
+ 'auto_create' => TRUE,
+ ];
+
+ $this->installEntitySchema('node');
+ $this->createEntityReferenceField('node', 'article', $field_name, 'Tags', 'taxonomy_term', 'default', $handler_settings, FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
+ $entity_type_manager = $this->container->get('entity_type.manager');
+ $entity_type_manager
+ ->getStorage('entity_form_display')
+ ->load('node.article.default')
+ ->setComponent($field_name, [
+ 'type' => 'entity_reference_autocomplete_tags',
+ 'weight' => -4,
+ ])
+ ->save();
+
+ $view_modes = [
+ 'default',
+ 'teaser',
+ ];
+ foreach ($view_modes as $view_mode) {
+ $entity_type_manager
+ ->getStorage('entity_view_display')
+ ->load("node.article.{$view_mode}")
+ ->setComponent($field_name, [
+ 'type' => 'entity_reference_label',
+ 'weight' => 10,
+ ])
+ ->save();
+ }
+ }
+
+ /**
+ * Creates and returns a taxonomy term.
+ *
+ * @param array $settings
+ * (optional) An array of values to override the following default
+ * properties of the term:
+ * - name: A random string.
+ * - description: A random string.
+ * - format: First available text format.
+ * - vid: Vocabulary ID of self::$vocabulary object.
+ * - langcode: LANGCODE_NOT_SPECIFIED.
+ * Defaults to an empty array.
+ *
+ * @return \Drupal\taxonomy\Entity\Term
+ * The created taxonomy term.
+ */
+ protected function createTerm(array $settings = []) {
+ $filter_formats = filter_formats();
+ $format = array_pop($filter_formats);
+ $settings += [
+ 'name' => $this->randomMachineName(),
+ 'description' => $this->randomMachineName(),
+ // Use the first available text format.
+ 'format' => $format->id(),
+ 'vid' => $this->vocabulary->id(),
+ 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
+ ];
+ $term = Term::create($settings);
+ $term->save();
+ return $term;
+ }
+
+}
diff --git a/core/modules/taxonomy/tests/src/Unit/Plugin/migrate/cckfield/TaxonomyTermReferenceCckLegacyTest.php b/core/modules/taxonomy/tests/src/Unit/Plugin/migrate/cckfield/TaxonomyTermReferenceCckLegacyTest.php
new file mode 100644
index 000000000..003185adb
--- /dev/null
+++ b/core/modules/taxonomy/tests/src/Unit/Plugin/migrate/cckfield/TaxonomyTermReferenceCckLegacyTest.php
@@ -0,0 +1,18 @@
+migration = $migration->reveal();
}
- /**
- * @covers ::processCckFieldValues
- */
public function testProcessCckFieldValues() {
- $this->plugin->processFieldValues($this->migration, 'somefieldname', []);
+ $this->testDefineValueProcessPipeline('processCckFieldValues');
+ }
+
+ /**
+ * @covers ::defineValueProcessPipeline
+ */
+ public function testDefineValueProcessPipeline($method = 'defineValueProcessPipeline') {
+ $this->plugin->$method($this->migration, 'somefieldname', []);
$expected = [
'plugin' => 'sub_process',
diff --git a/core/modules/taxonomy/tests/src/Unit/Plugin/migrate/field/TaxonomyTermReferenceFieldLegacyTest.php b/core/modules/taxonomy/tests/src/Unit/Plugin/migrate/field/TaxonomyTermReferenceFieldLegacyTest.php
new file mode 100644
index 000000000..60b1cab00
--- /dev/null
+++ b/core/modules/taxonomy/tests/src/Unit/Plugin/migrate/field/TaxonomyTermReferenceFieldLegacyTest.php
@@ -0,0 +1,18 @@
+prophesize(MigrationInterface::class);
- // The plugin's processFieldValues() method will call
+ // The plugin's defineValueProcessPipeline() method will call
// setProcessOfProperty() and return nothing. So, in order to examine the
// process pipeline created by the plugin, we need to ensure that
// getProcess() always returns the last input to setProcessOfProperty().
@@ -44,10 +44,10 @@ class TaxonomyTermReferenceFieldTest extends UnitTestCase {
}
/**
- * @covers ::processFieldValues
+ * @covers ::defineValueProcessPipeline
*/
- public function testProcessFieldValues() {
- $this->plugin->processFieldValues($this->migration, 'somefieldname', []);
+ public function testDefineValueProcessPipeline($method = 'defineValueProcessPipeline') {
+ $this->plugin->$method($this->migration, 'somefieldname', []);
$expected = [
'plugin' => 'sub_process',
diff --git a/core/modules/telephone/src/Plugin/Field/FieldType/TelephoneItem.php b/core/modules/telephone/src/Plugin/Field/FieldType/TelephoneItem.php
index f2aa1ccdd..17fc9d5e4 100644
--- a/core/modules/telephone/src/Plugin/Field/FieldType/TelephoneItem.php
+++ b/core/modules/telephone/src/Plugin/Field/FieldType/TelephoneItem.php
@@ -67,7 +67,7 @@ class TelephoneItem extends FieldItemBase {
'Length' => [
'max' => $max_length,
'maxMessage' => t('%name: the telephone number may not be longer than @max characters.', ['%name' => $this->getFieldDefinition()->getLabel(), '@max' => $max_length]),
- ]
+ ],
],
]);
diff --git a/core/modules/telephone/telephone.info.yml b/core/modules/telephone/telephone.info.yml
index 3e27ccba8..ab44ec026 100644
--- a/core/modules/telephone/telephone.info.yml
+++ b/core/modules/telephone/telephone.info.yml
@@ -5,4 +5,4 @@ package: Field types
version: VERSION
core: 8.x
dependencies:
- - field
+ - drupal:field
diff --git a/core/modules/telephone/tests/src/Functional/TelephoneFieldTest.php b/core/modules/telephone/tests/src/Functional/TelephoneFieldTest.php
index 18bedf072..75e626f66 100644
--- a/core/modules/telephone/tests/src/Functional/TelephoneFieldTest.php
+++ b/core/modules/telephone/tests/src/Functional/TelephoneFieldTest.php
@@ -21,7 +21,7 @@ class TelephoneFieldTest extends BrowserTestBase {
public static $modules = [
'field',
'node',
- 'telephone'
+ 'telephone',
];
/**
diff --git a/core/modules/text/src/Plugin/Field/FieldType/TextItem.php b/core/modules/text/src/Plugin/Field/FieldType/TextItem.php
index 447a0b180..dcee58346 100644
--- a/core/modules/text/src/Plugin/Field/FieldType/TextItem.php
+++ b/core/modules/text/src/Plugin/Field/FieldType/TextItem.php
@@ -62,7 +62,7 @@ class TextItem extends TextItemBase {
'Length' => [
'max' => $max_length,
'maxMessage' => t('%name: the text may not be longer than @max characters.', ['%name' => $this->getFieldDefinition()->getLabel(), '@max' => $max_length]),
- ]
+ ],
],
]);
}
diff --git a/core/modules/text/src/Plugin/migrate/field/d6/TextField.php b/core/modules/text/src/Plugin/migrate/field/d6/TextField.php
index aa324b6f7..dba6398a0 100644
--- a/core/modules/text/src/Plugin/migrate/field/d6/TextField.php
+++ b/core/modules/text/src/Plugin/migrate/field/d6/TextField.php
@@ -44,7 +44,7 @@ class TextField extends FieldPluginBase {
/**
* {@inheritdoc}
*/
- public function processFieldValues(MigrationInterface $migration, $field_name, $field_info) {
+ public function defineValueProcessPipeline(MigrationInterface $migration, $field_name, $field_info) {
$widget_type = isset($field_info['widget_type']) ? $field_info['widget_type'] : $field_info['widget']['type'];
if ($widget_type == 'optionwidgets_onoff') {
diff --git a/core/modules/text/src/Tests/TextFieldTest.php b/core/modules/text/tests/src/Functional/TextFieldTest.php
similarity index 88%
rename from core/modules/text/src/Tests/TextFieldTest.php
rename to core/modules/text/tests/src/Functional/TextFieldTest.php
index f3fb43f2d..ac01dd78a 100644
--- a/core/modules/text/src/Tests/TextFieldTest.php
+++ b/core/modules/text/tests/src/Functional/TextFieldTest.php
@@ -1,13 +1,14 @@
randomMachineName());
+ $field_name = mb_strtolower($this->randomMachineName());
$field_storage = FieldStorageConfig::create([
'field_name' => $field_name,
'entity_type' => 'entity_test',
'type' => 'text',
'settings' => [
'max_length' => $max_length,
- ]
+ ],
]);
$field_storage->save();
FieldConfig::create([
@@ -90,7 +95,7 @@ class TextFieldTest extends StringFieldTest {
$field_storage = FieldStorageConfig::create([
'field_name' => $file_field_name,
'entity_type' => 'entity_test',
- 'type' => 'file'
+ 'type' => 'file',
]);
$field_storage->save();
FieldConfig::create([
@@ -117,7 +122,7 @@ class TextFieldTest extends StringFieldTest {
$this->drupalPostForm('entity_test/add', $edit, 'Upload');
$this->assertResponse(200);
$edit = [
- 'text_long[0][value]' => 'Long text'
+ 'text_long[0][value]' => 'Long text',
];
$this->drupalPostForm(NULL, $edit, 'Save');
$this->assertResponse(200);
@@ -149,11 +154,11 @@ class TextFieldTest extends StringFieldTest {
$renderer = $this->container->get('renderer');
// Create a field.
- $field_name = Unicode::strtolower($this->randomMachineName());
+ $field_name = mb_strtolower($this->randomMachineName());
$field_storage = FieldStorageConfig::create([
'field_name' => $field_name,
'entity_type' => 'entity_test',
- 'type' => $field_type
+ 'type' => $field_type,
]);
$field_storage->save();
FieldConfig::create([
@@ -191,7 +196,7 @@ class TextFieldTest extends StringFieldTest {
"{$field_name}[0][value]" => $value,
];
$this->drupalPostForm(NULL, $edit, t('Save'));
- preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
+ preg_match('|entity_test/manage/(\d+)|', $this->getUrl(), $match);
$id = $match[1];
$this->assertText(t('entity_test @id has been created.', ['@id' => $id]), 'Entity was created');
@@ -199,15 +204,15 @@ class TextFieldTest extends StringFieldTest {
$entity = EntityTest::load($id);
$display = entity_get_display($entity->getEntityTypeId(), $entity->bundle(), 'full');
$content = $display->build($entity);
- $this->setRawContent($renderer->renderRoot($content));
- $this->assertNoRaw($value, 'HTML tags are not displayed.');
- $this->assertEscaped($value, 'Escaped HTML is displayed correctly.');
+ $rendered_entity = \Drupal::service('renderer')->renderRoot($content);
+ $this->assertNotContains($value, (string) $rendered_entity);
+ $this->assertContains(Html::escape($value), (string) $rendered_entity);
// Create a new text format that does not escape HTML, and grant the user
// access to it.
$this->drupalLogin($this->adminUser);
$edit = [
- 'format' => Unicode::strtolower($this->randomMachineName()),
+ 'format' => mb_strtolower($this->randomMachineName()),
'name' => $this->randomMachineName(),
];
$this->drupalPostForm('admin/config/content/formats/add', $edit, t('Save configuration'));
@@ -238,8 +243,8 @@ class TextFieldTest extends StringFieldTest {
$entity = EntityTest::load($id);
$display = entity_get_display($entity->getEntityTypeId(), $entity->bundle(), 'full');
$content = $display->build($entity);
- $this->setRawContent($renderer->renderRoot($content));
- $this->assertRaw($value, 'Value is displayed unfiltered');
+ $rendered_entity = \Drupal::service('renderer')->renderRoot($content);
+ $this->assertContains($value, (string) $rendered_entity);
}
}
diff --git a/core/modules/text/tests/src/FunctionalJavascript/TextareaWithSummaryTest.php b/core/modules/text/tests/src/FunctionalJavascript/TextareaWithSummaryTest.php
new file mode 100644
index 000000000..88153abce
--- /dev/null
+++ b/core/modules/text/tests/src/FunctionalJavascript/TextareaWithSummaryTest.php
@@ -0,0 +1,85 @@
+drupalCreateContentType(['type' => 'page']);
+
+ $account = $this->drupalCreateUser(['create page content', 'edit own page content']);
+ $this->drupalLogin($account);
+ }
+
+ /**
+ * Helper to test toggling the summary area.
+ */
+ protected function assertSummaryToggle() {
+ $this->drupalGet('node/add/page');
+ $widget = $this->getSession()->getPage()->findById('edit-body-wrapper');
+ $summary_field = $widget->findField('edit-body-0-summary');
+
+ $this->assertEquals(FALSE, $summary_field->isVisible(), 'Summary field is hidden by default.');
+ $this->assertEquals(FALSE, $widget->hasButton('Hide summary'), 'No Hide summary link by default.');
+
+ $widget->pressButton('Edit summary');
+ $this->assertEquals(FALSE, $widget->hasButton('Edit summary'), 'Edit summary link is removed after clicking.');
+ $this->assertEquals(TRUE, $summary_field->isVisible(), 'Summary field is shown.');
+
+ $widget->pressButton('Hide summary');
+ $this->assertEquals(FALSE, $widget->hasButton('Hide summary'), 'Hide summary link is removed after clicking.');
+ $this->assertEquals(FALSE, $summary_field->isVisible(), 'Summary field is hidden again.');
+ $this->assertEquals(TRUE, $widget->hasButton('Edit summary'), 'Edit summary link is visible again.');
+ }
+
+ /**
+ * Tests the textSummary javascript behavior.
+ */
+ public function testTextSummaryBehavior() {
+ // Test with field defaults.
+ $this->assertSummaryToggle();
+
+ // Repeat test with non-empty field description.
+ $body_field = FieldConfig::loadByName('node', 'page', 'body');
+ $body_field->set('description', 'Text with Summary field description.');
+ $body_field->save();
+
+ $this->assertSummaryToggle();
+
+ // Test summary is shown when non-empty.
+ $node = $this->createNode([
+ 'body' => [
+ [
+ 'value' => $this->randomMachineName(32),
+ 'summary' => $this->randomMachineName(32),
+ 'format' => filter_default_format(),
+ ],
+ ],
+ ]);
+
+ $this->drupalGet('node/' . $node->id() . '/edit');
+ $page = $this->getSession()->getPage();
+ $summary_field = $page->findField('edit-body-0-summary');
+
+ $this->assertEquals(TRUE, $summary_field->isVisible(), 'Non-empty summary field is shown by default.');
+ }
+
+}
diff --git a/core/modules/text/tests/src/Kernel/TextWithSummaryItemTest.php b/core/modules/text/tests/src/Kernel/TextWithSummaryItemTest.php
index a48c191b5..195250abb 100644
--- a/core/modules/text/tests/src/Kernel/TextWithSummaryItemTest.php
+++ b/core/modules/text/tests/src/Kernel/TextWithSummaryItemTest.php
@@ -26,7 +26,7 @@ class TextWithSummaryItemTest extends FieldKernelTestBase {
/**
* Field storage entity.
*
- * @var \Drupal\field\Entity\FieldStorageConfig.
+ * @var \Drupal\field\Entity\FieldStorageConfig
*/
protected $fieldStorage;
@@ -37,7 +37,6 @@ class TextWithSummaryItemTest extends FieldKernelTestBase {
*/
protected $field;
-
protected function setUp() {
parent::setUp();
@@ -106,7 +105,7 @@ class TextWithSummaryItemTest extends FieldKernelTestBase {
'type' => 'text_with_summary',
'settings' => [
'max_length' => 10,
- ]
+ ],
]);
$this->fieldStorage->save();
$this->field = FieldConfig::create([
diff --git a/core/modules/text/tests/src/Unit/Migrate/TextCckTest.php b/core/modules/text/tests/src/Unit/Migrate/TextCckTest.php
index 2273c186b..51ec9af9c 100644
--- a/core/modules/text/tests/src/Unit/Migrate/TextCckTest.php
+++ b/core/modules/text/tests/src/Unit/Migrate/TextCckTest.php
@@ -76,7 +76,7 @@ class TextCckTest extends UnitTestCase {
'widget_type' => 'optionwidgets_onoff',
'global_settings' => [
'allowed_values' => "foo\nbar",
- ]
+ ],
];
$this->plugin->processCckFieldValues($this->migration, 'field', $info);
@@ -101,7 +101,7 @@ class TextCckTest extends UnitTestCase {
'widget_type' => 'optionwidgets_onoff',
'global_settings' => [
'allowed_values' => "foo|Foo\nbaz|Baz",
- ]
+ ],
];
$this->plugin->processCckFieldValues($this->migration, 'field', $info);
diff --git a/core/modules/text/tests/src/Unit/Migrate/d6/TextFieldTest.php b/core/modules/text/tests/src/Unit/Migrate/d6/TextFieldTest.php
index 8bf5d2a2d..807b9f16e 100644
--- a/core/modules/text/tests/src/Unit/Migrate/d6/TextFieldTest.php
+++ b/core/modules/text/tests/src/Unit/Migrate/d6/TextFieldTest.php
@@ -33,7 +33,7 @@ class TextFieldTest extends UnitTestCase {
$migration = $this->prophesize(MigrationInterface::class);
- // The plugin's processFieldValues() method will call
+ // The plugin's defineValueProcessPipeline() method will call
// setProcessOfProperty() and return nothing. So, in order to examine the
// process pipeline created by the plugin, we need to ensure that
// getProcess() always returns the last input to setProcessOfProperty().
@@ -46,7 +46,11 @@ class TextFieldTest extends UnitTestCase {
}
/**
+ * Calls the deprecated processFieldValues() method to test BC.
+ *
* @covers ::processFieldValues
+ *
+ * @depends testFilteredTextValueProcessPipeline
*/
public function testProcessFilteredTextFieldValues() {
$field_info = [
@@ -69,14 +73,41 @@ class TextFieldTest extends UnitTestCase {
}
/**
+ * @covers ::defineValueProcessPipeline
+ */
+ public function testFilteredTextValueProcessPipeline() {
+ $field_info = [
+ 'widget_type' => 'text_textfield',
+ ];
+ $this->plugin->defineValueProcessPipeline($this->migration, 'body', $field_info);
+
+ $process = $this->migration->getProcess();
+ $this->assertSame('sub_process', $process['plugin']);
+ $this->assertSame('body', $process['source']);
+ $this->assertSame('value', $process['process']['value']);
+
+ // Ensure that filter format IDs will be looked up in the filter format
+ // migrations.
+ $lookup = $process['process']['format'][2];
+ $this->assertSame('migration', $lookup['plugin']);
+ $this->assertContains('d6_filter_format', $lookup['migration']);
+ $this->assertContains('d7_filter_format', $lookup['migration']);
+ $this->assertSame('format', $lookup['source']);
+ }
+
+ /**
+ * Calls the deprecated processFieldValues() method to test BC.
+ *
* @covers ::processFieldValues
+ *
+ * @depends testBooleanTextImplicitValueProcessPipeline
*/
public function testProcessBooleanTextImplicitValues() {
$info = [
'widget_type' => 'optionwidgets_onoff',
'global_settings' => [
'allowed_values' => "foo\nbar",
- ]
+ ],
];
$this->plugin->processFieldValues($this->migration, 'field', $info);
@@ -94,14 +125,43 @@ class TextFieldTest extends UnitTestCase {
}
/**
+ * @covers ::defineValueProcessPipeline
+ */
+ public function testBooleanTextImplicitValueProcessPipeline() {
+ $info = [
+ 'widget_type' => 'optionwidgets_onoff',
+ 'global_settings' => [
+ 'allowed_values' => "foo\nbar",
+ ],
+ ];
+ $this->plugin->defineValueProcessPipeline($this->migration, 'field', $info);
+
+ $expected = [
+ 'value' => [
+ 'plugin' => 'static_map',
+ 'source' => 'value',
+ 'default_value' => 0,
+ 'map' => [
+ 'bar' => 1,
+ ],
+ ],
+ ];
+ $this->assertSame($expected, $this->migration->getProcess()['process']);
+ }
+
+ /**
+ * Calls the deprecated processFieldValues() method to test BC.
+ *
* @covers ::processFieldValues
+ *
+ * @depends testBooleanTextExplicitValueProcessPipeline
*/
public function testProcessBooleanTextExplicitValues() {
$info = [
'widget_type' => 'optionwidgets_onoff',
'global_settings' => [
'allowed_values' => "foo|Foo\nbaz|Baz",
- ]
+ ],
];
$this->plugin->processFieldValues($this->migration, 'field', $info);
@@ -118,6 +178,31 @@ class TextFieldTest extends UnitTestCase {
$this->assertSame($expected, $this->migration->getProcess()['process']);
}
+ /**
+ * @covers ::defineValueProcessPipeline
+ */
+ public function testBooleanTextExplicitValueProcessPipeline() {
+ $info = [
+ 'widget_type' => 'optionwidgets_onoff',
+ 'global_settings' => [
+ 'allowed_values' => "foo|Foo\nbaz|Baz",
+ ],
+ ];
+ $this->plugin->defineValueProcessPipeline($this->migration, 'field', $info);
+
+ $expected = [
+ 'value' => [
+ 'plugin' => 'static_map',
+ 'source' => 'value',
+ 'default_value' => 0,
+ 'map' => [
+ 'baz' => 1,
+ ],
+ ],
+ ];
+ $this->assertSame($expected, $this->migration->getProcess()['process']);
+ }
+
/**
* Data provider for testGetFieldType().
*/
diff --git a/core/modules/text/tests/src/Unit/Plugin/migrate/cckfield/TextCckTest.php b/core/modules/text/tests/src/Unit/Plugin/migrate/cckfield/TextCckTest.php
index acd77909e..696c34cba 100644
--- a/core/modules/text/tests/src/Unit/Plugin/migrate/cckfield/TextCckTest.php
+++ b/core/modules/text/tests/src/Unit/Plugin/migrate/cckfield/TextCckTest.php
@@ -76,7 +76,7 @@ class TextCckTest extends UnitTestCase {
'widget_type' => 'optionwidgets_onoff',
'global_settings' => [
'allowed_values' => "foo\nbar",
- ]
+ ],
];
$this->plugin->processCckFieldValues($this->migration, 'field', $info);
@@ -101,7 +101,7 @@ class TextCckTest extends UnitTestCase {
'widget_type' => 'optionwidgets_onoff',
'global_settings' => [
'allowed_values' => "foo|Foo\nbaz|Baz",
- ]
+ ],
];
$this->plugin->processCckFieldValues($this->migration, 'field', $info);
diff --git a/core/modules/text/tests/src/Unit/Plugin/migrate/field/d6/TextFieldLegacyTest.php b/core/modules/text/tests/src/Unit/Plugin/migrate/field/d6/TextFieldLegacyTest.php
new file mode 100644
index 000000000..d30844bf0
--- /dev/null
+++ b/core/modules/text/tests/src/Unit/Plugin/migrate/field/d6/TextFieldLegacyTest.php
@@ -0,0 +1,36 @@
+prophesize(MigrationInterface::class);
- // The plugin's processFieldValues() method will call
+ // The plugin's defineValueProcessPipeline() method will call
// setProcessOfProperty() and return nothing. So, in order to examine the
// process pipeline created by the plugin, we need to ensure that
// getProcess() always returns the last input to setProcessOfProperty().
@@ -45,13 +45,13 @@ class TextFieldTest extends UnitTestCase {
}
/**
- * @covers ::processFieldValues
+ * @covers ::defineValueProcessPipeline
*/
- public function testProcessFilteredTextFieldValues() {
+ public function testProcessFilteredTextFieldValues($method = 'defineValueProcessPipeline') {
$field_info = [
'widget_type' => 'text_textfield',
];
- $this->plugin->processFieldValues($this->migration, 'body', $field_info);
+ $this->plugin->$method($this->migration, 'body', $field_info);
$process = $this->migration->getProcess();
$this->assertSame('sub_process', $process['plugin']);
@@ -68,16 +68,16 @@ class TextFieldTest extends UnitTestCase {
}
/**
- * @covers ::processFieldValues
+ * @covers ::defineValueProcessPipeline
*/
- public function testProcessBooleanTextImplicitValues() {
+ public function testProcessBooleanTextImplicitValues($method = 'defineValueProcessPipeline') {
$info = [
'widget_type' => 'optionwidgets_onoff',
'global_settings' => [
'allowed_values' => "foo\nbar",
- ]
+ ],
];
- $this->plugin->processFieldValues($this->migration, 'field', $info);
+ $this->plugin->$method($this->migration, 'field', $info);
$expected = [
'value' => [
@@ -93,16 +93,16 @@ class TextFieldTest extends UnitTestCase {
}
/**
- * @covers ::processFieldValues
+ * @covers ::defineValueProcessPipeline
*/
- public function testProcessBooleanTextExplicitValues() {
+ public function testProcessBooleanTextExplicitValues($method = 'defineValueProcessPipeline') {
$info = [
'widget_type' => 'optionwidgets_onoff',
'global_settings' => [
'allowed_values' => "foo|Foo\nbaz|Baz",
- ]
+ ],
];
- $this->plugin->processFieldValues($this->migration, 'field', $info);
+ $this->plugin->$method($this->migration, 'field', $info);
$expected = [
'value' => [
diff --git a/core/modules/text/text.es6.js b/core/modules/text/text.es6.js
index 6c11adae1..042241861 100644
--- a/core/modules/text/text.es6.js
+++ b/core/modules/text/text.es6.js
@@ -3,7 +3,7 @@
* Text behaviors.
*/
-(function ($, Drupal) {
+(function($, Drupal) {
/**
* Auto-hide summary textarea if empty and show hide and unhide links.
*
@@ -14,44 +14,52 @@
*/
Drupal.behaviors.textSummary = {
attach(context, settings) {
- $(context).find('.js-text-summary').once('text-summary').each(function () {
- const $widget = $(this).closest('.js-text-format-wrapper');
+ $(context)
+ .find('.js-text-summary')
+ .once('text-summary')
+ .each(function() {
+ const $widget = $(this).closest('.js-text-format-wrapper');
- const $summary = $widget.find('.js-text-summary-wrapper');
- const $summaryLabel = $summary.find('label').eq(0);
- const $full = $widget.find('.js-text-full').closest('.js-form-item');
- let $fullLabel = $full.find('label').eq(0);
+ const $summary = $widget.find('.js-text-summary-wrapper');
+ const $summaryLabel = $summary.find('label').eq(0);
+ const $full = $widget.children('.js-form-type-textarea');
+ let $fullLabel = $full.find('label').eq(0);
- // Create a placeholder label when the field cardinality is greater
- // than 1.
- if ($fullLabel.length === 0) {
- $fullLabel = $('').prependTo($full);
- }
-
- // Set up the edit/hide summary link.
- const $link = $(` ()`);
- const $button = $link.find('button');
- let toggleClick = true;
- $link.on('click', (e) => {
- if (toggleClick) {
- $summary.hide();
- $button.html(Drupal.t('Edit summary'));
- $link.appendTo($fullLabel);
+ // Create a placeholder label when the field cardinality is greater
+ // than 1.
+ if ($fullLabel.length === 0) {
+ $fullLabel = $('').prependTo($full);
}
- else {
- $summary.show();
- $button.html(Drupal.t('Hide summary'));
- $link.appendTo($summaryLabel);
- }
- e.preventDefault();
- toggleClick = !toggleClick;
- }).appendTo($summaryLabel);
- // If no summary is set, hide the summary field.
- if ($widget.find('.js-text-summary').val() === '') {
- $link.trigger('click');
- }
- });
+ // Set up the edit/hide summary link.
+ const $link = $(
+ ` ()`,
+ );
+ const $button = $link.find('button');
+ let toggleClick = true;
+ $link
+ .on('click', e => {
+ if (toggleClick) {
+ $summary.hide();
+ $button.html(Drupal.t('Edit summary'));
+ $link.appendTo($fullLabel);
+ } else {
+ $summary.show();
+ $button.html(Drupal.t('Hide summary'));
+ $link.appendTo($summaryLabel);
+ }
+ e.preventDefault();
+ toggleClick = !toggleClick;
+ })
+ .appendTo($summaryLabel);
+
+ // If no summary is set, hide the summary field.
+ if ($widget.find('.js-text-summary').val() === '') {
+ $link.trigger('click');
+ }
+ });
},
};
-}(jQuery, Drupal));
+})(jQuery, Drupal);
diff --git a/core/modules/text/text.info.yml b/core/modules/text/text.info.yml
index 71f45bdc4..5f09ace21 100644
--- a/core/modules/text/text.info.yml
+++ b/core/modules/text/text.info.yml
@@ -5,5 +5,5 @@ package: Field types
version: VERSION
core: 8.x
dependencies:
- - field
- - filter
+ - drupal:field
+ - drupal:filter
diff --git a/core/modules/text/text.js b/core/modules/text/text.js
index c760f2dd2..25a31e422 100644
--- a/core/modules/text/text.js
+++ b/core/modules/text/text.js
@@ -13,7 +13,7 @@
var $summary = $widget.find('.js-text-summary-wrapper');
var $summaryLabel = $summary.find('label').eq(0);
- var $full = $widget.find('.js-text-full').closest('.js-form-item');
+ var $full = $widget.children('.js-form-type-textarea');
var $fullLabel = $full.find('label').eq(0);
if ($fullLabel.length === 0) {
diff --git a/core/modules/text/text.module b/core/modules/text/text.module
index b88fdc1fd..a8c2b847f 100644
--- a/core/modules/text/text.module
+++ b/core/modules/text/text.module
@@ -91,7 +91,7 @@ function text_summary($text, $format = NULL, $size = NULL) {
}
// If we have a short body, the entire body is the summary.
- if (Unicode::strlen($text) <= $size) {
+ if (mb_strlen($text) <= $size) {
return $text;
}
diff --git a/core/modules/toolbar/js/escapeAdmin.es6.js b/core/modules/toolbar/js/escapeAdmin.es6.js
index c381f021c..c91c9d847 100644
--- a/core/modules/toolbar/js/escapeAdmin.es6.js
+++ b/core/modules/toolbar/js/escapeAdmin.es6.js
@@ -3,7 +3,7 @@
* Replaces the home link in toolbar with a back to site link.
*/
-(function ($, Drupal, drupalSettings) {
+(function($, Drupal, drupalSettings) {
const pathInfo = drupalSettings.path;
const escapeAdminPath = sessionStorage.getItem('escapeAdminPath');
const windowLocation = window.location;
@@ -12,7 +12,10 @@
// back to it when browsing administrative pages. If there is a destination
// parameter there is not need to save the current path because the page is
// loaded within an existing "workflow".
- if (!pathInfo.currentPathIsAdmin && !/destination=/.test(windowLocation.search)) {
+ if (
+ !pathInfo.currentPathIsAdmin &&
+ !/destination=/.test(windowLocation.search)
+ ) {
sessionStorage.setItem('escapeAdminPath', windowLocation);
}
@@ -29,15 +32,16 @@
*/
Drupal.behaviors.escapeAdmin = {
attach() {
- const $toolbarEscape = $('[data-toolbar-escape-admin]').once('escapeAdmin');
+ const $toolbarEscape = $('[data-toolbar-escape-admin]').once(
+ 'escapeAdmin',
+ );
if ($toolbarEscape.length && pathInfo.currentPathIsAdmin) {
if (escapeAdminPath !== null) {
$toolbarEscape.attr('href', escapeAdminPath);
- }
- else {
+ } else {
$toolbarEscape.text(Drupal.t('Home'));
}
}
},
};
-}(jQuery, Drupal, drupalSettings));
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/modules/toolbar/js/models/MenuModel.es6.js b/core/modules/toolbar/js/models/MenuModel.es6.js
index a7898b401..d3636078e 100644
--- a/core/modules/toolbar/js/models/MenuModel.es6.js
+++ b/core/modules/toolbar/js/models/MenuModel.es6.js
@@ -3,7 +3,7 @@
* A Backbone Model for collapsible menus.
*/
-(function (Backbone, Drupal) {
+(function(Backbone, Drupal) {
/**
* Backbone Model for collapsible menus.
*
@@ -11,19 +11,19 @@
*
* @augments Backbone.Model
*/
- Drupal.toolbar.MenuModel = Backbone.Model.extend(/** @lends Drupal.toolbar.MenuModel# */{
-
- /**
- * @type {object}
- *
- * @prop {object} subtrees
- */
- defaults: /** @lends Drupal.toolbar.MenuModel# */{
-
+ Drupal.toolbar.MenuModel = Backbone.Model.extend(
+ /** @lends Drupal.toolbar.MenuModel# */ {
/**
* @type {object}
+ *
+ * @prop {object} subtrees
*/
- subtrees: {},
+ defaults: /** @lends Drupal.toolbar.MenuModel# */ {
+ /**
+ * @type {object}
+ */
+ subtrees: {},
+ },
},
- });
-}(Backbone, Drupal));
+ );
+})(Backbone, Drupal);
diff --git a/core/modules/toolbar/js/models/ToolbarModel.es6.js b/core/modules/toolbar/js/models/ToolbarModel.es6.js
index 90c69d0bd..2a18a6aee 100644
--- a/core/modules/toolbar/js/models/ToolbarModel.es6.js
+++ b/core/modules/toolbar/js/models/ToolbarModel.es6.js
@@ -3,7 +3,7 @@
* A Backbone Model for the toolbar.
*/
-(function (Backbone, Drupal) {
+(function(Backbone, Drupal) {
/**
* Backbone model for the toolbar.
*
@@ -11,143 +11,149 @@
*
* @augments Backbone.Model
*/
- Drupal.toolbar.ToolbarModel = Backbone.Model.extend(/** @lends Drupal.toolbar.ToolbarModel# */{
-
- /**
- * @type {object}
- *
- * @prop activeTab
- * @prop activeTray
- * @prop isOriented
- * @prop isFixed
- * @prop areSubtreesLoaded
- * @prop isViewportOverflowConstrained
- * @prop orientation
- * @prop locked
- * @prop isTrayToggleVisible
- * @prop height
- * @prop offsets
- */
- defaults: /** @lends Drupal.toolbar.ToolbarModel# */{
-
+ Drupal.toolbar.ToolbarModel = Backbone.Model.extend(
+ /** @lends Drupal.toolbar.ToolbarModel# */ {
/**
- * The active toolbar tab. All other tabs should be inactive under
- * normal circumstances. It will remain active across page loads. The
- * active item is stored as an ID selector e.g. '#toolbar-item--1'.
- *
- * @type {string}
- */
- activeTab: null,
-
- /**
- * Represents whether a tray is open or not. Stored as an ID selector e.g.
- * '#toolbar-item--1-tray'.
- *
- * @type {string}
- */
- activeTray: null,
-
- /**
- * Indicates whether the toolbar is displayed in an oriented fashion,
- * either horizontal or vertical.
- *
- * @type {bool}
- */
- isOriented: false,
-
- /**
- * Indicates whether the toolbar is positioned absolute (false) or fixed
- * (true).
- *
- * @type {bool}
- */
- isFixed: false,
-
- /**
- * Menu subtrees are loaded through an AJAX request only when the Toolbar
- * is set to a vertical orientation.
- *
- * @type {bool}
- */
- areSubtreesLoaded: false,
-
- /**
- * If the viewport overflow becomes constrained, isFixed must be true so
- * that elements in the trays aren't lost off-screen and impossible to
- * get to.
- *
- * @type {bool}
- */
- isViewportOverflowConstrained: false,
-
- /**
- * The orientation of the active tray.
- *
- * @type {string}
- */
- orientation: 'horizontal',
-
- /**
- * A tray is locked if a user toggled it to vertical. Otherwise a tray
- * will switch between vertical and horizontal orientation based on the
- * configured breakpoints. The locked state will be maintained across page
- * loads.
- *
- * @type {bool}
- */
- locked: false,
-
- /**
- * Indicates whether the tray orientation toggle is visible.
- *
- * @type {bool}
- */
- isTrayToggleVisible: true,
-
- /**
- * The height of the toolbar.
- *
- * @type {number}
- */
- height: null,
-
- /**
- * The current viewport offsets determined by {@link Drupal.displace}. The
- * offsets suggest how a module might position is components relative to
- * the viewport.
- *
* @type {object}
*
- * @prop {number} top
- * @prop {number} right
- * @prop {number} bottom
- * @prop {number} left
+ * @prop activeTab
+ * @prop activeTray
+ * @prop isOriented
+ * @prop isFixed
+ * @prop areSubtreesLoaded
+ * @prop isViewportOverflowConstrained
+ * @prop orientation
+ * @prop locked
+ * @prop isTrayToggleVisible
+ * @prop height
+ * @prop offsets
*/
- offsets: {
- top: 0,
- right: 0,
- bottom: 0,
- left: 0,
+ defaults: /** @lends Drupal.toolbar.ToolbarModel# */ {
+ /**
+ * The active toolbar tab. All other tabs should be inactive under
+ * normal circumstances. It will remain active across page loads. The
+ * active item is stored as an ID selector e.g. '#toolbar-item--1'.
+ *
+ * @type {string}
+ */
+ activeTab: null,
+
+ /**
+ * Represents whether a tray is open or not. Stored as an ID selector e.g.
+ * '#toolbar-item--1-tray'.
+ *
+ * @type {string}
+ */
+ activeTray: null,
+
+ /**
+ * Indicates whether the toolbar is displayed in an oriented fashion,
+ * either horizontal or vertical.
+ *
+ * @type {bool}
+ */
+ isOriented: false,
+
+ /**
+ * Indicates whether the toolbar is positioned absolute (false) or fixed
+ * (true).
+ *
+ * @type {bool}
+ */
+ isFixed: false,
+
+ /**
+ * Menu subtrees are loaded through an AJAX request only when the Toolbar
+ * is set to a vertical orientation.
+ *
+ * @type {bool}
+ */
+ areSubtreesLoaded: false,
+
+ /**
+ * If the viewport overflow becomes constrained, isFixed must be true so
+ * that elements in the trays aren't lost off-screen and impossible to
+ * get to.
+ *
+ * @type {bool}
+ */
+ isViewportOverflowConstrained: false,
+
+ /**
+ * The orientation of the active tray.
+ *
+ * @type {string}
+ */
+ orientation: 'horizontal',
+
+ /**
+ * A tray is locked if a user toggled it to vertical. Otherwise a tray
+ * will switch between vertical and horizontal orientation based on the
+ * configured breakpoints. The locked state will be maintained across page
+ * loads.
+ *
+ * @type {bool}
+ */
+ locked: false,
+
+ /**
+ * Indicates whether the tray orientation toggle is visible.
+ *
+ * @type {bool}
+ */
+ isTrayToggleVisible: true,
+
+ /**
+ * The height of the toolbar.
+ *
+ * @type {number}
+ */
+ height: null,
+
+ /**
+ * The current viewport offsets determined by {@link Drupal.displace}. The
+ * offsets suggest how a module might position is components relative to
+ * the viewport.
+ *
+ * @type {object}
+ *
+ * @prop {number} top
+ * @prop {number} right
+ * @prop {number} bottom
+ * @prop {number} left
+ */
+ offsets: {
+ top: 0,
+ right: 0,
+ bottom: 0,
+ left: 0,
+ },
+ },
+
+ /**
+ * @inheritdoc
+ *
+ * @param {object} attributes
+ * Attributes for the toolbar.
+ * @param {object} options
+ * Options for the toolbar.
+ *
+ * @return {string|undefined}
+ * Returns an error message if validation failed.
+ */
+ validate(attributes, options) {
+ // Prevent the orientation being set to horizontal if it is locked, unless
+ // override has not been passed as an option.
+ if (
+ attributes.orientation === 'horizontal' &&
+ this.get('locked') &&
+ !options.override
+ ) {
+ return Drupal.t(
+ 'The toolbar cannot be set to a horizontal orientation when it is locked.',
+ );
+ }
},
},
-
- /**
- * @inheritdoc
- *
- * @param {object} attributes
- * Attributes for the toolbar.
- * @param {object} options
- * Options for the toolbar.
- *
- * @return {string|undefined}
- * Returns an error message if validation failed.
- */
- validate(attributes, options) {
- // Prevent the orientation being set to horizontal if it is locked, unless
- // override has not been passed as an option.
- if (attributes.orientation === 'horizontal' && this.get('locked') && !options.override) {
- return Drupal.t('The toolbar cannot be set to a horizontal orientation when it is locked.');
- }
- },
- });
-}(Backbone, Drupal));
+ );
+})(Backbone, Drupal);
diff --git a/core/modules/toolbar/js/toolbar.es6.js b/core/modules/toolbar/js/toolbar.es6.js
index 4149d27da..4e646a3dd 100644
--- a/core/modules/toolbar/js/toolbar.es6.js
+++ b/core/modules/toolbar/js/toolbar.es6.js
@@ -3,7 +3,7 @@
* Defines the behavior of the Drupal administration toolbar.
*/
-(function ($, Drupal, drupalSettings) {
+(function($, Drupal, drupalSettings) {
// Merge run-time settings with the defaults.
const options = $.extend(
{
@@ -44,106 +44,167 @@
return;
}
// Process the administrative toolbar.
- $(context).find('#toolbar-administration').once('toolbar').each(function () {
- // Establish the toolbar models and views.
- const model = new Drupal.toolbar.ToolbarModel({
- locked: JSON.parse(localStorage.getItem('Drupal.toolbar.trayVerticalLocked')),
- activeTab: document.getElementById(JSON.parse(localStorage.getItem('Drupal.toolbar.activeTabID'))),
- height: $('#toolbar-administration').outerHeight(),
- });
-
- Drupal.toolbar.models.toolbarModel = model;
-
- // Attach a listener to the configured media query breakpoints.
- // Executes it before Drupal.toolbar.views to avoid extra rendering.
- Object.keys(options.breakpoints).forEach((label) => {
- const mq = options.breakpoints[label];
- const mql = window.matchMedia(mq);
- Drupal.toolbar.mql[label] = mql;
- // Curry the model and the label of the media query breakpoint to
- // the mediaQueryChangeHandler function.
- mql.addListener(Drupal.toolbar.mediaQueryChangeHandler.bind(null, model, label));
- // Fire the mediaQueryChangeHandler for each configured breakpoint
- // so that they process once.
- Drupal.toolbar.mediaQueryChangeHandler.call(null, model, label, mql);
- });
-
- Drupal.toolbar.views.toolbarVisualView = new Drupal.toolbar.ToolbarVisualView({
- el: this,
- model,
- strings: options.strings,
- });
- Drupal.toolbar.views.toolbarAuralView = new Drupal.toolbar.ToolbarAuralView({
- el: this,
- model,
- strings: options.strings,
- });
- Drupal.toolbar.views.bodyVisualView = new Drupal.toolbar.BodyVisualView({
- el: this,
- model,
- });
-
- // Force layout render to fix mobile view. Only needed on load, not
- // for every media query match.
- model.trigger('change:isFixed', model, model.get('isFixed'));
- model.trigger('change:activeTray', model, model.get('activeTray'));
-
- // Render collapsible menus.
- const menuModel = new Drupal.toolbar.MenuModel();
- Drupal.toolbar.models.menuModel = menuModel;
- Drupal.toolbar.views.menuVisualView = new Drupal.toolbar.MenuVisualView({
- el: $(this).find('.toolbar-menu-administration').get(0),
- model: menuModel,
- strings: options.strings,
- });
-
- // Handle the resolution of Drupal.toolbar.setSubtrees.
- // This is handled with a deferred so that the function may be invoked
- // asynchronously.
- Drupal.toolbar.setSubtrees.done((subtrees) => {
- menuModel.set('subtrees', subtrees);
- const theme = drupalSettings.ajaxPageState.theme;
- localStorage.setItem(`Drupal.toolbar.subtrees.${theme}`, JSON.stringify(subtrees));
- // Indicate on the toolbarModel that subtrees are now loaded.
- model.set('areSubtreesLoaded', true);
- });
-
- // Trigger an initial attempt to load menu subitems. This first attempt
- // is made after the media query handlers have had an opportunity to
- // process. The toolbar starts in the vertical orientation by default,
- // unless the viewport is wide enough to accommodate a horizontal
- // orientation. Thus we give the Toolbar a chance to determine if it
- // should be set to horizontal orientation before attempting to load
- // menu subtrees.
- Drupal.toolbar.views.toolbarVisualView.loadSubtrees();
-
- $(document)
- // Update the model when the viewport offset changes.
- .on('drupalViewportOffsetChange.toolbar', (event, offsets) => {
- model.set('offsets', offsets);
+ $(context)
+ .find('#toolbar-administration')
+ .once('toolbar')
+ .each(function() {
+ // Establish the toolbar models and views.
+ const model = new Drupal.toolbar.ToolbarModel({
+ locked: JSON.parse(
+ localStorage.getItem('Drupal.toolbar.trayVerticalLocked'),
+ ),
+ activeTab: document.getElementById(
+ JSON.parse(localStorage.getItem('Drupal.toolbar.activeTabID')),
+ ),
+ height: $('#toolbar-administration').outerHeight(),
});
- // Broadcast model changes to other modules.
- model
- .on('change:orientation', (model, orientation) => {
- $(document).trigger('drupalToolbarOrientationChange', orientation);
- })
- .on('change:activeTab', (model, tab) => {
- $(document).trigger('drupalToolbarTabChange', tab);
- })
- .on('change:activeTray', (model, tray) => {
- $(document).trigger('drupalToolbarTrayChange', tray);
+ Drupal.toolbar.models.toolbarModel = model;
+
+ // Attach a listener to the configured media query breakpoints.
+ // Executes it before Drupal.toolbar.views to avoid extra rendering.
+ Object.keys(options.breakpoints).forEach(label => {
+ const mq = options.breakpoints[label];
+ const mql = window.matchMedia(mq);
+ Drupal.toolbar.mql[label] = mql;
+ // Curry the model and the label of the media query breakpoint to
+ // the mediaQueryChangeHandler function.
+ mql.addListener(
+ Drupal.toolbar.mediaQueryChangeHandler.bind(null, model, label),
+ );
+ // Fire the mediaQueryChangeHandler for each configured breakpoint
+ // so that they process once.
+ Drupal.toolbar.mediaQueryChangeHandler.call(
+ null,
+ model,
+ label,
+ mql,
+ );
});
- // If the toolbar's orientation is horizontal and no active tab is
- // defined then show the tray of the first toolbar tab by default (but
- // not the first 'Home' toolbar tab).
- if (Drupal.toolbar.models.toolbarModel.get('orientation') === 'horizontal' && Drupal.toolbar.models.toolbarModel.get('activeTab') === null) {
- Drupal.toolbar.models.toolbarModel.set({
- activeTab: $('.toolbar-bar .toolbar-tab:not(.home-toolbar-tab) a').get(0),
+ Drupal.toolbar.views.toolbarVisualView = new Drupal.toolbar.ToolbarVisualView(
+ {
+ el: this,
+ model,
+ strings: options.strings,
+ },
+ );
+ Drupal.toolbar.views.toolbarAuralView = new Drupal.toolbar.ToolbarAuralView(
+ {
+ el: this,
+ model,
+ strings: options.strings,
+ },
+ );
+ Drupal.toolbar.views.bodyVisualView = new Drupal.toolbar.BodyVisualView(
+ {
+ el: this,
+ model,
+ },
+ );
+
+ // Force layout render to fix mobile view. Only needed on load, not
+ // for every media query match.
+ model.trigger('change:isFixed', model, model.get('isFixed'));
+ model.trigger('change:activeTray', model, model.get('activeTray'));
+
+ // Render collapsible menus.
+ const menuModel = new Drupal.toolbar.MenuModel();
+ Drupal.toolbar.models.menuModel = menuModel;
+ Drupal.toolbar.views.menuVisualView = new Drupal.toolbar.MenuVisualView(
+ {
+ el: $(this)
+ .find('.toolbar-menu-administration')
+ .get(0),
+ model: menuModel,
+ strings: options.strings,
+ },
+ );
+
+ // Handle the resolution of Drupal.toolbar.setSubtrees.
+ // This is handled with a deferred so that the function may be invoked
+ // asynchronously.
+ Drupal.toolbar.setSubtrees.done(subtrees => {
+ menuModel.set('subtrees', subtrees);
+ const theme = drupalSettings.ajaxPageState.theme;
+ localStorage.setItem(
+ `Drupal.toolbar.subtrees.${theme}`,
+ JSON.stringify(subtrees),
+ );
+ // Indicate on the toolbarModel that subtrees are now loaded.
+ model.set('areSubtreesLoaded', true);
});
- }
- });
+
+ // Trigger an initial attempt to load menu subitems. This first attempt
+ // is made after the media query handlers have had an opportunity to
+ // process. The toolbar starts in the vertical orientation by default,
+ // unless the viewport is wide enough to accommodate a horizontal
+ // orientation. Thus we give the Toolbar a chance to determine if it
+ // should be set to horizontal orientation before attempting to load
+ // menu subtrees.
+ Drupal.toolbar.views.toolbarVisualView.loadSubtrees();
+
+ $(document)
+ // Update the model when the viewport offset changes.
+ .on('drupalViewportOffsetChange.toolbar', (event, offsets) => {
+ model.set('offsets', offsets);
+ });
+
+ // Broadcast model changes to other modules.
+ model
+ .on('change:orientation', (model, orientation) => {
+ $(document).trigger(
+ 'drupalToolbarOrientationChange',
+ orientation,
+ );
+ })
+ .on('change:activeTab', (model, tab) => {
+ $(document).trigger('drupalToolbarTabChange', tab);
+ })
+ .on('change:activeTray', (model, tray) => {
+ $(document).trigger('drupalToolbarTrayChange', tray);
+ });
+
+ // If the toolbar's orientation is horizontal and no active tab is
+ // defined then show the tray of the first toolbar tab by default (but
+ // not the first 'Home' toolbar tab).
+ if (
+ Drupal.toolbar.models.toolbarModel.get('orientation') ===
+ 'horizontal' &&
+ Drupal.toolbar.models.toolbarModel.get('activeTab') === null
+ ) {
+ Drupal.toolbar.models.toolbarModel.set({
+ activeTab: $(
+ '.toolbar-bar .toolbar-tab:not(.home-toolbar-tab) a',
+ ).get(0),
+ });
+ }
+
+ $(window).on({
+ 'dialog:aftercreate': (event, dialog, $element, settings) => {
+ const $toolbar = $('#toolbar-bar');
+ $toolbar.css('margin-top', '0');
+
+ // When off-canvas is positioned in top, toolbar has to be moved down.
+ if (settings.drupalOffCanvasPosition === 'top') {
+ const height = Drupal.offCanvas
+ .getContainer($element)
+ .outerHeight();
+ $toolbar.css('margin-top', `${height}px`);
+
+ $element.on('dialogContentResize.off-canvas', () => {
+ const newHeight = Drupal.offCanvas
+ .getContainer($element)
+ .outerHeight();
+ $toolbar.css('margin-top', `${newHeight}px`);
+ });
+ }
+ },
+ 'dialog:beforeclose': () => {
+ $('#toolbar-bar').css('margin-top', '0');
+ },
+ });
+ });
},
};
@@ -153,7 +214,6 @@
* @namespace
*/
Drupal.toolbar = {
-
/**
* A hash of View instances.
*
@@ -218,9 +278,13 @@
break;
case 'toolbar.wide':
- model.set({
- orientation: ((mql.matches && !model.get('locked')) ? 'horizontal' : 'vertical'),
- }, { validate: true });
+ model.set(
+ {
+ orientation:
+ mql.matches && !model.get('locked') ? 'horizontal' : 'vertical',
+ },
+ { validate: true },
+ );
// The tray orientation toggle visibility does not need to be
// validated.
model.set({
@@ -240,10 +304,12 @@
* @return {string}
* A string representing a DOM fragment.
*/
- Drupal.theme.toolbarOrientationToggle = function () {
- return ''
+ );
};
/**
@@ -256,7 +322,11 @@
* @param {number} [status]
* XMLHttpRequest status.
*/
- Drupal.AjaxCommands.prototype.setToolbarSubtrees = function (ajax, response, status) {
+ Drupal.AjaxCommands.prototype.setToolbarSubtrees = function(
+ ajax,
+ response,
+ status,
+ ) {
Drupal.toolbar.setSubtrees.resolve(response.subtrees);
};
-}(jQuery, Drupal, drupalSettings));
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/modules/toolbar/js/toolbar.js b/core/modules/toolbar/js/toolbar.js
index 7246420f8..f705a9b01 100644
--- a/core/modules/toolbar/js/toolbar.js
+++ b/core/modules/toolbar/js/toolbar.js
@@ -97,6 +97,26 @@
activeTab: $('.toolbar-bar .toolbar-tab:not(.home-toolbar-tab) a').get(0)
});
}
+
+ $(window).on({
+ 'dialog:aftercreate': function dialogAftercreate(event, dialog, $element, settings) {
+ var $toolbar = $('#toolbar-bar');
+ $toolbar.css('margin-top', '0');
+
+ if (settings.drupalOffCanvasPosition === 'top') {
+ var height = Drupal.offCanvas.getContainer($element).outerHeight();
+ $toolbar.css('margin-top', height + 'px');
+
+ $element.on('dialogContentResize.off-canvas', function () {
+ var newHeight = Drupal.offCanvas.getContainer($element).outerHeight();
+ $toolbar.css('margin-top', newHeight + 'px');
+ });
+ }
+ },
+ 'dialog:beforeclose': function dialogBeforeclose() {
+ $('#toolbar-bar').css('margin-top', '0');
+ }
+ });
});
}
};
diff --git a/core/modules/toolbar/js/toolbar.menu.es6.js b/core/modules/toolbar/js/toolbar.menu.es6.js
index 03fd41f37..614574ef7 100644
--- a/core/modules/toolbar/js/toolbar.menu.es6.js
+++ b/core/modules/toolbar/js/toolbar.menu.es6.js
@@ -8,18 +8,45 @@
* $('.toolbar-menu').drupalToolbarMenu();
*/
-(function ($, Drupal, drupalSettings) {
+(function($, Drupal, drupalSettings) {
/**
* Store the open menu tray.
*/
let activeItem = Drupal.url(drupalSettings.path.currentPath);
- $.fn.drupalToolbarMenu = function () {
+ $.fn.drupalToolbarMenu = function() {
const ui = {
handleOpen: Drupal.t('Extend'),
handleClose: Drupal.t('Collapse'),
};
+ /**
+ * Toggle the open/close state of a list is a menu.
+ *
+ * @param {jQuery} $item
+ * The li item to be toggled.
+ *
+ * @param {Boolean} switcher
+ * A flag that forces toggleClass to add or a remove a class, rather than
+ * simply toggling its presence.
+ */
+ function toggleList($item, switcher) {
+ const $toggle = $item
+ .children('.toolbar-box')
+ .children('.toolbar-handle');
+ switcher =
+ typeof switcher !== 'undefined' ? switcher : !$item.hasClass('open');
+ // Toggle the item open state.
+ $item.toggleClass('open', switcher);
+ // Twist the toggle.
+ $toggle.toggleClass('open', switcher);
+ // Adjust the toggle text.
+ $toggle
+ .find('.action')
+ // Expand Structure, Collapse Structure.
+ .text(switcher ? ui.handleClose : ui.handleOpen);
+ }
+
/**
* Handle clicks from the disclosure button on an item with sub-items.
*
@@ -56,30 +83,6 @@
event.stopPropagation();
}
- /**
- * Toggle the open/close state of a list is a menu.
- *
- * @param {jQuery} $item
- * The li item to be toggled.
- *
- * @param {Boolean} switcher
- * A flag that forces toggleClass to add or a remove a class, rather than
- * simply toggling its presence.
- */
- function toggleList($item, switcher) {
- const $toggle = $item.children('.toolbar-box').children('.toolbar-handle');
- switcher = (typeof switcher !== 'undefined') ? switcher : !$item.hasClass('open');
- // Toggle the item open state.
- $item.toggleClass('open', switcher);
- // Twist the toggle.
- $toggle.toggleClass('open', switcher);
- // Adjust the toggle text.
- $toggle
- .find('.action')
- // Expand Structure, Collapse Structure.
- .text((switcher) ? ui.handleClose : ui.handleOpen);
- }
-
/**
* Add markup to the menu elements.
*
@@ -104,8 +107,11 @@
const $item = $(element);
if ($item.children('ul.toolbar-menu').length) {
const $box = $item.children('.toolbar-box');
- options.text = Drupal.t('@label', { '@label': $box.find('a').text() });
- $item.children('.toolbar-box')
+ options.text = Drupal.t('@label', {
+ '@label': $box.find('a').text(),
+ });
+ $item
+ .children('.toolbar-box')
.append(Drupal.theme('toolbarMenuItemToggle', options));
}
});
@@ -124,7 +130,7 @@
* The current level number to be assigned to the list elements.
*/
function markListLevels($lists, level) {
- level = (!level) ? 1 : level;
+ level = !level ? 1 : level;
const $lis = $lists.children('li').addClass(`level-${level}`);
$lists = $lis.children('ul');
if ($lists.length) {
@@ -142,19 +148,23 @@
* The root of the menu.
*/
function openActiveItem($menu) {
- const pathItem = $menu.find(`a[href="${location.pathname}"]`);
+ const pathItem = $menu.find(`a[href="${window.location.pathname}"]`);
if (pathItem.length && !activeItem) {
- activeItem = location.pathname;
+ activeItem = window.location.pathname;
}
if (activeItem) {
- const $activeItem = $menu.find(`a[href="${activeItem}"]`).addClass('menu-item--active');
- const $activeTrail = $activeItem.parentsUntil('.root', 'li').addClass('menu-item--active-trail');
+ const $activeItem = $menu
+ .find(`a[href="${activeItem}"]`)
+ .addClass('menu-item--active');
+ const $activeTrail = $activeItem
+ .parentsUntil('.root', 'li')
+ .addClass('menu-item--active-trail');
toggleList($activeTrail, true);
}
}
// Return the jQuery object.
- return this.each(function (selector) {
+ return this.each(function(selector) {
const $menu = $(this).once('toolbar-menu');
if ($menu.length) {
// Bind event handlers.
@@ -186,7 +196,9 @@
* @return {string}
* A string representing a DOM fragment.
*/
- Drupal.theme.toolbarMenuItemToggle = function (options) {
- return ``;
+ Drupal.theme.toolbarMenuItemToggle = function(options) {
+ return ``;
};
-}(jQuery, Drupal, drupalSettings));
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/modules/toolbar/js/toolbar.menu.js b/core/modules/toolbar/js/toolbar.menu.js
index 45cfafbca..5f7345d8d 100644
--- a/core/modules/toolbar/js/toolbar.menu.js
+++ b/core/modules/toolbar/js/toolbar.menu.js
@@ -14,6 +14,17 @@
handleClose: Drupal.t('Collapse')
};
+ function toggleList($item, switcher) {
+ var $toggle = $item.children('.toolbar-box').children('.toolbar-handle');
+ switcher = typeof switcher !== 'undefined' ? switcher : !$item.hasClass('open');
+
+ $item.toggleClass('open', switcher);
+
+ $toggle.toggleClass('open', switcher);
+
+ $toggle.find('.action').text(switcher ? ui.handleClose : ui.handleOpen);
+ }
+
function toggleClickHandler(event) {
var $toggle = $(event.target);
var $item = $toggle.closest('li');
@@ -32,17 +43,6 @@
event.stopPropagation();
}
- function toggleList($item, switcher) {
- var $toggle = $item.children('.toolbar-box').children('.toolbar-handle');
- switcher = typeof switcher !== 'undefined' ? switcher : !$item.hasClass('open');
-
- $item.toggleClass('open', switcher);
-
- $toggle.toggleClass('open', switcher);
-
- $toggle.find('.action').text(switcher ? ui.handleClose : ui.handleOpen);
- }
-
function initItems($menu) {
var options = {
class: 'toolbar-icon toolbar-handle',
@@ -56,7 +56,9 @@
var $item = $(element);
if ($item.children('ul.toolbar-menu').length) {
var $box = $item.children('.toolbar-box');
- options.text = Drupal.t('@label', { '@label': $box.find('a').text() });
+ options.text = Drupal.t('@label', {
+ '@label': $box.find('a').text()
+ });
$item.children('.toolbar-box').append(Drupal.theme('toolbarMenuItemToggle', options));
}
});
@@ -72,9 +74,9 @@
}
function openActiveItem($menu) {
- var pathItem = $menu.find('a[href="' + location.pathname + '"]');
+ var pathItem = $menu.find('a[href="' + window.location.pathname + '"]');
if (pathItem.length && !activeItem) {
- activeItem = location.pathname;
+ activeItem = window.location.pathname;
}
if (activeItem) {
var $activeItem = $menu.find('a[href="' + activeItem + '"]').addClass('menu-item--active');
@@ -98,6 +100,6 @@
};
Drupal.theme.toolbarMenuItemToggle = function (options) {
- return '';
+ return '';
};
})(jQuery, Drupal, drupalSettings);
\ No newline at end of file
diff --git a/core/modules/toolbar/js/views/BodyVisualView.es6.js b/core/modules/toolbar/js/views/BodyVisualView.es6.js
index 0b513c3e1..8a88c3ae2 100644
--- a/core/modules/toolbar/js/views/BodyVisualView.es6.js
+++ b/core/modules/toolbar/js/views/BodyVisualView.es6.js
@@ -3,35 +3,46 @@
* A Backbone view for the body element.
*/
-(function ($, Drupal, Backbone) {
- Drupal.toolbar.BodyVisualView = Backbone.View.extend(/** @lends Drupal.toolbar.BodyVisualView# */{
- /**
- * Adjusts the body element with the toolbar position and dimension changes.
- *
- * @constructs
- *
- * @augments Backbone.View
- */
- initialize() {
- this.listenTo(this.model, 'change:activeTray ', this.render);
- this.listenTo(this.model, 'change:isFixed change:isViewportOverflowConstrained', this.isToolbarFixed);
- },
+(function($, Drupal, Backbone) {
+ Drupal.toolbar.BodyVisualView = Backbone.View.extend(
+ /** @lends Drupal.toolbar.BodyVisualView# */ {
+ /**
+ * Adjusts the body element with the toolbar position and dimension changes.
+ *
+ * @constructs
+ *
+ * @augments Backbone.View
+ */
+ initialize() {
+ this.listenTo(this.model, 'change:activeTray ', this.render);
+ this.listenTo(
+ this.model,
+ 'change:isFixed change:isViewportOverflowConstrained',
+ this.isToolbarFixed,
+ );
+ },
- isToolbarFixed() {
- // When the toolbar is fixed, it will not scroll with page scrolling.
- const isViewportOverflowConstrained = this.model.get('isViewportOverflowConstrained');
- $('body').toggleClass('toolbar-fixed', (isViewportOverflowConstrained || this.model.get('isFixed')));
- },
+ isToolbarFixed() {
+ // When the toolbar is fixed, it will not scroll with page scrolling.
+ const isViewportOverflowConstrained = this.model.get(
+ 'isViewportOverflowConstrained',
+ );
+ $('body').toggleClass(
+ 'toolbar-fixed',
+ isViewportOverflowConstrained || this.model.get('isFixed'),
+ );
+ },
- /**
- * @inheritdoc
- */
- render() {
- $('body')
- // Toggle the toolbar-tray-open class on the body element. The class is
- // applied when a toolbar tray is active. Padding might be applied to
- // the body element to prevent the tray from overlapping content.
- .toggleClass('toolbar-tray-open', !!this.model.get('activeTray'));
+ /**
+ * @inheritdoc
+ */
+ render() {
+ $('body')
+ // Toggle the toolbar-tray-open class on the body element. The class is
+ // applied when a toolbar tray is active. Padding might be applied to
+ // the body element to prevent the tray from overlapping content.
+ .toggleClass('toolbar-tray-open', !!this.model.get('activeTray'));
+ },
},
- });
-}(jQuery, Drupal, Backbone));
+ );
+})(jQuery, Drupal, Backbone);
diff --git a/core/modules/toolbar/js/views/MenuVisualView.es6.js b/core/modules/toolbar/js/views/MenuVisualView.es6.js
index 108f65c42..4d95ac989 100644
--- a/core/modules/toolbar/js/views/MenuVisualView.es6.js
+++ b/core/modules/toolbar/js/views/MenuVisualView.es6.js
@@ -3,38 +3,37 @@
* A Backbone view for the collapsible menus.
*/
-(function ($, Backbone, Drupal) {
- Drupal.toolbar.MenuVisualView = Backbone.View.extend(/** @lends Drupal.toolbar.MenuVisualView# */{
+(function($, Backbone, Drupal) {
+ Drupal.toolbar.MenuVisualView = Backbone.View.extend(
+ /** @lends Drupal.toolbar.MenuVisualView# */ {
+ /**
+ * Backbone View for collapsible menus.
+ *
+ * @constructs
+ *
+ * @augments Backbone.View
+ */
+ initialize() {
+ this.listenTo(this.model, 'change:subtrees', this.render);
+ },
- /**
- * Backbone View for collapsible menus.
- *
- * @constructs
- *
- * @augments Backbone.View
- */
- initialize() {
- this.listenTo(this.model, 'change:subtrees', this.render);
+ /**
+ * @inheritdoc
+ */
+ render() {
+ const subtrees = this.model.get('subtrees');
+ // Add subtrees.
+ Object.keys(subtrees || {}).forEach(id => {
+ this.$el
+ .find(`#toolbar-link-${id}`)
+ .once('toolbar-subtrees')
+ .after(subtrees[id]);
+ });
+ // Render the main menu as a nested, collapsible accordion.
+ if ('drupalToolbarMenu' in $.fn) {
+ this.$el.children('.toolbar-menu').drupalToolbarMenu();
+ }
+ },
},
-
- /**
- * @inheritdoc
- */
- render() {
- const subtrees = this.model.get('subtrees');
- // Add subtrees.
- Object.keys(subtrees || {}).forEach((id) => {
- this.$el
- .find(`#toolbar-link-${id}`)
- .once('toolbar-subtrees')
- .after(subtrees[id]);
- });
- // Render the main menu as a nested, collapsible accordion.
- if ('drupalToolbarMenu' in $.fn) {
- this.$el
- .children('.toolbar-menu')
- .drupalToolbarMenu();
- }
- },
- });
-}(jQuery, Backbone, Drupal));
+ );
+})(jQuery, Backbone, Drupal);
diff --git a/core/modules/toolbar/js/views/ToolbarAuralView.es6.js b/core/modules/toolbar/js/views/ToolbarAuralView.es6.js
index c27b864f0..c7ae04d2b 100644
--- a/core/modules/toolbar/js/views/ToolbarAuralView.es6.js
+++ b/core/modules/toolbar/js/views/ToolbarAuralView.es6.js
@@ -3,69 +3,79 @@
* A Backbone view for the aural feedback of the toolbar.
*/
-(function (Backbone, Drupal) {
- Drupal.toolbar.ToolbarAuralView = Backbone.View.extend(/** @lends Drupal.toolbar.ToolbarAuralView# */{
+(function(Backbone, Drupal) {
+ Drupal.toolbar.ToolbarAuralView = Backbone.View.extend(
+ /** @lends Drupal.toolbar.ToolbarAuralView# */ {
+ /**
+ * Backbone view for the aural feedback of the toolbar.
+ *
+ * @constructs
+ *
+ * @augments Backbone.View
+ *
+ * @param {object} options
+ * Options for the view.
+ * @param {object} options.strings
+ * Various strings to use in the view.
+ */
+ initialize(options) {
+ this.strings = options.strings;
- /**
- * Backbone view for the aural feedback of the toolbar.
- *
- * @constructs
- *
- * @augments Backbone.View
- *
- * @param {object} options
- * Options for the view.
- * @param {object} options.strings
- * Various strings to use in the view.
- */
- initialize(options) {
- this.strings = options.strings;
+ this.listenTo(
+ this.model,
+ 'change:orientation',
+ this.onOrientationChange,
+ );
+ this.listenTo(this.model, 'change:activeTray', this.onActiveTrayChange);
+ },
- this.listenTo(this.model, 'change:orientation', this.onOrientationChange);
- this.listenTo(this.model, 'change:activeTray', this.onActiveTrayChange);
+ /**
+ * Announces an orientation change.
+ *
+ * @param {Drupal.toolbar.ToolbarModel} model
+ * The toolbar model in question.
+ * @param {string} orientation
+ * The new value of the orientation attribute in the model.
+ */
+ onOrientationChange(model, orientation) {
+ Drupal.announce(
+ Drupal.t('Tray orientation changed to @orientation.', {
+ '@orientation': orientation,
+ }),
+ );
+ },
+
+ /**
+ * Announces a changed active tray.
+ *
+ * @param {Drupal.toolbar.ToolbarModel} model
+ * The toolbar model in question.
+ * @param {HTMLElement} tray
+ * The new value of the tray attribute in the model.
+ */
+ onActiveTrayChange(model, tray) {
+ const relevantTray =
+ tray === null ? model.previous('activeTray') : tray;
+ // Current activeTray and previous activeTray are empty, no state change
+ // to announce.
+ if (!relevantTray) {
+ return;
+ }
+ const action = tray === null ? Drupal.t('closed') : Drupal.t('opened');
+ const trayNameElement = relevantTray.querySelector(
+ '.toolbar-tray-name',
+ );
+ let text;
+ if (trayNameElement !== null) {
+ text = Drupal.t('Tray "@tray" @action.', {
+ '@tray': trayNameElement.textContent,
+ '@action': action,
+ });
+ } else {
+ text = Drupal.t('Tray @action.', { '@action': action });
+ }
+ Drupal.announce(text);
+ },
},
-
- /**
- * Announces an orientation change.
- *
- * @param {Drupal.toolbar.ToolbarModel} model
- * The toolbar model in question.
- * @param {string} orientation
- * The new value of the orientation attribute in the model.
- */
- onOrientationChange(model, orientation) {
- Drupal.announce(Drupal.t('Tray orientation changed to @orientation.', {
- '@orientation': orientation,
- }));
- },
-
- /**
- * Announces a changed active tray.
- *
- * @param {Drupal.toolbar.ToolbarModel} model
- * The toolbar model in question.
- * @param {HTMLElement} tray
- * The new value of the tray attribute in the model.
- */
- onActiveTrayChange(model, tray) {
- const relevantTray = (tray === null) ? model.previous('activeTray') : tray;
- // Current activeTray and previous activeTray are empty, no state change
- // to announce.
- if (!relevantTray) {
- return;
- }
- const action = (tray === null) ? Drupal.t('closed') : Drupal.t('opened');
- const trayNameElement = relevantTray.querySelector('.toolbar-tray-name');
- let text;
- if (trayNameElement !== null) {
- text = Drupal.t('Tray "@tray" @action.', {
- '@tray': trayNameElement.textContent, '@action': action,
- });
- }
- else {
- text = Drupal.t('Tray @action.', { '@action': action });
- }
- Drupal.announce(text);
- },
- });
-}(Backbone, Drupal));
+ );
+})(Backbone, Drupal);
diff --git a/core/modules/toolbar/js/views/ToolbarAuralView.js b/core/modules/toolbar/js/views/ToolbarAuralView.js
index f4cca42bd..6096d4170 100644
--- a/core/modules/toolbar/js/views/ToolbarAuralView.js
+++ b/core/modules/toolbar/js/views/ToolbarAuralView.js
@@ -29,7 +29,8 @@
var text = void 0;
if (trayNameElement !== null) {
text = Drupal.t('Tray "@tray" @action.', {
- '@tray': trayNameElement.textContent, '@action': action
+ '@tray': trayNameElement.textContent,
+ '@action': action
});
} else {
text = Drupal.t('Tray @action.', { '@action': action });
diff --git a/core/modules/toolbar/js/views/ToolbarVisualView.es6.js b/core/modules/toolbar/js/views/ToolbarVisualView.es6.js
index fd4fa6bfd..e263810da 100644
--- a/core/modules/toolbar/js/views/ToolbarVisualView.es6.js
+++ b/core/modules/toolbar/js/views/ToolbarVisualView.es6.js
@@ -3,328 +3,383 @@
* A Backbone view for the toolbar element. Listens to mouse & touch.
*/
-(function ($, Drupal, drupalSettings, Backbone) {
- Drupal.toolbar.ToolbarVisualView = Backbone.View.extend(/** @lends Drupal.toolbar.ToolbarVisualView# */{
+(function($, Drupal, drupalSettings, Backbone) {
+ Drupal.toolbar.ToolbarVisualView = Backbone.View.extend(
+ /** @lends Drupal.toolbar.ToolbarVisualView# */ {
+ /**
+ * Event map for the `ToolbarVisualView`.
+ *
+ * @return {object}
+ * A map of events.
+ */
+ events() {
+ // Prevents delay and simulated mouse events.
+ const touchEndToClick = function(event) {
+ event.preventDefault();
+ event.target.click();
+ };
- /**
- * Event map for the `ToolbarVisualView`.
- *
- * @return {object}
- * A map of events.
- */
- events() {
- // Prevents delay and simulated mouse events.
- const touchEndToClick = function (event) {
- event.preventDefault();
- event.target.click();
- };
+ return {
+ 'click .toolbar-bar .toolbar-tab .trigger': 'onTabClick',
+ 'click .toolbar-toggle-orientation button':
+ 'onOrientationToggleClick',
+ 'touchend .toolbar-bar .toolbar-tab .trigger': touchEndToClick,
+ 'touchend .toolbar-toggle-orientation button': touchEndToClick,
+ };
+ },
- return {
- 'click .toolbar-bar .toolbar-tab .trigger': 'onTabClick',
- 'click .toolbar-toggle-orientation button': 'onOrientationToggleClick',
- 'touchend .toolbar-bar .toolbar-tab .trigger': touchEndToClick,
- 'touchend .toolbar-toggle-orientation button': touchEndToClick,
- };
- },
+ /**
+ * Backbone view for the toolbar element. Listens to mouse & touch.
+ *
+ * @constructs
+ *
+ * @augments Backbone.View
+ *
+ * @param {object} options
+ * Options for the view object.
+ * @param {object} options.strings
+ * Various strings to use in the view.
+ */
+ initialize(options) {
+ this.strings = options.strings;
- /**
- * Backbone view for the toolbar element. Listens to mouse & touch.
- *
- * @constructs
- *
- * @augments Backbone.View
- *
- * @param {object} options
- * Options for the view object.
- * @param {object} options.strings
- * Various strings to use in the view.
- */
- initialize(options) {
- this.strings = options.strings;
+ this.listenTo(
+ this.model,
+ 'change:activeTab change:orientation change:isOriented change:isTrayToggleVisible',
+ this.render,
+ );
+ this.listenTo(this.model, 'change:mqMatches', this.onMediaQueryChange);
+ this.listenTo(this.model, 'change:offsets', this.adjustPlacement);
+ this.listenTo(
+ this.model,
+ 'change:activeTab change:orientation change:isOriented',
+ this.updateToolbarHeight,
+ );
- this.listenTo(this.model, 'change:activeTab change:orientation change:isOriented change:isTrayToggleVisible', this.render);
- this.listenTo(this.model, 'change:mqMatches', this.onMediaQueryChange);
- this.listenTo(this.model, 'change:offsets', this.adjustPlacement);
- this.listenTo(this.model, 'change:activeTab change:orientation change:isOriented', this.updateToolbarHeight);
+ // Add the tray orientation toggles.
+ this.$el
+ .find('.toolbar-tray .toolbar-lining')
+ .append(Drupal.theme('toolbarOrientationToggle'));
- // Add the tray orientation toggles.
- this.$el
- .find('.toolbar-tray .toolbar-lining')
- .append(Drupal.theme('toolbarOrientationToggle'));
+ // Trigger an activeTab change so that listening scripts can respond on
+ // page load. This will call render.
+ this.model.trigger('change:activeTab');
+ },
- // Trigger an activeTab change so that listening scripts can respond on
- // page load. This will call render.
- this.model.trigger('change:activeTab');
- },
+ /**
+ * Update the toolbar element height.
+ *
+ * @constructs
+ *
+ * @augments Backbone.View
+ */
+ updateToolbarHeight() {
+ const toolbarTabOuterHeight =
+ $('#toolbar-bar')
+ .find('.toolbar-tab')
+ .outerHeight() || 0;
+ const toolbarTrayHorizontalOuterHeight =
+ $('.is-active.toolbar-tray-horizontal').outerHeight() || 0;
+ this.model.set(
+ 'height',
+ toolbarTabOuterHeight + toolbarTrayHorizontalOuterHeight,
+ );
- /**
- * Update the toolbar element height.
- *
- * @constructs
- *
- * @augments Backbone.View
- */
- updateToolbarHeight() {
- const toolbarTabOuterHeight = $('#toolbar-bar').find('.toolbar-tab').outerHeight() || 0;
- const toolbarTrayHorizontalOuterHeight = $('.is-active.toolbar-tray-horizontal').outerHeight() || 0;
- this.model.set('height', toolbarTabOuterHeight + toolbarTrayHorizontalOuterHeight);
+ $('body').css({
+ 'padding-top': this.model.get('height'),
+ });
- $('body').css({
- 'padding-top': this.model.get('height'),
- });
+ this.triggerDisplace();
+ },
- this.triggerDisplace();
- },
+ // Trigger a recalculation of viewport displacing elements. Use setTimeout
+ // to ensure this recalculation happens after changes to visual elements
+ // have processed.
+ triggerDisplace() {
+ _.defer(() => {
+ Drupal.displace(true);
+ });
+ },
- // Trigger a recalculation of viewport displacing elements. Use setTimeout
- // to ensure this recalculation happens after changes to visual elements
- // have processed.
- triggerDisplace() {
- _.defer(() => {
- Drupal.displace(true);
- });
- },
+ /**
+ * @inheritdoc
+ *
+ * @return {Drupal.toolbar.ToolbarVisualView}
+ * The `ToolbarVisualView` instance.
+ */
+ render() {
+ this.updateTabs();
+ this.updateTrayOrientation();
+ this.updateBarAttributes();
- /**
- * @inheritdoc
- *
- * @return {Drupal.toolbar.ToolbarVisualView}
- * The `ToolbarVisualView` instance.
- */
- render() {
- this.updateTabs();
- this.updateTrayOrientation();
- this.updateBarAttributes();
+ $('body').removeClass('toolbar-loading');
- $('body').removeClass('toolbar-loading');
+ // Load the subtrees if the orientation of the toolbar is changed to
+ // vertical. This condition responds to the case that the toolbar switches
+ // from horizontal to vertical orientation. The toolbar starts in a
+ // vertical orientation by default and then switches to horizontal during
+ // initialization if the media query conditions are met. Simply checking
+ // that the orientation is vertical here would result in the subtrees
+ // always being loaded, even when the toolbar initialization ultimately
+ // results in a horizontal orientation.
+ //
+ // @see Drupal.behaviors.toolbar.attach() where admin menu subtrees
+ // loading is invoked during initialization after media query conditions
+ // have been processed.
+ if (
+ this.model.changed.orientation === 'vertical' ||
+ this.model.changed.activeTab
+ ) {
+ this.loadSubtrees();
+ }
- // Load the subtrees if the orientation of the toolbar is changed to
- // vertical. This condition responds to the case that the toolbar switches
- // from horizontal to vertical orientation. The toolbar starts in a
- // vertical orientation by default and then switches to horizontal during
- // initialization if the media query conditions are met. Simply checking
- // that the orientation is vertical here would result in the subtrees
- // always being loaded, even when the toolbar initialization ultimately
- // results in a horizontal orientation.
- //
- // @see Drupal.behaviors.toolbar.attach() where admin menu subtrees
- // loading is invoked during initialization after media query conditions
- // have been processed.
- if (this.model.changed.orientation === 'vertical' || this.model.changed.activeTab) {
- this.loadSubtrees();
- }
+ return this;
+ },
- return this;
- },
+ /**
+ * Responds to a toolbar tab click.
+ *
+ * @param {jQuery.Event} event
+ * The event triggered.
+ */
+ onTabClick(event) {
+ // If this tab has a tray associated with it, it is considered an
+ // activatable tab.
+ if (event.target.hasAttribute('data-toolbar-tray')) {
+ const activeTab = this.model.get('activeTab');
+ const clickedTab = event.target;
- /**
- * Responds to a toolbar tab click.
- *
- * @param {jQuery.Event} event
- * The event triggered.
- */
- onTabClick(event) {
- // If this tab has a tray associated with it, it is considered an
- // activatable tab.
- if (event.target.hasAttribute('data-toolbar-tray')) {
- const activeTab = this.model.get('activeTab');
- const clickedTab = event.target;
+ // Set the event target as the active item if it is not already.
+ this.model.set(
+ 'activeTab',
+ !activeTab || clickedTab !== activeTab ? clickedTab : null,
+ );
- // Set the event target as the active item if it is not already.
- this.model.set('activeTab', (!activeTab || clickedTab !== activeTab) ? clickedTab : null);
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ },
+
+ /**
+ * Toggles the orientation of a toolbar tray.
+ *
+ * @param {jQuery.Event} event
+ * The event triggered.
+ */
+ onOrientationToggleClick(event) {
+ const orientation = this.model.get('orientation');
+ // Determine the toggle-to orientation.
+ const antiOrientation =
+ orientation === 'vertical' ? 'horizontal' : 'vertical';
+ const locked = antiOrientation === 'vertical';
+ // Remember the locked state.
+ if (locked) {
+ localStorage.setItem('Drupal.toolbar.trayVerticalLocked', 'true');
+ } else {
+ localStorage.removeItem('Drupal.toolbar.trayVerticalLocked');
+ }
+ // Update the model.
+ this.model.set(
+ {
+ locked,
+ orientation: antiOrientation,
+ },
+ {
+ validate: true,
+ override: true,
+ },
+ );
event.preventDefault();
event.stopPropagation();
- }
- },
+ },
- /**
- * Toggles the orientation of a toolbar tray.
- *
- * @param {jQuery.Event} event
- * The event triggered.
- */
- onOrientationToggleClick(event) {
- const orientation = this.model.get('orientation');
- // Determine the toggle-to orientation.
- const antiOrientation = (orientation === 'vertical') ? 'horizontal' : 'vertical';
- const locked = antiOrientation === 'vertical';
- // Remember the locked state.
- if (locked) {
- localStorage.setItem('Drupal.toolbar.trayVerticalLocked', 'true');
- }
- else {
- localStorage.removeItem('Drupal.toolbar.trayVerticalLocked');
- }
- // Update the model.
- this.model.set({
- locked,
- orientation: antiOrientation,
- }, {
- validate: true,
- override: true,
- });
+ /**
+ * Updates the display of the tabs: toggles a tab and the associated tray.
+ */
+ updateTabs() {
+ const $tab = $(this.model.get('activeTab'));
+ // Deactivate the previous tab.
+ $(this.model.previous('activeTab'))
+ .removeClass('is-active')
+ .prop('aria-pressed', false);
+ // Deactivate the previous tray.
+ $(this.model.previous('activeTray')).removeClass('is-active');
- event.preventDefault();
- event.stopPropagation();
- },
-
- /**
- * Updates the display of the tabs: toggles a tab and the associated tray.
- */
- updateTabs() {
- const $tab = $(this.model.get('activeTab'));
- // Deactivate the previous tab.
- $(this.model.previous('activeTab'))
- .removeClass('is-active')
- .prop('aria-pressed', false);
- // Deactivate the previous tray.
- $(this.model.previous('activeTray'))
- .removeClass('is-active');
-
- // Activate the selected tab.
- if ($tab.length > 0) {
- $tab
- .addClass('is-active')
- // Mark the tab as pressed.
- .prop('aria-pressed', true);
- const name = $tab.attr('data-toolbar-tray');
- // Store the active tab name or remove the setting.
- const id = $tab.get(0).id;
- if (id) {
- localStorage.setItem('Drupal.toolbar.activeTabID', JSON.stringify(id));
- }
- // Activate the associated tray.
- const $tray = this.$el.find(`[data-toolbar-tray="${name}"].toolbar-tray`);
- if ($tray.length) {
- $tray.addClass('is-active');
- this.model.set('activeTray', $tray.get(0));
- }
- else {
+ // Activate the selected tab.
+ if ($tab.length > 0) {
+ $tab
+ .addClass('is-active')
+ // Mark the tab as pressed.
+ .prop('aria-pressed', true);
+ const name = $tab.attr('data-toolbar-tray');
+ // Store the active tab name or remove the setting.
+ const id = $tab.get(0).id;
+ if (id) {
+ localStorage.setItem(
+ 'Drupal.toolbar.activeTabID',
+ JSON.stringify(id),
+ );
+ }
+ // Activate the associated tray.
+ const $tray = this.$el.find(
+ `[data-toolbar-tray="${name}"].toolbar-tray`,
+ );
+ if ($tray.length) {
+ $tray.addClass('is-active');
+ this.model.set('activeTray', $tray.get(0));
+ } else {
+ // There is no active tray.
+ this.model.set('activeTray', null);
+ }
+ } else {
// There is no active tray.
this.model.set('activeTray', null);
+ localStorage.removeItem('Drupal.toolbar.activeTabID');
}
- }
- else {
- // There is no active tray.
- this.model.set('activeTray', null);
- localStorage.removeItem('Drupal.toolbar.activeTabID');
- }
- },
+ },
- /**
- * Update the attributes of the toolbar bar element.
- */
- updateBarAttributes() {
- const isOriented = this.model.get('isOriented');
- if (isOriented) {
- this.$el.find('.toolbar-bar').attr('data-offset-top', '');
- }
- else {
- this.$el.find('.toolbar-bar').removeAttr('data-offset-top');
- }
- // Toggle between a basic vertical view and a more sophisticated
- // horizontal and vertical display of the toolbar bar and trays.
- this.$el.toggleClass('toolbar-oriented', isOriented);
- },
-
- /**
- * Updates the orientation of the active tray if necessary.
- */
- updateTrayOrientation() {
- const orientation = this.model.get('orientation');
-
- // The antiOrientation is used to render the view of action buttons like
- // the tray orientation toggle.
- const antiOrientation = (orientation === 'vertical') ? 'horizontal' : 'vertical';
-
- // Toggle toolbar's parent classes before other toolbar classes to avoid
- // potential flicker and re-rendering.
- $('body')
- .toggleClass('toolbar-vertical', (orientation === 'vertical'))
- .toggleClass('toolbar-horizontal', (orientation === 'horizontal'));
-
- const removeClass = (antiOrientation === 'horizontal') ? 'toolbar-tray-horizontal' : 'toolbar-tray-vertical';
- const $trays = this.$el.find('.toolbar-tray')
- .removeClass(removeClass)
- .addClass(`toolbar-tray-${orientation}`);
-
- // Update the tray orientation toggle button.
- const iconClass = `toolbar-icon-toggle-${orientation}`;
- const iconAntiClass = `toolbar-icon-toggle-${antiOrientation}`;
- const $orientationToggle = this.$el.find('.toolbar-toggle-orientation')
- .toggle(this.model.get('isTrayToggleVisible'));
- $orientationToggle.find('button')
- .val(antiOrientation)
- .attr('title', this.strings[antiOrientation])
- .text(this.strings[antiOrientation])
- .removeClass(iconClass)
- .addClass(iconAntiClass);
-
- // Update data offset attributes for the trays.
- const dir = document.documentElement.dir;
- const edge = (dir === 'rtl') ? 'right' : 'left';
- // Remove data-offset attributes from the trays so they can be refreshed.
- $trays.removeAttr('data-offset-left data-offset-right data-offset-top');
- // If an active vertical tray exists, mark it as an offset element.
- $trays.filter('.toolbar-tray-vertical.is-active').attr(`data-offset-${edge}`, '');
- // If an active horizontal tray exists, mark it as an offset element.
- $trays.filter('.toolbar-tray-horizontal.is-active').attr('data-offset-top', '');
- },
-
- /**
- * Sets the tops of the trays so that they align with the bottom of the bar.
- */
- adjustPlacement() {
- const $trays = this.$el.find('.toolbar-tray');
- if (!this.model.get('isOriented')) {
- $trays.removeClass('toolbar-tray-horizontal').addClass('toolbar-tray-vertical');
- }
- },
-
- /**
- * Calls the endpoint URI that builds an AJAX command with the rendered
- * subtrees.
- *
- * The rendered admin menu subtrees HTML is cached on the client in
- * localStorage until the cache of the admin menu subtrees on the server-
- * side is invalidated. The subtreesHash is stored in localStorage as well
- * and compared to the subtreesHash in drupalSettings to determine when the
- * admin menu subtrees cache has been invalidated.
- */
- loadSubtrees() {
- const $activeTab = $(this.model.get('activeTab'));
- const orientation = this.model.get('orientation');
- // Only load and render the admin menu subtrees if:
- // (1) They have not been loaded yet.
- // (2) The active tab is the administration menu tab, indicated by the
- // presence of the data-drupal-subtrees attribute.
- // (3) The orientation of the tray is vertical.
- if (!this.model.get('areSubtreesLoaded') && typeof $activeTab.data('drupal-subtrees') !== 'undefined' && orientation === 'vertical') {
- const subtreesHash = drupalSettings.toolbar.subtreesHash;
- const theme = drupalSettings.ajaxPageState.theme;
- const endpoint = Drupal.url(`toolbar/subtrees/${subtreesHash}`);
- const cachedSubtreesHash = localStorage.getItem(`Drupal.toolbar.subtreesHash.${theme}`);
- const cachedSubtrees = JSON.parse(localStorage.getItem(`Drupal.toolbar.subtrees.${theme}`));
- const isVertical = this.model.get('orientation') === 'vertical';
- // If we have the subtrees in localStorage and the subtree hash has not
- // changed, then use the cached data.
- if (isVertical && subtreesHash === cachedSubtreesHash && cachedSubtrees) {
- Drupal.toolbar.setSubtrees.resolve(cachedSubtrees);
+ /**
+ * Update the attributes of the toolbar bar element.
+ */
+ updateBarAttributes() {
+ const isOriented = this.model.get('isOriented');
+ if (isOriented) {
+ this.$el.find('.toolbar-bar').attr('data-offset-top', '');
+ } else {
+ this.$el.find('.toolbar-bar').removeAttr('data-offset-top');
}
- // Only make the call to get the subtrees if the orientation of the
- // toolbar is vertical.
- else if (isVertical) {
- // Remove the cached menu information.
- localStorage.removeItem(`Drupal.toolbar.subtreesHash.${theme}`);
- localStorage.removeItem(`Drupal.toolbar.subtrees.${theme}`);
- // The AJAX response's command will trigger the resolve method of the
- // Drupal.toolbar.setSubtrees Promise.
- Drupal.ajax({ url: endpoint }).execute();
- // Cache the hash for the subtrees locally.
- localStorage.setItem(`Drupal.toolbar.subtreesHash.${theme}`, subtreesHash);
+ // Toggle between a basic vertical view and a more sophisticated
+ // horizontal and vertical display of the toolbar bar and trays.
+ this.$el.toggleClass('toolbar-oriented', isOriented);
+ },
+
+ /**
+ * Updates the orientation of the active tray if necessary.
+ */
+ updateTrayOrientation() {
+ const orientation = this.model.get('orientation');
+
+ // The antiOrientation is used to render the view of action buttons like
+ // the tray orientation toggle.
+ const antiOrientation =
+ orientation === 'vertical' ? 'horizontal' : 'vertical';
+
+ // Toggle toolbar's parent classes before other toolbar classes to avoid
+ // potential flicker and re-rendering.
+ $('body')
+ .toggleClass('toolbar-vertical', orientation === 'vertical')
+ .toggleClass('toolbar-horizontal', orientation === 'horizontal');
+
+ const removeClass =
+ antiOrientation === 'horizontal'
+ ? 'toolbar-tray-horizontal'
+ : 'toolbar-tray-vertical';
+ const $trays = this.$el
+ .find('.toolbar-tray')
+ .removeClass(removeClass)
+ .addClass(`toolbar-tray-${orientation}`);
+
+ // Update the tray orientation toggle button.
+ const iconClass = `toolbar-icon-toggle-${orientation}`;
+ const iconAntiClass = `toolbar-icon-toggle-${antiOrientation}`;
+ const $orientationToggle = this.$el
+ .find('.toolbar-toggle-orientation')
+ .toggle(this.model.get('isTrayToggleVisible'));
+ $orientationToggle
+ .find('button')
+ .val(antiOrientation)
+ .attr('title', this.strings[antiOrientation])
+ .text(this.strings[antiOrientation])
+ .removeClass(iconClass)
+ .addClass(iconAntiClass);
+
+ // Update data offset attributes for the trays.
+ const dir = document.documentElement.dir;
+ const edge = dir === 'rtl' ? 'right' : 'left';
+ // Remove data-offset attributes from the trays so they can be refreshed.
+ $trays.removeAttr('data-offset-left data-offset-right data-offset-top');
+ // If an active vertical tray exists, mark it as an offset element.
+ $trays
+ .filter('.toolbar-tray-vertical.is-active')
+ .attr(`data-offset-${edge}`, '');
+ // If an active horizontal tray exists, mark it as an offset element.
+ $trays
+ .filter('.toolbar-tray-horizontal.is-active')
+ .attr('data-offset-top', '');
+ },
+
+ /**
+ * Sets the tops of the trays so that they align with the bottom of the bar.
+ */
+ adjustPlacement() {
+ const $trays = this.$el.find('.toolbar-tray');
+ if (!this.model.get('isOriented')) {
+ $trays
+ .removeClass('toolbar-tray-horizontal')
+ .addClass('toolbar-tray-vertical');
}
- }
+ },
+
+ /**
+ * Calls the endpoint URI that builds an AJAX command with the rendered
+ * subtrees.
+ *
+ * The rendered admin menu subtrees HTML is cached on the client in
+ * localStorage until the cache of the admin menu subtrees on the server-
+ * side is invalidated. The subtreesHash is stored in localStorage as well
+ * and compared to the subtreesHash in drupalSettings to determine when the
+ * admin menu subtrees cache has been invalidated.
+ */
+ loadSubtrees() {
+ const $activeTab = $(this.model.get('activeTab'));
+ const orientation = this.model.get('orientation');
+ // Only load and render the admin menu subtrees if:
+ // (1) They have not been loaded yet.
+ // (2) The active tab is the administration menu tab, indicated by the
+ // presence of the data-drupal-subtrees attribute.
+ // (3) The orientation of the tray is vertical.
+ if (
+ !this.model.get('areSubtreesLoaded') &&
+ typeof $activeTab.data('drupal-subtrees') !== 'undefined' &&
+ orientation === 'vertical'
+ ) {
+ const subtreesHash = drupalSettings.toolbar.subtreesHash;
+ const theme = drupalSettings.ajaxPageState.theme;
+ const endpoint = Drupal.url(`toolbar/subtrees/${subtreesHash}`);
+ const cachedSubtreesHash = localStorage.getItem(
+ `Drupal.toolbar.subtreesHash.${theme}`,
+ );
+ const cachedSubtrees = JSON.parse(
+ localStorage.getItem(`Drupal.toolbar.subtrees.${theme}`),
+ );
+ const isVertical = this.model.get('orientation') === 'vertical';
+ // If we have the subtrees in localStorage and the subtree hash has not
+ // changed, then use the cached data.
+ if (
+ isVertical &&
+ subtreesHash === cachedSubtreesHash &&
+ cachedSubtrees
+ ) {
+ Drupal.toolbar.setSubtrees.resolve(cachedSubtrees);
+ }
+ // Only make the call to get the subtrees if the orientation of the
+ // toolbar is vertical.
+ else if (isVertical) {
+ // Remove the cached menu information.
+ localStorage.removeItem(`Drupal.toolbar.subtreesHash.${theme}`);
+ localStorage.removeItem(`Drupal.toolbar.subtrees.${theme}`);
+ // The AJAX response's command will trigger the resolve method of the
+ // Drupal.toolbar.setSubtrees Promise.
+ Drupal.ajax({ url: endpoint }).execute();
+ // Cache the hash for the subtrees locally.
+ localStorage.setItem(
+ `Drupal.toolbar.subtreesHash.${theme}`,
+ subtreesHash,
+ );
+ }
+ }
+ },
},
- });
-}(jQuery, Drupal, drupalSettings, Backbone));
+ );
+})(jQuery, Drupal, drupalSettings, Backbone);
diff --git a/core/modules/toolbar/tests/src/FunctionalJavascript/ToolbarIntegrationTest.php b/core/modules/toolbar/tests/src/FunctionalJavascript/ToolbarIntegrationTest.php
index c3b3d9abc..fd163985e 100644
--- a/core/modules/toolbar/tests/src/FunctionalJavascript/ToolbarIntegrationTest.php
+++ b/core/modules/toolbar/tests/src/FunctionalJavascript/ToolbarIntegrationTest.php
@@ -2,14 +2,14 @@
namespace Drupal\Tests\toolbar\FunctionalJavascript;
-use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
+use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
/**
* Tests the JavaScript functionality of the toolbar.
*
* @group toolbar
*/
-class ToolbarIntegrationTest extends JavascriptTestBase {
+class ToolbarIntegrationTest extends WebDriverTestBase {
/**
* {@inheritdoc}
@@ -27,7 +27,12 @@ class ToolbarIntegrationTest extends JavascriptTestBase {
]);
$this->drupalLogin($admin_user);
+ // Set size for horizontal toolbar.
+ $this->getSession()->resizeWindow(1200, 600);
$this->drupalGet('');
+ $this->assertNotEmpty($this->assertSession()->waitForElement('css', 'body.toolbar-horizontal'));
+ $this->assertNotEmpty($this->assertSession()->waitForElementVisible('css', '.toolbar-tray'));
+
$page = $this->getSession()->getPage();
// Test that it is possible to toggle the toolbar tray.
diff --git a/core/modules/toolbar/toolbar.info.yml b/core/modules/toolbar/toolbar.info.yml
index 6967bf657..d0114930b 100644
--- a/core/modules/toolbar/toolbar.info.yml
+++ b/core/modules/toolbar/toolbar.info.yml
@@ -5,4 +5,4 @@ core: 8.x
package: Core
version: VERSION
dependencies:
- - breakpoint
+ - drupal:breakpoint
diff --git a/core/modules/toolbar/toolbar.module b/core/modules/toolbar/toolbar.module
index c7feaee23..044d7490e 100644
--- a/core/modules/toolbar/toolbar.module
+++ b/core/modules/toolbar/toolbar.module
@@ -217,7 +217,7 @@ function toolbar_toolbar() {
* @return array
* The updated renderable array.
*
- * @see drupal_render()
+ * @see \Drupal\Core\Render\RendererInterface::render()
*/
function toolbar_prerender_toolbar_administration_tray(array $element) {
$menu_tree = \Drupal::service('toolbar.menu_tree');
diff --git a/core/modules/tour/js/tour.es6.js b/core/modules/tour/js/tour.es6.js
index fa599d041..b4dcb7abd 100644
--- a/core/modules/tour/js/tour.es6.js
+++ b/core/modules/tour/js/tour.es6.js
@@ -3,7 +3,7 @@
* Attaches behaviors for the Tour module's toolbar tab.
*/
-(function ($, Backbone, Drupal, document) {
+(function($, Backbone, Drupal, document) {
const queryString = decodeURI(window.location.search);
/**
@@ -25,27 +25,31 @@
*/
Drupal.behaviors.tour = {
attach(context) {
- $('body').once('tour').each(() => {
- const model = new Drupal.tour.models.StateModel();
- new Drupal.tour.views.ToggleTourView({
- el: $(context).find('#toolbar-tab-tour'),
- model,
+ $('body')
+ .once('tour')
+ .each(() => {
+ const model = new Drupal.tour.models.StateModel();
+ new Drupal.tour.views.ToggleTourView({
+ el: $(context).find('#toolbar-tab-tour'),
+ model,
+ });
+
+ model
+ // Allow other scripts to respond to tour events.
+ .on('change:isActive', (model, isActive) => {
+ $(document).trigger(
+ isActive ? 'drupalTourStarted' : 'drupalTourStopped',
+ );
+ })
+ // Initialization: check whether a tour is available on the current
+ // page.
+ .set('tour', $(context).find('ol#tour'));
+
+ // Start the tour immediately if toggled via query string.
+ if (/tour=?/i.test(queryString)) {
+ model.set('isActive', true);
+ }
});
-
- model
- // Allow other scripts to respond to tour events.
- .on('change:isActive', (model, isActive) => {
- $(document).trigger((isActive) ? 'drupalTourStarted' : 'drupalTourStopped');
- })
- // Initialization: check whether a tour is available on the current
- // page.
- .set('tour', $(context).find('ol#tour'));
-
- // Start the tour immediately if toggled via query string.
- if (/tour=?/i.test(queryString)) {
- model.set('isActive', true);
- }
- });
},
};
@@ -53,7 +57,6 @@
* @namespace
*/
Drupal.tour = Drupal.tour || {
-
/**
* @namespace Drupal.tour.models
*/
@@ -72,159 +75,158 @@
*
* @augments Backbone.Model
*/
- Drupal.tour.models.StateModel = Backbone.Model.extend(/** @lends Drupal.tour.models.StateModel# */{
+ Drupal.tour.models.StateModel = Backbone.Model.extend(
+ /** @lends Drupal.tour.models.StateModel# */ {
+ /**
+ * @type {object}
+ */
+ defaults: /** @lends Drupal.tour.models.StateModel# */ {
+ /**
+ * Indicates whether the Drupal root window has a tour.
+ *
+ * @type {Array}
+ */
+ tour: [],
- /**
- * @type {object}
- */
- defaults: /** @lends Drupal.tour.models.StateModel# */{
+ /**
+ * Indicates whether the tour is currently running.
+ *
+ * @type {bool}
+ */
+ isActive: false,
+
+ /**
+ * Indicates which tour is the active one (necessary to cleanly stop).
+ *
+ * @type {Array}
+ */
+ activeTour: [],
+ },
+ },
+ );
+
+ Drupal.tour.views.ToggleTourView = Backbone.View.extend(
+ /** @lends Drupal.tour.views.ToggleTourView# */ {
+ /**
+ * @type {object}
+ */
+ events: { click: 'onClick' },
/**
- * Indicates whether the Drupal root window has a tour.
+ * Handles edit mode toggle interactions.
*
- * @type {Array}
+ * @constructs
+ *
+ * @augments Backbone.View
*/
- tour: [],
+ initialize() {
+ this.listenTo(this.model, 'change:tour change:isActive', this.render);
+ this.listenTo(this.model, 'change:isActive', this.toggleTour);
+ },
/**
- * Indicates whether the tour is currently running.
+ * @inheritdoc
*
- * @type {bool}
+ * @return {Drupal.tour.views.ToggleTourView}
+ * The `ToggleTourView` view.
*/
- isActive: false,
+ render() {
+ // Render the visibility.
+ this.$el.toggleClass('hidden', this._getTour().length === 0);
+ // Render the state.
+ const isActive = this.model.get('isActive');
+ this.$el
+ .find('button')
+ .toggleClass('is-active', isActive)
+ .prop('aria-pressed', isActive);
+ return this;
+ },
/**
- * Indicates which tour is the active one (necessary to cleanly stop).
- *
- * @type {Array}
+ * Model change handler; starts or stops the tour.
*/
- activeTour: [],
- },
- });
-
- Drupal.tour.views.ToggleTourView = Backbone.View.extend(/** @lends Drupal.tour.views.ToggleTourView# */{
-
- /**
- * @type {object}
- */
- events: { click: 'onClick' },
-
- /**
- * Handles edit mode toggle interactions.
- *
- * @constructs
- *
- * @augments Backbone.View
- */
- initialize() {
- this.listenTo(this.model, 'change:tour change:isActive', this.render);
- this.listenTo(this.model, 'change:isActive', this.toggleTour);
- },
-
- /**
- * @inheritdoc
- *
- * @return {Drupal.tour.views.ToggleTourView}
- * The `ToggleTourView` view.
- */
- render() {
- // Render the visibility.
- this.$el.toggleClass('hidden', this._getTour().length === 0);
- // Render the state.
- const isActive = this.model.get('isActive');
- this.$el.find('button')
- .toggleClass('is-active', isActive)
- .prop('aria-pressed', isActive);
- return this;
- },
-
- /**
- * Model change handler; starts or stops the tour.
- */
- toggleTour() {
- if (this.model.get('isActive')) {
- const $tour = this._getTour();
- this._removeIrrelevantTourItems($tour, this._getDocument());
- const that = this;
- const close = Drupal.t('Close');
- if ($tour.find('li').length) {
- $tour.joyride({
- autoStart: true,
- postRideCallback() {
- that.model.set('isActive', false);
- },
- // HTML segments for tip layout.
- template: {
- link: `×`,
- button: '',
- },
- });
- this.model.set({ isActive: true, activeTour: $tour });
+ toggleTour() {
+ if (this.model.get('isActive')) {
+ const $tour = this._getTour();
+ this._removeIrrelevantTourItems($tour, this._getDocument());
+ const that = this;
+ const close = Drupal.t('Close');
+ if ($tour.find('li').length) {
+ $tour.joyride({
+ autoStart: true,
+ postRideCallback() {
+ that.model.set('isActive', false);
+ },
+ // HTML segments for tip layout.
+ template: {
+ link: `×`,
+ button:
+ '',
+ },
+ });
+ this.model.set({ isActive: true, activeTour: $tour });
+ }
+ } else {
+ this.model.get('activeTour').joyride('destroy');
+ this.model.set({ isActive: false, activeTour: [] });
}
- }
- else {
- this.model.get('activeTour').joyride('destroy');
- this.model.set({ isActive: false, activeTour: [] });
- }
- },
+ },
- /**
- * Toolbar tab click event handler; toggles isActive.
- *
- * @param {jQuery.Event} event
- * The click event.
- */
- onClick(event) {
- this.model.set('isActive', !this.model.get('isActive'));
- event.preventDefault();
- event.stopPropagation();
- },
+ /**
+ * Toolbar tab click event handler; toggles isActive.
+ *
+ * @param {jQuery.Event} event
+ * The click event.
+ */
+ onClick(event) {
+ this.model.set('isActive', !this.model.get('isActive'));
+ event.preventDefault();
+ event.stopPropagation();
+ },
- /**
- * Gets the tour.
- *
- * @return {jQuery}
- * A jQuery element pointing to a `` containing tour items.
- */
- _getTour() {
- return this.model.get('tour');
- },
+ /**
+ * Gets the tour.
+ *
+ * @return {jQuery}
+ * A jQuery element pointing to a `` containing tour items.
+ */
+ _getTour() {
+ return this.model.get('tour');
+ },
- /**
- * Gets the relevant document as a jQuery element.
- *
- * @return {jQuery}
- * A jQuery element pointing to the document within which a tour would be
- * started given the current state.
- */
- _getDocument() {
- return $(document);
- },
+ /**
+ * Gets the relevant document as a jQuery element.
+ *
+ * @return {jQuery}
+ * A jQuery element pointing to the document within which a tour would be
+ * started given the current state.
+ */
+ _getDocument() {
+ return $(document);
+ },
- /**
- * Removes tour items for elements that don't have matching page elements.
- *
- * Or that are explicitly filtered out via the 'tips' query string.
- *
- * @example
- * This will filter out tips that do not have a matching
- * page element or don't have the "bar" class.
- * http://example.com/foo?tips=bar
- *
- * @param {jQuery} $tour
- * A jQuery element pointing to a `` containing tour items.
- * @param {jQuery} $document
- * A jQuery element pointing to the document within which the elements
- * should be sought.
- *
- * @see Drupal.tour.views.ToggleTourView#_getDocument
- */
- _removeIrrelevantTourItems($tour, $document) {
- let removals = false;
- const tips = /tips=([^&]+)/.exec(queryString);
- $tour
- .find('li')
- .each(function () {
+ /**
+ * Removes tour items for elements that don't have matching page elements.
+ *
+ * Or that are explicitly filtered out via the 'tips' query string.
+ *
+ * @example
+ * This will filter out tips that do not have a matching
+ * page element or don't have the "bar" class.
+ * http://example.com/foo?tips=bar
+ *
+ * @param {jQuery} $tour
+ * A jQuery element pointing to a `` containing tour items.
+ * @param {jQuery} $document
+ * A jQuery element pointing to the document within which the elements
+ * should be sought.
+ *
+ * @see Drupal.tour.views.ToggleTourView#_getDocument
+ */
+ _removeIrrelevantTourItems($tour, $document) {
+ let removals = false;
+ const tips = /tips=([^&]+)/.exec(queryString);
+ $tour.find('li').each(function() {
const $this = $(this);
const itemId = $this.attr('data-id');
const itemClass = $this.attr('data-class');
@@ -236,34 +238,41 @@
return;
}
// Remove tip from the DOM if there is no corresponding page element.
- if ((!itemId && !itemClass) ||
+ if (
+ (!itemId && !itemClass) ||
(itemId && $document.find(`#${itemId}`).length) ||
- (itemClass && $document.find(`.${itemClass}`).length)) {
+ (itemClass && $document.find(`.${itemClass}`).length)
+ ) {
return;
}
removals = true;
$this.remove();
});
- // If there were removals, we'll have to do some clean-up.
- if (removals) {
- const total = $tour.find('li').length;
- if (!total) {
- this.model.set({ tour: [] });
+ // If there were removals, we'll have to do some clean-up.
+ if (removals) {
+ const total = $tour.find('li').length;
+ if (!total) {
+ this.model.set({ tour: [] });
+ }
+
+ $tour
+ .find('li')
+ // Rebuild the progress data.
+ .each(function(index) {
+ const progress = Drupal.t('!tour_item of !total', {
+ '!tour_item': index + 1,
+ '!total': total,
+ });
+ $(this)
+ .find('.tour-progress')
+ .text(progress);
+ })
+ // Update the last item to have "End tour" as the button.
+ .eq(-1)
+ .attr('data-text', Drupal.t('End tour'));
}
-
- $tour
- .find('li')
- // Rebuild the progress data.
- .each(function (index) {
- const progress = Drupal.t('!tour_item of !total', { '!tour_item': index + 1, '!total': total });
- $(this).find('.tour-progress').text(progress);
- })
- // Update the last item to have "End tour" as the button.
- .eq(-1)
- .attr('data-text', Drupal.t('End tour'));
- }
+ },
},
-
- });
-}(jQuery, Backbone, Drupal, document));
+ );
+})(jQuery, Backbone, Drupal, document);
diff --git a/core/modules/tour/js/tour.js b/core/modules/tour/js/tour.js
index 4e675db7b..8893fc3b1 100644
--- a/core/modules/tour/js/tour.js
+++ b/core/modules/tour/js/tour.js
@@ -122,7 +122,10 @@
}
$tour.find('li').each(function (index) {
- var progress = Drupal.t('!tour_item of !total', { '!tour_item': index + 1, '!total': total });
+ var progress = Drupal.t('!tour_item of !total', {
+ '!tour_item': index + 1,
+ '!total': total
+ });
$(this).find('.tour-progress').text(progress);
}).eq(-1).attr('data-text', Drupal.t('End tour'));
}
diff --git a/core/modules/tour/src/Entity/Tour.php b/core/modules/tour/src/Entity/Tour.php
index 9a8e1666a..feca0585b 100644
--- a/core/modules/tour/src/Entity/Tour.php
+++ b/core/modules/tour/src/Entity/Tour.php
@@ -12,6 +12,13 @@ use Drupal\tour\TourInterface;
* @ConfigEntityType(
* id = "tour",
* label = @Translation("Tour"),
+ * label_collection = @Translation("Tours"),
+ * label_singular = @Translation("tour"),
+ * label_plural = @Translation("tours"),
+ * label_count = @PluralTranslation(
+ * singular = "@count tour",
+ * plural = "@count tours",
+ * ),
* handlers = {
* "view_builder" = "Drupal\tour\TourViewBuilder",
* "access" = "Drupal\tour\TourAccessControlHandler",
diff --git a/core/modules/tour/src/Plugin/tour/tip/TipPluginText.php b/core/modules/tour/src/Plugin/tour/tip/TipPluginText.php
index 268741a83..b809a64ee 100644
--- a/core/modules/tour/src/Plugin/tour/tip/TipPluginText.php
+++ b/core/modules/tour/src/Plugin/tour/tip/TipPluginText.php
@@ -39,6 +39,13 @@ class TipPluginText extends TipPluginBase implements ContainerFactoryPluginInter
*/
protected $location;
+ /**
+ * Unique aria-id.
+ *
+ * @var string
+ */
+ protected $ariaId;
+
/**
* Constructs a \Drupal\tour\Plugin\tour\tip\TipPluginText object.
*
@@ -70,11 +77,10 @@ class TipPluginText extends TipPluginBase implements ContainerFactoryPluginInter
* A unique id to be used to generate aria attributes.
*/
public function getAriaId() {
- static $id;
- if (!isset($id)) {
- $id = Html::getUniqueId($this->get('id'));
+ if (!$this->ariaId) {
+ $this->ariaId = Html::getUniqueId($this->get('id'));
}
- return $id;
+ return $this->ariaId;
}
/**
diff --git a/core/modules/tour/src/Tests/TourTest.php b/core/modules/tour/src/Tests/TourTest.php
deleted file mode 100644
index 738bae8e2..000000000
--- a/core/modules/tour/src/Tests/TourTest.php
+++ /dev/null
@@ -1,83 +0,0 @@
- [
- 'data-id' => 'tour-test-1',
- 'data-class' => 'tour-test-1',
- ],
- ];
-
- /**
- * An admin user with administrative permissions for tour.
- *
- * @var \Drupal\user\UserInterface
- */
- protected $adminUser;
-
- /**
- * The permissions required for a logged in user to test tour tips.
- *
- * @var array
- * A list of permissions.
- */
- protected $permissions = ['access tour'];
-
- /**
- * {@inheritdoc}
- */
- protected function setUp() {
- parent::setUp();
-
- // Make sure we are using distinct default and administrative themes for
- // the duration of these tests.
- $this->container->get('theme_handler')->install(['bartik', 'seven']);
- $this->config('system.theme')
- ->set('default', 'bartik')
- ->set('admin', 'seven')
- ->save();
-
- $this->permissions[] = 'view the administration theme';
-
- // Create an admin user to view tour tips.
- $this->adminUser = $this->drupalCreateUser($this->permissions);
- $this->drupalLogin($this->adminUser);
-
- $this->drupalPlaceBlock('local_actions_block', [
- 'theme' => 'seven',
- 'region' => 'content'
- ]);
- }
-
- /**
- * A simple tip test.
- */
- public function testTips() {
- foreach ($this->tips as $path => $attributes) {
- $this->drupalGet($path);
- $this->assertTourTips($attributes);
- }
- }
-
-}
diff --git a/core/modules/hal/tests/src/Functional/EntityResource/Tour/TourHalJsonAnonTest.php b/core/modules/tour/tests/src/Functional/Hal/TourHalJsonAnonTest.php
similarity index 75%
rename from core/modules/hal/tests/src/Functional/EntityResource/Tour/TourHalJsonAnonTest.php
rename to core/modules/tour/tests/src/Functional/Hal/TourHalJsonAnonTest.php
index 536d63470..1461fc1a8 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/Tour/TourHalJsonAnonTest.php
+++ b/core/modules/tour/tests/src/Functional/Hal/TourHalJsonAnonTest.php
@@ -1,9 +1,9 @@
grantPermissionsToTestedRole(['access tour']);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function createEntity() {
+ $tour = Tour::create([
+ 'id' => 'tour-llama',
+ 'label' => 'Llama tour',
+ 'langcode' => 'en',
+ 'module' => 'tour',
+ 'routes' => [
+ [
+ 'route_name' => '',
+ ],
+ ],
+ 'tips' => [
+ 'tour-llama-1' => [
+ 'id' => 'tour-llama-1',
+ 'plugin' => 'text',
+ 'label' => 'Llama',
+ 'body' => 'Who handle the awesomeness of llamas?',
+ 'weight' => 100,
+ 'attributes' => [
+ 'data-id' => 'tour-llama-1',
+ ],
+ ],
+ ],
+ ]);
+ $tour->save();
+
+ return $tour;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedNormalizedEntity() {
+ return [
+ 'dependencies' => [],
+ 'id' => 'tour-llama',
+ 'label' => 'Llama tour',
+ 'langcode' => 'en',
+ 'module' => 'tour',
+ 'routes' => [
+ [
+ 'route_name' => '',
+ ],
+ ],
+ 'status' => TRUE,
+ 'tips' => [
+ 'tour-llama-1' => [
+ 'id' => 'tour-llama-1',
+ 'plugin' => 'text',
+ 'label' => 'Llama',
+ 'body' => 'Who handle the awesomeness of llamas?',
+ 'weight' => 100,
+ 'attributes' => [
+ 'data-id' => 'tour-llama-1',
+ ],
+ ],
+ ],
+ 'uuid' => $this->entity->uuid(),
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getNormalizedPostEntity() {
+ // @todo Update in https://www.drupal.org/node/2300677.
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedCacheContexts() {
+ return [
+ 'user.permissions',
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedUnauthorizedAccessMessage($method) {
+ if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
+ return parent::getExpectedUnauthorizedAccessMessage($method);
+ }
+
+ return "The following permissions are required: 'access tour' OR 'administer site configuration'.";
+ }
+
+}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Tour/TourXmlAnonTest.php b/core/modules/tour/tests/src/Functional/Rest/TourXmlAnonTest.php
similarity index 88%
rename from core/modules/rest/tests/src/Functional/EntityResource/Tour/TourXmlAnonTest.php
rename to core/modules/tour/tests/src/Functional/Rest/TourXmlAnonTest.php
index 4c96e37c4..8f44c87bb 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/Tour/TourXmlAnonTest.php
+++ b/core/modules/tour/tests/src/Functional/Rest/TourXmlAnonTest.php
@@ -1,6 +1,6 @@
drupalPlaceBlock('local_actions_block', [
'theme' => 'seven',
- 'region' => 'content'
+ 'region' => 'content',
]);
}
@@ -130,7 +130,7 @@ class TourTest extends TourTestBasic {
'url' => 'http://local/image.png',
'weight' => 1,
'attributes' => [
- 'data-id' => 'tour-code-test-2'
+ 'data-id' => 'tour-code-test-2',
],
],
],
diff --git a/core/modules/tour/tests/src/Unit/Plugin/tour/tip/TipPluginTextTest.php b/core/modules/tour/tests/src/Unit/Plugin/tour/tip/TipPluginTextTest.php
new file mode 100644
index 000000000..65a6d5145
--- /dev/null
+++ b/core/modules/tour/tests/src/Unit/Plugin/tour/tip/TipPluginTextTest.php
@@ -0,0 +1,44 @@
+ $id_instance_one,
+ ];
+ $config_instance_two = [
+ 'id' => $id_instance_two,
+ ];
+ $definition = [];
+ $plugin_id = 'text';
+ $token = $this->createMock('\Drupal\Core\Utility\Token');
+ $instance_one = new TipPluginText($config_instance_one, $plugin_id, $definition, $token);
+ $instance_two = new TipPluginText($config_instance_two, $plugin_id, $definition, $token);
+ $instance_three = new TipPluginText($config_instance_one, $plugin_id, $definition, $token);
+
+ $this->assertEquals($id_instance_one, $instance_one->getAriaId());
+ $this->assertEquals($id_instance_two, $instance_two->getAriaId());
+ $this->assertNotEquals($instance_one->getAriaId(), $instance_two->getAriaId());
+ $this->assertNotEquals($instance_one->getAriaId(), $instance_three->getAriaId());
+ }
+
+}
diff --git a/core/modules/tour/tests/tour_test/tour_test.info.yml b/core/modules/tour/tests/tour_test/tour_test.info.yml
index 7b31a6f79..a37d5d7a7 100644
--- a/core/modules/tour/tests/tour_test/tour_test.info.yml
+++ b/core/modules/tour/tests/tour_test/tour_test.info.yml
@@ -5,4 +5,4 @@ package: Testing
version: VERSION
core: 8.x
dependencies:
- - tour
+ - drupal:tour
diff --git a/core/modules/tour/tests/tour_test/tour_test.routing.yml b/core/modules/tour/tests/tour_test/tour_test.routing.yml
index 2e74a1da6..86ffeacb5 100644
--- a/core/modules/tour/tests/tour_test/tour_test.routing.yml
+++ b/core/modules/tour/tests/tour_test/tour_test.routing.yml
@@ -28,4 +28,3 @@ tour_test.3:
_controller: '\Drupal\tour_test\Controller\TourTestController::tourTest1'
requirements:
_access: 'TRUE'
-
diff --git a/core/modules/tracker/js/tracker-history.es6.js b/core/modules/tracker/js/tracker-history.es6.js
index 13090008d..dd79f15b9 100644
--- a/core/modules/tracker/js/tracker-history.es6.js
+++ b/core/modules/tracker/js/tracker-history.es6.js
@@ -3,75 +3,22 @@
*
* May only be loaded for authenticated users, with the History module enabled.
*/
-(function ($, Drupal, window) {
- /**
- * Render "new" and "updated" node indicators, as well as "X new" replies links.
- */
- Drupal.behaviors.trackerHistory = {
- attach(context) {
- // Find all "new" comment indicator placeholders newer than 30 days ago that
- // have not already been read after their last comment timestamp.
- const nodeIDs = [];
- const $nodeNewPlaceholders = $(context)
- .find('[data-history-node-timestamp]')
- .once('history')
- .filter(function () {
- const nodeTimestamp = parseInt(this.getAttribute('data-history-node-timestamp'), 10);
- const nodeID = this.getAttribute('data-history-node-id');
- if (Drupal.history.needsServerCheck(nodeID, nodeTimestamp)) {
- nodeIDs.push(nodeID);
- return true;
- }
-
- return false;
- });
-
- // Find all "new" comment indicator placeholders newer than 30 days ago that
- // have not already been read after their last comment timestamp.
- const $newRepliesPlaceholders = $(context)
- .find('[data-history-node-last-comment-timestamp]')
- .once('history')
- .filter(function () {
- const lastCommentTimestamp = parseInt(this.getAttribute('data-history-node-last-comment-timestamp'), 10);
- const nodeTimestamp = parseInt(this.previousSibling.previousSibling.getAttribute('data-history-node-timestamp'), 10);
- // Discard placeholders that have zero comments.
- if (lastCommentTimestamp === nodeTimestamp) {
- return false;
- }
- const nodeID = this.previousSibling.previousSibling.getAttribute('data-history-node-id');
- if (Drupal.history.needsServerCheck(nodeID, lastCommentTimestamp)) {
- if (nodeIDs.indexOf(nodeID) === -1) {
- nodeIDs.push(nodeID);
- }
- return true;
- }
-
- return false;
- });
-
- if ($nodeNewPlaceholders.length === 0 && $newRepliesPlaceholders.length === 0) {
- return;
- }
-
- // Fetch the node read timestamps from the server.
- Drupal.history.fetchTimestamps(nodeIDs, () => {
- processNodeNewIndicators($nodeNewPlaceholders);
- processNewRepliesIndicators($newRepliesPlaceholders);
- });
- },
- };
-
+(function($, Drupal, window) {
function processNodeNewIndicators($placeholders) {
const newNodeString = Drupal.t('new');
const updatedNodeString = Drupal.t('updated');
$placeholders.each((index, placeholder) => {
- const timestamp = parseInt(placeholder.getAttribute('data-history-node-timestamp'), 10);
+ const timestamp = parseInt(
+ placeholder.getAttribute('data-history-node-timestamp'),
+ 10,
+ );
const nodeID = placeholder.getAttribute('data-history-node-id');
const lastViewTimestamp = Drupal.history.getLastRead(nodeID);
if (timestamp > lastViewTimestamp) {
- const message = (lastViewTimestamp === 0) ? newNodeString : updatedNodeString;
+ const message =
+ lastViewTimestamp === 0 ? newNodeString : updatedNodeString;
$(placeholder).append(`${message}`);
}
});
@@ -81,8 +28,13 @@
// Figure out which placeholders need the "x new" replies links.
const placeholdersToUpdate = {};
$placeholders.each((index, placeholder) => {
- const timestamp = parseInt(placeholder.getAttribute('data-history-node-last-comment-timestamp'), 10);
- const nodeID = placeholder.previousSibling.previousSibling.getAttribute('data-history-node-id');
+ const timestamp = parseInt(
+ placeholder.getAttribute('data-history-node-last-comment-timestamp'),
+ 10,
+ );
+ const nodeID = placeholder.previousSibling.previousSibling.getAttribute(
+ 'data-history-node-id',
+ );
const lastViewTimestamp = Drupal.history.getLastRead(nodeID);
// Queue this placeholder's "X new" replies link to be downloaded from the
@@ -103,14 +55,93 @@
data: { 'node_ids[]': nodeIDs },
dataType: 'json',
success(results) {
- Object.keys(results || {}).forEach((nodeID) => {
+ Object.keys(results || {}).forEach(nodeID => {
if (placeholdersToUpdate.hasOwnProperty(nodeID)) {
const url = results[nodeID].first_new_comment_link;
- const text = Drupal.formatPlural(results[nodeID].new_comment_count, '1 new', '@count new');
- $(placeholdersToUpdate[nodeID]).append(`
${text}`);
+ const text = Drupal.formatPlural(
+ results[nodeID].new_comment_count,
+ '1 new',
+ '@count new',
+ );
+ $(placeholdersToUpdate[nodeID]).append(
+ `
${text}`,
+ );
}
});
},
});
}
-}(jQuery, Drupal, window));
+
+ /**
+ * Render "new" and "updated" node indicators, as well as "X new" replies links.
+ */
+ Drupal.behaviors.trackerHistory = {
+ attach(context) {
+ // Find all "new" comment indicator placeholders newer than 30 days ago that
+ // have not already been read after their last comment timestamp.
+ const nodeIDs = [];
+ const $nodeNewPlaceholders = $(context)
+ .find('[data-history-node-timestamp]')
+ .once('history')
+ .filter(function() {
+ const nodeTimestamp = parseInt(
+ this.getAttribute('data-history-node-timestamp'),
+ 10,
+ );
+ const nodeID = this.getAttribute('data-history-node-id');
+ if (Drupal.history.needsServerCheck(nodeID, nodeTimestamp)) {
+ nodeIDs.push(nodeID);
+ return true;
+ }
+
+ return false;
+ });
+
+ // Find all "new" comment indicator placeholders newer than 30 days ago that
+ // have not already been read after their last comment timestamp.
+ const $newRepliesPlaceholders = $(context)
+ .find('[data-history-node-last-comment-timestamp]')
+ .once('history')
+ .filter(function() {
+ const lastCommentTimestamp = parseInt(
+ this.getAttribute('data-history-node-last-comment-timestamp'),
+ 10,
+ );
+ const nodeTimestamp = parseInt(
+ this.previousSibling.previousSibling.getAttribute(
+ 'data-history-node-timestamp',
+ ),
+ 10,
+ );
+ // Discard placeholders that have zero comments.
+ if (lastCommentTimestamp === nodeTimestamp) {
+ return false;
+ }
+ const nodeID = this.previousSibling.previousSibling.getAttribute(
+ 'data-history-node-id',
+ );
+ if (Drupal.history.needsServerCheck(nodeID, lastCommentTimestamp)) {
+ if (nodeIDs.indexOf(nodeID) === -1) {
+ nodeIDs.push(nodeID);
+ }
+ return true;
+ }
+
+ return false;
+ });
+
+ if (
+ $nodeNewPlaceholders.length === 0 &&
+ $newRepliesPlaceholders.length === 0
+ ) {
+ return;
+ }
+
+ // Fetch the node read timestamps from the server.
+ Drupal.history.fetchTimestamps(nodeIDs, () => {
+ processNodeNewIndicators($nodeNewPlaceholders);
+ processNewRepliesIndicators($newRepliesPlaceholders);
+ });
+ },
+ };
+})(jQuery, Drupal, window);
diff --git a/core/modules/tracker/js/tracker-history.js b/core/modules/tracker/js/tracker-history.js
index a147eb8a6..30a500fe7 100644
--- a/core/modules/tracker/js/tracker-history.js
+++ b/core/modules/tracker/js/tracker-history.js
@@ -6,49 +6,6 @@
**/
(function ($, Drupal, window) {
- Drupal.behaviors.trackerHistory = {
- attach: function attach(context) {
- var nodeIDs = [];
- var $nodeNewPlaceholders = $(context).find('[data-history-node-timestamp]').once('history').filter(function () {
- var nodeTimestamp = parseInt(this.getAttribute('data-history-node-timestamp'), 10);
- var nodeID = this.getAttribute('data-history-node-id');
- if (Drupal.history.needsServerCheck(nodeID, nodeTimestamp)) {
- nodeIDs.push(nodeID);
- return true;
- }
-
- return false;
- });
-
- var $newRepliesPlaceholders = $(context).find('[data-history-node-last-comment-timestamp]').once('history').filter(function () {
- var lastCommentTimestamp = parseInt(this.getAttribute('data-history-node-last-comment-timestamp'), 10);
- var nodeTimestamp = parseInt(this.previousSibling.previousSibling.getAttribute('data-history-node-timestamp'), 10);
-
- if (lastCommentTimestamp === nodeTimestamp) {
- return false;
- }
- var nodeID = this.previousSibling.previousSibling.getAttribute('data-history-node-id');
- if (Drupal.history.needsServerCheck(nodeID, lastCommentTimestamp)) {
- if (nodeIDs.indexOf(nodeID) === -1) {
- nodeIDs.push(nodeID);
- }
- return true;
- }
-
- return false;
- });
-
- if ($nodeNewPlaceholders.length === 0 && $newRepliesPlaceholders.length === 0) {
- return;
- }
-
- Drupal.history.fetchTimestamps(nodeIDs, function () {
- processNodeNewIndicators($nodeNewPlaceholders);
- processNewRepliesIndicators($newRepliesPlaceholders);
- });
- }
- };
-
function processNodeNewIndicators($placeholders) {
var newNodeString = Drupal.t('new');
var updatedNodeString = Drupal.t('updated');
@@ -97,4 +54,47 @@
}
});
}
+
+ Drupal.behaviors.trackerHistory = {
+ attach: function attach(context) {
+ var nodeIDs = [];
+ var $nodeNewPlaceholders = $(context).find('[data-history-node-timestamp]').once('history').filter(function () {
+ var nodeTimestamp = parseInt(this.getAttribute('data-history-node-timestamp'), 10);
+ var nodeID = this.getAttribute('data-history-node-id');
+ if (Drupal.history.needsServerCheck(nodeID, nodeTimestamp)) {
+ nodeIDs.push(nodeID);
+ return true;
+ }
+
+ return false;
+ });
+
+ var $newRepliesPlaceholders = $(context).find('[data-history-node-last-comment-timestamp]').once('history').filter(function () {
+ var lastCommentTimestamp = parseInt(this.getAttribute('data-history-node-last-comment-timestamp'), 10);
+ var nodeTimestamp = parseInt(this.previousSibling.previousSibling.getAttribute('data-history-node-timestamp'), 10);
+
+ if (lastCommentTimestamp === nodeTimestamp) {
+ return false;
+ }
+ var nodeID = this.previousSibling.previousSibling.getAttribute('data-history-node-id');
+ if (Drupal.history.needsServerCheck(nodeID, lastCommentTimestamp)) {
+ if (nodeIDs.indexOf(nodeID) === -1) {
+ nodeIDs.push(nodeID);
+ }
+ return true;
+ }
+
+ return false;
+ });
+
+ if ($nodeNewPlaceholders.length === 0 && $newRepliesPlaceholders.length === 0) {
+ return;
+ }
+
+ Drupal.history.fetchTimestamps(nodeIDs, function () {
+ processNodeNewIndicators($nodeNewPlaceholders);
+ processNewRepliesIndicators($newRepliesPlaceholders);
+ });
+ }
+ };
})(jQuery, Drupal, window);
\ No newline at end of file
diff --git a/core/modules/tracker/src/Plugin/Menu/UserTrackerTab.php b/core/modules/tracker/src/Plugin/Menu/UserTrackerTab.php
index 95264803f..db0f2b463 100644
--- a/core/modules/tracker/src/Plugin/Menu/UserTrackerTab.php
+++ b/core/modules/tracker/src/Plugin/Menu/UserTrackerTab.php
@@ -32,7 +32,6 @@ class UserTrackerTab extends LocalTaskDefault {
return $this->currentUser;
}
-
/**
* {@inheritdoc}
*/
diff --git a/core/modules/tracker/tests/modules/tracker_test_views/tracker_test_views.info.yml b/core/modules/tracker/tests/modules/tracker_test_views/tracker_test_views.info.yml
index 45a64a1a4..7e9f34fb4 100644
--- a/core/modules/tracker/tests/modules/tracker_test_views/tracker_test_views.info.yml
+++ b/core/modules/tracker/tests/modules/tracker_test_views/tracker_test_views.info.yml
@@ -5,5 +5,5 @@ package: Testing
version: VERSION
core: 8.x
dependencies:
- - tracker
- - views
+ - drupal:tracker
+ - drupal:views
diff --git a/core/modules/tracker/tests/src/Functional/TrackerTest.php b/core/modules/tracker/tests/src/Functional/TrackerTest.php
index 1a77d95ac..75b7b5cde 100644
--- a/core/modules/tracker/tests/src/Functional/TrackerTest.php
+++ b/core/modules/tracker/tests/src/Functional/TrackerTest.php
@@ -93,6 +93,7 @@ class TrackerTest extends BrowserTestBase {
$expected_tags = Cache::mergeTags($expected_tags, $role_tags);
$block_tags = [
'block_view',
+ 'local_task',
'config:block.block.page_actions_block',
'config:block.block.page_tabs_block',
'config:block_list',
@@ -179,6 +180,7 @@ class TrackerTest extends BrowserTestBase {
$expected_tags = Cache::mergeTags($expected_tags, $role_tags);
$block_tags = [
'block_view',
+ 'local_task',
'config:block.block.page_actions_block',
'config:block.block.page_tabs_block',
'config:block_list',
@@ -361,8 +363,14 @@ class TrackerTest extends BrowserTestBase {
];
$this->drupalPostForm('comment/reply/node/' . $nodes[3]->id() . '/comment', $comment, t('Save'));
- // Start indexing backwards from node 3.
- \Drupal::state()->set('tracker.index_nid', 3);
+ // Create an unpublished node.
+ $unpublished = $this->drupalCreateNode([
+ 'title' => $this->randomMachineName(8),
+ 'status' => 0,
+ ]);
+
+ // Start indexing backwards from node 4.
+ \Drupal::state()->set('tracker.index_nid', 4);
// Clear the current tracker tables and rebuild them.
db_delete('tracker_node')
diff --git a/core/modules/tracker/tests/src/Functional/Views/TrackerUserUidTest.php b/core/modules/tracker/tests/src/Functional/Views/TrackerUserUidTest.php
index 7bd675b2e..9f2b0a758 100644
--- a/core/modules/tracker/tests/src/Functional/Views/TrackerUserUidTest.php
+++ b/core/modules/tracker/tests/src/Functional/Views/TrackerUserUidTest.php
@@ -31,7 +31,7 @@ class TrackerUserUidTest extends TrackerTestBase {
[
'nid' => $this->node->id(),
'title' => $this->node->label(),
- ]
+ ],
];
$view = Views::getView('test_tracker_user_uid');
diff --git a/core/modules/tracker/tests/src/Kernel/Plugin/migrate/source/d7/TrackerNodeTest.php b/core/modules/tracker/tests/src/Kernel/Plugin/migrate/source/d7/TrackerNodeTest.php
index 0318b8e7b..28d377261 100644
--- a/core/modules/tracker/tests/src/Kernel/Plugin/migrate/source/d7/TrackerNodeTest.php
+++ b/core/modules/tracker/tests/src/Kernel/Plugin/migrate/source/d7/TrackerNodeTest.php
@@ -30,7 +30,7 @@ class TrackerNodeTest extends MigrateSqlSourceTestBase {
'nid' => '2',
'published' => '1',
'changed' => '1421727536',
- ]
+ ],
];
// The expected results are identical to the source data.
diff --git a/core/modules/tracker/tests/src/Kernel/Plugin/migrate/source/d7/TrackerUserTest.php b/core/modules/tracker/tests/src/Kernel/Plugin/migrate/source/d7/TrackerUserTest.php
index ffcee93b5..293e559a2 100644
--- a/core/modules/tracker/tests/src/Kernel/Plugin/migrate/source/d7/TrackerUserTest.php
+++ b/core/modules/tracker/tests/src/Kernel/Plugin/migrate/source/d7/TrackerUserTest.php
@@ -31,7 +31,7 @@ class TrackerUserTest extends MigrateSqlSourceTestBase {
'uid' => '2',
'published' => '1',
'changed' => '1421727536',
- ]
+ ],
];
// The expected results are identical to the source data.
diff --git a/core/modules/tracker/tracker.info.yml b/core/modules/tracker/tracker.info.yml
index 79d179d1c..d85521067 100644
--- a/core/modules/tracker/tracker.info.yml
+++ b/core/modules/tracker/tracker.info.yml
@@ -2,8 +2,8 @@ name: Activity Tracker
type: module
description: 'Enables tracking of recent content for users.'
dependencies:
- - node
- - comment
+ - drupal:node
+ - drupal:comment
package: Core
version: VERSION
core: 8.x
diff --git a/core/modules/tracker/tracker.module b/core/modules/tracker/tracker.module
index bf40dc1e8..dadefecab 100644
--- a/core/modules/tracker/tracker.module
+++ b/core/modules/tracker/tracker.module
@@ -72,7 +72,7 @@ function tracker_cron() {
db_insert('tracker_node')
->fields([
'nid' => $nid,
- 'published' => $node->isPublished(),
+ 'published' => (int) $node->isPublished(),
'changed' => $changed,
])
->execute();
@@ -81,7 +81,7 @@ function tracker_cron() {
db_insert('tracker_user')
->fields([
'nid' => $nid,
- 'published' => $node->isPublished(),
+ 'published' => (int) $node->isPublished(),
'changed' => $changed,
'uid' => $node->getOwnerId(),
])
diff --git a/core/modules/tracker/tracker.routing.yml b/core/modules/tracker/tracker.routing.yml
index ea9855f32..7d94f9ba1 100644
--- a/core/modules/tracker/tracker.routing.yml
+++ b/core/modules/tracker/tracker.routing.yml
@@ -25,4 +25,3 @@ tracker.user_tab:
_permission: 'access content'
_entity_access: 'user.view'
user: \d+
-
diff --git a/core/modules/tracker/tracker.views.inc b/core/modules/tracker/tracker.views.inc
index 54402879d..be15d5747 100644
--- a/core/modules/tracker/tracker.views.inc
+++ b/core/modules/tracker/tracker.views.inc
@@ -173,7 +173,7 @@ function tracker_views_data_alter(&$data) {
'field' => 'uid',
'name table' => 'users_field_data',
'name field' => 'name',
- 'id' => 'tracker_user_uid'
+ 'id' => 'tracker_user_uid',
],
];
}
diff --git a/core/modules/update/src/Controller/UpdateController.php b/core/modules/update/src/Controller/UpdateController.php
index 30b25c206..6a8ec6abc 100644
--- a/core/modules/update/src/Controller/UpdateController.php
+++ b/core/modules/update/src/Controller/UpdateController.php
@@ -45,7 +45,7 @@ class UpdateController extends ControllerBase {
*/
public function updateStatus() {
$build = [
- '#theme' => 'update_report'
+ '#theme' => 'update_report',
];
if ($available = update_get_available(TRUE)) {
$this->moduleHandler()->loadInclude('update', 'compare.inc');
diff --git a/core/modules/update/src/Form/UpdateManagerInstall.php b/core/modules/update/src/Form/UpdateManagerInstall.php
index a3e7495c6..c3fed3848 100644
--- a/core/modules/update/src/Form/UpdateManagerInstall.php
+++ b/core/modules/update/src/Form/UpdateManagerInstall.php
@@ -135,18 +135,19 @@ class UpdateManagerInstall extends FormBase {
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$local_cache = NULL;
+ $all_files = $this->getRequest()->files->get('files', []);
if ($form_state->getValue('project_url')) {
$local_cache = update_manager_file_get($form_state->getValue('project_url'));
if (!$local_cache) {
- drupal_set_message($this->t('Unable to retrieve Drupal project from %url.', ['%url' => $form_state->getValue('project_url')]), 'error');
+ $this->messenger()->addError($this->t('Unable to retrieve Drupal project from %url.', ['%url' => $form_state->getValue('project_url')]));
return;
}
}
- elseif ($_FILES['files']['name']['project_upload']) {
+ elseif (!empty($all_files['project_upload'])) {
$validators = ['file_validate_extensions' => [archiver_get_extensions()]];
if (!($finfo = file_save_upload('project_upload', $validators, NULL, 0, FILE_EXISTS_REPLACE))) {
// Failed to upload the file. file_save_upload() calls
- // drupal_set_message() on failure.
+ // \Drupal\Core\Messenger\MessengerInterface::addError() on failure.
return;
}
$local_cache = $finfo->getFileUri();
@@ -157,13 +158,13 @@ class UpdateManagerInstall extends FormBase {
$archive = update_manager_archive_extract($local_cache, $directory);
}
catch (\Exception $e) {
- drupal_set_message($e->getMessage(), 'error');
+ $this->messenger()->addError($e->getMessage());
return;
}
$files = $archive->listContents();
if (!$files) {
- drupal_set_message($this->t('Provided archive contains no files.'), 'error');
+ $this->messenger()->addError($this->t('Provided archive contains no files.'));
return;
}
@@ -174,12 +175,12 @@ class UpdateManagerInstall extends FormBase {
$archive_errors = $this->moduleHandler->invokeAll('verify_update_archive', [$project, $local_cache, $directory]);
if (!empty($archive_errors)) {
- drupal_set_message(array_shift($archive_errors), 'error');
+ $this->messenger()->addError(array_shift($archive_errors));
// @todo: Fix me in D8: We need a way to set multiple errors on the same
// form element and have all of them appear!
if (!empty($archive_errors)) {
foreach ($archive_errors as $error) {
- drupal_set_message($error, 'error');
+ $this->messenger()->addError($error);
}
}
return;
@@ -193,7 +194,7 @@ class UpdateManagerInstall extends FormBase {
$updater = Updater::factory($project_location, $this->root);
}
catch (\Exception $e) {
- drupal_set_message($e->getMessage(), 'error');
+ $this->messenger()->addError($e->getMessage());
return;
}
@@ -201,16 +202,16 @@ class UpdateManagerInstall extends FormBase {
$project_title = Updater::getProjectTitle($project_location);
}
catch (\Exception $e) {
- drupal_set_message($e->getMessage(), 'error');
+ $this->messenger()->addError($e->getMessage());
return;
}
if (!$project_title) {
- drupal_set_message($this->t('Unable to determine %project name.', ['%project' => $project]), 'error');
+ $this->messenger()->addError($this->t('Unable to determine %project name.', ['%project' => $project]));
}
if ($updater->isInstalled()) {
- drupal_set_message($this->t('%project is already installed.', ['%project' => $project_title]), 'error');
+ $this->messenger()->addError($this->t('%project is already installed.', ['%project' => $project_title]));
return;
}
diff --git a/core/modules/update/src/UpdateSettingsForm.php b/core/modules/update/src/UpdateSettingsForm.php
index ac8981cf4..2a4e30d48 100644
--- a/core/modules/update/src/UpdateSettingsForm.php
+++ b/core/modules/update/src/UpdateSettingsForm.php
@@ -95,7 +95,7 @@ class UpdateSettingsForm extends ConfigFormBase implements ContainerInjectionInt
'all' => t('All newer versions'),
'security' => t('Only security updates'),
],
- '#description' => t('You can choose to send email only if a security update is available, or to be notified about all newer versions. If there are updates available of Drupal core or any of your installed modules and themes, your site will always print a message on the status report page, and will also display an error message on administration pages if there is a security update.', [':status_report' => $this->url('system.status')])
+ '#description' => t('You can choose to send email only if a security update is available, or to be notified about all newer versions. If there are updates available of Drupal core or any of your installed modules and themes, your site will always print a message on the status report page, and will also display an error message on administration pages if there is a security update.', [':status_report' => $this->url('system.status')]),
];
return parent::buildForm($form, $form_state);
diff --git a/core/modules/update/tests/aaa_update_test/aaa_update_test.info.yml b/core/modules/update/tests/aaa_update_test/aaa_update_test.info.yml
deleted file mode 100644
index c424e74f7..000000000
--- a/core/modules/update/tests/aaa_update_test/aaa_update_test.info.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-name: 'AAA Update test'
-type: module
-description: 'Support module for update module testing.'
-package: Testing
-version: VERSION
-core: 8.x
diff --git a/core/modules/update/tests/modules/update_test/aaa_update_test.sec.8.x-1.1_8.x-1.2.xml b/core/modules/update/tests/modules/update_test/aaa_update_test.sec.8.x-1.1_8.x-1.2.xml
new file mode 100644
index 000000000..560c82bf0
--- /dev/null
+++ b/core/modules/update/tests/modules/update_test/aaa_update_test.sec.8.x-1.1_8.x-1.2.xml
@@ -0,0 +1,72 @@
+
+
+ AAA Update test
+ aaa_update_test
+ Drupal
+ 8.x
+ 1
+ 1
+ 1
+ published
+ http://example.com/project/aaa_update_test
+
+ ProjectsModules
+
+
+
+ aaa_update_test 8.x-1.2
+ 8.x-1.2
+ DRUPAL-8--1-2
+ 1
+ 2
+ published
+ http://example.com/aaa_update_test-8-x-1-2-release
+ http://example.com/aaa_update_test-8-x-1-2.tar.gz
+ 1250424521
+ b966255555d9c9b86d480ca08cfaa98e
+ 1073741824
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeSecurity update
+
+
+
+ aaa_update_test 8.x-1.1
+ 8.x-1.1
+ DRUPAL-8--1-1
+ 1
+ 1
+ published
+ http://example.com/aaa_update_test-8-x-1-1-release
+ http://example.com/aaa_update_test-8-x-1-1.tar.gz
+ 1250424521
+ b966255555d9c9b86d480ca08cfaa98e
+ 1073741824
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeSecurity update
+ Release typeInsecure
+
+
+
+ aaa_update_test 8.x-1.0
+ 8.x-1.0
+ DRUPAL-8--1-0
+ 1
+ 0
+ published
+ http://example.com/aaa_update_test-8-x-1-0-release
+ http://example.com/aaa_update_test-8-x-1-0.tar.gz
+ 1250424521
+ b966255555d9c9b86d480ca08cfaa98e
+ 1073741824
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeInsecure
+
+
+
+
diff --git a/core/modules/update/tests/modules/update_test/aaa_update_test.sec.8.x-1.2.xml b/core/modules/update/tests/modules/update_test/aaa_update_test.sec.8.x-1.2.xml
new file mode 100644
index 000000000..0ebb766dd
--- /dev/null
+++ b/core/modules/update/tests/modules/update_test/aaa_update_test.sec.8.x-1.2.xml
@@ -0,0 +1,71 @@
+
+
+ AAA Update test
+ aaa_update_test
+ Drupal
+ 8.x
+ 1
+ 1
+ 1
+ published
+ http://example.com/project/aaa_update_test
+
+ ProjectsModules
+
+
+
+ aaa_update_test 8.x-1.2
+ 8.x-1.2
+ DRUPAL-8--1-2
+ 1
+ 2
+ published
+ http://example.com/aaa_update_test-8-x-1-2-release
+ http://example.com/aaa_update_test-8-x-1-2.tar.gz
+ 1250424521
+ b966255555d9c9b86d480ca08cfaa98e
+ 1073741824
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeSecurity update
+
+
+
+ aaa_update_test 8.x-1.1
+ 8.x-1.1
+ DRUPAL-8--1-1
+ 1
+ 1
+ published
+ http://example.com/aaa_update_test-8-x-1-1-release
+ http://example.com/aaa_update_test-8-x-1-1.tar.gz
+ 1250424521
+ b966255555d9c9b86d480ca08cfaa98e
+ 1073741824
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeInsecure
+
+
+
+ aaa_update_test 8.x-1.0
+ 8.x-1.0
+ DRUPAL-8--1-0
+ 1
+ 0
+ published
+ http://example.com/aaa_update_test-8-x-1-0-release
+ http://example.com/aaa_update_test-8-x-1-0.tar.gz
+ 1250424521
+ b966255555d9c9b86d480ca08cfaa98e
+ 1073741824
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeInsecure
+
+
+
+
diff --git a/core/modules/update/tests/modules/update_test/aaa_update_test.sec.8.x-1.2_8.x-2.2.xml b/core/modules/update/tests/modules/update_test/aaa_update_test.sec.8.x-1.2_8.x-2.2.xml
new file mode 100644
index 000000000..ee5265901
--- /dev/null
+++ b/core/modules/update/tests/modules/update_test/aaa_update_test.sec.8.x-1.2_8.x-2.2.xml
@@ -0,0 +1,169 @@
+
+
+ AAA Update test
+ aaa_update_test
+ Drupal
+ 8.x
+ 2
+ 1,2
+ 2
+ published
+ http://example.com/project/aaa_update_test
+
+ ProjectsModules
+
+
+
+ aaa_update_test 8.x-3.0-beta2
+ 8.x-3.0-beta2
+ 8.x-3.0-beta2
+ 3
+ 0
+ beta2
+ published
+ http://example.com/aaa_update_test-8-x-3-0-beta2-release
+ http://example.com/aaa_update_test-8-x-3-0-beta2.tar.gz
+ 1533298080
+ 2387284145e34a19fc45e03c258a2831
+ 16209911
+
+ Release typeBug fixes
+ Release typeNew features
+
+
+ Beta releases are not covered by Drupal security advisories.
+
+
+
+ aaa_update_test 8.x-3.0-beta1
+ 8.x-3.0-beta1
+ 8.x-3.0-beta1
+ 3
+ 0
+ beta1
+ published
+ http://example.com/aaa_update_test-8-x-3-0-beta1
+ http://example.com/aaa_update_test--8-x-3-0-beta1.tar.gz
+ 1533270485
+ 3d70ae568dd8f7100082b8c58bc8d1fe
+ 16209123
+
+ Release typeInsecure
+ Release typeBug fixes
+ Release typeNew features
+
+
+ Beta releases are not covered by Drupal security advisories.
+
+
+
+ aaa_update_test 8.x-2.2
+ 8.x-2.2
+ DRUPAL-8--2-2
+ 2
+ 2
+ published
+ http://example.com/aaa_update_test-8-x-2-2-release
+ http://example.com/aaa_update_test-8-x-2-2.tar.gz
+ 1250424521
+ b966255555d9c9b86d480ca08cfaa98e
+ 1073741824
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeSecurity update
+
+
+
+ aaa_update_test 8.x-2.1
+ 8.x-2.1
+ DRUPAL-8--2-1
+ 2
+ 1
+ published
+ http://example.com/aaa_update_test-8-x-2-1-release
+ http://example.com/aaa_update_test-8-x-2-1.tar.gz
+ 1250424521
+ b966255555d9c9b86d480ca08cfaa98e
+ 1073741824
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeSecurity update
+ Release typeInsecure
+
+
+
+ aaa_update_test 8.x-2.0
+ 8.x-2.0
+ DRUPAL-8--2-0
+ 2
+ 0
+ published
+ http://example.com/aaa_update_test-8-x-2-0-release
+ http://example.com/aaa_update_test-8-x-2-0.tar.gz
+ 1250424521
+ b966255555d9c9b86d480ca08cfaa98e
+ 1073741824
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeInsecure
+
+
+
+ aaa_update_test 8.x-1.2
+ 8.x-1.2
+ DRUPAL-8--1-2
+ 1
+ 2
+ published
+ http://example.com/aaa_update_test-8-x-1-2-release
+ http://example.com/aaa_update_test-8-x-1-2.tar.gz
+ 1250424521
+ b966255555d9c9b86d480ca08cfaa98e
+ 1073741824
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeSecurity update
+
+
+
+ aaa_update_test 8.x-1.1
+ 8.x-1.1
+ DRUPAL-8--1-1
+ 1
+ 1
+ published
+ http://example.com/aaa_update_test-8-x-1-1-release
+ http://example.com/aaa_update_test-8-x-1-1.tar.gz
+ 1250424521
+ b966255555d9c9b86d480ca08cfaa98e
+ 1073741824
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeInsecure
+
+
+
+ aaa_update_test 8.x-1.0
+ 8.x-1.0
+ DRUPAL-8--1-0
+ 1
+ 0
+ published
+ http://example.com/aaa_update_test-8-x-1-0-release
+ http://example.com/aaa_update_test-8-x-1-0.tar.gz
+ 1250424521
+ b966255555d9c9b86d480ca08cfaa98e
+ 1073741824
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeInsecure
+
+
+
+
diff --git a/core/modules/update/tests/modules/update_test/aaa_update_test.sec.8.x-2.2_1.x_secure.xml b/core/modules/update/tests/modules/update_test/aaa_update_test.sec.8.x-2.2_1.x_secure.xml
new file mode 100644
index 000000000..a42b58d5c
--- /dev/null
+++ b/core/modules/update/tests/modules/update_test/aaa_update_test.sec.8.x-2.2_1.x_secure.xml
@@ -0,0 +1,123 @@
+
+
+ AAA Update test
+ aaa_update_test
+ Drupal
+ 8.x
+ 2
+ 1,2
+ 2
+ published
+ http://example.com/project/aaa_update_test
+
+ ProjectsModules
+
+
+
+ aaa_update_test 8.x-2.2
+ 8.x-2.2
+ DRUPAL-8--2-2
+ 2
+ 2
+ published
+ http://example.com/aaa_update_test-8-x-2-2-release
+ http://example.com/aaa_update_test-8-x-2-2.tar.gz
+ 1250424521
+ b966255555d9c9b86d480ca08cfaa98e
+ 1073741824
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeSecurity update
+
+
+
+ aaa_update_test 8.x-2.1
+ 8.x-2.1
+ DRUPAL-8--2-1
+ 2
+ 1
+ published
+ http://example.com/aaa_update_test-8-x-2-1-release
+ http://example.com/aaa_update_test-8-x-2-1.tar.gz
+ 1250424521
+ b966255555d9c9b86d480ca08cfaa98e
+ 1073741824
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeSecurity update
+ Release typeInsecure
+
+
+
+ aaa_update_test 8.x-2.0
+ 8.x-2.0
+ DRUPAL-8--2-0
+ 2
+ 0
+ published
+ http://example.com/aaa_update_test-8-x-2-0-release
+ http://example.com/aaa_update_test-8-x-2-0.tar.gz
+ 1250424521
+ b966255555d9c9b86d480ca08cfaa98e
+ 1073741824
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeInsecure
+
+
+
+ aaa_update_test 8.x-1.2
+ 8.x-1.2
+ DRUPAL-8--1-2
+ 1
+ 2
+ published
+ http://example.com/aaa_update_test-8-x-1-2-release
+ http://example.com/aaa_update_test-8-x-1-2.tar.gz
+ 1250424521
+ b966255555d9c9b86d480ca08cfaa98e
+ 1073741824
+
+ Release typeNew features
+ Release typeBug fixes
+
+
+
+ aaa_update_test 8.x-1.1
+ 8.x-1.1
+ DRUPAL-8--1-1
+ 1
+ 1
+ published
+ http://example.com/aaa_update_test-8-x-1-1-release
+ http://example.com/aaa_update_test-8-x-1-1.tar.gz
+ 1250424521
+ b966255555d9c9b86d480ca08cfaa98e
+ 1073741824
+
+ Release typeNew features
+ Release typeBug fixes
+
+
+
+ aaa_update_test 8.x-1.0
+ 8.x-1.0
+ DRUPAL-8--1-0
+ 1
+ 0
+ published
+ http://example.com/aaa_update_test-8-x-1-0-release
+ http://example.com/aaa_update_test-8-x-1-0.tar.gz
+ 1250424521
+ b966255555d9c9b86d480ca08cfaa98e
+ 1073741824
+
+ Release typeNew features
+ Release typeBug fixes
+
+
+
+
diff --git a/core/modules/update/tests/modules/update_test/drupal.1.2-sec.xml b/core/modules/update/tests/modules/update_test/drupal.sec.0.1_0.2.xml
similarity index 66%
rename from core/modules/update/tests/modules/update_test/drupal.1.2-sec.xml
rename to core/modules/update/tests/modules/update_test/drupal.sec.0.1_0.2.xml
index f4b3f8303..ba954ae42 100644
--- a/core/modules/update/tests/modules/update_test/drupal.1.2-sec.xml
+++ b/core/modules/update/tests/modules/update_test/drupal.sec.0.1_0.2.xml
@@ -14,15 +14,15 @@
- Drupal 8.1.2
- 8.1.2
- DRUPAL-8-1-2
+ Drupal 8.0.2
+ 8.0.2
+ DRUPAL-8-0-2
8
- 1
+ 0
2
published
- http://example.com/drupal-8-1-2-release
- http://example.com/drupal-8-1-2.tar.gz
+ http://example.com/drupal-8-0-2-release
+ http://example.com/drupal-8-0-2.tar.gz
1250424641
b966255555d9c9b86d480ca08cfaa98e
4294967296
@@ -33,39 +33,42 @@
- Drupal 8.1.1
- 8.1.1
- DRUPAL-8-1-1
+ Drupal 8.0.1
+ 8.0.1
+ DRUPAL-8-0-1
8
- 1
+ 0
1
published
- http://example.com/drupal-8-1-1-release
- http://example.com/drupal-8-1-1.tar.gz
+ http://example.com/drupal-8-0-1-release
+ http://example.com/drupal-8-0-1.tar.gz
1250424581
b966255555d9c9b86d480ca08cfaa98e
2147483648
Release typeNew features
Release typeBug fixes
+ Release typeSecurity update
+ Release typeInsecure
- Drupal 8.1.0
- 8.1.0
- DRUPAL-8-1-0
+ Drupal 8.0.0
+ 8.0.0
+ DRUPAL-8-0-0
8
- 1
+ 0
0
published
- http://example.com/drupal-8-1-0-release
- http://example.com/drupal-8-1-0.tar.gz
+ http://example.com/drupal-8-0-0-release
+ http://example.com/drupal-8-0-0.tar.gz
1250424521
b966255555d9c9b86d480ca08cfaa98e
1073741824
Release typeNew features
Release typeBug fixes
+ Release typeInsecure
diff --git a/core/modules/update/tests/modules/update_test/drupal.sec.0.2-rc2-b.xml b/core/modules/update/tests/modules/update_test/drupal.sec.0.2-rc2-b.xml
new file mode 100644
index 000000000..a971ca73e
--- /dev/null
+++ b/core/modules/update/tests/modules/update_test/drupal.sec.0.2-rc2-b.xml
@@ -0,0 +1,263 @@
+
+
+Drupal
+drupal
+Drupal
+8.x
+8
+8
+8
+published
+http://example.com/project/drupal
+
+ ProjectsDrupal project
+
+
+
+ drupal 8.2.0-rc2
+ 8.2.0-rc2
+ 8.2.0-rc2
+ 8
+ 2
+ 0
+ rc2
+ published
+ http://example.com/drupal-8-2-0-rc2-release
+ http://example.com/drupal-8-2-0-rc2.tar.gz
+ 1533298080
+ 2387284145e34a19fc45e03c258a2831
+ 16209911
+
+ Release typeBug fixes
+ Release typeNew features
+
+
+ RC releases are not covered by Drupal security advisories.
+
+
+
+ drupal 8.2.0-rc1
+ 8.2.0-rc1
+ 8.2.0-rc1
+ 8
+ 2
+ 0
+ rc1
+ published
+ http://example.com/drupal-8-2-0-rc1-release
+ http://example.com/drupal-8-2-0-rc1.tar.gz
+ 1533270485
+ 3d70ae568dd8f7100082b8c58bc8d1fe
+ 16209123
+
+ Release typeBug fixes
+ Release typeNew features
+
+
+ RC releases are not covered by Drupal security advisories.
+
+
+
+ drupal 8.2.0-beta2
+ 8.2.0-beta2
+ 8.2.0-beta2
+ 8
+ 2
+ 0
+ beta2
+ published
+ http://example.com/drupal-8-2-0-beta2-release
+ http://example.com/drupal-8-2-0-beta2.tar.gz
+ 1533298080
+ 2387284145e34a19fc45e03c258a2831
+ 16209911
+
+ Release typeBug fixes
+ Release typeNew features
+
+
+ Beta releases are not covered by Drupal security advisories.
+
+
+
+ drupal 8.2.0-beta1
+ 8.2.0-beta1
+ 8.2.0-beta1
+ 8
+ 2
+ 0
+ beta1
+ published
+ http://example.com/drupal-8-2-0-beta1-release
+ http://example.com/drupal-8-2-0-beta1.tar.gz
+ 1533270485
+ 3d70ae568dd8f7100082b8c58bc8d1fe
+ 16209123
+
+ Release typeBug fixes
+ Release typeNew features
+
+
+ Beta releases are not covered by Drupal security advisories.
+
+
+
+ drupal 8.2.0-alpha2
+ 8.2.0-alpha2
+ 8.2.0-alpha2
+ 8
+ 2
+ 0
+ alpha2
+ published
+ http://example.com/drupal-8-2-0-alpha2-release
+ http://example.com/drupal-8-2-0-alpha2.tar.gz
+ 1533298080
+ 2387284145e34a19fc45e03c258a2831
+ 16209911
+
+ Release typeBug fixes
+ Release typeNew features
+
+
+ Alpha releases are not covered by Drupal security advisories.
+
+
+
+ drupal 8.2.0-alpha1
+ 8.2.0-alpha1
+ 8.2.0-alpha1
+ 8
+ 2
+ 0
+ alpha1
+ published
+ http://example.com/drupal-8.2.0-alpha1
+ http://example.com/drupal-8-2-0-alpha1.tar.gz
+ 1533270485
+ 3d70ae568dd8f7100082b8c58bc8d1fe
+ 16209123
+
+ Release typeBug fixes
+ Release typeNew features
+
+
+ Alpha releases are not covered by Drupal security advisories.
+
+
+
+ Drupal 8.1.2
+ 8.1.2
+ DRUPAL-8-1-2
+ 8
+ 1
+ 2
+ published
+ http://example.com/drupal-8-1-2-release
+ http://example.com/drupal-8-1-2.tar.gz
+ 1250424526
+ b966255555d9c9b86d480ca08cfaa98e
+ 4294967296
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeSecurity update
+
+
+
+ Drupal 8.1.1
+ 8.1.1
+ DRUPAL-8-1-1
+ 8
+ 1
+ 1
+ published
+ http://example.com/drupal-8-1-1-release
+ http://example.com/drupal-8-1-1.tar.gz
+ 1250424525
+ b966255555d9c9b86d480ca08cfaa98e
+ 2147483648
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeInsecure
+
+
+
+ Drupal 8.1.0
+ 8.1.0
+ DRUPAL-8-1-0
+ 8
+ 1
+ 0
+ published
+ http://example.com/drupal-8-1-0-release
+ http://example.com/drupal-8-1-0.tar.gz
+ 1250424524
+ b966255555d9c9b86d480ca08cfaa98e
+ 1073741824
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeInsecure
+
+
+
+ Drupal 8.0.2
+ 8.0.2
+ DRUPAL-8-0-2
+ 8
+ 0
+ 2
+ published
+ http://example.com/drupal-8-0-2-release
+ http://example.com/drupal-8-0-2.tar.gz
+ 1250424523
+ b966255555d9c9b86d480ca08cfaa98e
+ 4294967296
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeSecurity update
+
+
+
+ Drupal 8.0.1
+ 8.0.1
+ DRUPAL-8-0-1
+ 8
+ 0
+ 1
+ published
+ http://example.com/drupal-8-0-1-release
+ http://example.com/drupal-8-0-1.tar.gz
+ 1250424522
+ b966255555d9c9b86d480ca08cfaa98e
+ 2147483648
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeInsecure
+
+
+
+ Drupal 8.0.0
+ 8.0.0
+ DRUPAL-8-0-0
+ 8
+ 0
+ 0
+ published
+ http://example.com/drupal-8-0-0-release
+ http://example.com/drupal-8-0-0.tar.gz
+ 1250424521
+ b966255555d9c9b86d480ca08cfaa98e
+ 1073741824
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeInsecure
+
+
+
+
diff --git a/core/modules/update/tests/modules/update_test/drupal.sec.0.2-rc2.xml b/core/modules/update/tests/modules/update_test/drupal.sec.0.2-rc2.xml
new file mode 100644
index 000000000..bc422deb6
--- /dev/null
+++ b/core/modules/update/tests/modules/update_test/drupal.sec.0.2-rc2.xml
@@ -0,0 +1,269 @@
+
+
+Drupal
+drupal
+Drupal
+8.x
+8
+8
+8
+published
+http://example.com/project/drupal
+
+ ProjectsDrupal project
+
+
+
+ drupal 8.2.0-rc2
+ 8.2.0-rc2
+ 8.2.0-rc2
+ 8
+ 2
+ 0
+ rc2
+ published
+ http://example.com/drupal-8-2-0-rc2-release
+ http://example.com/drupal-8-2-0-rc2.tar.gz
+ 1533298080
+ 2387284145e34a19fc45e03c258a2831
+ 16209911
+
+ Release typeBug fixes
+ Release typeNew features
+ Release typeSecurity update
+
+
+ RC releases are not covered by Drupal security advisories.
+
+
+
+ drupal 8.2.0-rc1
+ 8.2.0-rc1
+ 8.2.0-rc1
+ 8
+ 2
+ 0
+ rc1
+ published
+ http://example.com/drupal-8-2-0-rc1-release
+ http://example.com/drupal-8-2-0-rc1.tar.gz
+ 1533270485
+ 3d70ae568dd8f7100082b8c58bc8d1fe
+ 16209123
+
+ Release typeInsecure
+ Release typeBug fixes
+ Release typeNew features
+
+
+ RC releases are not covered by Drupal security advisories.
+
+
+
+ drupal 8.2.0-beta2
+ 8.2.0-beta2
+ 8.2.0-beta2
+ 8
+ 2
+ 0
+ beta2
+ published
+ http://example.com/drupal-8-2-0-beta2-release
+ http://example.com/drupal-8-2-0-beta2.tar.gz
+ 1533298080
+ 2387284145e34a19fc45e03c258a2831
+ 16209911
+
+ Release typeBug fixes
+ Release typeNew features
+ Release typeInsecure
+
+
+ Beta releases are not covered by Drupal security advisories.
+
+
+
+ drupal 8.2.0-beta1
+ 8.2.0-beta1
+ 8.2.0-beta1
+ 8
+ 2
+ 0
+ beta1
+ published
+ http://example.com/drupal-8-2-0-beta1-release
+ http://example.com/drupal-8-2-0-beta1.tar.gz
+ 1533270485
+ 3d70ae568dd8f7100082b8c58bc8d1fe
+ 16209123
+
+ Release typeInsecure
+ Release typeBug fixes
+ Release typeNew features
+
+
+ Beta releases are not covered by Drupal security advisories.
+
+
+
+ drupal 8.2.0-alpha2
+ 8.2.0-alpha2
+ 8.2.0-alpha2
+ 8
+ 2
+ 0
+ alpha2
+ published
+ http://example.com/drupal-8-2-0-alpha2-release
+ http://example.com/drupal-8-2-0-alpha2.tar.gz
+ 1533298080
+ 2387284145e34a19fc45e03c258a2831
+ 16209911
+
+ Release typeBug fixes
+ Release typeNew features
+ Release typeInsecure
+
+
+ Alpha releases are not covered by Drupal security advisories.
+
+
+
+ drupal 8.2.0-alpha1
+ 8.2.0-alpha1
+ 8.2.0-alpha1
+ 8
+ 2
+ 0
+ alpha1
+ published
+ http://example.com/drupal-8.2.0-alpha1
+ http://example.com/drupal-8-2-0-alpha1.tar.gz
+ 1533270485
+ 3d70ae568dd8f7100082b8c58bc8d1fe
+ 16209123
+
+ Release typeInsecure
+ Release typeBug fixes
+ Release typeNew features
+
+
+ Alpha releases are not covered by Drupal security advisories.
+
+
+
+ Drupal 8.1.2
+ 8.1.2
+ DRUPAL-8-1-2
+ 8
+ 1
+ 2
+ published
+ http://example.com/drupal-8-1-2-release
+ http://example.com/drupal-8-1-2.tar.gz
+ 1250424641
+ b966255555d9c9b86d480ca08cfaa98e
+ 4294967296
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeSecurity update
+
+
+
+ Drupal 8.1.1
+ 8.1.1
+ DRUPAL-8-1-1
+ 8
+ 1
+ 1
+ published
+ http://example.com/drupal-8-1-1-release
+ http://example.com/drupal-8-1-1.tar.gz
+ 1250424581
+ b966255555d9c9b86d480ca08cfaa98e
+ 2147483648
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeInsecure
+
+
+
+ Drupal 8.1.0
+ 8.1.0
+ DRUPAL-8-1-0
+ 8
+ 1
+ 0
+ published
+ http://example.com/drupal-8-1-0-release
+ http://example.com/drupal-8-1-0.tar.gz
+ 1250424521
+ b966255555d9c9b86d480ca08cfaa98e
+ 1073741824
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeInsecure
+
+
+
+ Drupal 8.0.2
+ 8.0.2
+ DRUPAL-8-0-2
+ 8
+ 0
+ 2
+ published
+ http://example.com/drupal-8-0-2-release
+ http://example.com/drupal-8-0-2.tar.gz
+ 1250424641
+ b966255555d9c9b86d480ca08cfaa98e
+ 4294967296
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeSecurity update
+
+
+
+ Drupal 8.0.1
+ 8.0.1
+ DRUPAL-8-0-1
+ 8
+ 0
+ 1
+ published
+ http://example.com/drupal-8-0-1-release
+ http://example.com/drupal-8-0-1.tar.gz
+ 1250424581
+ b966255555d9c9b86d480ca08cfaa98e
+ 2147483648
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeInsecure
+
+
+
+ Drupal 8.0.0
+ 8.0.0
+ DRUPAL-8-0-0
+ 8
+ 0
+ 0
+ published
+ http://example.com/drupal-8-0-0-release
+ http://example.com/drupal-8-0-0.tar.gz
+ 1250424521
+ b966255555d9c9b86d480ca08cfaa98e
+ 1073741824
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeInsecure
+
+
+
+
diff --git a/core/modules/update/tests/modules/update_test/drupal.0.2-sec.xml b/core/modules/update/tests/modules/update_test/drupal.sec.0.2.xml
similarity index 95%
rename from core/modules/update/tests/modules/update_test/drupal.0.2-sec.xml
rename to core/modules/update/tests/modules/update_test/drupal.sec.0.2.xml
index 337600efb..0b9bbce63 100644
--- a/core/modules/update/tests/modules/update_test/drupal.0.2-sec.xml
+++ b/core/modules/update/tests/modules/update_test/drupal.sec.0.2.xml
@@ -48,6 +48,7 @@
Release typeNew features
Release typeBug fixes
+ Release typeInsecure
@@ -66,6 +67,7 @@
Release typeNew features
Release typeBug fixes
+ Release typeInsecure
diff --git a/core/modules/update/tests/modules/update_test/drupal.sec.1.2.xml b/core/modules/update/tests/modules/update_test/drupal.sec.1.2.xml
new file mode 100644
index 000000000..8665e46b3
--- /dev/null
+++ b/core/modules/update/tests/modules/update_test/drupal.sec.1.2.xml
@@ -0,0 +1,128 @@
+
+
+Drupal
+drupal
+Drupal
+8.x
+8
+8
+8
+published
+http://example.com/project/drupal
+
+ ProjectsDrupal project
+
+
+
+ Drupal 8.1.2
+ 8.1.2
+ DRUPAL-8-1-2
+ 8
+ 1
+ 2
+ published
+ http://example.com/drupal-8-1-2-release
+ http://example.com/drupal-8-1-2.tar.gz
+ 1250424641
+ b966255555d9c9b86d480ca08cfaa98e
+ 4294967296
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeSecurity update
+
+
+
+ Drupal 8.1.1
+ 8.1.1
+ DRUPAL-8-1-1
+ 8
+ 1
+ 1
+ published
+ http://example.com/drupal-8-1-1-release
+ http://example.com/drupal-8-1-1.tar.gz
+ 1250424581
+ b966255555d9c9b86d480ca08cfaa98e
+ 2147483648
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeInsecure
+
+
+
+ Drupal 8.1.0
+ 8.1.0
+ DRUPAL-8-1-0
+ 8
+ 1
+ 0
+ published
+ http://example.com/drupal-8-1-0-release
+ http://example.com/drupal-8-1-0.tar.gz
+ 1250424521
+ b966255555d9c9b86d480ca08cfaa98e
+ 1073741824
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeInsecure
+
+
+
+ Drupal 8.0.2
+ 8.0.2
+ DRUPAL-8-0-2
+ 8
+ 0
+ 2
+ published
+ http://example.com/drupal-8-0-2-release
+ http://example.com/drupal-8-0-2.tar.gz
+ 1250424641
+ b966255555d9c9b86d480ca08cfaa98e
+ 4294967296
+
+ Release typeNew features
+ Release typeBug fixes
+
+
+
+ Drupal 8.0.1
+ 8.0.1
+ DRUPAL-8-0-1
+ 8
+ 0
+ 1
+ published
+ http://example.com/drupal-8-0-1-release
+ http://example.com/drupal-8-0-1.tar.gz
+ 1250424581
+ b966255555d9c9b86d480ca08cfaa98e
+ 2147483648
+
+ Release typeNew features
+ Release typeBug fixes
+
+
+
+ Drupal 8.0.0
+ 8.0.0
+ DRUPAL-8-0-0
+ 8
+ 0
+ 0
+ published
+ http://example.com/drupal-8-0-0-release
+ http://example.com/drupal-8-0-0.tar.gz
+ 1250424521
+ b966255555d9c9b86d480ca08cfaa98e
+ 1073741824
+
+ Release typeNew features
+ Release typeBug fixes
+
+
+
+
diff --git a/core/modules/update/tests/modules/update_test/drupal.sec.1.2_insecure.xml b/core/modules/update/tests/modules/update_test/drupal.sec.1.2_insecure.xml
new file mode 100644
index 000000000..dadad421b
--- /dev/null
+++ b/core/modules/update/tests/modules/update_test/drupal.sec.1.2_insecure.xml
@@ -0,0 +1,131 @@
+
+
+Drupal
+drupal
+Drupal
+8.x
+8
+8
+8
+published
+http://example.com/project/drupal
+
+ ProjectsDrupal project
+
+
+
+ Drupal 8.1.2
+ 8.1.2
+ DRUPAL-8-1-2
+ 8
+ 1
+ 2
+ published
+ http://example.com/drupal-8-1-2-release
+ http://example.com/drupal-8-1-2.tar.gz
+ 1250424641
+ b966255555d9c9b86d480ca08cfaa98e
+ 4294967296
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeSecurity update
+
+
+
+ Drupal 8.1.1
+ 8.1.1
+ DRUPAL-8-1-1
+ 8
+ 1
+ 1
+ published
+ http://example.com/drupal-8-1-1-release
+ http://example.com/drupal-8-1-1.tar.gz
+ 1250424581
+ b966255555d9c9b86d480ca08cfaa98e
+ 2147483648
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeInsecure
+
+
+
+ Drupal 8.1.0
+ 8.1.0
+ DRUPAL-8-1-0
+ 8
+ 1
+ 0
+ published
+ http://example.com/drupal-8-1-0-release
+ http://example.com/drupal-8-1-0.tar.gz
+ 1250424521
+ b966255555d9c9b86d480ca08cfaa98e
+ 1073741824
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeInsecure
+
+
+
+ Drupal 8.0.2
+ 8.0.2
+ DRUPAL-8-0-2
+ 8
+ 0
+ 2
+ published
+ http://example.com/drupal-8-0-2-release
+ http://example.com/drupal-8-0-2.tar.gz
+ 1250424641
+ b966255555d9c9b86d480ca08cfaa98e
+ 4294967296
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeInsecure
+
+
+
+ Drupal 8.0.1
+ 8.0.1
+ DRUPAL-8-0-1
+ 8
+ 0
+ 1
+ published
+ http://example.com/drupal-8-0-1-release
+ http://example.com/drupal-8-0-1.tar.gz
+ 1250424581
+ b966255555d9c9b86d480ca08cfaa98e
+ 2147483648
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeInsecure
+
+
+
+ Drupal 8.0.0
+ 8.0.0
+ DRUPAL-8-0-0
+ 8
+ 0
+ 0
+ published
+ http://example.com/drupal-8-0-0-release
+ http://example.com/drupal-8-0-0.tar.gz
+ 1250424521
+ b966255555d9c9b86d480ca08cfaa98e
+ 1073741824
+
+ Release typeNew features
+ Release typeBug fixes
+ Release typeInsecure
+
+
+
+
diff --git a/core/modules/update/tests/modules/update_test/update_test_basetheme.1_1-sec.xml b/core/modules/update/tests/modules/update_test/update_test_basetheme.1_1-sec.xml
index 462558b59..3529e1976 100644
--- a/core/modules/update/tests/modules/update_test/update_test_basetheme.1_1-sec.xml
+++ b/core/modules/update/tests/modules/update_test/update_test_basetheme.1_1-sec.xml
@@ -46,6 +46,7 @@
Release typeNew features
Release typeBug fixes
+ Release typeInsecure
diff --git a/core/modules/update/tests/src/Functional/UpdateContribTest.php b/core/modules/update/tests/src/Functional/UpdateContribTest.php
index 20d397cd4..e16a3df77 100644
--- a/core/modules/update/tests/src/Functional/UpdateContribTest.php
+++ b/core/modules/update/tests/src/Functional/UpdateContribTest.php
@@ -178,7 +178,7 @@ class UpdateContribTest extends UpdateTestBase {
// we're really testing that the project listings are in the right order.
$bbb_project_link = 'BBB Update test';
$ccc_project_link = '
CCC Update test';
- $this->assertTrue(strpos($this->getRawContent(), $bbb_project_link) < strpos($this->getRawContent(), $ccc_project_link), "'BBB Update test' project is listed before the 'CCC Update test' project");
+ $this->assertTrue(strpos($this->getSession()->getPage()->getContent(), $bbb_project_link) < strpos($this->getSession()->getPage()->getContent(), $ccc_project_link), "'BBB Update test' project is listed before the 'CCC Update test' project");
}
/**
@@ -438,4 +438,130 @@ class UpdateContribTest extends UpdateTestBase {
$this->assertNoText(t('Security update'));
}
+ /**
+ * Tests update status of security releases.
+ *
+ * @param string $module_version
+ * The module version the site is using.
+ * @param string[] $expected_security_releases
+ * The security releases, if any, that the status report should recommend.
+ * @param string $expected_update_message_type
+ * The type of update message expected.
+ * @param string $fixture
+ * The fixture file to use.
+ *
+ * @dataProvider securityUpdateAvailabilityProvider
+ */
+ public function testSecurityUpdateAvailability($module_version, array $expected_security_releases, $expected_update_message_type, $fixture) {
+ $system_info = [
+ '#all' => [
+ 'version' => '8.0.0',
+ ],
+ 'aaa_update_test' => [
+ 'project' => 'aaa_update_test',
+ 'version' => $module_version,
+ 'hidden' => FALSE,
+ ],
+ ];
+ $this->config('update_test.settings')->set('system_info', $system_info)->save();
+ $this->refreshUpdateStatus(['drupal' => '0.0', 'aaa_update_test' => $fixture]);
+ $this->assertSecurityUpdates('aaa_update_test', $expected_security_releases, $expected_update_message_type, 'table.update:nth-of-type(2)');
+ }
+
+ /**
+ * Data provider method for testSecurityUpdateAvailability().
+ *
+ * These test cases rely on the following fixtures containing the following
+ * releases:
+ * - aaa_update_test.sec.8.x-1.2.xml
+ * - 8.x-1.2 Security update
+ * - 8.x-1.1 Insecure
+ * - 8.x-1.0 Insecure
+ * - aaa_update_test.sec.8.x-1.1_8.x-1.2.xml
+ * - 8.x-1.2 Security update
+ * - 8.x-1.1 Security update, Insecure
+ * - 8.x-1.0 Insecure
+ * - aaa_update_test.sec.8.x-1.2_8.x-2.2.xml
+ * - 8.x-3.0-beta2
+ * - 8.x-3.0-beta1 Insecure
+ * - 8.x-2.2 Security update
+ * - 8.x-2.1 Security update, Insecure
+ * - 8.x-2.0 Insecure
+ * - 8.x-1.2 Security update
+ * - 8.x-1.1 Insecure
+ * - 8.x-1.0 Insecure
+ * - aaa_update_test.sec.8.x-2.2_1.x_secure.xml
+ * - 8.x-2.2 Security update
+ * - 8.x-2.1 Security update, Insecure
+ * - 8.x-2.0 Insecure
+ * - 8.x-1.2
+ * - 8.x-1.1
+ * - 8.x-1.0
+ */
+ public function securityUpdateAvailabilityProvider() {
+ return [
+ // Security releases available for module major release 1.
+ // No releases for next major.
+ '8.x-1.0, 8.x-1.2' => [
+ 'module_patch_version' => '8.x-1.0',
+ 'expected_security_releases' => ['8.x-1.2'],
+ 'expected_update_message_type' => static::SECURITY_UPDATE_REQUIRED,
+ 'fixture' => 'sec.8.x-1.2',
+ ],
+ // Two security releases available for module major release 1.
+ // 8.x-1.1 security release marked as insecure.
+ // No releases for next major.
+ '8.x-1.0, 8.x-1.1 8.x-1.2' => [
+ 'module_patch_version' => '8.x-1.0',
+ 'expected_security_releases' => ['8.x-1.2'],
+ 'expected_update_message_type' => static::SECURITY_UPDATE_REQUIRED,
+ 'fixture' => 'sec.8.x-1.1_8.x-1.2',
+ ],
+ // Security release available for module major release 2.
+ // No releases for next major.
+ '8.x-2.0, 8.x-2.2' => [
+ 'module_patch_version' => '8.x-2.0',
+ 'expected_security_releases' => ['8.x-2.2'],
+ 'expected_update_message_type' => static::SECURITY_UPDATE_REQUIRED,
+ 'fixture' => 'sec.8.x-2.2_1.x_secure',
+ ],
+ '8.x-2.2, 8.x-1.2 8.x-2.2' => [
+ 'module_patch_version' => '8.x-2.2',
+ 'expected_security_releases' => [],
+ 'expected_update_message_type' => static::UPDATE_NONE,
+ 'fixture' => 'sec.8.x-1.2_8.x-2.2',
+ ],
+ // Security release available for module major release 1.
+ // Security release also available for next major.
+ '8.x-1.0, 8.x-1.2 8.x-2.2' => [
+ 'module_patch_version' => '8.x-1.0',
+ 'expected_security_releases' => ['8.x-1.2'],
+ 'expected_update_message_type' => static::SECURITY_UPDATE_REQUIRED,
+ 'fixture' => 'sec.8.x-1.2_8.x-2.2',
+ ],
+ // No security release available for module major release 1 but 1.x
+ // releases are not marked as insecure.
+ // Security release available for next major.
+ '8.x-1.0, 8.x-2.2, not insecure' => [
+ 'module_patch_version' => '8.x-1.0',
+ 'expected_security_releases' => [],
+ 'expected_update_message_type' => static::UPDATE_AVAILABLE,
+ 'fixture' => 'sec.8.x-2.2_1.x_secure',
+ ],
+ // On latest security release for module major release 1.
+ // Security release also available for next major.
+ '8.x-1.2, 8.x-1.2 8.x-2.2' => [
+ 'module_patch_version' => '8.x-1.2',
+ 'expected_security_release' => [],
+ 'expected_update_message_type' => static::UPDATE_NONE,
+ 'fixture' => 'sec.8.x-1.2_8.x-2.2',
+ ],
+ // @todo In https://www.drupal.org/node/2865920 add test cases:
+ // - 8.x-2.0 using fixture 'sec.8.x-1.2_8.x-2.2' to ensure that 8.x-2.2
+ // is the only security update.
+ // - 8.x-3.0-beta1 using fixture 'sec.8.x-1.2_8.x-2.2' to ensure that
+ // 8.x-2.2 is the only security update.
+ ];
+ }
+
}
diff --git a/core/modules/update/tests/src/Functional/UpdateCoreTest.php b/core/modules/update/tests/src/Functional/UpdateCoreTest.php
index 5cd4b1026..20ad53e81 100644
--- a/core/modules/update/tests/src/Functional/UpdateCoreTest.php
+++ b/core/modules/update/tests/src/Functional/UpdateCoreTest.php
@@ -160,20 +160,188 @@ class UpdateCoreTest extends UpdateTestBase {
/**
* Tests the Update Manager module when a security update is available.
+ *
+ * @param string $site_patch_version
+ * The patch version to set the site to for testing.
+ * @param string[] $expected_security_releases
+ * The security releases, if any, that the status report should recommend.
+ * @param string $expected_update_message_type
+ * The type of update message expected.
+ * @param string $fixture
+ * The test fixture that contains the test XML.
+ *
+ * @dataProvider securityUpdateAvailabilityProvider
*/
- public function testSecurityUpdateAvailable() {
- foreach ([0, 1] as $minor_version) {
- $this->setSystemInfo("8.$minor_version.0");
- $this->refreshUpdateStatus(['drupal' => "$minor_version.2-sec"]);
- $this->standardTests();
- $this->assertNoText(t('Up to date'));
- $this->assertNoText(t('Update available'));
- $this->assertText(t('Security update required!'));
- $this->assertRaw(\Drupal::l("8.$minor_version.2", Url::fromUri("http://example.com/drupal-8-$minor_version-2-release")), 'Link to release appears.');
- $this->assertRaw(\Drupal::l(t('Download'), Url::fromUri("http://example.com/drupal-8-$minor_version-2.tar.gz")), 'Link to download appears.');
- $this->assertRaw(\Drupal::l(t('Release notes'), Url::fromUri("http://example.com/drupal-8-$minor_version-2-release")), 'Link to release notes appears.');
- $this->assertRaw('error.svg', 'Error icon was found.');
+ public function testSecurityUpdateAvailability($site_patch_version, array $expected_security_releases, $expected_update_message_type, $fixture) {
+ $this->setSystemInfo("8.$site_patch_version");
+ $this->refreshUpdateStatus(['drupal' => $fixture]);
+ $this->assertSecurityUpdates('drupal-8', $expected_security_releases, $expected_update_message_type, 'table.update');
+ }
+
+ /**
+ * Data provider method for testSecurityUpdateAvailability().
+ *
+ * These test cases rely on the following fixtures containing the following
+ * releases:
+ * - drupal.sec.0.1_0.2.xml
+ * - 8.0.2 Security update
+ * - 8.0.1 Security update, Insecure
+ * - 8.0.0 Insecure
+ * - drupal.sec.0.2.xml
+ * - 8.0.2 Security update
+ * - 8.0.1 Insecure
+ * - 8.0.0 Insecure
+ * - drupal.sec.0.2-rc2.xml
+ * - 8.2.0-rc2 Security update
+ * - 8.2.0-rc1 Insecure
+ * - 8.2.0-beta2 Insecure
+ * - 8.2.0-beta1 Insecure
+ * - 8.2.0-alpha2 Insecure
+ * - 8.2.0-alpha1 Insecure
+ * - 8.1.2 Security update
+ * - 8.1.1 Insecure
+ * - 8.1.0 Insecure
+ * - 8.0.2 Security update
+ * - 8.0.1 Insecure
+ * - 8.0.0 Insecure
+ * - drupal.sec.1.2.xml
+ * - 8.1.2 Security update
+ * - 8.1.1 Insecure
+ * - 8.1.0 Insecure
+ * - 8.0.2
+ * - 8.0.1
+ * - 8.0.0
+ * - drupal.sec.1.2_insecure.xml
+ * - 8.1.2 Security update
+ * - 8.1.1 Insecure
+ * - 8.1.0 Insecure
+ * - 8.0.2 Insecure
+ * - 8.0.1 Insecure
+ * - 8.0.0 Insecure
+ * - drupal.sec.0.2-rc2-b.xml
+ * - 8.2.0-rc2
+ * - 8.2.0-rc1
+ * - 8.2.0-beta2
+ * - 8.2.0-beta1
+ * - 8.2.0-alpha2
+ * - 8.2.0-alpha1
+ * - 8.1.2 Security update
+ * - 8.1.1 Insecure
+ * - 8.1.0 Insecure
+ * - 8.0.2 Security update
+ * - 8.0.1 Insecure
+ * - 8.0.0 Insecure
+ */
+ public function securityUpdateAvailabilityProvider() {
+ $test_cases = [
+ // Security release available for site minor release 0.
+ // No releases for next minor.
+ '0.0, 0.2' => [
+ 'site_patch_version' => '0.0',
+ 'expected_security_releases' => ['0.2'],
+ 'expected_update_message_type' => static::SECURITY_UPDATE_REQUIRED,
+ 'fixture' => 'sec.0.2',
+ ],
+ // Site on latest security release available for site minor release 0.
+ // Minor release 1 also has a security release, and the current release
+ // is marked as insecure.
+ '0.2, 0.2' => [
+ 'site_patch_version' => '0.2',
+ 'expected_security_release' => ['1.2', '2.0-rc2'],
+ 'expected_update_message_type' => static::UPDATE_AVAILABLE,
+ 'fixture' => 'sec.0.2-rc2',
+ ],
+ // Two security releases available for site minor release 0.
+ // 0.1 security release marked as insecure.
+ // No releases for next minor.
+ '0.0, 0.1 0.2' => [
+ 'site_patch_version' => '0.0',
+ 'expected_security_releases' => ['0.2'],
+ 'expected_update_message_type' => static::SECURITY_UPDATE_REQUIRED,
+ 'fixture' => 'sec.0.1_0.2',
+ ],
+ // Security release available for site minor release 1.
+ // No releases for next minor.
+ '1.0, 1.2' => [
+ 'site_patch_version' => '1.0',
+ 'expected_security_releases' => ['1.2'],
+ 'expected_update_message_type' => static::SECURITY_UPDATE_REQUIRED,
+ 'fixture' => 'sec.1.2',
+ ],
+ // Security release available for site minor release 0.
+ // Security release also available for next minor.
+ '0.0, 0.2 1.2' => [
+ 'site_patch_version' => '0.0',
+ 'expected_security_releases' => ['0.2', '1.2', '2.0-rc2'],
+ 'expected_update_message_type' => static::SECURITY_UPDATE_REQUIRED,
+ 'fixture' => 'sec.0.2-rc2',
+ ],
+ // No newer security release for site minor 1.
+ // Previous minor has security release.
+ '1.2, 0.2 1.2' => [
+ 'site_patch_version' => '1.2',
+ 'expected_security_releases' => [],
+ 'expected_update_message_type' => static::UPDATE_NONE,
+ 'fixture' => 'sec.0.2-rc2',
+ ],
+ // No security release available for site minor release 0.
+ // Security release available for next minor.
+ '0.0, 1.2, insecure' => [
+ 'site_patch_version' => '0.0',
+ 'expected_security_releases' => ['1.2'],
+ 'expected_update_message_type' => static::SECURITY_UPDATE_REQUIRED,
+ 'fixture' => 'sec.1.2_insecure',
+ ],
+ // All releases for minor 0 are secure.
+ // Security release available for next minor.
+ '0.0, 1.2, secure' => [
+ 'site_patch_version' => '0.0',
+ 'expected_security_release' => ['1.2'],
+ 'expected_update_message_type' => static::UPDATE_AVAILABLE,
+ 'fixture' => 'sec.1.2',
+ ],
+ '0.2, 1.2, secure' => [
+ 'site_patch_version' => '0.2',
+ 'expected_security_release' => ['1.2'],
+ 'expected_update_message_type' => static::UPDATE_AVAILABLE,
+ 'fixture' => 'sec.1.2',
+ ],
+ // Site on 2.0-rc2 which is a security release.
+ '2.0-rc2, 0.2 1.2' => [
+ 'site_patch_version' => '2.0-rc2',
+ 'expected_security_releases' => [],
+ 'expected_update_message_type' => static::UPDATE_NONE,
+ 'fixture' => 'sec.0.2-rc2',
+ ],
+ ];
+ $pre_releases = [
+ '2.0-alpha1',
+ '2.0-alpha2',
+ '2.0-beta1',
+ '2.0-beta2',
+ '2.0-rc1',
+ '2.0-rc2',
+ ];
+
+ // If the site is on an alpha/beta/RC of an upcoming minor and none of the
+ // alpha/beta/RC versions are marked insecure, no security update should be
+ // required.
+ foreach ($pre_releases as $pre_release) {
+ $test_cases["Pre-release:$pre_release, no security update"] = [
+ 'site_patch_version' => $pre_release,
+ 'expected_security_releases' => [],
+ 'expected_update_message_type' => $pre_release === '2.0-rc2' ? static::UPDATE_NONE : static::UPDATE_AVAILABLE,
+ 'fixture' => 'sec.0.2-rc2-b',
+ ];
}
+
+ // @todo In https://www.drupal.org/node/2865920 add test cases:
+ // - For all pre-releases for 8.2.0 except 8.2.0-rc2 using the
+ // 'sec.0.2-rc2' fixture to ensure that 8.2.0-rc2 is the only security
+ // update.
+ // - For 8.1.0 using fixture 'sec.0.2-rc2' to ensure that only security
+ // updates are 8.1.2 and 8.2.0-rc2.
+ return $test_cases;
}
/**
@@ -270,7 +438,7 @@ class UpdateCoreTest extends UpdateTestBase {
->set('fetch.url', Url::fromRoute('update_test.update_test')->setAbsolute()->toString())
->save();
$this->config('update_test.settings')
- ->set('xml_map', ['drupal' => '0.2-sec'])
+ ->set('xml_map', ['drupal' => 'sec.0.2'])
->save();
$this->drupalGet('admin/reports/updates');
@@ -289,8 +457,9 @@ class UpdateCoreTest extends UpdateTestBase {
// Make sure duplicate messages don't appear on Update status pages.
$this->drupalGet('admin/reports/status');
// We're expecting "There is a security update..." inside the status report
- // itself, but the drupal_set_message() appears as an li so we can prefix
- // with that and search for the raw HTML.
+ // itself, but the message from
+ // \Drupal\Core\Messenger\MessengerInterface::addStatus() appears as an li
+ // so we can prefix with that and search for the raw HTML.
$this->assertNoRaw('
- ' . t('There is a security update available for your version of Drupal.'));
$this->drupalGet('admin/reports/updates');
diff --git a/core/modules/update/tests/src/Functional/UpdateTestBase.php b/core/modules/update/tests/src/Functional/UpdateTestBase.php
index a4d07d0b6..1daa8801e 100644
--- a/core/modules/update/tests/src/Functional/UpdateTestBase.php
+++ b/core/modules/update/tests/src/Functional/UpdateTestBase.php
@@ -25,6 +25,21 @@ use Drupal\Tests\BrowserTestBase;
*/
abstract class UpdateTestBase extends BrowserTestBase {
+ /**
+ * Denotes a security update will be required in the test case.
+ */
+ const SECURITY_UPDATE_REQUIRED = 'SECURITY_UPDATE_REQUIRED';
+
+ /**
+ * Denotes an update will be available in the test case.
+ */
+ const UPDATE_AVAILABLE = 'UPDATE_AVAILABLE';
+
+ /**
+ * Denotes no update will be available in the test case.
+ */
+ const UPDATE_NONE = 'UPDATE_NONE';
+
protected function setUp() {
parent::setUp();
@@ -81,4 +96,75 @@ abstract class UpdateTestBase extends BrowserTestBase {
$this->assertNoText(t('No available releases found'));
}
+ /**
+ * Asserts the expected security updates are displayed correctly on the page.
+ *
+ * @param string $project_path_part
+ * The project path part needed for the download and release links.
+ * @param string[] $expected_security_releases
+ * The security releases, if any, that the status report should recommend.
+ * @param string $expected_update_message_type
+ * The type of update message expected.
+ * @param string $update_element_css_locator
+ * The CSS locator for the page element that contains the security updates.
+ */
+ protected function assertSecurityUpdates($project_path_part, array $expected_security_releases, $expected_update_message_type, $update_element_css_locator) {
+ $assert_session = $this->assertSession();
+ $page = $this->getSession()->getPage();
+ $this->standardTests();
+ $assert_session->elementTextNotContains('css', $update_element_css_locator, 'Not supported');
+ $all_security_release_urls = array_map(function ($link) {
+ return $link->getAttribute('href');
+ }, $page->findAll('css', "$update_element_css_locator .version-security a[href$='-release']"));
+ $all_security_download_urls = array_map(function ($link) {
+ return $link->getAttribute('href');
+ }, $page->findAll('css', "$update_element_css_locator .version-security a[href$='.tar.gz']"));
+ if ($expected_security_releases) {
+ $expected_download_urls = [];
+ $expected_release_urls = [];
+ if ($expected_update_message_type === static::SECURITY_UPDATE_REQUIRED) {
+ $assert_session->elementTextNotContains('css', $update_element_css_locator, 'Update available');
+ $assert_session->elementTextContains('css', $update_element_css_locator, 'Security update required!');
+ $assert_session->responseContains('error.svg', 'Error icon was found.');
+ }
+ else {
+ $assert_session->elementTextContains('css', $update_element_css_locator, 'Update available');
+ $assert_session->elementTextNotContains('css', $update_element_css_locator, 'Security update required!');
+ }
+ $assert_session->elementTextNotContains('css', $update_element_css_locator, 'Up to date');
+ foreach ($expected_security_releases as $expected_security_release) {
+ $expected_url_version = str_replace('.', '-', $expected_security_release);
+ $release_url = "http://example.com/$project_path_part-$expected_url_version-release";
+ $download_url = "http://example.com/$project_path_part-$expected_url_version.tar.gz";
+ $expected_release_urls[] = $release_url;
+ $expected_download_urls[] = $download_url;
+ // Ensure the expected links are security links.
+ $this->assertTrue(in_array($release_url, $all_security_release_urls), "Release $release_url is a security release link.");
+ $this->assertTrue(in_array($download_url, $all_security_download_urls), "Release $download_url is a security download link.");
+ $assert_session->linkByHrefExists($release_url);
+ $assert_session->linkByHrefExists($download_url);
+ }
+ // Ensure no other links are shown as security releases.
+ $this->assertEquals([], array_diff($all_security_release_urls, $expected_release_urls));
+ $this->assertEquals([], array_diff($all_security_download_urls, $expected_download_urls));
+ }
+ else {
+ // Ensure there were no security links.
+ $this->assertEquals([], $all_security_release_urls);
+ $this->assertEquals([], $all_security_download_urls);
+ $assert_session->pageTextNotContains('Security update required!');
+ if ($expected_update_message_type === static::UPDATE_AVAILABLE) {
+ $assert_session->elementTextContains('css', $update_element_css_locator, 'Update available');
+ $assert_session->elementTextNotContains('css', $update_element_css_locator, 'Up to date');
+ }
+ elseif ($expected_update_message_type === static::UPDATE_NONE) {
+ $assert_session->elementTextNotContains('css', $update_element_css_locator, 'Update available');
+ $assert_session->elementTextContains('css', $update_element_css_locator, 'Up to date');
+ }
+ else {
+ $this->fail('Unexpected value for $expected_update_message_type: ' . $expected_update_message_type);
+ }
+ }
+ }
+
}
diff --git a/core/modules/update/tests/src/Functional/UpdateUploadTest.php b/core/modules/update/tests/src/Functional/UpdateUploadTest.php
index 609662006..e86dff549 100644
--- a/core/modules/update/tests/src/Functional/UpdateUploadTest.php
+++ b/core/modules/update/tests/src/Functional/UpdateUploadTest.php
@@ -190,10 +190,10 @@ class UpdateUploadTest extends UpdateTestBase {
* Tests only an *.info.yml file are detected without supporting files.
*/
public function testUpdateDirectory() {
- $type = Updater::getUpdaterFromDirectory(\Drupal::root() . '/core/modules/update/tests/modules/aaa_update_test');
+ $type = Updater::getUpdaterFromDirectory($this->root . '/core/modules/update/tests/modules/aaa_update_test');
$this->assertEqual($type, 'Drupal\\Core\\Updater\\Module', 'Detected a Module');
- $type = Updater::getUpdaterFromDirectory(\Drupal::root() . '/core/modules/update/tests/themes/update_test_basetheme');
+ $type = Updater::getUpdaterFromDirectory($this->root . '/core/modules/update/tests/themes/update_test_basetheme');
$this->assertEqual($type, 'Drupal\\Core\\Updater\\Theme', 'Detected a Theme.');
}
diff --git a/core/modules/update/update.authorize.inc b/core/modules/update/update.authorize.inc
index 1c2cc60fb..664f25f9c 100644
--- a/core/modules/update/update.authorize.inc
+++ b/core/modules/update/update.authorize.inc
@@ -261,7 +261,7 @@ function update_authorize_update_batch_finished($success, $results) {
'absolute' => TRUE,
'base_url' => $GLOBALS['base_url'],
],
- '#access' => $url->access(\Drupal::currentUser())
+ '#access' => $url->access(\Drupal::currentUser()),
];
// Unset the variable since it is no longer needed.
diff --git a/core/modules/update/update.compare.inc b/core/modules/update/update.compare.inc
index 2ba5fdce7..dd658120e 100644
--- a/core/modules/update/update.compare.inc
+++ b/core/modules/update/update.compare.inc
@@ -425,15 +425,6 @@ function update_calculate_project_update_status(&$project_data, $available) {
$project_data['recommended'] = $project_data['latest_version'];
}
- //
- // Check to see if we need an update or not.
- //
-
- if (!empty($project_data['security updates'])) {
- // If we found security updates, that always trumps any other status.
- $project_data['status'] = UPDATE_NOT_SECURE;
- }
-
if (isset($project_data['status'])) {
// If we already know the status, we're done.
return;
diff --git a/core/modules/update/update.info.yml b/core/modules/update/update.info.yml
index ae94a4c6f..b4536011d 100644
--- a/core/modules/update/update.info.yml
+++ b/core/modules/update/update.info.yml
@@ -6,4 +6,4 @@ package: Core
core: 8.x
configure: update.settings
dependencies:
- - file
+ - drupal:file
diff --git a/core/modules/update/update.install b/core/modules/update/update.install
index 4cff44c72..e4294a0a9 100644
--- a/core/modules/update/update.install
+++ b/core/modules/update/update.install
@@ -158,4 +158,8 @@ function _update_requirement_check($project, $type) {
function update_update_8001() {
// Empty update forces a call to drupal_flush_all_caches() which rebuilds the
// router.
+
+ // Use hook_post_update_NAME() instead to clear the cache.The use
+ // of hook_update_N to clear the cache has been deprecated see
+ // https://www.drupal.org/node/2960601 for more details.
}
diff --git a/core/modules/update/update.manager.inc b/core/modules/update/update.manager.inc
index 2229f0a20..c3f4681e7 100644
--- a/core/modules/update/update.manager.inc
+++ b/core/modules/update/update.manager.inc
@@ -53,17 +53,17 @@ function update_manager_download_batch_finished($success, $results) {
'#title' => t('Downloading updates failed:'),
'#items' => $results['errors'],
];
- drupal_set_message(\Drupal::service('renderer')->render($item_list), 'error');
+ \Drupal::messenger()->addError(\Drupal::service('renderer')->render($item_list));
}
elseif ($success) {
- drupal_set_message(t('Updates downloaded successfully.'));
+ \Drupal::messenger()->addStatus(t('Updates downloaded successfully.'));
$_SESSION['update_manager_update_projects'] = $results['projects'];
return new RedirectResponse(\Drupal::url('update.confirmation_page', [], ['absolute' => TRUE]));
}
else {
// Ideally we're catching all Exceptions, so they should never see this,
// but just in case, we have to tell them something.
- drupal_set_message(t('Fatal error trying to download.'), 'error');
+ \Drupal::messenger()->addError(t('Fatal error trying to download.'));
}
}
diff --git a/core/modules/update/update.module b/core/modules/update/update.module
index 0545aef8b..25c4d9376 100644
--- a/core/modules/update/update.module
+++ b/core/modules/update/update.module
@@ -185,17 +185,18 @@ function update_page_top() {
foreach (['core', 'contrib'] as $report_type) {
$type = 'update_' . $report_type;
// hook_requirements() supports render arrays therefore we need to render
- // them before using drupal_set_message().
+ // them before using
+ // \Drupal\Core\Messenger\MessengerInterface::addStatus().
if (isset($status[$type]['description']) && is_array($status[$type]['description'])) {
$status[$type]['description'] = \Drupal::service('renderer')->renderPlain($status[$type]['description']);
}
if (!empty($verbose)) {
if (isset($status[$type]['severity'])) {
if ($status[$type]['severity'] == REQUIREMENT_ERROR) {
- drupal_set_message($status[$type]['description'], 'error');
+ \Drupal::messenger()->addError($status[$type]['description']);
}
elseif ($status[$type]['severity'] == REQUIREMENT_WARNING) {
- drupal_set_message($status[$type]['description'], 'warning');
+ \Drupal::messenger()->addWarning($status[$type]['description']);
}
}
}
@@ -205,7 +206,7 @@ function update_page_top() {
if (isset($status[$type])
&& isset($status[$type]['reason'])
&& $status[$type]['reason'] === UPDATE_NOT_SECURE) {
- drupal_set_message($status[$type]['description'], 'error');
+ \Drupal::messenger()->addError($status[$type]['description']);
}
}
}
@@ -407,6 +408,45 @@ function update_get_available($refresh = FALSE) {
return $available;
}
+/**
+ * Identifies equivalent security releases with a hardcoded list.
+ *
+ * Generally, only the latest minor version of Drupal 8 is supported. However,
+ * when security fixes are backported to an old branch, and the site owner
+ * updates to the release containing the backported fix, they should not
+ * see "Security update required!" again if the only other security releases
+ * are releases for the same advisories.
+ *
+ * @return string[]
+ * A list of security release numbers that are equivalent to this release
+ * (i.e. covered by the same advisory), for backported security fixes only.
+ *
+ * @internal
+ *
+ * @deprecated in Drupal 8.6.0 and will be removed before Drupal 9.0.0. Use the
+ * 'Insecure' release type tag in update XML provided by Drupal.org to
+ * determine if releases are insecure.
+ */
+function _update_equivalent_security_releases() {
+ trigger_error("_update_equivalent_security_releases() was a temporary fix and will be removed before 9.0.0. Use the 'Insecure' release type tag in update XML provided by Drupal.org to determine if releases are insecure.", E_USER_DEPRECATED);
+ switch (\Drupal::VERSION) {
+ case '8.3.8':
+ return ['8.4.5', '8.5.0-rc1'];
+ case '8.3.9':
+ return ['8.4.6', '8.5.1'];
+ case '8.4.5':
+ return ['8.5.0-rc1'];
+ case '8.4.6':
+ return ['8.5.1'];
+ case '8.4.7':
+ return ['8.5.2'];
+ case '8.4.8':
+ return ['8.5.3'];
+ }
+
+ return [];
+}
+
/**
* Adds a task to the queue for fetching release history data for a project.
*
@@ -455,15 +495,15 @@ function update_fetch_data_finished($success, $results) {
if ($success) {
if (!empty($results)) {
if (!empty($results['updated'])) {
- drupal_set_message(\Drupal::translation()->formatPlural($results['updated'], 'Checked available update data for one project.', 'Checked available update data for @count projects.'));
+ \Drupal::messenger()->addStatus(\Drupal::translation()->formatPlural($results['updated'], 'Checked available update data for one project.', 'Checked available update data for @count projects.'));
}
if (!empty($results['failures'])) {
- drupal_set_message(\Drupal::translation()->formatPlural($results['failures'], 'Failed to get available update data for one project.', 'Failed to get available update data for @count projects.'), 'error');
+ \Drupal::messenger()->addError(\Drupal::translation()->formatPlural($results['failures'], 'Failed to get available update data for one project.', 'Failed to get available update data for @count projects.'));
}
}
}
else {
- drupal_set_message(t('An error occurred trying to get available update data.'), 'error');
+ \Drupal::messenger()->addError(t('An error occurred trying to get available update data.'), 'error');
}
}
@@ -625,7 +665,6 @@ function template_preprocess_update_last_check(&$variables) {
* version of Drupal core.
*
* @see \Drupal\Core\Extension\ExtensionDiscovery
- * @see _system_rebuild_module_data()
*/
function update_verify_update_archive($project, $archive_file, $directory) {
$errors = [];
diff --git a/core/modules/update/update.report.inc b/core/modules/update/update.report.inc
index 7b14a0585..e42093cf7 100644
--- a/core/modules/update/update.report.inc
+++ b/core/modules/update/update.report.inc
@@ -5,7 +5,6 @@
* Code required only when rendering the available updates report.
*/
-use Drupal\Component\Utility\Unicode;
use Drupal\Core\Template\Attribute;
use Drupal\Core\Url;
@@ -31,7 +30,7 @@ function template_preprocess_update_report(&$variables) {
'library' => [
'update/drupal.update.admin',
],
- ]
+ ],
];
// For no project update data, populate no data message.
@@ -54,7 +53,7 @@ function template_preprocess_update_report(&$variables) {
'#attributes' => ['class' => ['update']],
];
}
- $row_key = !empty($project['title']) ? Unicode::strtolower($project['title']) : Unicode::strtolower($project['name']);
+ $row_key = !empty($project['title']) ? mb_strtolower($project['title']) : mb_strtolower($project['name']);
// Add the project status row and details.
$rows[$project['project_type']][$row_key]['status'] = $project_status;
@@ -231,7 +230,7 @@ function template_preprocess_update_project_status(&$variables) {
$extra_item['data'] = [
'#prefix' => '',
'#markup' => $value['data'],
- '#suffix' => ''
+ '#suffix' => '',
];
$variables['extras'][] = $extra_item;
}
diff --git a/core/modules/user/config/install/user.mail.yml b/core/modules/user/config/install/user.mail.yml
index 9f2ad670c..b8efa9e91 100644
--- a/core/modules/user/config/install/user.mail.yml
+++ b/core/modules/user/config/install/user.mail.yml
@@ -11,7 +11,7 @@ register_no_approval_required:
body: "[user:display-name],\n\nThank you for registering at [site:name]. You may now log in by clicking this link or copying and pasting it into your browser:\n\n[user:one-time-login-url]\n\nThis link can only be used once to log in and will lead you to a page where you can set your password.\n\nAfter setting your password, you will be able to log in at [site:login-url] in the future using:\n\nusername: [user:name]\npassword: Your password\n\n-- [site:name] team"
subject: 'Account details for [user:display-name] at [site:name]'
register_pending_approval:
- body: "[user:display-name],\n\nThank you for registering at [site:name]. Your application for an account is currently pending approval. Once it has been approved, you will receive another email containing information about how to log in, set your password, and other details.\n\n\n-- [site:name] team"
+ body: "[user:display-name],\n\nThank you for registering at [site:name]. Your application for an account is currently pending approval. Once it has been approved, you will receive another email containing information about how to log in, set your password, and other details.\n\n-- [site:name] team"
subject: 'Account details for [user:display-name] at [site:name] (pending admin approval)'
register_pending_approval_admin:
body: "[user:display-name] has applied for an account.\n\n[user:edit-url]"
diff --git a/core/modules/user/migrations/d7_user.yml b/core/modules/user/migrations/d7_user.yml
index 00eabf2c7..5adbe05e8 100644
--- a/core/modules/user/migrations/d7_user.yml
+++ b/core/modules/user/migrations/d7_user.yml
@@ -21,7 +21,7 @@ process:
timezone: timezone
langcode:
plugin: user_langcode
- source: language
+ source: entity_language
fallback_to_site_default: false
preferred_langcode:
plugin: user_langcode
diff --git a/core/modules/user/migrations/user_profile_entity_display.yml b/core/modules/user/migrations/user_profile_entity_display.yml
index 733663626..eafba0047 100644
--- a/core/modules/user/migrations/user_profile_entity_display.yml
+++ b/core/modules/user/migrations/user_profile_entity_display.yml
@@ -17,7 +17,18 @@ process:
entity_type: 'constants/entity_type'
bundle: 'constants/bundle'
view_mode: 'constants/view_mode'
- field_name: name
+ field_name:
+ -
+ plugin: migration_lookup
+ migration: user_profile_field
+ source: fid
+ -
+ plugin: skip_on_empty
+ method: row
+ -
+ plugin: extract
+ index:
+ - 1
type:
plugin: static_map
source: type
diff --git a/core/modules/user/migrations/user_profile_entity_form_display.yml b/core/modules/user/migrations/user_profile_entity_form_display.yml
index 44a5c5333..7416ac87a 100644
--- a/core/modules/user/migrations/user_profile_entity_form_display.yml
+++ b/core/modules/user/migrations/user_profile_entity_form_display.yml
@@ -14,7 +14,18 @@ source:
process:
entity_type: 'constants/entity_type'
bundle: 'constants/bundle'
- field_name: name
+ field_name:
+ -
+ plugin: migration_lookup
+ migration: user_profile_field
+ source: fid
+ -
+ plugin: skip_on_empty
+ method: row
+ -
+ plugin: extract
+ index:
+ - 1
form_mode: 'constants/form_mode'
type:
plugin: static_map
diff --git a/core/modules/user/migrations/user_profile_field.yml b/core/modules/user/migrations/user_profile_field.yml
index 94d7defd0..af6018efc 100644
--- a/core/modules/user/migrations/user_profile_field.yml
+++ b/core/modules/user/migrations/user_profile_field.yml
@@ -10,7 +10,15 @@ source:
entity_type: user
process:
entity_type: 'constants/entity_type'
- field_name: name
+ field_name:
+ -
+ plugin: machine_name
+ source: name
+ -
+ plugin: make_unique_entity_field
+ length: 30
+ entity_type: field_storage_config
+ field: field_name
type:
plugin: static_map
source: type
diff --git a/core/modules/user/migrations/user_profile_field_instance.yml b/core/modules/user/migrations/user_profile_field_instance.yml
index 50e0bd283..b6c706ceb 100644
--- a/core/modules/user/migrations/user_profile_field_instance.yml
+++ b/core/modules/user/migrations/user_profile_field_instance.yml
@@ -14,7 +14,18 @@ process:
bundle: 'constants/bundle'
label: title
description: explanation
- field_name: name
+ field_name:
+ -
+ plugin: migration_lookup
+ migration: user_profile_field
+ source: fid
+ -
+ plugin: skip_on_empty
+ method: row
+ -
+ plugin: extract
+ index:
+ - 1
required: required
destination:
plugin: entity:field_config
diff --git a/core/modules/user/src/AccountForm.php b/core/modules/user/src/AccountForm.php
index d6959681f..3624a9c93 100644
--- a/core/modules/user/src/AccountForm.php
+++ b/core/modules/user/src/AccountForm.php
@@ -6,7 +6,7 @@ use Drupal\Component\Datetime\TimeInterface;
use Drupal\Component\Utility\Crypt;
use Drupal\Core\Entity\ContentEntityForm;
use Drupal\Core\Entity\EntityConstraintViolationListInterface;
-use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Entity\EntityRepositoryInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageInterface;
@@ -31,8 +31,8 @@ abstract class AccountForm extends ContentEntityForm {
/**
* Constructs a new EntityForm object.
*
- * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
- * The entity manager.
+ * @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository
+ * The entity repository.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager.
* @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
@@ -40,8 +40,8 @@ abstract class AccountForm extends ContentEntityForm {
* @param \Drupal\Component\Datetime\TimeInterface $time
* The time service.
*/
- public function __construct(EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager, EntityTypeBundleInfoInterface $entity_type_bundle_info = NULL, TimeInterface $time = NULL) {
- parent::__construct($entity_manager, $entity_type_bundle_info, $time);
+ public function __construct(EntityRepositoryInterface $entity_repository, LanguageManagerInterface $language_manager, EntityTypeBundleInfoInterface $entity_type_bundle_info = NULL, TimeInterface $time = NULL) {
+ parent::__construct($entity_repository, $entity_type_bundle_info, $time);
$this->languageManager = $language_manager;
}
@@ -50,7 +50,7 @@ abstract class AccountForm extends ContentEntityForm {
*/
public static function create(ContainerInterface $container) {
return new static(
- $container->get('entity.manager'),
+ $container->get('entity.repository'),
$container->get('language_manager'),
$container->get('entity_type.bundle.info'),
$container->get('datetime.time')
@@ -347,7 +347,7 @@ abstract class AccountForm extends ContentEntityForm {
'timezone',
'langcode',
'preferred_langcode',
- 'preferred_admin_langcode'
+ 'preferred_admin_langcode',
], parent::getEditedFieldNames($form_state));
}
@@ -365,7 +365,7 @@ abstract class AccountForm extends ContentEntityForm {
'timezone',
'langcode',
'preferred_langcode',
- 'preferred_admin_langcode'
+ 'preferred_admin_langcode',
];
foreach ($violations->getByFields($field_names) as $violation) {
list($field_name) = explode('.', $violation->getPropertyPath(), 2);
diff --git a/core/modules/user/src/AccountSettingsForm.php b/core/modules/user/src/AccountSettingsForm.php
index bd80dd7e2..4a1b5af4a 100644
--- a/core/modules/user/src/AccountSettingsForm.php
+++ b/core/modules/user/src/AccountSettingsForm.php
@@ -154,13 +154,13 @@ class AccountSettingsForm extends ConfigFormBase {
USER_REGISTER_ADMINISTRATORS_ONLY => $this->t('Administrators only'),
USER_REGISTER_VISITORS => $this->t('Visitors'),
USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL => $this->t('Visitors, but administrator approval is required'),
- ]
+ ],
];
$form['registration_cancellation']['user_email_verification'] = [
'#type' => 'checkbox',
'#title' => $this->t('Require email verification when a visitor creates an account'),
'#default_value' => $config->get('verify_mail'),
- '#description' => $this->t('New users will be required to validate their email address prior to logging into the site, and will be assigned a system-generated password. With this setting disabled, users will be logged in immediately upon registering, and may select their own passwords during registration.')
+ '#description' => $this->t('New users will be required to validate their email address prior to logging into the site, and will be assigned a system-generated password. With this setting disabled, users will be logged in immediately upon registering, and may select their own passwords during registration.'),
];
$form['registration_cancellation']['user_password_strength'] = [
'#type' => 'checkbox',
diff --git a/core/modules/user/src/ContextProvider/CurrentUserContext.php b/core/modules/user/src/ContextProvider/CurrentUserContext.php
index aacc5f774..5e1756918 100644
--- a/core/modules/user/src/ContextProvider/CurrentUserContext.php
+++ b/core/modules/user/src/ContextProvider/CurrentUserContext.php
@@ -4,9 +4,8 @@ namespace Drupal\user\ContextProvider;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Entity\EntityManagerInterface;
-use Drupal\Core\Plugin\Context\Context;
-use Drupal\Core\Plugin\Context\ContextDefinition;
use Drupal\Core\Plugin\Context\ContextProviderInterface;
+use Drupal\Core\Plugin\Context\EntityContext;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
@@ -56,7 +55,7 @@ class CurrentUserContext implements ContextProviderInterface {
$current_user->_skipProtectedUserFieldConstraint = TRUE;
}
- $context = new Context(new ContextDefinition('entity:user', $this->t('Current user')), $current_user);
+ $context = EntityContext::fromEntity($current_user, $this->t('Current user'));
$cacheability = new CacheableMetadata();
$cacheability->setCacheContexts(['user']);
$context->addCacheableDependency($cacheability);
diff --git a/core/modules/user/src/Controller/UserController.php b/core/modules/user/src/Controller/UserController.php
index be15fac4a..09544aa63 100644
--- a/core/modules/user/src/Controller/UserController.php
+++ b/core/modules/user/src/Controller/UserController.php
@@ -120,12 +120,17 @@ class UserController extends ControllerBase {
else {
/** @var \Drupal\user\UserInterface $reset_link_user */
if ($reset_link_user = $this->userStorage->load($uid)) {
- drupal_set_message($this->t('Another user (%other_user) is already logged into the site on this computer, but you tried to use a one-time link for user %resetting_user. Please log out and try using the link again.',
- ['%other_user' => $account->getUsername(), '%resetting_user' => $reset_link_user->getUsername(), ':logout' => $this->url('user.logout')]), 'warning');
+ $this->messenger()
+ ->addWarning($this->t('Another user (%other_user) is already logged into the site on this computer, but you tried to use a one-time link for user %resetting_user. Please log out and try using the link again.',
+ [
+ '%other_user' => $account->getUsername(),
+ '%resetting_user' => $reset_link_user->getUsername(),
+ ':logout' => $this->url('user.logout'),
+ ]));
}
else {
// Invalid one-time link specifies an unknown user.
- drupal_set_message($this->t('The one-time login link you clicked is invalid.'), 'error');
+ $this->messenger()->addError($this->t('The one-time login link you clicked is invalid.'));
}
return $this->redirect('');
}
@@ -218,13 +223,13 @@ class UserController extends ControllerBase {
$timeout = $this->config('user.settings')->get('password_reset_timeout');
// No time out for first time login.
if ($user->getLastLoginTime() && $current - $timestamp > $timeout) {
- drupal_set_message($this->t('You have tried to use a one-time login link that has expired. Please request a new one using the form below.'), 'error');
+ $this->messenger()->addError($this->t('You have tried to use a one-time login link that has expired. Please request a new one using the form below.'));
return $this->redirect('user.pass');
}
elseif ($user->isAuthenticated() && ($timestamp >= $user->getLastLoginTime()) && ($timestamp <= $current) && Crypt::hashEquals($hash, user_pass_rehash($user, $timestamp))) {
user_login_finalize($user);
$this->logger->notice('User %name used one-time login link at time %timestamp.', ['%name' => $user->getDisplayName(), '%timestamp' => $timestamp]);
- drupal_set_message($this->t('You have just used your one-time login link. It is no longer necessary to use this link to log in. Please change your password.'));
+ $this->messenger()->addStatus($this->t('You have just used your one-time login link. It is no longer necessary to use this link to log in. Please change your password.'));
// Let the user's password be changed without the current password
// check.
$token = Crypt::randomBytesBase64(55);
@@ -239,7 +244,7 @@ class UserController extends ControllerBase {
);
}
- drupal_set_message($this->t('You have tried to use a one-time login link that has either been used or is no longer valid. Please request a new one using the form below.'), 'error');
+ $this->messenger()->addError($this->t('You have tried to use a one-time login link that has either been used or is no longer valid. Please request a new one using the form below.'));
return $this->redirect('user.pass');
}
@@ -268,7 +273,7 @@ class UserController extends ControllerBase {
* NULL.
*/
public function userTitle(UserInterface $user = NULL) {
- return $user ? ['#markup' => $user->getUsername(), '#allowed_tags' => Xss::getHtmlTagList()] : '';
+ return $user ? ['#markup' => $user->getDisplayName(), '#allowed_tags' => Xss::getHtmlTagList()] : '';
}
/**
@@ -312,10 +317,10 @@ class UserController extends ControllerBase {
// Since user_cancel() is not invoked via Form API, batch processing
// needs to be invoked manually and should redirect to the front page
// after completion.
- return batch_process('');
+ return batch_process('');
}
else {
- drupal_set_message(t('You have tried to use an account cancellation link that has expired. Please request a new one using the form below.'), 'error');
+ $this->messenger()->addError($this->t('You have tried to use an account cancellation link that has expired. Please request a new one using the form below.'));
return $this->redirect('entity.user.cancel_form', ['user' => $user->id()], ['absolute' => TRUE]);
}
}
diff --git a/core/modules/user/src/Entity/Role.php b/core/modules/user/src/Entity/Role.php
index 0226f94b7..3512fee03 100644
--- a/core/modules/user/src/Entity/Role.php
+++ b/core/modules/user/src/Entity/Role.php
@@ -12,6 +12,13 @@ use Drupal\user\RoleInterface;
* @ConfigEntityType(
* id = "user_role",
* label = @Translation("Role"),
+ * label_collection = @Translation("Roles"),
+ * label_singular = @Translation("role"),
+ * label_plural = @Translation("roles"),
+ * label_count = @PluralTranslation(
+ * singular = "@count role",
+ * plural = "@count roles",
+ * ),
* handlers = {
* "storage" = "Drupal\user\RoleStorage",
* "access" = "Drupal\user\RoleAccessControlHandler",
diff --git a/core/modules/user/src/Entity/User.php b/core/modules/user/src/Entity/User.php
index 54439cff9..b8675866f 100644
--- a/core/modules/user/src/Entity/User.php
+++ b/core/modules/user/src/Entity/User.php
@@ -22,6 +22,13 @@ use Drupal\user\UserInterface;
* @ContentEntityType(
* id = "user",
* label = @Translation("User"),
+ * label_collection = @Translation("Users"),
+ * label_singular = @Translation("user"),
+ * label_plural = @Translation("users"),
+ * label_count = @PluralTranslation(
+ * singular = "@count user",
+ * plural = "@count users",
+ * ),
* handlers = {
* "storage" = "Drupal\user\UserStorage",
* "storage_schema" = "Drupal\user\UserStorageSchema",
@@ -348,6 +355,7 @@ class User extends ContentEntityBase implements UserInterface {
public function isAuthenticated() {
return $this->id() > 0;
}
+
/**
* {@inheritdoc}
*/
@@ -420,7 +428,7 @@ class User extends ContentEntityBase implements UserInterface {
'name' => [LanguageInterface::LANGCODE_DEFAULT => ''],
// Explicitly set the langcode to ensure that field definitions do not
// need to be fetched to figure out a default.
- 'langcode' => [LanguageInterface::LANGCODE_DEFAULT => LanguageInterface::LANGCODE_NOT_SPECIFIED]
+ 'langcode' => [LanguageInterface::LANGCODE_DEFAULT => LanguageInterface::LANGCODE_NOT_SPECIFIED],
], $entity_type->id());
}
return clone static::$anonymousUser;
diff --git a/core/modules/user/src/Form/UserCancelForm.php b/core/modules/user/src/Form/UserCancelForm.php
index da7a5f4f9..a1fd5f7c8 100644
--- a/core/modules/user/src/Form/UserCancelForm.php
+++ b/core/modules/user/src/Form/UserCancelForm.php
@@ -139,7 +139,7 @@ class UserCancelForm extends ContentEntityConfirmFormBase {
$this->entity->user_cancel_notify = $form_state->getValue('user_cancel_notify');
$this->entity->save();
_user_mail_notify('cancel_confirm', $this->entity);
- drupal_set_message($this->t('A confirmation request to cancel your account has been sent to your email address.'));
+ $this->messenger()->addStatus($this->t('A confirmation request to cancel your account has been sent to your email address.'));
$this->logger('user')->notice('Sent account cancellation request to %name %email.', ['%name' => $this->entity->label(), '%email' => '<' . $this->entity->getEmail() . '>']);
$form_state->setRedirect(
diff --git a/core/modules/user/src/Form/UserMultipleCancelConfirm.php b/core/modules/user/src/Form/UserMultipleCancelConfirm.php
index b4487c265..b858d6ae2 100644
--- a/core/modules/user/src/Form/UserMultipleCancelConfirm.php
+++ b/core/modules/user/src/Form/UserMultipleCancelConfirm.php
@@ -5,6 +5,7 @@ namespace Drupal\user\Form;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Url;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use Drupal\user\UserStorageInterface;
@@ -132,7 +133,7 @@ class UserMultipleCancelConfirm extends ConfirmFormBase {
if (isset($root)) {
$redirect = (count($accounts) == 1);
$message = $this->t('The user account %name cannot be canceled.', ['%name' => $root->label()]);
- drupal_set_message($message, $redirect ? 'error' : 'warning');
+ $this->messenger()->addMessage($message, $redirect ? MessengerInterface::TYPE_ERROR : MessengerInterface::TYPE_WARNING);
// If only user 1 was selected, redirect to the overview.
if ($redirect) {
return $this->redirect('entity.user.collection');
diff --git a/core/modules/user/src/Form/UserPasswordForm.php b/core/modules/user/src/Form/UserPasswordForm.php
index a7ae862bb..b80564198 100644
--- a/core/modules/user/src/Form/UserPasswordForm.php
+++ b/core/modules/user/src/Form/UserPasswordForm.php
@@ -140,7 +140,7 @@ class UserPasswordForm extends FormBase {
$mail = _user_mail_notify('password_reset', $account, $langcode);
if (!empty($mail)) {
$this->logger('user')->notice('Password reset instructions mailed to %name at %email.', ['%name' => $account->getUsername(), '%email' => $account->getEmail()]);
- drupal_set_message($this->t('Further instructions have been sent to your email address.'));
+ $this->messenger()->addStatus($this->t('Further instructions have been sent to your email address.'));
}
$form_state->setRedirect('user.page');
diff --git a/core/modules/user/src/Form/UserPermissionsForm.php b/core/modules/user/src/Form/UserPermissionsForm.php
index 7d3adaa5d..0f7f0378d 100644
--- a/core/modules/user/src/Form/UserPermissionsForm.php
+++ b/core/modules/user/src/Form/UserPermissionsForm.php
@@ -216,7 +216,7 @@ class UserPermissionsForm extends FormBase {
user_role_change_permissions($role_name, (array) $form_state->getValue($role_name));
}
- drupal_set_message($this->t('The changes have been saved.'));
+ $this->messenger()->addStatus($this->t('The changes have been saved.'));
}
}
diff --git a/core/modules/user/src/Plugin/Block/UserLoginBlock.php b/core/modules/user/src/Plugin/Block/UserLoginBlock.php
index a9341f9b7..8649494b9 100644
--- a/core/modules/user/src/Plugin/Block/UserLoginBlock.php
+++ b/core/modules/user/src/Plugin/Block/UserLoginBlock.php
@@ -64,7 +64,6 @@ class UserLoginBlock extends BlockBase implements ContainerFactoryPluginInterfac
);
}
-
/**
* {@inheritdoc}
*/
diff --git a/core/modules/user/src/Plugin/Validation/Constraint/UserNameConstraintValidator.php b/core/modules/user/src/Plugin/Validation/Constraint/UserNameConstraintValidator.php
index 9e4776bdc..7e41ed15c 100644
--- a/core/modules/user/src/Plugin/Validation/Constraint/UserNameConstraintValidator.php
+++ b/core/modules/user/src/Plugin/Validation/Constraint/UserNameConstraintValidator.php
@@ -2,7 +2,6 @@
namespace Drupal\user\Plugin\Validation\Constraint;
-use Drupal\Component\Utility\Unicode;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
@@ -53,7 +52,7 @@ class UserNameConstraintValidator extends ConstraintValidator {
) {
$this->context->addViolation($constraint->illegalMessage);
}
- if (Unicode::strlen($name) > USERNAME_MAX_LENGTH) {
+ if (mb_strlen($name) > USERNAME_MAX_LENGTH) {
$this->context->addViolation($constraint->tooLongMessage, ['%name' => $name, '%max' => USERNAME_MAX_LENGTH]);
}
}
diff --git a/core/modules/user/src/Plugin/migrate/ProfileValues.php b/core/modules/user/src/Plugin/migrate/ProfileValues.php
index b082d591a..8399436c0 100644
--- a/core/modules/user/src/Plugin/migrate/ProfileValues.php
+++ b/core/modules/user/src/Plugin/migrate/ProfileValues.php
@@ -3,6 +3,8 @@
namespace Drupal\user\Plugin\migrate;
use Drupal\migrate\Exception\RequirementsException;
+use Drupal\migrate\MigrateExecutable;
+use Drupal\migrate\MigrateSkipRowException;
use Drupal\migrate\Plugin\Migration;
/**
@@ -31,11 +33,28 @@ class ProfileValues extends Migration {
$definition['idMap']['plugin'] = 'null';
try {
$profile_field_migration = $this->migrationPluginManager->createStubMigration($definition);
+ $migrate_executable = new MigrateExecutable($profile_field_migration);
$source_plugin = $profile_field_migration->getSourcePlugin();
$source_plugin->checkRequirements();
foreach ($source_plugin as $row) {
$name = $row->getSourceProperty('name');
- $this->process[$name] = $name;
+ $fid = $row->getSourceProperty('fid');
+ // The user profile field name can be greater than 32 characters. Use
+ // the migrated profile field name in the process pipeline.
+ $configuration =
+ [
+ 'migration' => 'user_profile_field',
+ 'source_ids' => $fid,
+ ];
+ $plugin = $this->processPluginManager->createInstance('migration_lookup', $configuration, $profile_field_migration);
+ $new_value = $plugin->transform($fid, $migrate_executable, $row, 'tmp');
+ if (isset($new_value[1])) {
+ // Set the destination to the migrated profile field name.
+ $this->process[$new_value[1]] = $name;
+ }
+ else {
+ throw new MigrateSkipRowException("Can't migrate source field $name.");
+ }
}
}
catch (RequirementsException $e) {
diff --git a/core/modules/user/src/Plugin/migrate/User.php b/core/modules/user/src/Plugin/migrate/User.php
index 5316259ab..d918932dd 100644
--- a/core/modules/user/src/Plugin/migrate/User.php
+++ b/core/modules/user/src/Plugin/migrate/User.php
@@ -37,7 +37,7 @@ class User extends FieldMigration {
}
$info = $row->getSource();
$this->fieldPluginCache[$field_type]
- ->processFieldValues($this, $field_name, $info);
+ ->defineValueProcessPipeline($this, $field_name, $info);
}
else {
if ($this->cckPluginManager->hasDefinition($field_type)) {
diff --git a/core/modules/user/src/Plugin/migrate/destination/EntityUser.php b/core/modules/user/src/Plugin/migrate/destination/EntityUser.php
index b5317beb8..316d063bf 100644
--- a/core/modules/user/src/Plugin/migrate/destination/EntityUser.php
+++ b/core/modules/user/src/Plugin/migrate/destination/EntityUser.php
@@ -2,7 +2,6 @@
namespace Drupal\user\Plugin\migrate\destination;
-use Drupal\Component\Utility\Unicode;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityStorageInterface;
@@ -15,6 +14,52 @@ use Drupal\migrate\Row;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
+ * Provides a destination plugin for migrating user entities.
+ *
+ * Example:
+ *
+ * The example below migrates users and preserves original passwords from a
+ * source that has passwords as MD5 hashes without salt. The passwords will be
+ * salted and re-hashed before they are saved to the destination Drupal
+ * database. The MD5 hash used in the example is a hash of 'password'.
+ *
+ * The example uses the EmbeddedDataSource source plugin for the sake of
+ * simplicity. The mapping between old user_ids and new Drupal uids is saved in
+ * the migration map table.
+ * @code
+ * id: custom_user_migration
+ * label: Custom user migration
+ * source:
+ * plugin: embedded_data
+ * data_rows:
+ * -
+ * user_id: 1
+ * name: johnsmith
+ * mail: johnsmith@example.com
+ * hash: '5f4dcc3b5aa765d61d8327deb882cf99'
+ * ids:
+ * user_id:
+ * type: integer
+ * process:
+ * name: name
+ * mail: mail
+ * pass: hash
+ * status:
+ * plugin: default_value
+ * default_value: 1
+ * destination:
+ * plugin: entity:user
+ * md5_passwords: true
+ * @endcode
+ *
+ * For configuration options inherited from the parent class, refer to
+ * \Drupal\migrate\Plugin\migrate\destination\EntityContentBase.
+ *
+ * The example above is about migrating an MD5 password hash. For more examples
+ * on different password hash types and a list of other user properties, refer
+ * to the handbook documentation:
+ * @see https://www.drupal.org/docs/8/api/migrate-api/migrate-destination-plugins-examples/migrating-users
+ *
* @MigrateDestination(
* id = "entity:user"
* )
@@ -122,8 +167,8 @@ class EntityUser extends EntityContentBase {
if (is_array($name)) {
$name = reset($name);
}
- if (Unicode::strlen($name) > USERNAME_MAX_LENGTH) {
- $row->setDestinationProperty('name', Unicode::substr($name, 0, USERNAME_MAX_LENGTH));
+ if (mb_strlen($name) > USERNAME_MAX_LENGTH) {
+ $row->setDestinationProperty('name', mb_substr($name, 0, USERNAME_MAX_LENGTH));
}
}
diff --git a/core/modules/user/src/Plugin/migrate/process/UserLangcode.php b/core/modules/user/src/Plugin/migrate/process/UserLangcode.php
index 80f20783b..5373dad67 100644
--- a/core/modules/user/src/Plugin/migrate/process/UserLangcode.php
+++ b/core/modules/user/src/Plugin/migrate/process/UserLangcode.php
@@ -74,7 +74,7 @@ class UserLangcode extends ProcessPluginBase implements ContainerFactoryPluginIn
return 'en';
}
}
- // If the user's language does not exists, use the default language.
+ // If the user's language does not exist, use the default language.
elseif ($this->languageManager->getLanguage($value) === NULL) {
return $this->languageManager->getDefaultLanguage()->getId();
}
diff --git a/core/modules/user/src/Plugin/migrate/process/d6/UserUpdate7002.php b/core/modules/user/src/Plugin/migrate/process/d6/UserUpdate7002.php
index 0efae5d6b..858fcfc99 100644
--- a/core/modules/user/src/Plugin/migrate/process/d6/UserUpdate7002.php
+++ b/core/modules/user/src/Plugin/migrate/process/d6/UserUpdate7002.php
@@ -54,6 +54,7 @@ class UserUpdate7002 extends ProcessPluginBase implements ContainerFactoryPlugin
$container->get('config.factory')->get('system.date')
);
}
+
/**
* {@inheritdoc}
*/
diff --git a/core/modules/user/src/Plugin/migrate/source/d6/User.php b/core/modules/user/src/Plugin/migrate/source/d6/User.php
index 5213534b4..ea9457c42 100644
--- a/core/modules/user/src/Plugin/migrate/source/d6/User.php
+++ b/core/modules/user/src/Plugin/migrate/source/d6/User.php
@@ -33,14 +33,6 @@ class User extends DrupalSqlBase {
// Add roles field.
$fields['roles'] = $this->t('Roles');
- // Profile fields.
- if ($this->moduleExists('profile')) {
- $fields += $this->select('profile_fields', 'pf')
- ->fields('pf', ['name', 'title'])
- ->execute()
- ->fetchAllKeyed();
- }
-
return $fields;
}
diff --git a/core/modules/user/src/Plugin/migrate/source/d6/UserPicture.php b/core/modules/user/src/Plugin/migrate/source/d6/UserPicture.php
index 0d5bd6bcb..288cf6cac 100644
--- a/core/modules/user/src/Plugin/migrate/source/d6/UserPicture.php
+++ b/core/modules/user/src/Plugin/migrate/source/d6/UserPicture.php
@@ -37,6 +37,7 @@ class UserPicture extends DrupalSqlBase {
'picture' => "Path to the user's uploaded picture.",
];
}
+
/**
* {@inheritdoc}
*/
diff --git a/core/modules/user/src/Plugin/migrate/source/d6/UserPictureFile.php b/core/modules/user/src/Plugin/migrate/source/d6/UserPictureFile.php
index 72d18fd08..4414198e3 100644
--- a/core/modules/user/src/Plugin/migrate/source/d6/UserPictureFile.php
+++ b/core/modules/user/src/Plugin/migrate/source/d6/UserPictureFile.php
@@ -68,6 +68,7 @@ class UserPictureFile extends DrupalSqlBase {
'filename' => 'The picture filename.',
];
}
+
/**
* {@inheritdoc}
*/
diff --git a/core/modules/user/src/Plugin/migrate/source/d7/User.php b/core/modules/user/src/Plugin/migrate/source/d7/User.php
index 41f9ab4d6..3400bf550 100644
--- a/core/modules/user/src/Plugin/migrate/source/d7/User.php
+++ b/core/modules/user/src/Plugin/migrate/source/d7/User.php
@@ -62,18 +62,32 @@ class User extends FieldableEntity {
* {@inheritdoc}
*/
public function prepareRow(Row $row) {
+ $uid = $row->getSourceProperty('uid');
+
$roles = $this->select('users_roles', 'ur')
->fields('ur', ['rid'])
- ->condition('ur.uid', $row->getSourceProperty('uid'))
+ ->condition('ur.uid', $uid)
->execute()
->fetchCol();
$row->setSourceProperty('roles', $roles);
$row->setSourceProperty('data', unserialize($row->getSourceProperty('data')));
+ // If this entity was translated using Entity Translation, we need to get
+ // its source language to get the field values in the right language.
+ // The translations will be migrated by the d7_user_entity_translation
+ // migration.
+ $entity_translatable = $this->isEntityTranslatable('user');
+ $source_language = $this->getEntityTranslationSourceLanguage('user', $uid);
+ $language = $entity_translatable && $source_language ? $source_language : $row->getSourceProperty('language');
+ $row->setSourceProperty('entity_language', $language);
+
// Get Field API field values.
- foreach (array_keys($this->getFields('user')) as $field) {
- $row->setSourceProperty($field, $this->getFieldValues('user', $field, $row->getSourceProperty('uid')));
+ foreach ($this->getFields('user') as $field_name => $field) {
+ // Ensure we're using the right language if the entity and the field are
+ // translatable.
+ $field_language = $entity_translatable && $field['translatable'] ? $language : NULL;
+ $row->setSourceProperty($field_name, $this->getFieldValues('user', $field_name, $uid, NULL, $field_language));
}
// Get profile field values. This code is lifted directly from the D6
diff --git a/core/modules/user/src/Plugin/migrate/source/d7/UserEntityTranslation.php b/core/modules/user/src/Plugin/migrate/source/d7/UserEntityTranslation.php
new file mode 100644
index 000000000..7d4f8dfa2
--- /dev/null
+++ b/core/modules/user/src/Plugin/migrate/source/d7/UserEntityTranslation.php
@@ -0,0 +1,79 @@
+select('entity_translation', 'et')
+ ->fields('et')
+ ->condition('et.entity_type', 'user')
+ ->condition('et.source', '', '<>');
+
+ return $query;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function prepareRow(Row $row) {
+ $uid = $row->getSourceProperty('entity_id');
+ $language = $row->getSourceProperty('language');
+
+ // Get Field API field values.
+ foreach ($this->getFields('user') as $field_name => $field) {
+ // Ensure we're using the right language if the entity is translatable.
+ $field_language = $field['translatable'] ? $language : NULL;
+ $row->setSourceProperty($field_name, $this->getFieldValues('user', $field_name, $uid, NULL, $field_language));
+ }
+
+ return parent::prepareRow($row);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function fields() {
+ return [
+ 'entity_type' => $this->t('The entity type this translation relates to'),
+ 'entity_id' => $this->t('The entity id this translation relates to'),
+ 'revision_id' => $this->t('The entity revision id this translation relates to'),
+ 'language' => $this->t('The target language for this translation.'),
+ 'source' => $this->t('The source language from which this translation was created.'),
+ 'uid' => $this->t('The author of this translation.'),
+ 'status' => $this->t('Boolean indicating whether the translation is published (visible to non-administrators).'),
+ 'translate' => $this->t('A boolean indicating whether this translation needs to be updated.'),
+ 'created' => $this->t('The Unix timestamp when the translation was created.'),
+ 'changed' => $this->t('The Unix timestamp when the translation was most recently saved.'),
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getIds() {
+ return [
+ 'entity_id' => [
+ 'type' => 'integer',
+ ],
+ 'language' => [
+ 'type' => 'string',
+ ],
+ ];
+ }
+
+}
diff --git a/core/modules/user/src/Plugin/views/access/Permission.php b/core/modules/user/src/Plugin/views/access/Permission.php
index 8d0c43dfc..291eb1ca8 100644
--- a/core/modules/user/src/Plugin/views/access/Permission.php
+++ b/core/modules/user/src/Plugin/views/access/Permission.php
@@ -100,7 +100,6 @@ class Permission extends AccessPluginBase implements CacheableDependencyInterfac
return $this->t($this->options['perm']);
}
-
protected function defineOptions() {
$options = parent::defineOptions();
$options['perm'] = ['default' => 'access content'];
diff --git a/core/modules/user/src/Plugin/views/access/Role.php b/core/modules/user/src/Plugin/views/access/Role.php
index e35daa1ee..d1fe8abcc 100644
--- a/core/modules/user/src/Plugin/views/access/Role.php
+++ b/core/modules/user/src/Plugin/views/access/Role.php
@@ -96,7 +96,6 @@ class Role extends AccessPluginBase implements CacheableDependencyInterface {
}
}
-
protected function defineOptions() {
$options = parent::defineOptions();
$options['role'] = ['default' => []];
diff --git a/core/modules/user/src/Plugin/views/argument/Uid.php b/core/modules/user/src/Plugin/views/argument/Uid.php
index 5efbecd53..31f6c33af 100644
--- a/core/modules/user/src/Plugin/views/argument/Uid.php
+++ b/core/modules/user/src/Plugin/views/argument/Uid.php
@@ -23,7 +23,7 @@ class Uid extends NumericArgument {
protected $storage;
/**
- * Constructs a Drupal\Component\Plugin\PluginBase object.
+ * Constructs a \Drupal\user\Plugin\views\argument\Uid object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
diff --git a/core/modules/user/src/Plugin/views/field/Permissions.php b/core/modules/user/src/Plugin/views/field/Permissions.php
index 71aef5975..bab5503ba 100644
--- a/core/modules/user/src/Plugin/views/field/Permissions.php
+++ b/core/modules/user/src/Plugin/views/field/Permissions.php
@@ -33,7 +33,7 @@ class Permissions extends PrerenderList {
protected $moduleHandler;
/**
- * Constructs a Drupal\Component\Plugin\PluginBase object.
+ * Constructs a \Drupal\user\Plugin\views\field\Permissions object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
diff --git a/core/modules/user/src/Plugin/views/field/Roles.php b/core/modules/user/src/Plugin/views/field/Roles.php
index e7d9495bc..eec26f88a 100644
--- a/core/modules/user/src/Plugin/views/field/Roles.php
+++ b/core/modules/user/src/Plugin/views/field/Roles.php
@@ -25,7 +25,7 @@ class Roles extends PrerenderList {
protected $database;
/**
- * Constructs a Drupal\Component\Plugin\PluginBase object.
+ * Constructs a \Drupal\user\Plugin\views\field\Roles object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
@@ -82,7 +82,7 @@ class Roles extends PrerenderList {
$ordered_roles = array_flip(array_keys($roles));
foreach ($this->items as &$user_roles) {
// Create an array of rids that the user has in the role weight order.
- $sorted_keys = array_intersect_key($ordered_roles, $user_roles);
+ $sorted_keys = array_intersect_key($ordered_roles, $user_roles);
// Merge with the unsorted array of role information which has the
// effect of sorting it.
$user_roles = array_merge($sorted_keys, $user_roles);
diff --git a/core/modules/user/src/Plugin/views/field/UserData.php b/core/modules/user/src/Plugin/views/field/UserData.php
index 365ab3b74..1a4bb82ac 100644
--- a/core/modules/user/src/Plugin/views/field/UserData.php
+++ b/core/modules/user/src/Plugin/views/field/UserData.php
@@ -34,7 +34,6 @@ class UserData extends FieldPluginBase {
*/
protected $moduleHandler;
-
/**
* {@inheritdoc}
*/
diff --git a/core/modules/user/src/Plugin/views/wizard/Users.php b/core/modules/user/src/Plugin/views/wizard/Users.php
index 00b6863ac..6ba045f70 100644
--- a/core/modules/user/src/Plugin/views/wizard/Users.php
+++ b/core/modules/user/src/Plugin/views/wizard/Users.php
@@ -37,7 +37,7 @@ class Users extends WizardPluginBase {
'plugin_id' => 'boolean',
'entity_type' => 'user',
'entity_field' => 'status',
- ]
+ ],
];
/**
diff --git a/core/modules/user/src/ProfileForm.php b/core/modules/user/src/ProfileForm.php
index c856b75da..137868c4f 100644
--- a/core/modules/user/src/ProfileForm.php
+++ b/core/modules/user/src/ProfileForm.php
@@ -38,7 +38,7 @@ class ProfileForm extends AccountForm {
$account->save();
$form_state->setValue('uid', $account->id());
- drupal_set_message($this->t('The changes have been saved.'));
+ $this->messenger()->addStatus($this->t('The changes have been saved.'));
}
/**
diff --git a/core/modules/user/src/RegisterForm.php b/core/modules/user/src/RegisterForm.php
index 4974e69d8..18760f4d4 100644
--- a/core/modules/user/src/RegisterForm.php
+++ b/core/modules/user/src/RegisterForm.php
@@ -103,28 +103,28 @@ class RegisterForm extends AccountForm {
// New administrative account without notification.
if ($admin && !$notify) {
- drupal_set_message($this->t('Created a new user account for %name. No email has been sent.', [':url' => $account->url(), '%name' => $account->getUsername()]));
+ $this->messenger()->addStatus($this->t('Created a new user account for %name. No email has been sent.', [':url' => $account->url(), '%name' => $account->getUsername()]));
}
// No email verification required; log in user immediately.
elseif (!$admin && !\Drupal::config('user.settings')->get('verify_mail') && $account->isActive()) {
_user_mail_notify('register_no_approval_required', $account);
user_login_finalize($account);
- drupal_set_message($this->t('Registration successful. You are now logged in.'));
+ $this->messenger()->addStatus($this->t('Registration successful. You are now logged in.'));
$form_state->setRedirect('');
}
// No administrator approval required.
elseif ($account->isActive() || $notify) {
if (!$account->getEmail() && $notify) {
- drupal_set_message($this->t('The new user %name was created without an email address, so no welcome message was sent.', [':url' => $account->url(), '%name' => $account->getUsername()]));
+ $this->messenger()->addStatus($this->t('The new user %name was created without an email address, so no welcome message was sent.', [':url' => $account->url(), '%name' => $account->getUsername()]));
}
else {
$op = $notify ? 'register_admin_created' : 'register_no_approval_required';
if (_user_mail_notify($op, $account)) {
if ($notify) {
- drupal_set_message($this->t('A welcome message with further instructions has been emailed to the new user %name.', [':url' => $account->url(), '%name' => $account->getUsername()]));
+ $this->messenger()->addStatus($this->t('A welcome message with further instructions has been emailed to the new user %name.', [':url' => $account->url(), '%name' => $account->getUsername()]));
}
else {
- drupal_set_message($this->t('A welcome message with further instructions has been sent to your email address.'));
+ $this->messenger()->addStatus($this->t('A welcome message with further instructions has been sent to your email address.'));
$form_state->setRedirect('');
}
}
@@ -133,7 +133,7 @@ class RegisterForm extends AccountForm {
// Administrator approval required.
else {
_user_mail_notify('register_pending_approval', $account);
- drupal_set_message($this->t('Thank you for applying for an account. Your account is currently pending approval by the site administrator.
In the meantime, a welcome message with further instructions has been sent to your email address.'));
+ $this->messenger()->addStatus($this->t('Thank you for applying for an account. Your account is currently pending approval by the site administrator.
In the meantime, a welcome message with further instructions has been sent to your email address.'));
$form_state->setRedirect('');
}
}
diff --git a/core/modules/user/src/RoleForm.php b/core/modules/user/src/RoleForm.php
index e40067cf8..023159b87 100644
--- a/core/modules/user/src/RoleForm.php
+++ b/core/modules/user/src/RoleForm.php
@@ -57,11 +57,11 @@ class RoleForm extends EntityForm {
$edit_link = $this->entity->link($this->t('Edit'));
if ($status == SAVED_UPDATED) {
- drupal_set_message($this->t('Role %label has been updated.', ['%label' => $entity->label()]));
+ $this->messenger()->addStatus($this->t('Role %label has been updated.', ['%label' => $entity->label()]));
$this->logger('user')->notice('Role %label has been updated.', ['%label' => $entity->label(), 'link' => $edit_link]);
}
else {
- drupal_set_message($this->t('Role %label has been added.', ['%label' => $entity->label()]));
+ $this->messenger()->addStatus($this->t('Role %label has been added.', ['%label' => $entity->label()]));
$this->logger('user')->notice('Role %label has been added.', ['%label' => $entity->label(), 'link' => $edit_link]);
}
$form_state->setRedirect('entity.user_role.collection');
diff --git a/core/modules/user/src/RoleListBuilder.php b/core/modules/user/src/RoleListBuilder.php
index aa3b844fd..306677a1e 100644
--- a/core/modules/user/src/RoleListBuilder.php
+++ b/core/modules/user/src/RoleListBuilder.php
@@ -4,7 +4,11 @@ namespace Drupal\user;
use Drupal\Core\Config\Entity\DraggableListBuilder;
use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Messenger\MessengerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Defines a class to build a listing of user role entities.
@@ -13,6 +17,41 @@ use Drupal\Core\Form\FormStateInterface;
*/
class RoleListBuilder extends DraggableListBuilder {
+ /**
+ * The messenger.
+ *
+ * @var \Drupal\Core\Messenger\MessengerInterface
+ */
+ protected $messenger;
+
+ /**
+ * RoleListBuilder constructor.
+ *
+ * @param \Drupal\Core\Entity\EntityTypeInterface $entityType
+ * The entity type definition.
+ * @param \Drupal\Core\Entity\EntityStorageInterface $storage
+ * The entity storage class.
+ * @param \Drupal\Core\Messenger\MessengerInterface $messenger
+ * The messenger.
+ */
+ public function __construct(EntityTypeInterface $entityType,
+ EntityStorageInterface $storage,
+ MessengerInterface $messenger) {
+ parent::__construct($entityType, $storage);
+ $this->messenger = $messenger;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
+ return new static(
+ $entity_type,
+ $container->get('entity.manager')->getStorage($entity_type->id()),
+ $container->get('messenger')
+ );
+ }
+
/**
* {@inheritdoc}
*/
@@ -58,7 +97,7 @@ class RoleListBuilder extends DraggableListBuilder {
public function submitForm(array &$form, FormStateInterface $form_state) {
parent::submitForm($form, $form_state);
- drupal_set_message(t('The role settings have been updated.'));
+ $this->messenger->addStatus($this->t('The role settings have been updated.'));
}
}
diff --git a/core/modules/user/src/Tests/UserPasswordResetTest.php b/core/modules/user/src/Tests/Functional/UserPasswordResetTest.php
similarity index 94%
rename from core/modules/user/src/Tests/UserPasswordResetTest.php
rename to core/modules/user/src/Tests/Functional/UserPasswordResetTest.php
index b940878f2..11d069135 100644
--- a/core/modules/user/src/Tests/UserPasswordResetTest.php
+++ b/core/modules/user/src/Tests/Functional/UserPasswordResetTest.php
@@ -1,10 +1,11 @@
drupalLogin($account);
$this->account = User::load($account->id());
- $this->account->pass_raw = $account->pass_raw;
+ $this->account->passRaw = $account->passRaw;
$this->drupalLogout();
// Set the last login time that is used to generate the one-time link so
@@ -114,14 +108,6 @@ class UserPasswordResetTest extends PageCacheTagsTestBase {
$this->assertLink(t('Log out'));
$this->assertTitle(t('@name | @site', ['@name' => $this->account->getUsername(), '@site' => $this->config('system.site')->get('name')]), 'Logged in using password reset link.');
- // Make sure the ajax request from uploading a user picture does not
- // invalidate the reset token.
- $image = current($this->drupalGetTestFiles('image'));
- $edit = [
- 'files[user_picture_0]' => \Drupal::service('file_system')->realpath($image->uri),
- ];
- $this->drupalPostAjaxForm(NULL, $edit, 'user_picture_0_upload_button');
-
// Change the forgotten password.
$password = user_password();
$edit = ['pass[pass1]' => $password, 'pass[pass2]' => $password];
diff --git a/core/modules/user/src/Tests/FunctionalJavascript/UserPasswordResetTest.php b/core/modules/user/src/Tests/FunctionalJavascript/UserPasswordResetTest.php
new file mode 100644
index 000000000..caad73917
--- /dev/null
+++ b/core/modules/user/src/Tests/FunctionalJavascript/UserPasswordResetTest.php
@@ -0,0 +1,128 @@
+drupalCreateUser();
+
+ // Activate user by logging in.
+ $this->drupalLogin($account);
+
+ $this->account = User::load($account->id());
+ $this->account->pass_raw = $account->pass_raw;
+ $this->drupalLogout();
+
+ // Set the last login time that is used to generate the one-time link so
+ // that it is definitely over a second ago.
+ $account->login = REQUEST_TIME - mt_rand(10, 100000);
+ db_update('users_field_data')
+ ->fields(['login' => $account->getLastLoginTime()])
+ ->condition('uid', $account->id())
+ ->execute();
+ }
+
+ /**
+ * Tests password reset functionality with an AJAX form.
+ *
+ * Make sure the ajax request from uploading a user picture does not
+ * invalidate the reset token.
+ */
+ public function testUserPasswordResetWithAdditionalAjaxForm() {
+ $this->drupalGet(Url::fromRoute('user.reset.form', ['uid' => $this->account->id()]));
+
+ // Try to reset the password for an invalid account.
+ $this->drupalGet('user/password');
+
+ // Reset the password by username via the password reset page.
+ $edit['name'] = $this->account->getUsername();
+ $this->drupalPostForm(NULL, $edit, t('Submit'));
+
+ $resetURL = $this->getResetURL();
+ $this->drupalGet($resetURL);
+
+ // Login
+ $this->drupalPostForm(NULL, NULL, t('Log in'));
+
+ // Generate file.
+ $image_file = current($this->drupalGetTestFiles('image'));
+ $image_path = \Drupal::service('file_system')->realpath($image_file->uri);
+
+ // Upload file.
+ $this->getSession()->getPage()->attachFileToField('Picture', $image_path);
+ $this->assertSession()->waitForButton('Remove');
+
+ // Change the forgotten password.
+ $password = user_password();
+ $edit = ['pass[pass1]' => $password, 'pass[pass2]' => $password];
+ $this->drupalPostForm(NULL, $edit, t('Save'));
+
+ // Verify that the password reset session has been destroyed.
+ $this->drupalPostForm(NULL, $edit, t('Save'));
+ // Password needed to make profile changes.
+ $this->assertSession()->pageTextContains("Your current password is missing or incorrect; it's required to change the Password.");
+ }
+
+ /**
+ * Retrieves password reset email and extracts the login link.
+ */
+ public function getResetURL() {
+ // Assume the most recent email.
+ $_emails = $this->drupalGetMails();
+ $email = end($_emails);
+ $urls = [];
+ preg_match('#.+user/reset/.+#', $email['body'], $urls);
+
+ return $urls[0];
+ }
+
+}
diff --git a/core/modules/user/src/Tests/UserAdminSettingsFormTest.php b/core/modules/user/src/Tests/UserAdminSettingsFormTest.php
index b77411154..784d5e1e6 100644
--- a/core/modules/user/src/Tests/UserAdminSettingsFormTest.php
+++ b/core/modules/user/src/Tests/UserAdminSettingsFormTest.php
@@ -2,7 +2,7 @@
namespace Drupal\user\Tests;
-use Drupal\system\Tests\System\SystemConfigFormTestBase;
+use Drupal\KernelTests\ConfigFormTestBase;
use Drupal\user\AccountSettingsForm;
/**
@@ -10,8 +10,16 @@ use Drupal\user\AccountSettingsForm;
*
* @group user
*/
-class UserAdminSettingsFormTest extends SystemConfigFormTestBase {
+class UserAdminSettingsFormTest extends ConfigFormTestBase {
+ /**
+ * {@inheritdoc}
+ */
+ public static $modules = ['user', 'system'];
+
+ /**
+ * {@inheritdoc}
+ */
protected function setUp() {
parent::setUp();
diff --git a/core/modules/user/src/Tests/UserBlocksTest.php b/core/modules/user/src/Tests/UserBlocksTest.php
index 0a4c619da..b7e6cad7e 100644
--- a/core/modules/user/src/Tests/UserBlocksTest.php
+++ b/core/modules/user/src/Tests/UserBlocksTest.php
@@ -121,38 +121,6 @@ class UserBlocksTest extends WebTestBase {
$this->assertNoText(t('Unrecognized username or password. Forgot your password?'));
}
- /**
- * Test the Who's Online block.
- */
- public function testWhosOnlineBlock() {
- $block = $this->drupalPlaceBlock('views_block:who_s_online-who_s_online_block');
-
- // Generate users.
- $user1 = $this->drupalCreateUser(['access user profiles']);
- $user2 = $this->drupalCreateUser([]);
- $user3 = $this->drupalCreateUser([]);
-
- // Update access of two users to be within the active timespan.
- $this->updateAccess($user1->id());
- $this->updateAccess($user2->id(), REQUEST_TIME + 1);
-
- // Insert an inactive user who should not be seen in the block, and ensure
- // that the admin user used in setUp() does not appear.
- $inactive_time = REQUEST_TIME - (15 * 60) - 1;
- $this->updateAccess($user3->id(), $inactive_time);
- $this->updateAccess($this->adminUser->id(), $inactive_time);
-
- // Test block output.
- \Drupal::currentUser()->setAccount($user1);
- $content = entity_view($block, 'block');
- $this->setRawContent(\Drupal::service('renderer')->renderRoot($content));
- $this->assertRaw(t('2 users'), 'Correct number of online users (2 users).');
- $this->assertText($user1->getUsername(), 'Active user 1 found in online list.');
- $this->assertText($user2->getUsername(), 'Active user 2 found in online list.');
- $this->assertNoText($user3->getUsername(), 'Inactive user not found in online list.');
- $this->assertTrue(strpos($this->getRawContent(), $user1->getUsername()) > strpos($this->getRawContent(), $user2->getUsername()), 'Online users are ordered correctly.');
- }
-
/**
* Updates the access column for a user.
*/
diff --git a/core/modules/user/src/Tests/UserRegistrationTest.php b/core/modules/user/src/Tests/UserRegistrationTest.php
index 128b71b7d..95fd7a099 100644
--- a/core/modules/user/src/Tests/UserRegistrationTest.php
+++ b/core/modules/user/src/Tests/UserRegistrationTest.php
@@ -3,7 +3,7 @@
namespace Drupal\user\Tests;
use Drupal\Core\Entity\Entity\EntityFormDisplay;
-use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
@@ -273,11 +273,11 @@ class UserRegistrationTest extends WebTestBase {
$edit = ['mail' => 'test@example.com', 'name' => $account->getUsername()];
$this->drupalPostForm('user/register', $edit, t('Create new account'));
- $this->assertRaw(SafeMarkup::format('The username %value is already taken.', ['%value' => $account->getUsername()]));
+ $this->assertRaw(new FormattableMarkup('The username %value is already taken.', ['%value' => $account->getUsername()]));
$edit = ['mail' => $account->getEmail(), 'name' => $this->randomString()];
$this->drupalPostForm('user/register', $edit, t('Create new account'));
- $this->assertRaw(SafeMarkup::format('The email address %value is already taken.', ['%value' => $account->getEmail()]));
+ $this->assertRaw(new FormattableMarkup('The email address %value is already taken.', ['%value' => $account->getEmail()]));
}
/**
@@ -348,35 +348,27 @@ class UserRegistrationTest extends WebTestBase {
// Check that the 'add more' button works.
$field_storage->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
$field_storage->save();
- foreach (['js', 'nojs'] as $js) {
- $this->drupalGet('user/register');
- $this->assertRegistrationFormCacheTagsWithUserFields();
- // Add two inputs.
- $value = rand(1, 255);
- $edit = [];
- $edit['test_user_field[0][value]'] = $value;
- if ($js == 'js') {
- $this->drupalPostAjaxForm(NULL, $edit, 'test_user_field_add_more');
- $this->drupalPostAjaxForm(NULL, $edit, 'test_user_field_add_more');
- }
- else {
- $this->drupalPostForm(NULL, $edit, t('Add another item'));
- $this->drupalPostForm(NULL, $edit, t('Add another item'));
- }
- // Submit with three values.
- $edit['test_user_field[1][value]'] = $value + 1;
- $edit['test_user_field[2][value]'] = $value + 2;
- $edit['name'] = $name = $this->randomMachineName();
- $edit['mail'] = $mail = $edit['name'] . '@example.com';
- $this->drupalPostForm(NULL, $edit, t('Create new account'));
- // Check user fields.
- $accounts = $this->container->get('entity_type.manager')->getStorage('user')
- ->loadByProperties(['name' => $name, 'mail' => $mail]);
- $new_user = reset($accounts);
- $this->assertEqual($new_user->test_user_field[0]->value, $value, format_string('@js : The field value was correctly saved.', ['@js' => $js]));
- $this->assertEqual($new_user->test_user_field[1]->value, $value + 1, format_string('@js : The field value was correctly saved.', ['@js' => $js]));
- $this->assertEqual($new_user->test_user_field[2]->value, $value + 2, format_string('@js : The field value was correctly saved.', ['@js' => $js]));
- }
+ $this->drupalGet('user/register');
+ $this->assertRegistrationFormCacheTagsWithUserFields();
+ // Add two inputs.
+ $value = rand(1, 255);
+ $edit = [];
+ $edit['test_user_field[0][value]'] = $value;
+ $this->drupalPostForm(NULL, $edit, t('Add another item'));
+ $this->drupalPostForm(NULL, $edit, t('Add another item'));
+ // Submit with three values.
+ $edit['test_user_field[1][value]'] = $value + 1;
+ $edit['test_user_field[2][value]'] = $value + 2;
+ $edit['name'] = $name = $this->randomMachineName();
+ $edit['mail'] = $mail = $edit['name'] . '@example.com';
+ $this->drupalPostForm(NULL, $edit, t('Create new account'));
+ // Check user fields.
+ $accounts = $this->container->get('entity_type.manager')->getStorage('user')
+ ->loadByProperties(['name' => $name, 'mail' => $mail]);
+ $new_user = reset($accounts);
+ $this->assertEqual($new_user->test_user_field[0]->value, $value, 'The field value was correctly saved.');
+ $this->assertEqual($new_user->test_user_field[1]->value, $value + 1, 'The field value was correctly saved.');
+ $this->assertEqual($new_user->test_user_field[2]->value, $value + 2, 'The field value was correctly saved.');
}
/**
diff --git a/core/modules/user/src/UserAccessControlHandler.php b/core/modules/user/src/UserAccessControlHandler.php
index 8ff01d144..19fe6b692 100644
--- a/core/modules/user/src/UserAccessControlHandler.php
+++ b/core/modules/user/src/UserAccessControlHandler.php
@@ -106,7 +106,7 @@ class UserAccessControlHandler extends EntityAccessControlHandler {
return AccessResult::allowed()->cachePerPermissions()->cachePerUser();
}
else {
- return AccessResult::forbidden();
+ return AccessResult::neutral();
}
case 'preferred_langcode':
@@ -116,7 +116,7 @@ class UserAccessControlHandler extends EntityAccessControlHandler {
// Allow view access to own mail address and other personalization
// settings.
if ($operation == 'view') {
- return $is_own_account ? AccessResult::allowed()->cachePerUser() : AccessResult::forbidden();
+ return $is_own_account ? AccessResult::allowed()->cachePerUser() : AccessResult::neutral();
}
// Anyone that can edit the user can also edit this field.
return AccessResult::allowed()->cachePerPermissions();
@@ -127,14 +127,14 @@ class UserAccessControlHandler extends EntityAccessControlHandler {
case 'created':
// Allow viewing the created date, but not editing it.
- return ($operation == 'view') ? AccessResult::allowed() : AccessResult::forbidden();
+ return ($operation == 'view') ? AccessResult::allowed() : AccessResult::neutral();
case 'roles':
case 'status':
case 'access':
case 'login':
case 'init':
- return AccessResult::forbidden();
+ return AccessResult::neutral();
}
return parent::checkFieldAccess($operation, $field_definition, $account, $items);
diff --git a/core/modules/user/src/UserStorage.php b/core/modules/user/src/UserStorage.php
index 809f3e384..7ca4f2434 100644
--- a/core/modules/user/src/UserStorage.php
+++ b/core/modules/user/src/UserStorage.php
@@ -21,7 +21,7 @@ class UserStorage extends SqlContentEntityStorage implements UserStorageInterfac
// The anonymous user account is saved with the fixed user ID of 0.
// Therefore we need to check for NULL explicitly.
if ($entity->id() === NULL) {
- $entity->uid->value = $this->database->nextId($this->database->query('SELECT MAX(uid) FROM {users}')->fetchField());
+ $entity->uid->value = $this->database->nextId($this->database->query('SELECT MAX(uid) FROM {' . $this->getBaseTable() . '}')->fetchField());
$entity->enforceIsNew();
}
return parent::doSaveFieldItems($entity, $names);
@@ -39,7 +39,7 @@ class UserStorage extends SqlContentEntityStorage implements UserStorageInterfac
* {@inheritdoc}
*/
public function updateLastLoginTimestamp(UserInterface $account) {
- $this->database->update('users_field_data')
+ $this->database->update($this->getDataTable())
->fields(['login' => $account->getLastLoginTime()])
->condition('uid', $account->id())
->execute();
@@ -51,7 +51,7 @@ class UserStorage extends SqlContentEntityStorage implements UserStorageInterfac
* {@inheritdoc}
*/
public function updateLastAccessTimestamp(AccountInterface $account, $timestamp) {
- $this->database->update('users_field_data')
+ $this->database->update($this->getDataTable())
->fields([
'access' => $timestamp,
])
diff --git a/core/modules/user/src/UserStorageSchema.php b/core/modules/user/src/UserStorageSchema.php
index 5e590b895..6010687da 100644
--- a/core/modules/user/src/UserStorageSchema.php
+++ b/core/modules/user/src/UserStorageSchema.php
@@ -17,9 +17,11 @@ class UserStorageSchema extends SqlContentEntityStorageSchema {
protected function getEntitySchema(ContentEntityTypeInterface $entity_type, $reset = FALSE) {
$schema = parent::getEntitySchema($entity_type, $reset);
- $schema['users_field_data']['unique keys'] += [
- 'user__name' => ['name', 'langcode'],
- ];
+ if ($data_table = $this->storage->getDataTable()) {
+ $schema[$data_table]['unique keys'] += [
+ 'user__name' => ['name', 'langcode'],
+ ];
+ }
return $schema;
}
diff --git a/core/modules/user/src/UserViewsData.php b/core/modules/user/src/UserViewsData.php
index 854f6791e..955ab5967 100644
--- a/core/modules/user/src/UserViewsData.php
+++ b/core/modules/user/src/UserViewsData.php
@@ -60,7 +60,7 @@ class UserViewsData extends EntityViewsData {
'argument field' => 'uid',
'base' => 'node_field_data',
'field' => 'nid',
- 'relationship' => 'node_field_data:uid'
+ 'relationship' => 'node_field_data:uid',
],
];
diff --git a/core/modules/user/tests/fixtures/update/drupal-8.user-email-token-2587275.php b/core/modules/user/tests/fixtures/update/drupal-8.user-email-token-2587275.php
index eaa2a9a61..8f19b6e17 100644
--- a/core/modules/user/tests/fixtures/update/drupal-8.user-email-token-2587275.php
+++ b/core/modules/user/tests/fixtures/update/drupal-8.user-email-token-2587275.php
@@ -18,5 +18,5 @@ $connection->insert('config')
->values([
'collection' => '',
'name' => 'user.mail',
- 'data' => "a:10:{s:14:\"cancel_confirm\";a:2:{s:4:\"body\";s:369:\"[user:name],\n\nA request to cancel your account has been made at [site:name].\n\nYou may now cancel your account on [site:url-brief] by clicking this link or copying and pasting it into your browser:\n\n[user:cancel-url]\n\nNOTE: The cancellation of your account is not reversible.\n\nThis link expires in one day and nothing will happen if it is not used.\n\n-- [site:name] team\";s:7:\"subject\";s:59:\"Account cancellation request for [user:name] at [site:name]\";}s:14:\"password_reset\";a:2:{s:4:\"body\";s:397:\"[user:name],\n\nA request to reset the password for your account has been made at [site:name].\n\nYou may now log in by clicking this link or copying and pasting it to your browser:\n\n[user:one-time-login-url]\n\nThis link can only be used once to log in and will lead you to a page where you can set your password. It expires after one day and nothing will happen if it's not used.\n\n-- [site:name] team\";s:7:\"subject\";s:60:\"Replacement login information for [user:name] at [site:name]\";}s:22:\"register_admin_created\";a:2:{s:4:\"body\";s:463:\"[user:name],\n\nA site administrator at [site:name] has created an account for you. You may now log in by clicking this link or copying and pasting it to your browser:\n\n[user:one-time-login-url]\n\nThis link can only be used once to log in and will lead you to a page where you can set your password.\n\nAfter setting your password, you will be able to log in at [site:login-url] in the future using:\n\nusername: [user:name]\npassword: Your password\n\n-- [site:name] team\";s:7:\"subject\";s:58:\"An administrator created an account for you at [site:name]\";}s:29:\"register_no_approval_required\";a:2:{s:4:\"body\";s:437:\"[user:name],\n\nThank you for registering at [site:name]. You may now log in by clicking this link or copying and pasting it to your browser:\n\n[user:one-time-login-url]\n\nThis link can only be used once to log in and will lead you to a page where you can set your password.\n\nAfter setting your password, you will be able to log in at [site:login-url] in the future using:\n\nusername: [user:name]\npassword: Your password\n\n-- [site:name] team\";s:7:\"subject\";s:46:\"Account details for [user:name] at [site:name]\";}s:25:\"register_pending_approval\";a:2:{s:4:\"body\";s:281:\"[user:name],\n\nThank you for registering at [site:name]. Your application for an account is currently pending approval. Once it has been approved, you will receive another email containing information about how to log in, set your password, and other details.\n\n\n-- [site:name] team\";s:7:\"subject\";s:71:\"Account details for [user:name] at [site:name] (pending admin approval)\";}s:31:\"register_pending_approval_admin\";a:2:{s:4:\"body\";s:56:\"[user:name] has applied for an account.\n\n[user:edit-url]\";s:7:\"subject\";s:71:\"Account details for [user:name] at [site:name] (pending admin approval)\";}s:16:\"status_activated\";a:2:{s:4:\"body\";s:446:\"[user:name],\n\nYour account at [site:name] has been activated.\n\nYou may now log in by clicking this link or copying and pasting it into your browser:\n\n[user:one-time-login-url]\n\nThis link can only be used once to log in and will lead you to a page where you can set your password.\n\nAfter setting your password, you will be able to log in at [site:login-url] in the future using:\n\nusername: [user:name]\npassword: Your password\n\n-- [site:name] team\";s:7:\"subject\";s:57:\"Account details for [user:name] at [site:name] (approved)\";}s:14:\"status_blocked\";a:2:{s:4:\"body\";s:89:\"[user:name],\n\nYour account on [site:account-name] has been blocked.\n\n-- [site:name] team\";s:7:\"subject\";s:56:\"Account details for [user:name] at [site:name] (blocked)\";}s:15:\"status_canceled\";a:2:{s:4:\"body\";s:82:\"[user:name],\n\nYour account on [site:name] has been canceled.\n\n-- [site:name] team\";s:7:\"subject\";s:57:\"Account details for [user:name] at [site:name] (canceled)\";}s:8:\"langcode\";s:2:\"en\";}"
+ 'data' => "a:10:{s:14:\"cancel_confirm\";a:2:{s:4:\"body\";s:369:\"[user:name],\n\nA request to cancel your account has been made at [site:name].\n\nYou may now cancel your account on [site:url-brief] by clicking this link or copying and pasting it into your browser:\n\n[user:cancel-url]\n\nNOTE: The cancellation of your account is not reversible.\n\nThis link expires in one day and nothing will happen if it is not used.\n\n-- [site:name] team\";s:7:\"subject\";s:59:\"Account cancellation request for [user:name] at [site:name]\";}s:14:\"password_reset\";a:2:{s:4:\"body\";s:397:\"[user:name],\n\nA request to reset the password for your account has been made at [site:name].\n\nYou may now log in by clicking this link or copying and pasting it to your browser:\n\n[user:one-time-login-url]\n\nThis link can only be used once to log in and will lead you to a page where you can set your password. It expires after one day and nothing will happen if it's not used.\n\n-- [site:name] team\";s:7:\"subject\";s:60:\"Replacement login information for [user:name] at [site:name]\";}s:22:\"register_admin_created\";a:2:{s:4:\"body\";s:463:\"[user:name],\n\nA site administrator at [site:name] has created an account for you. You may now log in by clicking this link or copying and pasting it to your browser:\n\n[user:one-time-login-url]\n\nThis link can only be used once to log in and will lead you to a page where you can set your password.\n\nAfter setting your password, you will be able to log in at [site:login-url] in the future using:\n\nusername: [user:name]\npassword: Your password\n\n-- [site:name] team\";s:7:\"subject\";s:58:\"An administrator created an account for you at [site:name]\";}s:29:\"register_no_approval_required\";a:2:{s:4:\"body\";s:437:\"[user:name],\n\nThank you for registering at [site:name]. You may now log in by clicking this link or copying and pasting it to your browser:\n\n[user:one-time-login-url]\n\nThis link can only be used once to log in and will lead you to a page where you can set your password.\n\nAfter setting your password, you will be able to log in at [site:login-url] in the future using:\n\nusername: [user:name]\npassword: Your password\n\n-- [site:name] team\";s:7:\"subject\";s:46:\"Account details for [user:name] at [site:name]\";}s:25:\"register_pending_approval\";a:2:{s:4:\"body\";s:281:\"[user:name],\n\nThank you for registering at [site:name]. Your application for an account is currently pending approval. Once it has been approved, you will receive another email containing information about how to log in, set your password, and other details.\n\n\n-- [site:name] team\";s:7:\"subject\";s:71:\"Account details for [user:name] at [site:name] (pending admin approval)\";}s:31:\"register_pending_approval_admin\";a:2:{s:4:\"body\";s:56:\"[user:name] has applied for an account.\n\n[user:edit-url]\";s:7:\"subject\";s:71:\"Account details for [user:name] at [site:name] (pending admin approval)\";}s:16:\"status_activated\";a:2:{s:4:\"body\";s:446:\"[user:name],\n\nYour account at [site:name] has been activated.\n\nYou may now log in by clicking this link or copying and pasting it into your browser:\n\n[user:one-time-login-url]\n\nThis link can only be used once to log in and will lead you to a page where you can set your password.\n\nAfter setting your password, you will be able to log in at [site:login-url] in the future using:\n\nusername: [user:name]\npassword: Your password\n\n-- [site:name] team\";s:7:\"subject\";s:57:\"Account details for [user:name] at [site:name] (approved)\";}s:14:\"status_blocked\";a:2:{s:4:\"body\";s:89:\"[user:name],\n\nYour account on [site:account-name] has been blocked.\n\n-- [site:name] team\";s:7:\"subject\";s:56:\"Account details for [user:name] at [site:name] (blocked)\";}s:15:\"status_canceled\";a:2:{s:4:\"body\";s:82:\"[user:name],\n\nYour account on [site:name] has been canceled.\n\n-- [site:name] team\";s:7:\"subject\";s:57:\"Account details for [user:name] at [site:name] (canceled)\";}s:8:\"langcode\";s:2:\"en\";}",
])->execute();
diff --git a/core/modules/user/tests/modules/user_access_test/user_access_test.module b/core/modules/user/tests/modules/user_access_test/user_access_test.module
index 470a76a2d..ff9f0b2db 100644
--- a/core/modules/user/tests/modules/user_access_test/user_access_test.module
+++ b/core/modules/user/tests/modules/user_access_test/user_access_test.module
@@ -6,6 +6,9 @@
*/
use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Core\Session\AccountInterface;
use Drupal\user\Entity\User;
/**
@@ -22,3 +25,16 @@ function user_access_test_user_access(User $entity, $operation, $account) {
}
return AccessResult::neutral();
}
+
+/**
+ * Implements hook_entity_field_access().
+ */
+function user_access_test_entity_field_access($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, FieldItemListInterface $items = NULL) {
+ // Account with role sub-admin can view the status, init and mail fields for user with no roles.
+ if ($operation === 'view' && in_array($field_definition->getName(), ['status', 'init', 'mail'])) {
+ if (($items == NULL) || (count($items->getEntity()->getRoles()) == 1)) {
+ return AccessResult::allowedIfHasPermission($account, 'sub-admin');
+ }
+ }
+ return AccessResult::neutral();
+}
diff --git a/core/modules/user/tests/modules/user_access_test/user_access_test.permissions.yml b/core/modules/user/tests/modules/user_access_test/user_access_test.permissions.yml
new file mode 100644
index 000000000..dadf7ba30
--- /dev/null
+++ b/core/modules/user/tests/modules/user_access_test/user_access_test.permissions.yml
@@ -0,0 +1,2 @@
+sub-admin:
+ title: 'Administer users with no roles'
diff --git a/core/modules/user/tests/modules/user_form_test/user_form_test.routing.yml b/core/modules/user/tests/modules/user_form_test/user_form_test.routing.yml
index 9b7f58a21..9a3a59e56 100644
--- a/core/modules/user/tests/modules/user_form_test/user_form_test.routing.yml
+++ b/core/modules/user/tests/modules/user_form_test/user_form_test.routing.yml
@@ -4,4 +4,3 @@ user_form_test.cancel:
_entity_form: 'user.cancel'
requirements:
_permission: 'cancel other accounts'
-
diff --git a/core/modules/user/tests/modules/user_hooks_test/user_hooks_test.module b/core/modules/user/tests/modules/user_hooks_test/user_hooks_test.module
index 9baf8ab4d..cafaf908a 100644
--- a/core/modules/user/tests/modules/user_hooks_test/user_hooks_test.module
+++ b/core/modules/user/tests/modules/user_hooks_test/user_hooks_test.module
@@ -5,7 +5,7 @@
* Support module for user hooks testing.
*/
-use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Component\Render\FormattableMarkup;
/**
* Implements hook_user_format_name_alter().
@@ -13,7 +13,7 @@ use Drupal\Component\Utility\SafeMarkup;
function user_hooks_test_user_format_name_alter(&$name, $account) {
if (\Drupal::state()->get('user_hooks_test_user_format_name_alter', FALSE)) {
if (\Drupal::state()->get('user_hooks_test_user_format_name_alter_safe', FALSE)) {
- $name = SafeMarkup::format('@uid', ['@uid' => $account->id()]);
+ $name = new FormattableMarkup('@uid', ['@uid' => $account->id()]);
}
else {
$name = '' . $account->id() . '';
diff --git a/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_user_fields_access.yml b/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_user_fields_access.yml
new file mode 100644
index 000000000..5bad08f09
--- /dev/null
+++ b/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_user_fields_access.yml
@@ -0,0 +1,478 @@
+langcode: en
+status: true
+dependencies:
+ module:
+ - user
+id: test_user_fields_access
+label: ''
+module: views
+description: ''
+tag: ''
+base_table: users_field_data
+base_field: uid
+core: 8.x
+display:
+ default:
+ display_plugin: default
+ id: default
+ display_title: Master
+ position: 0
+ display_options:
+ access:
+ type: none
+ options: { }
+ cache:
+ type: tag
+ options: { }
+ query:
+ type: views_query
+ options:
+ disable_sql_rewrite: false
+ distinct: false
+ replica: false
+ query_comment: ''
+ query_tags: { }
+ exposed_form:
+ type: basic
+ options:
+ submit_button: Apply
+ reset_button: false
+ reset_button_label: Reset
+ exposed_sorts_label: 'Sort by'
+ expose_sort_order: true
+ sort_asc_label: Asc
+ sort_desc_label: Desc
+ pager:
+ type: mini
+ options:
+ items_per_page: 10
+ offset: 0
+ id: 0
+ total_pages: null
+ expose:
+ items_per_page: false
+ items_per_page_label: 'Items per page'
+ items_per_page_options: '5, 10, 25, 50'
+ items_per_page_options_all: false
+ items_per_page_options_all_label: '- All -'
+ offset: false
+ offset_label: Offset
+ tags:
+ previous: ‹‹
+ next: ››
+ style:
+ type: table
+ options:
+ grouping: { }
+ row_class: ''
+ default_row_class: true
+ override: true
+ sticky: false
+ caption: ''
+ summary: ''
+ description: ''
+ columns:
+ name: name
+ status: status
+ mail: mail
+ init: init
+ created: created
+ info:
+ name:
+ sortable: false
+ default_sort_order: asc
+ align: ''
+ separator: ''
+ empty_column: false
+ responsive: ''
+ status:
+ sortable: false
+ default_sort_order: asc
+ align: ''
+ separator: ''
+ empty_column: false
+ responsive: ''
+ mail:
+ sortable: false
+ default_sort_order: asc
+ align: ''
+ separator: ''
+ empty_column: false
+ responsive: ''
+ init:
+ sortable: false
+ default_sort_order: asc
+ align: ''
+ separator: ''
+ empty_column: false
+ responsive: ''
+ created:
+ sortable: false
+ default_sort_order: asc
+ align: ''
+ separator: ''
+ empty_column: false
+ responsive: ''
+ default: '-1'
+ empty_table: false
+ row:
+ type: fields
+ fields:
+ name:
+ id: name
+ table: users_field_data
+ field: name
+ relationship: none
+ group_type: group
+ admin_label: ''
+ label: Name
+ exclude: false
+ alter:
+ alter_text: false
+ text: ''
+ make_link: false
+ path: ''
+ absolute: false
+ external: false
+ replace_spaces: false
+ path_case: none
+ trim_whitespace: false
+ alt: ''
+ rel: ''
+ link_class: ''
+ prefix: ''
+ suffix: ''
+ target: ''
+ nl2br: false
+ max_length: 0
+ word_boundary: false
+ ellipsis: false
+ more_link: false
+ more_link_text: ''
+ more_link_path: ''
+ strip_tags: false
+ trim: false
+ preserve_tags: ''
+ html: false
+ element_type: ''
+ element_class: ''
+ element_label_type: ''
+ element_label_class: ''
+ element_label_colon: true
+ element_wrapper_type: ''
+ element_wrapper_class: ''
+ element_default_classes: true
+ empty: ''
+ hide_empty: false
+ empty_zero: false
+ hide_alter_empty: true
+ click_sort_column: value
+ type: user_name
+ settings:
+ link_to_entity: true
+ group_column: value
+ group_columns: { }
+ group_rows: true
+ delta_limit: 0
+ delta_offset: 0
+ delta_reversed: false
+ delta_first_last: false
+ multi_type: separator
+ separator: ', '
+ field_api_classes: false
+ entity_type: user
+ entity_field: name
+ plugin_id: field
+ status:
+ id: status
+ table: users_field_data
+ field: status
+ relationship: none
+ group_type: group
+ admin_label: ''
+ label: Status
+ exclude: false
+ alter:
+ alter_text: false
+ text: ''
+ make_link: false
+ path: ''
+ absolute: false
+ external: false
+ replace_spaces: false
+ path_case: none
+ trim_whitespace: false
+ alt: ''
+ rel: ''
+ link_class: ''
+ prefix: ''
+ suffix: ''
+ target: ''
+ nl2br: false
+ max_length: 0
+ word_boundary: true
+ ellipsis: true
+ more_link: false
+ more_link_text: ''
+ more_link_path: ''
+ strip_tags: false
+ trim: false
+ preserve_tags: ''
+ html: false
+ element_type: ''
+ element_class: ''
+ element_label_type: ''
+ element_label_class: ''
+ element_label_colon: true
+ element_wrapper_type: ''
+ element_wrapper_class: ''
+ element_default_classes: true
+ empty: ''
+ hide_empty: false
+ empty_zero: false
+ hide_alter_empty: true
+ click_sort_column: value
+ type: boolean
+ settings:
+ format: default
+ format_custom_true: ''
+ format_custom_false: ''
+ group_column: value
+ group_columns: { }
+ group_rows: true
+ delta_limit: 0
+ delta_offset: 0
+ delta_reversed: false
+ delta_first_last: false
+ multi_type: separator
+ separator: ', '
+ field_api_classes: false
+ entity_type: user
+ entity_field: status
+ plugin_id: field
+ mail:
+ id: mail
+ table: users_field_data
+ field: mail
+ relationship: none
+ group_type: group
+ admin_label: ''
+ label: Email
+ exclude: false
+ alter:
+ alter_text: false
+ text: ''
+ make_link: false
+ path: ''
+ absolute: false
+ external: false
+ replace_spaces: false
+ path_case: none
+ trim_whitespace: false
+ alt: ''
+ rel: ''
+ link_class: ''
+ prefix: ''
+ suffix: ''
+ target: ''
+ nl2br: false
+ max_length: 0
+ word_boundary: true
+ ellipsis: true
+ more_link: false
+ more_link_text: ''
+ more_link_path: ''
+ strip_tags: false
+ trim: false
+ preserve_tags: ''
+ html: false
+ element_type: ''
+ element_class: ''
+ element_label_type: ''
+ element_label_class: ''
+ element_label_colon: true
+ element_wrapper_type: ''
+ element_wrapper_class: ''
+ element_default_classes: true
+ empty: ''
+ hide_empty: false
+ empty_zero: false
+ hide_alter_empty: true
+ click_sort_column: value
+ type: basic_string
+ settings: { }
+ group_column: value
+ group_columns: { }
+ group_rows: true
+ delta_limit: 0
+ delta_offset: 0
+ delta_reversed: false
+ delta_first_last: false
+ multi_type: separator
+ separator: ', '
+ field_api_classes: false
+ entity_type: user
+ entity_field: mail
+ plugin_id: field
+ init:
+ id: init
+ table: users_field_data
+ field: init
+ relationship: none
+ group_type: group
+ admin_label: ''
+ label: Init
+ exclude: false
+ alter:
+ alter_text: false
+ text: ''
+ make_link: false
+ path: ''
+ absolute: false
+ external: false
+ replace_spaces: false
+ path_case: none
+ trim_whitespace: false
+ alt: ''
+ rel: ''
+ link_class: ''
+ prefix: ''
+ suffix: ''
+ target: ''
+ nl2br: false
+ max_length: 0
+ word_boundary: true
+ ellipsis: true
+ more_link: false
+ more_link_text: ''
+ more_link_path: ''
+ strip_tags: false
+ trim: false
+ preserve_tags: ''
+ html: false
+ element_type: ''
+ element_class: ''
+ element_label_type: ''
+ element_label_class: ''
+ element_label_colon: true
+ element_wrapper_type: ''
+ element_wrapper_class: ''
+ element_default_classes: true
+ empty: ''
+ hide_empty: false
+ empty_zero: false
+ hide_alter_empty: true
+ click_sort_column: value
+ type: basic_string
+ settings: { }
+ group_column: value
+ group_columns: { }
+ group_rows: true
+ delta_limit: 0
+ delta_offset: 0
+ delta_reversed: false
+ delta_first_last: false
+ multi_type: separator
+ separator: ', '
+ field_api_classes: false
+ entity_type: user
+ entity_field: init
+ plugin_id: field
+ created:
+ id: created
+ table: users_field_data
+ field: created
+ relationship: none
+ group_type: group
+ admin_label: ''
+ label: Created
+ exclude: false
+ alter:
+ alter_text: false
+ text: ''
+ make_link: false
+ path: ''
+ absolute: false
+ external: false
+ replace_spaces: false
+ path_case: none
+ trim_whitespace: false
+ alt: ''
+ rel: ''
+ link_class: ''
+ prefix: ''
+ suffix: ''
+ target: ''
+ nl2br: false
+ max_length: 0
+ word_boundary: true
+ ellipsis: true
+ more_link: false
+ more_link_text: ''
+ more_link_path: ''
+ strip_tags: false
+ trim: false
+ preserve_tags: ''
+ html: false
+ element_type: ''
+ element_class: ''
+ element_label_type: ''
+ element_label_class: ''
+ element_label_colon: true
+ element_wrapper_type: ''
+ element_wrapper_class: ''
+ element_default_classes: true
+ empty: ''
+ hide_empty: false
+ empty_zero: false
+ hide_alter_empty: true
+ click_sort_column: value
+ type: timestamp
+ settings:
+ date_format: medium
+ custom_date_format: ''
+ timezone: ''
+ group_column: value
+ group_columns: { }
+ group_rows: true
+ delta_limit: 0
+ delta_offset: 0
+ delta_reversed: false
+ delta_first_last: false
+ multi_type: separator
+ separator: ', '
+ field_api_classes: false
+ entity_type: user
+ entity_field: created
+ plugin_id: field
+ filters: { }
+ sorts: { }
+ title: ''
+ header: { }
+ footer: { }
+ empty: { }
+ relationships: { }
+ arguments: { }
+ display_extenders: { }
+ cache_metadata:
+ max-age: 0
+ contexts:
+ - 'languages:language_content'
+ - 'languages:language_interface'
+ - url.query_args
+ tags: { }
+ page_1:
+ display_plugin: page
+ id: page_1
+ display_title: Page
+ position: 1
+ display_options:
+ display_extenders: { }
+ path: test_user_fields_access
+ cache_metadata:
+ max-age: 0
+ contexts:
+ - 'languages:language_content'
+ - 'languages:language_interface'
+ - url.query_args
+ tags: { }
diff --git a/core/modules/user/tests/modules/user_test_views/user_test_views.info.yml b/core/modules/user/tests/modules/user_test_views/user_test_views.info.yml
index 2554769a3..b89ea6c0a 100644
--- a/core/modules/user/tests/modules/user_test_views/user_test_views.info.yml
+++ b/core/modules/user/tests/modules/user_test_views/user_test_views.info.yml
@@ -5,5 +5,5 @@ package: Testing
version: VERSION
core: 8.x
dependencies:
- - user
- - views
+ - drupal:user
+ - drupal:views
diff --git a/core/modules/hal/tests/src/Functional/EntityResource/Role/RoleHalJsonAnonTest.php b/core/modules/user/tests/src/Functional/Hal/RoleHalJsonAnonTest.php
similarity index 75%
rename from core/modules/hal/tests/src/Functional/EntityResource/Role/RoleHalJsonAnonTest.php
rename to core/modules/user/tests/src/Functional/Hal/RoleHalJsonAnonTest.php
index ca39204db..a08a1d094 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/Role/RoleHalJsonAnonTest.php
+++ b/core/modules/user/tests/src/Functional/Hal/RoleHalJsonAnonTest.php
@@ -1,9 +1,9 @@
grantPermissionsToTestedRole(['administer permissions']);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function createEntity() {
+ $role = Role::create([
+ 'id' => 'llama',
+ 'name' => $this->randomString(),
+ ]);
+ $role->save();
+
+ return $role;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedNormalizedEntity() {
+ return [
+ 'uuid' => $this->entity->uuid(),
+ 'weight' => 2,
+ 'langcode' => 'en',
+ 'status' => TRUE,
+ 'dependencies' => [],
+ 'id' => 'llama',
+ 'label' => NULL,
+ 'is_admin' => NULL,
+ 'permissions' => [],
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getNormalizedPostEntity() {
+ // @todo Update in https://www.drupal.org/node/2300677.
+ }
+
+}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Role/RoleXmlAnonTest.php b/core/modules/user/tests/src/Functional/Rest/RoleXmlAnonTest.php
similarity index 88%
rename from core/modules/rest/tests/src/Functional/EntityResource/Role/RoleXmlAnonTest.php
rename to core/modules/user/tests/src/Functional/Rest/RoleXmlAnonTest.php
index e780bee78..14910a620 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/Role/RoleXmlAnonTest.php
+++ b/core/modules/user/tests/src/Functional/Rest/RoleXmlAnonTest.php
@@ -1,6 +1,6 @@
NULL,
+ ];
+
+ /**
+ * @var \Drupal\user\UserInterface
+ */
+ protected $entity;
+
+ /**
+ * {@inheritdoc}
+ */
+ protected static $labelFieldName = 'name';
+
+ /**
+ * {@inheritdoc}
+ */
+ protected static $firstCreatedEntityId = 4;
+
+ /**
+ * {@inheritdoc}
+ */
+ protected static $secondCreatedEntityId = 5;
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUpAuthorization($method) {
+ switch ($method) {
+ case 'GET':
+ $this->grantPermissionsToTestedRole(['access user profiles']);
+ break;
+ case 'POST':
+ case 'PATCH':
+ case 'DELETE':
+ $this->grantPermissionsToTestedRole(['administer users']);
+ break;
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function createEntity() {
+ // Create a "Llama" user.
+ $user = User::create(['created' => 123456789]);
+ $user->setUsername('Llama')
+ ->setChangedTime(123456789)
+ ->activate()
+ ->save();
+
+ return $user;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function createAnotherEntity() {
+ /** @var \Drupal\user\UserInterface $user */
+ $user = $this->entity->createDuplicate();
+ $user->setUsername($user->label() . '_dupe');
+ $user->save();
+ return $user;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedNormalizedEntity() {
+ return [
+ 'uid' => [
+ ['value' => 3],
+ ],
+ 'uuid' => [
+ ['value' => $this->entity->uuid()],
+ ],
+ 'langcode' => [
+ [
+ 'value' => 'en',
+ ],
+ ],
+ 'name' => [
+ [
+ 'value' => 'Llama',
+ ],
+ ],
+ 'created' => [
+ $this->formatExpectedTimestampItemValues(123456789),
+ ],
+ 'changed' => [
+ $this->formatExpectedTimestampItemValues($this->entity->getChangedTime()),
+ ],
+ 'default_langcode' => [
+ [
+ 'value' => TRUE,
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getNormalizedPostEntity() {
+ return [
+ 'name' => [
+ [
+ 'value' => 'Dramallama',
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * Tests PATCHing security-sensitive base fields of the logged in account.
+ */
+ public function testPatchDxForSecuritySensitiveBaseFields() {
+ // The anonymous user is never allowed to modify itself.
+ if (!static::$auth) {
+ $this->markTestSkipped();
+ }
+
+ $this->initAuthentication();
+ $this->provisionEntityResource();
+
+ /** @var \Drupal\user\UserInterface $user */
+ $user = static::$auth ? $this->account : User::load(0);
+ // @todo Remove the array_diff_key() call in https://www.drupal.org/node/2821077.
+ $original_normalization = array_diff_key($this->serializer->normalize($user, static::$format), ['created' => TRUE, 'changed' => TRUE, 'name' => TRUE]);
+
+ // Since this test must be performed by the user that is being modified,
+ // we cannot use $this->getUrl().
+ $url = $user->toUrl()->setOption('query', ['_format' => static::$format]);
+ $request_options = [
+ RequestOptions::HEADERS => ['Content-Type' => static::$mimeType],
+ ];
+ $request_options = array_merge_recursive($request_options, $this->getAuthenticationRequestOptions('PATCH'));
+
+ // Test case 1: changing email.
+ $normalization = $original_normalization;
+ $normalization['mail'] = [['value' => 'new-email@example.com']];
+ $request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
+
+ // DX: 422 when changing email without providing the password.
+ $response = $this->request('PATCH', $url, $request_options);
+ $this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nmail: Your current password is missing or incorrect; it's required to change the Email.\n", $response, FALSE, FALSE, FALSE, FALSE);
+
+ $normalization['pass'] = [['existing' => 'wrong']];
+ $request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
+
+ // DX: 422 when changing email while providing a wrong password.
+ $response = $this->request('PATCH', $url, $request_options);
+ $this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nmail: Your current password is missing or incorrect; it's required to change the Email.\n", $response, FALSE, FALSE, FALSE, FALSE);
+
+ $normalization['pass'] = [['existing' => $this->account->passRaw]];
+ $request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
+
+ // 200 for well-formed request.
+ $response = $this->request('PATCH', $url, $request_options);
+ $this->assertResourceResponse(200, FALSE, $response);
+
+ // Test case 2: changing password.
+ $normalization = $original_normalization;
+ $new_password = $this->randomString();
+ $normalization['pass'] = [['value' => $new_password]];
+ $request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
+
+ // DX: 422 when changing password without providing the current password.
+ $response = $this->request('PATCH', $url, $request_options);
+ $this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\npass: Your current password is missing or incorrect; it's required to change the Password.\n", $response, FALSE, FALSE, FALSE, FALSE);
+
+ $normalization['pass'][0]['existing'] = $this->account->pass_raw;
+ $request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
+
+ // 200 for well-formed request.
+ $response = $this->request('PATCH', $url, $request_options);
+ $this->assertResourceResponse(200, FALSE, $response);
+
+ // Verify that we can log in with the new password.
+ $this->assertRpcLogin($user->getAccountName(), $new_password);
+
+ // Update password in $this->account, prepare for future requests.
+ $this->account->passRaw = $new_password;
+ $this->initAuthentication();
+ $request_options = [
+ RequestOptions::HEADERS => ['Content-Type' => static::$mimeType],
+ ];
+ $request_options = array_merge_recursive($request_options, $this->getAuthenticationRequestOptions('PATCH'));
+
+ // Test case 3: changing name.
+ $normalization = $original_normalization;
+ $normalization['name'] = [['value' => 'Cooler Llama']];
+ $request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
+
+ // DX: 403 when modifying username without required permission.
+ $response = $this->request('PATCH', $url, $request_options);
+ $this->assertResourceErrorResponse(403, "Access denied on updating field 'name'.", $response);
+
+ $this->grantPermissionsToTestedRole(['change own username']);
+
+ // 200 for well-formed request.
+ $response = $this->request('PATCH', $url, $request_options);
+ $this->assertResourceResponse(200, FALSE, $response);
+
+ // Verify that we can log in with the new username.
+ $this->assertRpcLogin('Cooler Llama', $new_password);
+ }
+
+ /**
+ * Verifies that logging in with the given username and password works.
+ *
+ * @param string $username
+ * The username to log in with.
+ * @param string $password
+ * The password to log in with.
+ */
+ protected function assertRpcLogin($username, $password) {
+ $request_body = [
+ 'name' => $username,
+ 'pass' => $password,
+ ];
+ $request_options = [
+ RequestOptions::HEADERS => [],
+ RequestOptions::BODY => $this->serializer->encode($request_body, 'json'),
+ ];
+ $response = $this->request('POST', Url::fromRoute('user.login.http')->setRouteParameter('_format', 'json'), $request_options);
+ $this->assertSame(200, $response->getStatusCode());
+ }
+
+ /**
+ * Tests PATCHing security-sensitive base fields to change other users.
+ */
+ public function testPatchSecurityOtherUser() {
+ // The anonymous user is never allowed to modify other users.
+ if (!static::$auth) {
+ $this->markTestSkipped();
+ }
+
+ $this->initAuthentication();
+ $this->provisionEntityResource();
+
+ /** @var \Drupal\user\UserInterface $user */
+ $user = $this->account;
+ $original_normalization = array_diff_key($this->serializer->normalize($user, static::$format), ['changed' => TRUE]);
+
+ // Since this test must be performed by the user that is being modified,
+ // we cannot use $this->getUrl().
+ $url = $user->toUrl()->setOption('query', ['_format' => static::$format]);
+ $request_options = [
+ RequestOptions::HEADERS => ['Content-Type' => static::$mimeType],
+ ];
+ $request_options = array_merge_recursive($request_options, $this->getAuthenticationRequestOptions('PATCH'));
+
+ $normalization = $original_normalization;
+ $normalization['mail'] = [['value' => 'new-email@example.com']];
+ $request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
+
+ // Try changing user 1's email.
+ $user1 = [
+ 'mail' => [['value' => 'another_email_address@example.com']],
+ 'uid' => [['value' => 1]],
+ 'name' => [['value' => 'another_user_name']],
+ 'pass' => [['existing' => $this->account->passRaw]],
+ 'uuid' => [['value' => '2e9403a4-d8af-4096-a116-624710140be0']],
+ ] + $original_normalization;
+ $request_options[RequestOptions::BODY] = $this->serializer->encode($user1, static::$format);
+ $response = $this->request('PATCH', $url, $request_options);
+ // Ensure the email address has not changed.
+ $this->assertEquals('admin@example.com', $this->entityStorage->loadUnchanged(1)->getEmail());
+ $this->assertResourceErrorResponse(403, "Access denied on updating field 'uid'. The entity ID cannot be changed.", $response);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedUnauthorizedAccessMessage($method) {
+ if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
+ return parent::getExpectedUnauthorizedAccessMessage($method);
+ }
+
+ switch ($method) {
+ case 'GET':
+ return "The 'access user profiles' permission is required and the user must be active.";
+ case 'PATCH':
+ return "You are not authorized to update this user entity.";
+ case 'DELETE':
+ return 'You are not authorized to delete this user entity.';
+ default:
+ return parent::getExpectedUnauthorizedAccessMessage($method);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedUnauthorizedAccessCacheability() {
+ // @see \Drupal\user\UserAccessControlHandler::checkAccess()
+ return parent::getExpectedUnauthorizedAccessCacheability()
+ ->addCacheTags(['user:3']);
+ }
+
+}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/User/UserXmlAnonTest.php b/core/modules/user/tests/src/Functional/Rest/UserXmlAnonTest.php
similarity index 91%
rename from core/modules/rest/tests/src/Functional/EntityResource/User/UserXmlAnonTest.php
rename to core/modules/user/tests/src/Functional/Rest/UserXmlAnonTest.php
index 8f1d4839a..4629192ce 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/User/UserXmlAnonTest.php
+++ b/core/modules/user/tests/src/Functional/Rest/UserXmlAnonTest.php
@@ -1,6 +1,6 @@
getPermissions();
+ $authenticated = \Drupal::config('user.role.authenticated');
+ $permissions = $authenticated->get('permissions');
sort($permissions);
- $this->assertNotIdentical($permissions, $authenticated->getPermissions());
+ $this->assertNotSame($permissions, $authenticated->get('permissions'));
$this->runUpdates();
- $authenticated = Role::load('authenticated');
- $this->assertIdentical($permissions, $authenticated->getPermissions());
+ $authenticated = \Drupal::config('user.role.authenticated');
+ $this->assertSame($permissions, $authenticated->get('permissions'));
}
}
diff --git a/core/modules/user/tests/src/Functional/UserAdminTest.php b/core/modules/user/tests/src/Functional/UserAdminTest.php
index 4e69346d2..82e5df807 100644
--- a/core/modules/user/tests/src/Functional/UserAdminTest.php
+++ b/core/modules/user/tests/src/Functional/UserAdminTest.php
@@ -108,7 +108,7 @@ class UserAdminTest extends BrowserTestBase {
$this->drupalPostForm('admin/people', $edit, t('Apply to selected items'), [
// Sort the table by username so that we know reliably which user will be
// targeted with the blocking action.
- 'query' => ['order' => 'name', 'sort' => 'asc']
+ 'query' => ['order' => 'name', 'sort' => 'asc'],
]);
$site_name = $this->config('system.site')->get('name');
$this->assertMailString('body', 'Your account on ' . $site_name . ' has been blocked.', 1, 'Blocked message found in the mail sent to user C.');
@@ -129,7 +129,7 @@ class UserAdminTest extends BrowserTestBase {
$this->drupalPostForm('admin/people', $editunblock, t('Apply to selected items'), [
// Sort the table by username so that we know reliably which user will be
// targeted with the blocking action.
- 'query' => ['order' => 'name', 'sort' => 'asc']
+ 'query' => ['order' => 'name', 'sort' => 'asc'],
]);
$user_storage->resetCache([$user_c->id()]);
$account = $user_storage->load($user_c->id());
diff --git a/core/modules/user/tests/src/Functional/UserCacheTagsTest.php b/core/modules/user/tests/src/Functional/UserCacheTagsTest.php
index a9e98575b..c9ab91fac 100644
--- a/core/modules/user/tests/src/Functional/UserCacheTagsTest.php
+++ b/core/modules/user/tests/src/Functional/UserCacheTagsTest.php
@@ -2,7 +2,7 @@
namespace Drupal\Tests\user\Functional;
-use Drupal\system\Tests\Entity\EntityWithUriCacheTagsTestBase;
+use Drupal\Tests\system\Functional\Entity\EntityWithUriCacheTagsTestBase;
use Drupal\user\Entity\Role;
use Drupal\user\Entity\User;
use Drupal\user\RoleInterface;
diff --git a/core/modules/user/tests/src/Functional/UserCancelTest.php b/core/modules/user/tests/src/Functional/UserCancelTest.php
index 9889cd65b..869cd37bd 100644
--- a/core/modules/user/tests/src/Functional/UserCancelTest.php
+++ b/core/modules/user/tests/src/Functional/UserCancelTest.php
@@ -99,23 +99,6 @@ class UserCancelTest extends BrowserTestBase {
\Drupal::service('module_installer')->install(['views']);
\Drupal::service('router.builder')->rebuild();
- // Update uid 1's name and password to we know it.
- $password = user_password();
- $account = [
- 'name' => 'user1',
- 'pass' => $this->container->get('password')->hash(trim($password)),
- ];
- // We cannot use $account->save() here, because this would result in the
- // password being hashed again.
- db_update('users_field_data')
- ->fields($account)
- ->condition('uid', 1)
- ->execute();
-
- // Reload and log in uid 1.
- $user_storage->resetCache([1]);
- $user1 = $user_storage->load(1);
- $user1->pass_raw = $password;
// Try to cancel uid 1's account with a different user.
$admin_user = $this->drupalCreateUser(['administer users']);
@@ -268,6 +251,12 @@ class UserCancelTest extends BrowserTestBase {
// Confirm account cancellation request.
$this->drupalGet("user/" . $account->id() . "/cancel/confirm/$timestamp/" . user_pass_rehash($account, $timestamp));
+ // Confirm that the user was redirected to the front page.
+ $this->assertSession()->addressEquals('');
+ $this->assertSession()->statusCodeEquals(200);
+ // Confirm that the confirmation message made it through to the end user.
+ $this->assertRaw(t('%name has been disabled.', ['%name' => $account->getUsername()]), "Confirmation message displayed to user.");
+
$user_storage->resetCache([$account->id()]);
$account = $user_storage->load($account->id());
$this->assertTrue($account->isBlocked(), 'User has been blocked.');
@@ -283,9 +272,6 @@ class UserCancelTest extends BrowserTestBase {
$storage->resetCache([$comment->id()]);
$comment = $storage->load($comment->id());
$this->assertFalse($comment->isPublished(), 'Comment of the user has been unpublished.');
-
- // Confirm that the confirmation message made it through to the end user.
- $this->assertRaw(t('%name has been disabled.', ['%name' => $account->getUsername()]), "Confirmation message displayed to user.");
}
/**
diff --git a/core/modules/user/tests/src/Functional/UserEditTest.php b/core/modules/user/tests/src/Functional/UserEditTest.php
index 0f75ff8dc..5cdbc19f0 100644
--- a/core/modules/user/tests/src/Functional/UserEditTest.php
+++ b/core/modules/user/tests/src/Functional/UserEditTest.php
@@ -2,6 +2,7 @@
namespace Drupal\Tests\user\Functional;
+use Drupal\Core\Cache\Cache;
use Drupal\Tests\BrowserTestBase;
/**
@@ -29,9 +30,15 @@ class UserEditTest extends BrowserTestBase {
// is the raw value and not a formatted one.
\Drupal::state()->set('user_hooks_test_user_format_name_alter', TRUE);
\Drupal::service('module_installer')->install(['user_hooks_test']);
+ Cache::invalidateTags(['rendered']);
$this->drupalGet('user/' . $user1->id() . '/edit');
$this->assertFieldByName('name', $user1->getAccountName());
+ // Ensure the formatted name is displayed when expected.
+ $this->drupalGet('user/' . $user1->id());
+ $this->assertSession()->responseContains($user1->getDisplayName());
+ $this->assertSession()->titleEquals(strip_tags($user1->getDisplayName()) . ' | Drupal');
+
// Check that filling out a single password field does not validate.
$edit = [];
$edit['pass[pass1]'] = '';
diff --git a/core/modules/user/tests/src/Functional/UserMailNotifyTest.php b/core/modules/user/tests/src/Functional/UserMailNotifyTest.php
index 57bebc042..df22f9cb9 100644
--- a/core/modules/user/tests/src/Functional/UserMailNotifyTest.php
+++ b/core/modules/user/tests/src/Functional/UserMailNotifyTest.php
@@ -30,7 +30,7 @@ class UserMailNotifyTest extends EntityKernelTestBase {
['status_canceled', ['status_canceled']],
['register_admin_created', ['register_admin_created']],
['register_no_approval_required', ['register_no_approval_required']],
- ['register_pending_approval', ['register_pending_approval', 'register_pending_approval_admin']]
+ ['register_pending_approval', ['register_pending_approval', 'register_pending_approval_admin']],
];
}
diff --git a/core/modules/user/tests/src/Functional/UserTranslationUITest.php b/core/modules/user/tests/src/Functional/UserTranslationUITest.php
index ae541da83..630e58586 100644
--- a/core/modules/user/tests/src/Functional/UserTranslationUITest.php
+++ b/core/modules/user/tests/src/Functional/UserTranslationUITest.php
@@ -75,4 +75,25 @@ class UserTranslationUITest extends ContentTranslationUITestBase {
}
}
+ /**
+ * Test translated user deletion.
+ */
+ public function testTranslatedUserDeletion() {
+ $this->drupalLogin($this->administrator);
+ $entity_id = $this->createEntity($this->getNewEntityValues('en'), 'en');
+
+ $entity = $this->container->get('entity_type.manager')
+ ->getStorage($this->entityTypeId)
+ ->load($entity_id);
+ $translated_entity = $entity->addTranslation('fr');
+ $translated_entity->save();
+
+ $url = $entity->toUrl(
+ 'edit-form',
+ ['language' => $this->container->get('language_manager')->getLanguage('en')]
+ );
+ $this->drupalPostForm($url, [], t('Cancel account'));
+ $this->assertSession()->statusCodeEquals(200);
+ }
+
}
diff --git a/core/modules/user/tests/src/Functional/Views/BulkFormAccessTest.php b/core/modules/user/tests/src/Functional/Views/BulkFormAccessTest.php
index 084015eec..5f613f3b1 100644
--- a/core/modules/user/tests/src/Functional/Views/BulkFormAccessTest.php
+++ b/core/modules/user/tests/src/Functional/Views/BulkFormAccessTest.php
@@ -2,7 +2,7 @@
namespace Drupal\Tests\user\Functional\Views;
-use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Component\Render\FormattableMarkup;
use Drupal\user\Entity\User;
/**
@@ -54,7 +54,7 @@ class BulkFormAccessTest extends UserTestBase {
$this->drupalPostForm('test-user-bulk-form', $edit, t('Apply to selected items'));
$this->assertResponse(200);
- $this->assertRaw(SafeMarkup::format('No access to execute %action on the @entity_type_label %entity_label.', [
+ $this->assertRaw(new FormattableMarkup('No access to execute %action on the @entity_type_label %entity_label.', [
'%action' => 'Block the selected user(s)',
'@entity_type_label' => 'User',
'%entity_label' => $no_edit_user->label(),
diff --git a/core/modules/user/tests/src/Functional/Views/HandlerFilterUserNameTest.php b/core/modules/user/tests/src/Functional/Views/HandlerFilterUserNameTest.php
index c2b15ff58..a5d271092 100644
--- a/core/modules/user/tests/src/Functional/Views/HandlerFilterUserNameTest.php
+++ b/core/modules/user/tests/src/Functional/Views/HandlerFilterUserNameTest.php
@@ -66,7 +66,6 @@ class HandlerFilterUserNameTest extends ViewTestBase {
}
}
-
/**
* Tests just using the filter.
*/
@@ -96,7 +95,7 @@ class HandlerFilterUserNameTest extends ViewTestBase {
$users = [$this->randomMachineName()];
$users = array_map('strtolower', $users);
$edit = [
- 'options[value]' => implode(', ', $users)
+ 'options[value]' => implode(', ', $users),
];
$this->drupalPostForm($path, $edit, t('Apply'));
$this->assertRaw(t('There are no entities matching "%value".', ['%value' => implode(', ', $users)]));
@@ -106,7 +105,7 @@ class HandlerFilterUserNameTest extends ViewTestBase {
$users = [$random_name, $this->names[0]];
$users = array_map('strtolower', $users);
$edit = [
- 'options[value]' => implode(', ', $users)
+ 'options[value]' => implode(', ', $users),
];
$users = [$users[0]];
$this->drupalPostForm($path, $edit, t('Apply'));
@@ -116,7 +115,7 @@ class HandlerFilterUserNameTest extends ViewTestBase {
$users = $this->names;
$users = array_map('strtolower', $users);
$edit = [
- 'options[value]' => implode(', ', $users)
+ 'options[value]' => implode(', ', $users),
];
$this->drupalPostForm($path, $edit, t('Apply'));
$this->assertNoRaw(t('There are no entities matching "%value".', ['%value' => implode(', ', $users)]));
diff --git a/core/modules/user/tests/src/Functional/Views/UserFieldsAccessChangeTest.php b/core/modules/user/tests/src/Functional/Views/UserFieldsAccessChangeTest.php
new file mode 100644
index 000000000..1a238579c
--- /dev/null
+++ b/core/modules/user/tests/src/Functional/Views/UserFieldsAccessChangeTest.php
@@ -0,0 +1,53 @@
+drupalGet($path);
+
+ // User has access to name and created date by default.
+ $this->assertText(t('Name'));
+ $this->assertText(t('Created'));
+
+ // User does not by default have access to init, mail and status.
+ $this->assertNoText(t('Init'));
+ $this->assertNoText(t('Email'));
+ $this->assertNoText(t('Status'));
+
+ // Assign sub-admin role to grant extra access.
+ $user = $this->drupalCreateUser(['sub-admin']);
+ $this->drupalLogin($user);
+ $this->drupalGet($path);
+
+ // Access for init, mail and status is added in hook_entity_field_access().
+ $this->assertText(t('Init'));
+ $this->assertText(t('Email'));
+ $this->assertText(t('Status'));
+ }
+
+}
diff --git a/core/modules/user/tests/src/FunctionalJavascript/RegistrationWithUserFieldsTest.php b/core/modules/user/tests/src/FunctionalJavascript/RegistrationWithUserFieldsTest.php
new file mode 100644
index 000000000..29306cd4d
--- /dev/null
+++ b/core/modules/user/tests/src/FunctionalJavascript/RegistrationWithUserFieldsTest.php
@@ -0,0 +1,138 @@
+page = $this->getSession()->getPage();
+ $this->webAssert = $this->assertSession();
+ }
+
+ /**
+ * Tests Field API fields on user registration forms.
+ */
+ public function testRegistrationWithUserFields() {
+ // Create a field on 'user' entity type.
+ $field_storage = FieldStorageConfig::create([
+ 'field_name' => 'test_user_field',
+ 'entity_type' => 'user',
+ 'type' => 'test_field',
+ 'cardinality' => 1,
+ ]);
+ $field_storage->save();
+ $field = FieldConfig::create([
+ 'field_storage' => $field_storage,
+ 'label' => 'Some user field',
+ 'bundle' => 'user',
+ 'required' => TRUE,
+ ]);
+ $field->save();
+
+ entity_get_form_display('user', 'user', 'default')
+ ->setComponent('test_user_field', ['type' => 'test_field_widget'])
+ ->save();
+ $user_registration_form = entity_get_form_display('user', 'user', 'register');
+ $user_registration_form->save();
+
+ // Check that the field does not appear on the registration form.
+ $this->drupalGet('user/register');
+ $this->webAssert->pageTextNotContains($field->label());
+
+ // Have the field appear on the registration form.
+ $user_registration_form->setComponent('test_user_field', ['type' => 'test_field_widget'])->save();
+
+ $this->drupalGet('user/register');
+ $this->webAssert->pageTextContains($field->label());
+
+ // In order to check the server side validation the native browser
+ // validation for required fields needs to be circumvented.
+ $session = $this->getSession();
+ $session->executeScript("jQuery('#edit-test-user-field-0-value').prop('required', false);");
+
+ // Check that validation errors are correctly reported.
+ $name = $this->randomMachineName();
+ $this->page->fillField('edit-name', $name);
+ $this->page->fillField('edit-mail', $name . '@example.com');
+
+ $this->page->pressButton('edit-submit');
+ $this->webAssert->pageTextContains(t('@name field is required.', ['@name' => $field->label()]));
+
+ // Invalid input.
+ $this->page->fillField('edit-test-user-field-0-value', '-1');
+ $this->page->pressButton('edit-submit');
+ $this->webAssert->pageTextContains($field->label() . ' does not accept the value -1.');
+
+ // Submit with valid data.
+ $value = (string) mt_rand(1, 255);
+ $this->page->fillField('edit-test-user-field-0-value', $value);
+ $this->page->pressButton('edit-submit');
+ // Check user fields.
+ $accounts = $this->container->get('entity_type.manager')->getStorage('user')
+ ->loadByProperties(['name' => $name, 'mail' => $name . '@example.com']);
+ $new_user = reset($accounts);
+ $this->assertEquals($value, $new_user->test_user_field->value, 'The field value was correctly saved.');
+
+ // Check that the 'add more' button works.
+ $field_storage->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
+ $field_storage->save();
+ $name = $this->randomMachineName();
+ $this->drupalGet('user/register');
+ $this->page->fillField('edit-name', $name);
+ $this->page->fillField('edit-mail', $name . '@example.com');
+ $this->page->fillField('test_user_field[0][value]', $value);
+ // Add two inputs.
+ $this->page->pressButton('test_user_field_add_more');
+ $this->webAssert->waitForElement('css', 'input[name="test_user_field[1][value]"]');
+ $this->page->fillField('test_user_field[1][value]', $value . '1');
+ $this->page->pressButton('test_user_field_add_more');
+ $this->webAssert->waitForElement('css', 'input[name="test_user_field[2][value]"]');
+ $this->page->fillField('test_user_field[2][value]', $value . '2');
+
+ // Submit with three values.
+ $this->page->pressButton('edit-submit');
+
+ // Check user fields.
+ $accounts = $this->container->get('entity_type.manager')
+ ->getStorage('user')
+ ->loadByProperties(['name' => $name, 'mail' => $name . '@example.com']);
+ $new_user = reset($accounts);
+ $this->assertEquals($value, $new_user->test_user_field[0]->value, t('The field value was correctly saved.'));
+ $this->assertEquals($value . '1', $new_user->test_user_field[1]->value, t('The field value was correctly saved.'));
+ $this->assertEquals($value . '2', $new_user->test_user_field[2]->value, t('The field value was correctly saved.'));
+ }
+
+}
diff --git a/core/modules/user/tests/src/Kernel/Condition/UserRoleConditionTest.php b/core/modules/user/tests/src/Kernel/Condition/UserRoleConditionTest.php
index 075b04c5e..d6923faf9 100644
--- a/core/modules/user/tests/src/Kernel/Condition/UserRoleConditionTest.php
+++ b/core/modules/user/tests/src/Kernel/Condition/UserRoleConditionTest.php
@@ -2,7 +2,7 @@
namespace Drupal\Tests\user\Kernel\Condition;
-use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Component\Render\FormattableMarkup;
use Drupal\KernelTests\KernelTestBase;
use Drupal\user\Entity\Role;
use Drupal\user\Entity\User;
@@ -151,7 +151,7 @@ class UserRoleConditionTest extends KernelTestBase {
$condition->setConfig('roles', [$this->role->id() => $this->role->id()]);
$condition->setConfig('negate', FALSE);
$this->assertTrue($condition->execute(), 'Authenticated user is a member of the custom role.');
- $this->assertEqual($condition->summary(), SafeMarkup::format('The user is a member of @roles', ['@roles' => $this->role->label()]));
+ $this->assertEqual($condition->summary(), new FormattableMarkup('The user is a member of @roles', ['@roles' => $this->role->label()]));
}
}
diff --git a/core/modules/user/tests/src/Kernel/Migrate/MigrateUserProfileEntityDisplayTest.php b/core/modules/user/tests/src/Kernel/Migrate/MigrateUserProfileEntityDisplayTest.php
index a4a203685..ac7c181b5 100644
--- a/core/modules/user/tests/src/Kernel/Migrate/MigrateUserProfileEntityDisplayTest.php
+++ b/core/modules/user/tests/src/Kernel/Migrate/MigrateUserProfileEntityDisplayTest.php
@@ -47,6 +47,10 @@ class MigrateUserProfileEntityDisplayTest extends MigrateDrupal6TestBase {
// Test PROFILE_HIDDEN field is hidden.
$this->assertNull($display->getComponent('profile_sold_to'));
+
+ // Test a checkbox field.
+ $component = $display->getComponent('profile_really_really_love_mig');
+ $this->assertIdentical('list_default', $component['type']);
}
}
diff --git a/core/modules/user/tests/src/Kernel/Migrate/MigrateUserProfileEntityFormDisplayTest.php b/core/modules/user/tests/src/Kernel/Migrate/MigrateUserProfileEntityFormDisplayTest.php
index a2b01aa17..2fcec8dab 100644
--- a/core/modules/user/tests/src/Kernel/Migrate/MigrateUserProfileEntityFormDisplayTest.php
+++ b/core/modules/user/tests/src/Kernel/Migrate/MigrateUserProfileEntityFormDisplayTest.php
@@ -49,7 +49,7 @@ class MigrateUserProfileEntityFormDisplayTest extends MigrateDrupal6TestBase {
$this->assertNull($display->getComponent('profile_sold_to'));
// Test that a checkbox field has the proper display label setting.
- $component = $display->getComponent('profile_love_migrations');
+ $component = $display->getComponent('profile_really_really_love_mig');
$this->assertIdentical('boolean_checkbox', $component['type']);
$this->assertIdentical(TRUE, $component['settings']['display_label']);
}
diff --git a/core/modules/user/tests/src/Kernel/Migrate/MigrateUserProfileFieldInstanceTest.php b/core/modules/user/tests/src/Kernel/Migrate/MigrateUserProfileFieldInstanceTest.php
index 971333258..ecc9bc965 100644
--- a/core/modules/user/tests/src/Kernel/Migrate/MigrateUserProfileFieldInstanceTest.php
+++ b/core/modules/user/tests/src/Kernel/Migrate/MigrateUserProfileFieldInstanceTest.php
@@ -68,8 +68,8 @@ class MigrateUserProfileFieldInstanceTest extends MigrateDrupal6TestBase {
$this->assertIdentical("Enter your birth date and we'll send you a coupon.", $field->getDescription());
// Another migrated checkbox field, with a different source visibility setting.
- $field = FieldConfig::load('user.user.profile_love_migrations');
- $this->assertIdentical('I love migrations', $field->label());
+ $field = FieldConfig::load('user.user.profile_really_really_love_mig');
+ $this->assertIdentical('I really, really, really love migrations', $field->label());
$this->assertIdentical("If you check this box, you love migrations.", $field->getDescription());
}
diff --git a/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserProfileValuesTest.php b/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserProfileValuesTest.php
index e734517a5..71acad69f 100644
--- a/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserProfileValuesTest.php
+++ b/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserProfileValuesTest.php
@@ -59,7 +59,13 @@ EOT;
$this->assertNull($user->profile_blog->title);
$this->assertIdentical([], $user->profile_blog->options);
$this->assertIdentical('http://example.com/blog', $user->profile_blog->uri);
- $this->assertNull($user->profile_love_migrations->value);
+
+ // Check that the source profile field names that are longer than 32
+ // characters have been migrated.
+ $this->assertNotNull($user->getFieldDefinition('profile_really_really_love_mig'));
+ $this->assertNotNull($user->getFieldDefinition('profile_really_really_love_mig1'));
+ $this->assertSame('1', $user->profile_really_really_love_mig->value);
+ $this->assertNull($user->profile_really_really_love_mig1->value);
$user = User::load(8);
$this->assertIdentical('Forward/slash', $user->profile_sold_to->value);
diff --git a/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserRoleTest.php b/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserRoleTest.php
index 5393805ca..0ca7f091a 100644
--- a/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserRoleTest.php
+++ b/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserRoleTest.php
@@ -60,7 +60,7 @@ class MigrateUserRoleTest extends MigrateDrupal6TestBase {
'access content',
'migrate test anonymous permission',
// From filter_format tables.
- 'use text format filtered_html'
+ 'use text format filtered_html',
];
$this->assertRole('anonymous', $permissions, 1, $id_map);
@@ -81,7 +81,7 @@ class MigrateUserRoleTest extends MigrateDrupal6TestBase {
'migrate test role 1 test permission',
// From filter format.
'use text format full_html',
- 'use text format php_code'
+ 'use text format php_code',
];
$this->assertRole('migrate_test_role_1', $permissions, 3, $id_map);
@@ -126,7 +126,7 @@ class MigrateUserRoleTest extends MigrateDrupal6TestBase {
'administrator1',
'migrate_test_role_11',
'migrate_test_role_21',
- 'migrate_test_role_3_that_is_longer_than_thirty_two_characters1'
+ 'migrate_test_role_3_that_is_longer_than_thirty_two_characters1',
];
$this->assertEmpty(Role::loadMultiple($roles));
diff --git a/core/modules/user/tests/src/Kernel/Migrate/d7/MigrateUserTest.php b/core/modules/user/tests/src/Kernel/Migrate/d7/MigrateUserTest.php
index bbfc41d77..7564fceec 100644
--- a/core/modules/user/tests/src/Kernel/Migrate/d7/MigrateUserTest.php
+++ b/core/modules/user/tests/src/Kernel/Migrate/d7/MigrateUserTest.php
@@ -24,11 +24,14 @@ class MigrateUserTest extends MigrateDrupal7TestBase {
*/
public static $modules = [
'comment',
+ 'content_translation',
'datetime',
'file',
'image',
'language',
'link',
+ // Required for translation migrations.
+ 'migrate_drupal_multilingual',
'node',
'system',
'taxonomy',
@@ -44,6 +47,9 @@ class MigrateUserTest extends MigrateDrupal7TestBase {
// Prepare to migrate user pictures as well.
$this->installEntitySchema('file');
+ $this->installEntitySchema('comment');
+ $this->installEntitySchema('node');
+ $this->installEntitySchema('taxonomy_term');
$this->createNodeCommentCombination('page');
$this->createNodeCommentCombination('article');
$this->createNodeCommentCombination('blog');
@@ -59,6 +65,8 @@ class MigrateUserTest extends MigrateDrupal7TestBase {
'd7_field',
'd7_field_instance',
'd7_user',
+ 'd7_entity_translation_settings',
+ 'd7_user_entity_translation',
]);
}
@@ -81,8 +89,10 @@ class MigrateUserTest extends MigrateDrupal7TestBase {
* The last login time.
* @param bool $blocked
* Whether or not the account is blocked.
- * @param string $langcode
- * The user account's language code.
+ * @param string $entity_langcode
+ * The user entity language code.
+ * @param string $prefered_langcode
+ * The user prefered language code.
* @param string $timezone
* The user account's timezone name.
* @param string $init
@@ -96,7 +106,7 @@ class MigrateUserTest extends MigrateDrupal7TestBase {
* @param bool $has_picture
* (optional) Whether the user is expected to have a picture attached.
*/
- protected function assertEntity($id, $label, $mail, $password, $created, $access, $login, $blocked, $langcode, $timezone, $init, $roles, $field_integer, $field_file_target_id = FALSE, $has_picture = FALSE) {
+ protected function assertEntity($id, $label, $mail, $password, $created, $access, $login, $blocked, $entity_langcode, $prefered_langcode, $timezone, $init, $roles, $field_integer, $field_file_target_id = FALSE, $has_picture = FALSE) {
/** @var \Drupal\user\UserInterface $user */
$user = User::load($id);
$this->assertTrue($user instanceof UserInterface);
@@ -115,20 +125,20 @@ class MigrateUserTest extends MigrateDrupal7TestBase {
// test if the value was imported correctly.
$language_manager = $this->container->get('language_manager');
$default_langcode = $language_manager->getDefaultLanguage()->getId();
- if ($langcode == '') {
+ if ($prefered_langcode == '') {
$this->assertSame('en', $user->langcode->value);
$this->assertSame($default_langcode, $user->preferred_langcode->value);
$this->assertSame($default_langcode, $user->preferred_admin_langcode->value);
}
- elseif ($language_manager->getLanguage($langcode) === NULL) {
+ elseif ($language_manager->getLanguage($prefered_langcode) === NULL) {
$this->assertSame($default_langcode, $user->langcode->value);
$this->assertSame($default_langcode, $user->preferred_langcode->value);
$this->assertSame($default_langcode, $user->preferred_admin_langcode->value);
}
else {
- $this->assertSame($langcode, $user->langcode->value);
- $this->assertSame($langcode, $user->preferred_langcode->value);
- $this->assertSame($langcode, $user->preferred_admin_langcode->value);
+ $this->assertSame($entity_langcode, $user->langcode->value);
+ $this->assertSame($prefered_langcode, $user->preferred_langcode->value);
+ $this->assertSame($prefered_langcode, $user->preferred_admin_langcode->value);
}
$this->assertSame($timezone, $user->getTimeZone());
@@ -170,10 +180,21 @@ class MigrateUserTest extends MigrateDrupal7TestBase {
$roles[] = reset($role);
}
+ $entity_translation = Database::getConnection('default', 'migrate')
+ ->select('entity_translation', 'et')
+ ->fields('et', ['language'])
+ ->condition('et.entity_type', 'user')
+ ->condition('et.entity_id', $source->uid)
+ ->condition('et.source', '')
+ ->execute()
+ ->fetchField();
+ $entity_language = $entity_translation ?: $source->language;
+
$field_integer = Database::getConnection('default', 'migrate')
->select('field_data_field_integer', 'fi')
->fields('fi', ['field_integer_value'])
->condition('fi.entity_id', $source->uid)
+ ->condition('fi.language', $entity_language)
->execute()
->fetchCol();
$field_integer = !empty($field_integer) ? $field_integer : NULL;
@@ -194,6 +215,7 @@ class MigrateUserTest extends MigrateDrupal7TestBase {
$source->access,
$source->login,
$source->status,
+ $entity_language,
$source->language,
$source->timezone,
$source->init,
@@ -218,4 +240,45 @@ class MigrateUserTest extends MigrateDrupal7TestBase {
}
}
+ /**
+ * Tests the Drupal 7 user entity translations to Drupal 8 migration.
+ */
+ public function testUserEntityTranslations() {
+ $manager = $this->container->get('content_translation.manager');
+
+ // Get the user and its translations.
+ $user = User::load(2);
+ $user_fr = $user->getTranslation('fr');
+ $user_is = $user->getTranslation('is');
+
+ // Test that fields translated with Entity Translation are migrated.
+ $this->assertSame('99', $user->field_integer->value);
+ $this->assertSame('9', $user_fr->field_integer->value);
+ $this->assertSame('1', $user_is->field_integer->value);
+
+ // Test that the French translation metadata is correctly migrated.
+ $metadata_fr = $manager->getTranslationMetadata($user_fr);
+ $this->assertSame('en', $metadata_fr->getSource());
+ $this->assertSame('1', $metadata_fr->getAuthor()->uid->value);
+ $this->assertSame('1531663916', $metadata_fr->getCreatedTime());
+ $this->assertFalse($metadata_fr->isOutdated());
+ $this->assertFalse($metadata_fr->isPublished());
+
+ // Test that the Icelandic translation metadata is correctly migrated.
+ $metadata_is = $manager->getTranslationMetadata($user_is);
+ $this->assertSame('en', $metadata_is->getSource());
+ $this->assertSame('2', $metadata_is->getAuthor()->uid->value);
+ $this->assertSame('1531663925', $metadata_is->getCreatedTime());
+ $this->assertTrue($metadata_is->isOutdated());
+ $this->assertTrue($metadata_is->isPublished());
+
+ // Test that untranslatable properties are the same as the source language.
+ $this->assertSame($user->label(), $user_fr->label());
+ $this->assertSame($user->label(), $user_is->label());
+ $this->assertSame($user->getEmail(), $user_fr->getEmail());
+ $this->assertSame($user->getEmail(), $user_is->getEmail());
+ $this->assertSame($user->getPassword(), $user_fr->getPassword());
+ $this->assertSame($user->getPassword(), $user_is->getPassword());
+ }
+
}
diff --git a/core/modules/user/tests/src/Kernel/Plugin/migrate/source/ProfileFieldTest.php b/core/modules/user/tests/src/Kernel/Plugin/migrate/source/ProfileFieldTest.php
index c32118571..99c61fd49 100644
--- a/core/modules/user/tests/src/Kernel/Plugin/migrate/source/ProfileFieldTest.php
+++ b/core/modules/user/tests/src/Kernel/Plugin/migrate/source/ProfileFieldTest.php
@@ -99,7 +99,7 @@ class ProfileFieldTest extends MigrateSqlSourceTestBase {
'fid' => 4,
'uid' => 1,
'value' => 'yellow',
- ]
+ ],
];
// Expected options are:
@@ -114,7 +114,7 @@ class ProfileFieldTest extends MigrateSqlSourceTestBase {
'blue' => 'blue',
'green' => 'green',
'yellow' => 'yellow',
- ]
+ ],
];
$tests[0]['expected_data'] = $profile_fields;
diff --git a/core/modules/user/tests/src/Kernel/Plugin/migrate/source/d7/UserEntityTranslationTest.php b/core/modules/user/tests/src/Kernel/Plugin/migrate/source/d7/UserEntityTranslationTest.php
new file mode 100644
index 000000000..e265ace61
--- /dev/null
+++ b/core/modules/user/tests/src/Kernel/Plugin/migrate/source/d7/UserEntityTranslationTest.php
@@ -0,0 +1,199 @@
+ 'user',
+ 'entity_id' => 1,
+ 'revision_id' => 1,
+ 'language' => 'en',
+ 'source' => '',
+ 'uid' => 1,
+ 'status' => 1,
+ 'translate' => 0,
+ 'created' => 1531343498,
+ 'changed' => 1531343498,
+ ],
+ [
+ 'entity_type' => 'user',
+ 'entity_id' => 1,
+ 'revision_id' => 1,
+ 'language' => 'fr',
+ 'source' => 'en',
+ 'uid' => 2,
+ 'status' => 1,
+ 'translate' => 1,
+ 'created' => 1531343508,
+ 'changed' => 1531343508,
+ ],
+ [
+ 'entity_type' => 'user',
+ 'entity_id' => 1,
+ 'revision_id' => 1,
+ 'language' => 'es',
+ 'source' => 'en',
+ 'uid' => 1,
+ 'status' => 0,
+ 'translate' => 0,
+ 'created' => 1531343456,
+ 'changed' => 1531343456,
+ ],
+ ];
+ $tests[0]['source_data']['field_config'] = [
+ [
+ 'id' => 1,
+ 'field_name' => 'field_test',
+ 'type' => 'text',
+ 'module' => 'text',
+ 'active' => 1,
+ 'storage_type' => 'field_sql_storage',
+ 'storage_module' => 'field_sql_storage',
+ 'storage_active' => 1,
+ 'locked' => 1,
+ 'data' => 'a:0:{}',
+ 'cardinality' => 1,
+ 'translatable' => 1,
+ 'deleted' => 0,
+ ],
+ ];
+ $tests[0]['source_data']['field_config_instance'] = [
+ [
+ 'id' => 1,
+ 'field_id' => 1,
+ 'field_name' => 'field_test',
+ 'entity_type' => 'user',
+ 'bundle' => 'user',
+ 'data' => 'a:0:{}',
+ 'deleted' => 0,
+ ],
+ ];
+ $tests[0]['source_data']['field_data_field_test'] = [
+ [
+ 'entity_type' => 'user',
+ 'bundle' => 'user',
+ 'deleted' => 0,
+ 'entity_id' => 1,
+ 'revision_id' => 1,
+ 'language' => 'en',
+ 'delta' => 0,
+ 'field_test_value' => 'English field',
+ 'field_test_format' => NULL,
+ ],
+ [
+ 'entity_type' => 'user',
+ 'bundle' => 'user',
+ 'deleted' => 0,
+ 'entity_id' => 1,
+ 'revision_id' => 1,
+ 'language' => 'fr',
+ 'delta' => 0,
+ 'field_test_value' => 'French field',
+ 'field_test_format' => NULL,
+ ],
+ [
+ 'entity_type' => 'user',
+ 'bundle' => 'user',
+ 'deleted' => 0,
+ 'entity_id' => 1,
+ 'revision_id' => 1,
+ 'language' => 'es',
+ 'delta' => 0,
+ 'field_test_value' => 'Spanish field',
+ 'field_test_format' => NULL,
+ ],
+ ];
+ $tests[0]['source_data']['users'] = [
+ [
+ 'uid' => 1,
+ 'name' => 'admin',
+ 'pass' => 'password123',
+ 'mail' => 'admin@example.com',
+ 'theme' => '',
+ 'signature' => '',
+ 'signature_format' => 'filtered_html',
+ 'created' => 1531343456,
+ 'access' => 1531343456,
+ 'login' => 1531343456,
+ 'status' => 1,
+ 'timezone' => 'America/New_York',
+ 'language' => 'fr',
+ 'picture' => 0,
+ 'init' => 'admin@example.com',
+ 'data' => 'a:0:{}',
+ ],
+ ];
+ $tests[0]['source_data']['users_roles'] = [
+ [
+ 'uid' => 1,
+ 'rid' => 3,
+ ],
+ ];
+
+ // The expected results.
+ $tests[0]['expected_data'] = [
+ [
+ 'entity_type' => 'user',
+ 'entity_id' => 1,
+ 'revision_id' => 1,
+ 'language' => 'fr',
+ 'source' => 'en',
+ 'uid' => 2,
+ 'status' => 1,
+ 'translate' => 1,
+ 'created' => 1531343508,
+ 'changed' => 1531343508,
+ 'field_test' => [
+ [
+ 'value' => 'French field',
+ 'format' => NULL,
+ ],
+ ],
+ ],
+ [
+ 'entity_type' => 'user',
+ 'entity_id' => 1,
+ 'revision_id' => 1,
+ 'language' => 'es',
+ 'source' => 'en',
+ 'uid' => 1,
+ 'status' => 0,
+ 'translate' => 0,
+ 'created' => 1531343456,
+ 'changed' => 1531343456,
+ 'field_test' => [
+ [
+ 'value' => 'Spanish field',
+ 'format' => NULL,
+ ],
+ ],
+ ],
+ ];
+
+ return $tests;
+ }
+
+}
diff --git a/core/modules/user/tests/src/Kernel/Plugin/migrate/source/d7/UserTest.php b/core/modules/user/tests/src/Kernel/Plugin/migrate/source/d7/UserTest.php
index 8e14d63c9..80935715e 100644
--- a/core/modules/user/tests/src/Kernel/Plugin/migrate/source/d7/UserTest.php
+++ b/core/modules/user/tests/src/Kernel/Plugin/migrate/source/d7/UserTest.php
@@ -24,6 +24,12 @@ class UserTest extends MigrateSqlSourceTestBase {
$tests = [];
// The source data.
+ $tests[0]['source_data']['field_config'] = [
+ [
+ 'id' => '11',
+ 'translatable' => '0',
+ ],
+ ];
$tests[0]['source_data']['field_config_instance'] = [
[
'id' => '33',
diff --git a/core/modules/user/tests/src/Kernel/UserAccountFormFieldsTest.php b/core/modules/user/tests/src/Kernel/UserAccountFormFieldsTest.php
index 643302192..fe967b2d9 100644
--- a/core/modules/user/tests/src/Kernel/UserAccountFormFieldsTest.php
+++ b/core/modules/user/tests/src/Kernel/UserAccountFormFieldsTest.php
@@ -24,8 +24,8 @@ class UserAccountFormFieldsTest extends KernelTestBase {
* Tests the root user account form section in the "Configure site" form.
*/
public function testInstallConfigureForm() {
- require_once \Drupal::root() . '/core/includes/install.core.inc';
- require_once \Drupal::root() . '/core/includes/install.inc';
+ require_once $this->root . '/core/includes/install.core.inc';
+ require_once $this->root . '/core/includes/install.inc';
$install_state = install_state_defaults();
$form_state = new FormState();
$form_state->addBuildInfo('args', [&$install_state]);
diff --git a/core/modules/user/tests/src/Kernel/UserInstallTest.php b/core/modules/user/tests/src/Kernel/UserInstallTest.php
index f61811be7..be3336f78 100644
--- a/core/modules/user/tests/src/Kernel/UserInstallTest.php
+++ b/core/modules/user/tests/src/Kernel/UserInstallTest.php
@@ -28,7 +28,6 @@ class UserInstallTest extends KernelTestBase {
user_install();
}
-
/**
* Test that the initial users have correct values.
*/
diff --git a/core/modules/user/tests/src/Kernel/UserValidationTest.php b/core/modules/user/tests/src/Kernel/UserValidationTest.php
index 4aa5850d1..98065f36b 100644
--- a/core/modules/user/tests/src/Kernel/UserValidationTest.php
+++ b/core/modules/user/tests/src/Kernel/UserValidationTest.php
@@ -215,7 +215,7 @@ class UserValidationTest extends KernelTestBase {
protected function assertAllowedValuesViolation(EntityInterface $entity, $field_name) {
$violations = $entity->validate();
$this->assertEqual(count($violations), 1, "Allowed values violation for $field_name found.");
- $this->assertEqual($violations[0]->getPropertyPath(), "$field_name.0.value");
+ $this->assertEqual($violations[0]->getPropertyPath(), $field_name === 'langcode' ? "$field_name.0" : "$field_name.0.value");
$this->assertEqual($violations[0]->getMessage(), t('The value you selected is not a valid choice.'));
}
diff --git a/core/modules/user/tests/src/Kernel/WhosOnlineBlockTest.php b/core/modules/user/tests/src/Kernel/WhosOnlineBlockTest.php
new file mode 100644
index 000000000..02844a161
--- /dev/null
+++ b/core/modules/user/tests/src/Kernel/WhosOnlineBlockTest.php
@@ -0,0 +1,118 @@
+installConfig(['system', 'block', 'views', 'user']);
+ $this->installSchema('system', ['sequences']);
+ $this->installEntitySchema('user');
+
+ $this->controller = $this->container
+ ->get('entity_type.manager')
+ ->getStorage('block');
+
+ // Create a block with only required values.
+ $this->block = $this->controller->create([
+ 'plugin' => 'views_block:who_s_online-who_s_online_block',
+ 'region' => 'sidebar_first',
+ 'id' => 'views_block__who_s_online_who_s_online_block',
+ 'theme' => \Drupal::configFactory()->get('system.theme')->get('default'),
+ 'label' => "Who's online",
+ 'visibility' => [],
+ 'weight' => 0,
+ ]);
+ $this->block->save();
+
+ $this->container->get('cache.render')->deleteAll();
+ $this->renderer = $this->container->get('renderer');
+ }
+
+ /**
+ * Test the Who's Online block.
+ */
+ public function testWhosOnlineBlock() {
+ $request_time = \Drupal::time()->getRequestTime();
+ // Generate users.
+ $user1 = User::create([
+ 'name' => 'user1',
+ 'mail' => 'user1@example.com',
+ ]);
+ $user1->addRole('administrator');
+ $user1->activate();
+ $user1->setLastAccessTime($request_time);
+ $user1->save();
+
+ $user2 = User::create([
+ 'name' => 'user2',
+ 'mail' => 'user2@example.com',
+ ]);
+ $user2->activate();
+ $user2->setLastAccessTime($request_time + 1);
+ $user2->save();
+
+ $user3 = User::create([
+ 'name' => 'user3',
+ 'mail' => 'user2@example.com',
+ ]);
+ $user3->activate();
+ // Insert an inactive user who should not be seen in the block.
+ $inactive_time = $request_time - (60 * 60);
+ $user3->setLastAccessTime($inactive_time);
+ $user3->save();
+
+ // Test block output.
+ \Drupal::currentUser()->setAccount($user1);
+
+ // Test the rendering of a block.
+ $entity = Block::load('views_block__who_s_online_who_s_online_block');
+ $output = entity_view($entity, 'block');
+ $this->setRawContent($this->renderer->renderRoot($output));
+ $this->assertRaw('2 users', 'Correct number of online users (2 users).');
+ $this->assertText($user1->getUsername(), 'Active user 1 found in online list.');
+ $this->assertText($user2->getUsername(), 'Active user 2 found in online list.');
+ $this->assertNoText($user3->getUsername(), 'Inactive user not found in online list.');
+ $this->assertTrue(strpos($this->getRawContent(), $user1->getUsername()) > strpos($this->getRawContent(), $user2->getUsername()), 'Online users are ordered correctly.');
+ }
+
+}
diff --git a/core/modules/user/tests/src/Traits/UserCreationTrait.php b/core/modules/user/tests/src/Traits/UserCreationTrait.php
index ccccfa77f..9d7798245 100644
--- a/core/modules/user/tests/src/Traits/UserCreationTrait.php
+++ b/core/modules/user/tests/src/Traits/UserCreationTrait.php
@@ -2,7 +2,7 @@
namespace Drupal\Tests\user\Traits;
-use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Session\AccountInterface;
use Drupal\user\Entity\Role;
use Drupal\user\Entity\User;
@@ -69,7 +69,7 @@ trait UserCreationTrait {
$account = User::create($edit);
$account->save();
- $this->assertTrue($account->id(), SafeMarkup::format('User created with name %name and pass %pass', ['%name' => $edit['name'], '%pass' => $edit['pass']]), 'User login');
+ $this->assertTrue($account->id(), new FormattableMarkup('User created with name %name and pass %pass', ['%name' => $edit['name'], '%pass' => $edit['pass']]), 'User login');
if (!$account->id()) {
return FALSE;
}
@@ -149,7 +149,7 @@ trait UserCreationTrait {
}
$result = $role->save();
- $this->assertIdentical($result, SAVED_NEW, SafeMarkup::format('Created role ID @rid with name @name.', [
+ $this->assertIdentical($result, SAVED_NEW, new FormattableMarkup('Created role ID @rid with name @name.', [
'@name' => var_export($role->label(), TRUE),
'@rid' => var_export($role->id(), TRUE),
]), 'Role');
@@ -161,10 +161,10 @@ trait UserCreationTrait {
$assigned_permissions = Role::load($role->id())->getPermissions();
$missing_permissions = array_diff($permissions, $assigned_permissions);
if (!$missing_permissions) {
- $this->pass(SafeMarkup::format('Created permissions: @perms', ['@perms' => implode(', ', $permissions)]), 'Role');
+ $this->pass(new FormattableMarkup('Created permissions: @perms', ['@perms' => implode(', ', $permissions)]), 'Role');
}
else {
- $this->fail(SafeMarkup::format('Failed to create permissions: @perms', ['@perms' => implode(', ', $missing_permissions)]), 'Role');
+ $this->fail(new FormattableMarkup('Failed to create permissions: @perms', ['@perms' => implode(', ', $missing_permissions)]), 'Role');
}
}
return $role->id();
@@ -188,7 +188,7 @@ trait UserCreationTrait {
$valid = TRUE;
foreach ($permissions as $permission) {
if (!in_array($permission, $available)) {
- $this->fail(SafeMarkup::format('Invalid permission %permission.', ['%permission' => $permission]), 'Role');
+ $this->fail(new FormattableMarkup('Invalid permission %permission.', ['%permission' => $permission]), 'Role');
$valid = FALSE;
}
}
diff --git a/core/modules/user/tests/src/Unit/PermissionAccessCheckTest.php b/core/modules/user/tests/src/Unit/PermissionAccessCheckTest.php
index 84290cc96..b7c9f68cf 100644
--- a/core/modules/user/tests/src/Unit/PermissionAccessCheckTest.php
+++ b/core/modules/user/tests/src/Unit/PermissionAccessCheckTest.php
@@ -79,7 +79,7 @@ class PermissionAccessCheckTest extends UnitTestCase {
->will($this->returnValueMap([
['allowed', TRUE],
['denied', FALSE],
- ['other', FALSE]
+ ['other', FALSE],
]
));
$route = new Route('', [], $requirements);
diff --git a/core/modules/user/tests/src/Unit/PermissionHandlerTest.php b/core/modules/user/tests/src/Unit/PermissionHandlerTest.php
index 2918d2f61..a621455d4 100644
--- a/core/modules/user/tests/src/Unit/PermissionHandlerTest.php
+++ b/core/modules/user/tests/src/Unit/PermissionHandlerTest.php
@@ -400,7 +400,7 @@ class TestPermissionCallbacks {
public function singleDescription() {
return [
- 'access_module_a' => 'single_description'
+ 'access_module_a' => 'single_description',
];
}
diff --git a/core/modules/user/tests/src/Unit/Plugin/views/field/UserBulkFormTest.php b/core/modules/user/tests/src/Unit/Plugin/views/field/UserBulkFormTest.php
index 11a970a0b..a6b2f543f 100644
--- a/core/modules/user/tests/src/Unit/Plugin/views/field/UserBulkFormTest.php
+++ b/core/modules/user/tests/src/Unit/Plugin/views/field/UserBulkFormTest.php
@@ -54,6 +54,8 @@ class UserBulkFormTest extends UnitTestCase {
$language_manager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
+ $messenger = $this->getMock('Drupal\Core\Messenger\MessengerInterface');
+
$views_data = $this->getMockBuilder('Drupal\views\ViewsData')
->disableOriginalConstructor()
->getMock();
@@ -84,7 +86,7 @@ class UserBulkFormTest extends UnitTestCase {
$definition['title'] = '';
$options = [];
- $user_bulk_form = new UserBulkForm([], 'user_bulk_form', $definition, $entity_manager, $language_manager);
+ $user_bulk_form = new UserBulkForm([], 'user_bulk_form', $definition, $entity_manager, $language_manager, $messenger);
$user_bulk_form->init($executable, $display, $options);
$this->assertAttributeEquals(array_slice($actions, 0, -1, TRUE), 'actions', $user_bulk_form);
diff --git a/core/modules/user/tests/src/Unit/PrivateTempStoreTest.php b/core/modules/user/tests/src/Unit/PrivateTempStoreTest.php
index be9376ad1..2d8e092c1 100644
--- a/core/modules/user/tests/src/Unit/PrivateTempStoreTest.php
+++ b/core/modules/user/tests/src/Unit/PrivateTempStoreTest.php
@@ -96,7 +96,6 @@ class PrivateTempStoreTest extends UnitTestCase {
$this->otherObject->owner = 2;
}
-
/**
* Tests the get() method.
*
diff --git a/core/modules/user/tests/src/Unit/Views/Argument/RolesRidTest.php b/core/modules/user/tests/src/Unit/Views/Argument/RolesRidTest.php
index 4e48fba03..e61901166 100644
--- a/core/modules/user/tests/src/Unit/Views/Argument/RolesRidTest.php
+++ b/core/modules/user/tests/src/Unit/Views/Argument/RolesRidTest.php
@@ -19,11 +19,18 @@ class RolesRidTest extends UnitTestCase {
* Tests the titleQuery method.
*
* @covers ::titleQuery
+ *
+ * @group legacy
+ *
+ * Note this is only a legacy test because it triggers a call to
+ * \Drupal\Core\Entity\EntityTypeInterface::getLabelCallback() which is mocked
+ * and triggers a deprecation error. Remove when ::getLabelCallback() is
+ * removed.
*/
public function testTitleQuery() {
$role1 = new Role([
'id' => 'test_rid_1',
- 'label' => 'test rid 1'
+ 'label' => 'test rid 1',
], 'user_role');
$role2 = new Role([
'id' => 'test_rid_2',
diff --git a/core/modules/user/user.api.php b/core/modules/user/user.api.php
index 364dd1ac6..823c08da6 100644
--- a/core/modules/user/user.api.php
+++ b/core/modules/user/user.api.php
@@ -137,7 +137,14 @@ function hook_user_login($account) {
$config = \Drupal::config('system.date');
// If the user has a NULL time zone, notify them to set a time zone.
if (!$account->getTimezone() && $config->get('timezone.user.configurable') && $config->get('timezone.user.warn')) {
- drupal_set_message(t('Configure your account time zone setting.', [':user-edit' => $account->url('edit-form', ['query' => \Drupal::destination()->getAsArray(), 'fragment' => 'edit-timezone'])]));
+ \Drupal::messenger()
+ ->addStatus(t('Configure your account time zone setting.', [
+ ':user-edit' => $account->url('edit-form', [
+ 'query' => \Drupal::destination()
+ ->getAsArray(),
+ 'fragment' => 'edit-timezone',
+ ]),
+ ]));
}
}
diff --git a/core/modules/user/user.es6.js b/core/modules/user/user.es6.js
index 74cab06f1..4739e1528 100644
--- a/core/modules/user/user.es6.js
+++ b/core/modules/user/user.es6.js
@@ -3,7 +3,7 @@
* User behaviors.
*/
-(function ($, Drupal, drupalSettings) {
+(function($, Drupal, drupalSettings) {
/**
* Attach handlers to evaluate the strength of any password fields and to
* check that its confirmation is correct.
@@ -16,7 +16,9 @@
*/
Drupal.behaviors.password = {
attach(context, settings) {
- const $passwordInput = $(context).find('input.js-password-field').once('password');
+ const $passwordInput = $(context)
+ .find('input.js-password-field')
+ .once('password');
if ($passwordInput.length) {
const translate = settings.password;
@@ -32,36 +34,55 @@
$passwordInputParentWrapper
.find('input.js-password-confirm')
.parent()
- .append(`${translate.confirmTitle}
`)
+ .append(
+ `${
+ translate.confirmTitle
+ }
`,
+ )
.addClass('confirm-parent');
- const $confirmInput = $passwordInputParentWrapper.find('input.js-password-confirm');
- const $confirmResult = $passwordInputParentWrapper.find('div.js-password-confirm');
+ const $confirmInput = $passwordInputParentWrapper.find(
+ 'input.js-password-confirm',
+ );
+ const $confirmResult = $passwordInputParentWrapper.find(
+ 'div.js-password-confirm',
+ );
const $confirmChild = $confirmResult.find('span');
// If the password strength indicator is enabled, add its markup.
if (settings.password.showStrengthIndicator) {
- const passwordMeter = `${translate.strengthTitle}
`;
- $confirmInput.parent().after('');
+ const passwordMeter = `${
+ translate.strengthTitle
+ }
`;
+ $confirmInput
+ .parent()
+ .after('');
$passwordInputParent.append(passwordMeter);
- $passwordSuggestions = $passwordInputParentWrapper.find('div.password-suggestions').hide();
+ $passwordSuggestions = $passwordInputParentWrapper
+ .find('div.password-suggestions')
+ .hide();
}
// Check that password and confirmation inputs match.
- const passwordCheckMatch = function (confirmInputVal) {
+ const passwordCheckMatch = function(confirmInputVal) {
const success = $passwordInput.val() === confirmInputVal;
const confirmClass = success ? 'ok' : 'error';
// Fill in the success message and set the class accordingly.
- $confirmChild.html(translate[`confirm${success ? 'Success' : 'Failure'}`])
- .removeClass('ok error').addClass(confirmClass);
+ $confirmChild
+ .html(translate[`confirm${success ? 'Success' : 'Failure'}`])
+ .removeClass('ok error')
+ .addClass(confirmClass);
};
// Check the password strength.
- const passwordCheck = function () {
+ const passwordCheck = function() {
if (settings.password.showStrengthIndicator) {
// Evaluate the password strength.
- const result = Drupal.evaluatePasswordStrength($passwordInput.val(), settings.password);
+ const result = Drupal.evaluatePasswordStrength(
+ $passwordInput.val(),
+ settings.password,
+ );
// Update the suggestions for how to improve the password.
if ($passwordSuggestions.html() !== result.message) {
@@ -73,21 +94,23 @@
$passwordSuggestions.toggle(result.strength !== 100);
// Adjust the length of the strength indicator.
- $passwordInputParent.find('.js-password-strength__indicator')
+ $passwordInputParent
+ .find('.js-password-strength__indicator')
.css('width', `${result.strength}%`)
.removeClass('is-weak is-fair is-good is-strong')
.addClass(result.indicatorClass);
// Update the strength indication text.
- $passwordInputParent.find('.js-password-strength__text').html(result.indicatorText);
+ $passwordInputParent
+ .find('.js-password-strength__text')
+ .html(result.indicatorText);
}
// Check the value in the confirm input and show results.
if ($confirmInput.val()) {
passwordCheckMatch($confirmInput.val());
$confirmResult.css({ visibility: 'visible' });
- }
- else {
+ } else {
$confirmResult.css({ visibility: 'hidden' });
}
};
@@ -112,7 +135,7 @@
* @return {object}
* An object containing strength, message, indicatorText and indicatorClass.
*/
- Drupal.evaluatePasswordStrength = function (password, translate) {
+ Drupal.evaluatePasswordStrength = function(password, translate) {
password = password.trim();
let indicatorText;
let indicatorClass;
@@ -128,12 +151,13 @@
// If there is a username edit box on the page, compare password to that,
// otherwise use value from the database.
const $usernameBox = $('input.username');
- const username = ($usernameBox.length > 0) ? $usernameBox.val() : translate.username;
+ const username =
+ $usernameBox.length > 0 ? $usernameBox.val() : translate.username;
// Lose 5 points for every character less than 12, plus a 30 point penalty.
if (password.length < 12) {
msg.push(translate.tooShort);
- strength -= ((12 - password.length) * 5) + 30;
+ strength -= (12 - password.length) * 5 + 30;
}
// Count weaknesses.
@@ -185,22 +209,21 @@
if (strength < 60) {
indicatorText = translate.weak;
indicatorClass = 'is-weak';
- }
- else if (strength < 70) {
+ } else if (strength < 70) {
indicatorText = translate.fair;
indicatorClass = 'is-fair';
- }
- else if (strength < 80) {
+ } else if (strength < 80) {
indicatorText = translate.good;
indicatorClass = 'is-good';
- }
- else if (strength <= 100) {
+ } else if (strength <= 100) {
indicatorText = translate.strong;
indicatorClass = 'is-strong';
}
// Assemble the final message.
- msg = `${translate.hasWeaknesses}`;
+ msg = `${translate.hasWeaknesses}`;
return {
strength,
@@ -209,4 +232,4 @@
indicatorClass,
};
};
-}(jQuery, Drupal, drupalSettings));
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/modules/user/user.info.yml b/core/modules/user/user.info.yml
index 8aec70e11..86faf68a6 100644
--- a/core/modules/user/user.info.yml
+++ b/core/modules/user/user.info.yml
@@ -7,4 +7,4 @@ core: 8.x
required: true
configure: user.admin_index
dependencies:
- - system
+ - drupal:system
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index cabe189e5..b18408ab5 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -491,9 +491,9 @@ function template_preprocess_username(&$variables) {
// unsanitized version, in case other preprocess functions want to implement
// their own shortening logic or add markup. If they do so, they must ensure
// that $variables['name'] is safe for printing.
- $name = $account->getDisplayName();
+ $name = $account->getDisplayName();
$variables['name_raw'] = $account->getUsername();
- if (Unicode::strlen($name) > 20) {
+ if (mb_strlen($name) > 20) {
$name = Unicode::truncate($name, 15, FALSE, TRUE);
$variables['truncated'] = TRUE;
}
@@ -609,7 +609,7 @@ function user_pass_reset_url($account, $options = []) {
],
[
'absolute' => TRUE,
- 'language' => \Drupal::languageManager()->getLanguage($langcode)
+ 'language' => \Drupal::languageManager()->getLanguage($langcode),
]
);
}
@@ -638,7 +638,7 @@ function user_cancel_url(UserInterface $account, $options = []) {
return \Drupal::url('user.cancel_confirm', [
'user' => $account->id(),
'timestamp' => $timestamp,
- 'hashed_pass' => user_pass_rehash($account, $timestamp)
+ 'hashed_pass' => user_pass_rehash($account, $timestamp),
], $url_options);
}
@@ -690,7 +690,7 @@ function user_cancel($edit, $uid, $method) {
$account = User::load($uid);
if (!$account) {
- drupal_set_message(t('The user account %id does not exist.', ['%id' => $uid]), 'error');
+ \Drupal::messenger()->addError(t('The user account %id does not exist.', ['%id' => $uid]));
\Drupal::logger('user')->error('Attempted to cancel non-existing user account: %id.', ['%id' => $uid]);
return;
}
@@ -761,7 +761,7 @@ function _user_cancel($edit, $account, $method) {
}
$account->block();
$account->save();
- drupal_set_message(t('%name has been disabled.', ['%name' => $account->getDisplayName()]));
+ \Drupal::messenger()->addStatus(t('%name has been disabled.', ['%name' => $account->getDisplayName()]));
$logger->notice('Blocked user: %name %email.', ['%name' => $account->getAccountName(), '%email' => '<' . $account->getEmail() . '>']);
break;
@@ -772,7 +772,7 @@ function _user_cancel($edit, $account, $method) {
_user_mail_notify('status_canceled', $account);
}
$account->delete();
- drupal_set_message(t('%name has been deleted.', ['%name' => $account->getDisplayName()]));
+ \Drupal::messenger()->addStatus(t('%name has been deleted.', ['%name' => $account->getDisplayName()]));
$logger->notice('Deleted user: %name %email.', ['%name' => $account->getAccountName(), '%email' => '<' . $account->getEmail() . '>']);
break;
}
@@ -900,7 +900,7 @@ function user_delete_multiple(array $uids) {
* content language of the current request.
*
* @return array
- * An array as expected by drupal_render().
+ * An array as expected by \Drupal\Core\Render\RendererInterface::render().
*/
function user_view($account, $view_mode = 'full', $langcode = NULL) {
return entity_view($account, $view_mode, $langcode);
@@ -918,7 +918,8 @@ function user_view($account, $view_mode = 'full', $langcode = NULL) {
* content language of the current request.
*
* @return array
- * An array in the format expected by drupal_render().
+ * An array in the format expected by
+ * \Drupal\Core\Render\RendererInterface::render().
*/
function user_view_multiple($accounts, $view_mode = 'full', $langcode = NULL) {
return entity_view_multiple($accounts, $view_mode, $langcode);
diff --git a/core/modules/user/user.permissions.es6.js b/core/modules/user/user.permissions.es6.js
index 7aa79bbfa..7332ea35d 100644
--- a/core/modules/user/user.permissions.es6.js
+++ b/core/modules/user/user.permissions.es6.js
@@ -3,7 +3,7 @@
* User permission page behaviors.
*/
-(function ($, Drupal) {
+(function($, Drupal) {
/**
* Shows checked and disabled checkboxes for inherited permissions.
*
@@ -15,50 +15,59 @@
Drupal.behaviors.permissions = {
attach(context) {
const self = this;
- $('table#permissions').once('permissions').each(function () {
- // On a site with many roles and permissions, this behavior initially
- // has to perform thousands of DOM manipulations to inject checkboxes
- // and hide them. By detaching the table from the DOM, all operations
- // can be performed without triggering internal layout and re-rendering
- // processes in the browser.
- const $table = $(this);
- let $ancestor;
- let method;
- if ($table.prev().length) {
- $ancestor = $table.prev();
- method = 'after';
- }
- else {
- $ancestor = $table.parent();
- method = 'append';
- }
- $table.detach();
+ $('table#permissions')
+ .once('permissions')
+ .each(function() {
+ // On a site with many roles and permissions, this behavior initially
+ // has to perform thousands of DOM manipulations to inject checkboxes
+ // and hide them. By detaching the table from the DOM, all operations
+ // can be performed without triggering internal layout and re-rendering
+ // processes in the browser.
+ const $table = $(this);
+ let $ancestor;
+ let method;
+ if ($table.prev().length) {
+ $ancestor = $table.prev();
+ method = 'after';
+ } else {
+ $ancestor = $table.parent();
+ method = 'append';
+ }
+ $table.detach();
- // Create dummy checkboxes. We use dummy checkboxes instead of reusing
- // the existing checkboxes here because new checkboxes don't alter the
- // submitted form. If we'd automatically check existing checkboxes, the
- // permission table would be polluted with redundant entries. This
- // is deliberate, but desirable when we automatically check them.
- const $dummy = $('')
- .attr('title', Drupal.t('This permission is inherited from the authenticated user role.'))
- .hide();
+ // Create dummy checkboxes. We use dummy checkboxes instead of reusing
+ // the existing checkboxes here because new checkboxes don't alter the
+ // submitted form. If we'd automatically check existing checkboxes, the
+ // permission table would be polluted with redundant entries. This
+ // is deliberate, but desirable when we automatically check them.
+ const $dummy = $(
+ '',
+ )
+ .attr(
+ 'title',
+ Drupal.t(
+ 'This permission is inherited from the authenticated user role.',
+ ),
+ )
+ .hide();
- $table
- .find('input[type="checkbox"]')
- .not('.js-rid-anonymous, .js-rid-authenticated')
- .addClass('real-checkbox js-real-checkbox')
- .after($dummy);
+ $table
+ .find('input[type="checkbox"]')
+ .not('.js-rid-anonymous, .js-rid-authenticated')
+ .addClass('real-checkbox js-real-checkbox')
+ .after($dummy);
- // Initialize the authenticated user checkbox.
- $table.find('input[type=checkbox].js-rid-authenticated')
- .on('click.permissions', self.toggle)
- // .triggerHandler() cannot be used here, as it only affects the first
- // element.
- .each(self.toggle);
+ // Initialize the authenticated user checkbox.
+ $table
+ .find('input[type=checkbox].js-rid-authenticated')
+ .on('click.permissions', self.toggle)
+ // .triggerHandler() cannot be used here, as it only affects the first
+ // element.
+ .each(self.toggle);
- // Re-insert the table into the DOM.
- $ancestor[method]($table);
- });
+ // Re-insert the table into the DOM.
+ $ancestor[method]($table);
+ });
},
/**
@@ -73,12 +82,12 @@
// jQuery performs too many layout calculations for .hide() and .show(),
// leading to a major page rendering lag on sites with many roles and
// permissions. Therefore, we toggle visibility directly.
- $row.find('.js-real-checkbox').each(function () {
- this.style.display = (authCheckbox.checked ? 'none' : '');
+ $row.find('.js-real-checkbox').each(function() {
+ this.style.display = authCheckbox.checked ? 'none' : '';
});
- $row.find('.js-dummy-checkbox').each(function () {
- this.style.display = (authCheckbox.checked ? '' : 'none');
+ $row.find('.js-dummy-checkbox').each(function() {
+ this.style.display = authCheckbox.checked ? '' : 'none';
});
},
};
-}(jQuery, Drupal));
+})(jQuery, Drupal);
diff --git a/core/modules/views/config/schema/views.area.schema.yml b/core/modules/views/config/schema/views.area.schema.yml
index a7d5aa2c4..d1396c7fd 100644
--- a/core/modules/views/config/schema/views.area.schema.yml
+++ b/core/modules/views/config/schema/views.area.schema.yml
@@ -77,4 +77,3 @@ views.area.http_status_code:
status_code:
type: integer
label: 'HTTP status code'
-
diff --git a/core/modules/views/js/ajax_view.es6.js b/core/modules/views/js/ajax_view.es6.js
index 0c8535890..55c85fda1 100644
--- a/core/modules/views/js/ajax_view.es6.js
+++ b/core/modules/views/js/ajax_view.es6.js
@@ -3,7 +3,7 @@
* Handles AJAX fetching of views, including filter submission and response.
*/
-(function ($, Drupal, drupalSettings) {
+(function($, Drupal, drupalSettings) {
/**
* Attaches the AJAX behavior to exposed filters forms and key View links.
*
@@ -13,14 +13,32 @@
* Attaches ajaxView functionality to relevant elements.
*/
Drupal.behaviors.ViewsAjaxView = {};
- Drupal.behaviors.ViewsAjaxView.attach = function () {
- if (drupalSettings && drupalSettings.views && drupalSettings.views.ajaxViews) {
- const ajaxViews = drupalSettings.views.ajaxViews;
- Object.keys(ajaxViews || {}).forEach((i) => {
+ Drupal.behaviors.ViewsAjaxView.attach = function(context, settings) {
+ if (settings && settings.views && settings.views.ajaxViews) {
+ const {
+ views: { ajaxViews },
+ } = settings;
+ Object.keys(ajaxViews || {}).forEach(i => {
Drupal.views.instances[i] = new Drupal.views.ajaxView(ajaxViews[i]);
});
}
};
+ Drupal.behaviors.ViewsAjaxView.detach = (context, settings, trigger) => {
+ if (trigger === 'unload') {
+ if (settings && settings.views && settings.views.ajaxViews) {
+ const {
+ views: { ajaxViews },
+ } = settings;
+ Object.keys(ajaxViews || {}).forEach(i => {
+ const selector = `.js-view-dom-id-${ajaxViews[i].view_dom_id}`;
+ if ($(selector, context).length) {
+ delete Drupal.views.instances[i];
+ delete settings.views.ajaxViews[i];
+ }
+ });
+ }
+ }
+ };
/**
* @namespace
@@ -42,7 +60,7 @@
* @param {string} settings.view_dom_id
* The DOM id of the view.
*/
- Drupal.views.ajaxView = function (settings) {
+ Drupal.views.ajaxView = function(settings) {
const selector = `.js-view-dom-id-${settings.view_dom_id}`;
this.$view = $(selector);
@@ -59,11 +77,13 @@
let queryString = window.location.search || '';
if (queryString !== '') {
// Remove the question mark and Drupal path component if any.
- queryString = queryString.slice(1).replace(/q=[^&]+&?|&?render=[^&]+/, '');
+ queryString = queryString
+ .slice(1)
+ .replace(/q=[^&]+&?|&?render=[^&]+/, '');
if (queryString !== '') {
// If there is a '?' in ajaxPath, clean url are on and & should be
// used to add parameters.
- queryString = ((/\?/.test(ajaxPath)) ? '&' : '?') + queryString;
+ queryString = (/\?/.test(ajaxPath) ? '&' : '?') + queryString;
}
}
@@ -79,15 +99,23 @@
this.settings = settings;
// Add the ajax to exposed forms.
- this.$exposed_form = $(`form#views-exposed-form-${settings.view_name.replace(/_/g, '-')}-${settings.view_display_id.replace(/_/g, '-')}`);
- this.$exposed_form.once('exposed-form').each($.proxy(this.attachExposedFormAjax, this));
+ this.$exposed_form = $(
+ `form#views-exposed-form-${settings.view_name.replace(
+ /_/g,
+ '-',
+ )}-${settings.view_display_id.replace(/_/g, '-')}`,
+ );
+ this.$exposed_form
+ .once('exposed-form')
+ .each($.proxy(this.attachExposedFormAjax, this));
// Add the ajax to pagers.
this.$view
// Don't attach to nested views. Doing so would attach multiple behaviors
// to a given element.
.filter($.proxy(this.filterNestedViews, this))
- .once('ajax-pager').each($.proxy(this.attachPagerAjax, this));
+ .once('ajax-pager')
+ .each($.proxy(this.attachPagerAjax, this));
// Add a trigger to update this view specifically. In order to trigger a
// refresh use the following code.
@@ -106,25 +134,27 @@
/**
* @method
*/
- Drupal.views.ajaxView.prototype.attachExposedFormAjax = function () {
+ Drupal.views.ajaxView.prototype.attachExposedFormAjax = function() {
const that = this;
this.exposedFormAjax = [];
// Exclude the reset buttons so no AJAX behaviours are bound. Many things
// break during the form reset phase if using AJAX.
- $('input[type=submit], input[type=image]', this.$exposed_form).not('[data-drupal-selector=edit-reset]').each(function (index) {
- const selfSettings = $.extend({}, that.element_settings, {
- base: $(this).attr('id'),
- element: this,
+ $('input[type=submit], input[type=image]', this.$exposed_form)
+ .not('[data-drupal-selector=edit-reset]')
+ .each(function(index) {
+ const selfSettings = $.extend({}, that.element_settings, {
+ base: $(this).attr('id'),
+ element: this,
+ });
+ that.exposedFormAjax[index] = Drupal.ajax(selfSettings);
});
- that.exposedFormAjax[index] = Drupal.ajax(selfSettings);
- });
};
/**
* @return {bool}
* If there is at least one parent with a view class return false.
*/
- Drupal.views.ajaxView.prototype.filterNestedViews = function () {
+ Drupal.views.ajaxView.prototype.filterNestedViews = function() {
// If there is at least one parent with a view class, this view
// is nested (e.g., an attachment). Bail.
return !this.$view.parents('.view').length;
@@ -133,8 +163,11 @@
/**
* Attach the ajax behavior to each link.
*/
- Drupal.views.ajaxView.prototype.attachPagerAjax = function () {
- this.$view.find('ul.js-pager__items > li > a, th.views-field a, .attachment .views-summary a')
+ Drupal.views.ajaxView.prototype.attachPagerAjax = function() {
+ this.$view
+ .find(
+ 'ul.js-pager__items > li > a, th.views-field a, .attachment .views-summary a',
+ )
.each($.proxy(this.attachPagerLinkAjax, this));
};
@@ -146,7 +179,7 @@
* @param {HTMLElement} link
* The link element.
*/
- Drupal.views.ajaxView.prototype.attachPagerLinkAjax = function (id, link) {
+ Drupal.views.ajaxView.prototype.attachPagerLinkAjax = function(id, link) {
const $link = $(link);
const viewData = {};
const href = $link.attr('href');
@@ -178,7 +211,7 @@
* @param {string} response.selector
* Selector to use.
*/
- Drupal.AjaxCommands.prototype.viewsScrollTop = function (ajax, response) {
+ Drupal.AjaxCommands.prototype.viewsScrollTop = function(ajax, response) {
// Scroll to the top of the view. This will allow users
// to browse newly loaded content after e.g. clicking a pager
// link.
@@ -193,7 +226,7 @@
}
// Only scroll upward.
if (offset.top - 10 < $(scrollTarget).scrollTop()) {
- $(scrollTarget).animate({ scrollTop: (offset.top - 10) }, 500);
+ $(scrollTarget).animate({ scrollTop: offset.top - 10 }, 500);
}
};
-}(jQuery, Drupal, drupalSettings));
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/modules/views/js/ajax_view.js b/core/modules/views/js/ajax_view.js
index a10eb837e..95a803d7f 100644
--- a/core/modules/views/js/ajax_view.js
+++ b/core/modules/views/js/ajax_view.js
@@ -7,14 +7,30 @@
(function ($, Drupal, drupalSettings) {
Drupal.behaviors.ViewsAjaxView = {};
- Drupal.behaviors.ViewsAjaxView.attach = function () {
- if (drupalSettings && drupalSettings.views && drupalSettings.views.ajaxViews) {
- var ajaxViews = drupalSettings.views.ajaxViews;
+ Drupal.behaviors.ViewsAjaxView.attach = function (context, settings) {
+ if (settings && settings.views && settings.views.ajaxViews) {
+ var ajaxViews = settings.views.ajaxViews;
+
Object.keys(ajaxViews || {}).forEach(function (i) {
Drupal.views.instances[i] = new Drupal.views.ajaxView(ajaxViews[i]);
});
}
};
+ Drupal.behaviors.ViewsAjaxView.detach = function (context, settings, trigger) {
+ if (trigger === 'unload') {
+ if (settings && settings.views && settings.views.ajaxViews) {
+ var ajaxViews = settings.views.ajaxViews;
+
+ Object.keys(ajaxViews || {}).forEach(function (i) {
+ var selector = '.js-view-dom-id-' + ajaxViews[i].view_dom_id;
+ if ($(selector, context).length) {
+ delete Drupal.views.instances[i];
+ delete settings.views.ajaxViews[i];
+ }
+ });
+ }
+ }
+ };
Drupal.views = {};
diff --git a/core/modules/views/js/base.es6.js b/core/modules/views/js/base.es6.js
index 88c5db334..a5e926000 100644
--- a/core/modules/views/js/base.es6.js
+++ b/core/modules/views/js/base.es6.js
@@ -3,7 +3,7 @@
* Some basic behaviors and utility functions for Views.
*/
-(function ($, Drupal, drupalSettings) {
+(function($, Drupal, drupalSettings) {
/**
* @namespace
*/
@@ -18,7 +18,7 @@
* @return {object}
* A map of query parameters.
*/
- Drupal.Views.parseQueryString = function (query) {
+ Drupal.Views.parseQueryString = function(query) {
const args = {};
const pos = query.indexOf('?');
if (pos !== -1) {
@@ -30,7 +30,9 @@
pair = pairs[i].split('=');
// Ignore the 'q' path argument, if present.
if (pair[0] !== 'q' && pair[1]) {
- args[decodeURIComponent(pair[0].replace(/\+/g, ' '))] = decodeURIComponent(pair[1].replace(/\+/g, ' '));
+ args[
+ decodeURIComponent(pair[0].replace(/\+/g, ' '))
+ ] = decodeURIComponent(pair[1].replace(/\+/g, ' '));
}
}
return args;
@@ -47,14 +49,18 @@
* @return {object}
* An object containing `view_args` and `view_path`.
*/
- Drupal.Views.parseViewArgs = function (href, viewPath) {
+ Drupal.Views.parseViewArgs = function(href, viewPath) {
const returnObj = {};
const path = Drupal.Views.getPath(href);
// Get viewPath url without baseUrl portion.
- const viewHref = Drupal.url(viewPath).substring(drupalSettings.path.baseUrl.length);
+ const viewHref = Drupal.url(viewPath).substring(
+ drupalSettings.path.baseUrl.length,
+ );
// Ensure we have a correct path.
if (viewHref && path.substring(0, viewHref.length + 1) === `${viewHref}/`) {
- returnObj.view_args = decodeURIComponent(path.substring(viewHref.length + 1, path.length));
+ returnObj.view_args = decodeURIComponent(
+ path.substring(viewHref.length + 1, path.length),
+ );
returnObj.view_path = path;
}
return returnObj;
@@ -69,7 +75,7 @@
* @return {string}
* The href without the protocol and domain.
*/
- Drupal.Views.pathPortion = function (href) {
+ Drupal.Views.pathPortion = function(href) {
// Remove e.g. http://example.com if present.
const protocol = window.location.protocol;
if (href.substring(0, protocol.length) === protocol) {
@@ -88,7 +94,7 @@
* @return {string}
* An internal path.
*/
- Drupal.Views.getPath = function (href) {
+ Drupal.Views.getPath = function(href) {
href = Drupal.Views.pathPortion(href);
href = href.substring(drupalSettings.path.baseUrl.length, href.length);
// 3 is the length of the '?q=' added to the url without clean urls.
@@ -103,4 +109,4 @@
}
return href;
};
-}(jQuery, Drupal, drupalSettings));
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/modules/views/src/Analyzer.php b/core/modules/views/src/Analyzer.php
index a6a8d56bf..eb09269e0 100644
--- a/core/modules/views/src/Analyzer.php
+++ b/core/modules/views/src/Analyzer.php
@@ -31,7 +31,6 @@ class Analyzer {
$this->moduleHandler = $module_handler;
}
-
/**
* Analyzes a review and return the results.
*
@@ -52,7 +51,8 @@ class Analyzer {
/**
* Formats the analyze result into a message string.
*
- * This is based upon the format of drupal_set_message which uses separate
+ * This is based upon the format of
+ * \Drupal\Core\Messenger\MessengerInterface::addMessage() which uses separate
* boxes for "ok", "warning" and "error".
*/
public function formatMessages(array $messages) {
diff --git a/core/modules/views/src/Controller/ViewAjaxController.php b/core/modules/views/src/Controller/ViewAjaxController.php
index 4aefdabd8..11df6c8d3 100644
--- a/core/modules/views/src/Controller/ViewAjaxController.php
+++ b/core/modules/views/src/Controller/ViewAjaxController.php
@@ -132,7 +132,20 @@ class ViewAjaxController implements ContainerInjectionInterface {
// Remove all of this stuff from the query of the request so it doesn't
// end up in pagers and tablesort URLs.
- foreach (['view_name', 'view_display_id', 'view_args', 'view_path', 'view_dom_id', 'pager_element', 'view_base_path', AjaxResponseSubscriber::AJAX_REQUEST_PARAMETER] as $key) {
+ // @todo Remove this parsing once these are removed from the request in
+ // https://www.drupal.org/node/2504709.
+ foreach ([
+ 'view_name',
+ 'view_display_id',
+ 'view_args',
+ 'view_path',
+ 'view_dom_id',
+ 'pager_element',
+ 'view_base_path',
+ AjaxResponseSubscriber::AJAX_REQUEST_PARAMETER,
+ FormBuilderInterface::AJAX_FORM_REQUEST,
+ MainContentViewSubscriber::WRAPPER_FORMAT,
+ ] as $key) {
$request->query->remove($key);
$request->request->remove($key);
}
@@ -152,6 +165,7 @@ class ViewAjaxController implements ContainerInjectionInterface {
// Add all POST data, because AJAX is always a post and many things,
// such as tablesorts, exposed filters and paging assume GET.
$request_all = $request->request->all();
+ unset($request_all['ajax_page_state']);
$query_all = $request->query->all();
$request->query->replace($request_all + $query_all);
@@ -159,13 +173,7 @@ class ViewAjaxController implements ContainerInjectionInterface {
// @see the redirect.destination service.
$origin_destination = $path;
- // Remove some special parameters you never want to have part of the
- // destination query.
$used_query_parameters = $request->query->all();
- // @todo Remove this parsing once these are removed from the request in
- // https://www.drupal.org/node/2504709.
- unset($used_query_parameters[FormBuilderInterface::AJAX_FORM_REQUEST], $used_query_parameters[MainContentViewSubscriber::WRAPPER_FORMAT], $used_query_parameters['ajax_page_state']);
-
$query = UrlHelper::buildQuery($used_query_parameters);
if ($query != '') {
$origin_destination .= '?' . $query;
diff --git a/core/modules/views/src/DisplayPluginCollection.php b/core/modules/views/src/DisplayPluginCollection.php
index 7c3c736fa..74b69103d 100644
--- a/core/modules/views/src/DisplayPluginCollection.php
+++ b/core/modules/views/src/DisplayPluginCollection.php
@@ -80,7 +80,7 @@ class DisplayPluginCollection extends DefaultLazyPluginCollection {
// display plugin isn't found.
catch (PluginException $e) {
$message = $e->getMessage();
- drupal_set_message(t('@message', ['@message' => $message]), 'warning');
+ \Drupal::messenger()->addWarning(t('@message', ['@message' => $message]));
}
// If no plugin instance has been created, return NULL.
diff --git a/core/modules/views/src/Entity/View.php b/core/modules/views/src/Entity/View.php
index 44007eb7f..11a093844 100644
--- a/core/modules/views/src/Entity/View.php
+++ b/core/modules/views/src/Entity/View.php
@@ -19,6 +19,14 @@ use Drupal\views\ViewEntityInterface;
* @ConfigEntityType(
* id = "view",
* label = @Translation("View", context = "View entity type"),
+ * label_collection = @Translation("Views", context = "View entity type"),
+ * label_singular = @Translation("view", context = "View entity type"),
+ * label_plural = @Translation("views", context = "View entity type"),
+ * label_count = @PluralTranslation(
+ * singular = "@count view",
+ * plural = "@count views",
+ * context = "View entity type",
+ * ),
* admin_permission = "administer views",
* entity_keys = {
* "id" = "id",
@@ -317,6 +325,8 @@ class View extends ConfigEntityBase implements ViewEntityInterface {
* An array containing display handlers of a view.
*
* @deprecated in Drupal 8.3.0, will be removed in Drupal 9.0.0.
+ *
+ * @see https://www.drupal.org/node/2831499
*/
private function fixTableNames(array &$displays) {
// Fix wrong table names for entity revision metadata fields.
@@ -421,7 +431,7 @@ class View extends ConfigEntityBase implements ViewEntityInterface {
'position' => 0,
'display_options' => [],
],
- ]
+ ],
];
}
diff --git a/core/modules/views/src/EntityViewsData.php b/core/modules/views/src/EntityViewsData.php
index a12bd55f2..f5672d3dc 100644
--- a/core/modules/views/src/EntityViewsData.php
+++ b/core/modules/views/src/EntityViewsData.php
@@ -207,7 +207,7 @@ class EntityViewsData implements EntityHandlerInterface, EntityViewsDataInterfac
$data[$base_table]['table']['join'][$data_table] = [
'left_field' => $base_field,
'field' => $base_field,
- 'type' => 'INNER'
+ 'type' => 'INNER',
];
$data[$data_table]['table']['group'] = $this->entityType->getLabel();
$data[$data_table]['table']['provider'] = $this->entityType->getProvider();
@@ -253,6 +253,9 @@ class EntityViewsData implements EntityHandlerInterface, EntityViewsDataInterfac
}
$this->addEntityLinks($data[$base_table]);
+ if ($views_revision_base_table) {
+ $this->addEntityLinks($data[$views_revision_base_table]);
+ }
// Load all typed data definitions of all fields. This should cover each of
// the entity base, revision, data tables.
diff --git a/core/modules/views/src/Form/ViewsExposedForm.php b/core/modules/views/src/Form/ViewsExposedForm.php
index 459708a7b..173dd776f 100644
--- a/core/modules/views/src/Form/ViewsExposedForm.php
+++ b/core/modules/views/src/Form/ViewsExposedForm.php
@@ -103,7 +103,7 @@ class ViewsExposedForm extends FormBase {
}
$form['actions'] = [
- '#type' => 'actions'
+ '#type' => 'actions',
];
$form['actions']['submit'] = [
// Prevent from showing up in \Drupal::request()->query.
@@ -183,7 +183,7 @@ class ViewsExposedForm extends FormBase {
// https://www.drupal.org/node/342316 is resolved.
$checked = Checkboxes::getCheckedCheckboxes($value);
foreach ($checked as $option_id) {
- $view->exposed_raw_input[$option_id] = $value[$option_id];
+ $view->exposed_raw_input[$key][] = $value[$option_id];
}
}
else {
diff --git a/core/modules/views/src/Plugin/Derivative/DefaultWizardDeriver.php b/core/modules/views/src/Plugin/Derivative/DefaultWizardDeriver.php
index f6a045cf3..a2616e8e8 100644
--- a/core/modules/views/src/Plugin/Derivative/DefaultWizardDeriver.php
+++ b/core/modules/views/src/Plugin/Derivative/DefaultWizardDeriver.php
@@ -11,6 +11,7 @@ use Drupal\views\Views;
* The derivatives store all base table plugin information.
*/
class DefaultWizardDeriver extends DeriverBase {
+
/**
* {@inheritdoc}
*/
@@ -25,7 +26,7 @@ class DefaultWizardDeriver extends DeriverBase {
'id' => 'standard',
'base_table' => $table,
'title' => $views_info['table']['base']['title'],
- 'class' => 'Drupal\views\Plugin\views\wizard\Standard'
+ 'class' => 'Drupal\views\Plugin\views\wizard\Standard',
];
}
}
diff --git a/core/modules/views/src/Plugin/Derivative/ViewsExposedFilterBlock.php b/core/modules/views/src/Plugin/Derivative/ViewsExposedFilterBlock.php
index fc2e9b894..f700f16ac 100644
--- a/core/modules/views/src/Plugin/Derivative/ViewsExposedFilterBlock.php
+++ b/core/modules/views/src/Plugin/Derivative/ViewsExposedFilterBlock.php
@@ -91,8 +91,8 @@ class ViewsExposedFilterBlock implements ContainerDeriverInterface {
'config_dependencies' => [
'config' => [
$view->getConfigDependencyName(),
- ]
- ]
+ ],
+ ],
];
$this->derivatives[$delta] += $base_plugin_definition;
}
diff --git a/core/modules/views/src/Plugin/EntityReferenceSelection/ViewsSelection.php b/core/modules/views/src/Plugin/EntityReferenceSelection/ViewsSelection.php
index 9911f61a0..aa791651f 100644
--- a/core/modules/views/src/Plugin/EntityReferenceSelection/ViewsSelection.php
+++ b/core/modules/views/src/Plugin/EntityReferenceSelection/ViewsSelection.php
@@ -26,7 +26,7 @@ class ViewsSelection extends SelectionPluginBase implements ContainerFactoryPlug
/**
* The loaded View object.
*
- * @var \Drupal\views\ViewExecutable;
+ * @var \Drupal\views\ViewExecutable
*/
protected $view;
@@ -132,7 +132,7 @@ class ViewsSelection extends SelectionPluginBase implements ContainerFactoryPlug
// Check that the view is valid and the display still exists.
$this->view = Views::getView($view_name);
if (!$this->view || !$this->view->access($display_name)) {
- drupal_set_message(t('The reference view %view_name cannot be found.', ['%view_name' => $view_name]), 'warning');
+ \Drupal::messenger()->addWarning(t('The reference view %view_name cannot be found.', ['%view_name' => $view_name]));
return FALSE;
}
$this->view->setDisplay($display_name);
diff --git a/core/modules/views/src/Plugin/Menu/ViewsMenuLink.php b/core/modules/views/src/Plugin/Menu/ViewsMenuLink.php
index b4f078fc6..3cbfc84e0 100644
--- a/core/modules/views/src/Plugin/Menu/ViewsMenuLink.php
+++ b/core/modules/views/src/Plugin/Menu/ViewsMenuLink.php
@@ -126,7 +126,6 @@ class ViewsMenuLink extends MenuLinkBase implements ContainerFactoryPluginInterf
return (bool) $this->loadView()->display_handler->getOption('menu')['expanded'];
}
-
/**
* {@inheritdoc}
*/
diff --git a/core/modules/views/src/Plugin/views/BrokenHandlerTrait.php b/core/modules/views/src/Plugin/views/BrokenHandlerTrait.php
index bd7d59cee..992e8e252 100644
--- a/core/modules/views/src/Plugin/views/BrokenHandlerTrait.php
+++ b/core/modules/views/src/Plugin/views/BrokenHandlerTrait.php
@@ -2,7 +2,7 @@
namespace Drupal\views\Plugin\views;
-use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Form\FormStateInterface;
/**
@@ -55,7 +55,7 @@ trait BrokenHandlerTrait {
foreach ($this->definition['original_configuration'] as $key => $value) {
if (is_scalar($value)) {
- $items[] = SafeMarkup::format('@key: @value', ['@key' => $key, '@value' => $value]);
+ $items[] = new FormattableMarkup('@key: @value', ['@key' => $key, '@value' => $value]);
}
}
diff --git a/core/modules/views/src/Plugin/views/HandlerBase.php b/core/modules/views/src/Plugin/views/HandlerBase.php
index a50f9cdab..28dfbfcab 100644
--- a/core/modules/views/src/Plugin/views/HandlerBase.php
+++ b/core/modules/views/src/Plugin/views/HandlerBase.php
@@ -230,9 +230,9 @@ abstract class HandlerBase extends PluginBase implements ViewsHandlerInterface {
default:
return $string;
case 'upper':
- return Unicode::strtoupper($string);
+ return mb_strtoupper($string);
case 'lower':
- return Unicode::strtolower($string);
+ return mb_strtolower($string);
case 'ucfirst':
return Unicode::ucfirst($string);
case 'ucwords':
@@ -313,6 +313,7 @@ abstract class HandlerBase extends PluginBase implements ViewsHandlerInterface {
public function usesGroupBy() {
return TRUE;
}
+
/**
* Provide a form for aggregation settings.
*/
diff --git a/core/modules/views/src/Plugin/views/PluginBase.php b/core/modules/views/src/Plugin/views/PluginBase.php
index 7c17d2bf9..d5614c68d 100644
--- a/core/modules/views/src/Plugin/views/PluginBase.php
+++ b/core/modules/views/src/Plugin/views/PluginBase.php
@@ -400,7 +400,7 @@ abstract class PluginBase extends ComponentPluginBase implements ContainerFactor
'#post_render' => [
function ($children, $elements) {
return Xss::filterAdmin($children);
- }
+ },
],
];
diff --git a/core/modules/views/src/Plugin/views/area/Result.php b/core/modules/views/src/Plugin/views/area/Result.php
index 2bbd23bb2..e8d194773 100644
--- a/core/modules/views/src/Plugin/views/area/Result.php
+++ b/core/modules/views/src/Plugin/views/area/Result.php
@@ -115,11 +115,13 @@ class Result extends AreaPluginBase {
// Send the output.
if (!empty($total) || !empty($this->options['empty'])) {
$output .= Xss::filterAdmin(str_replace(array_keys($replacements), array_values($replacements), $format));
+ // Return as render array.
+ return [
+ '#markup' => $output,
+ ];
}
- // Return as render array.
- return [
- '#markup' => $output,
- ];
+
+ return [];
}
}
diff --git a/core/modules/views/src/Plugin/views/area/View.php b/core/modules/views/src/Plugin/views/area/View.php
index 832e23185..27c8d4875 100644
--- a/core/modules/views/src/Plugin/views/area/View.php
+++ b/core/modules/views/src/Plugin/views/area/View.php
@@ -118,7 +118,7 @@ class View extends AreaPluginBase {
// Check if the view is part of the parent views of this view
$search = "$view_name:$display_id";
if (in_array($search, $this->view->parent_views)) {
- drupal_set_message(t("Recursion detected in view @view display @display.", ['@view' => $view_name, '@display' => $display_id]), 'error');
+ \Drupal::messenger()->addError(t("Recursion detected in view @view display @display.", ['@view' => $view_name, '@display' => $display_id]));
}
else {
if (!empty($this->options['inherit_arguments']) && !empty($this->view->args)) {
diff --git a/core/modules/views/src/Plugin/views/argument/ArgumentPluginBase.php b/core/modules/views/src/Plugin/views/argument/ArgumentPluginBase.php
index 9bcd5ba69..b1e2b1db4 100644
--- a/core/modules/views/src/Plugin/views/argument/ArgumentPluginBase.php
+++ b/core/modules/views/src/Plugin/views/argument/ArgumentPluginBase.php
@@ -398,7 +398,6 @@ abstract class ArgumentPluginBase extends HandlerBase implements CacheableDepend
return $output;
}
-
public function validateOptionsForm(&$form, FormStateInterface $form_state) {
$option_values = &$form_state->getValue('options');
if (empty($option_values)) {
@@ -550,7 +549,7 @@ abstract class ArgumentPluginBase extends HandlerBase implements CacheableDepend
'#type' => 'checkbox',
'#title' => $this->t('Skip default argument for view URL'),
'#default_value' => $this->options['default_argument_skip_url'],
- '#description' => $this->t('Select whether to include this default argument when constructing the URL for this view. Skipping default arguments is useful e.g. in the case of feeds.')
+ '#description' => $this->t('Select whether to include this default argument when constructing the URL for this view. Skipping default arguments is useful e.g. in the case of feeds.'),
];
$form['default_argument_type'] = [
@@ -640,7 +639,7 @@ abstract class ArgumentPluginBase extends HandlerBase implements CacheableDepend
'#default_value' => $this->options['summary']['number_of_records'],
'#options' => [
0 => $this->getSortName(),
- 1 => $this->t('Number of records')
+ 1 => $this->t('Number of records'),
],
'#states' => [
'visible' => [
@@ -721,6 +720,7 @@ abstract class ArgumentPluginBase extends HandlerBase implements CacheableDepend
$info = $this->defaultActions($this->options['validate']['fail']);
return $this->defaultAction($info);
}
+
/**
* Default action: ignore.
*
diff --git a/core/modules/views/src/Plugin/views/argument/NumericArgument.php b/core/modules/views/src/Plugin/views/argument/NumericArgument.php
index 954171a64..f6da02f66 100644
--- a/core/modules/views/src/Plugin/views/argument/NumericArgument.php
+++ b/core/modules/views/src/Plugin/views/argument/NumericArgument.php
@@ -105,16 +105,16 @@ class NumericArgument extends ArgumentPluginBase {
}
$placeholder = $this->placeholder();
- $null_check = empty($this->options['not']) ? '' : "OR $this->tableAlias.$this->realField IS NULL";
+ $null_check = empty($this->options['not']) ? '' : " OR $this->tableAlias.$this->realField IS NULL";
if (count($this->value) > 1) {
$operator = empty($this->options['not']) ? 'IN' : 'NOT IN';
$placeholder .= '[]';
- $this->query->addWhereExpression(0, "$this->tableAlias.$this->realField $operator($placeholder) $null_check", [$placeholder => $this->value]);
+ $this->query->addWhereExpression(0, "$this->tableAlias.$this->realField $operator($placeholder)" . $null_check, [$placeholder => $this->value]);
}
else {
$operator = empty($this->options['not']) ? '=' : '!=';
- $this->query->addWhereExpression(0, "$this->tableAlias.$this->realField $operator $placeholder $null_check", [$placeholder => $this->argument]);
+ $this->query->addWhereExpression(0, "$this->tableAlias.$this->realField $operator $placeholder" . $null_check, [$placeholder => $this->argument]);
}
}
diff --git a/core/modules/views/src/Plugin/views/argument/StringArgument.php b/core/modules/views/src/Plugin/views/argument/StringArgument.php
index 6058d6618..7bde8ff5c 100644
--- a/core/modules/views/src/Plugin/views/argument/StringArgument.php
+++ b/core/modules/views/src/Plugin/views/argument/StringArgument.php
@@ -2,7 +2,6 @@
namespace Drupal\views\Plugin\views\argument;
-use Drupal\Component\Utility\Unicode;
use Drupal\Core\Database\Database;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\Context\ContextDefinition;
@@ -214,7 +213,7 @@ class StringArgument extends ArgumentPluginBase {
// converting the arguments to lowercase.
if ($this->options['case'] != 'none' && Database::getConnection()->databaseType() == 'pgsql') {
foreach ($this->value as $key => $value) {
- $this->value[$key] = Unicode::strtolower($value);
+ $this->value[$key] = mb_strtolower($value);
}
}
diff --git a/core/modules/views/src/Plugin/views/argument_validator/Entity.php b/core/modules/views/src/Plugin/views/argument_validator/Entity.php
index 896d24bf6..befbbe7c2 100644
--- a/core/modules/views/src/Plugin/views/argument_validator/Entity.php
+++ b/core/modules/views/src/Plugin/views/argument_validator/Entity.php
@@ -5,7 +5,7 @@ namespace Drupal\views\Plugin\views\argument_validator;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\Plugin\Context\ContextDefinition;
+use Drupal\Core\Plugin\Context\EntityContextDefinition;
use Drupal\views\Plugin\views\argument\ArgumentPluginBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -233,7 +233,9 @@ class Entity extends ArgumentValidatorPluginBase {
* {@inheritdoc}
*/
public function getContextDefinition() {
- return new ContextDefinition('entity:' . $this->definition['entity_type'], $this->argument->adminLabel(), FALSE);
+ return EntityContextDefinition::fromEntityTypeId($this->definition['entity_type'])
+ ->setLabel($this->argument->adminLabel())
+ ->setRequired(FALSE);
}
}
diff --git a/core/modules/views/src/Plugin/views/cache/CachePluginBase.php b/core/modules/views/src/Plugin/views/cache/CachePluginBase.php
index 14df452a2..280ccf4d1 100644
--- a/core/modules/views/src/Plugin/views/cache/CachePluginBase.php
+++ b/core/modules/views/src/Plugin/views/cache/CachePluginBase.php
@@ -97,6 +97,9 @@ abstract class CachePluginBase extends PluginBase {
* Save data to the cache.
*
* A plugin should override this to provide specialized caching behavior.
+ *
+ * @param $type
+ * The cache type, either 'query', 'result'.
*/
public function cacheSet($type) {
switch ($type) {
@@ -119,6 +122,12 @@ abstract class CachePluginBase extends PluginBase {
* Retrieve data from the cache.
*
* A plugin should override this to provide specialized caching behavior.
+ *
+ * @param $type
+ * The cache type, either 'query', 'result'.
+ *
+ * @return bool
+ * TRUE if data has been taken from the cache, otherwise FALSE.
*/
public function cacheGet($type) {
$cutoff = $this->cacheExpire($type);
@@ -154,19 +163,22 @@ abstract class CachePluginBase extends PluginBase {
/**
* Post process any rendered data.
*
- * This can be valuable to be able to cache a view and still have some level of
- * dynamic output. In an ideal world, the actual output will include HTML
+ * This can be valuable to be able to cache a view and still have some level
+ * of dynamic output. In an ideal world, the actual output will include HTML
* comment based tokens, and then the post process can replace those tokens.
*
* Example usage. If it is known that the view is a node view and that the
* primary field will be a nid, you can do something like this:
- *
- *
+ * @code
+ *
+ * @endcode
*
* And then in the post render, create an array with the text that should
* go there:
*
- * strtr($output, array('', 'output for FIELD of nid 1');
+ * @code
+ * strtr($output, array('', 'output for FIELD of nid 1');
+ * @endcode
*
* All of the cached result data will be available in $view->result, as well,
* so all ids used in the query should be discoverable.
diff --git a/core/modules/views/src/Plugin/views/cache/None.php b/core/modules/views/src/Plugin/views/cache/None.php
index d05abd2ad..fc9b58b23 100644
--- a/core/modules/views/src/Plugin/views/cache/None.php
+++ b/core/modules/views/src/Plugin/views/cache/None.php
@@ -19,7 +19,6 @@ class None extends CachePluginBase {
return $this->t('None');
}
-
/**
* Overrides \Drupal\views\Plugin\views\cache\CachePluginBase::cacheGet().
*
diff --git a/core/modules/views/src/Plugin/views/display/Block.php b/core/modules/views/src/Plugin/views/display/Block.php
index 5e833d752..e210ee323 100644
--- a/core/modules/views/src/Plugin/views/display/Block.php
+++ b/core/modules/views/src/Plugin/views/display/Block.php
@@ -2,6 +2,8 @@
namespace Drupal\views\Plugin\views\display;
+use Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface;
+use Drupal\Core\Block\BlockManagerInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\views\Plugin\Block\ViewsBlock;
@@ -42,6 +44,13 @@ class Block extends DisplayPluginBase {
*/
protected $entityManager;
+ /**
+ * The block manager.
+ *
+ * @var \Drupal\Core\Block\BlockManagerInterface
+ */
+ protected $blockManager;
+
/**
* Constructs a new Block instance.
*
@@ -53,11 +62,14 @@ class Block extends DisplayPluginBase {
* The plugin implementation definition.
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
+ * @param \Drupal\Core\Block\BlockManagerInterface $block_manager
+ * The block manager.
*/
- public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager) {
+ public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager, BlockManagerInterface $block_manager) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->entityManager = $entity_manager;
+ $this->blockManager = $block_manager;
}
/**
@@ -68,7 +80,8 @@ class Block extends DisplayPluginBase {
$configuration,
$plugin_id,
$plugin_definition,
- $container->get('entity.manager')
+ $container->get('entity.manager'),
+ $container->get('plugin.manager.block')
);
}
@@ -364,6 +377,9 @@ class Block extends DisplayPluginBase {
$block->delete();
}
}
+ if ($this->blockManager instanceof CachedDiscoveryInterface) {
+ $this->blockManager->clearCachedDefinitions();
+ }
}
}
diff --git a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
index 012dec4f2..4f1cb2ecd 100644
--- a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
+++ b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
@@ -4,7 +4,7 @@ namespace Drupal\views\Plugin\views\display;
use Drupal\Component\Plugin\DependentPluginInterface;
use Drupal\Component\Utility\Html;
-use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Cache\CacheableDependencyInterface;
@@ -962,7 +962,6 @@ abstract class DisplayPluginBase extends PluginBase implements DisplayPluginInte
return $this->dependencies;
}
-
/**
* {@inheritdoc}
*/
@@ -1022,7 +1021,7 @@ abstract class DisplayPluginBase extends PluginBase implements DisplayPluginInte
}
if (!empty($class)) {
- $text = SafeMarkup::format('@text', ['@text' => $text]);
+ $text = new FormattableMarkup('@text', ['@text' => $text]);
}
if (empty($title)) {
@@ -1033,13 +1032,13 @@ abstract class DisplayPluginBase extends PluginBase implements DisplayPluginInte
'js' => 'nojs',
'view' => $this->view->storage->id(),
'display_id' => $this->display['id'],
- 'type' => $section
+ 'type' => $section,
], [
'attributes' => [
'class' => ['views-ajax-link', $class],
'title' => $title,
- 'id' => Html::getUniqueId('views-' . $this->display['id'] . '-' . $section)
- ]
+ 'id' => Html::getUniqueId('views-' . $this->display['id'] . '-' . $section),
+ ],
]));
}
@@ -2587,7 +2586,7 @@ abstract class DisplayPluginBase extends PluginBase implements DisplayPluginInte
public function getPagerText() {
return [
'items per page title' => $this->t('Items to display'),
- 'items per page description' => $this->t('Enter 0 for no limit.')
+ 'items per page description' => $this->t('Enter 0 for no limit.'),
];
}
diff --git a/core/modules/views/src/Plugin/views/display/DisplayPluginInterface.php b/core/modules/views/src/Plugin/views/display/DisplayPluginInterface.php
index f8630af08..80e478316 100644
--- a/core/modules/views/src/Plugin/views/display/DisplayPluginInterface.php
+++ b/core/modules/views/src/Plugin/views/display/DisplayPluginInterface.php
@@ -457,7 +457,6 @@ interface DisplayPluginInterface {
*/
public function execute();
-
/**
* Builds a basic render array which can be properly render cached.
*
diff --git a/core/modules/views/src/Plugin/views/display/Embed.php b/core/modules/views/src/Plugin/views/display/Embed.php
index 6abf9d660..3b82acab0 100644
--- a/core/modules/views/src/Plugin/views/display/Embed.php
+++ b/core/modules/views/src/Plugin/views/display/Embed.php
@@ -20,6 +20,11 @@ namespace Drupal\views\Plugin\views\display;
*/
class Embed extends DisplayPluginBase {
+ /**
+ * {@inheritdoc}
+ */
+ protected $usesAttachments = TRUE;
+
/**
* {@inheritdoc}
*/
diff --git a/core/modules/views/src/Plugin/views/display/EntityReference.php b/core/modules/views/src/Plugin/views/display/EntityReference.php
index eb5f04cfe..699169958 100644
--- a/core/modules/views/src/Plugin/views/display/EntityReference.php
+++ b/core/modules/views/src/Plugin/views/display/EntityReference.php
@@ -54,9 +54,6 @@ class EntityReference extends DisplayPluginBase {
$options['row']['contains']['type'] = ['default' => 'entity_reference'];
$options['defaults']['default']['row'] = FALSE;
- // Make sure the query is not cached.
- $options['defaults']['default']['cache'] = FALSE;
-
// Set the display title to an empty string (not used in this display type).
$options['title']['default'] = '';
$options['defaults']['default']['title'] = FALSE;
@@ -65,13 +62,12 @@ class EntityReference extends DisplayPluginBase {
}
/**
- * Overrides \Drupal\views\Plugin\views\display\DisplayPluginBase::optionsSummary().
- *
- * Disable 'cache' and 'title' so it won't be changed.
+ * {@inheritdoc}
*/
public function optionsSummary(&$categories, &$options) {
parent::optionsSummary($categories, $options);
- unset($options['query']);
+ // Disable 'title' so it won't be changed from the default set in
+ // \Drupal\views\Plugin\views\display\EntityReference::defineOptions.
unset($options['title']);
}
diff --git a/core/modules/views/src/Plugin/views/display/Feed.php b/core/modules/views/src/Plugin/views/display/Feed.php
index c664c053e..05169b9a2 100644
--- a/core/modules/views/src/Plugin/views/display/Feed.php
+++ b/core/modules/views/src/Plugin/views/display/Feed.php
@@ -73,7 +73,6 @@ class Feed extends PathPluginBase implements ResponseDisplayPluginInterface {
return $response;
}
-
/**
* {@inheritdoc}
*/
@@ -141,7 +140,7 @@ class Feed extends PathPluginBase implements ResponseDisplayPluginInterface {
// Overrides for standard stuff.
$options['style']['contains']['type']['default'] = 'rss';
- $options['style']['contains']['options']['default'] = ['description' => ''];
+ $options['style']['contains']['options']['default'] = ['description' => ''];
$options['sitename_title']['default'] = FALSE;
$options['row']['contains']['type']['default'] = 'rss_fields';
$options['defaults']['default']['style'] = FALSE;
diff --git a/core/modules/views/src/Plugin/views/display/Page.php b/core/modules/views/src/Plugin/views/display/Page.php
index baf143444..23409ec56 100644
--- a/core/modules/views/src/Plugin/views/display/Page.php
+++ b/core/modules/views/src/Plugin/views/display/Page.php
@@ -259,7 +259,7 @@ class Page extends PathPluginBase {
'none' => $this->t('No menu entry'),
'normal' => $this->t('Normal menu entry'),
'tab' => $this->t('Menu tab'),
- 'default tab' => $this->t('Default menu tab')
+ 'default tab' => $this->t('Default menu tab'),
],
'#default_value' => $menu['type'],
];
@@ -532,7 +532,7 @@ class Page extends PathPluginBase {
public function getPagerText() {
return [
'items per page title' => $this->t('Items per page'),
- 'items per page description' => $this->t('Enter 0 for no limit.')
+ 'items per page description' => $this->t('Enter 0 for no limit.'),
];
}
diff --git a/core/modules/views/src/Plugin/views/field/BulkForm.php b/core/modules/views/src/Plugin/views/field/BulkForm.php
index a253fc3ad..3ce092b98 100644
--- a/core/modules/views/src/Plugin/views/field/BulkForm.php
+++ b/core/modules/views/src/Plugin/views/field/BulkForm.php
@@ -8,6 +8,7 @@ use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\RevisionableInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Routing\RedirectDestinationTrait;
use Drupal\Core\TypedData\TranslatableInterface;
use Drupal\views\Entity\Render\EntityTranslationRenderTrait;
@@ -56,6 +57,13 @@ class BulkForm extends FieldPluginBase implements CacheableDependencyInterface {
*/
protected $languageManager;
+ /**
+ * The messenger.
+ *
+ * @var \Drupal\Core\Messenger\MessengerInterface
+ */
+ protected $messenger;
+
/**
* Constructs a new BulkForm object.
*
@@ -69,13 +77,18 @@ class BulkForm extends FieldPluginBase implements CacheableDependencyInterface {
* The entity manager.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager.
+ * @param \Drupal\Core\Messenger\MessengerInterface $messenger
+ * The messenger.
+ *
+ * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
*/
- public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager) {
+ public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager, MessengerInterface $messenger) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->entityManager = $entity_manager;
$this->actionStorage = $entity_manager->getStorage('action');
$this->languageManager = $language_manager;
+ $this->messenger = $messenger;
}
/**
@@ -87,7 +100,8 @@ class BulkForm extends FieldPluginBase implements CacheableDependencyInterface {
$plugin_id,
$plugin_definition,
$container->get('entity.manager'),
- $container->get('language_manager')
+ $container->get('language_manager'),
+ $container->get('messenger')
);
}
@@ -356,11 +370,11 @@ class BulkForm extends FieldPluginBase implements CacheableDependencyInterface {
// Skip execution if the user did not have access.
if (!$action->getPlugin()->access($entity, $this->view->getUser())) {
- $this->drupalSetMessage($this->t('No access to execute %action on the @entity_type_label %entity_label.', [
+ $this->messenger->addError($this->t('No access to execute %action on the @entity_type_label %entity_label.', [
'%action' => $action->label(),
'@entity_type_label' => $entity->getEntityType()->getLabel(),
- '%entity_label' => $entity->label()
- ]), 'error');
+ '%entity_label' => $entity->label(),
+ ]));
continue;
}
@@ -382,7 +396,7 @@ class BulkForm extends FieldPluginBase implements CacheableDependencyInterface {
// Don't display the message unless there are some elements affected and
// there is no confirmation form.
if ($count) {
- drupal_set_message($this->formatPlural($count, '%action was applied to @count item.', '%action was applied to @count items.', [
+ $this->messenger->addStatus($this->formatPlural($count, '%action was applied to @count item.', '%action was applied to @count items.', [
'%action' => $action->label(),
]));
}
@@ -426,13 +440,6 @@ class BulkForm extends FieldPluginBase implements CacheableDependencyInterface {
return FALSE;
}
- /**
- * Wraps drupal_set_message().
- */
- protected function drupalSetMessage($message = NULL, $type = 'status', $repeat = FALSE) {
- drupal_set_message($message, $type, $repeat);
- }
-
/**
* Calculates a bulk form key.
*
diff --git a/core/modules/views/src/Plugin/views/field/EntityField.php b/core/modules/views/src/Plugin/views/field/EntityField.php
index ee2882c3b..faf8da14b 100644
--- a/core/modules/views/src/Plugin/views/field/EntityField.php
+++ b/core/modules/views/src/Plugin/views/field/EntityField.php
@@ -408,10 +408,10 @@ class EntityField extends FieldPluginBase implements CacheableDependencyInterfac
];
$options['multi_type'] = [
- 'default' => 'separator'
+ 'default' => 'separator',
];
$options['separator'] = [
- 'default' => ', '
+ 'default' => ', ',
];
$options['field_api_classes'] = [
diff --git a/core/modules/views/src/Plugin/views/field/EntityOperations.php b/core/modules/views/src/Plugin/views/field/EntityOperations.php
index 6f0882979..d31cd1ea3 100644
--- a/core/modules/views/src/Plugin/views/field/EntityOperations.php
+++ b/core/modules/views/src/Plugin/views/field/EntityOperations.php
@@ -158,6 +158,7 @@ class EntityOperations extends FieldPluginBase {
protected function getLanguageManager() {
return $this->languageManager;
}
+
/**
* {@inheritdoc}
*/
diff --git a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php
index 78c6a1281..cbae7e0b1 100644
--- a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php
+++ b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php
@@ -4,7 +4,6 @@ namespace Drupal\views\Plugin\views\field;
use Drupal\Component\Utility\Html;
use Drupal\Component\Render\MarkupInterface;
-use Drupal\Component\Utility\Unicode;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Component\Utility\Xss;
use Drupal\Core\Form\FormStateInterface;
@@ -186,7 +185,13 @@ abstract class FieldPluginBase extends HandlerBase implements FieldHandlerInterf
}
if (empty($table_alias)) {
- debug(t('Handler @handler tried to add additional_field @identifier but @table could not be added!', ['@handler' => $this->definition['id'], '@identifier' => $identifier, '@table' => $info['table']]));
+ trigger_error(sprintf(
+ "Handler % tried to add additional_field %s but % could not be added!",
+ $this->definition['id'],
+ $identifier,
+ $info['table']
+ ), E_USER_WARNING);
+
$this->aliases[$identifier] = 'broken';
continue;
}
@@ -312,7 +317,7 @@ abstract class FieldPluginBase extends HandlerBase implements FieldHandlerInterf
// @todo Add possible html5 elements.
$elements = [
'' => $this->t('- Use default -'),
- '0' => $this->t('- None -')
+ '0' => $this->t('- None -'),
];
$elements += \Drupal::config('views.settings')->get('field_rewrite_elements');
}
@@ -1288,7 +1293,7 @@ abstract class FieldPluginBase extends HandlerBase implements FieldHandlerInterf
$base_path = base_path();
// Checks whether the path starts with the base_path.
if (strpos($more_link_path, $base_path) === 0) {
- $more_link_path = Unicode::substr($more_link_path, Unicode::strlen($base_path));
+ $more_link_path = mb_substr($more_link_path, mb_strlen($base_path));
}
// @todo Views should expect and store a leading /. See
@@ -1388,7 +1393,7 @@ abstract class FieldPluginBase extends HandlerBase implements FieldHandlerInterf
];
$alter += [
- 'path' => NULL
+ 'path' => NULL,
];
$path = $alter['path'];
@@ -1792,8 +1797,8 @@ abstract class FieldPluginBase extends HandlerBase implements FieldHandlerInterf
* The trimmed string.
*/
public static function trimText($alter, $value) {
- if (Unicode::strlen($value) > $alter['max_length']) {
- $value = Unicode::substr($value, 0, $alter['max_length']);
+ if (mb_strlen($value) > $alter['max_length']) {
+ $value = mb_substr($value, 0, $alter['max_length']);
if (!empty($alter['word_boundary'])) {
$regex = "(.*)\b.+";
if (function_exists('mb_ereg')) {
diff --git a/core/modules/views/src/Plugin/views/field/MachineName.php b/core/modules/views/src/Plugin/views/field/MachineName.php
index ea6a0c4be..b8ba62a4b 100644
--- a/core/modules/views/src/Plugin/views/field/MachineName.php
+++ b/core/modules/views/src/Plugin/views/field/MachineName.php
@@ -18,7 +18,9 @@ use Drupal\views\ResultRow;
class MachineName extends FieldPluginBase {
/**
- * @var array Stores the available options.
+ * Stores the available options.
+ *
+ * @var array
*/
protected $valueOptions;
diff --git a/core/modules/views/src/Plugin/views/field/PrerenderList.php b/core/modules/views/src/Plugin/views/field/PrerenderList.php
index f27973ebb..4ed091f81 100644
--- a/core/modules/views/src/Plugin/views/field/PrerenderList.php
+++ b/core/modules/views/src/Plugin/views/field/PrerenderList.php
@@ -78,8 +78,8 @@ abstract class PrerenderList extends FieldPluginBase implements MultiItemsFieldH
'#template' => '{{ items|safe_join(separator) }}',
'#context' => [
'items' => $items,
- 'separator' => $this->sanitizeValue($this->options['separator'], 'xss_admin')
- ]
+ 'separator' => $this->sanitizeValue($this->options['separator'], 'xss_admin'),
+ ],
];
}
else {
diff --git a/core/modules/views/src/Plugin/views/filter/BooleanOperator.php b/core/modules/views/src/Plugin/views/filter/BooleanOperator.php
index fa0d730f8..81a31880c 100644
--- a/core/modules/views/src/Plugin/views/filter/BooleanOperator.php
+++ b/core/modules/views/src/Plugin/views/filter/BooleanOperator.php
@@ -48,7 +48,6 @@ class BooleanOperator extends FilterPluginBase {
// Whether to accept NULL as a false value or not
public $accept_null = FALSE;
-
/**
* {@inheritdoc}
*/
diff --git a/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php b/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php
index fd273ddfa..5b35b6ad3 100644
--- a/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php
+++ b/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php
@@ -656,7 +656,6 @@ abstract class FilterPluginBase extends HandlerBase implements CacheableDependen
return FALSE;
}
-
/**
* Validate the build group options form.
*/
@@ -835,7 +834,6 @@ abstract class FilterPluginBase extends HandlerBase implements CacheableDependen
}
}
-
/**
* Render our chunk of the exposed filter form when selecting
*
@@ -1105,7 +1103,7 @@ abstract class FilterPluginBase extends HandlerBase implements CacheableDependen
'#required' => TRUE,
'#attributes' => [
'class' => ['default-radios'],
- ]
+ ],
];
// From all groups, let chose which is the default.
$form['group_info']['default_group_multiple'] = [
@@ -1114,7 +1112,7 @@ abstract class FilterPluginBase extends HandlerBase implements CacheableDependen
'#default_value' => $this->options['group_info']['default_group_multiple'],
'#attributes' => [
'class' => ['default-checkboxes'],
- ]
+ ],
];
$form['group_info']['add_group'] = [
@@ -1165,7 +1163,6 @@ abstract class FilterPluginBase extends HandlerBase implements CacheableDependen
$form_state->get('force_build_group_options', TRUE);
}
-
/**
* Make some translations to a form item to make it more suitable to
* exposing.
@@ -1207,7 +1204,6 @@ abstract class FilterPluginBase extends HandlerBase implements CacheableDependen
}
}
-
/**
* Sanitizes the HTML select element's options.
*
diff --git a/core/modules/views/src/Plugin/views/filter/GroupByNumeric.php b/core/modules/views/src/Plugin/views/filter/GroupByNumeric.php
index 577b2dfde..d68970bd5 100644
--- a/core/modules/views/src/Plugin/views/filter/GroupByNumeric.php
+++ b/core/modules/views/src/Plugin/views/filter/GroupByNumeric.php
@@ -20,6 +20,7 @@ class GroupByNumeric extends NumericFilter {
$this->{$info[$this->operator]['method']}($field);
}
}
+
protected function opBetween($field) {
$placeholder_min = $this->placeholder();
$placeholder_max = $this->placeholder();
diff --git a/core/modules/views/src/Plugin/views/filter/InOperator.php b/core/modules/views/src/Plugin/views/filter/InOperator.php
index 1e6682d69..e0add0fcb 100644
--- a/core/modules/views/src/Plugin/views/filter/InOperator.php
+++ b/core/modules/views/src/Plugin/views/filter/InOperator.php
@@ -369,7 +369,7 @@ class InOperator extends FilterPluginBase {
if ($values !== '') {
$values .= ', ';
}
- if (Unicode::strlen($values) > 8) {
+ if (mb_strlen($values) > 8) {
$values = Unicode::truncate($values, 8, FALSE, TRUE);
break;
}
diff --git a/core/modules/views/src/Plugin/views/filter/ManyToOne.php b/core/modules/views/src/Plugin/views/filter/ManyToOne.php
index 86eb1f336..1b3810cfe 100644
--- a/core/modules/views/src/Plugin/views/filter/ManyToOne.php
+++ b/core/modules/views/src/Plugin/views/filter/ManyToOne.php
@@ -103,6 +103,7 @@ class ManyToOne extends InOperator {
}
protected $valueFormType = 'select';
+
protected function valueForm(&$form, FormStateInterface $form_state) {
parent::valueForm($form, $form_state);
diff --git a/core/modules/views/src/Plugin/views/filter/NumericFilter.php b/core/modules/views/src/Plugin/views/filter/NumericFilter.php
index cdfbb06bb..d2a6ab099 100644
--- a/core/modules/views/src/Plugin/views/filter/NumericFilter.php
+++ b/core/modules/views/src/Plugin/views/filter/NumericFilter.php
@@ -190,6 +190,7 @@ class NumericFilter extends FilterPluginBase {
return $options;
}
+
/**
* Provide a simple textfield for equality
*/
@@ -299,7 +300,7 @@ class NumericFilter extends FilterPluginBase {
// Ensure there is something in the 'value'.
$form['value'] = [
'#type' => 'value',
- '#value' => NULL
+ '#value' => NULL,
];
}
}
diff --git a/core/modules/views/src/Plugin/views/filter/StringFilter.php b/core/modules/views/src/Plugin/views/filter/StringFilter.php
index f6b7e2482..f74451927 100644
--- a/core/modules/views/src/Plugin/views/filter/StringFilter.php
+++ b/core/modules/views/src/Plugin/views/filter/StringFilter.php
@@ -257,7 +257,7 @@ class StringFilter extends FilterPluginBase {
// Ensure there is something in the 'value'.
$form['value'] = [
'#type' => 'value',
- '#value' => NULL
+ '#value' => NULL,
];
}
}
diff --git a/core/modules/views/src/Plugin/views/join/JoinPluginBase.php b/core/modules/views/src/Plugin/views/join/JoinPluginBase.php
index ccf40f64e..a0bbedf73 100644
--- a/core/modules/views/src/Plugin/views/join/JoinPluginBase.php
+++ b/core/modules/views/src/Plugin/views/join/JoinPluginBase.php
@@ -226,7 +226,7 @@ class JoinPluginBase extends PluginBase implements JoinPluginInterface {
// Merge in some default values.
$configuration += [
'type' => 'LEFT',
- 'extra_operator' => 'AND'
+ 'extra_operator' => 'AND',
];
$this->configuration = $configuration;
@@ -281,6 +281,7 @@ class JoinPluginBase extends PluginBase implements JoinPluginInterface {
$select_query->addJoin($this->type, $right_table, $table['alias'], $condition, $arguments);
}
+
/**
* Adds the extras to the join condition.
*
diff --git a/core/modules/views/src/Plugin/views/pager/SqlBase.php b/core/modules/views/src/Plugin/views/pager/SqlBase.php
index d683202d1..5195acff0 100644
--- a/core/modules/views/src/Plugin/views/pager/SqlBase.php
+++ b/core/modules/views/src/Plugin/views/pager/SqlBase.php
@@ -233,7 +233,6 @@ abstract class SqlBase extends PagerPluginBase implements CacheableDependencyInt
$this->view->query->setOffset($offset);
}
-
/**
* Set the current page.
*
diff --git a/core/modules/views/src/Plugin/views/query/Sql.php b/core/modules/views/src/Plugin/views/query/Sql.php
index 09a754bb4..03749a329 100644
--- a/core/modules/views/src/Plugin/views/query/Sql.php
+++ b/core/modules/views/src/Plugin/views/query/Sql.php
@@ -8,6 +8,7 @@ use Drupal\Core\Database\Database;
use Drupal\Core\Database\Query\Condition;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Messenger\MessengerInterface;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
use Drupal\Core\Database\DatabaseExceptionWrapper;
use Drupal\views\Plugin\views\join\JoinPluginBase;
@@ -130,6 +131,13 @@ class Sql extends QueryPluginBase {
*/
protected $dateSql;
+ /**
+ * The messenger.
+ *
+ * @var \Drupal\Core\Messenger\MessengerInterface
+ */
+ protected $messenger;
+
/**
* Constructs a Sql object.
*
@@ -143,21 +151,28 @@ class Sql extends QueryPluginBase {
* The entity type manager.
* @param \Drupal\views\Plugin\views\query\DateSqlInterface $date_sql
* The database-specific date handler.
+ * @param \Drupal\Core\Messenger\MessengerInterface $messenger
+ * The messenger.
*/
- public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, DateSqlInterface $date_sql) {
+ public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, DateSqlInterface $date_sql, MessengerInterface $messenger) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->entityTypeManager = $entity_type_manager;
$this->dateSql = $date_sql;
+ $this->messenger = $messenger;
}
+ /**
+ * {@inheritdoc}
+ */
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity_type.manager'),
- $container->get('views.date_sql')
+ $container->get('views.date_sql'),
+ $container->get('messenger')
);
}
@@ -173,7 +188,7 @@ class Sql extends QueryPluginBase {
'link' => NULL,
'table' => $base_table,
'alias' => $base_table,
- 'base' => $base_table
+ 'base' => $base_table,
];
// init the table queue with our primary table.
@@ -1030,7 +1045,7 @@ class Sql extends QueryPluginBase {
$this->orderby[] = [
'field' => $as,
- 'direction' => strtoupper($order)
+ 'direction' => strtoupper($order),
];
}
@@ -1498,7 +1513,7 @@ class Sql extends QueryPluginBase {
if (!empty($this->limit) || !empty($this->offset)) {
// We can't have an offset without a limit, so provide a very large limit instead.
- $limit = intval(!empty($this->limit) ? $this->limit : 999999);
+ $limit = intval(!empty($this->limit) ? $this->limit : 999999);
$offset = intval(!empty($this->offset) ? $this->offset : 0);
$query->range($offset, $limit);
}
@@ -1522,7 +1537,7 @@ class Sql extends QueryPluginBase {
catch (DatabaseExceptionWrapper $e) {
$view->result = [];
if (!empty($view->live_preview)) {
- drupal_set_message($e->getMessage(), 'error');
+ $this->messenger->addError($e->getMessage());
}
else {
throw new DatabaseExceptionWrapper("Exception in {$view->storage->label()}[{$view->storage->id()}]: {$e->getMessage()}");
@@ -1778,7 +1793,7 @@ class Sql extends QueryPluginBase {
'filter' => 'groupby_numeric',
'sort' => 'groupby_numeric',
],
- ]
+ ],
];
}
diff --git a/core/modules/views/src/Plugin/views/relationship/EntityReverse.php b/core/modules/views/src/Plugin/views/relationship/EntityReverse.php
index 568bcd2f3..1510d22e7 100644
--- a/core/modules/views/src/Plugin/views/relationship/EntityReverse.php
+++ b/core/modules/views/src/Plugin/views/relationship/EntityReverse.php
@@ -53,7 +53,7 @@ class EntityReverse extends RelationshipPluginBase {
'left_field' => $left_field,
'table' => $this->definition['field table'],
'field' => $this->definition['field field'],
- 'adjusted' => TRUE
+ 'adjusted' => TRUE,
];
if (!empty($this->options['required'])) {
$first['type'] = 'INNER';
@@ -80,7 +80,7 @@ class EntityReverse extends RelationshipPluginBase {
'left_field' => 'entity_id',
'table' => $this->definition['base'],
'field' => $this->definition['base field'],
- 'adjusted' => TRUE
+ 'adjusted' => TRUE,
];
if (!empty($this->options['required'])) {
diff --git a/core/modules/views/src/Plugin/views/style/Opml.php b/core/modules/views/src/Plugin/views/style/Opml.php
index 016405e4c..02b1e3d90 100644
--- a/core/modules/views/src/Plugin/views/style/Opml.php
+++ b/core/modules/views/src/Plugin/views/style/Opml.php
@@ -56,8 +56,8 @@ class Opml extends StylePluginBase {
*/
public function render() {
if (empty($this->view->rowPlugin)) {
- debug('Drupal\views\Plugin\views\style\Opml: Missing row plugin');
- return;
+ trigger_error('Drupal\views\Plugin\views\style\Opml: Missing row plugin', E_WARNING);
+ return [];
}
$rows = [];
diff --git a/core/modules/views/src/Plugin/views/style/Rss.php b/core/modules/views/src/Plugin/views/style/Rss.php
index 96d1deaea..6693c4863 100644
--- a/core/modules/views/src/Plugin/views/style/Rss.php
+++ b/core/modules/views/src/Plugin/views/style/Rss.php
@@ -98,7 +98,7 @@ class Rss extends StylePluginBase {
public function render() {
if (empty($this->view->rowPlugin)) {
- debug('Drupal\views\Plugin\views\style\Rss: Missing row plugin');
+ trigger_error('Drupal\views\Plugin\views\style\Rss: Missing row plugin', E_WARNING);
return [];
}
$rows = [];
diff --git a/core/modules/views/src/Plugin/views/style/StylePluginBase.php b/core/modules/views/src/Plugin/views/style/StylePluginBase.php
index 89e3dfbd8..740ef5323 100644
--- a/core/modules/views/src/Plugin/views/style/StylePluginBase.php
+++ b/core/modules/views/src/Plugin/views/style/StylePluginBase.php
@@ -457,8 +457,8 @@ abstract class StylePluginBase extends PluginBase {
*/
public function render() {
if ($this->usesRowPlugin() && empty($this->view->rowPlugin)) {
- debug('Drupal\views\Plugin\views\style\StylePluginBase: Missing row plugin');
- return;
+ trigger_error('Drupal\views\Plugin\views\style\StylePluginBase: Missing row plugin', E_WARNING);
+ return [];
}
// Group the rows according to the grouping instructions, if specified.
diff --git a/core/modules/views/src/Plugin/views/wizard/WizardPluginBase.php b/core/modules/views/src/Plugin/views/wizard/WizardPluginBase.php
index 0b3dcc9a1..890432aa8 100644
--- a/core/modules/views/src/Plugin/views/wizard/WizardPluginBase.php
+++ b/core/modules/views/src/Plugin/views/wizard/WizardPluginBase.php
@@ -177,7 +177,7 @@ abstract class WizardPluginBase extends PluginBase implements WizardInterface {
'plugin_id' => 'boolean',
'entity_type' => $this->entityTypeId,
'entity_field' => $field_name,
- ]
+ ],
] + $this->filters;
}
diff --git a/core/modules/views/src/Tests/FieldApiDataTest.php b/core/modules/views/src/Tests/FieldApiDataTest.php
index d2d035cfc..b20a8712e 100644
--- a/core/modules/views/src/Tests/FieldApiDataTest.php
+++ b/core/modules/views/src/Tests/FieldApiDataTest.php
@@ -45,7 +45,7 @@ class FieldApiDataTest extends FieldTestBase {
'field_name' => $field_names[0],
'entity_type' => 'node',
'bundle' => 'page',
- 'label' => 'GiraffeA" label'
+ 'label' => 'GiraffeA" label',
];
FieldConfig::create($field)->save();
@@ -55,7 +55,7 @@ class FieldApiDataTest extends FieldTestBase {
'field_name' => $field_names[0],
'entity_type' => 'node',
'bundle' => 'article',
- 'label' => 'GiraffeB" label'
+ 'label' => 'GiraffeB" label',
])->save();
// Now create some example nodes/users for the view result.
@@ -230,7 +230,7 @@ class FieldApiDataTest extends FieldTestBase {
'field_name' => $this->fieldStorages[0]->getName(),
'entity_type' => 'node',
'bundle' => 'news',
- 'label' => 'GiraffeB" label'
+ 'label' => 'GiraffeB" label',
])->save();
$this->container->get('views.views_data')->clear();
$data = $this->getViewsData();
diff --git a/core/modules/views/src/Tests/ViewAjaxTest.php b/core/modules/views/src/Tests/ViewAjaxTest.php
deleted file mode 100644
index 820d9e2bc..000000000
--- a/core/modules/views/src/Tests/ViewAjaxTest.php
+++ /dev/null
@@ -1,74 +0,0 @@
-enableViewsTestModule();
- }
-
- /**
- * Tests an ajax view.
- */
- public function testAjaxView() {
- $this->drupalGet('test_ajax_view');
-
- $drupal_settings = $this->getDrupalSettings();
- $this->assertTrue(isset($drupal_settings['views']['ajax_path']), 'The Ajax callback path is set in drupalSettings.');
- $this->assertEqual(count($drupal_settings['views']['ajaxViews']), 1);
- $view_entry = array_keys($drupal_settings['views']['ajaxViews'])[0];
- $this->assertEqual($drupal_settings['views']['ajaxViews'][$view_entry]['view_name'], 'test_ajax_view', 'The view\'s ajaxViews array entry has the correct \'view_name\' key.');
- $this->assertEqual($drupal_settings['views']['ajaxViews'][$view_entry]['view_display_id'], 'page_1', 'The view\'s ajaxViews array entry has the correct \'view_display_id\' key.');
-
- $data = [];
- $data['view_name'] = 'test_ajax_view';
- $data['view_display_id'] = 'test_ajax_view';
-
- $post = [
- 'view_name' => 'test_ajax_view',
- 'view_display_id' => 'page_1',
- ];
- $post += $this->getAjaxPageStatePostData();
- $response = $this->drupalPost('views/ajax', '', $post, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]);
- $data = Json::decode($response);
-
- $this->assertTrue(isset($data[0]['settings']['views']['ajaxViews']));
-
- // Ensure that the view insert command is part of the result.
- $this->assertEqual($data[1]['command'], 'insert');
- $this->assertTrue(strpos($data[1]['selector'], '.js-view-dom-id-') === 0);
-
- $this->setRawContent($data[1]['data']);
- $result = $this->xpath('//div[contains(@class, "views-row")]');
- $this->assertEqual(count($result), 2, 'Ensure that two items are rendered in the HTML.');
- }
-
- /**
- * Ensures that non-ajax view cannot be accessed via an ajax HTTP request.
- */
- public function testNonAjaxViewViaAjax() {
- $this->drupalPost('views/ajax', '', ['view_name' => 'test_ajax_view', 'view_display_id' => 'default'], ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]);
- $this->assertResponse(200);
- $this->drupalPost('views/ajax', '', ['view_name' => 'test_view', 'view_display_id' => 'default'], ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]);
- $this->assertResponse(403);
- }
-
-}
diff --git a/core/modules/views/src/Tests/ViewTestData.php b/core/modules/views/src/Tests/ViewTestData.php
index 27ddf7c46..f0c8cfce7 100644
--- a/core/modules/views/src/Tests/ViewTestData.php
+++ b/core/modules/views/src/Tests/ViewTestData.php
@@ -108,7 +108,7 @@ class ViewTestData {
],
'primary key' => ['id'],
'unique keys' => [
- 'name' => ['name']
+ 'name' => ['name'],
],
'indexes' => [
'ages' => ['age'],
diff --git a/core/modules/views/src/ViewExecutable.php b/core/modules/views/src/ViewExecutable.php
index 16b89f370..82504661f 100644
--- a/core/modules/views/src/ViewExecutable.php
+++ b/core/modules/views/src/ViewExecutable.php
@@ -1096,7 +1096,8 @@ class ViewExecutable {
$argument->is_default = TRUE;
}
- // Set the argument, which will also validate that the argument can be set.
+ // Set the argument, which ensures that the argument is valid and
+ // possibly transforms the value.
if (!$argument->setArgument($arg)) {
$status = $argument->validateFail($arg);
break;
@@ -1110,9 +1111,11 @@ class ViewExecutable {
$argument->query($this->display_handler->useGroupBy());
}
- // Add this argument's substitution
+ // Add this argument's substitution.
$substitutions["{{ arguments.$id }}"] = $arg_title;
- $substitutions["{{ raw_arguments.$id }}"] = strip_tags(Html::decodeEntities($arg));
+ // Since argument validator plugins can potentially transform the value,
+ // use whatever value the argument handler now has, not the raw value.
+ $substitutions["{{ raw_arguments.$id }}"] = strip_tags(Html::decodeEntities($argument->getValue()));
// Test to see if we should use this argument's title
if (!empty($argument->options['title_enable']) && !empty($argument->options['title'])) {
diff --git a/core/modules/views/src/ViewsData.php b/core/modules/views/src/ViewsData.php
index e5281abf3..09522b984 100644
--- a/core/modules/views/src/ViewsData.php
+++ b/core/modules/views/src/ViewsData.php
@@ -133,10 +133,8 @@ class ViewsData {
*
* @param string|null $key
* The key of the cache entry to retrieve. Defaults to NULL, this will
- * return all table data.
- *
- * @deprecated NULL $key deprecated in Drupal 8.2.x and will be removed in
- * 9.0.0. Use getAll() instead.
+ * return all table data. NULL $key deprecated in Drupal 8.2.x and will be
+ * removed in 9.0.0. Use getAll() instead.
*
* @see https://www.drupal.org/node/2723553
*
diff --git a/core/modules/views/src/ViewsDataHelper.php b/core/modules/views/src/ViewsDataHelper.php
index bfda18cfd..6a9188bee 100644
--- a/core/modules/views/src/ViewsDataHelper.php
+++ b/core/modules/views/src/ViewsDataHelper.php
@@ -2,8 +2,7 @@
namespace Drupal\views;
-use Drupal\Component\Utility\Unicode;
-use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Component\Render\FormattableMarkup;
/**
* Defines a helper class for stuff related to views data.
@@ -120,7 +119,7 @@ class ViewsDataHelper {
}
else {
if ($string != 'base') {
- $strings[$field][$key][$string] = SafeMarkup::format("Error: missing @component", ['@component' => $string]);
+ $strings[$field][$key][$string] = new FormattableMarkup("Error: missing @component", ['@component' => $string]);
}
}
}
@@ -174,14 +173,14 @@ class ViewsDataHelper {
* decided.
*/
protected static function fetchedFieldSort($a, $b) {
- $a_group = Unicode::strtolower($a['group']);
- $b_group = Unicode::strtolower($b['group']);
+ $a_group = mb_strtolower($a['group']);
+ $b_group = mb_strtolower($b['group']);
if ($a_group != $b_group) {
return $a_group < $b_group ? -1 : 1;
}
- $a_title = Unicode::strtolower($a['title']);
- $b_title = Unicode::strtolower($b['title']);
+ $a_title = mb_strtolower($a['title']);
+ $b_title = mb_strtolower($b['title']);
if ($a_title != $b_title) {
return $a_title < $b_title ? -1 : 1;
}
diff --git a/core/modules/views/templates/views-view-grid.html.twig b/core/modules/views/templates/views-view-grid.html.twig
index f2b155218..b1aab93c5 100644
--- a/core/modules/views/templates/views-view-grid.html.twig
+++ b/core/modules/views/templates/views-view-grid.html.twig
@@ -59,7 +59,7 @@
{% for column in row.content %}
- {{ column.content }}
+ {{- column.content -}}
{% endfor %}
@@ -69,7 +69,7 @@
{% for row in column.content %}
- {{ row.content }}
+ {{- row.content -}}
{% endfor %}
diff --git a/core/modules/views/templates/views-view-list.html.twig b/core/modules/views/templates/views-view-list.html.twig
index 8de787cd4..558b31a28 100644
--- a/core/modules/views/templates/views-view-list.html.twig
+++ b/core/modules/views/templates/views-view-list.html.twig
@@ -28,7 +28,9 @@
<{{ list.type }}{{ list.attributes }}>
{% for row in rows %}
- - {{ row.content }}
+ -
+ {{- row.content -}}
+
{% endfor %}
{{ list.type }}>
diff --git a/core/modules/views/templates/views-view-unformatted.html.twig b/core/modules/views/templates/views-view-unformatted.html.twig
index 7ee1f81e5..b67b5f7d7 100644
--- a/core/modules/views/templates/views-view-unformatted.html.twig
+++ b/core/modules/views/templates/views-view-unformatted.html.twig
@@ -27,6 +27,6 @@
]
%}
- {{ row.content }}
+ {{- row.content -}}
{% endfor %}
diff --git a/core/modules/views/tests/fixtures/update/views.view.test_boolean_filter_values.yml b/core/modules/views/tests/fixtures/update/views.view.test_boolean_filter_values.yml
index 65c28478d..587b18358 100644
--- a/core/modules/views/tests/fixtures/update/views.view.test_boolean_filter_values.yml
+++ b/core/modules/views/tests/fixtures/update/views.view.test_boolean_filter_values.yml
@@ -601,4 +601,4 @@ display:
- 'user.node_grants:view'
- user.permissions
max-age: 0
- tags: { }
\ No newline at end of file
+ tags: { }
diff --git a/core/modules/views/tests/modules/views_entity_test/views_entity_test.info.yml b/core/modules/views/tests/modules/views_entity_test/views_entity_test.info.yml
index 2f4186ac3..dfa9747d0 100644
--- a/core/modules/views/tests/modules/views_entity_test/views_entity_test.info.yml
+++ b/core/modules/views/tests/modules/views_entity_test/views_entity_test.info.yml
@@ -5,5 +5,5 @@ package: Testing
version: VERSION
core: 8.x
dependencies:
- - views
- - entity_test
+ - drupal:views
+ - drupal:entity_test
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_row_renderers_revisions_base.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_row_renderers_revisions_base.yml
index a4ebba1c3..2f8e993d5 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_row_renderers_revisions_base.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_row_renderers_revisions_base.yml
@@ -222,4 +222,3 @@ display:
- 'user.node_grants:view'
- user.permissions
tags: { }
-
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_exposed_relationship_admin_ui.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_exposed_relationship_admin_ui.yml
index 7d5fb3f85..b37547156 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_exposed_relationship_admin_ui.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_exposed_relationship_admin_ui.yml
@@ -278,4 +278,3 @@ display:
plugin_id: standard
arguments: { }
display_extenders: { }
-
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_menu_link.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_menu_link.yml
index d51b92035..0ff0dc203 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_menu_link.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_menu_link.yml
@@ -35,4 +35,3 @@ display:
display_options:
display_extenders: { }
path: test-menu-link
-
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_preprocess.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_preprocess.yml
new file mode 100644
index 000000000..7f5518e07
--- /dev/null
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_preprocess.yml
@@ -0,0 +1,71 @@
+langcode: en
+status: true
+dependencies: { }
+id: test_preprocess
+label: ''
+module: views
+description: ''
+tag: ''
+base_table: entity_test
+base_field: nid
+core: '8'
+display:
+ default:
+ display_options:
+ access:
+ type: none
+ cache:
+ type: tag
+ exposed_form:
+ type: basic
+ sorts:
+ id:
+ table: entity_test
+ id: id
+ field: id
+ plugin_id: standard
+ entity_type: entity_test
+ entity_field: id
+ order: desc
+ pager:
+ type: full
+ options:
+ items_per_page: 5
+ style:
+ type: default
+ row:
+ type: 'entity:entity_test'
+ css_class: 'entity-test__default'
+ display_plugin: default
+ display_title: Master
+ id: default
+ position: 0
+ display_2:
+ display_options:
+ access:
+ type: none
+ cache:
+ type: tag
+ exposed_form:
+ type: basic
+ sorts:
+ id:
+ table: entity_test
+ id: id
+ field: id
+ plugin_id: standard
+ entity_type: entity_test
+ entity_field: id
+ order: desc
+ pager:
+ type: full
+ options:
+ items_per_page: 5
+ style:
+ type: default
+ row:
+ type: 'entity:entity_test'
+ css_class: 'entity-test__default and_another-class'
+ display_plugin: default
+ display_title: Alternate
+ id: display_2
diff --git a/core/modules/views/tests/modules/views_test_config/views_test_config.info.yml b/core/modules/views/tests/modules/views_test_config/views_test_config.info.yml
index 33925738e..36a09f374 100644
--- a/core/modules/views/tests/modules/views_test_config/views_test_config.info.yml
+++ b/core/modules/views/tests/modules/views_test_config/views_test_config.info.yml
@@ -5,4 +5,4 @@ package: Testing
version: VERSION
core: 8.x
dependencies:
- - views
+ - drupal:views
diff --git a/core/modules/views/tests/modules/views_test_data/src/Plugin/views/argument_validator/ArgumentValidatorTest.php b/core/modules/views/tests/modules/views_test_data/src/Plugin/views/argument_validator/ArgumentValidatorTest.php
index ff251b294..101e681ad 100644
--- a/core/modules/views/tests/modules/views_test_data/src/Plugin/views/argument_validator/ArgumentValidatorTest.php
+++ b/core/modules/views/tests/modules/views_test_data/src/Plugin/views/argument_validator/ArgumentValidatorTest.php
@@ -37,6 +37,12 @@ class ArgumentValidatorTest extends ArgumentValidatorPluginBase {
* {@inheritdoc}
*/
public function validateArgument($arg) {
+ if ($arg === 'this value should be replaced') {
+ // Set the argument to a numeric value so this is valid on PostgeSQL for
+ // numeric fields.
+ $this->argument->argument = '1';
+ return TRUE;
+ }
return $arg == $this->options['test_value'];
}
diff --git a/core/modules/views/tests/modules/views_test_data/src/Plugin/views/field/FieldFormButtonTest.php b/core/modules/views/tests/modules/views_test_data/src/Plugin/views/field/FieldFormButtonTest.php
index ee961ce69..2367981ad 100644
--- a/core/modules/views/tests/modules/views_test_data/src/Plugin/views/field/FieldFormButtonTest.php
+++ b/core/modules/views/tests/modules/views_test_data/src/Plugin/views/field/FieldFormButtonTest.php
@@ -68,7 +68,7 @@ class FieldFormButtonTest extends FieldPluginBase {
if (!empty($triggering_element['#test_button'])) {
$row_index = $triggering_element['#row_index'];
$view_args = !empty($this->view->args) ? implode(', ', $this->view->args) : $this->t('no arguments');
- drupal_set_message($this->t('The test button at row @row_index for @view_id (@display) View with args: @args was submitted.', [
+ $this->messenger()->addStatus($this->t('The test button at row @row_index for @view_id (@display) View with args: @args was submitted.', [
'@display' => $this->view->current_display,
'@view_id' => $this->view->id(),
'@args' => $view_args,
diff --git a/core/modules/views/tests/modules/views_test_data/src/Plugin/views/join/JoinTest.php b/core/modules/views/tests/modules/views_test_data/src/Plugin/views/join/JoinTest.php
index b57c970fb..ed96decd5 100644
--- a/core/modules/views/tests/modules/views_test_data/src/Plugin/views/join/JoinTest.php
+++ b/core/modules/views/tests/modules/views_test_data/src/Plugin/views/join/JoinTest.php
@@ -35,7 +35,6 @@ class JoinTest extends JoinPluginBase {
$this->joinValue = $join_value;
}
-
/**
* {@inheritdoc}
*/
diff --git a/core/modules/views/tests/modules/views_test_data/src/Plugin/views/query/QueryTest.php b/core/modules/views/tests/modules/views_test_data/src/Plugin/views/query/QueryTest.php
index 8868f75de..71aa2defc 100644
--- a/core/modules/views/tests/modules/views_test_data/src/Plugin/views/query/QueryTest.php
+++ b/core/modules/views/tests/modules/views_test_data/src/Plugin/views/query/QueryTest.php
@@ -60,7 +60,7 @@ class QueryTest extends QueryPluginBase {
$this->conditions[] = [
'field' => $field,
'value' => $value,
- 'operator' => $operator
+ 'operator' => $operator,
];
}
@@ -74,7 +74,6 @@ class QueryTest extends QueryPluginBase {
$this->orderBy = ['field' => $field, 'order' => $order];
}
-
public function ensureTable($table, $relationship = NULL, JoinPluginBase $join = NULL) {
// There is no concept of joins.
}
diff --git a/core/modules/views/tests/modules/views_test_data/views_test_data.info.yml b/core/modules/views/tests/modules/views_test_data/views_test_data.info.yml
index 39b72e453..6a18bb070 100644
--- a/core/modules/views/tests/modules/views_test_data/views_test_data.info.yml
+++ b/core/modules/views/tests/modules/views_test_data/views_test_data.info.yml
@@ -5,4 +5,4 @@ package: Testing
version: VERSION
core: 8.x
dependencies:
- - views
+ - drupal:views
diff --git a/core/modules/views/tests/modules/views_test_data/views_test_data.install b/core/modules/views/tests/modules/views_test_data/views_test_data.install
index 3590440b9..1ef60361c 100644
--- a/core/modules/views/tests/modules/views_test_data/views_test_data.install
+++ b/core/modules/views/tests/modules/views_test_data/views_test_data.install
@@ -29,7 +29,7 @@ function views_test_data_install() {
'p' => 'P',
'strong' => 'STRONG',
'em' => 'EM',
- 'marquee' => 'MARQUEE'
+ 'marquee' => 'MARQUEE',
];
\Drupal::configFactory()->getEditable('views.settings')->set('field_rewrite_elements', $values)->save();
}
diff --git a/core/modules/views/tests/modules/views_test_data/views_test_data.module b/core/modules/views/tests/modules/views_test_data/views_test_data.module
index cdb99cc10..64e0d44a0 100644
--- a/core/modules/views/tests/modules/views_test_data/views_test_data.module
+++ b/core/modules/views/tests/modules/views_test_data/views_test_data.module
@@ -129,5 +129,5 @@ function views_test_data_test_pre_render_function($element) {
* Implements hook_form_BASE_FORM_ID_alter().
*/
function views_test_data_form_views_form_test_form_multiple_default_alter(&$form, FormStateInterface $form_state, $form_id) {
- drupal_set_message(t('Test base form ID with Views forms and arguments.'));
+ \Drupal::messenger()->addStatus(t('Test base form ID with Views forms and arguments.'));
}
diff --git a/core/modules/views/tests/modules/views_test_data/views_test_data.views_execution.inc b/core/modules/views/tests/modules/views_test_data/views_test_data.views_execution.inc
index dd69f4c95..8eaa704a1 100644
--- a/core/modules/views/tests/modules/views_test_data/views_test_data.views_execution.inc
+++ b/core/modules/views/tests/modules/views_test_data/views_test_data.views_execution.inc
@@ -74,6 +74,11 @@ function views_test_data_placeholders() {
*/
function views_test_data_views_post_render(ViewExecutable $view, &$output, CachePluginBase $cache) {
\Drupal::state()->set('views_hook_test_views_post_render', TRUE);
+ if ($view->storage->id() === 'test_page_display' && $view->current_display === 'empty_row') {
+ for ($i = 0; $i < 5; $i++) {
+ $output['#rows'][0]['#rows'][] = [];
+ }
+ }
}
/**
diff --git a/core/modules/views/tests/modules/views_test_formatter/views_test_formatter.info.yml b/core/modules/views/tests/modules/views_test_formatter/views_test_formatter.info.yml
index 91658d87e..16d3a4b0c 100644
--- a/core/modules/views/tests/modules/views_test_formatter/views_test_formatter.info.yml
+++ b/core/modules/views/tests/modules/views_test_formatter/views_test_formatter.info.yml
@@ -5,4 +5,4 @@ package: Testing
version: VERSION
core: 8.x
dependencies:
- - views
+ - drupal:views
diff --git a/core/modules/views/tests/modules/views_test_language/views_test_language.info.yml b/core/modules/views/tests/modules/views_test_language/views_test_language.info.yml
index 0163ecb1e..ced53bd12 100644
--- a/core/modules/views/tests/modules/views_test_language/views_test_language.info.yml
+++ b/core/modules/views/tests/modules/views_test_language/views_test_language.info.yml
@@ -5,5 +5,5 @@ package: Testing
version: VERSION
core: 8.x
dependencies:
- - views
- - language
+ - drupal:views
+ - drupal:language
diff --git a/core/modules/views/tests/modules/views_test_modal/src/Controller/TestController.php b/core/modules/views/tests/modules/views_test_modal/src/Controller/TestController.php
new file mode 100644
index 000000000..b89cf4081
--- /dev/null
+++ b/core/modules/views/tests/modules/views_test_modal/src/Controller/TestController.php
@@ -0,0 +1,41 @@
+ 'link',
+ '#title' => $this->t('Administer content'),
+ '#url' => Url::fromUserInput('/admin/content'),
+ '#attributes' => [
+ 'class' => ['use-ajax'],
+ 'data-dialog-type' => 'modal',
+ 'data-dialog-options' => Json::encode([
+ 'dialogClass' => 'views-test-modal',
+ 'height' => '50%',
+ 'width' => '50%',
+ 'title' => $this->t('Administer content'),
+ ]),
+ ],
+ '#attached' => [
+ 'library' => [
+ 'core/drupal.dialog.ajax',
+ ],
+ ],
+ ];
+
+ return $build;
+ }
+
+}
diff --git a/core/modules/views/tests/modules/views_test_modal/views_test_modal.info.yml b/core/modules/views/tests/modules/views_test_modal/views_test_modal.info.yml
new file mode 100644
index 000000000..59be6d4e5
--- /dev/null
+++ b/core/modules/views/tests/modules/views_test_modal/views_test_modal.info.yml
@@ -0,0 +1,9 @@
+name: 'Views Test Modal'
+type: module
+description: 'Provides a test page that renders a View in a modal.'
+package: Testing
+version: VERSION
+core: 8.x
+dependencies:
+ - drupal:node
+ - drupal:views
diff --git a/core/modules/views/tests/modules/views_test_modal/views_test_modal.routing.yml b/core/modules/views/tests/modules/views_test_modal/views_test_modal.routing.yml
new file mode 100644
index 000000000..430bac18e
--- /dev/null
+++ b/core/modules/views/tests/modules/views_test_modal/views_test_modal.routing.yml
@@ -0,0 +1,6 @@
+views_test_modal.modal:
+ path: '/views-test-modal/modal'
+ defaults:
+ _controller: '\Drupal\views_test_modal\Controller\TestController::modal'
+ requirements:
+ _access: 'TRUE'
diff --git a/core/modules/views/tests/src/Functional/DefaultViewsTest.php b/core/modules/views/tests/src/Functional/DefaultViewsTest.php
index 9e4c338dd..b6120707d 100644
--- a/core/modules/views/tests/src/Functional/DefaultViewsTest.php
+++ b/core/modules/views/tests/src/Functional/DefaultViewsTest.php
@@ -4,7 +4,6 @@ namespace Drupal\Tests\views\Functional;
use Drupal\comment\CommentInterface;
use Drupal\comment\Tests\CommentTestTrait;
-use Drupal\Component\Utility\Unicode;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Url;
@@ -53,7 +52,7 @@ class DefaultViewsTest extends ViewTestBase {
$vocabulary = Vocabulary::create([
'name' => $this->randomMachineName(),
'description' => $this->randomMachineName(),
- 'vid' => Unicode::strtolower($this->randomMachineName()),
+ 'vid' => mb_strtolower($this->randomMachineName()),
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
'help' => '',
'nodes' => ['page' => 'page'],
@@ -62,7 +61,7 @@ class DefaultViewsTest extends ViewTestBase {
$vocabulary->save();
// Create a field.
- $field_name = Unicode::strtolower($this->randomMachineName());
+ $field_name = mb_strtolower($this->randomMachineName());
$handler_settings = [
'target_bundles' => [
@@ -97,7 +96,7 @@ class DefaultViewsTest extends ViewTestBase {
'status' => CommentInterface::PUBLISHED,
'entity_id' => $node->id(),
'entity_type' => 'node',
- 'field_name' => 'comment'
+ 'field_name' => 'comment',
];
Comment::create($comment)->save();
diff --git a/core/modules/views/tests/src/Functional/Entity/FieldEntityTest.php b/core/modules/views/tests/src/Functional/Entity/FieldEntityTest.php
index a46e54c9e..2393b6d26 100644
--- a/core/modules/views/tests/src/Functional/Entity/FieldEntityTest.php
+++ b/core/modules/views/tests/src/Functional/Entity/FieldEntityTest.php
@@ -65,7 +65,7 @@ class FieldEntityTest extends ViewTestBase {
'uid' => $account->id(),
'entity_id' => $node->id(),
'entity_type' => 'node',
- 'field_name' => 'comment'
+ 'field_name' => 'comment',
]);
$comment->save();
diff --git a/core/modules/views/tests/src/Functional/Entity/FilterEntityBundleTest.php b/core/modules/views/tests/src/Functional/Entity/FilterEntityBundleTest.php
index 993293ceb..22bb6435c 100644
--- a/core/modules/views/tests/src/Functional/Entity/FilterEntityBundleTest.php
+++ b/core/modules/views/tests/src/Functional/Entity/FilterEntityBundleTest.php
@@ -81,7 +81,7 @@ class FilterEntityBundleTest extends ViewTestBase {
'node.type.test_bundle_2',
],
'module' => [
- 'node'
+ 'node',
],
];
$this->assertIdentical($expected, $view->getDependencies());
diff --git a/core/modules/views/tests/src/Functional/GlossaryTest.php b/core/modules/views/tests/src/Functional/GlossaryTest.php
index d67f70dfd..ed247f182 100644
--- a/core/modules/views/tests/src/Functional/GlossaryTest.php
+++ b/core/modules/views/tests/src/Functional/GlossaryTest.php
@@ -2,7 +2,6 @@
namespace Drupal\Tests\views\Functional;
-use Drupal\Component\Utility\Unicode;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Url;
use Drupal\views\Tests\AssertViewsCacheTagsTrait;
@@ -41,7 +40,7 @@ class GlossaryTest extends ViewTestBase {
$nodes_by_char = [];
foreach ($nodes_per_char as $char => $count) {
$setting = [
- 'type' => $type->id()
+ 'type' => $type->id(),
];
for ($i = 0; $i < $count; $i++) {
$node = $setting;
@@ -109,7 +108,7 @@ class GlossaryTest extends ViewTestBase {
$this->assertResponse(200);
foreach ($nodes_per_char as $char => $count) {
$href = Url::fromRoute('view.glossary.page_1', ['arg_0' => $char])->toString();
- $label = Unicode::strtoupper($char);
+ $label = mb_strtoupper($char);
// Get the summary link for a certain character. Filter by label and href
// to ensure that both of them are correct.
$result = $this->xpath('//a[contains(@href, :href) and normalize-space(text())=:label]/..', [':href' => $href, ':label' => $label]);
diff --git a/core/modules/hal/tests/src/Functional/EntityResource/View/ViewHalJsonAnonTest.php b/core/modules/views/tests/src/Functional/Hal/ViewHalJsonAnonTest.php
similarity index 75%
rename from core/modules/hal/tests/src/Functional/EntityResource/View/ViewHalJsonAnonTest.php
rename to core/modules/views/tests/src/Functional/Hal/ViewHalJsonAnonTest.php
index 432bd3f8c..134b00f22 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/View/ViewHalJsonAnonTest.php
+++ b/core/modules/views/tests/src/Functional/Hal/ViewHalJsonAnonTest.php
@@ -1,9 +1,9 @@
'Test Example area',
'help' => 'A area handler which just exists for tests.',
'area' => [
- 'id' => 'test_example'
- ]
+ 'id' => 'test_example',
+ ],
];
return $data;
}
-
/**
* Tests the generic UI of a area handler.
*/
diff --git a/core/modules/views/tests/src/Functional/Handler/FieldWebTest.php b/core/modules/views/tests/src/Functional/Handler/FieldWebTest.php
index a3646f0c0..eda5b81ab 100644
--- a/core/modules/views/tests/src/Functional/Handler/FieldWebTest.php
+++ b/core/modules/views/tests/src/Functional/Handler/FieldWebTest.php
@@ -3,7 +3,6 @@
namespace Drupal\Tests\views\Functional\Handler;
use Drupal\Component\Utility\Html;
-use Drupal\Component\Utility\Unicode;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Render\RenderContext;
use Drupal\Core\Url;
@@ -484,7 +483,7 @@ class FieldWebTest extends ViewTestBase {
'p',
'strong',
'em',
- 'marquee'
+ 'marquee',
];
$this->assertEqual(array_keys($element_types), $expected_elements);
@@ -556,7 +555,7 @@ class FieldWebTest extends ViewTestBase {
// Tests for simple trimming by string length.
$row->views_test_data_name = $this->randomMachineName(8);
$name_field->options['alter']['max_length'] = 5;
- $trimmed_name = Unicode::substr($row->views_test_data_name, 0, 5);
+ $trimmed_name = mb_substr($row->views_test_data_name, 0, 5);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field, $row) {
return $name_field->advancedRender($row);
@@ -581,28 +580,28 @@ class FieldWebTest extends ViewTestBase {
[
'value' => $random_text_8,
'trimmed_value' => '',
- 'trimmed' => TRUE
+ 'trimmed' => TRUE,
],
// Create one string with two words which doesn't fit both into the limit.
[
'value' => $random_text_8 . ' ' . $random_text_8,
'trimmed_value' => '',
- 'trimmed' => TRUE
+ 'trimmed' => TRUE,
],
// Create one string which contains of two words, of which only the first
// fits into the limit.
[
'value' => $random_text_4 . ' ' . $random_text_8,
'trimmed_value' => $random_text_4,
- 'trimmed' => TRUE
+ 'trimmed' => TRUE,
],
// Create one string which contains of two words, of which both fits into
// the limit.
[
'value' => $random_text_2 . ' ' . $random_text_2,
'trimmed_value' => $random_text_2 . ' ' . $random_text_2,
- 'trimmed' => FALSE
- ]
+ 'trimmed' => FALSE,
+ ],
];
foreach ($tuples as $tuple) {
diff --git a/core/modules/views/tests/src/Functional/Plugin/ArgumentDefaultTest.php b/core/modules/views/tests/src/Functional/Plugin/ArgumentDefaultTest.php
index 57eb2320b..b12e43e85 100644
--- a/core/modules/views/tests/src/Functional/Plugin/ArgumentDefaultTest.php
+++ b/core/modules/views/tests/src/Functional/Plugin/ArgumentDefaultTest.php
@@ -55,9 +55,9 @@ class ArgumentDefaultTest extends ViewTestBase {
$options = [
'default_argument_type' => 'argument_default_test',
'default_argument_options' => [
- 'value' => 'John'
+ 'value' => 'John',
],
- 'default_action' => 'default'
+ 'default_action' => 'default',
];
$id = $view->addHandler('default', 'argument', 'views_test_data', 'name', $options);
$view->initHandlers();
@@ -81,7 +81,6 @@ class ArgumentDefaultTest extends ViewTestBase {
$this->assertIdenticalResultset($view, $expected_result, ['views_test_data_name' => 'name']);
}
-
/**
* Tests the use of a default argument plugin that provides no options.
*/
diff --git a/core/modules/views/tests/src/Functional/Plugin/CacheWebTest.php b/core/modules/views/tests/src/Functional/Plugin/CacheWebTest.php
index f8ce5a927..c919bcb83 100644
--- a/core/modules/views/tests/src/Functional/Plugin/CacheWebTest.php
+++ b/core/modules/views/tests/src/Functional/Plugin/CacheWebTest.php
@@ -51,8 +51,8 @@ class CacheWebTest extends ViewTestBase {
'type' => 'time',
'options' => [
'results_lifespan' => '3600',
- 'output_lifespan' => '3600'
- ]
+ 'output_lifespan' => '3600',
+ ],
]);
$view->save();
$this->container->get('router.builder')->rebuildIfNeeded();
@@ -70,7 +70,7 @@ class CacheWebTest extends ViewTestBase {
'config:user.role.anonymous',
'config:views.view.test_display',
'node_list',
- 'rendered'
+ 'rendered',
];
$this->assertCacheTags($cache_tags);
diff --git a/core/modules/views/tests/src/Functional/Plugin/ContextualFiltersBlockContextTest.php b/core/modules/views/tests/src/Functional/Plugin/ContextualFiltersBlockContextTest.php
index 215ee2890..41356b69c 100644
--- a/core/modules/views/tests/src/Functional/Plugin/ContextualFiltersBlockContextTest.php
+++ b/core/modules/views/tests/src/Functional/Plugin/ContextualFiltersBlockContextTest.php
@@ -110,7 +110,7 @@ class ContextualFiltersBlockContextTest extends ViewTestBase {
'label_display' => 'visible',
'views_label' => '',
'items_per_page' => 'none',
- 'context_mapping' => ['nid' => '@node.node_route_context:node']
+ 'context_mapping' => ['nid' => '@node.node_route_context:node'],
];
$this->assertEqual($block->getPlugin()->getConfiguration(), $expected_settings, 'Block settings are correct.');
diff --git a/core/modules/views/tests/src/Functional/Plugin/DisplayAttachmentTest.php b/core/modules/views/tests/src/Functional/Plugin/DisplayAttachmentTest.php
index 8c326baa0..44668447c 100644
--- a/core/modules/views/tests/src/Functional/Plugin/DisplayAttachmentTest.php
+++ b/core/modules/views/tests/src/Functional/Plugin/DisplayAttachmentTest.php
@@ -36,7 +36,6 @@ class DisplayAttachmentTest extends ViewTestBase {
$this->drupalLogin($admin_user);
}
-
/**
* Tests the attachment plugin.
*/
diff --git a/core/modules/views/tests/src/Functional/Plugin/DisplayEntityReferenceTest.php b/core/modules/views/tests/src/Functional/Plugin/DisplayEntityReferenceTest.php
index daad14896..499d1b2c8 100644
--- a/core/modules/views/tests/src/Functional/Plugin/DisplayEntityReferenceTest.php
+++ b/core/modules/views/tests/src/Functional/Plugin/DisplayEntityReferenceTest.php
@@ -125,6 +125,10 @@ class DisplayEntityReferenceTest extends ViewTestBase {
* Tests the entity reference display plugin.
*/
public function testEntityReferenceDisplay() {
+ // Test that the 'title' settings are not shown.
+ $this->drupalGet('admin/structure/views/view/test_display_entity_reference/edit/entity_reference_1');
+ $this->assertSession()->linkByHrefNotExists('admin/structure/views/nojs/display/test_display_entity_reference/entity_reference_1/title');
+
// Add the new field to the fields.
$this->drupalPostForm('admin/structure/views/nojs/add-handler/test_display_entity_reference/default/field', ['name[entity_test__' . $this->fieldName . '.' . $this->fieldName . ']' => TRUE], t('Add and configure fields'));
$this->drupalPostForm(NULL, [], t('Apply'));
diff --git a/core/modules/views/tests/src/Functional/Plugin/DisplayTest.php b/core/modules/views/tests/src/Functional/Plugin/DisplayTest.php
index 674815298..d8deaa598 100644
--- a/core/modules/views/tests/src/Functional/Plugin/DisplayTest.php
+++ b/core/modules/views/tests/src/Functional/Plugin/DisplayTest.php
@@ -285,7 +285,7 @@ class DisplayTest extends ViewTestBase {
'table' => 'views_test_data',
'field' => 'id',
'id' => 'id',
- 'value' => ['value' => 7297]
+ 'value' => ['value' => 7297],
];
$view->setHandler('default', 'filter', 'id', $item);
$this->executeView($view);
diff --git a/core/modules/views/tests/src/Functional/Plugin/ExposedFormCheckboxesTest.php b/core/modules/views/tests/src/Functional/Plugin/ExposedFormCheckboxesTest.php
index 80584d796..f5901a080 100644
--- a/core/modules/views/tests/src/Functional/Plugin/ExposedFormCheckboxesTest.php
+++ b/core/modules/views/tests/src/Functional/Plugin/ExposedFormCheckboxesTest.php
@@ -2,7 +2,6 @@
namespace Drupal\Tests\views\Functional\Plugin;
-use Drupal\Component\Utility\Unicode;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait;
use Drupal\taxonomy\Entity\Term;
@@ -123,7 +122,7 @@ class ExposedFormCheckboxesTest extends ViewTestBase {
}
// Create a field.
- $field_name = Unicode::strtolower($this->randomMachineName());
+ $field_name = mb_strtolower($this->randomMachineName());
$handler_settings = [
'target_bundles' => [
$this->vocabulary->id() => $this->vocabulary->id(),
diff --git a/core/modules/views/tests/src/Functional/Plugin/ExposedFormTest.php b/core/modules/views/tests/src/Functional/Plugin/ExposedFormTest.php
index eaa5aaa32..4cd81d289 100644
--- a/core/modules/views/tests/src/Functional/Plugin/ExposedFormTest.php
+++ b/core/modules/views/tests/src/Functional/Plugin/ExposedFormTest.php
@@ -104,9 +104,9 @@ class ExposedFormTest extends ViewTestBase {
'label' => 'Content: Type',
'operator_id' => 'type_op',
'reduce' => FALSE,
- 'description' => 'Exposed overridden description'
+ 'description' => 'Exposed overridden description',
],
- ]
+ ],
]);
$view->save();
$this->drupalGet('test_exposed_form_buttons', ['query' => [$identifier => 'article']]);
@@ -131,9 +131,9 @@ class ExposedFormTest extends ViewTestBase {
'label' => 'Content: Type',
'operator_id' => 'type_op',
'reduce' => FALSE,
- 'description' => 'Exposed overridden description'
+ 'description' => 'Exposed overridden description',
],
- ]
+ ],
]);
$this->executeView($view);
@@ -286,7 +286,7 @@ class ExposedFormTest extends ViewTestBase {
'entity_test_view_grants',
'theme',
'url.query_args',
- 'languages:language_content'
+ 'languages:language_content',
];
$this->drupalGet('test_exposed_form_sort_items_per_page');
diff --git a/core/modules/views/tests/src/Functional/Plugin/PagerTest.php b/core/modules/views/tests/src/Functional/Plugin/PagerTest.php
index c80fa09bf..272a3f073 100644
--- a/core/modules/views/tests/src/Functional/Plugin/PagerTest.php
+++ b/core/modules/views/tests/src/Functional/Plugin/PagerTest.php
@@ -418,7 +418,7 @@ class PagerTest extends ViewTestBase {
// Create source string.
$source = $this->localeStorage->createString(
[
- 'source' => $label
+ 'source' => $label,
]
);
$source->save();
diff --git a/core/modules/views/tests/src/Functional/Plugin/ViewsBulkTest.php b/core/modules/views/tests/src/Functional/Plugin/ViewsBulkTest.php
index 1b3078b54..e3faa8840 100644
--- a/core/modules/views/tests/src/Functional/Plugin/ViewsBulkTest.php
+++ b/core/modules/views/tests/src/Functional/Plugin/ViewsBulkTest.php
@@ -41,7 +41,7 @@ class ViewsBulkTest extends ViewTestBase {
$node_1 = $this->drupalCreateNode([
'type' => 'page',
'title' => 'The first node',
- 'changed' => \Drupal::time()->getRequestTime() - 180
+ 'changed' => \Drupal::time()->getRequestTime() - 180,
]);
// Login as administrator and go to admin/content.
@@ -53,7 +53,7 @@ class ViewsBulkTest extends ViewTestBase {
$node_2 = $this->drupalCreateNode([
'type' => 'page',
'title' => 'The second node',
- 'changed' => \Drupal::time()->getRequestTime() - 120
+ 'changed' => \Drupal::time()->getRequestTime() - 120,
]);
// Now click 'Apply to selected items' and assert the first node is selected
diff --git a/core/modules/views/tests/src/Functional/Plugin/ViewsFormTest.php b/core/modules/views/tests/src/Functional/Plugin/ViewsFormTest.php
index 3f6e1eca5..67c4cb035 100644
--- a/core/modules/views/tests/src/Functional/Plugin/ViewsFormTest.php
+++ b/core/modules/views/tests/src/Functional/Plugin/ViewsFormTest.php
@@ -27,7 +27,7 @@ class ViewsFormTest extends ViewTestBase {
$xpath = $this->cssSelect('.views-form form');
$this->assertIdentical(count($xpath), 1, 'There is one views form on the page.');
// Ensure we don't have nested form elements.
- $result = (bool) preg_match('#