Merge pull request #42801 from nextcloud/feat/appframework/route-attribute

pull/43730/head
Kate 3 months ago committed by GitHub
commit fa8b4f32d2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -177,6 +177,7 @@
"name": "expirationTime",
"in": "query",
"description": "Duration until the link expires",
"required": true,
"schema": {
"type": "integer",
"format": "int64"

@ -1,43 +0,0 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2020, Georg Ehrke
*
* @author Georg Ehrke <oc.list@georgehrke.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/>.
*
*/
return [
'ocs' => [
// Routes for querying statuses
['name' => 'Statuses#findAll', 'url' => '/api/v1/statuses', 'verb' => 'GET'],
['name' => 'Statuses#find', 'url' => '/api/v1/statuses/{userId}', 'verb' => 'GET'],
// Routes for manipulating your own status
['name' => 'UserStatus#getStatus', 'url' => '/api/v1/user_status', 'verb' => 'GET'],
['name' => 'UserStatus#setStatus', 'url' => '/api/v1/user_status/status', 'verb' => 'PUT'],
['name' => 'UserStatus#setPredefinedMessage', 'url' => '/api/v1/user_status/message/predefined', 'verb' => 'PUT'],
['name' => 'UserStatus#setCustomMessage', 'url' => '/api/v1/user_status/message/custom', 'verb' => 'PUT'],
['name' => 'UserStatus#clearMessage', 'url' => '/api/v1/user_status/message', 'verb' => 'DELETE'],
['name' => 'UserStatus#revertStatus', 'url' => '/api/v1/user_status/revert/{messageId}', 'verb' => 'DELETE'],
// Routes for listing default routes
['name' => 'PredefinedStatus#findAll', 'url' => '/api/v1/predefined_statuses/', 'verb' => 'GET'],
// Route for doing heartbeats
['name' => 'Heartbeat#heartbeat', 'url' => '/api/v1/heartbeat', 'verb' => 'PUT'],
],
];

@ -30,6 +30,7 @@ use OCA\UserStatus\Db\UserStatus;
use OCA\UserStatus\ResponseDefinitions;
use OCA\UserStatus\Service\StatusService;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\ApiRoute;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\AppFramework\Utility\ITimeFactory;
@ -81,6 +82,7 @@ class HeartbeatController extends OCSController {
* 204: User has no status to keep alive
* 400: Invalid status to update
*/
#[ApiRoute(verb: 'PUT', url: '/api/v1/heartbeat')]
public function heartbeat(string $status): DataResponse {
if (!\in_array($status, [IUserStatus::ONLINE, IUserStatus::AWAY], true)) {
return new DataResponse([], Http::STATUS_BAD_REQUEST);

@ -29,6 +29,7 @@ namespace OCA\UserStatus\Controller;
use OCA\UserStatus\ResponseDefinitions;
use OCA\UserStatus\Service\PredefinedStatusService;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\ApiRoute;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\IRequest;
@ -66,6 +67,7 @@ class PredefinedStatusController extends OCSController {
*
* 200: Predefined statuses returned
*/
#[ApiRoute(verb: 'GET', url: '/api/v1/predefined_statuses/')]
public function findAll():DataResponse {
// Filtering out the invisible one, that should only be set by API
return new DataResponse(array_filter($this->predefinedStatusService->getDefaultStatuses(), function (array $status) {

@ -32,6 +32,7 @@ use OCA\UserStatus\ResponseDefinitions;
use OCA\UserStatus\Service\StatusService;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\ApiRoute;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCS\OCSNotFoundException;
use OCP\AppFramework\OCSController;
@ -72,6 +73,7 @@ class StatusesController extends OCSController {
*
* 200: Statuses returned
*/
#[ApiRoute(verb: 'GET', url: '/api/v1/statuses')]
public function findAll(?int $limit = null, ?int $offset = null): DataResponse {
$allStatuses = $this->service->findAll($limit, $offset);
@ -91,6 +93,7 @@ class StatusesController extends OCSController {
*
* 200: Status returned
*/
#[ApiRoute(verb: 'GET', url: '/api/v1/statuses/{userId}')]
public function find(string $userId): DataResponse {
try {
$userStatus = $this->service->findByUserId($userId);

@ -39,6 +39,7 @@ use OCA\UserStatus\ResponseDefinitions;
use OCA\UserStatus\Service\StatusService;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\ApiRoute;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCS\OCSBadRequestException;
use OCP\AppFramework\OCS\OCSNotFoundException;
@ -72,6 +73,7 @@ class UserStatusController extends OCSController {
*
* 200: The status was found successfully
*/
#[ApiRoute(verb: 'GET', url: '/api/v1/user_status')]
public function getStatus(): DataResponse {
try {
$this->calendarStatusService->processCalendarStatus($this->userId);
@ -94,6 +96,7 @@ class UserStatusController extends OCSController {
*
* 200: The status was updated successfully
*/
#[ApiRoute(verb: 'PUT', url: '/api/v1/user_status/status')]
public function setStatus(string $statusType): DataResponse {
try {
$status = $this->service->setStatus($this->userId, $statusType, null, true);
@ -118,6 +121,7 @@ class UserStatusController extends OCSController {
*
* 200: The message was updated successfully
*/
#[ApiRoute(verb: 'PUT', url: '/api/v1/user_status/message/predefined')]
public function setPredefinedMessage(string $messageId,
?int $clearAt): DataResponse {
try {
@ -146,6 +150,7 @@ class UserStatusController extends OCSController {
*
* 200: The message was updated successfully
*/
#[ApiRoute(verb: 'PUT', url: '/api/v1/user_status/message/custom')]
public function setCustomMessage(?string $statusIcon,
?string $message,
?int $clearAt): DataResponse {
@ -179,6 +184,7 @@ class UserStatusController extends OCSController {
*
* 200: Message cleared successfully
*/
#[ApiRoute(verb: 'DELETE', url: '/api/v1/user_status/message')]
public function clearMessage(): DataResponse {
$this->service->clearMessage($this->userId);
return new DataResponse([]);
@ -195,6 +201,7 @@ class UserStatusController extends OCSController {
*
* 200: Status reverted
*/
#[ApiRoute(verb: 'DELETE', url: '/api/v1/user_status/revert/{messageId}')]
public function revertStatus(string $messageId): DataResponse {
$backupStatus = $this->service->revertUserStatus($this->userId, $messageId, true);
if ($backupStatus) {

@ -206,6 +206,224 @@
}
},
"paths": {
"/ocs/v2.php/apps/user_status/api/v1/heartbeat": {
"put": {
"operationId": "heartbeat-heartbeat",
"summary": "Keep the status alive",
"tags": [
"heartbeat"
],
"security": [
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"parameters": [
{
"name": "status",
"in": "query",
"description": "Only online, away",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Status successfully updated",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"$ref": "#/components/schemas/Private"
}
}
}
}
}
}
}
},
"400": {
"description": "Invalid status to update",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
},
"500": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
},
"204": {
"description": "User has no status to keep alive",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
}
}
}
},
"/ocs/v2.php/apps/user_status/api/v1/predefined_statuses": {
"get": {
"operationId": "predefined_status-find-all",
"summary": "Get all predefined messages",
"tags": [
"predefined_status"
],
"security": [
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"parameters": [
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Predefined statuses returned",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Predefined"
}
}
}
}
}
}
}
}
}
}
}
},
"/ocs/v2.php/apps/user_status/api/v1/statuses": {
"get": {
"operationId": "statuses-find-all",
@ -846,224 +1064,6 @@
}
}
}
},
"/ocs/v2.php/apps/user_status/api/v1/predefined_statuses": {
"get": {
"operationId": "predefined_status-find-all",
"summary": "Get all predefined messages",
"tags": [
"predefined_status"
],
"security": [
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"parameters": [
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Predefined statuses returned",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Predefined"
}
}
}
}
}
}
}
}
}
}
}
},
"/ocs/v2.php/apps/user_status/api/v1/heartbeat": {
"put": {
"operationId": "heartbeat-heartbeat",
"summary": "Keep the status alive",
"tags": [
"heartbeat"
],
"security": [
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"parameters": [
{
"name": "status",
"in": "query",
"description": "Only online, away",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Status successfully updated",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"$ref": "#/components/schemas/Private"
}
}
}
}
}
}
}
},
"400": {
"description": "Invalid status to update",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
},
"500": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
},
"204": {
"description": "User has no status to keep alive",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
}
}
}
}
},
"tags": []

