首页 > 代码库 > Dynamics 365 WebResourceUtility 工具修复部分问题

Dynamics 365 WebResourceUtility 工具修复部分问题

Dynamics 365 SDK/Tools/WebResourceUtility 是一个非常实用的工具,

在开发WebResource脚本时,常常需要把本地开发上传到开发环境,实用此工具不需要打开CRM找到具体的WebResource上传然后再发布,

利用此工具可以批量上传本地代码到开发环境并自动发布已上传的代码。

但工具包本身包含一些问题,幸好工具包提供源码,我修复了部分问题,支持OnPremise / IFD / Online Office365部署。

Update point:

  1. only publish WebResources which already uploaded.
  2. fixed authentication failed issue.
  3. support office 365 authenticate.
  4. online federation authenticate has been commented. 

ConsolelessServerConnection.cs

using System;
using System.Collections.Generic;
using System.ServiceModel.Description;
using System.Linq;

// These namespaces are found in the Microsoft.Xrm.Sdk.dll assembly
// located in the SDK\bin folder of the SDK download.
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Discovery;
using Microsoft.Xrm.Sdk;
using System.DirectoryServices.AccountManagement;

namespace Microsoft.Crm.Sdk.Samples
{
    public class ConsolelessServerConnection : ServerConnection
    {

        #region Private properties

        private Configuration config = new Configuration();

        #endregion Private properties

        public virtual ServerConnection.Configuration GetServerConfiguration(string server, string orgName, string user, string pw, string domain )
        {
            config.ServerAddress = server;
            if (config.ServerAddress.EndsWith(".dynamics.com"))
            {
                config.EndpointType = AuthenticationProviderType.LiveId;
                config.DiscoveryUri =
                    new Uri(String.Format("https://disco.{0}/XRMServices/2011/Discovery.svc", config.ServerAddress.Replace(orgName+".","")));

                config.DeviceCredentials = GetDeviceCredentials();
                ClientCredentials credentials = new ClientCredentials();
                credentials.UserName.UserName = user;
                credentials.UserName.Password = pw;
                
                config.Credentials = credentials;
                config.OrganizationUri = GetOrganizationAddress(config.DiscoveryUri, orgName);
            }
            else if (config.ServerAddress.EndsWith(".com"))
            {
                config.EndpointType = AuthenticationProviderType.Federation;
                config.DiscoveryUri =
                    new Uri(String.Format("https://{0}/XRMServices/2011/Discovery.svc", config.ServerAddress));
                ClientCredentials credentials = new ClientCredentials();
                credentials.Windows.ClientCredential = new System.Net.NetworkCredential(user, pw, domain);
                config.Credentials = credentials;
                config.OrganizationUri = GetOrganizationAddress(config.DiscoveryUri, orgName);
            }
            else
            {
                config.EndpointType = AuthenticationProviderType.ActiveDirectory;
                config.DiscoveryUri =
                    new Uri(String.Format("http://{0}/XRMServices/2011/Discovery.svc", config.ServerAddress));
                ClientCredentials credentials = new ClientCredentials();
                credentials.Windows.ClientCredential = new System.Net.NetworkCredential(user, pw, domain);
                config.Credentials = credentials;
                config.OrganizationUri = GetOrganizationAddress(config.DiscoveryUri, orgName);
            }

            if (configurations == null) configurations = new List<Configuration>();
            configurations.Add(config);

            return config;
        }

        public DiscoveryServiceProxy GetDiscoveryServiceProxy(Uri discoveryServiceUri)
        {
            IServiceManagement<IDiscoveryService> serviceManagement =
                        ServiceConfigurationFactory.CreateManagement<IDiscoveryService>(
                       discoveryServiceUri);
            AuthenticationProviderType endpointType = serviceManagement.AuthenticationType;

            
            AuthenticationCredentials authCredentials = GetCredentials(serviceManagement, endpointType);

            String organizationUri = String.Empty;
            
            return GetProxy<IDiscoveryService, DiscoveryServiceProxy>(serviceManagement, authCredentials);           
                 
        }

        public OrganizationServiceProxy GetOrganizationServiceProxy(Uri discoveryServiceUri, string orgName)
        {
            IServiceManagement<IDiscoveryService> serviceManagement =
                        ServiceConfigurationFactory.CreateManagement<IDiscoveryService>(
                       discoveryServiceUri);
            AuthenticationProviderType endpointType = serviceManagement.AuthenticationType;

            
            AuthenticationCredentials authCredentials = GetCredentials(serviceManagement, endpointType);

            String organizationUri = String.Empty;
            // Get the discovery service proxy.
            using (DiscoveryServiceProxy discoveryProxy =
                GetProxy<IDiscoveryService, DiscoveryServiceProxy>(serviceManagement, authCredentials))
            {

                if (discoveryProxy != null)
                {

                    // Obtain information about the organizations that the system user belongs to.
                    OrganizationDetailCollection orgs = DiscoverOrganizations(discoveryProxy);

                    OrganizationDetail org = orgs.Where(x => x.UniqueName.ToLower() == orgName.ToLower()).FirstOrDefault();

                    if (org != null)
                    {
                        Uri orgUri = new System.Uri(org.Endpoints[EndpointType.OrganizationService]);

                        IServiceManagement<IOrganizationService> orgServiceManagement =
                            ServiceConfigurationFactory.CreateManagement<IOrganizationService>(orgUri);

                        // Set the credentials.
                        AuthenticationCredentials credentials = GetCredentials(orgServiceManagement, endpointType);

                        return GetProxy<IOrganizationService, OrganizationServiceProxy>(orgServiceManagement, credentials); 

                    }
                    else
                    {
                        throw new InvalidOperationException("That OrgName does not exist on that server.");
                    }
                }
                else
                    throw new Exception("An invalid server name was specified.");
            } 
        }

        protected virtual Uri GetOrganizationAddress(Uri discoveryServiceUri, string orgName)
        {

            
            IServiceManagement<IDiscoveryService> serviceManagement =
                        ServiceConfigurationFactory.CreateManagement<IDiscoveryService>(
                       discoveryServiceUri);
            AuthenticationProviderType endpointType = serviceManagement.AuthenticationType;

            
            AuthenticationCredentials authCredentials = GetCredentials(serviceManagement, endpointType);

            String organizationUri = String.Empty;
            // Get the discovery service proxy.
            using (DiscoveryServiceProxy discoveryProxy =
                GetProxy<IDiscoveryService, DiscoveryServiceProxy>(serviceManagement, authCredentials))
            {
                // Obtain organization information from the Discovery service. 
                if (discoveryProxy != null)
                {
                    // Obtain information about the organizations that the system user belongs to.
                    OrganizationDetailCollection orgs = DiscoverOrganizations(discoveryProxy);

                    OrganizationDetail org = orgs.Where(x => x.UrlName.ToLower() == orgName.ToLower()).FirstOrDefault();

                    if (org != null)
                    {
                        return new System.Uri(org.Endpoints[EndpointType.OrganizationService]);                       
                    }
                    else
                    {
                        throw new InvalidOperationException("That OrgName does not exist on that server.");
                    }
                }
                else
                    throw new Exception("An invalid server name was specified.");
            }
        }

        #region CustomFunctions 修改获取发现服务或组织服务代码

        public OrganizationServiceProxy GetOrganizationServiceProxy(Uri orgUri, AuthenticationProviderType endpointType)
        {
            IServiceManagement<IOrganizationService> orgServiceManagement =
                            ServiceConfigurationFactory.CreateManagement<IOrganizationService>(orgUri);

            // Set the credentials.
            AuthenticationCredentials credentials = GetCredentials(orgServiceManagement, endpointType);

            return GetProxy<IOrganizationService, OrganizationServiceProxy>(orgServiceManagement, credentials);
        }

