Loading scheduler.links.task.yml +5 −47 Original line number Diff line number Diff line Loading @@ -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' scheduler.routing.yml +0 −6 Original line number Diff line number Diff line Loading @@ -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' src/Plugin/Derivative/DynamicLocalTasks.php 0 → 100644 +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); } } tests/src/Functional/SchedulerViewsAccessTest.php +31 −5 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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"); } } Loading
scheduler.links.task.yml +5 −47 Original line number Diff line number Diff line Loading @@ -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'
scheduler.routing.yml +0 −6 Original line number Diff line number Diff line Loading @@ -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'
src/Plugin/Derivative/DynamicLocalTasks.php 0 → 100644 +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); } }
tests/src/Functional/SchedulerViewsAccessTest.php +31 −5 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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"); } }