mirror of
https://github.com/nextcloud/server.git
synced 2026-06-07 15:53:04 -04:00
Merge pull request #41963 from nextcloud/backport/41957/stable28
[stable28] feat(out-of-office): Add OCS endpoint to set and clear absence
This commit is contained in:
commit
385495050b
11 changed files with 360 additions and 112 deletions
|
|
@ -30,11 +30,11 @@ return [
|
|||
['name' => 'invitation_response#decline', 'url' => '/invitation/decline/{token}', 'verb' => 'GET'],
|
||||
['name' => 'invitation_response#options', 'url' => '/invitation/moreOptions/{token}', 'verb' => 'GET'],
|
||||
['name' => 'invitation_response#processMoreOptionsResult', 'url' => '/invitation/moreOptions/{token}', 'verb' => 'POST'],
|
||||
['name' => 'availability_settings#updateAbsence', 'url' => '/settings/absence', 'verb' => 'POST'],
|
||||
['name' => 'availability_settings#clearAbsence', 'url' => '/settings/absence', 'verb' => 'DELETE'],
|
||||
],
|
||||
'ocs' => [
|
||||
['name' => 'direct#getUrl', 'url' => '/api/v1/direct', 'verb' => 'POST'],
|
||||
['name' => 'out_of_office#getCurrentOutOfOfficeData', 'url' => '/api/v1/outOfOffice/{userId}', 'verb' => 'GET'],
|
||||
['name' => 'out_of_office#setOutOfOffice', 'url' => '/api/v1/outOfOffice/{userId}', 'verb' => 'POST'],
|
||||
['name' => 'out_of_office#clearOutOfOffice', 'url' => '/api/v1/outOfOffice/{userId}', 'verb' => 'DELETE'],
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -193,7 +193,6 @@ return array(
|
|||
'OCA\\DAV\\Connector\\Sabre\\SharesPlugin' => $baseDir . '/../lib/Connector/Sabre/SharesPlugin.php',
|
||||
'OCA\\DAV\\Connector\\Sabre\\TagList' => $baseDir . '/../lib/Connector/Sabre/TagList.php',
|
||||
'OCA\\DAV\\Connector\\Sabre\\TagsPlugin' => $baseDir . '/../lib/Connector/Sabre/TagsPlugin.php',
|
||||
'OCA\\DAV\\Controller\\AvailabilitySettingsController' => $baseDir . '/../lib/Controller/AvailabilitySettingsController.php',
|
||||
'OCA\\DAV\\Controller\\BirthdayCalendarController' => $baseDir . '/../lib/Controller/BirthdayCalendarController.php',
|
||||
'OCA\\DAV\\Controller\\DirectController' => $baseDir . '/../lib/Controller/DirectController.php',
|
||||
'OCA\\DAV\\Controller\\InvitationResponseController' => $baseDir . '/../lib/Controller/InvitationResponseController.php',
|
||||
|
|
|
|||
|
|
@ -208,7 +208,6 @@ class ComposerStaticInitDAV
|
|||
'OCA\\DAV\\Connector\\Sabre\\SharesPlugin' => __DIR__ . '/..' . '/../lib/Connector/Sabre/SharesPlugin.php',
|
||||
'OCA\\DAV\\Connector\\Sabre\\TagList' => __DIR__ . '/..' . '/../lib/Connector/Sabre/TagList.php',
|
||||
'OCA\\DAV\\Connector\\Sabre\\TagsPlugin' => __DIR__ . '/..' . '/../lib/Connector/Sabre/TagsPlugin.php',
|
||||
'OCA\\DAV\\Controller\\AvailabilitySettingsController' => __DIR__ . '/..' . '/../lib/Controller/AvailabilitySettingsController.php',
|
||||
'OCA\\DAV\\Controller\\BirthdayCalendarController' => __DIR__ . '/..' . '/../lib/Controller/BirthdayCalendarController.php',
|
||||
'OCA\\DAV\\Controller\\DirectController' => __DIR__ . '/..' . '/../lib/Controller/DirectController.php',
|
||||
'OCA\\DAV\\Controller\\InvitationResponseController' => __DIR__ . '/..' . '/../lib/Controller/InvitationResponseController.php',
|
||||
|
|
|
|||
|
|
@ -1,99 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2023 Richard Steinmetz <richard@steinmetz.cloud>
|
||||
*
|
||||
* @author Richard Steinmetz <richard@steinmetz.cloud>
|
||||
*
|
||||
* @license AGPL-3.0-or-later
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\DAV\Controller;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use OCA\DAV\AppInfo\Application;
|
||||
use OCA\DAV\Service\AbsenceService;
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
|
||||
use OCP\AppFramework\Http\JSONResponse;
|
||||
use OCP\AppFramework\Http\Response;
|
||||
use OCP\IRequest;
|
||||
use OCP\IUserSession;
|
||||
use OCP\User\IAvailabilityCoordinator;
|
||||
|
||||
class AvailabilitySettingsController extends Controller {
|
||||
public function __construct(
|
||||
IRequest $request,
|
||||
private ?IUserSession $userSession,
|
||||
private AbsenceService $absenceService,
|
||||
private IAvailabilityCoordinator $coordinator,
|
||||
) {
|
||||
parent::__construct(Application::APP_ID, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \OCP\DB\Exception
|
||||
* @throws \Exception
|
||||
*/
|
||||
#[NoAdminRequired]
|
||||
public function updateAbsence(
|
||||
string $firstDay,
|
||||
string $lastDay,
|
||||
string $status,
|
||||
string $message,
|
||||
): Response {
|
||||
$user = $this->userSession?->getUser();
|
||||
if ($user === null) {
|
||||
return new JSONResponse([], Http::STATUS_FORBIDDEN);
|
||||
}
|
||||
|
||||
$parsedFirstDay = new DateTimeImmutable($firstDay);
|
||||
$parsedLastDay = new DateTimeImmutable($lastDay);
|
||||
if ($parsedFirstDay->getTimestamp() >= $parsedLastDay->getTimestamp()) {
|
||||
throw new \Exception('First day is on or after last day');
|
||||
}
|
||||
|
||||
$absence = $this->absenceService->createOrUpdateAbsence(
|
||||
$user,
|
||||
$firstDay,
|
||||
$lastDay,
|
||||
$status,
|
||||
$message,
|
||||
);
|
||||
$this->coordinator->clearCache($user->getUID());
|
||||
return new JSONResponse($absence);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \OCP\DB\Exception
|
||||
*/
|
||||
#[NoAdminRequired]
|
||||
public function clearAbsence(): Response {
|
||||
$user = $this->userSession?->getUser();
|
||||
if ($user === null) {
|
||||
return new JSONResponse([], Http::STATUS_FORBIDDEN);
|
||||
}
|
||||
|
||||
$this->absenceService->clearAbsence($user);
|
||||
$this->coordinator->clearCache($user->getUID());
|
||||
return new JSONResponse([]);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -26,14 +26,18 @@ declare(strict_types=1);
|
|||
|
||||
namespace OCA\DAV\Controller;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use OCA\DAV\Db\AbsenceMapper;
|
||||
use OCA\DAV\ResponseDefinitions;
|
||||
use OCA\DAV\Service\AbsenceService;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
use OCP\AppFramework\OCSController;
|
||||
use OCP\IRequest;
|
||||
use OCP\IUserSession;
|
||||
use OCP\User\IAvailabilityCoordinator;
|
||||
|
||||
/**
|
||||
* @psalm-import-type DAVOutOfOfficeData from ResponseDefinitions
|
||||
|
|
@ -44,6 +48,9 @@ class OutOfOfficeController extends OCSController {
|
|||
string $appName,
|
||||
IRequest $request,
|
||||
private AbsenceMapper $absenceMapper,
|
||||
private ?IUserSession $userSession,
|
||||
private AbsenceService $absenceService,
|
||||
private IAvailabilityCoordinator $coordinator,
|
||||
) {
|
||||
parent::__construct($appName, $request);
|
||||
}
|
||||
|
|
@ -74,4 +81,74 @@ class OutOfOfficeController extends OCSController {
|
|||
'message' => $data->getMessage(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set out-of-office absence
|
||||
*
|
||||
* @param string $firstDay First day of the absence in format `YYYY-MM-DD`
|
||||
* @param string $lastDay Last day of the absence in format `YYYY-MM-DD`
|
||||
* @param string $status Short text that is set as user status during the absence
|
||||
* @param string $message Longer multiline message that is shown to others during the absence
|
||||
* @return DataResponse<Http::STATUS_OK, DAVOutOfOfficeData, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, array{error: 'firstDay'}, array{}>|DataResponse<Http::STATUS_UNAUTHORIZED, null, array{}>
|
||||
*
|
||||
* 200: Absence data
|
||||
* 400: When the first day is not before the last day
|
||||
* 401: When the user is not logged in
|
||||
*/
|
||||
#[NoAdminRequired]
|
||||
public function setOutOfOffice(
|
||||
string $firstDay,
|
||||
string $lastDay,
|
||||
string $status,
|
||||
string $message,
|
||||
): DataResponse {
|
||||
$user = $this->userSession?->getUser();
|
||||
if ($user === null) {
|
||||
return new DataResponse(null, Http::STATUS_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
$parsedFirstDay = new DateTimeImmutable($firstDay);
|
||||
$parsedLastDay = new DateTimeImmutable($lastDay);
|
||||
if ($parsedFirstDay->getTimestamp() > $parsedLastDay->getTimestamp()) {
|
||||
return new DataResponse(['error' => 'firstDay'], Http::STATUS_BAD_REQUEST);
|
||||
}
|
||||
|
||||
$data = $this->absenceService->createOrUpdateAbsence(
|
||||
$user,
|
||||
$firstDay,
|
||||
$lastDay,
|
||||
$status,
|
||||
$message,
|
||||
);
|
||||
$this->coordinator->clearCache($user->getUID());
|
||||
|
||||
return new DataResponse([
|
||||
'id' => $data->getId(),
|
||||
'userId' => $data->getUserId(),
|
||||
'firstDay' => $data->getFirstDay(),
|
||||
'lastDay' => $data->getLastDay(),
|
||||
'status' => $data->getStatus(),
|
||||
'message' => $data->getMessage(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the out-of-office
|
||||
*
|
||||
* @return DataResponse<Http::STATUS_OK|Http::STATUS_UNAUTHORIZED, null, array{}>
|
||||
*
|
||||
* 200: When the absence was cleared successfully
|
||||
* 401: When the user is not logged in
|
||||
*/
|
||||
#[NoAdminRequired]
|
||||
public function clearOutOfOffice(): DataResponse {
|
||||
$user = $this->userSession?->getUser();
|
||||
if ($user === null) {
|
||||
return new DataResponse(null, Http::STATUS_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
$this->absenceService->clearAbsence($user);
|
||||
$this->coordinator->clearCache($user->getUID());
|
||||
return new DataResponse(null);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -317,6 +317,277 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"operationId": "out_of_office-set-out-of-office",
|
||||
"summary": "Set out-of-office absence",
|
||||
"tags": [
|
||||
"out_of_office"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearer_auth": []
|
||||
},
|
||||
{
|
||||
"basic_auth": []
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "firstDay",
|
||||
"in": "query",
|
||||
"description": "First day of the absence in format `YYYY-MM-DD`",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "lastDay",
|
||||
"in": "query",
|
||||
"description": "Last day of the absence in format `YYYY-MM-DD`",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "status",
|
||||
"in": "query",
|
||||
"description": "Short text that is set as user status during the absence",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "message",
|
||||
"in": "query",
|
||||
"description": "Longer multiline message that is shown to others during the absence",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "userId",
|
||||
"in": "path",
|
||||
"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": "Absence data",
|
||||
"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/OutOfOfficeData"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "When the first day is not before the last day",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"error"
|
||||
],
|
||||
"properties": {
|
||||
"error": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"firstDay"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "When the user is not logged in",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"operationId": "out_of_office-clear-out-of-office",
|
||||
"summary": "Clear the out-of-office",
|
||||
"tags": [
|
||||
"out_of_office"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearer_auth": []
|
||||
},
|
||||
{
|
||||
"basic_auth": []
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "userId",
|
||||
"in": "path",
|
||||
"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": "When the absence was cleared successfully",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "When the user is not logged in",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -57,7 +57,8 @@ import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
|
|||
import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js'
|
||||
import NcTextArea from '@nextcloud/vue/dist/Components/NcTextArea.js'
|
||||
import NcDateTimePickerNative from '@nextcloud/vue/dist/Components/NcDateTimePickerNative.js'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import { generateOcsUrl } from '@nextcloud/router'
|
||||
import { getCurrentUser } from '@nextcloud/auth'
|
||||
import axios from '@nextcloud/axios'
|
||||
import { formatDateAsYMD } from '../utils/date.js'
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
|
|
@ -110,7 +111,7 @@ export default {
|
|||
|
||||
this.loading = true
|
||||
try {
|
||||
await axios.post(generateUrl('/apps/dav/settings/absence'), {
|
||||
await axios.post(generateOcsUrl('/apps/dav/api/v1/outOfOffice/{userId}', { userId: getCurrentUser().uid }), {
|
||||
firstDay: formatDateAsYMD(this.firstDay),
|
||||
lastDay: formatDateAsYMD(this.lastDay),
|
||||
status: this.status,
|
||||
|
|
@ -127,7 +128,7 @@ export default {
|
|||
async clearAbsence() {
|
||||
this.loading = true
|
||||
try {
|
||||
await axios.delete(generateUrl('/apps/dav/settings/absence'))
|
||||
await axios.delete(generateOcsUrl('/apps/dav/api/v1/outOfOffice/{userId}', { userId: getCurrentUser().uid }))
|
||||
this.resetForm()
|
||||
showSuccess(this.$t('dav', 'Absence cleared'))
|
||||
} catch (error) {
|
||||
|
|
|
|||
4
dist/core-common.js
vendored
4
dist/core-common.js
vendored
File diff suppressed because one or more lines are too long
2
dist/core-common.js.map
vendored
2
dist/core-common.js.map
vendored
File diff suppressed because one or more lines are too long
4
dist/dav-settings-personal-availability.js
vendored
4
dist/dav-settings-personal-availability.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue