diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Entity/Feed.php b/core/modules/aggregator/lib/Drupal/aggregator/Entity/Feed.php index 861d4c1..196d50f 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\EntityStorageControllerInterface; use Drupal\aggregator\FeedInterface; +use Symfony\Component\EventDispatcher\GenericEvent; /** * Defines the aggregator feed entity class. @@ -106,14 +108,9 @@ public static function preDelete(EntityStorageControllerInterface $storage_contr */ public static function postDelete(EntityStorageControllerInterface $storage_controller, 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()->getStorageController('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 34293d9..e798089 100644 --- a/core/modules/block/block.services.yml +++ b/core/modules/block/block.services.yml @@ -2,6 +2,8 @@ services: plugin.manager.block: class: Drupal\block\Plugin\Type\BlockManager arguments: ['@container.namespaces', '@cache.block', '@language_manager', '@module_handler', '@string_translation'] + tags: + - { name: event_subscriber } cache.block: class: Drupal\Core\Cache\CacheBackendInterface tags: @@ -13,3 +15,8 @@ services: 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 88d232b..b6a2336 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\EntityStorageControllerInterface; 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. @@ -145,9 +147,8 @@ public function preSaveRevision(EntityStorageControllerInterface $storage_contro * {@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..1ba86b3 --- /dev/null +++ b/core/modules/block/lib/Drupal/block/BlockEntityBlockEventSubscriber.php @@ -0,0 +1,75 @@ +queryFactory = $query_factory; + $this->blockStorageController = $entity_manager->getStorageController('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 ($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/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 @@ +clearCachedDefinitions(); + } + } diff --git a/core/modules/block/lib/Drupal/block/Plugin/views/display/Block.php b/core/modules/block/lib/Drupal/block/Plugin/views/display/Block.php index 94bea95..0b49448 100644 --- a/core/modules/block/lib/Drupal/block/Plugin/views/display/Block.php +++ b/core/modules/block/lib/Drupal/block/Plugin/views/display/Block.php @@ -8,10 +8,13 @@ namespace Drupal\block\Plugin\views\display; +use Drupal\block\BlockPluginEvents; use Drupal\Component\Utility\String; use Drupal\views\Plugin\Block\ViewsBlock; use Drupal\views\Plugin\views\display\DisplayPluginBase; -use Drupal\views\Views; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\EventDispatcher\GenericEvent; /** * The plugin that handles a block. @@ -41,6 +44,42 @@ class Block extends DisplayPluginBase { */ protected $usesAttachments = TRUE; + /** + * The event dispatcher. + * + * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface + */ + protected $eventDispatcher; + + /** + * Constructs a Block 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 array $plugin_definition + * The plugin implementation definition. + * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface + */ + public function __construct(array $configuration, $plugin_id, array $plugin_definition, EventDispatcherInterface $event_dispatcher) { + parent::__construct($configuration, $plugin_id, $plugin_definition); + + $this->eventDispatcher = $event_dispatcher; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $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/menu.module b/core/modules/menu/menu.module index 660e1df..7b3a51f 100644 --- a/core/modules/menu/menu.module +++ b/core/modules/menu/menu.module @@ -11,10 +11,12 @@ * 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\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\MenuLinkStorageController; @@ -196,9 +198,11 @@ function menu_menu_predelete(Menu $menu) { function menu_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); } }