mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-26 00:32:42 +01:00
Mostly make the editor UI for triggers work
Summary: Ref T5474. This provides a Herald-like UI for editing workboard trigger rules. This probably has some missing pieces and doesn't actually save anything to the database yet, but the basics at least roughly work. Test Plan: {F6299886} Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T5474 Differential Revision: https://secure.phabricator.com/D20301
This commit is contained in:
parent
a5b3e33e3c
commit
567dea5449
12 changed files with 682 additions and 1 deletions
|
@ -100,6 +100,7 @@ return array(
|
||||||
'rsrc/css/application/policy/policy.css' => 'ceb56a08',
|
'rsrc/css/application/policy/policy.css' => 'ceb56a08',
|
||||||
'rsrc/css/application/ponder/ponder-view.css' => '05a09d0a',
|
'rsrc/css/application/ponder/ponder-view.css' => '05a09d0a',
|
||||||
'rsrc/css/application/project/project-card-view.css' => '3b1f7b20',
|
'rsrc/css/application/project/project-card-view.css' => '3b1f7b20',
|
||||||
|
'rsrc/css/application/project/project-triggers.css' => 'cb866c2d',
|
||||||
'rsrc/css/application/project/project-view.css' => '567858b3',
|
'rsrc/css/application/project/project-view.css' => '567858b3',
|
||||||
'rsrc/css/application/releeph/releeph-core.css' => 'f81ff2db',
|
'rsrc/css/application/releeph/releeph-core.css' => 'f81ff2db',
|
||||||
'rsrc/css/application/releeph/releeph-preview-branch.css' => '22db5c07',
|
'rsrc/css/application/releeph/releeph-preview-branch.css' => '22db5c07',
|
||||||
|
@ -432,6 +433,11 @@ return array(
|
||||||
'rsrc/js/application/transactions/behavior-show-older-transactions.js' => '600f440c',
|
'rsrc/js/application/transactions/behavior-show-older-transactions.js' => '600f440c',
|
||||||
'rsrc/js/application/transactions/behavior-transaction-comment-form.js' => '2bdadf1a',
|
'rsrc/js/application/transactions/behavior-transaction-comment-form.js' => '2bdadf1a',
|
||||||
'rsrc/js/application/transactions/behavior-transaction-list.js' => '9cec214e',
|
'rsrc/js/application/transactions/behavior-transaction-list.js' => '9cec214e',
|
||||||
|
'rsrc/js/application/trigger/TriggerRule.js' => 'e4a816a4',
|
||||||
|
'rsrc/js/application/trigger/TriggerRuleControl.js' => '5faf27b9',
|
||||||
|
'rsrc/js/application/trigger/TriggerRuleEditor.js' => 'b49fd60c',
|
||||||
|
'rsrc/js/application/trigger/TriggerRuleType.js' => '4feea7d3',
|
||||||
|
'rsrc/js/application/trigger/trigger-rule-editor.js' => '398fdf13',
|
||||||
'rsrc/js/application/typeahead/behavior-typeahead-browse.js' => '70245195',
|
'rsrc/js/application/typeahead/behavior-typeahead-browse.js' => '70245195',
|
||||||
'rsrc/js/application/typeahead/behavior-typeahead-search.js' => '7b139193',
|
'rsrc/js/application/typeahead/behavior-typeahead-search.js' => '7b139193',
|
||||||
'rsrc/js/application/uiexample/gesture-example.js' => '242dedd0',
|
'rsrc/js/application/uiexample/gesture-example.js' => '242dedd0',
|
||||||
|
@ -683,6 +689,7 @@ return array(
|
||||||
'javelin-behavior-time-typeahead' => '5803b9e7',
|
'javelin-behavior-time-typeahead' => '5803b9e7',
|
||||||
'javelin-behavior-toggle-class' => 'f5c78ae3',
|
'javelin-behavior-toggle-class' => 'f5c78ae3',
|
||||||
'javelin-behavior-toggle-widget' => '8f959ad0',
|
'javelin-behavior-toggle-widget' => '8f959ad0',
|
||||||
|
'javelin-behavior-trigger-rule-editor' => '398fdf13',
|
||||||
'javelin-behavior-typeahead-browse' => '70245195',
|
'javelin-behavior-typeahead-browse' => '70245195',
|
||||||
'javelin-behavior-typeahead-search' => '7b139193',
|
'javelin-behavior-typeahead-search' => '7b139193',
|
||||||
'javelin-behavior-user-menu' => '60cd9241',
|
'javelin-behavior-user-menu' => '60cd9241',
|
||||||
|
@ -875,6 +882,7 @@ return array(
|
||||||
'policy-transaction-detail-css' => 'c02b8384',
|
'policy-transaction-detail-css' => 'c02b8384',
|
||||||
'ponder-view-css' => '05a09d0a',
|
'ponder-view-css' => '05a09d0a',
|
||||||
'project-card-view-css' => '3b1f7b20',
|
'project-card-view-css' => '3b1f7b20',
|
||||||
|
'project-triggers-css' => 'cb866c2d',
|
||||||
'project-view-css' => '567858b3',
|
'project-view-css' => '567858b3',
|
||||||
'releeph-core' => 'f81ff2db',
|
'releeph-core' => 'f81ff2db',
|
||||||
'releeph-preview-branch' => '22db5c07',
|
'releeph-preview-branch' => '22db5c07',
|
||||||
|
@ -886,6 +894,10 @@ return array(
|
||||||
'syntax-default-css' => '055fc231',
|
'syntax-default-css' => '055fc231',
|
||||||
'syntax-highlighting-css' => '4234f572',
|
'syntax-highlighting-css' => '4234f572',
|
||||||
'tokens-css' => 'ce5a50bd',
|
'tokens-css' => 'ce5a50bd',
|
||||||
|
'trigger-rule' => 'e4a816a4',
|
||||||
|
'trigger-rule-control' => '5faf27b9',
|
||||||
|
'trigger-rule-editor' => 'b49fd60c',
|
||||||
|
'trigger-rule-type' => '4feea7d3',
|
||||||
'typeahead-browse-css' => 'b7ed02d2',
|
'typeahead-browse-css' => 'b7ed02d2',
|
||||||
'unhandled-exception-css' => '9ecfc00d',
|
'unhandled-exception-css' => '9ecfc00d',
|
||||||
),
|
),
|
||||||
|
@ -1217,6 +1229,12 @@ return array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
),
|
),
|
||||||
|
'398fdf13' => array(
|
||||||
|
'javelin-behavior',
|
||||||
|
'trigger-rule-editor',
|
||||||
|
'trigger-rule',
|
||||||
|
'trigger-rule-type',
|
||||||
|
),
|
||||||
'3b4899b0' => array(
|
'3b4899b0' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'phabricator-prefab',
|
'phabricator-prefab',
|
||||||
|
@ -1347,6 +1365,9 @@ return array(
|
||||||
'javelin-sound',
|
'javelin-sound',
|
||||||
'phabricator-notification',
|
'phabricator-notification',
|
||||||
),
|
),
|
||||||
|
'4feea7d3' => array(
|
||||||
|
'trigger-rule-control',
|
||||||
|
),
|
||||||
'506aa3f4' => array(
|
'506aa3f4' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-stratcom',
|
'javelin-stratcom',
|
||||||
|
@ -1432,6 +1453,9 @@ return array(
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
'phuix-dropdown-menu',
|
'phuix-dropdown-menu',
|
||||||
),
|
),
|
||||||
|
'5faf27b9' => array(
|
||||||
|
'phuix-form-control-view',
|
||||||
|
),
|
||||||
'600f440c' => array(
|
'600f440c' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-stratcom',
|
'javelin-stratcom',
|
||||||
|
@ -1850,6 +1874,10 @@ return array(
|
||||||
'b347a301' => array(
|
'b347a301' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
),
|
),
|
||||||
|
'b49fd60c' => array(
|
||||||
|
'multirow-row-manager',
|
||||||
|
'trigger-rule',
|
||||||
|
),
|
||||||
'b517bfa0' => array(
|
'b517bfa0' => array(
|
||||||
'phui-oi-list-view-css',
|
'phui-oi-list-view-css',
|
||||||
),
|
),
|
||||||
|
|
|
@ -65,6 +65,9 @@ final class PhabricatorProjectTriggerEditController
|
||||||
$v_name = $request->getStr('name');
|
$v_name = $request->getStr('name');
|
||||||
$v_edit = $request->getStr('editPolicy');
|
$v_edit = $request->getStr('editPolicy');
|
||||||
|
|
||||||
|
$v_rules = $request->getStr('rules');
|
||||||
|
$v_rules = phutil_json_decode($v_rules);
|
||||||
|
|
||||||
$xactions = array();
|
$xactions = array();
|
||||||
if (!$trigger->getID()) {
|
if (!$trigger->getID()) {
|
||||||
$xactions[] = $trigger->getApplicationTransactionTemplate()
|
$xactions[] = $trigger->getApplicationTransactionTemplate()
|
||||||
|
@ -81,6 +84,8 @@ final class PhabricatorProjectTriggerEditController
|
||||||
->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY)
|
->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY)
|
||||||
->setNewValue($v_edit);
|
->setNewValue($v_edit);
|
||||||
|
|
||||||
|
// TODO: Actually write the new rules to the database.
|
||||||
|
|
||||||
$editor = $trigger->getApplicationTransactionEditor()
|
$editor = $trigger->getApplicationTransactionEditor()
|
||||||
->setActor($viewer)
|
->setActor($viewer)
|
||||||
->setContentSourceFromRequest($request)
|
->setContentSourceFromRequest($request)
|
||||||
|
@ -133,8 +138,14 @@ final class PhabricatorProjectTriggerEditController
|
||||||
$header = pht('New Trigger');
|
$header = pht('New Trigger');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$form_id = celerity_generate_unique_node_id();
|
||||||
|
$table_id = celerity_generate_unique_node_id();
|
||||||
|
$create_id = celerity_generate_unique_node_id();
|
||||||
|
$input_id = celerity_generate_unique_node_id();
|
||||||
|
|
||||||
$form = id(new AphrontFormView())
|
$form = id(new AphrontFormView())
|
||||||
->setViewer($viewer);
|
->setViewer($viewer)
|
||||||
|
->setID($form_id);
|
||||||
|
|
||||||
if ($column) {
|
if ($column) {
|
||||||
$form->addHiddenInput('columnPHID', $column->getPHID());
|
$form->addHiddenInput('columnPHID', $column->getPHID());
|
||||||
|
@ -161,6 +172,46 @@ final class PhabricatorProjectTriggerEditController
|
||||||
->setPolicies($policies)
|
->setPolicies($policies)
|
||||||
->setError($e_edit));
|
->setError($e_edit));
|
||||||
|
|
||||||
|
$form->appendChild(
|
||||||
|
phutil_tag(
|
||||||
|
'input',
|
||||||
|
array(
|
||||||
|
'type' => 'hidden',
|
||||||
|
'name' => 'rules',
|
||||||
|
'id' => $input_id,
|
||||||
|
)));
|
||||||
|
|
||||||
|
$form->appendChild(
|
||||||
|
id(new PHUIFormInsetView())
|
||||||
|
->setTitle(pht('Rules'))
|
||||||
|
->setDescription(
|
||||||
|
pht(
|
||||||
|
'When a card is dropped into a column which uses this trigger:'))
|
||||||
|
->setRightButton(
|
||||||
|
javelin_tag(
|
||||||
|
'a',
|
||||||
|
array(
|
||||||
|
'href' => '#',
|
||||||
|
'class' => 'button button-green',
|
||||||
|
'id' => $create_id,
|
||||||
|
'mustcapture' => true,
|
||||||
|
),
|
||||||
|
pht('New Rule')))
|
||||||
|
->setContent(
|
||||||
|
javelin_tag(
|
||||||
|
'table',
|
||||||
|
array(
|
||||||
|
'id' => $table_id,
|
||||||
|
'class' => 'trigger-rules-table',
|
||||||
|
))));
|
||||||
|
|
||||||
|
$this->setupEditorBehavior(
|
||||||
|
$trigger,
|
||||||
|
$form_id,
|
||||||
|
$table_id,
|
||||||
|
$create_id,
|
||||||
|
$input_id);
|
||||||
|
|
||||||
$form->appendControl(
|
$form->appendControl(
|
||||||
id(new AphrontFormSubmitControl())
|
id(new AphrontFormSubmitControl())
|
||||||
->setValue($submit)
|
->setValue($submit)
|
||||||
|
@ -197,4 +248,34 @@ final class PhabricatorProjectTriggerEditController
|
||||||
->appendChild($column_view);
|
->appendChild($column_view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function setupEditorBehavior(
|
||||||
|
PhabricatorProjectTrigger $trigger,
|
||||||
|
$form_id,
|
||||||
|
$table_id,
|
||||||
|
$create_id,
|
||||||
|
$input_id) {
|
||||||
|
|
||||||
|
$rule_list = $trigger->getTriggerRules();
|
||||||
|
$rule_list = mpull($rule_list, 'toDictionary');
|
||||||
|
$rule_list = array_values($rule_list);
|
||||||
|
|
||||||
|
$type_list = PhabricatorProjectTriggerRule::getAllTriggerRules();
|
||||||
|
$type_list = mpull($type_list, 'newTemplate');
|
||||||
|
$type_list = array_values($type_list);
|
||||||
|
|
||||||
|
require_celerity_resource('project-triggers-css');
|
||||||
|
|
||||||
|
Javelin::initBehavior(
|
||||||
|
'trigger-rule-editor',
|
||||||
|
array(
|
||||||
|
'formNodeID' => $form_id,
|
||||||
|
'tableNodeID' => $table_id,
|
||||||
|
'createNodeID' => $create_id,
|
||||||
|
'inputNodeID' => $input_id,
|
||||||
|
|
||||||
|
'rules' => $rule_list,
|
||||||
|
'types' => $type_list,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,14 @@ final class PhabricatorProjectTriggerInvalidRule
|
||||||
$this->getRecord()->getType());
|
$this->getRecord()->getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getSelectControlName() {
|
||||||
|
return pht('(Invalid Rule)');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function isSelectableRule() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
protected function assertValidRuleValue($value) {
|
protected function assertValidRuleValue($value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -23,4 +31,32 @@ final class PhabricatorProjectTriggerInvalidRule
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function isValidRule() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function newInvalidView() {
|
||||||
|
return array(
|
||||||
|
id(new PHUIIconView())
|
||||||
|
->setIcon('fa-exclamation-triangle red'),
|
||||||
|
' ',
|
||||||
|
pht(
|
||||||
|
'This is a trigger rule with a valid type ("%s") but an invalid '.
|
||||||
|
'value.',
|
||||||
|
$this->getRecord()->getType()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getDefaultValue() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getPHUIXControlType() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getPHUIXControlSpecification() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,10 @@ final class PhabricatorProjectTriggerManiphestStatusRule
|
||||||
ManiphestTaskStatus::getTaskStatusName($value));
|
ManiphestTaskStatus::getTaskStatusName($value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getSelectControlName() {
|
||||||
|
return pht('Change status to');
|
||||||
|
}
|
||||||
|
|
||||||
protected function assertValidRuleValue($value) {
|
protected function assertValidRuleValue($value) {
|
||||||
if (!is_string($value)) {
|
if (!is_string($value)) {
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
|
@ -56,4 +60,21 @@ final class PhabricatorProjectTriggerManiphestStatusRule
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getDefaultValue() {
|
||||||
|
return head_key(ManiphestTaskStatus::getTaskStatusMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getPHUIXControlType() {
|
||||||
|
return 'select';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getPHUIXControlSpecification() {
|
||||||
|
$map = ManiphestTaskStatus::getTaskStatusMap();
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'options' => $map,
|
||||||
|
'order' => array_keys($map),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,9 +38,25 @@ abstract class PhabricatorProjectTriggerRule
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract public function getDescription();
|
abstract public function getDescription();
|
||||||
|
abstract public function getSelectControlName();
|
||||||
abstract protected function assertValidRuleValue($value);
|
abstract protected function assertValidRuleValue($value);
|
||||||
abstract protected function newDropTransactions($object, $value);
|
abstract protected function newDropTransactions($object, $value);
|
||||||
abstract protected function newDropEffects($value);
|
abstract protected function newDropEffects($value);
|
||||||
|
abstract protected function getDefaultValue();
|
||||||
|
abstract protected function getPHUIXControlType();
|
||||||
|
abstract protected function getPHUIXControlSpecification();
|
||||||
|
|
||||||
|
protected function isSelectableRule() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function isValidRule() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function newInvalidView() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
final public function getDropTransactions($object, $value) {
|
final public function getDropTransactions($object, $value) {
|
||||||
return $this->newDropTransactions($object, $value);
|
return $this->newDropTransactions($object, $value);
|
||||||
|
@ -95,4 +111,36 @@ abstract class PhabricatorProjectTriggerRule
|
||||||
return new PhabricatorProjectDropEffect();
|
return new PhabricatorProjectDropEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final public function toDictionary() {
|
||||||
|
$record = $this->getRecord();
|
||||||
|
|
||||||
|
$is_valid = $this->isValidRule();
|
||||||
|
if (!$is_valid) {
|
||||||
|
$invalid_view = hsprintf('%s', $this->newInvalidView());
|
||||||
|
} else {
|
||||||
|
$invalid_view = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'type' => $record->getType(),
|
||||||
|
'value' => $record->getValue(),
|
||||||
|
'isValidRule' => $is_valid,
|
||||||
|
'invalidView' => $invalid_view,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final public function newTemplate() {
|
||||||
|
return array(
|
||||||
|
'type' => $this->getTriggerType(),
|
||||||
|
'name' => $this->getSelectControlName(),
|
||||||
|
'selectable' => $this->isSelectableRule(),
|
||||||
|
'defaultValue' => $this->getDefaultValue(),
|
||||||
|
'control' => array(
|
||||||
|
'type' => $this->getPHUIXControlType(),
|
||||||
|
'specification' => $this->getPHUIXControlSpecification(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,14 @@ final class PhabricatorProjectTriggerUnknownRule
|
||||||
$this->getRecord()->getType());
|
$this->getRecord()->getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getSelectControlName() {
|
||||||
|
return pht('(Unknown Rule)');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function isSelectableRule() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
protected function assertValidRuleValue($value) {
|
protected function assertValidRuleValue($value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -23,4 +31,31 @@ final class PhabricatorProjectTriggerUnknownRule
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function isValidRule() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function newInvalidView() {
|
||||||
|
return array(
|
||||||
|
id(new PHUIIconView())
|
||||||
|
->setIcon('fa-exclamation-triangle yellow'),
|
||||||
|
' ',
|
||||||
|
pht(
|
||||||
|
'This is a trigger rule with a unknown type ("%s").',
|
||||||
|
$this->getRecord()->getType()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getDefaultValue() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getPHUIXControlType() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getPHUIXControlSpecification() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
38
webroot/rsrc/css/application/project/project-triggers.css
Normal file
38
webroot/rsrc/css/application/project/project-triggers.css
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
/**
|
||||||
|
* @provides project-triggers-css
|
||||||
|
*/
|
||||||
|
|
||||||
|
.trigger-rules-table {
|
||||||
|
margin: 16px 0;
|
||||||
|
border-collapse: separate;
|
||||||
|
border-spacing: 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trigger-rules-table tr {
|
||||||
|
background: {$bluebackground};
|
||||||
|
}
|
||||||
|
|
||||||
|
.trigger-rules-table td {
|
||||||
|
padding: 6px 4px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trigger-rules-table td.type-cell {
|
||||||
|
padding-left: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trigger-rules-table td.remove-column {
|
||||||
|
padding-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trigger-rules-table td.invalid-cell {
|
||||||
|
padding-left: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trigger-rules-table td.invalid-cell .phui-icon-view {
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trigger-rules-table td.value-cell {
|
||||||
|
width: 100%;
|
||||||
|
}
|
140
webroot/rsrc/js/application/trigger/TriggerRule.js
Normal file
140
webroot/rsrc/js/application/trigger/TriggerRule.js
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
/**
|
||||||
|
* @provides trigger-rule
|
||||||
|
* @javelin
|
||||||
|
*/
|
||||||
|
|
||||||
|
JX.install('TriggerRule', {
|
||||||
|
|
||||||
|
construct: function() {
|
||||||
|
},
|
||||||
|
|
||||||
|
properties: {
|
||||||
|
rowID: null,
|
||||||
|
type: null,
|
||||||
|
value: null,
|
||||||
|
editor: null,
|
||||||
|
isValidRule: true,
|
||||||
|
invalidView: null
|
||||||
|
},
|
||||||
|
|
||||||
|
statics: {
|
||||||
|
newFromDictionary: function(map) {
|
||||||
|
return new JX.TriggerRule()
|
||||||
|
.setType(map.type)
|
||||||
|
.setValue(map.value)
|
||||||
|
.setIsValidRule(map.isValidRule)
|
||||||
|
.setInvalidView(map.invalidView);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
members: {
|
||||||
|
_typeCell: null,
|
||||||
|
_valueCell: null,
|
||||||
|
_readValueCallback: null,
|
||||||
|
|
||||||
|
newRowContent: function() {
|
||||||
|
if (!this.getIsValidRule()) {
|
||||||
|
var invalid_cell = JX.$N(
|
||||||
|
'td',
|
||||||
|
{
|
||||||
|
colSpan: 2,
|
||||||
|
className: 'invalid-cell'
|
||||||
|
},
|
||||||
|
JX.$H(this.getInvalidView()));
|
||||||
|
|
||||||
|
return [invalid_cell];
|
||||||
|
}
|
||||||
|
|
||||||
|
var type_cell = this._getTypeCell();
|
||||||
|
var value_cell = this._getValueCell();
|
||||||
|
|
||||||
|
|
||||||
|
this._rebuildValueControl();
|
||||||
|
|
||||||
|
return [type_cell, value_cell];
|
||||||
|
},
|
||||||
|
|
||||||
|
getValueForSubmit: function() {
|
||||||
|
this._readValueFromControl();
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: this.getType(),
|
||||||
|
value: this.getValue()
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
_getTypeCell: function() {
|
||||||
|
if (!this._typeCell) {
|
||||||
|
var editor = this.getEditor();
|
||||||
|
var types = editor.getTypes();
|
||||||
|
|
||||||
|
var options = [];
|
||||||
|
for (var ii = 0; ii < types.length; ii++) {
|
||||||
|
var type = types[ii];
|
||||||
|
|
||||||
|
if (!type.getIsSelectable()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
options.push(
|
||||||
|
JX.$N('option', {value: type.getType()}, type.getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
var control = JX.$N('select', {}, options);
|
||||||
|
|
||||||
|
control.value = this.getType();
|
||||||
|
|
||||||
|
var on_change = JX.bind(this, this._onTypeChange);
|
||||||
|
JX.DOM.listen(control, 'onchange', null, on_change);
|
||||||
|
|
||||||
|
var attributes = {
|
||||||
|
className: 'type-cell'
|
||||||
|
};
|
||||||
|
|
||||||
|
this._typeCell = JX.$N('td', attributes, control);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._typeCell;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onTypeChange: function() {
|
||||||
|
var control = this._getTypeCell();
|
||||||
|
this.setType(control.value);
|
||||||
|
|
||||||
|
this._rebuildValueControl();
|
||||||
|
},
|
||||||
|
|
||||||
|
_getValueCell: function() {
|
||||||
|
if (!this._valueCell) {
|
||||||
|
var attributes = {
|
||||||
|
className: 'value-cell'
|
||||||
|
};
|
||||||
|
|
||||||
|
this._valueCell = JX.$N('td', attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._valueCell;
|
||||||
|
},
|
||||||
|
|
||||||
|
_rebuildValueControl: function() {
|
||||||
|
var value_cell = this._getValueCell();
|
||||||
|
|
||||||
|
var editor = this.getEditor();
|
||||||
|
var type = editor.getType(this.getType());
|
||||||
|
var control = type.getControl();
|
||||||
|
|
||||||
|
var input = control.newInput(this);
|
||||||
|
this._readValueCallback = input.get;
|
||||||
|
|
||||||
|
JX.DOM.setContent(value_cell, input.node);
|
||||||
|
},
|
||||||
|
|
||||||
|
_readValueFromControl: function() {
|
||||||
|
if (this._readValueCallback) {
|
||||||
|
this.setValue(this._readValueCallback());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
40
webroot/rsrc/js/application/trigger/TriggerRuleControl.js
Normal file
40
webroot/rsrc/js/application/trigger/TriggerRuleControl.js
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
/**
|
||||||
|
* @requires phuix-form-control-view
|
||||||
|
* @provides trigger-rule-control
|
||||||
|
* @javelin
|
||||||
|
*/
|
||||||
|
|
||||||
|
JX.install('TriggerRuleControl', {
|
||||||
|
|
||||||
|
construct: function() {
|
||||||
|
},
|
||||||
|
|
||||||
|
properties: {
|
||||||
|
type: null,
|
||||||
|
specification: null
|
||||||
|
},
|
||||||
|
|
||||||
|
statics: {
|
||||||
|
newFromDictionary: function(map) {
|
||||||
|
return new JX.TriggerRuleControl()
|
||||||
|
.setType(map.type)
|
||||||
|
.setSpecification(map.specification);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
members: {
|
||||||
|
newInput: function(rule) {
|
||||||
|
var phuix = new JX.PHUIXFormControl()
|
||||||
|
.setControl(this.getType(), this.getSpecification());
|
||||||
|
|
||||||
|
phuix.setValue(rule.getValue());
|
||||||
|
|
||||||
|
return {
|
||||||
|
node: phuix.getRawInputNode(),
|
||||||
|
get: JX.bind(phuix, phuix.getValue)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
137
webroot/rsrc/js/application/trigger/TriggerRuleEditor.js
Normal file
137
webroot/rsrc/js/application/trigger/TriggerRuleEditor.js
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
/**
|
||||||
|
* @requires multirow-row-manager
|
||||||
|
* trigger-rule
|
||||||
|
* @provides trigger-rule-editor
|
||||||
|
* @javelin
|
||||||
|
*/
|
||||||
|
|
||||||
|
JX.install('TriggerRuleEditor', {
|
||||||
|
|
||||||
|
construct: function(form_node) {
|
||||||
|
this._formNode = form_node;
|
||||||
|
this._rules = [];
|
||||||
|
this._types = [];
|
||||||
|
},
|
||||||
|
|
||||||
|
members: {
|
||||||
|
_formNode: null,
|
||||||
|
_tableNode: null,
|
||||||
|
_createButtonNode: null,
|
||||||
|
_inputNode: null,
|
||||||
|
_rowManager: null,
|
||||||
|
_rules: null,
|
||||||
|
_types: null,
|
||||||
|
|
||||||
|
setTableNode: function(table) {
|
||||||
|
this._tableNode = table;
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
setCreateButtonNode: function(button) {
|
||||||
|
this._createButtonNode = button;
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
setInputNode: function(input) {
|
||||||
|
this._inputNode = input;
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
start: function() {
|
||||||
|
var on_submit = JX.bind(this, this._submitForm);
|
||||||
|
JX.DOM.listen(this._formNode, 'submit', null, on_submit);
|
||||||
|
|
||||||
|
var manager = new JX.MultirowRowManager(this._tableNode);
|
||||||
|
this._rowManager = manager;
|
||||||
|
|
||||||
|
var on_remove = JX.bind(this, this._rowRemoved);
|
||||||
|
manager.listen('row-removed', on_remove);
|
||||||
|
|
||||||
|
var create_button = this._createButtonNode;
|
||||||
|
var on_create = JX.bind(this, this._createRow);
|
||||||
|
JX.DOM.listen(create_button, 'click', null, on_create);
|
||||||
|
},
|
||||||
|
|
||||||
|
_submitForm: function() {
|
||||||
|
var values = [];
|
||||||
|
for (var ii = 0; ii < this._rules.length; ii++) {
|
||||||
|
var rule = this._rules[ii];
|
||||||
|
values.push(rule.getValueForSubmit());
|
||||||
|
}
|
||||||
|
|
||||||
|
this._inputNode.value = JX.JSON.stringify(values);
|
||||||
|
},
|
||||||
|
|
||||||
|
_createRow: function(e) {
|
||||||
|
var rule = this.newRule();
|
||||||
|
this.addRule(rule);
|
||||||
|
e.kill();
|
||||||
|
},
|
||||||
|
|
||||||
|
newRule: function() {
|
||||||
|
// Create new rules with the first valid rule type.
|
||||||
|
var types = this.getTypes();
|
||||||
|
var type;
|
||||||
|
for (var ii = 0; ii < types.length; ii++) {
|
||||||
|
type = types[ii];
|
||||||
|
if (!type.getIsSelectable()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we make it here: this type is valid, so use it.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var default_value = type.getDefaultValue();
|
||||||
|
|
||||||
|
return new JX.TriggerRule()
|
||||||
|
.setType(type.getType())
|
||||||
|
.setValue(default_value);
|
||||||
|
},
|
||||||
|
|
||||||
|
addRule: function(rule) {
|
||||||
|
rule.setEditor(this);
|
||||||
|
this._rules.push(rule);
|
||||||
|
|
||||||
|
var manager = this._rowManager;
|
||||||
|
|
||||||
|
var row = manager.addRow([]);
|
||||||
|
var row_id = manager.getRowID(row);
|
||||||
|
rule.setRowID(row_id);
|
||||||
|
|
||||||
|
manager.updateRow(row_id, rule.newRowContent());
|
||||||
|
},
|
||||||
|
|
||||||
|
addType: function(type) {
|
||||||
|
this._types.push(type);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
getTypes: function() {
|
||||||
|
return this._types;
|
||||||
|
},
|
||||||
|
|
||||||
|
getType: function(type) {
|
||||||
|
for (var ii = 0; ii < this._types.length; ii++) {
|
||||||
|
if (this._types[ii].getType() === type) {
|
||||||
|
return this._types[ii];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
_rowRemoved: function(row_id) {
|
||||||
|
for (var ii = 0; ii < this._rules.length; ii++) {
|
||||||
|
var rule = this._rules[ii];
|
||||||
|
|
||||||
|
if (rule.getRowID() === row_id) {
|
||||||
|
this._rules.splice(ii, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
36
webroot/rsrc/js/application/trigger/TriggerRuleType.js
Normal file
36
webroot/rsrc/js/application/trigger/TriggerRuleType.js
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
/**
|
||||||
|
* @requires trigger-rule-control
|
||||||
|
* @provides trigger-rule-type
|
||||||
|
* @javelin
|
||||||
|
*/
|
||||||
|
|
||||||
|
JX.install('TriggerRuleType', {
|
||||||
|
|
||||||
|
construct: function() {
|
||||||
|
},
|
||||||
|
|
||||||
|
properties: {
|
||||||
|
type: null,
|
||||||
|
name: null,
|
||||||
|
isSelectable: true,
|
||||||
|
defaultValue: null,
|
||||||
|
control: null
|
||||||
|
},
|
||||||
|
|
||||||
|
statics: {
|
||||||
|
newFromDictionary: function(map) {
|
||||||
|
var control = JX.TriggerRuleControl.newFromDictionary(map.control);
|
||||||
|
|
||||||
|
return new JX.TriggerRuleType()
|
||||||
|
.setType(map.type)
|
||||||
|
.setName(map.name)
|
||||||
|
.setIsSelectable(map.selectable)
|
||||||
|
.setDefaultValue(map.defaultValue)
|
||||||
|
.setControl(control);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
members: {
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
41
webroot/rsrc/js/application/trigger/trigger-rule-editor.js
Normal file
41
webroot/rsrc/js/application/trigger/trigger-rule-editor.js
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
/**
|
||||||
|
* @requires javelin-behavior
|
||||||
|
* trigger-rule-editor
|
||||||
|
* trigger-rule
|
||||||
|
* trigger-rule-type
|
||||||
|
* @provides javelin-behavior-trigger-rule-editor
|
||||||
|
* @javelin
|
||||||
|
*/
|
||||||
|
|
||||||
|
JX.behavior('trigger-rule-editor', function(config) {
|
||||||
|
var form_node = JX.$(config.formNodeID);
|
||||||
|
var table_node = JX.$(config.tableNodeID);
|
||||||
|
var create_node = JX.$(config.createNodeID);
|
||||||
|
var input_node = JX.$(config.inputNodeID);
|
||||||
|
|
||||||
|
var editor = new JX.TriggerRuleEditor(form_node)
|
||||||
|
.setTableNode(table_node)
|
||||||
|
.setCreateButtonNode(create_node)
|
||||||
|
.setInputNode(input_node);
|
||||||
|
|
||||||
|
editor.start();
|
||||||
|
|
||||||
|
var ii;
|
||||||
|
|
||||||
|
for (ii = 0; ii < config.types.length; ii++) {
|
||||||
|
var type = JX.TriggerRuleType.newFromDictionary(config.types[ii]);
|
||||||
|
editor.addType(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.rules.length) {
|
||||||
|
for (ii = 0; ii < config.rules.length; ii++) {
|
||||||
|
var rule = JX.TriggerRule.newFromDictionary(config.rules[ii]);
|
||||||
|
editor.addRule(rule);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If the trigger doesn't have any rules yet, add an empty rule to start
|
||||||
|
// with, so the user doesn't have to click "New Rule".
|
||||||
|
editor.addRule(editor.newRule());
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
Loading…
Reference in a new issue