aboutsummaryrefslogtreecommitdiffstats
path: root/lib/public/AppFramework
diff options
context:
space:
mode:
Diffstat (limited to 'lib/public/AppFramework')
-rw-r--r--lib/public/AppFramework/ApiController.php20
-rw-r--r--lib/public/AppFramework/App.php64
-rw-r--r--lib/public/AppFramework/Attribute/ASince.php34
-rw-r--r--lib/public/AppFramework/Attribute/Catchable.php23
-rw-r--r--lib/public/AppFramework/Attribute/Consumable.php27
-rw-r--r--lib/public/AppFramework/Attribute/Dispatchable.php23
-rw-r--r--lib/public/AppFramework/Attribute/ExceptionalImplementable.php38
-rw-r--r--lib/public/AppFramework/Attribute/Implementable.php27
-rw-r--r--lib/public/AppFramework/Attribute/Listenable.php23
-rw-r--r--lib/public/AppFramework/Attribute/Throwable.php23
-rw-r--r--lib/public/AppFramework/AuthPublicShareController.php11
-rw-r--r--lib/public/AppFramework/Bootstrap/IBootstrap.php2
-rw-r--r--lib/public/AppFramework/Bootstrap/IRegistrationContext.php37
-rw-r--r--lib/public/AppFramework/Controller.php6
-rw-r--r--lib/public/AppFramework/Db/Entity.php140
-rw-r--r--lib/public/AppFramework/Db/QBMapper.php65
-rw-r--r--lib/public/AppFramework/Http/Attribute/ARateLimit.php2
-rw-r--r--lib/public/AppFramework/Http/Attribute/AppApiAdminAccessWithoutUser.php21
-rw-r--r--lib/public/AppFramework/Http/Attribute/AuthorizedAdminSetting.php2
-rw-r--r--lib/public/AppFramework/Http/Attribute/BruteForceProtection.php2
-rw-r--r--lib/public/AppFramework/Http/Attribute/CORS.php4
-rw-r--r--lib/public/AppFramework/Http/Attribute/ExAppRequired.php21
-rw-r--r--lib/public/AppFramework/Http/Attribute/OpenAPI.php18
-rw-r--r--lib/public/AppFramework/Http/Attribute/PasswordConfirmationRequired.php17
-rw-r--r--lib/public/AppFramework/Http/Attribute/RequestHeader.php34
-rw-r--r--lib/public/AppFramework/Http/ContentSecurityPolicy.php2
-rw-r--r--lib/public/AppFramework/Http/DataDisplayResponse.php4
-rw-r--r--lib/public/AppFramework/Http/DataDownloadResponse.php4
-rw-r--r--lib/public/AppFramework/Http/DataResponse.php4
-rw-r--r--lib/public/AppFramework/Http/DownloadResponse.php4
-rw-r--r--lib/public/AppFramework/Http/EmptyContentSecurityPolicy.php10
-rw-r--r--lib/public/AppFramework/Http/Events/BeforeLoginTemplateRenderedEvent.php4
-rw-r--r--lib/public/AppFramework/Http/FileDisplayResponse.php5
-rw-r--r--lib/public/AppFramework/Http/JSONResponse.php28
-rw-r--r--lib/public/AppFramework/Http/NotFoundResponse.php4
-rw-r--r--lib/public/AppFramework/Http/RedirectResponse.php4
-rw-r--r--lib/public/AppFramework/Http/RedirectToDefaultAppResponse.php7
-rw-r--r--lib/public/AppFramework/Http/Response.php21
-rw-r--r--lib/public/AppFramework/Http/StandaloneTemplateResponse.php6
-rw-r--r--lib/public/AppFramework/Http/StreamResponse.php4
-rw-r--r--lib/public/AppFramework/Http/Template/ExternalShareMenuAction.php41
-rw-r--r--lib/public/AppFramework/Http/Template/IMenuAction.php8
-rw-r--r--lib/public/AppFramework/Http/Template/LinkMenuAction.php23
-rw-r--r--lib/public/AppFramework/Http/Template/PublicTemplateResponse.php43
-rw-r--r--lib/public/AppFramework/Http/Template/SimpleMenuAction.php29
-rw-r--r--lib/public/AppFramework/Http/TemplateResponse.php14
-rw-r--r--lib/public/AppFramework/Http/TextPlainResponse.php4
-rw-r--r--lib/public/AppFramework/Http/TooManyRequestsResponse.php9
-rw-r--r--lib/public/AppFramework/Http/ZipResponse.php4
-rw-r--r--lib/public/AppFramework/OCS/OCSBadRequestException.php1
-rw-r--r--lib/public/AppFramework/OCS/OCSException.php1
-rw-r--r--lib/public/AppFramework/OCS/OCSForbiddenException.php1
-rw-r--r--lib/public/AppFramework/OCS/OCSNotFoundException.php1
-rw-r--r--lib/public/AppFramework/OCS/OCSPreconditionFailedException.php1
-rw-r--r--lib/public/AppFramework/OCSController.php12
-rw-r--r--lib/public/AppFramework/PublicShareController.php4
-rw-r--r--lib/public/AppFramework/Services/IInitialState.php2
-rw-r--r--lib/public/AppFramework/Utility/IControllerMethodReflector.php4
58 files changed, 721 insertions, 276 deletions
diff --git a/lib/public/AppFramework/ApiController.php b/lib/public/AppFramework/ApiController.php
index da1152090c6..729582c8505 100644
--- a/lib/public/AppFramework/ApiController.php
+++ b/lib/public/AppFramework/ApiController.php
@@ -7,6 +7,7 @@
*/
namespace OCP\AppFramework;
+use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\Attribute\PublicPage;
use OCP\AppFramework\Http\Response;
@@ -26,13 +27,13 @@ abstract class ApiController extends Controller {
* @param string $appName the name of the app
* @param IRequest $request an instance of the request
* @param string $corsMethods comma separated string of HTTP verbs which
- * should be allowed for websites or webapps when calling your API, defaults to
- * 'PUT, POST, GET, DELETE, PATCH'
+ * should be allowed for websites or webapps when calling your API, defaults to
+ * 'PUT, POST, GET, DELETE, PATCH'
* @param string $corsAllowedHeaders comma separated string of HTTP headers
- * which should be allowed for websites or webapps when calling your API,
- * defaults to 'Authorization, Content-Type, Accept'
+ * which should be allowed for websites or webapps when calling your API,
+ * defaults to 'Authorization, Content-Type, Accept'
* @param int $corsMaxAge number in seconds how long a preflighted OPTIONS
- * request should be cached, defaults to 1728000 seconds
+ * request should be cached, defaults to 1728000 seconds
* @since 7.0.0
*/
public function __construct($appName,
@@ -51,17 +52,14 @@ abstract class ApiController extends Controller {
* This method implements a preflighted cors response for you that you can
* link to for the options request
*
- * @NoAdminRequired
- * @NoCSRFRequired
- * @PublicPage
* @since 7.0.0
*/
#[NoCSRFRequired]
#[PublicPage]
+ #[NoAdminRequired]
public function preflightedCors() {
- if (isset($this->request->server['HTTP_ORIGIN'])) {
- $origin = $this->request->server['HTTP_ORIGIN'];
- } else {
+ $origin = $this->request->getHeader('origin');
+ if ($origin === '') {
$origin = '*';
}
diff --git a/lib/public/AppFramework/App.php b/lib/public/AppFramework/App.php
index a8ba6b701f9..c00fde47418 100644
--- a/lib/public/AppFramework/App.php
+++ b/lib/public/AppFramework/App.php
@@ -9,10 +9,10 @@ declare(strict_types=1);
*/
namespace OCP\AppFramework;
-use OC\AppFramework\Routing\RouteConfig;
-use OC\Route\Router;
+use OC\AppFramework\Utility\SimpleContainer;
use OC\ServerContainer;
-use OCP\Route\IRouter;
+use OCP\IConfig;
+use OCP\Server;
use Psr\Log\LoggerInterface;
/**
@@ -32,7 +32,7 @@ class App {
* some_app_id -> OCA\SomeAppId
* @param string $appId the app id
* @param string $topNamespace the namespace which should be prepended to
- * the transformed app id, defaults to OCA\
+ * the transformed app id, defaults to OCA\
* @return string the starting namespace for the app
* @since 8.0.0
*/
@@ -47,7 +47,7 @@ class App {
* @since 6.0.0
*/
public function __construct(string $appName, array $urlParams = []) {
- $runIsSetupDirectly = \OC::$server->getConfig()->getSystemValueBool('debug')
+ $runIsSetupDirectly = Server::get(IConfig::class)->getSystemValueBool('debug')
&& !ini_get('zend.exception_ignore_args');
if ($runIsSetupDirectly) {
@@ -58,23 +58,30 @@ class App {
$classNameParts = explode('\\', trim($applicationClassName, '\\'));
foreach ($e->getTrace() as $step) {
- if (isset($step['class'], $step['function'], $step['args'][0]) &&
- $step['class'] === ServerContainer::class &&
- $step['function'] === 'query' &&
- $step['args'][0] === $applicationClassName) {
+ if (isset($step['class'], $step['function'], $step['args'][0])
+ && $step['class'] === ServerContainer::class
+ && $step['function'] === 'query'
+ && $step['args'][0] === $applicationClassName) {
$setUpViaQuery = true;
break;
- } elseif (isset($step['class'], $step['function'], $step['args'][0]) &&
- $step['class'] === ServerContainer::class &&
- $step['function'] === 'getAppContainer' &&
- $step['args'][1] === $classNameParts[1]) {
+ } elseif (isset($step['class'], $step['function'], $step['args'][0])
+ && $step['class'] === ServerContainer::class
+ && $step['function'] === 'getAppContainer'
+ && $step['args'][1] === $classNameParts[1]) {
+ $setUpViaQuery = true;
+ break;
+ } elseif (isset($step['class'], $step['function'], $step['args'][0])
+ && $step['class'] === SimpleContainer::class
+ && preg_match('/{closure:OC\\\\AppFramework\\\\Utility\\\\SimpleContainer::buildClass\\(\\):\\d+}/', $step['function'])
+ && $step['args'][0] === $this) {
+ /* We are setup through a lazy ghost, fine */
$setUpViaQuery = true;
break;
}
}
if (!$setUpViaQuery && $applicationClassName !== \OCP\AppFramework\App::class) {
- \OCP\Server::get(LoggerInterface::class)->error($e->getMessage(), [
+ Server::get(LoggerInterface::class)->error($e->getMessage(), [
'app' => $appName,
'exception' => $e,
]);
@@ -97,35 +104,6 @@ class App {
}
/**
- * This function is to be called to create single routes and restful routes based on the given $routes array.
- *
- * Example code in routes.php of tasks app (it will register two restful resources):
- * $routes = array(
- * 'resources' => array(
- * 'lists' => array('url' => '/tasklists'),
- * 'tasks' => array('url' => '/tasklists/{listId}/tasks')
- * )
- * );
- *
- * $a = new TasksApp();
- * $a->registerRoutes($this, $routes);
- *
- * @param \OCP\Route\IRouter $router
- * @param array $routes
- * @since 6.0.0
- * @suppress PhanAccessMethodInternal
- * @deprecated 20.0.0 Just return an array from your routes.php
- */
- public function registerRoutes(IRouter $router, array $routes) {
- if (!($router instanceof Router)) {
- throw new \RuntimeException('Can only setup routes with real router');
- }
-
- $routeConfig = new RouteConfig($this->container, $router, $routes);
- $routeConfig->register();
- }
-
- /**
* This function is called by the routing component to fire up the frameworks dispatch mechanism.
*
* Example code in routes.php of the task app:
diff --git a/lib/public/AppFramework/Attribute/ASince.php b/lib/public/AppFramework/Attribute/ASince.php
new file mode 100644
index 00000000000..1e0c45348cf
--- /dev/null
+++ b/lib/public/AppFramework/Attribute/ASince.php
@@ -0,0 +1,34 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\AppFramework\Attribute;
+
+use Attribute;
+
+/**
+ * Abstract base attribute to declare an API's stability.
+ *
+ * @since 32.0.0
+ */
+#[Consumable(since: '32.0.0')]
+abstract class ASince {
+ /**
+ * @param string $since For shipped apps and server code such as core/ and lib/,
+ * this should be the server version. For other apps it
+ * should be the semantic app version.
+ */
+ public function __construct(
+ protected string $since,
+ ) {
+ }
+
+ public function getSince(): string {
+ return $this->since;
+ }
+}
diff --git a/lib/public/AppFramework/Attribute/Catchable.php b/lib/public/AppFramework/Attribute/Catchable.php
new file mode 100644
index 00000000000..d45401550f6
--- /dev/null
+++ b/lib/public/AppFramework/Attribute/Catchable.php
@@ -0,0 +1,23 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\AppFramework\Attribute;
+
+use Attribute;
+
+/**
+ * Attribute to declare that the exception is "catchable" by apps.
+ *
+ * @since 32.0.0
+ */
+#[Attribute(Attribute::TARGET_ALL | Attribute::IS_REPEATABLE)]
+#[Consumable(since: '32.0.0')]
+#[Implementable(since: '32.0.0')]
+class Catchable extends ASince {
+}
diff --git a/lib/public/AppFramework/Attribute/Consumable.php b/lib/public/AppFramework/Attribute/Consumable.php
new file mode 100644
index 00000000000..2175bb0af88
--- /dev/null
+++ b/lib/public/AppFramework/Attribute/Consumable.php
@@ -0,0 +1,27 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\AppFramework\Attribute;
+
+use Attribute;
+
+/**
+ * Attribute to declare that the API stability is limited to "consuming" the
+ * class, interface, enum, etc. Apps are not allowed to implement or replace them.
+ *
+ * For events use @see \OCP\AppFramework\Attribute\Listenable
+ * For exceptions use @see \OCP\AppFramework\Attribute\Catchable
+ *
+ * @since 32.0.0
+ */
+#[Attribute(Attribute::TARGET_ALL | Attribute::IS_REPEATABLE)]
+#[Consumable(since: '32.0.0')]
+#[Implementable(since: '32.0.0')]
+class Consumable extends ASince {
+}
diff --git a/lib/public/AppFramework/Attribute/Dispatchable.php b/lib/public/AppFramework/Attribute/Dispatchable.php
new file mode 100644
index 00000000000..ff703d4749e
--- /dev/null
+++ b/lib/public/AppFramework/Attribute/Dispatchable.php
@@ -0,0 +1,23 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\AppFramework\Attribute;
+
+use Attribute;
+
+/**
+ * Attribute to declare that the event is "dispatchable" by apps.
+ *
+ * @since 32.0.0
+ */
+#[Attribute(Attribute::TARGET_ALL | Attribute::IS_REPEATABLE)]
+#[Consumable(since: '32.0.0')]
+#[Implementable(since: '32.0.0')]
+class Dispatchable extends ASince {
+}
diff --git a/lib/public/AppFramework/Attribute/ExceptionalImplementable.php b/lib/public/AppFramework/Attribute/ExceptionalImplementable.php
new file mode 100644
index 00000000000..23e9f830d9b
--- /dev/null
+++ b/lib/public/AppFramework/Attribute/ExceptionalImplementable.php
@@ -0,0 +1,38 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\AppFramework\Attribute;
+
+use Attribute;
+
+/**
+ * Attribute to declare that the API marked as Consumable/Listenable/Catchable
+ * has an exception and is Implementable/Dispatchable/Throwable by a dedicated
+ * app. Changes to such an API have to be communicated to the affected app maintainers.
+ *
+ * @since 32.0.0
+ */
+#[Attribute(Attribute::TARGET_ALL | Attribute::IS_REPEATABLE)]
+#[Consumable(since: '32.0.0')]
+#[Implementable(since: '32.0.0')]
+class ExceptionalImplementable {
+ public function __construct(
+ protected string $app,
+ protected ?string $class = null,
+ ) {
+ }
+
+ public function getApp(): string {
+ return $this->app;
+ }
+
+ public function getClass(): ?string {
+ return $this->class;
+ }
+}
diff --git a/lib/public/AppFramework/Attribute/Implementable.php b/lib/public/AppFramework/Attribute/Implementable.php
new file mode 100644
index 00000000000..40ce0e0cf06
--- /dev/null
+++ b/lib/public/AppFramework/Attribute/Implementable.php
@@ -0,0 +1,27 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\AppFramework\Attribute;
+
+use Attribute;
+
+/**
+ * Attribute to declare that the API stability is limited to "implementing" the
+ * class, interface, enum, etc.
+ *
+ * For events use @see \OCP\AppFramework\Attribute\Dispatchable
+ * For exceptions use @see \OCP\AppFramework\Attribute\Throwable
+ *
+ * @since 32.0.0
+ */
+#[Attribute(Attribute::TARGET_ALL | Attribute::IS_REPEATABLE)]
+#[Consumable(since: '32.0.0')]
+#[Implementable(since: '32.0.0')]
+class Implementable extends ASince {
+}
diff --git a/lib/public/AppFramework/Attribute/Listenable.php b/lib/public/AppFramework/Attribute/Listenable.php
new file mode 100644
index 00000000000..98c2ca78690
--- /dev/null
+++ b/lib/public/AppFramework/Attribute/Listenable.php
@@ -0,0 +1,23 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\AppFramework\Attribute;
+
+use Attribute;
+
+/**
+ * Attribute to declare that the event is "listenable" by apps.
+ *
+ * @since 32.0.0
+ */
+#[Attribute(Attribute::TARGET_ALL | Attribute::IS_REPEATABLE)]
+#[Consumable(since: '32.0.0')]
+#[Implementable(since: '32.0.0')]
+class Listenable extends ASince {
+}
diff --git a/lib/public/AppFramework/Attribute/Throwable.php b/lib/public/AppFramework/Attribute/Throwable.php
new file mode 100644
index 00000000000..2c763c76b4c
--- /dev/null
+++ b/lib/public/AppFramework/Attribute/Throwable.php
@@ -0,0 +1,23 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\AppFramework\Attribute;
+
+use Attribute;
+
+/**
+ * Attribute to declare that the exception is "throwable" by apps.
+ *
+ * @since 32.0.0
+ */
+#[Attribute(Attribute::TARGET_ALL | Attribute::IS_REPEATABLE)]
+#[Consumable(since: '32.0.0')]
+#[Implementable(since: '32.0.0')]
+class Throwable extends ASince {
+}
diff --git a/lib/public/AppFramework/AuthPublicShareController.php b/lib/public/AppFramework/AuthPublicShareController.php
index d6c088d4a0c..28a92fedcc9 100644
--- a/lib/public/AppFramework/AuthPublicShareController.php
+++ b/lib/public/AppFramework/AuthPublicShareController.php
@@ -46,9 +46,6 @@ abstract class AuthPublicShareController extends PublicShareController {
}
/**
- * @PublicPage
- * @NoCSRFRequired
- *
* Show the authentication page
* The form has to submit to the authenticate method route
*
@@ -125,10 +122,6 @@ abstract class AuthPublicShareController extends PublicShareController {
}
/**
- * @UseSession
- * @PublicPage
- * @BruteForceProtection(action=publicLinkAuth)
- *
* Authenticate the share
*
* @since 14.0.0
@@ -196,10 +189,10 @@ abstract class AuthPublicShareController extends PublicShareController {
private function getRoute(string $function): string {
$app = strtolower($this->appName);
$class = (new \ReflectionClass($this))->getShortName();
- if (substr($class, -10) === 'Controller') {
+ if (str_ends_with($class, 'Controller')) {
$class = substr($class, 0, -10);
}
- return $app .'.'. $class .'.'. $function;
+ return $app . '.' . $class . '.' . $function;
}
/**
diff --git a/lib/public/AppFramework/Bootstrap/IBootstrap.php b/lib/public/AppFramework/Bootstrap/IBootstrap.php
index 81c34524191..7260d2b77a1 100644
--- a/lib/public/AppFramework/Bootstrap/IBootstrap.php
+++ b/lib/public/AppFramework/Bootstrap/IBootstrap.php
@@ -25,8 +25,6 @@ interface IBootstrap {
* At this stage you can assume that all services are registered and the DI
* container(s) are ready to be queried.
*
- * This is also the state where an optional `appinfo/app.php` was loaded.
- *
* @param IBootContext $context
*
* @since 20.0.0
diff --git a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php
index b86f7bcd76d..70b35228c87 100644
--- a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php
+++ b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php
@@ -17,6 +17,7 @@ use OCP\Collaboration\Reference\IReferenceProvider;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Template\ICustomTemplateProvider;
use OCP\IContainer;
+use OCP\Mail\Provider\IProvider as IMailProvider;
use OCP\Notification\INotifier;
use OCP\Preview\IProviderV2;
use OCP\SpeechToText\ISpeechToTextProvider;
@@ -68,7 +69,7 @@ interface IRegistrationContext {
* @param string $name
* @param callable $factory
* @psalm-param callable(\Psr\Container\ContainerInterface): mixed $factory
- * @param bool $shared
+ * @param bool $shared If set to true the factory result will be cached otherwise every query will call the factory again
*
* @return void
* @see IContainer::registerService()
@@ -412,4 +413,38 @@ interface IRegistrationContext {
* @since 30.0.0
*/
public function registerTaskProcessingTaskType(string $taskProcessingTaskTypeClass): void;
+
+ /**
+ * Register an implementation of \OCP\Files\Conversion\IConversionProvider
+ * that will handle the conversion of files from one MIME type to another
+ *
+ * @param string $class
+ * @psalm-param class-string<\OCP\Files\Conversion\IConversionProvider> $class
+ *
+ * @return void
+ *
+ * @since 31.0.0
+ */
+ public function registerFileConversionProvider(string $class): void;
+
+ /**
+ * Register a mail provider
+ *
+ * @param string $class
+ * @psalm-param class-string<IMailProvider> $class
+ * @since 30.0.0
+ */
+ public function registerMailProvider(string $class): void;
+
+
+ /**
+ * Register an implementation of \OCP\Config\Lexicon\IConfigLexicon that
+ * will handle the config lexicon
+ *
+ * @param string $configLexiconClass
+ *
+ * @psalm-param class-string<\OCP\Config\Lexicon\ILexicon> $configLexiconClass
+ * @since 31.0.0
+ */
+ public function registerConfigLexicon(string $configLexiconClass): void;
}
diff --git a/lib/public/AppFramework/Controller.php b/lib/public/AppFramework/Controller.php
index 44e8cecd810..cdeaac99366 100644
--- a/lib/public/AppFramework/Controller.php
+++ b/lib/public/AppFramework/Controller.php
@@ -123,7 +123,7 @@ abstract class Controller {
/**
* Serializes and formats a response
* @param mixed $response the value that was returned from a controller and
- * is not a Response instance
+ * is not a Response instance
* @param string $format the format for which a formatter has been registered
* @throws \DomainException if format does not match a registered formatter
* @return Response
@@ -135,7 +135,7 @@ abstract class Controller {
return $responder($response);
}
- throw new \DomainException('No responder registered for format '.
- $format . '!');
+ throw new \DomainException('No responder registered for format '
+ . $format . '!');
}
}
diff --git a/lib/public/AppFramework/Db/Entity.php b/lib/public/AppFramework/Db/Entity.php
index 46104e2faa4..3094070af5f 100644
--- a/lib/public/AppFramework/Db/Entity.php
+++ b/lib/public/AppFramework/Db/Entity.php
@@ -7,6 +7,8 @@
*/
namespace OCP\AppFramework\Db;
+use OCP\DB\Types;
+
use function lcfirst;
use function substr;
@@ -23,12 +25,13 @@ abstract class Entity {
public $id;
private array $_updatedFields = [];
+ /** @var array<string, \OCP\DB\Types::*> */
private array $_fieldTypes = ['id' => 'integer'];
/**
* Simple alternative constructor for building entities from a request
* @param array $params the array which was obtained via $this->params('key')
- * in the controller
+ * in the controller
* @since 7.0.0
*/
public static function fromParams(array $params): static {
@@ -52,9 +55,8 @@ abstract class Entity {
$instance = new static();
foreach ($row as $key => $value) {
- $prop = ucfirst($instance->columnToProperty($key));
- $setter = 'set' . $prop;
- $instance->$setter($value);
+ $prop = $instance->columnToProperty($key);
+ $instance->setter($prop, [$value]);
}
$instance->resetUpdatedFields();
@@ -64,10 +66,10 @@ abstract class Entity {
/**
- * @return array with attribute and type
+ * @return array<string, \OCP\DB\Types::*> with attribute and type
* @since 7.0.0
*/
- public function getFieldTypes() {
+ public function getFieldTypes(): array {
return $this->_fieldTypes;
}
@@ -76,50 +78,76 @@ abstract class Entity {
* Marks the entity as clean needed for setting the id after the insertion
* @since 7.0.0
*/
- public function resetUpdatedFields() {
+ public function resetUpdatedFields(): void {
$this->_updatedFields = [];
}
/**
* Generic setter for properties
+ *
+ * @throws \InvalidArgumentException
* @since 7.0.0
+ *
*/
protected function setter(string $name, array $args): void {
// setters should only work for existing attributes
- if (property_exists($this, $name)) {
- if ($args[0] === $this->$name) {
- return;
- }
- $this->markFieldUpdated($name);
-
- // if type definition exists, cast to correct type
- if ($args[0] !== null && array_key_exists($name, $this->_fieldTypes)) {
- $type = $this->_fieldTypes[$name];
- if ($type === 'blob') {
- // (B)LOB is treated as string when we read from the DB
- if (is_resource($args[0])) {
- $args[0] = stream_get_contents($args[0]);
- }
- $type = 'string';
+ if (!property_exists($this, $name)) {
+ throw new \BadFunctionCallException($name . ' is not a valid attribute');
+ }
+
+ if ($args[0] === $this->$name) {
+ return;
+ }
+ $this->markFieldUpdated($name);
+
+ // if type definition exists, cast to correct type
+ if ($args[0] !== null && array_key_exists($name, $this->_fieldTypes)) {
+ $type = $this->_fieldTypes[$name];
+ if ($type === Types::BLOB) {
+ // (B)LOB is treated as string when we read from the DB
+ if (is_resource($args[0])) {
+ $args[0] = stream_get_contents($args[0]);
}
+ $type = Types::STRING;
+ }
- if ($type === 'datetime') {
+ switch ($type) {
+ case Types::BIGINT:
+ case Types::SMALLINT:
+ settype($args[0], Types::INTEGER);
+ break;
+ case Types::BINARY:
+ case Types::DECIMAL:
+ case Types::TEXT:
+ settype($args[0], Types::STRING);
+ break;
+ case Types::TIME:
+ case Types::DATE:
+ case Types::DATETIME:
+ case Types::DATETIME_TZ:
if (!$args[0] instanceof \DateTime) {
$args[0] = new \DateTime($args[0]);
}
- } elseif ($type === 'json') {
+ break;
+ case Types::TIME_IMMUTABLE:
+ case Types::DATE_IMMUTABLE:
+ case Types::DATETIME_IMMUTABLE:
+ case Types::DATETIME_TZ_IMMUTABLE:
+ if (!$args[0] instanceof \DateTimeImmutable) {
+ $args[0] = new \DateTimeImmutable($args[0]);
+ }
+ break;
+ case Types::JSON:
if (!is_array($args[0])) {
$args[0] = json_decode($args[0], true);
}
- } else {
+ break;
+ default:
settype($args[0], $type);
- }
}
- $this->$name = $args[0];
- } else {
- throw new \BadFunctionCallException($name .
- ' is not a valid attribute');
}
+ $this->$name = $args[0];
+
}
/**
@@ -131,8 +159,8 @@ abstract class Entity {
if (property_exists($this, $name)) {
return $this->$name;
} else {
- throw new \BadFunctionCallException($name .
- ' is not a valid attribute');
+ throw new \BadFunctionCallException($name
+ . ' is not a valid attribute');
}
}
@@ -152,8 +180,8 @@ abstract class Entity {
} elseif ($this->isGetterForBoolProperty($methodName)) {
return $this->getter(lcfirst(substr($methodName, 2)));
} else {
- throw new \BadFunctionCallException($methodName .
- ' does not exist');
+ throw new \BadFunctionCallException($methodName
+ . ' does not exist');
}
}
@@ -182,16 +210,17 @@ abstract class Entity {
/**
* Transform a database columnname to a property
+ *
* @param string $columnName the name of the column
* @return string the property name
* @since 7.0.0
*/
- public function columnToProperty($columnName) {
+ public function columnToProperty(string $columnName) {
$parts = explode('_', $columnName);
- $property = null;
+ $property = '';
foreach ($parts as $part) {
- if ($property === null) {
+ if ($property === '') {
$property = $part;
} else {
$property .= ucfirst($part);
@@ -204,16 +233,17 @@ abstract class Entity {
/**
* Transform a property to a database column name
+ *
* @param string $property the name of the property
* @return string the column name
* @since 7.0.0
*/
- public function propertyToColumn($property) {
+ public function propertyToColumn(string $property): string {
$parts = preg_split('/(?=[A-Z])/', $property);
- $column = null;
+ $column = '';
foreach ($parts as $part) {
- if ($column === null) {
+ if ($column === '') {
$column = $part;
} else {
$column .= '_' . lcfirst($part);
@@ -228,19 +258,33 @@ abstract class Entity {
* @return array array of updated fields for update query
* @since 7.0.0
*/
- public function getUpdatedFields() {
+ public function getUpdatedFields(): array {
return $this->_updatedFields;
}
/**
- * Adds type information for a field so that its automatically casted to
+ * Adds type information for a field so that it's automatically cast to
* that value once its being returned from the database
+ *
* @param string $fieldName the name of the attribute
- * @param string $type the type which will be used to call settype()
+ * @param \OCP\DB\Types::* $type the type which will be used to match a cast
+ * @since 31.0.0 Parameter $type is now restricted to {@see \OCP\DB\Types} constants. The formerly accidentally supported types 'int'|'bool'|'double' are mapped to Types::INTEGER|Types::BOOLEAN|Types::FLOAT accordingly.
* @since 7.0.0
*/
- protected function addType($fieldName, $type) {
+ protected function addType(string $fieldName, string $type): void {
+ /** @psalm-suppress TypeDoesNotContainType */
+ if (in_array($type, ['bool', 'double', 'int', 'array', 'object'], true)) {
+ // Mapping legacy strings to the actual types
+ $type = match ($type) {
+ 'int' => Types::INTEGER,
+ 'bool' => Types::BOOLEAN,
+ 'double' => Types::FLOAT,
+ 'array',
+ 'object' => Types::STRING,
+ };
+ }
+
$this->_fieldTypes[$fieldName] = $type;
}
@@ -248,12 +292,13 @@ abstract class Entity {
/**
* Slugify the value of a given attribute
* Warning: This doesn't result in a unique value
+ *
* @param string $attributeName the name of the attribute, which value should be slugified
* @return string slugified value
* @since 7.0.0
* @deprecated 24.0.0
*/
- public function slugify($attributeName) {
+ public function slugify(string $attributeName): string {
// toSlug should only work for existing attributes
if (property_exists($this, $attributeName)) {
$value = $this->$attributeName;
@@ -262,9 +307,8 @@ abstract class Entity {
$value = strtolower($value);
// trim '-'
return trim($value, '-');
- } else {
- throw new \BadFunctionCallException($attributeName .
- ' is not a valid attribute');
}
+
+ throw new \BadFunctionCallException($attributeName . ' is not a valid attribute');
}
}
diff --git a/lib/public/AppFramework/Db/QBMapper.php b/lib/public/AppFramework/Db/QBMapper.php
index badd2483b58..7fb5b2a9afd 100644
--- a/lib/public/AppFramework/Db/QBMapper.php
+++ b/lib/public/AppFramework/Db/QBMapper.php
@@ -7,8 +7,10 @@ declare(strict_types=1);
*/
namespace OCP\AppFramework\Db;
+use Generator;
use OCP\DB\Exception;
use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\DB\Types;
use OCP\IDBConnection;
/**
@@ -33,7 +35,7 @@ abstract class QBMapper {
* @param IDBConnection $db Instance of the Db abstraction layer
* @param string $tableName the name of the table. set this to allow entity
* @param class-string<T>|null $entityClass the name of the entity that the sql should be
- * mapped to queries without using sql
+ * mapped to queries without using sql
* @since 14.0.0
*/
public function __construct(IDBConnection $db, string $tableName, ?string $entityClass = null) {
@@ -202,7 +204,7 @@ abstract class QBMapper {
* Returns the type parameter for the QueryBuilder for a specific property
* of the $entity
*
- * @param Entity $entity The entity to get the types from
+ * @param Entity $entity The entity to get the types from
* @psalm-param T $entity
* @param string $property The property of $entity to get the type for
* @return int|string
@@ -217,18 +219,33 @@ abstract class QBMapper {
switch ($types[ $property ]) {
case 'int':
- case 'integer':
+ case Types::INTEGER:
+ case Types::SMALLINT:
return IQueryBuilder::PARAM_INT;
- case 'string':
+ case Types::STRING:
return IQueryBuilder::PARAM_STR;
case 'bool':
- case 'boolean':
+ case Types::BOOLEAN:
return IQueryBuilder::PARAM_BOOL;
- case 'blob':
+ case Types::BLOB:
return IQueryBuilder::PARAM_LOB;
- case 'datetime':
- return IQueryBuilder::PARAM_DATE;
- case 'json':
+ case Types::DATE:
+ return IQueryBuilder::PARAM_DATETIME_MUTABLE;
+ case Types::DATETIME:
+ return IQueryBuilder::PARAM_DATETIME_MUTABLE;
+ case Types::DATETIME_TZ:
+ return IQueryBuilder::PARAM_DATETIME_TZ_MUTABLE;
+ case Types::DATE_IMMUTABLE:
+ return IQueryBuilder::PARAM_DATE_IMMUTABLE;
+ case Types::DATETIME_IMMUTABLE:
+ return IQueryBuilder::PARAM_DATETIME_IMMUTABLE;
+ case Types::DATETIME_TZ_IMMUTABLE:
+ return IQueryBuilder::PARAM_DATETIME_TZ_IMMUTABLE;
+ case Types::TIME:
+ return IQueryBuilder::PARAM_TIME_MUTABLE;
+ case Types::TIME_IMMUTABLE:
+ return IQueryBuilder::PARAM_TIME_IMMUTABLE;
+ case Types::JSON:
return IQueryBuilder::PARAM_JSON;
}
@@ -279,8 +296,8 @@ abstract class QBMapper {
* @since 14.0.0
*/
private function buildDebugMessage(string $msg, IQueryBuilder $sql): string {
- return $msg .
- ': query "' . $sql->getSQL() . '"; ';
+ return $msg
+ . ': query "' . $sql->getSQL() . '"; ';
}
@@ -295,7 +312,7 @@ abstract class QBMapper {
*/
protected function mapRowToEntity(array $row): Entity {
unset($row['DOCTRINE_ROWNUM']); // remove doctrine/dbal helper column
- return \call_user_func($this->entityClass .'::fromRow', $row);
+ return \call_user_func($this->entityClass . '::fromRow', $row);
}
@@ -303,8 +320,8 @@ abstract class QBMapper {
* Runs a sql query and returns an array of entities
*
* @param IQueryBuilder $query
- * @return Entity[] all fetched entities
- * @psalm-return T[] all fetched entities
+ * @return list<Entity> all fetched entities
+ * @psalm-return list<T> all fetched entities
* @throws Exception
* @since 14.0.0
*/
@@ -321,6 +338,26 @@ abstract class QBMapper {
}
}
+ /**
+ * Runs a sql query and yields each resulting entity to obtain database entries in a memory-efficient way
+ *
+ * @param IQueryBuilder $query
+ * @return Generator Generator of fetched entities
+ * @psalm-return Generator<T> Generator of fetched entities
+ * @throws Exception
+ * @since 30.0.0
+ */
+ protected function yieldEntities(IQueryBuilder $query): Generator {
+ $result = $query->executeQuery();
+ try {
+ while ($row = $result->fetch()) {
+ yield $this->mapRowToEntity($row);
+ }
+ } finally {
+ $result->closeCursor();
+ }
+ }
+
/**
* Returns an db result and throws exceptions when there are more or less
diff --git a/lib/public/AppFramework/Http/Attribute/ARateLimit.php b/lib/public/AppFramework/Http/Attribute/ARateLimit.php
index d92fcae1ae1..c06b1180ae3 100644
--- a/lib/public/AppFramework/Http/Attribute/ARateLimit.php
+++ b/lib/public/AppFramework/Http/Attribute/ARateLimit.php
@@ -17,6 +17,8 @@ namespace OCP\AppFramework\Http\Attribute;
*/
abstract class ARateLimit {
/**
+ * @param int $limit The maximum number of requests that can be made in the given period in seconds.
+ * @param int $period The time period in seconds.
* @since 27.0.0
*/
public function __construct(
diff --git a/lib/public/AppFramework/Http/Attribute/AppApiAdminAccessWithoutUser.php b/lib/public/AppFramework/Http/Attribute/AppApiAdminAccessWithoutUser.php
new file mode 100644
index 00000000000..6b78fee41af
--- /dev/null
+++ b/lib/public/AppFramework/Http/Attribute/AppApiAdminAccessWithoutUser.php
@@ -0,0 +1,21 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\AppFramework\Http\Attribute;
+
+use Attribute;
+
+/**
+ * Attribute for (sub)administrator controller methods that allow access for ExApps when the User is not set.
+ *
+ * @since 30.0.0
+ */
+#[Attribute]
+class AppApiAdminAccessWithoutUser {
+}
diff --git a/lib/public/AppFramework/Http/Attribute/AuthorizedAdminSetting.php b/lib/public/AppFramework/Http/Attribute/AuthorizedAdminSetting.php
index e81f195b298..83101143fc9 100644
--- a/lib/public/AppFramework/Http/Attribute/AuthorizedAdminSetting.php
+++ b/lib/public/AppFramework/Http/Attribute/AuthorizedAdminSetting.php
@@ -25,7 +25,7 @@ class AuthorizedAdminSetting {
* @since 27.0.0
*/
public function __construct(
- protected string $settings
+ protected string $settings,
) {
}
diff --git a/lib/public/AppFramework/Http/Attribute/BruteForceProtection.php b/lib/public/AppFramework/Http/Attribute/BruteForceProtection.php
index 9fd97cdc3ed..0fc1a3b9b6d 100644
--- a/lib/public/AppFramework/Http/Attribute/BruteForceProtection.php
+++ b/lib/public/AppFramework/Http/Attribute/BruteForceProtection.php
@@ -23,7 +23,7 @@ class BruteForceProtection {
* @since 27.0.0
*/
public function __construct(
- protected string $action
+ protected string $action,
) {
}
diff --git a/lib/public/AppFramework/Http/Attribute/CORS.php b/lib/public/AppFramework/Http/Attribute/CORS.php
index 2c3eac362cf..ff639635635 100644
--- a/lib/public/AppFramework/Http/Attribute/CORS.php
+++ b/lib/public/AppFramework/Http/Attribute/CORS.php
@@ -12,7 +12,9 @@ namespace OCP\AppFramework\Http\Attribute;
use Attribute;
/**
- * Attribute for controller methods that can also be accessed by not logged-in user
+ * Attribute for controller methods that can also be accessed by other websites.
+ * See https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS for an explanation of the functionality and the security implications.
+ * See https://docs.nextcloud.com/server/latest/developer_manual/digging_deeper/rest_apis.html on how to implement it in your controller.
*
* @since 27.0.0
*/
diff --git a/lib/public/AppFramework/Http/Attribute/ExAppRequired.php b/lib/public/AppFramework/Http/Attribute/ExAppRequired.php
new file mode 100644
index 00000000000..eb18da8027c
--- /dev/null
+++ b/lib/public/AppFramework/Http/Attribute/ExAppRequired.php
@@ -0,0 +1,21 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\AppFramework\Http\Attribute;
+
+use Attribute;
+
+/**
+ * Attribute for controller methods that can only be accessed by ExApps
+ *
+ * @since 30.0.0
+ */
+#[Attribute]
+class ExAppRequired {
+}
diff --git a/lib/public/AppFramework/Http/Attribute/OpenAPI.php b/lib/public/AppFramework/Http/Attribute/OpenAPI.php
index e996bffd917..1b44b2a57fe 100644
--- a/lib/public/AppFramework/Http/Attribute/OpenAPI.php
+++ b/lib/public/AppFramework/Http/Attribute/OpenAPI.php
@@ -51,13 +51,21 @@ class OpenAPI {
public const SCOPE_IGNORE = 'ignore';
/**
+ * APIs used by ExApps.
+ * Will be set automatically when an ExApp is required to access the route.
+ *
+ * @since 30.0.0
+ */
+ public const SCOPE_EX_APP = 'ex_app';
+
+ /**
* @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).
+ * 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).
+ * 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(
diff --git a/lib/public/AppFramework/Http/Attribute/PasswordConfirmationRequired.php b/lib/public/AppFramework/Http/Attribute/PasswordConfirmationRequired.php
index 0f0f4b38040..c41e5aa2445 100644
--- a/lib/public/AppFramework/Http/Attribute/PasswordConfirmationRequired.php
+++ b/lib/public/AppFramework/Http/Attribute/PasswordConfirmationRequired.php
@@ -18,4 +18,21 @@ use Attribute;
*/
#[Attribute]
class PasswordConfirmationRequired {
+ /**
+ * @param bool $strict - Whether password confirmation needs to happen in the request.
+ *
+ * @since 31.0.0
+ */
+ public function __construct(
+ protected bool $strict = false,
+ ) {
+ }
+
+ /**
+ * @since 31.0.0
+ */
+ public function getStrict(): bool {
+ return $this->strict;
+ }
+
}
diff --git a/lib/public/AppFramework/Http/Attribute/RequestHeader.php b/lib/public/AppFramework/Http/Attribute/RequestHeader.php
new file mode 100644
index 00000000000..1d0fbbfa0c3
--- /dev/null
+++ b/lib/public/AppFramework/Http/Attribute/RequestHeader.php
@@ -0,0 +1,34 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\AppFramework\Http\Attribute;
+
+use Attribute;
+
+/**
+ * This attribute allows documenting request headers and is primarily intended for OpenAPI documentation.
+ * It should be added whenever you use a request header in a controller method, in order to properly describe the header and its functionality.
+ * There are no checks that ensure the header is set, so you will still need to do this yourself in the controller method.
+ *
+ * @since 32.0.0
+ */
+#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
+class RequestHeader {
+ /**
+ * @param lowercase-string $name The name of the request header
+ * @param non-empty-string $description The description of the request header
+ * @param bool $indirect Allow indirect usage of the header for example in a middleware. Enabling this turns off the check which ensures that the header must be referenced in the controller method.
+ */
+ public function __construct(
+ protected string $name,
+ protected string $description,
+ protected bool $indirect = false,
+ ) {
+ }
+}
diff --git a/lib/public/AppFramework/Http/ContentSecurityPolicy.php b/lib/public/AppFramework/Http/ContentSecurityPolicy.php
index 281aaa06eb8..11ec79bbdb7 100644
--- a/lib/public/AppFramework/Http/ContentSecurityPolicy.php
+++ b/lib/public/AppFramework/Http/ContentSecurityPolicy.php
@@ -38,7 +38,7 @@ class ContentSecurityPolicy extends EmptyContentSecurityPolicy {
];
/**
* @var bool Whether inline CSS is allowed
- * TODO: Disallow per default
+ * TODO: Disallow per default
* @link https://github.com/owncloud/core/issues/13458
*/
protected $inlineStyleAllowed = true;
diff --git a/lib/public/AppFramework/Http/DataDisplayResponse.php b/lib/public/AppFramework/Http/DataDisplayResponse.php
index 889c57a7901..e1ded910328 100644
--- a/lib/public/AppFramework/Http/DataDisplayResponse.php
+++ b/lib/public/AppFramework/Http/DataDisplayResponse.php
@@ -13,9 +13,9 @@ use OCP\AppFramework\Http;
* Class DataDisplayResponse
*
* @since 8.1.0
- * @template S of int
+ * @template S of Http::STATUS_*
* @template H of array<string, mixed>
- * @template-extends Response<int, array<string, mixed>>
+ * @template-extends Response<Http::STATUS_*, array<string, mixed>>
*/
class DataDisplayResponse extends Response {
/**
diff --git a/lib/public/AppFramework/Http/DataDownloadResponse.php b/lib/public/AppFramework/Http/DataDownloadResponse.php
index 80100137c48..ee6bcf0d0c5 100644
--- a/lib/public/AppFramework/Http/DataDownloadResponse.php
+++ b/lib/public/AppFramework/Http/DataDownloadResponse.php
@@ -13,10 +13,10 @@ use OCP\AppFramework\Http;
* Class DataDownloadResponse
*
* @since 8.0.0
- * @template S of int
+ * @template S of Http::STATUS_*
* @template C of string
* @template H of array<string, mixed>
- * @template-extends DownloadResponse<int, string, array<string, mixed>>
+ * @template-extends DownloadResponse<Http::STATUS_*, string, array<string, mixed>>
*/
class DataDownloadResponse extends DownloadResponse {
/**
diff --git a/lib/public/AppFramework/Http/DataResponse.php b/lib/public/AppFramework/Http/DataResponse.php
index 2ebb66f9e73..2b54ce848ef 100644
--- a/lib/public/AppFramework/Http/DataResponse.php
+++ b/lib/public/AppFramework/Http/DataResponse.php
@@ -14,10 +14,10 @@ use OCP\AppFramework\Http;
* for responders to transform
* @since 8.0.0
* @psalm-type DataResponseType = array|int|float|string|bool|object|null|\stdClass|\JsonSerializable
- * @template S of int
+ * @template S of Http::STATUS_*
* @template-covariant T of DataResponseType
* @template H of array<string, mixed>
- * @template-extends Response<int, array<string, mixed>>
+ * @template-extends Response<Http::STATUS_*, array<string, mixed>>
*/
class DataResponse extends Response {
/**
diff --git a/lib/public/AppFramework/Http/DownloadResponse.php b/lib/public/AppFramework/Http/DownloadResponse.php
index 058b3070297..190de022d36 100644
--- a/lib/public/AppFramework/Http/DownloadResponse.php
+++ b/lib/public/AppFramework/Http/DownloadResponse.php
@@ -12,10 +12,10 @@ use OCP\AppFramework\Http;
/**
* Prompts the user to download the a file
* @since 7.0.0
- * @template S of int
+ * @template S of Http::STATUS_*
* @template C of string
* @template H of array<string, mixed>
- * @template-extends Response<int, array<string, mixed>>
+ * @template-extends Response<Http::STATUS_*, array<string, mixed>>
*/
class DownloadResponse extends Response {
/**
diff --git a/lib/public/AppFramework/Http/EmptyContentSecurityPolicy.php b/lib/public/AppFramework/Http/EmptyContentSecurityPolicy.php
index 5b4cf7eab8b..b8bbfdb7d67 100644
--- a/lib/public/AppFramework/Http/EmptyContentSecurityPolicy.php
+++ b/lib/public/AppFramework/Http/EmptyContentSecurityPolicy.php
@@ -26,7 +26,7 @@ class EmptyContentSecurityPolicy {
protected $strictDynamicAllowedOnScripts = null;
/**
* @var bool Whether eval in JS scripts is allowed
- * TODO: Disallow per default
+ * TODO: Disallow per default
* @link https://github.com/owncloud/core/issues/11925
*/
protected $evalScriptAllowed = null;
@@ -36,7 +36,7 @@ class EmptyContentSecurityPolicy {
protected $allowedScriptDomains = null;
/**
* @var bool Whether inline CSS is allowed
- * TODO: Disallow per default
+ * TODO: Disallow per default
* @link https://github.com/owncloud/core/issues/13458
*/
protected $inlineStyleAllowed = null;
@@ -89,7 +89,7 @@ class EmptyContentSecurityPolicy {
}
/**
- * Use the according JS nonce
+ * The base64 encoded nonce to be used for script source.
* This method is only for CSPMiddleware, custom values are ignored in mergePolicies of ContentSecurityPolicyManager
*
* @param string $nonce
@@ -106,7 +106,7 @@ class EmptyContentSecurityPolicy {
* @param bool $state
* @return $this
* @since 8.1.0
- * @deprecated Eval should not be used anymore. Please update your scripts. This function will stop functioning in a future version of Nextcloud.
+ * @deprecated 17.0.0 Eval should not be used anymore. Please update your scripts. This function will stop functioning in a future version of Nextcloud.
*/
public function allowEvalScript($state = true) {
$this->evalScriptAllowed = $state;
@@ -448,7 +448,7 @@ class EmptyContentSecurityPolicy {
if ($this->strictDynamicAllowed) {
$scriptSrc .= '\'strict-dynamic\' ';
}
- $scriptSrc .= '\'nonce-'.base64_encode($this->jsNonce).'\'';
+ $scriptSrc .= '\'nonce-' . $this->jsNonce . '\'';
$allowedScriptDomains = array_flip($this->allowedScriptDomains);
unset($allowedScriptDomains['\'self\'']);
$this->allowedScriptDomains = array_flip($allowedScriptDomains);
diff --git a/lib/public/AppFramework/Http/Events/BeforeLoginTemplateRenderedEvent.php b/lib/public/AppFramework/Http/Events/BeforeLoginTemplateRenderedEvent.php
index d18cad25acc..b724b3a72ad 100644
--- a/lib/public/AppFramework/Http/Events/BeforeLoginTemplateRenderedEvent.php
+++ b/lib/public/AppFramework/Http/Events/BeforeLoginTemplateRenderedEvent.php
@@ -20,7 +20,9 @@ class BeforeLoginTemplateRenderedEvent extends Event {
/**
* @since 28.0.0
*/
- public function __construct(private TemplateResponse $response) {
+ public function __construct(
+ private TemplateResponse $response,
+ ) {
parent::__construct();
}
diff --git a/lib/public/AppFramework/Http/FileDisplayResponse.php b/lib/public/AppFramework/Http/FileDisplayResponse.php
index 0cc51f7c59f..c18404b7d91 100644
--- a/lib/public/AppFramework/Http/FileDisplayResponse.php
+++ b/lib/public/AppFramework/Http/FileDisplayResponse.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -13,9 +14,9 @@ use OCP\Files\SimpleFS\ISimpleFile;
* Class FileDisplayResponse
*
* @since 11.0.0
- * @template S of int
+ * @template S of Http::STATUS_*
* @template H of array<string, mixed>
- * @template-extends Response<int, array<string, mixed>>
+ * @template-extends Response<Http::STATUS_*, array<string, mixed>>
*/
class FileDisplayResponse extends Response implements ICallbackResponse {
/** @var File|ISimpleFile */
diff --git a/lib/public/AppFramework/Http/JSONResponse.php b/lib/public/AppFramework/Http/JSONResponse.php
index 1614cb8ce01..a226e29a1b5 100644
--- a/lib/public/AppFramework/Http/JSONResponse.php
+++ b/lib/public/AppFramework/Http/JSONResponse.php
@@ -12,10 +12,10 @@ use OCP\AppFramework\Http;
/**
* A renderer for JSON calls
* @since 6.0.0
- * @template S of int
- * @template-covariant T of array|object|\stdClass|\JsonSerializable
+ * @template S of Http::STATUS_*
+ * @template-covariant T of null|string|int|float|bool|array|\stdClass|\JsonSerializable
* @template H of array<string, mixed>
- * @template-extends Response<int, array<string, mixed>>
+ * @template-extends Response<Http::STATUS_*, array<string, mixed>>
*/
class JSONResponse extends Response {
/**
@@ -23,6 +23,11 @@ class JSONResponse extends Response {
* @var T
*/
protected $data;
+ /**
+ * Additional `json_encode` flags
+ * @var int
+ */
+ protected $encodeFlags;
/**
@@ -30,12 +35,20 @@ class JSONResponse extends Response {
* @param T $data the object or array that should be transformed
* @param S $statusCode the Http status code, defaults to 200
* @param H $headers
+ * @param int $encodeFlags Additional `json_encode` flags
* @since 6.0.0
+ * @since 30.0.0 Added `$encodeFlags` param
*/
- public function __construct(mixed $data = [], int $statusCode = Http::STATUS_OK, array $headers = []) {
+ public function __construct(
+ mixed $data = [],
+ int $statusCode = Http::STATUS_OK,
+ array $headers = [],
+ int $encodeFlags = 0,
+ ) {
parent::__construct($statusCode, $headers);
$this->data = $data;
+ $this->encodeFlags = $encodeFlags;
$this->addHeader('Content-Type', 'application/json; charset=utf-8');
}
@@ -45,16 +58,19 @@ class JSONResponse extends Response {
* @return string the rendered json
* @since 6.0.0
* @throws \Exception If data could not get encoded
+ *
+ * @psalm-taint-escape has_quotes
+ * @psalm-taint-escape html
*/
public function render() {
- return json_encode($this->data, JSON_HEX_TAG | JSON_THROW_ON_ERROR);
+ return json_encode($this->data, JSON_HEX_TAG | JSON_THROW_ON_ERROR | $this->encodeFlags, 2048);
}
/**
* Sets values in the data json array
* @psalm-suppress InvalidTemplateParam
* @param T $data an array or object which will be transformed
- * to JSON
+ * to JSON
* @return JSONResponse Reference to this object
* @since 6.0.0 - return value was added in 7.0.0
*/
diff --git a/lib/public/AppFramework/Http/NotFoundResponse.php b/lib/public/AppFramework/Http/NotFoundResponse.php
index 9ebefe69be1..137d1a26655 100644
--- a/lib/public/AppFramework/Http/NotFoundResponse.php
+++ b/lib/public/AppFramework/Http/NotFoundResponse.php
@@ -12,9 +12,9 @@ use OCP\AppFramework\Http;
/**
* A generic 404 response showing an 404 error page as well to the end-user
* @since 8.1.0
- * @template S of int
+ * @template S of Http::STATUS_*
* @template H of array<string, mixed>
- * @template-extends TemplateResponse<int, array<string, mixed>>
+ * @template-extends TemplateResponse<Http::STATUS_*, array<string, mixed>>
*/
class NotFoundResponse extends TemplateResponse {
/**
diff --git a/lib/public/AppFramework/Http/RedirectResponse.php b/lib/public/AppFramework/Http/RedirectResponse.php
index 41fc4d83856..74847205976 100644
--- a/lib/public/AppFramework/Http/RedirectResponse.php
+++ b/lib/public/AppFramework/Http/RedirectResponse.php
@@ -12,9 +12,9 @@ use OCP\AppFramework\Http;
/**
* Redirects to a different URL
* @since 7.0.0
- * @template S of int
+ * @template S of Http::STATUS_*
* @template H of array<string, mixed>
- * @template-extends Response<int, array<string, mixed>>
+ * @template-extends Response<Http::STATUS_*, array<string, mixed>>
*/
class RedirectResponse extends Response {
private $redirectURL;
diff --git a/lib/public/AppFramework/Http/RedirectToDefaultAppResponse.php b/lib/public/AppFramework/Http/RedirectToDefaultAppResponse.php
index 3e2fcf6f6c7..0a0c04f671d 100644
--- a/lib/public/AppFramework/Http/RedirectToDefaultAppResponse.php
+++ b/lib/public/AppFramework/Http/RedirectToDefaultAppResponse.php
@@ -16,9 +16,9 @@ use OCP\IURLGenerator;
*
* @since 16.0.0
* @deprecated 23.0.0 Use RedirectResponse() with IURLGenerator::linkToDefaultPageUrl() instead
- * @template S of int
+ * @template S of Http::STATUS_*
* @template H of array<string, mixed>
- * @template-extends RedirectResponse<int, array<string, mixed>>
+ * @template-extends RedirectResponse<Http::STATUS_*, array<string, mixed>>
*/
class RedirectToDefaultAppResponse extends RedirectResponse {
/**
@@ -30,8 +30,7 @@ class RedirectToDefaultAppResponse extends RedirectResponse {
* @deprecated 23.0.0 Use RedirectResponse() with IURLGenerator::linkToDefaultPageUrl() instead
*/
public function __construct(int $status = Http::STATUS_SEE_OTHER, array $headers = []) {
- /** @var IURLGenerator $urlGenerator */
- $urlGenerator = \OC::$server->get(IURLGenerator::class);
+ $urlGenerator = \OCP\Server::get(IURLGenerator::class);
parent::__construct($urlGenerator->linkToDefaultPageUrl(), $status, $headers);
}
}
diff --git a/lib/public/AppFramework/Http/Response.php b/lib/public/AppFramework/Http/Response.php
index 0da290ad48b..bdebb12c00d 100644
--- a/lib/public/AppFramework/Http/Response.php
+++ b/lib/public/AppFramework/Http/Response.php
@@ -18,7 +18,7 @@ use Psr\Log\LoggerInterface;
*
* It handles headers, HTTP status code, last modified and ETag.
* @since 6.0.0
- * @template S of int
+ * @template S of Http::STATUS_*
* @template H of array<string, mixed>
*/
class Response {
@@ -93,11 +93,10 @@ class Response {
// Set expires header
$expires = new \DateTime();
- /** @var ITimeFactory $time */
$time = \OCP\Server::get(ITimeFactory::class);
$expires->setTimestamp($time->getTime());
- $expires->add(new \DateInterval('PT'.$cacheSeconds.'S'));
- $this->addHeader('Expires', $expires->format(\DateTimeInterface::RFC2822));
+ $expires->add(new \DateInterval('PT' . $cacheSeconds . 'S'));
+ $this->addHeader('Expires', $expires->format(\DateTimeInterface::RFC7231));
} else {
$this->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
unset($this->headers['Expires']);
@@ -111,8 +110,8 @@ class Response {
* @param string $name The name of the cookie
* @param string $value The value of the cookie
* @param \DateTime|null $expireDate Date on that the cookie should expire, if set
- * to null cookie will be considered as session
- * cookie.
+ * to null cookie will be considered as session
+ * cookie.
* @param string $sameSite The samesite value of the cookie. Defaults to Lax. Other possibilities are Strict or None
* @return $this
* @since 8.0.0
@@ -184,10 +183,10 @@ class Response {
if ($this->status === Http::STATUS_NOT_MODIFIED
&& stripos($name, 'x-') === 0) {
/** @var IConfig $config */
- $config = \OC::$server->get(IConfig::class);
+ $config = \OCP\Server::get(IConfig::class);
if ($config->getSystemValueBool('debug', false)) {
- \OC::$server->get(LoggerInterface::class)->error('Setting custom header on a 304 is not supported (Header: {header})', [
+ \OCP\Server::get(LoggerInterface::class)->error('Setting custom header on a 304 is not supported (Header: {header})', [
'header' => $name,
]);
}
@@ -229,7 +228,7 @@ class Response {
/**
* @psalm-suppress UndefinedClass
*/
- $request = \OC::$server->get(IRequest::class);
+ $request = \OCP\Server::get(IRequest::class);
$mergeWith = [
'X-Request-Id' => $request->getId(),
'Cache-Control' => 'no-cache, no-store, must-revalidate',
@@ -239,7 +238,7 @@ class Response {
];
if ($this->lastModified) {
- $mergeWith['Last-Modified'] = $this->lastModified->format(\DateTimeInterface::RFC2822);
+ $mergeWith['Last-Modified'] = $this->lastModified->format(\DateTimeInterface::RFC7231);
}
if ($this->ETag) {
@@ -289,7 +288,7 @@ class Response {
/**
* Get the currently used Content-Security-Policy
* @return EmptyContentSecurityPolicy|null Used Content-Security-Policy or null if
- * none specified.
+ * none specified.
* @since 8.1.0
*/
public function getContentSecurityPolicy() {
diff --git a/lib/public/AppFramework/Http/StandaloneTemplateResponse.php b/lib/public/AppFramework/Http/StandaloneTemplateResponse.php
index f729bd772fb..244a6b80f9f 100644
--- a/lib/public/AppFramework/Http/StandaloneTemplateResponse.php
+++ b/lib/public/AppFramework/Http/StandaloneTemplateResponse.php
@@ -7,6 +7,8 @@ declare(strict_types=1);
*/
namespace OCP\AppFramework\Http;
+use OCP\AppFramework\Http;
+
/**
* A template response that does not emit the loadAdditionalScripts events.
*
@@ -14,9 +16,9 @@ namespace OCP\AppFramework\Http;
* full nextcloud UI. Like the 2FA page, or the grant page in the login flow.
*
* @since 16.0.0
- * @template S of int
+ * @template S of Http::STATUS_*
* @template H of array<string, mixed>
- * @template-extends TemplateResponse<int, array<string, mixed>>
+ * @template-extends TemplateResponse<Http::STATUS_*, array<string, mixed>>
*/
class StandaloneTemplateResponse extends TemplateResponse {
}
diff --git a/lib/public/AppFramework/Http/StreamResponse.php b/lib/public/AppFramework/Http/StreamResponse.php
index 1039e20e5c5..d0e6e3e148a 100644
--- a/lib/public/AppFramework/Http/StreamResponse.php
+++ b/lib/public/AppFramework/Http/StreamResponse.php
@@ -13,9 +13,9 @@ use OCP\AppFramework\Http;
* Class StreamResponse
*
* @since 8.1.0
- * @template S of int
+ * @template S of Http::STATUS_*
* @template H of array<string, mixed>
- * @template-extends Response<int, array<string, mixed>>
+ * @template-extends Response<Http::STATUS_*, array<string, mixed>>
*/
class StreamResponse extends Response implements ICallbackResponse {
/** @var string */
diff --git a/lib/public/AppFramework/Http/Template/ExternalShareMenuAction.php b/lib/public/AppFramework/Http/Template/ExternalShareMenuAction.php
index cddf5d19171..281bb559a10 100644
--- a/lib/public/AppFramework/Http/Template/ExternalShareMenuAction.php
+++ b/lib/public/AppFramework/Http/Template/ExternalShareMenuAction.php
@@ -1,58 +1,29 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCP\AppFramework\Http\Template;
-use OCP\Util;
-
/**
* Class LinkMenuAction
*
* @since 14.0.0
*/
class ExternalShareMenuAction extends SimpleMenuAction {
- /** @var string */
- private $owner;
-
- /** @var string */
- private $displayname;
-
- /** @var string */
- private $shareName;
/**
* ExternalShareMenuAction constructor.
*
- * @param string $label
- * @param string $icon
- * @param string $owner
- * @param string $displayname
- * @param string $shareName
+ * @param string $label Translated label
+ * @param string $icon Icon CSS class
+ * @param string $owner Owner user ID (unused)
+ * @param string $displayname Display name of the owner (unused)
+ * @param string $shareName Name of the share (unused)
* @since 14.0.0
*/
public function __construct(string $label, string $icon, string $owner, string $displayname, string $shareName) {
parent::__construct('save', $label, $icon);
- $this->owner = $owner;
- $this->displayname = $displayname;
- $this->shareName = $shareName;
- }
-
- /**
- * @since 14.0.0
- */
- public function render(): string {
- return '<li>' .
- ' <button id="save-external-share" class="icon ' . Util::sanitizeHTML($this->getIcon()) . '" data-protected="false" data-owner-display-name="' . Util::sanitizeHTML($this->displayname) . '" data-owner="' . Util::sanitizeHTML($this->owner) . '" data-name="' . Util::sanitizeHTML($this->shareName) . '">' . Util::sanitizeHTML($this->getLabel()) . '</button>' .
- '</li>' .
- '<li id="external-share-menu-item" class="hidden">' .
- ' <span class="menuitem">' .
- ' <form class="save-form" action="#">' .
- ' <input type="text" id="remote_address" placeholder="user@yourNextcloud.org">' .
- ' <input type="submit" value=" " id="save-button-confirm" class="icon-confirm" disabled="disabled"></button>' .
- ' </form>' .
- ' </span>' .
- '</li>';
}
}
diff --git a/lib/public/AppFramework/Http/Template/IMenuAction.php b/lib/public/AppFramework/Http/Template/IMenuAction.php
index d3962300923..124e95fe019 100644
--- a/lib/public/AppFramework/Http/Template/IMenuAction.php
+++ b/lib/public/AppFramework/Http/Template/IMenuAction.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -18,12 +19,16 @@ interface IMenuAction {
public function getId(): string;
/**
+ * The translated label of the menu item.
+ *
* @since 14.0.0
* @return string
*/
public function getLabel(): string;
/**
+ * The link this menu item points to.
+ *
* @since 14.0.0
* @return string
*/
@@ -36,6 +41,9 @@ interface IMenuAction {
public function getPriority(): int;
/**
+ * Custom render function.
+ * The returned HTML will be wrapped within a listitem element (`<li>...</li>`).
+ *
* @since 14.0.0
* @return string
*/
diff --git a/lib/public/AppFramework/Http/Template/LinkMenuAction.php b/lib/public/AppFramework/Http/Template/LinkMenuAction.php
index 69e835f6b82..391802a1dce 100644
--- a/lib/public/AppFramework/Http/Template/LinkMenuAction.php
+++ b/lib/public/AppFramework/Http/Template/LinkMenuAction.php
@@ -1,12 +1,11 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCP\AppFramework\Http\Template;
-use OCP\Util;
-
/**
* Class LinkMenuAction
*
@@ -22,24 +21,6 @@ class LinkMenuAction extends SimpleMenuAction {
* @since 14.0.0
*/
public function __construct(string $label, string $icon, string $link) {
- parent::__construct('directLink-container', $label, $icon, $link);
- }
-
- /**
- * @return string
- * @since 14.0.0
- */
- public function render(): string {
- return '<li>' .
- '<a id="directLink-container">' .
- '<span class="icon ' . Util::sanitizeHTML($this->getIcon()) . '"></span>' .
- '<label for="directLink">' . Util::sanitizeHTML($this->getLabel()) . '</label>' .
- '</a>' .
- '</li>' .
- '<li>' .
- '<span class="menuitem">' .
- '<input id="directLink" type="text" readonly="" value="' . Util::sanitizeHTML($this->getLink()) . '">' .
- '</span>' .
- '</li>';
+ parent::__construct('directLink', $label, $icon, $link);
}
}
diff --git a/lib/public/AppFramework/Http/Template/PublicTemplateResponse.php b/lib/public/AppFramework/Http/Template/PublicTemplateResponse.php
index c12cf087755..4c156cdecea 100644
--- a/lib/public/AppFramework/Http/Template/PublicTemplateResponse.php
+++ b/lib/public/AppFramework/Http/Template/PublicTemplateResponse.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -8,18 +9,20 @@ namespace OCP\AppFramework\Http\Template;
use InvalidArgumentException;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\TemplateResponse;
+use OCP\IInitialStateService;
/**
* Class PublicTemplateResponse
*
* @since 14.0.0
* @template H of array<string, mixed>
- * @template S of int
- * @template-extends TemplateResponse<int, array<string, mixed>>
+ * @template S of Http::STATUS_*
+ * @template-extends TemplateResponse<Http::STATUS_*, array<string, mixed>>
*/
class PublicTemplateResponse extends TemplateResponse {
private $headerTitle = '';
private $headerDetails = '';
+ /** @var IMenuAction[] */
private $headerActions = [];
private $footerVisible = true;
@@ -33,9 +36,39 @@ class PublicTemplateResponse extends TemplateResponse {
* @param H $headers
* @since 14.0.0
*/
- public function __construct(string $appName, string $templateName, array $params = [], $status = Http::STATUS_OK, array $headers = []) {
+ public function __construct(
+ string $appName,
+ string $templateName,
+ array $params = [],
+ $status = Http::STATUS_OK,
+ array $headers = [],
+ ) {
parent::__construct($appName, $templateName, $params, 'public', $status, $headers);
- \OC_Util::addScript('core', 'public/publicpage');
+ \OCP\Util::addScript('core', 'public-page-menu');
+ \OCP\Util::addScript('core', 'public-page-user-menu');
+
+ $state = \OCP\Server::get(IInitialStateService::class);
+ $state->provideLazyInitialState('core', 'public-page-menu', function () {
+ $response = [];
+ foreach ($this->headerActions as $action) {
+ // First try in it is a custom action that provides rendered HTML
+ $rendered = $action->render();
+ if ($rendered === '') {
+ // If simple action, add the response data
+ if ($action instanceof SimpleMenuAction) {
+ $response[] = $action->getData();
+ }
+ } else {
+ // custom action so add the rendered output
+ $response[] = [
+ 'id' => $action->getId(),
+ 'label' => $action->getLabel(),
+ 'html' => $rendered,
+ ];
+ }
+ }
+ return $response;
+ });
}
/**
@@ -138,6 +171,6 @@ class PublicTemplateResponse extends TemplateResponse {
'template' => $this,
]);
$this->setParams($params);
- return parent::render();
+ return parent::render();
}
}
diff --git a/lib/public/AppFramework/Http/Template/SimpleMenuAction.php b/lib/public/AppFramework/Http/Template/SimpleMenuAction.php
index 2bb60975f26..03cb9b4c7ea 100644
--- a/lib/public/AppFramework/Http/Template/SimpleMenuAction.php
+++ b/lib/public/AppFramework/Http/Template/SimpleMenuAction.php
@@ -1,12 +1,11 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCP\AppFramework\Http\Template;
-use OCP\Util;
-
/**
* Class SimpleMenuAction
*
@@ -68,6 +67,8 @@ class SimpleMenuAction implements IMenuAction {
}
/**
+ * The icon CSS class to use.
+ *
* @return string
* @since 14.0.0
*/
@@ -92,14 +93,28 @@ class SimpleMenuAction implements IMenuAction {
}
/**
+ * Custom render function.
+ * The returned HTML must be wrapped within a listitem (`<li>...</li>`).
+ * * If an empty string is returned, the default design is used (based on the label and link specified).
* @return string
* @since 14.0.0
*/
public function render(): string {
- $detailContent = ($this->detail !== '') ? '&nbsp;<span class="download-size">(' . Util::sanitizeHTML($this->detail) . ')</span>' : '';
- return sprintf(
- '<li id="%s"><a href="%s"><span class="icon %s"></span>%s %s</a></li>',
- Util::sanitizeHTML($this->id), Util::sanitizeHTML($this->link), Util::sanitizeHTML($this->icon), Util::sanitizeHTML($this->label), $detailContent
- );
+ return '';
+ }
+
+ /**
+ * Return JSON data to let the frontend render the menu entry.
+ * @return array{id: string, label: string, href: string, icon: string, details: string|null}
+ * @since 31.0.0
+ */
+ public function getData(): array {
+ return [
+ 'id' => $this->id,
+ 'label' => $this->label,
+ 'href' => $this->link,
+ 'icon' => $this->icon,
+ 'details' => $this->detail,
+ ];
}
}
diff --git a/lib/public/AppFramework/Http/TemplateResponse.php b/lib/public/AppFramework/Http/TemplateResponse.php
index f9ac80cdc80..af37a1a2313 100644
--- a/lib/public/AppFramework/Http/TemplateResponse.php
+++ b/lib/public/AppFramework/Http/TemplateResponse.php
@@ -1,21 +1,27 @@
<?php
+declare(strict_types=1);
+
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
+
namespace OCP\AppFramework\Http;
use OCP\AppFramework\Http;
+use OCP\Server;
+use OCP\Template\ITemplateManager;
/**
* Response for a normal template
* @since 6.0.0
*
- * @template S of int
+ * @template S of Http::STATUS_*
* @template H of array<string, mixed>
- * @template-extends Response<int, array<string, mixed>>
+ * @template-extends Response<Http::STATUS_*, array<string, mixed>>
*/
class TemplateResponse extends Response {
/**
@@ -72,7 +78,7 @@ class TemplateResponse extends Response {
* @param string $appName the name of the app to load the template from
* @param string $templateName the name of the template
* @param array $params an array of parameters which should be passed to the
- * template
+ * template
* @param string $renderAs how the page should be rendered, defaults to user
* @param S $status
* @param H $headers
@@ -180,7 +186,7 @@ class TemplateResponse extends Response {
$renderAs = $this->renderAs;
}
- $template = new \OCP\Template($this->appName, $this->templateName, $renderAs);
+ $template = Server::get(ITemplateManager::class)->getTemplate($this->appName, $this->templateName, $renderAs);
foreach ($this->params as $key => $value) {
$template->assign($key, $value);
diff --git a/lib/public/AppFramework/Http/TextPlainResponse.php b/lib/public/AppFramework/Http/TextPlainResponse.php
index e7c728c37ab..9dfa2c5544d 100644
--- a/lib/public/AppFramework/Http/TextPlainResponse.php
+++ b/lib/public/AppFramework/Http/TextPlainResponse.php
@@ -12,9 +12,9 @@ use OCP\AppFramework\Http;
/**
* A renderer for text responses
* @since 22.0.0
- * @template S of int
+ * @template S of Http::STATUS_*
* @template H of array<string, mixed>
- * @template-extends Response<int, array<string, mixed>>
+ * @template-extends Response<Http::STATUS_*, array<string, mixed>>
*/
class TextPlainResponse extends Response {
/** @var string */
diff --git a/lib/public/AppFramework/Http/TooManyRequestsResponse.php b/lib/public/AppFramework/Http/TooManyRequestsResponse.php
index b7b0a98c9e1..f7084ec768d 100644
--- a/lib/public/AppFramework/Http/TooManyRequestsResponse.php
+++ b/lib/public/AppFramework/Http/TooManyRequestsResponse.php
@@ -8,14 +8,15 @@ declare(strict_types=1);
namespace OCP\AppFramework\Http;
use OCP\AppFramework\Http;
-use OCP\Template;
+use OCP\Server;
+use OCP\Template\ITemplateManager;
/**
* A generic 429 response showing an 404 error page as well to the end-user
* @since 19.0.0
- * @template S of int
+ * @template S of Http::STATUS_*
* @template H of array<string, mixed>
- * @template-extends Response<int, array<string, mixed>>
+ * @template-extends Response<Http::STATUS_*, array<string, mixed>>
*/
class TooManyRequestsResponse extends Response {
/**
@@ -34,7 +35,7 @@ class TooManyRequestsResponse extends Response {
* @since 19.0.0
*/
public function render() {
- $template = new Template('core', '429', 'blank');
+ $template = Server::get(ITemplateManager::class)->getTemplate('core', '429', TemplateResponse::RENDER_AS_BLANK);
return $template->fetchPage();
}
}
diff --git a/lib/public/AppFramework/Http/ZipResponse.php b/lib/public/AppFramework/Http/ZipResponse.php
index 3b9e251d332..a552eb1294f 100644
--- a/lib/public/AppFramework/Http/ZipResponse.php
+++ b/lib/public/AppFramework/Http/ZipResponse.php
@@ -15,9 +15,9 @@ use OCP\IRequest;
* Public library to send several files in one zip archive.
*
* @since 15.0.0
- * @template S of int
+ * @template S of Http::STATUS_*
* @template H of array<string, mixed>
- * @template-extends Response<int, array<string, mixed>>
+ * @template-extends Response<Http::STATUS_*, array<string, mixed>>
*/
class ZipResponse extends Response implements ICallbackResponse {
/** @var array{internalName: string, resource: resource, size: int, time: int}[] Files to be added to the zip response */
diff --git a/lib/public/AppFramework/OCS/OCSBadRequestException.php b/lib/public/AppFramework/OCS/OCSBadRequestException.php
index c229468fb0d..77b8ec6c86d 100644
--- a/lib/public/AppFramework/OCS/OCSBadRequestException.php
+++ b/lib/public/AppFramework/OCS/OCSBadRequestException.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/public/AppFramework/OCS/OCSException.php b/lib/public/AppFramework/OCS/OCSException.php
index 962bad830e7..02901992f8d 100644
--- a/lib/public/AppFramework/OCS/OCSException.php
+++ b/lib/public/AppFramework/OCS/OCSException.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/public/AppFramework/OCS/OCSForbiddenException.php b/lib/public/AppFramework/OCS/OCSForbiddenException.php
index 03b1db6104f..0d001377043 100644
--- a/lib/public/AppFramework/OCS/OCSForbiddenException.php
+++ b/lib/public/AppFramework/OCS/OCSForbiddenException.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/public/AppFramework/OCS/OCSNotFoundException.php b/lib/public/AppFramework/OCS/OCSNotFoundException.php
index 997b0c390f9..67cea9ed759 100644
--- a/lib/public/AppFramework/OCS/OCSNotFoundException.php
+++ b/lib/public/AppFramework/OCS/OCSNotFoundException.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/public/AppFramework/OCS/OCSPreconditionFailedException.php b/lib/public/AppFramework/OCS/OCSPreconditionFailedException.php
index 2e67263bcb9..4fc2820eaec 100644
--- a/lib/public/AppFramework/OCS/OCSPreconditionFailedException.php
+++ b/lib/public/AppFramework/OCS/OCSPreconditionFailedException.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/public/AppFramework/OCSController.php b/lib/public/AppFramework/OCSController.php
index b4bb62c41ed..7cde2a7e427 100644
--- a/lib/public/AppFramework/OCSController.php
+++ b/lib/public/AppFramework/OCSController.php
@@ -44,13 +44,13 @@ abstract class OCSController extends ApiController {
* @param string $appName the name of the app
* @param IRequest $request an instance of the request
* @param string $corsMethods comma separated string of HTTP verbs which
- * should be allowed for websites or webapps when calling your API, defaults to
- * 'PUT, POST, GET, DELETE, PATCH'
+ * should be allowed for websites or webapps when calling your API, defaults to
+ * 'PUT, POST, GET, DELETE, PATCH'
* @param string $corsAllowedHeaders comma separated string of HTTP headers
- * which should be allowed for websites or webapps when calling your API,
- * defaults to 'Authorization, Content-Type, Accept'
+ * which should be allowed for websites or webapps when calling your API,
+ * defaults to 'Authorization, Content-Type, Accept'
* @param int $corsMaxAge number in seconds how long a preflighted OPTIONS
- * request should be cached, defaults to 1728000 seconds
+ * request should be cached, defaults to 1728000 seconds
* @since 8.1.0
*/
public function __construct($appName,
@@ -81,7 +81,7 @@ abstract class OCSController extends ApiController {
* Since the OCS endpoints default to XML we need to find out the format
* again
* @param mixed $response the value that was returned from a controller and
- * is not a Response instance
+ * is not a Response instance
* @param string $format the format for which a formatter has been registered
* @throws \DomainException if format does not match a registered formatter
* @return Response
diff --git a/lib/public/AppFramework/PublicShareController.php b/lib/public/AppFramework/PublicShareController.php
index 458606455d1..999b3827565 100644
--- a/lib/public/AppFramework/PublicShareController.php
+++ b/lib/public/AppFramework/PublicShareController.php
@@ -98,8 +98,8 @@ abstract class PublicShareController extends Controller {
}
// If we are authenticated properly
- if ($this->session->get('public_link_authenticated_token') === $this->getToken() &&
- $this->session->get('public_link_authenticated_password_hash') === $this->getPasswordHash()) {
+ if ($this->session->get('public_link_authenticated_token') === $this->getToken()
+ && $this->session->get('public_link_authenticated_password_hash') === $this->getPasswordHash()) {
return true;
}
diff --git a/lib/public/AppFramework/Services/IInitialState.php b/lib/public/AppFramework/Services/IInitialState.php
index 24698108d68..ac58bcad3cc 100644
--- a/lib/public/AppFramework/Services/IInitialState.php
+++ b/lib/public/AppFramework/Services/IInitialState.php
@@ -37,7 +37,7 @@ interface IInitialState {
*
* @param string $key
* @param Closure $closure returns a primitive or an object that implements JsonSerializable
- * @psalm-param Closure():bool|Closure():int|Closure():float|Closure():string|Closure():\JsonSerializable $closure
+ * @psalm-param Closure():bool|Closure():int|Closure():float|Closure():string|Closure():array|Closure():\JsonSerializable $closure
*/
public function provideLazyInitialState(string $key, Closure $closure): void;
}
diff --git a/lib/public/AppFramework/Utility/IControllerMethodReflector.php b/lib/public/AppFramework/Utility/IControllerMethodReflector.php
index 577191cac40..95d7fbebb56 100644
--- a/lib/public/AppFramework/Utility/IControllerMethodReflector.php
+++ b/lib/public/AppFramework/Utility/IControllerMethodReflector.php
@@ -31,9 +31,9 @@ interface IControllerMethodReflector {
* Inspects the PHPDoc parameters for types
*
* @param string $parameter the parameter whose type comments should be
- * parsed
+ * parsed
* @return string|null type in the type parameters (@param int $something)
- * would return int or null if not existing
+ * would return int or null if not existing
* @since 8.0.0
* @deprecated 22.0.0 this method is only used internally
*/