diff --git a/core/modules/block_content/src/BlockContentViewsData.php b/core/modules/block_content/src/BlockContentViewsData.php index 3cd4a39..09d49b7 100644 --- a/core/modules/block_content/src/BlockContentViewsData.php +++ b/core/modules/block_content/src/BlockContentViewsData.php @@ -28,18 +28,6 @@ public function getViewsData() { $data['block_content_field_data']['type']['field']['id'] = 'field'; - // @todo Figure out the way to integrate this automatic in - // content_translation https://www.drupal.org/node/2410261. - if ($this->moduleHandler->moduleExists('content_translation')) { - $data['block_content']['translation_link'] = array( - 'title' => $this->t('Translation link'), - 'help' => $this->t('Provide a link to the translations overview for custom blocks.'), - 'field' => array( - 'id' => 'content_translation_link', - ), - ); - } - // Advertise this table as a possible base table. $data['block_content_revision']['table']['base']['help'] = $this->t('Block Content revision is a history of changes to block content.'); $data['block_content_revision']['table']['base']['defaults']['title'] = 'info'; diff --git a/core/modules/comment/config/schema/comment.views.schema.yml b/core/modules/comment/config/schema/comment.views.schema.yml index ed3f70b..606af82 100644 --- a/core/modules/comment/config/schema/comment.views.schema.yml +++ b/core/modules/comment/config/schema/comment.views.schema.yml @@ -20,33 +20,10 @@ views.field.comment_last_timestamp: type: views.field.date label: 'Last comment date' -views.field.comment_link: - type: views_field - label: 'Comment link' - mapping: - text: - type: views_field - label: 'Text to display' - link_to_entity: - type: views_field - label: 'Link field to the entity if there is no comment' - views.field.comment_link_approve: type: views.field.comment_link label: 'Comment approve link' -views.field.comment_link_delete: - type: views.field.comment_link - label: 'Comment delete link' - -views.field.comment_link_edit: - type: views.field.comment_link - label: 'Comment edit link' - mapping: - destination: - type: boolean - label: 'Use destination' - views.field.comment_link_reply: type: views.field.comment_link label: 'Comment reply link' diff --git a/core/modules/comment/src/CommentAccessControlHandler.php b/core/modules/comment/src/CommentAccessControlHandler.php index 1a42611..405b1d2 100644 --- a/core/modules/comment/src/CommentAccessControlHandler.php +++ b/core/modules/comment/src/CommentAccessControlHandler.php @@ -25,9 +25,16 @@ class CommentAccessControlHandler extends EntityAccessControlHandler { * {@inheritdoc} */ protected function checkAccess(EntityInterface $entity, $operation, $langcode, AccountInterface $account) { - /** @var \Drupal\Core\Entity\EntityInterface|\Drupal\user\EntityOwnerInterface $entity */ + /** @var \Drupal\comment\CommentInterface|\Drupal\user\EntityOwnerInterface $entity */ - if ($account->hasPermission('administer comments')) { + $comment_admin = $account->hasPermission('administer comments'); + if ($operation == 'approve') { + return AccessResult::allowedIf($comment_admin && !$entity->isPublished()) + ->cachePerPermissions() + ->cacheUntilEntityChanges($entity); + } + + if ($comment_admin) { $access = AccessResult::allowed()->cachePerPermissions(); return ($operation != 'view') ? $access : $access->andIf($entity->getCommentedEntity()->access($operation, $account, TRUE)); } diff --git a/core/modules/comment/src/CommentViewsData.php b/core/modules/comment/src/CommentViewsData.php index e853a58..c70f77f 100644 --- a/core/modules/comment/src/CommentViewsData.php +++ b/core/modules/comment/src/CommentViewsData.php @@ -102,30 +102,6 @@ public function getViewsData() { $data['comment_field_data']['status']['filter']['label'] = t('Approved comment status'); $data['comment_field_data']['status']['filter']['type'] = 'yes-no'; - $data['comment']['view_comment'] = array( - 'field' => array( - 'title' => t('Link to comment'), - 'help' => t('Provide a simple link to view the comment.'), - 'id' => 'comment_link', - ), - ); - - $data['comment']['edit_comment'] = array( - 'field' => array( - 'title' => t('Link to edit comment'), - 'help' => t('Provide a simple link to edit the comment.'), - 'id' => 'comment_link_edit', - ), - ); - - $data['comment']['delete_comment'] = array( - 'field' => array( - 'title' => t('Link to delete comment'), - 'help' => t('Provide a simple link to delete the comment.'), - 'id' => 'comment_link_delete', - ), - ); - $data['comment']['approve_comment'] = array( 'field' => array( 'title' => t('Link to approve comment'), @@ -195,16 +171,6 @@ public function getViewsData() { $data['comment_field_data']['pid']['relationship']['help'] = t('The parent comment'); $data['comment_field_data']['pid']['relationship']['label'] = t('parent'); - if (\Drupal::moduleHandler()->moduleExists('content_translation')) { - $data['comment']['translation_link'] = array( - 'title' => t('Translation link'), - 'help' => t('Provide a link to the translations overview for comments.'), - 'field' => array( - 'id' => 'content_translation_link', - ), - ); - } - // Define the base group of this table. Fields that don't have a group defined // will go into this field by default. $data['comment_entity_statistics']['table']['group'] = t('Comment Statistics'); diff --git a/core/modules/comment/src/Plugin/views/field/Link.php b/core/modules/comment/src/Plugin/views/field/Link.php deleted file mode 100644 index c2fc23d..0000000 --- a/core/modules/comment/src/Plugin/views/field/Link.php +++ /dev/null @@ -1,139 +0,0 @@ -get('entity.manager') - ); - } - - /** - * Constructs a Link field plugin. - * - * @param array $configuration - * A configuration array containing information about the plugin instance. - * @param string $plugin_id - * The plugin_id for the plugin instance. - * @param mixed $plugin_definition - * The plugin implementation definition. - * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager - * The entity manager service. - */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager) { - parent::__construct($configuration, $plugin_id, $plugin_definition); - $this->entityManager = $entity_manager; - } - - /** - * {@inheritdoc} - */ - protected function defineOptions() { - $options = parent::defineOptions(); - $options['text'] = array('default' => ''); - $options['link_to_entity'] = array('default' => FALSE); - return $options; - } - - /** - * {@inheritdoc} - */ - public function buildOptionsForm(&$form, FormStateInterface $form_state) { - $form['text'] = array( - '#type' => 'textfield', - '#title' => t('Text to display'), - '#default_value' => $this->options['text'], - ); - $form['link_to_entity'] = array( - '#title' => $this->t('Link field to the entity if there is no comment'), - '#type' => 'checkbox', - '#default_value' => $this->options['link_to_entity'], - ); - parent::buildOptionsForm($form, $form_state); - } - - /** - * {@inheritdoc} - */ - public function query() {} - - /** - * {@inheritdoc} - */ - public function render(ResultRow $values) { - $comment = $this->getEntity($values); - return $this->renderLink($comment, $values); - } - - /** - * Prepares the link pointing to the comment or its node. - * - * @param \Drupal\Core\Entity\EntityInterface $data - * The comment entity. - * @param \Drupal\views\ResultRow $values - * The values retrieved from a single row of a view's query result. - * - * @return string - * Returns a string for the link text. - */ - protected function renderLink($data, ResultRow $values) { - $text = !empty($this->options['text']) ? $this->options['text'] : $this->t('View'); - /** @var \Drupal\comment\CommentInterface $comment */ - $comment = $data; - $cid = $comment->id(); - - $this->options['alter']['make_link'] = TRUE; - $this->options['alter']['html'] = TRUE; - - if (!empty($cid)) { - $this->options['alter']['url'] = Url::fromRoute('entity.comment.canonical', ['comment' => $cid]); - $this->options['alter']['fragment'] = "comment-" . $cid; - } - // If there is no comment link to the node. - elseif ($this->options['link_to_node']) { - $entity = $comment->getCommentedEntity(); - $this->options['alter']['url'] = $entity->urlInfo(); - } - - return $text; - } - -} diff --git a/core/modules/comment/src/Plugin/views/field/LinkApprove.php b/core/modules/comment/src/Plugin/views/field/LinkApprove.php index 11e7812..6aa7259 100644 --- a/core/modules/comment/src/Plugin/views/field/LinkApprove.php +++ b/core/modules/comment/src/Plugin/views/field/LinkApprove.php @@ -7,9 +7,8 @@ namespace Drupal\comment\Plugin\views\field; -use Drupal\comment\CommentInterface; -use Drupal\Core\Session\AccountInterface; use Drupal\Core\Url; +use Drupal\views\Plugin\views\field\LinkBase; use Drupal\views\ResultRow; /** @@ -19,43 +18,28 @@ * * @ViewsField("comment_link_approve") */ -class LinkApprove extends Link { +class LinkApprove extends LinkBase { /** * {@inheritdoc} */ - public function access(AccountInterface $account) { - //needs permission to administer comments in general - return $account->hasPermission('administer comments'); + protected function getUrlInfo(ResultRow $row) { + return Url::fromRoute('comment.approve', ['comment' => $this->getEntity($row)->id()]); } /** - * Prepares the link pointing for approving the comment. - * - * @param \Drupal\Core\Entity\EntityInterface $data - * The comment entity. - * @param \Drupal\views\ResultRow $values - * The values retrieved from a single row of a view's query result. - * - * @return string - * Returns a string for the link text. + * {@inheritdoc} */ - protected function renderLink($data, ResultRow $values) { - $status = $this->getValue($values, 'status'); - - // Don't show an approve link on published comment. - if ($status == CommentInterface::PUBLISHED) { - return; - } - - $text = !empty($this->options['text']) ? $this->options['text'] : $this->t('Approve'); - $comment = $this->get_entity($values); - - $this->options['alter']['make_link'] = TRUE; - $this->options['alter']['url'] = Url::fromRoute('comment.approve', ['comment' => $comment->id()]); - $this->options['alter']['query'] = $this->getDestinationArray() + array('token' => \Drupal::csrfToken()->get($this->options['alter']['url']->toString())); + protected function renderLink(ResultRow $row) { + $this->options['alter']['query'] = $this->getDestinationArray(); + return parent::renderLink($row); + } - return $text; + /** + * {@inheritdoc} + */ + protected function getDefaultLabel() { + return $this->t('Approve'); } } diff --git a/core/modules/comment/src/Plugin/views/field/LinkDelete.php b/core/modules/comment/src/Plugin/views/field/LinkDelete.php deleted file mode 100644 index 5ede03f..0000000 --- a/core/modules/comment/src/Plugin/views/field/LinkDelete.php +++ /dev/null @@ -1,52 +0,0 @@ -hasPermission('administer comments'); - } - - /** - * Prepares the link for deleting the comment. - * - * @param \Drupal\Core\Entity\EntityInterface $data - * The comment entity. - * @param \Drupal\views\ResultRow $values - * The values retrieved from a single row of a view's query result. - * - * @return string - * Returns a string for the link text. - */ - protected function renderLink($data, ResultRow $values) { - $text = !empty($this->options['text']) ? $this->options['text'] : $this->t('Delete'); - $comment = $this->getEntity($values); - - $this->options['alter']['make_link'] = TRUE; - $this->options['alter']['url'] = $comment->urlInfo('delete-form'); - $this->options['alter']['query'] = $this->getDestinationArray(); - - return $text; - } - -} diff --git a/core/modules/comment/src/Plugin/views/field/LinkEdit.php b/core/modules/comment/src/Plugin/views/field/LinkEdit.php deleted file mode 100644 index 7ae0693..0000000 --- a/core/modules/comment/src/Plugin/views/field/LinkEdit.php +++ /dev/null @@ -1,71 +0,0 @@ - FALSE); - - return $options; - } - - public function buildOptionsForm(&$form, FormStateInterface $form_state) { - parent::buildOptionsForm($form, $form_state); - - $form['destination'] = array( - '#type' => 'checkbox', - '#title' => $this->t('Use destination'), - '#description' => $this->t('Add destination to the link'), - '#default_value' => $this->options['destination'], - ); - } - - /** - * Prepare the link for editing the comment. - * - * @param \Drupal\Core\Entity\EntityInterface $data - * The comment entity. - * @param \Drupal\views\ResultRow $values - * The values retrieved from a single row of a view's query result. - * - * @return string - * Returns a string for the link text. - */ - protected function renderLink($data, ResultRow $values) { - parent::renderLink($data, $values); - // Ensure user has access to edit this comment. - $comment = $this->getValue($values); - if (!$comment->access('update')) { - return; - } - - $text = !empty($this->options['text']) ? $this->options['text'] : $this->t('Edit'); - unset($this->options['alter']['fragment']); - - if (!empty($this->options['destination'])) { - $this->options['alter']['query'] = $this->getDestinationArray(); - } - - $this->options['alter']['url'] = $comment->urlInfo('edit-form'); - - return $text; - } - -} diff --git a/core/modules/comment/src/Plugin/views/field/LinkReply.php b/core/modules/comment/src/Plugin/views/field/LinkReply.php index 65c7545..32b5099 100644 --- a/core/modules/comment/src/Plugin/views/field/LinkReply.php +++ b/core/modules/comment/src/Plugin/views/field/LinkReply.php @@ -7,8 +7,8 @@ namespace Drupal\comment\Plugin\views\field; -use Drupal\Core\Session\AccountInterface; use Drupal\Core\Url; +use Drupal\views\Plugin\views\field\LinkBase; use Drupal\views\ResultRow; /** @@ -18,39 +18,27 @@ * * @ViewsField("comment_link_reply") */ -class LinkReply extends Link { +class LinkReply extends LinkBase { /** * {@inheritdoc} */ - public function access(AccountInterface $account) { - //check for permission to reply to comments - return $account->hasPermission('post comments'); - } - - /** - * Prepare the link for replying to the comment. - * - * @param \Drupal\Core\Entity\EntityInterface $data - * The comment entity. - * @param \Drupal\views\ResultRow $values - * The values retrieved from a single row of a view's query result. - * - * @return string - * Returns a string for the link text. - */ - protected function renderLink($data, ResultRow $values) { - $text = !empty($this->options['text']) ? $this->options['text'] : $this->t('Reply'); - $comment = $this->getEntity($values); - - $this->options['alter']['make_link'] = TRUE; - $this->options['alter']['url'] = Url::fromRoute('comment.reply', [ + protected function getUrlInfo(ResultRow $row) { + /** @var \Drupal\comment\CommentInterface $comment */ + $comment = $this->getEntity($row); + return Url::fromRoute('comment.reply', [ 'entity_type' => $comment->getCommentedEntityTypeId(), 'entity' => $comment->getCommentedEntityId(), 'field_name' => $comment->getFieldName(), 'pid' => $comment->id(), ]); - return $text; + } + + /** + * {@inheritdoc} + */ + protected function getDefaultLabel() { + return $this->t('Reply'); } } diff --git a/core/modules/contact/src/Plugin/views/field/ContactLink.php b/core/modules/contact/src/Plugin/views/field/ContactLink.php index f746e00..b963b76 100644 --- a/core/modules/contact/src/Plugin/views/field/ContactLink.php +++ b/core/modules/contact/src/Plugin/views/field/ContactLink.php @@ -7,14 +7,11 @@ namespace Drupal\contact\Plugin\views\field; -use Drupal\Core\Access\AccessManagerInterface; -use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Session\AccountInterface; use Drupal\Core\Url; -use Drupal\user\Plugin\views\field\Link; +use Drupal\views\Plugin\views\field\LinkBase; use Drupal\views\ResultRow; -use Symfony\Component\DependencyInjection\ContainerInterface; /** * Defines a field that links to the user contact page, if access is permitted. @@ -23,65 +20,7 @@ * * @ViewsField("contact_link") */ -class ContactLink extends Link { - - /** - * The access manager. - * - * @var \Drupal\Core\Access\AccessManagerInterface - */ - protected $accessManager; - - /** - * Current user object. - * - * @var \Drupal\Core\Session\AccountInterface - */ - protected $currentUser; - - /** - * Gets the current active user. - * - * @todo: https://drupal.org/node/2105123 put this method in - * \Drupal\Core\Plugin\PluginBase instead. - * - * @return \Drupal\Core\Session\AccountInterface - */ - protected function currentUser() { - if (!$this->currentUser) { - $this->currentUser = \Drupal::currentUser(); - } - return $this->currentUser; - } - - /** - * Constructs a ContactLink object. - * - * @param array $configuration - * A configuration array containing information about the plugin instance. - * @param string $plugin_id - * The plugin_id for the plugin instance. - * @param mixed $plugin_definition - * The plugin implementation definition. - * @param \Drupal\Core\Access\AccessManagerInterface $access_manager - * The access manager. - */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, AccessManagerInterface $access_manager) { - parent::__construct($configuration, $plugin_id, $plugin_definition); - $this->accessManager = $access_manager; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { - return new static( - $configuration, - $plugin_id, - $plugin_definition, - $container->get('access_manager') - ); - } +class ContactLink extends LinkBase { /** * {@inheritdoc} @@ -90,7 +29,7 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) { parent::buildOptionsForm($form, $form_state); $form['text']['#title'] = $this->t('Link label'); $form['text']['#required'] = TRUE; - $form['text']['#default_value'] = empty($this->options['text']) ? $this->t('contact') : $this->options['text']; + $form['text']['#default_value'] = empty($this->options['text']) ? $this->getDefaultLabel() : $this->options['text']; } /** @@ -101,26 +40,26 @@ public function access(AccountInterface $account) { return TRUE; } - /** * {@inheritdoc} */ - protected function renderLink(EntityInterface $entity, ResultRow $values) { - - if (empty($entity)) { - return; - } + protected function getUrlInfo(ResultRow $row) { + // Check access when we pull up the user account so we know if the user has + // made the contact page available. See contact_form_user_form_alter() and + // contact_user_profile_form_submit(). + return Url::fromRoute('entity.user.contact_form', ['user' => $this->getEntity($row)->id()]); + } - // Check access when we pull up the user account so we know - // if the user has made the contact page available. - if (!$this->accessManager->checkNamedRoute('entity.user.contact_form', array('user' => $entity->id()), $this->currentUser())) { - return; - } + /** + * {@inheritdoc} + */ + protected function renderLink(ResultRow $row) { + $entity = $this->getEntity($row); $this->options['alter']['make_link'] = TRUE; - $this->options['alter']['url'] = Url::fromRoute('entity.user.contact_form', ['user' => $entity->id()]); + $this->options['alter']['url'] = $this->getUrlInfo($row); - $title = $this->t('Contact %user', array('%user' => $entity->name->value)); + $title = $this->t('Contact %user', array('%user' => $entity->label())); $this->options['alter']['attributes'] = array('title' => $title); if (!empty($this->options['text'])) { @@ -131,4 +70,11 @@ protected function renderLink(EntityInterface $entity, ResultRow $values) { } } + /** + * {@inheritdoc} + */ + protected function getDefaultLabel() { + return $this->t('contact'); + } + } diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module index 82a866b..1f1a0eb 100644 --- a/core/modules/content_translation/content_translation.module +++ b/core/modules/content_translation/content_translation.module @@ -221,6 +221,30 @@ function content_translation_entity_operation(EntityInterface $entity) { } /** + * Implements hook_views_data_alter(). + */ +function content_translation_views_data_alter(array &$data) { + // Add the content translation entity link definition to Views data for entity + // types having translation enabled. + $entity_types = \Drupal::entityManager()->getDefinitions(); + /** @var \Drupal\content_translation\ContentTranslationManagerInterface $manager */ + $manager = \Drupal::service('content_translation.manager'); + foreach ($entity_types as $entity_type_id => $entity_type) { + $base_table = $entity_type->getBaseTable(); + if (isset($data[$base_table]) && $entity_type->hasLinkTemplate('drupal:content-translation-overview') && $manager->isEnabled($entity_type_id)) { + $t_arguments = ['@entity_type_label' => $entity_type->getLabel()]; + $data[$base_table]['translation_link'] = [ + 'field' => [ + 'title' => t('Link to translate @entity_type_label', $t_arguments), + 'help' => t('Provide a translation link to the @entity_type_label.', $t_arguments), + 'id' => 'content_translation_link', + ], + ]; + } + } +} + +/** * Implements hook_menu_links_discovered_alter(). */ function content_translation_menu_links_discovered_alter(array &$links) { diff --git a/core/modules/content_translation/src/Plugin/views/field/TranslationLink.php b/core/modules/content_translation/src/Plugin/views/field/TranslationLink.php index a084dfe..b320cf5 100644 --- a/core/modules/content_translation/src/Plugin/views/field/TranslationLink.php +++ b/core/modules/content_translation/src/Plugin/views/field/TranslationLink.php @@ -7,10 +7,7 @@ namespace Drupal\content_translation\Plugin\views\field; -use Drupal\Core\Form\FormStateInterface; -use Drupal\views\Plugin\views\field\FieldPluginBase; -use Drupal\Core\Entity\EntityInterface; -use Drupal\views\ResultRow; +use Drupal\views\Plugin\views\field\EntityLink; /** * Provides a translation link for an entity. @@ -19,69 +16,20 @@ * * @ViewsField("content_translation_link") */ -class TranslationLink extends FieldPluginBase { +class TranslationLink extends EntityLink { /** * {@inheritdoc} */ - protected function defineOptions() { - $options = parent::defineOptions(); - $options['text'] = array('default' => ''); - return $options; + protected function getEntityLinkTemplate() { + return 'drupal:content-translation-overview'; } /** * {@inheritdoc} */ - public function buildOptionsForm(&$form, FormStateInterface $form_state) { - $form['text'] = array( - '#type' => 'textfield', - '#title' => $this->t('Text to display'), - '#default_value' => $this->options['text'], - ); - parent::buildOptionsForm($form, $form_state); - } - - /** - * {@inheritdoc} - */ - public function render(ResultRow $values) { - return $this->renderLink($this->getEntity($values), $values); - } - - /** - * Alters the field to render a link. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entity being rendered. - * @param \Drupal\views\ResultRow $values - * The current row of the views result. - * - * @return string - * The actual rendered text (without the link) of this field. - */ - protected function renderLink(EntityInterface $entity, ResultRow $values) { - if (content_translation_translate_access($entity)->isAllowed()) { - $text = !empty($this->options['text']) ? $this->options['text'] : $this->t('Translate'); - - $this->options['alter']['make_link'] = TRUE; - $this->options['alter']['url'] = $entity->urlInfo('drupal:content-translation-overview'); - - return $text; - } - } - - /** - * {@inheritdoc} - */ - public function query() { - } - - /** - * {@inheritdoc} - */ - public function clickSortable() { - return FALSE; + protected function getDefaultLabel() { + return $this->t('Translate'); } } diff --git a/core/modules/node/config/optional/views.view.content_recent.yml b/core/modules/node/config/optional/views.view.content_recent.yml index 1ad0161..500e1fa 100644 --- a/core/modules/node/config/optional/views.view.content_recent.yml +++ b/core/modules/node/config/optional/views.view.content_recent.yml @@ -239,7 +239,7 @@ display: hide_alter_empty: true text: Edit entity_type: node - plugin_id: node_link_edit + plugin_id: entity_link_edit delete_node: id: delete_node table: node @@ -290,7 +290,7 @@ display: hide_alter_empty: true text: Delete entity_type: node - plugin_id: node_link_delete + plugin_id: entity_link_delete filters: status_extra: id: status_extra diff --git a/core/modules/node/config/schema/node.views.schema.yml b/core/modules/node/config/schema/node.views.schema.yml index b1e8e73..70c5c23 100644 --- a/core/modules/node/config/schema/node.views.schema.yml +++ b/core/modules/node/config/schema/node.views.schema.yml @@ -101,30 +101,6 @@ views.field.node: type: boolean label: 'Link this field to the original piece of content' -views.field.node_link: - type: views_field - label: 'Node link' - mapping: - text: - type: label - label: 'Text to display' - -views.field.node_link_delete: - type: views_field - label: 'Node delete link' - mapping: - text: - type: label - label: 'Text to display' - -views.field.node_link_edit: - type: views_field - label: 'Node edit link' - mapping: - text: - type: label - label: 'Text to display' - views.field.node_bulk_form: type: views_field_bulk_form label: 'Node bulk form' diff --git a/core/modules/node/src/NodeViewsData.php b/core/modules/node/src/NodeViewsData.php index aecaec2..ceb5587 100644 --- a/core/modules/node/src/NodeViewsData.php +++ b/core/modules/node/src/NodeViewsData.php @@ -61,40 +61,6 @@ public function getViewsData() { $data['node_field_data']['sticky']['filter']['type'] = 'yes-no'; $data['node_field_data']['sticky']['sort']['help'] = t('Whether or not the content is sticky. To list sticky content first, set this to descending.'); - if (\Drupal::moduleHandler()->moduleExists('content_translation')) { - $data['node']['translation_link'] = array( - 'title' => t('Translation link'), - 'help' => t('Provide a link to the translations overview for nodes.'), - 'field' => array( - 'id' => 'content_translation_link', - ), - ); - } - - $data['node']['view_node'] = array( - 'field' => array( - 'title' => t('Link to content'), - 'help' => t('Provide a simple link to the content.'), - 'id' => 'node_link', - ), - ); - - $data['node']['edit_node'] = array( - 'field' => array( - 'title' => t('Link to edit content'), - 'help' => t('Provide a simple link to edit the content.'), - 'id' => 'node_link_edit', - ), - ); - - $data['node']['delete_node'] = array( - 'field' => array( - 'title' => t('Link to delete content'), - 'help' => t('Provide a simple link to delete the content.'), - 'id' => 'node_link_delete', - ), - ); - $data['node']['path'] = array( 'field' => array( 'title' => t('Path'), @@ -292,7 +258,7 @@ public function getViewsData() { $data['node_field_revision']['langcode']['help'] = t('The language of the content or translation.'); - $data['node_revision']['link_to_revision'] = array( + $data['node_field_revision']['link_to_revision'] = array( 'field' => array( 'title' => t('Link to revision'), 'help' => t('Provide a simple link to the revision.'), @@ -301,7 +267,7 @@ public function getViewsData() { ), ); - $data['node_revision']['revert_revision'] = array( + $data['node_field_revision']['revert_revision'] = array( 'field' => array( 'title' => t('Link to revert revision'), 'help' => t('Provide a simple link to revert to the revision.'), @@ -310,7 +276,7 @@ public function getViewsData() { ), ); - $data['node_revision']['delete_revision'] = array( + $data['node_field_revision']['delete_revision'] = array( 'field' => array( 'title' => t('Link to delete revision'), 'help' => t('Provide a simple link to delete the content revision.'), diff --git a/core/modules/node/src/Plugin/views/field/Link.php b/core/modules/node/src/Plugin/views/field/Link.php deleted file mode 100644 index a0d7ba7..0000000 --- a/core/modules/node/src/Plugin/views/field/Link.php +++ /dev/null @@ -1,94 +0,0 @@ - ''); - return $options; - } - - /** - * {@inheritdoc} - */ - public function buildOptionsForm(&$form, FormStateInterface $form_state) { - $form['text'] = array( - '#type' => 'textfield', - '#title' => $this->t('Text to display'), - '#default_value' => $this->options['text'], - ); - parent::buildOptionsForm($form, $form_state); - - // The path is set by renderLink function so don't allow to set it. - $form['alter']['path'] = array('#access' => FALSE); - $form['alter']['external'] = array('#access' => FALSE); - } - - /** - * {@inheritdoc} - */ - public function query() { - $this->addAdditionalFields(); - } - - /** - * {@inheritdoc} - */ - public function render(ResultRow $values) { - if ($entity = $this->getEntity($values)) { - return $this->renderLink($entity, $values); - } - } - - /** - * Prepares the link to the node. - * - * @param \Drupal\Core\Entity\EntityInterface $node - * The node entity this field belongs to. - * @param ResultRow $values - * The values retrieved from the view's result set. - * - * @return string - * Returns a string for the link text. - */ - protected function renderLink($node, ResultRow $values) { - if ($node->access('view')) { - $this->options['alter']['make_link'] = TRUE; - $this->options['alter']['url'] = $node->urlInfo(); - $text = !empty($this->options['text']) ? $this->options['text'] : $this->t('View'); - return $text; - } - } - -} diff --git a/core/modules/node/src/Plugin/views/field/LinkDelete.php b/core/modules/node/src/Plugin/views/field/LinkDelete.php deleted file mode 100644 index 6dad5e0..0000000 --- a/core/modules/node/src/Plugin/views/field/LinkDelete.php +++ /dev/null @@ -1,47 +0,0 @@ -access('delete')) { - return; - } - - $this->options['alter']['make_link'] = TRUE; - $this->options['alter']['url'] = $node->urlInfo('delete-form'); - $this->options['alter']['query'] = $this->getDestinationArray(); - - $text = !empty($this->options['text']) ? $this->options['text'] : $this->t('Delete'); - return $text; - } - -} diff --git a/core/modules/node/src/Plugin/views/field/LinkEdit.php b/core/modules/node/src/Plugin/views/field/LinkEdit.php deleted file mode 100644 index 6d46642..0000000 --- a/core/modules/node/src/Plugin/views/field/LinkEdit.php +++ /dev/null @@ -1,48 +0,0 @@ -access('update')) { - return; - } - - $this->options['alter']['make_link'] = TRUE; - $this->options['alter']['url'] = $node->urlInfo('edit-form'); - $this->options['alter']['query'] = $this->getDestinationArray(); - - $text = !empty($this->options['text']) ? $this->options['text'] : $this->t('Edit'); - return $text; - } - -} diff --git a/core/modules/node/src/Plugin/views/field/RevisionLink.php b/core/modules/node/src/Plugin/views/field/RevisionLink.php index 8370170..c268304 100644 --- a/core/modules/node/src/Plugin/views/field/RevisionLink.php +++ b/core/modules/node/src/Plugin/views/field/RevisionLink.php @@ -7,12 +7,9 @@ namespace Drupal\node\Plugin\views\field; -use Drupal\Core\Session\AccountInterface; use Drupal\Core\Url; -use Drupal\node\Plugin\views\field\Link; -use Drupal\views\Plugin\views\display\DisplayPluginBase; +use Drupal\views\Plugin\views\field\LinkBase; use Drupal\views\ResultRow; -use Drupal\views\ViewExecutable; /** * Field handler to present a link to a node revision. @@ -21,78 +18,39 @@ * * @ViewsField("node_revision_link") */ -class RevisionLink extends Link { - - /** - * Overrides Drupal\views\Plugin\views\field\FieldPluginBase::init(). - */ - public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) { - parent::init($view, $display, $options); - - $this->additional_fields['node_vid'] = array('table' => 'node_field_revision', 'field' => 'vid'); - } +class RevisionLink extends LinkBase { /** * {@inheritdoc} */ - public function access(AccountInterface $account) { - return $account->hasPermission('view revisions') || $account->hasPermission('administer nodes'); + protected function getUrlInfo(ResultRow $row) { + /** @var \Drupal\node\NodeInterface $node */ + $node = $this->getEntity($row); + // Current revision uses the node view path. + return !$node->isDefaultRevision() ? + Url::fromRoute('entity.node.revision', ['node' => $node->id(), 'node_revision' => $node->getRevisionId()]) : + $node->urlInfo(); } /** - * Prepares the link to point to a node revision. - * - * @param \Drupal\Core\Entity\EntityInterface $data - * The node revision entity this field belongs to. - * @param \Drupal\views\ResultRow $values - * The values retrieved from the view's result set. - * - * @return string - * Returns a string for the link text. + * {@inheritdoc} */ - protected function renderLink($data, ResultRow $values) { - list($node, $vid) = $this->get_revision_entity($values, 'view'); - if (!isset($vid)) { - return; + protected function renderLink(ResultRow $row) { + /** @var \Drupal\node\NodeInterface $node */ + $node = $this->getEntity($row); + if (!$node->getRevisionid()) { + return ''; } - - // Current revision uses the node view path. - if (!$node->isDefaultRevision()) { - $url = Url::fromRoute('node.revision_show', ['node' => $node->nid, 'node_revision' => $vid]); - } - else { - $url = $node->urlInfo(); - } - - $this->options['alter']['make_link'] = TRUE; - $this->options['alter']['url'] = $url; + $text = parent::renderLink($row); $this->options['alter']['query'] = $this->getDestinationArray(); - - return !empty($this->options['text']) ? $this->options['text'] : $this->t('View'); + return $text; } /** - * Returns the revision values of a node. - * - * @param object $values - * An object containing all retrieved values. - * @param string $op - * The operation being performed. - * - * @return array - * A numerically indexed array containing the current node object and the - * revision ID for this row. + * {@inheritdoc} */ - function get_revision_entity($values, $op) { - $vid = $this->getValue($values, 'node_vid'); - $node = $this->getEntity($values); - // Unpublished nodes ignore access control. - $node->setPublished(TRUE); - // Ensure user has access to perform the operation on this node. - if (!$node->access($op)) { - return array($node, NULL); - } - return array($node, $vid); + protected function getDefaultLabel() { + return $this->t('View'); } } diff --git a/core/modules/node/src/Plugin/views/field/RevisionLinkDelete.php b/core/modules/node/src/Plugin/views/field/RevisionLinkDelete.php index c7519d0..76bbd4b 100644 --- a/core/modules/node/src/Plugin/views/field/RevisionLinkDelete.php +++ b/core/modules/node/src/Plugin/views/field/RevisionLinkDelete.php @@ -7,9 +7,7 @@ namespace Drupal\node\Plugin\views\field; -use Drupal\Core\Session\AccountInterface; use Drupal\Core\Url; -use Drupal\node\Plugin\views\field\RevisionLink; use Drupal\views\ResultRow; /** @@ -24,37 +22,17 @@ class RevisionLinkDelete extends RevisionLink { /** * {@inheritdoc} */ - public function access(AccountInterface $account) { - return $account->hasPermission('delete revisions') || $account->hasPermission('administer nodes'); + protected function getUrlInfo(ResultRow $row) { + /** @var \Drupal\node\NodeInterface $node */ + $node = $this->getEntity($row); + return Url::fromRoute('node.revision_delete_confirm', ['node' => $node->id(), 'node_revision' => $node->getRevisionId()]); } /** - * Prepares the link to delete a node revision. - * - * @param \Drupal\Core\Entity\EntityInterface $data - * The node revision entity this field belongs to. - * @param \Drupal\views\ResultRow $values - * The values retrieved from the view's result set. - * - * @return string - * Returns a string for the link text. + * {@inheritdoc} */ - protected function renderLink($data, ResultRow $values) { - list($node, $vid) = $this->get_revision_entity($values, 'delete'); - if (!isset($vid)) { - return; - } - - // Current revision cannot be deleted. - if ($node->isDefaultRevision()) { - return; - } - - $this->options['alter']['make_link'] = TRUE; - $this->options['alter']['url'] = Url::fromRoute('node.revision_delete_confirm', ['node' => $node->id(), 'node_revision' => $vid]); - $this->options['alter']['query'] = $this->getDestinationArray(); - - return !empty($this->options['text']) ? $this->options['text'] : $this->t('Delete'); + protected function getDefaultLabel() { + return $this->t('Delete'); } } diff --git a/core/modules/node/src/Plugin/views/field/RevisionLinkRevert.php b/core/modules/node/src/Plugin/views/field/RevisionLinkRevert.php index a5d1c97..5ffa30f 100644 --- a/core/modules/node/src/Plugin/views/field/RevisionLinkRevert.php +++ b/core/modules/node/src/Plugin/views/field/RevisionLinkRevert.php @@ -7,9 +7,7 @@ namespace Drupal\node\Plugin\views\field; -use Drupal\Core\Session\AccountInterface; use Drupal\Core\Url; -use Drupal\node\Plugin\views\field\RevisionLink; use Drupal\views\ResultRow; /** @@ -24,37 +22,16 @@ class RevisionLinkRevert extends RevisionLink { /** * {@inheritdoc} */ - public function access(AccountInterface $account) { - return $account->hasPermission('revert revisions') || $account->hasPermission('administer nodes'); + protected function getUrlInfo(ResultRow $row) { + /** @var \Drupal\node\NodeInterface $node */ + $node = $this->getEntity($row); + return Url::fromRoute('node.revision_revert_confirm', ['node' => $node->id(), 'node_revision' => $node->getRevisionId()]); } /** - * Prepares the link to revert node to a revision. - * - * @param \Drupal\Core\Entity\EntityInterface $data - * The node revision entity this field belongs to. - * @param \Drupal\views\ResultRow $values - * The values retrieved from the view's result set. - * - * @return string - * Returns a string for the link text. + * {@inheritdoc} */ - protected function renderLink($data, ResultRow $values) { - list($node, $vid) = $this->get_revision_entity($values, 'update'); - if (!isset($vid)) { - return; - } - - // Current revision cannot be reverted. - if ($node->isDefaultRevision()) { - return; - } - - $this->options['alter']['make_link'] = TRUE; - $this->options['alter']['url'] = Url::fromRoute('node.revision_revert_confirm', ['node' => $node->id(), 'node_revision' => $vid]); - $this->options['alter']['query'] = $this->getDestinationArray(); - - return !empty($this->options['text']) ? $this->options['text'] : $this->t('Revert'); + protected function getDefaultLabel() { + return $this->t('Revert'); } - } diff --git a/core/modules/taxonomy/config/schema/taxonomy.views.schema.yml b/core/modules/taxonomy/config/schema/taxonomy.views.schema.yml index 40b226a..a4b6320 100644 --- a/core/modules/taxonomy/config/schema/taxonomy.views.schema.yml +++ b/core/modules/taxonomy/config/schema/taxonomy.views.schema.yml @@ -83,14 +83,6 @@ views.argument_default.taxonomy_tid: type: string label: 'Multiple-value handling' -views.field.term_link_edit: - type: views_field - label: 'Taxonomy language' - mapping: - text: - type: label - label: 'Text to display' - views.field.term_name: type: views.field.field mapping: diff --git a/core/modules/taxonomy/src/Plugin/views/field/LinkEdit.php b/core/modules/taxonomy/src/Plugin/views/field/LinkEdit.php deleted file mode 100644 index 10f0337..0000000 --- a/core/modules/taxonomy/src/Plugin/views/field/LinkEdit.php +++ /dev/null @@ -1,89 +0,0 @@ -additional_fields['tid'] = 'tid'; - $this->additional_fields['vid'] = 'vid'; - } - - /** - * {@inheritdoc} - */ - protected function defineOptions() { - $options = parent::defineOptions(); - - $options['text'] = array('default' => ''); - - return $options; - } - - /** - * {@inheritdoc} - */ - public function buildOptionsForm(&$form, FormStateInterface $form_state) { - $form['text'] = array( - '#type' => 'textfield', - '#title' => $this->t('Text to display'), - '#default_value' => $this->options['text'], - ); - parent::buildOptionsForm($form, $form_state); - } - - /** - * {@inheritdoc} - */ - public function query() { - $this->ensureMyTable(); - $this->addAdditionalFields(); - } - - /** - * {@inheritdoc} - */ - public function render(ResultRow $values) { - // Check there is an actual value, as on a relationship there may not be. - if ($tid = $this->getValue($values, 'tid')) { - // Mock a term object for taxonomy_term_access(). Use machine name and - // vid to ensure compatibility with vid based and machine name based - // access checks. See http://drupal.org/node/995156 - $term = entity_create('taxonomy_term', array( - 'vid' => $values->{$this->aliases['vid']}, - )); - if ($term->access('update')) { - $text = !empty($this->options['text']) ? $this->options['text'] : $this->t('Edit'); - return \Drupal::l($text, new Url('entity.taxonomy.edit_form', ['taxonomy_term' => $tid], array('query' => $this->getDestinationArray()))); - } - } - } - -} diff --git a/core/modules/taxonomy/src/TermViewsData.php b/core/modules/taxonomy/src/TermViewsData.php index 1d3ba6f..104a33b 100644 --- a/core/modules/taxonomy/src/TermViewsData.php +++ b/core/modules/taxonomy/src/TermViewsData.php @@ -75,25 +75,6 @@ public function getViewsData() { unset($data['taxonomy_term_field_data']['vid']['argument']); unset($data['taxonomy_term_field_data']['vid']['sort']); - $data['taxonomy_term_data']['edit_term'] = array( - 'field' => array( - 'title' => t('Term edit link'), - 'help' => t('Provide a simple link to edit the term.'), - 'id' => 'term_link_edit', - 'click sortable' => FALSE, - ), - ); - - if (\Drupal::moduleHandler()->moduleExists('content_translation')) { - $data['taxonomy_term_data']['translation_link'] = array( - 'title' => t('Translation link'), - 'help' => t('Provide a link to the translations overview for taxonomy terms.'), - 'field' => array( - 'id' => 'content_translation_link', - ), - ); - } - $data['taxonomy_term_field_data']['name']['field']['id'] = 'term_name'; $data['taxonomy_term_field_data']['name']['argument']['many to one'] = TRUE; $data['taxonomy_term_field_data']['name']['argument']['empty field name'] = t('Uncategorized'); diff --git a/core/modules/user/config/schema/user.views.schema.yml b/core/modules/user/config/schema/user.views.schema.yml index 215bd41..d74140f 100644 --- a/core/modules/user/config/schema/user.views.schema.yml +++ b/core/modules/user/config/schema/user.views.schema.yml @@ -67,22 +67,6 @@ views_field_user: type: boolean label: 'Link this field to its user' -views.field.user_link: - type: views_field - label: 'User link' - mapping: - text: - type: text - label: 'Text to display' - -views.field.user_link_cancel: - type: views.field.user_link - label: 'User cancel link' - -views.field.user_link_edit: - type: views.field.user_link - label: 'User edit link' - views.field.user_permissions: type: views.field.prerender_list label: 'List of permission' diff --git a/core/modules/user/src/Plugin/views/field/Link.php b/core/modules/user/src/Plugin/views/field/Link.php deleted file mode 100644 index 78057cc..0000000 --- a/core/modules/user/src/Plugin/views/field/Link.php +++ /dev/null @@ -1,110 +0,0 @@ -additional_fields['uid'] = 'uid'; - } - - /** - * {@inheritdoc} - */ - protected function defineOptions() { - $options = parent::defineOptions(); - $options['text'] = array('default' => ''); - return $options; - } - - /** - * {@inheritdoc} - */ - public function buildOptionsForm(&$form, FormStateInterface $form_state) { - $form['text'] = array( - '#type' => 'textfield', - '#title' => $this->t('Text to display'), - '#default_value' => $this->options['text'], - ); - parent::buildOptionsForm($form, $form_state); - } - - /** - * {@inheritdoc} - */ - public function access(AccountInterface $account) { - return $account->hasPermission('administer users') || $account->hasPermission('access user profiles'); - } - - /** - * {@inheritdoc} - */ - public function query() { - $this->ensureMyTable(); - $this->addAdditionalFields(); - } - - /** - * {@inheritdoc} - */ - public function render(ResultRow $values) { - if ($entity = $this->getEntity($values)) { - return $this->renderLink($entity, $values); - } - } - - /** - * Alters the field to render a link. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * @param \Drupal\views\ResultRow $values - * The current row of the views result. - * - * @return string - * The actual rendered text (without the link) of this field. - */ - protected function renderLink(EntityInterface $entity, ResultRow $values) { - $text = !empty($this->options['text']) ? $this->options['text'] : $this->t('View'); - - $this->options['alter']['make_link'] = TRUE; - $this->options['alter']['url'] = $entity->urlInfo(); - - return $text; - } - -} diff --git a/core/modules/user/src/Plugin/views/field/LinkCancel.php b/core/modules/user/src/Plugin/views/field/LinkCancel.php deleted file mode 100644 index b29c426..0000000 --- a/core/modules/user/src/Plugin/views/field/LinkCancel.php +++ /dev/null @@ -1,38 +0,0 @@ -access('delete')) { - $this->options['alter']['make_link'] = TRUE; - - $text = !empty($this->options['text']) ? $this->options['text'] : $this->t('Cancel account'); - - $this->options['alter']['url'] = $entity->urlInfo('cancel-form'); - $this->options['alter']['query'] = $this->getDestinationArray(); - - return $text; - } - } - -} diff --git a/core/modules/user/src/Plugin/views/field/LinkEdit.php b/core/modules/user/src/Plugin/views/field/LinkEdit.php deleted file mode 100644 index 23a4f5a..0000000 --- a/core/modules/user/src/Plugin/views/field/LinkEdit.php +++ /dev/null @@ -1,37 +0,0 @@ -access('update')) { - $this->options['alter']['make_link'] = TRUE; - - $text = !empty($this->options['text']) ? $this->options['text'] : $this->t('Edit'); - - $this->options['alter']['url'] = $entity->urlInfo('edit-form', ['query' => ['destination' => $this->getDestinationArray()]]); - - return $text; - } - } - -} diff --git a/core/modules/user/src/UserViewsData.php b/core/modules/user/src/UserViewsData.php index edbdb99..cc24f2e 100644 --- a/core/modules/user/src/UserViewsData.php +++ b/core/modules/user/src/UserViewsData.php @@ -94,15 +94,6 @@ public function getViewsData() { $data['users_field_data']['preferred_admin_langcode']['title'] = t('Preferred admin language'); $data['users_field_data']['preferred_admin_langcode']['help'] = t('Preferred administrative language of the user'); - $data['users']['view_user'] = array( - 'field' => array( - 'title' => t('Link to user'), - 'help' => t('Provide a simple link to the user.'), - 'id' => 'user_link', - 'click sortable' => FALSE, - ), - ); - $data['users_field_data']['created_fulldate'] = array( 'title' => t('Created date'), 'help' => t('Date in the form of CCYYMMDD.'), @@ -216,34 +207,6 @@ public function getViewsData() { ), ); - if (\Drupal::moduleHandler()->moduleExists('content_translation')) { - $data['users']['translation_link'] = array( - 'title' => t('Translation link'), - 'help' => t('Provide a link to the translations overview for users.'), - 'field' => array( - 'id' => 'content_translation_link', - ), - ); - } - - $data['users']['edit_node'] = array( - 'field' => array( - 'title' => t('Link to edit user'), - 'help' => t('Provide a simple link to edit the user.'), - 'id' => 'user_link_edit', - 'click sortable' => FALSE, - ), - ); - - $data['users']['cancel_node'] = array( - 'field' => array( - 'title' => t('Link to cancel user'), - 'help' => t('Provide a simple link to cancel the user.'), - 'id' => 'user_link_cancel', - 'click sortable' => FALSE, - ), - ); - $data['users']['data'] = array( 'title' => t('Data'), 'help' => t('Provides access to the user data service.'), diff --git a/core/modules/views/config/schema/views.field.schema.yml b/core/modules/views/config/schema/views.field.schema.yml index de8ffcb..1c884fb 100644 --- a/core/modules/views/config/schema/views.field.schema.yml +++ b/core/modules/views/config/schema/views.field.schema.yml @@ -176,6 +176,23 @@ views.field.language: type: boolean label: 'Display in native language' + +views.field.entity_link: + type: views_field + label: 'Entity link' + mapping: + text: + type: label + label: 'Text to display' + +views.field.entity_link_delete: + type: views.field.entity_link + label: 'Entity delete link' + +views.field.entity_link_edit: + type: views.field.entity_link + label: 'Entity edit link' + views.field.bulk_form: type: views_field_bulk_form label: 'Bulk form' diff --git a/core/modules/views/src/EntityViewsData.php b/core/modules/views/src/EntityViewsData.php index 28e142d..5cd3215 100644 --- a/core/modules/views/src/EntityViewsData.php +++ b/core/modules/views/src/EntityViewsData.php @@ -203,6 +203,8 @@ public function getViewsData() { } } + $this->addEntityLinks($data[$base_table]); + // Load all typed data definitions of all fields. This should cover each of // the entity base, revision, data tables. $field_definitions = $this->entityManager->getBaseFieldDefinitions($this->entityType->id()); @@ -230,6 +232,44 @@ public function getViewsData() { } /** + * Sets the entity links in case corresponding link templates exist. + * + * @param array $data + * The views data of the base table. + */ + protected function addEntityLinks(array &$data) { + $entity_type_id = $this->entityType->id(); + $t_arguments = ['@entity_type_label' => $this->entityType->getLabel()]; + if ($this->entityType->hasLinkTemplate('canonical')) { + $data['view_' . $entity_type_id] = [ + 'field' => [ + 'title' => $this->t('Link to @entity_type_label', $t_arguments), + 'help' => $this->t('Provide a view link to the @entity_type_label.', $t_arguments), + 'id' => 'entity_link', + ], + ]; + } + if ($this->entityType->hasLinkTemplate('edit-form')) { + $data['edit_' . $entity_type_id] = [ + 'field' => [ + 'title' => $this->t('Link to edit @entity_type_label', $t_arguments), + 'help' => $this->t('Provide an edit link to the @entity_type_label.', $t_arguments), + 'id' => 'entity_link_edit', + ], + ]; + } + if ($this->entityType->hasLinkTemplate('delete-form')) { + $data['delete_' . $entity_type_id] = [ + 'field' => [ + 'title' => $this->t('Link to delete @entity_type_label', $t_arguments), + 'help' => $this->t('Provide a delete link to the @entity_type_label.', $t_arguments), + 'id' => 'entity_link_delete', + ], + ]; + } + } + + /** * Puts the views data for a single field onto the views data. * * @param string $table diff --git a/core/modules/views/src/Plugin/views/field/EntityLink.php b/core/modules/views/src/Plugin/views/field/EntityLink.php new file mode 100644 index 0000000..4d07c95 --- /dev/null +++ b/core/modules/views/src/Plugin/views/field/EntityLink.php @@ -0,0 +1,53 @@ +getEntity($values) ? parent::render($values) : ''; + } + + /** + * {@inheritdoc} + */ + protected function getUrlInfo(ResultRow $row) { + $template = $this->getEntityLinkTemplate(); + return $this->getEntity($row)->urlInfo($template); + } + + /** + * Returns the entity link template name identifying the link route. + * + * @returns string + * The link template name. + */ + protected function getEntityLinkTemplate() { + return 'canonical'; + } + + /** + * {@inheritdoc} + */ + protected function getDefaultLabel() { + return $this->t('view'); + } + +} diff --git a/core/modules/views/src/Plugin/views/field/EntityLinkDelete.php b/core/modules/views/src/Plugin/views/field/EntityLinkDelete.php new file mode 100644 index 0000000..673963f --- /dev/null +++ b/core/modules/views/src/Plugin/views/field/EntityLinkDelete.php @@ -0,0 +1,43 @@ +options['alter']['query'] = $this->getDestinationArray(); + return parent::renderLink($row); + } + + /** + * {@inheritdoc} + */ + protected function getDefaultLabel() { + return $this->t('delete'); + } + +} diff --git a/core/modules/views/src/Plugin/views/field/EntityLinkEdit.php b/core/modules/views/src/Plugin/views/field/EntityLinkEdit.php new file mode 100644 index 0000000..69d8b24 --- /dev/null +++ b/core/modules/views/src/Plugin/views/field/EntityLinkEdit.php @@ -0,0 +1,43 @@ +options['alter']['query'] = $this->getDestinationArray(); + return parent::renderLink($row); + } + + /** + * {@inheritdoc} + */ + protected function getDefaultLabel() { + return $this->t('edit'); + } + +} diff --git a/core/modules/views/src/Plugin/views/field/LinkBase.php b/core/modules/views/src/Plugin/views/field/LinkBase.php new file mode 100644 index 0000000..9e122af --- /dev/null +++ b/core/modules/views/src/Plugin/views/field/LinkBase.php @@ -0,0 +1,197 @@ +accessManager = $access_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('access_manager') + ); + } + + /** + * Gets the current active user. + * + * @todo: https://drupal.org/node/2105123 put this method in + * \Drupal\Core\Plugin\PluginBase instead. + * + * @return \Drupal\Core\Session\AccountInterface + * The current user. + */ + protected function currentUser() { + if (!$this->currentUser) { + $this->currentUser = \Drupal::currentUser(); + } + return $this->currentUser; + } + + /** + * {@inheritdoc} + */ + protected function defineOptions() { + $options = parent::defineOptions(); + $options['text'] = array('default' => $this->getDefaultLabel()); + return $options; + } + + /** + * {@inheritdoc} + */ + public function buildOptionsForm(&$form, FormStateInterface $form_state) { + $form['text'] = [ + '#type' => 'textfield', + '#title' => $this->t('Text to display'), + '#default_value' => $this->options['text'], + ]; + parent::buildOptionsForm($form, $form_state); + + // The path is set by ::renderLink() so we do not allow to set it. + $form['alter']['path'] += ['#access' => FALSE]; + $form['alter']['query'] += ['#access' => FALSE]; + $form['alter']['external'] += ['#access' => FALSE]; + } + + /** + * {@inheritdoc} + */ + public function usesGroupBy() { + return FALSE; + } + + /** + * {@inheritdoc} + */ + public function query() { + $this->addAdditionalFields(); + } + + /** + * {@inheritdoc} + */ + public function render(ResultRow $values) { + return $this->checkUrlAccess($values)->isAllowed() ? $this->renderLink($values) : ''; + } + + /** + * Checks access to the link route. + * + * @param \Drupal\views\ResultRow $row + * A view result row. + * + * @return \Drupal\Core\Access\AccessResultInterface + * The access result. + */ + protected function checkUrlAccess(ResultRow $row) { + $url = $this->getUrlInfo($row); + return $this->accessManager->checkNamedRoute($url->getRouteName(), $url->getRouteParameters(), $this->currentUser(), TRUE); + } + + /** + * Returns the URI elements of the link. + * + * @param \Drupal\views\ResultRow $row + * A view result row. + * + * @return \Drupal\Core\Url + * The URI elements of the link. + */ + abstract protected function getUrlInfo(ResultRow $row); + + /** + * Prepares the link to view a entity. + * + * @param \Drupal\views\ResultRow $row + * A view result row. + * + * @return string + * Returns a string for the link text. + */ + protected function renderLink(ResultRow $row) { + $this->options['alter']['make_link'] = TRUE; + $this->options['alter']['url'] = $this->getUrlInfo($row); + $text = !empty($this->options['text']) ? $this->sanitizeValue($this->options['text']) : $this->getDefaultLabel(); + $this->addLangcode($row); + return $text; + } + + /** + * Adds language information to the options. + * + * @param \Drupal\views\ResultRow $row + * A view result row. + */ + protected function addLangcode(ResultRow $row) { + $entity = $this->getEntity($row); + $langcode_key = $entity ? $entity->getEntityType()->getKey('langcode') : FALSE; + if ($langcode_key && isset($this->aliases[$langcode_key])) { + $this->options['alter']['language'] = $entity->language(); + } + } + + /** + * Returns the default label for this link. + * + * @return string + * The default link label. + */ + protected function getDefaultLabel() { + return $this->t('link'); + } + +} diff --git a/core/modules/views/src/Tests/Handler/FieldEntityLinkTest.php b/core/modules/views/src/Tests/Handler/FieldEntityLinkTest.php new file mode 100644 index 0000000..d00b3af --- /dev/null +++ b/core/modules/views/src/Tests/Handler/FieldEntityLinkTest.php @@ -0,0 +1,134 @@ +installEntitySchema('user'); + $this->installEntitySchema('entity_test'); + + // Create some test entities. + for ($i = 0; $i < 5; $i++) { + EntityTest::create(array( + 'name' => $this->randomString(), + ))->save(); + } + + // Create an admin role. + $role = Role::create(['id' => $this->randomMachineName()]); + $role->setIsAdmin(TRUE); + $role->save(); + + // Create and admin user. + $this->adminUser = User::create(['name' => $this->randomString()]); + $this->adminUser->addRole($role->id()); + $this->adminUser->save(); + } + + /** + * Tests entity link fields. + */ + public function testEntityLink() { + // Anonymous users cannot see edit/delete links. + $expected_results = ['canonical' => TRUE, 'edit-form' => FALSE, 'delete-form' => FALSE]; + $this->doTestEntityLink(\Drupal::currentUser(), $expected_results); + + // Admin users cannot see all links. + $expected_results = ['canonical' => TRUE, 'edit-form' => TRUE, 'delete-form' => TRUE]; + $this->doTestEntityLink($this->adminUser, $expected_results); + } + + /** + * Tests whether entity links behave as expected. + * + * @param \Drupal\Core\Session\AccountInterface $account + * The user account to be used to run the test; + * @param bool[] $expected_results + * An associative array of expected results keyed by link template name. + */ + protected function doTestEntityLink(AccountInterface $account, $expected_results) { + \Drupal::currentUser()->setAccount($account); + + $view = Views::getView('test_entity_test_link'); + $view->preview(); + + $info = [ + 'canonical' => [ + 'label' => 'View entity test', + 'field_id' => 'view_entity_test', + 'destination' => FALSE, + ], + 'edit-form' => [ + 'label' => 'Edit entity test', + 'field_id' => 'edit_entity_test', + 'destination' => TRUE, + ], + 'delete-form' => [ + 'label' => 'Delete entity test', + 'field_id' => 'delete_entity_test', + 'destination' => TRUE, + ], + ]; + + $index = 0; + foreach (EntityTest::loadMultiple() as $entity) { + foreach ($expected_results as $template => $expected_result) { + $expected_link = ''; + if ($expected_result) { + $path = $entity->url($template); + $destination = $info[$template]['destination'] ? '?destination=/' : ''; + $expected_link = '' . $info[$template]['label'] . ''; + } + $link = $view->style_plugin->getField($index, $info[$template]['field_id']); + $this->assertEqual($link, $expected_link, SafeMarkup::format('@template entity link behaves as expected.', ['@template' => $template])); + } + $index++; + } + } + +} diff --git a/core/modules/node/config/optional/views.view.content_recent.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_test_link.yml similarity index 51% copy from core/modules/node/config/optional/views.view.content_recent.yml copy to core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_test_link.yml index 1ad0161..8eb7a42 100644 --- a/core/modules/node/config/optional/views.view.content_recent.yml +++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_test_link.yml @@ -2,15 +2,14 @@ langcode: en status: true dependencies: module: - - node - - user -id: content_recent -label: 'Recent content' -module: node -description: 'Recent content.' -tag: default -base_table: node_field_data -base_field: nid + - entity_test +id: test_entity_test_link +label: 'Test Entity Test Link' +module: views +description: '' +tag: '' +base_table: entity_test +base_field: id core: 8.x display: default: @@ -20,9 +19,8 @@ display: position: 0 display_options: access: - type: perm - options: - perm: 'access content' + type: none + options: { } cache: type: none options: { } @@ -45,82 +43,77 @@ display: sort_asc_label: Asc sort_desc_label: Desc pager: - type: some + type: full options: items_per_page: 10 offset: 0 + id: 0 + total_pages: null + expose: + items_per_page: false + items_per_page_label: 'Items per page' + items_per_page_options: '5, 10, 25, 50' + items_per_page_options_all: false + items_per_page_options_all_label: '- All -' + offset: false + offset_label: Offset + tags: + previous: '‹ previous' + next: 'next ›' + first: '« first' + last: 'last »' + quantity: 9 style: - type: table + type: default options: grouping: { } row_class: '' default_row_class: true - override: true - sticky: false - caption: '' - summary: '' - description: '' - columns: - title: title - name: title - edit_node: edit_node - delete_node: delete_node - info: - title: - sortable: false - default_sort_order: asc - align: '' - separator: '' - empty_column: false - responsive: '' - name: - sortable: false - default_sort_order: asc - align: '' - separator: '' - empty_column: false - responsive: '' - edit_node: - sortable: false - default_sort_order: asc - align: '' - separator: '' - empty_column: true - responsive: '' - delete_node: - sortable: false - default_sort_order: asc - align: '' - separator: '' - empty_column: true - responsive: '' - default: '-1' - empty_table: false + uses_fields: false row: type: fields + options: + inline: { } + separator: '' + hide_empty: false + default_field_elements: true fields: - title: - id: title - table: node_field_data - field: title - entity_type: node - entity_field: title + name: + id: name + table: entity_test + field: name + relationship: none + group_type: group + admin_label: '' label: '' exclude: false alter: alter_text: false + text: '' make_link: false + path: '' absolute: false - trim: false - word_boundary: false - ellipsis: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' strip_tags: false + trim: false + preserve_tags: '' html: false - hide_empty: false - empty_zero: false - relationship: none - group_type: group - admin_label: '' element_type: '' element_class: '' element_label_type: '' @@ -130,18 +123,31 @@ display: element_wrapper_class: '' element_default_classes: true empty: '' + hide_empty: false + empty_zero: false hide_alter_empty: true + click_sort_column: value type: string settings: - link_to_entity: true + link_to_entity: false + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + entity_type: null + entity_field: name plugin_id: field - entity_type: node - entity_field: title - name: - id: name - table: users_field_data - field: name - relationship: uid + view_entity_test: + id: view_entity_test + table: entity_test + field: view_entity_test + relationship: none group_type: group admin_label: '' label: '' @@ -173,7 +179,7 @@ display: trim: false preserve_tags: '' html: false - element_type: div + element_type: '' element_class: '' element_label_type: '' element_label_class: '' @@ -185,14 +191,13 @@ display: hide_empty: false empty_zero: false hide_alter_empty: true - entity_type: user - entity_field: name - plugin_id: field - type: user_name - edit_node: - id: edit_node - table: node - field: edit_node + text: 'View entity test' + entity_type: entity_test + plugin_id: entity_link + edit_entity_test: + id: edit_entity_test + table: entity_test + field: edit_entity_test relationship: none group_type: group admin_label: '' @@ -237,13 +242,13 @@ display: hide_empty: false empty_zero: false hide_alter_empty: true - text: Edit - entity_type: node - plugin_id: node_link_edit - delete_node: - id: delete_node - table: node - field: delete_node + text: 'Edit entity test' + entity_type: entity_test + plugin_id: entity_link_edit + delete_entity_test: + id: delete_entity_test + table: entity_test + field: delete_entity_test relationship: none group_type: group admin_label: '' @@ -288,141 +293,34 @@ display: hide_empty: false empty_zero: false hide_alter_empty: true - text: Delete - entity_type: node - plugin_id: node_link_delete - filters: - status_extra: - id: status_extra - table: node_field_data - field: status_extra - relationship: none - group_type: group - admin_label: '' - operator: '=' - value: false - group: 1 - exposed: false - expose: - operator_id: '' - label: '' - description: '' - use_operator: false - operator: '' - identifier: '' - required: false - remember: false - multiple: false - remember_roles: - authenticated: authenticated - is_grouped: false - group_info: - label: '' - description: '' - identifier: '' - optional: true - widget: select - multiple: false - remember: false - default_group: All - default_group_multiple: { } - group_items: { } - entity_type: node - plugin_id: node_status - langcode: - id: langcode - table: node_field_data - field: langcode - relationship: none - group_type: group - admin_label: '' - operator: in - value: - '***LANGUAGE_language_content***': '***LANGUAGE_language_content***' - group: 1 - exposed: false - expose: - operator_id: '' - label: '' - description: '' - use_operator: false - operator: '' - identifier: '' - required: false - remember: false - multiple: false - remember_roles: - authenticated: authenticated - reduce: false - is_grouped: false - group_info: - label: '' - description: '' - identifier: '' - optional: true - widget: select - multiple: false - remember: false - default_group: All - default_group_multiple: { } - group_items: { } - entity_type: node - entity_field: langcode - plugin_id: language + text: 'Delete entity test' + entity_type: entity_test + plugin_id: entity_link_delete + filters: { } sorts: - changed: - id: changed - table: node_field_data - field: changed + id: + id: id + table: entity_test + field: id relationship: none group_type: group admin_label: '' - order: DESC + order: ASC exposed: false expose: label: '' - granularity: second - entity_type: node - entity_field: changed - plugin_id: date - title: 'Recent content' + entity_type: entity_test + entity_field: id + plugin_id: standard header: { } footer: { } - empty: - area_text_custom: - id: area_text_custom - table: views - field: area_text_custom - relationship: none - group_type: group - admin_label: '' - empty: true - tokenize: false - content: 'No content available.' - plugin_id: text_custom - relationships: - uid: - id: uid - table: node_field_data - field: uid - relationship: none - group_type: group - admin_label: author - required: true - entity_type: node - entity_field: uid - plugin_id: standard + empty: { } + relationships: { } arguments: { } display_extenders: { } - use_more: true - use_more_always: true - use_more_text: More - link_url: admin/content - link_display: custom_url - block_1: - display_plugin: block - id: block_1 - display_title: Block - position: 1 - display_options: - display_extenders: { } + cache_metadata: + contexts: + - entity_test_view_grants + - 'languages:language_content' + - 'languages:language_interface' + cacheable: false diff --git a/core/modules/views/tests/src/Unit/EntityViewsDataTest.php b/core/modules/views/tests/src/Unit/EntityViewsDataTest.php index 56d1596..fb34794 100644 --- a/core/modules/views/tests/src/Unit/EntityViewsDataTest.php +++ b/core/modules/views/tests/src/Unit/EntityViewsDataTest.php @@ -712,6 +712,33 @@ protected function assertField($data, $field_name) { } /** + * Tests add link types. + */ + public function testEntityLinks() { + $this->baseEntityType->setLinkTemplate('canonical', '/entity_test/{entity_test}'); + $this->baseEntityType->setLinkTemplate('edit-form', '/entity_test/{entity_test}/edit'); + $this->baseEntityType->setLinkTemplate('delete-form', '/entity_test/{entity_test}/delete'); + + $data = $this->viewsData->getViewsData(); + $this->assertEquals('entity_link', $data['entity_test']['view_entity_test']['field']['id']); + $this->assertEquals('entity_link_edit', $data['entity_test']['edit_entity_test']['field']['id']); + $this->assertEquals('entity_link_delete', $data['entity_test']['delete_entity_test']['field']['id']); + } + + /** + * Tests additional edit links. + */ + public function testEntityLinksJustEditForm() { + $this->baseEntityType->setLinkTemplate('edit-form', '/entity_test/{entity_test}/edit'); + + $data = $this->viewsData->getViewsData(); + $this->assertFalse(isset($data['entity_test']['view_entity_test'])); + $this->assertFalse(isset($data['entity_test']['delete_entity_test'])); + + $this->assertEquals('entity_link_edit', $data['entity_test']['edit_entity_test']['field']['id']); + } + + /** * Tests views data for a string field. * * @param $data