        /// <summary>
        /// Generic method to obtain discovery/organization service proxy instance.
        /// </summary>
        /// <typeparam name="TService">
        /// Set IDiscoveryService or IOrganizationService type to request respective service proxy instance.
        /// </typeparam>
        /// <typeparam name="TProxy">
        /// Set the return type to either DiscoveryServiceProxy or OrganizationServiceProxy type based on TService type.
        /// </typeparam>
        /// <param name="serviceManagement">An instance of IServiceManagement</param>
        /// <param name="authCredentials">The user‘s Microsoft Dynamics CRM logon credentials.</param>
        /// <returns></returns>
        /// <snippetAuthenticateWithNoHelp4>
        public TProxy GetProxy<TService, TProxy>(
            IServiceManagement<TService> serviceManagement,
            AuthenticationCredentials authCredentials)
            where TService : class
            where TProxy : ServiceProxy<TService>
        {
            Type classType = typeof(TProxy);

            if (serviceManagement.AuthenticationType !=
                AuthenticationProviderType.ActiveDirectory)
            {
                AuthenticationCredentials tokenCredentials =
                    serviceManagement.Authenticate(authCredentials);
                // Obtain discovery/organization service proxy for Federated, LiveId and OnlineFederated environments. 
                // Instantiate a new class of type using the 2 parameter constructor of type IServiceManagement and SecurityTokenResponse.
                return (TProxy)classType
                    .GetConstructor(new Type[] { typeof(IServiceManagement<TService>), typeof(SecurityTokenResponse) })
                    .Invoke(new object[] { serviceManagement, tokenCredentials.SecurityTokenResponse });
            }

            // Obtain discovery/organization service proxy for ActiveDirectory environment.
            // Instantiate a new class of type using the 2 parameter constructor of type IServiceManagement and ClientCredentials.
            return (TProxy)classType
                .GetConstructor(new Type[] { typeof(IServiceManagement<TService>), typeof(ClientCredentials) })
                .Invoke(new object[] { serviceManagement, authCredentials.ClientCredentials });
        }
   
        //<snippetAuthenticateWithNoHelp2>
        /// <summary>
        /// Obtain the AuthenticationCredentials based on AuthenticationProviderType.
        /// </summary>
        /// <param name="service">A service management object.</param>
        /// <param name="endpointType">An AuthenticationProviderType of the CRM environment.</param>
        /// <returns>Get filled credentials.</returns>
        public AuthenticationCredentials GetCredentials<TService>(IServiceManagement<TService> service, AuthenticationProviderType endpointType)
        {
            AuthenticationCredentials authCredentials = new AuthenticationCredentials();

            switch (endpointType)
            {
                case AuthenticationProviderType.ActiveDirectory:
                    authCredentials.ClientCredentials.Windows.ClientCredential =
                        new System.Net.NetworkCredential(config.Credentials.UserName.UserName,
                            config.Credentials.UserName.Password,
                            config.Credentials.Windows.ClientCredential.Domain);
                    break;
                case AuthenticationProviderType.LiveId:
                    authCredentials.ClientCredentials.UserName.UserName = config.Credentials.UserName.UserName;
                    authCredentials.ClientCredentials.UserName.Password = config.Credentials.UserName.Password;
                    authCredentials.SupportingCredentials = new AuthenticationCredentials();
                    authCredentials.SupportingCredentials.ClientCredentials =
                        Microsoft.Crm.Services.Utility.DeviceIdManager.LoadOrRegisterDevice();
                    break;
                default: // For Federated and OnlineFederated environments.     
                    authCredentials.ClientCredentials.UserName.UserName = config.Credentials.UserName.UserName;
                    authCredentials.ClientCredentials.UserName.Password = config.Credentials.UserName.Password;
                    // For OnlineFederated single-sign on, you could just use current UserPrincipalName instead of passing user name and password.
                    //authCredentials.UserPrincipalName = UserPrincipal.Current.UserPrincipalName;  // Windows Kerberos

                    //The service is configured for User Id authentication, but the user might provide Microsoft

                    //account credentials.If so, the supporting credentials must contain the device credentials.
                    if (endpointType == AuthenticationProviderType.OnlineFederation)
                    {
                        IdentityProvider provider = service.GetIdentityProvider(authCredentials.UserPrincipalName);
                        if (provider != null && provider.IdentityProviderType == IdentityProviderType.LiveId)
                        {
                            authCredentials.SupportingCredentials = new AuthenticationCredentials();
                            authCredentials.SupportingCredentials.ClientCredentials =
                                Microsoft.Crm.Services.Utility.DeviceIdManager.LoadOrRegisterDevice();
                        }
                    }

                    break;
            }

            return authCredentials;
        }
  
        #endregion
    }
}

  MainWindowViewModel.cs

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;
using System.Xml.Linq;
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Query;
using WebResourceUtility.Model;
using System.Text.RegularExpressions;
using System.ComponentModel;
using System.Windows.Threading;

namespace Microsoft.Crm.Sdk.Samples
{
    public class MainWindowViewModel : ViewModelBase
    {

        #region Commands


        RelayCommand _hideOutputWindow;
        public ICommand HideOutputWindow 
        {
            get
            {
                if (_hideOutputWindow == null)
                {
                    _hideOutputWindow = new RelayCommand(
                        param => { IsOutputWindowDisplayed = false; });
                }
                return _hideOutputWindow;
            }
        }

        RelayCommand _showOutputWindow;
        public ICommand ShowOutputWindow
        {
            get
            {
                if (_showOutputWindow == null)
                {
                    _showOutputWindow = new RelayCommand(
                        param => { IsOutputWindowDisplayed = true; });
                }
                return _showOutputWindow;
            }
        }

        RelayCommand _browseFolderCommand;
        public ICommand BrowseFolderCommand 
        {
            get
            {
                if (_browseFolderCommand == null)
                {
                    _browseFolderCommand = new RelayCommand(
                        param => this.ShowBrowseFolderDialog());
                }
                return _browseFolderCommand;
            }

        }

        RelayCommand _activateConnectionCommand;
        public ICommand ActivateConnectionCommand 
        {
            get
            {
                if (_activateConnectionCommand == null)
                {
                    _activateConnectionCommand = new RelayCommand(
                        param => this.ActivateSelectedConfiguration(),
                        param => this.CanActivateSelectedConfiguration());
                }
                return _activateConnectionCommand;
            }

        }

        RelayCommand _createNewConnectionCommand;
        public ICommand CreateNewConnectionCommand
        {
            get
            {
                if (_createNewConnectionCommand == null)
                {
                    _createNewConnectionCommand = new RelayCommand(
                        param => this.CreateNewConfiguration());
                }
                return _createNewConnectionCommand;
            }

        }
                
        RelayCommand _deleteConnectionCommand;
        public ICommand DeleteConnectionCommand
        {
            get
            {
                if (_deleteConnectionCommand == null)
                {
                    _deleteConnectionCommand = new RelayCommand(
                        param => this.DeleteSelectedConfiguration());
                }
                return _deleteConnectionCommand;
            }

        }

        RelayCommand _activateSolutionCommand;
        public ICommand ActivateSolutionCommand
        {
            get
            {
                if (_activateSolutionCommand == null)
                {
                    _activateSolutionCommand = new RelayCommand(
                        param => this.ActivateSelectedSolution());
                }
                return _activateSolutionCommand;
            }
        }

        RelayCommand _activateSelectedPackageCommand;
        public ICommand ActivateSelectedPackageCommand
        {
            get
            {
                if (_activateSelectedPackageCommand == null)
                {
                    _activateSelectedPackageCommand = new RelayCommand(
                        param => this.ActivatePackage());
                }
                return _activateSelectedPackageCommand;
            }
        }

        RelayCommand _createNewPackageCommand;
        public ICommand CreateNewPackageCommand
        {
            get
            {
                if (_createNewPackageCommand == null)
                {
                    _createNewPackageCommand = new RelayCommand(
                        param => this.CreateNewPackage());
                }
                return _createNewPackageCommand;
            }
        }

        RelayCommand _deleteActivePackageCommand;
        public ICommand DeleteActivePackageCommand
        {
            get
            {
                if (_deleteActivePackageCommand == null)
                {
                    _deleteActivePackageCommand = new RelayCommand(
                        param => this.DeleteSelectedPackage());
                }
                return _deleteActivePackageCommand;
            }

        }

        RelayCommand _saveActivePackageCommand;
        public ICommand SaveActivePackageCommand
        {
            get
            {
                if (_saveActivePackageCommand == null)
                {
                    _saveActivePackageCommand = new RelayCommand(
                        param => SavePackages());
                }
                return _saveActivePackageCommand;
            }
        }            

        RelayCommand _refreshFilesCommand;
        public ICommand RefreshFilesCommand
        {
            get
            {
                if (_refreshFilesCommand == null)
                {
                    _refreshFilesCommand = new RelayCommand(
                        param => this.SearchAndPopulateFiles());
                }
                return _refreshFilesCommand;
            }
        }

        RelayCommand _saveConnectionsCommand;
        public ICommand SaveConnectionsCommand
        {
            get
            {
                if (_saveConnectionsCommand == null)
                {
                    _saveConnectionsCommand = new RelayCommand(
                        param => SaveConfigurations());
                }
                return _saveConnectionsCommand;
            }
        }

