From cbd2e4c69549a8f15752abc58a23ff3f659b5191 Mon Sep 17 00:00:00 2001 From: ralf Date: Sat, 29 Jun 2024 11:52:34 +0200 Subject: [PATCH] * Calendar: allow to create recurring events with explicit recurrences --- calendar/inc/class.calendar_bo.inc.php | 2 +- calendar/inc/class.calendar_rrule.inc.php | 25 ++++++++++++--------- calendar/inc/class.calendar_uiforms.inc.php | 25 ++++++++++++++++++++- calendar/js/app.ts | 25 ++++++++++++++++----- calendar/lang/egw_de.lang | 5 ++--- calendar/lang/egw_en.lang | 3 ++- calendar/templates/default/edit.xet | 10 +++++++-- 7 files changed, 71 insertions(+), 24 deletions(-) diff --git a/calendar/inc/class.calendar_bo.inc.php b/calendar/inc/class.calendar_bo.inc.php index 513b6bffed..359c5a37f5 100644 --- a/calendar/inc/class.calendar_bo.inc.php +++ b/calendar/inc/class.calendar_bo.inc.php @@ -129,7 +129,7 @@ class calendar_bo MCAL_RECUR_MONTHLY_WDAY => 'Monthly (by day)', MCAL_RECUR_MONTHLY_MDAY => 'Monthly (by date)', MCAL_RECUR_YEARLY => 'Yearly', - MCAL_RECUR_RDATE/*calendar_rrule::PERIOD*/ => 'Explicit dates', + MCAL_RECUR_RDATE/*calendar_rrule::PERIOD*/ => 'Explicit recurrences', ); /** * @var array recur_days translates MCAL recur-days to verbose labels diff --git a/calendar/inc/class.calendar_rrule.inc.php b/calendar/inc/class.calendar_rrule.inc.php index cce2ec41ff..4c9d5eacd2 100644 --- a/calendar/inc/class.calendar_rrule.inc.php +++ b/calendar/inc/class.calendar_rrule.inc.php @@ -82,7 +82,7 @@ class calendar_rrule implements Iterator self::MONTHLY_WDAY => 'Monthly (by day)', self::MONTHLY_MDAY => 'Monthly (by date)', self::YEARLY => 'Yearly', - self::PERIOD => 'By date or period' + self::PERIOD => 'By date or period' ); /** @@ -316,6 +316,15 @@ class calendar_rrule implements Iterator } $this->interval = (int)$interval; + if ($exceptions) + { + foreach($exceptions as $exception) + { + $exception->setTimezone($this->time->getTimezone()); + $this->exceptions[] = $exception->format('Ymd'); + } + $this->exceptions_objs = $exceptions; + } $this->enddate = $enddate; if($type == self::PERIOD) { @@ -324,6 +333,11 @@ class calendar_rrule implements Iterator $rdate->setTimezone($this->time->getTimezone()); $this->period[] = $rdate; } + // if startdate is neither in the rdates, nor the exceptions --> prepend it to rdates + if (!in_array($this->time, $this->period) && !in_array($this->time, $this->exceptions_objs)) + { + array_unshift($this->period, clone($this->time)); + } $enddate = clone(count($this->period) ? end($this->period) : $this->time); // Make sure to include the last date as valid $enddate->modify('+1 second'); @@ -353,15 +367,6 @@ class calendar_rrule implements Iterator { $this->weekdays = self::getWeekday($this->time); } - if ($exceptions) - { - foreach($exceptions as $exception) - { - $exception->setTimezone($this->time->getTimezone()); - $this->exceptions[] = $exception->format('Ymd'); - } - $this->exceptions_objs = $exceptions; - } } /** diff --git a/calendar/inc/class.calendar_uiforms.inc.php b/calendar/inc/class.calendar_uiforms.inc.php index 96dc68f041..9505b55b4f 100644 --- a/calendar/inc/class.calendar_uiforms.inc.php +++ b/calendar/inc/class.calendar_uiforms.inc.php @@ -336,6 +336,18 @@ class calendar_uiforms extends calendar_ui } $update_type = 'edit'; } + if (!empty($content['recur_rdates']['delete_rdate'])) + { + $date = key($content['recur_rdates']['delete_rdate']); + // eT2 converts time to + if (!is_numeric($date)) $date = Api\DateTime::to(str_replace('Z', '', $date), 'ts'); + unset($content['recur_rdates']['delete_rdate']); + if (($key = array_search($date, $content['recur_rdates'])) !== false) + { + unset($content['recur_rdates'][$key]); + $content['recur_rdates'] = array_values($content['recur_rdates']); + } + } // delete an alarm if (!empty($content['alarm']['delete_alarm'])) { @@ -667,10 +679,21 @@ class calendar_uiforms extends calendar_ui switch((string)$button) { - case 'exception': // create an exception in a recuring event + case 'exception': // create an exception in a recurring event $msg = $this->_create_exception($event,$preserv); break; + case 'add_rdate': + if (!empty($event['recur_rdate']) && array_search($event['recur_rdate'], (array)$event['recur_rdates']) === false) + { + $event['recur_rdates'][] = $event['recur_rdate']; + usort($event['recur_rdates'], static function($a, $b) { + return $a <=> $b; + }); + $msg = lang('Added recurrence on %1.', Api\DateTime::to($event['recur_rdate'])); + } + break; + case 'edit': // Going from add dialog to full edit dialog unset($preserv['template']); diff --git a/calendar/js/app.ts b/calendar/js/app.ts index e610277886..e2660c7265 100644 --- a/calendar/js/app.ts +++ b/calendar/js/app.ts @@ -1403,17 +1403,24 @@ export class CalendarApp extends EgwApp } /** - * Function for disabling the recur_data multiselect box + * Function for disabling the recur_data multiselect box and add_rdate hbox * */ check_recur_type() { - var recurType = this.et2.getWidgetById('recur_type'); - var recurData = this.et2.getWidgetById('recur_data'); + const recurType = this.et2.getWidgetById('recur_type'); + const recurData = this.et2.getWidgetById('recur_data'); + const addRdate = this.et2.getWidgetById('button[add_rdate]'); + const recurRdate = this.et2.getWidgetById('recur_rdate'); if(recurType && recurData) { - recurData.set_disabled(recurType.get_value() != 2 && recurType.get_value() != 4); + recurData.set_disabled(recurType.value != 2 && recurType.value != 4); + } + if (recurType && addRdate && recurRdate) + { + addRdate.set_disabled(recurType.value != 9); + recurRdate.set_disabled(recurType.value != 9); } } @@ -1435,11 +1442,18 @@ export class CalendarApp extends EgwApp // Update recurring date limit, if not set it can't be before start if(widget) { - var recur_end = widget.getRoot().getWidgetById('recur_enddate'); + const recur_end = widget.getRoot().getWidgetById('recur_enddate'); if(recur_end && recur_end.getValue && !recur_end.value) { recur_end.set_min(widget.value); } + // update recur_rdate with start (specially time) and set start as minimum + const recur_rdate = widget.getRoot().getWidgetById('recur_rdate'); + if (recur_rdate) + { + recur_rdate.set_min(widget.value); + recur_rdate.value = widget.value; + } // Update end date, min duration is 1 minute let end = widget.getRoot().getWidgetById('end'); @@ -1453,7 +1467,6 @@ export class CalendarApp extends EgwApp } // Update currently selected alarm time this.alarm_custom_date(); - } /** diff --git a/calendar/lang/egw_de.lang b/calendar/lang/egw_de.lang index 80c23634c7..c0ae9cb0b5 100644 --- a/calendar/lang/egw_de.lang +++ b/calendar/lang/egw_de.lang @@ -32,6 +32,7 @@ add current view as favorite calendar de Ansicht als Favorit zufügen add new alarm calendar de Neuen Alarm erstellen add new event calendar de Einen neuen Termin hinzufügen add new participants or resource calendar de Neue(n) Teilnehmer oder Ressource auswählen +add recurrence calendar de Wiederholung hinzufügen add timesheet entry calendar de Stundenzettel hinzufügen added calendar de Neuer Termin added by synchronization calendar de Durch Synchronisation hinzugefügt @@ -247,7 +248,7 @@ exclude weekend calendar de Wochenende ausschließen execute a further action for this entry calendar de Führt einen weiteren Befehl für diesen Eintrag aus existing links calendar de Bestehende Verknüpfungen exists calendar de Existiert -explicit dates calendar de Explizite Termine +explicit recurrences calendar de Explizite Wiederholungen export definition to use for nextmatch export calendar de Export Profil der Listenansicht (Disketten Symbol) exports events from your calendar in ical format. calendar de Exportiert Termine im iCal-Format exports events from your calendar into a csv file. calendar de Exportiert Termine im CSV-Format @@ -524,8 +525,6 @@ search string for the events calendar de Suchtext für Termine select a %1 calendar de %1 auswählen select a color for this calendar calendar de Wählen Sie eine Farbe für diesen Kalender select a time calendar de Eine Zeit auswählen -select an action calendar de Befehl auswählen -select an action... calendar de Aktion auswählen ... select multiple contacts for a further action calendar de Mehrere Adressen für weiteren Befehl auswählen select resources calendar de Ressourcen auswählen select whether you want the participant stati reset to unknown, if an event is shifted later on. calendar de Wählen Sie aus, in welchem Fall der Teilnehmerstatus von Teilnehmern zurückgesetzt werden soll, wenn ein Termin verschoben wird. Der Teilnehmerstatus von Externen wird immer zurückgesetzt! diff --git a/calendar/lang/egw_en.lang b/calendar/lang/egw_en.lang index b1ba01c266..2da1cc5335 100644 --- a/calendar/lang/egw_en.lang +++ b/calendar/lang/egw_en.lang @@ -32,6 +32,7 @@ add current view as favorite calendar en Add current view as favorite add new alarm calendar en Add new alarm add new event calendar en Add new appointment add new participants or resource calendar en Add new participants or resource +add recurrence calendar en Add recurrence add timesheet entry calendar en Add timesheet entry added calendar en Added added by synchronization calendar en Added by synchronization @@ -247,7 +248,7 @@ exclude weekend calendar en Exclude Weekend execute a further action for this entry calendar en Execute a further action for this entry existing links calendar en Existing links exists calendar en Exists -explicit dates calendar en Explicit dates +explicit recurrences calendar en Explicit recurrences export definition to use for nextmatch export calendar en Export definition to use for nextmatch export exports events from your calendar in ical format. calendar en Exports events from your calendar in iCal format. exports events from your calendar into a csv file. calendar en Exports events from your calendar into a CSV file. diff --git a/calendar/templates/default/edit.xet b/calendar/templates/default/edit.xet index 7e99d8ddbf..3f9838e982 100644 --- a/calendar/templates/default/edit.xet +++ b/calendar/templates/default/edit.xet @@ -123,16 +123,22 @@ - + + + + + + + @@ -147,7 +153,7 @@ - +