Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 72 additions & 3 deletions lib/Controller/SettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ public function setConfig(string $key,
return $this->setSlotDuration($value);
case 'defaultReminder':
return $this->setDefaultReminder($value);
case 'defaultReminderPartDay':
return $this->setDefaultReminderPartDay($value);
case 'defaultReminderFullDay':
return $this->setDefaultReminderFullDay($value);
case 'showTasks':
return $this->setShowTasks($value);
case 'tasksSidebar':
Expand Down Expand Up @@ -346,16 +350,31 @@ private function setSlotDuration(string $value):JSONResponse {
return new JSONResponse();
}

/**
* validates reminder values
*
* @param string $value User-selected reminder value
* @param bool $allowPositive Whether positive trigger offsets are allowed
* @return bool
*/
private function isValidReminderValue(string $value, bool $allowPositive = false): bool {
if ($value === 'none') {
return true;
}

$options = $allowPositive ? [] : ['options' => ['max_range' => 0]];

return filter_var($value, FILTER_VALIDATE_INT, $options) !== false;
}

/**
* sets defaultReminder for user
*
* @param string $value User-selected option for default_reminder in agenda view
* @return JSONResponse
*/
private function setDefaultReminder(string $value):JSONResponse {
if ($value !== 'none'
&& filter_var($value, FILTER_VALIDATE_INT,
['options' => ['max_range' => 0]]) === false) {
if (!$this->isValidReminderValue($value)) {
return new JSONResponse([], Http::STATUS_UNPROCESSABLE_ENTITY);
}

Expand All @@ -372,4 +391,54 @@ private function setDefaultReminder(string $value):JSONResponse {

return new JSONResponse();
}

/**
* sets defaultReminderPartDay for user
*
* @param string $value User-selected option for the part-day default reminder
* @return JSONResponse
*/
private function setDefaultReminderPartDay(string $value):JSONResponse {
if (!$this->isValidReminderValue($value)) {
return new JSONResponse([], Http::STATUS_UNPROCESSABLE_ENTITY);
}

try {
$this->config->setUserValue(
$this->userId,
$this->appName,
'defaultReminderPartDay',
$value
);
} catch (\Exception $e) {
return new JSONResponse([], Http::STATUS_INTERNAL_SERVER_ERROR);
}

return new JSONResponse();
}

/**
* sets defaultReminderFullDay for user
*
* @param string $value User-selected option for the full-day default reminder
* @return JSONResponse
*/
private function setDefaultReminderFullDay(string $value):JSONResponse {
if (!$this->isValidReminderValue($value, true)) {
return new JSONResponse([], Http::STATUS_UNPROCESSABLE_ENTITY);
}

try {
$this->config->setUserValue(
$this->userId,
$this->appName,
'defaultReminderFullDay',
$value
);
} catch (\Exception $e) {
return new JSONResponse([], Http::STATUS_INTERNAL_SERVER_ERROR);
}

return new JSONResponse();
}
}
4 changes: 4 additions & 0 deletions lib/Service/CalendarInitialStateService.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ public function run(): void {
$attachmentsFolder = $this->config->getUserValue($this->userId, 'dav', 'attachmentsFolder', '/Calendar');
$slotDuration = $this->config->getUserValue($this->userId, $this->appName, 'slotDuration', $defaultSlotDuration);
$defaultReminder = $this->config->getUserValue($this->userId, $this->appName, 'defaultReminder', $defaultDefaultReminder);
$defaultReminderPartDay = $this->config->getUserValue($this->userId, $this->appName, 'defaultReminderPartDay', $defaultReminder);
$defaultReminderFullDay = $this->config->getUserValue($this->userId, $this->appName, 'defaultReminderFullDay', $defaultReminder);
$showTasks = $this->config->getUserValue($this->userId, $this->appName, 'showTasks', $defaultShowTasks) === 'yes';
$tasksSidebar = $this->config->getUserValue($this->userId, $this->appName, 'tasksSidebar', $defaultTasksSidebar) === 'yes';
$hideEventExport = $this->config->getAppValue($this->appName, 'hideEventExport', 'no') === 'yes';
Expand Down Expand Up @@ -101,6 +103,8 @@ public function run(): void {
$this->initialStateService->provideInitialState('attachments_folder', $attachmentsFolder);
$this->initialStateService->provideInitialState('slot_duration', $slotDuration);
$this->initialStateService->provideInitialState('default_reminder', $defaultReminder);
$this->initialStateService->provideInitialState('default_reminder_part_day', $defaultReminderPartDay);
$this->initialStateService->provideInitialState('default_reminder_full_day', $defaultReminderFullDay);
$this->initialStateService->provideInitialState('show_tasks', $showTasks);
$this->initialStateService->provideInitialState('tasks_sidebar', $tasksSidebar);
$this->initialStateService->provideInitialState('tasks_enabled', $tasksEnabled);
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"@nextcloud/axios": "^2.5.2",
"@nextcloud/calendar-availability-vue": "^3.0.0",
"@nextcloud/calendar-js": "^8.1.6",
"@nextcloud/cdav-library": "^2.2.0",
"@nextcloud/cdav-library": "^2.3.0",
"@nextcloud/dialogs": "^7.3.0",
"@nextcloud/event-bus": "^3.3.3",
"@nextcloud/initial-state": "^3.0.0",
Expand Down
96 changes: 74 additions & 22 deletions src/components/AppNavigation/EditCalendarModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,32 @@
</template>
<template v-if="!calendar.isSharedWithMe && isAfterVersion">
<div class="edit-calendar-modal__default-alarm">
<label for="default-alarm-select" class="edit-calendar-modal__default-alarm__label">
{{ $t('calendar', 'Default reminder') }}
<label for="default-alarm-partday-select" class="edit-calendar-modal__default-alarm__label">
{{ $t('calendar', 'Default reminder for part-day events') }}
</label>
<NcSelect
id="default-alarm-select"
v-model="selectedDefaultAlarm"
:options="defaultAlarmOptions"
id="default-alarm-partday-select"
v-model="selectedDefaultAlarmPartDay"
:options="defaultAlarmPartDayOptions"
:clearable="false"
:placeholder="$t('calendar', 'Select default reminder')"
class="edit-calendar-modal__default-alarm__select"
@update:modelValue="defaultAlarmChanged = true" />
</div>
<div class="edit-calendar-modal__default-alarm">
<label for="default-alarm-fullday-select" class="edit-calendar-modal__default-alarm__label">
{{ $t('calendar', 'Default reminder for full-day events') }}
</label>
<NcSelect
id="default-alarm-fullday-select"
v-model="selectedDefaultAlarmFullDay"
:options="defaultAlarmFullDayOptions"
:clearable="false"
:placeholder="$t('calendar', 'Select default reminder')"
class="edit-calendar-modal__default-alarm__select"
@update:modelValue="defaultAlarmChanged = true" />
<p class="edit-calendar-modal__default-alarm__hint">
{{ $t('calendar', 'This reminder will be automatically added to all new events created in this calendar') }}
{{ $t('calendar', 'These reminders will be automatically added to new events created in this calendar') }}
</p>
</div>
</template>
Expand Down Expand Up @@ -159,7 +172,8 @@ export default {
isTransparent: false,
calendarName: undefined,
calendarNameChanged: false,
selectedDefaultAlarm: null,
selectedDefaultAlarmPartDay: null,
selectedDefaultAlarmFullDay: null,
defaultAlarmChanged: false,
}
},
Expand Down Expand Up @@ -251,11 +265,11 @@ export default {
},

/**
* Get the default alarm options for the select dropdown
* Get the default alarm options for part-day (timed) events
*
* @return {Array}
*/
defaultAlarmOptions() {
defaultAlarmPartDayOptions() {
const settingsStore = useSettingsStore()
const currentUserTimezone = settingsStore.getResolvedTimezone
const locale = settingsStore.momentLocale
Expand All @@ -267,7 +281,6 @@ export default {
},
]

// Add standard alarm options for timed events
const alarms = getDefaultAlarms(false)
for (const alarm of alarms) {
const alarmObject = this.getAlarmObjectFromTriggerTime(alarm)
Expand All @@ -280,6 +293,35 @@ export default {
return options
},

/**
* Get the default alarm options for full-day (all-day) events
*
* @return {Array}
*/
defaultAlarmFullDayOptions() {
const settingsStore = useSettingsStore()
const currentUserTimezone = settingsStore.getResolvedTimezone
const locale = settingsStore.momentLocale

const options = [
{
label: this.$t('calendar', 'None'),
value: null,
},
]

const alarms = getDefaultAlarms(true)
for (const alarm of alarms) {
const alarmObject = this.getAlarmObjectFromTriggerTime(alarm)
options.push({
label: alarmFormat(alarmObject, true, currentUserTimezone, locale),
value: alarm,
})
}

return options
},

/**
* Whether the default alarm feature is supported (Nextcloud 34+)
*
Expand All @@ -302,13 +344,22 @@ export default {
this.calendarColorChanged = false
this.isTransparent = calendar.transparency === 'transparent'

// Initialize default alarm
if (calendar.defaultAlarm === null) {
this.selectedDefaultAlarm = this.defaultAlarmOptions[0]
// Initialize default alarm for part-day events
if (calendar.defaultAlarmPartDay === null) {
this.selectedDefaultAlarmPartDay = this.defaultAlarmPartDayOptions[0]
} else {
const value = parseInt(calendar.defaultAlarmPartDay)
const option = this.defaultAlarmPartDayOptions.find((opt) => opt.value === value)
this.selectedDefaultAlarmPartDay = option || this.defaultAlarmPartDayOptions[0]
}

// Initialize default alarm for full-day events
if (calendar.defaultAlarmFullDay === null) {
this.selectedDefaultAlarmFullDay = this.defaultAlarmFullDayOptions[0]
} else {
const value = parseInt(calendar.defaultAlarm)
const option = this.defaultAlarmOptions.find((opt) => opt.value === value)
this.selectedDefaultAlarm = option || this.defaultAlarmOptions[0]
const value = parseInt(calendar.defaultAlarmFullDay)
const option = this.defaultAlarmFullDayOptions.find((opt) => opt.value === value)
this.selectedDefaultAlarmFullDay = option || this.defaultAlarmFullDayOptions[0]
}
this.defaultAlarmChanged = false
},
Expand Down Expand Up @@ -377,19 +428,20 @@ export default {
},

/**
* Save the calendar default alarm.
* Save the calendar default alarms.
*/
async saveDefaultAlarm() {
try {
const defaultAlarmValue = this.selectedDefaultAlarm ? this.selectedDefaultAlarm.value : null
await this.calendarsStore.changeCalendarDefaultAlarm({
const pdayValue = this.selectedDefaultAlarmPartDay ? this.selectedDefaultAlarmPartDay.value : null
const fdayValue = this.selectedDefaultAlarmFullDay ? this.selectedDefaultAlarmFullDay.value : null
await this.calendarsStore.changeCalendarDefaultAlarms({
calendar: this.calendar,
defaultAlarm: defaultAlarmValue,
defaultAlarmPartDay: pdayValue,
defaultAlarmFullDay: fdayValue,
})
} catch (error) {
logger.error('Failed to save calendar default alarm', {
logger.error('Failed to save calendar default alarms', {
calendar: this.calendar,
defaultAlarm: this.selectedDefaultAlarm,
})
throw error
}
Expand Down
Loading
Loading