first commit

This commit is contained in:
2018-04-23 21:08:22 +02:00
commit c68c1dd9b0
12994 changed files with 1500824 additions and 0 deletions
@@ -0,0 +1,12 @@
name: 'Node module access tests'
type: module
description: 'Support module for node permission testing.'
package: Testing
# version: VERSION
# core: 8.x
# Information added by Drupal.org packaging script on 2017-04-19
version: '8.3.1'
core: '8.x'
project: 'drupal'
datestamp: 1492622988
@@ -0,0 +1,157 @@
<?php
/**
* @file
* Test module for testing the node access system.
*
* This module's functionality depends on the following state variables:
* - node_access_test.no_access_uid: Used in NodeQueryAlterTest to enable the
* node_access_all grant realm.
* - node_access_test.private: When TRUE, the module controls access for nodes
* with a 'private' property set, and inherits the default core access for
* nodes without this flag. When FALSE, the module controls access for all
* nodes.
* - node_access_test_secret_catalan: When set to TRUE and using the Catalan
* 'ca' language code, makes all Catalan content secret.
*
* @see node_access_test_node_grants()
* @see \Drupal\node\Tests\NodeQueryAlterTest
* @see \Drupal\node\Tests\NodeAccessBaseTableTest
*/
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Access\AccessResult;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\field\Entity\FieldConfig;
use Drupal\node\NodeTypeInterface;
use Drupal\node\NodeInterface;
/**
* Implements hook_node_grants().
*
* Provides three grant realms:
* - node_access_test_author: Grants users view, update, and delete privileges
* on nodes they have authored. Users receive a group ID matching their user
* ID on this realm.
* - node_access_test: Grants users view privileges when they have the
* 'node test view' permission. Users with this permission receive two group
* IDs for the realm, 8888 and 8889. Access for both realms is identical;
* the second group is added so that the interaction of multiple groups on
* a given grant realm can be tested in NodeAccessPagerTest.
* - node_access_all: Provides grants for the user whose user ID matches the
* 'node_access_test.no_access_uid' state variable. Access control on this
* realm is not provided in this module; instead,
* NodeQueryAlterTest::testNodeQueryAlterOverride() manually writes a node
* access record defining the access control for this realm.
*
* @see \Drupal\node\Tests\NodeQueryAlterTest::testNodeQueryAlterOverride()
* @see \Drupal\node\Tests\NodeAccessPagerTest
* @see node_access_test.permissions.yml
* @see node_access_test_node_access_records()
*/
function node_access_test_node_grants($account, $op) {
$grants = [];
$grants['node_access_test_author'] = [$account->id()];
if ($op == 'view' && $account->hasPermission('node test view', $account)) {
$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'] = [0];
}
return $grants;
}
/**
* Implements hook_node_access_records().
*
* By default, records are written for all nodes. When the
* 'node_access_test.private' state variable is set to TRUE, records
* are only written for nodes with a "private" property set, which causes the
* Node module to write the default global view grant for nodes that are not
* marked private.
*
* @see \Drupal\node\Tests\NodeAccessBaseTableTest::setUp()
* @see node_access_test_node_grants()
* @see node_access_test.permissions.yml
*/
function node_access_test_node_access_records(NodeInterface $node) {
$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[] = [
'realm' => 'node_access_test',
'gid' => 8888,
'grant_view' => 1,
'grant_update' => 0,
'grant_delete' => 0,
'priority' => 0,
];
$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[] = [
'realm' => 'node_access_test_author',
'gid' => $node->getOwnerId(),
'grant_view' => 1,
'grant_update' => 1,
'grant_delete' => 1,
'priority' => 0,
];
}
return $grants;
}
/**
* Adds the private field to a node type.
*
* @param \Drupal\node\NodeTypeInterface $type
* A node type entity.
*/
function node_access_test_add_field(NodeTypeInterface $type) {
$field_storage = FieldStorageConfig::create([
'field_name' => 'private',
'entity_type' => 'node',
'type' => 'integer',
]);
$field_storage->save();
$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', [
'type' => 'number',
])
->save();
}
/**
* Implements hook_node_access().
*/
function node_access_test_node_access(NodeInterface $node, $op, AccountInterface $account) {
$secret_catalan = \Drupal::state()
->get('node_access_test_secret_catalan') ?: 0;
if ($secret_catalan && $node->language()->getId() == 'ca') {
// Make all Catalan content secret.
return AccessResult::forbidden()->setCacheMaxAge(0);
}
// No opinion.
return AccessResult::neutral()->setCacheMaxAge(0);
}
@@ -0,0 +1,2 @@
node test view:
title: 'View content'
@@ -0,0 +1,12 @@
name: 'Node module empty access tests'
type: module
description: 'Support module for node permission testing. Provides empty grants hook implementations.'
package: Testing
# version: VERSION
# core: 8.x
# Information added by Drupal.org packaging script on 2017-04-19
version: '8.3.1'
core: '8.x'
project: 'drupal'
datestamp: 1492622988
@@ -0,0 +1,22 @@
<?php
/**
* @file
* Empty node access hook implementations.
*/
use Drupal\node\NodeInterface;
/**
* Implements hook_node_grants().
*/
function node_access_test_empty_node_grants($account, $op) {
return [];
}
/**
* Implements hook_node_access_records().
*/
function node_access_test_empty_node_access_records(NodeInterface $node) {
return [];
}
@@ -0,0 +1,14 @@
name: 'Node module access tests language'
type: module
description: 'Support module for language-aware node access testing.'
package: Testing
# version: VERSION
# core: 8.x
dependencies:
- options
# Information added by Drupal.org packaging script on 2017-04-19
version: '8.3.1'
core: '8.x'
project: 'drupal'
datestamp: 1492622988
@@ -0,0 +1,44 @@
<?php
/**
* @file
* Test module with a language-aware node access implementation.
*
* The module adds a 'private' field to page nodes that allows each translation
* of the node to be marked as private (viewable only by administrators).
*/
use Drupal\node\NodeInterface;
/**
* Implements hook_node_grants().
*
* 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'] = [7888];
return $grants;
}
/**
* Implements hook_node_access_records().
*/
function node_access_test_language_node_access_records(NodeInterface $node) {
$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[] = [
'realm' => 'node_access_language_test',
'gid' => 7888,
'grant_view' => empty($translation->field_private->value) ? 1 : 0,
'grant_update' => 0,
'grant_delete' => 0,
'priority' => 0,
'langcode' => $langcode,
];
}
return $grants;
}
@@ -0,0 +1,12 @@
name: 'Node module tests'
type: module
description: 'Support module for node related testing.'
package: Testing
# version: VERSION
# core: 8.x
# Information added by Drupal.org packaging script on 2017-04-19
version: '8.3.1'
core: '8.x'
project: 'drupal'
datestamp: 1492622988
@@ -0,0 +1,192 @@
<?php
/**
* @file
* A dummy module for testing node related hooks.
*
* This is a dummy module that implements node related hooks to test API
* interaction with the Node module.
*/
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[] = [
'key' => 'testElement',
'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'] = [
'#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'] = [
'#markup' => '<p>' . t('Extra data that should appear everywhere except the RSS feed for node @nid.', ['@nid' => $node->id()]) . '</p>',
];
}
}
/**
* Implements hook_ENTITY_TYPE_build_defaults_alter() for node entities.
*/
function node_test_node_build_defaults_alter(array &$build, NodeInterface &$node, $view_mode = 'full') {
if ($view_mode == 'rss') {
$node->rss_namespaces['xmlns:drupaltest'] = 'http://example.com/test-namespace';
}
}
/**
* Implements hook_node_grants().
*/
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 [
'test_article_realm' => [1],
'test_page_realm' => [1],
'test_alter_realm' => [2],
];
}
/**
* Implements hook_node_access_records().
*/
function node_test_node_access_records(NodeInterface $node) {
// Return nothing when testing for empty responses.
if (!empty($node->disable_node_access)) {
return;
}
$grants = [];
if ($node->getType() == 'article') {
// Create grant in arbitrary article_realm for article nodes.
$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[] = [
'realm' => 'test_page_realm',
'gid' => 1,
'grant_view' => 1,
'grant_update' => 0,
'grant_delete' => 0,
'priority' => 0,
];
}
return $grants;
}
/**
* Implements hook_node_access_records_alter().
*/
function node_test_node_access_records_alter(&$grants, NodeInterface $node) {
if (!empty($grants)) {
foreach ($grants as $key => $grant) {
// Alter grant from test_page_realm to test_alter_realm and modify the gid.
if ($grant['realm'] == 'test_page_realm' && $node->isPromoted()) {
$grants[$key]['realm'] = 'test_alter_realm';
$grants[$key]['gid'] = 2;
}
}
}
}
/**
* Implements hook_node_grants_alter().
*/
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 = [];
}
/**
* Implements hook_ENTITY_TYPE_presave() for node entities.
*/
function node_test_node_presave(NodeInterface $node) {
if ($node->getTitle() == 'testing_node_presave') {
// Sun, 19 Nov 1978 05:00:00 GMT
$node->setCreatedTime(280299600);
// Drupal 1.0 release.
$node->changed = 979534800;
}
// Determine changes.
if (!empty($node->original) && $node->original->getTitle() == 'test_changes') {
if ($node->original->getTitle() != $node->getTitle()) {
$node->title->value .= '_presave';
}
}
}
/**
* Implements hook_ENTITY_TYPE_update() for node entities.
*/
function node_test_node_update(NodeInterface $node) {
// Determine changes on update.
if (!empty($node->original) && $node->original->getTitle() == 'test_changes') {
if ($node->original->getTitle() != $node->getTitle()) {
$node->title->value .= '_update';
}
}
}
/**
* Implements hook_entity_view_mode_alter().
*/
function node_test_entity_view_mode_alter(&$view_mode, EntityInterface $entity, $context) {
// Only alter the view mode if we are on the test callback.
$change_view_mode = \Drupal::state()->get( 'node_test_change_view_mode') ?: '';
if ($change_view_mode) {
$view_mode = $change_view_mode;
}
}
/**
* Implements hook_ENTITY_TYPE_insert() for node entities.
*
* This tests saving a node on node insert.
*
* @see \Drupal\node\Tests\NodeSaveTest::testNodeSaveOnInsert()
*/
function node_test_node_insert(NodeInterface $node) {
// Set the node title to the node ID and save.
if ($node->getTitle() == 'new') {
$node->setTitle('Node ' . $node->id());
$node->setNewRevision(FALSE);
$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');
}
}
@@ -0,0 +1,9 @@
type: default
name: Default
description: 'Default description.'
help: ''
new_revision: true
display_submitted: true
preview_mode: 1
status: true
langcode: en
@@ -0,0 +1,12 @@
name: 'Node configuration tests'
type: module
description: 'Support module for node configuration tests.'
# core: 8.x
package: Testing
# version: VERSION
# Information added by Drupal.org packaging script on 2017-04-19
version: '8.3.1'
core: '8.x'
project: 'drupal'
datestamp: 1492622988
@@ -0,0 +1,9 @@
type: import
name: Import
description: 'Import description.'
help: ''
new_revision: false
display_submitted: true
preview_mode: 1
status: true
langcode: en
@@ -0,0 +1,12 @@
name: 'Node module exception tests'
type: module
description: 'Support module for node related exception testing.'
package: Testing
# version: VERSION
# core: 8.x
# Information added by Drupal.org packaging script on 2017-04-19
version: '8.3.1'
core: '8.x'
project: 'drupal'
datestamp: 1492622988
@@ -0,0 +1,17 @@
<?php
/**
* @file
* A module implementing node related hooks to test API interaction.
*/
use Drupal\node\NodeInterface;
/**
* Implements hook_ENTITY_TYPE_insert() for node entities.
*/
function node_test_exception_node_insert(NodeInterface $node) {
if ($node->getTitle() == 'testing_transaction_exception') {
throw new Exception('Test exception for rollback.');
}
}
@@ -0,0 +1,16 @@
name: 'Node test views'
type: module
description: 'Provides default views for views node tests.'
package: Testing
# version: VERSION
# core: 8.x
dependencies:
- node
- views
- language
# Information added by Drupal.org packaging script on 2017-04-19
version: '8.3.1'
core: '8.x'
project: 'drupal'
datestamp: 1492622988
@@ -0,0 +1,16 @@
<?php
/**
* @file
* Provides views data and hooks for node_test_views module.
*/
/**
* Implements hook_views_data_alter().
*/
function node_test_views_views_data_alter(array &$data) {
// Make node language use the basic field handler if requested.
if (\Drupal::state()->get('node_test_views.use_basic_handler')) {
$data['node_field_data']['langcode']['field']['id'] = 'language';
}
}
@@ -0,0 +1,99 @@
langcode: en
status: true
dependencies:
module:
- node
- user
id: test_contextual_links
label: 'Contextual links'
module: node
description: ''
tag: default
base_table: node_field_data
base_field: nid
core: 8.x
display:
default:
display_options:
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
pager:
type: full
options:
items_per_page: 10
offset: 0
id: 0
total_pages: null
expose:
items_per_page: false
items_per_page_label: 'Items per page'
items_per_page_options: '5, 10, 25, 50'
items_per_page_options_all: false
items_per_page_options_all_label: '- All -'
offset: false
offset_label: Offset
tags:
previous: ' Previous'
next: 'Next '
first: '« First'
last: 'Last »'
quantity: 9
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
row:
type: 'entity:node'
options:
view_mode: teaser
style:
type: default
options:
grouping: { }
row_class: ''
default_row_class: true
uses_fields: false
title: ''
header: { }
footer: { }
relationships: { }
fields: { }
arguments: { }
display_plugin: default
display_title: Master
id: default
position: 0
page_1:
display_options:
path: node/%/contextual-links
defaults:
arguments: false
arguments:
nid:
field: nid
id: nid
relationship: none
table: node_field_data
plugin_id: numeric
entity_type: node
entity_field: nid
menu:
type: tab
title: 'Test contextual link'
description: ''
menu_name: tools
weight: 0
context: '1'
display_plugin: page
display_title: Page
id: page_1
position: 1
@@ -0,0 +1,376 @@
langcode: en
status: true
dependencies:
module:
- node
- user
id: test_field_filters
label: 'Test field filters'
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: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: none
options:
items_per_page: 0
offset: 0
style:
type: default
row:
type: 'entity:node'
options:
view_mode: teaser
fields:
title:
id: title
table: node_field_data
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
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
entity_type: node
entity_field: title
plugin_id: field
filters:
status:
value: '1'
table: node_field_data
field: status
id: status
expose:
operator: ''
group: 1
plugin_id: boolean
entity_type: node
entity_field: status
title:
id: title
table: node_field_data
field: title
relationship: none
group_type: group
admin_label: ''
operator: contains
value: Paris
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: string
entity_type: node
entity_field: title
sorts:
created:
id: created
table: node_field_data
field: created
order: DESC
relationship: none
group_type: group
admin_label: ''
exposed: false
expose:
label: ''
granularity: second
plugin_id: date
entity_type: node
entity_field: created
title: 'Test field filters'
header: { }
footer: { }
empty: { }
relationships: { }
arguments: { }
rendering_language: '***LANGUAGE_entity_translation***'
page_bf:
display_plugin: page
id: page_bf
display_title: 'Body filter page'
position: 1
display_options:
path: test-body-filter
display_description: ''
title: 'Test body filters'
defaults:
title: false
filters: false
filter_groups: false
filters:
status:
value: '1'
table: node_field_data
field: status
id: status
expose:
operator: ''
group: 1
plugin_id: boolean
entity_type: node
entity_field: status
body_value:
id: body_value
table: node__body
field: body_value
relationship: none
group_type: group
admin_label: ''
operator: contains
value: Comida
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: string
entity_type: node
entity_field: body
filter_groups:
operator: AND
groups:
1: AND
page_bfp:
display_plugin: page
id: page_bfp
display_title: 'Body filter page Paris'
position: 1
display_options:
path: test-body-paris
display_description: ''
title: 'Test body filters'
defaults:
title: false
filters: false
filter_groups: false
filters:
status:
value: '1'
table: node_field_data
field: status
id: status
expose:
operator: ''
group: 1
entity_type: node
entity_field: status
plugin_id: boolean
body_value:
id: body_value
table: node__body
field: body_value
relationship: none
group_type: group
admin_label: ''
operator: contains
value: Paris
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: string
entity_type: node
entity_field: body
filter_groups:
operator: AND
groups:
1: AND
page_tf:
display_plugin: page
id: page_tf
display_title: 'Title filter page'
position: 1
display_options:
path: test-title-filter
display_description: ''
title: 'Test title filter'
defaults:
title: false
filters: false
filter_groups: false
filters:
status:
value: '1'
table: node_field_data
field: status
id: status
expose:
operator: ''
group: 1
entity_type: node
entity_field: status
plugin_id: boolean
title:
id: title
table: node_field_data
field: title
relationship: none
group_type: group
admin_label: ''
operator: contains
value: Comida
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: string
entity_type: node
entity_field: title
filter_groups:
operator: AND
groups:
1: AND
page_tfp:
display_plugin: page
id: page_tfp
display_title: 'Title filter page Paris'
position: 1
display_options:
path: test-title-paris
display_description: ''
title: 'Test title filter'
defaults:
title: false
@@ -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: { }
@@ -0,0 +1,71 @@
langcode: en
status: true
dependencies:
module:
- node
- user
id: test_filter_node_uid_revision
label: test_filter_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:
uid_revision:
admin_label: ''
field: uid_revision
id: uid_revision
is_grouped: false
operator: in
relationship: none
table: node_field_data
value:
- '1'
plugin_id: node_uid_revision
entity_type: node
entity_field: uid_revision
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_plugin: default
display_title: Master
id: default
position: 0
@@ -0,0 +1,292 @@
langcode: en
status: true
dependencies:
module:
- node
- user
id: test_language
label: 'Test language'
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: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: none
options:
items_per_page: 0
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
relationship: none
group_type: group
admin_label: ''
label: ''
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: false
ellipsis: false
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: false
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
plugin_id: field
entity_type: node
entity_field: title
langcode:
id: langcode
table: node_field_data
field: langcode
relationship: none
group_type: group
admin_label: ''
label: Language
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
plugin_id: field
entity_type: node
entity_field: langcode
settings:
native_language: false
type: language
filters:
status:
value: '1'
table: node_field_data
field: status
id: status
expose:
operator: ''
group: 1
plugin_id: boolean
entity_type: node
entity_field: status
type:
id: type
table: node_field_data
field: type
value:
page: page
plugin_id: bundle
entity_type: node
entity_field: type
langcode:
id: langcode
table: node_field_data
field: langcode
relationship: none
group_type: group
admin_label: ''
operator: in
value:
fr: fr
es: es
und: und
group: 1
exposed: false
expose:
operator_id: ''
label: ''
description: ''
use_operator: false
operator: ''
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
reduce: false
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: language
entity_type: node
entity_field: langcode
sorts:
langcode:
id: langcode
table: node_field_data
field: langcode
relationship: none
group_type: group
admin_label: ''
order: ASC
exposed: false
expose:
label: ''
plugin_id: standard
entity_type: node
entity_field: langcode
title: 'Language filter test'
header: { }
footer: { }
empty: { }
relationships: { }
arguments:
langcode:
id: langcode
table: node_field_data
field: langcode
relationship: none
group_type: group
admin_label: ''
default_action: ignore
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: { }
plugin_id: language
entity_type: node
entity_field: langcode
page_1:
display_plugin: page
id: page_1
display_title: Page
position: 1
display_options:
path: test-language
@@ -0,0 +1,192 @@
langcode: en
status: true
dependencies:
module:
- node
- user
id: test_nid_argument
label: test_nid_argument
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: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: none
options:
items_per_page: null
offset: 0
style:
type: default
row:
type: fields
fields:
nid:
id: nid
table: node_field_data
field: nid
relationship: none
group_type: group
admin_label: ''
label: ''
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: false
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: value
type: number_integer
settings:
thousand_separator: ''
prefix_suffix: true
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
entity_type: node
entity_field: nid
plugin_id: field
filters: { }
sorts: { }
title: test_nid_argument
header: { }
footer: { }
empty: { }
relationships: { }
arguments:
nid:
id: nid
table: node_field_data
field: nid
relationship: none
group_type: group
admin_label: ''
default_action: ignore
exception:
value: all
title_enable: false
title: All
title_enable: true
title: '{{ arguments.nid }}'
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
entity_field: nid
plugin_id: node_nid
display_extenders: { }
cache_metadata:
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- 'user.node_grants:view'
- user.permissions
cacheable: false
page_1:
display_plugin: page
id: page_1
display_title: Page
position: 1
display_options:
display_extenders: { }
path: test-nid-argument
cache_metadata:
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- 'user.node_grants:view'
- user.permissions
cacheable: false
@@ -0,0 +1,69 @@
langcode: en
status: true
dependencies:
module:
- node
id: test_node_bulk_form
label: ''
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: null
display_options:
style:
type: table
row:
type: fields
fields:
node_bulk_form:
id: node_bulk_form
table: node
field: node_bulk_form
plugin_id: node_bulk_form
entity_type: node
title:
id: title
table: node_field_data
field: title
plugin_id: field
entity_type: node
entity_field: title
sorts:
nid:
id: nid
table: node_field_data
field: nid
order: ASC
plugin_id: standard
entity_type: node
entity_field: nid
langcode:
id: langcode
table: node_field_data
field: langcode
relationship: none
group_type: group
admin_label: ''
order: ASC
exposed: false
expose:
label: ''
entity_type: node
entity_field: langcode
plugin_id: standard
display_extenders: { }
page_1:
display_plugin: page
id: page_1
display_title: Page
position: null
display_options:
path: test-node-bulk-form
@@ -0,0 +1,182 @@
langcode: en
status: true
dependencies:
module:
- node
- user
id: test_node_path_plugin
label: test_node_path_plugin
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: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: full
options:
items_per_page: 10
offset: 0
id: 0
total_pages: null
expose:
items_per_page: false
items_per_page_label: 'Items per page'
items_per_page_options: '5, 10, 25, 50'
items_per_page_options_all: false
items_per_page_options_all_label: '- All -'
offset: false
offset_label: Offset
tags:
previous: ' Previous'
next: 'Next '
first: '« First'
last: 'Last »'
quantity: 9
style:
type: default
row:
type: fields
fields:
path:
id: path
table: node
field: path
relationship: none
group_type: group
admin_label: ''
label: ''
exclude: false
alter:
alter_text: true
text: 'This is <strong>not escaped</strong> and this is <a href="{{ path }}" hreflang="en">the link</a>.'
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: false
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
absolute: false
entity_type: node
plugin_id: node_path
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
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_node_path_plugin
header: { }
footer: { }
empty: { }
relationships: { }
arguments: { }
display_extenders: { }
cache_metadata:
max-age: -1
contexts:
- 'languages:language_interface'
- url.query_args
- '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-node-path-plugin
cache_metadata:
max-age: -1
contexts:
- 'languages:language_interface'
- url.query_args
- 'user.node_grants:view'
- user.permissions
tags: { }
@@ -0,0 +1,224 @@
langcode: en
status: true
dependencies:
module:
- node
id: test_node_revision_links
label: test_node_revision_links
module: views
description: ''
tag: ''
base_table: node_field_revision
base_field: vid
core: '8'
display:
default:
display_plugin: default
id: default
display_title: Master
position: 0
display_options:
access:
type: none
options: { }
cache:
type: none
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: none
options:
items_per_page: 0
offset: 0
style:
type: default
row:
type: fields
fields:
link_to_revision:
id: link_to_revision
table: node_field_revision
field: link_to_revision
relationship: none
group_type: group
admin_label: ''
label: ''
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: false
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
text: 'Link to revision'
entity_type: node
plugin_id: node_revision_link
delete_revision:
id: delete_revision
table: node_field_revision
field: delete_revision
relationship: none
group_type: group
admin_label: ''
label: ''
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: false
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
text: 'Link to delete revision'
entity_type: node
plugin_id: node_revision_link_delete
revert_revision:
id: revert_revision
table: node_field_revision
field: revert_revision
relationship: none
group_type: group
admin_label: ''
label: ''
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: false
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
text: 'Link to delete revision'
entity_type: node
plugin_id: node_revision_link_revert
filters: { }
sorts: { }
title: test_node_revision_links
header: { }
footer: { }
empty: { }
relationships: { }
arguments: { }
display_extenders: { }
page_1:
display_plugin: page
id: page_1
display_title: Page
position: 1
display_options:
display_extenders: { }
path: test-node-revision-links
@@ -0,0 +1,67 @@
langcode: en
status: true
dependencies:
module:
- node
id: test_node_revision_nid
label: null
module: views
description: ''
tag: ''
base_table: node_field_revision
base_field: nid
core: '8'
display:
default:
display_options:
relationships:
nid:
id: nid
table: node_field_revision
field: nid
required: true
plugin_id: field
fields:
vid:
id: vid
table: node_field_revision
field: vid
plugin_id: field
entity_type: node
entity_field: vid
nid_1:
id: nid_1
table: node_field_revision
field: nid
plugin_id: field
entity_type: node
entity_field: nid
nid:
id: nid
table: node_field_data
field: nid
relationship: nid
plugin_id: field
entity_type: node
entity_field: nid
arguments:
nid:
id: nid
table: node_field_revision
field: nid
plugin_id: node_nid
entity_type: node
entity_field: nid
sorts:
vid:
id: vid
table: node_field_revision
field: vid
order: ASC
plugin_id: field
entity_type: node
entity_field: vid
display_plugin: default
display_title: Master
id: default
position: 0
@@ -0,0 +1,44 @@
langcode: en
status: true
dependencies:
module:
- node
id: test_node_revision_timestamp
label: null
module: views
description: ''
tag: ''
base_table: node_field_revision
base_field: vid
core: '8'
display:
default:
display_options:
fields:
vid:
id: vid
table: node_field_revision
field: vid
plugin_id: field
entity_type: node
entity_field: vid
revision_timestamp:
id: revision_timestamp
table: node_revision
field: revision_timestamp
plugin_id: field
entity_type: node
entity_field: revision_timestamp
sorts:
revision_timestamp:
id: revision_timestamp
table: node_revision
field: revision_timestamp
order: DESC
plugin_id: field
entity_type: node
entity_field: revision_timestamp
display_plugin: default
display_title: Master
id: default
position: 0
@@ -0,0 +1,58 @@
langcode: en
status: true
dependencies:
module:
- node
id: test_node_revision_vid
label: null
module: views
description: ''
tag: ''
base_table: node_field_revision
base_field: nid
core: '8'
display:
default:
display_options:
relationships:
vid:
id: vid
table: node_field_revision
field: vid
required: true
plugin_id: standard
fields:
vid:
id: vid
table: node_field_revision
field: vid
plugin_id: field
entity_type: node
entity_field: vid
nid_1:
id: nid_1
table: node_field_revision
field: nid
plugin_id: field
entity_type: node
entity_field: nid
nid:
id: nid
table: node_field_data
field: nid
relationship: vid
plugin_id: field
entity_type: node
entity_field: nid
arguments:
nid:
id: nid
table: node_field_revision
field: nid
plugin_id: node_nid
entity_type: node
entity_field: nid
display_plugin: default
display_title: Master
id: default
position: 0
@@ -0,0 +1,60 @@
langcode: en
status: true
dependencies:
module:
- node
- user
id: test_node_row_plugin
label: test_node_row_plugin
module: views
description: ''
tag: default
base_table: node_field_data
base_field: nid
core: '8'
display:
default:
display_options:
access:
type: perm
cache:
type: tag
exposed_form:
type: basic
filters:
status:
expose:
operator: ''
field: status
group: 1
id: status
table: node_field_data
value: '1'
plugin_id: boolean
entity_type: node
entity_field: status
pager:
options:
items_per_page: 10
type: full
query:
type: views_query
row:
options:
view_mode: teaser
type: 'entity:node'
sorts: { }
style:
type: default
title: test_node_row_plugin
display_plugin: default
display_title: Master
id: default
position: 0
page_1:
display_options:
path: test-node-row-plugin
display_plugin: page
display_title: Page
id: page_1
position: 0
@@ -0,0 +1,194 @@
langcode: en
status: true
dependencies:
config:
- field.storage.node.body
module:
- node
- text
- user
id: test_node_tokens
label: test_node_tokens
module: views
description: 'Verifies tokens provided by the Node module are replaced correctly.'
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: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: full
options:
items_per_page: 10
offset: 0
id: 0
total_pages: null
expose:
items_per_page: false
items_per_page_label: 'Items per page'
items_per_page_options: '5, 10, 25, 50'
items_per_page_options_all: false
items_per_page_options_all_label: '- All -'
offset: false
offset_label: Offset
tags:
previous: ' Previous'
next: 'Next '
first: '« First'
last: 'Last »'
quantity: 9
style:
type: default
options:
grouping: { }
row_class: ''
default_row_class: true
uses_fields: false
row:
type: fields
options:
inline: { }
separator: ''
hide_empty: false
default_field_elements: true
fields:
body:
id: body
table: node__body
field: body
relationship: none
group_type: group
admin_label: ''
label: ''
exclude: false
alter:
alter_text: true
text: "Body: {{ body }}<br />\nRaw value: {{ body__value }}<br />\nRaw summary: {{ body__summary }}<br />\nRaw format: {{ body__format }}"
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: false
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: value
type: text_default
settings: { }
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
plugin_id: field
filters: { }
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
header: { }
footer: { }
empty: { }
relationships: { }
arguments: { }
display_extenders: { }
cache_metadata:
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url.query_args
- 'user.node_grants:view'
- user.permissions
cacheable: false
page_1:
display_plugin: page
id: page_1
display_title: Page
position: 1
display_options:
display_extenders: { }
path: test_node_tokens
cache_metadata:
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url.query_args
- 'user.node_grants:view'
- user.permissions
cacheable: false
@@ -0,0 +1,205 @@
langcode: en
status: true
dependencies:
module:
- node
- user
id: test_node_view
label: test_node_view
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: null
display_options:
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: full
options:
items_per_page: 10
offset: 0
id: 0
total_pages: null
expose:
items_per_page: false
items_per_page_label: 'Items per page'
items_per_page_options: '5, 10, 25, 50'
items_per_page_options_all: false
items_per_page_options_all_label: '- All -'
offset: false
offset_label: Offset
tags:
previous: ' Previous'
next: 'Next '
first: '« First'
last: 'Last »'
quantity: 9
style:
type: default
row:
type: fields
fields:
nid:
id: nid
table: node_field_data
field: nid
relationship: none
group_type: group
admin_label: ''
label: Nid
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
plugin_id: field
entity_type: node
entity_field: nid
filters:
status:
value: '1'
table: node_field_data
field: status
id: status
expose:
operator: ''
group: 1
plugin_id: boolean
entity_type: node
entity_field: status
sorts:
created:
id: created
table: node_field_data
field: created
order: DESC
relationship: none
group_type: group
admin_label: ''
exposed: false
expose:
label: ''
granularity: second
plugin_id: date
entity_type: node
entity_field: created
title: test_node_view
header: { }
footer: { }
empty: { }
relationships: { }
arguments:
type:
id: type
table: node_field_data
field: type
relationship: none
group_type: group
admin_label: ''
default_action: 'not found'
exception:
value: all
title_enable: false
title: All
title_enable: true
title: '{{ arguments.type }}'
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: { }
glossary: false
limit: 0
case: none
path_case: none
transform_dash: false
break_phrase: false
plugin_id: node_type
entity_type: node
entity_field: type
page_1:
display_plugin: page
id: page_1
display_title: Page
position: null
display_options:
path: test-node-view
@@ -0,0 +1,145 @@
langcode: en
status: true
dependencies:
module:
- node
- user
id: test_status_extra
label: test_status_extra
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: null
display_options:
access:
type: perm
cache:
type: tag
query:
type: views_query
exposed_form:
type: basic
pager:
type: full
style:
type: default
row:
type: fields
fields:
title:
id: title
table: node_field_data
field: title
relationship: none
group_type: group
admin_label: ''
label: Title
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
plugin_id: field
entity_type: node
entity_field: title
filters:
status_extra:
id: status_extra
table: node_field_data
field: status_extra
relationship: none
group_type: group
admin_label: ''
operator: '='
value: false
group: 1
exposed: false
expose:
operator_id: '0'
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_status
entity_type: node
sorts:
nid:
id: nid
table: node_field_data
field: nid
order: ASC
plugin_id: standard
entity_type: node
entity_field: nid
filter_groups:
operator: AND
groups:
1: AND
page_1:
display_options:
path: test_status_extra
display_plugin: page
display_title: Page
id: page_1
position: 0
@@ -0,0 +1,12 @@
name: 'Node module access automatic cacheability bubbling tests'
type: module
description: 'Support module for node permission testing. Provides a route which does a node access query without explicitly specifying the corresponding cache context.'
package: Testing
# version: VERSION
# core: 8.x
# Information added by Drupal.org packaging script on 2017-04-19
version: '8.3.1'
core: '8.x'
project: 'drupal'
datestamp: 1492622988
@@ -0,0 +1,6 @@
node_access_test_auto_bubbling:
path: '/node_access_test_auto_bubbling'
defaults:
_controller: '\Drupal\node_access_test_auto_bubbling\Controller\NodeAccessTestAutoBubblingController::latest'
requirements:
_access: 'TRUE'
@@ -0,0 +1,29 @@
<?php
namespace Drupal\node_access_test_auto_bubbling\Controller;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\node\NodeInterface;
/**
* Returns a node ID listing.
*/
class NodeAccessTestAutoBubblingController extends ControllerBase implements ContainerInjectionInterface {
/**
* Lists the three latest published node IDs.
*
* @return array
* A render array.
*/
public function latest() {
$nids = $this->entityTypeManager()->getStorage('node')->getQuery()
->condition('status', NodeInterface::PUBLISHED)
->sort('created', 'DESC')
->range(0, 3)
->execute();
return ['#markup' => $this->t('The three latest nodes are: @nids.', ['@nids' => implode(', ', $nids)])];
}
}
@@ -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'];
/**
* {@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,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,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,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,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,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,187 @@
<?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 {
protected $nodes;
protected $revisionLogs;
protected $profile = "standard";
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 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,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,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,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,31 @@
<?php
namespace Drupal\Tests\node\Kernel\Action;
use Drupal\KernelTests\KernelTestBase;
use Drupal\system\Entity\Action;
/**
* @group node
*/
class UnpublishByKeywordActionTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['action', 'node', 'system', 'user'];
/**
* Tests creating an action using the node_unpublish_by_keyword_action plugin.
*
* @see https://www.drupal.org/node/2578519
*/
public function testUnpublishByKeywordAction() {
Action::create([
'id' => 'foo',
'label' => 'Foobaz',
'plugin' => 'node_unpublish_by_keyword_action',
])->save();
}
}
@@ -0,0 +1,59 @@
<?php
namespace Drupal\Tests\node\Kernel\Config;
use Drupal\node\Entity\NodeType;
use Drupal\KernelTests\KernelTestBase;
/**
* Change content types during config create method invocation.
*
* @group node
*/
class NodeImportChangeTest extends KernelTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['node', 'field', 'text', 'system', 'node_test_config', 'user'];
/**
* Set the default field storage backend for fields created during tests.
*/
protected function setUp() {
parent::setUp();
// Set default storage backend.
$this->installConfig(['field', 'node_test_config']);
}
/**
* Tests importing an updated content type.
*/
public function testImportChange() {
$node_type_id = 'default';
$node_type_config_name = "node.type.$node_type_id";
// Simulate config data to import:
// - a modified version (modified label) of the node type config.
$active = $this->container->get('config.storage');
$sync = $this->container->get('config.storage.sync');
$this->copyConfig($active, $sync);
$node_type = $active->read($node_type_config_name);
$new_label = 'Test update import field';
$node_type['name'] = $new_label;
// Save as files in the sync directory.
$sync->write($node_type_config_name, $node_type);
// Import the content of the sync directory.
$this->configImporter()->import();
// Check that the updated config was correctly imported.
$node_type = NodeType::load($node_type_id);
$this->assertEqual($node_type->label(), $new_label, 'Node type name has been updated.');
}
}
@@ -0,0 +1,75 @@
<?php
namespace Drupal\Tests\node\Kernel\Config;
use Drupal\field\Entity\FieldConfig;
use Drupal\node\Entity\NodeType;
use Drupal\KernelTests\KernelTestBase;
/**
* Create content types during config create method invocation.
*
* @group node
*/
class NodeImportCreateTest extends KernelTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['node', 'field', 'text', 'system', 'user'];
/**
* Set the default field storage backend for fields created during tests.
*/
protected function setUp() {
parent::setUp();
$this->installEntitySchema('user');
// Set default storage backend.
$this->installConfig(['field']);
}
/**
* Tests creating a content type during default config import.
*/
public function testImportCreateDefault() {
$node_type_id = 'default';
// Check that the content type does not exist yet.
$this->assertFalse(NodeType::load($node_type_id));
// 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(['node_test_config']);
$node_type = NodeType::load($node_type_id);
$this->assertTrue($node_type, 'The default content type was created.');
}
/**
* Tests creating a content type during config import.
*/
public function testImportCreate() {
$node_type_id = 'import';
$node_type_config_name = "node.type.$node_type_id";
// Simulate config data to import.
$active = $this->container->get('config.storage');
$sync = $this->container->get('config.storage.sync');
$this->copyConfig($active, $sync);
// Manually add new node type.
$src_dir = __DIR__ . '/../../../modules/node_test_config/sync';
$target_dir = config_get_config_directory(CONFIG_SYNC_DIRECTORY);
$this->assertTrue(file_unmanaged_copy("$src_dir/$node_type_config_name.yml", "$target_dir/$node_type_config_name.yml"));
// Import the content of the sync directory.
$this->configImporter()->import();
// Check that the content type was created.
$node_type = NodeType::load($node_type_id);
$this->assertTrue($node_type, 'Import node type from sync was created.');
$this->assertFalse(FieldConfig::loadByName('node', $node_type_id, 'body'));
}
}
@@ -0,0 +1,43 @@
<?php
namespace Drupal\Tests\node\Kernel\Migrate;
use Drupal\Tests\migrate_drupal\Kernel\MigrateDrupalTestBase;
use Drupal\migrate_drupal\Tests\StubTestTrait;
use Drupal\node\Entity\NodeType;
/**
* Test stub creation for nodes.
*
* @group node
*/
class MigrateNodeStubTest extends MigrateDrupalTestBase {
use StubTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['node'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installEntitySchema('node');
// Need at least one node type present.
NodeType::create([
'type' => 'testnodetype',
'name' => 'Test node type',
])->save();
}
/**
* Tests creation of node stubs.
*/
public function testStub() {
$this->performStubTest('node');
}
}
@@ -0,0 +1,61 @@
<?php
namespace Drupal\Tests\node\Kernel\Migrate\d6;
use Drupal\Core\Field\Entity\BaseFieldOverride;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
use Drupal\node\Entity\Node;
/**
* Test migrating node settings into the base_field_bundle_override config entity.
*
* @group migrate_drupal_6
*/
class MigrateNodeBundleSettingsTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installConfig(['node']);
$this->executeMigration('d6_node_type');
// Create a config entity that already exists.
BaseFieldOverride::create([
'field_name' => 'promote',
'entity_type' => 'node',
'bundle' => 'page',
])->save();
$this->executeMigrations([
'd6_node_setting_promote',
'd6_node_setting_status',
'd6_node_setting_sticky'
]);
}
/**
* Tests Drupal 6 node type settings to Drupal 8 migration.
*/
public function testNodeBundleSettings() {
// Test settings on test_page bundle.
$node = Node::create(['type' => 'test_page']);
$this->assertIdentical(1, $node->status->value);
$this->assertIdentical(1, $node->promote->value);
$this->assertIdentical(1, $node->sticky->value);
// Test settings for test_story bundle.
$node = Node::create(['type' => 'test_story']);
$this->assertIdentical(1, $node->status->value);
$this->assertIdentical(1, $node->promote->value);
$this->assertIdentical(0, $node->sticky->value);
// Test settings for the test_event bundle.
$node = Node::create(['type' => 'test_event']);
$this->assertIdentical(0, $node->status->value);
$this->assertIdentical(0, $node->promote->value);
$this->assertIdentical(1, $node->sticky->value);
}
}
@@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\node\Kernel\Migrate\d6;
use Drupal\Tests\SchemaCheckTestTrait;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Upgrade variables to node.settings.yml.
*
* @group migrate_drupal_6
*/
class MigrateNodeConfigsTest extends MigrateDrupal6TestBase {
use SchemaCheckTestTrait;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->executeMigration('d6_node_settings');
}
/**
* Tests Drupal 6 node settings to Drupal 8 migration.
*/
public function testNodeSettings() {
$config = $this->config('node.settings');
$this->assertIdentical(FALSE, $config->get('use_admin_theme'));
$this->assertConfigSchema(\Drupal::service('config.typed'), 'node.settings', $config->get());
}
}
@@ -0,0 +1,50 @@
<?php
namespace Drupal\Tests\node\Kernel\Migrate\d6;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Test D6NodeDeriver.
*
* @group migrate_drupal_6
*/
class MigrateNodeDeriverTest extends MigrateDrupal6TestBase {
/**
* 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('d6_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']);
$migrations = $this->pluginManager->createInstances('d6_node_translation');
$this->assertArrayHasKey('d6_node_translation:story', $migrations,
"Node translation migrations exist after content_translation installed");
}
}
@@ -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 MigrateNodeSettingPromoteTest extends MigrateDrupal6TestBase {
public static $modules = ['node', 'text'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installConfig(['node']);
$this->executeMigration('d6_node_type');
$this->executeMigration('d6_node_setting_promote');
}
/**
* Tests migration of the promote checkbox's settings.
*/
public function testMigration() {
$this->assertIdentical('Promoted to front page', BaseFieldOverride::load('node.article.promote')->label());
}
}
@@ -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'];
/**
* {@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());
}
}
@@ -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 MigrateNodeSettingStickyTest extends MigrateDrupal6TestBase {
public static $modules = ['node', 'text'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installConfig(['node']);
$this->executeMigration('d6_node_type');
$this->executeMigration('d6_node_setting_sticky');
}
/**
* Tests migration of the sticky checkbox's settings.
*/
public function testMigration() {
$this->assertIdentical('Sticky at the top of lists', BaseFieldOverride::load('node.article.sticky')->label());
}
}
@@ -0,0 +1,218 @@
<?php
namespace Drupal\Tests\node\Kernel\Migrate\d6;
use Drupal\Core\Database\Database;
use Drupal\migrate\Plugin\MigrateIdMapInterface;
use Drupal\node\Entity\Node;
use Drupal\Tests\file\Kernel\Migrate\d6\FileMigrationTestTrait;
/**
* Node content migration.
*
* @group migrate_drupal_6
*/
class MigrateNodeTest extends MigrateNodeTestBase {
use FileMigrationTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['language', 'content_translation'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->setUpMigratedFiles();
$this->installSchema('file', ['file_usage']);
$this->executeMigrations([
'language',
'd6_language_content_settings',
'd6_node',
'd6_node_translation',
]);
}
/**
* Test node migration from Drupal 6 to 8.
*/
public function testNode() {
$node = Node::load(1);
$this->assertIdentical('1', $node->id(), 'Node 1 loaded.');
$this->assertIdentical('und', $node->langcode->value);
$this->assertIdentical('test', $node->body->value);
$this->assertIdentical('test', $node->body->summary);
$this->assertIdentical('filtered_html', $node->body->format);
$this->assertIdentical('story', $node->getType(), 'Node has the correct bundle.');
$this->assertIdentical('Test title', $node->getTitle(), 'Node has the correct title.');
$this->assertIdentical('1388271197', $node->getCreatedTime(), 'Node has the correct created time.');
$this->assertIdentical(FALSE, $node->isSticky());
$this->assertIdentical('1', $node->getOwnerId());
$this->assertIdentical('1420861423', $node->getRevisionCreationTime());
/** @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->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);
$this->assertIdentical('filtered_html', $node->field_test->format);
$this->assertIdentical('10', $node->field_test_two->value);
$this->assertIdentical('20', $node->field_test_two[1]->value);
$this->assertIdentical('42.42', $node->field_test_three->value, 'Single field second value is correct.');
$this->assertIdentical('3412', $node->field_test_integer_selectlist[0]->value);
$this->assertIdentical('1', $node->field_test_identical1->value, 'Integer value is correct');
$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 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);
$this->assertIdentical(['target' => '_blank'], $node->field_test_link->options['attributes']);
// Test the file field meta.
$this->assertIdentical('desc', $node->field_test_filefield->description);
$this->assertIdentical('4', $node->field_test_filefield->target_id);
$node = Node::load(2);
$this->assertIdentical('Test title rev 3', $node->getTitle());
$this->assertIdentical('test rev 3', $node->body->value);
$this->assertIdentical('filtered_html', $node->body->format);
// Test that a link field with an external link is migrated.
$this->assertIdentical('http://groups.drupal.org/', $node->field_test_link->uri);
$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);
$this->assertSame('Buy it now', $node->field_test_link->title);
$this->assertSame(['attributes' => ['target' => '_blank']], $node->field_test_link->options);
// Test that translations are working.
$node = Node::load(10);
$this->assertIdentical('en', $node->langcode->value);
$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");
// Rerun migration with two source database changes.
// 1. Add an invalid link attributes and a different URL and
// title. If only the attributes are changed the error does not occur.
Database::getConnection('default', 'migrate')
->update('content_type_story')
->fields([
'field_test_link_url' => 'https://www.drupal.org/node/2127611',
'field_test_link_title' => 'Migrate API in Drupal 8',
'field_test_link_attributes' => '',
])
->condition('nid', '2')
->condition('vid', '3')
->execute();
// 2. Add a leading slash to an internal link.
Database::getConnection('default', 'migrate')
->update('content_type_story')
->fields([
'field_test_link_url' => '/node/10',
])
->condition('nid', '9')
->condition('vid', '12')
->execute();
$this->rerunMigration();
$node = Node::load(2);
$this->assertIdentical('https://www.drupal.org/node/2127611', $node->field_test_link->uri);
$this->assertIdentical('Migrate API in Drupal 8', $node->field_test_link->title);
$this->assertIdentical([], $node->field_test_link->options['attributes']);
$node = Node::load(9);
$this->assertSame('internal:/node/10', $node->field_test_link->uri);
$this->assertSame('Buy it now', $node->field_test_link->title);
$this->assertSame(['attributes' => ['target' => '_blank']], $node->field_test_link->options);
// Test that we can re-import using the EntityContentBase destination.
$title = $this->rerunMigration();
$node = Node::load(2);
$this->assertIdentical($title, $node->getTitle());
// Test multi-column fields are correctly upgraded.
$this->assertIdentical('test rev 3', $node->body->value);
$this->assertIdentical('full_html', $node->body->format);
// Now insert a row indicating a failure and set to update later.
$title = $this->rerunMigration([
'sourceid1' => 2,
'destid1' => NULL,
'source_row_status' => MigrateIdMapInterface::STATUS_NEEDS_UPDATE,
]);
$node = Node::load(2);
$this->assertIdentical($title, $node->getTitle());
}
/**
* Execute the migration a second time.
*
* @param array $new_row
* An optional row to be inserted into the id map.
*
* @return string
* The new title in the source for vid 3.
*/
protected function rerunMigration($new_row = []) {
$title = $this->randomString();
$source_connection = Database::getConnection('default', 'migrate');
$source_connection->update('node_revisions')
->fields([
'title' => $title,
'format' => 2,
])
->condition('vid', 3)
->execute();
$migration = $this->getMigration('d6_node:story');
$table_name = $migration->getIdMap()->mapTableName();
$default_connection = \Drupal::database();
$default_connection->truncate($table_name)->execute();
if ($new_row) {
$hash = $migration->getIdMap()->getSourceIDsHash(['nid' => $new_row['sourceid1']]);
$new_row['source_ids_hash'] = $hash;
$default_connection->insert($table_name)
->fields($new_row)
->execute();
}
$this->executeMigration($migration);
return $title;
}
}
@@ -0,0 +1,38 @@
<?php
namespace Drupal\Tests\node\Kernel\Migrate\d6;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
use Drupal\user\Entity\User;
/**
* Base class for Node migration tests.
*/
abstract class MigrateNodeTestBase extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installEntitySchema('node');
$this->installConfig(['node']);
$this->installSchema('node', ['node_access']);
$this->installSchema('system', ['sequences']);
// Create a new user which needs to have UID 1, because that is expected by
// the assertions from
// \Drupal\migrate_drupal\Tests\d6\MigrateNodeRevisionTest.
User::create([
'uid' => 1,
'name' => $this->randomMachineName(),
'status' => 1,
])->enforceIsNew()->save();
$this->migrateUsers(FALSE);
$this->migrateFields();
$this->executeMigration('d6_node_settings');
}
}
@@ -0,0 +1,69 @@
<?php
namespace Drupal\Tests\node\Kernel\Migrate\d6;
use Drupal\field\Entity\FieldConfig;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
use Drupal\node\Entity\NodeType;
/**
* Upgrade node types to node.type.*.yml.
*
* @group migrate_drupal_6
*/
class MigrateNodeTypeTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installConfig(['node']);
$this->executeMigration('d6_node_type');
}
/**
* Tests Drupal 6 node type to Drupal 8 migration.
*/
public function testNodeType() {
$id_map = $this->getMigration('d6_node_type')->getIdMap();
// Test the test_page content type.
$node_type_page = NodeType::load('test_page');
$this->assertIdentical('test_page', $node_type_page->id(), 'Node type test_page loaded');
$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(['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 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');
$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(['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 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');
$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(['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.');
}
}
@@ -0,0 +1,35 @@
<?php
namespace Drupal\Tests\node\Kernel\Migrate\d6;
use Drupal\Core\Entity\Entity\EntityViewMode;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Migrate view modes.
*
* @group migrate_drupal_6
*/
class MigrateViewModesTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->executeMigration('d6_view_modes');
}
/**
* Tests Drupal 6 view modes to Drupal 8 migration.
*/
public function testViewModes() {
// Test a new view mode.
$view_mode = EntityViewMode::load('node.preview');
$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(['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");
}
}
@@ -0,0 +1,40 @@
<?php
namespace Drupal\Tests\node\Kernel\Migrate\d7;
use Drupal\Tests\SchemaCheckTestTrait;
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
/**
* Upgrade variables to node.settings config object.
*
* @group node
*/
class MigrateNodeSettingsTest extends MigrateDrupal7TestBase {
use SchemaCheckTestTrait;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['node'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->executeMigration('d7_node_settings');
}
/**
* Tests migration of node variables to node.settings config object.
*/
public function testAggregatorSettings() {
$config = $this->config('node.settings');
$this->assertEqual(1, $config->get('use_admin_theme'));
}
}
@@ -0,0 +1,181 @@
<?php
namespace Drupal\Tests\node\Kernel\Migrate\d7;
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
use Drupal\node\Entity\Node;
use Drupal\node\NodeInterface;
/**
* Tests node migration.
*
* @group node
*/
class MigrateNodeTest extends MigrateDrupal7TestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'content_translation',
'comment',
'datetime',
'filter',
'image',
'language',
'link',
'node',
'taxonomy',
'telephone',
'text',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installEntitySchema('node');
$this->installEntitySchema('comment');
$this->installEntitySchema('taxonomy_term');
$this->installEntitySchema('file');
$this->installConfig(static::$modules);
$this->installSchema('node', ['node_access']);
$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',
'd7_node_translation',
]);
}
/**
* Asserts various aspects of a node.
*
* @param string $id
* The node ID.
* @param string $type
* The node type.
* @param string $langcode
* The expected language code.
* @param string $title
* The expected title.
* @param int $uid
* The expected author ID.
* @param bool $status
* The expected status of the node.
* @param int $created
* The expected creation time.
* @param int $changed
* The expected modification time.
* @param bool $promoted
* Whether the node is expected to be promoted to the front page.
* @param bool $sticky
* Whether the node is expected to be sticky.
*/
protected function assertEntity($id, $type, $langcode, $title, $uid, $status, $created, $changed, $promoted, $sticky) {
/** @var \Drupal\node\NodeInterface $node */
$node = Node::load($id);
$this->assertTrue($node instanceof NodeInterface);
$this->assertIdentical($type, $node->getType());
$this->assertIdentical($langcode, $node->langcode->value);
$this->assertIdentical($title, $node->getTitle());
$this->assertIdentical($uid, $node->getOwnerId());
$this->assertIdentical($status, $node->isPublished());
$this->assertIdentical($created, $node->getCreatedTime());
if (isset($changed)) {
$this->assertIdentical($changed, $node->getChangedTime());
}
$this->assertIdentical($promoted, $node->isPromoted());
$this->assertIdentical($sticky, $node->isSticky());
}
/**
* Asserts various aspects of a node revision.
*
* @param int $id
* The revision ID.
* @param string $title
* The expected title.
* @param int $uid
* The revision author ID.
* @param string $log
* The revision log message.
* @param int $timestamp
* The revision's time stamp.
*/
protected function assertRevision($id, $title, $uid, $log, $timestamp) {
$revision = \Drupal::entityManager()->getStorage('node')->loadRevision($id);
$this->assertTrue($revision instanceof NodeInterface);
$this->assertIdentical($title, $revision->getTitle());
$this->assertIdentical($uid, $revision->getRevisionUser()->id());
$this->assertIdentical($log, $revision->revision_log->value);
$this->assertIdentical($timestamp, $revision->getRevisionCreationTime());
}
/**
* Test node migration from Drupal 7 to 8.
*/
public function testNode() {
$this->assertEntity(1, 'test_content_type', 'en', 'A Node', '2', TRUE, '1421727515', '1441032132', TRUE, FALSE);
$this->assertRevision(1, 'A Node', '1', NULL, '1441032132');
$node = Node::load(1);
$this->assertTrue($node->field_boolean->value);
$this->assertIdentical('99-99-99-99', $node->field_phone->value);
// Use assertEqual() here instead, since SQLite interprets floats strictly.
$this->assertEqual('1', $node->field_float->value);
$this->assertIdentical('5', $node->field_integer->value);
$this->assertIdentical('Some more text', $node->field_text_list[0]->value);
$this->assertIdentical('7', $node->field_integer_list[0]->value);
$this->assertIdentical('qwerty', $node->field_text->value);
$this->assertIdentical('2', $node->field_file->target_id);
$this->assertIdentical('file desc', $node->field_file->description);
$this->assertTrue($node->field_file->display);
$this->assertIdentical('1', $node->field_images->target_id);
$this->assertIdentical('alt text', $node->field_images->alt);
$this->assertIdentical('title text', $node->field_images->title);
$this->assertIdentical('93', $node->field_images->width);
$this->assertIdentical('93', $node->field_images->height);
$this->assertIdentical('http://google.com', $node->field_link->uri);
$this->assertIdentical('Click Here', $node->field_link->title);
$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());
}
}
@@ -0,0 +1,54 @@
<?php
namespace Drupal\Tests\node\Kernel\Migrate\d7;
use Drupal\Core\Field\Entity\BaseFieldOverride;
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
/**
* Tests migration of the title field label for node types.
*
* @group node
*/
class MigrateNodeTitleLabelTest extends MigrateDrupal7TestBase {
public static $modules = ['node', 'text'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installConfig(static::$modules);
$this->installEntitySchema('node');
$this->executeMigrations(['d7_node_type', 'd7_node_title_label']);
}
/**
* Asserts various aspects of a base_field_override entity.
*
* @param string $id
* The override ID.
* @param string $label
* The label's expected (overridden) value.
*/
protected function assertEntity($id, $label) {
$override = BaseFieldOverride::load($id);
$this->assertTrue($override instanceof BaseFieldOverride);
/** @var \Drupal\Core\Field\Entity\BaseFieldOverride $override */
$this->assertIdentical($label, $override->getLabel());
}
/**
* Tests migration of node title field overrides.
*/
public function testMigration() {
$this->assertEntity('node.article.title', 'Title');
$this->assertEntity('node.blog.title', 'Title');
$this->assertEntity('node.book.title', 'Title');
$this->assertEntity('node.forum.title', 'Subject');
$this->assertEntity('node.page.title', 'Title');
$this->assertEntity('node.test_content_type.title', 'Title');
}
}
@@ -0,0 +1,81 @@
<?php
namespace Drupal\Tests\node\Kernel\Migrate\d7;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\FieldConfigInterface;
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
use Drupal\node\Entity\NodeType;
use Drupal\node\NodeTypeInterface;
/**
* Upgrade node types to node.type.*.yml.
*
* @group node
*/
class MigrateNodeTypeTest extends MigrateDrupal7TestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['node', 'text', 'filter'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installConfig(['node']);
$this->executeMigration('d7_node_type');
}
/**
* Tests a single node type.
*
* @dataProvider testNodeTypeDataProvider
*
* @param string $id
* The node type ID.
* @param string $label
* The expected label.
* @param string $description
* The expected node type description.
* @param string $help
* The expected help text.
*/
protected function assertEntity($id, $label, $description, $help, $display_submitted, $new_revision, $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');
$this->assertIdentical($new_revision, $entity->isNewRevision(), 'Is a new revision');
if ($body_label) {
/** @var \Drupal\field\FieldConfigInterface $body */
$body = FieldConfig::load('node.' . $id . '.body');
$this->assertTrue($body instanceof FieldConfigInterface);
$this->assertIdentical($body_label, $body->label());
}
}
/**
* 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');
// 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 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);
}
}
@@ -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',
]
);
}
}
@@ -0,0 +1,54 @@
<?php
namespace Drupal\Tests\node\Kernel;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\node\Entity\NodeType;
use Drupal\KernelTests\KernelTestBase;
/**
* Tests node body field storage.
*
* @group node
*/
class NodeBodyFieldStorageTest extends KernelTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['user', 'system', 'field', 'node', 'text', 'filter'];
protected function setUp() {
parent::setUp();
$this->installSchema('system', 'sequences');
// Necessary for module uninstall.
$this->installSchema('user', 'users_data');
$this->installEntitySchema('user');
$this->installEntitySchema('node');
$this->installConfig(['field', 'node']);
}
/**
* Tests node body field storage persistence even if there are no instances.
*/
public function testFieldOverrides() {
$field_storage = FieldStorageConfig::loadByName('node', 'body');
$this->assertTrue($field_storage, 'Node body field storage exists.');
$type = NodeType::create(['name' => 'Ponies', 'type' => 'ponies']);
$type->save();
node_add_body_field($type);
$field_storage = FieldStorageConfig::loadByName('node', 'body');
$this->assertTrue(count($field_storage->getBundles()) == 1, 'Node body field storage is being used on the new node type.');
$field = FieldConfig::loadByName('node', 'ponies', 'body');
$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(['node']);
$field_storage = FieldStorageConfig::loadByName('node', 'body');
$this->assertFalse($field_storage, 'Node body field storage does not exist after uninstalling the Node module.');
}
}
@@ -0,0 +1,83 @@
<?php
namespace Drupal\Tests\node\Kernel;
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
/**
* Tests that conditions, provided by the node module, are working properly.
*
* @group node
*/
class NodeConditionTest extends EntityKernelTestBase {
public static $modules = ['node'];
protected function setUp() {
parent::setUp();
// Create the node bundles required for testing.
$type = NodeType::create(['type' => 'page', 'name' => 'page']);
$type->save();
$type = NodeType::create(['type' => 'article', 'name' => 'article']);
$type->save();
$type = NodeType::create(['type' => 'test', 'name' => 'test']);
$type->save();
}
/**
* Tests conditions.
*/
public function testConditions() {
$manager = $this->container->get('plugin.manager.condition', $this->container->get('container.namespaces'));
$this->createUser();
// Get some nodes of various types to check against.
$page = Node::create(['type' => 'page', 'title' => $this->randomMachineName(), 'uid' => 1]);
$page->save();
$article = Node::create(['type' => 'article', 'title' => $this->randomMachineName(), 'uid' => 1]);
$article->save();
$test = Node::create(['type' => 'test', 'title' => $this->randomMachineName(), 'uid' => 1]);
$test->save();
// Grab the node type condition and configure it to check against node type
// of 'article' and set the context to the page type node.
$condition = $manager->createInstance('node_type')
->setConfig('bundles', ['article' => 'article'])
->setContextValue('node', $page);
$this->assertFalse($condition->execute(), 'Page type nodes fail node type checks for articles.');
// Check for the proper summary.
$this->assertEqual('The node bundle is article', $condition->summary());
// Set the node type check to page.
$condition->setConfig('bundles', ['page' => 'page']);
$this->assertTrue($condition->execute(), 'Page type nodes pass node type checks for pages');
// Check for the proper summary.
$this->assertEqual('The node bundle is page', $condition->summary());
// Set the node type check to page or article.
$condition->setConfig('bundles', ['page' => 'page', 'article' => 'article']);
$this->assertTrue($condition->execute(), 'Page type nodes pass node type checks for pages or articles');
// Check for the proper summary.
$this->assertEqual('The node bundle is page or article', $condition->summary());
// Set the context to the article node.
$condition->setContextValue('node', $article);
$this->assertTrue($condition->execute(), 'Article type nodes pass node type checks for pages or articles');
// Set the context to the test node.
$condition->setContextValue('node', $test);
$this->assertFalse($condition->execute(), 'Test type nodes pass node type checks for pages or articles');
// Check a greater than 2 bundles summary scenario.
$condition->setConfig('bundles', ['page' => 'page', 'article' => 'article', 'test' => 'test']);
$this->assertEqual('The node bundle is page, article or test', $condition->summary());
// Test Constructor injection.
$condition = $manager->createInstance('node_type', ['bundles' => ['article' => 'article'], 'context' => ['node' => $article]]);
$this->assertTrue($condition->execute(), 'Constructor injection of context and configuration working as anticipated.');
}
}
@@ -0,0 +1,149 @@
<?php
namespace Drupal\Tests\node\Kernel;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
/**
* Tests node field level access.
*
* @group node
*/
class NodeFieldAccessTest extends EntityKernelTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['node'];
/**
* Fields that only users with administer nodes permissions can change.
*
* @var array
*/
protected $administrativeFields = [
'status',
'promote',
'sticky',
'created',
'uid',
];
/**
* These fields are automatically managed and can not be changed by any user.
*
* @var array
*/
protected $readOnlyFields = ['changed', 'revision_uid', 'revision_timestamp'];
/**
* Test permissions on nodes status field.
*/
public function testAccessToAdministrativeFields() {
// Create the page node type with revisions disabled.
$page = NodeType::create([
'type' => 'page',
'new_revision' => FALSE,
]);
$page->save();
// Create the article node type with revisions disabled.
$article = NodeType::create([
'type' => 'article',
'new_revision' => TRUE,
]);
$article->save();
// An administrator user. No user exists yet, ensure that the first user
// does not have UID 1.
$content_admin_user = $this->createUser(['uid' => 2], ['administer nodes']);
// Two different editor users.
$page_creator_user = $this->createUser([], ['create page content', 'edit own page content', 'delete own page content']);
$page_manager_user = $this->createUser([], ['create page content', 'edit any page content', 'delete any page content']);
// An unprivileged user.
$page_unrelated_user = $this->createUser([], ['access content']);
// List of all users
$test_users = [
$content_admin_user,
$page_creator_user,
$page_manager_user,
$page_unrelated_user,
];
// Create three "Basic pages". One is owned by our test-user
// "page_creator", one by "page_manager", and one by someone else.
$node1 = Node::create([
'title' => $this->randomMachineName(8),
'uid' => $page_creator_user->id(),
'type' => 'page',
]);
$node2 = Node::create([
'title' => $this->randomMachineName(8),
'uid' => $page_manager_user->id(),
'type' => 'article',
]);
$node3 = Node::create([
'title' => $this->randomMachineName(8),
'type' => 'page',
]);
foreach ($this->administrativeFields as $field) {
// Checks on view operations.
foreach ($test_users as $account) {
$may_view = $node1->{$field}->access('view', $account);
$this->assertTrue($may_view, SafeMarkup::format('Any user may view the field @name.', ['@name' => $field]));
}
// Checks on edit operations.
$may_update = $node1->{$field}->access('edit', $page_creator_user);
$this->assertFalse($may_update, SafeMarkup::format('Users with permission "edit own page content" is not allowed to the field @name.', ['@name' => $field]));
$may_update = $node2->{$field}->access('edit', $page_creator_user);
$this->assertFalse($may_update, SafeMarkup::format('Users with permission "edit own page content" is not allowed to the field @name.', ['@name' => $field]));
$may_update = $node2->{$field}->access('edit', $page_manager_user);
$this->assertFalse($may_update, SafeMarkup::format('Users with permission "edit any page content" is not allowed to the field @name.', ['@name' => $field]));
$may_update = $node1->{$field}->access('edit', $page_manager_user);
$this->assertFalse($may_update, SafeMarkup::format('Users with permission "edit any page content" is not allowed to the field @name.', ['@name' => $field]));
$may_update = $node2->{$field}->access('edit', $page_unrelated_user);
$this->assertFalse($may_update, SafeMarkup::format('Users not having permission "edit any page content" is not allowed to the field @name.', ['@name' => $field]));
$may_update = $node1->{$field}->access('edit', $content_admin_user) && $node3->status->access('edit', $content_admin_user);
$this->assertTrue($may_update, SafeMarkup::format('Users with permission "administer nodes" may edit @name fields on all nodes.', ['@name' => $field]));
}
foreach ($this->readOnlyFields as $field) {
// Check view operation.
foreach ($test_users as $account) {
$may_view = $node1->{$field}->access('view', $account);
$this->assertTrue($may_view, SafeMarkup::format('Any user may view the field @name.', ['@name' => $field]));
}
// Check edit operation.
foreach ($test_users as $account) {
$may_view = $node1->{$field}->access('edit', $account);
$this->assertFalse($may_view, SafeMarkup::format('No user is not allowed to edit the field @name.', ['@name' => $field]));
}
}
// Check the revision_log field on node 1 which has revisions disabled.
$may_update = $node1->revision_log->access('edit', $content_admin_user);
$this->assertTrue($may_update, 'A user with permission "administer nodes" can edit the revision_log field when revisions are disabled.');
$may_update = $node1->revision_log->access('edit', $page_creator_user);
$this->assertFalse($may_update, 'A user without permission "administer nodes" can not edit the revision_log field when revisions are disabled.');
// Check the revision_log field on node 2 which has revisions enabled.
$may_update = $node2->revision_log->access('edit', $content_admin_user);
$this->assertTrue($may_update, 'A user with permission "administer nodes" can edit the revision_log field when revisions are enabled.');
$may_update = $node2->revision_log->access('edit', $page_creator_user);
$this->assertTrue($may_update, 'A user without permission "administer nodes" can edit the revision_log field when revisions are enabled.');
}
}
@@ -0,0 +1,64 @@
<?php
namespace Drupal\Tests\node\Kernel;
use Drupal\user\UserInterface;
use Drupal\Core\Field\Entity\BaseFieldOverride;
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
/**
* Tests node field overrides.
*
* @group node
*/
class NodeFieldOverridesTest extends EntityKernelTestBase {
/**
* Current logged in user.
*
* @var \Drupal\user\UserInterface
*/
protected $user;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['user', 'system', 'field', 'node'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installConfig(['user']);
$this->user = $this->createUser();
\Drupal::service('current_user')->setAccount($this->user);
}
/**
* Tests that field overrides work as expected.
*/
public function testFieldOverrides() {
if (!NodeType::load('ponies')) {
NodeType::create(['name' => 'Ponies', 'type' => 'ponies'])->save();
}
$override = BaseFieldOverride::loadByName('node', 'ponies', 'uid');
if ($override) {
$override->delete();
}
$uid_field = \Drupal::entityManager()->getBaseFieldDefinitions('node')['uid'];
$config = $uid_field->getConfig('ponies');
$config->save();
$this->assertEqual($config->get('default_value_callback'), 'Drupal\node\Entity\Node::getCurrentUserId');
/** @var \Drupal\node\NodeInterface $node */
$node = Node::create(['type' => 'ponies']);
$owner = $node->getOwner();
$this->assertTrue($owner instanceof UserInterface);
$this->assertEqual($owner->id(), $this->user->id());
}
}
@@ -0,0 +1,40 @@
<?php
namespace Drupal\Tests\node\Kernel;
use Drupal\Core\Language\LanguageInterface;
use Drupal\KernelTests\KernelTestBase;
/**
* Tests the admin listing fallback when views is not enabled.
*
* @group node
*/
class NodeListBuilderTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['node', 'user'];
protected function setUp() {
parent::setUp();
$this->installEntitySchema('node');
}
/**
* Tests that the correct cache contexts are set.
*/
public function testCacheContexts() {
/** @var \Drupal\Core\Entity\EntityListBuilderInterface $list_builder */
$list_builder = $this->container->get('entity.manager')->getListBuilder('node');
$build = $list_builder->render();
$this->container->get('renderer')->renderRoot($build);
$this->assertEqual(['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'url.query_args.pagers:0', 'user.node_grants:view', 'user.permissions'], $build['#cache']['contexts']);
}
}
@@ -0,0 +1,78 @@
<?php
namespace Drupal\Tests\node\Kernel;
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
/**
* Tests node owner functionality.
*
* @group Entity
*/
class NodeOwnerTest extends EntityKernelTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['node', 'language'];
protected function setUp() {
parent::setUp();
// Create the node bundles required for testing.
$type = NodeType::create([
'type' => 'page',
'name' => 'page',
]);
$type->save();
// Enable two additional languages.
ConfigurableLanguage::createFromLangcode('de')->save();
ConfigurableLanguage::createFromLangcode('it')->save();
$this->installSchema('node', 'node_access');
}
/**
* Tests node owner functionality.
*/
public function testOwner() {
$user = $this->createUser();
$container = \Drupal::getContainer();
$container->get('current_user')->setAccount($user);
// Create a test node.
$english = Node::create([
'type' => 'page',
'title' => $this->randomMachineName(),
'language' => 'en',
]);
$english->save();
$this->assertEqual($user->id(), $english->getOwnerId());
$german = $english->addTranslation('de');
$german->title = $this->randomString();
$italian = $english->addTranslation('it');
$italian->title = $this->randomString();
// Node::preSave() sets owner to anonymous user if owner is nor set.
$english->set('uid', ['target_id' => NULL]);
$german->set('uid', ['target_id' => NULL]);
$italian->set('uid', ['target_id' => NULL]);
// Entity::save() saves all translations!
$italian->save();
$this->assertEqual(0, $english->getOwnerId());
$this->assertEqual(0, $german->getOwnerId());
$this->assertEqual(0, $italian->getOwnerId());
}
}
@@ -0,0 +1,131 @@
<?php
namespace Drupal\Tests\node\Kernel;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Component\Utility\Html;
use Drupal\Core\Render\BubbleableMetadata;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
use Drupal\Tests\system\Kernel\Token\TokenReplaceKernelTestBase;
/**
* Generates text using placeholders for dummy content to check node token
* replacement.
*
* @group node
*/
class NodeTokenReplaceTest extends TokenReplaceKernelTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['node', 'filter'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installConfig(['filter', 'node']);
$node_type = NodeType::create(['type' => 'article', 'name' => 'Article']);
$node_type->save();
node_add_body_field($node_type);
}
/**
* Creates a node, then tests the tokens generated from it.
*/
public function testNodeTokenReplacement() {
$url_options = [
'absolute' => TRUE,
'language' => $this->interfaceLanguage,
];
// Create a user and a node.
$account = $this->createUser();
/* @var $node \Drupal\node\NodeInterface */
$node = Node::create([
'type' => 'article',
'tnid' => 0,
'uid' => $account->id(),
'title' => '<blink>Blinking Text</blink>',
'body' => [['value' => 'Regular NODE body for the test.', 'summary' => 'Fancy NODE summary.', 'format' => 'plain_text']],
]);
$node->save();
// Generate and test tokens.
$tests = [];
$tests['[node:nid]'] = $node->id();
$tests['[node:vid]'] = $node->getRevisionId();
$tests['[node:type]'] = 'article';
$tests['[node:type-name]'] = 'Article';
$tests['[node:title]'] = Html::escape($node->getTitle());
$tests['[node:body]'] = $node->body->processed;
$tests['[node:summary]'] = $node->body->summary_processed;
$tests['[node:langcode]'] = $node->language()->getId();
$tests['[node:url]'] = $node->url('canonical', $url_options);
$tests['[node:edit-url]'] = $node->url('edit-form', $url_options);
$tests['[node:author]'] = $account->getUsername();
$tests['[node:author:uid]'] = $node->getOwnerId();
$tests['[node:author:name]'] = $account->getUsername();
$tests['[node:created:since]'] = \Drupal::service('date.formatter')->formatTimeDiffSince($node->getCreatedTime(), ['langcode' => $this->interfaceLanguage->getId()]);
$tests['[node:changed:since]'] = \Drupal::service('date.formatter')->formatTimeDiffSince($node->getChangedTime(), ['langcode' => $this->interfaceLanguage->getId()]);
$base_bubbleable_metadata = BubbleableMetadata::createFromObject($node);
$metadata_tests = [];
$metadata_tests['[node:nid]'] = $base_bubbleable_metadata;
$metadata_tests['[node:vid]'] = $base_bubbleable_metadata;
$metadata_tests['[node:type]'] = $base_bubbleable_metadata;
$metadata_tests['[node:type-name]'] = $base_bubbleable_metadata;
$metadata_tests['[node:title]'] = $base_bubbleable_metadata;
$metadata_tests['[node:body]'] = $base_bubbleable_metadata;
$metadata_tests['[node:summary]'] = $base_bubbleable_metadata;
$metadata_tests['[node:langcode]'] = $base_bubbleable_metadata;
$metadata_tests['[node:url]'] = $base_bubbleable_metadata;
$metadata_tests['[node:edit-url]'] = $base_bubbleable_metadata;
$bubbleable_metadata = clone $base_bubbleable_metadata;
$metadata_tests['[node:author]'] = $bubbleable_metadata->addCacheTags(['user:1']);
$metadata_tests['[node:author:uid]'] = $bubbleable_metadata;
$metadata_tests['[node:author:name]'] = $bubbleable_metadata;
$bubbleable_metadata = clone $base_bubbleable_metadata;
$metadata_tests['[node:created:since]'] = $bubbleable_metadata->setCacheMaxAge(0);
$metadata_tests['[node:changed:since]'] = $bubbleable_metadata;
// Test to make sure that we generated something for each token.
$this->assertFalse(in_array(0, array_map('strlen', $tests)), 'No empty tokens generated.');
foreach ($tests as $input => $expected) {
$bubbleable_metadata = new BubbleableMetadata();
$output = $this->tokenService->replace($input, ['node' => $node], ['langcode' => $this->interfaceLanguage->getId()], $bubbleable_metadata);
$this->assertEqual($output, $expected, format_string('Node token %token replaced.', ['%token' => $input]));
$this->assertEqual($bubbleable_metadata, $metadata_tests[$input]);
}
// Repeat for a node without a summary.
$node = Node::create([
'type' => 'article',
'uid' => $account->id(),
'title' => '<blink>Blinking Text</blink>',
'body' => [['value' => 'A string that looks random like TR5c2I', 'format' => 'plain_text']],
]);
$node->save();
// Generate and test token - use full body as expected value.
$tests = [];
$tests['[node:summary]'] = $node->body->processed;
// Test to make sure that we generated something for each token.
$this->assertFalse(in_array(0, array_map('strlen', $tests)), 'No empty tokens generated for node without a summary.');
foreach ($tests as $input => $expected) {
$output = $this->tokenService->replace($input, ['node' => $node], ['language' => $this->interfaceLanguage]);
$this->assertEqual($output, $expected, new FormattableMarkup('Node token %token replaced for node without a summary.', ['%token' => $input]));
}
}
}
@@ -0,0 +1,72 @@
<?php
namespace Drupal\Tests\node\Kernel;
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
/**
* Tests node validation constraints.
*
* @group node
*/
class NodeValidationTest extends EntityKernelTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['node'];
/**
* Set the default field storage backend for fields created during tests.
*/
protected function setUp() {
parent::setUp();
// Create a node type for testing.
$type = NodeType::create(['type' => 'page', 'name' => 'page']);
$type->save();
}
/**
* Tests the node validation constraints.
*/
public function testValidation() {
$this->createUser();
$node = Node::create(['type' => 'page', 'title' => 'test', 'uid' => 1]);
$violations = $node->validate();
$this->assertEqual(count($violations), 0, 'No violations when validating a default node.');
$node->set('title', $this->randomString(256));
$violations = $node->validate();
$this->assertEqual(count($violations), 1, 'Violation found when title is too long.');
$this->assertEqual($violations[0]->getPropertyPath(), 'title.0.value');
$this->assertEqual($violations[0]->getMessage(), '<em class="placeholder">Title</em>: may not be longer than 255 characters.');
$node->set('title', NULL);
$violations = $node->validate();
$this->assertEqual(count($violations), 1, 'Violation found when title is not set.');
$this->assertEqual($violations[0]->getPropertyPath(), 'title');
$this->assertEqual($violations[0]->getMessage(), 'This value should not be null.');
$node->set('title', '');
$violations = $node->validate();
$this->assertEqual(count($violations), 1, 'Violation found when title is set to an empty string.');
$this->assertEqual($violations[0]->getPropertyPath(), 'title');
// Make the title valid again.
$node->set('title', $this->randomString());
// Save the node so that it gets an ID and a changed date.
$node->save();
// Set the changed date to something in the far past.
$node->set('changed', 433918800);
$violations = $node->validate();
$this->assertEqual(count($violations), 1, 'Violation found when changed date is before the last changed date.');
$this->assertEqual($violations[0]->getPropertyPath(), '');
$this->assertEqual($violations[0]->getMessage(), 'The content has either been modified by another user, or you have already submitted modifications. As a result, your changes cannot be saved.');
}
}
@@ -0,0 +1,185 @@
<?php
namespace Drupal\Tests\node\Kernel\Plugin\migrate\source\d6;
use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
/**
* Tests D6 node source plugin with 'node_type' configuration.
*
* @covers \Drupal\node\Plugin\migrate\source\d6\Node
*
* @group node
*/
class NodeByNodeTypeTest extends MigrateSqlSourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['node', 'user', 'migrate_drupal'];
/**
* {@inheritdoc}
*/
public function providerSource() {
$tests = [];
// The source data.
$tests[0]['source_data']['node'] = [
[
'nid' => 1,
'vid' => 1,
'type' => 'page',
'language' => 'en',
'title' => 'node title 1',
'uid' => 1,
'status' => 1,
'created' => 1279051598,
'changed' => 1279051598,
'comment' => 2,
'promote' => 1,
'moderate' => 0,
'sticky' => 0,
'tnid' => 1,
'translate' => 0,
],
[
'nid' => 2,
'vid' => 2,
'type' => 'page',
'language' => 'en',
'title' => 'node title 2',
'uid' => 1,
'status' => 1,
'created' => 1279290908,
'changed' => 1279308993,
'comment' => 0,
'promote' => 1,
'moderate' => 0,
'sticky' => 0,
'tnid' => 2,
'translate' => 0,
],
// Add another row with an article node and make sure it is not migrated.
[
'nid' => 5,
'vid' => 5,
'type' => 'article',
'language' => 'en',
'title' => 'node title 5',
'uid' => 1,
'status' => 1,
'created' => 1279290908,
'changed' => 1279308993,
'comment' => 0,
'promote' => 1,
'moderate' => 0,
'sticky' => 0,
'tnid' => 0,
'translate' => 0,
],
];
$tests[0]['source_data']['node_revisions'] = [
[
'nid' => 1,
'vid' => 1,
'title' => 'node title 1',
'uid' => 2,
'timestamp' => 1279051598,
'body' => 'body for node 1',
'teaser' => 'teaser for node 1',
'format' => 1,
'log' => 'log message 1',
],
[
'nid' => 2,
'vid' => 2,
'title' => 'node title 2',
'uid' => 2,
'timestamp' => 1279290908,
'body' => 'body for node 2',
'teaser' => 'teaser for node 2',
'format' => 1,
'log' => 'log message 2',
],
// Add another row with an article node and make sure it is not migrated.
[
'nid' => 5,
'vid' => 5,
'title' => 'node title 5',
'uid' => 2,
'timestamp' => 1279290908,
'body' => 'body for node 5',
'teaser' => 'body for node 5',
'format' => 1,
'log' => 'log message 3',
],
];
// The expected results.
$tests[0]['expected_data'] = [
[
// Node fields.
'nid' => 1,
'vid' => 1,
'type' => 'page',
'language' => 'en',
'title' => 'node title 1',
'node_uid' => 1,
'revision_uid' => 2,
'status' => 1,
'timestamp' => 1279051598,
'created' => 1279051598,
'changed' => 1279051598,
'comment' => 2,
'promote' => 1,
'moderate' => 0,
'sticky' => 0,
'tnid' => 1,
'translate' => 0,
// Node revision fields.
'body' => 'body for node 1',
'teaser' => 'teaser for node 1',
'format' => 1,
'log' => 'log message 1',
],
[
// Node fields.
'nid' => 2,
'vid' => 2,
'type' => 'page',
'language' => 'en',
'title' => 'node title 2',
'node_uid' => 1,
'revision_uid' => 2,
'status' => 1,
'timestamp' => 1279290908,
'created' => 1279290908,
'changed' => 1279308993,
'comment' => 0,
'promote' => 1,
'moderate' => 0,
'sticky' => 0,
'tnid' => 2,
'translate' => 0,
// Node revision fields.
'body' => 'body for node 2',
'teaser' => 'teaser for node 2',
'format' => 1,
'log' => 'log message 2',
],
];
// Do an automatic count.
$tests[0]['expected_count'] = NULL;
// Set up source plugin configuration.
$tests[0]['configuration'] = [
'node_type' => 'page',
];
return $tests;
}
}
@@ -0,0 +1,174 @@
<?php
namespace Drupal\Tests\node\Kernel\Plugin\migrate\source\d6;
use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
/**
* Tests D6 node revision source plugin.
*
* @covers \Drupal\node\Plugin\migrate\source\d6\NodeRevision
*
* @group node
*/
class NodeRevisionByNodeTypeTest extends MigrateSqlSourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['node', 'user', 'migrate_drupal'];
/**
* {@inheritdoc}
*/
public function providerSource() {
$tests = [];
// The source data.
$tests[0]['source_data']['node'] = [
[
'nid' => 1,
'type' => 'page',
'language' => 'en',
'status' => 1,
'created' => 1279051598,
'changed' => 1279051598,
'comment' => 2,
'promote' => 1,
'moderate' => 0,
'sticky' => 0,
'tnid' => 0,
'translate' => 0,
'vid' => 4,
'uid' => 1,
'title' => 'title for revision 4 (node 1)',
],
[
'nid' => 2,
'type' => 'article',
'language' => 'en',
'status' => 1,
'created' => 1279290908,
'changed' => 1279308993,
'comment' => 0,
'promote' => 1,
'moderate' => 0,
'sticky' => 0,
'tnid' => 0,
'translate' => 0,
'vid' => 2,
'uid' => 1,
'title' => 'title for revision 2 (node 2)',
],
];
$tests[0]['source_data']['node_revisions'] = [
[
'nid' => 1,
'vid' => 1,
'uid' => 1,
'title' => 'title for revision 1 (node 1)',
'body' => 'body for revision 1 (node 1)',
'teaser' => 'teaser for revision 1 (node 1)',
'log' => 'log for revision 1 (node 1)',
'format' => 1,
'timestamp' => 1279051598,
],
[
'nid' => 1,
'vid' => 3,
'uid' => 1,
'title' => 'title for revision 3 (node 1)',
'body' => 'body for revision 3 (node 1)',
'teaser' => 'teaser for revision 3 (node 1)',
'log' => 'log for revision 3 (node 1)',
'format' => 1,
'timestamp' => 1279051598,
],
[
'nid' => 1,
'vid' => 4,
'uid' => 1,
'title' => 'title for revision 4 (node 1)',
'body' => 'body for revision 4 (node 1)',
'teaser' => 'teaser for revision 4 (node 1)',
'log' => 'log for revision 4 (node 1)',
'format' => 1,
'timestamp' => 1279051598,
],
[
'nid' => 2,
'vid' => 2,
'uid' => 1,
'title' => 'title for revision 2 (node 2)',
'body' => 'body for revision 2 (node 2)',
'teaser' => 'teaser for revision 2 (node 2)',
'log' => 'log for revision 2 (node 2)',
'format' => 1,
'timestamp' => 1279308993,
],
];
// The expected results.
// There are three revisions of nid 1; vid 4 is the current one. The
// NodeRevision plugin should capture every revision EXCEPT that one.
// nid 2 will be ignored because source plugin configuration specifies
// a particular node type.
$tests[0]['expected_data'] = [
[
'nid' => 1,
'type' => 'page',
'language' => 'en',
'status' => 1,
'created' => 1279051598,
'changed' => 1279051598,
'comment' => 2,
'promote' => 1,
'moderate' => 0,
'sticky' => 0,
'tnid' => 1,
'translate' => 0,
'vid' => 1,
'node_uid' => 1,
'revision_uid' => 1,
'title' => 'title for revision 1 (node 1)',
'body' => 'body for revision 1 (node 1)',
'teaser' => 'teaser for revision 1 (node 1)',
'log' => 'log for revision 1 (node 1)',
'format' => 1,
],
[
'nid' => 1,
'type' => 'page',
'language' => 'en',
'status' => 1,
'created' => 1279051598,
'changed' => 1279051598,
'comment' => 2,
'promote' => 1,
'moderate' => 0,
'sticky' => 0,
'tnid' => 1,
'translate' => 0,
'vid' => 3,
'node_uid' => 1,
'revision_uid' => 1,
'title' => 'title for revision 3 (node 1)',
'body' => 'body for revision 3 (node 1)',
'teaser' => 'teaser for revision 3 (node 1)',
'log' => 'log for revision 3 (node 1)',
'format' => 1,
],
];
// Do an automatic count.
$tests[0]['expected_count'] = NULL;
// Set up source plugin configuration.
$tests[0]['configuration'] = [
'node_type' => 'page',
];
return $tests;
}
}
@@ -0,0 +1,169 @@
<?php
namespace Drupal\Tests\node\Kernel\Plugin\migrate\source\d6;
use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
/**
* Tests D6 node revision source plugin.
*
* @covers \Drupal\node\Plugin\migrate\source\d6\NodeRevision
*
* @group node
*/
class NodeRevisionTest extends MigrateSqlSourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['node', 'user', 'migrate_drupal'];
/**
* {@inheritdoc}
*/
public function providerSource() {
$tests = [];
// The source data.
$tests[0]['source_data']['node'] = [
[
'nid' => 1,
'type' => 'page',
'language' => 'en',
'status' => 1,
'created' => 1279051598,
'changed' => 1279051598,
'comment' => 2,
'promote' => 1,
'moderate' => 0,
'sticky' => 0,
'tnid' => 0,
'translate' => 0,
'vid' => 4,
'uid' => 1,
'title' => 'title for revision 1 (node 1)',
],
[
'nid' => 2,
'type' => 'article',
'language' => 'en',
'status' => 1,
'created' => 1279290908,
'changed' => 1279308993,
'comment' => 0,
'promote' => 1,
'moderate' => 0,
'sticky' => 0,
'tnid' => 0,
'translate' => 0,
'vid' => 2,
'uid' => 1,
'title' => 'title for revision 2 (node 2)',
],
];
$tests[0]['source_data']['node_revisions'] = [
[
'nid' => 1,
'vid' => 1,
'uid' => 1,
'title' => 'title for revision 1 (node 1)',
'body' => 'body for revision 1 (node 1)',
'teaser' => 'teaser for revision 1 (node 1)',
'log' => 'log for revision 1 (node 1)',
'format' => 1,
'timestamp' => 1279051598,
],
[
'nid' => 1,
'vid' => 3,
'uid' => 1,
'title' => 'title for revision 3 (node 1)',
'body' => 'body for revision 3 (node 1)',
'teaser' => 'teaser for revision 3 (node 1)',
'log' => 'log for revision 3 (node 1)',
'format' => 1,
'timestamp' => 1279051598,
],
[
'nid' => 1,
'vid' => 4,
'uid' => 1,
'title' => 'title for revision 4 (node 1)',
'body' => 'body for revision 4 (node 1)',
'teaser' => 'teaser for revision 4 (node 1)',
'log' => 'log for revision 4 (node 1)',
'format' => 1,
'timestamp' => 1279051598,
],
[
'nid' => 2,
'vid' => 2,
'uid' => 1,
'title' => 'title for revision 2 (node 2)',
'body' => 'body for revision 2 (node 2)',
'teaser' => 'teaser for revision 2 (node 2)',
'log' => 'log for revision 2 (node 2)',
'format' => 1,
'timestamp' => 1279308993,
],
];
// The expected results.
// There are three revisions of nid 1, but the NodeRevision source ignores
// the current revision. So only two revisions will be returned here. nid 2
// is ignored because it only has one revision (the current one).
$tests[0]['expected_data'] = [
[
// Node fields.
'nid' => 1,
'type' => 'page',
'language' => 'en',
'status' => 1,
'created' => 1279051598,
'changed' => 1279051598,
'comment' => 2,
'promote' => 1,
'moderate' => 0,
'sticky' => 0,
'tnid' => 1,
'translate' => 0,
// Node revision fields.
'vid' => 1,
'node_uid' => 1,
'revision_uid' => 1,
'title' => 'title for revision 1 (node 1)',
'body' => 'body for revision 1 (node 1)',
'teaser' => 'teaser for revision 1 (node 1)',
'log' => 'log for revision 1 (node 1)',
'format' => 1,
],
[
// Node fields.
'nid' => 1,
'type' => 'page',
'language' => 'en',
'status' => 1,
'created' => 1279051598,
'changed' => 1279051598,
'comment' => 2,
'promote' => 1,
'moderate' => 0,
'sticky' => 0,
'tnid' => 1,
'translate' => 0,
// Node revision fields.
'vid' => 3,
'node_uid' => 1,
'revision_uid' => 1,
'title' => 'title for revision 3 (node 1)',
'body' => 'body for revision 3 (node 1)',
'teaser' => 'teaser for revision 3 (node 1)',
'log' => 'log for revision 3 (node 1)',
'format' => 1,
],
];
return $tests;
}
}
@@ -0,0 +1,328 @@
<?php
namespace Drupal\Tests\node\Kernel\Plugin\migrate\source\d6;
use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
/**
* Tests D6 node source plugin.
*
* @covers \Drupal\node\Plugin\migrate\source\d6\Node
*
* @group node
*/
class NodeTest extends MigrateSqlSourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['node', 'user', 'migrate_drupal'];
/**
* {@inheritdoc}
*/
public function providerSource() {
$tests = [];
// The source data.
$tests[0]['source_data']['content_node_field'] = [
[
'field_name' => 'field_test_four',
'type' => 'number_float',
'global_settings' => 'a:0:{}',
'required' => '0',
'multiple' => '0',
'db_storage' => '1',
'module' => 'number',
'db_columns' => 'a:1:{s:5:"value";a:3:{s:4:"type";s:5:"float";s:8:"not null";b:0;s:8:"sortable";b:1;}}',
'active' => '1',
'locked' => '0',
],
];
$tests[0]['source_data']['content_node_field_instance'] = [
[
'field_name' => 'field_test_four',
'type_name' => 'story',
'weight' => '3',
'label' => 'Float Field',
'widget_type' => 'number',
'widget_settings' => 'a:0:{}',
'display_settings' => 'a:0:{}',
'description' => 'An example float field.',
'widget_module' => 'number',
'widget_active' => '1',
],
];
$tests[0]['source_data']['content_type_story'] = [
[
'nid' => 5,
'vid' => 5,
'uid' => 5,
'field_test_four_value' => '3.14159',
],
];
$tests[0]['source_data']['system'] = [
[
'type' => 'module',
'name' => 'content',
'schema_version' => 6001,
'status' => TRUE,
],
];
$tests[0]['source_data']['node'] = [
[
'nid' => 1,
'vid' => 1,
'type' => 'page',
'language' => 'en',
'title' => 'node title 1',
'uid' => 1,
'status' => 1,
'created' => 1279051598,
'changed' => 1279051598,
'comment' => 2,
'promote' => 1,
'moderate' => 0,
'sticky' => 0,
'translate' => 0,
'tnid' => 0,
],
[
'nid' => 2,
'vid' => 2,
'type' => 'page',
'language' => 'en',
'title' => 'node title 2',
'uid' => 1,
'status' => 1,
'created' => 1279290908,
'changed' => 1279308993,
'comment' => 0,
'promote' => 1,
'moderate' => 0,
'sticky' => 0,
'translate' => 0,
'tnid' => 0,
],
[
'nid' => 5,
'vid' => 5,
'type' => 'story',
'language' => 'en',
'title' => 'node title 5',
'uid' => 1,
'status' => 1,
'created' => 1279290908,
'changed' => 1279308993,
'comment' => 0,
'promote' => 1,
'moderate' => 0,
'sticky' => 0,
'translate' => 0,
'tnid' => 0,
],
[
'nid' => 6,
'vid' => 6,
'type' => 'story',
'language' => 'en',
'title' => 'node title 6',
'uid' => 1,
'status' => 1,
'created' => 1279290909,
'changed' => 1279308994,
'comment' => 0,
'promote' => 1,
'moderate' => 0,
'sticky' => 0,
'translate' => 0,
'tnid' => 6,
],
[
'nid' => 7,
'vid' => 7,
'type' => 'story',
'language' => 'fr',
'title' => 'node title 7',
'uid' => 1,
'status' => 1,
'created' => 1279290910,
'changed' => 1279308995,
'comment' => 0,
'promote' => 1,
'moderate' => 0,
'sticky' => 0,
'translate' => 0,
'tnid' => 6,
],
];
$tests[0]['source_data']['node_revisions'] = [
[
'nid' => 1,
'vid' => 1,
'uid' => 2,
'title' => 'node title 1',
'body' => 'body for node 1',
'teaser' => 'teaser for node 1',
'log' => '',
'format' => 1,
'timestamp' => 1279051598,
],
[
'nid' => 2,
'vid' => 2,
'uid' => 2,
'title' => 'node title 2',
'body' => 'body for node 2',
'teaser' => 'teaser for node 2',
'log' => '',
'format' => 1,
'timestamp' => 1279308993,
],
[
'nid' => 5,
'vid' => 5,
'uid' => 2,
'title' => 'node title 5',
'body' => 'body for node 5',
'teaser' => 'body for node 5',
'log' => '',
'format' => 1,
'timestamp' => 1279308993,
],
[
'nid' => 6,
'vid' => 6,
'uid' => 2,
'title' => 'node title 6',
'body' => 'body for node 6',
'teaser' => 'body for node 6',
'log' => '',
'format' => 1,
'timestamp' => 1279308994,
],
[
'nid' => 7,
'vid' => 7,
'uid' => 2,
'title' => 'node title 7',
'body' => 'body for node 7',
'teaser' => 'body for node 7',
'log' => '',
'format' => 1,
'timestamp' => 1279308995,
],
];
// The expected results.
$tests[0]['expected_data'] = [
[
// Node fields.
'nid' => 1,
'vid' => 1,
'type' => 'page',
'language' => 'en',
'title' => 'node title 1',
'node_uid' => 1,
'revision_uid' => 2,
'status' => 1,
'created' => 1279051598,
'changed' => 1279051598,
'comment' => 2,
'promote' => 1,
'moderate' => 0,
'sticky' => 0,
'tnid' => 1,
'translate' => 0,
// Node revision fields.
'body' => 'body for node 1',
'teaser' => 'teaser for node 1',
'log' => '',
'timestamp' => 1279051598,
'format' => 1,
],
[
// Node fields.
'nid' => 2,
'vid' => 2,
'type' => 'page',
'language' => 'en',
'title' => 'node title 2',
'node_uid' => 1,
'revision_uid' => 2,
'status' => 1,
'created' => 1279290908,
'changed' => 1279308993,
'comment' => 0,
'promote' => 1,
'moderate' => 0,
'sticky' => 0,
'tnid' => 2,
'translate' => 0,
// Node revision fields.
'body' => 'body for node 2',
'teaser' => 'teaser for node 2',
'log' => '',
'timestamp' => 1279308993,
'format' => 1,
],
[
'nid' => 5,
'vid' => 5,
'type' => 'story',
'language' => 'en',
'title' => 'node title 5',
'node_uid' => 1,
'revision_uid' => 2,
'status' => 1,
'created' => 1279290908,
'changed' => 1279308993,
'comment' => 0,
'promote' => 1,
'moderate' => 0,
'sticky' => 0,
'tnid' => 5,
'translate' => 0,
// Node revision fields.
'body' => 'body for node 5',
'teaser' => 'body for node 5',
'log' => '',
'timestamp' => 1279308993,
'format' => 1,
'field_test_four' => [
[
'value' => '3.14159',
'delta' => 0,
],
],
],
[
'nid' => 6,
'vid' => 6,
'type' => 'story',
'language' => 'en',
'title' => 'node title 6',
'node_uid' => 1,
'revision_uid' => 2,
'status' => 1,
'created' => 1279290909,
'changed' => 1279308994,
'comment' => 0,
'promote' => 1,
'moderate' => 0,
'sticky' => 0,
'tnid' => 6,
'translate' => 0,
// Node revision fields.
'body' => 'body for node 6',
'teaser' => 'body for node 6',
'log' => '',
'timestamp' => 1279308994,
'format' => 1,
],
];
return $tests;
}
}
@@ -0,0 +1,65 @@
<?php
namespace Drupal\Tests\node\Kernel\Plugin\migrate\source\d6;
/**
* Tests D6 node translation source plugin.
*
* @covers \Drupal\node\Plugin\migrate\source\d6\Node
*
* @group node
*/
class NodeTranslationTest extends NodeTest {
/**
* {@inheritdoc}
*/
public static $modules = ['node', 'user', 'migrate_drupal'];
/**
* {@inheritdoc}
*/
public function providerSource() {
// Get the source data from parent.
$tests = parent::providerSource();
// The expected results.
$tests[0]['expected_data'] = [
[
'nid' => 7,
'vid' => 7,
'type' => 'story',
'language' => 'fr',
'title' => 'node title 7',
'node_uid' => 1,
'revision_uid' => 2,
'status' => 1,
'created' => 1279290910,
'changed' => 1279308995,
'comment' => 0,
'promote' => 1,
'moderate' => 0,
'sticky' => 0,
'tnid' => 6,
'translate' => 0,
// Node revision fields.
'body' => 'body for node 7',
'teaser' => 'body for node 7',
'log' => '',
'timestamp' => 1279308995,
'format' => 1,
],
];
// Do an automatic count.
$tests[0]['expected_count'] = NULL;
// Set up source plugin configuration.
$tests[0]['configuration'] = [
'translations' => TRUE,
];
return $tests;
}
}
@@ -0,0 +1,67 @@
<?php
namespace Drupal\Tests\node\Kernel\Plugin\migrate\source\d6;
use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
/**
* Tests D6 node type source plugin.
*
* @covers \Drupal\node\Plugin\migrate\source\d6\NodeType
*
* @group node
*/
class NodeTypeTest extends MigrateSqlSourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['node', 'user', 'migrate_drupal'];
/**
* {@inheritdoc}
*/
public function providerSource() {
$tests = [];
// The source data.
$tests[0]['source_data']['node_type'] = [
[
'type' => 'page',
'name' => 'Page',
'module' => 'node',
'description' => 'A <em>page</em>, similar in form to a <em>story</em>, is a simple method for creating and displaying information that rarely changes, such as an "About us" section of a website. By default, a <em>page</em> entry does not allow visitor comments and is not featured on the site\'s initial home page.',
'help' => '',
'title_label' => 'Title',
'has_body' => 1,
'body_label' => 'Body',
'min_word_count' => 0,
'custom' => 1,
'modified' => 0,
'locked' => 0,
'orig_type' => 'page',
],
[
'type' => 'story',
'name' => 'Story',
'module' => 'node',
'description' => 'A <em>story</em>, similar in form to a <em>page</em>, is ideal for creating and displaying content that informs or engages website visitors. Press releases, site announcements, and informal blog-like entries may all be created with a <em>story</em> entry. By default, a <em>story</em> entry is automatically featured on the site\'s initial home page, and provides the ability to post comments.',
'help' => '',
'title_label' => 'Title',
'has_body' => 1,
'body_label' => 'Body',
'min_word_count' => 0,
'custom' => 1,
'modified' => 0,
'locked' => 0,
'orig_type' => 'story',
],
];
// The expected results.
$tests[0]['expected_data'] = $tests[0]['source_data']['node_type'];
return $tests;
}
}
@@ -0,0 +1,71 @@
<?php
namespace Drupal\Tests\node\Kernel\Plugin\migrate\source\d6;
use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
/**
* Tests D6 view mode source plugin.
*
* @covers \Drupal\node\Plugin\migrate\source\d6\ViewMode
*
* @group node
*/
class ViewModeTest extends MigrateSqlSourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['node', 'user', 'migrate_drupal'];
/**
* {@inheritdoc}
*/
public function providerSource() {
$tests = [];
// The source data.
$tests[0]['source_data']['content_node_field_instance'] = [
[
'display_settings' => serialize([
'weight' => '31',
'parent' => '',
'label' => [
'format' => 'above',
],
'teaser' => [
'format' => 'default',
'exclude' => 0,
],
'full' => [
'format' => 'default',
'exclude' => 0,
],
4 => [
'format' => 'default',
'exclude' => 0,
],
]),
],
];
// The expected results.
$tests[0]['expected_data'] = [
[
'entity_type' => 'node',
'view_mode' => '4',
],
[
'entity_type' => 'node',
'view_mode' => 'teaser',
],
[
'entity_type' => 'node',
'view_mode' => 'full',
],
];
return $tests;
}
}
@@ -0,0 +1,367 @@
<?php
namespace Drupal\Tests\node\Kernel\Plugin\migrate\source\d7;
use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
/**
* Tests D7 node source plugin.
*
* @covers \Drupal\node\Plugin\migrate\source\d7\Node
*
* @group node
*/
class NodeTest extends MigrateSqlSourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['node', 'user', 'migrate_drupal'];
/**
* {@inheritdoc}
*/
public function providerSource() {
$tests = [];
// The source data.
$tests[0]['source_data']['node'] = [
[
'nid' => 1,
'vid' => 1,
'type' => 'page',
'language' => 'en',
'title' => 'node title 1',
'uid' => 1,
'status' => 1,
'created' => 1279051598,
'changed' => 1279051598,
'comment' => 2,
'promote' => 1,
'sticky' => 0,
'tnid' => 0,
'translate' => 0,
],
[
'nid' => 2,
'vid' => 2,
'type' => 'page',
'language' => 'en',
'title' => 'node title 2',
'uid' => 1,
'status' => 1,
'created' => 1279290908,
'changed' => 1279308993,
'comment' => 0,
'promote' => 1,
'sticky' => 0,
'tnid' => 0,
'translate' => 0,
],
[
'nid' => 5,
'vid' => 5,
'type' => 'article',
'language' => 'en',
'title' => 'node title 5',
'uid' => 1,
'status' => 1,
'created' => 1279290908,
'changed' => 1279308993,
'comment' => 0,
'promote' => 1,
'sticky' => 0,
'tnid' => 0,
'translate' => 0,
],
[
'nid' => 6,
'vid' => 6,
'type' => 'article',
'language' => 'en',
'title' => 'node title 5',
'uid' => 1,
'status' => 1,
'created' => 1279291908,
'changed' => 1279309993,
'comment' => 0,
'promote' => 1,
'sticky' => 0,
'tnid' => 6,
'translate' => 0,
],
[
'nid' => 7,
'vid' => 7,
'type' => 'article',
'language' => 'fr',
'title' => 'fr - node title 5',
'uid' => 1,
'status' => 1,
'created' => 1279292908,
'changed' => 1279310993,
'comment' => 0,
'promote' => 1,
'sticky' => 0,
'tnid' => 6,
'translate' => 0,
],
];
$tests[0]['source_data']['node_revision'] = [
[
'nid' => 1,
'vid' => 1,
'uid' => 2,
'title' => 'node title 1',
'log' => '',
'timestamp' => 1279051598,
'status' => 1,
'comment' => 2,
'promote' => 1,
'sticky' => 0,
],
[
'nid' => 2,
'vid' => 2,
'uid' => 2,
'title' => 'node title 2',
'log' => '',
'timestamp' => 1279308993,
'status' => 1,
'comment' => 0,
'promote' => 1,
'sticky' => 0,
],
[
'nid' => 5,
'vid' => 5,
'uid' => 2,
'title' => 'node title 5',
'log' => '',
'timestamp' => 1279308993,
'status' => 1,
'comment' => 0,
'promote' => 1,
'sticky' => 0,
],
[
'nid' => 6,
'vid' => 6,
'uid' => 1,
'title' => 'node title 5',
'log' => '',
'timestamp' => 1279309993,
'status' => 1,
'comment' => 0,
'promote' => 1,
'sticky' => 0,
],
[
'nid' => 7,
'vid' => 7,
'uid' => 1,
'title' => 'fr - node title 5',
'log' => '',
'timestamp' => 1279310993,
'status' => 1,
'comment' => 0,
'promote' => 1,
'sticky' => 0,
],
];
$tests[0]['source_data']['field_config_instance'] = [
[
'id' => '2',
'field_id' => '2',
'field_name' => 'body',
'entity_type' => 'node',
'bundle' => 'page',
'data' => 'a:0:{}',
'deleted' => '0',
],
[
'id' => '3',
'field_id' => '2',
'field_name' => 'body',
'entity_type' => 'node',
'bundle' => 'article',
'data' => 'a:0:{}',
'deleted' => '0',
],
];
$tests[0]['source_data']['field_revision_body'] = [
[
'entity_type' => 'node',
'bundle' => 'page',
'deleted' => '0',
'entity_id' => '1',
'revision_id' => '1',
'language' => 'en',
'delta' => '0',
'body_value' => 'Foobaz',
'body_summary' => '',
'body_format' => 'filtered_html',
],
[
'entity_type' => 'node',
'bundle' => 'page',
'deleted' => '0',
'entity_id' => '2',
'revision_id' => '2',
'language' => 'en',
'delta' => '0',
'body_value' => 'body 2',
'body_summary' => '',
'body_format' => 'filtered_html',
],
[
'entity_type' => 'node',
'bundle' => 'page',
'deleted' => '0',
'entity_id' => '5',
'revision_id' => '5',
'language' => 'en',
'delta' => '0',
'body_value' => 'body 5',
'body_summary' => '',
'body_format' => 'filtered_html',
],
[
'entity_type' => 'node',
'bundle' => 'page',
'deleted' => '0',
'entity_id' => '6',
'revision_id' => '6',
'language' => 'en',
'delta' => '0',
'body_value' => 'body 6',
'body_summary' => '',
'body_format' => 'filtered_html',
],
[
'entity_type' => 'node',
'bundle' => 'page',
'deleted' => '0',
'entity_id' => '7',
'revision_id' => '7',
'language' => 'fr',
'delta' => '0',
'body_value' => 'fr - body 6',
'body_summary' => '',
'body_format' => 'filtered_html',
],
];
// The expected results.
$tests[0]['expected_data'] = [
[
'nid' => 1,
'vid' => 1,
'type' => 'page',
'language' => 'en',
'title' => 'node title 1',
'node_uid' => 1,
'revision_uid' => 2,
'status' => 1,
'created' => 1279051598,
'changed' => 1279051598,
'comment' => 2,
'promote' => 1,
'sticky' => 0,
'tnid' => 1,
'translate' => 0,
'log' => '',
'timestamp' => 1279051598,
'body' => [
[
'value' => 'Foobaz',
'summary' => '',
'format' => 'filtered_html',
],
],
],
[
'nid' => 2,
'vid' => 2,
'type' => 'page',
'language' => 'en',
'title' => 'node title 2',
'node_uid' => 1,
'revision_uid' => 2,
'status' => 1,
'created' => 1279290908,
'changed' => 1279308993,
'comment' => 0,
'promote' => 1,
'sticky' => 0,
'tnid' => 2,
'translate' => 0,
'log' => '',
'timestamp' => 1279308993,
'body' => [
[
'value' => 'body 2',
'summary' => '',
'format' => 'filtered_html',
],
],
],
[
'nid' => 5,
'vid' => 5,
'type' => 'article',
'language' => 'en',
'title' => 'node title 5',
'node_uid' => 1,
'revision_uid' => 2,
'status' => 1,
'created' => 1279290908,
'changed' => 1279308993,
'comment' => 0,
'promote' => 1,
'sticky' => 0,
'tnid' => 5,
'translate' => 0,
'log' => '',
'timestamp' => 1279308993,
'body' => [
[
'value' => 'body 5',
'summary' => '',
'format' => 'filtered_html',
],
],
],
[
'nid' => 6,
'vid' => 6,
'type' => 'article',
'language' => 'en',
'title' => 'node title 5',
'node_uid' => 1,
'revision_uid' => 1,
'status' => 1,
'created' => 1279291908,
'changed' => 1279309993,
'comment' => 0,
'promote' => 1,
'sticky' => 0,
'tnid' => 6,
'translate' => 0,
'log' => '',
'timestamp' => 1279309993,
'body' => [
[
'value' => 'body 6',
'summary' => '',
'format' => 'filtered_html',
],
],
],
];
return $tests;
}
}

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