Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F36623320
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
14 KB
Subscribers
None
View Options
diff --git a/src/parser/calendar/data/PhutilCalendarEventNode.php b/src/parser/calendar/data/PhutilCalendarEventNode.php
index 44184e8..12c4f35 100644
--- a/src/parser/calendar/data/PhutilCalendarEventNode.php
+++ b/src/parser/calendar/data/PhutilCalendarEventNode.php
@@ -1,179 +1,189 @@
<?php
final class PhutilCalendarEventNode
extends PhutilCalendarNode {
const NODETYPE = 'event';
private $uid;
private $name;
private $description;
private $startDateTime;
private $endDateTime;
private $duration;
private $createdDateTime;
private $modifiedDateTime;
private $organizer;
private $attendees = array();
private $recurrenceRule;
private $recurrenceExceptions = array();
private $recurrenceDates = array();
+ private $recurrenceID;
public function setUID($uid) {
$this->uid = $uid;
return $this;
}
public function getUID() {
return $this->uid;
}
public function setName($name) {
$this->name = $name;
return $this;
}
public function getName() {
return $this->name;
}
public function setDescription($description) {
$this->description = $description;
return $this;
}
public function getDescription() {
return $this->description;
}
public function setStartDateTime(PhutilCalendarDateTime $start) {
$this->startDateTime = $start;
return $this;
}
public function getStartDateTime() {
return $this->startDateTime;
}
public function setEndDateTime(PhutilCalendarDateTime $end) {
$this->endDateTime = $end;
return $this;
}
public function getEndDateTime() {
$end = $this->endDateTime;
if ($end) {
return $end;
}
$start = $this->getStartDateTime();
$duration = $this->getDuration();
if ($start && $duration) {
return id(new PhutilCalendarRelativeDateTime())
->setOrigin($start)
->setDuration($duration);
}
return null;
}
public function setDuration(PhutilCalendarDuration $duration) {
$this->duration = $duration;
return $this;
}
public function getDuration() {
return $this->duration;
}
public function setCreatedDateTime(PhutilCalendarDateTime $created) {
$this->createdDateTime = $created;
return $this;
}
public function getCreatedDateTime() {
return $this->createdDateTime;
}
public function setModifiedDateTime(PhutilCalendarDateTime $modified) {
$this->modifiedDateTime = $modified;
return $this;
}
public function getModifiedDateTime() {
return $this->modifiedDateTime;
}
public function setOrganizer(PhutilCalendarUserNode $organizer) {
$this->organizer = $organizer;
return $this;
}
public function getOrganizer() {
return $this->organizer;
}
public function setAttendees(array $attendees) {
assert_instances_of($attendees, 'PhutilCalendarUserNode');
$this->attendees = $attendees;
return $this;
}
public function getAttendees() {
return $this->attendees;
}
public function addAttendee(PhutilCalendarUserNode $attendee) {
$this->attendees[] = $attendee;
return $this;
}
public function setRecurrenceRule(
PhutilCalendarRecurrenceRule $recurrence_rule) {
$this->recurrenceRule = $recurrence_rule;
return $this;
}
public function getRecurrenceRule() {
return $this->recurrenceRule;
}
public function setRecurrenceUntilDateTime(PhutilCalendarDateTime $date) {
$this->recurrenceUntilDateTime = $date;
return $this;
}
public function getRecurrenceUntilDateTime() {
return $this->recurrenceUntilDateTime;
}
public function setRecurrenceCount($recurrence_count) {
$this->recurrenceCount = $recurrence_count;
return $this;
}
public function getRecurrenceCount() {
return $this->recurrenceCount;
}
public function setRecurrenceExceptions(array $recurrence_exceptions) {
assert_instances_of($recurrence_exceptions, 'PhutilCalendarDateTime');
$this->recurrenceExceptions = $recurrence_exceptions;
return $this;
}
public function getRecurrenceExceptions() {
return $this->recurrenceExceptions;
}
public function setRecurrenceDates(array $recurrence_dates) {
assert_instances_of($recurrence_dates, 'PhutilCalendarDateTime');
$this->recurrenceDates = $recurrence_dates;
return $this;
}
public function getRecurrenceDates() {
return $this->recurrenceDates;
}
+ public function setRecurrenceID($recurrence_id) {
+ $this->recurrenceID = $recurrence_id;
+ return $this;
+ }
+
+ public function getRecurrenceID() {
+ return $this->recurrenceID;
+ }
+
}
diff --git a/src/parser/calendar/ics/PhutilICSWriter.php b/src/parser/calendar/ics/PhutilICSWriter.php
index 5935b1d..c5baa79 100644
--- a/src/parser/calendar/ics/PhutilICSWriter.php
+++ b/src/parser/calendar/ics/PhutilICSWriter.php
@@ -1,380 +1,387 @@
<?php
final class PhutilICSWriter extends Phobject {
public function writeICSDocument(PhutilCalendarRootNode $node) {
$out = array();
foreach ($node->getChildren() as $child) {
$out[] = $this->writeNode($child);
}
return implode('', $out);
}
private function writeNode(PhutilCalendarNode $node) {
if (!$this->getICSNodeType($node)) {
return null;
}
$out = array();
$out[] = $this->writeBeginNode($node);
$out[] = $this->writeNodeProperties($node);
if ($node instanceof PhutilCalendarContainerNode) {
foreach ($node->getChildren() as $child) {
$out[] = $this->writeNode($child);
}
}
$out[] = $this->writeEndNode($node);
return implode('', $out);
}
private function writeBeginNode(PhutilCalendarNode $node) {
$type = $this->getICSNodeType($node);
return $this->wrapICSLine("BEGIN:{$type}");
}
private function writeEndNode(PhutilCalendarNode $node) {
$type = $this->getICSNodeType($node);
return $this->wrapICSLine("END:{$type}");
}
private function writeNodeProperties(PhutilCalendarNode $node) {
$properties = $this->getNodeProperties($node);
$out = array();
foreach ($properties as $property) {
$propname = $property['name'];
$propvalue = $property['value'];
$propline = array();
$propline[] = $propname;
foreach ($property['parameters'] as $parameter) {
$paramname = $parameter['name'];
$paramvalue = $parameter['value'];
$propline[] = ";{$paramname}={$paramvalue}";
}
$propline[] = ":{$propvalue}";
$propline = implode('', $propline);
$out[] = $this->wrapICSLine($propline);
}
return implode('', $out);
}
private function getICSNodeType(PhutilCalendarNode $node) {
switch ($node->getNodeType()) {
case PhutilCalendarDocumentNode::NODETYPE:
return 'VCALENDAR';
case PhutilCalendarEventNode::NODETYPE:
return 'VEVENT';
default:
return null;
}
}
private function wrapICSLine($line) {
$out = array();
$buf = '';
// NOTE: The line may contain sequences of combining characters which are
// more than 80 bytes in length. If it does, we'll split them in the
// middle of the sequence. This is okay and generally anticipated by
// RFC5545, which even allows implementations to split multibyte
// characters. The sequence will be stitched back together properly by
// whatever is parsing things.
foreach (phutil_utf8v($line) as $character) {
// If adding this character would bring the line over 75 bytes, start
// a new line.
if (strlen($buf) + strlen($character) > 75) {
$out[] = $buf."\r\n";
$buf = ' ';
}
$buf .= $character;
}
$out[] = $buf."\r\n";
return implode('', $out);
}
private function getNodeProperties(PhutilCalendarNode $node) {
switch ($node->getNodeType()) {
case PhutilCalendarDocumentNode::NODETYPE:
return $this->getDocumentNodeProperties($node);
case PhutilCalendarEventNode::NODETYPE:
return $this->getEventNodeProperties($node);
default:
return array();
}
}
private function getDocumentNodeProperties(
PhutilCalendarDocumentNode $event) {
$properties = array();
$properties[] = $this->newTextProperty(
'VERSION',
'2.0');
$properties[] = $this->newTextProperty(
'PRODID',
'-//Phacility//Phabricator//EN');
return $properties;
}
private function getEventNodeProperties(PhutilCalendarEventNode $event) {
$properties = array();
$uid = $event->getUID();
if (!strlen($uid)) {
throw new Exception(
pht(
'Unable to write ICS document: event has no UID, but each event '.
'MUST have a UID.'));
}
$properties[] = $this->newTextProperty(
'UID',
$uid);
$created = $event->getCreatedDateTime();
if ($created) {
$properties[] = $this->newDateTimeProperty(
'CREATED',
$event->getCreatedDateTime());
}
$dtstamp = $event->getModifiedDateTime();
if (!$dtstamp) {
throw new Exception(
pht(
'Unable to write ICS document: event has no modified time, but '.
'each event MUST have a modified time.'));
}
$properties[] = $this->newDateTimeProperty(
'DTSTAMP',
$dtstamp);
$dtstart = $event->getStartDateTime();
if ($dtstart) {
$properties[] = $this->newDateTimeProperty(
'DTSTART',
$dtstart);
}
$dtend = $event->getEndDateTime();
if ($dtend) {
$properties[] = $this->newDateTimeProperty(
'DTEND',
$event->getEndDateTime());
}
$name = $event->getName();
if (strlen($name)) {
$properties[] = $this->newTextProperty(
'SUMMARY',
$name);
}
$description = $event->getDescription();
if (strlen($description)) {
$properties[] = $this->newTextProperty(
'DESCRIPTION',
$description);
}
$organizer = $event->getOrganizer();
if ($organizer) {
$properties[] = $this->newUserProperty(
'ORGANIZER',
$organizer);
}
$attendees = $event->getAttendees();
if ($attendees) {
foreach ($attendees as $attendee) {
$properties[] = $this->newUserProperty(
'ATTENDEE',
$attendee);
}
}
$rrule = $event->getRecurrenceRule();
if ($rrule) {
$properties[] = $this->newRRULEProperty(
'RRULE',
$rrule);
}
+ $recurrence_id = $event->getRecurrenceID();
+ if ($recurrence_id) {
+ $properties[] = $this->newTextProperty(
+ 'RECURRENCE-ID',
+ $recurrence_id);
+ }
+
$exdates = $event->getRecurrenceExceptions();
if ($exdates) {
$properties[] = $this->newDateTimesProperty(
'EXDATE',
$exdates);
}
$rdates = $event->getRecurrenceDates();
if ($rdates) {
$properties[] = $this->newDateTimesProperty(
'RDATE',
$rdates);
}
return $properties;
}
private function newTextProperty(
$name,
$value,
array $parameters = array()) {
$map = array(
'\\' => '\\\\',
',' => '\\,',
"\n" => '\\n',
);
$value = (array)$value;
foreach ($value as $k => $v) {
$v = str_replace(array_keys($map), array_values($map), $v);
$value[$k] = $v;
}
$value = implode(',', $value);
return $this->newProperty($name, $value, $parameters);
}
private function newDateTimeProperty(
$name,
PhutilCalendarDateTime $value,
array $parameters = array()) {
return $this->newDateTimesProperty($name, array($value), $parameters);
}
private function newDateTimesProperty(
$name,
array $values,
array $parameters = array()) {
assert_instances_of($values, 'PhutilCalendarDateTime');
if (head($values)->getIsAllDay()) {
$parameters[] = array(
'name' => 'VALUE',
'values' => array(
'DATE',
),
);
}
$datetimes = array();
foreach ($values as $value) {
$datetimes[] = $value->getISO8601();
}
$datetimes = implode(';', $datetimes);
return $this->newProperty($name, $datetimes, $parameters);
}
private function newUserProperty(
$name,
PhutilCalendarUserNode $value,
array $parameters = array()) {
$parameters[] = array(
'name' => 'CN',
'values' => array(
$value->getName(),
),
);
$partstat = null;
switch ($value->getStatus()) {
case PhutilCalendarUserNode::STATUS_INVITED:
$partstat = 'NEEDS-ACTION';
break;
case PhutilCalendarUserNode::STATUS_ACCEPTED:
$partstat = 'ACCEPTED';
break;
case PhutilCalendarUserNode::STATUS_DECLINED:
$partstat = 'DECLINED';
break;
}
if ($partstat !== null) {
$parameters[] = array(
'name' => 'PARTSTAT',
'values' => array(
$partstat,
),
);
}
// TODO: We could reasonably fill in "ROLE" and "RSVP" here too, but it
// isn't clear if these are important to external programs or not.
return $this->newProperty($name, $value->getURI(), $parameters);
}
private function newRRULEProperty(
$name,
PhutilCalendarRecurrenceRule $rule,
array $parameters = array()) {
$value = $rule->toRRULE();
return $this->newProperty($name, $value, $parameters);
}
private function newProperty(
$name,
$value,
array $parameters = array()) {
$map = array(
'^' => '^^',
"\n" => '^n',
'"' => "^'",
);
$writable_params = array();
foreach ($parameters as $k => $parameter) {
$value_list = array();
foreach ($parameter['values'] as $v) {
$v = str_replace(array_keys($map), array_values($map), $v);
// If the parameter value isn't a very simple one, quote it.
// RFC5545 says that we MUST quote it if it has a colon, a semicolon,
// or a comma, and that we MUST quote it if it's a URI.
if (!preg_match('/^[A-Za-z0-9-]*\z/', $v)) {
$v = '"'.$v.'"';
}
$value_list[] = $v;
}
$writable_params[] = array(
'name' => $parameter['name'],
'value' => implode(',', $value_list),
);
}
return array(
'name' => $name,
'value' => $value,
'parameters' => $writable_params,
);
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Feb 26, 6:44 PM (18 h, 52 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
5f/8e/8cb6d90ef4bc61794060dfe60dfd
Attached To
rPHUTIL libphutil
Event Timeline
Log In to Comment