想要将Rally的故事从一个项目复制到另一个项目
Want to copy Rally stories from one project to another
我正试图将故事和任务(及其层次结构)从一个Rally项目复制到另一个。
我只需要从一个"模板"项目中复制,所以我不需要使用ProjectPicker小部件。只需简单地利用现有的复制故事和任务的应用程序即可,但它需要能够从一个项目中读取并写入另一个项目(您登录的当前项目将是最简单的。)
我修改了这个应用程序,这样它就可以从我没有登录的项目中读取——很好。复制会在我正在阅读的项目中产生一个复制的故事——而不是我在Rally中登录的那个——有人知道如何做到这一点吗?
我假设您使用的是Story Deep Copy应用程序。从您的问题来看,您似乎已经修改了复制项目的hangman变量。假设该项目的OID是22222,而要复制的目标项目的OID是33333。两个项目都在同一个工作区OID 11111中。
var dataSource = new rally.sdk.data.RallyDataSource('11111', '22222',
'__PROJECT_SCOPING_UP__', '__PROJECT_SCOPING_DOWN__');
我在this._copyStory
:中添加了一行
foundObject.Project._ref = "https://rally1.rallydev.com/slm/webservice/1.29/project/33333.js";
这是整个代码:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html dir="ltr">
<head>
<title>Deep Copy Story</title>
<meta name="Name" content="App: Story Deep Copy"/>
<meta name="Version" content="2012.01.14"/>
<meta name="Vendor" content="Rally Software"/>
<script type="text/javascript" src="/apps/1.29/sdk.js?apiVersion=1.29"></script>
<script type="text/javascript">
rally.StoryDeepCopy = function (rallyDataSource, config) {
var storyBuffer = [];
var firstStory = null;
var finishedCallback;
var that = this;
function getTypeFromRef(ref) {
if (rally.sdk.util.Ref.isRef(ref)) {
var list = ref.split("/");
list.pop();
return list.pop();
}
else {
throw "Function getTypeFromRef expected a Rally Reference.";
}
}
//dojo.connect(obj, event, context, method, dontFix);
this._fireEvent = function(eventName, eventArgs) {
if (config && config.eventListeners[eventName] && dojo.isFunction(config.eventListeners[eventName])) {
config.eventListeners[eventName](that, eventArgs);
}
};
// removes private and read only fields to keep from pushing them up.
this.filterObject = function (object) {
delete object.Discussion;
delete object.Rank;
delete object.LastUpdateDate;
delete object.Attachments;
delete object.AcceptedDate;
delete object.Blocker;
delete object.Defects;
delete object.TaskActualTotal;
delete object.TaskEstimateTotal;
delete object.TaskRemainingTotal;
delete object.TaskEstimateTotal;
delete object.RevisionHistory;
delete object.Subscription;
delete object.FormattedID;
delete object.CreationDate;
delete object.Changesets;
delete object.ObjectID;
for (var j in object) {
if (j.substring(0, 1) == '_') {
delete object[j];
}
}
return object;
};
this._addObject = function(object, typeName, callback) {
var item = dojo.clone(object);
item = this.filterObject(item);
function errorFunctionWrapper(error) {
if (dojo.isArray(error.Errors)) {
var errorMessage = error.Errors.pop();
if (errorMessage.indexOf("Not authorized to create:") >= 0) {
errorMessage = "Unable to create an object. This can happen due to a child or task being in a project you do not have write permissions to.";
}
rally.sdk.ui.AppHeader.showMessage("error", errorMessage, 10000);
}
else if(dojo.isObject(error)&&error.message){
rally.sdk.ui.AppHeader.showMessage("error", error.message, 10000);
error = [error.message];
}
if (dojo.isFunction(config.onError)) {
config.onError(error);
}
}
rallyDataSource.create(typeName, item, callback, errorFunctionWrapper);
};
this._copyAllFromBuffer = function() {
if (storyBuffer.length > 0) {
var story = storyBuffer.pop();
that._copyStory(story.ref, story.parent, that._copyAllFromBuffer);
}
else {
if (finishedCallback) {
finishedCallback(firstStory);
}
}
};
this._addStoriesToBuffer = function(storyArray, parentRef) {
dojo.forEach(storyArray, function (story) {
storyBuffer.push({
ref: story._ref,
parent: parentRef
});
});
};
this._copyStory = function(ref, parentRef, callback) {
rallyDataSource.getRallyObject(ref, function (foundObject) {
var type = getTypeFromRef(ref);
that._fireEvent("storyPreAdd", {story:foundObject});
console.log(foundObject.Name);
console.log(foundObject.Project._ref);
foundObject.Project._ref = "https://rally1.rallydev.com/slm/webservice/1.29/project/33333.js";
if (parentRef) {
foundObject.Parent = parentRef;
}
else {
foundObject.Name = "(Copy of) " + foundObject.Name;
}
that._addObject(foundObject, type, function (storyRef) {
if (!firstStory) {
firstStory = storyRef;
}
that._fireEvent("storyPostAdd", {});
that._addStoriesToBuffer(foundObject.Children, storyRef);
that._copyTasksToStory(foundObject.Tasks, storyRef, callback);
}, null);
});
};
this._copyTasksToStory = function(tasks, storyRef, callback) {
//Copy the array
var localTasks = tasks.slice(0);
if (localTasks.length > 0) {
var task = localTasks.pop();
that._copyTask(task._ref, storyRef, function () {
that._copyTasksToStory(localTasks, storyRef, callback);
});
}
else {
callback();
}
};
this._copyTask = function(ref, storyRef, callback) {
rallyDataSource.getRallyObject(ref, function (foundObject) {
var type = getTypeFromRef(ref);
foundObject.WorkProduct = storyRef;
that._fireEvent("taskPreAdd", {task:foundObject});
that._addObject(foundObject, type, function (ref, warnings) {
if (callback) {
that._fireEvent("taskPostAdd", [ref]);
callback();
}
}, null);
});
};
this.copyStory = function (ref, callback) {
that._copyStory(ref, undefined, that._copyAllFromBuffer);
finishedCallback = callback;
};
};
</script>
<script type="text/javascript">
rally.addOnLoad(function() {
var selectedValue = null;
var tasksAdded = 0;
var storiesAdded = 0;
var searchStories;
var goButton, chooseButton;
var chooser;
var waiter;
var dataSource = new rally.sdk.data.RallyDataSource('111111', '2222222',
'__PROJECT_SCOPING_UP__', '__PROJECT_SCOPING_DOWN__');
function taskPostAdd(object, args) {
tasksAdded = tasksAdded + 1;
displayTasksAdded(tasksAdded);
}
function taskPreAdd(object, args) {
dojo.byId("currentInfo").innerHTML = "Adding Task " + args.task.FormattedID + " - " + args.task.Name;
}
function storyPreAdd(object, args) {
dojo.byId("currentInfo").innerHTML = "Adding User Story " + args.story.FormattedID + " - " + args.story.Name;
}
function storyPostAdd(object, args) {
storiesAdded = storiesAdded + 1;
displayStoriesAdded(storiesAdded);
}
function displayStoriesAdded(count) {
dojo.byId("storyResult").innerHTML = "Stories added: " + count;
}
function displayTasksAdded(count) {
dojo.byId("taskResult").innerHTML = "Tasks added: " + tasksAdded;
}
function storyCopied(story) {
dojo.byId("currentInfo").innerHTML = "Copy complete: ";
var link = new rally.sdk.ui.basic.Link({
item: story,
text: story._refObjectName
});
link.display('currentInfo');
goButton.setEnabled(true);
chooseButton.setEnabled(true);
if(waiter) {
waiter.hide();
waiter = null;
}
}
function buttonPressed() {
if (selectedValue) {
var config = {
eventListeners:{
storyPreAdd:storyPreAdd,
storyPostAdd:storyPostAdd ,
taskPreAdd:taskPreAdd,
taskPostAdd: taskPostAdd
}
};
tasksAdded = 0;
displayTasksAdded(tasksAdded);
storiesAdded = 0;
displayStoriesAdded(storiesAdded);
dojo.byId("currentInfo").innerHTML = "";
var copy = new rally.StoryDeepCopy(dataSource, config);
goButton.setEnabled(false);
chooseButton.setEnabled(false);
waiter = new rally.sdk.ui.basic.Wait({});
waiter.display('wait');
copy.copyStory(rally.sdk.util.Ref.getRelativeRef(selectedValue), storyCopied);
}
}
function onChooserClose(chooser, args) {
if (args.selectedItem) {
selectedValue = args.selectedItem;
goButton.setEnabled(true);
dojo.byId('storyBox').innerHTML = args.selectedItem.FormattedID + ' - ' +
args.selectedItem.Name;
}
}
function showChooser() {
var chooserConfig = {
fetch:"FormattedID,Name,Description,Project",
title: 'Story Chooser'
};
chooser = new rally.sdk.ui.Chooser(chooserConfig, dataSource);
chooser.addEventListener('onClose', onChooserClose);
chooser.display();
}
rally.addOnLoad(function () {
goButton = new rally.sdk.ui.basic.Button({
text: "Copy",
enabled: false
});
goButton.addEventListener('onClick', buttonPressed);
goButton.display('goButton');
chooseButton = new rally.sdk.ui.basic.Button({
text: "Choose"
});
chooseButton.addEventListener('onClick', showChooser);
chooseButton.display('chooseButton');
showChooser();
rally.sdk.ui.AppHeader.setHelpTopic("252");
});
});
</script>
</head>
<body>
<div id="container">
<div style="float:left">
<span id="chooseButton"></span>
<span id="storyBox" style="line-height:18px;vertical-align:middle">[No story selected]</span>
<span id="goButton"></span>
</div>
<div id="wait" style="float:left; height: 16px; width: 24px;"></div>
<div style="margin-left:5px;padding-top:10px;clear:both">
<div id="currentInfo" style="height:16px"></div>
<div id="storyResult" style="margin-top:10px"></div>
<div id="taskResult"></div>
</div>
</div>
</body>
</html>
编辑:上面的代码被修改为使用对象下拉列表,从中可以选择目标项目。因此,没有必要对目标项目的参考进行硬编码。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html dir="ltr">
<head>
<title>Deep Copy Story</title>
<meta name="Name" content="App: Story Deep Copy"/>
<meta name="Version" content="2012.01.14"/>
<meta name="Vendor" content="Rally Software"/>
<script type="text/javascript" src="/apps/1.29/sdk.js?apiVersion=1.29"></script>
<script type="text/javascript">
rally.StoryDeepCopy = function (rallyDataSource, config) {
var storyBuffer = [];
var firstStory = null;
var finishedCallback;
var that = this;
function getTypeFromRef(ref) {
if (rally.sdk.util.Ref.isRef(ref)) {
var list = ref.split("/");
list.pop();
return list.pop();
}
else {
throw "Function getTypeFromRef expected a Rally Reference.";
}
}
//dojo.connect(obj, event, context, method, dontFix);
this._fireEvent = function(eventName, eventArgs) {
if (config && config.eventListeners[eventName] && dojo.isFunction(config.eventListeners[eventName])) {
config.eventListeners[eventName](that, eventArgs);
}
};
// removes private and read only fields to keep from pushing them up.
this.filterObject = function (object) {
delete object.Discussion;
delete object.Rank;
delete object.LastUpdateDate;
delete object.Attachments;
delete object.AcceptedDate;
delete object.Blocker;
delete object.Defects;
delete object.TaskActualTotal;
delete object.TaskEstimateTotal;
delete object.TaskRemainingTotal;
delete object.TaskEstimateTotal;
delete object.RevisionHistory;
delete object.Subscription;
delete object.FormattedID;
delete object.CreationDate;
delete object.Changesets;
delete object.ObjectID;
for (var j in object) {
if (j.substring(0, 1) == '_') {
delete object[j];
}
}
return object;
};
this._addObject = function(object, typeName, callback) {
var item = dojo.clone(object);
console.log(item);
item = this.filterObject(item);
function errorFunctionWrapper(error) {
if (dojo.isArray(error.Errors)) {
var errorMessage = error.Errors.pop();
if (errorMessage.indexOf("Not authorized to create:") >= 0) {
errorMessage = "Unable to create an object. This can happen due to a child or task being in a project you do not have write permissions to.";
}
rally.sdk.ui.AppHeader.showMessage("error", errorMessage, 10000);
}
else if(dojo.isObject(error)&&error.message){
rally.sdk.ui.AppHeader.showMessage("error", error.message, 10000);
error = [error.message];
}
if (dojo.isFunction(config.onError)) {
config.onError(error);
}
}
rallyDataSource.create(typeName, item, callback, errorFunctionWrapper);
};
this._copyAllFromBuffer = function() {
if (storyBuffer.length > 0) {
var story = storyBuffer.pop();
that._copyStory(story.ref, story.parent, that._copyAllFromBuffer);
}
else {
if (finishedCallback) {
finishedCallback(firstStory);
}
}
};
this._addStoriesToBuffer = function(storyArray, parentRef) {
dojo.forEach(storyArray, function (story) {
storyBuffer.push({
ref: story._ref,
parent: parentRef
});
});
};
this._copyStory = function(ref, parentRef, callback) {
rallyDataSource.getRallyObject(ref, function (foundObject) {
var type = getTypeFromRef(ref);
that._fireEvent("storyPreAdd", {story:foundObject});
foundObject.Project._ref = destinationProject;
if (parentRef) {
foundObject.Parent = parentRef;
}
else {
foundObject.Name = "(Copy of) " + foundObject.Name;
}
that._addObject(foundObject, type, function (storyRef) {
if (!firstStory) {
firstStory = storyRef;
}
that._fireEvent("storyPostAdd", {});
that._addStoriesToBuffer(foundObject.Children, storyRef);
that._copyTasksToStory(foundObject.Tasks, storyRef, callback);
}, null);
});
};
this._copyTasksToStory = function(tasks, storyRef, callback) {
//Copy the array
var localTasks = tasks.slice(0);
if (localTasks.length > 0) {
var task = localTasks.pop();
that._copyTask(task._ref, storyRef, function () {
that._copyTasksToStory(localTasks, storyRef, callback);
});
}
else {
callback();
}
};
this._copyTask = function(ref, storyRef, callback) {
rallyDataSource.getRallyObject(ref, function (foundObject) {
var type = getTypeFromRef(ref);
foundObject.WorkProduct = storyRef;
that._fireEvent("taskPreAdd", {task:foundObject});
that._addObject(foundObject, type, function (ref, warnings) {
if (callback) {
that._fireEvent("taskPostAdd", [ref]);
callback();
}
}, null);
});
};
this.copyStory = function (ref, callback) {
that._copyStory(ref, undefined, that._copyAllFromBuffer);
finishedCallback = callback;
};
};
</script>
<script type="text/javascript">
var destinationProject;
function dropdownChanged(dropdown, eventArgs) {
destinationProject = eventArgs.value
console.log(destinationProject);
}
rally.addOnLoad(function() {
var selectedValue = null;
var tasksAdded = 0;
var storiesAdded = 0;
var searchStories;
var goButton, chooseButton;
var chooser;
var waiter;
var dataSource = new rally.sdk.data.RallyDataSource('1111', '2222',
'__PROJECT_SCOPING_UP__', '__PROJECT_SCOPING_DOWN__');
var config = {
type : "project",
attribute: "Name",
query : '(State = "Open")'
};
var objectDropdown = new rally.sdk.ui.ObjectDropdown(config, dataSource);
objectDropdown.display("aDiv", dropdownChanged);
function taskPostAdd(object, args) {
tasksAdded = tasksAdded + 1;
displayTasksAdded(tasksAdded);
}
function taskPreAdd(object, args) {
dojo.byId("currentInfo").innerHTML = "Adding Task " + args.task.FormattedID + " - " + args.task.Name;
}
function storyPreAdd(object, args) {
dojo.byId("currentInfo").innerHTML = "Adding User Story " + args.story.FormattedID + " - " + args.story.Name;
}
function storyPostAdd(object, args) {
storiesAdded = storiesAdded + 1;
displayStoriesAdded(storiesAdded);
}
function displayStoriesAdded(count) {
dojo.byId("storyResult").innerHTML = "Stories added: " + count;
}
function displayTasksAdded(count) {
dojo.byId("taskResult").innerHTML = "Tasks added: " + tasksAdded;
}
function storyCopied(story) {
dojo.byId("currentInfo").innerHTML = "Copy complete: ";
var link = new rally.sdk.ui.basic.Link({
item: story,
text: story._refObjectName
});
link.display('currentInfo');
goButton.setEnabled(true);
chooseButton.setEnabled(true);
if(waiter) {
waiter.hide();
waiter = null;
}
}
function buttonPressed() {
if (selectedValue) {
var config = {
eventListeners:{
storyPreAdd:storyPreAdd,
storyPostAdd:storyPostAdd ,
taskPreAdd:taskPreAdd,
taskPostAdd: taskPostAdd
}
};
tasksAdded = 0;
displayTasksAdded(tasksAdded);
storiesAdded = 0;
displayStoriesAdded(storiesAdded);
dojo.byId("currentInfo").innerHTML = "";
var copy = new rally.StoryDeepCopy(dataSource, config);
goButton.setEnabled(false);
chooseButton.setEnabled(false);
waiter = new rally.sdk.ui.basic.Wait({});
waiter.display('wait');
copy.copyStory(rally.sdk.util.Ref.getRelativeRef(selectedValue), storyCopied);
}
}
function onChooserClose(chooser, args) {
if (args.selectedItem) {
selectedValue = args.selectedItem;
goButton.setEnabled(true);
dojo.byId('storyBox').innerHTML = args.selectedItem.FormattedID + ' - ' +
args.selectedItem.Name;
}
}
function showChooser() {
var chooserConfig = {
fetch:"FormattedID,Name,Description,Project",
title: 'Story Chooser'
};
chooser = new rally.sdk.ui.Chooser(chooserConfig, dataSource);
chooser.addEventListener('onClose', onChooserClose);
chooser.display();
}
rally.addOnLoad(function () {
goButton = new rally.sdk.ui.basic.Button({
text: "Copy",
enabled: false
});
goButton.addEventListener('onClick', buttonPressed);
goButton.display('goButton');
chooseButton = new rally.sdk.ui.basic.Button({
text: "Choose"
});
chooseButton.addEventListener('onClick', showChooser);
chooseButton.display('chooseButton');
showChooser();
rally.sdk.ui.AppHeader.setHelpTopic("252");
});
});
</script>
</head>
<body>
<div id="container">
<div id="aDiv"></div>
<div style="float:left">
<span id="chooseButton"></span>
<span id="storyBox" style="line-height:18px;vertical-align:middle">[No story selected]</span>
<span id="goButton"></span>
</div>
<div id="wait" style="float:left; height: 16px; width: 24px;"></div>
<div style="margin-left:5px;padding-top:10px;clear:both">
<div id="currentInfo" style="height:16px"></div>
<div id="storyResult" style="margin-top:10px"></div>
<div id="taskResult"></div>
</div>
</div>
</body>
</html>
相关文章:
- 当在Ember中点击一个项目时,我如何将一个活动类添加到项目列表中
- 通过另一个php应用程序将我的项目推送到Github存储库中
- angularjs移除焦点上的活动类并添加到下一个项目
- 相对于角度控制器中的另一个阵列过滤阵列项目
- 如何通过AngularJS删除一个项目
- 如何使用jquery将所选项目从一个下拉组列表(optgroup)移动到另一个下拉列表(optgroup)
- 获取每个项目中不为 null 的最后一个项目
- 在另一个项目中使用大理石测试rxjs5方法
- 使用javascript/jquery从现有数组中创建一个新数组,该数组保存项目存在的次数
- 如何在猫头鹰旋转木马中滑动所有可见项目,而不是使用上一个/下一个按钮
- Git为整个项目提供一个repo(frontend+node.js)
- 与杜兰达尔合作的第一个JavaScript项目.尝试从第三方 API 获取数据
- Asp.net 引导下拉菜单 - 选择一个项目
- 处理一个项目,想要添加以下菜单栏,并尽量减少向下滑动
- 如何将从GWT编译的JS添加/访问到另一个外部HTML / JS项目中
- 在angularJs的选择框中预先选择一个项目
- 我想在混合移动应用程序中使用Sqlite插件(Cordova)创建一个示例项目
- 如何将一个表单中的项目添加到 Rails 中另一个表单的下拉选项
- jQuery 使用 ID 作为 URL 选择一个项目
- 淡入<灯光>一个项目一个项目