upgrades core to 8.4.2

This commit is contained in:
Bachir Soussi Chiadmi
2017-11-14 16:09:54 +01:00
parent bd60eff9b3
commit c2b4e25be4
3504 changed files with 140306 additions and 38684 deletions
+18 -101
View File
@@ -2,9 +2,7 @@
namespace Drupal\node\Entity;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityChangedTrait;
use Drupal\Core\Entity\EntityPublishedTrait;
use Drupal\Core\Entity\EditorialContentEntityBase;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
@@ -61,6 +59,11 @@ use Drupal\user\UserInterface;
* "published" = "status",
* "uid" = "uid",
* },
* revision_metadata_keys = {
* "revision_user" = "revision_uid",
* "revision_created" = "revision_timestamp",
* "revision_log_message" = "revision_log"
* },
* bundle_entity_type = "node_type",
* field_ui_base_route = "entity.node_type.edit_form",
* common_reference_target = TRUE,
@@ -71,13 +74,11 @@ use Drupal\user\UserInterface;
* "edit-form" = "/node/{node}/edit",
* "version-history" = "/node/{node}/revisions",
* "revision" = "/node/{node}/revisions/{node_revision}/view",
* "create" = "/node",
* }
* )
*/
class Node extends ContentEntityBase implements NodeInterface {
use EntityChangedTrait;
use EntityPublishedTrait;
class Node extends EditorialContentEntityBase implements NodeInterface {
/**
* Whether the node is being previewed or not.
@@ -278,21 +279,6 @@ class Node extends ContentEntityBase implements NodeInterface {
return $this;
}
/**
* {@inheritdoc}
*/
public function getRevisionCreationTime() {
return $this->get('revision_timestamp')->value;
}
/**
* {@inheritdoc}
*/
public function setRevisionCreationTime($timestamp) {
$this->set('revision_timestamp', $timestamp);
return $this;
}
/**
* {@inheritdoc}
*/
@@ -300,13 +286,6 @@ class Node extends ContentEntityBase implements NodeInterface {
return $this->getRevisionUser();
}
/**
* {@inheritdoc}
*/
public function getRevisionUser() {
return $this->get('revision_uid')->entity;
}
/**
* {@inheritdoc}
*/
@@ -315,50 +294,11 @@ class Node extends ContentEntityBase implements NodeInterface {
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;
}
/**
* {@inheritdoc}
*/
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields = parent::baseFieldDefinitions($entity_type);
$fields += static::publishedBaseFieldDefinitions($entity_type);
$fields['title'] = BaseFieldDefinition::create('string')
->setLabel(t('Title'))
@@ -400,6 +340,16 @@ class Node extends ContentEntityBase implements NodeInterface {
])
->setDisplayConfigurable('form', TRUE);
$fields['status']
->setDisplayOptions('form', [
'type' => 'boolean_checkbox',
'settings' => [
'display_label' => TRUE,
],
'weight' => 120,
])
->setDisplayConfigurable('form', TRUE);
$fields['created'] = BaseFieldDefinition::create('created')
->setLabel(t('Authored on'))
->setDescription(t('The time that the node was created.'))
@@ -450,39 +400,6 @@ class Node extends ContentEntityBase implements NodeInterface {
])
->setDisplayConfigurable('form', TRUE);
$fields['revision_timestamp'] = BaseFieldDefinition::create('created')
->setLabel(t('Revision timestamp'))
->setDescription(t('The time that the current revision was created.'))
->setQueryable(FALSE)
->setRevisionable(TRUE);
$fields['revision_uid'] = BaseFieldDefinition::create('entity_reference')
->setLabel(t('Revision user ID'))
->setDescription(t('The user ID of the author of the current revision.'))
->setSetting('target_type', 'user')
->setQueryable(FALSE)
->setRevisionable(TRUE);
$fields['revision_log'] = BaseFieldDefinition::create('string_long')
->setLabel(t('Revision log message'))
->setDescription(t('Briefly describe the changes you have made.'))
->setRevisionable(TRUE)
->setDefaultValue('')
->setDisplayOptions('form', [
'type' => 'string_textarea',
'weight' => 25,
'settings' => [
'rows' => 4,
],
]);
$fields['revision_translation_affected'] = BaseFieldDefinition::create('boolean')
->setLabel(t('Revision translation affected'))
->setDescription(t('Indicates if the last edit of a translation belongs to current revision.'))
->setReadOnly(TRUE)
->setRevisionable(TRUE)
->setTranslatable(TRUE);
return $fields;
}
@@ -15,7 +15,7 @@ class NodeRouteProvider implements EntityRouteProviderInterface {
/**
* {@inheritdoc}
*/
public function getRoutes( EntityTypeInterface $entity_type) {
public function getRoutes(EntityTypeInterface $entity_type) {
$route_collection = new RouteCollection();
$route = (new Route('/node/{node}'))
->addDefaults([
@@ -0,0 +1,128 @@
<?php
namespace Drupal\node\EventSubscriber;
use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\ParamConverter\ParamNotConvertedException;
use Drupal\Core\Routing\UrlGeneratorInterface;
use Drupal\Core\State\StateInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\KernelEvents;
/**
* Redirect node translations that have been consolidated by migration.
*
* If we migrated node translations from Drupal 6 or 7, these nodes are now
* combined with their source language node. Since there still might be
* references to the URLs of these now consolidated nodes, this service catches
* the 404s and try to redirect them to the right node in the right language.
*
* The mapping of the old nids to the new ones is made by the
* NodeTranslationMigrateSubscriber class during the migration and is stored
* in the "node_translation_redirect" key/value collection.
*
* @see \Drupal\node\NodeServiceProvider
* @see \Drupal\node\EventSubscriber\NodeTranslationMigrateSubscriber
*/
class NodeTranslationExceptionSubscriber implements EventSubscriberInterface {
/**
* The key value factory.
*
* @var \Drupal\Core\KeyValueStore\KeyValueFactoryInterface
*/
protected $keyValue;
/**
* The language manager.
*
* @var \Drupal\Core\Language\LanguageManagerInterface
*/
protected $languageManager;
/**
* The URL generator.
*
* @var \Drupal\Core\Routing\UrlGeneratorInterface
*/
protected $urlGenerator;
/**
* The state service.
*
* @var \Drupal\Core\State\StateInterface
*/
protected $state;
/**
* Constructs the NodeTranslationExceptionSubscriber.
*
* @param \Drupal\Core\KeyValueStore\KeyValueFactoryInterface $key_value
* The key value factory.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager.
* @param \Drupal\Core\Routing\UrlGeneratorInterface $url_generator
* The URL generator.
* @param \Drupal\Core\State\StateInterface $state
* The state service.
*/
public function __construct(KeyValueFactoryInterface $key_value, LanguageManagerInterface $language_manager, UrlGeneratorInterface $url_generator, StateInterface $state) {
$this->keyValue = $key_value;
$this->languageManager = $language_manager;
$this->urlGenerator = $url_generator;
$this->state = $state;
}
/**
* Redirects not found node translations using the key value collection.
*
* @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
* The exception event.
*/
public function onException(GetResponseForExceptionEvent $event) {
$exception = $event->getException();
// If this is not a 404, we don't need to check for a redirection.
if (!($exception instanceof NotFoundHttpException)) {
return;
}
$previous_exception = $exception->getPrevious();
if ($previous_exception instanceof ParamNotConvertedException) {
$route_name = $previous_exception->getRouteName();
$parameters = $previous_exception->getRawParameters();
if ($route_name === 'entity.node.canonical' && isset($parameters['node'])) {
// If the node_translation_redirect state is not set, we don't need to check
// for a redirection.
if (!$this->state->get('node_translation_redirect')) {
return;
}
$old_nid = $parameters['node'];
$collection = $this->keyValue->get('node_translation_redirect');
if ($old_nid && $value = $collection->get($old_nid)) {
list($nid, $langcode) = $value;
$language = $this->languageManager->getLanguage($langcode);
$url = $this->urlGenerator->generateFromRoute('entity.node.canonical', ['node' => $nid], ['language' => $language]);
$response = new RedirectResponse($url, 301);
$event->setResponse($response);
}
}
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
$events = [];
$events[KernelEvents::EXCEPTION] = ['onException'];
return $events;
}
}
@@ -0,0 +1,113 @@
<?php
namespace Drupal\node\EventSubscriber;
use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
use Drupal\Core\State\StateInterface;
use Drupal\migrate\Event\EventBase;
use Drupal\migrate\Event\MigrateEvents;
use Drupal\migrate\Event\MigrateImportEvent;
use Drupal\migrate\Event\MigratePostRowSaveEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Creates a key value collection for migrated node translation redirections.
*
* If we are migrating node translations from Drupal 6 or 7, these nodes will be
* combined with their source node. Since there still might be references to the
* URLs of these now consolidated nodes, this service saves the mapping between
* the old nids to the new ones to be able to redirect them to the right node in
* the right language.
*
* The mapping is stored in the "node_translation_redirect" key/value collection
* and the redirection is made by the NodeTranslationExceptionSubscriber class.
*
* @see \Drupal\node\NodeServiceProvider
* @see \Drupal\node\EventSubscriber\NodeTranslationExceptionSubscriber
*/
class NodeTranslationMigrateSubscriber implements EventSubscriberInterface {
/**
* The key value factory.
*
* @var \Drupal\Core\KeyValueStore\KeyValueFactoryInterface
*/
protected $keyValue;
/**
* The state service.
*
* @var \Drupal\Core\State\StateInterface
*/
protected $state;
/**
* Constructs the NodeTranslationMigrateSubscriber.
*
* @param \Drupal\Core\KeyValueStore\KeyValueFactoryInterface $key_value
* The key value factory.
* @param \Drupal\Core\State\StateInterface $state
* The state service.
*/
public function __construct(KeyValueFactoryInterface $key_value, StateInterface $state) {
$this->keyValue = $key_value;
$this->state = $state;
}
/**
* Helper method to check if we are migrating translated nodes.
*
* @param \Drupal\migrate\Event\EventBase $event
* The migrate event.
*
* @return bool
* True if we are migrating translated nodes, false otherwise.
*/
protected function isNodeTranslationsMigration(EventBase $event) {
$migration = $event->getMigration();
$source_configuration = $migration->getSourceConfiguration();
$destination_configuration = $migration->getDestinationConfiguration();
return !empty($source_configuration['translations']) && $destination_configuration['plugin'] === 'entity:node';
}
/**
* Maps the old nid to the new one in the key value collection.
*
* @param \Drupal\migrate\Event\MigratePostRowSaveEvent $event
* The migrate post row save event.
*/
public function onPostRowSave(MigratePostRowSaveEvent $event) {
if ($this->isNodeTranslationsMigration($event)) {
$row = $event->getRow();
$source = $row->getSource();
$destination = $row->getDestination();
$collection = $this->keyValue->get('node_translation_redirect');
$collection->set($source['nid'], [$destination['nid'], $destination['langcode']]);
}
}
/**
* Set the node_translation_redirect state to enable the redirections.
*
* @param \Drupal\migrate\Event\MigrateImportEvent $event
* The migrate import event.
*/
public function onPostImport(MigrateImportEvent $event) {
if ($this->isNodeTranslationsMigration($event)) {
$this->state->set('node_translation_redirect', TRUE);
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
$events = [];
$events[MigrateEvents::POST_ROW_SAVE] = ['onPostRowSave'];
$events[MigrateEvents::POST_IMPORT] = ['onPostImport'];
return $events;
}
}
@@ -2,6 +2,7 @@
namespace Drupal\node\Form;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Form\ConfirmFormBase;
@@ -50,11 +51,13 @@ class NodeRevisionRevertForm extends ConfirmFormBase {
* The node storage.
* @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
* The date formatter service.
* @param \Drupal\Component\Datetime\TimeInterface $time
* The time service.
*/
public function __construct(EntityStorageInterface $node_storage, DateFormatterInterface $date_formatter) {
public function __construct(EntityStorageInterface $node_storage, DateFormatterInterface $date_formatter, TimeInterface $time) {
$this->nodeStorage = $node_storage;
$this->dateFormatter = $date_formatter;
$this->time = \Drupal::service('datetime.time');
$this->time = $time;
}
/**
@@ -63,7 +66,8 @@ class NodeRevisionRevertForm extends ConfirmFormBase {
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity.manager')->getStorage('node'),
$container->get('date.formatter')
$container->get('date.formatter'),
$container->get('datetime.time')
);
}
@@ -2,6 +2,7 @@
namespace Drupal\node\Form;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Form\FormStateInterface;
@@ -37,9 +38,11 @@ class NodeRevisionRevertTranslationForm extends NodeRevisionRevertForm {
* The date formatter service.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager.
* @param \Drupal\Component\Datetime\TimeInterface $time
* The time service.
*/
public function __construct(EntityStorageInterface $node_storage, DateFormatterInterface $date_formatter, LanguageManagerInterface $language_manager) {
parent::__construct($node_storage, $date_formatter);
public function __construct(EntityStorageInterface $node_storage, DateFormatterInterface $date_formatter, LanguageManagerInterface $language_manager, TimeInterface $time) {
parent::__construct($node_storage, $date_formatter, $time);
$this->languageManager = $language_manager;
}
@@ -50,7 +53,8 @@ class NodeRevisionRevertTranslationForm extends NodeRevisionRevertForm {
return new static(
$container->get('entity.manager')->getStorage('node'),
$container->get('date.formatter'),
$container->get('language_manager')
$container->get('language_manager'),
$container->get('datetime.time')
);
}
+58 -58
View File
@@ -7,6 +7,7 @@ use Drupal\Core\Entity\ContentEntityForm;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\user\PrivateTempStoreFactory;
use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -22,6 +23,13 @@ class NodeForm extends ContentEntityForm {
*/
protected $tempStoreFactory;
/**
* The Current User object.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $currentUser;
/**
* Constructs a NodeForm object.
*
@@ -33,10 +41,13 @@ class NodeForm extends ContentEntityForm {
* The entity type bundle service.
* @param \Drupal\Component\Datetime\TimeInterface $time
* The time service.
* @param \Drupal\Core\Session\AccountInterface $current_user
* The current user.
*/
public function __construct(EntityManagerInterface $entity_manager, PrivateTempStoreFactory $temp_store_factory, EntityTypeBundleInfoInterface $entity_type_bundle_info = NULL, TimeInterface $time = NULL) {
public function __construct(EntityManagerInterface $entity_manager, PrivateTempStoreFactory $temp_store_factory, EntityTypeBundleInfoInterface $entity_type_bundle_info = NULL, TimeInterface $time = NULL, AccountInterface $current_user) {
parent::__construct($entity_manager, $entity_type_bundle_info, $time);
$this->tempStoreFactory = $temp_store_factory;
$this->currentUser = $current_user;
}
/**
@@ -47,7 +58,8 @@ class NodeForm extends ContentEntityForm {
$container->get('entity.manager'),
$container->get('user.private_tempstore'),
$container->get('entity_type.bundle.info'),
$container->get('datetime.time')
$container->get('datetime.time'),
$container->get('current_user')
);
}
@@ -86,7 +98,10 @@ class NodeForm extends ContentEntityForm {
$node = $this->entity;
if ($this->operation == 'edit') {
$form['#title'] = $this->t('<em>Edit @type</em> @title', ['@type' => node_get_type_label($node), '@title' => $node->label()]);
$form['#title'] = $this->t('<em>Edit @type</em> @title', [
'@type' => node_get_type_label($node),
'@title' => $node->label()
]);
}
// Changed must be sent to the client, for later overwrite error checking.
@@ -99,6 +114,43 @@ class NodeForm extends ContentEntityForm {
$form['advanced']['#attributes']['class'][] = 'entity-meta';
$form['meta'] = [
'#type' => 'details',
'#group' => 'advanced',
'#weight' => -10,
'#title' => $this->t('Status'),
'#attributes' => ['class' => ['entity-meta__header']],
'#tree' => TRUE,
'#access' => $this->currentUser->hasPermission('administer nodes'),
];
$form['meta']['published'] = [
'#type' => 'item',
'#markup' => $node->isPublished() ? $this->t('Published') : $this->t('Not published'),
'#access' => !$node->isNew(),
'#wrapper_attributes' => ['class' => ['entity-meta__title']],
];
$form['meta']['changed'] = [
'#type' => 'item',
'#title' => $this->t('Last saved'),
'#markup' => !$node->isNew() ? format_date($node->getChangedTime(), 'short') : $this->t('Not saved yet'),
'#wrapper_attributes' => ['class' => ['entity-meta__last-saved']],
];
$form['meta']['author'] = [
'#type' => 'item',
'#title' => $this->t('Author'),
'#markup' => $node->getOwner()->getUsername(),
'#wrapper_attributes' => ['class' => ['entity-meta__author']],
];
$form['footer'] = [
'#type' => 'container',
'#weight' => 99,
'#attributes' => [
'class' => ['node-form-footer']
]
];
$form['status']['#group'] = 'footer';
// Node author information for administrators.
$form['author'] = [
'#type' => 'details',
@@ -147,8 +199,6 @@ class NodeForm extends ContentEntityForm {
$form['#attached']['library'][] = 'node/form';
$form['#entity_builders']['update_status'] = '::updateStatus';
return $form;
}
@@ -165,6 +215,9 @@ class NodeForm extends ContentEntityForm {
* The current state of the form.
*
* @see \Drupal\node\NodeForm::form()
*
* @deprecated in Drupal 8.4.x, will be removed before Drupal 9.0.0.
* The "Publish" button was removed.
*/
public function updateStatus($entity_type_id, NodeInterface $node, array $form, FormStateInterface $form_state) {
$element = $form_state->getTriggeringElement();
@@ -183,59 +236,6 @@ class NodeForm extends ContentEntityForm {
$element['submit']['#access'] = $preview_mode != DRUPAL_REQUIRED || $form_state->get('has_been_previewed');
// If saving is an option, privileged users get dedicated form submit
// buttons to adjust the publishing status while saving in one go.
// @todo This adjustment makes it close to impossible for contributed
// modules to integrate with "the Save operation" of this form. Modules
// need a way to plug themselves into 1) the ::submit() step, and
// 2) the ::save() step, both decoupled from the pressed form button.
if ($element['submit']['#access'] && \Drupal::currentUser()->hasPermission('administer nodes')) {
// isNew | prev status » default & publish label & unpublish label
// 1 | 1 » publish & Save and publish & Save as unpublished
// 1 | 0 » unpublish & Save and publish & Save as unpublished
// 0 | 1 » publish & Save and keep published & Save and unpublish
// 0 | 0 » unpublish & Save and keep unpublished & Save and publish
// Add a "Publish" button.
$element['publish'] = $element['submit'];
// If the "Publish" button is clicked, we want to update the status to "published".
$element['publish']['#published_status'] = TRUE;
$element['publish']['#dropbutton'] = 'save';
if ($node->isNew()) {
$element['publish']['#value'] = t('Save and publish');
}
else {
$element['publish']['#value'] = $node->isPublished() ? t('Save and keep published') : t('Save and publish');
}
$element['publish']['#weight'] = 0;
// Add a "Unpublish" button.
$element['unpublish'] = $element['submit'];
// If the "Unpublish" button is clicked, we want to update the status to "unpublished".
$element['unpublish']['#published_status'] = FALSE;
$element['unpublish']['#dropbutton'] = 'save';
if ($node->isNew()) {
$element['unpublish']['#value'] = t('Save as unpublished');
}
else {
$element['unpublish']['#value'] = !$node->isPublished() ? t('Save and keep unpublished') : t('Save and unpublish');
}
$element['unpublish']['#weight'] = 10;
// If already published, the 'publish' button is primary.
if ($node->isPublished()) {
unset($element['unpublish']['#button_type']);
}
// Otherwise, the 'unpublish' button is primary and should come first.
else {
unset($element['publish']['#button_type']);
$element['unpublish']['#weight'] = -10;
}
// Remove the "Save" button.
$element['submit']['#access'] = FALSE;
}
$element['preview'] = [
'#type' => 'submit',
'#access' => $preview_mode != DRUPAL_DISABLED && ($node->access('create') || $node->access('update')),
@@ -138,7 +138,7 @@ class NodeGrantDatabaseStorage implements NodeGrantDatabaseStorageInterface {
$grants = static::buildGrantsQueryCondition(node_access_grants('view', $account));
if (count($grants) > 0 ) {
if (count($grants) > 0) {
$query->condition($grants);
}
return $query->execute()->fetchField();
@@ -31,11 +31,11 @@ interface NodeGrantDatabaseStorageInterface {
* @param array $tables
* A list of tables that need to be part of the alter.
* @param string $op
* The operation to be performed on the node. Possible values are:
* - "view"
* - "update"
* - "delete"
* - "create"
* The operation to be performed on the node. Possible values are:
* - "view"
* - "update"
* - "delete"
* - "create"
* @param \Drupal\Core\Session\AccountInterface $account
* A user object representing the user for whom the operation is to be
* performed.
+1 -1
View File
@@ -110,7 +110,7 @@ class NodeListBuilder extends EntityListBuilder {
$row['title']['data'] = [
'#type' => 'link',
'#title' => $entity->label(),
'#suffix' => ' ' . drupal_render($mark),
'#suffix' => ' ' . \Drupal::service('renderer')->render($mark),
'#url' => $uri,
];
$row['type'] = node_get_type_label($entity);
+3 -2
View File
@@ -62,14 +62,15 @@ class NodePermissions {
],
"view $type_id revisions" => [
'title' => $this->t('%type_name: View revisions', $type_params),
'description' => t('To view a revision, you also need permission to view the content item.'),
],
"revert $type_id revisions" => [
'title' => $this->t('%type_name: Revert revisions', $type_params),
'description' => t('Role requires permission <em>view revisions</em> and <em>edit rights</em> for nodes in question, or <em>administer nodes</em>.'),
'description' => t('To revert a revision, you also need permission to edit the content item.'),
],
"delete $type_id revisions" => [
'title' => $this->t('%type_name: Delete revisions', $type_params),
'description' => $this->t('Role requires permission to <em>view revisions</em> and <em>delete rights</em> for nodes in question, or <em>administer nodes</em>.'),
'description' => $this->t('To delete a revision, you also need permission to delete the content item.'),
],
];
}
@@ -0,0 +1,42 @@
<?php
namespace Drupal\node;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\DependencyInjection\ServiceProviderInterface;
use Drupal\node\EventSubscriber\NodeTranslationExceptionSubscriber;
use Drupal\node\EventSubscriber\NodeTranslationMigrateSubscriber;
use Symfony\Component\DependencyInjection\Reference;
/**
* Registers services in the container.
*/
class NodeServiceProvider implements ServiceProviderInterface {
/**
* {@inheritdoc}
*/
public function register(ContainerBuilder $container) {
// Register the node.node_translation_migrate service in the container if
// the migrate and language modules are enabled.
$modules = $container->getParameter('container.modules');
if (isset($modules['migrate']) && isset($modules['language'])) {
$container->register('node.node_translation_migrate', NodeTranslationMigrateSubscriber::class)
->addTag('event_subscriber')
->addArgument(new Reference('keyvalue'))
->addArgument(new Reference('state'));
}
// Register the node.node_translation_exception service in the container if
// the language module is enabled.
if (isset($modules['language'])) {
$container->register('node.node_translation_exception', NodeTranslationExceptionSubscriber::class)
->addTag('event_subscriber')
->addArgument(new Reference('keyvalue'))
->addArgument(new Reference('language_manager'))
->addArgument(new Reference('url_generator'))
->addArgument(new Reference('state'));
}
}
}
+8 -6
View File
@@ -29,12 +29,14 @@ class NodeViewBuilder extends EntityViewBuilder {
if ($display->getComponent('links')) {
$build[$id]['links'] = [
'#lazy_builder' => [get_called_class() . '::renderLinks', [
$entity->id(),
$view_mode,
$entity->language()->getId(),
!empty($entity->in_preview),
]],
'#lazy_builder' => [
get_called_class() . '::renderLinks', [
$entity->id(),
$view_mode,
$entity->language()->getId(),
!empty($entity->in_preview),
],
],
];
}
@@ -2,8 +2,8 @@
namespace Drupal\node\Plugin\Action;
use Drupal\Core\Action\ActionBase;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Field\FieldUpdateActionBase;
use Drupal\node\NodeInterface;
/**
* Demotes a node.
@@ -14,25 +14,13 @@ use Drupal\Core\Session\AccountInterface;
* type = "node"
* )
*/
class DemoteNode extends ActionBase {
class DemoteNode extends FieldUpdateActionBase {
/**
* {@inheritdoc}
*/
public function execute($entity = NULL) {
$entity->setPromoted(FALSE);
$entity->save();
}
/**
* {@inheritdoc}
*/
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
/** @var \Drupal\node\NodeInterface $object */
$result = $object->access('update', $account, TRUE)
->andIf($object->promote->access('edit', $account, TRUE));
return $return_as_object ? $result : $result->isAllowed();
protected function getFieldsToUpdate() {
return ['promote' => NodeInterface::NOT_PROMOTED];
}
}
@@ -2,8 +2,8 @@
namespace Drupal\node\Plugin\Action;
use Drupal\Core\Action\ActionBase;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Field\FieldUpdateActionBase;
use Drupal\node\NodeInterface;
/**
* Promotes a node.
@@ -14,24 +14,13 @@ use Drupal\Core\Session\AccountInterface;
* type = "node"
* )
*/
class PromoteNode extends ActionBase {
class PromoteNode extends FieldUpdateActionBase {
/**
* {@inheritdoc}
*/
public function execute($entity = NULL) {
$entity->setPromoted(TRUE);
$entity->save();
}
/**
* {@inheritdoc}
*/
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
/** @var \Drupal\node\NodeInterface $object */
$access = $object->access('update', $account, TRUE)
->andif($object->promote->access('edit', $account, TRUE));
return $return_as_object ? $access : $access->isAllowed();
protected function getFieldsToUpdate() {
return ['promote' => NodeInterface::PROMOTED];
}
}
@@ -2,8 +2,8 @@
namespace Drupal\node\Plugin\Action;
use Drupal\Core\Action\ActionBase;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Field\FieldUpdateActionBase;
use Drupal\node\NodeInterface;
/**
* Publishes a node.
@@ -14,24 +14,13 @@ use Drupal\Core\Session\AccountInterface;
* type = "node"
* )
*/
class PublishNode extends ActionBase {
class PublishNode extends FieldUpdateActionBase {
/**
* {@inheritdoc}
*/
public function execute($entity = NULL) {
$entity->setPublished()->save();
}
/**
* {@inheritdoc}
*/
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
/** @var \Drupal\node\NodeInterface $object */
$result = $object->access('update', $account, TRUE)
->andIf($object->status->access('edit', $account, TRUE));
return $return_as_object ? $result : $result->isAllowed();
protected function getFieldsToUpdate() {
return ['status' => NodeInterface::PUBLISHED];
}
}
@@ -2,8 +2,8 @@
namespace Drupal\node\Plugin\Action;
use Drupal\Core\Action\ActionBase;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Field\FieldUpdateActionBase;
use Drupal\node\NodeInterface;
/**
* Makes a node sticky.
@@ -14,23 +14,13 @@ use Drupal\Core\Session\AccountInterface;
* type = "node"
* )
*/
class StickyNode extends ActionBase {
class StickyNode extends FieldUpdateActionBase {
/**
* {@inheritdoc}
*/
public function execute($entity = NULL) {
$entity->setSticky(TRUE)->save();
}
/**
* {@inheritdoc}
*/
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
/** @var \Drupal\node\NodeInterface $object */
$access = $object->access('update', $account, TRUE)
->andif($object->sticky->access('edit', $account, TRUE));
return $return_as_object ? $access : $access->isAllowed();
protected function getFieldsToUpdate() {
return ['sticky' => NodeInterface::STICKY];
}
}
@@ -24,7 +24,7 @@ class UnpublishByKeywordNode extends ConfigurableActionBase {
public function execute($node = NULL) {
foreach ($this->configuration['keywords'] as $keyword) {
$elements = node_view(clone $node);
if (strpos(drupal_render($elements), $keyword) !== FALSE || strpos($node->label(), $keyword) !== FALSE) {
if (strpos(\Drupal::service('renderer')->render($elements), $keyword) !== FALSE || strpos($node->label(), $keyword) !== FALSE) {
$node->setPublished(FALSE);
$node->save();
break;
@@ -2,8 +2,8 @@
namespace Drupal\node\Plugin\Action;
use Drupal\Core\Action\ActionBase;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Field\FieldUpdateActionBase;
use Drupal\node\NodeInterface;
/**
* Unpublishes a node.
@@ -14,24 +14,13 @@ use Drupal\Core\Session\AccountInterface;
* type = "node"
* )
*/
class UnpublishNode extends ActionBase {
class UnpublishNode extends FieldUpdateActionBase {
/**
* {@inheritdoc}
*/
public function execute($entity = NULL) {
$entity->setUnpublished()->save();
}
/**
* {@inheritdoc}
*/
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
/** @var \Drupal\node\NodeInterface $object */
$access = $object->access('update', $account, TRUE)
->andIf($object->status->access('edit', $account, TRUE));
return $return_as_object ? $access : $access->isAllowed();
protected function getFieldsToUpdate() {
return ['status' => NodeInterface::NOT_PUBLISHED];
}
}
@@ -2,8 +2,8 @@
namespace Drupal\node\Plugin\Action;
use Drupal\Core\Action\ActionBase;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Field\FieldUpdateActionBase;
use Drupal\node\NodeInterface;
/**
* Makes a node not sticky.
@@ -14,24 +14,13 @@ use Drupal\Core\Session\AccountInterface;
* type = "node"
* )
*/
class UnstickyNode extends ActionBase {
class UnstickyNode extends FieldUpdateActionBase {
/**
* {@inheritdoc}
*/
public function execute($entity = NULL) {
$entity->setSticky(FALSE)->save();
}
/**
* {@inheritdoc}
*/
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
/** @var \Drupal\node\NodeInterface $object */
$access = $object->access('update', $account, TRUE)
->andIf($object->sticky->access('edit', $account, TRUE));
return $return_as_object ? $access : $access->isAllowed();
protected function getFieldsToUpdate() {
return ['sticky' => NodeInterface::NOT_STICKY];
}
}
@@ -117,7 +117,7 @@ class NodeSearch extends ConfigurableSearchPluginBase implements AccessibleInter
/**
* {@inheritdoc}
*/
static public function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
@@ -141,6 +141,16 @@ class D7NodeDeriver extends DeriverBase implements ContainerDeriverInterface {
$values['source']['node_type'] = $node_type;
$values['destination']['default_bundle'] = $node_type;
// Comment status must be mapped to correct comment type.
// Comment type migration creates a separate comment type for each
// node type except for Forum which uses 'comment_forum'.
$comment_type = 'comment_node_' . $node_type;
if ($node_type == 'forum') {
$comment_type = 'comment_forum';
}
$nested_key = $comment_type . '/0/status';
$values['process'][$nested_key] = 'comment';
// If this migration is based on the d7_node_revision migration or
// is for translations of nodes, it should explicitly depend on the
// corresponding d7_node variant.
@@ -15,7 +15,9 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* Drupal 6 node source from database.
*
* @MigrateSource(
* id = "d6_node"
* id = "d6_node",
* source_module = "node"
*
* )
*/
class Node extends DrupalSqlBase {
@@ -176,24 +178,24 @@ class Node extends DrupalSqlBase {
}
/**
* Gets CCK field values for a node.
* Gets field values for a node.
*
* @param \Drupal\migrate\Row $node
* The node.
*
* @return array
* CCK field values, keyed by field name.
* Field values, keyed by field name.
*/
protected function getFieldValues(Row $node) {
$values = [];
foreach ($this->getFieldInfo($node->getSourceProperty('type')) as $field => $info) {
$values[$field] = $this->getCckData($info, $node);
$values[$field] = $this->getFieldData($info, $node);
}
return $values;
}
/**
* Gets CCK field and instance definitions from the database.
* Gets field and instance definitions from the database.
*
* @param string $node_type
* The node type for which to get field info.
@@ -205,14 +207,14 @@ class Node extends DrupalSqlBase {
if (!isset($this->fieldInfo)) {
$this->fieldInfo = [];
// Query the database directly for all CCK field info.
// Query the database directly for all field info.
$query = $this->select('content_node_field_instance', 'cnfi');
$query->join('content_node_field', 'cnf', 'cnf.field_name = cnfi.field_name');
$query->fields('cnfi');
$query->fields('cnf');
foreach ($query->execute() as $field) {
$this->fieldInfo[ $field['type_name'] ][ $field['field_name'] ] = $field;
$this->fieldInfo[$field['type_name']][$field['field_name']] = $field;
}
foreach ($this->fieldInfo as $type => $fields) {
@@ -230,7 +232,7 @@ class Node extends DrupalSqlBase {
}
/**
* Retrieves raw CCK field data for a node.
* Retrieves raw field data for a node.
*
* @param array $field
* A field and instance definition from getFieldInfo().
@@ -240,7 +242,7 @@ class Node extends DrupalSqlBase {
* @return array
* The field values, keyed by delta.
*/
protected function getCckData(array $field, Row $node) {
protected function getFieldData(array $field, Row $node) {
$field_table = 'content_' . $field['field_name'];
$node_table = 'content_type_' . $node->getSourceProperty('type');
@@ -276,10 +278,9 @@ class Node extends DrupalSqlBase {
return $query
// This call to isNotNull() is a kludge which relies on the convention
// that CCK field schemas usually define their most important
// column first. A better way would be to allow cckfield plugins to
// alter the query directly before it's run, but this will do for
// the time being.
// that field schemas usually define their most important column first.
// A better way would be to allow field plugins to alter the query
// directly before it's run, but this will do for the time being.
->isNotNull($field['field_name'] . '_' . $columns[0])
->condition('nid', $node->getSourceProperty('nid'))
->condition('vid', $node->getSourceProperty('vid'))
@@ -291,6 +292,24 @@ class Node extends DrupalSqlBase {
}
}
/**
* Retrieves raw field data for a node.
*
* @deprecated in Drupal 8.2.x, to be removed in Drupal 9.0.x. Use
* getFieldData() instead.
*
* @param array $field
* A field and instance definition from getFieldInfo().
* @param \Drupal\migrate\Row $node
* The node.
*
* @return array
* The field values, keyed by delta.
*/
protected function getCckData(array $field, Row $node) {
return $this->getFieldData($field, $node);
}
/**
* {@inheritdoc}
*/
@@ -1,13 +1,15 @@
<?php
namespace Drupal\node\Plugin\migrate\source\d6;
use Drupal\Core\Database\Query\SelectInterface;
/**
* Drupal 6 node revision source from database.
*
* @MigrateSource(
* id = "d6_node_revision"
* id = "d6_node_revision",
* source_module = "node"
* )
*/
class NodeRevision extends Node {
@@ -9,7 +9,8 @@ use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
* Drupal 6 Node types source from database.
*
* @MigrateSource(
* id = "d6_node_type"
* id = "d6_node_type",
* source_module = "node"
* )
*/
class NodeType extends DrupalSqlBase {
@@ -62,7 +63,7 @@ class NodeType extends DrupalSqlBase {
* {@inheritdoc}
*/
public function fields() {
return [
$fields = [
'type' => $this->t('Machine name of the node type.'),
'name' => $this->t('Human name of the node type.'),
'module' => $this->t('The module providing the node type.'),
@@ -78,6 +79,28 @@ class NodeType extends DrupalSqlBase {
'orig_type' => $this->t('The original type.'),
'teaser_length' => $this->t('Teaser length'),
];
if ($this->moduleExists('comment')) {
$fields += $this->getCommentFields();
}
return $fields;
}
/**
* Returns the fields containing comment settings for each node type.
*
* @return string[]
* An associative array of field descriptions, keyed by field.
*/
protected function getCommentFields() {
return [
'comment' => $this->t('Default comment setting'),
'comment_default_mode' => $this->t('Default display mode'),
'comment_default_per_page' => $this->t('Default comments per page'),
'comment_anonymous' => $this->t('Anonymous commenting'),
'comment_subject_field' => $this->t('Comment subject field'),
'comment_preview' => $this->t('Preview comment'),
'comment_form_location' => $this->t('Location of comment submission form'),
];
}
/**
@@ -111,6 +134,13 @@ class NodeType extends DrupalSqlBase {
$row->setSourceProperty('available_menus', [$default_node_menu]);
$row->setSourceProperty('parent', $default_node_menu . ':');
}
if ($this->moduleExists('comment')) {
foreach (array_keys($this->getCommentFields()) as $field) {
$row->setSourceProperty($field, $this->variableGet($field . '_' . $type, NULL));
}
}
return parent::prepareRow($row);
}
@@ -7,7 +7,7 @@ namespace Drupal\node\Plugin\migrate\source\d6;
*
* @MigrateSource(
* id = "d6_view_mode",
* source_provider = "content"
* source_module = "content"
* )
*/
class ViewMode extends ViewModeBase {
@@ -17,7 +17,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
*
* @MigrateSource(
* id = "d7_node",
* source_provider = "node"
* source_module = "node"
* )
*/
class Node extends FieldableEntity {
@@ -7,7 +7,7 @@ namespace Drupal\node\Plugin\migrate\source\d7;
*
* @MigrateSource(
* id = "d7_node_revision",
* source_provider = "node"
* source_module = "node"
* )
*/
class NodeRevision extends Node {
@@ -10,7 +10,7 @@ use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
*
* @MigrateSource(
* id = "d7_node_type",
* source_provider = "node"
* source_module = "node"
* )
*/
class NodeType extends DrupalSqlBase {
@@ -54,6 +54,28 @@ class NodeType extends DrupalSqlBase {
'orig_type' => $this->t('The original type.'),
'teaser_length' => $this->t('Teaser length'),
];
if ($this->moduleExists('comment')) {
$fields += $this->getCommentFields();
}
return $fields;
}
/**
* Returns the fields containing comment settings for each node type.
*
* @return string[]
* An associative array of field descriptions, keyed by field.
*/
protected function getCommentFields() {
return [
'comment' => $this->t('Default comment setting'),
'comment_default_mode' => $this->t('Default display mode'),
'comment_default_per_page' => $this->t('Default comments per page'),
'comment_anonymous' => $this->t('Anonymous commenting'),
'comment_subject_field' => $this->t('Comment subject field'),
'comment_preview' => $this->t('Preview comment'),
'comment_form_location' => $this->t('Location of comment submission form'),
];
}
/**
@@ -107,6 +129,13 @@ class NodeType extends DrupalSqlBase {
if ($parent = $this->variableGet('menu_parent_' . $type, NULL)) {
$row->setSourceProperty('parent', $parent . ':');
}
if ($this->moduleExists('comment')) {
foreach (array_keys($this->getCommentFields()) as $field) {
$row->setSourceProperty($field, $this->variableGet($field . '_' . $type, NULL));
}
}
return parent::prepareRow($row);
}
@@ -29,7 +29,7 @@ class Type extends StringArgument {
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Entity\EntityStorageInterface $storage
* @param \Drupal\Core\Entity\EntityStorageInterface $node_type_storage
* The entity storage class.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityStorageInterface $node_type_storage) {
@@ -2,6 +2,7 @@
namespace Drupal\node\Plugin\views\filter;
use Drupal\Core\Database\Query\Condition;
use Drupal\Core\Form\FormStateInterface;
use Drupal\views\Plugin\views\filter\FilterPluginBase;
@@ -14,8 +15,8 @@ use Drupal\views\Plugin\views\filter\FilterPluginBase;
*/
class Access extends FilterPluginBase {
public function adminSummary() { }
protected function operatorForm(&$form, FormStateInterface $form_state) { }
public function adminSummary() {}
protected function operatorForm(&$form, FormStateInterface $form_state) {}
public function canExpose() {
return FALSE;
}
@@ -27,10 +28,10 @@ class Access extends FilterPluginBase {
$account = $this->view->getUser();
if (!$account->hasPermission('bypass node access')) {
$table = $this->ensureMyTable();
$grants = db_or();
$grants = new Condition('OR');
foreach (node_access_grants('view', $account) as $realm => $gids) {
foreach ($gids as $gid) {
$grants->condition(db_and()
$grants->condition((new Condition('AND'))
->condition($table . '.gid', $gid)
->condition($table . '.realm', $realm)
);
@@ -14,11 +14,13 @@ use Drupal\views\Plugin\views\filter\FilterPluginBase;
*/
class Status extends FilterPluginBase {
public function adminSummary() { }
public function adminSummary() {}
protected function operatorForm(&$form, FormStateInterface $form_state) { }
protected function operatorForm(&$form, FormStateInterface $form_state) {}
public function canExpose() { return FALSE; }
public function canExpose() {
return FALSE;
}
public function query() {
$table = $this->ensureMyTable();
@@ -117,7 +117,8 @@ class NodeRevisionsTest extends NodeTestBase {
$node->save();
$node = Node::load($node->id()); // Make sure we get revision information.
// Make sure we get revision information.
$node = Node::load($node->id());
$nodes[] = clone $node;
}
@@ -168,9 +169,11 @@ class NodeRevisionsTest extends NodeTestBase {
// 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.');
$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.');
@@ -191,9 +194,11 @@ class NodeRevisionsTest extends NodeTestBase {
// 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->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.');
@@ -427,14 +427,14 @@ class PagePreviewTest extends NodeTestBase {
$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
// forward revision for it.
// pending revision for it.
$this->drupalPostForm(NULL, [], t('Save'));
$node = $this->drupalGetNodeByTitle($edit[$title_key]);
// Check that previewing a forward revision of a node works. This can not be
// 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
// forward revisions in core.
// pending revisions in core.
// @see https://www.drupal.org/node/2725533
$node->setNewRevision(TRUE);
$node->isDefaultRevision(FALSE);