@ -33,6 +33,7 @@ use OC\Authentication\Token\IProvider;
use OC\Authentication\Token\IToken;
use OC\User\Session;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\ApiRoute;
use OCP\AppFramework\Http\Attribute\UseSession;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCS\OCSForbiddenException;
@ -74,6 +75,7 @@ class AppPasswordController extends \OCP\AppFramework\OCSController {
*
* 200: App password returned
*/
#[ApiRoute(verb: 'GET', url: '/getapppassword', root: '/core')]
public function getAppPassword(): DataResponse {
// We do not allow the creation of new tokens if this is an app password
if ($this->session->exists('app_password')) {
@ -125,6 +127,7 @@ class AppPasswordController extends \OCP\AppFramework\OCSController {
*
* 200: App password deleted successfully
*/
#[ApiRoute(verb: 'DELETE', url: '/apppassword', root: '/core')]
public function deleteAppPassword(): DataResponse {
if (!$this->session->exists('app_password')) {
throw new OCSForbiddenException('no app password in use');
@ -152,6 +155,7 @@ class AppPasswordController extends \OCP\AppFramework\OCSController {
*
* 200: App password returned
*/
#[ApiRoute(verb: 'POST', url: '/apppassword/rotate', root: '/core')]
public function rotateAppPassword(): DataResponse {
if (!$this->session->exists('app_password')) {
throw new OCSForbiddenException('no app password in use');
@ -187,6 +191,7 @@ class AppPasswordController extends \OCP\AppFramework\OCSController {
* 403: Password confirmation failed
*/
#[UseSession]
#[ApiRoute(verb: 'PUT', url: '/apppassword/confirm', root: '/core')]
public function confirmUserPassword(string $password): DataResponse {
$loginName = $this->userSession->getLoginName();
$loginResult = $this->userManager->checkPassword($loginName, $password);

@ -32,6 +32,7 @@ namespace OC\Core\Controller;
use OCA\Core\ResponseDefinitions;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\ApiRoute;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\Collaboration\AutoComplete\AutoCompleteEvent;
@ -72,6 +73,7 @@ class AutoCompleteController extends OCSController {
*
* 200: Autocomplete results returned
*/
#[ApiRoute(verb: 'GET', url: '/autocomplete/get', root: '/core')]
public function get(string $search, ?string $itemType, ?string $itemId, ?string $sorter = null, array $shareTypes = [IShare::TYPE_USER], int $limit = 10): DataResponse {
// if enumeration/user listings are disabled, we'll receive an empty
// result from search() thus nothing else to do here.

@ -34,6 +34,7 @@ namespace OC\Core\Controller;
use OC\AppFramework\Utility\TimeFactory;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
use OCP\AppFramework\Http\DataDisplayResponse;
use OCP\AppFramework\Http\FileDisplayResponse;
use OCP\AppFramework\Http\JSONResponse;
@ -82,6 +83,7 @@ class AvatarController extends Controller {
* 200: Avatar returned
* 404: Avatar not found
*/
#[FrontpageRoute(verb: 'GET', url: '/avatar/{userId}/{size}/dark')]
public function getAvatarDark(string $userId, int $size) {
if ($size <= 64) {
if ($size !== 64) {
@ -128,6 +130,7 @@ class AvatarController extends Controller {
* 200: Avatar returned
* 404: Avatar not found
*/
#[FrontpageRoute(verb: 'GET', url: '/avatar/{userId}/{size}')]
public function getAvatar(string $userId, int $size) {
if ($size <= 64) {
if ($size !== 64) {
@ -161,6 +164,7 @@ class AvatarController extends Controller {
/**
* @NoAdminRequired
*/
#[FrontpageRoute(verb: 'POST', url: '/avatar/')]
public function postAvatar(?string $path = null): JSONResponse {
$files = $this->request->getUploadedFile('files');
@ -283,6 +287,7 @@ class AvatarController extends Controller {
/**
* @NoAdminRequired
*/
#[FrontpageRoute(verb: 'DELETE', url: '/avatar/')]
public function deleteAvatar(): JSONResponse {
try {
$avatar = $this->avatarManager->getAvatar($this->userId);
@ -299,6 +304,7 @@ class AvatarController extends Controller {
*
* @return JSONResponse|DataDisplayResponse
*/
#[FrontpageRoute(verb: 'GET', url: '/avatar/tmp')]
public function getTmpAvatar() {
$tmpAvatar = $this->cache->get('tmpAvatar');
if (is_null($tmpAvatar)) {
@ -325,6 +331,7 @@ class AvatarController extends Controller {
/**
* @NoAdminRequired
*/
#[FrontpageRoute(verb: 'POST', url: '/avatar/cropped')]
public function postCroppedAvatar(?array $crop = null): JSONResponse {
if (is_null($crop)) {
return new JSONResponse(['data' => ['message' => $this->l10n->t("No crop data provided")]],

@ -30,6 +30,7 @@ namespace OC\Core\Controller;
use OC\Security\CSRF\CsrfTokenManager;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
@ -49,6 +50,7 @@ class CSRFTokenController extends Controller {
* @NoCSRFRequired
* @PublicPage
*/
#[FrontpageRoute(verb: 'GET', url: '/csrftoken')]
public function index(): JSONResponse {
if (!$this->request->passesStrictCookieCheck()) {
return new JSONResponse([], Http::STATUS_FORBIDDEN);

@ -41,6 +41,7 @@ use OCA\OAuth2\Db\AccessTokenMapper;
use OCA\OAuth2\Db\ClientMapper;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\Attribute\UseSession;
use OCP\AppFramework\Http\Response;
@ -113,6 +114,7 @@ class ClientFlowLoginController extends Controller {
* @NoCSRFRequired
*/
#[UseSession]
#[FrontpageRoute(verb: 'GET', url: '/login/flow')]
public function showAuthPickerPage(string $clientIdentifier = '', string $user = '', int $direct = 0): StandaloneTemplateResponse {
$clientName = $this->getClientName();
$client = null;
@ -180,6 +182,7 @@ class ClientFlowLoginController extends Controller {
* @NoSameSiteCookieRequired
*/
#[UseSession]
#[FrontpageRoute(verb: 'GET', url: '/login/flow/grant')]
public function grantPage(string $stateToken = '',
string $clientIdentifier = '',
int $direct = 0): StandaloneTemplateResponse {
@ -232,6 +235,7 @@ class ClientFlowLoginController extends Controller {
* @return Http\RedirectResponse|Response
*/
#[UseSession]
#[FrontpageRoute(verb: 'POST', url: '/login/flow')]
public function generateAppPassword(string $stateToken,
string $clientIdentifier = '') {
if (!$this->isValidToken($stateToken)) {
@ -323,6 +327,7 @@ class ClientFlowLoginController extends Controller {
/**
* @PublicPage
*/
#[FrontpageRoute(verb: 'POST', url: '/login/flow/apptoken')]
public function apptokenRedirect(string $stateToken, string $user, string $password): Response {
if (!$this->isValidToken($stateToken)) {
return $this->stateTokenForbiddenResponse();

@ -33,6 +33,7 @@ use OC\Core\Service\LoginFlowV2Service;
use OCA\Core\ResponseDefinitions;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\Attribute\UseSession;
use OCP\AppFramework\Http\JSONResponse;
@ -84,6 +85,7 @@ class ClientFlowLoginV2Controller extends Controller {
* 200: Login flow credentials returned
* 404: Login flow not found or completed
*/
#[FrontpageRoute(verb: 'POST', url: '/login/v2/poll')]
public function poll(string $token): JSONResponse {
try {
$creds = $this->loginFlowV2Service->poll($token);
@ -100,6 +102,7 @@ class ClientFlowLoginV2Controller extends Controller {
*/
#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
#[UseSession]
#[FrontpageRoute(verb: 'GET', url: '/login/v2/flow/{token}')]
public function landing(string $token, $user = ''): Response {
if (!$this->loginFlowV2Service->startLoginFlow($token)) {
return $this->loginTokenForbiddenResponse();
@ -118,6 +121,7 @@ class ClientFlowLoginV2Controller extends Controller {
*/
#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
#[UseSession]
#[FrontpageRoute(verb: 'GET', url: '/login/v2/flow')]
public function showAuthPickerPage($user = ''): StandaloneTemplateResponse {
try {
$flow = $this->getFlowByLoginToken();
@ -152,6 +156,7 @@ class ClientFlowLoginV2Controller extends Controller {
*/
#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
#[UseSession]
#[FrontpageRoute(verb: 'GET', url: '/login/v2/grant')]
public function grantPage(?string $stateToken): StandaloneTemplateResponse {
if ($stateToken === null) {
return $this->stateTokenMissingResponse();
@ -187,6 +192,7 @@ class ClientFlowLoginV2Controller extends Controller {
/**
* @PublicPage
*/
#[FrontpageRoute(verb: 'POST', url: '/login/v2/apptoken')]
public function apptokenRedirect(?string $stateToken, string $user, string $password) {
if ($stateToken === null) {
return $this->stateTokenMissingResponse();
@ -234,6 +240,7 @@ class ClientFlowLoginV2Controller extends Controller {
* @NoAdminRequired
*/
#[UseSession]
#[FrontpageRoute(verb: 'POST', url: '/login/v2/grant')]
public function generateAppPassword(?string $stateToken): Response {
if ($stateToken === null) {
return $this->stateTokenMissingResponse();
@ -291,6 +298,7 @@ class ClientFlowLoginV2Controller extends Controller {
*
* 200: Login flow init returned
*/
#[FrontpageRoute(verb: 'POST', url: '/login/v2')]
public function init(): JSONResponse {
// Get client user agent
$userAgent = $this->request->getHeader('USER_AGENT');

@ -32,6 +32,7 @@ namespace OC\Core\Controller;
use Exception;
use OCA\Core\ResponseDefinitions;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\ApiRoute;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\Collaboration\Resources\CollectionException;
@ -84,6 +85,7 @@ class CollaborationResourcesController extends OCSController {
* 200: Collection returned
* 404: Collection not found
*/
#[ApiRoute(verb: 'GET', url: '/resources/collections/{collectionId}', root: '/collaboration')]
public function listCollection(int $collectionId): DataResponse {
try {
$collection = $this->getCollection($collectionId);
@ -105,6 +107,7 @@ class CollaborationResourcesController extends OCSController {
* 200: Collections returned
* 404: Collection not found
*/
#[ApiRoute(verb: 'GET', url: '/resources/collections/search/{filter}', root: '/collaboration')]
public function searchCollections(string $filter): DataResponse {
try {
$collections = $this->manager->searchCollections($this->userSession->getUser(), $filter);
@ -128,6 +131,7 @@ class CollaborationResourcesController extends OCSController {
* 200: Collection returned
* 404: Collection not found or resource inaccessible
*/
#[ApiRoute(verb: 'POST', url: '/resources/collections/{collectionId}', root: '/collaboration')]
public function addResource(int $collectionId, string $resourceType, string $resourceId): DataResponse {
try {
$collection = $this->getCollection($collectionId);
@ -162,6 +166,7 @@ class CollaborationResourcesController extends OCSController {
* 200: Collection returned
* 404: Collection or resource not found
*/
#[ApiRoute(verb: 'DELETE', url: '/resources/collections/{collectionId}', root: '/collaboration')]
public function removeResource(int $collectionId, string $resourceType, string $resourceId): DataResponse {
try {
$collection = $this->getCollection($collectionId);
@ -192,6 +197,7 @@ class CollaborationResourcesController extends OCSController {
* 200: Collections returned
* 404: Resource not accessible
*/
#[ApiRoute(verb: 'GET', url: '/resources/{resourceType}/{resourceId}', root: '/collaboration')]
public function getCollectionsByResource(string $resourceType, string $resourceId): DataResponse {
try {
$resource = $this->manager->getResourceForUser($resourceType, $resourceId, $this->userSession->getUser());
@ -220,6 +226,7 @@ class CollaborationResourcesController extends OCSController {
* 400: Creating collection is not possible
* 404: Resource inaccessible
*/
#[ApiRoute(verb: 'POST', url: '/resources/{baseResourceType}/{baseResourceId}', root: '/collaboration')]
public function createCollectionOnResource(string $baseResourceType, string $baseResourceId, string $name): DataResponse {
if (!isset($name[0]) || isset($name[64])) {
return new DataResponse([], Http::STATUS_BAD_REQUEST);
@ -253,6 +260,7 @@ class CollaborationResourcesController extends OCSController {
* 200: Collection returned
* 404: Collection not found
*/
#[ApiRoute(verb: 'PUT', url: '/resources/collections/{collectionId}', root: '/collaboration')]
public function renameCollection(int $collectionId, string $collectionName): DataResponse {
try {
$collection = $this->getCollection($collectionId);

@ -28,6 +28,7 @@ use Exception;
use OC\Contacts\ContactsMenu\Manager;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
use OCP\IUserSession;
@ -47,6 +48,7 @@ class ContactsMenuController extends Controller {
* @return \JsonSerializable[]
* @throws Exception
*/
#[FrontpageRoute(verb: 'POST', url: '/contactsmenu/contacts')]
public function index(?string $filter = null): array {
return $this->manager->getEntries($this->userSession->getUser(), $filter);
}
@ -57,6 +59,7 @@ class ContactsMenuController extends Controller {
* @return JSONResponse|\JsonSerializable
* @throws Exception
*/
#[FrontpageRoute(verb: 'POST', url: '/contactsmenu/findOne')]
public function findOne(int $shareType, string $shareWith) {
$contact = $this->manager->findOne($this->userSession->getUser(), $shareType, $shareWith);

@ -34,6 +34,7 @@ namespace OC\Core\Controller;
use OC\Files\AppData\Factory;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\FileDisplayResponse;
use OCP\AppFramework\Http\NotFoundResponse;
@ -69,6 +70,7 @@ class CssController extends Controller {
* @param string $appName css folder name
* @return FileDisplayResponse|NotFoundResponse
*/
#[FrontpageRoute(verb: 'GET', url: '/css/{appName}/{fileName}')]
public function getCss(string $fileName, string $appName): Response {
try {
$folder = $this->appData->getFolder($appName);

@ -28,6 +28,7 @@ declare(strict_types=1);
namespace OC\Core\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\TemplateResponse;
@ -37,6 +38,7 @@ class ErrorController extends \OCP\AppFramework\Controller {
* @PublicPage
* @NoCSRFRequired
*/
#[FrontpageRoute(verb: 'GET', url: 'error/403')]
public function error403(): TemplateResponse {
$response = new TemplateResponse(
'core',
@ -52,6 +54,7 @@ class ErrorController extends \OCP\AppFramework\Controller {
* @PublicPage
* @NoCSRFRequired
*/
#[FrontpageRoute(verb: 'GET', url: 'error/404')]
public function error404(): TemplateResponse {
$response = new TemplateResponse(
'core',

@ -25,6 +25,7 @@ namespace OC\Core\Controller;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
use OCP\AppFramework\Http\FileDisplayResponse;
use OCP\AppFramework\Http\Response;
use OCP\IAvatarManager;
@ -61,6 +62,7 @@ class GuestAvatarController extends Controller {
* 200: Custom avatar returned
* 201: Avatar returned
*/
#[FrontpageRoute(verb: 'GET', url: '/avatar/guest/{guestName}/{size}')]
public function getAvatar(string $guestName, string $size, ?bool $darkTheme = false) {
$size = (int) $size;
$darkTheme = $darkTheme ?? false;
@ -113,6 +115,7 @@ class GuestAvatarController extends Controller {
* 200: Custom avatar returned
* 201: Avatar returned
*/
#[FrontpageRoute(verb: 'GET', url: '/avatar/guest/{guestName}/{size}/dark')]
public function getAvatarDark(string $guestName, string $size) {
return $this->getAvatar($guestName, $size, true);
}

@ -28,6 +28,7 @@ namespace OC\Core\Controller;
use OC\Contacts\ContactsMenu\Manager;
use OCA\Core\ResponseDefinitions;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\ApiRoute;
use OCP\AppFramework\Http\DataResponse;
use OCP\IRequest;
use OCP\IUserSession;
@ -56,6 +57,7 @@ class HoverCardController extends \OCP\AppFramework\OCSController {
* 200: Account details returned
* 404: Account not found
*/
#[ApiRoute(verb: 'GET', url: '/v1/{userId}', root: '/hovercard')]
public function getUser(string $userId): DataResponse {
$contact = $this->manager->findOne($this->userSession->getUser(), IShare::TYPE_USER, $userId);

@ -34,6 +34,7 @@ namespace OC\Core\Controller;
use OC\Files\AppData\Factory;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\FileDisplayResponse;
use OCP\AppFramework\Http\NotFoundResponse;
@ -69,6 +70,7 @@ class JsController extends Controller {
* @param string $appName js folder name
* @return FileDisplayResponse|NotFoundResponse
*/
#[FrontpageRoute(verb: 'GET', url: '/js/{appName}/{fileName}')]
public function getJs(string $fileName, string $appName): Response {
try {
$folder = $this->appData->getFolder($appName);

@ -43,6 +43,7 @@ use OC\User\Session;
use OC_App;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\Attribute\UseSession;
@ -91,6 +92,7 @@ class LoginController extends Controller {
* @return RedirectResponse
*/
#[UseSession]
#[FrontpageRoute(verb: 'GET', url: '/logout')]
public function logout() {
$loginToken = $this->request->getCookie('nc_token');
if (!is_null($loginToken)) {
@ -127,6 +129,7 @@ class LoginController extends Controller {
*/
#[UseSession]
#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
#[FrontpageRoute(verb: 'GET', url: '/login')]
public function showLoginForm(string $user = null, string $redirect_url = null): Http\Response {
if ($this->userSession->isLoggedIn()) {
return new RedirectResponse($this->urlGenerator->linkToDefaultPageUrl());
@ -276,6 +279,7 @@ class LoginController extends Controller {
*/
#[UseSession]
#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
#[FrontpageRoute(verb: 'POST', url: '/login')]
public function tryLogin(Chain $loginChain,
string $user = '',
string $password = '',
@ -370,6 +374,7 @@ class LoginController extends Controller {
*/
#[UseSession]
#[NoCSRFRequired]
#[FrontpageRoute(verb: 'POST', url: '/login/confirm')]
public function confirmPassword(string $password): DataResponse {
$loginName = $this->userSession->getLoginName();
$loginResult = $this->userManager->checkPassword($loginName, $password);

@ -44,6 +44,7 @@ use OC\Core\Exception\ResetPasswordException;
use OC\Security\RateLimiting\Exception\RateLimitExceededException;
use OC\Security\RateLimiting\Limiter;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Http\TemplateResponse;
@ -108,6 +109,7 @@ class LostController extends Controller {
* @BruteForceProtection(action=passwordResetEmail)
* @AnonRateThrottle(limit=10, period=300)
*/
#[FrontpageRoute(verb: 'GET', url: '/lostpassword/reset/form/{token}/{userId}')]
public function resetform(string $token, string $userId): TemplateResponse {
try {
$this->checkPasswordResetToken($token, $userId);
@ -172,6 +174,7 @@ class LostController extends Controller {
* @BruteForceProtection(action=passwordResetEmail)
* @AnonRateThrottle(limit=10, period=300)
*/
#[FrontpageRoute(verb: 'POST', url: '/lostpassword/email')]
public function email(string $user): JSONResponse {
if ($this->config->getSystemValue('lost_password_link', '') !== '') {
return new JSONResponse($this->error($this->l10n->t('Password reset is disabled')));
@ -205,6 +208,7 @@ class LostController extends Controller {
* @BruteForceProtection(action=passwordResetEmail)
* @AnonRateThrottle(limit=10, period=300)
*/
#[FrontpageRoute(verb: 'POST', url: '/lostpassword/set/{token}/{userId}')]
public function setPassword(string $token, string $userId, string $password, bool $proceed): JSONResponse {
if ($this->encryptionManager->isEnabled() && !$proceed) {
$encryptionModules = $this->encryptionManager->getEncryptionModules();

@ -25,6 +25,7 @@ namespace OC\Core\Controller;
use OCA\Core\ResponseDefinitions;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\ApiRoute;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\INavigationManager;
@ -56,6 +57,7 @@ class NavigationController extends OCSController {
* 200: Apps navigation returned
* 304: No apps navigation changed
*/
#[ApiRoute(verb: 'GET', url: '/navigation/apps', root: '/core')]
public function getAppsNavigation(bool $absolute = false): DataResponse {
$navigation = $this->navigationManager->getAll();
if ($absolute) {
@ -83,6 +85,7 @@ class NavigationController extends OCSController {
* 200: Apps navigation returned
* 304: No apps navigation changed
*/
#[ApiRoute(verb: 'GET', url: '/navigation/settings', root: '/core')]
public function getSettingsNavigation(bool $absolute = false): DataResponse {
$navigation = $this->navigationManager->getAll('settings');
if ($absolute) {

@ -34,6 +34,7 @@ use OC\Template\JSConfigHelper;
use OCP\App\IAppManager;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\DataDisplayResponse;
use OCP\Defaults;
@ -87,6 +88,7 @@ class OCJSController extends Controller {
* @NoTwoFactorRequired
* @PublicPage
*/
#[FrontpageRoute(verb: 'GET', url: '/core/js/oc.js')]
public function getConfig(): DataDisplayResponse {
$data = $this->helper->getConfig();

@ -29,6 +29,7 @@ namespace OC\Core\Controller;
use Exception;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
use OCP\AppFramework\Http\DataResponse;
use OCP\Capabilities\ICapability;
use OCP\IConfig;
@ -64,6 +65,7 @@ class OCMController extends Controller {
* 200: OCM Provider details returned
* 500: OCM not supported
*/
#[FrontpageRoute(verb: 'GET', url: '/ocm-provider/')]
public function discovery(): DataResponse {
try {
$cap = Server::get(

@ -31,6 +31,7 @@ namespace OC\Core\Controller;
use OC\CapabilitiesManager;
use OC\Security\IdentityProof\Manager;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\ApiRoute;
use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\DataResponse;
use OCP\IRequest;
@ -53,6 +54,7 @@ class OCSController extends \OCP\AppFramework\OCSController {
* @PublicPage
*/
#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
#[ApiRoute(verb: 'GET', url: '/config', root: '')]
public function getConfig(): DataResponse {
$data = [
'version' => '1.7',
@ -74,6 +76,7 @@ class OCSController extends \OCP\AppFramework\OCSController {
*
* 200: Capabilities returned
*/
#[ApiRoute(verb: 'GET', url: '/capabilities', root: '/cloud')]
public function getCapabilities(): DataResponse {
$result = [];
[$major, $minor, $micro] = \OCP\Util::getVersion();
@ -102,6 +105,7 @@ class OCSController extends \OCP\AppFramework\OCSController {
* @BruteForceProtection(action=login)
*/
#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
#[ApiRoute(verb: 'POST', url: '/check', root: '/person')]
public function personCheck(string $login = '', string $password = ''): DataResponse {
if ($login !== '' && $password !== '') {
if ($this->userManager->checkPassword($login, $password)) {
@ -123,6 +127,7 @@ class OCSController extends \OCP\AppFramework\OCSController {
* @PublicPage
*/
#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
#[ApiRoute(verb: 'GET', url: '/key/{cloudId}', root: '/identityproof')]
public function getIdentityProof(string $cloudId): DataResponse {
$userObject = $this->userManager->get($cloudId);

@ -30,6 +30,7 @@ namespace OC\Core\Controller;
use OCA\Files_Sharing\SharedStorage;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\FileDisplayResponse;
use OCP\AppFramework\Http\RedirectResponse;
@ -74,6 +75,7 @@ class PreviewController extends Controller {
* 403: Getting preview is not allowed
* 404: Preview not found
*/
#[FrontpageRoute(verb: 'GET', url: '/core/preview.png')]
public function getPreview(
string $file = '',
int $x = 32,
@ -117,6 +119,7 @@ class PreviewController extends Controller {
* 403: Getting preview is not allowed
* 404: Preview not found
*/
#[FrontpageRoute(verb: 'GET', url: '/core/preview')]
public function getPreviewByFileId(
int $fileId = -1,
int $x = 32,

@ -30,6 +30,7 @@ namespace OC\Core\Controller;
use OC\Core\Db\ProfileConfigMapper;
use OC\Profile\ProfileManager;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\ApiRoute;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCS\OCSBadRequestException;
use OCP\AppFramework\OCS\OCSForbiddenException;
@ -68,6 +69,7 @@ class ProfileApiController extends OCSController {
*
* 200: Visibility updated successfully
*/
#[ApiRoute(verb: 'PUT', url: '/{targetUserId}', root: '/profile')]
public function setVisibility(string $targetUserId, string $paramId, string $visibility): DataResponse {
$requestingUser = $this->userSession->getUser();
$targetUser = $this->userManager->get($targetUserId);

@ -29,6 +29,7 @@ namespace OC\Core\Controller;
use OC\Profile\ProfileManager;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Services\IInitialState;
@ -65,6 +66,7 @@ class ProfilePageController extends Controller {
* @NoAdminRequired
* @NoSubAdminRequired
*/
#[FrontpageRoute(verb: 'GET', url: '/u/{targetUserId}')]
public function index(string $targetUserId): TemplateResponse {
$profileNotFoundTemplate = new TemplateResponse(
'core',

@ -27,6 +27,7 @@ declare(strict_types=1);
namespace OC\Core\Controller;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\Response;
use OCP\AppFramework\Http\StandaloneTemplateResponse;
@ -48,6 +49,7 @@ class RecommendedAppsController extends Controller {
* @NoCSRFRequired
* @return Response
*/
#[FrontpageRoute(verb: 'GET', url: '/core/apps/recommended')]
public function index(): Response {
$defaultPageUrl = $this->urlGenerator->linkToDefaultPageUrl();
$this->initialStateService->provideInitialState('core', 'defaultPageUrl', $defaultPageUrl);

@ -27,6 +27,7 @@ namespace OC\Core\Controller;
use OCA\Core\ResponseDefinitions;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\ApiRoute;
use OCP\AppFramework\Http\DataResponse;
use OCP\Collaboration\Reference\IDiscoverableReferenceProvider;
use OCP\Collaboration\Reference\IReferenceManager;
@ -59,6 +60,7 @@ class ReferenceApiController extends \OCP\AppFramework\OCSController {
*
* 200: References returned
*/
#[ApiRoute(verb: 'POST', url: '/extract', root: '/references')]
public function extract(string $text, bool $resolve = false, int $limit = 1): DataResponse {
$references = $this->referenceManager->extractReferences($text);
@ -87,6 +89,7 @@ class ReferenceApiController extends \OCP\AppFramework\OCSController {
*
* 200: Reference returned
*/
#[ApiRoute(verb: 'GET', url: '/resolve', root: '/references')]
public function resolveOne(string $reference): DataResponse {
/** @var ?CoreReference $resolvedReference */
$resolvedReference = $this->referenceManager->resolveReference(trim($reference))?->jsonSerialize();
@ -107,6 +110,7 @@ class ReferenceApiController extends \OCP\AppFramework\OCSController {
*
* 200: References returned
*/
#[ApiRoute(verb: 'POST', url: '/resolve', root: '/references')]
public function resolve(array $references, int $limit = 1): DataResponse {
$result = [];
$index = 0;
@ -132,6 +136,7 @@ class ReferenceApiController extends \OCP\AppFramework\OCSController {
*
* 200: Providers returned
*/
#[ApiRoute(verb: 'GET', url: '/providers', root: '/references')]
public function getProvidersInfo(): DataResponse {
$providers = $this->referenceManager->getDiscoverableProviders();
$jsonProviders = array_map(static function (IDiscoverableReferenceProvider $provider) {
@ -151,6 +156,7 @@ class ReferenceApiController extends \OCP\AppFramework\OCSController {
*
* 200: Provider touched
*/
#[ApiRoute(verb: 'PUT', url: '/provider/{providerId}', root: '/references')]
public function touchProvider(string $providerId, ?int $timestamp = null): DataResponse {
if ($this->userId !== null) {
$success = $this->referenceManager->touchProvider($this->userId, $providerId, $timestamp);

@ -27,6 +27,7 @@ namespace OC\Core\Controller;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
use OCP\AppFramework\Http\DataDownloadResponse;
use OCP\AppFramework\Http\DataResponse;
use OCP\Collaboration\Reference\IReferenceManager;
@ -57,6 +58,7 @@ class ReferenceController extends Controller {
* 200: Preview returned
* 404: Reference not found
*/
#[FrontpageRoute(verb: 'GET', url: '/core/references/preview/{referenceId}')]
public function preview(string $referenceId): DataDownloadResponse|DataResponse {
$reference = $this->referenceManager->getReferenceByCacheKey($referenceId);

@ -27,6 +27,7 @@ declare(strict_types=1);
namespace OC\Core\Controller;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
use OCP\ISearch;
@ -46,6 +47,7 @@ class SearchController extends Controller {
/**
* @NoAdminRequired
*/
#[FrontpageRoute(verb: 'GET', url: '/core/search')]
public function search(string $query, array $inApps = [], int $page = 1, int $size = 30): JSONResponse {
$results = $this->searcher->searchPaged($query, $inApps, $page, $size);

@ -30,6 +30,7 @@ use InvalidArgumentException;
use OCA\Core\ResponseDefinitions;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\AnonRateLimit;
use OCP\AppFramework\Http\Attribute\ApiRoute;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\PublicPage;
use OCP\AppFramework\Http\Attribute\UserRateLimit;
@ -72,6 +73,7 @@ class TextProcessingApiController extends \OCP\AppFramework\OCSController {
* 200: Task types returned
*/
#[PublicPage]
#[ApiRoute(verb: 'GET', url: '/tasktypes', root: '/textprocessing')]
public function taskTypes(): DataResponse {
$typeClasses = $this->textProcessingManager->getAvailableTaskTypes();
$types = [];
@ -113,6 +115,7 @@ class TextProcessingApiController extends \OCP\AppFramework\OCSController {
#[PublicPage]
#[UserRateLimit(limit: 20, period: 120)]
#[AnonRateLimit(limit: 5, period: 120)]
#[ApiRoute(verb: 'POST', url: '/schedule', root: '/textprocessing')]
public function schedule(string $input, string $type, string $appId, string $identifier = ''): DataResponse {
try {
$task = new Task($type, $input, $appId, $this->userId, $identifier);
@ -150,6 +153,7 @@ class TextProcessingApiController extends \OCP\AppFramework\OCSController {
* 404: Task not found
*/
#[PublicPage]
#[ApiRoute(verb: 'GET', url: '/task/{id}', root: '/textprocessing')]
public function getTask(int $id): DataResponse {
try {
$task = $this->textProcessingManager->getUserTask($id, $this->userId);
@ -177,6 +181,7 @@ class TextProcessingApiController extends \OCP\AppFramework\OCSController {
* 404: Task not found
*/
#[NoAdminRequired]
#[ApiRoute(verb: 'DELETE', url: '/task/{id}', root: '/textprocessing')]
public function deleteTask(int $id): DataResponse {
try {
$task = $this->textProcessingManager->getUserTask($id, $this->userId);
@ -207,6 +212,7 @@ class TextProcessingApiController extends \OCP\AppFramework\OCSController {
* 200: Task list returned
*/
#[NoAdminRequired]
#[ApiRoute(verb: 'GET', url: '/tasks/app/{appId}', root: '/textprocessing')]
public function listTasksByApp(string $appId, ?string $identifier = null): DataResponse {
try {
$tasks = $this->textProcessingManager->getUserTasksByApp($this->userId, $appId, $identifier);

@ -30,6 +30,7 @@ use OC\Files\AppData\AppData;
use OCA\Core\ResponseDefinitions;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\AnonRateLimit;
use OCP\AppFramework\Http\Attribute\ApiRoute;
use OCP\AppFramework\Http\Attribute\BruteForceProtection;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\PublicPage;
@ -69,6 +70,7 @@ class TextToImageApiController extends \OCP\AppFramework\OCSController {
* 200: Returns availability status
*/
#[PublicPage]
#[ApiRoute(verb: 'GET', url: '/is_available', root: '/text2image')]
public function isAvailable(): DataResponse {
return new DataResponse([
'isAvailable' => $this->textToImageManager->hasProviders(),
@ -91,6 +93,7 @@ class TextToImageApiController extends \OCP\AppFramework\OCSController {
#[PublicPage]
#[UserRateLimit(limit: 20, period: 120)]
#[AnonRateLimit(limit: 5, period: 120)]
#[ApiRoute(verb: 'POST', url: '/schedule', root: '/text2image')]
public function schedule(string $input, string $appId, string $identifier = '', int $numberOfImages = 8): DataResponse {
$task = new Task($input, $appId, $numberOfImages, $this->userId, $identifier);
try {
@ -125,6 +128,7 @@ class TextToImageApiController extends \OCP\AppFramework\OCSController {
*/
#[PublicPage]
#[BruteForceProtection(action: 'text2image')]
#[ApiRoute(verb: 'GET', url: '/task/{id}', root: '/text2image')]
public function getTask(int $id): DataResponse {
try {
$task = $this->textToImageManager->getUserTask($id, $this->userId);
@ -156,6 +160,7 @@ class TextToImageApiController extends \OCP\AppFramework\OCSController {
*/
#[PublicPage]
#[BruteForceProtection(action: 'text2image')]
#[ApiRoute(verb: 'GET', url: '/task/{id}/image/{index}', root: '/text2image')]
public function getImage(int $id, int $index): DataResponse|FileDisplayResponse {
try {
$task = $this->textToImageManager->getUserTask($id, $this->userId);
@ -195,6 +200,7 @@ class TextToImageApiController extends \OCP\AppFramework\OCSController {
*/
#[NoAdminRequired]
#[BruteForceProtection(action: 'text2image')]
#[ApiRoute(verb: 'DELETE', url: '/task/{id}', root: '/text2image')]
public function deleteTask(int $id): DataResponse {
try {
$task = $this->textToImageManager->getUserTask($id, $this->userId);
@ -228,6 +234,7 @@ class TextToImageApiController extends \OCP\AppFramework\OCSController {
*/
#[NoAdminRequired]
#[AnonRateLimit(limit: 5, period: 120)]
#[ApiRoute(verb: 'GET', url: '/tasks/app/{appId}', root: '/text2image')]
public function listTasksByApp(string $appId, ?string $identifier = null): DataResponse {
try {
$tasks = $this->textToImageManager->getUserTasksByApp($this->userId, $appId, $identifier);

@ -29,6 +29,7 @@ namespace OC\Core\Controller;
use InvalidArgumentException;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\ApiRoute;
use OCP\AppFramework\Http\DataResponse;
use OCP\IL10N;
use OCP\IRequest;
@ -55,6 +56,7 @@ class TranslationApiController extends \OCP\AppFramework\OCSController {
*
* 200: Supported languages returned
*/
#[ApiRoute(verb: 'GET', url: '/languages', root: '/translation')]
public function languages(): DataResponse {
return new DataResponse([
'languages' => array_map(fn ($lang) => $lang->jsonSerialize(), $this->translationManager->getLanguages()),
@ -78,6 +80,7 @@ class TranslationApiController extends \OCP\AppFramework\OCSController {
* 400: Language not detected or unable to translate
* 412: Translating is not possible
*/
#[ApiRoute(verb: 'POST', url: '/translate', root: '/translation')]
public function translate(string $text, ?string $fromLanguage, string $toLanguage): DataResponse {
try {
$translation = $this->translationManager->translate($text, $fromLanguage, $toLanguage);

@ -29,6 +29,7 @@ namespace OC\Core\Controller;
use OC\Authentication\TwoFactorAuth\Manager;
use OC_User;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\Attribute\UseSession;
use OCP\AppFramework\Http\RedirectResponse;
@ -89,6 +90,7 @@ class TwoFactorChallengeController extends Controller {
* @param string $redirect_url
* @return StandaloneTemplateResponse
*/
#[FrontpageRoute(verb: 'GET', url: '/login/selectchallenge')]
public function selectChallenge($redirect_url) {
$user = $this->userSession->getUser();
$providerSet = $this->twoFactorManager->getProviderSet($user);
@ -117,6 +119,7 @@ class TwoFactorChallengeController extends Controller {
* @return StandaloneTemplateResponse|RedirectResponse
*/
#[UseSession]
#[FrontpageRoute(verb: 'GET', url: '/login/challenge/{challengeProviderId}')]
public function showChallenge($challengeProviderId, $redirect_url) {
$user = $this->userSession->getUser();
$providerSet = $this->twoFactorManager->getProviderSet($user);
@ -171,6 +174,7 @@ class TwoFactorChallengeController extends Controller {
* @return RedirectResponse
*/
#[UseSession]
#[FrontpageRoute(verb: 'POST', url: '/login/challenge/{challengeProviderId}')]
public function solveChallenge($challengeProviderId, $challenge, $redirect_url = null) {
$user = $this->userSession->getUser();
$provider = $this->twoFactorManager->getProvider($user, $challengeProviderId);
@ -208,6 +212,7 @@ class TwoFactorChallengeController extends Controller {
* @NoAdminRequired
* @NoCSRFRequired
*/
#[FrontpageRoute(verb: 'GET', url: 'login/setupchallenge')]
public function setupProviders(): StandaloneTemplateResponse {
$user = $this->userSession->getUser();
$setupProviders = $this->twoFactorManager->getLoginSetupProviders($user);
@ -224,6 +229,7 @@ class TwoFactorChallengeController extends Controller {
* @NoAdminRequired
* @NoCSRFRequired
*/
#[FrontpageRoute(verb: 'GET', url: 'login/setupchallenge/{providerId}')]
public function setupProvider(string $providerId) {
$user = $this->userSession->getUser();
$providers = $this->twoFactorManager->getLoginSetupProviders($user);
@ -257,6 +263,7 @@ class TwoFactorChallengeController extends Controller {
*
* @todo handle the extreme edge case of an invalid provider ID and redirect to the provider selection page
*/
#[FrontpageRoute(verb: 'POST', url: 'login/setupchallenge/{providerId}')]
public function confirmProviderSetup(string $providerId) {
return new RedirectResponse($this->urlGenerator->linkToRoute(
'core.TwoFactorChallenge.showChallenge',

@ -34,6 +34,7 @@ use OC\Search\SearchQuery;
use OC\Search\UnsupportedFilter;
use OCA\Core\ResponseDefinitions;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\ApiRoute;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\IRequest;
@ -69,6 +70,7 @@ class UnifiedSearchController extends OCSController {
*
* 200: Providers returned
*/
#[ApiRoute(verb: 'GET', url: '/providers', root: '/search')]
public function getProviders(string $from = ''): DataResponse {
[$route, $parameters] = $this->getRouteInformation($from);
@ -99,6 +101,7 @@ class UnifiedSearchController extends OCSController {
* 200: Search entries returned
* 400: Searching is not possible
*/
#[ApiRoute(verb: 'GET', url: '/providers/{providerId}/search', root: '/search')]
public function search(
string $providerId,
// Unused parameter for OpenAPI spec generator

@ -28,6 +28,7 @@ declare(strict_types=1);
namespace OC\Core\Controller;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\Response;
use OCP\AppFramework\Http\TemplateResponse;
@ -46,6 +47,7 @@ class UnsupportedBrowserController extends Controller {
*
* @return Response
*/
#[FrontpageRoute(verb: 'GET', url: 'unsupported')]
public function index(): Response {
Util::addScript('core', 'unsupported-browser');
Util::addStyle('core', 'icons');

@ -25,6 +25,7 @@
namespace OC\Core\Controller;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
use OCP\IUserManager;
@ -47,6 +48,7 @@ class UserController extends Controller {
*
* @return JSONResponse
*/
#[FrontpageRoute(verb: 'POST', url: '/displaynames')]
public function getDisplayNames($users) {
$result = [];

@ -26,6 +26,7 @@ namespace OC\Core\Controller;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\Response;
@ -35,6 +36,7 @@ class WalledGardenController extends Controller {
* @PublicPage
* @NoCSRFRequired
*/
#[FrontpageRoute(verb: 'GET', url: '/204')]
public function get(): Response {
$resp = new Response();
$resp->setStatus(Http::STATUS_NO_CONTENT);

@ -33,6 +33,7 @@ use OC\Authentication\WebAuthn\Manager;
use OC\URLGenerator;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
use OCP\AppFramework\Http\Attribute\UseSession;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
@ -62,6 +63,7 @@ class WebAuthnController extends Controller {
* @PublicPage
*/
#[UseSession]
#[FrontpageRoute(verb: 'POST', url: 'login/webauthn/start')]
public function startAuthentication(string $loginName): JSONResponse {
$this->logger->debug('Starting WebAuthn login');
@ -86,6 +88,7 @@ class WebAuthnController extends Controller {
* @PublicPage
*/
#[UseSession]
#[FrontpageRoute(verb: 'POST', url: 'login/webauthn/finish')]
public function finishAuthentication(string $data): JSONResponse {
$this->logger->debug('Validating WebAuthn login');

@ -29,6 +29,7 @@ namespace OC\Core\Controller;
use OC\Http\WellKnown\RequestManager;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Http\Response;
@ -49,6 +50,7 @@ class WellKnownController extends Controller {
*
* @return Response
*/
#[FrontpageRoute(verb: 'GET', url: '.well-known/{service}')]
public function handle(string $service): Response {
$response = $this->requestManager->process(
$service,

@ -29,6 +29,7 @@ use OC\Security\IdentityProof\Manager;
use OC\Updater\ChangesCheck;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\ApiRoute;
use OCP\AppFramework\Http\DataResponse;
use OCP\Defaults;
use OCP\IConfig;
@ -63,6 +64,7 @@ class WhatsNewController extends OCSController {
* 200: Changes returned
* 204: No changes
*/
#[ApiRoute(verb: 'GET', url: '/whatsnew', root: '/core')]
public function get():DataResponse {
$user = $this->userSession->getUser();
if ($user === null) {
@ -110,6 +112,7 @@ class WhatsNewController extends OCSController {
*
* 200: Changes dismissed
*/
#[ApiRoute(verb: 'POST', url: '/whatsnew', root: '/core')]
public function dismiss(string $version):DataResponse {
$user = $this->userSession->getUser();
if ($user === null) {

@ -29,6 +29,7 @@ namespace OC\Core\Controller;
use OC\Authentication\Token\RemoteWipe;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
use OCP\AppFramework\Http\JSONResponse;
use OCP\Authentication\Exceptions\InvalidTokenException;
use OCP\IRequest;
@ -58,6 +59,7 @@ class WipeController extends Controller {
* 200: Device should be wiped
* 404: Device should not be wiped
*/
#[FrontpageRoute(verb: 'POST', url: '/core/wipe/check')]
public function checkWipe(string $token): JSONResponse {
try {
if ($this->remoteWipe->start($token)) {
@ -89,6 +91,7 @@ class WipeController extends Controller {
* 200: Wipe finished successfully
* 404: Device should not be wiped
*/
#[FrontpageRoute(verb: 'POST', url: '/core/wipe/success')]
public function wipeDone(string $token): JSONResponse {
try {
if ($this->remoteWipe->finish($token)) {

File diff suppressed because it is too large Load Diff

@ -35,139 +35,6 @@ declare(strict_types=1);
*
*/
use OC\Core\Application;
/** @var Application $application */
$application = \OC::$server->query(Application::class);
$application->registerRoutes($this, [
'routes' => [
['name' => 'lost#email', 'url' => '/lostpassword/email', 'verb' => 'POST'],
['name' => 'lost#resetform', 'url' => '/lostpassword/reset/form/{token}/{userId}', 'verb' => 'GET'],
['name' => 'lost#setPassword', 'url' => '/lostpassword/set/{token}/{userId}', 'verb' => 'POST'],
['name' => 'ProfilePage#index', 'url' => '/u/{targetUserId}', 'verb' => 'GET'],
['name' => 'user#getDisplayNames', 'url' => '/displaynames', 'verb' => 'POST'],
['name' => 'avatar#getAvatarDark', 'url' => '/avatar/{userId}/{size}/dark', 'verb' => 'GET'],
['name' => 'avatar#getAvatar', 'url' => '/avatar/{userId}/{size}', 'verb' => 'GET'],
['name' => 'avatar#deleteAvatar', 'url' => '/avatar/', 'verb' => 'DELETE'],
['name' => 'avatar#postCroppedAvatar', 'url' => '/avatar/cropped', 'verb' => 'POST'],
['name' => 'avatar#getTmpAvatar', 'url' => '/avatar/tmp', 'verb' => 'GET'],
['name' => 'avatar#postAvatar', 'url' => '/avatar/', 'verb' => 'POST'],
['name' => 'GuestAvatar#getAvatarDark', 'url' => '/avatar/guest/{guestName}/{size}/dark', 'verb' => 'GET'],
['name' => 'GuestAvatar#getAvatar', 'url' => '/avatar/guest/{guestName}/{size}', 'verb' => 'GET'],
['name' => 'CSRFToken#index', 'url' => '/csrftoken', 'verb' => 'GET'],
['name' => 'login#tryLogin', 'url' => '/login', 'verb' => 'POST'],
['name' => 'login#confirmPassword', 'url' => '/login/confirm', 'verb' => 'POST'],
['name' => 'login#showLoginForm', 'url' => '/login', 'verb' => 'GET'],
['name' => 'login#logout', 'url' => '/logout', 'verb' => 'GET'],
// Original login flow used by all clients
['name' => 'ClientFlowLogin#showAuthPickerPage', 'url' => '/login/flow', 'verb' => 'GET'],
['name' => 'ClientFlowLogin#generateAppPassword', 'url' => '/login/flow', 'verb' => 'POST'],
['name' => 'ClientFlowLogin#grantPage', 'url' => '/login/flow/grant', 'verb' => 'GET'],
['name' => 'ClientFlowLogin#apptokenRedirect', 'url' => '/login/flow/apptoken', 'verb' => 'POST'],
// NG login flow used by desktop client in case of Kerberos/fancy 2fa (smart cards for example)
['name' => 'ClientFlowLoginV2#poll', 'url' => '/login/v2/poll', 'verb' => 'POST'],
['name' => 'ClientFlowLoginV2#showAuthPickerPage', 'url' => '/login/v2/flow', 'verb' => 'GET'],
['name' => 'ClientFlowLoginV2#landing', 'url' => '/login/v2/flow/{token}', 'verb' => 'GET'],
['name' => 'ClientFlowLoginV2#grantPage', 'url' => '/login/v2/grant', 'verb' => 'GET'],
['name' => 'ClientFlowLoginV2#generateAppPassword', 'url' => '/login/v2/grant', 'verb' => 'POST'],
['name' => 'ClientFlowLoginV2#init', 'url' => '/login/v2', 'verb' => 'POST'],
['name' => 'ClientFlowLoginV2#apptokenRedirect', 'url' => '/login/v2/apptoken', 'verb' => 'POST'],
['name' => 'TwoFactorChallenge#selectChallenge', 'url' => '/login/selectchallenge', 'verb' => 'GET'],
['name' => 'TwoFactorChallenge#showChallenge', 'url' => '/login/challenge/{challengeProviderId}', 'verb' => 'GET'],
['name' => 'TwoFactorChallenge#solveChallenge', 'url' => '/login/challenge/{challengeProviderId}', 'verb' => 'POST'],
['name' => 'TwoFactorChallenge#setupProviders', 'url' => 'login/setupchallenge', 'verb' => 'GET'],
['name' => 'TwoFactorChallenge#setupProvider', 'url' => 'login/setupchallenge/{providerId}', 'verb' => 'GET'],
['name' => 'TwoFactorChallenge#confirmProviderSetup', 'url' => 'login/setupchallenge/{providerId}', 'verb' => 'POST'],
['name' => 'OCJS#getConfig', 'url' => '/core/js/oc.js', 'verb' => 'GET'],
['name' => 'Preview#getPreviewByFileId', 'url' => '/core/preview', 'verb' => 'GET'],
['name' => 'Preview#getPreview', 'url' => '/core/preview.png', 'verb' => 'GET'],
['name' => 'RecommendedApps#index', 'url' => '/core/apps/recommended', 'verb' => 'GET'],
['name' => 'Reference#preview', 'url' => '/core/references/preview/{referenceId}', 'verb' => 'GET'],
['name' => 'Css#getCss', 'url' => '/css/{appName}/{fileName}', 'verb' => 'GET'],
['name' => 'Js#getJs', 'url' => '/js/{appName}/{fileName}', 'verb' => 'GET'],
['name' => 'contactsMenu#index', 'url' => '/contactsmenu/contacts', 'verb' => 'POST'],
['name' => 'contactsMenu#findOne', 'url' => '/contactsmenu/findOne', 'verb' => 'POST'],
['name' => 'WalledGarden#get', 'url' => '/204', 'verb' => 'GET'],
['name' => 'Search#search', 'url' => '/core/search', 'verb' => 'GET'],
['name' => 'Wipe#checkWipe', 'url' => '/core/wipe/check', 'verb' => 'POST'],
['name' => 'Wipe#wipeDone', 'url' => '/core/wipe/success', 'verb' => 'POST'],
// Logins for passwordless auth
['name' => 'WebAuthn#startAuthentication', 'url' => 'login/webauthn/start', 'verb' => 'POST'],
['name' => 'WebAuthn#finishAuthentication', 'url' => 'login/webauthn/finish', 'verb' => 'POST'],
['name' => 'Error#error404', 'url' => 'error/404'],
['name' => 'Error#error403', 'url' => 'error/403'],
// Well known requests https://tools.ietf.org/html/rfc5785
['name' => 'WellKnown#handle', 'url' => '.well-known/{service}'],
// OCM Provider requests https://github.com/cs3org/OCM-API
['name' => 'OCM#discovery', 'url' => '/ocm-provider/'],
// Unsupported browser
['name' => 'UnsupportedBrowser#index', 'url' => 'unsupported'],
],
'ocs' => [
['root' => '/cloud', 'name' => 'OCS#getCapabilities', 'url' => '/capabilities', 'verb' => 'GET'],
['root' => '', 'name' => 'OCS#getConfig', 'url' => '/config', 'verb' => 'GET'],
['root' => '/person', 'name' => 'OCS#personCheck', 'url' => '/check', 'verb' => 'POST'],
['root' => '/identityproof', 'name' => 'OCS#getIdentityProof', 'url' => '/key/{cloudId}', 'verb' => 'GET'],
['root' => '/core', 'name' => 'Navigation#getAppsNavigation', 'url' => '/navigation/apps', 'verb' => 'GET'],
['root' => '/core', 'name' => 'Navigation#getSettingsNavigation', 'url' => '/navigation/settings', 'verb' => 'GET'],
['root' => '/core', 'name' => 'AutoComplete#get', 'url' => '/autocomplete/get', 'verb' => 'GET'],
['root' => '/core', 'name' => 'WhatsNew#get', 'url' => '/whatsnew', 'verb' => 'GET'],
['root' => '/core', 'name' => 'WhatsNew#dismiss', 'url' => '/whatsnew', 'verb' => 'POST'],
['root' => '/core', 'name' => 'AppPassword#getAppPassword', 'url' => '/getapppassword', 'verb' => 'GET'],
['root' => '/core', 'name' => 'AppPassword#rotateAppPassword', 'url' => '/apppassword/rotate', 'verb' => 'POST'],
['root' => '/core', 'name' => 'AppPassword#deleteAppPassword', 'url' => '/apppassword', 'verb' => 'DELETE'],
['root' => '/core', 'name' => 'AppPassword#confirmUserPassword', 'url' => '/apppassword/confirm', 'verb' => 'PUT'],
['root' => '/hovercard', 'name' => 'HoverCard#getUser', 'url' => '/v1/{userId}', 'verb' => 'GET'],
['root' => '/collaboration', 'name' => 'CollaborationResources#searchCollections', 'url' => '/resources/collections/search/{filter}', 'verb' => 'GET'],
['root' => '/collaboration', 'name' => 'CollaborationResources#listCollection', 'url' => '/resources/collections/{collectionId}', 'verb' => 'GET'],
['root' => '/collaboration', 'name' => 'CollaborationResources#renameCollection', 'url' => '/resources/collections/{collectionId}', 'verb' => 'PUT'],
['root' => '/collaboration', 'name' => 'CollaborationResources#addResource', 'url' => '/resources/collections/{collectionId}', 'verb' => 'POST'],
['root' => '/collaboration', 'name' => 'CollaborationResources#removeResource', 'url' => '/resources/collections/{collectionId}', 'verb' => 'DELETE'],
['root' => '/collaboration', 'name' => 'CollaborationResources#getCollectionsByResource', 'url' => '/resources/{resourceType}/{resourceId}', 'verb' => 'GET'],
['root' => '/collaboration', 'name' => 'CollaborationResources#createCollectionOnResource', 'url' => '/resources/{baseResourceType}/{baseResourceId}', 'verb' => 'POST'],
['root' => '/references', 'name' => 'ReferenceApi#resolveOne', 'url' => '/resolve', 'verb' => 'GET'],
['root' => '/references', 'name' => 'ReferenceApi#extract', 'url' => '/extract', 'verb' => 'POST'],
['root' => '/references', 'name' => 'ReferenceApi#resolve', 'url' => '/resolve', 'verb' => 'POST'],
['root' => '/references', 'name' => 'ReferenceApi#getProvidersInfo', 'url' => '/providers', 'verb' => 'GET'],
['root' => '/references', 'name' => 'ReferenceApi#touchProvider', 'url' => '/provider/{providerId}', 'verb' => 'PUT'],
['root' => '/profile', 'name' => 'ProfileApi#setVisibility', 'url' => '/{targetUserId}', 'verb' => 'PUT'],
// Unified search
['root' => '/search', 'name' => 'UnifiedSearch#getProviders', 'url' => '/providers', 'verb' => 'GET'],
['root' => '/search', 'name' => 'UnifiedSearch#search', 'url' => '/providers/{providerId}/search', 'verb' => 'GET'],
['root' => '/translation', 'name' => 'TranslationApi#languages', 'url' => '/languages', 'verb' => 'GET'],
['root' => '/translation', 'name' => 'TranslationApi#translate', 'url' => '/translate', 'verb' => 'POST'],
['root' => '/textprocessing', 'name' => 'TextProcessingApi#taskTypes', 'url' => '/tasktypes', 'verb' => 'GET'],
['root' => '/textprocessing', 'name' => 'TextProcessingApi#schedule', 'url' => '/schedule', 'verb' => 'POST'],
['root' => '/textprocessing', 'name' => 'TextProcessingApi#getTask', 'url' => '/task/{id}', 'verb' => 'GET'],
['root' => '/textprocessing', 'name' => 'TextProcessingApi#deleteTask', 'url' => '/task/{id}', 'verb' => 'DELETE'],
['root' => '/textprocessing', 'name' => 'TextProcessingApi#listTasksByApp', 'url' => '/tasks/app/{appId}', 'verb' => 'GET'],
['root' => '/text2image', 'name' => 'TextToImageApi#isAvailable', 'url' => '/is_available', 'verb' => 'GET'],
['root' => '/text2image', 'name' => 'TextToImageApi#schedule', 'url' => '/schedule', 'verb' => 'POST'],
['root' => '/text2image', 'name' => 'TextToImageApi#getTask', 'url' => '/task/{id}', 'verb' => 'GET'],
['root' => '/text2image', 'name' => 'TextToImageApi#getImage', 'url' => '/task/{id}/image/{index}', 'verb' => 'GET'],
['root' => '/text2image', 'name' => 'TextToImageApi#deleteTask', 'url' => '/task/{id}', 'verb' => 'DELETE'],
['root' => '/text2image', 'name' => 'TextToImageApi#listTasksByApp', 'url' => '/tasks/app/{appId}', 'verb' => 'GET'],
],
]);
// Post installation check
/** @var $this OCP\Route\IRouter */
// Core ajax actions
// Routing

@ -38,15 +38,18 @@ return array(
'OCP\\AppFramework\\Http' => $baseDir . '/lib/public/AppFramework/Http.php',
'OCP\\AppFramework\\Http\\Attribute\\ARateLimit' => $baseDir . '/lib/public/AppFramework/Http/Attribute/ARateLimit.php',
'OCP\\AppFramework\\Http\\Attribute\\AnonRateLimit' => $baseDir . '/lib/public/AppFramework/Http/Attribute/AnonRateLimit.php',
'OCP\\AppFramework\\Http\\Attribute\\ApiRoute' => $baseDir . '/lib/public/AppFramework/Http/Attribute/ApiRoute.php',
'OCP\\AppFramework\\Http\\Attribute\\AuthorizedAdminSetting' => $baseDir . '/lib/public/AppFramework/Http/Attribute/AuthorizedAdminSetting.php',
'OCP\\AppFramework\\Http\\Attribute\\BruteForceProtection' => $baseDir . '/lib/public/AppFramework/Http/Attribute/BruteForceProtection.php',
'OCP\\AppFramework\\Http\\Attribute\\CORS' => $baseDir . '/lib/public/AppFramework/Http/Attribute/CORS.php',
'OCP\\AppFramework\\Http\\Attribute\\FrontpageRoute' => $baseDir . '/lib/public/AppFramework/Http/Attribute/FrontpageRoute.php',
'OCP\\AppFramework\\Http\\Attribute\\IgnoreOpenAPI' => $baseDir . '/lib/public/AppFramework/Http/Attribute/IgnoreOpenAPI.php',
'OCP\\AppFramework\\Http\\Attribute\\NoAdminRequired' => $baseDir . '/lib/public/AppFramework/Http/Attribute/NoAdminRequired.php',
'OCP\\AppFramework\\Http\\Attribute\\NoCSRFRequired' => $baseDir . '/lib/public/AppFramework/Http/Attribute/NoCSRFRequired.php',
'OCP\\AppFramework\\Http\\Attribute\\OpenAPI' => $baseDir . '/lib/public/AppFramework/Http/Attribute/OpenAPI.php',
'OCP\\AppFramework\\Http\\Attribute\\PasswordConfirmationRequired' => $baseDir . '/lib/public/AppFramework/Http/Attribute/PasswordConfirmationRequired.php',
'OCP\\AppFramework\\Http\\Attribute\\PublicPage' => $baseDir . '/lib/public/AppFramework/Http/Attribute/PublicPage.php',
'OCP\\AppFramework\\Http\\Attribute\\Route' => $baseDir . '/lib/public/AppFramework/Http/Attribute/Route.php',
'OCP\\AppFramework\\Http\\Attribute\\StrictCookiesRequired' => $baseDir . '/lib/public/AppFramework/Http/Attribute/StrictCookiesRequired.php',
'OCP\\AppFramework\\Http\\Attribute\\SubAdminRequired' => $baseDir . '/lib/public/AppFramework/Http/Attribute/SubAdminRequired.php',
'OCP\\AppFramework\\Http\\Attribute\\UseSession' => $baseDir . '/lib/public/AppFramework/Http/Attribute/UseSession.php',

@ -71,15 +71,18 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\AppFramework\\Http' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http.php',
'OCP\\AppFramework\\Http\\Attribute\\ARateLimit' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/ARateLimit.php',
'OCP\\AppFramework\\Http\\Attribute\\AnonRateLimit' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/AnonRateLimit.php',
'OCP\\AppFramework\\Http\\Attribute\\ApiRoute' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/ApiRoute.php',
'OCP\\AppFramework\\Http\\Attribute\\AuthorizedAdminSetting' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/AuthorizedAdminSetting.php',
'OCP\\AppFramework\\Http\\Attribute\\BruteForceProtection' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/BruteForceProtection.php',
'OCP\\AppFramework\\Http\\Attribute\\CORS' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/CORS.php',
'OCP\\AppFramework\\Http\\Attribute\\FrontpageRoute' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/FrontpageRoute.php',
'OCP\\AppFramework\\Http\\Attribute\\IgnoreOpenAPI' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/IgnoreOpenAPI.php',
'OCP\\AppFramework\\Http\\Attribute\\NoAdminRequired' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/NoAdminRequired.php',
'OCP\\AppFramework\\Http\\Attribute\\NoCSRFRequired' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/NoCSRFRequired.php',
'OCP\\AppFramework\\Http\\Attribute\\OpenAPI' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/OpenAPI.php',
'OCP\\AppFramework\\Http\\Attribute\\PasswordConfirmationRequired' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/PasswordConfirmationRequired.php',
'OCP\\AppFramework\\Http\\Attribute\\PublicPage' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/PublicPage.php',
'OCP\\AppFramework\\Http\\Attribute\\Route' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/Route.php',
'OCP\\AppFramework\\Http\\Attribute\\StrictCookiesRequired' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/StrictCookiesRequired.php',
'OCP\\AppFramework\\Http\\Attribute\\SubAdminRequired' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/SubAdminRequired.php',
'OCP\\AppFramework\\Http\\Attribute\\UseSession' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/UseSession.php',

@ -136,7 +136,13 @@ class RouteConfig {
$controllerName = $this->buildControllerName($controller);
$actionName = $this->buildActionName($action);
$routeName = $routeNamePrefix . $this->appName . '.' . $controller . '.' . $action . $postfix;
/*
* The route name has to be lowercase, for symfony to match it correctly.
* This is required because smyfony allows mixed casing for controller names in the routes.
* To avoid breaking all the existing route names, registering and matching will only use the lowercase names.
* This is also safe on the PHP side because class and method names collide regardless of the casing.
*/
$routeName = strtolower($routeNamePrefix . $this->appName . '.' . $controller . '.' . $action . $postfix);
$router = $this->router->create($routeName, $url)
->method($verb);

@ -100,7 +100,13 @@ class RouteParser {
$controllerName = $this->buildControllerName($controller);
$actionName = $this->buildActionName($action);
$routeName = $routeNamePrefix . $appName . '.' . $controller . '.' . $action . $postfix;
/*
* The route name has to be lowercase, for symfony to match it correctly.
* This is required because smyfony allows mixed casing for controller names in the routes.
* To avoid breaking all the existing route names, registering and matching will only use the lowercase names.
* This is also safe on the PHP side because class and method names collide regardless of the casing.
*/
$routeName = strtolower($routeNamePrefix . $appName . '.' . $controller . '.' . $action . $postfix);
$routeObject = new Route($url);
$routeObject->method($verb);

@ -14,6 +14,7 @@
* @author Robin McCorkell <robin@mccorkell.me.uk>
* @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Thomas Müller <thomas.mueller@tmit.eu>
* @author Kate Döen <kate.doeen@nextcloud.com>
*
* @license AGPL-3.0
*
@ -32,8 +33,10 @@
*/
namespace OC\Route;
use DirectoryIterator;
use OC\AppFramework\Routing\RouteParser;
use OCP\AppFramework\App;
use OCP\AppFramework\Http\Attribute\Route as RouteAttribute;
use OCP\Diagnostics\IEventLogger;
use OCP\IConfig;
use OCP\IRequest;
@ -41,6 +44,9 @@ use OCP\Route\IRouter;
use OCP\Util;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
use ReflectionAttribute;
use ReflectionClass;
use ReflectionException;
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
use Symfony\Component\Routing\Exception\RouteNotFoundException;
use Symfony\Component\Routing\Generator\UrlGenerator;
@ -150,6 +156,22 @@ class Router implements IRouter {
}
}
$this->eventLogger->start('route:load:' . $requestedApp, 'Loading Routes for ' . $requestedApp);
if ($requestedApp !== null) {
$routes = $this->getAttributeRoutes($requestedApp);
if (count($routes) > 0) {
$this->useCollection($requestedApp);
$this->setupRoutes($routes, $requestedApp);
$collection = $this->getCollection($requestedApp);
$this->root->addCollection($collection);
// Also add the OCS collection
$collection = $this->getCollection($requestedApp . '.ocs');
$collection->addPrefix('/ocsapp');
$this->root->addCollection($collection);
}
}
foreach ($routingFiles as $app => $file) {
if (!isset($this->loadedApps[$app])) {
if (!\OC_App::isAppLoaded($app)) {
@ -173,6 +195,7 @@ class Router implements IRouter {
if (!isset($this->loadedApps['core'])) {
$this->loadedApps['core'] = true;
$this->useCollection('root');
$this->setupRoutes($this->getAttributeRoutes('core'), 'core');
require_once __DIR__ . '/../../../core/routes.php';
// Also add the OCS collection
@ -360,6 +383,13 @@ class Router implements IRouter {
if ($absolute === false) {
$referenceType = UrlGenerator::ABSOLUTE_PATH;
}
/*
* The route name has to be lowercase, for symfony to match it correctly.
* This is required because smyfony allows mixed casing for controller names in the routes.
* To avoid breaking all the existing route names, registering and matching will only use the lowercase names.
* This is also safe on the PHP side because class and method names collide regardless of the casing.
*/
$name = strtolower($name);
$name = $this->fixLegacyRootName($name);
if (str_contains($name, '.')) {
[$appName, $other] = explode('.', $name, 3);
@ -385,33 +415,78 @@ class Router implements IRouter {
}
protected function fixLegacyRootName(string $routeName): string {
if ($routeName === 'files.viewcontroller.showFile') {
return 'files.View.showFile';
if ($routeName === 'files.viewcontroller.showfile') {
return 'files.view.showfile';
}
if ($routeName === 'files_sharing.sharecontroller.showShare') {
return 'files_sharing.Share.showShare';
if ($routeName === 'files_sharing.sharecontroller.showshare') {
return 'files_sharing.share.showshare';
}
if ($routeName === 'files_sharing.sharecontroller.showAuthenticate') {
return 'files_sharing.Share.showAuthenticate';
if ($routeName === 'files_sharing.sharecontroller.showauthenticate') {
return 'files_sharing.share.showauthenticate';
}
if ($routeName === 'files_sharing.sharecontroller.authenticate') {
return 'files_sharing.Share.authenticate';
return 'files_sharing.share.authenticate';
}
if ($routeName === 'files_sharing.sharecontroller.downloadShare') {
return 'files_sharing.Share.downloadShare';
if ($routeName === 'files_sharing.sharecontroller.downloadshare') {
return 'files_sharing.share.downloadshare';
}
if ($routeName === 'files_sharing.publicpreview.directLink') {
return 'files_sharing.PublicPreview.directLink';
if ($routeName === 'files_sharing.publicpreview.directlink') {
return 'files_sharing.publicpreview.directlink';
}
if ($routeName === 'cloud_federation_api.requesthandlercontroller.addShare') {
return 'cloud_federation_api.RequestHandler.addShare';
if ($routeName === 'cloud_federation_api.requesthandlercontroller.addshare') {
return 'cloud_federation_api.requesthandler.addshare';
}
if ($routeName === 'cloud_federation_api.requesthandlercontroller.receiveNotification') {
return 'cloud_federation_api.RequestHandler.receiveNotification';
if ($routeName === 'cloud_federation_api.requesthandlercontroller.receivenotification') {
return 'cloud_federation_api.requesthandler.receivenotification';
}
return $routeName;
}
/**
* @throws ReflectionException
*/
private function getAttributeRoutes(string $app): array {
$routes = [];
if ($app === 'core') {
$appControllerPath = __DIR__ . '/../../../core/Controller';
$appNameSpace = 'OC\\Core';
} else {
$appControllerPath = \OC_App::getAppPath($app) . '/lib/Controller';
$appNameSpace = App::buildAppNamespace($app);
}
if (!file_exists($appControllerPath)) {
return [];
}
$dir = new DirectoryIterator($appControllerPath);
foreach ($dir as $file) {
if (!str_ends_with($file->getPathname(), 'Controller.php')) {
continue;
}
$class = new ReflectionClass($appNameSpace . '\\Controller\\' . basename($file->getPathname(), '.php'));
foreach ($class->getMethods() as $method) {
foreach ($method->getAttributes(RouteAttribute::class, ReflectionAttribute::IS_INSTANCEOF) as $attribute) {
$route = $attribute->newInstance();
$serializedRoute = $route->toArray();
// Remove 'Controller' suffix
$serializedRoute['name'] = substr($class->getShortName(), 0, -10) . '#' . $method->getName();
$key = $route->getType();
$routes[$key] ??= [];
$routes[$key][] = $serializedRoute;
}
}
}
return $routes;
}
/**
* To isolate the variable scope used inside the $file it is required in it's own method
*

@ -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,
);
}
}

@ -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,
);
}
}

@ -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;
}
}

@ -8,16 +8,16 @@
"packages": [
{
"name": "adhocore/cli",
"version": "v1.6.1",
"version": "v1.6.2",
"source": {
"type": "git",
"url": "https://github.com/adhocore/php-cli.git",
"reference": "25b5a93e5eebcdb70e20ee33313a011ea3a4f770"
"reference": "34191315b0da20b9b4ecad783d91db992fa209a4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/adhocore/php-cli/zipball/25b5a93e5eebcdb70e20ee33313a011ea3a4f770",
"reference": "25b5a93e5eebcdb70e20ee33313a011ea3a4f770",
"url": "https://api.github.com/repos/adhocore/php-cli/zipball/34191315b0da20b9b4ecad783d91db992fa209a4",
"reference": "34191315b0da20b9b4ecad783d91db992fa209a4",
"shasum": ""
},
"require": {
@ -62,7 +62,7 @@
],
"support": {
"issues": "https://github.com/adhocore/php-cli/issues",
"source": "https://github.com/adhocore/php-cli/tree/v1.6.1"
"source": "https://github.com/adhocore/php-cli/tree/v1.6.2"
},
"funding": [
{
@ -74,7 +74,7 @@
"type": "github"
}
],
"time": "2023-06-26T09:55:29+00:00"
"time": "2024-01-22T22:37:23+00:00"
},
{
"name": "nextcloud/openapi-extractor",
@ -82,12 +82,12 @@
"source": {
"type": "git",
"url": "https://github.com/nextcloud/openapi-extractor.git",
"reference": "3b7b6ff9659a5f15612d4749e085bbcb83509049"
"reference": "e15283dc655436460c531ce286b7f0bae3e0bf31"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nextcloud/openapi-extractor/zipball/3b7b6ff9659a5f15612d4749e085bbcb83509049",
"reference": "3b7b6ff9659a5f15612d4749e085bbcb83509049",
"url": "https://api.github.com/repos/nextcloud/openapi-extractor/zipball/e15283dc655436460c531ce286b7f0bae3e0bf31",
"reference": "e15283dc655436460c531ce286b7f0bae3e0bf31",
"shasum": ""
},
"require": {
@ -129,7 +129,7 @@
"source": "https://github.com/nextcloud/openapi-extractor/tree/main",
"issues": "https://github.com/nextcloud/openapi-extractor/issues"
},
"time": "2024-01-18T14:53:21+00:00"
"time": "2024-02-21T11:05:43+00:00"
},
{
"name": "nikic/php-parser",

Loading…
Cancel
Save