关闭工作项,仅当子工作项处于关闭状态时

分享于 

18分钟阅读

Web开发

  繁體

抽象画

在开发世界中,具有最灵活的应用程序,我们提供了扩展软件功能的机会。 在市场上类似软件不具备必需功能,但其中一个可能更倾向于特定的应用程序,基于它的提供的功能,如免费。开源。可以扩展。应用程序的功能或者业务需求需要的新功能。 如果你不愿意提供支持,我们可能会在你的应用程序中使用api来解决这种情况,如支持成本。时间。频繁更改。 有些可能是简单的配置更改,而其他一些则使用提供的api扩展功能。 这使我们能够集成我们需要的功能,并且控制在我们手中。

介绍

Team Foundation Server是一个ALM工具。 它可以根据业务需求进行扩展。 控件可以创建,以便在工作项窗体上填充它们。 我们可以通过创建控件来添加额外的业务功能。 由TFS提供的API使这成为可能。 仅当子工作项关闭时关闭工作项不作为TFS标准功能的一部分。 我们需要扩展功能,通过使用由TFS提供的API来实现这个功能。 可以在各种接口的帮助下使用 TFS。 Visual Studio,网络是最突出的。 在本文中,我将解释如何通过web实现上述描述的功能。 在这里找我其他的文件,这里是 Visual Studio。

背景

to是一个配置管理工具,它存储代码版本,以后移动到一个应用程序生命周期 management(ALM) 工具。 由于提供了额外的特性,它并不像市场上可以用的它的他工具那样有效。 因这里,我们需要将自定义功能开发到tmodel中,这是由for提供的API提供的。 为了提供对团队成员的灵活性,ccr可以通过各种平台操作,从而可以从任何设备访问。

是否需要限制在子进程打开时关闭工作项

TFS是一个ALM工具。用户故事。任务。问题。Bug。等等,可以登录到它。 我们可以应用经过验证的敏捷实践来管理应用程序的生命周期。 尽管本文描述的功能不是标准特性的一部分,但是我们打算开发一个。 创建这样的工具不仅可以让我们遵循经过验证的敏捷实践,而且可以方便开发者。经理。等等 等人跟踪工作。

定义

WIT

工作项类型

ALM

应用程序生命周期管理

wicc

工作项自定义控件

开发Web界面控件

注释:我们将开发一个需要安装到TFS服务器上的JQuery插件。

创建一个文件,并将一个TFS创建为 FF.WITEventHandler。 声明 TFS.WorkItemTracking.ControlsTFS.WorkItemTrackingTFS.Core 模块的依赖关系。

