-
-
Notifications
You must be signed in to change notification settings - Fork 4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(dav): implement personal absence settings
Signed-off-by: Richard Steinmetz <[email protected]>
- Loading branch information
Showing
12 changed files
with
567 additions
and
197 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ | |
* | ||
* @author Georg Ehrke <[email protected]> | ||
* @author Roeland Jago Douma <[email protected]> | ||
* @author Richard Steinmetz <[email protected]> | ||
* | ||
* @license GNU AGPL version 3 or any later version | ||
* | ||
|
@@ -28,7 +29,9 @@ | |
['name' => 'invitation_response#accept', 'url' => '/invitation/accept/{token}', 'verb' => 'GET'], | ||
['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' => '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'], | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
94 changes: 94 additions & 0 deletions
94
apps/dav/lib/Controller/AvailabilitySettingsController.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
/** | ||
* @copyright Copyright (c) 2023 Richard Steinmetz <[email protected]> | ||
* | ||
* @author Richard Steinmetz <[email protected]> | ||
* | ||
* @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; | ||
|
||
class AvailabilitySettingsController extends Controller { | ||
public function __construct( | ||
IRequest $request, | ||
private ?string $userId, | ||
private AbsenceService $absenceService, | ||
) { | ||
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 { | ||
$userId = $this->userId; | ||
if ($userId === 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( | ||
$userId, | ||
$firstDay, | ||
$lastDay, | ||
$status, | ||
$message, | ||
); | ||
return new JSONResponse($absence); | ||
} | ||
|
||
/** | ||
* @throws \OCP\DB\Exception | ||
*/ | ||
#[NoAdminRequired] | ||
public function clearAbsence(): Response { | ||
$userId = $this->userId; | ||
if ($userId === null) { | ||
return new JSONResponse([], Http::STATUS_FORBIDDEN); | ||
} | ||
|
||
$this->absenceService->clearAbsence($userId); | ||
return new JSONResponse([]); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ | |
* @copyright 2021 Christoph Wurst <[email protected]> | ||
* | ||
* @author 2021 Christoph Wurst <[email protected]> | ||
* @author Richard Steinmetz <[email protected]> | ||
* | ||
* @license GNU AGPL version 3 or any later version | ||
* | ||
|
@@ -26,10 +27,13 @@ | |
namespace OCA\DAV\Settings; | ||
|
||
use OCA\DAV\AppInfo\Application; | ||
use OCA\DAV\Db\AbsenceMapper; | ||
use OCP\AppFramework\Db\DoesNotExistException; | ||
use OCP\AppFramework\Http\TemplateResponse; | ||
use OCP\AppFramework\Services\IInitialState; | ||
use OCP\IConfig; | ||
use OCP\Settings\ISettings; | ||
use Psr\Log\LoggerInterface; | ||
|
||
class AvailabilitySettings implements ISettings { | ||
protected IConfig $config; | ||
|
@@ -38,7 +42,9 @@ class AvailabilitySettings implements ISettings { | |
|
||
public function __construct(IConfig $config, | ||
IInitialState $initialState, | ||
?string $userId) { | ||
?string $userId, | ||
private LoggerInterface $logger, | ||
private AbsenceMapper $absenceMapper) { | ||
$this->config = $config; | ||
$this->initialState = $initialState; | ||
$this->userId = $userId; | ||
|
@@ -54,6 +60,25 @@ public function getForm(): TemplateResponse { | |
'no' | ||
) | ||
); | ||
$hideAbsenceSettings = $this->config->getAppValue( | ||
Application::APP_ID, | ||
'hide_absence_settings', | ||
'yes', | ||
) === 'yes'; | ||
$this->initialState->provideInitialState('hide_absence_settings', $hideAbsenceSettings); | ||
if (!$hideAbsenceSettings) { | ||
try { | ||
$absence = $this->absenceMapper->findByUserId($this->userId); | ||
Check notice Code scanning / Psalm PossiblyNullArgument Note
Argument 1 of OCA\DAV\Db\AbsenceMapper::findByUserId cannot be null, possibly null value provided
|
||
$this->initialState->provideInitialState('absence', $absence); | ||
} catch (DoesNotExistException) { | ||
// The user has not yet set up an absence period. | ||
// Logging this error is not necessary. | ||
} catch (\OCP\DB\Exception $e) { | ||
$this->logger->error("Could not find absence data for user $this->userId: " . $e->getMessage(), [ | ||
'exception' => $e, | ||
]); | ||
} | ||
} | ||
|
||
return new TemplateResponse(Application::APP_ID, 'settings-personal-availability'); | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
<!-- | ||
- @copyright Copyright (c) 2023 Richard Steinmetz <[email protected]> | ||
- | ||
- @author Richard Steinmetz <[email protected]> | ||
- | ||
- @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/>. | ||
- | ||
--> | ||
|
||
<template> | ||
<div class="absence"> | ||
<div class="absence__dates"> | ||
<NcDateTimePickerNative id="absence-first-day" | ||
v-model="firstDay" | ||
:label="$t('dav', 'First day')" | ||
class="absence__dates__picker" /> | ||
<NcDateTimePickerNative id="absence-last-day" | ||
v-model="lastDay" | ||
:label="$t('dav', 'Last day (inclusive)')" | ||
class="absence__dates__picker" /> | ||
</div> | ||
<NcTextField :value.sync="status" :label="$t('dav', 'Short absence status')" /> | ||
<NcTextArea :value.sync="message" :label="$t('dav', 'Long absence Message')" /> | ||
|
||
<div class="absence__buttons"> | ||
<NcButton :disabled="loading || !valid" | ||
type="primary" | ||
@click="saveForm"> | ||
{{ $t('dav', 'Save') }} | ||
</NcButton> | ||
<NcButton :disabled="loading || !valid" | ||
type="error" | ||
@click="clearAbsence"> | ||
{{ $t('dav', 'Disable absence') }} | ||
</NcButton> | ||
</div> | ||
</div> | ||
</template> | ||
|
||
<script> | ||
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 axios from '@nextcloud/axios' | ||
import { formatDateAsYMD } from '../utils/date.js' | ||
import { loadState } from '@nextcloud/initial-state' | ||
import { showError } from '@nextcloud/dialogs' | ||
export default { | ||
name: 'AbsenceForm', | ||
components: { | ||
NcButton, | ||
NcTextField, | ||
NcTextArea, | ||
NcDateTimePickerNative, | ||
}, | ||
data() { | ||
const { firstDay, lastDay, status, message } = loadState('dav', 'absence', {}) | ||
return { | ||
loading: false, | ||
status: status ?? '', | ||
message: message ?? '', | ||
firstDay: firstDay ? new Date(firstDay) : new Date(), | ||
lastDay: lastDay ? new Date(lastDay) : null, | ||
} | ||
}, | ||
computed: { | ||
/** | ||
* @return {boolean} | ||
*/ | ||
valid() { | ||
return !!this.firstDay | ||
&& !!this.lastDay | ||
&& !!this.status | ||
&& this.lastDay > this.firstDay | ||
}, | ||
}, | ||
methods: { | ||
resetForm() { | ||
this.status = '' | ||
this.message = '' | ||
this.firstDay = new Date() | ||
this.lastDay = null | ||
}, | ||
async saveForm() { | ||
if (!this.valid) { | ||
return | ||
} | ||
this.loading = true | ||
try { | ||
await axios.post(generateUrl('/apps/dav/settings/absence'), { | ||
firstDay: formatDateAsYMD(this.firstDay), | ||
lastDay: formatDateAsYMD(this.lastDay), | ||
status: this.status, | ||
message: this.message, | ||
}) | ||
} catch (error) { | ||
showError(this.$t('dav', 'Failed to save your absence settings')) | ||
} finally { | ||
this.loading = false | ||
} | ||
}, | ||
async clearAbsence() { | ||
this.loading = true | ||
try { | ||
await axios.delete(generateUrl('/apps/dav/settings/absence')) | ||
this.resetForm() | ||
} catch (error) { | ||
showError(this.$t('dav', 'Failed to clear your absence settings')) | ||
} finally { | ||
this.loading = false | ||
} | ||
}, | ||
}, | ||
} | ||
</script> | ||
<style lang="scss" scoped> | ||
.absence { | ||
display: flex; | ||
flex-direction: column; | ||
gap: 5px; | ||
&__dates { | ||
display: flex; | ||
gap: 10px; | ||
width: 100%; | ||
&__picker { | ||
flex: 1 auto; | ||
::v-deep .native-datetime-picker--input { | ||
margin-bottom: 0; | ||
} | ||
} | ||
} | ||
&__buttons { | ||
display: flex; | ||
gap: 5px; | ||
} | ||
} | ||
</style> |
Oops, something went wrong.