Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2896760
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Advanced/Developer...
View Handle
View Hovercard
Size
58 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
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)
Attached To
Mode
rP Phorge
Attached
Detach File
Event Timeline
Log In to Comment