diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Entity/Feed.php b/core/modules/aggregator/lib/Drupal/aggregator/Entity/Feed.php index 24430b9..85e6164 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/Entity/Feed.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/Entity/Feed.php @@ -7,12 +7,14 @@ namespace Drupal\aggregator\Entity; +use Drupal\block\BlockPluginEvents; use Drupal\Core\Entity\ContentEntityBase; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Field\FieldDefinition; use Symfony\Component\DependencyInjection\Container; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\aggregator\FeedInterface; +use Symfony\Component\EventDispatcher\GenericEvent; /** * Defines the aggregator feed entity class. @@ -108,14 +110,10 @@ public static function preDelete(EntityStorageInterface $storage, array $entitie */ public static function postDelete(EntityStorageInterface $storage, array $entities) { if (\Drupal::moduleHandler()->moduleExists('block')) { - // Make sure there are no active blocks for these feeds. - $ids = \Drupal::entityQuery('block') - ->condition('plugin', 'aggregator_feed_block') - ->condition('settings.feed', array_keys($entities)) - ->execute(); - if ($ids) { - $block_storage = \Drupal::entityManager()->getStorage('block'); - $block_storage->delete($block_storage->loadMultiple($ids)); + foreach ($entities as $entity) { + $event = new GenericEvent('aggregator_feed_block', array('conditions' => array('settings.feed' => $entity->id()))); + \Drupal::service('event_dispatcher') + ->dispatch(BlockPluginEvents::PERMANENTLY_UNAVAILABLE, $event); } } } diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorRenderingTest.php b/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorRenderingTest.php index 161d215..4572345 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorRenderingTest.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorRenderingTest.php @@ -75,6 +75,17 @@ public function testBlockLinks() { // Check that the block is no longer displayed. $this->drupalGet('test-page'); $this->assertNoText($block->label(), 'Feed block is not displayed on the page when number of items is set to 0.'); + + // Set the number of news items to 5 to test that the block shows again. + $feed->block = 5; + $feed->save(); + $this->drupalGet('test-page'); + $this->assertText($block->label(), 'Feed block is displayed again on the page when number of items is set to 5.'); + + // Delete the feed and check that the block is no longer displayed. + $feed->delete(); + $this->drupalGet('test-page'); + $this->assertNoText($block->label(), 'Feed block is not displayed on the page once deleted.'); } /** diff --git a/core/modules/block/block.services.yml b/core/modules/block/block.services.yml index 51acbfc..aa49fc0 100644 --- a/core/modules/block/block.services.yml +++ b/core/modules/block/block.services.yml @@ -2,7 +2,14 @@ services: plugin.manager.block: class: Drupal\block\BlockManager arguments: ['@container.namespaces', '@cache.discovery', '@language_manager', '@module_handler', '@string_translation'] + tags: + - { name: event_subscriber } theme.negotiator.block.admin_demo: class: Drupal\block\Theme\AdminDemoNegotiator tags: - { name: theme_negotiator, priority: 1000 } + block.entity.event.subscriber: + class: Drupal\block\BlockEntityBlockEventSubscriber + arguments: ['@entity.query', '@entity.manager'] + tags: + - { name: event_subscriber } diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php b/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php index 71913b7..9f3975b 100644 --- a/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php +++ b/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php @@ -7,11 +7,13 @@ namespace Drupal\custom_block\Entity; +use Drupal\block\BlockPluginEvents; use Drupal\Core\Entity\ContentEntityBase; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Field\FieldDefinition; use Drupal\custom_block\CustomBlockInterface; +use Symfony\Component\EventDispatcher\GenericEvent; /** * Defines the custom block entity class. @@ -138,9 +140,8 @@ public function preSaveRevision(EntityStorageInterface $storage, \stdClass $reco * {@inheritdoc} */ public function delete() { - foreach ($this->getInstances() as $instance) { - $instance->delete(); - } + $event = new GenericEvent('custom_block:' . $this->uuid->value); + \Drupal::service('event_dispatcher')->dispatch(BlockPluginEvents::PERMANENTLY_UNAVAILABLE, $event); parent::delete(); } diff --git a/core/modules/block/lib/Drupal/block/BlockEntityBlockEventSubscriber.php b/core/modules/block/lib/Drupal/block/BlockEntityBlockEventSubscriber.php new file mode 100644 index 0000000..7d997de --- /dev/null +++ b/core/modules/block/lib/Drupal/block/BlockEntityBlockEventSubscriber.php @@ -0,0 +1,75 @@ +queryFactory = $query_factory; + $this->blockStorageController = $entity_manager->getStorage('block'); + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() { + $events[BlockPluginEvents::PERMANENTLY_UNAVAILABLE][] = 'deleteEntityByPlugin'; + return $events; + } + + /** + * Reacts on removing a block plugin. + */ + public function deleteEntityByPlugin(GenericEvent $event) { + $plugin_id = $event->getSubject(); + $query = $this->queryFactory->get('block'); + $query->condition('plugin', $plugin_id); + if ($event->hasArgument('conditions') && $conditions = $event->getArgument('conditions')) { + foreach ($conditions as $condition => $value) { + $query->condition($condition, $value); + } + } + $result = $query->execute(); + if ($blocks = $this->blockStorageController->loadMultiple($result)) { + $this->blockStorageController->delete($blocks); + } + } + +} diff --git a/core/modules/block/lib/Drupal/block/BlockManager.php b/core/modules/block/lib/Drupal/block/BlockManager.php index 4a1651b..a84c801 100644 --- a/core/modules/block/lib/Drupal/block/BlockManager.php +++ b/core/modules/block/lib/Drupal/block/BlockManager.php @@ -13,6 +13,8 @@ use Drupal\Core\Plugin\DefaultPluginManager; use Drupal\Core\StringTranslation\TranslationInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\EventDispatcher\GenericEvent; /** * Manages discovery and instantiation of block plugins. @@ -21,7 +23,7 @@ * * @see \Drupal\block\BlockPluginInterface */ -class BlockManager extends DefaultPluginManager implements BlockManagerInterface { +class BlockManager extends DefaultPluginManager implements BlockManagerInterface, EventSubscriberInterface { use StringTranslationTrait; @@ -116,4 +118,22 @@ public function getSortedDefinitions() { return $definitions; } + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() { + $events[BlockPluginEvents::PERMANENTLY_UNAVAILABLE][] = 'blockPluginRemoved'; + return $events; + } + + /** + * Reacts on removing a block plugin. + * + * @param \Symfony\Component\EventDispatcher\GenericEvent $event + * The event containing the plugin ID which is updated. + */ + public function blockPluginRemoved(GenericEvent $event) { + $this->clearCachedDefinitions(); + } + } diff --git a/core/modules/block/lib/Drupal/block/BlockPluginEvents.php b/core/modules/block/lib/Drupal/block/BlockPluginEvents.php new file mode 100644 index 0000000..29dce73 --- /dev/null +++ b/core/modules/block/lib/Drupal/block/BlockPluginEvents.php @@ -0,0 +1,30 @@ +eventDispatcher = $event_dispatcher; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('event_dispatcher') + ); + } + protected function defineOptions() { $options = parent::defineOptions(); @@ -317,15 +356,14 @@ public function usesExposed() { } /** - * Overrides \Drupal\views\Plugin\views\display\DisplayPluginBase::remove(). + * {@inheritdoc} */ public function remove() { parent::remove(); $plugin_id = 'views_block:' . $this->view->storage->id() . '-' . $this->display['id']; - foreach (entity_load_multiple_by_properties('block', array('plugin' => $plugin_id)) as $block) { - $block->delete(); - } + $event = new GenericEvent($plugin_id); + $this->eventDispatcher->dispatch(BlockPluginEvents::PERMANENTLY_UNAVAILABLE, $event); } } diff --git a/core/modules/block/tests/lib/Drupal/block_test/Plugin/Type/BlockManagerTest.php b/core/modules/block/tests/lib/Drupal/block_test/Plugin/Type/BlockManagerTest.php new file mode 100644 index 0000000..b5d5691 --- /dev/null +++ b/core/modules/block/tests/lib/Drupal/block_test/Plugin/Type/BlockManagerTest.php @@ -0,0 +1,51 @@ + 'Block Plugin Event Test', + 'description' => 'Tests block event responses.', + 'group' => 'Block', + ); + } + + /** + * Tests the reaction when firing block plugin events. + */ + public function testBlockPluginEvents() { + $event_dispatcher = new EventDispatcher(); + + $block_manager = $this->getMockBuilder('Drupal\block\Plugin\Type\BlockManager') + ->disableOriginalConstructor() + ->setMethods(array('clearCachedDefinitions')) + ->getMock(); + + $block_manager->expects($this->once()) + ->method('clearCachedDefinitions'); + + $event_dispatcher->addSubscriber($block_manager); + $event = new GenericEvent('test_plugin_id'); + + $event_dispatcher->dispatch(BlockPluginEvents::PERMANENTLY_UNAVAILABLE, $event); + } + +} diff --git a/core/modules/menu_ui/menu_ui.module b/core/modules/menu_ui/menu_ui.module index b12baee..045211e 100644 --- a/core/modules/menu_ui/menu_ui.module +++ b/core/modules/menu_ui/menu_ui.module @@ -11,11 +11,13 @@ * URLs to be added to the main site navigation menu. */ +use Drupal\block\BlockPluginEvents; use Drupal\Core\Entity\EntityInterface; use Drupal\block\BlockPluginInterface; use Drupal\Core\Render\Element; use Drupal\node\NodeTypeInterface; use Drupal\system\Entity\Menu; +use Symfony\Component\EventDispatcher\GenericEvent; use Symfony\Component\HttpFoundation\JsonResponse; use Drupal\menu_link\Entity\MenuLink; use Drupal\menu_link\MenuLinkStorage; @@ -191,9 +193,11 @@ function menu_ui_menu_predelete(Menu $menu) { function menu_ui_menu_delete(Menu $menu) { menu_cache_clear_all(); - // Invalidate the block cache to update menu-based derivatives. - if (\Drupal::moduleHandler()->moduleExists('block')) { - \Drupal::service('plugin.manager.block')->clearCachedDefinitions(); + // Inform the block plugin system that a menu has been deleted. + if (Drupal::moduleHandler()->moduleExists('block')) { + $event = new GenericEvent('menu_menu_block:' . $menu->id()); + Drupal::service('event_dispatcher') + ->dispatch(BlockPluginEvents::PERMANENTLY_UNAVAILABLE, $event); } }