Skip to content

Commit

Permalink
Merge pull request #15 from RallyCommunity/defect_fixes
Browse files Browse the repository at this point in the history
Fix hardcoded feature name and custom field copying, ordering of children
  • Loading branch information
krmorse authored Jan 18, 2019
2 parents 90d837c + c5f0ec7 commit 57300f1
Show file tree
Hide file tree
Showing 10 changed files with 138 additions and 46 deletions.
2 changes: 1 addition & 1 deletion App-debug.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
Rally.launchApp('CustomApp', {
name:"portfolio-item-copy",
parentRepos:"",
version:"1.0.1"
version:"1.0.2"
});
}, true);
</script>
Expand Down
72 changes: 55 additions & 17 deletions App.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ Ext.define('CustomApp', {
defaultSettings: {
portfolioitem : [''],
hierarchicalrequirement : ["ScheduleState","PlanEstimate"],
task : ["State","Estimate","TaskIndex","ToDo","Actuals"]
task : ["State","Estimate","TaskIndex","ToDo","Actuals"],
preserve_rank: true,
}
},

Expand Down Expand Up @@ -103,7 +104,9 @@ Ext.define('CustomApp', {
success: function(requiredFields) {
app.requiredFields = requiredFields;
// make sure required fields are part of the settings:
Ext.apply(app.fieldsToCopy, requiredFields);
_.each(_.keys(app.fieldsToCopy), function(type) {
app.fieldsToCopy[type] = _.uniq(app.fieldsToCopy[type].concat(requiredFields[type]));
});

if ( Ext.Array.contains( requiredFields.hierarchicalrequirement, 'Release' )) {
this.down('#release-label').setText('Default Release:');
Expand Down Expand Up @@ -134,7 +137,7 @@ Ext.define('CustomApp', {
title: 'Choose Item',
listeners: {
artifactChosen: function(dialog, selectedRecord){
this.down("#item-label").setText(selectedRecord.get('Name'));
this.down("#item-label").setText(selectedRecord.get('FormattedID') + ' - ' + selectedRecord.get('Name'));
this.down("#copy-button").setDisabled(true);
app.itemSelected(selectedRecord);

Expand All @@ -148,6 +151,8 @@ Ext.define('CustomApp', {
// updates the summary message.
itemSelected : function(root) {

app.setLoading('Analyzing hierarchy...');

var config = { model : "PortfolioItem",
fetch : true,
filters : [ { property : "ObjectID", operator : "=", value: root.get("ObjectID") } ]
Expand All @@ -174,8 +179,11 @@ Ext.define('CustomApp', {
// check project selected before enabling.
var projectRef = app.down("#project-picker").getValue();

if (projectRef !== null && projectRef !== "")
if (projectRef !== null && projectRef !== "") {
app.down("#copy-button").setDisabled(false);
}

app.setLoading(false);
});
});
});
Expand Down Expand Up @@ -282,7 +290,8 @@ Ext.define('CustomApp', {
callback: function(result, operation) {
if (operation.success===true) {
app.copyList[item.source.get("_ref")] = result.get("_ref");
app.down("#summary").setText("Created " + result.get("FormattedID"));
app.down("#summary").setText('(' + _.keys(app.copyList).length + ' of ' + app.list.length + '): ' +
"Created " + result.get("FormattedID") + ' - ' + result.get("Name"));
callback(null,result);
} else {
console.log("Error:",operation);
Expand All @@ -299,7 +308,13 @@ Ext.define('CustomApp', {

// reads a rally collection object
readCollection : function( collectionConfig, callback ) {
collectionConfig.reference.getCollection(collectionConfig.type,{fetch:true}).load({
var rank_field = Rally.data.Ranker.getRankField(collectionConfig.reference);
var direction = collectionConfig.direction;

collectionConfig.reference.getCollection(collectionConfig.type, {
fetch: true,
sorters: [ { property: rank_field, direction: direction} ]
}).load({
fetch : true,
callback : function(records,operation,success) {
callback(null,records);
Expand All @@ -311,21 +326,27 @@ Ext.define('CustomApp', {
// recursive method to create a list of all items to be copied.
createList : function(root,callback) {

var config = { model : root.raw._type,
var config = {
model : root.raw._type,
fetch : true,
filters : [ { property : "ObjectID", operator : "=", value: root.get("ObjectID") } ]
};

async.map([config], wsapiQuery, function(err,results) {

var obj = results[0][0];
var direction = "DESC";
app.list.push(obj);
var childRef = null;
if (app.defined(obj.get("Tasks"))) {
childRef = "Tasks";
direction = "DESC";
} else {
if (app.defined(obj.get("Children"))){
if (app.defined(obj.get("Children"))) {
childRef = "Children";
if (/PortfolioItem/.test(root.raw._type)) {
direction = "ASC";
}
} else {
if (app.defined(obj.get("UserStories"))) {
childRef = "UserStories";
Expand All @@ -334,13 +355,22 @@ Ext.define('CustomApp', {
}

if (app.isObject(childRef)) {
var config = { reference : obj, type : childRef };
async.map([config],app.readCollection,function(err,results){
var children = results[0];
async.map(children,app.createList,function(err,results){
callback(null,results);
var config = { reference : obj, type : childRef, direction: direction };
if ( app.getSetting('preserve_rank') && app.getSetting('preserve_rank') != "false" ) {
async.mapSeries([config],app.readCollection,function(err,results){
var children = results[0];
async.mapSeries(children,app.createList,function(err,results){
callback(null,results);
});
});
});
} else {
async.map([config],app.readCollection,function(err,results){
var children = results[0];
async.map(children,app.createList,function(err,results){
callback(null,results);
});
});
}
} else {
callback(null,obj);
}
Expand Down Expand Up @@ -394,6 +424,7 @@ Ext.define('CustomApp', {
model : config.model,
fetch : config.fetch,
filters : config.filters,
sorters: config.sorters,
listeners : {
scope : this,
load : function(store, data) {
Expand Down Expand Up @@ -442,14 +473,21 @@ Ext.define('CustomApp', {
},

getSettingsFields: function() {
return [{
return [
{
name: 'preserve_rank',
xtype: 'rallycheckboxfield',
margin: '10px 0 10px 0',
fieldLabel: 'Maintain Ranks'
},
{
name: 'portfolioitem',
xtype: 'rallyfieldpicker',
autoExpand: true,
alwaysExpanded: false,
modelTypes: ['PortfolioItem/Feature'],
modelTypes: ['PortfolioItem'],
margin: '10px 0 10px 0',
fieldLabel: 'Feature Fields',
fieldLabel: 'Portfolio Item Fields',
_shouldShowField: function(field) {
//console.log(field.name, field);
var attr = field.attributeDefinition;
Expand Down
26 changes: 21 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,30 @@ By default, it copies the following fields:

If there are required fields, the app will attempt to copy those fields, as well. If the Release field is required for stories, the app will present the user with a drop-down box for default release to put in as a placeholder for any parent stories that are being copied. (A parent story cannot have a release, so if it's required, a parent being copied will fail to copy because the story is first created as a standalone and then becomes a parent when a child is assigned to it.)

In addition, the app can be configured to copy additional fields by using Edit App Settings...
![](images/screenshot.png)

![alt text](https://raw.githubusercontent.com/wrackzone/portfolio-item-copy/master/screenshot.png "Screenshot")
## Settings

This app can be configured via the Edit App Settings gear menu item.

![](images/settings.png)

### Maintain Ranks

This allows for the rank order to be maintained in the copy. For large trees, this could be slower. (default: true)

### Feature Fields

Use to set additional fields from PortfolioItems to copy beyond the default and required fields.

### Story Fields

Use to set additional fields from User Stories to copy beyond the default and required fields.

### Task Fields

Use to set additional fields from Tasks to copy beyond the default and required fields.

## License

AppTemplate is released under the MIT license. See the file [LICENSE](./LICENSE) for the full text.

##Documentation for SDK

4 changes: 2 additions & 2 deletions deploy/App-external.html

Large diffs are not rendered by default.

74 changes: 56 additions & 18 deletions deploy/App-uncompressed.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
defaultSettings: {
portfolioitem : [''],
hierarchicalrequirement : ["ScheduleState","PlanEstimate"],
task : ["State","Estimate","TaskIndex","ToDo","Actuals"]
task : ["State","Estimate","TaskIndex","ToDo","Actuals"],
preserve_rank: true,
}
},

Expand Down Expand Up @@ -113,7 +114,9 @@
success: function(requiredFields) {
app.requiredFields = requiredFields;
// make sure required fields are part of the settings:
Ext.apply(app.fieldsToCopy, requiredFields);
_.each(_.keys(app.fieldsToCopy), function(type) {
app.fieldsToCopy[type] = _.uniq(app.fieldsToCopy[type].concat(requiredFields[type]));
});

if ( Ext.Array.contains( requiredFields.hierarchicalrequirement, 'Release' )) {
this.down('#release-label').setText('Default Release:');
Expand Down Expand Up @@ -144,7 +147,7 @@
title: 'Choose Item',
listeners: {
artifactChosen: function(dialog, selectedRecord){
this.down("#item-label").setText(selectedRecord.get('Name'));
this.down("#item-label").setText(selectedRecord.get('FormattedID') + ' - ' + selectedRecord.get('Name'));
this.down("#copy-button").setDisabled(true);
app.itemSelected(selectedRecord);

Expand All @@ -158,6 +161,8 @@
// updates the summary message.
itemSelected : function(root) {

app.setLoading('Analyzing hierarchy...');

var config = { model : "PortfolioItem",
fetch : true,
filters : [ { property : "ObjectID", operator : "=", value: root.get("ObjectID") } ]
Expand All @@ -184,8 +189,11 @@
// check project selected before enabling.
var projectRef = app.down("#project-picker").getValue();

if (projectRef !== null && projectRef !== "")
if (projectRef !== null && projectRef !== "") {
app.down("#copy-button").setDisabled(false);
}

app.setLoading(false);
});
});
});
Expand Down Expand Up @@ -292,7 +300,8 @@
callback: function(result, operation) {
if (operation.success===true) {
app.copyList[item.source.get("_ref")] = result.get("_ref");
app.down("#summary").setText("Created " + result.get("FormattedID"));
app.down("#summary").setText('(' + _.keys(app.copyList).length + ' of ' + app.list.length + '): ' +
"Created " + result.get("FormattedID") + ' - ' + result.get("Name"));
callback(null,result);
} else {
console.log("Error:",operation);
Expand All @@ -309,7 +318,13 @@

// reads a rally collection object
readCollection : function( collectionConfig, callback ) {
collectionConfig.reference.getCollection(collectionConfig.type,{fetch:true}).load({
var rank_field = Rally.data.Ranker.getRankField(collectionConfig.reference);
var direction = collectionConfig.direction;

collectionConfig.reference.getCollection(collectionConfig.type, {
fetch: true,
sorters: [ { property: rank_field, direction: direction} ]
}).load({
fetch : true,
callback : function(records,operation,success) {
callback(null,records);
Expand All @@ -321,21 +336,27 @@
// recursive method to create a list of all items to be copied.
createList : function(root,callback) {

var config = { model : root.raw._type,
var config = {
model : root.raw._type,
fetch : true,
filters : [ { property : "ObjectID", operator : "=", value: root.get("ObjectID") } ]
};

async.map([config], wsapiQuery, function(err,results) {

var obj = results[0][0];
var direction = "DESC";
app.list.push(obj);
var childRef = null;
if (app.defined(obj.get("Tasks"))) {
childRef = "Tasks";
direction = "DESC";
} else {
if (app.defined(obj.get("Children"))){
if (app.defined(obj.get("Children"))) {
childRef = "Children";
if (/PortfolioItem/.test(root.raw._type)) {
direction = "ASC";
}
} else {
if (app.defined(obj.get("UserStories"))) {
childRef = "UserStories";
Expand All @@ -344,13 +365,22 @@
}

if (app.isObject(childRef)) {
var config = { reference : obj, type : childRef };
async.map([config],app.readCollection,function(err,results){
var children = results[0];
async.map(children,app.createList,function(err,results){
callback(null,results);
var config = { reference : obj, type : childRef, direction: direction };
if ( app.getSetting('preserve_rank') && app.getSetting('preserve_rank') != "false" ) {
async.mapSeries([config],app.readCollection,function(err,results){
var children = results[0];
async.mapSeries(children,app.createList,function(err,results){
callback(null,results);
});
});
});
} else {
async.map([config],app.readCollection,function(err,results){
var children = results[0];
async.map(children,app.createList,function(err,results){
callback(null,results);
});
});
}
} else {
callback(null,obj);
}
Expand Down Expand Up @@ -404,6 +434,7 @@
model : config.model,
fetch : config.fetch,
filters : config.filters,
sorters: config.sorters,
listeners : {
scope : this,
load : function(store, data) {
Expand Down Expand Up @@ -452,14 +483,21 @@
},

getSettingsFields: function() {
return [{
return [
{
name: 'preserve_rank',
xtype: 'rallycheckboxfield',
margin: '10px 0 10px 0',
fieldLabel: 'Maintain Ranks'
},
{
name: 'portfolioitem',
xtype: 'rallyfieldpicker',
autoExpand: true,
alwaysExpanded: false,
modelTypes: ['PortfolioItem/Feature'],
modelTypes: ['PortfolioItem'],
margin: '10px 0 10px 0',
fieldLabel: 'Feature Fields',
fieldLabel: 'Portfolio Item Fields',
_shouldShowField: function(field) {
//console.log(field.name, field);
var attr = field.attributeDefinition;
Expand Down Expand Up @@ -598,7 +636,7 @@
Rally.launchApp('CustomApp', {
name:"portfolio-item-copy",
parentRepos:"",
version:"1.0.1"
version:"1.0.2"
});

});
Expand Down
Loading

0 comments on commit 57300f1

Please sign in to comment.