        RelayCommand<IEnumerable> _convertFileToResourceCommand;
        public ICommand ConvertFileToResourceCommand
        {
            get
            {
                if (_convertFileToResourceCommand == null)
                {
                    _convertFileToResourceCommand = new RelayCommand<IEnumerable>(AddFilesToWebResources);
                }
                return _convertFileToResourceCommand;
            }
        }

        RelayCommand<IEnumerable> _uploadWebResourcesCommand;
        public ICommand UploadWebResourcesCommand
        {
            get
            {
                if (_uploadWebResourcesCommand == null)
                {
                    _uploadWebResourcesCommand = new RelayCommand<IEnumerable>(UploadWebResources, param => CanUploadWebResource());
                }
                return _uploadWebResourcesCommand;
            }
        }

        RelayCommand _uploadAllWebResourcesCommand;
        public ICommand UploadAllWebResourcesCommand
        {
            get
            {
                if (_uploadAllWebResourcesCommand == null)
                {
                    _uploadAllWebResourcesCommand = 
                        new RelayCommand(param =>                    UploadAllWebResources(), 
                            param => CanUploadWebResource());
                }
                return _uploadAllWebResourcesCommand;
            }
        }

        RelayCommand<IEnumerable> _deleteWebResourcesCommand;
        public ICommand DeleteWebResourcesCommand
        {
            get
            {
                if (_deleteWebResourcesCommand == null)
                {
                    _deleteWebResourcesCommand = new RelayCommand<IEnumerable>(DeleteSelectedWebResources);
                }
                return _deleteWebResourcesCommand;
            }
        }
        
        #endregion 
        
        #region Properties

        public const string CONFIG_FILENAME = @"configurations.xml";
        public const string PACKAGES_FILENAME = @"packages.xml";
        public const string VALID_NAME_MSG = "ERROR: Web Resource names cannot contain spaces or hyphens. They must be alphanumeric and contain underscore characters, periods, and non-consecutive forward slash characters";
        public XElement XmlPackageData;
        public XElement XmlConfigData;

        private StringBuilder _progressMessage;
        public String ProgressMessage
        {
            get
            {
                return _progressMessage.ToString();
            }
            set
            {
                _progressMessage.AppendLine(value);
                OnPropertyChanged("ProgressMessage");
            }
        }

        private int _tabControlSelectedIndex;
        public int TabControlSelectedIndex
        {
            get
            {
                return _tabControlSelectedIndex;
            }
            set
            {
                _tabControlSelectedIndex = value;
                OnPropertyChanged("TabControlSelectedIndex");
            }
        }

        private bool _areAllButtonsEnabled = true;
        public bool AreAllButtonsEnabled
        {
            get
            {
                return _areAllButtonsEnabled;
            }
            set
            {
                _areAllButtonsEnabled = value;
                OnPropertyChanged("AreAllButtonsEnabled");
            }
        }

        public bool IsActiveConnectionSet
        {
            get 
            {
                return (ActiveConfiguration != null) ? true : false;
            }
        }
        public bool IsActiveSolutionSet
        {
            get
            {
                return (ActiveSolution != null) ? true : false;
            }
        }
        public bool IsActivePackageSet
        {
            get
            {
                return (ActivePackage != null) ? true : false;
            }
        }

        private bool _shouldPublishAllAfterUpload;
        public bool ShouldPublishAllAfterUpload
        {
            get
            {
                return _shouldPublishAllAfterUpload;
            }
            set 
            {
                _shouldPublishAllAfterUpload = value;
                OnPropertyChanged("ShouldPublishAllAfterUpload");
            }
        }

        private bool _isOutputWindowDisplayed = false;
        public bool IsOutputWindowDisplayed
        {
            get
            {
                return _isOutputWindowDisplayed;
            }
            set
            {
                _isOutputWindowDisplayed = value;
                OnPropertyChanged("IsOutputWindowDisplayed");
                OnPropertyChanged("IsWorkstationDisplayed");
            }
        }

        public bool IsWorkstationDisplayed
        {
            get
            {
                return !(IsOutputWindowDisplayed);
            }
        }

        private String _fileSearchText;
        public String FileSearchText
        {
            get { return _fileSearchText; }
            set { _fileSearchText = value; OnPropertyChanged("FileSearchText"); }
        }
        
        //WebResources Packages
        public ObservableCollection<XElement> Packages { get; set; }
        private XElement _selectedPackage;
        public XElement SelectedPackage
        {
            get
            {
                return _selectedPackage;
            }
            set
            {
                _selectedPackage = value;
                OnPropertyChanged("SelectedPackage");
            }
        }
        private XElement _activePackage;
        public XElement ActivePackage
        {
            get
            {
                return _activePackage;
            }
            set
            {
                _activePackage = value;
                OnPropertyChanged("ActivePackage");
                OnPropertyChanged("IsActivePackageSet");
            }
        }
        private bool _isActivePackageDirty = false;
        public bool IsActivePackageDirty
        {
            get
            {
                return _isActivePackageDirty;
            }
            set
            {
                _isActivePackageDirty = value;
                OnPropertyChanged("IsActivePackageDirty");
            }
        }

        //FileInfos for all potential resources in a directory
        public ObservableCollection<FileInfo> CurrentFiles { get; set; }
        public ObservableCollection<FileInfo> CurrentFilesSelected { get; set; }

        //Represents a collection of "WebResourceInfo" node from XML.
        public ObservableCollection<XElement> WebResourceInfos { get; set; }
        public ObservableCollection<XElement> WebResourceInfosSelected { get; set; }
        
        //Connections
        public ObservableCollection<XElement> Configurations { get; set; }
        private XElement _selectedConfiguration;
        public XElement SelectedConfiguration 
        {
            get { return _selectedConfiguration; }
            set
            {
                _selectedConfiguration = value;
                OnPropertyChanged("SelectedConfiguration");
            }
        }
        private XElement _activeConfiguration;
        public XElement ActiveConfiguration
        {
            get { return _activeConfiguration; }
            set
            {
                _activeConfiguration = value;
                OnPropertyChanged("ActiveConfiguration");
                OnPropertyChanged("IsActiveConnectionSet");
            }
        }

        //Solutions
        public ObservableCollection<Solution> UnmanagedSolutions { get; set; }
        private Solution _selectedSolution;
        public Solution SelectedSolution 
        {
            get 
            { 
                return _selectedSolution; 
            }
            set
            {
                _selectedSolution = value;
                OnPropertyChanged("SelectedSolution");
            }
        }
        private Solution _activeSolution;
        public Solution ActiveSolution
        {
            get
            {
                return _activeSolution;
            }
            set
            {
                _activeSolution = value;
                OnPropertyChanged("ActiveSolution");
                OnPropertyChanged("IsActiveSolutionSet");
            }
        }

        //Active Publisher
        private Publisher _activePublisher;
        public Publisher ActivePublisher
        {
            get { return _activePublisher; }
            set { _activePublisher = value; OnPropertyChanged("ActivePublisher"); }
        }


        private bool _shouldPublishAllUploadedWebResource;
        /// <summary>
        /// 是否更新已上传的WebResource
        /// </summary>
        public bool ShouldPublishAllUploadedWebResource
        {
            get
            {
                return _shouldPublishAllUploadedWebResource;
            }
            set
            {
                _shouldPublishAllUploadedWebResource = value;
                OnPropertyChanged("ShouldPublishAllUploadedWebResource");
            }
        }

        #endregion

        #region Fields
        //CRM Data Provider
        private ConsolelessServerConnection _serverConnect;        
        private static OrganizationServiceProxy _serviceProxy;
        private static OrganizationServiceContext _orgContext;

        BackgroundWorker worker;
        
        #endregion

        #region Constructor

