diff --git a/src/applications/calendar/controller/PhabricatorCalendarEventEditController.php b/src/applications/calendar/controller/PhabricatorCalendarEventEditController.php --- a/src/applications/calendar/controller/PhabricatorCalendarEventEditController.php +++ b/src/applications/calendar/controller/PhabricatorCalendarEventEditController.php @@ -74,6 +74,7 @@ $name = $event->getName(); $description = $event->getDescription(); $type = $event->getStatus(); + $is_all_day = $event->getIsAllDay(); $current_policies = id(new PhabricatorPolicyQuery()) ->setViewer($user) @@ -95,6 +96,7 @@ $subscribers = $request->getArr('subscribers'); $edit_policy = $request->getStr('editPolicy'); $view_policy = $request->getStr('viewPolicy'); + $is_all_day = $request->getStr('isAllDay'); $invitees = $request->getArr('invitees'); $new_invitees = $this->getNewInviteeList($invitees, $event); @@ -113,6 +115,11 @@ $xactions[] = id(new PhabricatorCalendarEventTransaction()) ->setTransactionType( + PhabricatorCalendarEventTransaction::TYPE_ALL_DAY) + ->setNewValue($is_all_day); + + $xactions[] = id(new PhabricatorCalendarEventTransaction()) + ->setTransactionType( PhabricatorCalendarEventTransaction::TYPE_START_DATE) ->setNewValue($start_value); @@ -184,6 +191,13 @@ ->setValue($type) ->setOptions($event->getStatusOptions()); + $all_day_select = id(new AphrontFormCheckboxControl()) + ->addCheckbox( + 'isAllDay', + 1, + pht('All Day Event'), + $is_all_day); + $start_control = id(new AphrontFormDateControl()) ->setUser($user) ->setName('start') @@ -234,6 +248,7 @@ ->setUser($user) ->appendChild($name) ->appendChild($status_select) + ->appendChild($all_day_select) ->appendChild($start_control) ->appendChild($end_control) ->appendControl($view_policies) diff --git a/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php b/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php --- a/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php +++ b/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php @@ -183,6 +183,16 @@ return parent::applyCustomExternalTransaction($object, $xaction); } + protected function didApplyInternalEffects( + PhabricatorLiskDAO $object, + array $xactions) { + + $object->removeViewerTimezone($this->requireActor()); + + return $xactions; + } + + protected function validateAllTransactions( PhabricatorLiskDAO $object, array $xactions) { diff --git a/src/applications/calendar/query/PhabricatorCalendarEventQuery.php b/src/applications/calendar/query/PhabricatorCalendarEventQuery.php --- a/src/applications/calendar/query/PhabricatorCalendarEventQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarEventQuery.php @@ -56,7 +56,13 @@ $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); - return $table->loadAllFromArray($data); + $events = $table->loadAllFromArray($data); + + foreach ($events as $event) { + $event->applyViewerTimezone($this->getViewer()); + } + + return $events; } protected function buildJoinClauseParts(AphrontDatabaseConnection $conn_r) { diff --git a/src/applications/calendar/storage/PhabricatorCalendarEvent.php b/src/applications/calendar/storage/PhabricatorCalendarEvent.php --- a/src/applications/calendar/storage/PhabricatorCalendarEvent.php +++ b/src/applications/calendar/storage/PhabricatorCalendarEvent.php @@ -24,6 +24,7 @@ protected $editPolicy; private $invitees = self::ATTACHABLE; + private $appliedViewer; const STATUS_AWAY = 1; const STATUS_SPORADIC = 2; @@ -37,15 +38,112 @@ return id(new PhabricatorCalendarEvent()) ->setUserPHID($actor->getPHID()) ->setIsCancelled(0) + ->setIsAllDay(0) ->setViewPolicy($actor->getPHID()) ->setEditPolicy($actor->getPHID()) - ->attachInvitees(array()); + ->attachInvitees(array()) + ->applyViewerTimezone($actor); + } + + public function applyViewerTimezone(PhabricatorUser $viewer) { + if ($this->appliedViewer) { + throw new Exception(pht('Viewer timezone is already applied!')); + } + + $this->appliedViewer = $viewer; + + if (!$this->getIsAllDay()) { + return $this; + } + + $zone = $viewer->getTimeZone(); + + + $this->setDateFrom( + $this->getDateEpochForTimeZone( + $this->getDateFrom(), + new DateTimeZone('GMT+12'), + 'Y-m-d', + null, + $zone)); + + $this->setDateTo( + $this->getDateEpochForTimeZone( + $this->getDateTo(), + new DateTimeZone('GMT-12'), + 'Y-m-d 23:59:59', + '-1 day', + $zone)); + + return $this; + } + + + public function removeViewerTimezone(PhabricatorUser $viewer) { + if (!$this->appliedViewer) { + throw new Exception(pht('Viewer timezone is not applied!')); + } + + if ($viewer->getPHID() != $this->appliedViewer->getPHID()) { + throw new Exception(pht('Removed viewer must match applied viewer!')); + } + + $this->appliedViewer = null; + + if (!$this->getIsAllDay()) { + return $this; + } + + $zone = $viewer->getTimeZone(); + + $this->setDateFrom( + $this->getDateEpochForTimeZone( + $this->getDateFrom(), + $zone, + 'Y-m-d', + null, + new DateTimeZone('GMT+12'))); + + $this->setDateTo( + $this->getDateEpochForTimeZone( + $this->getDateTo(), + $zone, + 'Y-m-d', + '+1 day', + new DateTimeZone('GMT-12'))); + + return $this; + } + + private function getDateEpochForTimeZone( + $epoch, + $src_zone, + $format, + $adjust, + $dst_zone) { + + $src = new DateTime('@'.$epoch); + $src->setTimeZone($src_zone); + + if (strlen($adjust)) { + $adjust = ' '.$adjust; + } + + $dst = new DateTime($src->format($format).$adjust, $dst_zone); + return $dst->format('U'); } public function save() { + if ($this->appliedViewer) { + throw new Exception( + pht( + 'Can not save event with viewer timezone still applied!')); + } + if (!$this->mailKey) { $this->mailKey = Filesystem::readRandomCharacters(20); } + return parent::save(); } diff --git a/src/applications/people/storage/PhabricatorUser.php b/src/applications/people/storage/PhabricatorUser.php --- a/src/applications/people/storage/PhabricatorUser.php +++ b/src/applications/people/storage/PhabricatorUser.php @@ -682,6 +682,10 @@ } } + public function getTimeZone() { + return new DateTimeZone($this->getTimezoneIdentifier()); + } + public function __toString() { return $this->getUsername(); } diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php --- a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php +++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php @@ -569,6 +569,11 @@ return $xaction; } + protected function didApplyInternalEffects( + PhabricatorLiskDAO $object, + array $xactions) { + return $xactions; + } protected function applyFinalEffects( PhabricatorLiskDAO $object, @@ -731,6 +736,8 @@ $this->applyInternalEffects($object, $xaction); } + $xactions = $this->didApplyInternalEffects($object, $xactions); + $object->save(); foreach ($xactions as $xaction) {