TFS.module("FujiFilm.WITEventHandler",
 [
 "TFS.WorkItemTracking.Controls",
 "TFS.WorkItemTracking",
 "TFS.Core" ]

创建一个构造函数并从TFS继承它。 WorkItemTracking.Controls.WorkItemControl

function () {// module contentvar WITOM = TFS.WorkItemTracking,
 WITCONTROLS = TFS.WorkItemTracking.Controls,
 delegate = TFS.Core.delegate;// Constructor for WITEventHandlerfunction WITEventHandler(container, options, workItemType)
{
 this.baseConstructor.call(this, container, options, workItemType);
}// WITEventHandler inherits from WorkItemControlWITEventHandler.inherit(WITCONTROLS.WorkItemControl, {
 _control: null,
 _status: null,
WITCONTROLS.registerWorkItemControl("WITEventHandler", WITEventHandler);
}

初始化控件用户界面。所有全局变量都在。

_init:function () {
 this._base();
 var oldStateValue;
 },

当工作项绑定到特定项目时更新控件数据。

invalidate: function (flushing) {
 },

清除控件数据。当控件需要将它的状态重置为" blank"( 例如当工作项窗体从特定工作项取消绑定时),框架调用这里方法。

 clear: function() {
 this._workItem= null;
},

将一个函数绑定到我们处理所有逻辑的工作项。

bind:function (workItem) {
}

在上述函数中,创建一个函数委托并将它的附加到上面绑定的工作项更改事件。

bind:function (workItem) {
 this._workItemChangeDelegate = function (sender, args) {
 }
 workItem.attachWorkItemChanged(this._workItemChangeDelegate);
 }

因此当工作项改变时,this._workItemChangeDelegate 会被激发。 这个方法包含我们需要实现的逻辑。

逻辑

逻辑很简单

  • 确定工作项是否已经更改
  • 如果已经更改,则检查它是否为状态控件
  • 如果状态控件及其更改为关闭
  • 检查子工作项是否打开
  • 如果子工作项打开,则
  • 向用户发出警告
  • 并恢复改变

标识单击的控件

如果仔细观察,在TFS工作项表单上生成的控件是动态控件。 我们需要一个机制来识别点击的控件。

所以为每个下拉列表创建一个事件。 ( 在本例中,我们只使用状态,所以我选择下拉列表。)

我们为上面突出显示的下拉按钮写一个事件。

$('.drop').bind('click', function () {
 if (this.id == "") 
 stateCtrl = $("#" + this.parentElement.id + "_txt")[0];
 else stateCtrl = $("#" + this.id + "_txt")[0];
});

单击按钮时,创建一个变量以加载控件的父级,这是实际的下拉控件。

将以前的值存储在名为

oldStateValue = workItem.fieldData[2];

现在确定你要检查哪个工作项目,Epic,用户故事,任务等等。

if (workItem.workItemType.name == "Epic")
 wiClosed = "Resolved";if (workItem.workItemType.name == "Task")
 wiClosed = "Closed";if (workItem.workItemType.name == "User Story")
 wiClosed = "Closed";

现在创建一个工作项更改的委托函数。

检查更改是否为'field-change'。

if (args.change === "field-change") 

一个字段更改后,将有多个自动字段更改,比如更改的日期,更改为 等等,更改状态字段。

for (var i in args.changedFields) {
 if (args.changedFields[i].fieldDefinition.name === "State" && stateCtrl.value === wiClosed) {

获取工作项的链接。 链接可能是孩子,父母,等等,我们只需要检查孩子。

var links = parentWorkItem.getLinks();for (var i in links) {
 var child = null;if (links[i].baseLinkType === "WorkItemLink" && links[i].getLinkTypeEnd().name == "Child") {

如果子进程可用,则检查子进程的状态。 如果子状态未关闭,则向用户显示警报并将状态值重置为前一个值。

state = child.fieldMap.STATE.getValue();if (state!= wiClosed) {
 alert("You must close all the open child work items to close this work item.");
 stateCtrl.value = oldStateValue;

这里我们只向用户显示一个基本的警报,但是对于开放子级的详细信息。 因此,如果至少找到一个打开的子进程,就中断循环。

最后的代码如下所示:

// Register this module as"FFFilm.WITEventHandler" and declare // dependencies on TFS.WorkItemTracking.Controls, TFS.WorkItemTracking and TFS.Core modulesTFS.module("FFFilm.WITEventHandler",
 [
 "TFS.WorkItemTracking.Controls",
 "TFS.WorkItemTracking",
 "TFS.Core" ],
 function () {
 // module contentvar WITOM = TFS.WorkItemTracking,
 WITCONTROLS = TFS.WorkItemTracking.Controls,
 delegate = TFS.Core.delegate;
 // Constructor for WITEventHandlerfunction WITEventHandler(container, options, workItemType) {
 this.baseConstructor.call(this, container, options, workItemType);
 }
 // WITEventHandler inherits from WorkItemControl WITEventHandler.inherit(WITCONTROLS.WorkItemControl, {
 _control: null,
 _status: null,
 // Initialize the control UI without data (in"blank" state).// Framework calls this method when the control needs to render its initial UI// Notes: // - The work item data is NOT available at this point// - Keep in mind that work item form is reused for multiple work items // by binding/unbinding the form to work item data _init: function () {
 this._base();
 var oldStateValue;
 var stateCtrl;
 var wiClosed;
 },
 // Update the control data// Framework calls this method when the control needs to update itself, such as when:// - work item form is bound to a specific work item// - underlying field value has changed due to rules or another control logic invalidate: function (flushing) {
 },
 // Clear the control data// Framework calls this method when the control needs to reset its state to"blank", such as when:// - work item form is unbound from a specific work item clear: function () {
 this._workItem = null;
 },
 bind: function (workItem) {
 this._base(workItem);
 $('.drop').bind('click', function () {
 if (this.id == "") 
 stateCtrl = $("#" + this.parentElement.id + "_txt")[0];
 else stateCtrl = $("#" + this.id + "_txt")[0];
 });
 oldStateValue = workItem.fieldData[2];
 if (workItem.workItemType.name == "Epic")
 wiClosed = "Resolved";
 if (workItem.workItemType.name == "Task")
 wiClosed = "Closed";
 if (workItem.workItemType.name == "User Story")
 wiClosed = "Closed";
 this._workItemChangeDelegate = function (sender, args) {
 if (args.change === "field-change") {
 // find a label field, with class workitemcontrol-label and// title contans helptext of args.changedFields[i].fieldDefinition// get that for value which is the state controlfor (var i in args.changedFields) {
 if (typeof stateCtrl!= 'undefined') {
 if (args.changedFields[i].fieldDefinition.name === "State" && stateCtrl.value === wiClosed) {
 var parentWorkItem = workItem;
 var links = parentWorkItem.getLinks();
 for (var i in links) {
 var child = null;
 var state = "";
 if (links[i].baseLinkType === "WorkItemLink" && links[i].getLinkTypeEnd().name == "Child") {
 parentWorkItem.store.beginGetWorkItem(links[i].getTargetId(), function (child, state) {
 state = child.fieldMap.STATE.getValue();
 // if state is not closed, then return trueif (state!= wiClosed) {
 // if the previous state is already the same, then do not display any thing to the user.if (stateCtrl.value!= oldStateValue) {
 alert("You must close all the open child work items to close this work item.");
 stateCtrl.value = oldStateValue;
 }
 }
 });
 break;
 }
 }
 }
 }
 }
 }
 }
 workItem.attachWorkItemChanged(this._workItemChangeDelegate);
 },
 unbind: function (workItem) {
 if (this._workItemChangeDelegate) {
 this._workItem.detachWorkItemChanged(this._workItemChangeDelegate);
 deletethis._workItemChangeDelegate;
 }
 }
 });
 // Register this module as a work item custom control called"WITEventHandler" WITCONTROLS.registerWorkItemControl("WITEventHandler", WITEventHandler);
 return {
 WITEventHandler: WITEventHandler
 };
 });

