Changeset View
Changeset View
Standalone View
Standalone View
src/applications/calendar/storage/PhabricatorCalendarEvent.php
Show All 16 Lines | final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO | ||||
protected $userPHID; | protected $userPHID; | ||||
protected $dateFrom; | protected $dateFrom; | ||||
protected $dateTo; | protected $dateTo; | ||||
protected $description; | protected $description; | ||||
protected $isCancelled; | protected $isCancelled; | ||||
protected $isAllDay; | protected $isAllDay; | ||||
protected $icon; | protected $icon; | ||||
protected $mailKey; | protected $mailKey; | ||||
protected $isStub; | |||||
protected $isRecurring = 0; | protected $isRecurring = 0; | ||||
protected $recurrenceFrequency = array(); | protected $recurrenceFrequency = array(); | ||||
protected $recurrenceEndDate; | protected $recurrenceEndDate; | ||||
private $isGhostEvent = false; | private $isGhostEvent = false; | ||||
protected $instanceOfEventPHID; | protected $instanceOfEventPHID; | ||||
protected $sequenceIndex; | protected $sequenceIndex; | ||||
Show All 33 Lines | public static function initializeNewCalendarEvent( | ||||
if ($mode == 'recurring') { | if ($mode == 'recurring') { | ||||
$is_recurring = true; | $is_recurring = true; | ||||
} | } | ||||
return id(new PhabricatorCalendarEvent()) | return id(new PhabricatorCalendarEvent()) | ||||
->setUserPHID($actor->getPHID()) | ->setUserPHID($actor->getPHID()) | ||||
->setIsCancelled(0) | ->setIsCancelled(0) | ||||
->setIsAllDay(0) | ->setIsAllDay(0) | ||||
->setIsStub(0) | |||||
->setIsRecurring($is_recurring) | ->setIsRecurring($is_recurring) | ||||
->setIcon(self::DEFAULT_ICON) | ->setIcon(self::DEFAULT_ICON) | ||||
->setViewPolicy($view_policy) | ->setViewPolicy($view_policy) | ||||
->setEditPolicy($actor->getPHID()) | ->setEditPolicy($actor->getPHID()) | ||||
->setSpacePHID($actor->getDefaultSpacePHID()) | ->setSpacePHID($actor->getDefaultSpacePHID()) | ||||
->attachInvitees(array()) | ->attachInvitees(array()) | ||||
->applyViewerTimezone($actor); | ->applyViewerTimezone($actor); | ||||
} | } | ||||
private function newChild(PhabricatorUser $actor, $sequence) { | |||||
if (!$this->isParentEvent()) { | |||||
throw new Exception( | |||||
pht( | |||||
'Unable to generate a new child event for an event which is not '. | |||||
'a recurring parent event!')); | |||||
} | |||||
$child = id(new self()) | |||||
->setIsCancelled(0) | |||||
->setIsStub(0) | |||||
->setInstanceOfEventPHID($this->getPHID()) | |||||
->setSequenceIndex($sequence) | |||||
->setIsRecurring(true) | |||||
->setRecurrenceFrequency($this->getRecurrenceFrequency()) | |||||
->attachParentEvent($this); | |||||
return $child->copyFromParent($actor); | |||||
} | |||||
protected function readField($field) { | |||||
static $inherit = array( | |||||
'userPHID' => true, | |||||
'isAllDay' => true, | |||||
'icon' => true, | |||||
'spacePHID' => true, | |||||
'viewPolicy' => true, | |||||
'editPolicy' => true, | |||||
'name' => true, | |||||
'description' => true, | |||||
); | |||||
// Read these fields from the parent event instead of this event. For | |||||
// example, we want any changes to the parent event's name to | |||||
if (isset($inherit[$field])) { | |||||
if ($this->getIsStub()) { | |||||
// TODO: This should be unconditional, but the execution order of | |||||
// CalendarEventQuery and applyViewerTimezone() are currently odd. | |||||
if ($this->parentEvent !== self::ATTACHABLE) { | |||||
return $this->getParentEvent()->readField($field); | |||||
} | |||||
} | |||||
} | |||||
return parent::readField($field); | |||||
} | |||||
public function copyFromParent(PhabricatorUser $actor) { | |||||
if (!$this->isChildEvent()) { | |||||
throw new Exception( | |||||
pht( | |||||
'Unable to copy from parent event: this is not a child event.')); | |||||
} | |||||
$parent = $this->getParentEvent(); | |||||
$this | |||||
->setUserPHID($parent->getUserPHID()) | |||||
->setIsAllDay($parent->getIsAllDay()) | |||||
->setIcon($parent->getIcon()) | |||||
->setSpacePHID($parent->getSpacePHID()) | |||||
->setViewPolicy($parent->getViewPolicy()) | |||||
->setEditPolicy($parent->getEditPolicy()) | |||||
->setName($parent->getName()) | |||||
->setDescription($parent->getDescription()); | |||||
$frequency = $parent->getFrequencyUnit(); | |||||
$modify_key = '+'.$this->getSequenceIndex().' '.$frequency; | |||||
$date = $parent->getDateFrom(); | |||||
$date_time = PhabricatorTime::getDateTimeFromEpoch($date, $actor); | |||||
$date_time->modify($modify_key); | |||||
$date = $date_time->format('U'); | |||||
$duration = $parent->getDateTo() - $parent->getDateFrom(); | |||||
$this | |||||
->setDateFrom($date) | |||||
->setDateTo($date + $duration); | |||||
return $this; | |||||
} | |||||
public function newStub(PhabricatorUser $actor, $sequence) { | |||||
$stub = $this->newChild($actor, $sequence); | |||||
$stub->setIsStub(1); | |||||
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); | |||||
$stub->save(); | |||||
unset($unguarded); | |||||
$stub->applyViewerTimezone($actor); | |||||
return $stub; | |||||
} | |||||
public function newGhost(PhabricatorUser $actor, $sequence) { | |||||
$ghost = $this->newChild($actor, $sequence); | |||||
$ghost | |||||
->setIsGhostEvent(true) | |||||
->makeEphemeral(); | |||||
$ghost->applyViewerTimezone($actor); | |||||
return $ghost; | |||||
} | |||||
public function applyViewerTimezone(PhabricatorUser $viewer) { | public function applyViewerTimezone(PhabricatorUser $viewer) { | ||||
if ($this->appliedViewer) { | if ($this->appliedViewer) { | ||||
throw new Exception(pht('Viewer timezone is already applied!')); | throw new Exception(pht('Viewer timezone is already applied!')); | ||||
} | } | ||||
$this->appliedViewer = $viewer; | $this->appliedViewer = $viewer; | ||||
if (!$this->getIsAllDay()) { | if (!$this->getIsAllDay()) { | ||||
▲ Show 20 Lines • Show All 115 Lines • ▼ Show 20 Lines | return array( | ||||
'isCancelled' => 'bool', | 'isCancelled' => 'bool', | ||||
'isAllDay' => 'bool', | 'isAllDay' => 'bool', | ||||
'icon' => 'text32', | 'icon' => 'text32', | ||||
'mailKey' => 'bytes20', | 'mailKey' => 'bytes20', | ||||
'isRecurring' => 'bool', | 'isRecurring' => 'bool', | ||||
'recurrenceEndDate' => 'epoch?', | 'recurrenceEndDate' => 'epoch?', | ||||
'instanceOfEventPHID' => 'phid?', | 'instanceOfEventPHID' => 'phid?', | ||||
'sequenceIndex' => 'uint32?', | 'sequenceIndex' => 'uint32?', | ||||
'isStub' => 'bool', | |||||
), | ), | ||||
self::CONFIG_KEY_SCHEMA => array( | self::CONFIG_KEY_SCHEMA => array( | ||||
'userPHID_dateFrom' => array( | 'userPHID_dateFrom' => array( | ||||
'columns' => array('userPHID', 'dateTo'), | 'columns' => array('userPHID', 'dateTo'), | ||||
), | ), | ||||
'key_instance' => array( | 'key_instance' => array( | ||||
'columns' => array('instanceOfEventPHID', 'sequenceIndex'), | 'columns' => array('instanceOfEventPHID', 'sequenceIndex'), | ||||
'unique' => true, | 'unique' => true, | ||||
▲ Show 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | public function getIsGhostEvent() { | ||||
return $this->isGhostEvent; | return $this->isGhostEvent; | ||||
} | } | ||||
public function setIsGhostEvent($is_ghost_event) { | public function setIsGhostEvent($is_ghost_event) { | ||||
$this->isGhostEvent = $is_ghost_event; | $this->isGhostEvent = $is_ghost_event; | ||||
return $this; | return $this; | ||||
} | } | ||||
public function generateNthGhost( | |||||
$sequence_index, | |||||
PhabricatorUser $actor) { | |||||
$frequency = $this->getFrequencyUnit(); | |||||
$modify_key = '+'.$sequence_index.' '.$frequency; | |||||
$instance_of = ($this->getPHID()) ? | |||||
$this->getPHID() : $this->instanceOfEventPHID; | |||||
$date = $this->dateFrom; | |||||
$date_time = PhabricatorTime::getDateTimeFromEpoch($date, $actor); | |||||
$date_time->modify($modify_key); | |||||
$date = $date_time->format('U'); | |||||
$duration = $this->dateTo - $this->dateFrom; | |||||
$edit_policy = PhabricatorPolicies::POLICY_NOONE; | |||||
$ghost_event = id(clone $this) | |||||
->setIsGhostEvent(true) | |||||
->setDateFrom($date) | |||||
->setDateTo($date + $duration) | |||||
->setIsRecurring(true) | |||||
->setRecurrenceFrequency($this->recurrenceFrequency) | |||||
->setInstanceOfEventPHID($instance_of) | |||||
->setSequenceIndex($sequence_index) | |||||
->setEditPolicy($edit_policy); | |||||
return $ghost_event; | |||||
} | |||||
public function getFrequencyUnit() { | public function getFrequencyUnit() { | ||||
$frequency = idx($this->recurrenceFrequency, 'rule'); | $frequency = idx($this->recurrenceFrequency, 'rule'); | ||||
switch ($frequency) { | switch ($frequency) { | ||||
case 'daily': | case 'daily': | ||||
return 'day'; | return 'day'; | ||||
case 'weekly': | case 'weekly': | ||||
return 'week'; | return 'week'; | ||||
case 'monthly': | case 'monthly': | ||||
return 'month'; | return 'month'; | ||||
case 'yearly': | case 'yearly': | ||||
return 'year'; | return 'year'; | ||||
default: | default: | ||||
return 'day'; | return 'day'; | ||||
} | } | ||||
} | } | ||||
public function getURI() { | public function getURI() { | ||||
$uri = '/'.$this->getMonogram(); | if ($this->getIsGhostEvent()) { | ||||
if ($this->isGhostEvent) { | $base = $this->getParentEvent()->getURI(); | ||||
$uri = $uri.'/'.$this->sequenceIndex; | $sequence = $this->getSequenceIndex(); | ||||
return "{$base}/{$sequence}/"; | |||||
} | } | ||||
return $uri; | |||||
return '/'.$this->getMonogram(); | |||||
} | } | ||||
public function getParentEvent() { | public function getParentEvent() { | ||||
return $this->assertAttached($this->parentEvent); | return $this->assertAttached($this->parentEvent); | ||||
} | } | ||||
public function attachParentEvent($event) { | public function attachParentEvent($event) { | ||||
$this->parentEvent = $event; | $this->parentEvent = $event; | ||||
return $this; | return $this; | ||||
} | } | ||||
public function getIsCancelled() { | public function isParentEvent() { | ||||
$instance_of = $this->instanceOfEventPHID; | return ($this->isRecurring && !$this->instanceOfEventPHID); | ||||
if ($instance_of != null && $this->getIsParentCancelled()) { | |||||
return true; | |||||
} | } | ||||
return $this->isCancelled; | |||||
public function isChildEvent() { | |||||
return ($this->instanceOfEventPHID !== null); | |||||
} | } | ||||
public function getIsRecurrenceParent() { | public function isCancelledEvent() { | ||||
if ($this->isRecurring && !$this->instanceOfEventPHID) { | if ($this->getIsCancelled()) { | ||||
return true; | return true; | ||||
} | } | ||||
return false; | |||||
} | |||||
public function getIsRecurrenceException() { | if ($this->isChildEvent()) { | ||||
if ($this->instanceOfEventPHID && !$this->isGhostEvent) { | if ($this->getParentEvent()->getIsCancelled()) { | ||||
return true; | return true; | ||||
} | } | ||||
return false; | |||||
} | } | ||||
public function getIsParentCancelled() { | |||||
if ($this->instanceOfEventPHID == null) { | |||||
return false; | |||||
} | |||||
$recurring_event = $this->getParentEvent(); | |||||
if ($recurring_event->getIsCancelled()) { | |||||
return true; | |||||
} | |||||
return false; | return false; | ||||
} | } | ||||
public function getDuration() { | public function getDuration() { | ||||
$seconds = $this->dateTo - $this->dateFrom; | $seconds = $this->dateTo - $this->dateFrom; | ||||
$minutes = round($seconds / 60, 1); | $minutes = round($seconds / 60, 1); | ||||
$hours = round($minutes / 60, 3); | $hours = round($minutes / 60, 3); | ||||
$days = round($hours / 24, 2); | $days = round($hours / 24, 2); | ||||
Show All 10 Lines | if ($days >= 1) { | ||||
round($hours, 1)); | round($hours, 1)); | ||||
} else if ($minutes >= 1) { | } else if ($minutes >= 1) { | ||||
return pht( | return pht( | ||||
'%s minute(s)', | '%s minute(s)', | ||||
round($minutes, 0)); | round($minutes, 0)); | ||||
} | } | ||||
} | } | ||||
/* -( Markup Interface )--------------------------------------------------- */ | /* -( Markup Interface )--------------------------------------------------- */ | ||||
/** | /** | ||||
* @task markup | * @task markup | ||||
*/ | */ | ||||
public function getMarkupFieldKey($field) { | public function getMarkupFieldKey($field) { | ||||
$hash = PhabricatorHash::digest($this->getMarkupText($field)); | $hash = PhabricatorHash::digest($this->getMarkupText($field)); | ||||
▲ Show 20 Lines • Show All 149 Lines • Show Last 20 Lines |