Commit 7ccdaaef authored by Jonathan Smith's avatar Jonathan Smith
Browse files

Issue #3224340 by jonathan1055, mpp, Berdir, lomale@bluewin.ch, Sseto, emscha,...

Issue #3224340 by jonathan1055, mpp, Berdir, lomale@bluewin.ch, Sseto, emscha, Gertlor, achton: Hardcoded local task dependency on view scheduler_scheduled_content
parent 2162165a
Loading
Loading
Loading
Loading
+5 −47
Original line number Diff line number Diff line
@@ -10,50 +10,8 @@ scheduler.cron_tab:
  weight: 10
  base_route: scheduler.admin_form

content_moderation.content:
  # Use content_moderation.content which is the same key as is used in the core
  # Content Moderation module. If that modules is enabled this avoids two
  # 'Overview' links. If https://www.drupal.org/project/drupal/issues/3199682
  # gets committed then this route could be removed from here.
  title: 'Overview'
  route_name: system.admin_content
  parent_id: system.admin_content

scheduler.scheduled_content:
  title: 'Scheduled content'
  route_name: view.scheduler_scheduled_content.overview
  parent_id: system.admin_content
  # Overview seems to have weight 0 and moderated content is weight 1.
  weight: 5

scheduler.media_overview:
  # This is added so that we get an 'overview' sub-task link alongside the
  # 'scheduled media' sub-task link.
  title: 'Overview'
  route_name: entity.media.collection
  parent_id: entity.media.collection

scheduler.scheduled_media:
  title: 'Scheduled media'
  route_name: view.scheduler_scheduled_media.overview
  parent_id: entity.media.collection
  weight: 5

scheduler.commerce_products:
  # This separate route for the parent_id is needed, because using
  # entity.commerce_product.collection does not render the local task links.
  route_name: scheduler.commerce_products
  base_route: scheduler.commerce_products

scheduler.commerce_product.collection:
  # This is added so that we get an 'overview' sub-task link alongside the
  # 'scheduled products' sub-task link.
  title: 'Overview'
  route_name: entity.commerce_product.collection
  parent_id: scheduler.commerce_products

scheduler.scheduled_products:
  title: 'Scheduled Products'
  route_name: view.scheduler_scheduled_commerce_product.overview
  parent_id: scheduler.commerce_products
  weight: 5
# Modules must not add hardcoded local tasks that depend on configuration, such
# as a view which could be disabled. Therefore we use a deriver to allow
# conditional logic to check for the views.
scheduler.local_tasks:
  deriver: 'Drupal\scheduler\Plugin\Derivative\DynamicLocalTasks'
+0 −6
Original line number Diff line number Diff line
@@ -25,9 +25,3 @@ scheduler.lightweight_cron:
    no_cache: TRUE
  requirements:
    _custom_access: '\Drupal\scheduler\Controller\LightweightCronController::access'

# This duplicate route is required for the local tasks. See links.task.yml.
scheduler.commerce_products:
  path: '/admin/commerce/products'
  requirements:
    _permission: 'access commerce_product overview'
+101 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\scheduler\Plugin\Derivative;

use Drupal\Component\Plugin\Derivative\DeriverBase;
use Drupal\views\Entity\View;

/**
 * Defines dynamic local tasks.
 *
 * The local tasks that define tabs for the 'Scheduled' entity views cannot be
 * hard-coded in the links.task.yml file because if a view is disabled its route
 * will not exist and this produces an exception "Route X does not exist." The
 * routes are defined here instead to enable checking that the views are loaded.
 */
class DynamicLocalTasks extends DeriverBase {