创建JQuery类并不足以使用以下功能。 我们需要一个清单文件来说明。

创建一个 manifest.xml 并将下面的内容复制到其中:

<WebAccessversion="11.0"><pluginname="Work Item Template Event Handler"vendor="FFFilm"moreinfo="http://www.FFmed.com"version="1.0.0"><modules><modulenamespace="FFFilm.WITEventHandler"kind="TFS.WorkItem.CustomControl"/></modules></plugin></WebAccess>

打包

在这种情况下,我们只需要服务器端部署。 用户需要对TFS的管理员权限才能完成这里任务。

在部署之前,我们需要打包我们创建的文件。

将JS文件命名为 FFFilm.WITEventHandler.min.js。 如果需要,可以缩小文件的大小。

复制文件的副本为 FujiFilm.WITEventHandler.debug.js。

压缩上三个文件,包括 Manifest.xml。 压缩前不要将它们放在文件夹中。 zip文件的NAME 不重要。

这将创建我们的包。

部署

打开 TFS web access。

转到 控制面板。单击扩展选项卡。 你应该会看到这里安装的所有插件。

单击 install install安装你现在创建的插件。 如果这里按钮不可见,则你没有所需的权限。

选择文件并指向我们创建的zip文件。 然后单击 ok install安装插件。

默认情况下,已经安装的插件将被禁用。 点击启用启用它。

现在导航到工作项并执行测试。

修改工作项类型

安装文件不够,因为我们尝试修改工作项,我们需要告诉工作项使用这个新控件。 为此,我们需要修改工作项模板。

为此,我们有多个第三方工具,其中TFS电动工具是突出的。

从 Visual Studio 导航到 tools-> 流程编辑器-> 工作项 Types-> 从服务器打开。

选择你喜欢的项目并从扩展中选择任务。

image004

image006

单击 new to添加新字段。

image008

填写以下详细信息:

image009

转到布局部分并添加控件。

image011

确保不向控件添加任何标签,因为我们不希望控件显示在 Visual Studio 中的工作项窗体上。

虽然我们构建的控件没有任何接口,添加标签将显示标签文本和默认文本框。

完成后,单击 save,将工作项保存到服务器。

使 Visual Studio 和 web access同时工作

要使 Visual Studio 和Web访问都正常工作,你不需要对工作项类型进行任何修改。 上所做的修改也足以满足 Visual Studio的需求。 确定控件 NAME MATCHES。

调试

在这种情况下调试很简单。 在 Chrome 中,按F12键打开开发人员工具窗口。

选择源选项卡,然后展开左侧的文件夹。 转到下面指定的文件夹,在下面你将找到你创建的 。js文件。 单击行号以设置断点,然后刷新浏览器以按断点。

扩展控件

本文中的代码和描述与任务工作项和状态控制的变化有关。 可以将这里扩展扩展到其他控件,修改。
这也可以通过只修改工作项类型来扩展到其他工作项类型。 它就像安装一次,如果对于许多其他工作项类型使用。
我们可以将这段代码作为基线来开发能够满足敏捷实践的多个控制。

面临的挑战

  • 在控件的更改事件上捕获
    编写了一个处理程序来捕获窗体上的任何事件。
  • 取消多个通知以显示子详细信息
    只显示一条通用消息。
  • 创建的处理程序应该被分离并从工作项中移除。 它的他工作项更改时,处理程序会继续并重复多个警告框,使用户感到烦恼。
    导航到 TFS.Controls.js,以准确标识如何分离处理程序。
  • 经常将程序集文件复制到指定位置。
    创建一个批处理文件,该文件复制 。dll,and pdb和。asp。文件

引用

in访问 2012中的工作项自定义控制开发- 部署

http://blogs.msdn.com/b/serkani/archive/2012/06/22/work-item-custom-control-development-in-tf-web-access-2012-deployment.aspx

PowerTools 2013

http://visualstudiogallery.msdn.microsoft.com/f017b10c-02b4-4d6d-9845-58a06545627f


WEB  Items  CLOS  Close  
相关文章