diff options
author | John Molakvoæ <skjnldsv@users.noreply.github.com> | 2024-03-15 13:03:34 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-03-15 13:03:34 +0100 |
commit | 9338ef36ded767f2c35b7ec575b351859420ed09 (patch) | |
tree | 65c53c6a36f300859dc22b2d423275bcf2911367 /lib/public | |
parent | 6b09a79227a5dc98aa4620c6e5e15b610a06c806 (diff) | |
parent | df1cd1ba7e6e1f6e66a2b3229b5c082f1b81162e (diff) | |
download | nextcloud-server-9338ef36ded767f2c35b7ec575b351859420ed09.tar.gz nextcloud-server-9338ef36ded767f2c35b7ec575b351859420ed09.zip |
Merge branch 'master' into refactor/OC-Server-getShareManager
Signed-off-by: John Molakvoæ <skjnldsv@users.noreply.github.com>
Diffstat (limited to 'lib/public')
219 files changed, 8712 insertions, 394 deletions
diff --git a/lib/public/Accounts/IAccountManager.php b/lib/public/Accounts/IAccountManager.php index 68eca469ad9..fbf2d831513 100644 --- a/lib/public/Accounts/IAccountManager.php +++ b/lib/public/Accounts/IAccountManager.php @@ -72,6 +72,7 @@ interface IAccountManager { /** * Contact details only visible locally * + * @since 15.0.0 * @deprecated 21.0.1 */ public const VISIBILITY_PRIVATE = 'private'; @@ -79,6 +80,7 @@ interface IAccountManager { /** * Contact details visible on trusted federated servers. * + * @since 15.0.0 * @deprecated 21.0.1 */ public const VISIBILITY_CONTACTS_ONLY = 'contacts'; @@ -86,6 +88,7 @@ interface IAccountManager { /** * Contact details visible on trusted federated servers and in the public lookup server. * + * @since 15.0.0 * @deprecated 21.0.1 */ public const VISIBILITY_PUBLIC = 'public'; @@ -105,14 +108,49 @@ interface IAccountManager { self::VISIBILITY_PUBLIC, ]; + /** + * @since 15.0.0 + */ public const PROPERTY_AVATAR = 'avatar'; + + /** + * @since 15.0.0 + */ public const PROPERTY_DISPLAYNAME = 'displayname'; + + /** + * @since 27.0.0 + */ public const PROPERTY_DISPLAYNAME_LEGACY = 'display-name'; + + /** + * @since 15.0.0 + */ public const PROPERTY_PHONE = 'phone'; + + /** + * @since 15.0.0 + */ public const PROPERTY_EMAIL = 'email'; + + /** + * @since 15.0.0 + */ public const PROPERTY_WEBSITE = 'website'; + + /** + * @since 15.0.0 + */ public const PROPERTY_ADDRESS = 'address'; + + /** + * @since 15.0.0 + */ public const PROPERTY_TWITTER = 'twitter'; + + /** + * @since 26.0.0 + */ public const PROPERTY_FEDIVERSE = 'fediverse'; /** @@ -161,10 +199,25 @@ interface IAccountManager { self::PROPERTY_PROFILE_ENABLED, ]; + + /** + * @since 22.0.0 + */ public const COLLECTION_EMAIL = 'additional_mail'; + /** + * @since 15.0.0 + */ public const NOT_VERIFIED = '0'; + + /** + * @since 15.0.0 + */ public const VERIFICATION_IN_PROGRESS = '1'; + + /** + * @since 15.0.0 + */ public const VERIFIED = '2'; /** diff --git a/lib/public/Activity/IExtension.php b/lib/public/Activity/IExtension.php index e02347f0373..23d96380c87 100644 --- a/lib/public/Activity/IExtension.php +++ b/lib/public/Activity/IExtension.php @@ -31,13 +31,43 @@ namespace OCP\Activity; * @since 8.0.0 */ interface IExtension { + /** + * @since 8.0.0 + */ public const METHOD_STREAM = 'stream'; + + /** + * @since 8.0.0 + */ public const METHOD_MAIL = 'email'; + + /** + * @since 20.0.0 + */ public const METHOD_NOTIFICATION = 'notification'; + /** + * @since 8.0.0 + */ public const PRIORITY_VERYLOW = 10; + + /** + * @since 8.0.0 + */ public const PRIORITY_LOW = 20; + + /** + * @since 8.0.0 + */ public const PRIORITY_MEDIUM = 30; + + /** + * @since 8.0.0 + */ public const PRIORITY_HIGH = 40; + + /** + * @since 8.0.0 + */ public const PRIORITY_VERYHIGH = 50; } diff --git a/lib/public/App/IAppManager.php b/lib/public/App/IAppManager.php index 2497544dcbe..142e8bb8515 100644 --- a/lib/public/App/IAppManager.php +++ b/lib/public/App/IAppManager.php @@ -45,8 +45,8 @@ interface IAppManager { /** * Returns the app information from "appinfo/info.xml". * - * @param string $appId - * @return mixed + * @param string|null $lang + * @return array|null * @since 14.0.0 */ public function getAppInfo(string $appId, bool $path = false, $lang = null); @@ -62,10 +62,20 @@ interface IAppManager { public function getAppVersion(string $appId, bool $useCache = true): string; /** + * Returns the app icon or null if none is found + * + * @param string $appId + * @param bool $dark Enable to request a dark icon variant, default is a white icon + * @return string|null + * @since 29.0.0 + */ + public function getAppIcon(string $appId, bool $dark = false): string|null; + + /** * Check if an app is enabled for user * * @param string $appId - * @param \OCP\IUser $user (optional) if not defined, the currently loggedin user will be used + * @param \OCP\IUser|null $user (optional) if not defined, the currently loggedin user will be used * @return bool * @since 8.0.0 */ @@ -248,7 +258,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/App/ManagerEvent.php b/lib/public/App/ManagerEvent.php index 0069e57db42..0853ce7981d 100644 --- a/lib/public/App/ManagerEvent.php +++ b/lib/public/App/ManagerEvent.php @@ -32,16 +32,19 @@ use OCP\EventDispatcher\Event; */ class ManagerEvent extends Event { /** + * @since 9.0.0 * @deprecated 22.0.0 */ public const EVENT_APP_ENABLE = 'OCP\App\IAppManager::enableApp'; /** + * @since 9.0.0 * @deprecated 22.0.0 */ public const EVENT_APP_ENABLE_FOR_GROUPS = 'OCP\App\IAppManager::enableAppForGroups'; /** + * @since 9.0.0 * @deprecated 22.0.0 */ public const EVENT_APP_DISABLE = 'OCP\App\IAppManager::disableApp'; diff --git a/lib/public/AppFramework/ApiController.php b/lib/public/AppFramework/ApiController.php index 66c278e62d8..9505af0b2e2 100644 --- a/lib/public/AppFramework/ApiController.php +++ b/lib/public/AppFramework/ApiController.php @@ -52,10 +52,10 @@ abstract class ApiController extends Controller { * @since 7.0.0 */ public function __construct($appName, - IRequest $request, - $corsMethods = 'PUT, POST, GET, DELETE, PATCH', - $corsAllowedHeaders = 'Authorization, Content-Type, Accept', - $corsMaxAge = 1728000) { + IRequest $request, + $corsMethods = 'PUT, POST, GET, DELETE, PATCH', + $corsAllowedHeaders = 'Authorization, Content-Type, Accept', + $corsMaxAge = 1728000) { parent::__construct($appName, $request); $this->corsMethods = $corsMethods; $this->corsAllowedHeaders = $corsAllowedHeaders; diff --git a/lib/public/AppFramework/App.php b/lib/public/AppFramework/App.php index 4d6e9177b78..f62c464ea55 100644 --- a/lib/public/AppFramework/App.php +++ b/lib/public/AppFramework/App.php @@ -38,6 +38,7 @@ use OC\AppFramework\Routing\RouteConfig; use OC\Route\Router; use OC\ServerContainer; use OCP\Route\IRouter; +use Psr\Log\LoggerInterface; /** * Class App @@ -98,8 +99,9 @@ class App { } if (!$setUpViaQuery && $applicationClassName !== \OCP\AppFramework\App::class) { - \OC::$server->getLogger()->logException($e, [ + \OCP\Server::get(LoggerInterface::class)->error($e->getMessage(), [ 'app' => $appName, + 'exception' => $e, ]); } } diff --git a/lib/public/AppFramework/AuthPublicShareController.php b/lib/public/AppFramework/AuthPublicShareController.php index 78dd45551ed..847f1823db8 100644 --- a/lib/public/AppFramework/AuthPublicShareController.php +++ b/lib/public/AppFramework/AuthPublicShareController.php @@ -57,9 +57,9 @@ abstract class AuthPublicShareController extends PublicShareController { * @since 14.0.0 */ public function __construct(string $appName, - IRequest $request, - ISession $session, - IURLGenerator $urlGenerator) { + IRequest $request, + ISession $session, + IURLGenerator $urlGenerator) { parent::__construct($appName, $request, $session); $this->urlGenerator = $urlGenerator; diff --git a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php index 720803a78d1..09bc703e0a4 100644 --- a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php +++ b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php @@ -37,10 +37,11 @@ use OCP\Collaboration\Reference\IReferenceProvider; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\Template\ICustomTemplateProvider; use OCP\IContainer; -use OCP\TextProcessing\IProvider as ITextProcessingProvider; use OCP\Notification\INotifier; use OCP\Preview\IProviderV2; use OCP\SpeechToText\ISpeechToTextProvider; +use OCP\TextProcessing\IProvider as ITextProcessingProvider; +use OCP\TextToImage\IProvider as ITextToImageProvider; use OCP\Translation\ITranslationProvider; /** @@ -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 * @@ -341,6 +352,14 @@ interface IRegistrationContext { public function registerCalendarRoomBackend(string $class): void; /** + * @param string $class + * @psalm-param class-string<\OCP\Calendar\Room\IBackend> $actionClass + * @return void + * @since 29.0.0 + */ + public function registerTeamResourceProvider(string $class): void; + + /** * Register an implementation of \OCP\UserMigration\IMigrator that * will handle the implementation of a migrator * @@ -371,4 +390,24 @@ 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; + + /** + * Register an implementation of \OCP\Settings\IDeclarativeSettings that + * will handle the implementation of declarative settings + * + * @param string $declarativeSettingsClass + * @psalm-param class-string<\OCP\Settings\IDeclarativeSettingsForm> $declarativeSettingsClass + * @return void + * @since 29.0.0 + */ + public function registerDeclarativeSettings(string $declarativeSettingsClass): void; } diff --git a/lib/public/AppFramework/Controller.php b/lib/public/AppFramework/Controller.php index e8500d5ae1a..12e1c31626b 100644 --- a/lib/public/AppFramework/Controller.php +++ b/lib/public/AppFramework/Controller.php @@ -65,7 +65,7 @@ abstract class Controller { * @since 6.0.0 - parameter $appName was added in 7.0.0 - parameter $app was removed in 7.0.0 */ public function __construct($appName, - IRequest $request) { + IRequest $request) { $this->appName = $appName; $this->request = $request; diff --git a/lib/public/AppFramework/Http.php b/lib/public/AppFramework/Http.php index 8b3cde6875e..64e7fb689e5 100644 --- a/lib/public/AppFramework/Http.php +++ b/lib/public/AppFramework/Http.php @@ -29,63 +29,298 @@ namespace OCP\AppFramework; * @since 6.0.0 */ class Http { + /** + * @since 6.0.0 + */ public const STATUS_CONTINUE = 100; + + /** + * @since 6.0.0 + */ public const STATUS_SWITCHING_PROTOCOLS = 101; + + /** + * @since 6.0.0 + */ public const STATUS_PROCESSING = 102; + + /** + * @since 6.0.0 + */ public const STATUS_OK = 200; + + /** + * @since 6.0.0 + */ public const STATUS_CREATED = 201; + + /** + * @since 6.0.0 + */ public const STATUS_ACCEPTED = 202; + + /** + * @since 6.0.0 + */ public const STATUS_NON_AUTHORATIVE_INFORMATION = 203; + + /** + * @since 6.0.0 + */ public const STATUS_NO_CONTENT = 204; + + /** + * @since 6.0.0 + */ public const STATUS_RESET_CONTENT = 205; + + /** + * @since 6.0.0 + */ public const STATUS_PARTIAL_CONTENT = 206; + + /** + * @since 6.0.0 + */ public const STATUS_MULTI_STATUS = 207; + + /** + * @since 6.0.0 + */ public const STATUS_ALREADY_REPORTED = 208; + + /** + * @since 6.0.0 + */ public const STATUS_IM_USED = 226; + + /** + * @since 6.0.0 + */ public const STATUS_MULTIPLE_CHOICES = 300; + + /** + * @since 6.0.0 + */ public const STATUS_MOVED_PERMANENTLY = 301; + + /** + * @since 6.0.0 + */ public const STATUS_FOUND = 302; + + /** + * @since 6.0.0 + */ public const STATUS_SEE_OTHER = 303; + + /** + * @since 6.0.0 + */ public const STATUS_NOT_MODIFIED = 304; + + /** + * @since 6.0.0 + */ public const STATUS_USE_PROXY = 305; + + /** + * @since 6.0.0 + */ public const STATUS_RESERVED = 306; + + /** + * @since 6.0.0 + */ public const STATUS_TEMPORARY_REDIRECT = 307; + + /** + * @since 6.0.0 + */ public const STATUS_BAD_REQUEST = 400; + + /** + * @since 6.0.0 + */ public const STATUS_UNAUTHORIZED = 401; + + /** + * @since 6.0.0 + */ public const STATUS_PAYMENT_REQUIRED = 402; + + /** + * @since 6.0.0 + */ public const STATUS_FORBIDDEN = 403; + + /** + * @since 6.0.0 + */ public const STATUS_NOT_FOUND = 404; + + /** + * @since 6.0.0 + */ public const STATUS_METHOD_NOT_ALLOWED = 405; + + /** + * @since 6.0.0 + */ public const STATUS_NOT_ACCEPTABLE = 406; + + /** + * @since 6.0.0 + */ public const STATUS_PROXY_AUTHENTICATION_REQUIRED = 407; + + /** + * @since 6.0.0 + */ public const STATUS_REQUEST_TIMEOUT = 408; + + /** + * @since 6.0.0 + */ public const STATUS_CONFLICT = 409; + + /** + * @since 6.0.0 + */ public const STATUS_GONE = 410; + + /** + * @since 6.0.0 + */ public const STATUS_LENGTH_REQUIRED = 411; + + /** + * @since 6.0.0 + */ public const STATUS_PRECONDITION_FAILED = 412; + + /** + * @since 6.0.0 + */ public const STATUS_REQUEST_ENTITY_TOO_LARGE = 413; + + /** + * @since 6.0.0 + */ public const STATUS_REQUEST_URI_TOO_LONG = 414; + + /** + * @since 6.0.0 + */ public const STATUS_UNSUPPORTED_MEDIA_TYPE = 415; + + /** + * @since 6.0.0 + */ public const STATUS_REQUEST_RANGE_NOT_SATISFIABLE = 416; + + /** + * @since 6.0.0 + */ public const STATUS_EXPECTATION_FAILED = 417; + + /** + * @since 6.0.0 + */ public const STATUS_IM_A_TEAPOT = 418; + + /** + * @since 6.0.0 + */ public const STATUS_UNPROCESSABLE_ENTITY = 422; + + /** + * @since 6.0.0 + */ public const STATUS_LOCKED = 423; + + /** + * @since 6.0.0 + */ public const STATUS_FAILED_DEPENDENCY = 424; + + /** + * @since 6.0.0 + */ public const STATUS_UPGRADE_REQUIRED = 426; + + /** + * @since 6.0.0 + */ public const STATUS_PRECONDITION_REQUIRED = 428; + + /** + * @since 6.0.0 + */ public const STATUS_TOO_MANY_REQUESTS = 429; + + /** + * @since 6.0.0 + */ public const STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE = 431; + + /** + * @since 6.0.0 + */ public const STATUS_INTERNAL_SERVER_ERROR = 500; + + /** + * @since 6.0.0 + */ public const STATUS_NOT_IMPLEMENTED = 501; + + /** + * @since 6.0.0 + */ public const STATUS_BAD_GATEWAY = 502; + + /** + * @since 6.0.0 + */ public const STATUS_SERVICE_UNAVAILABLE = 503; + + /** + * @since 6.0.0 + */ public const STATUS_GATEWAY_TIMEOUT = 504; + + /** + * @since 6.0.0 + */ public const STATUS_HTTP_VERSION_NOT_SUPPORTED = 505; + + /** + * @since 6.0.0 + */ public const STATUS_VARIANT_ALSO_NEGOTIATES = 506; + + /** + * @since 6.0.0 + */ public const STATUS_INSUFFICIENT_STORAGE = 507; + + /** + * @since 6.0.0 + */ public const STATUS_LOOP_DETECTED = 508; + + /** + * @since 6.0.0 + */ public const STATUS_BANDWIDTH_LIMIT_EXCEEDED = 509; + + /** + * @since 6.0.0 + */ public const STATUS_NOT_EXTENDED = 510; + + /** + * @since 6.0.0 + */ public const STATUS_NETWORK_AUTHENTICATION_REQUIRED = 511; } diff --git a/lib/public/AppFramework/Http/Attribute/ApiRoute.php b/lib/public/AppFramework/Http/Attribute/ApiRoute.php new file mode 100644 index 00000000000..0b566082521 --- /dev/null +++ b/lib/public/AppFramework/Http/Attribute/ApiRoute.php @@ -0,0 +1,63 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2024 Kate Döen <kate.doeen@nextcloud.com> + * + * @author Kate Döen <kate.doeen@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\AppFramework\Http\Attribute; + +use Attribute; + +/** + * This attribute can be used to define API routes on controller methods. + * + * It works in addition to the traditional routes.php method and has the same parameters + * (except for the `name` parameter which is not needed). + * + * @since 29.0.0 + */ +#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] +class ApiRoute extends Route { + /** + * @inheritDoc + * + * @since 29.0.0 + */ + public function __construct( + protected string $verb, + protected string $url, + protected ?array $requirements = null, + protected ?array $defaults = null, + protected ?string $root = null, + protected ?string $postfix = null, + ) { + parent::__construct( + Route::TYPE_API, + $verb, + $url, + $requirements, + $defaults, + $root, + $postfix, + ); + } +} diff --git a/lib/public/AppFramework/Http/Attribute/FrontpageRoute.php b/lib/public/AppFramework/Http/Attribute/FrontpageRoute.php new file mode 100644 index 00000000000..ac08513a2a9 --- /dev/null +++ b/lib/public/AppFramework/Http/Attribute/FrontpageRoute.php @@ -0,0 +1,63 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2024 Kate Döen <kate.doeen@nextcloud.com> + * + * @author Kate Döen <kate.doeen@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\AppFramework\Http\Attribute; + +use Attribute; + +/** + * This attribute can be used to define Frontpage routes on controller methods. + * + * It works in addition to the traditional routes.php method and has the same parameters + * (except for the `name` parameter which is not needed). + * + * @since 29.0.0 + */ +#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] +class FrontpageRoute extends Route { + /** + * @inheritDoc + * + * @since 29.0.0 + */ + public function __construct( + protected string $verb, + protected string $url, + protected ?array $requirements = null, + protected ?array $defaults = null, + protected ?string $root = null, + protected ?string $postfix = null, + ) { + parent::__construct( + Route::TYPE_FRONTPAGE, + $verb, + $url, + $requirements, + $defaults, + $root, + $postfix, + ); + } +} 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/Attribute/Route.php b/lib/public/AppFramework/Http/Attribute/Route.php new file mode 100644 index 00000000000..58579c1f956 --- /dev/null +++ b/lib/public/AppFramework/Http/Attribute/Route.php @@ -0,0 +1,161 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2024 Kate Döen <kate.doeen@nextcloud.com> + * + * @author Kate Döen <kate.doeen@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\AppFramework\Http\Attribute; + +use Attribute; + +/** + * This attribute can be used to define routes on controller methods. + * + * It works in addition to the traditional routes.php method and has the same parameters + * (except for the `name` parameter which is not needed). + * + * @since 29.0.0 + */ +#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] +class Route { + + /** + * Corresponds to the `ocs` key in routes.php + * + * @see ApiRoute + * @since 29.0.0 + */ + public const TYPE_API = 'ocs'; + + /** + * Corresponds to the `routes` key in routes.php + * + * @see FrontpageRoute + * @since 29.0.0 + */ + public const TYPE_FRONTPAGE = 'routes'; + + /** + * @param string $type Either Route::TYPE_API or Route::TYPE_FRONTPAGE. + * @psalm-param Route::TYPE_* $type + * @param string $verb HTTP method of the route. + * @psalm-param 'GET'|'HEAD'|'POST'|'PUT'|'DELETE'|'OPTIONS'|'PATCH' $verb + * @param string $url The path of the route. + * @param ?array<string, string> $requirements Array of regexes mapped to the path parameters. + * @param ?array<string, mixed> $defaults Array of default values mapped to the path parameters. + * @param ?string $root Custom root. For OCS all apps are allowed, but for index.php only some can use it. + * @param ?string $postfix Postfix for the route name. + * @since 29.0.0 + */ + public function __construct( + protected string $type, + protected string $verb, + protected string $url, + protected ?array $requirements = null, + protected ?array $defaults = null, + protected ?string $root = null, + protected ?string $postfix = null, + ) { + } + + /** + * @return array{ + * verb: string, + * url: string, + * requirements?: array<string, string>, + * defaults?: array<string, mixed>, + * root?: string, + * postfix?: string, + * } + * @since 29.0.0 + */ + public function toArray() { + $route = [ + 'verb' => $this->verb, + 'url' => $this->url, + ]; + + if ($this->requirements !== null) { + $route['requirements'] = $this->requirements; + } + if ($this->defaults !== null) { + $route['defaults'] = $this->defaults; + } + if ($this->root !== null) { + $route['root'] = $this->root; + } + if ($this->postfix !== null) { + $route['postfix'] = $this->postfix; + } + + return $route; + } + + /** + * @since 29.0.0 + */ + public function getType(): string { + return $this->type; + } + + /** + * @since 29.0.0 + */ + public function getVerb(): string { + return $this->verb; + } + + /** + * @since 29.0.0 + */ + public function getUrl(): string { + return $this->url; + } + + /** + * @since 29.0.0 + */ + public function getRequirements(): ?array { + return $this->requirements; + } + + /** + * @since 29.0.0 + */ + public function getDefaults(): ?array { + return $this->defaults; + } + + /** + * @since 29.0.0 + */ + public function getRoot(): ?string { + return $this->root; + } + + /** + * @since 29.0.0 + */ + public function getPostfix(): ?string { + return $this->postfix; + } +} 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..6662a302d7f 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 ?string $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; } @@ -446,29 +460,37 @@ class EmptyContentSecurityPolicy { $policy .= "base-uri 'none';"; $policy .= "manifest-src 'self';"; - if (!empty($this->allowedScriptDomains) || $this->evalScriptAllowed || $this->evalWasmAllowed) { + if (!empty($this->allowedScriptDomains) || $this->evalScriptAllowed || $this->evalWasmAllowed || is_string($this->jsNonce)) { $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/ParameterOutOfRangeException.php b/lib/public/AppFramework/Http/ParameterOutOfRangeException.php new file mode 100644 index 00000000000..22518d8eddf --- /dev/null +++ b/lib/public/AppFramework/Http/ParameterOutOfRangeException.php @@ -0,0 +1,79 @@ +<?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; + +/** + * @since 29.0.0 + */ +class ParameterOutOfRangeException extends \OutOfRangeException { + /** + * @since 29.0.0 + */ + public function __construct( + protected string $parameterName, + protected int $actualValue, + protected int $minValue, + protected int $maxValue, + ) { + parent::__construct( + sprintf( + 'Parameter %s must be between %d and %d', + $this->parameterName, + $this->minValue, + $this->maxValue, + ) + ); + } + + /** + * @since 29.0.0 + */ + public function getParameterName(): string { + return $this->parameterName; + } + + /** + * @since 29.0.0 + */ + public function getActualValue(): int { + return $this->actualValue; + } + + /** + * @since 29.0.0 + */ + public function getMinValue(): int { + return $this->minValue; + } + + /** + * @since 29.0.0 + */ + public function getMaxValue(): int { + return $this->maxValue; + } +} 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/Http/TooManyRequestsResponse.php b/lib/public/AppFramework/Http/TooManyRequestsResponse.php index 043ae0161e9..688fb6cc385 100644 --- a/lib/public/AppFramework/Http/TooManyRequestsResponse.php +++ b/lib/public/AppFramework/Http/TooManyRequestsResponse.php @@ -26,8 +26,8 @@ declare(strict_types=1); */ namespace OCP\AppFramework\Http; -use OCP\Template; use OCP\AppFramework\Http; +use OCP\Template; /** * A generic 429 response showing an 404 error page as well to the end-user diff --git a/lib/public/AppFramework/OCSController.php b/lib/public/AppFramework/OCSController.php index 51c71ad4e64..e7dd817b0aa 100644 --- a/lib/public/AppFramework/OCSController.php +++ b/lib/public/AppFramework/OCSController.php @@ -36,9 +36,24 @@ use OCP\IRequest; * @since 8.1.0 */ abstract class OCSController extends ApiController { + /** + * @since 22.0.0 + */ public const RESPOND_UNAUTHORISED = 997; + + /** + * @since 22.0.0 + */ public const RESPOND_SERVER_ERROR = 996; + + /** + * @since 22.0.0 + */ public const RESPOND_NOT_FOUND = 998; + + /** + * @since 22.0.0 + */ public const RESPOND_UNKNOWN_ERROR = 999; /** @var int */ @@ -59,10 +74,10 @@ abstract class OCSController extends ApiController { * @since 8.1.0 */ public function __construct($appName, - IRequest $request, - $corsMethods = 'PUT, POST, GET, DELETE, PATCH', - $corsAllowedHeaders = 'Authorization, Content-Type, Accept, OCS-APIRequest', - $corsMaxAge = 1728000) { + IRequest $request, + $corsMethods = 'PUT, POST, GET, DELETE, PATCH', + $corsAllowedHeaders = 'Authorization, Content-Type, Accept, OCS-APIRequest', + $corsMaxAge = 1728000) { parent::__construct($appName, $request, $corsMethods, $corsAllowedHeaders, $corsMaxAge); $this->registerResponder('json', function ($data) { diff --git a/lib/public/AppFramework/PublicShareController.php b/lib/public/AppFramework/PublicShareController.php index 52acbe841b4..cbcb9343198 100644 --- a/lib/public/AppFramework/PublicShareController.php +++ b/lib/public/AppFramework/PublicShareController.php @@ -53,8 +53,8 @@ abstract class PublicShareController extends Controller { * @since 14.0.0 */ public function __construct(string $appName, - IRequest $request, - ISession $session) { + IRequest $request, + ISession $session) { parent::__construct($appName, $request); $this->session = $session; @@ -86,7 +86,7 @@ abstract class PublicShareController extends Controller { * * @since 14.0.0 */ - abstract protected function getPasswordHash(): string; + abstract protected function getPasswordHash(): ?string; /** * Is the provided token a valid token diff --git a/lib/public/AppFramework/Services/IAppConfig.php b/lib/public/AppFramework/Services/IAppConfig.php index baac2946e3c..9340fdd3c32 100644 --- a/lib/public/AppFramework/Services/IAppConfig.php +++ b/lib/public/AppFramework/Services/IAppConfig.php @@ -5,6 +5,7 @@ declare(strict_types=1); /** * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl> * + * @author Maxence Lange <maxence@artificial-owl.com> * @author Roeland Jago Douma <roeland@famdouma.nl> * * @license GNU AGPL version 3 or any later version @@ -25,6 +26,8 @@ declare(strict_types=1); */ namespace OCP\AppFramework\Services; +use OCP\Exceptions\AppConfigUnknownKeyException; + /** * Wrapper for AppConfig for the AppFramework * @@ -37,7 +40,58 @@ interface IAppConfig { * @return string[] the keys stored for the app * @since 20.0.0 */ - public function getAppKeys(): array ; + public function getAppKeys(): array; + + /** + * Check if a key exists in the list of stored config values. + * + * @param string $key config key + * @param bool $lazy search within lazy loaded config + * + * @return bool TRUE if key exists + * @since 29.0.0 + */ + public function hasAppKey(string $key, ?bool $lazy = false): bool; + + /** + * best way to see if a value is set as sensitive (not displayed in report) + * + * @param string $key config key + * @param bool|null $lazy search within lazy loaded config + * + * @return bool TRUE if value is sensitive + * @throws AppConfigUnknownKeyException if config key is not known + * @since 29.0.0 + */ + public function isSensitive(string $key, ?bool $lazy = false): bool; + + /** + * Returns if the config key stored in database is lazy loaded + * + * **WARNING:** ignore lazy filtering, all config values are loaded from database + * + * @param string $key config key + * + * @return bool TRUE if config is lazy loaded + * @throws AppConfigUnknownKeyException if config key is not known + * @see IAppConfig for details about lazy loading + * @since 29.0.0 + */ + public function isLazy(string $key): bool; + + /** + * List all config values from an app with config key starting with $key. + * Returns an array with config key as key, stored value as value. + * + * **WARNING:** ignore lazy filtering, all config values are loaded from database + * + * @param string $key config keys prefix to search, can be empty. + * @param bool $filtered filter sensitive config values + * + * @return array<string, string> [configKey => configValue] + * @since 29.0.0 + */ + public function getAllAppValues(string $key = '', bool $filtered = false): array; /** * Writes a new app wide value @@ -46,20 +100,197 @@ interface IAppConfig { * @param string $value the value that should be stored * @return void * @since 20.0.0 + * @deprecated 29.0.0 use {@see setAppValueString()} */ public function setAppValue(string $key, string $value): void; /** + * Store a config key and its value in database + * + * If config key is already known with the exact same config value, the database is not updated. + * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded. + * + * If config value was previously stored as sensitive or lazy loaded, status cannot be altered without using {@see deleteKey()} first + * + * @param string $key config key + * @param string $value config value + * @param bool $sensitive if TRUE value will be hidden when listing config values. + * @param bool $lazy set config as lazy loaded + * + * @return bool TRUE if value was different, therefor updated in database + * @since 29.0.0 + * @see \OCP\IAppConfig for explanation about lazy loading + */ + public function setAppValueString(string $key, string $value, bool $lazy = false, bool $sensitive = false): bool; + + /** + * Store a config key and its value in database + * + * When handling huge value around and/or above 2,147,483,647, a debug log will be generated + * on 64bits system, as php int type reach its limit (and throw an exception) on 32bits when using huge numbers. + * + * When using huge numbers, it is advised to use {@see \OCP\Util::numericToNumber()} and {@see setValueString()} + * + * If config key is already known with the exact same config value, the database is not updated. + * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded. + * + * If config value was previously stored as sensitive or lazy loaded, status cannot be altered without using {@see deleteKey()} first + * + * @param string $key config key + * @param int $value config value + * @param bool $sensitive if TRUE value will be hidden when listing config values. + * @param bool $lazy set config as lazy loaded + * + * @return bool TRUE if value was different, therefor updated in database + * @since 29.0.0 + * @see \OCP\IAppConfig for explanation about lazy loading + */ + public function setAppValueInt(string $key, int $value, bool $lazy = false, bool $sensitive = false): bool; + + /** + * Store a config key and its value in database. + * + * If config key is already known with the exact same config value, the database is not updated. + * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded. + * + * If config value was previously stored as sensitive or lazy loaded, status cannot be altered without using {@see deleteKey()} first + * + * @param string $key config key + * @param float $value config value + * @param bool $sensitive if TRUE value will be hidden when listing config values. + * @param bool $lazy set config as lazy loaded + * + * @return bool TRUE if value was different, therefor updated in database + * @since 29.0.0 + * @see \OCP\IAppConfig for explanation about lazy loading + */ + public function setAppValueFloat(string $key, float $value, bool $lazy = false, bool $sensitive = false): bool; + + /** + * Store a config key and its value in database + * + * If config key is already known with the exact same config value, the database is not updated. + * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded. + * + * If config value was previously stored as lazy loaded, status cannot be altered without using {@see deleteKey()} first + * + * @param string $key config key + * @param bool $value config value + * @param bool $lazy set config as lazy loaded + * + * @return bool TRUE if value was different, therefor updated in database + * @since 29.0.0 + * @see \OCP\IAppConfig for explanation about lazy loading + */ + public function setAppValueBool(string $key, bool $value, bool $lazy = false): bool; + + /** + * Store a config key and its value in database + * + * If config key is already known with the exact same config value, the database is not updated. + * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded. + * + * If config value was previously stored as sensitive or lazy loaded, status cannot be altered without using {@see deleteKey()} first + * + * @param string $key config key + * @param array $value config value + * @param bool $sensitive if TRUE value will be hidden when listing config values. + * @param bool $lazy set config as lazy loaded + * + * @return bool TRUE if value was different, therefor updated in database + * @since 29.0.0 + * @see \OCP\IAppConfig for explanation about lazy loading + */ + public function setAppValueArray(string $key, array $value, bool $lazy = false, bool $sensitive = false): bool; + + /** * Looks up an app wide defined value * * @param string $key the key of the value, under which it was saved * @param string $default the default value to be returned if the value isn't set + * * @return string the saved value * @since 20.0.0 + * @deprecated 29.0.0 use {@see getAppValueString()} */ public function getAppValue(string $key, string $default = ''): string; /** + * Get config value assigned to a config key. + * If config key is not found in database, default value is returned. + * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE. + * + * @param string $key config key + * @param string $default default value + * @param bool $lazy search within lazy loaded config + * + * @return string stored config value or $default if not set in database + * @since 29.0.0 + * @see \OCP\IAppConfig for explanation about lazy loading + */ + public function getAppValueString(string $key, string $default = '', bool $lazy = false): string; + + /** + * Get config value assigned to a config key. + * If config key is not found in database, default value is returned. + * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE. + * + * @param string $key config key + * @param int $default default value + * @param bool $lazy search within lazy loaded config + * + * @return int stored config value or $default if not set in database + * @since 29.0.0 + * @see \OCP\IAppConfig for explanation about lazy loading + */ + public function getAppValueInt(string $key, int $default = 0, bool $lazy = false): int; + + /** + * Get config value assigned to a config key. + * If config key is not found in database, default value is returned. + * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE. + * + * @param string $key config key + * @param float $default default value + * @param bool $lazy search within lazy loaded config + * + * @return float stored config value or $default if not set in database + * @since 29.0.0 + * @see \OCP\IAppConfig for explanation about lazy loading + */ + public function getAppValueFloat(string $key, float $default = 0, bool $lazy = false): float; + + /** + * Get config value assigned to a config key. + * If config key is not found in database, default value is returned. + * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE. + * + * @param string $key config key + * @param bool $default default value + * @param bool $lazy search within lazy loaded config + * + * @return bool stored config value or $default if not set in database + * @since 29.0.0 + * @see \OCP\IAppConfig for explanation about lazy loading + */ + public function getAppValueBool(string $key, bool $default = false, bool $lazy = false): bool; + + /** + * Get config value assigned to a config key. + * If config key is not found in database, default value is returned. + * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE. + * + * @param string $key config key + * @param array $default default value + * @param bool $lazy search within lazy loaded config + * + * @return array stored config value or $default if not set in database + * @since 29.0.0 + * @see \OCP\IAppConfig for explanation about lazy loading + */ + public function getAppValueArray(string $key, array $default = [], bool $lazy = false): array; + + /** * Delete an app wide defined value * * @param string $key the key of the value, under which it was saved diff --git a/lib/public/AppFramework/Utility/ITimeFactory.php b/lib/public/AppFramework/Utility/ITimeFactory.php index d4f74c9d107..be1b80ff617 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 */ @@ -58,4 +58,12 @@ interface ITimeFactory extends ClockInterface { * @since 26.0.0 */ public function withTimeZone(\DateTimeZone $timezone): static; + + /** + * @param string|null $timezone + * @return \DateTimeZone Requested timezone if provided, UTC otherwise + * @throws \Exception + * @since 29.0.0 + */ + public function getTimeZone(?string $timezone = null): \DateTimeZone; } 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/Authentication/TwoFactorAuth/IRegistry.php b/lib/public/Authentication/TwoFactorAuth/IRegistry.php index 0f164902f67..19010e5e5bf 100644 --- a/lib/public/Authentication/TwoFactorAuth/IRegistry.php +++ b/lib/public/Authentication/TwoFactorAuth/IRegistry.php @@ -39,11 +39,13 @@ use OCP\IUser; */ interface IRegistry { /** + * @since 15.0.0 * @deprecated 22.0.0 */ public const EVENT_PROVIDER_ENABLED = self::class . '::enable'; /** + * @since 15.0.0 * @deprecated 22.0.0 */ public const EVENT_PROVIDER_DISABLED = self::class . '::disable'; diff --git a/lib/public/BackgroundJob/IJobList.php b/lib/public/BackgroundJob/IJobList.php index 65e2f5b6250..07b5ebcf48b 100644 --- a/lib/public/BackgroundJob/IJobList.php +++ b/lib/public/BackgroundJob/IJobList.php @@ -32,8 +32,8 @@ namespace OCP\BackgroundJob; * This interface provides functions to register background jobs * * To create a new background job create a new class that inherits from either - * \OC\BackgroundJob\Job, \OC\BackgroundJob\QueuedJob or - * \OC\BackgroundJob\TimedJob and register it using ->add($job, $argument), + * \OCP\BackgroundJob\Job, \OCP\BackgroundJob\QueuedJob or + * \OCP\BackgroundJob\TimedJob and register it using ->add($job, $argument), * $argument will be passed to the run() function of the job when the job is * executed. * @@ -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/BackgroundJob/Job.php b/lib/public/BackgroundJob/Job.php index a574e90e1a0..3d1a117ac9e 100644 --- a/lib/public/BackgroundJob/Job.php +++ b/lib/public/BackgroundJob/Job.php @@ -44,7 +44,6 @@ abstract class Job implements IJob, IParallelAwareJob { protected $argument; protected ITimeFactory $time; protected bool $allowParallelRuns = true; - private ?ILogger $logger = null; /** * @since 15.0.0 @@ -56,14 +55,13 @@ abstract class Job implements IJob, IParallelAwareJob { /** * The function to prepare the execution of the job. * - * - * @param IJobList $jobList - * @param ILogger|null $logger + * @return void * * @since 15.0.0 + * @deprecated since 25.0.0 Use start() instead. This method will be removed + * with the ILogger interface */ - public function execute(IJobList $jobList, ILogger $logger = null) { - $this->logger = $logger; + public function execute(IJobList $jobList, ?ILogger $logger = null) { $this->start($jobList); } @@ -73,19 +71,20 @@ abstract class Job implements IJob, IParallelAwareJob { */ public function start(IJobList $jobList): void { $jobList->setLastRun($this); - $logger = $this->logger ?? \OCP\Server::get(LoggerInterface::class); + $logger = \OCP\Server::get(LoggerInterface::class); try { + $jobDetails = get_class($this) . ' (id: ' . $this->getId() . ', arguments: ' . json_encode($this->getArgument()) . ')'; $jobStartTime = $this->time->getTime(); - $logger->debug('Run ' . get_class($this) . ' job with ID ' . $this->getId(), ['app' => 'cron']); + $logger->debug('Starting job ' . $jobDetails, ['app' => 'cron']); $this->run($this->argument); $timeTaken = $this->time->getTime() - $jobStartTime; - $logger->debug('Finished ' . get_class($this) . ' job with ID ' . $this->getId() . ' in ' . $timeTaken . ' seconds', ['app' => 'cron']); + $logger->debug('Finished job ' . $jobDetails . ' in ' . $timeTaken . ' seconds', ['app' => 'cron']); $jobList->setExecutionTime($this, $timeTaken); } catch (\Throwable $e) { if ($logger) { - $logger->error('Error while running background job (class: ' . get_class($this) . ', arguments: ' . print_r($this->argument, true) . ')', [ + $logger->error('Error while running background job ' . $jobDetails, [ 'app' => 'core', 'exception' => $e, ]); @@ -158,6 +157,7 @@ abstract class Job implements IJob, IParallelAwareJob { * The actual function that is called to run the job * * @param $argument + * @return void * * @since 15.0.0 */ diff --git a/lib/public/Cache/CappedMemoryCache.php b/lib/public/Cache/CappedMemoryCache.php index 6699600d42c..c92f68044ba 100644 --- a/lib/public/Cache/CappedMemoryCache.php +++ b/lib/public/Cache/CappedMemoryCache.php @@ -30,6 +30,7 @@ use OCP\ICache; * * @since 25.0.0 * @template T + * @template-implements \ArrayAccess<string,T> */ class CappedMemoryCache implements ICache, \ArrayAccess { private int $capacity; 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/LinkReferenceProvider.php b/lib/public/Collaboration/Reference/LinkReferenceProvider.php new file mode 100644 index 00000000000..d41c1160c7c --- /dev/null +++ b/lib/public/Collaboration/Reference/LinkReferenceProvider.php @@ -0,0 +1,221 @@ +<?php + +declare(strict_types=1); +/** + * @copyright Copyright (c) 2022 Julius Härtl <jus@bitgrid.net> + * + * @author Julius Härtl <jus@bitgrid.net> + * @author Anupam Kumar <kyteinsky@gmail.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\Reference; + +use Fusonic\OpenGraph\Consumer; +use GuzzleHttp\Exception\GuzzleException; +use GuzzleHttp\Psr7\LimitStream; +use GuzzleHttp\Psr7\Utils; +use OC\Security\RateLimiting\Exception\RateLimitExceededException; +use OC\Security\RateLimiting\Limiter; +use OC\SystemConfig; +use OCP\Files\AppData\IAppDataFactory; +use OCP\Files\NotFoundException; +use OCP\Http\Client\IClientService; +use OCP\IRequest; +use OCP\IURLGenerator; +use OCP\IUserSession; +use Psr\Log\LoggerInterface; + +/** + * @since 29.0.0 + */ +class LinkReferenceProvider implements IReferenceProvider { + + /** + * for image size and webpage header + * @since 29.0.0 + */ + public const MAX_CONTENT_LENGTH = 5 * 1024 * 1024; + + /** + * @since 29.0.0 + */ + public const ALLOWED_CONTENT_TYPES = [ + 'image/png', + 'image/jpg', + 'image/jpeg', + 'image/gif', + 'image/svg+xml', + 'image/webp' + ]; + + /** + * @since 29.0.0 + */ + public function __construct( + private IClientService $clientService, + private LoggerInterface $logger, + private SystemConfig $systemConfig, + private IAppDataFactory $appDataFactory, + private IURLGenerator $urlGenerator, + private Limiter $limiter, + private IUserSession $userSession, + private IRequest $request, + ) { + } + + /** + * @inheritDoc + * @since 29.0.0 + */ + public function matchReference(string $referenceText): bool { + if ($this->systemConfig->getValue('reference_opengraph', true) !== true) { + return false; + } + + return (bool)preg_match(IURLGenerator::URL_REGEX, $referenceText); + } + + /** + * @inheritDoc + * @since 29.0.0 + */ + public function resolveReference(string $referenceText): ?IReference { + if ($this->matchReference($referenceText)) { + $reference = new Reference($referenceText); + $this->fetchReference($reference); + return $reference; + } + + return null; + } + + /** + * Populates the reference with OpenGraph data + * + * @param Reference $reference + * @since 29.0.0 + */ + private function fetchReference(Reference $reference): void { + try { + $user = $this->userSession->getUser(); + if ($user) { + $this->limiter->registerUserRequest('opengraph', 10, 120, $user); + } else { + $this->limiter->registerAnonRequest('opengraph', 10, 120, $this->request->getRemoteAddress()); + } + } catch (RateLimitExceededException $e) { + return; + } + + $client = $this->clientService->newClient(); + try { + $headResponse = $client->head($reference->getId(), [ 'timeout' => 10 ]); + } catch (\Exception $e) { + $this->logger->debug('Failed to perform HEAD request to get target metadata', ['exception' => $e]); + return; + } + + $linkContentLength = $headResponse->getHeader('Content-Length'); + if (is_numeric($linkContentLength) && (int) $linkContentLength > self::MAX_CONTENT_LENGTH) { + $this->logger->debug('Skip resolving links pointing to content length > 5 MiB'); + return; + } + + $linkContentType = $headResponse->getHeader('Content-Type'); + $expectedContentTypeRegex = '/^text\/html;?/i'; + + // check the header begins with the expected content type + if (!preg_match($expectedContentTypeRegex, $linkContentType)) { + $this->logger->debug('Skip resolving links pointing to content type that is not "text/html"'); + return; + } + + try { + $response = $client->get($reference->getId(), [ 'timeout' => 10 ]); + } catch (\Exception $e) { + $this->logger->debug('Failed to fetch link for obtaining open graph data', ['exception' => $e]); + return; + } + + $responseBody = (string)$response->getBody(); + + // OpenGraph handling + $consumer = new Consumer(); + $consumer->useFallbackMode = true; + $object = $consumer->loadHtml($responseBody); + + $reference->setUrl($reference->getId()); + + if ($object->title) { + $reference->setTitle($object->title); + } + + if ($object->description) { + $reference->setDescription($object->description); + } + + if ($object->images) { + try { + $host = parse_url($object->images[0]->url, PHP_URL_HOST); + if ($host === false || $host === null) { + $this->logger->warning('Could not detect host of open graph image URI for ' . $reference->getId()); + return; + } + + $appData = $this->appDataFactory->get('core'); + try { + $folder = $appData->getFolder('opengraph'); + } catch (NotFoundException $e) { + $folder = $appData->newFolder('opengraph'); + } + + $response = $client->get($object->images[0]->url, ['timeout' => 10]); + $contentType = $response->getHeader('Content-Type'); + $contentLength = $response->getHeader('Content-Length'); + + if (in_array($contentType, self::ALLOWED_CONTENT_TYPES, true) && $contentLength < self::MAX_CONTENT_LENGTH) { + $stream = Utils::streamFor($response->getBody()); + $bodyStream = new LimitStream($stream, self::MAX_CONTENT_LENGTH, 0); + $reference->setImageContentType($contentType); + $folder->newFile(md5($reference->getId()), $bodyStream->getContents()); + $reference->setImageUrl($this->urlGenerator->linkToRouteAbsolute('core.Reference.preview', ['referenceId' => md5($reference->getId())])); + } + } catch (GuzzleException $e) { + $this->logger->info('Failed to fetch and store the open graph image for ' . $reference->getId(), ['exception' => $e]); + } catch (\Throwable $e) { + $this->logger->error('Failed to fetch and store the open graph image for ' . $reference->getId(), ['exception' => $e]); + } + } + } + + /** + * @inheritDoc + * @since 29.0.0 + */ + public function getCachePrefix(string $referenceId): string { + return $referenceId; + } + + /** + * @inheritDoc + * @since 29.0.0 + */ + public function getCacheKey(string $referenceId): ?string { + return null; + } +} 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/Comments/CommentsEntityEvent.php b/lib/public/Comments/CommentsEntityEvent.php index 3336b80d6b5..fd43a5539ff 100644 --- a/lib/public/Comments/CommentsEntityEvent.php +++ b/lib/public/Comments/CommentsEntityEvent.php @@ -33,6 +33,7 @@ use OCP\EventDispatcher\Event; */ class CommentsEntityEvent extends Event { /** + * @since 9.1.0 * @deprecated 22.0.0 - Listen to the typed event instead. */ public const EVENT_ENTITY = 'OCP\Comments\ICommentsManager::registerEntity'; diff --git a/lib/public/Comments/CommentsEvent.php b/lib/public/Comments/CommentsEvent.php index 1793a1e3a86..b04b5c2e6f9 100644 --- a/lib/public/Comments/CommentsEvent.php +++ b/lib/public/Comments/CommentsEvent.php @@ -32,21 +32,25 @@ use OCP\EventDispatcher\Event; */ class CommentsEvent extends Event { /** + * @since 11.0.0 * @deprecated 22.0.0 */ public const EVENT_ADD = 'OCP\Comments\ICommentsManager::addComment'; /** + * @since 11.0.0 * @deprecated 22.0.0 */ public const EVENT_PRE_UPDATE = 'OCP\Comments\ICommentsManager::preUpdateComment'; /** + * @since 11.0.0 * @deprecated 22.0.0 */ public const EVENT_UPDATE = 'OCP\Comments\ICommentsManager::updateComment'; /** + * @since 11.0.0 * @deprecated 22.0.0 */ public const EVENT_DELETE = 'OCP\Comments\ICommentsManager::deleteComment'; diff --git a/lib/public/Comments/IComment.php b/lib/public/Comments/IComment.php index eb696fa5f06..8182bcd15d0 100644 --- a/lib/public/Comments/IComment.php +++ b/lib/public/Comments/IComment.php @@ -33,6 +33,9 @@ namespace OCP\Comments; * @since 9.0.0 */ interface IComment { + /** + * @since 9.0.0 + */ public const MAX_MESSAGE_LENGTH = 1000; /** @@ -280,6 +283,25 @@ interface IComment { public function setReferenceId(?string $referenceId): IComment; /** + * Returns the metadata of the comment + * + * @return array|null + * @since 29.0.0 + */ + public function getMetaData(): ?array; + + /** + * Sets (overwrites) the metadata of the comment + * Data as a json encoded array + * + * @param array|null $metaData + * @return IComment + * @throws \JsonException When the metadata can not be converted to a json encoded string + * @since 29.0.0 + */ + public function setMetaData(?array $metaData): IComment; + + /** * Returns the reactions array if exists * * The keys is the emoji of reaction and the value is the total. diff --git a/lib/public/Comments/ICommentsManager.php b/lib/public/Comments/ICommentsManager.php index 8d7ffd164b3..3d47be3d951 100644 --- a/lib/public/Comments/ICommentsManager.php +++ b/lib/public/Comments/ICommentsManager.php @@ -114,11 +114,11 @@ interface ICommentsManager { * @since 9.0.0 */ public function getForObject( - $objectType, - $objectId, - $limit = 0, - $offset = 0, - \DateTime $notOlderThan = null + $objectType, + $objectId, + $limit = 0, + $offset = 0, + \DateTime $notOlderThan = null ); /** @@ -271,6 +271,7 @@ interface ICommentsManager { * @param IUser $user * @return array [$fileId => $unreadCount] * @since 12.0.0 + * @deprecated 29.0.0 use getNumberOfUnreadCommentsForObjects instead */ public function getNumberOfUnreadCommentsForFolder($folderId, IUser $user); diff --git a/lib/public/Console/ConsoleEvent.php b/lib/public/Console/ConsoleEvent.php index 99f42d2895f..2ab2521527c 100644 --- a/lib/public/Console/ConsoleEvent.php +++ b/lib/public/Console/ConsoleEvent.php @@ -32,6 +32,7 @@ use OCP\EventDispatcher\Event; */ class ConsoleEvent extends Event { /** + * @since 9.0.0 * @deprecated 22.0.0 */ public const EVENT_RUN = 'OC\Console\Application::run'; diff --git a/lib/public/Constants.php b/lib/public/Constants.php index 6a38190e167..8bae05b51e8 100644 --- a/lib/public/Constants.php +++ b/lib/public/Constants.php @@ -43,10 +43,30 @@ class Constants { * @since 8.0.0 */ public const PERMISSION_CREATE = 4; + + /** + * @since 8.0.0 + */ public const PERMISSION_READ = 1; + + /** + * @since 8.0.0 + */ public const PERMISSION_UPDATE = 2; + + /** + * @since 8.0.0 + */ public const PERMISSION_DELETE = 8; + + /** + * @since 8.0.0 + */ public const PERMISSION_SHARE = 16; + + /** + * @since 8.0.0 + */ public const PERMISSION_ALL = 31; /** 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/Events/AddMissingIndicesEvent.php b/lib/public/DB/Events/AddMissingIndicesEvent.php index dc942f3d63e..8b6d2a07a0c 100644 --- a/lib/public/DB/Events/AddMissingIndicesEvent.php +++ b/lib/public/DB/Events/AddMissingIndicesEvent.php @@ -39,6 +39,9 @@ class AddMissingIndicesEvent extends \OCP\EventDispatcher\Event { /** @var array<array-key, array{tableName: string, indexName: string, columns: string[], options: array{}, dropUnnamedIndex: bool, uniqueIndex: bool}> */ private array $missingIndices = []; + /** @var array<array-key, array{tableName: string, oldIndexNames: array, newIndexName: string, columns: string[], uniqueIndex: bool, options: array{}}> */ + private array $toReplaceIndices = []; + /** * @param string[] $columns * @since 28.0.0 @@ -75,4 +78,42 @@ class AddMissingIndicesEvent extends \OCP\EventDispatcher\Event { public function getMissingIndices(): array { return $this->missingIndices; } + + /** + * Replace one or more existing indices with a new one. Can be used to make an index unique afterwards or merge two indices into a multicolumn index. + * + * Note: Make sure to not use the same index name for the new index as for old indices. + * + * Example: + * + * <code> + * $event->replaceIndex( + * 'my_table', + * ['old_index_col_a', 'old_index_col_b'], + * 'new_index_col_a_b', + * ['column_a', 'column_b'], + * false + * ); + * </code> + * + * @since 29.0.0 + */ + public function replaceIndex(string $tableName, array $oldIndexNames, string $newIndexName, array $columns, bool $unique, array $options = []): void { + $this->toReplaceIndices[] = [ + 'tableName' => $tableName, + 'oldIndexNames' => $oldIndexNames, + 'newIndexName' => $newIndexName, + 'columns' => $columns, + 'uniqueIndex' => $unique, + 'options' => $options, + ]; + } + + /** + * @since 29.0.0 + * @return array<array-key, array{tableName: string, oldIndexNames: array, newIndexName: string, columns: string[], uniqueIndex: bool, options: array{}}> + */ + public function getIndicesToReplace(): array { + return $this->toReplaceIndices; + } } 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/Dashboard/Model/WidgetButton.php b/lib/public/Dashboard/Model/WidgetButton.php index 480249b539f..57ea06ec6b1 100644 --- a/lib/public/Dashboard/Model/WidgetButton.php +++ b/lib/public/Dashboard/Model/WidgetButton.php @@ -29,8 +29,19 @@ namespace OCP\Dashboard\Model; * @since 25.0.0 */ class WidgetButton { + /** + * @since 25.0.0 + */ public const TYPE_NEW = 'new'; + + /** + * @since 25.0.0 + */ public const TYPE_MORE = 'more'; + + /** + * @since 25.0.0 + */ public const TYPE_SETUP = 'setup'; private string $type; diff --git a/lib/public/Dashboard/Model/WidgetItem.php b/lib/public/Dashboard/Model/WidgetItem.php index 1c54d2bd426..22235053b5d 100644 --- a/lib/public/Dashboard/Model/WidgetItem.php +++ b/lib/public/Dashboard/Model/WidgetItem.php @@ -70,11 +70,11 @@ final class WidgetItem implements JsonSerializable { * @since 22.0.0 */ public function __construct(string $title = '', - string $subtitle = '', - string $link = '', - string $iconUrl = '', - string $sinceId = '', - string $overlayIconUrl = '') { + string $subtitle = '', + string $link = '', + string $iconUrl = '', + string $sinceId = '', + string $overlayIconUrl = '') { $this->title = $title; $this->subtitle = $subtitle; $this->iconUrl = $iconUrl; diff --git a/lib/public/EventDispatcher/IEventDispatcher.php b/lib/public/EventDispatcher/IEventDispatcher.php index 0a96fa799d4..a84e0fe2f3b 100644 --- a/lib/public/EventDispatcher/IEventDispatcher.php +++ b/lib/public/EventDispatcher/IEventDispatcher.php @@ -71,6 +71,15 @@ interface IEventDispatcher { /** * @template T of \OCP\EventDispatcher\Event + * @param string $eventName preferably the fully-qualified class name of the Event sub class + * + * @return bool TRUE if event has registered listeners + * @since 29.0.0 + */ + public function hasListeners(string $eventName): bool; + + /** + * @template T of \OCP\EventDispatcher\Event * @param string $eventName * @psalm-param string|class-string<T> $eventName * @param Event $event diff --git a/lib/public/Exceptions/AbortedEventException.php b/lib/public/Exceptions/AbortedEventException.php new file mode 100644 index 00000000000..5a3b797e386 --- /dev/null +++ b/lib/public/Exceptions/AbortedEventException.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\Exceptions; + +use Exception; + +/** + * @since 29.0.0 + */ +class AbortedEventException extends Exception { +} diff --git a/lib/public/Exceptions/AppConfigException.php b/lib/public/Exceptions/AppConfigException.php new file mode 100644 index 00000000000..73c91d9f018 --- /dev/null +++ b/lib/public/Exceptions/AppConfigException.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\Exceptions; + +use Exception; + +/** + * @since 29.0.0 + */ +class AppConfigException extends Exception { +} diff --git a/lib/public/Exceptions/AppConfigIncorrectTypeException.php b/lib/public/Exceptions/AppConfigIncorrectTypeException.php new file mode 100644 index 00000000000..1284e4b193e --- /dev/null +++ b/lib/public/Exceptions/AppConfigIncorrectTypeException.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\Exceptions; + +/** + * @since 29.0.0 + */ +class AppConfigIncorrectTypeException extends AppConfigException { +} diff --git a/lib/public/Exceptions/AppConfigTypeConflictException.php b/lib/public/Exceptions/AppConfigTypeConflictException.php new file mode 100644 index 00000000000..599fed0cb3b --- /dev/null +++ b/lib/public/Exceptions/AppConfigTypeConflictException.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\Exceptions; + +/** + * @since 29.0.0 + */ +class AppConfigTypeConflictException extends AppConfigException { +} diff --git a/lib/public/Exceptions/AppConfigUnknownKeyException.php b/lib/public/Exceptions/AppConfigUnknownKeyException.php new file mode 100644 index 00000000000..e2b9d7fd3dc --- /dev/null +++ b/lib/public/Exceptions/AppConfigUnknownKeyException.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\Exceptions; + +/** + * @since 29.0.0 + */ +class AppConfigUnknownKeyException extends AppConfigException { +} diff --git a/lib/public/Federation/Exceptions/ActionNotSupportedException.php b/lib/public/Federation/Exceptions/ActionNotSupportedException.php index e5f8d50fd99..a7dcda15fe5 100644 --- a/lib/public/Federation/Exceptions/ActionNotSupportedException.php +++ b/lib/public/Federation/Exceptions/ActionNotSupportedException.php @@ -38,7 +38,7 @@ class ActionNotSupportedException extends HintException { * */ public function __construct($action) { - $l = \OC::$server->getL10N('federation'); + $l = \OCP\Util::getL10N('federation'); $message = 'Action "' . $action . '" not supported or implemented.'; $hint = $l->t('Action "%s" not supported or implemented.', [$action]); parent::__construct($message, $hint); diff --git a/lib/public/Federation/Exceptions/AuthenticationFailedException.php b/lib/public/Federation/Exceptions/AuthenticationFailedException.php index 2bd3e361edd..63c8f63ab85 100644 --- a/lib/public/Federation/Exceptions/AuthenticationFailedException.php +++ b/lib/public/Federation/Exceptions/AuthenticationFailedException.php @@ -38,7 +38,7 @@ class AuthenticationFailedException extends HintException { * */ public function __construct() { - $l = \OC::$server->getL10N('federation'); + $l = \OCP\Util::getL10N('federation'); $message = 'Authentication failed, wrong token or provider ID given'; $hint = $l->t('Authentication failed, wrong token or provider ID given'); parent::__construct($message, $hint); diff --git a/lib/public/Federation/Exceptions/BadRequestException.php b/lib/public/Federation/Exceptions/BadRequestException.php index bc8b5c7b724..fbd781aee43 100644 --- a/lib/public/Federation/Exceptions/BadRequestException.php +++ b/lib/public/Federation/Exceptions/BadRequestException.php @@ -45,7 +45,7 @@ class BadRequestException extends HintException { * @param array $missingParameters */ public function __construct(array $missingParameters) { - $l = \OC::$server->getL10N('federation'); + $l = \OCP\Util::getL10N('federation'); $this->parameterList = $missingParameters; $parameterList = implode(',', $missingParameters); $message = 'Parameters missing in order to complete the request. Missing Parameters: ' . $parameterList; diff --git a/lib/public/Federation/Exceptions/ProviderAlreadyExistsException.php b/lib/public/Federation/Exceptions/ProviderAlreadyExistsException.php index d30d81eca27..cb0a8e8d51f 100644 --- a/lib/public/Federation/Exceptions/ProviderAlreadyExistsException.php +++ b/lib/public/Federation/Exceptions/ProviderAlreadyExistsException.php @@ -42,7 +42,7 @@ class ProviderAlreadyExistsException extends HintException { * @param string $existingProviderName name of cloud federation provider which already use the same ID */ public function __construct($newProviderId, $existingProviderName) { - $l = \OC::$server->getL10N('federation'); + $l = \OCP\Util::getL10N('federation'); $message = 'ID "' . $newProviderId . '" already used by cloud federation provider "' . $existingProviderName . '"'; $hint = $l->t('ID "%1$s" already used by cloud federation provider "%2$s"', [$newProviderId, $existingProviderName]); parent::__construct($message, $hint); diff --git a/lib/public/Federation/Exceptions/ProviderDoesNotExistsException.php b/lib/public/Federation/Exceptions/ProviderDoesNotExistsException.php index 849209da22d..0594bb6875b 100644 --- a/lib/public/Federation/Exceptions/ProviderDoesNotExistsException.php +++ b/lib/public/Federation/Exceptions/ProviderDoesNotExistsException.php @@ -39,7 +39,7 @@ class ProviderDoesNotExistsException extends HintException { * @param string $providerId cloud federation provider ID */ public function __construct($providerId) { - $l = \OC::$server->getL10N('federation'); + $l = \OCP\Util::getL10N('federation'); $message = 'Cloud Federation Provider with ID: "' . $providerId . '" does not exist.'; $hint = $l->t('Cloud Federation Provider with ID: "%s" does not exist.', [$providerId]); parent::__construct($message, $hint); 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/Federation/ICloudFederationProviderManager.php b/lib/public/Federation/ICloudFederationProviderManager.php index 7272653b14d..14ac59f3b0b 100644 --- a/lib/public/Federation/ICloudFederationProviderManager.php +++ b/lib/public/Federation/ICloudFederationProviderManager.php @@ -23,6 +23,9 @@ */ namespace OCP\Federation; +use OCP\Http\Client\IResponse; +use OCP\OCM\Exceptions\OCMProviderException; + /** * Class ICloudFederationProviderManager * @@ -80,10 +83,19 @@ interface ICloudFederationProviderManager { * @return mixed * * @since 14.0.0 + * @deprecated 29.0.0 - Use {@see sendCloudShare()} instead and handle errors manually */ public function sendShare(ICloudFederationShare $share); /** + * @param ICloudFederationShare $share + * @return IResponse + * @throws OCMProviderException + * @since 29.0.0 + */ + public function sendCloudShare(ICloudFederationShare $share): IResponse; + + /** * send notification about existing share * * @param string $url @@ -91,10 +103,20 @@ interface ICloudFederationProviderManager { * @return array|false * * @since 14.0.0 + * @deprecated 29.0.0 - Use {@see sendCloudNotification()} instead and handle errors manually */ public function sendNotification($url, ICloudFederationNotification $notification); /** + * @param string $url + * @param ICloudFederationNotification $notification + * @return IResponse + * @throws OCMProviderException + * @since 29.0.0 + */ + public function sendCloudNotification(string $url, ICloudFederationNotification $notification): IResponse; + + /** * check if the new cloud federation API is ready to be used * * @return bool diff --git a/lib/public/Files/Cache/ICache.php b/lib/public/Files/Cache/ICache.php index 1934cc24bd4..0a08673d6c3 100644 --- a/lib/public/Files/Cache/ICache.php +++ b/lib/public/Files/Cache/ICache.php @@ -38,9 +38,24 @@ use OCP\Files\Search\ISearchQuery; * @since 9.0.0 */ interface ICache { + /** + * @since 9.0.0 + */ public const NOT_FOUND = 0; + + /** + * @since 9.0.0 + */ public const PARTIAL = 1; //only partial data available, file not cached in the database + + /** + * @since 9.0.0 + */ public const SHALLOW = 2; //folder in cache, but not all child files are completely scanned + + /** + * @since 9.0.0 + */ public const COMPLETE = 3; /** diff --git a/lib/public/Files/Cache/ICacheEntry.php b/lib/public/Files/Cache/ICacheEntry.php index e1e8129394c..581d5aa6731 100644 --- a/lib/public/Files/Cache/ICacheEntry.php +++ b/lib/public/Files/Cache/ICacheEntry.php @@ -28,12 +28,16 @@ use ArrayAccess; * meta data for a file or folder * * @since 9.0.0 + * @template-extends ArrayAccess<string,mixed> * * This interface extends \ArrayAccess since v21.0.0, previous versions only * implemented it in the private implementation. Hence php would allow using the * object as array, while strictly speaking it didn't support this. */ interface ICacheEntry extends ArrayAccess { + /** + * @since 9.0.0 + */ public const DIRECTORY_MIMETYPE = 'httpd/unix-directory'; /** @@ -123,8 +127,8 @@ interface ICacheEntry extends ArrayAccess { public function getEtag(); /** - * Get the permissions for the file stored as bitwise combination of \OCP\PERMISSION_READ, \OCP\PERMISSION_CREATE - * \OCP\PERMISSION_UPDATE, \OCP\PERMISSION_DELETE and \OCP\PERMISSION_SHARE + * Get the permissions for the file stored as bitwise combination of \OCP\Constants::PERMISSION_READ, \OCP\Constants::PERMISSION_CREATE + * \OCP\Constants::PERMISSION_UPDATE, \OCP\Constants::PERMISSION_DELETE and \OCP\Constants::PERMISSION_SHARE * * @return int * @since 9.0.0 diff --git a/lib/public/Files/Cache/IScanner.php b/lib/public/Files/Cache/IScanner.php index 8a45bfa6de7..e6ee4ef435f 100644 --- a/lib/public/Files/Cache/IScanner.php +++ b/lib/public/Files/Cache/IScanner.php @@ -28,12 +28,34 @@ namespace OCP\Files\Cache; * @since 9.0.0 */ interface IScanner { + /** + * @since 9.0.0 + */ public const SCAN_RECURSIVE_INCOMPLETE = 2; // only recursive into not fully scanned folders + + /** + * @since 9.0.0 + */ public const SCAN_RECURSIVE = true; + + /** + * @since 9.0.0 + */ public const SCAN_SHALLOW = false; + /** + * @since 12.0.0 + */ public const REUSE_NONE = 0; + + /** + * @since 9.0.0 + */ public const REUSE_ETAG = 1; + + /** + * @since 9.0.0 + */ public const REUSE_SIZE = 2; /** diff --git a/lib/public/Files/Cache/IUpdater.php b/lib/public/Files/Cache/IUpdater.php index 5a776d4be7e..625bc91c5a7 100644 --- a/lib/public/Files/Cache/IUpdater.php +++ b/lib/public/Files/Cache/IUpdater.php @@ -53,7 +53,7 @@ interface IUpdater { * @param int $time * @since 9.0.0 */ - public function update($path, $time = null); + public function update($path, $time = null, ?int $sizeDifference = null); /** * Remove $path from the cache and update the size, etag and mtime of the parent folders diff --git a/lib/public/Files/Cache/IWatcher.php b/lib/public/Files/Cache/IWatcher.php index f70024247d5..eca09507167 100644 --- a/lib/public/Files/Cache/IWatcher.php +++ b/lib/public/Files/Cache/IWatcher.php @@ -28,8 +28,19 @@ namespace OCP\Files\Cache; * @since 9.0.0 */ interface IWatcher { + /** + * @since 9.0.0 + */ public const CHECK_NEVER = 0; // never check the underlying filesystem for updates + + /** + * @since 9.0.0 + */ public const CHECK_ONCE = 1; // check the underlying filesystem for updates once every request for each file + + /** + * @since 9.0.0 + */ public const CHECK_ALWAYS = 2; // always check the underlying filesystem for updates /** 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/Events/FileCacheUpdated.php b/lib/public/Files/Events/FileCacheUpdated.php index 18d6318f2f4..2669c51088e 100644 --- a/lib/public/Files/Events/FileCacheUpdated.php +++ b/lib/public/Files/Events/FileCacheUpdated.php @@ -44,7 +44,7 @@ class FileCacheUpdated extends Event { * @since 18.0.0 */ public function __construct(IStorage $storage, - string $path) { + string $path) { parent::__construct(); $this->storage = $storage; $this->path = $path; diff --git a/lib/public/Files/Events/Node/AbstractNodeEvent.php b/lib/public/Files/Events/Node/AbstractNodeEvent.php index d6c1c6d0f95..768c0eda2d5 100644 --- a/lib/public/Files/Events/Node/AbstractNodeEvent.php +++ b/lib/public/Files/Events/Node/AbstractNodeEvent.php @@ -32,14 +32,12 @@ use OCP\Files\Node; * @since 20.0.0 */ abstract class AbstractNodeEvent extends Event { - /** @var Node */ - private $node; - /** * @since 20.0.0 */ - public function __construct(Node $node) { - $this->node = $node; + public function __construct( + private Node $node + ) { } /** diff --git a/lib/public/Files/Events/Node/AbstractNodesEvent.php b/lib/public/Files/Events/Node/AbstractNodesEvent.php index 6bd9bc32a88..d736ebf1635 100644 --- a/lib/public/Files/Events/Node/AbstractNodesEvent.php +++ b/lib/public/Files/Events/Node/AbstractNodesEvent.php @@ -32,17 +32,13 @@ use OCP\Files\Node; * @since 20.0.0 */ abstract class AbstractNodesEvent extends Event { - /** @var Node */ - private $source; - /** @var Node */ - private $target; - /** * @since 20.0.0 */ - public function __construct(Node $source, Node $target) { - $this->source = $source; - $this->target = $target; + public function __construct( + private Node $source, + private Node $target + ) { } /** diff --git a/lib/public/Files/Events/Node/BeforeNodeDeletedEvent.php b/lib/public/Files/Events/Node/BeforeNodeDeletedEvent.php index 316a30fadd8..c0226a9b527 100644 --- a/lib/public/Files/Events/Node/BeforeNodeDeletedEvent.php +++ b/lib/public/Files/Events/Node/BeforeNodeDeletedEvent.php @@ -25,8 +25,17 @@ declare(strict_types=1); */ namespace OCP\Files\Events\Node; +use OCP\Exceptions\AbortedEventException; + /** * @since 20.0.0 */ class BeforeNodeDeletedEvent extends AbstractNodeEvent { + /** + * @since 28.0.0 + * @deprecated 29.0.0 - use OCP\Exceptions\AbortedEventException instead + */ + public function abortOperation(\Throwable $ex = null) { + throw new AbortedEventException($ex?->getMessage() ?? 'Operation aborted'); + } } diff --git a/lib/public/Files/Events/Node/BeforeNodeRenamedEvent.php b/lib/public/Files/Events/Node/BeforeNodeRenamedEvent.php index efbef03e383..4c2c566c8c6 100644 --- a/lib/public/Files/Events/Node/BeforeNodeRenamedEvent.php +++ b/lib/public/Files/Events/Node/BeforeNodeRenamedEvent.php @@ -25,8 +25,17 @@ declare(strict_types=1); */ namespace OCP\Files\Events\Node; +use OCP\Exceptions\AbortedEventException; + /** * @since 20.0.0 */ class BeforeNodeRenamedEvent extends AbstractNodesEvent { + /** + * @since 28.0.0 + * @deprecated 29.0.0 - use OCP\Exceptions\AbortedEventException instead + */ + public function abortOperation(\Throwable $ex = null) { + throw new AbortedEventException($ex?->getMessage() ?? 'Operation aborted'); + } } diff --git a/lib/public/Files/Events/NodeAddedToCache.php b/lib/public/Files/Events/NodeAddedToCache.php index 6986b4b5989..3a1947e7a16 100644 --- a/lib/public/Files/Events/NodeAddedToCache.php +++ b/lib/public/Files/Events/NodeAddedToCache.php @@ -44,7 +44,7 @@ class NodeAddedToCache extends Event { * @since 18.0.0 */ public function __construct(IStorage $storage, - string $path) { + string $path) { parent::__construct(); $this->storage = $storage; $this->path = $path; diff --git a/lib/public/Files/Events/NodeRemovedFromCache.php b/lib/public/Files/Events/NodeRemovedFromCache.php index 9f67cb71371..83c4bd16531 100644 --- a/lib/public/Files/Events/NodeRemovedFromCache.php +++ b/lib/public/Files/Events/NodeRemovedFromCache.php @@ -44,7 +44,7 @@ class NodeRemovedFromCache extends Event { * @since 18.0.0 */ public function __construct(IStorage $storage, - string $path) { + string $path) { parent::__construct(); $this->storage = $storage; $this->path = $path; 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/Folder.php b/lib/public/Files/Folder.php index eb81a2098ec..945df48a13e 100644 --- a/lib/public/Files/Folder.php +++ b/lib/public/Files/Folder.php @@ -152,11 +152,13 @@ interface Folder extends Node { public function searchBySystemTag(string $tagName, string $userId, int $limit = 0, int $offset = 0); /** - * get a file or folder inside the folder by it's internal id + * get a file or folder inside the folder by its internal id * * This method could return multiple entries. For example once the file/folder * is shared or mounted (files_external) to the user multiple times. * + * Note that the different entries can have different permissions. + * * @param int $id * @return \OCP\Files\Node[] * @since 6.0.0 @@ -164,6 +166,24 @@ interface Folder extends Node { public function getById($id); /** + * get a file or folder inside the folder by its internal id + * + * Unlike getById, this method only returns a single node even if the user has + * access to the file with the requested id multiple times. + * + * This method provides no guarantee about which of the nodes in returned and the + * returned node might, for example, have less permissions than other nodes for the same file + * + * Apps that require accurate information about the users access to the file should use getById + * instead of pick the correct node out of the result. + * + * @param int $id + * @return Node|null + * @since 29.0.0 + */ + public function getFirstNodeById(int $id): ?Node; + + /** * Get the amount of free space inside the folder * * @return int 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/IMimeTypeLoader.php b/lib/public/Files/IMimeTypeLoader.php index d4f2767bc17..39a5db85c1a 100644 --- a/lib/public/Files/IMimeTypeLoader.php +++ b/lib/public/Files/IMimeTypeLoader.php @@ -35,7 +35,7 @@ interface IMimeTypeLoader { * @return string|null * @since 8.2.0 */ - public function getMimetypeById($id); + public function getMimetypeById(int $id): ?string; /** * Get a mimetype ID, adding the mimetype to the DB if it does not exist @@ -44,7 +44,7 @@ interface IMimeTypeLoader { * @return int * @since 8.2.0 */ - public function getId($mimetype); + public function getId(string $mimetype): int; /** * Test if a mimetype exists in the database @@ -53,12 +53,12 @@ interface IMimeTypeLoader { * @return bool * @since 8.2.0 */ - public function exists($mimetype); + public function exists(string $mimetype): bool; /** * Clear all loaded mimetypes, allow for re-loading * * @since 8.2.0 */ - public function reset(); + public function reset(): void; } diff --git a/lib/public/Files/IRootFolder.php b/lib/public/Files/IRootFolder.php index 1fee0b3595e..c1c0e6e8c72 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 @@ -58,6 +60,24 @@ interface IRootFolder extends Folder, Emitter { public function getByIdInPath(int $id, string $path); /** + * get a file or folder inside the folder by its internal id + * + * Unlike getByIdInPath, this method only returns a single node even if the user has + * access to the file with the requested id multiple times. + * + * This method provides no guarantee about which of the nodes in returned and the + * returned node might, for example, have less permissions than other nodes for the same file + * + * Apps that require accurate information about the users access to the file should use getByIdInPath + * instead of pick the correct node out of the result. + * + * @param int $id + * @return Node|null + * @since 29.0.0 + */ + public function getFirstNodeByIdInPath(int $id, string $path): ?Node; + + /** * @return IMountPoint[] * * @since 28.0.0 @@ -65,6 +85,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/LockNotAcquiredException.php b/lib/public/Files/LockNotAcquiredException.php index 4a26878cedf..2cf8ea20acf 100644 --- a/lib/public/Files/LockNotAcquiredException.php +++ b/lib/public/Files/LockNotAcquiredException.php @@ -43,7 +43,7 @@ class LockNotAcquiredException extends \Exception { * @since 7.0.0 */ public function __construct($path, $lockType, $code = 0, \Exception $previous = null) { - $message = \OC::$server->getL10N('core')->t('Could not obtain lock type %d on "%s".', [$lockType, $path]); + $message = \OCP\Util::getL10N('core')->t('Could not obtain lock type %d on "%s".', [$lockType, $path]); parent::__construct($message, $code, $previous); } 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/Node.php b/lib/public/Files/Node.php index b49b4a0f83d..ecf1427fa1a 100644 --- a/lib/public/Files/Node.php +++ b/lib/public/Files/Node.php @@ -30,8 +30,8 @@ namespace OCP\Files; -use OCP\Lock\LockedException; use OCP\Files\Storage\IStorage; +use OCP\Lock\LockedException; /** * Interface Node diff --git a/lib/public/Files/Notify/IChange.php b/lib/public/Files/Notify/IChange.php index 2a4f806be46..d8fd61b1a47 100644 --- a/lib/public/Files/Notify/IChange.php +++ b/lib/public/Files/Notify/IChange.php @@ -29,9 +29,24 @@ namespace OCP\Files\Notify; * @since 12.0.0 */ interface IChange { + /** + * @since 12.0.0 + */ public const ADDED = 1; + + /** + * @since 12.0.0 + */ public const REMOVED = 2; + + /** + * @since 12.0.0 + */ public const MODIFIED = 3; + + /** + * @since 12.0.0 + */ public const RENAMED = 4; /** diff --git a/lib/public/Files/Search/ISearchBinaryOperator.php b/lib/public/Files/Search/ISearchBinaryOperator.php index 4b3bbfd21fc..6e088fd4c2c 100644 --- a/lib/public/Files/Search/ISearchBinaryOperator.php +++ b/lib/public/Files/Search/ISearchBinaryOperator.php @@ -27,8 +27,19 @@ namespace OCP\Files\Search; * @since 12.0.0 */ interface ISearchBinaryOperator extends ISearchOperator { + /** + * @since 12.0.0 + */ public const OPERATOR_AND = 'and'; + + /** + * @since 12.0.0 + */ public const OPERATOR_OR = 'or'; + + /** + * @since 12.0.0 + */ public const OPERATOR_NOT = 'not'; /** diff --git a/lib/public/Files/Search/ISearchComparison.php b/lib/public/Files/Search/ISearchComparison.php index 8ebaeced304..eb93ef70bf6 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 @@ -25,16 +26,59 @@ namespace OCP\Files\Search; /** * @since 12.0.0 + * + * @psalm-type ParamSingleValue = \DateTime|int|string|bool + * @psalm-type ParamValue = ParamSingleValue|list<ParamSingleValue> */ interface ISearchComparison extends ISearchOperator { + /** + * @since 12.0.0 + */ public const COMPARE_EQUAL = 'eq'; + + /** + * @since 12.0.0 + */ public const COMPARE_GREATER_THAN = 'gt'; + + /** + * @since 12.0.0 + */ public const COMPARE_GREATER_THAN_EQUAL = 'gte'; + + /** + * @since 12.0.0 + */ public const COMPARE_LESS_THAN = 'lt'; + + /** + * @since 12.0.0 + */ public const COMPARE_LESS_THAN_EQUAL = 'lte'; + + /** + * @since 12.0.0 + */ public const COMPARE_LIKE = 'like'; + + /** + * @since 23.0.0 + */ public const COMPARE_LIKE_CASE_SENSITIVE = 'clike'; + /** + * @since 28.0.0 + */ + public const COMPARE_DEFINED = 'is-defined'; + + /** + * @since 29.0.0 + */ + public const COMPARE_IN = 'in'; + + /** + * @since 23.0.0 + */ public const HINT_PATH_EQ_HASH = 'path_eq_hash'; // transform `path = "$path"` into `path_hash = md5("$path")`, on by default /** @@ -43,7 +87,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 +97,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 ParamValue * @since 12.0.0 */ - public function getValue(); + public function getValue(): string|int|bool|\DateTime|array; } diff --git a/lib/public/Files/Search/ISearchOrder.php b/lib/public/Files/Search/ISearchOrder.php index 3b9e6e6713a..978668907ad 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 @@ -29,7 +30,14 @@ use OCP\Files\FileInfo; * @since 12.0.0 */ interface ISearchOrder { + /** + * @since 12.0.0 + */ public const DIRECTION_ASCENDING = 'asc'; + + /** + * @since 12.0.0 + */ public const DIRECTION_DESCENDING = 'desc'; /** @@ -38,7 +46,7 @@ interface ISearchOrder { * @return string * @since 12.0.0 */ - public function getDirection(); + public function getDirection(): string; /** * The field to sort on @@ -46,7 +54,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/Storage/INotifyStorage.php b/lib/public/Files/Storage/INotifyStorage.php index 18bcf3c6adc..6b1dacc564a 100644 --- a/lib/public/Files/Storage/INotifyStorage.php +++ b/lib/public/Files/Storage/INotifyStorage.php @@ -31,9 +31,24 @@ use OCP\Files\Notify\INotifyHandler; * @since 9.1.0 */ interface INotifyStorage { + /** + * @since 9.1.0 + */ public const NOTIFY_ADDED = 1; + + /** + * @since 9.1.0 + */ public const NOTIFY_REMOVED = 2; + + /** + * @since 9.1.0 + */ public const NOTIFY_MODIFIED = 3; + + /** + * @since 9.1.0 + */ public const NOTIFY_RENAMED = 4; /** diff --git a/lib/public/Files/StorageAuthException.php b/lib/public/Files/StorageAuthException.php index 5fce191b57e..bc39f8e27f5 100644 --- a/lib/public/Files/StorageAuthException.php +++ b/lib/public/Files/StorageAuthException.php @@ -36,7 +36,7 @@ class StorageAuthException extends StorageNotAvailableException { * @since 9.0.0 */ public function __construct($message = '', \Exception $previous = null) { - $l = \OC::$server->getL10N('core'); + $l = \OCP\Util::getL10N('core'); parent::__construct($l->t('Storage unauthorized. %s', [$message]), self::STATUS_UNAUTHORIZED, $previous); } } diff --git a/lib/public/Files/StorageBadConfigException.php b/lib/public/Files/StorageBadConfigException.php index 0e16a24daa8..36d7329d4b5 100644 --- a/lib/public/Files/StorageBadConfigException.php +++ b/lib/public/Files/StorageBadConfigException.php @@ -36,7 +36,7 @@ class StorageBadConfigException extends StorageNotAvailableException { * @since 9.0.0 */ public function __construct($message = '', \Exception $previous = null) { - $l = \OC::$server->getL10N('core'); + $l = \OCP\Util::getL10N('core'); parent::__construct($l->t('Storage incomplete configuration. %s', [$message]), self::STATUS_INCOMPLETE_CONF, $previous); } } diff --git a/lib/public/Files/StorageConnectionException.php b/lib/public/Files/StorageConnectionException.php index 9113faf72a1..d29398172e6 100644 --- a/lib/public/Files/StorageConnectionException.php +++ b/lib/public/Files/StorageConnectionException.php @@ -36,7 +36,7 @@ class StorageConnectionException extends StorageNotAvailableException { * @since 9.0.0 */ public function __construct($message = '', \Exception $previous = null) { - $l = \OC::$server->getL10N('core'); + $l = \OCP\Util::getL10N('core'); parent::__construct($l->t('Storage connection error. %s', [$message]), self::STATUS_NETWORK_ERROR, $previous); } } diff --git a/lib/public/Files/StorageNotAvailableException.php b/lib/public/Files/StorageNotAvailableException.php index f600ef80808..78b004f8518 100644 --- a/lib/public/Files/StorageNotAvailableException.php +++ b/lib/public/Files/StorageNotAvailableException.php @@ -36,15 +36,43 @@ use OCP\HintException; /** * Storage is temporarily not available - * @since 6.0.0 - since 8.2.1 based on HintException + * @since 6.0.0 + * @since 8.2.1 based on HintException */ class StorageNotAvailableException extends HintException { + /** + * @since 8.2.0 + */ public const STATUS_SUCCESS = 0; + + /** + * @since 8.2.0 + */ public const STATUS_ERROR = 1; + + /** + * @since 8.2.0 + */ public const STATUS_INDETERMINATE = 2; + + /** + * @since 8.2.0 + */ public const STATUS_INCOMPLETE_CONF = 3; + + /** + * @since 8.2.0 + */ public const STATUS_UNAUTHORIZED = 4; + + /** + * @since 8.2.0 + */ public const STATUS_TIMEOUT = 5; + + /** + * @since 8.2.0 + */ public const STATUS_NETWORK_ERROR = 6; /** @@ -56,7 +84,7 @@ class StorageNotAvailableException extends HintException { * @since 6.0.0 */ public function __construct($message = '', $code = self::STATUS_ERROR, \Exception $previous = null) { - $l = \OC::$server->getL10N('core'); + $l = \OCP\Util::getL10N('core'); parent::__construct($message, $l->t('Storage is temporarily not available'), $code, $previous); } diff --git a/lib/public/Files/StorageTimeoutException.php b/lib/public/Files/StorageTimeoutException.php index bad88ad35e0..c20711d4181 100644 --- a/lib/public/Files/StorageTimeoutException.php +++ b/lib/public/Files/StorageTimeoutException.php @@ -36,7 +36,7 @@ class StorageTimeoutException extends StorageNotAvailableException { * @since 9.0.0 */ public function __construct($message = '', \Exception $previous = null) { - $l = \OC::$server->getL10N('core'); + $l = \OCP\Util::getL10N('core'); parent::__construct($l->t('Storage connection timeout. %s', [$message]), self::STATUS_TIMEOUT, $previous); } } 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..55feefc4f12 --- /dev/null +++ b/lib/public/FilesMetadata/IFilesMetadataManager.php @@ -0,0 +1,169 @@ +<?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 of 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; + + /** + * returns metadata of multiple file ids + * + * @param int[] $fileIds file ids + * + * @return array File ID is the array key, files without metadata are not returned in the array + * @psalm-return array<int, IFilesMetadata> + * @since 28.0.0 + */ + public function getMetadataForFiles(array $fileIds): array; + + /** + * 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..024b21039bc --- /dev/null +++ b/lib/public/FilesMetadata/Model/IFilesMetadata.php @@ -0,0 +1,386 @@ +<?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", + * "etag": "abcd1234", + * "indexed": false, + * "editPermission": 1 + * }, + * "myapp-anothermeta": { + * "value": 42, + * "type": "int", + * "etag": "0987zyxw", + * "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; + + /** + * returns file etag stored during the last update of the metadata key + * + * @param string $key metadata key + * @return string + * @since 29.0.0 + */ + public function getEtag(string $key): string; + + /** + * set file etag + * + * @param string $key metadata key + * @since 29.0.0 + */ + public function setEtag(string $key, string $etag): void; + + /** + * 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..6c551efea81 --- /dev/null +++ b/lib/public/FilesMetadata/Model/IMetadataValueWrapper.php @@ -0,0 +1,352 @@ +<?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; + + /** + * get stored etag value + * + * @return string stored etag + * @since 29.0.0 + */ + public function getEtag(): string; + + /** + * set etag value + * + * @param string $etag etag value + * + * @return self + * @since 29.0.0 + */ + public function setEtag(string $etag): self; + + /** + * @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/FullTextSearch/Exceptions/FullTextSearchIndexNotAvailableException.php b/lib/public/FullTextSearch/Exceptions/FullTextSearchIndexNotAvailableException.php new file mode 100644 index 00000000000..cf3c5905135 --- /dev/null +++ b/lib/public/FullTextSearch/Exceptions/FullTextSearchIndexNotAvailableException.php @@ -0,0 +1,35 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright 2023, Faraz Samapoor <f.samapoor@gmail.com> + * + * @author Faraz Samapoor <f.samapoor@gmail.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\FullTextSearch\Exceptions; + +/** + * @since 28.0.0 + * + * Class FullTextSearchIndexNotAvailableException + * + */ +class FullTextSearchIndexNotAvailableException extends \Exception { +} diff --git a/lib/public/FullTextSearch/Model/IIndex.php b/lib/public/FullTextSearch/Model/IIndex.php index dad10c934de..6403d408a80 100644 --- a/lib/public/FullTextSearch/Model/IIndex.php +++ b/lib/public/FullTextSearch/Model/IIndex.php @@ -43,24 +43,84 @@ namespace OCP\FullTextSearch\Model; * */ interface IIndex { + /** + * @since 15.0.0 + */ public const INDEX_OK = 1; + + /** + * @since 15.0.0 + */ public const INDEX_IGNORE = 2; + /** + * @since 15.0.0 + */ public const INDEX_META = 4; + + /** + * @since 15.0.0 + */ public const INDEX_CONTENT = 8; + + /** + * @since 16.0.0 + */ public const INDEX_PARTS = 16; + + /** + * @since 15.0.0 + */ public const INDEX_FULL = 28; + + /** + * @since 15.0.0 + */ public const INDEX_REMOVE = 32; + + /** + * @since 15.0.0 + */ public const INDEX_DONE = 64; + + /** + * @since 15.0.0 + */ public const INDEX_FAILED = 128; + /** + * @since 15.0.0 + */ public const ERROR_FAILED = 1; + + /** + * @since 15.0.0 + */ public const ERROR_FAILED2 = 2; + + /** + * @since 15.0.0 + */ public const ERROR_FAILED3 = 4; + /** + * @since 15.0.0 + */ public const ERROR_SEV_1 = 1; + + /** + * @since 15.0.0 + */ public const ERROR_SEV_2 = 2; + + /** + * @since 15.0.0 + */ public const ERROR_SEV_3 = 3; + + /** + * @since 15.0.0 + */ public const ERROR_SEV_4 = 4; diff --git a/lib/public/FullTextSearch/Model/IIndexDocument.php b/lib/public/FullTextSearch/Model/IIndexDocument.php index b659c2b33f1..b043c7d8bb1 100644 --- a/lib/public/FullTextSearch/Model/IIndexDocument.php +++ b/lib/public/FullTextSearch/Model/IIndexDocument.php @@ -41,7 +41,14 @@ namespace OCP\FullTextSearch\Model; * @since 15.0.0 */ interface IIndexDocument { + /** + * @since 15.0.0 + */ public const NOT_ENCODED = 0; + + /** + * @since 15.0.0 + */ public const ENCODED_BASE64 = 1; diff --git a/lib/public/FullTextSearch/Model/IRunner.php b/lib/public/FullTextSearch/Model/IRunner.php index c03126500cb..09782e27275 100644 --- a/lib/public/FullTextSearch/Model/IRunner.php +++ b/lib/public/FullTextSearch/Model/IRunner.php @@ -40,8 +40,19 @@ namespace OCP\FullTextSearch\Model; * */ interface IRunner { + /** + * @since 15.0.0 + */ public const RESULT_TYPE_SUCCESS = 1; + + /** + * @since 15.0.0 + */ public const RESULT_TYPE_WARNING = 4; + + /** + * @since 15.0.0 + */ public const RESULT_TYPE_FAIL = 9; diff --git a/lib/public/FullTextSearch/Model/ISearchRequestSimpleQuery.php b/lib/public/FullTextSearch/Model/ISearchRequestSimpleQuery.php index 91f3c6dfe1b..15c5f98d7b8 100644 --- a/lib/public/FullTextSearch/Model/ISearchRequestSimpleQuery.php +++ b/lib/public/FullTextSearch/Model/ISearchRequestSimpleQuery.php @@ -38,16 +38,59 @@ namespace OCP\FullTextSearch\Model; * */ interface ISearchRequestSimpleQuery { + /** + * @since 17.0.0 + */ public const COMPARE_TYPE_TEXT = 1; + + /** + * @since 17.0.0 + */ public const COMPARE_TYPE_KEYWORD = 2; + + /** + * @since 17.0.0 + */ public const COMPARE_TYPE_INT_EQ = 3; + + /** + * @since 17.0.0 + */ public const COMPARE_TYPE_INT_GTE = 4; + + /** + * @since 17.0.0 + */ public const COMPARE_TYPE_INT_GT = 5; + + /** + * @since 17.0.0 + */ public const COMPARE_TYPE_INT_LTE = 6; + + /** + * @since 17.0.0 + */ public const COMPARE_TYPE_INT_LT = 7; + + /** + * @since 17.0.0 + */ public const COMPARE_TYPE_BOOL = 8; + + /** + * @since 17.0.0 + */ public const COMPARE_TYPE_ARRAY = 9; + + /** + * @since 17.0.0 + */ public const COMPARE_TYPE_REGEX = 10; + + /** + * @since 17.0.0 + */ public const COMPARE_TYPE_WILDCARD = 11; 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..764c810f986 100644 --- a/lib/public/GroupInterface.php +++ b/lib/public/GroupInterface.php @@ -38,15 +38,44 @@ namespace OCP; interface GroupInterface { /** * actions that user backends can define + * + * @since 12.0.0 */ public const CREATE_GROUP = 0x00000001; + + /** + * @since 12.0.0 + */ public const DELETE_GROUP = 0x00000010; + + /** + * @since 12.0.0 + */ public const ADD_TO_GROUP = 0x00000100; + + /** + * @since 12.0.0 + * @deprecated 29.0.0 + */ public const REMOVE_FROM_GOUP = 0x00001000; // oops + + /** + * @since 12.0.0 + */ public const REMOVE_FROM_GROUP = 0x00001000; + //OBSOLETE const GET_DISPLAYNAME = 0x00010000; + + /** + * @since 12.0.0 + */ public const COUNT_USERS = 0x00100000; + + /** + * @since 12.0.0 + */ public const GROUP_DETAILS = 0x01000000; + /** * @since 13.0.0 */ @@ -86,7 +115,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 +128,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/Http/Client/IClient.php b/lib/public/Http/Client/IClient.php index fb1760c25f2..c6b7cafe73f 100644 --- a/lib/public/Http/Client/IClient.php +++ b/lib/public/Http/Client/IClient.php @@ -208,6 +208,47 @@ interface IClient { public function options(string $uri, array $options = []): IResponse; /** + * Get the response of a Throwable thrown by the request methods when possible + * + * @param \Throwable $e + * @return IResponse + * @throws \Throwable When $e did not have a response + * @since 29.0.0 + */ + public function getResponseFromThrowable(\Throwable $e): IResponse; + + /** + * Sends a HTTP request + * @param string $method The HTTP method to use + * @param string $uri + * @param array $options Array such as + * 'query' => [ + * 'field' => 'abc', + * 'other_field' => '123', + * 'file_name' => fopen('/path/to/file', 'r'), + * ], + * 'headers' => [ + * 'foo' => 'bar', + * ], + * 'cookies' => [ + * 'foo' => 'bar', + * ], + * 'allow_redirects' => [ + * 'max' => 10, // allow at most 10 redirects. + * 'strict' => true, // use "strict" RFC compliant redirects. + * 'referer' => true, // add a Referer header + * 'protocols' => ['https'] // only allow https URLs + * ], + * 'sink' => '/path/to/file', // save to a file or a stream + * 'verify' => true, // bool or string to CA file + * 'debug' => true, + * @return IResponse + * @throws \Exception If the request could not get completed + * @since 29.0.0 + */ + public function request(string $method, string $uri, array $options = []): IResponse; + + /** * Sends an asynchronous GET request * @param string $uri * @param array $options Array such as diff --git a/lib/public/Http/WellKnown/JrdResponse.php b/lib/public/Http/WellKnown/JrdResponse.php index 7a25f8fe0c3..077b58dc1d9 100644 --- a/lib/public/Http/WellKnown/JrdResponse.php +++ b/lib/public/Http/WellKnown/JrdResponse.php @@ -127,10 +127,10 @@ final class JrdResponse implements IResponse { * @since 21.0.0 */ public function addLink(string $rel, - ?string $type, - ?string $href, - ?array $titles = [], - ?array $properties = []): self { + ?string $type, + ?string $href, + ?array $titles = [], + ?array $properties = []): self { $this->links[] = array_filter([ 'rel' => $rel, 'type' => $type, diff --git a/lib/public/IAppConfig.php b/lib/public/IAppConfig.php index cf387a8a44c..afcdf67f92c 100644 --- a/lib/public/IAppConfig.php +++ b/lib/public/IAppConfig.php @@ -1,9 +1,12 @@ <?php + +declare(strict_types=1); /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Joas Schilling <coding@schilljs.com> + * @author Maxence Lange <maxence@artificial-owl.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> @@ -26,28 +29,486 @@ */ namespace OCP; +use OCP\Exceptions\AppConfigUnknownKeyException; + /** * This class provides an easy way for apps to store config values in the * database. + * + * **Note:** since 29.0.0, it supports **lazy loading** + * + * ### What is lazy loading ? + * In order to avoid loading useless config values into memory for each request, + * only non-lazy values are now loaded. + * + * Once a value that is lazy is requested, all lazy values will be loaded. + * + * Similarly, some methods from this class are marked with a warning about ignoring + * lazy loading. Use them wisely and only on parts of the code that are called + * during specific requests or actions to avoid loading the lazy values all the time. + * * @since 7.0.0 + * @since 29.0.0 - Supporting types and lazy loading */ interface IAppConfig { + /** @since 29.0.0 */ + public const VALUE_SENSITIVE = 1; + /** @since 29.0.0 */ + public const VALUE_MIXED = 2; + /** @since 29.0.0 */ + public const VALUE_STRING = 4; + /** @since 29.0.0 */ + public const VALUE_INT = 8; + /** @since 29.0.0 */ + public const VALUE_FLOAT = 16; + /** @since 29.0.0 */ + public const VALUE_BOOL = 32; + /** @since 29.0.0 */ + public const VALUE_ARRAY = 64; + /** - * check if a key is set in the appconfig - * @param string $app - * @param string $key - * @return bool + * Get list of all apps that have at least one config value stored in database + * + * **WARNING:** ignore lazy filtering, all config values are loaded from database + * + * @return string[] list of app ids * @since 7.0.0 */ - public function hasKey($app, $key); + public function getApps(): array; + + /** + * Returns all keys stored in database, related to an app. + * Please note that the values are not returned. + * + * **WARNING:** ignore lazy filtering, all config values are loaded from database + * + * @param string $app id of the app + * + * @return string[] list of stored config keys + * @since 29.0.0 + */ + public function getKeys(string $app): array; + + /** + * Check if a key exists in the list of stored config values. + * + * @param string $app id of the app + * @param string $key config key + * @param bool $lazy search within lazy loaded config + * + * @return bool TRUE if key exists + * @since 29.0.0 Added the $lazy argument + * @since 7.0.0 + */ + public function hasKey(string $app, string $key, ?bool $lazy = false): bool; + + /** + * best way to see if a value is set as sensitive (not displayed in report) + * + * @param string $app id of the app + * @param string $key config key + * @param bool|null $lazy search within lazy loaded config + * + * @return bool TRUE if value is sensitive + * @throws AppConfigUnknownKeyException if config key is not known + * @since 29.0.0 + */ + public function isSensitive(string $app, string $key, ?bool $lazy = false): bool; + + /** + * Returns if the config key stored in database is lazy loaded + * + * **WARNING:** ignore lazy filtering, all config values are loaded from database + * + * @param string $app id of the app + * @param string $key config key + * + * @return bool TRUE if config is lazy loaded + * @throws AppConfigUnknownKeyException if config key is not known + * @see IAppConfig for details about lazy loading + * @since 29.0.0 + */ + public function isLazy(string $app, string $key): bool; + + /** + * List all config values from an app with config key starting with $key. + * Returns an array with config key as key, stored value as value. + * + * **WARNING:** ignore lazy filtering, all config values are loaded from database + * + * @param string $app id of the app + * @param string $prefix config keys prefix to search, can be empty. + * @param bool $filtered filter sensitive config values + * + * @return array<string, string> [configKey => configValue] + * @since 29.0.0 + */ + public function getAllValues(string $app, string $prefix = '', bool $filtered = false): array; + + /** + * List all apps storing a specific config key and its stored value. + * Returns an array with appId as key, stored value as value. + * + * @param string $key config key + * @param bool $lazy search within lazy loaded config + * + * @return array<string, string|int|float|bool|array> [appId => configValue] + * @since 29.0.0 + */ + public function searchValues(string $key, bool $lazy = false): array; + + /** + * Get config value assigned to a config key. + * If config key is not found in database, default value is returned. + * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE. + * + * @param string $app id of the app + * @param string $key config key + * @param string $default default value + * @param bool $lazy search within lazy loaded config + * + * @return string stored config value or $default if not set in database + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + * @see getValueInt() + * @see getValueFloat() + * @see getValueBool() + * @see getValueArray() + */ + public function getValueString(string $app, string $key, string $default = '', bool $lazy = false): string; + + /** + * Get config value assigned to a config key. + * If config key is not found in database, default value is returned. + * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE. + * + * @param string $app id of the app + * @param string $key config key + * @param int $default default value + * @param bool $lazy search within lazy loaded config + * + * @return int stored config value or $default if not set in database + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + * @see getValueString() + * @see getValueFloat() + * @see getValueBool() + * @see getValueArray() + */ + public function getValueInt(string $app, string $key, int $default = 0, bool $lazy = false): int; + + /** + * Get config value assigned to a config key. + * If config key is not found in database, default value is returned. + * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE. + * + * @param string $app id of the app + * @param string $key config key + * @param float $default default value + * @param bool $lazy search within lazy loaded config + * + * @return float stored config value or $default if not set in database + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + * @see getValueString() + * @see getValueInt() + * @see getValueBool() + * @see getValueArray() + */ + public function getValueFloat(string $app, string $key, float $default = 0, bool $lazy = false): float; + + /** + * Get config value assigned to a config key. + * If config key is not found in database, default value is returned. + * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE. + * + * @param string $app id of the app + * @param string $key config key + * @param bool $default default value + * @param bool $lazy search within lazy loaded config + * + * @return bool stored config value or $default if not set in database + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + * @see getValueString() + * @see getValueInt() + * @see getValueFloat() + * @see getValueArray() + */ + public function getValueBool(string $app, string $key, bool $default = false, bool $lazy = false): bool; + + /** + * Get config value assigned to a config key. + * If config key is not found in database, default value is returned. + * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE. + * + * @param string $app id of the app + * @param string $key config key + * @param array $default default value + * @param bool $lazy search within lazy loaded config + * + * @return array stored config value or $default if not set in database + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + * @see getValueString() + * @see getValueInt() + * @see getValueFloat() + * @see getValueBool() + */ + public function getValueArray(string $app, string $key, array $default = [], bool $lazy = false): array; + + /** + * returns the type of config value + * + * **WARNING:** ignore lazy filtering, all config values are loaded from database + * + * @param string $app id of the app + * @param string $key config key + * + * @return int + * @throws AppConfigUnknownKeyException + * @since 29.0.0 + * @see VALUE_STRING + * @see VALUE_INT + * @see VALUE_FLOAT + * @see VALUE_BOOL + * @see VALUE_ARRAY + */ + public function getValueType(string $app, string $key): int; + + /** + * Store a config key and its value in database + * + * If config key is already known with the exact same config value, the database is not updated. + * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded. + * + * If config value was previously stored as sensitive or lazy loaded, status cannot be altered without using {@see deleteKey()} first + * + * @param string $app id of the app + * @param string $key config key + * @param string $value config value + * @param bool $sensitive if TRUE value will be hidden when listing config values. + * @param bool $lazy set config as lazy loaded + * + * @return bool TRUE if value was different, therefor updated in database + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + * @see setValueInt() + * @see setValueFloat() + * @see setValueBool() + * @see setValueArray() + */ + public function setValueString(string $app, string $key, string $value, bool $lazy = false, bool $sensitive = false): bool; + + /** + * Store a config key and its value in database + * + * When handling huge value around and/or above 2,147,483,647, a debug log will be generated + * on 64bits system, as php int type reach its limit (and throw an exception) on 32bits when using huge numbers. + * + * When using huge numbers, it is advised to use {@see \OCP\Util::numericToNumber()} and {@see setValueString()} + * + * If config key is already known with the exact same config value, the database is not updated. + * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded. + * + * If config value was previously stored as sensitive or lazy loaded, status cannot be altered without using {@see deleteKey()} first + * + * @param string $app id of the app + * @param string $key config key + * @param int $value config value + * @param bool $sensitive if TRUE value will be hidden when listing config values. + * @param bool $lazy set config as lazy loaded + * + * @return bool TRUE if value was different, therefor updated in database + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + * @see setValueString() + * @see setValueFloat() + * @see setValueBool() + * @see setValueArray() + */ + public function setValueInt(string $app, string $key, int $value, bool $lazy = false, bool $sensitive = false): bool; + + /** + * Store a config key and its value in database. + * + * If config key is already known with the exact same config value, the database is not updated. + * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded. + * + * If config value was previously stored as sensitive or lazy loaded, status cannot be altered without using {@see deleteKey()} first + * + * @param string $app id of the app + * @param string $key config key + * @param float $value config value + * @param bool $sensitive if TRUE value will be hidden when listing config values. + * @param bool $lazy set config as lazy loaded + * + * @return bool TRUE if value was different, therefor updated in database + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + * @see setValueString() + * @see setValueInt() + * @see setValueBool() + * @see setValueArray() + */ + public function setValueFloat(string $app, string $key, float $value, bool $lazy = false, bool $sensitive = false): bool; + + /** + * Store a config key and its value in database + * + * If config key is already known with the exact same config value, the database is not updated. + * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded. + * + * If config value was previously stored as lazy loaded, status cannot be altered without using {@see deleteKey()} first + * + * @param string $app id of the app + * @param string $key config key + * @param bool $value config value + * @param bool $lazy set config as lazy loaded + * + * @return bool TRUE if value was different, therefor updated in database + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + * @see setValueString() + * @see setValueInt() + * @see setValueFloat() + * @see setValueArray() + */ + public function setValueBool(string $app, string $key, bool $value, bool $lazy = false): bool; + + /** + * Store a config key and its value in database + * + * If config key is already known with the exact same config value, the database is not updated. + * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded. + * + * If config value was previously stored as sensitive or lazy loaded, status cannot be altered without using {@see deleteKey()} first + * + * @param string $app id of the app + * @param string $key config key + * @param array $value config value + * @param bool $sensitive if TRUE value will be hidden when listing config values. + * @param bool $lazy set config as lazy loaded + * + * @return bool TRUE if value was different, therefor updated in database + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + * @see setValueString() + * @see setValueInt() + * @see setValueFloat() + * @see setValueBool() + */ + public function setValueArray(string $app, string $key, array $value, bool $lazy = false, bool $sensitive = false): bool; + + /** + * switch sensitive status of a config value + * + * **WARNING:** ignore lazy filtering, all config values are loaded from database + * + * @param string $app id of the app + * @param string $key config key + * @param bool $sensitive TRUE to set as sensitive, FALSE to unset + * + * @return bool TRUE if database update were necessary + * @since 29.0.0 + */ + public function updateSensitive(string $app, string $key, bool $sensitive): bool; + + /** + * switch lazy loading status of a config value + * + * @param string $app id of the app + * @param string $key config key + * @param bool $lazy TRUE to set as lazy loaded, FALSE to unset + * + * @return bool TRUE if database update was necessary + * @since 29.0.0 + */ + public function updateLazy(string $app, string $key, bool $lazy): bool; + + /** + * returns an array contains details about a config value + * + * ``` + * [ + * "app" => "myapp", + * "key" => "mykey", + * "value" => "its_value", + * "lazy" => false, + * "type" => 4, + * "typeString" => "string", + * 'sensitive' => true + * ] + * ``` + * + * @param string $app id of the app + * @param string $key config key + * + * @return array + * @throws AppConfigUnknownKeyException if config key is not known in database + * @since 29.0.0 + */ + public function getDetails(string $app, string $key): array; + + /** + * Convert string like 'string', 'integer', 'float', 'bool' or 'array' to + * to bitflag {@see VALUE_STRING}, {@see VALUE_INT}, {@see VALUE_FLOAT}, + * {@see VALUE_BOOL} and {@see VALUE_ARRAY} + * + * @param string $type + * + * @return int + * @since 29.0.0 + */ + public function convertTypeToInt(string $type): int; + + /** + * Convert bitflag {@see VALUE_STRING}, {@see VALUE_INT}, {@see VALUE_FLOAT}, + * {@see VALUE_BOOL} and {@see VALUE_ARRAY} to human-readable string + * + * @param int $type + * + * @return string + * @since 29.0.0 + */ + public function convertTypeToString(int $type): string; + + /** + * Delete single config key from database. + * + * @param string $app id of the app + * @param string $key config key + * @since 29.0.0 + */ + public function deleteKey(string $app, string $key): void; + + /** + * delete all config keys linked to an app + * + * @param string $app id of the app + * @since 29.0.0 + */ + public function deleteApp(string $app): void; + + /** + * Clear the cache. + * + * The cache will be rebuilt only the next time a config value is requested. + * + * @param bool $reload set to TRUE to refill cache instantly after clearing it + * @since 29.0.0 + */ + public function clearCache(bool $reload = false): void; /** * get multiply values, either the app or key can be used as wildcard by setting it to false * * @param string|false $key * @param string|false $app + * * @return array|false * @since 7.0.0 + * @deprecated 29.0.0 Use {@see getAllValues()} or {@see searchValues()} */ public function getValues($app, $key); @@ -55,18 +516,10 @@ interface IAppConfig { * get all values of the app or and filters out sensitive data * * @param string $app + * * @return array * @since 12.0.0 + * @deprecated 29.0.0 Use {@see getAllValues()} or {@see searchValues()} */ public function getFilteredValues($app); - - /** - * Get all apps using the config - * @return string[] an array of app ids - * - * This function returns a list of all apps that have at least one - * entry in the appconfig table. - * @since 7.0.0 - */ - public function getApps(); } 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/IConfig.php b/lib/public/IConfig.php index 0e7a7523218..706e4776221 100644 --- a/lib/public/IConfig.php +++ b/lib/public/IConfig.php @@ -126,6 +126,7 @@ interface IConfig { * @param string $appName the appName that we stored the value under * @return string[] the keys stored for the app * @since 8.0.0 + * @deprecated 29.0.0 Use {@see IAppConfig} directly */ public function getAppKeys($appName); @@ -137,6 +138,7 @@ interface IConfig { * @param string $value the value that should be stored * @return void * @since 6.0.0 + * @deprecated 29.0.0 Use {@see IAppConfig} directly */ public function setAppValue($appName, $key, $value); @@ -146,8 +148,10 @@ interface IConfig { * @param string $appName the appName that we stored the value under * @param string $key the key of the value, under which it was saved * @param string $default the default value to be returned if the value isn't set + * * @return string the saved value * @since 6.0.0 - parameter $default was added in 7.0.0 + * @deprecated 29.0.0 Use {@see IAppConfig} directly */ public function getAppValue($appName, $key, $default = ''); @@ -157,6 +161,7 @@ interface IConfig { * @param string $appName the appName that we stored the value under * @param string $key the key of the value, under which it was saved * @since 8.0.0 + * @deprecated 29.0.0 Use {@see IAppConfig} directly */ public function deleteAppValue($appName, $key); @@ -165,6 +170,7 @@ interface IConfig { * * @param string $appName the appName the configs are stored under * @since 8.0.0 + * @deprecated 29.0.0 Use {@see IAppConfig} directly */ public function deleteAppValues($appName); diff --git a/lib/public/IDBConnection.php b/lib/public/IDBConnection.php index fe0267facc5..5613aa3743b 100644 --- a/lib/public/IDBConnection.php +++ b/lib/public/IDBConnection.php @@ -46,6 +46,26 @@ use OCP\DB\QueryBuilder\IQueryBuilder; */ 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. * * @return \OCP\DB\QueryBuilder\IQueryBuilder @@ -339,4 +359,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/IMemcacheTTL.php b/lib/public/IMemcacheTTL.php index 8424e5b2bfa..250e727308b 100644 --- a/lib/public/IMemcacheTTL.php +++ b/lib/public/IMemcacheTTL.php @@ -35,5 +35,22 @@ interface IMemcacheTTL extends IMemcache { * @param int $ttl time to live in seconds * @since 8.2.2 */ - public function setTTL($key, $ttl); + public function setTTL(string $key, int $ttl); + + /** + * Get the ttl for an existing value, in seconds till expiry + * + * @return int|false + * @since 27 + */ + public function getTTL(string $key): int|false; + /** + * Set the ttl for an existing value if the value matches + * + * @param string $key + * @param mixed $value + * @param int $ttl time to live in seconds + * @since 27 + */ + public function compareSetTTL(string $key, $value, int $ttl): 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/IPreview.php b/lib/public/IPreview.php index 2758eba8d63..d8e17b5b029 100644 --- a/lib/public/IPreview.php +++ b/lib/public/IPreview.php @@ -38,7 +38,14 @@ use OCP\Files\SimpleFS\ISimpleFile; * @since 6.0.0 */ interface IPreview { + /** + * @since 11.0.0 + */ public const MODE_FILL = 'fill'; + + /** + * @since 11.0.0 + */ public const MODE_COVER = 'cover'; /** diff --git a/lib/public/IURLGenerator.php b/lib/public/IURLGenerator.php index 8229d51f231..396867e22b3 100644 --- a/lib/public/IURLGenerator.php +++ b/lib/public/IURLGenerator.php @@ -41,6 +41,7 @@ interface IURLGenerator { * This is a copy of the frontend regex in core/src/OCP/comments.js, make sure to adjust both when changing * * @since 25.0.0 + * @since 29.0.0 changed to match localhost and hostnames with ports */ public const URL_REGEX = '/' . self::URL_REGEX_NO_MODIFIERS . '/mi'; @@ -50,8 +51,9 @@ interface IURLGenerator { * This is a copy of the frontend regex in core/src/OCP/comments.js, make sure to adjust both when changing * * @since 25.0.0 + * @since 29.0.0 changed to match localhost and hostnames with ports */ - public const URL_REGEX_NO_MODIFIERS = '(\s|\n|^)(https?:\/\/)((?:[-A-Z0-9+_]+\.)+[-A-Z]+(?:\/[-A-Z0-9+&@#%?=~_|!:,.;()]*)*)(\s|\n|$)'; + public const URL_REGEX_NO_MODIFIERS = '(\s|\n|^)(https?:\/\/)([-A-Z0-9+_.]+(?::[0-9]+)?(?:\/[-A-Z0-9+&@#%?=~_|!:,.;()]*)*)(\s|\n|$)'; /** * Returns the URL for a route @@ -95,6 +97,7 @@ interface IURLGenerator { * @param string $appName the name of the app * @param string $file the name of the file * @return string the url + * @throws \RuntimeException If the image does not exist * @since 6.0.0 */ public function imagePath(string $appName, string $file): 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/Log/Audit/CriticalActionPerformedEvent.php b/lib/public/Log/Audit/CriticalActionPerformedEvent.php index 79c67e5b8bd..45786a5c980 100644 --- a/lib/public/Log/Audit/CriticalActionPerformedEvent.php +++ b/lib/public/Log/Audit/CriticalActionPerformedEvent.php @@ -49,8 +49,8 @@ class CriticalActionPerformedEvent extends Event { * @since 22.0.0 */ public function __construct(string $logMessage, - array $parameters = [], - bool $obfuscateParameters = false) { + array $parameters = [], + bool $obfuscateParameters = false) { parent::__construct(); $this->logMessage = $logMessage; $this->parameters = $parameters; diff --git a/lib/public/Log/ILogFactory.php b/lib/public/Log/ILogFactory.php index e0128d6b11c..e51a674afbd 100644 --- a/lib/public/Log/ILogFactory.php +++ b/lib/public/Log/ILogFactory.php @@ -24,7 +24,6 @@ */ namespace OCP\Log; -use OCP\ILogger; use Psr\Log\LoggerInterface; /** @@ -42,15 +41,6 @@ interface ILogFactory { /** * @param string $path - * @return ILogger - * @since 14.0.0 - * @deprecated 22.0.0 Use \OCP\Log\ILogFactory::getCustomPsrLogger - * @see \OCP\Log\ILogFactory::getCustomPsrLogger - */ - public function getCustomLogger(string $path): ILogger; - - /** - * @param string $path * @param string $type * @param string $tag * @return LoggerInterface 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..c1e23cd3cd3 --- /dev/null +++ b/lib/public/Search/FilterDefinition.php @@ -0,0 +1,136 @@ +<?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 { + /** + * @since 28.0.0 + */ + public const TYPE_BOOL = 'bool'; + + /** + * @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_STRING = 'string'; + + /** + * @since 28.0.0 + */ + public const TYPE_STRINGS = 'strings'; + + /** + * @since 28.0.0 + */ + public const TYPE_DATETIME = 'datetime'; + + /** + * @since 28.0.0 + */ + public const TYPE_PERSON = 'person'; + + /** + * @since 28.0.0 + */ + public const TYPE_NC_USER = 'nc-user'; + + /** + * @since 28.0.0 + */ + 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/Search/SearchResult.php b/lib/public/Search/SearchResult.php index 9ac8b28fb8b..892c60777b3 100644 --- a/lib/public/Search/SearchResult.php +++ b/lib/public/Search/SearchResult.php @@ -54,9 +54,9 @@ final class SearchResult implements JsonSerializable { * @since 20.0.0 */ private function __construct(string $name, - bool $isPaginated, - array $entries, - $cursor = null) { + bool $isPaginated, + array $entries, + $cursor = null) { $this->name = $name; $this->isPaginated = $isPaginated; $this->entries = $entries; @@ -87,8 +87,8 @@ final class SearchResult implements JsonSerializable { * @since 20.0.0 */ public static function paginated(string $name, - array $entries, - $cursor): self { + array $entries, + $cursor): self { return new self( $name, true, diff --git a/lib/public/Search/SearchResultEntry.php b/lib/public/Search/SearchResultEntry.php index 86a3f08cfe6..f4ff1ec2a5c 100644 --- a/lib/public/Search/SearchResultEntry.php +++ b/lib/public/Search/SearchResultEntry.php @@ -98,11 +98,11 @@ class SearchResultEntry implements JsonSerializable { * @since 20.0.0 */ public function __construct(string $thumbnailUrl, - string $title, - string $subline, - string $resourceUrl, - string $icon = '', - bool $rounded = false) { + string $title, + string $subline, + string $resourceUrl, + string $icon = '', + bool $rounded = false) { $this->thumbnailUrl = $thumbnailUrl; $this->title = $title; $this->subline = $subline; diff --git a/lib/public/Security/ISecureRandom.php b/lib/public/Security/ISecureRandom.php index 3634ebf99f7..530befb0257 100644 --- a/lib/public/Security/ISecureRandom.php +++ b/lib/public/Security/ISecureRandom.php @@ -41,17 +41,36 @@ namespace OCP\Security; interface ISecureRandom { /** * Flags for characters that can be used for <code>generate($length, $characters)</code> + * @since 8.0.0 */ public const CHAR_UPPER = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + + /** + * @since 8.0.0 + */ public const CHAR_LOWER = 'abcdefghijklmnopqrstuvwxyz'; + + /** + * @since 8.0.0 + */ public const CHAR_DIGITS = '0123456789'; + + /** + * @since 8.0.0 + */ public const CHAR_SYMBOLS = '!\"#$%&\\\'()*+,-./:;<=>?@[\]^_`{|}~'; + + /** + * @since 12.0.0 + */ public const CHAR_ALPHANUMERIC = self::CHAR_UPPER . self::CHAR_LOWER . self::CHAR_DIGITS; /** * Characters that can be used for <code>generate($length, $characters)</code>, to - * generate human readable random strings. Lower- and upper-case characters and digits + * generate human-readable random strings. Lower- and upper-case characters and digits * are included. Characters which are ambiguous are excluded, such as I, l, and 1 and so on. + * + * @since 23.0.0 */ public const CHAR_HUMAN_READABLE = 'abcdefgijkmnopqrstwxyzABCDEFGHJKLMNPQRSTWXYZ23456789'; @@ -64,5 +83,5 @@ interface ISecureRandom { * @since 8.0.0 */ public function generate(int $length, - string $characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'): string; + string $characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'): string; } diff --git a/lib/public/Security/RateLimiting/ILimiter.php b/lib/public/Security/RateLimiting/ILimiter.php new file mode 100644 index 00000000000..cfc7387664d --- /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/Settings/DeclarativeSettingsTypes.php b/lib/public/Settings/DeclarativeSettingsTypes.php new file mode 100644 index 00000000000..01e20ee7cc9 --- /dev/null +++ b/lib/public/Settings/DeclarativeSettingsTypes.php @@ -0,0 +1,145 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2023 Andrey Borysenko <andrey.borysenko@nextcloud.com> + * + * @author Andrey Borysenko <andrey.borysenko@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\Settings; + +/** + * Declarative settings types supported in the IDeclarativeSettingsForm forms + * + * @since 29.0.0 + */ +final class DeclarativeSettingsTypes { + /** + * IDeclarativeSettingsForm section_type which is determines where the form is displayed + * + * @since 29.0.0 + */ + public const SECTION_TYPE_ADMIN = 'admin'; + + /** + * IDeclarativeSettingsForm section_type which is determines where the form is displayed + * + * @since 29.0.0 + */ + public const SECTION_TYPE_PERSONAL = 'personal'; + + /** + * IDeclarativeSettingsForm storage_type which is determines where and how the config value is stored + * + * + * For `external` storage_type the app implementing \OCP\Settings\SetDeclarativeSettingsValueEvent and \OCP\Settings\GetDeclarativeSettingsValueEvent events is responsible for storing and retrieving the config value. + * + * @since 29.0.0 + */ + public const STORAGE_TYPE_EXTERNAL = 'external'; + + /** + * IDeclarativeSettingsForm storage_type which is determines where and how the config value is stored + * + * For `internal` storage_type the config value is stored in default `appconfig` and `preferences` tables. + * For `external` storage_type the app implementing \OCP\Settings\SetDeclarativeSettingsValueEvent and \OCP\Settings\GetDeclarativeSettingsValueEvent events is responsible for storing and retrieving the config value. + * + * @since 29.0.0 + */ + public const STORAGE_TYPE_INTERNAL = 'internal'; + + /** + * NcInputField type text + * + * @since 29.0.0 + */ + public const TEXT = 'text'; + + /** + * NcInputField type password + * + * @since 29.0.0 + */ + public const PASSWORD = 'password'; + + /** + * NcInputField type email + * + * @since 29.0.0 + */ + public const EMAIL = 'email'; + + /** + * NcInputField type tel + * + * @since 29.0.0 + */ + public const TEL = 'tel'; + + /** + * NcInputField type url + * + * @since 29.0.0 + */ + public const URL = 'url'; + + /** + * NcInputField type number + * + * @since 29.0.0 + */ + public const NUMBER = 'number'; + + /** + * NcCheckboxRadioSwitch type checkbox + * + * @since 29.0.0 + */ + public const CHECKBOX = 'checkbox'; + + /** + * Multiple NcCheckboxRadioSwitch type checkbox representing a one config value (saved as JSON object) + * + * @since 29.0.0 + */ + public const MULTI_CHECKBOX = 'multi-checkbox'; + + /** + * NcCheckboxRadioSwitch type radio + * + * @since 29.0.0 + */ + public const RADIO = 'radio'; + + /** + * NcSelect + * + * @since 29.0.0 + */ + public const SELECT = 'select'; + + /** + * Multiple NcSelect representing a one config value (saved as JSON array) + * + * @since 29.0.0 + */ + public const MULTI_SELECT = 'multi-select'; +} diff --git a/lib/public/Settings/Events/DeclarativeSettingsGetValueEvent.php b/lib/public/Settings/Events/DeclarativeSettingsGetValueEvent.php new file mode 100644 index 00000000000..925bf9fe711 --- /dev/null +++ b/lib/public/Settings/Events/DeclarativeSettingsGetValueEvent.php @@ -0,0 +1,105 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2023 Kate Döen <kate.doeen@nextcloud.com> + * + * @author Kate Döen <kate.doeen@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\Settings\Events; + +use Exception; +use OCP\EventDispatcher\Event; +use OCP\IUser; +use OCP\Settings\IDeclarativeSettingsForm; + +/** + * @psalm-import-type DeclarativeSettingsValueTypes from IDeclarativeSettingsForm + * + * @since 29.0.0 + */ +class DeclarativeSettingsGetValueEvent extends Event { + /** + * @var ?DeclarativeSettingsValueTypes + */ + private mixed $value = null; + + /** + * @since 29.0.0 + */ + public function __construct( + private IUser $user, + private string $app, + private string $formId, + private string $fieldId, + ) { + parent::__construct(); + } + + /** + * @since 29.0.0 + */ + public function getUser(): IUser { + return $this->user; + } + + /** + * @since 29.0.0 + */ + public function getApp(): string { + return $this->app; + } + + /** + * @since 29.0.0 + */ + public function getFormId(): string { + return $this->formId; + } + + /** + * @since 29.0.0 + */ + public function getFieldId(): string { + return $this->fieldId; + } + + /** + * @since 29.0.0 + */ + public function setValue(mixed $value): void { + $this->value = $value; + } + + /** + * @return DeclarativeSettingsValueTypes + * @throws Exception + * + * @since 29.0.0 + */ + public function getValue(): mixed { + if ($this->value === null) { + throw new Exception('Value not set'); + } + + return $this->value; + } +} diff --git a/lib/public/Dashboard/RegisterWidgetEvent.php b/lib/public/Settings/Events/DeclarativeSettingsRegisterFormEvent.php index f0bf049571a..a9ea399fc20 100644 --- a/lib/public/Dashboard/RegisterWidgetEvent.php +++ b/lib/public/Settings/Events/DeclarativeSettingsRegisterFormEvent.php @@ -3,9 +3,9 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020 Julius Härtl <jus@bitgrid.net> + * @copyright Copyright (c) 2023 Kate Döen <kate.doeen@nextcloud.com> * - * @author Julius Härtl <jus@bitgrid.net> + * @author Kate Döen <kate.doeen@nextcloud.com> * * @license GNU AGPL version 3 or any later version * @@ -23,39 +23,31 @@ declare(strict_types=1); * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ -namespace OCP\Dashboard; + +namespace OCP\Settings\Events; use OCP\EventDispatcher\Event; +use OCP\Settings\IDeclarativeManager; +use OCP\Settings\IDeclarativeSettingsForm; /** - * Class RegisterPanelEvent - * - * This event is dispatched to allow apps supporting older Nextcloud versions to - * still register their dashboard panels so that they are only constructed when - * they are needed. Deprecated right away so we can drop it again after 19 is EOL - * and backward compatible apps can use OCP\AppFramework\Bootstrap\IBootstrap + * @psalm-import-type DeclarativeSettingsFormSchemaWithoutValues from IDeclarativeSettingsForm * - * @since 20.0.0 - * @deprecated 20.0.0 + * @since 29.0.0 */ -class RegisterWidgetEvent extends Event { - private $manager; - +class DeclarativeSettingsRegisterFormEvent extends Event { /** - * @param IManager $manager - * @since 20.0.0 + * @since 29.0.0 */ - public function __construct(IManager $manager) { + public function __construct(private IDeclarativeManager $manager) { parent::__construct(); - - $this->manager = $manager; } /** - * @param string $panelClass - * @since 20.0.0 + * @param DeclarativeSettingsFormSchemaWithoutValues $schema + * @since 29.0.0 */ - public function registerWidget(string $panelClass) { - $this->manager->lazyRegisterWidget($panelClass); + public function registerSchema(string $app, array $schema): void { + $this->manager->registerSchema($app, $schema); } } diff --git a/lib/public/Settings/Events/DeclarativeSettingsSetValueEvent.php b/lib/public/Settings/Events/DeclarativeSettingsSetValueEvent.php new file mode 100644 index 00000000000..f94c17681a8 --- /dev/null +++ b/lib/public/Settings/Events/DeclarativeSettingsSetValueEvent.php @@ -0,0 +1,87 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2023 Kate Döen <kate.doeen@nextcloud.com> + * + * @author Kate Döen <kate.doeen@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\Settings\Events; + +use OCP\EventDispatcher\Event; +use OCP\IUser; +use OCP\Settings\IDeclarativeSettingsForm; + +/** + * @psalm-import-type DeclarativeSettingsValueTypes from IDeclarativeSettingsForm + * + * @since 29.0.0 + */ +class DeclarativeSettingsSetValueEvent extends Event { + /** + * @param DeclarativeSettingsValueTypes $value + * @since 29.0.0 + */ + public function __construct( + private IUser $user, + private string $app, + private string $formId, + private string $fieldId, + private mixed $value, + ) { + parent::__construct(); + } + + /** + * @since 29.0.0 + */ + public function getUser(): IUser { + return $this->user; + } + + /** + * @since 29.0.0 + */ + public function getApp(): string { + return $this->app; + } + + /** + * @since 29.0.0 + */ + public function getFormId(): string { + return $this->formId; + } + + /** + * @since 29.0.0 + */ + public function getFieldId(): string { + return $this->fieldId; + } + + /** + * @since 29.0.0 + */ + public function getValue(): mixed { + return $this->value; + } +} diff --git a/lib/public/Settings/IDeclarativeManager.php b/lib/public/Settings/IDeclarativeManager.php new file mode 100644 index 00000000000..ac5bef6ed26 --- /dev/null +++ b/lib/public/Settings/IDeclarativeManager.php @@ -0,0 +1,92 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2023 Kate Döen <kate.doeen@nextcloud.com> + * + * @author Kate Döen <kate.doeen@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\Settings; + +use Exception; +use OC\AppFramework\Middleware\Security\Exceptions\NotAdminException; +use OCP\IUser; + +/** + * @since 29.0.0 + * + * @psalm-import-type DeclarativeSettingsValueTypes from IDeclarativeSettingsForm + * @psalm-import-type DeclarativeSettingsSectionType from IDeclarativeSettingsForm + * @psalm-import-type DeclarativeSettingsFormSchemaWithValues from IDeclarativeSettingsForm + * @psalm-import-type DeclarativeSettingsFormSchemaWithoutValues from IDeclarativeSettingsForm + */ +interface IDeclarativeManager { + /** + * Registers a new declarative settings schema. + * + * @param DeclarativeSettingsFormSchemaWithoutValues $schema + * @since 29.0.0 + */ + public function registerSchema(string $app, array $schema): void; + + /** + * Load all schemas from the registration context and events. + * + * @since 29.0.0 + */ + public function loadSchemas(): void; + + /** + * Gets the IDs of the forms for the given type and section. + * + * @param DeclarativeSettingsSectionType $type + * @param string $section + * @return array<string, list<string>> + * + * @since 29.0.0 + */ + public function getFormIDs(IUser $user, string $type, string $section): array; + + /** + * Gets the forms including the field values for the given type and section. + * + * @param IUser $user Used for reading values from the personal section or for authorization for the admin section. + * @param ?DeclarativeSettingsSectionType $type If it is null the forms will not be filtered by type. + * @param ?string $section If it is null the forms will not be filtered by section. + * @return list<DeclarativeSettingsFormSchemaWithValues> + * + * @since 29.0.0 + */ + public function getFormsWithValues(IUser $user, ?string $type, ?string $section): array; + + /** + * Sets a value for the given field ID. + * + * @param IUser $user Used for storing values in the personal section or for authorization for the admin section. + * @param DeclarativeSettingsValueTypes $value + * + * @throws Exception + * @throws NotAdminException + * + * @since 29.0.0 + */ + public function setValue(IUser $user, string $app, string $formId, string $fieldId, mixed $value): void; +} diff --git a/lib/public/Settings/IDeclarativeSettingsForm.php b/lib/public/Settings/IDeclarativeSettingsForm.php new file mode 100644 index 00000000000..7513af8217c --- /dev/null +++ b/lib/public/Settings/IDeclarativeSettingsForm.php @@ -0,0 +1,81 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2023 Kate Döen <kate.doeen@nextcloud.com> + * + * @author Kate Döen <kate.doeen@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\Settings; + +/** + * @since 29.0.0 + * + * @psalm-type DeclarativeSettingsSectionType = 'admin'|'personal' + * + * @psalm-type DeclarativeSettingsStorageType = 'internal'|'external' + * + * @psalm-type DeclarativeSettingsValueTypes = string|int|float|bool|list<string> + * + * @psalm-type DeclarativeSettingsFormField = array{ + * id: string, + * title: string, + * description?: string, + * type: 'text'|'password'|'email'|'tel'|'url'|'number'|'checkbox'|'multi-checkbox'|'radio'|'select'|'multi-select', + * placeholder?: string, + * label?: string, + * default: mixed, + * options?: list<string|array{name: string, value: mixed}>, + * } + * + * @psalm-type DeclarativeSettingsFormFieldWithValue = DeclarativeSettingsFormField&array{ + * value: DeclarativeSettingsValueTypes, + * } + * + * @psalm-type DeclarativeSettingsFormSchema = array{ + * id: string, + * priority: int, + * section_type: DeclarativeSettingsSectionType, + * section_id: string, + * storage_type: DeclarativeSettingsStorageType, + * title: string, + * description?: string, + * doc_url?: string, + * } + * + * @psalm-type DeclarativeSettingsFormSchemaWithValues = DeclarativeSettingsFormSchema&array{ + * app: string, + * fields: list<DeclarativeSettingsFormFieldWithValue>, + * } + * + * @psalm-type DeclarativeSettingsFormSchemaWithoutValues = DeclarativeSettingsFormSchema&array{ + * fields: list<DeclarativeSettingsFormField>, + * } + */ +interface IDeclarativeSettingsForm { + /** + * Gets the schema that defines the declarative settings form + * + * @return DeclarativeSettingsFormSchemaWithoutValues + * @since 29.0.0 + */ + public function getSchema(): array; +} diff --git a/lib/public/Settings/IManager.php b/lib/public/Settings/IManager.php index 10de596dbea..dbbbf3f57e4 100644 --- a/lib/public/Settings/IManager.php +++ b/lib/public/Settings/IManager.php @@ -6,6 +6,7 @@ * @author Christoph Wurst <christoph@winzerhof-wurst.at> * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> + * @author Kate Döen <kate.doeen@nextcloud.com> * * @license GNU AGPL version 3 or any later version * @@ -34,34 +35,48 @@ use OCP\IUser; interface IManager { /** * @since 9.1.0 + * @deprecated 29.0.0 Use {@see self::SETTINGS_ADMIN} instead */ public const KEY_ADMIN_SETTINGS = 'admin'; /** * @since 9.1.0 + * @deprecated 29.0.0 Use {@see self::SETTINGS_ADMIN} instead */ public const KEY_ADMIN_SECTION = 'admin-section'; /** * @since 13.0.0 + * @deprecated 29.0.0 Use {@see self::SETTINGS_PERSONAL} instead */ public const KEY_PERSONAL_SETTINGS = 'personal'; /** * @since 13.0.0 + * @deprecated 29.0.0 Use {@see self::SETTINGS_PERSONAL} instead */ public const KEY_PERSONAL_SECTION = 'personal-section'; /** - * @param string $type 'admin-section' or 'personal-section' - * @param string $section Class must implement OCP\Settings\ISection + * @since 29.0.0 + */ + public const SETTINGS_ADMIN = 'admin'; + + /** + * @since 29.0.0 + */ + public const SETTINGS_PERSONAL = 'personal'; + + /** + * @psalm-param self::SETTINGS_* $type + * @param class-string<IIconSection> $section * @since 14.0.0 */ public function registerSection(string $type, string $section); /** - * @param string $type 'admin' or 'personal' - * @param string $setting Class must implement OCP\Settings\ISettings + * @psalm-param self::SETTINGS_* $type + * @param class-string<ISettings> $setting * @since 14.0.0 */ public function registerSetting(string $type, string $setting); @@ -69,7 +84,7 @@ interface IManager { /** * returns a list of the admin sections * - * @return array<int, array<int, IIconSection>> array from IConSection[] where key is the priority + * @return array<int, list<IIconSection>> list of sections with priority as key * @since 9.1.0 */ public function getAdminSections(): array; @@ -77,7 +92,7 @@ interface IManager { /** * returns a list of the personal sections * - * @return array array of ISection[] where key is the priority + * @return array<int, list<IIconSection>> list of sections with priority as key * @since 13.0.0 */ public function getPersonalSections(): array; @@ -87,10 +102,10 @@ interface IManager { * * @param string $section the section id for which to load the settings * @param bool $subAdminOnly only return settings sub admins are supposed to see (since 17.0.0) - * @return array<int, array<int, ISettings>> array of ISettings[] where key is the priority + * @return array<int, list<ISettings>> list of settings with priority as key * @since 9.1.0 */ - public function getAdminSettings($section, bool $subAdminOnly = false): array; + public function getAdminSettings(string $section, bool $subAdminOnly = false): array; /** * Returns a list of admin settings that the given user can use for the give section @@ -103,7 +118,7 @@ interface IManager { /** * Returns a list of admin settings that the given user can use. * - * @return array<int, list<ISettings>> The array of admin settings there admin delegation is allowed. + * @return list<ISettings> The array of admin settings there admin delegation is allowed. * @since 23.0.0 */ public function getAllAllowedAdminSettings(IUser $user): array; @@ -112,13 +127,14 @@ interface IManager { * returns a list of the personal settings * * @param string $section the section id for which to load the settings - * @return array array of ISettings[] where key is the priority + * @return array<int, list<ISettings>> list of settings with priority as key * @since 13.0.0 */ - public function getPersonalSettings($section): array; + public function getPersonalSettings(string $section): array; /** * Get a specific section by type and id + * @psalm-param self::SETTINGS_* $type * @since 25.0.0 */ public function getSection(string $type, string $sectionId): ?IIconSection; 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..7ae4cfcba24 --- /dev/null +++ b/lib/public/SetupCheck/SetupResult.php @@ -0,0 +1,204 @@ +<?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; + +use OCP\RichObjectStrings\IValidator; + +/** + * @brief This class is used for storing the result of a setup check + * + * @since 28.0.0 + */ +class SetupResult implements \JsonSerializable { + /** + * @since 28.0.0 + */ + public const SUCCESS = 'success'; + + /** + * @since 28.0.0 + */ + public const INFO = 'info'; + + /** + * @since 28.0.0 + */ + public const WARNING = 'warning'; + + /** + * @since 28.0.0 + */ + 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 + * @throws \OCP\RichObjectStrings\InvalidObjectExeption + * @since 28.0.0 + * @since 28.0.2 Optional parameter ?array $descriptionParameters + * @since 28.0.2 throws \OCP\RichObjectStrings\InvalidObjectExeption + */ + private function __construct( + private string $severity, + private ?string $description = null, + private ?array $descriptionParameters = null, + private ?string $linkToDoc = null, + ) { + if ($description !== null && $descriptionParameters !== null) { + \OCP\Server::get(IValidator::class)->validate($description, $descriptionParameters); + } + } + + /** + * @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 + * @throws \OCP\RichObjectStrings\InvalidObjectExeption + * @since 28.0.0 + * @since 28.0.2 Optional parameter ?array $descriptionParameters + * @since 28.0.2 throws \OCP\RichObjectStrings\InvalidObjectExeption + */ + public static function success(?string $description = null, ?string $linkToDoc = null, ?array $descriptionParameters = null): self { + return new self(self::SUCCESS, $description, $descriptionParameters, $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 + * @throws \OCP\RichObjectStrings\InvalidObjectExeption + * @since 28.0.0 + * @since 28.0.2 Optional parameter ?array $descriptionParameters + * @since 28.0.2 throws \OCP\RichObjectStrings\InvalidObjectExeption + */ + public static function info(?string $description = null, ?string $linkToDoc = null, ?array $descriptionParameters = null): self { + return new self(self::INFO, $description, $descriptionParameters, $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 + * @throws \OCP\RichObjectStrings\InvalidObjectExeption + * @since 28.0.0 + * @since 28.0.2 Optional parameter ?array $descriptionParameters + * @since 28.0.2 throws \OCP\RichObjectStrings\InvalidObjectExeption + */ + public static function warning(?string $description = null, ?string $linkToDoc = null, ?array $descriptionParameters = null): self { + return new self(self::WARNING, $description, $descriptionParameters, $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 + * @throws \OCP\RichObjectStrings\InvalidObjectExeption + * @since 28.0.0 + * @since 28.0.2 Optional parameter ?array $descriptionParameters + * @since 28.0.2 throws \OCP\RichObjectStrings\InvalidObjectExeption + */ + public static function error(?string $description = null, ?string $linkToDoc = null, ?array $descriptionParameters = null): self { + return new self(self::ERROR, $description, $descriptionParameters, $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 description parameters for the setup check result + * + * If this returns null, description must not be treated as rich text + * + * @since 28.0.2 + */ + public function getDescriptionParameters(): ?array { + return $this->descriptionParameters; + } + + /** + * @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, + 'descriptionParameters' => $this->descriptionParameters, + 'linkToDoc' => $this->linkToDoc, + ]; + } +} diff --git a/lib/public/Share.php b/lib/public/Share.php index 9499cdb14b6..2004f77cf0a 100644 --- a/lib/public/Share.php +++ b/lib/public/Share.php @@ -33,8 +33,7 @@ namespace OCP; /** - * This class provides the ability for apps to share their content between users. - * Apps must create a backend class that implements OCP\Share_Backend and register it with this class. + * This class remains only for use with the ::class namespace used for various hooks * * It provides the following hooks: * - post_shared @@ -42,95 +41,4 @@ namespace OCP; * @deprecated 17.0.0 */ class Share extends \OC\Share\Constants { - /** - * Get the item of item type shared with a given user by source - * @param string $itemType - * @param string $itemSource - * @param string $user User to whom the item was shared - * @param string $owner Owner of the share - * @return array Return list of items with file_target, permissions and expiration - * @since 6.0.0 - parameter $owner was added in 8.0.0 - * @deprecated 17.0.0 - */ - public static function getItemSharedWithUser($itemType, $itemSource, $user, $owner = null) { - return \OC\Share\Share::getItemSharedWithUser($itemType, $itemSource, $user, $owner); - } - - /** - * Get the item of item type shared with the current user by source - * @param string $itemType - * @param string $itemSource - * @param int $format (optional) Format type must be defined by the backend - * @param mixed $parameters - * @param bool $includeCollections - * @return void - * @since 5.0.0 - * @deprecated 17.0.0 - */ - public static function getItemSharedWithBySource($itemType, $itemSource, $format = self::FORMAT_NONE, - $parameters = null, $includeCollections = false) { - // not used by any app - only here to not break apps syntax - } - - /** - * Based on the given token the share information will be returned - password protected shares will be verified - * @param string $token - * @param bool $checkPasswordProtection - * @return void - * @since 5.0.0 - parameter $checkPasswordProtection was added in 7.0.0 - * @deprecated 17.0.0 - */ - public static function getShareByToken($token, $checkPasswordProtection = true) { - // not used by any app - only here to not break apps syntax - } - - - /** - * Get the shared items of item type owned by the current user - * @param string $itemType - * @param int $format (optional) Format type must be defined by the backend - * @param mixed $parameters - * @param int $limit Number of items to return (optional) Returns all by default - * @param bool $includeCollections - * @return void - * @since 5.0.0 - * @deprecated 17.0.0 - */ - public static function getItemsShared($itemType, $format = self::FORMAT_NONE, $parameters = null, - $limit = -1, $includeCollections = false) { - // only used by AppVNCSafe app (https://github.com/vnc-biz/nextcloud-appvncsafe/issues/2) - only here to not break apps syntax - } - - /** - * Get the shared item of item type owned by the current user - * @param string $itemType - * @param string $itemSource - * @param int $format (optional) Format type must be defined by the backend - * @param mixed $parameters - * @param bool $includeCollections - * @return mixed Return depends on format - * @since 5.0.0 - * @deprecated 17.0.0 - * - * Refactoring notes: - * * defacto $parameters and $format is always the default and therefore is removed in the subsequent call - */ - public static function getItemShared($itemType, $itemSource, $format = self::FORMAT_NONE, - $parameters = null, $includeCollections = false) { - return \OC\Share\Share::getItemShared($itemType, $itemSource, self::FORMAT_NONE, null, $includeCollections); - } - - /** - * sent status if users got informed by mail about share - * @param string $itemType - * @param string $itemSource - * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK - * @param string $recipient with whom was the item shared - * @param bool $status - * @since 6.0.0 - parameter $originIsSource was added in 8.0.0 - * @deprecated 17.0.0 - */ - public static function setSendMailStatus($itemType, $itemSource, $shareType, $recipient, $status) { - // not used by any app - only here to not break apps syntax - } } diff --git a/lib/public/Share/Events/VerifyMountPointEvent.php b/lib/public/Share/Events/VerifyMountPointEvent.php index 650f4ad2245..af71314930b 100644 --- a/lib/public/Share/Events/VerifyMountPointEvent.php +++ b/lib/public/Share/Events/VerifyMountPointEvent.php @@ -44,8 +44,8 @@ class VerifyMountPointEvent extends Event { * @since 19.0.0 */ public function __construct(IShare $share, - View $view, - string $parent) { + View $view, + string $parent) { parent::__construct(); $this->share = $share; diff --git a/lib/public/Share/IManager.php b/lib/public/Share/IManager.php index 9ac224ed7ef..07517dd7eb5 100644 --- a/lib/public/Share/IManager.php +++ b/lib/public/Share/IManager.php @@ -416,6 +416,15 @@ interface IManager { public function shareWithGroupMembersOnly(); /** + * If shareWithGroupMembersOnly is enabled, return an optional + * list of groups that must be excluded from the principle of + * belonging to the same group. + * @return array + * @since 27.0.0 + */ + public function shareWithGroupMembersOnlyExcludeGroupsList(); + + /** * Check if users can share with groups * @return bool * @since 9.0.1 diff --git a/lib/public/Share/IShare.php b/lib/public/Share/IShare.php index 40548c6c73d..a059696a75e 100644 --- a/lib/public/Share/IShare.php +++ b/lib/public/Share/IShare.php @@ -213,7 +213,7 @@ interface IShare { * @since 9.0.0 * @throws NotFoundException */ - public function getNodeId(); + public function getNodeId(): int; /** * Set the type of node (file/folder) @@ -394,7 +394,7 @@ interface IShare { /** * Get the expiration date * - * @return \DateTime + * @return null|\DateTime * @since 9.0.0 */ public function getExpirationDate(); @@ -474,7 +474,7 @@ interface IShare { * If this share is obtained via a shareprovider the password is * hashed. * - * @return string + * @return string|null * @since 9.0.0 */ public function getPassword(); diff --git a/lib/public/Share/IShareProvider.php b/lib/public/Share/IShareProvider.php index b6e0c4ba38b..2a37508d449 100644 --- a/lib/public/Share/IShareProvider.php +++ b/lib/public/Share/IShareProvider.php @@ -70,7 +70,7 @@ interface IShareProvider { * @return IShare The share object * @since 17.0.0 */ -// public function acceptShare(IShare $share, string $recipient): IShare; + // public function acceptShare(IShare $share, string $recipient): IShare; /** * Delete a share diff --git a/lib/public/SpeechToText/ISpeechToTextProviderWithId.php b/lib/public/SpeechToText/ISpeechToTextProviderWithId.php new file mode 100644 index 00000000000..0fb337f4602 --- /dev/null +++ b/lib/public/SpeechToText/ISpeechToTextProviderWithId.php @@ -0,0 +1,14 @@ +<?php + +namespace OCP\SpeechToText; + +/** + * @since 28.0.0 + */ +interface ISpeechToTextProviderWithId extends ISpeechToTextProvider { + + /** + * @since 28.0.0 + */ + public function getId(): string; +} diff --git a/lib/public/SpeechToText/ISpeechToTextProviderWithUserId.php b/lib/public/SpeechToText/ISpeechToTextProviderWithUserId.php new file mode 100644 index 00000000000..2a9b667171d --- /dev/null +++ b/lib/public/SpeechToText/ISpeechToTextProviderWithUserId.php @@ -0,0 +1,37 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2024 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\SpeechToText; + +/** + * @since 29.0.0 + */ +interface ISpeechToTextProviderWithUserId extends ISpeechToTextProvider { + /** + * @since 29.0.0 + */ + public function setUserId(?string $userId): void; +} diff --git a/lib/public/SystemTag/ManagerEvent.php b/lib/public/SystemTag/ManagerEvent.php index 704aecd4536..48bd651f7a3 100644 --- a/lib/public/SystemTag/ManagerEvent.php +++ b/lib/public/SystemTag/ManagerEvent.php @@ -37,16 +37,19 @@ use OCP\EventDispatcher\Event; */ class ManagerEvent extends Event { /** + * @since 9.0.0 * @deprecated 22.0.0 */ public const EVENT_CREATE = 'OCP\SystemTag\ISystemTagManager::createTag'; /** + * @since 9.0.0 * @deprecated 22.0.0 */ public const EVENT_UPDATE = 'OCP\SystemTag\ISystemTagManager::updateTag'; /** + * @since 9.0.0 * @deprecated 22.0.0 */ public const EVENT_DELETE = 'OCP\SystemTag\ISystemTagManager::deleteTag'; diff --git a/lib/public/SystemTag/MapperEvent.php b/lib/public/SystemTag/MapperEvent.php index 6aa0ea77000..cbd1b2ff2db 100644 --- a/lib/public/SystemTag/MapperEvent.php +++ b/lib/public/SystemTag/MapperEvent.php @@ -36,11 +36,13 @@ use OCP\EventDispatcher\Event; */ class MapperEvent extends Event { /** + * @since 9.0.0 * @deprecated 22.0.0 */ public const EVENT_ASSIGN = 'OCP\SystemTag\ISystemTagObjectMapper::assignTags'; /** + * @since 9.0.0 * @deprecated 22.0.0 */ public const EVENT_UNASSIGN = 'OCP\SystemTag\ISystemTagObjectMapper::unassignTags'; diff --git a/lib/public/SystemTag/SystemTagsEntityEvent.php b/lib/public/SystemTag/SystemTagsEntityEvent.php index 0ce679a3a43..252029e6ba2 100644 --- a/lib/public/SystemTag/SystemTagsEntityEvent.php +++ b/lib/public/SystemTag/SystemTagsEntityEvent.php @@ -37,6 +37,7 @@ use OCP\EventDispatcher\Event; */ class SystemTagsEntityEvent extends Event { /** + * @since 9.1.0 * @deprecated 22.0.0 Listen to the typed event instead */ public const EVENT_ENTITY = 'OCP\SystemTag\ISystemTagManager::registerEntity'; diff --git a/lib/public/Talk/IBroker.php b/lib/public/Talk/IBroker.php index d3b6e1429e6..f2b512ea4a8 100644 --- a/lib/public/Talk/IBroker.php +++ b/lib/public/Talk/IBroker.php @@ -68,8 +68,8 @@ interface IBroker { * @since 24.0.0 */ public function createConversation(string $name, - array $moderators, - IConversationOptions $options = null): IConversation; + array $moderators, + IConversationOptions $options = null): IConversation; /** * Delete a conversation by id diff --git a/lib/public/Talk/ITalkBackend.php b/lib/public/Talk/ITalkBackend.php index 3ee995576a4..a2f4d962019 100644 --- a/lib/public/Talk/ITalkBackend.php +++ b/lib/public/Talk/ITalkBackend.php @@ -46,8 +46,8 @@ interface ITalkBackend { * @since 24.0.0 */ public function createConversation(string $name, - array $moderators, - IConversationOptions $options): IConversation; + array $moderators, + IConversationOptions $options): IConversation; /** * Delete a conversation by id diff --git a/lib/public/Teams/ITeamManager.php b/lib/public/Teams/ITeamManager.php new file mode 100644 index 00000000000..51d8a1feb5a --- /dev/null +++ b/lib/public/Teams/ITeamManager.php @@ -0,0 +1,58 @@ +<?php +/** + * @copyright Copyright (c) 2024 Julius Härtl <jus@bitgrid.net> + * + * @author Julius Härtl <jus@bitgrid.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\Teams; + +/** + * @since 29.0.0 + */ +interface ITeamManager { + /** + * Get all providers that have registered as a team resource provider + * + * @return ITeamResourceProvider[] + * @since 29.0.0 + */ + public function getProviders(): array; + + /** + * Get a specific team resource provider by its id + * + * @since 29.0.0 + */ + public function getProvider(string $providerId): ITeamResourceProvider; + + /** + * Returns all team resources for a given team and user + * + * @return TeamResource[] + * @since 29.0.0 + */ + public function getSharedWith(string $teamId, string $userId): array; + + /** + * Returns all teams for a given resource and user + * + * @since 29.0.0 + */ + public function getTeamsForResource(string $providerId, string $resourceId, string $userId): array; +} diff --git a/lib/public/Teams/ITeamResourceProvider.php b/lib/public/Teams/ITeamResourceProvider.php new file mode 100644 index 00000000000..722c877555e --- /dev/null +++ b/lib/public/Teams/ITeamResourceProvider.php @@ -0,0 +1,76 @@ +<?php +/** + * @copyright Copyright (c) 2024 Julius Härtl <jus@bitgrid.net> + * + * @author Julius Härtl <jus@bitgrid.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\Teams; + +/** + * Implement a provider of resources that are shared or owned by a team + * + * @since 29.0.0 + */ +interface ITeamResourceProvider { + + /** + * Unique identifier used to identify the provider (app id) + * + * @since 29.0.0 + */ + public function getId(): string; + + /** + * User visible name of the provider (app name) + * + * @since 29.0.0 + */ + public function getName(): string; + + /** + * Svg icon to show next to the provider (app icon) + * + * @since 29.0.0 + */ + public function getIconSvg(): string; + + /** + * Return all resources that are shared to the given team id for the current provider + * + * @param string $teamId + * @return TeamResource[] + * @since 29.0.0 + */ + public function getSharedWith(string $teamId): array; + + /** + * Check if a resource is shared with the given team + * + * @since 29.0.0 + */ + public function isSharedWithTeam(string $teamId, string $resourceId): bool; + + /** + * Return team ids that a resource is shared with or owned by + * + * @return string[] + * @since 29.0.0 + */ + public function getTeamsForResource(string $resourceId): array; +} diff --git a/lib/public/Teams/Team.php b/lib/public/Teams/Team.php new file mode 100644 index 00000000000..d3d6c2d143d --- /dev/null +++ b/lib/public/Teams/Team.php @@ -0,0 +1,73 @@ +<?php +/** + * @copyright Copyright (c) 2024 Julius Härtl <jus@bitgrid.net> + * + * @author Julius Härtl <jus@bitgrid.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\Teams; + +/** + * Simple abstraction to represent a team in the public API + * + * In the backend a team is a circle identified by the circles singleId + * + * @since 29.0.0 + */ +class Team implements \JsonSerializable { + + /** + * @since 29.0.0 + */ + public function __construct(private string $teamId, private string $displayName, private ?string $link) { + } + + /** + * Unique identifier of the team (singleId of the circle) + * + * @since 29.0.0 + */ + public function getId(): string { + return $this->teamId; + } + + /** + * @since 29.0.0 + */ + public function getDisplayName(): string { + return $this->displayName; + } + + /** + * @since 29.0.0 + */ + public function getLink(): ?string { + return $this->link; + } + + /** + * @since 29.0.0 + */ + public function jsonSerialize(): array { + return [ + 'teamId' => $this->teamId, + 'displayName' => $this->displayName, + 'link' => $this->link, + ]; + } +} diff --git a/lib/public/Teams/TeamResource.php b/lib/public/Teams/TeamResource.php new file mode 100644 index 00000000000..569583bb393 --- /dev/null +++ b/lib/public/Teams/TeamResource.php @@ -0,0 +1,129 @@ +<?php +/** + * @copyright Copyright (c) 2024 Julius Härtl <jus@bitgrid.net> + * + * @author Julius Härtl <jus@bitgrid.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\Teams; + +/** + * @since 29.0.0 + */ +class TeamResource implements \JsonSerializable { + /** + * @since 29.0.0 + */ + public function __construct( + private ITeamResourceProvider $teamResourceProvider, + private string $resourceId, + private string $label, + private string $url, + private ?string $iconSvg = null, + private ?string $iconURL = null, + private ?string $iconEmoji = null, + ) { + } + + /** + * Returns the provider details for the current resource + * + * @since 29.0.0 + */ + public function getProvider(): ITeamResourceProvider { + return $this->teamResourceProvider; + } + + /** + * Unique id of the resource (e.g. primary key id) + * @since 29.0.0 + */ + public function getId(): string { + return $this->resourceId; + } + + /** + * User visible label when listing resources + * + * @since 29.0.0 + */ + public function getLabel(): string { + return $this->label; + } + + /** + * Absolute url to navigate the user to the resource + * + * @since 29.0.0 + */ + public function getUrl(): string { + return $this->url; + } + + /** + * Svg icon to show next to the name for the resource + * + * From all icons the first one returning not null will be picked in order: iconEmoji, iconSvg, iconUrl + * + * @since 29.0.0 + */ + public function getIconSvg(): ?string { + return $this->iconSvg; + } + + /** + * Image url of the icon to show next to the name for the resource + * + * From all icons the first one returning not null will be picked in order: iconEmoji, iconSvg, iconUrl + * + * @since 29.0.0 + */ + public function getIconURL(): ?string { + return $this->iconURL; + } + + /** + * Emoji show next to the name for the resource + * + * From all icons the first one returning not null will be picked in order: iconEmoji, iconSvg, iconUrl + * + * @since 29.0.0 + */ + public function getIconEmoji(): ?string { + return $this->iconEmoji; + } + + /** + * @since 29.0.0 + */ + public function jsonSerialize(): array { + return [ + 'id' => $this->resourceId, + 'label' => $this->label, + 'url' => $this->url, + 'iconSvg' => $this->iconSvg, + 'iconURL' => $this->iconURL, + 'iconEmoji' => $this->iconEmoji, + 'provider' => [ + 'id' => $this->teamResourceProvider->getId(), + 'name' => $this->teamResourceProvider->getName(), + 'icon' => $this->teamResourceProvider->getIconSvg(), + ] + ]; + } +} 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..2f517a4ebe2 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; /** @@ -48,7 +50,7 @@ interface IManager { public function getProviders(): array; /** - * @return class-string<ITaskType>[] + * @return string[] * @since 27.1.0 */ public function getAvailableTaskTypes(): array; @@ -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/IProvider.php b/lib/public/TextProcessing/IProvider.php index 6132e60b493..fc57add1835 100644 --- a/lib/public/TextProcessing/IProvider.php +++ b/lib/public/TextProcessing/IProvider.php @@ -31,7 +31,7 @@ use RuntimeException; /** * This is the interface that is implemented by apps that * implement a text processing provider - * @template T of ITaskType + * @psalm-template-covariant T of ITaskType * @since 27.1.0 */ interface IProvider { 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/IProviderWithId.php b/lib/public/TextProcessing/IProviderWithId.php new file mode 100644 index 00000000000..1bd02278d1c --- /dev/null +++ b/lib/public/TextProcessing/IProviderWithId.php @@ -0,0 +1,39 @@ +<?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; + +/** + * @since 28.0.0 + * @extends IProvider<T> + * @template T of ITaskType + */ +interface IProviderWithId extends IProvider { + /** + * The id of this provider + * @since 28.0.0 + */ + public function getId(): string; +} 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..c62b7b2fff8 100644 --- a/lib/public/TextProcessing/Task.php +++ b/lib/public/TextProcessing/Task.php @@ -28,13 +28,12 @@ namespace OCP\TextProcessing; /** * This is a text processing task * @since 27.1.0 - * @psalm-template T of ITaskType - * @psalm-template S as class-string<T> - * @psalm-template P as IProvider<T> + * @psalm-template-covariant T of ITaskType */ final class Task implements \JsonSerializable { protected ?int $id = null; protected ?string $output = null; + private ?\DateTime $completionExpectedAt = null; /** * @since 27.1.0 @@ -73,7 +72,7 @@ final class Task implements \JsonSerializable { protected int $status = self::STATUS_UNKNOWN; /** - * @psalm-param S $type + * @psalm-param class-string<T> $type * @param string $type * @param string $input * @param string $appId @@ -91,13 +90,16 @@ final class Task implements \JsonSerializable { } /** - * @psalm-param P $provider + * @psalm-param IProvider<T> $provider * @param IProvider $provider * @return string * @since 27.1.0 */ 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()); @@ -105,7 +107,7 @@ final class Task implements \JsonSerializable { } /** - * @psalm-param P $provider + * @psalm-param IProvider<T> $provider * @param IProvider $provider * @return bool * @since 27.1.0 @@ -115,7 +117,7 @@ final class Task implements \JsonSerializable { } /** - * @psalm-return S + * @psalm-return class-string<T> * @since 27.1.0 */ final public function getType(): string { @@ -203,7 +205,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: class-string<T>, 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 +218,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..f2092476e78 --- /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 list<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/IProviderWithUserId.php b/lib/public/TextToImage/IProviderWithUserId.php new file mode 100644 index 00000000000..8afb0e56fbb --- /dev/null +++ b/lib/public/TextToImage/IProviderWithUserId.php @@ -0,0 +1,15 @@ +<?php + +declare(strict_types=1); + +namespace OCP\TextToImage; + +/** + * @since 29.0.0 + */ +interface IProviderWithUserId extends IProvider { + /** + * @since 29.0.0 + */ + public function setUserId(?string $userId): void; +} 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/Translation/ITranslationProviderWithId.php b/lib/public/Translation/ITranslationProviderWithId.php new file mode 100644 index 00000000000..fa08ef7cb17 --- /dev/null +++ b/lib/public/Translation/ITranslationProviderWithId.php @@ -0,0 +1,37 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2024 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\Translation; + +/** + * @since 29.0.0 + */ +interface ITranslationProviderWithId extends ITranslationProvider { + /** + * @since 29.0.0 + */ + public function getId(): string; +} diff --git a/lib/public/Translation/ITranslationProviderWithUserId.php b/lib/public/Translation/ITranslationProviderWithUserId.php new file mode 100644 index 00000000000..9a573a8150e --- /dev/null +++ b/lib/public/Translation/ITranslationProviderWithUserId.php @@ -0,0 +1,38 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2024 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\Translation; + +/** + * @since 29.0.0 + */ +interface ITranslationProviderWithUserId extends ITranslationProvider { + /** + * @param string|null $userId The userId of the user requesting the current task + * @since 29.0.0 + */ + public function setUserId(?string $userId); +} diff --git a/lib/public/User/Backend/IProvideEnabledStateBackend.php b/lib/public/User/Backend/IProvideEnabledStateBackend.php index d03beacd7b8..f12d99fd1a6 100644 --- a/lib/public/User/Backend/IProvideEnabledStateBackend.php +++ b/lib/public/User/Backend/IProvideEnabledStateBackend.php @@ -52,5 +52,5 @@ interface IProvideEnabledStateBackend { * * @return string[] */ - public function getDisabledUserList(int $offset = 0, ?int $limit = null): array; + public function getDisabledUserList(?int $limit = null, int $offset = 0): array; } diff --git a/lib/public/User/Events/BeforePasswordUpdatedEvent.php b/lib/public/User/Events/BeforePasswordUpdatedEvent.php index 11eb5ad9dd0..ee228ae01e7 100644 --- a/lib/public/User/Events/BeforePasswordUpdatedEvent.php +++ b/lib/public/User/Events/BeforePasswordUpdatedEvent.php @@ -51,8 +51,8 @@ class BeforePasswordUpdatedEvent extends Event { * @since 18.0.0 */ public function __construct(IUser $user, - string $password, - string $recoveryPassword = null) { + string $password, + string $recoveryPassword = null) { parent::__construct(); $this->user = $user; $this->password = $password; diff --git a/lib/public/User/Events/BeforeUserCreatedEvent.php b/lib/public/User/Events/BeforeUserCreatedEvent.php index 67e9177b34d..ee33239a12c 100644 --- a/lib/public/User/Events/BeforeUserCreatedEvent.php +++ b/lib/public/User/Events/BeforeUserCreatedEvent.php @@ -44,7 +44,7 @@ class BeforeUserCreatedEvent extends Event { * @since 18.0.0 */ public function __construct(string $uid, - string $password) { + string $password) { parent::__construct(); $this->uid = $uid; $this->password = $password; 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/OutOfOfficeEndedEvent.php b/lib/public/User/Events/OutOfOfficeEndedEvent.php new file mode 100644 index 00000000000..43a6bf77e28 --- /dev/null +++ b/lib/public/User/Events/OutOfOfficeEndedEvent.php @@ -0,0 +1,51 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2023 Richard Steinmetz <richard@steinmetz.cloud> + * + * @author Richard Steinmetz <richard@steinmetz.cloud> + * + * @license AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU 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 ended + * + * @since 28.0.0 + */ +class OutOfOfficeEndedEvent 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/Events/OutOfOfficeStartedEvent.php b/lib/public/User/Events/OutOfOfficeStartedEvent.php new file mode 100644 index 00000000000..f7816c968dd --- /dev/null +++ b/lib/public/User/Events/OutOfOfficeStartedEvent.php @@ -0,0 +1,51 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2023 Richard Steinmetz <richard@steinmetz.cloud> + * + * @author Richard Steinmetz <richard@steinmetz.cloud> + * + * @license AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU 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 started + * + * @since 28.0.0 + */ +class OutOfOfficeStartedEvent 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/PasswordUpdatedEvent.php b/lib/public/User/Events/PasswordUpdatedEvent.php index 41d510553b5..782d6d270ea 100644 --- a/lib/public/User/Events/PasswordUpdatedEvent.php +++ b/lib/public/User/Events/PasswordUpdatedEvent.php @@ -51,8 +51,8 @@ class PasswordUpdatedEvent extends Event { * @since 18.0.0 */ public function __construct(IUser $user, - string $password, - string $recoveryPassword = null) { + string $password, + string $recoveryPassword = null) { parent::__construct(); $this->user = $user; $this->password = $password; diff --git a/lib/public/User/Events/UserChangedEvent.php b/lib/public/User/Events/UserChangedEvent.php index f48dd3914e6..870b0326920 100644 --- a/lib/public/User/Events/UserChangedEvent.php +++ b/lib/public/User/Events/UserChangedEvent.php @@ -43,9 +43,9 @@ class UserChangedEvent extends Event { * @since 18.0.0 */ public function __construct(IUser $user, - string $feature, - $value, - $oldValue = null) { + string $feature, + $value, + $oldValue = null) { parent::__construct(); $this->user = $user; $this->feature = $feature; diff --git a/lib/public/User/Events/UserCreatedEvent.php b/lib/public/User/Events/UserCreatedEvent.php index 7d343bfd5b8..b0a734be0cb 100644 --- a/lib/public/User/Events/UserCreatedEvent.php +++ b/lib/public/User/Events/UserCreatedEvent.php @@ -45,7 +45,7 @@ class UserCreatedEvent extends Event { * @since 18.0.0 */ public function __construct(IUser $user, - string $password) { + string $password) { parent::__construct(); $this->user = $user; $this->password = $password; diff --git a/lib/public/User/Events/UserLiveStatusEvent.php b/lib/public/User/Events/UserLiveStatusEvent.php index d04c3b61e24..8b6207d685d 100644 --- a/lib/public/User/Events/UserLiveStatusEvent.php +++ b/lib/public/User/Events/UserLiveStatusEvent.php @@ -60,8 +60,8 @@ class UserLiveStatusEvent extends Event { * @since 20.0.0 */ public function __construct(IUser $user, - string $status, - int $timestamp) { + string $status, + int $timestamp) { parent::__construct(); $this->user = $user; $this->status = $status; diff --git a/lib/public/User/IAvailabilityCoordinator.php b/lib/public/User/IAvailabilityCoordinator.php new file mode 100644 index 00000000000..3a79e39b7b7 --- /dev/null +++ b/lib/public/User/IAvailabilityCoordinator.php @@ -0,0 +1,67 @@ +<?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; + + /** + * Reset the absence cache to null + * + * @since 28.0.0 + */ + public function clearCache(string $userId): void; + + /** + * Is the absence in effect at this moment + * + * @param IOutOfOfficeData $data + * @return bool + * @since 28.0.0 + */ + public function isInEffect(IOutOfOfficeData $data): bool; +} diff --git a/lib/public/User/IOutOfOfficeData.php b/lib/public/User/IOutOfOfficeData.php new file mode 100644 index 00000000000..31281104382 --- /dev/null +++ b/lib/public/User/IOutOfOfficeData.php @@ -0,0 +1,94 @@ +<?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 JsonSerializable; +use OCP\IUser; + +/** + * DTO to hold out-of-office information of a user + * + * @psalm-type OutOfOfficeData = array{ + * id: string, + * userId: string, + * startDate: int, + * endDate: int, + * shortMessage: string, + * message: string, + * } + * + * @since 28.0.0 + */ +interface IOutOfOfficeData extends JsonSerializable { + /** + * 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; + + /** + * @return OutOfOfficeData + * + * @since 28.0.0 + */ + public function jsonSerialize(): array; +} diff --git a/lib/public/UserMigration/IImportSource.php b/lib/public/UserMigration/IImportSource.php index da2c87ba241..e9d49784f5d 100644 --- a/lib/public/UserMigration/IImportSource.php +++ b/lib/public/UserMigration/IImportSource.php @@ -32,6 +32,9 @@ use OCP\Files\Folder; * @since 24.0.0 */ interface IImportSource { + /** + * @since 24.0.0 + */ public const PATH_USER = 'user.json'; /** diff --git a/lib/public/UserStatus/IManager.php b/lib/public/UserStatus/IManager.php index 9cc8eaad8ee..a85c1894c65 100644 --- a/lib/public/UserStatus/IManager.php +++ b/lib/public/UserStatus/IManager.php @@ -52,9 +52,11 @@ interface IManager { * @param string $messageId The id of the predefined message. * @param string $status The status to assign * @param bool $createBackup If true, this will store the old status so that it is possible to revert it later (e.g. after a call). + * @param string|null $customMessage * @since 23.0.0 + * @since 28.0.0 Optional parameter $customMessage was added */ - public function setUserStatus(string $userId, string $messageId, string $status, bool $createBackup = false): void; + public function setUserStatus(string $userId, string $messageId, string $status, bool $createBackup = false, ?string $customMessage = null): void; /** * Revert an automatically set user status. For example after leaving a call, diff --git a/lib/public/UserStatus/IUserStatus.php b/lib/public/UserStatus/IUserStatus.php index 74c54cc9da2..f167f9a82ee 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,30 @@ interface IUserStatus { public const MESSAGE_AVAILABILITY = 'availability'; /** + * @var string + * @since 28.0.1 + */ + public const MESSAGE_OUT_OF_OFFICE = 'out-of-office'; + + /** + * @var string + * @since 28.0.0 + */ + public const MESSAGE_VACATION = 'vacationing'; + + /** + * @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 34ccc61074f..87b0700f12b 100644 --- a/lib/public/Util.php +++ b/lib/public/Util.php @@ -46,11 +46,13 @@ namespace OCP; +use bantu\IniGetWrapper\IniGetWrapper; use OC\AppScriptDependency; use OC\AppScriptSort; use OCP\Share\IManager as IShareManager; -use bantu\IniGetWrapper\IniGetWrapper; +use OCP\Share\IManager; use Psr\Container\ContainerExceptionInterface; +use Psr\Log\LoggerInterface; /** * This class provides different helper functions to make the life of a developer easier @@ -58,17 +60,11 @@ use Psr\Container\ContainerExceptionInterface; * @since 4.0.0 */ class Util { - /** @var \OCP\Share\IManager */ - private static $shareManager; - - /** @var array */ - private static $scripts = []; + private static ?IManager $shareManager = null; - /** @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 @@ -111,19 +107,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 @@ -145,13 +128,10 @@ class Util { /** * get l10n object - * @param string $application - * @param string|null $language - * @return \OCP\IL10N * @since 6.0.0 - parameter $language was added in 8.0.0 */ - public static function getL10N($application, $language = null) { - return \OC::$server->getL10N($application, $language); + public static function getL10N(string $application, ?string $language = null): IL10N { + return Server::get(\OCP\L10N\IFactory::class)->get($application, $language); } /** @@ -165,6 +145,31 @@ 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"; + } + + // We need to handle the translation BEFORE the init script + // is loaded, as the init script might use translations + if ($application !== 'core' && !str_contains($file, 'l10n')) { + self::addTranslations($application, null, true); + } + + self::$scriptsInit[] = $path; + } + + /** * add a javascript file * * @param string $application @@ -215,7 +220,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)) { @@ -232,9 +238,10 @@ class Util { * Add a translation JS file * @param string $application application id * @param string $languageCode language code, defaults to the current locale + * @param bool $init whether the translations should be loaded early or not * @since 8.0.0 */ - public static function addTranslations($application, $languageCode = null) { + public static function addTranslations($application, $languageCode = null, $init = false) { if (is_null($languageCode)) { $languageCode = \OC::$server->getL10NFactory()->findLanguage($application); } @@ -243,7 +250,12 @@ class Util { } else { $path = "l10n/$languageCode"; } - self::$scripts[$application][] = $path; + + if ($init) { + self::$scriptsInit[] = $path; + } else { + self::$scripts[$application][] = $path; + } } /** @@ -511,10 +523,31 @@ class Util { } /** + * Get a list of characters forbidden in file names + * @return string[] + * @since 29.0.0 + */ + public static function getForbiddenFileNameChars(): array { + // Get always forbidden characters + $invalidChars = str_split(\OCP\Constants::FILENAME_INVALID_CHARS); + if ($invalidChars === false) { + $invalidChars = []; + } + + // Get admin defined invalid characters + $additionalChars = \OCP\Server::get(IConfig::class)->getSystemValue('forbidden_chars', []); + if (!is_array($additionalChars)) { + \OCP\Server::get(LoggerInterface::class)->error('Invalid system config value for "forbidden_chars" is ignored.'); + $additionalChars = []; + } + return array_merge($invalidChars, $additionalChars); + } + + /** * Returns whether the given file name is valid * @param string $file file name to check * @return bool true if the file name is valid, false otherwise - * @deprecated 8.1.0 use \OC\Files\View::verifyPath() + * @deprecated 8.1.0 use OCP\Files\Storage\IStorage::verifyPath() * @since 7.0.0 * @suppress PhanDeprecatedFunction */ diff --git a/lib/public/WorkflowEngine/IManager.php b/lib/public/WorkflowEngine/IManager.php index abdcdfa107a..83e2b69dc65 100644 --- a/lib/public/WorkflowEngine/IManager.php +++ b/lib/public/WorkflowEngine/IManager.php @@ -31,7 +31,14 @@ namespace OCP\WorkflowEngine; * @since 9.1 */ interface IManager { + /** + * @since 18.0.0 + */ public const SCOPE_ADMIN = 0; + + /** + * @since 18.0.0 + */ public const SCOPE_USER = 1; /** |