Page MenuHomePhabricator

D15655.id37733.diff
No OneTemporary

D15655.id37733.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -139,6 +139,7 @@
'AphrontDefaultApplicationConfiguration' => 'aphront/configuration/AphrontDefaultApplicationConfiguration.php',
'AphrontDialogResponse' => 'aphront/response/AphrontDialogResponse.php',
'AphrontDialogView' => 'view/AphrontDialogView.php',
+ 'AphrontEpochHTTPParameterType' => 'aphront/httpparametertype/AphrontEpochHTTPParameterType.php',
'AphrontException' => 'aphront/exception/AphrontException.php',
'AphrontFileResponse' => 'aphront/response/AphrontFileResponse.php',
'AphrontFormCheckboxControl' => 'view/form/control/AphrontFormCheckboxControl.php',
@@ -2100,7 +2101,6 @@
'PhabricatorCoreConfigOptions' => 'applications/config/option/PhabricatorCoreConfigOptions.php',
'PhabricatorCountdown' => 'applications/countdown/storage/PhabricatorCountdown.php',
'PhabricatorCountdownApplication' => 'applications/countdown/application/PhabricatorCountdownApplication.php',
- 'PhabricatorCountdownCommentController' => 'applications/countdown/controller/PhabricatorCountdownCommentController.php',
'PhabricatorCountdownController' => 'applications/countdown/controller/PhabricatorCountdownController.php',
'PhabricatorCountdownCountdownPHIDType' => 'applications/countdown/phid/PhabricatorCountdownCountdownPHIDType.php',
'PhabricatorCountdownDAO' => 'applications/countdown/storage/PhabricatorCountdownDAO.php',
@@ -2108,6 +2108,7 @@
'PhabricatorCountdownDefaultViewCapability' => 'applications/countdown/capability/PhabricatorCountdownDefaultViewCapability.php',
'PhabricatorCountdownDeleteController' => 'applications/countdown/controller/PhabricatorCountdownDeleteController.php',
'PhabricatorCountdownEditController' => 'applications/countdown/controller/PhabricatorCountdownEditController.php',
+ 'PhabricatorCountdownEditEngine' => 'applications/countdown/editor/PhabricatorCountdownEditEngine.php',
'PhabricatorCountdownEditor' => 'applications/countdown/editor/PhabricatorCountdownEditor.php',
'PhabricatorCountdownListController' => 'applications/countdown/controller/PhabricatorCountdownListController.php',
'PhabricatorCountdownMailReceiver' => 'applications/countdown/mail/PhabricatorCountdownMailReceiver.php',
@@ -2325,6 +2326,7 @@
'PhabricatorEmptyQueryException' => 'infrastructure/query/PhabricatorEmptyQueryException.php',
'PhabricatorEnv' => 'infrastructure/env/PhabricatorEnv.php',
'PhabricatorEnvTestCase' => 'infrastructure/env/__tests__/PhabricatorEnvTestCase.php',
+ 'PhabricatorEpochEditField' => 'applications/transactions/editfield/PhabricatorEpochEditField.php',
'PhabricatorEvent' => 'infrastructure/events/PhabricatorEvent.php',
'PhabricatorEventEngine' => 'infrastructure/events/PhabricatorEventEngine.php',
'PhabricatorEventListener' => 'infrastructure/events/PhabricatorEventListener.php',
@@ -4261,6 +4263,7 @@
'AphrontView',
'AphrontResponseProducerInterface',
),
+ 'AphrontEpochHTTPParameterType' => 'AphrontHTTPParameterType',
'AphrontException' => 'Exception',
'AphrontFileResponse' => 'AphrontResponse',
'AphrontFormCheckboxControl' => 'AphrontFormControl',
@@ -6525,7 +6528,6 @@
'PhabricatorProjectInterface',
),
'PhabricatorCountdownApplication' => 'PhabricatorApplication',
- 'PhabricatorCountdownCommentController' => 'PhabricatorCountdownController',
'PhabricatorCountdownController' => 'PhabricatorController',
'PhabricatorCountdownCountdownPHIDType' => 'PhabricatorPHIDType',
'PhabricatorCountdownDAO' => 'PhabricatorLiskDAO',
@@ -6533,6 +6535,7 @@
'PhabricatorCountdownDefaultViewCapability' => 'PhabricatorPolicyCapability',
'PhabricatorCountdownDeleteController' => 'PhabricatorCountdownController',
'PhabricatorCountdownEditController' => 'PhabricatorCountdownController',
+ 'PhabricatorCountdownEditEngine' => 'PhabricatorEditEngine',
'PhabricatorCountdownEditor' => 'PhabricatorApplicationTransactionEditor',
'PhabricatorCountdownListController' => 'PhabricatorCountdownController',
'PhabricatorCountdownMailReceiver' => 'PhabricatorObjectMailReceiver',
@@ -6776,6 +6779,7 @@
'PhabricatorEmptyQueryException' => 'Exception',
'PhabricatorEnv' => 'Phobject',
'PhabricatorEnvTestCase' => 'PhabricatorTestCase',
+ 'PhabricatorEpochEditField' => 'PhabricatorEditField',
'PhabricatorEvent' => 'PhutilEvent',
'PhabricatorEventEngine' => 'Phobject',
'PhabricatorEventListener' => 'PhutilEventListener',
diff --git a/src/aphront/httpparametertype/AphrontEpochHTTPParameterType.php b/src/aphront/httpparametertype/AphrontEpochHTTPParameterType.php
new file mode 100644
--- /dev/null
+++ b/src/aphront/httpparametertype/AphrontEpochHTTPParameterType.php
@@ -0,0 +1,37 @@
+<?php
+
+final class AphrontEpochHTTPParameterType
+ extends AphrontHTTPParameterType {
+
+ protected function getParameterExists(AphrontRequest $request, $key) {
+ return $request->getExists($key) ||
+ $request->getExists($key.'_d');
+ }
+
+ protected function getParameterValue(AphrontRequest $request, $key) {
+ return AphrontFormDateControlValue::newFromRequest($request, $key);
+ }
+
+ protected function getParameterTypeName() {
+ return 'epoch';
+ }
+
+ protected function getParameterFormatDescriptions() {
+ return array(
+ pht('An epoch timestamp, as an integer.'),
+ pht('An absolute date, as a string.'),
+ pht('A relative date, as a string.'),
+ pht('Separate date and time inputs, as strings.'),
+ );
+ }
+
+ protected function getParameterExamples() {
+ return array(
+ 'v=1460050737',
+ 'v=2022-01-01',
+ 'v=yesterday',
+ 'v_d=2022-01-01&v_t=12:34',
+ );
+ }
+
+}
diff --git a/src/applications/countdown/application/PhabricatorCountdownApplication.php b/src/applications/countdown/application/PhabricatorCountdownApplication.php
--- a/src/applications/countdown/application/PhabricatorCountdownApplication.php
+++ b/src/applications/countdown/application/PhabricatorCountdownApplication.php
@@ -46,9 +46,7 @@
=> 'PhabricatorCountdownViewController',
'comment/(?P<id>[1-9]\d*)/'
=> 'PhabricatorCountdownCommentController',
- 'edit/(?:(?P<id>[1-9]\d*)/)?'
- => 'PhabricatorCountdownEditController',
- 'create/'
+ $this->getEditRoutePattern('edit/')
=> 'PhabricatorCountdownEditController',
'delete/(?P<id>[1-9]\d*)/'
=> 'PhabricatorCountdownDeleteController',
diff --git a/src/applications/countdown/controller/PhabricatorCountdownCommentController.php b/src/applications/countdown/controller/PhabricatorCountdownCommentController.php
deleted file mode 100644
--- a/src/applications/countdown/controller/PhabricatorCountdownCommentController.php
+++ /dev/null
@@ -1,63 +0,0 @@
-<?php
-
-final class PhabricatorCountdownCommentController
- extends PhabricatorCountdownController {
-
- public function handleRequest(AphrontRequest $request) {
- $viewer = $request->getViewer();
- $id = $request->getURIData('id');
-
- if (!$request->isFormPost()) {
- return new Aphront400Response();
- }
-
- $countdown = id(new PhabricatorCountdownQuery())
- ->setViewer($viewer)
- ->withIDs(array($id))
- ->executeOne();
- if (!$countdown) {
- return new Aphront404Response();
- }
-
- $is_preview = $request->isPreviewRequest();
- $draft = PhabricatorDraft::buildFromRequest($request);
-
- $view_uri = '/'.$countdown->getMonogram();
-
- $xactions = array();
- $xactions[] = id(new PhabricatorCountdownTransaction())
- ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
- ->attachComment(
- id(new PhabricatorCountdownTransactionComment())
- ->setContent($request->getStr('comment')));
-
- $editor = id(new PhabricatorCountdownEditor())
- ->setActor($viewer)
- ->setContinueOnNoEffect($request->isContinueRequest())
- ->setContentSourceFromRequest($request)
- ->setIsPreview($is_preview);
-
- try {
- $xactions = $editor->applyTransactions($countdown, $xactions);
- } catch (PhabricatorApplicationTransactionNoEffectException $ex) {
- return id(new PhabricatorApplicationTransactionNoEffectResponse())
- ->setCancelURI($view_uri)
- ->setException($ex);
- }
-
- if ($draft) {
- $draft->replaceOrDelete();
- }
-
- if ($request->isAjax() && $is_preview) {
- return id(new PhabricatorApplicationTransactionResponse())
- ->setViewer($viewer)
- ->setTransactions($xactions)
- ->setIsPreview($is_preview);
- } else {
- return id(new AphrontRedirectResponse())
- ->setURI($view_uri);
- }
- }
-
-}
diff --git a/src/applications/countdown/controller/PhabricatorCountdownController.php b/src/applications/countdown/controller/PhabricatorCountdownController.php
--- a/src/applications/countdown/controller/PhabricatorCountdownController.php
+++ b/src/applications/countdown/controller/PhabricatorCountdownController.php
@@ -7,16 +7,5 @@
->setSearchEngine(new PhabricatorCountdownSearchEngine());
}
- protected function buildApplicationCrumbs() {
- $crumbs = parent::buildApplicationCrumbs();
-
- $crumbs->addAction(
- id(new PHUIListItemView())
- ->setName(pht('Create Countdown'))
- ->setHref($this->getApplicationURI('create/'))
- ->setIcon('fa-plus-square'));
-
- return $crumbs;
- }
}
diff --git a/src/applications/countdown/controller/PhabricatorCountdownEditController.php b/src/applications/countdown/controller/PhabricatorCountdownEditController.php
--- a/src/applications/countdown/controller/PhabricatorCountdownEditController.php
+++ b/src/applications/countdown/controller/PhabricatorCountdownEditController.php
@@ -4,205 +4,9 @@
extends PhabricatorCountdownController {
public function handleRequest(AphrontRequest $request) {
- $viewer = $request->getViewer();
- $id = $request->getURIData('id');
-
- if ($id) {
- $countdown = id(new PhabricatorCountdownQuery())
- ->setViewer($viewer)
- ->withIDs(array($id))
- ->requireCapabilities(
- array(
- PhabricatorPolicyCapability::CAN_VIEW,
- PhabricatorPolicyCapability::CAN_EDIT,
- ))
- ->executeOne();
- if (!$countdown) {
- return new Aphront404Response();
- }
- $date_value = AphrontFormDateControlValue::newFromEpoch(
- $viewer,
- $countdown->getEpoch());
- $v_projects = PhabricatorEdgeQuery::loadDestinationPHIDs(
- $countdown->getPHID(),
- PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
- $v_projects = array_reverse($v_projects);
- $title = pht('Edit Countdown: %s', $countdown->getTitle());
- } else {
- $title = pht('Create Countdown');
- $countdown = PhabricatorCountdown::initializeNewCountdown($viewer);
- $date_value = AphrontFormDateControlValue::newFromEpoch(
- $viewer, PhabricatorTime::getNow());
- $v_projects = array();
- }
-
- $errors = array();
- $e_text = true;
- $e_epoch = null;
-
- $v_text = $countdown->getTitle();
- $v_desc = $countdown->getDescription();
- $v_space = $countdown->getSpacePHID();
- $v_view = $countdown->getViewPolicy();
- $v_edit = $countdown->getEditPolicy();
-
- if ($request->isFormPost()) {
- $v_text = $request->getStr('title');
- $v_desc = $request->getStr('description');
- $v_space = $request->getStr('spacePHID');
- $date_value = AphrontFormDateControlValue::newFromRequest(
- $request,
- 'epoch');
- $v_view = $request->getStr('viewPolicy');
- $v_edit = $request->getStr('editPolicy');
- $v_projects = $request->getArr('projects');
-
- $type_title = PhabricatorCountdownTransaction::TYPE_TITLE;
- $type_epoch = PhabricatorCountdownTransaction::TYPE_EPOCH;
- $type_description = PhabricatorCountdownTransaction::TYPE_DESCRIPTION;
- $type_space = PhabricatorTransactions::TYPE_SPACE;
- $type_view = PhabricatorTransactions::TYPE_VIEW_POLICY;
- $type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY;
-
- $xactions = array();
-
- $xactions[] = id(new PhabricatorCountdownTransaction())
- ->setTransactionType($type_title)
- ->setNewValue($v_text);
-
- $xactions[] = id(new PhabricatorCountdownTransaction())
- ->setTransactionType($type_epoch)
- ->setNewValue($date_value);
-
- $xactions[] = id(new PhabricatorCountdownTransaction())
- ->setTransactionType($type_description)
- ->setNewValue($v_desc);
-
- $xactions[] = id(new PhabricatorCountdownTransaction())
- ->setTransactionType($type_space)
- ->setNewValue($v_space);
-
- $xactions[] = id(new PhabricatorCountdownTransaction())
- ->setTransactionType($type_view)
- ->setNewValue($v_view);
-
- $xactions[] = id(new PhabricatorCountdownTransaction())
- ->setTransactionType($type_edit)
- ->setNewValue($v_edit);
-
- $proj_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
- $xactions[] = id(new PhabricatorCountdownTransaction())
- ->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
- ->setMetadataValue('edge:type', $proj_edge_type)
- ->setNewValue(array('=' => array_fuse($v_projects)));
-
- $editor = id(new PhabricatorCountdownEditor())
- ->setActor($viewer)
- ->setContentSourceFromRequest($request)
- ->setContinueOnNoEffect(true);
-
- try {
- $editor->applyTransactions($countdown, $xactions);
-
- return id(new AphrontRedirectResponse())
- ->setURI('/'.$countdown->getMonogram());
- } catch (PhabricatorApplicationTransactionValidationException $ex) {
- $validation_exception = $ex;
-
- $e_title = $ex->getShortMessage($type_title);
- $e_epoch = $ex->getShortMessage($type_epoch);
- }
-
- }
-
- $crumbs = $this->buildApplicationCrumbs();
- $crumbs->setBorder(true);
-
- $cancel_uri = '/countdown/';
- if ($countdown->getID()) {
- $cancel_uri = '/countdown/'.$countdown->getID().'/';
- $crumbs->addTextCrumb('C'.$countdown->getID(), $cancel_uri);
- $crumbs->addTextCrumb(pht('Edit'));
- $submit_label = pht('Save Changes');
- $header_icon = 'fa-pencil';
- } else {
- $crumbs->addTextCrumb(pht('Create Countdown'));
- $submit_label = pht('Create Countdown');
- $header_icon = 'fa-plus-square';
- }
-
- $policies = id(new PhabricatorPolicyQuery())
- ->setViewer($viewer)
- ->setObject($countdown)
- ->execute();
-
- $form = id(new AphrontFormView())
- ->setUser($viewer)
- ->setAction($request->getRequestURI()->getPath())
- ->appendChild(
- id(new AphrontFormTextControl())
- ->setLabel(pht('Title'))
- ->setValue($v_text)
- ->setName('title')
- ->setError($e_text))
- ->appendControl(
- id(new AphrontFormDateControl())
- ->setName('epoch')
- ->setLabel(pht('End Date'))
- ->setError($e_epoch)
- ->setValue($date_value))
- ->appendControl(
- id(new PhabricatorRemarkupControl())
- ->setName('description')
- ->setLabel(pht('Description'))
- ->setValue($v_desc))
- ->appendControl(
- id(new AphrontFormPolicyControl())
- ->setName('viewPolicy')
- ->setPolicyObject($countdown)
- ->setPolicies($policies)
- ->setSpacePHID($v_space)
- ->setValue($v_view)
- ->setCapability(PhabricatorPolicyCapability::CAN_VIEW))
- ->appendControl(
- id(new AphrontFormPolicyControl())
- ->setName('editPolicy')
- ->setPolicyObject($countdown)
- ->setPolicies($policies)
- ->setValue($v_edit)
- ->setCapability(PhabricatorPolicyCapability::CAN_EDIT))
- ->appendControl(
- id(new AphrontFormTokenizerControl())
- ->setLabel(pht('Projects'))
- ->setName('projects')
- ->setValue($v_projects)
- ->setDatasource(new PhabricatorProjectDatasource()))
- ->appendChild(
- id(new AphrontFormSubmitControl())
- ->addCancelButton($cancel_uri)
- ->setValue($submit_label));
-
- $form_box = id(new PHUIObjectBoxView())
- ->setHeaderText(pht('Countdown'))
- ->setFormErrors($errors)
- ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
- ->setForm($form);
-
- $header = id(new PHUIHeaderView())
- ->setHeader($title)
- ->setHeaderIcon($header_icon);
-
- $view = id(new PHUITwoColumnView())
- ->setHeader($header)
- ->setFooter($form_box);
-
- return $this->newPage()
- ->setTitle($title)
- ->setCrumbs($crumbs)
- ->appendChild(
- array(
- $view,
- ));
+ return id(new PhabricatorCountdownEditEngine())
+ ->setController($this)
+ ->buildResponse();
}
}
diff --git a/src/applications/countdown/controller/PhabricatorCountdownListController.php b/src/applications/countdown/controller/PhabricatorCountdownListController.php
--- a/src/applications/countdown/controller/PhabricatorCountdownListController.php
+++ b/src/applications/countdown/controller/PhabricatorCountdownListController.php
@@ -13,4 +13,14 @@
->buildResponse();
}
+ protected function buildApplicationCrumbs() {
+ $crumbs = parent::buildApplicationCrumbs();
+
+ id(new PhabricatorCountdownEditEngine())
+ ->setViewer($this->getViewer())
+ ->addActionToCrumbs($crumbs);
+
+ return $crumbs;
+ }
+
}
diff --git a/src/applications/countdown/controller/PhabricatorCountdownViewController.php b/src/applications/countdown/controller/PhabricatorCountdownViewController.php
--- a/src/applications/countdown/controller/PhabricatorCountdownViewController.php
+++ b/src/applications/countdown/controller/PhabricatorCountdownViewController.php
@@ -55,12 +55,15 @@
$timeline = $this->buildTransactionTimeline(
$countdown,
new PhabricatorCountdownTransactionQuery());
- $add_comment = $this->buildCommentForm($countdown);
+
+ $comment_view = id(new PhabricatorCountdownEditEngine())
+ ->setViewer($viewer)
+ ->buildEditEngineCommentView($countdown);
$content = array(
$countdown_view,
$timeline,
- $add_comment,
+ $comment_view,
);
$view = id(new PHUITwoColumnView())
@@ -135,25 +138,4 @@
->setContent($content);
}
- private function buildCommentForm(PhabricatorCountdown $countdown) {
- $viewer = $this->getViewer();
-
- $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
-
- $add_comment_header = $is_serious
- ? pht('Add Comment')
- : pht('Last Words');
-
- $draft = PhabricatorDraft::newFromUserAndKey(
- $viewer, $countdown->getPHID());
-
- return id(new PhabricatorApplicationTransactionCommentView())
- ->setUser($viewer)
- ->setObjectPHID($countdown->getPHID())
- ->setDraft($draft)
- ->setHeaderText($add_comment_header)
- ->setAction($this->getApplicationURI('/comment/'.$countdown->getID().'/'))
- ->setSubmitButtonName(pht('Add Comment'));
- }
-
}
diff --git a/src/applications/countdown/editor/PhabricatorCountdownEditEngine.php b/src/applications/countdown/editor/PhabricatorCountdownEditEngine.php
new file mode 100644
--- /dev/null
+++ b/src/applications/countdown/editor/PhabricatorCountdownEditEngine.php
@@ -0,0 +1,108 @@
+<?php
+
+final class PhabricatorCountdownEditEngine
+ extends PhabricatorEditEngine {
+
+ const ENGINECONST = 'countdown.countdown';
+
+ public function isEngineConfigurable() {
+ return false;
+ }
+
+ public function getEngineName() {
+ return pht('Countdowns');
+ }
+
+ public function getSummaryHeader() {
+ return pht('Edit Countdowns');
+ }
+
+ public function getSummaryText() {
+ return pht('Creates and edits countdowns.');
+ }
+
+ public function getEngineApplicationClass() {
+ return 'PhabricatorCountdownApplication';
+ }
+
+ protected function newEditableObject() {
+ return PhabricatorCountdown::initializeNewCountdown(
+ $this->getViewer());
+ }
+
+ protected function newObjectQuery() {
+ return id(new PhabricatorCountdownQuery());
+ }
+
+ protected function getObjectCreateTitleText($object) {
+ return pht('Create Countdown');
+ }
+
+ protected function getObjectCreateButtonText($object) {
+ return pht('Create Countdown');
+ }
+
+ protected function getObjectEditTitleText($object) {
+ return pht('Edit Countdown: %s', $object->getTitle());
+ }
+
+ protected function getObjectEditShortText($object) {
+ return pht('Edit Countdown');
+ }
+
+ protected function getObjectCreateShortText() {
+ return pht('Create Countdown');
+ }
+
+ protected function getObjectName() {
+ return pht('Countdown');
+ }
+
+ protected function getCommentViewHeaderText($object) {
+ return pht('Last Words');
+ }
+
+ protected function getCommentViewButtonText($object) {
+ return pht('Contemplate Infinity');
+ }
+
+ protected function getObjectViewURI($object) {
+ return $object->getURI();
+ }
+
+ protected function buildCustomEditFields($object) {
+ $epoch_value = $object->getEpoch();
+ if ($epoch_value === null) {
+ $epoch_value = PhabricatorTime::getNow();
+ }
+
+ return array(
+ id(new PhabricatorTextEditField())
+ ->setKey('name')
+ ->setLabel(pht('Name'))
+ ->setIsRequired(true)
+ ->setTransactionType(PhabricatorCountdownTransaction::TYPE_TITLE)
+ ->setDescription(pht('The countdown name.'))
+ ->setConduitDescription(pht('Rename the countdown.'))
+ ->setConduitTypeDescription(pht('New countdown name.'))
+ ->setValue($object->getTitle()),
+ id(new PhabricatorEpochEditField())
+ ->setKey('epoch')
+ ->setLabel(pht('End Date'))
+ ->setTransactionType(PhabricatorCountdownTransaction::TYPE_EPOCH)
+ ->setDescription(pht('Date when the countdown ends.'))
+ ->setConduitDescription(pht('Change the end date of the countdown.'))
+ ->setConduitTypeDescription(pht('New countdown end date.'))
+ ->setValue($epoch_value),
+ id(new PhabricatorRemarkupEditField())
+ ->setKey('description')
+ ->setLabel(pht('Description'))
+ ->setTransactionType(PhabricatorCountdownTransaction::TYPE_DESCRIPTION)
+ ->setDescription(pht('Description of the countdown.'))
+ ->setConduitDescription(pht('Change the countdown description.'))
+ ->setConduitTypeDescription(pht('New description.'))
+ ->setValue($object->getDescription()),
+ );
+ }
+
+}
diff --git a/src/applications/countdown/editor/PhabricatorCountdownEditor.php b/src/applications/countdown/editor/PhabricatorCountdownEditor.php
--- a/src/applications/countdown/editor/PhabricatorCountdownEditor.php
+++ b/src/applications/countdown/editor/PhabricatorCountdownEditor.php
@@ -120,19 +120,27 @@
}
break;
case PhabricatorCountdownTransaction::TYPE_EPOCH:
- $date_value = AphrontFormDateControlValue::newFromEpoch(
- $this->requireActor(),
- $object->getEpoch());
- if (!$date_value->isValid()) {
+ if (!$object->getEpoch() && !$xactions) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
- pht('Invalid'),
- pht('You must give the countdown a valid end date.'),
- nonempty(last($xactions), null));
-
+ pht('Required'),
+ pht('You must give the countdown an end date.'),
+ null);
$error->setIsMissingFieldError(true);
$errors[] = $error;
}
+
+ foreach ($xactions as $xaction) {
+ $value = $xaction->getNewValue();
+ if (!$value->isValid()) {
+ $error = new PhabricatorApplicationTransactionValidationError(
+ $type,
+ pht('Invalid'),
+ pht('You must give the countdown a valid end date.'),
+ $xaction);
+ $errors[] = $error;
+ }
+ }
break;
}
diff --git a/src/applications/countdown/storage/PhabricatorCountdown.php b/src/applications/countdown/storage/PhabricatorCountdown.php
--- a/src/applications/countdown/storage/PhabricatorCountdown.php
+++ b/src/applications/countdown/storage/PhabricatorCountdown.php
@@ -28,10 +28,13 @@
$view_policy = $app->getPolicy(
PhabricatorCountdownDefaultViewCapability::CAPABILITY);
+ $edit_policy = $app->getPolicy(
+ PhabricatorCountdownDefaultEditCapability::CAPABILITY);
+
return id(new PhabricatorCountdown())
->setAuthorPHID($actor->getPHID())
->setViewPolicy($view_policy)
- ->setEpoch(PhabricatorTime::getNow())
+ ->setEditPolicy($edit_policy)
->setSpacePHID($actor->getDefaultSpacePHID());
}
@@ -55,6 +58,10 @@
return 'C'.$this->getID();
}
+ public function getURI() {
+ return '/'.$this->getMonogram();
+ }
+
public function save() {
if (!$this->getMailKey()) {
$this->setMailKey(Filesystem::readRandomCharacters(20));
diff --git a/src/applications/countdown/storage/PhabricatorCountdownTransaction.php b/src/applications/countdown/storage/PhabricatorCountdownTransaction.php
--- a/src/applications/countdown/storage/PhabricatorCountdownTransaction.php
+++ b/src/applications/countdown/storage/PhabricatorCountdownTransaction.php
@@ -33,42 +33,20 @@
$type = $this->getTransactionType();
switch ($type) {
case self::TYPE_TITLE:
- if ($old === null) {
- return pht(
- '%s created this countdown.',
- $this->renderHandleLink($author_phid));
- } else {
- return pht(
- '%s renamed this countdown from "%s" to "%s".',
- $this->renderHandleLink($author_phid),
- $old,
- $new);
- }
- break;
+ return pht(
+ '%s renamed this countdown from "%s" to "%s".',
+ $this->renderHandleLink($author_phid),
+ $old,
+ $new);
case self::TYPE_DESCRIPTION:
- if ($old === null) {
- return pht(
- '%s set the description of this countdown.',
- $this->renderHandleLink($author_phid));
- } else {
- return pht(
- '%s edited the description of this countdown.',
- $this->renderHandleLink($author_phid));
- }
- break;
+ return pht(
+ '%s edited the description of this countdown.',
+ $this->renderHandleLink($author_phid));
case self::TYPE_EPOCH:
- if ($old === null) {
- return pht(
- '%s set this countdown to end on %s.',
- $this->renderHandleLink($author_phid),
- phabricator_datetime($new, $this->getViewer()));
- } else if ($old != $new) {
- return pht(
- '%s updated this countdown to end on %s.',
- $this->renderHandleLink($author_phid),
- phabricator_datetime($new, $this->getViewer()));
- }
- break;
+ return pht(
+ '%s updated this countdown to end on %s.',
+ $this->renderHandleLink($author_phid),
+ phabricator_datetime($new, $this->getViewer()));
}
return parent::getTitle();
@@ -84,47 +62,20 @@
$type = $this->getTransactionType();
switch ($type) {
case self::TYPE_TITLE:
- if ($old === null) {
- return pht(
- '%s created %s.',
- $this->renderHandleLink($author_phid),
- $this->renderHandleLink($object_phid));
-
- } else {
- return pht(
- '%s renamed %s.',
- $this->renderHandleLink($author_phid),
- $this->renderHandleLink($object_phid));
- }
- break;
+ return pht(
+ '%s renamed %s.',
+ $this->renderHandleLink($author_phid),
+ $this->renderHandleLink($object_phid));
case self::TYPE_DESCRIPTION:
- if ($old === null) {
- return pht(
- '%s set the description of %s.',
- $this->renderHandleLink($author_phid),
- $this->renderHandleLink($object_phid));
-
- } else {
- return pht(
- '%s edited the description of %s.',
- $this->renderHandleLink($author_phid),
- $this->renderHandleLink($object_phid));
- }
- break;
+ return pht(
+ '%s edited the description of %s.',
+ $this->renderHandleLink($author_phid),
+ $this->renderHandleLink($object_phid));
case self::TYPE_EPOCH:
- if ($old === null) {
- return pht(
- '%s set the end date of %s.',
- $this->renderHandleLink($author_phid),
- $this->renderHandleLink($object_phid));
-
- } else {
- return pht(
- '%s edited the end date of %s.',
- $this->renderHandleLink($author_phid),
- $this->renderHandleLink($object_phid));
- }
- break;
+ return pht(
+ '%s edited the end date of %s.',
+ $this->renderHandleLink($author_phid),
+ $this->renderHandleLink($object_phid));
}
return parent::getTitleForFeed();
@@ -150,15 +101,6 @@
return $tags;
}
- public function shouldHide() {
- $old = $this->getOldValue();
- switch ($this->getTransactionType()) {
- case self::TYPE_DESCRIPTION:
- return ($old === null);
- }
- return parent::shouldHide();
- }
-
public function hasChangeDetails() {
switch ($this->getTransactionType()) {
case self::TYPE_DESCRIPTION:
diff --git a/src/applications/transactions/editfield/PhabricatorEpochEditField.php b/src/applications/transactions/editfield/PhabricatorEpochEditField.php
new file mode 100644
--- /dev/null
+++ b/src/applications/transactions/editfield/PhabricatorEpochEditField.php
@@ -0,0 +1,21 @@
+<?php
+
+final class PhabricatorEpochEditField
+ extends PhabricatorEditField {
+
+ protected function newControl() {
+ return id(new AphrontFormDateControl())
+ ->setViewer($this->getViewer());
+ }
+
+ protected function newHTTPParameterType() {
+ return new AphrontEpochHTTPParameterType();
+ }
+
+ protected function newConduitParameterType() {
+ // TODO: This isn't correct, but we don't have any methods which use this
+ // yet.
+ return new ConduitIntParameterType();
+ }
+
+}
diff --git a/src/view/form/control/AphrontFormDateControl.php b/src/view/form/control/AphrontFormDateControl.php
--- a/src/view/form/control/AphrontFormDateControl.php
+++ b/src/view/form/control/AphrontFormDateControl.php
@@ -130,10 +130,13 @@
$date_format = $this->getDateFormat();
$timezone = $this->getTimezone();
- $datetime = new DateTime($this->valueDate, $timezone);
- $date = $datetime->format($date_format);
+ try {
+ $datetime = new DateTime($this->valueDate, $timezone);
+ } catch (Exception $ex) {
+ return $this->valueDate;
+ }
- return $date;
+ return $datetime->format($date_format);
}
private function getTimeFormat() {
diff --git a/src/view/form/control/AphrontFormDateControlValue.php b/src/view/form/control/AphrontFormDateControlValue.php
--- a/src/view/form/control/AphrontFormDateControlValue.php
+++ b/src/view/form/control/AphrontFormDateControlValue.php
@@ -84,10 +84,33 @@
$value = new AphrontFormDateControlValue();
$value->viewer = $request->getViewer();
- list($value->valueDate, $value->valueTime) =
- $value->getFormattedDateFromDate(
- $request->getStr($key.'_d'),
- $request->getStr($key.'_t'));
+ $datetime = $request->getStr($key);
+ if (strlen($datetime)) {
+ $date = $datetime;
+ $time = null;
+ } else {
+ $date = $request->getStr($key.'_d');
+ $time = $request->getStr($key.'_t');
+ }
+
+ // If this looks like an epoch timestamp, prefix it with "@" so that
+ // DateTime() reads it as one. Assume small numbers are a "Ymd" digit
+ // string instead of an epoch timestamp for a time in 1970.
+ if (ctype_digit($date) && ($date > 30000000)) {
+ $date = '@'.$date;
+ $time = null;
+ }
+
+ $value->valueDate = $date;
+ $value->valueTime = $time;
+
+ $formatted = $value->getFormattedDateFromDate(
+ $value->valueDate,
+ $value->valueTime);
+
+ if ($formatted) {
+ list($value->valueDate, $value->valueTime) = $formatted;
+ }
$value->valueEnabled = $request->getStr($key.'_e');
return $value;
@@ -96,6 +119,11 @@
public static function newFromEpoch(PhabricatorUser $viewer, $epoch) {
$value = new AphrontFormDateControlValue();
$value->viewer = $viewer;
+
+ if (!$epoch) {
+ return $value;
+ }
+
$readable = $value->formatTime($epoch, 'Y!m!d!g:i A');
$readable = explode('!', $readable, 4);
@@ -120,10 +148,16 @@
$value = new AphrontFormDateControlValue();
$value->viewer = $viewer;
- list($value->valueDate, $value->valueTime) =
- $value->getFormattedDateFromDate(
- idx($dictionary, 'd'),
- idx($dictionary, 't'));
+ $value->valueDate = idx($dictionary, 'd');
+ $value->valueTime = idx($dictionary, 't');
+
+ $formatted = $value->getFormattedDateFromDate(
+ $value->valueDate,
+ $value->valueTime);
+
+ if ($formatted) {
+ list($value->valueDate, $value->valueTime) = $formatted;
+ }
$value->valueEnabled = idx($dictionary, 'e');
@@ -170,37 +204,12 @@
return null;
}
- $date = $this->valueDate;
- $time = $this->valueTime;
- $zone = $this->getTimezone();
-
- if (!strlen($time)) {
+ $datetime = $this->newDateTime($this->valueDate, $this->valueTime);
+ if (!$datetime) {
return null;
}
- $colloquial = array(
- 'elevenses' => '11:00 AM',
- 'morning tea' => '11:00 AM',
- 'noon' => '12:00 PM',
- 'high noon' => '12:00 PM',
- 'lunch' => '12:00 PM',
- 'tea time' => '3:00 PM',
- 'witching hour' => '12:00 AM',
- 'midnight' => '12:00 AM',
- );
-
- $normalized = phutil_utf8_strtolower($time);
- if (isset($colloquial[$normalized])) {
- $time = $colloquial[$normalized];
- }
-
- try {
- $datetime = new DateTime("{$date} {$time}", $zone);
- $value = $datetime->format('U');
- } catch (Exception $ex) {
- $value = null;
- }
- return $value;
+ return $datetime->format('U');
}
private function getTimeFormat() {
@@ -214,25 +223,34 @@
}
private function getFormattedDateFromDate($date, $time) {
- $original_input = $date;
- $zone = $this->getTimezone();
- $separator = $this->getFormatSeparator();
- $parts = preg_split('@[,./:-]@', $date);
- $date = implode($separator, $parts);
- $date = id(new DateTime($date, $zone));
-
- if ($date) {
- $date = $date->format($this->getDateFormat());
- } else {
- $date = $original_input;
+ $datetime = $this->newDateTime($date, $time);
+ if (!$datetime) {
+ return null;
}
- $date = id(new DateTime("{$date} {$time}", $zone));
-
return array(
- $date->format($this->getDateFormat()),
- $date->format($this->getTimeFormat()),
+ $datetime->format($this->getDateFormat()),
+ $datetime->format($this->getTimeFormat()),
);
+
+ return array($date, $time);
+ }
+
+ private function newDateTime($date, $time) {
+ $date = $this->getStandardDateFormat($date);
+ $time = $this->getStandardTimeFormat($time);
+ try {
+ $datetime = new DateTime("{$date} {$time}");
+ } catch (Exception $ex) {
+ return null;
+ }
+
+ // Set the timezone explicitly because it is ignored in the constructor
+ // if the date is an epoch timestamp.
+ $zone = $this->getTimezone();
+ $datetime->setTimezone($zone);
+
+ return $datetime;
}
private function getFormattedDateFromParts(
@@ -261,16 +279,7 @@
}
public function getDateTime() {
- $epoch = $this->getEpoch();
- $date = null;
-
- if ($epoch) {
- $zone = $this->getTimezone();
- $date = new DateTime('@'.$epoch);
- $date->setTimeZone($zone);
- }
-
- return $date;
+ return $this->newDateTime();
}
private function getTimezone() {
@@ -283,5 +292,56 @@
return $this->zone;
}
+ private function getStandardDateFormat($date) {
+ $colloquial = array(
+ 'newyear' => 'January 1',
+ 'valentine' => 'February 14',
+ 'pi' => 'March 14',
+ 'christma' => 'December 25',
+ );
+
+ // Lowercase the input, then remove punctuation, a "day" suffix, and an
+ // "s" if one is present. This allows all of these to match. This allows
+ // variations like "New Year's Day" and "New Year" to both match.
+ $normalized = phutil_utf8_strtolower($date);
+ $normalized = preg_replace('/[^a-z]/', '', $normalized);
+ $normalized = preg_replace('/day\z/', '', $normalized);
+ $normalized = preg_replace('/s\z/', '', $normalized);
+
+ if (isset($colloquial[$normalized])) {
+ return $colloquial[$normalized];
+ }
+
+ $separator = $this->getFormatSeparator();
+ $parts = preg_split('@[,./:-]@', $date);
+ return implode($separator, $parts);
+ }
+
+ private function getStandardTimeFormat($time) {
+ $colloquial = array(
+ 'crack of dawn' => '5:00 AM',
+ 'dawn' => '6:00 AM',
+ 'early' => '7:00 AM',
+ 'morning' => '8:00 AM',
+ 'elevenses' => '11:00 AM',
+ 'morning tea' => '11:00 AM',
+ 'noon' => '12:00 PM',
+ 'high noon' => '12:00 PM',
+ 'lunch' => '12:00 PM',
+ 'afternoon' => '2:00 PM',
+ 'tea time' => '3:00 PM',
+ 'evening' => '7:00 PM',
+ 'late' => '11:00 PM',
+ 'witching hour' => '12:00 AM',
+ 'midnight' => '12:00 AM',
+ );
+
+ $normalized = phutil_utf8_strtolower($time);
+ if (isset($colloquial[$normalized])) {
+ $time = $colloquial[$normalized];
+ }
+
+ return $time;
+ }
}

File Metadata

Mime Type
text/plain
Expires
Mon, Mar 10, 5:37 PM (1 w, 6 d ago)
Storage Engine
amazon-s3
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
phabricator/secure/c3/hx/exkbaput5rdlphw3
Default Alt Text
D15655.id37733.diff (37 KB)

Event Timeline