/** * Returns true or false depending on if the event falls in the specified * time-range. This is used for filtering purposes. * * The rules used to determine if an event falls within the specified * time-range is based on the CalDAV specification. * * @param DateTime $start * @param DateTime $end * @return bool */ public function isInTimeRange(\DateTime $start, \DateTime $end) { $dtstart = isset($this->DTSTART) ? $this->DTSTART->getDateTime() : null; $duration = isset($this->DURATION) ? VObject\DateTimeParser::parseDuration($this->DURATION) : null; $due = isset($this->DUE) ? $this->DUE->getDateTime() : null; $completed = isset($this->COMPLETED) ? $this->COMPLETED->getDateTime() : null; $created = isset($this->CREATED) ? $this->CREATED->getDateTime() : null; if ($dtstart) { if ($duration) { $effectiveEnd = clone $dtstart; $effectiveEnd->add($duration); return $start <= $effectiveEnd && $end > $dtstart; } elseif ($due) { return ($start < $due || $start <= $dtstart) && ($end > $dtstart || $end >= $due); } else { return $start <= $dtstart && $end > $dtstart; } } if ($due) { return $start < $due && $end >= $due; } if ($completed && $created) { return ($start <= $created || $start <= $completed) && ($end >= $created || $end >= $completed); } if ($completed) { return $start <= $completed && $end >= $completed; } if ($created) { return $end > $created; } return true; }
/** * Returns true or false depending on if the event falls in the specified * time-range. This is used for filtering purposes. * * The rules used to determine if an event falls within the specified * time-range is based on the CalDAV specification. * * @param \DateTime $start * @param \DateTime $end * @return bool */ public function isInTimeRange(\DateTime $start, \DateTime $end) { if ($this->RRULE) { $it = new VObject\RecurrenceIterator($this); $it->fastForward($start); // We fast-forwarded to a spot where the end-time of the // recurrence instance exceeded the start of the requested // time-range. // // If the starttime of the recurrence did not exceed the // end of the time range as well, we have a match. return $it->getDTStart() < $end && $it->getDTEnd() > $start; } $effectiveStart = $this->DTSTART->getDateTime(); if (isset($this->DTEND)) { // The DTEND property is considered non inclusive. So for a 3 day // event in july, dtstart and dtend would have to be July 1st and // July 4th respectively. // // See: // http://tools.ietf.org/html/rfc5545#page-54 $effectiveEnd = $this->DTEND->getDateTime(); } elseif (isset($this->DURATION)) { $effectiveEnd = clone $effectiveStart; $effectiveEnd->add(VObject\DateTimeParser::parseDuration($this->DURATION)); } elseif ($this->DTSTART->getDateType() == VObject\Property\DateTime::DATE) { $effectiveEnd = clone $effectiveStart; $effectiveEnd->modify('+1 day'); } else { $effectiveEnd = clone $effectiveStart; } return $start <= $effectiveEnd && $end > $effectiveStart; }
/** * Returns the 'effective start' and 'effective end' of this VAVAILABILITY * component. * * We use the DTSTART and DTEND or DURATION to determine this. * * The returned value is an array containing DateTimeImmutable instances. * If either the start or end is 'unbounded' its value will be null * instead. * * @return array */ function getEffectiveStartEnd() { $effectiveStart = $this->DTSTART->getDateTime(); if (isset($this->DTEND)) { $effectiveEnd = $this->DTEND->getDateTime(); } else { $effectiveEnd = $effectiveStart->add(VObject\DateTimeParser::parseDuration($this->DURATION)); } return [$effectiveStart, $effectiveEnd]; }
/** * Returns true or false depending on if the event falls in the specified * time-range. This is used for filtering purposes. * * The rules used to determine if an event falls within the specified * time-range is based on the CalDAV specification. * * @param \DateTime $start * @param \DateTime $end * @return bool */ public function isInTimeRange(\DateTime $start, \DateTime $end) { $effectiveTrigger = $this->getEffectiveTriggerTime(); if (isset($this->DURATION)) { $duration = VObject\DateTimeParser::parseDuration($this->DURATION); $repeat = (string) $this->repeat; if (!$repeat) { $repeat = 1; } $period = new \DatePeriod($effectiveTrigger, $duration, (int) $repeat); foreach ($period as $occurrence) { if ($start <= $occurrence && $end > $occurrence) { return true; } } return false; } else { return $start <= $effectiveTrigger && $end > $effectiveTrigger; } }
/** * Returns true or false depending on if the event falls in the specified * time-range. This is used for filtering purposes. * * The rules used to determine if an event falls within the specified * time-range is based on the CalDAV specification. * * @param DateTimeInterface $start * @param DateTimeInterface $end * * @return bool */ function isInTimeRange(DateTimeInterface $start, DateTimeInterface $end) { if ($this->RRULE) { try { $it = new EventIterator($this, null, $start->getTimezone()); } catch (NoInstancesException $e) { // If we've catched this exception, there are no instances // for the event that fall into the specified time-range. return false; } $it->fastForward($start); // We fast-forwarded to a spot where the end-time of the // recurrence instance exceeded the start of the requested // time-range. // // If the starttime of the recurrence did not exceed the // end of the time range as well, we have a match. return $it->getDTStart() < $end && $it->getDTEnd() > $start; } if (!isset($this->DTSTART)) { return false; } $effectiveStart = $this->DTSTART->getDateTime($start->getTimezone()); if (isset($this->DTEND)) { // The DTEND property is considered non inclusive. So for a 3 day // event in july, dtstart and dtend would have to be July 1st and // July 4th respectively. // // See: // http://tools.ietf.org/html/rfc5545#page-54 $effectiveEnd = $this->DTEND->getDateTime($end->getTimezone()); } elseif (isset($this->DURATION)) { $effectiveEnd = $effectiveStart->add(VObject\DateTimeParser::parseDuration($this->DURATION)); } elseif (!$this->DTSTART->hasTime()) { $effectiveEnd = $effectiveStart->modify('+1 day'); } else { $effectiveEnd = $effectiveStart; } return $start < $effectiveEnd && $end > $effectiveStart; }
public static function arrayForJSON($id, $vtodo, $user_timezone, $calendarId) { $task = array('id' => $id); $task['calendarid'] = $calendarId; $task['type'] = 'task'; $task['name'] = (string) $vtodo->SUMMARY; $task['created'] = (string) $vtodo->CREATED; $task['note'] = (string) $vtodo->DESCRIPTION; $task['location'] = (string) $vtodo->LOCATION; $categories = $vtodo->CATEGORIES; if ($categories) { $task['categories'] = $categories->getParts(); } $start = $vtodo->DTSTART; if ($start) { try { $start = $start->getDateTime(); $start->setTimezone(new \DateTimeZone($user_timezone)); $task['start'] = $start->format('Ymd\\THis'); } catch (\Exception $e) { $task['start'] = null; \OCP\Util::writeLog('tasks', $e->getMessage(), \OCP\Util::ERROR); } } else { $task['start'] = null; } $due = $vtodo->DUE; if ($due) { try { $due = $due->getDateTime(); $due->setTimezone(new \DateTimeZone($user_timezone)); $task['due'] = $due->format('Ymd\\THis'); } catch (\Exception $e) { $task['due'] = null; \OCP\Util::writeLog('tasks', $e->getMessage(), \OCP\Util::ERROR); } } else { $task['due'] = null; } $reminder = $vtodo->VALARM; if ($reminder) { try { $reminderType = $reminder->TRIGGER['VALUE']->getValue(); $reminderAction = $reminder->ACTION->getValue(); $reminderDate = null; $reminderDuration = null; if ($reminderType == 'DATE-TIME') { $reminderDate = $reminder->TRIGGER->getDateTime(); $reminderDate->setTimezone(new \DateTimeZone($user_timezone)); $reminderDate = $reminderDate->format('Ymd\\THis'); } elseif ($reminderType == 'DURATION' && ($start || $due)) { $parsed = VObject\DateTimeParser::parseDuration($reminder->TRIGGER, true); // Calculate the reminder date from duration and start date $related = null; if (is_object($reminder->TRIGGER['RELATED'])) { $related = $reminder->TRIGGER['RELATED']->getValue(); if ($related == 'END' && $due) { $reminderDate = $due->modify($parsed)->format('Ymd\\THis'); } else { throw new \Exception('Reminder duration related to not available date.'); } } elseif ($start) { $reminderDate = $start->modify($parsed)->format('Ymd\\THis'); } else { throw new \Exception('Reminder duration related to not available date.'); } preg_match('/^(?P<plusminus>\\+|-)?P((?P<week>\\d+)W)?((?P<day>\\d+)D)?(T((?P<hour>\\d+)H)?((?P<minute>\\d+)M)?((?P<second>\\d+)S)?)?$/', $reminder->TRIGGER, $matches); $invert = false; if ($matches['plusminus'] === '-') { $invert = true; } $parts = array('week', 'day', 'hour', 'minute', 'second'); $reminderDuration = array('token' => null); foreach ($parts as $part) { $matches[$part] = isset($matches[$part]) && $matches[$part] ? (int) $matches[$part] : 0; $reminderDuration[$part] = $matches[$part]; if ($matches[$part] && !$reminderDuration['token']) { $reminderDuration['token'] = $part; } } if ($reminderDuration['token'] == null) { $reminderDuration['token'] = $parts[0]; } $reminderDuration['params'] = array('id' => (int) $invert . (int) ($related == 'END'), 'related' => $related ? $related : 'START', 'invert' => $invert); } else { $reminderDate = null; $reminderDuration = null; } $task['reminder'] = array('type' => $reminderType, 'action' => $reminderAction, 'date' => $reminderDate, 'duration' => $reminderDuration); } catch (\Exception $e) { $task['reminder'] = null; \OCP\Util::writeLog('tasks', $e->getMessage(), \OCP\Util::ERROR); } } else { $task['reminder'] = null; } $priority = $vtodo->PRIORITY; if (isset($priority)) { $priority = (10 - $priority->getValue()) % 10; $task['priority'] = (string) $priority; if ($priority > 5) { $task['starred'] = true; } } else { $task['priority'] = '0'; $task['starred'] = false; } $completed = $vtodo->COMPLETED; if ($completed) { try { $completed = $completed->getDateTime(); $completed->setTimezone(new \DateTimeZone($user_timezone)); $task['completed_date'] = $completed->format('Ymd\\THis'); $task['completed'] = true; } catch (\Exception $e) { $task['completed'] = false; \OCP\Util::writeLog('tasks', $e->getMessage(), \OCP\Util::ERROR); } } else { $task['completed'] = false; } $percentComplete = $vtodo->{'PERCENT-COMPLETE'}; if ($percentComplete) { $task['complete'] = $percentComplete->getValue(); } else { $task['complete'] = '0'; } $comments = $vtodo->COMMENT; if ($comments) { $comments_parsed = array(); foreach ($comments as $com) { // parse time $time = $com['X-OC-DATE-TIME']; if ($time) { $time = new \DateTime($time); $time->setTimezone(new \DateTimeZone($user_timezone)); $time = $time->format('Ymd\\THis'); } // parse comment ID $comID = $com['X-OC-ID']; if ($comID) { $comID = $com['X-OC-ID']->getValue(); } // parse user ID $userID = $com['X-OC-USERID']; if ($userID) { $userID = (string) $com['X-OC-USERID']->getValue(); } $user = \OC::$server->getUserManager()->get($userID); $userName = $userID; if ($user) { $userName = $user->getDisplayName(); } $comments_parsed[] = array('id' => $comID, 'userID' => $userID, 'name' => $userName, 'comment' => $com->getValue(), 'time' => $time); } $task['comments'] = $comments_parsed; } return $task; }
/** * @expectedException LogicException */ function testParseICalendarDurationFail() { DateTimeParser::parseDuration('P1X', true); }
/** * Returns a DateInterval representation of the Duration property. * * If the property has more than one value, only the first is returned. * * @return \DateInterval */ public function getDateInterval() { $parts = $this->getParts(); $value = $parts[0]; return DateTimeParser::parseDuration($value); }
/** * Parses the input data and returns a correct VFREEBUSY object, wrapped in * a VCALENDAR. * * @return Component */ function getResult() { $busyTimes = []; foreach ($this->objects as $key => $object) { foreach ($object->getBaseComponents() as $component) { switch ($component->name) { case 'VEVENT': $FBTYPE = 'BUSY'; if (isset($component->TRANSP) && strtoupper($component->TRANSP) === 'TRANSPARENT') { break; } if (isset($component->STATUS)) { $status = strtoupper($component->STATUS); if ($status === 'CANCELLED') { break; } if ($status === 'TENTATIVE') { $FBTYPE = 'BUSY-TENTATIVE'; } } $times = []; if ($component->RRULE) { try { $iterator = new EventIterator($object, (string) $component->uid, $this->timeZone); } catch (NoInstancesException $e) { // This event is recurring, but it doesn't have a single // instance. We are skipping this event from the output // entirely. unset($this->objects[$key]); continue; } if ($this->start) { $iterator->fastForward($this->start); } $maxRecurrences = 200; while ($iterator->valid() && --$maxRecurrences) { $startTime = $iterator->getDTStart(); if ($this->end && $startTime > $this->end) { break; } $times[] = [$iterator->getDTStart(), $iterator->getDTEnd()]; $iterator->next(); } } else { $startTime = $component->DTSTART->getDateTime($this->timeZone); if ($this->end && $startTime > $this->end) { break; } $endTime = null; if (isset($component->DTEND)) { $endTime = $component->DTEND->getDateTime($this->timeZone); } elseif (isset($component->DURATION)) { $duration = DateTimeParser::parseDuration((string) $component->DURATION); $endTime = clone $startTime; $endTime = $endTime->add($duration); } elseif (!$component->DTSTART->hasTime()) { $endTime = clone $startTime; $endTime = $endTime->modify('+1 day'); } else { // The event had no duration (0 seconds) break; } $times[] = [$startTime, $endTime]; } foreach ($times as $time) { if ($this->end && $time[0] > $this->end) { break; } if ($this->start && $time[1] < $this->start) { break; } $busyTimes[] = [$time[0], $time[1], $FBTYPE]; } break; case 'VFREEBUSY': foreach ($component->FREEBUSY as $freebusy) { $fbType = isset($freebusy['FBTYPE']) ? strtoupper($freebusy['FBTYPE']) : 'BUSY'; // Skipping intervals marked as 'free' if ($fbType === 'FREE') { continue; } $values = explode(',', $freebusy); foreach ($values as $value) { list($startTime, $endTime) = explode('/', $value); $startTime = DateTimeParser::parseDateTime($startTime); if (substr($endTime, 0, 1) === 'P' || substr($endTime, 0, 2) === '-P') { $duration = DateTimeParser::parseDuration($endTime); $endTime = clone $startTime; $endTime = $endTime->add($duration); } else { $endTime = DateTimeParser::parseDateTime($endTime); } if ($this->start && $this->start > $endTime) { continue; } if ($this->end && $this->end < $startTime) { continue; } $busyTimes[] = [$startTime, $endTime, $fbType]; } } break; } } } if ($this->baseObject) { $calendar = $this->baseObject; } else { $calendar = new VCalendar(); } $vfreebusy = $calendar->createComponent('VFREEBUSY'); $calendar->add($vfreebusy); if ($this->start) { $dtstart = $calendar->createProperty('DTSTART'); $dtstart->setDateTime($this->start); $vfreebusy->add($dtstart); } if ($this->end) { $dtend = $calendar->createProperty('DTEND'); $dtend->setDateTime($this->end); $vfreebusy->add($dtend); } $dtstamp = $calendar->createProperty('DTSTAMP'); $dtstamp->setDateTime(new DateTimeImmutable('now', new \DateTimeZone('UTC'))); $vfreebusy->add($dtstamp); foreach ($busyTimes as $busyTime) { $busyTime[0] = $busyTime[0]->setTimeZone(new \DateTimeZone('UTC')); $busyTime[1] = $busyTime[1]->setTimeZone(new \DateTimeZone('UTC')); $prop = $calendar->createProperty('FREEBUSY', $busyTime[0]->format('Ymd\\THis\\Z') . '/' . $busyTime[1]->format('Ymd\\THis\\Z')); $prop['FBTYPE'] = $busyTime[2]; $vfreebusy->add($prop); } return $calendar; }
/** * This method takes an array of iCalendar objects and applies its busy * times on fbData. * * @param FreeBusyData $fbData * @param VCalendar[] $objects */ protected function calculateBusy(FreeBusyData $fbData, array $objects) { foreach ($objects as $key => $object) { foreach ($object->getBaseComponents() as $component) { switch ($component->name) { case 'VEVENT': $FBTYPE = 'BUSY'; if (isset($component->TRANSP) && strtoupper($component->TRANSP) === 'TRANSPARENT') { break; } if (isset($component->STATUS)) { $status = strtoupper($component->STATUS); if ($status === 'CANCELLED') { break; } if ($status === 'TENTATIVE') { $FBTYPE = 'BUSY-TENTATIVE'; } } $times = []; if ($component->RRULE) { try { $iterator = new EventIterator($object, (string) $component->uid, $this->timeZone); } catch (NoInstancesException $e) { // This event is recurring, but it doesn't have a single // instance. We are skipping this event from the output // entirely. unset($this->objects[$key]); continue; } if ($this->start) { $iterator->fastForward($this->start); } $maxRecurrences = Settings::$maxRecurrences; while ($iterator->valid() && --$maxRecurrences) { $startTime = $iterator->getDTStart(); if ($this->end && $startTime > $this->end) { break; } $times[] = [$iterator->getDTStart(), $iterator->getDTEnd()]; $iterator->next(); } } else { $startTime = $component->DTSTART->getDateTime($this->timeZone); if ($this->end && $startTime > $this->end) { break; } $endTime = null; if (isset($component->DTEND)) { $endTime = $component->DTEND->getDateTime($this->timeZone); } elseif (isset($component->DURATION)) { $duration = DateTimeParser::parseDuration((string) $component->DURATION); $endTime = clone $startTime; $endTime = $endTime->add($duration); } elseif (!$component->DTSTART->hasTime()) { $endTime = clone $startTime; $endTime = $endTime->modify('+1 day'); } else { // The event had no duration (0 seconds) break; } $times[] = [$startTime, $endTime]; } foreach ($times as $time) { if ($this->end && $time[0] > $this->end) { break; } if ($this->start && $time[1] < $this->start) { break; } $fbData->add($time[0]->getTimeStamp(), $time[1]->getTimeStamp(), $FBTYPE); } break; case 'VFREEBUSY': foreach ($component->FREEBUSY as $freebusy) { $fbType = isset($freebusy['FBTYPE']) ? strtoupper($freebusy['FBTYPE']) : 'BUSY'; // Skipping intervals marked as 'free' if ($fbType === 'FREE') { continue; } $values = explode(',', $freebusy); foreach ($values as $value) { list($startTime, $endTime) = explode('/', $value); $startTime = DateTimeParser::parseDateTime($startTime); if (substr($endTime, 0, 1) === 'P' || substr($endTime, 0, 2) === '-P') { $duration = DateTimeParser::parseDuration($endTime); $endTime = clone $startTime; $endTime = $endTime->add($duration); } else { $endTime = DateTimeParser::parseDateTime($endTime); } if ($this->start && $this->start > $endTime) { continue; } if ($this->end && $this->end < $startTime) { continue; } $fbData->add($startTime->getTimeStamp(), $endTime->getTimeStamp(), $fbType); } } break; } } } }
/** * parse reminder duration * * @param mixed $reminder * @param object $due * @param object $start * @return string */ private function parseReminderDuration($reminder, $due, $start) { $parsed = VObject\DateTimeParser::parseDuration($reminder->TRIGGER, true); // Calculate the reminder date from duration and start date $related = null; if (is_object($reminder->TRIGGER['RELATED'])) { $related = $reminder->TRIGGER['RELATED']->getValue(); if ($related == 'END' && $due) { $reminderDate = $this->helper->parseDateObject($due, $parsed); } else { throw new \Exception('Reminder duration related to not available date.'); } } elseif ($start) { $reminderDate = $this->helper->parseDateObject($start, $parsed); } else { throw new \Exception('Reminder duration related to not available date.'); } return array($related, $reminderDate); }
public static function arrayForJSON($id, $vtodo, $user_timezone) { $task = array('id' => $id); $task['name'] = $vtodo->getAsString('SUMMARY'); $task['created'] = $vtodo->getAsString('CREATED'); $task['note'] = $vtodo->getAsString('DESCRIPTION'); $task['location'] = $vtodo->getAsString('LOCATION'); $task['categories'] = $vtodo->getAsArray('CATEGORIES'); $start = $vtodo->DTSTART; if ($start) { try { $start = $start->getDateTime(); $start->setTimezone(new \DateTimeZone($user_timezone)); $task['start'] = $start->format('Ymd\\THis'); } catch (\Exception $e) { $task['start'] = null; \OCP\Util::writeLog('tasks_enhanced', $e->getMessage(), \OCP\Util::ERROR); } } else { $task['start'] = null; } $due = $vtodo->DUE; if ($due) { try { $due = $due->getDateTime(); $due->setTimezone(new \DateTimeZone($user_timezone)); $task['due'] = $due->format('Ymd\\THis'); } catch (\Exception $e) { $task['due'] = null; \OCP\Util::writeLog('tasks_enhanced', $e->getMessage(), \OCP\Util::ERROR); } } else { $task['due'] = null; } $reminder = $vtodo->VALARM; if ($reminder) { try { $reminderType = $reminder->TRIGGER['VALUE']->value; $reminderAction = $reminder->ACTION->value; $reminderDate = null; $reminderDuration = null; if ($reminderType == 'DATE-TIME') { $reminderDate = $reminder->TRIGGER->getDateTime(); $reminderDate->setTimezone(new \DateTimeZone($user_timezone)); $reminderDate = $reminderDate->format('Ymd\\THis'); } elseif ($reminderType == 'DURATION' && ($start || $due)) { $parsed = VObject\DateTimeParser::parseDuration($reminder->TRIGGER, true); // Calculate the reminder date from duration and start date $related = null; if (is_object($reminder->TRIGGER['RELATED'])) { $related = $reminder->TRIGGER['RELATED']->value; if (is_object($reminder->TRIGGER['RELATED']) && $reminder->TRIGGER['RELATED']->value == 'END' && $due) { $reminderDate = $due->modify($parsed)->format('Ymd\\THis'); } elseif ($start) { $reminderDate = $start->modify($parsed)->format('Ymd\\THis'); } } else { throw new \Exception('Reminder duration related to not available date.'); } $result = preg_match('/^(?P<plusminus>\\+|-)?P((?P<week>\\d+)W)?((?P<day>\\d+)D)?(T((?P<hour>\\d+)H)?((?P<minute>\\d+)M)?((?P<second>\\d+)S)?)?$/', $reminder->TRIGGER, $matches); $invert = false; if ($matches['plusminus'] === '-') { $invert = true; } $parts = array('week', 'day', 'hour', 'minute', 'second'); $reminderDuration = array('token' => null); foreach ($parts as $part) { $matches[$part] = isset($matches[$part]) && $matches[$part] ? (int) $matches[$part] : 0; $reminderDuration[$part] = $matches[$part]; if ($matches[$part] && !$reminderDuration['token']) { $reminderDuration['token'] = $part; } } if ($reminderDuration['token'] == null) { $reminderDuration['token'] = $parts[0]; } $reminderDuration['params'] = array('id' => (int) $invert . (int) ($related == 'END'), 'related' => $related ? $related : 'START', 'invert' => $invert); } else { $reminderDate = null; $reminderDuration = null; } $task['reminder'] = array('type' => $reminderType, 'action' => $reminderAction, 'date' => $reminderDate, 'duration' => $reminderDuration); } catch (\Exception $e) { $task['reminder'] = null; \OCP\Util::writeLog('tasks_enhanced', $e->getMessage(), \OCP\Util::ERROR); } } else { $task['reminder'] = null; } $starred = $vtodo->getAsString('PRIORITY'); if ($starred) { $task['starred'] = true; } else { $task['starred'] = false; } $completed = $vtodo->COMPLETED; if ($completed) { try { $completed = $completed->getDateTime(); $completed->setTimezone(new \DateTimeZone($user_timezone)); $task['completed_date'] = $completed->format('Ymd\\THis'); $task['completed'] = true; } catch (\Exception $e) { $task['completed'] = false; \OCP\Util::writeLog('tasks_enhanced', $e->getMessage(), \OCP\Util::ERROR); } } else { $task['completed'] = false; } $task['complete'] = $vtodo->getAsString('PERCENT-COMPLETE') == '' ? '0' : $vtodo->getAsString('PERCENT-COMPLETE'); $comments = $vtodo->COMMENT; if ($comments) { $comments_parsed = array(); foreach ($comments as $com) { $time = new \DateTime($com['DATE-TIME']->value); $time->setTimezone(new \DateTimeZone($user_timezone)); $time = $time->format('Ymd\\THis'); $comments_parsed[] = array('id' => (int) $com['ID']->value, 'userID' => $com['USERID']->value, 'name' => \OCP\USER::getDisplayName($com['USERID']->value), 'comment' => $com->value, 'time' => $time); } $task['comments'] = $comments_parsed; } return $task; }