【问题标题】:How can I track who entered a comment on a TFS Work Item如何跟踪谁在 TFS 工作项上输入了评论
【发布时间】:2010-10-26 00:06:57
【问题描述】:

在其他错误跟踪系统中处理问题时,有一些方法可以为清楚地标有作者用户名和输入日期的项目添加注释。我正在 TFS 的工作项中寻找类似的功能。 有这样的功能吗?

我们目前使用的系统允许我们点击热键将当前时间和用户名粘贴到多行文本字段中。所有用户都知道将该信息粘贴到他们键入的内容上方。虽然这是手动的,但它是可以接受且容易的。例如:

2009 年 5 月 1 日凌晨 1:20:00 - AManagr- 推迟到下一个版本,这就是为什么... 2009 年 4 月 24 日凌晨 1:20:00 - ADev - QA 机器的 XYZ Gizmo 组件已过时。这是发生这种情况的复杂方式......等等等等......这很难解决。 2009 年 4 月 22 日上午 1:20:00 - QAGuy - 我无法将报告保存为 PDF 文件。

我使用过的其他工具(可能是 Mantis?)内置了“注释”功能。所以我不能忘记将我的名字放在 cmets 上,或者知道新注释是放在字段的顶部还是底部等等……

手动输入您的姓名和日期/时间不是一个(好的)选项。但是,按一个键或工具栏按钮就可以了。

我不是在寻求关于将这样的长格式“笔记”分解为多个特定单独字段的建议。另外,我知道工作项的历史选项卡,但这还不够。谁写了什么,什么时候写的,需要明确,与正文保持一致。

更新

想象几个团队成员正在研究一个问题。它们都向工作项添加信息,每个都将更多文本附加到同一字段。您如何轻松知道谁添加了哪个部分?

历史记录为每个用户的更改显示一行,甚至显示该字段的更改。但那是在另一个屏幕上,很难在精神上解析它显示的数据。

他们可以“签署”文本的每一部分 - 但如果没有工具的帮助,这会很痛苦。

也许 Stackoverflows cmets 功能也是一个很好的例子。

