diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index f28e7977a0..a5fa60fc55 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -77,6 +77,7 @@ return array( 'OCP\\AppFramework\\QueryException' => $baseDir . '/lib/public/AppFramework/QueryException.php', 'OCP\\AppFramework\\Services\\IAppConfig' => $baseDir . '/lib/public/AppFramework/Services/IAppConfig.php', 'OCP\\AppFramework\\Services\\IInitialState' => $baseDir . '/lib/public/AppFramework/Services/IInitialState.php', + 'OCP\\AppFramework\\Services\\InitialStateProvider' => $baseDir . '/lib/public/AppFramework/Services/InitialStateProvider.php', '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', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index e584c63c64..4ffb9a7c2a 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -106,6 +106,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OCP\\AppFramework\\QueryException' => __DIR__ . '/../../..' . '/lib/public/AppFramework/QueryException.php', 'OCP\\AppFramework\\Services\\IAppConfig' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Services/IAppConfig.php', 'OCP\\AppFramework\\Services\\IInitialState' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Services/IInitialState.php', + 'OCP\\AppFramework\\Services\\InitialStateProvider' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Services/InitialStateProvider.php', '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', diff --git a/lib/private/AppFramework/Bootstrap/RegistrationContext.php b/lib/private/AppFramework/Bootstrap/RegistrationContext.php index 1ed2dac4c4..0f408380e8 100644 --- a/lib/private/AppFramework/Bootstrap/RegistrationContext.php +++ b/lib/private/AppFramework/Bootstrap/RegistrationContext.php @@ -69,6 +69,9 @@ class RegistrationContext { /** @var array[] */ private $alternativeLogins = []; + /** @var array[] */ + private $initialStates = []; + /** @var ILogger */ private $logger; @@ -164,6 +167,13 @@ class RegistrationContext { $class ); } + + public function registerInitialStateProvider(string $class): void { + $this->context->registerInitialState( + $this->appId, + $class + ); + } }; } @@ -243,6 +253,13 @@ class RegistrationContext { ]; } + public function registerInitialState(string $appId, string $class): void { + $this->initialStates[] = [ + 'appId' => $appId, + 'class' => $class, + ]; + } + /** * @param App[] $apps */ @@ -413,4 +430,11 @@ class RegistrationContext { public function getAlternativeLogins(): array { return $this->alternativeLogins; } + + /** + * @erturn array[] + */ + public function getInitialStates(): array { + return $this->initialStates; + } } diff --git a/lib/private/InitialStateService.php b/lib/private/InitialStateService.php index c74eb683bd..76e64d8b01 100644 --- a/lib/private/InitialStateService.php +++ b/lib/private/InitialStateService.php @@ -28,8 +28,12 @@ declare(strict_types=1); namespace OC; use Closure; +use OC\AppFramework\Bootstrap\Coordinator; +use OCP\AppFramework\QueryException; +use OCP\AppFramework\Services\InitialStateProvider; use OCP\IInitialStateService; use OCP\ILogger; +use OCP\IServerContainer; class InitialStateService implements IInitialStateService { @@ -42,8 +46,16 @@ class InitialStateService implements IInitialStateService { /** @var Closure[][] */ private $lazyStates = []; - public function __construct(ILogger $logger) { + /** @var Coordinator */ + private $bootstrapCoordinator; + + /** @var IServerContainer */ + private $container; + + public function __construct(ILogger $logger, Coordinator $bootstrapCoordinator, IServerContainer $container) { $this->logger = $logger; + $this->bootstrapCoordinator = $bootstrapCoordinator; + $this->container = $container; } public function provideInitialState(string $appName, string $key, $data): void { @@ -88,8 +100,45 @@ class InitialStateService implements IInitialStateService { $this->lazyStates = []; } + /** + * Load the lazy states via the IBootstrap mechanism + */ + private function loadLazyStates(): void { + $context = $this->bootstrapCoordinator->getRegistrationContext(); + + if ($context === null) { + // To early, nothing to do yet + return; + } + + $initialStates = $context->getInitialStates(); + foreach ($initialStates as $initialState) { + try { + $provider = $this->container->query($initialState['class']); + } catch (QueryException $e) { + // Log an continue. We can be fault tolerant here. + $this->logger->logException($e, [ + 'message' => 'Could not load initial state provider dynamically: ' . $e->getMessage(), + 'level' => ILogger::ERROR, + 'app' => $initialState['appId'], + ]); + continue; + } + + if (!($provider instanceof InitialStateProvider)) { + // Log an continue. We can be fault tolerant here. + $this->logger->error('Initial state provider is not an InitialStateProvider instance: ' . $initialState['class'], [ + 'app' => $initialState['appId'], + ]); + } + + $this->provideInitialState($initialState['appId'], $provider->getKey(), $provider); + } + } + public function getInitialStates(): array { $this->invokeLazyStateCallbacks(); + $this->loadLazyStates(); $appStates = []; foreach ($this->states as $app => $states) { diff --git a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php index 9d910d1c69..13181cbe1c 100644 --- a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php +++ b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php @@ -155,4 +155,17 @@ interface IRegistrationContext { * @since 20.0.0 */ public function registerAlternativeLogin(string $class): void; + + /** + * Register an initialstate provider + * + * It is allowed to register more than one provider per app. + * + * @param string $class + * + * @return void + * + * @since 21.0.0 + */ + public function registerInitialStateProvider(string $class): void; } diff --git a/lib/public/AppFramework/Services/InitialStateProvider.php b/lib/public/AppFramework/Services/InitialStateProvider.php new file mode 100644 index 0000000000..53357b4882 --- /dev/null +++ b/lib/public/AppFramework/Services/InitialStateProvider.php @@ -0,0 +1,49 @@ + + * + * @author Roeland Jago Douma + * + * @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\AppFramework\Services; + +/** + * @since 21.0.0 + */ +abstract class InitialStateProvider implements \JsonSerializable { + + /** + * @since 21.0.0 + */ + abstract public function getKey(): string; + + /** + * @since 21.0.0 + */ + abstract public function getData(); + + /** + * @since 21.0.0 + */ + final public function jsonSerialize() { + return $this->getData(); + } +} diff --git a/lib/public/IInitialStateService.php b/lib/public/IInitialStateService.php index 905cf2a9a5..7f3e495ea2 100644 --- a/lib/public/IInitialStateService.php +++ b/lib/public/IInitialStateService.php @@ -43,6 +43,8 @@ interface IInitialStateService { * @param string $appName * @param string $key * @param bool|int|float|string|array|\JsonSerializable $data + * + * @deprecated 21 Use OCP\AppFramework\Services\IInitialState or OCP\AppFramework\Services\InitialStateProvider */ public function provideInitialState(string $appName, string $key, $data): void; @@ -58,6 +60,8 @@ interface IInitialStateService { * @param string $appName * @param string $key * @param Closure $closure returns a primitive or an object that implements JsonSerializable + * + * @deprecated 21 Use OCP\AppFramework\Services\IInitialState or OCP\AppFramework\Services\InitialStateProvider */ public function provideLazyInitialState(string $appName, string $key, Closure $closure): void; } diff --git a/tests/lib/InitialStateServiceTest.php b/tests/lib/InitialStateServiceTest.php index 2afa257d3b..30eca05620 100644 --- a/tests/lib/InitialStateServiceTest.php +++ b/tests/lib/InitialStateServiceTest.php @@ -25,6 +25,8 @@ declare(strict_types=1); namespace Test; +use OC\AppFramework\Bootstrap\Coordinator; +use OCP\IServerContainer; use function json_encode; use JsonSerializable; use OC\InitialStateService; @@ -40,7 +42,9 @@ class InitialStateServiceTest extends TestCase { parent::setUp(); $this->service = new InitialStateService( - $this->createMock(ILogger::class) + $this->createMock(ILogger::class), + $this->createMock(Coordinator::class), + $this->createMock(IServerContainer::class) ); }