        public MainWindowViewModel()
        {
            XDocument xmlPackagesDocument = XDocument.Load(PACKAGES_FILENAME);
            XmlPackageData = http://www.mamicode.com/xmlPackagesDocument.Element("UtilityRoot");

            XDocument xmlConfigurationsDocument = XDocument.Load(CONFIG_FILENAME);
            XmlConfigData = http://www.mamicode.com/xmlConfigurationsDocument.Element("Configurations");

            Configurations = new ObservableCollection<XElement>();
            Packages = new ObservableCollection<XElement>();
            UnmanagedSolutions = new ObservableCollection<Solution>();
            CurrentFiles = new ObservableCollection<FileInfo>();
            CurrentFilesSelected = new ObservableCollection<FileInfo>();
            WebResourceInfos = new ObservableCollection<XElement>();
            WebResourceInfosSelected = new ObservableCollection<XElement>();

            //Begin loading the XML data
            LoadXmlData();

            TabControlSelectedIndex = 0;
            _progressMessage = new StringBuilder();
            _shouldPublishAllAfterUpload = false;
            _shouldPublishAllUploadedWebResource = true;
            List_UploadedWebResources = new List<Guid>();

            //Set up the background worker to handle upload web resources. Helps
            //prevent the UI from locking up and can have a Console-like output window
            //with real-time display.
            worker = new BackgroundWorker();            
            worker.WorkerReportsProgress = true;
            worker.DoWork += new DoWorkEventHandler(BeginUpload);
            worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
            {
                this.BeginInvoke(() =>
                {
                    AreAllButtonsEnabled = true;
                });
            };
        }

        #endregion

        #region Methods 

        private void LoadXmlData()
        {
            LoadConfigurations();
            LoadPackages();           
        }
     
        //Configuration Methods
        private void LoadConfigurations()
        {
            Configurations.Clear();

            var configs = XmlConfigData.Descendants("Configuration");
            foreach (var c in configs)
            {
                Configurations.Add(c);
            }
        }
        private void SaveConfigurations()
        {
            XmlConfigData.Descendants("Configuration").Remove();
            XmlConfigData.Add(Configurations.ToArray());

            XmlConfigData.Save(CONFIG_FILENAME);
        }
        private void CreateNewConfiguration()
        {
            XElement newConfig = new XElement("Configuration",
                new XAttribute("name", "New Connection"),
                new XAttribute("server", String.Empty),
                new XAttribute("orgName", String.Empty),
                new XAttribute("userName", String.Empty),
                new XAttribute("domain", String.Empty));

            Configurations.Add(newConfig);
            SelectedConfiguration = Configurations[Configurations.Count - 1];
        }
        private void DeleteSelectedConfiguration()
        {
            if (SelectedConfiguration != null)
            {
                //if trying to delete the configuration that is also active already,
                //let them by clearing ActiveConfiguration and solutions.
                if (SelectedConfiguration == ActiveConfiguration)
                {
                    ClearActiveConfiguration();
                    ClearSolutions();
                }

                //Finally clear the SelectedConfiguration and remove it from the list of Configurations.
                var toBeDeleted = Configurations.Where(x => x == SelectedConfiguration).FirstOrDefault();
                if (toBeDeleted != null)
                {
                    Configurations.Remove(toBeDeleted);
                    SelectedConfiguration = null;
                }
            }
        }
        private void ClearActiveConfiguration()
        {
            ActiveConfiguration = null;
        }
        private void ActivateSelectedConfiguration()
        {
            //User may have already been connected to another org, disconnect them.
            ClearActiveConfiguration();

            //Clear out any Solutions from the Solutions collection since they are 
            //Configuration specfic.
            ClearSolutions();

            //Instantiate new proxy. if it is successful, it will also
            //set the ActiveConfiguration and retrieve Solutions.
            InstantiateService();
        }
        private bool CanActivateSelectedConfiguration()
        {
            if (SelectedConfiguration != null &&
                !String.IsNullOrWhiteSpace(SelectedConfiguration.Attribute("server").Value) &&
                !String.IsNullOrWhiteSpace(SelectedConfiguration.Attribute("orgName").Value))
            {
                return true;
            }
            return false;
        }
    
        //Solution Methods
        private void LoadSolutions()
        {
            //Check whether it already exists
            QueryExpression queryUnmanagedSolutions = new QueryExpression
            {
                EntityName = Solution.EntityLogicalName,
                ColumnSet = new ColumnSet(true),
                Criteria = new FilterExpression()
            };
            queryUnmanagedSolutions.Criteria.AddCondition("ismanaged", ConditionOperator.Equal, false);
            EntityCollection querySolutionResults = _serviceProxy.RetrieveMultiple(queryUnmanagedSolutions);

            if (querySolutionResults.Entities.Count > 0)
            {
                //The Where() is important because a query for all solutions
                //where Type=Unmanaged returns 3 solutions. The CRM UI of a 
                //vanilla instance shows only 1 unmanaged solution: "Default". 
                //Assume "Active" and "Basic" should not be touched?
                UnmanagedSolutions = new ObservableCollection<Solution>(
                    querySolutionResults.Entities
                        .Select(x => x as Solution)
                        .Where(s => s.UniqueName != "Active" &&
                                    s.UniqueName != "Basic"
                        )
                    );

                //If only 1 solution returns just go ahead and default it.
                if (UnmanagedSolutions.Count == 1 && UnmanagedSolutions[0].UniqueName == "Default")
                {
                    SelectedSolution = UnmanagedSolutions[0];
                    ActiveSolution = SelectedSolution;

                    SetActivePublisher();

                    //Advance the user to the Packages TabItem
                    TabControlSelectedIndex = 2;
                }
                else
                {
                    //Advance the user to the Solutions TabItem
                    TabControlSelectedIndex = 1;
                }

                OnPropertyChanged("UnmanagedSolutions");
                OnPropertyChanged("SelectedSolution");
                OnPropertyChanged("ActiveSolution");
                
            }
        }
        private void InstantiateService()
        {
            try
            {
                if (SelectedConfiguration == null)
                    throw new Exception("Please choose a configuration.");

                //Get the Password
                string password = String.Empty;
                PasswordWindow pw = new PasswordWindow();
                pw.Owner = Application.Current.MainWindow;
                bool? submitted = pw.ShowDialog();
                if (submitted.Value)
                {
                    password = pw.GetPassword();
                }
                else
                {
                    ErrorWindow needPassword = new ErrorWindow("You need to supply a Password and Submit. Try again.");
                    needPassword.Owner = Application.Current.MainWindow;
                    needPassword.ShowDialog();
                    return;
                }
                

                _serverConnect = new ConsolelessServerConnection();
                ConsolelessServerConnection.Configuration _config = new ConsolelessServerConnection.Configuration();

                _config = _serverConnect.GetServerConfiguration(
                    SelectedConfiguration.Attribute("server").Value,
                    SelectedConfiguration.Attribute("orgName").Value,
                    SelectedConfiguration.Attribute("userName").Value, 
                    password,
                    SelectedConfiguration.Attribute("domain").Value);


                _serviceProxy = _serverConnect.GetOrganizationServiceProxy(_config.OrganizationUri,_config.EndpointType);

                // This statement is required to enable early-bound type support.
                _serviceProxy.ServiceConfiguration.CurrentServiceEndpoint.Behaviors
                    .Add(new ProxyTypesBehavior());

                // The OrganizationServiceContext is an object that wraps the service
                // proxy and allows creating/updating multiple records simultaneously.
                _orgContext = new OrganizationServiceContext(_serviceProxy);

                //Set the ActiveConnection
                ActiveConfiguration = SelectedConfiguration;

                //If all worked, retrieve the solutions.
                LoadSolutions();

            }
            catch (Exception e)
            {
                StringBuilder sb = new StringBuilder();
                sb.AppendLine(e.Message);
                sb.AppendLine();
                sb.AppendLine("Please fix the Connection information and try again.");
                ErrorWindow errorWindow = new ErrorWindow(sb.ToString());
                errorWindow.Owner = Application.Current.MainWindow;
                var x = errorWindow.ShowDialog();
            }
        }
        private void ClearSolutions()
        {
            //Clear solutions
            UnmanagedSolutions.Clear();
            SelectedSolution = null;
            ActiveSolution = null;

            ActivePublisher = null;
        }   
        private void ActivateSelectedSolution()
        {
            if (SelectedSolution != null)
            {
                ActiveSolution = SelectedSolution;

                SetActivePublisher();

                OnPropertyChanged("ActiveSolution");

                //Advance the user to the Packages TabItem
                TabControlSelectedIndex = 2;
            }
        }
        private void SetActivePublisher()
        {
            if (ActiveSolution == null)
                return;

            var pub = from p in _orgContext.CreateQuery<Publisher>()
                        where p.PublisherId.Value =http://www.mamicode.com/= ActiveSolution.PublisherId.Id"ActivePublisher");

        }

