diff --git a/src/applications/phrequent/controller/PhrequentTrackController.php b/src/applications/phrequent/controller/PhrequentTrackController.php index 95e7c0d9f6..2b653b53aa 100644 --- a/src/applications/phrequent/controller/PhrequentTrackController.php +++ b/src/applications/phrequent/controller/PhrequentTrackController.php @@ -1,112 +1,165 @@ phid = $data['phid']; $this->verb = $data['verb']; } public function processRequest() { $request = $this->getRequest(); - $user = $request->getUser(); - $editor = new PhrequentTrackingEditor(); + $viewer = $request->getUser(); $phid = $this->phid; $handle = id(new PhabricatorHandleQuery()) - ->setViewer($user) + ->setViewer($viewer) ->withPHIDs(array($phid)) ->executeOne(); + $done_uri = $handle->getURI(); - if (!$this->isStartingTracking() && - !$this->isStoppingTracking()) { - throw new Exception('Unrecognized verb: '.$this->verb); - } - + $current_timer = null; switch ($this->verb) { case 'start': $button_text = pht('Start Tracking'); $title_text = pht('Start Tracking Time'); $inner_text = pht('What time did you start working?'); $action_text = pht('Start Timer'); $label_text = pht('Start Time'); break; case 'stop': $button_text = pht('Stop Tracking'); $title_text = pht('Stop Tracking Time'); $inner_text = pht('What time did you stop working?'); $action_text = pht('Stop Timer'); $label_text = pht('Stop Time'); + + + $current_timer = id(new PhrequentUserTimeQuery()) + ->setViewer($viewer) + ->withUserPHIDs(array($viewer->getPHID())) + ->withObjectPHIDs(array($phid)) + ->withEnded(PhrequentUserTimeQuery::ENDED_NO) + ->executeOne(); + if (!$current_timer) { + return $this->newDialog() + ->setTitle(pht('Not Tracking Time')) + ->appendParagraph( + pht( + 'You are not currently tracking time on this object.')) + ->addCancelButton($done_uri); + } break; + default: + return new Aphront404Response(); } + $errors = array(); + $v_note = null; + $e_date = null; + $epoch_control = id(new AphrontFormDateControl()) - ->setUser($user) + ->setUser($viewer) ->setName('epoch') ->setLabel($action_text) ->setValue(time()); - $err = array(); - if ($request->isDialogFormPost()) { + $v_note = $request->getStr('note'); $timestamp = $epoch_control->readValueFromRequest($request); - $note = $request->getStr('note'); - if (!$epoch_control->isValid() || $timestamp > time()) { - $err[] = pht('Invalid date, please enter a valid non-future date'); + if (!$epoch_control->isValid()) { + $errors[] = pht('Please choose an valid date.'); + $e_date = pht('Invalid'); + } else { + $max_time = PhabricatorTime::getNow(); + if ($timestamp > $max_time) { + if ($this->isStoppingTracking()) { + $errors[] = pht( + 'You can not stop tracking time at a future time. Enter the '. + 'current time, or a time in the past.'); + } else { + $errors[] = pht( + 'You can not start tracking time at a future time. Enter the '. + 'current time, or a time in the past.'); + } + $e_date = pht('Invalid'); + } + + if ($this->isStoppingTracking()) { + $min_time = $current_timer->getDateStarted(); + if ($min_time > $timestamp) { + $errors[] = pht( + 'Stop time must be after start time.'); + $e_date = pht('Invalid'); + } + } } - if (!$err) { + if (!$errors) { + $editor = new PhrequentTrackingEditor(); if ($this->isStartingTracking()) { - $editor->startTracking($user, $this->phid, $timestamp); + $editor->startTracking($viewer, $this->phid, $timestamp); } else if ($this->isStoppingTracking()) { - $editor->stopTracking($user, $this->phid, $timestamp, $note); + $editor->stopTracking($viewer, $this->phid, $timestamp, $v_note); } - return id(new AphrontRedirectResponse()); + + return id(new AphrontRedirectResponse())->setURI($done_uri); } } + $epoch_control->setError($e_date); + $dialog = $this->newDialog() ->setTitle($title_text) - ->setWidth(AphrontDialogView::WIDTH_FORM); - - if ($err) { - $dialog->setErrors($err); - } + ->setWidth(AphrontDialogView::WIDTH_FORM) + ->setErrors($errors) + ->appendParagraph($inner_text); $form = new PHUIFormLayoutView(); - $form - ->appendChild(hsprintf( - '

%s


', $inner_text)); + + if ($this->isStoppingTracking()) { + $start_time = $current_timer->getDateStarted(); + $start_string = pht( + '%s (%s ago)', + phabricator_datetime($start_time, $viewer), + phutil_format_relative_time(PhabricatorTime::getNow() - $start_time)); + + $form->appendChild( + id(new AphrontFormStaticControl()) + ->setLabel(pht('Started At')) + ->setValue($start_string)); + } $form->appendChild($epoch_control); if ($this->isStoppingTracking()) { - $form - ->appendChild( - id(new AphrontFormTextControl()) - ->setLabel(pht('Note')) - ->setName('note')); + $form->appendChild( + id(new AphrontFormTextControl()) + ->setLabel(pht('Note')) + ->setName('note') + ->setValue($v_note)); } $dialog->appendChild($form); - $dialog->addCancelButton($handle->getURI()); + $dialog->addCancelButton($done_uri); + $dialog->addSubmitButton($action_text); return $dialog; } private function isStartingTracking() { return $this->verb === 'start'; } private function isStoppingTracking() { return $this->verb === 'stop'; } } diff --git a/src/applications/phrequent/event/PhrequentUIEventListener.php b/src/applications/phrequent/event/PhrequentUIEventListener.php index 9b0bdace6b..9987bcfd03 100644 --- a/src/applications/phrequent/event/PhrequentUIEventListener.php +++ b/src/applications/phrequent/event/PhrequentUIEventListener.php @@ -1,153 +1,151 @@ listen(PhabricatorEventType::TYPE_UI_DIDRENDERACTIONS); $this->listen(PhabricatorEventType::TYPE_UI_WILLRENDERPROPERTIES); } public function handleEvent(PhutilEvent $event) { switch ($event->getType()) { case PhabricatorEventType::TYPE_UI_DIDRENDERACTIONS: $this->handleActionEvent($event); break; case PhabricatorEventType::TYPE_UI_WILLRENDERPROPERTIES: $this->handlePropertyEvent($event); break; } } private function handleActionEvent($event) { $user = $event->getUser(); $object = $event->getValue('object'); if (!$object || !$object->getPHID()) { // No object, or the object has no PHID yet.. return; } if (!($object instanceof PhrequentTrackableInterface)) { // This object isn't a time trackable object. return; } if (!$this->canUseApplication($event->getUser())) { return; } $tracking = PhrequentUserTimeQuery::isUserTrackingObject( $user, $object->getPHID()); if (!$tracking) { $track_action = id(new PhabricatorActionView()) ->setName(pht('Start Tracking Time')) ->setIcon('fa-clock-o') ->setWorkflow(true) - ->setRenderAsForm(true) ->setHref('/phrequent/track/start/'.$object->getPHID().'/'); } else { $track_action = id(new PhabricatorActionView()) ->setName(pht('Stop Tracking Time')) ->setIcon('fa-clock-o red') ->setWorkflow(true) - ->setRenderAsForm(true) ->setHref('/phrequent/track/stop/'.$object->getPHID().'/'); } if (!$user->isLoggedIn()) { $track_action->setDisabled(true); } $this->addActionMenuItems($event, $track_action); } private function handlePropertyEvent($ui_event) { $user = $ui_event->getUser(); $object = $ui_event->getValue('object'); if (!$object || !$object->getPHID()) { // No object, or the object has no PHID yet.. return; } if (!($object instanceof PhrequentTrackableInterface)) { // This object isn't a time trackable object. return; } if (!$this->canUseApplication($ui_event->getUser())) { return; } $events = id(new PhrequentUserTimeQuery()) ->setViewer($user) ->withObjectPHIDs(array($object->getPHID())) ->needPreemptingEvents(true) ->execute(); $event_groups = mgroup($events, 'getUserPHID'); if (!$events) { return; } $handles = id(new PhabricatorHandleQuery()) ->setViewer($user) ->withPHIDs(array_keys($event_groups)) ->execute(); $status_view = new PHUIStatusListView(); foreach ($event_groups as $user_phid => $event_group) { $item = new PHUIStatusItemView(); $item->setTarget($handles[$user_phid]->renderLink()); $state = 'stopped'; foreach ($event_group as $event) { if ($event->getDateEnded() === null) { if ($event->isPreempted()) { $state = 'suspended'; } else { $state = 'active'; break; } } } switch ($state) { case 'active': $item->setIcon( PHUIStatusItemView::ICON_CLOCK, 'green', pht('Working Now')); break; case 'suspended': $item->setIcon( PHUIStatusItemView::ICON_CLOCK, 'yellow', pht('Interrupted')); break; case 'stopped': $item->setIcon( PHUIStatusItemView::ICON_CLOCK, 'bluegrey', pht('Not Working Now')); break; } $block = new PhrequentTimeBlock($event_group); $item->setNote( phutil_format_relative_time( $block->getTimeSpentOnObject( $object->getPHID(), time()))); $status_view->addItem($item); } $view = $ui_event->getValue('view'); $view->addProperty(pht('Time Spent'), $status_view); } }