diff --git a/core/includes/language.inc b/core/includes/language.inc index 976dd75..2f2d69e 100644 --- a/core/includes/language.inc +++ b/core/includes/language.inc @@ -8,6 +8,8 @@ */ use Drupal\Core\Language\Language; +use Drupal\block\BlockPluginEvents; +use Symfony\Component\EventDispatcher\GenericEvent; /** * No language negotiation. The default language is used. @@ -209,6 +211,11 @@ function language_types_disable($types) { foreach ($types as $type) { unset($enabled_types[$type]); + // Inform the block plugin system that a language has been deleted. + if (Drupal::moduleHandler()->moduleExists('block')) { + $event = new GenericEvent('language_block:' . $type); + Drupal::service('event_dispatcher')->dispatch(BlockPluginEvents::PERMANENTLY_UNAVAILABLE, $event); + } } variable_set('language_types', $enabled_types); diff --git a/core/modules/aggregator/aggregator.module b/core/modules/aggregator/aggregator.module index f7a908a..bca5322 100644 --- a/core/modules/aggregator/aggregator.module +++ b/core/modules/aggregator/aggregator.module @@ -6,7 +6,9 @@ */ use Drupal\aggregator\Plugin\Core\Entity\Feed; +use Drupal\block\BlockPluginEvents; use Drupal\Component\Plugin\Exception\PluginException; +use Symfony\Component\EventDispatcher\GenericEvent; /** * Denotes that a feed's items should never expire. @@ -333,9 +335,8 @@ function aggregator_save_category($edit) { ->execute(); // Make sure there is no active block for this category. if (Drupal::moduleHandler()->moduleExists('block')) { - foreach (entity_load_multiple_by_properties('block', array('plugin' => 'aggregator_category_block:' . $edit['cid'])) as $block) { - $block->delete(); - } + $event = new GenericEvent('aggregator_category_block:' . $edit['cid']); + \Drupal::service('event_dispatcher')->dispatch(BlockPluginEvents::DISAPPEAR_PERMANENT, $event); } $edit['title'] = ''; $op = 'delete'; diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Core/Entity/Feed.php b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Core/Entity/Feed.php index 73c015d..a212ac6 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Core/Entity/Feed.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Core/Entity/Feed.php @@ -13,6 +13,8 @@ use Drupal\Core\Entity\Annotation\EntityType; use Drupal\Core\Annotation\Translation; use Drupal\aggregator\FeedInterface; +use Drupal\block\BlockPluginEvents; +use Symfony\Component\EventDispatcher\GenericEvent; /** * Defines the aggregator feed entity class. @@ -227,13 +229,10 @@ public static function preDelete(EntityStorageControllerInterface $storage_contr */ public static function postDelete(EntityStorageControllerInterface $storage_controller, array $entities) { foreach ($entities as $entity) { - // Make sure there is no active block for this feed. - $block_configs = config_get_storage_names_with_prefix('plugin.core.block'); - foreach ($block_configs as $config_id) { - $config = config($config_id); - if ($config->get('id') == 'aggregator_feed_block:' . $entity->id()) { - $config->delete(); - } + // Inform the block plugin system that a feed has been deleted. + if (\Drupal::moduleHandler()->moduleExists('block')) { + $event = new GenericEvent('aggregator_feed_block:' . $entity->id()); + \Drupal::service('event_dispatcher')->dispatch(BlockPluginEvents::PERMANENTLY_UNAVAILABLE, $event); } } } diff --git a/core/modules/block/block.module b/core/modules/block/block.module index 321f584..ab9976d 100644 --- a/core/modules/block/block.module +++ b/core/modules/block/block.module @@ -583,17 +583,6 @@ function block_user_role_delete($role) { } /** - * Implements hook_menu_delete(). - */ -function block_menu_delete($menu) { - foreach (entity_load_multiple('block') as $block_id => $block) { - if ($block->get('plugin') == 'menu_menu_block:' . $menu->id()) { - $block->delete(); - } - } -} - -/** * Implements hook_admin_paths(). */ function block_admin_paths() { diff --git a/core/modules/block/block.services.yml b/core/modules/block/block.services.yml index 9b6b40d..e90c62a 100644 --- a/core/modules/block/block.services.yml +++ b/core/modules/block/block.services.yml @@ -1,7 +1,14 @@ services: + block.entity.event.subscriber: + class: Drupal\block\BlockEntityBlockEventSubscriber + arguments: ['@plugin.manager.entity'] + tags: + - { name: event_subscriber } plugin.manager.block: class: Drupal\block\Plugin\Type\BlockManager arguments: ['@container.namespaces', '@cache.block', '@language_manager', '@module_handler'] + tags: + - { name: event_subscriber } cache.block: class: Drupal\Core\Cache\CacheBackendInterface tags: diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlock.php b/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlock.php index 74dfc25..309da01 100644 --- a/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlock.php +++ b/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlock.php @@ -12,6 +12,8 @@ use Drupal\Core\Entity\Annotation\EntityType; use Drupal\Core\Annotation\Translation; use Drupal\custom_block\CustomBlockInterface; +use Drupal\block\BlockPluginEvents; +use Symfony\Component\EventDispatcher\GenericEvent; /** * Defines the custom block entity class. @@ -225,9 +227,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..3070353 --- /dev/null +++ b/core/modules/block/lib/Drupal/block/BlockEntityBlockEventSubscriber.php @@ -0,0 +1,55 @@ +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(); + foreach ($this->blockStorageController->loadByProperties(array('plugin' => $plugin_id)) as $block) { + $block->delete(); + } + } + +} 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..c4afebe --- /dev/null +++ b/core/modules/block/lib/Drupal/block/BlockPluginEvents.php @@ -0,0 +1,30 @@ +alterInfo($module_handler, 'block'); $this->setCacheBackend($cache_backend, $language_manager, 'block_plugins'); } + + /** + * {@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/Plugin/views/display/Block.php b/core/modules/block/lib/Drupal/block/Plugin/views/display/Block.php index 121d2de..0adb514 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,9 +8,15 @@ namespace Drupal\block\Plugin\views\display; +use Drupal\block\BlockPluginEvents; use Drupal\Component\Annotation\Plugin; use Drupal\Core\Annotation\Translation; use Drupal\views\Plugin\views\display\DisplayPluginBase; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\EventDispatcher\GenericEvent; /** * The plugin that handles a block. @@ -37,6 +43,13 @@ class Block extends DisplayPluginBase { */ protected $usesAttachments = TRUE; + /** + * The event dispatcher. + * + * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface + */ + protected $eventDispatcher; + protected function defineOptions() { $options = parent::defineOptions(); @@ -47,6 +60,35 @@ protected function defineOptions() { } /** + * 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') + ); + } + + /** * The display block handler returns the structure necessary for a block. */ public function execute() { @@ -185,15 +227,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 850feed..2006902 100644 --- a/core/modules/menu/menu.module +++ b/core/modules/menu/menu.module @@ -14,10 +14,12 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\block\BlockPluginInterface; use Drupal\system\Plugin\Core\Entity\Menu; +use Symfony\Component\EventDispatcher\GenericEvent; use Symfony\Component\HttpFoundation\JsonResponse; use Drupal\menu_link\Plugin\Core\Entity\MenuLink; use Drupal\menu_link\MenuLinkStorageController; use Drupal\node\NodeInterface; +use Drupal\block\BlockPluginEvents; /** * Maximum length of menu name as entered by the user. Database length is 32 @@ -303,9 +305,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 (module_exists('block')) { - Drupal::service('plugin.manager.block')->clearCachedDefinitions(); + // Inform the block plugin system you have deleted a menu. + // 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); } }