【问题讨论】:

    标签: tfs tfs-workitem


    【解决方案1】:

    是的,你可以。 TFS 工作项是可定制的。在这个版本中没有我想要的那么多,但你可以做你想做的。

    让我们使用以下字段定义来尝试一下。 Notes Date 和 Notes Author 是只读的,并从系统中获取它们的默认值。 Notes 字段是 HTML,您可以在其中放置任何您想要的内容。您可以在TFS Process Editor 中执行此操作。

     <FIELD reportable="dimension" type="DateTime" name="Notes Date" refname="System.VSTS.Notes.Date">
        <DEFAULT from="clock" />
        <READONLY not="[Global]\Team Foundation Administrators" />
      </FIELD>
      <FIELD reportable="dimension" type="String" name="Notes Author" refname="System.VSTS.Notes.Author">
        <DEFAULT from="currentuser" />
        <READONLY not="[Global]\Team Foundation Administrators" />
      </FIELD>
      <FIELD type="HTML" name="Notes" refname="System.VSTS.Notes" />
    </FIELDS>
    

    当然,您仍然需要在表单中添加控件。

    您可以尝试的另一件事是仅保留 Notes 字段并注册 WorkItemChanged 事件并编写 Web 服务以使用 Date 和 Author 更新 notes 字段。 Changed BY 和 Changed Date 字段将为您提供此信息。您可以在 Brian A. Randell 的这篇文章中了解可用的事件以及如何订阅它们 - Team Foundation System Event Service

    [WebService(Namespace = "http://mynamespace.com/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    public class UpdateWorkItem : System.Web.Services.WebService
    {
        private static TeamFoundationServer _Tfs;
        private static WorkItemStore _WorkItemStore;
    
        private static List<WorkItem> _ChangedWorkItems = new List<WorkItem>();
    
        [SoapDocumentMethod(Action = "http://schemas.microsoft.com/TeamFoundation/2005/06/Services/Notification/03/Notify", RequestNamespace = "http://schemas.microsoft.com/TeamFoundation/2005/06/Services/Notification/03")]
        [WebMethod]
        public void Notify(string eventXml, string tfsIdentityXml)
        {
    
            EventLog.WriteEntry("TFS Services", "Log Started: Notify Webmethod");
    
    
            // Load the recieved XML into a XMLDocument
            XmlDocument eventXmlDoc = new XmlDocument();
            eventXmlDoc.LoadXml(eventXml);
            XmlElement eventData = eventXmlDoc.DocumentElement;
    
            // Validate event data
            if (eventData != null)
            {
                // Get Work Item id from event data
                int id = GetWorkItemId(eventData);
    
                //EventLog.WriteEntry("TFS Services", String.Format("eventXmlDoc {0}", eventXmlDoc.InnerXml));
                EventLog.WriteEntry("TFS Services", String.Format("Got Id {0}", id));
                string changedby = GetWorkItemChangedBy(eventData);
                EventLog.WriteEntry("TFS Services", String.Format("Got changedby {0}", changedby));
                if (changedby != "TFSSERVICE")
                {
                    //Add a 15 second delay in order to make sure all workitems are saved first before starting to update them
                    Thread.Sleep(15000);
                    EventLog.WriteEntry("TFS Services", "Calling UpdateWorkItemInternal");
                    UpdateWorkItemInternal(id);
                }
            }
        }
    
        private int GetWorkItemId(XmlElement eventData)
        {
            return Convert.ToInt32(eventData.SelectSingleNode("CoreFields/IntegerFields/Field[ReferenceName='System.Id']/NewValue").InnerText);
        }
    
        private string GetWorkItemChangedBy(XmlElement eventData)
        {
            return Convert.ToString(eventData.SelectSingleNode("CoreFields/StringFields/Field[ReferenceName='System.ChangedBy']/NewValue").InnerText);
        }
    
        private static void UpdateWorkItemInternal(int id)
        {
            //Connect To TFS Server 
            EventLog.WriteEntry("TFS Services", string.Format("Updating Work Item {0}", id));
            _Tfs = TeamFoundationServerFactory.GetServer("TeamServer");
    
            _WorkItemStore = (WorkItemStore)_Tfs.GetService(typeof(WorkItemStore));
            WorkItem workItem = _WorkItemStore.GetWorkItem(id);
    
            switch ((string)workItem.Fields["System.WorkItemType"].Value)
            {
                case "Bug":
                    UpdateNotes(workItem);
                    break;
                default:
                    break;
            }
    
            foreach (WorkItem item in _ChangedWorkItems)
            {
                if (item.IsDirty)
                {
                    foreach (Field field in item.Fields)
                    {
                        if (!field.IsValid)
                        {
                            Console.Write("Not valid");
                        }
                    }
                    EventLog.WriteEntry("TFS Services", string.Format("Saving WorkItem: {0}", item.Id));
                    try
                    {
                        item.Save();
                    }
                    catch (Exception ex)
                    {
                    }
                }
            }
    
            _ChangedWorkItems.Clear();
        }
    
        private static void UpdateNotes(WorkItem workItem)
        {
           Field notes = workitem.Fields["System.VSTS.Notes"];
           if (notes != null)
           {
             notes = string.Format("{0} - {1}", workItem.ChangedDate, workItem.ChangedBy);
           } 
    
           if (workItem.IsDirty)
           {
               if (!_ChangedWorkItems.Contains(workItem))
               {
                   _ChangedWorkItems.Add(workItem);
               }
           }
        }
     }
    

    从我现有的代码中复制和粘贴一些内容既快速又肮脏,因此请仔细检查以确保我没有引入拼写错误。

    【讨论】:

    • 您的第一个建议只显示最后一个更新该备注字段的人?这很有用——但不完全是。我认为第二个例子可能会做我想做的事。我只想在更改注释时附加此信息 - 但我认为这是一个简单的添加。我想要一个可以交互应用的解决方案(与保存后 X 秒相比)
    【解决方案2】:

    我对“历史记录”标签的问题感到好奇?它在灰色条中包含日期/时间和用户名,并在其下方立即发表评论。

    如果他们没有发表评论,但对工作项进行了其他更改,则会有一个名为 Show Changed Fields 的折叠标题。不过评论总是可见的。

    更新

    您可以创建自定义工作项控件,以提供您自己的工作项历史视图。

    http://msdn.microsoft.com/en-us/library/bb286959.aspx

    另见TFS Work Item Tracking Custom Controls

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-03-25
      • 2012-06-20
      • 2016-09-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多