3

I'm trying to copy stories and tasks (and their hierarchies) from one Rally project to another.

I only need to copy from one 'template' project so I don't need to make use of the ProjectPicker widget. Simply leveraging the existing app that copies stories and tasks is all that is need but it needs to be able to read from one project and write to another project (the current project you're logged into would be the easiest.)

I modified the app so that it can read from a project I'm not logged into - good. The copy results in a copied story in the project I'm reading from - not the one I'm logged into in Rally - does anyone know how to make this happen?

4

1 回答 1

2

我假设您正在使用 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>
于 2013-07-23T02:48:41.113 回答