using System; using System.Collections.Generic; using System.IO; using System.Linq; using Umbraco.Core.IO; using Umbraco.Core.Services; namespace Umbraco.Web.HealthCheck.Checks.Config { public abstract class AbstractConfigCheck : HealthCheck { private readonly ConfigurationService _configurationService; private readonly ILocalizedTextService _textService; /// /// Gets the config file path. /// public abstract string FilePath { get; } /// /// Gets XPath statement to the config element to check. /// public abstract string XPath { get; } /// /// Gets the values to compare against. /// public abstract IEnumerable Values { get; } /// /// Gets the current value /// public string CurrentValue { get; set; } /// /// Gets the provided value /// public string ProvidedValue { get; set; } /// /// Gets the comparison type for checking the value. /// public abstract ValueComparisonType ValueComparisonType { get; } /// /// Gets the flag indicating if the check is considered successful if the config value is missing (defaults to false - an error - if missing) /// public virtual bool ValidIfConfigMissing { get { return false; } } protected AbstractConfigCheck(HealthCheckContext healthCheckContext) : base(healthCheckContext) { _textService = healthCheckContext.ApplicationContext.Services.TextService; _configurationService = new ConfigurationService(AbsoluteFilePath, XPath); } /// /// Gets the name of the file. /// private string FileName { get { return Path.GetFileName(FilePath); } } /// /// Gets the absolute file path. /// private string AbsoluteFilePath { get { return IOHelper.MapPath(FilePath); } } /// /// Gets the message for when the check has succeeded. /// public virtual string CheckSuccessMessage { get { return _textService.Localize("healthcheck/checkSuccessMessage", new[] { CurrentValue, Values.First(v => v.IsRecommended).Value, XPath, AbsoluteFilePath }); } } /// /// Gets the message for when the check has failed. /// public virtual string CheckErrorMessage { get { return ValueComparisonType == ValueComparisonType.ShouldEqual ? _textService.Localize("healthcheck/checkErrorMessageDifferentExpectedValue", new[] { CurrentValue, Values.First(v => v.IsRecommended).Value, XPath, AbsoluteFilePath }) : _textService.Localize("healthcheck/checkErrorMessageUnexpectedValue", new[] { CurrentValue, Values.First(v => v.IsRecommended).Value, XPath, AbsoluteFilePath }); } } /// /// Gets the rectify success message. /// public virtual string RectifySuccessMessage { get { var recommendedValue = Values.FirstOrDefault(v => v.IsRecommended); var rectifiedValue = recommendedValue != null ? recommendedValue.Value : ProvidedValue; return _textService.Localize("healthcheck/rectifySuccessMessage", new[] { CurrentValue, rectifiedValue, XPath, AbsoluteFilePath }); } } /// /// Gets a value indicating whether this check can be rectified automatically. /// public virtual bool CanRectify { get { return ValueComparisonType == ValueComparisonType.ShouldEqual; } } /// /// Gets a value indicating whether this check can be rectified automatically if a value is provided. /// public virtual bool CanRectifyWithValue { get { return ValueComparisonType == ValueComparisonType.ShouldNotEqual; } } public override IEnumerable GetStatus() { var successMessage = string.Format(CheckSuccessMessage, FileName, XPath, Values); var configValue = _configurationService.GetConfigurationValue(); if (configValue.Success == false) { if (ValidIfConfigMissing) { return new[] { new HealthCheckStatus(successMessage) { ResultType = StatusResultType.Success } }; } var errorMessage = configValue.Result; return new[] { new HealthCheckStatus(errorMessage) { ResultType = StatusResultType.Error } }; } CurrentValue = configValue.Result; // need to update the successMessage with the CurrentValue successMessage = string.Format(CheckSuccessMessage, FileName, XPath, Values, CurrentValue); var valueFound = Values.Any(value => string.Equals(CurrentValue, value.Value, StringComparison.InvariantCultureIgnoreCase)); if (ValueComparisonType == ValueComparisonType.ShouldEqual && valueFound || ValueComparisonType == ValueComparisonType.ShouldNotEqual && valueFound == false) { return new[] { new HealthCheckStatus(successMessage) { ResultType = StatusResultType.Success } }; } // Declare the action for rectifying the config value var rectifyAction = new HealthCheckAction("rectify", Id) { Name = _textService.Localize("healthcheck/rectifyButton"), ValueRequired = CanRectifyWithValue, }; var resultMessage = string.Format(CheckErrorMessage, FileName, XPath, Values, CurrentValue); return new[] { new HealthCheckStatus(resultMessage) { ResultType = StatusResultType.Error, Actions = CanRectify || CanRectifyWithValue ? new[] { rectifyAction } : new HealthCheckAction[0] } }; } /// /// Rectifies this check. /// /// public virtual HealthCheckStatus Rectify() { if (ValueComparisonType == ValueComparisonType.ShouldNotEqual) throw new InvalidOperationException(_textService.Localize("healthcheck/cannotRectifyShouldNotEqual")); var recommendedValue = Values.First(v => v.IsRecommended).Value; return UpdateConfigurationValue(recommendedValue); } /// /// Rectifies this check with a provided value. /// /// Value provided /// public virtual HealthCheckStatus Rectify(string value) { if (ValueComparisonType == ValueComparisonType.ShouldEqual) throw new InvalidOperationException(_textService.Localize("healthcheck/cannotRectifyShouldEqualWithValue")); if (string.IsNullOrWhiteSpace(value)) throw new InvalidOperationException(_textService.Localize("healthcheck/valueToRectifyNotProvided")); // Need to track provided value in order to correctly put together the rectify message ProvidedValue = value; return UpdateConfigurationValue(value); } private HealthCheckStatus UpdateConfigurationValue(string value) { var updateConfigFile = _configurationService.UpdateConfigFile(value); if (updateConfigFile.Success == false) { var message = updateConfigFile.Result; return new HealthCheckStatus(message) { ResultType = StatusResultType.Error }; } var resultMessage = string.Format(RectifySuccessMessage, FileName, XPath, Values); return new HealthCheckStatus(resultMessage) { ResultType = StatusResultType.Success }; } public override HealthCheckStatus ExecuteAction(HealthCheckAction action) { return string.IsNullOrEmpty(action.ProvidedValue) ? Rectify() : Rectify(action.ProvidedValue); } } }