        //Package Methods   
        private void LoadPackages()
        {
            Packages.Clear();
            var packages = XmlPackageData.Element("Packages").Descendants("Package");
            foreach (var p in packages)
            {
                Packages.Add(p);
            }
            OnPropertyChanged("Packages");
        }
        private void SavePackages()
        {
            //The user is influenced to believe a Save event will only 
            //save the ActivePackage but really it will save all of them.
            //Code is in place to prevent the user from editing one package then 
            //trying to load another without saving the first.

            //At this point the XmlRootData object is stale and needs to be 
            //repopulated with the Packages collection.
            XmlPackageData.Descendants("Package").Remove();

            //But the ActivePackage may have its Web Resources modified and they
            //need to be added back to the ActivePackage.
            if (ActivePackage != null)
            {
                ActivePackage.Elements("WebResourceInfo").Remove();
                ActivePackage.Add(WebResourceInfos.ToArray());
            }

            XmlPackageData.Element("Packages").Add(Packages.ToArray());

            XmlPackageData.Save(PACKAGES_FILENAME);

            IsActivePackageDirty = false;

        }
        private void DeleteSelectedPackage()
        {
            if (SelectedPackage != null)
            {
                var toBeDeleted = Packages.Where(x => x == SelectedPackage).FirstOrDefault();

                if (toBeDeleted != null)
                {
                    if (ActivePackage == SelectedPackage)
                    {
                        ActivePackage = null;
                        //Also, clear out any dependencies
                        CurrentFiles.Clear();
                        CurrentFilesSelected.Clear();
                        WebResourceInfos.Clear();
                        WebResourceInfosSelected.Clear();
                    }
                    Packages.Remove(toBeDeleted);
                    SelectedPackage = null;
                }
                SavePackages();
            }
        }
        private void ActivatePackage()
        {
            //Don‘t allow them to load a package without first saving
            //the ActivePackage if its dirty.
            if (ActivePackage != null && IsActivePackageDirty)
            {
                ErrorWindow dirtyPackageWindow =
                    new ErrorWindow("You have unsaved changes to the Active Package. Please save before loading another package.");
                dirtyPackageWindow.Owner = Application.Current.MainWindow;
                dirtyPackageWindow.ShowDialog();
                return;              
            }

            if (SelectedPackage != null)
            {
                ActivePackage = SelectedPackage;

                //Readies the Files DataGrid 
                SearchAndPopulateFiles();

                //Readies the Web Resources DataGrid
                LoadWebResourceInfos();
            }
        }
        private void CreateNewPackage()
        {
            if (ActivePackage != null)
            {
                SavePackages();
            }

            XElement newPackage = new XElement("Package",
                new XAttribute("name", "NewPackage"),
                new XAttribute("rootPath", String.Empty),
                new XAttribute("isNamePrefix", true));

            Packages.Add(newPackage);
            SelectedPackage = Packages[Packages.Count - 1];

            ActivatePackage();

            SavePackages();
        }
        private void LoadWebResourceInfos()
        {
            if (ActivePackage == null)
                return;

            //As always, clear the collection first.
            WebResourceInfos.Clear();
            WebResourceInfosSelected.Clear();

            var webResourceInfos = ActivePackage.Elements("WebResourceInfo");

            if (webResourceInfos != null)
            {
                foreach (var wr in webResourceInfos)
                {
                    WebResourceInfos.Add(wr);
                }
            }
        }
        private void SearchAndPopulateFiles()
        {
            if (ActivePackage == null)
                return;

            string searchText = FileSearchText; //Find all files

            string rootPath = ActivePackage.Attribute("rootPath").Value;
            DiscoverFiles(rootPath, searchText);

        }

