Changeset View
Changeset View
Standalone View
Standalone View
src/applications/calendar/storage/PhabricatorCalendarEvent.php
Show All 35 Lines | final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO | ||||
protected $editPolicy; | protected $editPolicy; | ||||
protected $spacePHID; | protected $spacePHID; | ||||
const DEFAULT_ICON = 'fa-calendar'; | const DEFAULT_ICON = 'fa-calendar'; | ||||
private $parentEvent = self::ATTACHABLE; | private $parentEvent = self::ATTACHABLE; | ||||
private $invitees = self::ATTACHABLE; | private $invitees = self::ATTACHABLE; | ||||
private $appliedViewer; | |||||
private $viewerDateFrom; | |||||
private $viewerDateTo; | |||||
// Frequency Constants | // Frequency Constants | ||||
const FREQUENCY_DAILY = 'daily'; | const FREQUENCY_DAILY = 'daily'; | ||||
const FREQUENCY_WEEKLY = 'weekly'; | const FREQUENCY_WEEKLY = 'weekly'; | ||||
const FREQUENCY_MONTHLY = 'monthly'; | const FREQUENCY_MONTHLY = 'monthly'; | ||||
const FREQUENCY_YEARLY = 'yearly'; | const FREQUENCY_YEARLY = 'yearly'; | ||||
public static function initializeNewCalendarEvent( | public static function initializeNewCalendarEvent( | ||||
▲ Show 20 Lines • Show All 99 Lines • ▼ Show 20 Lines | public function copyFromParent(PhabricatorUser $actor) { | ||||
$frequency = $parent->getFrequencyUnit(); | $frequency = $parent->getFrequencyUnit(); | ||||
$modify_key = '+'.$this->getSequenceIndex().' '.$frequency; | $modify_key = '+'.$this->getSequenceIndex().' '.$frequency; | ||||
$date = $parent->getDateFrom(); | $date = $parent->getDateFrom(); | ||||
$date_time = PhabricatorTime::getDateTimeFromEpoch($date, $actor); | $date_time = PhabricatorTime::getDateTimeFromEpoch($date, $actor); | ||||
$date_time->modify($modify_key); | $date_time->modify($modify_key); | ||||
$date = $date_time->format('U'); | $date = $date_time->format('U'); | ||||
$duration = $parent->getDateTo() - $parent->getDateFrom(); | $duration = $this->getDuration(); | ||||
$this | $this | ||||
->setDateFrom($date) | ->setDateFrom($date) | ||||
->setDateTo($date + $duration); | ->setDateTo($date + $duration); | ||||
return $this; | return $this; | ||||
} | } | ||||
Show All 18 Lines | $ghost | ||||
->setIsGhostEvent(true) | ->setIsGhostEvent(true) | ||||
->makeEphemeral(); | ->makeEphemeral(); | ||||
$ghost->applyViewerTimezone($actor); | $ghost->applyViewerTimezone($actor); | ||||
return $ghost; | return $ghost; | ||||
} | } | ||||
public function applyViewerTimezone(PhabricatorUser $viewer) { | public function getViewerDateFrom() { | ||||
if ($this->appliedViewer) { | if ($this->viewerDateFrom === null) { | ||||
throw new Exception(pht('Viewer timezone is already applied!')); | throw new PhutilInvalidStateException('applyViewerTimezone'); | ||||
} | } | ||||
$this->appliedViewer = $viewer; | return $this->viewerDateFrom; | ||||
if (!$this->getIsAllDay()) { | |||||
return $this; | |||||
} | |||||
$zone = $viewer->getTimeZone(); | |||||
$this->setDateFrom( | |||||
$this->getDateEpochForTimeZone( | |||||
$this->getDateFrom(), | |||||
new DateTimeZone('Pacific/Kiritimati'), | |||||
'Y-m-d', | |||||
null, | |||||
$zone)); | |||||
$this->setDateTo( | |||||
$this->getDateEpochForTimeZone( | |||||
$this->getDateTo(), | |||||
new DateTimeZone('Pacific/Midway'), | |||||
'Y-m-d 23:59:00', | |||||
'-1 day', | |||||
$zone)); | |||||
return $this; | |||||
} | } | ||||
public function getViewerDateTo() { | |||||
public function removeViewerTimezone(PhabricatorUser $viewer) { | if ($this->viewerDateTo === null) { | ||||
if (!$this->appliedViewer) { | throw new PhutilInvalidStateException('applyViewerTimezone'); | ||||
throw new Exception(pht('Viewer timezone is not applied!')); | |||||
} | } | ||||
if ($viewer->getPHID() != $this->appliedViewer->getPHID()) { | return $this->viewerDateTo; | ||||
throw new Exception(pht('Removed viewer must match applied viewer!')); | |||||
} | } | ||||
$this->appliedViewer = null; | public function applyViewerTimezone(PhabricatorUser $viewer) { | ||||
if (!$this->getIsAllDay()) { | if (!$this->getIsAllDay()) { | ||||
return $this; | $this->viewerDateFrom = $this->getDateFrom(); | ||||
} | $this->viewerDateTo = $this->getDateTo(); | ||||
} else { | |||||
$zone = $viewer->getTimeZone(); | $zone = $viewer->getTimeZone(); | ||||
$this->setDateFrom( | $this->viewerDateFrom = $this->getDateEpochForTimeZone( | ||||
$this->getDateEpochForTimeZone( | |||||
$this->getDateFrom(), | $this->getDateFrom(), | ||||
$zone, | new DateTimeZone('UTC'), | ||||
'Y-m-d', | 'Y-m-d', | ||||
null, | null, | ||||
new DateTimeZone('Pacific/Kiritimati'))); | $zone); | ||||
$this->setDateTo( | $this->viewerDateTo = $this->getDateEpochForTimeZone( | ||||
$this->getDateEpochForTimeZone( | |||||
$this->getDateTo(), | $this->getDateTo(), | ||||
$zone, | new DateTimeZone('UTC'), | ||||
'Y-m-d', | 'Y-m-d 23:59:00', | ||||
'+1 day', | null, | ||||
new DateTimeZone('Pacific/Midway'))); | $zone); | ||||
} | |||||
return $this; | return $this; | ||||
} | } | ||||
public function getDuration() { | |||||
return $this->getDateTo() - $this->getDateFrom(); | |||||
} | |||||
private function getDateEpochForTimeZone( | private function getDateEpochForTimeZone( | ||||
$epoch, | $epoch, | ||||
$src_zone, | $src_zone, | ||||
$format, | $format, | ||||
$adjust, | $adjust, | ||||
$dst_zone) { | $dst_zone) { | ||||
$src = new DateTime('@'.$epoch); | $src = new DateTime('@'.$epoch); | ||||
$src->setTimeZone($src_zone); | $src->setTimeZone($src_zone); | ||||
if (strlen($adjust)) { | if (strlen($adjust)) { | ||||
$adjust = ' '.$adjust; | $adjust = ' '.$adjust; | ||||
} | } | ||||
$dst = new DateTime($src->format($format).$adjust, $dst_zone); | $dst = new DateTime($src->format($format).$adjust, $dst_zone); | ||||
return $dst->format('U'); | return $dst->format('U'); | ||||
} | } | ||||
public function save() { | public function save() { | ||||
if ($this->appliedViewer) { | |||||
throw new Exception( | |||||
pht( | |||||
'Can not save event with viewer timezone still applied!')); | |||||
} | |||||
if (!$this->mailKey) { | if (!$this->mailKey) { | ||||
$this->mailKey = Filesystem::readRandomCharacters(20); | $this->mailKey = Filesystem::readRandomCharacters(20); | ||||
} | } | ||||
return parent::save(); | return parent::save(); | ||||
} | } | ||||
/** | /** | ||||
* Get the event start epoch for evaluating invitee availability. | * Get the event start epoch for evaluating invitee availability. | ||||
* | * | ||||
* When assessing availability, we pretend events start earlier than they | * When assessing availability, we pretend events start earlier than they | ||||
* really. This allows us to mark users away for the entire duration of a | * really do. This allows us to mark users away for the entire duration of a | ||||
* series of back-to-back meetings, even if they don't strictly overlap. | * series of back-to-back meetings, even if they don't strictly overlap. | ||||
* | * | ||||
* @return int Event start date for availability caches. | * @return int Event start date for availability caches. | ||||
*/ | */ | ||||
public function getDateFromForCache() { | public function getDateFromForCache() { | ||||
return ($this->getDateFrom() - phutil_units('15 minutes in seconds')); | return ($this->getViewerDateFrom() - phutil_units('15 minutes in seconds')); | ||||
} | } | ||||
protected function getConfiguration() { | protected function getConfiguration() { | ||||
return array( | return array( | ||||
self::CONFIG_AUX_PHID => true, | self::CONFIG_AUX_PHID => true, | ||||
self::CONFIG_COLUMN_SCHEMA => array( | self::CONFIG_COLUMN_SCHEMA => array( | ||||
'name' => 'text', | 'name' => 'text', | ||||
'dateFrom' => 'epoch', | 'dateFrom' => 'epoch', | ||||
▲ Show 20 Lines • Show All 135 Lines • ▼ Show 20 Lines | if ($this->isChildEvent()) { | ||||
if ($this->getParentEvent()->getIsCancelled()) { | if ($this->getParentEvent()->getIsCancelled()) { | ||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
public function getDuration() { | public function getDisplayDuration() { | ||||
$seconds = $this->dateTo - $this->dateFrom; | $seconds = $this->getDuration(); | ||||
$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); | ||||
$duration = ''; | $duration = ''; | ||||
if ($days >= 1) { | if ($days >= 1) { | ||||
return pht( | return pht( | ||||
'%s day(s)', | '%s day(s)', | ||||
round($days, 1)); | round($days, 1)); | ||||
} else if ($hours >= 1) { | } else if ($hours >= 1) { | ||||
return pht( | return pht( | ||||
'%s hour(s)', | '%s hour(s)', | ||||
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 )--------------------------------------------------- */ | ||||
/** | /** | ||||
▲ Show 20 Lines • Show All 153 Lines • Show Last 20 Lines |