mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-18 19:40:55 +01:00
Basic stacked action support for EditEngine
Summary: Ref T9132. This still has a lot of rough edges but the basics seem to work OK. Test Plan: {F1012627} Reviewers: chad Reviewed By: chad Maniphest Tasks: T9132 Differential Revision: https://secure.phabricator.com/D14653
This commit is contained in:
parent
b82863d972
commit
dc0d914134
14 changed files with 532 additions and 8 deletions
|
@ -420,6 +420,7 @@ return array(
|
||||||
'rsrc/js/application/repository/repository-crossreference.js' => 'e5339c43',
|
'rsrc/js/application/repository/repository-crossreference.js' => 'e5339c43',
|
||||||
'rsrc/js/application/search/behavior-reorder-queries.js' => 'e9581f08',
|
'rsrc/js/application/search/behavior-reorder-queries.js' => 'e9581f08',
|
||||||
'rsrc/js/application/slowvote/behavior-slowvote-embed.js' => '887ad43f',
|
'rsrc/js/application/slowvote/behavior-slowvote-embed.js' => '887ad43f',
|
||||||
|
'rsrc/js/application/transactions/behavior-comment-actions.js' => 'f2c64202',
|
||||||
'rsrc/js/application/transactions/behavior-reorder-fields.js' => 'b59e1e96',
|
'rsrc/js/application/transactions/behavior-reorder-fields.js' => 'b59e1e96',
|
||||||
'rsrc/js/application/transactions/behavior-show-older-transactions.js' => 'dbbf48b6',
|
'rsrc/js/application/transactions/behavior-show-older-transactions.js' => 'dbbf48b6',
|
||||||
'rsrc/js/application/transactions/behavior-transaction-comment-form.js' => 'b23b49e6',
|
'rsrc/js/application/transactions/behavior-transaction-comment-form.js' => 'b23b49e6',
|
||||||
|
@ -498,6 +499,8 @@ return array(
|
||||||
'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8',
|
'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8',
|
||||||
'rsrc/js/phuix/PHUIXActionView.js' => '8cf6d262',
|
'rsrc/js/phuix/PHUIXActionView.js' => '8cf6d262',
|
||||||
'rsrc/js/phuix/PHUIXDropdownMenu.js' => 'bd4c8dca',
|
'rsrc/js/phuix/PHUIXDropdownMenu.js' => 'bd4c8dca',
|
||||||
|
'rsrc/js/phuix/PHUIXFormControl.js' => 'f9fba5ee',
|
||||||
|
'rsrc/js/phuix/PHUIXIconView.js' => 'bff6884b',
|
||||||
),
|
),
|
||||||
'symbols' => array(
|
'symbols' => array(
|
||||||
'almanac-css' => 'dbb9b3af',
|
'almanac-css' => 'dbb9b3af',
|
||||||
|
@ -561,6 +564,7 @@ return array(
|
||||||
'javelin-behavior-audit-preview' => 'd835b03a',
|
'javelin-behavior-audit-preview' => 'd835b03a',
|
||||||
'javelin-behavior-bulk-job-reload' => 'edf8a145',
|
'javelin-behavior-bulk-job-reload' => 'edf8a145',
|
||||||
'javelin-behavior-choose-control' => '6153c708',
|
'javelin-behavior-choose-control' => '6153c708',
|
||||||
|
'javelin-behavior-comment-actions' => 'f2c64202',
|
||||||
'javelin-behavior-config-reorder-fields' => 'b6993408',
|
'javelin-behavior-config-reorder-fields' => 'b6993408',
|
||||||
'javelin-behavior-conpherence-drag-and-drop-photo' => 'cf86d16a',
|
'javelin-behavior-conpherence-drag-and-drop-photo' => 'cf86d16a',
|
||||||
'javelin-behavior-conpherence-menu' => '1d45c74d',
|
'javelin-behavior-conpherence-menu' => '1d45c74d',
|
||||||
|
@ -823,6 +827,8 @@ return array(
|
||||||
'phuix-action-list-view' => 'b5c256b8',
|
'phuix-action-list-view' => 'b5c256b8',
|
||||||
'phuix-action-view' => '8cf6d262',
|
'phuix-action-view' => '8cf6d262',
|
||||||
'phuix-dropdown-menu' => 'bd4c8dca',
|
'phuix-dropdown-menu' => 'bd4c8dca',
|
||||||
|
'phuix-form-control-view' => 'f9fba5ee',
|
||||||
|
'phuix-icon-view' => 'bff6884b',
|
||||||
'policy-css' => '957ea14c',
|
'policy-css' => '957ea14c',
|
||||||
'policy-edit-css' => '815c66f7',
|
'policy-edit-css' => '815c66f7',
|
||||||
'policy-transaction-detail-css' => '82100a43',
|
'policy-transaction-detail-css' => '82100a43',
|
||||||
|
@ -1767,6 +1773,10 @@ return array(
|
||||||
'javelin-util',
|
'javelin-util',
|
||||||
'javelin-request',
|
'javelin-request',
|
||||||
),
|
),
|
||||||
|
'bff6884b' => array(
|
||||||
|
'javelin-install',
|
||||||
|
'javelin-dom',
|
||||||
|
),
|
||||||
'c1700f6f' => array(
|
'c1700f6f' => array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
'javelin-util',
|
'javelin-util',
|
||||||
|
@ -1973,6 +1983,14 @@ return array(
|
||||||
'javelin-workflow',
|
'javelin-workflow',
|
||||||
'javelin-json',
|
'javelin-json',
|
||||||
),
|
),
|
||||||
|
'f2c64202' => array(
|
||||||
|
'javelin-behavior',
|
||||||
|
'javelin-stratcom',
|
||||||
|
'javelin-workflow',
|
||||||
|
'javelin-dom',
|
||||||
|
'phuix-form-control-view',
|
||||||
|
'phuix-icon-view',
|
||||||
|
),
|
||||||
'f36e01af' => array(
|
'f36e01af' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-behavior-device',
|
'javelin-behavior-device',
|
||||||
|
@ -2029,6 +2047,10 @@ return array(
|
||||||
'javelin-util',
|
'javelin-util',
|
||||||
'phabricator-busy',
|
'phabricator-busy',
|
||||||
),
|
),
|
||||||
|
'f9fba5ee' => array(
|
||||||
|
'javelin-install',
|
||||||
|
'javelin-dom',
|
||||||
|
),
|
||||||
'fa0f4fc2' => array(
|
'fa0f4fc2' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
|
|
|
@ -34,6 +34,7 @@ final class PhabricatorPasteViewController extends PhabricatorPasteController {
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withIDs(array($id))
|
->withIDs(array($id))
|
||||||
->needContent(true)
|
->needContent(true)
|
||||||
|
->needRawContent(true)
|
||||||
->executeOne();
|
->executeOne();
|
||||||
if (!$paste) {
|
if (!$paste) {
|
||||||
return new Aphront404Response();
|
return new Aphront404Response();
|
||||||
|
|
|
@ -53,6 +53,7 @@ final class PhabricatorProjectsEditEngineExtension
|
||||||
pht('Add projects.'),
|
pht('Add projects.'),
|
||||||
pht('Remove projects.'),
|
pht('Remove projects.'),
|
||||||
pht('Set associated projects, overwriting current value.'))
|
pht('Set associated projects, overwriting current value.'))
|
||||||
|
->setCommentActionLabel(pht('Add Projects'))
|
||||||
->setTransactionType($edge_type)
|
->setTransactionType($edge_type)
|
||||||
->setMetadataValue('edge:type', $project_edge_type)
|
->setMetadataValue('edge:type', $project_edge_type)
|
||||||
->setValue($project_phids);
|
->setValue($project_phids);
|
||||||
|
|
|
@ -50,6 +50,7 @@ final class PhabricatorSubscriptionsEditEngineExtension
|
||||||
pht('Add subscribers.'),
|
pht('Add subscribers.'),
|
||||||
pht('Remove subscribers.'),
|
pht('Remove subscribers.'),
|
||||||
pht('Set subscribers, overwriting current value.'))
|
pht('Set subscribers, overwriting current value.'))
|
||||||
|
->setCommentActionLabel(pht('Add Subscribers'))
|
||||||
->setTransactionType($subscribers_type)
|
->setTransactionType($subscribers_type)
|
||||||
->setValue($sub_phids);
|
->setValue($sub_phids);
|
||||||
|
|
||||||
|
|
|
@ -873,6 +873,8 @@ abstract class PhabricatorEditEngine
|
||||||
}
|
}
|
||||||
|
|
||||||
final public function buildEditEngineCommentView($object) {
|
final public function buildEditEngineCommentView($object) {
|
||||||
|
$config = $this->loadEditEngineConfiguration(null);
|
||||||
|
|
||||||
$viewer = $this->getViewer();
|
$viewer = $this->getViewer();
|
||||||
$object_phid = $object->getPHID();
|
$object_phid = $object->getPHID();
|
||||||
|
|
||||||
|
@ -897,6 +899,19 @@ abstract class PhabricatorEditEngine
|
||||||
|
|
||||||
$view->setCurrentVersion($this->loadDraftVersion($object));
|
$view->setCurrentVersion($this->loadDraftVersion($object));
|
||||||
|
|
||||||
|
$fields = $this->buildEditFields($object);
|
||||||
|
|
||||||
|
$all_types = array();
|
||||||
|
foreach ($fields as $field) {
|
||||||
|
// TODO: Load draft stuff.
|
||||||
|
$types = $field->getCommentEditTypes();
|
||||||
|
foreach ($types as $type) {
|
||||||
|
$all_types[] = $type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$view->setEditTypes($all_types);
|
||||||
|
|
||||||
return $view;
|
return $view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -999,6 +1014,9 @@ abstract class PhabricatorEditEngine
|
||||||
return new Aphront400Response();
|
return new Aphront400Response();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$config = $this->loadEditEngineConfiguration(null);
|
||||||
|
$fields = $this->buildEditFields($object);
|
||||||
|
|
||||||
$is_preview = $request->isPreviewRequest();
|
$is_preview = $request->isPreviewRequest();
|
||||||
$view_uri = $this->getObjectViewURI($object);
|
$view_uri = $this->getObjectViewURI($object);
|
||||||
|
|
||||||
|
@ -1025,11 +1043,46 @@ abstract class PhabricatorEditEngine
|
||||||
|
|
||||||
$xactions = array();
|
$xactions = array();
|
||||||
|
|
||||||
$xactions[] = id(clone $template)
|
$actions = $request->getStr('editengine.actions');
|
||||||
->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
|
if ($actions) {
|
||||||
->attachComment(
|
$type_map = array();
|
||||||
id(clone $comment_template)
|
foreach ($fields as $field) {
|
||||||
->setContent($comment_text));
|
$types = $field->getCommentEditTypes();
|
||||||
|
foreach ($types as $type) {
|
||||||
|
$type_map[$type->getEditType()] = $type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$actions = phutil_json_decode($actions);
|
||||||
|
foreach ($actions as $action) {
|
||||||
|
$type = idx($action, 'type');
|
||||||
|
if (!$type) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$edit_type = idx($type_map, $type);
|
||||||
|
if (!$edit_type) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$type_xactions = $edit_type->generateTransactions(
|
||||||
|
$template,
|
||||||
|
array(
|
||||||
|
'value' => idx($action, 'value'),
|
||||||
|
));
|
||||||
|
foreach ($type_xactions as $type_xaction) {
|
||||||
|
$xactions[] = $type_xaction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen($comment_text) || !$xactions) {
|
||||||
|
$xactions[] = id(clone $template)
|
||||||
|
->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
|
||||||
|
->attachComment(
|
||||||
|
id(clone $comment_template)
|
||||||
|
->setContent($comment_text));
|
||||||
|
}
|
||||||
|
|
||||||
$editor = $object->getApplicationTransactionEditor()
|
$editor = $object->getApplicationTransactionEditor()
|
||||||
->setActor($viewer)
|
->setActor($viewer)
|
||||||
|
|
|
@ -494,4 +494,8 @@ abstract class PhabricatorEditField extends Phobject {
|
||||||
return array($edit_type);
|
return array($edit_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getCommentEditTypes() {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,19 @@
|
||||||
abstract class PhabricatorTokenizerEditField
|
abstract class PhabricatorTokenizerEditField
|
||||||
extends PhabricatorPHIDListEditField {
|
extends PhabricatorPHIDListEditField {
|
||||||
|
|
||||||
|
private $commentActionLabel;
|
||||||
|
|
||||||
abstract protected function newDatasource();
|
abstract protected function newDatasource();
|
||||||
|
|
||||||
|
public function setCommentActionLabel($label) {
|
||||||
|
$this->commentActionLabel = $label;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCommentActionLabel() {
|
||||||
|
return $this->commentActionLabel;
|
||||||
|
}
|
||||||
|
|
||||||
protected function newControl() {
|
protected function newControl() {
|
||||||
$control = id(new AphrontFormTokenizerControl())
|
$control = id(new AphrontFormTokenizerControl())
|
||||||
->setDatasource($this->newDatasource());
|
->setDatasource($this->newDatasource());
|
||||||
|
@ -21,4 +32,42 @@ abstract class PhabricatorTokenizerEditField
|
||||||
return $request->getArr($key.'.original');
|
return $request->getArr($key.'.original');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function newEditType() {
|
||||||
|
$type = parent::newEditType();
|
||||||
|
|
||||||
|
if ($this->getUseEdgeTransactions()) {
|
||||||
|
$datasource = $this->newDatasource()
|
||||||
|
->setViewer($this->getViewer());
|
||||||
|
$type->setDatasource($datasource);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCommentEditTypes() {
|
||||||
|
if (!$this->getUseEdgeTransactions()) {
|
||||||
|
return parent::getCommentEditTypes();
|
||||||
|
}
|
||||||
|
|
||||||
|
$transaction_type = $this->getTransactionType();
|
||||||
|
if ($transaction_type === null) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$label = $this->getCommentActionLabel();
|
||||||
|
if ($label === null) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$type_key = $this->getEditTypeKey();
|
||||||
|
$base = $this->getEditType();
|
||||||
|
|
||||||
|
$add = id(clone $base)
|
||||||
|
->setEditType($type_key.'.add')
|
||||||
|
->setEdgeOperation('+')
|
||||||
|
->setLabel($label);
|
||||||
|
|
||||||
|
return array($add);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -811,7 +811,17 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
$this->adjustTransactionValues($object, $xaction);
|
$this->adjustTransactionValues($object, $xaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
$xactions = $this->filterTransactions($object, $xactions);
|
try {
|
||||||
|
$xactions = $this->filterTransactions($object, $xactions);
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
if ($read_locking) {
|
||||||
|
$object->endReadLocking();
|
||||||
|
}
|
||||||
|
if ($transaction_open) {
|
||||||
|
$object->killTransaction();
|
||||||
|
}
|
||||||
|
throw $ex;
|
||||||
|
}
|
||||||
|
|
||||||
// Now that we've merged, filtered, and combined transactions, check for
|
// Now that we've merged, filtered, and combined transactions, check for
|
||||||
// required capabilities.
|
// required capabilities.
|
||||||
|
|
|
@ -4,6 +4,7 @@ final class PhabricatorEdgeEditType extends PhabricatorEditType {
|
||||||
|
|
||||||
private $edgeOperation;
|
private $edgeOperation;
|
||||||
private $valueDescription;
|
private $valueDescription;
|
||||||
|
private $datasource;
|
||||||
|
|
||||||
public function setEdgeOperation($edge_operation) {
|
public function setEdgeOperation($edge_operation) {
|
||||||
$this->edgeOperation = $edge_operation;
|
$this->edgeOperation = $edge_operation;
|
||||||
|
@ -14,6 +15,15 @@ final class PhabricatorEdgeEditType extends PhabricatorEditType {
|
||||||
return $this->edgeOperation;
|
return $this->edgeOperation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setDatasource($datasource) {
|
||||||
|
$this->datasource = $datasource;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDatasource() {
|
||||||
|
return $this->datasource;
|
||||||
|
}
|
||||||
|
|
||||||
public function getValueType() {
|
public function getValueType() {
|
||||||
return 'list<phid>';
|
return 'list<phid>';
|
||||||
}
|
}
|
||||||
|
@ -46,4 +56,33 @@ final class PhabricatorEdgeEditType extends PhabricatorEditType {
|
||||||
return $this->valueDescription;
|
return $this->valueDescription;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getPHUIXControlType() {
|
||||||
|
$datasource = $this->getDatasource();
|
||||||
|
|
||||||
|
if (!$datasource) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'tokenizer';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPHUIXControlSpecification() {
|
||||||
|
$datasource = $this->getDatasource();
|
||||||
|
|
||||||
|
if (!$datasource) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$template = new AphrontTokenizerTemplateView();
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'markup' => $template->render(),
|
||||||
|
'config' => array(
|
||||||
|
'src' => $datasource->getDatasourceURI(),
|
||||||
|
'browseURI' => $datasource->getBrowseURI(),
|
||||||
|
'placeholder' => $datasource->getPlaceholderText(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ abstract class PhabricatorEditType extends Phobject {
|
||||||
|
|
||||||
private $editType;
|
private $editType;
|
||||||
private $transactionType;
|
private $transactionType;
|
||||||
|
private $label;
|
||||||
private $field;
|
private $field;
|
||||||
private $description;
|
private $description;
|
||||||
private $summary;
|
private $summary;
|
||||||
|
@ -30,6 +31,15 @@ abstract class PhabricatorEditType extends Phobject {
|
||||||
return $this->summary;
|
return $this->summary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setLabel($label) {
|
||||||
|
$this->label = $label;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLabel() {
|
||||||
|
return $this->label;
|
||||||
|
}
|
||||||
|
|
||||||
public function setField(PhabricatorEditField $field) {
|
public function setField(PhabricatorEditField $field) {
|
||||||
$this->field = $field;
|
$this->field = $field;
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -86,4 +96,12 @@ abstract class PhabricatorEditType extends Phobject {
|
||||||
return $xaction;
|
return $xaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getPHUIXControlType() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPHUIXControlSpecification() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ class PhabricatorApplicationTransactionCommentView extends AphrontView {
|
||||||
|
|
||||||
private $currentVersion;
|
private $currentVersion;
|
||||||
private $versionedDraft;
|
private $versionedDraft;
|
||||||
|
private $editTypes;
|
||||||
|
|
||||||
public function setObjectPHID($object_phid) {
|
public function setObjectPHID($object_phid) {
|
||||||
$this->objectPHID = $object_phid;
|
$this->objectPHID = $object_phid;
|
||||||
|
@ -100,6 +101,15 @@ class PhabricatorApplicationTransactionCommentView extends AphrontView {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setEditTypes($edit_types) {
|
||||||
|
$this->editTypes = $edit_types;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEditTypes() {
|
||||||
|
return $this->editTypes;
|
||||||
|
}
|
||||||
|
|
||||||
public function render() {
|
public function render() {
|
||||||
|
|
||||||
$user = $this->getUser();
|
$user = $this->getUser();
|
||||||
|
@ -182,7 +192,7 @@ class PhabricatorApplicationTransactionCommentView extends AphrontView {
|
||||||
$version_key = PhabricatorVersionedDraft::KEY_VERSION;
|
$version_key = PhabricatorVersionedDraft::KEY_VERSION;
|
||||||
$version_value = $this->getCurrentVersion();
|
$version_value = $this->getCurrentVersion();
|
||||||
|
|
||||||
return id(new AphrontFormView())
|
$form = id(new AphrontFormView())
|
||||||
->setUser($this->getUser())
|
->setUser($this->getUser())
|
||||||
->addSigil('transaction-append')
|
->addSigil('transaction-append')
|
||||||
->setWorkflow(true)
|
->setWorkflow(true)
|
||||||
|
@ -193,7 +203,57 @@ class PhabricatorApplicationTransactionCommentView extends AphrontView {
|
||||||
->setAction($this->getAction())
|
->setAction($this->getAction())
|
||||||
->setID($this->getFormID())
|
->setID($this->getFormID())
|
||||||
->addHiddenInput('__draft__', $draft_key)
|
->addHiddenInput('__draft__', $draft_key)
|
||||||
->addHiddenInput($version_key, $version_value)
|
->addHiddenInput($version_key, $version_value);
|
||||||
|
|
||||||
|
$edit_types = $this->getEditTypes();
|
||||||
|
if ($edit_types) {
|
||||||
|
|
||||||
|
$action_map = array();
|
||||||
|
foreach ($edit_types as $edit_type) {
|
||||||
|
$key = $edit_type->getEditType();
|
||||||
|
$action_map[$key] = array(
|
||||||
|
'key' => $key,
|
||||||
|
'label' => $edit_type->getLabel(),
|
||||||
|
'type' => $edit_type->getPHUIXControlType(),
|
||||||
|
'spec' => $edit_type->getPHUIXControlSpecification(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$options = array();
|
||||||
|
$options['+'] = pht('Add Action...');
|
||||||
|
foreach ($action_map as $key => $item) {
|
||||||
|
$options[$key] = $item['label'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$action_id = celerity_generate_unique_node_id();
|
||||||
|
$input_id = celerity_generate_unique_node_id();
|
||||||
|
|
||||||
|
$form->appendChild(
|
||||||
|
phutil_tag(
|
||||||
|
'input',
|
||||||
|
array(
|
||||||
|
'type' => 'hidden',
|
||||||
|
'name' => 'editengine.actions',
|
||||||
|
'id' => $input_id,
|
||||||
|
)));
|
||||||
|
|
||||||
|
$form->appendChild(
|
||||||
|
id(new AphrontFormSelectControl())
|
||||||
|
->setLabel(pht('Actions'))
|
||||||
|
->setID($action_id)
|
||||||
|
->setOptions($options));
|
||||||
|
|
||||||
|
Javelin::initBehavior(
|
||||||
|
'comment-actions',
|
||||||
|
array(
|
||||||
|
'actionID' => $action_id,
|
||||||
|
'inputID' => $input_id,
|
||||||
|
'formID' => $this->getFormID(),
|
||||||
|
'actions' => $action_map,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
$form
|
||||||
->appendChild(
|
->appendChild(
|
||||||
id(new PhabricatorRemarkupControl())
|
id(new PhabricatorRemarkupControl())
|
||||||
->setID($this->getCommentID())
|
->setID($this->getCommentID())
|
||||||
|
@ -207,6 +267,8 @@ class PhabricatorApplicationTransactionCommentView extends AphrontView {
|
||||||
->appendChild(
|
->appendChild(
|
||||||
id(new AphrontFormMarkupControl())
|
id(new AphrontFormMarkupControl())
|
||||||
->setValue($status));
|
->setValue($status));
|
||||||
|
|
||||||
|
return $form;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function renderPreviewPanel() {
|
private function renderPreviewPanel() {
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
/**
|
||||||
|
* @provides javelin-behavior-comment-actions
|
||||||
|
* @requires javelin-behavior
|
||||||
|
* javelin-stratcom
|
||||||
|
* javelin-workflow
|
||||||
|
* javelin-dom
|
||||||
|
* phuix-form-control-view
|
||||||
|
* phuix-icon-view
|
||||||
|
*/
|
||||||
|
|
||||||
|
JX.behavior('comment-actions', function(config) {
|
||||||
|
var action_map = config.actions;
|
||||||
|
|
||||||
|
var action_node = JX.$(config.actionID);
|
||||||
|
var form_node = JX.$(config.formID);
|
||||||
|
var input_node = JX.$(config.inputID);
|
||||||
|
|
||||||
|
var rows = {};
|
||||||
|
|
||||||
|
JX.DOM.listen(action_node, 'change', null, function() {
|
||||||
|
var options = action_node.options;
|
||||||
|
var option;
|
||||||
|
|
||||||
|
var selected = action_node.value;
|
||||||
|
action_node.value = '+';
|
||||||
|
|
||||||
|
for (var ii = 0; ii < options.length; ii++) {
|
||||||
|
option = options[ii];
|
||||||
|
if (option.value == selected) {
|
||||||
|
add_row(option);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
JX.DOM.listen(form_node, 'submit', null, function() {
|
||||||
|
var data = [];
|
||||||
|
|
||||||
|
for (var k in rows) {
|
||||||
|
data.push({
|
||||||
|
type: k,
|
||||||
|
value: rows[k].getValue()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
input_node.value = JX.JSON.stringify(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
function add_row(option) {
|
||||||
|
var action = action_map[option.value];
|
||||||
|
if (!action) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
option.disabled = true;
|
||||||
|
|
||||||
|
var icon = new JX.PHUIXIconView()
|
||||||
|
.setIcon('fa-times-circle');
|
||||||
|
var remove = JX.$N('a', {href: '#'}, icon.getNode());
|
||||||
|
|
||||||
|
var control = new JX.PHUIXFormControl()
|
||||||
|
.setLabel(action.label)
|
||||||
|
.setError(remove)
|
||||||
|
.setControl('tokenizer', action.spec);
|
||||||
|
var node = control.getNode();
|
||||||
|
|
||||||
|
rows[action.key] = control;
|
||||||
|
|
||||||
|
JX.DOM.listen(remove, 'click', null, function(e) {
|
||||||
|
e.kill();
|
||||||
|
JX.DOM.remove(node);
|
||||||
|
delete rows[action.key];
|
||||||
|
option.disabled = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: Grotesque.
|
||||||
|
action_node
|
||||||
|
.parentNode
|
||||||
|
.parentNode
|
||||||
|
.parentNode
|
||||||
|
.insertBefore(node, action_node.parentNode.parentNode.nextSibling);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
133
webroot/rsrc/js/phuix/PHUIXFormControl.js
Normal file
133
webroot/rsrc/js/phuix/PHUIXFormControl.js
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
/**
|
||||||
|
* @provides phuix-form-control-view
|
||||||
|
* @requires javelin-install
|
||||||
|
* javelin-dom
|
||||||
|
*/
|
||||||
|
|
||||||
|
JX.install('PHUIXFormControl', {
|
||||||
|
|
||||||
|
members: {
|
||||||
|
_node: null,
|
||||||
|
_labelNode: null,
|
||||||
|
_errorNode: null,
|
||||||
|
_inputNode: null,
|
||||||
|
_valueSetCallback: null,
|
||||||
|
_valueGetCallback: null,
|
||||||
|
|
||||||
|
setLabel: function(label) {
|
||||||
|
JX.DOM.setContent(this._getLabelNode(), label);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
setError: function(error) {
|
||||||
|
JX.DOM.setContent(this._getErrorNode(), error);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
setControl: function(type, spec) {
|
||||||
|
var node = this._getInputNode();
|
||||||
|
|
||||||
|
var input;
|
||||||
|
switch (type) {
|
||||||
|
case 'tokenizer':
|
||||||
|
input = this._newTokenizer(spec);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// TODO: Default or better error?
|
||||||
|
JX.$E('Bad Input Type');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JX.DOM.setContent(node, input.node);
|
||||||
|
this._valueGetCallback = input.get;
|
||||||
|
this._valueSetCallback = input.set;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
setValue: function(value) {
|
||||||
|
this._valueSetCallback(value);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
getValue: function() {
|
||||||
|
return this._valueGetCallback();
|
||||||
|
},
|
||||||
|
|
||||||
|
getNode: function() {
|
||||||
|
if (!this._node) {
|
||||||
|
|
||||||
|
var attrs = {
|
||||||
|
className: 'aphront-form-control grouped'
|
||||||
|
};
|
||||||
|
|
||||||
|
var content = [
|
||||||
|
this._getLabelNode(),
|
||||||
|
this._getErrorNode(),
|
||||||
|
this._getInputNode()
|
||||||
|
];
|
||||||
|
|
||||||
|
this._node = JX.$N('div', attrs, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._node;
|
||||||
|
},
|
||||||
|
|
||||||
|
_getLabelNode: function() {
|
||||||
|
if (!this._labelNode) {
|
||||||
|
var attrs = {
|
||||||
|
className: 'aphront-form-label'
|
||||||
|
};
|
||||||
|
|
||||||
|
this._labelNode = JX.$N('label', attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._labelNode;
|
||||||
|
},
|
||||||
|
|
||||||
|
_getErrorNode: function() {
|
||||||
|
if (!this._errorNode) {
|
||||||
|
var attrs = {
|
||||||
|
className: 'aphront-form-error'
|
||||||
|
};
|
||||||
|
|
||||||
|
this._errorNode = JX.$N('span', attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._errorNode;
|
||||||
|
},
|
||||||
|
|
||||||
|
_getInputNode: function() {
|
||||||
|
if (!this._inputNode) {
|
||||||
|
var attrs = {
|
||||||
|
className: 'aphront-form-input'
|
||||||
|
};
|
||||||
|
|
||||||
|
this._inputNode = JX.$N('div', attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._inputNode;
|
||||||
|
},
|
||||||
|
|
||||||
|
_newTokenizer: function(spec) {
|
||||||
|
var build = JX.Prefab.newTokenizerFromTemplate(
|
||||||
|
spec.markup,
|
||||||
|
spec.config);
|
||||||
|
build.tokenizer.start();
|
||||||
|
|
||||||
|
return {
|
||||||
|
node: build.node,
|
||||||
|
get: function() {
|
||||||
|
return JX.keys(build.tokenizer.getTokens());
|
||||||
|
},
|
||||||
|
set: function(map) {
|
||||||
|
for (var k in map) {
|
||||||
|
build.tokenizer.addToken(k, map[k]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
47
webroot/rsrc/js/phuix/PHUIXIconView.js
Normal file
47
webroot/rsrc/js/phuix/PHUIXIconView.js
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
/**
|
||||||
|
* @provides phuix-icon-view
|
||||||
|
* @requires javelin-install
|
||||||
|
* javelin-dom
|
||||||
|
*/
|
||||||
|
|
||||||
|
JX.install('PHUIXIconView', {
|
||||||
|
|
||||||
|
members: {
|
||||||
|
_node: null,
|
||||||
|
_icon: null,
|
||||||
|
_color: null,
|
||||||
|
|
||||||
|
setIcon: function(icon) {
|
||||||
|
var node = this.getNode();
|
||||||
|
if (this._icon) {
|
||||||
|
JX.DOM.alterClass(node, this._icon, false);
|
||||||
|
}
|
||||||
|
this._icon = icon;
|
||||||
|
JX.DOM.alterClass(node, this._icon, true);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
setColor: function(color) {
|
||||||
|
var node = this.getNode();
|
||||||
|
if (this._color) {
|
||||||
|
JX.DOM.alterClass(node, this._color, false);
|
||||||
|
}
|
||||||
|
this._color = color;
|
||||||
|
JX.DOM.alterClass(node, this._color, true);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
getNode: function() {
|
||||||
|
if (!this._node) {
|
||||||
|
var attrs = {
|
||||||
|
className: 'phui-icon-view phui-font-fa'
|
||||||
|
};
|
||||||
|
|
||||||
|
this._node = JX.$N('span', attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
Loading…
Reference in a new issue