Skip to content

Commit

Permalink
Fix project scoping and flow state issues
Browse files Browse the repository at this point in the history
  • Loading branch information
Kyle Morse committed Apr 16, 2018
1 parent 3d93e0a commit 1abb7f2
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 18 deletions.
30 changes: 30 additions & 0 deletions App-debug.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<!DOCTYPE html>
<html>
<head>
<title>portfolio-item-copy</title>


<script type="text/javascript" src="https://rally1.rallydev.com/apps/2.1/sdk-debug.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/async/1.22/async.min.js"></script>

<script type="text/javascript">
Rally.loadScripts(
[
"App.js",
"utility.js",
],
function() {
Rally.launchApp('CustomApp', {
name:"portfolio-item-copy",
parentRepos:"",
version:"1.0.1"
});
}, true);
</script>


<link rel="stylesheet" type="text/css" href="app.css"/>
</head>
<body>
</body>
</html>
26 changes: 21 additions & 5 deletions App.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,13 +121,19 @@ Ext.define('CustomApp', {
// displays a chooser to select the portfolio item
chooseItem : function() {

Ext.create('Rally.ui.dialog.ChooserDialog', {
Ext.create('Rally.ui.dialog.ArtifactChooserDialog', {
artifactTypes: ['PortfolioItem'],
autoShow: true,

//override to allow pi's in projects with only
//read access
_isArtifactEditable: function(record) {
return true;
},

title: 'Choose Item',
listeners: {
artifactChosen: function(selectedRecord){
artifactChosen: function(dialog, selectedRecord){
this.down("#item-label").setText(selectedRecord.get('Name'));
this.down("#copy-button").setDisabled(true);
app.itemSelected(selectedRecord);
Expand Down Expand Up @@ -233,10 +239,16 @@ Ext.define('CustomApp', {

Ext.Array.each( this.fieldsToCopy[type], function(field) {
var item_release = this._getRelease(item);
if ( !Ext.isEmpty(item_release) && Ext.Array.contains(reference_fields, field) ) {
copy[field] = { _ref: item_release._ref };
if (field === 'FlowState') {
if (Rally.util.Ref.getRelativeUri(item.get('Project')) === app.projectRef) {
copy[field] = Rally.util.Ref.getRelativeUri(item.get('FlowState'));
}
} else {
copy[field] = item.get(field);
if ( !Ext.isEmpty(item_release) && Ext.Array.contains(reference_fields, field) ) {
copy[field] = { _ref: item_release._ref };
} else {
copy[field] = item.get(field);
}
}
}, this);

Expand Down Expand Up @@ -374,6 +386,10 @@ Ext.define('CustomApp', {

Ext.create('Rally.data.WsapiDataStore', {
autoLoad : true,
context: {
project: null,
workspace: app.getContext().getWorkspaceRef()
},
limit : "Infinity",
model : config.model,
fetch : config.fetch,
Expand Down
2 changes: 1 addition & 1 deletion config.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "portfolio-item-copy",
"className": "CustomApp",
"server": "https://rally1.rallydev.com",
"sdk": "2.0",
"sdk": "2.1",
"javascript": [
"App.js",
"utility.js",
Expand Down
30 changes: 30 additions & 0 deletions deploy/App-external.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<!DOCTYPE html>
<html>
<head>
<title>portfolio-item-copy</title>

<script type="text/javascript" src="https://rally1.rallydev.com/apps/2.1/sdk.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/async/1.22/async.min.js"></script>

<script type="text/javascript">
Rally.onReady(function () {
var app=null;Ext.define("CustomApp",{extend:"Rally.app.App",componentCls:"app",layout:{type:"table",columns:2},config:{defaultSettings:{portfolioitem:[""],hierarchicalrequirement:["ScheduleState","PlanEstimate"],task:["State","Estimate","TaskIndex","ToDo","Actuals"]}},items:[{xtype:"label",text:"Copy To Project:",padding:"5px"},{id:"project-picker",xtype:"rallyprojectpicker",margin:"5px",model:"Project",field:"Name"},{xtype:"rallybutton",text:"Select Portfolio Item",margin:"5px",handler:function(){app.chooseItem()}},{id:"item-label",xtype:"label",margin:5,style:"font-weight:bold;",text:""},{itemId:"release-label",xtype:"label",padding:5,text:""},{itemId:"release-chooser",xtype:"container",margin:5},{id:"copy-button",xtype:"rallybutton",text:"Copy",margin:"5px",disabled:!0,handler:function(){app.performCopy()}},{id:"summary",xtype:"label",margin:"5px",style:"font-weight:bold;",text:""}],launch:function(){app=this;var e={portfolioitem:this.getSetting("portfolioitem"),hierarchicalrequirement:this.getSetting("hierarchicalrequirement"),task:this.getSetting("task")};Ext.isEmpty(e.portfolioitem)||Ext.isArray(e.portfolioitem)||(e.portfolioitem=e.portfolioitem.split(",")),Ext.isEmpty(e.hierarchicalrequirement)||Ext.isArray(e.hierarchicalrequirement)||(e.hierarchicalrequirement=e.hierarchicalrequirement.split(",")),Ext.isEmpty(e.task)||Ext.isArray(e.task)||(e.task=e.task.split(",")),app.fieldsToCopy=e,this._getRequiredFields().then({scope:this,success:function(e){app.requiredFields=e,Ext.apply(app.fieldsToCopy,e),Ext.Array.contains(e.hierarchicalrequirement,"Release")&&(this.down("#release-label").setText("Default Release:"),this.down("#release-chooser").removeAll(),this.down("#release-chooser").add({xtype:"rallyreleasecombobox",showArrows:!1}))}})},chooseItem:function(){Ext.create("Rally.ui.dialog.ArtifactChooserDialog",{artifactTypes:["PortfolioItem"],autoShow:!0,_isArtifactEditable:function(e){return!0},title:"Choose Item",listeners:{artifactChosen:function(e,t){this.down("#item-label").setText(t.get("Name")),this.down("#copy-button").setDisabled(!0),app.itemSelected(t)},scope:this}})},itemSelected:function(e){var t={model:"PortfolioItem",fetch:!0,filters:[{property:"ObjectID",operator:"=",value:e.get("ObjectID")}]};async.map([t],app.wsapiQuery,function(e,t){var r=t[0][0];app.list=[],async.map([r],app.createList,function(e,t){app.models={},app.types=_.uniq(_.map(app.list,function(e){return e.get("_type")})),async.mapSeries(app.types,app.loadModel,function(e,t){_.each(app.types,function(e,r){app.models[e]=t[r]}),app.down("#summary").setText(app.list.length+" Items to be copied");var r=app.down("#project-picker").getValue();null!==r&&""!==r&&app.down("#copy-button").setDisabled(!1)})})})},performCopy:function(){app.copyList={},app.projectRef=app.down("#project-picker").getValue(),async.mapSeries(app.list,app.copyItem,function(e,t){null===e?app.down("#summary").setText(t.length+" Items copied to "+t[0].get("FormattedID")):app.down("#summary").setText(e,!1)})},copyItem:function(e,t){var r={Name:e.get("Name"),Workspace:e.get("Workspace")._ref,Description:e.get("Description"),Owner:null!==e.get("Owner")?e.get("Owner")._ref:null,DisplayColor:_.isNull(e.get("DisplayColor"))||_.isUndefined(e.get("DisplayColor"))?null:e.get("DisplayColor")};r=app.copyTypeSpecificFields(r,e);var a=app.parentRef(e);if(null!==a){var i=app.copyList[a.ref];_.isUndefined(i)||(r[a.type]=i)}var o=app.models[e.get("_type")];async.map([{model:o,copy:r,source:e}],app.createItem,function(e,r){_.isUndefined(e)||_.isNull(e)?t(null,r[0]):t(e,null)})},copyTypeSpecificFields:function(e,t){var r=-1!==t.get("_type").toLowerCase().indexOf("portfolioitem")?"portfolioitem":t.get("_type");if(reference_fields=["Release","Iteration","Owner"],Ext.Array.each(this.fieldsToCopy[r],function(r){var a=this._getRelease(t);"FlowState"===r?Rally.util.Ref.getRelativeUri(t.get("Project"))===app.projectRef&&(e[r]=Rally.util.Ref.getRelativeUri(t.get("FlowState"))):!Ext.isEmpty(a)&&Ext.Array.contains(reference_fields,r)?e[r]={_ref:a._ref}:e[r]=t.get(r)},this),t.get("Tags").Count>0){var a=_.map(t.get("Tags")._tagsNameArray,function(e){return{_ref:e._ref}});e.Tags=a}return e},_getRelease:function(e){var t=e.get("Release");return Ext.isEmpty(t)&&this.down("rallyreleasecombobox")&&(t=this.down("rallyreleasecombobox").getRecord().getData()),t},createItem:function(e,t){var r=Ext.create(e.model,e.copy);r.set("Project",app.projectRef),r.save({callback:function(r,a){if(!0===a.success)app.copyList[e.source.get("_ref")]=r.get("_ref"),app.down("#summary").setText("Created "+r.get("FormattedID")),t(null,r);else{console.log("Error:",a);var i="<span class='icon-warning'> </span>Create Error when copying "+e.copy.Name;Ext.isEmpty(a.error)||Ext.isEmpty(a.error.errors)||(i+=":<br/>"+a.error.errors.join("<br/>")),t(i,null)}}})},readCollection:function(e,t){e.reference.getCollection(e.type,{fetch:!0}).load({fetch:!0,callback:function(e,r,a){t(null,e)}})},createList:function(e,t){var r={model:e.raw._type,fetch:!0,filters:[{property:"ObjectID",operator:"=",value:e.get("ObjectID")}]};async.map([r],wsapiQuery,function(e,r){var a=r[0][0];app.list.push(a);var i=null;if(app.defined(a.get("Tasks"))?i="Tasks":app.defined(a.get("Children"))?i="Children":app.defined(a.get("UserStories"))&&(i="UserStories"),app.isObject(i)){var o={reference:a,type:i};async.map([o],app.readCollection,function(e,r){var a=r[0];async.map(a,app.createList,function(e,r){t(null,r)})})}else t(null,a)})},parentRef:function(e){return _.isObject(e.get("Parent"))?{type:"Parent",ref:e.get("Parent")._ref}:_.isObject(e.get("WorkProduct"))?{type:"WorkProduct",ref:e.get("WorkProduct")._ref}:_.isObject(e.get("PortfolioItem"))?{type:"PortfolioItem",ref:e.get("PortfolioItem")._ref}:null},loadModel:function(e,t){Rally.data.ModelFactory.getModel({type:e,success:function(e){t(null,e)}})},isObject:function(e){return!_.isUndefined(e)&&!_.isNull(e)},defined:function(e){return app.isObject(e)&&e.Count>0},wsapiQuery:function(e,t){Ext.create("Rally.data.WsapiDataStore",{autoLoad:!0,context:{project:null,workspace:app.getContext().getWorkspaceRef()},limit:"Infinity",model:e.model,fetch:e.fetch,filters:e.filters,listeners:{scope:this,load:function(e,r){t(null,r)}}})},_getRequiredFields:function(){var e=Ext.create("Deft.Deferred");return Deft.Promise.all([this._getRequiredFieldsForModel("portfolioitem"),this._getRequiredFieldsForModel("hierarchicalrequirement"),this._getRequiredFieldsForModel("task")]).then({scope:this,success:function(t){e.resolve({portfolioitem:t[0],hierarchicalrequirement:t[1],task:t[2]})}}),e.promise},_getRequiredFieldsForModel:function(e){var t=Ext.create("Deft.Deferred");return Rally.data.ModelFactory.getModel({type:e,success:function(e){var r=e.getFields(),a=[];Ext.Array.each(r,function(e){e.required&&!e.readOnly&&a.push(e.name)}),t.resolve(a)}}),t.promise},getSettingsFields:function(){return[{name:"portfolioitem",xtype:"rallyfieldpicker",autoExpand:!0,alwaysExpanded:!1,modelTypes:["PortfolioItem/Feature"],margin:"10px 0 10px 0",fieldLabel:"Feature Fields",_shouldShowField:function(e){var t=e.attributeDefinition;return t&&!t.ReadOnly&&"COLLECTION"!==t.AttributeType},listeners:{ready:function(e){e.collapse()}},readyEvent:"ready"},{name:"hierarchicalrequirement",xtype:"rallyfieldpicker",autoExpand:!0,alwaysExpanded:!1,modelTypes:["HierarchicalRequirement"],margin:"10px 0 10px 0",fieldLabel:"Story Fields",_shouldShowField:function(e){var t=e.attributeDefinition;return t&&!t.ReadOnly&&"COLLECTION"!==t.AttributeType},listeners:{ready:function(e){e.collapse()}},readyEvent:"ready"},{name:"task",xtype:"rallyfieldpicker",autoExpand:!0,alwaysExpanded:!1,modelTypes:["Task"],margin:"10px 0 200px 0",fieldLabel:"Task Fields",_shouldShowField:function(e){var t=e.attributeDefinition;return t&&!t.ReadOnly&&"COLLECTION"!==t.AttributeType},listeners:{ready:function(e){e.collapse()}},readyEvent:"ready"}]},isExternal:function(){return void 0===this.getAppId()},onSettingsUpdate:function(e){console.log("onSettingsUpdate",e),Ext.apply(this,e),this.launch()}});
function getReleaseTimeBox(t){var e=t.getContext().getTimeboxScope(),n=null;e?n=e.getRecord().get("Name"):n="";return n}function wsapiQuery(t,e){Ext.create("Rally.data.WsapiDataStore",{autoLoad:!0,limit:"Infinity",context:{project:null,workspace:Rally.environment.getContext().getWorkspaceRef()},model:t.model,fetch:t.fetch,filters:t.filters,listeners:{scope:this,load:function(t,n){e(null,n)}}})}function snapshotQuery(t,e){var n={find:t.find,fetch:t.fetch,hydrate:t.hydrate,autoLoad:!0,pageSize:1e4,limit:"Infinity",listeners:{scope:this,load:function(t,n,o){console.log("snapshots:",n.length),e(null,n)}}};Ext.create("Rally.data.lookback.SnapshotStore",n)}function pointsUnitType(t){return"Points"==t}

Rally.launchApp('CustomApp', {
name:"portfolio-item-copy",
parentRepos:"",
version:"1.0.1"
});

});
</script>


<style type="text/css">

</style>
</head>
<body>
</body>
</html>
35 changes: 28 additions & 7 deletions deploy/App-uncompressed.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<title>portfolio-item-copy</title>

<script type="text/javascript" src="/apps/2.0/sdk.js"></script>
<script type="text/javascript" src="/apps/2.1/sdk.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/async/1.22/async.min.js"></script>

<script type="text/javascript">
Expand Down Expand Up @@ -131,13 +131,19 @@
// displays a chooser to select the portfolio item
chooseItem : function() {

Ext.create('Rally.ui.dialog.ChooserDialog', {
Ext.create('Rally.ui.dialog.ArtifactChooserDialog', {
artifactTypes: ['PortfolioItem'],
autoShow: true,

//override to allow pi's in projects with only
//read access
_isArtifactEditable: function(record) {
return true;
},

title: 'Choose Item',
listeners: {
artifactChosen: function(selectedRecord){
artifactChosen: function(dialog, selectedRecord){
this.down("#item-label").setText(selectedRecord.get('Name'));
this.down("#copy-button").setDisabled(true);
app.itemSelected(selectedRecord);
Expand Down Expand Up @@ -243,10 +249,16 @@

Ext.Array.each( this.fieldsToCopy[type], function(field) {
var item_release = this._getRelease(item);
if ( !Ext.isEmpty(item_release) && Ext.Array.contains(reference_fields, field) ) {
copy[field] = { _ref: item_release._ref };
if (field === 'FlowState') {
if (Rally.util.Ref.getRelativeUri(item.get('Project')) === app.projectRef) {
copy[field] = Rally.util.Ref.getRelativeUri(item.get('FlowState'));
}
} else {
copy[field] = item.get(field);
if ( !Ext.isEmpty(item_release) && Ext.Array.contains(reference_fields, field) ) {
copy[field] = { _ref: item_release._ref };
} else {
copy[field] = item.get(field);
}
}
}, this);

Expand Down Expand Up @@ -384,6 +396,10 @@

Ext.create('Rally.data.WsapiDataStore', {
autoLoad : true,
context: {
project: null,
workspace: app.getContext().getWorkspaceRef()
},
limit : "Infinity",
model : config.model,
fetch : config.fetch,
Expand Down Expand Up @@ -535,6 +551,10 @@
Ext.create('Rally.data.WsapiDataStore', {
autoLoad : true,
limit : "Infinity",
context: {
project: null,
workspace: Rally.environment.getContext().getWorkspaceRef()
},
model : config.model,
fetch : config.fetch,
filters : config.filters,
Expand Down Expand Up @@ -577,7 +597,8 @@

Rally.launchApp('CustomApp', {
name:"portfolio-item-copy",
parentRepos:""
parentRepos:"",
version:"1.0.1"
});

});
Expand Down
Loading

0 comments on commit 1abb7f2

Please sign in to comment.