diff options
Diffstat (limited to 'lib/public')
98 files changed, 4700 insertions, 100 deletions
diff --git a/lib/public/App/IAppManager.php b/lib/public/App/IAppManager.php index 2497544dcbe..4667cf13f0f 100644 --- a/lib/public/App/IAppManager.php +++ b/lib/public/App/IAppManager.php @@ -248,7 +248,30 @@ interface IAppManager { * * If `user` is not passed, the currently logged in user will be used * + * @param ?IUser $user User to query default app for + * @param bool $withFallbacks Include fallback values if no default app was configured manually + * Before falling back to predefined default apps, + * the user defined app order is considered and the first app would be used as the fallback. + * * @since 25.0.6 + * @since 28.0.0 Added optional $withFallbacks parameter + */ + public function getDefaultAppForUser(?IUser $user = null, bool $withFallbacks = true): string; + + /** + * Get the global default apps with fallbacks + * + * @return string[] The default applications + * @since 28.0.0 + */ + public function getDefaultApps(): array; + + /** + * Set the global default apps with fallbacks + * + * @param string[] $appId + * @throws \InvalidArgumentException If any of the apps is not installed + * @since 28.0.0 */ - public function getDefaultAppForUser(?IUser $user = null): string; + public function setDefaultApps(array $defaultApps): void; } diff --git a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php index 720803a78d1..3153b556a5c 100644 --- a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php +++ b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php @@ -38,6 +38,7 @@ use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\Template\ICustomTemplateProvider; use OCP\IContainer; use OCP\TextProcessing\IProvider as ITextProcessingProvider; +use OCP\TextToImage\IProvider as ITextToImageProvider; use OCP\Notification\INotifier; use OCP\Preview\IProviderV2; use OCP\SpeechToText\ISpeechToTextProvider; @@ -129,7 +130,7 @@ interface IRegistrationContext { * @param string $event preferably the fully-qualified class name of the Event sub class to listen for * @psalm-param string|class-string<T> $event preferably the fully-qualified class name of the Event sub class to listen for * @param string $listener fully qualified class name (or ::class notation) of a \OCP\EventDispatcher\IEventListener that can be built by the DI container - * @psalm-param class-string<\OCP\EventDispatcher\IEventListener> $listener fully qualified class name that can be built by the DI container + * @psalm-param class-string<\OCP\EventDispatcher\IEventListener<T>> $listener fully qualified class name that can be built by the DI container * @param int $priority The higher this value, the earlier an event * listener will be triggered in the chain (defaults to 0) * @@ -231,6 +232,16 @@ interface IRegistrationContext { public function registerTextProcessingProvider(string $providerClass): void; /** + * Register a custom text2image provider class that provides the possibility to generate images + * through the OCP\TextToImage APIs + * + * @param string $providerClass + * @psalm-param class-string<ITextToImageProvider> $providerClass + * @since 28.0.0 + */ + public function registerTextToImageProvider(string $providerClass): void; + + /** * Register a custom template provider class that is able to inject custom templates * in addition to the user defined ones * @@ -371,4 +382,13 @@ interface IRegistrationContext { * @since 26.0.0 */ public function registerPublicShareTemplateProvider(string $class): void; + + /** + * Register an implementation of \OCP\SetupCheck\ISetupCheck that + * will handle the implementation of a setup check + * + * @param class-string<\OCP\SetupCheck\ISetupCheck> $setupCheckClass + * @since 28.0.0 + */ + public function registerSetupCheck(string $setupCheckClass): void; } diff --git a/lib/public/AppFramework/Http/Attribute/IgnoreOpenAPI.php b/lib/public/AppFramework/Http/Attribute/IgnoreOpenAPI.php index 31ccd014321..4802ea5f1fd 100644 --- a/lib/public/AppFramework/Http/Attribute/IgnoreOpenAPI.php +++ b/lib/public/AppFramework/Http/Attribute/IgnoreOpenAPI.php @@ -31,6 +31,7 @@ use Attribute; * Attribute for controller methods that should be ignored when generating OpenAPI documentation * * @since 28.0.0 + * @deprecated 28.0.0 Use {@see OpenAPI} with {@see OpenAPI::SCOPE_IGNORE} instead: `#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]` */ #[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_CLASS)] class IgnoreOpenAPI { diff --git a/lib/public/AppFramework/Http/Attribute/OpenAPI.php b/lib/public/AppFramework/Http/Attribute/OpenAPI.php new file mode 100644 index 00000000000..c5b3bcf5dda --- /dev/null +++ b/lib/public/AppFramework/Http/Attribute/OpenAPI.php @@ -0,0 +1,99 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com> + * + * @author Joas Schilling <coding@schilljs.com> + * + * @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 <http://www.gnu.org/licenses/>. + */ + +namespace OCP\AppFramework\Http\Attribute; + +use Attribute; + +/** + * With this attribute a controller or a method can be moved into a different + * scope or tag. Scopes should be seen as API consumers, tags can be used to group + * different routes inside the same scope. + * + * @since 28.0.0 + */ +#[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)] +class OpenAPI { + /** + * APIs used for normal user facing interaction with your app, + * e.g. when you would implement a mobile client or standalone frontend. + * + * @since 28.0.0 + */ + public const SCOPE_DEFAULT = 'default'; + + /** + * APIs used to administrate your app's configuration on an administrative level. + * Will be set automatically when admin permissions are required to access the route. + * + * @since 28.0.0 + */ + public const SCOPE_ADMINISTRATION = 'administration'; + + /** + * APIs used by servers to federate with each other. + * + * @since 28.0.0 + */ + public const SCOPE_FEDERATION = 'federation'; + + /** + * Ignore this controller or method in all generated OpenAPI specifications. + * + * @since 28.0.0 + */ + public const SCOPE_IGNORE = 'ignore'; + + /** + * @param self::SCOPE_*|string $scope Scopes are used to define different clients. + * It is recommended to go with the scopes available as self::SCOPE_* constants, + * but in exotic cases other APIs might need documentation as well, + * then a free string can be provided (but it should be `a-z` only). + * @param ?list<string> $tags Tags can be used to group routes inside a scope + * for easier implementation and reviewing of the API specification. + * It defaults to the controller name in snake_case (should be `a-z` and underscore only). + * @since 28.0.0 + */ + public function __construct( + protected string $scope = self::SCOPE_DEFAULT, + protected ?array $tags = null, + ) { + } + + /** + * @since 28.0.0 + */ + public function getScope(): string { + return $this->scope; + } + + /** + * @return ?list<string> + * @since 28.0.0 + */ + public function getTags(): ?array { + return $this->tags; + } +} diff --git a/lib/public/AppFramework/Http/ContentSecurityPolicy.php b/lib/public/AppFramework/Http/ContentSecurityPolicy.php index f17dd9bd270..7f93f7004d9 100644 --- a/lib/public/AppFramework/Http/ContentSecurityPolicy.php +++ b/lib/public/AppFramework/Http/ContentSecurityPolicy.php @@ -48,6 +48,8 @@ class ContentSecurityPolicy extends EmptyContentSecurityPolicy { protected ?bool $evalWasmAllowed = false; /** @var bool Whether strict-dynamic should be set */ protected $strictDynamicAllowed = false; + /** @var bool Whether strict-dynamic should be set for 'script-src-elem' */ + protected $strictDynamicAllowedOnScripts = true; /** @var array Domains from which scripts can get loaded */ protected $allowedScriptDomains = [ '\'self\'', diff --git a/lib/public/AppFramework/Http/EmptyContentSecurityPolicy.php b/lib/public/AppFramework/Http/EmptyContentSecurityPolicy.php index 7e1de2ef2eb..aeee4a4ee74 100644 --- a/lib/public/AppFramework/Http/EmptyContentSecurityPolicy.php +++ b/lib/public/AppFramework/Http/EmptyContentSecurityPolicy.php @@ -37,10 +37,12 @@ namespace OCP\AppFramework\Http; * @since 9.0.0 */ class EmptyContentSecurityPolicy { - /** @var string Whether JS nonces should be used */ - protected $useJsNonce = null; + /** @var string JS nonce to be used */ + protected $jsNonce = null; /** @var bool Whether strict-dynamic should be used */ protected $strictDynamicAllowed = null; + /** @var bool Whether strict-dynamic should be used on script-src-elem */ + protected $strictDynamicAllowedOnScripts = null; /** * @var bool Whether eval in JS scripts is allowed * TODO: Disallow per default @@ -94,6 +96,18 @@ class EmptyContentSecurityPolicy { } /** + * In contrast to `useStrictDynamic` this only sets strict-dynamic on script-src-elem + * Meaning only grants trust to all imports of scripts that were loaded in `<script>` tags, and thus weakens less the CSP. + * @param bool $state + * @return EmptyContentSecurityPolicy + * @since 28.0.0 + */ + public function useStrictDynamicOnScripts(bool $state = false): self { + $this->strictDynamicAllowedOnScripts = $state; + return $this; + } + + /** * Use the according JS nonce * This method is only for CSPMiddleware, custom values are ignored in mergePolicies of ContentSecurityPolicyManager * @@ -102,7 +116,7 @@ class EmptyContentSecurityPolicy { * @since 11.0.0 */ public function useJsNonce($nonce) { - $this->useJsNonce = $nonce; + $this->jsNonce = $nonce; return $this; } @@ -448,27 +462,35 @@ class EmptyContentSecurityPolicy { if (!empty($this->allowedScriptDomains) || $this->evalScriptAllowed || $this->evalWasmAllowed) { $policy .= 'script-src '; - if (is_string($this->useJsNonce)) { + $scriptSrc = ''; + if (is_string($this->jsNonce)) { if ($this->strictDynamicAllowed) { - $policy .= '\'strict-dynamic\' '; + $scriptSrc .= '\'strict-dynamic\' '; } - $policy .= '\'nonce-'.base64_encode($this->useJsNonce).'\''; + $scriptSrc .= '\'nonce-'.base64_encode($this->jsNonce).'\''; $allowedScriptDomains = array_flip($this->allowedScriptDomains); unset($allowedScriptDomains['\'self\'']); $this->allowedScriptDomains = array_flip($allowedScriptDomains); if (count($allowedScriptDomains) !== 0) { - $policy .= ' '; + $scriptSrc .= ' '; } } if (is_array($this->allowedScriptDomains)) { - $policy .= implode(' ', $this->allowedScriptDomains); + $scriptSrc .= implode(' ', $this->allowedScriptDomains); } if ($this->evalScriptAllowed) { - $policy .= ' \'unsafe-eval\''; + $scriptSrc .= ' \'unsafe-eval\''; } if ($this->evalWasmAllowed) { - $policy .= ' \'wasm-unsafe-eval\''; + $scriptSrc .= ' \'wasm-unsafe-eval\''; } + $policy .= $scriptSrc . ';'; + } + + // We only need to set this if 'strictDynamicAllowed' is not set because otherwise we can simply fall back to script-src + if ($this->strictDynamicAllowedOnScripts && is_string($this->jsNonce) && !$this->strictDynamicAllowed) { + $policy .= 'script-src-elem \'strict-dynamic\' '; + $policy .= $scriptSrc ?? ''; $policy .= ';'; } diff --git a/lib/public/AppFramework/Http/Response.php b/lib/public/AppFramework/Http/Response.php index dd4f2c53418..d28f45f4c60 100644 --- a/lib/public/AppFramework/Http/Response.php +++ b/lib/public/AppFramework/Http/Response.php @@ -112,9 +112,8 @@ class Response { */ public function cacheFor(int $cacheSeconds, bool $public = false, bool $immutable = false) { if ($cacheSeconds > 0) { - $pragma = $public ? 'public' : 'private'; - $this->addHeader('Cache-Control', sprintf('%s, max-age=%s, %s', $pragma, $cacheSeconds, ($immutable ? 'immutable' : 'must-revalidate'))); - $this->addHeader('Pragma', $pragma); + $cacheStore = $public ? 'public' : 'private'; + $this->addHeader('Cache-Control', sprintf('%s, max-age=%s, %s', $cacheStore, $cacheSeconds, ($immutable ? 'immutable' : 'must-revalidate'))); // Set expires header $expires = new \DateTime(); @@ -125,7 +124,7 @@ class Response { $this->addHeader('Expires', $expires->format(\DateTimeInterface::RFC2822)); } else { $this->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate'); - unset($this->headers['Expires'], $this->headers['Pragma']); + unset($this->headers['Expires']); } return $this; diff --git a/lib/public/AppFramework/Utility/ITimeFactory.php b/lib/public/AppFramework/Utility/ITimeFactory.php index d4f74c9d107..23f67d3dc38 100644 --- a/lib/public/AppFramework/Utility/ITimeFactory.php +++ b/lib/public/AppFramework/Utility/ITimeFactory.php @@ -33,7 +33,7 @@ use Psr\Clock\ClockInterface; * Use this to get a timestamp or DateTime object in code to remain testable * * @since 8.0.0 - * @since 26.0.0 Extends the \Psr\Clock\ClockInterface interface + * @since 27.0.0 Extends the \Psr\Clock\ClockInterface interface * @ref https://www.php-fig.org/psr/psr-20/#21-clockinterface */ diff --git a/lib/public/Authentication/Exceptions/ExpiredTokenException.php b/lib/public/Authentication/Exceptions/ExpiredTokenException.php new file mode 100644 index 00000000000..5c1f4a30541 --- /dev/null +++ b/lib/public/Authentication/Exceptions/ExpiredTokenException.php @@ -0,0 +1,49 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2018 Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ +namespace OCP\Authentication\Exceptions; + +use OCP\Authentication\Token\IToken; + +/** + * @since 28.0.0 + */ +class ExpiredTokenException extends InvalidTokenException { + /** + * @since 28.0.0 + */ + public function __construct( + private IToken $token, + ) { + parent::__construct(); + } + + /** + * @since 28.0.0 + */ + public function getToken(): IToken { + return $this->token; + } +} diff --git a/lib/public/Authentication/Exceptions/InvalidTokenException.php b/lib/public/Authentication/Exceptions/InvalidTokenException.php new file mode 100644 index 00000000000..4869cbd6465 --- /dev/null +++ b/lib/public/Authentication/Exceptions/InvalidTokenException.php @@ -0,0 +1,33 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2016, ownCloud, Inc. + * + * @author Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ +namespace OCP\Authentication\Exceptions; + +use Exception; + +/** + * @since 28.0.0 + */ +class InvalidTokenException extends Exception { +} diff --git a/lib/public/Authentication/Exceptions/WipeTokenException.php b/lib/public/Authentication/Exceptions/WipeTokenException.php new file mode 100644 index 00000000000..81ea2dc57ad --- /dev/null +++ b/lib/public/Authentication/Exceptions/WipeTokenException.php @@ -0,0 +1,49 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ +namespace OCP\Authentication\Exceptions; + +use OCP\Authentication\Token\IToken; + +/** + * @since 28.0.0 + */ +class WipeTokenException extends InvalidTokenException { + /** + * @since 28.0.0 + */ + public function __construct( + private IToken $token, + ) { + parent::__construct(); + } + + /** + * @since 28.0.0 + */ + public function getToken(): IToken { + return $this->token; + } +} diff --git a/lib/public/Authentication/Token/IProvider.php b/lib/public/Authentication/Token/IProvider.php index da2e400eb79..59d2b8f3649 100644 --- a/lib/public/Authentication/Token/IProvider.php +++ b/lib/public/Authentication/Token/IProvider.php @@ -24,6 +24,10 @@ declare(strict_types=1); */ namespace OCP\Authentication\Token; +use OCP\Authentication\Exceptions\ExpiredTokenException; +use OCP\Authentication\Exceptions\InvalidTokenException; +use OCP\Authentication\Exceptions\WipeTokenException; + /** * @since 24.0.8 */ @@ -38,4 +42,15 @@ interface IProvider { * @return void */ public function invalidateTokensOfUser(string $uid, ?string $clientName); + + /** + * Get a token by token string id + * + * @since 28.0.0 + * @throws InvalidTokenException + * @throws ExpiredTokenException + * @throws WipeTokenException + * @return IToken + */ + public function getToken(string $tokenId): IToken; } diff --git a/lib/public/Authentication/Token/IToken.php b/lib/public/Authentication/Token/IToken.php new file mode 100644 index 00000000000..7b6ce8327c6 --- /dev/null +++ b/lib/public/Authentication/Token/IToken.php @@ -0,0 +1,139 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2016, ownCloud, Inc. + * + * @author Christoph Wurst <christoph@winzerhof-wurst.at> + * @author Robin Appelman <robin@icewind.nl> + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ +namespace OCP\Authentication\Token; + +use JsonSerializable; + +/** + * @since 28.0.0 + */ +interface IToken extends JsonSerializable { + /** + * @since 28.0.0 + */ + public const TEMPORARY_TOKEN = 0; + /** + * @since 28.0.0 + */ + public const PERMANENT_TOKEN = 1; + /** + * @since 28.0.0 + */ + public const WIPE_TOKEN = 2; + /** + * @since 28.0.0 + */ + public const DO_NOT_REMEMBER = 0; + /** + * @since 28.0.0 + */ + public const REMEMBER = 1; + + /** + * Get the token ID + * @since 28.0.0 + */ + public function getId(): int; + + /** + * Get the user UID + * @since 28.0.0 + */ + public function getUID(): string; + + /** + * Get the login name used when generating the token + * @since 28.0.0 + */ + public function getLoginName(): string; + + /** + * Get the (encrypted) login password + * @since 28.0.0 + */ + public function getPassword(): ?string; + + /** + * Get the timestamp of the last password check + * @since 28.0.0 + */ + public function getLastCheck(): int; + + /** + * Set the timestamp of the last password check + * @since 28.0.0 + */ + public function setLastCheck(int $time): void; + + /** + * Get the authentication scope for this token + * @since 28.0.0 + */ + public function getScope(): string; + + /** + * Get the authentication scope for this token + * @since 28.0.0 + */ + public function getScopeAsArray(): array; + + /** + * Set the authentication scope for this token + * @since 28.0.0 + */ + public function setScope(array $scope): void; + + /** + * Get the name of the token + * @since 28.0.0 + */ + public function getName(): string; + + /** + * Get the remember state of the token + * @since 28.0.0 + */ + public function getRemember(): int; + + /** + * Set the token + * @since 28.0.0 + */ + public function setToken(string $token): void; + + /** + * Set the password + * @since 28.0.0 + */ + public function setPassword(string $password): void; + + /** + * Set the expiration time of the token + * @since 28.0.0 + */ + public function setExpires(?int $expires): void; +} diff --git a/lib/public/BackgroundJob/IJobList.php b/lib/public/BackgroundJob/IJobList.php index 65e2f5b6250..0b00326ca1a 100644 --- a/lib/public/BackgroundJob/IJobList.php +++ b/lib/public/BackgroundJob/IJobList.php @@ -58,6 +58,19 @@ interface IJobList { public function add($job, $argument = null): void; /** + * Add a job to the list but only run it after the given timestamp + * + * For cron background jobs this means the job will likely run shortly after the timestamp + * has been reached. For ajax background jobs the job might only run when users are active + * on the instance again. + * + * @param class-string<IJob> $job + * @param mixed $argument The serializable argument to be passed to $job->run() when the job is executed + * @since 28.0.0 + */ + public function scheduleAfter(string $job, int $runAfter, $argument = null): void; + + /** * Remove a job from the list * * @param IJob|class-string<IJob> $job @@ -147,7 +160,8 @@ interface IJobList { public function resetBackgroundJob(IJob $job): void; /** - * Checks whether a job of the passed class is reserved to run + * Checks whether a job of the passed class was reserved to run + * in the last 6h * * @param string|null $className * @return bool diff --git a/lib/public/Collaboration/AutoComplete/AutoCompleteEvent.php b/lib/public/Collaboration/AutoComplete/AutoCompleteEvent.php index 5206dc37115..23879b93fa1 100644 --- a/lib/public/Collaboration/AutoComplete/AutoCompleteEvent.php +++ b/lib/public/Collaboration/AutoComplete/AutoCompleteEvent.php @@ -29,6 +29,7 @@ use OCP\EventDispatcher\GenericEvent; /** * @since 16.0.0 + * @deprecated Use {@see AutoCompleteFilterEvent} instead */ class AutoCompleteEvent extends GenericEvent { /** diff --git a/lib/public/Collaboration/AutoComplete/AutoCompleteFilterEvent.php b/lib/public/Collaboration/AutoComplete/AutoCompleteFilterEvent.php new file mode 100644 index 00000000000..fd1bec42abf --- /dev/null +++ b/lib/public/Collaboration/AutoComplete/AutoCompleteFilterEvent.php @@ -0,0 +1,107 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com> + * + * @author Joas Schilling <coding@schilljs.com> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ +namespace OCP\Collaboration\AutoComplete; + +use OCP\EventDispatcher\Event; + +/** + * @since 28.0.0 + */ +class AutoCompleteFilterEvent extends Event { + /** + * @since 28.0.0 + */ + public function __construct( + protected array $results, + protected string $search, + protected ?string $itemType, + protected ?string $itemId, + protected ?string $sorter, + protected array $shareTypes, + protected int $limit, + ) { + parent::__construct(); + } + + /** + * @since 28.0.0 + */ + public function getResults(): array { + return $this->results; + } + + /** + * @param array $results + * @since 28.0.0 + */ + public function setResults(array $results): void { + $this->results = $results; + } + + /** + * @since 28.0.0 + */ + public function getSearchTerm(): string { + return $this->search; + } + + /** + * @return int[] List of `\OCP\Share\IShare::TYPE_*` constants + * @since 28.0.0 + */ + public function getShareTypes(): array { + return $this->shareTypes; + } + + /** + * @since 28.0.0 + */ + public function getItemType(): ?string { + return $this->itemType; + } + + /** + * @since 28.0.0 + */ + public function getItemId(): ?string { + return $this->itemId; + } + + /** + * @return ?string List of desired sort identifiers, top priority first. When multiple are given they are joined with a pipe: `commenters|share-recipients` + * @since 28.0.0 + */ + public function getSorter(): ?string { + return $this->sorter; + } + + /** + * @since 28.0.0 + */ + public function getLimit(): int { + return $this->limit; + } +} diff --git a/lib/public/Collaboration/Reference/IReference.php b/lib/public/Collaboration/Reference/IReference.php index 1b9157fd9b1..c0cfa72c36d 100644 --- a/lib/public/Collaboration/Reference/IReference.php +++ b/lib/public/Collaboration/Reference/IReference.php @@ -126,4 +126,11 @@ interface IReference extends JsonSerializable { * @since 25.0.0 */ public function getOpenGraphObject(): array; + + /** + * @return array{richObjectType: string, richObject: array<string, mixed>, openGraphObject: array{id: string, name: string, description: ?string, thumb: ?string, link: string}, accessible: bool} + * + * @since 25.0.0 + */ + public function jsonSerialize(): array; } diff --git a/lib/public/Collaboration/Reference/Reference.php b/lib/public/Collaboration/Reference/Reference.php index 0dcb665713c..8dc88edbeac 100644 --- a/lib/public/Collaboration/Reference/Reference.php +++ b/lib/public/Collaboration/Reference/Reference.php @@ -242,7 +242,7 @@ class Reference implements IReference { * @since 25.0.0 * @return array{richObjectType: string, richObject: array<string, mixed>, openGraphObject: OpenGraphObject, accessible: bool} */ - public function jsonSerialize() { + public function jsonSerialize(): array { return [ 'richObjectType' => $this->getRichObjectType(), 'richObject' => $this->getRichObject(), diff --git a/lib/public/Contacts/ContactsMenu/IBulkProvider.php b/lib/public/Contacts/ContactsMenu/IBulkProvider.php new file mode 100644 index 00000000000..43d727e2ec3 --- /dev/null +++ b/lib/public/Contacts/ContactsMenu/IBulkProvider.php @@ -0,0 +1,40 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright 2023 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ +namespace OCP\Contacts\ContactsMenu; + +/** + * Process contacts menu entries in bulk + * + * @since 28.0 + */ +interface IBulkProvider { + /** + * @since 28.0 + * @param list<IEntry> $entries + * @return void + */ + public function process(array $entries): void; +} diff --git a/lib/public/Contacts/ContactsMenu/IEntry.php b/lib/public/Contacts/ContactsMenu/IEntry.php index 9d78b0c8f57..1307e2c74f7 100644 --- a/lib/public/Contacts/ContactsMenu/IEntry.php +++ b/lib/public/Contacts/ContactsMenu/IEntry.php @@ -54,6 +54,20 @@ interface IEntry extends JsonSerializable { public function addAction(IAction $action): void; /** + * Set the (system) contact's user status + * + * @since 28.0 + * @param string $status + * @param string $statusMessage + * @param string|null $icon + * @return void + */ + public function setStatus(string $status, + string $statusMessage = null, + int $statusMessageTimestamp = null, + string $icon = null): void; + + /** * Get an arbitrary property from the contact * * @since 12.0 diff --git a/lib/public/Contacts/ContactsMenu/IProvider.php b/lib/public/Contacts/ContactsMenu/IProvider.php index c05f2707c18..200ee0b1fea 100644 --- a/lib/public/Contacts/ContactsMenu/IProvider.php +++ b/lib/public/Contacts/ContactsMenu/IProvider.php @@ -1,4 +1,7 @@ <?php + +declare(strict_types=1); + /** * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at> * @@ -23,6 +26,10 @@ namespace OCP\Contacts\ContactsMenu; /** + * Process contacts menu entries + * + * @see IBulkProvider for providers that work with the full dataset at once + * * @since 12.0 */ interface IProvider { diff --git a/lib/public/Contacts/IManager.php b/lib/public/Contacts/IManager.php index 6ca349b95d0..97fa2e61529 100644 --- a/lib/public/Contacts/IManager.php +++ b/lib/public/Contacts/IManager.php @@ -107,22 +107,22 @@ interface IManager { * This function can be used to delete the contact identified by the given id * * @param int $id the unique identifier to a contact - * @param string $address_book_key identifier of the address book in which the contact shall be deleted + * @param string $addressBookKey identifier of the address book in which the contact shall be deleted * @return bool successful or not * @since 6.0.0 */ - public function delete($id, $address_book_key); + public function delete($id, $addressBookKey); /** * This function is used to create a new contact if 'id' is not given or not present. * Otherwise the contact will be updated by replacing the entire data set. * * @param array $properties this array if key-value-pairs defines a contact - * @param string $address_book_key identifier of the address book in which the contact shall be created or updated - * @return array an array representing the contact just created or updated + * @param string $addressBookKey identifier of the address book in which the contact shall be created or updated + * @return ?array an array representing the contact just created or updated * @since 6.0.0 */ - public function createOrUpdate($properties, $address_book_key); + public function createOrUpdate($properties, $addressBookKey); /** * Check if contacts are available (e.g. contacts app enabled) @@ -135,20 +135,19 @@ interface IManager { /** * Registers an address book * - * @param \OCP\IAddressBook $address_book * @return void * @since 6.0.0 */ - public function registerAddressBook(\OCP\IAddressBook $address_book); + public function registerAddressBook(\OCP\IAddressBook $addressBook); /** * Unregisters an address book * - * @param \OCP\IAddressBook $address_book + * @param \OCP\IAddressBook $addressBook * @return void * @since 6.0.0 */ - public function unregisterAddressBook(\OCP\IAddressBook $address_book); + public function unregisterAddressBook(\OCP\IAddressBook $addressBook); /** * In order to improve lazy loading a closure can be registered which will be called in case diff --git a/lib/public/DB/QueryBuilder/IQueryBuilder.php b/lib/public/DB/QueryBuilder/IQueryBuilder.php index 63fdfb65971..dba5f390d6e 100644 --- a/lib/public/DB/QueryBuilder/IQueryBuilder.php +++ b/lib/public/DB/QueryBuilder/IQueryBuilder.php @@ -27,6 +27,7 @@ */ namespace OCP\DB\QueryBuilder; +use Doctrine\DBAL\ArrayParameterType; use Doctrine\DBAL\Connection; use Doctrine\DBAL\ParameterType; use OCP\DB\Exception; @@ -72,11 +73,11 @@ interface IQueryBuilder { /** * @since 9.0.0 */ - public const PARAM_INT_ARRAY = Connection::PARAM_INT_ARRAY; + public const PARAM_INT_ARRAY = ArrayParameterType::INTEGER; /** * @since 9.0.0 */ - public const PARAM_STR_ARRAY = Connection::PARAM_STR_ARRAY; + public const PARAM_STR_ARRAY = ArrayParameterType::STRING; /** * @since 24.0.0 Indicates how many rows can be deleted at once with MySQL @@ -907,7 +908,7 @@ interface IQueryBuilder { * @link http://www.zetacomponents.org * * @param mixed $value - * @param mixed $type + * @param self::PARAM_* $type * @param string $placeHolder The name to bind with. The string must start with a colon ':'. * * @return IParameter @@ -935,7 +936,7 @@ interface IQueryBuilder { * </code> * * @param mixed $value - * @param integer $type + * @param self::PARAM_* $type * * @return IParameter * @since 8.2.0 diff --git a/lib/public/Dashboard/IManager.php b/lib/public/Dashboard/IManager.php index 77bff7b34ff..135fd4b4514 100644 --- a/lib/public/Dashboard/IManager.php +++ b/lib/public/Dashboard/IManager.php @@ -40,7 +40,7 @@ interface IManager { /** * @since 20.0.0 * - * @return IWidget[] + * @return array<string, IWidget> */ public function getWidgets(): array; } diff --git a/lib/public/Federation/ICloudFederationNotification.php b/lib/public/Federation/ICloudFederationNotification.php index 2b51b2bc8db..26a98027d5e 100644 --- a/lib/public/Federation/ICloudFederationNotification.php +++ b/lib/public/Federation/ICloudFederationNotification.php @@ -34,8 +34,8 @@ interface ICloudFederationNotification { * * @param string $notificationType (e.g. SHARE_ACCEPTED) * @param string $resourceType (e.g. file, calendar, contact,...) - * @param $providerId id of the share - * @param array $notification , payload of the notification + * @param string $providerId id of the share + * @param array $notification payload of the notification * * @since 14.0.0 */ diff --git a/lib/public/Files/Config/ICachedMountInfo.php b/lib/public/Files/Config/ICachedMountInfo.php index dafd2423fdc..78a92874275 100644 --- a/lib/public/Files/Config/ICachedMountInfo.php +++ b/lib/public/Files/Config/ICachedMountInfo.php @@ -84,4 +84,12 @@ interface ICachedMountInfo { * @since 24.0.0 */ public function getMountProvider(): string; + + /** + * Get a key that uniquely identifies the mount + * + * @return string + * @since 28.0.0 + */ + public function getKey(): string; } diff --git a/lib/public/Files/ConnectionLostException.php b/lib/public/Files/ConnectionLostException.php new file mode 100644 index 00000000000..8e5deb99b46 --- /dev/null +++ b/lib/public/Files/ConnectionLostException.php @@ -0,0 +1,33 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2016, ownCloud, Inc. + * + * @author Côme Chilliet <come.chilliet@nextcloud.com> + * + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCP\Files; + +/** + * Exception for lost connection with the + * @since 25.0.11 + */ +class ConnectionLostException extends \RuntimeException { +} diff --git a/lib/public/Files/FileInfo.php b/lib/public/Files/FileInfo.php index 83ae4adef92..817b03dfc65 100644 --- a/lib/public/Files/FileInfo.php +++ b/lib/public/Files/FileInfo.php @@ -6,6 +6,7 @@ * @author Felix Heidecke <felix@heidecke.me> * @author Joas Schilling <coding@schilljs.com> * @author Julius Härtl <jus@bitgrid.net> + * @author Maxence Lange <maxence@artificial-owl.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> @@ -299,4 +300,21 @@ interface FileInfo { * @since 18.0.0 */ public function getUploadTime(): int; + + /** + * Get the fileid or the parent folder + * or -1 if this item has no parent folder (because it is the root) + * + * @return int + * @since 28.0.0 + */ + public function getParentId(): int; + + /** + * Get the metadata, if available + * + * @return array<string, int|string|bool|float|string[]|int[]> + * @since 28.0.0 + */ + public function getMetadata(): array; } diff --git a/lib/public/Files/IHomeStorage.php b/lib/public/Files/IHomeStorage.php index 7eb3ffc4a24..1fea80f2d87 100644 --- a/lib/public/Files/IHomeStorage.php +++ b/lib/public/Files/IHomeStorage.php @@ -27,6 +27,7 @@ namespace OCP\Files; use OCP\Files\Storage\IStorage; +use OCP\IUser; /** * Interface IHomeStorage @@ -34,4 +35,11 @@ use OCP\Files\Storage\IStorage; * @since 7.0.0 */ interface IHomeStorage extends IStorage { + /** + * Get the user for this home storage + * + * @return IUser + * @since 28.0.0 + */ + public function getUser(): IUser; } diff --git a/lib/public/Files/IRootFolder.php b/lib/public/Files/IRootFolder.php index 1fee0b3595e..44f0ba5f2e1 100644 --- a/lib/public/Files/IRootFolder.php +++ b/lib/public/Files/IRootFolder.php @@ -26,7 +26,9 @@ namespace OCP\Files; use OC\Hooks\Emitter; use OC\User\NoUserException; +use OCP\Files\Cache\ICacheEntry; use OCP\Files\Mount\IMountPoint; +use OCP\Files\Node as INode; /** * Interface IRootFolder @@ -65,6 +67,16 @@ interface IRootFolder extends Folder, Emitter { public function getMountsIn(string $mountPoint): array; /** + * Create a `Node` for a file or folder from the cache entry and mountpoint + * + * @param ICacheEntry $cacheEntry + * @param IMountPoint $mountPoint + * @return Node + * @since 28.0.0 + */ + public function getNodeFromCacheEntryAndMount(ICacheEntry $cacheEntry, IMountPoint $mountPoint): INode; + + /** * @since 28.0.0 */ public function getMount(string $mountPoint): IMountPoint; diff --git a/lib/public/Files/Mount/IMountManager.php b/lib/public/Files/Mount/IMountManager.php index a55e5758199..df2cc4c6209 100644 --- a/lib/public/Files/Mount/IMountManager.php +++ b/lib/public/Files/Mount/IMountManager.php @@ -26,6 +26,8 @@ declare(strict_types=1); */ namespace OCP\Files\Mount; +use OCP\Files\Config\ICachedMountInfo; + /** * Interface IMountManager * @@ -106,4 +108,14 @@ interface IMountManager { * @since 8.2.0 */ public function findByNumericId(int $id): array; + + /** + * Return the mount matching a cached mount info (or mount file info) + * + * @param ICachedMountInfo $info + * + * @return IMountPoint|null + * @since 28.0.0 + */ + public function getMountFromMountInfo(ICachedMountInfo $info): ?IMountPoint; } diff --git a/lib/public/Files/Search/ISearchComparison.php b/lib/public/Files/Search/ISearchComparison.php index 8ebaeced304..d7313fbaf2a 100644 --- a/lib/public/Files/Search/ISearchComparison.php +++ b/lib/public/Files/Search/ISearchComparison.php @@ -3,6 +3,7 @@ * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> * * @author Christoph Wurst <christoph@winzerhof-wurst.at> + * @author Maxence Lange <maxence@artificial-owl.com> * @author Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version @@ -43,7 +44,7 @@ interface ISearchComparison extends ISearchOperator { * @return string * @since 12.0.0 */ - public function getType(); + public function getType(): string; /** * Get the name of the field to compare with @@ -53,13 +54,21 @@ interface ISearchComparison extends ISearchOperator { * @return string * @since 12.0.0 */ - public function getField(); + public function getField(): string; + + /** + * extra means data are not related to the main files table + * + * @return string + * @since 28.0.0 + */ + public function getExtra(): string; /** * Get the value to compare the field with * - * @return string|integer|\DateTime + * @return string|integer|bool|\DateTime * @since 12.0.0 */ - public function getValue(); + public function getValue(): string|int|bool|\DateTime; } diff --git a/lib/public/Files/Search/ISearchOrder.php b/lib/public/Files/Search/ISearchOrder.php index 3b9e6e6713a..5b73e7b102c 100644 --- a/lib/public/Files/Search/ISearchOrder.php +++ b/lib/public/Files/Search/ISearchOrder.php @@ -3,6 +3,7 @@ * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> * * @author Christoph Wurst <christoph@winzerhof-wurst.at> + * @author Maxence Lange <maxence@artificial-owl.com> * @author Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version @@ -38,7 +39,7 @@ interface ISearchOrder { * @return string * @since 12.0.0 */ - public function getDirection(); + public function getDirection(): string; /** * The field to sort on @@ -46,7 +47,15 @@ interface ISearchOrder { * @return string * @since 12.0.0 */ - public function getField(); + public function getField(): string; + + /** + * extra means data are not related to the main files table + * + * @return string + * @since 28.0.0 + */ + public function getExtra(): string; /** * Apply the sorting on 2 FileInfo objects diff --git a/lib/public/Files/Template/TemplateFileCreator.php b/lib/public/Files/Template/TemplateFileCreator.php index 43e96b6f21b..0c2f7a115a2 100644 --- a/lib/public/Files/Template/TemplateFileCreator.php +++ b/lib/public/Files/Template/TemplateFileCreator.php @@ -42,6 +42,7 @@ final class TemplateFileCreator implements \JsonSerializable { protected $order = 100; /** * @since 27.0.0 + * @deprecated 28.0.0 */ protected string $actionLabel = ''; diff --git a/lib/public/FilesMetadata/AMetadataEvent.php b/lib/public/FilesMetadata/AMetadataEvent.php new file mode 100644 index 00000000000..8cb8ea8d1b8 --- /dev/null +++ b/lib/public/FilesMetadata/AMetadataEvent.php @@ -0,0 +1,68 @@ +<?php + +declare(strict_types=1); +/** + * @copyright 2023 Maxence Lange <maxence@artificial-owl.com> + * + * @author Maxence Lange <maxence@artificial-owl.com> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\FilesMetadata; + +use OCP\EventDispatcher\Event; +use OCP\Files\Node; +use OCP\FilesMetadata\Model\IFilesMetadata; + +/** + * @since 28.0.0 + */ +abstract class AMetadataEvent extends Event { + /** + * @param Node $node + * @param IFilesMetadata $metadata + * @since 28.0.0 + */ + public function __construct( + protected Node $node, + protected IFilesMetadata $metadata + ) { + parent::__construct(); + } + + /** + * returns related node + * + * @return Node + * @since 28.0.0 + */ + public function getNode(): Node { + return $this->node; + } + + /** + * returns metadata. if known, it already contains data from the database. + * If the object is modified using its setters, changes are stored in database at the end of the event. + * + * @return IFilesMetadata + * @since 28.0.0 + */ + public function getMetadata(): IFilesMetadata { + return $this->metadata; + } +} diff --git a/lib/public/FilesMetadata/Event/MetadataBackgroundEvent.php b/lib/public/FilesMetadata/Event/MetadataBackgroundEvent.php new file mode 100644 index 00000000000..3d175994c32 --- /dev/null +++ b/lib/public/FilesMetadata/Event/MetadataBackgroundEvent.php @@ -0,0 +1,40 @@ +<?php + +declare(strict_types=1); +/** + * @copyright 2023 Maxence Lange <maxence@artificial-owl.com> + * + * @author Maxence Lange <maxence@artificial-owl.com> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\FilesMetadata\Event; + +use OCP\FilesMetadata\AMetadataEvent; + +/** + * MetadataBackgroundEvent is an event similar to MetadataLiveEvent but dispatched + * on a background thread instead of live thread. Meaning there is no limit to + * the time required for the generation of your metadata. + * + * @see AMetadataEvent::getMetadata() + * @see AMetadataEvent::getNode() + * @since 28.0.0 + */ +class MetadataBackgroundEvent extends AMetadataEvent { +} diff --git a/lib/public/FilesMetadata/Event/MetadataLiveEvent.php b/lib/public/FilesMetadata/Event/MetadataLiveEvent.php new file mode 100644 index 00000000000..a89692fde92 --- /dev/null +++ b/lib/public/FilesMetadata/Event/MetadataLiveEvent.php @@ -0,0 +1,67 @@ +<?php + +declare(strict_types=1); +/** + * @copyright 2023 Maxence Lange <maxence@artificial-owl.com> + * + * @author Maxence Lange <maxence@artificial-owl.com> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\FilesMetadata\Event; + +use OCP\FilesMetadata\AMetadataEvent; + +/** + * MetadataLiveEvent is an event initiated when a file is created or updated. + * The app contains the Node related to the created/updated file, and a FilesMetadata that already + * contains the currently known metadata. + * + * Setting new metadata, or modifying already existing metadata with different value, will trigger + * the save of the metadata in the database. + * + * @see AMetadataEvent::getMetadata() + * @see AMetadataEvent::getNode() + * @see MetadataLiveEvent::requestBackgroundJob() + * @since 28.0.0 + */ +class MetadataLiveEvent extends AMetadataEvent { + private bool $runAsBackgroundJob = false; + + /** + * For heavy process, call this method if your app prefers to update metadata on a + * background/cron job, instead of the live process. + * A similar MetadataBackgroundEvent will be broadcast on next cron tick. + * + * @return void + * @since 28.0.0 + */ + public function requestBackgroundJob(): void { + $this->runAsBackgroundJob = true; + } + + /** + * return true if any app that catch this event requested a re-run as background job + * + * @return bool + * @since 28.0.0 + */ + public function isRunAsBackgroundJobRequested(): bool { + return $this->runAsBackgroundJob; + } +} diff --git a/lib/public/FilesMetadata/Event/MetadataNamedEvent.php b/lib/public/FilesMetadata/Event/MetadataNamedEvent.php new file mode 100644 index 00000000000..f8cfcf9bd01 --- /dev/null +++ b/lib/public/FilesMetadata/Event/MetadataNamedEvent.php @@ -0,0 +1,74 @@ +<?php + +declare(strict_types=1); +/** + * @copyright 2023 Maxence Lange <maxence@artificial-owl.com> + * + * @author Maxence Lange <maxence@artificial-owl.com> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\FilesMetadata\Event; + +use OCP\Files\Node; +use OCP\FilesMetadata\AMetadataEvent; +use OCP\FilesMetadata\Model\IFilesMetadata; + +/** + * MetadataNamedEvent is an event similar to MetadataBackgroundEvent completed with a target name, + * used to limit the refresh of metadata only listeners capable of filtering themselves out. + * + * Meaning that when using this event, your app must implement a filter on the event's registered + * name returned by getName() + * + * This event is mostly triggered when a registered name is added to the files scan + * i.e. ./occ files:scan --generate-metadata [name] + * + * @see AMetadataEvent::getMetadata() + * @see AMetadataEvent::getNode() + * @see MetadataNamedEvent::getName() + * @since 28.0.0 + */ +class MetadataNamedEvent extends AMetadataEvent { + /** + * @param Node $node + * @param IFilesMetadata $metadata + * @param string $name name assigned to the event + * + * @since 28.0.0 + */ + public function __construct( + Node $node, + IFilesMetadata $metadata, + private string $name = '' + ) { + parent::__construct($node, $metadata); + } + + /** + * get the assigned name for the event. + * This is used to know if your app is the called one when running the + * ./occ files:scan --generate-metadata [name] + * + * @return string + * @since 28.0.0 + */ + public function getName(): string { + return $this->name; + } +} diff --git a/lib/public/FilesMetadata/Exceptions/FilesMetadataException.php b/lib/public/FilesMetadata/Exceptions/FilesMetadataException.php new file mode 100644 index 00000000000..e3f75a7a7af --- /dev/null +++ b/lib/public/FilesMetadata/Exceptions/FilesMetadataException.php @@ -0,0 +1,34 @@ +<?php + +declare(strict_types=1); +/** + * @copyright 2023 Maxence Lange <maxence@artificial-owl.com> + * + * @author Maxence Lange <maxence@artificial-owl.com> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\FilesMetadata\Exceptions; + +use Exception; + +/** + * @since 28.0.0 + */ +class FilesMetadataException extends Exception { +} diff --git a/lib/public/FilesMetadata/Exceptions/FilesMetadataKeyFormatException.php b/lib/public/FilesMetadata/Exceptions/FilesMetadataKeyFormatException.php new file mode 100644 index 00000000000..0083e985a5e --- /dev/null +++ b/lib/public/FilesMetadata/Exceptions/FilesMetadataKeyFormatException.php @@ -0,0 +1,32 @@ +<?php + +declare(strict_types=1); +/** + * @copyright 2023 Maxence Lange <maxence@artificial-owl.com> + * + * @author Maxence Lange <maxence@artificial-owl.com> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\FilesMetadata\Exceptions; + +/** + * @since 28.0.0 + */ +class FilesMetadataKeyFormatException extends FilesMetadataException { +} diff --git a/lib/public/FilesMetadata/Exceptions/FilesMetadataNotFoundException.php b/lib/public/FilesMetadata/Exceptions/FilesMetadataNotFoundException.php new file mode 100644 index 00000000000..9c03c5ba370 --- /dev/null +++ b/lib/public/FilesMetadata/Exceptions/FilesMetadataNotFoundException.php @@ -0,0 +1,32 @@ +<?php + +declare(strict_types=1); +/** + * @copyright 2023 Maxence Lange <maxence@artificial-owl.com> + * + * @author Maxence Lange <maxence@artificial-owl.com> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\FilesMetadata\Exceptions; + +/** + * @since 28.0.0 + */ +class FilesMetadataNotFoundException extends FilesMetadataException { +} diff --git a/lib/public/FilesMetadata/Exceptions/FilesMetadataTypeException.php b/lib/public/FilesMetadata/Exceptions/FilesMetadataTypeException.php new file mode 100644 index 00000000000..1d134c67ecf --- /dev/null +++ b/lib/public/FilesMetadata/Exceptions/FilesMetadataTypeException.php @@ -0,0 +1,32 @@ +<?php + +declare(strict_types=1); +/** + * @copyright 2023 Maxence Lange <maxence@artificial-owl.com> + * + * @author Maxence Lange <maxence@artificial-owl.com> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\FilesMetadata\Exceptions; + +/** + * @since 28.0.0 + */ +class FilesMetadataTypeException extends FilesMetadataException { +} diff --git a/lib/public/FilesMetadata/IFilesMetadataManager.php b/lib/public/FilesMetadata/IFilesMetadataManager.php new file mode 100644 index 00000000000..de6fc62ba94 --- /dev/null +++ b/lib/public/FilesMetadata/IFilesMetadataManager.php @@ -0,0 +1,158 @@ +<?php + +declare(strict_types=1); +/** + * @copyright 2023 Maxence Lange <maxence@artificial-owl.com> + * + * @author Maxence Lange <maxence@artificial-owl.com> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\FilesMetadata; + +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\Files\Node; +use OCP\FilesMetadata\Exceptions\FilesMetadataException; +use OCP\FilesMetadata\Exceptions\FilesMetadataNotFoundException; +use OCP\FilesMetadata\Model\IFilesMetadata; +use OCP\FilesMetadata\Model\IMetadataValueWrapper; + +/** + * Manager for FilesMetadata; manage files' metadata. + * + * @since 28.0.0 + */ +interface IFilesMetadataManager { + /** @since 28.0.0 */ + public const PROCESS_LIVE = 1; + /** @since 28.0.0 */ + public const PROCESS_BACKGROUND = 2; + /** @since 28.0.0 */ + public const PROCESS_NAMED = 4; + + /** + * initiate the process of refreshing the metadata in relation to a node + * usually, this process: + * - get current metadata from database, if available, or create a new one + * - dispatch a MetadataLiveEvent, + * - save new metadata in database, if metadata have been changed during the event + * - refresh metadata indexes if needed, + * - prep a new cronjob if an app request it during the event, + * + * @param Node $node related node + * @param int $process type of process + * @param string $namedEvent limit process to a named event + * + * @return IFilesMetadata + * @see self::PROCESS_BACKGROUND + * @see self::PROCESS_LIVE + * @see self::PROCESS_NAMED + * @since 28.0.0 + */ + public function refreshMetadata( + Node $node, + int $process = self::PROCESS_LIVE, + string $namedEvent = '' + ): IFilesMetadata; + + /** + * returns metadata from a file id + * + * @param int $fileId file id + * @param boolean $generate Generate if metadata does not exist + * + * @return IFilesMetadata + * @throws FilesMetadataNotFoundException if not found + * @since 28.0.0 + */ + public function getMetadata(int $fileId, bool $generate = false): IFilesMetadata; + + /** + * save metadata to database and refresh indexes. + * metadata are saved if new data are available. + * on update, a check on syncToken is done to avoid conflict (race condition) + * + * @param IFilesMetadata $filesMetadata + * + * @throws FilesMetadataException if metadata seems malformed + * @since 28.0.0 + */ + public function saveMetadata(IFilesMetadata $filesMetadata): void; + + /** + * delete metadata and its indexes + * + * @param int $fileId file id + * + * @return void + * @since 28.0.0 + */ + public function deleteMetadata(int $fileId): void; + + /** + * generate and return a MetadataQuery to help building sql queries + * + * @param IQueryBuilder $qb + * @param string $fileTableAlias alias of the table that contains data about files + * @param string $fileIdField alias of the field that contains file ids + * + * @return IMetadataQuery|null NULL if table are not set yet or never used + * @see IMetadataQuery + * @since 28.0.0 + */ + public function getMetadataQuery( + IQueryBuilder $qb, + string $fileTableAlias, + string $fileIdField + ): ?IMetadataQuery; + + /** + * returns all type of metadata currently available. + * The list is stored in a IFilesMetadata with null values but correct type. + * + * @return IFilesMetadata + * @since 28.0.0 + */ + public function getKnownMetadata(): IFilesMetadata; + + /** + * initiate a metadata key with its type. + * The call is mandatory before using the metadata property in a webdav request. + * It is not needed to only use this method when the app is enabled: the method can be + * called each time during the app loading as the metadata will only be initiated if not known + * + * @param string $key metadata key + * @param string $type metadata type + * @param bool $indexed TRUE if metadata can be search + * @param int $editPermission remote edit permission via Webdav PROPPATCH + * + * @see IMetadataValueWrapper::TYPE_INT + * @see IMetadataValueWrapper::TYPE_FLOAT + * @see IMetadataValueWrapper::TYPE_BOOL + * @see IMetadataValueWrapper::TYPE_ARRAY + * @see IMetadataValueWrapper::TYPE_STRING_LIST + * @see IMetadataValueWrapper::TYPE_INT_LIST + * @see IMetadataValueWrapper::TYPE_STRING + * @see IMetadataValueWrapper::EDIT_FORBIDDEN + * @see IMetadataValueWrapper::EDIT_REQ_OWNERSHIP + * @see IMetadataValueWrapper::EDIT_REQ_WRITE_PERMISSION + * @see IMetadataValueWrapper::EDIT_REQ_READ_PERMISSION + * @since 28.0.0 + */ + public function initMetadata(string $key, string $type, bool $indexed, int $editPermission): void; +} diff --git a/lib/public/FilesMetadata/IMetadataQuery.php b/lib/public/FilesMetadata/IMetadataQuery.php new file mode 100644 index 00000000000..c1c649ac243 --- /dev/null +++ b/lib/public/FilesMetadata/IMetadataQuery.php @@ -0,0 +1,92 @@ +<?php + +declare(strict_types=1); +/** + * @copyright 2023 Maxence Lange <maxence@artificial-owl.com> + * + * @author Maxence Lange <maxence@artificial-owl.com> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\FilesMetadata; + +use OCP\FilesMetadata\Model\IFilesMetadata; + +/** + * Model that help building queries with metadata and metadata indexes + * + * @since 28.0.0 + */ +interface IMetadataQuery { + /** @since 28.0.0 */ + public const EXTRA = 'metadata'; + + /** + * Add metadata linked to file id to the query + * + * @see self::extractMetadata() + * @since 28.0.0 + */ + public function retrieveMetadata(): void; + + /** + * extract metadata from a result row + * + * @param array $row result row + * + * @return IFilesMetadata metadata + * @see self::retrieveMetadata() + * @since 28.0.0 + */ + public function extractMetadata(array $row): IFilesMetadata; + + /** + * join the metadata_index table, based on a metadataKey. + * This will prep the query for condition based on this specific metadataKey. + * If a link to the metadataKey already exists, returns known alias. + * + * TODO: investigate how to support a search done on multiple values for same key (AND). + * + * @param string $metadataKey metadata key + * @param bool $enforce limit the request only to existing metadata + * + * @return string generated table alias + * @since 28.0.0 + */ + public function joinIndex(string $metadataKey, bool $enforce = false): string; + + /** + * returns the name of the field for metadata key to be used in query expressions + * + * @param string $metadataKey metadata key + * + * @return string table field + * @since 28.0.0 + */ + public function getMetadataKeyField(string $metadataKey): string; + + /** + * returns the name of the field for metadata string value to be used in query expressions + * + * @param string $metadataKey metadata key + * + * @return string table field + * @since 28.0.0 + */ + public function getMetadataValueField(string $metadataKey): string; +} diff --git a/lib/public/FilesMetadata/Model/IFilesMetadata.php b/lib/public/FilesMetadata/Model/IFilesMetadata.php new file mode 100644 index 00000000000..7697a2f37ad --- /dev/null +++ b/lib/public/FilesMetadata/Model/IFilesMetadata.php @@ -0,0 +1,367 @@ +<?php + +declare(strict_types=1); +/** + * @copyright 2023 Maxence Lange <maxence@artificial-owl.com> + * + * @author Maxence Lange <maxence@artificial-owl.com> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\FilesMetadata\Model; + +use JsonSerializable; +use OCP\FilesMetadata\Exceptions\FilesMetadataNotFoundException; +use OCP\FilesMetadata\Exceptions\FilesMetadataTypeException; + +/** + * Model that represent metadata linked to a specific file. + * + * Example of json stored in the database + * { + * "mymeta": { + * "value": "this is a test", + * "type": "string", + * "indexed": false, + * "editPermission": 1 + * }, + * "myapp-anothermeta": { + * "value": 42, + * "type": "int", + * "indexed": true, + * "editPermission": 0 + * } + * } + * + * @see IMetadataValueWrapper + * @since 28.0.0 + */ +interface IFilesMetadata extends JsonSerializable { + /** + * returns the file id linked to this metadata + * + * @return int related file id + * @since 28.0.0 + */ + public function getFileId(): int; + + /** + * returns last time metadata were updated in the database + * + * @return int timestamp + * @since 28.0.0 + */ + public function lastUpdateTimestamp(): int; + + /** + * returns the token known at the time the metadata were extracted from database + * + * @return string token + * @since 28.0.0 + */ + public function getSyncToken(): string; + + /** + * returns all current metadata keys + * + * @return string[] list of keys + * @since 28.0.0 + */ + public function getKeys(): array; + + /** + * returns true if search metadata key exists + * + * @param string $needle metadata key to search + * + * @return bool TRUE if key exist + * @since 28.0.0 + */ + public function hasKey(string $needle): bool; + + /** + * return the list of metadata keys set as indexed + * + * @return string[] list of indexes + * @since 28.0.0 + */ + public function getIndexes(): array; + + /** + * returns true if key exists and is set as indexed + * + * @param string $key metadata key + * + * @return bool + * @since 28.0.0 + */ + public function isIndex(string $key): bool; + + /** + * set remote edit permission + * (Webdav PROPPATCH) + * + * @param string $key metadata key + * @param int $permission remote edit permission + * + * @since 28.0.0 + */ + public function setEditPermission(string $key, int $permission): void; + + /** + * returns remote edit permission + * (Webdav PROPPATCH) + * + * @param string $key metadata key + * + * @return int + * @since 28.0.0 + */ + public function getEditPermission(string $key): int; + + /** + * returns string value for a metadata key + * + * @param string $key metadata key + * + * @return string metadata value + * @throws FilesMetadataNotFoundException + * @throws FilesMetadataTypeException + * @since 28.0.0 + */ + public function getString(string $key): string; + + /** + * returns int value for a metadata key + * + * @param string $key metadata key + * + * @return int metadata value + * @throws FilesMetadataNotFoundException + * @throws FilesMetadataTypeException + * @since 28.0.0 + */ + public function getInt(string $key): int; + + /** + * returns float value for a metadata key + * + * @param string $key metadata key + * + * @return float metadata value + * @throws FilesMetadataNotFoundException + * @throws FilesMetadataTypeException + * @since 28.0.0 + */ + public function getFloat(string $key): float; + + /** + * returns bool value for a metadata key + * + * @param string $key metadata key + * + * @return bool metadata value + * @throws FilesMetadataNotFoundException + * @throws FilesMetadataTypeException + * @since 28.0.0 + */ + public function getBool(string $key): bool; + + /** + * returns array for a metadata key + * + * @param string $key metadata key + * + * @return array metadata value + * @throws FilesMetadataNotFoundException + * @throws FilesMetadataTypeException + * @since 28.0.0 + */ + public function getArray(string $key): array; + + /** + * returns string[] value for a metadata key + * + * @param string $key metadata key + * + * @return string[] metadata value + * @throws FilesMetadataNotFoundException + * @throws FilesMetadataTypeException + * @since 28.0.0 + */ + public function getStringList(string $key): array; + + /** + * returns int[] value for a metadata key + * + * @param string $key metadata key + * + * @return int[] metadata value + * @throws FilesMetadataNotFoundException + * @throws FilesMetadataTypeException + * @since 28.0.0 + */ + public function getIntList(string $key): array; + + /** + * returns the value type of the metadata (string, int, ...) + * + * @param string $key metadata key + * + * @return string value type + * @throws FilesMetadataNotFoundException + * @see IMetadataValueWrapper::TYPE_STRING + * @see IMetadataValueWrapper::TYPE_INT + * @see IMetadataValueWrapper::TYPE_FLOAT + * @see IMetadataValueWrapper::TYPE_BOOL + * @see IMetadataValueWrapper::TYPE_ARRAY + * @see IMetadataValueWrapper::TYPE_STRING_LIST + * @see IMetadataValueWrapper::TYPE_INT_LIST + * @since 28.0.0 + */ + public function getType(string $key): string; + + /** + * set a metadata key/value pair for string value + * + * @param string $key metadata key + * @param string $value metadata value + * @param bool $index set TRUE if value must be indexed + * + * @return self + * @since 28.0.0 + */ + public function setString(string $key, string $value, bool $index = false): self; + + /** + * set a metadata key/value pair for int value + * + * @param string $key metadata key + * @param int $value metadata value + * @param bool $index set TRUE if value must be indexed + * + * @return self + * @since 28.0.0 + */ + public function setInt(string $key, int $value, bool $index = false): self; + + /** + * set a metadata key/value pair for float value + * + * @param string $key metadata key + * @param float $value metadata value + * + * @return self + * @since 28.0.0 + */ + public function setFloat(string $key, float $value): self; + + /** + * set a metadata key/value pair for bool value + * + * @param string $key metadata key + * @param bool $value metadata value + * @param bool $index set TRUE if value must be indexed + * + * @return self + * @since 28.0.0 + */ + public function setBool(string $key, bool $value, bool $index = false): self; + + /** + * set a metadata key/value pair for array + * + * @param string $key metadata key + * @param array $value metadata value + * + * @return self + * @since 28.0.0 + */ + public function setArray(string $key, array $value): self; + + /** + * set a metadata key/value pair for list of string + * + * @param string $key metadata key + * @param string[] $value metadata value + * @param bool $index set TRUE if each values from the list must be indexed + * + * @return self + * @since 28.0.0 + */ + public function setStringList(string $key, array $value, bool $index = false): self; + + /** + * set a metadata key/value pair for list of int + * + * @param string $key metadata key + * @param int[] $value metadata value + * @param bool $index set TRUE if each values from the list must be indexed + * + * @return self + * @since 28.0.0 + */ + public function setIntList(string $key, array $value, bool $index = false): self; + + /** + * unset a metadata + * + * @param string $key metadata key + * + * @return self + * @since 28.0.0 + */ + public function unset(string $key): self; + + /** + * unset metadata with key starting with prefix + * + * @param string $keyPrefix metadata key prefix + * + * @return self + * @since 28.0.0 + */ + public function removeStartsWith(string $keyPrefix): self; + + /** + * returns true if object have been updated since last import + * + * @return bool TRUE if metadata have been modified + * @since 28.0.0 + */ + public function updated(): bool; + + /** + * returns metadata in a simple array with METADATA_KEY => METADATA_VALUE + * + * @return array metadata + * @since 28.0.0 + */ + public function asArray(): array; + + /** + * deserialize the object from a json + * + * @param array $data serialized version of the object + * + * @return self + * @see jsonSerialize + * @since 28.0.0 + */ + public function import(array $data): self; +} diff --git a/lib/public/FilesMetadata/Model/IMetadataValueWrapper.php b/lib/public/FilesMetadata/Model/IMetadataValueWrapper.php new file mode 100644 index 00000000000..d34cd070c8b --- /dev/null +++ b/lib/public/FilesMetadata/Model/IMetadataValueWrapper.php @@ -0,0 +1,334 @@ +<?php + +declare(strict_types=1); +/** + * @copyright 2023 Maxence Lange <maxence@artificial-owl.com> + * + * @author Maxence Lange <maxence@artificial-owl.com> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\FilesMetadata\Model; + +use JsonSerializable; +use OCP\FilesMetadata\Exceptions\FilesMetadataNotFoundException; +use OCP\FilesMetadata\Exceptions\FilesMetadataTypeException; + +/** + * Model that store the value of a single metadata. + * It stores the value, its type and the index status. + * + * @see IFilesMetadata + * @since 28.0.0 + */ +interface IMetadataValueWrapper extends JsonSerializable { + /** @since 28.0.0 */ + public const TYPE_STRING = 'string'; + /** @since 28.0.0 */ + public const TYPE_INT = 'int'; + /** @since 28.0.0 */ + public const TYPE_FLOAT = 'float'; + /** @since 28.0.0 */ + public const TYPE_BOOL = 'bool'; + /** @since 28.0.0 */ + public const TYPE_ARRAY = 'array'; + /** @since 28.0.0 */ + public const TYPE_STRING_LIST = 'string[]'; + /** @since 28.0.0 */ + public const TYPE_INT_LIST = 'int[]'; + + /** @since 28.0.0 */ + public const EDIT_FORBIDDEN = 0; + /** @since 28.0.0 */ + public const EDIT_REQ_OWNERSHIP = 1; + /** @since 28.0.0 */ + public const EDIT_REQ_WRITE_PERMISSION = 2; + /** @since 28.0.0 */ + public const EDIT_REQ_READ_PERMISSION = 3; + + + /** + * Unless a call of import() to deserialize an object is expected, a valid value type is needed here. + * + * @param string $type value type + * + * @see self::TYPE_INT + * @see self::TYPE_FLOAT + * @see self::TYPE_BOOL + * @see self::TYPE_ARRAY + * @see self::TYPE_STRING_LIST + * @see self::TYPE_INT_LIST + * @see self::TYPE_STRING + * @since 28.0.0 + */ + public function __construct(string $type); + + /** + * returns the value type + * + * @return string value type + * @see self::TYPE_INT + * @see self::TYPE_FLOAT + * @see self::TYPE_BOOL + * @see self::TYPE_ARRAY + * @see self::TYPE_STRING_LIST + * @see self::TYPE_INT_LIST + * @see self::TYPE_STRING + * @since 28.0.0 + */ + public function getType(): string; + + /** + * returns if the set value type is the one expected + * + * @param string $type value type + * + * @return bool + * @see self::TYPE_INT + * @see self::TYPE_FLOAT + * @see self::TYPE_BOOL + * @see self::TYPE_ARRAY + * @see self::TYPE_STRING_LIST + * @see self::TYPE_INT_LIST + * @see self::TYPE_STRING + * @since 28.0.0 + */ + public function isType(string $type): bool; + + /** + * throws an exception if the type is not correctly set + * + * @param string $type value type + * + * @return self + * @throws FilesMetadataTypeException if type cannot be confirmed + * @see self::TYPE_INT + * @see self::TYPE_BOOL + * @see self::TYPE_ARRAY + * @see self::TYPE_STRING_LIST + * @see self::TYPE_INT_LIST + * @see self::TYPE_STRING + * @see self::TYPE_FLOAT + * @since 28.0.0 + */ + public function assertType(string $type): self; + + /** + * set a string value + * + * @param string $value string to be set as value + * + * @return self + * @throws FilesMetadataTypeException if wrapper was not set to store a string + * @since 28.0.0 + */ + public function setValueString(string $value): self; + + /** + * set a int value + * + * @param int $value int to be set as value + * + * @return self + * @throws FilesMetadataTypeException if wrapper was not set to store an int + * @since 28.0.0 + */ + public function setValueInt(int $value): self; + + /** + * set a float value + * + * @param float $value float to be set as value + * + * @return self + * @throws FilesMetadataTypeException if wrapper was not set to store a float + * @since 28.0.0 + */ + public function setValueFloat(float $value): self; + + /** + * set a bool value + * + * @param bool $value bool to be set as value + * + * @return self + * @throws FilesMetadataTypeException if wrapper was not set to store a bool + * @since 28.0.0 + */ + public function setValueBool(bool $value): self; + + /** + * set an array value + * + * @param array $value array to be set as value + * + * @return self + * @throws FilesMetadataTypeException if wrapper was not set to store an array + * @since 28.0.0 + */ + public function setValueArray(array $value): self; + + /** + * set a string list value + * + * @param string[] $value string list to be set as value + * + * @return self + * @throws FilesMetadataTypeException if wrapper was not set to store a string list + * @since 28.0.0 + */ + public function setValueStringList(array $value): self; + + /** + * set an int list value + * + * @param int[] $value int list to be set as value + * + * @return self + * @throws FilesMetadataTypeException if wrapper was not set to store an int list + * @since 28.0.0 + */ + public function setValueIntList(array $value): self; + + + /** + * get stored value + * + * @return string set value + * @throws FilesMetadataTypeException if wrapper was not set to store a string + * @throws FilesMetadataNotFoundException if value is not set + * @since 28.0.0 + */ + public function getValueString(): string; + + /** + * get stored value + * + * @return int set value + * @throws FilesMetadataTypeException if wrapper was not set to store an int + * @throws FilesMetadataNotFoundException if value is not set + * @since 28.0.0 + */ + public function getValueInt(): int; + + /** + * get stored value + * + * @return float set value + * @throws FilesMetadataTypeException if wrapper was not set to store a float + * @throws FilesMetadataNotFoundException if value is not set + * @since 28.0.0 + */ + public function getValueFloat(): float; + + /** + * get stored value + * + * @return bool set value + * @throws FilesMetadataTypeException if wrapper was not set to store a bool + * @throws FilesMetadataNotFoundException if value is not set + * @since 28.0.0 + */ + public function getValueBool(): bool; + + /** + * get stored value + * + * @return array set value + * @throws FilesMetadataTypeException if wrapper was not set to store an array + * @throws FilesMetadataNotFoundException if value is not set + * @since 28.0.0 + */ + public function getValueArray(): array; + + /** + * get stored value + * + * @return string[] set value + * @throws FilesMetadataTypeException if wrapper was not set to store a string list + * @throws FilesMetadataNotFoundException if value is not set + * @since 28.0.0 + */ + public function getValueStringList(): array; + + /** + * get stored value + * + * @return int[] set value + * @throws FilesMetadataTypeException if wrapper was not set to store an int list + * @throws FilesMetadataNotFoundException if value is not set + * @since 28.0.0 + */ + public function getValueIntList(): array; + + /** + * get stored value + * + * @return string|int|float|bool|array|string[]|int[] set value + * @throws FilesMetadataNotFoundException if value is not set + * @since 28.0.0 + */ + public function getValueAny(): mixed; + + /** + * @param bool $indexed TRUE to set the stored value as an indexed value + * + * @return self + * @since 28.0.0 + */ + public function setIndexed(bool $indexed): self; + + /** + * returns if value is an indexed value + * + * @return bool TRUE if value is an indexed value + * @since 28.0.0 + */ + public function isIndexed(): bool; + + /** + * set remote edit permission + * (Webdav PROPPATCH) + * + * @param int $permission edit permission + * + * @return self + * @since 28.0.0 + */ + public function setEditPermission(int $permission): self; + + /** + * get remote edit permission + * (Webdav PROPPATCH) + * + * @return int edit permission + * @since 28.0.0 + */ + public function getEditPermission(): int; + + /** + * deserialize the object from a json + * + * @param array $data serialized version of the object + * + * @return self + * @see jsonSerialize + * @since 28.0.0 + */ + public function import(array $data): self; +} diff --git a/lib/public/Group/Backend/ABackend.php b/lib/public/Group/Backend/ABackend.php index 7f5cf732335..274b98655e4 100644 --- a/lib/public/Group/Backend/ABackend.php +++ b/lib/public/Group/Backend/ABackend.php @@ -6,6 +6,7 @@ declare(strict_types=1); * @copyright Copyright (c) 2018 Roeland Jago Douma <roeland@famdouma.nl> * * @author Roeland Jago Douma <roeland@famdouma.nl> + * @author Carl Schwan <carl@carlschwan.eu> * * @license GNU AGPL version 3 or any later version * @@ -30,7 +31,7 @@ use OCP\GroupInterface; /** * @since 14.0.0 */ -abstract class ABackend implements GroupInterface { +abstract class ABackend implements GroupInterface, IBatchMethodsBackend { /** * @deprecated 14.0.0 * @since 14.0.0 @@ -65,4 +66,29 @@ abstract class ABackend implements GroupInterface { return (bool)($actions & $implements); } + + /** + * @since 28.0.0 + */ + public function groupsExists(array $gids): array { + return array_values(array_filter( + $gids, + fn (string $gid): bool => $this->groupExists($gid), + )); + } + + /** + * @since 28.0.0 + */ + public function getGroupsDetails(array $gids): array { + if (!($this instanceof IGroupDetailsBackend || $this->implementsActions(GroupInterface::GROUP_DETAILS))) { + throw new \Exception("Should not have been called"); + } + /** @var IGroupDetailsBackend $this */ + $groupData = []; + foreach ($gids as $gid) { + $groupData[$gid] = $this->getGroupDetails($gid); + } + return $groupData; + } } diff --git a/lib/public/Group/Backend/IBatchMethodsBackend.php b/lib/public/Group/Backend/IBatchMethodsBackend.php new file mode 100644 index 00000000000..2af00e42825 --- /dev/null +++ b/lib/public/Group/Backend/IBatchMethodsBackend.php @@ -0,0 +1,61 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2018 Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * @author Carl Schwan <carl@carlschwan.eu> + * @author Côme Chilliet <come.chilliet@nextcloud.com> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ +namespace OCP\Group\Backend; + +/** + * @brief Optional interface for group backends + * @since 28.0.0 + */ +interface IBatchMethodsBackend { + /** + * @brief Batch method to check if a list of groups exists + * + * The default implementation in ABackend will just call groupExists in + * a loop. But a GroupBackend implementation should provides a more optimized + * override this method to provide a more optimized way to execute this operation. + * + * @param list<string> $gids + * @return list<string> the list of group that exists + * @since 28.0.0 + */ + public function groupsExists(array $gids): array; + + /** + * @brief Batch method to get the group details of a list of groups + * + * The default implementation in ABackend will just call getGroupDetails in + * a loop. But a GroupBackend implementation should override this method + * to provide a more optimized way to execute this operation. + * + * @throw \RuntimeException if called on a backend that doesn't implements IGroupDetailsBackend + * + * @return array<string, array{displayName?: string}> + * @since 28.0.0 + */ + public function getGroupsDetails(array $gids): array; +} diff --git a/lib/public/Group/Backend/IGroupDetailsBackend.php b/lib/public/Group/Backend/IGroupDetailsBackend.php index 4852f978195..851c10388e0 100644 --- a/lib/public/Group/Backend/IGroupDetailsBackend.php +++ b/lib/public/Group/Backend/IGroupDetailsBackend.php @@ -6,6 +6,7 @@ declare(strict_types=1); * @copyright Copyright (c) 2018 Roeland Jago Douma <roeland@famdouma.nl> * * @author Roeland Jago Douma <roeland@famdouma.nl> + * @author Carl Schwan <carl@carlschwan.eu> * * @license GNU AGPL version 3 or any later version * @@ -26,10 +27,17 @@ declare(strict_types=1); namespace OCP\Group\Backend; /** + * @brief Optional interface for group backends * @since 14.0.0 */ interface IGroupDetailsBackend { /** + * @brief Get additional details for a group, for example the display name. + * + * The array returned can be empty when no additional information is available + * for the group. + * + * @return array{displayName?: string} * @since 14.0.0 */ public function getGroupDetails(string $gid): array; diff --git a/lib/public/GroupInterface.php b/lib/public/GroupInterface.php index a18d38df002..599a0eb2ff0 100644 --- a/lib/public/GroupInterface.php +++ b/lib/public/GroupInterface.php @@ -86,7 +86,8 @@ interface GroupInterface { public function getUserGroups($uid); /** - * get a list of all groups + * @brief Get a list of all groups + * * @param string $search * @param int $limit * @param int $offset @@ -98,7 +99,8 @@ interface GroupInterface { public function getGroups(string $search = '', int $limit = -1, int $offset = 0); /** - * check if a group exists + * @brief Check if a group exists + * * @param string $gid * @return bool * @since 4.5.0 diff --git a/lib/public/ICacheFactory.php b/lib/public/ICacheFactory.php index d70a836aa52..70ad955849d 100644 --- a/lib/public/ICacheFactory.php +++ b/lib/public/ICacheFactory.php @@ -75,4 +75,20 @@ interface ICacheFactory { * @since 13.0.0 */ public function createLocal(string $prefix = ''): ICache; + + /** + * Create an in-memory cache instance + * + * Useful for remembering values inside one process. Cache memory is cleared + * when the object is garbage-collected. Implementation may also expire keys + * earlier when the TTL is reached or too much memory is consumed. + * + * Cache keys are local to the cache object. When building two in-memory + * caches, there is no data exchange between the instances. + * + * @param int $capacity maximum number of cache keys + * @return ICache + * @since 28.0.0 + */ + public function createInMemory(int $capacity = 512): ICache; } diff --git a/lib/public/IDBConnection.php b/lib/public/IDBConnection.php index fe0267facc5..a528bb1a57b 100644 --- a/lib/public/IDBConnection.php +++ b/lib/public/IDBConnection.php @@ -45,6 +45,18 @@ use OCP\DB\QueryBuilder\IQueryBuilder; * @since 6.0.0 */ interface IDBConnection { + /* @since 28.0.0 */ + public const PLATFORM_MYSQL = 'mysql'; + + /* @since 28.0.0 */ + public const PLATFORM_ORACLE = 'oracle'; + + /* @since 28.0.0 */ + public const PLATFORM_POSTGRES = 'postgres'; + + /* @since 28.0.0 */ + public const PLATFORM_SQLITE = 'sqlite'; + /** * Gets the QueryBuilder for the connection. * @@ -339,4 +351,12 @@ interface IDBConnection { * @since 13.0.0 */ public function migrateToSchema(Schema $toSchema): void; + + /** + * Returns the database provider name + * @link https://github.com/nextcloud/server/issues/30877 + * @since 28.0.0 + * @return IDBConnection::PLATFORM_* + */ + public function getDatabaseProvider(): string; } diff --git a/lib/public/IGroup.php b/lib/public/IGroup.php index ec26cc55b69..51417641e26 100644 --- a/lib/public/IGroup.php +++ b/lib/public/IGroup.php @@ -1,4 +1,7 @@ <?php + +declare(strict_types=1); + /** * @copyright Copyright (c) 2016, ownCloud, Inc. * @@ -38,7 +41,7 @@ interface IGroup { * @return string * @since 8.0.0 */ - public function getGID(); + public function getGID(): string; /** * Returns the group display name @@ -46,7 +49,7 @@ interface IGroup { * @return string * @since 12.0.0 */ - public function getDisplayName(); + public function getDisplayName(): string; /** * Set the group display name @@ -60,43 +63,47 @@ interface IGroup { /** * get all users in the group * - * @return \OCP\IUser[] + * @return IUser[] * @since 8.0.0 */ - public function getUsers(); + public function getUsers(): array; /** * check if a user is in the group * - * @param \OCP\IUser $user + * @param IUser $user + * * @return bool * @since 8.0.0 */ - public function inGroup(IUser $user); + public function inGroup(IUser $user): bool; /** * add a user to the group * - * @param \OCP\IUser $user + * @param IUser $user + * * @since 8.0.0 */ - public function addUser(IUser $user); + public function addUser(IUser $user): void; /** - * remove a user from the group + * Remove a user from the group + * + * @param IUser $user * - * @param \OCP\IUser $user * @since 8.0.0 */ - public function removeUser($user); + public function removeUser(IUser $user): void; /** * search for users in the group by userid * * @param string $search - * @param int $limit - * @param int $offset - * @return \OCP\IUser[] + * @param int|null $limit + * @param int|null $offset + * + * @return IUser[] * @since 8.0.0 */ public function searchUsers(string $search, ?int $limit = null, ?int $offset = null): array; @@ -108,7 +115,7 @@ interface IGroup { * @return int|bool * @since 8.0.0 */ - public function count($search = ''); + public function count(string $search = ''): int|bool; /** * returns the number of disabled users @@ -116,18 +123,19 @@ interface IGroup { * @return int|bool * @since 14.0.0 */ - public function countDisabled(); + public function countDisabled(): int|bool; /** - * search for users in the group by displayname + * Search for users in the group by displayname * * @param string $search - * @param int $limit - * @param int $offset - * @return \OCP\IUser[] + * @param int|null $limit + * @param int|null $offset + * + * @return IUser[] * @since 8.0.0 */ - public function searchDisplayName($search, $limit = null, $offset = null); + public function searchDisplayName(string $search, int $limit = null, int $offset = null): array; /** * Get the names of the backends the group is connected to @@ -135,27 +143,27 @@ interface IGroup { * @return string[] * @since 22.0.0 */ - public function getBackendNames(); + public function getBackendNames(): array; /** - * delete the group + * Delete the group * * @return bool * @since 8.0.0 */ - public function delete(); + public function delete(): bool; /** * @return bool * @since 14.0.0 */ - public function canRemoveUser(); + public function canRemoveUser(): bool; /** * @return bool * @since 14.0.0 */ - public function canAddUser(); + public function canAddUser(): bool; /** * @return bool diff --git a/lib/public/INavigationManager.php b/lib/public/INavigationManager.php index 710cbd1248d..36f80c3293f 100644 --- a/lib/public/INavigationManager.php +++ b/lib/public/INavigationManager.php @@ -33,6 +33,10 @@ namespace OCP; /** + * @psalm-type NavigationEntry = array{id: string, order: int, href: string, name: string, app?: string, icon?: string, classes?: string, type?: string} + */ + +/** * Manages the ownCloud navigation * @since 6.0.0 */ @@ -58,9 +62,11 @@ interface INavigationManager { /** * Creates a new navigation entry * - * @param array|\Closure $entry Array containing: id, name, order, icon and href key + * @param array array|\Closure $entry Array containing: id, name, order, icon and href key + * If a menu entry (type = 'link') is added, you shall also set app to the app that added the entry. * The use of a closure is preferred, because it will avoid * loading the routing of your app, unless required. + * @psalm-param NavigationEntry|callable():NavigationEntry $entry * @return void * @since 6.0.0 */ diff --git a/lib/public/IPhoneNumberUtil.php b/lib/public/IPhoneNumberUtil.php new file mode 100644 index 00000000000..733de0e35a6 --- /dev/null +++ b/lib/public/IPhoneNumberUtil.php @@ -0,0 +1,57 @@ +<?php + +declare(strict_types=1); +/** + * + * @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com> + * + * @author Joas Schilling <coding@schilljs.com> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP; + +/** + * @since 28.0.0 + */ +interface IPhoneNumberUtil { + /** + * Returns the country code for a specific region + * + * For example, this would be `41` for Switzerland and `49` for Germany. + * Returns null when the region is invalid. + * + * @param string $regionCode Two-letter region code of ISO 3166-1 + * @return int|null Null when invalid/unsupported, the phone country code otherwise + * @since 28.0.0 + */ + public function getCountryCodeForRegion(string $regionCode): ?int; + + /** + * Converts a given input into an E164 formatted phone number + * + * E164 is the international format without any formatting characters or spaces. + * E.g. +41446681800 where +41 is the region code. + * + * @param string $input Input phone number can contain formatting spaces, slashes and dashes + * @param string|null $defaultRegion Two-letter region code of ISO 3166-1 + * @return string|null Null when the input is invalid for the given region or requires a region. + * @since 28.0.0 + */ + public function convertToStandardFormat(string $input, ?string $defaultRegion = null): ?string; +} diff --git a/lib/public/IUserManager.php b/lib/public/IUserManager.php index 1efb3d5f0c2..0a94c5ad928 100644 --- a/lib/public/IUserManager.php +++ b/lib/public/IUserManager.php @@ -140,6 +140,12 @@ interface IUserManager { public function searchDisplayName($pattern, $limit = null, $offset = null); /** + * @return IUser[] + * @since 28.0.0 + */ + public function getDisabledUsers(?int $limit = null, int $offset = 0): array; + + /** * Search known users (from phonebook sync) by displayName * * @param string $searcher diff --git a/lib/public/Migration/IOutput.php b/lib/public/Migration/IOutput.php index 70fb56b6bdd..97b0e15b9b5 100644 --- a/lib/public/Migration/IOutput.php +++ b/lib/public/Migration/IOutput.php @@ -32,6 +32,13 @@ interface IOutput { /** * @param string $message * @return void + * @since 28.0.0 + */ + public function debug(string $message): void; + + /** + * @param string $message + * @return void * @since 9.1.0 */ public function info($message); diff --git a/lib/public/OCM/Events/ResourceTypeRegisterEvent.php b/lib/public/OCM/Events/ResourceTypeRegisterEvent.php new file mode 100644 index 00000000000..1048d8d0d49 --- /dev/null +++ b/lib/public/OCM/Events/ResourceTypeRegisterEvent.php @@ -0,0 +1,62 @@ +<?php + +declare(strict_types=1); +/* + * @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com> + * + * @author Joas Schilling <coding@schilljs.com> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\OCM\Events; + +use OCP\EventDispatcher\Event; +use OCP\OCM\IOCMProvider; + +/** + * Use this event to register additional OCM resources before the API returns + * them in the OCM provider list and capability + * + * @since 28.0.0 + */ +class ResourceTypeRegisterEvent extends Event { + /** + * @param IOCMProvider $provider + * @since 28.0.0 + */ + public function __construct( + protected IOCMProvider $provider, + ) { + parent::__construct(); + } + + /** + * @param string $name + * @param list<string> $shareTypes List of supported share recipients, e.g. 'user', 'group', … + * @param array<string, string> $protocols List of supported protocols and their location, + * e.g. ['webdav' => '/remote.php/webdav/'] + * @since 28.0.0 + */ + public function registerResourceType(string $name, array $shareTypes, array $protocols): void { + $resourceType = $this->provider->createNewResourceType(); + $resourceType->setName($name) + ->setShareTypes($shareTypes) + ->setProtocols($protocols); + $this->provider->addResourceType($resourceType); + } +} diff --git a/lib/public/OCM/Exceptions/OCMArgumentException.php b/lib/public/OCM/Exceptions/OCMArgumentException.php new file mode 100644 index 00000000000..e3abd7bf26b --- /dev/null +++ b/lib/public/OCM/Exceptions/OCMArgumentException.php @@ -0,0 +1,34 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright 2023, Maxence Lange <maxence@artificial-owl.com> + * + * @author Maxence Lange <maxence@artificial-owl.com> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ +namespace OCP\OCM\Exceptions; + +use Exception; + +/** + * @since 28.0.0 + */ +class OCMArgumentException extends Exception { +} diff --git a/lib/public/OCM/Exceptions/OCMProviderException.php b/lib/public/OCM/Exceptions/OCMProviderException.php new file mode 100644 index 00000000000..32dab10dc68 --- /dev/null +++ b/lib/public/OCM/Exceptions/OCMProviderException.php @@ -0,0 +1,34 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright 2023, Maxence Lange <maxence@artificial-owl.com> + * + * @author Maxence Lange <maxence@artificial-owl.com> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ +namespace OCP\OCM\Exceptions; + +use Exception; + +/** + * @since 28.0.0 + */ +class OCMProviderException extends Exception { +} diff --git a/lib/public/OCM/IOCMDiscoveryService.php b/lib/public/OCM/IOCMDiscoveryService.php new file mode 100644 index 00000000000..2407e7b24e8 --- /dev/null +++ b/lib/public/OCM/IOCMDiscoveryService.php @@ -0,0 +1,48 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2023 Maxence Lange <maxence@artificial-owl.com> + * + * @author Maxence Lange <maxence@artificial-owl.com> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\OCM; + +use OCP\OCM\Exceptions\OCMProviderException; + +/** + * Discover remote OCM services + * + * @since 28.0.0 + */ +interface IOCMDiscoveryService { + /** + * Discover remote OCM services + * + * @param string $remote address of the remote provider + * @param bool $skipCache ignore cache, refresh data + * + * @return IOCMProvider + * @throws OCMProviderException if no valid discovery data can be returned + * @since 28.0.0 + */ + public function discover(string $remote, bool $skipCache = false): IOCMProvider; +} diff --git a/lib/public/OCM/IOCMProvider.php b/lib/public/OCM/IOCMProvider.php new file mode 100644 index 00000000000..6df7eed370c --- /dev/null +++ b/lib/public/OCM/IOCMProvider.php @@ -0,0 +1,165 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2023 Maxence Lange <maxence@artificial-owl.com> + * + * @author Maxence Lange <maxence@artificial-owl.com> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\OCM; + +use JsonSerializable; +use OCP\OCM\Exceptions\OCMArgumentException; +use OCP\OCM\Exceptions\OCMProviderException; + +/** + * Model based on the Open Cloud Mesh Discovery API + * @link https://github.com/cs3org/OCM-API/ + * @since 28.0.0 + */ +interface IOCMProvider extends JsonSerializable { + /** + * enable OCM + * + * @param bool $enabled + * + * @return $this + * @since 28.0.0 + */ + public function setEnabled(bool $enabled): static; + + /** + * is set as enabled ? + * + * @return bool + * @since 28.0.0 + */ + public function isEnabled(): bool; + + /** + * get set API Version + * + * @param string $apiVersion + * + * @return $this + * @since 28.0.0 + */ + public function setApiVersion(string $apiVersion): static; + + /** + * returns API version + * + * @return string + * @since 28.0.0 + */ + public function getApiVersion(): string; + + /** + * configure endpoint + * + * @param string $endPoint + * + * @return $this + * @since 28.0.0 + */ + public function setEndPoint(string $endPoint): static; + + /** + * get configured endpoint + * + * @return string + * @since 28.0.0 + */ + public function getEndPoint(): string; + + /** + * create a new resource to later add it with {@see addResourceType()} + * @return IOCMResource + * @since 28.0.0 + */ + public function createNewResourceType(): IOCMResource; + + /** + * add a single resource to the object + * + * @param IOCMResource $resource + * + * @return $this + * @since 28.0.0 + */ + public function addResourceType(IOCMResource $resource): static; + + /** + * set resources + * + * @param IOCMResource[] $resourceTypes + * + * @return $this + * @since 28.0.0 + */ + public function setResourceTypes(array $resourceTypes): static; + + /** + * get all set resources + * + * @return IOCMResource[] + * @since 28.0.0 + */ + public function getResourceTypes(): array; + + /** + * extract a specific string value from the listing of protocols, based on resource-name and protocol-name + * + * @param string $resourceName + * @param string $protocol + * + * @return string + * @throws OCMArgumentException + * @since 28.0.0 + */ + public function extractProtocolEntry(string $resourceName, string $protocol): string; + + /** + * import data from an array + * + * @param array<string, int|string|bool|array> $data + * + * @return $this + * @throws OCMProviderException in case a descent provider cannot be generated from data + * @since 28.0.0 + */ + public function import(array $data): static; + + /** + * @return array{ + * enabled: bool, + * apiVersion: string, + * endPoint: string, + * resourceTypes: array{ + * name: string, + * shareTypes: string[], + * protocols: array<string, string> + * }[] + * } + * @since 28.0.0 + */ + public function jsonSerialize(): array; +} diff --git a/lib/public/OCM/IOCMResource.php b/lib/public/OCM/IOCMResource.php new file mode 100644 index 00000000000..242c77116f1 --- /dev/null +++ b/lib/public/OCM/IOCMResource.php @@ -0,0 +1,111 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2023 Maxence Lange <maxence@artificial-owl.com> + * + * @author Maxence Lange <maxence@artificial-owl.com> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\OCM; + +use JsonSerializable; + +/** + * Model based on the Open Cloud Mesh Discovery API + * + * @link https://github.com/cs3org/OCM-API/ + * @since 28.0.0 + */ +interface IOCMResource extends JsonSerializable { + /** + * set name of the resource + * + * @param string $name + * + * @return $this + * @since 28.0.0 + */ + public function setName(string $name): static; + + /** + * get name of the resource + * + * @return string + * @since 28.0.0 + */ + public function getName(): string; + + /** + * set share types + * + * @param string[] $shareTypes + * + * @return $this + * @since 28.0.0 + */ + public function setShareTypes(array $shareTypes): static; + + /** + * get share types + * + * @return string[] + * @since 28.0.0 + */ + public function getShareTypes(): array; + + /** + * set available protocols + * + * @param array<string, string> $protocols + * + * @return $this + * @since 28.0.0 + */ + public function setProtocols(array $protocols): static; + + /** + * get configured protocols + * + * @return array<string, string> + * @since 28.0.0 + */ + public function getProtocols(): array; + + /** + * import data from an array + * + * @param array $data + * + * @return $this + * @since 28.0.0 + */ + public function import(array $data): static; + + /** + * @return array{ + * name: string, + * shareTypes: string[], + * protocols: array<string, string> + * } + * @since 28.0.0 + */ + public function jsonSerialize(): array; +} diff --git a/lib/public/Profile/IProfileManager.php b/lib/public/Profile/IProfileManager.php new file mode 100644 index 00000000000..996e49d116e --- /dev/null +++ b/lib/public/Profile/IProfileManager.php @@ -0,0 +1,106 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com> + * + * @author Joas Schilling <coding@schilljs.com> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\Profile; + +use OCP\Accounts\IAccountManager; +use OCP\IUser; + +/** + * @since 28.0.0 + */ +interface IProfileManager { + /** + * Visible to users, guests, and public access + * + * @since 28.0.0 + */ + public const VISIBILITY_SHOW = 'show'; + + /** + * Visible to users and guests + * + * @since 28.0.0 + */ + public const VISIBILITY_SHOW_USERS_ONLY = 'show_users_only'; + + /** + * Visible to nobody + * + * @since 28.0.0 + */ + public const VISIBILITY_HIDE = 'hide'; + + /** + * Default account property visibility + * + * @since 28.0.0 + */ + public const DEFAULT_PROPERTY_VISIBILITY = [ + IAccountManager::PROPERTY_ADDRESS => self::VISIBILITY_SHOW_USERS_ONLY, + IAccountManager::PROPERTY_AVATAR => self::VISIBILITY_SHOW, + IAccountManager::PROPERTY_BIOGRAPHY => self::VISIBILITY_SHOW, + IAccountManager::PROPERTY_DISPLAYNAME => self::VISIBILITY_SHOW, + IAccountManager::PROPERTY_HEADLINE => self::VISIBILITY_SHOW, + IAccountManager::PROPERTY_ORGANISATION => self::VISIBILITY_SHOW, + IAccountManager::PROPERTY_ROLE => self::VISIBILITY_SHOW, + IAccountManager::PROPERTY_EMAIL => self::VISIBILITY_SHOW_USERS_ONLY, + IAccountManager::PROPERTY_PHONE => self::VISIBILITY_SHOW_USERS_ONLY, + IAccountManager::PROPERTY_TWITTER => self::VISIBILITY_SHOW, + IAccountManager::PROPERTY_WEBSITE => self::VISIBILITY_SHOW, + ]; + + /** + * Default visibility + * + * @since 28.0.0 + */ + public const DEFAULT_VISIBILITY = self::VISIBILITY_SHOW_USERS_ONLY; + + /** + * If no user is passed as an argument return whether profile is enabled globally in `config.php` + * + * @since 28.0.0 + */ + public function isProfileEnabled(?IUser $user = null): bool; + + /** + * Return whether the profile parameter of the target user + * is visible to the visiting user + * + * @since 28.0.0 + */ + public function isProfileFieldVisible(string $profileField, IUser $targetUser, ?IUser $visitingUser): bool; + + /** + * Return the profile parameters of the target user that are visible to the visiting user + * in an associative array + * + * @return array{userId: string, address?: ?string, biography?: ?string, displayname?: ?string, headline?: ?string, isUserAvatarVisible?: bool, organisation?: ?string, role?: ?string, actions: list<array{id: string, icon: string, title: string, target: ?string}>} + * @since 28.0.0 + */ + public function getProfileFields(IUser $targetUser, ?IUser $visitingUser): array; +} diff --git a/lib/public/Search/FilterDefinition.php b/lib/public/Search/FilterDefinition.php new file mode 100644 index 00000000000..7e1538acedb --- /dev/null +++ b/lib/public/Search/FilterDefinition.php @@ -0,0 +1,101 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright 2023 Benjamin Gaussorgues <benjamin.gaussorgues@nextcloud.com> + * + * @author Benjamin Gaussorgues <benjamin.gaussorgues@nextcloud.com> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ +namespace OCP\Search; + +use InvalidArgumentException; + +/** + * Filter definition + * + * Describe filter attributes + * + * @since 28.0.0 + */ +class FilterDefinition { + public const TYPE_BOOL = 'bool'; + public const TYPE_INT = 'int'; + public const TYPE_FLOAT = 'float'; + public const TYPE_STRING = 'string'; + public const TYPE_STRINGS = 'strings'; + public const TYPE_DATETIME = 'datetime'; + public const TYPE_PERSON = 'person'; + public const TYPE_NC_USER = 'nc-user'; + public const TYPE_NC_GROUP = 'nc-group'; + + /** + * Build filter definition + * + * @param self::TYPE_* $type + * @param bool $exclusive If true, all providers not supporting this filter will be ignored when this filter is provided + * @throw InvalidArgumentException in case of invalid name. Allowed characters are -, 0-9, a-z. + * @since 28.0.0 + */ + public function __construct( + private string $name, + private string $type = self::TYPE_STRING, + private bool $exclusive = true, + ) { + if (!preg_match('/[-0-9a-z]+/Au', $name)) { + throw new InvalidArgumentException('Invalid filter name. Allowed characters are [-0-9a-z]'); + } + } + + /** + * Filter name + * + * Name is used in query string and for advanced syntax `name: <value>` + * + * @since 28.0.0 + */ + public function name(): string { + return $this->name; + } + + /** + * Filter type + * + * Expected type of value for the filter + * + * @return self::TYPE_* + * @since 28.0.0 + */ + public function type(): string { + return $this->type; + } + + /** + * Is filter exclusive? + * + * If exclusive, only provider with support for this filter will receive the query. + * Example: if an exclusive filter `mimetype` is declared, a search with this term will not + * be send to providers like `settings` that doesn't support it. + * + * @since 28.0.0 + */ + public function exclusive(): bool { + return $this->exclusive; + } +} diff --git a/lib/public/Search/IFilter.php b/lib/public/Search/IFilter.php new file mode 100644 index 00000000000..6065622cb71 --- /dev/null +++ b/lib/public/Search/IFilter.php @@ -0,0 +1,55 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright 2023 Benjamin Gaussorgues <benjamin.gaussorgues@nextcloud.com> + * + * @author Benjamin Gaussorgues <benjamin.gaussorgues@nextcloud.com> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ +namespace OCP\Search; + +/** + * Interface for search filters + * + * @since 28.0.0 + */ +interface IFilter { + /** @since 28.0.0 */ + public const BUILTIN_TERM = 'term'; + /** @since 28.0.0 */ + public const BUILTIN_SINCE = 'since'; + /** @since 28.0.0 */ + public const BUILTIN_UNTIL = 'until'; + /** @since 28.0.0 */ + public const BUILTIN_PERSON = 'person'; + /** @since 28.0.0 */ + public const BUILTIN_TITLE_ONLY = 'title-only'; + /** @since 28.0.0 */ + public const BUILTIN_PLACES = 'places'; + /** @since 28.0.0 */ + public const BUILTIN_PROVIDER = 'provider'; + + /** + * Get filter value + * + * @since 28.0.0 + */ + public function get(): mixed; +} diff --git a/lib/public/Search/IFilterCollection.php b/lib/public/Search/IFilterCollection.php new file mode 100644 index 00000000000..6ca53a1c628 --- /dev/null +++ b/lib/public/Search/IFilterCollection.php @@ -0,0 +1,57 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright 2023 Benjamin Gaussorgues <benjamin.gaussorgues@nextcloud.com> + * + * @author Benjamin Gaussorgues <benjamin.gaussorgues@nextcloud.com> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ +namespace OCP\Search; + +use IteratorAggregate; + +/** + * Interface for search filters + * + * @since 28.0.0 + * @extends IteratorAggregate<string, \OCP\Search\IFilter> + */ +interface IFilterCollection extends IteratorAggregate { + /** + * Check if a filter exits + * + * @since 28.0.0 + */ + public function has(string $name): bool; + + /** + * Get a filter by name + * + * @since 28.0.0 + */ + public function get(string $name): ?IFilter; + + /** + * Return Iterator of filters + * + * @since 28.0.0 + */ + public function getIterator(): \Traversable; +} diff --git a/lib/public/Search/IFilteringProvider.php b/lib/public/Search/IFilteringProvider.php new file mode 100644 index 00000000000..dbe1044a539 --- /dev/null +++ b/lib/public/Search/IFilteringProvider.php @@ -0,0 +1,72 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright 2023 Benjamin Gaussorgues <benjamin.gaussorgues@nextcloud.com> + * + * @author Benjamin Gaussorgues <benjamin.gaussorgues@nextcloud.com> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ +namespace OCP\Search; + +/** + * Interface for advanced search providers + * + * These providers will be implemented in apps, so they can participate in the + * global search results of Nextcloud. If an app provides more than one type of + * resource, e.g. contacts and address books in Nextcloud Contacts, it should + * register one provider per group. + * + * @since 28.0.0 + */ +interface IFilteringProvider extends IProvider { + /** + * Return the names of filters supported by the application + * + * If a filter sent by client is not in this list, + * the current provider will be ignored. + * Example: + * array('term', 'since', 'custom-filter'); + * + * @since 28.0.0 + * @return string[] Name of supported filters (default or defined by application) + */ + public function getSupportedFilters(): array; + + /** + * Get alternate IDs handled by this provider + * + * A search provider can complete results from other search providers. + * For example, files and full-text-search can search in files. + * If you use `in:files` in a search, provider files will be invoked, + * with all other providers declaring `files` in this method + * + * @since 28.0.0 + * @return string[] IDs + */ + public function getAlternateIds(): array; + + /** + * Allows application to declare custom filters + * + * @since 28.0.0 + * @return list<FilterDefinition> + */ + public function getCustomFilters(): array; +} diff --git a/lib/public/Search/IInAppSearch.php b/lib/public/Search/IInAppSearch.php new file mode 100644 index 00000000000..9e1e294bf4d --- /dev/null +++ b/lib/public/Search/IInAppSearch.php @@ -0,0 +1,34 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright 2023 Benjamin Gaussorgues <benjamin.gaussorgues@nextcloud.com> + * + * @author Benjamin Gaussorgues <benjamin.gaussorgues@nextcloud.com> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ +namespace OCP\Search; + +/** + * Interface for search providers supporting in-app search + * + * @since 28.0.0 + */ +interface IInAppSearch extends IProvider { +} diff --git a/lib/public/Search/IProvider.php b/lib/public/Search/IProvider.php index 61655c47367..95d7a1b163a 100644 --- a/lib/public/Search/IProvider.php +++ b/lib/public/Search/IProvider.php @@ -68,15 +68,17 @@ interface IProvider { /** * Get the search provider order * The lower the int, the higher it will be sorted (0 will be before 10) + * If null, the search provider will be hidden in the UI and the API not called * * @param string $route the route the user is currently at, e.g. files.view.index * @param array $routeParameters the parameters of the route the user is currently at, e.g. [fileId = 982, dir = "/"] * - * @return int + * @return int|null * * @since 20.0.0 + * @since 28.0.0 Can return null */ - public function getOrder(string $route, array $routeParameters): int; + public function getOrder(string $route, array $routeParameters): ?int; /** * Find matching search entries in an app diff --git a/lib/public/Search/ISearchQuery.php b/lib/public/Search/ISearchQuery.php index a545d1dbccb..75d95b45c4c 100644 --- a/lib/public/Search/ISearchQuery.php +++ b/lib/public/Search/ISearchQuery.php @@ -52,6 +52,20 @@ interface ISearchQuery { public function getTerm(): string; /** + * Get a single request filter + * + * @since 28.0.0 + */ + public function getFilter(string $name): ?IFilter; + + /** + * Get request filters + * + * @since 28.0.0 + */ + public function getFilters(): IFilterCollection; + + /** * Get the sort order of results as defined as SORT_* constants on this interface * * @return int diff --git a/lib/public/Security/RateLimiting/ILimiter.php b/lib/public/Security/RateLimiting/ILimiter.php new file mode 100644 index 00000000000..275746b0b49 --- /dev/null +++ b/lib/public/Security/RateLimiting/ILimiter.php @@ -0,0 +1,72 @@ +<?php + +declare(strict_types=1); + +/* + * @copyright 2023 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2023 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @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 <http://www.gnu.org/licenses/>. + */ + +namespace OCP\Security\RateLimiting; + +use OCP\AppFramework\Http\Attribute\AnonRateLimit; +use OCP\AppFramework\Http\Attribute\UserRateLimit; +use OCP\IUser; + +/** + * Programmatic rate limiter for web requests that are not handled by an app framework controller + * + * @see AnonRateLimit + * @see UserRateLimit + * + * @since 28.0.0 + */ +interface ILimiter { + /** + * Registers attempt for an anonymous request + * + * @param string $identifier + * @param int $anonLimit + * @param int $anonPeriod in seconds + * @param string $ip + * @throws IRateLimitExceededException if limits are reached, which should cause a HTTP 429 response + * @since 28.0.0 + * + */ + public function registerAnonRequest(string $identifier, + int $anonLimit, + int $anonPeriod, + string $ip): void; + + /** + * Registers attempt for an authenticated request + * + * @param string $identifier + * @param int $userLimit + * @param int $userPeriod in seconds + * @param IUser $user the acting user + * @throws IRateLimitExceededException if limits are reached, which should cause a HTTP 429 response + * @since 28.0.0 + * + */ + public function registerUserRequest(string $identifier, + int $userLimit, + int $userPeriod, + IUser $user): void; +} diff --git a/lib/public/Security/RateLimiting/IRateLimitExceededException.php b/lib/public/Security/RateLimiting/IRateLimitExceededException.php new file mode 100644 index 00000000000..9bc8c22a67c --- /dev/null +++ b/lib/public/Security/RateLimiting/IRateLimitExceededException.php @@ -0,0 +1,36 @@ +<?php + +declare(strict_types=1); + +/* + * @copyright 2023 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2023 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @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 <http://www.gnu.org/licenses/>. + */ + +namespace OCP\Security\RateLimiting; + +use Throwable; + +/** + * Thrown if the (anonymous) user has exceeded a rate limit + * + * @since 28.0.0 + */ +interface IRateLimitExceededException extends Throwable { +} diff --git a/lib/public/SetupCheck/ISetupCheck.php b/lib/public/SetupCheck/ISetupCheck.php new file mode 100644 index 00000000000..96eb6ddd7da --- /dev/null +++ b/lib/public/SetupCheck/ISetupCheck.php @@ -0,0 +1,53 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2022 Carl Schwan <carl@carlschwan.eu> + * + * @author Carl Schwan <carl@carlschwan.eu> + * @author Côme Chilliet <come.chilliet@nextcloud.com> + * + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCP\SetupCheck; + +/** + * This interface needs to be implemented if you want to provide custom + * setup checks in your application. The results of these checks will them + * be displayed in the admin overview. + * + * @since 28.0.0 + */ +interface ISetupCheck { + /** + * @since 28.0.0 + * @return string Category id, one of security/system/accounts, or a custom one which will be merged in system + */ + public function getCategory(): string; + + /** + * @since 28.0.0 + * @return string Translated name to display to the user + */ + public function getName(): string; + + /** + * @since 28.0.0 + */ + public function run(): SetupResult; +} diff --git a/lib/public/SetupCheck/ISetupCheckManager.php b/lib/public/SetupCheck/ISetupCheckManager.php new file mode 100644 index 00000000000..4b963e7c6b8 --- /dev/null +++ b/lib/public/SetupCheck/ISetupCheckManager.php @@ -0,0 +1,37 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2023 Côme Chilliet <come.chilliet@nextcloud.com> + * + * @author Côme Chilliet <come.chilliet@nextcloud.com> + * + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCP\SetupCheck; + +/** + * @since 28.0.0 + */ +interface ISetupCheckManager { + /** + * @since 28.0.0 + * @return array<string,array<string,SetupResult>> Result of each check, first level key is category, second level key is title + */ + public function runAll(): array; +} diff --git a/lib/public/SetupCheck/SetupResult.php b/lib/public/SetupCheck/SetupResult.php new file mode 100644 index 00000000000..e4a7744178a --- /dev/null +++ b/lib/public/SetupCheck/SetupResult.php @@ -0,0 +1,156 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2022 Carl Schwan <carl@carlschwan.eu> + * + * @author Carl Schwan <carl@carlschwan.eu> + * @author Côme Chilliet <come.chilliet@nextcloud.com> + * + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCP\SetupCheck; + +/** + * @brief This class is used for storing the result of a setup check + * + * @since 28.0.0 + */ +class SetupResult implements \JsonSerializable { + public const SUCCESS = 'success'; + public const INFO = 'info'; + public const WARNING = 'warning'; + public const ERROR = 'error'; + + /** + * @param string $name Translated name to display to the user + */ + private ?string $name = null; + + /** + * @brief Private constructor, use success()/info()/warning()/error() instead + * @param self::SUCCESS|self::INFO|self::WARNING|self::ERROR $severity + * @since 28.0.0 + */ + private function __construct( + private string $severity, + private ?string $description = null, + private ?string $linkToDoc = null, + ) { + } + + /** + * @brief Create a success result object + * @param ?string $description Translated detailed description to display to the user + * @param ?string $linkToDoc URI of related relevent documentation, be it from Nextcloud or another project + * @since 28.0.0 + */ + public static function success(?string $description = null, ?string $linkToDoc = null): self { + return new self(self::SUCCESS, $description, $linkToDoc); + } + + /** + * @brief Create an info result object + * @param ?string $description Translated detailed description to display to the user + * @param ?string $linkToDoc URI of related relevent documentation, be it from Nextcloud or another project + * @since 28.0.0 + */ + public static function info(?string $description = null, ?string $linkToDoc = null): self { + return new self(self::INFO, $description, $linkToDoc); + } + + /** + * @brief Create a warning result object + * @param ?string $description Translated detailed description to display to the user + * @param ?string $linkToDoc URI of related relevent documentation, be it from Nextcloud or another project + * @since 28.0.0 + */ + public static function warning(?string $description = null, ?string $linkToDoc = null): self { + return new self(self::WARNING, $description, $linkToDoc); + } + + /** + * @brief Create an error result object + * @param ?string $description Translated detailed description to display to the user + * @param ?string $linkToDoc URI of related relevent documentation, be it from Nextcloud or another project + * @since 28.0.0 + */ + public static function error(?string $description = null, ?string $linkToDoc = null): self { + return new self(self::ERROR, $description, $linkToDoc); + } + + /** + * @brief Get the severity for the setup check result + * + * @return self::SUCCESS|self::INFO|self::WARNING|self::ERROR + * @since 28.0.0 + */ + public function getSeverity(): string { + return $this->severity; + } + + /** + * @brief Get the description for the setup check result + * + * @since 28.0.0 + */ + public function getDescription(): ?string { + return $this->description; + } + + /** + * @brief Get the name for the setup check + * + * @since 28.0.0 + */ + public function getName(): ?string { + return $this->name; + } + + /** + * @brief Set the name from the setup check + * + * @since 28.0.0 + */ + public function setName(string $name): void { + $this->name = $name; + } + + /** + * @brief Get a link to the doc for the explanation. + * + * @since 28.0.0 + */ + public function getLinkToDoc(): ?string { + return $this->linkToDoc; + } + + /** + * @brief Get an array representation of the result for API responses + * + * @since 28.0.0 + */ + public function jsonSerialize(): array { + return [ + 'name' => $this->name, + 'severity' => $this->severity, + 'description' => $this->description, + 'linkToDoc' => $this->linkToDoc, + ]; + } +} diff --git a/lib/public/Share/IShare.php b/lib/public/Share/IShare.php index 40548c6c73d..e5a943b0bac 100644 --- a/lib/public/Share/IShare.php +++ b/lib/public/Share/IShare.php @@ -394,7 +394,7 @@ interface IShare { /** * Get the expiration date * - * @return \DateTime + * @return null|\DateTime * @since 9.0.0 */ public function getExpirationDate(); diff --git a/lib/public/TextProcessing/Exception/TaskFailureException.php b/lib/public/TextProcessing/Exception/TaskFailureException.php new file mode 100644 index 00000000000..300864711e7 --- /dev/null +++ b/lib/public/TextProcessing/Exception/TaskFailureException.php @@ -0,0 +1,10 @@ +<?php + +namespace OCP\TextProcessing\Exception; + +/** + * Exception thrown when a task failed + * @since 28.0.0 + */ +class TaskFailureException extends \RuntimeException { +} diff --git a/lib/public/TextProcessing/IManager.php b/lib/public/TextProcessing/IManager.php index dec0baba4bb..ff1222b094d 100644 --- a/lib/public/TextProcessing/IManager.php +++ b/lib/public/TextProcessing/IManager.php @@ -27,7 +27,9 @@ declare(strict_types=1); namespace OCP\TextProcessing; use OCP\Common\Exception\NotFoundException; +use OCP\DB\Exception; use OCP\PreConditionNotMetException; +use OCP\TextProcessing\Exception\TaskFailureException; use RuntimeException; /** @@ -56,7 +58,7 @@ interface IManager { /** * @param Task $task The task to run * @throws PreConditionNotMetException If no or not the requested provider was registered but this method was still called - * @throws RuntimeException If something else failed + * @throws TaskFailureException If running the task failed * @since 27.1.0 */ public function runTask(Task $task): string; @@ -68,11 +70,26 @@ interface IManager { * * @param Task $task The task to schedule * @throws PreConditionNotMetException If no or not the requested provider was registered but this method was still called + * @throws Exception storing the task in the database failed * @since 27.1.0 */ public function scheduleTask(Task $task) : void; /** + * If the designated provider for the passed task provides an expected average runtime, we check if the runtime fits into the + * max execution time of this php process and run it synchronously if it does, if it doesn't fit (or the provider doesn't provide that information) + * execution is deferred to a background job + * + * @param Task $task The task to schedule + * @returns bool A boolean indicating whether the task was run synchronously (`true`) or offloaded to a background job (`false`) + * @throws PreConditionNotMetException If no or not the requested provider was registered but this method was still called + * @throws TaskFailureException If running the task failed + * @throws Exception storing the task in the database failed + * @since 28.0.0 + */ + public function runOrScheduleTask(Task $task): bool; + + /** * Delete a task that has been scheduled before * * @param Task $task The task to delete diff --git a/lib/public/TextProcessing/IProviderWithExpectedRuntime.php b/lib/public/TextProcessing/IProviderWithExpectedRuntime.php new file mode 100644 index 00000000000..17767fc02d4 --- /dev/null +++ b/lib/public/TextProcessing/IProviderWithExpectedRuntime.php @@ -0,0 +1,41 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> + * + * @author Marcel Klehr <mklehr@gmx.net> + * + * @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 <http://www.gnu.org/licenses/>. + */ + + +namespace OCP\TextProcessing; + +/** + * This interface allows the system to learn the provider's expected runtime + * @since 28.0.0 + * @template T of ITaskType + * @template-extends IProvider<T> + */ +interface IProviderWithExpectedRuntime extends IProvider { + /** + * @return int The expected average runtime of a task in seconds + * @since 28.0.0 + */ + public function getExpectedRuntime(): int; +} diff --git a/lib/public/TextProcessing/IProviderWithUserId.php b/lib/public/TextProcessing/IProviderWithUserId.php new file mode 100644 index 00000000000..0a01a4c56c4 --- /dev/null +++ b/lib/public/TextProcessing/IProviderWithUserId.php @@ -0,0 +1,41 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> + * + * @author Marcel Klehr <mklehr@gmx.net> + * + * @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 <http://www.gnu.org/licenses/>. + */ + + +namespace OCP\TextProcessing; + +/** + * This interface allows providers to access the user that initiated the task being run. + * @since 28.0.0 + * @template T of ITaskType + * @template-extends IProvider<T> + */ +interface IProviderWithUserId extends IProvider { + /** + * @param ?string $userId the current user's id + * @since 28.0.0 + */ + public function setUserId(?string $userId): void; +} diff --git a/lib/public/TextProcessing/Task.php b/lib/public/TextProcessing/Task.php index 446e414cb04..25b7132ee31 100644 --- a/lib/public/TextProcessing/Task.php +++ b/lib/public/TextProcessing/Task.php @@ -35,6 +35,7 @@ namespace OCP\TextProcessing; final class Task implements \JsonSerializable { protected ?int $id = null; protected ?string $output = null; + private ?\DateTime $completionExpectedAt = null; /** * @since 27.1.0 @@ -98,6 +99,9 @@ final class Task implements \JsonSerializable { */ public function visitProvider(IProvider $provider): string { if ($this->canUseProvider($provider)) { + if ($provider instanceof IProviderWithUserId) { + $provider->setUserId($this->getUserId()); + } return $provider->process($this->getInput()); } else { throw new \RuntimeException('Task of type ' . $this->getType() . ' cannot visit provider with task type ' . $provider->getTaskType()); @@ -203,7 +207,7 @@ final class Task implements \JsonSerializable { } /** - * @psalm-return array{id: ?int, type: S, status: 0|1|2|3|4, userId: ?string, appId: string, input: string, output: ?string, identifier: string} + * @psalm-return array{id: ?int, type: S, status: 0|1|2|3|4, userId: ?string, appId: string, input: string, output: ?string, identifier: string, completionExpectedAt: ?int} * @since 27.1.0 */ public function jsonSerialize(): array { @@ -216,6 +220,24 @@ final class Task implements \JsonSerializable { 'input' => $this->getInput(), 'output' => $this->getOutput(), 'identifier' => $this->getIdentifier(), + 'completionExpectedAt' => $this->getCompletionExpectedAt()?->getTimestamp(), ]; } + + /** + * @param null|\DateTime $completionExpectedAt + * @return void + * @since 28.0.0 + */ + final public function setCompletionExpectedAt(?\DateTime $completionExpectedAt): void { + $this->completionExpectedAt = $completionExpectedAt; + } + + /** + * @return \DateTime|null + * @since 28.0.0 + */ + final public function getCompletionExpectedAt(): ?\DateTime { + return $this->completionExpectedAt; + } } diff --git a/lib/public/TextToImage/Events/AbstractTextToImageEvent.php b/lib/public/TextToImage/Events/AbstractTextToImageEvent.php new file mode 100644 index 00000000000..56c68195602 --- /dev/null +++ b/lib/public/TextToImage/Events/AbstractTextToImageEvent.php @@ -0,0 +1,52 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> + * + * @author Marcel Klehr <mklehr@gmx.net> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\TextToImage\Events; + +use OCP\EventDispatcher\Event; +use OCP\TextToImage\Task; + +/** + * @since 28.0.0 + */ +abstract class AbstractTextToImageEvent extends Event { + /** + * @since 28.0.0 + */ + public function __construct( + private Task $task + ) { + parent::__construct(); + } + + /** + * @return Task + * @since 28.0.0 + */ + public function getTask(): Task { + return $this->task; + } +} diff --git a/lib/public/TextToImage/Events/TaskFailedEvent.php b/lib/public/TextToImage/Events/TaskFailedEvent.php new file mode 100644 index 00000000000..0d91b3a4f67 --- /dev/null +++ b/lib/public/TextToImage/Events/TaskFailedEvent.php @@ -0,0 +1,54 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> + * + * @author Marcel Klehr <mklehr@gmx.net> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\TextToImage\Events; + +use OCP\TextToImage\Task; + +/** + * @since 28.0.0 + */ +class TaskFailedEvent extends AbstractTextToImageEvent { + /** + * @param Task $task + * @param string $errorMessage + * @since 28.0.0 + */ + public function __construct( + Task $task, + private string $errorMessage, + ) { + parent::__construct($task); + } + + /** + * @return string + * @since 28.0.0 + */ + public function getErrorMessage(): string { + return $this->errorMessage; + } +} diff --git a/lib/public/TextToImage/Events/TaskSuccessfulEvent.php b/lib/public/TextToImage/Events/TaskSuccessfulEvent.php new file mode 100644 index 00000000000..15d263c0ff0 --- /dev/null +++ b/lib/public/TextToImage/Events/TaskSuccessfulEvent.php @@ -0,0 +1,33 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> + * + * @author Marcel Klehr <mklehr@gmx.net> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\TextToImage\Events; + +/** + * @since 28.0.0 + */ +class TaskSuccessfulEvent extends AbstractTextToImageEvent { +} diff --git a/lib/public/TextToImage/Exception/TaskFailureException.php b/lib/public/TextToImage/Exception/TaskFailureException.php new file mode 100644 index 00000000000..a640fdff2e8 --- /dev/null +++ b/lib/public/TextToImage/Exception/TaskFailureException.php @@ -0,0 +1,31 @@ +<?php + +/** + * @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> + * + * @author Marcel Klehr <mklehr@gmx.net> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\TextToImage\Exception; + +/** + * @since 28.0.0 + */ +class TaskFailureException extends TextToImageException { +} diff --git a/lib/public/TextToImage/Exception/TaskNotFoundException.php b/lib/public/TextToImage/Exception/TaskNotFoundException.php new file mode 100644 index 00000000000..bd713fe3905 --- /dev/null +++ b/lib/public/TextToImage/Exception/TaskNotFoundException.php @@ -0,0 +1,31 @@ +<?php + +/** + * @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> + * + * @author Marcel Klehr <mklehr@gmx.net> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\TextToImage\Exception; + +/** + * @since 28.0.0 + */ +class TaskNotFoundException extends TextToImageException { +} diff --git a/lib/public/TextToImage/Exception/TextToImageException.php b/lib/public/TextToImage/Exception/TextToImageException.php new file mode 100644 index 00000000000..3d4fd192c94 --- /dev/null +++ b/lib/public/TextToImage/Exception/TextToImageException.php @@ -0,0 +1,31 @@ +<?php + +/** + * @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> + * + * @author Marcel Klehr <mklehr@gmx.net> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\TextToImage\Exception; + +/** + * @since 28.0.0 + */ +class TextToImageException extends \Exception { +} diff --git a/lib/public/TextToImage/IManager.php b/lib/public/TextToImage/IManager.php new file mode 100644 index 00000000000..30b88217690 --- /dev/null +++ b/lib/public/TextToImage/IManager.php @@ -0,0 +1,116 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> + * + * @author Marcel Klehr <mklehr@gmx.net> + * + * @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 <http://www.gnu.org/licenses/>. + */ + + +namespace OCP\TextToImage; + +use OCP\DB\Exception; +use OCP\PreConditionNotMetException; +use OCP\TextToImage\Exception\TaskFailureException; +use OCP\TextToImage\Exception\TaskNotFoundException; +use RuntimeException; + +/** + * API surface for apps interacting with and making use of TextToImage providers + * without knowing which providers are installed + * @since 28.0.0 + */ +interface IManager { + /** + * @since 28.0.0 + */ + public function hasProviders(): bool; + + /** + * @since 28.0.0 + * @return IProvider[] + */ + public function getProviders(): array; + + /** + * @param Task $task The task to run + * @throws PreConditionNotMetException If no or not the requested provider was registered but this method was still called + * @throws TaskFailureException If something else failed. When this is thrown task status was already set to failure. + * @since 28.0.0 + */ + public function runTask(Task $task): void; + + /** + * Will schedule a TextToImage process in the background. The result will become available + * with the \OCP\TextToImage\TaskSuccessfulEvent + * If inference fails a \OCP\TextToImage\Events\TaskFailedEvent will be dispatched instead + * + * @param Task $task The task to schedule + * @throws PreConditionNotMetException If no provider was registered but this method was still called + * @throws Exception If there was a problem inserting the task into the database + * @since 28.0.0 + */ + public function scheduleTask(Task $task) : void; + + /** + * @throws Exception if there was a problem inserting the task into the database + * @throws PreConditionNotMetException if no provider is registered + * @throws TaskFailureException If the task run failed + * @since 28.0.0 + */ + public function runOrScheduleTask(Task $task) : void; + + /** + * Delete a task that has been scheduled before + * + * @param Task $task The task to delete + * @since 28.0.0 + */ + public function deleteTask(Task $task): void; + + /** + * @param int $id The id of the task + * @return Task + * @throws RuntimeException If the query failed + * @throws TaskNotFoundException If the task could not be found + * @since 28.0.0 + */ + public function getTask(int $id): Task; + + /** + * @param int $id The id of the task + * @param string|null $userId The user id that scheduled the task + * @return Task + * @throws RuntimeException If the query failed + * @throws TaskNotFoundException If the task could not be found + * @since 28.0.0 + */ + public function getUserTask(int $id, ?string $userId): Task; + + /** + * @param ?string $userId + * @param string $appId + * @param string|null $identifier + * @return Task[] + * @since 28.0.0 + * @throws RuntimeException If the query failed + */ + public function getUserTasksByApp(?string $userId, string $appId, ?string $identifier = null): array; +} diff --git a/lib/public/TextToImage/IProvider.php b/lib/public/TextToImage/IProvider.php new file mode 100644 index 00000000000..9f2ff51f599 --- /dev/null +++ b/lib/public/TextToImage/IProvider.php @@ -0,0 +1,64 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> + * + * @author Marcel Klehr <mklehr@gmx.net> + * + * @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 <http://www.gnu.org/licenses/>. + */ + +namespace OCP\TextToImage; + +use RuntimeException; + +/** + * This is the interface that is implemented by apps that + * implement a text to image provider + * @since 28.0.0 + */ +interface IProvider { + /** + * An arbitrary unique text string identifying this provider + * @since 28.0.0 + */ + public function getId(): string; + + /** + * The localized name of this provider + * @since 28.0.0 + */ + public function getName(): string; + + /** + * Processes a text + * + * @param string $prompt The input text + * @param resource[] $resources The file resources to write the images to + * @return void + * @since 28.0.0 + * @throws RuntimeException If the text could not be processed + */ + public function generate(string $prompt, array $resources): void; + + /** + * The expected runtime for one task with this provider in seconds + * @since 28.0.0 + */ + public function getExpectedRuntime(): int; +} diff --git a/lib/public/TextToImage/Task.php b/lib/public/TextToImage/Task.php new file mode 100644 index 00000000000..e610af6aa96 --- /dev/null +++ b/lib/public/TextToImage/Task.php @@ -0,0 +1,212 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> + * + * @author Marcel Klehr <mklehr@gmx.net> + * + * @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 <http://www.gnu.org/licenses/>. + */ + +namespace OCP\TextToImage; + +use DateTime; +use OCP\Files\AppData\IAppDataFactory; +use OCP\Files\NotFoundException; +use OCP\Files\NotPermittedException; +use OCP\IImage; +use OCP\Image; + +/** + * This is a text to image task + * + * @since 28.0.0 + */ +final class Task implements \JsonSerializable { + protected ?int $id = null; + + protected ?DateTime $completionExpectedAt = null; + + /** + * @since 28.0.0 + */ + public const STATUS_FAILED = 4; + /** + * @since 28.0.0 + */ + public const STATUS_SUCCESSFUL = 3; + /** + * @since 28.0.0 + */ + public const STATUS_RUNNING = 2; + /** + * @since 28.0.0 + */ + public const STATUS_SCHEDULED = 1; + /** + * @since 28.0.0 + */ + public const STATUS_UNKNOWN = 0; + + /** + * @psalm-var self::STATUS_* + */ + protected int $status = self::STATUS_UNKNOWN; + + /** + * @param string $input + * @param string $appId + * @param int $numberOfImages + * @param string|null $userId + * @param null|string $identifier An arbitrary identifier for this task. max length: 255 chars + * @since 28.0.0 + */ + final public function __construct( + protected string $input, + protected string $appId, + protected int $numberOfImages, + protected ?string $userId, + protected ?string $identifier = '', + ) { + } + + /** + * @return IImage[]|null + * @since 28.0.0 + */ + final public function getOutputImages(): ?array { + $appData = \OCP\Server::get(IAppDataFactory::class)->get('core'); + try { + $folder = $appData->getFolder('text2image')->getFolder((string)$this->getId()); + $images = []; + for ($i = 0; $i < $this->getNumberOfImages(); $i++) { + $image = new Image(); + $image->loadFromFileHandle($folder->getFile((string) $i)->read()); + $images[] = $image; + } + return $images; + } catch (NotFoundException|NotPermittedException) { + return null; + } + } + + /** + * @return int + * @since 28.0.0 + */ + final public function getNumberOfImages(): int { + return $this->numberOfImages; + } + + /** + * @psalm-return self::STATUS_* + * @since 28.0.0 + */ + final public function getStatus(): int { + return $this->status; + } + + /** + * @psalm-param self::STATUS_* $status + * @since 28.0.0 + */ + final public function setStatus(int $status): void { + $this->status = $status; + } + + /** + * @param ?DateTime $at + * @since 28.0.0 + */ + final public function setCompletionExpectedAt(?DateTime $at): void { + $this->completionExpectedAt = $at; + } + + /** + * @return ?DateTime + * @since 28.0.0 + */ + final public function getCompletionExpectedAt(): ?DateTime { + return $this->completionExpectedAt; + } + + /** + * @return int|null + * @since 28.0.0 + */ + final public function getId(): ?int { + return $this->id; + } + + /** + * @param int|null $id + * @since 28.0.0 + */ + final public function setId(?int $id): void { + $this->id = $id; + } + + /** + * @return string + * @since 28.0.0 + */ + final public function getInput(): string { + return $this->input; + } + + /** + * @return string + * @since 28.0.0 + */ + final public function getAppId(): string { + return $this->appId; + } + + /** + * @return null|string + * @since 28.0.0 + */ + final public function getIdentifier(): ?string { + return $this->identifier; + } + + /** + * @return string|null + * @since 28.0.0 + */ + final public function getUserId(): ?string { + return $this->userId; + } + + /** + * @psalm-return array{id: ?int, status: self::STATUS_*, userId: ?string, appId: string, input: string, identifier: ?string, numberOfImages: int, completionExpectedAt: ?int} + * @since 28.0.0 + */ + public function jsonSerialize(): array { + return [ + 'id' => $this->getId(), + 'status' => $this->getStatus(), + 'userId' => $this->getUserId(), + 'appId' => $this->getAppId(), + 'numberOfImages' => $this->getNumberOfImages(), + 'input' => $this->getInput(), + 'identifier' => $this->getIdentifier(), + 'completionExpectedAt' => $this->getCompletionExpectedAt()->getTimestamp(), + ]; + } +} diff --git a/lib/public/User/Events/OutOfOfficeChangedEvent.php b/lib/public/User/Events/OutOfOfficeChangedEvent.php new file mode 100644 index 00000000000..5e5753b7202 --- /dev/null +++ b/lib/public/User/Events/OutOfOfficeChangedEvent.php @@ -0,0 +1,50 @@ +<?php + +declare(strict_types=1); + +/* + * @copyright 2023 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2023 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @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 <http://www.gnu.org/licenses/>. + */ + +namespace OCP\User\Events; + +use OCP\EventDispatcher\Event; +use OCP\User\IOutOfOfficeData; + +/** + * Emitted when a user's out-of-office period has changed + * + * @since 28.0.0 + */ +class OutOfOfficeChangedEvent extends Event { + /** + * @since 28.0.0 + */ + public function __construct(private IOutOfOfficeData $data) { + parent::__construct(); + } + + /** + * @since 28.0.0 + */ + public function getData(): IOutOfOfficeData { + return $this->data; + } +} diff --git a/lib/public/User/Events/OutOfOfficeClearedEvent.php b/lib/public/User/Events/OutOfOfficeClearedEvent.php new file mode 100644 index 00000000000..48a77c77023 --- /dev/null +++ b/lib/public/User/Events/OutOfOfficeClearedEvent.php @@ -0,0 +1,50 @@ +<?php + +declare(strict_types=1); + +/* + * @copyright 2023 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2023 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @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 <http://www.gnu.org/licenses/>. + */ + +namespace OCP\User\Events; + +use OCP\EventDispatcher\Event; +use OCP\User\IOutOfOfficeData; + +/** + * Emitted when a user's out-of-office period is cleared + * + * @since 28.0.0 + */ +class OutOfOfficeClearedEvent extends Event { + /** + * @since 28.0.0 + */ + public function __construct(private IOutOfOfficeData $data) { + parent::__construct(); + } + + /** + * @since 28.0.0 + */ + public function getData(): IOutOfOfficeData { + return $this->data; + } +} diff --git a/lib/public/User/Events/OutOfOfficeScheduledEvent.php b/lib/public/User/Events/OutOfOfficeScheduledEvent.php new file mode 100644 index 00000000000..2bcbec63478 --- /dev/null +++ b/lib/public/User/Events/OutOfOfficeScheduledEvent.php @@ -0,0 +1,50 @@ +<?php + +declare(strict_types=1); + +/* + * @copyright 2023 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2023 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @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 <http://www.gnu.org/licenses/>. + */ + +namespace OCP\User\Events; + +use OCP\EventDispatcher\Event; +use OCP\User\IOutOfOfficeData; + +/** + * Emitted when a user's out-of-office period is scheduled + * + * @since 28.0.0 + */ +class OutOfOfficeScheduledEvent extends Event { + /** + * @since 28.0.0 + */ + public function __construct(private IOutOfOfficeData $data) { + parent::__construct(); + } + + /** + * @since 28.0.0 + */ + public function getData(): IOutOfOfficeData { + return $this->data; + } +} diff --git a/lib/public/User/IAvailabilityCoordinator.php b/lib/public/User/IAvailabilityCoordinator.php new file mode 100644 index 00000000000..749241f13bc --- /dev/null +++ b/lib/public/User/IAvailabilityCoordinator.php @@ -0,0 +1,51 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright 2023 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2023 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @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 <http://www.gnu.org/licenses/>. + */ + +namespace OCP\User; + +use OCP\IUser; + +/** + * Coordinator for availability and out-of-office messages + * + * @since 28.0.0 + */ +interface IAvailabilityCoordinator { + /** + * Check if the feature is enabled on this instance + * + * @return bool + * + * @since 28.0.0 + */ + public function isEnabled(): bool; + + /** + * Get the user's out-of-office message, if any + * + * @since 28.0.0 + */ + public function getCurrentOutOfOfficeData(IUser $user): ?IOutOfOfficeData; +} diff --git a/lib/public/User/IOutOfOfficeData.php b/lib/public/User/IOutOfOfficeData.php new file mode 100644 index 00000000000..03444449d58 --- /dev/null +++ b/lib/public/User/IOutOfOfficeData.php @@ -0,0 +1,77 @@ +<?php + +declare(strict_types=1); + +/* + * @copyright 2023 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2023 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @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 <http://www.gnu.org/licenses/>. + */ + +namespace OCP\User; + +use OCP\IUser; + +/** + * DTO to hold out-of-office information of a user + * + * @since 28.0.0 + */ +interface IOutOfOfficeData { + /** + * Get the unique token assigned to the current out-of-office event + * + * @since 28.0.0 + */ + public function getId(): string; + + /** + * @since 28.0.0 + */ + public function getUser(): IUser; + + /** + * Get the accurate out-of-office start date + * + * This event is not guaranteed to be emitted exactly at start date + * + * @since 28.0.0 + */ + public function getStartDate(): int; + + /** + * Get the (preliminary) out-of-office end date + * + * @since 28.0.0 + */ + public function getEndDate(): int; + + /** + * Get the short summary text displayed in the user status and similar + * + * @since 28.0.0 + */ + public function getShortMessage(): string; + + /** + * Get the long out-of-office message for auto responders and similar + * + * @since 28.0.0 + */ + public function getMessage(): string; +} diff --git a/lib/public/UserStatus/IUserStatus.php b/lib/public/UserStatus/IUserStatus.php index 74c54cc9da2..c96d07d298b 100644 --- a/lib/public/UserStatus/IUserStatus.php +++ b/lib/public/UserStatus/IUserStatus.php @@ -53,6 +53,12 @@ interface IUserStatus { /** * @var string + * @since 28.0.0 + */ + public const BUSY = 'busy'; + + /** + * @var string * @since 20.0.0 */ public const OFFLINE = 'offline'; @@ -76,6 +82,18 @@ interface IUserStatus { public const MESSAGE_AVAILABILITY = 'availability'; /** + * @var string + * @since 28.0.0 + */ + public const MESSAGE_CALENDAR_BUSY = 'meeting'; + + /** + * @var string + * @since 28.0.0 + */ + public const MESSAGE_CALENDAR_BUSY_TENTATIVE = 'busy-tentative'; + + /** * Get the user this status is connected to * * @return string diff --git a/lib/public/Util.php b/lib/public/Util.php index bff8038b3dd..cabb84c0cf6 100644 --- a/lib/public/Util.php +++ b/lib/public/Util.php @@ -49,6 +49,7 @@ namespace OCP; use OC\AppScriptDependency; use OC\AppScriptSort; use bantu\IniGetWrapper\IniGetWrapper; +use OCP\Share\IManager; use Psr\Container\ContainerExceptionInterface; /** @@ -57,17 +58,11 @@ use Psr\Container\ContainerExceptionInterface; * @since 4.0.0 */ class Util { - /** @var \OCP\Share\IManager */ - private static $shareManager; + private static ?IManager $shareManager = null; - /** @var array */ - private static $scripts = []; - - /** @var array */ - private static $scriptDeps = []; - - /** @var array */ - private static $sortedScriptDeps = []; + private static array $scriptsInit = []; + private static array $scripts = []; + private static array $scriptDeps = []; /** * get the current installed version of Nextcloud @@ -110,19 +105,6 @@ class Util { } /** - * write a message in the log - * @param string $app - * @param string $message - * @param int $level - * @since 4.0.0 - * @deprecated 13.0.0 use log of \OCP\ILogger - */ - public static function writeLog($app, $message, $level) { - $context = ['app' => $app]; - \OC::$server->getLogger()->log($level, $message, $context); - } - - /** * check if sharing is disabled for the current user * * @return boolean @@ -164,6 +146,25 @@ class Util { } /** + * Add a standalone init js file that is loaded for initialization + * + * Be careful loading scripts using this method as they are loaded early + * and block the initial page rendering. They should not have dependencies + * on any other scripts than core-common and core-main. + * + * @since 28.0.0 + */ + public static function addInitScript(string $application, string $file): void { + if (!empty($application)) { + $path = "$application/js/$file"; + } else { + $path = "js/$file"; + } + + self::$scriptsInit[] = $path; + } + + /** * add a javascript file * * @param string $application @@ -214,7 +215,8 @@ class Util { $sortedScripts = $scriptSort->sort(self::$scripts, self::$scriptDeps); // Flatten array and remove duplicates - $sortedScripts = $sortedScripts ? array_merge(...array_values(($sortedScripts))) : []; + $sortedScripts = array_merge([self::$scriptsInit], $sortedScripts); + $sortedScripts = array_merge(...array_values($sortedScripts)); // Override core-common and core-main order if (in_array('core/js/main', $sortedScripts)) { |