  /**
   * {@inheritdoc}
   */
  public function getDerivativeDefinitions($base_plugin_definition) {

    // Define a local task for scheduled content (nodes) view, only when the
    // view can be loaded, is enabled and that the overview display exists.
    $view = View::load('scheduler_scheduled_content');
    if ($view && $view->status() && $view->getDisplay('overview')) {
      // The content overview has weight 0 and moderated content has weight 1
      // so use weight 5 for the scheduled content tab.
      $this->derivatives['scheduler.scheduled_content'] = [
        'title' => 'Scheduled content',
        'route_name' => 'view.scheduler_scheduled_content.overview',
        'parent_id' => 'system.admin_content',
        'weight' => 5,
      ] + $base_plugin_definition;

      // Core content_moderation module defines an 'overview' local task which
      // is required when adding additional local tasks. If that module is not
      // installed then define the tab here. This can be removed if
      // https://www.drupal.org/project/drupal/issues/3199682 gets committed.
      if (!\Drupal::moduleHandler()->moduleExists('content_moderation')) {
        $this->derivatives['scheduler.content_overview'] = [
          'title' => 'Overview',
          'route_name' => 'system.admin_content',
          'parent_id' => 'system.admin_content',
        ] + $base_plugin_definition;
      }
    }

    $view = View::load('scheduler_scheduled_media');
    if ($view && $view->status() && $view->getDisplay('overview')) {
      // Define local task for scheduled media view.
      $this->derivatives['scheduler.scheduled_media'] = [
        'title' => 'Scheduled media',
        'route_name' => 'view.scheduler_scheduled_media.overview',
        'parent_id' => 'entity.media.collection',
        'weight' => 5,
      ] + $base_plugin_definition;

      // This task is added so that we get an 'overview' sub-task link alongside
      // the 'scheduled media' sub-task link.
      $this->derivatives['scheduler.media_overview'] = [
        'title' => 'Overview',
        'route_name' => 'entity.media.collection',
        'parent_id' => 'entity.media.collection',
      ] + $base_plugin_definition;
    }

    $view = View::load('scheduler_scheduled_commerce_product');
    if ($view && $view->status() && $view->getDisplay('overview')) {

      // The page created by route entity.commerce_product.collection does not
      // have any tabs or sub-links, because the Commerce Product module does
      // not specify any local tasks for this route. Therefore we need a
      // top-level task which just defines the route name as a base route. This
      // will be used as the parent for the two tabs defined below.
      $this->derivatives['scheduler.commerce_products'] = [
        'route_name' => 'entity.commerce_product.collection',
        'base_route' => 'entity.commerce_product.collection',
      ] + $base_plugin_definition;

      // Define local task for the scheduled products view.
      $this->derivatives['scheduler.scheduled_products'] = [
        'title' => 'Scheduled products',
        'route_name' => 'view.scheduler_scheduled_commerce_product.overview',
        'parent_id' => 'scheduler.local_tasks:scheduler.commerce_products',
        'weight' => 5,
      ] + $base_plugin_definition;

      // This task is added so that we get an 'overview' sub-task link alongside
      // the 'scheduled products' sub-task link.
      $this->derivatives['scheduler.commerce_product.collection'] = [
        'title' => 'Overview',
        'route_name' => 'entity.commerce_product.collection',
        'parent_id' => 'scheduler.local_tasks:scheduler.commerce_products',
      ] + $base_plugin_definition;
    }

    return parent::getDerivativeDefinitions($base_plugin_definition);
  }

}
+31 −5
Original line number Diff line number Diff line
@@ -160,12 +160,13 @@ class SchedulerViewsAccessTest extends SchedulerBrowserTestBase {
   */
  public function testViewScheduledContentOverview($entityTypeId, $bundle) {
    $this->createScheduledItems($entityTypeId, $bundle);
    $scheduled_urls = [
      'node' => 'admin/content/scheduled',
      'media' => 'admin/content/media/scheduled',
      'commerce_product' => 'admin/commerce/products/scheduled',

    $admin_urls = [
      'node' => 'admin/content',
      'media' => 'admin/content/media',
      'commerce_product' => 'admin/commerce/products',
    ];
    $scheduled_url = $scheduled_urls[$entityTypeId];
    $scheduled_url = $admin_urls[$entityTypeId] . '/scheduled';
    $assert = $this->assertSession();

    // Try to access the scheduled content overview as an anonymous visitor.
@@ -196,6 +197,31 @@ class SchedulerViewsAccessTest extends SchedulerBrowserTestBase {
    $assert->pageTextContains("$entityTypeId created by Scheduler Editor for unpublishing");
    $assert->pageTextContains("$entityTypeId created by Scheduler Viewer for publishing");
    $assert->pageTextContains("$entityTypeId created by Scheduler Viewer for unpublishing");

    // Log in as admin and check that the main page is available.
    $this->drupalLogin($this->adminUser);
    $this->drupalGet($admin_urls[$entityTypeId]);
    $assert->statusCodeEquals(200);

    // Disable the scheduled view.
    $view_ids = [
      'node' => 'scheduler_scheduled_content',
      'media' => 'scheduler_scheduled_media',
      'commerce_product' => 'scheduler_scheduled_commerce_product',
    ];
    $view = $this->container->get('entity_type.manager')->getStorage('view')->load($view_ids[$entityTypeId]);
    $view->disable()->save();

    // Check access to the main content page is unaffected.
    $this->drupalGet($admin_urls[$entityTypeId]);
    $assert->statusCodeEquals(200);
    $assert->pageTextContains("$entityTypeId created by Scheduler Editor for unpublishing");

    // Attempt to view the scheduled page. Interactively this gives a 404 'page
    // not found' but in phpunit the page is still shown with a 200 code.
    // However the page is empty so we can check that the content is not shown.
    $this->drupalGet($scheduled_url);
    $assert->pageTextNotContains("$entityTypeId created by Scheduler Editor for unpublishing");
  }

}