Move all files to 2017/
This commit is contained in:
parent
ac7370f67f
commit
2875863330
15717 changed files with 0 additions and 0 deletions
|
@ -0,0 +1,6 @@
|
|||
name: 'Node module access tests'
|
||||
type: module
|
||||
description: 'Support module for node permission testing.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
|
@ -0,0 +1,163 @@
|
|||
<?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')) {
|
||||
$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);
|
||||
}
|
||||
|
||||
// Grant access if a specific user is specified.
|
||||
if (\Drupal::state()->get('node_access_test.allow_uid') === $account->id()) {
|
||||
return AccessResult::allowed();
|
||||
}
|
||||
|
||||
// No opinion.
|
||||
return AccessResult::neutral()->setCacheMaxAge(0);
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
node test view:
|
||||
title: 'View content'
|
|
@ -0,0 +1,6 @@
|
|||
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
|
|
@ -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,8 @@
|
|||
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:
|
||||
- drupal:options
|
|
@ -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,6 @@
|
|||
name: 'Node module tests'
|
||||
type: module
|
||||
description: 'Support module for node related testing.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
|
@ -0,0 +1,191 @@
|
|||
<?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::messenger()->addStatus('Storage is not set');
|
||||
$form_state->set('node_test_form_alter', TRUE);
|
||||
}
|
||||
else {
|
||||
\Drupal::messenger()->addStatus('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,6 @@
|
|||
name: 'Node configuration tests'
|
||||
type: module
|
||||
description: 'Support module for node configuration tests.'
|
||||
core: 8.x
|
||||
package: Testing
|
||||
version: VERSION
|
|
@ -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,6 @@
|
|||
name: 'Node module exception tests'
|
||||
type: module
|
||||
description: 'Support module for node related exception testing.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
|
@ -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,10 @@
|
|||
name: 'Node test views'
|
||||
type: module
|
||||
description: 'Provides default views for views node tests.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- drupal:node
|
||||
- drupal:views
|
||||
- drupal:language
|
|
@ -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,106 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
||||
- user
|
||||
id: test_argument_node_uid_revision
|
||||
label: test_argument_node_uid_revision
|
||||
module: views
|
||||
description: ''
|
||||
tag: default
|
||||
base_table: node_field_data
|
||||
base_field: nid
|
||||
core: 8.0-dev
|
||||
display:
|
||||
default:
|
||||
display_options:
|
||||
access:
|
||||
type: perm
|
||||
cache:
|
||||
type: tag
|
||||
exposed_form:
|
||||
type: basic
|
||||
fields:
|
||||
nid:
|
||||
id: nid
|
||||
table: node_field_data
|
||||
field: nid
|
||||
plugin_id: field
|
||||
entity_type: node
|
||||
entity_field: nid
|
||||
filter_groups:
|
||||
groups:
|
||||
1: AND
|
||||
operator: AND
|
||||
filters: { }
|
||||
sorts:
|
||||
nid:
|
||||
id: nid
|
||||
table: node_field_data
|
||||
field: nid
|
||||
order: ASC
|
||||
plugin_id: standard
|
||||
relationship: none
|
||||
entity_type: node
|
||||
entity_field: nid
|
||||
pager:
|
||||
type: full
|
||||
query:
|
||||
type: views_query
|
||||
style:
|
||||
type: default
|
||||
row:
|
||||
type: fields
|
||||
display_extenders: { }
|
||||
arguments:
|
||||
uid_revision:
|
||||
id: uid_revision
|
||||
table: node_field_data
|
||||
field: uid_revision
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
default_action: empty
|
||||
exception:
|
||||
value: all
|
||||
title_enable: false
|
||||
title: All
|
||||
title_enable: false
|
||||
title: ''
|
||||
default_argument_type: fixed
|
||||
default_argument_options:
|
||||
argument: ''
|
||||
default_argument_skip_url: false
|
||||
summary_options:
|
||||
base_path: ''
|
||||
count: true
|
||||
items_per_page: 25
|
||||
override: false
|
||||
summary:
|
||||
sort_order: asc
|
||||
number_of_records: 0
|
||||
format: default_summary
|
||||
specify_validation: false
|
||||
validate:
|
||||
type: none
|
||||
fail: 'not found'
|
||||
validate_options: { }
|
||||
break_phrase: false
|
||||
not: false
|
||||
entity_type: node
|
||||
plugin_id: node_uid_revision
|
||||
display_plugin: default
|
||||
display_title: Master
|
||||
id: default
|
||||
position: 0
|
||||
cache_metadata:
|
||||
max-age: -1
|
||||
contexts:
|
||||
- 'languages:language_content'
|
||||
- 'languages:language_interface'
|
||||
- url
|
||||
- url.query_args
|
||||
- 'user.node_grants:view'
|
||||
- user.permissions
|
||||
tags: { }
|
|
@ -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 revert 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,89 @@
|
|||
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
|
||||
langcode:
|
||||
id: langcode
|
||||
table: node_field_revision
|
||||
field: langcode
|
||||
plugin_id: field
|
||||
entity_type: node
|
||||
entity_field: langcode
|
||||
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
|
||||
langcode:
|
||||
id: langcode
|
||||
table: node_field_revision
|
||||
field: langcode
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
order: DESC
|
||||
exposed: false
|
||||
expose:
|
||||
label: ''
|
||||
entity_type: node
|
||||
entity_field: langcode
|
||||
plugin_id: standard
|
||||
display_extenders: { }
|
||||
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,81 @@
|
|||
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
|
||||
langcode:
|
||||
id: langcode
|
||||
table: node_field_revision
|
||||
field: langcode
|
||||
entity_type: node
|
||||
entity_field: langcode
|
||||
plugin_id: field
|
||||
arguments:
|
||||
nid:
|
||||
id: nid
|
||||
table: node_field_revision
|
||||
field: nid
|
||||
plugin_id: node_nid
|
||||
entity_type: node
|
||||
entity_field: nid
|
||||
display_extenders: { }
|
||||
sorts:
|
||||
langcode:
|
||||
id: langcode
|
||||
table: node_field_revision
|
||||
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_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,6 @@
|
|||
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
|
|
@ -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,49 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
/**
|
||||
* Asserts that buttons are present on a page.
|
||||
*/
|
||||
trait AssertButtonsTrait {
|
||||
|
||||
/**
|
||||
* Assert method to verify the buttons in the dropdown element.
|
||||
*
|
||||
* @param array $buttons
|
||||
* A collection of buttons to assert for on the page.
|
||||
* @param bool $dropbutton
|
||||
* Whether to check if the buttons are in a dropbutton widget or not.
|
||||
*/
|
||||
public function assertButtons(array $buttons, $dropbutton = TRUE) {
|
||||
|
||||
// Try to find a Save button.
|
||||
$save_button = $this->xpath('//input[@type="submit"][@value="Save"]');
|
||||
|
||||
// Verify that the number of buttons passed as parameters is
|
||||
// available in the dropbutton widget.
|
||||
if ($dropbutton) {
|
||||
$i = 0;
|
||||
$count = count($buttons);
|
||||
|
||||
// Assert there is no save button.
|
||||
$this->assertTrue(empty($save_button));
|
||||
|
||||
// Dropbutton elements.
|
||||
/** @var \Behat\Mink\Element\NodeElement[] $elements */
|
||||
$elements = $this->xpath('//div[@class="dropbutton-wrapper"]//input[@type="submit"]');
|
||||
$this->assertEqual($count, count($elements));
|
||||
foreach ($elements as $element) {
|
||||
$value = $element->getValue() ?: '';
|
||||
$this->assertEqual($buttons[$i], $value);
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Assert there is a save button.
|
||||
$this->assertTrue(!empty($save_button));
|
||||
$this->assertNoRaw('dropbutton-wrapper');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional\Hal;
|
||||
|
||||
use Drupal\Tests\hal\Functional\EntityResource\HalEntityNormalizationTrait;
|
||||
use Drupal\Tests\node\Functional\Rest\NodeResourceTestBase;
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* @group hal
|
||||
*/
|
||||
class NodeHalJsonAnonTest extends NodeResourceTestBase {
|
||||
|
||||
use HalEntityNormalizationTrait;
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['hal'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'hal_json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/hal+json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $patchProtectedFieldNames = [
|
||||
'revision_timestamp' => NULL,
|
||||
'created' => "The 'administer nodes' permission is required.",
|
||||
'changed' => NULL,
|
||||
'promote' => "The 'administer nodes' permission is required.",
|
||||
'sticky' => "The 'administer nodes' permission is required.",
|
||||
'path' => "The following permissions are required: 'create url aliases' OR 'administer url aliases'.",
|
||||
'revision_uid' => NULL,
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedNormalizedEntity() {
|
||||
$default_normalization = parent::getExpectedNormalizedEntity();
|
||||
|
||||
$normalization = $this->applyHalFieldNormalization($default_normalization);
|
||||
|
||||
$author = User::load($this->entity->getOwnerId());
|
||||
return $normalization + [
|
||||
'_links' => [
|
||||
'self' => [
|
||||
'href' => $this->baseUrl . '/llama?_format=hal_json',
|
||||
],
|
||||
'type' => [
|
||||
'href' => $this->baseUrl . '/rest/type/node/camelids',
|
||||
],
|
||||
$this->baseUrl . '/rest/relation/node/camelids/revision_uid' => [
|
||||
[
|
||||
'href' => $this->baseUrl . '/user/' . $author->id() . '?_format=hal_json',
|
||||
],
|
||||
],
|
||||
$this->baseUrl . '/rest/relation/node/camelids/uid' => [
|
||||
[
|
||||
'href' => $this->baseUrl . '/user/' . $author->id() . '?_format=hal_json',
|
||||
'lang' => 'en',
|
||||
],
|
||||
],
|
||||
],
|
||||
'_embedded' => [
|
||||
$this->baseUrl . '/rest/relation/node/camelids/revision_uid' => [
|
||||
[
|
||||
'_links' => [
|
||||
'self' => [
|
||||
'href' => $this->baseUrl . '/user/' . $author->id() . '?_format=hal_json',
|
||||
],
|
||||
'type' => [
|
||||
'href' => $this->baseUrl . '/rest/type/user/user',
|
||||
],
|
||||
],
|
||||
'uuid' => [
|
||||
['value' => $author->uuid()],
|
||||
],
|
||||
],
|
||||
],
|
||||
$this->baseUrl . '/rest/relation/node/camelids/uid' => [
|
||||
[
|
||||
'_links' => [
|
||||
'self' => [
|
||||
'href' => $this->baseUrl . '/user/' . $author->id() . '?_format=hal_json',
|
||||
],
|
||||
'type' => [
|
||||
'href' => $this->baseUrl . '/rest/type/user/user',
|
||||
],
|
||||
],
|
||||
'uuid' => [
|
||||
['value' => $author->uuid()],
|
||||
],
|
||||
'lang' => 'en',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
return parent::getNormalizedPostEntity() + [
|
||||
'_links' => [
|
||||
'type' => [
|
||||
'href' => $this->baseUrl . '/rest/type/node/camelids',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional\Hal;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group hal
|
||||
*/
|
||||
class NodeHalJsonBasicAuthTest extends NodeHalJsonAnonTest {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional\Hal;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group hal
|
||||
*/
|
||||
class NodeHalJsonCookieTest extends NodeHalJsonAnonTest {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional\Hal;
|
||||
|
||||
use Drupal\Tests\node\Functional\Rest\NodeTypeResourceTestBase;
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group hal
|
||||
*/
|
||||
class NodeTypeHalJsonAnonTest extends NodeTypeResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['hal'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'hal_json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/hal+json';
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional\Hal;
|
||||
|
||||
use Drupal\Tests\node\Functional\Rest\NodeTypeResourceTestBase;
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group hal
|
||||
*/
|
||||
class NodeTypeHalJsonBasicAuthTest extends NodeTypeResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['hal', 'basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'hal_json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/hal+json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional\Hal;
|
||||
|
||||
use Drupal\Tests\node\Functional\Rest\NodeTypeResourceTestBase;
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group hal
|
||||
*/
|
||||
class NodeTypeHalJsonCookieTest extends NodeTypeResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['hal'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'hal_json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/hal+json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional\Migrate\d6;
|
||||
|
||||
use Drupal\Tests\node\Kernel\Migrate\d6\MigrateNodeTestBase;
|
||||
|
||||
/**
|
||||
* Node content revisions migration.
|
||||
*
|
||||
* @group migrate_drupal_6
|
||||
*/
|
||||
class MigrateNodeRevisionTest extends MigrateNodeTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['language', 'content_translation', 'menu_ui'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->executeMigrations(['d6_node', 'd6_node_revision']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test node revisions migration from Drupal 6 to 8.
|
||||
*/
|
||||
public function testNodeRevision() {
|
||||
$node = \Drupal::entityManager()->getStorage('node')->loadRevision(2001);
|
||||
/** @var \Drupal\node\NodeInterface $node */
|
||||
$this->assertIdentical('1', $node->id());
|
||||
$this->assertIdentical('2001', $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\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 = mb_strtolower($this->randomMachineName());
|
||||
FieldStorageConfig::create([
|
||||
'field_name' => $this->fieldName,
|
||||
'entity_type' => 'node',
|
||||
'type' => 'text',
|
||||
'cardinality' => -1,
|
||||
])->save();
|
||||
|
||||
// Attach an instance of the field to the page content type.
|
||||
FieldConfig::create([
|
||||
'field_name' => $this->fieldName,
|
||||
'entity_type' => 'node',
|
||||
'bundle' => 'page',
|
||||
'label' => $this->randomMachineName() . '_label',
|
||||
])->save();
|
||||
entity_get_form_display('node', 'page', 'default')
|
||||
->setComponent($this->fieldName, [
|
||||
'type' => 'text_textfield',
|
||||
])
|
||||
->save();
|
||||
|
||||
$edit = [
|
||||
'title[0][value]' => 'a',
|
||||
'promote[value]' => FALSE,
|
||||
'sticky[value]' => 1,
|
||||
"{$this->fieldName}[0][value]" => $this->randomString(32),
|
||||
];
|
||||
$this->drupalPostForm('node/add/page', $edit, t('Add another item'));
|
||||
$this->assertNoFieldChecked('edit-promote-value', 'Promote stayed unchecked');
|
||||
$this->assertFieldChecked('edit-sticky-value', 'Sticky stayed checked');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Tests\system\Functional\Cache\AssertPageCacheContextsAndTagsTrait;
|
||||
|
||||
/**
|
||||
* Tests the node access automatic cacheability bubbling logic.
|
||||
*
|
||||
* @group node
|
||||
* @group Cache
|
||||
* @group cacheability_safeguards
|
||||
*/
|
||||
class NodeAccessAutoBubblingTest extends NodeTestBase {
|
||||
|
||||
use AssertPageCacheContextsAndTagsTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node_access_test', 'node_access_test_auto_bubbling'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
node_access_rebuild();
|
||||
|
||||
// Create some content.
|
||||
$this->drupalCreateNode();
|
||||
$this->drupalCreateNode();
|
||||
$this->drupalCreateNode();
|
||||
$this->drupalCreateNode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the node grants cache context is auto-added, only when needed.
|
||||
*
|
||||
* @see node_query_node_access_alter()
|
||||
*/
|
||||
public function testNodeAccessCacheabilitySafeguard() {
|
||||
$this->dumpHeaders = TRUE;
|
||||
|
||||
// The node grants cache context should be added automatically.
|
||||
$this->drupalGet(new Url('node_access_test_auto_bubbling'));
|
||||
$this->assertCacheContext('user.node_grants:view');
|
||||
|
||||
// The root user has the 'bypass node access' permission, which means the
|
||||
// node grants cache context is not necessary.
|
||||
$this->drupalLogin($this->rootUser);
|
||||
$this->drupalGet(new Url('node_access_test_auto_bubbling'));
|
||||
$this->assertNoCacheContext('user.node_grants:view');
|
||||
$this->drupalLogout();
|
||||
|
||||
// Uninstall the module with the only hook_node_grants() implementation.
|
||||
$this->container->get('module_installer')->uninstall(['node_access_test']);
|
||||
$this->rebuildContainer();
|
||||
|
||||
// Because there are no node grants defined, there also is no need for the
|
||||
// node grants cache context to be bubbled.
|
||||
$this->drupalGet(new Url('node_access_test_auto_bubbling'));
|
||||
$this->assertNoCacheContext('user.node_grants:view');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,226 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\node\Entity\NodeType;
|
||||
|
||||
/**
|
||||
* Tests behavior of the node access subsystem if the base table is not node.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeAccessBaseTableTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node_access_test', 'views'];
|
||||
|
||||
/**
|
||||
* The installation profile to use with this test.
|
||||
*
|
||||
* This test class requires the "tags" taxonomy field.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $profile = 'standard';
|
||||
|
||||
/**
|
||||
* Nodes by user.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $nodesByUser;
|
||||
|
||||
/**
|
||||
* A public tid.
|
||||
*
|
||||
* @var \Drupal\Core\Database\StatementInterface
|
||||
*/
|
||||
protected $publicTid;
|
||||
|
||||
/**
|
||||
* A private tid.
|
||||
*
|
||||
* @var \Drupal\Core\Database\StatementInterface
|
||||
*/
|
||||
protected $privateTid;
|
||||
|
||||
/**
|
||||
* A web user.
|
||||
*/
|
||||
protected $webUser;
|
||||
|
||||
/**
|
||||
* The nids visible.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $nidsVisible;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
node_access_test_add_field(NodeType::load('article'));
|
||||
|
||||
node_access_rebuild();
|
||||
\Drupal::state()->set('node_access_test.private', TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the "private" node access functionality.
|
||||
*
|
||||
* - Create 2 users with "access content" and "create article" permissions.
|
||||
* - Each user creates one private and one not private article.
|
||||
*
|
||||
* - Test that each user can view the other user's non-private article.
|
||||
* - Test that each user cannot view the other user's private article.
|
||||
* - Test that each user finds only appropriate (non-private + own private)
|
||||
* in taxonomy listing.
|
||||
* - Create another user with 'view any private content'.
|
||||
* - Test that user 4 can view all content created above.
|
||||
* - Test that user 4 can view all content on taxonomy listing.
|
||||
*/
|
||||
public function testNodeAccessBasic() {
|
||||
$num_simple_users = 2;
|
||||
$simple_users = [];
|
||||
|
||||
// Nodes keyed by uid and nid: $nodes[$uid][$nid] = $is_private;
|
||||
$this->nodesByUser = [];
|
||||
// Titles keyed by nid.
|
||||
$titles = [];
|
||||
// Array of nids marked private.
|
||||
$private_nodes = [];
|
||||
for ($i = 0; $i < $num_simple_users; $i++) {
|
||||
$simple_users[$i] = $this->drupalCreateUser(['access content', 'create article content']);
|
||||
}
|
||||
foreach ($simple_users as $this->webUser) {
|
||||
$this->drupalLogin($this->webUser);
|
||||
foreach ([0 => 'Public', 1 => 'Private'] as $is_private => $type) {
|
||||
$edit = [
|
||||
'title[0][value]' => t('@private_public Article created by @user', ['@private_public' => $type, '@user' => $this->webUser->getUsername()]),
|
||||
];
|
||||
if ($is_private) {
|
||||
$edit['private[0][value]'] = TRUE;
|
||||
$edit['body[0][value]'] = 'private node';
|
||||
$edit['field_tags[target_id]'] = 'private';
|
||||
}
|
||||
else {
|
||||
$edit['body[0][value]'] = 'public node';
|
||||
$edit['field_tags[target_id]'] = 'public';
|
||||
}
|
||||
|
||||
$this->drupalPostForm('node/add/article', $edit, t('Save'));
|
||||
$node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
|
||||
$this->assertEqual($is_private, (int) $node->private->value, 'The private status of the node was properly set in the node_access_test table.');
|
||||
if ($is_private) {
|
||||
$private_nodes[] = $node->id();
|
||||
}
|
||||
$titles[$node->id()] = $edit['title[0][value]'];
|
||||
$this->nodesByUser[$this->webUser->id()][$node->id()] = $is_private;
|
||||
}
|
||||
}
|
||||
$this->publicTid = db_query('SELECT tid FROM {taxonomy_term_field_data} WHERE name = :name AND default_langcode = 1', [':name' => 'public'])->fetchField();
|
||||
$this->privateTid = db_query('SELECT tid FROM {taxonomy_term_field_data} WHERE name = :name AND default_langcode = 1', [':name' => 'private'])->fetchField();
|
||||
$this->assertTrue($this->publicTid, 'Public tid was found');
|
||||
$this->assertTrue($this->privateTid, 'Private tid was found');
|
||||
foreach ($simple_users as $this->webUser) {
|
||||
$this->drupalLogin($this->webUser);
|
||||
// Check own nodes to see that all are readable.
|
||||
foreach ($this->nodesByUser as $uid => $data) {
|
||||
foreach ($data as $nid => $is_private) {
|
||||
$this->drupalGet('node/' . $nid);
|
||||
if ($is_private) {
|
||||
$should_be_visible = $uid == $this->webUser->id();
|
||||
}
|
||||
else {
|
||||
$should_be_visible = TRUE;
|
||||
}
|
||||
$this->assertResponse($should_be_visible ? 200 : 403, strtr('A %private node by user %uid is %visible for user %current_uid.', [
|
||||
'%private' => $is_private ? 'private' : 'public',
|
||||
'%uid' => $uid,
|
||||
'%visible' => $should_be_visible ? 'visible' : 'not visible',
|
||||
'%current_uid' => $this->webUser->id(),
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
// Check to see that the correct nodes are shown on taxonomy/private
|
||||
// and taxonomy/public.
|
||||
$this->assertTaxonomyPage(FALSE);
|
||||
}
|
||||
|
||||
// Now test that a user with 'node test view' permissions can view content.
|
||||
$access_user = $this->drupalCreateUser(['access content', 'create article content', 'node test view', 'search content']);
|
||||
$this->drupalLogin($access_user);
|
||||
|
||||
foreach ($this->nodesByUser as $private_status) {
|
||||
foreach ($private_status as $nid => $is_private) {
|
||||
$this->drupalGet('node/' . $nid);
|
||||
$this->assertResponse(200);
|
||||
}
|
||||
}
|
||||
|
||||
// This user should be able to see all of the nodes on the relevant
|
||||
// taxonomy pages.
|
||||
$this->assertTaxonomyPage(TRUE);
|
||||
|
||||
// Rebuild the node access permissions, repeat the test. This is done to
|
||||
// ensure that node access is rebuilt correctly even if the current user
|
||||
// does not have the bypass node access permission.
|
||||
node_access_rebuild();
|
||||
|
||||
foreach ($this->nodesByUser as $private_status) {
|
||||
foreach ($private_status as $nid => $is_private) {
|
||||
$this->drupalGet('node/' . $nid);
|
||||
$this->assertResponse(200);
|
||||
}
|
||||
}
|
||||
|
||||
// This user should be able to see all of the nodes on the relevant
|
||||
// taxonomy pages.
|
||||
$this->assertTaxonomyPage(TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks taxonomy/term listings to ensure only accessible nodes are listed.
|
||||
*
|
||||
* @param $is_admin
|
||||
* A boolean indicating whether the current user is an administrator. If
|
||||
* TRUE, all nodes should be listed. If FALSE, only public nodes and the
|
||||
* user's own private nodes should be listed.
|
||||
*/
|
||||
protected function assertTaxonomyPage($is_admin) {
|
||||
foreach ([$this->publicTid, $this->privateTid] as $tid_is_private => $tid) {
|
||||
$this->drupalGet("taxonomy/term/$tid");
|
||||
$this->nidsVisible = [];
|
||||
foreach ($this->xpath("//a[text()='Read more']") as $link) {
|
||||
// See also testTranslationRendering() in NodeTranslationUITest.
|
||||
$this->assertTrue(preg_match('|node/(\d+)$|', $link->getAttribute('href'), $matches), 'Read more points to a node');
|
||||
$this->nidsVisible[$matches[1]] = TRUE;
|
||||
}
|
||||
foreach ($this->nodesByUser as $uid => $data) {
|
||||
foreach ($data as $nid => $is_private) {
|
||||
// Private nodes should be visible on the private term page,
|
||||
// public nodes should be visible on the public term page.
|
||||
$should_be_visible = $tid_is_private == $is_private;
|
||||
// Non-administrators can only see their own nodes on the private
|
||||
// term page.
|
||||
if (!$is_admin && $tid_is_private) {
|
||||
$should_be_visible = $should_be_visible && $uid == $this->webUser->id();
|
||||
}
|
||||
$this->assertIdentical(isset($this->nidsVisible[$nid]), $should_be_visible, strtr('A %private node by user %uid is %visible for user %current_uid on the %tid_is_private page.', [
|
||||
'%private' => $is_private ? 'private' : 'public',
|
||||
'%uid' => $uid,
|
||||
'%visible' => isset($this->nidsVisible[$nid]) ? 'visible' : 'not visible',
|
||||
'%current_uid' => $this->webUser->id(),
|
||||
'%tid_is_private' => $tid_is_private ? 'private' : 'public',
|
||||
]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\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 = mb_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,131 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
|
||||
/**
|
||||
* Tests that the node_access system stores the proper fallback marker.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeAccessLanguageFallbackTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Enable language and a non-language-aware node access module.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['language', 'node_access_test', 'content_translation'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// After enabling a node access module, the {node_access} table has to be
|
||||
// rebuilt.
|
||||
node_access_rebuild();
|
||||
|
||||
// Add Hungarian, Catalan, and Afrikaans.
|
||||
ConfigurableLanguage::createFromLangcode('hu')->save();
|
||||
ConfigurableLanguage::createFromLangcode('ca')->save();
|
||||
ConfigurableLanguage::createFromLangcode('af')->save();
|
||||
|
||||
// Enable content translation for the current entity type.
|
||||
\Drupal::service('content_translation.manager')->setEnabled('node', 'page', TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests node access fallback handling with multiple node languages.
|
||||
*/
|
||||
public function testNodeAccessLanguageFallback() {
|
||||
// The node_access_test module allows nodes to be marked private. We need to
|
||||
// ensure that system honors the fallback system of node access properly.
|
||||
// Note that node_access_test_language is language-sensitive and does not
|
||||
// apply to the fallback test.
|
||||
|
||||
// Create one node in Hungarian and marked as private.
|
||||
$node = $this->drupalCreateNode([
|
||||
'body' => [[]],
|
||||
'langcode' => 'hu',
|
||||
'private' => [['value' => 1]],
|
||||
'status' => 1,
|
||||
]);
|
||||
|
||||
// There should be one entry in node_access, with fallback set to hu.
|
||||
$this->checkRecords(1, 'hu');
|
||||
|
||||
// Create a translation user.
|
||||
$admin = $this->drupalCreateUser([
|
||||
'bypass node access',
|
||||
'administer nodes',
|
||||
'translate any entity',
|
||||
'administer content translation',
|
||||
]);
|
||||
$this->drupalLogin($admin);
|
||||
$this->drupalGet('node/' . $node->id() . '/translations');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
// Create a Catalan translation through the UI.
|
||||
$url_options = ['language' => \Drupal::languageManager()->getLanguage('ca')];
|
||||
$this->drupalGet('node/' . $node->id() . '/translations/add/hu/ca', $url_options);
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
// Save the form.
|
||||
$this->getSession()->getPage()->pressButton('Save (this translation)');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
// Check the node access table.
|
||||
$this->checkRecords(2, 'hu');
|
||||
|
||||
// Programmatically create a translation. This process lets us check that
|
||||
// both forms and code behave in the same way.
|
||||
$storage = \Drupal::entityTypeManager()->getStorage('node');
|
||||
// Reload the node.
|
||||
$node = $storage->load(1);
|
||||
// Create an Afrikaans translation.
|
||||
$translation = $node->addTranslation('af');
|
||||
$translation->title->value = $this->randomString();
|
||||
$translation->status = 1;
|
||||
$node->save();
|
||||
|
||||
// Check the node access table.
|
||||
$this->checkRecords(3, 'hu');
|
||||
|
||||
// For completeness, edit the Catalan version again.
|
||||
$this->drupalGet('node/' . $node->id() . '/edit', $url_options);
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
// Save the form.
|
||||
$this->getSession()->getPage()->pressButton('Save (this translation)');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
// Check the node access table.
|
||||
$this->checkRecords(3, 'hu');
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries the node_access table and checks for proper storage.
|
||||
*
|
||||
* @param int $count
|
||||
* The number of rows expected by the query (equal to the translation
|
||||
* count).
|
||||
* @param $langcode
|
||||
* The expected language code set as the fallback property.
|
||||
*/
|
||||
public function checkRecords($count, $langcode = 'hu') {
|
||||
$select = \Drupal::database()
|
||||
->select('node_access', 'na')
|
||||
->fields('na', ['nid', 'fallback', 'langcode', 'grant_view'])
|
||||
->condition('na.realm', 'node_access_test', '=')
|
||||
->condition('na.gid', 8888, '=');
|
||||
$records = $select->execute()->fetchAll();
|
||||
// Check that the expected record count is returned.
|
||||
$this->assertEquals(count($records), $count);
|
||||
// The fallback value is 'hu' and should be set to 1. For other languages,
|
||||
// it should be set to 0. Casting to boolean lets us run that comparison.
|
||||
foreach ($records as $record) {
|
||||
$this->assertEquals((bool) $record->fallback, $record->langcode === $langcode);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,255 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Tests node_access and db_select() with node_access tag functionality with
|
||||
* multiple languages with a test node access module that is not language-aware.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeAccessLanguageTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['language', 'node_access_test'];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
node_access_test_add_field(NodeType::load('page'));
|
||||
|
||||
// After enabling a node access module, the access table has to be rebuild.
|
||||
node_access_rebuild();
|
||||
|
||||
// Enable the private node feature of the node_access_test module.
|
||||
\Drupal::state()->set('node_access_test.private', TRUE);
|
||||
|
||||
// Add Hungarian, Catalan and Croatian.
|
||||
ConfigurableLanguage::createFromLangcode('hu')->save();
|
||||
ConfigurableLanguage::createFromLangcode('ca')->save();
|
||||
ConfigurableLanguage::createFromLangcode('hr')->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests node access with multiple node languages and no private nodes.
|
||||
*/
|
||||
public function testNodeAccess() {
|
||||
$web_user = $this->drupalCreateUser(['access content']);
|
||||
|
||||
$expected_node_access = ['view' => TRUE, 'update' => FALSE, 'delete' => FALSE];
|
||||
$expected_node_access_no_access = ['view' => FALSE, 'update' => FALSE, 'delete' => FALSE];
|
||||
|
||||
// Creating a public node with langcode Hungarian, will be saved as the
|
||||
// fallback in node access table.
|
||||
$node_public_hu = $this->drupalCreateNode(['body' => [[]], 'langcode' => 'hu', 'private' => FALSE]);
|
||||
$this->assertTrue($node_public_hu->language()->getId() == 'hu', 'Node created as Hungarian.');
|
||||
|
||||
// Tests the default access is provided for the public Hungarian node.
|
||||
$this->assertNodeAccess($expected_node_access, $node_public_hu, $web_user);
|
||||
|
||||
// Tests that Hungarian provided specifically results in the same.
|
||||
$this->assertNodeAccess($expected_node_access, $node_public_hu->getTranslation('hu'), $web_user);
|
||||
|
||||
// Creating a public node with no special langcode, like when no language
|
||||
// module enabled.
|
||||
$node_public_no_language = $this->drupalCreateNode([
|
||||
'private' => FALSE,
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
]);
|
||||
$this->assertTrue($node_public_no_language->language()->getId() == LanguageInterface::LANGCODE_NOT_SPECIFIED, 'Node created with not specified language.');
|
||||
|
||||
// Tests that access is granted if requested with no language.
|
||||
$this->assertNodeAccess($expected_node_access, $node_public_no_language, $web_user);
|
||||
|
||||
// Reset the node access cache and turn on our test node access code.
|
||||
\Drupal::entityManager()->getAccessControlHandler('node')->resetCache();
|
||||
\Drupal::state()->set('node_access_test_secret_catalan', 1);
|
||||
$node_public_ca = $this->drupalCreateNode(['body' => [[]], 'langcode' => 'ca', 'private' => FALSE]);
|
||||
$this->assertTrue($node_public_ca->language()->getId() == 'ca', 'Node created as Catalan.');
|
||||
|
||||
// Tests that access is granted if requested with no language.
|
||||
$this->assertNodeAccess($expected_node_access, $node_public_no_language, $web_user);
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $node_public_ca, $web_user);
|
||||
|
||||
// Tests that Hungarian node is still accessible.
|
||||
$this->assertNodeAccess($expected_node_access, $node_public_hu, $web_user);
|
||||
$this->assertNodeAccess($expected_node_access, $node_public_hu->getTranslation('hu'), $web_user);
|
||||
|
||||
// Tests that Catalan is still not accessible.
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $node_public_ca->getTranslation('ca'), $web_user);
|
||||
|
||||
// Make Catalan accessible.
|
||||
\Drupal::state()->set('node_access_test_secret_catalan', 0);
|
||||
|
||||
// Tests that Catalan is accessible on a node with a Catalan version as the
|
||||
// static cache has not been reset.
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $node_public_ca, $web_user);
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $node_public_ca->getTranslation('ca'), $web_user);
|
||||
|
||||
\Drupal::entityManager()->getAccessControlHandler('node')->resetCache();
|
||||
|
||||
// Tests that access is granted if requested with no language.
|
||||
$this->assertNodeAccess($expected_node_access, $node_public_no_language, $web_user);
|
||||
$this->assertNodeAccess($expected_node_access, $node_public_ca, $web_user);
|
||||
|
||||
// Tests that Hungarian node is still accessible.
|
||||
$this->assertNodeAccess($expected_node_access, $node_public_hu, $web_user);
|
||||
$this->assertNodeAccess($expected_node_access, $node_public_hu->getTranslation('hu'), $web_user);
|
||||
|
||||
// Tests that Catalan is accessible on a node with a Catalan version.
|
||||
$this->assertNodeAccess($expected_node_access, $node_public_ca->getTranslation('ca'), $web_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests node access with multiple node languages and private nodes.
|
||||
*/
|
||||
public function testNodeAccessPrivate() {
|
||||
$web_user = $this->drupalCreateUser(['access content']);
|
||||
$expected_node_access = ['view' => TRUE, 'update' => FALSE, 'delete' => FALSE];
|
||||
$expected_node_access_no_access = ['view' => FALSE, 'update' => FALSE, 'delete' => FALSE];
|
||||
|
||||
// Creating a private node with langcode Hungarian, will be saved as the
|
||||
// fallback in node access table.
|
||||
$node_private_hu = $this->drupalCreateNode(['body' => [[]], 'langcode' => 'hu', 'private' => TRUE]);
|
||||
$this->assertTrue($node_private_hu->language()->getId() == 'hu', 'Node created as Hungarian.');
|
||||
|
||||
// Tests the default access is not provided for the private Hungarian node.
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $node_private_hu, $web_user);
|
||||
|
||||
// Tests that Hungarian provided specifically results in the same.
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $node_private_hu->getTranslation('hu'), $web_user);
|
||||
|
||||
// Creating a private node with no special langcode, like when no language
|
||||
// module enabled.
|
||||
$node_private_no_language = $this->drupalCreateNode([
|
||||
'private' => TRUE,
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
]);
|
||||
$this->assertTrue($node_private_no_language->language()->getId() == LanguageInterface::LANGCODE_NOT_SPECIFIED, 'Node created with not specified language.');
|
||||
|
||||
// Tests that access is not granted if requested with no language.
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $node_private_no_language, $web_user);
|
||||
|
||||
// Reset the node access cache and turn on our test node access code.
|
||||
\Drupal::entityManager()->getAccessControlHandler('node')->resetCache();
|
||||
\Drupal::state()->set('node_access_test_secret_catalan', 1);
|
||||
|
||||
// Tests that access is not granted if requested with no language.
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $node_private_no_language, $web_user);
|
||||
|
||||
// Creating a private node with langcode Catalan to test that the
|
||||
// node_access_test_secret_catalan flag works.
|
||||
$private_ca_user = $this->drupalCreateUser(['access content', 'node test view']);
|
||||
$node_private_ca = $this->drupalCreateNode(['body' => [[]], 'langcode' => 'ca', 'private' => TRUE]);
|
||||
$this->assertTrue($node_private_ca->language()->getId() == 'ca', 'Node created as Catalan.');
|
||||
|
||||
// Tests that Catalan is still not accessible to either user.
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $node_private_ca, $web_user);
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $node_private_ca->getTranslation('ca'), $web_user);
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $node_private_ca, $private_ca_user);
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $node_private_ca->getTranslation('ca'), $private_ca_user);
|
||||
|
||||
\Drupal::entityManager()->getAccessControlHandler('node')->resetCache();
|
||||
\Drupal::state()->set('node_access_test_secret_catalan', 0);
|
||||
|
||||
// Tests that Catalan is still not accessible for a user with no access to
|
||||
// private nodes.
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $node_private_ca, $web_user);
|
||||
$this->assertNodeAccess($expected_node_access_no_access, $node_private_ca->getTranslation('ca'), $web_user);
|
||||
|
||||
// Tests that Catalan is accessible by a user with the permission to see
|
||||
// private nodes.
|
||||
$this->assertNodeAccess($expected_node_access, $node_private_ca, $private_ca_user);
|
||||
$this->assertNodeAccess($expected_node_access, $node_private_ca->getTranslation('ca'), $private_ca_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests db_select() with a 'node_access' tag and langcode metadata.
|
||||
*/
|
||||
public function testNodeAccessQueryTag() {
|
||||
// Create a normal authenticated user.
|
||||
$web_user = $this->drupalCreateUser(['access content']);
|
||||
|
||||
// Load the user 1 user for later use as an admin user with permission to
|
||||
// see everything.
|
||||
$admin_user = User::load(1);
|
||||
|
||||
// Creating a private node with langcode Hungarian, will be saved as
|
||||
// the fallback in node access table.
|
||||
$node_private = $this->drupalCreateNode(['body' => [[]], 'langcode' => 'hu', 'private' => TRUE]);
|
||||
$this->assertTrue($node_private->language()->getId() == 'hu', 'Node created as Hungarian.');
|
||||
|
||||
// Creating a public node with langcode Hungarian, will be saved as
|
||||
// the fallback in node access table.
|
||||
$node_public = $this->drupalCreateNode(['body' => [[]], 'langcode' => 'hu', 'private' => FALSE]);
|
||||
$this->assertTrue($node_public->language()->getId() == 'hu', 'Node created as Hungarian.');
|
||||
|
||||
// Creating a public node with no special langcode, like when no language
|
||||
// module enabled.
|
||||
$node_no_language = $this->drupalCreateNode([
|
||||
'private' => FALSE,
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
]);
|
||||
$this->assertTrue($node_no_language->language()->getId() == LanguageInterface::LANGCODE_NOT_SPECIFIED, 'Node created with not specified language.');
|
||||
|
||||
// Query the nodes table as the web user with the node access tag and no
|
||||
// specific langcode.
|
||||
$select = db_select('node', 'n')
|
||||
->fields('n', ['nid'])
|
||||
->addMetaData('account', $web_user)
|
||||
->addTag('node_access');
|
||||
$nids = $select->execute()->fetchAllAssoc('nid');
|
||||
|
||||
// The public node and no language node should be returned. Because no
|
||||
// langcode is given it will use the fallback node.
|
||||
$this->assertEqual(count($nids), 2, 'db_select() returns 2 node');
|
||||
$this->assertTrue(array_key_exists($node_public->id(), $nids), 'Returned node ID is public node.');
|
||||
$this->assertTrue(array_key_exists($node_no_language->id(), $nids), 'Returned node ID is no language node.');
|
||||
|
||||
// Query the nodes table as the web user with the node access tag and
|
||||
// langcode de.
|
||||
$select = db_select('node', 'n')
|
||||
->fields('n', ['nid'])
|
||||
->addMetaData('account', $web_user)
|
||||
->addMetaData('langcode', 'de')
|
||||
->addTag('node_access');
|
||||
$nids = $select->execute()->fetchAllAssoc('nid');
|
||||
|
||||
// Because no nodes are created in German, no nodes are returned.
|
||||
$this->assertTrue(empty($nids), 'db_select() returns an empty result.');
|
||||
|
||||
// Query the nodes table as admin user (full access) with the node access
|
||||
// tag and no specific langcode.
|
||||
$select = db_select('node', 'n')
|
||||
->fields('n', ['nid'])
|
||||
->addMetaData('account', $admin_user)
|
||||
->addTag('node_access');
|
||||
$nids = $select->execute()->fetchAllAssoc('nid');
|
||||
|
||||
// All nodes are returned.
|
||||
$this->assertEqual(count($nids), 3, 'db_select() returns all three nodes.');
|
||||
|
||||
// Query the nodes table as admin user (full access) with the node access
|
||||
// tag and langcode de.
|
||||
$select = db_select('node', 'n')
|
||||
->fields('n', ['nid'])
|
||||
->addMetaData('account', $admin_user)
|
||||
->addMetaData('langcode', 'de')
|
||||
->addTag('node_access');
|
||||
$nids = $select->execute()->fetchAllAssoc('nid');
|
||||
|
||||
// All nodes are returned because node access tag is not invoked when the
|
||||
// user is user 1.
|
||||
$this->assertEqual(count($nids), 3, 'db_select() returns all three nodes.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\user\RoleInterface;
|
||||
|
||||
/**
|
||||
* Tests the interaction of the node access system with menu links.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeAccessMenuLinkTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['menu_ui', 'block'];
|
||||
|
||||
/**
|
||||
* A user with permission to manage menu links and create nodes.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $contentAdminUser;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalPlaceBlock('system_menu_block:main');
|
||||
|
||||
$this->contentAdminUser = $this->drupalCreateUser([
|
||||
'access content',
|
||||
'administer content types',
|
||||
'administer menu',
|
||||
]);
|
||||
|
||||
$this->config('user.role.' . RoleInterface::ANONYMOUS_ID)->set('permissions', [])->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* SA-CORE-2015-003: Tests menu links to nodes when node access is restricted.
|
||||
*/
|
||||
public function testNodeAccessMenuLink() {
|
||||
|
||||
$menu_link_title = $this->randomString();
|
||||
|
||||
$this->drupalLogin($this->contentAdminUser);
|
||||
$edit = [
|
||||
'title[0][value]' => $this->randomString(),
|
||||
'body[0][value]' => $this->randomString(),
|
||||
'menu[enabled]' => 1,
|
||||
'menu[title]' => $menu_link_title,
|
||||
];
|
||||
$this->drupalPostForm('node/add/page', $edit, t('Save'));
|
||||
$this->assertLink($menu_link_title);
|
||||
|
||||
// Ensure anonymous users without "access content" permission do not see
|
||||
// this menu link.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('');
|
||||
$this->assertNoLink($menu_link_title);
|
||||
|
||||
// Ensure anonymous users with "access content" permission see this menu
|
||||
// link.
|
||||
$this->config('user.role.' . RoleInterface::ANONYMOUS_ID)->set('permissions', ['access content'])->save();
|
||||
$this->drupalGet('');
|
||||
$this->assertLink($menu_link_title);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\comment\CommentInterface;
|
||||
use Drupal\comment\Tests\CommentTestTrait;
|
||||
use Drupal\comment\Entity\Comment;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests access controlled node views have the right amount of comment pages.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeAccessPagerTest extends BrowserTestBase {
|
||||
|
||||
use CommentTestTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node_access_test', 'comment', 'forum'];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
node_access_rebuild();
|
||||
$this->drupalCreateContentType(['type' => 'page', 'name' => t('Basic page')]);
|
||||
$this->addDefaultCommentField('node', 'page');
|
||||
$this->webUser = $this->drupalCreateUser(['access content', 'access comments', 'node test view']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the comment pager for nodes with multiple grants per realm.
|
||||
*/
|
||||
public function testCommentPager() {
|
||||
// Create a node.
|
||||
$node = $this->drupalCreateNode();
|
||||
|
||||
// Create 60 comments.
|
||||
for ($i = 0; $i < 60; $i++) {
|
||||
$comment = Comment::create([
|
||||
'entity_id' => $node->id(),
|
||||
'entity_type' => 'node',
|
||||
'field_name' => 'comment',
|
||||
'subject' => $this->randomMachineName(),
|
||||
'comment_body' => [
|
||||
['value' => $this->randomMachineName()],
|
||||
],
|
||||
'status' => CommentInterface::PUBLISHED,
|
||||
]);
|
||||
$comment->save();
|
||||
}
|
||||
|
||||
$this->drupalLogin($this->webUser);
|
||||
|
||||
// View the node page. With the default 50 comments per page there should
|
||||
// be two pages (0, 1) but no third (2) page.
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$this->assertText($node->label());
|
||||
$this->assertText(t('Comments'));
|
||||
$this->assertRaw('page=1');
|
||||
$this->assertNoRaw('page=2');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the forum node pager for nodes with multiple grants per realm.
|
||||
*/
|
||||
public function testForumPager() {
|
||||
// Look up the forums vocabulary ID.
|
||||
$vid = $this->config('forum.settings')->get('vocabulary');
|
||||
$this->assertTrue($vid, 'Forum navigation vocabulary ID is set.');
|
||||
|
||||
// Look up the general discussion term.
|
||||
$tree = \Drupal::entityManager()->getStorage('taxonomy_term')->loadTree($vid, 0, 1);
|
||||
$tid = reset($tree)->tid;
|
||||
$this->assertTrue($tid, 'General discussion term is found in the forum vocabulary.');
|
||||
|
||||
// Create 30 nodes.
|
||||
for ($i = 0; $i < 30; $i++) {
|
||||
$this->drupalCreateNode([
|
||||
'nid' => NULL,
|
||||
'type' => 'forum',
|
||||
'taxonomy_forums' => [
|
||||
['target_id' => $tid],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
// View the general discussion forum page. With the default 25 nodes per
|
||||
// page there should be two pages for 30 nodes, no more.
|
||||
$this->drupalLogin($this->webUser);
|
||||
$this->drupalGet('forum/' . $tid);
|
||||
$this->assertRaw('page=1');
|
||||
$this->assertNoRaw('page=2');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\node\Entity\NodeType;
|
||||
|
||||
/**
|
||||
* Ensures that node access rebuild functions work correctly even
|
||||
* when other modules implements hook_node_grants().
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeAccessRebuildNodeGrantsTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* A user to create nodes that only it has access to.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $webUser;
|
||||
|
||||
/**
|
||||
* A user to test the rebuild nodes feature which can't access the nodes.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->adminUser = $this->drupalCreateUser(['administer site configuration', 'access administration pages', 'access site reports']);
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
$this->webUser = $this->drupalCreateUser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests rebuilding the node access permissions table with content.
|
||||
*/
|
||||
public function testNodeAccessRebuildNodeGrants() {
|
||||
\Drupal::service('module_installer')->install(['node_access_test']);
|
||||
\Drupal::state()->set('node_access_test.private', TRUE);
|
||||
node_access_test_add_field(NodeType::load('page'));
|
||||
$this->resetAll();
|
||||
|
||||
// Create 30 nodes so that _node_access_rebuild_batch_operation() has to run
|
||||
// more than once.
|
||||
for ($i = 0; $i < 30; $i++) {
|
||||
$nodes[] = $this->drupalCreateNode([
|
||||
'uid' => $this->webUser->id(),
|
||||
'private' => [['value' => 1]],
|
||||
]);
|
||||
}
|
||||
|
||||
/** @var \Drupal\node\NodeGrantDatabaseStorageInterface $grant_storage */
|
||||
$grant_storage = \Drupal::service('node.grant_storage');
|
||||
// Default realm access and node records are present.
|
||||
foreach ($nodes as $node) {
|
||||
$this->assertTrue($node->private->value);
|
||||
$this->assertTrue($grant_storage->access($node, 'view', $this->webUser)->isAllowed(), 'Prior to rebuilding node access the grant storage returns allowed for the node author.');
|
||||
$this->assertTrue($grant_storage->access($node, 'view', $this->adminUser)->isAllowed(), 'Prior to rebuilding node access the grant storage returns allowed for the admin user.');
|
||||
}
|
||||
|
||||
$this->assertEqual(1, \Drupal::service('node.grant_storage')->checkAll($this->webUser), 'There is an all realm access record');
|
||||
$this->assertTrue(\Drupal::state()->get('node.node_access_needs_rebuild'), 'Node access permissions need to be rebuilt');
|
||||
|
||||
// Rebuild permissions.
|
||||
$this->drupalGet('admin/reports/status');
|
||||
$this->clickLink(t('Rebuild permissions'));
|
||||
$this->drupalPostForm(NULL, [], t('Rebuild permissions'));
|
||||
$this->assertText(t('The content access permissions have been rebuilt.'));
|
||||
|
||||
// Test if the rebuild by user that cannot bypass node access and does not
|
||||
// have access to the nodes has been successful.
|
||||
$this->assertFalse($this->adminUser->hasPermission('bypass node access'));
|
||||
$this->assertNull(\Drupal::state()->get('node.node_access_needs_rebuild'), 'Node access permissions have been rebuilt');
|
||||
foreach ($nodes as $node) {
|
||||
$this->assertTrue($grant_storage->access($node, 'view', $this->webUser)->isAllowed(), 'After rebuilding node access the grant storage returns allowed for the node author.');
|
||||
$this->assertFalse($grant_storage->access($node, 'view', $this->adminUser)->isForbidden(), 'After rebuilding node access the grant storage returns forbidden for the admin user.');
|
||||
}
|
||||
$this->assertFalse(\Drupal::service('node.grant_storage')->checkAll($this->webUser), 'There is no all realm access record');
|
||||
|
||||
// Test an anonymous node access rebuild from code.
|
||||
$this->drupalLogout();
|
||||
node_access_rebuild();
|
||||
foreach ($nodes as $node) {
|
||||
$this->assertTrue($grant_storage->access($node, 'view', $this->webUser)->isAllowed(), 'After rebuilding node access the grant storage returns allowed for the node author.');
|
||||
$this->assertFalse($grant_storage->access($node, 'view', $this->adminUser)->isForbidden(), 'After rebuilding node access the grant storage returns forbidden for the admin user.');
|
||||
}
|
||||
$this->assertFalse(\Drupal::service('node.grant_storage')->checkAll($this->webUser), 'There is no all realm access record');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests rebuilding the node access permissions table with no content.
|
||||
*/
|
||||
public function testNodeAccessRebuildNoAccessModules() {
|
||||
// Default realm access is present.
|
||||
$this->assertEqual(1, \Drupal::service('node.grant_storage')->count(), 'There is an all realm access record');
|
||||
|
||||
// No need to rebuild permissions.
|
||||
$this->assertFalse(\Drupal::state()->get('node.node_access_needs_rebuild'), 'Node access permissions need to be rebuilt');
|
||||
|
||||
// Rebuild permissions.
|
||||
$this->drupalGet('admin/reports/status');
|
||||
$this->clickLink(t('Rebuild permissions'));
|
||||
$this->drupalPostForm(NULL, [], t('Rebuild permissions'));
|
||||
$this->assertText(t('Content permissions have been rebuilt.'));
|
||||
$this->assertNull(\Drupal::state()->get('node.node_access_needs_rebuild'), 'Node access permissions have been rebuilt');
|
||||
|
||||
// Default realm access is still present.
|
||||
$this->assertEqual(1, \Drupal::service('node.grant_storage')->count(), 'There is an all realm access record');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\node\Entity\Node;
|
||||
|
||||
/**
|
||||
* Tests hook_node_access_records when acquiring grants.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeAccessRecordsTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Enable a module that implements node access API hooks and alter hook.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node_test'];
|
||||
|
||||
/**
|
||||
* Creates a node and tests the creation of node access rules.
|
||||
*/
|
||||
public function testNodeAccessRecords() {
|
||||
// Create an article node.
|
||||
$node1 = $this->drupalCreateNode(['type' => 'article']);
|
||||
$this->assertTrue(Node::load($node1->id()), 'Article node created.');
|
||||
|
||||
// Check to see if grants added by node_test_node_access_records made it in.
|
||||
$records = db_query('SELECT realm, gid FROM {node_access} WHERE nid = :nid', [':nid' => $node1->id()])->fetchAll();
|
||||
$this->assertEqual(count($records), 1, 'Returned the correct number of rows.');
|
||||
$this->assertEqual($records[0]->realm, 'test_article_realm', 'Grant with article_realm acquired for node without alteration.');
|
||||
$this->assertEqual($records[0]->gid, 1, 'Grant with gid = 1 acquired for node without alteration.');
|
||||
|
||||
// Create an unpromoted "Basic page" node.
|
||||
$node2 = $this->drupalCreateNode(['type' => 'page', 'promote' => 0]);
|
||||
$this->assertTrue(Node::load($node2->id()), 'Unpromoted basic page node created.');
|
||||
|
||||
// Check to see if grants added by node_test_node_access_records made it in.
|
||||
$records = db_query('SELECT realm, gid FROM {node_access} WHERE nid = :nid', [':nid' => $node2->id()])->fetchAll();
|
||||
$this->assertEqual(count($records), 1, 'Returned the correct number of rows.');
|
||||
$this->assertEqual($records[0]->realm, 'test_page_realm', 'Grant with page_realm acquired for node without alteration.');
|
||||
$this->assertEqual($records[0]->gid, 1, 'Grant with gid = 1 acquired for node without alteration.');
|
||||
|
||||
// Create an unpromoted, unpublished "Basic page" node.
|
||||
$node3 = $this->drupalCreateNode(['type' => 'page', 'promote' => 0, 'status' => 0]);
|
||||
$this->assertTrue(Node::load($node3->id()), 'Unpromoted, unpublished basic page node created.');
|
||||
|
||||
// Check to see if grants added by node_test_node_access_records made it in.
|
||||
$records = db_query('SELECT realm, gid FROM {node_access} WHERE nid = :nid', [':nid' => $node3->id()])->fetchAll();
|
||||
$this->assertEqual(count($records), 1, 'Returned the correct number of rows.');
|
||||
$this->assertEqual($records[0]->realm, 'test_page_realm', 'Grant with page_realm acquired for node without alteration.');
|
||||
$this->assertEqual($records[0]->gid, 1, 'Grant with gid = 1 acquired for node without alteration.');
|
||||
|
||||
// Create a promoted "Basic page" node.
|
||||
$node4 = $this->drupalCreateNode(['type' => 'page', 'promote' => 1]);
|
||||
$this->assertTrue(Node::load($node4->id()), 'Promoted basic page node created.');
|
||||
|
||||
// Check to see if grant added by node_test_node_access_records was altered
|
||||
// by node_test_node_access_records_alter.
|
||||
$records = db_query('SELECT realm, gid FROM {node_access} WHERE nid = :nid', [':nid' => $node4->id()])->fetchAll();
|
||||
$this->assertEqual(count($records), 1, 'Returned the correct number of rows.');
|
||||
$this->assertEqual($records[0]->realm, 'test_alter_realm', 'Altered grant with alter_realm acquired for node.');
|
||||
$this->assertEqual($records[0]->gid, 2, 'Altered grant with gid = 2 acquired for node.');
|
||||
|
||||
// Check to see if we can alter grants with hook_node_grants_alter().
|
||||
$operations = ['view', 'update', 'delete'];
|
||||
// Create a user that is allowed to access content.
|
||||
$web_user = $this->drupalCreateUser(['access content']);
|
||||
foreach ($operations as $op) {
|
||||
$grants = node_test_node_grants($web_user, $op);
|
||||
$altered_grants = $grants;
|
||||
\Drupal::moduleHandler()->alter('node_grants', $altered_grants, $web_user, $op);
|
||||
$this->assertNotEqual($grants, $altered_grants, format_string('Altered the %op grant for a user.', ['%op' => $op]));
|
||||
}
|
||||
|
||||
// Check that core does not grant access to an unpublished node when an
|
||||
// empty $grants array is returned.
|
||||
$node6 = $this->drupalCreateNode(['status' => 0, 'disable_node_access' => TRUE]);
|
||||
$records = db_query('SELECT realm, gid FROM {node_access} WHERE nid = :nid', [':nid' => $node6->id()])->fetchAll();
|
||||
$this->assertEqual(count($records), 0, 'Returned no records for unpublished node.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
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'] = '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/node_assign_owner_action', $edit, t('Save'));
|
||||
$this->assertResponse(200);
|
||||
|
||||
$action_id = $edit['id'];
|
||||
|
||||
// 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'));
|
||||
$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(NULL, $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($action_id);
|
||||
$this->assertFalse($action, 'The node_assign_owner_action action is not available after being deleted.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,204 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\user\RoleInterface;
|
||||
|
||||
/**
|
||||
* Tests node administration page functionality.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeAdminTest extends NodeTestBase {
|
||||
/**
|
||||
* A user with permission to bypass access content.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* A user with the 'access content overview' permission.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $baseUser1;
|
||||
|
||||
/**
|
||||
* A normal user with permission to view own unpublished content.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $baseUser2;
|
||||
|
||||
/**
|
||||
* A normal user with permission to bypass node access content.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $baseUser3;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['views'];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Remove the "view own unpublished content" permission which is set
|
||||
// by default for authenticated users so we can test this permission
|
||||
// correctly.
|
||||
user_role_revoke_permissions(RoleInterface::AUTHENTICATED_ID, ['view own unpublished content']);
|
||||
|
||||
$this->adminUser = $this->drupalCreateUser(['access administration pages', 'access content overview', 'administer nodes', 'bypass node access']);
|
||||
$this->baseUser1 = $this->drupalCreateUser(['access content overview']);
|
||||
$this->baseUser2 = $this->drupalCreateUser(['access content overview', 'view own unpublished content']);
|
||||
$this->baseUser3 = $this->drupalCreateUser(['access content overview', 'bypass node access']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the table sorting works on the content admin pages.
|
||||
*/
|
||||
public function testContentAdminSort() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
$changed = REQUEST_TIME;
|
||||
foreach (['dd', 'aa', 'DD', 'bb', 'cc', 'CC', 'AA', 'BB'] as $prefix) {
|
||||
$changed += 1000;
|
||||
$node = $this->drupalCreateNode(['title' => $prefix . $this->randomMachineName(6)]);
|
||||
db_update('node_field_data')
|
||||
->fields(['changed' => $changed])
|
||||
->condition('nid', $node->id())
|
||||
->execute();
|
||||
}
|
||||
|
||||
// Test that the default sort by node.changed DESC actually fires properly.
|
||||
$nodes_query = db_select('node_field_data', 'n')
|
||||
->fields('n', ['title'])
|
||||
->orderBy('changed', 'DESC')
|
||||
->execute()
|
||||
->fetchCol();
|
||||
|
||||
$this->drupalGet('admin/content');
|
||||
foreach ($nodes_query as $delta => $string) {
|
||||
$elements = $this->xpath('//table[contains(@class, :class)]/tbody/tr[' . ($delta + 1) . ']/td[2]/a[normalize-space(text())=:label]', [':class' => 'views-table', ':label' => $string]);
|
||||
$this->assertTrue(!empty($elements), 'The node was found in the correct order.');
|
||||
}
|
||||
|
||||
// Compare the rendered HTML node list to a query for the nodes ordered by
|
||||
// title to account for possible database-dependent sort order.
|
||||
$nodes_query = db_select('node_field_data', 'n')
|
||||
->fields('n', ['title'])
|
||||
->orderBy('title')
|
||||
->execute()
|
||||
->fetchCol();
|
||||
|
||||
$this->drupalGet('admin/content', ['query' => ['sort' => 'asc', 'order' => 'title']]);
|
||||
foreach ($nodes_query as $delta => $string) {
|
||||
$elements = $this->xpath('//table[contains(@class, :class)]/tbody/tr[' . ($delta + 1) . ']/td[2]/a[normalize-space(text())=:label]', [':class' => 'views-table', ':label' => $string]);
|
||||
$this->assertTrue(!empty($elements), 'The node was found in the correct order.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests content overview with different user permissions.
|
||||
*
|
||||
* Taxonomy filters are tested separately.
|
||||
*
|
||||
* @see TaxonomyNodeFilterTestCase
|
||||
*/
|
||||
public function testContentAdminPages() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Use an explicit changed time to ensure the expected order in the content
|
||||
// admin listing. We want these to appear in the table in the same order as
|
||||
// they appear in the following code, and the 'content' View has a table
|
||||
// style configuration with a default sort on the 'changed' field DESC.
|
||||
$time = time();
|
||||
$nodes['published_page'] = $this->drupalCreateNode(['type' => 'page', 'changed' => $time--]);
|
||||
$nodes['published_article'] = $this->drupalCreateNode(['type' => 'article', 'changed' => $time--]);
|
||||
$nodes['unpublished_page_1'] = $this->drupalCreateNode(['type' => 'page', 'changed' => $time--, 'uid' => $this->baseUser1->id(), 'status' => 0]);
|
||||
$nodes['unpublished_page_2'] = $this->drupalCreateNode(['type' => 'page', 'changed' => $time, 'uid' => $this->baseUser2->id(), 'status' => 0]);
|
||||
|
||||
// Verify view, edit, and delete links for any content.
|
||||
$this->drupalGet('admin/content');
|
||||
$this->assertResponse(200);
|
||||
|
||||
$node_type_labels = $this->xpath('//td[contains(@class, "views-field-type")]');
|
||||
$delta = 0;
|
||||
foreach ($nodes as $node) {
|
||||
$this->assertLinkByHref('node/' . $node->id());
|
||||
$this->assertLinkByHref('node/' . $node->id() . '/edit');
|
||||
$this->assertLinkByHref('node/' . $node->id() . '/delete');
|
||||
// Verify that we can see the content type label.
|
||||
$this->assertEqual(trim($node_type_labels[$delta]->getText()), $node->type->entity->label());
|
||||
$delta++;
|
||||
}
|
||||
|
||||
// Verify filtering by publishing status.
|
||||
$this->drupalGet('admin/content', ['query' => ['status' => TRUE]]);
|
||||
|
||||
$this->assertLinkByHref('node/' . $nodes['published_page']->id() . '/edit');
|
||||
$this->assertLinkByHref('node/' . $nodes['published_article']->id() . '/edit');
|
||||
$this->assertNoLinkByHref('node/' . $nodes['unpublished_page_1']->id() . '/edit');
|
||||
|
||||
// Verify filtering by status and content type.
|
||||
$this->drupalGet('admin/content', ['query' => ['status' => TRUE, 'type' => 'page']]);
|
||||
|
||||
$this->assertLinkByHref('node/' . $nodes['published_page']->id() . '/edit');
|
||||
$this->assertNoLinkByHref('node/' . $nodes['published_article']->id() . '/edit');
|
||||
|
||||
// Verify no operation links are displayed for regular users.
|
||||
$this->drupalLogout();
|
||||
$this->drupalLogin($this->baseUser1);
|
||||
$this->drupalGet('admin/content');
|
||||
$this->assertResponse(200);
|
||||
$this->assertLinkByHref('node/' . $nodes['published_page']->id());
|
||||
$this->assertLinkByHref('node/' . $nodes['published_article']->id());
|
||||
$this->assertNoLinkByHref('node/' . $nodes['published_page']->id() . '/edit');
|
||||
$this->assertNoLinkByHref('node/' . $nodes['published_page']->id() . '/delete');
|
||||
$this->assertNoLinkByHref('node/' . $nodes['published_article']->id() . '/edit');
|
||||
$this->assertNoLinkByHref('node/' . $nodes['published_article']->id() . '/delete');
|
||||
|
||||
// Verify no unpublished content is displayed without permission.
|
||||
$this->assertNoLinkByHref('node/' . $nodes['unpublished_page_1']->id());
|
||||
$this->assertNoLinkByHref('node/' . $nodes['unpublished_page_1']->id() . '/edit');
|
||||
$this->assertNoLinkByHref('node/' . $nodes['unpublished_page_1']->id() . '/delete');
|
||||
|
||||
// Verify no tableselect.
|
||||
$this->assertNoFieldByName('nodes[' . $nodes['published_page']->id() . ']', '', 'No tableselect found.');
|
||||
|
||||
// Verify unpublished content is displayed with permission.
|
||||
$this->drupalLogout();
|
||||
$this->drupalLogin($this->baseUser2);
|
||||
$this->drupalGet('admin/content');
|
||||
$this->assertResponse(200);
|
||||
$this->assertLinkByHref('node/' . $nodes['unpublished_page_2']->id());
|
||||
// Verify no operation links are displayed.
|
||||
$this->assertNoLinkByHref('node/' . $nodes['unpublished_page_2']->id() . '/edit');
|
||||
$this->assertNoLinkByHref('node/' . $nodes['unpublished_page_2']->id() . '/delete');
|
||||
|
||||
// Verify user cannot see unpublished content of other users.
|
||||
$this->assertNoLinkByHref('node/' . $nodes['unpublished_page_1']->id());
|
||||
$this->assertNoLinkByHref('node/' . $nodes['unpublished_page_1']->id() . '/edit');
|
||||
$this->assertNoLinkByHref('node/' . $nodes['unpublished_page_1']->id() . '/delete');
|
||||
|
||||
// Verify no tableselect.
|
||||
$this->assertNoFieldByName('nodes[' . $nodes['unpublished_page_2']->id() . ']', '', 'No tableselect found.');
|
||||
|
||||
// Verify node access can be bypassed.
|
||||
$this->drupalLogout();
|
||||
$this->drupalLogin($this->baseUser3);
|
||||
$this->drupalGet('admin/content');
|
||||
$this->assertResponse(200);
|
||||
foreach ($nodes as $node) {
|
||||
$this->assertLinkByHref('node/' . $node->id());
|
||||
$this->assertLinkByHref('node/' . $node->id() . '/edit');
|
||||
$this->assertLinkByHref('node/' . $node->id() . '/delete');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,177 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\block\Entity\Block;
|
||||
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
|
||||
use Drupal\Tests\system\Functional\Cache\AssertPageCacheContextsAndTagsTrait;
|
||||
use Drupal\user\RoleInterface;
|
||||
|
||||
/**
|
||||
* Tests node block functionality.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeBlockFunctionalTest extends NodeTestBase {
|
||||
|
||||
use AssertPageCacheContextsAndTagsTrait;
|
||||
|
||||
/**
|
||||
* An administrative user for testing.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* An unprivileged user for testing.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $webUser;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['block', 'views'];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create users and test node.
|
||||
$this->adminUser = $this->drupalCreateUser(['administer content types', 'administer nodes', 'administer blocks', 'access content overview']);
|
||||
$this->webUser = $this->drupalCreateUser(['access content', 'create article content']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the recent comments block.
|
||||
*/
|
||||
public function testRecentNodeBlock() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Disallow anonymous users to view content.
|
||||
user_role_change_permissions(RoleInterface::ANONYMOUS_ID, [
|
||||
'access content' => FALSE,
|
||||
]);
|
||||
|
||||
// Enable the recent content block with two items.
|
||||
$block = $this->drupalPlaceBlock('views_block:content_recent-block_1', ['id' => 'test_block', 'items_per_page' => 2]);
|
||||
|
||||
// Test that block is not visible without nodes.
|
||||
$this->drupalGet('');
|
||||
$this->assertText(t('No content available.'), 'Block with "No content available." found.');
|
||||
|
||||
// Add some test nodes.
|
||||
$default_settings = ['uid' => $this->webUser->id(), 'type' => 'article'];
|
||||
$node1 = $this->drupalCreateNode($default_settings);
|
||||
$node2 = $this->drupalCreateNode($default_settings);
|
||||
$node3 = $this->drupalCreateNode($default_settings);
|
||||
|
||||
// Change the changed time for node so that we can test ordering.
|
||||
db_update('node_field_data')
|
||||
->fields([
|
||||
'changed' => $node1->getChangedTime() + 100,
|
||||
])
|
||||
->condition('nid', $node2->id())
|
||||
->execute();
|
||||
db_update('node_field_data')
|
||||
->fields([
|
||||
'changed' => $node1->getChangedTime() + 200,
|
||||
])
|
||||
->condition('nid', $node3->id())
|
||||
->execute();
|
||||
|
||||
// Test that a user without the 'access content' permission cannot
|
||||
// see the block.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('');
|
||||
$this->assertNoText($block->label(), 'Block was not found.');
|
||||
|
||||
// Test that only the 2 latest nodes are shown.
|
||||
$this->drupalLogin($this->webUser);
|
||||
$this->assertNoText($node1->label(), 'Node not found in block.');
|
||||
$this->assertText($node2->label(), 'Node found in block.');
|
||||
$this->assertText($node3->label(), 'Node found in block.');
|
||||
|
||||
// Check to make sure nodes are in the right order.
|
||||
$this->assertTrue($this->xpath('//div[@id="block-test-block"]//div[@class="item-list"]/ul/li[1]/div/span/a[text() = "' . $node3->label() . '"]'), 'Nodes were ordered correctly in block.');
|
||||
|
||||
$this->drupalLogout();
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Set the number of recent nodes to show to 10.
|
||||
$block->getPlugin()->setConfigurationValue('items_per_page', 10);
|
||||
$block->save();
|
||||
|
||||
// Post an additional node.
|
||||
$node4 = $this->drupalCreateNode($default_settings);
|
||||
|
||||
// Test that all four nodes are shown.
|
||||
$this->drupalGet('');
|
||||
$this->assertText($node1->label(), 'Node found in block.');
|
||||
$this->assertText($node2->label(), 'Node found in block.');
|
||||
$this->assertText($node3->label(), 'Node found in block.');
|
||||
$this->assertText($node4->label(), 'Node found in block.');
|
||||
|
||||
$this->assertCacheContexts(['languages:language_content', 'languages:language_interface', 'theme', 'url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT, 'user']);
|
||||
|
||||
// Enable the "Powered by Drupal" block only on article nodes.
|
||||
$edit = [
|
||||
'id' => strtolower($this->randomMachineName()),
|
||||
'region' => 'sidebar_first',
|
||||
'visibility[node_type][bundles][article]' => 'article',
|
||||
];
|
||||
$theme = \Drupal::service('theme_handler')->getDefault();
|
||||
$this->drupalPostForm("admin/structure/block/add/system_powered_by_block/$theme", $edit, t('Save block'));
|
||||
|
||||
$block = Block::load($edit['id']);
|
||||
$visibility = $block->getVisibility();
|
||||
$this->assertTrue(isset($visibility['node_type']['bundles']['article']), 'Visibility settings were saved to configuration');
|
||||
|
||||
// Create a page node.
|
||||
$node5 = $this->drupalCreateNode(['uid' => $this->adminUser->id(), 'type' => 'page']);
|
||||
|
||||
$this->drupalLogout();
|
||||
$this->drupalLogin($this->webUser);
|
||||
|
||||
// Verify visibility rules.
|
||||
$this->drupalGet('');
|
||||
$label = $block->label();
|
||||
$this->assertNoText($label, 'Block was not displayed on the front page.');
|
||||
$this->assertCacheContexts(['languages:language_content', 'languages:language_interface', 'theme', 'url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT, 'user', 'route']);
|
||||
|
||||
// Ensure that a page that does not have a node context can still be cached,
|
||||
// the front page is the user page which is already cached from the login
|
||||
// request above.
|
||||
$this->assertSame('HIT', $this->getSession()->getResponseHeader('X-Drupal-Dynamic-Cache'));
|
||||
|
||||
$this->drupalGet('node/add/article');
|
||||
$this->assertText($label, 'Block was displayed on the node/add/article page.');
|
||||
$this->assertCacheContexts(['languages:language_content', 'languages:language_interface', 'session', 'theme', 'url.path', 'url.query_args', 'user', 'route']);
|
||||
|
||||
// The node/add/article page is an admin path and currently uncacheable.
|
||||
$this->assertSame('UNCACHEABLE', $this->getSession()->getResponseHeader('X-Drupal-Dynamic-Cache'));
|
||||
|
||||
$this->drupalGet('node/' . $node1->id());
|
||||
$this->assertText($label, 'Block was displayed on the node/N when node is of type article.');
|
||||
$this->assertCacheContexts(['languages:language_content', 'languages:language_interface', 'theme', 'url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT, 'user', 'route', 'timezone']);
|
||||
$this->assertSame('MISS', $this->getSession()->getResponseHeader('X-Drupal-Dynamic-Cache'));
|
||||
$this->drupalGet('node/' . $node1->id());
|
||||
$this->assertSame('HIT', $this->getSession()->getResponseHeader('X-Drupal-Dynamic-Cache'));
|
||||
|
||||
$this->drupalGet('node/' . $node5->id());
|
||||
$this->assertNoText($label, 'Block was not displayed on nodes of type page.');
|
||||
$this->assertCacheContexts(['languages:language_content', 'languages:language_interface', 'theme', 'url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT, 'user', 'route', 'timezone']);
|
||||
$this->assertSame('MISS', $this->getSession()->getResponseHeader('X-Drupal-Dynamic-Cache'));
|
||||
$this->drupalGet('node/' . $node5->id());
|
||||
$this->assertSame('HIT', $this->getSession()->getResponseHeader('X-Drupal-Dynamic-Cache'));
|
||||
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalGet('admin/structure/block');
|
||||
$this->assertText($label, 'Block was displayed on the admin/structure/block page.');
|
||||
$this->assertLinkByHref($block->url());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\Tests\system\Functional\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()
|
||||
->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,42 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\node\Entity\Node;
|
||||
|
||||
/**
|
||||
* Tests views contextual links on nodes.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeContextualLinksTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'contextual',
|
||||
];
|
||||
|
||||
/**
|
||||
* Tests contextual links.
|
||||
*/
|
||||
public function testNodeContextualLinks() {
|
||||
// Create a node item.
|
||||
$node = Node::create([
|
||||
'type' => 'article',
|
||||
'title' => 'Unnamed',
|
||||
]);
|
||||
$node->save();
|
||||
|
||||
$user = $this->drupalCreateUser([
|
||||
'administer nodes',
|
||||
'access contextual links',
|
||||
]);
|
||||
$this->drupalLogin($user);
|
||||
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$this->assertSession()->elementAttributeContains('css', 'div[data-contextual-id]', 'data-contextual-id', 'node:node=' . $node->id() . ':');
|
||||
}
|
||||
|
||||
}
|
|
@ -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,283 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\node\NodeInterface;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Create a node and test node edit functionality.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeEditFormTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* A normal logged in user.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $webUser;
|
||||
|
||||
/**
|
||||
* A user with permission to bypass content access checks.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* The node storage.
|
||||
*
|
||||
* @var \Drupal\node\NodeStorageInterface
|
||||
*/
|
||||
protected $nodeStorage;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public static $modules = ['block', 'node', 'datetime'];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->webUser = $this->drupalCreateUser(['edit own page content', 'create page content']);
|
||||
$this->adminUser = $this->drupalCreateUser(['bypass node access', 'administer nodes']);
|
||||
$this->drupalPlaceBlock('local_tasks_block');
|
||||
|
||||
$this->nodeStorage = $this->container->get('entity.manager')->getStorage('node');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks node edit functionality.
|
||||
*/
|
||||
public function testNodeEdit() {
|
||||
$this->drupalLogin($this->webUser);
|
||||
|
||||
$title_key = 'title[0][value]';
|
||||
$body_key = 'body[0][value]';
|
||||
// Create node to edit.
|
||||
$edit = [];
|
||||
$edit[$title_key] = $this->randomMachineName(8);
|
||||
$edit[$body_key] = $this->randomMachineName(16);
|
||||
$this->drupalPostForm('node/add/page', $edit, t('Save'));
|
||||
|
||||
// Check that the node exists in the database.
|
||||
$node = $this->drupalGetNodeByTitle($edit[$title_key]);
|
||||
$this->assertTrue($node, 'Node found in database.');
|
||||
|
||||
// Check that "edit" link points to correct page.
|
||||
$this->clickLink(t('Edit'));
|
||||
$this->assertUrl($node->url('edit-form', ['absolute' => TRUE]));
|
||||
|
||||
// Check that the title and body fields are displayed with the correct values.
|
||||
// @todo Ideally assertLink would support HTML, but it doesn't.
|
||||
$this->assertRaw('Edit<span class="visually-hidden">(active tab)</span>', 'Edit tab found and marked active.');
|
||||
$this->assertFieldByName($title_key, $edit[$title_key], 'Title field displayed.');
|
||||
$this->assertFieldByName($body_key, $edit[$body_key], 'Body field displayed.');
|
||||
|
||||
// Edit the content of the node.
|
||||
$edit = [];
|
||||
$edit[$title_key] = $this->randomMachineName(8);
|
||||
$edit[$body_key] = $this->randomMachineName(16);
|
||||
// Stay on the current page, without reloading.
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
|
||||
// Check that the title and body fields are displayed with the updated values.
|
||||
$this->assertText($edit[$title_key], 'Title displayed.');
|
||||
$this->assertText($edit[$body_key], 'Body displayed.');
|
||||
|
||||
// Log in as a second administrator user.
|
||||
$second_web_user = $this->drupalCreateUser(['administer nodes', 'edit any page content']);
|
||||
$this->drupalLogin($second_web_user);
|
||||
// Edit the same node, creating a new revision.
|
||||
$this->drupalGet("node/" . $node->id() . "/edit");
|
||||
$edit = [];
|
||||
$edit['title[0][value]'] = $this->randomMachineName(8);
|
||||
$edit[$body_key] = $this->randomMachineName(16);
|
||||
$edit['revision'] = TRUE;
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
|
||||
// Ensure that the node revision has been created.
|
||||
$revised_node = $this->drupalGetNodeByTitle($edit['title[0][value]'], TRUE);
|
||||
$this->assertNotIdentical($node->getRevisionId(), $revised_node->getRevisionId(), 'A new revision has been created.');
|
||||
// Ensure that the node author is preserved when it was not changed in the
|
||||
// edit form.
|
||||
$this->assertIdentical($node->getOwnerId(), $revised_node->getOwnerId(), 'The node author has been preserved.');
|
||||
// Ensure that the revision authors are different since the revisions were
|
||||
// made by different users.
|
||||
$first_node_version = node_revision_load($node->getRevisionId());
|
||||
$second_node_version = node_revision_load($revised_node->getRevisionId());
|
||||
$this->assertNotIdentical($first_node_version->getRevisionUser()->id(), $second_node_version->getRevisionUser()->id(), 'Each revision has a distinct user.');
|
||||
|
||||
// Check if the node revision checkbox is rendered on node edit form.
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$this->assertFieldById('edit-revision', NULL, 'The revision field is present.');
|
||||
|
||||
// Check that details form element opens when there are errors on child
|
||||
// elements.
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$edit = [];
|
||||
// This invalid date will trigger an error.
|
||||
$edit['created[0][value][date]'] = $this->randomMachineName(8);
|
||||
// Get the current amount of open details elements.
|
||||
$open_details_elements = count($this->cssSelect('details[open="open"]'));
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
// The node author details must be open.
|
||||
$this->assertRaw('<details class="node-form-author js-form-wrapper form-wrapper" data-drupal-selector="edit-author" id="edit-author" open="open">');
|
||||
// Only one extra details element should now be open.
|
||||
$open_details_elements++;
|
||||
$this->assertEqual(count($this->cssSelect('details[open="open"]')), $open_details_elements, 'Exactly one extra open <details> element found.');
|
||||
|
||||
// Edit the same node, save it and verify it's unpublished after unchecking
|
||||
// the 'Published' boolean_checkbox and clicking 'Save'.
|
||||
$this->drupalGet("node/" . $node->id() . "/edit");
|
||||
$edit = ['status[value]' => FALSE];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->nodeStorage->resetCache([$node->id()]);
|
||||
$node = $this->nodeStorage->load($node->id());
|
||||
$this->assertFalse($node->isPublished(), 'Node is unpublished');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests changing a node's "authored by" field.
|
||||
*/
|
||||
public function testNodeEditAuthoredBy() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Create node to edit.
|
||||
$body_key = 'body[0][value]';
|
||||
$edit = [];
|
||||
$edit['title[0][value]'] = $this->randomMachineName(8);
|
||||
$edit[$body_key] = $this->randomMachineName(16);
|
||||
$this->drupalPostForm('node/add/page', $edit, t('Save'));
|
||||
|
||||
// Check that the node was authored by the currently logged in user.
|
||||
$node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
|
||||
$this->assertIdentical($node->getOwnerId(), $this->adminUser->id(), 'Node authored by admin user.');
|
||||
|
||||
$this->checkVariousAuthoredByValues($node, 'uid[0][target_id]');
|
||||
|
||||
// Check that normal users cannot change the authored by information.
|
||||
$this->drupalLogin($this->webUser);
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$this->assertNoFieldByName('uid[0][target_id]');
|
||||
|
||||
// Now test with the Autocomplete (Tags) field widget.
|
||||
/** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display */
|
||||
$form_display = \Drupal::entityManager()->getStorage('entity_form_display')->load('node.page.default');
|
||||
$widget = $form_display->getComponent('uid');
|
||||
$widget['type'] = 'entity_reference_autocomplete_tags';
|
||||
$widget['settings'] = [
|
||||
'match_operator' => 'CONTAINS',
|
||||
'size' => 60,
|
||||
'placeholder' => '',
|
||||
];
|
||||
$form_display->setComponent('uid', $widget);
|
||||
$form_display->save();
|
||||
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Save the node without making any changes.
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', [], t('Save'));
|
||||
$this->nodeStorage->resetCache([$node->id()]);
|
||||
$node = $this->nodeStorage->load($node->id());
|
||||
$this->assertIdentical($this->webUser->id(), $node->getOwner()->id());
|
||||
|
||||
$this->checkVariousAuthoredByValues($node, 'uid[target_id]');
|
||||
|
||||
// Hide the 'authored by' field from the form.
|
||||
$form_display->removeComponent('uid')->save();
|
||||
|
||||
// Check that saving the node without making any changes keeps the proper
|
||||
// author ID.
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', [], t('Save'));
|
||||
$this->nodeStorage->resetCache([$node->id()]);
|
||||
$node = $this->nodeStorage->load($node->id());
|
||||
$this->assertIdentical($this->webUser->id(), $node->getOwner()->id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the node meta information.
|
||||
*/
|
||||
public function testNodeMetaInformation() {
|
||||
// Check that regular users (i.e. without the 'administer nodes' permission)
|
||||
// can not see the meta information.
|
||||
$this->drupalLogin($this->webUser);
|
||||
$this->drupalGet('node/add/page');
|
||||
$this->assertNoText('Not saved yet');
|
||||
|
||||
// Create node to edit.
|
||||
$edit['title[0][value]'] = $this->randomMachineName(8);
|
||||
$edit['body[0][value]'] = $this->randomMachineName(16);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
|
||||
$node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
|
||||
$this->drupalGet("node/" . $node->id() . "/edit");
|
||||
$this->assertNoText('Published');
|
||||
$this->assertNoText(format_date($node->getChangedTime(), 'short'));
|
||||
|
||||
// Check that users with the 'administer nodes' permission can see the meta
|
||||
// information.
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalGet('node/add/page');
|
||||
$this->assertText('Not saved yet');
|
||||
|
||||
// Create node to edit.
|
||||
$edit['title[0][value]'] = $this->randomMachineName(8);
|
||||
$edit['body[0][value]'] = $this->randomMachineName(16);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
|
||||
$node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
|
||||
$this->drupalGet("node/" . $node->id() . "/edit");
|
||||
$this->assertText('Published');
|
||||
$this->assertText(format_date($node->getChangedTime(), 'short'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the "authored by" works correctly with various values.
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* A node object.
|
||||
* @param string $form_element_name
|
||||
* The name of the form element to populate.
|
||||
*/
|
||||
protected function checkVariousAuthoredByValues(NodeInterface $node, $form_element_name) {
|
||||
// Try to change the 'authored by' field to an invalid user name.
|
||||
$edit = [
|
||||
$form_element_name => 'invalid-name',
|
||||
];
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
|
||||
$this->assertRaw(t('There are no entities matching "%name".', ['%name' => 'invalid-name']));
|
||||
|
||||
// Change the authored by field to an empty string, which should assign
|
||||
// authorship to the anonymous user (uid 0).
|
||||
$edit[$form_element_name] = '';
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
|
||||
$this->nodeStorage->resetCache([$node->id()]);
|
||||
$node = $this->nodeStorage->load($node->id());
|
||||
$uid = $node->getOwnerId();
|
||||
// Most SQL database drivers stringify fetches but entities are not
|
||||
// necessarily stored in a SQL database. At the same time, NULL/FALSE/""
|
||||
// won't do.
|
||||
$this->assertTrue($uid === 0 || $uid === '0', 'Node authored by anonymous user.');
|
||||
|
||||
// Go back to the edit form and check that the correct value is displayed
|
||||
// in the author widget.
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$anonymous_user = User::getAnonymousUser();
|
||||
$expected = $anonymous_user->label() . ' (' . $anonymous_user->id() . ')';
|
||||
$this->assertFieldByName($form_element_name, $expected, 'Authored by field displays the correct value for the anonymous user.');
|
||||
|
||||
// Change the authored by field to another user's name (that is not
|
||||
// logged in).
|
||||
$edit[$form_element_name] = $this->webUser->getUsername();
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->nodeStorage->resetCache([$node->id()]);
|
||||
$node = $this->nodeStorage->load($node->id());
|
||||
$this->assertIdentical($node->getOwnerId(), $this->webUser->id(), 'Node authored by normal user.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Tests\EntityViewTrait;
|
||||
|
||||
/**
|
||||
* Tests changing view modes for nodes.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeEntityViewModeAlterTest extends NodeTestBase {
|
||||
|
||||
use EntityViewTrait;
|
||||
|
||||
/**
|
||||
* Enable dummy module that implements hook_ENTITY_TYPE_view() for nodes.
|
||||
*/
|
||||
public static $modules = ['node_test'];
|
||||
|
||||
/**
|
||||
* Create a "Basic page" node and verify its consistency in the database.
|
||||
*/
|
||||
public function testNodeViewModeChange() {
|
||||
$web_user = $this->drupalCreateUser(['create page content', 'edit own page content']);
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
// Create a node.
|
||||
$edit = [];
|
||||
$edit['title[0][value]'] = $this->randomMachineName(8);
|
||||
$edit['body[0][value]'] = t('Data that should appear only in the body for the node.');
|
||||
$edit['body[0][summary]'] = t('Extra data that should appear only in the teaser for the node.');
|
||||
$this->drupalPostForm('node/add/page', $edit, t('Save'));
|
||||
|
||||
$node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
|
||||
|
||||
// Set the flag to alter the view mode and view the node.
|
||||
\Drupal::state()->set('node_test_change_view_mode', 'teaser');
|
||||
Cache::invalidateTags(['rendered']);
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
|
||||
// Check that teaser mode is viewed.
|
||||
$this->assertText('Extra data that should appear only in the teaser for the node.', 'Teaser text present');
|
||||
// Make sure body text is not present.
|
||||
$this->assertNoText('Data that should appear only in the body for the node.', 'Body text not present');
|
||||
|
||||
// Test that the correct build mode has been set.
|
||||
$build = $this->buildEntityView($node);
|
||||
$this->assertEqual($build['#view_mode'], 'teaser', 'The view mode has correctly been set to teaser.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests multilingual support for fields.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeFieldMultilingualTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node', 'language'];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create Basic page node type.
|
||||
$this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']);
|
||||
|
||||
// Setup users.
|
||||
$admin_user = $this->drupalCreateUser(['administer languages', 'administer content types', 'access administration pages', 'create page content', 'edit own page content']);
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
// Add a new language.
|
||||
ConfigurableLanguage::createFromLangcode('it')->save();
|
||||
|
||||
// Enable URL language detection and selection.
|
||||
$edit = ['language_interface[enabled][language-url]' => '1'];
|
||||
$this->drupalPostForm('admin/config/regional/language/detection', $edit, t('Save settings'));
|
||||
|
||||
// Set "Basic page" content type to use multilingual support.
|
||||
$edit = [
|
||||
'language_configuration[language_alterable]' => TRUE,
|
||||
];
|
||||
$this->drupalPostForm('admin/structure/types/manage/page', $edit, t('Save content type'));
|
||||
$this->assertRaw(t('The content type %type has been updated.', ['%type' => 'Basic page']), 'Basic page content type has been updated.');
|
||||
|
||||
// Make node body translatable.
|
||||
$field_storage = FieldStorageConfig::loadByName('node', 'body');
|
||||
$field_storage->setTranslatable(TRUE);
|
||||
$field_storage->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether field languages are correctly set through the node form.
|
||||
*/
|
||||
public function testMultilingualNodeForm() {
|
||||
// Create "Basic page" content.
|
||||
$langcode = language_get_default_langcode('node', 'page');
|
||||
$title_key = 'title[0][value]';
|
||||
$title_value = $this->randomMachineName(8);
|
||||
$body_key = 'body[0][value]';
|
||||
$body_value = $this->randomMachineName(16);
|
||||
|
||||
// Create node to edit.
|
||||
$edit = [];
|
||||
$edit[$title_key] = $title_value;
|
||||
$edit[$body_key] = $body_value;
|
||||
$this->drupalPostForm('node/add/page', $edit, t('Save'));
|
||||
|
||||
// Check that the node exists in the database.
|
||||
$node = $this->drupalGetNodeByTitle($edit[$title_key]);
|
||||
$this->assertTrue($node, 'Node found in database.');
|
||||
$this->assertTrue($node->language()->getId() == $langcode && $node->body->value == $body_value, 'Field language correctly set.');
|
||||
|
||||
// Change node language.
|
||||
$langcode = 'it';
|
||||
$this->drupalGet("node/{$node->id()}/edit");
|
||||
$edit = [
|
||||
$title_key => $this->randomMachineName(8),
|
||||
'langcode[0][value]' => $langcode,
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$node = $this->drupalGetNodeByTitle($edit[$title_key], TRUE);
|
||||
$this->assertTrue($node, 'Node found in database.');
|
||||
$this->assertTrue($node->language()->getId() == $langcode && $node->body->value == $body_value, 'Field language correctly changed.');
|
||||
|
||||
// Enable content language URL detection.
|
||||
$this->container->get('language_negotiator')->saveConfiguration(LanguageInterface::TYPE_CONTENT, [LanguageNegotiationUrl::METHOD_ID => 0]);
|
||||
|
||||
// Test multilingual field language fallback logic.
|
||||
$this->drupalGet("it/node/{$node->id()}");
|
||||
$this->assertRaw($body_value, 'Body correctly displayed using Italian as requested language');
|
||||
|
||||
$this->drupalGet("node/{$node->id()}");
|
||||
$this->assertRaw($body_value, 'Body correctly displayed using English as requested language');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests multilingual field display settings.
|
||||
*/
|
||||
public function testMultilingualDisplaySettings() {
|
||||
// Create "Basic page" content.
|
||||
$title_key = 'title[0][value]';
|
||||
$title_value = $this->randomMachineName(8);
|
||||
$body_key = 'body[0][value]';
|
||||
$body_value = $this->randomMachineName(16);
|
||||
|
||||
// Create node to edit.
|
||||
$edit = [];
|
||||
$edit[$title_key] = $title_value;
|
||||
$edit[$body_key] = $body_value;
|
||||
$this->drupalPostForm('node/add/page', $edit, t('Save'));
|
||||
|
||||
// Check that the node exists in the database.
|
||||
$node = $this->drupalGetNodeByTitle($edit[$title_key]);
|
||||
$this->assertTrue($node, 'Node found in database.');
|
||||
|
||||
// Check if node body is showed.
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$body = $this->xpath('//article[contains(concat(" ", normalize-space(@class), " "), :node-class)]//div[contains(concat(" ", normalize-space(@class), " "), :content-class)]/descendant::p', [
|
||||
':node-class' => ' node ',
|
||||
':content-class' => 'node__content',
|
||||
]);
|
||||
$this->assertEqual($body[0]->getText(), $node->body->value, 'Node body found.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,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,62 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\user\Entity\Role;
|
||||
|
||||
/**
|
||||
* Tests the node entity preview functionality for anonymous user.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodePreviewAnonymousTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Enable node module to test on the preview.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// Create Basic page node type.
|
||||
$this->drupalCreateContentType([
|
||||
'type' => 'page',
|
||||
'name' => 'Basic page',
|
||||
'display_submitted' => FALSE,
|
||||
]);
|
||||
|
||||
// Grant create and editing permissions to anonymous user:
|
||||
$anonymous_role = Role::load(AccountInterface::ANONYMOUS_ROLE);
|
||||
$anonymous_role->grantPermission('create page content');
|
||||
$anonymous_role->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the node preview functionality for anonymous users.
|
||||
*/
|
||||
public function testAnonymousPagePreview() {
|
||||
|
||||
$title_key = 'title[0][value]';
|
||||
$body_key = 'body[0][value]';
|
||||
|
||||
// Fill in node creation form and preview node.
|
||||
$edit = [
|
||||
$title_key => $this->randomMachineName(),
|
||||
$body_key => $this->randomMachineName(),
|
||||
];
|
||||
$this->drupalPostForm('node/add/page', $edit, t('Preview'));
|
||||
|
||||
// Check that the preview is displaying the title, body and term.
|
||||
$this->assertSession()->linkExists(t('Back to content editing'));
|
||||
$this->assertSession()->responseContains($edit[$body_key]);
|
||||
$this->assertSession()->titleEquals($edit[$title_key] . ' | Drupal');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,197 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
/**
|
||||
* Tests that node access queries are properly altered by the node module.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeQueryAlterTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node_access_test'];
|
||||
|
||||
/**
|
||||
* User with permission to view content.
|
||||
*/
|
||||
protected $accessUser;
|
||||
|
||||
/**
|
||||
* User without permission to view content.
|
||||
*/
|
||||
protected $noAccessUser;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
node_access_rebuild();
|
||||
|
||||
// Create some content.
|
||||
$this->drupalCreateNode();
|
||||
$this->drupalCreateNode();
|
||||
$this->drupalCreateNode();
|
||||
$this->drupalCreateNode();
|
||||
|
||||
// Create user with simple node access permission. The 'node test view'
|
||||
// permission is implemented and granted by the node_access_test module.
|
||||
$this->accessUser = $this->drupalCreateUser(['access content overview', 'access content', 'node test view']);
|
||||
$this->noAccessUser = $this->drupalCreateUser(['access content overview', 'access content']);
|
||||
$this->noAccessUser2 = $this->drupalCreateUser(['access content overview', 'access content']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests 'node_access' query alter, for user with access.
|
||||
*
|
||||
* Verifies that a non-standard table alias can be used, and that a user with
|
||||
* node access can view the nodes.
|
||||
*/
|
||||
public function testNodeQueryAlterLowLevelWithAccess() {
|
||||
// User with access should be able to view 4 nodes.
|
||||
try {
|
||||
$query = db_select('node', 'mytab')
|
||||
->fields('mytab');
|
||||
$query->addTag('node_access');
|
||||
$query->addMetaData('op', 'view');
|
||||
$query->addMetaData('account', $this->accessUser);
|
||||
|
||||
$result = $query->execute()->fetchAll();
|
||||
$this->assertEqual(count($result), 4, 'User with access can see correct nodes');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->fail(t('Altered query is malformed'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests 'node_access' query alter with revision-enabled nodes.
|
||||
*/
|
||||
public function testNodeQueryAlterWithRevisions() {
|
||||
// Execute a query that only deals with the 'node_revision' table.
|
||||
try {
|
||||
$query = \Drupal::entityTypeManager()->getStorage('node')->getQuery();
|
||||
$result = $query
|
||||
->allRevisions()
|
||||
->execute();
|
||||
|
||||
$this->assertEqual(count($result), 4, 'User with access can see correct nodes');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->fail('Altered query is malformed');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests 'node_access' query alter, for user without access.
|
||||
*
|
||||
* Verifies that a non-standard table alias can be used, and that a user
|
||||
* without node access cannot view the nodes.
|
||||
*/
|
||||
public function testNodeQueryAlterLowLevelNoAccess() {
|
||||
// User without access should be able to view 0 nodes.
|
||||
try {
|
||||
$query = db_select('node', 'mytab')
|
||||
->fields('mytab');
|
||||
$query->addTag('node_access');
|
||||
$query->addMetaData('op', 'view');
|
||||
$query->addMetaData('account', $this->noAccessUser);
|
||||
|
||||
$result = $query->execute()->fetchAll();
|
||||
$this->assertEqual(count($result), 0, 'User with no access cannot see nodes');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->fail(t('Altered query is malformed'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests 'node_access' query alter, for edit access.
|
||||
*
|
||||
* Verifies that a non-standard table alias can be used, and that a user with
|
||||
* view-only node access cannot edit the nodes.
|
||||
*/
|
||||
public function testNodeQueryAlterLowLevelEditAccess() {
|
||||
// User with view-only access should not be able to edit nodes.
|
||||
try {
|
||||
$query = db_select('node', 'mytab')
|
||||
->fields('mytab');
|
||||
$query->addTag('node_access');
|
||||
$query->addMetaData('op', 'update');
|
||||
$query->addMetaData('account', $this->accessUser);
|
||||
|
||||
$result = $query->execute()->fetchAll();
|
||||
$this->assertEqual(count($result), 0, 'User with view-only access cannot edit nodes');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->fail($e->getMessage());
|
||||
$this->fail((string) $query);
|
||||
$this->fail(t('Altered query is malformed'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests 'node_access' query alter override.
|
||||
*
|
||||
* Verifies that node_access_view_all_nodes() is called from
|
||||
* node_query_node_access_alter(). We do this by checking that a user who
|
||||
* normally would not have view privileges is able to view the nodes when we
|
||||
* add a record to {node_access} paired with a corresponding privilege in
|
||||
* hook_node_grants().
|
||||
*/
|
||||
public function testNodeQueryAlterOverride() {
|
||||
$record = [
|
||||
'nid' => 0,
|
||||
'gid' => 0,
|
||||
'realm' => 'node_access_all',
|
||||
'grant_view' => 1,
|
||||
'grant_update' => 0,
|
||||
'grant_delete' => 0,
|
||||
];
|
||||
db_insert('node_access')->fields($record)->execute();
|
||||
|
||||
// Test that the noAccessUser still doesn't have the 'view'
|
||||
// privilege after adding the node_access record.
|
||||
drupal_static_reset('node_access_view_all_nodes');
|
||||
try {
|
||||
$query = db_select('node', 'mytab')
|
||||
->fields('mytab');
|
||||
$query->addTag('node_access');
|
||||
$query->addMetaData('op', 'view');
|
||||
$query->addMetaData('account', $this->noAccessUser);
|
||||
|
||||
$result = $query->execute()->fetchAll();
|
||||
$this->assertEqual(count($result), 0, 'User view privileges are not overridden');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->fail(t('Altered query is malformed'));
|
||||
}
|
||||
|
||||
// Have node_test_node_grants return a node_access_all privilege,
|
||||
// to grant the noAccessUser 'view' access. To verify that
|
||||
// node_access_view_all_nodes is properly checking the specified
|
||||
// $account instead of the current user, we will log in as
|
||||
// noAccessUser2.
|
||||
$this->drupalLogin($this->noAccessUser2);
|
||||
\Drupal::state()->set('node_access_test.no_access_uid', $this->noAccessUser->id());
|
||||
drupal_static_reset('node_access_view_all_nodes');
|
||||
try {
|
||||
$query = db_select('node', 'mytab')
|
||||
->fields('mytab');
|
||||
$query->addTag('node_access');
|
||||
$query->addMetaData('op', 'view');
|
||||
$query->addMetaData('account', $this->noAccessUser);
|
||||
|
||||
$result = $query->execute()->fetchAll();
|
||||
$this->assertEqual(count($result), 4, 'User view privileges are overridden');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->fail(t('Altered query is malformed'));
|
||||
}
|
||||
\Drupal::state()->delete('node_access_test.no_access_uid');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\filter\Entity\FilterFormat;
|
||||
|
||||
/**
|
||||
* Ensures that data added to nodes by other modules appears in RSS feeds.
|
||||
*
|
||||
* Create a node, enable the node_test module to ensure that extra data is
|
||||
* added to the node's renderable array, then verify that the data appears on
|
||||
* the site-wide RSS feed at rss.xml.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeRSSContentTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Enable a module that implements hook_node_view().
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node_test', 'views'];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Use bypass node access permission here, because the test class uses
|
||||
// hook_grants_alter() to deny access to everyone on node_access
|
||||
// queries.
|
||||
$user = $this->drupalCreateUser(['bypass node access', 'access content', 'create article content']);
|
||||
$this->drupalLogin($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that a new node includes the custom data when added to an RSS feed.
|
||||
*/
|
||||
public function testNodeRSSContent() {
|
||||
// Create a node.
|
||||
$node = $this->drupalCreateNode(['type' => 'article', 'promote' => 1]);
|
||||
|
||||
$this->drupalGet('rss.xml');
|
||||
|
||||
// Check that content added in 'rss' view mode appear in RSS feed.
|
||||
$rss_only_content = t('Extra data that should appear only in the RSS feed for node @nid.', ['@nid' => $node->id()]);
|
||||
$this->assertText($rss_only_content, 'Node content designated for RSS appear in RSS feed.');
|
||||
|
||||
// Check that content added in view modes other than 'rss' doesn't
|
||||
// appear in RSS feed.
|
||||
$non_rss_content = t('Extra data that should appear everywhere except the RSS feed for node @nid.', ['@nid' => $node->id()]);
|
||||
$this->assertNoText($non_rss_content, 'Node content not designed for RSS does not appear in RSS feed.');
|
||||
|
||||
// Check that extra RSS elements and namespaces are added to RSS feed.
|
||||
$test_element = '<testElement>' . t('Value of testElement RSS element for node @nid.', ['@nid' => $node->id()]) . '</testElement>';
|
||||
$test_ns = 'xmlns:drupaltest="http://example.com/test-namespace"';
|
||||
$this->assertRaw($test_element, 'Extra RSS elements appear in RSS feed.');
|
||||
$this->assertRaw($test_ns, 'Extra namespaces appear in RSS feed.');
|
||||
|
||||
// Check that content added in 'rss' view mode doesn't appear when
|
||||
// viewing node.
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$this->assertNoText($rss_only_content, 'Node content designed for RSS does not appear when viewing node.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests relative, root-relative, protocol-relative and absolute URLs.
|
||||
*/
|
||||
public function testUrlHandling() {
|
||||
// Only the plain_text text format is available by default, which escapes
|
||||
// all HTML.
|
||||
FilterFormat::create([
|
||||
'format' => 'full_html',
|
||||
'name' => 'Full HTML',
|
||||
'filters' => [],
|
||||
])->save();
|
||||
|
||||
$defaults = [
|
||||
'type' => 'article',
|
||||
'promote' => 1,
|
||||
];
|
||||
$this->drupalCreateNode($defaults + [
|
||||
'body' => [
|
||||
'value' => '<p><a href="' . file_url_transform_relative(file_create_url('public://root-relative')) . '">Root-relative URL</a></p>',
|
||||
'format' => 'full_html',
|
||||
],
|
||||
]);
|
||||
$protocol_relative_url = substr(file_create_url('public://protocol-relative'), strlen(\Drupal::request()->getScheme() . ':'));
|
||||
$this->drupalCreateNode($defaults + [
|
||||
'body' => [
|
||||
'value' => '<p><a href="' . $protocol_relative_url . '">Protocol-relative URL</a></p>',
|
||||
'format' => 'full_html',
|
||||
],
|
||||
]);
|
||||
$absolute_url = file_create_url('public://absolute');
|
||||
$this->drupalCreateNode($defaults + [
|
||||
'body' => [
|
||||
'value' => '<p><a href="' . $absolute_url . '">Absolute URL</a></p>',
|
||||
'format' => 'full_html',
|
||||
],
|
||||
]);
|
||||
|
||||
$this->drupalGet('rss.xml');
|
||||
$this->assertRaw(file_create_url('public://root-relative'), 'Root-relative URL is transformed to absolute.');
|
||||
$this->assertRaw($protocol_relative_url, 'Protocol-relative URL is left untouched.');
|
||||
$this->assertRaw($absolute_url, 'Absolute URL is left untouched.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,173 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\Tests\Traits\Core\GeneratePermutationsTrait;
|
||||
|
||||
/**
|
||||
* Tests user permissions for node revisions.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeRevisionPermissionsTest extends NodeTestBase {
|
||||
|
||||
use GeneratePermutationsTrait;
|
||||
|
||||
/**
|
||||
* The node revisions.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $nodeRevisions = [];
|
||||
|
||||
/**
|
||||
* The accounts.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $accounts = [];
|
||||
|
||||
// Map revision permission names to node revision access ops.
|
||||
protected $map = [
|
||||
'view' => 'view all revisions',
|
||||
'update' => 'revert all revisions',
|
||||
'delete' => 'delete all revisions',
|
||||
];
|
||||
|
||||
// Map revision permission names to node type revision access ops.
|
||||
protected $typeMap = [
|
||||
'view' => 'view page revisions',
|
||||
'update' => 'revert page revisions',
|
||||
'delete' => 'delete page revisions',
|
||||
];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$types = ['page', 'article'];
|
||||
|
||||
foreach ($types as $type) {
|
||||
// Create a node with several revisions.
|
||||
$nodes[$type] = $this->drupalCreateNode(['type' => $type]);
|
||||
$this->nodeRevisions[$type][] = $nodes[$type];
|
||||
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
// Create a revision for the same nid and settings with a random log.
|
||||
$revision = clone $nodes[$type];
|
||||
$revision->setNewRevision();
|
||||
$revision->revision_log = $this->randomMachineName(32);
|
||||
$revision->save();
|
||||
$this->nodeRevisions[$type][] = $revision;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests general revision access permissions.
|
||||
*/
|
||||
public function testNodeRevisionAccessAnyType() {
|
||||
// Create three users, one with each revision permission.
|
||||
foreach ($this->map as $op => $permission) {
|
||||
// Create the user.
|
||||
$account = $this->drupalCreateUser(
|
||||
[
|
||||
'access content',
|
||||
'edit any page content',
|
||||
'delete any page content',
|
||||
$permission,
|
||||
]
|
||||
);
|
||||
$account->op = $op;
|
||||
$this->accounts[] = $account;
|
||||
}
|
||||
|
||||
// Create an admin account (returns TRUE for all revision permissions).
|
||||
$admin_account = $this->drupalCreateUser(['access content', 'administer nodes']);
|
||||
$admin_account->is_admin = TRUE;
|
||||
$this->accounts['admin'] = $admin_account;
|
||||
$accounts['admin'] = $admin_account;
|
||||
|
||||
// Create a normal account (returns FALSE for all revision permissions).
|
||||
$normal_account = $this->drupalCreateUser();
|
||||
$normal_account->op = FALSE;
|
||||
$this->accounts[] = $normal_account;
|
||||
$accounts[] = $normal_account;
|
||||
$revision = $this->nodeRevisions['page'][1];
|
||||
|
||||
$parameters = [
|
||||
'op' => array_keys($this->map),
|
||||
'account' => $this->accounts,
|
||||
];
|
||||
|
||||
$permutations = $this->generatePermutations($parameters);
|
||||
|
||||
$node_revision_access = \Drupal::service('access_check.node.revision');
|
||||
foreach ($permutations as $case) {
|
||||
// Skip this test if there are no revisions for the node.
|
||||
if (!($revision->isDefaultRevision() && (db_query('SELECT COUNT(vid) FROM {node_field_revision} WHERE nid = :nid', [':nid' => $revision->id()])->fetchField() == 1 || $case['op'] == 'update' || $case['op'] == 'delete'))) {
|
||||
if (!empty($case['account']->is_admin) || $case['account']->hasPermission($this->map[$case['op']])) {
|
||||
$this->assertTrue($node_revision_access->checkAccess($revision, $case['account'], $case['op']), "{$this->map[$case['op']]} granted.");
|
||||
}
|
||||
else {
|
||||
$this->assertFalse($node_revision_access->checkAccess($revision, $case['account'], $case['op']), "{$this->map[$case['op']]} not granted.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test that access is FALSE for a node administrator with an invalid $node
|
||||
// or $op parameters.
|
||||
$admin_account = $accounts['admin'];
|
||||
$this->assertFalse($node_revision_access->checkAccess($revision, $admin_account, 'invalid-op'), 'NodeRevisionAccessCheck() returns FALSE with an invalid op.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests revision access permissions for a specific content type.
|
||||
*/
|
||||
public function testNodeRevisionAccessPerType() {
|
||||
// Create three users, one with each revision permission.
|
||||
foreach ($this->typeMap as $op => $permission) {
|
||||
// Create the user.
|
||||
$account = $this->drupalCreateUser(
|
||||
[
|
||||
'access content',
|
||||
'edit any page content',
|
||||
'delete any page content',
|
||||
$permission,
|
||||
]
|
||||
);
|
||||
$account->op = $op;
|
||||
$accounts[] = $account;
|
||||
}
|
||||
|
||||
$parameters = [
|
||||
'op' => array_keys($this->typeMap),
|
||||
'account' => $accounts,
|
||||
];
|
||||
|
||||
// Test that the accounts have access to the corresponding page revision
|
||||
// permissions.
|
||||
$revision = $this->nodeRevisions['page'][1];
|
||||
|
||||
$permutations = $this->generatePermutations($parameters);
|
||||
$node_revision_access = \Drupal::service('access_check.node.revision');
|
||||
foreach ($permutations as $case) {
|
||||
// Skip this test if there are no revisions for the node.
|
||||
if (!($revision->isDefaultRevision() && (db_query('SELECT COUNT(vid) FROM {node_field_revision} WHERE nid = :nid', [':nid' => $revision->id()])->fetchField() == 1 || $case['op'] == 'update' || $case['op'] == 'delete'))) {
|
||||
if (!empty($case['account']->is_admin) || $case['account']->hasPermission($this->typeMap[$case['op']])) {
|
||||
$this->assertTrue($node_revision_access->checkAccess($revision, $case['account'], $case['op']), "{$this->typeMap[$case['op']]} granted.");
|
||||
}
|
||||
else {
|
||||
$this->assertFalse($node_revision_access->checkAccess($revision, $case['account'], $case['op']), "{$this->typeMap[$case['op']]} not granted.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test that the accounts have no access to the article revisions.
|
||||
$revision = $this->nodeRevisions['article'][1];
|
||||
|
||||
foreach ($permutations as $case) {
|
||||
$this->assertFalse($node_revision_access->checkAccess($revision, $case['account'], $case['op']), "{$this->typeMap[$case['op']]} did not grant revision permission for articles.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,224 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\node\NodeInterface;
|
||||
|
||||
/**
|
||||
* Create a node with revisions and test viewing, saving, reverting, and
|
||||
* deleting revisions for user with access to all.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeRevisionsAllTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* A list of nodes created to be used as starting point of different tests.
|
||||
*
|
||||
* @var Drupal\node\NodeInterface[]
|
||||
*/
|
||||
protected $nodes;
|
||||
|
||||
/**
|
||||
* Revision logs of nodes created by the setup method.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $revisionLogs;
|
||||
|
||||
/**
|
||||
* An arbitrary user for revision authoring.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $revisionUser;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create and log in user.
|
||||
$web_user = $this->drupalCreateUser(
|
||||
[
|
||||
'view page revisions',
|
||||
'revert page revisions',
|
||||
'delete page revisions',
|
||||
'edit any page content',
|
||||
'delete any page content',
|
||||
]
|
||||
);
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
// Create an initial node.
|
||||
$node = $this->drupalCreateNode();
|
||||
|
||||
// Create a user for revision authoring.
|
||||
// This must be different from user performing revert.
|
||||
$this->revisionUser = $this->drupalCreateUser();
|
||||
|
||||
$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();
|
||||
// Ensure the revision author is a different user.
|
||||
$node->setRevisionUserId($this->revisionUser->id());
|
||||
$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 the revision author is the user performing the revert.
|
||||
$this->assertTrue($reverted_node->getRevisionUserId() == $this->loggedInUser->id(), 'Node revision author is user performing revert.');
|
||||
// And that its not the revision author.
|
||||
$this->assertTrue($reverted_node->getRevisionUserId() != $this->revisionUser->id(), 'Node revision author is not original revision author.');
|
||||
|
||||
// Confirm that this is not the current version.
|
||||
$node = node_revision_load($node->getRevisionId());
|
||||
$this->assertFalse($node->isDefaultRevision(), 'Third node revision is not the current one.');
|
||||
|
||||
// Confirm that the node can still be updated.
|
||||
$this->drupalPostForm("node/" . $reverted_node->id() . "/edit", ['body[0][value]' => 'We are Drupal.'], t('Save'));
|
||||
$this->assertText(t('Basic page @title has been updated.', ['@title' => $reverted_node->getTitle()]), 'Node was successfully saved after reverting a revision.');
|
||||
$this->assertText('We are Drupal.', 'Node was correctly updated after reverting a revision.');
|
||||
|
||||
// Confirm revisions delete properly.
|
||||
$this->drupalPostForm("node/" . $node->id() . "/revisions/" . $nodes[1]->getRevisionId() . "/delete", [], t('Delete'));
|
||||
$this->assertRaw(t('Revision from %revision-date of @type %title has been deleted.',
|
||||
[
|
||||
'%revision-date' => format_date($nodes[1]->getRevisionCreationTime()),
|
||||
'@type' => 'Basic page',
|
||||
'%title' => $nodes[1]->getTitle(),
|
||||
]),
|
||||
'Revision deleted.');
|
||||
$this->assertTrue(db_query('SELECT COUNT(vid) FROM {node_revision} WHERE nid = :nid and vid = :vid',
|
||||
[':nid' => $node->id(), ':vid' => $nodes[1]->getRevisionId()])->fetchField() == 0,
|
||||
'Revision not found.');
|
||||
|
||||
// Set the revision timestamp to an older date to make sure that the
|
||||
// confirmation message correctly displays the stored revision date.
|
||||
$old_revision_date = REQUEST_TIME - 86400;
|
||||
db_update('node_revision')
|
||||
->condition('vid', $nodes[2]->getRevisionId())
|
||||
->fields([
|
||||
'revision_timestamp' => $old_revision_date,
|
||||
])
|
||||
->execute();
|
||||
$this->drupalPostForm("node/" . $node->id() . "/revisions/" . $nodes[2]->getRevisionId() . "/revert", [], t('Revert'));
|
||||
$this->assertRaw(t('@type %title has been reverted to the revision from %revision-date.', [
|
||||
'@type' => 'Basic page',
|
||||
'%title' => $nodes[2]->getTitle(),
|
||||
'%revision-date' => format_date($old_revision_date),
|
||||
]));
|
||||
|
||||
// Create 50 more revisions in order to trigger paging on the revisions
|
||||
// overview screen.
|
||||
$node = $nodes[0];
|
||||
for ($i = 0; $i < 50; $i++) {
|
||||
$logs[] = $node->revision_log = $this->randomMachineName(32);
|
||||
|
||||
$node = $this->createNodeRevision($node);
|
||||
$nodes[] = clone $node;
|
||||
}
|
||||
|
||||
$this->drupalGet('node/' . $node->id() . '/revisions');
|
||||
|
||||
// Check that the pager exists.
|
||||
$this->assertRaw('page=1');
|
||||
|
||||
// Check that the last revision is displayed on the first page.
|
||||
$this->assertText(end($logs));
|
||||
|
||||
// Go to the second page and check that one of the initial three revisions
|
||||
// is displayed.
|
||||
$this->clickLink(t('Page 2'));
|
||||
$this->assertText($logs[2]);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,451 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\NodeInterface;
|
||||
use Drupal\Component\Serialization\Json;
|
||||
|
||||
/**
|
||||
* Create a node with revisions and test viewing, saving, reverting, and
|
||||
* deleting revisions for users with access for this content type.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeRevisionsTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* An array of node revisions.
|
||||
*
|
||||
* @var \Drupal\node\NodeInterface[]
|
||||
*/
|
||||
protected $nodes;
|
||||
|
||||
/**
|
||||
* Revision log messages.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $revisionLogs;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['node', 'contextual', 'datetime', 'language', 'content_translation'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Enable additional languages.
|
||||
ConfigurableLanguage::createFromLangcode('de')->save();
|
||||
ConfigurableLanguage::createFromLangcode('it')->save();
|
||||
|
||||
$field_storage_definition = [
|
||||
'field_name' => 'untranslatable_string_field',
|
||||
'entity_type' => 'node',
|
||||
'type' => 'string',
|
||||
'cardinality' => 1,
|
||||
'translatable' => FALSE,
|
||||
];
|
||||
$field_storage = FieldStorageConfig::create($field_storage_definition);
|
||||
$field_storage->save();
|
||||
|
||||
$field_definition = [
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'page',
|
||||
];
|
||||
$field = FieldConfig::create($field_definition);
|
||||
$field->save();
|
||||
|
||||
// Enable translation for page nodes.
|
||||
\Drupal::service('content_translation.manager')->setEnabled('node', 'page', TRUE);
|
||||
|
||||
// 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',
|
||||
'access contextual links',
|
||||
'translate any entity',
|
||||
'administer content types',
|
||||
]
|
||||
);
|
||||
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
// Create initial node.
|
||||
$node = $this->drupalCreateNode();
|
||||
$settings = get_object_vars($node);
|
||||
$settings['revision'] = 1;
|
||||
$settings['isDefaultRevision'] = TRUE;
|
||||
|
||||
$nodes = [];
|
||||
$logs = [];
|
||||
|
||||
// Get 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);
|
||||
|
||||
// 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->untranslatable_string_field->value = $this->randomString();
|
||||
$node->setNewRevision();
|
||||
|
||||
// Edit the 1st and 2nd revision with a different user.
|
||||
if ($i < 2) {
|
||||
$editor = $this->drupalCreateUser();
|
||||
$node->setRevisionUserId($editor->id());
|
||||
}
|
||||
else {
|
||||
$node->setRevisionUserId($web_user->id());
|
||||
}
|
||||
|
||||
$node->save();
|
||||
|
||||
// Make sure we get revision information.
|
||||
$node = Node::load($node->id());
|
||||
$nodes[] = clone $node;
|
||||
}
|
||||
|
||||
$this->nodes = $nodes;
|
||||
$this->revisionLogs = $logs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks node revision related 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];
|
||||
|
||||
// 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 log message appears on "revisions overview" page.
|
||||
$this->drupalGet("node/" . $node->id() . "/revisions");
|
||||
foreach ($logs as $revision_log) {
|
||||
$this->assertText($revision_log, 'Revision log message found.');
|
||||
}
|
||||
// Original author, and editor names should appear on revisions overview.
|
||||
$web_user = $nodes[0]->revision_uid->entity;
|
||||
$this->assertText(t('by @name', ['@name' => $web_user->getAccountName()]));
|
||||
$editor = $nodes[2]->revision_uid->entity;
|
||||
$this->assertText(t('by @name', ['@name' => $editor->getAccountName()]));
|
||||
|
||||
// Confirm that this is the default revision.
|
||||
$this->assertTrue($node->isDefaultRevision(), 'Third node revision is the default 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]->label(),
|
||||
'%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 the revision author is the user performing the revert.
|
||||
$this->assertTrue($reverted_node->getRevisionUserId() == $this->loggedInUser->id(), 'Node revision author is user performing revert.');
|
||||
// And that its not the revision author.
|
||||
$this->assertTrue($reverted_node->getRevisionUserId() != $nodes[1]->getRevisionUserId(), 'Node revision author is not original revision author.');
|
||||
|
||||
// Confirm that this is not the default version.
|
||||
$node = node_revision_load($node->getRevisionId());
|
||||
$this->assertFalse($node->isDefaultRevision(), 'Third node revision is not the default 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]->label(),
|
||||
]), '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.');
|
||||
$this->assertTrue(db_query('SELECT COUNT(vid) FROM {node_field_revision} WHERE nid = :nid and vid = :vid', [':nid' => $node->id(), ':vid' => $nodes[1]->getRevisionId()])->fetchField() == 0, 'Field 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]->label(),
|
||||
'%revision-date' => format_date($old_revision_date),
|
||||
]));
|
||||
|
||||
// Make a new revision and set it to not be default.
|
||||
// This will create a new revision that is not "front facing".
|
||||
$new_node_revision = clone $node;
|
||||
$new_body = $this->randomMachineName();
|
||||
$new_node_revision->body->value = $new_body;
|
||||
// Save this as a non-default revision.
|
||||
$new_node_revision->setNewRevision();
|
||||
$new_node_revision->isDefaultRevision = FALSE;
|
||||
$new_node_revision->save();
|
||||
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$this->assertNoText($new_body, 'Revision body text is not present on default version of node.');
|
||||
|
||||
// Verify that the new body text is present on the revision.
|
||||
$this->drupalGet("node/" . $node->id() . "/revisions/" . $new_node_revision->getRevisionId() . "/view");
|
||||
$this->assertText($new_body, 'Revision body text is present when loading specific revision.');
|
||||
|
||||
// Verify that the non-default revision vid is greater than the default
|
||||
// revision vid.
|
||||
$default_revision = db_select('node', 'n')
|
||||
->fields('n', ['vid'])
|
||||
->condition('nid', $node->id())
|
||||
->execute()
|
||||
->fetchCol();
|
||||
$default_revision_vid = $default_revision[0];
|
||||
$this->assertTrue($new_node_revision->getRevisionId() > $default_revision_vid, 'Revision vid is greater than default revision vid.');
|
||||
|
||||
// Create an 'EN' node with a revision log message.
|
||||
$node = $this->drupalCreateNode();
|
||||
$node->title = 'Node title in EN';
|
||||
$node->revision_log = 'Simple revision message (EN)';
|
||||
$node->save();
|
||||
|
||||
$this->drupalGet("node/" . $node->id() . "/revisions");
|
||||
$this->assertResponse(403);
|
||||
|
||||
// Create a new revision and new log message.
|
||||
$node = Node::load($node->id());
|
||||
$node->body->value = 'New text (EN)';
|
||||
$node->revision_log = 'New revision message (EN)';
|
||||
$node->setNewRevision();
|
||||
$node->save();
|
||||
|
||||
// Check both revisions are shown on the node revisions overview page.
|
||||
$this->drupalGet("node/" . $node->id() . "/revisions");
|
||||
$this->assertText('Simple revision message (EN)');
|
||||
$this->assertText('New revision message (EN)');
|
||||
|
||||
// Create an 'EN' node with a revision log message.
|
||||
$node = $this->drupalCreateNode();
|
||||
$node->langcode = 'en';
|
||||
$node->title = 'Node title in EN';
|
||||
$node->revision_log = 'Simple revision message (EN)';
|
||||
$node->save();
|
||||
|
||||
$this->drupalGet("node/" . $node->id() . "/revisions");
|
||||
$this->assertResponse(403);
|
||||
|
||||
// Add a translation in 'DE' and create a new revision and new log message.
|
||||
$translation = $node->addTranslation('de');
|
||||
$translation->title->value = 'Node title in DE';
|
||||
$translation->body->value = 'New text (DE)';
|
||||
$translation->revision_log = 'New revision message (DE)';
|
||||
$translation->setNewRevision();
|
||||
$translation->save();
|
||||
|
||||
// View the revision UI in 'IT', only the original node revision is shown.
|
||||
$this->drupalGet("it/node/" . $node->id() . "/revisions");
|
||||
$this->assertText('Simple revision message (EN)');
|
||||
$this->assertNoText('New revision message (DE)');
|
||||
|
||||
// View the revision UI in 'DE', only the translated node revision is shown.
|
||||
$this->drupalGet("de/node/" . $node->id() . "/revisions");
|
||||
$this->assertNoText('Simple revision message (EN)');
|
||||
$this->assertText('New revision message (DE)');
|
||||
|
||||
// View the revision UI in 'EN', only the original node revision is shown.
|
||||
$this->drupalGet("node/" . $node->id() . "/revisions");
|
||||
$this->assertText('Simple revision message (EN)');
|
||||
$this->assertNoText('New revision message (DE)');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that revisions are correctly saved without log messages.
|
||||
*/
|
||||
public function testNodeRevisionWithoutLogMessage() {
|
||||
$node_storage = $this->container->get('entity.manager')->getStorage('node');
|
||||
// Create a node with an initial log message.
|
||||
$revision_log = $this->randomMachineName(10);
|
||||
$node = $this->drupalCreateNode(['revision_log' => $revision_log]);
|
||||
|
||||
// Save over the same revision and explicitly provide an empty log message
|
||||
// (for example, to mimic the case of a node form submitted with no text in
|
||||
// the "log message" field), and check that the original log message is
|
||||
// preserved.
|
||||
$new_title = $this->randomMachineName(10) . 'testNodeRevisionWithoutLogMessage1';
|
||||
|
||||
$node = clone $node;
|
||||
$node->title = $new_title;
|
||||
$node->revision_log = '';
|
||||
$node->setNewRevision(FALSE);
|
||||
|
||||
$node->save();
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$this->assertText($new_title, 'New node title appears on the page.');
|
||||
$node_storage->resetCache([$node->id()]);
|
||||
$node_revision = $node_storage->load($node->id());
|
||||
$this->assertEqual($node_revision->revision_log->value, $revision_log, 'After an existing node revision is re-saved without a log message, the original log message is preserved.');
|
||||
|
||||
// Create another node with an initial revision log message.
|
||||
$node = $this->drupalCreateNode(['revision_log' => $revision_log]);
|
||||
|
||||
// Save a new node revision without providing a log message, and check that
|
||||
// this revision has an empty log message.
|
||||
$new_title = $this->randomMachineName(10) . 'testNodeRevisionWithoutLogMessage2';
|
||||
|
||||
$node = clone $node;
|
||||
$node->title = $new_title;
|
||||
$node->setNewRevision();
|
||||
$node->revision_log = NULL;
|
||||
|
||||
$node->save();
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$this->assertText($new_title, 'New node title appears on the page.');
|
||||
$node_storage->resetCache([$node->id()]);
|
||||
$node_revision = $node_storage->load($node->id());
|
||||
$this->assertTrue(empty($node_revision->revision_log->value), 'After a new node revision is saved with an empty log message, the log message for the node is empty.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets server-rendered contextual links for the given contextual links IDs.
|
||||
*
|
||||
* @param string[] $ids
|
||||
* An array of contextual link IDs.
|
||||
* @param string $current_path
|
||||
* The Drupal path for the page for which the contextual links are rendered.
|
||||
*
|
||||
* @return string
|
||||
* The decoded JSON response body.
|
||||
*/
|
||||
protected function renderContextualLinks(array $ids, $current_path) {
|
||||
$post = [];
|
||||
for ($i = 0; $i < count($ids); $i++) {
|
||||
$post['ids[' . $i . ']'] = $ids[$i];
|
||||
}
|
||||
$response = $this->drupalPost('contextual/render', 'application/json', $post, ['query' => ['destination' => $current_path]]);
|
||||
|
||||
return Json::decode($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the revision translations are correctly reverted.
|
||||
*/
|
||||
public function testRevisionTranslationRevert() {
|
||||
// Create a node and a few revisions.
|
||||
$node = $this->drupalCreateNode(['langcode' => 'en']);
|
||||
|
||||
$initial_revision_id = $node->getRevisionId();
|
||||
$initial_title = $node->label();
|
||||
$this->createRevisions($node, 2);
|
||||
|
||||
// Translate the node and create a few translation revisions.
|
||||
$translation = $node->addTranslation('it');
|
||||
$this->createRevisions($translation, 3);
|
||||
$revert_id = $node->getRevisionId();
|
||||
$translated_title = $translation->label();
|
||||
$untranslatable_string = $node->untranslatable_string_field->value;
|
||||
|
||||
// Create a new revision for the default translation in-between a series of
|
||||
// translation revisions.
|
||||
$this->createRevisions($node, 1);
|
||||
$default_translation_title = $node->label();
|
||||
|
||||
// And create a few more translation revisions.
|
||||
$this->createRevisions($translation, 2);
|
||||
$translation_revision_id = $translation->getRevisionId();
|
||||
|
||||
// Now revert the a translation revision preceding the last default
|
||||
// translation revision, and check that the desired value was reverted but
|
||||
// the default translation value was preserved.
|
||||
$revert_translation_url = Url::fromRoute('node.revision_revert_translation_confirm', [
|
||||
'node' => $node->id(),
|
||||
'node_revision' => $revert_id,
|
||||
'langcode' => 'it',
|
||||
]);
|
||||
$this->drupalPostForm($revert_translation_url, [], t('Revert'));
|
||||
/** @var \Drupal\node\NodeStorage $node_storage */
|
||||
$node_storage = $this->container->get('entity.manager')->getStorage('node');
|
||||
$node_storage->resetCache();
|
||||
/** @var \Drupal\node\NodeInterface $node */
|
||||
$node = $node_storage->load($node->id());
|
||||
$this->assertTrue($node->getRevisionId() > $translation_revision_id);
|
||||
$this->assertEqual($node->label(), $default_translation_title);
|
||||
$this->assertEqual($node->getTranslation('it')->label(), $translated_title);
|
||||
$this->assertNotEqual($node->untranslatable_string_field->value, $untranslatable_string);
|
||||
|
||||
$latest_revision_id = $translation->getRevisionId();
|
||||
|
||||
// Now revert the a translation revision preceding the last default
|
||||
// translation revision again, and check that the desired value was reverted
|
||||
// but the default translation value was preserved. But in addition the
|
||||
// untranslated field will be reverted as well.
|
||||
$this->drupalPostForm($revert_translation_url, ['revert_untranslated_fields' => TRUE], t('Revert'));
|
||||
$node_storage->resetCache();
|
||||
/** @var \Drupal\node\NodeInterface $node */
|
||||
$node = $node_storage->load($node->id());
|
||||
$this->assertTrue($node->getRevisionId() > $latest_revision_id);
|
||||
$this->assertEqual($node->label(), $default_translation_title);
|
||||
$this->assertEqual($node->getTranslation('it')->label(), $translated_title);
|
||||
$this->assertEqual($node->untranslatable_string_field->value, $untranslatable_string);
|
||||
|
||||
$latest_revision_id = $translation->getRevisionId();
|
||||
|
||||
// Now revert the entity revision to the initial one where the translation
|
||||
// didn't exist.
|
||||
$revert_url = Url::fromRoute('node.revision_revert_confirm', [
|
||||
'node' => $node->id(),
|
||||
'node_revision' => $initial_revision_id,
|
||||
]);
|
||||
$this->drupalPostForm($revert_url, [], t('Revert'));
|
||||
$node_storage->resetCache();
|
||||
/** @var \Drupal\node\NodeInterface $node */
|
||||
$node = $node_storage->load($node->id());
|
||||
$this->assertTrue($node->getRevisionId() > $latest_revision_id);
|
||||
$this->assertEqual($node->label(), $initial_title);
|
||||
$this->assertFalse($node->hasTranslation('it'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a series of revisions for the specified node.
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* The node object.
|
||||
* @param $count
|
||||
* The number of revisions to be created.
|
||||
*/
|
||||
protected function createRevisions(NodeInterface $node, $count) {
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$node->title = $this->randomString();
|
||||
$node->untranslatable_string_field->value = $this->randomString();
|
||||
$node->setNewRevision(TRUE);
|
||||
$node->save();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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');
|
||||
|
||||
$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');
|
||||
|
||||
$this->assertUrl($node->toUrl());
|
||||
$this->assertLink(t('Revisions'));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
<?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'));
|
||||
|
||||
// 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'));
|
||||
|
||||
// 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 latest affected revision having been a default revision
|
||||
// is displayed as the current one.
|
||||
$this->assertNoLinkByHref('/node/' . $node_id . '/revisions/1/revert');
|
||||
$elements = $this->xpath('//tr[contains(@class, "revision-current")]/td/a[1]');
|
||||
// The site may be installed in a subdirectory, so check if the URL is
|
||||
// contained in the retrieved one.
|
||||
$this->assertContains('/node/1', current($elements)->getAttribute('href'));
|
||||
|
||||
// Verify that the default revision can be an older revision than the latest
|
||||
// one.
|
||||
// Assert that the revisions with translations changes are shown.
|
||||
$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');
|
||||
}
|
||||
|
||||
}
|
184
2017/web/core/modules/node/tests/src/Functional/NodeSaveTest.php
Normal file
184
2017/web/core/modules/node/tests/src/Functional/NodeSaveTest.php
Normal file
|
@ -0,0 +1,184 @@
|
|||
<?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),
|
||||
// Sun, 19 Nov 1978 05:00:00 GMT.
|
||||
'created' => 280299600,
|
||||
// Drupal 1.0 release.
|
||||
'changed' => 979534800,
|
||||
];
|
||||
|
||||
Node::create($edit)->save();
|
||||
$node = $this->drupalGetNodeByTitle($edit['title']);
|
||||
$this->assertEqual($node->getCreatedTime(), 280299600, 'Creating a node programmatically uses programmatically set "created" timestamp.');
|
||||
$this->assertEqual($node->getChangedTime(), 979534800, 'Creating a node programmatically uses programmatically set "changed" timestamp.');
|
||||
|
||||
// Update the timestamps.
|
||||
$node->setCreatedTime(979534800);
|
||||
$node->changed = 280299600;
|
||||
|
||||
$node->save();
|
||||
$node = $this->drupalGetNodeByTitle($edit['title'], TRUE);
|
||||
$this->assertEqual($node->getCreatedTime(), 979534800, 'Updating a node uses user-set "created" timestamp.');
|
||||
// Allowing setting changed timestamps is required, see
|
||||
// Drupal\content_translation\ContentTranslationMetadataWrapper::setChangedTime($timestamp)
|
||||
// for example.
|
||||
$this->assertEqual($node->getChangedTime(), 280299600, 'Updating a node uses user-set "changed" timestamp.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests node presave and static node load cache.
|
||||
*
|
||||
* This test determines changes in hook_ENTITY_TYPE_presave() and verifies
|
||||
* that the static node load cache is cleared upon save.
|
||||
*/
|
||||
public function testDeterminingChanges() {
|
||||
// Initial creation.
|
||||
$node = Node::create([
|
||||
'uid' => $this->webUser->id(),
|
||||
'type' => 'article',
|
||||
'title' => 'test_changes',
|
||||
]);
|
||||
$node->save();
|
||||
|
||||
// Update the node without applying changes.
|
||||
$node->save();
|
||||
$this->assertEqual($node->label(), 'test_changes', 'No changes have been determined.');
|
||||
|
||||
// Apply changes.
|
||||
$node->title = 'updated';
|
||||
$node->save();
|
||||
|
||||
// The hook implementations node_test_node_presave() and
|
||||
// node_test_node_update() determine changes and change the title.
|
||||
$this->assertEqual($node->label(), 'updated_presave_update', 'Changes have been determined.');
|
||||
|
||||
// Test the static node load cache to be cleared.
|
||||
$node = Node::load($node->id());
|
||||
$this->assertEqual($node->label(), 'updated_presave', 'Static cache has been cleared.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests saving a node on node insert.
|
||||
*
|
||||
* This test ensures that a node has been fully saved when
|
||||
* hook_ENTITY_TYPE_insert() is invoked, so that the node can be saved again
|
||||
* in a hook implementation without errors.
|
||||
*
|
||||
* @see node_test_node_insert()
|
||||
*/
|
||||
public function testNodeSaveOnInsert() {
|
||||
// node_test_node_insert() triggers a save on insert if the title equals
|
||||
// 'new'.
|
||||
$node = $this->drupalCreateNode(['title' => 'new']);
|
||||
$this->assertEqual($node->getTitle(), 'Node ' . $node->id(), 'Node saved on node insert.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
/**
|
||||
* Tests if the syndicate block is available.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeSyndicateBlockTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['block'];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create a user and log in.
|
||||
$admin_user = $this->drupalCreateUser(['administer blocks']);
|
||||
$this->drupalLogin($admin_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the "Syndicate" block is shown when enabled.
|
||||
*/
|
||||
public function testSyndicateBlock() {
|
||||
// Place the "Syndicate" block and confirm that it is rendered.
|
||||
$this->drupalPlaceBlock('node_syndicate_block', ['id' => 'test_syndicate_block']);
|
||||
$this->drupalGet('');
|
||||
$this->assertFieldByXPath('//div[@id="block-test-syndicate-block"]/*', NULL, 'Syndicate block found.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
/**
|
||||
* Tests node template suggestions.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeTemplateSuggestionsTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Tests if template_preprocess_node() generates the correct suggestions.
|
||||
*/
|
||||
public function testNodeThemeHookSuggestions() {
|
||||
// Create node to be rendered.
|
||||
$node = $this->drupalCreateNode();
|
||||
$view_mode = 'full';
|
||||
|
||||
// Simulate theming of the node.
|
||||
$build = \Drupal::entityManager()->getViewBuilder('node')->view($node, $view_mode);
|
||||
|
||||
$variables['elements'] = $build;
|
||||
$suggestions = \Drupal::moduleHandler()->invokeAll('theme_suggestions_node', [$variables]);
|
||||
|
||||
$this->assertEqual($suggestions, ['node__full', 'node__page', 'node__page__full', 'node__' . $node->id(), 'node__' . $node->id() . '__full'], 'Found expected node suggestions.');
|
||||
|
||||
// Change the view mode.
|
||||
$view_mode = 'node.my_custom_view_mode';
|
||||
$build = \Drupal::entityManager()->getViewBuilder('node')->view($node, $view_mode);
|
||||
|
||||
$variables['elements'] = $build;
|
||||
$suggestions = \Drupal::moduleHandler()->invokeAll('theme_suggestions_node', [$variables]);
|
||||
|
||||
$this->assertEqual($suggestions, ['node__node_my_custom_view_mode', 'node__page', 'node__page__node_my_custom_view_mode', 'node__' . $node->id(), 'node__' . $node->id() . '__node_my_custom_view_mode'], 'Found expected node suggestions.');
|
||||
}
|
||||
|
||||
}
|
110
2017/web/core/modules/node/tests/src/Functional/NodeTestBase.php
Normal file
110
2017/web/core/modules/node/tests/src/Functional/NodeTestBase.php
Normal file
|
@ -0,0 +1,110 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\node\NodeInterface;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Sets up page and article content types.
|
||||
*/
|
||||
abstract class NodeTestBase extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node', 'datetime'];
|
||||
|
||||
/**
|
||||
* The node access control handler.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityAccessControlHandlerInterface
|
||||
*/
|
||||
protected $accessHandler;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create Basic page and Article node types.
|
||||
if ($this->profile != 'standard') {
|
||||
$this->drupalCreateContentType([
|
||||
'type' => 'page',
|
||||
'name' => 'Basic page',
|
||||
'display_submitted' => FALSE,
|
||||
]);
|
||||
$this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']);
|
||||
}
|
||||
$this->accessHandler = \Drupal::entityManager()->getAccessControlHandler('node');
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that node access correctly grants or denies access.
|
||||
*
|
||||
* @param array $ops
|
||||
* An associative array of the expected node access grants for the node
|
||||
* and account, with each key as the name of an operation (e.g. 'view',
|
||||
* 'delete') and each value a Boolean indicating whether access to that
|
||||
* operation should be granted.
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* The node object to check.
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The user account for which to check access.
|
||||
*/
|
||||
public function assertNodeAccess(array $ops, NodeInterface $node, AccountInterface $account) {
|
||||
foreach ($ops as $op => $result) {
|
||||
$this->assertEqual($result, $this->accessHandler->access($node, $op, $account), $this->nodeAccessAssertMessage($op, $result, $node->language()->getId()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that node create access correctly grants or denies access.
|
||||
*
|
||||
* @param string $bundle
|
||||
* The node bundle to check access to.
|
||||
* @param bool $result
|
||||
* Whether access should be granted or not.
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The user account for which to check access.
|
||||
* @param string|null $langcode
|
||||
* (optional) The language code indicating which translation of the node
|
||||
* to check. If NULL, the untranslated (fallback) access is checked.
|
||||
*/
|
||||
public function assertNodeCreateAccess($bundle, $result, AccountInterface $account, $langcode = NULL) {
|
||||
$this->assertEqual($result, $this->accessHandler->createAccess($bundle, $account, [
|
||||
'langcode' => $langcode,
|
||||
]), $this->nodeAccessAssertMessage('create', $result, $langcode));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an assert message to display which node access was tested.
|
||||
*
|
||||
* @param string $operation
|
||||
* The operation to check access for.
|
||||
* @param bool $result
|
||||
* Whether access should be granted or not.
|
||||
* @param string|null $langcode
|
||||
* (optional) The language code indicating which translation of the node
|
||||
* to check. If NULL, the untranslated (fallback) access is checked.
|
||||
*
|
||||
* @return string
|
||||
* An assert message string which contains information in plain English
|
||||
* about the node access permission test that was performed.
|
||||
*/
|
||||
public function nodeAccessAssertMessage($operation, $result, $langcode = NULL) {
|
||||
return format_string(
|
||||
'Node access returns @result with operation %op, language code %langcode.',
|
||||
[
|
||||
'@result' => $result ? 'true' : 'false',
|
||||
'%op' => $operation,
|
||||
'%langcode' => !empty($langcode) ? $langcode : 'empty',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\comment\Tests\CommentTestTrait;
|
||||
use Drupal\Component\Utility\Html;
|
||||
|
||||
/**
|
||||
* Tests node title.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeTitleTest extends NodeTestBase {
|
||||
|
||||
use CommentTestTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['comment', 'views', 'block'];
|
||||
|
||||
/**
|
||||
* A user with permission to bypass access content.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->drupalPlaceBlock('system_breadcrumb_block');
|
||||
|
||||
$this->adminUser = $this->drupalCreateUser(['administer nodes', 'create article content', 'create page content', 'post comments']);
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->addDefaultCommentField('node', 'page');
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates one node and tests if the node title has the correct value.
|
||||
*/
|
||||
public function testNodeTitle() {
|
||||
// Create "Basic page" content with title.
|
||||
// Add the node to the frontpage so we can test if teaser links are
|
||||
// clickable.
|
||||
$settings = [
|
||||
'title' => $this->randomMachineName(8),
|
||||
'promote' => 1,
|
||||
];
|
||||
$node = $this->drupalCreateNode($settings);
|
||||
|
||||
// Test <title> tag.
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$xpath = '//title';
|
||||
$this->assertEqual($this->xpath($xpath)[0]->getText(), $node->label() . ' | Drupal', 'Page title is equal to node title.', 'Node');
|
||||
|
||||
// Test breadcrumb in comment preview.
|
||||
$this->drupalGet('comment/reply/node/' . $node->id() . '/comment');
|
||||
$xpath = '//nav[@class="breadcrumb"]/ol/li[last()]/a';
|
||||
$this->assertEqual($this->xpath($xpath)[0]->getText(), $node->label(), 'Node breadcrumb is equal to node title.', 'Node');
|
||||
|
||||
// Test node title in comment preview.
|
||||
$this->assertEqual($this->xpath('//article[contains(concat(" ", normalize-space(@class), " "), :node-class)]/h2/a/span', [':node-class' => ' node--type-' . $node->bundle() . ' '])[0]->getText(), $node->label(), 'Node preview title is equal to node title.', 'Node');
|
||||
|
||||
// Test node title is clickable on teaser list (/node).
|
||||
$this->drupalGet('node');
|
||||
$this->clickLink($node->label());
|
||||
|
||||
// Test edge case where node title is set to 0.
|
||||
$settings = [
|
||||
'title' => 0,
|
||||
];
|
||||
$node = $this->drupalCreateNode($settings);
|
||||
// Test that 0 appears as <title>.
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$this->assertTitle(0 . ' | Drupal', 'Page title is equal to 0.', 'Node');
|
||||
// Test that 0 appears in the template <h1>.
|
||||
$xpath = '//h1';
|
||||
$this->assertEqual(current($this->xpath($xpath)), 0, 'Node title is displayed as 0.', 'Node');
|
||||
|
||||
// Test edge case where node title contains special characters.
|
||||
$edge_case_title = 'article\'s "title".';
|
||||
$settings = [
|
||||
'title' => $edge_case_title,
|
||||
];
|
||||
$node = $this->drupalCreateNode($settings);
|
||||
// Test that the title appears as <title>. The title will be escaped on the
|
||||
// the page.
|
||||
$edge_case_title_escaped = Html::escape($edge_case_title);
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$this->assertRaw('<title>' . $edge_case_title_escaped . ' | Drupal</title>', 'Page title is equal to article\'s "title".', 'Node');
|
||||
|
||||
// Test that the title appears as <title> when reloading the node page.
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$this->assertRaw('<title>' . $edge_case_title_escaped . ' | Drupal</title>', 'Page title is equal to article\'s "title".', 'Node');
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
|
||||
/**
|
||||
* Create a node with dangerous tags in its title and test that they are
|
||||
* escaped.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeTitleXSSTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Tests XSS functionality with a node entity.
|
||||
*/
|
||||
public function testNodeTitleXSS() {
|
||||
// Prepare a user to do the stuff.
|
||||
$web_user = $this->drupalCreateUser(['create page content', 'edit any page content']);
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
$xss = '<script>alert("xss")</script>';
|
||||
$title = $xss . $this->randomMachineName();
|
||||
$edit = [];
|
||||
$edit['title[0][value]'] = $title;
|
||||
|
||||
$this->drupalPostForm('node/add/page', $edit, t('Preview'));
|
||||
$this->assertNoRaw($xss, 'Harmful tags are escaped when previewing a node.');
|
||||
|
||||
$settings = ['title' => $title];
|
||||
$node = $this->drupalCreateNode($settings);
|
||||
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
// Titles should be escaped.
|
||||
$this->assertRaw('<title>' . Html::escape($title) . ' | Drupal</title>', 'Title is displayed when viewing a node.');
|
||||
$this->assertNoRaw($xss, 'Harmful tags are escaped when viewing a node.');
|
||||
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$this->assertNoRaw($xss, 'Harmful tags are escaped when editing a node.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,529 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Tests\content_translation\Functional\ContentTranslationUITestBase;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
|
||||
/**
|
||||
* Tests the Node Translation UI.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeTranslationUITest extends ContentTranslationUITestBase {
|
||||
|
||||
/**
|
||||
* {inheritdoc}
|
||||
*/
|
||||
protected $defaultCacheContexts = [
|
||||
'languages:language_interface',
|
||||
'theme',
|
||||
'route',
|
||||
'timezone',
|
||||
'url.path.parent',
|
||||
'url.query_args:_wrapper_format',
|
||||
'user.roles',
|
||||
'url.path.is_front',
|
||||
// These two cache contexts are added by BigPipe.
|
||||
'cookies:big_pipe_nojs',
|
||||
'session.exists',
|
||||
];
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['block', 'language', 'content_translation', 'node', 'datetime', 'field_ui', 'help'];
|
||||
|
||||
/**
|
||||
* The profile to install as a basis for testing.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $profile = 'standard';
|
||||
|
||||
protected function setUp() {
|
||||
$this->entityTypeId = 'node';
|
||||
$this->bundle = 'article';
|
||||
parent::setUp();
|
||||
|
||||
// Ensure the help message is shown even with prefixed paths.
|
||||
$this->drupalPlaceBlock('help_block', ['region' => 'content']);
|
||||
|
||||
// Display the language selector.
|
||||
$this->drupalLogin($this->administrator);
|
||||
$edit = ['language_configuration[language_alterable]' => TRUE];
|
||||
$this->drupalPostForm('admin/structure/types/manage/article', $edit, t('Save content type'));
|
||||
$this->drupalLogin($this->translator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the basic translation UI.
|
||||
*/
|
||||
public function testTranslationUI() {
|
||||
parent::testTranslationUI();
|
||||
$this->doUninstallTest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests changing the published status on a node without fields.
|
||||
*/
|
||||
public function testPublishedStatusNoFields() {
|
||||
// Test changing the published status of an article without fields.
|
||||
$this->drupalLogin($this->administrator);
|
||||
// Delete all fields.
|
||||
$this->drupalGet('admin/structure/types/manage/article/fields');
|
||||
$this->drupalPostForm('admin/structure/types/manage/article/fields/node.article.' . $this->fieldName . '/delete', [], t('Delete'));
|
||||
$this->drupalPostForm('admin/structure/types/manage/article/fields/node.article.field_tags/delete', [], t('Delete'));
|
||||
$this->drupalPostForm('admin/structure/types/manage/article/fields/node.article.field_image/delete', [], t('Delete'));
|
||||
|
||||
// Add a node.
|
||||
$default_langcode = $this->langcodes[0];
|
||||
$values[$default_langcode] = ['title' => [['value' => $this->randomMachineName()]]];
|
||||
$this->entityId = $this->createEntity($values[$default_langcode], $default_langcode);
|
||||
$storage = $this->container->get('entity_type.manager')
|
||||
->getStorage($this->entityTypeId);
|
||||
$storage->resetCache([$this->entityId]);
|
||||
$entity = $storage->load($this->entityId);
|
||||
|
||||
// Add a content translation.
|
||||
$langcode = 'fr';
|
||||
$language = ConfigurableLanguage::load($langcode);
|
||||
$values[$langcode] = ['title' => [['value' => $this->randomMachineName()]]];
|
||||
|
||||
$entity_type_id = $entity->getEntityTypeId();
|
||||
$add_url = Url::fromRoute("entity.$entity_type_id.content_translation_add", [
|
||||
$entity->getEntityTypeId() => $entity->id(),
|
||||
'source' => $default_langcode,
|
||||
'target' => $langcode,
|
||||
], ['language' => $language]);
|
||||
$edit = $this->getEditValues($values, $langcode);
|
||||
$edit['status[value]'] = FALSE;
|
||||
$this->drupalPostForm($add_url, $edit, t('Save (this translation)'));
|
||||
|
||||
$storage->resetCache([$this->entityId]);
|
||||
$entity = $storage->load($this->entityId);
|
||||
$translation = $entity->getTranslation($langcode);
|
||||
// Make sure we unpublished the node correctly.
|
||||
$this->assertFalse($this->manager->getTranslationMetadata($translation)->isPublished(), 'The translation has been correctly unpublished.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getTranslatorPermissions() {
|
||||
return array_merge(parent::getTranslatorPermissions(), ['administer nodes', "edit any $this->bundle content"]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getEditorPermissions() {
|
||||
return ['administer nodes', 'create article content'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getAdministratorPermissions() {
|
||||
return array_merge(parent::getAdministratorPermissions(), ['access administration pages', 'administer content types', 'administer node fields', 'access content overview', 'bypass node access', 'administer languages', 'administer themes', 'view the administration theme']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNewEntityValues($langcode) {
|
||||
return ['title' => [['value' => $this->randomMachineName()]]] + parent::getNewEntityValues($langcode);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doTestPublishedStatus() {
|
||||
$storage = $this->container->get('entity_type.manager')
|
||||
->getStorage($this->entityTypeId);
|
||||
$storage->resetCache([$this->entityId]);
|
||||
$entity = $storage->load($this->entityId);
|
||||
$languages = $this->container->get('language_manager')->getLanguages();
|
||||
|
||||
$statuses = [
|
||||
TRUE,
|
||||
FALSE,
|
||||
];
|
||||
|
||||
foreach ($statuses as $index => $value) {
|
||||
// (Un)publish the node translations and check that the translation
|
||||
// statuses are (un)published accordingly.
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
$options = ['language' => $languages[$langcode]];
|
||||
$url = $entity->urlInfo('edit-form', $options);
|
||||
$this->drupalPostForm($url, ['status[value]' => $value], t('Save') . $this->getFormSubmitSuffix($entity, $langcode), $options);
|
||||
}
|
||||
$storage->resetCache([$this->entityId]);
|
||||
$entity = $storage->load($this->entityId);
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
// The node is created as unpublished thus we switch to the published
|
||||
// status first.
|
||||
$status = !$index;
|
||||
$translation = $entity->getTranslation($langcode);
|
||||
$this->assertEqual($status, $this->manager->getTranslationMetadata($translation)->isPublished(), 'The translation has been correctly unpublished.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doTestAuthoringInfo() {
|
||||
$storage = $this->container->get('entity_type.manager')
|
||||
->getStorage($this->entityTypeId);
|
||||
$storage->resetCache([$this->entityId]);
|
||||
$entity = $storage->load($this->entityId);
|
||||
$languages = $this->container->get('language_manager')->getLanguages();
|
||||
$values = [];
|
||||
|
||||
// Post different base field information for each translation.
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
$user = $this->drupalCreateUser();
|
||||
$values[$langcode] = [
|
||||
'uid' => $user->id(),
|
||||
'created' => REQUEST_TIME - mt_rand(0, 1000),
|
||||
'sticky' => (bool) mt_rand(0, 1),
|
||||
'promote' => (bool) mt_rand(0, 1),
|
||||
];
|
||||
$edit = [
|
||||
'uid[0][target_id]' => $user->getUsername(),
|
||||
'created[0][value][date]' => format_date($values[$langcode]['created'], 'custom', 'Y-m-d'),
|
||||
'created[0][value][time]' => format_date($values[$langcode]['created'], 'custom', 'H:i:s'),
|
||||
'sticky[value]' => $values[$langcode]['sticky'],
|
||||
'promote[value]' => $values[$langcode]['promote'],
|
||||
];
|
||||
$options = ['language' => $languages[$langcode]];
|
||||
$url = $entity->urlInfo('edit-form', $options);
|
||||
$this->drupalPostForm($url, $edit, $this->getFormSubmitAction($entity, $langcode), $options);
|
||||
}
|
||||
|
||||
$storage->resetCache([$this->entityId]);
|
||||
$entity = $storage->load($this->entityId);
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
$translation = $entity->getTranslation($langcode);
|
||||
$metadata = $this->manager->getTranslationMetadata($translation);
|
||||
$this->assertEqual($metadata->getAuthor()->id(), $values[$langcode]['uid'], 'Translation author correctly stored.');
|
||||
$this->assertEqual($metadata->getCreatedTime(), $values[$langcode]['created'], 'Translation date correctly stored.');
|
||||
$this->assertEqual($translation->isSticky(), $values[$langcode]['sticky'], 'Sticky of Translation correctly stored.');
|
||||
$this->assertEqual($translation->isPromoted(), $values[$langcode]['promote'], 'Promoted of Translation correctly stored.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that translation page inherits admin status of edit page.
|
||||
*/
|
||||
public function testTranslationLinkTheme() {
|
||||
$this->drupalLogin($this->administrator);
|
||||
$article = $this->drupalCreateNode(['type' => 'article', 'langcode' => $this->langcodes[0]]);
|
||||
|
||||
// Set up Seven as the admin theme and use it for node editing.
|
||||
$this->container->get('theme_handler')->install(['seven']);
|
||||
$edit = [];
|
||||
$edit['admin_theme'] = 'seven';
|
||||
$edit['use_admin_theme'] = TRUE;
|
||||
$this->drupalPostForm('admin/appearance', $edit, t('Save configuration'));
|
||||
$this->drupalGet('node/' . $article->id() . '/translations');
|
||||
$this->assertRaw('core/themes/seven/css/base/elements.css', 'Translation uses admin theme if edit is admin.');
|
||||
|
||||
// Turn off admin theme for editing, assert inheritance to translations.
|
||||
$edit['use_admin_theme'] = FALSE;
|
||||
$this->drupalPostForm('admin/appearance', $edit, t('Save configuration'));
|
||||
$this->drupalGet('node/' . $article->id() . '/translations');
|
||||
$this->assertNoRaw('core/themes/seven/css/base/elements.css', 'Translation uses frontend theme if edit is frontend.');
|
||||
|
||||
// Assert presence of translation page itself (vs. DisabledBundle below).
|
||||
$this->assertResponse(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that no metadata is stored for a disabled bundle.
|
||||
*/
|
||||
public function testDisabledBundle() {
|
||||
// Create a bundle that does not have translation enabled.
|
||||
$disabledBundle = $this->randomMachineName();
|
||||
$this->drupalCreateContentType(['type' => $disabledBundle, 'name' => $disabledBundle]);
|
||||
|
||||
// Create a node for each bundle.
|
||||
$node = $this->drupalCreateNode([
|
||||
'type' => $this->bundle,
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
]);
|
||||
|
||||
// Make sure that nothing was inserted into the {content_translation} table.
|
||||
$rows = db_query('SELECT nid, count(nid) AS count FROM {node_field_data} WHERE type <> :type GROUP BY nid HAVING count(nid) >= 2', [':type' => $this->bundle])->fetchAll();
|
||||
$this->assertEqual(0, count($rows));
|
||||
|
||||
// Ensure the translation tab is not accessible.
|
||||
$this->drupalGet('node/' . $node->id() . '/translations');
|
||||
$this->assertResponse(403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that translations are rendered properly.
|
||||
*/
|
||||
public function testTranslationRendering() {
|
||||
$default_langcode = $this->langcodes[0];
|
||||
$values[$default_langcode] = $this->getNewEntityValues($default_langcode);
|
||||
$this->entityId = $this->createEntity($values[$default_langcode], $default_langcode);
|
||||
$node = \Drupal::entityManager()->getStorage($this->entityTypeId)->load($this->entityId);
|
||||
$node->setPromoted(TRUE);
|
||||
|
||||
// Create translations.
|
||||
foreach (array_diff($this->langcodes, [$default_langcode]) as $langcode) {
|
||||
$values[$langcode] = $this->getNewEntityValues($langcode);
|
||||
$translation = $node->addTranslation($langcode, $values[$langcode]);
|
||||
// Publish and promote the translation to frontpage.
|
||||
$translation->setPromoted(TRUE);
|
||||
$translation->setPublished();
|
||||
}
|
||||
$node->save();
|
||||
|
||||
// Test that the frontpage view displays the correct translations.
|
||||
\Drupal::service('module_installer')->install(['views'], TRUE);
|
||||
$this->rebuildContainer();
|
||||
$this->doTestTranslations('node', $values);
|
||||
|
||||
// Enable the translation language renderer.
|
||||
$view = \Drupal::entityManager()->getStorage('view')->load('frontpage');
|
||||
$display = &$view->getDisplay('default');
|
||||
$display['display_options']['rendering_language'] = '***LANGUAGE_entity_translation***';
|
||||
$view->save();
|
||||
|
||||
// Need to check from the beginning, including the base_path, in the url
|
||||
// since the pattern for the default language might be a substring of
|
||||
// the strings for other languages.
|
||||
$base_path = base_path();
|
||||
|
||||
// Check the frontpage for 'Read more' links to each translation.
|
||||
// See also assertTaxonomyPage() in NodeAccessBaseTableTest.
|
||||
$node_href = 'node/' . $node->id();
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
$this->drupalGet('node', ['language' => \Drupal::languageManager()->getLanguage($langcode)]);
|
||||
$num_match_found = 0;
|
||||
if ($langcode == 'en') {
|
||||
// Site default language does not have langcode prefix in the URL.
|
||||
$expected_href = $base_path . $node_href;
|
||||
}
|
||||
else {
|
||||
$expected_href = $base_path . $langcode . '/' . $node_href;
|
||||
}
|
||||
$pattern = '|^' . $expected_href . '$|';
|
||||
foreach ($this->xpath("//a[text()='Read more']") as $link) {
|
||||
if (preg_match($pattern, $link->getAttribute('href'), $matches) == TRUE) {
|
||||
$num_match_found++;
|
||||
}
|
||||
}
|
||||
$this->assertTrue($num_match_found == 1, 'There is 1 Read more link, ' . $expected_href . ', for the ' . $langcode . ' translation of a node on the frontpage. (Found ' . $num_match_found . '.)');
|
||||
}
|
||||
|
||||
// Check the frontpage for 'Add new comment' links that include the
|
||||
// language.
|
||||
$comment_form_href = 'node/' . $node->id() . '#comment-form';
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
$this->drupalGet('node', ['language' => \Drupal::languageManager()->getLanguage($langcode)]);
|
||||
$num_match_found = 0;
|
||||
if ($langcode == 'en') {
|
||||
// Site default language does not have langcode prefix in the URL.
|
||||
$expected_href = $base_path . $comment_form_href;
|
||||
}
|
||||
else {
|
||||
$expected_href = $base_path . $langcode . '/' . $comment_form_href;
|
||||
}
|
||||
$pattern = '|^' . $expected_href . '$|';
|
||||
foreach ($this->xpath("//a[text()='Add new comment']") as $link) {
|
||||
if (preg_match($pattern, $link->getAttribute('href'), $matches) == TRUE) {
|
||||
$num_match_found++;
|
||||
}
|
||||
}
|
||||
$this->assertTrue($num_match_found == 1, 'There is 1 Add new comment link, ' . $expected_href . ', for the ' . $langcode . ' translation of a node on the frontpage. (Found ' . $num_match_found . '.)');
|
||||
}
|
||||
|
||||
// Test that the node page displays the correct translations.
|
||||
$this->doTestTranslations('node/' . $node->id(), $values);
|
||||
|
||||
// Test that the node page has the correct alternate hreflang links.
|
||||
$this->doTestAlternateHreflangLinks($node->urlInfo());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the given path displays the correct translation values.
|
||||
*
|
||||
* @param string $path
|
||||
* The path to be tested.
|
||||
* @param array $values
|
||||
* The translation values to be found.
|
||||
*/
|
||||
protected function doTestTranslations($path, array $values) {
|
||||
$languages = $this->container->get('language_manager')->getLanguages();
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
$this->drupalGet($path, ['language' => $languages[$langcode]]);
|
||||
$this->assertText($values[$langcode]['title'][0]['value'], format_string('The %langcode node translation is correctly displayed.', ['%langcode' => $langcode]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the given path provides the correct alternate hreflang links.
|
||||
*
|
||||
* @param \Drupal\Core\Url $url
|
||||
* The path to be tested.
|
||||
*/
|
||||
protected function doTestAlternateHreflangLinks(Url $url) {
|
||||
$languages = $this->container->get('language_manager')->getLanguages();
|
||||
$url->setAbsolute();
|
||||
$urls = [];
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
$language_url = clone $url;
|
||||
$urls[$langcode] = $language_url->setOption('language', $languages[$langcode]);
|
||||
}
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
$this->drupalGet($urls[$langcode]);
|
||||
foreach ($urls as $alternate_langcode => $language_url) {
|
||||
// Retrieve desired link elements from the HTML head.
|
||||
$links = $this->xpath('head/link[@rel = "alternate" and @href = :href and @hreflang = :hreflang]',
|
||||
[':href' => $language_url->toString(), ':hreflang' => $alternate_langcode]);
|
||||
$this->assert(isset($links[0]), format_string('The %langcode node translation has the correct alternate hreflang link for %alternate_langcode: %link.', ['%langcode' => $langcode, '%alternate_langcode' => $alternate_langcode, '%link' => $url->toString()]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getFormSubmitSuffix(EntityInterface $entity, $langcode) {
|
||||
if (!$entity->isNew() && $entity->isTranslatable()) {
|
||||
$translations = $entity->getTranslationLanguages();
|
||||
if ((count($translations) > 1 || !isset($translations[$langcode])) && ($field = $entity->getFieldDefinition('status'))) {
|
||||
return ' ' . ($field->isTranslatable() ? t('(this translation)') : t('(all translations)'));
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests uninstalling content_translation.
|
||||
*/
|
||||
protected function doUninstallTest() {
|
||||
// Delete all the nodes so there is no data.
|
||||
$nodes = Node::loadMultiple();
|
||||
foreach ($nodes as $node) {
|
||||
$node->delete();
|
||||
}
|
||||
$language_count = count(\Drupal::configFactory()->listAll('language.content_settings.'));
|
||||
\Drupal::service('module_installer')->uninstall(['content_translation']);
|
||||
$this->rebuildContainer();
|
||||
$this->assertEqual($language_count, count(\Drupal::configFactory()->listAll('language.content_settings.')), 'Languages have been fixed rather than deleted during content_translation uninstall.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doTestTranslationEdit() {
|
||||
$storage = $this->container->get('entity_type.manager')
|
||||
->getStorage($this->entityTypeId);
|
||||
$storage->resetCache([$this->entityId]);
|
||||
$entity = $storage->load($this->entityId);
|
||||
$languages = $this->container->get('language_manager')->getLanguages();
|
||||
$type_name = node_get_type_label($entity);
|
||||
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
// We only want to test the title for non-english translations.
|
||||
if ($langcode != 'en') {
|
||||
$options = ['language' => $languages[$langcode]];
|
||||
$url = $entity->urlInfo('edit-form', $options);
|
||||
$this->drupalGet($url);
|
||||
|
||||
$title = t('<em>Edit @type</em> @title [%language translation]', [
|
||||
'@type' => $type_name,
|
||||
'@title' => $entity->getTranslation($langcode)->label(),
|
||||
'%language' => $languages[$langcode]->getName(),
|
||||
]);
|
||||
$this->assertRaw($title);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that revision translations are rendered properly.
|
||||
*/
|
||||
public function testRevisionTranslationRendering() {
|
||||
$storage = \Drupal::entityTypeManager()->getStorage('node');
|
||||
|
||||
// Create a node.
|
||||
$nid = $this->createEntity(['title' => 'First rev en title'], 'en');
|
||||
$node = $storage->load($nid);
|
||||
$original_revision_id = $node->getRevisionId();
|
||||
|
||||
// Add a French translation.
|
||||
$translation = $node->addTranslation('fr');
|
||||
$translation->title = 'First rev fr title';
|
||||
$translation->setNewRevision(FALSE);
|
||||
$translation->save();
|
||||
|
||||
// Create a new revision.
|
||||
$node->title = 'Second rev en title';
|
||||
$node->setNewRevision(TRUE);
|
||||
$node->save();
|
||||
|
||||
// Get an English view of this revision.
|
||||
$original_revision = $storage->loadRevision($original_revision_id);
|
||||
$original_revision_url = $original_revision->toUrl('revision')->toString();
|
||||
|
||||
// Should be different from regular node URL.
|
||||
$this->assertNotIdentical($original_revision_url, $original_revision->toUrl()->toString());
|
||||
$this->drupalGet($original_revision_url);
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Contents should be in English, of correct revision.
|
||||
$this->assertText('First rev en title');
|
||||
$this->assertNoText('First rev fr title');
|
||||
|
||||
// Get a French view.
|
||||
$url_fr = $original_revision->getTranslation('fr')->toUrl('revision')->toString();
|
||||
|
||||
// Should have different URL from English.
|
||||
$this->assertNotIdentical($url_fr, $original_revision->toUrl()->toString());
|
||||
$this->assertNotIdentical($url_fr, $original_revision_url);
|
||||
$this->drupalGet($url_fr);
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Contents should be in French, of correct revision.
|
||||
$this->assertText('First rev fr title');
|
||||
$this->assertNoText('First rev en title');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that title is not escaped (but XSS-filtered) for details form element.
|
||||
*/
|
||||
public function testDetailsTitleIsNotEscaped() {
|
||||
$this->drupalLogin($this->administrator);
|
||||
// Make the image field a multi-value field in order to display a
|
||||
// details form element.
|
||||
$edit = ['cardinality_number' => 2];
|
||||
$this->drupalPostForm('admin/structure/types/manage/article/fields/node.article.field_image/storage', $edit, t('Save field settings'));
|
||||
|
||||
// Make the image field non-translatable.
|
||||
$edit = ['settings[node][article][fields][field_image]' => FALSE];
|
||||
$this->drupalPostForm('admin/config/regional/content-language', $edit, t('Save configuration'));
|
||||
|
||||
// Create a node.
|
||||
$nid = $this->createEntity(['title' => 'Node with multi-value image field en title'], 'en');
|
||||
|
||||
// Add a French translation and assert the title markup is not escaped.
|
||||
$this->drupalGet("node/$nid/translations/add/en/fr");
|
||||
$markup = 'Image <span class="translation-entity-all-languages">(all languages)</span>';
|
||||
$this->assertSession()->assertNoEscaped($markup);
|
||||
$this->assertSession()->responseContains($markup);
|
||||
}
|
||||
|
||||
}
|
|
@ -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.');
|
||||
}
|
||||
|
||||
}
|
262
2017/web/core/modules/node/tests/src/Functional/NodeTypeTest.php
Normal file
262
2017/web/core/modules/node/tests/src/Functional/NodeTypeTest.php
Normal file
|
@ -0,0 +1,262 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Tests\system\Functional\Menu\AssertBreadcrumbTrait;
|
||||
use Drupal\Tests\system\Functional\Cache\AssertPageCacheContextsAndTagsTrait;
|
||||
|
||||
/**
|
||||
* Ensures that node type functions work correctly.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeTypeTest extends NodeTestBase {
|
||||
|
||||
use AssertBreadcrumbTrait;
|
||||
use AssertPageCacheContextsAndTagsTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['field_ui', 'block'];
|
||||
|
||||
/**
|
||||
* Ensures that node type functions (node_type_get_*) work correctly.
|
||||
*
|
||||
* Load available node types and validate the returned data.
|
||||
*/
|
||||
public function testNodeTypeGetFunctions() {
|
||||
$node_types = NodeType::loadMultiple();
|
||||
$node_names = node_type_get_names();
|
||||
|
||||
$this->assertTrue(isset($node_types['article']), 'Node type article is available.');
|
||||
$this->assertTrue(isset($node_types['page']), 'Node type basic page is available.');
|
||||
|
||||
$this->assertEqual($node_types['article']->label(), $node_names['article'], 'Correct node type base has been returned.');
|
||||
|
||||
$article = NodeType::load('article');
|
||||
$this->assertEqual($node_types['article'], $article, 'Correct node type has been returned.');
|
||||
$this->assertEqual($node_types['article']->label(), $article->label(), 'Correct node type name has been returned.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests creating a content type programmatically and via a form.
|
||||
*/
|
||||
public function testNodeTypeCreation() {
|
||||
// Create a content type programmatically.
|
||||
$type = $this->drupalCreateContentType();
|
||||
|
||||
$type_exists = (bool) NodeType::load($type->id());
|
||||
$this->assertTrue($type_exists, 'The new content type has been created in the database.');
|
||||
|
||||
// Log in a test user.
|
||||
$web_user = $this->drupalCreateUser(['create ' . $type->label() . ' content']);
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
$this->drupalGet('node/add/' . $type->id());
|
||||
$this->assertResponse(200, 'The new content type can be accessed at node/add.');
|
||||
|
||||
// Create a content type via the user interface.
|
||||
$web_user = $this->drupalCreateUser(['bypass node access', 'administer content types']);
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
$this->drupalGet('node/add');
|
||||
$this->assertCacheTag('config:node_type_list');
|
||||
$this->assertCacheContext('user.permissions');
|
||||
$elements = $this->cssSelect('dl.node-type-list dt');
|
||||
$this->assertEqual(3, count($elements));
|
||||
|
||||
$edit = [
|
||||
'name' => 'foo',
|
||||
'title_label' => 'title for foo',
|
||||
'type' => 'foo',
|
||||
];
|
||||
$this->drupalPostForm('admin/structure/types/add', $edit, t('Save and manage fields'));
|
||||
$type_exists = (bool) NodeType::load('foo');
|
||||
$this->assertTrue($type_exists, 'The new content type has been created in the database.');
|
||||
|
||||
$this->drupalGet('node/add');
|
||||
$elements = $this->cssSelect('dl.node-type-list dt');
|
||||
$this->assertEqual(4, count($elements));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests editing a node type using the UI.
|
||||
*/
|
||||
public function testNodeTypeEditing() {
|
||||
$assert = $this->assertSession();
|
||||
$this->drupalPlaceBlock('system_breadcrumb_block');
|
||||
$web_user = $this->drupalCreateUser(['bypass node access', 'administer content types', 'administer node fields']);
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
$field = FieldConfig::loadByName('node', 'page', 'body');
|
||||
$this->assertEqual($field->getLabel(), 'Body', 'Body field was found.');
|
||||
|
||||
// Verify that title and body fields are displayed.
|
||||
$this->drupalGet('node/add/page');
|
||||
$assert->pageTextContains('Title');
|
||||
$assert->pageTextContains('Body');
|
||||
|
||||
// Rename the title field.
|
||||
$edit = [
|
||||
'title_label' => 'Foo',
|
||||
];
|
||||
$this->drupalPostForm('admin/structure/types/manage/page', $edit, t('Save content type'));
|
||||
|
||||
$this->drupalGet('node/add/page');
|
||||
$assert->pageTextContains('Foo');
|
||||
$assert->pageTextNotContains('Title');
|
||||
|
||||
// Change the name and the description.
|
||||
$edit = [
|
||||
'name' => 'Bar',
|
||||
'description' => 'Lorem ipsum.',
|
||||
];
|
||||
$this->drupalPostForm('admin/structure/types/manage/page', $edit, t('Save content type'));
|
||||
|
||||
$this->drupalGet('node/add');
|
||||
$assert->pageTextContains('Bar');
|
||||
$assert->pageTextContains('Lorem ipsum');
|
||||
$this->clickLink('Bar');
|
||||
$assert->pageTextContains('Foo');
|
||||
$assert->pageTextContains('Body');
|
||||
|
||||
// Change the name through the API
|
||||
/** @var \Drupal\node\NodeTypeInterface $node_type */
|
||||
$node_type = NodeType::load('page');
|
||||
$node_type->set('name', 'NewBar');
|
||||
$node_type->save();
|
||||
|
||||
/** @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface $bundle_info */
|
||||
$bundle_info = \Drupal::service('entity_type.bundle.info');
|
||||
$node_bundles = $bundle_info->getBundleInfo('node');
|
||||
$this->assertEqual($node_bundles['page']['label'], 'NewBar', 'Node type bundle cache is updated');
|
||||
|
||||
// Remove the body field.
|
||||
$this->drupalPostForm('admin/structure/types/manage/page/fields/node.page.body/delete', [], t('Delete'));
|
||||
// Resave the settings for this type.
|
||||
$this->drupalPostForm('admin/structure/types/manage/page', [], t('Save content type'));
|
||||
$front_page_path = Url::fromRoute('<front>')->toString();
|
||||
$this->assertBreadcrumb('admin/structure/types/manage/page/fields', [
|
||||
$front_page_path => 'Home',
|
||||
'admin/structure/types' => 'Content types',
|
||||
'admin/structure/types/manage/page' => 'NewBar',
|
||||
]);
|
||||
// Check that the body field doesn't exist.
|
||||
$this->drupalGet('node/add/page');
|
||||
$assert->pageTextNotContains('Body');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests deleting a content type that still has content.
|
||||
*/
|
||||
public function testNodeTypeDeletion() {
|
||||
$this->drupalPlaceBlock('page_title_block');
|
||||
// Create a content type programmatically.
|
||||
$type = $this->drupalCreateContentType();
|
||||
|
||||
// Log in a test user.
|
||||
$web_user = $this->drupalCreateUser([
|
||||
'bypass node access',
|
||||
'administer content types',
|
||||
]);
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
// Add a new node of this type.
|
||||
$node = $this->drupalCreateNode(['type' => $type->id()]);
|
||||
// Attempt to delete the content type, which should not be allowed.
|
||||
$this->drupalGet('admin/structure/types/manage/' . $type->label() . '/delete');
|
||||
$this->assertRaw(
|
||||
t('%type is used by 1 piece of content on your site. You can not remove this content type until you have removed all of the %type content.', ['%type' => $type->label()]),
|
||||
'The content type will not be deleted until all nodes of that type are removed.'
|
||||
);
|
||||
$this->assertNoText(t('This action cannot be undone.'), 'The node type deletion confirmation form is not available.');
|
||||
|
||||
// Delete the node.
|
||||
$node->delete();
|
||||
// Attempt to delete the content type, which should now be allowed.
|
||||
$this->drupalGet('admin/structure/types/manage/' . $type->label() . '/delete');
|
||||
$this->assertRaw(
|
||||
t('Are you sure you want to delete the content type %type?', ['%type' => $type->label()]),
|
||||
'The content type is available for deletion.'
|
||||
);
|
||||
$this->assertText(t('This action cannot be undone.'), 'The node type deletion confirmation form is available.');
|
||||
|
||||
// Test that a locked node type could not be deleted.
|
||||
$this->container->get('module_installer')->install(['node_test_config']);
|
||||
// Lock the default node type.
|
||||
$locked = \Drupal::state()->get('node.type.locked');
|
||||
$locked['default'] = 'default';
|
||||
\Drupal::state()->set('node.type.locked', $locked);
|
||||
// Call to flush all caches after installing the forum module in the same
|
||||
// way installing a module through the UI does.
|
||||
$this->resetAll();
|
||||
$this->drupalGet('admin/structure/types/manage/default');
|
||||
$this->assertNoLink(t('Delete'));
|
||||
$this->drupalGet('admin/structure/types/manage/default/delete');
|
||||
$this->assertResponse(403);
|
||||
$this->container->get('module_installer')->uninstall(['node_test_config']);
|
||||
$this->container = \Drupal::getContainer();
|
||||
unset($locked['default']);
|
||||
\Drupal::state()->set('node.type.locked', $locked);
|
||||
$this->drupalGet('admin/structure/types/manage/default');
|
||||
$this->clickLink(t('Delete'));
|
||||
$this->assertResponse(200);
|
||||
$this->drupalPostForm(NULL, [], t('Delete'));
|
||||
$this->assertFalse((bool) NodeType::load('default'), 'Node type with machine default deleted.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Field UI integration for content types.
|
||||
*/
|
||||
public function testNodeTypeFieldUiPermissions() {
|
||||
// Create an admin user who can only manage node fields.
|
||||
$admin_user_1 = $this->drupalCreateUser(['administer content types', 'administer node fields']);
|
||||
$this->drupalLogin($admin_user_1);
|
||||
|
||||
// Test that the user only sees the actions available to him.
|
||||
$this->drupalGet('admin/structure/types');
|
||||
$this->assertLinkByHref('admin/structure/types/manage/article/fields');
|
||||
$this->assertNoLinkByHref('admin/structure/types/manage/article/display');
|
||||
|
||||
// Create another admin user who can manage node fields display.
|
||||
$admin_user_2 = $this->drupalCreateUser(['administer content types', 'administer node display']);
|
||||
$this->drupalLogin($admin_user_2);
|
||||
|
||||
// Test that the user only sees the actions available to him.
|
||||
$this->drupalGet('admin/structure/types');
|
||||
$this->assertNoLinkByHref('admin/structure/types/manage/article/fields');
|
||||
$this->assertLinkByHref('admin/structure/types/manage/article/display');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for when there are no content types defined.
|
||||
*/
|
||||
public function testNodeTypeNoContentType() {
|
||||
/** @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface $bundle_info */
|
||||
$bundle_info = \Drupal::service('entity_type.bundle.info');
|
||||
$this->assertEqual(2, count($bundle_info->getBundleInfo('node')), 'The bundle information service has 2 bundles for the Node entity type.');
|
||||
$web_user = $this->drupalCreateUser(['administer content types']);
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
// Delete 'article' bundle.
|
||||
$this->drupalPostForm('admin/structure/types/manage/article/delete', [], t('Delete'));
|
||||
// Delete 'page' bundle.
|
||||
$this->drupalPostForm('admin/structure/types/manage/page/delete', [], t('Delete'));
|
||||
|
||||
// Navigate to content type administration screen
|
||||
$this->drupalGet('admin/structure/types');
|
||||
$this->assertRaw(t('No content types available. <a href=":link">Add content type</a>.', [
|
||||
':link' => Url::fromRoute('node.type_add')->toString(),
|
||||
]), 'Empty text when there are no content types in the system is correct.');
|
||||
|
||||
$bundle_info->clearCachedBundles();
|
||||
$this->assertEqual(0, count($bundle_info->getBundleInfo('node')), 'The bundle information service has 0 bundles for the Node entity type.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,180 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
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;
|
||||
// Create an empty po file so we don't attempt to download one from
|
||||
// localize.drupal.org. It does not need to match the version exactly as the
|
||||
// multi-lingual system will fallback.
|
||||
\Drupal::service('file_system')->mkdir($this->publicFilesDirectory . '/translations', NULL, TRUE);
|
||||
file_put_contents($this->publicFilesDirectory . "/translations/drupal-8.0.0.{$this->defaultLangcode}.po", '');
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the node type translation.
|
||||
*/
|
||||
public function testNodeTypeTranslation() {
|
||||
$type = mb_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 = mb_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 = mb_strtolower($this->randomMachineName(16));
|
||||
$name = $this->randomString();
|
||||
$this->drupalCreateContentType(['type' => $type, 'name' => $name]);
|
||||
|
||||
// Set tabs.
|
||||
$this->drupalPlaceBlock('local_tasks_block', ['primary' => TRUE]);
|
||||
|
||||
// Change default language.
|
||||
$this->drupalPostForm('admin/config/regional/language', ['site_default_language' => 'es'], 'Save configuration');
|
||||
|
||||
// Try re-using the email field.
|
||||
$this->drupalGet("es/admin/structure/types/manage/$type/fields/add-field");
|
||||
$this->drupalPostForm(NULL, ['existing_storage_name' => 'field_email', 'existing_storage_label' => 'Email'], 'Save and continue');
|
||||
$this->assertResponse(200);
|
||||
$this->drupalGet("es/admin/structure/types/manage/$type/fields/node.$type.field_email/translate");
|
||||
$this->assertResponse(200);
|
||||
$this->assertText("The configuration objects have different language codes so they cannot be translated");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
|
||||
/**
|
||||
* Tests the node language extra field display.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeViewLanguageTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node', 'datetime', 'language'];
|
||||
|
||||
/**
|
||||
* Tests the language extra field display.
|
||||
*/
|
||||
public function testViewLanguage() {
|
||||
// Add Spanish language.
|
||||
ConfigurableLanguage::createFromLangcode('es')->save();
|
||||
|
||||
// Set language field visible.
|
||||
entity_get_display('node', 'page', 'full')
|
||||
->setComponent('langcode')
|
||||
->save();
|
||||
|
||||
// Create a node in Spanish.
|
||||
$node = $this->drupalCreateNode(['langcode' => 'es']);
|
||||
|
||||
$this->drupalGet($node->urlInfo());
|
||||
$this->assertText('Spanish', 'The language field is displayed properly.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
|
||||
/**
|
||||
* Tests the node/{node} page.
|
||||
*
|
||||
* @group node
|
||||
* @see \Drupal\node\Controller\NodeController
|
||||
*/
|
||||
class NodeViewTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Tests the html head links.
|
||||
*/
|
||||
public function testHtmlHeadLinks() {
|
||||
$node = $this->drupalCreateNode();
|
||||
|
||||
$this->drupalGet($node->urlInfo());
|
||||
|
||||
$result = $this->xpath('//link[@rel = "canonical"]');
|
||||
$this->assertEqual($result[0]->getAttribute('href'), $node->url());
|
||||
|
||||
// Link relations are checked for access for anonymous users.
|
||||
$result = $this->xpath('//link[@rel = "version-history"]');
|
||||
$this->assertFalse($result, 'Version history not present for anonymous users without access.');
|
||||
|
||||
$result = $this->xpath('//link[@rel = "edit-form"]');
|
||||
$this->assertFalse($result, 'Edit form not present for anonymous users without access.');
|
||||
|
||||
$this->drupalLogin($this->createUser(['access content']));
|
||||
$this->drupalGet($node->urlInfo());
|
||||
|
||||
$result = $this->xpath('//link[@rel = "canonical"]');
|
||||
$this->assertEqual($result[0]->getAttribute('href'), $node->url());
|
||||
|
||||
// Link relations are present regardless of access for authenticated users.
|
||||
$result = $this->xpath('//link[@rel = "version-history"]');
|
||||
$this->assertEqual($result[0]->getAttribute('href'), $node->url('version-history'));
|
||||
|
||||
$result = $this->xpath('//link[@rel = "edit-form"]');
|
||||
$this->assertEqual($result[0]->getAttribute('href'), $node->url('edit-form'));
|
||||
|
||||
// Give anonymous users access to edit the node. Do this through the UI to
|
||||
// ensure caches are handled properly.
|
||||
$this->drupalLogin($this->rootUser);
|
||||
$edit = [
|
||||
'anonymous[edit own ' . $node->bundle() . ' content]' => TRUE,
|
||||
];
|
||||
$this->drupalPostForm('admin/people/permissions', $edit, 'Save permissions');
|
||||
$this->drupalLogout();
|
||||
|
||||
// Anonymous user's should now see the edit-form link but not the
|
||||
// version-history link.
|
||||
$this->drupalGet($node->urlInfo());
|
||||
$result = $this->xpath('//link[@rel = "canonical"]');
|
||||
$this->assertEqual($result[0]->getAttribute('href'), $node->url());
|
||||
|
||||
$result = $this->xpath('//link[@rel = "version-history"]');
|
||||
$this->assertFalse($result, 'Version history not present for anonymous users without access.');
|
||||
|
||||
$result = $this->xpath('//link[@rel = "edit-form"]');
|
||||
$this->assertEqual($result[0]->getAttribute('href'), $node->url('edit-form'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the Link header.
|
||||
*/
|
||||
public function testLinkHeader() {
|
||||
$node = $this->drupalCreateNode();
|
||||
|
||||
$expected = [
|
||||
'<' . Html::escape($node->url('canonical')) . '>; rel="canonical"',
|
||||
'<' . Html::escape($node->url('canonical'), ['alias' => TRUE]) . '>; rel="shortlink"',
|
||||
'<' . Html::escape($node->url('revision')) . '>; rel="revision"',
|
||||
];
|
||||
|
||||
$this->drupalGet($node->urlInfo());
|
||||
|
||||
$links = $this->drupalGetHeaders()['Link'];
|
||||
$this->assertEqual($links, $expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we store and retrieve multi-byte UTF-8 characters correctly.
|
||||
*/
|
||||
public function testMultiByteUtf8() {
|
||||
$title = '🐝';
|
||||
$this->assertTrue(mb_strlen($title, 'utf-8') < strlen($title), 'Title has multi-byte characters.');
|
||||
$node = $this->drupalCreateNode(['title' => $title]);
|
||||
$this->drupalGet($node->urlInfo());
|
||||
$result = $this->xpath('//span[contains(@class, "field--name-title")]');
|
||||
$this->assertEqual($result[0]->getText(), $title, 'The passed title was returned.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,480 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional;
|
||||
|
||||
use Drupal\comment\Tests\CommentTestTrait;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\taxonomy\Entity\Term;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
use Drupal\Tests\field\Traits\EntityReferenceTestTrait;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests the node entity preview functionality.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class PagePreviewTest extends NodeTestBase {
|
||||
|
||||
use EntityReferenceTestTrait;
|
||||
use CommentTestTrait;
|
||||
use TestFileCreationTrait {
|
||||
getTestFiles as drupalGetTestFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable the comment, node and taxonomy modules to test on the preview.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node', 'taxonomy', 'comment', 'image', 'file', 'text', 'node_test', 'menu_ui'];
|
||||
|
||||
/**
|
||||
* The name of the created field.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->addDefaultCommentField('node', 'page');
|
||||
|
||||
$web_user = $this->drupalCreateUser(['edit own page content', 'create page content', 'administer menu']);
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
// Add a vocabulary so we can test different view modes.
|
||||
$vocabulary = Vocabulary::create([
|
||||
'name' => $this->randomMachineName(),
|
||||
'description' => $this->randomMachineName(),
|
||||
'vid' => $this->randomMachineName(),
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
'help' => '',
|
||||
]);
|
||||
$vocabulary->save();
|
||||
|
||||
$this->vocabulary = $vocabulary;
|
||||
|
||||
// Add a term to the vocabulary.
|
||||
$term = Term::create([
|
||||
'name' => $this->randomMachineName(),
|
||||
'description' => $this->randomMachineName(),
|
||||
'vid' => $this->vocabulary->id(),
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
]);
|
||||
$term->save();
|
||||
|
||||
$this->term = $term;
|
||||
|
||||
// Create an image field.
|
||||
FieldStorageConfig::create([
|
||||
'field_name' => 'field_image',
|
||||
'entity_type' => 'node',
|
||||
'type' => 'image',
|
||||
'settings' => [],
|
||||
'cardinality' => FieldStorageConfig::CARDINALITY_UNLIMITED,
|
||||
])->save();
|
||||
|
||||
$field_config = FieldConfig::create([
|
||||
'field_name' => 'field_image',
|
||||
'label' => 'Images',
|
||||
'entity_type' => 'node',
|
||||
'bundle' => 'page',
|
||||
'required' => FALSE,
|
||||
'settings' => [],
|
||||
]);
|
||||
$field_config->save();
|
||||
|
||||
// Create a field.
|
||||
$this->fieldName = mb_strtolower($this->randomMachineName());
|
||||
$handler_settings = [
|
||||
'target_bundles' => [
|
||||
$this->vocabulary->id() => $this->vocabulary->id(),
|
||||
],
|
||||
'auto_create' => TRUE,
|
||||
];
|
||||
$this->createEntityReferenceField('node', 'page', $this->fieldName, 'Tags', 'taxonomy_term', 'default', $handler_settings, FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
|
||||
|
||||
entity_get_form_display('node', 'page', 'default')
|
||||
->setComponent($this->fieldName, [
|
||||
'type' => 'entity_reference_autocomplete_tags',
|
||||
])
|
||||
->save();
|
||||
|
||||
// Show on default display and teaser.
|
||||
entity_get_display('node', 'page', 'default')
|
||||
->setComponent($this->fieldName, [
|
||||
'type' => 'entity_reference_label',
|
||||
])
|
||||
->save();
|
||||
entity_get_display('node', 'page', 'teaser')
|
||||
->setComponent($this->fieldName, [
|
||||
'type' => 'entity_reference_label',
|
||||
])
|
||||
->save();
|
||||
|
||||
entity_get_form_display('node', 'page', 'default')
|
||||
->setComponent('field_image', [
|
||||
'type' => 'image_image',
|
||||
'settings' => [],
|
||||
])
|
||||
->save();
|
||||
|
||||
entity_get_display('node', 'page', 'default')
|
||||
->setComponent('field_image')
|
||||
->save();
|
||||
|
||||
// Create a multi-value text field.
|
||||
$field_storage = FieldStorageConfig::create([
|
||||
'field_name' => 'field_test_multi',
|
||||
'entity_type' => 'node',
|
||||
'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
|
||||
'type' => 'text',
|
||||
'settings' => [
|
||||
'max_length' => 50,
|
||||
],
|
||||
]);
|
||||
$field_storage->save();
|
||||
FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'page',
|
||||
])->save();
|
||||
|
||||
entity_get_form_display('node', 'page', 'default')
|
||||
->setComponent('field_test_multi', [
|
||||
'type' => 'text_textfield',
|
||||
])
|
||||
->save();
|
||||
|
||||
entity_get_display('node', 'page', 'default')
|
||||
->setComponent('field_test_multi', [
|
||||
'type' => 'string',
|
||||
])
|
||||
->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the node preview functionality.
|
||||
*/
|
||||
public function testPagePreview() {
|
||||
$title_key = 'title[0][value]';
|
||||
$body_key = 'body[0][value]';
|
||||
$term_key = $this->fieldName . '[target_id]';
|
||||
|
||||
// Fill in node creation form and preview node.
|
||||
$edit = [];
|
||||
$edit[$title_key] = '<em>' . $this->randomMachineName(8) . '</em>';
|
||||
$edit[$body_key] = $this->randomMachineName(16);
|
||||
$edit[$term_key] = $this->term->getName();
|
||||
|
||||
// Upload an image.
|
||||
$test_image = current($this->drupalGetTestFiles('image', 39325));
|
||||
$edit['files[field_image_0][]'] = \Drupal::service('file_system')->realpath($test_image->uri);
|
||||
$this->drupalPostForm('node/add/page', $edit, t('Upload'));
|
||||
|
||||
// Add an alt tag and preview the node.
|
||||
$this->drupalPostForm(NULL, ['field_image[0][alt]' => 'Picture of llamas'], t('Preview'));
|
||||
|
||||
// Check that the preview is displaying the title, body and term.
|
||||
$expected_title = $edit[$title_key] . ' | Drupal';
|
||||
$this->assertSession()->titleEquals($expected_title);
|
||||
$this->assertEscaped($edit[$title_key], 'Title displayed and escaped.');
|
||||
$this->assertText($edit[$body_key], 'Body displayed.');
|
||||
$this->assertText($edit[$term_key], 'Term displayed.');
|
||||
$this->assertLink(t('Back to content editing'));
|
||||
|
||||
// Check that we see the class of the node type on the body element.
|
||||
$body_class_element = $this->xpath("//body[contains(@class, 'page-node-type-page')]");
|
||||
$this->assertTrue(!empty($body_class_element), 'Node type body class found.');
|
||||
|
||||
// Get the UUID.
|
||||
$url = parse_url($this->getUrl());
|
||||
$paths = explode('/', $url['path']);
|
||||
$view_mode = array_pop($paths);
|
||||
$uuid = array_pop($paths);
|
||||
|
||||
// Switch view mode. We'll remove the body from the teaser view mode.
|
||||
entity_get_display('node', 'page', 'teaser')
|
||||
->removeComponent('body')
|
||||
->save();
|
||||
|
||||
$view_mode_edit = ['view_mode' => 'teaser'];
|
||||
$this->drupalPostForm('node/preview/' . $uuid . '/full', $view_mode_edit, t('Switch'));
|
||||
$this->assertRaw('view-mode-teaser', 'View mode teaser class found.');
|
||||
$this->assertNoText($edit[$body_key], 'Body not displayed.');
|
||||
|
||||
// Check that the title, body and term fields are displayed with the
|
||||
// values after going back to the content edit page.
|
||||
$this->clickLink(t('Back to content editing'));
|
||||
$this->assertFieldByName($title_key, $edit[$title_key], 'Title field displayed.');
|
||||
$this->assertFieldByName($body_key, $edit[$body_key], 'Body field displayed.');
|
||||
$this->assertFieldByName($term_key, $edit[$term_key], 'Term field displayed.');
|
||||
$this->assertFieldByName('field_image[0][alt]', 'Picture of llamas');
|
||||
$this->getSession()->getPage()->pressButton('Add another item');
|
||||
$this->assertFieldByName('field_test_multi[0][value]');
|
||||
$this->assertFieldByName('field_test_multi[1][value]');
|
||||
|
||||
// Return to page preview to check everything is as expected.
|
||||
$this->drupalPostForm(NULL, [], t('Preview'));
|
||||
$this->assertSession()->titleEquals($expected_title);
|
||||
$this->assertEscaped($edit[$title_key], 'Title displayed and escaped.');
|
||||
$this->assertText($edit[$body_key], 'Body displayed.');
|
||||
$this->assertText($edit[$term_key], 'Term displayed.');
|
||||
$this->assertLink(t('Back to content editing'));
|
||||
|
||||
// Assert the content is kept when reloading the page.
|
||||
$this->drupalGet('node/add/page', ['query' => ['uuid' => $uuid]]);
|
||||
$this->assertFieldByName($title_key, $edit[$title_key], 'Title field displayed.');
|
||||
$this->assertFieldByName($body_key, $edit[$body_key], 'Body field displayed.');
|
||||
$this->assertFieldByName($term_key, $edit[$term_key], 'Term field displayed.');
|
||||
|
||||
// Save the node - this is a new POST, so we need to upload the image.
|
||||
$this->drupalPostForm('node/add/page', $edit, t('Upload'));
|
||||
$this->drupalPostForm(NULL, ['field_image[0][alt]' => 'Picture of llamas'], t('Save'));
|
||||
$node = $this->drupalGetNodeByTitle($edit[$title_key]);
|
||||
|
||||
// Check the term was displayed on the saved node.
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$this->assertText($edit[$term_key], 'Term displayed.');
|
||||
|
||||
// Check the term appears again on the edit form.
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$this->assertFieldByName($term_key, $edit[$term_key] . ' (' . $this->term->id() . ')', 'Term field displayed.');
|
||||
|
||||
// Check with two new terms on the edit form, additionally to the existing
|
||||
// one.
|
||||
$edit = [];
|
||||
$newterm1 = $this->randomMachineName(8);
|
||||
$newterm2 = $this->randomMachineName(8);
|
||||
$edit[$term_key] = $this->term->getName() . ', ' . $newterm1 . ', ' . $newterm2;
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Preview'));
|
||||
$this->assertRaw('>' . $newterm1 . '<', 'First new term displayed.');
|
||||
$this->assertRaw('>' . $newterm2 . '<', 'Second new term displayed.');
|
||||
// The first term should be displayed as link, the others not.
|
||||
$this->assertLink($this->term->getName());
|
||||
$this->assertNoLink($newterm1);
|
||||
$this->assertNoLink($newterm2);
|
||||
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
|
||||
|
||||
// Check with one more new term, keeping old terms, removing the existing
|
||||
// one.
|
||||
$edit = [];
|
||||
$newterm3 = $this->randomMachineName(8);
|
||||
$edit[$term_key] = $newterm1 . ', ' . $newterm3 . ', ' . $newterm2;
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Preview'));
|
||||
$this->assertRaw('>' . $newterm1 . '<', 'First existing term displayed.');
|
||||
$this->assertRaw('>' . $newterm2 . '<', 'Second existing term displayed.');
|
||||
$this->assertRaw('>' . $newterm3 . '<', 'Third new term displayed.');
|
||||
$this->assertNoText($this->term->getName());
|
||||
$this->assertLink($newterm1);
|
||||
$this->assertLink($newterm2);
|
||||
$this->assertNoLink($newterm3);
|
||||
|
||||
// Check that editing an existing node after it has been previewed and not
|
||||
// saved doesn't remember the previous changes.
|
||||
$edit = [
|
||||
$title_key => $this->randomMachineName(8),
|
||||
];
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Preview'));
|
||||
$this->assertText($edit[$title_key], 'New title displayed.');
|
||||
$this->clickLink(t('Back to content editing'));
|
||||
$this->assertFieldByName($title_key, $edit[$title_key], 'New title value displayed.');
|
||||
// Navigate away from the node without saving.
|
||||
$this->drupalGet('<front>');
|
||||
// Go back to the edit form, the title should have its initial value.
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$this->assertFieldByName($title_key, $node->label(), 'Correct title value displayed.');
|
||||
|
||||
// Check with required preview.
|
||||
$node_type = NodeType::load('page');
|
||||
$node_type->setPreviewMode(DRUPAL_REQUIRED);
|
||||
$node_type->save();
|
||||
$this->drupalGet('node/add/page');
|
||||
$this->assertNoRaw('edit-submit');
|
||||
$this->drupalPostForm('node/add/page', [$title_key => 'Preview'], t('Preview'));
|
||||
$this->clickLink(t('Back to content editing'));
|
||||
$this->assertRaw('edit-submit');
|
||||
|
||||
// Check that destination is remembered when clicking on preview. When going
|
||||
// back to the edit form and clicking save, we should go back to the
|
||||
// original destination, if set.
|
||||
$destination = 'node';
|
||||
$this->drupalPostForm($node->toUrl('edit-form'), [], t('Preview'), ['query' => ['destination' => $destination]]);
|
||||
$parameters = ['node_preview' => $node->uuid(), 'view_mode_id' => 'full'];
|
||||
$options = ['absolute' => TRUE, 'query' => ['destination' => $destination]];
|
||||
$this->assertUrl(Url::fromRoute('entity.node.preview', $parameters, $options));
|
||||
$this->drupalPostForm(NULL, ['view_mode' => 'teaser'], t('Switch'));
|
||||
$this->clickLink(t('Back to content editing'));
|
||||
$this->drupalPostForm(NULL, [], t('Save'));
|
||||
$this->assertUrl($destination);
|
||||
|
||||
// Check that preview page works as expected without a destination set.
|
||||
$this->drupalPostForm($node->toUrl('edit-form'), [], t('Preview'));
|
||||
$parameters = ['node_preview' => $node->uuid(), 'view_mode_id' => 'full'];
|
||||
$this->assertUrl(Url::fromRoute('entity.node.preview', $parameters, ['absolute' => TRUE]));
|
||||
$this->drupalPostForm(NULL, ['view_mode' => 'teaser'], t('Switch'));
|
||||
$this->clickLink(t('Back to content editing'));
|
||||
$this->drupalPostForm(NULL, [], t('Save'));
|
||||
$this->assertUrl($node->toUrl());
|
||||
$this->assertResponse(200);
|
||||
|
||||
/** @var \Drupal\Core\File\FileSystemInterface $file_system */
|
||||
$file_system = \Drupal::service('file_system');
|
||||
// Assert multiple items can be added and are not lost when previewing.
|
||||
$test_image_1 = current($this->drupalGetTestFiles('image', 39325));
|
||||
$edit_image_1['files[field_image_0][]'] = $file_system->realpath($test_image_1->uri);
|
||||
$test_image_2 = current($this->drupalGetTestFiles('image', 39325));
|
||||
$edit_image_2['files[field_image_1][]'] = $file_system->realpath($test_image_2->uri);
|
||||
$edit['field_image[0][alt]'] = 'Alt 1';
|
||||
|
||||
$this->drupalPostForm('node/add/page', $edit_image_1, t('Upload'));
|
||||
$this->drupalPostForm(NULL, $edit, t('Preview'));
|
||||
$this->clickLink(t('Back to content editing'));
|
||||
$this->assertFieldByName('files[field_image_1][]');
|
||||
$this->drupalPostForm(NULL, $edit_image_2, t('Upload'));
|
||||
$this->assertNoFieldByName('files[field_image_1][]');
|
||||
|
||||
$title = 'node_test_title';
|
||||
$example_text_1 = 'example_text_preview_1';
|
||||
$example_text_2 = 'example_text_preview_2';
|
||||
$example_text_3 = 'example_text_preview_3';
|
||||
$this->drupalGet('node/add/page');
|
||||
$edit = [
|
||||
'title[0][value]' => $title,
|
||||
'field_test_multi[0][value]' => $example_text_1,
|
||||
];
|
||||
$this->assertRaw('Storage is not set');
|
||||
$this->drupalPostForm(NULL, $edit, t('Preview'));
|
||||
$this->clickLink(t('Back to content editing'));
|
||||
$this->assertRaw('Storage is set');
|
||||
$this->assertFieldByName('field_test_multi[0][value]');
|
||||
$this->drupalPostForm(NULL, [], t('Save'));
|
||||
$this->assertText('Basic page ' . $title . ' has been created.');
|
||||
$node = $this->drupalGetNodeByTitle($title);
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$this->getSession()->getPage()->pressButton('Add another item');
|
||||
$this->getSession()->getPage()->pressButton('Add another item');
|
||||
$edit = [
|
||||
'field_test_multi[1][value]' => $example_text_2,
|
||||
'field_test_multi[2][value]' => $example_text_3,
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Preview'));
|
||||
$this->clickLink(t('Back to content editing'));
|
||||
$this->drupalPostForm(NULL, $edit, t('Preview'));
|
||||
$this->clickLink(t('Back to content editing'));
|
||||
$this->assertFieldByName('field_test_multi[0][value]', $example_text_1);
|
||||
$this->assertFieldByName('field_test_multi[1][value]', $example_text_2);
|
||||
$this->assertFieldByName('field_test_multi[2][value]', $example_text_3);
|
||||
|
||||
// Now save the node and make sure all values got saved.
|
||||
$this->drupalPostForm(NULL, [], t('Save'));
|
||||
$this->assertText($example_text_1);
|
||||
$this->assertText($example_text_2);
|
||||
$this->assertText($example_text_3);
|
||||
|
||||
// Edit again, change the menu_ui settings and click on preview.
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$edit = [
|
||||
'menu[enabled]' => TRUE,
|
||||
'menu[title]' => 'Changed title',
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Preview'));
|
||||
$this->clickLink(t('Back to content editing'));
|
||||
$this->assertFieldChecked('edit-menu-enabled', 'Menu option is still checked');
|
||||
$this->assertFieldByName('menu[title]', 'Changed title', 'Menu link title is correct after preview');
|
||||
|
||||
// Save, change the title while saving and make sure that it is correctly
|
||||
// saved.
|
||||
$edit = [
|
||||
'menu[enabled]' => TRUE,
|
||||
'menu[title]' => 'Second title change',
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$this->assertFieldByName('menu[title]', 'Second title change', 'Menu link title is correct after saving');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the node preview functionality, when using revisions.
|
||||
*/
|
||||
public function testPagePreviewWithRevisions() {
|
||||
$title_key = 'title[0][value]';
|
||||
$body_key = 'body[0][value]';
|
||||
$term_key = $this->fieldName . '[target_id]';
|
||||
// Force revision on "Basic page" content.
|
||||
$node_type = NodeType::load('page');
|
||||
$node_type->setNewRevision(TRUE);
|
||||
$node_type->save();
|
||||
|
||||
// Fill in node creation form and preview node.
|
||||
$edit = [];
|
||||
$edit[$title_key] = $this->randomMachineName(8);
|
||||
$edit[$body_key] = $this->randomMachineName(16);
|
||||
$edit[$term_key] = $this->term->id();
|
||||
$edit['revision_log[0][value]'] = $this->randomString(32);
|
||||
$this->drupalPostForm('node/add/page', $edit, t('Preview'));
|
||||
|
||||
// Check that the preview is displaying the title, body and term.
|
||||
$this->assertTitle(t('@title | Drupal', ['@title' => $edit[$title_key]]), 'Basic page title is preview.');
|
||||
$this->assertText($edit[$title_key], 'Title displayed.');
|
||||
$this->assertText($edit[$body_key], 'Body displayed.');
|
||||
$this->assertText($edit[$term_key], 'Term displayed.');
|
||||
|
||||
// Check that the title and body fields are displayed with the correct
|
||||
// values after going back to the content edit page.
|
||||
$this->clickLink(t('Back to content editing')); $this->assertFieldByName($title_key, $edit[$title_key], 'Title field displayed.');
|
||||
$this->assertFieldByName($body_key, $edit[$body_key], 'Body field displayed.');
|
||||
$this->assertFieldByName($term_key, $edit[$term_key], 'Term field displayed.');
|
||||
|
||||
// Check that the revision log field has the correct value.
|
||||
$this->assertFieldByName('revision_log[0][value]', $edit['revision_log[0][value]'], 'Revision log field displayed.');
|
||||
|
||||
// Save the node after coming back from the preview page so we can create a
|
||||
// pending revision for it.
|
||||
$this->drupalPostForm(NULL, [], t('Save'));
|
||||
$node = $this->drupalGetNodeByTitle($edit[$title_key]);
|
||||
|
||||
// Check that previewing a pending revision of a node works. This can not be
|
||||
// accomplished through the UI so we have to use API calls.
|
||||
// @todo Change this test to use the UI when we will be able to create
|
||||
// pending revisions in core.
|
||||
// @see https://www.drupal.org/node/2725533
|
||||
$node->setNewRevision(TRUE);
|
||||
$node->isDefaultRevision(FALSE);
|
||||
|
||||
/** @var \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver */
|
||||
$controller_resolver = \Drupal::service('controller_resolver');
|
||||
$node_preview_controller = $controller_resolver->getControllerFromDefinition('\Drupal\node\Controller\NodePreviewController::view');
|
||||
$node_preview_controller($node, 'full');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the node preview accessible for simultaneous node editing.
|
||||
*/
|
||||
public function testSimultaneousPreview() {
|
||||
$title_key = 'title[0][value]';
|
||||
$node = $this->drupalCreateNode([]);
|
||||
|
||||
$edit = [$title_key => 'New page title'];
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Preview'));
|
||||
$this->assertText($edit[$title_key]);
|
||||
|
||||
$user2 = $this->drupalCreateUser(['edit any page content']);
|
||||
$this->drupalLogin($user2);
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$this->assertFieldByName($title_key, $node->label(), 'No title leaked from previous user.');
|
||||
|
||||
$edit2 = [$title_key => 'Another page title'];
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit2, t('Preview'));
|
||||
$this->assertUrl(\Drupal::url('entity.node.preview', ['node_preview' => $node->uuid(), 'view_mode_id' => 'full'], ['absolute' => TRUE]));
|
||||
$this->assertText($edit2[$title_key]);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
<?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,24 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class NodeJsonAnonTest extends NodeResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class NodeJsonBasicAuthTest extends NodeResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class NodeJsonCookieTest extends NodeResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
|
@ -0,0 +1,264 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional\Rest;
|
||||
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
use Drupal\user\Entity\User;
|
||||
use GuzzleHttp\RequestOptions;
|
||||
|
||||
abstract class NodeResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
use BcTimestampNormalizerUnixTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['node', 'path'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $entityTypeId = 'node';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $patchProtectedFieldNames = [
|
||||
'revision_timestamp' => NULL,
|
||||
'revision_uid' => NULL,
|
||||
'created' => "The 'administer nodes' permission is required.",
|
||||
'changed' => NULL,
|
||||
'promote' => "The 'administer nodes' permission is required.",
|
||||
'sticky' => "The 'administer nodes' permission is required.",
|
||||
'path' => "The following permissions are required: 'create url aliases' OR 'administer url aliases'.",
|
||||
];
|
||||
|
||||
/**
|
||||
* @var \Drupal\node\NodeInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method) {
|
||||
switch ($method) {
|
||||
case 'GET':
|
||||
$this->grantPermissionsToTestedRole(['access content']);
|
||||
break;
|
||||
case 'POST':
|
||||
$this->grantPermissionsToTestedRole(['access content', 'create camelids content']);
|
||||
break;
|
||||
case 'PATCH':
|
||||
// Do not grant the 'create url aliases' permission to test the case
|
||||
// when the path field is protected/not accessible, see
|
||||
// \Drupal\Tests\rest\Functional\EntityResource\Term\TermResourceTestBase
|
||||
// for a positive test.
|
||||
$this->grantPermissionsToTestedRole(['access content', 'edit any camelids content']);
|
||||
break;
|
||||
case 'DELETE':
|
||||
$this->grantPermissionsToTestedRole(['access content', 'delete any camelids content']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
if (!NodeType::load('camelids')) {
|
||||
// Create a "Camelids" node type.
|
||||
NodeType::create([
|
||||
'name' => 'Camelids',
|
||||
'type' => 'camelids',
|
||||
])->save();
|
||||
}
|
||||
|
||||
// Create a "Llama" node.
|
||||
$node = Node::create(['type' => 'camelids']);
|
||||
$node->setTitle('Llama')
|
||||
->setOwnerId(static::$auth ? $this->account->id() : 0)
|
||||
->setPublished()
|
||||
->setCreatedTime(123456789)
|
||||
->setChangedTime(123456789)
|
||||
->setRevisionCreationTime(123456789)
|
||||
->set('path', '/llama')
|
||||
->save();
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedNormalizedEntity() {
|
||||
$author = User::load($this->entity->getOwnerId());
|
||||
return [
|
||||
'nid' => [
|
||||
['value' => 1],
|
||||
],
|
||||
'uuid' => [
|
||||
['value' => $this->entity->uuid()],
|
||||
],
|
||||
'vid' => [
|
||||
['value' => 1],
|
||||
],
|
||||
'langcode' => [
|
||||
[
|
||||
'value' => 'en',
|
||||
],
|
||||
],
|
||||
'type' => [
|
||||
[
|
||||
'target_id' => 'camelids',
|
||||
'target_type' => 'node_type',
|
||||
'target_uuid' => NodeType::load('camelids')->uuid(),
|
||||
],
|
||||
],
|
||||
'title' => [
|
||||
[
|
||||
'value' => 'Llama',
|
||||
],
|
||||
],
|
||||
'status' => [
|
||||
[
|
||||
'value' => TRUE,
|
||||
],
|
||||
],
|
||||
'created' => [
|
||||
$this->formatExpectedTimestampItemValues(123456789),
|
||||
],
|
||||
'changed' => [
|
||||
$this->formatExpectedTimestampItemValues($this->entity->getChangedTime()),
|
||||
],
|
||||
'promote' => [
|
||||
[
|
||||
'value' => TRUE,
|
||||
],
|
||||
],
|
||||
'sticky' => [
|
||||
[
|
||||
'value' => FALSE,
|
||||
],
|
||||
],
|
||||
'revision_timestamp' => [
|
||||
$this->formatExpectedTimestampItemValues(123456789),
|
||||
],
|
||||
'revision_translation_affected' => [
|
||||
[
|
||||
'value' => TRUE,
|
||||
],
|
||||
],
|
||||
'default_langcode' => [
|
||||
[
|
||||
'value' => TRUE,
|
||||
],
|
||||
],
|
||||
'uid' => [
|
||||
[
|
||||
'target_id' => (int) $author->id(),
|
||||
'target_type' => 'user',
|
||||
'target_uuid' => $author->uuid(),
|
||||
'url' => base_path() . 'user/' . $author->id(),
|
||||
],
|
||||
],
|
||||
'revision_uid' => [
|
||||
[
|
||||
'target_id' => (int) $author->id(),
|
||||
'target_type' => 'user',
|
||||
'target_uuid' => $author->uuid(),
|
||||
'url' => base_path() . 'user/' . $author->id(),
|
||||
],
|
||||
],
|
||||
'revision_log' => [],
|
||||
'path' => [
|
||||
[
|
||||
'alias' => '/llama',
|
||||
'pid' => 1,
|
||||
'langcode' => 'en',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
return [
|
||||
'type' => [
|
||||
[
|
||||
'target_id' => 'camelids',
|
||||
],
|
||||
],
|
||||
'title' => [
|
||||
[
|
||||
'value' => 'Dramallama',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedUnauthorizedAccessMessage($method) {
|
||||
if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
|
||||
return parent::getExpectedUnauthorizedAccessMessage($method);
|
||||
}
|
||||
|
||||
if ($method === 'GET' || $method == 'PATCH' || $method == 'DELETE') {
|
||||
return "The 'access content' permission is required.";
|
||||
}
|
||||
return parent::getExpectedUnauthorizedAccessMessage($method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests PATCHing a node's path with and without 'create url aliases'.
|
||||
*
|
||||
* For a positive test, see the similar test coverage for Term.
|
||||
*
|
||||
* @see \Drupal\Tests\rest\Functional\EntityResource\Term\TermResourceTestBase::testPatchPath()
|
||||
*/
|
||||
public function testPatchPath() {
|
||||
$this->initAuthentication();
|
||||
$this->provisionEntityResource();
|
||||
$this->setUpAuthorization('GET');
|
||||
$this->setUpAuthorization('PATCH');
|
||||
|
||||
$url = $this->getEntityResourceUrl()->setOption('query', ['_format' => static::$format]);
|
||||
|
||||
// GET node's current normalization.
|
||||
$response = $this->request('GET', $url, $this->getAuthenticationRequestOptions('GET'));
|
||||
$normalization = $this->serializer->decode((string) $response->getBody(), static::$format);
|
||||
|
||||
// Change node's path alias.
|
||||
$normalization['path'][0]['alias'] .= 's-rule-the-world';
|
||||
|
||||
// Create node PATCH request.
|
||||
$request_options = [];
|
||||
$request_options[RequestOptions::HEADERS]['Content-Type'] = static::$mimeType;
|
||||
$request_options = array_merge_recursive($request_options, $this->getAuthenticationRequestOptions('PATCH'));
|
||||
$request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
|
||||
|
||||
// PATCH request: 403 when creating URL aliases unauthorized. Before
|
||||
// asserting the 403 response, assert that the stored path alias remains
|
||||
// unchanged.
|
||||
$response = $this->request('PATCH', $url, $request_options);
|
||||
$this->assertSame('/llama', $this->entityStorage->loadUnchanged($this->entity->id())->get('path')->alias);
|
||||
$this->assertResourceErrorResponse(403, "Access denied on updating field 'path'. " . static::$patchProtectedFieldNames['path'], $response);
|
||||
|
||||
// Grant permission to create URL aliases.
|
||||
$this->grantPermissionsToTestedRole(['create url aliases']);
|
||||
|
||||
// Repeat PATCH request: 200.
|
||||
$response = $this->request('PATCH', $url, $request_options);
|
||||
$this->assertResourceResponse(200, FALSE, $response);
|
||||
$updated_normalization = $this->serializer->decode((string) $response->getBody(), static::$format);
|
||||
$this->assertSame($normalization['path'], $updated_normalization['path']);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class NodeTypeJsonAnonTest extends NodeTypeResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class NodeTypeJsonBasicAuthTest extends NodeTypeResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue