Skip to content

Commit

Permalink
feat(dav): implement personal absence settings
Browse files Browse the repository at this point in the history
Signed-off-by: Richard Steinmetz <[email protected]>
  • Loading branch information
st3iny committed Nov 7, 2023
1 parent 84d2a9b commit 1248c5d
Show file tree
Hide file tree
Showing 12 changed files with 567 additions and 197 deletions.
5 changes: 4 additions & 1 deletion apps/dav/appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand All @@ -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'],
Expand Down
1 change: 1 addition & 0 deletions apps/dav/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@
'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',
Expand Down
1 change: 1 addition & 0 deletions apps/dav/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ 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',
Expand Down
94 changes: 94 additions & 0 deletions apps/dav/lib/Controller/AvailabilitySettingsController.php
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([]);
}

}
27 changes: 26 additions & 1 deletion apps/dav/lib/Settings/AvailabilitySettings.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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');
}
Expand Down
160 changes: 160 additions & 0 deletions apps/dav/src/components/AbsenceForm.vue
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>
Loading

0 comments on commit 1248c5d

Please sign in to comment.