Update to Drupal 8.2.0. For more information, see https://www.drupal.org/project/drupal/releases/8.2.0
This commit is contained in:
parent
2f563ab520
commit
f1c8716f57
1732 changed files with 52334 additions and 11780 deletions
|
@ -106,7 +106,7 @@ display:
|
|||
id: status
|
||||
table: node_field_data
|
||||
field: status
|
||||
value: true
|
||||
value: '1'
|
||||
group: 0
|
||||
expose:
|
||||
operator: '0'
|
||||
|
|
|
@ -370,7 +370,7 @@ display:
|
|||
group_type: group
|
||||
admin_label: ''
|
||||
operator: '='
|
||||
value: true
|
||||
value: '1'
|
||||
group: 1
|
||||
exposed: true
|
||||
expose:
|
||||
|
|
|
@ -105,7 +105,7 @@ display:
|
|||
operator: '='
|
||||
relationship: none
|
||||
table: node_field_data
|
||||
value: true
|
||||
value: '1'
|
||||
plugin_id: boolean
|
||||
entity_type: node
|
||||
entity_field: promote
|
||||
|
@ -116,7 +116,7 @@ display:
|
|||
group: 1
|
||||
id: status
|
||||
table: node_field_data
|
||||
value: true
|
||||
value: '1'
|
||||
plugin_id: boolean
|
||||
entity_type: node
|
||||
entity_field: status
|
||||
|
|
|
@ -6,7 +6,7 @@ node.settings:
|
|||
mapping:
|
||||
use_admin_theme:
|
||||
type: boolean
|
||||
label: 'Use admin theme when editing or creating content'
|
||||
label: 'Use administration theme when editing or creating content'
|
||||
|
||||
node.type.*:
|
||||
type: config_entity
|
||||
|
|
|
@ -11,6 +11,9 @@ process:
|
|||
entity_type: 'constants/entity_type'
|
||||
bundle: type
|
||||
field_name: 'constants/field_name'
|
||||
label:
|
||||
plugin: default_value
|
||||
default_value: 'Publishing status'
|
||||
'default_value/0/value': 'options/status'
|
||||
destination:
|
||||
plugin: entity:base_field_override
|
||||
|
|
|
@ -492,8 +492,7 @@ function hook_node_links_alter(array &$links, NodeInterface $entity, array &$con
|
|||
'#links' => array(
|
||||
'node-report' => array(
|
||||
'title' => t('Report'),
|
||||
'href' => "node/{$entity->id()}/report",
|
||||
'query' => array('token' => \Drupal::getContainer()->get('csrf_token')->get("node/{$entity->id()}/report")),
|
||||
'url' => Url::fromRoute('node_test.report', ['node' => $entity->id()], ['query' => ['token' => \Drupal::getContainer()->get('csrf_token')->get("node/{$entity->id()}/report")]]),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -320,7 +320,7 @@ function node_type_load($name) {
|
|||
* A Body field object.
|
||||
*/
|
||||
function node_add_body_field(NodeTypeInterface $type, $label = 'Body') {
|
||||
// Add or remove the body field, as needed.
|
||||
// Add or remove the body field, as needed.
|
||||
$field_storage = FieldStorageConfig::loadByName('node', 'body');
|
||||
$field = FieldConfig::loadByName('node', $type->id(), 'body');
|
||||
if (empty($field)) {
|
||||
|
@ -751,7 +751,7 @@ function node_get_recent($number = 10) {
|
|||
// If not, restrict the query to published nodes.
|
||||
$query->condition('status', NODE_PUBLISHED);
|
||||
}
|
||||
}
|
||||
}
|
||||
$nids = $query
|
||||
->sort('changed', 'DESC')
|
||||
->range(0, $number)
|
||||
|
|
|
@ -12,7 +12,6 @@ administer nodes:
|
|||
restrict access: true
|
||||
access content overview:
|
||||
title: 'Access the Content overview page'
|
||||
description: 'Get an overview of all content.'
|
||||
access content:
|
||||
title: 'View published content'
|
||||
view own unpublished content:
|
||||
|
|
|
@ -172,9 +172,9 @@ class NodeController extends ControllerBase implements ContainerInjectionInterfa
|
|||
$delete_permission = (($account->hasPermission("delete $type revisions") || $account->hasPermission('delete all revisions') || $account->hasPermission('administer nodes')) && $node->access('delete'));
|
||||
|
||||
$rows = array();
|
||||
$latest_revision = TRUE;
|
||||
$default_revision = $node->getRevisionId();
|
||||
|
||||
foreach ($this->_getRevisionIds($node, $node_storage) as $vid) {
|
||||
foreach ($this->getRevisionIds($node, $node_storage) as $vid) {
|
||||
/** @var \Drupal\node\NodeInterface $revision */
|
||||
$revision = $node_storage->loadRevision($vid);
|
||||
// Only show revisions that are affected by the language that is being
|
||||
|
@ -182,7 +182,7 @@ class NodeController extends ControllerBase implements ContainerInjectionInterfa
|
|||
if ($revision->hasTranslation($langcode) && $revision->getTranslation($langcode)->isRevisionTranslationAffected()) {
|
||||
$username = [
|
||||
'#theme' => 'username',
|
||||
'#account' => $revision->getRevisionAuthor(),
|
||||
'#account' => $revision->getRevisionUser(),
|
||||
];
|
||||
|
||||
// Use revision link to link to revisions that are not active.
|
||||
|
@ -210,7 +210,7 @@ class NodeController extends ControllerBase implements ContainerInjectionInterfa
|
|||
$this->renderer->addCacheableDependency($column['data'], $username);
|
||||
$row[] = $column;
|
||||
|
||||
if ($latest_revision) {
|
||||
if ($vid == $default_revision) {
|
||||
$row[] = [
|
||||
'data' => [
|
||||
'#prefix' => '<em>',
|
||||
|
@ -218,16 +218,17 @@ class NodeController extends ControllerBase implements ContainerInjectionInterfa
|
|||
'#suffix' => '</em>',
|
||||
],
|
||||
];
|
||||
foreach ($row as &$current) {
|
||||
$current['class'] = ['revision-current'];
|
||||
}
|
||||
$latest_revision = FALSE;
|
||||
|
||||
$rows[] = [
|
||||
'data' => $row,
|
||||
'class' => ['revision-current'],
|
||||
];
|
||||
}
|
||||
else {
|
||||
$links = [];
|
||||
if ($revert_permission) {
|
||||
$links['revert'] = [
|
||||
'title' => $this->t('Revert'),
|
||||
'title' => $vid < $node->getRevisionId() ? $this->t('Revert') : $this->t('Set as current revision'),
|
||||
'url' => $has_translations ?
|
||||
Url::fromRoute('node.revision_revert_translation_confirm', ['node' => $node->id(), 'node_revision' => $vid, 'langcode' => $langcode]) :
|
||||
Url::fromRoute('node.revision_revert_confirm', ['node' => $node->id(), 'node_revision' => $vid]),
|
||||
|
@ -247,9 +248,9 @@ class NodeController extends ControllerBase implements ContainerInjectionInterfa
|
|||
'#links' => $links,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$rows[] = $row;
|
||||
$rows[] = $row;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -260,6 +261,7 @@ class NodeController extends ControllerBase implements ContainerInjectionInterfa
|
|||
'#attached' => array(
|
||||
'library' => array('node/drupal.node.admin'),
|
||||
),
|
||||
'#attributes' => ['class' => 'node-revision-table'],
|
||||
);
|
||||
|
||||
$build['pager'] = array('#type' => 'pager');
|
||||
|
@ -283,7 +285,7 @@ class NodeController extends ControllerBase implements ContainerInjectionInterfa
|
|||
/**
|
||||
* Gets a list of node revision IDs for a specific node.
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* The node entity.
|
||||
* @param \Drupal\node\NodeStorageInterface $node_storage
|
||||
* The node storage handler.
|
||||
|
@ -291,7 +293,7 @@ class NodeController extends ControllerBase implements ContainerInjectionInterfa
|
|||
* @return int[]
|
||||
* Node revision IDs (in descending order).
|
||||
*/
|
||||
protected function _getRevisionIds(NodeInterface $node, NodeStorageInterface $node_storage) {
|
||||
protected function getRevisionIds(NodeInterface $node, NodeStorageInterface $node_storage) {
|
||||
$result = $node_storage->getQuery()
|
||||
->allRevisions()
|
||||
->condition($node->getEntityType()->getKey('id'), $node->id())
|
||||
|
|
|
@ -102,8 +102,8 @@ class Node extends ContentEntityBase implements NodeInterface {
|
|||
|
||||
// If no revision author has been set explicitly, make the node owner the
|
||||
// revision author.
|
||||
if (!$this->getRevisionAuthor()) {
|
||||
$this->setRevisionAuthorId($this->getOwnerId());
|
||||
if (!$this->getRevisionUser()) {
|
||||
$this->setRevisionUserId($this->getOwnerId());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -178,13 +178,8 @@ class Node extends ContentEntityBase implements NodeInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function access($operation = 'view', AccountInterface $account = NULL, $return_as_object = FALSE) {
|
||||
if ($operation == 'create') {
|
||||
return parent::access($operation, $account, $return_as_object);
|
||||
}
|
||||
|
||||
return \Drupal::entityManager()
|
||||
->getAccessControlHandler($this->entityTypeId)
|
||||
->access($this, $operation, $account, $return_as_object);
|
||||
// This override exists to set the operation to the default value "view".
|
||||
return parent::access($operation, $account, $return_as_object);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -311,6 +306,13 @@ class Node extends ContentEntityBase implements NodeInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRevisionAuthor() {
|
||||
return $this->getRevisionUser();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRevisionUser() {
|
||||
return $this->get('revision_uid')->entity;
|
||||
}
|
||||
|
||||
|
@ -318,7 +320,45 @@ class Node extends ContentEntityBase implements NodeInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function setRevisionAuthorId($uid) {
|
||||
$this->set('revision_uid', $uid);
|
||||
$this->setRevisionUserId($uid);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setRevisionUser(UserInterface $user) {
|
||||
$this->set('revision_uid', $user);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRevisionUserId() {
|
||||
return $this->get('revision_uid')->entity->id();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setRevisionUserId($user_id) {
|
||||
$this->set('revision_uid', $user_id);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRevisionLogMessage() {
|
||||
return $this->get('revision_log')->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setRevisionLogMessage($revision_log_message) {
|
||||
$this->set('revision_log', $revision_log_message);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
|
|
@ -83,7 +83,7 @@ class NodeType extends ConfigEntityBundleBase implements NodeTypeInterface {
|
|||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $new_revision = FALSE;
|
||||
protected $new_revision = TRUE;
|
||||
|
||||
/**
|
||||
* The preview mode.
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
namespace Drupal\node\Form;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Form\FormBase;
|
||||
|
@ -14,7 +13,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
|||
/**
|
||||
* Contains a form for switching the view mode of a node during preview.
|
||||
*/
|
||||
class NodePreviewForm extends FormBase implements ContainerInjectionInterface {
|
||||
class NodePreviewForm extends FormBase {
|
||||
|
||||
/**
|
||||
* The entity manager service.
|
||||
|
|
|
@ -322,7 +322,7 @@ class NodeForm extends ContentEntityForm {
|
|||
$node->setNewRevision();
|
||||
// If a new revision is created, save the current user as revision author.
|
||||
$node->setRevisionCreationTime(REQUEST_TIME);
|
||||
$node->setRevisionAuthorId(\Drupal::currentUser()->id());
|
||||
$node->setRevisionUserId(\Drupal::currentUser()->id());
|
||||
}
|
||||
else {
|
||||
$node->setNewRevision(FALSE);
|
||||
|
@ -356,7 +356,7 @@ class NodeForm extends ContentEntityForm {
|
|||
$node->save();
|
||||
$node_link = $node->link($this->t('View'));
|
||||
$context = array('@type' => $node->getType(), '%title' => $node->label(), 'link' => $node_link);
|
||||
$t_args = array('@type' => node_get_type_label($node), '%title' => $node->label());
|
||||
$t_args = array('@type' => node_get_type_label($node), '%title' => $node->link($node->label()));
|
||||
|
||||
if ($insert) {
|
||||
$this->logger('content')->notice('@type: added %title.', $context);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Drupal\node;
|
||||
|
||||
use Drupal\Core\Entity\RevisionLogInterface;
|
||||
use Drupal\user\EntityOwnerInterface;
|
||||
use Drupal\Core\Entity\EntityChangedInterface;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
|
@ -9,7 +10,7 @@ use Drupal\Core\Entity\ContentEntityInterface;
|
|||
/**
|
||||
* Provides an interface defining a node entity.
|
||||
*/
|
||||
interface NodeInterface extends ContentEntityInterface, EntityChangedInterface, EntityOwnerInterface {
|
||||
interface NodeInterface extends ContentEntityInterface, EntityChangedInterface, EntityOwnerInterface, RevisionLogInterface {
|
||||
|
||||
/**
|
||||
* Gets the node type.
|
||||
|
@ -140,6 +141,9 @@ interface NodeInterface extends ContentEntityInterface, EntityChangedInterface,
|
|||
*
|
||||
* @return \Drupal\user\UserInterface
|
||||
* The user entity for the revision author.
|
||||
*
|
||||
* @deprecated in Drupal 8.2.0, will be removed before Drupal 9.0.0. Use
|
||||
* \Drupal\Core\Entity\RevisionLogInterface::getRevisionUser() instead.
|
||||
*/
|
||||
public function getRevisionAuthor();
|
||||
|
||||
|
@ -151,6 +155,9 @@ interface NodeInterface extends ContentEntityInterface, EntityChangedInterface,
|
|||
*
|
||||
* @return \Drupal\node\NodeInterface
|
||||
* The called node entity.
|
||||
*
|
||||
* @deprecated in Drupal 8.2.0, will be removed before Drupal 9.0.0. Use
|
||||
* \Drupal\Core\Entity\RevisionLogInterface::setRevisionUserId() instead.
|
||||
*/
|
||||
public function setRevisionAuthorId($uid);
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ interface NodeStorageInterface extends ContentEntityStorageInterface {
|
|||
/**
|
||||
* Gets a list of node revision IDs for a specific node.
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* The node entity.
|
||||
*
|
||||
* @return int[]
|
||||
|
@ -36,7 +36,7 @@ interface NodeStorageInterface extends ContentEntityStorageInterface {
|
|||
/**
|
||||
* Counts the number of revisions in the default language.
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* The node entity.
|
||||
*
|
||||
* @return int
|
||||
|
|
|
@ -21,7 +21,6 @@ class NodeTypeAccessControlHandler extends EntityAccessControlHandler {
|
|||
switch ($operation) {
|
||||
case 'view':
|
||||
return AccessResult::allowedIfHasPermission($account, 'access content');
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
if ($entity->isLocked()) {
|
||||
|
@ -34,7 +33,7 @@ class NodeTypeAccessControlHandler extends EntityAccessControlHandler {
|
|||
|
||||
default:
|
||||
return parent::checkAccess($entity, $operation, $account);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -261,7 +261,7 @@ class Node extends DrupalSqlBase {
|
|||
/**
|
||||
* Adapt our query for translations.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Query\SelectInterface
|
||||
* @param \Drupal\Core\Database\Query\SelectInterface $query
|
||||
* The generated query.
|
||||
*/
|
||||
protected function handleTranslations(SelectInterface $query) {
|
||||
|
|
|
@ -39,7 +39,7 @@ class Vid extends NumericArgument {
|
|||
* The plugin implementation definition.
|
||||
* @param \Drupal\Core\Database\Connection $database
|
||||
* Database Service Object.
|
||||
* @param \Drupal\node\NodeStorageInterface
|
||||
* @param \Drupal\node\NodeStorageInterface $node_storage
|
||||
* The node storage.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, Connection $database, NodeStorageInterface $node_storage) {
|
||||
|
|
|
@ -49,7 +49,7 @@ class Node extends WizardPluginBase {
|
|||
public function getAvailableSorts() {
|
||||
// You can't execute functions in properties, so override the method
|
||||
return array(
|
||||
'node_field_data-title:DESC' => $this->t('Title')
|
||||
'node_field_data-title:ASC' => $this->t('Title')
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -217,7 +217,7 @@ class Node extends WizardPluginBase {
|
|||
// entities. If a particular entity type (i.e., bundle) has been
|
||||
// selected above, then we only search for taxonomy fields associated
|
||||
// with that bundle. Otherwise, we use all bundles.
|
||||
$bundles = array_keys(entity_get_bundles($this->entityTypeId));
|
||||
$bundles = array_keys($this->bundleInfoService->getBundleInfo($this->entityTypeId));
|
||||
// Double check that this is a real bundle before using it (since above
|
||||
// we added a dummy option 'all' to the bundle list on the form).
|
||||
if (isset($selected_bundle) && in_array($selected_bundle, $bundles)) {
|
||||
|
|
|
@ -35,14 +35,14 @@ class MigrateNodeRevisionTest extends MigrateNodeTestBase {
|
|||
$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->getRevisionAuthor()->id());
|
||||
$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->getRevisionAuthor()->id());
|
||||
$this->assertIdentical('1', $node->getRevisionUser()->id());
|
||||
$this->assertIdentical('modified rev 3', $node->revision_log->value);
|
||||
$this->assertIdentical('1390095703', $node->getRevisionCreationTime());
|
||||
}
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\node\Tests;
|
||||
|
||||
/**
|
||||
* Tests basic node_access functionality with hook_node_grants().
|
||||
*
|
||||
* This test just wraps the existing default permissions test while a module
|
||||
* that implements hook_node_grants() is enabled.
|
||||
*
|
||||
* @see \Drupal\node\NodeGrantDatabaseStorage
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeAccessGrantsTest extends NodeAccessTest {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('node_access_test_empty');
|
||||
|
||||
/**
|
||||
* Test operations not supported by node grants.
|
||||
*/
|
||||
function testUnsupportedOperation() {
|
||||
$web_user = $this->drupalCreateUser(['access content']);
|
||||
$node = $this->drupalCreateNode();
|
||||
$this->assertNodeAccess(['random_operation' => FALSE], $node, $web_user);
|
||||
}
|
||||
|
||||
}
|
|
@ -254,9 +254,9 @@ class NodeAccessLanguageAwareCombinationTest extends NodeTestBase {
|
|||
|
||||
// Query with no language specified. The fallback (hu or und) will be used.
|
||||
$select = db_select('node', 'n')
|
||||
->fields('n', array('nid'))
|
||||
->addMetaData('account', $this->webUser)
|
||||
->addTag('node_access');
|
||||
->fields('n', array('nid'))
|
||||
->addMetaData('account', $this->webUser)
|
||||
->addTag('node_access');
|
||||
$nids = $select->execute()->fetchAllAssoc('nid');
|
||||
|
||||
// Four nodes should be returned with public Hungarian translations or the
|
||||
|
@ -269,10 +269,10 @@ class NodeAccessLanguageAwareCombinationTest extends NodeTestBase {
|
|||
|
||||
// Query with Hungarian (hu) specified.
|
||||
$select = db_select('node', 'n')
|
||||
->fields('n', array('nid'))
|
||||
->addMetaData('account', $this->webUser)
|
||||
->addMetaData('langcode', 'hu')
|
||||
->addTag('node_access');
|
||||
->fields('n', array('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).
|
||||
|
@ -283,10 +283,10 @@ class NodeAccessLanguageAwareCombinationTest extends NodeTestBase {
|
|||
|
||||
// Query with Catalan (ca) specified.
|
||||
$select = db_select('node', 'n')
|
||||
->fields('n', array('nid'))
|
||||
->addMetaData('account', $this->webUser)
|
||||
->addMetaData('langcode', 'ca')
|
||||
->addTag('node_access');
|
||||
->fields('n', array('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).
|
||||
|
@ -297,10 +297,10 @@ class NodeAccessLanguageAwareCombinationTest extends NodeTestBase {
|
|||
|
||||
// Query with German (de) specified.
|
||||
$select = db_select('node', 'n')
|
||||
->fields('n', array('nid'))
|
||||
->addMetaData('account', $this->webUser)
|
||||
->addMetaData('langcode', 'de')
|
||||
->addTag('node_access');
|
||||
->fields('n', array('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.
|
||||
|
@ -309,9 +309,9 @@ class NodeAccessLanguageAwareCombinationTest extends NodeTestBase {
|
|||
// 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', array('nid'))
|
||||
->addMetaData('account', $this->adminUser)
|
||||
->addTag('node_access');
|
||||
->fields('n', array('nid'))
|
||||
->addMetaData('account', $this->adminUser)
|
||||
->addTag('node_access');
|
||||
$nids = $select->execute()->fetchAllAssoc('nid');
|
||||
|
||||
// All nodes are returned.
|
||||
|
@ -320,10 +320,10 @@ class NodeAccessLanguageAwareCombinationTest extends NodeTestBase {
|
|||
// Query the nodes table as admin user (full access) with the node access
|
||||
// tag and langcode de.
|
||||
$select = db_select('node', 'n')
|
||||
->fields('n', array('nid'))
|
||||
->addMetaData('account', $this->adminUser)
|
||||
->addMetaData('langcode', 'de')
|
||||
->addTag('node_access');
|
||||
->fields('n', array('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
|
||||
|
|
|
@ -195,9 +195,9 @@ class NodeAccessLanguageAwareTest extends NodeTestBase {
|
|||
|
||||
// Query with no language specified. The fallback (hu) will be used.
|
||||
$select = db_select('node', 'n')
|
||||
->fields('n', array('nid'))
|
||||
->addMetaData('account', $this->webUser)
|
||||
->addTag('node_access');
|
||||
->fields('n', array('nid'))
|
||||
->addMetaData('account', $this->webUser)
|
||||
->addTag('node_access');
|
||||
$nids = $select->execute()->fetchAllAssoc('nid');
|
||||
|
||||
// Three nodes should be returned:
|
||||
|
@ -211,10 +211,10 @@ class NodeAccessLanguageAwareTest extends NodeTestBase {
|
|||
|
||||
// Query with Hungarian (hu) specified.
|
||||
$select = db_select('node', 'n')
|
||||
->fields('n', array('nid'))
|
||||
->addMetaData('account', $this->webUser)
|
||||
->addMetaData('langcode', 'hu')
|
||||
->addTag('node_access');
|
||||
->fields('n', array('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
|
||||
|
@ -225,10 +225,10 @@ class NodeAccessLanguageAwareTest extends NodeTestBase {
|
|||
|
||||
// Query with Catalan (ca) specified.
|
||||
$select = db_select('node', 'n')
|
||||
->fields('n', array('nid'))
|
||||
->addMetaData('account', $this->webUser)
|
||||
->addMetaData('langcode', 'ca')
|
||||
->addTag('node_access');
|
||||
->fields('n', array('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
|
||||
|
@ -239,10 +239,10 @@ class NodeAccessLanguageAwareTest extends NodeTestBase {
|
|||
|
||||
// Query with German (de) specified.
|
||||
$select = db_select('node', 'n')
|
||||
->fields('n', array('nid'))
|
||||
->addMetaData('account', $this->webUser)
|
||||
->addMetaData('langcode', 'de')
|
||||
->addTag('node_access');
|
||||
->fields('n', array('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.
|
||||
|
@ -251,9 +251,9 @@ class NodeAccessLanguageAwareTest extends NodeTestBase {
|
|||
// 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', array('nid'))
|
||||
->addMetaData('account', $this->adminUser)
|
||||
->addTag('node_access');
|
||||
->fields('n', array('nid'))
|
||||
->addMetaData('account', $this->adminUser)
|
||||
->addTag('node_access');
|
||||
$nids = $select->execute()->fetchAllAssoc('nid');
|
||||
|
||||
// All nodes are returned.
|
||||
|
@ -262,10 +262,10 @@ class NodeAccessLanguageAwareTest extends NodeTestBase {
|
|||
// Query the nodes table as admin user (full access) with the node access
|
||||
// tag and langcode de.
|
||||
$select = db_select('node', 'n')
|
||||
->fields('n', array('nid'))
|
||||
->addMetaData('account', $this->adminUser)
|
||||
->addMetaData('langcode', 'de')
|
||||
->addTag('node_access');
|
||||
->fields('n', array('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
|
||||
|
|
|
@ -204,9 +204,9 @@ class NodeAccessLanguageTest extends NodeTestBase {
|
|||
// Query the nodes table as the web user with the node access tag and no
|
||||
// specific langcode.
|
||||
$select = db_select('node', 'n')
|
||||
->fields('n', array('nid'))
|
||||
->addMetaData('account', $web_user)
|
||||
->addTag('node_access');
|
||||
->fields('n', array('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
|
||||
|
@ -218,10 +218,10 @@ class NodeAccessLanguageTest extends NodeTestBase {
|
|||
// Query the nodes table as the web user with the node access tag and
|
||||
// langcode de.
|
||||
$select = db_select('node', 'n')
|
||||
->fields('n', array('nid'))
|
||||
->addMetaData('account', $web_user)
|
||||
->addMetaData('langcode', 'de')
|
||||
->addTag('node_access');
|
||||
->fields('n', array('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.
|
||||
|
@ -230,9 +230,9 @@ class NodeAccessLanguageTest extends NodeTestBase {
|
|||
// 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', array('nid'))
|
||||
->addMetaData('account', $admin_user)
|
||||
->addTag('node_access');
|
||||
->fields('n', array('nid'))
|
||||
->addMetaData('account', $admin_user)
|
||||
->addTag('node_access');
|
||||
$nids = $select->execute()->fetchAllAssoc('nid');
|
||||
|
||||
// All nodes are returned.
|
||||
|
@ -241,10 +241,10 @@ class NodeAccessLanguageTest extends NodeTestBase {
|
|||
// Query the nodes table as admin user (full access) with the node access
|
||||
// tag and langcode de.
|
||||
$select = db_select('node', 'n')
|
||||
->fields('n', array('nid'))
|
||||
->addMetaData('account', $admin_user)
|
||||
->addMetaData('langcode', 'de')
|
||||
->addTag('node_access');
|
||||
->fields('n', array('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
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\node\Tests;
|
||||
|
||||
use Drupal\user\RoleInterface;
|
||||
|
||||
/**
|
||||
* Tests basic node_access functionality.
|
||||
*
|
||||
* Note that hook_node_access_records() is covered in another test class.
|
||||
*
|
||||
* @group node
|
||||
* @todo Cover hook_node_access in a separate test class.
|
||||
*/
|
||||
class NodeAccessTest extends NodeTestBase {
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// Clear permissions for authenticated users.
|
||||
$this->config('user.role.' . RoleInterface::AUTHENTICATED_ID)->set('permissions', array())->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs basic tests for node_access function.
|
||||
*/
|
||||
function testNodeAccess() {
|
||||
// Ensures user without 'access content' permission can do nothing.
|
||||
$web_user1 = $this->drupalCreateUser(array('create page content', 'edit any page content', 'delete any page content'));
|
||||
$node1 = $this->drupalCreateNode(array('type' => 'page'));
|
||||
$this->assertNodeCreateAccess($node1->bundle(), FALSE, $web_user1);
|
||||
$this->assertNodeAccess(array('view' => FALSE, 'update' => FALSE, 'delete' => FALSE), $node1, $web_user1);
|
||||
|
||||
// Ensures user with 'bypass node access' permission can do everything.
|
||||
$web_user2 = $this->drupalCreateUser(array('bypass node access'));
|
||||
$node2 = $this->drupalCreateNode(array('type' => 'page'));
|
||||
$this->assertNodeCreateAccess($node2->bundle(), TRUE, $web_user2);
|
||||
$this->assertNodeAccess(array('view' => TRUE, 'update' => TRUE, 'delete' => TRUE), $node2, $web_user2);
|
||||
|
||||
// User cannot 'view own unpublished content'.
|
||||
$web_user3 = $this->drupalCreateUser(array('access content'));
|
||||
$node3 = $this->drupalCreateNode(array('status' => 0, 'uid' => $web_user3->id()));
|
||||
$this->assertNodeAccess(array('view' => FALSE), $node3, $web_user3);
|
||||
|
||||
// User cannot create content without permission.
|
||||
$this->assertNodeCreateAccess($node3->bundle(), FALSE, $web_user3);
|
||||
|
||||
// User can 'view own unpublished content', but another user cannot.
|
||||
$web_user4 = $this->drupalCreateUser(array('access content', 'view own unpublished content'));
|
||||
$web_user5 = $this->drupalCreateUser(array('access content', 'view own unpublished content'));
|
||||
$node4 = $this->drupalCreateNode(array('status' => 0, 'uid' => $web_user4->id()));
|
||||
$this->assertNodeAccess(array('view' => TRUE, 'update' => FALSE), $node4, $web_user4);
|
||||
$this->assertNodeAccess(array('view' => FALSE), $node4, $web_user5);
|
||||
|
||||
// Tests the default access provided for a published node.
|
||||
$node5 = $this->drupalCreateNode();
|
||||
$this->assertNodeAccess(array('view' => TRUE, 'update' => FALSE, 'delete' => FALSE), $node5, $web_user3);
|
||||
|
||||
// Tests the "edit any BUNDLE" and "delete any BUNDLE" permissions.
|
||||
$web_user6 = $this->drupalCreateUser(array('access content', 'edit any page content', 'delete any page content'));
|
||||
$node6 = $this->drupalCreateNode(array('type' => 'page'));
|
||||
$this->assertNodeAccess(array('view' => TRUE, 'update' => TRUE, 'delete' => TRUE), $node6, $web_user6);
|
||||
|
||||
// Tests the "edit own BUNDLE" and "delete own BUNDLE" permission.
|
||||
$web_user7 = $this->drupalCreateUser(array('access content', 'edit own page content', 'delete own page content'));
|
||||
// User should not be able to edit or delete nodes they do not own.
|
||||
$this->assertNodeAccess(array('view' => TRUE, 'update' => FALSE, 'delete' => FALSE), $node6, $web_user7);
|
||||
|
||||
// User should be able to edit or delete nodes they own.
|
||||
$node7 = $this->drupalCreateNode(array('type' => 'page', 'uid' => $web_user7->id()));
|
||||
$this->assertNodeAccess(array('view' => TRUE, 'update' => TRUE, 'delete' => TRUE), $node7, $web_user7);
|
||||
}
|
||||
|
||||
}
|
|
@ -48,7 +48,11 @@ class NodeCreationTest extends NodeTestBase {
|
|||
$this->drupalPostForm('node/add/page', $edit, t('Save'));
|
||||
|
||||
// Check that the Basic page has been created.
|
||||
$this->assertRaw(t('@post %title has been created.', array('@post' => 'Basic page', '%title' => $edit['title[0][value]'])), 'Basic page created.');
|
||||
$this->assertText(t('@post @title has been created.', array('@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)]', array(':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]']);
|
||||
|
@ -137,7 +141,11 @@ class NodeCreationTest extends NodeTestBase {
|
|||
$this->assertText(t('Test page text'));
|
||||
|
||||
// Confirm that the node was created.
|
||||
$this->assertRaw(t('@post %title has been created.', array('@post' => 'Basic page', '%title' => $edit['title[0][value]'])));
|
||||
$this->assertText(t('@post @title has been created.', array('@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)]', array(':href' => 'node/'));
|
||||
$this->assert(isset($view_link), 'The message area contains a link to a node');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -110,7 +110,7 @@ class NodeEditFormTest extends NodeTestBase {
|
|||
// 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->getRevisionAuthor()->id(), $second_node_version->getRevisionAuthor()->id(), 'Each revision has a distinct user.');
|
||||
$this->assertNotIdentical($first_node_version->getRevisionUser()->id(), $second_node_version->getRevisionUser()->id(), 'Each revision has a distinct user.');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -119,7 +119,7 @@ class NodeFieldMultilingualTest extends WebTestBase {
|
|||
|
||||
// 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', array(
|
||||
$body = $this->xpath('//article[contains(concat(" ", normalize-space(@class), " "), :node-class)]//div[contains(concat(" ", normalize-space(@class), " "), :content-class)]/descendant::p', array(
|
||||
':node-class' => ' node ',
|
||||
':content-class' => 'node__content',
|
||||
));
|
||||
|
|
|
@ -52,11 +52,13 @@ class NodeFormSaveChangedTimeTest extends WebTestBase {
|
|||
* Test the changed time after API and FORM save without changes.
|
||||
*/
|
||||
public function testChangedTimeAfterSaveWithoutChanges() {
|
||||
$node = entity_load('node', 1);
|
||||
$storage = $this->container->get('entity_type.manager')->getStorage('node');
|
||||
$storage->resetCache([1]);
|
||||
$node = $storage->load(1);
|
||||
$changed_timestamp = $node->getChangedTime();
|
||||
|
||||
$node->save();
|
||||
$node = entity_load('node', 1, TRUE);
|
||||
$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.
|
||||
|
@ -65,7 +67,8 @@ class NodeFormSaveChangedTimeTest extends WebTestBase {
|
|||
// Save the node on the regular node edit form.
|
||||
$this->drupalPostForm('node/1/edit', array(), t('Save'));
|
||||
|
||||
$node = entity_load('node', 1, TRUE);
|
||||
$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.");
|
||||
}
|
||||
|
||||
|
|
|
@ -39,9 +39,9 @@ class NodeLoadMultipleTest extends NodeTestBase {
|
|||
$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 = entity_load_multiple_by_properties('node', array('promote' => 0));
|
||||
$nodes = $this->container->get('entity_type.manager')->getStorage('node')
|
||||
->loadByProperties(array('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);
|
||||
|
|
|
@ -109,10 +109,10 @@ class NodeRevisionsTest extends NodeTestBase {
|
|||
// Edit the 2nd revision with a different user.
|
||||
if ($i == 1) {
|
||||
$editor = $this->drupalCreateUser();
|
||||
$node->setRevisionAuthorId($editor->id());
|
||||
$node->setRevisionUserId($editor->id());
|
||||
}
|
||||
else {
|
||||
$node->setRevisionAuthorId($web_user->id());
|
||||
$node->setRevisionUserId($web_user->id());
|
||||
}
|
||||
|
||||
$node->save();
|
||||
|
|
|
@ -121,4 +121,43 @@ class NodeRevisionsUiTest extends NodeTestBase {
|
|||
$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();
|
||||
|
||||
$node->setNewRevision(TRUE);
|
||||
$node->save();
|
||||
$node->setNewRevision(TRUE);
|
||||
$node->save();
|
||||
$node->isDefaultRevision(FALSE);
|
||||
$node->setNewRevision(TRUE);
|
||||
$node->save();
|
||||
$node->isDefaultRevision(FALSE);
|
||||
$node->setNewRevision(TRUE);
|
||||
$node->save();
|
||||
|
||||
$node_id = $node->id();
|
||||
|
||||
$this->drupalGet('node/' . $node_id . '/revisions');
|
||||
|
||||
// Verify that the default revision can be an older revision than the latest
|
||||
// one.
|
||||
$this->assertLinkByHref('/node/' . $node_id . '/revisions/5/revert');
|
||||
$this->assertLinkByHref('/node/' . $node_id . '/revisions/4/revert');
|
||||
$this->assertNoLinkByHref('/node/' . $node_id . '/revisions/3/revert');
|
||||
$current_revision_row = $this->xpath("//table[contains(@class, :table_class)]//tbody//tr[3 and contains(@class, :class) and contains(., :text)]", [
|
||||
':table_class' => 'node-revision-table',
|
||||
':class' => 'revision-current',
|
||||
':text' => 'Current revision',
|
||||
]);
|
||||
$this->assertEqual(count($current_revision_row), 1, 'The default revision can be a revision other than the latest one.');
|
||||
$this->assertLinkByHref('/node/' . $node_id . '/revisions/2/revert');
|
||||
$this->assertLinkByHref('/node/' . $node_id . '/revisions/1/revert');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -83,7 +83,10 @@ class NodeTranslationUITest extends ContentTranslationUITestBase {
|
|||
$default_langcode = $this->langcodes[0];
|
||||
$values[$default_langcode] = array('title' => array(array('value' => $this->randomMachineName())));
|
||||
$entity_id = $this->createEntity($values[$default_langcode], $default_langcode);
|
||||
$entity = entity_load($this->entityTypeId, $entity_id, TRUE);
|
||||
$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';
|
||||
|
@ -98,7 +101,8 @@ class NodeTranslationUITest extends ContentTranslationUITestBase {
|
|||
], array('language' => $language));
|
||||
$this->drupalPostForm($add_url, $this->getEditValues($values, $langcode), t('Save and unpublish (this translation)'));
|
||||
|
||||
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
|
||||
$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.');
|
||||
|
@ -148,7 +152,10 @@ class NodeTranslationUITest extends ContentTranslationUITestBase {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doTestPublishedStatus() {
|
||||
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
|
||||
$storage = $this->container->get('entity_type.manager')
|
||||
->getStorage($this->entityTypeId);
|
||||
$storage->resetCache([$this->entityId]);
|
||||
$entity = $storage->load($this->entityId);
|
||||
$languages = $this->container->get('language_manager')->getLanguages();
|
||||
|
||||
$actions = array(
|
||||
|
@ -164,7 +171,8 @@ class NodeTranslationUITest extends ContentTranslationUITestBase {
|
|||
$url = $entity->urlInfo('edit-form', $options);
|
||||
$this->drupalPostForm($url, array(), $action . $this->getFormSubmitSuffix($entity, $langcode), $options);
|
||||
}
|
||||
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
|
||||
$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.
|
||||
|
@ -179,7 +187,10 @@ class NodeTranslationUITest extends ContentTranslationUITestBase {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doTestAuthoringInfo() {
|
||||
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
|
||||
$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 = array();
|
||||
|
||||
|
@ -204,7 +215,8 @@ class NodeTranslationUITest extends ContentTranslationUITestBase {
|
|||
$this->drupalPostForm($url, $edit, $this->getFormSubmitAction($entity, $langcode), $options);
|
||||
}
|
||||
|
||||
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
|
||||
$storage->resetCache([$this->entityId]);
|
||||
$entity = $storage->load($this->entityId);
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
$translation = $entity->getTranslation($langcode);
|
||||
$metadata = $this->manager->getTranslationMetadata($translation);
|
||||
|
@ -424,7 +436,10 @@ class NodeTranslationUITest extends ContentTranslationUITestBase {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doTestTranslationEdit() {
|
||||
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
|
||||
$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);
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@ class BulkFormAccessTest extends NodeTestBase {
|
|||
'node_bulk_form[0]' => TRUE,
|
||||
'action' => 'node_unpublish_action',
|
||||
);
|
||||
$this->drupalPostForm('test-node-bulk-form', $edit, t('Apply'));
|
||||
$this->drupalPostForm('test-node-bulk-form', $edit, t('Apply to selected items'));
|
||||
$this->assertRaw(SafeMarkup::format('No access to execute %action on the @entity_type_label %entity_label.', [
|
||||
'%action' => 'Unpublish content',
|
||||
'@entity_type_label' => 'Content',
|
||||
|
@ -115,7 +115,11 @@ class BulkFormAccessTest extends NodeTestBase {
|
|||
'node_bulk_form[0]' => TRUE,
|
||||
'action' => 'node_unpublish_action',
|
||||
);
|
||||
$this->drupalPostForm('test-node-bulk-form', $edit, t('Apply'));
|
||||
$this->drupalPostForm('test-node-bulk-form', $edit, t('Apply to selected items'));
|
||||
// Test that the action message isn't shown.
|
||||
$this->assertNoRaw(SafeMarkup::format('%action was applied to 1 item.', [
|
||||
'%action' => 'Unpublish content',
|
||||
]));
|
||||
// Re-load the node and check the status.
|
||||
$node = Node::load($node->id());
|
||||
$this->assertTrue($node->isPublished(), 'The node is still published.');
|
||||
|
@ -160,7 +164,7 @@ class BulkFormAccessTest extends NodeTestBase {
|
|||
'node_bulk_form[1]' => TRUE,
|
||||
'action' => 'node_delete_action',
|
||||
);
|
||||
$this->drupalPostForm('test-node-bulk-form', $edit, t('Apply'));
|
||||
$this->drupalPostForm('test-node-bulk-form', $edit, t('Apply to selected items'));
|
||||
$this->drupalPostForm(NULL, array(), t('Delete'));
|
||||
// Ensure the private node still exists.
|
||||
$private_node = Node::load($private_node->id());
|
||||
|
|
|
@ -105,7 +105,7 @@ class BulkFormTest extends NodeTestBase {
|
|||
'node_bulk_form[0]' => TRUE,
|
||||
'action' => 'node_unpublish_action',
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply'));
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
|
||||
$node = $this->loadNode($node->id());
|
||||
$this->assertFalse($node->isPublished(), 'Node has been unpublished');
|
||||
$this->assertTrue($node->getTranslation('en-gb')->isPublished(), 'Node translation has not been unpublished');
|
||||
|
@ -116,7 +116,7 @@ class BulkFormTest extends NodeTestBase {
|
|||
'node_bulk_form[0]' => TRUE,
|
||||
'action' => 'node_publish_action',
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply'));
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
|
||||
$node = $this->loadNode($node->id());
|
||||
$this->assertTrue($node->isPublished(), 'Node has been published again');
|
||||
|
||||
|
@ -128,7 +128,7 @@ class BulkFormTest extends NodeTestBase {
|
|||
'node_bulk_form[0]' => TRUE,
|
||||
'action' => 'node_make_sticky_action',
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply'));
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
|
||||
$node = $this->loadNode($node->id());
|
||||
$this->assertTrue($node->isSticky(), 'Node has been made sticky');
|
||||
$this->assertFalse($node->getTranslation('en-gb')->isSticky(), 'Node translation has not been made sticky');
|
||||
|
@ -139,7 +139,7 @@ class BulkFormTest extends NodeTestBase {
|
|||
'node_bulk_form[0]' => TRUE,
|
||||
'action' => 'node_make_unsticky_action',
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply'));
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
|
||||
$node = $this->loadNode($node->id());
|
||||
$this->assertFalse($node->isSticky(), 'Node is not sticky anymore');
|
||||
|
||||
|
@ -151,7 +151,7 @@ class BulkFormTest extends NodeTestBase {
|
|||
'node_bulk_form[0]' => TRUE,
|
||||
'action' => 'node_promote_action',
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply'));
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
|
||||
$node = $this->loadNode($node->id());
|
||||
$this->assertTrue($node->isPromoted(), 'Node has been promoted to the front page');
|
||||
$this->assertFalse($node->getTranslation('en-gb')->isPromoted(), 'Node translation has not been promoted to the front page');
|
||||
|
@ -162,7 +162,7 @@ class BulkFormTest extends NodeTestBase {
|
|||
'node_bulk_form[0]' => TRUE,
|
||||
'action' => 'node_unpromote_action',
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply'));
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
|
||||
$node = $this->loadNode($node->id());
|
||||
$this->assertFalse($node->isPromoted(), 'Node has been demoted');
|
||||
|
||||
|
@ -185,7 +185,7 @@ class BulkFormTest extends NodeTestBase {
|
|||
'node_bulk_form[9]' => FALSE, // Node 5, British English, untranslated.
|
||||
'action' => 'node_unpublish_action',
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply'));
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
|
||||
$node = $this->loadNode(1);
|
||||
$this->assertFalse($node->getTranslation('en')->isPublished(), '1: English translation has been unpublished');
|
||||
$this->assertFalse($node->getTranslation('en-gb')->isPublished(), '1: British English translation has been unpublished');
|
||||
|
@ -226,7 +226,7 @@ class BulkFormTest extends NodeTestBase {
|
|||
'node_bulk_form[9]' => FALSE, // Node 5, British English, untranslated.
|
||||
'action' => 'node_delete_action',
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply'));
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
|
||||
|
||||
$label = $this->loadNode(1)->label();
|
||||
$this->assertText("$label (Original translation) - The following content translations will be deleted:");
|
||||
|
|
|
@ -2,7 +2,7 @@ type: default
|
|||
name: Default
|
||||
description: 'Default description.'
|
||||
help: ''
|
||||
new_revision: false
|
||||
new_revision: true
|
||||
display_submitted: true
|
||||
preview_mode: 1
|
||||
status: true
|
||||
|
|
|
@ -91,7 +91,7 @@ display:
|
|||
plugin_id: field
|
||||
filters:
|
||||
status:
|
||||
value: true
|
||||
value: '1'
|
||||
table: node_field_data
|
||||
field: status
|
||||
id: status
|
||||
|
@ -177,7 +177,7 @@ display:
|
|||
filter_groups: false
|
||||
filters:
|
||||
status:
|
||||
value: true
|
||||
value: '1'
|
||||
table: node_field_data
|
||||
field: status
|
||||
id: status
|
||||
|
@ -244,7 +244,7 @@ display:
|
|||
filter_groups: false
|
||||
filters:
|
||||
status:
|
||||
value: true
|
||||
value: '1'
|
||||
table: node_field_data
|
||||
field: status
|
||||
id: status
|
||||
|
@ -311,7 +311,7 @@ display:
|
|||
filter_groups: false
|
||||
filters:
|
||||
status:
|
||||
value: true
|
||||
value: '1'
|
||||
table: node_field_data
|
||||
field: status
|
||||
id: status
|
||||
|
|
|
@ -166,7 +166,7 @@ display:
|
|||
type: language
|
||||
filters:
|
||||
status:
|
||||
value: true
|
||||
value: '1'
|
||||
table: node_field_data
|
||||
field: status
|
||||
id: status
|
||||
|
|
|
@ -123,7 +123,7 @@ display:
|
|||
plugin_id: node_path
|
||||
filters:
|
||||
status:
|
||||
value: true
|
||||
value: '1'
|
||||
table: node_field_data
|
||||
field: status
|
||||
plugin_id: boolean
|
||||
|
|
|
@ -29,7 +29,7 @@ display:
|
|||
group: 1
|
||||
id: status
|
||||
table: node_field_data
|
||||
value: true
|
||||
value: '1'
|
||||
plugin_id: boolean
|
||||
entity_type: node
|
||||
entity_field: status
|
||||
|
|
|
@ -123,7 +123,7 @@ display:
|
|||
entity_field: nid
|
||||
filters:
|
||||
status:
|
||||
value: true
|
||||
value: '1'
|
||||
table: node_field_data
|
||||
field: status
|
||||
id: status
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Kernel\Migrate\d6;
|
||||
|
||||
use Drupal\Core\Field\Entity\BaseFieldOverride;
|
||||
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
|
||||
|
||||
/**
|
||||
* @group migrate_drupal_6
|
||||
*/
|
||||
class MigrateNodeSettingStatusTest extends MigrateDrupal6TestBase {
|
||||
|
||||
public static $modules = ['node', 'text'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installConfig(['node']);
|
||||
$this->executeMigration('d6_node_type');
|
||||
$this->executeMigration('d6_node_setting_status');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests migration of the publishing status checkbox's settings.
|
||||
*/
|
||||
public function testMigration() {
|
||||
$this->assertIdentical('Publishing status', BaseFieldOverride::load('node.article.status')->label());
|
||||
}
|
||||
|
||||
}
|
|
@ -51,7 +51,7 @@ class MigrateNodeTest extends MigrateNodeTestBase {
|
|||
/** @var \Drupal\node\NodeInterface $node_revision */
|
||||
$node_revision = \Drupal::entityManager()->getStorage('node')->loadRevision(1);
|
||||
$this->assertIdentical('Test title', $node_revision->getTitle());
|
||||
$this->assertIdentical('1', $node_revision->getRevisionAuthor()->id(), 'Node revision has the correct user');
|
||||
$this->assertIdentical('1', $node_revision->getRevisionUser()->id(), 'Node revision has the correct user');
|
||||
// This is empty on the first revision.
|
||||
$this->assertIdentical(NULL, $node_revision->revision_log->value);
|
||||
$this->assertIdentical('This is a shared text field', $node->field_test->value);
|
||||
|
|
|
@ -111,7 +111,7 @@ class MigrateNodeTest extends MigrateDrupal7TestBase {
|
|||
$revision = \Drupal::entityManager()->getStorage('node')->loadRevision($id);
|
||||
$this->assertTrue($revision instanceof NodeInterface);
|
||||
$this->assertIdentical($title, $revision->getTitle());
|
||||
$this->assertIdentical($uid, $revision->getRevisionAuthor()->id());
|
||||
$this->assertIdentical($uid, $revision->getRevisionUser()->id());
|
||||
$this->assertIdentical($log, $revision->revision_log->value);
|
||||
$this->assertIdentical($timestamp, $revision->getRevisionCreationTime());
|
||||
}
|
||||
|
|
262
core/modules/node/tests/src/Kernel/NodeAccessTest.php
Normal file
262
core/modules/node/tests/src/Kernel/NodeAccessTest.php
Normal file
|
@ -0,0 +1,262 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\node\Kernel;
|
||||
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\node\NodeInterface;
|
||||
use Drupal\simpletest\ContentTypeCreationTrait;
|
||||
use Drupal\simpletest\NodeCreationTrait;
|
||||
use Drupal\simpletest\UserCreationTrait;
|
||||
use Drupal\user\RoleInterface;
|
||||
|
||||
/**
|
||||
* Tests basic node_access functionality.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeAccessTest extends KernelTestBase {
|
||||
|
||||
use NodeCreationTrait {
|
||||
getNodeByTitle as drupalGetNodeByTitle;
|
||||
createNode as drupalCreateNode;
|
||||
}
|
||||
use UserCreationTrait {
|
||||
createUser as drupalCreateUser;
|
||||
createRole as drupalCreateRole;
|
||||
createAdminRole as drupalCreateAdminRole;
|
||||
}
|
||||
use ContentTypeCreationTrait {
|
||||
createContentType as drupalCreateContentType;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'node',
|
||||
'datetime',
|
||||
'user',
|
||||
'system',
|
||||
'filter',
|
||||
'field',
|
||||
'text',
|
||||
];
|
||||
|
||||
/**
|
||||
* Access handler.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityAccessControlHandlerInterface
|
||||
*/
|
||||
protected $accessHandler;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installSchema('system', 'sequences');
|
||||
$this->installSchema('node', 'node_access');
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('node');
|
||||
$this->installConfig('filter');
|
||||
$this->installConfig('node');
|
||||
$this->accessHandler = $this->container->get('entity_type.manager')
|
||||
->getAccessControlHandler('node');
|
||||
// Clear permissions for authenticated users.
|
||||
$this->config('user.role.' . RoleInterface::AUTHENTICATED_ID)
|
||||
->set('permissions', [])
|
||||
->save();
|
||||
|
||||
// Create user 1 who has special permissions.
|
||||
$this->drupalCreateUser();
|
||||
|
||||
// Create a node type.
|
||||
$this->drupalCreateContentType(array(
|
||||
'type' => 'page',
|
||||
'name' => 'Basic page',
|
||||
'display_submitted' => FALSE,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs basic tests for node_access function.
|
||||
*/
|
||||
public function testNodeAccess() {
|
||||
// Ensures user without 'access content' permission can do nothing.
|
||||
$web_user1 = $this->drupalCreateUser([
|
||||
'create page content',
|
||||
'edit any page content',
|
||||
'delete any page content',
|
||||
]);
|
||||
$node1 = $this->drupalCreateNode(['type' => 'page']);
|
||||
$this->assertNodeCreateAccess($node1->bundle(), FALSE, $web_user1);
|
||||
$this->assertNodeAccess([
|
||||
'view' => FALSE,
|
||||
'update' => FALSE,
|
||||
'delete' => FALSE,
|
||||
], $node1, $web_user1);
|
||||
|
||||
// Ensures user with 'bypass node access' permission can do everything.
|
||||
$web_user2 = $this->drupalCreateUser(['bypass node access']);
|
||||
$node2 = $this->drupalCreateNode(['type' => 'page']);
|
||||
$this->assertNodeCreateAccess($node2->bundle(), TRUE, $web_user2);
|
||||
$this->assertNodeAccess([
|
||||
'view' => TRUE,
|
||||
'update' => TRUE,
|
||||
'delete' => TRUE,
|
||||
], $node2, $web_user2);
|
||||
|
||||
// User cannot 'view own unpublished content'.
|
||||
$web_user3 = $this->drupalCreateUser(['access content']);
|
||||
$node3 = $this->drupalCreateNode([
|
||||
'status' => 0,
|
||||
'uid' => $web_user3->id(),
|
||||
]);
|
||||
$this->assertNodeAccess(['view' => FALSE], $node3, $web_user3);
|
||||
|
||||
// User cannot create content without permission.
|
||||
$this->assertNodeCreateAccess($node3->bundle(), FALSE, $web_user3);
|
||||
|
||||
// User can 'view own unpublished content', but another user cannot.
|
||||
$web_user4 = $this->drupalCreateUser([
|
||||
'access content',
|
||||
'view own unpublished content',
|
||||
]);
|
||||
$web_user5 = $this->drupalCreateUser([
|
||||
'access content',
|
||||
'view own unpublished content',
|
||||
]);
|
||||
$node4 = $this->drupalCreateNode([
|
||||
'status' => 0,
|
||||
'uid' => $web_user4->id(),
|
||||
]);
|
||||
$this->assertNodeAccess([
|
||||
'view' => TRUE,
|
||||
'update' => FALSE,
|
||||
], $node4, $web_user4);
|
||||
$this->assertNodeAccess(['view' => FALSE], $node4, $web_user5);
|
||||
|
||||
// Tests the default access provided for a published node.
|
||||
$node5 = $this->drupalCreateNode();
|
||||
$this->assertNodeAccess([
|
||||
'view' => TRUE,
|
||||
'update' => FALSE,
|
||||
'delete' => FALSE,
|
||||
], $node5, $web_user3);
|
||||
|
||||
// Tests the "edit any BUNDLE" and "delete any BUNDLE" permissions.
|
||||
$web_user6 = $this->drupalCreateUser([
|
||||
'access content',
|
||||
'edit any page content',
|
||||
'delete any page content',
|
||||
]);
|
||||
$node6 = $this->drupalCreateNode(['type' => 'page']);
|
||||
$this->assertNodeAccess([
|
||||
'view' => TRUE,
|
||||
'update' => TRUE,
|
||||
'delete' => TRUE,
|
||||
], $node6, $web_user6);
|
||||
|
||||
// Tests the "edit own BUNDLE" and "delete own BUNDLE" permission.
|
||||
$web_user7 = $this->drupalCreateUser([
|
||||
'access content',
|
||||
'edit own page content',
|
||||
'delete own page content',
|
||||
]);
|
||||
// User should not be able to edit or delete nodes they do not own.
|
||||
$this->assertNodeAccess([
|
||||
'view' => TRUE,
|
||||
'update' => FALSE,
|
||||
'delete' => FALSE,
|
||||
], $node6, $web_user7);
|
||||
|
||||
// User should be able to edit or delete nodes they own.
|
||||
$node7 = $this->drupalCreateNode([
|
||||
'type' => 'page',
|
||||
'uid' => $web_user7->id(),
|
||||
]);
|
||||
$this->assertNodeAccess([
|
||||
'view' => TRUE,
|
||||
'update' => TRUE,
|
||||
'delete' => TRUE,
|
||||
], $node7, $web_user7);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test operations not supported by node grants.
|
||||
*/
|
||||
public function testUnsupportedOperation() {
|
||||
$this->enableModules(['node_access_test_empty']);
|
||||
$web_user = $this->drupalCreateUser(['access content']);
|
||||
$node = $this->drupalCreateNode();
|
||||
$this->assertNodeAccess(['random_operation' => FALSE], $node, $web_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that node access correctly grants or denies access.
|
||||
*
|
||||
* @param array $ops
|
||||
* An associative array of the expected node access grants for the node
|
||||
* and account, with each key as the name of an operation (e.g. 'view',
|
||||
* 'delete') and each value a Boolean indicating whether access to that
|
||||
* operation should be granted.
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* The node object to check.
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The user account for which to check access.
|
||||
*/
|
||||
public function assertNodeAccess(array $ops, NodeInterface $node, AccountInterface $account) {
|
||||
foreach ($ops as $op => $result) {
|
||||
$this->assertEquals($result, $this->accessHandler->access($node, $op, $account), $this->nodeAccessAssertMessage($op, $result, $node->language()
|
||||
->getId()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that node create access correctly grants or denies access.
|
||||
*
|
||||
* @param string $bundle
|
||||
* The node bundle to check access to.
|
||||
* @param bool $result
|
||||
* Whether access should be granted or not.
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The user account for which to check access.
|
||||
* @param string|null $langcode
|
||||
* (optional) The language code indicating which translation of the node
|
||||
* to check. If NULL, the untranslated (fallback) access is checked.
|
||||
*/
|
||||
public function assertNodeCreateAccess($bundle, $result, AccountInterface $account, $langcode = NULL) {
|
||||
$this->assertEquals($result, $this->accessHandler->createAccess($bundle, $account, [
|
||||
'langcode' => $langcode,
|
||||
]), $this->nodeAccessAssertMessage('create', $result, $langcode));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an assert message to display which node access was tested.
|
||||
*
|
||||
* @param string $operation
|
||||
* The operation to check access for.
|
||||
* @param bool $result
|
||||
* Whether access should be granted or not.
|
||||
* @param string|null $langcode
|
||||
* (optional) The language code indicating which translation of the node
|
||||
* to check. If NULL, the untranslated (fallback) access is checked.
|
||||
*
|
||||
* @return string
|
||||
* An assert message string which contains information in plain English
|
||||
* about the node access permission test that was performed.
|
||||
*/
|
||||
public function nodeAccessAssertMessage($operation, $result, $langcode = NULL) {
|
||||
return new FormattableMarkup(
|
||||
'Node access returns @result with operation %op, language code %langcode.',
|
||||
[
|
||||
'@result' => $result ? 'true' : 'false',
|
||||
'%op' => $operation,
|
||||
'%langcode' => !empty($langcode) ? $langcode : 'empty',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
}
|
Reference in a new issue