@@ -456,7 +465,7 @@ endforeach;
aria-label="Uhrzeit"
aria-describedby="Uhrzeit">
@@ -728,7 +816,6 @@ endforeach;
endforeach; ?>
-
diff --git a/application/Calendar/CalendarController.php b/application/Calendar/CalendarController.php
index cade4b5d5..8ec0c586d 100644
--- a/application/Calendar/CalendarController.php
+++ b/application/Calendar/CalendarController.php
@@ -109,7 +109,10 @@ class CalendarController extends mfBaseController
die();
case "insertCalendarEvent":
$r = $this->request;
- $calendarEvents = CalendarModel::insertCalendarEvent($r, $this->me);
+ if ($r->rruleData) {
+ $rrules=json_encode($this->generateGraphRecurrence($r->rruleData, $r->start));
+ }
+ $calendarEvents = CalendarModel::insertCalendarEvent($r, $this->me,$rrules);
if ($r->customer_info_check) {
if ($r->customer_info_type == 1) {
$body = $r->customer_info_text;
@@ -389,6 +392,86 @@ class CalendarController extends mfBaseController
die();
}
+ private function generateGraphRecurrence($data, $startDate) {
+ $graph = [
+ 'pattern' => [],
+ 'range' => []
+ ];
+ $freq = strtoupper($data['rrule_frequency']);
+ switch ($freq) {
+ case 'DAILY':
+ $graph['pattern']['type'] = 'daily';
+ break;
+ case 'WEEKLY':
+ $graph['pattern']['type'] = 'weekly';
+ break;
+ case 'MONTHLY':
+ if (!empty($data['monthly_type']) && $data['monthly_type'] === 'BYMONTHDAY') {
+ $graph['pattern']['type'] = 'absoluteMonthly';
+ $graph['pattern']['dayOfMonth'] = intval($data['rrule_bymonthday']);
+ } elseif (!empty($data['monthly_type']) && $data['monthly_type'] === 'BYSETPOS') {
+ $graph['pattern']['type'] = 'relativeMonthly';
+ // Mapping: 1 => first, 2 => second, 3 => third, 4 => fourth, -1 => last
+ $setpos = intval($data['rrule_setpos']);
+ $indexMapping = [
+ 1 => 'first',
+ 2 => 'second',
+ 3 => 'third',
+ 4 => 'fourth',
+ -1 => 'last'
+ ];
+ $graph['pattern']['index'] = isset($indexMapping[$setpos]) ? $indexMapping[$setpos] : 'first';
+ if (!empty($data['rrule_bynweekday'])) {
+ $graph['pattern']['daysOfWeek'] = [$this->convertDayToGraph(strtoupper($data['rrule_bynweekday']))];
+ }
+ }
+ break;
+ case 'YEARLY':
+ $graph['pattern']['type'] = 'absoluteYearly';
+ break;
+ default:
+ $graph['pattern']['type'] = 'daily';
+ }
+
+ $graph['pattern']['interval'] = 1;
+
+ if ($freq === 'WEEKLY' && !empty($data['rrule-byweekday'])) {
+ $days = is_array($data['rrule-byweekday']) ? $data['rrule-byweekday'] : [$data['rrule-byweekday']];
+ $graphDays = [];
+ foreach($days as $day) {
+ $graphDays[] = $this->convertDayToGraph(strtoupper($day));
+ }
+ $graph['pattern']['daysOfWeek'] = $graphDays;
+ }
+
+
+ $graph['range']['startDate'] = $startDate;
+ if (!empty($data['rrule_until'])) {
+ $graph['range']['endDate'] = $data['rrule_until'];
+ $graph['range']['type'] = 'endDate';
+ } elseif (!empty($data['rrule_count'])) {
+ $graph['range']['numberOfOccurrences'] = intval($data['rrule_count']);
+ $graph['range']['type'] = 'numbered';
+ } else {
+ $graph['range']['type'] = 'noEnd';
+ }
+ return $graph;
+ }
+
+
+ private function convertDayToGraph($dayAbbrev) {
+ $mapping = [
+ 'MO' => 'monday',
+ 'TU' => 'tuesday',
+ 'WE' => 'wednesday',
+ 'TH' => 'thursday',
+ 'FR' => 'friday',
+ 'SA' => 'saturday',
+ 'SU' => 'sunday'
+ ];
+ return isset($mapping[$dayAbbrev]) ? $mapping[$dayAbbrev] : $dayAbbrev;
+ }
+
protected function indexAction()
{
$this->layout()->setTemplate("Calendar/Index");
diff --git a/application/Calendar/CalendarModel.php b/application/Calendar/CalendarModel.php
index 32bf0e89a..3e247584e 100644
--- a/application/Calendar/CalendarModel.php
+++ b/application/Calendar/CalendarModel.php
@@ -445,13 +445,15 @@ WHERE `TimerecordingCategory`.`hourday`!='1' AND `TimerecordingCategory`.`hourda
}
$Allcalendar['Daniel Whoknows'] = 2;
$Allcalendar['Stefan Plaschg'] = 26;
- $res = $dbcal->select("cal_events", "id, uuid, calendar_id, user_id, start_time, end_time, timezone, all_day_event, name, description, location, repeat_end_time, reminder, ctime, mtime, muser_id, busy, status, resource_event_id, private, rrule, background, files_folder_id, read_only, category_id, exception_for_event_id, recurrence_id, is_organizer,event_type,customer,customer_info,customer_info_send,customer_info_reminder,busy,attendees,organizer,is_organizer,accepted", "1=1 AND id='" . $id . "' ORDER BY id");
+ $res = $dbcal->select("cal_events", "id, uuid, calendar_id, user_id, start_time, end_time, timezone, all_day_event, name, description, location, repeat_end_time, reminder, ctime, mtime, muser_id, busy, status, resource_event_id, private, rrule, background, files_folder_id, read_only, category_id, exception_for_event_id, recurrence_id, recurrence, is_organizer,event_type,customer,customer_info,customer_info_send,customer_info_reminder,busy,attendees,organizer,is_organizer,accepted", "1=1 AND id='" . $id . "' ORDER BY id");
if ($dbcal->num_rows($res)) {
$data = $dbcal->fetch_array($res);
-
-
+ if ($data['recurrence']) {
+ $recurrence = self::generateDataRecurrence(json_decode($data['recurrence'], true));
+ } else {
+ $recurrence = false;
+ }
$attachment = 0;
-
if ($data['all_day_event'] == 1) {
$starttime = date("Y-m-d", $data['start_time']);
$endtime = date("Y-m-d", $data['end_time']);
@@ -517,6 +519,7 @@ WHERE `TimerecordingCategory`.`hourday`!='1' AND `TimerecordingCategory`.`hourda
}
}
}
+
if (!$data['accepted'] && $data['busy'] == 1) {
$data['accepted']['ok'] = 1;
$data['accepted'] = json_encode($data['accepted']);
@@ -543,6 +546,7 @@ WHERE `TimerecordingCategory`.`hourday`!='1' AND `TimerecordingCategory`.`hourda
'attendees' => array('attendees' => json_encode($AttendeeArray)),
'organizer' => array('organizer' => $data['organizer']),
'accepted' => array('accepted' => $data['accepted']),
+ 'recurrence' => array('recurrence' => $recurrence),
'attachment' => array('attachment' => $attachment, 'order' => $attachment),
'attachments' => array('attachments' => $attachments, 'order' => $attachments)
);
@@ -814,7 +818,7 @@ WHERE `TimerecordingCategory`.`hourday`!='1' AND `TimerecordingCategory`.`hourda
$db->insert("TheTool_CalendarQueue", $data);
}
- public static function insertCalendarEvent($r, $me)
+ public static function insertCalendarEvent($r, $me, $rrules = "")
{
$description = ($r->description);
$attachments = ($r->attachments);
@@ -877,7 +881,7 @@ WHERE `TimerecordingCategory`.`hourday`!='1' AND `TimerecordingCategory`.`hourda
$customer_info_send = NULL;
}
- $dataarray = array("start_time" => $start, 'end_time' => $end, 'name' => $title, 'description' => $description, 'location' => $location, 'calendar_id' => $user_id, 'uuid' => "a5eb79b3-fca7-5378-a09e-" . rand(100000000000, 999999999999), 'user_id' => 1, 'timezone' => 'Europe/Amsterdam', 'all_day_event' => 0, 'repeat_end_time' => 0, 'reminder' => $reminder, 'ctime' => time(), 'cname' => $me->name, 'mtime' => time(), 'mname' => $me->name, 'user_id' => 1, 'busy' => $busy, 'status' => 'CONFIRMED', 'resource_event_id' => 0, 'private' => $privateflag, 'rrule' => '', 'background' => 'EBF1E2', 'files_folder_id' => 0, 'read_only' => 0,'categories'=>$categories, 'exception_for_event_id' => 0, 'recurrence_id' => 0, 'is_organizer' => 1, 'event_type' => $type, 'customer' => $customer, 'customer_info' => $customer_info, 'customer_info_send' => $customer_info_send, 'customer_info_reminder' => $customer_info_reminder_check);
+ $dataarray = array("start_time" => $start, 'end_time' => $end, 'name' => $title, 'description' => $description, 'location' => $location, 'calendar_id' => $user_id, 'uuid' => "a5eb79b3-fca7-5378-a09e-" . rand(100000000000, 999999999999), 'user_id' => 1, 'timezone' => 'Europe/Amsterdam', 'all_day_event' => 0, 'repeat_end_time' => 0, 'reminder' => $reminder, 'ctime' => time(), 'cname' => $me->name, 'mtime' => time(), 'mname' => $me->name, 'user_id' => 1, 'busy' => $busy, 'status' => 'CONFIRMED', 'resource_event_id' => 0, 'private' => $privateflag, 'rrule' => '', 'background' => 'EBF1E2', 'files_folder_id' => 0, 'read_only' => 0, 'categories' => $categories, 'exception_for_event_id' => 0, 'recurrence_id' => 0, 'is_organizer' => 1, 'event_type' => $type, 'customer' => $customer, 'customer_info' => $customer_info, 'customer_info_send' => $customer_info_send, 'customer_info_reminder' => $customer_info_reminder_check);
$db->insert("cal_events", $dataarray);
$event_id = $dataarray['uuid'];
@@ -898,6 +902,9 @@ WHERE `TimerecordingCategory`.`hourday`!='1' AND `TimerecordingCategory`.`hourda
if ($attendees)
$dataarray['attendees'] = $attendees;
$dataarray['attachments'] = $attachments;
+ if ($rrules) {
+ $dataarray['rrule'] = $rrules;
+ }
$json_data = json_encode($dataarray);
$data = [];
@@ -972,6 +979,87 @@ WHERE `TimerecordingCategory`.`hourday`!='1' AND `TimerecordingCategory`.`hourda
die();
}
+ public static function generateDataRecurrence($graph)
+ {
+ $data = [];
+ switch ($graph['pattern']['type']) {
+ case 'daily':
+ $data['rrule_frequency'] = 'DAILY';
+ break;
+ case 'weekly':
+ $data['rrule_frequency'] = 'WEEKLY';
+ if (!empty($graph['pattern']['daysOfWeek'])) {
+ $data['rrule_byweekday'] = array_map(function ($day) {
+ return self::convertGraphToDay($day);
+ }, $graph['pattern']['daysOfWeek']);
+ }
+ break;
+ case 'absoluteMonthly':
+ $data['rrule_frequency'] = 'MONTHLY';
+ $data['monthly_type'] = 'BYMONTHDAY';
+ $data['rrule_bymonthday'] = intval($graph['pattern']['dayOfMonth']);
+ break;
+ case 'relativeMonthly':
+ $data['rrule_frequency'] = 'MONTHLY';
+ $data['monthly_type'] = 'BYSETPOS';
+ $indexMapping = [
+ 'first' => 1,
+ 'second' => 2,
+ 'third' => 3,
+ 'fourth' => 4,
+ 'last' => -1
+ ];
+ $index = isset($graph['pattern']['index']) ? $graph['pattern']['index'] : 'first';
+ $data['rrule_setpos'] = isset($indexMapping[$index]) ? $indexMapping[$index] : 1;
+ if (!empty($graph['pattern']['daysOfWeek'])) {
+ $data['rrule_bynweekday'] = self::convertGraphToDay($graph['pattern']['daysOfWeek'][0]);
+ }
+ break;
+ case 'absoluteYearly':
+ $data['rrule_frequency'] = 'YEARLY';
+ break;
+ default:
+ $data['rrule_frequency'] = 'DAILY';
+ }
+
+ // Falls benötigt, kann der Start-Datum-Wert übernommen werden.
+ if (!empty($graph['range']['startDate'])) {
+ $data['startDate'] = $graph['range']['startDate'];
+ }
+
+ // Wiederherstellen des Endbereichs
+ if (!empty($graph['range']['type'])) {
+ switch ($graph['range']['type']) {
+ case 'endDate':
+ $data['rrule_until'] = $graph['range']['endDate'];
+ break;
+ case 'numbered':
+ $data['rrule_count'] = intval($graph['range']['numberOfOccurrences']);
+ break;
+ case 'noEnd':
+ break;
+ }
+ }
+
+ return $data;
+ }
+
+ public static function convertGraphToDay($graphDay)
+ {
+ $mapping = [
+ 'MONDAY' => 'MO',
+ 'TUESDAY' => 'TU',
+ 'WEDNESDAY' => 'WE',
+ 'THURSDAY' => 'TH',
+ 'FRIDAY' => 'FR',
+ 'SATURDAY' => 'SA',
+ 'SUNDAY' => 'SU'
+ ];
+
+ $graphDay = strtoupper($graphDay);
+ return isset($mapping[$graphDay]) ? $mapping[$graphDay] : $graphDay;
+ }
+
public static function create(array $data)
{
$model = new Calendar();
diff --git a/public/js/pages/Calendar/View.js b/public/js/pages/Calendar/View.js
index f943cbed1..cecdd60dd 100644
--- a/public/js/pages/Calendar/View.js
+++ b/public/js/pages/Calendar/View.js
@@ -554,6 +554,19 @@ document.addEventListener('DOMContentLoaded', function () {
$('#delete-event').hide();
$('#update-event').hide();
$('.show-attendee').hide();
+ $('#recurringCheck').prop('checked', true);
+ $('#recurringCheck').change();
+ if (data.data.recurrence)
+ {
+ $('#rrule-frequency').val(data.data.recurrence.recurrence.rrule_frequency);
+ $('#rrule-frequency').trigger('change');
+ $('#rrule-interval').val(data.data.recurrence.recurrence.rrule_interval);
+ $('#rrule-byweekday').val(data.data.recurrence.recurrence.rrule_byweekday);
+ $('#rrule-until').val(data.data.recurrence.recurrence.rrule_until);
+ $('#monthly-type').val(data.data.recurrence.recurrence.monthly_type);
+ $('#monthly-type').trigger('change');
+ $('#rrule-bymonthday').val(data.data.recurrence.recurrence.rrule_bymonthday);
+ }
}
@@ -914,9 +927,11 @@ if (typeof (EventSource) !== 'undefined') {
let rruleflag = false;
let cursorclass = '';
if (event.rrule) {
+ $('.calendar-check').eq(0).trigger('change');
rrule = event.rrule;
duration = event.duration;
rruleflag = true;
+ return;
}
if (event.calendar_id in calendarRights) {
if (calendarRights[event.calendar_id] == 'all') {
@@ -1190,7 +1205,15 @@ $(document).ready(function () {
$('#EventModal .is-require').each(function (index, value) {
$(this).removeClass('required');
});
-
+ $('#recurring-settings').hide();
+ $('#weekly-options').hide();
+ $('#monthly-type').val('BYMONTHDAY');
+ $('#rrule-frequency').val('');
+ $('#rrule-count').val('');
+ $('#rrule-until').val('');
+ $('#rrule-byweekday').val('');
+ $('#rrule-bymonthday').val('1');
+ $('#monthly-options').hide();
// $('.select2-multiple').select2();
$('#calendar-users').prop("disabled", false);
$("#calendar-users option").each(function () {
@@ -1298,6 +1321,27 @@ $(document).ready(function () {
users.push($(this).val());
});
+ if ($('#recurringCheck').is(':checked') && $('#rrule-frequency').val() !== '') {
+ var rruleData = {};
+ rruleData.rrule_frequency = $('#rrule-frequency').val();
+
+ if (rruleData.rrule_frequency === 'WEEKLY') {
+ // Mehrere Wochentage als Array
+ rruleData['rrule-byweekday'] = $('#rrule-byweekday').val();
+ } else if (rruleData.rrule_frequency === 'MONTHLY') {
+ rruleData.monthly_type = $('#monthly-type').val();
+ if (rruleData.monthly_type === 'BYMONTHDAY') {
+ rruleData.rrule_bymonthday = $('#rrule-bymonthday').val();
+ } else if (rruleData.monthly_type === 'BYSETPOS') {
+ rruleData.rrule_setpos = $('#rrule-setpos').val();
+ rruleData.rrule_bynweekday = $('#rrule-bynweekday').val();
+ }
+ }
+ // Optionale Felder
+ rruleData.rrule_count = $('#rrule-count').val();
+ rruleData.rrule_until = $('#rrule-until').val();
+ }
+
$.post(requestInsertUrl, {
start: start,
@@ -1312,6 +1356,7 @@ $(document).ready(function () {
attachments: attachments,
users: users,
privateflag: privateflag,
+ rruleData: rruleData,
attendees: $('#calendar-attendees').val(),
customer: customer,
customer_info_check: customer_info_check,
@@ -2416,15 +2461,55 @@ $(document).ready(function () {
;
});
}
-
-// $('.select-2').select2({
-// containerCssClass : 'meine-custom-dropdown', // Klasse für das Dropdown-Menü
-// containerCss : "wrap",
-// selectionCssClass: 'meine-custom-selection'
-// });
+
$(document).on('focusin', function (e) {
if ($(e.target).closest(".tox-tinymce, .tox-tinymce-aux, .moxman-window, .tam-assetmanager-root").length) {
e.stopImmediatePropagation();
}
});
+ $(document).ready(function () {
+ // Checkbox toggelt das RRule-Panel
+ $('#recurringCheck').on('change', function () {
+ if ($(this).is(':checked')) {
+ $('#recurring-settings').show();
+ $('#rrule-until').attr('min' , $('#start-date').val());
+ } else {
+ $('#recurring-settings').hide();
+ $('#weekly-options').hide();
+ $('#monthly-options').hide();
+ }
+ });
+
+ // Falls eine Frequenz gewählt wird, passende Felder (weekly/monthly) einblenden
+ $('#rrule-frequency').on('change', function () {
+ var freq = $(this).val();
+
+ // Alles erstmal verstecken
+ $('#weekly-options').hide();
+ $('#monthly-options').hide();
+
+ if (freq === 'WEEKLY') {
+ $('#weekly-options').show();
+ } else if (freq === 'MONTHLY') {
+ $('#monthly-options').show();
+ }
+ });
+
+ // Zeige/Verstecke im monatlichen Bereich "Tag des Monats" oder "X. Wochentag"
+ $('#monthly-type').on('change', function () {
+ var monthlyType = $(this).val();
+ if (monthlyType === 'BYMONTHDAY') {
+ $('#monthly-day-select').show();
+ $('#monthly-setpos-select').hide();
+ } else {
+ $('#monthly-day-select').hide();
+ $('#monthly-setpos-select').show();
+ }
+ });
+
+ // Du kannst bei Klick auf "Hinzufügen" oder "Speichern" dann aus den ausgewählten Werten
+ // dein RRule-String generieren (z.B. mit rrule.js oder manuell).
+
+ });
+
});