mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-20 12:30:56 +01:00
Convert Countdown to EditEngine
Summary: Fixes T10684. Fixes T10520. This primarily implements a date/epoch field, and then does a bunch of standard plumbing. Test Plan: - Created countdowns. - Edited countdowns. - Used HTTP prefilling. - Created a countdown ending on "Christmas Morning", etc. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10520, T10684 Differential Revision: https://secure.phabricator.com/D15655
This commit is contained in:
parent
1f423c3bd1
commit
cdec319143
15 changed files with 366 additions and 456 deletions
|
@ -139,6 +139,7 @@ phutil_register_library_map(array(
|
|||
'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 @@ phutil_register_library_map(array(
|
|||
'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 @@ phutil_register_library_map(array(
|
|||
'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 @@ phutil_register_library_map(array(
|
|||
'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 @@ phutil_register_library_map(array(
|
|||
'AphrontView',
|
||||
'AphrontResponseProducerInterface',
|
||||
),
|
||||
'AphrontEpochHTTPParameterType' => 'AphrontHTTPParameterType',
|
||||
'AphrontException' => 'Exception',
|
||||
'AphrontFileResponse' => 'AphrontResponse',
|
||||
'AphrontFormCheckboxControl' => 'AphrontFormControl',
|
||||
|
@ -6525,7 +6528,6 @@ phutil_register_library_map(array(
|
|||
'PhabricatorProjectInterface',
|
||||
),
|
||||
'PhabricatorCountdownApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorCountdownCommentController' => 'PhabricatorCountdownController',
|
||||
'PhabricatorCountdownController' => 'PhabricatorController',
|
||||
'PhabricatorCountdownCountdownPHIDType' => 'PhabricatorPHIDType',
|
||||
'PhabricatorCountdownDAO' => 'PhabricatorLiskDAO',
|
||||
|
@ -6533,6 +6535,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorCountdownDefaultViewCapability' => 'PhabricatorPolicyCapability',
|
||||
'PhabricatorCountdownDeleteController' => 'PhabricatorCountdownController',
|
||||
'PhabricatorCountdownEditController' => 'PhabricatorCountdownController',
|
||||
'PhabricatorCountdownEditEngine' => 'PhabricatorEditEngine',
|
||||
'PhabricatorCountdownEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||
'PhabricatorCountdownListController' => 'PhabricatorCountdownController',
|
||||
'PhabricatorCountdownMailReceiver' => 'PhabricatorObjectMailReceiver',
|
||||
|
@ -6776,6 +6779,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorEmptyQueryException' => 'Exception',
|
||||
'PhabricatorEnv' => 'Phobject',
|
||||
'PhabricatorEnvTestCase' => 'PhabricatorTestCase',
|
||||
'PhabricatorEpochEditField' => 'PhabricatorEditField',
|
||||
'PhabricatorEvent' => 'PhutilEvent',
|
||||
'PhabricatorEventEngine' => 'Phobject',
|
||||
'PhabricatorEventListener' => 'PhutilEventListener',
|
||||
|
|
|
@ -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',
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -46,9 +46,7 @@ final class PhabricatorCountdownApplication extends PhabricatorApplication {
|
|||
=> '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',
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -7,16 +7,5 @@ abstract class PhabricatorCountdownController extends PhabricatorController {
|
|||
->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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,205 +4,9 @@ final class PhabricatorCountdownEditController
|
|||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,4 +13,14 @@ final class PhabricatorCountdownListController
|
|||
->buildResponse();
|
||||
}
|
||||
|
||||
protected function buildApplicationCrumbs() {
|
||||
$crumbs = parent::buildApplicationCrumbs();
|
||||
|
||||
id(new PhabricatorCountdownEditEngine())
|
||||
->setViewer($this->getViewer())
|
||||
->addActionToCrumbs($crumbs);
|
||||
|
||||
return $crumbs;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -55,12 +55,15 @@ final class PhabricatorCountdownViewController
|
|||
$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 @@ final class PhabricatorCountdownViewController
|
|||
->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'));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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()),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -120,19 +120,27 @@ final class PhabricatorCountdownEditor
|
|||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -28,10 +28,13 @@ final class PhabricatorCountdown extends PhabricatorCountdownDAO
|
|||
$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 @@ final class PhabricatorCountdown extends PhabricatorCountdownDAO
|
|||
return 'C'.$this->getID();
|
||||
}
|
||||
|
||||
public function getURI() {
|
||||
return '/'.$this->getMonogram();
|
||||
}
|
||||
|
||||
public function save() {
|
||||
if (!$this->getMailKey()) {
|
||||
$this->setMailKey(Filesystem::readRandomCharacters(20));
|
||||
|
|
|
@ -33,42 +33,20 @@ final class PhabricatorCountdownTransaction
|
|||
$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 @@ final class PhabricatorCountdownTransaction
|
|||
$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 @@ final class PhabricatorCountdownTransaction
|
|||
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:
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -130,10 +130,13 @@ final class AphrontFormDateControl extends AphrontFormControl {
|
|||
$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() {
|
||||
|
|
|
@ -84,10 +84,33 @@ final class AphrontFormDateControlValue extends Phobject {
|
|||
$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 @@ final class AphrontFormDateControlValue extends Phobject {
|
|||
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 @@ final class AphrontFormDateControlValue extends Phobject {
|
|||
$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 @@ final class AphrontFormDateControlValue extends Phobject {
|
|||
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 @@ final class AphrontFormDateControlValue extends Phobject {
|
|||
}
|
||||
|
||||
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 @@ final class AphrontFormDateControlValue extends Phobject {
|
|||
}
|
||||
|
||||
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 @@ final class AphrontFormDateControlValue extends Phobject {
|
|||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue