diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php --- a/plugins/calendar/calendar.php +++ b/plugins/calendar/calendar.php @@ -788,6 +788,22 @@ case "subscribe": if (!$this->driver->subscribe_calendar($cal)) $this->rc->output->show_message($this->gettext('errorsaving'), 'error'); + else { + $calendars = $this->driver->list_calendars(); + $calendar = $calendars[$cal['id']]; + + // find parent folder and check if it's a "user calendar" + // if it's also activated we need to refresh it (#5340) + while ($calendar['parent']) { + if (isset($calendars[$calendar['parent']])) + $calendar = $calendars[$calendar['parent']]; + else + break; + } + + if ($calendar['id'] != $cal['id'] && $calendar['active'] && $calendar['group'] == "other user") + $this->rc->output->command('plugin.refresh_source', $calendar['id']); + } return; case "search": $results = array(); diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js --- a/plugins/calendar/calendar_ui.js +++ b/plugins/calendar/calendar_ui.js @@ -2958,6 +2958,15 @@ return false; }; + this.calendar_refresh_source = function(id) + { + // got race-conditions fc.currentFetchID when using refetchEvents, + // so we remove and add the source instead + // fc.fullCalendar('refetchEvents', me.calendars[id]); + fc.fullCalendar('removeEventSource', me.calendars[id]); + fc.fullCalendar('addEventSource', me.calendars[id]); + }; + this.calendar_destroy_source = function(id) { var delete_ids = []; @@ -4235,6 +4244,7 @@ rcmail.register_command('add-resource', function(){ cal.add_resource2event(); }, false); // register callback commands + rcmail.addEventListener('plugin.refresh_source', function(data) { cal.calendar_refresh_source(data); }); rcmail.addEventListener('plugin.destroy_source', function(p){ cal.calendar_destroy_source(p.id); }); rcmail.addEventListener('plugin.unlock_saving', function(p){ cal.unlock_saving(); }); rcmail.addEventListener('plugin.refresh_calendar', function(p){ cal.refresh(p); }); diff --git a/plugins/calendar/drivers/kolab/kolab_user_calendar.php b/plugins/calendar/drivers/kolab/kolab_user_calendar.php --- a/plugins/calendar/drivers/kolab/kolab_user_calendar.php +++ b/plugins/calendar/drivers/kolab/kolab_user_calendar.php @@ -223,11 +223,11 @@ } } - // aggregate all calendar folders the user shares (but are not subscribed) - foreach (kolab_storage::list_user_folders($this->userdata, 'event', false) as $foldername) { + // aggregate all calendar folders the user shares (but are not activated) + foreach (kolab_storage::list_user_folders($this->userdata, 'event', 2) as $foldername) { $cal = new kolab_calendar($foldername, $this->cal); foreach ($cal->list_events($start, $end, $search, 1) as $event) { - $this->events[$event['id']] = $event; + $this->events[$event['id'] ?: $event['uid']] = $event; $this->timeindex[$this->time_key($event)] = $event['id']; } } diff --git a/plugins/libkolab/lib/kolab_storage.php b/plugins/libkolab/lib/kolab_storage.php --- a/plugins/libkolab/lib/kolab_storage.php +++ b/plugins/libkolab/lib/kolab_storage.php @@ -825,11 +825,7 @@ if (!$filter) { // Get ALL folders list, standard way if ($subscribed) { - $folders = self::$imap->list_folders_subscribed($root, $mbox); - // add temporarily subscribed folders - if (self::$with_tempsubs && is_array($_SESSION['kolab_subscribed_folders'])) { - $folders = array_unique(array_merge($folders, $_SESSION['kolab_subscribed_folders'])); - } + $folders = self::_imap_list_subscribed($root, $mbox); } else { $folders = self::_imap_list_folders($root, $mbox); @@ -866,12 +862,7 @@ // Get folders list if ($subscribed) { - $folders = self::$imap->list_folders_subscribed($root, $mbox); - - // add temporarily subscribed folders - if (self::$with_tempsubs && is_array($_SESSION['kolab_subscribed_folders'])) { - $folders = array_unique(array_merge($folders, $_SESSION['kolab_subscribed_folders'])); - } + $folders = self::_imap_list_subscribed($root, $mbox); } else { $folders = self::_imap_list_folders($root, $mbox); @@ -934,6 +925,21 @@ return $folders; } + /** + * Wrapper for rcube_imap::list_folders_subscribed() + * with support for temporarily subscribed folders + */ + protected static function _imap_list_subscribed($root, $mbox) + { + $folders = self::$imap->list_folders_subscribed($root, $mbox); + + // add temporarily subscribed folders + if (self::$with_tempsubs && is_array($_SESSION['kolab_subscribed_folders'])) { + $folders = array_unique(array_merge($folders, $_SESSION['kolab_subscribed_folders'])); + } + + return $folders; + } /** * Search for shared or otherwise not listed groupware folders the user has access @@ -1538,12 +1544,12 @@ * * @param array User entry from LDAP * @param string Data type to list folders for (contact,event,task,journal,file,note,mail,configuration) - * @param boolean Return subscribed folders only (null to use configured subscription mode) + * @param int 1 - subscribed folders only, 0 - all folders, 2 - all non-active * @param array Will be filled with folder-types data * * @return array List of folders */ - public static function list_user_folders($user, $type, $subscribed = null, &$folderdata = array()) + public static function list_user_folders($user, $type, $subscribed = 0, &$folderdata = array()) { self::setup(); @@ -1554,9 +1560,19 @@ if (!empty($user[$user_attrib])) { list($mbox) = explode('@', $user[$user_attrib]); - $delimiter = self::$imap->get_hierarchy_delimiter(); - $other_ns = self::namespace_root('other'); - $folders = self::list_folders($other_ns . $mbox . $delimiter, '*', $type, $subscribed, $folderdata); + $delimiter = self::$imap->get_hierarchy_delimiter(); + $other_ns = self::namespace_root('other'); + $prefix = $other_ns . $mbox . $delimiter; + $subscribed = (int) $subscribed; + $subs = $subscribed < 2 ? (bool) $subscribed : false; + $folders = self::list_folders($prefix, '*', $type, $subs, $folderdata); + + if ($subscribed === 2 && !empty($folders)) { + $active = self::get_states(); + if (!empty($active)) { + $folders = array_diff($folders, $active); + } + } } return $folders; diff --git a/plugins/tasklist/tasklist.php b/plugins/tasklist/tasklist.php --- a/plugins/tasklist/tasklist.php +++ b/plugins/tasklist/tasklist.php @@ -1352,14 +1352,63 @@ else if ($rec['date'] < $today) $mask |= self::FILTER_MASK_OVERDUE; - if ($duedate <= $today || ($rec['startdate'] && $start <= $today)) - $mask |= self::FILTER_MASK_TODAY; - if ($duedate <= $tomorrow || ($rec['startdate'] && $start <= $tomorrow)) - $mask |= self::FILTER_MASK_TOMORROW; - if (($start > $tomorrow && $start <= $weeklimit) || ($duedate > $tomorrow && $duedate <= $weeklimit)) - $mask |= self::FILTER_MASK_WEEK; - else if ($start > $weeklimit || ($rec['date'] && $duedate > $weeklimit)) - $mask |= self::FILTER_MASK_LATER; + if (empty($rec['recurrence']) || $duedate < $today || $start > $weeklimit) { + if ($duedate <= $today || ($rec['startdate'] && $start <= $today)) + $mask |= self::FILTER_MASK_TODAY; + if ($duedate <= $tomorrow || ($rec['startdate'] && $start <= $tomorrow)) + $mask |= self::FILTER_MASK_TOMORROW; + if (($start > $tomorrow && $start <= $weeklimit) || ($duedate > $tomorrow && $duedate <= $weeklimit)) + $mask |= self::FILTER_MASK_WEEK; + else if ($start > $weeklimit || $duedate > $weeklimit) + $mask |= self::FILTER_MASK_LATER; + } + else if ($rec['startdate'] || $rec['date']) { + $date = new DateTime($rec['startdate'] ?: $rec['date'], $this->timezone); + + // set safe recurrence start + while ($date->format('Y-m-d') >= $today) { + switch ($rec['recurrence']['FREQ']) { + case 'DAILY': + $date = clone $today_date; + $date->sub(new DateInterval('P1D')); + break; + case 'WEEKLY': $date->sub(new DateInterval('P7D')); break; + case 'MONTHLY': $date->sub(new DateInterval('P1M')); break; + case 'YEARLY': $date->sub(new DateInterval('P1Y')); break; + default; break 2; + } + } + + $date->_dateonly = true; + + $engine = libcalendaring::get_recurrence(); + $engine->init($rec['recurrence'], $date); + + // check task occurrences (stop next week) + // FIXME: is there a faster way of doing this? + while ($date = $engine->next()) { + $date = $date->format('Y-m-d'); + + // break iteration asap + if ($date > $duedate || ($mask & self::FILTER_MASK_LATER)) { + break; + } + + if ($date == $today) { + $mask |= self::FILTER_MASK_TODAY; + } + else if ($date == $tomorrow) { + $mask |= self::FILTER_MASK_TOMORROW; + } + else if ($date > $tomorrow && $date <= $weeklimit) { + $mask |= self::FILTER_MASK_WEEK; + } + else if ($date > $weeklimit) { + $mask |= self::FILTER_MASK_LATER; + break; + } + } + } // add masks for assigned tasks if ($this->is_organizer($rec) && !empty($rec['attendees']) && $this->is_attendee($rec) === false)