mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-18 11:30:55 +01:00
Moderize Countdown
Summary: [DRAFT] Ref T8895. Makes a reasonable attempt at: - Project Support - Timeline / History - Better Search - Better ObjectItemLists Test Plan: Needs better testing (I'm sleepy) Reviewers: btrahan, epriestley Reviewed By: epriestley Subscribers: eadler, epriestley, Korvin Maniphest Tasks: T8895 Differential Revision: https://secure.phabricator.com/D13660
This commit is contained in:
parent
1643685e72
commit
fdd6351a64
16 changed files with 630 additions and 130 deletions
36
resources/sql/autopatches/20150719.countdown.1.sql
Normal file
36
resources/sql/autopatches/20150719.countdown.1.sql
Normal file
|
@ -0,0 +1,36 @@
|
|||
CREATE TABLE {$NAMESPACE}_countdown.countdown_transaction (
|
||||
id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
phid VARBINARY(64) NOT NULL,
|
||||
authorPHID VARBINARY(64) NOT NULL,
|
||||
objectPHID VARBINARY(64) NOT NULL,
|
||||
viewPolicy VARBINARY(64) NOT NULL,
|
||||
editPolicy VARBINARY(64) NOT NULL,
|
||||
commentPHID VARBINARY(64),
|
||||
commentVersion INT UNSIGNED NOT NULL,
|
||||
transactionType VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
oldValue LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
newValue LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
contentSource LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
metadata LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
dateModified INT UNSIGNED NOT NULL,
|
||||
UNIQUE KEY `key_phid` (phid),
|
||||
KEY `key_object` (objectPHID)
|
||||
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
||||
|
||||
CREATE TABLE {$NAMESPACE}_countdown.edge (
|
||||
src VARBINARY(64) NOT NULL,
|
||||
type INT UNSIGNED NOT NULL,
|
||||
dst VARBINARY(64) NOT NULL,
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
seq INT UNSIGNED NOT NULL,
|
||||
dataID INT UNSIGNED,
|
||||
PRIMARY KEY (src, type, dst),
|
||||
KEY `src` (src, type, dateCreated, seq),
|
||||
UNIQUE KEY `key_dst` (dst, type, src)
|
||||
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
||||
|
||||
CREATE TABLE {$NAMESPACE}_countdown.edgedata (
|
||||
id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
data LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}
|
||||
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
2
resources/sql/autopatches/20150719.countdown.2.sql
Normal file
2
resources/sql/autopatches/20150719.countdown.2.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_countdown.countdown
|
||||
ADD editPolicy VARBINARY(64) NOT NULL;
|
2
resources/sql/autopatches/20150719.countdown.3.sql
Normal file
2
resources/sql/autopatches/20150719.countdown.3.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
UPDATE {$NAMESPACE}_countdown.countdown
|
||||
SET editPolicy = authorPHID WHERE editPolicy = '';
|
|
@ -1799,13 +1799,19 @@ phutil_register_library_map(array(
|
|||
'PhabricatorCountdownController' => 'applications/countdown/controller/PhabricatorCountdownController.php',
|
||||
'PhabricatorCountdownCountdownPHIDType' => 'applications/countdown/phid/PhabricatorCountdownCountdownPHIDType.php',
|
||||
'PhabricatorCountdownDAO' => 'applications/countdown/storage/PhabricatorCountdownDAO.php',
|
||||
'PhabricatorCountdownDefaultEditCapability' => 'applications/countdown/capability/PhabricatorCountdownDefaultEditCapability.php',
|
||||
'PhabricatorCountdownDefaultViewCapability' => 'applications/countdown/capability/PhabricatorCountdownDefaultViewCapability.php',
|
||||
'PhabricatorCountdownDeleteController' => 'applications/countdown/controller/PhabricatorCountdownDeleteController.php',
|
||||
'PhabricatorCountdownEditController' => 'applications/countdown/controller/PhabricatorCountdownEditController.php',
|
||||
'PhabricatorCountdownEditor' => 'applications/countdown/editor/PhabricatorCountdownEditor.php',
|
||||
'PhabricatorCountdownListController' => 'applications/countdown/controller/PhabricatorCountdownListController.php',
|
||||
'PhabricatorCountdownQuery' => 'applications/countdown/query/PhabricatorCountdownQuery.php',
|
||||
'PhabricatorCountdownRemarkupRule' => 'applications/countdown/remarkup/PhabricatorCountdownRemarkupRule.php',
|
||||
'PhabricatorCountdownReplyHandler' => 'applications/countdown/mail/PhabricatorCountdownReplyHandler.php',
|
||||
'PhabricatorCountdownSchemaSpec' => 'applications/countdown/storage/PhabricatorCountdownSchemaSpec.php',
|
||||
'PhabricatorCountdownSearchEngine' => 'applications/countdown/query/PhabricatorCountdownSearchEngine.php',
|
||||
'PhabricatorCountdownTransaction' => 'applications/countdown/storage/PhabricatorCountdownTransaction.php',
|
||||
'PhabricatorCountdownTransactionQuery' => 'applications/countdown/query/PhabricatorCountdownTransactionQuery.php',
|
||||
'PhabricatorCountdownView' => 'applications/countdown/view/PhabricatorCountdownView.php',
|
||||
'PhabricatorCountdownViewController' => 'applications/countdown/controller/PhabricatorCountdownViewController.php',
|
||||
'PhabricatorCredentialsUsedByObjectEdgeType' => 'applications/passphrase/edge/PhabricatorCredentialsUsedByObjectEdgeType.php',
|
||||
|
@ -5548,19 +5554,28 @@ phutil_register_library_map(array(
|
|||
'PhabricatorCountdownDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
'PhabricatorFlaggableInterface',
|
||||
'PhabricatorSubscribableInterface',
|
||||
'PhabricatorApplicationTransactionInterface',
|
||||
'PhabricatorSpacesInterface',
|
||||
'PhabricatorProjectInterface',
|
||||
),
|
||||
'PhabricatorCountdownApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorCountdownController' => 'PhabricatorController',
|
||||
'PhabricatorCountdownCountdownPHIDType' => 'PhabricatorPHIDType',
|
||||
'PhabricatorCountdownDAO' => 'PhabricatorLiskDAO',
|
||||
'PhabricatorCountdownDefaultEditCapability' => 'PhabricatorPolicyCapability',
|
||||
'PhabricatorCountdownDefaultViewCapability' => 'PhabricatorPolicyCapability',
|
||||
'PhabricatorCountdownDeleteController' => 'PhabricatorCountdownController',
|
||||
'PhabricatorCountdownEditController' => 'PhabricatorCountdownController',
|
||||
'PhabricatorCountdownEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||
'PhabricatorCountdownListController' => 'PhabricatorCountdownController',
|
||||
'PhabricatorCountdownQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhabricatorCountdownRemarkupRule' => 'PhabricatorObjectRemarkupRule',
|
||||
'PhabricatorCountdownReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
|
||||
'PhabricatorCountdownSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
||||
'PhabricatorCountdownSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'PhabricatorCountdownTransaction' => 'PhabricatorApplicationTransaction',
|
||||
'PhabricatorCountdownTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||
'PhabricatorCountdownView' => 'AphrontTagView',
|
||||
'PhabricatorCountdownViewController' => 'PhabricatorCountdownController',
|
||||
'PhabricatorCredentialsUsedByObjectEdgeType' => 'PhabricatorEdgeType',
|
||||
|
|
|
@ -38,6 +38,7 @@ final class PhabricatorCountdownApplication extends PhabricatorApplication {
|
|||
|
||||
public function getRoutes() {
|
||||
return array(
|
||||
'/C(?P<id>[1-9]\d*)' => 'PhabricatorCountdownViewController',
|
||||
'/countdown/' => array(
|
||||
'(?:query/(?P<queryKey>[^/]+)/)?'
|
||||
=> 'PhabricatorCountdownListController',
|
||||
|
@ -55,6 +56,11 @@ final class PhabricatorCountdownApplication extends PhabricatorApplication {
|
|||
'template' => PhabricatorCountdownCountdownPHIDType::TYPECONST,
|
||||
'capability' => PhabricatorPolicyCapability::CAN_VIEW,
|
||||
),
|
||||
PhabricatorCountdownDefaultEditCapability::CAPABILITY => array(
|
||||
'caption' => pht('Default edit policy for new countdowns.'),
|
||||
'template' => PhabricatorCountdownCountdownPHIDType::TYPECONST,
|
||||
'capability' => PhabricatorPolicyCapability::CAN_EDIT,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCountdownDefaultEditCapability
|
||||
extends PhabricatorPolicyCapability {
|
||||
|
||||
const CAPABILITY = 'countdown.default.edit';
|
||||
|
||||
public function getCapabilityName() {
|
||||
return pht('Default Edit Policy');
|
||||
}
|
||||
|
||||
}
|
|
@ -3,21 +3,15 @@
|
|||
final class PhabricatorCountdownEditController
|
||||
extends PhabricatorCountdownController {
|
||||
|
||||
private $id;
|
||||
public function willProcessRequest(array $data) {
|
||||
$this->id = idx($data, 'id');
|
||||
}
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
$id = $request->getURIData('id');
|
||||
|
||||
public function processRequest() {
|
||||
|
||||
$request = $this->getRequest();
|
||||
$user = $request->getUser();
|
||||
|
||||
if ($this->id) {
|
||||
if ($id) {
|
||||
$page_title = pht('Edit Countdown');
|
||||
$countdown = id(new PhabricatorCountdownQuery())
|
||||
->setViewer($user)
|
||||
->withIDs(array($this->id))
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($id))
|
||||
->requireCapabilities(
|
||||
array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
|
@ -28,12 +22,18 @@ final class PhabricatorCountdownEditController
|
|||
return new Aphront404Response();
|
||||
}
|
||||
$date_value = AphrontFormDateControlValue::newFromEpoch(
|
||||
$user,
|
||||
$viewer,
|
||||
$countdown->getEpoch());
|
||||
$v_projects = PhabricatorEdgeQuery::loadDestinationPHIDs(
|
||||
$countdown->getPHID(),
|
||||
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
|
||||
$v_projects = array_reverse($v_projects);
|
||||
} else {
|
||||
$page_title = pht('Create Countdown');
|
||||
$countdown = PhabricatorCountdown::initializeNewCountdown($user);
|
||||
$date_value = AphrontFormDateControlValue::newFromEpoch($user, time());
|
||||
$countdown = PhabricatorCountdown::initializeNewCountdown($viewer);
|
||||
$date_value = AphrontFormDateControlValue::newFromEpoch(
|
||||
$viewer, PhabricatorTime::getNow());
|
||||
$v_projects = array();
|
||||
}
|
||||
|
||||
$errors = array();
|
||||
|
@ -42,6 +42,8 @@ final class PhabricatorCountdownEditController
|
|||
|
||||
$v_text = $countdown->getTitle();
|
||||
$v_space = $countdown->getSpacePHID();
|
||||
$v_view = $countdown->getViewPolicy();
|
||||
$v_edit = $countdown->getEditPolicy();
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
$v_text = $request->getStr('title');
|
||||
|
@ -49,27 +51,61 @@ final class PhabricatorCountdownEditController
|
|||
$date_value = AphrontFormDateControlValue::newFromRequest(
|
||||
$request,
|
||||
'epoch');
|
||||
$view_policy = $request->getStr('viewPolicy');
|
||||
$v_view = $request->getStr('viewPolicy');
|
||||
$v_edit = $request->getStr('editPolicy');
|
||||
$v_projects = $request->getArr('projects');
|
||||
|
||||
$e_text = null;
|
||||
if (!strlen($v_text)) {
|
||||
$e_text = pht('Required');
|
||||
$errors[] = pht('You must give the countdown a name.');
|
||||
}
|
||||
if (!$date_value->isValid()) {
|
||||
$e_epoch = pht('Invalid');
|
||||
$errors[] = pht('You must give the countdown a valid end date.');
|
||||
}
|
||||
$type_title = PhabricatorCountdownTransaction::TYPE_TITLE;
|
||||
$type_epoch = PhabricatorCountdownTransaction::TYPE_EPOCH;
|
||||
$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_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);
|
||||
|
||||
if (!count($errors)) {
|
||||
$countdown->setTitle($v_text);
|
||||
$countdown->setEpoch($date_value->getEpoch());
|
||||
$countdown->setViewPolicy($view_policy);
|
||||
$countdown->setSpacePHID($v_space);
|
||||
$countdown->save();
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI('/countdown/'.$countdown->getID().'/');
|
||||
->setURI('/'.$countdown->getMonogram());
|
||||
} catch (PhabricatorApplicationTransactionValidationException $ex) {
|
||||
$validation_exception = $ex;
|
||||
|
||||
$e_title = $ex->getShortMessage($type_title);
|
||||
$e_epoch = $ex->getShortMessage($type_epoch);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
|
@ -86,12 +122,12 @@ final class PhabricatorCountdownEditController
|
|||
}
|
||||
|
||||
$policies = id(new PhabricatorPolicyQuery())
|
||||
->setViewer($user)
|
||||
->setViewer($viewer)
|
||||
->setObject($countdown)
|
||||
->execute();
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setUser($user)
|
||||
->setUser($viewer)
|
||||
->setAction($request->getRequestURI()->getPath())
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
|
@ -99,21 +135,33 @@ final class PhabricatorCountdownEditController
|
|||
->setValue($v_text)
|
||||
->setName('title')
|
||||
->setError($e_text))
|
||||
->appendChild(
|
||||
->appendControl(
|
||||
id(new AphrontFormDateControl())
|
||||
->setUser($user)
|
||||
->setName('epoch')
|
||||
->setLabel(pht('End Date'))
|
||||
->setError($e_epoch)
|
||||
->setValue($date_value))
|
||||
->appendChild(
|
||||
->appendControl(
|
||||
id(new AphrontFormPolicyControl())
|
||||
->setUser($user)
|
||||
->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)
|
||||
|
|
|
@ -3,31 +3,24 @@
|
|||
final class PhabricatorCountdownViewController
|
||||
extends PhabricatorCountdownController {
|
||||
|
||||
private $id;
|
||||
|
||||
public function shouldAllowPublic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function willProcessRequest(array $data) {
|
||||
$this->id = $data['id'];
|
||||
}
|
||||
|
||||
public function processRequest() {
|
||||
|
||||
$request = $this->getRequest();
|
||||
$user = $request->getUser();
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
$id = $request->getURIData('id');
|
||||
|
||||
$countdown = id(new PhabricatorCountdownQuery())
|
||||
->setViewer($user)
|
||||
->withIDs(array($this->id))
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($id))
|
||||
->executeOne();
|
||||
if (!$countdown) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$countdown_view = id(new PhabricatorCountdownView())
|
||||
->setUser($user)
|
||||
->setUser($viewer)
|
||||
->setCountdown($countdown)
|
||||
->setHeadless(true);
|
||||
|
||||
|
@ -38,10 +31,22 @@ final class PhabricatorCountdownViewController
|
|||
->buildApplicationCrumbs()
|
||||
->addTextCrumb("C{$id}");
|
||||
|
||||
$epoch = $countdown->getEpoch();
|
||||
if ($epoch >= PhabricatorTime::getNow()) {
|
||||
$icon = 'fa-clock-o';
|
||||
$color = '';
|
||||
$status = pht('Running');
|
||||
} else {
|
||||
$icon = 'fa-check-square-o';
|
||||
$color = 'dark';
|
||||
$status = pht('Launched');
|
||||
}
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader($title)
|
||||
->setUser($user)
|
||||
->setPolicyObject($countdown);
|
||||
->setUser($viewer)
|
||||
->setPolicyObject($countdown)
|
||||
->setStatus($icon, $color, $status);
|
||||
|
||||
$actions = $this->buildActionListView($countdown);
|
||||
$properties = $this->buildPropertyListView($countdown, $actions);
|
||||
|
@ -50,10 +55,16 @@ final class PhabricatorCountdownViewController
|
|||
->setHeader($header)
|
||||
->addPropertyList($properties);
|
||||
|
||||
$timeline = $this->buildTransactionTimeline(
|
||||
$countdown,
|
||||
new PhabricatorCountdownTransactionQuery());
|
||||
$timeline->setShouldTerminate(true);
|
||||
|
||||
$content = array(
|
||||
$crumbs,
|
||||
$object_box,
|
||||
$countdown_view,
|
||||
$timeline,
|
||||
);
|
||||
|
||||
return $this->buildApplicationPage(
|
||||
|
@ -105,6 +116,7 @@ final class PhabricatorCountdownViewController
|
|||
|
||||
$view = id(new PHUIPropertyListView())
|
||||
->setUser($viewer)
|
||||
->setObject($countdown)
|
||||
->setActionList($actions);
|
||||
|
||||
$view->addProperty(
|
||||
|
|
193
src/applications/countdown/editor/PhabricatorCountdownEditor.php
Normal file
193
src/applications/countdown/editor/PhabricatorCountdownEditor.php
Normal file
|
@ -0,0 +1,193 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCountdownEditor
|
||||
extends PhabricatorApplicationTransactionEditor {
|
||||
|
||||
public function getEditorApplicationClass() {
|
||||
return 'PhabricatorCountdownApplication';
|
||||
}
|
||||
|
||||
public function getEditorObjectsDescription() {
|
||||
return pht('Countdown');
|
||||
}
|
||||
|
||||
public function getTransactionTypes() {
|
||||
$types = parent::getTransactionTypes();
|
||||
|
||||
$types[] = PhabricatorCountdownTransaction::TYPE_TITLE;
|
||||
$types[] = PhabricatorCountdownTransaction::TYPE_EPOCH;
|
||||
|
||||
$types[] = PhabricatorTransactions::TYPE_EDGE;
|
||||
$types[] = PhabricatorTransactions::TYPE_SPACE;
|
||||
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
|
||||
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
protected function getCustomTransactionOldValue(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case PhabricatorCountdownTransaction::TYPE_TITLE:
|
||||
return $object->getTitle();
|
||||
case PhabricatorCountdownTransaction::TYPE_EPOCH:
|
||||
return $object->getEpoch();
|
||||
}
|
||||
|
||||
return parent::getCustomTransactionOldValue($object, $xaction);
|
||||
}
|
||||
|
||||
protected function getCustomTransactionNewValue(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case PhabricatorCountdownTransaction::TYPE_TITLE:
|
||||
return $xaction->getNewValue();
|
||||
case PhabricatorCountdownTransaction::TYPE_EPOCH:
|
||||
return $xaction->getNewValue()->getEpoch();
|
||||
}
|
||||
|
||||
return parent::getCustomTransactionNewValue($object, $xaction);
|
||||
}
|
||||
|
||||
protected function applyCustomInternalTransaction(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
$type = $xaction->getTransactionType();
|
||||
switch ($type) {
|
||||
case PhabricatorCountdownTransaction::TYPE_TITLE:
|
||||
$object->setTitle($xaction->getNewValue());
|
||||
return;
|
||||
case PhabricatorCountdownTransaction::TYPE_EPOCH:
|
||||
$object->setEpoch($xaction->getNewValue());
|
||||
return;
|
||||
}
|
||||
|
||||
return parent::applyCustomInternalTransaction($object, $xaction);
|
||||
}
|
||||
|
||||
protected function applyCustomExternalTransaction(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
$type = $xaction->getTransactionType();
|
||||
switch ($type) {
|
||||
case PhabricatorCountdownTransaction::TYPE_TITLE:
|
||||
return;
|
||||
case PhabricatorCountdownTransaction::TYPE_EPOCH:
|
||||
return;
|
||||
}
|
||||
|
||||
return parent::applyCustomExternalTransaction($object, $xaction);
|
||||
}
|
||||
|
||||
protected function validateTransaction(
|
||||
PhabricatorLiskDAO $object,
|
||||
$type,
|
||||
array $xactions) {
|
||||
|
||||
$errors = parent::validateTransaction($object, $type, $xactions);
|
||||
|
||||
switch ($type) {
|
||||
case PhabricatorCountdownTransaction::TYPE_TITLE:
|
||||
$missing = $this->validateIsEmptyTextField(
|
||||
$object->getTitle(),
|
||||
$xactions);
|
||||
|
||||
if ($missing) {
|
||||
$error = new PhabricatorApplicationTransactionValidationError(
|
||||
$type,
|
||||
pht('Required'),
|
||||
pht('You must give the countdown a name.'),
|
||||
nonempty(last($xactions), null));
|
||||
|
||||
$error->setIsMissingFieldError(true);
|
||||
$errors[] = $error;
|
||||
}
|
||||
break;
|
||||
case PhabricatorCountdownTransaction::TYPE_EPOCH:
|
||||
$date_value = AphrontFormDateControlValue::newFromEpoch(
|
||||
$this->requireActor(),
|
||||
$object->getEpoch());
|
||||
if (!$date_value->isValid()) {
|
||||
$error = new PhabricatorApplicationTransactionValidationError(
|
||||
$type,
|
||||
pht('Invalid'),
|
||||
pht('You must give the countdown a valid end date.'),
|
||||
nonempty(last($xactions), null));
|
||||
|
||||
$error->setIsMissingFieldError(true);
|
||||
$errors[] = $error;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
protected function shouldSendMail(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getMailTagsMap() {
|
||||
return array(
|
||||
PhabricatorCountdownTransaction::MAILTAG_TITLE =>
|
||||
pht('Someone changes the countdown title.'),
|
||||
PhabricatorCountdownTransaction::MAILTAG_EPOCH =>
|
||||
pht('Someone changes the countdown end date.'),
|
||||
PhabricatorCountdownTransaction::MAILTAG_OTHER =>
|
||||
pht('Other countdown activity not listed above occurs.'),
|
||||
);
|
||||
}
|
||||
|
||||
protected function buildMailTemplate(PhabricatorLiskDAO $object) {
|
||||
$monogram = $object->getMonogram();
|
||||
$name = $object->getName();
|
||||
|
||||
return id(new PhabricatorMetaMTAMail())
|
||||
->setSubject("{$monogram}: {$name}")
|
||||
->addHeader('Thread-Topic', $monogram);
|
||||
}
|
||||
|
||||
protected function buildMailBody(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
|
||||
$body = parent::buildMailBody($object, $xactions);
|
||||
|
||||
$body->addLinkSection(
|
||||
pht('COUNTDOWN DETAIL'),
|
||||
PhabricatorEnv::getProductionURI('/'.$object->getMonogram()));
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
protected function getMailTo(PhabricatorLiskDAO $object) {
|
||||
return array($object->getAuthorPHID());
|
||||
}
|
||||
|
||||
protected function getMailSubjectPrefix() {
|
||||
return 'Countdown';
|
||||
}
|
||||
|
||||
protected function buildReplyHandler(PhabricatorLiskDAO $object) {
|
||||
return id(new PhabricatorCountdownReplyHandler())
|
||||
->setMailReceiver($object);
|
||||
}
|
||||
|
||||
protected function shouldPublishFeedStory(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function supportsSearch() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCountdownReplyHandler
|
||||
extends PhabricatorApplicationTransactionReplyHandler {
|
||||
|
||||
public function validateMailReceiver($mail_receiver) {
|
||||
if (!($mail_receiver instanceof PhabricatorCountdown)) {
|
||||
throw new Exception(pht('Mail receiver is not a %s!', 'Countdown'));
|
||||
}
|
||||
}
|
||||
|
||||
public function getObjectPrefix() {
|
||||
return 'C';
|
||||
}
|
||||
|
||||
}
|
|
@ -23,62 +23,51 @@ final class PhabricatorCountdownQuery
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function withUpcoming($upcoming) {
|
||||
public function withUpcoming() {
|
||||
$this->upcoming = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function loadPage() {
|
||||
$table = new PhabricatorCountdown();
|
||||
$conn_r = $table->establishConnection('r');
|
||||
|
||||
$data = queryfx_all(
|
||||
$conn_r,
|
||||
'SELECT * FROM %T %Q %Q %Q',
|
||||
$table->getTableName(),
|
||||
$this->buildWhereClause($conn_r),
|
||||
$this->buildOrderClause($conn_r),
|
||||
$this->buildLimitClause($conn_r));
|
||||
|
||||
$countdowns = $table->loadAllFromArray($data);
|
||||
|
||||
return $countdowns;
|
||||
return $this->loadStandardPage($this->newResultObject());
|
||||
}
|
||||
|
||||
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
|
||||
$where = array();
|
||||
public function newResultObject() {
|
||||
return new PhabricatorCountdown();
|
||||
}
|
||||
|
||||
$where[] = $this->buildPagingClause($conn_r);
|
||||
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
|
||||
$where = parent::buildWhereClauseParts($conn);
|
||||
|
||||
if ($this->ids) {
|
||||
if ($this->ids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'id IN (%Ld)',
|
||||
$this->ids);
|
||||
}
|
||||
|
||||
if ($this->phids) {
|
||||
if ($this->phids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'phid IN (%Ls)',
|
||||
$this->phids);
|
||||
}
|
||||
|
||||
if ($this->authorPHIDs) {
|
||||
if ($this->authorPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'authorPHID in (%Ls)',
|
||||
$this->authorPHIDs);
|
||||
}
|
||||
|
||||
if ($this->upcoming) {
|
||||
if ($this->upcoming !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'epoch >= %d',
|
||||
time());
|
||||
PhabricatorTime::getNow());
|
||||
}
|
||||
|
||||
return $this->formatWhereClause($where);
|
||||
return $where;
|
||||
}
|
||||
|
||||
public function getQueryApplicationClass() {
|
||||
|
|
|
@ -11,53 +11,39 @@ final class PhabricatorCountdownSearchEngine
|
|||
return 'PhabricatorCountdownApplication';
|
||||
}
|
||||
|
||||
public function buildSavedQueryFromRequest(AphrontRequest $request) {
|
||||
$saved = new PhabricatorSavedQuery();
|
||||
$saved->setParameter(
|
||||
'authorPHIDs',
|
||||
$this->readUsersFromRequest($request, 'authors'));
|
||||
|
||||
$saved->setParameter('upcoming', $request->getBool('upcoming'));
|
||||
|
||||
return $saved;
|
||||
public function newQuery() {
|
||||
return new PhabricatorCountdownQuery();
|
||||
}
|
||||
|
||||
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
|
||||
$query = id(new PhabricatorCountdownQuery());
|
||||
protected function buildQueryFromParameters(array $map) {
|
||||
$query = $this->newQuery();
|
||||
|
||||
$author_phids = $saved->getParameter('authorPHIDs', array());
|
||||
if ($author_phids) {
|
||||
$query->withAuthorPHIDs($author_phids);
|
||||
if ($map['authorPHIDs']) {
|
||||
$query->withAuthorPHIDs($map['authorPHIDs']);
|
||||
}
|
||||
|
||||
if ($saved->getParameter('upcoming')) {
|
||||
$query->withUpcoming(true);
|
||||
if ($map['upcoming'] && $map['upcoming'][0] == 'upcoming') {
|
||||
$query->withUpcoming();
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function buildSearchForm(
|
||||
AphrontFormView $form,
|
||||
PhabricatorSavedQuery $saved_query) {
|
||||
protected function buildCustomSearchFields() {
|
||||
|
||||
$author_phids = $saved_query->getParameter('authorPHIDs', array());
|
||||
$upcoming = $saved_query->getParameter('upcoming');
|
||||
|
||||
$form
|
||||
->appendControl(
|
||||
id(new AphrontFormTokenizerControl())
|
||||
->setDatasource(new PhabricatorPeopleDatasource())
|
||||
->setName('authors')
|
||||
return array(
|
||||
id(new PhabricatorUsersSearchField())
|
||||
->setLabel(pht('Authors'))
|
||||
->setValue($author_phids))
|
||||
->appendChild(
|
||||
id(new AphrontFormCheckboxControl())
|
||||
->addCheckbox(
|
||||
'upcoming',
|
||||
1,
|
||||
pht('Show only countdowns that are still counting down.'),
|
||||
$upcoming));
|
||||
->setKey('authorPHIDs')
|
||||
->setAliases(array('author', 'authors')),
|
||||
|
||||
id(new PhabricatorSearchCheckboxesField())
|
||||
->setKey('upcoming')
|
||||
->setOptions(array(
|
||||
'upcoming' => pht('Show only upcoming countdowns.'),
|
||||
)),
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
protected function getURI($path) {
|
||||
|
@ -89,7 +75,7 @@ final class PhabricatorCountdownSearchEngine
|
|||
'authorPHIDs',
|
||||
array($this->requireViewer()->getPHID()));
|
||||
case 'upcoming':
|
||||
return $query->setParameter('upcoming', true);
|
||||
return $query->setParameter('upcoming', array('upcoming'));
|
||||
}
|
||||
|
||||
return parent::buildSavedQueryFromBuiltin($query_key);
|
||||
|
@ -115,28 +101,35 @@ final class PhabricatorCountdownSearchEngine
|
|||
$list->setUser($viewer);
|
||||
foreach ($countdowns as $countdown) {
|
||||
$id = $countdown->getID();
|
||||
$ended = false;
|
||||
$icon = 'fa-clock-o';
|
||||
$color = 'green';
|
||||
$epoch = $countdown->getEpoch();
|
||||
if ($epoch <= PhabricatorTime::getNow()) {
|
||||
$ended = true;
|
||||
$icon = 'fa-check-square-o';
|
||||
$color = 'grey';
|
||||
}
|
||||
|
||||
$item = id(new PHUIObjectItemView())
|
||||
->setUser($viewer)
|
||||
->setObject($countdown)
|
||||
->setObjectName("C{$id}")
|
||||
->setHeader($countdown->getTitle())
|
||||
->setStatusIcon($icon.' '.$color)
|
||||
->setHref($this->getApplicationURI("{$id}/"))
|
||||
->addByline(
|
||||
pht(
|
||||
'Created by %s',
|
||||
$handles[$countdown->getAuthorPHID()]->renderLink()));
|
||||
|
||||
$epoch = $countdown->getEpoch();
|
||||
if ($epoch >= time()) {
|
||||
$item->addIcon(
|
||||
'none',
|
||||
pht('Ends %s', phabricator_datetime($epoch, $viewer)));
|
||||
} else {
|
||||
$item->addIcon(
|
||||
'delete',
|
||||
pht('Ended %s', phabricator_datetime($epoch, $viewer)));
|
||||
if ($ended) {
|
||||
$item->addAttribute(
|
||||
pht('Launched on %s', phabricator_datetime($epoch, $viewer)));
|
||||
$item->setDisabled(true);
|
||||
} else {
|
||||
$item->addAttribute(
|
||||
phabricator_datetime($epoch, $viewer));
|
||||
}
|
||||
|
||||
$list->addItem($item);
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCountdownTransactionQuery
|
||||
extends PhabricatorApplicationTransactionQuery {
|
||||
|
||||
public function getTemplateApplicationTransaction() {
|
||||
return new PhabricatorCountdownTransaction();
|
||||
}
|
||||
|
||||
}
|
|
@ -4,12 +4,16 @@ final class PhabricatorCountdown extends PhabricatorCountdownDAO
|
|||
implements
|
||||
PhabricatorPolicyInterface,
|
||||
PhabricatorFlaggableInterface,
|
||||
PhabricatorSpacesInterface {
|
||||
PhabricatorSubscribableInterface,
|
||||
PhabricatorApplicationTransactionInterface,
|
||||
PhabricatorSpacesInterface,
|
||||
PhabricatorProjectInterface {
|
||||
|
||||
protected $title;
|
||||
protected $authorPHID;
|
||||
protected $epoch;
|
||||
protected $viewPolicy;
|
||||
protected $editPolicy;
|
||||
|
||||
protected $spacePHID;
|
||||
|
||||
|
@ -43,6 +47,48 @@ final class PhabricatorCountdown extends PhabricatorCountdownDAO
|
|||
PhabricatorCountdownCountdownPHIDType::TYPECONST);
|
||||
}
|
||||
|
||||
public function getMonogram() {
|
||||
return 'C'.$this->getID();
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorSubscribableInterface )----------------------------------- */
|
||||
|
||||
|
||||
public function isAutomaticallySubscribed($phid) {
|
||||
return ($phid == $this->getAuthorPHID());
|
||||
}
|
||||
|
||||
public function shouldShowSubscribersProperty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function shouldAllowSubscription($phid) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
|
||||
|
||||
|
||||
public function getApplicationTransactionEditor() {
|
||||
return new PhabricatorCountdownEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new PhabricatorCountdownTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
@ -59,16 +105,16 @@ final class PhabricatorCountdown extends PhabricatorCountdownDAO
|
|||
case PhabricatorPolicyCapability::CAN_VIEW:
|
||||
return $this->getViewPolicy();
|
||||
case PhabricatorPolicyCapability::CAN_EDIT:
|
||||
return PhabricatorPolicies::POLICY_NOONE;
|
||||
return $this->getEditPolicy();
|
||||
}
|
||||
}
|
||||
|
||||
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
|
||||
return ($viewer->getPHID() == $this->getAuthorPHID());
|
||||
return false;
|
||||
}
|
||||
|
||||
public function describeAutomaticCapability($capability) {
|
||||
return pht('The author of a countdown can always view and edit it.');
|
||||
return false;
|
||||
}
|
||||
|
||||
/* -( PhabricatorSpacesInterface )------------------------------------------- */
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCountdownSchemaSpec
|
||||
extends PhabricatorConfigSchemaSpec {
|
||||
|
||||
public function buildSchemata() {
|
||||
$this->buildEdgeSchemata(new PhabricatorCountdown());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCountdownTransaction
|
||||
extends PhabricatorApplicationTransaction {
|
||||
|
||||
const TYPE_TITLE = 'countdown:title';
|
||||
const TYPE_EPOCH = 'countdown:epoch';
|
||||
|
||||
const MAILTAG_TITLE = 'countdown:title';
|
||||
const MAILTAG_EPOCH = 'countdown:epoch';
|
||||
const MAILTAG_OTHER = 'countdown:other';
|
||||
|
||||
public function getApplicationName() {
|
||||
return 'countdown';
|
||||
}
|
||||
|
||||
public function getApplicationTransactionType() {
|
||||
return PhabricatorCountdownCountdownPHIDType::TYPECONST;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionCommentObject() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
$author_phid = $this->getAuthorPHID();
|
||||
$object_phid = $this->getObjectPHID();
|
||||
|
||||
$old = $this->getOldValue();
|
||||
$new = $this->getNewValue();
|
||||
|
||||
$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);
|
||||
}
|
||||
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 parent::getTitle();
|
||||
}
|
||||
|
||||
public function getTitleForFeed() {
|
||||
$author_phid = $this->getAuthorPHID();
|
||||
$object_phid = $this->getObjectPHID();
|
||||
|
||||
$old = $this->getOldValue();
|
||||
$new = $this->getNewValue();
|
||||
|
||||
$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 parent::getTitleForFeed();
|
||||
}
|
||||
|
||||
public function getMailTags() {
|
||||
$tags = parent::getMailTags();
|
||||
|
||||
switch ($this->getTransactionType()) {
|
||||
case self::TYPE_TITLE:
|
||||
$tags[] = self::MAILTAG_TITLE;
|
||||
break;
|
||||
case self::TYPE_EPOCH:
|
||||
$tags[] = self::MAILTAG_EPOCH;
|
||||
break;
|
||||
default:
|
||||
$tags[] = self::MAILTAG_OTHER;
|
||||
break;
|
||||
}
|
||||
|
||||
return $tags;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue