diff --git a/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php index e1d0731..66a4284 100644 --- a/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php +++ b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php @@ -7,6 +7,7 @@ namespace Drupal\Core\Plugin; +use Drupal\block\BlockPluginEvents; use Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface; use Drupal\Component\Plugin\Discovery\DiscoveryCachedTrait; use Drupal\Component\Plugin\Exception\PluginNotFoundException; @@ -20,13 +21,14 @@ use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery; use Drupal\Core\Plugin\Factory\ContainerFactory; +use Symfony\Component\EventDispatcher\GenericEvent; /** * Base class for plugin managers. * * @ingroup plugin_api */ -class DefaultPluginManager extends PluginManagerBase implements PluginManagerInterface, CachedDiscoveryInterface { +class DefaultPluginManager extends PluginManagerBase implements PluginManagerInterface, CachedDiscoveryInterface, DefaultPluginManagerInterface { use DiscoveryCachedTrait; @@ -105,24 +107,7 @@ public function __construct($subdir, \Traversable $namespaces, ModuleHandlerInte } /** - * Initialize the cache backend. - * - * Plugin definitions are cached using the provided cache backend. The - * interface language is added as a suffix to the cache key. - * - * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend - * Cache backend instance to use. - * @param string $cache_key - * Cache key prefix to use, the language code will be appended - * automatically. - * @param array $cache_tags - * (optional) When providing a list of cache tags, the cached plugin - * definitions are tagged with the provided cache tags. These cache tags can - * then be used to clear the corresponding cached plugin definitions. Note - * that this should be used with care! For clearing all cached plugin - * definitions of a plugin manager, call that plugin manager's - * clearCachedDefinitions() method. Only use cache tags when cached plugin - * definitions should be cleared along with other, related cache entries. + * {@inheritdoc} */ public function setCacheBackend(CacheBackendInterface $cache_backend, $cache_key, array $cache_tags = array()) { $this->cacheBackend = $cache_backend; @@ -241,4 +226,17 @@ protected function findDefinitions() { return $definitions; } + /** + * {@inheritdoc} + */ + public function removePluginId($plugin_id, $permanent = TRUE, array $conditions = array()) { + /** @var \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher */ + $event_dispatcher = \Drupal::service('event_dispatcher'); + $event_dispatcher->dispatch($permanent ? BlockPluginEvents::PERMANENTLY_UNAVAILABLE : BlockPluginEvents::TEMPORARILY_UNAVAILABLE, new GenericEvent(array( + 'plugin_id' => $plugin_id, + 'permanent' => $permanent, + 'conditions' => $conditions, + ))); + } + } diff --git a/core/lib/Drupal/Core/Plugin/DefaultPluginManagerInterface.php b/core/lib/Drupal/Core/Plugin/DefaultPluginManagerInterface.php new file mode 100644 index 0000000..9f21d72 --- /dev/null +++ b/core/lib/Drupal/Core/Plugin/DefaultPluginManagerInterface.php @@ -0,0 +1,58 @@ +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)); + /** @var \Drupal\Core\Plugin\DefaultPluginManagerInterface $block_manager */ + if (\Drupal::hasService('plugin.manager.block') && $block_manager = \Drupal::service('plugin.manager.block')) { + foreach ($entities as $entity) { + $block_manager->removePluginId('aggregator_feed_block', FALSE, array('conditions' => array('settings.feed' => $entity->id()))); } } } diff --git a/core/modules/aggregator/src/Tests/AggregatorRenderingTest.php b/core/modules/aggregator/src/Tests/AggregatorRenderingTest.php index d967d1b..be436cc 100644 --- a/core/modules/aggregator/src/Tests/AggregatorRenderingTest.php +++ b/core/modules/aggregator/src/Tests/AggregatorRenderingTest.php @@ -77,6 +77,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 e5e58d1..a7f7330 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\BlockManager arguments: ['@container.namespaces', '@cache.discovery', '@module_handler', '@string_translation'] + tags: + - { name: event_subscriber } theme.negotiator.block.admin_demo: class: Drupal\block\Theme\AdminDemoNegotiator tags: @@ -21,3 +23,8 @@ services: arguments: ['@current_route_match'] tags: - { name: 'event_subscriber' } + block.entity.event.subscriber: + class: Drupal\block\BlockEntityBlockEventSubscriber + arguments: ['@entity.query', '@entity.manager'] + tags: + - { name: event_subscriber } diff --git a/core/modules/block/src/BlockEntityBlockEventSubscriber.php b/core/modules/block/src/BlockEntityBlockEventSubscriber.php new file mode 100644 index 0000000..e569a8a --- /dev/null +++ b/core/modules/block/src/BlockEntityBlockEventSubscriber.php @@ -0,0 +1,73 @@ +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/src/BlockManager.php b/core/modules/block/src/BlockManager.php index 706af51..a6b9c95 100644 --- a/core/modules/block/src/BlockManager.php +++ b/core/modules/block/src/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; use ContextAwarePluginManagerTrait; @@ -115,4 +117,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/src/BlockManagerInterface.php b/core/modules/block/src/BlockManagerInterface.php index 6656004..b6598f8 100644 --- a/core/modules/block/src/BlockManagerInterface.php +++ b/core/modules/block/src/BlockManagerInterface.php @@ -8,11 +8,12 @@ namespace Drupal\block; use Drupal\Core\Plugin\Context\ContextAwarePluginManagerInterface; +use Drupal\Core\Plugin\DefaultPluginManagerInterface; /** * Provides an interface for the discovery and instantiation of block plugins. */ -interface BlockManagerInterface extends ContextAwarePluginManagerInterface { +interface BlockManagerInterface extends ContextAwarePluginManagerInterface, DefaultPluginManagerInterface { /** * Gets the names of all block categories. diff --git a/core/modules/block/src/BlockPluginEvents.php b/core/modules/block/src/BlockPluginEvents.php new file mode 100644 index 0000000..29dce73 --- /dev/null +++ b/core/modules/block/src/BlockPluginEvents.php @@ -0,0 +1,30 @@ +blockManager = $block_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return parent::create($configuration, $plugin_id, $plugin_definition, + $container->get('plugin.manager.block') + ); + } + + /** * Returns plugin-specific settings for the block. * * @param array $settings @@ -318,15 +356,13 @@ 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(); - } + $this->blockManager->removePluginId($plugin_id, TRUE); } } diff --git a/core/modules/block/tests/src/BlockManagerTest.php b/core/modules/block/tests/src/BlockManagerTest.php new file mode 100644 index 0000000..ca6334f --- /dev/null +++ b/core/modules/block/tests/src/BlockManagerTest.php @@ -0,0 +1,48 @@ +getMockBuilder('Drupal\block\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/block_content/src/Entity/BlockContent.php b/core/modules/block_content/src/Entity/BlockContent.php index 630fe51..a5784fa 100644 --- a/core/modules/block_content/src/Entity/BlockContent.php +++ b/core/modules/block_content/src/Entity/BlockContent.php @@ -127,10 +127,11 @@ public function preSaveRevision(EntityStorageInterface $storage, \stdClass $reco * {@inheritdoc} */ public function delete() { - foreach ($this->getInstances() as $instance) { - $instance->delete(); - } parent::delete(); + + /** @var \Drupal\block\BlockManagerInterface $block_manager */ + $block_manager = \Drupal::service('plugin.manager.block'); + $block_manager->removePluginId('custom_block:' . $this->uuid()); } /** diff --git a/core/modules/system/src/Entity/Menu.php b/core/modules/system/src/Entity/Menu.php index 756728f..e5d0f9f 100644 --- a/core/modules/system/src/Entity/Menu.php +++ b/core/modules/system/src/Entity/Menu.php @@ -64,4 +64,15 @@ public function isLocked() { return (bool) $this->locked; } + public static function postDelete(EntityStorageInterface $storage, array $entities) { + parent::postDelete($storage, $entities); + + foreach ($entities as $entity) { + /** @var \Drupal\block\BlockManagerInterface $block_manager */ + $block_manager = \Drupal::service('plugin.manager.block'); + $block_manager->removePluginId('menu_menu_block:' . $entity->id()); + } + } + + }