/** * 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 Sabre_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(Sabre_VObject_DateTimeParser::parseDuration($this->DURATION)); } elseif ($this->DTSTART->getDateType() == Sabre_VObject_Element_DateTime::DATE) { $effectiveEnd = clone $effectiveStart; $effectiveEnd->modify('+1 day'); } else { $effectiveEnd = clone $effectiveStart; } return $start <= $effectiveEnd && $end > $effectiveStart; }
/** * 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 Sabre_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)) { $effectiveEnd = $this->DTEND->getDateTime(); // If this was an all-day event, we should just increase the // end-date by 1. Otherwise the event will last until the second // the date changed, by increasing this by 1 day the event lasts // all of the last day as well. if ($this->DTSTART->getDateType() == Sabre_VObject_Element_DateTime::DATE) { $effectiveEnd->modify('+1 day'); } } elseif (isset($this->DURATION)) { $effectiveEnd = clone $effectiveStart; $effectiveEnd->add(Sabre_VObject_DateTimeParser::parseDuration($this->DURATION)); } elseif ($this->DTSTART->getDateType() == Sabre_VObject_Element_DateTime::DATE) { $effectiveEnd = clone $effectiveStart; $effectiveEnd->modify('+1 day'); } else { $effectiveEnd = clone $effectiveStart; } return $start <= $effectiveEnd && $end > $effectiveStart; }
/** * 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) ? Sabre_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) { $effectiveTrigger = $this->getEffectiveTriggerTime(); if (isset($this->DURATION)) { $duration = Sabre_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; } }
/** * This method is responsible for parsing the request and generating the * response for the CALDAV:free-busy-query REPORT. * * @param DOMNode $dom * @return void */ protected function freeBusyQueryReport(DOMNode $dom) { $start = null; $end = null; foreach ($dom->firstChild->childNodes as $childNode) { $clark = Sabre_DAV_XMLUtil::toClarkNotation($childNode); if ($clark == '{' . self::NS_CALDAV . '}time-range') { $start = $childNode->getAttribute('start'); $end = $childNode->getAttribute('end'); break; } } if ($start) { $start = Sabre_VObject_DateTimeParser::parseDateTime($start); } if ($end) { $end = Sabre_VObject_DateTimeParser::parseDateTime($end); } if (!$start && !$end) { throw new Sabre_DAV_Exception_BadRequest('The freebusy report must have a time-range filter'); } $acl = $this->server->getPlugin('acl'); if (!$acl) { throw new Sabre_DAV_Exception('The ACL plugin must be loaded for free-busy queries to work'); } $uri = $this->server->getRequestUri(); $acl->checkPrivileges($uri, '{' . self::NS_CALDAV . '}read-free-busy'); $calendar = $this->server->tree->getNodeForPath($uri); if (!$calendar instanceof Sabre_CalDAV_ICalendar) { throw new Sabre_DAV_Exception_NotImplemented('The free-busy-query REPORT is only implemented on calendars'); } $objects = array_map(function ($child) { $obj = $child->get(); if (is_resource($obj)) { $obj = stream_get_contents($obj); } return $obj; }, $calendar->getChildren()); $generator = new Sabre_VObject_FreeBusyGenerator(); $generator->setObjects($objects); $generator->setTimeRange($start, $end); $result = $generator->getResult(); $result = $result->serialize(); $this->server->httpResponse->sendStatus(200); $this->server->httpResponse->setHeader('Content-Type', 'text/calendar'); $this->server->httpResponse->setHeader('Content-Length', strlen($result)); $this->server->httpResponse->sendBody($result); }
/** * @param wdcal_local $localization * @param string $baseurl * @param int $calendar_id * @param int $uri * @return string */ function wdcal_getEditPage_str(&$localization, $baseurl, $calendar_id, $uri) { $server = dav_create_server(true, true, false); if ($uri > 0) { $calendar = dav_get_current_user_calendar_by_id($server, $calendar_id, DAV_ACL_WRITE); if (!$calendar) { $calendar = dav_get_current_user_calendar_by_id($server, $calendar_id, DAV_ACL_READ); $calendars = array(); } else { $calendars = dav_get_current_user_calendars($server, DAV_ACL_WRITE); } if ($calendar == null) { return "Calendar not found"; } $obj_uri = Sabre_CalDAV_Backend_Common::loadCalendarobjectById($uri); $vObject = dav_get_current_user_calendarobject($server, $calendar, $obj_uri["uri"], DAV_ACL_WRITE); $component = dav_get_eventComponent($vObject); if ($component == null) { return t('Could not open component for editing'); } /** @var Sabre\VObject\Property\DateTime $dtstart */ $dtstart = $component->__get("DTSTART"); $event = array("id" => IntVal($uri), "Summary" => $component->__get("SUMMARY") ? $component->__get("SUMMARY")->value : null, "StartTime" => $dtstart->getDateTime()->getTimeStamp(), "EndTime" => Sabre_CalDAV_Backend_Common::getDtEndTimeStamp($component), "IsAllDayEvent" => strlen($dtstart->value) == 8, "Description" => $component->__get("DESCRIPTION") ? $component->__get("DESCRIPTION")->value : null, "Location" => $component->__get("LOCATION") ? $component->__get("LOCATION")->value : null, "Color" => $component->__get("X-ANIMEXX-COLOR") ? $component->__get("X-ANIMEXX-COLOR")->value : null); $exdates = $component->select("EXDATE"); $recurrentce_exdates = array(); /** @var Sabre\VObject\Property\MultiDateTime $x */ foreach ($exdates as $x) { /** @var DateTime $y */ $z = $x->getDateTimes(); foreach ($z as $y) { $recurrentce_exdates[] = $y->getTimeStamp(); } } $notifications = array(); $alarms = $component->select("VALARM"); foreach ($alarms as $alarm) { /** @var Sabre_VObject_Component_VAlarm $alarm */ $action = $alarm->__get("ACTION")->value; $trigger = $alarm->__get("TRIGGER"); if (isset($trigger['VALUE']) && strtoupper($trigger['VALUE']) !== 'DURATION') { notice("The notification of this event cannot be parsed"); continue; } /** @var DateInterval $triggerDuration */ $triggerDuration = Sabre_VObject_DateTimeParser::parseDuration($trigger); $unit = "hour"; $value = 1; if ($triggerDuration->s > 0) { $unit = "second"; $value = $triggerDuration->s + $triggerDuration->i * 60 + $triggerDuration->h * 3600 + $triggerDuration->d * 3600 * 24; // @TODO support more than days? } elseif ($triggerDuration->i) { $unit = "minute"; $value = $triggerDuration->i + $triggerDuration->h * 60 + $triggerDuration->d * 60 * 24; } elseif ($triggerDuration->h) { $unit = "hour"; $value = $triggerDuration->h + $triggerDuration->d * 24; } elseif ($triggerDuration->d > 0) { $unit = "day"; $value = $triggerDuration->d; } $rel = isset($trigger['RELATED']) && strtoupper($trigger['RELATED']) == 'END' ? 'end' : 'start'; $notifications[] = array("action" => strtolower($action), "rel" => $rel, "trigger_unit" => $unit, "trigger_value" => $value); } if ($component->select("RRULE")) { $recurrence = new Sabre_VObject_RecurrenceIterator($vObject, (string) $component->__get("UID")); } else { $recurrence = null; } } elseif (isset($_REQUEST["start"]) && $_REQUEST["start"] > 0) { $calendars = dav_get_current_user_calendars($server, DAV_ACL_WRITE); //$calendar = dav_get_current_user_calendar_by_id($server, $calendar_id, DAV_ACL_WRITE); $event = array("id" => 0, "Summary" => $_REQUEST["title"], "StartTime" => InTVal($_REQUEST["start"]), "EndTime" => IntVal($_REQUEST["end"]), "IsAllDayEvent" => $_REQUEST["isallday"], "Description" => "", "Location" => "", "Color" => null); if ($_REQUEST["isallday"]) { $notifications = array(); } else { $notifications = array(array("action" => "email", "rel" => "start", "trigger_unit" => "hour", "trigger_value" => 1)); } $recurrence = null; $recurrentce_exdates = array(); } else { $calendars = dav_get_current_user_calendars($server, DAV_ACL_WRITE); //$calendar = dav_get_current_user_calendar_by_id($server, $calendar_id, DAV_ACL_WRITE); $event = array("id" => 0, "Summary" => "", "StartTime" => time(), "EndTime" => time() + 3600, "IsAllDayEvent" => "0", "Description" => "", "Location" => "", "Color" => null); $notifications = array(array("action" => "email", "rel" => "start", "trigger_unit" => "hour", "trigger_value" => 1)); $recurrence = null; $recurrentce_exdates = array(); } $postto = $baseurl . "/dav/wdcal/" . ($uri == 0 ? "new/" : $calendar_id . "/" . $uri . "/edit/"); $out = "<a href='" . $baseurl . "/dav/wdcal/'>" . t("Go back to the calendar") . "</a><br><br>"; $out .= "<form method='POST' action='{$postto}'>\n\t\t<input type='hidden' name='form_security_token' value='" . get_form_security_token('caledit') . "'>\n"; $out .= "<h2>" . t("Event data") . "</h2>"; $out .= "<label for='calendar' class='block'>" . t("Calendar") . ":</label><select id='calendar' name='calendar' size='1'>"; $found = false; $cal_col = "aaaaaa"; foreach ($calendars as $cal) { $prop = $cal->getProperties(array("id", DAV_DISPLAYNAME, DAV_CALENDARCOLOR)); $out .= "<option value='" . $prop["id"] . "' "; if ($prop["id"] == $calendar_id) { $out .= "selected"; $cal_col = $prop[DAV_CALENDARCOLOR]; $found = true; } elseif (!$found) { $cal_col = $prop[DAV_CALENDARCOLOR]; } $out .= ">" . escape_tags($prop[DAV_DISPLAYNAME]) . "</option>\n"; } $out .= "</select>"; $out .= " <label class='plain'><input type='checkbox' name='color_override' id='color_override' "; if (!is_null($event["Color"])) { $out .= "checked"; } $out .= "> " . t("Special color") . ":</label>"; $out .= "<span id='cal_color_holder' "; if (is_null($event["Color"])) { $out .= "style='display: none;'"; } $out .= "><input name='color' id='cal_color' value='" . (is_null($event["Color"]) ? "#" . $cal_col : escape_tags($event["Color"])) . "'></span>"; $out .= "<br>\n"; $out .= "<label class='block' for='cal_summary'>" . t("Subject") . ":</label>\n\t\t<input name='summary' id='cal_summary' value=\"" . escape_tags($event["Summary"]) . "\"><br>\n"; $out .= "<label class='block' for='cal_allday'>Is All-Day event:</label><input type='checkbox' name='allday' id='cal_allday' " . ($event["IsAllDayEvent"] ? "checked" : "") . "><br>\n"; $out .= "<label class='block' for='cal_start_date'>" . t("Starts") . ":</label>"; $out .= "<input name='start_date' value='" . $localization->dateformat_datepicker_php($event["StartTime"]) . "' id='cal_start_date'>"; $out .= "<input name='start_time' value='" . date("H:i", $event["StartTime"]) . "' id='cal_start_time'>"; $out .= "<br>\n"; $out .= "<label class='block' for='cal_end_date'>" . t("Ends") . ":</label>"; $out .= "<input name='end_date' value='" . $localization->dateformat_datepicker_php($event["EndTime"]) . "' id='cal_end_date'>"; $out .= "<input name='end_time' value='" . date("H:i", $event["EndTime"]) . "' id='cal_end_time'>"; $out .= "<br>\n"; $out .= "<label class='block' for='cal_location'>" . t("Location") . ":</label><input name='location' id='cal_location' value=\"" . escape_tags($event["Location"]) . "\"><br>\n"; $out .= "<label class='block' for='event-desc-textarea'>" . t("Description") . ":</label> <textarea id='event-desc-textarea' name='wdcal_desc' style='vertical-align: top; width: 400px; height: 100px;'>" . escape_tags($event["Description"]) . "</textarea>"; $out .= "<br style='clear: both;'>"; $out .= "<h2>" . t("Recurrence") . "</h2>"; $out .= "<label class='block' for='rec_frequency'>" . t("Frequency") . ":</label> <select id='rec_frequency' name='rec_frequency' size='1'>"; $out .= "<option value=''>" . t("None") . "</option>\n"; $out .= "<option value='daily' "; if ($recurrence && $recurrence->frequency == "daily") { $out .= "selected"; } $out .= ">" . t("Daily") . "</option>\n"; $out .= "<option value='weekly' "; if ($recurrence && $recurrence->frequency == "weekly") { $out .= "selected"; } $out .= ">" . t("Weekly") . "</option>\n"; $out .= "<option value='monthly' "; if ($recurrence && $recurrence->frequency == "monthly") { $out .= "selected"; } $out .= ">" . t("Monthly") . "</option>\n"; $out .= "<option value='yearly' "; if ($recurrence && $recurrence->frequency == "yearly") { $out .= "selected"; } $out .= ">" . t("Yearly") . "</option>\n"; $out .= "</select><br>\n"; $out .= "<div id='rec_details'>"; $select = "<select id='rec_interval' name='rec_interval' size='1'>"; for ($i = 1; $i < 50; $i++) { $select .= "<option value='{$i}' "; if ($recurrence && $i == $recurrence->interval) { $select .= "selected"; } $select .= ">{$i}</option>\n"; } $select .= "</select>"; $time = "<span class='rec_daily'>" . t("days") . "</span>"; $time .= "<span class='rec_weekly'>" . t("weeks") . "</span>"; $time .= "<span class='rec_monthly'>" . t("months") . "</span>"; $time .= "<span class='rec_yearly'>" . t("years") . "</span>"; $out .= "<label class='block'>" . t("Interval") . ":</label> " . str_replace(array("%select%", "%time%"), array($select, $time), t("All %select% %time%")) . "<br>"; $out .= "<div class='rec_daily'>"; $out .= "<label class='block'>" . t("Days") . ":</label>"; if ($recurrence && $recurrence->byDay) { $byday = $recurrence->byDay; } else { $byday = array("MO", "TU", "WE", "TH", "FR", "SA", "SU"); } if ($localization->getFirstDayOfWeek() == 0) { $out .= "<label class='plain'><input class='rec_daily_byday' type='checkbox' name='rec_daily_byday[]' value='SU' "; if (in_array("SU", $byday)) { $out .= "checked"; } $out .= ">" . t("Sunday") . "</label> "; } $out .= "<label class='plain'><input class='rec_daily_byday' type='checkbox' name='rec_daily_byday[]' value='MO' "; if (in_array("MO", $byday)) { $out .= "checked"; } $out .= ">" . t("Monday") . "</label> "; $out .= "<label class='plain'><input class='rec_daily_byday' type='checkbox' name='rec_daily_byday[]' value='TU' "; if (in_array("TU", $byday)) { $out .= "checked"; } $out .= ">" . t("Tuesday") . "</label> "; $out .= "<label class='plain'><input class='rec_daily_byday' type='checkbox' name='rec_daily_byday[]' value='WE' "; if (in_array("WE", $byday)) { $out .= "checked"; } $out .= ">" . t("Wednesday") . "</label> "; $out .= "<label class='plain'><input class='rec_daily_byday' type='checkbox' name='rec_daily_byday[]' value='TH' "; if (in_array("TH", $byday)) { $out .= "checked"; } $out .= ">" . t("Thursday") . "</label> "; $out .= "<label class='plain'><input class='rec_daily_byday' type='checkbox' name='rec_daily_byday[]' value='FR' "; if (in_array("FR", $byday)) { $out .= "checked"; } $out .= ">" . t("Friday") . "</label> "; $out .= "<label class='plain'><input class='rec_daily_byday' type='checkbox' name='rec_daily_byday[]' value='SA' "; if (in_array("SA", $byday)) { $out .= "checked"; } $out .= ">" . t("Saturday") . "</label> "; if ($localization->getFirstDayOfWeek() != 0) { $out .= "<label class='plain'><input class='rec_daily_byday' type='checkbox' name='rec_daily_byday[]' value='SU' "; if (in_array("SU", $byday)) { $out .= "checked"; } $out .= ">" . t("Sunday") . "</label> "; } $out .= "</div>"; $out .= "<div class='rec_weekly'>"; $out .= "<label class='block'>" . t("Days") . ":</label>"; if ($recurrence && $recurrence->byDay) { $byday = $recurrence->byDay; } else { $days = array("MO", "TU", "WE", "TH", "FR", "SA", "SU"); $byday = array($days[date("N", $event["StartTime"]) - 1]); } if ($localization->getFirstDayOfWeek() == 0) { $out .= "<label class='plain'><input class='rec_weekly_byday' type='checkbox' name='rec_weekly_byday[]' value='SU' "; if (in_array("SU", $byday)) { $out .= "checked"; } $out .= ">" . t("Sunday") . "</label> "; } $out .= "<label class='plain'><input class='rec_weekly_byday' type='checkbox' name='rec_weekly_byday[]' value='MO' "; if (in_array("MO", $byday)) { $out .= "checked"; } $out .= ">" . t("Monday") . "</label> "; $out .= "<label class='plain'><input class='rec_weekly_byday' type='checkbox' name='rec_weekly_byday[]' value='TU' "; if (in_array("TU", $byday)) { $out .= "checked"; } $out .= ">" . t("Tuesday") . "</label> "; $out .= "<label class='plain'><input class='rec_weekly_byday' type='checkbox' name='rec_weekly_byday[]' value='WE' "; if (in_array("WE", $byday)) { $out .= "checked"; } $out .= ">" . t("Wednesday") . "</label> "; $out .= "<label class='plain'><input class='rec_weekly_byday' type='checkbox' name='rec_weekly_byday[]' value='TH' "; if (in_array("TH", $byday)) { $out .= "checked"; } $out .= ">" . t("Thursday") . "</label> "; $out .= "<label class='plain'><input class='rec_weekly_byday' type='checkbox' name='rec_weekly_byday[]' value='FR' "; if (in_array("FR", $byday)) { $out .= "checked"; } $out .= ">" . t("Friday") . "</label> "; $out .= "<label class='plain'><input class='rec_weekly_byday' type='checkbox' name='rec_weekly_byday[]' value='SA' "; if (in_array("SA", $byday)) { $out .= "checked"; } $out .= ">" . t("Saturday") . "</label> "; if ($localization->getFirstDayOfWeek() != 0) { $out .= "<label class='plain'><input class='rec_weekly_byday' type='checkbox' name='rec_weekly_byday[]' value='SU' "; if (in_array("SU", $byday)) { $out .= "checked"; } $out .= ">" . t("Sunday") . "</label> "; } $out .= "<br>"; $out .= "<label class='block'>" . t("First day of week:") . "</label>"; if ($recurrence && $recurrence->weekStart != "") { $wkst = $recurrence->weekStart; } else { if ($localization->getFirstDayOfWeek() == 0) { $wkst = "SU"; } else { $wkst = "MO"; } } $out .= "<label class='plain'><input type='radio' name='rec_weekly_wkst' value='SU' "; if ($wkst == "SU") { $out .= "checked"; } $out .= ">" . t("Sunday") . "</label> "; $out .= "<label class='plain'><input type='radio' name='rec_weekly_wkst' value='MO' "; if ($wkst == "MO") { $out .= "checked"; } $out .= ">" . t("Monday") . "</label><br>\n"; $out .= "</div>"; $monthly_rule = ""; if ($recurrence && ($recurrence->frequency == "monthly" || $recurrence->frequency == "yearly")) { if (is_null($recurrence->byDay) && !is_null($recurrence->byMonthDay) && count($recurrence->byMonthDay) == 1) { $day = date("j", $event["StartTime"]); if ($recurrence->byMonthDay[0] == $day) { $monthly_rule = "bymonthday"; } else { $lastday = date("t", $event["StartTime"]); if ($recurrence->byMonthDay[0] == -1 * ($lastday - $day + 1)) { $monthly_rule = "bymonthday_neg"; } } } if (is_null($recurrence->byMonthDay) && !is_null($recurrence->byDay) && count($recurrence->byDay) == 1) { $num = IntVal($recurrence->byDay[0]); /* $dayMap = array( 'SU' => 0, 'MO' => 1, 'TU' => 2, 'WE' => 3, 'TH' => 4, 'FR' => 5, 'SA' => 6, ); if ($num == 0) { $num = 1; $weekday = $dayMap[$recurrence->byDay[0]]; } else { $weekday = $dayMap[substr($recurrence->byDay[0], strlen($num))]; } echo $num . " - " . $weekday; */ if ($num > 0) { $monthly_rule = "byday"; } if ($num < 0) { $monthly_rule = "byday_neg"; } } if ($monthly_rule == "") { notice("The recurrence of this event cannot be parsed"); } } $out .= "<div class='rec_monthly'>"; $out .= "<label class='block' for='rec_monthly_day'>" . t("Day of month") . ":</label>"; $out .= "<select id='rec_monthly_day' name='rec_monthly_day' size='1'>"; $out .= "<option value='bymonthday' "; if ($monthly_rule == "bymonthday") { $out .= "selected"; } $out .= ">" . t("#num#th of each month") . "</option>\n"; $out .= "<option value='bymonthday_neg' "; if ($monthly_rule == "bymonthday_neg") { $out .= "selected"; } $out .= ">" . t("#num#th-last of each month") . "</option>\n"; $out .= "<option value='byday' "; if ($monthly_rule == "byday") { $out .= "selected"; } $out .= ">" . t("#num#th #wkday# of each month") . "</option>\n"; $out .= "<option value='byday_neg' "; if ($monthly_rule == "byday_neg") { $out .= "selected"; } $out .= ">" . t("#num#th-last #wkday# of each month") . "</option>\n"; $out .= "</select>"; $out .= "</div>\n"; if ($recurrence && $recurrence->frequency == "yearly") { if (count($recurrence->byMonth) != 1 || $recurrence->byMonth[0] != date("n", $event["StartTime"])) { notice("The recurrence of this event cannot be parsed!"); } } $out .= "<div class='rec_yearly'>"; $out .= "<label class='block'>" . t("Month") . ":</label> <span class='rec_month_name'>#month#</span><br>\n"; $out .= "<label class='block' for='rec_yearly_day'>" . t("Day of month") . ":</label>"; $out .= "<select id='rec_yearly_day' name='rec_yearly_day' size='1'>"; $out .= "<option value='bymonthday' "; if ($monthly_rule == "bymonthday") { $out .= "selected"; } $out .= ">" . t("#num#th of the given month") . "</option>\n"; $out .= "<option value='bymonthday_neg' "; if ($monthly_rule == "bymonthday_neg") { $out .= "selected"; } $out .= ">" . t("#num#th-last of the given month") . "</option>\n"; $out .= "<option value='byday' "; if ($monthly_rule == "byday") { $out .= "selected"; } $out .= ">" . t("#num#th #wkday# of the given month") . "</option>\n"; $out .= "<option value='byday_neg' "; if ($monthly_rule == "byday_neg") { $out .= "selected"; } $out .= ">" . t("#num#th-last #wkday# of the given month") . "</option>\n"; $out .= "</select>"; $out .= "</div>\n"; if ($recurrence) { $until = $recurrence->until; $count = $recurrence->count; if (is_a($until, "DateTime")) { /** @var DateTime $until */ $rule_type = "date"; $rule_until_date = $until->getTimestamp(); $rule_until_count = 1; } elseif ($count > 0) { $rule_type = "count"; $rule_until_date = time(); $rule_until_count = $count; } else { $rule_type = "infinite"; $rule_until_date = time(); $rule_until_count = 1; } } else { $rule_type = "infinite"; $rule_until_date = time(); $rule_until_count = 1; } $out .= "<label class='block' for='rec_until_type'>" . t("Repeat until") . ":</label> "; $out .= "<select name='rec_until_type' id='rec_until_type' size='1'>"; $out .= "<option value='infinite' "; if ($rule_type == "infinite") { $out .= "selected"; } $out .= ">" . t("Infinite") . "</option>\n"; $out .= "<option value='date' "; if ($rule_type == "date") { $out .= "selected"; } $out .= ">" . t("Until the following date") . ":</option>\n"; $out .= "<option value='count' "; if ($rule_type == "count") { $out .= "selected"; } $out .= ">" . t("Number of times") . ":</option>\n"; $out .= "</select>"; $out .= "<input name='rec_until_date' value='" . $localization->dateformat_datepicker_php($rule_until_date) . "' id='rec_until_date'>"; $out .= "<input name='rec_until_count' value='{$rule_until_count}' id='rec_until_count'><br>"; $out .= "<label class='block'>" . t("Exceptions") . ":</label><div class='rec_exceptions'>"; $out .= "<div class='rec_exceptions_none' "; if (count($recurrentce_exdates) > 0) { $out .= "style='display: none;'"; } $out .= ">" . t("none") . "</div>"; $out .= "<div class='rec_exceptions_holder' "; if (count($recurrentce_exdates) == 0) { $out .= "style='display: none;'"; } $out .= ">"; foreach ($recurrentce_exdates as $exdate) { $out .= "<div data-timestamp='{$exdate}' class='except'><input type='hidden' class='rec_exception' name='rec_exceptions[]' value='{$exdate}'>"; $out .= "<a href='#' class='exception_remover'>[remove]</a> "; $out .= $localization->date_timestamp2localDate($exdate); $out .= "</div>\n"; } $out .= "</div><div><a href='#' class='exception_adder'>[add]</a></div>"; $out .= "</div>\n"; $out .= "<br>\n"; $out .= "</div><br>"; $out .= "<h2>" . t("Notification") . "</h2>"; if (!$notifications) { $notifications = array(); } $notifications["new"] = array("action" => "email", "trigger_value" => 60, "trigger_unit" => "minute", "rel" => "start"); foreach ($notifications as $index => $noti) { $unparsable = false; if (!in_array($noti["action"], array("email", "display"))) { $unparsable = true; } $out .= "<div class='noti_holder' "; if (!is_numeric($index) && $index == "new") { $out .= "style='display: none;' id='noti_new_row'"; } $out .= "><label class='block' for='noti_type_" . $index . "'>" . t("Notify by") . ":</label>"; $out .= "<select name='noti_type[{$index}]' size='1' id='noti_type_" . $index . "'>"; $out .= "<option value=''>- " . t("Remove") . " -</option>\n"; $out .= "<option value='email' "; if (!$unparsable && $noti["action"] == "email") { $out .= "selected"; } $out .= ">" . t("E-Mail") . "</option>\n"; $out .= "<option value='display' "; if (!$unparsable && $noti["action"] == "display") { $out .= "selected"; } $out .= ">" . t("On Friendica / Display") . "</option>\n"; //$out .= "<option value='other' "; if ($unparsable) $out .= "selected"; $out .= ">- " . t("other (leave it untouched)") . " -</option>\n"; // @TODO $out .= "</select><br>"; $out .= "<label class='block'>" . t("Time") . ":</label>"; $out .= "<input name='noti_value[{$index}]' size='5' style='width: 5em;' value='" . $noti["trigger_value"] . "'>"; $out .= "<select name='noti_unit[{$index}]' size='1'>"; $out .= "<option value='H' "; if ($noti["trigger_unit"] == "hour") { $out .= "selected"; } $out .= ">" . t("Hours") . "</option>\n"; $out .= "<option value='M' "; if ($noti["trigger_unit"] == "minute") { $out .= "selected"; } $out .= ">" . t("Minutes") . "</option>\n"; $out .= "<option value='S' "; if ($noti["trigger_unit"] == "second") { $out .= "selected"; } $out .= ">" . t("Seconds") . "</option>\n"; $out .= "<option value='D' "; if ($noti["trigger_unit"] == "day") { $out .= "selected"; } $out .= ">" . t("Days") . "</option>\n"; $out .= "<option value='W' "; if ($noti["trigger_unit"] == "week") { $out .= "selected"; } $out .= ">" . t("Weeks") . "</option>\n"; $out .= "</select>"; $out .= " <label class='plain'>" . t("before the") . " <select name='noti_ref[{$index}]' size='1'>"; $out .= "<option value='start' "; if ($noti["rel"] == "start") { $out .= "selected"; } $out .= ">" . t("start of the event") . "</option>\n"; $out .= "<option value='end' "; if ($noti["rel"] == "end") { $out .= "selected"; } $out .= ">" . t("end of the event") . "</option>\n"; $out .= "</select></label>\n"; $out .= "</div>"; } $out .= "<input type='hidden' name='new_alarm' id='new_alarm' value='0'><div id='new_alarm_adder'><a href='#'>" . t("Add a notification") . "</a></div>"; $out .= "<script>\$(function() {\n\t\twdcal_edit_init('" . $localization->dateformat_datepicker_js() . "', '{$baseurl}/dav/');\n\t});</script>"; $out .= "<br><input type='submit' name='save' value='Save'></form>"; return $out; }
/** * Parses some information from calendar objects, used for optimized * calendar-queries. * * Blantently copied from Sabre_CalDAV_Backend_PDO * * Returns an array with the following keys: * * etag * * size * * componentType * * firstOccurence * * lastOccurence * * @param string $calendarData * @return array */ function getDenormalizedData($calendarData) { $vObject = Sabre_VObject_Reader::read($calendarData); $componentType = null; $component = null; $firstOccurence = null; $lastOccurence = null; foreach ($vObject->getComponents() as $component) { if ($component->name !== 'VTIMEZONE') { $componentType = $component->name; break; } } if (!$componentType) { throw new Sabre_DAV_Exception_BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component'); } if ($componentType === 'VEVENT') { $firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp(); // Finding the last occurence is a bit harder if (!isset($component->RRULE)) { if (isset($component->DTEND)) { $lastOccurence = $component->DTEND->getDateTime()->getTimeStamp(); } elseif (isset($component->DURATION)) { $endDate = clone $component->DTSTART->getDateTime(); $endDate->add(Sabre_VObject_DateTimeParser::parse($component->DURATION->value)); $lastOccurence = $endDate->getTimeStamp(); } elseif ($component->DTSTART->getDateType() === Sabre_VObject_Property_DateTime::DATE) { $endDate = clone $component->DTSTART->getDateTime(); $endDate->modify('+1 day'); $lastOccurence = $endDate->getTimeStamp(); } else { $lastOccurence = $firstOccurence; } } else { $it = new Sabre_VObject_RecurrenceIterator($vObject, (string) $component->UID); $maxDate = new DateTime(self::MAX_DATE); if ($it->isInfinite()) { $lastOccurence = $maxDate->getTimeStamp(); } else { $end = $it->getDtEnd(); while ($it->valid() && $end < $maxDate) { $end = $it->getDtEnd(); $it->next(); } $lastOccurence = $end->getTimeStamp(); } } } return array('etag' => md5($calendarData), 'size' => strlen($calendarData), 'componentType' => $componentType, 'firstOccurence' => $firstOccurence, 'lastOccurence' => $lastOccurence); }
/** * Parses the CALDAV:expand element * * @param DOMElement $parentNode * @return void */ protected function parseExpand(DOMElement $parentNode) { $start = $parentNode->getAttribute('start'); if (!$start) { throw new Sabre_DAV_Exception_BadRequest('The "start" attribute is required for the CALDAV:expand element'); } $start = Sabre_VObject_DateTimeParser::parseDateTime($start); $end = $parentNode->getAttribute('end'); if (!$end) { throw new Sabre_DAV_Exception_BadRequest('The "end" attribute is required for the CALDAV:expand element'); } $end = Sabre_VObject_DateTimeParser::parseDateTime($end); if ($end <= $start) { throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the expand element.'); } return array('start' => $start, 'end' => $end); }
/** * Creates the iterator * * You should pass a VCALENDAR component, as well as the UID of the event * we're going to traverse. * * @param Sabre_VObject_Component $comp */ public function __construct(Sabre_VObject_Component $vcal, $uid = null) { if (is_null($uid)) { if ($vcal->name === 'VCALENDAR') { throw new InvalidArgumentException('If you pass a VCALENDAR object, you must pass a uid argument as well'); } $components = array($vcal); $uid = (string) $vcal->uid; } else { $components = $vcal->select('VEVENT'); } foreach ($components as $component) { if ((string) $component->uid == $uid) { if (isset($component->{'RECURRENCE-ID'})) { $this->overriddenEvents[$component->DTSTART->getDateTime()->getTimeStamp()] = $component; $this->overriddenDates[] = $component->{'RECURRENCE-ID'}->getDateTime(); } else { $this->baseEvent = $component; } } } if (!$this->baseEvent) { throw new InvalidArgumentException('Could not find a base event with uid: ' . $uid); } $this->startDate = clone $this->baseEvent->DTSTART->getDateTime(); $this->endDate = null; if (isset($this->baseEvent->DTEND)) { $this->endDate = clone $this->baseEvent->DTEND->getDateTime(); } else { $this->endDate = clone $this->startDate; if (isset($this->baseEvent->DURATION)) { $this->endDate->add(Sabre_VObject_DateTimeParser::parse($this->baseEvent->DURATION->value)); } } $this->currentDate = clone $this->startDate; $rrule = (string) $this->baseEvent->RRULE; $parts = explode(';', $rrule); foreach ($parts as $part) { list($key, $value) = explode('=', $part, 2); switch (strtoupper($key)) { case 'FREQ': if (!in_array(strtolower($value), array('secondly', 'minutely', 'hourly', 'daily', 'weekly', 'monthly', 'yearly'))) { throw new InvalidArgumentException('Unknown value for FREQ=' . strtoupper($value)); } $this->frequency = strtolower($value); break; case 'UNTIL': $this->until = Sabre_VObject_DateTimeParser::parse($value); break; case 'COUNT': $this->count = (int) $value; break; case 'INTERVAL': $this->interval = (int) $value; break; case 'BYSECOND': $this->bySecond = explode(',', $value); break; case 'BYMINUTE': $this->byMinute = explode(',', $value); break; case 'BYHOUR': $this->byHour = explode(',', $value); break; case 'BYDAY': $this->byDay = explode(',', strtoupper($value)); break; case 'BYMONTHDAY': $this->byMonthDay = explode(',', $value); break; case 'BYYEARDAY': $this->byYearDay = explode(',', $value); break; case 'BYWEEKNO': $this->byWeekNo = explode(',', $value); break; case 'BYMONTH': $this->byMonth = explode(',', $value); break; case 'BYSETPOS': $this->bySetPos = explode(',', $value); break; case 'WKST': $this->weekStart = strtoupper($value); break; } } // Parsing exception dates if (isset($this->baseEvent->EXDATE)) { foreach ($this->baseEvent->EXDATE as $exDate) { foreach (explode(',', (string) $exDate) as $exceptionDate) { $this->exceptionDates[] = Sabre_VObject_DateTimeParser::parse($exceptionDate, $this->startDate->getTimeZone()); } } } }
/** * Parses the input data and returns a correct VFREEBUSY object, wrapped in * a VCALENDAR. * * @return Sabre_VObject_Component */ public function getResult() { $busyTimes = array(); foreach ($this->objects as $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 = array(); if ($component->RRULE) { $iterator = new Sabre_VObject_RecurrenceIterator($object, (string) $component->uid); if ($this->start) { $iterator->fastForward($this->start); } $maxRecurrences = 200; while ($iterator->valid() && --$maxRecurrences) { $startTime = $iterator->getDTStart(); if ($this->end && $startTime > $this->end) { break; } $times[] = array($iterator->getDTStart(), $iterator->getDTEnd()); $iterator->next(); } } else { $startTime = $component->DTSTART->getDateTime(); if ($this->end && $startTime > $this->end) { break; } $endTime = null; if (isset($component->DTEND)) { $endTime = $component->DTEND->getDateTime(); } elseif (isset($component->DURATION)) { $duration = Sabre_VObject_DateTimeParser::parseDuration((string) $component->DURATION); $endTime = clone $startTime; $endTime->add($duration); } elseif ($component->DTSTART->getDateType() === Sabre_VObject_Property_DateTime::DATE) { $endTime = clone $startTime; $endTime->modify('+1 day'); } else { // The event had no duration (0 seconds) break; } $times[] = array($startTime, $endTime); } foreach ($times as $time) { if ($this->end && $time[0] > $this->end) { break; } if ($this->start && $time[1] < $this->start) { break; } $busyTimes[] = array($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 = Sabre_VObject_DateTimeParser::parseDateTime($startTime); if (substr($endTime, 0, 1) === 'P' || substr($endTime, 0, 2) === '-P') { $duration = Sabre_VObject_DateTimeParser::parseDuration($endTime); $endTime = clone $startTime; $endTime->add($duration); } else { $endTime = Sabre_VObject_DateTimeParser::parseDateTime($endTime); } if ($this->start && $this->start > $endTime) { continue; } if ($this->end && $this->end < $startTime) { continue; } $busyTimes[] = array($startTime, $endTime, $fbType); } } break; } } } if ($this->baseObject) { $calendar = $this->baseObject; } else { $calendar = new Sabre_VObject_Component('VCALENDAR'); $calendar->version = '2.0'; $calendar->prodid = '-//SabreDAV//Sabre VObject ' . Sabre_VObject_Version::VERSION . '//EN'; $calendar->calscale = 'GREGORIAN'; } $vfreebusy = new Sabre_VObject_Component('VFREEBUSY'); $calendar->add($vfreebusy); if ($this->start) { $dtstart = new Sabre_VObject_Property_DateTime('DTSTART'); $dtstart->setDateTime($this->start, Sabre_VObject_Property_DateTime::UTC); $vfreebusy->add($dtstart); } if ($this->end) { $dtend = new Sabre_VObject_Property_DateTime('DTEND'); $dtend->setDateTime($this->start, Sabre_VObject_Property_DateTime::UTC); $vfreebusy->add($dtend); } $dtstamp = new Sabre_VObject_Property_DateTime('DTSTAMP'); $dtstamp->setDateTime(new DateTime('now'), Sabre_VObject_Property_DateTime::UTC); $vfreebusy->add($dtstamp); foreach ($busyTimes as $busyTime) { $busyTime[0]->setTimeZone(new DateTimeZone('UTC')); $busyTime[1]->setTimeZone(new DateTimeZone('UTC')); $prop = new Sabre_VObject_Property('FREEBUSY', $busyTime[0]->format('Ymd\\THis\\Z') . '/' . $busyTime[1]->format('Ymd\\THis\\Z')); $prop['FBTYPE'] = $busyTime[2]; $vfreebusy->add($prop); } return $calendar; }
/** * @depends testParseICalendarDate * @expectedException Sabre_DAV_Exception_BadRequest */ function testParseICalendarDateBadFormat() { $dateTime = Sabre_VObject_DateTimeParser::parseDate('20100316T141405'); }
/** * Validates if a component matches the given time range. * * This is all based on the rules specified in rfc4791, which are quite * complex. * * @param Sabre_VObject_Node $component * @param DateTime $start * @param DateTime $end * @return bool */ protected function validateTimeRange(Sabre_VObject_Node $component, $start, $end) { if (is_null($start)) { $start = new DateTime('1900-01-01'); } if (is_null($end)) { $end = new DateTime('3000-01-01'); } switch ($component->name) { case 'VEVENT': case 'VTODO': case 'VJOURNAL': return $component->isInTimeRange($start, $end); case 'VFREEBUSY': throw new Sabre_DAV_Exception_NotImplemented('time-range filters are currently not supported on ' . $component->name . ' components'); case 'VALARM': $trigger = $component->TRIGGER; if (!isset($trigger['TYPE']) || strtoupper($trigger['TYPE']) === 'DURATION') { $triggerDuration = Sabre_VObject_DateTimeParser::parseDuration($component->TRIGGER); $related = isset($trigger['RELATED']) && strtoupper($trigger['RELATED']) == 'END' ? 'END' : 'START'; $parentComponent = $component->parent; if ($related === 'START') { $effectiveTrigger = clone $parentComponent->DTSTART->getDateTime(); $effectiveTrigger->add($triggerDuration); } else { if ($parentComponent->name === 'VTODO') { $endProp = 'DUE'; } elseif ($parentComponent->name === 'VEVENT') { $endProp = 'DTEND'; } else { throw new Sabre_DAV_Exception('time-range filters on VALARM components are only supported when they are a child of VTODO or VEVENT'); } if (isset($parentComponent->{$endProp})) { $effectiveTrigger = clone $parentComponent->{$endProp}->getDateTime(); $effectiveTrigger->add($triggerDuration); } elseif (isset($parentComponent->DURATION)) { $effectiveTrigger = clone $parentComponent->DTSTART->getDateTime(); $duration = Sabre_VObject_DateTimeParser::parseDuration($parentComponent->DURATION); $effectiveTrigger->add($duration); $effectiveTrigger->add($triggerDuration); } else { $effectiveTrigger = clone $parentComponent->DTSTART->getDateTime(); $effectiveTrigger->add($triggerDuration); } } } else { $effectiveTrigger = $trigger->getDateTime(); } if (isset($component->DURATION)) { $duration = Sabre_VObject_DateTimeParser::parseDuration($component->DURATION); $repeat = (string) $component->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; } break; case 'COMPLETED': case 'CREATED': case 'DTEND': case 'DTSTAMP': case 'DTSTART': case 'DUE': case 'LAST-MODIFIED': return $start <= $component->getDateTime() && $end >= $component->getDateTime(); default: throw new Sabre_DAV_Exception_BadRequest('You cannot create a time-range filter on a ' . $component->name . ' component'); } }