Beispiel #1
0
 /// <summary>
 /// Initializes a new instance of the <see cref="TeamFoundationServerInfo"/> class.
 /// </summary>
 /// <param name="name">The name of the Team Foundation Server.</param>
 /// <param name="uri">The URI of the Team Foundation Server.</param>
 /// <param name="majorVersion">The major version of the Team Foundation Server.</param>
 /// <param name="displayVersion">The display version of the Team Foundation Server.</param>
 /// <param name="shortDisplayVersion">The short display version of the Team Foundation Server.</param>
 public TeamFoundationServerInfo(string name, Uri uri, TfsMajorVersion majorVersion, string displayVersion, string shortDisplayVersion)
 {
     this.Name                = name;
     this.Uri                 = uri;
     this.MajorVersion        = majorVersion;
     this.DisplayVersion      = displayVersion;
     this.ShortDisplayVersion = shortDisplayVersion;
 }
 public IList <string> GetObjectTokens(TfsTeamProjectCollection tpc, TfsMajorVersion tfsVersion, string teamProjectName, string teamProjectUri)
 {
     return(this.objectTokenFactory(tpc, tfsVersion, teamProjectName, teamProjectUri));
 }
        public static void Apply(ApplicationTask task, TfsTeamProjectCollection tfs, TfsMajorVersion tfsVersion, string teamProjectName, SecurityGroupChange securityGroup)
        {
            var ims               = tfs.GetService <IIdentityManagementService>();
            var css               = tfs.GetService <ICommonStructureService>();
            var teamProject       = css.GetProjectFromName(teamProjectName);
            var groupIdentityName = string.Format(CultureInfo.InvariantCulture, @"[{0}]\{1}", teamProjectName, securityGroup.Name);

            // Create the group if needed.
            var existingGroup = ims.ListApplicationGroups(teamProject.Uri, ReadIdentityOptions.IncludeReadFromSource).FirstOrDefault(g => string.Equals(g.DisplayName, groupIdentityName, StringComparison.OrdinalIgnoreCase));
            var members       = new List <TeamFoundationIdentity>();
            IdentityDescriptor groupDescriptor;

            if (existingGroup == null)
            {
                // There is no existing group with the same name, create one.
                task.Status     = "Creating new \"{0}\" security group".FormatCurrent(groupIdentityName);
                groupDescriptor = ims.CreateApplicationGroup(teamProject.Uri, securityGroup.Name, securityGroup.Description);
            }
            else
            {
                // We have an existing group with the same name, update the description.
                task.Status     = "Updating existing \"{0}\" security group".FormatCurrent(groupIdentityName);
                groupDescriptor = existingGroup.Descriptor;
                var applicationGroupWithMembers = ims.ReadIdentity(groupDescriptor, MembershipQuery.Direct, ReadIdentityOptions.None);
                if (applicationGroupWithMembers.Members != null && applicationGroupWithMembers.Members.Any())
                {
                    members.AddRange(ims.ReadIdentities(applicationGroupWithMembers.Members, MembershipQuery.Direct, ReadIdentityOptions.None).Where(m => m != null));
                }
                if (!string.IsNullOrEmpty(securityGroup.Description))
                {
                    ims.UpdateApplicationGroup(groupDescriptor, GroupProperty.Description, securityGroup.Description);
                }
            }

            // Set permissions.
            if (securityGroup.PermissionGroupChanges.SelectMany(g => g.PermissionChanges).Any(p => p.Action != PermissionChangeAction.None))
            {
                var securityNamespaces = tfs.GetService <ISecurityService>().GetSecurityNamespaces();
                foreach (var groupChange in securityGroup.PermissionGroupChanges.Where(g => g.PermissionChanges.Any(c => c.Action != PermissionChangeAction.None)))
                {
                    foreach (var securityNamespace in securityNamespaces)
                    {
                        var factory = permissionGroupFactories.FirstOrDefault(f => f.AppliesTo(securityNamespace.Description.NamespaceId, groupChange.PermissionGroup.Scope));
                        if (factory != null)
                        {
                            var tokens = factory.GetObjectTokens(tfs, tfsVersion, teamProject.Name, teamProject.Uri);
                            if (tokens != null)
                            {
                                foreach (var token in tokens)
                                {
                                    ApplySecurityNamespacePermissions(token, groupDescriptor, securityNamespace, groupChange.PermissionChanges);
                                }
                            }
                        }
                    }
                }
                task.Status = "Applied security permissions";
            }

            // Set members.
            if (securityGroup.RemoveAllUsers || !string.IsNullOrEmpty(securityGroup.UsersToAdd) || !string.IsNullOrEmpty(securityGroup.UsersToRemove))
            {
                ApplyGroupMemberChanges(task, securityGroup, groupDescriptor, ims, members);
                task.Status = "Applied group member changes";
            }
        }
        public static void ExportPermissions(ILogger logger, ApplicationTask task, TfsTeamProjectCollection tfs, TfsMajorVersion tfsVersion, IList <SecurityGroupPermissionExportRequest> exportRequests)
        {
            if (exportRequests.Any())
            {
                var step = 0;
                var securityNamespaces = tfs.GetService <ISecurityService>().GetSecurityNamespaces();
                var ims = tfs.GetService <IIdentityManagementService>();
                foreach (var exportRequest in exportRequests)
                {
                    task.SetProgress(step++, string.Format(CultureInfo.CurrentCulture, "Exporting \"{0}\" permissions from Team Project \"{1}\"", exportRequest.SecurityGroup.Name, exportRequest.SecurityGroup.TeamProject.Name));
                    try
                    {
                        var identity = ims.ReadIdentity(IdentitySearchFactor.Identifier, exportRequest.SecurityGroup.Sid, MembershipQuery.None, ReadIdentityOptions.None);
                        if (identity == null)
                        {
                            var message = "The security group \"{0}\" could not be retrieved.".FormatCurrent(exportRequest.SecurityGroup.FullName);
                            logger.Log(message, TraceEventType.Warning);
                            task.SetWarning(message);
                        }
                        else
                        {
                            var permissions = new List <PermissionChangePersistenceData>();
                            foreach (var securityNamespace in securityNamespaces)
                            {
                                foreach (var factory in permissionGroupFactories)
                                {
                                    if (factory.AppliesTo(securityNamespace.Description.NamespaceId))
                                    {
                                        var tokens = factory.GetObjectTokens(tfs, tfsVersion, exportRequest.SecurityGroup.TeamProject.Name, exportRequest.SecurityGroup.TeamProject.Uri.ToString());
                                        if (tokens != null)
                                        {
                                            var permissionGroup = factory.GetPermissionGroup(securityNamespace);
                                            foreach (var token in tokens)
                                            {
                                                var acl = securityNamespace.QueryAccessControlList(token, new[] { identity.Descriptor }, false);
                                                foreach (var ace in acl.AccessControlEntries)
                                                {
                                                    foreach (var permission in permissionGroup.Permissions)
                                                    {
                                                        var action = PermissionChangeAction.Inherit;
                                                        if ((permission.PermissionBit & ace.Allow) == permission.PermissionBit)
                                                        {
                                                            action = PermissionChangeAction.Allow;
                                                        }
                                                        else if ((permission.PermissionBit & ace.Deny) == permission.PermissionBit)
                                                        {
                                                            action = PermissionChangeAction.Deny;
                                                        }
                                                        permissions.Add(new PermissionChangePersistenceData(permission.Scope, permission.PermissionConstant, action));
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }

                            Directory.CreateDirectory(Path.GetDirectoryName(exportRequest.FileName));
                            PermissionChangePersistenceData.Save(exportRequest.FileName, permissions);
                        }
                    }
                    catch (Exception exc)
                    {
                        var message = string.Format(CultureInfo.CurrentCulture, "An error occurred while exporting \"{0}\" permissions from Team Project \"{1}\"", exportRequest.SecurityGroup.Name, exportRequest.SecurityGroup.TeamProject.Name);
                        logger.Log(message, exc);
                        task.SetError(message, exc);
                    }
                    if (task.IsCanceled)
                    {
                        task.Status = "Canceled";
                        break;
                    }
                }
            }
        }
Beispiel #5
0
        private static void NormalizeWorkItemTypeDefinition(XmlDocument normalizedXmlDefinition, TfsMajorVersion tfsMajorVersion)
        {
            var rawXml = normalizedXmlDefinition.OuterXml;

            // Replace "[Project]\" with "[project]\" everywhere.
            rawXml = rawXml.Replace(@"[Project]\", @"[project]\");

            normalizedXmlDefinition.LoadXml(rawXml);

            // Remove the work item type refname if present, since it is not returned from the project.
            var workitemTypeNode = normalizedXmlDefinition.SelectSingleNode("//WORKITEMTYPE");
            var refnameAttribute = workitemTypeNode.Attributes["refname"];

            if (refnameAttribute != null)
            {
                workitemTypeNode.Attributes.Remove(refnameAttribute);
            }

            // Set the default page layout mode if needed.
            foreach (var pageNode in normalizedXmlDefinition.SelectNodes("//WORKITEMTYPE/FORM/WebLayout/Page").Cast <XmlElement>())
            {
                AddAttributeIfNeeded(pageNode, "LayoutMode", "FirstColumnWide");
            }

            // Remove all empty CustomControlOptions.
            foreach (var customControlOptionsNode in normalizedXmlDefinition.SelectNodes("//CustomControlOptions").Cast <XmlElement>().Where(n => !n.HasChildNodes))
            {
                customControlOptionsNode.ParentNode.RemoveChild(customControlOptionsNode);
            }

            // Remove the "expanditems" attribute for ALLOWEDVALUES, SUGGESTEDVALUES and PROHIBITEDVALUES when it is true (the default).
            foreach (XmlAttribute expandItemsAttribute in normalizedXmlDefinition.SelectNodes("//ALLOWEDVALUES[@expanditems='true']/@expanditems | //SUGGESTEDVALUES[@expanditems='true']/@expanditems | //PROHIBITEDVALUES[@expanditems='true']/@expanditems"))
            {
                expandItemsAttribute.OwnerElement.Attributes.Remove(expandItemsAttribute);
            }

            // Remove global list references (these are typically lists of builds for the Team Project).
            foreach (XmlNode buildsGlobalList in normalizedXmlDefinition.SelectNodes("//WORKITEMTYPE/FIELDS/FIELD/SUGGESTEDVALUES[@filteritems='excludegroups' and count(GLOBALLIST) = 1]"))
            {
                buildsGlobalList.ParentNode.RemoveChild(buildsGlobalList);
            }

            // Normalize certain casing differences.
            foreach (XmlAttribute closedInErrorAttribute in normalizedXmlDefinition.SelectNodes("//DEFAULTREASON[@value='Closed in error']/@value | //REASON[@value='Closed in error']/@value"))
            {
                closedInErrorAttribute.Value = "Closed in Error";
            }
            foreach (XmlAttribute overtakenByEventsAttribute in normalizedXmlDefinition.SelectNodes("//DEFAULTREASON[@value='Overtaken by events']/@value | //REASON[@value='Overtaken by events']/@value"))
            {
                overtakenByEventsAttribute.Value = "Overtaken by Events";
            }
            foreach (XmlAttribute notFixedAttribute in normalizedXmlDefinition.SelectNodes("//DEFAULTREASON[@value='Not fixed']/@value | //REASON[@value='Not fixed']/@value"))
            {
                notFixedAttribute.Value = "Not Fixed";
            }
            foreach (XmlAttribute reconsideringAttribute in normalizedXmlDefinition.SelectNodes("//DEFAULTREASON[@value='Reconsidering the feature']/@value | //REASON[@value='Reconsidering the feature']/@value"))
            {
                reconsideringAttribute.Value = "Reconsidering the Feature";
            }
            foreach (XmlAttribute reconsideringAttribute in normalizedXmlDefinition.SelectNodes("//DEFAULTREASON[@value='Reconsidering the epic']/@value | //REASON[@value='Reconsidering the epic']/@value"))
            {
                reconsideringAttribute.Value = "Reconsidering the Epic";
            }
            foreach (XmlAttribute fieldIdAttribute in normalizedXmlDefinition.SelectNodes("//WORKITEMTYPE/FIELDS/FIELD[@refname='System.Id']/@name"))
            {
                fieldIdAttribute.Value = "ID";
            }

            // Add certain fields that are auto-generated if they're not present.
            var fieldDefinitionsNode = normalizedXmlDefinition.SelectSingleNode("//WORKITEMTYPE/FIELDS");
            var highestMatchingTfsVersionWithSystemFields = SystemFields.Keys.OrderBy(v => (int)v).Last(v => v <= tfsMajorVersion);
            var currentSystemFields = SystemFields[highestMatchingTfsVersionWithSystemFields];

            foreach (var refname in currentSystemFields.Keys)
            {
                var field = fieldDefinitionsNode.SelectSingleNode(string.Format(CultureInfo.InvariantCulture, "FIELD[@refname='{0}']", refname));
                if (field == null)
                {
                    field = normalizedXmlDefinition.CreateElement("FIELD");
                    foreach (var attributeEntry in currentSystemFields[refname])
                    {
                        var attribute = normalizedXmlDefinition.CreateAttribute(attributeEntry.Key);
                        attribute.Value = attributeEntry.Value;
                        field.Attributes.Append(attribute);
                    }
                    fieldDefinitionsNode.AppendChild(field);
                }
            }

            // Process fields.
            foreach (XmlElement field in normalizedXmlDefinition.SelectNodes("//WORKITEMTYPE/FIELDS/FIELD"))
            {
                var refname = field.Attributes["refname"].Value;

                // The ALLOWEXISTINGVALUE rule, when applied to a field, is in fact a shortcut
                // to apply it to all its rules (within the field but also in workflow states and transitions).
                var allowExistingValueNode = field.SelectSingleNode("ALLOWEXISTINGVALUE");
                if (allowExistingValueNode != null)
                {
                    // The ALLOWEXISTINGVALUE doesn't make sense if there are only DEFAULT or COPY rules in the list,
                    // in which case the ALLOWEXISTINGVALUE rule isn't present in an exported WITD.
                    Predicate <XmlNode> predicate = parentNode => parentNode.ChildNodes.Cast <XmlNode>().Any(childNode => !(string.Equals(childNode.LocalName, "DEFAULT", StringComparison.OrdinalIgnoreCase) || string.Equals(childNode.LocalName, "COPY", StringComparison.OrdinalIgnoreCase)));
                    foreach (var ruleElementName in FieldRuleElementNames)
                    {
                        AddElementIfNeeded(field.SelectSingleNode(ruleElementName), "ALLOWEXISTINGVALUE", predicate);
                    }

                    foreach (XmlNode fieldReference in normalizedXmlDefinition.SelectNodes(string.Format(CultureInfo.InvariantCulture, "//WORKITEMTYPE/WORKFLOW/STATES/STATE/FIELDS/FIELD[@refname='{0}'] | //WORKITEMTYPE/WORKFLOW/TRANSITIONS/TRANSITION/FIELDS/FIELD[@refname='{0}']", refname)))
                    {
                        AddElementIfNeeded(fieldReference, "ALLOWEXISTINGVALUE", predicate);
                    }

                    field.RemoveChild(allowExistingValueNode);
                }

                // Set the reportable and syncnamechanges attributes for certain system fields.
                SetAttributeIfNeeded(field, refname, KnownFieldsWithReportableDimension, "reportable", "dimension");
                SetAttributeIfNeeded(field, refname, KnownFieldsWithReportableDetail, "reportable", "detail");
                SetAttributeIfNeeded(field, refname, KnownFieldsWithReportableMeasure, "reportable", "measure");
                SetAttributeIfNeeded(field, refname, KnownFieldsWithSyncNameChanges, "syncnamechanges", "true");
                SetAttributeIfNeeded(field, refname, KnownFieldsWithFormulaSum, "formula", "sum");

                // Certain system fields have added spaces to their names, remove the spaces to normalize them.
                // See http://blogs.msdn.com/b/greggboer/archive/2010/02/25/names-changed-for-core-wit-fields-and-implications-thereof.aspx for more information.
                if (KnownFieldsWithNamesToRemoveSpaces.Contains(refname))
                {
                    var nameAttribute = field.Attributes["name"];
                    nameAttribute.Value = nameAttribute.Value.Replace(" ", string.Empty);

                    // The HyperLinkCount field not only had spaces removed but also a case change on the 'L'.
                    if (string.Equals(nameAttribute.Value, "HyperlinkCount", StringComparison.OrdinalIgnoreCase))
                    {
                        nameAttribute.Value = "HyperLinkCount";
                    }

                    // The AreaID and IterationID field not only had spaces removed but also a case change on the 'D'.
                    if (string.Equals(nameAttribute.Value, "AreaId", StringComparison.OrdinalIgnoreCase))
                    {
                        nameAttribute.Value = "AreaID";
                    }
                    if (string.Equals(nameAttribute.Value, "IterationId", StringComparison.OrdinalIgnoreCase))
                    {
                        nameAttribute.Value = "IterationID";
                    }
                }
            }

            // Sort field definitions by "refname" everywhere.
            SortChildNodes(normalizedXmlDefinition.SelectSingleNode("//WORKITEMTYPE/FIELDS"), n => GetValue(n.Attributes["refname"]));
            foreach (XmlNode stateFieldsNode in normalizedXmlDefinition.SelectNodes("//WORKITEMTYPE/WORKFLOW/STATES/STATE/FIELDS"))
            {
                SortChildNodes(stateFieldsNode, n => GetValue(n.Attributes["refname"]));
            }
            foreach (XmlNode transitionFieldsNode in normalizedXmlDefinition.SelectNodes("//WORKITEMTYPE/WORKFLOW/TRANSITIONS/TRANSITION/FIELDS"))
            {
                SortChildNodes(transitionFieldsNode, n => GetValue(n.Attributes["refname"]));
            }

            // Sort workflow states by "value".
            SortChildNodes(normalizedXmlDefinition.SelectSingleNode("//WORKITEMTYPE/WORKFLOW/STATES"), n => GetValue(n.Attributes["value"]));

            // Sort transitions by combination of "from" and "to".
            SortChildNodes(normalizedXmlDefinition.SelectSingleNode("//WORKITEMTYPE/WORKFLOW/TRANSITIONS"), n => string.Concat(GetValue(n.Attributes["from"]), " -> ", GetValue(n.Attributes["to"])));

            // Sort child nodes of fields anywhere.
            foreach (XmlNode field in normalizedXmlDefinition.SelectNodes("//FIELDS/FIELD"))
            {
                SortChildNodes(field);
            }

            // Sort child nodes of FIELD/WHEN, FIELD/WHENNOT, FIELD/WHENCHANGED, FIELD/WHENNOTCHANGED.
            foreach (XmlNode field in normalizedXmlDefinition.SelectNodes("//FIELDS/FIELD/WHEN"))
            {
                SortChildNodes(field);
            }
            foreach (XmlNode field in normalizedXmlDefinition.SelectNodes("//FIELDS/FIELD/WHENNOT"))
            {
                SortChildNodes(field);
            }
            foreach (XmlNode field in normalizedXmlDefinition.SelectNodes("//FIELDS/FIELD/WHENCHANGED"))
            {
                SortChildNodes(field);
            }
            foreach (XmlNode field in normalizedXmlDefinition.SelectNodes("//FIELDS/FIELD/WHENNOTCHANGED"))
            {
                SortChildNodes(field);
            }

            // Sort child nodes of allowed values.
            foreach (XmlNode allowedValuesList in normalizedXmlDefinition.SelectNodes("//ALLOWEDVALUES"))
            {
                SortChildNodes(allowedValuesList);
            }

            // Sort child nodes of suggested values.
            foreach (XmlNode suggestedValuesList in normalizedXmlDefinition.SelectNodes("//SUGGESTEDVALUES"))
            {
                SortChildNodes(suggestedValuesList);
            }

            // Sort child nodes of prohibited values.
            foreach (XmlNode prohibitedValuesList in normalizedXmlDefinition.SelectNodes("//PROHIBITEDVALUES"))
            {
                SortChildNodes(prohibitedValuesList);
            }

            // Sort child nodes of transitions.
            foreach (XmlNode transitionList in normalizedXmlDefinition.SelectNodes("//WORKITEMTYPE/WORKFLOW/TRANSITIONS/TRANSITION"))
            {
                SortChildNodes(transitionList);
            }

            // Sort child nodes of transition reasons.
            foreach (XmlNode transitionReasonList in normalizedXmlDefinition.SelectNodes("//WORKITEMTYPE/WORKFLOW/TRANSITIONS/TRANSITION/REASONS"))
            {
                SortChildNodes(transitionReasonList);
            }

            // Sort child nodes of transition actions.
            foreach (XmlNode transitionActionList in normalizedXmlDefinition.SelectNodes("//WORKITEMTYPE/WORKFLOW/TRANSITIONS/TRANSITION/ACTIONS"))
            {
                SortChildNodes(transitionActionList);
            }

            // Sort child nodes of links control options.
            foreach (XmlNode linksControlOptionList in normalizedXmlDefinition.SelectNodes("//LinksControlOptions"))
            {
                SortChildNodes(linksControlOptionList);
            }
        }
Beispiel #6
0
        public static XmlDocument GetNormalizedXmlDefinition(WorkItemConfigurationItem item, TfsMajorVersion tfsMajorVersion)
        {
            var normalizedXmlDefinition = new XmlDocument();

            normalizedXmlDefinition.LoadXml(item.XmlDefinition.OuterXml);

            // Perform pre-normalization.
            PreNormalizeXml(normalizedXmlDefinition);

            // Perform type-specific normalization.
            if (item.Type == WorkItemConfigurationItemType.WorkItemType)
            {
                NormalizeWorkItemTypeDefinition(normalizedXmlDefinition, tfsMajorVersion);
            }
            else if (item.Type == WorkItemConfigurationItemType.AgileConfiguration)
            {
                NormalizeAgileConfiguration(normalizedXmlDefinition);
            }
            else if (item.Type == WorkItemConfigurationItemType.CommonConfiguration)
            {
                NormalizeCommonConfiguration(normalizedXmlDefinition);
            }
            else if (item.Type == WorkItemConfigurationItemType.ProcessConfiguration)
            {
                NormalizeProcessConfiguration(normalizedXmlDefinition);
            }
            else if (item.Type == WorkItemConfigurationItemType.Categories)
            {
                NormalizeCategories(normalizedXmlDefinition);
            }

            // Perform other normalization after the specific normalization (nodes could have been added or removed).
            PostNormalizeXml(normalizedXmlDefinition);

            return(normalizedXmlDefinition);
        }
        public static WorkItemConfigurationComparisonResult Compare(TfsMajorVersion tfsMajorVersion, WorkItemConfiguration source, WorkItemConfiguration target, bool ignoreCase)
        {
            var itemResults = new List <WorkItemConfigurationItemComparisonResult>();

            var sourceItems = source.Items.ToList();
            var targetItems = target.Items.ToList();

            // Ignore TFS 2012 agile/common config when both source and target have TFS 2013 process config.
            if (sourceItems.Any(i => i.Type == WorkItemConfigurationItemType.ProcessConfiguration) && targetItems.Any(i => i.Type == WorkItemConfigurationItemType.ProcessConfiguration))
            {
                sourceItems.RemoveAll(i => i.Type == WorkItemConfigurationItemType.CommonConfiguration || i.Type == WorkItemConfigurationItemType.AgileConfiguration);
                targetItems.RemoveAll(i => i.Type == WorkItemConfigurationItemType.CommonConfiguration || i.Type == WorkItemConfigurationItemType.AgileConfiguration);
            }

            // If the source doesn't have categories or agile/common/process config, ignore them in the target as well.
            if (!sourceItems.Any(i => i.Type == WorkItemConfigurationItemType.Categories))
            {
                targetItems.RemoveAll(i => i.Type == WorkItemConfigurationItemType.Categories);
            }
            if (!sourceItems.Any(i => i.Type == WorkItemConfigurationItemType.AgileConfiguration))
            {
                targetItems.RemoveAll(i => i.Type == WorkItemConfigurationItemType.AgileConfiguration);
            }
            if (!sourceItems.Any(i => i.Type == WorkItemConfigurationItemType.CommonConfiguration))
            {
                targetItems.RemoveAll(i => i.Type == WorkItemConfigurationItemType.CommonConfiguration);
            }
            if (!sourceItems.Any(i => i.Type == WorkItemConfigurationItemType.ProcessConfiguration))
            {
                targetItems.RemoveAll(i => i.Type == WorkItemConfigurationItemType.ProcessConfiguration);
            }

            foreach (var itemOnlyInSource in sourceItems.Where(s => !targetItems.Any(t => s.Type == t.Type && string.Equals(s.Name, t.Name, StringComparison.OrdinalIgnoreCase))))
            {
                itemResults.Add(new WorkItemConfigurationItemComparisonResult(itemOnlyInSource.XmlDefinition, null, itemOnlyInSource.Name, itemOnlyInSource.Type, ComparisonStatus.ExistsOnlyInSource));
            }

            var comparisonType = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;

            foreach (var targetItem in targetItems)
            {
                var sourceItem = sourceItems.FirstOrDefault(s => s.Type == targetItem.Type && string.Equals(s.Name, targetItem.Name, StringComparison.OrdinalIgnoreCase));
                if (sourceItem == null)
                {
                    itemResults.Add(new WorkItemConfigurationItemComparisonResult(null, targetItem.XmlDefinition, targetItem.Name, targetItem.Type, ComparisonStatus.ExistsOnlyInTarget));
                }
                else
                {
                    // To allow better comparisons between source XML files from Process Templates
                    // and the actual exported work item configuration item, normalize and process some special cases.
                    var sourceNormalizedXmlDefinition = XmlNormalizer.GetNormalizedXmlDefinition(sourceItem, tfsMajorVersion);
                    var targetNormalizedXmlDefinition = XmlNormalizer.GetNormalizedXmlDefinition(targetItem, tfsMajorVersion);

                    var partResults = new List <WorkItemConfigurationItemPartComparisonResult>();
                    var sourceParts = sourceItem.GetParts(sourceNormalizedXmlDefinition);
                    var targetParts = targetItem.GetParts(targetNormalizedXmlDefinition);
                    var totalSize   = sourceParts.Sum(p => p.NormalizedXmlDefinition == null ? 0 : p.NormalizedXmlDefinition.OuterXml.Length);
                    foreach (WorkItemConfigurationItemPart sourcePart in sourceParts)
                    {
                        var targetPart = targetParts.SingleOrDefault(p => p.Name == sourcePart.Name);
                        ComparisonStatus status;
                        if (targetPart == null)
                        {
                            status = ComparisonStatus.ExistsOnlyInSource;
                        }
                        else
                        {
                            status = string.Equals(XmlNormalizer.GetValue(sourcePart.NormalizedXmlDefinition), XmlNormalizer.GetValue(targetPart.NormalizedXmlDefinition), comparisonType) ? ComparisonStatus.AreEqual : ComparisonStatus.AreDifferent;
                        }
                        var relativeSize = (sourcePart.NormalizedXmlDefinition == null ? 0 : sourcePart.NormalizedXmlDefinition.OuterXml.Length) / (double)totalSize;
                        partResults.Add(new WorkItemConfigurationItemPartComparisonResult(sourcePart.Name, status, relativeSize));
                    }

                    itemResults.Add(new WorkItemConfigurationItemComparisonResult(sourceNormalizedXmlDefinition, targetNormalizedXmlDefinition, sourceItem.Name, sourceItem.Type, partResults));
                }
            }

            return(new WorkItemConfigurationComparisonResult(source, target, itemResults));
        }
        private async Task <ICollection <BuildDefinitionTemplate> > GetBuildTemplatesAsync(TfsMajorVersion tfsVersion, bool fromBuildDefinitions)
        {
            var buildTemplates = new List <BuildDefinitionTemplate>();

            using (var dialog = new TeamProjectPicker(TeamProjectPickerMode.SingleProject, false))
            {
                var result = dialog.ShowDialog(Application.Current.MainWindow.GetIWin32Window());
                if (result == System.Windows.Forms.DialogResult.OK && dialog.SelectedProjects != null && dialog.SelectedProjects.Length > 0)
                {
                    var teamProjectCollection = dialog.SelectedTeamProjectCollection;
                    var teamProject           = dialog.SelectedProjects.First();
                    var buildServer           = teamProjectCollection.GetClient <BuildHttpClient>();

                    if (fromBuildDefinitions)
                    {
                        var buildDefinitions = new List <BuildDefinition>();
                        if (tfsVersion == TfsMajorVersion.TeamServices)
                        {
                            // Use a single API call if available.
                            var projectBuildDefinitions = await buildServer.GetFullDefinitionsAsync(project : teamProject.Name);

                            buildDefinitions.AddRange(projectBuildDefinitions);
                        }
                        else
                        {
                            // Otherwise get the base info first and then individual details.
                            var baseBuildDefinitions = await buildServer.GetDefinitionsAsync(project : teamProject.Name);

                            foreach (var baseBuildDefinition in baseBuildDefinitions.Where(b => b.Type == DefinitionType.Build))
                            {
                                var buildDefinition = await buildServer.GetDefinitionAsync(project : teamProject.Name, definitionId : baseBuildDefinition.Id);

                                buildDefinitions.Add(buildDefinition);
                            }
                        }
                        buildDefinitions = buildDefinitions.Where(b => b.Type == DefinitionType.Build).OrderBy(b => b.Name).ToList();

                        if (!buildDefinitions.Any())
                        {
                            MessageBox.Show("The selected Team Project does not contain any build definitions.", "No Build Definitions", MessageBoxButton.OK, MessageBoxImage.Information);
                        }
                        else
                        {
                            foreach (var buildDefinition in buildDefinitions)
                            {
                                var template = new BuildDefinitionTemplate
                                {
                                    Id          = buildDefinition.Name.Replace(" ", ""),
                                    Name        = buildDefinition.Name,
                                    Description = "Build template based on build definition \"{0}\" in Team Project \"{1}\"".FormatCurrent(buildDefinition.Name, teamProject.Name),
                                    Template    = buildDefinition
                                };
                                buildTemplates.Add(template);
                            }
                        }
                    }
                    else
                    {
                        var buildTemplatesFromProject = await buildServer.GetTemplatesAsync(project : teamProject.Name);

                        buildTemplatesFromProject = buildTemplatesFromProject.Where(t => t.Template != null && t.Template.Project != null).OrderBy(t => t.Name).ToList();

                        if (!buildTemplatesFromProject.Any())
                        {
                            MessageBox.Show("The selected Team Project does not contain any build templates.", "No Build Templates", MessageBoxButton.OK, MessageBoxImage.Information);
                        }
                        else
                        {
                            foreach (var buildTemplate in buildTemplatesFromProject)
                            {
                                buildTemplates.Add(buildTemplate);
                            }
                        }
                    }
                }
            }

            if (buildTemplates.Any())
            {
                var picker = new ItemsPickerDialog();
                picker.ItemDisplayMemberPath = nameof(BuildDefinitionTemplate.Name);
                picker.Owner          = Application.Current.MainWindow;
                picker.Title          = fromBuildDefinitions ? "Select the build definitions to import" : "Select the build templates to import";
                picker.SelectionMode  = SelectionMode.Multiple;
                picker.AvailableItems = buildTemplates;
                if (picker.ShowDialog() == true)
                {
                    return(picker.SelectedItems.Cast <BuildDefinitionTemplate>().ToArray());
                }
            }

            return(new BuildDefinitionTemplate[0]);
        }