From f1751c44286930ea4ef41b046cdc4570330ee301 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Thu, 8 Dec 2022 10:55:19 +0100 Subject: [PATCH] Introduced app enable/disable/update typed events OCP\App\ManagerEvent is depreciated since 22 without a replacement Signed-off-by: Thomas Citharel --- lib/composer/composer/autoload_classmap.php | 3 + lib/composer/composer/autoload_static.php | 3 + lib/private/App/AppManager.php | 29 +++++++--- lib/private/Server.php | 1 + lib/private/legacy/OC_App.php | 6 +- lib/public/App/Events/AppDisableEvent.php | 51 +++++++++++++++++ lib/public/App/Events/AppEnableEvent.php | 62 +++++++++++++++++++++ lib/public/App/Events/AppUpdateEvent.php | 51 +++++++++++++++++ tests/lib/App/AppManagerTest.php | 28 ++++++++-- tests/lib/AppTest.php | 4 +- 10 files changed, 221 insertions(+), 17 deletions(-) create mode 100644 lib/public/App/Events/AppDisableEvent.php create mode 100644 lib/public/App/Events/AppEnableEvent.php create mode 100644 lib/public/App/Events/AppUpdateEvent.php diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index ee6117f9b7344..1ecd8152c0f37 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -82,6 +82,9 @@ 'OCP\\AppFramework\\Utility\\IControllerMethodReflector' => $baseDir . '/lib/public/AppFramework/Utility/IControllerMethodReflector.php', 'OCP\\AppFramework\\Utility\\ITimeFactory' => $baseDir . '/lib/public/AppFramework/Utility/ITimeFactory.php', 'OCP\\App\\AppPathNotFoundException' => $baseDir . '/lib/public/App/AppPathNotFoundException.php', + 'OCP\\App\\Events\\AppDisableEvent' => $baseDir . '/lib/public/App/Events/AppDisableEvent.php', + 'OCP\\App\\Events\\AppEnableEvent' => $baseDir . '/lib/public/App/Events/AppEnableEvent.php', + 'OCP\\App\\Events\\AppUpdateEvent' => $baseDir . '/lib/public/App/Events/AppUpdateEvent.php', 'OCP\\App\\IAppManager' => $baseDir . '/lib/public/App/IAppManager.php', 'OCP\\App\\ManagerEvent' => $baseDir . '/lib/public/App/ManagerEvent.php', 'OCP\\Authentication\\Events\\AnyLoginFailedEvent' => $baseDir . '/lib/public/Authentication/Events/AnyLoginFailedEvent.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 253b9ddbadadc..2127835e51483 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -115,6 +115,9 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\AppFramework\\Utility\\IControllerMethodReflector' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Utility/IControllerMethodReflector.php', 'OCP\\AppFramework\\Utility\\ITimeFactory' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Utility/ITimeFactory.php', 'OCP\\App\\AppPathNotFoundException' => __DIR__ . '/../../..' . '/lib/public/App/AppPathNotFoundException.php', + 'OCP\\App\\Events\\AppDisableEvent' => __DIR__ . '/../../..' . '/lib/public/App/Events/AppDisableEvent.php', + 'OCP\\App\\Events\\AppEnableEvent' => __DIR__ . '/../../..' . '/lib/public/App/Events/AppEnableEvent.php', + 'OCP\\App\\Events\\AppUpdateEvent' => __DIR__ . '/../../..' . '/lib/public/App/Events/AppUpdateEvent.php', 'OCP\\App\\IAppManager' => __DIR__ . '/../../..' . '/lib/public/App/IAppManager.php', 'OCP\\App\\ManagerEvent' => __DIR__ . '/../../..' . '/lib/public/App/ManagerEvent.php', 'OCP\\Authentication\\Events\\AnyLoginFailedEvent' => __DIR__ . '/../../..' . '/lib/public/Authentication/Events/AnyLoginFailedEvent.php', diff --git a/lib/private/App/AppManager.php b/lib/private/App/AppManager.php index d14f0a2644e03..0a89711f17898 100644 --- a/lib/private/App/AppManager.php +++ b/lib/private/App/AppManager.php @@ -40,8 +40,11 @@ use OC\AppConfig; use OCP\App\AppPathNotFoundException; +use OCP\App\Events\AppDisableEvent; +use OCP\App\Events\AppEnableEvent; use OCP\App\IAppManager; use OCP\App\ManagerEvent; +use OCP\EventDispatcher\IEventDispatcher; use OCP\ICacheFactory; use OCP\IConfig; use OCP\IGroup; @@ -80,7 +83,9 @@ class AppManager implements IAppManager { private $memCacheFactory; /** @var EventDispatcherInterface */ - private $dispatcher; + private $legacyDispatcher; + + private IEventDispatcher $dispatcher; /** @var LoggerInterface */ private $logger; @@ -108,13 +113,15 @@ public function __construct(IUserSession $userSession, AppConfig $appConfig, IGroupManager $groupManager, ICacheFactory $memCacheFactory, - EventDispatcherInterface $dispatcher, + EventDispatcherInterface $legacyDispatcher, + IEventDispatcher $dispatcher, LoggerInterface $logger) { $this->userSession = $userSession; $this->config = $config; $this->appConfig = $appConfig; $this->groupManager = $groupManager; $this->memCacheFactory = $memCacheFactory; + $this->legacyDispatcher = $legacyDispatcher; $this->dispatcher = $dispatcher; $this->logger = $logger; } @@ -163,7 +170,7 @@ public function getEnabledAppsForUser(IUser $user) { } /** - * @param \OCP\IGroup $group + * @param IGroup $group * @return array */ public function getEnabledAppsForGroup(IGroup $group): array { @@ -287,7 +294,7 @@ private function checkAppForGroups(string $enabled, IGroup $group): bool { * Notice: This actually checks if the app is enabled and not only if it is installed. * * @param string $appId - * @param \OCP\IGroup[]|String[] $groups + * @param IGroup[]|String[] $groups * @return bool */ public function isInstalled($appId) { @@ -320,7 +327,8 @@ public function enableApp(string $appId, bool $forceEnable = false): void { $this->installedAppsCache[$appId] = 'yes'; $this->appConfig->setValue($appId, 'enabled', 'yes'); - $this->dispatcher->dispatch(ManagerEvent::EVENT_APP_ENABLE, new ManagerEvent( + $this->dispatcher->dispatchTyped(new AppEnableEvent($appId)); + $this->legacyDispatcher->dispatch(ManagerEvent::EVENT_APP_ENABLE, new ManagerEvent( ManagerEvent::EVENT_APP_ENABLE, $appId )); $this->clearAppsCache(); @@ -345,7 +353,7 @@ public function hasProtectedAppType($types) { * Enable an app only for specific groups * * @param string $appId - * @param \OCP\IGroup[] $groups + * @param IGroup[] $groups * @param bool $forceEnable * @throws \InvalidArgumentException if app can't be enabled for groups * @throws AppPathNotFoundException @@ -363,8 +371,9 @@ public function enableAppForGroups(string $appId, array $groups, bool $forceEnab $this->ignoreNextcloudRequirementForApp($appId); } + /** @var string[] $groupIds */ $groupIds = array_map(function ($group) { - /** @var \OCP\IGroup $group */ + /** @var IGroup $group */ return ($group instanceof IGroup) ? $group->getGID() : $group; @@ -372,7 +381,8 @@ public function enableAppForGroups(string $appId, array $groups, bool $forceEnab $this->installedAppsCache[$appId] = json_encode($groupIds); $this->appConfig->setValue($appId, 'enabled', json_encode($groupIds)); - $this->dispatcher->dispatch(ManagerEvent::EVENT_APP_ENABLE_FOR_GROUPS, new ManagerEvent( + $this->dispatcher->dispatchTyped(new AppEnableEvent($appId, $groupIds)); + $this->legacyDispatcher->dispatch(ManagerEvent::EVENT_APP_ENABLE_FOR_GROUPS, new ManagerEvent( ManagerEvent::EVENT_APP_ENABLE_FOR_GROUPS, $appId, $groups )); $this->clearAppsCache(); @@ -407,7 +417,8 @@ public function disableApp($appId, $automaticDisabled = false) { \OC_App::executeRepairSteps($appId, $appData['repair-steps']['uninstall']); } - $this->dispatcher->dispatch(ManagerEvent::EVENT_APP_DISABLE, new ManagerEvent( + $this->dispatcher->dispatchTyped(new AppDisableEvent($appId)); + $this->legacyDispatcher->dispatch(ManagerEvent::EVENT_APP_DISABLE, new ManagerEvent( ManagerEvent::EVENT_APP_DISABLE, $appId )); $this->clearAppsCache(); diff --git a/lib/private/Server.php b/lib/private/Server.php index f9fc585e74d76..9a4ee0da19882 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -928,6 +928,7 @@ public function __construct($webRoot, \OC\Config $config) { $c->get(IGroupManager::class), $c->get(ICacheFactory::class), $c->get(SymfonyAdapter::class), + $c->get(IEventDispatcher::class), $c->get(LoggerInterface::class) ); }); diff --git a/lib/private/legacy/OC_App.php b/lib/private/legacy/OC_App.php index 7f51d81d21bba..a7887d2bed7f4 100644 --- a/lib/private/legacy/OC_App.php +++ b/lib/private/legacy/OC_App.php @@ -50,9 +50,12 @@ * along with this program. If not, see * */ + +use OCP\App\Events\AppUpdateEvent; use OCP\AppFramework\QueryException; use OCP\App\ManagerEvent; use OCP\Authentication\IAlternativeLogin; +use OCP\EventDispatcher\IEventDispatcher; use OCP\ILogger; use OCP\Settings\IManager as ISettingsManager; use OC\AppFramework\Bootstrap\Coordinator; @@ -1042,6 +1045,7 @@ public static function updateApp(string $appId): bool { $version = \OC_App::getAppVersion($appId); \OC::$server->getConfig()->setAppValue($appId, 'installed_version', $version); + \OC::$server->get(IEventDispatcher::class)->dispatchTyped(new AppUpdateEvent($appId)); \OC::$server->getEventDispatcher()->dispatch(ManagerEvent::EVENT_APP_UPDATE, new ManagerEvent( ManagerEvent::EVENT_APP_UPDATE, $appId )); @@ -1061,7 +1065,7 @@ public static function executeRepairSteps(string $appId, array $steps) { // load the app self::loadApp($appId); - $dispatcher = \OC::$server->get(\OCP\EventDispatcher\IEventDispatcher::class); + $dispatcher = \OC::$server->get(IEventDispatcher::class); // load the steps $r = new Repair([], $dispatcher, \OC::$server->get(LoggerInterface::class)); diff --git a/lib/public/App/Events/AppDisableEvent.php b/lib/public/App/Events/AppDisableEvent.php new file mode 100644 index 0000000000000..5a50587446fe3 --- /dev/null +++ b/lib/public/App/Events/AppDisableEvent.php @@ -0,0 +1,51 @@ + + * + * @author Thomas Citharel + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ +namespace OCP\App\Events; + +use OCP\EventDispatcher\Event; + +/** + * @since 27.0.0 + */ +class AppDisableEvent extends Event { + private string $appId; + + /** + * @since 27.0.0 + */ + public function __construct(string $appId) { + parent::__construct(); + + $this->appId = $appId; + } + + /** + * @since 27.0.0 + */ + public function getAppId(): string { + return $this->appId; + } +} diff --git a/lib/public/App/Events/AppEnableEvent.php b/lib/public/App/Events/AppEnableEvent.php new file mode 100644 index 0000000000000..1aff3630f86cc --- /dev/null +++ b/lib/public/App/Events/AppEnableEvent.php @@ -0,0 +1,62 @@ + + * + * @author Thomas Citharel + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ +namespace OCP\App\Events; + +use OCP\EventDispatcher\Event; + +/** + * @since 27.0.0 + */ +class AppEnableEvent extends Event { + private string $appId; + /** @var string[] */ + private array $groupIds; + + /** + * @param string[] $groupIds + * @since 27.0.0 + */ + public function __construct(string $appId, array $groupIds = []) { + parent::__construct(); + + $this->appId = $appId; + $this->groupIds = $groupIds; + } + + /** + * @since 27.0.0 + */ + public function getAppId(): string { + return $this->appId; + } + + /** + * @since 27.0.0 + */ + public function getGroupIds(): array { + return $this->groupIds; + } +} diff --git a/lib/public/App/Events/AppUpdateEvent.php b/lib/public/App/Events/AppUpdateEvent.php new file mode 100644 index 0000000000000..92f1f8f9b16fe --- /dev/null +++ b/lib/public/App/Events/AppUpdateEvent.php @@ -0,0 +1,51 @@ + + * + * @author Thomas Citharel + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ +namespace OCP\App\Events; + +use OCP\EventDispatcher\Event; + +/** + * @since 27.0.0 + */ +class AppUpdateEvent extends Event { + private string $appId; + + /** + * @since 27.0.0 + */ + public function __construct(string $appId) { + parent::__construct(); + + $this->appId = $appId; + } + + /** + * @since 27.0.0 + */ + public function getAppId(): string { + return $this->appId; + } +} diff --git a/tests/lib/App/AppManagerTest.php b/tests/lib/App/AppManagerTest.php index de515837406ff..bf9592ac6a685 100644 --- a/tests/lib/App/AppManagerTest.php +++ b/tests/lib/App/AppManagerTest.php @@ -14,7 +14,10 @@ use OC\App\AppManager; use OC\AppConfig; use OCP\App\AppPathNotFoundException; +use OCP\App\Events\AppDisableEvent; +use OCP\App\Events\AppEnableEvent; use OCP\App\IAppManager; +use OCP\EventDispatcher\IEventDispatcher; use OCP\ICache; use OCP\ICacheFactory; use OCP\IConfig; @@ -91,6 +94,9 @@ protected function getAppConfig() { protected $cacheFactory; /** @var EventDispatcherInterface|MockObject */ + protected $legacyEventDispatcher; + + /** @var IEventDispatcher|MockObject */ protected $eventDispatcher; /** @var LoggerInterface|MockObject */ @@ -108,7 +114,8 @@ protected function setUp(): void { $this->appConfig = $this->getAppConfig(); $this->cacheFactory = $this->createMock(ICacheFactory::class); $this->cache = $this->createMock(ICache::class); - $this->eventDispatcher = $this->createMock(EventDispatcherInterface::class); + $this->legacyEventDispatcher = $this->createMock(EventDispatcherInterface::class); + $this->eventDispatcher = $this->createMock(IEventDispatcher::class); $this->logger = $this->createMock(LoggerInterface::class); $this->cacheFactory->expects($this->any()) ->method('createDistributed') @@ -120,6 +127,7 @@ protected function setUp(): void { $this->appConfig, $this->groupManager, $this->cacheFactory, + $this->legacyEventDispatcher, $this->eventDispatcher, $this->logger ); @@ -137,12 +145,14 @@ public function testEnableApp() { if ($this->manager->isEnabledForUser('files_trashbin')) { $this->manager->disableApp('files_trashbin'); } + $this->eventDispatcher->expects($this->once())->method('dispatchTyped')->with(new AppEnableEvent('files_trashbin')); $this->manager->enableApp('files_trashbin'); $this->assertEquals('yes', $this->appConfig->getValue('files_trashbin', 'enabled', 'no')); } public function testDisableApp() { $this->expectClearCache(); + $this->eventDispatcher->expects($this->once())->method('dispatchTyped')->with(new AppDisableEvent('files_trashbin')); $this->manager->disableApp('files_trashbin'); $this->assertEquals('no', $this->appConfig->getValue('files_trashbin', 'enabled', 'no')); } @@ -175,7 +185,7 @@ public function testEnableAppForGroups() { /** @var AppManager|MockObject $manager */ $manager = $this->getMockBuilder(AppManager::class) ->setConstructorArgs([ - $this->userSession, $this->config, $this->appConfig, $this->groupManager, $this->cacheFactory, $this->eventDispatcher, $this->logger + $this->userSession, $this->config, $this->appConfig, $this->groupManager, $this->cacheFactory, $this->legacyEventDispatcher, $this->eventDispatcher, $this->logger ]) ->setMethods([ 'getAppPath', @@ -187,6 +197,8 @@ public function testEnableAppForGroups() { ->with('test') ->willReturn('apps/test'); + $this->eventDispatcher->expects($this->once())->method('dispatchTyped')->with(new AppEnableEvent('test', ['group1', 'group2'])); + $manager->enableAppForGroups('test', $groups); $this->assertEquals('["group1","group2"]', $this->appConfig->getValue('test', 'enabled', 'no')); } @@ -222,7 +234,7 @@ public function testEnableAppForGroupsAllowedTypes(array $appInfo) { /** @var AppManager|MockObject $manager */ $manager = $this->getMockBuilder(AppManager::class) ->setConstructorArgs([ - $this->userSession, $this->config, $this->appConfig, $this->groupManager, $this->cacheFactory, $this->eventDispatcher, $this->logger + $this->userSession, $this->config, $this->appConfig, $this->groupManager, $this->cacheFactory, $this->legacyEventDispatcher, $this->eventDispatcher, $this->logger ]) ->setMethods([ 'getAppPath', @@ -240,6 +252,8 @@ public function testEnableAppForGroupsAllowedTypes(array $appInfo) { ->with('test') ->willReturn($appInfo); + $this->eventDispatcher->expects($this->once())->method('dispatchTyped')->with(new AppEnableEvent('test', ['group1', 'group2'])); + $manager->enableAppForGroups('test', $groups); $this->assertEquals('["group1","group2"]', $this->appConfig->getValue('test', 'enabled', 'no')); } @@ -276,7 +290,7 @@ public function testEnableAppForGroupsForbiddenTypes($type) { /** @var AppManager|MockObject $manager */ $manager = $this->getMockBuilder(AppManager::class) ->setConstructorArgs([ - $this->userSession, $this->config, $this->appConfig, $this->groupManager, $this->cacheFactory, $this->eventDispatcher, $this->logger + $this->userSession, $this->config, $this->appConfig, $this->groupManager, $this->cacheFactory, $this->legacyEventDispatcher, $this->eventDispatcher, $this->logger ]) ->setMethods([ 'getAppPath', @@ -296,6 +310,8 @@ public function testEnableAppForGroupsForbiddenTypes($type) { 'types' => [$type], ]); + $this->eventDispatcher->expects($this->never())->method('dispatchTyped')->with(new AppEnableEvent('test', ['group1', 'group2'])); + $manager->enableAppForGroups('test', $groups); } @@ -470,7 +486,7 @@ public function testGetAppsForUser() { public function testGetAppsNeedingUpgrade() { /** @var AppManager|MockObject $manager */ $manager = $this->getMockBuilder(AppManager::class) - ->setConstructorArgs([$this->userSession, $this->config, $this->appConfig, $this->groupManager, $this->cacheFactory, $this->eventDispatcher, $this->logger]) + ->setConstructorArgs([$this->userSession, $this->config, $this->appConfig, $this->groupManager, $this->cacheFactory, $this->legacyEventDispatcher, $this->eventDispatcher, $this->logger]) ->setMethods(['getAppInfo']) ->getMock(); @@ -521,7 +537,7 @@ function ($appId) use ($appInfos) { public function testGetIncompatibleApps() { /** @var AppManager|MockObject $manager */ $manager = $this->getMockBuilder(AppManager::class) - ->setConstructorArgs([$this->userSession, $this->config, $this->appConfig, $this->groupManager, $this->cacheFactory, $this->eventDispatcher, $this->logger]) + ->setConstructorArgs([$this->userSession, $this->config, $this->appConfig, $this->groupManager, $this->cacheFactory, $this->legacyEventDispatcher, $this->eventDispatcher, $this->logger]) ->setMethods(['getAppInfo']) ->getMock(); diff --git a/tests/lib/AppTest.php b/tests/lib/AppTest.php index 4b2619a3761ca..5cdee5e120068 100644 --- a/tests/lib/AppTest.php +++ b/tests/lib/AppTest.php @@ -12,6 +12,7 @@ use OC\App\AppManager; use OC\App\InfoParser; use OC\AppConfig; +use OCP\EventDispatcher\IEventDispatcher; use OCP\IAppConfig; use Psr\Log\LoggerInterface; @@ -553,13 +554,14 @@ private function setupAppConfigMock() { */ private function registerAppConfig(AppConfig $appConfig) { $this->overwriteService(AppConfig::class, $appConfig); - $this->overwriteService(AppManager::class, new \OC\App\AppManager( + $this->overwriteService(AppManager::class, new AppManager( \OC::$server->getUserSession(), \OC::$server->getConfig(), $appConfig, \OC::$server->getGroupManager(), \OC::$server->getMemCacheFactory(), \OC::$server->getEventDispatcher(), + \OC::$server->get(IEventDispatcher::class), \OC::$server->get(LoggerInterface::class) )); }