Page MenuHomePhorge

No OneTemporary

diff --git a/scripts/celerity_mapper.php b/scripts/celerity_mapper.php
index f766a669fb..2c6c26b496 100755
--- a/scripts/celerity_mapper.php
+++ b/scripts/celerity_mapper.php
@@ -1,189 +1,191 @@
#!/usr/bin/env php
<?php
$package_spec = array(
'core.pkg.css' => array(
'phabricator-core-css',
'phabricator-core-buttons-css',
'phabricator-standard-page-view',
'aphront-dialog-view-css',
'aphront-form-view-css',
'aphront-panel-view-css',
'aphront-side-nav-view-css',
'aphront-table-view-css',
'aphront-crumbs-view-css',
'aphront-tokenizer-control-css',
'aphront-typeahead-control-css',
'phabricator-directory-css',
'phabricator-remarkup-css',
'syntax-highlighting-css',
),
'differential.pkg.css' => array(
'differential-core-view-css',
'differential-changeset-view-css',
'differential-revision-detail-css',
'differential-revision-history-css',
'differential-table-of-contents-css',
'differential-revision-comment-css',
'differential-revision-add-comment-css',
'differential-revision-comment-list-css',
),
'differential.pkg.js' => array(
'javelin-behavior-differential-feedback-preview',
'javelin-behavior-differential-edit-inline-comments',
'javelin-behavior-differential-populate',
'javelin-behavior-differential-show-more',
'javelin-behavior-differential-diff-radios',
),
'diffusion.pkg.css' => array(
'diffusion-commit-view-css',
),
);
require_once dirname(__FILE__).'/__init_script__.php';
if ($argc != 2) {
$self = basename($argv[0]);
echo "usage: {$self} <webroot>\n";
exit(1);
}
phutil_require_module('phutil', 'filesystem');
phutil_require_module('phutil', 'filesystem/filefinder');
phutil_require_module('phutil', 'future/exec');
phutil_require_module('phutil', 'parser/docblock');
$root = Filesystem::resolvePath($argv[1]);
echo "Finding static resources...\n";
$files = id(new FileFinder($root))
->withType('f')
->withSuffix('js')
->withSuffix('css')
->setGenerateChecksums(true)
->find();
echo "Processing ".count($files)." files";
$file_map = array();
foreach ($files as $path => $hash) {
echo ".";
$name = '/'.Filesystem::readablePath($path, $root);
$file_map[$name] = array(
'hash' => $hash,
'disk' => $path,
);
}
echo "\n";
$runtime_map = array();
$hash_map = array();
$parser = new PhutilDocblockParser();
foreach ($file_map as $path => $info) {
$data = Filesystem::readFile($info['disk']);
$matches = array();
$ok = preg_match('@/[*][*].*?[*]/@s', $data, $matches);
if (!$ok) {
throw new Exception(
"File {$path} does not have a header doc comment. Encode dependency ".
"data in a header docblock.");
}
list($description, $metadata) = $parser->parse($matches[0]);
$provides = preg_split('/\s+/', trim(idx($metadata, 'provides')));
$requires = preg_split('/\s+/', trim(idx($metadata, 'requires')));
$provides = array_filter($provides);
$requires = array_filter($requires);
+ var_dump($requires);
+
if (count($provides) !== 1) {
throw new Exception(
"File {$path} must @provide exactly one Celerity target.");
}
$provides = reset($provides);
$type = 'js';
if (preg_match('/\.css$/', $path)) {
$type = 'css';
}
$uri = '/res/'.substr($info['hash'], 0, 8).$path;
$hash_map[$provides] = $info['hash'];
$runtime_map[$provides] = array(
'uri' => $uri,
'type' => $type,
'requires' => $requires,
'disk' => $path,
);
}
$package_map = array();
foreach ($package_spec as $name => $package) {
$hashes = array();
$type = null;
foreach ($package as $symbol) {
if (empty($hash_map[$symbol])) {
throw new Exception(
"Package specification for '{$name}' includes '{$symbol}', but that ".
"symbol is not defined anywhere.");
}
if ($type === null) {
$type = $runtime_map[$symbol]['type'];
} else {
$ntype = $runtime_map[$symbol]['type'];
if ($type !== $ntype) {
throw new Exception(
"Package specification for '{$name}' mixes resources of type ".
"'{$type}' with resources of type '{$ntype}'. Each package may only ".
"contain one type of resource.");
}
}
$hashes[] = $symbol.':'.$hash_map[$symbol];
}
$key = substr(md5(implode("\n", $hashes)), 0, 8);
$package_map['packages'][$key] = array(
'name' => $name,
'symbols' => $package,
'uri' => '/res/pkg/'.$key.'/'.$name,
'type' => $type,
);
foreach ($package as $symbol) {
$package_map['reverse'][$symbol] = $key;
}
}
$runtime_map = var_export($runtime_map, true);
$runtime_map = preg_replace('/\s+$/m', '', $runtime_map);
$runtime_map = preg_replace('/array \(/', 'array(', $runtime_map);
$package_map = var_export($package_map, true);
$pacakge_map = preg_replace('/\s+$/m', '', $package_map);
$package_map = preg_replace('/array \(/', 'array(', $package_map);
$resource_map = <<<EOFILE
<?php
/**
* This file is automatically generated. Use 'celerity_mapper.php' to rebuild
* it.
* @generated
*/
celerity_register_resource_map({$runtime_map}, {$pacakge_map});
EOFILE;
echo "Writing map...\n";
Filesystem::writeFile(
$root.'/../src/__celerity_resource_map__.php',
$resource_map);
echo "Done.\n";
diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php
index c8bd5852be..193ab071fc 100644
--- a/src/__celerity_resource_map__.php
+++ b/src/__celerity_resource_map__.php
@@ -1,590 +1,620 @@
<?php
/**
* This file is automatically generated. Use 'celerity_mapper.php' to rebuild
* it.
* @generated
*/
celerity_register_resource_map(array(
'aphront-crumbs-view-css' =>
array(
'uri' => '/res/c666a518/rsrc/css/aphront/crumbs-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/aphront/crumbs-view.css',
),
'aphront-dark-console-css' =>
array(
'uri' => '/res/056b0c12/rsrc/css/aphront/dark-console.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/aphront/dark-console.css',
),
'aphront-dialog-view-css' =>
array(
'uri' => '/res/7101ab69/rsrc/css/aphront/dialog-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/aphront/dialog-view.css',
),
'aphront-error-view-css' =>
array(
'uri' => '/res/19b27527/rsrc/css/aphront/error-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/aphront/error-view.css',
),
'aphront-form-view-css' =>
array(
'uri' => '/res/8aaef437/rsrc/css/aphront/form-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/aphront/form-view.css',
),
'aphront-headsup-action-list-view-css' =>
array(
'uri' => '/res/8fd91c1d/rsrc/css/aphront/headsup-action-list-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/aphront/headsup-action-list-view.css',
),
'aphront-panel-view-css' =>
array(
'uri' => '/res/63672373/rsrc/css/aphront/panel-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/aphront/panel-view.css',
),
'aphront-request-failure-view-css' =>
array(
'uri' => '/res/97b8337a/rsrc/css/aphront/request-failure-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/aphront/request-failure-view.css',
),
'aphront-side-nav-view-css' =>
array(
- 'uri' => '/res/09b7eb85/rsrc/css/aphront/side-nav-view.css',
+ 'uri' => '/res/4f4c5ca8/rsrc/css/aphront/side-nav-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/aphront/side-nav-view.css',
),
'aphront-table-view-css' =>
array(
'uri' => '/res/7bf17fb8/rsrc/css/aphront/table-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/aphront/table-view.css',
),
'aphront-tokenizer-control-css' =>
array(
'uri' => '/res/a3d23074/rsrc/css/aphront/tokenizer.css',
'type' => 'css',
'requires' =>
array(
0 => 'aphront-typeahead-control-css',
),
'disk' => '/rsrc/css/aphront/tokenizer.css',
),
'aphront-typeahead-control-css' =>
array(
'uri' => '/res/928df9f0/rsrc/css/aphront/typeahead.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/aphront/typeahead.css',
),
'phabricator-standard-page-view' =>
array(
'uri' => '/res/0d41ea7c/rsrc/css/application/base/standard-page-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/base/standard-page-view.css',
),
'differential-revision-add-comment-css' =>
array(
'uri' => '/res/aaae14d3/rsrc/css/application/differential/add-comment.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/differential/add-comment.css',
),
'differential-changeset-view-css' =>
array(
'uri' => '/res/f26ca6f9/rsrc/css/application/differential/changeset-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/differential/changeset-view.css',
),
'differential-core-view-css' =>
array(
'uri' => '/res/525d1a12/rsrc/css/application/differential/core.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/differential/core.css',
),
'differential-revision-comment-list-css' =>
array(
'uri' => '/res/10b9a829/rsrc/css/application/differential/revision-comment-list.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/differential/revision-comment-list.css',
),
'differential-revision-comment-css' =>
array(
'uri' => '/res/b271baaf/rsrc/css/application/differential/revision-comment.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/differential/revision-comment.css',
),
'differential-revision-detail-css' =>
array(
'uri' => '/res/623e3946/rsrc/css/application/differential/revision-detail.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/differential/revision-detail.css',
),
'differential-revision-history-css' =>
array(
'uri' => '/res/755f3da3/rsrc/css/application/differential/revision-history.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/differential/revision-history.css',
),
'differential-table-of-contents-css' =>
array(
'uri' => '/res/e68f6f05/rsrc/css/application/differential/table-of-contents.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/differential/table-of-contents.css',
),
'diffusion-commit-view-css' =>
array(
- 'uri' => '/res/4593ecc8/rsrc/css/application/diffusion/commit-view.css',
+ 'uri' => '/res/8c139192/rsrc/css/application/diffusion/commit-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/diffusion/commit-view.css',
),
'phabricator-directory-css' =>
array(
'uri' => '/res/6a000601/rsrc/css/application/directory/phabricator-directory.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/directory/phabricator-directory.css',
),
'mainphest-task-detail-css' =>
array(
'uri' => '/res/e5f3beca/rsrc/css/application/maniphest/task-detail.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/maniphest/task-detail.css',
),
'maniphest-task-summary-css' =>
array(
'uri' => '/res/94d01e6f/rsrc/css/application/maniphest/task-summary.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/maniphest/task-summary.css',
),
'maniphest-transaction-detail-css' =>
array(
'uri' => '/res/9418efc9/rsrc/css/application/maniphest/transaction-detail.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/maniphest/transaction-detail.css',
),
'phabricator-object-selector-css' =>
array(
'uri' => '/res/52a7e289/rsrc/css/application/objectselector/object-selector.css',
'type' => 'css',
'requires' =>
array(
0 => 'aphront-dialog-view-css',
),
'disk' => '/rsrc/css/application/objectselector/object-selector.css',
),
'phabricator-profile-css' =>
array(
'uri' => '/res/259ad37f/rsrc/css/application/people/profile.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/people/profile.css',
),
'phabricator-core-buttons-css' =>
array(
'uri' => '/res/53b4f712/rsrc/css/core/buttons.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/core/buttons.css',
),
'phabricator-core-css' =>
array(
'uri' => '/res/6eebb99b/rsrc/css/core/core.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/core/core.css',
),
'phabricator-remarkup-css' =>
array(
'uri' => '/res/786989c3/rsrc/css/core/remarkup.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/core/remarkup.css',
),
'syntax-highlighting-css' =>
array(
'uri' => '/res/fb673ece/rsrc/css/core/syntax.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/core/syntax.css',
),
'javelin-behavior-dark-console' =>
array(
'uri' => '/res/020b0265/rsrc/js/application/core/behavior-dark-console.js',
'type' => 'js',
'requires' =>
array(
),
'disk' => '/rsrc/js/application/core/behavior-dark-console.js',
),
'javelin-behavior-phabricator-object-selector' =>
array(
'uri' => '/res/4fe735af/rsrc/js/application/core/behavior-object-selector.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-lib-dev',
),
'disk' => '/rsrc/js/application/core/behavior-object-selector.js',
),
'javelin-behavior-aphront-basic-tokenizer' =>
array(
'uri' => '/res/8317d761/rsrc/js/application/core/behavior-tokenizer.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-lib-dev',
),
'disk' => '/rsrc/js/application/core/behavior-tokenizer.js',
),
'javelin-behavior-workflow' =>
array(
'uri' => '/res/15446e7e/rsrc/js/application/core/behavior-workflow.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-lib-dev',
),
'disk' => '/rsrc/js/application/core/behavior-workflow.js',
),
+ 'multirow-row-manager' =>
+ array(
+ 'uri' => '/res/330d076b/rsrc/js/application/core/MultirowRowManager.js',
+ 'type' => 'js',
+ 'requires' =>
+ array(
+ 0 => 'javelin-lib-dev',
+ ),
+ 'disk' => '/rsrc/js/application/core/MultirowRowManager.js',
+ ),
'javelin-behavior-differential-add-reviewers' =>
array(
'uri' => '/res/330154e4/rsrc/js/application/differential/behavior-add-reviewers.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-lib-dev',
),
'disk' => '/rsrc/js/application/differential/behavior-add-reviewers.js',
),
'javelin-behavior-differential-feedback-preview' =>
array(
'uri' => '/res/8695d8b8/rsrc/js/application/differential/behavior-comment-preview.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-lib-dev',
),
'disk' => '/rsrc/js/application/differential/behavior-comment-preview.js',
),
'javelin-behavior-differential-diff-radios' =>
array(
'uri' => '/res/fdeb3823/rsrc/js/application/differential/behavior-diff-radios.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-lib-dev',
),
'disk' => '/rsrc/js/application/differential/behavior-diff-radios.js',
),
'javelin-behavior-differential-edit-inline-comments' =>
array(
'uri' => '/res/74747b2e/rsrc/js/application/differential/behavior-edit-inline-comments.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-lib-dev',
),
'disk' => '/rsrc/js/application/differential/behavior-edit-inline-comments.js',
),
'javelin-behavior-differential-populate' =>
array(
'uri' => '/res/a13dcd7e/rsrc/js/application/differential/behavior-populate.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-lib-dev',
),
'disk' => '/rsrc/js/application/differential/behavior-populate.js',
),
'javelin-behavior-differential-show-all-comments' =>
array(
'uri' => '/res/2a3592b8/rsrc/js/application/differential/behavior-show-all-comments.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-lib-dev',
),
'disk' => '/rsrc/js/application/differential/behavior-show-all-comments.js',
),
'javelin-behavior-differential-show-more' =>
array(
'uri' => '/res/ea998002/rsrc/js/application/differential/behavior-show-more.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-lib-dev',
),
'disk' => '/rsrc/js/application/differential/behavior-show-more.js',
),
+ 'javelin-behavior-herald-rule-editor' =>
+ array(
+ 'uri' => '/res/f18bcd5e/rsrc/js/application/herald/herald-rule-editor.js',
+ 'type' => 'js',
+ 'requires' =>
+ array(
+ 0 => 'herald-rule-editor',
+ ),
+ 'disk' => '/rsrc/js/application/herald/herald-rule-editor.js',
+ ),
+ 'herald-rule-editor' =>
+ array(
+ 'uri' => '/res/e71d1d0e/rsrc/js/application/herald/HeraldRuleEditor.js',
+ 'type' => 'js',
+ 'requires' =>
+ array(
+ 0 => 'multirow-row-manager',
+ ),
+ 'disk' => '/rsrc/js/application/herald/HeraldRuleEditor.js',
+ ),
'javelin-behavior-maniphest-transaction-controls' =>
array(
'uri' => '/res/fc6a8722/rsrc/js/application/maniphest/behavior-transaction-controls.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-lib-dev',
),
'disk' => '/rsrc/js/application/maniphest/behavior-transaction-controls.js',
),
'javelin-magical-init' =>
array(
'uri' => '/res/76614f84/rsrc/js/javelin/init.dev.js',
'type' => 'js',
'requires' =>
array(
),
'disk' => '/rsrc/js/javelin/init.dev.js',
),
'javelin-init-prod' =>
array(
'uri' => '/res/1267c868/rsrc/js/javelin/init.min.js',
'type' => 'js',
'requires' =>
array(
),
'disk' => '/rsrc/js/javelin/init.min.js',
),
'javelin-lib-dev' =>
array(
'uri' => '/res/a0e7a5e9/rsrc/js/javelin/javelin.dev.js',
'type' => 'js',
'requires' =>
array(
),
'disk' => '/rsrc/js/javelin/javelin.dev.js',
),
'javelin-lib-prod' =>
array(
'uri' => '/res/2f2b3b2e/rsrc/js/javelin/javelin.min.js',
'type' => 'js',
'requires' =>
array(
),
'disk' => '/rsrc/js/javelin/javelin.min.js',
),
'javelin-typeahead-dev' =>
array(
'uri' => '/res/6de6ae59/rsrc/js/javelin/typeahead.dev.js',
'type' => 'js',
'requires' =>
array(
),
'disk' => '/rsrc/js/javelin/typeahead.dev.js',
),
'javelin-typeahead-prod' =>
array(
'uri' => '/res/69d5fad1/rsrc/js/javelin/typeahead.min.js',
'type' => 'js',
'requires' =>
array(
),
'disk' => '/rsrc/js/javelin/typeahead.min.js',
),
'javelin-workflow-dev' =>
array(
'uri' => '/res/c6b17f93/rsrc/js/javelin/workflow.dev.js',
'type' => 'js',
'requires' =>
array(
),
'disk' => '/rsrc/js/javelin/workflow.dev.js',
),
'javelin-workflow-prod' =>
array(
'uri' => '/res/b758e0a0/rsrc/js/javelin/workflow.min.js',
'type' => 'js',
'requires' =>
array(
),
'disk' => '/rsrc/js/javelin/workflow.min.js',
),
), array (
'packages' =>
array (
- 73063447 =>
+ '848f4c9f' =>
array (
'name' => 'core.pkg.css',
'symbols' =>
array (
0 => 'phabricator-core-css',
1 => 'phabricator-core-buttons-css',
2 => 'phabricator-standard-page-view',
3 => 'aphront-dialog-view-css',
4 => 'aphront-form-view-css',
5 => 'aphront-panel-view-css',
6 => 'aphront-side-nav-view-css',
7 => 'aphront-table-view-css',
8 => 'aphront-crumbs-view-css',
9 => 'aphront-tokenizer-control-css',
10 => 'aphront-typeahead-control-css',
11 => 'phabricator-directory-css',
12 => 'phabricator-remarkup-css',
13 => 'syntax-highlighting-css',
),
- 'uri' => '/res/pkg/73063447/core.pkg.css',
+ 'uri' => '/res/pkg/848f4c9f/core.pkg.css',
'type' => 'css',
),
'76f3c1f8' =>
array (
'name' => 'differential.pkg.css',
'symbols' =>
array (
0 => 'differential-core-view-css',
1 => 'differential-changeset-view-css',
2 => 'differential-revision-detail-css',
3 => 'differential-revision-history-css',
4 => 'differential-table-of-contents-css',
5 => 'differential-revision-comment-css',
6 => 'differential-revision-add-comment-css',
7 => 'differential-revision-comment-list-css',
),
'uri' => '/res/pkg/76f3c1f8/differential.pkg.css',
'type' => 'css',
),
'30d594cf' =>
array (
'name' => 'differential.pkg.js',
'symbols' =>
array (
0 => 'javelin-behavior-differential-feedback-preview',
1 => 'javelin-behavior-differential-edit-inline-comments',
2 => 'javelin-behavior-differential-populate',
3 => 'javelin-behavior-differential-show-more',
4 => 'javelin-behavior-differential-diff-radios',
),
'uri' => '/res/pkg/30d594cf/differential.pkg.js',
'type' => 'js',
),
- '2393c3a4' =>
+ 'eadf6ec3' =>
array (
'name' => 'diffusion.pkg.css',
'symbols' =>
array (
0 => 'diffusion-commit-view-css',
),
- 'uri' => '/res/pkg/2393c3a4/diffusion.pkg.css',
+ 'uri' => '/res/pkg/eadf6ec3/diffusion.pkg.css',
'type' => 'css',
),
),
'reverse' =>
array (
- 'phabricator-core-css' => '73063447',
- 'phabricator-core-buttons-css' => '73063447',
- 'phabricator-standard-page-view' => '73063447',
- 'aphront-dialog-view-css' => '73063447',
- 'aphront-form-view-css' => '73063447',
- 'aphront-panel-view-css' => '73063447',
- 'aphront-side-nav-view-css' => '73063447',
- 'aphront-table-view-css' => '73063447',
- 'aphront-crumbs-view-css' => '73063447',
- 'aphront-tokenizer-control-css' => '73063447',
- 'aphront-typeahead-control-css' => '73063447',
- 'phabricator-directory-css' => '73063447',
- 'phabricator-remarkup-css' => '73063447',
- 'syntax-highlighting-css' => '73063447',
+ 'phabricator-core-css' => '848f4c9f',
+ 'phabricator-core-buttons-css' => '848f4c9f',
+ 'phabricator-standard-page-view' => '848f4c9f',
+ 'aphront-dialog-view-css' => '848f4c9f',
+ 'aphront-form-view-css' => '848f4c9f',
+ 'aphront-panel-view-css' => '848f4c9f',
+ 'aphront-side-nav-view-css' => '848f4c9f',
+ 'aphront-table-view-css' => '848f4c9f',
+ 'aphront-crumbs-view-css' => '848f4c9f',
+ 'aphront-tokenizer-control-css' => '848f4c9f',
+ 'aphront-typeahead-control-css' => '848f4c9f',
+ 'phabricator-directory-css' => '848f4c9f',
+ 'phabricator-remarkup-css' => '848f4c9f',
+ 'syntax-highlighting-css' => '848f4c9f',
'differential-core-view-css' => '76f3c1f8',
'differential-changeset-view-css' => '76f3c1f8',
'differential-revision-detail-css' => '76f3c1f8',
'differential-revision-history-css' => '76f3c1f8',
'differential-table-of-contents-css' => '76f3c1f8',
'differential-revision-comment-css' => '76f3c1f8',
'differential-revision-add-comment-css' => '76f3c1f8',
'differential-revision-comment-list-css' => '76f3c1f8',
'javelin-behavior-differential-feedback-preview' => '30d594cf',
'javelin-behavior-differential-edit-inline-comments' => '30d594cf',
'javelin-behavior-differential-populate' => '30d594cf',
'javelin-behavior-differential-show-more' => '30d594cf',
'javelin-behavior-differential-diff-radios' => '30d594cf',
- 'diffusion-commit-view-css' => '2393c3a4',
+ 'diffusion-commit-view-css' => 'eadf6ec3',
),
));
diff --git a/src/applications/herald/controller/rule/HeraldRuleController.php b/src/applications/herald/controller/rule/HeraldRuleController.php
index 84c0e2b20b..0bf62be8d3 100644
--- a/src/applications/herald/controller/rule/HeraldRuleController.php
+++ b/src/applications/herald/controller/rule/HeraldRuleController.php
@@ -1,481 +1,496 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class HeraldRuleController extends HeraldController {
private $id;
public function willProcessRequest(array $data) {
$this->id = (int)idx($data, 'id');
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$content_type_map = HeraldContentTypeConfig::getContentTypeMap();
if ($this->id) {
$rule = id(new HeraldRule())->load($this->id);
if (!$rule) {
return new Aphront404Response();
}
if ($rule->getAuthorPHID() != $user->getPHID()) {
throw new Exception("You don't own this rule and can't edit it.");
}
} else {
$rule = new HeraldRule();
$rule->setAuthorPHID($user->getPHID());
$rule->setMustMatchAll(true);
$type = $request->getStr('type');
if (!isset($content_type_map[$type])) {
$type = HeraldContentTypeConfig::CONTENT_TYPE_DIFFERENTIAL;
}
$rule->setContentType($type);
}
$local_version = id(new HeraldRule())->getConfigVersion();
if ($rule->getConfigVersion() > $local_version) {
throw new Exception(
"This rule was created with a newer version of Herald. You can not ".
"view or edit it in this older version. Try dev or wait for a push.");
}
// Upgrade rule version to our version, since we might add newly-defined
// conditions, etc.
$rule->setConfigVersion($local_version);
$rule_conditions = $rule->loadConditions();
$rule_actions = $rule->loadActions();
$rule->attachConditions($rule_conditions);
$rule->attachActions($rule_actions);
$arr = "\xC2\xAB";
$e_name = true;
$errors = array();
if ($request->isFormPost() && $request->getStr('save')) {
$rule->setName($request->getStr('name'));
$rule->setMustMatchAll(($request->getStr('must_match') == 'all'));
if (!strlen($rule->getName())) {
$e_name = "{$arr} Required";
$errors[] = "Rule must have a name.";
}
$data = json_decode($request->getStr('rule'), true);
if (!is_array($data) ||
!$data['conditions'] ||
!$data['actions']) {
throw new Exception("Failed to decode rule data.");
}
$conditions = array();
foreach ($data['conditions'] as $condition) {
$obj = new HeraldCondition();
$obj->setFieldName($condition[0]);
$obj->setCondition($condition[1]);
if (is_array($condition[2])) {
$obj->setValue(array_keys($condition[2]));
} else {
$obj->setValue($condition[2]);
}
$cond_type = $obj->getCondition();
if ($cond_type == HeraldConditionConfig::CONDITION_REGEXP) {
if (@preg_match($obj->getValue(), '') === false) {
$errors[] =
'The regular expression "'.$obj->getValue().'" is not valid. '.
'Regular expressions must have enclosing characters (e.g. '.
'"@/path/to/file@", not "/path/to/file") and be syntactically '.
'correct.';
}
}
if ($cond_type == HeraldConditionConfig::CONDITION_REGEXP_PAIR) {
$json = json_decode($obj->getValue(), true);
if (!is_array($json)) {
$errors[] =
'The regular expression pair "'.$obj->getValue().'" is not '.
'valid JSON. Enter a valid JSON array with two elements.';
} else {
if (count($json) != 2) {
$errors[] =
'The regular expression pair "'.$obj->getValue().'" must have '.
'exactly two elements.';
} else {
$key_regexp = array_shift($json);
$val_regexp = array_shift($json);
if (@preg_match($key_regexp, '') === false) {
$errors[] =
'The first regexp, "'.$key_regexp.'" in the regexp pair '.
'is not a valid regexp.';
}
if (@preg_match($val_regexp, '') === false) {
$errors[] =
'The second regexp, "'.$val_regexp.'" in the regexp pair '.
'is not a valid regexp.';
}
}
}
}
$conditions[] = $obj;
}
$actions = array();
foreach ($data['actions'] as $action) {
$obj = new HeraldAction();
$obj->setAction($action[0]);
if (!isset($action[1])) {
// Legitimate for any action which doesn't need a target, like
// "Do nothing".
$action[1] = null;
}
if (is_array($action[1])) {
$obj->setTarget(array_keys($action[1]));
} else {
$obj->setTarget($action[1]);
}
$actions[] = $obj;
}
$rule->attachConditions($conditions);
$rule->attachActions($actions);
if (!$errors) {
try {
$rule->openTransaction();
$rule->save();
$rule->saveConditions($conditions);
$rule->saveActions($actions);
$rule->saveTransaction();
$uri = '/herald/view/'.$rule->getContentType().'/';
return id(new AphrontRedirectResponse())
->setURI($uri);
} catch (QueryDuplicateKeyException $ex) {
$e_name = "{$arr} Not Unique";
$errors[] = "Rule name is not unique. Choose a unique name.";
}
}
}
$phids = array();
$phids[] = $rule->getAuthorPHID();
foreach ($rule->getActions() as $action) {
foreach ($action->getTarget() as $target) {
$target = (array)$target;
foreach ($target as $phid) {
$phids[] = $phid;
}
}
}
foreach ($rule->getConditions() as $condition) {
$value = $condition->getValue();
if (is_array($value)) {
foreach ($value as $phid) {
$phids[] = $phid;
}
}
}
$handles = id(new PhabricatorObjectHandleData($phids))
->loadHandles();
if ($errors) {
$errors = '!!';//<tools:error title="Form Errors" errors={$errors} />;
}
// require_static('herald-css');
$options = array(
'all' => 'all of',
'any' => 'any of',
);
$selected = $rule->getMustMatchAll() ? 'all' : 'any';
$must_match = array();
foreach ($options as $key => $option) {
$must_match[] = phutil_render_tag(
'option',
array(
'selected' => ($selected == $key) ? 'selected' : null,
),
phutil_escape_html($option));
}
$must_match =
'<select name="must_match">'.
implode("\n", $must_match).
'</select>';
if ($rule->getID()) {
$action = '/herald/rule/'.$rule->getID().'/';
} else {
$action = '/herald/rule/'.$rule->getID().'/';
}
$type_name = $content_type_map[$rule->getContentType()];
$form = id(new AphrontFormView())
->setUser($user)
+ ->setID('herald-rule-edit-form')
->addHiddenInput('type', $rule->getContentType())
->addHiddenInput('save', true)
->addHiddenInput('rule', '')
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Rule Name')
->setError($e_name)
->setValue($rule->getName()))
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('Author')
->setValue($handles[$rule->getAuthorPHID()]->getName()))
->appendChild(
id(new AphrontFormMarkupControl())
->setValue(
"This rule triggers for <strong>{$type_name}</strong>."))
->appendChild(
'<h1>Conditions</h1>'.
'<div style="margin: .5em 0 1em; padding: .5em; background: #aaa;">'.
- '<a href="#" class="button green">Create New Condition</a>'.
+ javelin_render_tag(
+ 'a',
+ array(
+ 'href' => '#',
+ 'class' => 'button green',
+ 'sigil' => 'create-action',
+ ),
+ 'Create New Condition').
'<p>When '.$must_match.' these conditions are met:</p>'.
- '<table></table>'.
+ javelin_render_tag(
+ 'table',
+ array(
+ 'sigil' => 'rule-conditions',
+ ),
+ '').
'</div>')
->appendChild(
'<h1>Action</h1>'.
'<div style="margin: .5em 0 1em; padding: .5em; background: #aaa;">'.
'<a href="#" class="button green">Create New Action</a>'.
'<p>Take these actions:</p>'.
- '<table></table>'.
+ javelin_render_tag(
+ 'table',
+ array(
+ 'sigil' => 'rule-actions',
+ ),
+ '').
'</div>')
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Save')
->addCancelButton('/herald/view/'.$rule->getContentType().'/'));
/*
$form =
<div>
<tools:form action={URI::getRequestURI()} method="post" width="wide">
<input type="hidden" name="save" value="true" />
<input type="hidden" name="rule" value="" sigil="rule" />
<input type="hidden" name="type" value={$request->getStr('type')} />
{$errors}
<h1>Edit Rule</h1>
<tools:fieldset>
<tools:control
type="text"
label="Rule Name"
error={$e_name}>
<input type="text" name="name" value={$rule->getName()} />
</tools:control>
<tools:control type="static" label="Owner">
{$handles[$rule->getAuthorPHID()]->getName()}
</tools:control>
<tools:control type="static">
This rule triggers for
<strong>{$content_type_map[$rule->getContentType()]}</strong>.
</tools:control>
</tools:fieldset>
<h1 style="margin-top: 1.5em;">Conditions</h1>
<tools:fieldset>
<div style="padding: .25em 0 1em 60px;">
<div style="float: right;">
<a href="#"
sigil="create-condition"
class="button green"
mustcapture={true}>Create New Condition</a>
</div>
When {$must_match} these conditions are met:
</div>
<div class="flush" />
<table class="condition-table" sigil="rule-conditions" />
</tools:fieldset>
<h1 style="margin-top: 1.5em;">Actions</h1>
<tools:fieldset>
<div style="padding: .25em 0 1.5em 60px;">
<div style="float: right;">
<a href="#"
sigil="create-action"
class="button green"
mustcapture={true}>Create New Action</a>
</div>
Take these actions:
</div>
<div class="flush" />
<table sigil="rule-actions" class="action-table" />
</tools:fieldset>
<div style="margin-top: 0.5em;">
<tools:control type="submit">
<button>Save Rule</button>
<a href="/herald/" class="button grey">Cancel</a>
</tools:control>
</div>
</tools:form>
</div>;
*/
$serial_conditions = array(
array('default', 'default', ''),
);
if ($rule->getConditions()) {
$serial_conditions = array();
foreach ($rule->getConditions() as $condition) {
$value = $condition->getValue();
if (is_array($value)) {
$value_map = array();
foreach ($value as $k => $fbid) {
$value_map[$fbid] = $handles[$fbid]->getExtendedDisplayName();
}
$value = $value_map;
}
$serial_conditions[] = array(
$condition->getFieldName(),
$condition->getCondition(),
$value,
);
}
}
$serial_actions = array(
array('default', ''),
);
if ($rule->getActions()) {
$serial_actions = array();
foreach ($rule->getActions() as $action) {
$target_map = array();
foreach ((array)$action->getTarget() as $fbid) {
$target_map[$fbid] = $handles[$fbid]->getExtendedDisplayName();
}
$serial_actions[] = array(
$action->getAction(),
$target_map,
);
}
}
$all_rules = id(new HeraldRule())->loadAllWhere(
'authorPHID = %d AND contentType = %s',
$rule->getAuthorPHID(),
$rule->getContentType());
$all_rules = mpull($all_rules, 'getName', 'getID');
asort($all_rules);
unset($all_rules[$rule->getID()]);
$config_info = array();
$config_info['fields']
= HeraldFieldConfig::getFieldMapForContentType($rule->getContentType());
$config_info['conditions'] = HeraldConditionConfig::getConditionMap();
foreach ($config_info['fields'] as $field => $name) {
$config_info['conditionMap'][$field] = array_keys(
HeraldConditionConfig::getConditionMapForField($field));
}
foreach ($config_info['fields'] as $field => $fname) {
foreach ($config_info['conditions'] as $condition => $cname) {
$config_info['values'][$field][$condition] =
HeraldValueTypeConfig::getValueTypeForFieldAndCondition(
$field,
$condition);
}
}
$config_info['actions'] =
HeraldActionConfig::getActionMapForContentType($rule->getContentType());
foreach ($config_info['actions'] as $action => $name) {
$config_info['targets'][$action] =
HeraldValueTypeConfig::getValueTypeForAction($action);
}
-/*
Javelin::initBehavior(
'herald-rule-editor',
array(
- 'root' => 'qq',//$form->requireUniqueId(),
+ 'root' => 'herald-rule-edit-form',
'conditions' => (object) $serial_conditions,
'actions' => (object) $serial_actions,
'template' => $this->buildTokenizerTemplates() + array(
'rules' => $all_rules,
),
'info' => $config_info,
));
-*/
-
$panel = new AphrontPanelView();
$panel->setHeader('Edit Herald Rule');
$panel->setWidth(AphrontPanelView::WIDTH_WIDE);
$panel->appendChild($form);
return $this->buildStandardPageResponse(
$panel,
array(
'title' => 'Edit Rule',
));
}
protected function buildTokenizerTemplates() {
return array(
'source' => array(
'email' => '/datasource/mailable/',
'user' => '/datasource/user/',
'repository' => '/datasource/repository/',
'tag' => '/datasource/tag/',
'package' => '/datasource/package/',
),
'markup' => 'derp derp',//id(<javelin:tokenizer-template />)->toString(),
);
}
}
diff --git a/src/view/form/base/AphrontFormView.php b/src/view/form/base/AphrontFormView.php
index 4722a32f7d..710b223638 100755
--- a/src/view/form/base/AphrontFormView.php
+++ b/src/view/form/base/AphrontFormView.php
@@ -1,99 +1,106 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class AphrontFormView extends AphrontView {
private $action;
private $method = 'POST';
private $header;
private $data = array();
private $encType;
private $user;
private $workflow;
+ private $id;
+
+ public function setID($id) {
+ $this->id = $id;
+ return $this;
+ }
public function setUser(PhabricatorUser $user) {
$this->user = $user;
return $this;
}
public function setAction($action) {
$this->action = $action;
return $this;
}
public function setMethod($method) {
$this->method = $method;
return $this;
}
public function setEncType($enc_type) {
$this->encType = $enc_type;
return $this;
}
public function addHiddenInput($key, $value) {
$this->data[$key] = $value;
return $this;
}
public function setWorkflow($workflow) {
$this->workflow = $workflow;
return $this;
}
public function render() {
require_celerity_resource('aphront-form-view-css');
return javelin_render_tag(
'form',
array(
'action' => $this->action,
'method' => $this->method,
'class' => 'aphront-form-view',
'enctype' => $this->encType,
'sigil' => $this->workflow ? 'workflow' : null,
+ 'id' => $this->id,
),
$this->renderDataInputs().
$this->renderChildren());
}
private function renderDataInputs() {
if (!$this->user) {
throw new Exception('You must pass the user to AphrontFormView.');
}
$data = $this->data + array(
'__form__' => 1,
'__csrf__' => $this->user->getCSRFToken(),
);
$inputs = array();
foreach ($data as $key => $value) {
if ($value === null) {
continue;
}
$inputs[] = phutil_render_tag(
'input',
array(
'type' => 'hidden',
'name' => $key,
'value' => $value,
));
}
return implode("\n", $inputs);
}
}
diff --git a/webroot/rsrc/js/application/core/MultirowRowManager.js b/webroot/rsrc/js/application/core/MultirowRowManager.js
new file mode 100644
index 0000000000..45d1bf368a
--- /dev/null
+++ b/webroot/rsrc/js/application/core/MultirowRowManager.js
@@ -0,0 +1,143 @@
+/**
+ * @requires javelin-lib-dev
+ * @provides multirow-row-manager
+ * @javelin
+ */
+
+
+/**
+ * Give a MultirowRowManager a table DOM elem to manage.
+ * You can add rows, and provide a given ID if you like.
+ * You can update rows by ID.
+ * Rows are automatically equipped with a removal button.
+ * You can listen to the 'row-removed' event on the Manager to get
+ * notifications of these row removals, with the DOM id of the removed
+ * row as event data.
+ */
+JX.install('MultirowRowManager', {
+ /**
+ * @param DOM element <table> root Container for rows
+ */
+ construct : function(root, minRows) {
+ this._root = root;
+ this._rows = [];
+
+ if (typeof minRows !== "undefined") {
+ this._minRows = minRows;
+ } else {
+ this._minRows = 1;
+ }
+
+ JX.DOM.listen(
+ this._root,
+ 'click',
+ JX.MultirowRowManager._removeSigil,
+ JX.bind(this, this._onrowremoved));
+ },
+
+ members : {
+ _count : 0,
+ _nextID : 0,
+ _root : null,
+ _rows : null,
+
+ _generateRowID : function() {
+ return "" + this._nextID++;
+ },
+
+ _wrapRowContents : function(row_id, row_contents) {
+ var row = JX.$N('tr',
+ { sigil : JX.MultirowRowManager.getRowSigil(),
+ meta : { multirow_row_manager_row_id : row_id }
+ },
+ row_contents);
+
+ var removeButton = JX.$N(
+ 'td',
+ {},
+ JX.$N(
+ 'a',
+ { className: "button",
+ sigil: JX.MultirowRowManager._removeSigil
+ },
+ '-'));
+
+ JX.DOM.appendContent(row, removeButton);
+ return row;
+ },
+
+ getRowID : function(row) {
+ return JX.Stratcom.getData(row).multirow_row_manager_row_id;
+ },
+ /**
+ * @param row_contents [DOM elements] New contents of row
+ * @param row_id row ID to update, will throw if this row has been removed
+ */
+ updateRow : function(row_id, row_contents) {
+ if (__DEV__) {
+ if (typeof this._rows[row_id] === "undefined") {
+ throw new Error("JX.MultirowRowManager.updateRow(row_id, " +
+ "row_contents): provided row id does not exist." +
+ " Use addRow to create a new row and make sure " +
+ "not to update rows that have been deleted.");
+ }
+ }
+ var old_row = this._rows[row_id];
+ var new_row = this._wrapRowContents(row_id, row_contents);
+ JX.copy(JX.Stratcom.getData(new_row), JX.Stratcom.getData(old_row));
+
+ JX.DOM.replace(old_row, new_row);
+ this._rows[row_id] = new_row;
+
+ this._oncountchanged(); // Fix the new button.
+ return new_row;
+ },
+
+ addRow : function(row_contents) {
+ var row_id = this._generateRowID();
+ var row = this._wrapRowContents(row_id, row_contents);
+ JX.DOM.appendContent(this._root, row);
+
+ this._count++;
+ this._oncountchanged();
+
+ this._rows[row_id] = row;
+ return row;
+ },
+ _onrowremoved : function(e) {
+ if (!JX.Stratcom.getData(e.getTarget()).enabled) {
+ return;
+ }
+ var row = e.getNode(JX.MultirowRowManager.getRowSigil());
+ var row_id = this.getRowID(row);
+ delete this._rows[row_id];
+ JX.DOM.remove(row);
+
+ this._count--;
+ this._oncountchanged();
+ this.invoke('row-removed', row_id);
+ },
+
+ _oncountchanged : function(e) {
+ var buttons = JX.DOM.scry(
+ this._root,
+ 'a',
+ JX.MultirowRowManager._removeSigil);
+
+ var disable = (this._minRows >= 0 && this._count <= this._minRows);
+ for (var i = 0; i < buttons.length; i++) {
+ var button = buttons[i];
+ JX.DOM.alterClass(button, 'disabled', disable);
+ JX.Stratcom.getData(button).enabled = !disable;
+ }
+ }
+ },
+ events : ['row-removed'],
+ statics : {
+ getRowSigil : function() {
+ return "tools-multirow-row-manager-row";
+ },
+ _removeSigil : "tools-multirow-row-manager-row-remove"
+ }
+});
+
diff --git a/webroot/rsrc/js/application/herald/HeraldRuleEditor.js b/webroot/rsrc/js/application/herald/HeraldRuleEditor.js
new file mode 100644
index 0000000000..a6bce77dda
--- /dev/null
+++ b/webroot/rsrc/js/application/herald/HeraldRuleEditor.js
@@ -0,0 +1,363 @@
+/**
+ * @requires multirow-row-manager
+ * javelin-lib-dev
+ * javelin-typeahead-dev
+ * @provides herald-rule-editor
+ * @javelin
+ */
+
+JX.install('HeraldRuleEditor', {
+ construct : function(config) {
+ var root = JX.$(config.root);
+ this._root = root;
+
+ JX.DOM.listen(
+ root,
+ 'click',
+ 'create-condition',
+ JX.bind(this, this._onnewcondition));
+
+ JX.DOM.listen(
+ root,
+ 'click',
+ 'create-action',
+ JX.bind(this, this._onnewaction));
+
+ JX.DOM.listen(root, 'change', null, JX.bind(this, this._onchange));
+ JX.DOM.listen(root, 'submit', null, JX.bind(this, this._onsubmit));
+
+ var conditionsTable = JX.DOM.find(root, 'table', 'rule-conditions');
+ var actionsTable = JX.DOM.find(root, 'table', 'rule-actions');
+
+ this._conditionsRowManager = new JX.MultirowRowManager(conditionsTable);
+ this._conditionsRowManager.listen(
+ 'row-removed',
+ JX.bind(this, function(row_id) {
+ delete this._config.conditions[row_id];
+ }));
+
+ this._actionsRowManager = new JX.MultirowRowManager(actionsTable);
+ this._actionsRowManager.listen(
+ 'row-removed',
+ JX.bind(this, function(row_id) {
+ delete this._config.actions[row_id];
+ }));
+
+ this._conditionGetters = {};
+ this._conditionTypes = {};
+ this._actionGetters = {};
+ this._actionTypes = {};
+
+ this._config = config;
+
+ var conditions = this._config.conditions;
+ this._config.conditions = [];
+
+ var actions = this._config.actions;
+ this._config.actions = [];
+
+ this._renderConditions(conditions);
+ this._renderActions(actions);
+ },
+
+ members : {
+ _config : null,
+ _root : null,
+ _conditionGetters : null,
+ _conditionTypes : null,
+ _actionGetters : null,
+ _actionTypes : null,
+ _conditionsRowManager : null,
+ _actionsRowManager : null,
+
+ _onnewcondition : function(e) {
+ this._newCondition();
+ e.kill();
+ },
+ _onnewaction : function(e) {
+ this._newAction();
+ e.kill();
+ },
+ _onchange : function(e) {
+ var target = e.getTarget();
+
+ var row = e.getNode(JX.MultirowRowManager.getRowSigil());
+ if (!row) {
+ // Changing the "when all of / any of these..." dropdown.
+ return;
+ }
+
+ if (JX.Stratcom.hasSigil(target, 'field-select')) {
+ this._onfieldchange(row);
+ } else if (JX.Stratcom.hasSigil(target, 'condition-select')) {
+ this._onconditionchange(row);
+ } else if (JX.Stratcom.hasSigil(target, 'action-select')) {
+ this._onactionchange(row);
+ }
+ },
+ _onsubmit : function(e) {
+ var rule = JX.DOM.find(this._root, 'input', 'rule');
+
+ var k;
+
+ for (k in this._config.conditions) {
+ this._config.conditions[k][2] = this._getConditionValue(k);
+ }
+
+ var acts = this._config.actions;
+ for (k in this._config.actions) {
+ this._config.actions[k][1] = this._getActionTarget(k);
+ }
+
+ rule.value = JX.JSON.serialize({
+ conditions: this._config.conditions,
+ actions: this._config.actions
+ });
+ },
+
+ _getConditionValue : function(id) {
+ if (this._conditionGetters[id]) {
+ return this._conditionGetters[id]();
+ }
+ return this._config.conditions[id][2];
+ },
+
+ _getActionTarget : function(id) {
+ if (this._actionGetters[id]) {
+ return this._actionGetters[id]();
+ }
+ return this._config.actions[id][1];
+ },
+
+ _onactionchange : function(r) {
+ var target = JX.DOM.find(r, 'select', 'action-select');
+ var row_id = this._actionsRowManager.getRowID(r);
+
+ this._config.actions[row_id][0] = target.value;
+
+ var target_cell = JX.DOM.find(r, 'td', 'target-cell');
+ var target_input = this._renderTargetInputForRow(row_id);
+
+ JX.DOM.setContent(target_cell, target_input);
+ },
+ _onfieldchange : function(r) {
+ var target = JX.DOM.find(r, 'select', 'field-select');
+ var row_id = this._actionsRowManager.getRowID(r);
+
+ this._config.conditions[row_id][0] = target.value;
+
+ var condition_cell = JX.DOM.find(r, 'td', 'condition-cell');
+ var condition_select = this._renderSelect(
+ this._selectKeys(
+ this._config.info.conditions,
+ this._config.info.conditionMap[target.value]),
+ this._config.conditions[row_id][1],
+ 'condition-select');
+
+ JX.DOM.setContent(condition_cell, condition_select);
+
+ this._onconditionchange(r);
+ },
+ _onconditionchange : function(r) {
+ var target = JX.DOM.find(r, 'select', 'condition-select');
+ var row_id = this._conditionsRowManager.getRowID(r);
+
+ this._config.conditions[row_id][1] = target.value;
+
+ var value_cell = JX.DOM.find(r, 'td', 'value-cell');
+ var value_input = this._renderValueInputForRow(row_id);
+ JX.DOM.setContent(value_cell, value_input);
+ },
+
+ _renderTargetInputForRow : function(row_id) {
+ var action = this._config.actions[row_id];
+ var type = this._config.info.targets[action[0]];
+
+ var input = this._buildInput(type);
+ var node = input[0];
+ var get_fn = input[1];
+ var set_fn = input[2];
+
+ if (node) {
+ JX.Stratcom.addSigil(node, 'action-target');
+ }
+
+
+ var old_type = this._actionTypes[row_id];
+ if (old_type == type || !old_type) {
+ set_fn(this._getActionTarget(row_id));
+ }
+
+ this._actionTypes[row_id] = type;
+ this._actionGetters[row_id] = get_fn;
+
+ return node;
+ },
+
+ _buildInput : function(type) {
+ var input;
+ var get_fn;
+ var set_fn;
+ switch (type) {
+ case 'rule':
+ input = this._renderSelect(this._config.template.rules);
+ get_fn = function() { return input.value; };
+ set_fn = function(v) { input.value = v; };
+ break;
+ case 'email':
+ case 'employee':
+ case 'repository':
+ case 'tag':
+ case 'package':
+ var tokenizer = this._newTokenizer(type);
+ input = tokenizer[0];
+ get_fn = tokenizer[1];
+ set_fn = tokenizer[2];
+ break;
+ case 'none':
+ input = '';
+ get_fn = JX.bag;
+ set_fn = JX.bag;
+ break;
+ default:
+ input = JX.$N('input');
+ get_fn = function() { return input.value; };
+ set_fn = function(v) { input.value = v; };
+ break;
+ }
+
+ return [input, get_fn, set_fn];
+ },
+
+ _renderValueInputForRow : function(row_id) {
+ var cond = this._config.conditions[row_id];
+ var type = this._config.info.values[cond[0]][cond[1]];
+
+ var input = this._buildInput(type);
+ var node = input[0];
+ var get_fn = input[1];
+ var set_fn = input[2];
+
+ if (node) {
+ JX.Stratcom.addSigil(node, 'condition-value');
+ }
+
+ var old_type = this._conditionTypes[row_id];
+ if (old_type == type || !old_type) {
+ set_fn(this._getConditionValue(row_id));
+ }
+
+ this._conditionTypes[row_id] = type;
+ this._conditionGetters[row_id] = get_fn;
+
+ return node;
+ },
+
+ _newTokenizer : function(type) {
+ var template = JX.$N(
+ 'div',
+ new JX.HTML(this._config.template.markup));
+ template = template.firstChild;
+ template.id = '';
+
+ var datasource = new JX.TypeaheadPreloadedSource(
+ this._config.template.source[type]);
+
+ var typeahead = new JX.Typeahead(template);
+ typeahead.setDatasource(datasource);
+
+ var tokenizer = new JX.Tokenizer(template);
+ tokenizer.setTypeahead(typeahead);
+ tokenizer.start();
+
+ return [
+ template,
+ function() {
+ return tokenizer.getTokens();
+ },
+ function(map) {
+ for (var k in map) {
+ tokenizer.addToken(k, map[k]);
+ }
+ }];
+ },
+ _selectKeys : function(map, keys) {
+ var r = {};
+ for (var ii = 0; ii < keys.length; ii++) {
+ r[keys[ii]] = map[keys[ii]];
+ }
+ return r;
+ },
+ _renderConditions : function(conditions) {
+ for (var k in conditions) {
+ this._newCondition(conditions[k]);
+ }
+ },
+ _newCondition : function(data) {
+ var row = this._conditionsRowManager.addRow([]);
+ var row_id = this._conditionsRowManager.getRowID(row);
+ this._config.conditions[row_id] = data || [null, null, ''];
+ var r = this._conditionsRowManager.updateRow(
+ row_id,
+ this._renderCondition(row_id));
+
+ this._onfieldchange(r);
+ },
+ _renderCondition : function(row_id) {
+ var field_select = this._renderSelect(
+ this._config.info.fields,
+ this._config.conditions[row_id][0],
+ 'field-select');
+ var field_cell = JX.$N('td', {sigil: 'field-cell'}, field_select);
+
+ var condition_cell = JX.$N('td', {sigil: 'condition-cell'});
+ var value_cell = JX.$N('td', {className : 'value', sigil: 'value-cell'});
+
+ return [field_cell, condition_cell, value_cell];
+ },
+ _renderActions : function(actions) {
+ for (var k in actions) {
+ this._newAction(actions[k]);
+ delete actions[k];
+ }
+ },
+ _newAction : function(data) {
+ data = data || [];
+ var temprow = this._actionsRowManager.addRow([]);
+ var row_id = this._actionsRowManager.getRowID(temprow);
+ this._config.actions[row_id] = data;
+ var r = this._actionsRowManager.updateRow(row_id,
+ this._renderAction(data));
+ this._onactionchange(r);
+ },
+ _renderAction : function(action) {
+ var action_select = this._renderSelect(
+ this._config.info.actions,
+ action[0],
+ 'action-select');
+ var action_cell = JX.$N('td', {sigil: 'action-cell'}, action_select);
+
+ var target_cell = JX.$N(
+ 'td',
+ {className : 'target', sigil : 'target-cell'});
+
+ return [action_cell, target_cell];
+ },
+ _renderSelect : function(map, selected, sigil) {
+ var select = JX.$N(
+ 'select',
+ {
+ style : {width: '250px', margin: '0 .5em 0 0'},
+ sigil : sigil
+ });
+ for (var k in map) {
+ select.options[select.options.length] = new Option(map[k], k);
+ if (k == selected) {
+ select.value = k;
+ }
+ }
+ select.value = select.value || JX.keys(map)[0];
+ return select;
+ }
+ }
+});
\ No newline at end of file
diff --git a/webroot/rsrc/js/application/herald/herald-rule-editor.js b/webroot/rsrc/js/application/herald/herald-rule-editor.js
new file mode 100644
index 0000000000..ff4f17ecc0
--- /dev/null
+++ b/webroot/rsrc/js/application/herald/herald-rule-editor.js
@@ -0,0 +1,10 @@
+/**
+ * @requires herald-rule-editor
+ * javelin-behavior
+ * @provides javelin-behavior-herald-rule-editor
+ * @javelin
+ */
+
+JX.behavior('herald-rule-editor', function(config) {
+ new JX.HeraldRuleEditor(config);
+});

File Metadata

Mime Type
text/x-diff
Expires
Jan 19 2025, 23:32 (6 w, 6 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1129939
Default Alt Text
(58 KB)

Event Timeline