        //Misc
        private void ShowBrowseFolderDialog()
        {
            System.Windows.Forms.FolderBrowserDialog dlgWRFolder = new System.Windows.Forms.FolderBrowserDialog()
            {
                Description = "Select the folder containing the potential Web Resource files",
                ShowNewFolderButton = false
            };
            if (dlgWRFolder.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {                
                IsActivePackageDirty = false;

                //Because Web Resources are relative to the root path, 
                //all the current Web ResourceInfos should be cleared
                WebResourceInfos.Clear();
                WebResourceInfosSelected.Clear();

                //Change the rootpath and notify all bindings
                ActivePackage.Attribute("rootPath").Value = http://www.mamicode.com/dlgWRFolder.SelectedPath;"ActivePackage");

                //Auto-save
                SavePackages();

                //Display new files
                SearchAndPopulateFiles();
            }
        }
        private void DiscoverFiles(string rootPath, string searchText)
        {
            CurrentFiles.Clear();
            CurrentFilesSelected.Clear();

            if (rootPath != String.Empty)
            {
                DirectoryInfo di = new DirectoryInfo(rootPath);

                var files = di.EnumerateFiles("*", SearchOption.AllDirectories)
                    .Where(f => ResourceExtensions.ValidExtensions.Contains(f.Extension))
                    .Where(f => f.Name != PACKAGES_FILENAME);                

                if (!string.IsNullOrWhiteSpace(searchText))
                {
                    files = files.Where(f => f.FullName.Contains(searchText));
                }

                foreach (FileInfo f in files)
                {
                    CurrentFiles.Add(f);                    
                }
                OnPropertyChanged("CurrentFiles");
            }
        }
        private void AddFilesToWebResources(object parameter)
        {
            //Set the ActivePackage as Dirty. 
            IsActivePackageDirty = true;

            //Clear the collection of selected files
            CurrentFilesSelected.Clear();

            //List<FileInfo> selectedFiles = new List<FileInfo>();
            if (parameter != null && parameter is IEnumerable)
            {
                foreach (var fileInfo in (IEnumerable)parameter)
                {
                    CurrentFilesSelected.Add((FileInfo)fileInfo);
                }
            }

            if (CurrentFilesSelected.Count > 0)
            {
                foreach (FileInfo fi in CurrentFilesSelected)
                {
                    //Add it to the list of web resource info, if not already there.
                    //The matching criteria will be the ?
                    
                    XElement newInfo = ConvertFileInfoToWebResourceInfo(fi);

                    if (WebResourceInfos.Where(w => w.Attribute("filePath").Value =http://www.mamicode.com/= newInfo.Attribute("filePath").Value).Count() == 0)
                    {
                        WebResourceInfos.Add(newInfo);                        
                    }
                    else
                    {
                        //it‘s already in the list! do nothing.
                    }
                }                
            }
        }
        private void DeleteSelectedWebResources(object parameter)
        {
            //Set the ActivePackage as Dirty. 
            IsActivePackageDirty = true;

            WebResourceInfosSelected.Clear();

            if (parameter != null && parameter is IEnumerable)
            {
                //Lists allow the ForEach extension method good for
                //removing items of a collection. Looping through an 
                //enumerable caused indexing errors after the first
                //iteration.
                List<XElement> infosToDelete = new List<XElement>();

                foreach (var wr in (IEnumerable)parameter)
                {
                    infosToDelete.Add((XElement)wr);
                }

                infosToDelete.ForEach(info => WebResourceInfos.Remove(info));
            }
        }
        private XElement ConvertFileInfoToWebResourceInfo(FileInfo fi)
        {
            var x = fi.Extension.Split(‘.‘);
            string type = x[x.Length - 1].ToLower();

            String name = fi.FullName.Replace(ActivePackage.Attribute("rootPath").Value.Replace("/", "\\"), String.Empty);

            XElement newWebResourceInfo = new XElement("WebResourceInfo",
                new XAttribute("name", name.Replace("\\", "/")),
                new XAttribute("filePath", name),
                new XAttribute("displayName", fi.Name),
                new XAttribute("type", type),
                new XAttribute("description", String.Empty));

            return newWebResourceInfo;
        }

        private void BeginUpload(object s, DoWorkEventArgs args)
        {
            //Retrieve all Web Resources so we can determine if each
            //needs be created or updated.
            var crmResources = RetrieveWebResourcesForActiveSolution();

            //Create or Update the WebResource
            if (WebResourceInfosSelected.Count > 0)
            {
                _progressMessage.Clear();
                AddMessage(String.Format("Processing {0} Web Resources...", WebResourceInfosSelected.Count.ToString()));
                int i = 1;

                foreach (XElement fi in WebResourceInfosSelected)
                {
                    string name = GetWebResourceFullNameIncludingPrefix(fi);
                    
                    AddMessage(String.Empty);
                    AddMessage(i.ToString() + ") " + name);

                    if (IsWebResourceNameValid(name))
                    {
                        //If the Unmanaged Solution already contains the Web Resource,
                        //do an Update.
                        var resourceThatMayExist = crmResources.Where(w => w.Name == name).FirstOrDefault();
                        if (resourceThatMayExist != null)
                        {
                            AddMessage("Already exists. Updating...");
                            UpdateWebResource(fi, resourceThatMayExist);
                            AddMessage("Done.");
                        }
                        //If not, create the Web Resource and a Solution Component.
                        else
                        {
                            AddMessage("Doesn‘t exist. Creating...");
                            CreateWebResource(fi);
                            AddMessage("Done.");
                        }
                    }
                    else
                    {
                        AddMessage(VALID_NAME_MSG);
                    }

                    i++;
                }
                
                AddMessage(String.Empty);
                AddMessage("Done processing files.");

                //All WebResources should be in. Publish all.
                if (ShouldPublishAllAfterUpload)
                {
                    AddMessage(String.Empty);
                    AddMessage("You chose to publish all customizations. Please be patient as it may take a few minutes to complete.");
                    PublishAll();
                }
                else if (ShouldPublishAllUploadedWebResource) 
                {
                    if (List_UploadedWebResources.Count > 0)
                    {
                        AddMessage(String.Empty);
                        AddMessage("You chose to publish uploaded webresources. Please be patient as it may take a few minutes to complete.");
                        PublishUploadedWebResource();
                    }
                }
                else
                {
                    AddMessage(String.Empty);
                    AddMessage("You chose not to publish all customizations.");
                }
                AddMessage("Process complete!");                
            }
        }

        List<Guid> List_UploadedWebResources;
        private void PublishUploadedWebResource()
        {
            try
            {
                AddMessage(String.Empty);
                AddMessage("Publishing uploaded...");

                PublishXmlRequest publishRequest = new PublishXmlRequest();

                StringBuilder sb = new StringBuilder();
              
                foreach (Guid webresId in List_UploadedWebResources)
                {
                    sb.Append("<webresource>" + webresId.ToString() + "</webresource>");
                }
                publishRequest.ParameterXml = string.Format(@"<importexportxml><webresources>{0}</webresources></importexportxml>"
                    , sb.ToString());
                var response = (PublishXmlResponse)_serviceProxy.Execute(publishRequest);
                
                AddMessage("Done.");
            }
            catch (Exception e)
            {
                AddMessage("Error publishing: " + e.Message);
            }
        }
        private void UploadWebResources(object parameter)
        {
            IsOutputWindowDisplayed = true;
            AreAllButtonsEnabled = false;

            //Clear the collection of selected Web Resources
            WebResourceInfosSelected.Clear();

            //Clear the collection of uploaded Web Resources
            List_UploadedWebResources.Clear();

            if (parameter != null && parameter is IEnumerable)
            {
                foreach (var webResource in (IEnumerable)parameter)
                {
                    WebResourceInfosSelected.Add((XElement)webResource);
                }
            }
            worker.RunWorkerAsync(WebResourceInfosSelected);
        }        
        private void UploadAllWebResources()
        {
            UploadWebResources(WebResourceInfos);            
        }

        private bool CanUploadWebResource()
        {
            if (ActiveConfiguration != null &&
                ActiveSolution != null &&
                ActivePublisher != null &&
                ActivePackage != null)
            {
                return true;
            }
            return false;
        }
        private void PublishAll()
        {
            try
            {
                AddMessage(String.Empty);
                AddMessage("Publishing all customizations...");     
                
                PublishAllXmlRequest publishRequest = new PublishAllXmlRequest();
                var response = (PublishAllXmlResponse)_serviceProxy.Execute(publishRequest);

                AddMessage("Done.");
            }
            catch (Exception e)
            {
                AddMessage("Error publishing: " + e.Message);
            }
        }        
        private IEnumerable<WebResource> RetrieveWebResourcesForActiveSolution()
        {
            //The following query finds all WebResources that are SolutionComponents for
            //the ActiveSolution. Simply querying WebResources does not retrieve the desired
            //results. Additionally, when creating WebResources, you must create a SolutionComponent
            //if attaching to any unmanaged solution other than the "Default Solution."
            var webResources = from wr in _orgContext.CreateQuery<WebResource>()
                               join sc in _orgContext.CreateQuery<SolutionComponent>()
                                 on wr.WebResourceId equals sc.ObjectId
                               where wr.IsManaged == false
                               where wr.IsCustomizable.Value =http://www.mamicode.com/= true"rootPath").Value + webResourceInfo.Attribute("filePath").Value),
                        DisplayName = webResourceInfo.Attribute("displayName").Value,
                        Description = webResourceInfo.Attribute("description").Value,
                        LogicalName = WebResource.EntityLogicalName,
                        Name = GetWebResourceFullNameIncludingPrefix(webResourceInfo)

                    };

                    wr.WebResourceType = new OptionSetValue((int)ResourceExtensions.ConvertStringExtension(webResourceInfo.Attribute("type").Value));

                    //Special cases attributes for different web resource types.
                    switch (wr.WebResourceType.Value)
                    {
                        case (int)ResourceExtensions.WebResourceType.Silverlight:
                            wr.SilverlightVersion = "4.0";
                            break;
                    }

                    // ActivePublisher.CustomizationPrefix + "_/" + ActivePackage.Attribute("name").Value + webResourceInfo.Attribute("name").Value.Replace("\\", "/"),
                    Guid theGuid = _serviceProxy.Create(wr);

                    List_UploadedWebResources.Add(theGuid);

                    //If not the "Default Solution", create a SolutionComponent to assure it gets
                    //associated with the ActiveSolution. Web Resources are automatically added
                    //as SolutionComponents to the Default Solution.
                    if (ActiveSolution.UniqueName != "Default")
                    {
                        AddSolutionComponentRequest scRequest = new AddSolutionComponentRequest();
                        scRequest.ComponentType = (int)componenttype.WebResource;
                        scRequest.SolutionUniqueName = ActiveSolution.UniqueName;
                        scRequest.ComponentId = theGuid;
                        var response = (AddSolutionComponentResponse)_serviceProxy.Execute(scRequest);
                    }
                }
                catch (Exception e)
                {
                    AddMessage("Error: " + e.Message);
                    return;
                }            
            
        }
        private void UpdateWebResource(XElement webResourceInfo, WebResource existingResource)
        {
            try
            {
                //These are the only 3 things that should (can) change.
                WebResource wr = new WebResource()
                {
                    Content = getEncodedFileContents(ActivePackage.Attribute("rootPath").Value + webResourceInfo.Attribute("filePath").Value),
                    DisplayName = webResourceInfo.Attribute("displayName").Value,
                    Description = webResourceInfo.Attribute("description").Value
                };
                wr.WebResourceId = existingResource.WebResourceId;
                _serviceProxy.Update(wr);

                List_UploadedWebResources.Add(existingResource.WebResourceId.Value);

            }
            catch (Exception e)
            {
                AddMessage("Error: " + e.Message);
                return;
            }
        }
        private string getEncodedFileContents(String pathToFile)
        {
        
            FileStream fs = new FileStream(pathToFile, FileMode.Open, FileAccess.Read);
            byte[] binaryData = http://www.mamicode.com/new byte[fs.Length];"GB2312"), binaryData);
            //return System.Convert.ToBase64String(convertedBytes, 0, convertedBytes.Length);
            #endregion


        }
        private string GetWebResourceFullNameIncludingPrefix(XElement webResourceInfo)
        {
            //The Web Resource name always starts with the Publisher‘s Prefix
            //i.e., "new_"
            string name = ActivePublisher.CustomizationPrefix + "_";

            //Check to see if the user has chosen to add the Package Name as part of the 
            //prefix.
            if (!String.IsNullOrWhiteSpace(ActivePackage.Attribute("isNamePrefix").Value) &&
                Boolean.Parse(ActivePackage.Attribute("isNamePrefix").Value) == true)
            {
                name += "/" + ActivePackage.Attribute("name").Value;
            }
                 
            //Finally add the name on to the prefix
            name += webResourceInfo.Attribute("name").Value;

            return name;
        }
        private bool IsWebResourceNameValid(string name)
        {
            Regex inValidWRNameRegex = new Regex("[^a-z0-9A-Z_\\./]|[/]{2,}", 
                (RegexOptions.Compiled | RegexOptions.CultureInvariant));

            bool result = true;

            //Test valid characters
            if (inValidWRNameRegex.IsMatch(name))
            {
                AddMessage(VALID_NAME_MSG);
                result = false;
            }

            //Test length
            //Remove the customization prefix and leading _ 
            if (name.Remove(0, ActivePublisher.CustomizationPrefix.Length + 1).Length > 100)
            {
                AddMessage("ERROR: Web Resource name must be <= 100 characters.");
                result = false;
            }
  
            return result;
        }
        private void AddMessage(string msg)
        {
            //Assures that this happens on the UI thread
            this.BeginInvoke(() =>
            {
                ProgressMessage = msg;
            });
        }

        #endregion

        
    }
}

