Allow administrative editing of project resources
Summary: Currently, you can only edit your own affiliation to projects. Enable users to be managed in a more reasonable batched way. I'll lock this down to admins/owners and add a transaction log at some point. Test Plan: Edited project affiliations. Verified Herald still works. Reviewed By: jungejason Reviewers: jungejason, tuomaspelkonen, aran CC: aran, jungejason Differential Revision: 677
This commit is contained in:
@@ -258,7 +258,7 @@ celerity_register_resource_map(array(
|
|||||||
),
|
),
|
||||||
'herald-rule-editor' =>
|
'herald-rule-editor' =>
|
||||||
array(
|
array(
|
||||||
'uri' => '/res/ba957508/rsrc/js/application/herald/HeraldRuleEditor.js',
|
'uri' => '/res/4d6dff2b/rsrc/js/application/herald/HeraldRuleEditor.js',
|
||||||
'type' => 'js',
|
'type' => 'js',
|
||||||
'requires' =>
|
'requires' =>
|
||||||
array(
|
array(
|
||||||
@@ -271,6 +271,7 @@ celerity_register_resource_map(array(
|
|||||||
6 => 'javelin-typeahead-preloaded-source',
|
6 => 'javelin-typeahead-preloaded-source',
|
||||||
7 => 'javelin-stratcom',
|
7 => 'javelin-stratcom',
|
||||||
8 => 'javelin-json',
|
8 => 'javelin-json',
|
||||||
|
9 => 'phabricator-prefab',
|
||||||
),
|
),
|
||||||
'disk' => '/rsrc/js/application/herald/HeraldRuleEditor.js',
|
'disk' => '/rsrc/js/application/herald/HeraldRuleEditor.js',
|
||||||
),
|
),
|
||||||
@@ -665,6 +666,24 @@ celerity_register_resource_map(array(
|
|||||||
),
|
),
|
||||||
'disk' => '/rsrc/js/application/phriction/phriction-document-preview.js',
|
'disk' => '/rsrc/js/application/phriction/phriction-document-preview.js',
|
||||||
),
|
),
|
||||||
|
'javelin-behavior-projects-resource-editor' =>
|
||||||
|
array(
|
||||||
|
'uri' => '/res/a54d5616/rsrc/js/application/projects/projects-resource-editor.js',
|
||||||
|
'type' => 'js',
|
||||||
|
'requires' =>
|
||||||
|
array(
|
||||||
|
0 => 'javelin-behavior',
|
||||||
|
1 => 'phabricator-prefab',
|
||||||
|
2 => 'multirow-row-manager',
|
||||||
|
3 => 'javelin-tokenizer',
|
||||||
|
4 => 'javelin-typeahead-preloaded-source',
|
||||||
|
5 => 'javelin-typeahead',
|
||||||
|
6 => 'javelin-dom',
|
||||||
|
7 => 'javelin-json',
|
||||||
|
8 => 'javelin-util',
|
||||||
|
),
|
||||||
|
'disk' => '/rsrc/js/application/projects/projects-resource-editor.js',
|
||||||
|
),
|
||||||
'javelin-behavior-refresh-csrf' =>
|
'javelin-behavior-refresh-csrf' =>
|
||||||
array(
|
array(
|
||||||
'uri' => '/res/39aa51f7/rsrc/js/application/core/behavior-refresh-csrf.js',
|
'uri' => '/res/39aa51f7/rsrc/js/application/core/behavior-refresh-csrf.js',
|
||||||
@@ -1094,6 +1113,18 @@ celerity_register_resource_map(array(
|
|||||||
),
|
),
|
||||||
'disk' => '/rsrc/css/application/objectselector/object-selector.css',
|
'disk' => '/rsrc/css/application/objectselector/object-selector.css',
|
||||||
),
|
),
|
||||||
|
'phabricator-prefab' =>
|
||||||
|
array(
|
||||||
|
'uri' => '/res/5784a112/rsrc/js/application/core/Prefab.js',
|
||||||
|
'type' => 'js',
|
||||||
|
'requires' =>
|
||||||
|
array(
|
||||||
|
0 => 'javelin-install',
|
||||||
|
1 => 'javelin-util',
|
||||||
|
2 => 'javelin-dom',
|
||||||
|
),
|
||||||
|
'disk' => '/rsrc/js/application/core/Prefab.js',
|
||||||
|
),
|
||||||
'phabricator-profile-css' =>
|
'phabricator-profile-css' =>
|
||||||
array(
|
array(
|
||||||
'uri' => '/res/ebe1ac2f/rsrc/css/application/profile/profile-view.css',
|
'uri' => '/res/ebe1ac2f/rsrc/css/application/profile/profile-view.css',
|
||||||
@@ -1169,6 +1200,15 @@ celerity_register_resource_map(array(
|
|||||||
),
|
),
|
||||||
'disk' => '/rsrc/css/application/phriction/phriction-document-css.css',
|
'disk' => '/rsrc/css/application/phriction/phriction-document-css.css',
|
||||||
),
|
),
|
||||||
|
'project-edit-css' =>
|
||||||
|
array(
|
||||||
|
'uri' => '/res/c192b5f9/rsrc/css/application/projects/project-edit.css',
|
||||||
|
'type' => 'css',
|
||||||
|
'requires' =>
|
||||||
|
array(
|
||||||
|
),
|
||||||
|
'disk' => '/rsrc/css/application/projects/project-edit.css',
|
||||||
|
),
|
||||||
'syntax-highlighting-css' =>
|
'syntax-highlighting-css' =>
|
||||||
array(
|
array(
|
||||||
'uri' => '/res/e5cc3d88/rsrc/css/core/syntax.css',
|
'uri' => '/res/e5cc3d88/rsrc/css/core/syntax.css',
|
||||||
|
|||||||
@@ -49,8 +49,12 @@ class PhabricatorProjectProfileEditController
|
|||||||
|
|
||||||
$options = PhabricatorProjectStatus::getStatusMap();
|
$options = PhabricatorProjectStatus::getStatusMap();
|
||||||
|
|
||||||
|
$affiliations = $project->loadAffiliations();
|
||||||
|
$affiliations = mpull($affiliations, null, 'getUserPHID');
|
||||||
|
|
||||||
$e_name = true;
|
$e_name = true;
|
||||||
$errors = array();
|
$errors = array();
|
||||||
|
$state = null;
|
||||||
if ($request->isFormPost()) {
|
if ($request->isFormPost()) {
|
||||||
$project->setName($request->getStr('name'));
|
$project->setName($request->getStr('name'));
|
||||||
$project->setStatus($request->getStr('status'));
|
$project->setStatus($request->getStr('status'));
|
||||||
@@ -89,12 +93,97 @@ class PhabricatorProjectProfileEditController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$resources = $request->getStr('resources');
|
||||||
|
$resources = json_decode($resources, true);
|
||||||
|
if (!is_array($resources)) {
|
||||||
|
throw new Exception(
|
||||||
|
"Project resource information was not correctly encoded in the ".
|
||||||
|
"request.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$state = array();
|
||||||
|
foreach ($resources as $resource) {
|
||||||
|
$user_phid = $resource['phid'];
|
||||||
|
if (!$user_phid) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (isset($state[$user_phid])) {
|
||||||
|
// TODO: We should deal with this better -- the user has entered
|
||||||
|
// the same resource more than once.
|
||||||
|
}
|
||||||
|
$state[$user_phid] = array(
|
||||||
|
'phid' => $user_phid,
|
||||||
|
'status' => $resource['status'],
|
||||||
|
'role' => $resource['role'],
|
||||||
|
'owner' => $resource['owner'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$all_phids = array_merge(array_keys($state), array_keys($affiliations));
|
||||||
|
$all_phids = array_unique($all_phids);
|
||||||
|
|
||||||
|
$delete_affiliations = array();
|
||||||
|
$save_affiliations = array();
|
||||||
|
foreach ($all_phids as $phid) {
|
||||||
|
$old = idx($affiliations, $phid);
|
||||||
|
$new = idx($state, $phid);
|
||||||
|
|
||||||
|
if ($old && !$new) {
|
||||||
|
$delete_affiliations[] = $affiliations[$phid];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$old) {
|
||||||
|
$affil = new PhabricatorProjectAffiliation();
|
||||||
|
$affil->setUserPHID($phid);
|
||||||
|
} else {
|
||||||
|
$affil = $old;
|
||||||
|
}
|
||||||
|
|
||||||
|
$affil->setRole((string)$new['role']);
|
||||||
|
$affil->setStatus((string)$new['status']);
|
||||||
|
$affil->setIsOwner((int)$new['owner']);
|
||||||
|
|
||||||
|
$save_affiliations[] = $affil;
|
||||||
|
}
|
||||||
|
|
||||||
if (!$errors) {
|
if (!$errors) {
|
||||||
$project->save();
|
$project->save();
|
||||||
$profile->setProjectPHID($project->getPHID());
|
$profile->setProjectPHID($project->getPHID());
|
||||||
$profile->save();
|
$profile->save();
|
||||||
|
|
||||||
|
foreach ($delete_affiliations as $affil) {
|
||||||
|
$affil->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($save_affiliations as $save) {
|
||||||
|
$save->setProjectPHID($project->getPHID());
|
||||||
|
$save->save();
|
||||||
|
}
|
||||||
|
|
||||||
return id(new AphrontRedirectResponse())
|
return id(new AphrontRedirectResponse())
|
||||||
->setURI('/project/view/'.$project->getID().'/');
|
->setURI('/project/view/'.$project->getID().'/');
|
||||||
|
} else {
|
||||||
|
$phids = array_keys($state);
|
||||||
|
$handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
|
||||||
|
foreach ($state as $phid => $info) {
|
||||||
|
$state[$phid]['name'] = $handles[$phid]->getFullName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$phids = mpull($affiliations, 'getUserPHID');
|
||||||
|
$handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
|
||||||
|
|
||||||
|
$state = array();
|
||||||
|
foreach ($affiliations as $affil) {
|
||||||
|
$user_phid = $affil->getUserPHID();
|
||||||
|
$state[] = array(
|
||||||
|
'phid' => $user_phid,
|
||||||
|
'name' => $handles[$user_phid]->getFullName(),
|
||||||
|
'status' => $affil->getStatus(),
|
||||||
|
'role' => $affil->getRole(),
|
||||||
|
'owner' => $affil->getIsOwner(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,8 +198,11 @@ class PhabricatorProjectProfileEditController
|
|||||||
$title = 'Edit Project';
|
$title = 'Edit Project';
|
||||||
$action = '/project/edit/'.$project->getID().'/';
|
$action = '/project/edit/'.$project->getID().'/';
|
||||||
|
|
||||||
|
require_celerity_resource('project-edit-css');
|
||||||
|
|
||||||
$form = new AphrontFormView();
|
$form = new AphrontFormView();
|
||||||
$form
|
$form
|
||||||
|
->setID('project-edit-form')
|
||||||
->setUser($user)
|
->setUser($user)
|
||||||
->setAction($action)
|
->setAction($action)
|
||||||
->setEncType('multipart/form-data')
|
->setEncType('multipart/form-data')
|
||||||
@@ -141,11 +233,49 @@ class PhabricatorProjectProfileEditController
|
|||||||
id(new AphrontFormFileControl())
|
id(new AphrontFormFileControl())
|
||||||
->setLabel('Change Image')
|
->setLabel('Change Image')
|
||||||
->setName('image'))
|
->setName('image'))
|
||||||
|
->appendChild(
|
||||||
|
'<h1>Resources</h1>'.
|
||||||
|
'<input type="hidden" name="resources" id="resources" />'.
|
||||||
|
'<div class="aphront-form-inset">'.
|
||||||
|
'<div style="float: right;">'.
|
||||||
|
javelin_render_tag(
|
||||||
|
'a',
|
||||||
|
array(
|
||||||
|
'href' => '#',
|
||||||
|
'class' => 'button green',
|
||||||
|
'sigil' => 'add-resource',
|
||||||
|
'mustcapture' => true,
|
||||||
|
),
|
||||||
|
'Add New Resource').
|
||||||
|
'</div>'.
|
||||||
|
'<p></p>'.
|
||||||
|
'<div style="clear: both;"></div>'.
|
||||||
|
javelin_render_tag(
|
||||||
|
'table',
|
||||||
|
array(
|
||||||
|
'sigil' => 'resources',
|
||||||
|
'class' => 'project-resource-table',
|
||||||
|
),
|
||||||
|
'').
|
||||||
|
'</div>')
|
||||||
->appendChild(
|
->appendChild(
|
||||||
id(new AphrontFormSubmitControl())
|
id(new AphrontFormSubmitControl())
|
||||||
->addCancelButton('/project/view/'.$project->getID().'/')
|
->addCancelButton('/project/view/'.$project->getID().'/')
|
||||||
->setValue('Save'));
|
->setValue('Save'));
|
||||||
|
|
||||||
|
$template = new AphrontTokenizerTemplateView();
|
||||||
|
$template = $template->render();
|
||||||
|
|
||||||
|
Javelin::initBehavior(
|
||||||
|
'projects-resource-editor',
|
||||||
|
array(
|
||||||
|
'root' => 'project-edit-form',
|
||||||
|
'tokenizerTemplate' => $template,
|
||||||
|
'tokenizerSource' => '/typeahead/common/users/',
|
||||||
|
'input' => 'resources',
|
||||||
|
'state' => array_values($state),
|
||||||
|
));
|
||||||
|
|
||||||
$panel = new AphrontPanelView();
|
$panel = new AphrontPanelView();
|
||||||
$panel->setHeader($header_name);
|
$panel->setHeader($header_name);
|
||||||
$panel->setWidth(AphrontPanelView::WIDTH_WIDE);
|
$panel->setWidth(AphrontPanelView::WIDTH_WIDE);
|
||||||
|
|||||||
@@ -13,8 +13,13 @@ phutil_require_module('phabricator', 'applications/files/transform');
|
|||||||
phutil_require_module('phabricator', 'applications/phid/handle/data');
|
phutil_require_module('phabricator', 'applications/phid/handle/data');
|
||||||
phutil_require_module('phabricator', 'applications/project/constants/status');
|
phutil_require_module('phabricator', 'applications/project/constants/status');
|
||||||
phutil_require_module('phabricator', 'applications/project/controller/base');
|
phutil_require_module('phabricator', 'applications/project/controller/base');
|
||||||
|
phutil_require_module('phabricator', 'applications/project/storage/affiliation');
|
||||||
phutil_require_module('phabricator', 'applications/project/storage/profile');
|
phutil_require_module('phabricator', 'applications/project/storage/profile');
|
||||||
phutil_require_module('phabricator', 'applications/project/storage/project');
|
phutil_require_module('phabricator', 'applications/project/storage/project');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/celerity/api');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/javelin/api');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/javelin/markup');
|
||||||
|
phutil_require_module('phabricator', 'view/control/tokenizer');
|
||||||
phutil_require_module('phabricator', 'view/form/base');
|
phutil_require_module('phabricator', 'view/form/base');
|
||||||
phutil_require_module('phabricator', 'view/form/control/file');
|
phutil_require_module('phabricator', 'view/form/control/file');
|
||||||
phutil_require_module('phabricator', 'view/form/control/select');
|
phutil_require_module('phabricator', 'view/form/control/select');
|
||||||
|
|||||||
30
webroot/rsrc/css/application/projects/project-edit.css
Normal file
30
webroot/rsrc/css/application/projects/project-edit.css
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* @provides project-edit-css
|
||||||
|
*/
|
||||||
|
|
||||||
|
.project-resource-table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-resource-table td {
|
||||||
|
padding: 2px 4px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-resource-table td label {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #666666;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-resource-table td.user-tokenizer {
|
||||||
|
width: 35%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-resource-table td.role-label {
|
||||||
|
padding-left: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-resource-table td.role input {
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
28
webroot/rsrc/js/application/core/Prefab.js
Normal file
28
webroot/rsrc/js/application/core/Prefab.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* @provides phabricator-prefab
|
||||||
|
* @requires javelin-install
|
||||||
|
* javelin-util
|
||||||
|
* javelin-dom
|
||||||
|
* @javelin
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utilities for client-side rendering (the greatest thing in the world).
|
||||||
|
*/
|
||||||
|
JX.install('Prefab', {
|
||||||
|
|
||||||
|
statics : {
|
||||||
|
renderSelect : function(map, selected, attrs) {
|
||||||
|
var select = JX.$N('select', attrs || {});
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
* javelin-typeahead-preloaded-source
|
* javelin-typeahead-preloaded-source
|
||||||
* javelin-stratcom
|
* javelin-stratcom
|
||||||
* javelin-json
|
* javelin-json
|
||||||
|
* phabricator-prefab
|
||||||
* @provides herald-rule-editor
|
* @provides herald-rule-editor
|
||||||
* @javelin
|
* @javelin
|
||||||
*/
|
*/
|
||||||
@@ -350,20 +351,11 @@ JX.install('HeraldRuleEditor', {
|
|||||||
return [action_cell, target_cell];
|
return [action_cell, target_cell];
|
||||||
},
|
},
|
||||||
_renderSelect : function(map, selected, sigil) {
|
_renderSelect : function(map, selected, sigil) {
|
||||||
var select = JX.$N(
|
var attrs = {
|
||||||
'select',
|
|
||||||
{
|
|
||||||
style : {width: '250px', margin: '0 .5em 0 0'},
|
style : {width: '250px', margin: '0 .5em 0 0'},
|
||||||
sigil : sigil
|
sigil : sigil
|
||||||
});
|
};
|
||||||
for (var k in map) {
|
return JX.Prefab.renderSelect(map, selected, attrs);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
122
webroot/rsrc/js/application/projects/projects-resource-editor.js
Normal file
122
webroot/rsrc/js/application/projects/projects-resource-editor.js
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
/**
|
||||||
|
* @requires javelin-behavior
|
||||||
|
* phabricator-prefab
|
||||||
|
* multirow-row-manager
|
||||||
|
* javelin-tokenizer
|
||||||
|
* javelin-typeahead-preloaded-source
|
||||||
|
* javelin-typeahead
|
||||||
|
* javelin-dom
|
||||||
|
* javelin-json
|
||||||
|
* javelin-util
|
||||||
|
* @provides javelin-behavior-projects-resource-editor
|
||||||
|
* @javelin
|
||||||
|
*/
|
||||||
|
|
||||||
|
JX.behavior('projects-resource-editor', function(config) {
|
||||||
|
|
||||||
|
var root = JX.$(config.root);
|
||||||
|
var resources_table = JX.DOM.find(root, 'table', 'resources');
|
||||||
|
var manager = new JX.MultirowRowManager(resources_table);
|
||||||
|
var resource_rows = [];
|
||||||
|
|
||||||
|
for (var ii = 0; ii < config.state.length; ii++) {
|
||||||
|
addRow(config.state[ii]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderRow(data) {
|
||||||
|
|
||||||
|
var template = JX.$N('div', JX.$H(config.tokenizerTemplate)).firstChild;
|
||||||
|
template.id = '';
|
||||||
|
var datasource = new JX.TypeaheadPreloadedSource(
|
||||||
|
config.tokenizerSource);
|
||||||
|
var typeahead = new JX.Typeahead(template);
|
||||||
|
typeahead.setDatasource(datasource);
|
||||||
|
var tokenizer = new JX.Tokenizer(template);
|
||||||
|
tokenizer.setTypeahead(typeahead);
|
||||||
|
tokenizer.setLimit(1);
|
||||||
|
tokenizer.start();
|
||||||
|
|
||||||
|
if (data.phid) {
|
||||||
|
tokenizer.addToken(data.phid, data.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
var status = JX.Prefab.renderSelect(
|
||||||
|
{'' : 'Current', 'former' : 'Former'},
|
||||||
|
data.status || '');
|
||||||
|
|
||||||
|
var role = JX.$N('input', {type: 'text', value : data.role || ''});
|
||||||
|
|
||||||
|
var ownership = JX.Prefab.renderSelect(
|
||||||
|
{0 : 'Nonowner', 1 : 'Owner'},
|
||||||
|
data.owner || 0);
|
||||||
|
|
||||||
|
var as_object = function() {
|
||||||
|
var tokens = tokenizer.getTokens();
|
||||||
|
return {
|
||||||
|
phid : JX.keys(tokens)[0] || null,
|
||||||
|
status : status.value,
|
||||||
|
role : role.value,
|
||||||
|
owner : ownership.value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var r = [];
|
||||||
|
r.push([null, JX.$N('label', {}, 'User:')]);
|
||||||
|
r.push(['user-tokenizer', template]);
|
||||||
|
r.push(['role-label', JX.$N('label', {}, 'Role:')]);
|
||||||
|
r.push([null, status]);
|
||||||
|
r.push(['role', role]);
|
||||||
|
r.push([null, ownership]);
|
||||||
|
|
||||||
|
for (var ii = 0; ii < r.length; ii++) {
|
||||||
|
r[ii] = JX.$N('td', {className : r[ii][0]}, r[ii][1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
nodes : r,
|
||||||
|
dataCallback : as_object
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function onaddresource(e) {
|
||||||
|
e.kill();
|
||||||
|
addRow({});
|
||||||
|
}
|
||||||
|
|
||||||
|
function addRow(info) {
|
||||||
|
var data = renderRow(info);
|
||||||
|
var row = manager.addRow(data.nodes);
|
||||||
|
var id = manager.getRowID(row);
|
||||||
|
|
||||||
|
resource_rows[id] = data.dataCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onsubmit(e) {
|
||||||
|
var result = [];
|
||||||
|
for (var ii = 0; ii < resource_rows.length; ii++) {
|
||||||
|
if (resource_rows[ii]) {
|
||||||
|
var obj = resource_rows[ii]();
|
||||||
|
result.push(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JX.$(config.input).value = JX.JSON.stringify(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
JX.DOM.listen(
|
||||||
|
root,
|
||||||
|
'click',
|
||||||
|
'add-resource',
|
||||||
|
onaddresource);
|
||||||
|
|
||||||
|
JX.DOM.listen(
|
||||||
|
root,
|
||||||
|
'submit',
|
||||||
|
null,
|
||||||
|
onsubmit);
|
||||||
|
|
||||||
|
manager.listen(
|
||||||
|
'row-removed',
|
||||||
|
function(row_id) {
|
||||||
|
delete resource_rows[row_id];
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user