Lotus Notes 到 Team Foundation Server
电子邮件到工作项。
Microsoft Team Foundation Server (TFS) 在许多组织中用于缺陷跟踪。客户可以通过各种渠道(包括电话和电子邮件)记录缺陷。有一些插件可以将 Microsoft Outlook 的电子邮件转换为 TFS 中的工作项。但是,目前还没有插件可以实现 Lotus Notes 电子邮件服务与 TFS 的集成。本文将介绍 Lotus Notes 与 TFS 之间的集成问题。
引言
客户帮助台会收到大量工单,这些工单将被分类并管理在 TFS 或 HP Application Lifecycle Manager (ALM) 等缺陷管理工具中。大多数情况下,工单是通过电子邮件接收的,支持团队会根据电子邮件内容创建缺陷或工单,并将其分配给特定的/相关的组。对于 Outlook,有一个插件可以与 TFS 集成。这有助于支持团队通过点击 Outlook 中的几个按钮在 TFS 中创建缺陷!Lotus Notes 是另一个广泛使用的电子邮件客户端,目前还没有插件可以实现此功能。这需要手动创建缺陷并将电子邮件内容复制到缺陷中,等等。
本文旨在为 Lotus Notes 到 TFS 工作项的集成过程提供解决方案。我们采用了两阶段方法 - 第一阶段是在 Lotus Notes 外部提供一个独立的 GUI,用于列出所有电子邮件并在 TFS 中创建不同的工作项;在第二阶段,我们将研究创建一个 Lotus Notes 的 Java 插件,该插件将调用 .NET 服务将详细信息传递给 TFS。作为微软技术布道者,我将带您完成第一阶段,并仅解释第二阶段的方法。Java 社区中的任何人都肯定可以采用第二阶段的方法,开发一个 Lotus Notes 插件,从而实现 Lotus Notes 与 TFS 的无缝集成。
第一阶段:WPF 应用程序
在第一阶段,我们将创建一个独立的 Windows 应用程序,该应用程序将从 Lotus Notes 中提取电子邮件信息并将其写入 TFS 作为工作项。下图显示了流程。
假设:解决方案将部署的系统已安装并配置了 Lotus Notes
创建新的 WPF 应用程序
打开 Visual Studio 并创建一个新的 WPF 应用程序。让我们从集成 Lotus Notes 开始开发。Lotus Notes 提供基于 COM 的 .NET 程序集,用于访问电子邮件。这些程序集将安装在已安装并配置了 Lotus Notes 的客户端系统中。
添加 Lotus Domino Objects 引用
添加 Lotus Domino Objects 引用
-
在解决方案资源管理器中右键单击项目名称,然后选择“添加”->“引用”。
-
在“引用管理器”窗口中选择“COM”部分,然后选择“Lotus Domino Objects”程序集。
-
单击“确定”将引用添加到项目中。
添加 TFS 引用
将以下 TFS 程序集引用添加到项目中。这些程序集可以在以下位置找到:
C:\Program Files\Microsoft Visual Studio 12.0\Common7\IDE\Reference Assemblies\v2.0\
-
Microsoft.TeamFoundation.Common.dll
-
Microsoft.TeamFoundation.Client.dll
-
Microsoft.TeamFoundation.WorkItemTracking.Client.dll
从 Lotus Notes 检索收件箱
-
检索 Lotus Notes 中的收件箱详细信息。
-
使用 Lotus Notes 数据库名称和当前用户密码连接到 Lotus Notes。
-
提供当前用户的 Lotus Notes 密码以检索电子邮件文件。
Lotus Notes 数据库设置
Lotus Notes 数据库详细信息可从 Lotus Notes 配置中获取。
-
打开 Lotus Notes,导航到“文件”->“首选项”->“位置”。
-
选择当前位置,然后单击“编辑”。此时将出现“编辑位置”对话框。
-
单击“邮件”选项卡,然后检查“邮件文件”选项的配置值。这将指示 Lotus Notes 的数据库名称。
设计 WPF 屏幕
设计一个 UI 来捕获用户独有的数据库名称和密码。让我们定义一个简单的 UI,其中包含两个文本框用于接受详细信息,以及一个“获取电子邮件”按钮,用于调用根据用户输入检索收件箱详细信息的函数。
现在,向主窗口添加一个数据网格,以显示收件箱中的电子邮件。我们定义用于显示字段的列:“发件人”或发送者、电子邮件日期和主题。
以下 XAML 代码片段定义了一个名为“mailGrid”的简单数据网格。
<DataGrid HorizontalAlignment="Left" Height="429" Margin="21,183,0,0" VerticalAlignment="Top" AutoGenerateColumns="False" Width="911" x:Name="mailGrid" ItemsSource="{Binding}" > <DataGrid.Columns> <DataGridTextColumn Header="From" Binding="{Binding From}" Width="180" /> <DataGridTextColumn Header="Date" Binding="{Binding Date}" Width="100" /> <DataGridTextColumn Header="Subject" Binding="{Binding Subject}" Width="*"/> </DataGrid.Columns> </DataGrid>
检索 Notes 收件箱
我们使用 Domino COM 程序集中的各种类和构造来检索 Lotus Notes 中的电子邮件。NoteSession 是 Domino 程序集中的主要类。创建 NoteSession 类的对象,并使用密码进行初始化。
_lotesNotesSession = new NotesSession(); _lotesNotesSession.Initialize(pwd.Password);
调用 GetDatabase 方法,并传入数据库名称。Lotus Notes 的数据库对象定义了不同的视图,如收件箱、已发送、发件箱等。获取收件箱视图以检索邮件。
_localDatabase = _lotesNotesSession.GetDatabase("", dbName.Text, false); _mailView = _localDatabase.GetView("($Inbox)");
GetDatabase 方法接受三个参数。
-
服务器名称:指定服务器名称,当您需要直接从服务器获取电子邮件时。
-
文件名:指定 Lotus Notes 客户端的数据库名称,其中电子邮件将从客户端可用。
-
失败时创建:布尔值
我们使用 Lotus Notes 本地实例的数据库名称来检索与本地 Lotus Notes 数据库同步的电子邮件。
从邮件视图获取所有条目,并从每个条目中检索 NoteDocument。
NotesViewEntryCollection notesViewCollection = _mailView.AllEntries; --------------------- NotesViewEntry viewEntry = notesViewCollection.GetNthEntry(rowCount); NotesDocument document = viewEntry.Document;
从文档中检索 NoteItem 集合,它代表电子邮件组件。
object documentItems = document.Items; Array itemArray1 = (System.Array)documentItems; --------------------- NotesItem notesItem = (NotesItem)itemArray1.GetValue(itemCount);
从 NotesItem 检索数据
if (notesItem.Name == "INetFrom") { dr["From"] = notesItem.Text; } if (notesItem.Name == "PostedDate") dr["Date"] = notesItem.Text; if (notesItem.Name == "Subject") dr["Subject"] = notesItem.Text; if (notesItem.Name == "Body") dr["Body"] = notesItem.Text;
以下是从本地 Lotus Notes 数据库检索电子邮件数据的完整代码。
private void btnGetMails_Click(object sender, RoutedEventArgs e) { List<string> senders = new List<string>(); dtmails.Rows.Clear(); _lotesNotesSession = new NotesSession(); _lotesNotesSession.Initialize(pwd.Password); _localDatabase = _lotesNotesSession.GetDatabase("", dbName.Text, false); _mailView = _localDatabase.GetView("($Inbox)"); NotesViewEntryCollection notesViewCollection = _mailView.AllEntries; for (int rowCount = 1; rowCount <= notesViewCollection.Count; rowCount++) { NotesViewEntry viewEntry = notesViewCollection.GetNthEntry(rowCount); NotesDocument document = viewEntry.Document; object documentItems = document.Items; Array itemArray1 = (System.Array)documentItems; DataRow dr = dtmails.NewRow(); for (int itemCount = 0; itemCount < itemArray1.Length; itemCount++) { NotesItem notesItem = (NotesItem)itemArray1.GetValue(itemCount); if (notesItem.Text != null) { if (notesItem.Name == "INetFrom") { dr["From"] = notesItem.Text; if (!senders.Contains(notesItem.Text)) senders.Add(notesItem.Text); } if (notesItem.Name == "PostedDate") dr["Date"] = notesItem.Text; if (notesItem.Name == "Subject") dr["Subject"] = notesItem.Text; if (notesItem.Name == "Body") dr["Body"] = notesItem.Text; } } if (!string.IsNullOrEmpty(dr["From"].ToString())) dtmails.Rows.Add(dr); } mailGrid.DataContext = dtmails; senders.Sort(); senderName.DataContext = senders; }
让我们对应用程序进行一些修改,以便在单独的窗口中显示整个邮件正文。定义另一个窗口,其中包含一个关闭按钮,用于显示邮件正文的详细信息。
<Grid> <ScrollViewer> <TextBlock x:Name="mailData" Padding="20,0,0,0"> </TextBlock> </ScrollViewer> <Button Content="Close" HorizontalAlignment="Left" Height="24" Margin="657,536,0,0" VerticalAlignment="Top" Width="90" x:Name="btnClose" Click="btnClose_Click"/> </Grid>
当窗口加载时,将 mailData 文本块设置为来自 Utilities 类的。Utilities 类是一个静态类,用于在两个窗口之间传递数据以及定义静态方法。Utilities 类的 MailBody 属性将从主窗口设置。
private void Window_Loaded(object sender, RoutedEventArgs e) { mailData.Text = Utilities.MailBody; }
现在,让我们对主窗口进行一些更改,以调用带有选定电子邮件详细信息的新窗口。向数据网格添加一个模板列和一个按钮。
<DataGridTemplateColumn Width="60" Header="View"> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <Button Click="ShowDetails">Details</Button> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn>
在主窗口的代码隐藏中定义 ShowDetails 方法。ShowDetails 将 Utilities 类的 MailBody 属性设置为选定电子邮件的正文,并将 MailDetails 窗口作为对话框调用。
void ShowDetails(object sender, RoutedEventArgs e) { if(mailGrid.SelectedItems.Count>0) { System.Data.DataRowView dataView = (System.Data.DataRowView)mailGrid.SelectedItems[0]; DataRow data = dataView.Row; Utilities.MailBody = data["Body"].ToString(); MailDetails obj = new MailDetails(); obj.ShowDialog(); } }
运行项目,并指定 Lotus Notes 的数据库和密码以检索电子邮件详细信息。以下屏幕显示了带有加载的收件箱内容的主窗口。
正如您所见,除了我们讨论的内容之外,我们在收件箱视图的顶部添加了一些过滤器,用于根据发件人和日期过滤记录或搜索记录。过滤器的实现将在以下代码片段中进行说明。
private void btnFilter_Click(object sender, RoutedEventArgs e) { string query = string.Empty; if (senderName.SelectedIndex > -1) query += " From ='" + senderName.SelectedValue.ToString() + "'"; if(_operator.SelectedIndex>-1) { if (senderName.SelectedIndex > -1) query += " AND "; query += " Date " + _operator.Text + " '" + sentDate.SelectedDate.ToString() + "'"; } if(!string.IsNullOrEmpty(query)) { var filteredData = dtmails.Select(query); mailGrid.DataContext = null; if(filteredData.Count()>0) mailGrid.DataContext = filteredData.CopyToDataTable(); } }
在 TFS 中创建工作项
将从 Lotus Notes 中检索电子邮件,并在应用程序中显示,并提供搜索选项。现在,让我们连接到 TFS 项目,并根据应用程序中选定的电子邮件创建工作项。
假设:- 用户应具有 TFS 访问权限。
设计 WPF 屏幕
设计 UI 以捕获用于连接的 TFS 服务器信息。一旦用户指定了 TFS 服务器详细信息,将显示关联的项目集合、项目和工作项供用户选择。
用户应提供 TFS 服务器详细信息,然后单击“重新加载”按钮以加载项目集合详细信息。当项目集合选择发生更改时,系统将查询 TFS 以获取相应的项目信息。根据项目选择,将填充工作项名称。
TFS 的实用工具方法
定义实用工具方法以从 TFS 检索项目集合详细信息。GetProjectCollection 方法将连接到指定的 TFS 服务器,并检索项目集合详细信息。这将获取用户有权访问的项目集合。
通过将 TFS 服务器详细信息传递给 TfsConfigurationServerFactory 的 GetConfigurationServer 方法,获取 TfsConfigurationServer 的实例。获取 TfsConfigurationServer 的团队项目集合目录。从目录中检索团队项目集合名称。
public static List<string> GetProjectCollection(string tfsServerName) { List<string> projCollection = new List<string>(); Uri tfsUri = new Uri(tfsServerName); TfsConfigurationServer configurationServer = TfsConfigurationServerFactory.GetConfigurationServer(tfsUri); configurationServer.EnsureAuthenticated(); // Get the catalog of team project collections ReadOnlyCollection<CatalogNode> collectionNodes = configurationServer.CatalogNode.QueryChildren( new[] { CatalogResourceTypes.ProjectCollection }, false, CatalogQueryOptions.None); // List the team project collections foreach (CatalogNode collectionNode in collectionNodes) { // Use the InstanceId property to get the team project collection Guid collectionId = new Guid(collectionNode.Resource.Properties["InstanceId"]); TfsTeamProjectCollection teamProjectCollection = configurationServer.GetTeamProjectCollection(collectionId); string colName = teamProjectCollection.Name; if (colName.IndexOf("\\") > -1) colName = colName.Substring(colName.IndexOf("\\") + 1); projCollection.Add(colName); } return projCollection; }
现在,定义另一个实用工具方法以检索指定团队项目下的团队项目列表。将项目集合 URI 传递给 TfsTeamProjectCollectionFactory 的 GetTeamProjectCollection 方法。从团队项目集合对象中检索团队项目目录。
public static List<string> GetProjects(string tfsServerName, string projectCollName) { List<string> projects = new List<string>(); Uri collectionUri = new Uri(tfsServerName + "\\" + projectCollName); var TeamProjectCollection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(collectionUri); ReadOnlyCollection<CatalogNode> projectNodes = TeamProjectCollection.CatalogNode.QueryChildren( new[] { CatalogResourceTypes.TeamProject }, false, CatalogQueryOptions.None); // List the team projects in the collection foreach (CatalogNode projectNode in projectNodes) { projects.Add(projectNode.Resource.DisplayName); } return projects; }
定义另一个实用工具方法以检索所选团队项目下的工作项。通过传递项目集合 URI 来获取团队项目集合。从团队项目集合对象中,获取 WorkItemStore 服务,该服务又包含指定团队项目集合下的项目。从项目中检索工作项类型。
public static List<string> GetWorkItemTypes(string projectName, string tfsServerName, string projectCollName) { List<string> workItems = new List<string>(); Uri collectionUri = new Uri(tfsServerName + "\\" + projectCollName); TfsTeamProjectCollection tpc = new TfsTeamProjectCollection(collectionUri); WorkItemStore workItemStore = tpc.GetService<WorkItemStore>(); Project teamProject = workItemStore.Projects[projectName]; var workItemTypes = teamProject.WorkItemTypes; foreach (WorkItemType wt in workItemTypes) { workItems.Add(wt.Name); } return workItems; }
将 TFS 详细信息绑定到 WPF 控件
绑定项目集合下拉列表
projectCollection.DataContext = Utilities.GetProjectCollection(tfsServer.Text);
绑定项目下拉列表。
projects.DataContext = Utilities.GetProjects(tfsServer.Text, projectCollection.SelectedValue.ToString());
在项目更改时绑定工作项详细信息
workItems.DataContext = Utilities.GetWorkItemTypes(projects.SelectedValue.ToString(), tfsServer.Text, projectCollection.SelectedValue.ToString());
运行并验证应用程序
运行应用程序,并提供 Lotus Notes 数据库名称和密码。通过单击“获取电子邮件”按钮加载收件箱。通过按住 Shift 键选择一个或多个电子邮件,以在 TFS 中创建工作项。
指定 TFS 服务器名称,然后单击“重新加载”按钮以加载项目集合详细信息。选择一个团队项目集合,这将加载所选项目集合下的项目。从“项目”下拉列表中选择一个项目,以加载相应的工作项类型。
选择要创建的工作项类型,然后单击“创建工作项”按钮,在 TFS 中所选项目下创建工作项。
完成工作项创建后,将显示一个弹出窗口,显示创建的工作项数量。
在 TFS 中验证新创建的工作项,以确认从 Lotus Notes 电子邮件创建工作项已完成。
第二阶段:Lotus Notes 插件
上述解决方案可以修改为 .NET 服务,并支持不同的数据格式,如 JSON、XML 和 SOAP。基于 Java 的 Lotus Notes 插件可以调用该服务,将电子邮件详细信息直接推送到 TFS。下图显示了 Lotus Notes 与 TFS 的最终预期集成。
社区中的 Java 专家可以考虑实现此解决方案的第二阶段,以实现 Lotus Note 与 TFS 的无缝集成。