  WebResourceView.xaml

  1 <UserControl
  2     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  5     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  6     mc:Ignorable="d"
  7     x:Class="Microsoft.Crm.Sdk.Samples.WebResourceView"
  8     xmlns:view="clr-namespace:Microsoft.Crm.Sdk.Samples"
  9     x:Name="UserControl" d:DesignWidth="500"
 10     >
 11 
 12     <UserControl.Resources>
 13         <view:BoolToVisibilityConverter x:Key="boolToVisibilityConverter" />
 14     </UserControl.Resources>
 15     <Border x:Name="LayoutRoot" CornerRadius="8,8,8,8" Background="LightGray" >
 16         <Grid>
 17             <Grid.RowDefinitions>
 18                 <RowDefinition Height="Auto"  />
 19                 <RowDefinition Height="Auto" />
 20                 <RowDefinition Height="Auto" />
 21                 <RowDefinition Height="Auto" />
 22                 <RowDefinition Height="Auto" />
 23                 <RowDefinition Height="Auto" />
 24             </Grid.RowDefinitions>
 25 
 26             <TextBlock Text="Load an existing package or create a new one."    
 27                 Margin="4"
 28                 FontWeight="Bold" 
 29                 FontSize="18.667" 
 30                 FontFamily="/WebResourceUtility;component/Fonts/#Arial" >
 31             </TextBlock>
 32 
 33             <TextBlock Grid.Row="1" 
 34                 Text="Packages" 
 35                 FontWeight="Bold" 
 36                 Margin="4,10,4,0"  />
 37             
 38             <!-- Packages -->
 39             <ScrollViewer Grid.Row="2" VerticalScrollBarVisibility="Auto" Height="Auto" MaxHeight="120" Margin="4,0,4,0" >
 40                 <DataGrid x:Name="WebResourcePackagesGrid"                                        
 41                     IsManipulationEnabled="False"
 42                     ItemsSource="{Binding Path=Packages}"
 43                     SelectedItem="{Binding Path=SelectedPackage, Mode=TwoWay}"
 44                     SelectionMode="Single" 
 45                     AutoGenerateColumns="False" 
 46                     IsReadOnly="True" >
 47                     <DataGrid.Columns>
 48                         <DataGridTextColumn Header="Package Name" Binding="{Binding Path=Attribute[name].Value}" Width="Auto" MinWidth="120" />
 49                         <DataGridTextColumn Header="Root Path" Binding="{Binding Path=Attribute[rootPath].Value}" Width="*" />
 50                     </DataGrid.Columns>
 51                 </DataGrid>
 52             </ScrollViewer>
 53 
 54             <!-- Package Commands -->
 55             <StackPanel Orientation="Horizontal" Grid.Row="3" Margin="0,0,0,20"
 56                 
 57                 HorizontalAlignment="Stretch">
 58                 <Button x:Name="LoadPackageButton" 
 59                 Command="{Binding Path=ActivateSelectedPackageCommand}" 
 60                 Content="Load Package" 
 61                 Margin="4,2,0,0" />                
 62                 
 63                 <Button x:Name="NewPackageButton" Command="{Binding Path=CreateNewPackageCommand}"  Content="New Package" Margin="4,2,0,0" Height="20.277" VerticalAlignment="Top"/>
 64                 <Button x:Name="DeletePackageButton" Command="{Binding Path=DeleteActivePackageCommand}"  Content="Delete" Margin="4,2,4,0" />                
 65             </StackPanel>
 66             
 67             
 68             <!-- Package Workstation -->
 69             <Border x:Name="OutputBorder"
 70                     Grid.Row="4" 
 71                     Margin="4,0,4,8" 
 72                     CornerRadius="8,8,8,8" 
 73                     Background="DarkGray" 
 74                     Visibility="{Binding Path=IsActivePackageSet, Converter={StaticResource boolToVisibilityConverter}}">
 75                 <Grid>
 76                     <Grid Grid.Row="0" Visibility="{Binding Path=IsWorkstationDisplayed, Converter={StaticResource boolToVisibilityConverter}}">
 77                     <Grid.RowDefinitions>
 78                         <RowDefinition Height="Auto"/>
 79                         <RowDefinition Height="Auto"/>
 80                         <RowDefinition Height="Auto"/>
 81                         <RowDefinition Height="Auto"/>
 82                         <RowDefinition Height="Auto"/>
 83                         <RowDefinition Height="Auto"/>
 84                     </Grid.RowDefinitions>
 85                     <Grid.ColumnDefinitions>
 86                         <ColumnDefinition />
 87                         <ColumnDefinition />
 88                         <ColumnDefinition />
 89                         <ColumnDefinition />
 90                     </Grid.ColumnDefinitions>
 91 
 92                     <Label Margin="4" HorizontalAlignment="Right" FontWeight="Bold">Package Name</Label>
 93                         <TextBox Margin="4" 
 94                             Grid.Column="1"
 95                             Grid.ColumnSpan="2"
 96                             ToolTip="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"
 97                             >
 98                             <TextBox.Text>
 99                                 <Binding Path="ActivePackage.Attribute[name].Value" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" >
100                                     <Binding.ValidationRules>
101                                         <view:RegexValidationRule Pattern="[^a-z0-9A-Z_\\./]|[/]{2,}" Message="Error: Can only contain alphnumeric, periods, underscores, and single forward slash" />
102                                     </Binding.ValidationRules>
103                                 </Binding>
104                             </TextBox.Text>
105                         </TextBox>
106                             <Label Grid.Row="1" HorizontalAlignment="Right" Margin="4" FontWeight="Bold">Root Path</Label>
107                     <TextBox IsEnabled="False" Text="{Binding Path=ActivePackage.Attribute[rootPath].Value, Mode=TwoWay}"
108                     Grid.Row="1" 
109                     Grid.Column="1"
110                     Grid.ColumnSpan="2"
111                     Margin="4"/>
112                     <Button x:Name="BrowseButton" 
113                             Command="{Binding BrowseFolderCommand}" 
114                             Content="Browse"
115                             Grid.Row="1"
116                             Margin="2,4,0,4"
117                             Grid.Column="3"
118                             Width="60"
119                             HorizontalAlignment="Left"/>
120 
121                     
122 
123                     <!-- File Search Section -->
124                     <GroupBox Grid.Row="2"
125                         Grid.ColumnSpan="4"                        
126                         Margin="4" >
127                         <GroupBox.Header>
128                             <TextBlock Text="File Search" FontWeight="Bold" />                  
129                         </GroupBox.Header>
130                         <Grid>
131                             <Grid.RowDefinitions>
132                                 <RowDefinition Height="Auto"/>
133                                 <RowDefinition Height="Auto"/>
134                                 <RowDefinition Height="Auto"/>
135                                 <RowDefinition Height="*"/>
136                                 <RowDefinition Height="Auto"/>
137                             </Grid.RowDefinitions>
138                             <Grid.ColumnDefinitions>
139                                 <ColumnDefinition Width="Auto" />
140                                 <ColumnDefinition Width="*" />
141                                 <ColumnDefinition Width="Auto" />
142                             </Grid.ColumnDefinitions>
143                             
144                             <Label VerticalAlignment="Bottom" HorizontalAlignment="Right" FontWeight="Bold">Search by Filename:</Label>
145 
146                             <TextBox x:Name="FileNameTextBox" 
147                             Text="{Binding Path=FileSearchText, Mode=TwoWay}" 
148                             Grid.Row="0" 
149                             Grid.Column="1" 
150                             />
151 
152                             <Button x:Name="RefreshFilesButton" 
153                                     Margin="4,0,0,0" 
154                                     Width="100"
155                                     Command="{Binding Path=RefreshFilesCommand}"                             
156                                     Content="Search" 
157                                     Grid.Row="0"
158                                     Grid.Column="3"
159                             />
160 
161                             <DataGrid x:Name="FileDataGrid" 
162                                 IsReadOnly="True"
163                                 SelectionMode="Extended" 
164                                 SelectionUnit="FullRow"
165                                 ItemsSource="{Binding Path=CurrentFiles}"                                
166                                 AutoGenerateColumns="False"
167                                 Grid.Row="3" 
168                                 Grid.ColumnSpan="3" 
169                                 Height="140"                                 
170                                 Margin="0,4,0,0" >
171                                 <DataGrid.Columns>
172                                     <DataGridTextColumn  Header="Type" Binding="{Binding Path=Extension}" />
173                                     <DataGridTextColumn  Header="File Name" Binding="{Binding Path=Name}"  />
174                                     <DataGridTextColumn  Header="Path" Binding="{Binding Path=FullName}"/>                                   
175                                 </DataGrid.Columns>
176                             </DataGrid>
177 
178                             
179 
180                             <!-- The DataGrid‘s SelectedItems property is read-only and can not be 
181                             bound via XAML. We need to have the SelectedItems to know which
182                             to add to the Web Resource datagrid. 
183                             Workaround:
184                             When the user clicks the Add Web Resource button, send a CommandParameter
185                             along with the Command that is bound to the grid‘s SelectedItems. In
186                             the RelayCommand, extract the Items and update the ViewModel accordingly.
187                             NOTE: This will only keep the ViewModel synced with the selectedrows when
188                             the button is clicked. Any changes thereafter are unrecognized until clicked
189                             again 
190                             -->
191                             
192                             <Button x:Name="AddFileButton" 
193                             Command="{Binding Path=ConvertFileToResourceCommand}"
194                             CommandParameter="{Binding ElementName=FileDataGrid, Path=SelectedItems}"
195                             Margin="0,2,0,4"
196                             Content="Add Files to Web Resources" 
197                             Grid.Row="4"
198                             Grid.Column="0"/>
199                         </Grid>
200                     </GroupBox>
201 
202                     <!-- Web Resources Section -->
203                     <GroupBox 
204                     Grid.Row="3"                    
205                     Grid.ColumnSpan="4"
206                     Margin="4,4,4,8">
207                         <GroupBox.Header>
208                             <TextBlock Text="Web Resources" FontWeight="Bold" />                  
209                         </GroupBox.Header>
210                         <Grid>
211                             <Grid.RowDefinitions>
212                                 <RowDefinition Height="Auto" />
213                                 <RowDefinition Height="Auto" />
214                                 <RowDefinition Height="*"/>
215                                 <RowDefinition Height="Auto"/>
216 
217                             </Grid.RowDefinitions>
218                             <Grid.ColumnDefinitions>
219                                 <ColumnDefinition Width="Auto" />
220                                 <ColumnDefinition Width="*" />
221                             </Grid.ColumnDefinitions>
222 
223                             <CheckBox Grid.Row="1" x:Name="PackageNamePrefixCheckBox" Content="Use Package Name as Web Resource namespace" 
224                                 IsChecked="{Binding Path=ActivePackage.Attribute[isNamePrefix].Value}"
225                                 Margin="0,4,0,0"
226                                 Grid.ColumnSpan="2"/>
227                             
228                             <StackPanel Grid.ColumnSpan="2"  Orientation="Horizontal">
229                                 <TextBlock >Web Resource name prefix:</TextBlock>
230                                 <TextBlock Margin="4,0,0,0" 
231                                         Text="{Binding Path=ActivePublisher.CustomizationPrefix}"></TextBlock>
232                                 <TextBlock Text="_"
233                                        Visibility="{Binding Path=IsActiveSolutionSet, Converter={StaticResource boolToVisibilityConverter}}"/>
234                                 <TextBlock Text="/"
235                                         Visibility="{Binding ElementName=PackageNamePrefixCheckBox, Path=IsChecked, Converter={StaticResource boolToVisibilityConverter}}" />
236                                 <TextBlock Text="{Binding Path=ActivePackage.Attribute[name].Value}"
237                                        Visibility="{Binding ElementName=PackageNamePrefixCheckBox, Path=IsChecked, Converter={StaticResource boolToVisibilityConverter}}"/>
238                                
239                             </StackPanel>
240                             
241                             
242                             <DataGrid x:Name="WebResourcesDataGrid" 
243                             ItemsSource="{Binding Path=WebResourceInfos}"                                
244                             SelectionMode="Extended" 
245                             AutoGenerateColumns="False"
246                             Grid.Row="2"
247                             Grid.ColumnSpan="2" 
248                             Height="140" Margin="0,4,0,0">
249                                 <DataGrid.Columns>
250                                     <DataGridTextColumn IsReadOnly="True" Header="Type" Binding="{Binding Path=Attribute[type].Value}" />                                    
251                                     <DataGridTextColumn Header="Name (editable)" Binding="{Binding Path=Attribute[name].Value}" />
252                                     <DataGridTextColumn Header="Display Name (editable)" Binding="{Binding Path=Attribute[displayName].Value, Mode=TwoWay}" />                                    
253                                     <DataGridTextColumn Header="Description (editable)" Binding="{Binding Path=Attribute[description].Value, Mode=TwoWay}" />                                                                   
254                                 </DataGrid.Columns>
255                             </DataGrid>                            
256 
257                             <Button Grid.Row="3" x:Name="RemoveWebResourceButton" Margin="0,2,0,0"
258                                 Content="Remove Web Resource" 
259                                 Command="{Binding Path=DeleteWebResourcesCommand}"
260                                 CommandParameter="{Binding ElementName=WebResourcesDataGrid, Path=SelectedItems}"                                    
261                                 />                                
262                             
263                         </Grid>
264                     </GroupBox>
265 
266                     <!-- Publish options-->
267                     <CheckBox HorizontalAlignment="Left" Margin="4"
268                             Grid.Row="4" Grid.ColumnSpan="4"
269                             IsChecked="{Binding Path=ShouldPublishAllUploadedWebResource, Mode=TwoWay}" 
270                             Content="Publish uploaded web resources." />
271                         
272                     <CheckBox HorizontalAlignment="Right" Margin="4"
273                             Grid.Row="4" Grid.ColumnSpan="4"
274                             IsChecked="{Binding Path=ShouldPublishAllAfterUpload, Mode=TwoWay}" 
275                             Content="Publish all customizations after uploading web resources." />
276 
277                         <!-- Package Commands -->
278                     <StackPanel 
279                         Grid.Row="5" 
280                         Grid.Column="0" 
281                         Grid.ColumnSpan="4" 
282                         Orientation="Horizontal" 
283                         HorizontalAlignment="Center" >
284                         <Button x:Name="SavePackageButton"
285                                 Command="{Binding Path=SaveActivePackageCommand}"  
286                                 Content="Save Package" 
287                                 Margin="4"
288                                 Grid.Row="4"
289                                 Grid.ColumnSpan="4" Width="120" Height="30"/>
290                         <Button x:Name="DeployAllButton"
291                                 Width="120"
292                                 Command="{Binding Path=UploadAllWebResourcesCommand}"                                
293                                 Margin="4"
294                                 Content="Upload All" 
295                                 />
296                         <Button x:Name="DeployWebResourcesButton"
297                                 Command="{Binding Path=UploadWebResourcesCommand}"
298                                 Width="120"
299                                 CommandParameter="{Binding ElementName=WebResourcesDataGrid, Path=SelectedItems}"
300                                 Margin="4"
301                                 Content="Upload Selected" 
302                                 />
303                         <Button  Command="{Binding Path=ShowOutputWindow}"
304                                 HorizontalAlignment="Right"                               
305                                 Margin="4"
306                                 Content="Show Output" 
307                                 />
308                         </StackPanel>
309 
310                 </Grid>
311                     <Grid Grid.Row="0"
312                           Visibility="{Binding Path=IsOutputWindowDisplayed, Converter={StaticResource boolToVisibilityConverter}}"
313                           MaxHeight="{Binding ElementName=OutputBorder, Path=ActualHeight}">
314                         <Grid.RowDefinitions>
315                             <RowDefinition Height="Auto" />
316                             <RowDefinition Height="*"  />
317                             <RowDefinition Height="Auto" />
318                         </Grid.RowDefinitions>
319                         <TextBlock Text="Output" FontWeight="Bold" Margin="4" />
320                         <ScrollViewer Grid.Row="1"
321                                       Margin="4"
322                                       HorizontalScrollBarVisibility="Visible" 
323                                       VerticalScrollBarVisibility="Visible" >
324                             <TextBox IsReadOnly="True" Text="{Binding Path=ProgressMessage}" />
325                         </ScrollViewer>
326                         <Button Grid.Row="2" Content="Hide Output" Command="{Binding Path=HideOutputWindow}" Width="140" Margin="4" />
327                     </Grid>
328                 </Grid>
329             </Border>
330 
331             
332         </Grid>
333     </Border>
334 
335 </UserControl>

 

Dynamics 365 WebResourceUtility 工具修复部分问题