diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs index 4a00aeb3ee..2dcbe06458 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs @@ -1,146 +1,141 @@ -using System.Collections.Generic; -using System.Configuration; -using Umbraco.Core.Macros; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - internal class ContentElement : UmbracoConfigurationElement, IContentSection - { - private const string DefaultPreviewBadge = @"In Preview Mode - click to end"; - - [ConfigurationProperty("imaging")] - internal ContentImagingElement Imaging => (ContentImagingElement) this["imaging"]; - - [ConfigurationProperty("scripteditor")] - internal ContentScriptEditorElement ScriptEditor => (ContentScriptEditorElement) this["scripteditor"]; - - [ConfigurationProperty("ResolveUrlsFromTextString")] - internal InnerTextConfigurationElement ResolveUrlsFromTextString => GetOptionalTextElement("ResolveUrlsFromTextString", false); - - [ConfigurationProperty("UploadAllowDirectories")] - internal InnerTextConfigurationElement UploadAllowDirectories => GetOptionalTextElement("UploadAllowDirectories", true); - - public IEnumerable Error404Collection => Errors.Error404Collection; - - [ConfigurationProperty("errors", IsRequired = true)] - internal ContentErrorsElement Errors => (ContentErrorsElement) base["errors"]; - - [ConfigurationProperty("notifications", IsRequired = true)] - internal NotificationsElement Notifications => (NotificationsElement) base["notifications"]; - - [ConfigurationProperty("ensureUniqueNaming")] - internal InnerTextConfigurationElement EnsureUniqueNaming => GetOptionalTextElement("ensureUniqueNaming", true); - - [ConfigurationProperty("XmlCacheEnabled")] - internal InnerTextConfigurationElement XmlCacheEnabled => GetOptionalTextElement("XmlCacheEnabled", true); - - [ConfigurationProperty("ContinouslyUpdateXmlDiskCache")] - internal InnerTextConfigurationElement ContinouslyUpdateXmlDiskCache => GetOptionalTextElement("ContinouslyUpdateXmlDiskCache", true); - - [ConfigurationProperty("XmlContentCheckForDiskChanges")] - internal InnerTextConfigurationElement XmlContentCheckForDiskChanges => GetOptionalTextElement("XmlContentCheckForDiskChanges", false); - - [ConfigurationProperty("EnableSplashWhileLoading")] - internal InnerTextConfigurationElement EnableSplashWhileLoading => GetOptionalTextElement("EnableSplashWhileLoading", false); - - [ConfigurationProperty("PropertyContextHelpOption")] - internal InnerTextConfigurationElement PropertyContextHelpOption => GetOptionalTextElement("PropertyContextHelpOption", "text"); - - [ConfigurationProperty("ForceSafeAliases")] - internal InnerTextConfigurationElement ForceSafeAliases => GetOptionalTextElement("ForceSafeAliases", true); - - [ConfigurationProperty("PreviewBadge")] - internal InnerTextConfigurationElement PreviewBadge => GetOptionalTextElement("PreviewBadge", DefaultPreviewBadge); - - [ConfigurationProperty("UmbracoLibraryCacheDuration")] - internal InnerTextConfigurationElement UmbracoLibraryCacheDuration => GetOptionalTextElement("UmbracoLibraryCacheDuration", 1800); - - [ConfigurationProperty("MacroErrors")] - internal InnerTextConfigurationElement MacroErrors => GetOptionalTextElement("MacroErrors", MacroErrorBehaviour.Inline); - - [ConfigurationProperty("disallowedUploadFiles")] - internal CommaDelimitedConfigurationElement DisallowedUploadFiles => GetOptionalDelimitedElement("disallowedUploadFiles", new[] {"ashx", "aspx", "ascx", "config", "cshtml", "vbhtml", "asmx", "air", "axd"}); - - [ConfigurationProperty("allowedUploadFiles")] - internal CommaDelimitedConfigurationElement AllowedUploadFiles => GetOptionalDelimitedElement("allowedUploadFiles", new string[0]); - - [ConfigurationProperty("cloneXmlContent")] - internal InnerTextConfigurationElement CloneXmlContent => GetOptionalTextElement("cloneXmlContent", true); - - [ConfigurationProperty("GlobalPreviewStorageEnabled")] - internal InnerTextConfigurationElement GlobalPreviewStorageEnabled => GetOptionalTextElement("GlobalPreviewStorageEnabled", false); - - [ConfigurationProperty("defaultDocumentTypeProperty")] - internal InnerTextConfigurationElement DefaultDocumentTypeProperty => GetOptionalTextElement("defaultDocumentTypeProperty", "Textstring"); - - [ConfigurationProperty("showDeprecatedPropertyEditors")] - internal InnerTextConfigurationElement ShowDeprecatedPropertyEditors => GetOptionalTextElement("showDeprecatedPropertyEditors", false); - - [ConfigurationProperty("EnableInheritedDocumentTypes")] - internal InnerTextConfigurationElement EnableInheritedDocumentTypes => GetOptionalTextElement("EnableInheritedDocumentTypes", true); - - [ConfigurationProperty("EnableInheritedMediaTypes")] - internal InnerTextConfigurationElement EnableInheritedMediaTypes => GetOptionalTextElement("EnableInheritedMediaTypes", true); - - [ConfigurationProperty("loginBackgroundImage")] - internal InnerTextConfigurationElement LoginBackgroundImage => GetOptionalTextElement("loginBackgroundImage", string.Empty); - - string IContentSection.NotificationEmailAddress => Notifications.NotificationEmailAddress; - - bool IContentSection.DisableHtmlEmail => Notifications.DisableHtmlEmail; - - IEnumerable IContentSection.ImageFileTypes => Imaging.ImageFileTypes; - - IEnumerable IContentSection.ImageTagAllowedAttributes => Imaging.ImageTagAllowedAttributes; - - IEnumerable IContentSection.ImageAutoFillProperties => Imaging.ImageAutoFillProperties; - - bool IContentSection.ScriptEditorDisable => ScriptEditor.ScriptEditorDisable; - - string IContentSection.ScriptFolderPath => ScriptEditor.ScriptFolderPath; - - IEnumerable IContentSection.ScriptFileTypes => ScriptEditor.ScriptFileTypes; - - bool IContentSection.ResolveUrlsFromTextString => ResolveUrlsFromTextString; - - bool IContentSection.UploadAllowDirectories => UploadAllowDirectories; - - bool IContentSection.EnsureUniqueNaming => EnsureUniqueNaming; - - bool IContentSection.XmlCacheEnabled => XmlCacheEnabled; - - bool IContentSection.ContinouslyUpdateXmlDiskCache => ContinouslyUpdateXmlDiskCache; - - bool IContentSection.XmlContentCheckForDiskChanges => XmlContentCheckForDiskChanges; - - bool IContentSection.EnableSplashWhileLoading => EnableSplashWhileLoading; - - string IContentSection.PropertyContextHelpOption => PropertyContextHelpOption; - - bool IContentSection.ForceSafeAliases => ForceSafeAliases; - - string IContentSection.PreviewBadge => PreviewBadge; - - int IContentSection.UmbracoLibraryCacheDuration => UmbracoLibraryCacheDuration; - - MacroErrorBehaviour IContentSection.MacroErrorBehaviour => MacroErrors; - - IEnumerable IContentSection.DisallowedUploadFiles => DisallowedUploadFiles; - - IEnumerable IContentSection.AllowedUploadFiles => AllowedUploadFiles; - - bool IContentSection.CloneXmlContent => CloneXmlContent; - - bool IContentSection.GlobalPreviewStorageEnabled => GlobalPreviewStorageEnabled; - - string IContentSection.DefaultDocumentTypeProperty => DefaultDocumentTypeProperty; - - bool IContentSection.ShowDeprecatedPropertyEditors => ShowDeprecatedPropertyEditors; - - bool IContentSection.EnableInheritedDocumentTypes => EnableInheritedDocumentTypes; - - bool IContentSection.EnableInheritedMediaTypes => EnableInheritedMediaTypes; - - string IContentSection.LoginBackgroundImage => LoginBackgroundImage; - } -} +using System.Collections.Generic; +using System.Configuration; +using Umbraco.Core.Macros; + +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + internal class ContentElement : UmbracoConfigurationElement, IContentSection + { + private const string DefaultPreviewBadge = @"In Preview Mode - click to end"; + + [ConfigurationProperty("imaging")] + internal ContentImagingElement Imaging => (ContentImagingElement) this["imaging"]; + + [ConfigurationProperty("scripteditor")] + internal ContentScriptEditorElement ScriptEditor => (ContentScriptEditorElement) this["scripteditor"]; + + [ConfigurationProperty("ResolveUrlsFromTextString")] + internal InnerTextConfigurationElement ResolveUrlsFromTextString => GetOptionalTextElement("ResolveUrlsFromTextString", false); + + [ConfigurationProperty("UploadAllowDirectories")] + internal InnerTextConfigurationElement UploadAllowDirectories => GetOptionalTextElement("UploadAllowDirectories", true); + + public IEnumerable Error404Collection => Errors.Error404Collection; + + [ConfigurationProperty("errors", IsRequired = true)] + internal ContentErrorsElement Errors => (ContentErrorsElement) base["errors"]; + + [ConfigurationProperty("notifications", IsRequired = true)] + internal NotificationsElement Notifications => (NotificationsElement) base["notifications"]; + + [ConfigurationProperty("ensureUniqueNaming")] + internal InnerTextConfigurationElement EnsureUniqueNaming => GetOptionalTextElement("ensureUniqueNaming", true); + + [ConfigurationProperty("XmlCacheEnabled")] + internal InnerTextConfigurationElement XmlCacheEnabled => GetOptionalTextElement("XmlCacheEnabled", true); + + [ConfigurationProperty("ContinouslyUpdateXmlDiskCache")] + internal InnerTextConfigurationElement ContinouslyUpdateXmlDiskCache => GetOptionalTextElement("ContinouslyUpdateXmlDiskCache", true); + + [ConfigurationProperty("XmlContentCheckForDiskChanges")] + internal InnerTextConfigurationElement XmlContentCheckForDiskChanges => GetOptionalTextElement("XmlContentCheckForDiskChanges", false); + + [ConfigurationProperty("EnableSplashWhileLoading")] + internal InnerTextConfigurationElement EnableSplashWhileLoading => GetOptionalTextElement("EnableSplashWhileLoading", false); + + [ConfigurationProperty("PropertyContextHelpOption")] + internal InnerTextConfigurationElement PropertyContextHelpOption => GetOptionalTextElement("PropertyContextHelpOption", "text"); + + [ConfigurationProperty("ForceSafeAliases")] + internal InnerTextConfigurationElement ForceSafeAliases => GetOptionalTextElement("ForceSafeAliases", true); + + [ConfigurationProperty("PreviewBadge")] + internal InnerTextConfigurationElement PreviewBadge => GetOptionalTextElement("PreviewBadge", DefaultPreviewBadge); + + [ConfigurationProperty("MacroErrors")] + internal InnerTextConfigurationElement MacroErrors => GetOptionalTextElement("MacroErrors", MacroErrorBehaviour.Inline); + + [ConfigurationProperty("disallowedUploadFiles")] + internal CommaDelimitedConfigurationElement DisallowedUploadFiles => GetOptionalDelimitedElement("disallowedUploadFiles", new[] {"ashx", "aspx", "ascx", "config", "cshtml", "vbhtml", "asmx", "air", "axd"}); + + [ConfigurationProperty("allowedUploadFiles")] + internal CommaDelimitedConfigurationElement AllowedUploadFiles => GetOptionalDelimitedElement("allowedUploadFiles", new string[0]); + + [ConfigurationProperty("cloneXmlContent")] + internal InnerTextConfigurationElement CloneXmlContent => GetOptionalTextElement("cloneXmlContent", true); + + [ConfigurationProperty("GlobalPreviewStorageEnabled")] + internal InnerTextConfigurationElement GlobalPreviewStorageEnabled => GetOptionalTextElement("GlobalPreviewStorageEnabled", false); + + [ConfigurationProperty("defaultDocumentTypeProperty")] + internal InnerTextConfigurationElement DefaultDocumentTypeProperty => GetOptionalTextElement("defaultDocumentTypeProperty", "Textstring"); + + [ConfigurationProperty("showDeprecatedPropertyEditors")] + internal InnerTextConfigurationElement ShowDeprecatedPropertyEditors => GetOptionalTextElement("showDeprecatedPropertyEditors", false); + + [ConfigurationProperty("EnableInheritedDocumentTypes")] + internal InnerTextConfigurationElement EnableInheritedDocumentTypes => GetOptionalTextElement("EnableInheritedDocumentTypes", true); + + [ConfigurationProperty("EnableInheritedMediaTypes")] + internal InnerTextConfigurationElement EnableInheritedMediaTypes => GetOptionalTextElement("EnableInheritedMediaTypes", true); + + [ConfigurationProperty("loginBackgroundImage")] + internal InnerTextConfigurationElement LoginBackgroundImage => GetOptionalTextElement("loginBackgroundImage", string.Empty); + + string IContentSection.NotificationEmailAddress => Notifications.NotificationEmailAddress; + + bool IContentSection.DisableHtmlEmail => Notifications.DisableHtmlEmail; + + IEnumerable IContentSection.ImageFileTypes => Imaging.ImageFileTypes; + + IEnumerable IContentSection.ImageTagAllowedAttributes => Imaging.ImageTagAllowedAttributes; + + IEnumerable IContentSection.ImageAutoFillProperties => Imaging.ImageAutoFillProperties; + + bool IContentSection.ScriptEditorDisable => ScriptEditor.ScriptEditorDisable; + + string IContentSection.ScriptFolderPath => ScriptEditor.ScriptFolderPath; + + IEnumerable IContentSection.ScriptFileTypes => ScriptEditor.ScriptFileTypes; + + bool IContentSection.ResolveUrlsFromTextString => ResolveUrlsFromTextString; + + bool IContentSection.UploadAllowDirectories => UploadAllowDirectories; + + bool IContentSection.EnsureUniqueNaming => EnsureUniqueNaming; + + bool IContentSection.XmlCacheEnabled => XmlCacheEnabled; + + bool IContentSection.ContinouslyUpdateXmlDiskCache => ContinouslyUpdateXmlDiskCache; + + bool IContentSection.XmlContentCheckForDiskChanges => XmlContentCheckForDiskChanges; + + bool IContentSection.EnableSplashWhileLoading => EnableSplashWhileLoading; + + string IContentSection.PropertyContextHelpOption => PropertyContextHelpOption; + + bool IContentSection.ForceSafeAliases => ForceSafeAliases; + + string IContentSection.PreviewBadge => PreviewBadge; + + MacroErrorBehaviour IContentSection.MacroErrorBehaviour => MacroErrors; + + IEnumerable IContentSection.DisallowedUploadFiles => DisallowedUploadFiles; + + IEnumerable IContentSection.AllowedUploadFiles => AllowedUploadFiles; + + bool IContentSection.CloneXmlContent => CloneXmlContent; + + bool IContentSection.GlobalPreviewStorageEnabled => GlobalPreviewStorageEnabled; + + string IContentSection.DefaultDocumentTypeProperty => DefaultDocumentTypeProperty; + + bool IContentSection.ShowDeprecatedPropertyEditors => ShowDeprecatedPropertyEditors; + + bool IContentSection.EnableInheritedDocumentTypes => EnableInheritedDocumentTypes; + + bool IContentSection.EnableInheritedMediaTypes => EnableInheritedMediaTypes; + + string IContentSection.LoginBackgroundImage => LoginBackgroundImage; + } +} diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs index 6929646114..7f6f57f4cf 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs @@ -1,72 +1,70 @@ -using System.Collections.Generic; -using Umbraco.Core.Macros; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface IContentSection : IUmbracoConfigurationSection - { - string NotificationEmailAddress { get; } - - bool DisableHtmlEmail { get; } - - IEnumerable ImageFileTypes { get; } - - IEnumerable ImageTagAllowedAttributes { get; } - - IEnumerable ImageAutoFillProperties { get; } - - string ScriptFolderPath { get; } - - IEnumerable ScriptFileTypes { get; } - - bool ScriptEditorDisable { get; } - - bool ResolveUrlsFromTextString { get; } - +using System.Collections.Generic; +using Umbraco.Core.Macros; + +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + public interface IContentSection : IUmbracoConfigurationSection + { + string NotificationEmailAddress { get; } + + bool DisableHtmlEmail { get; } + + IEnumerable ImageFileTypes { get; } + + IEnumerable ImageTagAllowedAttributes { get; } + + IEnumerable ImageAutoFillProperties { get; } + + string ScriptFolderPath { get; } + + IEnumerable ScriptFileTypes { get; } + + bool ScriptEditorDisable { get; } + + bool ResolveUrlsFromTextString { get; } + bool UploadAllowDirectories { get; } - IEnumerable Error404Collection { get; } - - bool EnsureUniqueNaming { get; } - - bool XmlCacheEnabled { get; } - - bool ContinouslyUpdateXmlDiskCache { get; } - - bool XmlContentCheckForDiskChanges { get; } - - bool EnableSplashWhileLoading { get; } - - string PropertyContextHelpOption { get; } - - bool ForceSafeAliases { get; } - - string PreviewBadge { get; } - - int UmbracoLibraryCacheDuration { get; } - - MacroErrorBehaviour MacroErrorBehaviour { get; } - - IEnumerable DisallowedUploadFiles { get; } - - IEnumerable AllowedUploadFiles { get; } - - bool CloneXmlContent { get; } - - bool GlobalPreviewStorageEnabled { get; } - - string DefaultDocumentTypeProperty { get; } - - /// - /// Gets a value indicating whether to show deprecated property editors in - /// a datatype list of available editors. - /// - bool ShowDeprecatedPropertyEditors { get; } - - bool EnableInheritedDocumentTypes { get; } - - bool EnableInheritedMediaTypes { get; } - - string LoginBackgroundImage { get; } - } -} + IEnumerable Error404Collection { get; } + + bool EnsureUniqueNaming { get; } + + bool XmlCacheEnabled { get; } + + bool ContinouslyUpdateXmlDiskCache { get; } + + bool XmlContentCheckForDiskChanges { get; } + + bool EnableSplashWhileLoading { get; } + + string PropertyContextHelpOption { get; } + + bool ForceSafeAliases { get; } + + string PreviewBadge { get; } + + MacroErrorBehaviour MacroErrorBehaviour { get; } + + IEnumerable DisallowedUploadFiles { get; } + + IEnumerable AllowedUploadFiles { get; } + + bool CloneXmlContent { get; } + + bool GlobalPreviewStorageEnabled { get; } + + string DefaultDocumentTypeProperty { get; } + + /// + /// Gets a value indicating whether to show deprecated property editors in + /// a datatype list of available editors. + /// + bool ShowDeprecatedPropertyEditors { get; } + + bool EnableInheritedDocumentTypes { get; } + + bool EnableInheritedMediaTypes { get; } + + string LoginBackgroundImage { get; } + } +} diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/ContentElementTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/ContentElementTests.cs index 0980b600e8..fe32a43cc9 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/ContentElementTests.cs +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/ContentElementTests.cs @@ -1,204 +1,199 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using NUnit.Framework; -using Umbraco.Core; -using Umbraco.Core.Configuration; -using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.Macros; - -namespace Umbraco.Tests.Configurations.UmbracoSettings -{ - [TestFixture] - public class ContentElementTests : UmbracoSettingsTests - { - [Test] - public void EmailAddress() - { - Assert.IsTrue(SettingsSection.Content.NotificationEmailAddress == "robot@umbraco.dk"); - } - [Test] - public virtual void DisableHtmlEmail() - { - Assert.IsTrue(SettingsSection.Content.DisableHtmlEmail == true); - } - - [Test] - public virtual void Can_Set_Multiple() - { - Assert.IsTrue(SettingsSection.Content.Error404Collection.Count() == 3); - Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(0).Culture == "default"); - Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(0).ContentId == 1047); - Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(0).HasContentId); - Assert.IsFalse(SettingsSection.Content.Error404Collection.ElementAt(0).HasContentKey); - Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(1).Culture == "en-US"); - Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(1).ContentXPath == "$site/error [@name = 'error']"); - Assert.IsFalse(SettingsSection.Content.Error404Collection.ElementAt(1).HasContentId); - Assert.IsFalse(SettingsSection.Content.Error404Collection.ElementAt(1).HasContentKey); - Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(2).Culture == "en-UK"); - Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(2).ContentKey == new Guid("8560867F-B88F-4C74-A9A4-679D8E5B3BFC")); - Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(2).HasContentKey); - Assert.IsFalse(SettingsSection.Content.Error404Collection.ElementAt(2).HasContentId); - } - - [Test] - public void ScriptFolderPath() - { - Assert.IsTrue(SettingsSection.Content.ScriptFolderPath == "/scripts"); - } - [Test] - public void ScriptFileTypes() - { - Assert.IsTrue(SettingsSection.Content.ScriptFileTypes.All(x => "js,xml".Split(',').Contains(x))); - } - [Test] - public void DisableScriptEditor() - { - Assert.IsTrue(SettingsSection.Content.ScriptEditorDisable == false); - } - - [Test] - public void ImageFileTypes() - { - Assert.IsTrue(SettingsSection.Content.ImageFileTypes.All(x => "jpeg,jpg,gif,bmp,png,tiff,tif".Split(',').Contains(x))); - } - [Test] - public void AllowedAttributes() - { - Assert.IsTrue(SettingsSection.Content.ImageTagAllowedAttributes.All(x => "src,alt,border,class,style,align,id,name,onclick,usemap".Split(',').Contains(x))); - } - [Test] - public virtual void ImageAutoFillProperties() - { - Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.Count() == 2); - Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).Alias == "umbracoFile"); - Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).WidthFieldAlias == "umbracoWidth"); - Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).HeightFieldAlias == "umbracoHeight"); - Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).LengthFieldAlias == "umbracoBytes"); - Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).ExtensionFieldAlias == "umbracoExtension"); - Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).Alias == "umbracoFile2"); - Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).WidthFieldAlias == "umbracoWidth2"); - Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).HeightFieldAlias == "umbracoHeight2"); - Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).LengthFieldAlias == "umbracoBytes2"); - Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).ExtensionFieldAlias == "umbracoExtension2"); - } - - [Test] - public void UploadAllowDirectories() - { - Assert.IsTrue(SettingsSection.Content.UploadAllowDirectories == true); - } - [Test] - public void DefaultDocumentTypeProperty() - { - Assert.IsTrue(SettingsSection.Content.DefaultDocumentTypeProperty == "Textstring"); - } - [Test] - public void GlobalPreviewStorageEnabled() - { - Assert.IsTrue(SettingsSection.Content.GlobalPreviewStorageEnabled == false); - } - [Test] - public void CloneXmlContent() - { - Assert.IsTrue(SettingsSection.Content.CloneXmlContent == true); - } - [Test] - public void EnsureUniqueNaming() - { - Assert.IsTrue(SettingsSection.Content.EnsureUniqueNaming == true); - } - - - [Test] - public void ForceSafeAliases() - { - Assert.IsTrue(SettingsSection.Content.ForceSafeAliases == true); - } - [Test] - public void XmlCacheEnabled() - { - Assert.IsTrue(SettingsSection.Content.XmlCacheEnabled == true); - } - [Test] - public void ContinouslyUpdateXmlDiskCache() - { - Assert.IsTrue(SettingsSection.Content.ContinouslyUpdateXmlDiskCache == true); - } - [Test] - public virtual void XmlContentCheckForDiskChanges() - { - Assert.IsTrue(SettingsSection.Content.XmlContentCheckForDiskChanges == true); - } - [Test] - public void EnableSplashWhileLoading() - { - Assert.IsTrue(SettingsSection.Content.EnableSplashWhileLoading == false); - } - [Test] - public void PropertyContextHelpOption() - { - Assert.IsTrue(SettingsSection.Content.PropertyContextHelpOption == "text"); - } - [Test] - public void PreviewBadge() - { - Assert.IsTrue(SettingsSection.Content.PreviewBadge == @"In Preview Mode - click to end"); - } - [Test] - public void UmbracoLibraryCacheDuration() - { - Assert.IsTrue(SettingsSection.Content.UmbracoLibraryCacheDuration == 1800); - } - [Test] - public void ResolveUrlsFromTextString() - { - Assert.IsFalse(SettingsSection.Content.ResolveUrlsFromTextString); - } - [Test] - public void MacroErrors() - { - Assert.IsTrue(SettingsSection.Content.MacroErrorBehaviour == MacroErrorBehaviour.Inline); - } - - [Test] - public void DisallowedUploadFiles() - { - Assert.IsTrue(SettingsSection.Content.DisallowedUploadFiles.All(x => "ashx,aspx,ascx,config,cshtml,vbhtml,asmx,air,axd".Split(',').Contains(x))); - } - - [Test] - public void AllowedUploadFiles() - { - Assert.IsTrue(SettingsSection.Content.AllowedUploadFiles.All(x => "jpg,gif,png".Split(',').Contains(x))); - } - - [Test] - [TestCase("png", true)] - [TestCase("jpg", true)] - [TestCase("gif", true)] - // TODO: Why does it flip to TestingDefaults=true for these two tests on AppVeyor. WHY? - //[TestCase("bmp", false)] - //[TestCase("php", false)] - [TestCase("ashx", false)] - [TestCase("config", false)] - public void IsFileAllowedForUpload_WithWhitelist(string extension, bool expected) - { - // Make really sure that defaults are NOT used - TestingDefaults = false; - - Debug.WriteLine("Extension being tested", extension); - Debug.WriteLine("AllowedUploadFiles: {0}", SettingsSection.Content.AllowedUploadFiles); - Debug.WriteLine("DisallowedUploadFiles: {0}", SettingsSection.Content.DisallowedUploadFiles); - - var allowedContainsExtension = SettingsSection.Content.AllowedUploadFiles.Any(x => x.InvariantEquals(extension)); - var disallowedContainsExtension = SettingsSection.Content.DisallowedUploadFiles.Any(x => x.InvariantEquals(extension)); - - Debug.WriteLine("AllowedContainsExtension: {0}", allowedContainsExtension); - Debug.WriteLine("DisallowedContainsExtension: {0}", disallowedContainsExtension); - - Assert.AreEqual(SettingsSection.Content.IsFileAllowedForUpload(extension), expected); - } - } -} +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Macros; + +namespace Umbraco.Tests.Configurations.UmbracoSettings +{ + [TestFixture] + public class ContentElementTests : UmbracoSettingsTests + { + [Test] + public void EmailAddress() + { + Assert.IsTrue(SettingsSection.Content.NotificationEmailAddress == "robot@umbraco.dk"); + } + [Test] + public virtual void DisableHtmlEmail() + { + Assert.IsTrue(SettingsSection.Content.DisableHtmlEmail == true); + } + + [Test] + public virtual void Can_Set_Multiple() + { + Assert.IsTrue(SettingsSection.Content.Error404Collection.Count() == 3); + Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(0).Culture == "default"); + Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(0).ContentId == 1047); + Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(0).HasContentId); + Assert.IsFalse(SettingsSection.Content.Error404Collection.ElementAt(0).HasContentKey); + Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(1).Culture == "en-US"); + Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(1).ContentXPath == "$site/error [@name = 'error']"); + Assert.IsFalse(SettingsSection.Content.Error404Collection.ElementAt(1).HasContentId); + Assert.IsFalse(SettingsSection.Content.Error404Collection.ElementAt(1).HasContentKey); + Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(2).Culture == "en-UK"); + Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(2).ContentKey == new Guid("8560867F-B88F-4C74-A9A4-679D8E5B3BFC")); + Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(2).HasContentKey); + Assert.IsFalse(SettingsSection.Content.Error404Collection.ElementAt(2).HasContentId); + } + + [Test] + public void ScriptFolderPath() + { + Assert.IsTrue(SettingsSection.Content.ScriptFolderPath == "/scripts"); + } + [Test] + public void ScriptFileTypes() + { + Assert.IsTrue(SettingsSection.Content.ScriptFileTypes.All(x => "js,xml".Split(',').Contains(x))); + } + [Test] + public void DisableScriptEditor() + { + Assert.IsTrue(SettingsSection.Content.ScriptEditorDisable == false); + } + + [Test] + public void ImageFileTypes() + { + Assert.IsTrue(SettingsSection.Content.ImageFileTypes.All(x => "jpeg,jpg,gif,bmp,png,tiff,tif".Split(',').Contains(x))); + } + [Test] + public void AllowedAttributes() + { + Assert.IsTrue(SettingsSection.Content.ImageTagAllowedAttributes.All(x => "src,alt,border,class,style,align,id,name,onclick,usemap".Split(',').Contains(x))); + } + [Test] + public virtual void ImageAutoFillProperties() + { + Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.Count() == 2); + Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).Alias == "umbracoFile"); + Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).WidthFieldAlias == "umbracoWidth"); + Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).HeightFieldAlias == "umbracoHeight"); + Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).LengthFieldAlias == "umbracoBytes"); + Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).ExtensionFieldAlias == "umbracoExtension"); + Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).Alias == "umbracoFile2"); + Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).WidthFieldAlias == "umbracoWidth2"); + Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).HeightFieldAlias == "umbracoHeight2"); + Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).LengthFieldAlias == "umbracoBytes2"); + Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).ExtensionFieldAlias == "umbracoExtension2"); + } + + [Test] + public void UploadAllowDirectories() + { + Assert.IsTrue(SettingsSection.Content.UploadAllowDirectories == true); + } + [Test] + public void DefaultDocumentTypeProperty() + { + Assert.IsTrue(SettingsSection.Content.DefaultDocumentTypeProperty == "Textstring"); + } + [Test] + public void GlobalPreviewStorageEnabled() + { + Assert.IsTrue(SettingsSection.Content.GlobalPreviewStorageEnabled == false); + } + [Test] + public void CloneXmlContent() + { + Assert.IsTrue(SettingsSection.Content.CloneXmlContent == true); + } + [Test] + public void EnsureUniqueNaming() + { + Assert.IsTrue(SettingsSection.Content.EnsureUniqueNaming == true); + } + + + [Test] + public void ForceSafeAliases() + { + Assert.IsTrue(SettingsSection.Content.ForceSafeAliases == true); + } + [Test] + public void XmlCacheEnabled() + { + Assert.IsTrue(SettingsSection.Content.XmlCacheEnabled == true); + } + [Test] + public void ContinouslyUpdateXmlDiskCache() + { + Assert.IsTrue(SettingsSection.Content.ContinouslyUpdateXmlDiskCache == true); + } + [Test] + public virtual void XmlContentCheckForDiskChanges() + { + Assert.IsTrue(SettingsSection.Content.XmlContentCheckForDiskChanges == true); + } + [Test] + public void EnableSplashWhileLoading() + { + Assert.IsTrue(SettingsSection.Content.EnableSplashWhileLoading == false); + } + [Test] + public void PropertyContextHelpOption() + { + Assert.IsTrue(SettingsSection.Content.PropertyContextHelpOption == "text"); + } + [Test] + public void PreviewBadge() + { + Assert.IsTrue(SettingsSection.Content.PreviewBadge == @"In Preview Mode - click to end"); + } + [Test] + public void ResolveUrlsFromTextString() + { + Assert.IsFalse(SettingsSection.Content.ResolveUrlsFromTextString); + } + [Test] + public void MacroErrors() + { + Assert.IsTrue(SettingsSection.Content.MacroErrorBehaviour == MacroErrorBehaviour.Inline); + } + + [Test] + public void DisallowedUploadFiles() + { + Assert.IsTrue(SettingsSection.Content.DisallowedUploadFiles.All(x => "ashx,aspx,ascx,config,cshtml,vbhtml,asmx,air,axd".Split(',').Contains(x))); + } + + [Test] + public void AllowedUploadFiles() + { + Assert.IsTrue(SettingsSection.Content.AllowedUploadFiles.All(x => "jpg,gif,png".Split(',').Contains(x))); + } + + [Test] + [TestCase("png", true)] + [TestCase("jpg", true)] + [TestCase("gif", true)] + // TODO: Why does it flip to TestingDefaults=true for these two tests on AppVeyor. WHY? + //[TestCase("bmp", false)] + //[TestCase("php", false)] + [TestCase("ashx", false)] + [TestCase("config", false)] + public void IsFileAllowedForUpload_WithWhitelist(string extension, bool expected) + { + // Make really sure that defaults are NOT used + TestingDefaults = false; + + Debug.WriteLine("Extension being tested", extension); + Debug.WriteLine("AllowedUploadFiles: {0}", SettingsSection.Content.AllowedUploadFiles); + Debug.WriteLine("DisallowedUploadFiles: {0}", SettingsSection.Content.DisallowedUploadFiles); + + var allowedContainsExtension = SettingsSection.Content.AllowedUploadFiles.Any(x => x.InvariantEquals(extension)); + var disallowedContainsExtension = SettingsSection.Content.DisallowedUploadFiles.Any(x => x.InvariantEquals(extension)); + + Debug.WriteLine("AllowedContainsExtension: {0}", allowedContainsExtension); + Debug.WriteLine("DisallowedContainsExtension: {0}", disallowedContainsExtension); + + Assert.AreEqual(SettingsSection.Content.IsFileAllowedForUpload(extension), expected); + } + } +} diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.config b/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.config index 07eb355d68..9b52c8356e 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.config +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.config @@ -77,10 +77,6 @@ In Preview Mode - click to end]]> - - - 1800 - - - $(NuGetPackageFolders.Split(';')[0]) - - - - - - - - - + + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {5D3B8245-ADA6-453F-A008-50ED04BFE770} + Library + Properties + Umbraco.Tests + Umbraco.Tests + v4.7.2 + 512 + ..\ + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + latest + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + latest + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + SqlResources.resx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + ResourceFiles.resx + + + + + + + + + + + + + + + + + + + + True + True + ImportResources.resx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + TestFiles.resx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Designer + + + Designer + Always + + + Designer + Always + + + Designer + Always + + + Designer + Always + + + + + + Designer + + + + Always + + + + + {31785BC3-256C-4613-B2F5-A1B0BDDED8C1} + Umbraco.Core + + + {651E1350-91B6-44B7-BD60-7207006D7003} + Umbraco.Web + + + {07fbc26b-2927-4a22-8d96-d644c667fecc} + Umbraco.Examine + + + + + ResXFileCodeGenerator + SqlResources.Designer.cs + Designer + + + ResXFileCodeGenerator + ImportResources.Designer.cs + Designer + + + ResXFileCodeGenerator + ResourceFiles.Designer.cs + + + ResXFileCodeGenerator + TestFiles.Designer.cs + Designer + + + + + Designer + Always + + + + + + + Designer + + + + + + + + + + Designer + + + + + + + + + + + + + $(NuGetPackageFolders.Split(';')[0]) + + + + + + + + + \ No newline at end of file diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.config b/src/Umbraco.Web.UI/config/umbracoSettings.config index 9e4644c20e..59f8e7bd5d 100644 --- a/src/Umbraco.Web.UI/config/umbracoSettings.config +++ b/src/Umbraco.Web.UI/config/umbracoSettings.config @@ -78,9 +78,6 @@ In Preview Mode - click to end ]]> - - - 1800 diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs index 8f354240e0..db4b03df1e 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs @@ -1,725 +1,741 @@ -using System; -using System.Collections.Generic; -using System.Configuration; -using System.IO; -using System.Linq; -using System.Threading; -using System.Xml.XPath; -using Examine; -using Examine.LuceneEngine.SearchCriteria; -using Examine.Providers; -using Lucene.Net.Store; -using Umbraco.Core; -using Umbraco.Core.Logging; -using Umbraco.Core.Models; -using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.Xml; -using Umbraco.Examine; -using umbraco; -using Umbraco.Core.Cache; -using Umbraco.Core.Services; -using Umbraco.Web.Composing; - -namespace Umbraco.Web.PublishedCache.XmlPublishedCache -{ - /// - /// An IPublishedMediaStore that first checks for the media in Examine, and then reverts to the database - /// - /// - /// NOTE: In the future if we want to properly cache all media this class can be extended or replaced when these classes/interfaces are exposed publicly. - /// - internal class PublishedMediaCache : PublishedCacheBase, IPublishedMediaCache - { - private readonly IMediaService _mediaService; - private readonly IUserService _userService; - - // by default these are null unless specified by the ctor dedicated to tests - // when they are null the cache derives them from the ExamineManager, see - // method GetExamineManagerSafe(). - // - private readonly ISearcher _searchProvider; - private readonly IIndexer _indexProvider; - private readonly XmlStore _xmlStore; - private readonly PublishedContentTypeCache _contentTypeCache; - - // must be specified by the ctor - private readonly ICacheProvider _cacheProvider; - - public PublishedMediaCache(XmlStore xmlStore, IMediaService mediaService, IUserService userService, ICacheProvider cacheProvider, PublishedContentTypeCache contentTypeCache) - : base(false) - { - _mediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService)); - _userService = userService ?? throw new ArgumentNullException(nameof(userService)); - - _cacheProvider = cacheProvider; - _xmlStore = xmlStore; - _contentTypeCache = contentTypeCache; - } - - /// - /// Generally used for unit testing to use an explicit examine searcher - /// - /// - /// - /// - /// - /// - /// - internal PublishedMediaCache(IMediaService mediaService, IUserService userService, ISearcher searchProvider, BaseIndexProvider indexProvider, ICacheProvider cacheProvider, PublishedContentTypeCache contentTypeCache) - : base(false) - { - _mediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService)); - _userService = userService ?? throw new ArgumentNullException(nameof(userService)); - _searchProvider = searchProvider ?? throw new ArgumentNullException(nameof(searchProvider)); - _indexProvider = indexProvider ?? throw new ArgumentNullException(nameof(indexProvider)); - _cacheProvider = cacheProvider; - _contentTypeCache = contentTypeCache; - } - - static PublishedMediaCache() - { - InitializeCacheConfig(); - } - - public override IPublishedContent GetById(bool preview, int nodeId) - { - return GetUmbracoMedia(nodeId); - } - - public override IPublishedContent GetById(bool preview, Guid nodeId) - { - throw new NotImplementedException(); - } - - public override bool HasById(bool preview, int contentId) - { - return GetUmbracoMedia(contentId) != null; - } - - public override IEnumerable GetAtRoot(bool preview) - { - var searchProvider = GetSearchProviderSafe(); - - if (searchProvider != null) - { - try - { - // first check in Examine for the cache values - // +(+parentID:-1) +__IndexType:media - - var criteria = searchProvider.CreateCriteria("media"); - var filter = criteria.ParentId(-1).Not().Field(UmbracoExamineIndexer.IndexPathFieldName, "-1,-21,".MultipleCharacterWildcard()); - - var result = searchProvider.Search(filter.Compile()); - if (result != null) - return result.Select(x => CreateFromCacheValues(ConvertFromSearchResult(x))); - } - catch (Exception ex) - { - if (ex is FileNotFoundException) - { - //Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco - //See this thread: http://examine.cdodeplex.com/discussions/264341 - //Catch the exception here for the time being, and just fallback to GetMedia - //TODO: Need to fix examine in LB scenarios! - Current.Logger.Error("Could not load data from Examine index for media", ex); - } - else if (ex is AlreadyClosedException) - { - //If the app domain is shutting down and the site is under heavy load the index reader will be closed and it really cannot - //be re-opened since the app domain is shutting down. In this case we have no option but to try to load the data from the db. - Current.Logger.Error("Could not load data from Examine index for media, the app domain is most likely in a shutdown state", ex); - } - else throw; - } - } - - //something went wrong, fetch from the db - - var rootMedia = _mediaService.GetRootMedia(); - return rootMedia.Select(m => GetUmbracoMedia(m.Id)); - } - - public override IPublishedContent GetSingleByXPath(bool preview, string xpath, XPathVariable[] vars) - { - throw new NotImplementedException("PublishedMediaCache does not support XPath."); - //var navigator = CreateNavigator(preview); - //var iterator = navigator.Select(xpath, vars); - //return GetSingleByXPath(iterator); - } - - public override IPublishedContent GetSingleByXPath(bool preview, XPathExpression xpath, XPathVariable[] vars) - { - throw new NotImplementedException("PublishedMediaCache does not support XPath."); - //var navigator = CreateNavigator(preview); - //var iterator = navigator.Select(xpath, vars); - //return GetSingleByXPath(iterator); - } - - private IPublishedContent GetSingleByXPath(XPathNodeIterator iterator) - { - throw new NotImplementedException("PublishedMediaCache does not support XPath."); - //if (iterator.MoveNext() == false) return null; - //var idAttr = iterator.Current.GetAttribute("id", ""); - //int id; - //return int.TryParse(idAttr, out id) ? GetUmbracoMedia(id) : null; - } - - public override IEnumerable GetByXPath(bool preview, string xpath, XPathVariable[] vars) - { - throw new NotImplementedException("PublishedMediaCache does not support XPath."); - //var navigator = CreateNavigator(preview); - //var iterator = navigator.Select(xpath, vars); - //return GetByXPath(iterator); - } - - public override IEnumerable GetByXPath(bool preview, XPathExpression xpath, XPathVariable[] vars) - { - throw new NotImplementedException("PublishedMediaCache does not support XPath."); - //var navigator = CreateNavigator(preview); - //var iterator = navigator.Select(xpath, vars); - //return GetByXPath(iterator); - } - - private IEnumerable GetByXPath(XPathNodeIterator iterator) - { - while (iterator.MoveNext()) - { - var idAttr = iterator.Current.GetAttribute("id", ""); - int id; - if (int.TryParse(idAttr, out id)) - yield return GetUmbracoMedia(id); - } - } - - public override XPathNavigator CreateNavigator(bool preview) - { - throw new NotImplementedException("PublishedMediaCache does not support XPath."); - //var doc = _xmlStore.GetMediaXml(); - //return doc.CreateNavigator(); - } - - public override XPathNavigator CreateNodeNavigator(int id, bool preview) - { - // preview is ignored for media cache - - // this code is mostly used when replacing old media.ToXml() code, and that code - // stored the XML attached to the media itself - so for some time in memory - so - // unless we implement some sort of cache here, we're probably degrading perfs. - - XPathNavigator navigator = null; - var node = _xmlStore.GetMediaXmlNode(id); - if (node != null) - { - navigator = node.CreateNavigator(); - } - return navigator; - } - - public override bool HasContent(bool preview) { throw new NotImplementedException(); } - - private static IExamineManager GetExamineManagerSafe() - { - try - { - return ExamineManager.Instance; - } - catch (TypeInitializationException) - { - return null; - } - } - - private ISearcher GetSearchProviderSafe() - { - if (_searchProvider != null) - return _searchProvider; - - var eMgr = GetExamineManagerSafe(); - if (eMgr == null) return null; - - try - { - //by default use the internal index - return eMgr.GetSearcher(Constants.Examine.InternalIndexer); - } - catch (FileNotFoundException) - { - //Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco - //See this thread: http://examine.cdodeplex.com/discussions/264341 - //Catch the exception here for the time being, and just fallback to GetMedia - //TODO: Need to fix examine in LB scenarios! - } - catch (NullReferenceException) - { - //This will occur when the search provider cannot be initialized. In newer examine versions the initialization is lazy and therefore - // the manager will return the singleton without throwing initialization errors, however if examine isn't configured correctly a null - // reference error will occur because the examine settings are null. - } - catch (AlreadyClosedException) - { - //If the app domain is shutting down and the site is under heavy load the index reader will be closed and it really cannot - //be re-opened since the app domain is shutting down. In this case we have no option but to try to load the data from the db. - } - return null; - } - - private IPublishedContent GetUmbracoMedia(int id) - { - // this recreates an IPublishedContent and model each time - // it is called, but at least it should NOT hit the database - // nor Lucene each time, relying on the memory cache instead - - if (id <= 0) return null; // fail fast - - var cacheValues = GetCacheValues(id, GetUmbracoMediaCacheValues); - - return cacheValues == null ? null : CreateFromCacheValues(cacheValues); - } - - private CacheValues GetUmbracoMediaCacheValues(int id) - { - var searchProvider = GetSearchProviderSafe(); - - if (searchProvider != null) - { - try - { - // first check in Examine as this is WAY faster - // - // the filter will create a query like this: - // +(+__NodeId:3113 -__Path:-1,-21,*) +__IndexType:media - // - // note that since the use of the wildcard, it automatically escapes it in Lucene. - - var criteria = searchProvider.CreateCriteria("media"); - var filter = criteria.Id(id.ToInvariantString()).Not().Field(UmbracoExamineIndexer.IndexPathFieldName, "-1,-21,".MultipleCharacterWildcard()); - - var result = searchProvider.Search(filter.Compile()).FirstOrDefault(); - if (result != null) return ConvertFromSearchResult(result); - } - catch (Exception ex) - { - if (ex is FileNotFoundException) - { - //Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco - //See this thread: http://examine.cdodeplex.com/discussions/264341 - //Catch the exception here for the time being, and just fallback to GetMedia - //TODO: Need to fix examine in LB scenarios! - Current.Logger.Error("Could not load data from Examine index for media", ex); - } - else if (ex is AlreadyClosedException) - { - //If the app domain is shutting down and the site is under heavy load the index reader will be closed and it really cannot - //be re-opened since the app domain is shutting down. In this case we have no option but to try to load the data from the db. - Current.Logger.Error("Could not load data from Examine index for media, the app domain is most likely in a shutdown state", ex); - } - else throw; - } - } - - // don't log a warning here, as it can flood the log in case of eg a media picker referencing a media - // that has been deleted, hence is not in the Examine index anymore (for a good reason). try to get - // the media from the service, first - var media = _mediaService.GetById(id); - if (media == null || media.Trashed) return null; // not found, ok - - // so, the media was not found in Examine's index *yet* it exists, which probably indicates that - // the index is corrupted. Or not up-to-date. Log a warning, but only once, and only if seeing the - // error more that a number of times. - - var miss = Interlocked.CompareExchange(ref _examineIndexMiss, 0, 0); // volatile read - if (miss < ExamineIndexMissMax && Interlocked.Increment(ref _examineIndexMiss) == ExamineIndexMissMax) - Current.Logger.Warn(() => $"Failed ({ExamineIndexMissMax} times) to retrieve medias from Examine index and had to load" - + " them from DB. This may indicate that the Examine index is corrupted."); - - return ConvertFromIMedia(media); - } - - private const int ExamineIndexMissMax = 10; - private int _examineIndexMiss; - - internal CacheValues ConvertFromXPathNodeIterator(XPathNodeIterator media, int id) - { - if (media?.Current != null) - { - return media.Current.Name.InvariantEquals("error") - ? null - : ConvertFromXPathNavigator(media.Current); - } - - Current.Logger.Warn(() => - $"Could not retrieve media {id} from Examine index or from legacy library.GetMedia method"); - - return null; - } - - internal CacheValues ConvertFromSearchResult(SearchResult searchResult) - { - // note: fixing fields in 7.x, removed by Shan for 8.0 - - return new CacheValues - { - Values = searchResult.Fields, - FromExamine = true - }; - } - - internal CacheValues ConvertFromXPathNavigator(XPathNavigator xpath, bool forceNav = false) - { - if (xpath == null) throw new ArgumentNullException(nameof(xpath)); - - var values = new Dictionary { { "nodeName", xpath.GetAttribute("nodeName", "") } }; - values["nodeTypeAlias"] = xpath.Name; - - var result = xpath.SelectChildren(XPathNodeType.Element); - //add the attributes e.g. id, parentId etc - if (result.Current != null && result.Current.HasAttributes) - { - if (result.Current.MoveToFirstAttribute()) - { - //checking for duplicate keys because of the 'nodeTypeAlias' might already be added above. - if (values.ContainsKey(result.Current.Name) == false) - { - values[result.Current.Name] = result.Current.Value; - } - while (result.Current.MoveToNextAttribute()) - { - if (values.ContainsKey(result.Current.Name) == false) - { - values[result.Current.Name] = result.Current.Value; - } - } - result.Current.MoveToParent(); - } - } - // because, migration - if (values.ContainsKey("key") == false) - values["key"] = Guid.Empty.ToString(); - //add the user props - while (result.MoveNext()) - { - if (result.Current != null && result.Current.HasAttributes == false) - { - var value = result.Current.Value; - if (string.IsNullOrEmpty(value)) - { - if (result.Current.HasAttributes || result.Current.SelectChildren(XPathNodeType.Element).Count > 0) - { - value = result.Current.OuterXml; - } - } - values[result.Current.Name] = value; - } - } - - return new CacheValues - { - Values = values, - XPath = forceNav ? xpath : null // outside of tests we do NOT want to cache the navigator! - }; - } - - internal CacheValues ConvertFromIMedia(IMedia media) - { - var values = new Dictionary(); - - var creator = _userService.GetProfileById(media.CreatorId); - var creatorName = creator == null ? "" : creator.Name; - - values["id"] = media.Id.ToString(); - values["key"] = media.Key.ToString(); - values["parentID"] = media.ParentId.ToString(); - values["level"] = media.Level.ToString(); - values["creatorID"] = media.CreatorId.ToString(); - values["creatorName"] = creatorName; - values["writerID"] = media.CreatorId.ToString(); - values["writerName"] = creatorName; - values["template"] = "0"; - values["urlName"] = ""; - values["sortOrder"] = media.SortOrder.ToString(); - values["createDate"] = media.CreateDate.ToString("yyyy-MM-dd HH:mm:ss"); - values["updateDate"] = media.UpdateDate.ToString("yyyy-MM-dd HH:mm:ss"); - values["nodeName"] = media.Name; - values["path"] = media.Path; - values["nodeType"] = media.ContentType.Id.ToString(); - values["nodeTypeAlias"] = media.ContentType.Alias; - - // add the user props - foreach (var prop in media.Properties) - values[prop.Alias] = prop.GetValue()?.ToString(); - - return new CacheValues - { - Values = values - }; - } - - /// - /// We will need to first check if the document was loaded by Examine, if so we'll need to check if this property exists - /// in the results, if it does not, then we'll have to revert to looking up in the db. - /// - /// - /// - /// - private IPublishedProperty GetProperty(DictionaryPublishedContent dd, string alias) - { - //lets check if the alias does not exist on the document. - //NOTE: Examine will not index empty values and we do not output empty XML Elements to the cache - either of these situations - // would mean that the property is missing from the collection whether we are getting the value from Examine or from the library media cache. - if (dd.Properties.All(x => x.Alias.InvariantEquals(alias) == false)) - { - return null; - } - - if (dd.LoadedFromExamine) - { - //We are going to check for a special field however, that is because in some cases we store a 'Raw' - //value in the index such as for xml/html. - var rawValue = dd.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(UmbracoExamineIndexer.RawFieldPrefix + alias)); - return rawValue - ?? dd.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(alias)); - } - - //if its not loaded from examine, then just return the property - return dd.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(alias)); - } - - /// - /// A Helper methods to return the children for media whther it is based on examine or xml - /// - /// - /// - /// - private IEnumerable GetChildrenMedia(int parentId, XPathNavigator xpath = null) - { - - //if there is no navigator, try examine first, then re-look it up - if (xpath == null) - { - var searchProvider = GetSearchProviderSafe(); - - if (searchProvider != null) - { - try - { - //first check in Examine as this is WAY faster - var criteria = searchProvider.CreateCriteria("media"); - - var filter = criteria.ParentId(parentId).Not().Field(UmbracoExamineIndexer.IndexPathFieldName, "-1,-21,".MultipleCharacterWildcard()); - //the above filter will create a query like this, NOTE: That since the use of the wildcard, it automatically escapes it in Lucene. - //+(+parentId:3113 -__Path:-1,-21,*) +__IndexType:media - - // sort with the Sort field (updated for 8.0) - var results = searchProvider.Search( - filter.And().OrderBy(new SortableField("sortOrder", SortType.Int)).Compile()); - - if (results.Any()) - { - // var medias = results.Select(ConvertFromSearchResult); - var medias = results.Select(x => - { - int nid; - if (int.TryParse(x["__NodeId"], out nid) == false && int.TryParse(x["NodeId"], out nid) == false) - throw new Exception("Failed to extract NodeId from search result."); - var cacheValues = GetCacheValues(nid, id => ConvertFromSearchResult(x)); - return CreateFromCacheValues(cacheValues); - }); - - return medias; - } - - //if there's no result then return null. Previously we defaulted back to library.GetMedia below - //but this will always get called for when we are getting descendents since many items won't have - //children and then we are hitting the database again! - //So instead we're going to rely on Examine to have the correct results like it should. - return Enumerable.Empty(); - } - catch (FileNotFoundException) - { - //Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco - //See this thread: http://examine.cdodeplex.com/discussions/264341 - //Catch the exception here for the time being, and just fallback to GetMedia - } - } - - //falling back to get media - - var media = library.GetMedia(parentId, true); - if (media?.Current != null) - { - xpath = media.Current; - } - else - { - return Enumerable.Empty(); - } - } - - var mediaList = new List(); - - // this is so bad, really - var item = xpath.Select("//*[@id='" + parentId + "']"); - if (item.Current == null) - return Enumerable.Empty(); - var items = item.Current.SelectChildren(XPathNodeType.Element); - - // and this does not work, because... meh - //var q = "//* [@id='" + parentId + "']/* [@id]"; - //var items = xpath.Select(q); - - foreach (XPathNavigator itemm in items) - { - int id; - if (int.TryParse(itemm.GetAttribute("id", ""), out id) == false) - continue; // wtf? - var captured = itemm; - var cacheValues = GetCacheValues(id, idd => ConvertFromXPathNavigator(captured)); - mediaList.Add(CreateFromCacheValues(cacheValues)); - } - - ////The xpath might be the whole xpath including the current ones ancestors so we need to select the current node - //var item = xpath.Select("//*[@id='" + parentId + "']"); - //if (item.Current == null) - //{ - // return Enumerable.Empty(); - //} - //var children = item.Current.SelectChildren(XPathNodeType.Element); - - //foreach(XPathNavigator x in children) - //{ - // //NOTE: I'm not sure why this is here, it is from legacy code of ExamineBackedMedia, but - // // will leave it here as it must have done something! - // if (x.Name != "contents") - // { - // //make sure it's actually a node, not a property - // if (!string.IsNullOrEmpty(x.GetAttribute("path", "")) && - // !string.IsNullOrEmpty(x.GetAttribute("id", ""))) - // { - // mediaList.Add(ConvertFromXPathNavigator(x)); - // } - // } - //} - - return mediaList; - } - - internal void Resync() - { - // clear recursive properties cached by XmlPublishedContent.GetProperty - // assume that nothing else is going to cache IPublishedProperty items (else would need to do ByKeySearch) - // NOTE all properties cleared when clearing the content cache (see content cache) - //_cacheProvider.ClearCacheObjectTypes(); - //_cacheProvider.ClearCacheByKeySearch("XmlPublishedCache.PublishedMediaCache:RecursiveProperty-"); - } - - #region Content types - - public override PublishedContentType GetContentType(int id) - { - return _contentTypeCache.Get(PublishedItemType.Media, id); - } - - public override PublishedContentType GetContentType(string alias) - { - return _contentTypeCache.Get(PublishedItemType.Media, alias); - } - - public override IEnumerable GetByContentType(PublishedContentType contentType) - { - throw new NotImplementedException(); - } - - #endregion - - // REFACTORING - - // caching the basic atomic values - and the parent id - // but NOT caching actual parent nor children and NOT even - // the list of children ids - BUT caching the path - - internal class CacheValues - { - public IReadOnlyDictionary Values { get; set; } - public XPathNavigator XPath { get; set; } - public bool FromExamine { get; set; } - } - - public const string PublishedMediaCacheKey = "MediaCacheMeh."; - private const int PublishedMediaCacheTimespanSeconds = 4 * 60; // 4 mins - private static TimeSpan _publishedMediaCacheTimespan; - private static bool _publishedMediaCacheEnabled; - - private static void InitializeCacheConfig() - { - var value = ConfigurationManager.AppSettings["Umbraco.PublishedMediaCache.Seconds"]; - int seconds; - if (int.TryParse(value, out seconds) == false) - seconds = PublishedMediaCacheTimespanSeconds; - if (seconds > 0) - { - _publishedMediaCacheEnabled = true; - _publishedMediaCacheTimespan = TimeSpan.FromSeconds(seconds); - } - else - { - _publishedMediaCacheEnabled = false; - } - } - - internal IPublishedContent CreateFromCacheValues(CacheValues cacheValues) - { - var content = new DictionaryPublishedContent( - cacheValues.Values, - parentId => parentId < 0 ? null : GetUmbracoMedia(parentId), - GetChildrenMedia, - GetProperty, - _cacheProvider, - _contentTypeCache, - cacheValues.XPath, // though, outside of tests, that should be null - cacheValues.FromExamine - ); - return content.CreateModel(); - } - - private static CacheValues GetCacheValues(int id, Func func) - { - if (_publishedMediaCacheEnabled == false) - return func(id); - - var cache = Current.ApplicationCache.RuntimeCache; - var key = PublishedMediaCacheKey + id; - return (CacheValues)cache.GetCacheItem(key, () => func(id), _publishedMediaCacheTimespan); - } - - internal static void ClearCache(int id) - { - var cache = Current.ApplicationCache.RuntimeCache; - var sid = id.ToString(); - var key = PublishedMediaCacheKey + sid; - - // we do clear a lot of things... but the cache refresher is somewhat - // convoluted and it's hard to tell what to clear exactly ;-( - - // clear the parent - NOT (why?) - //var exist = (CacheValues) cache.GetCacheItem(key); - //if (exist != null) - // cache.ClearCacheItem(PublishedMediaCacheKey + GetValuesValue(exist.Values, "parentID")); - - // clear the item - cache.ClearCacheItem(key); - - // clear all children - in case we moved and their path has changed - var fid = "/" + sid + "/"; - cache.ClearCacheObjectTypes((k, v) => - GetValuesValue(v.Values, "path", "__Path").Contains(fid)); - } - - private static string GetValuesValue(IReadOnlyDictionary d, params string[] keys) - { - string value = null; - var ignored = keys.Any(x => d.TryGetValue(x, out value)); - return value ?? ""; - } - } -} +using System; +using System.Collections.Generic; +using System.Configuration; +using System.IO; +using System.Linq; +using System.Threading; +using System.Xml.XPath; +using Examine; +using Examine.LuceneEngine.SearchCriteria; +using Examine.Providers; +using Lucene.Net.Store; +using Umbraco.Core; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Xml; +using Umbraco.Examine; +using umbraco; +using Umbraco.Core.Cache; +using Umbraco.Core.Services; +using Umbraco.Web.Composing; + +namespace Umbraco.Web.PublishedCache.XmlPublishedCache +{ + /// + /// An IPublishedMediaStore that first checks for the media in Examine, and then reverts to the database + /// + /// + /// NOTE: In the future if we want to properly cache all media this class can be extended or replaced when these classes/interfaces are exposed publicly. + /// + internal class PublishedMediaCache : PublishedCacheBase, IPublishedMediaCache + { + private readonly IMediaService _mediaService; + private readonly IUserService _userService; + + // by default these are null unless specified by the ctor dedicated to tests + // when they are null the cache derives them from the ExamineManager, see + // method GetExamineManagerSafe(). + // + private readonly ISearcher _searchProvider; + private readonly IIndexer _indexProvider; + private readonly XmlStore _xmlStore; + private readonly PublishedContentTypeCache _contentTypeCache; + + // must be specified by the ctor + private readonly ICacheProvider _cacheProvider; + + public PublishedMediaCache(XmlStore xmlStore, IMediaService mediaService, IUserService userService, ICacheProvider cacheProvider, PublishedContentTypeCache contentTypeCache) + : base(false) + { + _mediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService)); + _userService = userService ?? throw new ArgumentNullException(nameof(userService)); + + _cacheProvider = cacheProvider; + _xmlStore = xmlStore; + _contentTypeCache = contentTypeCache; + } + + /// + /// Generally used for unit testing to use an explicit examine searcher + /// + /// + /// + /// + /// + /// + /// + internal PublishedMediaCache(IMediaService mediaService, IUserService userService, ISearcher searchProvider, BaseIndexProvider indexProvider, ICacheProvider cacheProvider, PublishedContentTypeCache contentTypeCache) + : base(false) + { + _mediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService)); + _userService = userService ?? throw new ArgumentNullException(nameof(userService)); + _searchProvider = searchProvider ?? throw new ArgumentNullException(nameof(searchProvider)); + _indexProvider = indexProvider ?? throw new ArgumentNullException(nameof(indexProvider)); + _cacheProvider = cacheProvider; + _contentTypeCache = contentTypeCache; + } + + static PublishedMediaCache() + { + InitializeCacheConfig(); + } + + public override IPublishedContent GetById(bool preview, int nodeId) + { + return GetUmbracoMedia(nodeId); + } + + public override IPublishedContent GetById(bool preview, Guid nodeId) + { + throw new NotImplementedException(); + } + + public override bool HasById(bool preview, int contentId) + { + return GetUmbracoMedia(contentId) != null; + } + + public override IEnumerable GetAtRoot(bool preview) + { + var searchProvider = GetSearchProviderSafe(); + + if (searchProvider != null) + { + try + { + // first check in Examine for the cache values + // +(+parentID:-1) +__IndexType:media + + var criteria = searchProvider.CreateCriteria("media"); + var filter = criteria.ParentId(-1).Not().Field(UmbracoExamineIndexer.IndexPathFieldName, "-1,-21,".MultipleCharacterWildcard()); + + var result = searchProvider.Search(filter.Compile()); + if (result != null) + return result.Select(x => CreateFromCacheValues(ConvertFromSearchResult(x))); + } + catch (Exception ex) + { + if (ex is FileNotFoundException) + { + //Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco + //See this thread: http://examine.cdodeplex.com/discussions/264341 + //Catch the exception here for the time being, and just fallback to GetMedia + //TODO: Need to fix examine in LB scenarios! + Current.Logger.Error("Could not load data from Examine index for media", ex); + } + else if (ex is AlreadyClosedException) + { + //If the app domain is shutting down and the site is under heavy load the index reader will be closed and it really cannot + //be re-opened since the app domain is shutting down. In this case we have no option but to try to load the data from the db. + Current.Logger.Error("Could not load data from Examine index for media, the app domain is most likely in a shutdown state", ex); + } + else throw; + } + } + + //something went wrong, fetch from the db + + var rootMedia = _mediaService.GetRootMedia(); + return rootMedia.Select(m => GetUmbracoMedia(m.Id)); + } + + public override IPublishedContent GetSingleByXPath(bool preview, string xpath, XPathVariable[] vars) + { + throw new NotImplementedException("PublishedMediaCache does not support XPath."); + //var navigator = CreateNavigator(preview); + //var iterator = navigator.Select(xpath, vars); + //return GetSingleByXPath(iterator); + } + + public override IPublishedContent GetSingleByXPath(bool preview, XPathExpression xpath, XPathVariable[] vars) + { + throw new NotImplementedException("PublishedMediaCache does not support XPath."); + //var navigator = CreateNavigator(preview); + //var iterator = navigator.Select(xpath, vars); + //return GetSingleByXPath(iterator); + } + + private IPublishedContent GetSingleByXPath(XPathNodeIterator iterator) + { + throw new NotImplementedException("PublishedMediaCache does not support XPath."); + //if (iterator.MoveNext() == false) return null; + //var idAttr = iterator.Current.GetAttribute("id", ""); + //int id; + //return int.TryParse(idAttr, out id) ? GetUmbracoMedia(id) : null; + } + + public override IEnumerable GetByXPath(bool preview, string xpath, XPathVariable[] vars) + { + throw new NotImplementedException("PublishedMediaCache does not support XPath."); + //var navigator = CreateNavigator(preview); + //var iterator = navigator.Select(xpath, vars); + //return GetByXPath(iterator); + } + + public override IEnumerable GetByXPath(bool preview, XPathExpression xpath, XPathVariable[] vars) + { + throw new NotImplementedException("PublishedMediaCache does not support XPath."); + //var navigator = CreateNavigator(preview); + //var iterator = navigator.Select(xpath, vars); + //return GetByXPath(iterator); + } + + private IEnumerable GetByXPath(XPathNodeIterator iterator) + { + while (iterator.MoveNext()) + { + var idAttr = iterator.Current.GetAttribute("id", ""); + int id; + if (int.TryParse(idAttr, out id)) + yield return GetUmbracoMedia(id); + } + } + + public override XPathNavigator CreateNavigator(bool preview) + { + throw new NotImplementedException("PublishedMediaCache does not support XPath."); + //var doc = _xmlStore.GetMediaXml(); + //return doc.CreateNavigator(); + } + + public override XPathNavigator CreateNodeNavigator(int id, bool preview) + { + // preview is ignored for media cache + + // this code is mostly used when replacing old media.ToXml() code, and that code + // stored the XML attached to the media itself - so for some time in memory - so + // unless we implement some sort of cache here, we're probably degrading perfs. + + XPathNavigator navigator = null; + var node = _xmlStore.GetMediaXmlNode(id); + if (node != null) + { + navigator = node.CreateNavigator(); + } + return navigator; + } + + public override bool HasContent(bool preview) { throw new NotImplementedException(); } + + private static IExamineManager GetExamineManagerSafe() + { + try + { + return ExamineManager.Instance; + } + catch (TypeInitializationException) + { + return null; + } + } + + private ISearcher GetSearchProviderSafe() + { + if (_searchProvider != null) + return _searchProvider; + + var eMgr = GetExamineManagerSafe(); + if (eMgr == null) return null; + + try + { + //by default use the internal index + return eMgr.GetSearcher(Constants.Examine.InternalIndexer); + } + catch (FileNotFoundException) + { + //Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco + //See this thread: http://examine.cdodeplex.com/discussions/264341 + //Catch the exception here for the time being, and just fallback to GetMedia + //TODO: Need to fix examine in LB scenarios! + } + catch (NullReferenceException) + { + //This will occur when the search provider cannot be initialized. In newer examine versions the initialization is lazy and therefore + // the manager will return the singleton without throwing initialization errors, however if examine isn't configured correctly a null + // reference error will occur because the examine settings are null. + } + catch (AlreadyClosedException) + { + //If the app domain is shutting down and the site is under heavy load the index reader will be closed and it really cannot + //be re-opened since the app domain is shutting down. In this case we have no option but to try to load the data from the db. + } + return null; + } + + private IPublishedContent GetUmbracoMedia(int id) + { + // this recreates an IPublishedContent and model each time + // it is called, but at least it should NOT hit the database + // nor Lucene each time, relying on the memory cache instead + + if (id <= 0) return null; // fail fast + + var cacheValues = GetCacheValues(id, GetUmbracoMediaCacheValues); + + return cacheValues == null ? null : CreateFromCacheValues(cacheValues); + } + + private CacheValues GetUmbracoMediaCacheValues(int id) + { + var searchProvider = GetSearchProviderSafe(); + + if (searchProvider != null) + { + try + { + // first check in Examine as this is WAY faster + // + // the filter will create a query like this: + // +(+__NodeId:3113 -__Path:-1,-21,*) +__IndexType:media + // + // note that since the use of the wildcard, it automatically escapes it in Lucene. + + var criteria = searchProvider.CreateCriteria("media"); + var filter = criteria.Id(id.ToInvariantString()).Not().Field(UmbracoExamineIndexer.IndexPathFieldName, "-1,-21,".MultipleCharacterWildcard()); + + var result = searchProvider.Search(filter.Compile()).FirstOrDefault(); + if (result != null) return ConvertFromSearchResult(result); + } + catch (Exception ex) + { + if (ex is FileNotFoundException) + { + //Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco + //See this thread: http://examine.cdodeplex.com/discussions/264341 + //Catch the exception here for the time being, and just fallback to GetMedia + //TODO: Need to fix examine in LB scenarios! + Current.Logger.Error("Could not load data from Examine index for media", ex); + } + else if (ex is AlreadyClosedException) + { + //If the app domain is shutting down and the site is under heavy load the index reader will be closed and it really cannot + //be re-opened since the app domain is shutting down. In this case we have no option but to try to load the data from the db. + Current.Logger.Error("Could not load data from Examine index for media, the app domain is most likely in a shutdown state", ex); + } + else throw; + } + } + + // don't log a warning here, as it can flood the log in case of eg a media picker referencing a media + // that has been deleted, hence is not in the Examine index anymore (for a good reason). try to get + // the media from the service, first + var media = _mediaService.GetById(id); + if (media == null || media.Trashed) return null; // not found, ok + + // so, the media was not found in Examine's index *yet* it exists, which probably indicates that + // the index is corrupted. Or not up-to-date. Log a warning, but only once, and only if seeing the + // error more that a number of times. + + var miss = Interlocked.CompareExchange(ref _examineIndexMiss, 0, 0); // volatile read + if (miss < ExamineIndexMissMax && Interlocked.Increment(ref _examineIndexMiss) == ExamineIndexMissMax) + Current.Logger.Warn(() => $"Failed ({ExamineIndexMissMax} times) to retrieve medias from Examine index and had to load" + + " them from DB. This may indicate that the Examine index is corrupted."); + + return ConvertFromIMedia(media); + } + + private const int ExamineIndexMissMax = 10; + private int _examineIndexMiss; + + internal CacheValues ConvertFromXPathNodeIterator(XPathNodeIterator media, int id) + { + if (media?.Current != null) + { + return media.Current.Name.InvariantEquals("error") + ? null + : ConvertFromXPathNavigator(media.Current); + } + + Current.Logger.Warn(() => + $"Could not retrieve media {id} from Examine index or from legacy library.GetMedia method"); + + return null; + } + + internal CacheValues ConvertFromSearchResult(SearchResult searchResult) + { + // note: fixing fields in 7.x, removed by Shan for 8.0 + + return new CacheValues + { + Values = searchResult.Fields, + FromExamine = true + }; + } + + internal CacheValues ConvertFromXPathNavigator(XPathNavigator xpath, bool forceNav = false) + { + if (xpath == null) throw new ArgumentNullException(nameof(xpath)); + + var values = new Dictionary { { "nodeName", xpath.GetAttribute("nodeName", "") } }; + values["nodeTypeAlias"] = xpath.Name; + + var result = xpath.SelectChildren(XPathNodeType.Element); + //add the attributes e.g. id, parentId etc + if (result.Current != null && result.Current.HasAttributes) + { + if (result.Current.MoveToFirstAttribute()) + { + //checking for duplicate keys because of the 'nodeTypeAlias' might already be added above. + if (values.ContainsKey(result.Current.Name) == false) + { + values[result.Current.Name] = result.Current.Value; + } + while (result.Current.MoveToNextAttribute()) + { + if (values.ContainsKey(result.Current.Name) == false) + { + values[result.Current.Name] = result.Current.Value; + } + } + result.Current.MoveToParent(); + } + } + // because, migration + if (values.ContainsKey("key") == false) + values["key"] = Guid.Empty.ToString(); + //add the user props + while (result.MoveNext()) + { + if (result.Current != null && result.Current.HasAttributes == false) + { + var value = result.Current.Value; + if (string.IsNullOrEmpty(value)) + { + if (result.Current.HasAttributes || result.Current.SelectChildren(XPathNodeType.Element).Count > 0) + { + value = result.Current.OuterXml; + } + } + values[result.Current.Name] = value; + } + } + + return new CacheValues + { + Values = values, + XPath = forceNav ? xpath : null // outside of tests we do NOT want to cache the navigator! + }; + } + + internal CacheValues ConvertFromIMedia(IMedia media) + { + var values = new Dictionary(); + + var creator = _userService.GetProfileById(media.CreatorId); + var creatorName = creator == null ? "" : creator.Name; + + values["id"] = media.Id.ToString(); + values["key"] = media.Key.ToString(); + values["parentID"] = media.ParentId.ToString(); + values["level"] = media.Level.ToString(); + values["creatorID"] = media.CreatorId.ToString(); + values["creatorName"] = creatorName; + values["writerID"] = media.CreatorId.ToString(); + values["writerName"] = creatorName; + values["template"] = "0"; + values["urlName"] = ""; + values["sortOrder"] = media.SortOrder.ToString(); + values["createDate"] = media.CreateDate.ToString("yyyy-MM-dd HH:mm:ss"); + values["updateDate"] = media.UpdateDate.ToString("yyyy-MM-dd HH:mm:ss"); + values["nodeName"] = media.Name; + values["path"] = media.Path; + values["nodeType"] = media.ContentType.Id.ToString(); + values["nodeTypeAlias"] = media.ContentType.Alias; + + // add the user props + foreach (var prop in media.Properties) + values[prop.Alias] = prop.GetValue()?.ToString(); + + return new CacheValues + { + Values = values + }; + } + + /// + /// We will need to first check if the document was loaded by Examine, if so we'll need to check if this property exists + /// in the results, if it does not, then we'll have to revert to looking up in the db. + /// + /// + /// + /// + private IPublishedProperty GetProperty(DictionaryPublishedContent dd, string alias) + { + //lets check if the alias does not exist on the document. + //NOTE: Examine will not index empty values and we do not output empty XML Elements to the cache - either of these situations + // would mean that the property is missing from the collection whether we are getting the value from Examine or from the library media cache. + if (dd.Properties.All(x => x.Alias.InvariantEquals(alias) == false)) + { + return null; + } + + if (dd.LoadedFromExamine) + { + //We are going to check for a special field however, that is because in some cases we store a 'Raw' + //value in the index such as for xml/html. + var rawValue = dd.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(UmbracoExamineIndexer.RawFieldPrefix + alias)); + return rawValue + ?? dd.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(alias)); + } + + //if its not loaded from examine, then just return the property + return dd.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(alias)); + } + + /// + /// A Helper methods to return the children for media whther it is based on examine or xml + /// + /// + /// + /// + private IEnumerable GetChildrenMedia(int parentId, XPathNavigator xpath = null) + { + //if there is no navigator, try examine first, then re-look it up + if (xpath != null) + { + return ToIPublishedContent(parentId, xpath); + } + + var searchProvider = GetSearchProviderSafe(); + + if (searchProvider != null) + { + try + { + //first check in Examine as this is WAY faster + var criteria = searchProvider.CreateCriteria("media"); + + var filter = criteria.ParentId(parentId).Not().Field(UmbracoExamineIndexer.IndexPathFieldName, "-1,-21,".MultipleCharacterWildcard()); + //the above filter will create a query like this, NOTE: That since the use of the wildcard, it automatically escapes it in Lucene. + //+(+parentId:3113 -__Path:-1,-21,*) +__IndexType:media + + // sort with the Sort field (updated for 8.0) + var results = searchProvider.Search( + filter.And().OrderBy(new SortableField("sortOrder", SortType.Int)).Compile()); + + if (results.Any()) + { + // var medias = results.Select(ConvertFromSearchResult); + var medias = results.Select(x => + { + int nid; + if (int.TryParse(x["__NodeId"], out nid) == false && int.TryParse(x["NodeId"], out nid) == false) + throw new Exception("Failed to extract NodeId from search result."); + var cacheValues = GetCacheValues(nid, id => ConvertFromSearchResult(x)); + return CreateFromCacheValues(cacheValues); + }); + + return medias; + } + + //if there's no result then return null. Previously we defaulted back to library.GetMedia below + //but this will always get called for when we are getting descendents since many items won't have + //children and then we are hitting the database again! + //So instead we're going to rely on Examine to have the correct results like it should. + return Enumerable.Empty(); + } + catch (FileNotFoundException) + { + //Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco + //See this thread: http://examine.cdodeplex.com/discussions/264341 + //Catch the exception here for the time being, and just fallback to GetMedia + } + } + + //falling back to get media + var media = Current.Services.MediaService.GetById(parentId); + if (media == null) + { + return Enumerable.Empty(); + } + + var serialized = EntityXmlSerializer.Serialize( + Current.Services.MediaService, + Current.Services.DataTypeService, + Current.Services.UserService, + Current.Services.LocalizationService, + Current.UrlSegmentProviders, + media, + true); + + var mediaIterator = serialized.CreateNavigator().Select("/"); + + if (mediaIterator.Current == null) + { + return Enumerable.Empty(); + } + xpath = mediaIterator.Current; + + return ToIPublishedContent(parentId, xpath); + } + + + internal IEnumerable ToIPublishedContent(int parentId, XPathNavigator xpath) + { + var mediaList = new List(); + + // this is so bad, really + var item = xpath.Select("//*[@id='" + parentId + "']"); + if (item.Current == null) + return Enumerable.Empty(); + var items = item.Current.SelectChildren(XPathNodeType.Element); + + // and this does not work, because... meh + //var q = "//* [@id='" + parentId + "']/* [@id]"; + //var items = xpath.Select(q); + + foreach (XPathNavigator itemm in items) + { + int id; + if (int.TryParse(itemm.GetAttribute("id", ""), out id) == false) + continue; // wtf? + var captured = itemm; + var cacheValues = GetCacheValues(id, idd => ConvertFromXPathNavigator(captured)); + mediaList.Add(CreateFromCacheValues(cacheValues)); + } + + return mediaList; + } + + + internal XPathNodeIterator GetMediaXPathNodeIterator(int mediaId, bool deep) + { + var media = Current.Services.MediaService.GetById(mediaId); + if (media == null) return null; + + var serialized = EntityXmlSerializer.Serialize( + Current.Services.MediaService, + Current.Services.DataTypeService, + Current.Services.UserService, + Current.Services.LocalizationService, + Current.UrlSegmentProviders, + media, + deep); + + return serialized.CreateNavigator().Select("/"); + } + + + + internal void Resync() + { + // clear recursive properties cached by XmlPublishedContent.GetProperty + // assume that nothing else is going to cache IPublishedProperty items (else would need to do ByKeySearch) + // NOTE all properties cleared when clearing the content cache (see content cache) + //_cacheProvider.ClearCacheObjectTypes(); + //_cacheProvider.ClearCacheByKeySearch("XmlPublishedCache.PublishedMediaCache:RecursiveProperty-"); + } + + #region Content types + + public override PublishedContentType GetContentType(int id) + { + return _contentTypeCache.Get(PublishedItemType.Media, id); + } + + public override PublishedContentType GetContentType(string alias) + { + return _contentTypeCache.Get(PublishedItemType.Media, alias); + } + + public override IEnumerable GetByContentType(PublishedContentType contentType) + { + throw new NotImplementedException(); + } + + #endregion + + // REFACTORING + + // caching the basic atomic values - and the parent id + // but NOT caching actual parent nor children and NOT even + // the list of children ids - BUT caching the path + + internal class CacheValues + { + public IReadOnlyDictionary Values { get; set; } + public XPathNavigator XPath { get; set; } + public bool FromExamine { get; set; } + } + + public const string PublishedMediaCacheKey = "MediaCacheMeh."; + private const int PublishedMediaCacheTimespanSeconds = 4 * 60; // 4 mins + private static TimeSpan _publishedMediaCacheTimespan; + private static bool _publishedMediaCacheEnabled; + + private static void InitializeCacheConfig() + { + var value = ConfigurationManager.AppSettings["Umbraco.PublishedMediaCache.Seconds"]; + int seconds; + if (int.TryParse(value, out seconds) == false) + seconds = PublishedMediaCacheTimespanSeconds; + if (seconds > 0) + { + _publishedMediaCacheEnabled = true; + _publishedMediaCacheTimespan = TimeSpan.FromSeconds(seconds); + } + else + { + _publishedMediaCacheEnabled = false; + } + } + + internal IPublishedContent CreateFromCacheValues(CacheValues cacheValues) + { + var content = new DictionaryPublishedContent( + cacheValues.Values, + parentId => parentId < 0 ? null : GetUmbracoMedia(parentId), + GetChildrenMedia, + GetProperty, + _cacheProvider, + _contentTypeCache, + cacheValues.XPath, // though, outside of tests, that should be null + cacheValues.FromExamine + ); + return content.CreateModel(); + } + + private static CacheValues GetCacheValues(int id, Func func) + { + if (_publishedMediaCacheEnabled == false) + return func(id); + + var cache = Current.ApplicationCache.RuntimeCache; + var key = PublishedMediaCacheKey + id; + return (CacheValues)cache.GetCacheItem(key, () => func(id), _publishedMediaCacheTimespan); + } + + internal static void ClearCache(int id) + { + var cache = Current.ApplicationCache.RuntimeCache; + var sid = id.ToString(); + var key = PublishedMediaCacheKey + sid; + + // we do clear a lot of things... but the cache refresher is somewhat + // convoluted and it's hard to tell what to clear exactly ;-( + + // clear the parent - NOT (why?) + //var exist = (CacheValues) cache.GetCacheItem(key); + //if (exist != null) + // cache.ClearCacheItem(PublishedMediaCacheKey + GetValuesValue(exist.Values, "parentID")); + + // clear the item + cache.ClearCacheItem(key); + + // clear all children - in case we moved and their path has changed + var fid = "/" + sid + "/"; + cache.ClearCacheObjectTypes((k, v) => + GetValuesValue(v.Values, "path", "__Path").Contains(fid)); + } + + private static string GetValuesValue(IReadOnlyDictionary d, params string[] keys) + { + string value = null; + var ignored = keys.Any(x => d.TryGetValue(x, out value)); + return value ?? ""; + } + } +} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index f9708e5c04..5e59dcc996 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -1,1585 +1,1582 @@ - - - - - v4.7.2 - false - false - {651E1350-91B6-44B7-BD60-7207006D7003} - Library - Umbraco.Web - Umbraco.Web - ..\ - true - Off - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - latest - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - bin\Release\Umbraco.Web.xml - false - latest - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {31785bc3-256c-4613-b2f5-a1b0bdded8c1} - Umbraco.Core - - - Umbraco.Examine - {07FBC26B-2927-4A22-8D96-D644C667FECC} - - - - - - - Properties\SolutionInfo.cs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ASPXCodeBehind - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ASPXCodeBehind - - - - - True - True - Reference.map - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - True - True - Resources.resx - - - - - - - - - - - - - ASPXCodeBehind - - - ASPXCodeBehind - - - ASPXCodeBehind - - - ASPXCodeBehind - - - AssignDomain2.aspx - ASPXCodeBehind - - - AssignDomain2.aspx - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ASPXCodeBehind - - - - - - - - - - - - - - - - - - - - - True - True - Strings.resx - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ASPXCodeBehind - - - ASPXCodeBehind - - - - ASPXCodeBehind - - - - - ASPXCodeBehind - - - - ASPXCodeBehind - - - ASPXCodeBehind - - - ASPXCodeBehind - - - ASPXCodeBehind - - - ASPXCodeBehind - - - ASPXCodeBehind - - - ASPXCodeBehind - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Code - - - Code - - - Code - - - Code - - - True - True - Settings.settings - - - Code - - - - - ASPXCodeBehind - - - Component - - - - - Code - - - - - - - FeedProxy.aspx - ASPXCodeBehind - - - FeedProxy.aspx - - - EditRelationType.aspx - - - EditRelationType.aspx - - - NewRelationType.aspx - ASPXCodeBehind - - - NewRelationType.aspx - - - RelationTypesWebService.asmx - Component - - - - - Preview.aspx - ASPXCodeBehind - - - Preview.aspx - - - insertMasterpageContent.aspx - ASPXCodeBehind - - - insertMasterpageContent.aspx - - - insertMasterpagePlaceholder.aspx - ASPXCodeBehind - - - insertMasterpagePlaceholder.aspx - - - republish.aspx - - - republish.aspx - - - SendPublish.aspx - ASPXCodeBehind - - - SendPublish.aspx - - - Code - - - editPackage.aspx - ASPXCodeBehind - - - editPackage.aspx - - - exportDocumenttype.aspx - ASPXCodeBehind - - - importDocumenttype.aspx - ASPXCodeBehind - - - notifications.aspx - ASPXCodeBehind - - - notifications.aspx - - - rollBack.aspx - - - rollBack.aspx - - - sendToTranslation.aspx - ASPXCodeBehind - - - sendToTranslation.aspx - - - viewAuditTrail.aspx - ASPXCodeBehind - - - viewAuditTrail.aspx - - - DictionaryItemList.aspx - ASPXCodeBehind - - - DictionaryItemList.aspx - - - EditDictionaryItem.aspx - ASPXCodeBehind - - - EditDictionaryItem.aspx - - - Code - - - - - - - - - - - default.aspx - ASPXCodeBehind - - - default.aspx - - - details.aspx - ASPXCodeBehind - - - details.aspx - - - preview.aspx - ASPXCodeBehind - - - preview.aspx - - - xml.aspx - ASPXCodeBehind - - - xml.aspx - - - - - - - - - - - - CheckForUpgrade.asmx - Component - - - legacyAjaxCalls.asmx - Component - - - nodeSorter.asmx - Component - - - ASPXCodeBehind - - - - - - - True - True - Reference.map - - - - - - - - - - - Component - - - - Component - - - - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - ResXFileCodeGenerator - Strings.Designer.cs - - - - - - - - - - - - ASPXCodeBehind - - - ASPXCodeBehind - - - - - ASPXCodeBehind - - - - - - - - ASPXCodeBehind - - - ASPXCodeBehind - - - - - ASPXCodeBehind - - - - - ASPXCodeBehind - - - ASPXCodeBehind - - - ASPXCodeBehind - - - - - ASPXCodeBehind - - - - - ASPXCodeBehind - - - ASPXCodeBehind - - - - ASPXCodeBehind - - - - - - - MSDiscoCodeGenerator - Reference.cs - Designer - - - Mvc\web.config - - - MSDiscoCodeGenerator - Reference.cs - - - - - Reference.map - - - Reference.map - - - SettingsSingleFileGenerator - Settings1.Designer.cs - - - - - Dynamic - Web References\org.umbraco.our\ - http://our.umbraco.org/umbraco/webservices/api/repository.asmx - - - - - Settings - umbraco_org_umbraco_our_Repository - - - Dynamic - Web References\org.umbraco.update\ - http://update.umbraco.org/checkforupgrade.asmx - - - - - Settings - umbraco_org_umbraco_update_CheckForUpgrade - - - - - - - $(PlatformTargetAsMSBuildArchitecture) - - - - - - - - - - - + + + + + v4.7.2 + false + false + {651E1350-91B6-44B7-BD60-7207006D7003} + Library + Umbraco.Web + Umbraco.Web + ..\ + true + Off + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + latest + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + bin\Release\Umbraco.Web.xml + false + latest + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {31785bc3-256c-4613-b2f5-a1b0bdded8c1} + Umbraco.Core + + + Umbraco.Examine + {07FBC26B-2927-4A22-8D96-D644C667FECC} + + + + + + + Properties\SolutionInfo.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ASPXCodeBehind + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ASPXCodeBehind + + + + + True + True + Reference.map + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + Resources.resx + + + + + + + + + + + + + ASPXCodeBehind + + + ASPXCodeBehind + + + ASPXCodeBehind + + + ASPXCodeBehind + + + AssignDomain2.aspx + ASPXCodeBehind + + + AssignDomain2.aspx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ASPXCodeBehind + + + + + + + + + + + + + + + + + + + + + True + True + Strings.resx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ASPXCodeBehind + + + ASPXCodeBehind + + + + ASPXCodeBehind + + + + + ASPXCodeBehind + + + + ASPXCodeBehind + + + ASPXCodeBehind + + + ASPXCodeBehind + + + ASPXCodeBehind + + + ASPXCodeBehind + + + ASPXCodeBehind + + + ASPXCodeBehind + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Code + + + Code + + + Code + + + True + True + Settings.settings + + + Code + + + + + ASPXCodeBehind + + + Component + + + + + Code + + + + + + + FeedProxy.aspx + ASPXCodeBehind + + + FeedProxy.aspx + + + EditRelationType.aspx + + + EditRelationType.aspx + + + NewRelationType.aspx + ASPXCodeBehind + + + NewRelationType.aspx + + + RelationTypesWebService.asmx + Component + + + + + Preview.aspx + ASPXCodeBehind + + + Preview.aspx + + + insertMasterpageContent.aspx + ASPXCodeBehind + + + insertMasterpageContent.aspx + + + insertMasterpagePlaceholder.aspx + ASPXCodeBehind + + + insertMasterpagePlaceholder.aspx + + + republish.aspx + + + republish.aspx + + + SendPublish.aspx + ASPXCodeBehind + + + SendPublish.aspx + + + Code + + + editPackage.aspx + ASPXCodeBehind + + + editPackage.aspx + + + exportDocumenttype.aspx + ASPXCodeBehind + + + importDocumenttype.aspx + ASPXCodeBehind + + + notifications.aspx + ASPXCodeBehind + + + notifications.aspx + + + rollBack.aspx + + + rollBack.aspx + + + sendToTranslation.aspx + ASPXCodeBehind + + + sendToTranslation.aspx + + + viewAuditTrail.aspx + ASPXCodeBehind + + + viewAuditTrail.aspx + + + DictionaryItemList.aspx + ASPXCodeBehind + + + DictionaryItemList.aspx + + + EditDictionaryItem.aspx + ASPXCodeBehind + + + EditDictionaryItem.aspx + + + Code + + + + + + + + + + + default.aspx + ASPXCodeBehind + + + default.aspx + + + details.aspx + ASPXCodeBehind + + + details.aspx + + + preview.aspx + ASPXCodeBehind + + + preview.aspx + + + xml.aspx + ASPXCodeBehind + + + xml.aspx + + + + + + + + + + + + CheckForUpgrade.asmx + Component + + + legacyAjaxCalls.asmx + Component + + + nodeSorter.asmx + Component + + + ASPXCodeBehind + + + + + + + True + True + Reference.map + + + + + + + + + + + Component + + + + Component + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + ResXFileCodeGenerator + Strings.Designer.cs + + + + + + + + + + + + ASPXCodeBehind + + + ASPXCodeBehind + + + + + ASPXCodeBehind + + + + + + + + ASPXCodeBehind + + + ASPXCodeBehind + + + + + ASPXCodeBehind + + + + + ASPXCodeBehind + + + ASPXCodeBehind + + + ASPXCodeBehind + + + + + ASPXCodeBehind + + + + + ASPXCodeBehind + + + ASPXCodeBehind + + + + ASPXCodeBehind + + + + + + + MSDiscoCodeGenerator + Reference.cs + Designer + + + Mvc\web.config + + + MSDiscoCodeGenerator + Reference.cs + + + + + Reference.map + + + Reference.map + + + SettingsSingleFileGenerator + Settings1.Designer.cs + + + + + Dynamic + Web References\org.umbraco.our\ + http://our.umbraco.org/umbraco/webservices/api/repository.asmx + + + + + Settings + umbraco_org_umbraco_our_Repository + + + Dynamic + Web References\org.umbraco.update\ + http://update.umbraco.org/checkforupgrade.asmx + + + + + Settings + umbraco_org_umbraco_update_CheckForUpgrade + + + + + + + $(PlatformTargetAsMSBuildArchitecture) + + + + + + + + + + + \ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/library.cs b/src/Umbraco.Web/umbraco.presentation/library.cs deleted file mode 100644 index d76b4066f6..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/library.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System; -using System.Xml.Linq; -using System.Xml.XPath; -using Umbraco.Core.Cache; -using Umbraco.Core.Configuration; -using Umbraco.Core.Logging; -using Umbraco.Core.Services; -using Umbraco.Web.Composing; - -namespace umbraco -{ - /// - /// Function library for umbraco. Includes various helper-methods and methods to - /// save and load data from umbraco. - /// - /// Especially usefull in XSLT where any of these methods can be accesed using the umbraco.library name-space. Example: - /// <xsl:value-of select="umbraco.library:NiceUrl(@id)"/> - /// - [Obsolete("v8.kill.kill")] - public class library - { - /// - /// Get a media object as an xml object - /// - /// The identifier of the media object to be returned - /// If true, children of the media object is returned - /// An umbraco xml node of the media (same format as a document node) - public static XPathNodeIterator GetMedia(int MediaId, bool deep) - { - try - { - if (UmbracoConfig.For.UmbracoSettings().Content.UmbracoLibraryCacheDuration > 0) - { - var xml = Current.ApplicationCache.RuntimeCache.GetCacheItem( - $"{CacheKeys.MediaCacheKey}_{MediaId}_{deep}", - timeout: TimeSpan.FromSeconds(UmbracoConfig.For.UmbracoSettings().Content.UmbracoLibraryCacheDuration), - getCacheItem: () => GetMediaDo(MediaId, deep).Item1); - - if (xml != null) - { - //returning the root element of the Media item fixes the problem - return xml.CreateNavigator().Select("/"); - } - - } - else - { - var xml = GetMediaDo(MediaId, deep).Item1; - - //returning the root element of the Media item fixes the problem - return xml.CreateNavigator().Select("/"); - } - } - catch(Exception ex) - { - Current.Logger.Error("An error occurred looking up media", ex); - } - - Current.Logger.Debug(() => $"No media result for id {MediaId}"); - - var errorXml = new XElement("error", string.Format("No media is maching '{0}'", MediaId)); - return errorXml.CreateNavigator().Select("/"); - } - - - private static Tuple GetMediaDo(int mediaId, bool deep) - { - var media = Current.Services.MediaService.GetById(mediaId); - if (media == null) return null; - - var serialized = EntityXmlSerializer.Serialize( - Current.Services.MediaService, - Current.Services.DataTypeService, - Current.Services.UserService, - Current.Services.LocalizationService, - Current.UrlSegmentProviders, - media, - deep); - return Tuple.Create(serialized, media.Path); - } - } -}