updated core and modules
This commit is contained in:
@@ -5,8 +5,8 @@ package: Testing
|
||||
# version: VERSION
|
||||
# core: 8.x
|
||||
|
||||
# Information added by Drupal.org packaging script on 2016-08-03
|
||||
version: '8.1.8'
|
||||
# Information added by Drupal.org packaging script on 2017-08-16
|
||||
version: '8.3.7'
|
||||
core: '8.x'
|
||||
project: 'drupal'
|
||||
datestamp: 1470233790
|
||||
datestamp: 1502903957
|
||||
|
||||
@@ -50,15 +50,15 @@ use Drupal\node\NodeInterface;
|
||||
* @see node_access_test_node_access_records()
|
||||
*/
|
||||
function node_access_test_node_grants($account, $op) {
|
||||
$grants = array();
|
||||
$grants['node_access_test_author'] = array($account->id());
|
||||
$grants = [];
|
||||
$grants['node_access_test_author'] = [$account->id()];
|
||||
if ($op == 'view' && $account->hasPermission('node test view', $account)) {
|
||||
$grants['node_access_test'] = array(8888, 8889);
|
||||
$grants['node_access_test'] = [8888, 8889];
|
||||
}
|
||||
|
||||
$no_access_uid = \Drupal::state()->get('node_access_test.no_access_uid') ?: 0;
|
||||
if ($op == 'view' && $account->id() == $no_access_uid) {
|
||||
$grants['node_access_all'] = array(0);
|
||||
$grants['node_access_all'] = [0];
|
||||
}
|
||||
return $grants;
|
||||
}
|
||||
@@ -77,37 +77,37 @@ function node_access_test_node_grants($account, $op) {
|
||||
* @see node_access_test.permissions.yml
|
||||
*/
|
||||
function node_access_test_node_access_records(NodeInterface $node) {
|
||||
$grants = array();
|
||||
$grants = [];
|
||||
// For NodeAccessBaseTableTestCase, only set records for private nodes.
|
||||
if (!\Drupal::state()->get('node_access_test.private') || $node->private->value) {
|
||||
// Groups 8888 and 8889 for the node_access_test realm both receive a view
|
||||
// grant for all controlled nodes. See node_access_test_node_grants().
|
||||
$grants[] = array(
|
||||
$grants[] = [
|
||||
'realm' => 'node_access_test',
|
||||
'gid' => 8888,
|
||||
'grant_view' => 1,
|
||||
'grant_update' => 0,
|
||||
'grant_delete' => 0,
|
||||
'priority' => 0,
|
||||
);
|
||||
$grants[] = array(
|
||||
];
|
||||
$grants[] = [
|
||||
'realm' => 'node_access_test',
|
||||
'gid' => 8889,
|
||||
'grant_view' => 1,
|
||||
'grant_update' => 0,
|
||||
'grant_delete' => 0,
|
||||
'priority' => 0,
|
||||
);
|
||||
];
|
||||
// For the author realm, the group ID is equivalent to a user ID, which
|
||||
// means there are many many groups of just 1 user.
|
||||
$grants[] = array(
|
||||
$grants[] = [
|
||||
'realm' => 'node_access_test_author',
|
||||
'gid' => $node->getOwnerId(),
|
||||
'grant_view' => 1,
|
||||
'grant_update' => 1,
|
||||
'grant_delete' => 1,
|
||||
'priority' => 0,
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
return $grants;
|
||||
@@ -120,25 +120,25 @@ function node_access_test_node_access_records(NodeInterface $node) {
|
||||
* A node type entity.
|
||||
*/
|
||||
function node_access_test_add_field(NodeTypeInterface $type) {
|
||||
$field_storage = FieldStorageConfig::create(array(
|
||||
$field_storage = FieldStorageConfig::create([
|
||||
'field_name' => 'private',
|
||||
'entity_type' => 'node',
|
||||
'type' => 'integer',
|
||||
));
|
||||
]);
|
||||
$field_storage->save();
|
||||
$field = FieldConfig::create(array(
|
||||
$field = FieldConfig::create([
|
||||
'field_name' => 'private',
|
||||
'entity_type' => 'node',
|
||||
'bundle' => $type->id(),
|
||||
'label' => 'Private',
|
||||
));
|
||||
]);
|
||||
$field->save();
|
||||
|
||||
// Assign widget settings for the 'default' form mode.
|
||||
entity_get_form_display('node', $type->id(), 'default')
|
||||
->setComponent('private', array(
|
||||
->setComponent('private', [
|
||||
'type' => 'number',
|
||||
))
|
||||
])
|
||||
->save();
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -5,8 +5,8 @@ package: Testing
|
||||
# version: VERSION
|
||||
# core: 8.x
|
||||
|
||||
# Information added by Drupal.org packaging script on 2016-08-03
|
||||
version: '8.1.8'
|
||||
# Information added by Drupal.org packaging script on 2017-08-16
|
||||
version: '8.3.7'
|
||||
core: '8.x'
|
||||
project: 'drupal'
|
||||
datestamp: 1470233790
|
||||
datestamp: 1502903957
|
||||
|
||||
+2
-2
@@ -11,12 +11,12 @@ use Drupal\node\NodeInterface;
|
||||
* Implements hook_node_grants().
|
||||
*/
|
||||
function node_access_test_empty_node_grants($account, $op) {
|
||||
return array();
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_access_records().
|
||||
*/
|
||||
function node_access_test_empty_node_access_records(NodeInterface $node) {
|
||||
return array();
|
||||
return [];
|
||||
}
|
||||
|
||||
+3
-3
@@ -7,8 +7,8 @@ package: Testing
|
||||
dependencies:
|
||||
- options
|
||||
|
||||
# Information added by Drupal.org packaging script on 2016-08-03
|
||||
version: '8.1.8'
|
||||
# Information added by Drupal.org packaging script on 2017-08-16
|
||||
version: '8.3.7'
|
||||
core: '8.x'
|
||||
project: 'drupal'
|
||||
datestamp: 1470233790
|
||||
datestamp: 1502903957
|
||||
|
||||
+4
-4
@@ -16,7 +16,7 @@ use Drupal\node\NodeInterface;
|
||||
* This module defines a single grant realm. All users belong to this group.
|
||||
*/
|
||||
function node_access_test_language_node_grants($account, $op) {
|
||||
$grants['node_access_language_test'] = array(7888);
|
||||
$grants['node_access_language_test'] = [7888];
|
||||
return $grants;
|
||||
}
|
||||
|
||||
@@ -24,13 +24,13 @@ function node_access_test_language_node_grants($account, $op) {
|
||||
* Implements hook_node_access_records().
|
||||
*/
|
||||
function node_access_test_language_node_access_records(NodeInterface $node) {
|
||||
$grants = array();
|
||||
$grants = [];
|
||||
|
||||
// Create grants for each translation of the node.
|
||||
foreach ($node->getTranslationLanguages() as $langcode => $language) {
|
||||
// If the translation is not marked as private, grant access.
|
||||
$translation = $node->getTranslation($langcode);
|
||||
$grants[] = array(
|
||||
$grants[] = [
|
||||
'realm' => 'node_access_language_test',
|
||||
'gid' => 7888,
|
||||
'grant_view' => empty($translation->field_private->value) ? 1 : 0,
|
||||
@@ -38,7 +38,7 @@ function node_access_test_language_node_access_records(NodeInterface $node) {
|
||||
'grant_delete' => 0,
|
||||
'priority' => 0,
|
||||
'langcode' => $langcode,
|
||||
);
|
||||
];
|
||||
}
|
||||
return $grants;
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ package: Testing
|
||||
# version: VERSION
|
||||
# core: 8.x
|
||||
|
||||
# Information added by Drupal.org packaging script on 2016-08-03
|
||||
version: '8.1.8'
|
||||
# Information added by Drupal.org packaging script on 2017-08-16
|
||||
version: '8.3.7'
|
||||
core: '8.x'
|
||||
project: 'drupal'
|
||||
datestamp: 1470233790
|
||||
datestamp: 1502903957
|
||||
|
||||
@@ -10,32 +10,34 @@
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\node\NodeInterface;
|
||||
|
||||
|
||||
/**
|
||||
* Implements hook_ENTITY_TYPE_view() for node entities.
|
||||
*/
|
||||
function node_test_node_view(array &$build, NodeInterface $node, EntityViewDisplayInterface $display, $view_mode) {
|
||||
if ($view_mode == 'rss') {
|
||||
// Add RSS elements and namespaces when building the RSS feed.
|
||||
$node->rss_elements[] = array(
|
||||
$node->rss_elements[] = [
|
||||
'key' => 'testElement',
|
||||
'value' => t('Value of testElement RSS element for node @nid.', array('@nid' => $node->id())),
|
||||
);
|
||||
'value' => t('Value of testElement RSS element for node @nid.', ['@nid' => $node->id()]),
|
||||
];
|
||||
|
||||
// Add content that should be displayed only in the RSS feed.
|
||||
$build['extra_feed_content'] = array(
|
||||
'#markup' => '<p>' . t('Extra data that should appear only in the RSS feed for node @nid.', array('@nid' => $node->id())) . '</p>',
|
||||
$build['extra_feed_content'] = [
|
||||
'#markup' => '<p>' . t('Extra data that should appear only in the RSS feed for node @nid.', ['@nid' => $node->id()]) . '</p>',
|
||||
'#weight' => 10,
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
if ($view_mode != 'rss') {
|
||||
// Add content that should NOT be displayed in the RSS feed.
|
||||
$build['extra_non_feed_content'] = array(
|
||||
'#markup' => '<p>' . t('Extra data that should appear everywhere except the RSS feed for node @nid.', array('@nid' => $node->id())) . '</p>',
|
||||
);
|
||||
$build['extra_non_feed_content'] = [
|
||||
'#markup' => '<p>' . t('Extra data that should appear everywhere except the RSS feed for node @nid.', ['@nid' => $node->id()]) . '</p>',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,11 +57,11 @@ function node_test_node_grants(AccountInterface $account, $op) {
|
||||
// Give everyone full grants so we don't break other node tests.
|
||||
// Our node access tests asserts three realms of access.
|
||||
// See testGrantAlter().
|
||||
return array(
|
||||
'test_article_realm' => array(1),
|
||||
'test_page_realm' => array(1),
|
||||
'test_alter_realm' => array(2),
|
||||
);
|
||||
return [
|
||||
'test_article_realm' => [1],
|
||||
'test_page_realm' => [1],
|
||||
'test_alter_realm' => [2],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,28 +72,28 @@ function node_test_node_access_records(NodeInterface $node) {
|
||||
if (!empty($node->disable_node_access)) {
|
||||
return;
|
||||
}
|
||||
$grants = array();
|
||||
$grants = [];
|
||||
if ($node->getType() == 'article') {
|
||||
// Create grant in arbitrary article_realm for article nodes.
|
||||
$grants[] = array(
|
||||
$grants[] = [
|
||||
'realm' => 'test_article_realm',
|
||||
'gid' => 1,
|
||||
'grant_view' => 1,
|
||||
'grant_update' => 0,
|
||||
'grant_delete' => 0,
|
||||
'priority' => 0,
|
||||
);
|
||||
];
|
||||
}
|
||||
elseif ($node->getType() == 'page') {
|
||||
// Create grant in arbitrary page_realm for page nodes.
|
||||
$grants[] = array(
|
||||
$grants[] = [
|
||||
'realm' => 'test_page_realm',
|
||||
'gid' => 1,
|
||||
'grant_view' => 1,
|
||||
'grant_update' => 0,
|
||||
'grant_delete' => 0,
|
||||
'priority' => 0,
|
||||
);
|
||||
];
|
||||
}
|
||||
return $grants;
|
||||
}
|
||||
@@ -116,7 +118,7 @@ function node_test_node_access_records_alter(&$grants, NodeInterface $node) {
|
||||
*/
|
||||
function node_test_node_grants_alter(&$grants, AccountInterface $account, $op) {
|
||||
// Return an empty array of grants to prove that we can alter by reference.
|
||||
$grants = array();
|
||||
$grants = [];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -175,3 +177,16 @@ function node_test_node_insert(NodeInterface $node) {
|
||||
$node->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_alter().
|
||||
*/
|
||||
function node_test_form_alter(&$form, FormStateInterface $form_state, $form_id) {
|
||||
if (!$form_state->get('node_test_form_alter')) {
|
||||
drupal_set_message('Storage is not set');
|
||||
$form_state->set('node_test_form_alter', TRUE);
|
||||
}
|
||||
else {
|
||||
drupal_set_message('Storage is set');
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ type: default
|
||||
name: Default
|
||||
description: 'Default description.'
|
||||
help: ''
|
||||
new_revision: false
|
||||
new_revision: true
|
||||
display_submitted: true
|
||||
preview_mode: 1
|
||||
status: true
|
||||
|
||||
@@ -5,8 +5,8 @@ description: 'Support module for node configuration tests.'
|
||||
package: Testing
|
||||
# version: VERSION
|
||||
|
||||
# Information added by Drupal.org packaging script on 2016-08-03
|
||||
version: '8.1.8'
|
||||
# Information added by Drupal.org packaging script on 2017-08-16
|
||||
version: '8.3.7'
|
||||
core: '8.x'
|
||||
project: 'drupal'
|
||||
datestamp: 1470233790
|
||||
datestamp: 1502903957
|
||||
|
||||
@@ -5,8 +5,8 @@ package: Testing
|
||||
# version: VERSION
|
||||
# core: 8.x
|
||||
|
||||
# Information added by Drupal.org packaging script on 2016-08-03
|
||||
version: '8.1.8'
|
||||
# Information added by Drupal.org packaging script on 2017-08-16
|
||||
version: '8.3.7'
|
||||
core: '8.x'
|
||||
project: 'drupal'
|
||||
datestamp: 1470233790
|
||||
datestamp: 1502903957
|
||||
|
||||
@@ -9,8 +9,8 @@ dependencies:
|
||||
- views
|
||||
- language
|
||||
|
||||
# Information added by Drupal.org packaging script on 2016-08-03
|
||||
version: '8.1.8'
|
||||
# Information added by Drupal.org packaging script on 2017-08-16
|
||||
version: '8.3.7'
|
||||
core: '8.x'
|
||||
project: 'drupal'
|
||||
datestamp: 1470233790
|
||||
datestamp: 1502903957
|
||||
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
||||
- user
|
||||
id: test_argument_node_uid_revision
|
||||
label: test_argument_node_uid_revision
|
||||
module: views
|
||||
description: ''
|
||||
tag: default
|
||||
base_table: node_field_data
|
||||
base_field: nid
|
||||
core: 8.0-dev
|
||||
display:
|
||||
default:
|
||||
display_options:
|
||||
access:
|
||||
type: perm
|
||||
cache:
|
||||
type: tag
|
||||
exposed_form:
|
||||
type: basic
|
||||
fields:
|
||||
nid:
|
||||
id: nid
|
||||
table: node_field_data
|
||||
field: nid
|
||||
plugin_id: field
|
||||
entity_type: node
|
||||
entity_field: nid
|
||||
filter_groups:
|
||||
groups:
|
||||
1: AND
|
||||
operator: AND
|
||||
filters: { }
|
||||
sorts:
|
||||
nid:
|
||||
id: nid
|
||||
table: node_field_data
|
||||
field: nid
|
||||
order: ASC
|
||||
plugin_id: standard
|
||||
relationship: none
|
||||
entity_type: node
|
||||
entity_field: nid
|
||||
pager:
|
||||
type: full
|
||||
query:
|
||||
type: views_query
|
||||
style:
|
||||
type: default
|
||||
row:
|
||||
type: fields
|
||||
display_extenders: { }
|
||||
arguments:
|
||||
uid_revision:
|
||||
id: uid_revision
|
||||
table: node_field_data
|
||||
field: uid_revision
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
default_action: empty
|
||||
exception:
|
||||
value: all
|
||||
title_enable: false
|
||||
title: All
|
||||
title_enable: false
|
||||
title: ''
|
||||
default_argument_type: fixed
|
||||
default_argument_options:
|
||||
argument: ''
|
||||
default_argument_skip_url: false
|
||||
summary_options:
|
||||
base_path: ''
|
||||
count: true
|
||||
items_per_page: 25
|
||||
override: false
|
||||
summary:
|
||||
sort_order: asc
|
||||
number_of_records: 0
|
||||
format: default_summary
|
||||
specify_validation: false
|
||||
validate:
|
||||
type: none
|
||||
fail: 'not found'
|
||||
validate_options: { }
|
||||
break_phrase: false
|
||||
not: false
|
||||
entity_type: node
|
||||
plugin_id: node_uid_revision
|
||||
display_plugin: default
|
||||
display_title: Master
|
||||
id: default
|
||||
position: 0
|
||||
cache_metadata:
|
||||
max-age: -1
|
||||
contexts:
|
||||
- 'languages:language_content'
|
||||
- 'languages:language_interface'
|
||||
- url
|
||||
- url.query_args
|
||||
- 'user.node_grants:view'
|
||||
- user.permissions
|
||||
tags: { }
|
||||
+4
-4
@@ -91,7 +91,7 @@ display:
|
||||
plugin_id: field
|
||||
filters:
|
||||
status:
|
||||
value: true
|
||||
value: '1'
|
||||
table: node_field_data
|
||||
field: status
|
||||
id: status
|
||||
@@ -177,7 +177,7 @@ display:
|
||||
filter_groups: false
|
||||
filters:
|
||||
status:
|
||||
value: true
|
||||
value: '1'
|
||||
table: node_field_data
|
||||
field: status
|
||||
id: status
|
||||
@@ -244,7 +244,7 @@ display:
|
||||
filter_groups: false
|
||||
filters:
|
||||
status:
|
||||
value: true
|
||||
value: '1'
|
||||
table: node_field_data
|
||||
field: status
|
||||
id: status
|
||||
@@ -311,7 +311,7 @@ display:
|
||||
filter_groups: false
|
||||
filters:
|
||||
status:
|
||||
value: true
|
||||
value: '1'
|
||||
table: node_field_data
|
||||
field: status
|
||||
id: status
|
||||
|
||||
+203
@@ -0,0 +1,203 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
||||
- user
|
||||
id: test_filter_node_access
|
||||
label: test_filter_node_access
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: node_field_data
|
||||
base_field: nid
|
||||
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: true
|
||||
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: some
|
||||
options:
|
||||
items_per_page: 10
|
||||
offset: 0
|
||||
style:
|
||||
type: default
|
||||
row:
|
||||
type: fields
|
||||
options:
|
||||
default_field_elements: true
|
||||
inline: { }
|
||||
separator: ''
|
||||
hide_empty: false
|
||||
fields:
|
||||
title:
|
||||
id: title
|
||||
table: node_field_data
|
||||
field: title
|
||||
entity_type: node
|
||||
entity_field: title
|
||||
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
|
||||
settings:
|
||||
link_to_entity: true
|
||||
plugin_id: field
|
||||
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
|
||||
type: string
|
||||
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
|
||||
filters:
|
||||
status:
|
||||
value: '1'
|
||||
table: node_field_data
|
||||
field: status
|
||||
plugin_id: boolean
|
||||
entity_type: node
|
||||
entity_field: status
|
||||
id: status
|
||||
expose:
|
||||
operator: ''
|
||||
group: 1
|
||||
nid:
|
||||
id: nid
|
||||
table: node_access
|
||||
field: nid
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
operator: '='
|
||||
value: ''
|
||||
group: 1
|
||||
exposed: false
|
||||
expose:
|
||||
operator_id: ''
|
||||
label: ''
|
||||
description: ''
|
||||
use_operator: false
|
||||
operator: ''
|
||||
identifier: ''
|
||||
required: false
|
||||
remember: false
|
||||
multiple: false
|
||||
remember_roles:
|
||||
authenticated: authenticated
|
||||
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: node_access
|
||||
sorts:
|
||||
created:
|
||||
id: created
|
||||
table: node_field_data
|
||||
field: created
|
||||
order: DESC
|
||||
entity_type: node
|
||||
entity_field: created
|
||||
plugin_id: date
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
exposed: false
|
||||
expose:
|
||||
label: ''
|
||||
granularity: second
|
||||
title: test_filter_node_access
|
||||
header: { }
|
||||
footer: { }
|
||||
empty: { }
|
||||
relationships: { }
|
||||
arguments: { }
|
||||
display_extenders: { }
|
||||
cache_metadata:
|
||||
max-age: -1
|
||||
contexts:
|
||||
- 'languages:language_content'
|
||||
- 'languages:language_interface'
|
||||
- 'user.node_grants:view'
|
||||
- user.permissions
|
||||
tags: { }
|
||||
page_1:
|
||||
display_plugin: page
|
||||
id: page_1
|
||||
display_title: Page
|
||||
position: 1
|
||||
display_options:
|
||||
display_extenders: { }
|
||||
path: test_filter_node_access
|
||||
cache_metadata:
|
||||
max-age: -1
|
||||
contexts:
|
||||
- 'languages:language_content'
|
||||
- 'languages:language_interface'
|
||||
- 'user.node_grants:view'
|
||||
- user.permissions
|
||||
tags: { }
|
||||
+1
-1
@@ -166,7 +166,7 @@ display:
|
||||
type: language
|
||||
filters:
|
||||
status:
|
||||
value: true
|
||||
value: '1'
|
||||
table: node_field_data
|
||||
field: status
|
||||
id: status
|
||||
|
||||
+1
-1
@@ -123,7 +123,7 @@ display:
|
||||
plugin_id: node_path
|
||||
filters:
|
||||
status:
|
||||
value: true
|
||||
value: '1'
|
||||
table: node_field_data
|
||||
field: status
|
||||
plugin_id: boolean
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ display:
|
||||
group: 1
|
||||
id: status
|
||||
table: node_field_data
|
||||
value: true
|
||||
value: '1'
|
||||
plugin_id: boolean
|
||||
entity_type: node
|
||||
entity_field: status
|
||||
|
||||
+1
-1
@@ -123,7 +123,7 @@ display:
|
||||
entity_field: nid
|
||||
filters:
|
||||
status:
|
||||
value: true
|
||||
value: '1'
|
||||
table: node_field_data
|
||||
field: status
|
||||
id: status
|
||||
|
||||
+3
-3
@@ -5,8 +5,8 @@ package: Testing
|
||||
# version: VERSION
|
||||
# core: 8.x
|
||||
|
||||
# Information added by Drupal.org packaging script on 2016-08-03
|
||||
version: '8.1.8'
|
||||
# Information added by Drupal.org packaging script on 2017-08-16
|
||||
version: '8.3.7'
|
||||
core: '8.x'
|
||||
project: 'drupal'
|
||||
datestamp: 1470233790
|
||||
datestamp: 1502903957
|
||||
|
||||
+3
-30
@@ -4,40 +4,13 @@ namespace Drupal\node_access_test_auto_bubbling\Controller;
|
||||
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Drupal\Core\Entity\Query\QueryFactory;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Drupal\node\NodeInterface;
|
||||
|
||||
/**
|
||||
* Returns a node ID listing.
|
||||
*/
|
||||
class NodeAccessTestAutoBubblingController extends ControllerBase implements ContainerInjectionInterface {
|
||||
|
||||
/**
|
||||
* The entity query factory service.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\Query\QueryFactory
|
||||
*/
|
||||
protected $entityQuery;
|
||||
|
||||
/**
|
||||
* Constructs a new NodeAccessTestAutoBubblingController.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\Query\QueryFactory $entity_query
|
||||
* The entity query factory.
|
||||
*/
|
||||
public function __construct(QueryFactory $entity_query) {
|
||||
$this->entityQuery = $entity_query;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity.query')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists the three latest published node IDs.
|
||||
*
|
||||
@@ -45,8 +18,8 @@ class NodeAccessTestAutoBubblingController extends ControllerBase implements Con
|
||||
* A render array.
|
||||
*/
|
||||
public function latest() {
|
||||
$nids = $this->entityQuery->get('node')
|
||||
->condition('status', NODE_PUBLISHED)
|
||||
$nids = $this->entityTypeManager()->getStorage('node')->getQuery()
|
||||
->condition('status', NodeInterface::PUBLISHED)
|
||||
->sort('created', 'DESC')
|
||||
->range(0, 3)
|
||||
->execute();
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
/**
|
||||
* Asserts that buttons are present on a page.
|
||||
*/
|
||||
trait AssertButtonsTrait {
|
||||
|
||||
/**
|
||||
* Assert method to verify the buttons in the dropdown element.
|
||||
*
|
||||
* @param array $buttons
|
||||
* A collection of buttons to assert for on the page.
|
||||
* @param bool $dropbutton
|
||||
* Whether to check if the buttons are in a dropbutton widget or not.
|
||||
*/
|
||||
public function assertButtons(array $buttons, $dropbutton = TRUE) {
|
||||
|
||||
// Try to find a Save button.
|
||||
$save_button = $this->xpath('//input[@type="submit"][@value="Save"]');
|
||||
|
||||
// Verify that the number of buttons passed as parameters is
|
||||
// available in the dropbutton widget.
|
||||
if ($dropbutton) {
|
||||
$i = 0;
|
||||
$count = count($buttons);
|
||||
|
||||
// Assert there is no save button.
|
||||
$this->assertTrue(empty($save_button));
|
||||
|
||||
// Dropbutton elements.
|
||||
/** @var \Behat\Mink\Element\NodeElement[] $elements */
|
||||
$elements = $this->xpath('//div[@class="dropbutton-wrapper"]//input[@type="submit"]');
|
||||
$this->assertEqual($count, count($elements));
|
||||
foreach ($elements as $element) {
|
||||
$value = $element->getValue() ?: '';
|
||||
$this->assertEqual($buttons[$i], $value);
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Assert there is a save button.
|
||||
$this->assertTrue(!empty($save_button));
|
||||
$this->assertNoRaw('dropbutton-wrapper');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional\Migrate\d6;
|
||||
use Drupal\Tests\node\Kernel\Migrate\d6\MigrateNodeTestBase;
|
||||
|
||||
/**
|
||||
* Node content revisions migration.
|
||||
*
|
||||
* @group migrate_drupal_6
|
||||
*/
|
||||
class MigrateNodeRevisionTest extends MigrateNodeTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['language', 'content_translation', 'menu_ui'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->executeMigrations(['d6_node', 'd6_node_revision']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test node revisions migration from Drupal 6 to 8.
|
||||
*/
|
||||
public function testNodeRevision() {
|
||||
$node = \Drupal::entityManager()->getStorage('node')->loadRevision(2);
|
||||
/** @var \Drupal\node\NodeInterface $node */
|
||||
$this->assertIdentical('1', $node->id());
|
||||
$this->assertIdentical('2', $node->getRevisionId());
|
||||
$this->assertIdentical('und', $node->langcode->value);
|
||||
$this->assertIdentical('Test title rev 2', $node->getTitle());
|
||||
$this->assertIdentical('body test rev 2', $node->body->value);
|
||||
$this->assertIdentical('teaser test rev 2', $node->body->summary);
|
||||
$this->assertIdentical('2', $node->getRevisionUser()->id());
|
||||
$this->assertIdentical('modified rev 2', $node->revision_log->value);
|
||||
$this->assertIdentical('1390095702', $node->getRevisionCreationTime());
|
||||
|
||||
$node = \Drupal::entityManager()->getStorage('node')->loadRevision(5);
|
||||
$this->assertIdentical('1', $node->id());
|
||||
$this->assertIdentical('body test rev 3', $node->body->value);
|
||||
$this->assertIdentical('1', $node->getRevisionUser()->id());
|
||||
$this->assertIdentical('modified rev 3', $node->revision_log->value);
|
||||
$this->assertIdentical('1390095703', $node->getRevisionCreationTime());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional\Migrate\d7;
|
||||
|
||||
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
|
||||
|
||||
/**
|
||||
* Tests the d7_node node deriver.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeMigrateDeriverTest extends MigrateDrupal7TestBase {
|
||||
|
||||
public static $modules = ['node'];
|
||||
|
||||
public function testBuilder() {
|
||||
$process = $this->getMigration('d7_node:test_content_type')->getProcess();
|
||||
$this->assertIdentical('field_boolean', $process['field_boolean'][0]['source']);
|
||||
$this->assertIdentical('field_email', $process['field_email'][0]['source']);
|
||||
$this->assertIdentical('field_phone', $process['field_phone'][0]['source']);
|
||||
$this->assertIdentical('field_date', $process['field_date'][0]['source']);
|
||||
$this->assertIdentical('field_date_with_end_time', $process['field_date_with_end_time'][0]['source']);
|
||||
$this->assertIdentical('field_file', $process['field_file'][0]['source']);
|
||||
$this->assertIdentical('field_float', $process['field_float'][0]['source']);
|
||||
$this->assertIdentical('field_images', $process['field_images'][0]['source']);
|
||||
$this->assertIdentical('field_integer', $process['field_integer'][0]['source']);
|
||||
$this->assertIdentical('field_link', $process['field_link'][0]['source']);
|
||||
$this->assertIdentical('field_text_list', $process['field_text_list'][0]['source']);
|
||||
$this->assertIdentical('field_integer_list', $process['field_integer_list'][0]['source']);
|
||||
$this->assertIdentical('field_long_text', $process['field_long_text'][0]['source']);
|
||||
$this->assertIdentical('field_term_reference', $process['field_term_reference'][0]['source']);
|
||||
$this->assertIdentical('field_text', $process['field_text'][0]['source']);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Tests the persistence of basic options through multiple steps.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class MultiStepNodeFormBasicOptionsTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* The field name to create.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName;
|
||||
|
||||
/**
|
||||
* Tests changing the default values of basic options to ensure they persist.
|
||||
*/
|
||||
public function testMultiStepNodeFormBasicOptions() {
|
||||
// Prepare a user to create the node.
|
||||
$web_user = $this->drupalCreateUser(['administer nodes', 'create page content']);
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
// Create an unlimited cardinality field.
|
||||
$this->fieldName = Unicode::strtolower($this->randomMachineName());
|
||||
FieldStorageConfig::create([
|
||||
'field_name' => $this->fieldName,
|
||||
'entity_type' => 'node',
|
||||
'type' => 'text',
|
||||
'cardinality' => -1,
|
||||
])->save();
|
||||
|
||||
// Attach an instance of the field to the page content type.
|
||||
FieldConfig::create([
|
||||
'field_name' => $this->fieldName,
|
||||
'entity_type' => 'node',
|
||||
'bundle' => 'page',
|
||||
'label' => $this->randomMachineName() . '_label',
|
||||
])->save();
|
||||
entity_get_form_display('node', 'page', 'default')
|
||||
->setComponent($this->fieldName, [
|
||||
'type' => 'text_textfield',
|
||||
])
|
||||
->save();
|
||||
|
||||
$edit = [
|
||||
'title[0][value]' => 'a',
|
||||
'promote[value]' => FALSE,
|
||||
'sticky[value]' => 1,
|
||||
"{$this->fieldName}[0][value]" => $this->randomString(32),
|
||||
];
|
||||
$this->drupalPostForm('node/add/page', $edit, t('Add another item'));
|
||||
$this->assertNoFieldChecked('edit-promote-value', 'Promote stayed unchecked');
|
||||
$this->assertFieldChecked('edit-sticky-value', 'Sticky stayed checked');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
|
||||
|
||||
/**
|
||||
* Tests the node access automatic cacheability bubbling logic.
|
||||
*
|
||||
* @group node
|
||||
* @group Cache
|
||||
* @group cacheability_safeguards
|
||||
*/
|
||||
class NodeAccessAutoBubblingTest extends NodeTestBase {
|
||||
|
||||
use AssertPageCacheContextsAndTagsTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node_access_test', 'node_access_test_auto_bubbling'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
node_access_rebuild();
|
||||
|
||||
// Create some content.
|
||||
$this->drupalCreateNode();
|
||||
$this->drupalCreateNode();
|
||||
$this->drupalCreateNode();
|
||||
$this->drupalCreateNode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the node grants cache context is auto-added, only when needed.
|
||||
*
|
||||
* @see node_query_node_access_alter()
|
||||
*/
|
||||
public function testNodeAccessCacheabilitySafeguard() {
|
||||
$this->dumpHeaders = TRUE;
|
||||
|
||||
// The node grants cache context should be added automatically.
|
||||
$this->drupalGet(new Url('node_access_test_auto_bubbling'));
|
||||
$this->assertCacheContext('user.node_grants:view');
|
||||
|
||||
// The root user has the 'bypass node access' permission, which means the
|
||||
// node grants cache context is not necessary.
|
||||
$this->drupalLogin($this->rootUser);
|
||||
$this->drupalGet(new Url('node_access_test_auto_bubbling'));
|
||||
$this->assertNoCacheContext('user.node_grants:view');
|
||||
$this->drupalLogout();
|
||||
|
||||
// Uninstall the module with the only hook_node_grants() implementation.
|
||||
$this->container->get('module_installer')->uninstall(['node_access_test']);
|
||||
$this->rebuildContainer();
|
||||
|
||||
// Because there are no node grants defined, there also is no need for the
|
||||
// node grants cache context to be bubbled.
|
||||
$this->drupalGet(new Url('node_access_test_auto_bubbling'));
|
||||
$this->assertNoCacheContext('user.node_grants:view');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\node\Entity\NodeType;
|
||||
|
||||
/**
|
||||
* Tests behavior of the node access subsystem if the base table is not node.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeAccessBaseTableTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node_access_test', 'views'];
|
||||
|
||||
/**
|
||||
* The installation profile to use with this test.
|
||||
*
|
||||
* This test class requires the "tags" taxonomy field.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $profile = 'standard';
|
||||
|
||||
/**
|
||||
* Nodes by user.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $nodesByUser;
|
||||
|
||||
/**
|
||||
* A public tid.
|
||||
*
|
||||
* @var \Drupal\Core\Database\StatementInterface
|
||||
*/
|
||||
protected $publicTid;
|
||||
|
||||
/**
|
||||
* A private tid.
|
||||
*
|
||||
* @var \Drupal\Core\Database\StatementInterface
|
||||
*/
|
||||
protected $privateTid;
|
||||
|
||||
/**
|
||||
* A web user.
|
||||
*/
|
||||
protected $webUser;
|
||||
|
||||
/**
|
||||
* The nids visible.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $nidsVisible;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
node_access_test_add_field(NodeType::load('article'));
|
||||
|
||||
node_access_rebuild();
|
||||
\Drupal::state()->set('node_access_test.private', TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the "private" node access functionality.
|
||||
*
|
||||
* - Create 2 users with "access content" and "create article" permissions.
|
||||
* - Each user creates one private and one not private article.
|
||||
*
|
||||
* - Test that each user can view the other user's non-private article.
|
||||
* - Test that each user cannot view the other user's private article.
|
||||
* - Test that each user finds only appropriate (non-private + own private)
|
||||
* in taxonomy listing.
|
||||
* - Create another user with 'view any private content'.
|
||||
* - Test that user 4 can view all content created above.
|
||||
* - Test that user 4 can view all content on taxonomy listing.
|
||||
*/
|
||||
public function testNodeAccessBasic() {
|
||||
$num_simple_users = 2;
|
||||
$simple_users = [];
|
||||
|
||||
// Nodes keyed by uid and nid: $nodes[$uid][$nid] = $is_private;
|
||||
$this->nodesByUser = [];
|
||||
// Titles keyed by nid.
|
||||
$titles = [];
|
||||
// Array of nids marked private.
|
||||
$private_nodes = [];
|
||||
for ($i = 0; $i < $num_simple_users; $i++) {
|
||||
$simple_users[$i] = $this->drupalCreateUser(['access content', 'create article content']);
|
||||
}
|
||||
foreach ($simple_users as $this->webUser) {
|
||||
$this->drupalLogin($this->webUser);
|
||||
foreach ([0 => 'Public', 1 => 'Private'] as $is_private => $type) {
|
||||
$edit = [
|
||||
'title[0][value]' => t('@private_public Article created by @user', ['@private_public' => $type, '@user' => $this->webUser->getUsername()]),
|
||||
];
|
||||
if ($is_private) {
|
||||
$edit['private[0][value]'] = TRUE;
|
||||
$edit['body[0][value]'] = 'private node';
|
||||
$edit['field_tags[target_id]'] = 'private';
|
||||
}
|
||||
else {
|
||||
$edit['body[0][value]'] = 'public node';
|
||||
$edit['field_tags[target_id]'] = 'public';
|
||||
}
|
||||
|
||||
$this->drupalPostForm('node/add/article', $edit, t('Save'));
|
||||
$node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
|
||||
$this->assertEqual($is_private, (int)$node->private->value, 'The private status of the node was properly set in the node_access_test table.');
|
||||
if ($is_private) {
|
||||
$private_nodes[] = $node->id();
|
||||
}
|
||||
$titles[$node->id()] = $edit['title[0][value]'];
|
||||
$this->nodesByUser[$this->webUser->id()][$node->id()] = $is_private;
|
||||
}
|
||||
}
|
||||
$this->publicTid = db_query('SELECT tid FROM {taxonomy_term_field_data} WHERE name = :name AND default_langcode = 1', [':name' => 'public'])->fetchField();
|
||||
$this->privateTid = db_query('SELECT tid FROM {taxonomy_term_field_data} WHERE name = :name AND default_langcode = 1', [':name' => 'private'])->fetchField();
|
||||
$this->assertTrue($this->publicTid, 'Public tid was found');
|
||||
$this->assertTrue($this->privateTid, 'Private tid was found');
|
||||
foreach ($simple_users as $this->webUser) {
|
||||
$this->drupalLogin($this->webUser);
|
||||
// Check own nodes to see that all are readable.
|
||||
foreach ($this->nodesByUser as $uid => $data) {
|
||||
foreach ($data as $nid => $is_private) {
|
||||
$this->drupalGet('node/' . $nid);
|
||||
if ($is_private) {
|
||||
$should_be_visible = $uid == $this->webUser->id();
|
||||
}
|
||||
else {
|
||||
$should_be_visible = TRUE;
|
||||
}
|
||||
$this->assertResponse($should_be_visible ? 200 : 403, strtr('A %private node by user %uid is %visible for user %current_uid.', [
|
||||
'%private' => $is_private ? 'private' : 'public',
|
||||
'%uid' => $uid,
|
||||
'%visible' => $should_be_visible ? 'visible' : 'not visible',
|
||||
'%current_uid' => $this->webUser->id(),
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
// Check to see that the correct nodes are shown on taxonomy/private
|
||||
// and taxonomy/public.
|
||||
$this->assertTaxonomyPage(FALSE);
|
||||
}
|
||||
|
||||
// Now test that a user with 'node test view' permissions can view content.
|
||||
$access_user = $this->drupalCreateUser(['access content', 'create article content', 'node test view', 'search content']);
|
||||
$this->drupalLogin($access_user);
|
||||
|
||||
foreach ($this->nodesByUser as $private_status) {
|
||||
foreach ($private_status as $nid => $is_private) {
|
||||
$this->drupalGet('node/' . $nid);
|
||||
$this->assertResponse(200);
|
||||
}
|
||||
}
|
||||
|
||||
// This user should be able to see all of the nodes on the relevant
|
||||
// taxonomy pages.
|
||||
$this->assertTaxonomyPage(TRUE);
|
||||
|
||||
// Rebuild the node access permissions, repeat the test. This is done to
|
||||
// ensure that node access is rebuilt correctly even if the current user
|
||||
// does not have the bypass node access permission.
|
||||
node_access_rebuild();
|
||||
|
||||
foreach ($this->nodesByUser as $private_status) {
|
||||
foreach ($private_status as $nid => $is_private) {
|
||||
$this->drupalGet('node/' . $nid);
|
||||
$this->assertResponse(200);
|
||||
}
|
||||
}
|
||||
|
||||
// This user should be able to see all of the nodes on the relevant
|
||||
// taxonomy pages.
|
||||
$this->assertTaxonomyPage(TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks taxonomy/term listings to ensure only accessible nodes are listed.
|
||||
*
|
||||
* @param $is_admin
|
||||
* A boolean indicating whether the current user is an administrator. If
|
||||
* TRUE, all nodes should be listed. If FALSE, only public nodes and the
|
||||
* user's own private nodes should be listed.
|
||||
*/
|
||||
protected function assertTaxonomyPage($is_admin) {
|
||||
foreach ([$this->publicTid, $this->privateTid] as $tid_is_private => $tid) {
|
||||
$this->drupalGet("taxonomy/term/$tid");
|
||||
$this->nidsVisible = [];
|
||||
foreach ($this->xpath("//a[text()='Read more']") as $link) {
|
||||
// See also testTranslationRendering() in NodeTranslationUITest.
|
||||
$this->assertTrue(preg_match('|node/(\d+)$|', $link->getAttribute('href'), $matches), 'Read more points to a node');
|
||||
$this->nidsVisible[$matches[1]] = TRUE;
|
||||
}
|
||||
foreach ($this->nodesByUser as $uid => $data) {
|
||||
foreach ($data as $nid => $is_private) {
|
||||
// Private nodes should be visible on the private term page,
|
||||
// public nodes should be visible on the public term page.
|
||||
$should_be_visible = $tid_is_private == $is_private;
|
||||
// Non-administrators can only see their own nodes on the private
|
||||
// term page.
|
||||
if (!$is_admin && $tid_is_private) {
|
||||
$should_be_visible = $should_be_visible && $uid == $this->webUser->id();
|
||||
}
|
||||
$this->assertIdentical(isset($this->nidsVisible[$nid]), $should_be_visible, strtr('A %private node by user %uid is %visible for user %current_uid on the %tid_is_private page.', [
|
||||
'%private' => $is_private ? 'private' : 'public',
|
||||
'%uid' => $uid,
|
||||
'%visible' => isset($this->nidsVisible[$nid]) ? 'visible' : 'not visible',
|
||||
'%current_uid' => $this->webUser->id(),
|
||||
'%tid_is_private' => $tid_is_private ? 'private' : 'public',
|
||||
]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Tests the interaction of the node access system with fields.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeAccessFieldTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node_access_test', 'field_ui'];
|
||||
|
||||
/**
|
||||
* A user with permission to bypass access content.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* A user with permission to manage content types and fields.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $contentAdminUser;
|
||||
|
||||
/**
|
||||
* The name of the created field.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
node_access_rebuild();
|
||||
|
||||
// Create some users.
|
||||
$this->adminUser = $this->drupalCreateUser(['access content', 'bypass node access']);
|
||||
$this->contentAdminUser = $this->drupalCreateUser(['access content', 'administer content types', 'administer node fields']);
|
||||
|
||||
// Add a custom field to the page content type.
|
||||
$this->fieldName = Unicode::strtolower($this->randomMachineName() . '_field_name');
|
||||
FieldStorageConfig::create([
|
||||
'field_name' => $this->fieldName,
|
||||
'entity_type' => 'node',
|
||||
'type' => 'text'
|
||||
])->save();
|
||||
FieldConfig::create([
|
||||
'field_name' => $this->fieldName,
|
||||
'entity_type' => 'node',
|
||||
'bundle' => 'page',
|
||||
])->save();
|
||||
entity_get_display('node', 'page', 'default')
|
||||
->setComponent($this->fieldName)
|
||||
->save();
|
||||
entity_get_form_display('node', 'page', 'default')
|
||||
->setComponent($this->fieldName)
|
||||
->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests administering fields when node access is restricted.
|
||||
*/
|
||||
public function testNodeAccessAdministerField() {
|
||||
// Create a page node.
|
||||
$fieldData = [];
|
||||
$value = $fieldData[0]['value'] = $this->randomMachineName();
|
||||
$node = $this->drupalCreateNode([$this->fieldName => $fieldData]);
|
||||
|
||||
// Log in as the administrator and confirm that the field value is present.
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$this->assertText($value, 'The saved field value is visible to an administrator.');
|
||||
|
||||
// Log in as the content admin and try to view the node.
|
||||
$this->drupalLogin($this->contentAdminUser);
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$this->assertText('Access denied', 'Access is denied for the content admin.');
|
||||
|
||||
// Modify the field default as the content admin.
|
||||
$edit = [];
|
||||
$default = 'Sometimes words have two meanings';
|
||||
$edit["default_value_input[{$this->fieldName}][0][value]"] = $default;
|
||||
$this->drupalPostForm(
|
||||
"admin/structure/types/manage/page/fields/node.page.{$this->fieldName}",
|
||||
$edit,
|
||||
t('Save settings')
|
||||
);
|
||||
|
||||
// Log in as the administrator.
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Confirm that the existing node still has the correct field value.
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$this->assertText($value, 'The original field value is visible to an administrator.');
|
||||
|
||||
// Confirm that the new default value appears when creating a new node.
|
||||
$this->drupalGet('node/add/page');
|
||||
$this->assertRaw($default, 'The updated default value is displayed when creating a new node.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
/**
|
||||
* Tests the node access grants cache context service.
|
||||
*
|
||||
* @group node
|
||||
* @group Cache
|
||||
*/
|
||||
class NodeAccessGrantsCacheContextTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node_access_test'];
|
||||
|
||||
/**
|
||||
* User with permission to view content.
|
||||
*/
|
||||
protected $accessUser;
|
||||
|
||||
/**
|
||||
* User without permission to view content.
|
||||
*/
|
||||
protected $noAccessUser;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
node_access_rebuild();
|
||||
|
||||
// Create some content.
|
||||
$this->drupalCreateNode();
|
||||
$this->drupalCreateNode();
|
||||
$this->drupalCreateNode();
|
||||
$this->drupalCreateNode();
|
||||
|
||||
// Create user with simple node access permission. The 'node test view'
|
||||
// permission is implemented and granted by the node_access_test module.
|
||||
$this->accessUser = $this->drupalCreateUser(['access content overview', 'access content', 'node test view']);
|
||||
$this->noAccessUser = $this->drupalCreateUser(['access content overview', 'access content']);
|
||||
$this->noAccessUser2 = $this->drupalCreateUser(['access content overview', 'access content']);
|
||||
|
||||
$this->userMapping = [
|
||||
1 => $this->rootUser,
|
||||
2 => $this->accessUser,
|
||||
3 => $this->noAccessUser,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that for each given user, the expected cache context is returned.
|
||||
*
|
||||
* @param array $expected
|
||||
* Expected values, keyed by user ID, expected cache contexts as values.
|
||||
*/
|
||||
protected function assertUserCacheContext(array $expected) {
|
||||
foreach ($expected as $uid => $context) {
|
||||
if ($uid > 0) {
|
||||
$this->drupalLogin($this->userMapping[$uid]);
|
||||
}
|
||||
$this->pass('Asserting cache context for user ' . $uid . '.');
|
||||
$this->assertIdentical($context, $this->container->get('cache_context.user.node_grants')->getContext('view'));
|
||||
}
|
||||
$this->drupalLogout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests NodeAccessGrantsCacheContext::getContext().
|
||||
*/
|
||||
public function testCacheContext() {
|
||||
$this->assertUserCacheContext([
|
||||
0 => 'view.all:0;node_access_test_author:0;node_access_all:0',
|
||||
1 => 'all',
|
||||
2 => 'view.all:0;node_access_test_author:2;node_access_test:8888,8889',
|
||||
3 => 'view.all:0;node_access_test_author:3',
|
||||
]);
|
||||
|
||||
// Grant view to all nodes (because nid = 0) for users in the
|
||||
// 'node_access_all' realm.
|
||||
$record = [
|
||||
'nid' => 0,
|
||||
'gid' => 0,
|
||||
'realm' => 'node_access_all',
|
||||
'grant_view' => 1,
|
||||
'grant_update' => 0,
|
||||
'grant_delete' => 0,
|
||||
];
|
||||
db_insert('node_access')->fields($record)->execute();
|
||||
|
||||
// Put user accessUser (uid 0) in the realm.
|
||||
\Drupal::state()->set('node_access_test.no_access_uid', 0);
|
||||
drupal_static_reset('node_access_view_all_nodes');
|
||||
$this->assertUserCacheContext([
|
||||
0 => 'view.all',
|
||||
1 => 'all',
|
||||
2 => 'view.all:0;node_access_test_author:2;node_access_test:8888,8889',
|
||||
3 => 'view.all:0;node_access_test_author:3',
|
||||
]);
|
||||
|
||||
// Put user accessUser (uid 2) in the realm.
|
||||
\Drupal::state()->set('node_access_test.no_access_uid', $this->accessUser->id());
|
||||
drupal_static_reset('node_access_view_all_nodes');
|
||||
$this->assertUserCacheContext([
|
||||
0 => 'view.all:0;node_access_test_author:0',
|
||||
1 => 'all',
|
||||
2 => 'view.all',
|
||||
3 => 'view.all:0;node_access_test_author:3',
|
||||
]);
|
||||
|
||||
// Put user noAccessUser (uid 3) in the realm.
|
||||
\Drupal::state()->set('node_access_test.no_access_uid', $this->noAccessUser->id());
|
||||
drupal_static_reset('node_access_view_all_nodes');
|
||||
$this->assertUserCacheContext([
|
||||
0 => 'view.all:0;node_access_test_author:0',
|
||||
1 => 'all',
|
||||
2 => 'view.all:0;node_access_test_author:2;node_access_test:8888,8889',
|
||||
3 => 'view.all',
|
||||
]);
|
||||
|
||||
// Uninstall the node_access_test module
|
||||
$this->container->get('module_installer')->uninstall(['node_access_test']);
|
||||
drupal_static_reset('node_access_view_all_nodes');
|
||||
$this->assertUserCacheContext([
|
||||
0 => 'view.all',
|
||||
1 => 'all',
|
||||
2 => 'view.all',
|
||||
3 => 'view.all',
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,334 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\user\Entity\User;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Tests node access functionality with multiple languages and two node access
|
||||
* modules.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeAccessLanguageAwareCombinationTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Enable language and two node access modules.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['language', 'node_access_test_language', 'node_access_test'];
|
||||
|
||||
/**
|
||||
* A set of nodes to use in testing.
|
||||
*
|
||||
* @var \Drupal\node\NodeInterface[]
|
||||
*/
|
||||
protected $nodes = [];
|
||||
|
||||
/**
|
||||
* A normal authenticated user.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface.
|
||||
*/
|
||||
protected $webUser;
|
||||
|
||||
/**
|
||||
* User 1.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface.
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
node_access_test_add_field(NodeType::load('page'));
|
||||
|
||||
// Create the 'private' field, which allows the node to be marked as private
|
||||
// (restricted access) in a given translation.
|
||||
$field_storage = FieldStorageConfig::create([
|
||||
'field_name' => 'field_private',
|
||||
'entity_type' => 'node',
|
||||
'type' => 'boolean',
|
||||
'cardinality' => 1,
|
||||
]);
|
||||
$field_storage->save();
|
||||
|
||||
FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'page',
|
||||
'widget' => [
|
||||
'type' => 'options_buttons',
|
||||
],
|
||||
'settings' => [
|
||||
'on_label' => 'Private',
|
||||
'off_label' => 'Not private',
|
||||
],
|
||||
])->save();
|
||||
|
||||
// After enabling a node access module, the access table has to be rebuild.
|
||||
node_access_rebuild();
|
||||
|
||||
// Add Hungarian and Catalan.
|
||||
ConfigurableLanguage::createFromLangcode('hu')->save();
|
||||
ConfigurableLanguage::createFromLangcode('ca')->save();
|
||||
|
||||
// Create a normal authenticated user.
|
||||
$this->webUser = $this->drupalCreateUser(['access content']);
|
||||
|
||||
// Load the user 1 user for later use as an admin user with permission to
|
||||
// see everything.
|
||||
$this->adminUser = User::load(1);
|
||||
|
||||
// The node_access_test_language module allows individual translations of a
|
||||
// node to be marked private (not viewable by normal users), and the
|
||||
// node_access_test module allows whole nodes to be marked private. (In a
|
||||
// real-world implementation, hook_node_access_records_alter() might be
|
||||
// implemented by one or both modules to enforce that private nodes or
|
||||
// translations are always private, but we want to test the default,
|
||||
// additive behavior of node access).
|
||||
|
||||
// Create six Hungarian nodes with Catalan translations:
|
||||
// 1. One public with neither language marked as private.
|
||||
// 2. One private with neither language marked as private.
|
||||
// 3. One public with only the Hungarian translation private.
|
||||
// 4. One public with only the Catalan translation private.
|
||||
// 5. One public with both the Hungarian and Catalan translations private.
|
||||
// 6. One private with both the Hungarian and Catalan translations private.
|
||||
$this->nodes['public_both_public'] = $node = $this->drupalCreateNode([
|
||||
'body' => [[]],
|
||||
'langcode' => 'hu',
|
||||
'field_private' => [['value' => 0]],
|
||||
'private' => FALSE,
|
||||
]);
|
||||
$translation = $node->addTranslation('ca');
|
||||
$translation->title->value = $this->randomString();
|
||||
$translation->field_private->value = 0;
|
||||
$node->save();
|
||||
|
||||
$this->nodes['private_both_public'] = $node = $this->drupalCreateNode([
|
||||
'body' => [[]],
|
||||
'langcode' => 'hu',
|
||||
'field_private' => [['value' => 0]],
|
||||
'private' => TRUE,
|
||||
]);
|
||||
$translation = $node->addTranslation('ca');
|
||||
$translation->title->value = $this->randomString();
|
||||
$translation->field_private->value = 0;
|
||||
$node->save();
|
||||
|
||||
$this->nodes['public_hu_private'] = $node = $this->drupalCreateNode([
|
||||
'body' => [[]],
|
||||
'langcode' => 'hu',
|
||||
'field_private' => [['value' => 1]],
|
||||
'private' => FALSE,
|
||||
]);
|
||||
$translation = $node->addTranslation('ca');
|
||||
$translation->title->value = $this->randomString();
|
||||
$translation->field_private->value = 0;
|
||||
$node->save();
|
||||
|
||||
$this->nodes['public_ca_private'] = $node = $this->drupalCreateNode([
|
||||
'body' => [[]],
|
||||
'langcode' => 'hu',
|
||||
'field_private' => [['value' => 0]],
|
||||
'private' => FALSE,
|
||||
]);
|
||||
$translation = $node->addTranslation('ca');
|
||||
$translation->title->value = $this->randomString();
|
||||
$translation->field_private->value = 1;
|
||||
$node->save();
|
||||
|
||||
$this->nodes['public_both_private'] = $node = $this->drupalCreateNode([
|
||||
'body' => [[]],
|
||||
'langcode' => 'hu',
|
||||
'field_private' => [['value' => 1]],
|
||||
'private' => FALSE,
|
||||
]);
|
||||
$translation = $node->addTranslation('ca');
|
||||
$translation->title->value = $this->randomString();
|
||||
$translation->field_private->value = 1;
|
||||
$node->save();
|
||||
|
||||
$this->nodes['private_both_private'] = $node = $this->drupalCreateNode([
|
||||
'body' => [[]],
|
||||
'langcode' => 'hu',
|
||||
'field_private' => [['value' => 1]],
|
||||
'private' => TRUE,
|
||||
]);
|
||||
$translation = $node->addTranslation('ca');
|
||||
$translation->title->value = $this->randomString();
|
||||
$translation->field_private->value = 1;
|
||||
$node->save();
|
||||
|
||||
$this->nodes['public_no_language_private'] = $this->drupalCreateNode([
|
||||
'field_private' => [['value' => 1]],
|
||||
'private' => FALSE,
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
]);
|
||||
$this->nodes['public_no_language_public'] = $this->drupalCreateNode([
|
||||
'field_private' => [['value' => 0]],
|
||||
'private' => FALSE,
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
]);
|
||||
$this->nodes['private_no_language_private'] = $this->drupalCreateNode([
|
||||
'field_private' => [['value' => 1]],
|
||||
'private' => TRUE,
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
]);
|
||||
$this->nodes['private_no_language_public'] = $this->drupalCreateNode([
|
||||
'field_private' => [['value' => 1]],
|
||||
'private' => TRUE,
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests node access and node access queries with multiple node languages.
|
||||
*/
|
||||
public function testNodeAccessLanguageAwareCombination() {
|
||||
|
||||
$expected_node_access = ['view' => TRUE, 'update' => FALSE, 'delete' => FALSE];
|
||||
$expected_node_access_no_access = ['view' => FALSE, 'update' => FALSE, 'delete' => FALSE];
|
||||
|
||||
// When the node and both translations are public, access should always be
|
||||
// granted.
|
||||
$this->assertNodeAccess($expected_node_access, $this->nodes['public_both_public'], $this->webUser);
|
||||
$this->assertNodeAccess($expected_node_access, $this->nodes['public_both_public']->getTranslation('hu'), $this->webUser);
|
||||
$this->assertNodeAccess($expected_node_access, $this->nodes['public_both_public']->getTranslation('ca'), $this->webUser);
|
||||
|
||||
// If the node is marked private but both existing translations are not,
|
||||
// access should still be granted, because the grants are additive.
|
||||
$this->assertNodeAccess($expected_node_access, $this->nodes['private_both_public'], $this->webUser);
|
||||
$this->assertNodeAccess($expected_node_access, $this->nodes['private_both_public']->getTranslation('hu'), $this->webUser);
|
||||
$this->assertNodeAccess($expected_node_access, $this->nodes['private_both_public']->getTranslation('ca'), $this->webUser);
|
||||
|
||||
// If the node is marked private, but a existing translation is public,
|
||||
// access should only be granted for the public translation. With the
|
||||
// Hungarian translation marked as private, but the Catalan translation
|
||||
// public, the access is granted.
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $this->nodes['public_hu_private'], $this->webUser);
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $this->nodes['public_hu_private']->getTranslation('hu'), $this->webUser);
|
||||
$this->assertNodeAccess($expected_node_access, $this->nodes['public_hu_private']->getTranslation('ca'), $this->webUser);
|
||||
|
||||
// With the Catalan translation marked as private, but the node public,
|
||||
// access is granted for the existing Hungarian translation, but not for the
|
||||
// Catalan.
|
||||
$this->assertNodeAccess($expected_node_access, $this->nodes['public_ca_private'], $this->webUser);
|
||||
$this->assertNodeAccess($expected_node_access, $this->nodes['public_ca_private']->getTranslation('hu'), $this->webUser);
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $this->nodes['public_ca_private']->getTranslation('ca'), $this->webUser);
|
||||
|
||||
// With both translations marked as private, but the node public, access
|
||||
// should be denied in all cases.
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $this->nodes['public_both_private'], $this->webUser);
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $this->nodes['public_both_private']->getTranslation('hu'), $this->webUser);
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $this->nodes['public_both_private']->getTranslation('ca'), $this->webUser);
|
||||
|
||||
// If the node and both its existing translations are private, access should
|
||||
// be denied in all cases.
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $this->nodes['private_both_private'], $this->webUser);
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $this->nodes['private_both_private']->getTranslation('hu'), $this->webUser);
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $this->nodes['private_both_private']->getTranslation('ca'), $this->webUser);
|
||||
|
||||
// No access for all languages as the language aware node access module
|
||||
// denies access.
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $this->nodes['public_no_language_private'], $this->webUser);
|
||||
|
||||
// Access only for request with no language defined.
|
||||
$this->assertNodeAccess($expected_node_access, $this->nodes['public_no_language_public'], $this->webUser);
|
||||
|
||||
// No access for all languages as both node access modules deny access.
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $this->nodes['private_no_language_private'], $this->webUser);
|
||||
|
||||
// No access for all languages as the non language aware node access module
|
||||
// denies access.
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $this->nodes['private_no_language_public'], $this->webUser);
|
||||
|
||||
// Query the node table with the node access tag in several languages.
|
||||
|
||||
// Query with no language specified. The fallback (hu or und) will be used.
|
||||
$select = db_select('node', 'n')
|
||||
->fields('n', ['nid'])
|
||||
->addMetaData('account', $this->webUser)
|
||||
->addTag('node_access');
|
||||
$nids = $select->execute()->fetchAllAssoc('nid');
|
||||
|
||||
// Four nodes should be returned with public Hungarian translations or the
|
||||
// no language public node.
|
||||
$this->assertEqual(count($nids), 4, 'db_select() returns 4 nodes when no langcode is specified.');
|
||||
$this->assertTrue(array_key_exists($this->nodes['public_both_public']->id(), $nids), 'Returned node ID is full public node.');
|
||||
$this->assertTrue(array_key_exists($this->nodes['public_ca_private']->id(), $nids), 'Returned node ID is Hungarian public only node.');
|
||||
$this->assertTrue(array_key_exists($this->nodes['private_both_public']->id(), $nids), 'Returned node ID is both public non-language-aware private only node.');
|
||||
$this->assertTrue(array_key_exists($this->nodes['public_no_language_public']->id(), $nids), 'Returned node ID is no language public node.');
|
||||
|
||||
// Query with Hungarian (hu) specified.
|
||||
$select = db_select('node', 'n')
|
||||
->fields('n', ['nid'])
|
||||
->addMetaData('account', $this->webUser)
|
||||
->addMetaData('langcode', 'hu')
|
||||
->addTag('node_access');
|
||||
$nids = $select->execute()->fetchAllAssoc('nid');
|
||||
|
||||
// Three nodes should be returned (with public Hungarian translations).
|
||||
$this->assertEqual(count($nids), 3, 'db_select() returns 3 nodes.');
|
||||
$this->assertTrue(array_key_exists($this->nodes['public_both_public']->id(), $nids), 'Returned node ID is both public node.');
|
||||
$this->assertTrue(array_key_exists($this->nodes['public_ca_private']->id(), $nids), 'Returned node ID is Hungarian public only node.');
|
||||
$this->assertTrue(array_key_exists($this->nodes['private_both_public']->id(), $nids), 'Returned node ID is both public non-language-aware private only node.');
|
||||
|
||||
// Query with Catalan (ca) specified.
|
||||
$select = db_select('node', 'n')
|
||||
->fields('n', ['nid'])
|
||||
->addMetaData('account', $this->webUser)
|
||||
->addMetaData('langcode', 'ca')
|
||||
->addTag('node_access');
|
||||
$nids = $select->execute()->fetchAllAssoc('nid');
|
||||
|
||||
// Three nodes should be returned (with public Catalan translations).
|
||||
$this->assertEqual(count($nids), 3, 'db_select() returns 3 nodes.');
|
||||
$this->assertTrue(array_key_exists($this->nodes['public_both_public']->id(), $nids), 'Returned node ID is both public node.');
|
||||
$this->assertTrue(array_key_exists($this->nodes['public_hu_private']->id(), $nids), 'Returned node ID is Catalan public only node.');
|
||||
$this->assertTrue(array_key_exists($this->nodes['private_both_public']->id(), $nids), 'Returned node ID is both public non-language-aware private only node.');
|
||||
|
||||
// Query with German (de) specified.
|
||||
$select = db_select('node', 'n')
|
||||
->fields('n', ['nid'])
|
||||
->addMetaData('account', $this->webUser)
|
||||
->addMetaData('langcode', 'de')
|
||||
->addTag('node_access');
|
||||
$nids = $select->execute()->fetchAllAssoc('nid');
|
||||
|
||||
// There are no nodes with German translations, so no results are returned.
|
||||
$this->assertTrue(empty($nids), 'db_select() returns an empty result.');
|
||||
|
||||
// Query the nodes table as admin user (full access) with the node access
|
||||
// tag and no specific langcode.
|
||||
$select = db_select('node', 'n')
|
||||
->fields('n', ['nid'])
|
||||
->addMetaData('account', $this->adminUser)
|
||||
->addTag('node_access');
|
||||
$nids = $select->execute()->fetchAllAssoc('nid');
|
||||
|
||||
// All nodes are returned.
|
||||
$this->assertEqual(count($nids), 10, 'db_select() returns all nodes.');
|
||||
|
||||
// Query the nodes table as admin user (full access) with the node access
|
||||
// tag and langcode de.
|
||||
$select = db_select('node', 'n')
|
||||
->fields('n', ['nid'])
|
||||
->addMetaData('account', $this->adminUser)
|
||||
->addMetaData('langcode', 'de')
|
||||
->addTag('node_access');
|
||||
$nids = $select->execute()->fetchAllAssoc('nid');
|
||||
|
||||
// Even though there is no German translation, all nodes are returned
|
||||
// because node access filtering does not occur when the user is user 1.
|
||||
$this->assertEqual(count($nids), 10, 'db_select() returns all nodes.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\user\Entity\User;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Tests node_access and db_select() with node_access tag functionality with
|
||||
* multiple languages with node_access_test_language which is language-aware.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeAccessLanguageAwareTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Enable language and a language-aware node access module.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['language', 'node_access_test_language'];
|
||||
|
||||
/**
|
||||
* A set of nodes to use in testing.
|
||||
*
|
||||
* @var \Drupal\node\NodeInterface[]
|
||||
*/
|
||||
protected $nodes = [];
|
||||
|
||||
/**
|
||||
* A user with permission to bypass access content.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* A normal authenticated user.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $webUser;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create the 'private' field, which allows the node to be marked as private
|
||||
// (restricted access) in a given translation.
|
||||
$field_storage = FieldStorageConfig::create([
|
||||
'field_name' => 'field_private',
|
||||
'entity_type' => 'node',
|
||||
'type' => 'boolean',
|
||||
'cardinality' => 1,
|
||||
]);
|
||||
$field_storage->save();
|
||||
|
||||
FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'page',
|
||||
'widget' => [
|
||||
'type' => 'options_buttons',
|
||||
],
|
||||
'settings' => [
|
||||
'on_label' => 'Private',
|
||||
'off_label' => 'Not private',
|
||||
],
|
||||
])->save();
|
||||
|
||||
// After enabling a node access module, the access table has to be rebuild.
|
||||
node_access_rebuild();
|
||||
|
||||
// Create a normal authenticated user.
|
||||
$this->webUser = $this->drupalCreateUser(['access content']);
|
||||
|
||||
// Load the user 1 user for later use as an admin user with permission to
|
||||
// see everything.
|
||||
$this->adminUser = User::load(1);
|
||||
|
||||
// Add Hungarian and Catalan.
|
||||
ConfigurableLanguage::createFromLangcode('hu')->save();
|
||||
ConfigurableLanguage::createFromLangcode('ca')->save();
|
||||
|
||||
// The node_access_test_language module allows individual translations of a
|
||||
// node to be marked private (not viewable by normal users).
|
||||
|
||||
// Create six nodes:
|
||||
// 1. Four Hungarian nodes with Catalan translations
|
||||
// - One with neither language marked as private.
|
||||
// - One with only the Hungarian translation private.
|
||||
// - One with only the Catalan translation private.
|
||||
// - One with both the Hungarian and Catalan translations private.
|
||||
// 2. Two nodes with no language specified.
|
||||
// - One public.
|
||||
// - One private.
|
||||
$this->nodes['both_public'] = $node = $this->drupalCreateNode([
|
||||
'body' => [[]],
|
||||
'langcode' => 'hu',
|
||||
'field_private' => [['value' => 0]],
|
||||
]);
|
||||
$translation = $node->addTranslation('ca');
|
||||
$translation->title->value = $this->randomString();
|
||||
$translation->field_private->value = 0;
|
||||
$node->save();
|
||||
|
||||
$this->nodes['ca_private'] = $node = $this->drupalCreateNode([
|
||||
'body' => [[]],
|
||||
'langcode' => 'hu',
|
||||
'field_private' => [['value' => 0]],
|
||||
]);
|
||||
$translation = $node->addTranslation('ca');
|
||||
$translation->title->value = $this->randomString();
|
||||
$translation->field_private->value = 1;
|
||||
$node->save();
|
||||
|
||||
$this->nodes['hu_private'] = $node = $this->drupalCreateNode([
|
||||
'body' => [[]],
|
||||
'langcode' => 'hu',
|
||||
'field_private' => [['value' => 1]],
|
||||
]);
|
||||
$translation = $node->addTranslation('ca');
|
||||
$translation->title->value = $this->randomString();
|
||||
$translation->field_private->value = 0;
|
||||
$node->save();
|
||||
|
||||
$this->nodes['both_private'] = $node = $this->drupalCreateNode([
|
||||
'body' => [[]],
|
||||
'langcode' => 'hu',
|
||||
'field_private' => [['value' => 1]],
|
||||
]);
|
||||
$translation = $node->addTranslation('ca');
|
||||
$translation->title->value = $this->randomString();
|
||||
$translation->field_private->value = 1;
|
||||
$node->save();
|
||||
|
||||
$this->nodes['no_language_public'] = $this->drupalCreateNode([
|
||||
'field_private' => [['value' => 0]],
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
]);
|
||||
$this->nodes['no_language_private'] = $this->drupalCreateNode([
|
||||
'field_private' => [['value' => 1]],
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests node access and node access queries with multiple node languages.
|
||||
*/
|
||||
public function testNodeAccessLanguageAware() {
|
||||
// The node_access_test_language module only grants view access.
|
||||
$expected_node_access = ['view' => TRUE, 'update' => FALSE, 'delete' => FALSE];
|
||||
$expected_node_access_no_access = ['view' => FALSE, 'update' => FALSE, 'delete' => FALSE];
|
||||
|
||||
// When both Hungarian and Catalan are marked as public, access to the
|
||||
// Hungarian translation should be granted with the default entity object or
|
||||
// when the Hungarian translation is specified explicitly.
|
||||
$this->assertNodeAccess($expected_node_access, $this->nodes['both_public'], $this->webUser);
|
||||
$this->assertNodeAccess($expected_node_access, $this->nodes['both_public']->getTranslation('hu'), $this->webUser);
|
||||
// Access to the Catalan translation should also be granted.
|
||||
$this->assertNodeAccess($expected_node_access, $this->nodes['both_public']->getTranslation('ca'), $this->webUser);
|
||||
|
||||
// When Hungarian is marked as private, access to the Hungarian translation
|
||||
// should be denied with the default entity object or when the Hungarian
|
||||
// translation is specified explicitly.
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $this->nodes['hu_private'], $this->webUser);
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $this->nodes['hu_private']->getTranslation('hu'), $this->webUser);
|
||||
// Access to the Catalan translation should be granted.
|
||||
$this->assertNodeAccess($expected_node_access, $this->nodes['hu_private']->getTranslation('ca'), $this->webUser);
|
||||
|
||||
// When Catalan is marked as private, access to the Hungarian translation
|
||||
// should be granted with the default entity object or when the Hungarian
|
||||
// translation is specified explicitly.
|
||||
$this->assertNodeAccess($expected_node_access, $this->nodes['ca_private'], $this->webUser);
|
||||
$this->assertNodeAccess($expected_node_access, $this->nodes['ca_private']->getTranslation('hu'), $this->webUser);
|
||||
// Access to the Catalan translation should be granted.
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $this->nodes['ca_private']->getTranslation('ca'), $this->webUser);
|
||||
|
||||
// When both translations are marked as private, access should be denied
|
||||
// regardless of the entity object specified.
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $this->nodes['both_private'], $this->webUser);
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $this->nodes['both_private']->getTranslation('hu'), $this->webUser);
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $this->nodes['both_private']->getTranslation('ca'), $this->webUser);
|
||||
|
||||
// When no language is specified for a private node, access to every node
|
||||
// translation is denied.
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $this->nodes['no_language_private'], $this->webUser);
|
||||
|
||||
// When no language is specified for a public node, access should be
|
||||
// granted.
|
||||
$this->assertNodeAccess($expected_node_access, $this->nodes['no_language_public'], $this->webUser);
|
||||
|
||||
// Query the node table with the node access tag in several languages.
|
||||
|
||||
// Query with no language specified. The fallback (hu) will be used.
|
||||
$select = db_select('node', 'n')
|
||||
->fields('n', ['nid'])
|
||||
->addMetaData('account', $this->webUser)
|
||||
->addTag('node_access');
|
||||
$nids = $select->execute()->fetchAllAssoc('nid');
|
||||
|
||||
// Three nodes should be returned:
|
||||
// - Node with both translations public.
|
||||
// - Node with only the Catalan translation marked as private.
|
||||
// - No language node marked as public.
|
||||
$this->assertEqual(count($nids), 3, 'db_select() returns 3 nodes when no langcode is specified.');
|
||||
$this->assertTrue(array_key_exists($this->nodes['both_public']->id(), $nids), 'The node with both translations public is returned.');
|
||||
$this->assertTrue(array_key_exists($this->nodes['ca_private']->id(), $nids), 'The node with only the Catalan translation private is returned.');
|
||||
$this->assertTrue(array_key_exists($this->nodes['no_language_public']->id(), $nids), 'The node with no language is returned.');
|
||||
|
||||
// Query with Hungarian (hu) specified.
|
||||
$select = db_select('node', 'n')
|
||||
->fields('n', ['nid'])
|
||||
->addMetaData('account', $this->webUser)
|
||||
->addMetaData('langcode', 'hu')
|
||||
->addTag('node_access');
|
||||
$nids = $select->execute()->fetchAllAssoc('nid');
|
||||
|
||||
// Two nodes should be returned: the node with both translations public, and
|
||||
// the node with only the Catalan translation marked as private.
|
||||
$this->assertEqual(count($nids), 2, 'db_select() returns 2 nodes when the hu langcode is specified.');
|
||||
$this->assertTrue(array_key_exists($this->nodes['both_public']->id(), $nids), 'The node with both translations public is returned.');
|
||||
$this->assertTrue(array_key_exists($this->nodes['ca_private']->id(), $nids), 'The node with only the Catalan translation private is returned.');
|
||||
|
||||
// Query with Catalan (ca) specified.
|
||||
$select = db_select('node', 'n')
|
||||
->fields('n', ['nid'])
|
||||
->addMetaData('account', $this->webUser)
|
||||
->addMetaData('langcode', 'ca')
|
||||
->addTag('node_access');
|
||||
$nids = $select->execute()->fetchAllAssoc('nid');
|
||||
|
||||
// Two nodes should be returned: the node with both translations public, and
|
||||
// the node with only the Hungarian translation marked as private.
|
||||
$this->assertEqual(count($nids), 2, 'db_select() returns 2 nodes when the hu langcode is specified.');
|
||||
$this->assertTrue(array_key_exists($this->nodes['both_public']->id(), $nids), 'The node with both translations public is returned.');
|
||||
$this->assertTrue(array_key_exists($this->nodes['hu_private']->id(), $nids), 'The node with only the Hungarian translation private is returned.');
|
||||
|
||||
// Query with German (de) specified.
|
||||
$select = db_select('node', 'n')
|
||||
->fields('n', ['nid'])
|
||||
->addMetaData('account', $this->webUser)
|
||||
->addMetaData('langcode', 'de')
|
||||
->addTag('node_access');
|
||||
$nids = $select->execute()->fetchAllAssoc('nid');
|
||||
|
||||
// There are no nodes with German translations, so no results are returned.
|
||||
$this->assertTrue(empty($nids), 'db_select() returns an empty result when the de langcode is specified.');
|
||||
|
||||
// Query the nodes table as admin user (full access) with the node access
|
||||
// tag and no specific langcode.
|
||||
$select = db_select('node', 'n')
|
||||
->fields('n', ['nid'])
|
||||
->addMetaData('account', $this->adminUser)
|
||||
->addTag('node_access');
|
||||
$nids = $select->execute()->fetchAllAssoc('nid');
|
||||
|
||||
// All nodes are returned.
|
||||
$this->assertEqual(count($nids), 6, 'db_select() returns all nodes.');
|
||||
|
||||
// Query the nodes table as admin user (full access) with the node access
|
||||
// tag and langcode de.
|
||||
$select = db_select('node', 'n')
|
||||
->fields('n', ['nid'])
|
||||
->addMetaData('account', $this->adminUser)
|
||||
->addMetaData('langcode', 'de')
|
||||
->addTag('node_access');
|
||||
$nids = $select->execute()->fetchAllAssoc('nid');
|
||||
|
||||
// Even though there is no German translation, all nodes are returned
|
||||
// because node access filtering does not occur when the user is user 1.
|
||||
$this->assertEqual(count($nids), 6, 'db_select() returns all nodes.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Tests node_access and db_select() with node_access tag functionality with
|
||||
* multiple languages with a test node access module that is not language-aware.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeAccessLanguageTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['language', 'node_access_test'];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
node_access_test_add_field(NodeType::load('page'));
|
||||
|
||||
// After enabling a node access module, the access table has to be rebuild.
|
||||
node_access_rebuild();
|
||||
|
||||
// Enable the private node feature of the node_access_test module.
|
||||
\Drupal::state()->set('node_access_test.private', TRUE);
|
||||
|
||||
// Add Hungarian, Catalan and Croatian.
|
||||
ConfigurableLanguage::createFromLangcode('hu')->save();
|
||||
ConfigurableLanguage::createFromLangcode('ca')->save();
|
||||
ConfigurableLanguage::createFromLangcode('hr')->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests node access with multiple node languages and no private nodes.
|
||||
*/
|
||||
public function testNodeAccess() {
|
||||
$web_user = $this->drupalCreateUser(['access content']);
|
||||
|
||||
$expected_node_access = ['view' => TRUE, 'update' => FALSE, 'delete' => FALSE];
|
||||
$expected_node_access_no_access = ['view' => FALSE, 'update' => FALSE, 'delete' => FALSE];
|
||||
|
||||
// Creating a public node with langcode Hungarian, will be saved as the
|
||||
// fallback in node access table.
|
||||
$node_public_hu = $this->drupalCreateNode(['body' => [[]], 'langcode' => 'hu', 'private' => FALSE]);
|
||||
$this->assertTrue($node_public_hu->language()->getId() == 'hu', 'Node created as Hungarian.');
|
||||
|
||||
// Tests the default access is provided for the public Hungarian node.
|
||||
$this->assertNodeAccess($expected_node_access, $node_public_hu, $web_user);
|
||||
|
||||
// Tests that Hungarian provided specifically results in the same.
|
||||
$this->assertNodeAccess($expected_node_access, $node_public_hu->getTranslation('hu'), $web_user);
|
||||
|
||||
// Creating a public node with no special langcode, like when no language
|
||||
// module enabled.
|
||||
$node_public_no_language = $this->drupalCreateNode([
|
||||
'private' => FALSE,
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
]);
|
||||
$this->assertTrue($node_public_no_language->language()->getId() == LanguageInterface::LANGCODE_NOT_SPECIFIED, 'Node created with not specified language.');
|
||||
|
||||
// Tests that access is granted if requested with no language.
|
||||
$this->assertNodeAccess($expected_node_access, $node_public_no_language, $web_user);
|
||||
|
||||
// Reset the node access cache and turn on our test node access code.
|
||||
\Drupal::entityManager()->getAccessControlHandler('node')->resetCache();
|
||||
\Drupal::state()->set('node_access_test_secret_catalan', 1);
|
||||
$node_public_ca = $this->drupalCreateNode(['body' => [[]], 'langcode' => 'ca', 'private' => FALSE]);
|
||||
$this->assertTrue($node_public_ca->language()->getId() == 'ca', 'Node created as Catalan.');
|
||||
|
||||
// Tests that access is granted if requested with no language.
|
||||
$this->assertNodeAccess($expected_node_access, $node_public_no_language, $web_user);
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $node_public_ca, $web_user);
|
||||
|
||||
// Tests that Hungarian node is still accessible.
|
||||
$this->assertNodeAccess($expected_node_access, $node_public_hu, $web_user);
|
||||
$this->assertNodeAccess($expected_node_access, $node_public_hu->getTranslation('hu'), $web_user);
|
||||
|
||||
// Tests that Catalan is still not accessible.
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $node_public_ca->getTranslation('ca'), $web_user);
|
||||
|
||||
// Make Catalan accessible.
|
||||
\Drupal::state()->set('node_access_test_secret_catalan', 0);
|
||||
|
||||
// Tests that Catalan is accessible on a node with a Catalan version as the
|
||||
// static cache has not been reset.
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $node_public_ca, $web_user);
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $node_public_ca->getTranslation('ca'), $web_user);
|
||||
|
||||
\Drupal::entityManager()->getAccessControlHandler('node')->resetCache();
|
||||
|
||||
// Tests that access is granted if requested with no language.
|
||||
$this->assertNodeAccess($expected_node_access, $node_public_no_language, $web_user);
|
||||
$this->assertNodeAccess($expected_node_access, $node_public_ca, $web_user);
|
||||
|
||||
// Tests that Hungarian node is still accessible.
|
||||
$this->assertNodeAccess($expected_node_access, $node_public_hu, $web_user);
|
||||
$this->assertNodeAccess($expected_node_access, $node_public_hu->getTranslation('hu'), $web_user);
|
||||
|
||||
// Tests that Catalan is accessible on a node with a Catalan version.
|
||||
$this->assertNodeAccess($expected_node_access, $node_public_ca->getTranslation('ca'), $web_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests node access with multiple node languages and private nodes.
|
||||
*/
|
||||
public function testNodeAccessPrivate() {
|
||||
$web_user = $this->drupalCreateUser(['access content']);
|
||||
$expected_node_access = ['view' => TRUE, 'update' => FALSE, 'delete' => FALSE];
|
||||
$expected_node_access_no_access = ['view' => FALSE, 'update' => FALSE, 'delete' => FALSE];
|
||||
|
||||
// Creating a private node with langcode Hungarian, will be saved as the
|
||||
// fallback in node access table.
|
||||
$node_private_hu = $this->drupalCreateNode(['body' => [[]], 'langcode' => 'hu', 'private' => TRUE]);
|
||||
$this->assertTrue($node_private_hu->language()->getId() == 'hu', 'Node created as Hungarian.');
|
||||
|
||||
// Tests the default access is not provided for the private Hungarian node.
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $node_private_hu, $web_user);
|
||||
|
||||
// Tests that Hungarian provided specifically results in the same.
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $node_private_hu->getTranslation('hu'), $web_user);
|
||||
|
||||
// Creating a private node with no special langcode, like when no language
|
||||
// module enabled.
|
||||
$node_private_no_language = $this->drupalCreateNode([
|
||||
'private' => TRUE,
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
]);
|
||||
$this->assertTrue($node_private_no_language->language()->getId() == LanguageInterface::LANGCODE_NOT_SPECIFIED, 'Node created with not specified language.');
|
||||
|
||||
// Tests that access is not granted if requested with no language.
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $node_private_no_language, $web_user);
|
||||
|
||||
// Reset the node access cache and turn on our test node access code.
|
||||
\Drupal::entityManager()->getAccessControlHandler('node')->resetCache();
|
||||
\Drupal::state()->set('node_access_test_secret_catalan', 1);
|
||||
|
||||
// Tests that access is not granted if requested with no language.
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $node_private_no_language, $web_user);
|
||||
|
||||
// Creating a private node with langcode Catalan to test that the
|
||||
// node_access_test_secret_catalan flag works.
|
||||
$private_ca_user = $this->drupalCreateUser(['access content', 'node test view']);
|
||||
$node_private_ca = $this->drupalCreateNode(['body' => [[]], 'langcode' => 'ca', 'private' => TRUE]);
|
||||
$this->assertTrue($node_private_ca->language()->getId() == 'ca', 'Node created as Catalan.');
|
||||
|
||||
// Tests that Catalan is still not accessible to either user.
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $node_private_ca, $web_user);
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $node_private_ca->getTranslation('ca'), $web_user);
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $node_private_ca, $private_ca_user);
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $node_private_ca->getTranslation('ca'), $private_ca_user);
|
||||
|
||||
\Drupal::entityManager()->getAccessControlHandler('node')->resetCache();
|
||||
\Drupal::state()->set('node_access_test_secret_catalan', 0);
|
||||
|
||||
// Tests that Catalan is still not accessible for a user with no access to
|
||||
// private nodes.
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $node_private_ca, $web_user);
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $node_private_ca->getTranslation('ca'), $web_user);
|
||||
|
||||
// Tests that Catalan is accessible by a user with the permission to see
|
||||
// private nodes.
|
||||
$this->assertNodeAccess($expected_node_access, $node_private_ca, $private_ca_user);
|
||||
$this->assertNodeAccess($expected_node_access, $node_private_ca->getTranslation('ca'), $private_ca_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests db_select() with a 'node_access' tag and langcode metadata.
|
||||
*/
|
||||
public function testNodeAccessQueryTag() {
|
||||
// Create a normal authenticated user.
|
||||
$web_user = $this->drupalCreateUser(['access content']);
|
||||
|
||||
// Load the user 1 user for later use as an admin user with permission to
|
||||
// see everything.
|
||||
$admin_user = User::load(1);
|
||||
|
||||
// Creating a private node with langcode Hungarian, will be saved as
|
||||
// the fallback in node access table.
|
||||
$node_private = $this->drupalCreateNode(['body' => [[]], 'langcode' => 'hu', 'private' => TRUE]);
|
||||
$this->assertTrue($node_private->language()->getId() == 'hu', 'Node created as Hungarian.');
|
||||
|
||||
// Creating a public node with langcode Hungarian, will be saved as
|
||||
// the fallback in node access table.
|
||||
$node_public = $this->drupalCreateNode(['body' => [[]], 'langcode' => 'hu', 'private' => FALSE]);
|
||||
$this->assertTrue($node_public->language()->getId() == 'hu', 'Node created as Hungarian.');
|
||||
|
||||
// Creating a public node with no special langcode, like when no language
|
||||
// module enabled.
|
||||
$node_no_language = $this->drupalCreateNode([
|
||||
'private' => FALSE,
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
]);
|
||||
$this->assertTrue($node_no_language->language()->getId() == LanguageInterface::LANGCODE_NOT_SPECIFIED, 'Node created with not specified language.');
|
||||
|
||||
// Query the nodes table as the web user with the node access tag and no
|
||||
// specific langcode.
|
||||
$select = db_select('node', 'n')
|
||||
->fields('n', ['nid'])
|
||||
->addMetaData('account', $web_user)
|
||||
->addTag('node_access');
|
||||
$nids = $select->execute()->fetchAllAssoc('nid');
|
||||
|
||||
// The public node and no language node should be returned. Because no
|
||||
// langcode is given it will use the fallback node.
|
||||
$this->assertEqual(count($nids), 2, 'db_select() returns 2 node');
|
||||
$this->assertTrue(array_key_exists($node_public->id(), $nids), 'Returned node ID is public node.');
|
||||
$this->assertTrue(array_key_exists($node_no_language->id(), $nids), 'Returned node ID is no language node.');
|
||||
|
||||
// Query the nodes table as the web user with the node access tag and
|
||||
// langcode de.
|
||||
$select = db_select('node', 'n')
|
||||
->fields('n', ['nid'])
|
||||
->addMetaData('account', $web_user)
|
||||
->addMetaData('langcode', 'de')
|
||||
->addTag('node_access');
|
||||
$nids = $select->execute()->fetchAllAssoc('nid');
|
||||
|
||||
// Because no nodes are created in German, no nodes are returned.
|
||||
$this->assertTrue(empty($nids), 'db_select() returns an empty result.');
|
||||
|
||||
// Query the nodes table as admin user (full access) with the node access
|
||||
// tag and no specific langcode.
|
||||
$select = db_select('node', 'n')
|
||||
->fields('n', ['nid'])
|
||||
->addMetaData('account', $admin_user)
|
||||
->addTag('node_access');
|
||||
$nids = $select->execute()->fetchAllAssoc('nid');
|
||||
|
||||
// All nodes are returned.
|
||||
$this->assertEqual(count($nids), 3, 'db_select() returns all three nodes.');
|
||||
|
||||
// Query the nodes table as admin user (full access) with the node access
|
||||
// tag and langcode de.
|
||||
$select = db_select('node', 'n')
|
||||
->fields('n', ['nid'])
|
||||
->addMetaData('account', $admin_user)
|
||||
->addMetaData('langcode', 'de')
|
||||
->addTag('node_access');
|
||||
$nids = $select->execute()->fetchAllAssoc('nid');
|
||||
|
||||
// All nodes are returned because node access tag is not invoked when the
|
||||
// user is user 1.
|
||||
$this->assertEqual(count($nids), 3, 'db_select() returns all three nodes.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\user\RoleInterface;
|
||||
|
||||
/**
|
||||
* Tests the interaction of the node access system with menu links.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeAccessMenuLinkTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['menu_ui', 'block'];
|
||||
|
||||
/**
|
||||
* A user with permission to manage menu links and create nodes.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $contentAdminUser;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalPlaceBlock('system_menu_block:main');
|
||||
|
||||
$this->contentAdminUser = $this->drupalCreateUser([
|
||||
'access content',
|
||||
'administer content types',
|
||||
'administer menu'
|
||||
]);
|
||||
|
||||
$this->config('user.role.' . RoleInterface::ANONYMOUS_ID)->set('permissions', [])->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* SA-CORE-2015-003: Tests menu links to nodes when node access is restricted.
|
||||
*/
|
||||
public function testNodeAccessMenuLink() {
|
||||
|
||||
$menu_link_title = $this->randomString();
|
||||
|
||||
$this->drupalLogin($this->contentAdminUser);
|
||||
$edit = [
|
||||
'title[0][value]' => $this->randomString(),
|
||||
'body[0][value]' => $this->randomString(),
|
||||
'menu[enabled]' => 1,
|
||||
'menu[title]' => $menu_link_title,
|
||||
];
|
||||
$this->drupalPostForm('node/add/page', $edit, t('Save'));
|
||||
$this->assertLink($menu_link_title);
|
||||
|
||||
// Ensure anonymous users without "access content" permission do not see
|
||||
// this menu link.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('');
|
||||
$this->assertNoLink($menu_link_title);
|
||||
|
||||
// Ensure anonymous users with "access content" permission see this menu
|
||||
// link.
|
||||
$this->config('user.role.' . RoleInterface::ANONYMOUS_ID)->set('permissions', ['access content'])->save();
|
||||
$this->drupalGet('');
|
||||
$this->assertLink($menu_link_title);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\comment\CommentInterface;
|
||||
use Drupal\comment\Tests\CommentTestTrait;
|
||||
use Drupal\comment\Entity\Comment;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests access controlled node views have the right amount of comment pages.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeAccessPagerTest extends BrowserTestBase {
|
||||
|
||||
use CommentTestTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node_access_test', 'comment', 'forum'];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
node_access_rebuild();
|
||||
$this->drupalCreateContentType(['type' => 'page', 'name' => t('Basic page')]);
|
||||
$this->addDefaultCommentField('node', 'page');
|
||||
$this->webUser = $this->drupalCreateUser(['access content', 'access comments', 'node test view']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the comment pager for nodes with multiple grants per realm.
|
||||
*/
|
||||
public function testCommentPager() {
|
||||
// Create a node.
|
||||
$node = $this->drupalCreateNode();
|
||||
|
||||
// Create 60 comments.
|
||||
for ($i = 0; $i < 60; $i++) {
|
||||
$comment = Comment::create([
|
||||
'entity_id' => $node->id(),
|
||||
'entity_type' => 'node',
|
||||
'field_name' => 'comment',
|
||||
'subject' => $this->randomMachineName(),
|
||||
'comment_body' => [
|
||||
['value' => $this->randomMachineName()],
|
||||
],
|
||||
'status' => CommentInterface::PUBLISHED,
|
||||
]);
|
||||
$comment->save();
|
||||
}
|
||||
|
||||
$this->drupalLogin($this->webUser);
|
||||
|
||||
// View the node page. With the default 50 comments per page there should
|
||||
// be two pages (0, 1) but no third (2) page.
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$this->assertText($node->label());
|
||||
$this->assertText(t('Comments'));
|
||||
$this->assertRaw('page=1');
|
||||
$this->assertNoRaw('page=2');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the forum node pager for nodes with multiple grants per realm.
|
||||
*/
|
||||
public function testForumPager() {
|
||||
// Look up the forums vocabulary ID.
|
||||
$vid = $this->config('forum.settings')->get('vocabulary');
|
||||
$this->assertTrue($vid, 'Forum navigation vocabulary ID is set.');
|
||||
|
||||
// Look up the general discussion term.
|
||||
$tree = \Drupal::entityManager()->getStorage('taxonomy_term')->loadTree($vid, 0, 1);
|
||||
$tid = reset($tree)->tid;
|
||||
$this->assertTrue($tid, 'General discussion term is found in the forum vocabulary.');
|
||||
|
||||
// Create 30 nodes.
|
||||
for ($i = 0; $i < 30; $i++) {
|
||||
$this->drupalCreateNode([
|
||||
'nid' => NULL,
|
||||
'type' => 'forum',
|
||||
'taxonomy_forums' => [
|
||||
['target_id' => $tid],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
// View the general discussion forum page. With the default 25 nodes per
|
||||
// page there should be two pages for 30 nodes, no more.
|
||||
$this->drupalLogin($this->webUser);
|
||||
$this->drupalGet('forum/' . $tid);
|
||||
$this->assertRaw('page=1');
|
||||
$this->assertNoRaw('page=2');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\node\Entity\NodeType;
|
||||
|
||||
/**
|
||||
* Ensures that node access rebuild functions work correctly even
|
||||
* when other modules implements hook_node_grants().
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeAccessRebuildNodeGrantsTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* A user to create nodes that only it has access to.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $webUser;
|
||||
|
||||
/**
|
||||
* A user to test the rebuild nodes feature which can't access the nodes.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->adminUser = $this->drupalCreateUser(['administer site configuration', 'access administration pages', 'access site reports']);
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
$this->webUser = $this->drupalCreateUser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests rebuilding the node access permissions table with content.
|
||||
*/
|
||||
public function testNodeAccessRebuildNodeGrants() {
|
||||
\Drupal::service('module_installer')->install(['node_access_test']);
|
||||
\Drupal::state()->set('node_access_test.private', TRUE);
|
||||
node_access_test_add_field(NodeType::load('page'));
|
||||
$this->resetAll();
|
||||
|
||||
// Create 30 nodes so that _node_access_rebuild_batch_operation() has to run
|
||||
// more than once.
|
||||
for ($i = 0; $i < 30; $i++) {
|
||||
$nodes[] = $this->drupalCreateNode([
|
||||
'uid' => $this->webUser->id(),
|
||||
'private' => [['value' => 1]]
|
||||
]);
|
||||
}
|
||||
|
||||
/** @var \Drupal\node\NodeGrantDatabaseStorageInterface $grant_storage */
|
||||
$grant_storage = \Drupal::service('node.grant_storage');
|
||||
// Default realm access and node records are present.
|
||||
foreach ($nodes as $node) {
|
||||
$this->assertTrue($node->private->value);
|
||||
$this->assertTrue($grant_storage->access($node, 'view', $this->webUser)->isAllowed(), 'Prior to rebuilding node access the grant storage returns allowed for the node author.');
|
||||
$this->assertTrue($grant_storage->access($node, 'view', $this->adminUser)->isAllowed(), 'Prior to rebuilding node access the grant storage returns allowed for the admin user.');
|
||||
}
|
||||
|
||||
$this->assertEqual(1, \Drupal::service('node.grant_storage')->checkAll($this->webUser), 'There is an all realm access record');
|
||||
$this->assertTrue(\Drupal::state()->get('node.node_access_needs_rebuild'), 'Node access permissions need to be rebuilt');
|
||||
|
||||
// Rebuild permissions.
|
||||
$this->drupalGet('admin/reports/status');
|
||||
$this->clickLink(t('Rebuild permissions'));
|
||||
$this->drupalPostForm(NULL, [], t('Rebuild permissions'));
|
||||
$this->assertText(t('The content access permissions have been rebuilt.'));
|
||||
|
||||
// Test if the rebuild by user that cannot bypass node access and does not
|
||||
// have access to the nodes has been successful.
|
||||
$this->assertFalse($this->adminUser->hasPermission('bypass node access'));
|
||||
$this->assertNull(\Drupal::state()->get('node.node_access_needs_rebuild'), 'Node access permissions have been rebuilt');
|
||||
foreach ($nodes as $node) {
|
||||
$this->assertTrue($grant_storage->access($node, 'view', $this->webUser)->isAllowed(), 'After rebuilding node access the grant storage returns allowed for the node author.');
|
||||
$this->assertFalse($grant_storage->access($node, 'view', $this->adminUser)->isForbidden(), 'After rebuilding node access the grant storage returns forbidden for the admin user.');
|
||||
}
|
||||
$this->assertFalse(\Drupal::service('node.grant_storage')->checkAll($this->webUser), 'There is no all realm access record');
|
||||
|
||||
// Test an anonymous node access rebuild from code.
|
||||
$this->drupalLogout();
|
||||
node_access_rebuild();
|
||||
foreach ($nodes as $node) {
|
||||
$this->assertTrue($grant_storage->access($node, 'view', $this->webUser)->isAllowed(), 'After rebuilding node access the grant storage returns allowed for the node author.');
|
||||
$this->assertFalse($grant_storage->access($node, 'view', $this->adminUser)->isForbidden(), 'After rebuilding node access the grant storage returns forbidden for the admin user.');
|
||||
}
|
||||
$this->assertFalse(\Drupal::service('node.grant_storage')->checkAll($this->webUser), 'There is no all realm access record');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests rebuilding the node access permissions table with no content.
|
||||
*/
|
||||
public function testNodeAccessRebuildNoAccessModules() {
|
||||
// Default realm access is present.
|
||||
$this->assertEqual(1, \Drupal::service('node.grant_storage')->count(), 'There is an all realm access record');
|
||||
|
||||
// No need to rebuild permissions.
|
||||
$this->assertFalse(\Drupal::state()->get('node.node_access_needs_rebuild'), 'Node access permissions need to be rebuilt');
|
||||
|
||||
// Rebuild permissions.
|
||||
$this->drupalGet('admin/reports/status');
|
||||
$this->clickLink(t('Rebuild permissions'));
|
||||
$this->drupalPostForm(NULL, [], t('Rebuild permissions'));
|
||||
$this->assertText(t('Content permissions have been rebuilt.'));
|
||||
$this->assertNull(\Drupal::state()->get('node.node_access_needs_rebuild'), 'Node access permissions have been rebuilt');
|
||||
|
||||
// Default realm access is still present.
|
||||
$this->assertEqual(1, \Drupal::service('node.grant_storage')->count(), 'There is an all realm access record');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\node\Entity\Node;
|
||||
|
||||
/**
|
||||
* Tests hook_node_access_records when acquiring grants.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeAccessRecordsTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Enable a module that implements node access API hooks and alter hook.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node_test'];
|
||||
|
||||
/**
|
||||
* Creates a node and tests the creation of node access rules.
|
||||
*/
|
||||
public function testNodeAccessRecords() {
|
||||
// Create an article node.
|
||||
$node1 = $this->drupalCreateNode(['type' => 'article']);
|
||||
$this->assertTrue(Node::load($node1->id()), 'Article node created.');
|
||||
|
||||
// Check to see if grants added by node_test_node_access_records made it in.
|
||||
$records = db_query('SELECT realm, gid FROM {node_access} WHERE nid = :nid', [':nid' => $node1->id()])->fetchAll();
|
||||
$this->assertEqual(count($records), 1, 'Returned the correct number of rows.');
|
||||
$this->assertEqual($records[0]->realm, 'test_article_realm', 'Grant with article_realm acquired for node without alteration.');
|
||||
$this->assertEqual($records[0]->gid, 1, 'Grant with gid = 1 acquired for node without alteration.');
|
||||
|
||||
// Create an unpromoted "Basic page" node.
|
||||
$node2 = $this->drupalCreateNode(['type' => 'page', 'promote' => 0]);
|
||||
$this->assertTrue(Node::load($node2->id()), 'Unpromoted basic page node created.');
|
||||
|
||||
// Check to see if grants added by node_test_node_access_records made it in.
|
||||
$records = db_query('SELECT realm, gid FROM {node_access} WHERE nid = :nid', [':nid' => $node2->id()])->fetchAll();
|
||||
$this->assertEqual(count($records), 1, 'Returned the correct number of rows.');
|
||||
$this->assertEqual($records[0]->realm, 'test_page_realm', 'Grant with page_realm acquired for node without alteration.');
|
||||
$this->assertEqual($records[0]->gid, 1, 'Grant with gid = 1 acquired for node without alteration.');
|
||||
|
||||
// Create an unpromoted, unpublished "Basic page" node.
|
||||
$node3 = $this->drupalCreateNode(['type' => 'page', 'promote' => 0, 'status' => 0]);
|
||||
$this->assertTrue(Node::load($node3->id()), 'Unpromoted, unpublished basic page node created.');
|
||||
|
||||
// Check to see if grants added by node_test_node_access_records made it in.
|
||||
$records = db_query('SELECT realm, gid FROM {node_access} WHERE nid = :nid', [':nid' => $node3->id()])->fetchAll();
|
||||
$this->assertEqual(count($records), 1, 'Returned the correct number of rows.');
|
||||
$this->assertEqual($records[0]->realm, 'test_page_realm', 'Grant with page_realm acquired for node without alteration.');
|
||||
$this->assertEqual($records[0]->gid, 1, 'Grant with gid = 1 acquired for node without alteration.');
|
||||
|
||||
// Create a promoted "Basic page" node.
|
||||
$node4 = $this->drupalCreateNode(['type' => 'page', 'promote' => 1]);
|
||||
$this->assertTrue(Node::load($node4->id()), 'Promoted basic page node created.');
|
||||
|
||||
// Check to see if grant added by node_test_node_access_records was altered
|
||||
// by node_test_node_access_records_alter.
|
||||
$records = db_query('SELECT realm, gid FROM {node_access} WHERE nid = :nid', [':nid' => $node4->id()])->fetchAll();
|
||||
$this->assertEqual(count($records), 1, 'Returned the correct number of rows.');
|
||||
$this->assertEqual($records[0]->realm, 'test_alter_realm', 'Altered grant with alter_realm acquired for node.');
|
||||
$this->assertEqual($records[0]->gid, 2, 'Altered grant with gid = 2 acquired for node.');
|
||||
|
||||
// Check to see if we can alter grants with hook_node_grants_alter().
|
||||
$operations = ['view', 'update', 'delete'];
|
||||
// Create a user that is allowed to access content.
|
||||
$web_user = $this->drupalCreateUser(['access content']);
|
||||
foreach ($operations as $op) {
|
||||
$grants = node_test_node_grants($web_user, $op);
|
||||
$altered_grants = $grants;
|
||||
\Drupal::moduleHandler()->alter('node_grants', $altered_grants, $web_user, $op);
|
||||
$this->assertNotEqual($grants, $altered_grants, format_string('Altered the %op grant for a user.', ['%op' => $op]));
|
||||
}
|
||||
|
||||
// Check that core does not grant access to an unpublished node when an
|
||||
// empty $grants array is returned.
|
||||
$node6 = $this->drupalCreateNode(['status' => 0, 'disable_node_access' => TRUE]);
|
||||
$records = db_query('SELECT realm, gid FROM {node_access} WHERE nid = :nid', [':nid' => $node6->id()])->fetchAll();
|
||||
$this->assertEqual(count($records), 0, 'Returned no records for unpublished node.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\Component\Utility\Crypt;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\system\Entity\Action;
|
||||
|
||||
/**
|
||||
* Tests configuration of actions provided by the Node module.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeActionsConfigurationTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['action', 'node'];
|
||||
|
||||
/**
|
||||
* Tests configuration of the node_assign_owner_action action.
|
||||
*/
|
||||
public function testAssignOwnerNodeActionConfiguration() {
|
||||
// Create a user with permission to view the actions administration pages.
|
||||
$user = $this->drupalCreateUser(['administer actions']);
|
||||
$this->drupalLogin($user);
|
||||
|
||||
// Make a POST request to admin/config/system/actions.
|
||||
$edit = [];
|
||||
$edit['action'] = Crypt::hashBase64('node_assign_owner_action');
|
||||
$this->drupalPostForm('admin/config/system/actions', $edit, t('Create'));
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Make a POST request to the individual action configuration page.
|
||||
$edit = [];
|
||||
$action_label = $this->randomMachineName();
|
||||
$edit['label'] = $action_label;
|
||||
$edit['id'] = strtolower($action_label);
|
||||
$edit['owner_uid'] = $user->id();
|
||||
$this->drupalPostForm('admin/config/system/actions/add/' . Crypt::hashBase64('node_assign_owner_action'), $edit, t('Save'));
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Make sure that the new action was saved properly.
|
||||
$this->assertText(t('The action has been successfully saved.'), 'The node_assign_owner_action action has been successfully saved.');
|
||||
$this->assertText($action_label, 'The label of the node_assign_owner_action action appears on the actions administration page after saving.');
|
||||
|
||||
// Make another POST request to the action edit page.
|
||||
$this->clickLink(t('Configure'));
|
||||
preg_match('|admin/config/system/actions/configure/(.+)|', $this->getUrl(), $matches);
|
||||
$aid = $matches[1];
|
||||
$edit = [];
|
||||
$new_action_label = $this->randomMachineName();
|
||||
$edit['label'] = $new_action_label;
|
||||
$edit['owner_uid'] = $user->id();
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Make sure that the action updated properly.
|
||||
$this->assertText(t('The action has been successfully saved.'), 'The node_assign_owner_action action has been successfully updated.');
|
||||
$this->assertNoText($action_label, 'The old label for the node_assign_owner_action action does not appear on the actions administration page after updating.');
|
||||
$this->assertText($new_action_label, 'The new label for the node_assign_owner_action action appears on the actions administration page after updating.');
|
||||
|
||||
// Make sure that deletions work properly.
|
||||
$this->drupalGet('admin/config/system/actions');
|
||||
$this->clickLink(t('Delete'));
|
||||
$this->assertResponse(200);
|
||||
$edit = [];
|
||||
$this->drupalPostForm("admin/config/system/actions/configure/$aid/delete", $edit, t('Delete'));
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Make sure that the action was actually deleted.
|
||||
$this->assertRaw(t('The action %action has been deleted.', ['%action' => $new_action_label]), 'The delete confirmation message appears after deleting the node_assign_owner_action action.');
|
||||
$this->drupalGet('admin/config/system/actions');
|
||||
$this->assertResponse(200);
|
||||
$this->assertNoText($new_action_label, 'The label for the node_assign_owner_action action does not appear on the actions administration page after deleting.');
|
||||
|
||||
$action = Action::load($aid);
|
||||
$this->assertFalse($action, 'The node_assign_owner_action action is not available after being deleted.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\user\RoleInterface;
|
||||
|
||||
/**
|
||||
* Tests node administration page functionality.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeAdminTest extends NodeTestBase {
|
||||
/**
|
||||
* A user with permission to bypass access content.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* A user with the 'access content overview' permission.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $baseUser1;
|
||||
|
||||
/**
|
||||
* A normal user with permission to view own unpublished content.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $baseUser2;
|
||||
|
||||
/**
|
||||
* A normal user with permission to bypass node access content.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $baseUser3;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['views'];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Remove the "view own unpublished content" permission which is set
|
||||
// by default for authenticated users so we can test this permission
|
||||
// correctly.
|
||||
user_role_revoke_permissions(RoleInterface::AUTHENTICATED_ID, ['view own unpublished content']);
|
||||
|
||||
$this->adminUser = $this->drupalCreateUser(['access administration pages', 'access content overview', 'administer nodes', 'bypass node access']);
|
||||
$this->baseUser1 = $this->drupalCreateUser(['access content overview']);
|
||||
$this->baseUser2 = $this->drupalCreateUser(['access content overview', 'view own unpublished content']);
|
||||
$this->baseUser3 = $this->drupalCreateUser(['access content overview', 'bypass node access']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the table sorting works on the content admin pages.
|
||||
*/
|
||||
public function testContentAdminSort() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
$changed = REQUEST_TIME;
|
||||
foreach (['dd', 'aa', 'DD', 'bb', 'cc', 'CC', 'AA', 'BB'] as $prefix) {
|
||||
$changed += 1000;
|
||||
$node = $this->drupalCreateNode(['title' => $prefix . $this->randomMachineName(6)]);
|
||||
db_update('node_field_data')
|
||||
->fields(['changed' => $changed])
|
||||
->condition('nid', $node->id())
|
||||
->execute();
|
||||
}
|
||||
|
||||
// Test that the default sort by node.changed DESC actually fires properly.
|
||||
$nodes_query = db_select('node_field_data', 'n')
|
||||
->fields('n', ['title'])
|
||||
->orderBy('changed', 'DESC')
|
||||
->execute()
|
||||
->fetchCol();
|
||||
|
||||
$this->drupalGet('admin/content');
|
||||
foreach ($nodes_query as $delta => $string) {
|
||||
$elements = $this->xpath('//table[contains(@class, :class)]/tbody/tr[' . ($delta + 1) . ']/td[2]/a[normalize-space(text())=:label]', [':class' => 'views-table', ':label' => $string]);
|
||||
$this->assertTrue(!empty($elements), 'The node was found in the correct order.');
|
||||
}
|
||||
|
||||
// Compare the rendered HTML node list to a query for the nodes ordered by
|
||||
// title to account for possible database-dependent sort order.
|
||||
$nodes_query = db_select('node_field_data', 'n')
|
||||
->fields('n', ['title'])
|
||||
->orderBy('title')
|
||||
->execute()
|
||||
->fetchCol();
|
||||
|
||||
$this->drupalGet('admin/content', ['query' => ['sort' => 'asc', 'order' => 'title']]);
|
||||
foreach ($nodes_query as $delta => $string) {
|
||||
$elements = $this->xpath('//table[contains(@class, :class)]/tbody/tr[' . ($delta + 1) . ']/td[2]/a[normalize-space(text())=:label]', [':class' => 'views-table', ':label' => $string]);
|
||||
$this->assertTrue(!empty($elements), 'The node was found in the correct order.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests content overview with different user permissions.
|
||||
*
|
||||
* Taxonomy filters are tested separately.
|
||||
*
|
||||
* @see TaxonomyNodeFilterTestCase
|
||||
*/
|
||||
public function testContentAdminPages() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Use an explicit changed time to ensure the expected order in the content
|
||||
// admin listing. We want these to appear in the table in the same order as
|
||||
// they appear in the following code, and the 'content' View has a table
|
||||
// style configuration with a default sort on the 'changed' field DESC.
|
||||
$time = time();
|
||||
$nodes['published_page'] = $this->drupalCreateNode(['type' => 'page', 'changed' => $time--]);
|
||||
$nodes['published_article'] = $this->drupalCreateNode(['type' => 'article', 'changed' => $time--]);
|
||||
$nodes['unpublished_page_1'] = $this->drupalCreateNode(['type' => 'page', 'changed' => $time--, 'uid' => $this->baseUser1->id(), 'status' => 0]);
|
||||
$nodes['unpublished_page_2'] = $this->drupalCreateNode(['type' => 'page', 'changed' => $time, 'uid' => $this->baseUser2->id(), 'status' => 0]);
|
||||
|
||||
// Verify view, edit, and delete links for any content.
|
||||
$this->drupalGet('admin/content');
|
||||
$this->assertResponse(200);
|
||||
|
||||
$node_type_labels = $this->xpath('//td[contains(@class, "views-field-type")]');
|
||||
$delta = 0;
|
||||
foreach ($nodes as $node) {
|
||||
$this->assertLinkByHref('node/' . $node->id());
|
||||
$this->assertLinkByHref('node/' . $node->id() . '/edit');
|
||||
$this->assertLinkByHref('node/' . $node->id() . '/delete');
|
||||
// Verify that we can see the content type label.
|
||||
$this->assertEqual(trim($node_type_labels[$delta]->getText()), $node->type->entity->label());
|
||||
$delta++;
|
||||
}
|
||||
|
||||
// Verify filtering by publishing status.
|
||||
$this->drupalGet('admin/content', ['query' => ['status' => TRUE]]);
|
||||
|
||||
$this->assertLinkByHref('node/' . $nodes['published_page']->id() . '/edit');
|
||||
$this->assertLinkByHref('node/' . $nodes['published_article']->id() . '/edit');
|
||||
$this->assertNoLinkByHref('node/' . $nodes['unpublished_page_1']->id() . '/edit');
|
||||
|
||||
// Verify filtering by status and content type.
|
||||
$this->drupalGet('admin/content', ['query' => ['status' => TRUE, 'type' => 'page']]);
|
||||
|
||||
$this->assertLinkByHref('node/' . $nodes['published_page']->id() . '/edit');
|
||||
$this->assertNoLinkByHref('node/' . $nodes['published_article']->id() . '/edit');
|
||||
|
||||
// Verify no operation links are displayed for regular users.
|
||||
$this->drupalLogout();
|
||||
$this->drupalLogin($this->baseUser1);
|
||||
$this->drupalGet('admin/content');
|
||||
$this->assertResponse(200);
|
||||
$this->assertLinkByHref('node/' . $nodes['published_page']->id());
|
||||
$this->assertLinkByHref('node/' . $nodes['published_article']->id());
|
||||
$this->assertNoLinkByHref('node/' . $nodes['published_page']->id() . '/edit');
|
||||
$this->assertNoLinkByHref('node/' . $nodes['published_page']->id() . '/delete');
|
||||
$this->assertNoLinkByHref('node/' . $nodes['published_article']->id() . '/edit');
|
||||
$this->assertNoLinkByHref('node/' . $nodes['published_article']->id() . '/delete');
|
||||
|
||||
// Verify no unpublished content is displayed without permission.
|
||||
$this->assertNoLinkByHref('node/' . $nodes['unpublished_page_1']->id());
|
||||
$this->assertNoLinkByHref('node/' . $nodes['unpublished_page_1']->id() . '/edit');
|
||||
$this->assertNoLinkByHref('node/' . $nodes['unpublished_page_1']->id() . '/delete');
|
||||
|
||||
// Verify no tableselect.
|
||||
$this->assertNoFieldByName('nodes[' . $nodes['published_page']->id() . ']', '', 'No tableselect found.');
|
||||
|
||||
// Verify unpublished content is displayed with permission.
|
||||
$this->drupalLogout();
|
||||
$this->drupalLogin($this->baseUser2);
|
||||
$this->drupalGet('admin/content');
|
||||
$this->assertResponse(200);
|
||||
$this->assertLinkByHref('node/' . $nodes['unpublished_page_2']->id());
|
||||
// Verify no operation links are displayed.
|
||||
$this->assertNoLinkByHref('node/' . $nodes['unpublished_page_2']->id() . '/edit');
|
||||
$this->assertNoLinkByHref('node/' . $nodes['unpublished_page_2']->id() . '/delete');
|
||||
|
||||
// Verify user cannot see unpublished content of other users.
|
||||
$this->assertNoLinkByHref('node/' . $nodes['unpublished_page_1']->id());
|
||||
$this->assertNoLinkByHref('node/' . $nodes['unpublished_page_1']->id() . '/edit');
|
||||
$this->assertNoLinkByHref('node/' . $nodes['unpublished_page_1']->id() . '/delete');
|
||||
|
||||
// Verify no tableselect.
|
||||
$this->assertNoFieldByName('nodes[' . $nodes['unpublished_page_2']->id() . ']', '', 'No tableselect found.');
|
||||
|
||||
// Verify node access can be bypassed.
|
||||
$this->drupalLogout();
|
||||
$this->drupalLogin($this->baseUser3);
|
||||
$this->drupalGet('admin/content');
|
||||
$this->assertResponse(200);
|
||||
foreach ($nodes as $node) {
|
||||
$this->assertLinkByHref('node/' . $node->id());
|
||||
$this->assertLinkByHref('node/' . $node->id() . '/edit');
|
||||
$this->assertLinkByHref('node/' . $node->id() . '/delete');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\block\Entity\Block;
|
||||
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
|
||||
use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
|
||||
use Drupal\user\RoleInterface;
|
||||
|
||||
/**
|
||||
* Tests node block functionality.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeBlockFunctionalTest extends NodeTestBase {
|
||||
|
||||
use AssertPageCacheContextsAndTagsTrait;
|
||||
|
||||
/**
|
||||
* An administrative user for testing.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* An unprivileged user for testing.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $webUser;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['block', 'views'];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create users and test node.
|
||||
$this->adminUser = $this->drupalCreateUser(['administer content types', 'administer nodes', 'administer blocks', 'access content overview']);
|
||||
$this->webUser = $this->drupalCreateUser(['access content', 'create article content']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the recent comments block.
|
||||
*/
|
||||
public function testRecentNodeBlock() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Disallow anonymous users to view content.
|
||||
user_role_change_permissions(RoleInterface::ANONYMOUS_ID, [
|
||||
'access content' => FALSE,
|
||||
]);
|
||||
|
||||
// Enable the recent content block with two items.
|
||||
$block = $this->drupalPlaceBlock('views_block:content_recent-block_1', ['id' => 'test_block', 'items_per_page' => 2]);
|
||||
|
||||
// Test that block is not visible without nodes.
|
||||
$this->drupalGet('');
|
||||
$this->assertText(t('No content available.'), 'Block with "No content available." found.');
|
||||
|
||||
// Add some test nodes.
|
||||
$default_settings = ['uid' => $this->webUser->id(), 'type' => 'article'];
|
||||
$node1 = $this->drupalCreateNode($default_settings);
|
||||
$node2 = $this->drupalCreateNode($default_settings);
|
||||
$node3 = $this->drupalCreateNode($default_settings);
|
||||
|
||||
// Change the changed time for node so that we can test ordering.
|
||||
db_update('node_field_data')
|
||||
->fields([
|
||||
'changed' => $node1->getChangedTime() + 100,
|
||||
])
|
||||
->condition('nid', $node2->id())
|
||||
->execute();
|
||||
db_update('node_field_data')
|
||||
->fields([
|
||||
'changed' => $node1->getChangedTime() + 200,
|
||||
])
|
||||
->condition('nid', $node3->id())
|
||||
->execute();
|
||||
|
||||
// Test that a user without the 'access content' permission cannot
|
||||
// see the block.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('');
|
||||
$this->assertNoText($block->label(), 'Block was not found.');
|
||||
|
||||
// Test that only the 2 latest nodes are shown.
|
||||
$this->drupalLogin($this->webUser);
|
||||
$this->assertNoText($node1->label(), 'Node not found in block.');
|
||||
$this->assertText($node2->label(), 'Node found in block.');
|
||||
$this->assertText($node3->label(), 'Node found in block.');
|
||||
|
||||
// Check to make sure nodes are in the right order.
|
||||
$this->assertTrue($this->xpath('//div[@id="block-test-block"]//div[@class="item-list"]/ul/li[1]/div/span/a[text() = "' . $node3->label() . '"]'), 'Nodes were ordered correctly in block.');
|
||||
|
||||
$this->drupalLogout();
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Set the number of recent nodes to show to 10.
|
||||
$block->getPlugin()->setConfigurationValue('items_per_page', 10);
|
||||
$block->save();
|
||||
|
||||
// Post an additional node.
|
||||
$node4 = $this->drupalCreateNode($default_settings);
|
||||
|
||||
// Test that all four nodes are shown.
|
||||
$this->drupalGet('');
|
||||
$this->assertText($node1->label(), 'Node found in block.');
|
||||
$this->assertText($node2->label(), 'Node found in block.');
|
||||
$this->assertText($node3->label(), 'Node found in block.');
|
||||
$this->assertText($node4->label(), 'Node found in block.');
|
||||
|
||||
$this->assertCacheContexts(['languages:language_content', 'languages:language_interface', 'theme', 'url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT, 'user']);
|
||||
|
||||
// Enable the "Powered by Drupal" block only on article nodes.
|
||||
$edit = [
|
||||
'id' => strtolower($this->randomMachineName()),
|
||||
'region' => 'sidebar_first',
|
||||
'visibility[node_type][bundles][article]' => 'article',
|
||||
];
|
||||
$theme = \Drupal::service('theme_handler')->getDefault();
|
||||
$this->drupalPostForm("admin/structure/block/add/system_powered_by_block/$theme", $edit, t('Save block'));
|
||||
|
||||
$block = Block::load($edit['id']);
|
||||
$visibility = $block->getVisibility();
|
||||
$this->assertTrue(isset($visibility['node_type']['bundles']['article']), 'Visibility settings were saved to configuration');
|
||||
|
||||
// Create a page node.
|
||||
$node5 = $this->drupalCreateNode(['uid' => $this->adminUser->id(), 'type' => 'page']);
|
||||
|
||||
$this->drupalLogout();
|
||||
$this->drupalLogin($this->webUser);
|
||||
|
||||
// Verify visibility rules.
|
||||
$this->drupalGet('');
|
||||
$label = $block->label();
|
||||
$this->assertNoText($label, 'Block was not displayed on the front page.');
|
||||
$this->assertCacheContexts(['languages:language_content', 'languages:language_interface', 'theme', 'url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT, 'user', 'route']);
|
||||
$this->drupalGet('node/add/article');
|
||||
$this->assertText($label, 'Block was displayed on the node/add/article page.');
|
||||
$this->assertCacheContexts(['languages:language_content', 'languages:language_interface', 'session', 'theme', 'url.path', 'url.query_args', 'user', 'route']);
|
||||
$this->drupalGet('node/' . $node1->id());
|
||||
$this->assertText($label, 'Block was displayed on the node/N when node is of type article.');
|
||||
$this->assertCacheContexts(['languages:language_content', 'languages:language_interface', 'theme', 'url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT, 'user', 'route', 'timezone']);
|
||||
$this->drupalGet('node/' . $node5->id());
|
||||
$this->assertNoText($label, 'Block was not displayed on nodes of type page.');
|
||||
$this->assertCacheContexts(['languages:language_content', 'languages:language_interface', 'theme', 'url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT, 'user', 'route', 'timezone']);
|
||||
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalGet('admin/structure/block');
|
||||
$this->assertText($label, 'Block was displayed on the admin/structure/block page.');
|
||||
$this->assertLinkByHref($block->url());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\system\Tests\Entity\EntityWithUriCacheTagsTestBase;
|
||||
|
||||
/**
|
||||
* Tests the Node entity's cache tags.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeCacheTagsTest extends EntityWithUriCacheTagsTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['node'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
// Create a "Camelids" node type.
|
||||
NodeType::create([
|
||||
'name' => 'Camelids',
|
||||
'type' => 'camelids',
|
||||
])->save();
|
||||
|
||||
// Create a "Llama" node.
|
||||
$node = Node::create(['type' => 'camelids']);
|
||||
$node->setTitle('Llama')
|
||||
->setPublished(TRUE)
|
||||
->save();
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDefaultCacheContexts() {
|
||||
$defaults = parent::getDefaultCacheContexts();
|
||||
// @see \Drupal\node\Controller\NodeViewController::view()
|
||||
$defaults[] = 'user.roles:anonymous';
|
||||
return $defaults;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getAdditionalCacheContextsForEntity(EntityInterface $entity) {
|
||||
return ['timezone'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Each node must have an author.
|
||||
*/
|
||||
protected function getAdditionalCacheTagsForEntity(EntityInterface $node) {
|
||||
return ['user:' . $node->getOwnerId(), 'user_view'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getAdditionalCacheContextsForEntityListing() {
|
||||
return ['user.node_grants:view'];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\node\Entity\Node;
|
||||
|
||||
/**
|
||||
* Create a node and test saving it.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeCreationTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* Enable dummy module that implements hook_ENTITY_TYPE_insert() for
|
||||
* exceptions (function node_test_exception_node_insert() ).
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node_test_exception', 'dblog', 'test_page_test'];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$web_user = $this->drupalCreateUser(['create page content', 'edit own page content']);
|
||||
$this->drupalLogin($web_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a "Basic page" node and verifies its consistency in the database.
|
||||
*/
|
||||
public function testNodeCreation() {
|
||||
$node_type_storage = \Drupal::entityManager()->getStorage('node_type');
|
||||
|
||||
// Test /node/add page with only one content type.
|
||||
$node_type_storage->load('article')->delete();
|
||||
$this->drupalGet('node/add');
|
||||
$this->assertResponse(200);
|
||||
$this->assertUrl('node/add/page');
|
||||
// Create a node.
|
||||
$edit = [];
|
||||
$edit['title[0][value]'] = $this->randomMachineName(8);
|
||||
$edit['body[0][value]'] = $this->randomMachineName(16);
|
||||
$this->drupalPostForm('node/add/page', $edit, t('Save'));
|
||||
|
||||
// Check that the Basic page has been created.
|
||||
$this->assertText(t('@post @title has been created.', ['@post' => 'Basic page', '@title' => $edit['title[0][value]']]), 'Basic page created.');
|
||||
|
||||
// Verify that the creation message contains a link to a node.
|
||||
$view_link = $this->xpath('//div[@class="messages"]//a[contains(@href, :href)]', [':href' => 'node/']);
|
||||
$this->assert(isset($view_link), 'The message area contains a link to a node');
|
||||
|
||||
// Check that the node exists in the database.
|
||||
$node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
|
||||
$this->assertTrue($node, 'Node found in database.');
|
||||
|
||||
// Verify that pages do not show submitted information by default.
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$this->assertNoText($node->getOwner()->getUsername());
|
||||
$this->assertNoText(format_date($node->getCreatedTime()));
|
||||
|
||||
// Change the node type setting to show submitted by information.
|
||||
/** @var \Drupal\node\NodeTypeInterface $node_type */
|
||||
$node_type = $node_type_storage->load('page');
|
||||
$node_type->setDisplaySubmitted(TRUE);
|
||||
$node_type->save();
|
||||
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$this->assertText($node->getOwner()->getUsername());
|
||||
$this->assertText(format_date($node->getCreatedTime()));
|
||||
|
||||
// Check if the node revision checkbox is not rendered on node creation form.
|
||||
$admin_user = $this->drupalCreateUser(['administer nodes', 'create page content']);
|
||||
$this->drupalLogin($admin_user);
|
||||
$this->drupalGet('node/add/page');
|
||||
$this->assertNoFieldById('edit-revision', NULL, 'The revision checkbox is not present.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that a transaction rolls back the failed creation.
|
||||
*/
|
||||
public function testFailedPageCreation() {
|
||||
// Create a node.
|
||||
$edit = [
|
||||
'uid' => $this->loggedInUser->id(),
|
||||
'name' => $this->loggedInUser->name,
|
||||
'type' => 'page',
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
'title' => 'testing_transaction_exception',
|
||||
];
|
||||
|
||||
try {
|
||||
// An exception is generated by node_test_exception_node_insert() if the
|
||||
// title is 'testing_transaction_exception'.
|
||||
Node::create($edit)->save();
|
||||
$this->fail(t('Expected exception has not been thrown.'));
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->pass(t('Expected exception has been thrown.'));
|
||||
}
|
||||
|
||||
if (Database::getConnection()->supportsTransactions()) {
|
||||
// Check that the node does not exist in the database.
|
||||
$node = $this->drupalGetNodeByTitle($edit['title']);
|
||||
$this->assertFalse($node, 'Transactions supported, and node not found in database.');
|
||||
}
|
||||
else {
|
||||
// Check that the node exists in the database.
|
||||
$node = $this->drupalGetNodeByTitle($edit['title']);
|
||||
$this->assertTrue($node, 'Transactions not supported, and node found in database.');
|
||||
|
||||
// Check that the failed rollback was logged.
|
||||
$records = static::getWatchdogIdsForFailedExplicitRollback();
|
||||
$this->assertTrue(count($records) > 0, 'Transactions not supported, and rollback error logged to watchdog.');
|
||||
}
|
||||
|
||||
// Check that the rollback error was logged.
|
||||
$records = static::getWatchdogIdsForTestExceptionRollback();
|
||||
$this->assertTrue(count($records) > 0, 'Rollback explanatory error logged to watchdog.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an unpublished node and confirms correct redirect behavior.
|
||||
*/
|
||||
public function testUnpublishedNodeCreation() {
|
||||
// Set the front page to the test page.
|
||||
$this->config('system.site')->set('page.front', '/test-page')->save();
|
||||
|
||||
// Set "Basic page" content type to be unpublished by default.
|
||||
$fields = \Drupal::entityManager()->getFieldDefinitions('node', 'page');
|
||||
$fields['status']->getConfig('page')
|
||||
->setDefaultValue(FALSE)
|
||||
->save();
|
||||
|
||||
// Create a node.
|
||||
$edit = [];
|
||||
$edit['title[0][value]'] = $this->randomMachineName(8);
|
||||
$edit['body[0][value]'] = $this->randomMachineName(16);
|
||||
$this->drupalPostForm('node/add/page', $edit, t('Save'));
|
||||
|
||||
// Check that the user was redirected to the home page.
|
||||
$this->assertUrl('');
|
||||
$this->assertText(t('Test page text'));
|
||||
|
||||
// Confirm that the node was created.
|
||||
$this->assertText(t('@post @title has been created.', ['@post' => 'Basic page', '@title' => $edit['title[0][value]']]));
|
||||
|
||||
// Verify that the creation message contains a link to a node.
|
||||
$view_link = $this->xpath('//div[@class="messages"]//a[contains(@href, :href)]', [':href' => 'node/']);
|
||||
$this->assert(isset($view_link), 'The message area contains a link to a node');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the author autocompletion textfield.
|
||||
*/
|
||||
public function testAuthorAutocomplete() {
|
||||
$admin_user = $this->drupalCreateUser(['administer nodes', 'create page content']);
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
$this->drupalGet('node/add/page');
|
||||
|
||||
$result = $this->xpath('//input[@id="edit-uid-0-value" and contains(@data-autocomplete-path, "user/autocomplete")]');
|
||||
$this->assertEqual(count($result), 0, 'No autocompletion without access user profiles.');
|
||||
|
||||
$admin_user = $this->drupalCreateUser(['administer nodes', 'create page content', 'access user profiles']);
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
$this->drupalGet('node/add/page');
|
||||
|
||||
$result = $this->xpath('//input[@id="edit-uid-0-target-id" and contains(@data-autocomplete-path, "/entity_reference_autocomplete/user/default")]');
|
||||
$this->assertEqual(count($result), 1, 'Ensure that the user does have access to the autocompletion');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check node/add when no node types exist.
|
||||
*/
|
||||
public function testNodeAddWithoutContentTypes() {
|
||||
$this->drupalGet('node/add');
|
||||
$this->assertResponse(200);
|
||||
$this->assertNoLinkByHref('/admin/structure/types/add');
|
||||
|
||||
// Test /node/add page without content types.
|
||||
foreach (\Drupal::entityManager()->getStorage('node_type')->loadMultiple() as $entity ) {
|
||||
$entity->delete();
|
||||
}
|
||||
|
||||
$this->drupalGet('node/add');
|
||||
$this->assertResponse(403);
|
||||
|
||||
$admin_content_types = $this->drupalCreateUser(['administer content types']);
|
||||
$this->drupalLogin($admin_content_types);
|
||||
|
||||
$this->drupalGet('node/add');
|
||||
|
||||
$this->assertLinkByHref('/admin/structure/types/add');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the watchdog IDs of the records with the rollback exception message.
|
||||
*
|
||||
* @return int[]
|
||||
* Array containing the IDs of the log records with the rollback exception
|
||||
* message.
|
||||
*/
|
||||
protected static function getWatchdogIdsForTestExceptionRollback() {
|
||||
// PostgreSQL doesn't support bytea LIKE queries, so we need to unserialize
|
||||
// first to check for the rollback exception message.
|
||||
$matches = [];
|
||||
$query = db_query("SELECT wid, variables FROM {watchdog}");
|
||||
foreach ($query as $row) {
|
||||
$variables = (array) unserialize($row->variables);
|
||||
if (isset($variables['@message']) && $variables['@message'] === 'Test exception for rollback.') {
|
||||
$matches[] = $row->wid;
|
||||
}
|
||||
}
|
||||
return $matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the log records with the explicit rollback failed exception message.
|
||||
*
|
||||
* @return \Drupal\Core\Database\StatementInterface
|
||||
* A prepared statement object (already executed), which contains the log
|
||||
* records with the explicit rollback failed exception message.
|
||||
*/
|
||||
protected static function getWatchdogIdsForFailedExplicitRollback() {
|
||||
return db_query("SELECT wid FROM {watchdog} WHERE message LIKE 'Explicit rollback failed%'")->fetchAll();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\node\NodeInterface;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Create a node and test node edit functionality.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeEditFormTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* A normal logged in user.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $webUser;
|
||||
|
||||
/**
|
||||
* A user with permission to bypass content access checks.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* The node storage.
|
||||
*
|
||||
* @var \Drupal\node\NodeStorageInterface
|
||||
*/
|
||||
protected $nodeStorage;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public static $modules = ['block', 'node', 'datetime'];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->webUser = $this->drupalCreateUser(['edit own page content', 'create page content']);
|
||||
$this->adminUser = $this->drupalCreateUser(['bypass node access', 'administer nodes']);
|
||||
$this->drupalPlaceBlock('local_tasks_block');
|
||||
|
||||
$this->nodeStorage = $this->container->get('entity.manager')->getStorage('node');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks node edit functionality.
|
||||
*/
|
||||
public function testNodeEdit() {
|
||||
$this->drupalLogin($this->webUser);
|
||||
|
||||
$title_key = 'title[0][value]';
|
||||
$body_key = 'body[0][value]';
|
||||
// Create node to edit.
|
||||
$edit = [];
|
||||
$edit[$title_key] = $this->randomMachineName(8);
|
||||
$edit[$body_key] = $this->randomMachineName(16);
|
||||
$this->drupalPostForm('node/add/page', $edit, t('Save'));
|
||||
|
||||
// Check that the node exists in the database.
|
||||
$node = $this->drupalGetNodeByTitle($edit[$title_key]);
|
||||
$this->assertTrue($node, 'Node found in database.');
|
||||
|
||||
// Check that "edit" link points to correct page.
|
||||
$this->clickLink(t('Edit'));
|
||||
$this->assertUrl($node->url('edit-form', ['absolute' => TRUE]));
|
||||
|
||||
// Check that the title and body fields are displayed with the correct values.
|
||||
// @todo Ideally assertLink would support HTML, but it doesn't.
|
||||
$this->assertRaw('Edit<span class="visually-hidden">(active tab)</span>', 'Edit tab found and marked active.');
|
||||
$this->assertFieldByName($title_key, $edit[$title_key], 'Title field displayed.');
|
||||
$this->assertFieldByName($body_key, $edit[$body_key], 'Body field displayed.');
|
||||
|
||||
// Edit the content of the node.
|
||||
$edit = [];
|
||||
$edit[$title_key] = $this->randomMachineName(8);
|
||||
$edit[$body_key] = $this->randomMachineName(16);
|
||||
// Stay on the current page, without reloading.
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
|
||||
// Check that the title and body fields are displayed with the updated values.
|
||||
$this->assertText($edit[$title_key], 'Title displayed.');
|
||||
$this->assertText($edit[$body_key], 'Body displayed.');
|
||||
|
||||
// Log in as a second administrator user.
|
||||
$second_web_user = $this->drupalCreateUser(['administer nodes', 'edit any page content']);
|
||||
$this->drupalLogin($second_web_user);
|
||||
// Edit the same node, creating a new revision.
|
||||
$this->drupalGet("node/" . $node->id() . "/edit");
|
||||
$edit = [];
|
||||
$edit['title[0][value]'] = $this->randomMachineName(8);
|
||||
$edit[$body_key] = $this->randomMachineName(16);
|
||||
$edit['revision'] = TRUE;
|
||||
$this->drupalPostForm(NULL, $edit, t('Save and keep published'));
|
||||
|
||||
// Ensure that the node revision has been created.
|
||||
$revised_node = $this->drupalGetNodeByTitle($edit['title[0][value]'], TRUE);
|
||||
$this->assertNotIdentical($node->getRevisionId(), $revised_node->getRevisionId(), 'A new revision has been created.');
|
||||
// Ensure that the node author is preserved when it was not changed in the
|
||||
// edit form.
|
||||
$this->assertIdentical($node->getOwnerId(), $revised_node->getOwnerId(), 'The node author has been preserved.');
|
||||
// Ensure that the revision authors are different since the revisions were
|
||||
// made by different users.
|
||||
$first_node_version = node_revision_load($node->getRevisionId());
|
||||
$second_node_version = node_revision_load($revised_node->getRevisionId());
|
||||
$this->assertNotIdentical($first_node_version->getRevisionUser()->id(), $second_node_version->getRevisionUser()->id(), 'Each revision has a distinct user.');
|
||||
|
||||
// Check if the node revision checkbox is rendered on node edit form.
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$this->assertFieldById('edit-revision', NULL, 'The revision field is present.');
|
||||
|
||||
// Check that details form element opens when there are errors on child
|
||||
// elements.
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$edit = [];
|
||||
// This invalid date will trigger an error.
|
||||
$edit['created[0][value][date]'] = $this->randomMachineName(8);
|
||||
// Get the current amount of open details elements.
|
||||
$open_details_elements = count($this->cssSelect('details[open="open"]'));
|
||||
$this->drupalPostForm(NULL, $edit, t('Save and keep published'));
|
||||
// The node author details must be open.
|
||||
$this->assertRaw('<details class="node-form-author js-form-wrapper form-wrapper" data-drupal-selector="edit-author" id="edit-author" open="open">');
|
||||
// Only one extra details element should now be open.
|
||||
$open_details_elements++;
|
||||
$this->assertEqual(count($this->cssSelect('details[open="open"]')), $open_details_elements, 'Exactly one extra open <details> element found.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests changing a node's "authored by" field.
|
||||
*/
|
||||
public function testNodeEditAuthoredBy() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Create node to edit.
|
||||
$body_key = 'body[0][value]';
|
||||
$edit = [];
|
||||
$edit['title[0][value]'] = $this->randomMachineName(8);
|
||||
$edit[$body_key] = $this->randomMachineName(16);
|
||||
$this->drupalPostForm('node/add/page', $edit, t('Save and publish'));
|
||||
|
||||
// Check that the node was authored by the currently logged in user.
|
||||
$node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
|
||||
$this->assertIdentical($node->getOwnerId(), $this->adminUser->id(), 'Node authored by admin user.');
|
||||
|
||||
$this->checkVariousAuthoredByValues($node, 'uid[0][target_id]');
|
||||
|
||||
// Check that normal users cannot change the authored by information.
|
||||
$this->drupalLogin($this->webUser);
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$this->assertNoFieldByName('uid[0][target_id]');
|
||||
|
||||
// Now test with the Autcomplete (Tags) field widget.
|
||||
/** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display */
|
||||
$form_display = \Drupal::entityManager()->getStorage('entity_form_display')->load('node.page.default');
|
||||
$widget = $form_display->getComponent('uid');
|
||||
$widget['type'] = 'entity_reference_autocomplete_tags';
|
||||
$widget['settings'] = [
|
||||
'match_operator' => 'CONTAINS',
|
||||
'size' => 60,
|
||||
'placeholder' => '',
|
||||
];
|
||||
$form_display->setComponent('uid', $widget);
|
||||
$form_display->save();
|
||||
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Save the node without making any changes.
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', [], t('Save and keep published'));
|
||||
$this->nodeStorage->resetCache([$node->id()]);
|
||||
$node = $this->nodeStorage->load($node->id());
|
||||
$this->assertIdentical($this->webUser->id(), $node->getOwner()->id());
|
||||
|
||||
$this->checkVariousAuthoredByValues($node, 'uid[target_id]');
|
||||
|
||||
// Hide the 'authored by' field from the form.
|
||||
$form_display->removeComponent('uid')->save();
|
||||
|
||||
// Check that saving the node without making any changes keeps the proper
|
||||
// author ID.
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', [], t('Save and keep published'));
|
||||
$this->nodeStorage->resetCache([$node->id()]);
|
||||
$node = $this->nodeStorage->load($node->id());
|
||||
$this->assertIdentical($this->webUser->id(), $node->getOwner()->id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the "authored by" works correctly with various values.
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* A node object.
|
||||
* @param string $form_element_name
|
||||
* The name of the form element to populate.
|
||||
*/
|
||||
protected function checkVariousAuthoredByValues(NodeInterface $node, $form_element_name) {
|
||||
// Try to change the 'authored by' field to an invalid user name.
|
||||
$edit = [
|
||||
$form_element_name => 'invalid-name',
|
||||
];
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save and keep published'));
|
||||
$this->assertRaw(t('There are no entities matching "%name".', ['%name' => 'invalid-name']));
|
||||
|
||||
// Change the authored by field to an empty string, which should assign
|
||||
// authorship to the anonymous user (uid 0).
|
||||
$edit[$form_element_name] = '';
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save and keep published'));
|
||||
$this->nodeStorage->resetCache([$node->id()]);
|
||||
$node = $this->nodeStorage->load($node->id());
|
||||
$uid = $node->getOwnerId();
|
||||
// Most SQL database drivers stringify fetches but entities are not
|
||||
// necessarily stored in a SQL database. At the same time, NULL/FALSE/""
|
||||
// won't do.
|
||||
$this->assertTrue($uid === 0 || $uid === '0', 'Node authored by anonymous user.');
|
||||
|
||||
// Go back to the edit form and check that the correct value is displayed
|
||||
// in the author widget.
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$anonymous_user = User::getAnonymousUser();
|
||||
$expected = $anonymous_user->label() . ' (' . $anonymous_user->id() . ')';
|
||||
$this->assertFieldByName($form_element_name, $expected, 'Authored by field displays the correct value for the anonymous user.');
|
||||
|
||||
// Change the authored by field to another user's name (that is not
|
||||
// logged in).
|
||||
$edit[$form_element_name] = $this->webUser->getUsername();
|
||||
$this->drupalPostForm(NULL, $edit, t('Save and keep published'));
|
||||
$this->nodeStorage->resetCache([$node->id()]);
|
||||
$node = $this->nodeStorage->load($node->id());
|
||||
$this->assertIdentical($node->getOwnerId(), $this->webUser->id(), 'Node authored by normal user.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Tests\EntityViewTrait;
|
||||
|
||||
/**
|
||||
* Tests changing view modes for nodes.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeEntityViewModeAlterTest extends NodeTestBase {
|
||||
|
||||
use EntityViewTrait;
|
||||
|
||||
/**
|
||||
* Enable dummy module that implements hook_ENTITY_TYPE_view() for nodes.
|
||||
*/
|
||||
public static $modules = ['node_test'];
|
||||
|
||||
/**
|
||||
* Create a "Basic page" node and verify its consistency in the database.
|
||||
*/
|
||||
public function testNodeViewModeChange() {
|
||||
$web_user = $this->drupalCreateUser(['create page content', 'edit own page content']);
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
// Create a node.
|
||||
$edit = [];
|
||||
$edit['title[0][value]'] = $this->randomMachineName(8);
|
||||
$edit['body[0][value]'] = t('Data that should appear only in the body for the node.');
|
||||
$edit['body[0][summary]'] = t('Extra data that should appear only in the teaser for the node.');
|
||||
$this->drupalPostForm('node/add/page', $edit, t('Save'));
|
||||
|
||||
$node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
|
||||
|
||||
// Set the flag to alter the view mode and view the node.
|
||||
\Drupal::state()->set('node_test_change_view_mode', 'teaser');
|
||||
Cache::invalidateTags(['rendered']);
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
|
||||
// Check that teaser mode is viewed.
|
||||
$this->assertText('Extra data that should appear only in the teaser for the node.', 'Teaser text present');
|
||||
// Make sure body text is not present.
|
||||
$this->assertNoText('Data that should appear only in the body for the node.', 'Body text not present');
|
||||
|
||||
// Test that the correct build mode has been set.
|
||||
$build = $this->buildEntityView($node);
|
||||
$this->assertEqual($build['#view_mode'], 'teaser', 'The view mode has correctly been set to teaser.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests multilingual support for fields.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeFieldMultilingualTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node', 'language'];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create Basic page node type.
|
||||
$this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']);
|
||||
|
||||
// Setup users.
|
||||
$admin_user = $this->drupalCreateUser(['administer languages', 'administer content types', 'access administration pages', 'create page content', 'edit own page content']);
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
// Add a new language.
|
||||
ConfigurableLanguage::createFromLangcode('it')->save();
|
||||
|
||||
// Enable URL language detection and selection.
|
||||
$edit = ['language_interface[enabled][language-url]' => '1'];
|
||||
$this->drupalPostForm('admin/config/regional/language/detection', $edit, t('Save settings'));
|
||||
|
||||
// Set "Basic page" content type to use multilingual support.
|
||||
$edit = [
|
||||
'language_configuration[language_alterable]' => TRUE,
|
||||
];
|
||||
$this->drupalPostForm('admin/structure/types/manage/page', $edit, t('Save content type'));
|
||||
$this->assertRaw(t('The content type %type has been updated.', ['%type' => 'Basic page']), 'Basic page content type has been updated.');
|
||||
|
||||
// Make node body translatable.
|
||||
$field_storage = FieldStorageConfig::loadByName('node', 'body');
|
||||
$field_storage->setTranslatable(TRUE);
|
||||
$field_storage->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether field languages are correctly set through the node form.
|
||||
*/
|
||||
public function testMultilingualNodeForm() {
|
||||
// Create "Basic page" content.
|
||||
$langcode = language_get_default_langcode('node', 'page');
|
||||
$title_key = 'title[0][value]';
|
||||
$title_value = $this->randomMachineName(8);
|
||||
$body_key = 'body[0][value]';
|
||||
$body_value = $this->randomMachineName(16);
|
||||
|
||||
// Create node to edit.
|
||||
$edit = [];
|
||||
$edit[$title_key] = $title_value;
|
||||
$edit[$body_key] = $body_value;
|
||||
$this->drupalPostForm('node/add/page', $edit, t('Save'));
|
||||
|
||||
// Check that the node exists in the database.
|
||||
$node = $this->drupalGetNodeByTitle($edit[$title_key]);
|
||||
$this->assertTrue($node, 'Node found in database.');
|
||||
$this->assertTrue($node->language()->getId() == $langcode && $node->body->value == $body_value, 'Field language correctly set.');
|
||||
|
||||
// Change node language.
|
||||
$langcode = 'it';
|
||||
$this->drupalGet("node/{$node->id()}/edit");
|
||||
$edit = [
|
||||
$title_key => $this->randomMachineName(8),
|
||||
'langcode[0][value]' => $langcode,
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$node = $this->drupalGetNodeByTitle($edit[$title_key], TRUE);
|
||||
$this->assertTrue($node, 'Node found in database.');
|
||||
$this->assertTrue($node->language()->getId() == $langcode && $node->body->value == $body_value, 'Field language correctly changed.');
|
||||
|
||||
// Enable content language URL detection.
|
||||
$this->container->get('language_negotiator')->saveConfiguration(LanguageInterface::TYPE_CONTENT, [LanguageNegotiationUrl::METHOD_ID => 0]);
|
||||
|
||||
// Test multilingual field language fallback logic.
|
||||
$this->drupalGet("it/node/{$node->id()}");
|
||||
$this->assertRaw($body_value, 'Body correctly displayed using Italian as requested language');
|
||||
|
||||
$this->drupalGet("node/{$node->id()}");
|
||||
$this->assertRaw($body_value, 'Body correctly displayed using English as requested language');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests multilingual field display settings.
|
||||
*/
|
||||
public function testMultilingualDisplaySettings() {
|
||||
// Create "Basic page" content.
|
||||
$title_key = 'title[0][value]';
|
||||
$title_value = $this->randomMachineName(8);
|
||||
$body_key = 'body[0][value]';
|
||||
$body_value = $this->randomMachineName(16);
|
||||
|
||||
// Create node to edit.
|
||||
$edit = [];
|
||||
$edit[$title_key] = $title_value;
|
||||
$edit[$body_key] = $body_value;
|
||||
$this->drupalPostForm('node/add/page', $edit, t('Save'));
|
||||
|
||||
// Check that the node exists in the database.
|
||||
$node = $this->drupalGetNodeByTitle($edit[$title_key]);
|
||||
$this->assertTrue($node, 'Node found in database.');
|
||||
|
||||
// Check if node body is showed.
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$body = $this->xpath('//article[contains(concat(" ", normalize-space(@class), " "), :node-class)]//div[contains(concat(" ", normalize-space(@class), " "), :content-class)]/descendant::p', [
|
||||
':node-class' => ' node ',
|
||||
':content-class' => 'node__content',
|
||||
]);
|
||||
$this->assertEqual($body[0]->getText(), $node->body->value, 'Node body found.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
/**
|
||||
* Tests all the different buttons on the node form.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeFormButtonsTest extends NodeTestBase {
|
||||
|
||||
use AssertButtonsTrait;
|
||||
|
||||
/**
|
||||
* A normal logged in user.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $webUser;
|
||||
|
||||
/**
|
||||
* A user with permission to bypass access content.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create a user that has no access to change the state of the node.
|
||||
$this->webUser = $this->drupalCreateUser(['create article content', 'edit own article content']);
|
||||
// Create a user that has access to change the state of the node.
|
||||
$this->adminUser = $this->drupalCreateUser(['administer nodes', 'bypass node access']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the right buttons are displayed for saving nodes.
|
||||
*/
|
||||
public function testNodeFormButtons() {
|
||||
$node_storage = $this->container->get('entity.manager')->getStorage('node');
|
||||
// Log in as administrative user.
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Verify the buttons on a node add form.
|
||||
$this->drupalGet('node/add/article');
|
||||
$this->assertButtons([t('Save and publish'), t('Save as unpublished')]);
|
||||
|
||||
// Save the node and assert it's published after clicking
|
||||
// 'Save and publish'.
|
||||
$edit = ['title[0][value]' => $this->randomString()];
|
||||
$this->drupalPostForm('node/add/article', $edit, t('Save and publish'));
|
||||
|
||||
// Get the node.
|
||||
$node_1 = $node_storage->load(1);
|
||||
$this->assertTrue($node_1->isPublished(), 'Node is published');
|
||||
|
||||
// Verify the buttons on a node edit form.
|
||||
$this->drupalGet('node/' . $node_1->id() . '/edit');
|
||||
$this->assertButtons([t('Save and keep published'), t('Save and unpublish')]);
|
||||
|
||||
// Save the node and verify it's still published after clicking
|
||||
// 'Save and keep published'.
|
||||
$this->drupalPostForm(NULL, $edit, t('Save and keep published'));
|
||||
$node_storage->resetCache([1]);
|
||||
$node_1 = $node_storage->load(1);
|
||||
$this->assertTrue($node_1->isPublished(), 'Node is published');
|
||||
|
||||
// Save the node and verify it's unpublished after clicking
|
||||
// 'Save and unpublish'.
|
||||
$this->drupalPostForm('node/' . $node_1->id() . '/edit', $edit, t('Save and unpublish'));
|
||||
$node_storage->resetCache([1]);
|
||||
$node_1 = $node_storage->load(1);
|
||||
$this->assertFalse($node_1->isPublished(), 'Node is unpublished');
|
||||
|
||||
// Verify the buttons on an unpublished node edit screen.
|
||||
$this->drupalGet('node/' . $node_1->id() . '/edit');
|
||||
$this->assertButtons([t('Save and keep unpublished'), t('Save and publish')]);
|
||||
|
||||
// Create a node as a normal user.
|
||||
$this->drupalLogout();
|
||||
$this->drupalLogin($this->webUser);
|
||||
|
||||
// Verify the buttons for a normal user.
|
||||
$this->drupalGet('node/add/article');
|
||||
$this->assertButtons([t('Save')], FALSE);
|
||||
|
||||
// Create the node.
|
||||
$edit = ['title[0][value]' => $this->randomString()];
|
||||
$this->drupalPostForm('node/add/article', $edit, t('Save'));
|
||||
$node_2 = $node_storage->load(2);
|
||||
$this->assertTrue($node_2->isPublished(), 'Node is published');
|
||||
|
||||
// Log in as an administrator and unpublish the node that just
|
||||
// was created by the normal user.
|
||||
$this->drupalLogout();
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalPostForm('node/' . $node_2->id() . '/edit', [], t('Save and unpublish'));
|
||||
$node_storage->resetCache([2]);
|
||||
$node_2 = $node_storage->load(2);
|
||||
$this->assertFalse($node_2->isPublished(), 'Node is unpublished');
|
||||
|
||||
// Log in again as the normal user, save the node and verify
|
||||
// it's still unpublished.
|
||||
$this->drupalLogout();
|
||||
$this->drupalLogin($this->webUser);
|
||||
$this->drupalPostForm('node/' . $node_2->id() . '/edit', [], t('Save'));
|
||||
$node_storage->resetCache([2]);
|
||||
$node_2 = $node_storage->load(2);
|
||||
$this->assertFalse($node_2->isPublished(), 'Node is still unpublished');
|
||||
$this->drupalLogout();
|
||||
|
||||
// Set article content type default to unpublished. This will change the
|
||||
// the initial order of buttons and/or status of the node when creating
|
||||
// a node.
|
||||
$fields = \Drupal::entityManager()->getFieldDefinitions('node', 'article');
|
||||
$fields['status']->getConfig('article')
|
||||
->setDefaultValue(FALSE)
|
||||
->save();
|
||||
|
||||
// Verify the buttons on a node add form for an administrator.
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalGet('node/add/article');
|
||||
$this->assertButtons([t('Save as unpublished'), t('Save and publish')]);
|
||||
|
||||
// Verify the node is unpublished by default for a normal user.
|
||||
$this->drupalLogout();
|
||||
$this->drupalLogin($this->webUser);
|
||||
$edit = ['title[0][value]' => $this->randomString()];
|
||||
$this->drupalPostForm('node/add/article', $edit, t('Save'));
|
||||
$node_3 = $node_storage->load(3);
|
||||
$this->assertFalse($node_3->isPublished(), 'Node is unpublished');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests updating the changed time after API and FORM entity save.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeFormSaveChangedTimeTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = [
|
||||
'node',
|
||||
];
|
||||
|
||||
/**
|
||||
* An user with permissions to create and edit articles.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $authorUser;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create a node type.
|
||||
$this->drupalCreateContentType([
|
||||
'type' => 'article',
|
||||
'name' => 'Article',
|
||||
]);
|
||||
|
||||
$this->authorUser = $this->drupalCreateUser(['access content', 'create article content', 'edit any article content'], 'author');
|
||||
$this->drupalLogin($this->authorUser);
|
||||
|
||||
// Create one node of the above node type .
|
||||
$this->drupalCreateNode([
|
||||
'type' => 'article',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the changed time after API and FORM save without changes.
|
||||
*/
|
||||
public function testChangedTimeAfterSaveWithoutChanges() {
|
||||
$storage = $this->container->get('entity_type.manager')->getStorage('node');
|
||||
$storage->resetCache([1]);
|
||||
$node = $storage->load(1);
|
||||
$changed_timestamp = $node->getChangedTime();
|
||||
$node->save();
|
||||
$storage->resetCache([1]);
|
||||
$node = $storage->load(1);
|
||||
$this->assertEqual($changed_timestamp, $node->getChangedTime(), "The entity's changed time wasn't updated after API save without changes.");
|
||||
|
||||
// Ensure different save timestamps.
|
||||
sleep(1);
|
||||
|
||||
// Save the node on the regular node edit form.
|
||||
$this->drupalPostForm('node/1/edit', [], t('Save'));
|
||||
|
||||
$storage->resetCache([1]);
|
||||
$node = $storage->load(1);
|
||||
$this->assertNotEqual($changed_timestamp, $node->getChangedTime(), "The entity's changed time was updated after form save without changes.");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests help functionality for nodes.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeHelpTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array.
|
||||
*/
|
||||
public static $modules = ['block', 'node', 'help'];
|
||||
|
||||
/**
|
||||
* The name of the test node type to create.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $testType;
|
||||
|
||||
/**
|
||||
* The test 'node help' text to be checked.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $testText;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create user.
|
||||
$admin_user = $this->drupalCreateUser([
|
||||
'administer content types',
|
||||
'administer nodes',
|
||||
'bypass node access',
|
||||
]);
|
||||
|
||||
$this->drupalLogin($admin_user);
|
||||
$this->drupalPlaceBlock('help_block');
|
||||
|
||||
$this->testType = 'type';
|
||||
$this->testText = t('Help text to find on node forms.');
|
||||
|
||||
// Create content type.
|
||||
$this->drupalCreateContentType([
|
||||
'type' => $this->testType,
|
||||
'help' => $this->testText,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that help text appears on node add/edit forms.
|
||||
*/
|
||||
public function testNodeShowHelpText() {
|
||||
// Check the node add form.
|
||||
$this->drupalGet('node/add/' . $this->testType);
|
||||
$this->assertResponse(200);
|
||||
$this->assertText($this->testText);
|
||||
|
||||
// Create node and check the node edit form.
|
||||
$node = $this->drupalCreateNode(['type' => $this->testType]);
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$this->assertResponse(200);
|
||||
$this->assertText($this->testText);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\node\NodeInterface;
|
||||
|
||||
/**
|
||||
* Tests the output of node links (read more, add new comment, etc).
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeLinksTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['views'];
|
||||
|
||||
/**
|
||||
* Tests that the links can be hidden in the view display settings.
|
||||
*/
|
||||
public function testHideLinks() {
|
||||
$node = $this->drupalCreateNode([
|
||||
'type' => 'article',
|
||||
'promote' => NodeInterface::PROMOTED,
|
||||
]);
|
||||
|
||||
// Links are displayed by default.
|
||||
$this->drupalGet('node');
|
||||
$this->assertText($node->getTitle());
|
||||
$this->assertLink('Read more');
|
||||
|
||||
// Hide links.
|
||||
entity_get_display('node', 'article', 'teaser')
|
||||
->removeComponent('links')
|
||||
->save();
|
||||
|
||||
$this->drupalGet('node');
|
||||
$this->assertText($node->getTitle());
|
||||
$this->assertNoLink('Read more');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\node\Entity\Node;
|
||||
|
||||
/**
|
||||
* Tests the loading of multiple nodes.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeLoadMultipleTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Enable Views to test the frontpage against Node::loadMultiple() results.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['views'];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$web_user = $this->drupalCreateUser(['create article content', 'create page content']);
|
||||
$this->drupalLogin($web_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates four nodes and ensures that they are loaded correctly.
|
||||
*/
|
||||
public function testNodeMultipleLoad() {
|
||||
$node1 = $this->drupalCreateNode(['type' => 'article', 'promote' => 1]);
|
||||
$node2 = $this->drupalCreateNode(['type' => 'article', 'promote' => 1]);
|
||||
$node3 = $this->drupalCreateNode(['type' => 'article', 'promote' => 0]);
|
||||
$node4 = $this->drupalCreateNode(['type' => 'page', 'promote' => 0]);
|
||||
|
||||
// Confirm that promoted nodes appear in the default node listing.
|
||||
$this->drupalGet('node');
|
||||
$this->assertText($node1->label(), 'Node title appears on the default listing.');
|
||||
$this->assertText($node2->label(), 'Node title appears on the default listing.');
|
||||
$this->assertNoText($node3->label(), 'Node title does not appear in the default listing.');
|
||||
$this->assertNoText($node4->label(), 'Node title does not appear in the default listing.');
|
||||
// Load nodes with only a condition. Nodes 3 and 4 will be loaded.
|
||||
$nodes = $this->container->get('entity_type.manager')->getStorage('node')
|
||||
->loadByProperties(['promote' => 0]);
|
||||
$this->assertEqual($node3->label(), $nodes[$node3->id()]->label(), 'Node was loaded.');
|
||||
$this->assertEqual($node4->label(), $nodes[$node4->id()]->label(), 'Node was loaded.');
|
||||
$count = count($nodes);
|
||||
$this->assertTrue($count == 2, format_string('@count nodes loaded.', ['@count' => $count]));
|
||||
|
||||
// Load nodes by nid. Nodes 1, 2 and 4 will be loaded.
|
||||
$nodes = Node::loadMultiple([1, 2, 4]);
|
||||
$count = count($nodes);
|
||||
$this->assertTrue(count($nodes) == 3, format_string('@count nodes loaded', ['@count' => $count]));
|
||||
$this->assertTrue(isset($nodes[$node1->id()]), 'Node is correctly keyed in the array');
|
||||
$this->assertTrue(isset($nodes[$node2->id()]), 'Node is correctly keyed in the array');
|
||||
$this->assertTrue(isset($nodes[$node4->id()]), 'Node is correctly keyed in the array');
|
||||
foreach ($nodes as $node) {
|
||||
$this->assertTrue(is_object($node), 'Node is an object');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
/**
|
||||
* Tests that the post information (submitted by Username on date) text displays
|
||||
* appropriately.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodePostSettingsTest extends NodeTestBase {
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$web_user = $this->drupalCreateUser(['create page content', 'administer content types', 'access user profiles']);
|
||||
$this->drupalLogin($web_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms "Basic page" content type and post information is on a new node.
|
||||
*/
|
||||
public function testPagePostInfo() {
|
||||
|
||||
// Set "Basic page" content type to display post information.
|
||||
$edit = [];
|
||||
$edit['display_submitted'] = TRUE;
|
||||
$this->drupalPostForm('admin/structure/types/manage/page', $edit, t('Save content type'));
|
||||
|
||||
// Create a node.
|
||||
$edit = [];
|
||||
$edit['title[0][value]'] = $this->randomMachineName(8);
|
||||
$edit['body[0][value]'] = $this->randomMachineName(16);
|
||||
$this->drupalPostForm('node/add/page', $edit, t('Save'));
|
||||
|
||||
// Check that the post information is displayed.
|
||||
$node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
|
||||
$elements = $this->xpath('//div[contains(@class, :class)]', [':class' => 'node__submitted']);
|
||||
$this->assertEqual(count($elements), 1, 'Post information is displayed.');
|
||||
$node->delete();
|
||||
|
||||
// Set "Basic page" content type to display post information.
|
||||
$edit = [];
|
||||
$edit['display_submitted'] = FALSE;
|
||||
$this->drupalPostForm('admin/structure/types/manage/page', $edit, t('Save content type'));
|
||||
|
||||
// Create a node.
|
||||
$edit = [];
|
||||
$edit['title[0][value]'] = $this->randomMachineName(8);
|
||||
$edit['body[0][value]'] = $this->randomMachineName(16);
|
||||
$this->drupalPostForm('node/add/page', $edit, t('Save'));
|
||||
|
||||
// Check that the post information is displayed.
|
||||
$elements = $this->xpath('//div[contains(@class, :class)]', [':class' => 'node__submitted']);
|
||||
$this->assertEqual(count($elements), 0, 'Post information is not displayed.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
/**
|
||||
* Tests that node access queries are properly altered by the node module.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeQueryAlterTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node_access_test'];
|
||||
|
||||
/**
|
||||
* User with permission to view content.
|
||||
*/
|
||||
protected $accessUser;
|
||||
|
||||
/**
|
||||
* User without permission to view content.
|
||||
*/
|
||||
protected $noAccessUser;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
node_access_rebuild();
|
||||
|
||||
// Create some content.
|
||||
$this->drupalCreateNode();
|
||||
$this->drupalCreateNode();
|
||||
$this->drupalCreateNode();
|
||||
$this->drupalCreateNode();
|
||||
|
||||
// Create user with simple node access permission. The 'node test view'
|
||||
// permission is implemented and granted by the node_access_test module.
|
||||
$this->accessUser = $this->drupalCreateUser(['access content overview', 'access content', 'node test view']);
|
||||
$this->noAccessUser = $this->drupalCreateUser(['access content overview', 'access content']);
|
||||
$this->noAccessUser2 = $this->drupalCreateUser(['access content overview', 'access content']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests 'node_access' query alter, for user with access.
|
||||
*
|
||||
* Verifies that a non-standard table alias can be used, and that a user with
|
||||
* node access can view the nodes.
|
||||
*/
|
||||
public function testNodeQueryAlterLowLevelWithAccess() {
|
||||
// User with access should be able to view 4 nodes.
|
||||
try {
|
||||
$query = db_select('node', 'mytab')
|
||||
->fields('mytab');
|
||||
$query->addTag('node_access');
|
||||
$query->addMetaData('op', 'view');
|
||||
$query->addMetaData('account', $this->accessUser);
|
||||
|
||||
$result = $query->execute()->fetchAll();
|
||||
$this->assertEqual(count($result), 4, 'User with access can see correct nodes');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->fail(t('Altered query is malformed'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests 'node_access' query alter with revision-enabled nodes.
|
||||
*/
|
||||
public function testNodeQueryAlterWithRevisions() {
|
||||
// Execute a query that only deals with the 'node_revision' table.
|
||||
try {
|
||||
$query = \Drupal::entityTypeManager()->getStorage('node')->getQuery();
|
||||
$result = $query
|
||||
->allRevisions()
|
||||
->execute();
|
||||
|
||||
$this->assertEqual(count($result), 4, 'User with access can see correct nodes');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->fail('Altered query is malformed');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests 'node_access' query alter, for user without access.
|
||||
*
|
||||
* Verifies that a non-standard table alias can be used, and that a user
|
||||
* without node access cannot view the nodes.
|
||||
*/
|
||||
public function testNodeQueryAlterLowLevelNoAccess() {
|
||||
// User without access should be able to view 0 nodes.
|
||||
try {
|
||||
$query = db_select('node', 'mytab')
|
||||
->fields('mytab');
|
||||
$query->addTag('node_access');
|
||||
$query->addMetaData('op', 'view');
|
||||
$query->addMetaData('account', $this->noAccessUser);
|
||||
|
||||
$result = $query->execute()->fetchAll();
|
||||
$this->assertEqual(count($result), 0, 'User with no access cannot see nodes');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->fail(t('Altered query is malformed'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests 'node_access' query alter, for edit access.
|
||||
*
|
||||
* Verifies that a non-standard table alias can be used, and that a user with
|
||||
* view-only node access cannot edit the nodes.
|
||||
*/
|
||||
public function testNodeQueryAlterLowLevelEditAccess() {
|
||||
// User with view-only access should not be able to edit nodes.
|
||||
try {
|
||||
$query = db_select('node', 'mytab')
|
||||
->fields('mytab');
|
||||
$query->addTag('node_access');
|
||||
$query->addMetaData('op', 'update');
|
||||
$query->addMetaData('account', $this->accessUser);
|
||||
|
||||
$result = $query->execute()->fetchAll();
|
||||
$this->assertEqual(count($result), 0, 'User with view-only access cannot edit nodes');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->fail($e->getMessage());
|
||||
$this->fail((string) $query);
|
||||
$this->fail(t('Altered query is malformed'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests 'node_access' query alter override.
|
||||
*
|
||||
* Verifies that node_access_view_all_nodes() is called from
|
||||
* node_query_node_access_alter(). We do this by checking that a user who
|
||||
* normally would not have view privileges is able to view the nodes when we
|
||||
* add a record to {node_access} paired with a corresponding privilege in
|
||||
* hook_node_grants().
|
||||
*/
|
||||
public function testNodeQueryAlterOverride() {
|
||||
$record = [
|
||||
'nid' => 0,
|
||||
'gid' => 0,
|
||||
'realm' => 'node_access_all',
|
||||
'grant_view' => 1,
|
||||
'grant_update' => 0,
|
||||
'grant_delete' => 0,
|
||||
];
|
||||
db_insert('node_access')->fields($record)->execute();
|
||||
|
||||
// Test that the noAccessUser still doesn't have the 'view'
|
||||
// privilege after adding the node_access record.
|
||||
drupal_static_reset('node_access_view_all_nodes');
|
||||
try {
|
||||
$query = db_select('node', 'mytab')
|
||||
->fields('mytab');
|
||||
$query->addTag('node_access');
|
||||
$query->addMetaData('op', 'view');
|
||||
$query->addMetaData('account', $this->noAccessUser);
|
||||
|
||||
$result = $query->execute()->fetchAll();
|
||||
$this->assertEqual(count($result), 0, 'User view privileges are not overridden');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->fail(t('Altered query is malformed'));
|
||||
}
|
||||
|
||||
// Have node_test_node_grants return a node_access_all privilege,
|
||||
// to grant the noAccessUser 'view' access. To verify that
|
||||
// node_access_view_all_nodes is properly checking the specified
|
||||
// $account instead of the current user, we will log in as
|
||||
// noAccessUser2.
|
||||
$this->drupalLogin($this->noAccessUser2);
|
||||
\Drupal::state()->set('node_access_test.no_access_uid', $this->noAccessUser->id());
|
||||
drupal_static_reset('node_access_view_all_nodes');
|
||||
try {
|
||||
$query = db_select('node', 'mytab')
|
||||
->fields('mytab');
|
||||
$query->addTag('node_access');
|
||||
$query->addMetaData('op', 'view');
|
||||
$query->addMetaData('account', $this->noAccessUser);
|
||||
|
||||
$result = $query->execute()->fetchAll();
|
||||
$this->assertEqual(count($result), 4, 'User view privileges are overridden');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->fail(t('Altered query is malformed'));
|
||||
}
|
||||
\Drupal::state()->delete('node_access_test.no_access_uid');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\filter\Entity\FilterFormat;
|
||||
|
||||
/**
|
||||
* Ensures that data added to nodes by other modules appears in RSS feeds.
|
||||
*
|
||||
* Create a node, enable the node_test module to ensure that extra data is
|
||||
* added to the node's renderable array, then verify that the data appears on
|
||||
* the site-wide RSS feed at rss.xml.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeRSSContentTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Enable a module that implements hook_node_view().
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node_test', 'views'];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Use bypass node access permission here, because the test class uses
|
||||
// hook_grants_alter() to deny access to everyone on node_access
|
||||
// queries.
|
||||
$user = $this->drupalCreateUser(['bypass node access', 'access content', 'create article content']);
|
||||
$this->drupalLogin($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that a new node includes the custom data when added to an RSS feed.
|
||||
*/
|
||||
public function testNodeRSSContent() {
|
||||
// Create a node.
|
||||
$node = $this->drupalCreateNode(['type' => 'article', 'promote' => 1]);
|
||||
|
||||
$this->drupalGet('rss.xml');
|
||||
|
||||
// Check that content added in 'rss' view mode appear in RSS feed.
|
||||
$rss_only_content = t('Extra data that should appear only in the RSS feed for node @nid.', ['@nid' => $node->id()]);
|
||||
$this->assertText($rss_only_content, 'Node content designated for RSS appear in RSS feed.');
|
||||
|
||||
// Check that content added in view modes other than 'rss' doesn't
|
||||
// appear in RSS feed.
|
||||
$non_rss_content = t('Extra data that should appear everywhere except the RSS feed for node @nid.', ['@nid' => $node->id()]);
|
||||
$this->assertNoText($non_rss_content, 'Node content not designed for RSS does not appear in RSS feed.');
|
||||
|
||||
// Check that extra RSS elements and namespaces are added to RSS feed.
|
||||
$test_element = '<testElement>' . t('Value of testElement RSS element for node @nid.', ['@nid' => $node->id()]) . '</testElement>';
|
||||
$test_ns = 'xmlns:drupaltest="http://example.com/test-namespace"';
|
||||
$this->assertRaw($test_element, 'Extra RSS elements appear in RSS feed.');
|
||||
$this->assertRaw($test_ns, 'Extra namespaces appear in RSS feed.');
|
||||
|
||||
// Check that content added in 'rss' view mode doesn't appear when
|
||||
// viewing node.
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$this->assertNoText($rss_only_content, 'Node content designed for RSS does not appear when viewing node.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests relative, root-relative, protocol-relative and absolute URLs.
|
||||
*/
|
||||
public function testUrlHandling() {
|
||||
// Only the plain_text text format is available by default, which escapes
|
||||
// all HTML.
|
||||
FilterFormat::create([
|
||||
'format' => 'full_html',
|
||||
'name' => 'Full HTML',
|
||||
'filters' => [],
|
||||
])->save();
|
||||
|
||||
$defaults = [
|
||||
'type' => 'article',
|
||||
'promote' => 1,
|
||||
];
|
||||
$this->drupalCreateNode($defaults + [
|
||||
'body' => [
|
||||
'value' => '<p><a href="' . file_url_transform_relative(file_create_url('public://root-relative')) . '">Root-relative URL</a></p>',
|
||||
'format' => 'full_html',
|
||||
],
|
||||
]);
|
||||
$protocol_relative_url = substr(file_create_url('public://protocol-relative'), strlen(\Drupal::request()->getScheme() . ':'));
|
||||
$this->drupalCreateNode($defaults + [
|
||||
'body' => [
|
||||
'value' => '<p><a href="' . $protocol_relative_url . '">Protocol-relative URL</a></p>',
|
||||
'format' => 'full_html',
|
||||
],
|
||||
]);
|
||||
$absolute_url = file_create_url('public://absolute');
|
||||
$this->drupalCreateNode($defaults + [
|
||||
'body' => [
|
||||
'value' => '<p><a href="' . $absolute_url . '">Absolute URL</a></p>',
|
||||
'format' => 'full_html',
|
||||
],
|
||||
]);
|
||||
|
||||
$this->drupalGet('rss.xml');
|
||||
$this->assertRaw(file_create_url('public://root-relative'), 'Root-relative URL is transformed to absolute.');
|
||||
$this->assertRaw($protocol_relative_url, 'Protocol-relative URL is left untouched.');
|
||||
$this->assertRaw($absolute_url, 'Absolute URL is left untouched.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\Tests\Traits\Core\GeneratePermutationsTrait;
|
||||
|
||||
/**
|
||||
* Tests user permissions for node revisions.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeRevisionPermissionsTest extends NodeTestBase {
|
||||
|
||||
use GeneratePermutationsTrait;
|
||||
|
||||
/**
|
||||
* The node revisions.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $nodeRevisions = [];
|
||||
|
||||
/**
|
||||
* The accounts.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $accounts = [];
|
||||
|
||||
// Map revision permission names to node revision access ops.
|
||||
protected $map = [
|
||||
'view' => 'view all revisions',
|
||||
'update' => 'revert all revisions',
|
||||
'delete' => 'delete all revisions',
|
||||
];
|
||||
|
||||
// Map revision permission names to node type revision access ops.
|
||||
protected $typeMap = [
|
||||
'view' => 'view page revisions',
|
||||
'update' => 'revert page revisions',
|
||||
'delete' => 'delete page revisions',
|
||||
];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$types = ['page', 'article'];
|
||||
|
||||
foreach ($types as $type) {
|
||||
// Create a node with several revisions.
|
||||
$nodes[$type] = $this->drupalCreateNode(['type' => $type]);
|
||||
$this->nodeRevisions[$type][] = $nodes[$type];
|
||||
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
// Create a revision for the same nid and settings with a random log.
|
||||
$revision = clone $nodes[$type];
|
||||
$revision->setNewRevision();
|
||||
$revision->revision_log = $this->randomMachineName(32);
|
||||
$revision->save();
|
||||
$this->nodeRevisions[$type][] = $revision;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests general revision access permissions.
|
||||
*/
|
||||
public function testNodeRevisionAccessAnyType() {
|
||||
// Create three users, one with each revision permission.
|
||||
foreach ($this->map as $op => $permission) {
|
||||
// Create the user.
|
||||
$account = $this->drupalCreateUser(
|
||||
[
|
||||
'access content',
|
||||
'edit any page content',
|
||||
'delete any page content',
|
||||
$permission,
|
||||
]
|
||||
);
|
||||
$account->op = $op;
|
||||
$this->accounts[] = $account;
|
||||
}
|
||||
|
||||
// Create an admin account (returns TRUE for all revision permissions).
|
||||
$admin_account = $this->drupalCreateUser(['access content', 'administer nodes']);
|
||||
$admin_account->is_admin = TRUE;
|
||||
$this->accounts['admin'] = $admin_account;
|
||||
$accounts['admin'] = $admin_account;
|
||||
|
||||
// Create a normal account (returns FALSE for all revision permissions).
|
||||
$normal_account = $this->drupalCreateUser();
|
||||
$normal_account->op = FALSE;
|
||||
$this->accounts[] = $normal_account;
|
||||
$accounts[] = $normal_account;
|
||||
$revision = $this->nodeRevisions['page'][1];
|
||||
|
||||
$parameters = [
|
||||
'op' => array_keys($this->map),
|
||||
'account' => $this->accounts,
|
||||
];
|
||||
|
||||
$permutations = $this->generatePermutations($parameters);
|
||||
|
||||
$node_revision_access = \Drupal::service('access_check.node.revision');
|
||||
foreach ($permutations as $case) {
|
||||
// Skip this test if there are no revisions for the node.
|
||||
if (!($revision->isDefaultRevision() && (db_query('SELECT COUNT(vid) FROM {node_field_revision} WHERE nid = :nid', [':nid' => $revision->id()])->fetchField() == 1 || $case['op'] == 'update' || $case['op'] == 'delete'))) {
|
||||
if (!empty($case['account']->is_admin) || $case['account']->hasPermission($this->map[$case['op']])) {
|
||||
$this->assertTrue($node_revision_access->checkAccess($revision, $case['account'], $case['op']), "{$this->map[$case['op']]} granted.");
|
||||
}
|
||||
else {
|
||||
$this->assertFalse($node_revision_access->checkAccess($revision, $case['account'], $case['op']), "{$this->map[$case['op']]} not granted.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test that access is FALSE for a node administrator with an invalid $node
|
||||
// or $op parameters.
|
||||
$admin_account = $accounts['admin'];
|
||||
$this->assertFalse($node_revision_access->checkAccess($revision, $admin_account, 'invalid-op'), 'NodeRevisionAccessCheck() returns FALSE with an invalid op.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests revision access permissions for a specific content type.
|
||||
*/
|
||||
public function testNodeRevisionAccessPerType() {
|
||||
// Create three users, one with each revision permission.
|
||||
foreach ($this->typeMap as $op => $permission) {
|
||||
// Create the user.
|
||||
$account = $this->drupalCreateUser(
|
||||
[
|
||||
'access content',
|
||||
'edit any page content',
|
||||
'delete any page content',
|
||||
$permission,
|
||||
]
|
||||
);
|
||||
$account->op = $op;
|
||||
$accounts[] = $account;
|
||||
}
|
||||
|
||||
$parameters = [
|
||||
'op' => array_keys($this->typeMap),
|
||||
'account' => $accounts,
|
||||
];
|
||||
|
||||
// Test that the accounts have access to the corresponding page revision
|
||||
// permissions.
|
||||
$revision = $this->nodeRevisions['page'][1];
|
||||
|
||||
$permutations = $this->generatePermutations($parameters);
|
||||
$node_revision_access = \Drupal::service('access_check.node.revision');
|
||||
foreach ($permutations as $case) {
|
||||
// Skip this test if there are no revisions for the node.
|
||||
if (!($revision->isDefaultRevision() && (db_query('SELECT COUNT(vid) FROM {node_field_revision} WHERE nid = :nid', [':nid' => $revision->id()])->fetchField() == 1 || $case['op'] == 'update' || $case['op'] == 'delete'))) {
|
||||
if (!empty($case['account']->is_admin) || $case['account']->hasPermission($this->typeMap[$case['op']], $case['account'])) {
|
||||
$this->assertTrue($node_revision_access->checkAccess($revision, $case['account'], $case['op']), "{$this->typeMap[$case['op']]} granted.");
|
||||
}
|
||||
else {
|
||||
$this->assertFalse($node_revision_access->checkAccess($revision, $case['account'], $case['op']), "{$this->typeMap[$case['op']]} not granted.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test that the accounts have no access to the article revisions.
|
||||
$revision = $this->nodeRevisions['article'][1];
|
||||
|
||||
foreach ($permutations as $case) {
|
||||
$this->assertFalse($node_revision_access->checkAccess($revision, $case['account'], $case['op']), "{$this->typeMap[$case['op']]} did not grant revision permission for articles.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\node\NodeInterface;
|
||||
|
||||
/**
|
||||
* Create a node with revisions and test viewing, saving, reverting, and
|
||||
* deleting revisions for user with access to all.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeRevisionsAllTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* A list of nodes created to be used as starting point of different tests.
|
||||
*
|
||||
* @var Drupal\node\NodeInterface[]
|
||||
*/
|
||||
protected $nodes;
|
||||
|
||||
/**
|
||||
* Revision logs of nodes created by the setup method.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $revisionLogs;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create and log in user.
|
||||
$web_user = $this->drupalCreateUser(
|
||||
[
|
||||
'view page revisions',
|
||||
'revert page revisions',
|
||||
'delete page revisions',
|
||||
'edit any page content',
|
||||
'delete any page content'
|
||||
]
|
||||
);
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
// Create an initial node.
|
||||
$node = $this->drupalCreateNode();
|
||||
|
||||
$settings = get_object_vars($node);
|
||||
$settings['revision'] = 1;
|
||||
|
||||
$nodes = [];
|
||||
$logs = [];
|
||||
|
||||
// Get the original node.
|
||||
$nodes[] = clone $node;
|
||||
|
||||
// Create three revisions.
|
||||
$revision_count = 3;
|
||||
for ($i = 0; $i < $revision_count; $i++) {
|
||||
$logs[] = $node->revision_log = $this->randomMachineName(32);
|
||||
|
||||
$node = $this->createNodeRevision($node);
|
||||
$nodes[] = clone $node;
|
||||
}
|
||||
|
||||
$this->nodes = $nodes;
|
||||
$this->revisionLogs = $logs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new revision for a given node.
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* A node object.
|
||||
*
|
||||
* @return \Drupal\node\NodeInterface
|
||||
* A node object with up to date revision information.
|
||||
*/
|
||||
protected function createNodeRevision(NodeInterface $node) {
|
||||
// Create revision with a random title and body and update variables.
|
||||
$node->title = $this->randomMachineName();
|
||||
$node->body = [
|
||||
'value' => $this->randomMachineName(32),
|
||||
'format' => filter_default_format(),
|
||||
];
|
||||
$node->setNewRevision();
|
||||
$node->save();
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks node revision operations.
|
||||
*/
|
||||
public function testRevisions() {
|
||||
$node_storage = $this->container->get('entity.manager')->getStorage('node');
|
||||
$nodes = $this->nodes;
|
||||
$logs = $this->revisionLogs;
|
||||
|
||||
// Get last node for simple checks.
|
||||
$node = $nodes[3];
|
||||
|
||||
// Create and log in user.
|
||||
$content_admin = $this->drupalCreateUser(
|
||||
[
|
||||
'view all revisions',
|
||||
'revert all revisions',
|
||||
'delete all revisions',
|
||||
'edit any page content',
|
||||
'delete any page content'
|
||||
]
|
||||
);
|
||||
$this->drupalLogin($content_admin);
|
||||
|
||||
// Confirm the correct revision text appears on "view revisions" page.
|
||||
$this->drupalGet("node/" . $node->id() . "/revisions/" . $node->getRevisionId() . "/view");
|
||||
$this->assertText($node->body->value, 'Correct text displays for version.');
|
||||
|
||||
// Confirm the correct revision log message appears on the "revisions
|
||||
// overview" page.
|
||||
$this->drupalGet("node/" . $node->id() . "/revisions");
|
||||
foreach ($logs as $revision_log) {
|
||||
$this->assertText($revision_log, 'Revision log message found.');
|
||||
}
|
||||
|
||||
// Confirm that this is the current revision.
|
||||
$this->assertTrue($node->isDefaultRevision(), 'Third node revision is the current one.');
|
||||
|
||||
// Confirm that revisions revert properly.
|
||||
$this->drupalPostForm("node/" . $node->id() . "/revisions/" . $nodes[1]->getRevisionId() . "/revert", [], t('Revert'));
|
||||
$this->assertRaw(t('@type %title has been reverted to the revision from %revision-date.',
|
||||
[
|
||||
'@type' => 'Basic page',
|
||||
'%title' => $nodes[1]->getTitle(),
|
||||
'%revision-date' => format_date($nodes[1]->getRevisionCreationTime())
|
||||
]),
|
||||
'Revision reverted.');
|
||||
$node_storage->resetCache([$node->id()]);
|
||||
$reverted_node = $node_storage->load($node->id());
|
||||
$this->assertTrue(($nodes[1]->body->value == $reverted_node->body->value), 'Node reverted correctly.');
|
||||
|
||||
// Confirm that this is not the current version.
|
||||
$node = node_revision_load($node->getRevisionId());
|
||||
$this->assertFalse($node->isDefaultRevision(), 'Third node revision is not the current one.');
|
||||
|
||||
// Confirm that the node can still be updated.
|
||||
$this->drupalPostForm("node/" . $reverted_node->id() . "/edit", ['body[0][value]' => 'We are Drupal.'], t('Save'));
|
||||
$this->assertText(t('Basic page @title has been updated.', ['@title' => $reverted_node->getTitle()]), 'Node was successfully saved after reverting a revision.');
|
||||
$this->assertText('We are Drupal.', 'Node was correctly updated after reverting a revision.');
|
||||
|
||||
// Confirm revisions delete properly.
|
||||
$this->drupalPostForm("node/" . $node->id() . "/revisions/" . $nodes[1]->getRevisionId() . "/delete", [], t('Delete'));
|
||||
$this->assertRaw(t('Revision from %revision-date of @type %title has been deleted.',
|
||||
[
|
||||
'%revision-date' => format_date($nodes[1]->getRevisionCreationTime()),
|
||||
'@type' => 'Basic page',
|
||||
'%title' => $nodes[1]->getTitle(),
|
||||
]),
|
||||
'Revision deleted.');
|
||||
$this->assertTrue(db_query('SELECT COUNT(vid) FROM {node_revision} WHERE nid = :nid and vid = :vid',
|
||||
[':nid' => $node->id(), ':vid' => $nodes[1]->getRevisionId()])->fetchField() == 0,
|
||||
'Revision not found.');
|
||||
|
||||
// Set the revision timestamp to an older date to make sure that the
|
||||
// confirmation message correctly displays the stored revision date.
|
||||
$old_revision_date = REQUEST_TIME - 86400;
|
||||
db_update('node_revision')
|
||||
->condition('vid', $nodes[2]->getRevisionId())
|
||||
->fields([
|
||||
'revision_timestamp' => $old_revision_date,
|
||||
])
|
||||
->execute();
|
||||
$this->drupalPostForm("node/" . $node->id() . "/revisions/" . $nodes[2]->getRevisionId() . "/revert", [], t('Revert'));
|
||||
$this->assertRaw(t('@type %title has been reverted to the revision from %revision-date.', [
|
||||
'@type' => 'Basic page',
|
||||
'%title' => $nodes[2]->getTitle(),
|
||||
'%revision-date' => format_date($old_revision_date),
|
||||
]));
|
||||
|
||||
// Create 50 more revisions in order to trigger paging on the revisions
|
||||
// overview screen.
|
||||
$node = $nodes[0];
|
||||
for ($i = 0; $i < 50; $i++) {
|
||||
$logs[] = $node->revision_log = $this->randomMachineName(32);
|
||||
|
||||
$node = $this->createNodeRevision($node);
|
||||
$nodes[] = clone $node;
|
||||
}
|
||||
|
||||
$this->drupalGet('node/' . $node->id() . '/revisions');
|
||||
|
||||
// Check that the pager exists.
|
||||
$this->assertRaw('page=1');
|
||||
|
||||
// Check that the last revision is displayed on the first page.
|
||||
$this->assertText(end($logs));
|
||||
|
||||
// Go to the second page and check that one of the initial three revisions
|
||||
// is displayed.
|
||||
$this->clickLink(t('Page 2'));
|
||||
$this->assertText($logs[2]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\node\Entity\NodeType;
|
||||
|
||||
/**
|
||||
* Tests the revision tab display.
|
||||
*
|
||||
* This test is similar to NodeRevisionsUITest except that it uses a user with
|
||||
* the bypass node access permission to make sure that the revision access
|
||||
* check adds correct cacheability metadata.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeRevisionsUiBypassAccessTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* User with bypass node access permission.
|
||||
*
|
||||
* @var \Drupal\user\Entity\User
|
||||
*/
|
||||
protected $editor;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['block'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create a user.
|
||||
$this->editor = $this->drupalCreateUser([
|
||||
'administer nodes',
|
||||
'edit any page content',
|
||||
'view page revisions',
|
||||
'bypass node access',
|
||||
'access user profiles',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the Revision tab is displayed correctly.
|
||||
*/
|
||||
public function testDisplayRevisionTab() {
|
||||
$this->drupalPlaceBlock('local_tasks_block');
|
||||
|
||||
$this->drupalLogin($this->editor);
|
||||
$node_storage = $this->container->get('entity.manager')->getStorage('node');
|
||||
|
||||
// Set page revision setting 'create new revision'. This will mean new
|
||||
// revisions are created by default when the node is edited.
|
||||
$type = NodeType::load('page');
|
||||
$type->setNewRevision(TRUE);
|
||||
$type->save();
|
||||
|
||||
// Create the node.
|
||||
$node = $this->drupalCreateNode();
|
||||
|
||||
// Verify the checkbox is checked on the node edit form.
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$this->assertFieldChecked('edit-revision', "'Create new revision' checkbox is checked");
|
||||
|
||||
// Uncheck the create new revision checkbox and save the node.
|
||||
$edit = ['revision' => FALSE];
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, 'Save and keep published');
|
||||
|
||||
$this->assertUrl($node->toUrl());
|
||||
$this->assertNoLink(t('Revisions'));
|
||||
|
||||
// Verify the checkbox is checked on the node edit form.
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$this->assertFieldChecked('edit-revision', "'Create new revision' checkbox is checked");
|
||||
|
||||
// Submit the form without changing the checkbox.
|
||||
$edit = [];
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, 'Save and keep published');
|
||||
|
||||
$this->assertUrl($node->toUrl());
|
||||
$this->assertLink(t('Revisions'));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
|
||||
/**
|
||||
* Tests the UI for controlling node revision behavior.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeRevisionsUiTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* @var \Drupal\user\Entity\User
|
||||
*/
|
||||
protected $editor;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create users.
|
||||
$this->editor = $this->drupalCreateUser([
|
||||
'administer nodes',
|
||||
'edit any page content',
|
||||
'view page revisions',
|
||||
'access user profiles',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that unchecking 'Create new revision' works when editing a node.
|
||||
*/
|
||||
public function testNodeFormSaveWithoutRevision() {
|
||||
$this->drupalLogin($this->editor);
|
||||
$node_storage = $this->container->get('entity.manager')->getStorage('node');
|
||||
|
||||
// Set page revision setting 'create new revision'. This will mean new
|
||||
// revisions are created by default when the node is edited.
|
||||
$type = NodeType::load('page');
|
||||
$type->setNewRevision(TRUE);
|
||||
$type->save();
|
||||
|
||||
// Create the node.
|
||||
$node = $this->drupalCreateNode();
|
||||
|
||||
// Verify the checkbox is checked on the node edit form.
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$this->assertFieldChecked('edit-revision', "'Create new revision' checkbox is checked");
|
||||
|
||||
// Uncheck the create new revision checkbox and save the node.
|
||||
$edit = ['revision' => FALSE];
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save and keep published'));
|
||||
|
||||
// Load the node again and check the revision is the same as before.
|
||||
$node_storage->resetCache([$node->id()]);
|
||||
$node_revision = $node_storage->load($node->id(), TRUE);
|
||||
$this->assertEqual($node_revision->getRevisionId(), $node->getRevisionId(), "After an existing node is saved with 'Create new revision' unchecked, a new revision is not created.");
|
||||
|
||||
// Verify the checkbox is checked on the node edit form.
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$this->assertFieldChecked('edit-revision', "'Create new revision' checkbox is checked");
|
||||
|
||||
// Submit the form without changing the checkbox.
|
||||
$edit = [];
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save and keep published'));
|
||||
|
||||
// Load the node again and check the revision is different from before.
|
||||
$node_storage->resetCache([$node->id()]);
|
||||
$node_revision = $node_storage->load($node->id());
|
||||
$this->assertNotEqual($node_revision->getRevisionId(), $node->getRevisionId(), "After an existing node is saved with 'Create new revision' checked, a new revision is created.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks HTML double escaping of revision logs.
|
||||
*/
|
||||
public function testNodeRevisionDoubleEscapeFix() {
|
||||
$this->drupalLogin($this->editor);
|
||||
$nodes = [];
|
||||
|
||||
// Create the node.
|
||||
$node = $this->drupalCreateNode();
|
||||
|
||||
$username = [
|
||||
'#theme' => 'username',
|
||||
'#account' => $this->editor,
|
||||
];
|
||||
$editor = \Drupal::service('renderer')->renderPlain($username);
|
||||
|
||||
// Get original node.
|
||||
$nodes[] = clone $node;
|
||||
|
||||
// Create revision with a random title and body and update variables.
|
||||
$node->title = $this->randomMachineName();
|
||||
$node->body = [
|
||||
'value' => $this->randomMachineName(32),
|
||||
'format' => filter_default_format(),
|
||||
];
|
||||
$node->setNewRevision();
|
||||
$revision_log = 'Revision <em>message</em> with markup.';
|
||||
$node->revision_log->value = $revision_log;
|
||||
$node->save();
|
||||
// Make sure we get revision information.
|
||||
$node = Node::load($node->id());
|
||||
$nodes[] = clone $node;
|
||||
|
||||
$this->drupalGet('node/' . $node->id() . '/revisions');
|
||||
|
||||
// Assert the old revision message.
|
||||
$date = format_date($nodes[0]->revision_timestamp->value, 'short');
|
||||
$url = new Url('entity.node.revision', ['node' => $nodes[0]->id(), 'node_revision' => $nodes[0]->getRevisionId()]);
|
||||
$this->assertRaw(\Drupal::l($date, $url) . ' by ' . $editor);
|
||||
|
||||
// Assert the current revision message.
|
||||
$date = format_date($nodes[1]->revision_timestamp->value, 'short');
|
||||
$this->assertRaw($nodes[1]->link($date) . ' by ' . $editor . '<p class="revision-log">' . $revision_log . '</p>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the Revisions tab.
|
||||
*/
|
||||
public function testNodeRevisionsTabWithDefaultRevision() {
|
||||
$this->drupalLogin($this->editor);
|
||||
|
||||
// Create the node.
|
||||
$node = $this->drupalCreateNode();
|
||||
$storage = \Drupal::entityTypeManager()->getStorage($node->getEntityTypeId());
|
||||
|
||||
// Create a new revision based on the default revision.
|
||||
// Revision 2.
|
||||
$node = $storage->load($node->id());
|
||||
$node->setNewRevision(TRUE);
|
||||
$node->save();
|
||||
|
||||
// Revision 3.
|
||||
$node = $storage->load($node->id());
|
||||
$node->setNewRevision(TRUE);
|
||||
$node->save();
|
||||
|
||||
// Revision 4.
|
||||
// Trigger translation changes in order to show the revision.
|
||||
$node = $storage->load($node->id());
|
||||
$node->setTitle($this->randomString());
|
||||
$node->isDefaultRevision(FALSE);
|
||||
$node->setNewRevision(TRUE);
|
||||
$node->save();
|
||||
|
||||
// Revision 5.
|
||||
$node = $storage->load($node->id());
|
||||
$node->isDefaultRevision(FALSE);
|
||||
$node->setNewRevision(TRUE);
|
||||
$node->save();
|
||||
|
||||
$node_id = $node->id();
|
||||
|
||||
$this->drupalGet('node/' . $node_id . '/revisions');
|
||||
|
||||
// Verify that the default revision can be an older revision than the latest
|
||||
// one.
|
||||
// Assert that the revisions with translations changes are shown: 1 and 4.
|
||||
$this->assertLinkByHref('/node/' . $node_id . '/revisions/1/revert');
|
||||
$this->assertLinkByHref('/node/' . $node_id . '/revisions/4/revert');
|
||||
|
||||
// Assert that the revisions without translations changes are filtered out:
|
||||
// 2, 3 and 5.
|
||||
$this->assertNoLinkByHref('/node/' . $node_id . '/revisions/2/revert');
|
||||
$this->assertNoLinkByHref('/node/' . $node_id . '/revisions/3/revert');
|
||||
$this->assertNoLinkByHref('/node/' . $node_id . '/revisions/5/revert');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\node\Entity\Node;
|
||||
|
||||
/**
|
||||
* Tests $node->save() for saving content.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeSaveTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* A normal logged in user.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $webUser;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node_test'];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create a user that is allowed to post; we'll use this to test the submission.
|
||||
$web_user = $this->drupalCreateUser(['create article content']);
|
||||
$this->drupalLogin($web_user);
|
||||
$this->webUser = $web_user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether custom node IDs are saved properly during an import operation.
|
||||
*
|
||||
* Workflow:
|
||||
* - first create a piece of content
|
||||
* - save the content
|
||||
* - check if node exists
|
||||
*/
|
||||
public function testImport() {
|
||||
// Node ID must be a number that is not in the database.
|
||||
$nids = \Drupal::entityManager()->getStorage('node')->getQuery()
|
||||
->sort('nid', 'DESC')
|
||||
->range(0, 1)
|
||||
->execute();
|
||||
$max_nid = reset($nids);
|
||||
$test_nid = $max_nid + mt_rand(1000, 1000000);
|
||||
$title = $this->randomMachineName(8);
|
||||
$node = [
|
||||
'title' => $title,
|
||||
'body' => [['value' => $this->randomMachineName(32)]],
|
||||
'uid' => $this->webUser->id(),
|
||||
'type' => 'article',
|
||||
'nid' => $test_nid,
|
||||
];
|
||||
/** @var \Drupal\node\NodeInterface $node */
|
||||
$node = Node::create($node);
|
||||
$node->enforceIsNew();
|
||||
|
||||
$this->assertEqual($node->getOwnerId(), $this->webUser->id());
|
||||
|
||||
$node->save();
|
||||
// Test the import.
|
||||
$node_by_nid = Node::load($test_nid);
|
||||
$this->assertTrue($node_by_nid, 'Node load by node ID.');
|
||||
|
||||
$node_by_title = $this->drupalGetNodeByTitle($title);
|
||||
$this->assertTrue($node_by_title, 'Node load by node title.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies accuracy of the "created" and "changed" timestamp functionality.
|
||||
*/
|
||||
public function testTimestamps() {
|
||||
// Use the default timestamps.
|
||||
$edit = [
|
||||
'uid' => $this->webUser->id(),
|
||||
'type' => 'article',
|
||||
'title' => $this->randomMachineName(8),
|
||||
];
|
||||
|
||||
Node::create($edit)->save();
|
||||
$node = $this->drupalGetNodeByTitle($edit['title']);
|
||||
$this->assertEqual($node->getCreatedTime(), REQUEST_TIME, 'Creating a node sets default "created" timestamp.');
|
||||
$this->assertEqual($node->getChangedTime(), REQUEST_TIME, 'Creating a node sets default "changed" timestamp.');
|
||||
|
||||
// Store the timestamps.
|
||||
$created = $node->getCreatedTime();
|
||||
|
||||
$node->save();
|
||||
$node = $this->drupalGetNodeByTitle($edit['title'], TRUE);
|
||||
$this->assertEqual($node->getCreatedTime(), $created, 'Updating a node preserves "created" timestamp.');
|
||||
|
||||
// Programmatically set the timestamps using hook_ENTITY_TYPE_presave().
|
||||
$node->title = 'testing_node_presave';
|
||||
|
||||
$node->save();
|
||||
$node = $this->drupalGetNodeByTitle('testing_node_presave', TRUE);
|
||||
$this->assertEqual($node->getCreatedTime(), 280299600, 'Saving a node uses "created" timestamp set in presave hook.');
|
||||
$this->assertEqual($node->getChangedTime(), 979534800, 'Saving a node uses "changed" timestamp set in presave hook.');
|
||||
|
||||
// Programmatically set the timestamps on the node.
|
||||
$edit = [
|
||||
'uid' => $this->webUser->id(),
|
||||
'type' => 'article',
|
||||
'title' => $this->randomMachineName(8),
|
||||
'created' => 280299600, // Sun, 19 Nov 1978 05:00:00 GMT
|
||||
'changed' => 979534800, // Drupal 1.0 release.
|
||||
];
|
||||
|
||||
Node::create($edit)->save();
|
||||
$node = $this->drupalGetNodeByTitle($edit['title']);
|
||||
$this->assertEqual($node->getCreatedTime(), 280299600, 'Creating a node programmatically uses programmatically set "created" timestamp.');
|
||||
$this->assertEqual($node->getChangedTime(), 979534800, 'Creating a node programmatically uses programmatically set "changed" timestamp.');
|
||||
|
||||
// Update the timestamps.
|
||||
$node->setCreatedTime(979534800);
|
||||
$node->changed = 280299600;
|
||||
|
||||
$node->save();
|
||||
$node = $this->drupalGetNodeByTitle($edit['title'], TRUE);
|
||||
$this->assertEqual($node->getCreatedTime(), 979534800, 'Updating a node uses user-set "created" timestamp.');
|
||||
// Allowing setting changed timestamps is required, see
|
||||
// Drupal\content_translation\ContentTranslationMetadataWrapper::setChangedTime($timestamp)
|
||||
// for example.
|
||||
$this->assertEqual($node->getChangedTime(), 280299600, 'Updating a node uses user-set "changed" timestamp.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests node presave and static node load cache.
|
||||
*
|
||||
* This test determines changes in hook_ENTITY_TYPE_presave() and verifies
|
||||
* that the static node load cache is cleared upon save.
|
||||
*/
|
||||
public function testDeterminingChanges() {
|
||||
// Initial creation.
|
||||
$node = Node::create([
|
||||
'uid' => $this->webUser->id(),
|
||||
'type' => 'article',
|
||||
'title' => 'test_changes',
|
||||
]);
|
||||
$node->save();
|
||||
|
||||
// Update the node without applying changes.
|
||||
$node->save();
|
||||
$this->assertEqual($node->label(), 'test_changes', 'No changes have been determined.');
|
||||
|
||||
// Apply changes.
|
||||
$node->title = 'updated';
|
||||
$node->save();
|
||||
|
||||
// The hook implementations node_test_node_presave() and
|
||||
// node_test_node_update() determine changes and change the title.
|
||||
$this->assertEqual($node->label(), 'updated_presave_update', 'Changes have been determined.');
|
||||
|
||||
// Test the static node load cache to be cleared.
|
||||
$node = Node::load($node->id());
|
||||
$this->assertEqual($node->label(), 'updated_presave', 'Static cache has been cleared.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests saving a node on node insert.
|
||||
*
|
||||
* This test ensures that a node has been fully saved when
|
||||
* hook_ENTITY_TYPE_insert() is invoked, so that the node can be saved again
|
||||
* in a hook implementation without errors.
|
||||
*
|
||||
* @see node_test_node_insert()
|
||||
*/
|
||||
public function testNodeSaveOnInsert() {
|
||||
// node_test_node_insert() triggers a save on insert if the title equals
|
||||
// 'new'.
|
||||
$node = $this->drupalCreateNode(['title' => 'new']);
|
||||
$this->assertEqual($node->getTitle(), 'Node ' . $node->id(), 'Node saved on node insert.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
/**
|
||||
* Tests if the syndicate block is available.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeSyndicateBlockTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['block'];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create a user and log in.
|
||||
$admin_user = $this->drupalCreateUser(['administer blocks']);
|
||||
$this->drupalLogin($admin_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the "Syndicate" block is shown when enabled.
|
||||
*/
|
||||
public function testSyndicateBlock() {
|
||||
// Place the "Syndicate" block and confirm that it is rendered.
|
||||
$this->drupalPlaceBlock('node_syndicate_block', ['id' => 'test_syndicate_block']);
|
||||
$this->drupalGet('');
|
||||
$this->assertFieldByXPath('//div[@id="block-test-syndicate-block"]/*', NULL, 'Syndicate block found.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
/**
|
||||
* Tests node template suggestions.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeTemplateSuggestionsTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Tests if template_preprocess_node() generates the correct suggestions.
|
||||
*/
|
||||
public function testNodeThemeHookSuggestions() {
|
||||
// Create node to be rendered.
|
||||
$node = $this->drupalCreateNode();
|
||||
$view_mode = 'full';
|
||||
|
||||
// Simulate theming of the node.
|
||||
$build = \Drupal::entityManager()->getViewBuilder('node')->view($node, $view_mode);
|
||||
|
||||
$variables['elements'] = $build;
|
||||
$suggestions = \Drupal::moduleHandler()->invokeAll('theme_suggestions_node', [$variables]);
|
||||
|
||||
$this->assertEqual($suggestions, ['node__full', 'node__page', 'node__page__full', 'node__' . $node->id(), 'node__' . $node->id() . '__full'], 'Found expected node suggestions.');
|
||||
|
||||
// Change the view mode.
|
||||
$view_mode = 'node.my_custom_view_mode';
|
||||
$build = \Drupal::entityManager()->getViewBuilder('node')->view($node, $view_mode);
|
||||
|
||||
$variables['elements'] = $build;
|
||||
$suggestions = \Drupal::moduleHandler()->invokeAll('theme_suggestions_node', [$variables]);
|
||||
|
||||
$this->assertEqual($suggestions, ['node__node_my_custom_view_mode', 'node__page', 'node__page__node_my_custom_view_mode', 'node__' . $node->id(), 'node__' . $node->id() . '__node_my_custom_view_mode'], 'Found expected node suggestions.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\node\NodeInterface;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Sets up page and article content types.
|
||||
*/
|
||||
abstract class NodeTestBase extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node', 'datetime'];
|
||||
|
||||
/**
|
||||
* The node access control handler.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityAccessControlHandlerInterface
|
||||
*/
|
||||
protected $accessHandler;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create Basic page and Article node types.
|
||||
if ($this->profile != 'standard') {
|
||||
$this->drupalCreateContentType([
|
||||
'type' => 'page',
|
||||
'name' => 'Basic page',
|
||||
'display_submitted' => FALSE,
|
||||
]);
|
||||
$this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']);
|
||||
}
|
||||
$this->accessHandler = \Drupal::entityManager()->getAccessControlHandler('node');
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that node access correctly grants or denies access.
|
||||
*
|
||||
* @param array $ops
|
||||
* An associative array of the expected node access grants for the node
|
||||
* and account, with each key as the name of an operation (e.g. 'view',
|
||||
* 'delete') and each value a Boolean indicating whether access to that
|
||||
* operation should be granted.
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* The node object to check.
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The user account for which to check access.
|
||||
*/
|
||||
public function assertNodeAccess(array $ops, NodeInterface $node, AccountInterface $account) {
|
||||
foreach ($ops as $op => $result) {
|
||||
$this->assertEqual($result, $this->accessHandler->access($node, $op, $account), $this->nodeAccessAssertMessage($op, $result, $node->language()->getId()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that node create access correctly grants or denies access.
|
||||
*
|
||||
* @param string $bundle
|
||||
* The node bundle to check access to.
|
||||
* @param bool $result
|
||||
* Whether access should be granted or not.
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The user account for which to check access.
|
||||
* @param string|null $langcode
|
||||
* (optional) The language code indicating which translation of the node
|
||||
* to check. If NULL, the untranslated (fallback) access is checked.
|
||||
*/
|
||||
public function assertNodeCreateAccess($bundle, $result, AccountInterface $account, $langcode = NULL) {
|
||||
$this->assertEqual($result, $this->accessHandler->createAccess($bundle, $account, [
|
||||
'langcode' => $langcode,
|
||||
]), $this->nodeAccessAssertMessage('create', $result, $langcode));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an assert message to display which node access was tested.
|
||||
*
|
||||
* @param string $operation
|
||||
* The operation to check access for.
|
||||
* @param bool $result
|
||||
* Whether access should be granted or not.
|
||||
* @param string|null $langcode
|
||||
* (optional) The language code indicating which translation of the node
|
||||
* to check. If NULL, the untranslated (fallback) access is checked.
|
||||
*
|
||||
* @return string
|
||||
* An assert message string which contains information in plain English
|
||||
* about the node access permission test that was performed.
|
||||
*/
|
||||
public function nodeAccessAssertMessage($operation, $result, $langcode = NULL) {
|
||||
return format_string(
|
||||
'Node access returns @result with operation %op, language code %langcode.',
|
||||
[
|
||||
'@result' => $result ? 'true' : 'false',
|
||||
'%op' => $operation,
|
||||
'%langcode' => !empty($langcode) ? $langcode : 'empty'
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\comment\Tests\CommentTestTrait;
|
||||
use Drupal\Component\Utility\Html;
|
||||
|
||||
/**
|
||||
* Tests node title.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeTitleTest extends NodeTestBase {
|
||||
|
||||
use CommentTestTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['comment', 'views', 'block'];
|
||||
|
||||
/**
|
||||
* A user with permission to bypass access content.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->drupalPlaceBlock('system_breadcrumb_block');
|
||||
|
||||
$this->adminUser = $this->drupalCreateUser(['administer nodes', 'create article content', 'create page content', 'post comments']);
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->addDefaultCommentField('node', 'page');
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates one node and tests if the node title has the correct value.
|
||||
*/
|
||||
public function testNodeTitle() {
|
||||
// Create "Basic page" content with title.
|
||||
// Add the node to the frontpage so we can test if teaser links are
|
||||
// clickable.
|
||||
$settings = [
|
||||
'title' => $this->randomMachineName(8),
|
||||
'promote' => 1,
|
||||
];
|
||||
$node = $this->drupalCreateNode($settings);
|
||||
|
||||
// Test <title> tag.
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$xpath = '//title';
|
||||
$this->assertEqual($this->xpath($xpath)[0]->getText(), $node->label() . ' | Drupal', 'Page title is equal to node title.', 'Node');
|
||||
|
||||
// Test breadcrumb in comment preview.
|
||||
$this->drupalGet('comment/reply/node/' . $node->id() . '/comment');
|
||||
$xpath = '//nav[@class="breadcrumb"]/ol/li[last()]/a';
|
||||
$this->assertEqual($this->xpath($xpath)[0]->getText(), $node->label(), 'Node breadcrumb is equal to node title.', 'Node');
|
||||
|
||||
// Test node title in comment preview.
|
||||
$this->assertEqual($this->xpath('//article[contains(concat(" ", normalize-space(@class), " "), :node-class)]/h2/a/span', [':node-class' => ' node--type-' . $node->bundle() . ' '])[0]->getText(), $node->label(), 'Node preview title is equal to node title.', 'Node');
|
||||
|
||||
// Test node title is clickable on teaser list (/node).
|
||||
$this->drupalGet('node');
|
||||
$this->clickLink($node->label());
|
||||
|
||||
// Test edge case where node title is set to 0.
|
||||
$settings = [
|
||||
'title' => 0,
|
||||
];
|
||||
$node = $this->drupalCreateNode($settings);
|
||||
// Test that 0 appears as <title>.
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$this->assertTitle(0 . ' | Drupal', 'Page title is equal to 0.', 'Node');
|
||||
// Test that 0 appears in the template <h1>.
|
||||
$xpath = '//h1';
|
||||
$this->assertEqual(current($this->xpath($xpath)), 0, 'Node title is displayed as 0.', 'Node');
|
||||
|
||||
// Test edge case where node title contains special characters.
|
||||
$edge_case_title = 'article\'s "title".';
|
||||
$settings = [
|
||||
'title' => $edge_case_title,
|
||||
];
|
||||
$node = $this->drupalCreateNode($settings);
|
||||
// Test that the title appears as <title>. The title will be escaped on the
|
||||
// the page.
|
||||
$edge_case_title_escaped = Html::escape($edge_case_title);
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$this->assertRaw('<title>' . $edge_case_title_escaped . ' | Drupal</title>', 'Page title is equal to article\'s "title".', 'Node');
|
||||
|
||||
// Test that the title appears as <title> when reloading the node page.
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$this->assertRaw('<title>' . $edge_case_title_escaped . ' | Drupal</title>', 'Page title is equal to article\'s "title".', 'Node');
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
|
||||
/**
|
||||
* Create a node with dangerous tags in its title and test that they are
|
||||
* escaped.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeTitleXSSTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Tests XSS functionality with a node entity.
|
||||
*/
|
||||
public function testNodeTitleXSS() {
|
||||
// Prepare a user to do the stuff.
|
||||
$web_user = $this->drupalCreateUser(['create page content', 'edit any page content']);
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
$xss = '<script>alert("xss")</script>';
|
||||
$title = $xss . $this->randomMachineName();
|
||||
$edit = [];
|
||||
$edit['title[0][value]'] = $title;
|
||||
|
||||
$this->drupalPostForm('node/add/page', $edit, t('Preview'));
|
||||
$this->assertNoRaw($xss, 'Harmful tags are escaped when previewing a node.');
|
||||
|
||||
$settings = ['title' => $title];
|
||||
$node = $this->drupalCreateNode($settings);
|
||||
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
// Titles should be escaped.
|
||||
$this->assertRaw('<title>' . Html::escape($title) . ' | Drupal</title>', 'Title is displayed when viewing a node.');
|
||||
$this->assertNoRaw($xss, 'Harmful tags are escaped when viewing a node.');
|
||||
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$this->assertNoRaw($xss, 'Harmful tags are escaped when editing a node.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,512 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Tests\content_translation\Functional\ContentTranslationUITestBase;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
|
||||
/**
|
||||
* Tests the Node Translation UI.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeTranslationUITest extends ContentTranslationUITestBase {
|
||||
|
||||
/**
|
||||
* {inheritdoc}
|
||||
*/
|
||||
protected $defaultCacheContexts = [
|
||||
'languages:language_interface',
|
||||
'session',
|
||||
'theme',
|
||||
'route',
|
||||
'timezone',
|
||||
'url.path.parent',
|
||||
'url.query_args:_wrapper_format',
|
||||
'user'
|
||||
];
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['block', 'language', 'content_translation', 'node', 'datetime', 'field_ui', 'help'];
|
||||
|
||||
/**
|
||||
* The profile to install as a basis for testing.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $profile = 'standard';
|
||||
|
||||
protected function setUp() {
|
||||
$this->entityTypeId = 'node';
|
||||
$this->bundle = 'article';
|
||||
parent::setUp();
|
||||
|
||||
// Ensure the help message is shown even with prefixed paths.
|
||||
$this->drupalPlaceBlock('help_block', ['region' => 'content']);
|
||||
|
||||
// Display the language selector.
|
||||
$this->drupalLogin($this->administrator);
|
||||
$edit = ['language_configuration[language_alterable]' => TRUE];
|
||||
$this->drupalPostForm('admin/structure/types/manage/article', $edit, t('Save content type'));
|
||||
$this->drupalLogin($this->translator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the basic translation UI.
|
||||
*/
|
||||
public function testTranslationUI() {
|
||||
parent::testTranslationUI();
|
||||
$this->doUninstallTest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests changing the published status on a node without fields.
|
||||
*/
|
||||
public function testPublishedStatusNoFields() {
|
||||
// Test changing the published status of an article without fields.
|
||||
$this->drupalLogin($this->administrator);
|
||||
// Delete all fields.
|
||||
$this->drupalGet('admin/structure/types/manage/article/fields');
|
||||
$this->drupalPostForm('admin/structure/types/manage/article/fields/node.article.' . $this->fieldName . '/delete', [], t('Delete'));
|
||||
$this->drupalPostForm('admin/structure/types/manage/article/fields/node.article.field_tags/delete', [], t('Delete'));
|
||||
$this->drupalPostForm('admin/structure/types/manage/article/fields/node.article.field_image/delete', [], t('Delete'));
|
||||
|
||||
// Add a node.
|
||||
$default_langcode = $this->langcodes[0];
|
||||
$values[$default_langcode] = ['title' => [['value' => $this->randomMachineName()]]];
|
||||
$this->entityId = $this->createEntity($values[$default_langcode], $default_langcode);
|
||||
$storage = $this->container->get('entity_type.manager')
|
||||
->getStorage($this->entityTypeId);
|
||||
$storage->resetCache([$this->entityId]);
|
||||
$entity = $storage->load($this->entityId);
|
||||
|
||||
// Add a content translation.
|
||||
$langcode = 'fr';
|
||||
$language = ConfigurableLanguage::load($langcode);
|
||||
$values[$langcode] = ['title' => [['value' => $this->randomMachineName()]]];
|
||||
|
||||
$entity_type_id = $entity->getEntityTypeId();
|
||||
$add_url = Url::fromRoute("entity.$entity_type_id.content_translation_add", [
|
||||
$entity->getEntityTypeId() => $entity->id(),
|
||||
'source' => $default_langcode,
|
||||
'target' => $langcode
|
||||
], ['language' => $language]);
|
||||
$this->drupalPostForm($add_url, $this->getEditValues($values, $langcode), t('Save and unpublish (this translation)'));
|
||||
|
||||
$storage->resetCache([$this->entityId]);
|
||||
$entity = $storage->load($this->entityId);
|
||||
$translation = $entity->getTranslation($langcode);
|
||||
// Make sure we unpublished the node correctly.
|
||||
$this->assertFalse($this->manager->getTranslationMetadata($translation)->isPublished(), 'The translation has been correctly unpublished.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getTranslatorPermissions() {
|
||||
return array_merge(parent::getTranslatorPermissions(), ['administer nodes', "edit any $this->bundle content"]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getEditorPermissions() {
|
||||
return ['administer nodes', 'create article content'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getAdministratorPermissions() {
|
||||
return array_merge(parent::getAdministratorPermissions(), ['access administration pages', 'administer content types', 'administer node fields', 'access content overview', 'bypass node access', 'administer languages', 'administer themes', 'view the administration theme']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNewEntityValues($langcode) {
|
||||
return ['title' => [['value' => $this->randomMachineName()]]] + parent::getNewEntityValues($langcode);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getFormSubmitAction(EntityInterface $entity, $langcode) {
|
||||
if ($entity->getTranslation($langcode)->isPublished()) {
|
||||
return t('Save and keep published') . $this->getFormSubmitSuffix($entity, $langcode);
|
||||
}
|
||||
else {
|
||||
return t('Save and keep unpublished') . $this->getFormSubmitSuffix($entity, $langcode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doTestPublishedStatus() {
|
||||
$storage = $this->container->get('entity_type.manager')
|
||||
->getStorage($this->entityTypeId);
|
||||
$storage->resetCache([$this->entityId]);
|
||||
$entity = $storage->load($this->entityId);
|
||||
$languages = $this->container->get('language_manager')->getLanguages();
|
||||
|
||||
$actions = [
|
||||
t('Save and keep published'),
|
||||
t('Save and unpublish'),
|
||||
];
|
||||
|
||||
foreach ($actions as $index => $action) {
|
||||
// (Un)publish the node translations and check that the translation
|
||||
// statuses are (un)published accordingly.
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
$options = ['language' => $languages[$langcode]];
|
||||
$url = $entity->urlInfo('edit-form', $options);
|
||||
$this->drupalPostForm($url, [], $action . $this->getFormSubmitSuffix($entity, $langcode), $options);
|
||||
}
|
||||
$storage->resetCache([$this->entityId]);
|
||||
$entity = $storage->load($this->entityId);
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
// The node is created as unpublished thus we switch to the published
|
||||
// status first.
|
||||
$status = !$index;
|
||||
$translation = $entity->getTranslation($langcode);
|
||||
$this->assertEqual($status, $this->manager->getTranslationMetadata($translation)->isPublished(), 'The translation has been correctly unpublished.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doTestAuthoringInfo() {
|
||||
$storage = $this->container->get('entity_type.manager')
|
||||
->getStorage($this->entityTypeId);
|
||||
$storage->resetCache([$this->entityId]);
|
||||
$entity = $storage->load($this->entityId);
|
||||
$languages = $this->container->get('language_manager')->getLanguages();
|
||||
$values = [];
|
||||
|
||||
// Post different base field information for each translation.
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
$user = $this->drupalCreateUser();
|
||||
$values[$langcode] = [
|
||||
'uid' => $user->id(),
|
||||
'created' => REQUEST_TIME - mt_rand(0, 1000),
|
||||
'sticky' => (bool) mt_rand(0, 1),
|
||||
'promote' => (bool) mt_rand(0, 1),
|
||||
];
|
||||
$edit = [
|
||||
'uid[0][target_id]' => $user->getUsername(),
|
||||
'created[0][value][date]' => format_date($values[$langcode]['created'], 'custom', 'Y-m-d'),
|
||||
'created[0][value][time]' => format_date($values[$langcode]['created'], 'custom', 'H:i:s'),
|
||||
'sticky[value]' => $values[$langcode]['sticky'],
|
||||
'promote[value]' => $values[$langcode]['promote'],
|
||||
];
|
||||
$options = ['language' => $languages[$langcode]];
|
||||
$url = $entity->urlInfo('edit-form', $options);
|
||||
$this->drupalPostForm($url, $edit, $this->getFormSubmitAction($entity, $langcode), $options);
|
||||
}
|
||||
|
||||
$storage->resetCache([$this->entityId]);
|
||||
$entity = $storage->load($this->entityId);
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
$translation = $entity->getTranslation($langcode);
|
||||
$metadata = $this->manager->getTranslationMetadata($translation);
|
||||
$this->assertEqual($metadata->getAuthor()->id(), $values[$langcode]['uid'], 'Translation author correctly stored.');
|
||||
$this->assertEqual($metadata->getCreatedTime(), $values[$langcode]['created'], 'Translation date correctly stored.');
|
||||
$this->assertEqual($translation->isSticky(), $values[$langcode]['sticky'], 'Sticky of Translation correctly stored.');
|
||||
$this->assertEqual($translation->isPromoted(), $values[$langcode]['promote'], 'Promoted of Translation correctly stored.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that translation page inherits admin status of edit page.
|
||||
*/
|
||||
public function testTranslationLinkTheme() {
|
||||
$this->drupalLogin($this->administrator);
|
||||
$article = $this->drupalCreateNode(['type' => 'article', 'langcode' => $this->langcodes[0]]);
|
||||
|
||||
// Set up Seven as the admin theme and use it for node editing.
|
||||
$this->container->get('theme_handler')->install(['seven']);
|
||||
$edit = [];
|
||||
$edit['admin_theme'] = 'seven';
|
||||
$edit['use_admin_theme'] = TRUE;
|
||||
$this->drupalPostForm('admin/appearance', $edit, t('Save configuration'));
|
||||
$this->drupalGet('node/' . $article->id() . '/translations');
|
||||
$this->assertRaw('core/themes/seven/css/base/elements.css', 'Translation uses admin theme if edit is admin.');
|
||||
|
||||
// Turn off admin theme for editing, assert inheritance to translations.
|
||||
$edit['use_admin_theme'] = FALSE;
|
||||
$this->drupalPostForm('admin/appearance', $edit, t('Save configuration'));
|
||||
$this->drupalGet('node/' . $article->id() . '/translations');
|
||||
$this->assertNoRaw('core/themes/seven/css/base/elements.css', 'Translation uses frontend theme if edit is frontend.');
|
||||
|
||||
// Assert presence of translation page itself (vs. DisabledBundle below).
|
||||
$this->assertResponse(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that no metadata is stored for a disabled bundle.
|
||||
*/
|
||||
public function testDisabledBundle() {
|
||||
// Create a bundle that does not have translation enabled.
|
||||
$disabledBundle = $this->randomMachineName();
|
||||
$this->drupalCreateContentType(['type' => $disabledBundle, 'name' => $disabledBundle]);
|
||||
|
||||
// Create a node for each bundle.
|
||||
$node = $this->drupalCreateNode([
|
||||
'type' => $this->bundle,
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
]);
|
||||
|
||||
// Make sure that nothing was inserted into the {content_translation} table.
|
||||
$rows = db_query('SELECT nid, count(nid) AS count FROM {node_field_data} WHERE type <> :type GROUP BY nid HAVING count(nid) >= 2', [':type' => $this->bundle])->fetchAll();
|
||||
$this->assertEqual(0, count($rows));
|
||||
|
||||
// Ensure the translation tab is not accessible.
|
||||
$this->drupalGet('node/' . $node->id() . '/translations');
|
||||
$this->assertResponse(403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that translations are rendered properly.
|
||||
*/
|
||||
public function testTranslationRendering() {
|
||||
$default_langcode = $this->langcodes[0];
|
||||
$values[$default_langcode] = $this->getNewEntityValues($default_langcode);
|
||||
$this->entityId = $this->createEntity($values[$default_langcode], $default_langcode);
|
||||
$node = \Drupal::entityManager()->getStorage($this->entityTypeId)->load($this->entityId);
|
||||
$node->setPromoted(TRUE);
|
||||
|
||||
// Create translations.
|
||||
foreach (array_diff($this->langcodes, [$default_langcode]) as $langcode) {
|
||||
$values[$langcode] = $this->getNewEntityValues($langcode);
|
||||
$translation = $node->addTranslation($langcode, $values[$langcode]);
|
||||
// Publish and promote the translation to frontpage.
|
||||
$translation->setPromoted(TRUE);
|
||||
$translation->setPublished(TRUE);
|
||||
}
|
||||
$node->save();
|
||||
|
||||
// Test that the frontpage view displays the correct translations.
|
||||
\Drupal::service('module_installer')->install(['views'], TRUE);
|
||||
$this->rebuildContainer();
|
||||
$this->doTestTranslations('node', $values);
|
||||
|
||||
// Enable the translation language renderer.
|
||||
$view = \Drupal::entityManager()->getStorage('view')->load('frontpage');
|
||||
$display = &$view->getDisplay('default');
|
||||
$display['display_options']['rendering_language'] = '***LANGUAGE_entity_translation***';
|
||||
$view->save();
|
||||
|
||||
// Need to check from the beginning, including the base_path, in the url
|
||||
// since the pattern for the default language might be a substring of
|
||||
// the strings for other languages.
|
||||
$base_path = base_path();
|
||||
|
||||
// Check the frontpage for 'Read more' links to each translation.
|
||||
// See also assertTaxonomyPage() in NodeAccessBaseTableTest.
|
||||
$node_href = 'node/' . $node->id();
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
$this->drupalGet('node', ['language' => \Drupal::languageManager()->getLanguage($langcode)]);
|
||||
$num_match_found = 0;
|
||||
if ($langcode == 'en') {
|
||||
// Site default language does not have langcode prefix in the URL.
|
||||
$expected_href = $base_path . $node_href;
|
||||
}
|
||||
else {
|
||||
$expected_href = $base_path . $langcode . '/' . $node_href;
|
||||
}
|
||||
$pattern = '|^' . $expected_href . '$|';
|
||||
foreach ($this->xpath("//a[text()='Read more']") as $link) {
|
||||
if (preg_match($pattern, $link->getAttribute('href'), $matches) == TRUE) {
|
||||
$num_match_found++;
|
||||
}
|
||||
}
|
||||
$this->assertTrue($num_match_found == 1, 'There is 1 Read more link, ' . $expected_href . ', for the ' . $langcode . ' translation of a node on the frontpage. (Found ' . $num_match_found . '.)');
|
||||
}
|
||||
|
||||
// Check the frontpage for 'Add new comment' links that include the
|
||||
// language.
|
||||
$comment_form_href = 'node/' . $node->id() . '#comment-form';
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
$this->drupalGet('node', ['language' => \Drupal::languageManager()->getLanguage($langcode)]);
|
||||
$num_match_found = 0;
|
||||
if ($langcode == 'en') {
|
||||
// Site default language does not have langcode prefix in the URL.
|
||||
$expected_href = $base_path . $comment_form_href;
|
||||
}
|
||||
else {
|
||||
$expected_href = $base_path . $langcode . '/' . $comment_form_href;
|
||||
}
|
||||
$pattern = '|^' . $expected_href . '$|';
|
||||
foreach ($this->xpath("//a[text()='Add new comment']") as $link) {
|
||||
if (preg_match($pattern, $link->getAttribute('href'), $matches) == TRUE) {
|
||||
$num_match_found++;
|
||||
}
|
||||
}
|
||||
$this->assertTrue($num_match_found == 1, 'There is 1 Add new comment link, ' . $expected_href . ', for the ' . $langcode . ' translation of a node on the frontpage. (Found ' . $num_match_found . '.)');
|
||||
}
|
||||
|
||||
// Test that the node page displays the correct translations.
|
||||
$this->doTestTranslations('node/' . $node->id(), $values);
|
||||
|
||||
// Test that the node page has the correct alternate hreflang links.
|
||||
$this->doTestAlternateHreflangLinks($node->urlInfo());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the given path displays the correct translation values.
|
||||
*
|
||||
* @param string $path
|
||||
* The path to be tested.
|
||||
* @param array $values
|
||||
* The translation values to be found.
|
||||
*/
|
||||
protected function doTestTranslations($path, array $values) {
|
||||
$languages = $this->container->get('language_manager')->getLanguages();
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
$this->drupalGet($path, ['language' => $languages[$langcode]]);
|
||||
$this->assertText($values[$langcode]['title'][0]['value'], format_string('The %langcode node translation is correctly displayed.', ['%langcode' => $langcode]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the given path provides the correct alternate hreflang links.
|
||||
*
|
||||
* @param \Drupal\Core\Url $url
|
||||
* The path to be tested.
|
||||
*/
|
||||
protected function doTestAlternateHreflangLinks(Url $url) {
|
||||
$languages = $this->container->get('language_manager')->getLanguages();
|
||||
$url->setAbsolute();
|
||||
$urls = [];
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
$language_url = clone $url;
|
||||
$urls[$langcode] = $language_url->setOption('language', $languages[$langcode]);
|
||||
}
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
$this->drupalGet($urls[$langcode]);
|
||||
foreach ($urls as $alternate_langcode => $language_url) {
|
||||
// Retrieve desired link elements from the HTML head.
|
||||
$links = $this->xpath('head/link[@rel = "alternate" and @href = :href and @hreflang = :hreflang]',
|
||||
[':href' => $language_url->toString(), ':hreflang' => $alternate_langcode]);
|
||||
$this->assert(isset($links[0]), format_string('The %langcode node translation has the correct alternate hreflang link for %alternate_langcode: %link.', ['%langcode' => $langcode, '%alternate_langcode' => $alternate_langcode, '%link' => $url->toString()]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getFormSubmitSuffix(EntityInterface $entity, $langcode) {
|
||||
if (!$entity->isNew() && $entity->isTranslatable()) {
|
||||
$translations = $entity->getTranslationLanguages();
|
||||
if ((count($translations) > 1 || !isset($translations[$langcode])) && ($field = $entity->getFieldDefinition('status'))) {
|
||||
return ' ' . ($field->isTranslatable() ? t('(this translation)') : t('(all translations)'));
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests uninstalling content_translation.
|
||||
*/
|
||||
protected function doUninstallTest() {
|
||||
// Delete all the nodes so there is no data.
|
||||
$nodes = Node::loadMultiple();
|
||||
foreach ($nodes as $node) {
|
||||
$node->delete();
|
||||
}
|
||||
$language_count = count(\Drupal::configFactory()->listAll('language.content_settings.'));
|
||||
\Drupal::service('module_installer')->uninstall(['content_translation']);
|
||||
$this->rebuildContainer();
|
||||
$this->assertEqual($language_count, count(\Drupal::configFactory()->listAll('language.content_settings.')), 'Languages have been fixed rather than deleted during content_translation uninstall.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doTestTranslationEdit() {
|
||||
$storage = $this->container->get('entity_type.manager')
|
||||
->getStorage($this->entityTypeId);
|
||||
$storage->resetCache([$this->entityId]);
|
||||
$entity = $storage->load($this->entityId);
|
||||
$languages = $this->container->get('language_manager')->getLanguages();
|
||||
$type_name = node_get_type_label($entity);
|
||||
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
// We only want to test the title for non-english translations.
|
||||
if ($langcode != 'en') {
|
||||
$options = ['language' => $languages[$langcode]];
|
||||
$url = $entity->urlInfo('edit-form', $options);
|
||||
$this->drupalGet($url);
|
||||
|
||||
$title = t('<em>Edit @type</em> @title [%language translation]', [
|
||||
'@type' => $type_name,
|
||||
'@title' => $entity->getTranslation($langcode)->label(),
|
||||
'%language' => $languages[$langcode]->getName(),
|
||||
]);
|
||||
$this->assertRaw($title);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that revision translations are rendered properly.
|
||||
*/
|
||||
public function testRevisionTranslationRendering() {
|
||||
$storage = \Drupal::entityTypeManager()->getStorage('node');
|
||||
|
||||
// Create a node.
|
||||
$nid = $this->createEntity(['title' => 'First rev en title'], 'en');
|
||||
$node = $storage->load($nid);
|
||||
$original_revision_id = $node->getRevisionId();
|
||||
|
||||
// Add a French translation.
|
||||
$translation = $node->addTranslation('fr');
|
||||
$translation->title = 'First rev fr title';
|
||||
$translation->setNewRevision(FALSE);
|
||||
$translation->save();
|
||||
|
||||
// Create a new revision.
|
||||
$node->title = 'Second rev en title';
|
||||
$node->setNewRevision(TRUE);
|
||||
$node->save();
|
||||
|
||||
// Get an English view of this revision.
|
||||
$original_revision = $storage->loadRevision($original_revision_id);
|
||||
$original_revision_url = $original_revision->toUrl('revision')->toString();
|
||||
|
||||
// Should be different from regular node URL.
|
||||
$this->assertNotIdentical($original_revision_url, $original_revision->toUrl()->toString());
|
||||
$this->drupalGet($original_revision_url);
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Contents should be in English, of correct revision.
|
||||
$this->assertText('First rev en title');
|
||||
$this->assertNoText('First rev fr title');
|
||||
|
||||
// Get a French view.
|
||||
$url_fr = $original_revision->getTranslation('fr')->toUrl('revision')->toString();
|
||||
|
||||
// Should have different URL from English.
|
||||
$this->assertNotIdentical($url_fr, $original_revision->toUrl()->toString());
|
||||
$this->assertNotIdentical($url_fr, $original_revision_url);
|
||||
$this->drupalGet($url_fr);
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Contents should be in French, of correct revision.
|
||||
$this->assertText('First rev fr title');
|
||||
$this->assertNoText('First rev en title');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
|
||||
/**
|
||||
* Tests node type initial language settings.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeTypeInitialLanguageTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['language', 'field_ui'];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$web_user = $this->drupalCreateUser(['bypass node access', 'administer content types', 'administer node fields', 'administer node form display', 'administer node display', 'administer languages']);
|
||||
$this->drupalLogin($web_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the node type initial language defaults, and modifies them.
|
||||
*
|
||||
* The default initial language must be the site's default, and the language
|
||||
* locked option must be on.
|
||||
*/
|
||||
public function testNodeTypeInitialLanguageDefaults() {
|
||||
$this->drupalGet('admin/structure/types/manage/article');
|
||||
$this->assertOptionSelected('edit-language-configuration-langcode', LanguageInterface::LANGCODE_SITE_DEFAULT, 'The default initial language is the site default.');
|
||||
$this->assertNoFieldChecked('edit-language-configuration-language-alterable', 'Language selector is hidden by default.');
|
||||
|
||||
// Tests if the language field cannot be rearranged on the manage fields tab.
|
||||
$this->drupalGet('admin/structure/types/manage/article/fields');
|
||||
$language_field = $this->xpath('//*[@id="field-overview"]/*[@id="language"]');
|
||||
$this->assert(empty($language_field), 'Language field is not visible on manage fields tab.');
|
||||
|
||||
$this->drupalGet('node/add/article');
|
||||
$this->assertNoField('langcode', 'Language is not selectable on node add/edit page by default.');
|
||||
|
||||
// Adds a new language and set it as default.
|
||||
$edit = [
|
||||
'predefined_langcode' => 'hu',
|
||||
];
|
||||
$this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language'));
|
||||
$edit = [
|
||||
'site_default_language' => 'hu',
|
||||
];
|
||||
$this->drupalPostForm('admin/config/regional/language', $edit, t('Save configuration'));
|
||||
|
||||
// Tests the initial language after changing the site default language.
|
||||
// First unhide the language selector.
|
||||
$edit = [
|
||||
'language_configuration[language_alterable]' => TRUE,
|
||||
];
|
||||
$this->drupalPostForm('admin/structure/types/manage/article', $edit, t('Save content type'));
|
||||
$this->drupalGet('node/add/article');
|
||||
$this->assertField('langcode[0][value]', 'Language is selectable on node add/edit page when language not hidden.');
|
||||
$this->assertOptionSelected('edit-langcode-0-value', 'hu', 'The initial language is the site default on the node add page after the site default language is changed.');
|
||||
|
||||
// Tests if the language field can be rearranged on the manage form display
|
||||
// tab.
|
||||
$this->drupalGet('admin/structure/types/manage/article/form-display');
|
||||
$language_field = $this->xpath('//*[@id="langcode"]');
|
||||
$this->assert(!empty($language_field), 'Language field is visible on manage form display tab.');
|
||||
|
||||
// Tests if the language field can be rearranged on the manage display tab.
|
||||
$this->drupalGet('admin/structure/types/manage/article/display');
|
||||
$language_display = $this->xpath('//*[@id="langcode"]');
|
||||
$this->assert(!empty($language_display), 'Language field is visible on manage display tab.');
|
||||
// Tests if the language field is hidden by default.
|
||||
$this->assertOptionSelected('edit-fields-langcode-region', 'hidden', 'Language is hidden by default on manage display tab.');
|
||||
|
||||
// Changes the initial language settings.
|
||||
$edit = [
|
||||
'language_configuration[langcode]' => 'en',
|
||||
];
|
||||
$this->drupalPostForm('admin/structure/types/manage/article', $edit, t('Save content type'));
|
||||
$this->drupalGet('node/add/article');
|
||||
$this->assertOptionSelected('edit-langcode-0-value', 'en', 'The initial language is the defined language.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests language field visibility features.
|
||||
*/
|
||||
public function testLanguageFieldVisibility() {
|
||||
// Creates a node to test Language field visibility feature.
|
||||
$edit = [
|
||||
'title[0][value]' => $this->randomMachineName(8),
|
||||
'body[0][value]' => $this->randomMachineName(16),
|
||||
];
|
||||
$this->drupalPostForm('node/add/article', $edit, t('Save'));
|
||||
$node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
|
||||
$this->assertTrue($node, 'Node found in database.');
|
||||
|
||||
// Loads node page and check if Language field is hidden by default.
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$language_field = $this->xpath('//div[@id=:id]/div', [
|
||||
':id' => 'field-language-display',
|
||||
]);
|
||||
$this->assertTrue(empty($language_field), 'Language field value is not shown by default on node page.');
|
||||
|
||||
// Configures Language field formatter and check if it is saved.
|
||||
$edit = [
|
||||
'fields[langcode][type]' => 'language',
|
||||
'fields[langcode][region]' => 'content',
|
||||
];
|
||||
$this->drupalPostForm('admin/structure/types/manage/article/display', $edit, t('Save'));
|
||||
$this->drupalGet('admin/structure/types/manage/article/display');
|
||||
$this->assertOptionSelected('edit-fields-langcode-type', 'language', 'Language field has been set to visible.');
|
||||
|
||||
// Loads node page and check if Language field is shown.
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$language_field = $this->xpath('//div[@id=:id]/div', [
|
||||
':id' => 'field-language-display',
|
||||
]);
|
||||
$this->assertFalse(empty($language_field), 'Language field value is shown on node page.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Ensures that node types translation work correctly.
|
||||
*
|
||||
* Note that the child site is installed in French; therefore, when making
|
||||
* assertions on translated text it is important to provide a langcode. This
|
||||
* ensures the asserts pass regardless of the Drupal version.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeTypeTranslationTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = [
|
||||
'block',
|
||||
'config_translation',
|
||||
'field_ui',
|
||||
'node',
|
||||
];
|
||||
|
||||
/**
|
||||
* The default language code to use in this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $defaultLangcode = 'fr';
|
||||
|
||||
/**
|
||||
* Languages to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $additionalLangcodes = ['es'];
|
||||
|
||||
/**
|
||||
* Administrator user for tests.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$admin_permissions = [
|
||||
'administer content types',
|
||||
'administer node fields',
|
||||
'administer languages',
|
||||
'administer site configuration',
|
||||
'administer themes',
|
||||
'translate configuration',
|
||||
];
|
||||
|
||||
// Create and log in user.
|
||||
$this->adminUser = $this->drupalCreateUser($admin_permissions);
|
||||
|
||||
// Add languages.
|
||||
foreach ($this->additionalLangcodes as $langcode) {
|
||||
ConfigurableLanguage::createFromLangcode($langcode)->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Install Drupal in a language other than English for this test. This is not
|
||||
* needed to test the node type translation itself but acts as a regression
|
||||
* test.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2584603
|
||||
*/
|
||||
protected function installParameters() {
|
||||
$parameters = parent::installParameters();
|
||||
$parameters['parameters']['langcode'] = $this->defaultLangcode;
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the node type translation.
|
||||
*/
|
||||
public function testNodeTypeTranslation() {
|
||||
$type = Unicode::strtolower($this->randomMachineName(16));
|
||||
$name = $this->randomString();
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalCreateContentType(['type' => $type, 'name' => $name]);
|
||||
|
||||
// Translate the node type name.
|
||||
$langcode = $this->additionalLangcodes[0];
|
||||
$translated_name = $langcode . '-' . $name;
|
||||
$edit = [
|
||||
"translation[config_names][node.type.$type][name]" => $translated_name,
|
||||
];
|
||||
|
||||
// Edit the title label to avoid having an exception when we save the translation.
|
||||
$this->drupalPostForm("admin/structure/types/manage/$type/translate/$langcode/add", $edit, t('Save translation'));
|
||||
|
||||
// Check the name is translated without admin theme for editing.
|
||||
$this->drupalPostForm('admin/appearance', ['use_admin_theme' => '0'], t('Save configuration'));
|
||||
$this->drupalGet("$langcode/node/add/$type");
|
||||
// This is a Spanish page, so ensure the text asserted is translated in
|
||||
// Spanish and not French by adding the langcode option.
|
||||
$this->assertRaw(t('Create @name', ['@name' => $translated_name], ['langcode' => $langcode]));
|
||||
|
||||
// Check the name is translated with admin theme for editing.
|
||||
$this->drupalPostForm('admin/appearance', ['use_admin_theme' => '1'], t('Save configuration'));
|
||||
$this->drupalGet("$langcode/node/add/$type");
|
||||
// This is a Spanish page, so ensure the text asserted is translated in
|
||||
// Spanish and not French by adding the langcode option.
|
||||
$this->assertRaw(t('Create @name', ['@name' => $translated_name], ['langcode' => $langcode]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the node type title label translation.
|
||||
*/
|
||||
public function testNodeTypeTitleLabelTranslation() {
|
||||
$type = Unicode::strtolower($this->randomMachineName(16));
|
||||
$name = $this->randomString();
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalCreateContentType(['type' => $type, 'name' => $name]);
|
||||
$langcode = $this->additionalLangcodes[0];
|
||||
|
||||
// Edit the title label for it to be displayed on the translation form.
|
||||
$this->drupalPostForm("admin/structure/types/manage/$type", ['title_label' => 'Edited title'], t('Save content type'));
|
||||
|
||||
// Assert that the title label is displayed on the translation form with the right value.
|
||||
$this->drupalGet("admin/structure/types/manage/$type/translate/$langcode/add");
|
||||
$this->assertText('Edited title');
|
||||
|
||||
// Translate the title label.
|
||||
$this->drupalPostForm(NULL, ["translation[config_names][core.base_field_override.node.$type.title][label]" => 'Translated title'], t('Save translation'));
|
||||
|
||||
// Assert that the right title label is displayed on the node add form. The
|
||||
// translations are created in this test; therefore, the assertions do not
|
||||
// use t(). If t() were used then the correct langcodes would need to be
|
||||
// provided.
|
||||
$this->drupalGet("node/add/$type");
|
||||
$this->assertText('Edited title');
|
||||
$this->drupalGet("$langcode/node/add/$type");
|
||||
$this->assertText('Translated title');
|
||||
|
||||
// Add an e-mail field.
|
||||
$this->drupalPostForm("admin/structure/types/manage/$type/fields/add-field", ['new_storage_type' => 'email', 'label' => 'Email', 'field_name' => 'email'], 'Save and continue');
|
||||
$this->drupalPostForm(NULL, [], 'Save field settings');
|
||||
$this->drupalPostForm(NULL, [], 'Save settings');
|
||||
|
||||
$type = Unicode::strtolower($this->randomMachineName(16));
|
||||
$name = $this->randomString();
|
||||
$this->drupalCreateContentType(['type' => $type, 'name' => $name]);
|
||||
|
||||
// Set tabs.
|
||||
$this->drupalPlaceBlock('local_tasks_block', ['primary' => TRUE]);
|
||||
|
||||
// Change default language.
|
||||
$this->drupalPostForm('admin/config/regional/language', ['site_default_language' => 'es'], 'Save configuration');
|
||||
|
||||
// Try re-using the email field.
|
||||
$this->drupalGet("es/admin/structure/types/manage/$type/fields/add-field");
|
||||
$this->drupalPostForm(NULL, ['existing_storage_name' => 'field_email', 'existing_storage_label' => 'Email'], 'Save and continue');
|
||||
$this->assertResponse(200);
|
||||
$this->drupalGet("es/admin/structure/types/manage/$type/fields/node.$type.field_email/translate");
|
||||
$this->assertResponse(200);
|
||||
$this->assertText("The configuration objects have different language codes so they cannot be translated");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
|
||||
/**
|
||||
* Tests the node language extra field display.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeViewLanguageTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node', 'datetime', 'language'];
|
||||
|
||||
/**
|
||||
* Tests the language extra field display.
|
||||
*/
|
||||
public function testViewLanguage() {
|
||||
// Add Spanish language.
|
||||
ConfigurableLanguage::createFromLangcode('es')->save();
|
||||
|
||||
// Set language field visible.
|
||||
entity_get_display('node', 'page', 'full')
|
||||
->setComponent('langcode')
|
||||
->save();
|
||||
|
||||
// Create a node in Spanish.
|
||||
$node = $this->drupalCreateNode(['langcode' => 'es']);
|
||||
|
||||
$this->drupalGet($node->urlInfo());
|
||||
$this->assertText('Spanish', 'The language field is displayed properly.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
|
||||
/**
|
||||
* Tests the node/{node} page.
|
||||
*
|
||||
* @group node
|
||||
* @see \Drupal\node\Controller\NodeController
|
||||
*/
|
||||
class NodeViewTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Tests the html head links.
|
||||
*/
|
||||
public function testHtmlHeadLinks() {
|
||||
$node = $this->drupalCreateNode();
|
||||
|
||||
$this->drupalGet($node->urlInfo());
|
||||
|
||||
$result = $this->xpath('//link[@rel = "canonical"]');
|
||||
$this->assertEqual($result[0]->getAttribute('href'), $node->url());
|
||||
|
||||
// Link relations are checked for access for anonymous users.
|
||||
$result = $this->xpath('//link[@rel = "version-history"]');
|
||||
$this->assertFalse($result, 'Version history not present for anonymous users without access.');
|
||||
|
||||
$result = $this->xpath('//link[@rel = "edit-form"]');
|
||||
$this->assertFalse($result, 'Edit form not present for anonymous users without access.');
|
||||
|
||||
$this->drupalLogin($this->createUser(['access content']));
|
||||
$this->drupalGet($node->urlInfo());
|
||||
|
||||
$result = $this->xpath('//link[@rel = "canonical"]');
|
||||
$this->assertEqual($result[0]->getAttribute('href'), $node->url());
|
||||
|
||||
// Link relations are present regardless of access for authenticated users.
|
||||
$result = $this->xpath('//link[@rel = "version-history"]');
|
||||
$this->assertEqual($result[0]->getAttribute('href'), $node->url('version-history'));
|
||||
|
||||
$result = $this->xpath('//link[@rel = "edit-form"]');
|
||||
$this->assertEqual($result[0]->getAttribute('href'), $node->url('edit-form'));
|
||||
|
||||
// Give anonymous users access to edit the node. Do this through the UI to
|
||||
// ensure caches are handled properly.
|
||||
$this->drupalLogin($this->rootUser);
|
||||
$edit = [
|
||||
'anonymous[edit own ' . $node->bundle() . ' content]' => TRUE
|
||||
];
|
||||
$this->drupalPostForm('admin/people/permissions', $edit, 'Save permissions');
|
||||
$this->drupalLogout();
|
||||
|
||||
// Anonymous user's should now see the edit-form link but not the
|
||||
// version-history link.
|
||||
$this->drupalGet($node->urlInfo());
|
||||
$result = $this->xpath('//link[@rel = "canonical"]');
|
||||
$this->assertEqual($result[0]->getAttribute('href'), $node->url());
|
||||
|
||||
$result = $this->xpath('//link[@rel = "version-history"]');
|
||||
$this->assertFalse($result, 'Version history not present for anonymous users without access.');
|
||||
|
||||
$result = $this->xpath('//link[@rel = "edit-form"]');
|
||||
$this->assertEqual($result[0]->getAttribute('href'), $node->url('edit-form'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the Link header.
|
||||
*/
|
||||
public function testLinkHeader() {
|
||||
$node = $this->drupalCreateNode();
|
||||
|
||||
$expected = [
|
||||
'<' . Html::escape($node->url('canonical')) . '>; rel="canonical"',
|
||||
'<' . Html::escape($node->url('canonical'), ['alias' => TRUE]) . '>; rel="shortlink"',
|
||||
'<' . Html::escape($node->url('revision')) . '>; rel="revision"',
|
||||
];
|
||||
|
||||
$this->drupalGet($node->urlInfo());
|
||||
|
||||
$links = $this->drupalGetHeaders()['Link'];
|
||||
$this->assertEqual($links, $expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we store and retrieve multi-byte UTF-8 characters correctly.
|
||||
*/
|
||||
public function testMultiByteUtf8() {
|
||||
$title = '🐝';
|
||||
$this->assertTrue(mb_strlen($title, 'utf-8') < strlen($title), 'Title has multi-byte characters.');
|
||||
$node = $this->drupalCreateNode(['title' => $title]);
|
||||
$this->drupalGet($node->urlInfo());
|
||||
$result = $this->xpath('//span[contains(@class, "field--name-title")]');
|
||||
$this->assertEqual($result[0]->getText(), $title, 'The passed title was returned.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\node\Entity\Node;
|
||||
|
||||
/**
|
||||
* Create a node and test edit permissions.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class PageViewTest extends NodeTestBase {
|
||||
/**
|
||||
* Tests an anonymous and unpermissioned user attempting to edit the node.
|
||||
*/
|
||||
public function testPageView() {
|
||||
// Create a node to view.
|
||||
$node = $this->drupalCreateNode();
|
||||
$this->assertTrue(Node::load($node->id()), 'Node created.');
|
||||
|
||||
// Try to edit with anonymous user.
|
||||
$this->drupalGet("node/" . $node->id() . "/edit");
|
||||
$this->assertResponse(403);
|
||||
|
||||
// Create a user without permission to edit node.
|
||||
$web_user = $this->drupalCreateUser(['access content']);
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
// Attempt to access edit page.
|
||||
$this->drupalGet("node/" . $node->id() . "/edit");
|
||||
$this->assertResponse(403);
|
||||
|
||||
// Create user with permission to edit node.
|
||||
$web_user = $this->drupalCreateUser(['bypass node access']);
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
// Attempt to access edit page.
|
||||
$this->drupalGet("node/" . $node->id() . "/edit");
|
||||
$this->assertResponse(200);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional\Views;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
|
||||
/**
|
||||
* Tests if entity access is respected on a node bulk operations form.
|
||||
*
|
||||
* @group node
|
||||
* @see \Drupal\node\Plugin\views\field\BulkForm
|
||||
* @see \Drupal\node\Tests\NodeTestBase
|
||||
* @see \Drupal\node\Tests\NodeAccessBaseTableTest
|
||||
* @see \Drupal\node\Tests\Views\BulkFormTest
|
||||
*/
|
||||
class BulkFormAccessTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node_test_views', 'node_access_test'];
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_node_bulk_form'];
|
||||
|
||||
/**
|
||||
* The node access control handler.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityAccessControlHandlerInterface
|
||||
*/
|
||||
protected $accessHandler;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp($import_test_views = TRUE) {
|
||||
parent::setUp($import_test_views);
|
||||
|
||||
// Create Article node type.
|
||||
$this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']);
|
||||
|
||||
$this->accessHandler = \Drupal::entityManager()->getAccessControlHandler('node');
|
||||
|
||||
node_access_test_add_field(NodeType::load('article'));
|
||||
|
||||
// After enabling a node access module, the access table has to be rebuild.
|
||||
node_access_rebuild();
|
||||
|
||||
// Enable the private node feature of the node_access_test module.
|
||||
\Drupal::state()->set('node_access_test.private', TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if nodes that may not be edited, can not be edited in bulk.
|
||||
*/
|
||||
public function testNodeEditAccess() {
|
||||
// Create an account who will be the author of a private node.
|
||||
$author = $this->drupalCreateUser();
|
||||
// Create a private node (author may view, edit and delete, others may not).
|
||||
$node = $this->drupalCreateNode([
|
||||
'type' => 'article',
|
||||
'private' => [[
|
||||
'value' => TRUE,
|
||||
]],
|
||||
'uid' => $author->id(),
|
||||
]);
|
||||
// Create an account that may view the private node, but not edit it.
|
||||
$account = $this->drupalCreateUser(['node test view']);
|
||||
$this->drupalLogin($account);
|
||||
|
||||
// Ensure the node is published.
|
||||
$this->assertTrue($node->isPublished(), 'Node is initially published.');
|
||||
|
||||
// Ensure that the node can not be edited.
|
||||
$this->assertEqual(FALSE, $this->accessHandler->access($node, 'update', $account), 'The node may not be edited.');
|
||||
|
||||
// Test editing the node using the bulk form.
|
||||
$edit = [
|
||||
'node_bulk_form[0]' => TRUE,
|
||||
'action' => 'node_unpublish_action',
|
||||
];
|
||||
$this->drupalPostForm('test-node-bulk-form', $edit, t('Apply to selected items'));
|
||||
$this->assertRaw(SafeMarkup::format('No access to execute %action on the @entity_type_label %entity_label.', [
|
||||
'%action' => 'Unpublish content',
|
||||
'@entity_type_label' => 'Content',
|
||||
'%entity_label' => $node->label(),
|
||||
]));
|
||||
|
||||
// Re-load the node and check the status.
|
||||
$node = Node::load($node->id());
|
||||
$this->assertTrue($node->isPublished(), 'The node is still published.');
|
||||
|
||||
// Create an account that may view the private node, but can update the
|
||||
// status.
|
||||
$account = $this->drupalCreateUser(['administer nodes', 'node test view']);
|
||||
$this->drupalLogin($account);
|
||||
|
||||
// Ensure the node is published.
|
||||
$this->assertTrue($node->isPublished(), 'Node is initially published.');
|
||||
|
||||
// Ensure that the private node can not be edited.
|
||||
$this->assertEqual(FALSE, $node->access('update', $account), 'The node may not be edited.');
|
||||
$this->assertEqual(TRUE, $node->status->access('edit', $account), 'The node status can be edited.');
|
||||
|
||||
// Test editing the node using the bulk form.
|
||||
$edit = [
|
||||
'node_bulk_form[0]' => TRUE,
|
||||
'action' => 'node_unpublish_action',
|
||||
];
|
||||
$this->drupalPostForm('test-node-bulk-form', $edit, t('Apply to selected items'));
|
||||
// Test that the action message isn't shown.
|
||||
$this->assertNoRaw(SafeMarkup::format('%action was applied to 1 item.', [
|
||||
'%action' => 'Unpublish content',
|
||||
]));
|
||||
// Re-load the node and check the status.
|
||||
$node = Node::load($node->id());
|
||||
$this->assertTrue($node->isPublished(), 'The node is still published.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if nodes that may not be deleted, can not be deleted in bulk.
|
||||
*/
|
||||
public function testNodeDeleteAccess() {
|
||||
// Create an account who will be the author of a private node.
|
||||
$author = $this->drupalCreateUser();
|
||||
// Create a private node (author may view, edit and delete, others may not).
|
||||
$private_node = $this->drupalCreateNode([
|
||||
'type' => 'article',
|
||||
'private' => [[
|
||||
'value' => TRUE,
|
||||
]],
|
||||
'uid' => $author->id(),
|
||||
]);
|
||||
// Create an account that may view the private node, but not delete it.
|
||||
$account = $this->drupalCreateUser(['access content', 'administer nodes', 'delete own article content', 'node test view']);
|
||||
// Create a node that may be deleted too, to ensure the delete confirmation
|
||||
// page is shown later. In node_access_test.module, nodes may only be
|
||||
// deleted by the author.
|
||||
$own_node = $this->drupalCreateNode([
|
||||
'type' => 'article',
|
||||
'private' => [[
|
||||
'value' => TRUE,
|
||||
]],
|
||||
'uid' => $account->id(),
|
||||
]);
|
||||
$this->drupalLogin($account);
|
||||
|
||||
// Ensure that the private node can not be deleted.
|
||||
$this->assertEqual(FALSE, $this->accessHandler->access($private_node, 'delete', $account), 'The private node may not be deleted.');
|
||||
// Ensure that the public node may be deleted.
|
||||
$this->assertEqual(TRUE, $this->accessHandler->access($own_node, 'delete', $account), 'The own node may be deleted.');
|
||||
|
||||
// Try to delete the node using the bulk form.
|
||||
$edit = [
|
||||
'node_bulk_form[0]' => TRUE,
|
||||
'node_bulk_form[1]' => TRUE,
|
||||
'action' => 'node_delete_action',
|
||||
];
|
||||
$this->drupalPostForm('test-node-bulk-form', $edit, t('Apply to selected items'));
|
||||
$this->drupalPostForm(NULL, [], t('Delete'));
|
||||
// Ensure the private node still exists.
|
||||
$private_node = Node::load($private_node->id());
|
||||
$this->assertNotNull($private_node, 'The private node has not been deleted.');
|
||||
// Ensure the own node is deleted.
|
||||
$own_node = Node::load($own_node->id());
|
||||
$this->assertNull($own_node, 'The own node is deleted.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional\Views;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests a node bulk form.
|
||||
*
|
||||
* @group node
|
||||
* @see \Drupal\node\Plugin\views\field\BulkForm
|
||||
*/
|
||||
class BulkFormTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Modules to be enabled.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node_test_views', 'language'];
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_node_bulk_form'];
|
||||
|
||||
/**
|
||||
* The test nodes.
|
||||
*
|
||||
* @var \Drupal\node\NodeInterface[]
|
||||
*/
|
||||
protected $nodes;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp($import_test_views = TRUE) {
|
||||
parent::setUp($import_test_views);
|
||||
|
||||
ConfigurableLanguage::createFromLangcode('en-gb')->save();
|
||||
ConfigurableLanguage::createFromLangcode('it')->save();
|
||||
|
||||
// Create some test nodes.
|
||||
$this->nodes = [];
|
||||
$langcodes = ['en', 'en-gb', 'it'];
|
||||
for ($i = 1; $i <= 5; $i++) {
|
||||
$langcode = $langcodes[($i - 1) % 3];
|
||||
$values = [
|
||||
'title' => $this->randomMachineName() . ' [' . $i . ':' . $langcode . ']',
|
||||
'langcode' => $langcode,
|
||||
'promote' => FALSE,
|
||||
];
|
||||
$node = $this->drupalCreateNode($values);
|
||||
$this->pass(SafeMarkup::format('Node %title created with language %langcode.', ['%title' => $node->label(), '%langcode' => $node->language()->getId()]));
|
||||
$this->nodes[] = $node;
|
||||
}
|
||||
|
||||
// Create translations for all languages for some nodes.
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
$node = $this->nodes[$i];
|
||||
foreach ($langcodes as $langcode) {
|
||||
if (!$node->hasTranslation($langcode)) {
|
||||
$title = $this->randomMachineName() . ' [' . $node->id() . ':' . $langcode . ']';
|
||||
$translation = $node->addTranslation($langcode, ['title' => $title, 'promote' => FALSE]);
|
||||
$this->pass(SafeMarkup::format('Translation %title created with language %langcode.', ['%title' => $translation->label(), '%langcode' => $translation->language()->getId()]));
|
||||
}
|
||||
}
|
||||
$node->save();
|
||||
}
|
||||
|
||||
// Create a node with only one translation.
|
||||
$node = $this->nodes[2];
|
||||
$langcode = 'en';
|
||||
$title = $this->randomMachineName() . ' [' . $node->id() . ':' . $langcode . ']';
|
||||
$translation = $node->addTranslation($langcode, ['title' => $title]);
|
||||
$this->pass(SafeMarkup::format('Translation %title created with language %langcode.', ['%title' => $translation->label(), '%langcode' => $translation->language()->getId()]));
|
||||
$node->save();
|
||||
|
||||
// Check that all created translations are selected by the test view.
|
||||
$view = Views::getView('test_node_bulk_form');
|
||||
$view->execute();
|
||||
$this->assertEqual(count($view->result), 10, 'All created translations are selected.');
|
||||
|
||||
// Check the operations are accessible to the logged in user.
|
||||
$this->drupalLogin($this->drupalCreateUser(['administer nodes', 'access content overview', 'bypass node access']));
|
||||
$this->drupalGet('test-node-bulk-form');
|
||||
$elements = $this->xpath('//select[@id="edit-action"]//option');
|
||||
$this->assertIdentical(count($elements), 8, 'All node operations are found.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the node bulk form.
|
||||
*/
|
||||
public function testBulkForm() {
|
||||
// Unpublish a node using the bulk form.
|
||||
$node = reset($this->nodes);
|
||||
$this->assertTrue($node->isPublished(), 'Node is initially published');
|
||||
$this->assertTrue($node->getTranslation('en-gb')->isPublished(), 'Node translation is published');
|
||||
$this->assertTrue($node->getTranslation('it')->isPublished(), 'Node translation is published');
|
||||
$edit = [
|
||||
'node_bulk_form[0]' => TRUE,
|
||||
'action' => 'node_unpublish_action',
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
|
||||
$node = $this->loadNode($node->id());
|
||||
$this->assertFalse($node->isPublished(), 'Node has been unpublished');
|
||||
$this->assertTrue($node->getTranslation('en-gb')->isPublished(), 'Node translation has not been unpublished');
|
||||
$this->assertTrue($node->getTranslation('it')->isPublished(), 'Node translation has not been unpublished');
|
||||
|
||||
// Publish action.
|
||||
$edit = [
|
||||
'node_bulk_form[0]' => TRUE,
|
||||
'action' => 'node_publish_action',
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
|
||||
$node = $this->loadNode($node->id());
|
||||
$this->assertTrue($node->isPublished(), 'Node has been published again');
|
||||
|
||||
// Make sticky action.
|
||||
$this->assertFalse($node->isSticky(), 'Node is not sticky');
|
||||
$this->assertFalse($node->getTranslation('en-gb')->isSticky(), 'Node translation is not sticky');
|
||||
$this->assertFalse($node->getTranslation('it')->isSticky(), 'Node translation is not sticky');
|
||||
$edit = [
|
||||
'node_bulk_form[0]' => TRUE,
|
||||
'action' => 'node_make_sticky_action',
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
|
||||
$node = $this->loadNode($node->id());
|
||||
$this->assertTrue($node->isSticky(), 'Node has been made sticky');
|
||||
$this->assertFalse($node->getTranslation('en-gb')->isSticky(), 'Node translation has not been made sticky');
|
||||
$this->assertFalse($node->getTranslation('it')->isSticky(), 'Node translation has not been made sticky');
|
||||
|
||||
// Make unsticky action.
|
||||
$edit = [
|
||||
'node_bulk_form[0]' => TRUE,
|
||||
'action' => 'node_make_unsticky_action',
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
|
||||
$node = $this->loadNode($node->id());
|
||||
$this->assertFalse($node->isSticky(), 'Node is not sticky anymore');
|
||||
|
||||
// Promote to front page.
|
||||
$this->assertFalse($node->isPromoted(), 'Node is not promoted to the front page');
|
||||
$this->assertFalse($node->getTranslation('en-gb')->isPromoted(), 'Node translation is not promoted to the front page');
|
||||
$this->assertFalse($node->getTranslation('it')->isPromoted(), 'Node translation is not promoted to the front page');
|
||||
$edit = [
|
||||
'node_bulk_form[0]' => TRUE,
|
||||
'action' => 'node_promote_action',
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
|
||||
$node = $this->loadNode($node->id());
|
||||
$this->assertTrue($node->isPromoted(), 'Node has been promoted to the front page');
|
||||
$this->assertFalse($node->getTranslation('en-gb')->isPromoted(), 'Node translation has not been promoted to the front page');
|
||||
$this->assertFalse($node->getTranslation('it')->isPromoted(), 'Node translation has not been promoted to the front page');
|
||||
|
||||
// Demote from front page.
|
||||
$edit = [
|
||||
'node_bulk_form[0]' => TRUE,
|
||||
'action' => 'node_unpromote_action',
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
|
||||
$node = $this->loadNode($node->id());
|
||||
$this->assertFalse($node->isPromoted(), 'Node has been demoted');
|
||||
|
||||
// Select a bunch of translated and untranslated nodes and check that
|
||||
// operations are always applied to individual translations.
|
||||
$edit = [
|
||||
// Original and all translations.
|
||||
'node_bulk_form[0]' => TRUE, // Node 1, English, original.
|
||||
'node_bulk_form[1]' => TRUE, // Node 1, British English.
|
||||
'node_bulk_form[2]' => TRUE, // Node 1, Italian.
|
||||
// Original and only one translation.
|
||||
'node_bulk_form[3]' => TRUE, // Node 2, English.
|
||||
'node_bulk_form[4]' => TRUE, // Node 2, British English, original.
|
||||
'node_bulk_form[5]' => FALSE, // Node 2, Italian.
|
||||
// Only a single translation.
|
||||
'node_bulk_form[6]' => TRUE, // Node 3, English.
|
||||
'node_bulk_form[7]' => FALSE, // Node 3, Italian, original.
|
||||
// Only a single untranslated node.
|
||||
'node_bulk_form[8]' => TRUE, // Node 4, English, untranslated.
|
||||
'node_bulk_form[9]' => FALSE, // Node 5, British English, untranslated.
|
||||
'action' => 'node_unpublish_action',
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
|
||||
$node = $this->loadNode(1);
|
||||
$this->assertFalse($node->getTranslation('en')->isPublished(), '1: English translation has been unpublished');
|
||||
$this->assertFalse($node->getTranslation('en-gb')->isPublished(), '1: British English translation has been unpublished');
|
||||
$this->assertFalse($node->getTranslation('it')->isPublished(), '1: Italian translation has been unpublished');
|
||||
$node = $this->loadNode(2);
|
||||
$this->assertFalse($node->getTranslation('en')->isPublished(), '2: English translation has been unpublished');
|
||||
$this->assertFalse($node->getTranslation('en-gb')->isPublished(), '2: British English translation has been unpublished');
|
||||
$this->assertTrue($node->getTranslation('it')->isPublished(), '2: Italian translation has not been unpublished');
|
||||
$node = $this->loadNode(3);
|
||||
$this->assertFalse($node->getTranslation('en')->isPublished(), '3: English translation has been unpublished');
|
||||
$this->assertTrue($node->getTranslation('it')->isPublished(), '3: Italian translation has not been unpublished');
|
||||
$node = $this->loadNode(4);
|
||||
$this->assertFalse($node->isPublished(), '4: Node has been unpublished');
|
||||
$node = $this->loadNode(5);
|
||||
$this->assertTrue($node->isPublished(), '5: Node has not been unpublished');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test multiple deletion.
|
||||
*/
|
||||
public function testBulkDeletion() {
|
||||
// Select a bunch of translated and untranslated nodes and check that
|
||||
// nodes and individual translations are properly deleted.
|
||||
$edit = [
|
||||
// Original and all translations.
|
||||
'node_bulk_form[0]' => TRUE, // Node 1, English, original.
|
||||
'node_bulk_form[1]' => TRUE, // Node 1, British English.
|
||||
'node_bulk_form[2]' => TRUE, // Node 1, Italian.
|
||||
// Original and only one translation.
|
||||
'node_bulk_form[3]' => TRUE, // Node 2, English.
|
||||
'node_bulk_form[4]' => TRUE, // Node 2, British English, original.
|
||||
'node_bulk_form[5]' => FALSE, // Node 2, Italian.
|
||||
// Only a single translation.
|
||||
'node_bulk_form[6]' => TRUE, // Node 3, English.
|
||||
'node_bulk_form[7]' => FALSE, // Node 3, Italian, original.
|
||||
// Only a single untranslated node.
|
||||
'node_bulk_form[8]' => TRUE, // Node 4, English, untranslated.
|
||||
'node_bulk_form[9]' => FALSE, // Node 5, British English, untranslated.
|
||||
'action' => 'node_delete_action',
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
|
||||
|
||||
$label = $this->loadNode(1)->label();
|
||||
$this->assertText("$label (Original translation) - The following content translations will be deleted:");
|
||||
$label = $this->loadNode(2)->label();
|
||||
$this->assertText("$label (Original translation) - The following content translations will be deleted:");
|
||||
$label = $this->loadNode(3)->getTranslation('en')->label();
|
||||
$this->assertText($label);
|
||||
$this->assertNoText("$label (Original translation) - The following content translations will be deleted:");
|
||||
$label = $this->loadNode(4)->label();
|
||||
$this->assertText($label);
|
||||
$this->assertNoText("$label (Original translation) - The following content translations will be deleted:");
|
||||
|
||||
$this->drupalPostForm(NULL, [], t('Delete'));
|
||||
|
||||
$node = $this->loadNode(1);
|
||||
$this->assertNull($node, '1: Node has been deleted');
|
||||
$node = $this->loadNode(2);
|
||||
$this->assertNull($node, '2: Node has been deleted');
|
||||
$node = $this->loadNode(3);
|
||||
$result = count($node->getTranslationLanguages()) && $node->language()->getId() == 'it';
|
||||
$this->assertTrue($result, '3: English translation has been deleted');
|
||||
$node = $this->loadNode(4);
|
||||
$this->assertNull($node, '4: Node has been deleted');
|
||||
$node = $this->loadNode(5);
|
||||
$this->assertTrue($node, '5: Node has not been deleted');
|
||||
|
||||
$this->assertText('Deleted 8 posts.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the specified node from the storage.
|
||||
*
|
||||
* @param int $id
|
||||
* The node identifier.
|
||||
*
|
||||
* @return \Drupal\node\NodeInterface
|
||||
* The loaded node.
|
||||
*/
|
||||
protected function loadNode($id) {
|
||||
/** @var \Drupal\node\NodeStorage $storage */
|
||||
$storage = $this->container->get('entity.manager')->getStorage('node');
|
||||
$storage->resetCache([$id]);
|
||||
return $storage->load($id);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional\Views;
|
||||
|
||||
use Drupal\node\Entity\NodeType;
|
||||
|
||||
/**
|
||||
* Tests the node_access filter handler.
|
||||
*
|
||||
* @group node
|
||||
* @see \Drupal\node\Plugin\views\filter\Access
|
||||
*/
|
||||
class FilterNodeAccessTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* An array of users.
|
||||
*
|
||||
* @var \Drupal\user\Entity\User[]
|
||||
*/
|
||||
protected $users;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['node_access_test'];
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_filter_node_access'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp($import_test_views = TRUE) {
|
||||
parent::setUp($import_test_views);
|
||||
|
||||
$this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']);
|
||||
|
||||
node_access_test_add_field(NodeType::load('article'));
|
||||
|
||||
node_access_rebuild();
|
||||
\Drupal::state()->set('node_access_test.private', TRUE);
|
||||
|
||||
$num_simple_users = 2;
|
||||
$this->users = [];
|
||||
|
||||
for ($i = 0; $i < $num_simple_users; $i++) {
|
||||
$this->users[$i] = $this->drupalCreateUser(['access content', 'create article content']);
|
||||
}
|
||||
foreach ($this->users as $web_user) {
|
||||
$this->drupalLogin($web_user);
|
||||
foreach ([0 => 'Public', 1 => 'Private'] as $is_private => $type) {
|
||||
$settings = [
|
||||
'body' => [[
|
||||
'value' => $type . ' node',
|
||||
'format' => filter_default_format(),
|
||||
]],
|
||||
'title' => t('@private_public Article created by @user', ['@private_public' => $type, '@user' => $web_user->getUsername()]),
|
||||
'type' => 'article',
|
||||
'uid' => $web_user->id(),
|
||||
'private' => (bool) $is_private,
|
||||
];
|
||||
|
||||
$node = $this->drupalCreateNode($settings);
|
||||
$this->assertEqual($is_private, (int) $node->private->value, 'The private status of the node was properly set in the node_access_test table.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the node access filter.
|
||||
*/
|
||||
public function testFilterNodeAccess() {
|
||||
$this->drupalLogin($this->users[0]);
|
||||
$this->drupalGet('test_filter_node_access');
|
||||
// Test that the private node of the current user is shown.
|
||||
$this->assertText('Private Article created by ' . $this->users[0]->getUsername());
|
||||
// Test that the private node of the other use isn't shown.
|
||||
$this->assertNoText('Private Article created by ' . $this->users[1]->getUsername());
|
||||
// Test that both public nodes are shown.
|
||||
$this->assertText('Public Article created by ' . $this->users[0]->getUsername());
|
||||
$this->assertText('Public Article created by ' . $this->users[1]->getUsername());
|
||||
|
||||
// Switch users and test the other private node is shown.
|
||||
$this->drupalLogin($this->users[1]);
|
||||
$this->drupalGet('test_filter_node_access');
|
||||
// Test that the private node of the current user is shown.
|
||||
$this->assertText('Private Article created by ' . $this->users[1]->getUsername());
|
||||
// Test that the private node of the other use isn't shown.
|
||||
$this->assertNoText('Private Article created by ' . $this->users[0]->getUsername());
|
||||
|
||||
// Test that a user with administer nodes permission can't see all nodes.
|
||||
$administer_nodes_user = $this->drupalCreateUser(['access content', 'administer nodes']);
|
||||
$this->drupalLogin($administer_nodes_user);
|
||||
$this->drupalGet('test_filter_node_access');
|
||||
$this->assertNoText('Private Article created by ' . $this->users[0]->getUsername());
|
||||
$this->assertNoText('Private Article created by ' . $this->users[1]->getUsername());
|
||||
$this->assertText('Public Article created by ' . $this->users[0]->getUsername());
|
||||
$this->assertText('Public Article created by ' . $this->users[1]->getUsername());
|
||||
|
||||
// Test that a user with bypass node access can see all nodes.
|
||||
$bypass_access_user = $this->drupalCreateUser(['access content', 'bypass node access']);
|
||||
$this->drupalLogin($bypass_access_user);
|
||||
$this->drupalGet('test_filter_node_access');
|
||||
$this->assertText('Private Article created by ' . $this->users[0]->getUsername());
|
||||
$this->assertText('Private Article created by ' . $this->users[1]->getUsername());
|
||||
$this->assertText('Public Article created by ' . $this->users[0]->getUsername());
|
||||
$this->assertText('Public Article created by ' . $this->users[1]->getUsername());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional\Views;
|
||||
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests the node_uid_revision handler.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class FilterUidRevisionTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_filter_node_uid_revision'];
|
||||
|
||||
/**
|
||||
* Tests the node_uid_revision filter.
|
||||
*/
|
||||
public function testFilter() {
|
||||
$author = $this->drupalCreateUser();
|
||||
$no_author = $this->drupalCreateUser();
|
||||
|
||||
$expected_result = [];
|
||||
// Create one node, with the author as the node author.
|
||||
$node = $this->drupalCreateNode(['uid' => $author->id()]);
|
||||
$expected_result[] = ['nid' => $node->id()];
|
||||
// Create one node of which an additional revision author will be the
|
||||
// author.
|
||||
$node = $this->drupalCreateNode(['revision_uid' => $no_author->id()]);
|
||||
$expected_result[] = ['nid' => $node->id()];
|
||||
$revision = clone $node;
|
||||
// Force to add a new revision.
|
||||
$revision->set('vid', NULL);
|
||||
$revision->set('revision_uid', $author->id());
|
||||
$revision->save();
|
||||
|
||||
// Create one node on which the author has neither authorship of revisions
|
||||
// or the main node.
|
||||
$this->drupalCreateNode(['uid' => $no_author->id()]);
|
||||
|
||||
$view = Views::getView('test_filter_node_uid_revision');
|
||||
$view->initHandlers();
|
||||
$view->filter['uid_revision']->value = [$author->id()];
|
||||
|
||||
$this->executeView($view);
|
||||
$this->assertIdenticalResultset($view, $expected_result, ['nid' => 'nid'], 'Make sure that the view only returns nodes which match either the node or the revision author.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,374 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional\Views;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\Tests\views\Functional\ViewTestBase;
|
||||
use Drupal\views\Tests\AssertViewsCacheTagsTrait;
|
||||
use Drupal\views\ViewExecutable;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests the default frontpage provided by views.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class FrontPageTest extends ViewTestBase {
|
||||
|
||||
use AssertViewsCacheTagsTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $dumpHeaders = TRUE;
|
||||
|
||||
/**
|
||||
* The entity storage for nodes.
|
||||
*
|
||||
* @var \Drupal\node\NodeStorage
|
||||
*/
|
||||
protected $nodeStorage;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node', 'contextual'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp($import_test_views = TRUE) {
|
||||
parent::setUp($import_test_views);
|
||||
|
||||
$this->nodeStorage = $this->container->get('entity.manager')
|
||||
->getStorage('node');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the frontpage.
|
||||
*/
|
||||
public function testFrontPage() {
|
||||
$site_name = $this->randomMachineName();
|
||||
$this->config('system.site')
|
||||
->set('name', $site_name)
|
||||
->save();
|
||||
|
||||
$view = Views::getView('frontpage');
|
||||
|
||||
// Tests \Drupal\node\Plugin\views\row\RssPluginBase::calculateDependencies().
|
||||
$expected = [
|
||||
'config' => [
|
||||
'core.entity_view_mode.node.rss',
|
||||
'core.entity_view_mode.node.teaser',
|
||||
],
|
||||
'module' => [
|
||||
'node',
|
||||
'user',
|
||||
],
|
||||
];
|
||||
$this->assertIdentical($expected, $view->getDependencies());
|
||||
|
||||
$view->setDisplay('page_1');
|
||||
$this->executeView($view);
|
||||
$view->preview();
|
||||
|
||||
$this->assertEqual($view->getTitle(), format_string('Welcome to @site_name', ['@site_name' => $site_name]), 'The welcome title is used for the empty view.');
|
||||
$view->destroy();
|
||||
|
||||
// Create some nodes on the frontpage view. Add more than 10 nodes in order
|
||||
// to enable paging.
|
||||
$expected = [];
|
||||
for ($i = 0; $i < 20; $i++) {
|
||||
$values = [];
|
||||
$values['type'] = 'article';
|
||||
$values['title'] = $this->randomMachineName();
|
||||
$values['promote'] = TRUE;
|
||||
$values['status'] = TRUE;
|
||||
// Test descending sort order.
|
||||
$values['created'] = REQUEST_TIME - $i;
|
||||
// Test the sticky order.
|
||||
if ($i == 5) {
|
||||
$values['sticky'] = TRUE;
|
||||
$node = $this->nodeStorage->create($values);
|
||||
$node->save();
|
||||
// Put the sticky on at the front.
|
||||
array_unshift($expected, ['nid' => $node->id()]);
|
||||
}
|
||||
else {
|
||||
$values['sticky'] = FALSE;
|
||||
$node = $this->nodeStorage->create($values);
|
||||
$node->save();
|
||||
array_push($expected, ['nid' => $node->id()]);
|
||||
}
|
||||
}
|
||||
|
||||
// Create some nodes which aren't on the frontpage, either because they
|
||||
// aren't promoted or because they aren't published.
|
||||
$not_expected_nids = [];
|
||||
|
||||
$values = [];
|
||||
$values['type'] = 'article';
|
||||
$values['title'] = $this->randomMachineName();
|
||||
$values['status'] = TRUE;
|
||||
$values['promote'] = FALSE;
|
||||
$node = $this->nodeStorage->create($values);
|
||||
$node->save();
|
||||
$not_expected_nids[] = $node->id();
|
||||
|
||||
$values['promote'] = TRUE;
|
||||
$values['status'] = FALSE;
|
||||
$values['title'] = $this->randomMachineName();
|
||||
$node = $this->nodeStorage->create($values);
|
||||
$node->save();
|
||||
$not_expected_nids[] = $node->id();
|
||||
|
||||
$values['promote'] = TRUE;
|
||||
$values['sticky'] = TRUE;
|
||||
$values['status'] = FALSE;
|
||||
$values['title'] = $this->randomMachineName();
|
||||
$node = $this->nodeStorage->create($values);
|
||||
$node->save();
|
||||
$not_expected_nids[] = $node->id();
|
||||
|
||||
$column_map = ['nid' => 'nid'];
|
||||
|
||||
$view->setDisplay('page_1');
|
||||
$this->executeView($view);
|
||||
$this->assertIdenticalResultset($view, array_slice($expected, 0, 10), $column_map, 'Ensure that the right nodes are displayed on the frontpage.');
|
||||
$this->assertNotInResultSet($view, $not_expected_nids, 'Ensure no unexpected node is in the result.');
|
||||
$view->destroy();
|
||||
|
||||
$view->setDisplay('page_1');
|
||||
$view->setCurrentPage(1);
|
||||
$this->executeView($view);
|
||||
$this->assertIdenticalResultset($view, array_slice($expected, 10, 10), $column_map, 'Ensure that the right nodes are displayed on second page of the frontpage.');
|
||||
$this->assertNotInResultSet($view, $not_expected_nids, 'Ensure no unexpected node is in the result.');
|
||||
$view->destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that an amount of nids aren't in the result.
|
||||
*
|
||||
* @param \Drupal\views\ViewExecutable $view
|
||||
* An executed View.
|
||||
* @param array $not_expected_nids
|
||||
* An array of nids which should not be part of the resultset.
|
||||
* @param string $message
|
||||
* (optional) A custom message to display with the assertion.
|
||||
*/
|
||||
protected function assertNotInResultSet(ViewExecutable $view, array $not_expected_nids, $message = '') {
|
||||
$found_nids = array_filter($view->result, function ($row) use ($not_expected_nids) {
|
||||
return in_array($row->nid, $not_expected_nids);
|
||||
});
|
||||
$this->assertFalse($found_nids, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the frontpage when logged in as admin.
|
||||
*/
|
||||
public function testAdminFrontPage() {
|
||||
// When a user with sufficient permissions is logged in, views_ui adds
|
||||
// contextual links to the homepage view. This verifies there are no errors.
|
||||
\Drupal::service('module_installer')->install(['views_ui']);
|
||||
// Log in root user with sufficient permissions.
|
||||
$this->drupalLogin($this->rootUser);
|
||||
// Test frontpage view.
|
||||
$this->drupalGet('node');
|
||||
$this->assertResponse(200);
|
||||
// Check that the frontpage view was rendered.
|
||||
$this->assertPattern('/class=".+view-frontpage/', 'Frontpage view was rendered');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the cache tags when using the "none" cache plugin.
|
||||
*/
|
||||
public function testCacheTagsWithCachePluginNone() {
|
||||
$this->enablePageCaching();
|
||||
$this->doTestFrontPageViewCacheTags(FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the cache tags when using the "tag" cache plugin.
|
||||
*/
|
||||
public function testCacheTagsWithCachePluginTag() {
|
||||
$this->enablePageCaching();
|
||||
|
||||
$view = Views::getView('frontpage');
|
||||
$view->setDisplay('page_1');
|
||||
$view->display_handler->overrideOption('cache', [
|
||||
'type' => 'tag',
|
||||
]);
|
||||
$view->save();
|
||||
|
||||
$this->doTestFrontPageViewCacheTags(TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the cache tags when using the "time" cache plugin.
|
||||
*/
|
||||
public function testCacheTagsWithCachePluginTime() {
|
||||
$this->enablePageCaching();
|
||||
|
||||
$view = Views::getView('frontpage');
|
||||
$view->setDisplay('page_1');
|
||||
$view->display_handler->overrideOption('cache', [
|
||||
'type' => 'time',
|
||||
'options' => [
|
||||
'results_lifespan' => 3600,
|
||||
'output_lifespan' => 3600,
|
||||
],
|
||||
]);
|
||||
$view->save();
|
||||
|
||||
$this->doTestFrontPageViewCacheTags(TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the cache tags on the front page.
|
||||
*
|
||||
* @param bool $do_assert_views_caches
|
||||
* Whether to check Views' result & output caches.
|
||||
*/
|
||||
protected function doTestFrontPageViewCacheTags($do_assert_views_caches) {
|
||||
$view = Views::getView('frontpage');
|
||||
$view->setDisplay('page_1');
|
||||
|
||||
$cache_contexts = [
|
||||
// Cache contexts associated with the view.
|
||||
'user.node_grants:view',
|
||||
'languages:' . LanguageInterface::TYPE_INTERFACE,
|
||||
// Cache contexts associated with the route's access checking.
|
||||
'user.permissions',
|
||||
// Default cache contexts of the renderer.
|
||||
'theme',
|
||||
'url.query_args',
|
||||
// Attached feed.
|
||||
'url.site',
|
||||
];
|
||||
|
||||
$cache_context_tags = \Drupal::service('cache_contexts_manager')->convertTokensToKeys($cache_contexts)->getCacheTags();
|
||||
|
||||
// Test before there are any nodes.
|
||||
$empty_node_listing_cache_tags = [
|
||||
'config:views.view.frontpage',
|
||||
'node_list',
|
||||
];
|
||||
|
||||
$render_cache_tags = Cache::mergeTags($empty_node_listing_cache_tags, $cache_context_tags);
|
||||
$render_cache_tags = Cache::mergeTags($render_cache_tags, ['config:system.site']);
|
||||
$this->assertViewsCacheTags(
|
||||
$view,
|
||||
$empty_node_listing_cache_tags,
|
||||
$do_assert_views_caches,
|
||||
$render_cache_tags
|
||||
);
|
||||
$expected_tags = Cache::mergeTags($empty_node_listing_cache_tags, $cache_context_tags);
|
||||
$expected_tags = Cache::mergeTags($expected_tags, ['http_response', 'rendered', 'config:user.role.anonymous', 'config:system.site']);
|
||||
$this->assertPageCacheContextsAndTags(
|
||||
Url::fromRoute('view.frontpage.page_1'),
|
||||
$cache_contexts,
|
||||
$expected_tags
|
||||
);
|
||||
|
||||
// Create some nodes on the frontpage view. Add more than 10 nodes in order
|
||||
// to enable paging.
|
||||
$this->drupalCreateContentType(['type' => 'article']);
|
||||
for ($i = 0; $i < 15; $i++) {
|
||||
$node = Node::create([
|
||||
'body' => [
|
||||
[
|
||||
'value' => $this->randomMachineName(32),
|
||||
'format' => filter_default_format(),
|
||||
]
|
||||
],
|
||||
'type' => 'article',
|
||||
'created' => $i,
|
||||
'title' => $this->randomMachineName(8),
|
||||
'nid' => $i + 1,
|
||||
]);
|
||||
$node->enforceIsNew(TRUE);
|
||||
$node->save();
|
||||
}
|
||||
$cache_contexts = Cache::mergeContexts($cache_contexts, [
|
||||
'timezone',
|
||||
]);
|
||||
|
||||
$this->pass('First page');
|
||||
// First page.
|
||||
$first_page_result_cache_tags = [
|
||||
'config:views.view.frontpage',
|
||||
'node_list',
|
||||
'node:6',
|
||||
'node:7',
|
||||
'node:8',
|
||||
'node:9',
|
||||
'node:10',
|
||||
'node:11',
|
||||
'node:12',
|
||||
'node:13',
|
||||
'node:14',
|
||||
'node:15',
|
||||
];
|
||||
$cache_context_tags = \Drupal::service('cache_contexts_manager')->convertTokensToKeys($cache_contexts)->getCacheTags();
|
||||
$first_page_output_cache_tags = Cache::mergeTags($first_page_result_cache_tags, $cache_context_tags);
|
||||
$first_page_output_cache_tags = Cache::mergeTags($first_page_output_cache_tags, [
|
||||
'config:filter.format.plain_text',
|
||||
'node_view',
|
||||
'user_view',
|
||||
'user:0',
|
||||
]
|
||||
);
|
||||
$view->setDisplay('page_1');
|
||||
$view->setCurrentPage(0);
|
||||
$this->assertViewsCacheTags(
|
||||
$view,
|
||||
$first_page_result_cache_tags,
|
||||
$do_assert_views_caches,
|
||||
$first_page_output_cache_tags
|
||||
);
|
||||
$this->assertPageCacheContextsAndTags(
|
||||
Url::fromRoute('view.frontpage.page_1'),
|
||||
$cache_contexts,
|
||||
Cache::mergeTags($first_page_output_cache_tags, ['http_response', 'rendered', 'config:user.role.anonymous'])
|
||||
);
|
||||
|
||||
// Second page.
|
||||
$this->pass('Second page');
|
||||
$this->assertPageCacheContextsAndTags(Url::fromRoute('view.frontpage.page_1', [], ['query' => ['page' => 1]]), $cache_contexts, [
|
||||
// The cache tags for the listed nodes.
|
||||
'node:1',
|
||||
'node:2',
|
||||
'node:3',
|
||||
'node:4',
|
||||
'node:5',
|
||||
// The rest.
|
||||
'config:filter.format.plain_text',
|
||||
'config:views.view.frontpage',
|
||||
'node_list',
|
||||
'node_view',
|
||||
'user_view',
|
||||
'user:0',
|
||||
'http_response',
|
||||
'rendered',
|
||||
// FinishResponseSubscriber adds this cache tag to responses that have the
|
||||
// 'user.permissions' cache context for anonymous users.
|
||||
'config:user.role.anonymous',
|
||||
]);
|
||||
|
||||
// Let's update a node title on the first page and ensure that the page
|
||||
// cache entry invalidates.
|
||||
$node = Node::load(10);
|
||||
$title = $node->getTitle() . 'a';
|
||||
$node->setTitle($title);
|
||||
$node->save();
|
||||
|
||||
$this->drupalGet(Url::fromRoute('view.frontpage.page_1'));
|
||||
$this->assertText($title);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional\Views;
|
||||
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
|
||||
/**
|
||||
* Tests node field filters with translations.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeFieldFilterTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['language'];
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_field_filters'];
|
||||
|
||||
/**
|
||||
* List of node titles by language.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $nodeTitles = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp($import_test_views = TRUE) {
|
||||
parent::setUp($import_test_views);
|
||||
|
||||
// Create Page content type.
|
||||
if ($this->profile != 'standard') {
|
||||
$this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']);
|
||||
}
|
||||
|
||||
// Add two new languages.
|
||||
ConfigurableLanguage::createFromLangcode('fr')->save();
|
||||
ConfigurableLanguage::createFromLangcode('es')->save();
|
||||
|
||||
// Set up node titles.
|
||||
$this->nodeTitles = [
|
||||
'en' => 'Food in Paris',
|
||||
'es' => 'Comida en Paris',
|
||||
'fr' => 'Nouriture en Paris',
|
||||
];
|
||||
|
||||
// Create node with translations.
|
||||
$node = $this->drupalCreateNode(['title' => $this->nodeTitles['en'], 'langcode' => 'en', 'type' => 'page', 'body' => [['value' => $this->nodeTitles['en']]]]);
|
||||
foreach (['es', 'fr'] as $langcode) {
|
||||
$translation = $node->addTranslation($langcode, ['title' => $this->nodeTitles[$langcode]]);
|
||||
$translation->body->value = $this->nodeTitles[$langcode];
|
||||
}
|
||||
$node->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests body and title filters.
|
||||
*/
|
||||
public function testFilters() {
|
||||
// Test the title filter page, which filters for title contains 'Comida'.
|
||||
// Should show just the Spanish translation, once.
|
||||
$this->assertPageCounts('test-title-filter', ['es' => 1, 'fr' => 0, 'en' => 0], 'Comida title filter');
|
||||
|
||||
// Test the body filter page, which filters for body contains 'Comida'.
|
||||
// Should show just the Spanish translation, once.
|
||||
$this->assertPageCounts('test-body-filter', ['es' => 1, 'fr' => 0, 'en' => 0], 'Comida body filter');
|
||||
|
||||
// Test the title Paris filter page, which filters for title contains
|
||||
// 'Paris'. Should show each translation once.
|
||||
$this->assertPageCounts('test-title-paris', ['es' => 1, 'fr' => 1, 'en' => 1], 'Paris title filter');
|
||||
|
||||
// Test the body Paris filter page, which filters for body contains
|
||||
// 'Paris'. Should show each translation once.
|
||||
$this->assertPageCounts('test-body-paris', ['es' => 1, 'fr' => 1, 'en' => 1], 'Paris body filter');
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given node translation counts are correct.
|
||||
*
|
||||
* @param string $path
|
||||
* Path of the page to test.
|
||||
* @param array $counts
|
||||
* Array whose keys are languages, and values are the number of times
|
||||
* that translation should be shown on the given page.
|
||||
* @param string $message
|
||||
* Message suffix to display.
|
||||
*/
|
||||
protected function assertPageCounts($path, $counts, $message) {
|
||||
// Disable read more links.
|
||||
entity_get_display('node', 'page', 'teaser')->removeComponent('links')->save();
|
||||
|
||||
// Get the text of the page.
|
||||
$this->drupalGet($path);
|
||||
$text = $this->getTextContent();
|
||||
|
||||
// Check the counts. Note that the title and body are both shown on the
|
||||
// page, and they are the same. So the title/body string should appear on
|
||||
// the page twice as many times as the input count.
|
||||
foreach ($counts as $langcode => $count) {
|
||||
$this->assertEqual(substr_count($text, $this->nodeTitles[$langcode]), 2 * $count, 'Translation ' . $langcode . ' has count ' . $count . ' with ' . $message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional\Views;
|
||||
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
|
||||
/**
|
||||
* Tests replacement of Views tokens supplied by the Node module.
|
||||
*
|
||||
* @group node
|
||||
* @see \Drupal\node\Tests\NodeTokenReplaceTest
|
||||
*/
|
||||
class NodeFieldTokensTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_node_tokens'];
|
||||
|
||||
/**
|
||||
* Tests token replacement for Views tokens supplied by the Node module.
|
||||
*/
|
||||
public function testViewsTokenReplacement() {
|
||||
// Create the Article content type with a standard body field.
|
||||
/* @var $node_type \Drupal\node\NodeTypeInterface */
|
||||
$node_type = NodeType::create(['type' => 'article', 'name' => 'Article']);
|
||||
$node_type->save();
|
||||
node_add_body_field($node_type);
|
||||
|
||||
// Create a user and a node.
|
||||
$account = $this->createUser();
|
||||
$body = $this->randomMachineName(32);
|
||||
$summary = $this->randomMachineName(16);
|
||||
|
||||
/** @var $node \Drupal\node\NodeInterface */
|
||||
$node = Node::create([
|
||||
'type' => 'article',
|
||||
'tnid' => 0,
|
||||
'uid' => $account->id(),
|
||||
'title' => 'Testing Views tokens',
|
||||
'body' => [['value' => $body, 'summary' => $summary, 'format' => 'plain_text']],
|
||||
]);
|
||||
$node->save();
|
||||
|
||||
$this->drupalGet('test_node_tokens');
|
||||
|
||||
// Body: {{ body }}<br />
|
||||
$this->assertRaw("Body: <p>$body</p>");
|
||||
|
||||
// Raw value: {{ body__value }}<br />
|
||||
$this->assertRaw("Raw value: $body");
|
||||
|
||||
// Raw summary: {{ body__summary }}<br />
|
||||
$this->assertRaw("Raw summary: $summary");
|
||||
|
||||
// Raw format: {{ body__format }}<br />
|
||||
$this->assertRaw("Raw format: plain_text");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional\Views;
|
||||
|
||||
/**
|
||||
* Tests the node integration into views.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeIntegrationTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_node_view'];
|
||||
|
||||
/**
|
||||
* Tests basic node view with a node type argument.
|
||||
*/
|
||||
public function testNodeViewTypeArgument() {
|
||||
// Create two content types with three nodes each.
|
||||
$types = [];
|
||||
$all_nids = [];
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
$type = $this->drupalCreateContentType(['name' => '<em>' . $this->randomMachineName() . '</em>']);
|
||||
$types[] = $type;
|
||||
|
||||
for ($j = 0; $j < 5; $j++) {
|
||||
// Ensure the right order of the nodes.
|
||||
$node = $this->drupalCreateNode(['type' => $type->id(), 'created' => REQUEST_TIME - ($i * 5 + $j)]);
|
||||
$nodes[$type->id()][$node->id()] = $node;
|
||||
$all_nids[] = $node->id();
|
||||
}
|
||||
}
|
||||
|
||||
$this->drupalGet('test-node-view');
|
||||
$this->assertResponse(404);
|
||||
|
||||
$this->drupalGet('test-node-view/all');
|
||||
$this->assertResponse(200);
|
||||
$this->assertNids($all_nids);
|
||||
|
||||
foreach ($types as $type) {
|
||||
$this->drupalGet("test-node-view/{$type->id()}");
|
||||
$this->assertEscaped($type->label());
|
||||
$this->assertNids(array_keys($nodes[$type->id()]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that a list of nodes appear on the page.
|
||||
*
|
||||
* @param array $expected_nids
|
||||
* An array of node IDs.
|
||||
*/
|
||||
protected function assertNids(array $expected_nids = []) {
|
||||
$result = $this->xpath('//span[@class="field-content"]');
|
||||
$nids = [];
|
||||
foreach ($result as $element) {
|
||||
$nids[] = (int) $element->getText();
|
||||
}
|
||||
$this->assertEqual($nids, $expected_nids);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional\Views;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\views\Plugin\views\PluginBase;
|
||||
use Drupal\views\Tests\ViewTestData;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests node language fields, filters, and sorting.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeLanguageTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['language', 'node_test_views'];
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_language'];
|
||||
|
||||
/**
|
||||
* List of node titles by language.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $nodeTitles = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp($import_test_views = TRUE) {
|
||||
parent::setUp(FALSE);
|
||||
|
||||
// Create Page content type.
|
||||
if ($this->profile != 'standard') {
|
||||
$this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']);
|
||||
ViewTestData::createTestViews(get_class($this), ['node_test_views']);
|
||||
}
|
||||
|
||||
// Add two new languages.
|
||||
ConfigurableLanguage::createFromLangcode('fr')->save();
|
||||
ConfigurableLanguage::createFromLangcode('es')->save();
|
||||
|
||||
// Make the body field translatable. The title is already translatable by
|
||||
// definition.
|
||||
$field_storage = FieldStorageConfig::loadByName('node', 'body');
|
||||
$field_storage->setTranslatable(TRUE);
|
||||
$field_storage->save();
|
||||
|
||||
// Set up node titles. They should not include the words "French",
|
||||
// "English", or "Spanish", as there is a language field in the view
|
||||
// that prints out those words.
|
||||
$this->nodeTitles = [
|
||||
LanguageInterface::LANGCODE_NOT_SPECIFIED => [
|
||||
'First node und',
|
||||
],
|
||||
'es' => [
|
||||
'Primero nodo es',
|
||||
'Segundo nodo es',
|
||||
'Tercera nodo es',
|
||||
],
|
||||
'en' => [
|
||||
'First node en',
|
||||
'Second node en',
|
||||
],
|
||||
'fr' => [
|
||||
'Premier nœud fr',
|
||||
]
|
||||
];
|
||||
|
||||
// Create nodes with translations.
|
||||
foreach ($this->nodeTitles['es'] as $index => $title) {
|
||||
$node = $this->drupalCreateNode(['title' => $title, 'langcode' => 'es', 'type' => 'page', 'promote' => 1]);
|
||||
foreach (['en', 'fr'] as $langcode) {
|
||||
if (isset($this->nodeTitles[$langcode][$index])) {
|
||||
$translation = $node->addTranslation($langcode, ['title' => $this->nodeTitles[$langcode][$index]]);
|
||||
$translation->body->value = $this->randomMachineName(32);
|
||||
}
|
||||
}
|
||||
$node->save();
|
||||
}
|
||||
// Create non-translatable nodes.
|
||||
foreach ($this->nodeTitles[LanguageInterface::LANGCODE_NOT_SPECIFIED] as $index => $title) {
|
||||
$node = $this->drupalCreateNode(['title' => $title, 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, 'type' => 'page', 'promote' => 1]);
|
||||
$node->body->value = $this->randomMachineName(32);
|
||||
$node->save();
|
||||
}
|
||||
|
||||
$this->container->get('router.builder')->rebuild();
|
||||
|
||||
$user = $this->drupalCreateUser(['access content overview', 'access content']);
|
||||
$this->drupalLogin($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests translation language filter, field, and sort.
|
||||
*/
|
||||
public function testLanguages() {
|
||||
// Test the page with no arguments. It is filtered to Spanish and French.
|
||||
// The page shows node titles and languages.
|
||||
$this->drupalGet('test-language');
|
||||
$message = 'French/Spanish page';
|
||||
|
||||
// Test that the correct nodes are shown.
|
||||
foreach ($this->nodeTitles as $langcode => $list) {
|
||||
foreach ($list as $title) {
|
||||
if ($langcode == 'en') {
|
||||
$this->assertNoText($title, $title . ' does not appear on ' . $message);
|
||||
}
|
||||
else {
|
||||
$this->assertText($title, $title . ' does appear on ' . $message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test that the language field value is shown.
|
||||
$this->assertNoText('English', 'English language is not shown on ' . $message);
|
||||
$this->assertText('French', 'French language is shown on ' . $message);
|
||||
$this->assertText('Spanish', 'Spanish language is shown on ' . $message);
|
||||
|
||||
// Test page sorting, which is by language code, ascending. So the
|
||||
// Spanish nodes should appear before the French nodes.
|
||||
$page = $this->getTextContent();
|
||||
$pos_es_max = 0;
|
||||
$pos_fr_min = 10000;
|
||||
foreach ($this->nodeTitles['es'] as $title) {
|
||||
$pos_es_max = max($pos_es_max, strpos($page, $title));
|
||||
}
|
||||
foreach ($this->nodeTitles['fr'] as $title) {
|
||||
$pos_fr_min = min($pos_fr_min, strpos($page, $title));
|
||||
}
|
||||
$this->assertTrue($pos_es_max < $pos_fr_min, 'Spanish translations appear before French on ' . $message);
|
||||
|
||||
// Test the argument -- filter to just Spanish.
|
||||
$this->drupalGet('test-language/es');
|
||||
// This time, test just the language field.
|
||||
$message = 'Spanish argument page';
|
||||
$this->assertNoText('English', 'English language is not shown on ' . $message);
|
||||
$this->assertNoText('French', 'French language is not shown on ' . $message);
|
||||
$this->assertText('Spanish', 'Spanish language is shown on ' . $message);
|
||||
|
||||
// Test the front page view filter. Only node titles in the current language
|
||||
// should be displayed on the front page by default.
|
||||
foreach ($this->nodeTitles as $langcode => $titles) {
|
||||
// The frontpage view does not display content without a language.
|
||||
if ($langcode == LanguageInterface::LANGCODE_NOT_SPECIFIED) {
|
||||
continue;
|
||||
}
|
||||
$this->drupalGet(($langcode == 'en' ? '' : "$langcode/") . 'node');
|
||||
foreach ($titles as $title) {
|
||||
$this->assertText($title);
|
||||
}
|
||||
foreach ($this->nodeTitles as $control_langcode => $control_titles) {
|
||||
if ($langcode != $control_langcode) {
|
||||
foreach ($control_titles as $title) {
|
||||
$this->assertNoText($title);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test the node admin view filter. By default all translations should show.
|
||||
$this->drupalGet('admin/content');
|
||||
foreach ($this->nodeTitles as $titles) {
|
||||
foreach ($titles as $title) {
|
||||
$this->assertText($title);
|
||||
}
|
||||
}
|
||||
// When filtered, only the specific languages should show.
|
||||
foreach ($this->nodeTitles as $langcode => $titles) {
|
||||
$this->drupalGet('admin/content', ['query' => ['langcode' => $langcode]]);
|
||||
foreach ($titles as $title) {
|
||||
$this->assertText($title);
|
||||
}
|
||||
foreach ($this->nodeTitles as $control_langcode => $control_titles) {
|
||||
if ($langcode != $control_langcode) {
|
||||
foreach ($control_titles as $title) {
|
||||
$this->assertNoText($title);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Override the config for the front page view, so that the language
|
||||
// filter is set to the site default language instead. This should just
|
||||
// show the English nodes, no matter what the content language is.
|
||||
$config = $this->config('views.view.frontpage');
|
||||
$config->set('display.default.display_options.filters.langcode.value', [PluginBase::VIEWS_QUERY_LANGUAGE_SITE_DEFAULT => PluginBase::VIEWS_QUERY_LANGUAGE_SITE_DEFAULT]);
|
||||
$config->save();
|
||||
foreach ($this->nodeTitles as $langcode => $titles) {
|
||||
if ($langcode == LanguageInterface::LANGCODE_NOT_SPECIFIED) {
|
||||
continue;
|
||||
}
|
||||
$this->drupalGet(($langcode == 'en' ? '' : "$langcode/") . 'node');
|
||||
foreach ($this->nodeTitles as $control_langcode => $control_titles) {
|
||||
foreach ($control_titles as $title) {
|
||||
if ($control_langcode == 'en') {
|
||||
$this->assertText($title, 'English title is shown when filtering is site default');
|
||||
}
|
||||
else {
|
||||
$this->assertNoText($title, 'Non-English title is not shown when filtering is site default');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Override the config so that the language filter is set to the UI
|
||||
// language, and make that have a fixed value of 'es'.
|
||||
//
|
||||
// IMPORTANT: Make sure this part of the test is last -- it is changing
|
||||
// language configuration!
|
||||
$config->set('display.default.display_options.filters.langcode.value', ['***LANGUAGE_language_interface***' => '***LANGUAGE_language_interface***']);
|
||||
$config->save();
|
||||
$language_config = $this->config('language.types');
|
||||
$language_config->set('negotiation.language_interface.enabled', ['language-selected' => 1]);
|
||||
$language_config->save();
|
||||
$language_config = $this->config('language.negotiation');
|
||||
$language_config->set('selected_langcode', 'es');
|
||||
$language_config->save();
|
||||
|
||||
// With a fixed language selected, there is no language-based URL.
|
||||
$this->drupalGet('node');
|
||||
foreach ($this->nodeTitles as $control_langcode => $control_titles) {
|
||||
foreach ($control_titles as $title) {
|
||||
if ($control_langcode == 'es') {
|
||||
$this->assertText($title, 'Spanish title is shown when filtering is fixed UI language');
|
||||
}
|
||||
else {
|
||||
$this->assertNoText($title, 'Non-Spanish title is not shown when filtering is fixed UI language');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests native name display in language field.
|
||||
*/
|
||||
public function testNativeLanguageField() {
|
||||
$this->assertLanguageNames();
|
||||
|
||||
// Modify test view to display native language names and set translations.
|
||||
$config = $this->config('views.view.test_language');
|
||||
$config->set('display.default.display_options.fields.langcode.settings.native_language', TRUE);
|
||||
$config->save();
|
||||
\Drupal::languageManager()->getLanguageConfigOverride('fr', 'language.entity.fr')->set('label', 'Français')->save();
|
||||
\Drupal::languageManager()->getLanguageConfigOverride('es', 'language.entity.es')->set('label', 'Español')->save();
|
||||
$this->assertLanguageNames(TRUE);
|
||||
|
||||
// Modify test view to use the views built-in language field and test that.
|
||||
\Drupal::state()->set('node_test_views.use_basic_handler', TRUE);
|
||||
Views::viewsData()->clear();
|
||||
$config = $this->config('views.view.test_language');
|
||||
$config->set('display.default.display_options.fields.langcode.native_language', FALSE);
|
||||
$config->clear('display.default.display_options.fields.langcode.settings');
|
||||
$config->clear('display.default.display_options.fields.langcode.type');
|
||||
$config->set('display.default.display_options.fields.langcode.plugin_id', 'language');
|
||||
$config->save();
|
||||
$this->assertLanguageNames();
|
||||
$config->set('display.default.display_options.fields.langcode.native_language', TRUE)->save();
|
||||
$this->assertLanguageNames(TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts the presence of language names in their English or native forms.
|
||||
*
|
||||
* @param bool $native
|
||||
* (optional) Whether to assert the language name in its native form.
|
||||
*/
|
||||
protected function assertLanguageNames($native = FALSE) {
|
||||
$this->drupalGet('test-language');
|
||||
if ($native) {
|
||||
$this->assertText('Français', 'French language shown in native form.');
|
||||
$this->assertText('Español', 'Spanish language shown in native form.');
|
||||
$this->assertNoText('French', 'French language not shown in English.');
|
||||
$this->assertNoText('Spanish', 'Spanish language not shown in English.');
|
||||
}
|
||||
else {
|
||||
$this->assertNoText('Français', 'French language not shown in native form.');
|
||||
$this->assertNoText('Español', 'Spanish language not shown in native form.');
|
||||
$this->assertText('French', 'French language shown in English.');
|
||||
$this->assertText('Spanish', 'Spanish language shown in English.');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional\Views;
|
||||
|
||||
use Drupal\Tests\views\Functional\Wizard\WizardTestBase;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests the wizard with node_revision as base table.
|
||||
*
|
||||
* @group node
|
||||
* @see \Drupal\node\Plugin\views\wizard\NodeRevision
|
||||
*/
|
||||
class NodeRevisionWizardTest extends WizardTestBase {
|
||||
|
||||
/**
|
||||
* Tests creating a node revision view.
|
||||
*/
|
||||
public function testViewAdd() {
|
||||
$this->drupalCreateContentType(['type' => 'article']);
|
||||
// Create two nodes with two revision.
|
||||
$node_storage = \Drupal::entityManager()->getStorage('node');
|
||||
/** @var \Drupal\node\NodeInterface $node */
|
||||
$node = $node_storage->create(['title' => $this->randomString(), 'type' => 'article', 'created' => REQUEST_TIME + 40]);
|
||||
$node->save();
|
||||
|
||||
$node = $node->createDuplicate();
|
||||
$node->setNewRevision();
|
||||
$node->created->value = REQUEST_TIME + 20;
|
||||
$node->save();
|
||||
|
||||
$node = $node_storage->create(['title' => $this->randomString(), 'type' => 'article', 'created' => REQUEST_TIME + 30]);
|
||||
$node->save();
|
||||
|
||||
$node = $node->createDuplicate();
|
||||
$node->setNewRevision();
|
||||
$node->created->value = REQUEST_TIME + 10;
|
||||
$node->save();
|
||||
|
||||
$view = [];
|
||||
$view['label'] = $this->randomMachineName(16);
|
||||
$view['id'] = strtolower($this->randomMachineName(16));
|
||||
$view['description'] = $this->randomMachineName(16);
|
||||
$view['page[create]'] = FALSE;
|
||||
$view['show[wizard_key]'] = 'node_revision';
|
||||
$this->drupalPostForm('admin/structure/views/add', $view, t('Save and edit'));
|
||||
|
||||
$view_storage_controller = \Drupal::entityManager()->getStorage('view');
|
||||
/** @var \Drupal\views\Entity\View $view */
|
||||
$view = $view_storage_controller->load($view['id']);
|
||||
|
||||
$this->assertEqual($view->get('base_table'), 'node_field_revision');
|
||||
|
||||
$executable = Views::executableFactory()->get($view);
|
||||
$this->executeView($executable);
|
||||
|
||||
$this->assertIdenticalResultset($executable, [['vid' => 1], ['vid' => 3], ['vid' => 2], ['vid' => 4]],
|
||||
['vid' => 'vid']);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional\Views;
|
||||
|
||||
use Drupal\Tests\views\Functional\ViewTestBase;
|
||||
use Drupal\views\Tests\ViewTestData;
|
||||
|
||||
/**
|
||||
* Base class for all node Views tests.
|
||||
*/
|
||||
abstract class NodeTestBase extends ViewTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['node_test_views'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp($import_test_views = TRUE) {
|
||||
parent::setUp($import_test_views);
|
||||
|
||||
if ($import_test_views) {
|
||||
ViewTestData::createTestViews(get_class($this), ['node_test_views']);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional\Views;
|
||||
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests the node row plugin.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class PathPluginTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node'];
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_node_path_plugin'];
|
||||
|
||||
/**
|
||||
* Contains all nodes used by this test.
|
||||
*
|
||||
* @var Node[]
|
||||
*/
|
||||
protected $nodes;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp($import_test_views = TRUE) {
|
||||
parent::setUp($import_test_views);
|
||||
|
||||
$this->drupalCreateContentType(['type' => 'article']);
|
||||
|
||||
// Create two nodes.
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
$this->nodes[] = $this->drupalCreateNode(
|
||||
[
|
||||
'type' => 'article',
|
||||
'body' => [
|
||||
[
|
||||
'value' => $this->randomMachineName(42),
|
||||
'format' => filter_default_format(),
|
||||
'summary' => $this->randomMachineName(),
|
||||
],
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the node path plugin.
|
||||
*/
|
||||
public function testPathPlugin() {
|
||||
/** @var \Drupal\Core\Render\RendererInterface $renderer */
|
||||
$renderer = $this->container->get('renderer');
|
||||
$view = Views::getView('test_node_path_plugin');
|
||||
$view->initDisplay();
|
||||
$view->setDisplay('page_1');
|
||||
$view->initStyle();
|
||||
$view->rowPlugin->options['view_mode'] = 'full';
|
||||
|
||||
// Test with view_mode full.
|
||||
$output = $view->preview();
|
||||
$output = $renderer->renderRoot($output);
|
||||
foreach ($this->nodes as $node) {
|
||||
$this->assertTrue(strpos($output, 'This is <strong>not escaped</strong> and this is ' . $node->link('the link')) !== FALSE, 'Make sure path field rewriting is not escaped.');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional\Views;
|
||||
|
||||
/**
|
||||
* Tests the different revision link handlers.
|
||||
*
|
||||
* @group node
|
||||
*
|
||||
* @see \Drupal\node\Plugin\views\field\RevisionLink
|
||||
* @see \Drupal\node\Plugin\views\field\RevisionLinkDelete
|
||||
* @see \Drupal\node\Plugin\views\field\RevisionLinkRevert
|
||||
*/
|
||||
class RevisionLinkTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_node_revision_links'];
|
||||
|
||||
/**
|
||||
* Tests revision links.
|
||||
*/
|
||||
public function testRevisionLinks() {
|
||||
// Create one user which can view/revert and delete and one which can only
|
||||
// do one of them.
|
||||
$this->drupalCreateContentType(['name' => 'page', 'type' => 'page']);
|
||||
$account = $this->drupalCreateUser(['revert all revisions', 'view all revisions', 'delete all revisions', 'edit any page content', 'delete any page content']);
|
||||
$this->drupalLogin($account);
|
||||
// Create two nodes, one without an additional revision and one with a
|
||||
// revision.
|
||||
$nodes = [
|
||||
$this->drupalCreateNode(),
|
||||
$this->drupalCreateNode(),
|
||||
];
|
||||
|
||||
$first_revision = $nodes[1]->getRevisionId();
|
||||
// Create revision of the node.
|
||||
$nodes[1]->setNewRevision();
|
||||
$nodes[1]->save();
|
||||
$second_revision = $nodes[1]->getRevisionId();
|
||||
|
||||
$this->drupalGet('test-node-revision-links');
|
||||
$this->assertResponse(200, 'Test view can be accessed in the path expected');
|
||||
// The first node revision should link to the node directly as you get an
|
||||
// access denied if you link to the revision.
|
||||
$url = $nodes[0]->urlInfo()->toString();
|
||||
$this->assertLinkByHref($url);
|
||||
$this->assertNoLinkByHref($url . '/revisions/' . $nodes[0]->getRevisionId() . '/view');
|
||||
$this->assertNoLinkByHref($url . '/revisions/' . $nodes[0]->getRevisionId() . '/delete');
|
||||
$this->assertNoLinkByHref($url . '/revisions/' . $nodes[0]->getRevisionId() . '/revert');
|
||||
|
||||
// For the second node the current revision got set to the last revision, so
|
||||
// the first one should also link to the node page itself.
|
||||
$url = $nodes[1]->urlInfo()->toString();
|
||||
$this->assertLinkByHref($url);
|
||||
$this->assertLinkByHref($url . '/revisions/' . $first_revision . '/view');
|
||||
$this->assertLinkByHref($url . '/revisions/' . $first_revision . '/delete');
|
||||
$this->assertLinkByHref($url . '/revisions/' . $first_revision . '/revert');
|
||||
$this->assertNoLinkByHref($url . '/revisions/' . $second_revision . '/view');
|
||||
$this->assertNoLinkByHref($url . '/revisions/' . $second_revision . '/delete');
|
||||
$this->assertNoLinkByHref($url . '/revisions/' . $second_revision . '/revert');
|
||||
|
||||
$accounts = [
|
||||
'view' => $this->drupalCreateUser(['view all revisions']),
|
||||
'revert' => $this->drupalCreateUser(['revert all revisions', 'edit any page content']),
|
||||
'delete' => $this->drupalCreateUser(['delete all revisions', 'delete any page content']),
|
||||
];
|
||||
|
||||
$url = $nodes[1]->urlInfo()->toString();
|
||||
// Render the view with users which can only delete/revert revisions.
|
||||
foreach ($accounts as $allowed_operation => $account) {
|
||||
$this->drupalLogin($account);
|
||||
$this->drupalGet('test-node-revision-links');
|
||||
// Check expected links.
|
||||
foreach (['revert', 'delete'] as $operation) {
|
||||
if ($operation == $allowed_operation) {
|
||||
$this->assertLinkByHref($url . '/revisions/' . $first_revision . '/' . $operation);
|
||||
}
|
||||
else {
|
||||
$this->assertNoLinkByHref($url . '/revisions/' . $first_revision . '/' . $operation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional\Views;
|
||||
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests the node row plugin.
|
||||
*
|
||||
* @group node
|
||||
* @see \Drupal\node\Plugin\views\row\NodeRow
|
||||
*/
|
||||
class RowPluginTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node'];
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_node_row_plugin'];
|
||||
|
||||
/**
|
||||
* Contains all nodes used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $nodes;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp($import_test_views = TRUE) {
|
||||
parent::setUp($import_test_views);
|
||||
|
||||
$this->drupalCreateContentType(['type' => 'article']);
|
||||
|
||||
// Create two nodes.
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
$this->nodes[] = $this->drupalCreateNode(
|
||||
[
|
||||
'type' => 'article',
|
||||
'body' => [
|
||||
[
|
||||
'value' => $this->randomMachineName(42),
|
||||
'format' => filter_default_format(),
|
||||
'summary' => $this->randomMachineName(),
|
||||
],
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the node row plugin.
|
||||
*/
|
||||
public function testRowPlugin() {
|
||||
/** @var \Drupal\Core\Render\RendererInterface $renderer */
|
||||
$renderer = $this->container->get('renderer');
|
||||
$view = Views::getView('test_node_row_plugin');
|
||||
$view->initDisplay();
|
||||
$view->setDisplay('page_1');
|
||||
$view->initStyle();
|
||||
$view->rowPlugin->options['view_mode'] = 'full';
|
||||
|
||||
// Test with view_mode full.
|
||||
$output = $view->preview();
|
||||
$output = $renderer->renderRoot($output);
|
||||
foreach ($this->nodes as $node) {
|
||||
$this->assertFalse(strpos($output, $node->body->summary) !== FALSE, 'Make sure the teaser appears in the output of the view.');
|
||||
$this->assertTrue(strpos($output, $node->body->value) !== FALSE, 'Make sure the full text appears in the output of the view.');
|
||||
}
|
||||
|
||||
// Test with teasers.
|
||||
$view->rowPlugin->options['view_mode'] = 'teaser';
|
||||
$output = $view->preview();
|
||||
$output = $renderer->renderRoot($output);
|
||||
foreach ($this->nodes as $node) {
|
||||
$this->assertTrue(strpos($output, $node->body->summary) !== FALSE, 'Make sure the teaser appears in the output of the view.');
|
||||
$this->assertFalse(strpos($output, $node->body->value) !== FALSE, 'Make sure the full text does not appears in the output of the view if teaser is set as viewmode.');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional\Views;
|
||||
|
||||
use Drupal\node\NodeInterface;
|
||||
|
||||
/**
|
||||
* Tests the node.status_extra field handler.
|
||||
*
|
||||
* @group node
|
||||
* @see \Drupal\node\Plugin\views\filter\Status
|
||||
*/
|
||||
class StatusExtraTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_status_extra'];
|
||||
|
||||
/**
|
||||
* Tests the status extra filter.
|
||||
*/
|
||||
public function testStatusExtra() {
|
||||
$node_author = $this->drupalCreateUser(['view own unpublished content']);
|
||||
$node_author_not_unpublished = $this->drupalCreateUser();
|
||||
$normal_user = $this->drupalCreateUser();
|
||||
$admin_user = $this->drupalCreateUser(['bypass node access']);
|
||||
|
||||
// Create one published and one unpublished node by the admin.
|
||||
$node_published = $this->drupalCreateNode(['uid' => $admin_user->id()]);
|
||||
$node_unpublished = $this->drupalCreateNode(['uid' => $admin_user->id(), 'status' => NodeInterface::NOT_PUBLISHED]);
|
||||
|
||||
// Create one unpublished node by a certain author user.
|
||||
$node_unpublished2 = $this->drupalCreateNode(['uid' => $node_author->id(), 'status' => NodeInterface::NOT_PUBLISHED]);
|
||||
|
||||
// Create one unpublished node by a user who does not have the `view own
|
||||
// unpublished content` permission.
|
||||
$node_unpublished3 = $this->drupalCreateNode(['uid' => $node_author_not_unpublished->id(), 'status' => NodeInterface::NOT_PUBLISHED]);
|
||||
|
||||
// The administrator should simply see all nodes.
|
||||
$this->drupalLogin($admin_user);
|
||||
$this->drupalGet('test_status_extra');
|
||||
$this->assertText($node_published->label());
|
||||
$this->assertText($node_unpublished->label());
|
||||
$this->assertText($node_unpublished2->label());
|
||||
$this->assertText($node_unpublished3->label());
|
||||
|
||||
// The node author should see the published node and his own node.
|
||||
$this->drupalLogin($node_author);
|
||||
$this->drupalGet('test_status_extra');
|
||||
$this->assertText($node_published->label());
|
||||
$this->assertNoText($node_unpublished->label());
|
||||
$this->assertText($node_unpublished2->label());
|
||||
$this->assertNoText($node_unpublished3->label());
|
||||
|
||||
// The normal user should just see the published node.
|
||||
$this->drupalLogin($normal_user);
|
||||
$this->drupalGet('test_status_extra');
|
||||
$this->assertText($node_published->label());
|
||||
$this->assertNoText($node_unpublished->label());
|
||||
$this->assertNoText($node_unpublished2->label());
|
||||
$this->assertNoText($node_unpublished3->label());
|
||||
|
||||
// The author without the permission to see his own unpublished node should
|
||||
// just see the published node.
|
||||
$this->drupalLogin($node_author_not_unpublished);
|
||||
$this->drupalGet('test_status_extra');
|
||||
$this->assertText($node_published->label());
|
||||
$this->assertNoText($node_unpublished->label());
|
||||
$this->assertNoText($node_unpublished2->label());
|
||||
$this->assertNoText($node_unpublished3->label());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,7 +26,7 @@ class NodeImportChangeTest extends KernelTestBase {
|
||||
parent::setUp();
|
||||
|
||||
// Set default storage backend.
|
||||
$this->installConfig(array('field', 'node_test_config'));
|
||||
$this->installConfig(['field', 'node_test_config']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,7 +18,7 @@ class NodeImportCreateTest extends KernelTestBase {
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('node', 'field', 'text', 'system', 'user');
|
||||
public static $modules = ['node', 'field', 'text', 'system', 'user'];
|
||||
|
||||
/**
|
||||
* Set the default field storage backend for fields created during tests.
|
||||
@@ -28,7 +28,7 @@ class NodeImportCreateTest extends KernelTestBase {
|
||||
$this->installEntitySchema('user');
|
||||
|
||||
// Set default storage backend.
|
||||
$this->installConfig(array('field'));
|
||||
$this->installConfig(['field']);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,7 +42,7 @@ class NodeImportCreateTest extends KernelTestBase {
|
||||
|
||||
// Enable node_test_config module and check that the content type
|
||||
// shipped in the module's default config is created.
|
||||
$this->container->get('module_installer')->install(array('node_test_config'));
|
||||
$this->container->get('module_installer')->install(['node_test_config']);
|
||||
$node_type = NodeType::load($node_type_id);
|
||||
$this->assertTrue($node_type, 'The default content type was created.');
|
||||
}
|
||||
|
||||
@@ -13,6 +13,11 @@ use Drupal\node\Entity\Node;
|
||||
*/
|
||||
class MigrateNodeBundleSettingsTest extends MigrateDrupal6TestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['menu_ui'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Drupal\Tests\node\Kernel\Migrate\d6;
|
||||
|
||||
use Drupal\config\Tests\SchemaCheckTestTrait;
|
||||
use Drupal\Tests\SchemaCheckTestTrait;
|
||||
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,7 +10,7 @@ use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
|
||||
*/
|
||||
class MigrateNodeSettingPromoteTest extends MigrateDrupal6TestBase {
|
||||
|
||||
public static $modules = ['node', 'text'];
|
||||
public static $modules = ['node', 'text', 'menu_ui'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Kernel\Migrate\d6;
|
||||
|
||||
use Drupal\Core\Field\Entity\BaseFieldOverride;
|
||||
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
|
||||
|
||||
/**
|
||||
* @group migrate_drupal_6
|
||||
*/
|
||||
class MigrateNodeSettingStatusTest extends MigrateDrupal6TestBase {
|
||||
|
||||
public static $modules = ['node', 'text', 'menu_ui'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installConfig(['node']);
|
||||
$this->executeMigration('d6_node_type');
|
||||
$this->executeMigration('d6_node_setting_status');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests migration of the publishing status checkbox's settings.
|
||||
*/
|
||||
public function testMigration() {
|
||||
$this->assertIdentical('Publishing status', BaseFieldOverride::load('node.article.status')->label());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,7 +10,7 @@ use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
|
||||
*/
|
||||
class MigrateNodeSettingStickyTest extends MigrateDrupal6TestBase {
|
||||
|
||||
public static $modules = ['node', 'text'];
|
||||
public static $modules = ['node', 'text', 'menu_ui'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
||||
@@ -19,7 +19,7 @@ class MigrateNodeTest extends MigrateNodeTestBase {
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['language', 'content_translation'];
|
||||
public static $modules = ['language', 'content_translation', 'menu_ui'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -28,7 +28,12 @@ class MigrateNodeTest extends MigrateNodeTestBase {
|
||||
parent::setUp();
|
||||
$this->setUpMigratedFiles();
|
||||
$this->installSchema('file', ['file_usage']);
|
||||
$this->executeMigrations(['language', 'd6_node', 'd6_node_translation']);
|
||||
$this->executeMigrations([
|
||||
'language',
|
||||
'd6_language_content_settings',
|
||||
'd6_node',
|
||||
'd6_node_translation',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,7 +56,9 @@ class MigrateNodeTest extends MigrateNodeTestBase {
|
||||
/** @var \Drupal\node\NodeInterface $node_revision */
|
||||
$node_revision = \Drupal::entityManager()->getStorage('node')->loadRevision(1);
|
||||
$this->assertIdentical('Test title', $node_revision->getTitle());
|
||||
$this->assertIdentical('1', $node_revision->getRevisionAuthor()->id(), 'Node revision has the correct user');
|
||||
$this->assertIdentical('1', $node_revision->getRevisionUser()->id(), 'Node revision has the correct user');
|
||||
$this->assertSame('1', $node_revision->id(), 'Node 1 loaded.');
|
||||
$this->assertSame('1', $node_revision->getRevisionId(), 'Node 1 revision 1 loaded.');
|
||||
// This is empty on the first revision.
|
||||
$this->assertIdentical(NULL, $node_revision->revision_log->value);
|
||||
$this->assertIdentical('This is a shared text field', $node->field_test->value);
|
||||
@@ -65,6 +72,11 @@ class MigrateNodeTest extends MigrateNodeTestBase {
|
||||
$this->assertIdentical('1', $node->field_test_identical2->value, 'Integer value is correct');
|
||||
$this->assertIdentical('This is a field with exclude unset.', $node->field_test_exclude_unset->value, 'Field with exclude unset is correct.');
|
||||
|
||||
// Test that date fields are migrated.
|
||||
$this->assertSame('2013-01-02T04:05:00', $node->field_test_date->value, 'Date field is correct');
|
||||
$this->assertSame('1391357160', $node->field_test_datestamp->value, 'Datestamp field is correct');
|
||||
$this->assertSame('2015-03-04T06:07:00', $node->field_test_datetime->value, 'Datetime field is correct');
|
||||
|
||||
// Test that link fields are migrated.
|
||||
$this->assertIdentical('https://www.drupal.org/project/drupal', $node->field_test_link->uri);
|
||||
$this->assertIdentical('Drupal project page', $node->field_test_link->title);
|
||||
@@ -72,7 +84,20 @@ class MigrateNodeTest extends MigrateNodeTestBase {
|
||||
|
||||
// Test the file field meta.
|
||||
$this->assertIdentical('desc', $node->field_test_filefield->description);
|
||||
$this->assertIdentical('5', $node->field_test_filefield->target_id);
|
||||
$this->assertIdentical('4', $node->field_test_filefield->target_id);
|
||||
|
||||
// Test that an email field is migrated.
|
||||
$this->assertSame('PrincessRuwenne@example.com', $node->field_test_email->value);
|
||||
|
||||
// Test that node reference field values were migrated.
|
||||
$node = Node::load(18);
|
||||
$this->assertCount(2, $node->field_company);
|
||||
$this->assertSame('Klingon Empire', $node->field_company[0]->entity->label());
|
||||
$this->assertSame('Romulan Empire', $node->field_company[1]->entity->label());
|
||||
|
||||
// Test that user reference field values were migrated.
|
||||
$this->assertCount(1, $node->field_commander);
|
||||
$this->assertSame('joe.roe', $node->field_commander[0]->entity->getUsername());
|
||||
|
||||
$node = Node::load(2);
|
||||
$this->assertIdentical('Test title rev 3', $node->getTitle());
|
||||
@@ -84,6 +109,20 @@ class MigrateNodeTest extends MigrateNodeTestBase {
|
||||
$this->assertIdentical('Drupal Groups', $node->field_test_link->title);
|
||||
$this->assertIdentical([], $node->field_test_link->options['attributes']);
|
||||
|
||||
$node = Node::load(3);
|
||||
// Test multivalue field.
|
||||
$value_1 = $node->field_multivalue->value;
|
||||
$value_2 = $node->field_multivalue[1]->value;
|
||||
|
||||
// SQLite does not support scales for float data types so we need to convert
|
||||
// the value manually.
|
||||
if ($this->container->get('database')->driver() == 'sqlite') {
|
||||
$value_1 = sprintf('%01.2f', $value_1);
|
||||
$value_2 = sprintf('%01.2f', $value_2);
|
||||
}
|
||||
$this->assertSame('33.00', $value_1);
|
||||
$this->assertSame('44.00', $value_2);
|
||||
|
||||
// Test that a link field with an internal link is migrated.
|
||||
$node = Node::load(9);
|
||||
$this->assertSame('internal:/node/10', $node->field_test_link->uri);
|
||||
@@ -96,6 +135,14 @@ class MigrateNodeTest extends MigrateNodeTestBase {
|
||||
$this->assertIdentical('The Real McCoy', $node->title->value);
|
||||
$this->assertTrue($node->hasTranslation('fr'), "Node 10 has french translation");
|
||||
|
||||
// Test that content_translation_source is set.
|
||||
$manager = $this->container->get('content_translation.manager');
|
||||
$this->assertIdentical('en', $manager->getTranslationMetadata($node->getTranslation('fr'))->getSource());
|
||||
|
||||
// Test that content_translation_source for a source other than English.
|
||||
$node = Node::load(12);
|
||||
$this->assertIdentical('zu', $manager->getTranslationMetadata($node->getTranslation('en'))->getSource());
|
||||
|
||||
// Node 11 is a translation of node 10, and should not be imported separately.
|
||||
$this->assertNull(Node::load(11), "Node 11 doesn't exist in D8, it was a translation");
|
||||
|
||||
@@ -143,11 +190,11 @@ class MigrateNodeTest extends MigrateNodeTestBase {
|
||||
$this->assertIdentical('full_html', $node->body->format);
|
||||
|
||||
// Now insert a row indicating a failure and set to update later.
|
||||
$title = $this->rerunMigration(array(
|
||||
$title = $this->rerunMigration([
|
||||
'sourceid1' => 2,
|
||||
'destid1' => NULL,
|
||||
'source_row_status' => MigrateIdMapInterface::STATUS_NEEDS_UPDATE,
|
||||
));
|
||||
]);
|
||||
$node = Node::load(2);
|
||||
$this->assertIdentical($title, $node->getTitle());
|
||||
}
|
||||
@@ -165,10 +212,10 @@ class MigrateNodeTest extends MigrateNodeTestBase {
|
||||
$title = $this->randomString();
|
||||
$source_connection = Database::getConnection('default', 'migrate');
|
||||
$source_connection->update('node_revisions')
|
||||
->fields(array(
|
||||
->fields([
|
||||
'title' => $title,
|
||||
'format' => 2,
|
||||
))
|
||||
])
|
||||
->condition('vid', 3)
|
||||
->execute();
|
||||
$migration = $this->getMigration('d6_node:story');
|
||||
|
||||
@@ -13,6 +13,11 @@ use Drupal\node\Entity\NodeType;
|
||||
*/
|
||||
class MigrateNodeTypeTest extends MigrateDrupal6TestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['menu_ui'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@@ -33,12 +38,18 @@ class MigrateNodeTypeTest extends MigrateDrupal6TestBase {
|
||||
$this->assertIdentical(TRUE, $node_type_page->displaySubmitted());
|
||||
$this->assertIdentical(FALSE, $node_type_page->isNewRevision());
|
||||
$this->assertIdentical(DRUPAL_OPTIONAL, $node_type_page->getPreviewMode());
|
||||
$this->assertIdentical($id_map->lookupDestinationID(array('test_page')), array('test_page'));
|
||||
$this->assertIdentical($id_map->lookupDestinationID(['test_page']), ['test_page']);
|
||||
|
||||
// Test we have a body field.
|
||||
$field = FieldConfig::loadByName('node', 'test_page', 'body');
|
||||
$this->assertIdentical('This is the body field label', $field->getLabel(), 'Body field was found.');
|
||||
|
||||
// Test default menus.
|
||||
$expected_available_menus = ['navigation'];
|
||||
$this->assertSame($expected_available_menus, $node_type_page->getThirdPartySetting('menu_ui', 'available_menus'));
|
||||
$expected_parent = 'navigation:';
|
||||
$this->assertSame($expected_parent, $node_type_page->getThirdPartySetting('menu_ui', 'parent'));
|
||||
|
||||
// Test the test_story content type.
|
||||
$node_type_story = NodeType::load('test_story');
|
||||
$this->assertIdentical('test_story', $node_type_story->id(), 'Node type test_story loaded');
|
||||
@@ -46,12 +57,18 @@ class MigrateNodeTypeTest extends MigrateDrupal6TestBase {
|
||||
$this->assertIdentical(TRUE, $node_type_story->displaySubmitted());
|
||||
$this->assertIdentical(FALSE, $node_type_story->isNewRevision());
|
||||
$this->assertIdentical(DRUPAL_OPTIONAL, $node_type_story->getPreviewMode());
|
||||
$this->assertIdentical($id_map->lookupDestinationID(array('test_story')), array('test_story'));
|
||||
$this->assertIdentical($id_map->lookupDestinationID(['test_story']), ['test_story']);
|
||||
|
||||
// Test we don't have a body field.
|
||||
$field = FieldConfig::loadByName('node', 'test_story', 'body');
|
||||
$this->assertIdentical(NULL, $field, 'No body field found');
|
||||
|
||||
// Test default menus.
|
||||
$expected_available_menus = ['navigation'];
|
||||
$this->assertSame($expected_available_menus, $node_type_story->getThirdPartySetting('menu_ui', 'available_menus'));
|
||||
$expected_parent = 'navigation:';
|
||||
$this->assertSame($expected_parent, $node_type_story->getThirdPartySetting('menu_ui', 'parent'));
|
||||
|
||||
// Test the test_event content type.
|
||||
$node_type_event = NodeType::load('test_event');
|
||||
$this->assertIdentical('test_event', $node_type_event->id(), 'Node type test_event loaded');
|
||||
@@ -59,11 +76,16 @@ class MigrateNodeTypeTest extends MigrateDrupal6TestBase {
|
||||
$this->assertIdentical(TRUE, $node_type_event->displaySubmitted());
|
||||
$this->assertIdentical(TRUE, $node_type_event->isNewRevision());
|
||||
$this->assertIdentical(DRUPAL_OPTIONAL, $node_type_event->getPreviewMode());
|
||||
$this->assertIdentical($id_map->lookupDestinationID(array('test_event')), array('test_event'));
|
||||
$this->assertIdentical($id_map->lookupDestinationID(['test_event']), ['test_event']);
|
||||
|
||||
// Test we have a body field.
|
||||
$field = FieldConfig::loadByName('node', 'test_event', 'body');
|
||||
$this->assertIdentical('Body', $field->getLabel(), 'Body field was found.');
|
||||
|
||||
$expected_available_menus = ['navigation'];
|
||||
$this->assertSame($expected_available_menus, $node_type_event->getThirdPartySetting('menu_ui', 'available_menus'));
|
||||
$expected_parent = 'navigation:';
|
||||
$this->assertSame($expected_parent, $node_type_event->getThirdPartySetting('menu_ui', 'parent'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ class MigrateViewModesTest extends MigrateDrupal6TestBase {
|
||||
$this->assertIdentical(FALSE, is_null($view_mode), 'Preview view mode loaded.');
|
||||
$this->assertIdentical('Preview', $view_mode->label(), 'View mode has correct label.');
|
||||
// Test the ID map.
|
||||
$this->assertIdentical(array('node', 'preview'), $this->getMigration('d6_view_modes')->getIdMap()->lookupDestinationID(array(1)));
|
||||
$this->assertIdentical(['node', 'preview'], $this->getMigration('d6_view_modes')->getIdMap()->lookupDestinationID([1]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Kernel\Migrate\d7;
|
||||
|
||||
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
|
||||
|
||||
/**
|
||||
* Test D7NodeDeriver.
|
||||
*
|
||||
* @group migrate_drupal_7
|
||||
*/
|
||||
class MigrateNodeDeriverTest extends MigrateDrupal7TestBase {
|
||||
|
||||
/**
|
||||
* The migration plugin manager.
|
||||
*
|
||||
* @var \Drupal\migrate\Plugin\MigrationPluginManagerInterface
|
||||
*/
|
||||
protected $pluginManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
$this->pluginManager = $this->container->get('plugin.manager.migration');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test node translation migrations with translation disabled.
|
||||
*/
|
||||
public function testNoTranslations() {
|
||||
// Without content_translation, there should be no translation migrations.
|
||||
$migrations = $this->pluginManager->createInstances('d7_node_translation');
|
||||
$this->assertSame([], $migrations,
|
||||
"No node translation migrations without content_translation");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test node translation migrations with translation enabled.
|
||||
*/
|
||||
public function testTranslations() {
|
||||
// With content_translation, there should be translation migrations for
|
||||
// each content type.
|
||||
$this->enableModules(['language', 'content_translation', 'node', 'filter']);
|
||||
$migrations = $this->pluginManager->createInstances('d7_node_translation');
|
||||
$this->assertArrayHasKey('d7_node_translation:article', $migrations,
|
||||
"Node translation migrations exist after content_translation installed");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Drupal\Tests\node\Kernel\Migrate\d7;
|
||||
|
||||
use Drupal\config\Tests\SchemaCheckTestTrait;
|
||||
use Drupal\Tests\SchemaCheckTestTrait;
|
||||
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,17 +13,23 @@ use Drupal\node\NodeInterface;
|
||||
*/
|
||||
class MigrateNodeTest extends MigrateDrupal7TestBase {
|
||||
|
||||
public static $modules = array(
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'content_translation',
|
||||
'comment',
|
||||
'datetime',
|
||||
'filter',
|
||||
'image',
|
||||
'language',
|
||||
'link',
|
||||
'menu_ui',
|
||||
'node',
|
||||
'taxonomy',
|
||||
'telephone',
|
||||
'text',
|
||||
);
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -40,15 +46,17 @@ class MigrateNodeTest extends MigrateDrupal7TestBase {
|
||||
$this->installSchema('system', ['sequences']);
|
||||
|
||||
$this->executeMigrations([
|
||||
'language',
|
||||
'd7_user_role',
|
||||
'd7_user',
|
||||
'd7_node_type',
|
||||
'd7_language_content_settings',
|
||||
'd7_comment_type',
|
||||
'd7_taxonomy_vocabulary',
|
||||
'd7_field',
|
||||
'd7_field_instance',
|
||||
'd7_node:test_content_type',
|
||||
'd7_node:article',
|
||||
'd7_node',
|
||||
'd7_node_translation',
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -111,7 +119,7 @@ class MigrateNodeTest extends MigrateDrupal7TestBase {
|
||||
$revision = \Drupal::entityManager()->getStorage('node')->loadRevision($id);
|
||||
$this->assertTrue($revision instanceof NodeInterface);
|
||||
$this->assertIdentical($title, $revision->getTitle());
|
||||
$this->assertIdentical($uid, $revision->getRevisionAuthor()->id());
|
||||
$this->assertIdentical($uid, $revision->getRevisionUser()->id());
|
||||
$this->assertIdentical($log, $revision->revision_log->value);
|
||||
$this->assertIdentical($timestamp, $revision->getRevisionCreationTime());
|
||||
}
|
||||
@@ -142,11 +150,36 @@ class MigrateNodeTest extends MigrateDrupal7TestBase {
|
||||
$this->assertIdentical('93', $node->field_images->height);
|
||||
$this->assertIdentical('http://google.com', $node->field_link->uri);
|
||||
$this->assertIdentical('Click Here', $node->field_link->title);
|
||||
// Test that an email field is migrated.
|
||||
$this->assertSame('default@example.com', $node->field_email->value);
|
||||
$this->assertSame('another@example.com', $node->field_email[1]->value);
|
||||
|
||||
$node = Node::load(2);
|
||||
$this->assertSame('en', $node->langcode->value);
|
||||
$this->assertIdentical("...is that it's the absolute best show ever. Trust me, I would know.", $node->body->value);
|
||||
$this->assertSame('The thing about Deep Space 9', $node->label());
|
||||
$this->assertIdentical('internal:/', $node->field_link->uri);
|
||||
$this->assertIdentical('Home', $node->field_link->title);
|
||||
$this->assertTrue($node->hasTranslation('is'), "Node 2 has an Icelandic translation");
|
||||
|
||||
$translation = $node->getTranslation('is');
|
||||
$this->assertSame('is', $translation->langcode->value);
|
||||
$this->assertSame("is - ...is that it's the absolute best show ever. Trust me, I would know.", $translation->body->value);
|
||||
$this->assertSame('is - The thing about Deep Space 9', $translation->label());
|
||||
$this->assertSame('internal:/', $translation->field_link->uri);
|
||||
$this->assertSame('Home', $translation->field_link->title);
|
||||
|
||||
// Test that content_translation_source is set.
|
||||
$manager = $this->container->get('content_translation.manager');
|
||||
$this->assertSame('en', $manager->getTranslationMetadata($node->getTranslation('is'))->getSource());
|
||||
|
||||
// Node 3 is a translation of node 2, and should not be imported separately.
|
||||
$this->assertNull(Node::load(3), "Node 3 doesn't exist in D8, it was a translation");
|
||||
|
||||
// Test that content_translation_source for a source other than English.
|
||||
$node = Node::load(4);
|
||||
$this->assertSame('is', $manager->getTranslationMetadata($node->getTranslation('en'))->getSource());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
|
||||
*/
|
||||
class MigrateNodeTitleLabelTest extends MigrateDrupal7TestBase {
|
||||
|
||||
public static $modules = ['node', 'text'];
|
||||
public static $modules = ['node', 'text', 'menu_ui'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
||||
@@ -20,14 +20,14 @@ class MigrateNodeTypeTest extends MigrateDrupal7TestBase {
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('node', 'text', 'filter');
|
||||
public static $modules = ['node', 'text', 'filter', 'menu_ui'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installConfig(array('node'));
|
||||
$this->installConfig(['node']);
|
||||
$this->executeMigration('d7_node_type');
|
||||
}
|
||||
|
||||
@@ -45,12 +45,13 @@ class MigrateNodeTypeTest extends MigrateDrupal7TestBase {
|
||||
* @param string $help
|
||||
* The expected help text.
|
||||
*/
|
||||
protected function assertEntity($id, $label, $description, $help, $display_submitted, $new_revision, $body_label = NULL) {
|
||||
protected function assertEntity($id, $label, $description, $help, $display_submitted, $new_revision, $expected_available_menus, $expected_parent, $body_label = NULL) {
|
||||
/** @var \Drupal\node\NodeTypeInterface $entity */
|
||||
$entity = NodeType::load($id);
|
||||
$this->assertTrue($entity instanceof NodeTypeInterface);
|
||||
$this->assertIdentical($label, $entity->label());
|
||||
$this->assertIdentical($description, $entity->getDescription());
|
||||
|
||||
$this->assertIdentical($help, $entity->getHelp());
|
||||
|
||||
$this->assertIdentical($display_submitted, $entity->displaySubmitted(), 'Submission info is displayed');
|
||||
@@ -62,20 +63,32 @@ class MigrateNodeTypeTest extends MigrateDrupal7TestBase {
|
||||
$this->assertTrue($body instanceof FieldConfigInterface);
|
||||
$this->assertIdentical($body_label, $body->label());
|
||||
}
|
||||
|
||||
$this->assertSame($expected_available_menus, $entity->getThirdPartySetting('menu_ui', 'available_menus'));
|
||||
$this->assertSame($expected_parent, $entity->getThirdPartySetting('menu_ui', 'parent'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Drupal 7 node type to Drupal 8 migration.
|
||||
*/
|
||||
public function testNodeType() {
|
||||
$this->assertEntity('article', 'Article', 'Use <em>articles</em> for time-sensitive content like news, press releases or blog posts.', 'Help text for articles', TRUE, FALSE, "Body");
|
||||
$this->assertEntity('blog', 'Blog entry', 'Use for multi-user blogs. Every user gets a personal blog.', 'Blog away, good sir!', TRUE, FALSE, 'Body');
|
||||
$expected_available_menus = ['main-menu'];
|
||||
$expected_parent = 'main-menu:0:';
|
||||
|
||||
$this->assertEntity('article', 'Article', 'Use <em>articles</em> for time-sensitive content like news, press releases or blog posts.', 'Help text for articles', TRUE, FALSE, $expected_available_menus, $expected_parent, "Body");
|
||||
$this->assertEntity('blog', 'Blog entry', 'Use for multi-user blogs. Every user gets a personal blog.', 'Blog away, good sir!', TRUE, FALSE, $expected_available_menus, $expected_parent, 'Body');
|
||||
// book's display_submitted flag is not set, so it will default to TRUE.
|
||||
$this->assertEntity('book', 'Book page', '<em>Books</em> have a built-in hierarchical navigation. Use for handbooks or tutorials.', '', TRUE, TRUE, "Body");
|
||||
$this->assertEntity('forum', 'Forum topic', 'A <em>forum topic</em> starts a new discussion thread within a forum.', 'No name-calling, no flame wars. Be nice.', TRUE, FALSE, 'Body');
|
||||
$this->assertEntity('page', 'Basic page', "Use <em>basic pages</em> for your static content, such as an 'About us' page.", 'Help text for basic pages', FALSE, FALSE, "Body");
|
||||
$this->assertEntity('book', 'Book page', '<em>Books</em> have a built-in hierarchical navigation. Use for handbooks or tutorials.', '', TRUE, TRUE, $expected_available_menus, $expected_parent, "Body");
|
||||
$this->assertEntity('forum', 'Forum topic', 'A <em>forum topic</em> starts a new discussion thread within a forum.', 'No name-calling, no flame wars. Be nice.', TRUE, FALSE, $expected_available_menus, $expected_parent, 'Body');
|
||||
$this->assertEntity('page', 'Basic page', "Use <em>basic pages</em> for your static content, such as an 'About us' page.", 'Help text for basic pages', FALSE, FALSE, $expected_available_menus, $expected_parent, "Body");
|
||||
// This node type does not carry a body field.
|
||||
$this->assertEntity('test_content_type', 'Test content type', 'This is the description of the test content type.', 'Help text for test content type', FALSE, TRUE);
|
||||
$expected_available_menus = [
|
||||
'main-menu',
|
||||
'management',
|
||||
'navigation',
|
||||
'user-menu',
|
||||
];
|
||||
$this->assertEntity('test_content_type', 'Test content type', 'This is the description of the test content type.', 'Help text for test content type', FALSE, TRUE, $expected_available_menus, $expected_parent);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Kernel;
|
||||
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\node\NodeInterface;
|
||||
use Drupal\simpletest\ContentTypeCreationTrait;
|
||||
use Drupal\simpletest\NodeCreationTrait;
|
||||
use Drupal\simpletest\UserCreationTrait;
|
||||
use Drupal\user\RoleInterface;
|
||||
|
||||
/**
|
||||
* Tests basic node_access functionality.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeAccessTest extends KernelTestBase {
|
||||
|
||||
use NodeCreationTrait {
|
||||
getNodeByTitle as drupalGetNodeByTitle;
|
||||
createNode as drupalCreateNode;
|
||||
}
|
||||
use UserCreationTrait {
|
||||
createUser as drupalCreateUser;
|
||||
createRole as drupalCreateRole;
|
||||
createAdminRole as drupalCreateAdminRole;
|
||||
}
|
||||
use ContentTypeCreationTrait {
|
||||
createContentType as drupalCreateContentType;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'node',
|
||||
'datetime',
|
||||
'user',
|
||||
'system',
|
||||
'filter',
|
||||
'field',
|
||||
'text',
|
||||
];
|
||||
|
||||
/**
|
||||
* Access handler.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityAccessControlHandlerInterface
|
||||
*/
|
||||
protected $accessHandler;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installSchema('system', 'sequences');
|
||||
$this->installSchema('node', 'node_access');
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('node');
|
||||
$this->installConfig('filter');
|
||||
$this->installConfig('node');
|
||||
$this->accessHandler = $this->container->get('entity_type.manager')
|
||||
->getAccessControlHandler('node');
|
||||
// Clear permissions for authenticated users.
|
||||
$this->config('user.role.' . RoleInterface::AUTHENTICATED_ID)
|
||||
->set('permissions', [])
|
||||
->save();
|
||||
|
||||
// Create user 1 who has special permissions.
|
||||
$this->drupalCreateUser();
|
||||
|
||||
// Create a node type.
|
||||
$this->drupalCreateContentType([
|
||||
'type' => 'page',
|
||||
'name' => 'Basic page',
|
||||
'display_submitted' => FALSE,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs basic tests for node_access function.
|
||||
*/
|
||||
public function testNodeAccess() {
|
||||
// Ensures user without 'access content' permission can do nothing.
|
||||
$web_user1 = $this->drupalCreateUser([
|
||||
'create page content',
|
||||
'edit any page content',
|
||||
'delete any page content',
|
||||
]);
|
||||
$node1 = $this->drupalCreateNode(['type' => 'page']);
|
||||
$this->assertNodeCreateAccess($node1->bundle(), FALSE, $web_user1);
|
||||
$this->assertNodeAccess([
|
||||
'view' => FALSE,
|
||||
'update' => FALSE,
|
||||
'delete' => FALSE,
|
||||
], $node1, $web_user1);
|
||||
|
||||
// Ensures user with 'bypass node access' permission can do everything.
|
||||
$web_user2 = $this->drupalCreateUser(['bypass node access']);
|
||||
$node2 = $this->drupalCreateNode(['type' => 'page']);
|
||||
$this->assertNodeCreateAccess($node2->bundle(), TRUE, $web_user2);
|
||||
$this->assertNodeAccess([
|
||||
'view' => TRUE,
|
||||
'update' => TRUE,
|
||||
'delete' => TRUE,
|
||||
], $node2, $web_user2);
|
||||
|
||||
// User cannot 'view own unpublished content'.
|
||||
$web_user3 = $this->drupalCreateUser(['access content']);
|
||||
$node3 = $this->drupalCreateNode([
|
||||
'status' => 0,
|
||||
'uid' => $web_user3->id(),
|
||||
]);
|
||||
$this->assertNodeAccess(['view' => FALSE], $node3, $web_user3);
|
||||
|
||||
// User cannot create content without permission.
|
||||
$this->assertNodeCreateAccess($node3->bundle(), FALSE, $web_user3);
|
||||
|
||||
// User can 'view own unpublished content', but another user cannot.
|
||||
$web_user4 = $this->drupalCreateUser([
|
||||
'access content',
|
||||
'view own unpublished content',
|
||||
]);
|
||||
$web_user5 = $this->drupalCreateUser([
|
||||
'access content',
|
||||
'view own unpublished content',
|
||||
]);
|
||||
$node4 = $this->drupalCreateNode([
|
||||
'status' => 0,
|
||||
'uid' => $web_user4->id(),
|
||||
]);
|
||||
$this->assertNodeAccess([
|
||||
'view' => TRUE,
|
||||
'update' => FALSE,
|
||||
], $node4, $web_user4);
|
||||
$this->assertNodeAccess(['view' => FALSE], $node4, $web_user5);
|
||||
|
||||
// Tests the default access provided for a published node.
|
||||
$node5 = $this->drupalCreateNode();
|
||||
$this->assertNodeAccess([
|
||||
'view' => TRUE,
|
||||
'update' => FALSE,
|
||||
'delete' => FALSE,
|
||||
], $node5, $web_user3);
|
||||
|
||||
// Tests the "edit any BUNDLE" and "delete any BUNDLE" permissions.
|
||||
$web_user6 = $this->drupalCreateUser([
|
||||
'access content',
|
||||
'edit any page content',
|
||||
'delete any page content',
|
||||
]);
|
||||
$node6 = $this->drupalCreateNode(['type' => 'page']);
|
||||
$this->assertNodeAccess([
|
||||
'view' => TRUE,
|
||||
'update' => TRUE,
|
||||
'delete' => TRUE,
|
||||
], $node6, $web_user6);
|
||||
|
||||
// Tests the "edit own BUNDLE" and "delete own BUNDLE" permission.
|
||||
$web_user7 = $this->drupalCreateUser([
|
||||
'access content',
|
||||
'edit own page content',
|
||||
'delete own page content',
|
||||
]);
|
||||
// User should not be able to edit or delete nodes they do not own.
|
||||
$this->assertNodeAccess([
|
||||
'view' => TRUE,
|
||||
'update' => FALSE,
|
||||
'delete' => FALSE,
|
||||
], $node6, $web_user7);
|
||||
|
||||
// User should be able to edit or delete nodes they own.
|
||||
$node7 = $this->drupalCreateNode([
|
||||
'type' => 'page',
|
||||
'uid' => $web_user7->id(),
|
||||
]);
|
||||
$this->assertNodeAccess([
|
||||
'view' => TRUE,
|
||||
'update' => TRUE,
|
||||
'delete' => TRUE,
|
||||
], $node7, $web_user7);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test operations not supported by node grants.
|
||||
*/
|
||||
public function testUnsupportedOperation() {
|
||||
$this->enableModules(['node_access_test_empty']);
|
||||
$web_user = $this->drupalCreateUser(['access content']);
|
||||
$node = $this->drupalCreateNode();
|
||||
$this->assertNodeAccess(['random_operation' => FALSE], $node, $web_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that node access correctly grants or denies access.
|
||||
*
|
||||
* @param array $ops
|
||||
* An associative array of the expected node access grants for the node
|
||||
* and account, with each key as the name of an operation (e.g. 'view',
|
||||
* 'delete') and each value a Boolean indicating whether access to that
|
||||
* operation should be granted.
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* The node object to check.
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The user account for which to check access.
|
||||
*/
|
||||
public function assertNodeAccess(array $ops, NodeInterface $node, AccountInterface $account) {
|
||||
foreach ($ops as $op => $result) {
|
||||
$this->assertEquals($result, $this->accessHandler->access($node, $op, $account), $this->nodeAccessAssertMessage($op, $result, $node->language()
|
||||
->getId()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that node create access correctly grants or denies access.
|
||||
*
|
||||
* @param string $bundle
|
||||
* The node bundle to check access to.
|
||||
* @param bool $result
|
||||
* Whether access should be granted or not.
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The user account for which to check access.
|
||||
* @param string|null $langcode
|
||||
* (optional) The language code indicating which translation of the node
|
||||
* to check. If NULL, the untranslated (fallback) access is checked.
|
||||
*/
|
||||
public function assertNodeCreateAccess($bundle, $result, AccountInterface $account, $langcode = NULL) {
|
||||
$this->assertEquals($result, $this->accessHandler->createAccess($bundle, $account, [
|
||||
'langcode' => $langcode,
|
||||
]), $this->nodeAccessAssertMessage('create', $result, $langcode));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an assert message to display which node access was tested.
|
||||
*
|
||||
* @param string $operation
|
||||
* The operation to check access for.
|
||||
* @param bool $result
|
||||
* Whether access should be granted or not.
|
||||
* @param string|null $langcode
|
||||
* (optional) The language code indicating which translation of the node
|
||||
* to check. If NULL, the untranslated (fallback) access is checked.
|
||||
*
|
||||
* @return string
|
||||
* An assert message string which contains information in plain English
|
||||
* about the node access permission test that was performed.
|
||||
*/
|
||||
public function nodeAccessAssertMessage($operation, $result, $langcode = NULL) {
|
||||
return new FormattableMarkup(
|
||||
'Node access returns @result with operation %op, language code %langcode.',
|
||||
[
|
||||
'@result' => $result ? 'true' : 'false',
|
||||
'%op' => $operation,
|
||||
'%langcode' => !empty($langcode) ? $langcode : 'empty',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,7 +28,7 @@ class NodeBodyFieldStorageTest extends KernelTestBase {
|
||||
$this->installSchema('user', 'users_data');
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('node');
|
||||
$this->installConfig(array('field', 'node'));
|
||||
$this->installConfig(['field', 'node']);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,7 +46,7 @@ class NodeBodyFieldStorageTest extends KernelTestBase {
|
||||
$field->delete();
|
||||
$field_storage = FieldStorageConfig::loadByName('node', 'body');
|
||||
$this->assertTrue(count($field_storage->getBundles()) == 0, 'Node body field storage exists after deleting the only instance of a field.');
|
||||
\Drupal::service('module_installer')->uninstall(array('node'));
|
||||
\Drupal::service('module_installer')->uninstall(['node']);
|
||||
$field_storage = FieldStorageConfig::loadByName('node', 'body');
|
||||
$this->assertFalse($field_storage, 'Node body field storage does not exist after uninstalling the Node module.');
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user