using System; using System.Configuration; using System.IO; using System.Threading; using Umbraco.Core.Configuration; using Umbraco.Core; using Umbraco.Core.IO; namespace Umbraco.Configuration { /// /// Represents the models builder configuration. /// public class ModelsBuilderConfig : IModelsBuilderConfig { private const string Prefix = "Umbraco.ModelsBuilder."; private object _modelsModelLock; private bool _modelsModelConfigured; private ModelsMode _modelsMode; private object _flagOutOfDateModelsLock; private bool _flagOutOfDateModelsConfigured; private bool _flagOutOfDateModels; public string DefaultModelsDirectory => "~/App_Data/Models"; /// /// Initializes a new instance of the class. /// public ModelsBuilderConfig() { // giant kill switch, default: false // must be explicitely set to true for anything else to happen Enable = ConfigurationManager.AppSettings[Prefix + "Enable"] == "true"; // ensure defaults are initialized for tests ModelsNamespace = Constants.ModelsBuilder.DefaultModelsNamespace; ModelsDirectory = DefaultModelsDirectory; DebugLevel = 0; // stop here, everything is false if (!Enable) return; // default: false AcceptUnsafeModelsDirectory = ConfigurationManager.AppSettings[Prefix + "AcceptUnsafeModelsDirectory"].InvariantEquals("true"); // default: true EnableFactory = !ConfigurationManager.AppSettings[Prefix + "EnableFactory"].InvariantEquals("false"); // default: initialized above with DefaultModelsNamespace const var value = ConfigurationManager.AppSettings[Prefix + "ModelsNamespace"]; if (!string.IsNullOrWhiteSpace(value)) ModelsNamespace = value; // default: initialized above with DefaultModelsDirectory const value = ConfigurationManager.AppSettings[Prefix + "ModelsDirectory"]; if (!string.IsNullOrWhiteSpace(value)) { // GetModelsDirectory will ensure that the path is safe ModelsDirectory = value; } // default: 0 value = ConfigurationManager.AppSettings[Prefix + "DebugLevel"]; if (!string.IsNullOrWhiteSpace(value)) { if (!int.TryParse(value, out var debugLevel)) throw new ConfigurationErrorsException($"Invalid debug level \"{value}\"."); DebugLevel = debugLevel; } } /// /// Initializes a new instance of the class. /// public ModelsBuilderConfig( bool enable = false, ModelsMode modelsMode = ModelsMode.Nothing, string modelsNamespace = null, bool enableFactory = true, bool flagOutOfDateModels = true, string modelsDirectory = null, bool acceptUnsafeModelsDirectory = false, int debugLevel = 0) { Enable = enable; _modelsMode = modelsMode; ModelsNamespace = string.IsNullOrWhiteSpace(modelsNamespace) ? Constants.ModelsBuilder.DefaultModelsNamespace : modelsNamespace; EnableFactory = enableFactory; _flagOutOfDateModels = flagOutOfDateModels; ModelsDirectory = string.IsNullOrWhiteSpace(modelsDirectory) ? DefaultModelsDirectory : modelsDirectory; AcceptUnsafeModelsDirectory = acceptUnsafeModelsDirectory; DebugLevel = debugLevel; } /// /// Gets a value indicating whether the whole models experience is enabled. /// /// /// If this is false then absolutely nothing happens. /// Default value is false which means that unless we have this setting, nothing happens. /// public bool Enable { get; } /// /// Gets the models mode. /// public ModelsMode ModelsMode => LazyInitializer.EnsureInitialized(ref _modelsMode, ref _modelsModelConfigured, ref _modelsModelLock, () => { // mode var modelsMode = ConfigurationManager.AppSettings[Prefix + "ModelsMode"]; if (string.IsNullOrWhiteSpace(modelsMode)) return ModelsMode.Nothing; //default switch (modelsMode) { case nameof(ModelsMode.Nothing): return ModelsMode.Nothing; case nameof(ModelsMode.PureLive): return ModelsMode.PureLive; case nameof(ModelsMode.AppData): return ModelsMode.AppData; case nameof(ModelsMode.LiveAppData): return ModelsMode.LiveAppData; default: throw new ConfigurationErrorsException($"ModelsMode \"{modelsMode}\" is not a valid mode." + " Note that modes are case-sensitive. Possible values are: " + string.Join(", ", Enum.GetNames(typeof(ModelsMode)))); } }); /// /// Gets a value indicating whether system.web/compilation/@debug is true. /// public bool IsDebug { get { if (ConfigurationManager.GetSection("system.web/compilation") is ConfigurationSection section && bool.TryParse(section.ElementInformation.Properties["debug"].Value.ToString(), out var isDebug)) { return isDebug; } return false; } } /// /// Gets the models namespace. /// /// That value could be overriden by other (attribute in user's code...). Return default if no value was supplied. public string ModelsNamespace { get; } /// /// Gets a value indicating whether we should enable the models factory. /// /// Default value is true because no factory is enabled by default in Umbraco. public bool EnableFactory { get; } /// /// Gets a value indicating whether we should flag out-of-date models. /// /// Models become out-of-date when data types or content types are updated. When this /// setting is activated the ~/App_Data/Models/ood.txt file is then created. When models are /// generated through the dashboard, the files is cleared. Default value is false. public bool FlagOutOfDateModels => LazyInitializer.EnsureInitialized(ref _flagOutOfDateModels, ref _flagOutOfDateModelsConfigured, ref _flagOutOfDateModelsLock, () => { var flagOutOfDateModels = !ConfigurationManager.AppSettings[Prefix + "FlagOutOfDateModels"].InvariantEquals("false"); if (ModelsMode == ModelsMode.Nothing || ModelsMode.IsLive()) { flagOutOfDateModels = false; } return flagOutOfDateModels; }); /// /// Gets the models directory. /// /// Default is ~/App_Data/Models but that can be changed. public string ModelsDirectory { get; } /// /// Gets a value indicating whether to accept an unsafe value for ModelsDirectory. /// /// An unsafe value is an absolute path, or a relative path pointing outside /// of the website root. public bool AcceptUnsafeModelsDirectory { get; } /// /// Gets a value indicating the debug log level. /// /// 0 means minimal (safe on live site), anything else means more and more details (maybe not safe). public int DebugLevel { get; } } }