/** * returns next occurrence _ignoring exceptions_ or NULL if there is none/not computable * * NOTE: an ongoing event during $from [start, end[ is considered as next * NOTE: for previous events on ongoing event is considered as previous * * NOTE: computing the next occurrence of an open end rrule can be dangerous, as it might result * in a endless loop. Therefore we only make a limited number of attempts before giving up. * * @param Calendar_Model_Event $_event * @param Tinebase_Record_RecordSet $_exceptions * @param Tinebase_DateTime $_from * @param Int $_which * @return Calendar_Model_Event|NULL */ public static function computeNextOccurrence($_event, $_exceptions, $_from, $_which = 1) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' $from = ' . $_from->toString()); } if ($_which === 0 || $_event->dtstart >= $_from && $_event->dtend > $_from) { return $_event; } $freqMap = array(self::FREQ_DAILY => Tinebase_DateTime::MODIFIER_DAY, self::FREQ_WEEKLY => Tinebase_DateTime::MODIFIER_WEEK, self::FREQ_MONTHLY => Tinebase_DateTime::MODIFIER_MONTH, self::FREQ_YEARLY => Tinebase_DateTime::MODIFIER_YEAR); $rrule = new Calendar_Model_Rrule(NULL, TRUE); $rrule->setFromString($_event->rrule); $from = clone $_from; $until = clone $from; $interval = $_which * $rrule->interval; // we don't want to compute ourself $ownEvent = clone $_event; $ownEvent->setRecurId($_event->getId()); $exceptions = clone $_exceptions; $exceptions->addRecord($ownEvent); $recurSet = new Tinebase_Record_RecordSet('Calendar_Model_Event'); if ($_from->isEarlier($_event->dtstart)) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' from is ealier dtstart -> given event is next occurrence'); } return $_event; } $rangeDate = $_which > 0 ? $until : $from; if (!isset($freqMap[$rrule->freq])) { if (Tinebase_Core::isLogLevel(Zend_Log::ERR)) { Tinebase_Core::getLogger()->err(__METHOD__ . '::' . __LINE__ . ' Invalid RRULE:' . print_r($rrule->toArray(), true)); } throw new Calendar_Exception('Invalid freq in RRULE: ' . $rrule->freq); } $rangeDate->add($interval, $freqMap[$rrule->freq]); $attempts = 0; if ($_event->rrule_until instanceof DateTime && Tinebase_Core::isLogLevel(Zend_Log::TRACE)) { Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' Event rrule_until: ' . $_event->rrule_until->toString()); } while (TRUE) { if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) { Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' trying to find next occurrence from ' . $from->toString()); } if ($_event->rrule_until instanceof DateTime && $from->isLater($_event->rrule_until)) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' passed rrule_until -> no further occurrences'); } return NULL; } $until = $_event->rrule_until instanceof DateTime && $until->isLater($_event->rrule_until) ? clone $_event->rrule_until : $until; $recurSet->merge(self::computeRecurrenceSet($_event, $exceptions, $from, $until)); $attempts++; // NOTE: computeRecurrenceSet also returns events during $from in some cases, but we need // to events later than $from. $recurSet = $recurSet->filter(function ($event) use($from) { return $event->dtstart >= $from; }); if (count($recurSet) >= abs($_which)) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " found next occurrence after {$attempts} attempt(s)"); } break; } if ($attempts > count($exceptions) + 5) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " could not find the next occurrence after {$attempts} attempts, giving up"); } return NULL; } $from->add($interval, $freqMap[$rrule->freq]); $until->add($interval, $freqMap[$rrule->freq]); } $recurSet->sort('dtstart', $_which > 0 ? 'ASC' : 'DESC'); $nextOccurrence = $recurSet[abs($_which) - 1]; if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' $nextOccurrence->dtstart = ' . $nextOccurrence->dtstart->toString()); } return $nextOccurrence; }
/** * search tree nodes for search combo * * @param Tinebase_Model_Tree_Node_Filter $_filter * @param Tinebase_Record_Interface $_pagination * * @return Tinebase_Record_RecordSet of Tinebase_Model_Tree_Node */ protected function _searchNodesRecursive($_filter, $_pagination) { $files = new Tinebase_Record_RecordSet('Tinebase_Model_Tree_Node'); $ret = new Tinebase_Record_RecordSet('Tinebase_Model_Tree_Node'); $folders = $this->_getRootNodes(); $folders->merge($this->_getOtherUserNodes()); while ($folders->count()) { $node = $folders->getFirstRecord(); $filter = new Tinebase_Model_Tree_Node_Filter(array(array('field' => 'path', 'operator' => 'equals', 'value' => $node->path)), 'AND'); $result = $this->search($filter); $folders->merge($result->filter('type', Tinebase_Model_Tree_Node::TYPE_FOLDER)); if ($_filter->getFilter('query') && $_filter->getFilter('query')->getValue()) { $files->merge($result->filter('type', Tinebase_Model_Tree_Node::TYPE_FILE)->filter('name', '/^' . $_filter->getFilter('query')->getValue() . './i', true)); } else { $files->merge($result->filter('type', Tinebase_Model_Tree_Node::TYPE_FILE)); } $folders->removeRecord($node); } $this->_recursiveSearchTotalCount = $files->count(); $ret = $files->sortByPagination($_pagination)->limitByPagination($_pagination); return $ret; }
/** * generates path for the record * * @param Tinebase_Record_Abstract $record * @param boolean $rebuildRecursively * @return Tinebase_Record_RecordSet * * TODO what about acl? the account who creates the path probably does not see all relations ... */ public function generatePathForRecord(Tinebase_Record_Abstract $record, $rebuildRecursively = false) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Generate path for ' . get_class($record) . ' record with id ' . $record->getId()); } $recordController = Tinebase_Core::getApplicationInstance(get_class($record)); // if we rebuild recursively, dont do any tree operation, just rebuild the paths for the record and be done with it if (false === $rebuildRecursively) { // fetch full record + check acl $record = $recordController->get($record->getId()); $currentPaths = Tinebase_Record_Path::getInstance()->getPathsForRecords($record); } $newPaths = new Tinebase_Record_RecordSet('Tinebase_Model_Path'); // fetch all parent -> child relations and add to path $newPaths->merge($this->_getPathsOfRecord($record, $rebuildRecursively)); if (method_exists($recordController, 'generatePathForRecord')) { $newPaths->merge($recordController->generatePathForRecord($record)); } // if we rebuild recursively, dont do any tree operation, just rebuild the paths for the record and be done with it if (false === $rebuildRecursively) { //compare currentPaths with newPaths to find out if we need to make subtree updates //we should do this before the new paths of the current record have been persisted to DB! $currentShadowPathOffset = array(); foreach ($currentPaths as $offset => $path) { $currentShadowPathOffset[$path->shadow_path] = $offset; } $newShadowPathOffset = array(); foreach ($newPaths as $offset => $path) { $newShadowPathOffset[$path->shadow_path] = $offset; } $toDelete = array(); $anyOldOffset = null; foreach ($currentShadowPathOffset as $shadowPath => $offset) { $anyOldOffset = $offset; // parent path has been deleted! if (false === isset($newShadowPathOffset[$shadowPath])) { $toDelete[] = $shadowPath; continue; } $currentPath = $currentPaths[$offset]; $newPath = $newPaths[$newShadowPathOffset[$shadowPath]]; // path changed (a title was updated or similar) if ($currentPath->path !== $newPath->path) { // update ... set path = REPLACE(path, $currentPath->path, $newPath->path) where shadow_path LIKE '$shadowPath/%' $this->_backend->replacePathForShadowPathTree($shadowPath, $currentPath->path, $newPath->path); } unset($newShadowPathOffset[$shadowPath]); } // new parents if (count($newShadowPathOffset) > 0 && null !== $anyOldOffset) { $anyPath = $currentPaths[$anyOldOffset]; $newParents = array_values($newShadowPathOffset); foreach ($newParents as $newParentOffset) { $newParent = $newPaths[$newParentOffset]; // insert into ... select // REPLACE(path, $anyPath->path, $newParent->path) as path, // REPLACE(shadow_path, $anyPath->shadow_path, $newParent->shadow_path) as shadow_path // from ... where shadow_path LIKE '$anyPath->shadow_path/%' $this->_backend->copyTreeByShadowPath($anyPath->shadow_path, $newParent->path, $anyPath->path, $newParent->shadow_path, $anyPath->shadow_path); } } //execute deletes only now, important to make 100% sure "new parents" just above still has data to work on! foreach ($toDelete as $delete) { // delete where shadow_path LIKE '$delete/%' $this->_backend->deleteForShadowPathTree($delete); } } // delete current paths of this record $this->deletePathsForRecord($record); // recreate new paths of this record foreach ($newPaths as $path) { $this->_backend->create($path); } if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Created ' . count($newPaths) . ' paths.'); } if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' ' . print_r($newPaths->toArray(), true)); } return $newPaths; }
/** * get file list * * @param Filemanager_Model_DownloadLink $download * @param array $splittedPath * @param Tinebase_Model_Tree_Node $node * @return Tinebase_Record_RecordSet * * @todo move basePath calculation to view. In the controller we should start the path with $download-getId(). */ public function getFileList(Filemanager_Model_DownloadLink $download, $splittedPath, $node = null) { if ($node === null) { $node = $this->getNode($download, $splittedPath); } $basePath = '/download/show/' . $download->getId() . '/'; if (count($splittedPath) > 0) { $basePath .= implode('/', $splittedPath) . '/'; } $children = $this->_getTreeNodeBackend()->getChildren($node); foreach ($children as $child) { $child->path = $basePath . $child->name; } $files = new Tinebase_Record_RecordSet('Tinebase_Model_Tree_Node'); if (count($splittedPath) > 0) { $parent = $this->_getTreeNodeBackend()->get($node->parent_id); $parent->name = '..'; $parent->path = $basePath . '..'; $files->addRecord($parent); } $files->merge($children->filter('type', Tinebase_Model_Tree_Node::TYPE_FOLDER)->sort('name')); $files->merge($children->filter('type', Tinebase_Model_Tree_Node::TYPE_FILE)->sort('name')); return $files; }
public function compareCalendars($cal1, $cal2, $from, $until) { $matchingEvents = new Tinebase_Record_RecordSet('Calendar_Model_Event'); $changedEvents = new Tinebase_Record_RecordSet('Calendar_Model_Event'); $missingEventsInCal1 = new Tinebase_Record_RecordSet('Calendar_Model_Event'); $missingEventsInCal2 = new Tinebase_Record_RecordSet('Calendar_Model_Event'); $cal2EventIdsAlreadyProcessed = array(); while ($from->isEarlier($until)) { $endWeek = $from->getClone()->addWeek(1); if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) { Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Comparing period ' . $from . ' - ' . $endWeek); } // get all events from cal1+cal2 for the week $cal1Events = $this->_getEventsForPeriodAndCalendar($cal1, $from, $endWeek); $cal1EventsClone = clone $cal1Events; $cal2Events = $this->_getEventsForPeriodAndCalendar($cal2, $from, $endWeek); $cal2EventsClone = clone $cal2Events; $from->addWeek(1); if (count($cal1Events) == 0 && count($cal2Events) == 0) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' No events found'); } continue; } foreach ($cal1Events as $event) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Checking event "' . $event->summary . '" ' . $event->dtstart . ' - ' . $event->dtend); } if ($event->container_id != $cal1) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Event is in another calendar - skip'); } $cal1Events->removeRecord($event); continue; } $summaryMatch = $cal2Events->filter('summary', $event->summary); if (count($summaryMatch) > 0) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " Found " . count($summaryMatch) . ' events with matching summaries'); } $dtStartMatch = $summaryMatch->filter('dtstart', $event->dtstart); if (count($dtStartMatch) > 0) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " Found " . count($summaryMatch) . ' events with matching dtstarts and summaries'); } $matchingEvents->merge($dtStartMatch); // remove from cal1+cal2 $cal1Events->removeRecord($event); $cal2Events->removeRecords($dtStartMatch); $cal2EventIdsAlreadyProcessed = array_merge($cal2EventIdsAlreadyProcessed, $dtStartMatch->getArrayOfIds()); } else { $changedEvents->merge($summaryMatch); $cal1Events->removeRecord($event); $cal2Events->removeRecords($summaryMatch); $cal2EventIdsAlreadyProcessed = array_merge($cal2EventIdsAlreadyProcessed, $summaryMatch->getArrayOfIds()); } } } // add missing events if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " Found " . count($cal1Events) . ' events missing in cal2'); } $missingEventsInCal2->merge($cal1Events); // compare cal2 -> cal1 and add events as missing from cal1 that we did not detect before foreach ($cal2EventsClone as $event) { if (in_array($event->getId(), $cal2EventIdsAlreadyProcessed)) { continue; } if ($event->container_id != $cal2) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Event is in another calendar - skip'); } continue; } $missingEventsInCal1->addRecord($event); } } $result = array('matching' => $matchingEvents, 'changed' => $changedEvents, 'missingInCal1' => $missingEventsInCal1, 'missingInCal2' => $missingEventsInCal2); return $result; }
/** * get and resolve all alarms of given record(s) * * @param Tinebase_Record_Interface|Tinebase_Record_RecordSet $_record */ public function getAlarms($_record) { $events = new Tinebase_Record_RecordSet('Calendar_Model_Event', array($_record)); if ($_record->exdate instanceof Tinebase_Record_RecordSet) { $events->merge($_record->exdate->filter('is_deleted', 0)); } $this->_eventController->getAlarms($events); }
/** * returns next occurrence _ignoring exceptions_ or NULL if there is none/not computable * * NOTE: computing the next occurrence of an open end rrule can be dangoures, as it might result * in a endless loop. Therefore we only make a limited number of attempts before giving up. * * @param Calendar_Model_Event $_event * @param Tinebase_Record_RecordSet $_exceptions * @param Tinebase_DateTime $_from * @param Int $_which * @return Calendar_Model_Event */ public static function computeNextOccurrence($_event, $_exceptions, $_from, $_which = 1) { $freqMap = array(self::FREQ_DAILY => Tinebase_DateTime::MODIFIER_DAY, self::FREQ_WEEKLY => Tinebase_DateTime::MODIFIER_WEEK, self::FREQ_MONTHLY => Tinebase_DateTime::MODIFIER_MONTH, self::FREQ_YEARLY => Tinebase_DateTime::MODIFIER_YEAR); $rrule = new Calendar_Model_Rrule(NULL, TRUE); $rrule->setFromString($_event->rrule); $from = clone $_from; $until = clone $from; $interval = $_which * $rrule->interval; // we don't want to compute ourself $ownEvent = clone $_event; $ownEvent->setRecurId(); $_exceptions->addRecord($ownEvent); $recurSet = new Tinebase_Record_RecordSet('Calendar_Model_Event'); if ($_from->isEarlier($_event->dtstart)) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' from is ealier dtstart -> given event is next occurrence'); } return $_event; } $until->add($interval, $freqMap[$rrule->freq]); $attempts = 0; while (TRUE) { if ($_event->rrule_until instanceof DateTime && $from->isLater($_event->rrule_until)) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' passed rrule_until -> no furthor occurrences'); } return NULL; } $until = $_event->rrule_until instanceof DateTime && $until->isLater($_event->rrule_until) ? $_event->rrule_until : $until; $recurSet->merge(self::computeRecurrenceSet($_event, $_exceptions, $from, $until)); $attempts++; if (count($recurSet) >= $_which) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " found next occurrence after {$attempts} attempt(s)"); } break; } if ($attempts > count($_exceptions) + 5) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " could not find the next occurrence after {$attempts} attempts, giving up"); } return NULL; } $from->add($interval, $freqMap[$rrule->freq]); $until->add($interval, $freqMap[$rrule->freq]); } $recurSet->sort('dtstart', 'ASC'); return $recurSet[$_which - 1]; }
/** * get core data for all applications * * @return Tinebase_Record_RecordSet */ public function getCoreData() { $result = new Tinebase_Record_RecordSet('CoreData_Model_CoreData'); // loop all installed apps and collect CoreData foreach (Tinebase_Core::getUser()->getApplications() as $application) { $appControllerName = $application->name . '_Controller'; if (class_exists($appControllerName)) { $appController = call_user_func($appControllerName . '::getInstance'); if (method_exists($appController, 'getCoreDataForApplication')) { $coreDataOfApplication = $appController->getCoreDataForApplication(); $result->merge($coreDataOfApplication); } } } return $result; }