diff --git a/src/SIM.Base/SolrStateResolver.cs b/src/SIM.Base/SolrStateResolver.cs index 67687f5d..616d415e 100644 --- a/src/SIM.Base/SolrStateResolver.cs +++ b/src/SIM.Base/SolrStateResolver.cs @@ -10,6 +10,23 @@ namespace SIM { public class SolrStateResolver { + public virtual bool IsSolrAvailable(string solrUrl) + { + try + { + var request = WebRequest.Create(solrUrl) as HttpWebRequest; + request.Method = "HEAD"; + using (var response = request.GetResponse() as HttpWebResponse) + { + return response.StatusCode == HttpStatusCode.OK; + } + } + catch (WebException) + { + return false; + } + } + public virtual SolrState.CurrentState GetServiceState(ServiceControllerWrapper service) { if (service != null) @@ -76,6 +93,27 @@ public virtual string GetVersion(string solrUrl) return string.Empty; } + + public virtual string GetSeparateSolrAttribute(string solrUrl, string attributName) + { + HttpClient client = new HttpClient(); + + using (Stream stream = client.GetStreamAsync($"{solrUrl}/admin/info/system?wt=json").Result) + using (StreamReader streamReader = new StreamReader(stream)) + using (JsonReader reader = new JsonTextReader(streamReader)) + { + while (reader.Read()) + { + if (string.Equals(reader.Path, attributName, StringComparison.OrdinalIgnoreCase) + && !string.Equals((string)reader.Value, attributName, StringComparison.OrdinalIgnoreCase)) + { + return (string)reader.Value; + } + } + } + + return string.Empty; + } } public class ServiceControllerWrapper diff --git a/src/SIM.Pipelines/InstallSearchIndexes/CreateIndexDirectoryAction.cs b/src/SIM.Pipelines/InstallSearchIndexes/CreateIndexDirectoryAction.cs new file mode 100644 index 00000000..042d03d1 --- /dev/null +++ b/src/SIM.Pipelines/InstallSearchIndexes/CreateIndexDirectoryAction.cs @@ -0,0 +1,47 @@ +using JetBrains.Annotations; +using SIM.Extensions; +using Sitecore.Diagnostics.Base; + +namespace SIM.Pipelines.InstallSearchIndexes +{ + public class CreateIndexDirectoryAction : InstallSearchIndexesProcessor + { + protected override void Process([NotNull] InstallSearchIndexesArgs args) + { + Assert.ArgumentNotNull(args, nameof(args)); + + foreach (var index in args._AvailableSearchIndexesDictionary) + { + string newCorePath = args.SolrFolder.EnsureEnd(@"\") + index.Value; + CreateIndexDirectory(args.SolrVersion, args.SolrFolder, newCorePath); + } + } + + private void CreateIndexDirectory(string solrVersion, string solrFolder, string newCorePath) + { + string sourcePath = GetSourceConfPath(solrVersion, solrFolder); + FileSystem.FileSystem.Local.Directory.Copy(sourcePath, newCorePath, recursive: true); + } + + private string GetSourceConfPath(string solrVersion, string solrFolder) + { + string confPath = string.Empty; + + if (!string.IsNullOrEmpty(solrVersion) && char.IsDigit(solrVersion[0])) + { + int firstDigit = int.Parse(solrVersion[0].ToString()); + + if (firstDigit >= 7) + { + confPath = solrFolder.EnsureEnd(@"\") + @"\configsets\_default"; + } + else + { + confPath = solrFolder.EnsureEnd(@"\") + @"\configsets\data_driven_schema_configs"; + } + } + + return confPath; + } + } +} diff --git a/src/SIM.Pipelines/InstallSearchIndexes/CreateSolrCoreAction.cs b/src/SIM.Pipelines/InstallSearchIndexes/CreateSolrCoreAction.cs new file mode 100644 index 00000000..950bc691 --- /dev/null +++ b/src/SIM.Pipelines/InstallSearchIndexes/CreateSolrCoreAction.cs @@ -0,0 +1,36 @@ +using JetBrains.Annotations; +using SIM.Extensions; +using Sitecore.Diagnostics.Base; +using System.IO; + +namespace SIM.Pipelines.InstallSearchIndexes +{ + public class CreateSolrCoreAction : InstallSearchIndexesProcessor + { + protected override void Process([NotNull] InstallSearchIndexesArgs args) + { + Assert.ArgumentNotNull(args, nameof(args)); + + foreach (var index in args._AvailableSearchIndexesDictionary) + { + string newCorePath = args.SolrFolder.EnsureEnd(@"\") + index.Value; + + RequestAndGetResponseStream($"{args.SolrUrl}/admin/cores?action=CREATE&name={index.Value}&instanceDir={newCorePath}&config=solrconfig.xml&schema=schema.xml&dataDir=data"); + UpdateCorePropertiesFile(index.Value, newCorePath); + } + } + + private Stream RequestAndGetResponseStream(string url) + { + return WebRequestHelper.RequestAndGetResponse(url).GetResponseStream(); + } + + private void UpdateCorePropertiesFile(string coreName, string newCorePath) + { + string filePath = string.Format(newCorePath.EnsureEnd(@"\") + @"core.properties"); + string newText = @"update.autoCreateFields=false" + "\r\n" + "name=" + coreName; + FileSystem.FileSystem.Local.File.Delete(filePath); + FileSystem.FileSystem.Local.File.WriteAllText(filePath, newText); + } + } +} diff --git a/src/SIM.Pipelines/InstallSearchIndexes/InstallSearchIndexesArgs.cs b/src/SIM.Pipelines/InstallSearchIndexes/InstallSearchIndexesArgs.cs new file mode 100644 index 00000000..6e299a21 --- /dev/null +++ b/src/SIM.Pipelines/InstallSearchIndexes/InstallSearchIndexesArgs.cs @@ -0,0 +1,49 @@ +using JetBrains.Annotations; +using SIM.Instances; +using SIM.Pipelines.Processors; +using Sitecore.Diagnostics.Base; +using System.Collections.Generic; + +namespace SIM.Pipelines.InstallSearchIndexes +{ + public class InstallSearchIndexesArgs : ProcessorArgs + { + [UsedImplicitly] + public string InstanceName + { + get + { + return Instance != null ? Instance.Name : string.Empty; + } + } + + public Dictionary _AvailableSearchIndexesDictionary; + + public string SolrUrl; + + public string SolrVersion; + + public string SolrFolder; + public Instance Instance { get; } + + public string AuthCookies { get; } + + public IDictionary Headers { get; } + + public InstallSearchIndexesArgs([NotNull] Instance instance, [CanBeNull] Dictionary availableSearchIndexesDictionary, [NotNull] string solrUrl, [NotNull] string solrVersion, [NotNull] string solrFolder, [CanBeNull] IDictionary headers = null, [CanBeNull] string cookies = null) + { + Assert.ArgumentNotNull(instance, nameof(instance)); + Assert.ArgumentNotNull(solrUrl, nameof(solrUrl)); + Assert.ArgumentNotNull(solrVersion, nameof(solrVersion)); + Assert.ArgumentNotNull(solrFolder, nameof(solrFolder)); + + SolrUrl = solrUrl; + SolrVersion = solrVersion; + SolrFolder = solrFolder; + _AvailableSearchIndexesDictionary = availableSearchIndexesDictionary; + Instance = instance; + AuthCookies = cookies; + Headers = headers; + } + } +} diff --git a/src/SIM.Pipelines/InstallSearchIndexes/InstallSearchIndexesProcessor.cs b/src/SIM.Pipelines/InstallSearchIndexes/InstallSearchIndexesProcessor.cs new file mode 100644 index 00000000..83b0804a --- /dev/null +++ b/src/SIM.Pipelines/InstallSearchIndexes/InstallSearchIndexesProcessor.cs @@ -0,0 +1,46 @@ +using JetBrains.Annotations; +using SIM.Pipelines.Processors; +using Sitecore.Diagnostics.Base; + +namespace SIM.Pipelines.InstallSearchIndexes +{ + public abstract class InstallSearchIndexesProcessor : Processor + { + public override sealed long EvaluateStepsCount(ProcessorArgs args) + { + Assert.ArgumentNotNull(args, nameof(args)); + + return EvaluateStepsCount((InstallSearchIndexesArgs)args); + } + + public override sealed bool IsRequireProcessing(ProcessorArgs args) + { + Assert.ArgumentNotNull(args, nameof(args)); + + return IsRequireProcessing((InstallSearchIndexesArgs)args); + } + + protected virtual long EvaluateStepsCount([NotNull] InstallSearchIndexesArgs args) + { + Assert.ArgumentNotNull(args, nameof(args)); + + return 1; + } + + protected virtual bool IsRequireProcessing([NotNull] InstallSearchIndexesArgs args) + { + Assert.ArgumentNotNull(args, nameof(args)); + + return true; + } + + protected override void Process(ProcessorArgs args) + { + Assert.ArgumentNotNull(args, nameof(args)); + + Process((InstallSearchIndexesArgs)args); + } + + protected abstract void Process([NotNull] InstallSearchIndexesArgs args); + } +} diff --git a/src/SIM.Pipelines/InstallSearchIndexes/PopulateNewIndexAction.cs b/src/SIM.Pipelines/InstallSearchIndexes/PopulateNewIndexAction.cs new file mode 100644 index 00000000..12488f32 --- /dev/null +++ b/src/SIM.Pipelines/InstallSearchIndexes/PopulateNewIndexAction.cs @@ -0,0 +1,60 @@ +using JetBrains.Annotations; +using SIM.Extensions; +using SIM.Instances; +using Sitecore.Diagnostics.Base; +using System; +using System.Collections.Generic; + +namespace SIM.Pipelines.InstallSearchIndexes +{ + public class PopulateNewIndexAction : InstallSearchIndexesProcessor + { + protected override void Process([NotNull] InstallSearchIndexesArgs args) + { + Assert.ArgumentNotNull(args, nameof(args)); + + foreach (var index in args._AvailableSearchIndexesDictionary) + { + string newCorePath = args.SolrFolder.EnsureEnd(@"\") + index.Value; + PopulateNewIndex(index.Key, args.Instance, args.Headers, args.AuthCookies); + } + } + + private void PopulateNewIndex(string coreID, Instance instance, IDictionary headers, string authCookie) + { + var popUrl = instance.GetUrl(@"/sitecore/admin/PopulateManagedSchema.aspx?indexes=" + coreID); + + try + { + int sitecoreVersion; + + int.TryParse(instance.Product.ShortVersion, out sitecoreVersion); + + if (sitecoreVersion >= 91) + { + if (headers == null) + { + return; + } + var result = WebRequestHelper.DownloadString(popUrl, headers: headers).Trim(); + + } + else if (sitecoreVersion == 90) + { + string instanceUri = instance.GetUrl(); + + if (string.IsNullOrEmpty(authCookie)) + { + return; + } + var result = WebRequestHelper.DownloadString(popUrl, cookies: authCookie).Trim(); + } + } + catch (Exception ex) + { + return; + } + } + + } +} diff --git a/src/SIM.Pipelines/InstallSearchIndexes/UpdateManagedSchemaAction.cs b/src/SIM.Pipelines/InstallSearchIndexes/UpdateManagedSchemaAction.cs new file mode 100644 index 00000000..600ea02f --- /dev/null +++ b/src/SIM.Pipelines/InstallSearchIndexes/UpdateManagedSchemaAction.cs @@ -0,0 +1,49 @@ +using JetBrains.Annotations; +using SIM.Extensions; +using Sitecore.Diagnostics.Base; +using System.Xml; + +namespace SIM.Pipelines.InstallSearchIndexes +{ + public class UpdateManagedSchemaAction : InstallSearchIndexesProcessor + { + protected override void Process([NotNull] InstallSearchIndexesArgs args) + { + Assert.ArgumentNotNull(args, nameof(args)); + + foreach (var index in args._AvailableSearchIndexesDictionary) + { + string newCorePath = args.SolrFolder.EnsureEnd(@"\") + index.Value; + + UpdateManagedSchemaFile(newCorePath); + } + } + + private void UpdateManagedSchemaFile(string newCorePath) + { + XmlDocument doc = new XmlDocument(); + doc.Load(newCorePath.EnsureEnd(@"\") + @"conf\managed-schema"); + XmlElement newField = doc.CreateElement("field"); + newField.SetAttribute("name", "_uniqueid"); + newField.SetAttribute("type", "string"); + newField.SetAttribute("indexed", "true"); + newField.SetAttribute("required", "true"); + newField.SetAttribute("stored", "true"); + XmlNode schemaNode = doc.SelectSingleNode("/schema"); + + if (schemaNode != null) + { + schemaNode.AppendChild(newField); + } + + XmlNode uniqueKeyNode = doc.SelectSingleNode("/schema/uniqueKey"); + if (uniqueKeyNode != null) + + { + uniqueKeyNode.InnerText = "_uniqueid"; + } + + doc.Save(newCorePath.EnsureEnd(@"\") + @"conf\managed-schema"); + } + } +} diff --git a/src/SIM.Pipelines/PipelinesConfig.cs b/src/SIM.Pipelines/PipelinesConfig.cs index 7173ae0c..d80be067 100644 --- a/src/SIM.Pipelines/PipelinesConfig.cs +++ b/src/SIM.Pipelines/PipelinesConfig.cs @@ -237,6 +237,12 @@ public static class PipelinesConfig + + + + + + diff --git a/src/SIM.Pipelines/SIM.Pipelines.csproj b/src/SIM.Pipelines/SIM.Pipelines.csproj index 1723277e..cfac5117 100644 --- a/src/SIM.Pipelines/SIM.Pipelines.csproj +++ b/src/SIM.Pipelines/SIM.Pipelines.csproj @@ -108,6 +108,12 @@ + + + + + + diff --git a/src/SIM.Tool.Base/Pipelines/InstallSearchIndexesWizardArgs.cs b/src/SIM.Tool.Base/Pipelines/InstallSearchIndexesWizardArgs.cs new file mode 100644 index 00000000..bfd56fa0 --- /dev/null +++ b/src/SIM.Tool.Base/Pipelines/InstallSearchIndexesWizardArgs.cs @@ -0,0 +1,82 @@ +using JetBrains.Annotations; +using SIM.Extensions; +using SIM.Instances; +using SIM.Pipelines.InstallModules; +using SIM.Pipelines.InstallSearchIndexes; +using SIM.Pipelines.Processors; +using SIM.Tool.Base.Wizards; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; + +namespace SIM.Tool.Base.Pipelines +{ + [UsedImplicitly] + public class InstallSearchIndexesWizardArgs : WizardArgs + { + [UsedImplicitly] + public string InstanceName + { + get + { + return Instance != null ? Instance.Name : string.Empty; + } + } + + public List _AvailableSearchIndexesList; + + public Dictionary _AvailableSearchIndexesDictionary; + + public Instance Instance { get; } + + public string SolrUrl; + + public string SolrVersion; + + public string SolrFolder; + + public IDictionary Headers { get; } + + public string AuthCookies { get; } + + public InstallSearchIndexesWizardArgs(Instance instance, Dictionary availableSearchIndexesDictionary, string solrUrl, string solrVersion, string solrFolder, IDictionary headers = null, string cookies = null) + { + SolrUrl = solrUrl; + SolrVersion = solrVersion; + SolrFolder = solrFolder; + _AvailableSearchIndexesList = new List(); + + foreach (var availableIndex in availableSearchIndexesDictionary) + { + _AvailableSearchIndexesList.Add(availableIndex.Value); + } + _AvailableSearchIndexesDictionary = availableSearchIndexesDictionary; + Instance = instance; + AuthCookies = cookies; + Headers = headers; + } + + public override ProcessorArgs ToProcessorArgs() + { + Dictionary newAvailableSearchIndexesDictionary = new Dictionary(); + + foreach (var index in _AvailableSearchIndexesDictionary) + { + foreach (var availableSearchIndex in _AvailableSearchIndexesList) + { + if (availableSearchIndex == index.Value) + { + newAvailableSearchIndexesDictionary.Add(index.Key, index.Value); + } + } + } + + return new InstallSearchIndexesArgs(Instance, newAvailableSearchIndexesDictionary, SolrUrl, SolrVersion, SolrFolder, Headers, AuthCookies); + } + + } +} diff --git a/src/SIM.Tool.Base/SIM.Tool.Base.csproj b/src/SIM.Tool.Base/SIM.Tool.Base.csproj index 26fbb9ea..7792dd54 100644 --- a/src/SIM.Tool.Base/SIM.Tool.Base.csproj +++ b/src/SIM.Tool.Base/SIM.Tool.Base.csproj @@ -82,6 +82,7 @@ + diff --git a/src/SIM.Tool.Windows/Images/16/search_index.png b/src/SIM.Tool.Windows/Images/16/search_index.png new file mode 100644 index 00000000..1a9fbc79 Binary files /dev/null and b/src/SIM.Tool.Windows/Images/16/search_index.png differ diff --git a/src/SIM.Tool.Windows/Images/24/search_index.png b/src/SIM.Tool.Windows/Images/24/search_index.png new file mode 100644 index 00000000..8076119c Binary files /dev/null and b/src/SIM.Tool.Windows/Images/24/search_index.png differ diff --git a/src/SIM.Tool.Windows/Images/32/search_index.png b/src/SIM.Tool.Windows/Images/32/search_index.png new file mode 100644 index 00000000..58b1295a Binary files /dev/null and b/src/SIM.Tool.Windows/Images/32/search_index.png differ diff --git a/src/SIM.Tool.Windows/Images/48/search_index.png b/src/SIM.Tool.Windows/Images/48/search_index.png new file mode 100644 index 00000000..de82aca8 Binary files /dev/null and b/src/SIM.Tool.Windows/Images/48/search_index.png differ diff --git a/src/SIM.Tool.Windows/MainWindowComponents/Buttons/AddSolrCoresButton.cs b/src/SIM.Tool.Windows/MainWindowComponents/Buttons/AddSolrCoresButton.cs new file mode 100644 index 00000000..7d5f163b --- /dev/null +++ b/src/SIM.Tool.Windows/MainWindowComponents/Buttons/AddSolrCoresButton.cs @@ -0,0 +1,170 @@ +using SIM.Instances; +using SIM.Tool.Base.Wizards; +using Sitecore.Diagnostics.Base; +using System.Collections.Generic; +using System.IO; +using System.Windows; +using System.Xml; +using Newtonsoft.Json; +using SIM.Tool.Windows.MainWindowComponents.Helpers; +using SIM.Tool.Base.Pipelines; +using SIM.Tool.Base; + +namespace SIM.Tool.Windows.MainWindowComponents.Buttons +{ + public class AddSolrCoresButton : InstanceOnlyButton + { + public override void OnClick(Window mainWindow, Instance instance) + { + if (instance != null) + { + + SolrStateResolver solrStateResolver = new SolrStateResolver(); + ButtonAuthenticationHelper buttonAuthenticationHelper = new ButtonAuthenticationHelper(); + XmlDocument sitecoreWebResultConfig = instance.GetWebResultConfig(); + string solrUrl = GetUrlFrom(sitecoreWebResultConfig); + + if (!solrStateResolver.IsSolrAvailable(solrUrl)) + { + WindowHelper.ShowMessage($"Failed to access Solr at the following URL: {solrUrl}.\r\n Solr is not available.\r\nPlease make sure Solr is running and accessible.", + messageBoxImage: MessageBoxImage.Warning, + messageBoxButton: MessageBoxButton.OK); + + return; + } + + string solrAttribute = "solr_home"; + string solrFolder = solrStateResolver.GetSeparateSolrAttribute(solrUrl, solrAttribute); + string solrVersion = solrStateResolver.GetVersion(solrUrl); + XmlNodeList existingSolrCores = GetExistingCoresFromSolr(solrUrl); + Dictionary indexDataDictionary = GetindexDataListFromConfig(sitecoreWebResultConfig); + Dictionary availableSearchIndexesDictionary = new Dictionary(); + + Dictionary headers = null; + string authCookie = null; + + int.TryParse(instance.Product.ShortVersion, out int sitecoreVersion); + + if (sitecoreVersion >= 91) + { + headers = buttonAuthenticationHelper.GetIdServerAuthToken(mainWindow, instance); + + if (headers == null) + { + return; + } + } + else if (sitecoreVersion == 90) + { + string instanceUri = instance.GetUrl(); + + authCookie = buttonAuthenticationHelper.GetAuthCookie(mainWindow, instance); + + if (string.IsNullOrEmpty(authCookie)) + { + return; + } + } + + foreach (var indexData in indexDataDictionary) + { + string coreName; + string coreID; + + if (!string.IsNullOrEmpty(indexData.Value) && indexData.Value != "$(id)") + { + coreID = indexData.Key; + coreName = indexData.Value; + } + else + { + coreID = indexData.Key; + coreName = indexData.Key; + } + + bool itemMatched = false; + + foreach (XmlElement existingSolrnode in existingSolrCores) + { + string existingCoreName = existingSolrnode["name"].InnerText; + if (coreName == existingCoreName) + { + itemMatched = true; + break; + } + } + + if (!itemMatched) + { + availableSearchIndexesDictionary.Add(coreID, coreName); + } + } + + var id = MainWindowHelper.GetListItemID(instance.ID); + WizardPipelineManager.Start("installSearchIndexes", mainWindow, null, null, ignore => MainWindowHelper.MakeInstanceSelected(id), () => new InstallSearchIndexesWizardArgs(instance, availableSearchIndexesDictionary, solrUrl, solrVersion, solrFolder, headers, authCookie)); + } + } + + private Dictionary GetindexDataListFromConfig(XmlDocument sitecoreConfig) + { + Dictionary indexData = new Dictionary(); + + foreach (XmlNode indexNode in sitecoreConfig.SelectNodes("//sitecore/contentSearch/configuration/indexes/index")) + { + string indexId = indexNode.Attributes["id"]?.Value; + string coreName = indexNode.SelectSingleNode("param[@desc='core']")?.InnerText; + + if (!string.IsNullOrEmpty(indexId) && !string.IsNullOrEmpty(coreName)) + { + indexData.Add(indexId, coreName); + } + } + + return indexData; + } + + private XmlNodeList GetExistingCoresFromSolr(string solrUrl) + { + var solrInfoUrl = $"{solrUrl}/admin/cores?action=STATUS&wt=json"; + var doc = GetXmlDocumenFromSolrResponse(solrInfoUrl); + XmlNodeList solrIndexes = doc.SelectNodes("//status/*"); + return solrIndexes; + } + + private static string GetUrlFrom(XmlDocument sitecoreConfig) + { + string connectionStringName = "solr.search"; + XmlNode connectionStringNode = sitecoreConfig.SelectSingleNode("//connectionStrings/add[@name='" + connectionStringName + "']"); + Assert.IsNotNull(connectionStringNode, "ConnectionString with name '" + connectionStringName + "' not found."); + return connectionStringNode.Attributes["connectionString"].Value; + } + + private XmlDocumentEx GetXmlDocumenFromSolrResponse(string solrInfoUrl) + { + Stream response = RequestAndGetResponseStream(solrInfoUrl); + string responseAsString = GetStringFromStream(response); + string xmlString = ReturnXml(responseAsString); + var doc = XmlDocumentEx.LoadXml(xmlString); + return doc; + } + + private Stream RequestAndGetResponseStream(string url) + { + return WebRequestHelper.RequestAndGetResponse(url).GetResponseStream(); + } + + private string GetStringFromStream(Stream stream) + { + using (var reader = new StreamReader(stream)) + { + return reader.ReadToEnd(); + } + } + + private string ReturnXml(string jsonObject) + { + string xmlString = JsonConvert.DeserializeXmlNode(jsonObject, "response").OuterXml; + return xmlString; + } + } +} diff --git a/src/SIM.Tool.Windows/MainWindowComponents/Buttons/InstallModulesForSitecore9AndLaterButton.cs b/src/SIM.Tool.Windows/MainWindowComponents/Buttons/InstallModulesForSitecore9AndLaterButton.cs index 06fa88cb..18a17451 100644 --- a/src/SIM.Tool.Windows/MainWindowComponents/Buttons/InstallModulesForSitecore9AndLaterButton.cs +++ b/src/SIM.Tool.Windows/MainWindowComponents/Buttons/InstallModulesForSitecore9AndLaterButton.cs @@ -1,16 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Net; -using System.Threading.Tasks; +using System.Collections.Generic; using System.Windows; using JetBrains.Annotations; -using SIM.Core; using SIM.Instances; -using SIM.Tool.Base; using SIM.Tool.Base.Pipelines; using SIM.Tool.Base.Wizards; -using SIM.Tool.Windows.Dialogs; -using SIM.Tool.Windows.UserControls.SitecoreAuthentication; +using SIM.Tool.Windows.MainWindowComponents.Helpers; namespace SIM.Tool.Windows.MainWindowComponents.Buttons { @@ -22,176 +16,35 @@ public override void OnClick(Window mainWindow, Instance instance) if (instance != null) { Dictionary headers = null; - string authCookieOrIdServerAuthToken = null; + string authCookie = null; int.TryParse(instance.Product.ShortVersion, out int sitecoreVersion); + ButtonAuthenticationHelper buttonAuthenticationHelper = new ButtonAuthenticationHelper(); + if (sitecoreVersion >= 91) { - string sitecoreIdServerUri = "https://" + instance.SitecoreEnvironment.Name + "Id.local"; - authCookieOrIdServerAuthToken = this.GetAuthToken(sitecoreIdServerUri, CoreAppSettings.AppLoginAsAdminUserName.Value, CoreAppSettings.AppLoginAsAdminNewPassword.Value, mainWindow); - authCookieOrIdServerAuthToken = this.ValidateAuthToken(authCookieOrIdServerAuthToken, sitecoreIdServerUri, - CoreAppSettings.AppLoginAsAdminUserName.Value, CoreAppSettings.AppLoginAsAdminNewPassword.Value, mainWindow); + headers = buttonAuthenticationHelper.GetIdServerAuthToken(mainWindow, instance); - if (string.IsNullOrEmpty(authCookieOrIdServerAuthToken)) + if (headers == null) { return; - } - - headers = new Dictionary { { "Authorization", authCookieOrIdServerAuthToken } }; + } } else if (sitecoreVersion == 90) { string instanceUri = instance.GetUrl(); - if (!this.IsInstanceUriValid(instanceUri)) - { - return; - } - - authCookieOrIdServerAuthToken = this.GetAuthCookie(instanceUri, CoreAppSettings.AppLoginAsAdminUserName.Value, CoreAppSettings.AppLoginAsAdminNewPassword.Value, mainWindow); - authCookieOrIdServerAuthToken = this.ValidateAuthCookie(authCookieOrIdServerAuthToken, instanceUri, - CoreAppSettings.AppLoginAsAdminUserName.Value, CoreAppSettings.AppLoginAsAdminNewPassword.Value, mainWindow); + authCookie = buttonAuthenticationHelper.GetAuthCookie(mainWindow, instance); - if (string.IsNullOrEmpty(authCookieOrIdServerAuthToken)) + if (string.IsNullOrEmpty(authCookie)) { return; } } var id = MainWindowHelper.GetListItemID(instance.ID); - WizardPipelineManager.Start("installmodules", mainWindow, null, null, ignore => MainWindowHelper.MakeInstanceSelected(id), () => new InstallModulesWizardArgs(instance, authCookieOrIdServerAuthToken, headers)); - } - } - - private string GetAuthToken(string sitecoreIdServerUri, string userName, string password, Window mainWindow) - { - string idServerAuthToken = null; - - WindowHelper.LongRunningTask(() => - { - var task = Task.Run(async () => { - idServerAuthToken = await - SitecoreIdServerAuth.GetToken(sitecoreIdServerUri, userName, password); - }); - task?.Wait(); - }, "Get authentication token", mainWindow, "Getting authentication token", "", true); - - return idServerAuthToken; - } - - private string ValidateAuthToken(string idServerAuthToken, string sitecoreIdServerUri, string userName, string password, Window mainWindow) - { - if (SitecoreIdServerAuth.CurrentHttpStatusCode == HttpStatusCode.InternalServerError) - { - WindowHelper.HandleError("Unable to get authentication token using the following Sitecore Identity Server URI:\n\n" + - sitecoreIdServerUri, true); - return null; - } - else if (SitecoreIdServerAuth.CurrentHttpStatusCode != HttpStatusCode.OK && SitecoreIdServerAuth.CurrentHttpStatusCode != HttpStatusCode.BadRequest) - { - WindowHelper.ShowMessage( - "Unable to get authentication token using the following Sitecore Identity Server URI:\n\n" + - sitecoreIdServerUri + - "\n\nThe '" + SitecoreIdServerAuth.CurrentHttpStatusCode + "' status code has been returned." + - "\n\nPlease make sure that this Sitecore Identity Server is running.", - MessageBoxButton.OK, - MessageBoxImage.Warning); - return null; - } - - if (SitecoreIdServerAuth.CurrentHttpStatusCode == HttpStatusCode.BadRequest) - { - if (WindowHelper.ShowMessage( - "Unable to get authentication token using the following data:\n\nIdentity Server URI: " + sitecoreIdServerUri + - "\nUser name: " + userName + "\nPassword: " + password + "\n\nWould you like to try to continue installation using other URI and credentials?", - MessageBoxButton.YesNo, - MessageBoxImage.Warning) == MessageBoxResult.Yes) - { - CredentialsContext credentialsContext = - WindowHelper.ShowDialog(new CredentialsContext(userName, password, sitecoreIdServerUri), null) as CredentialsContext; - if (credentialsContext != null) - { - idServerAuthToken = this.GetAuthToken(credentialsContext.Uri, credentialsContext.UserName, credentialsContext.Password, mainWindow); - this.ValidateAuthToken(idServerAuthToken, credentialsContext.Uri, credentialsContext.UserName, credentialsContext.Password, mainWindow); - } - } + WizardPipelineManager.Start("installmodules", mainWindow, null, null, ignore => MainWindowHelper.MakeInstanceSelected(id), () => new InstallModulesWizardArgs(instance, authCookie, headers)); } - - return idServerAuthToken; - } - - private string GetAuthCookie(string instanceUri, string userName, string password, Window mainWindow) - { - string authCookie = null; - - WindowHelper.LongRunningTask(() => - { - var task = Task.Run(async () => { - authCookie = await - SitecoreServicesClientAuth.GetCookie(instanceUri, userName, password); - }); - task?.Wait(); - }, "Get authentication cookie", mainWindow, "Getting authentication cookie", "", true); - - return authCookie; - } - - private string ValidateAuthCookie(string authCookie, string instanceUri, string userName, string password, Window mainWindow) - { - if (SitecoreServicesClientAuth.CurrentHttpStatusCode == HttpStatusCode.InternalServerError) - { - WindowHelper.HandleError("Unable to get authentication cookie using the following Sitecore instance URI:\n\n" + - instanceUri + - "\n\nPlease make sure that a valid certificate is used for the SSL binding of your Sitecore site.", true); - return null; - } - else if (SitecoreServicesClientAuth.CurrentHttpStatusCode != HttpStatusCode.OK && SitecoreServicesClientAuth.CurrentHttpStatusCode != HttpStatusCode.Forbidden) - { - WindowHelper.ShowMessage( - "Unable to get authentication cookie using the following Sitecore instance URI:\n\n" + - instanceUri + - "\n\nThe '" + SitecoreServicesClientAuth.CurrentHttpStatusCode + "' status code has been returned." + - "\n\nPlease make sure that this Sitecore site is running.", - MessageBoxButton.OK, - MessageBoxImage.Warning); - return null; - } - - if (SitecoreServicesClientAuth.CurrentHttpStatusCode == HttpStatusCode.Forbidden) - { - if (WindowHelper.ShowMessage( - "Unable to get authentication cookie using the following credentials:\n\n" + - "User name: " + userName + "\nPassword: " + password + "\n\nWould you like to try to continue installation using other credentials?", - MessageBoxButton.YesNo, - MessageBoxImage.Warning) == MessageBoxResult.Yes) - { - CredentialsContext credentialsContext = - WindowHelper.ShowDialog(new CredentialsContext(userName, password), null) as CredentialsContext; - if (credentialsContext != null) - { - authCookie = this.GetAuthCookie(instanceUri, credentialsContext.UserName, credentialsContext.Password, mainWindow); - this.ValidateAuthCookie(authCookie, instanceUri, credentialsContext.UserName, credentialsContext.Password, mainWindow); - } - } - } - - return authCookie; - } - - private bool IsInstanceUriValid(string instanceUri) - { - if (!instanceUri.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase)) - { - WindowHelper.ShowMessage( - "The selected Sitecore instance does not use SSL/TLS connection according to the following URI:\n\n" + - instanceUri + - "\n\nThe SSC Auth services that are needed for package installation require an SSL/TLS connection, so you need to set up a valid certificate and SSL binding for your Sitecore site.", - MessageBoxButton.OK, - MessageBoxImage.Warning); - return false; - } - - return true; - } + } } } \ No newline at end of file diff --git a/src/SIM.Tool.Windows/MainWindowComponents/Helpers/ButtonAuthenticationHelper.cs b/src/SIM.Tool.Windows/MainWindowComponents/Helpers/ButtonAuthenticationHelper.cs new file mode 100644 index 00000000..56b513fd --- /dev/null +++ b/src/SIM.Tool.Windows/MainWindowComponents/Helpers/ButtonAuthenticationHelper.cs @@ -0,0 +1,177 @@ +using SIM.Core; +using SIM.Instances; +using SIM.Tool.Base; +using SIM.Tool.Windows.Dialogs; +using SIM.Tool.Windows.UserControls.SitecoreAuthentication; +using System; +using System.Collections.Generic; +using System.Net; +using System.Threading.Tasks; +using System.Windows; + +namespace SIM.Tool.Windows.MainWindowComponents.Helpers +{ + public class ButtonAuthenticationHelper + { + public Dictionary GetIdServerAuthToken(Window mainWindow, Instance instance) + { + string sitecoreIdServerUri = "https://" + instance.SitecoreEnvironment.Name + "Id.local"; + string idServerAuthToken = GetAuthToken(sitecoreIdServerUri, CoreAppSettings.AppLoginAsAdminUserName.Value, CoreAppSettings.AppLoginAsAdminNewPassword.Value, mainWindow); + idServerAuthToken = ValidateAuthToken(idServerAuthToken, sitecoreIdServerUri, + CoreAppSettings.AppLoginAsAdminUserName.Value, CoreAppSettings.AppLoginAsAdminNewPassword.Value, mainWindow); + + if (string.IsNullOrEmpty(idServerAuthToken)) + { + return null; + } + return new Dictionary { { "Authorization", idServerAuthToken } }; ; + } + + public string GetAuthCookie(Window mainWindow, Instance instance) + { + string instanceUri = instance.GetUrl(); + + if (!IsInstanceUriValid(instanceUri)) + { + return null; + } + + string authCookie = GetAuthClientCookie(instanceUri, CoreAppSettings.AppLoginAsAdminUserName.Value, CoreAppSettings.AppLoginAsAdminNewPassword.Value, mainWindow); + authCookie = ValidateAuthCookie(authCookie, instanceUri, + CoreAppSettings.AppLoginAsAdminUserName.Value, CoreAppSettings.AppLoginAsAdminNewPassword.Value, mainWindow); + + return authCookie; + } + + private string GetAuthToken(string sitecoreIdServerUri, string userName, string password, Window mainWindow) + { + string idServerAuthToken = null; + + WindowHelper.LongRunningTask(() => + { + var task = Task.Run(async () => { + idServerAuthToken = await + SitecoreIdServerAuth.GetToken(sitecoreIdServerUri, userName, password); + }); + task?.Wait(); + }, "Get authentication token", mainWindow, "Getting authentication token", "", true); + + return idServerAuthToken; + } + + private string ValidateAuthToken(string idServerAuthToken, string sitecoreIdServerUri, string userName, string password, Window mainWindow) + { + if (SitecoreIdServerAuth.CurrentHttpStatusCode == HttpStatusCode.InternalServerError) + { + WindowHelper.HandleError("Unable to get authentication token using the following Sitecore Identity Server URI:\n\n" + + sitecoreIdServerUri, true); + return null; + } + else if (SitecoreIdServerAuth.CurrentHttpStatusCode != HttpStatusCode.OK && SitecoreIdServerAuth.CurrentHttpStatusCode != HttpStatusCode.BadRequest) + { + WindowHelper.ShowMessage( + "Unable to get authentication token using the following Sitecore Identity Server URI:\n\n" + + sitecoreIdServerUri + + "\n\nThe '" + SitecoreIdServerAuth.CurrentHttpStatusCode + "' status code has been returned." + + "\n\nPlease make sure that this Sitecore Identity Server is running.", + MessageBoxButton.OK, + MessageBoxImage.Warning); + return null; + } + + if (SitecoreIdServerAuth.CurrentHttpStatusCode == HttpStatusCode.BadRequest) + { + if (WindowHelper.ShowMessage( + "Unable to get authentication token using the following data:\n\nIdentity Server URI: " + sitecoreIdServerUri + + "\nUser name: " + userName + "\nPassword: " + password + "\n\nWould you like to try to continue installation using other URI and credentials?", + MessageBoxButton.YesNo, + MessageBoxImage.Warning) == MessageBoxResult.Yes) + { + CredentialsContext credentialsContext = + WindowHelper.ShowDialog(new CredentialsContext(userName, password, sitecoreIdServerUri), null) as CredentialsContext; + if (credentialsContext != null) + { + idServerAuthToken = this.GetAuthToken(credentialsContext.Uri, credentialsContext.UserName, credentialsContext.Password, mainWindow); + this.ValidateAuthToken(idServerAuthToken, credentialsContext.Uri, credentialsContext.UserName, credentialsContext.Password, mainWindow); + } + } + } + + return idServerAuthToken; + } + + private string GetAuthClientCookie(string instanceUri, string userName, string password, Window mainWindow) + { + string authCookie = null; + + WindowHelper.LongRunningTask(() => + { + var task = Task.Run(async () => { + authCookie = await + SitecoreServicesClientAuth.GetCookie(instanceUri, userName, password); + }); + task?.Wait(); + }, "Get authentication cookie", mainWindow, "Getting authentication cookie", "", true); + + return authCookie; + } + + private string ValidateAuthCookie(string authCookie, string instanceUri, string userName, string password, Window mainWindow) + { + if (SitecoreServicesClientAuth.CurrentHttpStatusCode == HttpStatusCode.InternalServerError) + { + WindowHelper.HandleError("Unable to get authentication cookie using the following Sitecore instance URI:\n\n" + + instanceUri + + "\n\nPlease make sure that a valid certificate is used for the SSL binding of your Sitecore site.", true); + return null; + } + else if (SitecoreServicesClientAuth.CurrentHttpStatusCode != HttpStatusCode.OK && SitecoreServicesClientAuth.CurrentHttpStatusCode != HttpStatusCode.Forbidden) + { + WindowHelper.ShowMessage( + "Unable to get authentication cookie using the following Sitecore instance URI:\n\n" + + instanceUri + + "\n\nThe '" + SitecoreServicesClientAuth.CurrentHttpStatusCode + "' status code has been returned." + + "\n\nPlease make sure that this Sitecore site is running.", + MessageBoxButton.OK, + MessageBoxImage.Warning); + return null; + } + + if (SitecoreServicesClientAuth.CurrentHttpStatusCode == HttpStatusCode.Forbidden) + { + if (WindowHelper.ShowMessage( + "Unable to get authentication cookie using the following credentials:\n\n" + + "User name: " + userName + "\nPassword: " + password + "\n\nWould you like to try to continue installation using other credentials?", + MessageBoxButton.YesNo, + MessageBoxImage.Warning) == MessageBoxResult.Yes) + { + CredentialsContext credentialsContext = + WindowHelper.ShowDialog(new CredentialsContext(userName, password), null) as CredentialsContext; + if (credentialsContext != null) + { + authCookie = this.GetAuthClientCookie(instanceUri, credentialsContext.UserName, credentialsContext.Password, mainWindow); + this.ValidateAuthCookie(authCookie, instanceUri, credentialsContext.UserName, credentialsContext.Password, mainWindow); + } + } + } + + return authCookie; + } + + private bool IsInstanceUriValid(string instanceUri) + { + if (!instanceUri.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase)) + { + WindowHelper.ShowMessage( + "The selected Sitecore instance does not use SSL/TLS connection according to the following URI:\n\n" + + instanceUri + + "\n\nThe SSC Auth services that are needed for package installation require an SSL/TLS connection, so you need to set up a valid certificate and SSL binding for your Sitecore site.", + MessageBoxButton.OK, + MessageBoxImage.Warning); + return false; + } + + return true; + } + } +} diff --git a/src/SIM.Tool.Windows/MainWindowData.cs b/src/SIM.Tool.Windows/MainWindowData.cs index 4c28f768..e4f633c5 100644 --- a/src/SIM.Tool.Windows/MainWindowData.cs +++ b/src/SIM.Tool.Windows/MainWindowData.cs @@ -691,6 +691,12 @@ private static ButtonDefinition GetPatchButton() Image = "/Images/$lg/install.png, SIM.Tool.Windows", Handler = new InstallModulesForSitecore9AndLaterButton() }, + new ButtonDefinition + { + Label = "Add Solr Cores", + Image = "/Images/$lg/search_index.png, SIM.Tool.Windows", + Handler = new AddSolrCoresButton() + }, } }, GetManageGroupDefinition(), @@ -1158,6 +1164,7 @@ private static GroupDefinition GetManageGroupDefinition() new ButtonDefinition { Label = "Restore", Image = "/Images/$sm/box_out.png, SIM.Tool.Windows", Handler = new RestoreInstanceButton() }, new ButtonDefinition { Handler = new ExportInstanceButton() }, new ButtonDefinition { Label = "Export", Image = "/Images/$sm/download.png, SIM.Tool.Windows", Handler = new ExportInstanceButton() }, + new ButtonDefinition { Label = "Add Solr cores", Image = "/Images/$sm/search_index.png, SIM.Tool.Windows", Handler = new AddSolrCoresButton() }, new ButtonDefinition { Handler = new InstallModulesButton() }, new ButtonDefinition { Label = "Install packages", Image = "/Images/$sm/install.png, SIM.Tool.Windows", Handler = new InstallModulesButton() }, new ButtonDefinition { Handler = new InstallModulesButton() }, diff --git a/src/SIM.Tool.Windows/SIM.Tool.Windows.csproj b/src/SIM.Tool.Windows/SIM.Tool.Windows.csproj index e95f1659..a2816e8a 100644 --- a/src/SIM.Tool.Windows/SIM.Tool.Windows.csproj +++ b/src/SIM.Tool.Windows/SIM.Tool.Windows.csproj @@ -156,6 +156,7 @@ + @@ -229,6 +230,7 @@ + @@ -300,6 +302,9 @@ SelectModules.xaml + + AvailableSearchIndexes.xaml + @@ -659,6 +664,10 @@ + + + + SettingsSingleFileGenerator Settings.Designer.cs @@ -749,6 +758,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + MSBuild:Compile Designer diff --git a/src/SIM.Tool.Windows/UserControls/Install/SearchIndexes/AvailableSearchIndexes.xaml b/src/SIM.Tool.Windows/UserControls/Install/SearchIndexes/AvailableSearchIndexes.xaml new file mode 100644 index 00000000..358b9493 --- /dev/null +++ b/src/SIM.Tool.Windows/UserControls/Install/SearchIndexes/AvailableSearchIndexes.xaml @@ -0,0 +1,18 @@ + + + + + + + + + + + diff --git a/src/SIM.Tool.Windows/UserControls/Install/SearchIndexes/AvailableSearchIndexes.xaml.cs b/src/SIM.Tool.Windows/UserControls/Install/SearchIndexes/AvailableSearchIndexes.xaml.cs new file mode 100644 index 00000000..a8d65db6 --- /dev/null +++ b/src/SIM.Tool.Windows/UserControls/Install/SearchIndexes/AvailableSearchIndexes.xaml.cs @@ -0,0 +1,68 @@ +using SIM.Tool.Base.Pipelines; +using SIM.Tool.Base.Wizards; +using SIM.Tool.Windows.UserControls.MultipleDeletion; +using System.Collections.Generic; +using System.Linq; +using System.Windows; + +namespace SIM.Tool.Windows.UserControls.Install.SearchIndexes +{ + public partial class AvailableSearchIndexes : IWizardStep, IFlowControl + { + private List CoreNamesCheckBoxItems; + + public AvailableSearchIndexes() + { + InitializeComponent(); + } + + public void InitializeStep(WizardArgs wizardArgs) + { + var args = (InstallSearchIndexesWizardArgs)wizardArgs; + CoreNamesCheckBoxItems = new List(); + + foreach (var availableSearchIndex in args._AvailableSearchIndexesList) + { + CoreNamesCheckBoxItems.Add(new EnvironmentCheckBox(availableSearchIndex)); + } + + if (args._AvailableSearchIndexesList != null && args._AvailableSearchIndexesList.Count > 0) + { + foreach (string availableIndexes in args._AvailableSearchIndexesList) + { + CoreNamesCheckBoxItems.Where(item => item.Name == availableIndexes).FirstOrDefault().IsChecked = true; + } + } + + SearchIndexesListBox.DataContext = CoreNamesCheckBoxItems; + } + + public bool OnMovingBack(WizardArgs wizardArgs) + { + return true; + } + + public bool OnMovingNext(WizardArgs wizardArgs) + { + if (CoreNamesCheckBoxItems != null && CoreNamesCheckBoxItems.Where(item => item.IsChecked).Any()) + { + return true; + } + + MessageBox.Show("You haven't selected any of the Solr cores", "Install Solr cores", MessageBoxButton.OK, MessageBoxImage.Warning); + return false; + } + + public bool SaveChanges(WizardArgs wizardArgs) + { + var args = (InstallSearchIndexesWizardArgs)wizardArgs; + + if (CoreNamesCheckBoxItems != null && CoreNamesCheckBoxItems.Where(item => item.IsChecked).Any()) + { + args._AvailableSearchIndexesList = CoreNamesCheckBoxItems.Where(item => item.IsChecked).Select(item => item.Name).ToList(); + } + + return true; + } + } +} diff --git a/src/SIM.Tool.Windows/WizardPipelinesConfig.cs b/src/SIM.Tool.Windows/WizardPipelinesConfig.cs index b40f7789..120ebdae 100644 --- a/src/SIM.Tool.Windows/WizardPipelinesConfig.cs +++ b/src/SIM.Tool.Windows/WizardPipelinesConfig.cs @@ -325,8 +325,18 @@ Note that due to the large size of each installation package the whole download method=""OpenVisualStudio"" /> - + + + + + + +