/// <summary> /// Maps a single work item into the document. /// </summary> /// <param name="node">The node for the work item to map.</param> /// <param name="workItemLayout">The work item layout that is to be used.</param> /// <param name="bookmarkNamingFunction">A function to return the bookmark to use for the given work item id.</param> /// <param name="workItemsCustomXMLPart">The custom XML part that contains the work item data.</param> /// <param name="insertionPoint">The relative location where the work item is to be mapped.</param> /// <param name="relativeWorkItemId">If inserting relative to a work item, the id of the work item relative to which the insertion is to be done, otherwise ignored.</param> private void MapWorkItemIntoDocument(WorkItemTreeNode node, IWorkItemLayout workItemLayout, Func <int, string> bookmarkNamingFunction, CustomXMLPart workItemsCustomXMLPart, InsertionPoint insertionPoint, int relativeWorkItemId) { BuildingBlock buildingBlock = workItemLayout.ChooseBuildingBlock(node); IEnumerable <ContentControl> contentControls = null; switch (insertionPoint) { case InsertionPoint.CurrentLocation: { contentControls = this.wordDocument.InsertBuildingBlock(buildingBlock, bookmarkNamingFunction(node.WorkItem.Id)); break; } case InsertionPoint.BeforeBookmark: { contentControls = this.wordDocument.InsertBuildingBlockBeforeBookmark(buildingBlock, bookmarkNamingFunction(node.WorkItem.Id), bookmarkNamingFunction(relativeWorkItemId)); break; } case InsertionPoint.AfterBookmark: { contentControls = this.wordDocument.InsertBuildingBlockAfterBookmark(buildingBlock, bookmarkNamingFunction(node.WorkItem.Id), bookmarkNamingFunction(relativeWorkItemId)); break; } } foreach (ContentControl c in contentControls) { if (Utilities.IsValidTag(c.Tag) && node.WorkItem.FieldReferenceNames.Contains(c.Tag)) { if (c.Type == WdContentControlType.wdContentControlText || c.Type == WdContentControlType.wdContentControlDate) { string xpath = string.Format(CultureInfo.InvariantCulture, "/wi:WorkItems/wi:WorkItem[wi:Field[@name='{0}']={1}]/wi:Field[@name='{2}']", Constants.SystemIdFieldReferenceName, node.WorkItem.Id, c.Tag); this.wordDocument.MapContentControl(c, xpath, "xmlns:wi='" + Constants.WorkItemNamespace + "'", workItemsCustomXMLPart); } else if (c.Type == WdContentControlType.wdContentControlRichText) { this.wordDocument.PopulateRichTextContentControlWithHtml(c, node.WorkItem[c.Tag].ToString()); c.Tag = string.Concat(c.Tag, "-", node.WorkItem.Id.ToString(CultureInfo.InvariantCulture)); } } } }
/// <summary> /// Adds new work items when there were none before to place the new ones relative to. /// </summary> /// <param name="afterIds">The new list of work item ids.</param> /// <param name="nodes">The work item tree nodes for the query being processed.</param> /// <param name="layout">The layout to apply to the newly added work items.</param> /// <param name="customXMLPart">The custom XML part that contains the work item data.</param> /// <param name="bookmarkNamingFunction">A function to return the bookmark to use for the given work item, parameter is work item id.</param> private void AddNewWorkItemsWhenNoneBefore(int[] afterIds, WorkItemTreeNode[] nodes, IWorkItemLayout layout, CustomXMLPart customXMLPart, Func <int, string> bookmarkNamingFunction) { for (int i = 0; i < afterIds.Length; i++) { this.MapWorkItemIntoDocument(nodes[i], layout, (int id) => bookmarkNamingFunction(id), customXMLPart, InsertionPoint.CurrentLocation, 0); } }
/// <summary> /// Adds new work items after the last existing one. /// </summary> /// <param name="newIds">The new work items ids.</param> /// <param name="succeeds">The id of the last existing work item.</param> /// <param name="nodes">The work item tree nodes for the query being processed.</param> /// <param name="workItemLayout">The work item layout to apply to the newly added work items.</param> /// <param name="customXMLPart">The custom XML part that contains the work item data.</param> /// <param name="bookmarkNamingFunction">A function to return the bookmark to use for the given work item, parameter is work item id.</param> private void AddAllNewWorkItemsAfterLastExisting(int[] newIds, int succeeds, WorkItemTreeNode[] nodes, IWorkItemLayout workItemLayout, CustomXMLPart customXMLPart, Func <int, string> bookmarkNamingFunction) { int lastId = succeeds; for (int i = 0; i < newIds.Length; i++) { this.MapWorkItemIntoDocument(nodes.Where(n => n.WorkItem.Id == newIds[i]).Single(), workItemLayout, (int id) => bookmarkNamingFunction(id), customXMLPart, InsertionPoint.AfterBookmark, lastId); lastId = newIds[i]; } }
/// <summary> /// Adds new work items that come after any existing ones, stopping when the first existing one is found. /// </summary> /// <param name="newIds">The new work item ids.</param> /// <param name="beforeIds">The old list of work item ids.</param> /// <param name="afterIds">The new list of work item ids.</param> /// <param name="nodes">The work item tree nodes for the query being processed.</param> /// <param name="workItemLayout">The work item layout to apply to the newly added work items.</param> /// <param name="customXMLPart">The custom XML part that contains the work item data.</param> /// <param name="bookmarkNamingFunction">A function to return the bookmark to use for the given work item, parameter is work item id.</param> private void AddNewWorkItemsAfterExisting(int[] newIds, int[] beforeIds, int[] afterIds, WorkItemTreeNode[] nodes, IWorkItemLayout workItemLayout, CustomXMLPart customXMLPart, Func <int, string> bookmarkNamingFunction) { for (int i = afterIds.Length - 1; i >= 0; i--) { if (newIds.Contains(afterIds[i])) { int succeeds = FindLastExistingWorkItemId(afterIds.Take(i).ToArray(), beforeIds); if (succeeds >= 0) { this.MapWorkItemIntoDocument(nodes[i], workItemLayout, (int id) => bookmarkNamingFunction(id), customXMLPart, InsertionPoint.AfterBookmark, succeeds); } } else { break; } } }
/// <summary> /// Adds new work items that come before any existing ones. /// </summary> /// <param name="newIds">The new work item ids.</param> /// <param name="beforeIds">The old list of work item ids.</param> /// <param name="afterIds">The new list of work item ids.</param> /// <param name="nodes">The work item tree nodes for the query being processed.</param> /// <param name="workItemLayout">The work item layout to apply to the newly added work items.</param> /// <param name="customXMLPart">The custom XML part that contains the work item data.</param> /// <param name="bookmarkNamingFunction">A function to return the bookmark to use for the given work item, parameter is work item id.</param> private void AddNewWorkItemsBeforeExisting(int[] newIds, int[] beforeIds, int[] afterIds, WorkItemTreeNode[] nodes, IWorkItemLayout workItemLayout, CustomXMLPart customXMLPart, Func <int, string> bookmarkNamingFunction) { for (int i = 0; i < afterIds.Length; i++) { if (newIds.Contains(afterIds[i])) { int precedes = FindFirstExistingWorkItemId(afterIds.Skip(i + 1).ToArray(), beforeIds); if (precedes >= 0) { this.MapWorkItemIntoDocument(nodes[i], workItemLayout, (int id) => bookmarkNamingFunction(id), customXMLPart, InsertionPoint.BeforeBookmark, precedes); } } } }
/// <summary> /// Adds the new work items that were not in the last run of a query. /// </summary> /// <remarks> /// If the current work items have been manually sorted then new items are just added to the end, otherwise they are added in the new sort order. /// </remarks> /// <param name="queryWorkItemsBefore">The query to work item association data from before the refresh.</param> /// <param name="queryWorkItemsAfter">The query to work item association data as a result of the refresh.</param> /// <param name="workItemLayout">The work item layout associated with the query.</param> /// <param name="customXMLPart">The custom XML part that contains the work item data.</param> /// <param name="bookmarkNamingFunction">A function to return the bookmark to use for the given work item, given the work item id.</param> /// <param name="isManuallySorted">Indicates if the current work items are manually sorted.</param> /// <param name="cancellationToken">Used to cancel the refresh.</param> private void AddNewWorkItems(QueryWorkItems queryWorkItemsBefore, QueryWorkItems queryWorkItemsAfter, IWorkItemLayout workItemLayout, CustomXMLPart customXMLPart, Func <int, string> bookmarkNamingFunction, bool isManuallySorted, CancellationToken cancellationToken) { this.logger.Log(TraceEventType.Verbose, "Adding new work items"); cancellationToken.ThrowIfCancellationRequested(); int[] newIds = queryWorkItemsAfter.WorkItemIds.Except(queryWorkItemsBefore.WorkItemIds).ToArray(); if (newIds.Length > 0) { // We have some new work items that were not present last time the query was executed. int[] beforeIds = queryWorkItemsBefore.WorkItemIds.ToArray(); int[] afterIds = queryWorkItemsAfter.WorkItemIds.ToArray(); WorkItemTreeNode[] nodes = queryWorkItemsAfter.WorkItemTreeNodes.ToArray(); Debug.Assert(afterIds.Length == nodes.Length, "Number of after nodes must match number of after ids."); if (isManuallySorted) { this.AddAllNewWorkItemsAfterLastExisting(newIds, this.GetLastPhysicalWorkItem(beforeIds, bookmarkNamingFunction), nodes, workItemLayout, customXMLPart, bookmarkNamingFunction); } else if (afterIds.Intersect(beforeIds).Count() > 0) { this.AddNewWorkItemsBeforeExisting(newIds, beforeIds, afterIds, nodes, workItemLayout, customXMLPart, (int id) => bookmarkNamingFunction(id)); this.AddNewWorkItemsAfterExisting(newIds, beforeIds, afterIds, nodes, workItemLayout, customXMLPart, (int id) => bookmarkNamingFunction(id)); } else { this.AddNewWorkItemsWhenNoneBefore(afterIds, nodes, workItemLayout, customXMLPart, (int id) => bookmarkNamingFunction(id)); } } }