diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 6caeadd0e5..9dc6f9457f 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -16,7 +16,7 @@ This document gives you a quick overview on how to get started, we will link to ## Guidelines for contributions we welcome -Not all changes are wanted, so on occassion we might close a PR without merging it. We will give you feedback why we can't accept your changes and we'll be nice about it, thanking you for spending your valueable time. +Not all changes are wanted, so on occassion we might close a PR without merging it. We will give you feedback why we can't accept your changes and we'll be nice about it, thanking you for spending your valuable time. We have [documented what we consider small and large changes](CONTRIBUTION_GUIDELINES.md). Make sure to talk to us before making large changes. diff --git a/build/NuSpecs/UmbracoCms.Web.nuspec b/build/NuSpecs/UmbracoCms.Web.nuspec index 51d7e3b8d0..35e79d8127 100644 --- a/build/NuSpecs/UmbracoCms.Web.nuspec +++ b/build/NuSpecs/UmbracoCms.Web.nuspec @@ -25,7 +25,7 @@ - + diff --git a/build/NuSpecs/UmbracoCms.nuspec b/build/NuSpecs/UmbracoCms.nuspec index 434fe812ef..565d693979 100644 --- a/build/NuSpecs/UmbracoCms.nuspec +++ b/build/NuSpecs/UmbracoCms.nuspec @@ -22,11 +22,11 @@ not want this to happen as the alpha of the next major is, really, the next major already. --> - + - - + + diff --git a/build/build.ps1 b/build/build.ps1 index ec53199251..dafae2665a 100644 --- a/build/build.ps1 +++ b/build/build.ps1 @@ -452,9 +452,22 @@ if ($this.OnError()) { return } $this.PrepareAzureGallery() if ($this.OnError()) { return } + $this.PostPackageHook() + if ($this.OnError()) { return } Write-Host "Done" }) + $ubuild.DefineMethod("PostPackageHook", + { + # run hook + if ($this.HasMethod("PostPackage")) + { + Write-Host "Run PostPackage hook" + $this.PostPackage(); + if (-not $?) { throw "Failed to run hook." } + } + }) + # ################################################################ # RUN # ################################################################ diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index b5af335791..ce40bd9baa 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -19,4 +19,4 @@ using System.Resources; // these are FYI and changed automatically [assembly: AssemblyFileVersion("8.0.0")] -[assembly: AssemblyInformationalVersion("8.0.0-alpha.52")] +[assembly: AssemblyInformationalVersion("8.0.0-alpha.58")] diff --git a/src/Umbraco.Core/Cache/CacheHelper.cs b/src/Umbraco.Core/Cache/CacheHelper.cs index f99b1e847b..cd2225ae9d 100644 --- a/src/Umbraco.Core/Cache/CacheHelper.cs +++ b/src/Umbraco.Core/Cache/CacheHelper.cs @@ -4,27 +4,10 @@ using System.Web; namespace Umbraco.Core.Cache { /// - /// Class that is exposed by the ApplicationContext for application wide caching purposes + /// Represents the application-wide caches. /// public class CacheHelper { - public static CacheHelper NoCache { get; } = new CacheHelper(NullCacheProvider.Instance, NullCacheProvider.Instance, NullCacheProvider.Instance, new IsolatedRuntimeCache(_ => NullCacheProvider.Instance)); - - /// - /// Creates a cache helper with disabled caches - /// - /// - /// - /// Good for unit testing - /// - public static CacheHelper CreateDisabledCacheHelper() - { - // do *not* return NoCache - // NoCache is a special instance that is detected by RepositoryBase and disables all cache policies - // CreateDisabledCacheHelper is used in tests to use no cache, *but* keep all cache policies - return new CacheHelper(NullCacheProvider.Instance, NullCacheProvider.Instance, NullCacheProvider.Instance, new IsolatedRuntimeCache(_ => NullCacheProvider.Instance)); - } - /// /// Initializes a new instance for use in the web /// @@ -40,7 +23,6 @@ namespace Umbraco.Core.Cache /// /// Initializes a new instance for use in the web /// - /// public CacheHelper(System.Web.Caching.Cache cache) : this( new HttpRuntimeCacheProvider(cache), @@ -50,30 +32,39 @@ namespace Umbraco.Core.Cache { } - /// /// Initializes a new instance based on the provided providers /// - /// - /// - /// - /// public CacheHelper( IRuntimeCacheProvider httpCacheProvider, ICacheProvider staticCacheProvider, ICacheProvider requestCacheProvider, IsolatedRuntimeCache isolatedCacheManager) { - if (httpCacheProvider == null) throw new ArgumentNullException("httpCacheProvider"); - if (staticCacheProvider == null) throw new ArgumentNullException("staticCacheProvider"); - if (requestCacheProvider == null) throw new ArgumentNullException("requestCacheProvider"); - if (isolatedCacheManager == null) throw new ArgumentNullException("isolatedCacheManager"); - RuntimeCache = httpCacheProvider; - StaticCache = staticCacheProvider; - RequestCache = requestCacheProvider; - IsolatedRuntimeCache = isolatedCacheManager; + RuntimeCache = httpCacheProvider ?? throw new ArgumentNullException(nameof(httpCacheProvider)); + StaticCache = staticCacheProvider ?? throw new ArgumentNullException(nameof(staticCacheProvider)); + RequestCache = requestCacheProvider ?? throw new ArgumentNullException(nameof(requestCacheProvider)); + IsolatedRuntimeCache = isolatedCacheManager ?? throw new ArgumentNullException(nameof(isolatedCacheManager)); } + /// + /// Gets the special disabled instance. + /// + /// + /// When used by repositories, all cache policies apply, but the underlying caches do not cache anything. + /// Used by tests. + /// + public static CacheHelper Disabled { get; } = new CacheHelper(NullCacheProvider.Instance, NullCacheProvider.Instance, NullCacheProvider.Instance, new IsolatedRuntimeCache(_ => NullCacheProvider.Instance)); + + /// + /// Gets the special no-cache instance. + /// + /// + /// When used by repositories, all cache policies are bypassed. + /// Used by repositories that do no cache. + /// + public static CacheHelper NoCache { get; } = new CacheHelper(NullCacheProvider.Instance, NullCacheProvider.Instance, NullCacheProvider.Instance, new IsolatedRuntimeCache(_ => NullCacheProvider.Instance)); + /// /// Returns the current Request cache /// diff --git a/src/Umbraco.Core/Cache/CacheRefresherCollectionBuilder.cs b/src/Umbraco.Core/Cache/CacheRefresherCollectionBuilder.cs index 11ac05844b..8bae755149 100644 --- a/src/Umbraco.Core/Cache/CacheRefresherCollectionBuilder.cs +++ b/src/Umbraco.Core/Cache/CacheRefresherCollectionBuilder.cs @@ -1,15 +1,9 @@ -using System.Collections.Generic; -using LightInject; -using Umbraco.Core.Composing; +using Umbraco.Core.Composing; namespace Umbraco.Core.Cache { public class CacheRefresherCollectionBuilder : LazyCollectionBuilderBase { - public CacheRefresherCollectionBuilder(IServiceContainer container) - : base(container) - { } - protected override CacheRefresherCollectionBuilder This => this; } } diff --git a/src/Umbraco.Core/Components/AuditEventsComponent.cs b/src/Umbraco.Core/Components/AuditEventsComponent.cs index 134aa18414..08d4702afa 100644 --- a/src/Umbraco.Core/Components/AuditEventsComponent.cs +++ b/src/Umbraco.Core/Components/AuditEventsComponent.cs @@ -12,11 +12,36 @@ using Umbraco.Core.Services.Implement; namespace Umbraco.Core.Components { - public sealed class AuditEventsComponent : UmbracoComponentBase, IUmbracoCoreComponent + public sealed class AuditEventsComponent : IComponent { - private IAuditService _auditService; - private IUserService _userService; - private IEntityService _entityService; + private readonly IAuditService _auditService; + private readonly IUserService _userService; + private readonly IEntityService _entityService; + + public AuditEventsComponent(IAuditService auditService, IUserService userService, IEntityService entityService) + { + _auditService = auditService; + _userService = userService; + _entityService = entityService; + } + + public void Initialize() + { + UserService.SavedUserGroup += OnSavedUserGroupWithUsers; + + UserService.SavedUser += OnSavedUser; + UserService.DeletedUser += OnDeletedUser; + UserService.UserGroupPermissionsAssigned += UserGroupPermissionAssigned; + + MemberService.Saved += OnSavedMember; + MemberService.Deleted += OnDeletedMember; + MemberService.AssignedRoles += OnAssignedRoles; + MemberService.RemovedRoles += OnRemovedRoles; + MemberService.Exported += OnMemberExported; + } + + public void Terminate() + { } private IUser CurrentPerformingUser { @@ -46,25 +71,6 @@ namespace Umbraco.Core.Components } } - public void Initialize(IAuditService auditService, IUserService userService, IEntityService entityService) - { - _auditService = auditService; - _userService = userService; - _entityService = entityService; - - UserService.SavedUserGroup += OnSavedUserGroupWithUsers; - - UserService.SavedUser += OnSavedUser; - UserService.DeletedUser += OnDeletedUser; - UserService.UserGroupPermissionsAssigned += UserGroupPermissionAssigned; - - MemberService.Saved += OnSavedMember; - MemberService.Deleted += OnDeletedMember; - MemberService.AssignedRoles += OnAssignedRoles; - MemberService.RemovedRoles += OnRemovedRoles; - MemberService.Exported += OnMemberExported; - } - private string FormatEmail(IMember member) { return member == null ? string.Empty : member.Email.IsNullOrWhiteSpace() ? "" : $"<{member.Email}>"; diff --git a/src/Umbraco.Core/Components/AuditEventsComposer.cs b/src/Umbraco.Core/Components/AuditEventsComposer.cs new file mode 100644 index 0000000000..692cb6c6dd --- /dev/null +++ b/src/Umbraco.Core/Components/AuditEventsComposer.cs @@ -0,0 +1,5 @@ +namespace Umbraco.Core.Components +{ + public sealed class AuditEventsComposer : ComponentComposer, ICoreComposer + { } +} diff --git a/src/Umbraco.Core/Components/BootLoader.cs b/src/Umbraco.Core/Components/BootLoader.cs deleted file mode 100644 index fd292990c8..0000000000 --- a/src/Umbraco.Core/Components/BootLoader.cs +++ /dev/null @@ -1,368 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -using LightInject; -using Umbraco.Core.Collections; -using Umbraco.Core.Exceptions; -using Umbraco.Core.Logging; -using Umbraco.Core.Scoping; - -namespace Umbraco.Core.Components -{ - // note: this class is NOT thread-safe in any ways - - internal class BootLoader - { - private readonly IServiceContainer _container; - private readonly ProfilingLogger _proflog; - private readonly ILogger _logger; - private IUmbracoComponent[] _components; - private bool _booted; - - private const int LogThresholdMilliseconds = 100; - - /// - /// Initializes a new instance of the class. - /// - /// The application container. - public BootLoader(IServiceContainer container) - { - _container = container ?? throw new ArgumentNullException(nameof(container)); - _proflog = container.GetInstance(); - _logger = container.GetInstance(); - } - - private class EnableInfo - { - public bool Enabled; - public int Weight = -1; - } - - public void Boot(IEnumerable componentTypes, RuntimeLevel level) - { - if (_booted) throw new InvalidOperationException("Can not boot, has already booted."); - - var orderedComponentTypes = PrepareComponentTypes(componentTypes, level); - - InstanciateComponents(orderedComponentTypes); - ComposeComponents(level); - - using (var scope = _container.GetInstance().CreateScope()) - { - InitializeComponents(); - scope.Complete(); - } - - // rejoice! - _booted = true; - } - - private IEnumerable PrepareComponentTypes(IEnumerable componentTypes, RuntimeLevel level) - { - using (_proflog.DebugDuration("Preparing component types.", "Prepared component types.")) - { - return PrepareComponentTypes2(componentTypes, level); - } - } - - private IEnumerable PrepareComponentTypes2(IEnumerable componentTypes, RuntimeLevel level) - { - // create a list, remove those that cannot be enabled due to runtime level - var componentTypeList = componentTypes - .Where(x => - { - // use the min level specified by the attribute if any - // otherwise, user components have Run min level, anything else is Unknown (always run) - var attr = x.GetCustomAttribute(); - var minLevel = attr?.MinLevel ?? (x.Implements() ? RuntimeLevel.Run : RuntimeLevel.Unknown); - return level >= minLevel; - }) - .ToList(); - - // cannot remove that one - ever - if (componentTypeList.Contains(typeof(UmbracoCoreComponent)) == false) - componentTypeList.Add(typeof(UmbracoCoreComponent)); - - // enable or disable components - EnableDisableComponents(componentTypeList); - - // sort the components according to their dependencies - var requirements = new Dictionary>(); - foreach (var type in componentTypeList) requirements[type] = null; - foreach (var type in componentTypeList) - { - GatherRequirementsFromRequireAttribute(type, componentTypeList, requirements); - GatherRequirementsFromRequiredAttribute(type, componentTypeList, requirements); - } - - // only for debugging, this is verbose - //_logger.Debug(GetComponentsReport(requirements)); - - // sort components - var graph = new TopoGraph>>(kvp => kvp.Key, kvp => kvp.Value); - graph.AddItems(requirements); - List sortedComponentTypes; - try - { - sortedComponentTypes = graph.GetSortedItems().Select(x => x.Key).ToList(); - } - catch (Exception e) - { - // in case of an error, force-dump everything to log - _logger.Info("Component Report:\r\n{ComponentReport}", GetComponentsReport(requirements)); - _logger.Error(e, "Failed to sort compontents."); - throw; - } - - // bit verbose but should help for troubleshooting - var text = "Ordered Components: " + Environment.NewLine + string.Join(Environment.NewLine, sortedComponentTypes) + Environment.NewLine; - Console.WriteLine(text); - _logger.Debug("Ordered Components: {SortedComponentTypes}", sortedComponentTypes); - - return sortedComponentTypes; - } - - private static string GetComponentsReport(Dictionary> requirements) - { - var text = new StringBuilder(); - text.AppendLine("Components & Dependencies:"); - text.AppendLine(); - - foreach (var kvp in requirements) - { - var type = kvp.Key; - - text.AppendLine(type.FullName); - foreach (var attribute in type.GetCustomAttributes()) - text.AppendLine(" -> " + attribute.RequiredType + (attribute.Weak.HasValue - ? (attribute.Weak.Value ? " (weak)" : (" (strong" + (requirements.ContainsKey(attribute.RequiredType) ? ", missing" : "") + ")")) - : "")); - foreach (var attribute in type.GetCustomAttributes()) - text.AppendLine(" -< " + attribute.RequiringType); - foreach (var i in type.GetInterfaces()) - { - text.AppendLine(" : " + i.FullName); - foreach (var attribute in i.GetCustomAttributes()) - text.AppendLine(" -> " + attribute.RequiredType + (attribute.Weak.HasValue - ? (attribute.Weak.Value ? " (weak)" : (" (strong" + (requirements.ContainsKey(attribute.RequiredType) ? ", missing" : "") + ")")) - : "")); - foreach (var attribute in i.GetCustomAttributes()) - text.AppendLine(" -< " + attribute.RequiringType); - } - if (kvp.Value != null) - foreach (var t in kvp.Value) - text.AppendLine(" = " + t); - text.AppendLine(); - } - text.AppendLine("/"); - text.AppendLine(); - return text.ToString(); - } - - private static void EnableDisableComponents(ICollection types) - { - var enabled = new Dictionary(); - - // process the enable/disable attributes - // these two attributes are *not* inherited and apply to *classes* only (not interfaces). - // remote declarations (when a component enables/disables *another* component) - // have priority over local declarations (when a component disables itself) so that - // ppl can enable components that, by default, are disabled. - // what happens in case of conflicting remote declarations is unspecified. more - // precisely, the last declaration to be processed wins, but the order of the - // declarations depends on the type finder and is unspecified. - foreach (var componentType in types) - { - foreach (var attr in componentType.GetCustomAttributes()) - { - var type = attr.EnabledType ?? componentType; - if (enabled.TryGetValue(type, out var enableInfo) == false) enableInfo = enabled[type] = new EnableInfo(); - var weight = type == componentType ? 1 : 2; - if (enableInfo.Weight > weight) continue; - - enableInfo.Enabled = true; - enableInfo.Weight = weight; - } - foreach (var attr in componentType.GetCustomAttributes()) - { - var type = attr.DisabledType ?? componentType; - if (type == typeof(UmbracoCoreComponent)) throw new InvalidOperationException("Cannot disable UmbracoCoreComponent."); - if (enabled.TryGetValue(type, out var enableInfo) == false) enableInfo = enabled[type] = new EnableInfo(); - var weight = type == componentType ? 1 : 2; - if (enableInfo.Weight > weight) continue; - - enableInfo.Enabled = false; - enableInfo.Weight = weight; - } - } - - // remove components that end up being disabled - foreach (var kvp in enabled.Where(x => x.Value.Enabled == false)) - types.Remove(kvp.Key); - } - - private static void GatherRequirementsFromRequireAttribute(Type type, ICollection types, IDictionary> requirements) - { - // get 'require' attributes - // these attributes are *not* inherited because we want to "custom-inherit" for interfaces only - var requireAttributes = type - .GetInterfaces().SelectMany(x => x.GetCustomAttributes()) // those marking interfaces - .Concat(type.GetCustomAttributes()); // those marking the component - - // what happens in case of conflicting attributes (different strong/weak for same type) is not specified. - foreach (var attr in requireAttributes) - { - if (attr.RequiredType == type) continue; // ignore self-requirements (+ exclude in implems, below) - - // requiring an interface = require any enabled component implementing that interface - // unless strong, and then require at least one enabled component implementing that interface - if (attr.RequiredType.IsInterface) - { - var implems = types.Where(x => x != type && attr.RequiredType.IsAssignableFrom(x)).ToList(); - if (implems.Count > 0) - { - if (requirements[type] == null) requirements[type] = new List(); - requirements[type].AddRange(implems); - } - else if (attr.Weak == false) // if explicitely set to !weak, is strong, else is weak - throw new Exception($"Broken component dependency: {type.FullName} -> {attr.RequiredType.FullName}."); - } - // requiring a class = require that the component is enabled - // unless weak, and then requires it if it is enabled - else - { - if (types.Contains(attr.RequiredType)) - { - if (requirements[type] == null) requirements[type] = new List(); - requirements[type].Add(attr.RequiredType); - } - else if (attr.Weak != true) // if not explicitely set to weak, is strong - throw new Exception($"Broken component dependency: {type.FullName} -> {attr.RequiredType.FullName}."); - } - } - } - - private static void GatherRequirementsFromRequiredAttribute(Type type, ICollection types, IDictionary> requirements) - { - // get 'required' attributes - // fixme explain - var requiredAttributes = type - .GetInterfaces().SelectMany(x => x.GetCustomAttributes()) - .Concat(type.GetCustomAttributes()); - - foreach (var attr in requiredAttributes) - { - if (attr.RequiringType == type) continue; // ignore self-requirements (+ exclude in implems, below) - - if (attr.RequiringType.IsInterface) - { - var implems = types.Where(x => x != type && attr.RequiringType.IsAssignableFrom(x)).ToList(); - foreach (var implem in implems) - { - if (requirements[implem] == null) requirements[implem] = new List(); - requirements[implem].Add(type); - } - } - else - { - if (types.Contains(attr.RequiringType)) - { - if (requirements[attr.RequiringType] == null) requirements[attr.RequiringType] = new List(); - requirements[attr.RequiringType].Add(type); - } - } - } - } - - private void InstanciateComponents(IEnumerable types) - { - using (_proflog.DebugDuration("Instanciating components.", "Instanciated components.")) - { - _components = types.Select(x => (IUmbracoComponent) Activator.CreateInstance(x)).ToArray(); - } - } - - private void ComposeComponents(RuntimeLevel level) - { - using (_proflog.DebugDuration($"Composing components. (log when >{LogThresholdMilliseconds}ms)", "Composed components.")) - { - var composition = new Composition(_container, level); - foreach (var component in _components) - { - var componentType = component.GetType(); - using (_proflog.DebugDuration($"Composing {componentType.FullName}.", $"Composed {componentType.FullName}.", thresholdMilliseconds: LogThresholdMilliseconds)) - { - component.Compose(composition); - } - } - } - } - - private void InitializeComponents() - { - // use a container scope to ensure that PerScope instances are disposed - // components that require instances that should not survive should register them with PerScope lifetime - using (_proflog.DebugDuration($"Initializing components. (log when >{LogThresholdMilliseconds}ms)", "Initialized components.")) - using (_container.BeginScope()) - { - foreach (var component in _components) - { - var componentType = component.GetType(); - var initializers = componentType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) - .Where(x => x.Name == "Initialize" && x.IsGenericMethod == false); - using (_proflog.DebugDuration($"Initializing {componentType.FullName}.", $"Initialised {componentType.FullName}.", thresholdMilliseconds: LogThresholdMilliseconds)) - { - foreach (var initializer in initializers) - { - var parameters = initializer.GetParameters() - .Select(x => GetParameter(componentType, x.ParameterType)) - .ToArray(); - initializer.Invoke(component, parameters); - } - } - } - } - } - - private object GetParameter(Type componentType, Type parameterType) - { - object param; - - try - { - param = _container.TryGetInstance(parameterType); - } - catch (Exception e) - { - throw new BootFailedException($"Could not get parameter of type {parameterType.FullName} for component {componentType.FullName}.", e); - } - - if (param == null) throw new BootFailedException($"Could not get parameter of type {parameterType.FullName} for component {componentType.FullName}."); - return param; - } - - public void Terminate() - { - if (_booted == false) - { - _proflog.Logger.Warn("Cannot terminate, has not booted."); - return; - } - - using (_proflog.DebugDuration($"Terminating. (log components when >{LogThresholdMilliseconds}ms)", "Terminated.")) - { - for (var i = _components.Length - 1; i >= 0; i--) // terminate components in reverse order - { - var component = _components[i]; - var componentType = component.GetType(); - using (_proflog.DebugDuration($"Terminating {componentType.FullName}.", $"Terminated {componentType.FullName}.", thresholdMilliseconds: LogThresholdMilliseconds)) - { - component.Terminate(); - } - } - } - } - } -} diff --git a/src/Umbraco.Core/Components/ComponentCollection.cs b/src/Umbraco.Core/Components/ComponentCollection.cs new file mode 100644 index 0000000000..4fa81b9760 --- /dev/null +++ b/src/Umbraco.Core/Components/ComponentCollection.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core.Composing; +using Umbraco.Core.Logging; + +namespace Umbraco.Core.Components +{ + /// + /// Represents the collection of implementations. + /// + public class ComponentCollection : BuilderCollectionBase + { + private const int LogThresholdMilliseconds = 100; + + private readonly IProfilingLogger _logger; + + public ComponentCollection(IEnumerable items, IProfilingLogger logger) + : base(items) + { + _logger = logger; + } + + public void Initialize() + { + using (_logger.DebugDuration($"Initializing. (log components when >{LogThresholdMilliseconds}ms)", "Initialized.")) + { + foreach (var component in this.Reverse()) // terminate components in reverse order + { + var componentType = component.GetType(); + using (_logger.DebugDuration($"Initializing {componentType.FullName}.", $"Initialized {componentType.FullName}.", thresholdMilliseconds: LogThresholdMilliseconds)) + { + component.Initialize(); + } + } + } + } + + public void Terminate() + { + using (_logger.DebugDuration($"Terminating. (log components when >{LogThresholdMilliseconds}ms)", "Terminated.")) + { + foreach (var component in this.Reverse()) // terminate components in reverse order + { + var componentType = component.GetType(); + using (_logger.DebugDuration($"Terminating {componentType.FullName}.", $"Terminated {componentType.FullName}.", thresholdMilliseconds: LogThresholdMilliseconds)) + { + component.Terminate(); + component.DisposeIfDisposable(); + } + } + } + } + } +} diff --git a/src/Umbraco.Core/Components/ComponentCollectionBuilder.cs b/src/Umbraco.Core/Components/ComponentCollectionBuilder.cs new file mode 100644 index 0000000000..584de7a8f2 --- /dev/null +++ b/src/Umbraco.Core/Components/ComponentCollectionBuilder.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core.Composing; +using Umbraco.Core.Logging; + +namespace Umbraco.Core.Components +{ + /// + /// Builds a . + /// + public class ComponentCollectionBuilder : OrderedCollectionBuilderBase + { + private const int LogThresholdMilliseconds = 100; + + private IProfilingLogger _logger; + + public ComponentCollectionBuilder() + { } + + protected override ComponentCollectionBuilder This => this; + + protected override IEnumerable CreateItems(IFactory factory) + { + _logger = factory.GetInstance(); + + using (_logger.DebugDuration($"Creating components. (log when >{LogThresholdMilliseconds}ms)", "Created.")) + { + return base.CreateItems(factory); + } + } + + protected override IComponent CreateItem(IFactory factory, Type itemType) + { + using (_logger.DebugDuration($"Creating {itemType.FullName}.", $"Created {itemType.FullName}.", thresholdMilliseconds: LogThresholdMilliseconds)) + { + return base.CreateItem(factory, itemType); + } + } + } +} diff --git a/src/Umbraco.Core/Components/ComponentComposer.cs b/src/Umbraco.Core/Components/ComponentComposer.cs new file mode 100644 index 0000000000..792790c42f --- /dev/null +++ b/src/Umbraco.Core/Components/ComponentComposer.cs @@ -0,0 +1,20 @@ +namespace Umbraco.Core.Components +{ + /// + /// Provides a base class for composers which compose a component. + /// + /// The type of the component + public abstract class ComponentComposer : IComposer + where TComponent : IComponent + { + /// + public virtual void Compose(Composition composition) + { + composition.Components().Append(); + } + + // note: thanks to this class, a component that does not compose anything can be + // registered with one line: + // public class MyComponentComposer : ComponentComposer { } + } +} diff --git a/src/Umbraco.Core/Components/ComposeAfterAttribute.cs b/src/Umbraco.Core/Components/ComposeAfterAttribute.cs new file mode 100644 index 0000000000..a8fdfaa92b --- /dev/null +++ b/src/Umbraco.Core/Components/ComposeAfterAttribute.cs @@ -0,0 +1,59 @@ +using System; + +namespace Umbraco.Core.Components +{ + /// + /// Indicates that a composer requires another composer. + /// + /// + /// This attribute is *not* inherited. This means that a composer class inheriting from + /// another composer class does *not* inherit its requirements. However, the runtime checks + /// the *interfaces* of every composer for their requirements, so requirements declared on + /// interfaces are inherited by every composer class implementing the interface. + /// When targeting a class, indicates a dependency on the composer which must be enabled, + /// unless the requirement has explicitly been declared as weak (and then, only if the composer + /// is enabled). + /// When targeting an interface, indicates a dependency on enabled composers implementing + /// the interface. It could be no composer at all, unless the requirement has explicitly been + /// declared as strong (and at least one composer must be enabled). + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true, Inherited = false)] + public sealed class ComposeAfterAttribute : Attribute + { + /// + /// Initializes a new instance of the class. + /// + /// The type of the required composer. + public ComposeAfterAttribute(Type requiredType) + { + if (typeof(IComposer).IsAssignableFrom(requiredType) == false) + throw new ArgumentException($"Type {requiredType.FullName} is invalid here because it does not implement {typeof(IComposer).FullName}."); + RequiredType = requiredType; + } + + /// + /// Initializes a new instance of the class. + /// + /// The type of the required composer. + /// A value indicating whether the requirement is weak. + public ComposeAfterAttribute(Type requiredType, bool weak) + : this(requiredType) + { + Weak = weak; + } + + /// + /// Gets the required type. + /// + public Type RequiredType { get; } + + /// + /// Gets a value indicating whether the requirement is weak. + /// + /// Returns true if the requirement is weak (requires the other composer if it + /// is enabled), false if the requirement is strong (requires the other composer to be + /// enabled), and null if unspecified, in which case it is strong for classes and weak for + /// interfaces. + public bool? Weak { get; } + } +} diff --git a/src/Umbraco.Core/Components/ComposeBeforeAttribute.cs b/src/Umbraco.Core/Components/ComposeBeforeAttribute.cs new file mode 100644 index 0000000000..17065d1676 --- /dev/null +++ b/src/Umbraco.Core/Components/ComposeBeforeAttribute.cs @@ -0,0 +1,40 @@ +using System; + +namespace Umbraco.Core.Components +{ + /// + /// Indicates that a component is required by another composer. + /// + /// + /// This attribute is *not* inherited. This means that a composer class inheriting from + /// another composer class does *not* inherit its requirements. However, the runtime checks + /// the *interfaces* of every composer for their requirements, so requirements declared on + /// interfaces are inherited by every composer class implementing the interface. + /// When targeting a class, indicates a dependency on the composer which must be enabled, + /// unless the requirement has explicitly been declared as weak (and then, only if the composer + /// is enabled). + /// When targeting an interface, indicates a dependency on enabled composers implementing + /// the interface. It could be no composer at all, unless the requirement has explicitly been + /// declared as strong (and at least one composer must be enabled). + /// + + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true, Inherited = false)] + public sealed class ComposeBeforeAttribute : Attribute + { + /// + /// Initializes a new instance of the class. + /// + /// The type of the required composer. + public ComposeBeforeAttribute(Type requiringType) + { + if (typeof(IComposer).IsAssignableFrom(requiringType) == false) + throw new ArgumentException($"Type {requiringType.FullName} is invalid here because it does not implement {typeof(IComposer).FullName}."); + RequiringType = requiringType; + } + + /// + /// Gets the required type. + /// + public Type RequiringType { get; } + } +} diff --git a/src/Umbraco.Core/Components/Composers.cs b/src/Umbraco.Core/Components/Composers.cs new file mode 100644 index 0000000000..1c836e9e5c --- /dev/null +++ b/src/Umbraco.Core/Components/Composers.cs @@ -0,0 +1,294 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using Umbraco.Core.Collections; +using Umbraco.Core.Logging; + +namespace Umbraco.Core.Components +{ + // note: this class is NOT thread-safe in any ways + + /// + /// Handles the composers. + /// + public class Composers + { + private readonly Composition _composition; + private readonly IProfilingLogger _logger; + private readonly IEnumerable _composerTypes; + + private const int LogThresholdMilliseconds = 100; + + /// + /// Initializes a new instance of the class. + /// + /// The composition. + /// The composer types. + /// A profiling logger. + public Composers(Composition composition, IEnumerable composerTypes, IProfilingLogger logger) + { + _composition = composition ?? throw new ArgumentNullException(nameof(composition)); + _composerTypes = composerTypes ?? throw new ArgumentNullException(nameof(composerTypes)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + private class EnableInfo + { + public bool Enabled; + public int Weight = -1; + } + + /// + /// Instantiates and composes the composers. + /// + public void Compose() + { + // make sure it is there + _composition.WithCollectionBuilder(); + + IEnumerable orderedComposerTypes; + + using (_logger.DebugDuration("Preparing composer types.", "Prepared composer types.")) + { + orderedComposerTypes = PrepareComposerTypes(); + } + + var composers = InstantiateComposers(orderedComposerTypes); + + using (_logger.DebugDuration($"Composing composers. (log when >{LogThresholdMilliseconds}ms)", "Composed composers.")) + { + foreach (var composer in composers) + { + var componentType = composer.GetType(); + using (_logger.DebugDuration($"Composing {componentType.FullName}.", $"Composed {componentType.FullName}.", thresholdMilliseconds: LogThresholdMilliseconds)) + { + composer.Compose(_composition); + } + } + } + } + + private IEnumerable PrepareComposerTypes() + { + // create a list, remove those that cannot be enabled due to runtime level + var composerTypeList = _composerTypes + .Where(x => + { + // use the min level specified by the attribute if any + // otherwise, user composers have Run min level, anything else is Unknown (always run) + var attr = x.GetCustomAttribute(); + var minLevel = attr?.MinLevel ?? (x.Implements() ? RuntimeLevel.Run : RuntimeLevel.Unknown); + return _composition.RuntimeState.Level >= minLevel; + }) + .ToList(); + + // enable or disable composers + EnableDisableComposers(composerTypeList); + + // sort the composers according to their dependencies + var requirements = new Dictionary>(); + foreach (var type in composerTypeList) requirements[type] = null; + foreach (var type in composerTypeList) + { + GatherRequirementsFromRequireAttribute(type, composerTypeList, requirements); + GatherRequirementsFromRequiredByAttribute(type, composerTypeList, requirements); + } + + // only for debugging, this is verbose + //_logger.Debug(GetComposersReport(requirements)); + + // sort composers + var graph = new TopoGraph>>(kvp => kvp.Key, kvp => kvp.Value); + graph.AddItems(requirements); + List sortedComposerTypes; + try + { + sortedComposerTypes = graph.GetSortedItems().Select(x => x.Key).ToList(); + } + catch (Exception e) + { + // in case of an error, force-dump everything to log + _logger.Info("Composer Report:\r\n{ComposerReport}", GetComposersReport(requirements)); + _logger.Error(e, "Failed to sort composers."); + throw; + } + + // bit verbose but should help for troubleshooting + //var text = "Ordered Composers: " + Environment.NewLine + string.Join(Environment.NewLine, sortedComposerTypes) + Environment.NewLine; + _logger.Debug("Ordered Composers: {SortedComposerTypes}", sortedComposerTypes); + + return sortedComposerTypes; + } + + private static string GetComposersReport(Dictionary> requirements) + { + var text = new StringBuilder(); + text.AppendLine("Composers & Dependencies:"); + text.AppendLine(); + + foreach (var kvp in requirements) + { + var type = kvp.Key; + + text.AppendLine(type.FullName); + foreach (var attribute in type.GetCustomAttributes()) + text.AppendLine(" -> " + attribute.RequiredType + (attribute.Weak.HasValue + ? (attribute.Weak.Value ? " (weak)" : (" (strong" + (requirements.ContainsKey(attribute.RequiredType) ? ", missing" : "") + ")")) + : "")); + foreach (var attribute in type.GetCustomAttributes()) + text.AppendLine(" -< " + attribute.RequiringType); + foreach (var i in type.GetInterfaces()) + { + text.AppendLine(" : " + i.FullName); + foreach (var attribute in i.GetCustomAttributes()) + text.AppendLine(" -> " + attribute.RequiredType + (attribute.Weak.HasValue + ? (attribute.Weak.Value ? " (weak)" : (" (strong" + (requirements.ContainsKey(attribute.RequiredType) ? ", missing" : "") + ")")) + : "")); + foreach (var attribute in i.GetCustomAttributes()) + text.AppendLine(" -< " + attribute.RequiringType); + } + if (kvp.Value != null) + foreach (var t in kvp.Value) + text.AppendLine(" = " + t); + text.AppendLine(); + } + text.AppendLine("/"); + text.AppendLine(); + return text.ToString(); + } + + private static void EnableDisableComposers(ICollection types) + { + var enabled = new Dictionary(); + + // process the enable/disable attributes + // these two attributes are *not* inherited and apply to *classes* only (not interfaces). + // remote declarations (when a composer enables/disables *another* composer) + // have priority over local declarations (when a composer disables itself) so that + // ppl can enable composers that, by default, are disabled. + // what happens in case of conflicting remote declarations is unspecified. more + // precisely, the last declaration to be processed wins, but the order of the + // declarations depends on the type finder and is unspecified. + foreach (var composerType in types) + { + foreach (var attr in composerType.GetCustomAttributes()) + { + var type = attr.EnabledType ?? composerType; + if (enabled.TryGetValue(type, out var enableInfo) == false) enableInfo = enabled[type] = new EnableInfo(); + var weight = type == composerType ? 1 : 2; + if (enableInfo.Weight > weight) continue; + + enableInfo.Enabled = true; + enableInfo.Weight = weight; + } + foreach (var attr in composerType.GetCustomAttributes()) + { + var type = attr.DisabledType ?? composerType; + if (enabled.TryGetValue(type, out var enableInfo) == false) enableInfo = enabled[type] = new EnableInfo(); + var weight = type == composerType ? 1 : 2; + if (enableInfo.Weight > weight) continue; + + enableInfo.Enabled = false; + enableInfo.Weight = weight; + } + } + + // remove composers that end up being disabled + foreach (var kvp in enabled.Where(x => x.Value.Enabled == false)) + types.Remove(kvp.Key); + } + + private static void GatherRequirementsFromRequireAttribute(Type type, ICollection types, IDictionary> requirements) + { + // get 'require' attributes + // these attributes are *not* inherited because we want to "custom-inherit" for interfaces only + var requireAttributes = type + .GetInterfaces().SelectMany(x => x.GetCustomAttributes()) // those marking interfaces + .Concat(type.GetCustomAttributes()); // those marking the composer + + // what happens in case of conflicting attributes (different strong/weak for same type) is not specified. + foreach (var attr in requireAttributes) + { + if (attr.RequiredType == type) continue; // ignore self-requirements (+ exclude in implems, below) + + // requiring an interface = require any enabled composer implementing that interface + // unless strong, and then require at least one enabled composer implementing that interface + if (attr.RequiredType.IsInterface) + { + var implems = types.Where(x => x != type && attr.RequiredType.IsAssignableFrom(x)).ToList(); + if (implems.Count > 0) + { + if (requirements[type] == null) requirements[type] = new List(); + requirements[type].AddRange(implems); + } + else if (attr.Weak == false) // if explicitly set to !weak, is strong, else is weak + throw new Exception($"Broken composer dependency: {type.FullName} -> {attr.RequiredType.FullName}."); + } + // requiring a class = require that the composer is enabled + // unless weak, and then requires it if it is enabled + else + { + if (types.Contains(attr.RequiredType)) + { + if (requirements[type] == null) requirements[type] = new List(); + requirements[type].Add(attr.RequiredType); + } + else if (attr.Weak != true) // if not explicitly set to weak, is strong + throw new Exception($"Broken composer dependency: {type.FullName} -> {attr.RequiredType.FullName}."); + } + } + } + + private static void GatherRequirementsFromRequiredByAttribute(Type type, ICollection types, IDictionary> requirements) + { + // get 'required' attributes + // these attributes are *not* inherited because we want to "custom-inherit" for interfaces only + var requiredAttributes = type + .GetInterfaces().SelectMany(x => x.GetCustomAttributes()) // those marking interfaces + .Concat(type.GetCustomAttributes()); // those marking the composer + + foreach (var attr in requiredAttributes) + { + if (attr.RequiringType == type) continue; // ignore self-requirements (+ exclude in implems, below) + + // required by an interface = by any enabled composer implementing this that interface + if (attr.RequiringType.IsInterface) + { + var implems = types.Where(x => x != type && attr.RequiringType.IsAssignableFrom(x)).ToList(); + foreach (var implem in implems) + { + if (requirements[implem] == null) requirements[implem] = new List(); + requirements[implem].Add(type); + } + } + // required by a class + else + { + if (types.Contains(attr.RequiringType)) + { + if (requirements[attr.RequiringType] == null) requirements[attr.RequiringType] = new List(); + requirements[attr.RequiringType].Add(type); + } + } + } + } + + private IEnumerable InstantiateComposers(IEnumerable types) + { + IComposer InstantiateComposer(Type type) + { + var ctor = type.GetConstructor(Array.Empty()); + if (ctor == null) + throw new InvalidOperationException($"Composer {type.FullName} does not have a parameter-less constructor."); + return (IComposer) ctor.Invoke(Array.Empty()); + } + + using (_logger.DebugDuration("Instantiating composers.", "Instantiated composers.")) + { + return types.Select(InstantiateComposer).ToArray(); + } + } + } +} diff --git a/src/Umbraco.Core/Components/Composition.cs b/src/Umbraco.Core/Components/Composition.cs index 671469c73a..dd0b83dcb3 100644 --- a/src/Umbraco.Core/Components/Composition.cs +++ b/src/Umbraco.Core/Components/Composition.cs @@ -1,4 +1,8 @@ -using LightInject; +using System; +using System.Collections.Generic; +using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; namespace Umbraco.Core.Components { @@ -10,28 +14,206 @@ namespace Umbraco.Core.Components /// avoid accessing the container. This is because everything needs to be properly registered and with /// the proper lifecycle. These methods will take care of it. Directly registering into the container /// may cause issues. - public class Composition + public class Composition : IRegister { + private readonly Dictionary _builders = new Dictionary(); + private readonly Dictionary _uniques = new Dictionary(); + private readonly IRegister _register; + /// /// Initializes a new instance of the class. /// - /// A container. - /// The runtime level. - public Composition(IServiceContainer container, RuntimeLevel level) + /// A register. + /// A type loader. + /// A logger. + /// The runtime state. + /// Optional configs. + public Composition(IRegister register, TypeLoader typeLoader, IProfilingLogger logger, IRuntimeState runtimeState, Configs configs = null) { - Container = container; - RuntimeLevel = level; + _register = register; + TypeLoader = typeLoader; + Logger = logger; + RuntimeState = runtimeState; + + if (configs == null) + { + configs = new Configs(); + configs.AddCoreConfigs(); + } + + Configs = configs; + } + + #region Services + + /// + /// Gets the logger. + /// + public IProfilingLogger Logger { get; } + + /// + /// Gets the type loader. + /// + public TypeLoader TypeLoader { get; } + + /// + /// Gets the runtime state. + /// + public IRuntimeState RuntimeState { get; } + + /// + /// Gets the configurations. + /// + public Configs Configs { get; } + + #endregion + + #region IRegister + + /// + public object Concrete => _register.Concrete; + + /// + public void Register(Type serviceType, Lifetime lifetime = Lifetime.Transient) + => _register.Register(serviceType, lifetime); + + /// + public void Register(Type serviceType, Type implementingType, Lifetime lifetime = Lifetime.Transient) + => _register.Register(serviceType, implementingType, lifetime); + + /// + public void Register(Func factory, Lifetime lifetime = Lifetime.Transient) + => _register.Register(factory, lifetime); + + /// + public void RegisterInstance(Type serviceType, object instance) + => _register.RegisterInstance(serviceType, instance); + + /// + public void RegisterAuto(Type serviceBaseType) + => _register.RegisterAuto(serviceBaseType); + + /// + public void ConfigureForWeb() + => _register.ConfigureForWeb(); + + /// + public IFactory CreateFactory() + { + foreach (var onCreating in OnCreatingFactory.Values) + onCreating(); + + foreach (var unique in _uniques.Values) + unique.RegisterWith(_register); + + foreach (var builder in _builders.Values) + builder.RegisterWith(_register); + + Configs.RegisterWith(_register); + + return _register.CreateFactory(); } /// - /// Gets the container. + /// Gets a dictionary of action to execute when creating the factory. /// - /// Use with care! - public IServiceContainer Container { get; } + public Dictionary OnCreatingFactory { get; } = new Dictionary(); + + #endregion + + #region Unique /// - /// Gets the runtime level. + /// Registers a unique service. /// - public RuntimeLevel RuntimeLevel { get; } + /// Unique services have one single implementation, and a Singleton lifetime. + public void RegisterUnique(Type serviceType, Type implementingType) + => _uniques[serviceType] = new Unique(serviceType, implementingType); + + /// + /// Registers a unique service. + /// + /// Unique services have one single implementation, and a Singleton lifetime. + public void RegisterUnique(Type serviceType, object instance) + => _uniques[serviceType] = new Unique(serviceType, instance); + + /// + /// Registers a unique service. + /// + /// Unique services have one single implementation, and a Singleton lifetime. + public void RegisterUnique(Func factory) + => _uniques[typeof(TService)] = new Unique(factory); + + private class Unique + { + private readonly Type _serviceType; + private readonly Type _implementingType; + private readonly object _instance; + + protected Unique(Type serviceType) + { + _serviceType = serviceType; + } + + public Unique(Type serviceType, Type implementingType) + : this(serviceType) + { + _implementingType = implementingType; + } + + public Unique(Type serviceType, object instance) + : this(serviceType) + { + _instance = instance; + } + + public virtual void RegisterWith(IRegister register) + { + if (_implementingType != null) + register.Register(_serviceType, _implementingType, Lifetime.Singleton); + else if (_instance != null) + register.RegisterInstance(_serviceType, _instance); + } + } + + private class Unique : Unique + { + private readonly Func _factory; + + public Unique(Func factory) + : base(typeof(TService)) + { + _factory = factory; + } + + public override void RegisterWith(IRegister register) + { + register.Register(_factory, Lifetime.Singleton); + } + } + + #endregion + + #region Collection Builders + + /// + /// Gets a collection builder (and registers the collection). + /// + /// The type of the collection builder. + /// The collection builder. + public TBuilder WithCollectionBuilder() + where TBuilder: ICollectionBuilder, new() + { + var typeOfBuilder = typeof(TBuilder); + + if (_builders.TryGetValue(typeOfBuilder, out var o)) + return (TBuilder) o; + + var builder = new TBuilder(); + _builders[typeOfBuilder] = builder; + return builder; + } + + #endregion } } diff --git a/src/Umbraco.Core/Components/CompositionExtensions.cs b/src/Umbraco.Core/Components/CompositionExtensions.cs index 7e94e4dc2b..93d190d17e 100644 --- a/src/Umbraco.Core/Components/CompositionExtensions.cs +++ b/src/Umbraco.Core/Components/CompositionExtensions.cs @@ -1,9 +1,8 @@ using System; -using System.Runtime.CompilerServices; -using LightInject; using Umbraco.Core.Cache; using Umbraco.Core.Dictionary; using Umbraco.Core.Composing; +using Umbraco.Core.IO; using Umbraco.Core.Migrations; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Persistence.Mappers; @@ -19,6 +18,45 @@ namespace Umbraco.Core.Components /// public static class CompositionExtensions { + #region FileSystems + + /// + /// Registers a filesystem. + /// + /// The type of the filesystem. + /// The implementing type. + /// The composition. + /// A factory method creating the supporting filesystem. + /// The register. + public static void RegisterFileSystem(this Composition composition, Func supportingFileSystemFactory) + where TImplementing : FileSystemWrapper, TFileSystem + { + composition.RegisterUnique(factory => + { + var fileSystems = factory.GetInstance(); + return fileSystems.GetFileSystem(supportingFileSystemFactory(factory)); + }); + } + + /// + /// Registers a filesystem. + /// + /// The type of the filesystem. + /// The composition. + /// A factory method creating the supporting filesystem. + /// The register. + public static void RegisterFileSystem(this Composition composition, Func supportingFileSystemFactory) + where TFileSystem : FileSystemWrapper + { + composition.RegisterUnique(factory => + { + var fileSystems = factory.GetInstance(); + return fileSystems.GetFileSystem(supportingFileSystemFactory(factory)); + }); + } + + #endregion + #region Collection Builders /// @@ -26,60 +64,66 @@ namespace Umbraco.Core.Components /// /// The composition. public static CacheRefresherCollectionBuilder CacheRefreshers(this Composition composition) - => composition.Container.GetInstance(); + => composition.WithCollectionBuilder(); /// /// Gets the mappers collection builder. /// /// The composition. public static MapperCollectionBuilder Mappers(this Composition composition) - => composition.Container.GetInstance(); + => composition.WithCollectionBuilder(); /// /// Gets the package actions collection builder. /// /// The composition. internal static PackageActionCollectionBuilder PackageActions(this Composition composition) - => composition.Container.GetInstance(); + => composition.WithCollectionBuilder(); /// /// Gets the data editor collection builder. /// /// The composition. public static DataEditorCollectionBuilder DataEditors(this Composition composition) - => composition.Container.GetInstance(); + => composition.WithCollectionBuilder(); /// /// Gets the property value converters collection builder. /// /// The composition. public static PropertyValueConverterCollectionBuilder PropertyValueConverters(this Composition composition) - => composition.Container.GetInstance(); + => composition.WithCollectionBuilder(); /// /// Gets the url segment providers collection builder. /// /// The composition. public static UrlSegmentProviderCollectionBuilder UrlSegmentProviders(this Composition composition) - => composition.Container.GetInstance(); + => composition.WithCollectionBuilder(); /// /// Gets the validators collection builder. /// /// The composition. internal static ManifestValueValidatorCollectionBuilder Validators(this Composition composition) - => composition.Container.GetInstance(); + => composition.WithCollectionBuilder(); /// /// Gets the post-migrations collection builder. /// /// The composition. internal static PostMigrationCollectionBuilder PostMigrations(this Composition composition) - => composition.Container.GetInstance(); + => composition.WithCollectionBuilder(); + + /// + /// Gets the components collection builder. + /// + public static ComponentCollectionBuilder Components(this Composition composition) + => composition.WithCollectionBuilder(); #endregion - #region Singleton + #region Uniques /// /// Sets the culture dictionary factory. @@ -89,7 +133,7 @@ namespace Umbraco.Core.Components public static void SetCultureDictionaryFactory(this Composition composition) where T : ICultureDictionaryFactory { - composition.Container.RegisterSingleton(); + composition.RegisterUnique(); } /// @@ -97,9 +141,9 @@ namespace Umbraco.Core.Components /// /// The composition. /// A function creating a culture dictionary factory. - public static void SetCultureDictionaryFactory(this Composition composition, Func factory) + public static void SetCultureDictionaryFactory(this Composition composition, Func factory) { - composition.Container.RegisterSingleton(factory); + composition.RegisterUnique(factory); } /// @@ -109,7 +153,7 @@ namespace Umbraco.Core.Components /// A factory. public static void SetCultureDictionaryFactory(this Composition composition, ICultureDictionaryFactory factory) { - composition.Container.RegisterSingleton(_ => factory); + composition.RegisterUnique(_ => factory); } /// @@ -120,7 +164,7 @@ namespace Umbraco.Core.Components public static void SetPublishedContentModelFactory(this Composition composition) where T : IPublishedModelFactory { - composition.Container.RegisterSingleton(); + composition.RegisterUnique(); } /// @@ -128,9 +172,9 @@ namespace Umbraco.Core.Components /// /// The composition. /// A function creating a published content model factory. - public static void SetPublishedContentModelFactory(this Composition composition, Func factory) + public static void SetPublishedContentModelFactory(this Composition composition, Func factory) { - composition.Container.RegisterSingleton(factory); + composition.RegisterUnique(factory); } /// @@ -140,7 +184,7 @@ namespace Umbraco.Core.Components /// A published content model factory. public static void SetPublishedContentModelFactory(this Composition composition, IPublishedModelFactory factory) { - composition.Container.RegisterSingleton(_ => factory); + composition.RegisterUnique(_ => factory); } /// @@ -151,7 +195,7 @@ namespace Umbraco.Core.Components public static void SetServerRegistrar(this Composition composition) where T : IServerRegistrar { - composition.Container.RegisterSingleton(); + composition.RegisterUnique(); } /// @@ -159,9 +203,9 @@ namespace Umbraco.Core.Components /// /// The composition. /// A function creating a server registar. - public static void SetServerRegistrar(this Composition composition, Func factory) + public static void SetServerRegistrar(this Composition composition, Func factory) { - composition.Container.RegisterSingleton(factory); + composition.RegisterUnique(factory); } /// @@ -171,7 +215,7 @@ namespace Umbraco.Core.Components /// A server registrar. public static void SetServerRegistrar(this Composition composition, IServerRegistrar registrar) { - composition.Container.RegisterSingleton(_ => registrar); + composition.RegisterUnique(_ => registrar); } /// @@ -182,7 +226,7 @@ namespace Umbraco.Core.Components public static void SetServerMessenger(this Composition composition) where T : IServerMessenger { - composition.Container.RegisterSingleton(); + composition.RegisterUnique(); } /// @@ -190,9 +234,9 @@ namespace Umbraco.Core.Components /// /// The composition. /// A function creating a server messenger. - public static void SetServerMessenger(this Composition composition, Func factory) + public static void SetServerMessenger(this Composition composition, Func factory) { - composition.Container.RegisterSingleton(factory); + composition.RegisterUnique(factory); } /// @@ -202,7 +246,7 @@ namespace Umbraco.Core.Components /// A server messenger. public static void SetServerMessenger(this Composition composition, IServerMessenger registrar) { - composition.Container.RegisterSingleton(_ => registrar); + composition.RegisterUnique(_ => registrar); } /// @@ -213,7 +257,7 @@ namespace Umbraco.Core.Components public static void SetShortStringHelper(this Composition composition) where T : IShortStringHelper { - composition.Container.RegisterSingleton(); + composition.RegisterUnique(); } /// @@ -221,9 +265,9 @@ namespace Umbraco.Core.Components /// /// The composition. /// A function creating a short string helper. - public static void SetShortStringHelper(this Composition composition, Func factory) + public static void SetShortStringHelper(this Composition composition, Func factory) { - composition.Container.RegisterSingleton(factory); + composition.RegisterUnique(factory); } /// @@ -233,7 +277,7 @@ namespace Umbraco.Core.Components /// A short string helper. public static void SetShortStringHelper(this Composition composition, IShortStringHelper helper) { - composition.Container.RegisterSingleton(_ => helper); + composition.RegisterUnique(_ => helper); } #endregion diff --git a/src/Umbraco.Core/Components/DisableAttribute.cs b/src/Umbraco.Core/Components/DisableAttribute.cs new file mode 100644 index 0000000000..f9a7249b89 --- /dev/null +++ b/src/Umbraco.Core/Components/DisableAttribute.cs @@ -0,0 +1,38 @@ +using System; + +namespace Umbraco.Core.Components +{ + /// + /// Indicates that a composer should be disabled. + /// + /// + /// If a type is specified, disables the composer of that type, else disables the composer marked with the attribute. + /// This attribute is *not* inherited. + /// This attribute applies to classes only, it is not possible to enable/disable interfaces. + /// If a composer ends up being both enabled and disabled: attributes marking the composer itself have lower priority + /// than attributes on *other* composers, eg if a composer declares itself as disabled it is possible to enable it from + /// another composer. Anything else is unspecified. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] + public class DisableAttribute : Attribute + { + /// + /// Initializes a new instance of the class. + /// + public DisableAttribute() + { } + + /// + /// Initializes a new instance of the class. + /// + public DisableAttribute(Type disabledType) + { + DisabledType = disabledType; + } + + /// + /// Gets the disabled type, or null if it is the composer marked with the attribute. + /// + public Type DisabledType { get; } + } +} diff --git a/src/Umbraco.Core/Components/DisableComponentAttribute.cs b/src/Umbraco.Core/Components/DisableComponentAttribute.cs deleted file mode 100644 index f7ff71e119..0000000000 --- a/src/Umbraco.Core/Components/DisableComponentAttribute.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; - -namespace Umbraco.Core.Components -{ - /// - /// Indicates that a component should be disabled. - /// - /// - /// If a type is specified, disables the component of that type, else disables the component marked with the attribute. - /// This attribute is *not* inherited. - /// This attribute applies to classes only, it is not possible to enable/disable interfaces. - /// If a component ends up being both enabled and disabled: attributes marking the component itself have lower priority - /// than attributes on *other* components, eg if a component declares itself as disabled it is possible to enable it from - /// another component. Anything else is unspecified. - /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] - public class DisableComponentAttribute : Attribute - { - /// - /// Initializes a new instance of the class. - /// - public DisableComponentAttribute() - { } - - /// - /// Initializes a new instance of the class. - /// - public DisableComponentAttribute(Type disabledType) - { - DisabledType = disabledType; - } - - /// - /// Gets the disabled type, or null if it is the component marked with the attribute. - /// - public Type DisabledType { get; } - } -} diff --git a/src/Umbraco.Core/Components/EnableAttribute.cs b/src/Umbraco.Core/Components/EnableAttribute.cs new file mode 100644 index 0000000000..edf3cbdc2e --- /dev/null +++ b/src/Umbraco.Core/Components/EnableAttribute.cs @@ -0,0 +1,38 @@ +using System; + +namespace Umbraco.Core.Components +{ + /// + /// Indicates that a composer should be enabled. + /// + /// + /// If a type is specified, enables the composer of that type, else enables the composer marked with the attribute. + /// This attribute is *not* inherited. + /// This attribute applies to classes only, it is not possible to enable/disable interfaces. + /// If a composer ends up being both enabled and disabled: attributes marking the composer itself have lower priority + /// than attributes on *other* composers, eg if a composer declares itself as disabled it is possible to enable it from + /// another composer. Anything else is unspecified. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] + public class EnableAttribute : Attribute + { + /// + /// Initializes a new instance of the class. + /// + public EnableAttribute() + { } + + /// + /// Initializes a new instance of the class. + /// + public EnableAttribute(Type enabledType) + { + EnabledType = enabledType; + } + + /// + /// Gets the enabled type, or null if it is the composer marked with the attribute. + /// + public Type EnabledType { get; } + } +} diff --git a/src/Umbraco.Core/Components/EnableComponentAttribute.cs b/src/Umbraco.Core/Components/EnableComponentAttribute.cs deleted file mode 100644 index fa76dc2404..0000000000 --- a/src/Umbraco.Core/Components/EnableComponentAttribute.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; - -namespace Umbraco.Core.Components -{ - /// - /// Indicates that a component should be enabled. - /// - /// - /// If a type is specified, enables the component of that type, else enables the component marked with the attribute. - /// This attribute is *not* inherited. - /// This attribute applies to classes only, it is not possible to enable/disable interfaces. - /// If a component ends up being both enabled and disabled: attributes marking the component itself have lower priority - /// than attributes on *other* components, eg if a component declares itself as disabled it is possible to enable it from - /// another component. Anything else is unspecified. - /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] - public class EnableComponentAttribute : Attribute - { - /// - /// Initializes a new instance of the class. - /// - public EnableComponentAttribute() - { } - - /// - /// Initializes a new instance of the class. - /// - public EnableComponentAttribute(Type enabledType) - { - EnabledType = enabledType; - } - - /// - /// Gets the enabled type, or null if it is the component marked with the attribute. - /// - public Type EnabledType { get; } - } -} diff --git a/src/Umbraco.Core/Components/IComponent.cs b/src/Umbraco.Core/Components/IComponent.cs new file mode 100644 index 0000000000..b1954d821b --- /dev/null +++ b/src/Umbraco.Core/Components/IComponent.cs @@ -0,0 +1,25 @@ +namespace Umbraco.Core.Components +{ + /// + /// Represents a component. + /// + /// + /// Components are created by DI and therefore must have a public constructor. + /// All components are terminated in reverse order when Umbraco terminates, and + /// disposable components are disposed. + /// The Dispose method may be invoked more than once, and components + /// should ensure they support this. + /// + public interface IComponent + { + /// + /// Initializes the component. + /// + void Initialize(); + + /// + /// Terminates the component. + /// + void Terminate(); + } +} diff --git a/src/Umbraco.Core/Components/IComposer.cs b/src/Umbraco.Core/Components/IComposer.cs new file mode 100644 index 0000000000..ce02aa4f13 --- /dev/null +++ b/src/Umbraco.Core/Components/IComposer.cs @@ -0,0 +1,16 @@ +using Umbraco.Core.Composing; + +namespace Umbraco.Core.Components +{ + /// + /// Represents a composer. + /// + public interface IComposer : IDiscoverable + { + /// + /// Compose. + /// + /// + void Compose(Composition composition); + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Components/ICoreComposer.cs b/src/Umbraco.Core/Components/ICoreComposer.cs new file mode 100644 index 0000000000..94aa9953a9 --- /dev/null +++ b/src/Umbraco.Core/Components/ICoreComposer.cs @@ -0,0 +1,13 @@ +namespace Umbraco.Core.Components +{ + /// + /// Represents a core . + /// + /// + /// All core composers are required by (compose before) all user composers, + /// and require (compose after) all runtime composers. + /// + [ComposeAfter(typeof(IRuntimeComposer))] + public interface ICoreComposer : IComposer + { } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Components/IRuntimeComponent.cs b/src/Umbraco.Core/Components/IRuntimeComponent.cs deleted file mode 100644 index 7f89d21e87..0000000000 --- a/src/Umbraco.Core/Components/IRuntimeComponent.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Umbraco.Core.Components -{ - public interface IRuntimeComponent : IUmbracoComponent - { } -} diff --git a/src/Umbraco.Core/Components/IRuntimeComposer.cs b/src/Umbraco.Core/Components/IRuntimeComposer.cs new file mode 100644 index 0000000000..4b8253ee6c --- /dev/null +++ b/src/Umbraco.Core/Components/IRuntimeComposer.cs @@ -0,0 +1,11 @@ +namespace Umbraco.Core.Components +{ + /// + /// Represents a runtime . + /// + /// + /// All runtime composers are required by (compose before) all core composers + /// + public interface IRuntimeComposer : IComposer + { } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Components/IUmbracoComponent.cs b/src/Umbraco.Core/Components/IUmbracoComponent.cs deleted file mode 100644 index d25b97c270..0000000000 --- a/src/Umbraco.Core/Components/IUmbracoComponent.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Umbraco.Core.Composing; - -namespace Umbraco.Core.Components -{ - /// - /// Represents an Umbraco component. - /// - public interface IUmbracoComponent : IDiscoverable - { - /// - /// Composes the component. - /// - /// The composition. - void Compose(Composition composition); - - /// - /// Terminates the component. - /// - void Terminate(); - } -} diff --git a/src/Umbraco.Core/Components/IUmbracoCoreComponent.cs b/src/Umbraco.Core/Components/IUmbracoCoreComponent.cs deleted file mode 100644 index 28ff286da3..0000000000 --- a/src/Umbraco.Core/Components/IUmbracoCoreComponent.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Umbraco.Core.Components -{ - [RequireComponent(typeof(IRuntimeComponent))] - public interface IUmbracoCoreComponent : IUmbracoComponent - { } -} diff --git a/src/Umbraco.Core/Components/IUmbracoUserComponent.cs b/src/Umbraco.Core/Components/IUmbracoUserComponent.cs deleted file mode 100644 index 61a07cfd48..0000000000 --- a/src/Umbraco.Core/Components/IUmbracoUserComponent.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Umbraco.Core.Components -{ - [RequireComponent(typeof(UmbracoCoreComponent))] - public interface IUmbracoUserComponent : IUmbracoComponent - { } -} diff --git a/src/Umbraco.Core/Components/IUserComposer.cs b/src/Umbraco.Core/Components/IUserComposer.cs new file mode 100644 index 0000000000..59e0023635 --- /dev/null +++ b/src/Umbraco.Core/Components/IUserComposer.cs @@ -0,0 +1,12 @@ +namespace Umbraco.Core.Components +{ + /// + /// Represents a user . + /// + /// + /// All user composers require (compose after) all core composers. + /// + [ComposeAfter(typeof(ICoreComposer))] + public interface IUserComposer : IComposer + { } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Components/ManifestWatcherComponent.cs b/src/Umbraco.Core/Components/ManifestWatcherComponent.cs index 0a8d9ccd52..2420e1d5bb 100644 --- a/src/Umbraco.Core/Components/ManifestWatcherComponent.cs +++ b/src/Umbraco.Core/Components/ManifestWatcherComponent.cs @@ -5,16 +5,24 @@ using Umbraco.Core.Manifest; namespace Umbraco.Core.Components { - [RuntimeLevel(MinLevel = RuntimeLevel.Run)] - public class ManifestWatcherComponent : UmbracoComponentBase, IUmbracoCoreComponent + public sealed class ManifestWatcherComponent : IComponent { + private readonly IRuntimeState _runtimeState; + private readonly ILogger _logger; + // if configured and in debug mode, a ManifestWatcher watches App_Plugins folders for // package.manifest chances and restarts the application on any change private ManifestWatcher _mw; - public void Initialize(IRuntimeState runtime, ILogger logger) + public ManifestWatcherComponent(IRuntimeState runtimeState, ILogger logger) { - if (runtime.Debug == false) return; + _runtimeState = runtimeState; + _logger = logger; + } + + public void Initialize() + { + if (_runtimeState.Debug == false) return; //if (ApplicationContext.Current.IsConfigured == false || GlobalSettings.DebugMode == false) // return; @@ -22,13 +30,15 @@ namespace Umbraco.Core.Components var appPlugins = IOHelper.MapPath("~/App_Plugins/"); if (Directory.Exists(appPlugins) == false) return; - _mw = new ManifestWatcher(logger); + _mw = new ManifestWatcher(_logger); _mw.Start(Directory.GetDirectories(appPlugins)); } - public override void Terminate() + public void Terminate() { - _mw?.Dispose(); + if (_mw == null) return; + + _mw.Dispose(); _mw = null; } } diff --git a/src/Umbraco.Core/Components/ManifestWatcherComposer.cs b/src/Umbraco.Core/Components/ManifestWatcherComposer.cs new file mode 100644 index 0000000000..b08680156b --- /dev/null +++ b/src/Umbraco.Core/Components/ManifestWatcherComposer.cs @@ -0,0 +1,6 @@ +namespace Umbraco.Core.Components +{ + [RuntimeLevel(MinLevel = RuntimeLevel.Run)] + public class ManifestWatcherComposer : ComponentComposer, ICoreComposer + { } +} diff --git a/src/Umbraco.Core/Components/RelateOnCopyComponent.cs b/src/Umbraco.Core/Components/RelateOnCopyComponent.cs index bc66dccd31..404d385680 100644 --- a/src/Umbraco.Core/Components/RelateOnCopyComponent.cs +++ b/src/Umbraco.Core/Components/RelateOnCopyComponent.cs @@ -6,14 +6,16 @@ using Umbraco.Core.Services.Implement; namespace Umbraco.Core.Components { //TODO: This should just exist in the content service/repo! - [RuntimeLevel(MinLevel = RuntimeLevel.Run)] - public sealed class RelateOnCopyComponent : UmbracoComponentBase, IUmbracoCoreComponent + public sealed class RelateOnCopyComponent : IComponent { public void Initialize() { ContentService.Copied += ContentServiceCopied; } + public void Terminate() + { } + private static void ContentServiceCopied(IContentService sender, Events.CopyEventArgs e) { if (e.RelateToOriginal == false) return; diff --git a/src/Umbraco.Core/Components/RelateOnCopyComposer.cs b/src/Umbraco.Core/Components/RelateOnCopyComposer.cs new file mode 100644 index 0000000000..f5e9423edd --- /dev/null +++ b/src/Umbraco.Core/Components/RelateOnCopyComposer.cs @@ -0,0 +1,6 @@ +namespace Umbraco.Core.Components +{ + [RuntimeLevel(MinLevel = RuntimeLevel.Run)] + public sealed class RelateOnCopyComposer : ComponentComposer, ICoreComposer + { } +} diff --git a/src/Umbraco.Core/Components/RelateOnTrashComponent.cs b/src/Umbraco.Core/Components/RelateOnTrashComponent.cs index 8bcce50c68..6279bb98ba 100644 --- a/src/Umbraco.Core/Components/RelateOnTrashComponent.cs +++ b/src/Umbraco.Core/Components/RelateOnTrashComponent.cs @@ -7,8 +7,7 @@ using Umbraco.Core.Services.Implement; namespace Umbraco.Core.Components { - [RuntimeLevel(MinLevel = RuntimeLevel.Run)] - public sealed class RelateOnTrashComponent : UmbracoComponentBase, IUmbracoCoreComponent + public sealed class RelateOnTrashComponent : IComponent { public void Initialize() { @@ -18,6 +17,9 @@ namespace Umbraco.Core.Components MediaService.Trashed += MediaService_Trashed; } + public void Terminate() + { } + private static void ContentService_Moved(IContentService sender, MoveEventArgs e) { foreach (var item in e.MoveInfoCollection.Where(x => x.OriginalPath.Contains(Constants.System.RecycleBinContent.ToInvariantString()))) diff --git a/src/Umbraco.Core/Components/RelateOnTrashComposer.cs b/src/Umbraco.Core/Components/RelateOnTrashComposer.cs new file mode 100644 index 0000000000..5d89bc0e37 --- /dev/null +++ b/src/Umbraco.Core/Components/RelateOnTrashComposer.cs @@ -0,0 +1,6 @@ +namespace Umbraco.Core.Components +{ + [RuntimeLevel(MinLevel = RuntimeLevel.Run)] + public sealed class RelateOnTrashComposer : ComponentComposer, ICoreComposer + { } +} diff --git a/src/Umbraco.Core/Components/RequireComponentAttribute.cs b/src/Umbraco.Core/Components/RequireComponentAttribute.cs deleted file mode 100644 index 2d53413f99..0000000000 --- a/src/Umbraco.Core/Components/RequireComponentAttribute.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; - -namespace Umbraco.Core.Components -{ - /// - /// Indicates that a component requires another component. - /// - /// - /// This attribute is *not* inherited. This means that a component class inheriting from - /// another component class does *not* inherit its requirements. However, the bootloader checks - /// the *interfaces* of every component for their requirements, so requirements declared on - /// interfaces are inherited by every component class implementing the interface. - /// When targetting a class, indicates a dependency on the component which must be enabled, - /// unless the requirement has explicitely been declared as weak (and then, only if the component - /// is enabled). - /// When targetting an interface, indicates a dependency on enabled components implementing - /// the interface. It could be no component at all, unless the requirement has explicitely been - /// declared as strong (and at least one component must be enabled). - /// - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true, Inherited = false)] - public class RequireComponentAttribute : Attribute - { - /// - /// Initializes a new instance of the class. - /// - /// The type of the required component. - public RequireComponentAttribute(Type requiredType) - { - if (typeof(IUmbracoComponent).IsAssignableFrom(requiredType) == false) - throw new ArgumentException($"Type {requiredType.FullName} is invalid here because it does not implement {typeof(IUmbracoComponent).FullName}."); - RequiredType = requiredType; - } - - /// - /// Initializes a new instance of the class. - /// - /// The type of the required component. - /// A value indicating whether the requirement is weak. - public RequireComponentAttribute(Type requiredType, bool weak) - : this(requiredType) - { - Weak = weak; - } - - /// - /// Gets the required type. - /// - public Type RequiredType { get; } - - /// - /// Gets a value indicating whether the requirement is weak. - /// - /// Returns true if the requirement is weak (requires the other component if it - /// is enabled), false if the requirement is strong (requires the other component to be - /// enabled), and null if unspecified, in which case it is strong for classes and weak for - /// interfaces. - public bool? Weak { get; } - } -} diff --git a/src/Umbraco.Core/Components/RequiredComponentAttribute.cs b/src/Umbraco.Core/Components/RequiredComponentAttribute.cs deleted file mode 100644 index 7895445179..0000000000 --- a/src/Umbraco.Core/Components/RequiredComponentAttribute.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; - -namespace Umbraco.Core.Components -{ - /// - /// Indicates that a component is required by another component. - /// - /// - /// fixme - /// This attribute is *not* inherited. This means that a component class inheriting from - /// another component class does *not* inherit its requirements. However, the bootloader checks - /// the *interfaces* of every component for their requirements, so requirements declared on - /// interfaces are inherited by every component class implementing the interface. - /// When targetting a class, indicates a dependency on the component which must be enabled, - /// unless the requirement has explicitely been declared as weak (and then, only if the component - /// is enabled). - /// When targetting an interface, indicates a dependency on enabled components implementing - /// the interface. It could be no component at all, unless the requirement has explicitely been - /// declared as strong (and at least one component must be enabled). - /// - - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true, Inherited = false)] - public class RequiredComponentAttribute : Attribute - { - /// - /// Initializes a new instance of the class. - /// - /// The type of the required component. - public RequiredComponentAttribute(Type requiringType) - { - if (typeof(IUmbracoComponent).IsAssignableFrom(requiringType) == false) - throw new ArgumentException($"Type {requiringType.FullName} is invalid here because it does not implement {typeof(IUmbracoComponent).FullName}."); - RequiringType = requiringType; - } - - /// - /// Gets the required type. - /// - public Type RequiringType { get; } - } -} diff --git a/src/Umbraco.Core/Components/UmbracoComponentBase.cs b/src/Umbraco.Core/Components/UmbracoComponentBase.cs deleted file mode 100644 index 476c4c6d59..0000000000 --- a/src/Umbraco.Core/Components/UmbracoComponentBase.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Umbraco.Core.Components -{ - /// - /// Provides a base class for implementations. - /// - public abstract class UmbracoComponentBase : IUmbracoComponent - { - /// - public virtual void Compose(Composition composition) - { } - - /// - public virtual void Terminate() - { } - } -} diff --git a/src/Umbraco.Core/Components/UmbracoCoreComponent.cs b/src/Umbraco.Core/Components/UmbracoCoreComponent.cs deleted file mode 100644 index 9f6709c494..0000000000 --- a/src/Umbraco.Core/Components/UmbracoCoreComponent.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Umbraco.Core.Components -{ - // the UmbracoCoreComponent requires all IUmbracoCoreComponent - // all user-level components should require the UmbracoCoreComponent - - [RequireComponent(typeof(IUmbracoCoreComponent))] - public class UmbracoCoreComponent : UmbracoComponentBase - { } -} diff --git a/src/Umbraco.Core/Composing/CollectionBuilderBase.cs b/src/Umbraco.Core/Composing/CollectionBuilderBase.cs index 3fac2d3255..7633f6b001 100644 --- a/src/Umbraco.Core/Composing/CollectionBuilderBase.cs +++ b/src/Umbraco.Core/Composing/CollectionBuilderBase.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Linq.Expressions; -using LightInject; namespace Umbraco.Core.Composing { @@ -18,65 +16,30 @@ namespace Umbraco.Core.Composing { private readonly List _types = new List(); private readonly object _locker = new object(); - private Func, TCollection> _collectionCtor; - private ServiceRegistration[] _registrations; - - /// - /// Initializes a new instance of the - /// class with a service container. - /// - /// A service container. - protected CollectionBuilderBase(IServiceContainer container) - { - Container = container; - // ReSharper disable once DoNotCallOverridableMethodsInConstructor - Initialize(); - } - - /// - /// Gets the service container. - /// - protected IServiceContainer Container { get; } + private Type[] _registeredTypes; /// /// Gets the internal list of types as an IEnumerable (immutable). /// public IEnumerable GetTypes() => _types; - /// - /// Initializes a new instance of the builder. - /// - /// This is called by the constructor and, by default, registers the - /// collection automatically. - protected virtual void Initialize() + /// + public virtual void RegisterWith(IRegister register) { - // compile the auto-collection constructor - var argType = typeof(IEnumerable); - var ctorArgTypes = new[] { argType }; - var constructor = typeof(TCollection).GetConstructor(ctorArgTypes); - if (constructor != null) - { - var exprArg = Expression.Parameter(argType, "items"); - var exprNew = Expression.New(constructor, exprArg); - var expr = Expression.Lambda, TCollection>>(exprNew, exprArg); - _collectionCtor = expr.Compile(); - } - // else _collectionCtor remains null, assuming CreateCollection has been overriden - - // we just don't want to support re-registering collections here - var registration = Container.GetAvailableService(); - if (registration != null) - throw new InvalidOperationException("Collection builders cannot be registered once the collection itself has been registered."); + if (_registeredTypes != null) + throw new InvalidOperationException("This builder has already been registered."); // register the collection - Container.Register(_ => CreateCollection(), CollectionLifetime); + register.Register(CreateCollection, CollectionLifetime); + + // register the types + RegisterTypes(register); } /// /// Gets the collection lifetime. /// - /// Return null for transient collections. - protected virtual ILifetime CollectionLifetime => new PerContainerLifetime(); + protected virtual Lifetime CollectionLifetime => Lifetime.Singleton; /// /// Configures the internal list of types. @@ -87,8 +50,8 @@ namespace Umbraco.Core.Composing { lock (_locker) { - if (_registrations != null) - throw new InvalidOperationException("Cannot configure a collection builder after its types have been resolved."); + if (_registeredTypes != null) + throw new InvalidOperationException("Cannot configure a collection builder after it has been registered."); action(_types); } } @@ -104,55 +67,54 @@ namespace Umbraco.Core.Composing return types; } - private void RegisterTypes() + private void RegisterTypes(IRegister register) { lock (_locker) { - if (_registrations != null) return; + if (_registeredTypes != null) return; var types = GetRegisteringTypes(_types).ToArray(); + + // ensure they are safe foreach (var type in types) EnsureType(type, "register"); - var prefix = GetType().FullName + "_"; - var i = 0; + // register them foreach (var type in types) - { - var name = $"{prefix}{i++:00000}"; - Container.Register(typeof(TItem), type, name); - } + register.Register(type); - _registrations = Container.AvailableServices - .Where(x => x.ServiceName.StartsWith(prefix)) - .OrderBy(x => x.ServiceName) - .ToArray(); + _registeredTypes = types; } } /// /// Creates the collection items. /// - /// The arguments. /// The collection items. - protected virtual IEnumerable CreateItems(params object[] args) + protected virtual IEnumerable CreateItems(IFactory factory) { - RegisterTypes(); // will do it only once + if (_registeredTypes == null) + throw new InvalidOperationException("Cannot create items before the collection builder has been registered."); - var type = typeof (TItem); - return _registrations - .Select(x => (TItem) Container.GetInstanceOrThrow(type, x.ServiceName, x.ImplementingType, args)) + return _registeredTypes // respect order + .Select(x => CreateItem(factory, x)) .ToArray(); // safe } + /// + /// Creates a collection item. + /// + protected virtual TItem CreateItem(IFactory factory, Type itemType) + => (TItem) factory.GetInstance(itemType); + /// /// Creates a collection. /// /// A collection. /// Creates a new collection each time it is invoked. - public virtual TCollection CreateCollection() + public virtual TCollection CreateCollection(IFactory factory) { - if (_collectionCtor == null) throw new InvalidOperationException("Collection auto-creation is not possible."); - return _collectionCtor(CreateItems()); + return factory.CreateInstance(CreateItems(factory)); } protected Type EnsureType(Type type, string action) diff --git a/src/Umbraco.Core/Composing/Composers/ConfigurationComposer.cs b/src/Umbraco.Core/Composing/Composers/ConfigurationComposer.cs new file mode 100644 index 0000000000..ca86f623cc --- /dev/null +++ b/src/Umbraco.Core/Composing/Composers/ConfigurationComposer.cs @@ -0,0 +1,25 @@ +using Umbraco.Core.Components; +using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.UmbracoSettings; + +namespace Umbraco.Core.Composing.Composers +{ + /// + /// Compose configurations. + /// + public static class ConfigurationComposer + { + public static Composition ComposeConfiguration(this Composition composition) + { + // common configurations are already registered + // register others + + composition.RegisterUnique(factory => factory.GetInstance().Content); + composition.RegisterUnique(factory => factory.GetInstance().Templates); + composition.RegisterUnique(factory => factory.GetInstance().RequestHandler); + composition.RegisterUnique(factory => factory.GetInstance().Security); + + return composition; + } + } +} diff --git a/src/Umbraco.Core/Composing/Composers/CoreMappingProfilesComposer.cs b/src/Umbraco.Core/Composing/Composers/CoreMappingProfilesComposer.cs new file mode 100644 index 0000000000..0274b8f1a9 --- /dev/null +++ b/src/Umbraco.Core/Composing/Composers/CoreMappingProfilesComposer.cs @@ -0,0 +1,16 @@ +using AutoMapper; +using Umbraco.Core.Components; +using Umbraco.Core.Models.Identity; + +namespace Umbraco.Core.Composing.Composers + +{ + public static class CoreMappingProfilesComposer + { + public static Composition ComposeCoreMappingProfiles(this Composition composition) + { + composition.Register(); + return composition; + } + } +} diff --git a/src/Umbraco.Core/Composing/Composers/FileSystemsComposer.cs b/src/Umbraco.Core/Composing/Composers/FileSystemsComposer.cs new file mode 100644 index 0000000000..f1fb095406 --- /dev/null +++ b/src/Umbraco.Core/Composing/Composers/FileSystemsComposer.cs @@ -0,0 +1,89 @@ +using Umbraco.Core.Components; +using Umbraco.Core.IO; +using Umbraco.Core.IO.MediaPathSchemes; + +namespace Umbraco.Core.Composing.Composers +{ + public static class FileSystemsComposer + { + /* + * HOW TO REPLACE THE MEDIA UNDERLYING FILESYSTEM + * ---------------------------------------------- + * + * Create a component and use it to modify the composition by adding something like: + * + * composition.Container.RegisterFileSystem( + * factory => new PhysicalFileSystem("~/somewhere")); + * + * return whatever supporting filesystem you like. + * + * + * HOW TO IMPLEMENT MY OWN FILESYSTEM + * ---------------------------------- + * + * Create your filesystem class: + * + * public class MyFileSystem : FileSystemWrapper + * { + * public MyFileSystem(IFileSystem innerFileSystem) + * : base(innerFileSystem) + * { } + * } + * + * The ctor can have more parameters that will be resolved by the container. + * + * Register your filesystem, in a component: + * + * composition.Container.RegisterFileSystem( + * factory => new PhysicalFileSystem("~/my")); + * + * And that's it, you can inject MyFileSystem wherever it's needed. + * + * + * You can also declare a filesystem interface: + * + * public interface IMyFileSystem : IFileSystem + * { } + * + * Make the class implement the interface, then + * register your filesystem, in a component: + * + * composition.Container.RegisterFileSystem( + * factory => new PhysicalFileSystem("~/my")); + * + * And that's it, you can inject IMyFileSystem wherever it's needed. + * + * + * WHAT IS SHADOWING + * ----------------- + * + * Shadowing is the technology used for Deploy to implement some sort of + * transaction-management on top of filesystems. The plumbing explained above, + * compared to creating your own physical filesystem, ensures that your filesystem + * would participate into such transactions. + * + * + */ + + public static Composition ComposeFileSystems(this Composition composition) + { + // register FileSystems, which manages all filesystems + // it needs to be registered (not only the interface) because it provides additional + // functionality eg for scoping, and is injected in the scope provider - whereas the + // interface is really for end-users to get access to filesystems. + composition.RegisterUnique(factory => factory.CreateInstance(factory)); + + // register IFileSystems, which gives access too all filesystems + composition.RegisterUnique(factory => factory.GetInstance()); + + // register the scheme for media paths + composition.RegisterUnique(); + + // register the IMediaFileSystem implementation with a supporting filesystem + composition.RegisterFileSystem( + factory => new PhysicalFileSystem("~/media")); + + return composition; + } + } +} diff --git a/src/Umbraco.Core/Composing/Composers/RepositoriesComposer.cs b/src/Umbraco.Core/Composing/Composers/RepositoriesComposer.cs new file mode 100644 index 0000000000..62b92081c1 --- /dev/null +++ b/src/Umbraco.Core/Composing/Composers/RepositoriesComposer.cs @@ -0,0 +1,54 @@ +using Umbraco.Core.Components; +using Umbraco.Core.Persistence.Repositories; +using Umbraco.Core.Persistence.Repositories.Implement; + +namespace Umbraco.Core.Composing.Composers +{ + /// + /// Composes repositories. + /// + public static class RepositoriesComposer + { + public static Composition ComposeRepositories(this Composition composition) + { + // repositories + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + + return composition; + } + } +} diff --git a/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs b/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs new file mode 100644 index 0000000000..1b77aaa7d6 --- /dev/null +++ b/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs @@ -0,0 +1,96 @@ +using System; +using System.IO; +using System.Linq; +using Umbraco.Core.Cache; +using Umbraco.Core.Components; +using Umbraco.Core.Events; +using Umbraco.Core.IO; +using Umbraco.Core.Logging; +using Umbraco.Core.Services; +using Umbraco.Core.Services.Implement; + +namespace Umbraco.Core.Composing.Composers +{ + public static class ServicesComposer + { + public static Composition ComposeServices(this Composition composition) + { + // register a transient messages factory, which will be replaced by the web + // boot manager when running in a web context + composition.RegisterUnique(); + + // register the service context + composition.RegisterUnique(); + + // register the special idk map + composition.RegisterUnique(); + + // register the services + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.Register(SourcesFactory); + composition.RegisterUnique(factory => new LocalizedTextService( + factory.GetInstance>(), + factory.GetInstance())); + + //TODO: These are replaced in the web project - we need to declare them so that + // something is wired up, just not sure this is very nice but will work for now. + composition.RegisterUnique(); + composition.RegisterUnique(); + + return composition; + } + + private static LocalizedTextServiceFileSources SourcesFactory(IFactory container) + { + var mainLangFolder = new DirectoryInfo(IOHelper.MapPath(SystemDirectories.Umbraco + "/config/lang/")); + var appPlugins = new DirectoryInfo(IOHelper.MapPath(SystemDirectories.AppPlugins)); + var configLangFolder = new DirectoryInfo(IOHelper.MapPath(SystemDirectories.Config + "/lang/")); + + var pluginLangFolders = appPlugins.Exists == false + ? Enumerable.Empty() + : appPlugins.GetDirectories() + .SelectMany(x => x.GetDirectories("Lang")) + .SelectMany(x => x.GetFiles("*.xml", SearchOption.TopDirectoryOnly)) + .Where(x => Path.GetFileNameWithoutExtension(x.FullName).Length == 5) + .Select(x => new LocalizedTextServiceSupplementaryFileSource(x, false)); + + //user defined langs that overwrite the default, these should not be used by plugin creators + var userLangFolders = configLangFolder.Exists == false + ? Enumerable.Empty() + : configLangFolder + .GetFiles("*.user.xml", SearchOption.TopDirectoryOnly) + .Where(x => Path.GetFileNameWithoutExtension(x.FullName).Length == 10) + .Select(x => new LocalizedTextServiceSupplementaryFileSource(x, true)); + + return new LocalizedTextServiceFileSources( + container.GetInstance(), + container.GetInstance().RuntimeCache, + mainLangFolder, + pluginLangFolders.Concat(userLangFolders)); + } + } +} diff --git a/src/Umbraco.Core/Composing/CompositionExtensions.cs b/src/Umbraco.Core/Composing/CompositionExtensions.cs new file mode 100644 index 0000000000..cfc465b59d --- /dev/null +++ b/src/Umbraco.Core/Composing/CompositionExtensions.cs @@ -0,0 +1,62 @@ +using Umbraco.Core.Cache; +using Umbraco.Core.Components; +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence; + +namespace Umbraco.Core.Composing +{ + /// + /// Provides extension methods to the class. + /// + public static class CompositionExtensions + { + #region Essentials + + /// + /// Registers essential services. + /// + public static void RegisterEssentials(this Composition composition, + ILogger logger, IProfiler profiler, IProfilingLogger profilingLogger, + IMainDom mainDom, + CacheHelper appCaches, + IUmbracoDatabaseFactory databaseFactory, + TypeLoader typeLoader, + IRuntimeState state) + { + composition.RegisterUnique(logger); + composition.RegisterUnique(profiler); + composition.RegisterUnique(profilingLogger); + composition.RegisterUnique(mainDom); + composition.RegisterUnique(appCaches); + composition.RegisterUnique(factory => factory.GetInstance().RuntimeCache); + composition.RegisterUnique(databaseFactory); + composition.RegisterUnique(factory => factory.GetInstance().SqlContext); + composition.RegisterUnique(typeLoader); + composition.RegisterUnique(state); + } + + #endregion + + #region Unique + + /// + /// Registers a unique service as its own implementation. + /// + public static void RegisterUnique(this Composition composition) + => composition.RegisterUnique(typeof(TService), typeof(TService)); + + /// + /// Registers a unique service with an implementation type. + /// + public static void RegisterUnique(this Composition composition) + => composition.RegisterUnique(typeof(TService), typeof(TImplementing)); + + /// + /// Registers a unique service with an implementing instance. + /// + public static void RegisterUnique(this Composition composition, TService instance) + => composition.RegisterUnique(typeof(TService), instance); + + #endregion + } +} diff --git a/src/Umbraco.Core/Composing/CompositionRoots/ConfigurationCompositionRoot.cs b/src/Umbraco.Core/Composing/CompositionRoots/ConfigurationCompositionRoot.cs deleted file mode 100644 index 80de6d1c35..0000000000 --- a/src/Umbraco.Core/Composing/CompositionRoots/ConfigurationCompositionRoot.cs +++ /dev/null @@ -1,24 +0,0 @@ -using LightInject; -using Umbraco.Core.Configuration; -using Umbraco.Core.Configuration.UmbracoSettings; - -namespace Umbraco.Core.Composing.CompositionRoots -{ - /// - /// Sets up IoC container for Umbraco configuration classes - /// - public sealed class ConfigurationCompositionRoot : ICompositionRoot - { - public void Compose(IServiceRegistry container) - { - container.Register(factory => UmbracoConfig.For.UmbracoSettings()); - container.Register(factory => factory.GetInstance().Content); - container.Register(factory => factory.GetInstance().Templates); - container.Register(factory => factory.GetInstance().RequestHandler); - container.Register(factory => UmbracoConfig.For.GlobalSettings()); - container.Register(factory => UmbracoConfig.For.DashboardSettings()); - - // fixme - other sections we need to add? - } - } -} diff --git a/src/Umbraco.Core/Composing/CompositionRoots/CoreMappingProfilesCompositionRoot.cs b/src/Umbraco.Core/Composing/CompositionRoots/CoreMappingProfilesCompositionRoot.cs deleted file mode 100644 index 6b55a4af7e..0000000000 --- a/src/Umbraco.Core/Composing/CompositionRoots/CoreMappingProfilesCompositionRoot.cs +++ /dev/null @@ -1,13 +0,0 @@ -using LightInject; -using Umbraco.Core.Models.Identity; - -namespace Umbraco.Core.Composing.CompositionRoots -{ - public sealed class CoreMappingProfilesCompositionRoot : ICompositionRoot - { - public void Compose(IServiceRegistry container) - { - container.Register(); - } - } -} diff --git a/src/Umbraco.Core/Composing/CompositionRoots/RepositoryCompositionRoot.cs b/src/Umbraco.Core/Composing/CompositionRoots/RepositoryCompositionRoot.cs deleted file mode 100644 index 9c36bf5cec..0000000000 --- a/src/Umbraco.Core/Composing/CompositionRoots/RepositoryCompositionRoot.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System; -using LightInject; -using Umbraco.Core.Cache; -using Umbraco.Core.Persistence.Repositories; -using Umbraco.Core.Persistence.Repositories.Implement; - -namespace Umbraco.Core.Composing.CompositionRoots -{ - /// - /// Sets the IoC container for the umbraco data layer/repositories/sql/database/etc... - /// - public sealed class RepositoryCompositionRoot : ICompositionRoot - { - public const string DisabledCache = "DisabledCache"; - - public void Compose(IServiceRegistry container) - { - // register cache helpers - // the main cache helper is registered by CoreBootManager and is used by most repositories - // the disabled one is used by those repositories that have an annotated ctor parameter - container.RegisterSingleton(factory => CacheHelper.CreateDisabledCacheHelper(), DisabledCache); - - // resolve ctor dependency from GetInstance() runtimeArguments, if possible - 'factory' is - // the container, 'info' describes the ctor argument, and 'args' contains the args that - // were passed to GetInstance() - use first arg if it is the right type, - // - // for ... - //container.RegisterConstructorDependency((factory, info, args) => - //{ - // if (info.Member.DeclaringType != typeof(EntityContainerRepository)) return default; - // return args.Length > 0 && args[0] is Guid guid ? guid : default; - //}); - - // register repositories - // repos depend on various things, - // some repositories have an annotated ctor parameter to pick the right cache helper - - // repositories - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - - // repositories that depend on a filesystem - // these have an annotated ctor parameter to pick the right file system - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - } - } -} diff --git a/src/Umbraco.Core/Composing/CompositionRoots/ServicesCompositionRoot.cs b/src/Umbraco.Core/Composing/CompositionRoots/ServicesCompositionRoot.cs deleted file mode 100644 index 92b8139a04..0000000000 --- a/src/Umbraco.Core/Composing/CompositionRoots/ServicesCompositionRoot.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using LightInject; -using Umbraco.Core.Cache; -using Umbraco.Core.Events; -using Umbraco.Core.IO; -using Umbraco.Core.Logging; -using Umbraco.Core.Services; -using Umbraco.Core.Services.Implement; - -namespace Umbraco.Core.Composing.CompositionRoots -{ - public sealed class ServicesCompositionRoot : ICompositionRoot - { - public void Compose(IServiceRegistry container) - { - // register a transient messages factory, which will be replaced by the web - // boot manager when running in a web context - container.RegisterSingleton(); - - // register the service context - container.RegisterSingleton(); - - // register the special idk map - container.RegisterSingleton(); - - // register the services - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.Register(factory => - { - var mainLangFolder = new DirectoryInfo(IOHelper.MapPath(SystemDirectories.Umbraco + "/config/lang/")); - var appPlugins = new DirectoryInfo(IOHelper.MapPath(SystemDirectories.AppPlugins)); - var configLangFolder = new DirectoryInfo(IOHelper.MapPath(SystemDirectories.Config + "/lang/")); - - var pluginLangFolders = appPlugins.Exists == false - ? Enumerable.Empty() - : appPlugins.GetDirectories() - .SelectMany(x => x.GetDirectories("Lang")) - .SelectMany(x => x.GetFiles("*.xml", SearchOption.TopDirectoryOnly)) - .Where(x => Path.GetFileNameWithoutExtension(x.FullName).Length == 5) - .Select(x => new LocalizedTextServiceSupplementaryFileSource(x, false)); - - //user defined langs that overwrite the default, these should not be used by plugin creators - var userLangFolders = configLangFolder.Exists == false - ? Enumerable.Empty() - : configLangFolder - .GetFiles("*.user.xml", SearchOption.TopDirectoryOnly) - .Where(x => Path.GetFileNameWithoutExtension(x.FullName).Length == 10) - .Select(x => new LocalizedTextServiceSupplementaryFileSource(x, true)); - - return new LocalizedTextServiceFileSources( - factory.GetInstance(), - factory.GetInstance().RuntimeCache, - mainLangFolder, - pluginLangFolders.Concat(userLangFolders)); - }); - container.RegisterSingleton(factory => new LocalizedTextService( - factory.GetInstance>(), - factory.GetInstance())); - - //TODO: These are replaced in the web project - we need to declare them so that - // something is wired up, just not sure this is very nice but will work for now. - container.RegisterSingleton(); - container.RegisterSingleton(); - } - } -} diff --git a/src/Umbraco.Core/Composing/Current.cs b/src/Umbraco.Core/Composing/Current.cs index 9515398236..cf67409925 100644 --- a/src/Umbraco.Core/Composing/Current.cs +++ b/src/Umbraco.Core/Composing/Current.cs @@ -1,5 +1,4 @@ using System; -using LightInject; using Umbraco.Core.Cache; using Umbraco.Core.Configuration; using Umbraco.Core.Dictionary; @@ -20,47 +19,61 @@ namespace Umbraco.Core.Composing /// Provides a static service locator for most singletons. /// /// - /// This class is initialized with the container via LightInjectExtensions.ConfigureUmbracoCore, + /// This class is initialized with the container in UmbracoApplicationBase, /// right after the container is created in UmbracoApplicationBase.HandleApplicationStart. /// Obviously, this is a service locator, which some may consider an anti-pattern. And yet, /// practically, it works. /// public static class Current { - private static IServiceContainer _container; + private static IFactory _factory; + + // fixme - refactor + // we don't want Umbraco tests to die because the container has not been properly initialized, + // for some too-important things such as IShortStringHelper or loggers, so if it's not + // registered we setup a default one. We should really refactor our tests so that it does + // not happen. private static IShortStringHelper _shortStringHelper; private static ILogger _logger; private static IProfiler _profiler; - private static ProfilingLogger _profilingLogger; + private static IProfilingLogger _profilingLogger; private static IPublishedValueFallback _publishedValueFallback; + private static Configs _configs; /// - /// Gets or sets the DI container. + /// Gets or sets the factory. /// - public static IServiceContainer Container + public static IFactory Factory { get { - if (_container == null) throw new Exception("No container has been set."); - return _container; + if (_factory == null) throw new InvalidOperationException("No factory has been set."); + return _factory; } set { - if (_container != null) throw new Exception("A container has already been set."); - _container = value; + if (_factory != null) throw new InvalidOperationException("A factory has already been set."); + if (_configs != null) throw new InvalidOperationException("Configs are unlocked."); + _factory = value; } } - internal static bool HasContainer => _container != null; + internal static bool HasFactory => _factory != null; - // for UNIT TESTS exclusively! - // resets *everything* that is 'current' - internal static void Reset() + /// + /// Resets . Indented for testing only, and not supported in production code. + /// + /// + /// For UNIT TESTS exclusively. + /// Resets everything that is 'current'. + /// + public static void Reset() { - _container?.Dispose(); - _container = null; + _factory.DisposeIfDisposable(); + _factory = null; + _configs = null; _shortStringHelper = null; _logger = null; _profiler = null; @@ -70,97 +83,119 @@ namespace Umbraco.Core.Composing Resetted?.Invoke(null, EventArgs.Empty); } + /// + /// Unlocks . Intended for testing only, and not supported in production code. + /// + /// + /// For UNIT TESTS exclusively. + /// Unlocks so that it is possible to add configurations + /// directly to without having to wire composition. + /// + public static void UnlockConfigs() + { + if (_factory != null) + throw new InvalidOperationException("Cannot unlock configs when a factory has been set."); + _configs = new Configs(); + } + internal static event EventHandler Resetted; #region Getters - // fixme - refactor - // we don't want Umbraco to die because the container has not been properly initialized, - // for some too-important things such as IShortStringHelper or loggers, so if it's not - // registered we setup a default one. We should really refactor our tests so that it does - // not happen. Will do when we get rid of IShortStringHelper. - public static IShortStringHelper ShortStringHelper - => _shortStringHelper ?? (_shortStringHelper = _container?.TryGetInstance() - ?? new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(UmbracoConfig.For.UmbracoSettings()))); + => _shortStringHelper ?? (_shortStringHelper = _factory?.TryGetInstance() + ?? new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(Configs.Settings()))); public static ILogger Logger - => _logger ?? (_logger = _container?.TryGetInstance() - ?? new DebugDiagnosticsLogger()); + => _logger ?? (_logger = _factory?.TryGetInstance() + ?? new DebugDiagnosticsLogger()); public static IProfiler Profiler - => _profiler ?? (_profiler = _container?.TryGetInstance() - ?? new LogProfiler(Logger)); + => _profiler ?? (_profiler = _factory?.TryGetInstance() + ?? new LogProfiler(Logger)); - public static ProfilingLogger ProfilingLogger - => _profilingLogger ?? (_profilingLogger = _container?.TryGetInstance()) + public static IProfilingLogger ProfilingLogger + => _profilingLogger ?? (_profilingLogger = _factory?.TryGetInstance()) ?? new ProfilingLogger(Logger, Profiler); public static IRuntimeState RuntimeState - => Container.GetInstance(); + => Factory.GetInstance(); public static TypeLoader TypeLoader - => Container.GetInstance(); + => Factory.GetInstance(); - public static FileSystems FileSystems - => Container.GetInstance(); + public static Configs Configs + { + get + { + if (_configs != null) return _configs; + if (_factory == null) throw new InvalidOperationException("Can not get Current.Config during composition. Use composition.Config."); + return _factory.GetInstance(); + } + } + + public static IFileSystems FileSystems + => Factory.GetInstance(); + + public static IMediaFileSystem MediaFileSystem + => Factory.GetInstance(); public static UrlSegmentProviderCollection UrlSegmentProviders - => Container.GetInstance(); + => Factory.GetInstance(); public static CacheRefresherCollection CacheRefreshers - => Container.GetInstance(); + => Factory.GetInstance(); public static DataEditorCollection DataEditors - => Container.GetInstance(); + => Factory.GetInstance(); public static PropertyEditorCollection PropertyEditors - => Container.GetInstance(); + => Factory.GetInstance(); public static ParameterEditorCollection ParameterEditors - => Container.GetInstance(); + => Factory.GetInstance(); internal static ManifestValueValidatorCollection ManifestValidators - => Container.GetInstance(); + => Factory.GetInstance(); internal static PackageActionCollection PackageActions - => Container.GetInstance(); + => Factory.GetInstance(); internal static PropertyValueConverterCollection PropertyValueConverters - => Container.GetInstance(); + => Factory.GetInstance(); internal static IPublishedModelFactory PublishedModelFactory - => Container.GetInstance(); + => Factory.GetInstance(); public static IServerMessenger ServerMessenger - => Container.GetInstance(); + => Factory.GetInstance(); public static IServerRegistrar ServerRegistrar - => Container.GetInstance(); + => Factory.GetInstance(); public static ICultureDictionaryFactory CultureDictionaryFactory - => Container.GetInstance(); + => Factory.GetInstance(); public static CacheHelper ApplicationCache - => Container.GetInstance(); + => Factory.GetInstance(); public static ServiceContext Services - => Container.GetInstance(); + => Factory.GetInstance(); public static IScopeProvider ScopeProvider - => Container.GetInstance(); + => Factory.GetInstance(); public static ISqlContext SqlContext - => Container.GetInstance(); + => Factory.GetInstance(); public static IPublishedContentTypeFactory PublishedContentTypeFactory - => Container.GetInstance(); + => Factory.GetInstance(); public static IPublishedValueFallback PublishedValueFallback - => _publishedValueFallback ?? Container.GetInstance() ?? new NoopPublishedValueFallback(); + => _publishedValueFallback ?? Factory.GetInstance() ?? new NoopPublishedValueFallback(); public static IVariationContextAccessor VariationContextAccessor - => Container.GetInstance(); + => Factory.GetInstance(); #endregion } diff --git a/src/Umbraco.Core/Composing/FactoryExtensions.cs b/src/Umbraco.Core/Composing/FactoryExtensions.cs new file mode 100644 index 0000000000..2640b7f7e6 --- /dev/null +++ b/src/Umbraco.Core/Composing/FactoryExtensions.cs @@ -0,0 +1,87 @@ +using System; +using System.Linq; +using System.Reflection; + +namespace Umbraco.Core.Composing +{ + /// + /// Provides extension methods to the class. + /// + public static class FactoryExtensions + { + /// + /// Gets an instance of a service. + /// + /// The type of the service. + /// The factory. + /// An instance of the specified type. + /// Throws an exception if the factory failed to get an instance of the specified type. + public static T GetInstance(this IFactory factory) + => (T)factory.GetInstance(typeof(T)); + + /// + /// Tries to get an instance of a service. + /// + /// The type of the service. + /// An instance of the specified type, or null. + /// Returns null if the factory does not know how to get an instance + /// of the specified type. Throws an exception if the factory does know how + /// to get an instance of the specified type, but failed to do so. + public static T TryGetInstance(this IFactory factory) + => (T)factory.TryGetInstance(typeof(T)); + + /// + /// Creates an instance with arguments. + /// + /// The type of the instance. + /// The factory. + /// Arguments. + /// An instance of the specified type. + /// + /// Throws an exception if the factory failed to get an instance of the specified type. + /// The arguments are used as dependencies by the factory. + /// + public static T CreateInstance(this IFactory factory, params object[] args) + => (T)factory.CreateInstance(typeof(T), args); + + /// + /// Creates an instance of a service, with arguments. + /// + /// + /// The type of the instance. + /// Named arguments. + /// An instance of the specified type. + /// + /// The instance type does not need to be registered into the factory. + /// The arguments are used as dependencies by the factory. Other dependencies + /// are retrieved from the factory. + /// + public static object CreateInstance(this IFactory factory, Type type, params object[] args) + { + // LightInject has this, but then it requires RegisterConstructorDependency etc and has various oddities + // including the most annoying one, which is that it does not work on singletons (hard to fix) + //return factory.GetInstance(type, args); + + // this method is essentially used to build singleton instances, so it is assumed that it would be + // more expensive to build and cache a dynamic method ctor than to simply invoke the ctor, as we do + // here - this can be discussed + + // TODO: we currently try the ctor with most parameters, but we could want to fall back to others + + var ctor = type.GetConstructors(BindingFlags.Instance | BindingFlags.Public).OrderByDescending(x => x.GetParameters().Length).FirstOrDefault(); + if (ctor == null) throw new InvalidOperationException($"Could not find a public constructor for type {type.FullName}."); + + var ctorParameters = ctor.GetParameters(); + var ctorArgs = new object[ctorParameters.Length]; + var i = 0; + foreach (var parameter in ctorParameters) + { + // no! IsInstanceOfType is not ok here + // ReSharper disable once UseMethodIsInstanceOfType + var arg = args?.FirstOrDefault(a => parameter.ParameterType.IsAssignableFrom(a.GetType())); + ctorArgs[i++] = arg ?? factory.GetInstance(parameter.ParameterType); + } + return ctor.Invoke(ctorArgs); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Composing/ICollectionBuilder.cs b/src/Umbraco.Core/Composing/ICollectionBuilder.cs index 5efc03c9ac..84ff3ba747 100644 --- a/src/Umbraco.Core/Composing/ICollectionBuilder.cs +++ b/src/Umbraco.Core/Composing/ICollectionBuilder.cs @@ -1,11 +1,23 @@ namespace Umbraco.Core.Composing { + /// + /// Represents a collection builder. + /// + public interface ICollectionBuilder + { + /// + /// Registers the builder so it can build the collection, by + /// registering the collection and the types. + /// + void RegisterWith(IRegister register); + } + /// /// Represents a collection builder. /// /// The type of the collection. /// The type of the items. - public interface ICollectionBuilder + public interface ICollectionBuilder : ICollectionBuilder where TCollection : IBuilderCollection { /// @@ -13,6 +25,6 @@ /// /// A collection. /// Creates a new collection each time it is invoked. - TCollection CreateCollection(); + TCollection CreateCollection(IFactory factory); } } diff --git a/src/Umbraco.Core/Composing/IFactory.cs b/src/Umbraco.Core/Composing/IFactory.cs new file mode 100644 index 0000000000..9a59b1c052 --- /dev/null +++ b/src/Umbraco.Core/Composing/IFactory.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; + +namespace Umbraco.Core.Composing +{ + // Implementing: + // + // The factory + // - always picks the constructor with the most parameters + // - supports Lazy parameters (and prefers them over non-Lazy) in constructors + // - what happens with 'releasing' is unclear + + /// + /// Defines a service factory for Umbraco. + /// + public interface IFactory + { + /// + /// Gets the concrete factory. + /// + object Concrete { get; } + + /// + /// Gets an instance of a service. + /// + /// The type of the service. + /// An instance of the specified type. + /// Throws an exception if the container failed to get an instance of the specified type. + object GetInstance(Type type); + + /// + /// Tries to get an instance of a service. + /// + /// The type of the service. + /// An instance of the specified type, or null. + /// Returns null if the container does not know how to get an instance + /// of the specified type. Throws an exception if the container does know how + /// to get an instance of the specified type, but failed to do so. + object TryGetInstance(Type type); + + /// + /// Gets all instances of a service. + /// + /// The type of the service. + IEnumerable GetAllInstances(Type serviceType); + + /// + /// Gets all instances of a service. + /// + /// The type of the service. + IEnumerable GetAllInstances(); + + /// + /// Releases an instance. + /// + /// The instance. + /// + /// See https://stackoverflow.com/questions/14072208 and http://kozmic.net/2010/08/27/must-i-release-everything-when-using-windsor/, + /// you only need to release instances you specifically resolved, and even then, if done right, that might never be needed. For + /// instance, LightInject does not require this and does not support it - should work with scopes. + /// + void Release(object instance); + + /// + /// Begins a scope. + /// + /// + /// When the scope is disposed, scoped instances that have been created during the scope are disposed. + /// Scopes can be nested. Each instance is disposed individually. + /// + IDisposable BeginScope(); + + /// + /// Enables per-request scope. + /// + /// + /// Ties scopes to web requests. + /// + void EnablePerWebRequestScope(); + } +} diff --git a/src/Umbraco.Core/Composing/IRegister.cs b/src/Umbraco.Core/Composing/IRegister.cs new file mode 100644 index 0000000000..8ad3db5409 --- /dev/null +++ b/src/Umbraco.Core/Composing/IRegister.cs @@ -0,0 +1,76 @@ +using System; + +namespace Umbraco.Core.Composing +{ + // Implementing: + // + // The register + // - supports registering a service, even after some instances of other services have been created + // - supports re-registering a service, as long as no instance of that service has been created + // - throws when re-registering a service, and an instance of that service has been created + // + // - registers only one implementation of a nameless service, re-registering replaces the previous + // registration - names are required to register multiple implementations - and getting an + // IEnumerable of the service, nameless, returns them all + + /// + /// Defines a service register for Umbraco. + /// + public interface IRegister + { + /// + /// Gets the concrete container. + /// + object Concrete { get; } + + /// + /// Registers a service as its own implementation. + /// + void Register(Type serviceType, Lifetime lifetime = Lifetime.Transient); + + /// + /// Registers a service with an implementation type. + /// + void Register(Type serviceType, Type implementingType, Lifetime lifetime = Lifetime.Transient); + + /// + /// Registers a service with an implementation factory. + /// + void Register(Func factory, Lifetime lifetime = Lifetime.Transient); + + /// + /// Registers a service with an implementing instance. + /// + void RegisterInstance(Type serviceType, object instance); + + /// + /// Registers a base type for auto-registration. + /// + /// + /// Auto-registration means that anytime the container is asked to create an instance + /// of a type deriving from , it will first register that + /// type automatically. + /// This can be used for instance for views or controllers. Then, one just needs to + /// register a common base class or interface, and the container knows how to create instances. + /// + void RegisterAuto(Type serviceBaseType); + + #region Control + + /// + /// Configures the container for web support. + /// + /// + /// Enables support for MVC, WebAPI, but *not* per-request scope. This is used early in the boot + /// process, where anything "scoped" should not be linked to a web request. + /// + void ConfigureForWeb(); + + /// + /// Creates the factory. + /// + IFactory CreateFactory(); + + #endregion + } +} diff --git a/src/Umbraco.Core/Composing/LazyCollectionBuilderBase.cs b/src/Umbraco.Core/Composing/LazyCollectionBuilderBase.cs index ee263f458f..a1a06621e9 100644 --- a/src/Umbraco.Core/Composing/LazyCollectionBuilderBase.cs +++ b/src/Umbraco.Core/Composing/LazyCollectionBuilderBase.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using LightInject; namespace Umbraco.Core.Composing { @@ -15,31 +14,22 @@ namespace Umbraco.Core.Composing where TBuilder : LazyCollectionBuilderBase where TCollection : IBuilderCollection { - private readonly List>> _producers1 = new List>>(); - private readonly List>> _producers2 = new List>>(); + private readonly List>> _producers = new List>>(); private readonly List _excluded = new List(); - /// - /// Initializes a new instance of the class. - /// - protected LazyCollectionBuilderBase(IServiceContainer container) - : base(container) - { } - protected abstract TBuilder This { get; } /// /// Clears all types in the collection. /// - /// The buidler. + /// The builder. public TBuilder Clear() { Configure(types => { types.Clear(); - _producers1.Clear(); - _producers2.Clear(); - _excluded.Clear(); + _producers.Clear(); + _excluded.Clear(); }); return This; } @@ -84,21 +74,7 @@ namespace Umbraco.Core.Composing { Configure(types => { - _producers1.Add(producer); - }); - return This; - } - - /// - /// Adds a types producer to the collection. - /// - /// The types producer. - /// The builder. - public TBuilder Add(Func> producer) - { - Configure(types => - { - _producers2.Add(producer); + _producers.Add(producer); }); return This; } @@ -137,8 +113,7 @@ namespace Umbraco.Core.Composing protected override IEnumerable GetRegisteringTypes(IEnumerable types) { return types - .Union(_producers1.SelectMany(x => x())) - .Union(_producers2.SelectMany(x => x(Container))) + .Union(_producers.SelectMany(x => x())) .Distinct() .Select(x => EnsureType(x, "register")) .Except(_excluded); diff --git a/src/Umbraco.Core/Composing/Lifetime.cs b/src/Umbraco.Core/Composing/Lifetime.cs new file mode 100644 index 0000000000..e1b9950c39 --- /dev/null +++ b/src/Umbraco.Core/Composing/Lifetime.cs @@ -0,0 +1,49 @@ +namespace Umbraco.Core.Composing +{ + /// + /// Specifies the lifetime of a registered instance. + /// + public enum Lifetime + { + /// + /// Always get a new instance. + /// + /// Corresponds to Transient in LightInject, Castle Windsor + /// or MS.DI, PerDependency in Autofac. + Transient, + + /// + /// One unique instance per request. + /// + // fixme - not what you think! + // currently, corresponds to 'Request' in LightInject which is 'Transient + disposed by Scope' + // but NOT (in LightInject) a per-web-request lifetime, more a TransientScoped + // + // we use it for controllers, httpContextBase and umbracoContext + // - so that they are automatically disposed at the end of the scope (ie request) + // - not sure they should not be simply 'scoped'? + // + // Castle has an extra PerWebRequest something, and others use scope + // what about Request before first request ie during application startup? + // see http://blog.ploeh.dk/2009/11/17/UsingCastleWindsor'sPerWebRequestlifestylewithASP.NETMVConIIS7/ + // Castle ends up requiring a special scope manager too + // see https://groups.google.com/forum/#!topic/castle-project-users/1E2W9LVIYR4 + // + // but maybe also - why are we requiring scoped services at startup? + Request, + + /// + /// One unique instance per container scope. + /// + /// Corresponds to Scope in LightInject, Scoped in MS.DI + /// or Castle Windsor, PerLifetimeScope in Autofac. + Scope, + + /// + /// One unique instance per container. + /// + /// Corresponds to Singleton in LightInject, Castle Windsor + /// or MS.DI and to SingleInstance in Autofac. + Singleton + } +} diff --git a/src/Umbraco.Core/Composing/LightInject/LightInjectContainer.cs b/src/Umbraco.Core/Composing/LightInject/LightInjectContainer.cs new file mode 100644 index 0000000000..b39622f66a --- /dev/null +++ b/src/Umbraco.Core/Composing/LightInject/LightInjectContainer.cs @@ -0,0 +1,279 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Threading; +using LightInject; + +namespace Umbraco.Core.Composing.LightInject +{ + /// + /// Implements DI with LightInject. + /// + public class LightInjectContainer : IRegister, IFactory, IDisposable + { + private int _disposed; + + /// + /// Initializes a new instance of the with a LightInject container. + /// + protected LightInjectContainer(ServiceContainer container) + { + Container = container; + } + + /// + /// Creates a new instance of the class. + /// + public static LightInjectContainer Create() + => new LightInjectContainer(CreateServiceContainer()); + + /// + /// Creates a new instance of the LightInject service container. + /// + protected static ServiceContainer CreateServiceContainer() + { + var container = new ServiceContainer(new ContainerOptions { EnablePropertyInjection = false }); + + // note: the block below is disabled, as it is too LightInject-specific + // + // supports annotated constructor injections + // eg to specify the service name on some services + //container.EnableAnnotatedConstructorInjection(); + + // note: the block below is disabled, we do not allow property injection at all anymore + // (see options in CreateServiceContainer) + // + // from the docs: "LightInject considers all read/write properties a dependency, but implements + // a loose strategy around property dependencies, meaning that it will NOT throw an exception + // in the case of an unresolved property dependency." + // + // in Umbraco we do NOT want to do property injection by default, so we have to disable it. + // from the docs, the following line will cause the container to "now only try to inject + // dependencies for properties that is annotated with the InjectAttribute." + // + // could not find it documented, but tests & code review shows that LightInject considers a + // property to be "injectable" when its setter exists and is not static, nor private, nor + // it is an index property. which means that eg protected or internal setters are OK. + //Container.EnableAnnotatedPropertyInjection(); + + // ensure that we do *not* scan assemblies + // we explicitly RegisterFrom our own composition roots and don't want them scanned + container.AssemblyScanner = new AssemblyScanner(/*container.AssemblyScanner*/); + + // see notes in MixedLightInjectScopeManagerProvider + container.ScopeManagerProvider = new MixedLightInjectScopeManagerProvider(); + + // note: the block below is disabled, because it does not work, because collection builders + // are singletons, and constructor dependencies don't work on singletons, see + // https://github.com/seesharper/LightInject/issues/294 + // + // if looking for a IContainer, and one was passed in args, use it + // this is for collection builders which require the IContainer + //container.RegisterConstructorDependency((c, i, a) => a.OfType().FirstOrDefault()); + // + // and, the block below is also disabled, because it is ugly + // + //// which means that the only way to inject the container into builders is to register it + //container.RegisterInstance(this); + // + // instead, we use an explicit GetInstance with arguments implementation + + return container; + } + + /// + /// Gets the LightInject container. + /// + protected ServiceContainer Container { get; } + + /// + /// + public object Concrete => Container; + + /// + public void Dispose() + { + if (Interlocked.Exchange(ref _disposed, 1) == 1) + return; + + Container.Dispose(); + } + + /// + public IFactory CreateFactory() => this; + + #region Factory + + /// + public object GetInstance(Type type) + => Container.GetInstance(type); + + /// + public object TryGetInstance(Type type) + => Container.TryGetInstance(type); + + /// + public IEnumerable GetAllInstances() + => Container.GetAllInstances(); + + /// + public IEnumerable GetAllInstances(Type type) + => Container.GetAllInstances(type); + + /// + public void Release(object instance) + { + // nothing to release with LightInject + } + + // notes: + // we may want to look into MS code, eg: + // TypeActivatorCache in MVC at https://github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNetCore.Mvc.Core/Internal/TypeActivatorCache.cs + // which relies onto + // ActivatorUtilities at https://github.com/aspnet/DependencyInjection/blob/master/shared/Microsoft.Extensions.ActivatorUtilities.Sources/ActivatorUtilities.cs + + #endregion + + #region Registry + + /// + public void Register(Type serviceType, Lifetime lifetime = Lifetime.Transient) + { + switch (lifetime) + { + case Lifetime.Transient: + Container.Register(serviceType); + break; + case Lifetime.Request: + case Lifetime.Scope: + case Lifetime.Singleton: + Container.Register(serviceType, GetLifetime(lifetime)); + break; + default: + throw new NotSupportedException($"Lifetime {lifetime} is not supported."); + } + } + + /// + public void Register(Type serviceType, Type implementingType, Lifetime lifetime = Lifetime.Transient) + { + switch (lifetime) + { + case Lifetime.Transient: + Container.Register(serviceType, implementingType, implementingType.Name); + break; + case Lifetime.Request: + case Lifetime.Scope: + case Lifetime.Singleton: + Container.Register(serviceType, implementingType, GetLifetime(lifetime)); + break; + default: + throw new NotSupportedException($"Lifetime {lifetime} is not supported."); + } + } + + /// + public void Register(Func factory, Lifetime lifetime = Lifetime.Transient) + { + switch (lifetime) + { + case Lifetime.Transient: + Container.Register(f => factory(this)); + break; + case Lifetime.Request: + case Lifetime.Scope: + case Lifetime.Singleton: + Container.Register(f => factory(this), GetLifetime(lifetime)); + break; + default: + throw new NotSupportedException($"Lifetime {lifetime} is not supported."); + } + } + + private ILifetime GetLifetime(Lifetime lifetime) + { + switch (lifetime) + { + case Lifetime.Transient: + return null; + case Lifetime.Request: + return new PerRequestLifeTime(); + case Lifetime.Scope: + return new PerScopeLifetime(); + case Lifetime.Singleton: + return new PerContainerLifetime(); + default: + throw new NotSupportedException($"Lifetime {lifetime} is not supported."); + } + } + + /// + public void RegisterInstance(Type serviceType, object instance) + => Container.RegisterInstance(serviceType, instance); + + /// + public void RegisterAuto(Type serviceBaseType) + { + Container.RegisterFallback((serviceType, serviceName) => + { + // https://github.com/seesharper/LightInject/issues/173 + if (serviceBaseType.IsAssignableFromGtd(serviceType)) + Container.Register(serviceType); + return false; + }, null); + } + + // was the Light-Inject specific way of dealing with args, but we've replaced it with our own + // beware! does NOT work on singletons, see https://github.com/seesharper/LightInject/issues/294 + // + ///// + //public void RegisterConstructorDependency(Func factory) + // => Container.RegisterConstructorDependency((f, x) => factory(this, x)); + // + ///// + //public void RegisterConstructorDependency(Func factory) + // => Container.RegisterConstructorDependency((f, x, a) => factory(this, x, a)); + + #endregion + + #region Control + + /// + public IDisposable BeginScope() + => Container.BeginScope(); + + /// + public virtual void ConfigureForWeb() + { } + + /// + public void EnablePerWebRequestScope() + { + if (!(Container.ScopeManagerProvider is MixedLightInjectScopeManagerProvider smp)) + throw new Exception("Container.ScopeManagerProvider is not MixedLightInjectScopeManagerProvider."); + smp.EnablePerWebRequestScope(); + } + + private class AssemblyScanner : IAssemblyScanner + { + //private readonly IAssemblyScanner _scanner; + + //public AssemblyScanner(IAssemblyScanner scanner) + //{ + // _scanner = scanner; + //} + + public void Scan(Assembly assembly, IServiceRegistry serviceRegistry, Func lifetime, Func shouldRegister, Func serviceNameProvider) + { + // nothing - we *could* scan non-Umbraco assemblies, though + } + + public void Scan(Assembly assembly, IServiceRegistry serviceRegistry) + { + // nothing - we *could* scan non-Umbraco assemblies, though + } + } + + #endregion + } +} diff --git a/src/Umbraco.Core/Exceptions/LightInjectException.cs b/src/Umbraco.Core/Composing/LightInject/LightInjectException.cs similarity index 96% rename from src/Umbraco.Core/Exceptions/LightInjectException.cs rename to src/Umbraco.Core/Composing/LightInject/LightInjectException.cs index 03fd9f2f9f..fa0aed21ca 100644 --- a/src/Umbraco.Core/Exceptions/LightInjectException.cs +++ b/src/Umbraco.Core/Composing/LightInject/LightInjectException.cs @@ -1,9 +1,7 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Text; -namespace Umbraco.Core.Exceptions +namespace Umbraco.Core.Composing.LightInject { /// /// Represents errors that occur due to LightInject. diff --git a/src/Umbraco.Core/Composing/MixedLightInjectScopeManagerProvider.cs b/src/Umbraco.Core/Composing/LightInject/MixedLightInjectScopeManagerProvider.cs similarity index 97% rename from src/Umbraco.Core/Composing/MixedLightInjectScopeManagerProvider.cs rename to src/Umbraco.Core/Composing/LightInject/MixedLightInjectScopeManagerProvider.cs index 05bdbc446d..470079c6c0 100644 --- a/src/Umbraco.Core/Composing/MixedLightInjectScopeManagerProvider.cs +++ b/src/Umbraco.Core/Composing/LightInject/MixedLightInjectScopeManagerProvider.cs @@ -1,7 +1,7 @@ using LightInject; using LightInject.Web; -namespace Umbraco.Core.Composing +namespace Umbraco.Core.Composing.LightInject { // by default, the container's scope manager provider is PerThreadScopeManagerProvider, // and then container.EnablePerWebRequestScope() replaces it with PerWebRequestScopeManagerProvider, diff --git a/src/Umbraco.Core/Composing/LightInjectExtensions.cs b/src/Umbraco.Core/Composing/LightInjectExtensions.cs deleted file mode 100644 index 68ba48c803..0000000000 --- a/src/Umbraco.Core/Composing/LightInjectExtensions.cs +++ /dev/null @@ -1,392 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using LightInject; -using Umbraco.Core.Exceptions; - -namespace Umbraco.Core.Composing -{ - /// - /// Provides extensions to LightInject. - /// - public static class LightInjectExtensions - { - /// - /// Configure the container for Umbraco Core usage and assign to Current. - /// - /// The container. - /// The container is now the unique application container and is now accessible via Current.Container. - internal static void ConfigureUmbracoCore(this ServiceContainer container) - { - // supports annotated constructor injections - // eg to specify the service name on some services - container.EnableAnnotatedConstructorInjection(); - - // from the docs: "LightInject considers all read/write properties a dependency, but implements - // a loose strategy around property dependencies, meaning that it will NOT throw an exception - // in the case of an unresolved property dependency." - // - // in Umbraco we do NOT want to do property injection by default, so we have to disable it. - // from the docs, the following line will cause the container to "now only try to inject - // dependencies for properties that is annotated with the InjectAttribute." - // - // could not find it documented, but tests & code review shows that LightInject considers a - // property to be "injectable" when its setter exists and is not static, nor private, nor - // it is an index property. which means that eg protected or internal setters are OK. - container.EnableAnnotatedPropertyInjection(); - - // ensure that we do *not* scan assemblies - // we explicitely RegisterFrom our own composition roots and don't want them scanned - container.AssemblyScanner = new AssemblyScanner(/*container.AssemblyScanner*/); - - // see notes in MixedLightInjectScopeManagerProvider - container.ScopeManagerProvider = new MixedLightInjectScopeManagerProvider(); - - // self-register - container.Register(_ => container); - - // configure the current container - Current.Container = container; - } - - private class AssemblyScanner : IAssemblyScanner - { - //private readonly IAssemblyScanner _scanner; - - //public AssemblyScanner(IAssemblyScanner scanner) - //{ - // _scanner = scanner; - //} - - public void Scan(Assembly assembly, IServiceRegistry serviceRegistry, Func lifetime, Func shouldRegister, Func serviceNameProvider) - { - // nothing - we *could* scan non-Umbraco assemblies, though - } - - public void Scan(Assembly assembly, IServiceRegistry serviceRegistry) - { - // nothing - we *could* scan non-Umbraco assemblies, though - } - } - - /// - /// Registers a service implementation with a specified lifetime. - /// - /// The type of the service. - /// The type of the implementation. - /// The type of the lifetime. - /// The container. - public static void Register(this IServiceContainer container) - where TImplementation : TService - where TLifetime : ILifetime, new() - { - container.Register(new TLifetime()); - } - - /// - /// Registers a service implementation with a specified lifetime. - /// - /// The type of the service. - /// The type of the lifetime. - /// The container. - /// A factory. - public static void Register(this IServiceContainer container, Func factory) - where TLifetime : ILifetime, new() - { - container.Register(factory, new TLifetime()); - } - - /// - /// Registers several service implementations with a specified lifetime. - /// - /// The type of the service. - /// The type of the lifetime. - /// The container. - /// The types of the implementations. - public static void RegisterMany(this IServiceContainer container, IEnumerable implementations) - where TLifeTime : ILifetime, new() - { - foreach (var implementation in implementations) - { - // if "typeof (TService)" is there then "implementation.FullName" MUST be there too - container.Register(typeof(TService), implementation, implementation.FullName, new TLifeTime()); - } - } - - /// - /// Registers the TService with the factory that describes the dependencies of the service, as a singleton. - /// - public static void RegisterSingleton(this IServiceRegistry container, Func factory, string serviceName) - { - var registration = container.GetAvailableService(serviceName); - if (registration == null) - { - container.Register(factory, serviceName, new PerContainerLifetime()); - } - else - { - if (registration.Lifetime is PerContainerLifetime == false) - throw new InvalidOperationException("Existing registration lifetime is not PerContainer."); - UpdateRegistration(registration, null, factory); - } - } - - /// - /// Registers the TService with the TImplementation as a singleton. - /// - public static void RegisterSingleton(this IServiceRegistry container) - where TImplementation : TService - { - var registration = container.GetAvailableService(); - - if (registration == null) - { - container.Register(new PerContainerLifetime()); - } - else - { - if (registration.Lifetime is PerContainerLifetime == false) - throw new InvalidOperationException("Existing registration lifetime is not PerContainer."); - UpdateRegistration(registration, typeof(TImplementation), null); - } - } - - /// - /// Registers a concrete type as a singleton service. - /// - public static void RegisterSingleton(this IServiceRegistry container) - { - var registration = container.GetAvailableService(); - if (registration == null) - { - container.Register(new PerContainerLifetime()); - } - else - { - if (registration.Lifetime is PerContainerLifetime == false) - throw new InvalidOperationException("Existing registration lifetime is not PerContainer."); - UpdateRegistration(registration, typeof(TImplementation), null); - } - - } - - /// - /// Registers the TService with the factory that describes the dependencies of the service, as a singleton. - /// - /// - /// - /// - public static void RegisterSingleton(this IServiceRegistry container, Func factory) - { - var registration = container.GetAvailableService(); - if (registration == null) - container.Register(factory, new PerContainerLifetime()); - else - UpdateRegistration(registration, null, factory); - } - - // note - what's below ALSO applies to non-singleton ie transient services - // - // see https://github.com/seesharper/LightInject/issues/133 - // - // note: we *could* use tracking lifetimes for singletons to ensure they have not been resolved - // already but that would not work for transient as the transient lifetime is null (and that is - // optimized in LightInject) - // - // also, RegisterSingleton above is dangerous because ppl could still use Register with a - // PerContainerLifetime and it will not work + the default Register would not work either for other - // lifetimes - // - // all in all, not sure we want to let ppl have direct access to the container - // we might instead want to expose some methods in UmbracoComponentBase or whatever? - - /// - /// Updates a registration. - /// - private static void UpdateRegistration(Registration registration, Type implementingType, Delegate factoryExpression) - { - // if the container has compiled already then the registrations have been captured, - // and re-registering - although updating available services - does not modify the - // output of GetInstance - // - // so we have to rely on different methods - // - // assuming the service has NOT been resolved, both methods below work, but the first - // one looks simpler. it would be good to check whether the service HAS been resolved - // but I am not sure how to do it right now, depending on the lifetime - // - // if the service HAS been resolved then updating is probably a bad idea - - // not sure which is best? that one works, though, and looks simpler - registration.ImplementingType = implementingType; - registration.FactoryExpression = factoryExpression; - - //container.Override( - // r => r.ServiceType == typeof (TService) && (registration.ServiceName == null || r.ServiceName == registration.ServiceName), - // (f, r) => - // { - // r.ImplementingType = implementingType; - // r.FactoryExpression = factoryExpression; - // return r; - // }); - } - - /// - /// Gets the available service registrations for a service type. - /// - /// The service type. - /// The container. - /// The service registrations for the service type. - public static IEnumerable GetAvailableServices(this IServiceRegistry container) - { - var typeofTService = typeof(TService); - return container.AvailableServices.Where(x => x.ServiceType == typeofTService); - } - - /// - /// Gets the unique available service registration for a service type. - /// - /// The service type. - /// The container. - /// The unique service registration for the service type. - /// Can return null, but throws if more than one registration exist for the service type. - public static ServiceRegistration GetAvailableService(this IServiceRegistry container) - { - var typeofTService = typeof(TService); - return container.AvailableServices.SingleOrDefault(x => x.ServiceType == typeofTService); - } - - /// - /// Gets the unique available service registration for a service type and a name. - /// - /// The service type. - /// The container. - /// The name. - /// The unique service registration for the service type and the name. - /// Can return null, but throws if more than one registration exist for the service type and the name. - public static ServiceRegistration GetAvailableService(this IServiceRegistry container, string name) - { - var typeofTService = typeof(TService); - return container.AvailableServices.SingleOrDefault(x => x.ServiceType == typeofTService && x.ServiceName == name); - } - - /// - /// Gets an instance of a TService or throws a meaningful exception. - /// - /// The service type. - /// The container. - /// The instance. - public static TService GetInstanceOrThrow(this IServiceFactory factory) - { - if (factory == null) - throw new ArgumentNullException(nameof(factory)); - - try - { - return factory.GetInstance(); - } - catch (Exception e) - { - LightInjectException.TryThrow(e); - throw; - } - } - - /// - /// Gets an instance of a TService or throws a meaningful exception. - /// - /// The container. - /// The type of the service. - /// The name of the service. - /// The implementing type. - /// Arguments. - /// The instance. - internal static object GetInstanceOrThrow(this IServiceFactory factory, Type tService, string serviceName, Type implementingType, object[] args) - { - if (factory == null) - throw new ArgumentNullException(nameof(factory)); - - // fixme temp - STOP doing this, it confuses LightInject and then we get ugly exception traces - // we HAVE to let LightInject throw - and catch at THE OUTERMOST if InvalidOperationException in LightInject.Anything! - - return factory.GetInstance(tService, serviceName, args); - //try - //{ - // return factory.GetInstance(tService, serviceName, args); - //} - //catch (Exception e) - //{ - // LightInjectException.TryThrow(e, implementingType); - // throw; - //} - } - - /// - /// Registers a base type for auto-registration. - /// - /// The base type. - /// The container. - /// - /// Any type that inherits/implements the base type will be auto-registered on-demand. - /// This methods works with actual types. Use the other overload for eg generic definitions. - /// - public static void RegisterAuto(this IServiceContainer container) - { - container.RegisterFallback((serviceType, serviceName) => - { - //Current.Logger.Debug(typeof(LightInjectExtensions), $"Fallback for type {serviceType.FullName}."); - // https://github.com/seesharper/LightInject/issues/173 - - if (typeof(T).IsAssignableFrom(serviceType)) - container.Register(serviceType); - return false; - }, null); - } - - /// - /// Registers a base type for auto-registration. - /// - /// The container. - /// The base type. - /// - /// Any type that inherits/implements the base type will be auto-registered on-demand. - /// This methods works with actual types, as well as generic definitions eg typeof(MyBase{}). - /// - public static void RegisterAuto(this IServiceContainer container, Type type) - { - container.RegisterFallback((serviceType, serviceName) => - { - //Current.Logger.Debug(typeof(LightInjectExtensions), $"Fallback for type {serviceType.FullName}."); - // https://github.com/seesharper/LightInject/issues/173 - - if (type.IsAssignableFromGtd(serviceType)) - container.Register(serviceType); - return false; - }, null); - } - - /// - /// Registers and instanciates a collection builder. - /// - /// The type of the collection builder. - /// The container. - /// The collection builder. - public static TBuilder RegisterCollectionBuilder(this IServiceContainer container) - { - // make sure it's not already registered - // we just don't want to support re-registering collection builders - var registration = container.GetAvailableService(); - if (registration != null) - throw new InvalidOperationException("Collection builders should be registered only once."); - - // register the builder - per container - var builderLifetime = new PerContainerLifetime(); - container.Register(builderLifetime); - - // return the builder - // (also initializes the builder) - return container.GetInstance(); - } - } -} diff --git a/src/Umbraco.Core/Composing/OrderedCollectionBuilderBase.cs b/src/Umbraco.Core/Composing/OrderedCollectionBuilderBase.cs index 4811551cd2..bde1bf96c5 100644 --- a/src/Umbraco.Core/Composing/OrderedCollectionBuilderBase.cs +++ b/src/Umbraco.Core/Composing/OrderedCollectionBuilderBase.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using LightInject; namespace Umbraco.Core.Composing { @@ -14,20 +13,12 @@ namespace Umbraco.Core.Composing where TBuilder : OrderedCollectionBuilderBase where TCollection : IBuilderCollection { - /// - /// Initializes a new instance of the class. - /// - /// - protected OrderedCollectionBuilderBase(IServiceContainer container) - : base (container) - { } - protected abstract TBuilder This { get; } /// /// Clears all types in the collection. /// - /// The buidler. + /// The builder. public TBuilder Clear() { Configure(types => types.Clear()); @@ -87,26 +78,6 @@ namespace Umbraco.Core.Composing return This; } - /// - /// Appends types to the collections. - /// - /// The types to append. - /// The builder. - public TBuilder Append(Func> types) - { - Configure(list => - { - foreach (var type in types(Container)) - { - // would be detected by CollectionBuilderBase when registering, anyways, but let's fail fast - EnsureType(type, "register"); - if (list.Contains(type)) list.Remove(type); - list.Add(type); - } - }); - return This; - } - /// /// Appends a type after another type. /// diff --git a/src/Umbraco.Core/Composing/RegisterExtensions.cs b/src/Umbraco.Core/Composing/RegisterExtensions.cs new file mode 100644 index 0000000000..4db1a2e9e4 --- /dev/null +++ b/src/Umbraco.Core/Composing/RegisterExtensions.cs @@ -0,0 +1,32 @@ +namespace Umbraco.Core.Composing +{ + /// + /// Provides extension methods to the class. + /// + public static class RegisterExtensions + { + /// + /// Registers a service with an implementation type. + /// + public static void Register(this IRegister register, Lifetime lifetime = Lifetime.Transient) + => register.Register(typeof(TService), typeof(TImplementing), lifetime); + + /// + /// Registers a service as its own implementation. + /// + public static void Register(this IRegister register, Lifetime lifetime = Lifetime.Transient) + => register.Register(typeof(TService), lifetime); + + /// + /// Registers a service with an implementing instance. + /// + public static void RegisterInstance(this IRegister register, TService instance) + => register.RegisterInstance(typeof(TService), instance); + + /// + /// Registers a base type for auto-registration. + /// + public static void RegisterAuto(this IRegister register) + => register.RegisterAuto(typeof(TServiceBase)); + } +} diff --git a/src/Umbraco.Core/Composing/RegisterFactory.cs b/src/Umbraco.Core/Composing/RegisterFactory.cs new file mode 100644 index 0000000000..8ee6e5a94c --- /dev/null +++ b/src/Umbraco.Core/Composing/RegisterFactory.cs @@ -0,0 +1,56 @@ +using System; +using System.Configuration; +using System.Reflection; + +namespace Umbraco.Core.Composing +{ + /// + /// Creates the container. + /// + public static class RegisterFactory + { + // cannot use typeof().AssemblyQualifiedName on the web container - we don't reference it + // a normal Umbraco site should run on the web container, but an app may run on the core one + private const string CoreLightInjectContainerTypeName = "Umbraco.Core.Composing.LightInject.LightInjectContainer,Umbraco.Core"; + private const string WebLightInjectContainerTypeName = "Umbraco.Web.Composing.LightInject.LightInjectContainer,Umbraco.Web"; + + /// + /// Creates a new instance of the configured container. + /// + /// + /// To override the default LightInjectContainer, add an appSetting named umbracoContainerType with + /// a fully qualified type name to a class with a static method "Create" returning an IRegister. + /// + public static IRegister Create() + { + Type type; + + var configuredTypeName = ConfigurationManager.AppSettings["umbracoRegisterType"]; + if (configuredTypeName.IsNullOrWhiteSpace()) + { + // try to get the web LightInject container type, + // else the core LightInject container type + type = Type.GetType(configuredTypeName = WebLightInjectContainerTypeName) ?? + Type.GetType(configuredTypeName = CoreLightInjectContainerTypeName); + } + else + { + // try to get the configured type + type = Type.GetType(configuredTypeName); + } + + if (type == null) + throw new Exception($"Cannot find register factory class '{configuredTypeName}'."); + + var factoryMethod = type.GetMethod("Create", BindingFlags.Public | BindingFlags.Static); + if (factoryMethod == null) + throw new Exception($"Register factory class '{configuredTypeName}' does not have a public static method named Create."); + + var container = factoryMethod.Invoke(null, Array.Empty()) as IRegister; + if (container == null) + throw new Exception($"Register factory '{configuredTypeName}' did not return an IRegister implementation."); + + return container; + } + } +} diff --git a/src/Umbraco.Core/Composing/TypeLoader.cs b/src/Umbraco.Core/Composing/TypeLoader.cs index ef58671e91..3121e869c3 100644 --- a/src/Umbraco.Core/Composing/TypeLoader.cs +++ b/src/Umbraco.Core/Composing/TypeLoader.cs @@ -30,11 +30,10 @@ namespace Umbraco.Core.Composing private const string CacheKey = "umbraco-types.list"; private readonly IRuntimeCacheProvider _runtimeCache; - private readonly IGlobalSettings _globalSettings; - private readonly ProfilingLogger _logger; + private readonly IProfilingLogger _logger; private readonly Dictionary _types = new Dictionary(); - private readonly object _typesLock = new object(); + private readonly object _locko = new object(); private readonly object _timerLock = new object(); private Timer _timer; @@ -43,31 +42,30 @@ namespace Umbraco.Core.Composing private string _currentAssembliesHash; private IEnumerable _assemblies; private bool _reportedChange; - private static LocalTempStorage _localTempStorage = LocalTempStorage.Unknown; + private static LocalTempStorage _localTempStorage; private static string _fileBasePath; /// /// Initializes a new instance of the class. /// /// The application runtime cache. - /// + /// Files storage mode. /// A profiling logger. - /// Used by LightInject. - public TypeLoader(IRuntimeCacheProvider runtimeCache, IGlobalSettings globalSettings, ProfilingLogger logger) - : this(runtimeCache, globalSettings, logger, true) + public TypeLoader(IRuntimeCacheProvider runtimeCache, LocalTempStorage localTempStorage, IProfilingLogger logger) + : this(runtimeCache, localTempStorage, logger, true) { } /// /// Initializes a new instance of the class. /// /// The application runtime cache. - /// + /// Files storage mode. /// A profiling logger. /// Whether to detect changes using hashes. - internal TypeLoader(IRuntimeCacheProvider runtimeCache, IGlobalSettings globalSettings, ProfilingLogger logger, bool detectChanges) + internal TypeLoader(IRuntimeCacheProvider runtimeCache, LocalTempStorage localTempStorage, IProfilingLogger logger, bool detectChanges) { _runtimeCache = runtimeCache ?? throw new ArgumentNullException(nameof(runtimeCache)); - _globalSettings = globalSettings ?? throw new ArgumentNullException(nameof(globalSettings)); + _localTempStorage = localTempStorage == LocalTempStorage.Unknown ? LocalTempStorage.Default : localTempStorage; _logger = logger ?? throw new ArgumentNullException(nameof(logger)); if (detectChanges) @@ -100,6 +98,13 @@ namespace Umbraco.Core.Composing } } + /// + /// Initializes a new, test/blank, instance of the class. + /// + /// The initialized instance cannot get types. + internal TypeLoader() + { } + /// /// Gets or sets the set of assemblies to scan. /// @@ -206,7 +211,7 @@ namespace Umbraco.Core.Composing /// The hash. /// Each file is a tuple containing the FileInfo object and a boolean which indicates whether to hash the /// file properties (false) or the file contents (true). - private static string GetFileHash(IEnumerable> filesAndFolders, ProfilingLogger logger) + private static string GetFileHash(IEnumerable> filesAndFolders, IProfilingLogger logger) { using (logger.TraceDuration("Determining hash of code files on disk", "Hash determined")) { @@ -264,7 +269,7 @@ namespace Umbraco.Core.Composing /// A profiling logger. /// The hash. // internal for tests - internal static string GetFileHash(IEnumerable filesAndFolders, ProfilingLogger logger) + internal static string GetFileHash(IEnumerable filesAndFolders, IProfilingLogger logger) { using (logger.TraceDuration("Determining hash of code files on disk", "Hash determined")) { @@ -380,14 +385,15 @@ namespace Umbraco.Core.Composing private string GetFileBasePath() { - var localTempStorage = _globalSettings.LocalTempStorageLocation; - if (_localTempStorage != localTempStorage) + lock (_locko) { - string path; - switch (_globalSettings.LocalTempStorageLocation) + if (_fileBasePath != null) + return _fileBasePath; + + switch (_localTempStorage) { case LocalTempStorage.AspNetTemp: - path = Path.Combine(HttpRuntime.CodegenDir, "UmbracoData", "umbraco-types"); + _fileBasePath = Path.Combine(HttpRuntime.CodegenDir, "UmbracoData", "umbraco-types"); break; case LocalTempStorage.EnvironmentTemp: // include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back @@ -395,33 +401,30 @@ namespace Umbraco.Core.Composing // utilizing an old path - assuming we cannot have SHA1 collisions on AppDomainAppId var appDomainHash = HttpRuntime.AppDomainAppId.ToSHA1(); var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoData", appDomainHash); - path = Path.Combine(cachePath, "umbraco-types"); + _fileBasePath = Path.Combine(cachePath, "umbraco-types"); break; case LocalTempStorage.Default: default: var tempFolder = IOHelper.MapPath("~/App_Data/TEMP/TypesCache"); - path = Path.Combine(tempFolder, "umbraco-types." + NetworkHelper.FileSafeMachineName); + _fileBasePath = Path.Combine(tempFolder, "umbraco-types." + NetworkHelper.FileSafeMachineName); break; } - _fileBasePath = path; - _localTempStorage = localTempStorage; + // ensure that the folder exists + var directory = Path.GetDirectoryName(_fileBasePath); + if (directory == null) + throw new InvalidOperationException($"Could not determine folder for path \"{_fileBasePath}\"."); + if (Directory.Exists(directory) == false) + Directory.CreateDirectory(directory); + + return _fileBasePath; } - - // ensure that the folder exists - var directory = Path.GetDirectoryName(_fileBasePath); - if (directory == null) - throw new InvalidOperationException($"Could not determine folder for path \"{_fileBasePath}\"."); - if (Directory.Exists(directory) == false) - Directory.CreateDirectory(directory); - - return _fileBasePath; } // internal for tests internal void WriteCache() { - _logger.Logger.Debug("Writing cache file."); + _logger.Debug("Writing cache file."); var typesListFilePath = GetTypesListFilePath(); using (var stream = GetFileStream(typesListFilePath, FileMode.Create, FileAccess.Write, FileShare.None, ListFileOpenWriteTimeout)) using (var writer = new StreamWriter(stream)) @@ -493,7 +496,7 @@ namespace Umbraco.Core.Composing if (--attempts == 0) throw; - _logger.Logger.Debug("Attempted to get filestream for file {Path} failed, {NumberOfAttempts} attempts left, pausing for {PauseMilliseconds} milliseconds", path, attempts, pauseMilliseconds); + _logger.Debug("Attempted to get filestream for file {Path} failed, {NumberOfAttempts} attempts left, pausing for {PauseMilliseconds} milliseconds", path, attempts, pauseMilliseconds); Thread.Sleep(pauseMilliseconds); } } @@ -514,7 +517,7 @@ namespace Umbraco.Core.Composing if (--attempts == 0) throw; - _logger.Logger.Debug("Attempted to delete file {Path} failed, {NumberOfAttempts} attempts left, pausing for {PauseMilliseconds} milliseconds", path, attempts, pauseMilliseconds); + _logger.Debug("Attempted to delete file {Path} failed, {NumberOfAttempts} attempts left, pausing for {PauseMilliseconds} milliseconds", path, attempts, pauseMilliseconds); Thread.Sleep(pauseMilliseconds); } } @@ -534,6 +537,9 @@ namespace Umbraco.Core.Composing /// Caching is disabled when using specific assemblies. public IEnumerable GetTypes(bool cache = true, IEnumerable specificAssemblies = null) { + if (_logger == null) + throw new InvalidOperationException("Cannot get types from a test/blank type loader."); + // do not cache anything from specific assemblies cache &= specificAssemblies == null; @@ -541,7 +547,7 @@ namespace Umbraco.Core.Composing if (!typeof(IDiscoverable).IsAssignableFrom(typeof(T))) { // warn - _logger.Logger.Debug("Running a full, " + (cache ? "" : "non-") + "cached, scan for non-discoverable type {TypeName} (slow).", typeof(T).FullName); + _logger.Debug("Running a full, " + (cache ? "" : "non-") + "cached, scan for non-discoverable type {TypeName} (slow).", typeof(T).FullName); return GetTypesInternal( typeof(T), null, @@ -559,7 +565,7 @@ namespace Umbraco.Core.Composing // warn if (!cache) - _logger.Logger.Debug("Running a non-cached, filter for discoverable type {TypeName} (slowish).", typeof(T).FullName); + _logger.Debug("Running a non-cached, filter for discoverable type {TypeName} (slowish).", typeof(T).FullName); // filter the cached discovered types (and maybe cache the result) return GetTypesInternal( @@ -582,13 +588,16 @@ namespace Umbraco.Core.Composing public IEnumerable GetTypesWithAttribute(bool cache = true, IEnumerable specificAssemblies = null) where TAttribute : Attribute { + if (_logger == null) + throw new InvalidOperationException("Cannot get types from a test/blank type loader."); + // do not cache anything from specific assemblies cache &= specificAssemblies == null; // if not IDiscoverable, directly get types if (!typeof(IDiscoverable).IsAssignableFrom(typeof(T))) { - _logger.Logger.Debug("Running a full, " + (cache ? "" : "non-") + "cached, scan for non-discoverable type {TypeName} / attribute {AttributeName} (slow).", typeof(T).FullName, typeof(TAttribute).FullName); + _logger.Debug("Running a full, " + (cache ? "" : "non-") + "cached, scan for non-discoverable type {TypeName} / attribute {AttributeName} (slow).", typeof(T).FullName, typeof(TAttribute).FullName); return GetTypesInternal( typeof(T), typeof(TAttribute), @@ -606,7 +615,7 @@ namespace Umbraco.Core.Composing // warn if (!cache) - _logger.Logger.Debug("Running a non-cached, filter for discoverable type {TypeName} / attribute {AttributeName} (slowish).", typeof(T).FullName, typeof(TAttribute).FullName); + _logger.Debug("Running a non-cached, filter for discoverable type {TypeName} / attribute {AttributeName} (slowish).", typeof(T).FullName, typeof(TAttribute).FullName); // filter the cached discovered types (and maybe cache the result) return GetTypesInternal( @@ -629,11 +638,14 @@ namespace Umbraco.Core.Composing public IEnumerable GetAttributedTypes(bool cache = true, IEnumerable specificAssemblies = null) where TAttribute : Attribute { + if (_logger == null) + throw new InvalidOperationException("Cannot get types from a test/blank type loader."); + // do not cache anything from specific assemblies cache &= specificAssemblies == null; if (!cache) - _logger.Logger.Debug("Running a full, non-cached, scan for types / attribute {AttributeName} (slow).", typeof(TAttribute).FullName); + _logger.Debug("Running a full, non-cached, scan for types / attribute {AttributeName} (slow).", typeof(TAttribute).FullName); return GetTypesInternal( typeof (object), typeof (TAttribute), @@ -655,7 +667,7 @@ namespace Umbraco.Core.Composing var name = GetName(baseType, attributeType); - lock (_typesLock) + lock (_locko) using (_logger.TraceDuration( "Getting " + name, "Got " + name)) // cannot contain typesFound.Count as it's evaluated before the find @@ -689,7 +701,7 @@ namespace Umbraco.Core.Composing if (typeList != null) { // need to put some logging here to try to figure out why this is happening: http://issues.umbraco.org/issue/U4-3505 - _logger.Logger.Debug("Getting {TypeName}: found a cached type list.", GetName(baseType, attributeType)); + _logger.Debug("Getting {TypeName}: found a cached type list.", GetName(baseType, attributeType)); return typeList.Types; } @@ -705,7 +717,7 @@ namespace Umbraco.Core.Composing // report (only once) and scan and update the cache file if (_reportedChange == false) { - _logger.Logger.Debug("Assemblies changes detected, need to rescan everything."); + _logger.Debug("Assemblies changes detected, need to rescan everything."); _reportedChange = true; } } @@ -720,7 +732,7 @@ namespace Umbraco.Core.Composing // so in this instance there will never be a result. if (cacheResult.Exception is CachedTypeNotFoundInFileException || cacheResult.Success == false) { - _logger.Logger.Debug("Getting {TypeName}: failed to load from cache file, must scan assemblies.", GetName(baseType, attributeType)); + _logger.Debug("Getting {TypeName}: failed to load from cache file, must scan assemblies.", GetName(baseType, attributeType)); scan = true; } else @@ -739,7 +751,7 @@ namespace Umbraco.Core.Composing catch (Exception ex) { // in case of any exception, we have to exit, and revert to scanning - _logger.Logger.Error(ex, "Getting {TypeName}: failed to load cache file type {CacheType}, reverting to scanning assemblies.", GetName(baseType, attributeType), type); + _logger.Error(ex, "Getting {TypeName}: failed to load cache file type {CacheType}, reverting to scanning assemblies.", GetName(baseType, attributeType), type); scan = true; break; } @@ -747,7 +759,7 @@ namespace Umbraco.Core.Composing if (scan == false) { - _logger.Logger.Debug("Getting {TypeName}: loaded types from cache file.", GetName(baseType, attributeType)); + _logger.Debug("Getting {TypeName}: loaded types from cache file.", GetName(baseType, attributeType)); } } } @@ -755,7 +767,7 @@ namespace Umbraco.Core.Composing if (scan) { // either we had to scan, or we could not get the types from the cache file - scan now - _logger.Logger.Debug("Getting {TypeName}: " + action + ".", GetName(baseType, attributeType)); + _logger.Debug("Getting {TypeName}: " + action + ".", GetName(baseType, attributeType)); foreach (var t in finder()) typeList.Add(t); @@ -773,11 +785,11 @@ namespace Umbraco.Core.Composing UpdateCache(); } - _logger.Logger.Debug("Got {TypeName}, caching ({CacheType}).", GetName(baseType, attributeType), added.ToString().ToLowerInvariant()); + _logger.Debug("Got {TypeName}, caching ({CacheType}).", GetName(baseType, attributeType), added.ToString().ToLowerInvariant()); } else { - _logger.Logger.Debug("Got {TypeName}.", GetName(baseType, attributeType)); + _logger.Debug("Got {TypeName}.", GetName(baseType, attributeType)); } return typeList.Types; diff --git a/src/Umbraco.Core/Composing/TypeLoaderExtensions.cs b/src/Umbraco.Core/Composing/TypeLoaderExtensions.cs index 6177151a00..ba57243071 100644 --- a/src/Umbraco.Core/Composing/TypeLoaderExtensions.cs +++ b/src/Umbraco.Core/Composing/TypeLoaderExtensions.cs @@ -42,13 +42,5 @@ namespace Umbraco.Core.Composing { return mgr.GetTypesWithAttribute(); } - - /// - /// Gets all classes implementing ISqlSyntaxProvider and marked with the SqlSyntaxProviderAttribute. - /// - public static IEnumerable GetSqlSyntaxProviders(this TypeLoader mgr) - { - return mgr.GetTypesWithAttribute(); - } } } diff --git a/src/Umbraco.Core/Composing/WeightedCollectionBuilderBase.cs b/src/Umbraco.Core/Composing/WeightedCollectionBuilderBase.cs index 99fa2d3eb9..da47c53bf8 100644 --- a/src/Umbraco.Core/Composing/WeightedCollectionBuilderBase.cs +++ b/src/Umbraco.Core/Composing/WeightedCollectionBuilderBase.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using LightInject; namespace Umbraco.Core.Composing { @@ -15,20 +14,12 @@ namespace Umbraco.Core.Composing where TBuilder : WeightedCollectionBuilderBase where TCollection : IBuilderCollection { - /// - /// Initializes a new instance of the class. - /// - /// - protected WeightedCollectionBuilderBase(IServiceContainer container) - : base(container) - { } - protected abstract TBuilder This { get; } /// /// Clears all types in the collection. /// - /// The buidler. + /// The builder. public TBuilder Clear() { Configure(types => types.Clear()); diff --git a/src/Umbraco.Core/ConfigsExtensions.cs b/src/Umbraco.Core/ConfigsExtensions.cs new file mode 100644 index 0000000000..1414dbc852 --- /dev/null +++ b/src/Umbraco.Core/ConfigsExtensions.cs @@ -0,0 +1,52 @@ +using System.IO; +using Umbraco.Core.Cache; +using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.Dashboard; +using Umbraco.Core.Configuration.Grid; +using Umbraco.Core.Configuration.HealthChecks; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.IO; +using Umbraco.Core.Logging; + +namespace Umbraco.Core +{ + /// + /// Provides extension methods for the class. + /// + public static class ConfigsExtensions + { + public static IGlobalSettings Global(this Configs configs) + => configs.GetConfig(); + + public static IUmbracoSettingsSection Settings(this Configs configs) + => configs.GetConfig(); + + public static IDashboardSection Dashboards(this Configs configs) + => configs.GetConfig(); + + public static IHealthChecks HealthChecks(this Configs configs) + => configs.GetConfig(); + + public static IGridConfig Grids(this Configs configs) + => configs.GetConfig(); + + internal static CoreDebug CoreDebug(this Configs configs) + => configs.GetConfig(); + + public static void AddCoreConfigs(this Configs configs) + { + var configDir = new DirectoryInfo(IOHelper.MapPath(SystemDirectories.Config)); + + configs.Add(() => new GlobalSettings()); + configs.Add("umbracoConfiguration/settings"); + configs.Add("umbracoConfiguration/dashBoard"); + configs.Add("umbracoConfiguration/HealthChecks"); + + configs.Add(() => new CoreDebug()); + + // GridConfig depends on runtime caches, manifest parsers... and cannot be available during composition + configs.Add(factory => new GridConfig(factory.GetInstance(), factory.GetInstance(), configDir, factory.GetInstance().Debug)); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/Configs.cs b/src/Umbraco.Core/Configuration/Configs.cs new file mode 100644 index 0000000000..3dbbe5d4ff --- /dev/null +++ b/src/Umbraco.Core/Configuration/Configs.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using Umbraco.Core.Composing; +using Umbraco.Core.Logging; + +namespace Umbraco.Core.Configuration +{ + /// + /// Represents Umbraco configurations. + /// + /// + /// During composition, use composition.Configs. When running, either inject the required configuration, + /// or use Current.Configs. + /// + public class Configs + { + private readonly Dictionary> _configs = new Dictionary>(); + private Dictionary> _registerings = new Dictionary>(); + + /// + /// Gets a configuration. + /// + public TConfig GetConfig() + where TConfig : class + { + if (!_configs.TryGetValue(typeof(TConfig), out var configFactory)) + throw new InvalidOperationException($"No configuration of type {typeof(TConfig)} has been added."); + + return (TConfig) configFactory.Value; + } + + /// + /// Adds a configuration, provided by a factory. + /// + public void Add(Func configFactory) + where TConfig : class + { + // make sure it is not too late + if (_registerings == null) + throw new InvalidOperationException("Configurations have already been registered."); + + var typeOfConfig = typeof(TConfig); + + var lazyConfigFactory = _configs[typeOfConfig] = new Lazy(configFactory); + _registerings[typeOfConfig] = register => register.Register(_ => (TConfig) lazyConfigFactory.Value, Lifetime.Singleton); + } + + /// + /// Adds a configuration, provided by a factory. + /// + public void Add(Func configFactory) + where TConfig : class + { + // make sure it is not too late + if (_registerings == null) + throw new InvalidOperationException("Configurations have already been registered."); + + var typeOfConfig = typeof(TConfig); + + _configs[typeOfConfig] = new Lazy(() => + { + if (Current.HasFactory) return Current.Factory.GetInstance(); + throw new InvalidOperationException($"Cannot get configuration of type {typeOfConfig} during composition."); + }); + _registerings[typeOfConfig] = register => register.Register(configFactory, Lifetime.Singleton); + } + + /// + /// Adds a configuration, provided by a configuration section. + /// + public void Add(string sectionName) + where TConfig : class + { + Add(() => GetConfig(sectionName)); + } + + private static TConfig GetConfig(string sectionName) + where TConfig : class + { + // note: need to use SafeCallContext here because ConfigurationManager.GetSection ends up getting AppDomain.Evidence + // which will want to serialize the call context including anything that is in there - what a mess! + + using (new SafeCallContext()) + { + if ((ConfigurationManager.GetSection(sectionName) is TConfig config)) + return config; + var ex = new ConfigurationErrorsException($"Could not get configuration section \"{sectionName}\" from config files."); + Current.Logger.Error(ex, "Config error"); + throw ex; + } + } + + /// + /// Registers configurations in a register. + /// + public void RegisterWith(IRegister register) + { + // do it only once + if (_registerings == null) + throw new InvalidOperationException("Configurations have already been registered."); + + register.RegisterInstance(this); + + foreach (var registering in _registerings.Values) + registering(register); + + // no need to keep them around + _registerings = null; + } + } +} diff --git a/src/Umbraco.Core/Configuration/CoreDebug.cs b/src/Umbraco.Core/Configuration/CoreDebug.cs index 71d0f24941..a71b311d7c 100644 --- a/src/Umbraco.Core/Configuration/CoreDebug.cs +++ b/src/Umbraco.Core/Configuration/CoreDebug.cs @@ -2,16 +2,6 @@ namespace Umbraco.Core.Configuration { - internal static class CoreDebugExtensions - { - private static CoreDebug _coreDebug; - - public static CoreDebug CoreDebug(this UmbracoConfig config) - { - return _coreDebug ?? (_coreDebug = new CoreDebug()); - } - } - internal class CoreDebug { public CoreDebug() diff --git a/src/Umbraco.Core/Configuration/Grid/GridConfig.cs b/src/Umbraco.Core/Configuration/Grid/GridConfig.cs index beade2d6d1..6c16a5e7ef 100644 --- a/src/Umbraco.Core/Configuration/Grid/GridConfig.cs +++ b/src/Umbraco.Core/Configuration/Grid/GridConfig.cs @@ -6,11 +6,11 @@ namespace Umbraco.Core.Configuration.Grid { class GridConfig : IGridConfig { - public GridConfig(ILogger logger, IRuntimeCacheProvider runtimeCache, DirectoryInfo appPlugins, DirectoryInfo configFolder, bool isDebug) + public GridConfig(ILogger logger, IRuntimeCacheProvider runtimeCache, DirectoryInfo configFolder, bool isDebug) { - EditorsConfig = new GridEditorsConfig(logger, runtimeCache, appPlugins, configFolder, isDebug); + EditorsConfig = new GridEditorsConfig(logger, runtimeCache, configFolder, isDebug); } - public IGridEditorsConfig EditorsConfig { get; private set; } + public IGridEditorsConfig EditorsConfig { get; } } } diff --git a/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs b/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs index 708c563d9d..94249aa135 100644 --- a/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs +++ b/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs @@ -14,15 +14,13 @@ namespace Umbraco.Core.Configuration.Grid { private readonly ILogger _logger; private readonly IRuntimeCacheProvider _runtimeCache; - private readonly DirectoryInfo _appPlugins; private readonly DirectoryInfo _configFolder; private readonly bool _isDebug; - public GridEditorsConfig(ILogger logger, IRuntimeCacheProvider runtimeCache, DirectoryInfo appPlugins, DirectoryInfo configFolder, bool isDebug) + public GridEditorsConfig(ILogger logger, IRuntimeCacheProvider runtimeCache, DirectoryInfo configFolder, bool isDebug) { _logger = logger; _runtimeCache = runtimeCache; - _appPlugins = appPlugins; _configFolder = configFolder; _isDebug = isDebug; } @@ -31,7 +29,7 @@ namespace Umbraco.Core.Configuration.Grid { get { - Func> getResult = () => + List GetResult() { // fixme - should use the common one somehow! + ignoring _appPlugins here! var parser = new ManifestParser(_runtimeCache, Current.ManifestValidators, _logger); @@ -55,20 +53,16 @@ namespace Umbraco.Core.Configuration.Grid // add manifest editors, skip duplicates foreach (var gridEditor in parser.Manifest.GridEditors) { - if (editors.Contains(gridEditor) == false) - editors.Add(gridEditor); + if (editors.Contains(gridEditor) == false) editors.Add(gridEditor); } return editors; - }; + } //cache the result if debugging is disabled var result = _isDebug - ? getResult() - : _runtimeCache.GetCacheItem>( - typeof(GridEditorsConfig) + "Editors", - () => getResult(), - TimeSpan.FromMinutes(10)); + ? GetResult() + : _runtimeCache.GetCacheItem>(typeof(GridEditorsConfig) + ".Editors",GetResult, TimeSpan.FromMinutes(10)); return result; } diff --git a/src/Umbraco.Core/Configuration/UmbracoConfig.cs b/src/Umbraco.Core/Configuration/UmbracoConfig.cs deleted file mode 100644 index 6a1203313e..0000000000 --- a/src/Umbraco.Core/Configuration/UmbracoConfig.cs +++ /dev/null @@ -1,196 +0,0 @@ -using System; -using System.Configuration; -using System.IO; -using Umbraco.Core.Cache; -using Umbraco.Core.Configuration.Dashboard; -using Umbraco.Core.Configuration.Grid; -using Umbraco.Core.Configuration.HealthChecks; -using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.Composing; -using Umbraco.Core.Logging; - -namespace Umbraco.Core.Configuration -{ - /// - /// The gateway to all umbraco configuration - /// - public class UmbracoConfig - { - #region Singleton - - private static readonly Lazy Lazy = new Lazy(() => new UmbracoConfig()); - - public static UmbracoConfig For => Lazy.Value; - - #endregion - - /// - /// Default constructor - /// - private UmbracoConfig() - { - // note: need to use SafeCallContext here because ConfigurationManager.GetSection ends up getting AppDomain.Evidence - // which will want to serialize the call context including anything that is in there - what a mess! - - if (_umbracoSettings == null) - { - IUmbracoSettingsSection umbracoSettings; - using (new SafeCallContext()) - { - umbracoSettings = ConfigurationManager.GetSection("umbracoConfiguration/settings") as IUmbracoSettingsSection; - } - SetUmbracoSettings(umbracoSettings); - } - - if (_dashboardSection == null) - { - IDashboardSection dashboardConfig; - using (new SafeCallContext()) - { - dashboardConfig = ConfigurationManager.GetSection("umbracoConfiguration/dashBoard") as IDashboardSection; - } - SetDashboardSettings(dashboardConfig); - } - - if (_healthChecks == null) - { - var healthCheckConfig = ConfigurationManager.GetSection("umbracoConfiguration/HealthChecks") as IHealthChecks; - SetHealthCheckSettings(healthCheckConfig); - } - } - - /// - /// Constructor - can be used for testing - /// - /// - /// - /// - /// - public UmbracoConfig(IUmbracoSettingsSection umbracoSettings, IDashboardSection dashboardSettings, IHealthChecks healthChecks, IGlobalSettings globalSettings) - { - SetHealthCheckSettings(healthChecks); - SetUmbracoSettings(umbracoSettings); - SetDashboardSettings(dashboardSettings); - SetGlobalConfig(globalSettings); - } - - private IHealthChecks _healthChecks; - private IDashboardSection _dashboardSection; - private IUmbracoSettingsSection _umbracoSettings; - private IGridConfig _gridConfig; - private IGlobalSettings _globalSettings; - - /// - /// Gets the IHealthCheck config - /// - public IHealthChecks HealthCheck() - { - if (_healthChecks == null) - { - var ex = new ConfigurationErrorsException("Could not load the " + typeof(IHealthChecks) + " from config file, ensure the web.config and healthchecks.config files are formatted correctly"); - Current.Logger.Error(ex, "Config error"); - throw ex; - } - - return _healthChecks; - } - - /// - /// Gets the IDashboardSection - /// - public IDashboardSection DashboardSettings() - { - if (_dashboardSection == null) - { - var ex = new ConfigurationErrorsException("Could not load the " + typeof(IDashboardSection) + " from config file, ensure the web.config and Dashboard.config files are formatted correctly"); - Current.Logger.Error(ex, "Config error"); - throw ex; - } - - return _dashboardSection; - } - - /// - /// Only for testing - /// - /// - public void SetDashboardSettings(IDashboardSection value) - { - _dashboardSection = value; - } - - /// - /// Only for testing - /// - /// - public void SetHealthCheckSettings(IHealthChecks value) - { - _healthChecks = value; - } - - /// - /// Only for testing - /// - /// - public void SetUmbracoSettings(IUmbracoSettingsSection value) - { - _umbracoSettings = value; - } - - /// - /// Only for testing - /// - /// - public void SetGlobalConfig(IGlobalSettings value) - { - _globalSettings = value; - } - - /// - /// Gets the IGlobalSettings - /// - public IGlobalSettings GlobalSettings() - { - return _globalSettings ?? (_globalSettings = new GlobalSettings()); - } - - /// - /// Gets the IUmbracoSettings - /// - public IUmbracoSettingsSection UmbracoSettings() - { - if (_umbracoSettings == null) - { - var ex = new ConfigurationErrorsException("Could not load the " + typeof (IUmbracoSettingsSection) + " from config file, ensure the web.config and umbracoSettings.config files are formatted correctly"); - Current.Logger.Error(ex, "Config error"); - throw ex; - } - - return _umbracoSettings; - } - - /// - /// Only for testing - /// - /// - public void SetGridConfig(IGridConfig value) - { - _gridConfig = value; - } - - /// - /// Gets the IGridConfig - /// - public IGridConfig GridConfig(ILogger logger, IRuntimeCacheProvider runtimeCache, DirectoryInfo appPlugins, DirectoryInfo configFolder, bool isDebug) - { - if (_gridConfig == null) - { - _gridConfig = new GridConfig(logger, runtimeCache, appPlugins, configFolder, isDebug); - } - - return _gridConfig; - } - - //TODO: Add other configurations here ! - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Constants-Composing.cs b/src/Umbraco.Core/Constants-Composing.cs index 1e8c9886d2..e65629a278 100644 --- a/src/Umbraco.Core/Constants-Composing.cs +++ b/src/Umbraco.Core/Constants-Composing.cs @@ -9,20 +9,6 @@ /// Defines constants for composition. /// public static class Composing - { - /// - /// Defines file system names. - /// - public static class FileSystems - { - public const string ScriptFileSystem = "ScriptFileSystem"; - public const string PartialViewFileSystem = "PartialViewFileSystem"; - public const string PartialViewMacroFileSystem = "PartialViewMacroFileSystem"; - public const string StylesheetFileSystem = "StylesheetFileSystem"; - public const string MasterpageFileSystem = "MasterpageFileSystem"; - public const string ViewFileSystem = "ViewFileSystem"; - public const string JavascriptLibraryFileSystem = "JavascriptLibraryFileSystem"; - } - } + { } } } diff --git a/src/Umbraco.Core/ContentExtensions.cs b/src/Umbraco.Core/ContentExtensions.cs index 15ba6b1a22..1431396d01 100644 --- a/src/Umbraco.Core/ContentExtensions.cs +++ b/src/Umbraco.Core/ContentExtensions.cs @@ -5,6 +5,8 @@ using System.IO; using System.Linq; using System.Web; using System.Xml.Linq; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.Models; @@ -18,8 +20,8 @@ namespace Umbraco.Core public static class ContentExtensions { // this ain't pretty - private static MediaFileSystem _mediaFileSystem; - private static MediaFileSystem MediaFileSystem => _mediaFileSystem ?? (_mediaFileSystem = Current.FileSystems.MediaFileSystem); + private static IMediaFileSystem _mediaFileSystem; + private static IMediaFileSystem MediaFileSystem => _mediaFileSystem ?? (_mediaFileSystem = Current.MediaFileSystem); #region IContent @@ -178,8 +180,22 @@ namespace Umbraco.Core private static void SetUploadFile(this IContentBase content, IContentTypeService contentTypeService, string propertyTypeAlias, string filename, Stream filestream, string culture = null, string segment = null) { - var property = GetProperty(content, contentTypeService, propertyTypeAlias); - var oldpath = property.GetValue(culture, segment) is string svalue ? MediaFileSystem.GetRelativePath(svalue) : null; + var property = GetProperty(content, contentTypeService propertyTypeAlias); + + // Fixes https://github.com/umbraco/Umbraco-CMS/issues/3937 - Assigning a new file to an + // existing IMedia with extension SetValue causes exception 'Illegal characters in path' + string oldpath = null; + if (property.GetValue(culture, segment) is string svalue) + { + if (svalue.DetectIsJson()) + { + // the property value is a JSON serialized image crop data set - grab the "src" property as the file source + var jObject = JsonConvert.DeserializeObject(svalue); + svalue = jObject != null ? jObject.GetValueAsString("src") : svalue; + } + oldpath = MediaFileSystem.GetRelativePath(svalue); + } + var filepath = MediaFileSystem.StoreFile(content, property.PropertyType, filename, filestream, oldpath); property.SetValue(MediaFileSystem.GetUrl(filepath), culture, segment); } diff --git a/src/Umbraco.Core/Events/QueuingEventDispatcher.cs b/src/Umbraco.Core/Events/QueuingEventDispatcher.cs index e0d3847c17..b31b64e435 100644 --- a/src/Umbraco.Core/Events/QueuingEventDispatcher.cs +++ b/src/Umbraco.Core/Events/QueuingEventDispatcher.cs @@ -32,9 +32,9 @@ namespace Umbraco.Core.Events } } - private MediaFileSystem _mediaFileSystem; + private IMediaFileSystem _mediaFileSystem; // fixme inject - private MediaFileSystem MediaFileSystem => _mediaFileSystem ?? (_mediaFileSystem = Current.FileSystems.MediaFileSystem); + private IMediaFileSystem MediaFileSystem => _mediaFileSystem ?? (_mediaFileSystem = Current.MediaFileSystem); } } diff --git a/src/Umbraco.Core/Exceptions/BootFailedException.cs b/src/Umbraco.Core/Exceptions/BootFailedException.cs index ec07389d37..10a648fd76 100644 --- a/src/Umbraco.Core/Exceptions/BootFailedException.cs +++ b/src/Umbraco.Core/Exceptions/BootFailedException.cs @@ -1,9 +1,10 @@ using System; +using System.Text; namespace Umbraco.Core.Exceptions { /// - /// An exception that is thrown if the Umbraco application cannnot boot. + /// An exception that is thrown if the Umbraco application cannot boot. /// public class BootFailedException : Exception { @@ -29,5 +30,31 @@ namespace Umbraco.Core.Exceptions public BootFailedException(string message, Exception inner) : base(message, inner) { } + + /// + /// Rethrows a captured . + /// + /// The exception can be null, in which case a default message is used. + public static void Rethrow(BootFailedException bootFailedException) + { + if (bootFailedException == null) + throw new BootFailedException(DefaultMessage); + + // see https://stackoverflow.com/questions/57383 + // would that be the correct way to do it? + //ExceptionDispatchInfo.Capture(bootFailedException).Throw(); + + Exception e = bootFailedException; + var m = new StringBuilder(); + m.Append(DefaultMessage); + while (e != null) + { + m.Append($"\n\n-> {e.GetType().FullName}: {e.Message}"); + if (string.IsNullOrWhiteSpace(e.StackTrace) == false) + m.Append($"\n{e.StackTrace}"); + e = e.InnerException; + } + throw new BootFailedException(m.ToString()); + } } } diff --git a/src/Umbraco.Core/IMainDom.cs b/src/Umbraco.Core/IMainDom.cs new file mode 100644 index 0000000000..3a8cd13ff1 --- /dev/null +++ b/src/Umbraco.Core/IMainDom.cs @@ -0,0 +1,38 @@ +using System; + +namespace Umbraco.Core +{ + /// + /// Represents the main AppDomain running for a given application. + /// + /// + /// There can be only one "main" AppDomain running for a given application at a time. + /// It is possible to register against the MainDom and be notified when it is released. + /// + public interface IMainDom + { + /// + /// Gets a value indicating whether the current domain is the main domain. + /// + bool IsMainDom { get; } + + /// + /// Registers a resource that requires the current AppDomain to be the main domain to function. + /// + /// An action to execute before the AppDomain releases the main domain status. + /// An optional weight (lower goes first). + /// A value indicating whether it was possible to register. + bool Register(Action release, int weight = 100); + + /// + /// Registers a resource that requires the current AppDomain to be the main domain to function. + /// + /// An action to execute when registering. + /// An action to execute before the AppDomain releases the main domain status. + /// An optional weight (lower goes first). + /// A value indicating whether it was possible to register. + /// If registering is successful, then the action + /// is guaranteed to execute before the AppDomain releases the main domain status. + bool Register(Action install, Action release, int weight = 100); + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/IO/FileSystemExtensions.cs b/src/Umbraco.Core/IO/FileSystemExtensions.cs index 4b73e64e80..ade2c58b38 100644 --- a/src/Umbraco.Core/IO/FileSystemExtensions.cs +++ b/src/Umbraco.Core/IO/FileSystemExtensions.cs @@ -65,5 +65,33 @@ namespace Umbraco.Core.IO } fs.DeleteFile(tempFile); } + + /// + /// Unwraps a filesystem. + /// + /// + /// A filesystem can be wrapped in a (public) or a (internal), + /// and this method deals with the various wrappers and + /// + public static IFileSystem Unwrap(this IFileSystem filesystem) + { + var unwrapping = true; + while (unwrapping) + { + switch (filesystem) + { + case FileSystemWrapper wrapper: + filesystem = wrapper.InnerFileSystem; + break; + case ShadowWrapper shadow: + filesystem = shadow.InnerFileSystem; + break; + default: + unwrapping = false; + break; + } + } + return filesystem; + } } } diff --git a/src/Umbraco.Core/IO/FileSystemProviderAttribute.cs b/src/Umbraco.Core/IO/FileSystemProviderAttribute.cs deleted file mode 100644 index b3b6cb6b79..0000000000 --- a/src/Umbraco.Core/IO/FileSystemProviderAttribute.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Umbraco.Core.CodeAnnotations; - -namespace Umbraco.Core.IO -{ - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] - public class FileSystemProviderAttribute : Attribute - { - public string Alias { get; private set; } - - public FileSystemProviderAttribute(string alias) - { - Alias = alias; - } - } -} diff --git a/src/Umbraco.Core/IO/FileSystemWrapper.cs b/src/Umbraco.Core/IO/FileSystemWrapper.cs index 2c4377b89b..14d028c16d 100644 --- a/src/Umbraco.Core/IO/FileSystemWrapper.cs +++ b/src/Umbraco.Core/IO/FileSystemWrapper.cs @@ -16,103 +16,103 @@ namespace Umbraco.Core.IO /// public abstract class FileSystemWrapper : IFileSystem { - protected FileSystemWrapper(IFileSystem wrapped) + protected FileSystemWrapper(IFileSystem innerFileSystem) { - Wrapped = wrapped; + InnerFileSystem = innerFileSystem; } - internal IFileSystem Wrapped { get; set; } + internal IFileSystem InnerFileSystem { get; set; } public IEnumerable GetDirectories(string path) { - return Wrapped.GetDirectories(path); + return InnerFileSystem.GetDirectories(path); } public void DeleteDirectory(string path) { - Wrapped.DeleteDirectory(path); + InnerFileSystem.DeleteDirectory(path); } public void DeleteDirectory(string path, bool recursive) { - Wrapped.DeleteDirectory(path, recursive); + InnerFileSystem.DeleteDirectory(path, recursive); } public bool DirectoryExists(string path) { - return Wrapped.DirectoryExists(path); + return InnerFileSystem.DirectoryExists(path); } public void AddFile(string path, Stream stream) { - Wrapped.AddFile(path, stream); + InnerFileSystem.AddFile(path, stream); } public void AddFile(string path, Stream stream, bool overrideExisting) { - Wrapped.AddFile(path, stream, overrideExisting); + InnerFileSystem.AddFile(path, stream, overrideExisting); } public IEnumerable GetFiles(string path) { - return Wrapped.GetFiles(path); + return InnerFileSystem.GetFiles(path); } public IEnumerable GetFiles(string path, string filter) { - return Wrapped.GetFiles(path, filter); + return InnerFileSystem.GetFiles(path, filter); } public Stream OpenFile(string path) { - return Wrapped.OpenFile(path); + return InnerFileSystem.OpenFile(path); } public void DeleteFile(string path) { - Wrapped.DeleteFile(path); + InnerFileSystem.DeleteFile(path); } public bool FileExists(string path) { - return Wrapped.FileExists(path); + return InnerFileSystem.FileExists(path); } public string GetRelativePath(string fullPathOrUrl) { - return Wrapped.GetRelativePath(fullPathOrUrl); + return InnerFileSystem.GetRelativePath(fullPathOrUrl); } public string GetFullPath(string path) { - return Wrapped.GetFullPath(path); + return InnerFileSystem.GetFullPath(path); } public string GetUrl(string path) { - return Wrapped.GetUrl(path); + return InnerFileSystem.GetUrl(path); } public DateTimeOffset GetLastModified(string path) { - return Wrapped.GetLastModified(path); + return InnerFileSystem.GetLastModified(path); } public DateTimeOffset GetCreated(string path) { - return Wrapped.GetCreated(path); + return InnerFileSystem.GetCreated(path); } public long GetSize(string path) { - return Wrapped.GetSize(path); + return InnerFileSystem.GetSize(path); } - public bool CanAddPhysical => Wrapped.CanAddPhysical; + public bool CanAddPhysical => InnerFileSystem.CanAddPhysical; public void AddFile(string path, string physicalPath, bool overrideIfExists = true, bool copy = false) { - Wrapped.AddFile(path, physicalPath, overrideIfExists, copy); + InnerFileSystem.AddFile(path, physicalPath, overrideIfExists, copy); } } } diff --git a/src/Umbraco.Core/IO/FileSystems.cs b/src/Umbraco.Core/IO/FileSystems.cs index 62ce25dff0..7fc846319b 100644 --- a/src/Umbraco.Core/IO/FileSystems.cs +++ b/src/Umbraco.Core/IO/FileSystems.cs @@ -1,25 +1,18 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Configuration; -using System.IO; -using System.Linq; -using System.Reflection; using System.Threading; -using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Composing; namespace Umbraco.Core.IO { - public class FileSystems + public class FileSystems : IFileSystems { - private readonly IFileSystemProvidersSection _config; - private readonly ConcurrentSet _wrappers = new ConcurrentSet(); + private readonly IFactory _container; private readonly ILogger _logger; - private readonly ConcurrentDictionary _providerLookup = new ConcurrentDictionary(); - private readonly ConcurrentDictionary _filesystems = new ConcurrentDictionary(); + private readonly ConcurrentDictionary> _filesystems = new ConcurrentDictionary>(); // wrappers for shadow support private ShadowWrapper _macroPartialFileSystem; @@ -28,40 +21,48 @@ namespace Umbraco.Core.IO private ShadowWrapper _scriptsFileSystem; private ShadowWrapper _masterPagesFileSystem; private ShadowWrapper _mvcViewsFileSystem; - + // well-known file systems lazy initialization private object _wkfsLock = new object(); private bool _wkfsInitialized; - private object _wkfsObject; + private object _wkfsObject; // unused - private MediaFileSystem _mediaFileSystem; - + // shadow support + private readonly List _shadowWrappers = new List(); + private readonly object _shadowLocker = new object(); + private static Guid _shadowCurrentId = Guid.Empty; // static - unique!! #region Constructor // DI wants a public ctor - // but IScopeProviderInternal is not public - public FileSystems(ILogger logger) + public FileSystems(IFactory container, ILogger logger) { - // fixme inject config section => can be used by tests - _config = (FileSystemProvidersSection) ConfigurationManager.GetSection("umbracoConfiguration/FileSystemProviders"); + _container = container; _logger = logger; } // for tests only, totally unsafe internal void Reset() { - _wrappers.Clear(); - _providerLookup.Clear(); + _shadowWrappers.Clear(); _filesystems.Clear(); Volatile.Write(ref _wkfsInitialized, false); + _shadowCurrentId = Guid.Empty; } + // for tests only, totally unsafe + internal static void ResetShadowId() + { + _shadowCurrentId = Guid.Empty; + } + + // set by the scope provider when taking control of filesystems internal Func IsScoped { get; set; } = () => false; #endregion #region Well-Known FileSystems + /// public IFileSystem MacroPartialsFileSystem { get @@ -71,6 +72,7 @@ namespace Umbraco.Core.IO } } + /// public IFileSystem PartialViewsFileSystem { get @@ -80,6 +82,7 @@ namespace Umbraco.Core.IO } } + /// public IFileSystem StylesheetsFileSystem { get @@ -89,6 +92,7 @@ namespace Umbraco.Core.IO } } + /// public IFileSystem ScriptsFileSystem { get @@ -97,16 +101,18 @@ namespace Umbraco.Core.IO return _scriptsFileSystem; } } - + + /// public IFileSystem MasterPagesFileSystem { get { if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); - return _masterPagesFileSystem;// fixme - see 7.6?! + return _masterPagesFileSystem; } } + /// public IFileSystem MvcViewsFileSystem { get @@ -116,15 +122,6 @@ namespace Umbraco.Core.IO } } - public MediaFileSystem MediaFileSystem - { - get - { - if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); - return _mediaFileSystem; - } - } - private void EnsureWellKnownFileSystems() { LazyInitializer.EnsureInitialized(ref _wkfsObject, ref _wkfsInitialized, ref _wkfsLock, CreateWellKnownFileSystems); @@ -141,15 +138,20 @@ namespace Umbraco.Core.IO var masterPagesFileSystem = new PhysicalFileSystem(SystemDirectories.Masterpages); var mvcViewsFileSystem = new PhysicalFileSystem(SystemDirectories.MvcViews); - _macroPartialFileSystem = new ShadowWrapper(macroPartialFileSystem, "Views/MacroPartials", () => IsScoped()); - _partialViewsFileSystem = new ShadowWrapper(partialViewsFileSystem, "Views/Partials", () => IsScoped()); - _stylesheetsFileSystem = new ShadowWrapper(stylesheetsFileSystem, "css", () => IsScoped()); - _scriptsFileSystem = new ShadowWrapper(scriptsFileSystem, "scripts", () => IsScoped()); - _masterPagesFileSystem = new ShadowWrapper(masterPagesFileSystem, "masterpages", () => IsScoped()); - _mvcViewsFileSystem = new ShadowWrapper(mvcViewsFileSystem, "Views", () => IsScoped()); + _macroPartialFileSystem = new ShadowWrapper(macroPartialFileSystem, "Views/MacroPartials", IsScoped); + _partialViewsFileSystem = new ShadowWrapper(partialViewsFileSystem, "Views/Partials", IsScoped); + _stylesheetsFileSystem = new ShadowWrapper(stylesheetsFileSystem, "css", IsScoped); + _scriptsFileSystem = new ShadowWrapper(scriptsFileSystem, "scripts", IsScoped); + _masterPagesFileSystem = new ShadowWrapper(masterPagesFileSystem, "masterpages", IsScoped); + _mvcViewsFileSystem = new ShadowWrapper(mvcViewsFileSystem, "Views", IsScoped); - // filesystems obtained from GetFileSystemProvider are already wrapped and do not need to be wrapped again - _mediaFileSystem = GetFileSystemProvider(); + // fixme locking? + _shadowWrappers.Add(_macroPartialFileSystem); + _shadowWrappers.Add(_partialViewsFileSystem); + _shadowWrappers.Add(_stylesheetsFileSystem); + _shadowWrappers.Add(_scriptsFileSystem); + _shadowWrappers.Add(_masterPagesFileSystem); + _shadowWrappers.Add(_mvcViewsFileSystem); return null; } @@ -158,156 +160,28 @@ namespace Umbraco.Core.IO #region Providers - /// - /// used to cache the lookup of how to construct this object so we don't have to reflect each time. - /// - private class ProviderConstructionInfo - { - public object[] Parameters { get; set; } - public ConstructorInfo Constructor { get; set; } - //public string ProviderAlias { get; set; } - } - - /// - /// Gets an underlying (non-typed) filesystem supporting a strongly-typed filesystem. - /// - /// The alias of the strongly-typed filesystem. - /// The non-typed filesystem supporting the strongly-typed filesystem with the specified alias. - /// This method should not be used directly, used instead. - public IFileSystem GetUnderlyingFileSystemProvider(string alias) - { - return GetUnderlyingFileSystemProvider(alias, null); - } - - /// - /// Gets an underlying (non-typed) filesystem supporting a strongly-typed filesystem. - /// - /// The alias of the strongly-typed filesystem. - /// A fallback creator for the filesystem. - /// The non-typed filesystem supporting the strongly-typed filesystem with the specified alias. - /// This method should not be used directly, used instead. - internal IFileSystem GetUnderlyingFileSystemProvider(string alias, Func fallback) - { - // either get the constructor info from cache or create it and add to cache - var ctorInfo = _providerLookup.GetOrAdd(alias, _ => GetUnderlyingFileSystemCtor(alias, fallback)); - return ctorInfo == null ? fallback() : (IFileSystem) ctorInfo.Constructor.Invoke(ctorInfo.Parameters); - } - - private IFileSystem GetUnderlyingFileSystemNoCache(string alias, Func fallback) - { - var ctorInfo = GetUnderlyingFileSystemCtor(alias, fallback); - return ctorInfo == null ? fallback() : (IFileSystem) ctorInfo.Constructor.Invoke(ctorInfo.Parameters); - } - - private ProviderConstructionInfo GetUnderlyingFileSystemCtor(string alias, Func fallback) - { - // get config - if (_config.Providers.TryGetValue(alias, out var providerConfig) == false) - { - if (fallback != null) return null; - throw new ArgumentException($"No provider found with alias {alias}."); - } - - // get the filesystem type - var providerType = Type.GetType(providerConfig.Type); - if (providerType == null) - throw new InvalidOperationException($"Could not find type {providerConfig.Type}."); - - // ensure it implements IFileSystem - if (providerType.IsAssignableFrom(typeof (IFileSystem))) - throw new InvalidOperationException($"Type {providerType.FullName} does not implement IFileSystem."); - - // find a ctor matching the config parameters - var paramCount = providerConfig.Parameters?.Count ?? 0; - var constructor = providerType.GetConstructors().SingleOrDefault(x - => x.GetParameters().Length == paramCount && x.GetParameters().All(y => providerConfig.Parameters.Keys.Contains(y.Name))); - if (constructor == null) - throw new InvalidOperationException($"Type {providerType.FullName} has no ctor matching the {paramCount} configuration parameter(s)."); - - var parameters = new object[paramCount]; - if (providerConfig.Parameters != null) // keeps ReSharper happy - { - var allKeys = providerConfig.Parameters.Keys.ToArray(); - for (var i = 0; i < paramCount; i++) - parameters[i] = providerConfig.Parameters[allKeys[i]]; - } - - return new ProviderConstructionInfo - { - Constructor = constructor, - Parameters = parameters, - //ProviderAlias = s - }; - } - /// /// Gets a strongly-typed filesystem. /// /// The type of the filesystem. /// A strongly-typed filesystem of the specified type. /// - /// Ideally, this should cache the instances, but that would break backward compatibility, so we - /// only do it for our own MediaFileSystem - for everything else, it's the responsibility of the caller - /// to ensure that they maintain singletons. This is important for singletons, as each filesystem maintains - /// its own shadow and having multiple instances would lead to inconsistencies. /// Note that any filesystem created by this method *after* shadowing begins, will *not* be /// shadowing (and an exception will be thrown by the ShadowWrapper). /// - // fixme - should it change for v8? - public TFileSystem GetFileSystemProvider() + public TFileSystem GetFileSystem(IFileSystem supporting) where TFileSystem : FileSystemWrapper { - return GetFileSystemProvider(null); - } + if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); - /// - /// Gets a strongly-typed filesystem. - /// - /// The type of the filesystem. - /// A fallback creator for the inner filesystem. - /// A strongly-typed filesystem of the specified type. - /// - /// The fallback creator is used only if nothing is configured. - /// Ideally, this should cache the instances, but that would break backward compatibility, so we - /// only do it for our own MediaFileSystem - for everything else, it's the responsibility of the caller - /// to ensure that they maintain singletons. This is important for singletons, as each filesystem maintains - /// its own shadow and having multiple instances would lead to inconsistencies. - /// Note that any filesystem created by this method *after* shadowing begins, will *not* be - /// shadowing (and an exception will be thrown by the ShadowWrapper). - /// - public TFileSystem GetFileSystemProvider(Func fallback) - where TFileSystem : FileSystemWrapper - { - var alias = GetFileSystemAlias(); - return (TFileSystem)_filesystems.GetOrAdd(alias, _ => + return (TFileSystem) _filesystems.GetOrAdd(typeof(TFileSystem), _ => new Lazy(() => { - // gets the inner fs, create the strongly-typed fs wrapping the inner fs, register & return - // so we are double-wrapping here - // could be optimized by having FileSystemWrapper inherit from ShadowWrapper, maybe - var innerFs = GetUnderlyingFileSystemNoCache(alias, fallback); - var shadowWrapper = new ShadowWrapper(innerFs, "typed/" + alias, () => IsScoped()); - var fs = (IFileSystem) Activator.CreateInstance(typeof(TFileSystem), shadowWrapper); - _wrappers.Add(shadowWrapper); // keeping a reference to the wrapper - return fs; - }); - } + var name = typeof(TFileSystem).FullName; + if (name == null) throw new Exception("panic!"); - private string GetFileSystemAlias() - { - var fsType = typeof(TFileSystem); - - // validate the ctor - var constructor = fsType.GetConstructors().SingleOrDefault(x - => x.GetParameters().Length == 1 && TypeHelper.IsTypeAssignableFrom(x.GetParameters().Single().ParameterType)); - if (constructor == null) - throw new InvalidOperationException("Type " + fsType.FullName + " must inherit from FileSystemWrapper and have a constructor that accepts one parameter of type " + typeof(IFileSystem).FullName + "."); - - // find the attribute and get the alias - var attr = (FileSystemProviderAttribute)fsType.GetCustomAttributes(typeof(FileSystemProviderAttribute), false).SingleOrDefault(); - if (attr == null) - throw new InvalidOperationException("Type " + fsType.FullName + "is missing the required FileSystemProviderAttribute."); - - return attr.Alias; + var shadowWrapper = CreateShadowWrapper(supporting, "typed/" + name); + return _container.CreateInstance(shadowWrapper); + })).Value; } #endregion @@ -318,68 +192,75 @@ namespace Umbraco.Core.IO // shadowing is thread-safe, but entering and exiting shadow mode is not, and there is only one // global shadow for the entire application, so great care should be taken to ensure that the // application is *not* doing anything else when using a shadow. - // shadow applies to well-known filesystems *only* - at the moment, any other filesystem that would - // be created directly (via ctor) or via GetFileSystem is *not* shadowed. - - // shadow must be enabled in an app event handler before anything else ie before any filesystem - // is actually created and used - after, it is too late - enabling shadow has a neglictible perfs - // impact. - // NO! by the time an app event handler is instanciated it is already too late, see note in ctor. - //internal void EnableShadow() - //{ - // if (_mvcViewsFileSystem != null) // test one of the fs... - // throw new InvalidOperationException("Cannot enable shadow once filesystems have been created."); - // _shadowEnabled = true; - //} internal ICompletable Shadow(Guid id) { if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); - var typed = _wrappers.ToArray(); - var wrappers = new ShadowWrapper[typed.Length + 6]; - var i = 0; - while (i < typed.Length) wrappers[i] = typed[i++]; - wrappers[i++] = _macroPartialFileSystem; - wrappers[i++] = _partialViewsFileSystem; - wrappers[i++] = _stylesheetsFileSystem; - wrappers[i++] = _scriptsFileSystem; - wrappers[i++] = _masterPagesFileSystem; - wrappers[i] = _mvcViewsFileSystem; + return new ShadowFileSystems(this, id); // will invoke BeginShadow and EndShadow + } - return new ShadowFileSystems(id, wrappers, _logger); + internal void BeginShadow(Guid id) + { + lock (_shadowLocker) + { + // if we throw here, it means that something very wrong happened. + if (_shadowCurrentId != Guid.Empty) + throw new InvalidOperationException("Already shadowing."); + _shadowCurrentId = id; + + _logger.Debug("Shadow '{ShadowId}'", id); + + foreach (var wrapper in _shadowWrappers) + wrapper.Shadow(id); + } + } + + internal void EndShadow(Guid id, bool completed) + { + lock (_shadowLocker) + { + // if we throw here, it means that something very wrong happened. + if (_shadowCurrentId == Guid.Empty) + throw new InvalidOperationException("Not shadowing."); + if (id != _shadowCurrentId) + throw new InvalidOperationException("Not the current shadow."); + + _logger.Debug("UnShadow '{ShadowId}' {Status}", id, completed ? "complete" : "abort"); + + var exceptions = new List(); + foreach (var wrapper in _shadowWrappers) + { + try + { + // this may throw an AggregateException if some of the changes could not be applied + wrapper.UnShadow(completed); + } + catch (AggregateException ae) + { + exceptions.Add(ae); + } + } + + _shadowCurrentId = Guid.Empty; + + if (exceptions.Count > 0) + throw new AggregateException(completed ? "Failed to apply all changes (see exceptions)." : "Failed to abort (see exceptions).", exceptions); + } + } + + private ShadowWrapper CreateShadowWrapper(IFileSystem filesystem, string shadowPath) + { + lock (_shadowLocker) + { + var wrapper = new ShadowWrapper(filesystem, shadowPath, IsScoped); + if (_shadowCurrentId != Guid.Empty) + wrapper.Shadow(_shadowCurrentId); + _shadowWrappers.Add(wrapper); + return wrapper; + } } #endregion - - private class ConcurrentSet - where T : class - { - private readonly HashSet _set = new HashSet(); - - public void Add(T item) - { - lock (_set) - { - _set.Add(item); - } - } - - public void Clear() - { - lock (_set) - { - _set.Clear(); - } - } - - public T[] ToArray() - { - lock (_set) - { - return _set.ToArray(); - } - } - } } } diff --git a/src/Umbraco.Core/IO/IFileSystem.cs b/src/Umbraco.Core/IO/IFileSystem.cs index 115cb8a5c1..14b015a468 100644 --- a/src/Umbraco.Core/IO/IFileSystem.cs +++ b/src/Umbraco.Core/IO/IFileSystem.cs @@ -5,7 +5,7 @@ using System.IO; namespace Umbraco.Core.IO { /// - /// Provides methods allowing the manipulation of files within an Umbraco application. + /// Provides methods allowing the manipulation of files. /// public interface IFileSystem { diff --git a/src/Umbraco.Core/IO/IFileSystems.cs b/src/Umbraco.Core/IO/IFileSystems.cs new file mode 100644 index 0000000000..d74ad48145 --- /dev/null +++ b/src/Umbraco.Core/IO/IFileSystems.cs @@ -0,0 +1,38 @@ +namespace Umbraco.Core.IO +{ + /// + /// Provides the system filesystems. + /// + public interface IFileSystems + { + /// + /// Gets the macro partials filesystem. + /// + IFileSystem MacroPartialsFileSystem { get; } + + /// + /// Gets the partial views filesystem. + /// + IFileSystem PartialViewsFileSystem { get; } + + /// + /// Gets the stylesheets filesystem. + /// + IFileSystem StylesheetsFileSystem { get; } + + /// + /// Gets the scripts filesystem. + /// + IFileSystem ScriptsFileSystem { get; } + + /// + /// Gets the masterpages filesystem. + /// + IFileSystem MasterPagesFileSystem { get; } + + /// + /// Gets the MVC views filesystem. + /// + IFileSystem MvcViewsFileSystem { get; } + } +} diff --git a/src/Umbraco.Core/IO/IMediaFileSystem.cs b/src/Umbraco.Core/IO/IMediaFileSystem.cs new file mode 100644 index 0000000000..ed88516135 --- /dev/null +++ b/src/Umbraco.Core/IO/IMediaFileSystem.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Umbraco.Core.Models; + +namespace Umbraco.Core.IO +{ + /// + /// Provides methods allowing the manipulation of media files. + /// + public interface IMediaFileSystem : IFileSystem + { + /// + /// Delete media files. + /// + /// Files to delete (filesystem-relative paths). + void DeleteMediaFiles(IEnumerable files); + + /// + /// Gets the file path of a media file. + /// + /// The file name. + /// The unique identifier of the content/media owning the file. + /// The unique identifier of the property type owning the file. + /// The filesystem-relative path to the media file. + /// With the old media path scheme, this CREATES a new media path each time it is invoked. + string GetMediaPath(string filename, Guid cuid, Guid puid); + + /// + /// Gets the file path of a media file. + /// + /// The file name. + /// A previous file path. + /// The unique identifier of the content/media owning the file. + /// The unique identifier of the property type owning the file. + /// The filesystem-relative path to the media file. + /// In the old, legacy, number-based scheme, we try to re-use the media folder + /// specified by . Else, we CREATE a new one. Each time we are invoked. + string GetMediaPath(string filename, string prevpath, Guid cuid, Guid puid); + + /// + /// Stores a media file associated to a property of a content item. + /// + /// The content item owning the media file. + /// The property type owning the media file. + /// The media file name. + /// A stream containing the media bytes. + /// An optional filesystem-relative filepath to the previous media file. + /// The filesystem-relative filepath to the media file. + /// + /// The file is considered "owned" by the content/propertyType. + /// If an is provided then that file (and associated thumbnails if any) is deleted + /// before the new file is saved, and depending on the media path scheme, the folder may be reused for the new file. + /// + string StoreFile(IContentBase content, PropertyType propertyType, string filename, Stream filestream, string oldpath); + + /// + /// Copies a media file as a new media file, associated to a property of a content item. + /// + /// The content item owning the copy of the media file. + /// The property type owning the copy of the media file. + /// The filesystem-relative path to the source media file. + /// The filesystem-relative path to the copy of the media file. + string CopyFile(IContentBase content, PropertyType propertyType, string sourcepath); + } +} diff --git a/src/Umbraco.Core/IO/IMediaPathScheme.cs b/src/Umbraco.Core/IO/IMediaPathScheme.cs index 5cfb43ed77..9a38cdc74f 100644 --- a/src/Umbraco.Core/IO/IMediaPathScheme.cs +++ b/src/Umbraco.Core/IO/IMediaPathScheme.cs @@ -7,35 +7,27 @@ namespace Umbraco.Core.IO /// public interface IMediaPathScheme { - // fixme - // to anyone finding this code: YES the Initialize() method is CompletelyBroken™ (temporal whatever) - // but at the moment, the media filesystem wants a scheme which wants a filesystem, and it's all - // convoluted due to how filesystems are managed in FileSystems - clear that part first! - - /// - /// Initialize. - /// - void Initialize(IFileSystem filesystem); - /// /// Gets a media file path. /// + /// The media filesystem. /// The (content, media) item unique identifier. /// The property type unique identifier. /// The file name. /// A previous filename. /// The filesystem-relative complete file path. - string GetFilePath(Guid itemGuid, Guid propertyGuid, string filename, string previous = null); + string GetFilePath(IMediaFileSystem fileSystem, Guid itemGuid, Guid propertyGuid, string filename, string previous = null); /// /// Gets the directory that can be deleted when the file is deleted. /// + /// The media filesystem. /// The filesystem-relative path of the file. /// The filesystem-relative path of the directory. /// /// The directory, and anything below it, will be deleted. /// Can return null (or empty) when no directory should be deleted. /// - string GetDeleteDirectory(string filepath); + string GetDeleteDirectory(IMediaFileSystem fileSystem, string filepath); } } diff --git a/src/Umbraco.Core/IO/MediaFileSystem.cs b/src/Umbraco.Core/IO/MediaFileSystem.cs index fd58f573cc..2ce1230bcc 100644 --- a/src/Umbraco.Core/IO/MediaFileSystem.cs +++ b/src/Umbraco.Core/IO/MediaFileSystem.cs @@ -1,15 +1,15 @@ using System; using System.Collections.Generic; +using System.Configuration; using System.IO; using System.Linq; using System.Threading.Tasks; -using LightInject; -using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Exceptions; using Umbraco.Core.Logging; using Umbraco.Core.Media; - using Umbraco.Core.Models; namespace Umbraco.Core.IO @@ -17,78 +17,32 @@ namespace Umbraco.Core.IO /// /// A custom file system provider for media /// - [FileSystemProvider("media")] - public class MediaFileSystem : FileSystemWrapper + public class MediaFileSystem : FileSystemWrapper, IMediaFileSystem { - public MediaFileSystem(IFileSystem wrapped) - : base(wrapped) - { - // due to how FileSystems is written at the moment, the ctor cannot be used to inject - // dependencies, so we have to rely on property injection for anything we might need - Current.Container.InjectProperties(this); - MediaPathScheme.Initialize(this); - } + private readonly IMediaPathScheme _mediaPathScheme; + private readonly IContentSection _contentConfig; + private readonly ILogger _logger; - [Inject] - internal IMediaPathScheme MediaPathScheme { get; set; } - - [Inject] - internal IContentSection ContentConfig { get; set; } - - [Inject] - internal ILogger Logger { get; set; } - /// - /// Deletes all files passed in. + /// Initializes a new instance of the class. /// - /// - /// - /// - internal bool DeleteFiles(IEnumerable files, Action onError = null) + public MediaFileSystem(IFileSystem innerFileSystem, IContentSection contentConfig, IMediaPathScheme mediaPathScheme, ILogger logger) + : base(innerFileSystem) { - //ensure duplicates are removed - files = files.Distinct(); - - var allsuccess = true; - var rootRelativePath = GetRelativePath("/"); - - Parallel.ForEach(files, file => - { - try - { - if (file.IsNullOrWhiteSpace()) return; - - var relativeFilePath = GetRelativePath(file); - if (FileExists(relativeFilePath) == false) return; - - var parentDirectory = Path.GetDirectoryName(relativeFilePath); - - // don't want to delete the media folder if not using directories. - if (ContentConfig.UploadAllowDirectories && parentDirectory != rootRelativePath) - { - //issue U4-771: if there is a parent directory the recursive parameter should be true - DeleteDirectory(parentDirectory, string.IsNullOrEmpty(parentDirectory) == false); - } - else - { - DeleteFile(file); - } - } - catch (Exception e) - { - onError?.Invoke(file, e); - allsuccess = false; - } - }); - - return allsuccess; + _contentConfig = contentConfig; + _mediaPathScheme = mediaPathScheme; + _logger = logger; } + /// public void DeleteMediaFiles(IEnumerable files) { files = files.Distinct(); - Parallel.ForEach(files, file => + // kinda try to keep things under control + var options = new ParallelOptions { MaxDegreeOfParallelism = 20 }; + + Parallel.ForEach(files, options, file => { try { @@ -96,73 +50,44 @@ namespace Umbraco.Core.IO if (FileExists(file) == false) return; DeleteFile(file); - var directory = MediaPathScheme.GetDeleteDirectory(file); + var directory = _mediaPathScheme.GetDeleteDirectory(this, file); if (!directory.IsNullOrWhiteSpace()) DeleteDirectory(directory, true); } catch (Exception e) { - Logger.Error(e, "Failed to delete attached file '{File}'", file); + _logger.Error(e, "Failed to delete media file '{File}'.", file); } }); } #region Media Path - /// - /// Gets the file path of a media file. - /// - /// The file name. - /// The unique identifier of the content/media owning the file. - /// The unique identifier of the property type owning the file. - /// The filesystem-relative path to the media file. - /// With the old media path scheme, this CREATES a new media path each time it is invoked. + /// public string GetMediaPath(string filename, Guid cuid, Guid puid) { filename = Path.GetFileName(filename); if (filename == null) throw new ArgumentException("Cannot become a safe filename.", nameof(filename)); filename = IOHelper.SafeFileName(filename.ToLowerInvariant()); - return MediaPathScheme.GetFilePath(cuid, puid, filename); + return _mediaPathScheme.GetFilePath(this, cuid, puid, filename); } - /// - /// Gets the file path of a media file. - /// - /// The file name. - /// A previous file path. - /// The unique identifier of the content/media owning the file. - /// The unique identifier of the property type owning the file. - /// The filesystem-relative path to the media file. - /// In the old, legacy, number-based scheme, we try to re-use the media folder - /// specified by . Else, we CREATE a new one. Each time we are invoked. + /// public string GetMediaPath(string filename, string prevpath, Guid cuid, Guid puid) { filename = Path.GetFileName(filename); if (filename == null) throw new ArgumentException("Cannot become a safe filename.", nameof(filename)); filename = IOHelper.SafeFileName(filename.ToLowerInvariant()); - return MediaPathScheme.GetFilePath(cuid, puid, filename, prevpath); + return _mediaPathScheme.GetFilePath(this, cuid, puid, filename, prevpath); } #endregion #region Associated Media Files - /// - /// Stores a media file associated to a property of a content item. - /// - /// The content item owning the media file. - /// The property type owning the media file. - /// The media file name. - /// A stream containing the media bytes. - /// An optional filesystem-relative filepath to the previous media file. - /// The filesystem-relative filepath to the media file. - /// - /// The file is considered "owned" by the content/propertyType. - /// If an is provided then that file (and associated thumbnails if any) is deleted - /// before the new file is saved, and depending on the media path scheme, the folder may be reused for the new file. - /// + /// public string StoreFile(IContentBase content, PropertyType propertyType, string filename, Stream filestream, string oldpath) { if (content == null) throw new ArgumentNullException(nameof(content)); @@ -181,13 +106,7 @@ namespace Umbraco.Core.IO return filepath; } - /// - /// Copies a media file as a new media file, associated to a property of a content item. - /// - /// The content item owning the copy of the media file. - /// The property type owning the copy of the media file. - /// The filesystem-relative path to the source media file. - /// The filesystem-relative path to the copy of the media file. + /// public string CopyFile(IContentBase content, PropertyType propertyType, string sourcepath) { if (content == null) throw new ArgumentNullException(nameof(content)); @@ -204,9 +123,6 @@ namespace Umbraco.Core.IO return filepath; } - - #endregion - } } diff --git a/src/Umbraco.Core/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs b/src/Umbraco.Core/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs index 37c6a7b209..59f8213414 100644 --- a/src/Umbraco.Core/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs +++ b/src/Umbraco.Core/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs @@ -12,11 +12,7 @@ namespace Umbraco.Core.IO.MediaPathSchemes public class CombinedGuidsMediaPathScheme : IMediaPathScheme { /// - public void Initialize(IFileSystem filesystem) - { } - - /// - public string GetFilePath(Guid itemGuid, Guid propertyGuid, string filename, string previous = null) + public string GetFilePath(IMediaFileSystem fileSystem, Guid itemGuid, Guid propertyGuid, string filename, string previous = null) { // assumes that cuid and puid keys can be trusted - and that a single property type // for a single content cannot store two different files with the same name @@ -25,6 +21,6 @@ namespace Umbraco.Core.IO.MediaPathSchemes } /// - public string GetDeleteDirectory(string filepath) => Path.GetDirectoryName(filepath); + public string GetDeleteDirectory(IMediaFileSystem fileSystem, string filepath) => Path.GetDirectoryName(filepath); } } diff --git a/src/Umbraco.Core/IO/MediaPathSchemes/OriginalMediaPathScheme.cs b/src/Umbraco.Core/IO/MediaPathSchemes/OriginalMediaPathScheme.cs index 1ed2ea59ce..1948a9f662 100644 --- a/src/Umbraco.Core/IO/MediaPathSchemes/OriginalMediaPathScheme.cs +++ b/src/Umbraco.Core/IO/MediaPathSchemes/OriginalMediaPathScheme.cs @@ -2,6 +2,7 @@ using System.Globalization; using System.IO; using System.Threading; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration; namespace Umbraco.Core.IO.MediaPathSchemes @@ -17,18 +18,11 @@ namespace Umbraco.Core.IO.MediaPathSchemes public class OriginalMediaPathScheme : IMediaPathScheme { private readonly object _folderCounterLock = new object(); - private IFileSystem _filesystem; private long _folderCounter; private bool _folderCounterInitialized; /// - public void Initialize(IFileSystem filesystem) - { - _filesystem = filesystem; - } - - /// - public string GetFilePath(Guid itemGuid, Guid propertyGuid, string filename, string previous = null) + public string GetFilePath(IMediaFileSystem fileSystem, Guid itemGuid, Guid propertyGuid, string filename, string previous = null) { string directory; if (previous != null) @@ -37,45 +31,45 @@ namespace Umbraco.Core.IO.MediaPathSchemes // prevpath should be "/" OR "-" // and we want to reuse the "" part, so try to find it - var sep = UmbracoConfig.For.UmbracoSettings().Content.UploadAllowDirectories ? "/" : "-"; + var sep = Current.Configs.Settings().Content.UploadAllowDirectories ? "/" : "-"; var pos = previous.IndexOf(sep, StringComparison.Ordinal); var s = pos > 0 ? previous.Substring(0, pos) : null; - directory = pos > 0 && int.TryParse(s, out _) ? s : GetNextDirectory(); + directory = pos > 0 && int.TryParse(s, out _) ? s : GetNextDirectory(fileSystem); } else { - directory = GetNextDirectory(); + directory = GetNextDirectory(fileSystem); } if (directory == null) throw new InvalidOperationException("Cannot use a null directory."); - return UmbracoConfig.For.UmbracoSettings().Content.UploadAllowDirectories + return Current.Configs.Settings().Content.UploadAllowDirectories ? Path.Combine(directory, filename).Replace('\\', '/') : directory + "-" + filename; } /// - public string GetDeleteDirectory(string filepath) + public string GetDeleteDirectory(IMediaFileSystem fileSystem, string filepath) { return Path.GetDirectoryName(filepath); } - private string GetNextDirectory() + private string GetNextDirectory(IFileSystem fileSystem) { - EnsureFolderCounterIsInitialized(); + EnsureFolderCounterIsInitialized(fileSystem); return Interlocked.Increment(ref _folderCounter).ToString(CultureInfo.InvariantCulture); } - private void EnsureFolderCounterIsInitialized() + private void EnsureFolderCounterIsInitialized(IFileSystem fileSystem) { lock (_folderCounterLock) { if (_folderCounterInitialized) return; _folderCounter = 1000; // seed - var directories = _filesystem.GetDirectories(""); + var directories = fileSystem.GetDirectories(""); foreach (var directory in directories) { if (long.TryParse(directory, out var folderNumber) && folderNumber > _folderCounter) diff --git a/src/Umbraco.Core/IO/MediaPathSchemes/TwoGuidsMediaPathScheme.cs b/src/Umbraco.Core/IO/MediaPathSchemes/TwoGuidsMediaPathScheme.cs index 4a6fdfcdbe..3c06e295e6 100644 --- a/src/Umbraco.Core/IO/MediaPathSchemes/TwoGuidsMediaPathScheme.cs +++ b/src/Umbraco.Core/IO/MediaPathSchemes/TwoGuidsMediaPathScheme.cs @@ -12,17 +12,13 @@ namespace Umbraco.Core.IO.MediaPathSchemes public class TwoGuidsMediaPathScheme : IMediaPathScheme { /// - public void Initialize(IFileSystem filesystem) - { } - - /// - public string GetFilePath(Guid itemGuid, Guid propertyGuid, string filename, string previous = null) + public string GetFilePath(IMediaFileSystem fileSystem, Guid itemGuid, Guid propertyGuid, string filename, string previous = null) { return Path.Combine(itemGuid.ToString("N"), propertyGuid.ToString("N"), filename).Replace('\\', '/'); } /// - public string GetDeleteDirectory(string filepath) + public string GetDeleteDirectory(IMediaFileSystem fileSystem, string filepath) { return Path.GetDirectoryName(filepath); } diff --git a/src/Umbraco.Core/IO/ShadowFileSystems.cs b/src/Umbraco.Core/IO/ShadowFileSystems.cs index 289667b0db..bce0cc6df7 100644 --- a/src/Umbraco.Core/IO/ShadowFileSystems.cs +++ b/src/Umbraco.Core/IO/ShadowFileSystems.cs @@ -1,158 +1,36 @@ using System; -using System.Collections.Generic; -using Umbraco.Core.Logging; namespace Umbraco.Core.IO { + // shadow filesystems is definitively ... too convoluted + internal class ShadowFileSystems : ICompletable { - // note: taking a reference to the _manager instead of using manager.Current - // to avoid using Current everywhere but really, we support only 1 scope at - // a time, not multiple scopes in case of multiple managers (not supported) - - // fixme - why are we managing logical call context here? should be bound - // to the current scope, always => REFACTOR! but there should be something in - // place (static?) to ensure we only have one concurrent shadow FS? - // - // => yes, that's _currentId - need to cleanup this entirely - // and, we probably need a way to stop shadowing entirely without cycling the app - - private const string ItemKey = "Umbraco.Core.IO.ShadowFileSystems"; - - private static readonly object Locker = new object(); - private static Guid _currentId = Guid.Empty; - - private readonly Guid _id; - private readonly ShadowWrapper[] _wrappers; - private readonly ILogger _logger; - + private readonly FileSystems _fileSystems; private bool _completed; - //static ShadowFileSystems() - //{ - // SafeCallContext.Register( - // () => - // { - // var scope = CallContext.LogicalGetData(ItemKey); - // CallContext.FreeNamedDataSlot(ItemKey); - // return scope; - // }, - // o => - // { - // if (CallContext.LogicalGetData(ItemKey) != null) throw new InvalidOperationException(); - // if (o != null) CallContext.LogicalSetData(ItemKey, o); - // }); - //} - - public ShadowFileSystems(Guid id, ShadowWrapper[] wrappers, ILogger logger) + // invoked by the filesystems when shadowing + public ShadowFileSystems(FileSystems fileSystems, Guid id) { - lock (Locker) - { - if (_currentId != Guid.Empty) - throw new InvalidOperationException("Already shadowing."); - _currentId = id; - } + _fileSystems = fileSystems; + Id = id; - _logger = logger; - _logger.Debug("Shadow '{ShadowId}'", id); - _id = id; - - _wrappers = wrappers; - foreach (var wrapper in _wrappers) - wrapper.Shadow(id); - } - - // fixme - remove - //// internal for tests + FileSystems - //// do NOT use otherwise - //internal static ShadowFileSystems CreateScope(Guid id, ShadowWrapper[] wrappers, ILogger logger) - //{ - // lock (Locker) - // { - // if (CallContext.LogicalGetData(ItemKey) != null) throw new InvalidOperationException("Already shadowing."); - // CallContext.LogicalSetData(ItemKey, ItemKey); // value does not matter - // } - // return new ShadowFileSystems(id, wrappers, logger); - //} - - //internal static bool InScope => NoScope == false; - - //internal static bool NoScope => CallContext.LogicalGetData(ItemKey) == null; - - public void Complete() - { - _completed = true; - //lock (Locker) - //{ - // _logger.Debug("UnShadow " + _id + " (complete)."); - - // var exceptions = new List(); - // foreach (var wrapper in _wrappers) - // { - // try - // { - // // this may throw an AggregateException if some of the changes could not be applied - // wrapper.UnShadow(true); - // } - // catch (AggregateException ae) - // { - // exceptions.Add(ae); - // } - // } - - // if (exceptions.Count > 0) - // throw new AggregateException("Failed to apply all changes (see exceptions).", exceptions); - - // // last, & *only* if successful (otherwise we'll unshadow & cleanup as best as we can) - // CallContext.FreeNamedDataSlot(ItemKey); - //} - } - - public void Dispose() - { - lock (Locker) - { - _logger.Debug("UnShadow '{ShadowId}' {Status}", _id, _completed ? "complete" : "abort"); - - var exceptions = new List(); - foreach (var wrapper in _wrappers) - { - try - { - // this may throw an AggregateException if some of the changes could not be applied - wrapper.UnShadow(_completed); - } - catch (AggregateException ae) - { - exceptions.Add(ae); - } - } - - _currentId = Guid.Empty; - - if (exceptions.Count > 0) - throw new AggregateException(_completed ? "Failed to apply all changes (see exceptions)." : "Failed to abort (see exceptions).", exceptions); - - //if (CallContext.LogicalGetData(ItemKey) == null) return; - - //try - //{ - // _logger.Debug("UnShadow " + _id + " (abort)"); - // foreach (var wrapper in _wrappers) - // wrapper.UnShadow(false); // should not throw - //} - //finally - //{ - // // last, & always - // CallContext.FreeNamedDataSlot(ItemKey); - //} - } + _fileSystems.BeginShadow(id); } // for tests - internal static void ResetId() + public Guid Id { get; } + + // invoked by the scope when exiting, if completed + public void Complete() { - _currentId = Guid.Empty; + _completed = true; + } + + // invoked by the scope when exiting + public void Dispose() + { + _fileSystems.EndShadow(Id, _completed); } } } diff --git a/src/Umbraco.Core/IO/ShadowWrapper.cs b/src/Umbraco.Core/IO/ShadowWrapper.cs index 94bd61b162..6493238391 100644 --- a/src/Umbraco.Core/IO/ShadowWrapper.cs +++ b/src/Umbraco.Core/IO/ShadowWrapper.cs @@ -75,6 +75,8 @@ namespace Umbraco.Core.IO } } + public IFileSystem InnerFileSystem => _innerFileSystem; + private IFileSystem FileSystem { get diff --git a/src/Umbraco.Core/IRuntime.cs b/src/Umbraco.Core/IRuntime.cs index 37b265ad77..d433dde12d 100644 --- a/src/Umbraco.Core/IRuntime.cs +++ b/src/Umbraco.Core/IRuntime.cs @@ -1,4 +1,4 @@ -using LightInject; +using Umbraco.Core.Composing; namespace Umbraco.Core { @@ -10,8 +10,14 @@ namespace Umbraco.Core /// /// Boots the runtime. /// - /// The application service container. - void Boot(ServiceContainer container); + /// The application register. + /// The application factory. + IFactory Boot(IRegister register); + + /// + /// Gets the runtime state. + /// + IRuntimeState State { get; } /// /// Terminates the runtime. diff --git a/src/Umbraco.Core/Logging/IProfilingLogger.cs b/src/Umbraco.Core/Logging/IProfilingLogger.cs new file mode 100644 index 0000000000..1d7c231e95 --- /dev/null +++ b/src/Umbraco.Core/Logging/IProfilingLogger.cs @@ -0,0 +1,40 @@ +using System; + +namespace Umbraco.Core.Logging +{ + /// + /// Defines the profiling logging service. + /// + public interface IProfilingLogger : ILogger + { + /// + /// Profiles an action and log as information messages. + /// + DisposableTimer TraceDuration(string startMessage); + + /// + /// Profiles an action and log as information messages. + /// + DisposableTimer TraceDuration(string startMessage, string completeMessage, string failMessage = null); + + /// + /// Profiles an action and log as information messages. + /// + DisposableTimer TraceDuration(Type loggerType, string startMessage, string completeMessage, string failMessage = null); + + /// + /// Profiles an action and log as debug messages. + /// + DisposableTimer DebugDuration(string startMessage); + + /// + /// Profiles an action and log as debug messages. + /// + DisposableTimer DebugDuration(string startMessage, string completeMessage, string failMessage = null, int thresholdMilliseconds = 0); + + /// + /// Profiles an action and log as debug messages. + /// + DisposableTimer DebugDuration(Type loggerType, string startMessage, string completeMessage, string failMessage = null, int thresholdMilliseconds = 0); + } +} diff --git a/src/Umbraco.Core/Logging/MessageTemplates.cs b/src/Umbraco.Core/Logging/MessageTemplates.cs index 194d47c339..47de1230ff 100644 --- a/src/Umbraco.Core/Logging/MessageTemplates.cs +++ b/src/Umbraco.Core/Logging/MessageTemplates.cs @@ -1,6 +1,10 @@ using System; +using System.IO; using System.Linq; +using System.Text; using Serilog; +using Serilog.Events; +using Serilog.Parsing; namespace Umbraco.Core.Logging { @@ -29,7 +33,23 @@ namespace Umbraco.Core.Logging if (!bound) throw new FormatException($"Could not format message \"{messageTemplate}\" with {args.Length} args."); - return parsedTemplate.Render(boundProperties.ToDictionary(x => x.Name, x => x.Value)); + var values = boundProperties.ToDictionary(x => x.Name, x => x.Value); + + // this ends up putting every string parameter between quotes + //return parsedTemplate.Render(values); + + // this does not + var tw = new StringWriter(); + foreach (var t in parsedTemplate.Tokens) + { + if (t is PropertyToken pt && + values.TryGetValue(pt.PropertyName, out var propVal) && + (propVal as ScalarValue)?.Value is string s) + tw.Write(s); + else + t.Render(values, tw); + } + return tw.ToString(); } } } diff --git a/src/Umbraco.Core/Logging/ProfilingLogger.cs b/src/Umbraco.Core/Logging/ProfilingLogger.cs index 80560e923a..d642926147 100644 --- a/src/Umbraco.Core/Logging/ProfilingLogger.cs +++ b/src/Umbraco.Core/Logging/ProfilingLogger.cs @@ -3,14 +3,23 @@ namespace Umbraco.Core.Logging { /// - /// Provides debug or trace logging with duration management. + /// Provides logging and profiling services. /// - public sealed class ProfilingLogger + public sealed class ProfilingLogger : IProfilingLogger { + /// + /// Gets the underlying implementation. + /// public ILogger Logger { get; } + /// + /// Gets the underlying implementation. + /// public IProfiler Profiler { get; } + /// + /// Initializes a new instance of the class. + /// public ProfilingLogger(ILogger logger, IProfiler profiler) { Logger = logger ?? throw new ArgumentNullException(nameof(logger)); @@ -52,5 +61,72 @@ namespace Umbraco.Core.Logging ? new DisposableTimer(Logger, LogLevel.Debug, Profiler, loggerType, startMessage, completeMessage, failMessage, thresholdMilliseconds) : null; } + + #region ILogger + + public bool IsEnabled(Type reporting, LogLevel level) + => Logger.IsEnabled(reporting, level); + + public void Fatal(Type reporting, Exception exception, string message) + => Logger.Fatal(reporting, exception, message); + + public void Fatal(Type reporting, Exception exception) + => Logger.Fatal(reporting, exception); + + public void Fatal(Type reporting, string message) + => Logger.Fatal(reporting, message); + + public void Fatal(Type reporting, Exception exception, string messageTemplate, params object[] propertyValues) + => Logger.Fatal(reporting, exception, messageTemplate, propertyValues); + + public void Fatal(Type reporting, string messageTemplate, params object[] propertyValues) + => Logger.Fatal(reporting, messageTemplate, propertyValues); + + public void Error(Type reporting, Exception exception, string message) + => Logger.Error(reporting, exception, message); + + public void Error(Type reporting, Exception exception) + => Logger.Error(reporting, exception); + + public void Error(Type reporting, string message) + => Logger.Error(reporting, message); + + public void Error(Type reporting, Exception exception, string messageTemplate, params object[] propertyValues) + => Logger.Error(reporting, exception, messageTemplate, propertyValues); + + public void Error(Type reporting, string messageTemplate, params object[] propertyValues) + => Logger.Error(reporting, messageTemplate, propertyValues); + + public void Warn(Type reporting, string message) + => Logger.Warn(reporting, message); + + public void Warn(Type reporting, string messageTemplate, params object[] propertyValues) + => Logger.Warn(reporting, messageTemplate, propertyValues); + + public void Warn(Type reporting, Exception exception, string message) + => Logger.Warn(reporting, exception, message); + + public void Warn(Type reporting, Exception exception, string messageTemplate, params object[] propertyValues) + => Logger.Warn(reporting, exception, messageTemplate, propertyValues); + + public void Info(Type reporting, string message) + => Logger.Info(reporting, message); + + public void Info(Type reporting, string messageTemplate, params object[] propertyValues) + => Logger.Info(reporting, messageTemplate, propertyValues); + + public void Debug(Type reporting, string message) + => Logger.Debug(reporting, message); + + public void Debug(Type reporting, string messageTemplate, params object[] propertyValues) + => Logger.Debug(reporting, messageTemplate, propertyValues); + + public void Verbose(Type reporting, string message) + => Logger.Verbose(reporting, message); + + public void Verbose(Type reporting, string messageTemplate, params object[] propertyValues) + => Logger.Verbose(reporting, messageTemplate, propertyValues); + + #endregion } } diff --git a/src/Umbraco.Core/Logging/Serilog/SerilogLogger.cs b/src/Umbraco.Core/Logging/Serilog/SerilogLogger.cs index c23a43a820..17d86b45e1 100644 --- a/src/Umbraco.Core/Logging/Serilog/SerilogLogger.cs +++ b/src/Umbraco.Core/Logging/Serilog/SerilogLogger.cs @@ -4,6 +4,7 @@ using System.Reflection; using System.Threading; using Serilog; using Serilog.Events; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Diagnostics; @@ -165,7 +166,7 @@ namespace Umbraco.Core.Logging.Serilog messageTemplate += "\r\nThe thread has been aborted, because the request has timed out."; // dump if configured, or if stacktrace contains Monitor.ReliableEnter - dump = UmbracoConfig.For.CoreDebug().DumpOnTimeoutThreadAbort || IsMonitorEnterThreadAbortException(exception); + dump = Current.Configs.CoreDebug().DumpOnTimeoutThreadAbort || IsMonitorEnterThreadAbortException(exception); // dump if it is ok to dump (might have a cap on number of dump...) dump &= MiniDump.OkToDump(); @@ -216,9 +217,9 @@ namespace Umbraco.Core.Logging.Serilog /// public void Warn(Type reporting, string message) { - LoggerFor(reporting).Warning(message); + LoggerFor(reporting).Warning(message); } - + /// public void Warn(Type reporting, string message, params object[] propertyValues) { @@ -230,7 +231,7 @@ namespace Umbraco.Core.Logging.Serilog { LoggerFor(reporting).Warning(exception, message); } - + /// public void Warn(Type reporting, Exception exception, string messageTemplate, params object[] propertyValues) { @@ -254,7 +255,7 @@ namespace Umbraco.Core.Logging.Serilog { LoggerFor(reporting).Debug(message); } - + /// public void Debug(Type reporting, string messageTemplate, params object[] propertyValues) { diff --git a/src/Umbraco.Core/MainDom.cs b/src/Umbraco.Core/MainDom.cs index eb036fd441..0b4551a7cc 100644 --- a/src/Umbraco.Core/MainDom.cs +++ b/src/Umbraco.Core/MainDom.cs @@ -8,15 +8,13 @@ using Umbraco.Core.Logging; namespace Umbraco.Core { /// - /// Represents the main AppDomain running for a given application. + /// Provides the full implementation of . /// /// - /// There can be only one "main" AppDomain running for a given application at a time. /// When an AppDomain starts, it tries to acquire the main domain status. /// When an AppDomain stops (eg the application is restarting) it should release the main domain status. - /// It is possible to register against the MainDom and be notified when it is released. /// - internal class MainDom : IRegisteredObject + internal class MainDom : IMainDom, IRegisteredObject { #region Vars @@ -84,9 +82,7 @@ namespace Umbraco.Core /// An optional weight (lower goes first). /// A value indicating whether it was possible to register. public bool Register(Action release, int weight = 100) - { - return Register(null, release, weight); - } + => Register(null, release, weight); /// /// Registers a resource that requires the current AppDomain to be the main domain to function. @@ -195,7 +191,9 @@ namespace Umbraco.Core } } - // gets a value indicating whether we are the main domain + /// + /// Gets a value indicating whether the current domain is the main domain. + /// public bool IsMainDom => _isMainDom; // IRegisteredObject diff --git a/src/Umbraco.Core/Migrations/Install/DatabaseBuilder.cs b/src/Umbraco.Core/Migrations/Install/DatabaseBuilder.cs index ef6b3b720b..e2c34c31cb 100644 --- a/src/Umbraco.Core/Migrations/Install/DatabaseBuilder.cs +++ b/src/Umbraco.Core/Migrations/Install/DatabaseBuilder.cs @@ -120,7 +120,7 @@ namespace Umbraco.Core.Migrations.Install #region Configure Connection String - private const string EmbeddedDatabaseConnectionString = @"Data Source=|DataDirectory|\Umbraco.sdf;Flush Interval=1;"; + public const string EmbeddedDatabaseConnectionString = @"Data Source=|DataDirectory|\Umbraco.sdf;Flush Interval=1;"; /// /// Configures a connection string for the embedded database. @@ -369,52 +369,6 @@ namespace Umbraco.Core.Migrations.Install #endregion - #region Utils - - internal static void GiveLegacyAChance(IUmbracoDatabaseFactory factory, ILogger logger) - { - // look for the legacy appSettings key - var legacyConnString = ConfigurationManager.AppSettings[Constants.System.UmbracoConnectionName]; - if (string.IsNullOrWhiteSpace(legacyConnString)) return; - - var test = legacyConnString.ToLowerInvariant(); - if (test.Contains("sqlce4umbraco")) - { - // sql ce - ConfigureEmbeddedDatabaseConnection(factory, logger); - } - else if (test.Contains("tcp:")) - { - // sql azure - SaveConnectionString(legacyConnString, Constants.DbProviderNames.SqlServer, logger); - factory.Configure(legacyConnString, Constants.DbProviderNames.SqlServer); - } - else if (test.Contains("datalayer=mysql")) - { - // mysql - - // strip the datalayer part off - var connectionStringWithoutDatalayer = string.Empty; - // ReSharper disable once LoopCanBeConvertedToQuery - foreach (var variable in legacyConnString.Split(';').Where(x => x.ToLowerInvariant().StartsWith("datalayer") == false)) - connectionStringWithoutDatalayer = $"{connectionStringWithoutDatalayer}{variable};"; - - SaveConnectionString(connectionStringWithoutDatalayer, Constants.DbProviderNames.MySql, logger); - factory.Configure(connectionStringWithoutDatalayer, Constants.DbProviderNames.MySql); - } - else - { - // sql server - SaveConnectionString(legacyConnString, Constants.DbProviderNames.SqlServer, logger); - factory.Configure(legacyConnString, Constants.DbProviderNames.SqlServer); - } - - // remove the legacy connection string, so we don't end up in a loop if something goes wrong - GlobalSettings.RemoveSetting(Constants.System.UmbracoConnectionName); - } - - #endregion - #region Database Schema /// diff --git a/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs b/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs index eba5e61390..5525cc4a50 100644 --- a/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs +++ b/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs @@ -116,8 +116,12 @@ namespace Umbraco.Core.Migrations.Install /// /// Initializes the database by creating the umbraco db schema. /// + /// This needs to execute as part of a transaction. public void InitializeDatabaseSchema() { + if (!_database.InTransaction) + throw new InvalidOperationException("Database is not in a transaction."); + var e = new DatabaseCreationEventArgs(); FireBeforeCreation(e); @@ -409,9 +413,14 @@ namespace Umbraco.Core.Migrations.Install /// If a table with the same name already exists, the parameter will determine /// whether the table is overwritten. If true, the table will be overwritten, whereas this method will /// not do anything if the parameter is false. + /// + /// This need to execute as part of a transaction. /// public void CreateTable(bool overwrite, Type modelType, DatabaseDataCreator dataCreation) { + if (!_database.InTransaction) + throw new InvalidOperationException("Database is not in a transaction."); + var tableDefinition = DefinitionFactory.GetTableDefinition(modelType, SqlSyntax); var tableName = tableDefinition.Name; @@ -429,70 +438,64 @@ namespace Umbraco.Core.Migrations.Install tableExist = false; } - if (tableExist == false) - { - using (var transaction = _database.GetTransaction()) - { - //Execute the Create Table sql - var created = _database.Execute(new Sql(createSql)); - _logger.Info("Create Table '{TableName}' ({Created}): \n {Sql}", tableName, created, createSql); - - //If any statements exists for the primary key execute them here - if (string.IsNullOrEmpty(createPrimaryKeySql) == false) - { - var createdPk = _database.Execute(new Sql(createPrimaryKeySql)); - _logger.Info("Create Primary Key ({CreatedPk}):\n {Sql}", createdPk, createPrimaryKeySql); - } - - //Turn on identity insert if db provider is not mysql - if (SqlSyntax.SupportsIdentityInsert() && tableDefinition.Columns.Any(x => x.IsIdentity)) - _database.Execute(new Sql($"SET IDENTITY_INSERT {SqlSyntax.GetQuotedTableName(tableName)} ON ")); - - //Call the NewTable-event to trigger the insert of base/default data - //OnNewTable(tableName, _db, e, _logger); - - dataCreation.InitializeBaseData(tableName); - - //Turn off identity insert if db provider is not mysql - if (SqlSyntax.SupportsIdentityInsert() && tableDefinition.Columns.Any(x => x.IsIdentity)) - _database.Execute(new Sql($"SET IDENTITY_INSERT {SqlSyntax.GetQuotedTableName(tableName)} OFF;")); - - //Special case for MySql - if (SqlSyntax is MySqlSyntaxProvider && tableName.Equals("umbracoUser")) - { - _database.Update("SET id = @IdAfter WHERE id = @IdBefore AND userLogin = @Login", new { IdAfter = 0, IdBefore = 1, Login = "admin" }); - } - - //Loop through index statements and execute sql - foreach (var sql in indexSql) - { - var createdIndex = _database.Execute(new Sql(sql)); - _logger.Info("Create Index ({CreatedIndex}):\n {Sql}", createdIndex, sql); - } - - //Loop through foreignkey statements and execute sql - foreach (var sql in foreignSql) - { - var createdFk = _database.Execute(new Sql(sql)); - _logger.Info("Create Foreign Key ({CreatedFk}):\n {Sql}", createdFk, sql); - } - - transaction.Complete(); - - if (overwrite) - { - _logger.Info("Table '{TableName}' was recreated", tableName); - } - else - { - _logger.Info("New table '{TableName}' was created", tableName); - } - } - } - else + if (tableExist) { // The table exists and was not recreated/overwritten. _logger.Info("Table '{TableName}' already exists - no changes were made", tableName); + return; + } + + //Execute the Create Table sql + var created = _database.Execute(new Sql(createSql)); + _logger.Info("Create Table '{TableName}' ({Created}): \n {Sql}", tableName, created, createSql); + + //If any statements exists for the primary key execute them here + if (string.IsNullOrEmpty(createPrimaryKeySql) == false) + { + var createdPk = _database.Execute(new Sql(createPrimaryKeySql)); + _logger.Info("Create Primary Key ({CreatedPk}):\n {Sql}", createdPk, createPrimaryKeySql); + } + + //Turn on identity insert if db provider is not mysql + if (SqlSyntax.SupportsIdentityInsert() && tableDefinition.Columns.Any(x => x.IsIdentity)) + _database.Execute(new Sql($"SET IDENTITY_INSERT {SqlSyntax.GetQuotedTableName(tableName)} ON ")); + + //Call the NewTable-event to trigger the insert of base/default data + //OnNewTable(tableName, _db, e, _logger); + + dataCreation.InitializeBaseData(tableName); + + //Turn off identity insert if db provider is not mysql + if (SqlSyntax.SupportsIdentityInsert() && tableDefinition.Columns.Any(x => x.IsIdentity)) + _database.Execute(new Sql($"SET IDENTITY_INSERT {SqlSyntax.GetQuotedTableName(tableName)} OFF;")); + + //Special case for MySql + if (SqlSyntax is MySqlSyntaxProvider && tableName.Equals("umbracoUser")) + { + _database.Update("SET id = @IdAfter WHERE id = @IdBefore AND userLogin = @Login", new { IdAfter = 0, IdBefore = 1, Login = "admin" }); + } + + //Loop through index statements and execute sql + foreach (var sql in indexSql) + { + var createdIndex = _database.Execute(new Sql(sql)); + _logger.Info("Create Index ({CreatedIndex}):\n {Sql}", createdIndex, sql); + } + + //Loop through foreignkey statements and execute sql + foreach (var sql in foreignSql) + { + var createdFk = _database.Execute(new Sql(sql)); + _logger.Info("Create Foreign Key ({CreatedFk}):\n {Sql}", createdFk, sql); + } + + if (overwrite) + { + _logger.Info("Table '{TableName}' was recreated", tableName); + } + else + { + _logger.Info("New table '{TableName}' was created", tableName); } } diff --git a/src/Umbraco.Core/Migrations/MigrationBuilder.cs b/src/Umbraco.Core/Migrations/MigrationBuilder.cs index 3d8c88c771..d2d2b7d32a 100644 --- a/src/Umbraco.Core/Migrations/MigrationBuilder.cs +++ b/src/Umbraco.Core/Migrations/MigrationBuilder.cs @@ -1,36 +1,20 @@ using System; -using LightInject; +using Umbraco.Core.Composing; namespace Umbraco.Core.Migrations { public class MigrationBuilder : IMigrationBuilder { - private readonly IServiceContainer _container; + private readonly IFactory _container; - public MigrationBuilder(IServiceContainer container) + public MigrationBuilder(IFactory container) { _container = container; - - // because the builder should be "per container" this ctor should run only once per container. - // - // note: constructor dependencies do NOT work with lifetimes other than transient - // see https://github.com/seesharper/LightInject/issues/294 - // - // resolve ctor dependency from GetInstance() runtimeArguments, if possible - 'factory' is - // the container, 'info' describes the ctor argument, and 'args' contains the args that - // were passed to GetInstance() - use first arg if it is the right type. - // - // for IMigrationContext - container.RegisterConstructorDependency((factory, info, args) => args.Length > 0 ? args[0] as IMigrationContext : null); } public IMigration Build(Type migrationType, IMigrationContext context) { - // LightInject .Create() is a shortcut for .Register() + .GetInstance() - // but it does not support parameters, so we do it ourselves here - - _container.Register(migrationType); - return (IMigration) _container.GetInstance(migrationType, new object[] { context }); + return (IMigration) _container.CreateInstance(migrationType, context); } } } diff --git a/src/Umbraco.Core/Migrations/PostMigrationCollectionBuilder.cs b/src/Umbraco.Core/Migrations/PostMigrationCollectionBuilder.cs index 63cdaf4454..b23d4f1c9c 100644 --- a/src/Umbraco.Core/Migrations/PostMigrationCollectionBuilder.cs +++ b/src/Umbraco.Core/Migrations/PostMigrationCollectionBuilder.cs @@ -1,16 +1,11 @@ -using LightInject; -using Umbraco.Core.Composing; +using Umbraco.Core.Composing; namespace Umbraco.Core.Migrations { public class PostMigrationCollectionBuilder : LazyCollectionBuilderBase { - public PostMigrationCollectionBuilder(IServiceContainer container) - : base(container) - { } - protected override PostMigrationCollectionBuilder This => this; - protected override ILifetime CollectionLifetime => null; // transient + protected override Lifetime CollectionLifetime => Lifetime.Transient; } } diff --git a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs index f7e1ee9921..b469c02a3c 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs @@ -118,7 +118,9 @@ namespace Umbraco.Core.Migrations.Upgrade To("{EE429F1B-9B26-43CA-89F8-A86017C809A3}"); To("{08919C4B-B431-449C-90EC-2B8445B5C6B1}"); To("{7EB0254C-CB8B-4C75-B15B-D48C55B449EB}"); + To("{648A2D5F-7467-48F8-B309-E99CEEE00E2A}"); // fixed version To("{C39BF2A7-1454-4047-BBFE-89E40F66ED63}"); + To("{64EBCE53-E1F0-463A-B40B-E98EFCCA8AE2}"); //FINAL diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropTaskTables.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropTaskTables.cs index e8fd4f409e..008b3e4b5f 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropTaskTables.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropTaskTables.cs @@ -9,10 +9,10 @@ public override void Migrate() { - if (TableExists("cmsTaskType")) - Delete.Table("cmsTaskType"); if (TableExists("cmsTask")) - Delete.Table("cmsTask"); + Delete.Table("cmsTask").Do(); + if (TableExists("cmsTaskType")) + Delete.Table("cmsTaskType").Do(); } } } diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/MakeRedirectUrlVariant.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/MakeRedirectUrlVariant.cs new file mode 100644 index 0000000000..2e366c7c14 --- /dev/null +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/MakeRedirectUrlVariant.cs @@ -0,0 +1,29 @@ +using Umbraco.Core.Persistence.Dtos; + +namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 +{ + public class MakeRedirectUrlVariant : MigrationBase + { + public MakeRedirectUrlVariant(IMigrationContext context) + : base(context) + { } + + public override void Migrate() + { + AddColumn("culture"); + + Delete.Index("IX_umbracoRedirectUrl").OnTable(Constants.DatabaseSchema.Tables.RedirectUrl).Do(); + Create.Index("IX_umbracoRedirectUrl").OnTable(Constants.DatabaseSchema.Tables.RedirectUrl) + .OnColumn("urlHash") + .Ascending() + .OnColumn("contentKey") + .Ascending() + .OnColumn("culture") + .Ascending() + .OnColumn("createDateUtc") + .Ascending() + .WithOptions().Unique() + .Do(); + } + } +} diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/RefactorVariantsModel.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/RefactorVariantsModel.cs index aa498583ff..6ddd49841d 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/RefactorVariantsModel.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/RefactorVariantsModel.cs @@ -11,7 +11,8 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 public override void Migrate() { - Delete.Column("edited").FromTable(Constants.DatabaseSchema.Tables.ContentVersionCultureVariation).Do(); + if (ColumnExists(Constants.DatabaseSchema.Tables.ContentVersionCultureVariation, "edited")) + Delete.Column("edited").FromTable(Constants.DatabaseSchema.Tables.ContentVersionCultureVariation).Do(); // add available column diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs index 3ae5b4f534..d2f5e3d308 100644 --- a/src/Umbraco.Core/Models/Content.cs +++ b/src/Umbraco.Core/Models/Content.cs @@ -17,6 +17,7 @@ namespace Umbraco.Core.Models { private ISimpleContentType _contentType; private ITemplate _template; + private int? _templateId; private ContentScheduleCollection _schedule; private bool _published; private PublishedState _publishedState; @@ -85,7 +86,7 @@ namespace Umbraco.Core.Models // ReSharper disable once ClassNeverInstantiated.Local private class PropertySelectors { - public readonly PropertyInfo TemplateSelector = ExpressionHelper.GetPropertyInfo(x => x.Template); + public readonly PropertyInfo TemplateSelector = ExpressionHelper.GetPropertyInfo(x => x.TemplateId); public readonly PropertyInfo PublishedSelector = ExpressionHelper.GetPropertyInfo(x => x.Published); public readonly PropertyInfo ContentScheduleSelector = ExpressionHelper.GetPropertyInfo(x => x.ContentSchedule); public readonly PropertyInfo PublishCultureInfosSelector = ExpressionHelper.GetPropertyInfo>(x => x.PublishCultureInfos); @@ -133,10 +134,10 @@ namespace Umbraco.Core.Models /// the Default template from the ContentType will be returned. /// [DataMember] - public ITemplate Template + public int? TemplateId { - get => _template ?? _contentType.DefaultTemplate; - set => SetPropertyValueAndDetectChanges(value, ref _template, Ps.Value.TemplateSelector); + get => _templateId; + set => SetPropertyValueAndDetectChanges(value, ref _templateId, Ps.Value.TemplateSelector); } @@ -195,7 +196,7 @@ namespace Umbraco.Core.Models /// [IgnoreDataMember] - public ITemplate PublishTemplate { get; internal set; } // set by persistence + public int? PublishTemplateId { get; internal set; } // set by persistence /// [IgnoreDataMember] @@ -460,9 +461,6 @@ namespace Umbraco.Core.Models { base.ResetDirtyProperties(rememberDirty); - if (Template != null) - Template.ResetDirtyProperties(rememberDirty); - // take care of the published state _publishedState = _published ? PublishedState.Published : PublishedState.Unpublished; @@ -504,8 +502,6 @@ namespace Umbraco.Core.Models //need to manually clone this since it's not settable clonedContent._contentType = ContentType; - - //if culture infos exist then deal with event bindings if (clonedContent._publishInfos != null) { diff --git a/src/Umbraco.Core/Models/IContent.cs b/src/Umbraco.Core/Models/IContent.cs index aadd920995..2e71d392c6 100644 --- a/src/Umbraco.Core/Models/IContent.cs +++ b/src/Umbraco.Core/Models/IContent.cs @@ -18,9 +18,9 @@ namespace Umbraco.Core.Models ContentScheduleCollection ContentSchedule { get; set; } /// - /// Gets or sets the template used to render the content. + /// Gets or sets the template id used to render the content. /// - ITemplate Template { get; set; } + int? TemplateId { get; set; } /// /// Gets a value indicating whether the content is published. @@ -45,10 +45,10 @@ namespace Umbraco.Core.Models bool Blueprint { get; } /// - /// Gets the template used to render the published version of the content. + /// Gets the template id used to render the published version of the content. /// /// When editing the content, the template can change, but this will not until the content is published. - ITemplate PublishTemplate { get; } + int? PublishTemplateId { get; } /// /// Gets the name of the published version of the content. diff --git a/src/Umbraco.Core/Models/IRedirectUrl.cs b/src/Umbraco.Core/Models/IRedirectUrl.cs index f3c65fe89c..e066881645 100644 --- a/src/Umbraco.Core/Models/IRedirectUrl.cs +++ b/src/Umbraco.Core/Models/IRedirectUrl.cs @@ -27,11 +27,18 @@ namespace Umbraco.Core.Models [DataMember] DateTime CreateDateUtc { get; set; } + /// + /// Gets or sets the culture. + /// + [DataMember] + string Culture { get; set; } + /// /// Gets or sets the redirect url route. /// /// Is a proper Umbraco route eg /path/to/foo or 123/path/tofoo. [DataMember] string Url { get; set; } + } } diff --git a/src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs b/src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs index 23c232324a..dcf86a0b42 100644 --- a/src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs +++ b/src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Reflection; using System.Security.Claims; using System.Threading.Tasks; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Models.Entities; using Umbraco.Core.Models.Membership; @@ -66,7 +67,7 @@ namespace Umbraco.Core.Models.Identity _startContentIds = new int[] { }; _groups = new IReadOnlyUserGroup[] { }; _allowedSections = new string[] { }; - _culture = UmbracoConfig.For.GlobalSettings().DefaultUILanguage; //fixme inject somehow? + _culture = Current.Configs.Global().DefaultUILanguage; //fixme inject somehow? _groups = new IReadOnlyUserGroup[0]; _roles = new ObservableCollection>(); _roles.CollectionChanged += _roles_CollectionChanged; @@ -83,7 +84,7 @@ namespace Umbraco.Core.Models.Identity _startContentIds = new int[] { }; _groups = new IReadOnlyUserGroup[] { }; _allowedSections = new string[] { }; - _culture = UmbracoConfig.For.GlobalSettings().DefaultUILanguage; //fixme inject somehow? + _culture = Current.Configs.Global().DefaultUILanguage; //fixme inject somehow? _groups = groups.ToArray(); _roles = new ObservableCollection>(_groups.Select(x => new IdentityUserRole { @@ -442,6 +443,6 @@ namespace Umbraco.Core.Models.Identity groups => groups.GetHashCode()); } - + } } diff --git a/src/Umbraco.Core/Models/Language.cs b/src/Umbraco.Core/Models/Language.cs index e190c8ad3b..03f8f87cd3 100644 --- a/src/Umbraco.Core/Models/Language.cs +++ b/src/Umbraco.Core/Models/Language.cs @@ -88,7 +88,7 @@ namespace Umbraco.Core.Models try { - var globalSettings = (IGlobalSettings) Composing.Current.Container.GetInstance(typeof(IGlobalSettings)); + var globalSettings = (IGlobalSettings) Composing.Current.Factory.GetInstance(typeof(IGlobalSettings)); var defaultUiCulture = CultureInfo.GetCultureInfo(globalSettings.DefaultUILanguage); Thread.CurrentThread.CurrentUICulture = defaultUiCulture; diff --git a/src/Umbraco.Core/Models/Membership/User.cs b/src/Umbraco.Core/Models/Membership/User.cs index 0694194996..650aa6cb29 100644 --- a/src/Umbraco.Core/Models/Membership/User.cs +++ b/src/Umbraco.Core/Models/Membership/User.cs @@ -27,7 +27,7 @@ namespace Umbraco.Core.Models.Membership { SessionTimeout = 60; _userGroups = new HashSet(); - _language = UmbracoConfig.For.GlobalSettings().DefaultUILanguage; //fixme inject somehow? + _language = Current.Configs.Global().DefaultUILanguage; //fixme inject somehow? _isApproved = true; _isLockedOut = false; _startContentIds = new int[] { }; @@ -453,7 +453,7 @@ namespace Umbraco.Core.Models.Membership base.PerformDeepClone(clone); var clonedEntity = (User)clone; - + //manually clone the start node props clonedEntity._startContentIds = _startContentIds.ToArray(); clonedEntity._startMediaIds = _startMediaIds.ToArray(); @@ -483,7 +483,7 @@ namespace Umbraco.Core.Models.Membership //need to create new collections otherwise they'll get copied by ref clonedEntity._userGroups = new HashSet(_userGroups); clonedEntity._allowedSections = _allowedSections != null ? new List(_allowedSections) : null; - + } /// diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs index 0c049e81bf..4e1ce7ddd7 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs @@ -62,7 +62,7 @@ namespace Umbraco.Core.Models.PublishedContent /// /// Gets the identifier of the template to use to render the content item. /// - int TemplateId { get; } + int? TemplateId { get; } /// /// Gets the identifier of the user who created the content item. diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtensionsForModels.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtensionsForModels.cs index df3213eb07..bfc65b70d6 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtensionsForModels.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtensionsForModels.cs @@ -21,7 +21,7 @@ namespace Umbraco.Core.Models.PublishedContent // in order to provide a nice, "fluent" experience, this extension method // needs to access Current, which is not always initialized in tests - not // very elegant, but works - if (!Current.HasContainer) return content; + if (!Current.HasFactory) return content; // get model // if factory returns nothing, throw diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs index 6a69d0b9e1..36755c8944 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs @@ -73,7 +73,7 @@ namespace Umbraco.Core.Models.PublishedContent public virtual string Path => _content.Path; /// - public virtual int TemplateId => _content.TemplateId; + public virtual int? TemplateId => _content.TemplateId; /// public virtual int CreatorId => _content.CreatorId; diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedModelFactory.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedModelFactory.cs index 67758c1c69..ee3fd62985 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedModelFactory.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedModelFactory.cs @@ -112,7 +112,7 @@ namespace Umbraco.Core.Models.PublishedContent if (ctor != null) return ctor(); var listType = typeof(List<>).MakeGenericType(modelInfo.ModelType); - ctor = modelInfo.ListCtor = ReflectionUtilities.EmitConstuctor>(declaring: listType); + ctor = modelInfo.ListCtor = ReflectionUtilities.EmitConstructor>(declaring: listType); return ctor(); } diff --git a/src/Umbraco.Core/Models/RedirectUrl.cs b/src/Umbraco.Core/Models/RedirectUrl.cs index 187d9fdd6e..55b799244e 100644 --- a/src/Umbraco.Core/Models/RedirectUrl.cs +++ b/src/Umbraco.Core/Models/RedirectUrl.cs @@ -28,12 +28,14 @@ namespace Umbraco.Core.Models public readonly PropertyInfo ContentIdSelector = ExpressionHelper.GetPropertyInfo(x => x.ContentId); public readonly PropertyInfo ContentKeySelector = ExpressionHelper.GetPropertyInfo(x => x.ContentKey); public readonly PropertyInfo CreateDateUtcSelector = ExpressionHelper.GetPropertyInfo(x => x.CreateDateUtc); + public readonly PropertyInfo CultureSelector = ExpressionHelper.GetPropertyInfo(x => x.Culture); public readonly PropertyInfo UrlSelector = ExpressionHelper.GetPropertyInfo(x => x.Url); } private int _contentId; private Guid _contentKey; private DateTime _createDateUtc; + private string _culture; private string _url; /// @@ -57,6 +59,13 @@ namespace Umbraco.Core.Models set { SetPropertyValueAndDetectChanges(value, ref _createDateUtc, Ps.Value.CreateDateUtcSelector); } } + /// + public string Culture + { + get { return _culture; } + set { SetPropertyValueAndDetectChanges(value, ref _culture, Ps.Value.CultureSelector); } + } + /// public string Url { diff --git a/src/Umbraco.Core/Models/UserExtensions.cs b/src/Umbraco.Core/Models/UserExtensions.cs index 6733eea500..ba4d8cf590 100644 --- a/src/Umbraco.Core/Models/UserExtensions.cs +++ b/src/Umbraco.Core/Models/UserExtensions.cs @@ -105,7 +105,7 @@ namespace Umbraco.Core.Models } //use the custom avatar - var avatarUrl = Current.FileSystems.MediaFileSystem.GetUrl(user.Avatar); + var avatarUrl = Current.MediaFileSystem.GetUrl(user.Avatar); return new[] { avatarUrl + "?width=30&height=30&mode=crop", diff --git a/src/Umbraco.Core/Persistence/Dtos/RedirectUrlDto.cs b/src/Umbraco.Core/Persistence/Dtos/RedirectUrlDto.cs index b2bc990f6b..57e7138827 100644 --- a/src/Umbraco.Core/Persistence/Dtos/RedirectUrlDto.cs +++ b/src/Umbraco.Core/Persistence/Dtos/RedirectUrlDto.cs @@ -16,7 +16,7 @@ namespace Umbraco.Core.Persistence.Dtos // notes // - // we want a unique, non-clustered index on (url ASC, contentId ASC, createDate DESC) but the + // we want a unique, non-clustered index on (url ASC, contentId ASC, culture ASC, createDate DESC) but the // problem is that the index key must be 900 bytes max. should we run without an index? done // some perfs comparisons, and running with an index on a hash is only slightly slower on // inserts, and much faster on reads, so... we have an index on a hash. @@ -41,9 +41,13 @@ namespace Umbraco.Core.Persistence.Dtos [NullSetting(NullSetting = NullSettings.NotNull)] public string Url { get; set; } + [Column("culture")] + [NullSetting(NullSetting = NullSettings.Null)] + public string Culture { get; set; } + [Column("urlHash")] [NullSetting(NullSetting = NullSettings.NotNull)] - [Index(IndexTypes.UniqueNonClustered, Name = "IX_umbracoRedirectUrl", ForColumns = "urlHash, contentKey, createDateUtc")] + [Index(IndexTypes.UniqueNonClustered, Name = "IX_umbracoRedirectUrl", ForColumns = "urlHash, contentKey, culture, createDateUtc")] [Length(40)] public string UrlHash { get; set; } } diff --git a/src/Umbraco.Core/Persistence/Factories/ContentBaseFactory.cs b/src/Umbraco.Core/Persistence/Factories/ContentBaseFactory.cs index c8467f47e2..7fe1d44921 100644 --- a/src/Umbraco.Core/Persistence/Factories/ContentBaseFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/ContentBaseFactory.cs @@ -282,7 +282,7 @@ namespace Umbraco.Core.Persistence.Factories var dto = new DocumentVersionDto { Id = entity.VersionId, - TemplateId = entity.Template?.Id, + TemplateId = entity.TemplateId, Published = false, // always building the current, unpublished one ContentVersionDto = BuildContentVersionDto(entity, contentDto) diff --git a/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs b/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs index c920a18c3b..728441964a 100644 --- a/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs @@ -93,18 +93,36 @@ namespace Umbraco.Core.Persistence.Factories return dto; } - public static IEnumerable BuildDtos(int currentVersionId, int publishedVersionId, IEnumerable properties, ILanguageRepository languageRepository, out bool edited, out HashSet editedCultures) + /// + /// Creates a collection of from a collection of + /// + /// + /// The of the entity containing the collection of + /// + /// + /// + /// The properties to map + /// + /// out parameter indicating that one or more properties have been edited + /// out parameter containing a collection of of edited cultures when the contentVariation varies by culture + /// + public static IEnumerable BuildDtos(ContentVariation contentVariation, int currentVersionId, int publishedVersionId, IEnumerable properties, + ILanguageRepository languageRepository, out bool edited, out HashSet editedCultures) { var propertyDataDtos = new List(); edited = false; editedCultures = null; // don't allocate unless necessary + string defaultCulture = null; //don't allocate unless necessary + + var entityVariesByCulture = contentVariation.VariesByCulture(); foreach (var property in properties) { if (property.PropertyType.IsPublishing) { - var editingCultures = property.PropertyType.VariesByCulture(); - if (editingCultures && editedCultures == null) editedCultures = new HashSet(StringComparer.OrdinalIgnoreCase); + //create the resulting hashset if it's not created and the entity varies by culture + if (entityVariesByCulture && editedCultures == null) + editedCultures = new HashSet(StringComparer.OrdinalIgnoreCase); // publishing = deal with edit and published values foreach (var propertyValue in property.Values) @@ -125,12 +143,24 @@ namespace Umbraco.Core.Persistence.Factories var sameValues = propertyValue.PublishedValue == null ? propertyValue.EditedValue == null : propertyValue.PublishedValue.Equals(propertyValue.EditedValue); edited |= !sameValues; - if (editingCultures && // cultures can be edited, ie CultureNeutral is supported - propertyValue.Culture != null && propertyValue.Segment == null && // and value is CultureNeutral - !sameValues) // and edited and published are different + if (entityVariesByCulture // cultures can be edited, ie CultureNeutral is supported + && propertyValue.Culture != null && propertyValue.Segment == null // and value is CultureNeutral + && !sameValues) // and edited and published are different { editedCultures.Add(propertyValue.Culture); // report culture as edited } + + // flag culture as edited if it contains an edited invariant property + if (propertyValue.Culture == null //invariant property + && !sameValues // and edited and published are different + && entityVariesByCulture) //only when the entity is variant + { + if (defaultCulture == null) + defaultCulture = languageRepository.GetDefaultIsoCode(); + + editedCultures.Add(defaultCulture); + } + } } else diff --git a/src/Umbraco.Core/Persistence/Mappers/MapperCollectionBuilder.cs b/src/Umbraco.Core/Persistence/Mappers/MapperCollectionBuilder.cs index 6d641cab3d..80819933f5 100644 --- a/src/Umbraco.Core/Persistence/Mappers/MapperCollectionBuilder.cs +++ b/src/Umbraco.Core/Persistence/Mappers/MapperCollectionBuilder.cs @@ -1,19 +1,14 @@ -using LightInject; -using Umbraco.Core.Composing; +using Umbraco.Core.Composing; namespace Umbraco.Core.Persistence.Mappers { public class MapperCollectionBuilder : LazyCollectionBuilderBase { - public MapperCollectionBuilder(IServiceContainer container) - : base(container) - { } - protected override MapperCollectionBuilder This => this; - protected override void Initialize() + public override void RegisterWith(IRegister register) { - base.Initialize(); + base.RegisterWith(register); // default initializer registers // - service MapperCollectionBuilder, returns MapperCollectionBuilder @@ -21,10 +16,10 @@ namespace Umbraco.Core.Persistence.Mappers // we want to register extra // - service IMapperCollection, returns MappersCollectionBuilder's collection - Container.Register(factory => factory.GetInstance()); + register.Register(factory => factory.GetInstance()); } - public MapperCollectionBuilder AddCore() + public MapperCollectionBuilder AddCoreMappers() { Add(); Add(); diff --git a/src/Umbraco.Core/Persistence/Repositories/IRedirectUrlRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IRedirectUrlRepository.cs index 47a56bb530..d05f4e007c 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IRedirectUrlRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IRedirectUrlRepository.cs @@ -14,8 +14,9 @@ namespace Umbraco.Core.Persistence.Repositories /// /// The Umbraco redirect url route. /// The content unique key. + /// The culture. /// - IRedirectUrl Get(string url, Guid contentKey); + IRedirectUrl Get(string url, Guid contentKey, string culture); /// /// Deletes a redirect url. diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/AuditRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/AuditRepository.cs index 6c61fe7ad5..45f083bc6b 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/AuditRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/AuditRepository.cs @@ -1,10 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; -using LightInject; using NPoco; using Umbraco.Core.Cache; -using Umbraco.Core.Composing.CompositionRoots; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Persistence.DatabaseModelDefinitions; @@ -16,8 +14,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement { internal class AuditRepository : NPocoRepositoryBase, IAuditRepository { - public AuditRepository(IScopeAccessor scopeAccessor, [Inject(RepositoryCompositionRoot.DisabledCache)] CacheHelper cache, ILogger logger) - : base(scopeAccessor, cache, logger) + public AuditRepository(IScopeAccessor scopeAccessor, ILogger logger) + : base(scopeAccessor, CacheHelper.NoCache, logger) { } protected override void PersistNewItem(IAuditItem entity) diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs index 35496aaba7..054ab8cb4b 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs @@ -273,8 +273,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement var publishing = content.PublishedState == PublishedState.Publishing; // ensure that the default template is assigned - if (entity.Template == null) - entity.Template = entity.ContentType.DefaultTemplate; + if (entity.TemplateId.HasValue == false) + entity.TemplateId = entity.ContentType.DefaultTemplate?.Id; // sanitize names SanitizeNames(content, publishing); @@ -352,7 +352,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement } // persist the property data - var propertyDataDtos = PropertyFactory.BuildDtos(content.VersionId, content.PublishedVersionId, entity.Properties, LanguageRepository, out var edited, out var editedCultures); + var propertyDataDtos = PropertyFactory.BuildDtos(content.ContentType.Variations, content.VersionId, content.PublishedVersionId, entity.Properties, LanguageRepository, out var edited, out var editedCultures); foreach (var propertyDataDto in propertyDataDtos) Database.Insert(propertyDataDto); @@ -404,7 +404,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement if (content.PublishedState == PublishedState.Publishing) { content.Published = true; - content.PublishTemplate = content.Template; + content.PublishTemplateId = content.TemplateId; content.PublisherId = content.WriterId; content.PublishName = content.Name; content.PublishDate = content.UpdateDate; @@ -414,7 +414,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement else if (content.PublishedState == PublishedState.Unpublishing) { content.Published = false; - content.PublishTemplate = null; + content.PublishTemplateId = null; content.PublisherId = null; content.PublishName = null; content.PublishDate = null; @@ -527,7 +527,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement Database.Execute(deletePropertyDataSql); // insert property data - var propertyDataDtos = PropertyFactory.BuildDtos(content.VersionId, publishing ? content.PublishedVersionId : 0, entity.Properties, LanguageRepository, out var edited, out var editedCultures); + var propertyDataDtos = PropertyFactory.BuildDtos(content.ContentType.Variations, content.VersionId, publishing ? content.PublishedVersionId : 0, + entity.Properties, LanguageRepository, out var edited, out var editedCultures); foreach (var propertyDataDto in propertyDataDtos) Database.Insert(propertyDataDto); @@ -608,7 +609,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement if (content.PublishedState == PublishedState.Publishing) { content.Published = true; - content.PublishTemplate = content.Template; + content.PublishTemplateId = content.TemplateId; content.PublisherId = content.WriterId; content.PublishName = content.Name; content.PublishDate = content.UpdateDate; @@ -618,7 +619,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement else if (content.PublishedState == PublishedState.Unpublishing) { content.Published = false; - content.PublishTemplate = null; + content.PublishTemplateId = null; content.PublisherId = null; content.PublishName = null; content.PublishDate = null; @@ -1077,18 +1078,19 @@ namespace Umbraco.Core.Persistence.Repositories.Implement // assign templates and properties foreach (var temp in temps) { - // complete the item - if (temp.Template1Id.HasValue && templates.TryGetValue(temp.Template1Id.Value, out var template)) - temp.Content.Template = template; - if (temp.Template2Id.HasValue && templates.TryGetValue(temp.Template2Id.Value, out template)) - temp.Content.PublishTemplate = template; + // set the template ID if it matches an existing template + if (temp.Template1Id.HasValue && templates.ContainsKey(temp.Template1Id.Value)) + temp.Content.TemplateId = temp.Template1Id; + if (temp.Template2Id.HasValue && templates.ContainsKey(temp.Template2Id.Value)) + temp.Content.PublishTemplateId = temp.Template2Id; + // set properties if (properties.ContainsKey(temp.VersionId)) temp.Content.Properties = properties[temp.VersionId]; else throw new InvalidOperationException($"No property data found for version: '{temp.VersionId}'."); - //load in the schedule + // load in the schedule if (schedule.TryGetValue(temp.Content.Id, out var s)) temp.Content.ContentSchedule = s; } @@ -1122,7 +1124,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement // get template if (dto.DocumentVersionDto.TemplateId.HasValue && dto.DocumentVersionDto.TemplateId.Value > 0) - content.Template = _templateRepository.Get(dto.DocumentVersionDto.TemplateId.Value); + content.TemplateId = dto.DocumentVersionDto.TemplateId; // get properties - indexed by version id var versionId = dto.DocumentVersionDto.Id; diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs index dbfdc8e980..3e665e321f 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs @@ -284,7 +284,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement Database.Insert(mediaVersionDto); // persist the property data - var propertyDataDtos = PropertyFactory.BuildDtos(media.VersionId, 0, entity.Properties, LanguageRepository, out _, out _); + var propertyDataDtos = PropertyFactory.BuildDtos(media.ContentType.Variations, media.VersionId, 0, entity.Properties, LanguageRepository, out _, out _); foreach (var propertyDataDto in propertyDataDtos) Database.Insert(propertyDataDto); @@ -341,7 +341,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement // replace the property data var deletePropertyDataSql = SqlContext.Sql().Delete().Where(x => x.VersionId == media.VersionId); Database.Execute(deletePropertyDataSql); - var propertyDataDtos = PropertyFactory.BuildDtos(media.VersionId, 0, entity.Properties, LanguageRepository, out _, out _); + var propertyDataDtos = PropertyFactory.BuildDtos(media.ContentType.Variations, media.VersionId, 0, entity.Properties, LanguageRepository, out _, out _); foreach (var propertyDataDto in propertyDataDtos) Database.Insert(propertyDataDto); diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs index fd79b231de..bfadebd61b 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs @@ -310,7 +310,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement Database.Insert(dto); // persist the property data - var propertyDataDtos = PropertyFactory.BuildDtos(member.VersionId, 0, entity.Properties, LanguageRepository, out _, out _); + var propertyDataDtos = PropertyFactory.BuildDtos(member.ContentType.Variations, member.VersionId, 0, entity.Properties, LanguageRepository, out _, out _); foreach (var propertyDataDto in propertyDataDtos) Database.Insert(propertyDataDto); @@ -375,7 +375,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement // replace the property data var deletePropertyDataSql = SqlContext.Sql().Delete().Where(x => x.VersionId == member.VersionId); Database.Execute(deletePropertyDataSql); - var propertyDataDtos = PropertyFactory.BuildDtos(member.VersionId, 0, entity.Properties, LanguageRepository, out _, out _); + var propertyDataDtos = PropertyFactory.BuildDtos(member.ContentType.Variations, member.VersionId, 0, entity.Properties, LanguageRepository, out _, out _); foreach (var propertyDataDto in propertyDataDtos) Database.Insert(propertyDataDto); diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/PartialViewMacroRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/PartialViewMacroRepository.cs index 741bb98e7c..d707bcee10 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/PartialViewMacroRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/PartialViewMacroRepository.cs @@ -1,14 +1,12 @@ -using LightInject; -using Umbraco.Core.IO; +using Umbraco.Core.IO; using Umbraco.Core.Models; namespace Umbraco.Core.Persistence.Repositories.Implement { internal class PartialViewMacroRepository : PartialViewRepository, IPartialViewMacroRepository { - - public PartialViewMacroRepository([Inject("PartialViewMacroFileSystem")] IFileSystem fileSystem) - : base(fileSystem) + public PartialViewMacroRepository(IFileSystems fileSystems) + : base(fileSystems.MacroPartialsFileSystem) { } protected override PartialViewType ViewType => PartialViewType.PartialViewMacro; diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/PartialViewRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/PartialViewRepository.cs index 2aa63813e5..d04bc47cd8 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/PartialViewRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/PartialViewRepository.cs @@ -2,7 +2,6 @@ using System.IO; using System.Linq; using System.Text; -using LightInject; using Umbraco.Core.IO; using Umbraco.Core.Models; @@ -10,7 +9,11 @@ namespace Umbraco.Core.Persistence.Repositories.Implement { internal class PartialViewRepository : FileRepository, IPartialViewRepository { - public PartialViewRepository([Inject("PartialViewFileSystem")] IFileSystem fileSystem) + public PartialViewRepository(IFileSystems fileSystems) + : base(fileSystems.PartialViewsFileSystem) + { } + + protected PartialViewRepository(IFileSystem fileSystem) : base(fileSystem) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/RedirectUrlRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/RedirectUrlRepository.cs index 5ec7fd6f3c..3ca937ffcd 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/RedirectUrlRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/RedirectUrlRepository.cs @@ -104,6 +104,7 @@ JOIN umbracoNode ON umbracoRedirectUrl.contentKey=umbracoNode.uniqueID"); ContentKey = redirectUrl.ContentKey, CreateDateUtc = redirectUrl.CreateDateUtc, Url = redirectUrl.Url, + Culture = redirectUrl.Culture, UrlHash = redirectUrl.Url.ToSHA1() }; } @@ -121,6 +122,7 @@ JOIN umbracoNode ON umbracoRedirectUrl.contentKey=umbracoNode.uniqueID"); url.ContentId = dto.ContentId; url.ContentKey = dto.ContentKey; url.CreateDateUtc = dto.CreateDateUtc; + url.Culture = dto.Culture; url.Url = dto.Url; return url; } @@ -130,10 +132,10 @@ JOIN umbracoNode ON umbracoRedirectUrl.contentKey=umbracoNode.uniqueID"); } } - public IRedirectUrl Get(string url, Guid contentKey) + public IRedirectUrl Get(string url, Guid contentKey, string culture) { var urlHash = url.ToSHA1(); - var sql = GetBaseQuery(false).Where(x => x.Url == url && x.UrlHash == urlHash && x.ContentKey == contentKey); + var sql = GetBaseQuery(false).Where(x => x.Url == url && x.UrlHash == urlHash && x.ContentKey == contentKey && x.Culture == culture); var dto = Database.Fetch(sql).FirstOrDefault(); return dto == null ? null : Map(dto); } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/RelationRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/RelationRepository.cs index f79344028f..fb5ba00ea0 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/RelationRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/RelationRepository.cs @@ -1,10 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; -using LightInject; using NPoco; using Umbraco.Core.Cache; -using Umbraco.Core.Composing.CompositionRoots; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; @@ -22,8 +20,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement { private readonly IRelationTypeRepository _relationTypeRepository; - public RelationRepository(IScopeAccessor scopeAccessor, [Inject(RepositoryCompositionRoot.DisabledCache)] CacheHelper cache, ILogger logger, IRelationTypeRepository relationTypeRepository) - : base(scopeAccessor, cache, logger) + public RelationRepository(IScopeAccessor scopeAccessor, ILogger logger, IRelationTypeRepository relationTypeRepository) + : base(scopeAccessor, CacheHelper.NoCache, logger) { _relationTypeRepository = relationTypeRepository; } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ScriptRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ScriptRepository.cs index d5719554c9..85b41a2a1c 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ScriptRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ScriptRepository.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using LightInject; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; using Umbraco.Core.Models; @@ -16,8 +15,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement { private readonly IContentSection _contentConfig; - public ScriptRepository([Inject("ScriptFileSystem")] IFileSystem fileSystem, IContentSection contentConfig) - : base(fileSystem) + public ScriptRepository(IFileSystems fileSystems, IContentSection contentConfig) + : base(fileSystems.ScriptsFileSystem) { _contentConfig = contentConfig ?? throw new ArgumentNullException(nameof(contentConfig)); } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ServerRegistrationRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ServerRegistrationRepository.cs index 531df1ba13..2679f8f92f 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ServerRegistrationRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ServerRegistrationRepository.cs @@ -14,9 +14,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement { internal class ServerRegistrationRepository : NPocoRepositoryBase, IServerRegistrationRepository { - // fixme - should we use NoCache instead of CreateDisabledCacheHelper?! public ServerRegistrationRepository(IScopeAccessor scopeAccessor, ILogger logger) - : base(scopeAccessor, CacheHelper.CreateDisabledCacheHelper(), logger) + : base(scopeAccessor, CacheHelper.NoCache, logger) { } protected override IRepositoryCachePolicy CreateCachePolicy() diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/StylesheetRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/StylesheetRepository.cs index 73dcb44fef..4c02a8f4b5 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/StylesheetRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/StylesheetRepository.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using LightInject; using Umbraco.Core.IO; using Umbraco.Core.Models; @@ -12,8 +11,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement /// internal class StylesheetRepository : FileRepository, IStylesheetRepository { - public StylesheetRepository([Inject("StylesheetFileSystem")] IFileSystem fileSystem) - : base(fileSystem) + public StylesheetRepository(IFileSystems fileSystems) + : base(fileSystems.StylesheetsFileSystem) { } #region Overrides of FileRepository diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/TemplateRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/TemplateRepository.cs index 83876db599..19ef303ebe 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/TemplateRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/TemplateRepository.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; -using LightInject; using NPoco; using Umbraco.Core.Cache; using Umbraco.Core.Configuration.UmbracoSettings; @@ -30,13 +29,11 @@ namespace Umbraco.Core.Persistence.Repositories.Implement private readonly ViewHelper _viewHelper; private readonly MasterPageHelper _masterPageHelper; - public TemplateRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger, ITemplatesSection templateConfig, - [Inject(Constants.Composing.FileSystems.MasterpageFileSystem)] IFileSystem masterpageFileSystem, - [Inject(Constants.Composing.FileSystems.ViewFileSystem)] IFileSystem viewFileSystem) + public TemplateRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger, ITemplatesSection templateConfig, IFileSystems fileSystems) : base(scopeAccessor, cache, logger) { - _masterpagesFileSystem = masterpageFileSystem; - _viewsFileSystem = viewFileSystem; + _masterpagesFileSystem = fileSystems.MasterPagesFileSystem; + _viewsFileSystem = fileSystems.MvcViewsFileSystem; _templateConfig = templateConfig; _viewHelper = new ViewHelper(_viewsFileSystem); _masterPageHelper = new MasterPageHelper(_masterpagesFileSystem); @@ -152,7 +149,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement //Save to db var template = (Template)entity; template.AddingEntity(); - + var dto = TemplateFactory.BuildDto(template, NodeObjectTypeId, template.Id); //Create the (base) node data - umbracoNode diff --git a/src/Umbraco.Core/Persistence/SqlContext.cs b/src/Umbraco.Core/Persistence/SqlContext.cs index d11bce166f..6f9f91114c 100644 --- a/src/Umbraco.Core/Persistence/SqlContext.cs +++ b/src/Umbraco.Core/Persistence/SqlContext.cs @@ -12,6 +12,8 @@ namespace Umbraco.Core.Persistence /// public class SqlContext : ISqlContext { + private readonly Lazy _mappers; + /// /// Initializes a new instance of the class. /// @@ -20,38 +22,20 @@ namespace Umbraco.Core.Persistence /// The database type. /// The mappers. public SqlContext(ISqlSyntaxProvider sqlSyntax, DatabaseType databaseType, IPocoDataFactory pocoDataFactory, IMapperCollection mappers = null) - { - // for tests - Mappers = mappers ?? new Mappers.MapperCollection(Enumerable.Empty()); - - SqlSyntax = sqlSyntax ?? throw new ArgumentNullException(nameof(sqlSyntax)); - PocoDataFactory = pocoDataFactory ?? throw new ArgumentNullException(nameof(pocoDataFactory)); - DatabaseType = databaseType ?? throw new ArgumentNullException(nameof(databaseType)); - Templates = new SqlTemplates(this); - } - - /// - /// Initializes a new instance of the class. - /// - /// Initializes an empty context which must be fully initialized using the - /// method; this is done in - /// as soon as the factory is fully configured. - internal SqlContext() + : this(sqlSyntax, databaseType, pocoDataFactory, new Lazy(() => mappers ?? new Mappers.MapperCollection(Enumerable.Empty()))) { } /// - /// Initializes this . + /// Initializes a new instance of the class. /// /// The sql syntax provider. /// The Poco data factory. /// The database type. /// The mappers. - /// Fully initializes an initially empty context; this is done in - /// as soon as the factory is fully configured. - internal void Initialize(ISqlSyntaxProvider sqlSyntax, DatabaseType databaseType, IPocoDataFactory pocoDataFactory, IMapperCollection mappers = null) + public SqlContext(ISqlSyntaxProvider sqlSyntax, DatabaseType databaseType, IPocoDataFactory pocoDataFactory, Lazy mappers) { // for tests - Mappers = mappers ?? new Mappers.MapperCollection(Enumerable.Empty()); + _mappers = mappers; SqlSyntax = sqlSyntax ?? throw new ArgumentNullException(nameof(sqlSyntax)); PocoDataFactory = pocoDataFactory ?? throw new ArgumentNullException(nameof(pocoDataFactory)); @@ -60,10 +44,10 @@ namespace Umbraco.Core.Persistence } /// - public ISqlSyntaxProvider SqlSyntax { get; private set; } + public ISqlSyntaxProvider SqlSyntax { get; } /// - public DatabaseType DatabaseType { get; private set; } + public DatabaseType DatabaseType { get; } /// public Sql Sql() => NPoco.Sql.BuilderFor((ISqlContext) this); @@ -75,12 +59,12 @@ namespace Umbraco.Core.Persistence public IQuery Query() => new Query(this); /// - public SqlTemplates Templates { get; private set; } + public SqlTemplates Templates { get; } /// - public IPocoDataFactory PocoDataFactory { get; private set; } + public IPocoDataFactory PocoDataFactory { get; } /// - public IMapperCollection Mappers { get; private set; } + public IMapperCollection Mappers => _mappers.Value; } } diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/MySqlSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/MySqlSyntaxProvider.cs index d69786fbfc..5cf9fa3af8 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/MySqlSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/MySqlSyntaxProvider.cs @@ -11,7 +11,6 @@ namespace Umbraco.Core.Persistence.SqlSyntax /// /// Represents an SqlSyntaxProvider for MySql /// - [SqlSyntaxProvider(Constants.DbProviderNames.MySql)] public class MySqlSyntaxProvider : SqlSyntaxProviderBase { private readonly ILogger _logger; @@ -334,7 +333,7 @@ ORDER BY TABLE_NAME, INDEX_NAME", switch (systemMethod) { case SystemMethods.NewGuid: - return null; // NOT SUPPORTED! + return null; // NOT SUPPORTED! case SystemMethods.CurrentDateTime: return "CURRENT_TIMESTAMP"; } diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs index 75fc9c0b69..8f39e36fb9 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs @@ -4,14 +4,12 @@ using System.Linq; using NPoco; using Umbraco.Core.Persistence.DatabaseAnnotations; using Umbraco.Core.Persistence.DatabaseModelDefinitions; -using Umbraco.Core.Persistence.Querying; namespace Umbraco.Core.Persistence.SqlSyntax { /// /// Represents an SqlSyntaxProvider for Sql Ce /// - [SqlSyntaxProvider(Constants.DbProviderNames.SqlCe)] public class SqlCeSyntaxProvider : MicrosoftSqlSyntaxProviderBase { public override Sql SelectTop(Sql sql, int top) diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs index 90a2215e3d..8b8550b694 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Data.Common; using System.Linq; using NPoco; +using Umbraco.Core.Logging; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Scoping; @@ -11,29 +12,9 @@ namespace Umbraco.Core.Persistence.SqlSyntax /// /// Represents an SqlSyntaxProvider for Sql Server. /// - [SqlSyntaxProvider(Constants.DbProviderNames.SqlServer)] public class SqlServerSyntaxProvider : MicrosoftSqlSyntaxProviderBase { - // IUmbracoDatabaseFactory to be lazily injected - public SqlServerSyntaxProvider(Lazy lazyScopeProvider) - { - _serverVersion = new Lazy(() => - { - var scopeProvider = lazyScopeProvider.Value; - if (scopeProvider == null) - throw new InvalidOperationException("Failed to determine Sql Server version (no scope provider)."); - using (var scope = scopeProvider.CreateScope()) - { - var version = DetermineVersion(scope.Database); - scope.Complete(); - return version; - } - }); - } - - private readonly Lazy _serverVersion; - - internal ServerVersionInfo ServerVersion => _serverVersion.Value; + internal ServerVersionInfo ServerVersion { get; private set; } internal enum VersionName { @@ -62,19 +43,31 @@ namespace Umbraco.Core.Persistence.SqlSyntax internal class ServerVersionInfo { - public string Edition { get; set; } - public string InstanceName { get; set; } - public string ProductVersion { get; set; } - public VersionName ProductVersionName { get; private set; } - public EngineEdition EngineEdition { get; set; } - public bool IsAzure => EngineEdition == EngineEdition.Azure; - public string MachineName { get; set; } - public string ProductLevel { get; set; } - - public void Initialize() + public ServerVersionInfo() { - ProductVersionName = MapProductVersion(ProductVersion); + ProductVersionName = VersionName.Unknown; + EngineEdition = EngineEdition.Unknown; } + + public ServerVersionInfo(string edition, string instanceName, string productVersion, EngineEdition engineEdition, string machineName, string productLevel) + { + Edition = edition; + InstanceName = instanceName; + ProductVersion = productVersion; + ProductVersionName = MapProductVersion(ProductVersion); + EngineEdition = engineEdition; + MachineName = machineName; + ProductLevel = productLevel; + } + + public string Edition { get; } + public string InstanceName { get; } + public string ProductVersion { get; } + public VersionName ProductVersionName { get; } + public EngineEdition EngineEdition { get; } + public bool IsAzure => EngineEdition == EngineEdition.Azure; + public string MachineName { get; } + public string ProductLevel { get; } } private static VersionName MapProductVersion(string productVersion) @@ -105,8 +98,14 @@ namespace Umbraco.Core.Persistence.SqlSyntax } } - private static ServerVersionInfo DetermineVersion(IUmbracoDatabase database) + internal ServerVersionInfo GetSetVersion(string connectionString, string providerName, ILogger logger) { + var factory = DbProviderFactories.GetFactory(providerName); + var connection = factory.CreateConnection(); + + if (connection == null) + throw new InvalidOperationException($"Could not create a connection for provider \"{providerName}\"."); + // Edition: "Express Edition", "Windows Azure SQL Database..." // EngineEdition: 1/Desktop 2/Standard 3/Enterprise 4/Express 5/Azure // ProductLevel: RTM, SPx, CTP... @@ -123,44 +122,30 @@ namespace Umbraco.Core.Persistence.SqlSyntax SERVERPROPERTY('ResourceLastUpdateDateTime') ResourceLastUpdateDateTime, SERVERPROPERTY('ProductLevel') ProductLevel;"; - try - { - var version = database.Fetch(sql).First(); - version.Initialize(); - return version; - } - catch (Exception e) - { - // can't ignore, really - throw new Exception("Failed to determine Sql Server version (see inner exception).", e); - } - } - - internal static VersionName GetVersionName(string connectionString, string providerName) - { - var factory = DbProviderFactories.GetFactory(providerName); - var connection = factory.CreateConnection(); - - if (connection == null) - throw new InvalidOperationException($"Could not create a connection for provider \"{providerName}\"."); - connection.ConnectionString = connectionString; + ServerVersionInfo version; using (connection) { try { connection.Open(); var command = connection.CreateCommand(); - command.CommandText = "SELECT SERVERPROPERTY('ProductVersion');"; - var productVersion = command.ExecuteScalar().ToString(); + command.CommandText = sql; + using (var reader = command.ExecuteReader()) + { + reader.Read(); + version = new ServerVersionInfo(reader.GetString(0), reader.GetString(2), reader.GetString(3), (EngineEdition) reader.GetInt32(5), reader.GetString(7), reader.GetString(9)); + } connection.Close(); - return MapProductVersion(productVersion); } - catch + catch (Exception e) { - return VersionName.Unknown; + logger.Error(e, "Failed to detected SqlServer version."); + version = new ServerVersionInfo(); // all unknown } } + + return ServerVersion = version; } /// diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderAttribute.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderAttribute.cs deleted file mode 100644 index 191ee86bac..0000000000 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderAttribute.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; - -namespace Umbraco.Core.Persistence.SqlSyntax -{ - /// - /// Attribute for implementations of an ISqlSyntaxProvider - /// - [AttributeUsage(AttributeTargets.Class)] - public class SqlSyntaxProviderAttribute : Attribute - { - public SqlSyntaxProviderAttribute(string providerName) - { - ProviderName = providerName; - } - - /// - /// Gets or sets the ProviderName that corresponds to the sql syntax in a provider. - /// - public string ProviderName { get; set; } - } -} diff --git a/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs b/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs index 1e67993895..c9a509fe94 100644 --- a/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs +++ b/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs @@ -1,14 +1,11 @@ using System; -using System.Collections.Generic; using System.Configuration; using System.Data.Common; -using System.Linq; using System.Threading; using NPoco; using NPoco.FluentMappings; using Umbraco.Core.Exceptions; using Umbraco.Core.Logging; -using Umbraco.Core.Migrations.Install; using Umbraco.Core.Persistence.FaultHandling; using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.SqlSyntax; @@ -27,10 +24,8 @@ namespace Umbraco.Core.Persistence /// internal class UmbracoDatabaseFactory : DisposableObject, IUmbracoDatabaseFactory { - private readonly ISqlSyntaxProvider[] _sqlSyntaxProviders; - private readonly IMapperCollection _mappers; + private readonly Lazy _mappers; private readonly ILogger _logger; - private readonly SqlContext _sqlContext = new SqlContext(); private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); private DatabaseFactory _npocoDatabaseFactory; @@ -51,24 +46,20 @@ namespace Umbraco.Core.Persistence /// /// Initializes a new instance of the . /// - /// Used by LightInject. - public UmbracoDatabaseFactory(IEnumerable sqlSyntaxProviders, ILogger logger, IMapperCollection mappers) - : this(Constants.System.UmbracoConnectionName, sqlSyntaxProviders, logger, mappers) - { - if (Configured == false) - DatabaseBuilder.GiveLegacyAChance(this, logger); - } + /// Used by core runtime. + public UmbracoDatabaseFactory(ILogger logger, Lazy mappers) + : this(Constants.System.UmbracoConnectionName, logger, mappers) + { } /// /// Initializes a new instance of the . /// /// Used by the other ctor and in tests. - public UmbracoDatabaseFactory(string connectionStringName, IEnumerable sqlSyntaxProviders, ILogger logger, IMapperCollection mappers) + public UmbracoDatabaseFactory(string connectionStringName, ILogger logger, Lazy mappers) { if (string.IsNullOrWhiteSpace(connectionStringName)) throw new ArgumentNullOrEmptyException(nameof(connectionStringName)); _mappers = mappers ?? throw new ArgumentNullException(nameof(mappers)); - _sqlSyntaxProviders = sqlSyntaxProviders?.ToArray() ?? throw new ArgumentNullException(nameof(sqlSyntaxProviders)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); var settings = ConfigurationManager.ConnectionStrings[connectionStringName]; @@ -92,10 +83,9 @@ namespace Umbraco.Core.Persistence /// Initializes a new instance of the . /// /// Used in tests. - public UmbracoDatabaseFactory(string connectionString, string providerName, IEnumerable sqlSyntaxProviders, ILogger logger, IMapperCollection mappers) + public UmbracoDatabaseFactory(string connectionString, string providerName, ILogger logger, Lazy mappers) { _mappers = mappers ?? throw new ArgumentNullException(nameof(mappers)); - _sqlSyntaxProviders = sqlSyntaxProviders?.ToArray() ?? throw new ArgumentNullException(nameof(sqlSyntaxProviders)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); if (string.IsNullOrWhiteSpace(connectionString) || string.IsNullOrWhiteSpace(providerName)) @@ -139,7 +129,7 @@ namespace Umbraco.Core.Persistence if (setting.IsNullOrWhiteSpace() || !setting.StartsWith("SqlServer.") || !Enum.TryParse(setting.Substring("SqlServer.".Length), out var versionName, true)) { - versionName = SqlServerSyntaxProvider.GetVersionName(_connectionString, _providerName); + versionName = ((SqlServerSyntaxProvider) _sqlSyntax).GetSetVersion(_connectionString, _providerName, _logger).ProductVersionName; } else { @@ -165,7 +155,7 @@ namespace Umbraco.Core.Persistence } /// - public ISqlContext SqlContext => _sqlContext; + public ISqlContext SqlContext { get; private set; } /// public void ConfigureForUpgrade() @@ -218,9 +208,7 @@ namespace Umbraco.Core.Persistence if (_npocoDatabaseFactory == null) throw new NullReferenceException("The call to UmbracoDatabaseFactory.Config yielded a null UmbracoDatabaseFactory instance."); - // can initialize now because it is the UmbracoDatabaseFactory that determines - // the sql syntax, poco data factory, and database type - _sqlContext.Initialize(_sqlSyntax, _databaseType, _pocoDataFactory, _mappers); + SqlContext = new SqlContext(_sqlSyntax, _databaseType, _pocoDataFactory, _mappers); _logger.Debug("Configured."); Configured = true; @@ -245,17 +233,17 @@ namespace Umbraco.Core.Persistence // gets the sql syntax provider that corresponds, from attribute private ISqlSyntaxProvider GetSqlSyntaxProvider(string providerName) { - var name = providerName.ToLowerInvariant(); - var provider = _sqlSyntaxProviders.FirstOrDefault(x => - x.GetType() - .FirstAttribute() - .ProviderName.ToLowerInvariant() - .Equals(name)); - if (provider != null) return provider; - throw new InvalidOperationException($"Unknown provider name \"{providerName}\""); - - // previously we'd try to return SqlServerSyntaxProvider by default but this is bad - //provider = _syntaxProviders.FirstOrDefault(x => x.GetType() == typeof(SqlServerSyntaxProvider)); + switch (providerName) + { + case Constants.DbProviderNames.MySql: + return new MySqlSyntaxProvider(_logger); + case Constants.DbProviderNames.SqlCe: + return new SqlCeSyntaxProvider(); + case Constants.DbProviderNames.SqlServer: + return new SqlServerSyntaxProvider(); + default: + throw new InvalidOperationException($"Unknown provider name \"{providerName}\""); + } } // ensures that the database is configured, else throws @@ -277,7 +265,7 @@ namespace Umbraco.Core.Persistence // method used by NPoco's UmbracoDatabaseFactory to actually create the database instance private UmbracoDatabase CreateDatabaseInstance() { - return new UmbracoDatabase(_connectionString, _sqlContext, _dbProviderFactory, _logger, _connectionRetryPolicy, _commandRetryPolicy); + return new UmbracoDatabase(_connectionString, SqlContext, _dbProviderFactory, _logger, _connectionRetryPolicy, _commandRetryPolicy); } protected override void DisposeResources() diff --git a/src/Umbraco.Core/PropertyEditors/DataEditorCollectionBuilder.cs b/src/Umbraco.Core/PropertyEditors/DataEditorCollectionBuilder.cs index 2a53142a1c..c0c0a3651e 100644 --- a/src/Umbraco.Core/PropertyEditors/DataEditorCollectionBuilder.cs +++ b/src/Umbraco.Core/PropertyEditors/DataEditorCollectionBuilder.cs @@ -1,14 +1,9 @@ -using LightInject; -using Umbraco.Core.Composing; +using Umbraco.Core.Composing; namespace Umbraco.Core.PropertyEditors { public class DataEditorCollectionBuilder : LazyCollectionBuilderBase { - public DataEditorCollectionBuilder(IServiceContainer container) - : base(container) - { } - protected override DataEditorCollectionBuilder This => this; } } diff --git a/src/Umbraco.Core/PropertyEditors/ManifestValueValidatorCollectionBuilder.cs b/src/Umbraco.Core/PropertyEditors/ManifestValueValidatorCollectionBuilder.cs index d616ecf715..8f7c68c813 100644 --- a/src/Umbraco.Core/PropertyEditors/ManifestValueValidatorCollectionBuilder.cs +++ b/src/Umbraco.Core/PropertyEditors/ManifestValueValidatorCollectionBuilder.cs @@ -1,14 +1,9 @@ -using LightInject; -using Umbraco.Core.Composing; +using Umbraco.Core.Composing; namespace Umbraco.Core.PropertyEditors { internal class ManifestValueValidatorCollectionBuilder : LazyCollectionBuilderBase { - public ManifestValueValidatorCollectionBuilder(IServiceContainer container) - : base(container) - { } - protected override ManifestValueValidatorCollectionBuilder This => this; } } diff --git a/src/Umbraco.Core/PropertyEditors/PropertyValueConverterCollectionBuilder.cs b/src/Umbraco.Core/PropertyEditors/PropertyValueConverterCollectionBuilder.cs index 5c5a8c16c8..a64fe8c43a 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyValueConverterCollectionBuilder.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyValueConverterCollectionBuilder.cs @@ -1,14 +1,9 @@ -using LightInject; -using Umbraco.Core.Composing; +using Umbraco.Core.Composing; namespace Umbraco.Core.PropertyEditors { public class PropertyValueConverterCollectionBuilder : OrderedCollectionBuilderBase { - public PropertyValueConverterCollectionBuilder(IServiceContainer container) - : base(container) - { } - protected override PropertyValueConverterCollectionBuilder This => this; } } diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/GridValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/GridValueConverter.cs index 2131764ad6..29f6de0271 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/GridValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/GridValueConverter.cs @@ -7,6 +7,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Umbraco.Core.Configuration; using Umbraco.Core.Composing; +using Umbraco.Core.Configuration.Grid; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models.PublishedContent; @@ -19,9 +20,13 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters [DefaultPropertyValueConverter(typeof(JsonValueConverter))] //this shadows the JsonValueConverter public class GridValueConverter : JsonValueConverter { - public GridValueConverter(PropertyEditorCollection propertyEditors) + private readonly IGridConfig _config; + + public GridValueConverter(PropertyEditorCollection propertyEditors, IGridConfig config) : base(propertyEditors) - { } + { + _config = config; + } public override bool IsConverter(PublishedPropertyType propertyType) => propertyType.EditorAlias.InvariantEquals(Constants.PropertyEditors.Aliases.Grid); @@ -46,15 +51,6 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters //so we have the grid json... we need to merge in the grid's configuration values with the values // we've saved in the database so that when the front end gets this value, it is up-to-date. - //TODO: Change all singleton access to use ctor injection in v8!!! - //TODO: That would mean that property value converters would need to be request lifespan, hrm.... - var gridConfig = UmbracoConfig.For.GridConfig( - Current.ProfilingLogger.Logger, - Current.ApplicationCache.RuntimeCache, - new DirectoryInfo(IOHelper.MapPath(SystemDirectories.AppPlugins)), - new DirectoryInfo(IOHelper.MapPath(SystemDirectories.Config)), - Current.RuntimeState.Debug); - var sections = GetArray(obj, "sections"); foreach (var section in sections.Cast()) { @@ -74,7 +70,7 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters if (alias.IsNullOrWhiteSpace() == false) { //find the alias in config - var found = gridConfig.EditorsConfig.Editors.FirstOrDefault(x => x.Alias == alias); + var found = _config.EditorsConfig.Editors.FirstOrDefault(x => x.Alias == alias); if (found != null) { //add/replace the editor value with the one from config diff --git a/src/Umbraco.Core/ReflectionUtilities.cs b/src/Umbraco.Core/ReflectionUtilities.cs index 870cb9ec13..622d81f5f2 100644 --- a/src/Umbraco.Core/ReflectionUtilities.cs +++ b/src/Umbraco.Core/ReflectionUtilities.cs @@ -295,7 +295,7 @@ namespace Umbraco.Core /// Occurs when the constructor does not exist and is true. /// Occurs when is not a Func or when /// is specified and does not match the function's returned type. - public static TLambda EmitConstuctor(bool mustExist = true, Type declaring = null) + public static TLambda EmitConstructor(bool mustExist = true, Type declaring = null) { var (_, lambdaParameters, lambdaReturned) = AnalyzeLambda(true, true); diff --git a/src/Umbraco.Core/Runtime/CoreRuntime.cs b/src/Umbraco.Core/Runtime/CoreRuntime.cs old mode 100755 new mode 100644 index dde91ad385..5d2359d04c --- a/src/Umbraco.Core/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Core/Runtime/CoreRuntime.cs @@ -1,14 +1,13 @@ using System; using System.Collections.Generic; -using System.Configuration; +using System.Diagnostics; +using System.Linq; using System.Reflection; using System.Threading; using System.Web; -using LightInject; using Umbraco.Core.Cache; using Umbraco.Core.Components; using Umbraco.Core.Composing; -using Umbraco.Core.Composing.CompositionRoots; using Umbraco.Core.Configuration; using Umbraco.Core.Exceptions; using Umbraco.Core.IO; @@ -16,11 +15,9 @@ using Umbraco.Core.Logging; using Umbraco.Core.Logging.Serilog; using Umbraco.Core.Migrations.Upgrade; using Umbraco.Core.Persistence; -using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.Persistence.Mappers; -using Umbraco.Core.Persistence.SqlSyntax; -using Umbraco.Core.Scoping; using Umbraco.Core.Services.Implement; +using Umbraco.Core.Sync; namespace Umbraco.Core.Runtime { @@ -31,44 +28,38 @@ namespace Umbraco.Core.Runtime /// should be possible to use this runtime in console apps. public class CoreRuntime : IRuntime { - private BootLoader _bootLoader; + private ComponentCollection _components; + private IFactory _factory; private RuntimeState _state; /// - /// Initializes a new instance of the class. + /// Gets the logger. /// - public CoreRuntime() - { } + protected ILogger Logger { get; private set; } + + /// + /// Gets the profiler. + /// + protected IProfiler Profiler { get; private set; } + + /// + /// Gets the profiling logger. + /// + protected IProfilingLogger ProfilingLogger { get; private set; } + + /// + public IRuntimeState State => _state; /// - public virtual void Boot(ServiceContainer container) + public virtual IFactory Boot(IRegister register) { - container.ConfigureUmbracoCore(); // also sets Current.Container + // create and register the essential services + // ie the bare minimum required to boot - // register the essential stuff, - // ie the global application logger - // (profiler etc depend on boot manager) - var logger = GetLogger(); - container.RegisterInstance(logger); - // now it is ok to use Current.Logger - - ConfigureUnhandledException(logger); - ConfigureAssemblyResolve(logger); - - Compose(container); - - // prepare essential stuff - - var path = GetApplicationRootPath(); - if (string.IsNullOrWhiteSpace(path) == false) - IOHelper.SetRootDirectory(path); - - _state = (RuntimeState) container.GetInstance(); - _state.Level = RuntimeLevel.Boot; - - Logger = container.GetInstance(); - Profiler = container.GetInstance(); - ProfilingLogger = container.GetInstance(); + // loggers + var logger = Logger = GetLogger(); + var profiler = Profiler = GetProfiler(); + var profilingLogger = ProfilingLogger = new ProfilingLogger(logger, profiler); // the boot loader boots using a container scope, so anything that is PerScope will // be disposed after the boot loader has booted, and anything else will remain. @@ -79,56 +70,126 @@ namespace Umbraco.Core.Runtime // are NOT disposed - which is not a big deal as long as they remain lightweight // objects. - using (var bootTimer = ProfilingLogger.TraceDuration( + using (var timer = profilingLogger.TraceDuration( $"Booting Umbraco {UmbracoVersion.SemanticVersion.ToSemanticString()} on {NetworkHelper.MachineName}.", "Booted.", "Boot failed.")) { - // throws if not full-trust - new AspNetHostingPermission(AspNetHostingPermissionLevel.Unrestricted).Demand(); + logger.Debug("Runtime: {Runtime}", GetType().FullName); - try - { - Logger.Debug("Runtime: {Runtime}", GetType().FullName); + // application environment + ConfigureUnhandledException(); + ConfigureAssemblyResolve(); + ConfigureApplicationRootPath(); - AquireMainDom(container); - DetermineRuntimeLevel(container); - var componentTypes = ResolveComponentTypes(); - _bootLoader = new BootLoader(container); - _bootLoader.Boot(componentTypes, _state.Level); - } - catch (Exception e) - { - _state.Level = RuntimeLevel.BootFailed; - var bfe = e as BootFailedException ?? new BootFailedException("Boot failed.", e); - _state.BootFailedException = bfe; - bootTimer.Fail(exception: bfe); // be sure to log the exception - even if we repeat ourselves - - // throwing here can cause w3wp to hard-crash and we want to avoid it. - // instead, we're logging the exception and setting level to BootFailed. - // various parts of Umbraco such as UmbracoModule and UmbracoDefaultOwinStartup - // understand this and will nullify themselves, while UmbracoModule will - // throw a BootFailedException for every requests. - } + Boot(register, timer); } - //fixme - // after Umbraco has started there is a scope in "context" and that context is - // going to stay there and never get destroyed nor reused, so we have to ensure that - // everything is cleared - //var sa = container.GetInstance(); - //sa.Scope?.Dispose(); + return _factory; } /// - /// Gets a logger. + /// Boots the runtime within a timer. /// - protected virtual ILogger GetLogger() + protected virtual IFactory Boot(IRegister register, DisposableTimer timer) { - return SerilogLogger.CreateWithDefaultConfiguration(); + Composition composition = null; + + try + { + // throws if not full-trust + new AspNetHostingPermission(AspNetHostingPermissionLevel.Unrestricted).Demand(); + + // application caches + var appCaches = GetAppCaches(); + var runtimeCache = appCaches.RuntimeCache; + + // database factory + var databaseFactory = GetDatabaseFactory(); + + // configs + var configs = GetConfigs(); + + // type loader + var localTempStorage = configs.Global().LocalTempStorageLocation; + var typeLoader = new TypeLoader(runtimeCache, localTempStorage, ProfilingLogger); + + // runtime state + // beware! must use '() => _factory.GetInstance()' and NOT '_factory.GetInstance' + // as the second one captures the current value (null) and therefore fails + _state = new RuntimeState(Logger, + configs.Settings(), configs.Global(), + new Lazy(() => _factory.GetInstance()), + new Lazy(() => _factory.GetInstance())) + { + Level = RuntimeLevel.Boot + }; + + // main dom + var mainDom = new MainDom(Logger); + + // create the composition + composition = new Composition(register, typeLoader, ProfilingLogger, _state, configs); + composition.RegisterEssentials(Logger, Profiler, ProfilingLogger, mainDom, appCaches, databaseFactory, typeLoader, _state); + + // register runtime-level services + // there should be none, really - this is here "just in case" + Compose(composition); + + // acquire the main domain, determine our runtime level + AcquireMainDom(mainDom); + DetermineRuntimeLevel(databaseFactory, ProfilingLogger); + + // get composers, and compose + var composerTypes = ResolveComposerTypes(typeLoader); + composition.WithCollectionBuilder(); + var composers = new Composers(composition, composerTypes, ProfilingLogger); + composers.Compose(); + + // create the factory + _factory = Current.Factory = composition.CreateFactory(); + + // create & initialize the components + _components = _factory.GetInstance(); + _components.Initialize(); + } + catch (Exception e) + { + var bfe = e as BootFailedException ?? new BootFailedException("Boot failed.", e); + + if (_state != null) + { + _state.Level = RuntimeLevel.BootFailed; + _state.BootFailedException = bfe; + } + + timer.Fail(exception: bfe); // be sure to log the exception - even if we repeat ourselves + + // if something goes wrong above, we may end up with no factory + // meaning nothing can get the runtime state, etc - so let's try + // to make sure we have a factory + if (_factory == null) + { + try + { + _factory = Current.Factory = composition?.CreateFactory(); + } + catch { /* yea */ } + } + + Debugger.Break(); + + // throwing here can cause w3wp to hard-crash and we want to avoid it. + // instead, we're logging the exception and setting level to BootFailed. + // various parts of Umbraco such as UmbracoModule and UmbracoDefaultOwinStartup + // understand this and will nullify themselves, while UmbracoModule will + // throw a BootFailedException for every requests. + } + + return _factory; } - protected virtual void ConfigureUnhandledException(ILogger logger) + protected virtual void ConfigureUnhandledException() { //take care of unhandled exceptions - there is nothing we can do to // prevent the launch process to go down but at least we can try @@ -141,32 +202,37 @@ namespace Umbraco.Core.Runtime var msg = "Unhandled exception in AppDomain"; if (isTerminating) msg += " (terminating)"; msg += "."; - logger.Error(exception, msg); + Logger.Error(exception, msg); }; } - protected virtual void ConfigureAssemblyResolve(ILogger logger) + protected virtual void ConfigureAssemblyResolve() { // When an assembly can't be resolved. In here we can do magic with the assembly name and try loading another. // This is used for loading a signed assembly of AutoMapper (v. 3.1+) without having to recompile old code. AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => { // ensure the assembly is indeed AutoMapper and that the PublicKeyToken is null before trying to Load again - // do NOT just replace this with 'return Assembly', as it will cause an infinite loop -> stackoverflow + // do NOT just replace this with 'return Assembly', as it will cause an infinite loop -> stack overflow if (args.Name.StartsWith("AutoMapper") && args.Name.EndsWith("PublicKeyToken=null")) return Assembly.Load(args.Name.Replace(", PublicKeyToken=null", ", PublicKeyToken=be96cd2c38ef1005")); return null; }; } - - private void AquireMainDom(IServiceFactory container) + protected virtual void ConfigureApplicationRootPath() { - using (var timer = ProfilingLogger.DebugDuration("Acquiring MainDom.", "Aquired.")) + var path = GetApplicationRootPath(); + if (string.IsNullOrWhiteSpace(path) == false) + IOHelper.SetRootDirectory(path); + } + + private void AcquireMainDom(MainDom mainDom) + { + using (var timer = ProfilingLogger.DebugDuration("Acquiring MainDom.", "Acquired.")) { try { - var mainDom = container.GetInstance(); mainDom.Acquire(); } catch @@ -178,38 +244,38 @@ namespace Umbraco.Core.Runtime } // internal for tests - internal void DetermineRuntimeLevel(IServiceFactory container) + internal void DetermineRuntimeLevel(IUmbracoDatabaseFactory databaseFactory, IProfilingLogger profilingLogger) { - using (var timer = ProfilingLogger.DebugDuration("Determining runtime level.", "Determined.")) + using (var timer = profilingLogger.DebugDuration("Determining runtime level.", "Determined.")) { try { - var dbfactory = container.GetInstance(); - SetRuntimeStateLevel(dbfactory, Logger); + _state.DetermineRuntimeLevel(databaseFactory, profilingLogger); - Logger.Debug("Runtime level: {RuntimeLevel}", _state.Level); + profilingLogger.Debug("Runtime level: {RuntimeLevel}", _state.Level); if (_state.Level == RuntimeLevel.Upgrade) { - Logger.Debug("Configure database factory for upgrades."); - dbfactory.ConfigureForUpgrade(); + profilingLogger.Debug("Configure database factory for upgrades."); + databaseFactory.ConfigureForUpgrade(); } } catch { + _state.Level = RuntimeLevel.BootFailed; timer.Fail(); throw; } } } - private IEnumerable ResolveComponentTypes() + private IEnumerable ResolveComposerTypes(TypeLoader typeLoader) { - using (var timer = ProfilingLogger.TraceDuration("Resolving component types.", "Resolved.")) + using (var timer = ProfilingLogger.TraceDuration("Resolving composer types.", "Resolved.")) { try { - return GetComponentTypes(); + return GetComposerTypes(typeLoader); } catch { @@ -222,208 +288,76 @@ namespace Umbraco.Core.Runtime /// public virtual void Terminate() { - using (ProfilingLogger.DebugDuration("Terminating Umbraco.", "Terminated.")) - { - _bootLoader?.Terminate(); - } + _components.Terminate(); } /// /// Composes the runtime. /// - public virtual void Compose(ServiceContainer container) + public virtual void Compose(Composition composition) { - // compose the very essential things that are needed to bootstrap, before anything else, - // and only these things - the rest should be composed in runtime components - - // register basic things - // logging, runtime state, configuration - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterSingleton(); - container.RegisterFrom(); - - // register caches - // need the deep clone runtime cache profiver to ensure entities are cached properly, ie - // are cloned in and cloned out - no request-based cache here since no web-based context, - // will be overriden later or - container.RegisterSingleton(_ => new CacheHelper( - new DeepCloneRuntimeCacheProvider(new ObjectCacheRuntimeCacheProvider()), - new StaticCacheProvider(), - NullCacheProvider.Instance, - new IsolatedRuntimeCache(type => new DeepCloneRuntimeCacheProvider(new ObjectCacheRuntimeCacheProvider())))); - container.RegisterSingleton(f => f.GetInstance().RuntimeCache); - - // register the type loader - container.RegisterSingleton(); - - // register syntax providers - required by database factory - container.Register("MySqlSyntaxProvider"); - container.Register("SqlCeSyntaxProvider"); - container.Register("SqlServerSyntaxProvider"); - - // register persistence mappers - required by database factory so needs to be done here - // means the only place the collection can be modified is in a runtime - afterwards it - // has been frozen and it is too late - var mapperCollectionBuilder = container.RegisterCollectionBuilder(); - ComposeMapperCollection(mapperCollectionBuilder); - - // register database factory - required to check for migrations - // will be initialized with syntax providers and a logger, and will try to configure - // from the default connection string name, if possible, else will remain non-configured - // until properly configured (eg when installing) - container.RegisterSingleton(); - container.RegisterSingleton(f => f.GetInstance().SqlContext); - - // register the scope provider - container.RegisterSingleton(); // implements both IScopeProvider and IScopeAccessor - container.RegisterSingleton(f => f.GetInstance()); - container.RegisterSingleton(f => f.GetInstance()); - - // register MainDom - container.RegisterSingleton(); + // nothing } - protected virtual void ComposeMapperCollection(MapperCollectionBuilder builder) - { - builder.AddCore(); - } - - private void SetRuntimeStateLevel(IUmbracoDatabaseFactory databaseFactory, ILogger logger) - { - var localVersion = UmbracoVersion.LocalVersion; // the local, files, version - var codeVersion = _state.SemanticVersion; // the executing code version - var connect = false; - - // we don't know yet - _state.Level = RuntimeLevel.Unknown; - - if (localVersion == null) - { - // there is no local version, we are not installed - logger.Debug("No local version, need to install Umbraco."); - _state.Level = RuntimeLevel.Install; - } - else if (localVersion < codeVersion) - { - // there *is* a local version, but it does not match the code version - // need to upgrade - logger.Debug("Local version '{LocalVersion}' < code version '{CodeVersion}', need to upgrade Umbraco.", localVersion, codeVersion); - _state.Level = RuntimeLevel.Upgrade; - } - else if (localVersion > codeVersion) - { - logger.Warn("Local version '{LocalVersion}' > code version '{CodeVersion}', downgrading is not supported.", localVersion, codeVersion); - _state.Level = RuntimeLevel.BootFailed; - - // in fact, this is bad enough that we want to throw - throw new BootFailedException($"Local version \"{localVersion}\" > code version \"{codeVersion}\", downgrading is not supported."); - } - else if (databaseFactory.Configured == false) - { - // local version *does* match code version, but the database is not configured - // install (again? this is a weird situation...) - logger.Debug("Database is not configured, need to install Umbraco."); - _state.Level = RuntimeLevel.Install; - } - - // install? not going to test anything else - if (_state.Level == RuntimeLevel.Install) - return; - - // else, keep going, - // anything other than install wants a database - see if we can connect - // (since this is an already existing database, assume localdb is ready) - for (var i = 0; i < 5; i++) - { - connect = databaseFactory.CanConnect; - if (connect) break; - logger.Debug("Could not immediately connect to database, trying again."); - Thread.Sleep(1000); - } - - if (connect == false) - { - // cannot connect to configured database, this is bad, fail - logger.Debug("Could not connect to database."); - _state.Level = RuntimeLevel.BootFailed; - - // in fact, this is bad enough that we want to throw - throw new BootFailedException("A connection string is configured but Umbraco could not connect to the database."); - } - - // if we already know we want to upgrade, - // still run EnsureUmbracoUpgradeState to get the states - // (v7 will just get a null state, that's ok) - - // else - // look for a matching migration entry - bypassing services entirely - they are not 'up' yet - // fixme - in a LB scenario, ensure that the DB gets upgraded only once! - bool noUpgrade; - try - { - noUpgrade = EnsureUmbracoUpgradeState(databaseFactory, logger); - } - catch (Exception e) - { - // can connect to the database but cannot check the upgrade state... oops - logger.Warn(e, "Could not check the upgrade state."); - throw new BootFailedException("Could not check the upgrade state.", e); - } - - if (noUpgrade) - { - // the database version matches the code & files version, all clear, can run - _state.Level = RuntimeLevel.Run; - return; - } - - // the db version does not match... but we do have a migration table - // so, at least one valid table, so we quite probably are installed & need to upgrade - - // although the files version matches the code version, the database version does not - // which means the local files have been upgraded but not the database - need to upgrade - logger.Debug("Has not reached the final upgrade step, need to upgrade Umbraco."); - _state.Level = RuntimeLevel.Upgrade; - } - - protected virtual bool EnsureUmbracoUpgradeState(IUmbracoDatabaseFactory databaseFactory, ILogger logger) - { - var upgrader = new UmbracoUpgrader(); - var stateValueKey = upgrader.StateValueKey; - - // no scope, no service - just directly accessing the database - using (var database = databaseFactory.CreateDatabase()) - { - _state.CurrentMigrationState = KeyValueService.GetValue(database, stateValueKey); - _state.FinalMigrationState = upgrader.Plan.FinalState; - } - - logger.Debug("Final upgrade state is {FinalMigrationState}, database contains {DatabaseState}", _state.FinalMigrationState, _state.CurrentMigrationState ?? ""); - - return _state.CurrentMigrationState == _state.FinalMigrationState; - } - - #region Locals - - protected ILogger Logger { get; private set; } - - protected IProfiler Profiler { get; private set; } - - protected ProfilingLogger ProfilingLogger { get; private set; } - - #endregion - #region Getters // getters can be implemented by runtimes inheriting from CoreRuntime - // fixme - inject! no Current! - protected virtual IEnumerable GetComponentTypes() => Current.TypeLoader.GetTypes(); + /// + /// Gets all composer types. + /// + protected virtual IEnumerable GetComposerTypes(TypeLoader typeLoader) + => typeLoader.GetTypes(); + + /// + /// Gets a logger. + /// + protected virtual ILogger GetLogger() + => SerilogLogger.CreateWithDefaultConfiguration(); + + /// + /// Gets a profiler. + /// + protected virtual IProfiler GetProfiler() + => new LogProfiler(ProfilingLogger); + + /// + /// Gets the application caches. + /// + protected virtual CacheHelper GetAppCaches() + { + // need the deep clone runtime cache provider to ensure entities are cached properly, ie + // are cloned in and cloned out - no request-based cache here since no web-based context, + // is overriden by the web runtime + + return new CacheHelper( + new DeepCloneRuntimeCacheProvider(new ObjectCacheRuntimeCacheProvider()), + new StaticCacheProvider(), + NullCacheProvider.Instance, + new IsolatedRuntimeCache(type => new DeepCloneRuntimeCacheProvider(new ObjectCacheRuntimeCacheProvider()))); + } // by default, returns null, meaning that Umbraco should auto-detect the application root path. // override and return the absolute path to the Umbraco site/solution, if needed - protected virtual string GetApplicationRootPath() => null; + protected virtual string GetApplicationRootPath() + => null; + + /// + /// Gets the database factory. + /// + /// This is strictly internal, for tests only. + protected internal virtual IUmbracoDatabaseFactory GetDatabaseFactory() + => new UmbracoDatabaseFactory(Logger, new Lazy(() => _factory.GetInstance())); + + /// + /// Gets the configurations. + /// + protected virtual Configs GetConfigs() + { + var configs = new Configs(); + configs.AddCoreConfigs(); + return configs; + } #endregion } diff --git a/src/Umbraco.Core/Runtime/CoreRuntimeComponent.cs b/src/Umbraco.Core/Runtime/CoreRuntimeComponent.cs index 90d3ece254..b9efdd6432 100644 --- a/src/Umbraco.Core/Runtime/CoreRuntimeComponent.cs +++ b/src/Umbraco.Core/Runtime/CoreRuntimeComponent.cs @@ -1,133 +1,25 @@ -using System; -using System.Collections.Generic; -using System.Configuration; -using System.IO; +using System.Collections.Generic; using AutoMapper; -using LightInject; -using Umbraco.Core.Cache; using Umbraco.Core.Components; -using Umbraco.Core.Composing; -using Umbraco.Core.Composing.CompositionRoots; -using Umbraco.Core.Configuration; -using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; -using Umbraco.Core.IO.MediaPathSchemes; -using Umbraco.Core.Logging; -using Umbraco.Core.Manifest; -using Umbraco.Core.Migrations; -using Umbraco.Core.Migrations.Install; -using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.Persistence; -using Umbraco.Core.PropertyEditors; -using Umbraco.Core.PropertyEditors.Validators; -using Umbraco.Core.Scoping; -using Umbraco.Core.Services; -using Umbraco.Core.Strings; -using Umbraco.Core.Sync; -using Umbraco.Core._Legacy.PackageActions; -using IntegerValidator = Umbraco.Core.PropertyEditors.Validators.IntegerValidator; namespace Umbraco.Core.Runtime { - public class CoreRuntimeComponent : UmbracoComponentBase, IRuntimeComponent + public class CoreRuntimeComponent : IComponent { - public override void Compose(Composition composition) + private readonly IEnumerable _mapperProfiles; + + public CoreRuntimeComponent(IEnumerable mapperProfiles) { - base.Compose(composition); - - // register from roots - composition.Container.RegisterFrom(); - composition.Container.RegisterFrom(); - composition.Container.RegisterFrom(); - - // register database builder - // *not* a singleton, don't want to keep it around - composition.Container.Register(); - - // register filesystems - composition.Container.RegisterSingleton(); - composition.Container.RegisterSingleton(factory => factory.GetInstance().MediaFileSystem); - composition.Container.RegisterSingleton(factory => factory.GetInstance().ScriptsFileSystem, Constants.Composing.FileSystems.ScriptFileSystem); - composition.Container.RegisterSingleton(factory => factory.GetInstance().PartialViewsFileSystem, Constants.Composing.FileSystems.PartialViewFileSystem); - composition.Container.RegisterSingleton(factory => factory.GetInstance().MacroPartialsFileSystem, Constants.Composing.FileSystems.PartialViewMacroFileSystem); - composition.Container.RegisterSingleton(factory => factory.GetInstance().StylesheetsFileSystem, Constants.Composing.FileSystems.StylesheetFileSystem); - composition.Container.RegisterSingleton(factory => factory.GetInstance().MasterPagesFileSystem, Constants.Composing.FileSystems.MasterpageFileSystem); - composition.Container.RegisterSingleton(factory => factory.GetInstance().MvcViewsFileSystem, Constants.Composing.FileSystems.ViewFileSystem); - - // register manifest parser, will be injected in collection builders where needed - composition.Container.RegisterSingleton(); - - // register our predefined validators - composition.Container.RegisterCollectionBuilder() - .Add() - .Add() - .Add() - .Add() - .Add() - .Add(); - - // properties and parameters derive from data editors - composition.Container.RegisterCollectionBuilder() - .Add(factory => factory.GetInstance().GetDataEditors()); - composition.Container.RegisterSingleton(); - composition.Container.RegisterSingleton(); - - // register a server registrar, by default it's the db registrar - composition.Container.RegisterSingleton(f => - { - if ("true".InvariantEquals(ConfigurationManager.AppSettings["umbracoDisableElectionForSingleServer"])) - return new SingleServerRegistrar(f.GetInstance()); - return new DatabaseServerRegistrar( - new Lazy(f.GetInstance), - new DatabaseServerRegistrarOptions()); - }); - - // by default we'll use the database server messenger with default options (no callbacks), - // this will be overridden by either the legacy thing or the db thing in the corresponding - // components in the web project - fixme - should obsolete the legacy thing - composition.Container.RegisterSingleton(factory - => new DatabaseServerMessenger( - factory.GetInstance(), - factory.GetInstance(), - factory.GetInstance(), - factory.GetInstance(), - factory.GetInstance(), - true, new DatabaseServerMessengerOptions())); - - composition.Container.RegisterCollectionBuilder() - .Add(factory => factory.GetInstance().GetCacheRefreshers()); - - composition.Container.RegisterCollectionBuilder() - .Add(f => f.GetInstance().GetPackageActions()); - - composition.Container.RegisterCollectionBuilder() - .Append(factory => factory.GetInstance().GetTypes()); - - composition.Container.Register(new PerContainerLifetime()); - - composition.Container.RegisterSingleton(factory - => new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(factory.GetInstance()))); - - composition.Container.RegisterCollectionBuilder() - .Append(); - - composition.Container.RegisterCollectionBuilder() - .Add(factory => factory.GetInstance().GetTypes()); - - composition.Container.RegisterSingleton(); - - // by default, register a noop factory - composition.Container.RegisterSingleton(); - - composition.Container.RegisterSingleton(); + _mapperProfiles = mapperProfiles; } - internal void Initialize(IEnumerable mapperProfiles) + public void Initialize() { // mapper profiles have been registered & are created by the container Mapper.Initialize(configuration => { - foreach (var profile in mapperProfiles) + foreach (var profile in _mapperProfiles) configuration.AddProfile(profile); }); @@ -139,5 +31,8 @@ namespace Umbraco.Core.Runtime IOHelper.EnsurePathExists(SystemDirectories.MvcViews + "/Partials"); IOHelper.EnsurePathExists(SystemDirectories.MvcViews + "/MacroPartials"); } + + public void Terminate() + { } } } diff --git a/src/Umbraco.Core/Runtime/CoreRuntimeComposer.cs b/src/Umbraco.Core/Runtime/CoreRuntimeComposer.cs new file mode 100644 index 0000000000..3d959f5263 --- /dev/null +++ b/src/Umbraco.Core/Runtime/CoreRuntimeComposer.cs @@ -0,0 +1,121 @@ +using System; +using System.Configuration; +using Umbraco.Core.Cache; +using Umbraco.Core.Components; +using Umbraco.Core.Composing; +using Umbraco.Core.Composing.Composers; +using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Logging; +using Umbraco.Core.Manifest; +using Umbraco.Core.Migrations; +using Umbraco.Core.Migrations.Install; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Mappers; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.PropertyEditors.Validators; +using Umbraco.Core.Scoping; +using Umbraco.Core.Services; +using Umbraco.Core.Strings; +using Umbraco.Core.Sync; +using Umbraco.Core._Legacy.PackageActions; +using IntegerValidator = Umbraco.Core.PropertyEditors.Validators.IntegerValidator; + +namespace Umbraco.Core.Runtime +{ + public class CoreRuntimeComposer : ComponentComposer, IRuntimeComposer + { + public override void Compose(Composition composition) + { + base.Compose(composition); + + // composers + composition + .ComposeConfiguration() + .ComposeRepositories() + .ComposeServices() + .ComposeCoreMappingProfiles() + .ComposeFileSystems(); + + // register persistence mappers - required by database factory so needs to be done here + // means the only place the collection can be modified is in a runtime - afterwards it + // has been frozen and it is too late + composition.WithCollectionBuilder().AddCoreMappers(); + + // register the scope provider + composition.RegisterUnique(); // implements both IScopeProvider and IScopeAccessor + composition.RegisterUnique(f => f.GetInstance()); + composition.RegisterUnique(f => f.GetInstance()); + + // register database builder + // *not* a singleton, don't want to keep it around + composition.Register(); + + // register manifest parser, will be injected in collection builders where needed + composition.RegisterUnique(); + + // register our predefined validators + composition.WithCollectionBuilder() + .Add() + .Add() + .Add() + .Add() + .Add() + .Add(); + + // properties and parameters derive from data editors + composition.WithCollectionBuilder() + .Add(() => composition.TypeLoader.GetDataEditors()); + composition.RegisterUnique(); + composition.RegisterUnique(); + + // register a server registrar, by default it's the db registrar + composition.RegisterUnique(f => + { + if ("true".InvariantEquals(ConfigurationManager.AppSettings["umbracoDisableElectionForSingleServer"])) + return new SingleServerRegistrar(f.GetInstance()); + return new DatabaseServerRegistrar( + new Lazy(f.GetInstance), + new DatabaseServerRegistrarOptions()); + }); + + // by default we'll use the database server messenger with default options (no callbacks), + // this will be overridden by either the legacy thing or the db thing in the corresponding + // components in the web project - fixme - should obsolete the legacy thing + composition.RegisterUnique(factory + => new DatabaseServerMessenger( + factory.GetInstance(), + factory.GetInstance(), + factory.GetInstance(), + factory.GetInstance(), + factory.GetInstance(), + true, new DatabaseServerMessengerOptions())); + + composition.WithCollectionBuilder() + .Add(() => composition.TypeLoader.GetCacheRefreshers()); + + composition.WithCollectionBuilder() + .Add(() => composition.TypeLoader.GetPackageActions()); + + composition.WithCollectionBuilder() + .Append(composition.TypeLoader.GetTypes()); + + composition.RegisterUnique(); + + composition.RegisterUnique(factory + => new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(factory.GetInstance()))); + + composition.WithCollectionBuilder() + .Append(); + + composition.WithCollectionBuilder() + .Add(() => composition.TypeLoader.GetTypes()); + + composition.RegisterUnique(factory => new MigrationBuilder(factory)); + + // by default, register a noop factory + composition.RegisterUnique(); + } + } +} diff --git a/src/Umbraco.Core/RuntimeState.cs b/src/Umbraco.Core/RuntimeState.cs index 4f6f56531b..df2ee44a7d 100644 --- a/src/Umbraco.Core/RuntimeState.cs +++ b/src/Umbraco.Core/RuntimeState.cs @@ -7,6 +7,9 @@ using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Exceptions; using Umbraco.Core.Logging; +using Umbraco.Core.Migrations.Upgrade; +using Umbraco.Core.Persistence; +using Umbraco.Core.Services.Implement; using Umbraco.Core.Sync; namespace Umbraco.Core @@ -17,76 +20,64 @@ namespace Umbraco.Core internal class RuntimeState : IRuntimeState { private readonly ILogger _logger; - private readonly Lazy _serverRegistrar; - private readonly Lazy _mainDom; private readonly IUmbracoSettingsSection _settings; private readonly IGlobalSettings _globalSettings; private readonly HashSet _applicationUrls = new HashSet(); - private RuntimeLevel _level; + private readonly Lazy _mainDom; + private readonly Lazy _serverRegistrar; + private RuntimeLevel _level = RuntimeLevel.Unknown; /// /// Initializes a new instance of the class. /// - /// A logger. - /// A (lazy) server registrar. - /// A (lazy) MainDom. - public RuntimeState(ILogger logger, Lazy serverRegistrar, Lazy mainDom, IUmbracoSettingsSection settings, IGlobalSettings globalSettings) + public RuntimeState(ILogger logger, IUmbracoSettingsSection settings, IGlobalSettings globalSettings, + Lazy mainDom, Lazy serverRegistrar) { _logger = logger; - _serverRegistrar = serverRegistrar; - _mainDom = mainDom; _settings = settings; _globalSettings = globalSettings; + _mainDom = mainDom; + _serverRegistrar = serverRegistrar; } + /// + /// Gets the server registrar. + /// + /// + /// This is NOT exposed in the interface. + /// private IServerRegistrar ServerRegistrar => _serverRegistrar.Value; /// /// Gets the application MainDom. /// - /// This is NOT exposed in the interface as MainDom is internal. - public MainDom MainDom => _mainDom.Value; + /// + /// This is NOT exposed in the interface as MainDom is internal. + /// + public IMainDom MainDom => _mainDom.Value; - /// - /// Gets the version of the executing code. - /// + /// public Version Version => UmbracoVersion.Current; - /// - /// Gets the version comment of the executing code. - /// + /// public string VersionComment => UmbracoVersion.Comment; - /// - /// Gets the semantic version of the executing code. - /// + /// public SemVersion SemanticVersion => UmbracoVersion.SemanticVersion; - /// - /// Gets a value indicating whether the application is running in debug mode. - /// + /// public bool Debug { get; } = GlobalSettings.DebugMode; - /// - /// Gets a value indicating whether the runtime is the current main domain. - /// + /// public bool IsMainDom => MainDom.IsMainDom; - /// - /// Get the server's current role. - /// + /// public ServerRole ServerRole => ServerRegistrar.GetCurrentServerRole(); - /// - /// Gets the Umbraco application url. - /// - /// This is eg "http://www.example.com". + /// public Uri ApplicationUrl { get; private set; } - /// - /// Gets the Umbraco application virtual path. - /// - /// This is either "/" or eg "/virtual". + /// public string ApplicationVirtualPath { get; } = HttpRuntime.AppDomainAppVirtualPath; /// @@ -95,9 +86,7 @@ namespace Umbraco.Core /// public string FinalMigrationState { get; internal set; } - /// - /// Gets the runtime level of execution. - /// + /// public RuntimeLevel Level { get => _level; @@ -137,9 +126,123 @@ namespace Umbraco.Core return _runLevel.WaitHandle.WaitOne(timeout); } - /// - /// Gets the exception that caused the boot to fail. - /// + /// public BootFailedException BootFailedException { get; internal set; } + + /// + /// Determines the runtime level. + /// + public void DetermineRuntimeLevel(IUmbracoDatabaseFactory databaseFactory, ILogger logger) + { + var localVersion = UmbracoVersion.LocalVersion; // the local, files, version + var codeVersion = SemanticVersion; // the executing code version + var connect = false; + + if (localVersion == null) + { + // there is no local version, we are not installed + logger.Debug("No local version, need to install Umbraco."); + Level = RuntimeLevel.Install; + return; + } + + if (localVersion < codeVersion) + { + // there *is* a local version, but it does not match the code version + // need to upgrade + logger.Debug("Local version '{LocalVersion}' < code version '{CodeVersion}', need to upgrade Umbraco.", localVersion, codeVersion); + Level = RuntimeLevel.Upgrade; + } + else if (localVersion > codeVersion) + { + logger.Warn("Local version '{LocalVersion}' > code version '{CodeVersion}', downgrading is not supported.", localVersion, codeVersion); + + // in fact, this is bad enough that we want to throw + throw new BootFailedException($"Local version \"{localVersion}\" > code version \"{codeVersion}\", downgrading is not supported."); + } + else if (databaseFactory.Configured == false) + { + // local version *does* match code version, but the database is not configured + // install (again? this is a weird situation...) + logger.Debug("Database is not configured, need to install Umbraco."); + Level = RuntimeLevel.Install; + return; + } + + // else, keep going, + // anything other than install wants a database - see if we can connect + // (since this is an already existing database, assume localdb is ready) + for (var i = 0; i < 5; i++) + { + connect = databaseFactory.CanConnect; + if (connect) break; + logger.Debug("Could not immediately connect to database, trying again."); + Thread.Sleep(1000); + } + + if (connect == false) + { + // cannot connect to configured database, this is bad, fail + logger.Debug("Could not connect to database."); + + // in fact, this is bad enough that we want to throw + throw new BootFailedException("A connection string is configured but Umbraco could not connect to the database."); + } + + // if we already know we want to upgrade, + // still run EnsureUmbracoUpgradeState to get the states + // (v7 will just get a null state, that's ok) + + // else + // look for a matching migration entry - bypassing services entirely - they are not 'up' yet + // fixme - in a LB scenario, ensure that the DB gets upgraded only once! + bool noUpgrade; + try + { + noUpgrade = EnsureUmbracoUpgradeState(databaseFactory, logger); + } + catch (Exception e) + { + // can connect to the database but cannot check the upgrade state... oops + logger.Warn(e, "Could not check the upgrade state."); + throw new BootFailedException("Could not check the upgrade state.", e); + } + + // if we already know we want to upgrade, exit here + if (Level == RuntimeLevel.Upgrade) + return; + + if (noUpgrade) + { + // the database version matches the code & files version, all clear, can run + Level = RuntimeLevel.Run; + return; + } + + // the db version does not match... but we do have a migration table + // so, at least one valid table, so we quite probably are installed & need to upgrade + + // although the files version matches the code version, the database version does not + // which means the local files have been upgraded but not the database - need to upgrade + logger.Debug("Has not reached the final upgrade step, need to upgrade Umbraco."); + Level = RuntimeLevel.Upgrade; + } + + protected virtual bool EnsureUmbracoUpgradeState(IUmbracoDatabaseFactory databaseFactory, ILogger logger) + { + var upgrader = new UmbracoUpgrader(); + var stateValueKey = upgrader.StateValueKey; + + // no scope, no service - just directly accessing the database + using (var database = databaseFactory.CreateDatabase()) + { + CurrentMigrationState = KeyValueService.GetValue(database, stateValueKey); + FinalMigrationState = upgrader.Plan.FinalState; + } + + logger.Debug("Final upgrade state is {FinalMigrationState}, database contains {DatabaseState}", CurrentMigrationState, FinalMigrationState ?? ""); + + return CurrentMigrationState == FinalMigrationState; + } } } diff --git a/src/Umbraco.Core/Scoping/Scope.cs b/src/Umbraco.Core/Scoping/Scope.cs index adc5482e68..b8d4d7b430 100644 --- a/src/Umbraco.Core/Scoping/Scope.cs +++ b/src/Umbraco.Core/Scoping/Scope.cs @@ -1,6 +1,7 @@ using System; using System.Data; using Umbraco.Core.Cache; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Events; using Umbraco.Core.IO; @@ -491,7 +492,7 @@ namespace Umbraco.Core.Scoping // caching config // true if Umbraco.CoreDebug.LogUncompletedScope appSetting is set to "true" private static bool LogUncompletedScopes => (_logUncompletedScopes - ?? (_logUncompletedScopes = UmbracoConfig.For.CoreDebug().LogUncompletedScopes)).Value; + ?? (_logUncompletedScopes = Current.Configs.CoreDebug().LogUncompletedScopes)).Value; /// public void ReadLock(params int[] lockIds) diff --git a/src/Umbraco.Core/Security/MembershipProviderExtensions.cs b/src/Umbraco.Core/Security/MembershipProviderExtensions.cs index ca01330212..ff595a5d45 100644 --- a/src/Umbraco.Core/Security/MembershipProviderExtensions.cs +++ b/src/Umbraco.Core/Security/MembershipProviderExtensions.cs @@ -4,6 +4,7 @@ using System.Threading; using System.Web; using System.Web.Hosting; using System.Web.Security; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Models; using Umbraco.Core.Services; @@ -87,11 +88,11 @@ namespace Umbraco.Core.Security /// public static MembershipProvider GetUsersMembershipProvider() { - if (Membership.Providers[UmbracoConfig.For.UmbracoSettings().Providers.DefaultBackOfficeUserProvider] == null) + if (Membership.Providers[Current.Configs.Settings().Providers.DefaultBackOfficeUserProvider] == null) { - throw new InvalidOperationException("No membership provider found with name " + UmbracoConfig.For.UmbracoSettings().Providers.DefaultBackOfficeUserProvider); + throw new InvalidOperationException("No membership provider found with name " + Current.Configs.Settings().Providers.DefaultBackOfficeUserProvider); } - return Membership.Providers[UmbracoConfig.For.UmbracoSettings().Providers.DefaultBackOfficeUserProvider]; + return Membership.Providers[Current.Configs.Settings().Providers.DefaultBackOfficeUserProvider]; } /// diff --git a/src/Umbraco.Core/Services/EntityXmlSerializer.cs b/src/Umbraco.Core/Services/EntityXmlSerializer.cs index 5b64584dc6..d938e032a8 100644 --- a/src/Umbraco.Core/Services/EntityXmlSerializer.cs +++ b/src/Umbraco.Core/Services/EntityXmlSerializer.cs @@ -52,7 +52,7 @@ namespace Umbraco.Core.Services xml.Add(new XAttribute("writerName", content.GetWriterProfile(userService)?.Name ?? "??")); xml.Add(new XAttribute("writerID", content.WriterId)); - xml.Add(new XAttribute("template", content.Template?.Id.ToString(CultureInfo.InvariantCulture) ?? "0")); + xml.Add(new XAttribute("template", content.TemplateId?.ToString(CultureInfo.InvariantCulture) ?? "")); xml.Add(new XAttribute("isPublished", content.Published)); diff --git a/src/Umbraco.Core/Services/IRedirectUrlService.cs b/src/Umbraco.Core/Services/IRedirectUrlService.cs index 62e59e910c..3bd9b6f2cf 100644 --- a/src/Umbraco.Core/Services/IRedirectUrlService.cs +++ b/src/Umbraco.Core/Services/IRedirectUrlService.cs @@ -14,8 +14,9 @@ namespace Umbraco.Core.Services /// /// The Umbraco url route. /// The content unique key. + /// The culture. /// Is a proper Umbraco route eg /path/to/foo or 123/path/tofoo. - void Register(string url, Guid contentKey); + void Register(string url, Guid contentKey, string culture = null); /// /// Deletes all redirect urls for a given content. diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index 741a483bbe..bfd6e8d0da 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -27,13 +27,13 @@ namespace Umbraco.Core.Services.Implement private readonly IContentTypeRepository _contentTypeRepository; private readonly IDocumentBlueprintRepository _documentBlueprintRepository; private readonly ILanguageRepository _languageRepository; - private readonly MediaFileSystem _mediaFileSystem; + private readonly IMediaFileSystem _mediaFileSystem; private IQuery _queryNotTrashed; #region Constructors public ContentService(IScopeProvider provider, ILogger logger, - IEventMessagesFactory eventMessagesFactory, MediaFileSystem mediaFileSystem, + IEventMessagesFactory eventMessagesFactory, IMediaFileSystem mediaFileSystem, IDocumentRepository documentRepository, IEntityRepository entityRepository, IAuditRepository auditRepository, IContentTypeRepository contentTypeRepository, IDocumentBlueprintRepository documentBlueprintRepository, ILanguageRepository languageRepository) : base(provider, logger, eventMessagesFactory) @@ -1552,9 +1552,7 @@ namespace Umbraco.Core.Services.Implement var args = new DeleteEventArgs(c, false); // raise event & get flagged files scope.Events.Dispatch(Deleted, this, args, nameof(Deleted)); - // fixme not going to work, do it differently - _mediaFileSystem.DeleteFiles(args.MediaFilesToDelete, // remove flagged files - (file, e) => Logger.Error(e, "An error occurred while deleting file attached to nodes: {File}", file)); + // media files deleted by QueuingEventDispatcher } const int pageSize = 500; @@ -2735,6 +2733,8 @@ namespace Umbraco.Core.Services.Implement } } + private static readonly string[] ArrayOfOneNullString = { null }; + public IContent CreateContentFromBlueprint(IContent blueprint, string name, int userId = 0) { if (blueprint == null) throw new ArgumentNullException(nameof(blueprint)); @@ -2746,8 +2746,23 @@ namespace Umbraco.Core.Services.Implement content.CreatorId = userId; content.WriterId = userId; - foreach (var property in blueprint.Properties) - content.SetValue(property.Alias, property.GetValue()); //fixme doesn't take into account variants + var now = DateTime.Now; + var cultures = blueprint.CultureInfos.Any() ? blueprint.CultureInfos.Select(x=>x.Key) : ArrayOfOneNullString; + foreach (var culture in cultures) + { + foreach (var property in blueprint.Properties) + { + content.SetValue(property.Alias, property.GetValue(culture), culture); + } + + content.Name = blueprint.Name; + if (!string.IsNullOrEmpty(culture)) + { + content.SetCultureInfo(culture, blueprint.GetCultureName(culture), now); + } + } + + return content; } diff --git a/src/Umbraco.Core/Services/Implement/MediaService.cs b/src/Umbraco.Core/Services/Implement/MediaService.cs index 1d04462836..f8c6badb37 100644 --- a/src/Umbraco.Core/Services/Implement/MediaService.cs +++ b/src/Umbraco.Core/Services/Implement/MediaService.cs @@ -26,11 +26,11 @@ namespace Umbraco.Core.Services.Implement private readonly IAuditRepository _auditRepository; private readonly IEntityRepository _entityRepository; - private readonly MediaFileSystem _mediaFileSystem; + private readonly IMediaFileSystem _mediaFileSystem; #region Constructors - public MediaService(IScopeProvider provider, MediaFileSystem mediaFileSystem, ILogger logger, IEventMessagesFactory eventMessagesFactory, + public MediaService(IScopeProvider provider, IMediaFileSystem mediaFileSystem, ILogger logger, IEventMessagesFactory eventMessagesFactory, IMediaRepository mediaRepository, IAuditRepository auditRepository, IMediaTypeRepository mediaTypeRepository, IEntityRepository entityRepository) : base(provider, logger, eventMessagesFactory) @@ -757,8 +757,7 @@ namespace Umbraco.Core.Services.Implement var args = new DeleteEventArgs(c, false); // raise event & get flagged files scope.Events.Dispatch(Deleted, this, args); - _mediaFileSystem.DeleteFiles(args.MediaFilesToDelete, // remove flagged files - (file, e) => Logger.Error(e, "An error occurred while deleting file attached to nodes: {File}", file)); + // media files deleted by QueuingEventDispatcher } const int pageSize = 500; diff --git a/src/Umbraco.Core/Services/Implement/MemberService.cs b/src/Umbraco.Core/Services/Implement/MemberService.cs index e191493736..2f8c2f9a79 100644 --- a/src/Umbraco.Core/Services/Implement/MemberService.cs +++ b/src/Umbraco.Core/Services/Implement/MemberService.cs @@ -28,14 +28,14 @@ namespace Umbraco.Core.Services.Implement private readonly IAuditRepository _auditRepository; private readonly IMemberGroupService _memberGroupService; - private readonly MediaFileSystem _mediaFileSystem; + private readonly IMediaFileSystem _mediaFileSystem; //only for unit tests! internal MembershipProviderBase MembershipProvider { get; set; } #region Constructor - public MemberService(IScopeProvider provider, ILogger logger, IEventMessagesFactory eventMessagesFactory, IMemberGroupService memberGroupService, MediaFileSystem mediaFileSystem, + public MemberService(IScopeProvider provider, ILogger logger, IEventMessagesFactory eventMessagesFactory, IMemberGroupService memberGroupService, IMediaFileSystem mediaFileSystem, IMemberRepository memberRepository, IMemberTypeRepository memberTypeRepository, IMemberGroupRepository memberGroupRepository, IAuditRepository auditRepository) : base(provider, logger, eventMessagesFactory) { @@ -929,10 +929,7 @@ namespace Umbraco.Core.Services.Implement args.CanCancel = false; scope.Events.Dispatch(Deleted, this, args); - // fixme - this is MOOT because the event will not trigger immediately - // it's been refactored already (think it's the dispatcher that deals with it?) - _mediaFileSystem.DeleteFiles(args.MediaFilesToDelete, // remove flagged files - (file, e) => Logger.Error(e, "An error occurred while deleting file attached to nodes: {File}", file)); + // media files deleted by QueuingEventDispatcher } #endregion diff --git a/src/Umbraco.Core/Services/Implement/PackagingService.cs b/src/Umbraco.Core/Services/Implement/PackagingService.cs index fff865e097..106d2b9f12 100644 --- a/src/Umbraco.Core/Services/Implement/PackagingService.cs +++ b/src/Umbraco.Core/Services/Implement/PackagingService.cs @@ -66,7 +66,7 @@ namespace Umbraco.Core.Services.Implement IEntityService entityService, IUserService userService, IScopeProvider scopeProvider, - IEnumerable urlSegmentProviders, + UrlSegmentProviderCollection urlSegmentProviders, IAuditRepository auditRepository, IContentTypeRepository contentTypeRepository, PropertyEditorCollection propertyEditors) { diff --git a/src/Umbraco.Core/Services/Implement/RedirectUrlService.cs b/src/Umbraco.Core/Services/Implement/RedirectUrlService.cs index e9703bd85c..80816961fc 100644 --- a/src/Umbraco.Core/Services/Implement/RedirectUrlService.cs +++ b/src/Umbraco.Core/Services/Implement/RedirectUrlService.cs @@ -19,15 +19,15 @@ namespace Umbraco.Core.Services.Implement _redirectUrlRepository = redirectUrlRepository; } - public void Register(string url, Guid contentKey) + public void Register(string url, Guid contentKey, string culture = null) { using (var scope = ScopeProvider.CreateScope()) { - var redir = _redirectUrlRepository.Get(url, contentKey); + var redir = _redirectUrlRepository.Get(url, contentKey, culture); if (redir != null) redir.CreateDateUtc = DateTime.UtcNow; else - redir = new RedirectUrl { Key = Guid.NewGuid(), Url = url, ContentKey = contentKey }; + redir = new RedirectUrl { Key = Guid.NewGuid(), Url = url, ContentKey = contentKey, Culture = culture}; _redirectUrlRepository.Save(redir); scope.Complete(); } diff --git a/src/Umbraco.Core/Services/ServiceContext.cs b/src/Umbraco.Core/Services/ServiceContext.cs index f90b1d8d64..731d3a58c6 100644 --- a/src/Umbraco.Core/Services/ServiceContext.cs +++ b/src/Umbraco.Core/Services/ServiceContext.cs @@ -38,7 +38,6 @@ namespace Umbraco.Core.Services /// /// Initializes a new instance of the class with lazy services. /// - /// Used by IoC. Note that LightInject will favor lazy args when picking a constructor. public ServiceContext(Lazy publicAccessService, Lazy domainService, Lazy auditService, Lazy localizedTextService, Lazy tagService, Lazy contentService, Lazy userService, Lazy memberService, Lazy mediaService, Lazy contentTypeService, Lazy mediaTypeService, Lazy dataTypeService, Lazy fileService, Lazy localizationService, Lazy packagingService, Lazy serverRegistrationService, Lazy entityService, Lazy relationService, Lazy treeService, Lazy sectionService, Lazy macroService, Lazy memberTypeService, Lazy memberGroupService, Lazy notificationService, Lazy externalLoginService, Lazy redirectUrlService, Lazy consentService) { _publicAccessService = publicAccessService; @@ -71,10 +70,13 @@ namespace Umbraco.Core.Services } /// - /// Initializes a new instance of the class with services. + /// Creates a partial service context with only some services (for tests). /// - /// Used in tests. All items are optional and remain null if not specified. - public ServiceContext(IContentService contentService = null, + /// + /// Using a true constructor for this confuses DI containers. + /// + public static ServiceContext CreatePartial( + IContentService contentService = null, IMediaService mediaService = null, IContentTypeService contentTypeService = null, IMediaTypeService mediaTypeService = null, @@ -102,40 +104,43 @@ namespace Umbraco.Core.Services IRedirectUrlService redirectUrlService = null, IConsentService consentService = null) { - if (serverRegistrationService != null) _serverRegistrationService = new Lazy(() => serverRegistrationService); - if (externalLoginService != null) _externalLoginService = new Lazy(() => externalLoginService); - if (auditService != null) _auditService = new Lazy(() => auditService); - if (localizedTextService != null) _localizedTextService = new Lazy(() => localizedTextService); - if (tagService != null) _tagService = new Lazy(() => tagService); - if (contentService != null) _contentService = new Lazy(() => contentService); - if (mediaService != null) _mediaService = new Lazy(() => mediaService); - if (contentTypeService != null) _contentTypeService = new Lazy(() => contentTypeService); - if (mediaTypeService != null) _mediaTypeService = new Lazy(() => mediaTypeService); - if (dataTypeService != null) _dataTypeService = new Lazy(() => dataTypeService); - if (fileService != null) _fileService = new Lazy(() => fileService); - if (localizationService != null) _localizationService = new Lazy(() => localizationService); - if (packagingService != null) _packagingService = new Lazy(() => packagingService); - if (entityService != null) _entityService = new Lazy(() => entityService); - if (relationService != null) _relationService = new Lazy(() => relationService); - if (sectionService != null) _sectionService = new Lazy(() => sectionService); - if (memberGroupService != null) _memberGroupService = new Lazy(() => memberGroupService); - if (memberTypeService != null) _memberTypeService = new Lazy(() => memberTypeService); - if (treeService != null) _treeService = new Lazy(() => treeService); - if (memberService != null) _memberService = new Lazy(() => memberService); - if (userService != null) _userService = new Lazy(() => userService); - if (notificationService != null) _notificationService = new Lazy(() => notificationService); - if (domainService != null) _domainService = new Lazy(() => domainService); - if (macroService != null) _macroService = new Lazy(() => macroService); - if (publicAccessService != null) _publicAccessService = new Lazy(() => publicAccessService); - if (redirectUrlService != null) _redirectUrlService = new Lazy(() => redirectUrlService); - if (consentService != null) _consentService = new Lazy(() => consentService); + Lazy Lazy(T service) => service == null ? null : new Lazy(() => service); + + return new ServiceContext( + Lazy(publicAccessService), + Lazy(domainService), + Lazy(auditService), + Lazy(localizedTextService), + Lazy(tagService), + Lazy(contentService), + Lazy(userService), + Lazy(memberService), + Lazy(mediaService), + Lazy(contentTypeService), + Lazy(mediaTypeService), + Lazy(dataTypeService), + Lazy(fileService), + Lazy(localizationService), + Lazy(packagingService), + Lazy(serverRegistrationService), + Lazy(entityService), + Lazy(relationService), + Lazy(treeService), + Lazy(sectionService), + Lazy(macroService), + Lazy(memberTypeService), + Lazy(memberGroupService), + Lazy(notificationService), + Lazy(externalLoginService), + Lazy(redirectUrlService), + Lazy(consentService)); } - + /// /// Gets the /// public IPublicAccessService PublicAccessService => _publicAccessService.Value; - + /// /// Gets the /// diff --git a/src/Umbraco.Core/SimpleMainDom.cs b/src/Umbraco.Core/SimpleMainDom.cs new file mode 100644 index 0000000000..87cc7bcff1 --- /dev/null +++ b/src/Umbraco.Core/SimpleMainDom.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Umbraco.Core +{ + /// + /// Provides a simple implementation of . + /// + public class SimpleMainDom : IMainDom + { + private readonly object _locko = new object(); + private readonly List> _callbacks = new List>(); + private bool _isStopping; + + /// + public bool IsMainDom { get; private set; } = true; + + /// + public bool Register(Action release, int weight = 100) + => Register(null, release, weight); + + /// + public bool Register(Action install, Action release, int weight = 100) + { + lock (_locko) + { + if (_isStopping) return false; + install?.Invoke(); + if (release != null) + _callbacks.Add(new KeyValuePair(weight, release)); + return true; + } + } + + public void Stop() + { + lock (_locko) + { + if (_isStopping) return; + if (IsMainDom == false) return; // probably not needed + _isStopping = true; + } + + try + { + foreach (var callback in _callbacks.OrderBy(x => x.Key).Select(x => x.Value)) + { + callback(); // no timeout on callbacks + } + } + finally + { + // in any case... + IsMainDom = false; + } + } + } +} diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs index 03a371204c..90fc472e30 100644 --- a/src/Umbraco.Core/StringExtensions.cs +++ b/src/Umbraco.Core/StringExtensions.cs @@ -187,7 +187,6 @@ namespace Umbraco.Core outputArray[i] = char.IsLetterOrDigit(inputArray[i]) ? inputArray[i] : replacement; return new string(outputArray); } - private static readonly char[] CleanForXssChars = "*?(){}[];:%<>/\\|&'\"".ToCharArray(); /// @@ -1115,7 +1114,7 @@ namespace Umbraco.Core /// Checks UmbracoSettings.ForceSafeAliases to determine whether it should filter the text. public static string ToSafeAliasWithForcingCheck(this string alias) { - return UmbracoConfig.For.UmbracoSettings().Content.ForceSafeAliases ? alias.ToSafeAlias() : alias; + return Current.Configs.Settings().Content.ForceSafeAliases ? alias.ToSafeAlias() : alias; } /// @@ -1127,7 +1126,7 @@ namespace Umbraco.Core /// Checks UmbracoSettings.ForceSafeAliases to determine whether it should filter the text. public static string ToSafeAliasWithForcingCheck(this string alias, string culture) { - return UmbracoConfig.For.UmbracoSettings().Content.ForceSafeAliases ? alias.ToSafeAlias(culture) : alias; + return Current.Configs.Settings().Content.ForceSafeAliases ? alias.ToSafeAlias(culture) : alias; } // the new methods to get a url segment diff --git a/src/Umbraco.Core/Strings/UrlSegmentProviderCollectionBuilder.cs b/src/Umbraco.Core/Strings/UrlSegmentProviderCollectionBuilder.cs index a9b8234e14..5183c28e15 100644 --- a/src/Umbraco.Core/Strings/UrlSegmentProviderCollectionBuilder.cs +++ b/src/Umbraco.Core/Strings/UrlSegmentProviderCollectionBuilder.cs @@ -1,14 +1,9 @@ -using LightInject; -using Umbraco.Core.Composing; +using Umbraco.Core.Composing; namespace Umbraco.Core.Strings { public class UrlSegmentProviderCollectionBuilder : OrderedCollectionBuilderBase { - public UrlSegmentProviderCollectionBuilder(IServiceContainer container) - : base(container) - { } - protected override UrlSegmentProviderCollectionBuilder This => this; } } diff --git a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs index 20f9276ba1..5cfcb501e5 100644 --- a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs +++ b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs @@ -33,7 +33,7 @@ namespace Umbraco.Core.Sync private readonly IRuntimeState _runtime; private readonly ManualResetEvent _syncIdle; private readonly object _locko = new object(); - private readonly ProfilingLogger _profilingLogger; + private readonly IProfilingLogger _profilingLogger; private readonly ISqlContext _sqlContext; private readonly Lazy _distCacheFilePath; private int _lastId = -1; @@ -46,7 +46,7 @@ namespace Umbraco.Core.Sync public DatabaseServerMessengerOptions Options { get; } public DatabaseServerMessenger( - IRuntimeState runtime, IScopeProvider scopeProvider, ISqlContext sqlContext, ProfilingLogger proflog, IGlobalSettings globalSettings, + IRuntimeState runtime, IScopeProvider scopeProvider, ISqlContext sqlContext, IProfilingLogger proflog, IGlobalSettings globalSettings, bool distributedEnabled, DatabaseServerMessengerOptions options) : base(distributedEnabled) { @@ -54,7 +54,7 @@ namespace Umbraco.Core.Sync _sqlContext = sqlContext; _runtime = runtime; _profilingLogger = proflog ?? throw new ArgumentNullException(nameof(proflog)); - Logger = proflog.Logger; + Logger = proflog; Options = options ?? throw new ArgumentNullException(nameof(options)); _lastPruned = _lastSync = DateTime.UtcNow; _syncIdle = new ManualResetEvent(true); diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index d83a08483a..9df90efcf0 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -154,34 +154,47 @@ - + + + - - - - - - - - + + + + + + + + + + + + - - + + - - - - + + + + + + + + + + - - + + + @@ -191,6 +204,8 @@ + + @@ -244,7 +259,6 @@ - @@ -314,11 +328,15 @@ + + + + @@ -326,6 +344,7 @@ + @@ -367,6 +386,7 @@ + @@ -569,7 +589,7 @@ - + @@ -586,7 +606,6 @@ - @@ -1221,7 +1240,6 @@ - @@ -1280,6 +1298,7 @@ + @@ -1424,6 +1443,7 @@ + diff --git a/src/Umbraco.Core/_Legacy/PackageActions/PackageActionCollectionBuilder.cs b/src/Umbraco.Core/_Legacy/PackageActions/PackageActionCollectionBuilder.cs index 42ab3ec7c2..2f73a2b489 100644 --- a/src/Umbraco.Core/_Legacy/PackageActions/PackageActionCollectionBuilder.cs +++ b/src/Umbraco.Core/_Legacy/PackageActions/PackageActionCollectionBuilder.cs @@ -1,14 +1,9 @@ -using LightInject; -using Umbraco.Core.Composing; +using Umbraco.Core.Composing; namespace Umbraco.Core._Legacy.PackageActions { internal class PackageActionCollectionBuilder : LazyCollectionBuilderBase { - public PackageActionCollectionBuilder(IServiceContainer container) - : base(container) - { } - protected override PackageActionCollectionBuilder This => this; } } diff --git a/src/Umbraco.Examine/Config/ConfigIndexCriteria.cs b/src/Umbraco.Examine/Config/ConfigIndexCriteria.cs deleted file mode 100644 index de2a5ced36..0000000000 --- a/src/Umbraco.Examine/Config/ConfigIndexCriteria.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Examine; - -namespace Umbraco.Examine.Config -{ - /// - /// a data structure for storing indexing/searching instructions based on config based indexers - /// - public class ConfigIndexCriteria - { - /// - /// Constructor - /// - /// - /// - /// - /// - /// - public ConfigIndexCriteria(IEnumerable standardFields, IEnumerable userFields, IEnumerable includeNodeTypes, IEnumerable excludeNodeTypes, int? parentNodeId) - { - UserFields = userFields.ToList(); - StandardFields = standardFields.ToList(); - IncludeItemTypes = includeNodeTypes; - ExcludeItemTypes = excludeNodeTypes; - ParentNodeId = parentNodeId; - } - - public IEnumerable StandardFields { get; internal set; } - public IEnumerable UserFields { get; internal set; } - - public IEnumerable IncludeItemTypes { get; internal set; } - public IEnumerable ExcludeItemTypes { get; internal set; } - public int? ParentNodeId { get; internal set; } - } -} \ No newline at end of file diff --git a/src/Umbraco.Examine/Config/ConfigIndexField.cs b/src/Umbraco.Examine/Config/ConfigIndexField.cs deleted file mode 100644 index ec9cbf797e..0000000000 --- a/src/Umbraco.Examine/Config/ConfigIndexField.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System.Configuration; - -namespace Umbraco.Examine.Config -{ - /// - /// A configuration item representing a field to index - /// - public sealed class ConfigIndexField : ConfigurationElement - { - [ConfigurationProperty("Name", IsRequired = true)] - public string Name - { - get => (string)this["Name"]; - set => this["Name"] = value; - } - - [ConfigurationProperty("EnableSorting", IsRequired = false)] - public bool EnableSorting - { - get => (bool)this["EnableSorting"]; - set => this["EnableSorting"] = value; - } - - [ConfigurationProperty("Type", IsRequired = false, DefaultValue = "String")] - public string Type - { - get => (string)this["Type"]; - set => this["Type"] = value; - } - - public bool Equals(ConfigIndexField other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return string.Equals(Name, other.Name); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((ConfigIndexField)obj); - } - - public override int GetHashCode() - { - return Name.GetHashCode(); - } - - public static bool operator ==(ConfigIndexField left, ConfigIndexField right) - { - return Equals(left, right); - } - - public static bool operator !=(ConfigIndexField left, ConfigIndexField right) - { - return !Equals(left, right); - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Examine/Config/IndexFieldCollection.cs b/src/Umbraco.Examine/Config/IndexFieldCollection.cs deleted file mode 100644 index 063c157dbe..0000000000 --- a/src/Umbraco.Examine/Config/IndexFieldCollection.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Configuration; - -namespace Umbraco.Examine.Config -{ - public sealed class IndexFieldCollection : ConfigurationElementCollection - { - #region Overridden methods to define collection - protected override ConfigurationElement CreateNewElement() - { - return new ConfigIndexField(); - } - protected override object GetElementKey(ConfigurationElement element) - { - ConfigIndexField field = (ConfigIndexField)element; - return field.Name; - } - - public override bool IsReadOnly() - { - return false; - } - #endregion - - /// - /// Adds an index field to the collection - /// - /// - public void Add(ConfigIndexField field) - { - BaseAdd(field, true); - } - - /// - /// Default property for accessing an IndexField definition - /// - /// Field Name - /// - public new ConfigIndexField this[string name] - { - get - { - return (ConfigIndexField)this.BaseGet(name); - } - } - - } -} \ No newline at end of file diff --git a/src/Umbraco.Examine/Config/IndexFieldCollectionExtensions.cs b/src/Umbraco.Examine/Config/IndexFieldCollectionExtensions.cs deleted file mode 100644 index eea117ce05..0000000000 --- a/src/Umbraco.Examine/Config/IndexFieldCollectionExtensions.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Collections.Generic; - -namespace Umbraco.Examine.Config -{ - public static class IndexFieldCollectionExtensions - { - public static List ToList(this IndexFieldCollection indexes) - { - List fields = new List(); - foreach (ConfigIndexField field in indexes) - fields.Add(field); - return fields; - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Examine/Config/IndexSet.cs b/src/Umbraco.Examine/Config/IndexSet.cs deleted file mode 100644 index 65b1cd5aec..0000000000 --- a/src/Umbraco.Examine/Config/IndexSet.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System.Configuration; -using System.IO; -using System.Web; -using System.Web.Hosting; - -namespace Umbraco.Examine.Config -{ - public sealed class IndexSet : ConfigurationElement - { - - [ConfigurationProperty("SetName", IsRequired = true, IsKey = true)] - public string SetName => (string)this["SetName"]; - - /// - /// When this property is set, the indexing will only index documents that are descendants of this node. - /// - [ConfigurationProperty("IndexParentId", IsRequired = false, IsKey = false)] - public int? IndexParentId - { - get - { - if (this["IndexParentId"] == null) - return null; - - return (int)this["IndexParentId"]; - } - } - - /// - /// The collection of node types to index, if not specified, all node types will be indexed (apart from the ones specified in the ExcludeNodeTypes collection). - /// - [ConfigurationCollection(typeof(IndexFieldCollection))] - [ConfigurationProperty("IncludeNodeTypes", IsDefaultCollection = false, IsRequired = false)] - public IndexFieldCollection IncludeNodeTypes => (IndexFieldCollection)base["IncludeNodeTypes"]; - - /// - /// The collection of node types to not index. If specified, these node types will not be indexed. - /// - [ConfigurationCollection(typeof(IndexFieldCollection))] - [ConfigurationProperty("ExcludeNodeTypes", IsDefaultCollection = false, IsRequired = false)] - public IndexFieldCollection ExcludeNodeTypes => (IndexFieldCollection)base["ExcludeNodeTypes"]; - - /// - /// A collection of user defined umbraco fields to index - /// - /// - /// If this property is not specified, or if it's an empty collection, the default user fields will be all user fields defined in Umbraco - /// - [ConfigurationCollection(typeof(IndexFieldCollection))] - [ConfigurationProperty("IndexUserFields", IsDefaultCollection = false, IsRequired = false)] - public IndexFieldCollection IndexUserFields => (IndexFieldCollection)base["IndexUserFields"]; - - /// - /// The fields umbraco values that will be indexed. i.e. id, nodeTypeAlias, writer, etc... - /// - /// - /// If this is not specified, or if it's an empty collection, the default optins will be specified: - /// - id - /// - version - /// - parentID - /// - level - /// - writerID - /// - creatorID - /// - nodeType - /// - template - /// - sortOrder - /// - createDate - /// - updateDate - /// - nodeName - /// - urlName - /// - writerName - /// - creatorName - /// - nodeTypeAlias - /// - path - /// - [ConfigurationCollection(typeof(IndexFieldCollection))] - [ConfigurationProperty("IndexAttributeFields", IsDefaultCollection = false, IsRequired = false)] - public IndexFieldCollection IndexAttributeFields => (IndexFieldCollection)base["IndexAttributeFields"]; - } -} diff --git a/src/Umbraco.Examine/Config/IndexSetCollection.cs b/src/Umbraco.Examine/Config/IndexSetCollection.cs deleted file mode 100644 index bfce8e4cce..0000000000 --- a/src/Umbraco.Examine/Config/IndexSetCollection.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Configuration; - - -namespace Umbraco.Examine.Config -{ - public sealed class IndexSetCollection : ConfigurationElementCollection - { - #region Overridden methods to define collection - protected override ConfigurationElement CreateNewElement() - { - return new IndexSet(); - } - protected override object GetElementKey(ConfigurationElement element) - { - return ((IndexSet)element).SetName; - } - public override ConfigurationElementCollectionType CollectionType => ConfigurationElementCollectionType.BasicMap; - protected override string ElementName => "IndexSet"; - - #endregion - - /// - /// Default property for accessing Image Sets - /// - /// - /// - public new IndexSet this[string setName] => (IndexSet)this.BaseGet(setName); - } -} \ No newline at end of file diff --git a/src/Umbraco.Examine/Config/IndexSets.cs b/src/Umbraco.Examine/Config/IndexSets.cs deleted file mode 100644 index c6ad1476c3..0000000000 --- a/src/Umbraco.Examine/Config/IndexSets.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Configuration; - -namespace Umbraco.Examine.Config -{ - public sealed class IndexSets : ConfigurationSection - { - #region Singleton definition - - private IndexSets() { } - - public static IndexSets Instance { get; } = ConfigurationManager.GetSection(SectionName) as IndexSets; - - #endregion - - private const string SectionName = "ExamineLuceneIndexSets"; - - [ConfigurationCollection(typeof(IndexSetCollection))] - [ConfigurationProperty("", IsDefaultCollection = true, IsRequired = true)] - public IndexSetCollection Sets => (IndexSetCollection)base[""]; - } -} diff --git a/src/Umbraco.Examine/ContentIndexPopulator.cs b/src/Umbraco.Examine/ContentIndexPopulator.cs index 72615b4b26..51b9de4a0b 100644 --- a/src/Umbraco.Examine/ContentIndexPopulator.cs +++ b/src/Umbraco.Examine/ContentIndexPopulator.cs @@ -58,8 +58,10 @@ namespace Umbraco.Examine _parentId = parentId; } - protected override void PopulateIndexes(IEnumerable indexes) + protected override void PopulateIndexes(IReadOnlyList indexes) { + if (indexes.Count == 0) return; + const int pageSize = 10000; var pageIndex = 0; diff --git a/src/Umbraco.Examine/ContentValueSetBuilder.cs b/src/Umbraco.Examine/ContentValueSetBuilder.cs index 07e5e4565b..5674698b20 100644 --- a/src/Umbraco.Examine/ContentValueSetBuilder.cs +++ b/src/Umbraco.Examine/ContentValueSetBuilder.cs @@ -53,14 +53,14 @@ namespace Umbraco.Examine {"updateDate", new object[] {c.UpdateDate}}, //Always add invariant updateDate {"nodeName", PublishedValuesOnly //Always add invariant nodeName ? c.PublishName.Yield() - : c.Name.Yield()}, + : c.Name.Yield()}, {"urlName", urlValue.Yield()}, //Always add invariant urlName {"path", c.Path.Yield()}, {"nodeType", new object[] {c.ContentType.Id}}, {"creatorName", (c.GetCreatorProfile(_userService)?.Name ?? "??").Yield() }, {"writerName",(c.GetWriterProfile(_userService)?.Name ?? "??").Yield() }, {"writerID", new object[] {c.WriterId}}, - {"template", new object[] {c.Template?.Id ?? 0}}, + {"templateID", new object[] {c.TemplateId ?? 0}}, {UmbracoContentIndex.VariesByCultureFieldName, new object[] {0}}, }; diff --git a/src/Umbraco.Examine/IIndexPopulator.cs b/src/Umbraco.Examine/IIndexPopulator.cs index 153e88d46b..97a1216fae 100644 --- a/src/Umbraco.Examine/IIndexPopulator.cs +++ b/src/Umbraco.Examine/IIndexPopulator.cs @@ -1,22 +1,20 @@ -using System.Collections.Generic; -using Examine; +using Examine; namespace Umbraco.Examine { public interface IIndexPopulator { /// - /// If this index is registered with this populatr + /// If this index is registered with this populator /// /// /// bool IsRegistered(IIndex index); /// - /// Populate indexers + /// Populate indexers /// /// void Populate(params IIndex[] indexes); } - } diff --git a/src/Umbraco.Examine/IndexPopulator.cs b/src/Umbraco.Examine/IndexPopulator.cs index 9cd985df16..f9d4d85dc8 100644 --- a/src/Umbraco.Examine/IndexPopulator.cs +++ b/src/Umbraco.Examine/IndexPopulator.cs @@ -38,9 +38,9 @@ namespace Umbraco.Examine public void Populate(params IIndex[] indexes) { - PopulateIndexes(indexes.Where(IsRegistered)); + PopulateIndexes(indexes.Where(IsRegistered).ToList()); } - protected abstract void PopulateIndexes(IEnumerable indexes); + protected abstract void PopulateIndexes(IReadOnlyList indexes); } } diff --git a/src/Umbraco.Examine/IndexRebuilder.cs b/src/Umbraco.Examine/IndexRebuilder.cs index d946a8f783..43c309b9c5 100644 --- a/src/Umbraco.Examine/IndexRebuilder.cs +++ b/src/Umbraco.Examine/IndexRebuilder.cs @@ -42,6 +42,8 @@ namespace Umbraco.Examine ? ExamineManager.Indexes.Where(x => !x.IndexExists()) : ExamineManager.Indexes).ToArray(); + if (indexes.Length == 0) return; + foreach (var index in indexes) { index.CreateIndex(); // clear the index diff --git a/src/Umbraco.Examine/MediaIndexPopulator.cs b/src/Umbraco.Examine/MediaIndexPopulator.cs index 2232d359e7..6dadcbe4b3 100644 --- a/src/Umbraco.Examine/MediaIndexPopulator.cs +++ b/src/Umbraco.Examine/MediaIndexPopulator.cs @@ -39,8 +39,10 @@ namespace Umbraco.Examine _mediaValueSetBuilder = mediaValueSetBuilder; } - protected override void PopulateIndexes(IEnumerable indexes) + protected override void PopulateIndexes(IReadOnlyList indexes) { + if (indexes.Count == 0) return; + const int pageSize = 10000; var pageIndex = 0; diff --git a/src/Umbraco.Examine/MediaValueSetBuilder.cs b/src/Umbraco.Examine/MediaValueSetBuilder.cs index 8df51570c1..f0e5e895e6 100644 --- a/src/Umbraco.Examine/MediaValueSetBuilder.cs +++ b/src/Umbraco.Examine/MediaValueSetBuilder.cs @@ -14,7 +14,7 @@ namespace Umbraco.Examine private readonly IUserService _userService; public MediaValueSetBuilder(PropertyEditorCollection propertyEditors, - IEnumerable urlSegmentProviders, + UrlSegmentProviderCollection urlSegmentProviders, IUserService userService) : base(propertyEditors, false) { diff --git a/src/Umbraco.Examine/MemberIndexPopulator.cs b/src/Umbraco.Examine/MemberIndexPopulator.cs index 6e97ee9195..e20dab91ca 100644 --- a/src/Umbraco.Examine/MemberIndexPopulator.cs +++ b/src/Umbraco.Examine/MemberIndexPopulator.cs @@ -17,8 +17,10 @@ namespace Umbraco.Examine _memberService = memberService; _valueSetBuilder = valueSetBuilder; } - protected override void PopulateIndexes(IEnumerable indexes) + protected override void PopulateIndexes(IReadOnlyList indexes) { + if (indexes.Count == 0) return; + const int pageSize = 1000; var pageIndex = 0; diff --git a/src/Umbraco.Examine/Umbraco.Examine.csproj b/src/Umbraco.Examine/Umbraco.Examine.csproj index 2bdf6f833d..a68131da0d 100644 --- a/src/Umbraco.Examine/Umbraco.Examine.csproj +++ b/src/Umbraco.Examine/Umbraco.Examine.csproj @@ -48,19 +48,12 @@ - + - - - - - - - diff --git a/src/Umbraco.Examine/UmbracoContentIndex.cs b/src/Umbraco.Examine/UmbracoContentIndex.cs index c584f5bf51..a9e2c72cb6 100644 --- a/src/Umbraco.Examine/UmbracoContentIndex.cs +++ b/src/Umbraco.Examine/UmbracoContentIndex.cs @@ -6,14 +6,10 @@ using System.Linq; using Examine; using Umbraco.Core; using Umbraco.Core.Services; -using Umbraco.Core.Strings; -using Examine.LuceneEngine.Indexing; -using Examine.LuceneEngine.Providers; using Lucene.Net.Analysis; using Lucene.Net.Store; using Umbraco.Core.Composing; using Umbraco.Core.Logging; -using Umbraco.Examine.Config; using Examine.LuceneEngine; namespace Umbraco.Examine @@ -27,18 +23,7 @@ namespace Umbraco.Examine protected ILocalizationService LanguageService { get; } #region Constructors - - /// - /// Constructor for configuration providers - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public UmbracoContentIndex() - { - LanguageService = Current.Services.LocalizationService; - - //note: The validator for this config based indexer is set in the Initialize method - } - + /// /// Create an index at runtime /// @@ -52,14 +37,14 @@ namespace Umbraco.Examine /// public UmbracoContentIndex( string name, - FieldDefinitionCollection fieldDefinitions, Directory luceneDirectory, + FieldDefinitionCollection fieldDefinitions, Analyzer defaultAnalyzer, - ProfilingLogger profilingLogger, + IProfilingLogger profilingLogger, ILocalizationService languageService, IContentValueSetValidator validator, IReadOnlyDictionary indexValueTypes = null) - : base(name, fieldDefinitions, luceneDirectory, defaultAnalyzer, profilingLogger, validator, indexValueTypes) + : base(name, luceneDirectory, fieldDefinitions, defaultAnalyzer, profilingLogger, validator, indexValueTypes) { if (validator == null) throw new ArgumentNullException(nameof(validator)); LanguageService = languageService ?? throw new ArgumentNullException(nameof(languageService)); @@ -70,65 +55,6 @@ namespace Umbraco.Examine #endregion - #region Initialize - - /// - /// Set up all properties for the indexer based on configuration information specified. This will ensure that - /// all of the folders required by the indexer are created and exist. This will also create an instruction - /// file declaring the computer name that is part taking in the indexing. This file will then be used to - /// determine the master indexer machine in a load balanced environment (if one exists). - /// - /// The friendly name of the provider. - /// A collection of the name/value pairs representing the provider-specific attributes specified in the configuration for this provider. - /// - /// The name of the provider is null. - /// - /// - /// The name of the provider has a length of zero. - /// - /// - /// An attempt is made to call on a provider after the provider has already been initialized. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public override void Initialize(string name, NameValueCollection config) - { - base.Initialize(name, config); - - var supportUnpublished = false; - var supportProtected = false; - - //check if there's a flag specifying to support unpublished content, - //if not, set to false; - if (config["supportUnpublished"] != null) - bool.TryParse(config["supportUnpublished"], out supportUnpublished); - - //check if there's a flag specifying to support protected content, - //if not, set to false; - if (config["supportProtected"] != null) - bool.TryParse(config["supportProtected"], out supportProtected); - - - //now we need to build up the indexer options so we can create our validator - int? parentId = null; - if (IndexSetName.IsNullOrWhiteSpace() == false) - { - var indexSet = IndexSets.Instance.Sets[IndexSetName]; - parentId = indexSet.IndexParentId; - } - - ValueSetValidator = new ContentValueSetValidator( - supportUnpublished, supportProtected, - //Using a singleton here, we can't inject this when using config based providers and we don't use this - //anywhere else in this class - Current.Services.PublicAccessService, - parentId, - ConfigIndexCriteria?.IncludeItemTypes, ConfigIndexCriteria?.ExcludeItemTypes); - - PublishedValuesOnly = supportUnpublished; - } - - #endregion - /// /// Special check for invalid paths /// @@ -166,8 +92,8 @@ namespace Umbraco.Examine //these are the invalid items so we'll delete them //since the path is not valid we need to delete this item in case it exists in the index already and has now //been moved to an invalid parent. - foreach (var i in group) - base.PerformDeleteFromIndex(i.Id, args => { /*noop*/ }); + + base.PerformDeleteFromIndex(group.Select(x => x.Id), args => { /*noop*/ }); } else { @@ -192,24 +118,28 @@ namespace Umbraco.Examine /// When a content node is deleted, we also need to delete it's children from the index so we need to perform a /// custom Lucene search to find all decendents and create Delete item queues for them too. /// - /// ID of the node to delete + /// ID of the node to delete /// - protected override void PerformDeleteFromIndex(string nodeId, Action onComplete) + protected override void PerformDeleteFromIndex(IEnumerable itemIds, Action onComplete) { - //find all descendants based on path - var descendantPath = $@"\-1\,*{nodeId}\,*"; - var rawQuery = $"{IndexPathFieldName}:{descendantPath}"; - var searcher = GetSearcher(); - var c = searcher.CreateQuery(); - var filtered = c.NativeQuery(rawQuery); - var results = filtered.Execute(); + var idsAsList = itemIds.ToList(); + foreach (var nodeId in idsAsList) + { + //find all descendants based on path + var descendantPath = $@"\-1\,*{nodeId}\,*"; + var rawQuery = $"{IndexPathFieldName}:{descendantPath}"; + var searcher = GetSearcher(); + var c = searcher.CreateQuery(); + var filtered = c.NativeQuery(rawQuery); + var results = filtered.Execute(); - ProfilingLogger.Logger.Debug(GetType(), "DeleteFromIndex with query: {Query} (found {TotalItems} results)", rawQuery, results.TotalItemCount); + ProfilingLogger.Debug(GetType(), "DeleteFromIndex with query: {Query} (found {TotalItems} results)", rawQuery, results.TotalItemCount); - //need to queue a delete item for each one found - QueueIndexOperation(results.Select(r => new IndexOperation(new ValueSet(r.Id), IndexOperationType.Delete))); + //need to queue a delete item for each one found + QueueIndexOperation(results.Select(r => new IndexOperation(new ValueSet(r.Id), IndexOperationType.Delete))); + } - base.PerformDeleteFromIndex(nodeId, onComplete); + base.PerformDeleteFromIndex(idsAsList, onComplete); } } diff --git a/src/Umbraco.Examine/UmbracoExamineIndex.cs b/src/Umbraco.Examine/UmbracoExamineIndex.cs index fc7f834a29..24952050da 100644 --- a/src/Umbraco.Examine/UmbracoExamineIndex.cs +++ b/src/Umbraco.Examine/UmbracoExamineIndex.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Linq; using Examine.LuceneEngine.Providers; using Lucene.Net.Analysis; @@ -9,11 +8,9 @@ using Lucene.Net.Index; using Umbraco.Core; using Examine; using Examine.LuceneEngine; -using Examine.LuceneEngine.Indexing; using Lucene.Net.Store; using Umbraco.Core.Composing; using Umbraco.Core.Logging; -using Umbraco.Examine.Config; using Directory = Lucene.Net.Store.Directory; namespace Umbraco.Examine @@ -43,18 +40,6 @@ namespace Umbraco.Examine /// public const string RawFieldPrefix = SpecialFieldPrefix + "Raw_"; - /// - /// Constructor for config provider based indexes - /// - [EditorBrowsable(EditorBrowsableState.Never)] - protected UmbracoExamineIndex() - : base() - { - ProfilingLogger = Current.ProfilingLogger; - _configBased = true; - _diagnostics = new UmbracoExamineIndexDiagnostics(this, ProfilingLogger.Logger); - } - /// /// Create a new /// @@ -67,13 +52,13 @@ namespace Umbraco.Examine /// protected UmbracoExamineIndex( string name, - FieldDefinitionCollection fieldDefinitions, Directory luceneDirectory, + FieldDefinitionCollection fieldDefinitions, Analyzer defaultAnalyzer, - ProfilingLogger profilingLogger, + IProfilingLogger profilingLogger, IValueSetValidator validator = null, IReadOnlyDictionary indexValueTypes = null) - : base(name, fieldDefinitions, luceneDirectory, defaultAnalyzer, validator, indexValueTypes) + : base(name, luceneDirectory, fieldDefinitions, defaultAnalyzer, validator, indexValueTypes) { ProfilingLogger = profilingLogger ?? throw new ArgumentNullException(nameof(profilingLogger)); @@ -81,14 +66,12 @@ namespace Umbraco.Examine if (luceneDirectory is FSDirectory fsDir) LuceneIndexFolder = fsDir.Directory; - _diagnostics = new UmbracoExamineIndexDiagnostics(this, ProfilingLogger.Logger); + _diagnostics = new UmbracoExamineIndexDiagnostics(this, ProfilingLogger); } private readonly bool _configBased = false; - - - protected ProfilingLogger ProfilingLogger { get; } + protected IProfilingLogger ProfilingLogger { get; } /// /// When set to true Umbraco will keep the index in sync with Umbraco data automatically @@ -105,106 +88,19 @@ namespace Umbraco.Examine return searcher.GetAllIndexedFields(); } - protected ConfigIndexCriteria ConfigIndexCriteria { get; private set; } - - /// - /// The index set name which references an Examine - /// - public string IndexSetName { get; private set; } - - #region Initialize - - - /// - /// Setup the properties for the indexer from the provider settings - /// - /// - /// - /// - /// This is ONLY used for configuration based indexes - /// - public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config) - { - ProfilingLogger.Logger.Debug(GetType(), "{IndexerName} indexer initializing", name); - - if (config["enableDefaultEventHandler"] != null && bool.TryParse(config["enableDefaultEventHandler"], out var enabled)) - { - EnableDefaultEventHandler = enabled; - } - - //this is config based, so add the default definitions - foreach (var field in UmbracoFieldDefinitionCollection.UmbracoIndexFieldDefinitions) - { - FieldDefinitionCollection.TryAdd(field); - } - - //Need to check if the index set is specified... - if (config["indexSet"] == null) - { - var possibleSuffixes = new[] {"Index", "Indexer"}; - foreach (var suffix in possibleSuffixes) - { - if (!name.EndsWith(suffix)) continue; - - var setNameByConvension = name.Remove(name.LastIndexOf(suffix, StringComparison.Ordinal)) + "IndexSet"; - //check if we can assign the index set by naming convention - var set = IndexSets.Instance.Sets.Cast().SingleOrDefault(x => x.SetName == setNameByConvension); - - if (set == null) continue; - - //we've found an index set by naming conventions :) - IndexSetName = set.SetName; - - var indexSet = IndexSets.Instance.Sets[IndexSetName]; - - //get the index criteria and ensure folder - ConfigIndexCriteria = CreateFieldDefinitionsFromConfig(indexSet); - foreach (var fieldDefinition in ConfigIndexCriteria.StandardFields.Union(ConfigIndexCriteria.UserFields)) - { - //replace any existing or add - FieldDefinitionCollection.AddOrUpdate(fieldDefinition); - } - break; - } - } - else - { - //if an index set is specified, ensure it exists and initialize the indexer based on the set - - if (IndexSets.Instance.Sets[config["indexSet"]] == null) - throw new ArgumentException("The indexSet specified for the LuceneExamineIndexer provider does not exist"); - - IndexSetName = config["indexSet"]; - - var indexSet = IndexSets.Instance.Sets[IndexSetName]; - - //get the index criteria and ensure folder - ConfigIndexCriteria = CreateFieldDefinitionsFromConfig(indexSet); - foreach (var fieldDefinition in ConfigIndexCriteria.StandardFields.Union(ConfigIndexCriteria.UserFields)) - { - //replace any existing or add - FieldDefinitionCollection.AddOrUpdate(fieldDefinition); - } - } - - base.Initialize(name, config); - } - - #endregion - /// /// override to check if we can actually initialize. /// /// /// This check is required since the base examine lib will try to rebuild on startup /// - protected override void PerformDeleteFromIndex(string nodeId, Action onComplete) + protected override void PerformDeleteFromIndex(IEnumerable itemIds, Action onComplete) { if (CanInitialize()) { using (new SafeCallContext()) { - base.PerformDeleteFromIndex(nodeId, onComplete); + base.PerformDeleteFromIndex(itemIds, onComplete); } } } @@ -226,7 +122,7 @@ namespace Umbraco.Examine /// protected override void OnIndexingError(IndexingErrorEventArgs ex) { - ProfilingLogger.Logger.Error(GetType(), ex.InnerException, ex.Message); + ProfilingLogger.Error(GetType(), ex.InnerException, ex.Message); base.OnIndexingError(ex); } @@ -262,7 +158,7 @@ namespace Umbraco.Examine /// protected override void AddDocument(Document doc, ValueSet valueSet, IndexWriter writer) { - ProfilingLogger.Logger.Debug(GetType(), + ProfilingLogger.Debug(GetType(), "Write lucene doc id:{DocumentId}, category:{DocumentCategory}, type:{DocumentItemType}", valueSet.Id, valueSet.Category, @@ -288,17 +184,7 @@ namespace Umbraco.Examine e.ValueSet.Values[IconFieldName] = icon; } } - - private ConfigIndexCriteria CreateFieldDefinitionsFromConfig(IndexSet indexSet) - { - return new ConfigIndexCriteria( - indexSet.IndexAttributeFields.Cast().Select(x => new FieldDefinition(x.Name, x.Type)).ToArray(), - indexSet.IndexUserFields.Cast().Select(x => new FieldDefinition(x.Name, x.Type)).ToArray(), - indexSet.IncludeNodeTypes.ToList().Select(x => x.Name).ToArray(), - indexSet.ExcludeNodeTypes.ToList().Select(x => x.Name).ToArray(), - indexSet.IndexParentId); - } - + #region IIndexDiagnostics private readonly UmbracoExamineIndexDiagnostics _diagnostics; diff --git a/src/Umbraco.Examine/UmbracoExamineIndexDiagnostics.cs b/src/Umbraco.Examine/UmbracoExamineIndexDiagnostics.cs index 227b52e085..fed5b9bae7 100644 --- a/src/Umbraco.Examine/UmbracoExamineIndexDiagnostics.cs +++ b/src/Umbraco.Examine/UmbracoExamineIndexDiagnostics.cs @@ -64,7 +64,7 @@ namespace Umbraco.Examine { [nameof(UmbracoExamineIndex.CommitCount)] = _index.CommitCount, [nameof(UmbracoExamineIndex.DefaultAnalyzer)] = _index.DefaultAnalyzer.GetType().Name, - [nameof(UmbracoExamineIndex.DirectoryFactory)] = _index.DirectoryFactory, + ["LuceneDirectory"] = _index.GetLuceneDirectory().GetType().Name, [nameof(UmbracoExamineIndex.EnableDefaultEventHandler)] = _index.EnableDefaultEventHandler, [nameof(UmbracoExamineIndex.LuceneIndexFolder)] = _index.LuceneIndexFolder == null diff --git a/src/Umbraco.Examine/UmbracoMemberIndex.cs b/src/Umbraco.Examine/UmbracoMemberIndex.cs index 27490e0e18..9782f94fe4 100644 --- a/src/Umbraco.Examine/UmbracoMemberIndex.cs +++ b/src/Umbraco.Examine/UmbracoMemberIndex.cs @@ -1,18 +1,7 @@ -using System; -using System.Linq; -using Umbraco.Core; -using Umbraco.Core.Models; -using Umbraco.Core.Persistence.DatabaseModelDefinitions; -using Umbraco.Core.Services; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.ComponentModel; +using System.Collections.Generic; using Examine; using Examine.LuceneEngine; -using Examine.LuceneEngine.Indexing; -using Examine.LuceneEngine.Providers; using Lucene.Net.Analysis; -using Umbraco.Core.Composing; using Umbraco.Core.Logging; using Directory = Lucene.Net.Store.Directory; @@ -24,14 +13,6 @@ namespace Umbraco.Examine /// public class UmbracoMemberIndex : UmbracoExamineIndex { - /// - /// Constructor for config/provider based indexes - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public UmbracoMemberIndex() - { - } - /// /// Constructor to allow for creating an indexer at runtime /// @@ -46,19 +27,12 @@ namespace Umbraco.Examine FieldDefinitionCollection fieldDefinitions, Directory luceneDirectory, Analyzer analyzer, - ProfilingLogger profilingLogger, + IProfilingLogger profilingLogger, IValueSetValidator validator = null) : - base(name, fieldDefinitions, luceneDirectory, analyzer, profilingLogger, validator) + base(name, luceneDirectory, fieldDefinitions, analyzer, profilingLogger, validator) { } - public override void Initialize(string name, NameValueCollection config) - { - base.Initialize(name, config); - - ValueSetValidator = new MemberValueSetValidator(ConfigIndexCriteria.IncludeItemTypes, ConfigIndexCriteria.ExcludeItemTypes); - } - /// /// Overridden to ensure that the umbraco system field definitions are in place /// diff --git a/src/Umbraco.Tests.Benchmarks/BulkInsertBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/BulkInsertBenchmarks.cs index 0505974304..ee2e75cfad 100644 --- a/src/Umbraco.Tests.Benchmarks/BulkInsertBenchmarks.cs +++ b/src/Umbraco.Tests.Benchmarks/BulkInsertBenchmarks.cs @@ -4,18 +4,12 @@ using System.Data.SqlServerCe; using System.IO; using System.Linq; using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Configs; -using BenchmarkDotNet.Diagnosers; -using BenchmarkDotNet.Horology; -using BenchmarkDotNet.Jobs; using Umbraco.Core; using Umbraco.Core.Logging; using Umbraco.Core.Migrations.Install; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.Persistence.Mappers; -using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Scoping; using Umbraco.Tests.Benchmarks.Config; using Umbraco.Tests.TestHelpers; @@ -34,13 +28,11 @@ namespace Umbraco.Tests.Benchmarks { IScopeProvider f = null; var l = new Lazy(() => f); - var p = new SqlServerSyntaxProvider(l); var factory = new UmbracoDatabaseFactory( "server=.\\SQLExpress;database=YOURDB;user id=YOURUSER;password=YOURPASS", Constants.DatabaseProviders.SqlServer, - new [] { p }, logger, - new MapperCollection(Enumerable.Empty())); + new Lazy(() => new MapperCollection(Enumerable.Empty()))); return factory.CreateDatabase(); } @@ -49,9 +41,8 @@ namespace Umbraco.Tests.Benchmarks var f = new UmbracoDatabaseFactory( cstr, Constants.DatabaseProviders.SqlCe, - new[] { new SqlCeSyntaxProvider() }, logger, - new MapperCollection(Enumerable.Empty())); + new Lazy(() => new MapperCollection(Enumerable.Empty()))); return f.CreateDatabase(); } diff --git a/src/Umbraco.Tests.Benchmarks/CtorInvokeBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/CtorInvokeBenchmarks.cs index ccd9b01969..8d15613791 100644 --- a/src/Umbraco.Tests.Benchmarks/CtorInvokeBenchmarks.cs +++ b/src/Umbraco.Tests.Benchmarks/CtorInvokeBenchmarks.cs @@ -150,7 +150,7 @@ namespace Umbraco.Tests.Benchmarks // however, unfortunately, the generated "compiled to delegate" code cannot access private stuff :( - _emittedCtor = ReflectionUtilities.EmitConstuctor>(); + _emittedCtor = ReflectionUtilities.EmitConstructor>(); } public IFoo IlCtor(IFoo foo) @@ -167,7 +167,7 @@ namespace Umbraco.Tests.Benchmarks [Benchmark] public void EmitCtor() { - var ctor = ReflectionUtilities.EmitConstuctor>(); + var ctor = ReflectionUtilities.EmitConstructor>(); var foo = ctor(_foo); } diff --git a/src/Umbraco.Tests/App.config b/src/Umbraco.Tests/App.config index f76f6b73b6..73f9656f56 100644 --- a/src/Umbraco.Tests/App.config +++ b/src/Umbraco.Tests/App.config @@ -67,7 +67,7 @@ - + diff --git a/src/Umbraco.Tests/Cache/DistributedCache/DistributedCacheTests.cs b/src/Umbraco.Tests/Cache/DistributedCache/DistributedCacheTests.cs index dc67bb532f..68b666632c 100644 --- a/src/Umbraco.Tests/Cache/DistributedCache/DistributedCacheTests.cs +++ b/src/Umbraco.Tests/Cache/DistributedCache/DistributedCacheTests.cs @@ -1,11 +1,15 @@ using System; using System.Collections.Generic; using System.Linq; -using LightInject; +using Moq; using NUnit.Framework; +using Umbraco.Core; using Umbraco.Core.Cache; +using Umbraco.Core.Components; using Umbraco.Core.Composing; +using Umbraco.Core.Logging; using Umbraco.Core.Sync; +using Umbraco.Tests.Components; namespace Umbraco.Tests.Cache.DistributedCache { @@ -20,15 +24,18 @@ namespace Umbraco.Tests.Cache.DistributedCache [SetUp] public void Setup() { - var container = new ServiceContainer(); - container.ConfigureUmbracoCore(); + var register = RegisterFactory.Create(); - container.Register(_ => new TestServerRegistrar()); - container.Register(_ => new TestServerMessenger(), new PerContainerLifetime()); + var composition = new Composition(register, new TypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); - container.RegisterCollectionBuilder() + composition.RegisterUnique(_ => new TestServerRegistrar()); + composition.RegisterUnique(_ => new TestServerMessenger()); + + composition.WithCollectionBuilder() .Add(); + Current.Factory = composition.CreateFactory(); + _distributedCache = new Umbraco.Web.Cache.DistributedCache(); } diff --git a/src/Umbraco.Tests/Cache/DistributedCacheBinderTests.cs b/src/Umbraco.Tests/Cache/DistributedCacheBinderTests.cs index 3cc5e061a5..50b27da89f 100644 --- a/src/Umbraco.Tests/Cache/DistributedCacheBinderTests.cs +++ b/src/Umbraco.Tests/Cache/DistributedCacheBinderTests.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Threading; using Moq; using NUnit.Framework; +using Umbraco.Core.Components; using Umbraco.Core.Composing; using Umbraco.Core.Events; using Umbraco.Core.Models; @@ -19,6 +20,15 @@ namespace Umbraco.Tests.Cache [UmbracoTest(WithApplication = true)] public class DistributedCacheBinderTests : UmbracoTestBase { + protected override void Compose(Composition composition) + { + base.Compose(composition); + // refreshers.HandleEvents wants a UmbracoContext + // which wants these + composition.RegisterUnique(_ => Mock.Of()); + composition.WithCollectionBuilder(); + } + [Test] public void Can_Find_All_Event_Handlers() { @@ -138,11 +148,6 @@ namespace Umbraco.Tests.Cache if (domain.GetData(".appVPath") == null) domain.SetData(".appVPath", ""); - // refreshers.HandleEvents wants a UmbracoContext - // which wants these - Container.RegisterSingleton(_ => Mock.Of()); - Container.RegisterCollectionBuilder(); - // create some event definitions var definitions = new IEventDefinition[] { diff --git a/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs b/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs index 7f2edac876..28e753cadc 100644 --- a/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs +++ b/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs @@ -4,6 +4,8 @@ using Moq; using NUnit.Framework; using Umbraco.Core.Cache; using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; using Umbraco.Tests.Testing.Objects.Accessors; @@ -48,16 +50,14 @@ namespace Umbraco.Tests.Cache.PublishedCache "; } - public override void SetUp() + protected override void Initialize() { - base.SetUp(); + base.Initialize(); _httpContextFactory = new FakeHttpContextFactory("~/Home"); - var umbracoSettings = SettingsForTests.GenerateMockUmbracoSettings(); - var globalSettings = SettingsForTests.GenerateMockGlobalSettings(); - SettingsForTests.ConfigureSettings(umbracoSettings); - SettingsForTests.ConfigureSettings(globalSettings); + var umbracoSettings = Factory.GetInstance(); + var globalSettings = Factory.GetInstance(); _xml = new XmlDocument(); _xml.LoadXml(GetXml()); diff --git a/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs b/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs index aeda2eaca2..203350203d 100644 --- a/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs +++ b/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs @@ -1,5 +1,4 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Linq; using System.Xml; @@ -7,13 +6,13 @@ using Examine; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Cache; +using Umbraco.Core.Composing; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Strings; using Umbraco.Tests.TestHelpers; using Umbraco.Web.PublishedCache.XmlPublishedCache; using Umbraco.Tests.Testing; using Current = Umbraco.Web.Composing.Current; -using LightInject; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; using Umbraco.Tests.PublishedContent; @@ -30,7 +29,7 @@ namespace Umbraco.Tests.Cache.PublishedCache { base.Compose(); - Container.GetInstance() + Composition.WithCollectionBuilder() .Clear() .Append(); } @@ -207,7 +206,7 @@ namespace Umbraco.Tests.Cache.PublishedCache var store = new PublishedMediaCache(new XmlStore((XmlDocument)null, null, null, null), ServiceContext.MediaService, ServiceContext.UserService, new StaticCacheProvider(), ContentTypesCache); var doc = store.CreateFromCacheValues(store.ConvertFromSearchResult(result)); - DoAssert(doc, 1234, key, 0, 0, "/media/test.jpg", "Image", 23, "Shannon", "Shannon", 0, 0, "-1,1234", DateTime.Parse("2012-07-17T10:34:09"), DateTime.Parse("2012-07-16T10:34:09"), 2); + DoAssert(doc, 1234, key, templateIdVal: null, 0, "/media/test.jpg", "Image", 23, "Shannon", "Shannon", 0, 0, "-1,1234", DateTime.Parse("2012-07-17T10:34:09"), DateTime.Parse("2012-07-16T10:34:09"), 2); Assert.AreEqual(null, doc.Parent); } @@ -223,7 +222,7 @@ namespace Umbraco.Tests.Cache.PublishedCache var cache = new PublishedMediaCache(new XmlStore((XmlDocument)null, null, null, null), ServiceContext.MediaService, ServiceContext.UserService, new StaticCacheProvider(), ContentTypesCache); var doc = cache.CreateFromCacheValues(cache.ConvertFromXPathNavigator(navigator, true)); - DoAssert(doc, 2000, key, 0, 2, "image1", "Image", 23, "Shannon", "Shannon", 33, 33, "-1,2000", DateTime.Parse("2012-06-12T14:13:17"), DateTime.Parse("2012-07-20T18:50:43"), 1); + DoAssert(doc, 2000, key, templateIdVal: null, 2, "image1", "Image", 23, "Shannon", "Shannon", 33, 33, "-1,2000", DateTime.Parse("2012-06-12T14:13:17"), DateTime.Parse("2012-07-20T18:50:43"), 1); Assert.AreEqual(null, doc.Parent); Assert.AreEqual(2, doc.Children.Count()); Assert.AreEqual(2001, doc.Children.ElementAt(0).Id); @@ -337,7 +336,7 @@ namespace Umbraco.Tests.Cache.PublishedCache DictionaryPublishedContent dicDoc, int idVal = 1234, Guid keyVal = default(Guid), - int templateIdVal = 0, + int? templateIdVal = null, int sortOrderVal = 44, string urlNameVal = "testing", string nodeTypeAliasVal = "myType", @@ -368,7 +367,7 @@ namespace Umbraco.Tests.Cache.PublishedCache IPublishedContent doc, int idVal = 1234, Guid keyVal = default(Guid), - int templateIdVal = 0, + int? templateIdVal = null, int sortOrderVal = 44, string urlNameVal = "testing", string nodeTypeAliasVal = "myType", @@ -402,9 +401,6 @@ namespace Umbraco.Tests.Cache.PublishedCache Assert.AreEqual(createDateVal.Value, doc.CreateDate); Assert.AreEqual(updateDateVal.Value, doc.UpdateDate); Assert.AreEqual(levelVal, doc.Level); - } - - } } diff --git a/src/Umbraco.Tests/Clr/ReflectionUtilitiesTests.cs b/src/Umbraco.Tests/Clr/ReflectionUtilitiesTests.cs index 46dae8bcfd..f40ca3f500 100644 --- a/src/Umbraco.Tests/Clr/ReflectionUtilitiesTests.cs +++ b/src/Umbraco.Tests/Clr/ReflectionUtilitiesTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Reflection; using NUnit.Framework; using Umbraco.Core; @@ -13,16 +14,16 @@ namespace Umbraco.Tests.Clr [Test] public void EmitCtorEmits() { - var ctor1 = ReflectionUtilities.EmitConstuctor>(); + var ctor1 = ReflectionUtilities.EmitConstructor>(); Assert.IsInstanceOf(ctor1()); - var ctor2 = ReflectionUtilities.EmitConstuctor>(declaring: typeof(Class1)); + var ctor2 = ReflectionUtilities.EmitConstructor>(declaring: typeof(Class1)); Assert.IsInstanceOf(ctor2()); - var ctor3 = ReflectionUtilities.EmitConstuctor>(); + var ctor3 = ReflectionUtilities.EmitConstructor>(); Assert.IsInstanceOf(ctor3(42)); - var ctor4 = ReflectionUtilities.EmitConstuctor>(declaring: typeof(Class3)); + var ctor4 = ReflectionUtilities.EmitConstructor>(declaring: typeof(Class3)); Assert.IsInstanceOf(ctor4(42)); } @@ -43,14 +44,14 @@ namespace Umbraco.Tests.Clr [Test] public void EmitCtorEmitsPrivateCtor() { - var ctor = ReflectionUtilities.EmitConstuctor>(); + var ctor = ReflectionUtilities.EmitConstructor>(); Assert.IsInstanceOf(ctor("foo")); } [Test] public void EmitCtorThrowsIfNotFound() { - Assert.Throws(() => ReflectionUtilities.EmitConstuctor>()); + Assert.Throws(() => ReflectionUtilities.EmitConstructor>()); } [Test] @@ -63,7 +64,7 @@ namespace Umbraco.Tests.Clr [Test] public void EmitCtorReturnsNull() { - Assert.IsNull(ReflectionUtilities.EmitConstuctor>(false)); + Assert.IsNull(ReflectionUtilities.EmitConstructor>(false)); } [Test] @@ -553,6 +554,22 @@ namespace Umbraco.Tests.Clr // fixme - missing tests specifying 'returned' on method, property + [Test] + public void DeconstructAnonymousType() + { + var o = new { a = 1, b = "hello" }; + + var getters = new Dictionary>(); + foreach (var prop in o.GetType().GetProperties()) + getters[prop.Name] = ReflectionUtilities.EmitMethodUnsafe>(prop.GetMethod); + + Assert.AreEqual(2, getters.Count); + Assert.IsTrue(getters.ContainsKey("a")); + Assert.IsTrue(getters.ContainsKey("b")); + Assert.AreEqual(1, getters["a"](o)); + Assert.AreEqual("hello", getters["b"](o)); + } + #region IL Code // these functions can be examined in eg DotPeek to understand IL works diff --git a/src/Umbraco.Tests/Components/ComponentTests.cs b/src/Umbraco.Tests/Components/ComponentTests.cs index 995350f80e..a04636f919 100644 --- a/src/Umbraco.Tests/Components/ComponentTests.cs +++ b/src/Umbraco.Tests/Components/ComponentTests.cs @@ -1,17 +1,16 @@ using System; using System.Collections.Generic; using System.Linq; -using LightInject; using Moq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Components; +using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Scoping; -using Umbraco.Tests.TestHelpers; namespace Umbraco.Tests.Components { @@ -19,309 +18,426 @@ namespace Umbraco.Tests.Components public class ComponentTests { private static readonly List Composed = new List(); - private static readonly List Initialized = new List(); + private static readonly List Initialized = new List(); + private static readonly List Terminated = new List(); - private static IServiceContainer MockContainer(Action> setup = null) + private static IFactory MockFactory(Action> setup = null) { // fixme use IUmbracoDatabaseFactory vs UmbracoDatabaseFactory, clean it all up! - var testObjects = new TestObjects(null); + var mock = new Mock(); + var logger = Mock.Of(); - var s = testObjects.GetDefaultSqlSyntaxProviders(logger); - var f = new UmbracoDatabaseFactory(s, logger, new MapperCollection(Enumerable.Empty())); - var fs = new FileSystems(logger); + var f = new UmbracoDatabaseFactory(logger, new Lazy(() => new MapperCollection(Enumerable.Empty()))); + var fs = new FileSystems(mock.Object, logger); var p = new ScopeProvider(f, fs, logger); - var mock = new Mock(); mock.Setup(x => x.GetInstance(typeof (ILogger))).Returns(logger); - mock.Setup(x => x.GetInstance(typeof (ProfilingLogger))).Returns(new ProfilingLogger(Mock.Of(), Mock.Of())); + mock.Setup(x => x.GetInstance(typeof (IProfilingLogger))).Returns(new ProfilingLogger(Mock.Of(), Mock.Of())); mock.Setup(x => x.GetInstance(typeof (IUmbracoDatabaseFactory))).Returns(f); mock.Setup(x => x.GetInstance(typeof (IScopeProvider))).Returns(p); + setup?.Invoke(mock); return mock.Object; } + private static IRegister MockRegister() + { + return Mock.Of(); + } + + private static TypeLoader MockTypeLoader() + { + return new TypeLoader(); + } + + public static IRuntimeState MockRuntimeState(RuntimeLevel level) + { + var runtimeState = Mock.Of(); + Mock.Get(runtimeState).Setup(x => x.Level).Returns(level); + return runtimeState; + } + [Test] public void Boot1A() { - var container = MockContainer(); + var register = MockRegister(); + var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); - var loader = new BootLoader(container); + var types = TypeArray(); + var composers = new Composers(composition, types, Mock.Of()); Composed.Clear(); // 2 is Core and requires 4 // 3 is User - goes away with RuntimeLevel.Unknown // => reorder components accordingly - loader.Boot(TypeArray(), RuntimeLevel.Unknown); - AssertTypeArray(TypeArray(), Composed); + composers.Compose(); + AssertTypeArray(TypeArray(), Composed); + + var factory = MockFactory(m => + { + m.Setup(x => x.TryGetInstance(It.Is(t => t == typeof(ISomeResource)))).Returns(() => new SomeResource()); + m.Setup(x => x.GetInstance(It.IsAny())).Returns((type) => + { + if (type == typeof(Composer1)) return new Composer1(); + if (type == typeof(Composer5)) return new Composer5(); + if (type == typeof(Component5)) return new Component5(new SomeResource()); + if (type == typeof(IProfilingLogger)) return new ProfilingLogger(Mock.Of(), Mock.Of()); + throw new NotSupportedException(type.FullName); + }); + }); + + var builder = composition.WithCollectionBuilder(); + builder.RegisterWith(register); + var components = builder.CreateCollection(factory); + + Assert.IsEmpty(components); + components.Initialize(); + Assert.IsEmpty(Initialized); + components.Terminate(); + Assert.IsEmpty(Terminated); } [Test] public void Boot1B() { - var container = MockContainer(); + var register = MockRegister(); + var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Run)); - var loader = new BootLoader(container); + var types = TypeArray(); + var components = new Core.Components.Composers(composition, types, Mock.Of()); Composed.Clear(); // 2 is Core and requires 4 // 3 is User - stays with RuntimeLevel.Run // => reorder components accordingly - loader.Boot(TypeArray(), RuntimeLevel.Run); - AssertTypeArray(TypeArray(), Composed); + components.Compose(); + AssertTypeArray(TypeArray(), Composed); } [Test] public void Boot2() { - var container = MockContainer(); + var register = MockRegister(); + var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); - var loader = new BootLoader(container); + var types = TypeArray(); + var components = new Core.Components.Composers(composition, types, Mock.Of()); Composed.Clear(); // 21 is required by 20 // => reorder components accordingly - loader.Boot(TypeArray(), RuntimeLevel.Unknown); - AssertTypeArray(TypeArray(), Composed); + components.Compose(); + AssertTypeArray(TypeArray(), Composed); } [Test] public void Boot3() { - var container = MockContainer(); + var register = MockRegister(); + var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); - var loader = new BootLoader(container); + var types = TypeArray(); + var components = new Core.Components.Composers(composition, types, Mock.Of()); Composed.Clear(); // i23 requires 22 // 24, 25 implement i23 // 25 required by i23 // => reorder components accordingly - loader.Boot(TypeArray(), RuntimeLevel.Unknown); - AssertTypeArray(TypeArray(), Composed); + components.Compose(); + AssertTypeArray(TypeArray(), Composed); } [Test] public void BrokenRequire() { - var container = MockContainer(); + var register = MockRegister(); + var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); - var thing = new BootLoader(container); + var types = TypeArray(); + var components = new Core.Components.Composers(composition, types, Mock.Of()); Composed.Clear(); try { // 2 is Core and requires 4 // 4 is missing // => throw - thing.Boot(TypeArray < Component1, Component2, Component3>(), RuntimeLevel.Unknown); + components.Compose(); Assert.Fail("Expected exception."); } catch (Exception e) { - Assert.AreEqual("Broken component dependency: Umbraco.Tests.Components.ComponentTests+Component2 -> Umbraco.Tests.Components.ComponentTests+Component4.", e.Message); + Assert.AreEqual("Broken composer dependency: Umbraco.Tests.Components.ComponentTests+Composer2 -> Umbraco.Tests.Components.ComponentTests+Composer4.", e.Message); } } [Test] public void BrokenRequired() { - var container = MockContainer(); + var register = MockRegister(); + var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); - var thing = new BootLoader(container); + var types = TypeArray(); + var components = new Core.Components.Composers(composition, types, Mock.Of()); Composed.Clear(); // 2 is Core and requires 4 // 13 is required by 1 // 1 is missing // => reorder components accordingly - thing.Boot(TypeArray(), RuntimeLevel.Unknown); - AssertTypeArray(TypeArray(), Composed); + components.Compose(); + AssertTypeArray(TypeArray(), Composed); } [Test] public void Initialize() { - var container = MockContainer(m => + Composed.Clear(); + Initialized.Clear(); + Terminated.Clear(); + + var register = MockRegister(); + var factory = MockFactory(m => { m.Setup(x => x.TryGetInstance(It.Is(t => t == typeof (ISomeResource)))).Returns(() => new SomeResource()); + m.Setup(x => x.GetInstance(It.IsAny())).Returns((type) => + { + if (type == typeof(Composer1)) return new Composer1(); + if (type == typeof(Composer5)) return new Composer5(); + if (type == typeof(Component5)) return new Component5(new SomeResource()); + if (type == typeof(IProfilingLogger)) return new ProfilingLogger(Mock.Of(), Mock.Of()); + throw new NotSupportedException(type.FullName); + }); }); + var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); - var thing = new BootLoader(container); - Composed.Clear(); - thing.Boot(new[] { typeof(Component1), typeof(Component5) }, RuntimeLevel.Unknown); - Assert.AreEqual(2, Composed.Count); - Assert.AreEqual(typeof(Component1), Composed[0]); - Assert.AreEqual(typeof(Component5), Composed[1]); - Assert.AreEqual(1, Initialized.Count); - Assert.AreEqual("Umbraco.Tests.Components.ComponentTests+SomeResource", Initialized[0]); + var types = new[] { typeof(Composer1), typeof(Composer5) }; + var composers = new Composers(composition, types, Mock.Of()); + + Assert.IsEmpty(Composed); + composers.Compose(); + AssertTypeArray(TypeArray(), Composed); + + var builder = composition.WithCollectionBuilder(); + builder.RegisterWith(register); + var components = builder.CreateCollection(factory); + + Assert.IsEmpty(Initialized); + components.Initialize(); + AssertTypeArray(TypeArray(), Initialized); + + Assert.IsEmpty(Terminated); + components.Terminate(); + AssertTypeArray(TypeArray(), Terminated); } [Test] public void Requires1() { - var container = MockContainer(); + var register = MockRegister(); + var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); - var thing = new BootLoader(container); + var types = new[] { typeof(Composer6), typeof(Composer7), typeof(Composer8) }; + var components = new Core.Components.Composers(composition, types, Mock.Of()); Composed.Clear(); - thing.Boot(new[] { typeof(Component6), typeof(Component7), typeof(Component8) }, RuntimeLevel.Unknown); + components.Compose(); Assert.AreEqual(2, Composed.Count); - Assert.AreEqual(typeof(Component6), Composed[0]); - Assert.AreEqual(typeof(Component8), Composed[1]); + Assert.AreEqual(typeof(Composer6), Composed[0]); + Assert.AreEqual(typeof(Composer8), Composed[1]); } [Test] public void Requires2A() { - var container = MockContainer(); + var register = MockRegister(); + var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); - var thing = new BootLoader(container); + var types = new[] { typeof(Composer9), typeof(Composer2), typeof(Composer4) }; + var components = new Core.Components.Composers(composition, types, Mock.Of()); Composed.Clear(); - thing.Boot(new[] { typeof(Component9), typeof(Component2), typeof(Component4) }, RuntimeLevel.Unknown); + components.Compose(); Assert.AreEqual(2, Composed.Count); - Assert.AreEqual(typeof(Component4), Composed[0]); - Assert.AreEqual(typeof(Component2), Composed[1]); + Assert.AreEqual(typeof(Composer4), Composed[0]); + Assert.AreEqual(typeof(Composer2), Composed[1]); //Assert.AreEqual(typeof(Component9), Composed[2]); -- goes away with RuntimeLevel.Unknown } [Test] public void Requires2B() { - var container = MockContainer(); + var register = MockRegister(); + var factory = MockFactory(); + var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Run)); - var thing = new BootLoader(container); + var types = new[] { typeof(Composer9), typeof(Composer2), typeof(Composer4) }; + var composers = new Composers(composition, types, Mock.Of()); Composed.Clear(); - thing.Boot(new[] { typeof(Component9), typeof(Component2), typeof(Component4) }, RuntimeLevel.Run); + composers.Compose(); + var builder = composition.WithCollectionBuilder(); + builder.RegisterWith(register); + var components = builder.CreateCollection(factory); Assert.AreEqual(3, Composed.Count); - Assert.AreEqual(typeof(Component4), Composed[0]); - Assert.AreEqual(typeof(Component2), Composed[1]); - Assert.AreEqual(typeof(Component9), Composed[2]); + Assert.AreEqual(typeof(Composer4), Composed[0]); + Assert.AreEqual(typeof(Composer2), Composed[1]); + Assert.AreEqual(typeof(Composer9), Composed[2]); } [Test] public void WeakDependencies() { - var container = MockContainer(); + var register = MockRegister(); + var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); - var thing = new BootLoader(container); + var types = new[] { typeof(Composer10) }; + var components = new Core.Components.Composers(composition, types, Mock.Of()); Composed.Clear(); - thing.Boot(new[] { typeof(Component10) }, RuntimeLevel.Unknown); + components.Compose(); Assert.AreEqual(1, Composed.Count); - Assert.AreEqual(typeof(Component10), Composed[0]); + Assert.AreEqual(typeof(Composer10), Composed[0]); - thing = new BootLoader(container); + types = new[] { typeof(Composer11) }; + components = new Core.Components.Composers(composition, types, Mock.Of()); Composed.Clear(); - Assert.Throws(() => thing.Boot(new[] { typeof(Component11) }, RuntimeLevel.Unknown)); + Assert.Throws(() => components.Compose()); - thing = new BootLoader(container); + types = new[] { typeof(Composer2) }; + components = new Core.Components.Composers(composition, types, Mock.Of()); Composed.Clear(); - Assert.Throws(() => thing.Boot(new[] { typeof(Component2) }, RuntimeLevel.Unknown)); + Assert.Throws(() => components.Compose()); - thing = new BootLoader(container); + types = new[] { typeof(Composer12) }; + components = new Core.Components.Composers(composition, types, Mock.Of()); Composed.Clear(); - thing.Boot(new[] { typeof(Component12) }, RuntimeLevel.Unknown); + components.Compose(); Assert.AreEqual(1, Composed.Count); - Assert.AreEqual(typeof(Component12), Composed[0]); + Assert.AreEqual(typeof(Composer12), Composed[0]); } [Test] public void DisableMissing() { - var container = MockContainer(); + var register = MockRegister(); + var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown)); - var thing = new BootLoader(container); + var types = new[] { typeof(Composer6), typeof(Composer8) }; // 8 disables 7 which is not in the list + var components = new Core.Components.Composers(composition, types, Mock.Of()); Composed.Clear(); - thing.Boot(new[] { typeof(Component6), typeof(Component8) }, RuntimeLevel.Unknown); // 8 disables 7 which is not in the list + components.Compose(); Assert.AreEqual(2, Composed.Count); - Assert.AreEqual(typeof(Component6), Composed[0]); - Assert.AreEqual(typeof(Component8), Composed[1]); + Assert.AreEqual(typeof(Composer6), Composed[0]); + Assert.AreEqual(typeof(Composer8), Composed[1]); } #region Components - public class TestComponentBase : UmbracoComponentBase + public class TestComposerBase : IComposer { - public override void Compose(Composition composition) + public virtual void Compose(Composition composition) { - base.Compose(composition); Composed.Add(GetType()); } } - public class Component1 : TestComponentBase + public class Composer1 : TestComposerBase { } - [RequireComponent(typeof(Component4))] - public class Component2 : TestComponentBase, IUmbracoCoreComponent + [ComposeAfter(typeof(Composer4))] + public class Composer2 : TestComposerBase, ICoreComposer { } - public class Component3 : TestComponentBase, IUmbracoUserComponent + public class Composer3 : TestComposerBase, IUserComposer { } - public class Component4 : TestComponentBase + public class Composer4 : TestComposerBase { } - public class Component5 : TestComponentBase + public class Composer5 : TestComposerBase { - public void Initialize(ISomeResource resource) + public override void Compose(Composition composition) { - Initialized.Add(resource.GetType().FullName); + base.Compose(composition); + composition.Components().Append(); } } - [DisableComponent] - public class Component6 : TestComponentBase + public class TestComponentBase : IComponent + { + public virtual void Initialize() + { + Initialized.Add(GetType()); + } + + public virtual void Terminate() + { + Terminated.Add(GetType()); + } + } + + public class Component5 : TestComponentBase + { + private readonly ISomeResource _resource; + + public Component5(ISomeResource resource) + { + _resource = resource; + } + } + + [Disable] + public class Composer6 : TestComposerBase { } - public class Component7 : TestComponentBase + public class Composer7 : TestComposerBase { } - [DisableComponent(typeof(Component7))] - [EnableComponent(typeof(Component6))] - public class Component8 : TestComponentBase + [Disable(typeof(Composer7))] + [Enable(typeof(Composer6))] + public class Composer8 : TestComposerBase { } - public interface ITestComponent : IUmbracoUserComponent + public interface ITestComposer : IUserComposer { } - public class Component9 : TestComponentBase, ITestComponent + public class Composer9 : TestComposerBase, ITestComposer { } - [RequireComponent(typeof(ITestComponent))] - public class Component10 : TestComponentBase + [ComposeAfter(typeof(ITestComposer))] + public class Composer10 : TestComposerBase { } - [RequireComponent(typeof(ITestComponent), false)] - public class Component11 : TestComponentBase + [ComposeAfter(typeof(ITestComposer), false)] + public class Composer11 : TestComposerBase { } - [RequireComponent(typeof(Component4), true)] - public class Component12 : TestComponentBase, IUmbracoCoreComponent + [ComposeAfter(typeof(Composer4), true)] + public class Composer12 : TestComposerBase, ICoreComposer { } - [RequiredComponent(typeof(Component1))] - public class Component13 : TestComponentBase + [ComposeBefore(typeof(Composer1))] + public class Composer13 : TestComposerBase { } public interface ISomeResource { } public class SomeResource : ISomeResource { } - public class Component20 : TestComponentBase + public class Composer20 : TestComposerBase { } - [RequiredComponent(typeof(Component20))] - public class Component21 : TestComponentBase + [ComposeBefore(typeof(Composer20))] + public class Composer21 : TestComposerBase { } - public class Component22 : TestComponentBase + public class Composer22 : TestComposerBase { } - [RequireComponent(typeof(Component22))] - public interface IComponent23 : IUmbracoComponent + [ComposeAfter(typeof(Composer22))] + public interface IComposer23 : IComposer { } - public class Component24 : TestComponentBase, IComponent23 + public class Composer24 : TestComposerBase, IComposer23 { } // should insert itself between 22 and anything i23 - [RequiredComponent(typeof(IComponent23))] + [ComposeBefore(typeof(IComposer23))] //[RequireComponent(typeof(Component22))] - not needed, implement i23 - public class Component25 : TestComponentBase, IComponent23 + public class Composer25 : TestComposerBase, IComposer23 { } #endregion diff --git a/src/Umbraco.Tests/Composing/CollectionBuildersTests.cs b/src/Umbraco.Tests/Composing/CollectionBuildersTests.cs index 87b0cd5173..4c262fbf82 100644 --- a/src/Umbraco.Tests/Composing/CollectionBuildersTests.cs +++ b/src/Umbraco.Tests/Composing/CollectionBuildersTests.cs @@ -1,39 +1,40 @@ using System; using System.Collections.Generic; using System.Linq; -using LightInject; +using Moq; using NUnit.Framework; using Umbraco.Core.Composing; +using Umbraco.Core; +using Umbraco.Core.Components; +using Umbraco.Core.Logging; +using Umbraco.Tests.Components; namespace Umbraco.Tests.Composing { [TestFixture] public class CollectionBuildersTests { - private ServiceContainer _container; + private Composition _composition; [SetUp] public void Setup() { Current.Reset(); - _container = new ServiceContainer(); - _container.ConfigureUmbracoCore(); + var register = RegisterFactory.Create(); + _composition = new Composition(register, new TypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); } [TearDown] public void TearDown() { Current.Reset(); - - _container.Dispose(); - _container = null; } [Test] public void ContainsTypes() { - var builder = _container.RegisterCollectionBuilder() + var builder = _composition.WithCollectionBuilder() .Append() .Append(); @@ -42,14 +43,15 @@ namespace Umbraco.Tests.Composing Assert.IsFalse(builder.Has()); //Assert.IsFalse(col.ContainsType()); // does not compile - var col = builder.CreateCollection(); + var factory = _composition.CreateFactory(); + var col = builder.CreateCollection(factory); AssertCollection(col, typeof(Resolved1), typeof(Resolved2)); } [Test] public void CanClearBuilderBeforeCollectionIsCreated() { - var builder = _container.RegisterCollectionBuilder() + var builder = _composition.WithCollectionBuilder() .Append() .Append(); @@ -57,18 +59,20 @@ namespace Umbraco.Tests.Composing Assert.IsFalse(builder.Has()); Assert.IsFalse(builder.Has()); - var col = builder.CreateCollection(); + var factory = _composition.CreateFactory(); + var col = builder.CreateCollection(factory); AssertCollection(col); } [Test] public void CannotClearBuilderOnceCollectionIsCreated() { - var builder = _container.RegisterCollectionBuilder() + var builder = _composition.WithCollectionBuilder() .Append() .Append(); - var col = builder.CreateCollection(); + var factory = _composition.CreateFactory(); + var col = builder.CreateCollection(factory); Assert.Throws(() => builder.Clear()); } @@ -76,7 +80,7 @@ namespace Umbraco.Tests.Composing [Test] public void CanAppendToBuilder() { - var builder = _container.RegisterCollectionBuilder(); + var builder = _composition.WithCollectionBuilder(); builder.Append(); builder.Append(); @@ -84,16 +88,18 @@ namespace Umbraco.Tests.Composing Assert.IsTrue(builder.Has()); Assert.IsFalse(builder.Has()); - var col = builder.CreateCollection(); + var factory = _composition.CreateFactory(); + var col = builder.CreateCollection(factory); AssertCollection(col, typeof(Resolved1), typeof(Resolved2)); } [Test] public void CannotAppendToBuilderOnceCollectionIsCreated() { - var builder = _container.RegisterCollectionBuilder(); + var builder = _composition.WithCollectionBuilder(); - var col = builder.CreateCollection(); + var factory = _composition.CreateFactory(); + var col = builder.CreateCollection(factory); Assert.Throws(() => builder.Append() @@ -103,18 +109,21 @@ namespace Umbraco.Tests.Composing [Test] public void CanAppendDuplicateToBuilderAndDeDuplicate() { - var builder = _container.RegisterCollectionBuilder(); + var builder = _composition.WithCollectionBuilder(); builder.Append(); builder.Append(); - var col = builder.CreateCollection(); + var factory = _composition.CreateFactory(); + + var col = builder.CreateCollection(factory); AssertCollection(col, typeof(Resolved1)); } [Test] public void CannotAppendInvalidTypeToBUilder() { - var builder = _container.RegisterCollectionBuilder(); + var builder = _composition.WithCollectionBuilder(); + //builder.Append(); // does not compile Assert.Throws(() => builder.Append(new[] { typeof (Resolved4) }) // throws @@ -124,7 +133,7 @@ namespace Umbraco.Tests.Composing [Test] public void CanRemoveFromBuilder() { - var builder = _container.RegisterCollectionBuilder() + var builder = _composition.WithCollectionBuilder() .Append() .Append() .Remove(); @@ -133,30 +142,33 @@ namespace Umbraco.Tests.Composing Assert.IsFalse(builder.Has()); Assert.IsFalse(builder.Has()); - var col = builder.CreateCollection(); + var factory = _composition.CreateFactory(); + var col = builder.CreateCollection(factory); AssertCollection(col, typeof(Resolved1)); } [Test] public void CanRemoveMissingFromBuilder() { - var builder = _container.RegisterCollectionBuilder() + var builder = _composition.WithCollectionBuilder() .Append() .Append() .Remove(); - var col = builder.CreateCollection(); + var factory = _composition.CreateFactory(); + var col = builder.CreateCollection(factory); AssertCollection(col, typeof(Resolved1), typeof(Resolved2)); } [Test] public void CannotRemoveFromBuilderOnceCollectionIsCreated() { - var builder = _container.RegisterCollectionBuilder() + var builder = _composition.WithCollectionBuilder() .Append() .Append(); - var col = builder.CreateCollection(); + var factory = _composition.CreateFactory(); + var col = builder.CreateCollection(factory); Assert.Throws(() => builder.Remove() // throws ); @@ -165,7 +177,7 @@ namespace Umbraco.Tests.Composing [Test] public void CanInsertIntoBuilder() { - var builder = _container.RegisterCollectionBuilder() + var builder = _composition.WithCollectionBuilder() .Append() .Append() .Insert(); @@ -174,18 +186,20 @@ namespace Umbraco.Tests.Composing Assert.IsTrue(builder.Has()); Assert.IsTrue(builder.Has()); - var col = builder.CreateCollection(); + var factory = _composition.CreateFactory(); + var col = builder.CreateCollection(factory); AssertCollection(col, typeof(Resolved3), typeof(Resolved1), typeof(Resolved2)); } [Test] public void CannotInsertIntoBuilderOnceCollectionIsCreated() { - var builder = _container.RegisterCollectionBuilder() + var builder = _composition.WithCollectionBuilder() .Append() .Append(); - var col = builder.CreateCollection(); + var factory = _composition.CreateFactory(); + var col = builder.CreateCollection(factory); Assert.Throws(() => builder.Insert() // throws ); @@ -194,29 +208,31 @@ namespace Umbraco.Tests.Composing [Test] public void CanInsertDuplicateIntoBuilderAndDeDuplicate() { - var builder = _container.RegisterCollectionBuilder() + var builder = _composition.WithCollectionBuilder() .Append() .Append() .Insert(); - var col = builder.CreateCollection(); + var factory = _composition.CreateFactory(); + var col = builder.CreateCollection(factory); AssertCollection(col, typeof(Resolved2), typeof(Resolved1)); } [Test] public void CanInsertIntoEmptyBuilder() { - var builder = _container.RegisterCollectionBuilder(); + var builder = _composition.WithCollectionBuilder(); builder.Insert(); - var col = builder.CreateCollection(); + var factory = _composition.CreateFactory(); + var col = builder.CreateCollection(factory); AssertCollection(col, typeof(Resolved2)); } [Test] public void CannotInsertIntoBuilderAtWrongIndex() { - var builder = _container.RegisterCollectionBuilder() + var builder = _composition.WithCollectionBuilder() .Append() .Append(); @@ -232,7 +248,7 @@ namespace Umbraco.Tests.Composing [Test] public void CanInsertIntoBuilderBefore() { - var builder = _container.RegisterCollectionBuilder() + var builder = _composition.WithCollectionBuilder() .Append() .Append() .InsertBefore(); @@ -241,18 +257,20 @@ namespace Umbraco.Tests.Composing Assert.IsTrue(builder.Has()); Assert.IsTrue(builder.Has()); - var col = builder.CreateCollection(); + var factory = _composition.CreateFactory(); + var col = builder.CreateCollection(factory); AssertCollection(col, typeof(Resolved1), typeof(Resolved3), typeof(Resolved2)); } [Test] public void CannotInsertIntoBuilderBeforeOnceCollectionIsCreated() { - var builder = _container.RegisterCollectionBuilder() + var builder = _composition.WithCollectionBuilder() .Append() .Append(); - var col = builder.CreateCollection(); + var factory = _composition.CreateFactory(); + var col = builder.CreateCollection(factory); Assert.Throws(() => builder.InsertBefore() ); @@ -261,19 +279,20 @@ namespace Umbraco.Tests.Composing [Test] public void CanInsertDuplicateIntoBuilderBeforeAndDeDuplicate() { - var builder = _container.RegisterCollectionBuilder() + var builder = _composition.WithCollectionBuilder() .Append() .Append() .InsertBefore(); - var col = builder.CreateCollection(); + var factory = _composition.CreateFactory(); + var col = builder.CreateCollection(factory); AssertCollection(col, typeof(Resolved2), typeof(Resolved1)); } [Test] public void CannotInsertIntoBuilderBeforeMissing() { - var builder = _container.RegisterCollectionBuilder() + var builder = _composition.WithCollectionBuilder() .Append(); Assert.Throws(() => @@ -284,7 +303,7 @@ namespace Umbraco.Tests.Composing [Test] public void ScopeBuilderCreatesScopedCollection() { - _container.RegisterCollectionBuilder() + _composition.WithCollectionBuilder() .Append() .Append(); @@ -292,19 +311,25 @@ namespace Umbraco.Tests.Composing // but the container manages the scope, so to test the scope // the collection must come from the container - var col1 = _container.GetInstance(); - AssertCollection(col1, typeof(Resolved1), typeof(Resolved2)); + var factory = _composition.CreateFactory(); - var col2 = _container.GetInstance(); - AssertCollection(col2, typeof(Resolved1), typeof(Resolved2)); + using (factory.BeginScope()) + { + var col1 = factory.GetInstance(); + AssertCollection(col1, typeof(Resolved1), typeof(Resolved2)); + + var col2 = factory.GetInstance(); + AssertCollection(col2, typeof(Resolved1), typeof(Resolved2)); + + AssertSameCollection(col1, col2); + } - AssertSameCollection(col1, col2); } [Test] public void TransientBuilderCreatesTransientCollection() { - _container.RegisterCollectionBuilder() + _composition.WithCollectionBuilder() .Append() .Append(); @@ -312,10 +337,12 @@ namespace Umbraco.Tests.Composing // but the container manages the scope, so to test the scope // the collection must come from the container - var col1 = _container.GetInstance(); + var factory = _composition.CreateFactory(); + + var col1 = factory.GetInstance(); AssertCollection(col1, typeof(Resolved1), typeof(Resolved2)); - var col2 = _container.GetInstance(); + var col2 = factory.GetInstance(); AssertCollection(col1, typeof(Resolved1), typeof(Resolved2)); AssertNotSameCollection(col1, col2); @@ -324,19 +351,20 @@ namespace Umbraco.Tests.Composing [Test] public void BuilderRespectsTypesOrder() { - var builder = _container.RegisterCollectionBuilder() + var builder = _composition.WithCollectionBuilder() .Append() .Insert() .InsertBefore(); - var col1 = builder.CreateCollection(); + var factory = _composition.CreateFactory(); + var col1 = builder.CreateCollection(factory); AssertCollection(col1, typeof(Resolved1), typeof(Resolved2), typeof(Resolved3)); } [Test] public void ScopeBuilderRespectsContainerScope() { - _container.RegisterCollectionBuilder() + _composition.WithCollectionBuilder() .Append() .Append(); @@ -344,34 +372,40 @@ namespace Umbraco.Tests.Composing // but the container manages the scope, so to test the scope // the collection must come from the container - var scope1 = _container.BeginScope(); + TestCollection col1A, col1B; + + var factory = _composition.CreateFactory(); + + using (factory.BeginScope()) + { + col1A = factory.GetInstance(); + col1B = factory.GetInstance(); + } - var col1A = _container.GetInstance(); AssertCollection(col1A, typeof(Resolved1), typeof(Resolved2)); - var col1B = _container.GetInstance(); AssertCollection(col1B, typeof(Resolved1), typeof(Resolved2)); - AssertSameCollection(col1A, col1B); - _container.ScopeManagerProvider.GetScopeManager(_container).CurrentScope.Dispose(); - var scope2 = _container.BeginScope(); + TestCollection col2; + + using (factory.BeginScope()) + { + col2 = factory.GetInstance(); + } - var col2 = _container.GetInstance(); AssertCollection(col2, typeof(Resolved1), typeof(Resolved2)); - AssertNotSameCollection(col1A, col2); - - _container.ScopeManagerProvider.GetScopeManager(_container).CurrentScope.Dispose(); } [Test] public void WeightedBuilderCreatesWeightedCollection() { - var builder = _container.RegisterCollectionBuilder() + var builder = _composition.WithCollectionBuilder() .Add() .Add(); - var col = builder.CreateCollection(); + var factory = _composition.CreateFactory(); + var col = builder.CreateCollection(factory); AssertCollection(col, typeof(Resolved2), typeof(Resolved1)); } @@ -432,44 +466,28 @@ namespace Umbraco.Tests.Composing // ReSharper disable once ClassNeverInstantiated.Local private class TestCollectionBuilder : OrderedCollectionBuilderBase { - public TestCollectionBuilder(IServiceContainer container) - : base(container) - { } - protected override TestCollectionBuilder This => this; } // ReSharper disable once ClassNeverInstantiated.Local private class TestCollectionBuilderTransient : OrderedCollectionBuilderBase { - public TestCollectionBuilderTransient(IServiceContainer container) - : base(container) - { } - protected override TestCollectionBuilderTransient This => this; - protected override ILifetime CollectionLifetime => null; // transient + protected override Lifetime CollectionLifetime => Lifetime.Transient; // transient } // ReSharper disable once ClassNeverInstantiated.Local private class TestCollectionBuilderScope : OrderedCollectionBuilderBase { - public TestCollectionBuilderScope(IServiceContainer container) - : base(container) - { } - protected override TestCollectionBuilderScope This => this; - protected override ILifetime CollectionLifetime => new PerScopeLifetime(); + protected override Lifetime CollectionLifetime => Lifetime.Scope; } // ReSharper disable once ClassNeverInstantiated.Local private class TestCollectionBuilderWeighted : WeightedCollectionBuilderBase { - public TestCollectionBuilderWeighted(IServiceContainer container) - : base(container) - { } - protected override TestCollectionBuilderWeighted This => this; } diff --git a/src/Umbraco.Tests/Composing/ComposingTestBase.cs b/src/Umbraco.Tests/Composing/ComposingTestBase.cs index be595885e7..48850afd97 100644 --- a/src/Umbraco.Tests/Composing/ComposingTestBase.cs +++ b/src/Umbraco.Tests/Composing/ComposingTestBase.cs @@ -4,6 +4,7 @@ using Moq; using NUnit.Framework; using Umbraco.Core.Cache; using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Tests.TestHelpers; @@ -13,14 +14,14 @@ namespace Umbraco.Tests.Composing { protected TypeLoader TypeLoader { get; private set; } - protected ProfilingLogger ProfilingLogger { get; private set; } + protected IProfilingLogger ProfilingLogger { get; private set; } [SetUp] public void Initialize() { ProfilingLogger = new ProfilingLogger(Mock.Of(), Mock.Of()); - TypeLoader = new TypeLoader(NullCacheProvider.Instance, SettingsForTests.GenerateMockGlobalSettings(), ProfilingLogger, detectChanges: false) + TypeLoader = new TypeLoader(NullCacheProvider.Instance, LocalTempStorage.Default, ProfilingLogger, detectChanges: false) { AssembliesToScan = AssembliesToScan }; diff --git a/src/Umbraco.Tests/Composing/ContainerConformingTests.cs b/src/Umbraco.Tests/Composing/ContainerConformingTests.cs new file mode 100644 index 0000000000..9585c98cb2 --- /dev/null +++ b/src/Umbraco.Tests/Composing/ContainerConformingTests.cs @@ -0,0 +1,356 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using Umbraco.Core.Composing; + +namespace Umbraco.Tests.Composing +{ + [TestFixture] + public class ContainerConformingTests + { + // tests that a container conforms + + private IRegister GetRegister() => RegisterFactory.Create(); + + [Test] + public void CanRegisterAndGet() + { + var register = GetRegister(); + + register.Register(); + + var factory = register.CreateFactory(); + + var thing = factory.GetInstance(); + Assert.IsNotNull(thing); + Assert.IsInstanceOf(thing); + } + + [Test] + public void CanRegisterAndGetLazy() + { + var register = GetRegister(); + + register.Register(); + + var factory = register.CreateFactory(); + + var lazyThing = factory.GetInstance>(); + Assert.IsNotNull(lazyThing); + Assert.IsInstanceOf>(lazyThing); + var thing = lazyThing.Value; + Assert.IsNotNull(thing); + Assert.IsInstanceOf(thing); + } + + [Test] + public void CannotRegistedAndGetBase() + { + var register = GetRegister(); + + register.Register(); + + var factory = register.CreateFactory(); + + Assert.IsNull(factory.TryGetInstance()); + } + + [Test] + public void CannotRegisterAndGetInterface() + { + var register = GetRegister(); + + register.Register(); + + var factory = register.CreateFactory(); + + Assert.IsNull(factory.TryGetInstance()); + } + + [Test] + public void CanRegisterAndGetAllBase() + { + var register = GetRegister(); + + register.Register(); + + var factory = register.CreateFactory(); + + var things = factory.GetAllInstances(); + Assert.AreEqual(1, things.Count()); + + // lightInject: would be zero with option EnableVariance set to false + } + + [Test] + public void CanRegisterAndGetAllInterface() + { + var register = GetRegister(); + + register.Register(); + + var factory = register.CreateFactory(); + + var things = factory.GetAllInstances(); + Assert.AreEqual(1, things.Count()); + + // lightInject: would be zero with option EnableVariance set to false + } + + [Test] + public void CanRegisterBaseAndGet() + { + var register = GetRegister(); + + register.Register(); + + var factory = register.CreateFactory(); + + var thing = factory.GetInstance(); + Assert.IsNotNull(thing); + Assert.IsInstanceOf(thing); + } + + [Test] + public void CanRegisterInterfaceAndGet() + { + var register = GetRegister(); + + register.Register(); + + var factory = register.CreateFactory(); + + var thing = factory.GetInstance(); + Assert.IsNotNull(thing); + Assert.IsInstanceOf(thing); + } + + [Test] + public void NonSingletonServiceIsNotUnique() + { + var register = GetRegister(); + + register.Register(); + register.Register(); + + var factory = register.CreateFactory(); + + var things = factory.GetInstance>(); + Assert.AreEqual(2, things.Count()); + + Assert.IsNull(factory.TryGetInstance()); + } + + [Test] + public void SingletonServiceIsUnique() // fixme - but what is LightInject actually doing + { + var register = GetRegister(); + + // fixme + // LightInject is 'unique' per serviceType+serviceName + // but that's not how all containers work + // and we should not rely on it + // if we need unique, use RegisterUnique + + // for Core services that ppl may want to redefine in components, + // it is important to be able to have a unique, singleton implementation, + // and to redefine it - how it's done at container's level depends + // on each container + + // redefine the service + register.Register(Lifetime.Singleton); + register.Register(Lifetime.Singleton); + + var factory = register.CreateFactory(); + + var things = factory.GetInstance>(); + Assert.AreEqual(1, things.Count()); + + var thing = factory.GetInstance(); + Assert.IsInstanceOf(thing); + } + + [Test] + public void SingletonImplementationIsNotUnique() + { + var register = GetRegister(); + + // define two implementations + register.Register(Lifetime.Singleton); + register.Register(Lifetime.Singleton); + + var factory = register.CreateFactory(); + + var things = factory.GetInstance>(); + Assert.AreEqual(2, things.Count()); + + Assert.IsNull(factory.TryGetInstance()); + } + + [Test] + public void ActualInstanceIsNotUnique() + { + var register = GetRegister(); + + // define two instances + register.RegisterInstance(typeof(Thing1), new Thing1()); + register.RegisterInstance(typeof(Thing1), new Thing2()); + + var factory = register.CreateFactory(); + + var things = factory.GetInstance>(); + //Assert.AreEqual(2, things.Count()); + Assert.AreEqual(1, things.Count()); // well, yes they are unique? + + Assert.IsNull(factory.TryGetInstance()); + } + + [Test] + public void InterfaceInstanceIsNotUnique() + { + var register = GetRegister(); + + // define two instances + register.RegisterInstance(typeof(IThing), new Thing1()); + register.RegisterInstance(typeof(IThing), new Thing2()); + + var factory = register.CreateFactory(); + + var things = factory.GetInstance>(); + //Assert.AreEqual(2, things.Count()); + Assert.AreEqual(1, things.Count()); // well, yes they are unique? + + //Assert.IsNull(factory.TryGetInstance()); + Assert.IsNotNull(factory.TryGetInstance()); // well, what? + } + + [Test] + public void CanInjectEnumerableOfBase() + { + var register = GetRegister(); + + register.Register(); + register.Register(); + register.Register(); + + var factory = register.CreateFactory(); + + var needThings = factory.GetInstance(); + Assert.AreEqual(2, needThings.Things.Count()); + } + + [Test] + public void CanGetEnumerableOfBase() + { + var register = GetRegister(); + + register.Register(); + register.Register(); + + var factory = register.CreateFactory(); + + var things = factory.GetInstance>(); + Assert.AreEqual(2, things. Count()); + } + + [Test] + public void CanGetEmptyEnumerableOfBase() + { + var register = GetRegister(); + var factory = register.CreateFactory(); + + var things = factory.GetInstance>(); + Assert.AreEqual(0, things.Count()); + } + + [Test] + public void CanGetEmptyAllInstancesOfBase() + { + var register = GetRegister(); + var factory = register.CreateFactory(); + + var things = factory.GetAllInstances(); + Assert.AreEqual(0, things.Count()); + } + + [Test] + public void CanTryGetEnumerableOfBase() + { + var register = GetRegister(); + + register.Register(); + register.Register(); + + var factory = register.CreateFactory(); + + var things = factory.TryGetInstance>(); + Assert.AreEqual(2, things.Count()); + } + + [Test] + public void CanRegisterSingletonInterface() + { + var register = GetRegister(); + register.Register(Lifetime.Singleton); + var factory = register.CreateFactory(); + var s1 = factory.GetInstance(); + var s2 = factory.GetInstance(); + Assert.AreSame(s1, s2); + } + + [Test] + public void CanRegisterSingletonClass() + { + var register = GetRegister(); + register.Register(Lifetime.Singleton); + var factory = register.CreateFactory(); + var s1 = factory.GetInstance(); + var s2 = factory.GetInstance(); + Assert.AreSame(s1, s2); + } + + [Test] + public void CanReRegisterSingletonInterface() + { + var register = GetRegister(); + register.Register(Lifetime.Singleton); + register.Register(Lifetime.Singleton); + var factory = register.CreateFactory(); + var s = factory.GetInstance(); + Assert.IsInstanceOf(s); + } + + [Test] + public void CanRegisterSingletonWithCreate() + { + var register = GetRegister(); + register.Register(c => c.CreateInstance(new Thing1()), Lifetime.Singleton); + var factory = register.CreateFactory(); + var s1 = factory.GetInstance(); + var s2 = factory.GetInstance(); + Assert.AreSame(s1, s2); + } + + public interface IThing { } + + public abstract class ThingBase : IThing { } + public class Thing1 : ThingBase { } + public class Thing2 : ThingBase { } + + public class Thing3 : ThingBase + { + public Thing3(Thing1 thing) { } + } + + public class NeedThings + { + public NeedThings(IEnumerable things) + { + Things = things; + } + + public IEnumerable Things { get; } + } + } +} diff --git a/src/Umbraco.Tests/Composing/LazyCollectionBuilderTests.cs b/src/Umbraco.Tests/Composing/LazyCollectionBuilderTests.cs index 7a39186fea..cbabae1a83 100644 --- a/src/Umbraco.Tests/Composing/LazyCollectionBuilderTests.cs +++ b/src/Umbraco.Tests/Composing/LazyCollectionBuilderTests.cs @@ -1,10 +1,15 @@ using System; using System.Collections.Generic; using System.Linq; -using LightInject; +using Moq; using NUnit.Framework; using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Components; using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; +using Umbraco.Tests.Components; namespace Umbraco.Tests.Composing { @@ -23,6 +28,11 @@ namespace Umbraco.Tests.Composing Current.Reset(); } + private IRegister CreateRegister() + { + return RegisterFactory.Create(); + } + // note // lazy collection builder does not throw on duplicate, just uses distinct types // so we don't have a test for duplicates as we had with resolvers in v7 @@ -30,22 +40,24 @@ namespace Umbraco.Tests.Composing [Test] public void LazyCollectionBuilderHandlesTypes() { - var container = new ServiceContainer(); - container.ConfigureUmbracoCore(); + var container = CreateRegister(); + var composition = new Composition(container, new TypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); - container.RegisterCollectionBuilder() + composition.WithCollectionBuilder() .Add() .Add() .Add() .Add(); - var values = container.GetInstance(); + var factory = composition.CreateFactory(); + + var values = factory.GetInstance(); Assert.AreEqual(3, values.Count()); Assert.IsTrue(values.Select(x => x.GetType()) .ContainsAll(new[] { typeof(TransientObject1), typeof(TransientObject2), typeof(TransientObject3) })); - var other = container.GetInstance(); + var other = factory.GetInstance(); Assert.AreNotSame(values, other); // transient var o1 = other.FirstOrDefault(x => x is TransientObject1); Assert.IsFalse(values.Contains(o1)); // transient @@ -54,21 +66,23 @@ namespace Umbraco.Tests.Composing [Test] public void LazyCollectionBuilderHandlesProducers() { - var container = new ServiceContainer(); - container.ConfigureUmbracoCore(); + var container = CreateRegister(); + var composition = new Composition(container, new TypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); - container.RegisterCollectionBuilder() + composition.WithCollectionBuilder() .Add(() => new[] { typeof(TransientObject3), typeof(TransientObject2) }) .Add(() => new[] { typeof(TransientObject3), typeof(TransientObject2) }) .Add(() => new[] { typeof(TransientObject1) }); - var values = container.GetInstance(); + var factory = composition.CreateFactory(); + + var values = factory.GetInstance(); Assert.AreEqual(3, values.Count()); Assert.IsTrue(values.Select(x => x.GetType()) .ContainsAll(new[] { typeof(TransientObject1), typeof(TransientObject2), typeof(TransientObject3) })); - var other = container.GetInstance(); + var other = factory.GetInstance(); Assert.AreNotSame(values, other); // transient var o1 = other.FirstOrDefault(x => x is TransientObject1); Assert.IsFalse(values.Contains(o1)); // transient @@ -77,22 +91,24 @@ namespace Umbraco.Tests.Composing [Test] public void LazyCollectionBuilderHandlesTypesAndProducers() { - var container = new ServiceContainer(); - container.ConfigureUmbracoCore(); + var container = CreateRegister(); + var composition = new Composition(container, new TypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); - container.RegisterCollectionBuilder() + composition.WithCollectionBuilder() .Add() .Add() .Add() .Add(() => new[] { typeof(TransientObject1) }); - var values = container.GetInstance(); + var factory = composition.CreateFactory(); + + var values = factory.GetInstance(); Assert.AreEqual(3, values.Count()); Assert.IsTrue(values.Select(x => x.GetType()) .ContainsAll(new[] { typeof(TransientObject1), typeof(TransientObject2), typeof(TransientObject3) })); - var other = container.GetInstance(); + var other = factory.GetInstance(); Assert.AreNotSame(values, other); // transient var o1 = other.FirstOrDefault(x => x is TransientObject1); Assert.IsFalse(values.Contains(o1)); // transient @@ -101,10 +117,10 @@ namespace Umbraco.Tests.Composing [Test] public void LazyCollectionBuilderThrowsOnIllegalTypes() { - var container = new ServiceContainer(); - container.ConfigureUmbracoCore(); + var container = CreateRegister(); + var composition = new Composition(container, new TypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); - container.RegisterCollectionBuilder() + composition.WithCollectionBuilder() .Add() // illegal, does not implement the interface! @@ -115,23 +131,25 @@ namespace Umbraco.Tests.Composing Assert.Throws(() => { - // but throws here when trying to register the types - var values = container.GetInstance(); + // but throws here when trying to register the types, right before creating the factory + var factory = composition.CreateFactory(); }); } [Test] public void LazyCollectionBuilderCanExcludeTypes() { - var container = new ServiceContainer(); - container.ConfigureUmbracoCore(); + var container = CreateRegister(); + var composition = new Composition(container, new TypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); - container.RegisterCollectionBuilder() + composition.WithCollectionBuilder() .Add() .Add(() => new[] { typeof(TransientObject3), typeof(TransientObject2), typeof(TransientObject1) }) .Exclude(); - var values = container.GetInstance(); + var factory = composition.CreateFactory(); + + var values = factory.GetInstance(); Assert.AreEqual(2, values.Count()); Assert.IsFalse(values.Select(x => x.GetType()) @@ -139,7 +157,7 @@ namespace Umbraco.Tests.Composing Assert.IsTrue(values.Select(x => x.GetType()) .ContainsAll(new[] { typeof(TransientObject1), typeof(TransientObject2) })); - var other = container.GetInstance(); + var other = factory.GetInstance(); Assert.AreNotSame(values, other); // transient var o1 = other.FirstOrDefault(x => x is TransientObject1); Assert.IsFalse(values.Contains(o1)); // transient @@ -165,13 +183,9 @@ namespace Umbraco.Tests.Composing // ReSharper disable once ClassNeverInstantiated.Local private class TestCollectionBuilder : LazyCollectionBuilderBase { - public TestCollectionBuilder(IServiceContainer container) - : base(container) - { } - protected override TestCollectionBuilder This => this; - protected override ILifetime CollectionLifetime => null; // transient + protected override Lifetime CollectionLifetime => Lifetime.Transient; // transient } // ReSharper disable once ClassNeverInstantiated.Local diff --git a/src/Umbraco.Tests/Composing/LightInjectValidation.cs b/src/Umbraco.Tests/Composing/LightInjectValidation.cs new file mode 100644 index 0000000000..75062e613c --- /dev/null +++ b/src/Umbraco.Tests/Composing/LightInjectValidation.cs @@ -0,0 +1,349 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using LightInject; +using System.Collections.Concurrent; +using System.Collections.ObjectModel; +using System.Reflection; +using ServiceMap = System.Collections.Generic.Dictionary>; + +/********************************************************************************* + The MIT License (MIT) + + Copyright (c) 2017 bernhard.richter@gmail.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +****************************************************************************** + LightInject.Validation version 1.0.1 + http://www.lightinject.net/ + http://twitter.com/bernhardrichter +******************************************************************************/ + +namespace Umbraco.Tests.Composing +{ + public static class LightInjectValidation + { + private static readonly ConcurrentDictionary LifeSpans = new ConcurrentDictionary(); + + private const string NotDisposeMessageServiceType = + @"The service {0} is being injected as a constructor argument into {1} implements IDisposable, " + + "but is registered without a lifetime (transient). LightInject will not be able to dispose the instance represented by {0}. " + + "If the intent was to manually control the instantiation and destruction, inject Func<{0}> instead. " + + "Otherwise register `{0}` with a lifetime (PerContainer, PerRequest or PerScope)."; + + private const string NotDisposeMessageImplementingType = + @"The service {0} represented by {1} is being injected as a constructor argument into {2} implements IDisposable, " + + "but is registered without a lifetime (transient). LightInject will not be able to dispose the instance represented by {0}. " + + "If the intent was to manually control the instantiation and destruction, inject Func<{0}> instead. " + + "Otherwise register `{0}` with a lifetime (PerContainer, PerRequest or PerScope)."; + + + private const string MissingDeferredDependency = + @"The injected '{0}' does not contain a registration for the underlying type '{1}'. " + + "Ensure that '{1}' is registered so that the service can be resolved by '{0}'"; + + /* + The service 'NameSpace.IBar' that is being injected into 'NameSpace.Foo' is registered with +with a 'Transient' lifetime while the 'NameSpace.Foo' is registered with the 'PerScope' lifetime. +Ensure that 'NameSpace.IBar' is registered with a lifetime that is equal to or has a longer lifetime than the 'PerScope' lifetime. + */ + private const string CaptiveDependency = + @"The service '{0}' that is being injected into {1} is registered with " + + "a '{2}' lifetime while the {1} is registered with the '{3}' lifetime. " + + "Ensure that '{0}' is registered with a lifetime that is equal to or has a longer lifetime than the '{3}' lifetime. " + + "Alternatively ensure that `{1}` is registered with a lifetime that is equal to or " + + "has a shorter lifetime than `{2}` lifetime."; + + private const string MissingDependency = + "Class: 'NameSpace.Foo', Parameter 'NameSpace.IBar bar' -> The injected service NameSpace IBar is not registered." + ; + + + static LightInjectValidation() + { + LifeSpans.TryAdd(typeof(PerRequestLifeTime), 10); + LifeSpans.TryAdd(typeof(PerScopeLifetime), 20); + LifeSpans.TryAdd(typeof(PerContainerLifetime), 30); + } + + public static IEnumerable Validate(this ServiceContainer container) + { + var serviceMap = container.AvailableServices.GroupBy(sr => sr.ServiceType).ToDictionary(gr => gr.Key, + gr => gr.ToDictionary(sr => sr.ServiceName, sr => sr, StringComparer.OrdinalIgnoreCase)); + + var verifyableServices = container.AvailableServices.Where(sr => sr.ImplementingType != null); + + return verifyableServices.SelectMany(sr => + ValidateConstructor(serviceMap, sr, container.ConstructorSelector.Execute(sr.ImplementingType))); + } + + private static IReadOnlyCollection ValidateConstructor(ServiceMap serviceMap, + ServiceRegistration serviceRegistration, ConstructorInfo constructorInfo) + { + var result = new Collection(); + + foreach (var parameter in constructorInfo.GetParameters()) + { + var validationTarget = new ValidationTarget(serviceRegistration, parameter); + Validate(validationTarget, serviceMap, result); + } + return result; + } + + private static void Validate(ValidationTarget validationTarget, ServiceMap serviceMap, ICollection result) + { + var registration = GetServiceRegistration(serviceMap, validationTarget); + if (registration == null) + { + if (validationTarget.ServiceType.IsFunc() || validationTarget.ServiceType.IsLazy()) + { + var serviceType = validationTarget.ServiceType.GenericTypeArguments[0]; + var underlyingvalidationTarget = validationTarget.WithServiceDescription(serviceType, string.Empty); + registration = GetServiceRegistration(serviceMap, underlyingvalidationTarget); + + if (registration != null) + { + return; + } + + if (serviceMap.ContainsAmbiguousRegistrationFor(serviceType)) + { + result.Add(new ValidationResult("", ValidationSeverity.Ambiguous, underlyingvalidationTarget)); + } + else + { + string message = string.Format(MissingDeferredDependency, validationTarget.ServiceType, underlyingvalidationTarget.ServiceType); + result.Add(new ValidationResult(message, ValidationSeverity.MissingDependency, underlyingvalidationTarget)); + } + } + else if (validationTarget.ServiceType.IsGenericType && validationTarget.ServiceType.GetGenericTypeDefinition() == typeof(IEnumerable<>)) + { + var serviceType = validationTarget.ServiceType.GenericTypeArguments[0]; + var underlyingvalidationTarget = validationTarget.WithServiceDescription(serviceType, string.Empty); + var registrations = GetServiceRegistrations(serviceMap, underlyingvalidationTarget); + if (registrations.Any()) return; + + // strict: there has to be at least 1 + string message = string.Format(MissingDeferredDependency, validationTarget.ServiceType, underlyingvalidationTarget.ServiceType); + result.Add(new ValidationResult(message, ValidationSeverity.MissingDependency, underlyingvalidationTarget)); + } + else + { + if (serviceMap.ContainsAmbiguousRegistrationFor(validationTarget.ServiceType)) + { + result.Add(new ValidationResult("", ValidationSeverity.Ambiguous, validationTarget)); + } + else + { + result.Add(new ValidationResult("", ValidationSeverity.MissingDependency, validationTarget)); + } + } + } + else + { + ValidateDisposable(validationTarget, result, registration); + ValidateLifetime(validationTarget, registration, result); + } + } + + private static void ValidateDisposable(ValidationTarget validationTarget, ICollection result, + ServiceRegistration registration) + { + if (registration.ServiceType.Implements()) + { + var message = string.Format(NotDisposeMessageServiceType, registration.ServiceType, + validationTarget.DeclaringService.ImplementingType); + result.Add(new ValidationResult(message, ValidationSeverity.NotDisposed, validationTarget)); + } + + else if (registration.ImplementingType != null && registration.ImplementingType.Implements()) + { + var message = string.Format(NotDisposeMessageImplementingType, registration.ImplementingType, + registration.ServiceType, + validationTarget.DeclaringService.ImplementingType); + result.Add(new ValidationResult(message, ValidationSeverity.NotDisposed, validationTarget)); + } + } + + + private static void ValidateLifetime(ValidationTarget validationTarget, ServiceRegistration dependencyRegistration, ICollection result) + { + if (GetLifespan(validationTarget.DeclaringService.Lifetime) > GetLifespan(dependencyRegistration.Lifetime)) + { + var message = string.Format(CaptiveDependency, dependencyRegistration.ServiceType, + validationTarget.DeclaringService.ServiceType, GetLifetimeName(dependencyRegistration.Lifetime), + GetLifetimeName(validationTarget.DeclaringService.Lifetime)); + result.Add(new ValidationResult(message, ValidationSeverity.Captive, validationTarget)); + } + } + + public static void SetLifespan(int lifeSpan) where TLifetime : ILifetime + { + LifeSpans.TryAdd(typeof(TLifetime), lifeSpan); + } + + private static IEnumerable GetServiceRegistrations(ServiceMap serviceMap, ValidationTarget validationTarget) + { + return serviceMap.Where(x => validationTarget.ServiceType.IsAssignableFrom(x.Key)).SelectMany(x => x.Value.Values); + } + + private static ServiceRegistration GetServiceRegistration(ServiceMap serviceMap, ValidationTarget validationTarget) + { + if (!serviceMap.TryGetValue(validationTarget.ServiceType, out var registrations)) + { + return null; + } + + if (registrations.TryGetValue(string.Empty, out var registration)) + { + return registration; + } + + if (registrations.Count == 1) + { + return registrations.Values.First(); + } + + if (registrations.TryGetValue(validationTarget.ServiceName, out registration)) + { + return registration; + } + + return null; + } + + private static string GetLifetimeName(ILifetime lifetime) + { + if (lifetime == null) + { + return "Transient"; + } + return lifetime.GetType().Name; + } + + private static int GetLifespan(ILifetime lifetime) + { + if (lifetime == null) + { + return 0; + } + if (LifeSpans.TryGetValue(lifetime.GetType(), out var lifespan)) + { + return lifespan; + } + return 0; + } + } + + + public class ValidationTarget + { + public ServiceRegistration DeclaringService { get; } + public ParameterInfo Parameter { get; } + public Type ServiceType { get; } + public string ServiceName { get; } + + + public ValidationTarget(ServiceRegistration declaringRegistration, ParameterInfo parameter) : this(declaringRegistration, parameter, parameter.ParameterType, string.Empty) + { + } + + + public ValidationTarget(ServiceRegistration declaringService, ParameterInfo parameter, Type serviceType, string serviceName) + { + DeclaringService = declaringService; + Parameter = parameter; + ServiceType = serviceType; + ServiceName = serviceName; + + + if (serviceType.GetTypeInfo().IsGenericType && serviceType.GetTypeInfo().ContainsGenericParameters) + { + ServiceType = serviceType.GetGenericTypeDefinition(); + } + + } + + public ValidationTarget WithServiceDescription(Type serviceType, string serviceName) + { + return new ValidationTarget(DeclaringService, Parameter, serviceType, serviceName); + } + + } + + + + + + public class ValidationResult + { + public ValidationResult(string message, ValidationSeverity severity, ValidationTarget validationTarget) + { + Message = message; + Severity = severity; + ValidationTarget = validationTarget; + } + + public string Message { get; } + + public ValidationSeverity Severity { get; } + public ValidationTarget ValidationTarget { get; } + } + + public enum ValidationSeverity + { + NoIssues, + Captive, + NotDisposed, + MissingDependency, + Ambiguous + } + + internal static class TypeExtensions + { + public static bool Implements(this Type type) + { + return type.GetTypeInfo().ImplementedInterfaces.Contains(typeof(TBaseType)); + } + + public static bool IsFunc(this Type type) + { + var typeInfo = type.GetTypeInfo(); + return typeInfo.IsGenericType && typeInfo.GetGenericTypeDefinition() == typeof(Func<>); + } + + public static bool IsLazy(this Type type) + { + var typeInfo = type.GetTypeInfo(); + return typeInfo.IsGenericType && typeInfo.GetGenericTypeDefinition() == typeof(Lazy<>); + } + } + + internal static class ServiceMapExtensions + { + public static bool ContainsAmbiguousRegistrationFor(this ServiceMap serviceMap, Type serviceType) + { + if (!serviceMap.TryGetValue(serviceType, out var registrations)) + { + return false; + } + return registrations.Count > 1; + } + } +} diff --git a/src/Umbraco.Tests/Composing/PackageActionCollectionTests.cs b/src/Umbraco.Tests/Composing/PackageActionCollectionTests.cs index e2145f557a..d100713102 100644 --- a/src/Umbraco.Tests/Composing/PackageActionCollectionTests.cs +++ b/src/Umbraco.Tests/Composing/PackageActionCollectionTests.cs @@ -1,10 +1,14 @@ using System; using System.Linq; using System.Xml; -using LightInject; +using Moq; using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Components; using Umbraco.Core.Composing; +using Umbraco.Core.Logging; using Umbraco.Core._Legacy.PackageActions; +using Umbraco.Tests.Components; namespace Umbraco.Tests.Composing { @@ -14,18 +18,21 @@ namespace Umbraco.Tests.Composing [Test] public void PackageActionCollectionBuilderWorks() { - var container = new ServiceContainer(); - container.ConfigureUmbracoCore(); + var container = RegisterFactory.Create(); + + var composition = new Composition(container, new TypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); - container.RegisterCollectionBuilder() + composition.WithCollectionBuilder() .Add(() => TypeLoader.GetPackageActions()); + Current.Factory = composition.CreateFactory(); + var actions = Current.PackageActions; Assert.AreEqual(2, actions.Count()); // order is unspecified, but both must be there - bool hasAction1 = actions.ElementAt(0) is PackageAction1 || actions.ElementAt(1) is PackageAction1; - bool hasAction2 = actions.ElementAt(0) is PackageAction2 || actions.ElementAt(1) is PackageAction2; + var hasAction1 = actions.ElementAt(0) is PackageAction1 || actions.ElementAt(1) is PackageAction1; + var hasAction2 = actions.ElementAt(0) is PackageAction2 || actions.ElementAt(1) is PackageAction2; Assert.IsTrue(hasAction1); Assert.IsTrue(hasAction2); } diff --git a/src/Umbraco.Tests/Composing/TypeFinderTests.cs b/src/Umbraco.Tests/Composing/TypeFinderTests.cs index 9b52546dff..2b9474310b 100644 --- a/src/Umbraco.Tests/Composing/TypeFinderTests.cs +++ b/src/Umbraco.Tests/Composing/TypeFinderTests.cs @@ -93,7 +93,7 @@ namespace Umbraco.Tests.Composing Assert.AreEqual(21, typesFound.Count()); // + classes in Umbraco.Web are marked with [Tree] } - private static ProfilingLogger GetTestProfilingLogger() + private static IProfilingLogger GetTestProfilingLogger() { var logger = new DebugDiagnosticsLogger(); var profiler = new TestProfiler(); diff --git a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs index 75edcd6404..5148c7eb1b 100644 --- a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs +++ b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs @@ -9,6 +9,7 @@ using umbraco; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; @@ -27,7 +28,7 @@ namespace Umbraco.Tests.Composing public void Initialize() { // this ensures it's reset - _typeLoader = new TypeLoader(NullCacheProvider.Instance, SettingsForTests.GenerateMockGlobalSettings(), new ProfilingLogger(Mock.Of(), Mock.Of())); + _typeLoader = new TypeLoader(NullCacheProvider.Instance, LocalTempStorage.Default, new ProfilingLogger(Mock.Of(), Mock.Of())); foreach (var file in Directory.GetFiles(IOHelper.MapPath("~/App_Data/TEMP/TypesCache"))) File.Delete(file); diff --git a/src/Umbraco.Tests/Configurations/GlobalSettingsTests.cs b/src/Umbraco.Tests/Configurations/GlobalSettingsTests.cs index 34fb2add8b..8587a7b194 100644 --- a/src/Umbraco.Tests/Configurations/GlobalSettingsTests.cs +++ b/src/Umbraco.Tests/Configurations/GlobalSettingsTests.cs @@ -2,6 +2,8 @@ using System.Web.Routing; using Moq; using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.IO; using Umbraco.Tests.TestHelpers; @@ -45,14 +47,13 @@ namespace Umbraco.Tests.Configurations [TestCase("~/some-wacky/nestedPath", "/MyVirtualDir/NestedVDir/", "some-wacky-nestedpath")] public void Umbraco_Mvc_Area(string path, string rootPath, string outcome) { - var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettingsMock = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettingsMock.Setup(x => x.Path).Returns(IOHelper.ResolveUrl(path)); - SettingsForTests.ConfigureSettings(globalSettingsMock.Object); SystemDirectories.Root = rootPath; - Assert.AreEqual(outcome, UmbracoConfig.For.GlobalSettings().GetUmbracoMvcArea()); + Assert.AreEqual(outcome, Current.Configs.Global().GetUmbracoMvcArea()); } - + [TestCase("/umbraco/editContent.aspx")] [TestCase("/install/default.aspx")] [TestCase("/install/")] @@ -92,10 +93,9 @@ namespace Umbraco.Tests.Configurations public void Is_Reserved_By_Route(string url, bool shouldMatch) { //reset the app config, we only want to test routes not the hard coded paths - var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettingsMock = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettingsMock.Setup(x => x.ReservedPaths).Returns(""); globalSettingsMock.Setup(x => x.ReservedUrls).Returns(""); - SettingsForTests.ConfigureSettings(globalSettingsMock.Object); var routes = new RouteCollection(); diff --git a/src/Umbraco.Tests/CoreThings/TryConvertToTests.cs b/src/Umbraco.Tests/CoreThings/TryConvertToTests.cs index 2e187d85a5..120a7f40c9 100644 --- a/src/Umbraco.Tests/CoreThings/TryConvertToTests.cs +++ b/src/Umbraco.Tests/CoreThings/TryConvertToTests.cs @@ -1,9 +1,9 @@ using System; using NUnit.Framework; using Umbraco.Core; -using Umbraco.Core.Strings; -using Umbraco.Tests.TestHelpers; using Umbraco.Core.Composing; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Strings; using Umbraco.Tests.Testing; namespace Umbraco.Tests.CoreThings @@ -11,14 +11,11 @@ namespace Umbraco.Tests.CoreThings [TestFixture] public class TryConvertToTests : UmbracoTestBase { - public override void SetUp() + protected void Compose() { - base.SetUp(); + base.Compose(); - var settings = SettingsForTests.GetDefaultUmbracoSettings(); - - // fixme - base should do it! - Container.RegisterSingleton(_ => new DefaultShortStringHelper(settings)); + Composition.RegisterUnique(f => new DefaultShortStringHelper(f.GetInstance())); } [Test] diff --git a/src/Umbraco.Tests/CoreThings/UdiTests.cs b/src/Umbraco.Tests/CoreThings/UdiTests.cs index 62aa56bd14..2b4ace8810 100644 --- a/src/Umbraco.Tests/CoreThings/UdiTests.cs +++ b/src/Umbraco.Tests/CoreThings/UdiTests.cs @@ -2,13 +2,13 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; -using LightInject; using Moq; using Newtonsoft.Json; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; using Umbraco.Core.Deploy; using Umbraco.Core.Logging; using Umbraco.Core.Serialization; @@ -23,11 +23,11 @@ namespace Umbraco.Tests.CoreThings public void SetUp() { // fixme - bad in a unit test - but Udi has a static ctor that wants it?! - var container = new Mock(); + var container = new Mock(); var globalSettings = SettingsForTests.GenerateMockGlobalSettings(); - container.Setup(x => x.GetInstance(typeof (TypeLoader))).Returns( - new TypeLoader(NullCacheProvider.Instance, globalSettings, new ProfilingLogger(Mock.Of(), Mock.Of()))); - Current.Container = container.Object; + container.Setup(x => x.GetInstance(typeof(TypeLoader))).Returns( + new TypeLoader(NullCacheProvider.Instance, LocalTempStorage.Default, new ProfilingLogger(Mock.Of(), Mock.Of()))); + Current.Factory = container.Object; Udi.ResetUdiTypes(); } diff --git a/src/Umbraco.Tests/FrontEnd/UmbracoHelperTests.cs b/src/Umbraco.Tests/FrontEnd/UmbracoHelperTests.cs index 182f1a98f5..7508395c64 100644 --- a/src/Umbraco.Tests/FrontEnd/UmbracoHelperTests.cs +++ b/src/Umbraco.Tests/FrontEnd/UmbracoHelperTests.cs @@ -1,13 +1,13 @@ using System; using System.Collections.Generic; using System.Text; -using LightInject; using Moq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Tests.TestHelpers; using Umbraco.Core.Cache; using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Web; @@ -407,19 +407,19 @@ namespace Umbraco.Tests.FrontEnd private void SetUpDependencyContainer() { // fixme - bad in a unit test - but Udi has a static ctor that wants it?! - var container = new Mock(); + var container = new Mock(); var globalSettings = SettingsForTests.GenerateMockGlobalSettings(); container .Setup(x => x.GetInstance(typeof(TypeLoader))) .Returns(new TypeLoader( NullCacheProvider.Instance, - globalSettings, + LocalTempStorage.Default, new ProfilingLogger(Mock.Of(), Mock.Of()) ) ); - Current.Container = container.Object; + Current.Factory = container.Object; } } } diff --git a/src/Umbraco.Tests/IO/FileSystemsTests.cs b/src/Umbraco.Tests/IO/FileSystemsTests.cs index 49b8af6d18..52de1bbcfa 100644 --- a/src/Umbraco.Tests/IO/FileSystemsTests.cs +++ b/src/Umbraco.Tests/IO/FileSystemsTests.cs @@ -1,16 +1,18 @@ using System; using System.IO; using System.Text; -using LightInject; using Moq; using NUnit.Framework; using Umbraco.Core; +using Umbraco.Core.Components; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Composing; +using Umbraco.Core.Composing.Composers; using Umbraco.Core.IO; using Umbraco.Core.IO.MediaPathSchemes; using Umbraco.Core.Logging; using Umbraco.Core.Services; +using Umbraco.Tests.Components; using Umbraco.Tests.TestHelpers; namespace Umbraco.Tests.IO @@ -18,22 +20,32 @@ namespace Umbraco.Tests.IO [TestFixture] public class FileSystemsTests { - private ServiceContainer _container; + private IRegister _register; + private IFactory _factory; [SetUp] public void Setup() { - //init the config singleton - var config = SettingsForTests.GetDefaultUmbracoSettings(); - SettingsForTests.ConfigureSettings(config); + _register = RegisterFactory.Create(); - _container = new ServiceContainer(); - _container.ConfigureUmbracoCore(); - _container.Register(_ => Mock.Of()); - _container.Register(); - _container.Register(_ => Mock.Of()); - _container.Register(_ => Mock.Of()); - _container.RegisterSingleton(); + var composition = new Composition(_register, new TypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); + + composition.Register(_ => Mock.Of()); + composition.Register(_ => Mock.Of()); + composition.Register(_ => Mock.Of()); + composition.RegisterUnique(); + + composition.Configs.Add(SettingsForTests.GetDefaultGlobalSettings); + composition.Configs.Add(SettingsForTests.GetDefaultUmbracoSettings); + + composition.ComposeFileSystems(); + + composition.Configs.Add(SettingsForTests.GetDefaultUmbracoSettings); + + _factory = composition.CreateFactory(); + + Current.Reset(); + Current.Factory = _factory; // make sure we start clean // because some tests will create corrupt or weird filesystems @@ -47,31 +59,47 @@ namespace Umbraco.Tests.IO FileSystems.Reset(); Current.Reset(); - _container.Dispose(); + _register.DisposeIfDisposable(); } - private FileSystems FileSystems => _container.GetInstance(); + private FileSystems FileSystems => _factory.GetInstance(); [Test] - public void Can_Get_Base_File_System() + public void Can_Get_MediaFileSystem() { - var fileSystem = FileSystems.GetUnderlyingFileSystemProvider("media"); - + var fileSystem = _factory.GetInstance(); Assert.NotNull(fileSystem); } [Test] - public void Can_Get_Typed_File_System() + public void Can_Get_IMediaFileSystem() { - var fileSystem = FileSystems.GetFileSystemProvider(); - + var fileSystem = _factory.GetInstance(); Assert.NotNull(fileSystem); } [Test] - public void Media_Fs_Safe_Delete() + public void IMediaFileSystem_Is_Singleton() { - var fs = FileSystems.GetFileSystemProvider(); + var fileSystem1 = _factory.GetInstance(); + var fileSystem2 = _factory.GetInstance(); + Assert.AreSame(fileSystem1, fileSystem2); + } + + [Test] + public void Can_Unwrap_MediaFileSystem() + { + var fileSystem = _factory.GetInstance(); + var unwrapped = fileSystem.Unwrap(); + Assert.IsNotNull(unwrapped); + var physical = unwrapped as PhysicalFileSystem; + Assert.IsNotNull(physical); + } + + [Test] + public void Can_Delete_MediaFiles() + { + var fs = _factory.GetInstance(); var ms = new MemoryStream(Encoding.UTF8.GetBytes("test")); var virtPath = fs.GetMediaPath("file.txt", Guid.NewGuid(), Guid.NewGuid()); fs.AddFile(virtPath, ms); @@ -93,52 +121,42 @@ namespace Umbraco.Tests.IO Assert.IsTrue(Directory.Exists(physPath)); } - public void Singleton_Typed_File_System() - { - var fs1 = FileSystems.GetFileSystemProvider(); - var fs2 = FileSystems.GetFileSystemProvider(); - Assert.AreSame(fs1, fs2); + // fixme - don't make sense anymore + /* + [Test] + public void Cannot_Get_InvalidFileSystem() + { + // throws because InvalidTypedFileSystem does not have the proper attribute with an alias + Assert.Throws(() => FileSystems.GetFileSystem()); } [Test] - public void Exception_Thrown_On_Invalid_Typed_File_System() - { - Assert.Throws(() => FileSystems.GetFileSystemProvider()); - } - - [Test] - public void Exception_Thrown_On_NonConfigured_Typed_File_System() + public void Cannot_Get_NonConfiguredFileSystem() { // note: we need to reset the manager between tests else the Accept_Fallback test would corrupt that one - Assert.Throws(() => FileSystems.GetFileSystemProvider()); + // throws because NonConfiguredFileSystem has the proper attribute with an alias, + // but then the container cannot find an IFileSystem implementation for that alias + Assert.Throws(() => FileSystems.GetFileSystem()); + + // all we'd need to pass is to register something like: + //_container.Register("noconfig", factory => new PhysicalFileSystem("~/foo")); } - [Test] - public void Accept_Fallback_On_NonConfigured_Typed_File_System() + internal class InvalidFileSystem : FileSystemWrapper { - var fs = FileSystems.GetFileSystemProvider(() => new PhysicalFileSystem("~/App_Data/foo")); - - Assert.NotNull(fs); - } - - /// - /// Used in unit tests, for a typed file system we need to inherit from FileSystemWrapper and they MUST have a ctor - /// that only accepts a base IFileSystem object - /// - internal class InvalidTypedFileSystem : FileSystemWrapper - { - public InvalidTypedFileSystem(IFileSystem wrapped, string invalidParam) - : base(wrapped) + public InvalidFileSystem(IFileSystem innerFileSystem) + : base(innerFileSystem) { } } - [FileSystemProvider("noconfig")] - internal class NonConfiguredTypeFileSystem : FileSystemWrapper + [InnerFileSystem("noconfig")] + internal class NonConfiguredFileSystem : FileSystemWrapper { - public NonConfiguredTypeFileSystem(IFileSystem wrapped) - : base(wrapped) + public NonConfiguredFileSystem(IFileSystem innerFileSystem) + : base(innerFileSystem) { } } + */ } } diff --git a/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs b/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs index 90fdf3810d..262b026cae 100644 --- a/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs +++ b/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs @@ -1,10 +1,12 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using Moq; using NUnit.Framework; using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Scoping; @@ -25,7 +27,7 @@ namespace Umbraco.Tests.IO { SafeCallContext.Clear(); ClearFiles(); - ShadowFileSystems.ResetId(); + FileSystems.ResetShadowId(); } [TearDown] @@ -33,13 +35,13 @@ namespace Umbraco.Tests.IO { SafeCallContext.Clear(); ClearFiles(); - ShadowFileSystems.ResetId(); + FileSystems.ResetShadowId(); } private static void ClearFiles() { TestHelper.DeleteDirectory(IOHelper.MapPath("FileSysTests")); - TestHelper.DeleteDirectory(IOHelper.MapPath("App_Data")); + TestHelper.DeleteDirectory(IOHelper.MapPath("App_Data/TEMP/ShadowFs")); } private static string NormPath(string path) @@ -373,74 +375,92 @@ namespace Umbraco.Tests.IO Assert.IsFalse(File.Exists(path + "/ShadowTests/sub/sub/f2.txt")); } + class FS : FileSystemWrapper + { + public FS(IFileSystem innerFileSystem) + : base(innerFileSystem) + { } + } + [Test] public void ShadowScopeComplete() { var logger = Mock.Of(); var path = IOHelper.MapPath("FileSysTests"); - var appdata = IOHelper.MapPath("App_Data"); + var shadowfs = IOHelper.MapPath("App_Data/TEMP/ShadowFs"); Directory.CreateDirectory(path); - Directory.CreateDirectory(appdata); + Directory.CreateDirectory(shadowfs); var scopedFileSystems = false; - var fs = new PhysicalFileSystem(path, "ignore"); - var sw = new ShadowWrapper(fs, "shadow", () => scopedFileSystems); - var swa = new[] { sw }; + var phy = new PhysicalFileSystem(path, "ignore"); + + var container = Mock.Of(); + var fileSystems = new FileSystems(container, logger) { IsScoped = () => scopedFileSystems }; + var fs = fileSystems.GetFileSystem(phy); + var sw = (ShadowWrapper) fs.InnerFileSystem; using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) sw.AddFile("sub/f1.txt", ms); - Assert.IsTrue(fs.FileExists("sub/f1.txt")); + Assert.IsTrue(phy.FileExists("sub/f1.txt")); Guid id; // explicit shadow without scope does not work sw.Shadow(id = Guid.NewGuid()); - Assert.IsTrue(Directory.Exists(appdata + "/TEMP/ShadowFs/" + id)); + Assert.IsTrue(Directory.Exists(shadowfs + "/" + id)); using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) sw.AddFile("sub/f2.txt", ms); - Assert.IsTrue(fs.FileExists("sub/f2.txt")); + Assert.IsTrue(phy.FileExists("sub/f2.txt")); sw.UnShadow(true); - Assert.IsTrue(fs.FileExists("sub/f2.txt")); - Assert.IsFalse(Directory.Exists(appdata + "/TEMP/ShadowFs/" + id)); + Assert.IsTrue(phy.FileExists("sub/f2.txt")); + Assert.IsFalse(Directory.Exists(shadowfs + "/" + id)); // shadow with scope but no complete does not complete scopedFileSystems = true; // pretend we have a scope - var scope = new ShadowFileSystems(id = Guid.NewGuid(), swa, logger); - Assert.IsTrue(Directory.Exists(appdata + "/TEMP/ShadowFs/" + id)); + var scope = new ShadowFileSystems(fileSystems, id = Guid.NewGuid()); + Assert.IsTrue(Directory.Exists(shadowfs + "/" + id)); using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) sw.AddFile("sub/f3.txt", ms); - Assert.IsFalse(fs.FileExists("sub/f3.txt")); - Assert.AreEqual(1, Directory.GetDirectories(appdata + "/TEMP/ShadowFs").Length); + Assert.IsFalse(phy.FileExists("sub/f3.txt")); + var dirs = Directory.GetDirectories(shadowfs); + Assert.AreEqual(1, dirs.Length); + Assert.AreEqual((shadowfs + "/" + id).Replace('\\', '/'), dirs[0].Replace('\\', '/')); + dirs = Directory.GetDirectories(dirs[0]); + var typedDir = dirs.FirstOrDefault(x => x.Replace('\\', '/').EndsWith("/typed")); + Assert.IsNotNull(typedDir); + dirs = Directory.GetDirectories(typedDir); + var scopedDir = dirs.FirstOrDefault(x => x.Replace('\\', '/').EndsWith("/Umbraco.Tests.IO.ShadowFileSystemTests+FS")); // this is where files go + Assert.IsNotNull(scopedDir); scope.Dispose(); scopedFileSystems = false; - Assert.IsFalse(fs.FileExists("sub/f3.txt")); - TestHelper.TryAssert(() => Assert.IsFalse(Directory.Exists(appdata + "/TEMP/ShadowFs/" + id))); + Assert.IsFalse(phy.FileExists("sub/f3.txt")); + TestHelper.TryAssert(() => Assert.IsFalse(Directory.Exists(shadowfs + "/" + id))); // shadow with scope and complete does complete scopedFileSystems = true; // pretend we have a scope - scope = new ShadowFileSystems(id = Guid.NewGuid(), swa, logger); - Assert.IsTrue(Directory.Exists(appdata + "/TEMP/ShadowFs/" + id)); + scope = new ShadowFileSystems(fileSystems, id = Guid.NewGuid()); + Assert.IsTrue(Directory.Exists(shadowfs + "/" + id)); using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) sw.AddFile("sub/f4.txt", ms); - Assert.IsFalse(fs.FileExists("sub/f4.txt")); - Assert.AreEqual(1, Directory.GetDirectories(appdata + "/TEMP/ShadowFs").Length); + Assert.IsFalse(phy.FileExists("sub/f4.txt")); + Assert.AreEqual(1, Directory.GetDirectories(shadowfs).Length); scope.Complete(); scope.Dispose(); scopedFileSystems = false; - TestHelper.TryAssert(() => Assert.AreEqual(0, Directory.GetDirectories(appdata + "/TEMP/ShadowFs").Length)); - Assert.IsTrue(fs.FileExists("sub/f4.txt")); - Assert.IsFalse(Directory.Exists(appdata + "/TEMP/ShadowFs/" + id)); + TestHelper.TryAssert(() => Assert.AreEqual(0, Directory.GetDirectories(shadowfs).Length)); + Assert.IsTrue(phy.FileExists("sub/f4.txt")); + Assert.IsFalse(Directory.Exists(shadowfs + "/" + id)); // test scope for "another thread" scopedFileSystems = true; // pretend we have a scope - scope = new ShadowFileSystems(id = Guid.NewGuid(), swa, logger); - Assert.IsTrue(Directory.Exists(appdata + "/TEMP/ShadowFs/" + id)); + scope = new ShadowFileSystems(fileSystems, id = Guid.NewGuid()); + Assert.IsTrue(Directory.Exists(shadowfs + "/" + id)); using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) sw.AddFile("sub/f5.txt", ms); - Assert.IsFalse(fs.FileExists("sub/f5.txt")); + Assert.IsFalse(phy.FileExists("sub/f5.txt")); // pretend we're another thread w/out scope scopedFileSystems = false; @@ -448,12 +468,12 @@ namespace Umbraco.Tests.IO sw.AddFile("sub/f6.txt", ms); scopedFileSystems = true; // pretend we have a scope - Assert.IsTrue(fs.FileExists("sub/f6.txt")); // other thread has written out to fs + Assert.IsTrue(phy.FileExists("sub/f6.txt")); // other thread has written out to fs scope.Complete(); scope.Dispose(); scopedFileSystems = false; - Assert.IsTrue(fs.FileExists("sub/f5.txt")); - Assert.IsFalse(Directory.Exists(appdata + "/TEMP/ShadowFs/" + id)); + Assert.IsTrue(phy.FileExists("sub/f5.txt")); + Assert.IsFalse(Directory.Exists(shadowfs + "/" + id)); } [Test] @@ -462,27 +482,30 @@ namespace Umbraco.Tests.IO var logger = Mock.Of(); var path = IOHelper.MapPath("FileSysTests"); - var appdata = IOHelper.MapPath("App_Data"); + var shadowfs = IOHelper.MapPath("App_Data/TEMP/ShadowFs"); Directory.CreateDirectory(path); var scopedFileSystems = false; - var fs = new PhysicalFileSystem(path, "ignore"); - var sw = new ShadowWrapper(fs, "shadow", () => scopedFileSystems); - var swa = new[] { sw }; + var phy = new PhysicalFileSystem(path, "ignore"); + + var container = Mock.Of(); + var fileSystems = new FileSystems(container, logger) { IsScoped = () => scopedFileSystems }; + var fs = fileSystems.GetFileSystem( phy); + var sw = (ShadowWrapper) fs.InnerFileSystem; using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) sw.AddFile("sub/f1.txt", ms); - Assert.IsTrue(fs.FileExists("sub/f1.txt")); + Assert.IsTrue(phy.FileExists("sub/f1.txt")); Guid id; scopedFileSystems = true; // pretend we have a scope - var scope = new ShadowFileSystems(id = Guid.NewGuid(), swa, logger); - Assert.IsTrue(Directory.Exists(appdata + "/TEMP/ShadowFs/" + id)); + var scope = new ShadowFileSystems(fileSystems, id = Guid.NewGuid()); + Assert.IsTrue(Directory.Exists(shadowfs + "/" + id)); using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) sw.AddFile("sub/f2.txt", ms); - Assert.IsFalse(fs.FileExists("sub/f2.txt")); + Assert.IsFalse(phy.FileExists("sub/f2.txt")); // pretend we're another thread w/out scope scopedFileSystems = false; @@ -490,15 +513,15 @@ namespace Umbraco.Tests.IO sw.AddFile("sub/f2.txt", ms); scopedFileSystems = true; // pretend we have a scope - Assert.IsTrue(fs.FileExists("sub/f2.txt")); // other thread has written out to fs + Assert.IsTrue(phy.FileExists("sub/f2.txt")); // other thread has written out to fs scope.Complete(); scope.Dispose(); scopedFileSystems = false; - Assert.IsTrue(fs.FileExists("sub/f2.txt")); - TestHelper.TryAssert(() => Assert.IsFalse(Directory.Exists(appdata + "/TEMP/ShadowFs/" + id))); + Assert.IsTrue(phy.FileExists("sub/f2.txt")); + TestHelper.TryAssert(() => Assert.IsFalse(Directory.Exists(shadowfs + "/" + id))); string text; - using (var s = fs.OpenFile("sub/f2.txt")) + using (var s = phy.OpenFile("sub/f2.txt")) using (var r = new StreamReader(s)) text = r.ReadToEnd(); @@ -512,27 +535,30 @@ namespace Umbraco.Tests.IO var logger = Mock.Of(); var path = IOHelper.MapPath("FileSysTests"); - var appdata = IOHelper.MapPath("App_Data"); + var shadowfs = IOHelper.MapPath("App_Data/TEMP/ShadowFs"); Directory.CreateDirectory(path); var scopedFileSystems = false; - var fs = new PhysicalFileSystem(path, "ignore"); - var sw = new ShadowWrapper(fs, "shadow", () => scopedFileSystems); - var swa = new[] { sw }; + var phy = new PhysicalFileSystem(path, "ignore"); + + var container = Mock.Of(); + var fileSystems = new FileSystems(container, logger) { IsScoped = () => scopedFileSystems }; + var fs = fileSystems.GetFileSystem( phy); + var sw = (ShadowWrapper)fs.InnerFileSystem; using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) sw.AddFile("sub/f1.txt", ms); - Assert.IsTrue(fs.FileExists("sub/f1.txt")); + Assert.IsTrue(phy.FileExists("sub/f1.txt")); Guid id; scopedFileSystems = true; // pretend we have a scope - var scope = new ShadowFileSystems(id = Guid.NewGuid(), swa, logger); - Assert.IsTrue(Directory.Exists(appdata + "/TEMP/ShadowFs/" + id)); + var scope = new ShadowFileSystems(fileSystems, id = Guid.NewGuid()); + Assert.IsTrue(Directory.Exists(shadowfs + "/" + id)); using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) sw.AddFile("sub/f2.txt", ms); - Assert.IsFalse(fs.FileExists("sub/f2.txt")); + Assert.IsFalse(phy.FileExists("sub/f2.txt")); // pretend we're another thread w/out scope scopedFileSystems = false; @@ -540,11 +566,11 @@ namespace Umbraco.Tests.IO sw.AddFile("sub/f2.txt/f2.txt", ms); scopedFileSystems = true; // pretend we have a scope - Assert.IsTrue(fs.FileExists("sub/f2.txt/f2.txt")); // other thread has written out to fs + Assert.IsTrue(phy.FileExists("sub/f2.txt/f2.txt")); // other thread has written out to fs using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) sw.AddFile("sub/f3.txt", ms); - Assert.IsFalse(fs.FileExists("sub/f3.txt")); + Assert.IsFalse(phy.FileExists("sub/f3.txt")); scope.Complete(); @@ -570,7 +596,7 @@ namespace Umbraco.Tests.IO } // still, the rest of the changes has been applied ok - Assert.IsTrue(fs.FileExists("sub/f3.txt")); + Assert.IsTrue(phy.FileExists("sub/f3.txt")); } [Test] diff --git a/src/Umbraco.Tests/Integration/ContentEventsTests.cs b/src/Umbraco.Tests/Integration/ContentEventsTests.cs index 2aee582acc..7b22d282f0 100644 --- a/src/Umbraco.Tests/Integration/ContentEventsTests.cs +++ b/src/Umbraco.Tests/Integration/ContentEventsTests.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using LightInject; using Moq; using NUnit.Framework; using Umbraco.Core; @@ -20,6 +19,7 @@ using static Umbraco.Tests.Cache.DistributedCache.DistributedCacheTests; namespace Umbraco.Tests.Integration { [TestFixture] + [Category("Slow")] [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] public class ContentEventsTests : TestWithSomeContentBase { @@ -50,10 +50,10 @@ namespace Umbraco.Tests.Integration { base.Compose(); - Container.Register(_ => new TestServerRegistrar()); // localhost-only - Container.Register(new PerContainerLifetime()); + Composition.Register(_ => new TestServerRegistrar()); // localhost-only + Composition.RegisterUnique(); - Container.RegisterCollectionBuilder() + Composition.WithCollectionBuilder() .Add() .Add() .Add(); diff --git a/src/Umbraco.Tests/Issues/U9560.cs b/src/Umbraco.Tests/Issues/U9560.cs index c5748e21e0..e422cbc86b 100644 --- a/src/Umbraco.Tests/Issues/U9560.cs +++ b/src/Umbraco.Tests/Issues/U9560.cs @@ -3,7 +3,8 @@ using NUnit.Framework; using Umbraco.Core.Models; using Umbraco.Core.Persistence; using Umbraco.Tests.Testing; -using LightInject; +using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Tests.TestHelpers; namespace Umbraco.Tests.Issues @@ -26,7 +27,7 @@ namespace Umbraco.Tests.Issues var aliasName = string.Empty; // read fields, same as what we do with PetaPoco Fetch - using (var db = Container.GetInstance().CreateDatabase()) + using (var db = Factory.GetInstance().CreateDatabase()) { db.OpenSharedConnection(); try @@ -54,7 +55,7 @@ namespace Umbraco.Tests.Issues Assert.AreEqual("Alias", aliasName); // try differently - using (var db = Container.GetInstance().CreateDatabase()) + using (var db = Factory.GetInstance().CreateDatabase()) { db.OpenSharedConnection(); try diff --git a/src/Umbraco.Tests/Macros/MacroTests.cs b/src/Umbraco.Tests/Macros/MacroTests.cs index 2cb7b1aa4e..984fb94fab 100644 --- a/src/Umbraco.Tests/Macros/MacroTests.cs +++ b/src/Umbraco.Tests/Macros/MacroTests.cs @@ -4,7 +4,9 @@ using System.Web.UI.WebControls; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Cache; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Models; using Umbraco.Tests.TestHelpers; using Umbraco.Web.Macros; @@ -26,7 +28,9 @@ namespace Umbraco.Tests.Macros new IsolatedRuntimeCache(type => new ObjectCacheRuntimeCacheProvider())); //Current.ApplicationContext = new ApplicationContext(cacheHelper, new ProfilingLogger(Mock.Of(), Mock.Of())); - UmbracoConfig.For.SetUmbracoSettings(SettingsForTests.GetDefaultUmbracoSettings()); + Current.Reset(); + Current.UnlockConfigs(); + Current.Configs.Add(SettingsForTests.GetDefaultUmbracoSettings); } [TestCase("123", "IntProp", typeof(int))] diff --git a/src/Umbraco.Tests/Manifest/ManifestParserTests.cs b/src/Umbraco.Tests/Manifest/ManifestParserTests.cs index 19aaf79581..ce3d1d705c 100644 --- a/src/Umbraco.Tests/Manifest/ManifestParserTests.cs +++ b/src/Umbraco.Tests/Manifest/ManifestParserTests.cs @@ -2,7 +2,6 @@ using System.Linq; using Moq; using System.Text; -using LightInject; using NUnit.Framework; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -14,27 +13,25 @@ using Umbraco.Core.Manifest; using Umbraco.Core.PropertyEditors; using Umbraco.Core.PropertyEditors.Validators; using Umbraco.Core.Services; -using Umbraco.Web.ContentApps; namespace Umbraco.Tests.Manifest { [TestFixture] public class ManifestParserTests { - private ManifestParser _parser; [SetUp] public void Setup() { Current.Reset(); - var container = Mock.Of(); - Current.Container = container; + var factory = Mock.Of(); + Current.Factory = factory; - var serviceContext = new ServiceContext( + var serviceContext = ServiceContext.CreatePartial( localizedTextService: Mock.Of()); - Mock.Get(container) + Mock.Get(factory) .Setup(x => x.GetInstance(It.IsAny())) .Returns(x => { diff --git a/src/Umbraco.Tests/Membership/MembershipProviderBaseTests.cs b/src/Umbraco.Tests/Membership/MembershipProviderBaseTests.cs index 2a5b76e1bd..f1f46133c3 100644 --- a/src/Umbraco.Tests/Membership/MembershipProviderBaseTests.cs +++ b/src/Umbraco.Tests/Membership/MembershipProviderBaseTests.cs @@ -3,17 +3,9 @@ using System.Collections.Specialized; using System.Configuration.Provider; using System.Security.Cryptography; using System.Web.Security; -using LightInject; using Moq; using NUnit.Framework; -using Umbraco.Core; -using Umbraco.Core.Composing; -using Umbraco.Core.IO; -using Umbraco.Core.Logging; -using Umbraco.Core.Persistence; -using Umbraco.Core.Scoping; using Umbraco.Core.Security; -using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; namespace Umbraco.Tests.Membership diff --git a/src/Umbraco.Tests/Misc/ApplicationUrlHelperTests.cs b/src/Umbraco.Tests/Misc/ApplicationUrlHelperTests.cs index 9c0560351b..47c854333a 100644 --- a/src/Umbraco.Tests/Misc/ApplicationUrlHelperTests.cs +++ b/src/Umbraco.Tests/Misc/ApplicationUrlHelperTests.cs @@ -1,7 +1,4 @@ using System; -using System.Configuration; -using System.Linq; -using LightInject; using Moq; using NUnit.Framework; using Umbraco.Core; @@ -11,8 +8,6 @@ using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Sync; using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.Cache.DistributedCache; -using Umbraco.Tests.TestHelpers.Stubs; namespace Umbraco.Tests.Misc @@ -32,7 +27,7 @@ namespace Umbraco.Tests.Misc [Test] public void NoApplicationUrlByDefault() { - var state = new RuntimeState(Mock.Of(), new Lazy(Mock.Of), new Lazy(Mock.Of), Mock.Of(), Mock.Of()); + var state = new RuntimeState(Mock.Of(), Mock.Of(), Mock.Of(), new Lazy(), new Lazy()); Assert.IsNull(state.ApplicationUrl); } @@ -51,10 +46,7 @@ namespace Umbraco.Tests.Misc var registrar = new Mock(); registrar.Setup(x => x.GetCurrentServerUmbracoApplicationUrl()).Returns("http://server1.com/umbraco"); - var state = new RuntimeState( - Mock.Of(), - new Lazy(() => registrar.Object), - new Lazy(Mock.Of), settings, globalConfig.Object); + var state = new RuntimeState(Mock.Of(), settings, globalConfig.Object, new Lazy(), new Lazy(() => registrar.Object)); state.EnsureApplicationUrl(); @@ -75,9 +67,9 @@ namespace Umbraco.Tests.Misc ApplicationUrlHelper.ApplicationUrlProvider = request => "http://server1.com/umbraco"; - - var state = new RuntimeState(Mock.Of(), new Lazy(Mock.Of), new Lazy(Mock.Of), settings, globalConfig.Object); + + var state = new RuntimeState(Mock.Of(), settings, globalConfig.Object, new Lazy(), new Lazy(() => Mock.Of())); state.EnsureApplicationUrl(); @@ -101,7 +93,7 @@ namespace Umbraco.Tests.Misc // still NOT set Assert.IsNull(url); } - + [Test] public void SetApplicationUrlFromStSettingsNoSsl() { @@ -112,8 +104,8 @@ namespace Umbraco.Tests.Misc var globalConfig = Mock.Get(SettingsForTests.GenerateMockGlobalSettings()); globalConfig.Setup(x => x.UseHttps).Returns(false); - - + + var url = ApplicationUrlHelper.TryGetApplicationUrl(settings, Mock.Of(), globalConfig.Object, Mock.Of()); Assert.AreEqual("http://mycoolhost.com/umbraco", url); @@ -129,8 +121,8 @@ namespace Umbraco.Tests.Misc var globalConfig = Mock.Get(SettingsForTests.GenerateMockGlobalSettings()); globalConfig.Setup(x => x.UseHttps).Returns(true); - - + + var url = ApplicationUrlHelper.TryGetApplicationUrl(settings, Mock.Of(), globalConfig.Object, Mock.Of()); Assert.AreEqual("https://mycoolhost.com/umbraco", url); @@ -146,13 +138,11 @@ namespace Umbraco.Tests.Misc var globalConfig = Mock.Get(SettingsForTests.GenerateMockGlobalSettings()); globalConfig.Setup(x => x.UseHttps).Returns(true); - + var url = ApplicationUrlHelper.TryGetApplicationUrl(settings, Mock.Of(), globalConfig.Object, Mock.Of()); Assert.AreEqual("httpx://whatever.com/umbraco", url); } - - } } diff --git a/src/Umbraco.Tests/Misc/UriUtilityTests.cs b/src/Umbraco.Tests/Misc/UriUtilityTests.cs index 0bd9156f20..42c69d3967 100644 --- a/src/Umbraco.Tests/Misc/UriUtilityTests.cs +++ b/src/Umbraco.Tests/Misc/UriUtilityTests.cs @@ -90,7 +90,6 @@ namespace Umbraco.Tests.Misc var settings = SettingsForTests.GenerateMockUmbracoSettings(); var requestMock = Mock.Get(settings.RequestHandler); requestMock.Setup(x => x.AddTrailingSlash).Returns(trailingSlash); - SettingsForTests.ConfigureSettings(settings); UriUtility.SetAppDomainAppVirtualPath("/"); diff --git a/src/Umbraco.Tests/Models/ContentTests.cs b/src/Umbraco.Tests/Models/ContentTests.cs index bec1b15e90..56de6ce477 100644 --- a/src/Umbraco.Tests/Models/ContentTests.cs +++ b/src/Umbraco.Tests/Models/ContentTests.cs @@ -5,17 +5,16 @@ using System.Globalization; using System.Linq; using System.Threading; using Moq; -using NUnit.Framework; using Umbraco.Core; +using NUnit.Framework; using Umbraco.Core.Cache; +using Umbraco.Core.Composing.Composers; using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; using Umbraco.Core.Serialization; using Umbraco.Core.Services; -using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Tests.Testing; @@ -26,22 +25,15 @@ namespace Umbraco.Tests.Models [TestFixture] public class ContentTests : UmbracoTestBase { - public override void SetUp() - { - base.SetUp(); - - var config = SettingsForTests.GetDefaultUmbracoSettings(); - SettingsForTests.ConfigureSettings(config); - } - protected override void Compose() { base.Compose(); - Container.Register(_ => Mock.Of()); - Container.Register(); - Container.Register(_ => Mock.Of()); - Container.Register(_ => Mock.Of()); + Composition.Register(_ => Mock.Of()); + Composition.ComposeFileSystems(); + + Composition.Register(_ => Mock.Of()); + Composition.Register(_ => Mock.Of()); } [Test] @@ -208,7 +200,7 @@ namespace Umbraco.Tests.Models Assert.AreNotSame(content.Properties, clone.Properties); } - private static ProfilingLogger GetTestProfilingLogger() + private static IProfilingLogger GetTestProfilingLogger() { var logger = new DebugDiagnosticsLogger(); var profiler = new TestProfiler(); @@ -237,10 +229,7 @@ namespace Umbraco.Tests.Models content.ContentSchedule.Add(DateTime.Now, DateTime.Now.AddDays(1)); //content.ChangePublishedState(PublishedState.Published); content.SortOrder = 5; - content.Template = new Template((string) "Test Template", (string) "testTemplate") - { - Id = 88 - }; + content.TemplateId = 88; content.Trashed = false; content.UpdateDate = DateTime.Now; content.WriterId = 23; @@ -299,10 +288,7 @@ namespace Umbraco.Tests.Models content.Path = "-1,4,10"; content.ContentSchedule.Add(DateTime.Now, DateTime.Now.AddDays(1)); content.SortOrder = 5; - content.Template = new Template((string) "Test Template", (string) "testTemplate") - { - Id = 88 - }; + content.TemplateId = 88; content.Trashed = false; content.UpdateDate = DateTime.Now; content.WriterId = 23; @@ -329,8 +315,8 @@ namespace Umbraco.Tests.Models Assert.AreEqual(clone.PublishedState, content.PublishedState); Assert.AreEqual(clone.SortOrder, content.SortOrder); Assert.AreEqual(clone.PublishedState, content.PublishedState); - Assert.AreNotSame(clone.Template, content.Template); - Assert.AreEqual(clone.Template, content.Template); + Assert.AreNotSame(clone.TemplateId, content.TemplateId); + Assert.AreEqual(clone.TemplateId, content.TemplateId); Assert.AreEqual(clone.Trashed, content.Trashed); Assert.AreEqual(clone.UpdateDate, content.UpdateDate); Assert.AreEqual(clone.VersionId, content.VersionId); @@ -405,34 +391,29 @@ namespace Umbraco.Tests.Models content.Level = 3; content.Path = "-1,4,10"; content.SortOrder = 5; - content.Template = new Template((string)"Test Template", (string)"testTemplate") - { - Id = 88 - }; + content.TemplateId = 88; content.Trashed = true; content.UpdateDate = DateTime.Now; content.WriterId = 23; - content.Template.UpdateDate = DateTime.Now; //update a child object - // Act content.ResetDirtyProperties(); // Assert Assert.IsTrue(content.WasDirty()); - Assert.IsTrue(content.WasPropertyDirty("Id")); - Assert.IsTrue(content.WasPropertyDirty("CreateDate")); - Assert.IsTrue(content.WasPropertyDirty("CreatorId")); - Assert.IsTrue(content.WasPropertyDirty("Key")); - Assert.IsTrue(content.WasPropertyDirty("Level")); - Assert.IsTrue(content.WasPropertyDirty("Path")); - Assert.IsTrue(content.WasPropertyDirty("ContentSchedule")); - Assert.IsTrue(content.WasPropertyDirty("SortOrder")); - Assert.IsTrue(content.WasPropertyDirty("Template")); - Assert.IsTrue(content.WasPropertyDirty("Trashed")); - Assert.IsTrue(content.WasPropertyDirty("UpdateDate")); - Assert.IsTrue(content.WasPropertyDirty("WriterId")); + Assert.IsTrue(content.WasPropertyDirty(nameof(Content.Id))); + Assert.IsTrue(content.WasPropertyDirty(nameof(Content.CreateDate))); + Assert.IsTrue(content.WasPropertyDirty(nameof(Content.CreatorId))); + Assert.IsTrue(content.WasPropertyDirty(nameof(Content.Key))); + Assert.IsTrue(content.WasPropertyDirty(nameof(Content.Level))); + Assert.IsTrue(content.WasPropertyDirty(nameof(Content.Path))); + Assert.IsTrue(content.WasPropertyDirty(nameof(Content.ContentSchedule))); + Assert.IsTrue(content.WasPropertyDirty(nameof(Content.SortOrder))); + Assert.IsTrue(content.WasPropertyDirty(nameof(Content.TemplateId))); + Assert.IsTrue(content.WasPropertyDirty(nameof(Content.Trashed))); + Assert.IsTrue(content.WasPropertyDirty(nameof(Content.UpdateDate))); + Assert.IsTrue(content.WasPropertyDirty(nameof(Content.WriterId))); foreach (var prop in content.Properties) { Assert.IsTrue(prop.WasDirty()); @@ -452,8 +433,6 @@ namespace Umbraco.Tests.Models Assert.IsTrue(culture.Value.WasPropertyDirty("Name")); Assert.IsTrue(culture.Value.WasPropertyDirty("Date")); } - //verify child objects were reset too - Assert.IsTrue(content.Template.WasPropertyDirty("UpdateDate")); } [Test] @@ -479,10 +458,7 @@ namespace Umbraco.Tests.Models content.ContentSchedule.Add(DateTime.Now, DateTime.Now.AddDays(1)); //content.ChangePublishedState(PublishedState.Publishing); content.SortOrder = 5; - content.Template = new Template((string) "Test Template", (string) "testTemplate") - { - Id = 88 - }; + content.TemplateId = 88; content.Trashed = false; content.UpdateDate = DateTime.Now; content.WriterId = 23; diff --git a/src/Umbraco.Tests/Models/ContentTypeTests.cs b/src/Umbraco.Tests/Models/ContentTypeTests.cs index 6ef28d8290..d9e65ba6c6 100644 --- a/src/Umbraco.Tests/Models/ContentTypeTests.cs +++ b/src/Umbraco.Tests/Models/ContentTypeTests.cs @@ -93,7 +93,7 @@ namespace Umbraco.Tests.Models } } - private static ProfilingLogger GetTestProfilingLogger() + private static IProfilingLogger GetTestProfilingLogger() { var logger = new DebugDiagnosticsLogger(); var profiler = new TestProfiler(); diff --git a/src/Umbraco.Tests/Models/ContentXmlTest.cs b/src/Umbraco.Tests/Models/ContentXmlTest.cs index ab318ec1cb..a6a5e3a9ef 100644 --- a/src/Umbraco.Tests/Models/ContentXmlTest.cs +++ b/src/Umbraco.Tests/Models/ContentXmlTest.cs @@ -49,7 +49,7 @@ namespace Umbraco.Tests.Models Assert.AreEqual(content.GetCreatorProfile(ServiceContext.UserService).Name, (string)element.Attribute("creatorName")); Assert.AreEqual(content.GetWriterProfile(ServiceContext.UserService).Name, (string)element.Attribute("writerName")); Assert.AreEqual(content.WriterId.ToString(), (string)element.Attribute("writerID")); - Assert.AreEqual(content.Template == null ? "0" : content.Template.Id.ToString(), (string)element.Attribute("template")); + Assert.AreEqual(content.TemplateId.ToString(), (string)element.Attribute("template")); Assert.AreEqual(content.Properties["title"].GetValue().ToString(), element.Elements("title").Single().Value); Assert.AreEqual(content.Properties["bodyText"].GetValue().ToString(), element.Elements("bodyText").Single().Value); diff --git a/src/Umbraco.Tests/Models/MacroTests.cs b/src/Umbraco.Tests/Models/MacroTests.cs index b4060134bd..d980cd1d58 100644 --- a/src/Umbraco.Tests/Models/MacroTests.cs +++ b/src/Umbraco.Tests/Models/MacroTests.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using NUnit.Framework; +using Umbraco.Core.Composing; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; using Umbraco.Tests.TestHelpers; @@ -13,8 +14,9 @@ namespace Umbraco.Tests.Models [SetUp] public void Init() { - var config = SettingsForTests.GetDefaultUmbracoSettings(); - SettingsForTests.ConfigureSettings(config); + Current.Reset(); + Current.UnlockConfigs(); + Current.Configs.Add(SettingsForTests.GetDefaultUmbracoSettings); } [Test] diff --git a/src/Umbraco.Tests/Models/Mapping/AutoMapperTests.cs b/src/Umbraco.Tests/Models/Mapping/AutoMapperTests.cs index 23a9eafe76..1f7c5624bf 100644 --- a/src/Umbraco.Tests/Models/Mapping/AutoMapperTests.cs +++ b/src/Umbraco.Tests/Models/Mapping/AutoMapperTests.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using AutoMapper; using NUnit.Framework; @@ -9,7 +8,6 @@ using Umbraco.Core.Manifest; using Umbraco.Core.PropertyEditors; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; -using LightInject; namespace Umbraco.Tests.Models.Mapping { @@ -22,16 +20,16 @@ namespace Umbraco.Tests.Models.Mapping base.Compose(); var manifestBuilder = new ManifestParser( - CacheHelper.CreateDisabledCacheHelper().RuntimeCache, + CacheHelper.Disabled.RuntimeCache, new ManifestValueValidatorCollection(Enumerable.Empty()), - Logger) + Composition.Logger) { Path = TestHelper.CurrentAssemblyDirectory }; - Container.Register(_ => manifestBuilder); + Composition.RegisterUnique(_ => manifestBuilder); Func> typeListProducerList = Enumerable.Empty; - Container.GetInstance() + Composition.WithCollectionBuilder() .Clear() .Add(typeListProducerList); } @@ -39,7 +37,7 @@ namespace Umbraco.Tests.Models.Mapping [Test] public void AssertConfigurationIsValid() { - var profiles = Container.GetAllInstances().ToArray(); + var profiles = Factory.GetAllInstances().ToArray(); var config = new MapperConfiguration(cfg => { diff --git a/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs b/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs index 76e618ea26..b2d1440010 100644 --- a/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs +++ b/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs @@ -1,11 +1,9 @@ using System; -using System.Collections.Generic; using System.Linq; using AutoMapper; using Moq; using NUnit.Framework; using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; @@ -55,13 +53,13 @@ namespace Umbraco.Tests.Models.Mapping var dataEditors = new DataEditorCollection(editors); _editorsMock = new Mock(dataEditors); _editorsMock.Setup(x => x[It.IsAny()]).Returns(editors[0]); - Container.RegisterSingleton(f => _editorsMock.Object); + Composition.RegisterUnique(f => _editorsMock.Object); - Container.RegisterSingleton(_ => _contentTypeService.Object); - Container.RegisterSingleton(_ => _contentService.Object); - Container.RegisterSingleton(_ => _dataTypeService.Object); - Container.RegisterSingleton(_ => _entityService.Object); - Container.RegisterSingleton(_ => _fileService.Object); + Composition.RegisterUnique(_ => _contentTypeService.Object); + Composition.RegisterUnique(_ => _contentService.Object); + Composition.RegisterUnique(_ => _dataTypeService.Object); + Composition.RegisterUnique(_ => _entityService.Object); + Composition.RegisterUnique(_ => _fileService.Object); } [Test] diff --git a/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs b/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs index d2a5faa17f..e3551921d8 100644 --- a/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs +++ b/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs @@ -12,7 +12,6 @@ using Umbraco.Core.PropertyEditors; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; using Umbraco.Web.Models.ContentEditing; -using Umbraco.Core.Composing; using Umbraco.Tests.Testing; using Current = Umbraco.Web.Composing.Current; @@ -26,8 +25,8 @@ namespace Umbraco.Tests.Models.Mapping { base.Compose(); - Container.RegisterSingleton(f => Mock.Of()); - Container.RegisterSingleton(f => Mock.Of()); + Composition.RegisterUnique(f => Mock.Of()); + Composition.RegisterUnique(f => Mock.Of()); } [DataEditor("Test.Test", "Test", "~/Test.html")] diff --git a/src/Umbraco.Tests/Models/MediaXmlTest.cs b/src/Umbraco.Tests/Models/MediaXmlTest.cs index 492803ce17..1a56fac4eb 100644 --- a/src/Umbraco.Tests/Models/MediaXmlTest.cs +++ b/src/Umbraco.Tests/Models/MediaXmlTest.cs @@ -29,7 +29,12 @@ namespace Umbraco.Tests.Models // reference, so static ctor runs, so event handlers register // and then, this will reset the width, height... because the file does not exist, of course ;-( - var ignored = new FileUploadPropertyEditor(Mock.Of(), new MediaFileSystem(Mock.Of()), Mock.Of()); + var logger = Mock.Of(); + var scheme = Mock.Of(); + var config = Mock.Of(); + + var mediaFileSystem = new MediaFileSystem(Mock.Of(), config, scheme, logger); + var ignored = new FileUploadPropertyEditor(Mock.Of(), mediaFileSystem, config); var media = MockedMedia.CreateMediaImage(mediaType, -1); media.WriterId = -1; // else it's zero and that's not a user and it breaks the tests diff --git a/src/Umbraco.Tests/Models/MemberTests.cs b/src/Umbraco.Tests/Models/MemberTests.cs index 76ea804c57..c09f2e9460 100644 --- a/src/Umbraco.Tests/Models/MemberTests.cs +++ b/src/Umbraco.Tests/Models/MemberTests.cs @@ -2,9 +2,10 @@ using System.Diagnostics; using System.Linq; using NUnit.Framework; +using Umbraco.Core.Composing; using Umbraco.Core.Models; -using Umbraco.Core.Models.Entities; using Umbraco.Core.Serialization; +using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; namespace Umbraco.Tests.Models @@ -12,6 +13,15 @@ namespace Umbraco.Tests.Models [TestFixture] public class MemberTests { + [SetUp] + public void Setup() + { + Current.Reset(); + Current.UnlockConfigs(); + Current.Configs.Add(SettingsForTests.GetDefaultGlobalSettings); + Current.Configs.Add(SettingsForTests.GetDefaultUmbracoSettings); + } + [Test] public void Can_Deep_Clone() { diff --git a/src/Umbraco.Tests/Models/UserTests.cs b/src/Umbraco.Tests/Models/UserTests.cs index 5e982633d2..797b79381a 100644 --- a/src/Umbraco.Tests/Models/UserTests.cs +++ b/src/Umbraco.Tests/Models/UserTests.cs @@ -2,14 +2,24 @@ using System.Diagnostics; using System.Linq; using NUnit.Framework; +using Umbraco.Core.Composing; using Umbraco.Core.Models.Membership; using Umbraco.Core.Serialization; +using Umbraco.Tests.TestHelpers; namespace Umbraco.Tests.Models { [TestFixture] public class UserTests { + [SetUp] + public void Setup() + { + Current.Reset(); + Current.UnlockConfigs(); + Current.Configs.Add(SettingsForTests.GetDefaultGlobalSettings); + } + [Test] public void Can_Deep_Clone() { diff --git a/src/Umbraco.Tests/Models/VariationTests.cs b/src/Umbraco.Tests/Models/VariationTests.cs index a5b6b71b9a..7c1ef3f580 100644 --- a/src/Umbraco.Tests/Models/VariationTests.cs +++ b/src/Umbraco.Tests/Models/VariationTests.cs @@ -1,9 +1,9 @@ using System; -using LightInject; using Moq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; @@ -27,8 +27,13 @@ namespace Umbraco.Tests.Models // need to be able to retrieve them all... Current.Reset(); - var container = Mock.Of(); - Current.Container = container; + + var configs = new Configs(); + configs.Add(SettingsForTests.GetDefaultGlobalSettings); + configs.Add(SettingsForTests.GetDefaultUmbracoSettings); + + var factory = Mock.Of(); + Current.Factory = factory; var dataEditors = new DataEditorCollection(new IDataEditor[] { @@ -46,20 +51,20 @@ namespace Umbraco.Tests.Models .Setup(x => x.GetDataType(It.IsAny())) .Returns(x => dataType); - var serviceContext = new ServiceContext( + var serviceContext = ServiceContext.CreatePartial( dataTypeService: dataTypeService, localizedTextService: Mock.Of()); - Mock.Get(container) + Mock.Get(factory) .Setup(x => x.GetInstance(It.IsAny())) .Returns(x => { + if (x == typeof(Configs)) return configs; if (x == typeof(PropertyEditorCollection)) return propertyEditors; if (x == typeof(ServiceContext)) return serviceContext; if (x == typeof(ILocalizedTextService)) return serviceContext.LocalizationService; - throw new Exception("oops"); + throw new NotSupportedException(x.FullName); }); - } [Test] @@ -402,7 +407,6 @@ namespace Umbraco.Tests.Models var content = new Content("content", -1, contentType) { Id = 1, VersionId = 1 }; // change - now we vary by culture - contentType.Variations |= ContentVariation.Culture; propertyType.Variations |= ContentVariation.Culture; diff --git a/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs b/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs index 788ee597dd..32689fe192 100644 --- a/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs +++ b/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs @@ -14,6 +14,7 @@ using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; +using Umbraco.Web.Security; namespace Umbraco.Tests.Persistence { @@ -33,7 +34,7 @@ namespace Umbraco.Tests.Persistence _sqlCeSyntaxProvider = new SqlCeSyntaxProvider(); _sqlSyntaxProviders = new[] { (ISqlSyntaxProvider) _sqlCeSyntaxProvider }; _logger = Mock.Of(); - _databaseFactory = new UmbracoDatabaseFactory(_sqlSyntaxProviders, _logger, Mock.Of()); + _databaseFactory = new UmbracoDatabaseFactory(_logger, new Lazy(() => Mock.Of())); } [TearDown] @@ -76,7 +77,7 @@ namespace Umbraco.Tests.Persistence } // re-create the database factory and database context with proper connection string - _databaseFactory = new UmbracoDatabaseFactory(connString, Constants.DbProviderNames.SqlCe, _sqlSyntaxProviders, _logger, Mock.Of()); + _databaseFactory = new UmbracoDatabaseFactory(connString, Constants.DbProviderNames.SqlCe, _logger, new Lazy(() => Mock.Of())); // create application context //var appCtx = new ApplicationContext( @@ -88,9 +89,11 @@ namespace Umbraco.Tests.Persistence // create the umbraco database DatabaseSchemaCreator schemaHelper; using (var database = _databaseFactory.CreateDatabase()) + using (var transaction = database.GetTransaction()) { schemaHelper = new DatabaseSchemaCreator(database, _logger); schemaHelper.InitializeDatabaseSchema(); + transaction.Complete(); } var umbracoNodeTable = schemaHelper.TableExists("umbracoNode"); diff --git a/src/Umbraco.Tests/Persistence/FaultHandling/ConnectionRetryTest.cs b/src/Umbraco.Tests/Persistence/FaultHandling/ConnectionRetryTest.cs index bff43d5ea8..6aba8187f2 100644 --- a/src/Umbraco.Tests/Persistence/FaultHandling/ConnectionRetryTest.cs +++ b/src/Umbraco.Tests/Persistence/FaultHandling/ConnectionRetryTest.cs @@ -19,8 +19,7 @@ namespace Umbraco.Tests.Persistence.FaultHandling { const string connectionString = @"server=.\SQLEXPRESS;database=EmptyForTest;user id=x;password=umbraco"; const string providerName = Constants.DbProviderNames.SqlServer; - var sqlSyntax = new[] { new SqlServerSyntaxProvider(new Lazy(() => null)) }; - var factory = new UmbracoDatabaseFactory(connectionString, providerName, sqlSyntax, Mock.Of(), Mock.Of()); + var factory = new UmbracoDatabaseFactory(connectionString, providerName, Mock.Of(), new Lazy(() => Mock.Of())); using (var database = factory.CreateDatabase()) { @@ -34,8 +33,7 @@ namespace Umbraco.Tests.Persistence.FaultHandling { const string connectionString = @"server=.\SQLEXPRESS;database=EmptyForTest;user id=umbraco;password=umbraco"; const string providerName = Constants.DbProviderNames.SqlServer; - var sqlSyntax = new[] { new SqlServerSyntaxProvider(new Lazy(() => null)) }; - var factory = new UmbracoDatabaseFactory(connectionString, providerName, sqlSyntax, Mock.Of(), Mock.Of()); + var factory = new UmbracoDatabaseFactory(connectionString, providerName, Mock.Of(), new Lazy(() => Mock.Of())); using (var database = factory.CreateDatabase()) { diff --git a/src/Umbraco.Tests/Persistence/Repositories/AuditRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/AuditRepositoryTest.cs index eb85656ee4..de970c900d 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/AuditRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/AuditRepositoryTest.cs @@ -1,6 +1,5 @@ using System.Linq; using NUnit.Framework; -using Umbraco.Core.Composing; using Umbraco.Core.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.DatabaseModelDefinitions; @@ -26,7 +25,7 @@ namespace Umbraco.Tests.Persistence.Repositories var sp = TestObjects.GetScopeProvider(Logger); using (var scope = sp.CreateScope()) { - var repo = new AuditRepository((IScopeAccessor) sp, CacheHelper, Logger); + var repo = new AuditRepository((IScopeAccessor) sp, Logger); repo.Save(new AuditItem(-1, AuditType.System, -1, ObjectTypes.GetName(UmbracoObjectTypes.Document), "This is a System audit trail")); var dtos = scope.Database.Fetch("WHERE id > -1"); @@ -42,7 +41,7 @@ namespace Umbraco.Tests.Persistence.Repositories var sp = TestObjects.GetScopeProvider(Logger); using (var scope = sp.CreateScope()) { - var repo = new AuditRepository((IScopeAccessor) sp, CacheHelper, Logger); + var repo = new AuditRepository((IScopeAccessor) sp, Logger); for (var i = 0; i < 100; i++) { @@ -55,7 +54,7 @@ namespace Umbraco.Tests.Persistence.Repositories using (var scope = sp.CreateScope()) { - var repo = new AuditRepository((IScopeAccessor) sp, CacheHelper, Logger); + var repo = new AuditRepository((IScopeAccessor) sp, Logger); var page = repo.GetPagedResultsByQuery(sp.SqlContext.Query(), 0, 10, out var total, Direction.Descending, null, null); @@ -70,7 +69,7 @@ namespace Umbraco.Tests.Persistence.Repositories var sp = TestObjects.GetScopeProvider(Logger); using (var scope = sp.CreateScope()) { - var repo = new AuditRepository((IScopeAccessor)sp, CacheHelper, Logger); + var repo = new AuditRepository((IScopeAccessor)sp, Logger); for (var i = 0; i < 100; i++) { @@ -83,7 +82,7 @@ namespace Umbraco.Tests.Persistence.Repositories using (var scope = sp.CreateScope()) { - var repo = new AuditRepository((IScopeAccessor)sp, CacheHelper, Logger); + var repo = new AuditRepository((IScopeAccessor)sp, Logger); var query = sp.SqlContext.Query().Where(x => x.UserId == -1); @@ -113,7 +112,7 @@ namespace Umbraco.Tests.Persistence.Repositories var sp = TestObjects.GetScopeProvider(Logger); using (var scope = sp.CreateScope()) { - var repo = new AuditRepository((IScopeAccessor) sp, CacheHelper, Logger); + var repo = new AuditRepository((IScopeAccessor) sp, Logger); for (var i = 0; i < 100; i++) { @@ -126,7 +125,7 @@ namespace Umbraco.Tests.Persistence.Repositories using (var scope = sp.CreateScope()) { - var repo = new AuditRepository((IScopeAccessor) sp, CacheHelper, Logger); + var repo = new AuditRepository((IScopeAccessor) sp, Logger); var page = repo.GetPagedResultsByQuery(sp.SqlContext.Query(), 0, 9, out var total, Direction.Descending, new[] {AuditType.Publish}, null) @@ -144,7 +143,7 @@ namespace Umbraco.Tests.Persistence.Repositories var sp = TestObjects.GetScopeProvider(Logger); using (var scope = sp.CreateScope()) { - var repo = new AuditRepository((IScopeAccessor) sp, CacheHelper, Logger); + var repo = new AuditRepository((IScopeAccessor) sp, Logger); for (var i = 0; i < 100; i++) { @@ -157,7 +156,7 @@ namespace Umbraco.Tests.Persistence.Repositories using (var scope = sp.CreateScope()) { - var repo = new AuditRepository((IScopeAccessor) sp, CacheHelper, Logger); + var repo = new AuditRepository((IScopeAccessor) sp, Logger); var page = repo.GetPagedResultsByQuery(sp.SqlContext.Query(), 0, 8, out var total, Direction.Descending, null, sp.SqlContext.Query().Where(item => item.Comment == "Content created")) diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs index 6a282169fb..aeaf76967f 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs @@ -35,8 +35,8 @@ namespace Umbraco.Tests.Persistence.Repositories private DocumentRepository CreateRepository(IScopeAccessor scopeAccessor, out ContentTypeRepository contentTypeRepository) { - var cacheHelper = CacheHelper.CreateDisabledCacheHelper(); - var templateRepository = new TemplateRepository(scopeAccessor, cacheHelper, Logger, Mock.Of(), Mock.Of(), Mock.Of()); + var cacheHelper = CacheHelper.Disabled; + var templateRepository = new TemplateRepository(scopeAccessor, cacheHelper, Logger, Mock.Of(), TestObjects.GetFileSystemsMock()); var tagRepository = new TagRepository(scopeAccessor, cacheHelper, Logger); contentTypeRepository = new ContentTypeRepository(scopeAccessor, cacheHelper, Logger, templateRepository); var languageRepository = new LanguageRepository(scopeAccessor, cacheHelper, Logger); @@ -46,20 +46,20 @@ namespace Umbraco.Tests.Persistence.Repositories private ContentTypeRepository CreateRepository(IScopeAccessor scopeAccessor) { - var templateRepository = new TemplateRepository(scopeAccessor, CacheHelper.CreateDisabledCacheHelper(), Logger, Mock.Of(), Mock.Of(), Mock.Of()); - var contentTypeRepository = new ContentTypeRepository(scopeAccessor, CacheHelper.CreateDisabledCacheHelper(), Logger, templateRepository); + var templateRepository = new TemplateRepository(scopeAccessor, CacheHelper.Disabled, Logger, Mock.Of(), TestObjects.GetFileSystemsMock()); + var contentTypeRepository = new ContentTypeRepository(scopeAccessor, CacheHelper.Disabled, Logger, templateRepository); return contentTypeRepository; } private MediaTypeRepository CreateMediaTypeRepository(IScopeAccessor scopeAccessor) { - var contentTypeRepository = new MediaTypeRepository(scopeAccessor, CacheHelper.CreateDisabledCacheHelper(), Logger); + var contentTypeRepository = new MediaTypeRepository(scopeAccessor, CacheHelper.Disabled, Logger); return contentTypeRepository; } private EntityContainerRepository CreateContainerRepository(IScopeAccessor scopeAccessor, Guid containerEntityType) { - return new EntityContainerRepository(scopeAccessor, CacheHelper.CreateDisabledCacheHelper(), Logger, containerEntityType); + return new EntityContainerRepository(scopeAccessor, CacheHelper.Disabled, Logger, containerEntityType); } //TODO Add test to verify SetDefaultTemplates updates both AllowedTemplates and DefaultTemplate(id). @@ -71,7 +71,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var templateRepo = new TemplateRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Logger, Mock.Of(), Mock.Of(), Mock.Of()); + var templateRepo = new TemplateRepository((IScopeAccessor) provider, CacheHelper.Disabled, Logger, Mock.Of(), TestObjects.GetFileSystemsMock()); var repository = CreateRepository((IScopeAccessor) provider); var templates = new[] { diff --git a/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs index 41daa05022..ce871d0d0a 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs @@ -6,7 +6,7 @@ using Umbraco.Core.Models; using Umbraco.Core.Persistence.Repositories; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; -using LightInject; +using Umbraco.Core.Composing; using Umbraco.Core.Persistence.Repositories.Implement; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; @@ -20,12 +20,12 @@ namespace Umbraco.Tests.Persistence.Repositories { private IDataTypeRepository CreateRepository() { - return Container.GetInstance(); + return Factory.GetInstance(); } private EntityContainerRepository CreateContainerRepository(IScopeAccessor scopeAccessor) { - return new EntityContainerRepository(scopeAccessor, CacheHelper.CreateDisabledCacheHelper(), Logger, Constants.ObjectTypes.DataTypeContainer); + return new EntityContainerRepository(scopeAccessor, CacheHelper.Disabled, Logger, Constants.ObjectTypes.DataTypeContainer); } [Test] diff --git a/src/Umbraco.Tests/Persistence/Repositories/DictionaryRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/DictionaryRepositoryTest.cs index 8e8306095f..155ea1905c 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/DictionaryRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/DictionaryRepositoryTest.cs @@ -6,8 +6,8 @@ using Umbraco.Core.Models; using Umbraco.Core.Persistence.Repositories; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; -using LightInject; -using Umbraco.Core.Scoping; +using Umbraco.Core; +using Umbraco.Core.Composing; namespace Umbraco.Tests.Persistence.Repositories { @@ -24,7 +24,7 @@ namespace Umbraco.Tests.Persistence.Repositories private IDictionaryRepository CreateRepository() { - return Container.GetInstance(); + return Factory.GetInstance(); } [Test] @@ -230,7 +230,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var languageRepository = Container.GetInstance(); + var languageRepository = Factory.GetInstance(); var repository = CreateRepository(); var language = languageRepository.Get(1); diff --git a/src/Umbraco.Tests/Persistence/Repositories/DocumentRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/DocumentRepositoryTest.cs index 8827766d4a..2cd8e158b3 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/DocumentRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/DocumentRepositoryTest.cs @@ -64,7 +64,7 @@ namespace Umbraco.Tests.Persistence.Repositories { cacheHelper = cacheHelper ?? CacheHelper; - templateRepository = new TemplateRepository(scopeAccessor, cacheHelper, Logger, Mock.Of(), Mock.Of(), Mock.Of()); + templateRepository = new TemplateRepository(scopeAccessor, cacheHelper, Logger, Mock.Of(), TestObjects.GetFileSystemsMock()); var tagRepository = new TagRepository(scopeAccessor, cacheHelper, Logger); contentTypeRepository = new ContentTypeRepository(scopeAccessor, cacheHelper, Logger, templateRepository); var languageRepository = new LanguageRepository(scopeAccessor, cacheHelper, Logger); @@ -434,8 +434,9 @@ namespace Umbraco.Tests.Persistence.Repositories var fetched = repository.Get(textpage.Id); - Assert.NotNull(textpage.Template); - Assert.AreEqual(textpage.Template, contentType.DefaultTemplate); + Assert.True(textpage.TemplateId.HasValue); + Assert.NotZero(textpage.TemplateId.Value); + Assert.AreEqual(textpage.TemplateId, contentType.DefaultTemplate.Id); scope.Complete(); @@ -559,12 +560,12 @@ namespace Umbraco.Tests.Persistence.Repositories var repository = CreateRepository((IScopeAccessor)provider, out _); var content = repository.Get(NodeDto.NodeIdSeed + 2); - content.Template = null; + content.TemplateId = null; repository.Save(content); var updatedContent = repository.Get(NodeDto.NodeIdSeed + 2); - Assert.IsNull(updatedContent.Template); + Assert.False(updatedContent.TemplateId.HasValue); } } @@ -672,7 +673,7 @@ namespace Umbraco.Tests.Persistence.Repositories [Test] public void AliasRegexTest() { - var regex = new SqlServerSyntaxProvider(new Lazy(() => null)).AliasRegex; + var regex = new SqlServerSyntaxProvider().AliasRegex; Assert.AreEqual(@"(\[\w+]\.\[\w+])\s+AS\s+(\[\w+])", regex.ToString()); const string sql = "SELECT [table].[column1] AS [alias1], [table].[column2] AS [alias2] FROM [table];"; var matches = regex.Matches(sql); diff --git a/src/Umbraco.Tests/Persistence/Repositories/DomainRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/DomainRepositoryTest.cs index e88b6e3f44..a5402f964e 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/DomainRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/DomainRepositoryTest.cs @@ -22,12 +22,12 @@ namespace Umbraco.Tests.Persistence.Repositories private DomainRepository CreateRepository(IScopeProvider provider, out ContentTypeRepository contentTypeRepository, out DocumentRepository documentRepository, out LanguageRepository languageRepository) { var accessor = (IScopeAccessor) provider; - var templateRepository = new TemplateRepository(accessor, DisabledCache, Logger, Mock.Of(), Mock.Of(), Mock.Of()); - var tagRepository = new TagRepository(accessor, DisabledCache, Logger); - contentTypeRepository = new ContentTypeRepository(accessor, DisabledCache, Logger, templateRepository); - languageRepository = new LanguageRepository(accessor, DisabledCache, Logger); - documentRepository = new DocumentRepository(accessor, DisabledCache, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, Mock.Of()); - var domainRepository = new DomainRepository(accessor, DisabledCache, Logger); + var templateRepository = new TemplateRepository(accessor, Core.Cache.CacheHelper.Disabled, Logger, Mock.Of(), TestObjects.GetFileSystemsMock()); + var tagRepository = new TagRepository(accessor, Core.Cache.CacheHelper.Disabled, Logger); + contentTypeRepository = new ContentTypeRepository(accessor, Core.Cache.CacheHelper.Disabled, Logger, templateRepository); + languageRepository = new LanguageRepository(accessor, Core.Cache.CacheHelper.Disabled, Logger); + documentRepository = new DocumentRepository(accessor, Core.Cache.CacheHelper.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, Mock.Of()); + var domainRepository = new DomainRepository(accessor, Core.Cache.CacheHelper.Disabled, Logger); return domainRepository; } diff --git a/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs index a063d2e387..2f91c602af 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs @@ -26,7 +26,7 @@ namespace Umbraco.Tests.Persistence.Repositories private LanguageRepository CreateRepository(IScopeProvider provider) { - return new LanguageRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); + return new LanguageRepository((IScopeAccessor) provider, CacheHelper.Disabled, Mock.Of()); } [Test] diff --git a/src/Umbraco.Tests/Persistence/Repositories/MacroRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MacroRepositoryTest.cs index 5ae25d629f..e21debfdfd 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MacroRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MacroRepositoryTest.cs @@ -35,7 +35,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); + var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.Disabled, Mock.Of()); var macro = new Macro("test1", "Test", "~/views/macropartials/test.cshtml", MacroTypes.PartialView); ; @@ -52,7 +52,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); + var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.Disabled, Mock.Of()); var macro = repository.Get(1); macro.Alias = "test2"; @@ -69,7 +69,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); + var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.Disabled, Mock.Of()); // Assert Assert.That(repository, Is.Not.Null); @@ -83,7 +83,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); + var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.Disabled, Mock.Of()); // Act var macro = repository.Get(1); @@ -111,7 +111,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); + var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.Disabled, Mock.Of()); // Act var macros = repository.GetMany(); @@ -129,7 +129,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); + var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.Disabled, Mock.Of()); // Act var query = scope.SqlContext.Query().Where(x => x.Alias.ToUpper() == "TEST1"); @@ -147,7 +147,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); + var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.Disabled, Mock.Of()); // Act var query = scope.SqlContext.Query().Where(x => x.Name.StartsWith("Test")); @@ -165,7 +165,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); + var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.Disabled, Mock.Of()); // Act var macro = new Macro("test", "Test", "~/views/macropartials/test.cshtml", MacroTypes.PartialView); @@ -186,7 +186,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); + var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.Disabled, Mock.Of()); // Act var macro = repository.Get(2); @@ -221,7 +221,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); + var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.Disabled, Mock.Of()); // Act var macro = repository.Get(3); @@ -242,7 +242,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); + var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.Disabled, Mock.Of()); // Act var exists = repository.Exists(3); @@ -261,7 +261,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); + var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.Disabled, Mock.Of()); var macro = repository.Get(1); macro.Properties.Add(new MacroProperty("new1", "New1", 3, "test")); @@ -287,7 +287,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); + var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.Disabled, Mock.Of()); var macro = new Macro("newmacro", "A new macro", "~/views/macropartials/test1.cshtml", MacroTypes.PartialView); macro.Properties.Add(new MacroProperty("blah1", "New1", 4, "test.editor")); @@ -312,7 +312,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); + var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.Disabled, Mock.Of()); var macro = new Macro("newmacro", "A new macro", "~/views/macropartials/test1.cshtml", MacroTypes.PartialView); macro.Properties.Add(new MacroProperty("blah1", "New1", 4, "test.editor")); @@ -336,7 +336,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); + var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.Disabled, Mock.Of()); var macro = new Macro("newmacro", "A new macro", "~/views/macropartials/test1.cshtml", MacroTypes.PartialView); var prop1 = new MacroProperty("blah1", "New1", 4, "test.editor"); @@ -367,7 +367,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); + var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.Disabled, Mock.Of()); var macro = repository.Get(1); macro.Properties.Add(new MacroProperty("new1", "New1", 3, "test")); @@ -394,7 +394,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); + var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.Disabled, Mock.Of()); var macro = repository.Get(1); macro.Properties.Add(new MacroProperty("new1", "New1", 3, "test")); @@ -422,7 +422,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); + var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.Disabled, Mock.Of()); repository.Save(new Macro("test1", "Test1", "~/views/macropartials/test1.cshtml", MacroTypes.PartialView)); repository.Save(new Macro("test2", "Test2", "~/views/macropartials/test2.cshtml", MacroTypes.PartialView)); diff --git a/src/Umbraco.Tests/Persistence/Repositories/MediaTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MediaTypeRepositoryTest.cs index 85f49306d2..ae4308db55 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MediaTypeRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MediaTypeRepositoryTest.cs @@ -2,6 +2,7 @@ using System.Linq; using NUnit.Framework; using Umbraco.Core; +using Umbraco.Core.Cache; using Umbraco.Core.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Dtos; @@ -20,12 +21,12 @@ namespace Umbraco.Tests.Persistence.Repositories { private MediaTypeRepository CreateRepository(IScopeProvider provider) { - return new MediaTypeRepository((IScopeAccessor) provider, DisabledCache, Logger); + return new MediaTypeRepository((IScopeAccessor) provider, CacheHelper.Disabled, Logger); } private EntityContainerRepository CreateContainerRepository(IScopeProvider provider) { - return new EntityContainerRepository((IScopeAccessor) provider, DisabledCache, Logger, Constants.ObjectTypes.MediaTypeContainer); + return new EntityContainerRepository((IScopeAccessor) provider, CacheHelper.Disabled, Logger, Constants.ObjectTypes.MediaTypeContainer); } diff --git a/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs index 38ecc6c8ae..18bfc4fcea 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs @@ -6,6 +6,7 @@ using Moq; using NPoco; using NUnit.Framework; using Umbraco.Core; +using Umbraco.Core.Cache; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; using Umbraco.Core.Models; @@ -28,10 +29,10 @@ namespace Umbraco.Tests.Persistence.Repositories private MemberRepository CreateRepository(IScopeProvider provider, out MemberTypeRepository memberTypeRepository, out MemberGroupRepository memberGroupRepository) { var accessor = (IScopeAccessor) provider; - memberTypeRepository = new MemberTypeRepository(accessor, DisabledCache, Logger); - memberGroupRepository = new MemberGroupRepository(accessor, DisabledCache, Logger); - var tagRepo = new TagRepository(accessor, DisabledCache, Logger); - var repository = new MemberRepository(accessor, DisabledCache, Logger, memberTypeRepository, memberGroupRepository, tagRepo, Mock.Of()); + memberTypeRepository = new MemberTypeRepository(accessor, CacheHelper.Disabled, Logger); + memberGroupRepository = new MemberGroupRepository(accessor, CacheHelper.Disabled, Logger); + var tagRepo = new TagRepository(accessor, CacheHelper.Disabled, Logger); + var repository = new MemberRepository(accessor, CacheHelper.Disabled, Logger, memberTypeRepository, memberGroupRepository, tagRepo, Mock.Of()); return repository; } diff --git a/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs index e3d15613c2..87d146f9f4 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs @@ -22,7 +22,7 @@ namespace Umbraco.Tests.Persistence.Repositories { private MemberTypeRepository CreateRepository(IScopeProvider provider) { - return new MemberTypeRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); + return new MemberTypeRepository((IScopeAccessor) provider, CacheHelper.Disabled, Mock.Of()); } [Test] diff --git a/src/Umbraco.Tests/Persistence/Repositories/PartialViewRepositoryTests.cs b/src/Umbraco.Tests/Persistence/Repositories/PartialViewRepositoryTests.cs index cf307d2ea9..9c326b3ddc 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/PartialViewRepositoryTests.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/PartialViewRepositoryTests.cs @@ -1,12 +1,10 @@ using System.Linq; +using Moq; using NUnit.Framework; using Umbraco.Core.IO; using Umbraco.Core.Models; -using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.PropertyEditors; -using Umbraco.Core.Composing; using Umbraco.Core.Persistence.Repositories.Implement; -using Umbraco.Core.Scoping; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; @@ -29,7 +27,7 @@ namespace Umbraco.Tests.Persistence.Repositories { base.Compose(); - Container.RegisterSingleton(f => new DataEditorCollection(Enumerable.Empty())); + Composition.RegisterUnique(f => new DataEditorCollection(Enumerable.Empty())); } [Test] @@ -37,10 +35,13 @@ namespace Umbraco.Tests.Persistence.Repositories { // unless noted otherwise, no changes / 7.2.8 + var fileSystems = Mock.Of(); + Mock.Get(fileSystems).Setup(x => x.PartialViewsFileSystem).Returns(_fileSystem); + var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = new PartialViewRepository(_fileSystem); + var repository = new PartialViewRepository(fileSystems); var partialView = new PartialView(PartialViewType.PartialView, "test-path-1.cshtml") { Content = "// partialView" }; repository.Save(partialView); diff --git a/src/Umbraco.Tests/Persistence/Repositories/PublicAccessRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/PublicAccessRepositoryTest.cs index 39b3b0f797..e76d794e69 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/PublicAccessRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/PublicAccessRepositoryTest.cs @@ -307,7 +307,7 @@ namespace Umbraco.Tests.Persistence.Repositories private DocumentRepository CreateRepository(IScopeProvider provider, out ContentTypeRepository contentTypeRepository) { var accessor = (IScopeAccessor) provider; - var templateRepository = new TemplateRepository(accessor, CacheHelper, Logger, Mock.Of(), Mock.Of(), Mock.Of()); + var templateRepository = new TemplateRepository(accessor, CacheHelper, Logger, Mock.Of(), TestObjects.GetFileSystemsMock()); var tagRepository = new TagRepository(accessor, CacheHelper, Logger); contentTypeRepository = new ContentTypeRepository(accessor, CacheHelper, Logger, templateRepository); var languageRepository = new LanguageRepository(accessor, CacheHelper, Logger); diff --git a/src/Umbraco.Tests/Persistence/Repositories/RelationRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/RelationRepositoryTest.cs index 9ffcfa4442..697951d021 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/RelationRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/RelationRepositoryTest.cs @@ -30,8 +30,8 @@ namespace Umbraco.Tests.Persistence.Repositories private RelationRepository CreateRepository(IScopeProvider provider, out RelationTypeRepository relationTypeRepository) { var accessor = (IScopeAccessor) provider; - relationTypeRepository = new RelationTypeRepository(accessor, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); - var repository = new RelationRepository(accessor, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), relationTypeRepository); + relationTypeRepository = new RelationTypeRepository(accessor, CacheHelper.Disabled, Mock.Of()); + var repository = new RelationRepository(accessor, Mock.Of(), relationTypeRepository); return repository; } @@ -266,12 +266,11 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var relationTypeRepository = new RelationTypeRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); - var relationRepository = new RelationRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), relationTypeRepository); + var relationTypeRepository = new RelationTypeRepository((IScopeAccessor) provider, CacheHelper.Disabled, Mock.Of()); + var relationRepository = new RelationRepository((IScopeAccessor) provider, Mock.Of(), relationTypeRepository); relationTypeRepository.Save(relateContent); - relationTypeRepository.Save(relateContentType); - + relationTypeRepository.Save(relateContentType); //Create and Save ContentType "umbTextpage" -> (NodeDto.NodeIdSeed) ContentType contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage", "Textpage"); diff --git a/src/Umbraco.Tests/Persistence/Repositories/RelationTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/RelationTypeRepositoryTest.cs index ce59f17ea1..26b2fe48c2 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/RelationTypeRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/RelationTypeRepositoryTest.cs @@ -28,7 +28,7 @@ namespace Umbraco.Tests.Persistence.Repositories private RelationTypeRepository CreateRepository(IScopeProvider provider) { - return new RelationTypeRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); + return new RelationTypeRepository((IScopeAccessor) provider, CacheHelper.Disabled, Mock.Of()); } @@ -232,7 +232,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = ScopeProvider.CreateScope()) { - var repository = new RelationTypeRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); + var repository = new RelationTypeRepository((IScopeAccessor) provider, CacheHelper.Disabled, Mock.Of()); repository.Save(relateContent);//Id 2 repository.Save(relateContentType);//Id 3 diff --git a/src/Umbraco.Tests/Persistence/Repositories/ScriptRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ScriptRepositoryTest.cs index 755682a913..36c1bbdfb4 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ScriptRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ScriptRepositoryTest.cs @@ -4,13 +4,10 @@ using System.Text; using Moq; using NUnit.Framework; using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.Models; -using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.Repositories.Implement; using Umbraco.Core.PropertyEditors; -using Umbraco.Core.Scoping; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; @@ -20,13 +17,16 @@ namespace Umbraco.Tests.Persistence.Repositories [UmbracoTest(WithApplication = true, Database = UmbracoTestOptions.Database.NewEmptyPerFixture)] public class ScriptRepositoryTest : TestWithDatabaseBase { + private IFileSystems _fileSystems; private IFileSystem _fileSystem; public override void SetUp() { base.SetUp(); + _fileSystems = Mock.Of(); _fileSystem = new PhysicalFileSystem(SystemDirectories.Scripts); + Mock.Get(_fileSystems).Setup(x => x.ScriptsFileSystem).Returns(_fileSystem); using (var stream = CreateStream("Umbraco.Sys.registerNamespace(\"Umbraco.Utils\");")) { _fileSystem.AddFile("test-script.js", stream); @@ -37,7 +37,7 @@ namespace Umbraco.Tests.Persistence.Repositories { base.Compose(); - Container.RegisterSingleton(f => new DataEditorCollection(Enumerable.Empty())); + Composition.RegisterUnique(f => new DataEditorCollection(Enumerable.Empty())); } [Test] @@ -47,9 +47,8 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = ScopeProvider.CreateScope()) { - // Act - var repository = new ScriptRepository(_fileSystem, Mock.Of()); + var repository = new ScriptRepository(_fileSystems, Mock.Of()); // Assert Assert.That(repository, Is.Not.Null); @@ -63,12 +62,12 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = ScopeProvider.CreateScope()) { - var repository = new ScriptRepository(_fileSystem, Mock.Of()); + var repository = new ScriptRepository(_fileSystems, Mock.Of()); // Act var script = new Script("test-add-script.js") { Content = "/// " }; repository.Save(script); - + //Assert Assert.That(_fileSystem.FileExists("test-add-script.js"), Is.True); @@ -82,16 +81,16 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = ScopeProvider.CreateScope()) { - var repository = new ScriptRepository(_fileSystem, Mock.Of()); + var repository = new ScriptRepository(_fileSystems, Mock.Of()); // Act var script = new Script("test-updated-script.js") { Content = "/// " }; repository.Save(script); - + script.Content = "/// "; repository.Save(script); - + var scriptUpdated = repository.Get("test-updated-script.js"); @@ -108,12 +107,12 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = ScopeProvider.CreateScope()) { - var repository = new ScriptRepository(_fileSystem, Mock.Of()); + var repository = new ScriptRepository(_fileSystems, Mock.Of()); // Act var script = repository.Get("test-script.js"); repository.Delete(script); - + // Assert @@ -128,7 +127,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = ScopeProvider.CreateScope()) { - var repository = new ScriptRepository(_fileSystem, Mock.Of()); + var repository = new ScriptRepository(_fileSystems, Mock.Of()); // Act var exists = repository.Get("test-script.js"); @@ -147,7 +146,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = ScopeProvider.CreateScope()) { - var repository = new ScriptRepository(_fileSystem, Mock.Of()); + var repository = new ScriptRepository(_fileSystems, Mock.Of()); var script = new Script("test-script1.js") { Content = "/// " }; repository.Save(script); @@ -155,7 +154,7 @@ namespace Umbraco.Tests.Persistence.Repositories repository.Save(script2); var script3 = new Script("test-script3.js") { Content = "/// " }; repository.Save(script3); - + // Act var scripts = repository.GetMany(); @@ -175,7 +174,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = ScopeProvider.CreateScope()) { - var repository = new ScriptRepository(_fileSystem, Mock.Of()); + var repository = new ScriptRepository(_fileSystems, Mock.Of()); var script = new Script("test-script1.js") { Content = "/// " }; repository.Save(script); @@ -183,7 +182,7 @@ namespace Umbraco.Tests.Persistence.Repositories repository.Save(script2); var script3 = new Script("test-script3.js") { Content = "/// " }; repository.Save(script3); - + // Act var scripts = repository.GetMany("test-script1.js", "test-script2.js"); @@ -203,7 +202,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = ScopeProvider.CreateScope()) { - var repository = new ScriptRepository(_fileSystem, Mock.Of()); + var repository = new ScriptRepository(_fileSystems, Mock.Of()); // Act var exists = repository.Exists("test-script.js"); @@ -222,17 +221,17 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = ScopeProvider.CreateScope()) { - var repository = new ScriptRepository(_fileSystem, Mock.Of()); + var repository = new ScriptRepository(_fileSystems, Mock.Of()); var script = new Script("test-move-script.js") { Content = content }; repository.Save(script); - + // Act script = repository.Get("test-move-script.js"); script.Path = "moved/test-move-script.js"; repository.Save(script); - + var existsOld = repository.Exists("test-move-script.js"); var existsNew = repository.Exists("moved/test-move-script.js"); @@ -255,11 +254,11 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = ScopeProvider.CreateScope()) { - var repository = new ScriptRepository(_fileSystem, Mock.Of()); + var repository = new ScriptRepository(_fileSystems, Mock.Of()); var script = new Script("test-path-1.js") { Content = "// script" }; repository.Save(script); - + Assert.IsTrue(_fileSystem.FileExists("test-path-1.js")); Assert.AreEqual("test-path-1.js", script.Path); Assert.AreEqual("/scripts/test-path-1.js", script.VirtualPath); @@ -267,14 +266,14 @@ namespace Umbraco.Tests.Persistence.Repositories //ensure you can prefix the same path as the root path name script = new Script("scripts/path-2/test-path-2.js") { Content = "// script" }; repository.Save(script); - + Assert.IsTrue(_fileSystem.FileExists("scripts/path-2/test-path-2.js")); Assert.AreEqual("scripts\\path-2\\test-path-2.js", script.Path); Assert.AreEqual("/scripts/scripts/path-2/test-path-2.js", script.VirtualPath); script = new Script("path-2/test-path-2.js") { Content = "// script" }; repository.Save(script); - + Assert.IsTrue(_fileSystem.FileExists("path-2/test-path-2.js")); Assert.AreEqual("path-2\\test-path-2.js", script.Path); // fixed in 7.3 - 7.2.8 does not update the path Assert.AreEqual("/scripts/path-2/test-path-2.js", script.VirtualPath); @@ -286,7 +285,7 @@ namespace Umbraco.Tests.Persistence.Repositories script = new Script("path-2\\test-path-3.js") { Content = "// script" }; repository.Save(script); - + Assert.IsTrue(_fileSystem.FileExists("path-2/test-path-3.js")); Assert.AreEqual("path-2\\test-path-3.js", script.Path); Assert.AreEqual("/scripts/path-2/test-path-3.js", script.VirtualPath); @@ -328,11 +327,11 @@ namespace Umbraco.Tests.Persistence.Repositories base.TearDown(); //Delete all files - Purge((PhysicalFileSystem) _fileSystem, ""); - _fileSystem = null; + Purge(_fileSystems.ScriptsFileSystem, ""); + _fileSystems = null; } - private void Purge(PhysicalFileSystem fs, string path) + private void Purge(IFileSystem fs, string path) { var files = fs.GetFiles(path, "*.js"); foreach (var file in files) diff --git a/src/Umbraco.Tests/Persistence/Repositories/StylesheetRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/StylesheetRepositoryTest.cs index dd0bc36ff3..6fae1d4749 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/StylesheetRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/StylesheetRepositoryTest.cs @@ -2,6 +2,7 @@ using System.IO; using System.Linq; using System.Text; +using Moq; using NUnit.Framework; using Umbraco.Core.IO; using Umbraco.Core.Models; @@ -15,13 +16,16 @@ namespace Umbraco.Tests.Persistence.Repositories [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] public class StylesheetRepositoryTest : TestWithDatabaseBase { + private IFileSystems _fileSystems; private IFileSystem _fileSystem; public override void SetUp() { base.SetUp(); + _fileSystems = Mock.Of(); _fileSystem = new PhysicalFileSystem(SystemDirectories.Css); + Mock.Get(_fileSystems).Setup(x => x.StylesheetsFileSystem).Returns(_fileSystem); var stream = CreateStream("body {background:#EE7600; color:#FFF;}"); _fileSystem.AddFile("styles.css", stream); } @@ -30,11 +34,10 @@ namespace Umbraco.Tests.Persistence.Repositories public void Can_Instantiate_Repository() { // Arrange - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) + using (ScopeProvider.CreateScope()) { // Act - var repository = new StylesheetRepository(_fileSystem); + var repository = new StylesheetRepository(_fileSystems); // Assert @@ -46,15 +49,14 @@ namespace Umbraco.Tests.Persistence.Repositories public void Can_Perform_Add() { // Arrange - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) + using (ScopeProvider.CreateScope()) { - var repository = new StylesheetRepository(_fileSystem); + var repository = new StylesheetRepository(_fileSystems); // Act var stylesheet = new Stylesheet("test-add.css") { Content = "body { color:#000; } .bold {font-weight:bold;}" }; repository.Save(stylesheet); - + //Assert Assert.That(_fileSystem.FileExists("test-add.css"), Is.True); @@ -65,20 +67,19 @@ namespace Umbraco.Tests.Persistence.Repositories public void Can_Perform_Update() { // Arrange - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) + using (ScopeProvider.CreateScope()) { - var repository = new StylesheetRepository(_fileSystem); + var repository = new StylesheetRepository(_fileSystems); // Act var stylesheet = new Stylesheet("test-update.css") { Content = "body { color:#000; } .bold {font-weight:bold;}" }; repository.Save(stylesheet); - + var stylesheetUpdate = repository.Get("test-update.css"); stylesheetUpdate.Content = "body { color:#000; }"; repository.Save(stylesheetUpdate); - + var stylesheetUpdated = repository.Get("test-update.css"); @@ -93,20 +94,19 @@ namespace Umbraco.Tests.Persistence.Repositories public void Can_Perform_Update_With_Property() { // Arrange - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) + using (ScopeProvider.CreateScope()) { - var repository = new StylesheetRepository(_fileSystem); + var repository = new StylesheetRepository(_fileSystems); // Act var stylesheet = new Stylesheet("test-update.css") { Content = "body { color:#000; } .bold {font-weight:bold;}" }; repository.Save(stylesheet); - + stylesheet.AddProperty(new StylesheetProperty("Test", "p", "font-size:2em;")); repository.Save(stylesheet); - + //re-get stylesheet = repository.Get(stylesheet.Name); @@ -121,15 +121,14 @@ namespace Umbraco.Tests.Persistence.Repositories public void Throws_When_Adding_Duplicate_Properties() { // Arrange - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) + using (ScopeProvider.CreateScope()) { - var repository = new StylesheetRepository(_fileSystem); + var repository = new StylesheetRepository(_fileSystems); // Act var stylesheet = new Stylesheet("test-update.css") { Content = "body { color:#000; } .bold {font-weight:bold;}" }; repository.Save(stylesheet); - + stylesheet.AddProperty(new StylesheetProperty("Test", "p", "font-size:2em;")); @@ -141,18 +140,17 @@ namespace Umbraco.Tests.Persistence.Repositories public void Can_Perform_Delete() { // Arrange - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) + using (ScopeProvider.CreateScope()) { - var repository = new StylesheetRepository(_fileSystem); + var repository = new StylesheetRepository(_fileSystems); // Act var stylesheet = new Stylesheet("test-delete.css") { Content = "body { color:#000; } .bold {font-weight:bold;}" }; repository.Save(stylesheet); - + repository.Delete(stylesheet); - + //Assert Assert.That(_fileSystem.FileExists("test-delete.css"), Is.False); @@ -163,10 +161,9 @@ namespace Umbraco.Tests.Persistence.Repositories public void Can_Perform_Get() { // Arrange - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) + using (ScopeProvider.CreateScope()) { - var repository = new StylesheetRepository(_fileSystem); + var repository = new StylesheetRepository(_fileSystems); // Act var stylesheet = repository.Get("styles.css"); @@ -183,14 +180,13 @@ namespace Umbraco.Tests.Persistence.Repositories public void Can_Perform_GetAll() { // Arrange - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) + using (ScopeProvider.CreateScope()) { - var repository = new StylesheetRepository(_fileSystem); + var repository = new StylesheetRepository(_fileSystems); var stylesheet = new Stylesheet("styles-v2.css") { Content = "body { color:#000; } .bold {font-weight:bold;}" }; repository.Save(stylesheet); - + // Act var stylesheets = repository.GetMany(); @@ -207,14 +203,13 @@ namespace Umbraco.Tests.Persistence.Repositories public void Can_Perform_GetAll_With_Params() { // Arrange - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) + using (ScopeProvider.CreateScope()) { - var repository = new StylesheetRepository(_fileSystem); + var repository = new StylesheetRepository(_fileSystems); var stylesheet = new Stylesheet("styles-v2.css") { Content = "body { color:#000; } .bold {font-weight:bold;}" }; repository.Save(stylesheet); - + // Act var stylesheets = repository.GetMany("styles-v2.css", "styles.css"); @@ -231,10 +226,9 @@ namespace Umbraco.Tests.Persistence.Repositories public void Can_Perform_Exists() { // Arrange - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) + using (ScopeProvider.CreateScope()) { - var repository = new StylesheetRepository(_fileSystem); + var repository = new StylesheetRepository(_fileSystems); // Act var exists = repository.Exists("styles.css"); @@ -249,21 +243,20 @@ namespace Umbraco.Tests.Persistence.Repositories { // unless noted otherwise, no changes / 7.2.8 - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) + using (ScopeProvider.CreateScope()) { - var repository = new StylesheetRepository(_fileSystem); + var repository = new StylesheetRepository(_fileSystems); var stylesheet = new Stylesheet("test-path-1.css") { Content = "body { color:#000; } .bold {font-weight:bold;}" }; repository.Save(stylesheet); - + Assert.IsTrue(_fileSystem.FileExists("test-path-1.css")); Assert.AreEqual("test-path-1.css", stylesheet.Path); Assert.AreEqual("/css/test-path-1.css", stylesheet.VirtualPath); stylesheet = new Stylesheet("path-2/test-path-2.css") { Content = "body { color:#000; } .bold {font-weight:bold;}" }; repository.Save(stylesheet); - + Assert.IsTrue(_fileSystem.FileExists("path-2/test-path-2.css")); Assert.AreEqual("path-2\\test-path-2.css", stylesheet.Path); // fixed in 7.3 - 7.2.8 does not update the path Assert.AreEqual("/css/path-2/test-path-2.css", stylesheet.VirtualPath); @@ -275,7 +268,7 @@ namespace Umbraco.Tests.Persistence.Repositories stylesheet = new Stylesheet("path-2\\test-path-3.css") { Content = "body { color:#000; } .bold {font-weight:bold;}" }; repository.Save(stylesheet); - + Assert.IsTrue(_fileSystem.FileExists("path-2/test-path-3.css")); Assert.AreEqual("path-2\\test-path-3.css", stylesheet.Path); Assert.AreEqual("/css/path-2/test-path-3.css", stylesheet.VirtualPath); diff --git a/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs index d53d680e94..78eb736007 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs @@ -1,6 +1,7 @@ using System.Linq; using Moq; using NUnit.Framework; +using Umbraco.Core.Cache; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; using Umbraco.Core.Models; @@ -946,26 +947,26 @@ namespace Umbraco.Tests.Persistence.Repositories private TagRepository CreateRepository(IScopeProvider provider) { - return new TagRepository((IScopeAccessor) provider, DisabledCache, Logger); + return new TagRepository((IScopeAccessor) provider, CacheHelper.Disabled, Logger); } private DocumentRepository CreateContentRepository(IScopeProvider provider, out ContentTypeRepository contentTypeRepository) { var accessor = (IScopeAccessor) provider; - var templateRepository = new TemplateRepository(accessor, DisabledCache, Logger, Mock.Of(), Mock.Of(), Mock.Of()); - var tagRepository = new TagRepository(accessor, DisabledCache, Logger); - contentTypeRepository = new ContentTypeRepository(accessor, DisabledCache, Logger, templateRepository); - var languageRepository = new LanguageRepository(accessor, DisabledCache, Logger); - var repository = new DocumentRepository(accessor, DisabledCache, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, Mock.Of()); + var templateRepository = new TemplateRepository(accessor, CacheHelper.Disabled, Logger, Mock.Of(), TestObjects.GetFileSystemsMock()); + var tagRepository = new TagRepository(accessor, CacheHelper.Disabled, Logger); + contentTypeRepository = new ContentTypeRepository(accessor, CacheHelper.Disabled, Logger, templateRepository); + var languageRepository = new LanguageRepository(accessor, CacheHelper.Disabled, Logger); + var repository = new DocumentRepository(accessor, CacheHelper.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, Mock.Of()); return repository; } private MediaRepository CreateMediaRepository(IScopeProvider provider, out MediaTypeRepository mediaTypeRepository) { var accessor = (IScopeAccessor) provider; - var tagRepository = new TagRepository(accessor, DisabledCache, Logger); - mediaTypeRepository = new MediaTypeRepository(accessor, DisabledCache, Logger); - var repository = new MediaRepository(accessor, DisabledCache, Logger, mediaTypeRepository, tagRepository, Mock.Of(), Mock.Of()); + var tagRepository = new TagRepository(accessor, CacheHelper.Disabled, Logger); + mediaTypeRepository = new MediaTypeRepository(accessor, CacheHelper.Disabled, Logger); + var repository = new MediaRepository(accessor, CacheHelper.Disabled, Logger, mediaTypeRepository, tagRepository, Mock.Of(), Mock.Of()); return repository; } } diff --git a/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs index 3ef769adbc..f4bed68315 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs @@ -6,6 +6,7 @@ using System.Text; using Moq; using NUnit.Framework; using Umbraco.Core; +using Umbraco.Core.Cache; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; using Umbraco.Core.Models; @@ -22,47 +23,46 @@ namespace Umbraco.Tests.Persistence.Repositories [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] public class TemplateRepositoryTest : TestWithDatabaseBase { - private IFileSystem _masterPageFileSystem; - private IFileSystem _viewsFileSystem; + private IFileSystems _fileSystems; private ITemplateRepository CreateRepository(IScopeProvider provider, ITemplatesSection templatesSection = null) { - return new TemplateRepository((IScopeAccessor) provider, DisabledCache, Logger, + return new TemplateRepository((IScopeAccessor) provider, CacheHelper.Disabled, Logger, templatesSection ?? Mock.Of(t => t.DefaultRenderingEngine == RenderingEngine.Mvc), - _masterPageFileSystem, _viewsFileSystem); + _fileSystems); } public override void SetUp() { base.SetUp(); - _masterPageFileSystem = new PhysicalFileSystem(SystemDirectories.Masterpages); - _viewsFileSystem = new PhysicalFileSystem(SystemDirectories.MvcViews); + _fileSystems = Mock.Of(); + var masterPageFileSystem = new PhysicalFileSystem(SystemDirectories.Masterpages); + Mock.Get(_fileSystems).Setup(x => x.MasterPagesFileSystem).Returns(masterPageFileSystem); + var viewsFileSystem = new PhysicalFileSystem(SystemDirectories.MvcViews); + Mock.Get(_fileSystems).Setup(x => x.MvcViewsFileSystem).Returns(viewsFileSystem); } [Test] public void Can_Instantiate_Repository() { // Arrange - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) + using (ScopeProvider.CreateScope()) { - var repository = CreateRepository(provider); + var repository = CreateRepository(ScopeProvider); // Assert Assert.That(repository, Is.Not.Null); } - } [Test] public void Can_Perform_Add_MasterPage_Detect_Content() { // Arrange - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) + using (ScopeProvider.CreateScope()) { - var repository = CreateRepository(provider); + var repository = CreateRepository(ScopeProvider); // Act var template = new Template("test", "test") @@ -70,32 +70,28 @@ namespace Umbraco.Tests.Persistence.Repositories Content = @"<%@ Master Language=""C#"" %>" }; repository.Save(template); - //Assert Assert.That(repository.Get("test"), Is.Not.Null); - Assert.That(_masterPageFileSystem.FileExists("test.master"), Is.True); + Assert.That(_fileSystems.MasterPagesFileSystem.FileExists("test.master"), Is.True); } - } [Test] public void Can_Perform_Add_MasterPage_With_Default_Content() { // Arrange - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) + using (ScopeProvider.CreateScope()) { - var repository = CreateRepository(provider, Mock.Of(x => x.DefaultRenderingEngine == RenderingEngine.WebForms)); + var repository = CreateRepository(ScopeProvider, Mock.Of(x => x.DefaultRenderingEngine == RenderingEngine.WebForms)); // Act var template = new Template("test", "test"); repository.Save(template); - //Assert Assert.That(repository.Get("test"), Is.Not.Null); - Assert.That(_masterPageFileSystem.FileExists("test.master"), Is.True); + Assert.That(_fileSystems.MasterPagesFileSystem.FileExists("test.master"), Is.True); Assert.AreEqual(@"<%@ Master Language=""C#"" MasterPageFile=""~/umbraco/masterpages/default.master"" AutoEventWireup=""true"" %> @@ -103,47 +99,41 @@ namespace Umbraco.Tests.Persistence.Repositories ".StripWhitespace(), template.Content.StripWhitespace()); } - } [Test] public void Can_Perform_Add_MasterPage_With_Default_Content_With_Parent() { // Arrange - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) + using (ScopeProvider.CreateScope()) { - var repository = CreateRepository(provider, Mock.Of(x => x.DefaultRenderingEngine == RenderingEngine.WebForms)); + var repository = CreateRepository(ScopeProvider, Mock.Of(x => x.DefaultRenderingEngine == RenderingEngine.WebForms)); //NOTE: This has to be persisted first var template = new Template("test", "test"); repository.Save(template); - // Act var template2 = new Template("test2", "test2"); template2.SetMasterTemplate(template); repository.Save(template2); - //Assert Assert.That(repository.Get("test2"), Is.Not.Null); - Assert.That(_masterPageFileSystem.FileExists("test2.master"), Is.True); + Assert.That(_fileSystems.MasterPagesFileSystem.FileExists("test2.master"), Is.True); Assert.AreEqual(@"<%@ Master Language=""C#"" MasterPageFile=""~/masterpages/test.master"" AutoEventWireup=""true"" %> ".StripWhitespace(), template2.Content.StripWhitespace()); } - } [Test] public void Can_Perform_Add_View() { // Arrange - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) + using (ScopeProvider.CreateScope()) { - var repository = CreateRepository(provider); + var repository = CreateRepository(ScopeProvider); // Act var template = new Template("test", "test"); @@ -152,19 +142,17 @@ namespace Umbraco.Tests.Persistence.Repositories //Assert Assert.That(repository.Get("test"), Is.Not.Null); - Assert.That(_viewsFileSystem.FileExists("test.cshtml"), Is.True); + Assert.That(_fileSystems.MvcViewsFileSystem.FileExists("test.cshtml"), Is.True); } - } [Test] public void Can_Perform_Add_View_With_Default_Content() { // Arrange - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) + using (ScopeProvider.CreateScope()) { - var repository = CreateRepository(provider); + var repository = CreateRepository(ScopeProvider); // Act var template = new Template("test", "test") @@ -172,41 +160,36 @@ namespace Umbraco.Tests.Persistence.Repositories Content = ViewHelper.GetDefaultFileContent() }; repository.Save(template); - //Assert Assert.That(repository.Get("test"), Is.Not.Null); - Assert.That(_viewsFileSystem.FileExists("test.cshtml"), Is.True); + Assert.That(_fileSystems.MvcViewsFileSystem.FileExists("test.cshtml"), Is.True); Assert.AreEqual( @"@inherits Umbraco.Web.Mvc.UmbracoViewPage @{ Layout = null;}".StripWhitespace(), template.Content.StripWhitespace()); } - } [Test] public void Can_Perform_Add_View_With_Default_Content_With_Parent() { // Arrange - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) + using (ScopeProvider.CreateScope()) { - var repository = CreateRepository(provider); + var repository = CreateRepository(ScopeProvider); //NOTE: This has to be persisted first var template = new Template("test", "test"); repository.Save(template); - // Act var template2 = new Template("test2", "test2"); template2.SetMasterTemplate(template); repository.Save(template2); - //Assert Assert.That(repository.Get("test2"), Is.Not.Null); - Assert.That(_viewsFileSystem.FileExists("test2.cshtml"), Is.True); + Assert.That(_fileSystems.MvcViewsFileSystem.FileExists("test2.cshtml"), Is.True); Assert.AreEqual( "@inherits Umbraco.Web.Mvc.UmbracoViewPage @{ Layout = \"test.cshtml\";}".StripWhitespace(), template2.Content.StripWhitespace()); @@ -217,10 +200,9 @@ namespace Umbraco.Tests.Persistence.Repositories public void Can_Perform_Add_Unique_Alias() { // Arrange - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) + using (ScopeProvider.CreateScope()) { - var repository = CreateRepository(provider); + var repository = CreateRepository(ScopeProvider); // Act var template = new Template("test", "test") @@ -228,29 +210,25 @@ namespace Umbraco.Tests.Persistence.Repositories Content = ViewHelper.GetDefaultFileContent() }; repository.Save(template); - var template2 = new Template("test", "test") { Content = ViewHelper.GetDefaultFileContent() }; repository.Save(template2); - //Assert Assert.AreEqual("test1", template2.Alias); } - } [Test] public void Can_Perform_Update_Unique_Alias() { // Arrange - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) + using (ScopeProvider.CreateScope()) { - var repository = CreateRepository(provider); + var repository = CreateRepository(ScopeProvider); // Act var template = new Template("test", "test") @@ -258,35 +236,30 @@ namespace Umbraco.Tests.Persistence.Repositories Content = ViewHelper.GetDefaultFileContent() }; repository.Save(template); - var template2 = new Template("test1", "test1") { Content = ViewHelper.GetDefaultFileContent() }; repository.Save(template2); - template.Alias = "test1"; repository.Save(template); - //Assert Assert.AreEqual("test11", template.Alias); - Assert.That(_viewsFileSystem.FileExists("test11.cshtml"), Is.True); - Assert.That(_viewsFileSystem.FileExists("test.cshtml"), Is.False); + Assert.That(_fileSystems.MvcViewsFileSystem.FileExists("test11.cshtml"), Is.True); + Assert.That(_fileSystems.MvcViewsFileSystem.FileExists("test.cshtml"), Is.False); } - } [Test] public void Can_Perform_Update_MasterPage() { // Arrange - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) + using (ScopeProvider.CreateScope()) { - var repository = CreateRepository(provider); + var repository = CreateRepository(ScopeProvider); // Act var template = new Template("test", "test") @@ -294,30 +267,25 @@ namespace Umbraco.Tests.Persistence.Repositories Content = @"<%@ Master Language=""C#"" %>" }; repository.Save(template); - template.Content = @"<%@ Master Language=""VB"" %>"; repository.Save(template); - var updated = repository.Get("test"); // Assert - Assert.That(_masterPageFileSystem.FileExists("test.master"), Is.True); + Assert.That(_fileSystems.MasterPagesFileSystem.FileExists("test.master"), Is.True); Assert.That(updated.Content, Is.EqualTo(@"<%@ Master Language=""VB"" %>")); } - - } [Test] public void Can_Perform_Update_View() { // Arrange - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) + using (ScopeProvider.CreateScope()) { - var repository = CreateRepository(provider); + var repository = CreateRepository(ScopeProvider); // Act var template = new Template("test", "test") @@ -325,16 +293,14 @@ namespace Umbraco.Tests.Persistence.Repositories Content = ViewHelper.GetDefaultFileContent() }; repository.Save(template); - template.Content += ""; repository.Save(template); - var updated = repository.Get("test"); // Assert - Assert.That(_viewsFileSystem.FileExists("test.cshtml"), Is.True); + Assert.That(_fileSystems.MvcViewsFileSystem.FileExists("test.cshtml"), Is.True); Assert.That(updated.Content, Is.EqualTo(ViewHelper.GetDefaultFileContent() + "")); } } @@ -343,27 +309,24 @@ namespace Umbraco.Tests.Persistence.Repositories public void Can_Perform_Delete_MasterPage() { // Arrange - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) + using (ScopeProvider.CreateScope()) { - var repository = CreateRepository(provider); + var repository = CreateRepository(ScopeProvider); var template = new Template("test", "test") { Content = @"<%@ Master Language=""C#"" %>" }; repository.Save(template); - // Act var templates = repository.Get("test"); - Assert.That(_masterPageFileSystem.FileExists("test.master"), Is.True); + Assert.That(_fileSystems.MasterPagesFileSystem.FileExists("test.master"), Is.True); repository.Delete(templates); - // Assert Assert.IsNull(repository.Get("test")); - Assert.That(_masterPageFileSystem.FileExists("test.master"), Is.False); + Assert.That(_fileSystems.MasterPagesFileSystem.FileExists("test.master"), Is.False); } } @@ -371,27 +334,24 @@ namespace Umbraco.Tests.Persistence.Repositories public void Can_Perform_Delete_View() { // Arrange - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) + using (ScopeProvider.CreateScope()) { - var repository = CreateRepository(provider); + var repository = CreateRepository(ScopeProvider); var template = new Template("test", "test") { Content = ViewHelper.GetDefaultFileContent() }; repository.Save(template); - // Act var templates = repository.Get("test"); - Assert.That(_viewsFileSystem.FileExists("test.cshtml"), Is.True); + Assert.That(_fileSystems.MvcViewsFileSystem.FileExists("test.cshtml"), Is.True); repository.Delete(templates); - // Assert Assert.IsNull(repository.Get("test")); - Assert.That(_viewsFileSystem.FileExists("test.cshtml"), Is.False); + Assert.That(_fileSystems.MvcViewsFileSystem.FileExists("test.cshtml"), Is.False); } } @@ -399,39 +359,33 @@ namespace Umbraco.Tests.Persistence.Repositories public void Can_Perform_Delete_When_Assigned_To_Doc() { // Arrange - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) + using (ScopeProvider.CreateScope()) { - var templateRepository = CreateRepository(provider); + var templateRepository = CreateRepository(ScopeProvider); - var tagRepository = new TagRepository((IScopeAccessor) provider, DisabledCache, Logger); - var contentTypeRepository = new ContentTypeRepository((IScopeAccessor) provider, DisabledCache, Logger, templateRepository); - var languageRepository = new LanguageRepository((IScopeAccessor) provider, DisabledCache, Logger); - var contentRepo = new DocumentRepository((IScopeAccessor) provider, DisabledCache, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, Mock.Of()); + var tagRepository = new TagRepository((IScopeAccessor) ScopeProvider, CacheHelper.Disabled, Logger); + var contentTypeRepository = new ContentTypeRepository((IScopeAccessor) ScopeProvider, CacheHelper.Disabled, Logger, templateRepository); + var languageRepository = new LanguageRepository((IScopeAccessor) ScopeProvider, CacheHelper.Disabled, Logger); + var contentRepo = new DocumentRepository((IScopeAccessor) ScopeProvider, CacheHelper.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, Mock.Of()); var contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage2", "Textpage"); ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! contentTypeRepository.Save(contentType); var textpage = MockedContent.CreateSimpleContent(contentType); contentRepo.Save(textpage); - - var template = new Template("test", "test") { Content = @"<%@ Master Language=""C#"" %>" }; templateRepository.Save(template); - - textpage.Template = template; + textpage.TemplateId = template.Id; contentRepo.Save(textpage); - // Act var templates = templateRepository.Get("test"); templateRepository.Delete(templates); - // Assert Assert.IsNull(templateRepository.Get("test")); @@ -442,10 +396,9 @@ namespace Umbraco.Tests.Persistence.Repositories public void Can_Perform_Delete_On_Nested_Templates() { // Arrange - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) + using (ScopeProvider.CreateScope()) { - var repository = CreateRepository(provider); + var repository = CreateRepository(ScopeProvider); var parent = new Template("parent", "parent") { @@ -466,12 +419,10 @@ namespace Umbraco.Tests.Persistence.Repositories repository.Save(parent); repository.Save(child); repository.Save(baby); - // Act var templates = repository.Get("parent"); repository.Delete(templates); - // Assert Assert.IsNull(repository.Get("test")); @@ -482,10 +433,9 @@ namespace Umbraco.Tests.Persistence.Repositories public void Can_Get_All() { // Arrange - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) + using (ScopeProvider.CreateScope()) { - var repository = CreateRepository(provider); + var repository = CreateRepository(ScopeProvider); var created = CreateHierarchy(repository).ToArray(); @@ -510,10 +460,9 @@ namespace Umbraco.Tests.Persistence.Repositories public void Can_Get_Children() { // Arrange - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) + using (ScopeProvider.CreateScope()) { - var repository = CreateRepository(provider); + var repository = CreateRepository(ScopeProvider); var created = CreateHierarchy(repository).ToArray(); @@ -521,7 +470,6 @@ namespace Umbraco.Tests.Persistence.Repositories var childrenById = repository.GetChildren(created[1].Id); var childrenByAlias = repository.GetChildren(created[1].Alias); - // Assert Assert.AreEqual(2, childrenById.Count()); Assert.AreEqual(2, childrenById.DistinctBy(x => x.Id).Count()); @@ -534,17 +482,15 @@ namespace Umbraco.Tests.Persistence.Repositories public void Can_Get_Children_At_Root() { // Arrange - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) + using (ScopeProvider.CreateScope()) { - var repository = CreateRepository(provider); + var repository = CreateRepository(ScopeProvider); CreateHierarchy(repository).ToArray(); // Act var children = repository.GetChildren(-1); - // Assert Assert.AreEqual(1, children.Count()); Assert.AreEqual(1, children.DistinctBy(x => x.Id).Count()); @@ -555,18 +501,15 @@ namespace Umbraco.Tests.Persistence.Repositories public void Can_Get_Descendants() { // Arrange - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) + using (ScopeProvider.CreateScope()) { - var repository = CreateRepository(provider); - + var repository = CreateRepository(ScopeProvider); var created = CreateHierarchy(repository).ToArray(); // Act var descendantsById = repository.GetDescendants(created[1].Id); var descendantsByAlias = repository.GetDescendants(created[1].Alias); - // Assert Assert.AreEqual(3, descendantsById.Count()); Assert.AreEqual(3, descendantsById.DistinctBy(x => x.Id).Count()); @@ -580,10 +523,9 @@ namespace Umbraco.Tests.Persistence.Repositories public void Path_Is_Set_Correctly_On_Creation() { // Arrange - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) + using (ScopeProvider.CreateScope()) { - var repository = CreateRepository(provider); + var repository = CreateRepository(ScopeProvider); var parent = new Template("parent", "parent"); var child1 = new Template("child1", "child1"); @@ -612,7 +554,6 @@ namespace Umbraco.Tests.Persistence.Repositories baby2.MasterTemplateAlias = toddler4.Alias; baby2.MasterTemplateId = new Lazy(() => toddler4.Id); - // Act repository.Save(parent); repository.Save(child1); @@ -623,7 +564,6 @@ namespace Umbraco.Tests.Persistence.Repositories repository.Save(toddler4); repository.Save(baby1); repository.Save(baby2); - // Assert Assert.AreEqual(string.Format("-1,{0}", parent.Id), parent.Path); @@ -643,10 +583,9 @@ namespace Umbraco.Tests.Persistence.Repositories public void Path_Is_Set_Correctly_On_Update() { // Arrange - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) + using (ScopeProvider.CreateScope()) { - var repository = CreateRepository(provider); + var repository = CreateRepository(ScopeProvider); var parent = new Template("parent", "parent"); var child1 = new Template("child1", "child1"); @@ -668,12 +607,10 @@ namespace Umbraco.Tests.Persistence.Repositories repository.Save(child2); repository.Save(toddler1); repository.Save(toddler2); - //Act toddler2.SetMasterTemplate(child2); repository.Save(toddler2); - //Assert Assert.AreEqual($"-1,{parent.Id},{child2.Id},{toddler2.Id}", toddler2.Path); @@ -684,10 +621,9 @@ namespace Umbraco.Tests.Persistence.Repositories public void Path_Is_Set_Correctly_On_Update_With_Master_Template_Removal() { // Arrange - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = ScopeProvider.CreateScope()) + using (ScopeProvider.CreateScope()) { - var repository = CreateRepository(provider); + var repository = CreateRepository(ScopeProvider); var parent = new Template("parent", "parent"); var child1 = new Template("child1", "child1"); @@ -697,12 +633,10 @@ namespace Umbraco.Tests.Persistence.Repositories repository.Save(parent); repository.Save(child1); - //Act child1.SetMasterTemplate(null); repository.Save(child1); - //Assert Assert.AreEqual($"-1,{child1.Id}", child1.Path); @@ -714,21 +648,18 @@ namespace Umbraco.Tests.Persistence.Repositories { base.TearDown(); - _masterPageFileSystem = null; - _viewsFileSystem = null; + _fileSystems = null; + //Delete all files var fsMaster = new PhysicalFileSystem(SystemDirectories.Masterpages); var masterPages = fsMaster.GetFiles("", "*.master"); foreach (var file in masterPages) - { fsMaster.DeleteFile(file); - } + var fsViews = new PhysicalFileSystem(SystemDirectories.MvcViews); - var views = fsMaster.GetFiles("", "*.cshtml"); + var views = fsViews.GetFiles("", "*.cshtml"); foreach (var file in views) - { - fsMaster.DeleteFile(file); - } + fsViews.DeleteFile(file); } protected Stream CreateStream(string contents = null) diff --git a/src/Umbraco.Tests/Persistence/Repositories/UserGroupRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/UserGroupRepositoryTest.cs index aeec6065df..f0fb8cff88 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/UserGroupRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/UserGroupRepositoryTest.cs @@ -19,7 +19,7 @@ namespace Umbraco.Tests.Persistence.Repositories { private UserGroupRepository CreateRepository(IScopeProvider provider) { - return new UserGroupRepository((IScopeAccessor) provider, Core.Cache.CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); + return new UserGroupRepository((IScopeAccessor) provider, Core.Cache.CacheHelper.Disabled, Mock.Of()); } [Test] @@ -131,7 +131,7 @@ namespace Umbraco.Tests.Persistence.Repositories var id = userGroup.Id; - var repository2 = new UserGroupRepository((IScopeAccessor) provider, Core.Cache.CacheHelper.CreateDisabledCacheHelper(), Logger); + var repository2 = new UserGroupRepository((IScopeAccessor) provider, Core.Cache.CacheHelper.Disabled, Logger); repository2.Delete(userGroup); scope.Complete(); diff --git a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs index e778f987eb..847972cc50 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs @@ -41,7 +41,7 @@ namespace Umbraco.Tests.Persistence.Repositories private DocumentRepository CreateContentRepository(IScopeProvider provider, out IContentTypeRepository contentTypeRepository, out ITemplateRepository templateRepository) { var accessor = (IScopeAccessor) provider; - templateRepository = new TemplateRepository(accessor, CacheHelper, Logger, Mock.Of(), Mock.Of(), Mock.Of()); + templateRepository = new TemplateRepository(accessor, CacheHelper, Logger, Mock.Of(), TestObjects.GetFileSystemsMock()); var tagRepository = new TagRepository(accessor, CacheHelper, Logger); contentTypeRepository = new ContentTypeRepository(accessor, CacheHelper, Logger, templateRepository); var languageRepository = new LanguageRepository(accessor, CacheHelper, Logger); @@ -52,14 +52,14 @@ namespace Umbraco.Tests.Persistence.Repositories private UserRepository CreateRepository(IScopeProvider provider) { var accessor = (IScopeAccessor) provider; - var repository = new UserRepository(accessor, CacheHelper.CreateDisabledCacheHelper(), Logger, Mappers, TestObjects.GetGlobalSettings()); + var repository = new UserRepository(accessor, CacheHelper.Disabled, Logger, Mappers, TestObjects.GetGlobalSettings()); return repository; } private UserGroupRepository CreateUserGroupRepository(IScopeProvider provider) { var accessor = (IScopeAccessor) provider; - return new UserGroupRepository(accessor, CacheHelper.CreateDisabledCacheHelper(), Logger); + return new UserGroupRepository(accessor, CacheHelper.Disabled, Logger); } [Test] @@ -207,7 +207,7 @@ namespace Umbraco.Tests.Persistence.Repositories var id = user.Id; - var repository2 = new UserRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Logger, Mock.Of(),TestObjects.GetGlobalSettings()); + var repository2 = new UserRepository((IScopeAccessor) provider, CacheHelper.Disabled, Logger, Mock.Of(),TestObjects.GetGlobalSettings()); repository2.Delete(user); diff --git a/src/Umbraco.Tests/Persistence/SyntaxProvider/SqlCeSyntaxProviderTests.cs b/src/Umbraco.Tests/Persistence/SyntaxProvider/SqlCeSyntaxProviderTests.cs index df842dc43c..557de9eb11 100644 --- a/src/Umbraco.Tests/Persistence/SyntaxProvider/SqlCeSyntaxProviderTests.cs +++ b/src/Umbraco.Tests/Persistence/SyntaxProvider/SqlCeSyntaxProviderTests.cs @@ -74,7 +74,7 @@ WHERE (([umbracoNode].[nodeObjectType] = @0))) x)".Replace(Environment.NewLine, [Test] public void Format_SqlServer_NonClusteredIndexDefinition_AddsNonClusteredDirective() { - var sqlSyntax = new SqlServerSyntaxProvider(new Lazy(() => null)); + var sqlSyntax = new SqlServerSyntaxProvider(); var indexDefinition = CreateIndexDefinition(); indexDefinition.IndexType = IndexTypes.NonClustered; @@ -86,7 +86,7 @@ WHERE (([umbracoNode].[nodeObjectType] = @0))) x)".Replace(Environment.NewLine, [Test] public void Format_SqlServer_NonClusteredIndexDefinition_UsingIsClusteredFalse_AddsClusteredDirective() { - var sqlSyntax = new SqlServerSyntaxProvider(new Lazy(() => null)); + var sqlSyntax = new SqlServerSyntaxProvider(); var indexDefinition = CreateIndexDefinition(); indexDefinition.IndexType = IndexTypes.Clustered; @@ -99,7 +99,7 @@ WHERE (([umbracoNode].[nodeObjectType] = @0))) x)".Replace(Environment.NewLine, public void CreateIndexBuilder_SqlServer_NonClustered_CreatesNonClusteredIndex() { var logger = Mock.Of(); - var sqlSyntax = new SqlServerSyntaxProvider(new Lazy(() => null)); + var sqlSyntax = new SqlServerSyntaxProvider(); var db = new TestDatabase(DatabaseType.SqlServer2005, sqlSyntax); var context = new MigrationContext(db, logger); @@ -120,7 +120,7 @@ WHERE (([umbracoNode].[nodeObjectType] = @0))) x)".Replace(Environment.NewLine, public void CreateIndexBuilder_SqlServer_Unique_CreatesUniqueNonClusteredIndex() { var logger = Mock.Of(); - var sqlSyntax = new SqlServerSyntaxProvider(new Lazy(() => null)); + var sqlSyntax = new SqlServerSyntaxProvider(); var db = new TestDatabase(DatabaseType.SqlServer2005, sqlSyntax); var context = new MigrationContext(db, logger); @@ -141,7 +141,7 @@ WHERE (([umbracoNode].[nodeObjectType] = @0))) x)".Replace(Environment.NewLine, public void CreateIndexBuilder_SqlServer_Unique_CreatesUniqueNonClusteredIndex_Multi_Columnn() { var logger = Mock.Of(); - var sqlSyntax = new SqlServerSyntaxProvider(new Lazy(() => null)); + var sqlSyntax = new SqlServerSyntaxProvider(); var db = new TestDatabase(DatabaseType.SqlServer2005, sqlSyntax); var context = new MigrationContext(db, logger); @@ -162,7 +162,7 @@ WHERE (([umbracoNode].[nodeObjectType] = @0))) x)".Replace(Environment.NewLine, public void CreateIndexBuilder_SqlServer_Clustered_CreatesClusteredIndex() { var logger = Mock.Of(); - var sqlSyntax = new SqlServerSyntaxProvider(new Lazy(() => null)); + var sqlSyntax = new SqlServerSyntaxProvider(); var db = new TestDatabase(DatabaseType.SqlServer2005, sqlSyntax); var context = new MigrationContext(db, logger); diff --git a/src/Umbraco.Tests/PropertyEditors/ImageCropperTest.cs b/src/Umbraco.Tests/PropertyEditors/ImageCropperTest.cs index 1f6b569a0e..c55da764e2 100644 --- a/src/Umbraco.Tests/PropertyEditors/ImageCropperTest.cs +++ b/src/Umbraco.Tests/PropertyEditors/ImageCropperTest.cs @@ -1,21 +1,23 @@ using System; using System.Globalization; -using LightInject; using Moq; using Newtonsoft.Json; using NUnit.Framework; using Newtonsoft.Json.Linq; using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Components; using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; -using Umbraco.Core.IO.MediaPathSchemes; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; using Umbraco.Core.PropertyEditors.ValueConverters; using Umbraco.Core.Services; +using Umbraco.Tests.Components; using Umbraco.Tests.TestHelpers; using Umbraco.Web.Models; using Umbraco.Web; @@ -68,14 +70,18 @@ namespace Umbraco.Tests.PropertyEditors { try { - var container = new ServiceContainer(); - container.ConfigureUmbracoCore(); - container.RegisterCollectionBuilder(); + var container = RegisterFactory.Create(); + var composition = new Composition(container, new TypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); - container.Register(f => Mock.Of()); - container.Register(f => Mock.Of()); - container.RegisterSingleton(); - var mediaFileSystem = new MediaFileSystem(Mock.Of()); + composition.WithCollectionBuilder(); + + Current.Factory = composition.CreateFactory(); + + var logger = Mock.Of(); + var scheme = Mock.Of(); + var config = Mock.Of(); + + var mediaFileSystem = new MediaFileSystem(Mock.Of(), config, scheme, logger); var dataTypeService = new TestObjects.TestDataTypeService( new DataType(new ImageCropperPropertyEditor(Mock.Of(), mediaFileSystem, Mock.Of(), Mock.Of())) { Id = 1 }); diff --git a/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueEditorTests.cs b/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueEditorTests.cs index f7d09bd2c0..ea5fbcaa06 100644 --- a/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueEditorTests.cs +++ b/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueEditorTests.cs @@ -1,14 +1,16 @@ using System; using System.Threading; -using LightInject; using Moq; using NUnit.Framework; using Umbraco.Core; +using Umbraco.Core.Components; using Umbraco.Core.Composing; +using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Core.Strings; +using Umbraco.Tests.Components; using Umbraco.Tests.TestHelpers; namespace Umbraco.Tests.PropertyEditors @@ -22,10 +24,13 @@ namespace Umbraco.Tests.PropertyEditors //normalize culture Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture; - var container = new ServiceContainer(); - container.ConfigureUmbracoCore(); - container.Register(_ + var register = RegisterFactory.Create(); + var composition = new Composition(register, new TypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); + + register.Register(_ => new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()))); + + Current.Factory = composition.CreateFactory(); } [TearDown] diff --git a/src/Umbraco.Tests/Published/ConvertersTests.cs b/src/Umbraco.Tests/Published/ConvertersTests.cs index edc4face17..eb0f1a9368 100644 --- a/src/Umbraco.Tests/Published/ConvertersTests.cs +++ b/src/Umbraco.Tests/Published/ConvertersTests.cs @@ -1,15 +1,18 @@ using System; using System.Collections.Generic; using System.Linq; -using LightInject; using Moq; using NUnit.Framework; using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Components; using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; +using Umbraco.Tests.Components; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Web; @@ -172,10 +175,11 @@ namespace Umbraco.Tests.Published public void SimpleConverter3Test() { Current.Reset(); - var container = new ServiceContainer(); - container.ConfigureUmbracoCore(); + var register = RegisterFactory.Create(); - Current.Container.RegisterCollectionBuilder() + var composition = new Composition(register, new TypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); + + composition.WithCollectionBuilder() .Append() .Append(); @@ -184,7 +188,9 @@ namespace Umbraco.Tests.Published typeof (PublishedSnapshotTestObjects.TestElementModel1), typeof (PublishedSnapshotTestObjects.TestElementModel2), typeof (PublishedSnapshotTestObjects.TestContentModel1), typeof (PublishedSnapshotTestObjects.TestContentModel2), }); - Current.Container.Register(f => factory); + register.Register(f => factory); + + Current.Factory = composition.CreateFactory(); var cacheMock = new Mock(); var cacheContent = new Dictionary(); @@ -193,9 +199,9 @@ namespace Umbraco.Tests.Published publishedSnapshotMock.Setup(x => x.Content).Returns(cacheMock.Object); var publishedSnapshotAccessorMock = new Mock(); publishedSnapshotAccessorMock.Setup(x => x.PublishedSnapshot).Returns(publishedSnapshotMock.Object); - Current.Container.Register(f => publishedSnapshotAccessorMock.Object); + register.Register(f => publishedSnapshotAccessorMock.Object); - var converters = Current.Container.GetInstance(); + var converters = Current.Factory.GetInstance(); var dataTypeService = new TestObjects.TestDataTypeService( new DataType(new VoidEditor(Mock.Of())) { Id = 1 }, diff --git a/src/Umbraco.Tests/Published/NestedContentTests.cs b/src/Umbraco.Tests/Published/NestedContentTests.cs index cf00345b65..fc1b4d8f3c 100644 --- a/src/Umbraco.Tests/Published/NestedContentTests.cs +++ b/src/Umbraco.Tests/Published/NestedContentTests.cs @@ -270,7 +270,7 @@ namespace Umbraco.Tests.Published // ReSharper disable UnassignedGetOnlyAutoProperty public override int Id { get; } - public override int TemplateId { get; } + public override int? TemplateId { get; } public override int SortOrder { get; } public override string Name { get; } public override PublishedCultureInfo GetCulture(string culture = ".") => throw new NotSupportedException(); diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs index 8e3f99c543..1f596b95c4 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs @@ -4,6 +4,7 @@ using System.Data; using Moq; using NUnit.Framework; using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Events; using Umbraco.Core.Logging; @@ -34,8 +35,11 @@ namespace Umbraco.Tests.PublishedContent // this test implements a full standalone NuCache (based upon a test IDataSource, does not // use any local db files, does not rely on any database) - and tests variations - SettingsForTests.ConfigureSettings(SettingsForTests.GenerateMockUmbracoSettings()); - var globalSettings = UmbracoConfig.For.GlobalSettings(); + Current.Reset(); + Current.UnlockConfigs(); + Current.Configs.Add(SettingsForTests.GenerateMockUmbracoSettings); + Current.Configs.Add(() => new GlobalSettings()); + var globalSettings = Current.Configs.Global(); // create a content node kit var kit = new ContentNodeKit @@ -101,7 +105,7 @@ namespace Umbraco.Tests.PublishedContent Mock.Get(dataTypeService).Setup(x => x.GetAll()).Returns(dataTypes); // create a service context - var serviceContext = new ServiceContext( + var serviceContext = ServiceContext.CreatePartial( dataTypeService : dataTypeService, memberTypeService: Mock.Of(), memberService: Mock.Of(), diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs index aa9e7e4918..3af930c4c3 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs @@ -201,7 +201,7 @@ namespace Umbraco.Tests.PublishedContent public IPublishedContent Parent { get; set; } public int Id { get; set; } public Guid Key { get; set; } - public int TemplateId { get; set; } + public int? TemplateId { get; set; } public int SortOrder { get; set; } public string Name { get; set; } public PublishedCultureInfo GetCulture(string culture = null) => throw new NotSupportedException(); diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs index 4b431d18e6..7a96f670e6 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs @@ -2,13 +2,10 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; -using System.Reflection; using Moq; using NUnit.Framework; -using Umbraco.Core.Composing; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Tests.Testing; using Umbraco.Web; @@ -23,12 +20,12 @@ namespace Umbraco.Tests.PublishedContent { base.Compose(); - Container.RegisterSingleton(_ => GetServiceContext()); + Composition.RegisterUnique(_ => GetServiceContext()); } protected ServiceContext GetServiceContext() { - var serviceContext = TestObjects.GetServiceContextMock(Container); + var serviceContext = TestObjects.GetServiceContextMock(Factory); MockLocalizationService(serviceContext); return serviceContext; } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs index 2732fb465a..c5b8e21870 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs @@ -3,6 +3,7 @@ using System.Linq; using NUnit.Framework; using Umbraco.Core.Models.PublishedContent; using Umbraco.Web; +using Umbraco.Core; using Umbraco.Tests.Testing; namespace Umbraco.Tests.PublishedContent diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs index 85432768f0..6b280832da 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs @@ -1,6 +1,5 @@ using System; using System.Linq; -using System.Collections.ObjectModel; using System.Web.Routing; using Moq; using Umbraco.Core.Models.PublishedContent; @@ -10,7 +9,8 @@ using Umbraco.Web.Routing; using Umbraco.Web.Security; using Umbraco.Core.Composing; using Current = Umbraco.Core.Composing.Current; -using LightInject; +using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; @@ -37,12 +37,12 @@ namespace Umbraco.Tests.PublishedContent { base.Compose(); - Container.RegisterSingleton(f => new PublishedModelFactory(f.GetInstance().GetTypes())); + Composition.RegisterUnique(f => new PublishedModelFactory(f.GetInstance().GetTypes())); } - protected override TypeLoader CreateTypeLoader(IServiceFactory f) + protected override TypeLoader CreateTypeLoader(IRuntimeCacheProvider runtimeCache, IGlobalSettings globalSettings, IProfilingLogger logger) { - var pluginManager = base.CreateTypeLoader(f); + var pluginManager = base.CreateTypeLoader(runtimeCache, globalSettings, logger); // this is so the model factory looks into the test assembly pluginManager.AssembliesToScan = pluginManager.AssembliesToScan diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs index b1fbc43f08..d18c6b6668 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs @@ -1,14 +1,12 @@ using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; using Umbraco.Core.PropertyEditors.ValueConverters; using Umbraco.Tests.TestHelpers; -using LightInject; using Moq; +using Umbraco.Core.Composing; using Umbraco.Core.Logging; using Umbraco.Core.Models; -using Umbraco.Core.Services; using Umbraco.Web.PropertyEditors; namespace Umbraco.Tests.PublishedContent @@ -25,10 +23,8 @@ namespace Umbraco.Tests.PublishedContent // fixme - what about the if (PropertyValueConvertersResolver.HasCurrent == false) ?? // can we risk double - registering and then, what happens? - var builder = Container.TryGetInstance() - ?? Container.RegisterCollectionBuilder(); - - builder.Clear() + Composition.WithCollectionBuilder() + .Clear() .Append() .Append() .Append(); @@ -38,7 +34,7 @@ namespace Umbraco.Tests.PublishedContent { base.Initialize(); - var converters = Container.GetInstance(); + var converters = Factory.GetInstance(); var dataTypeService = new TestObjects.TestDataTypeService( new DataType(new RichTextPropertyEditor(Mock.Of())) { Id = 1 }); diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index ed7affa79e..914956dce1 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -9,8 +9,9 @@ using Umbraco.Core.PropertyEditors; using Umbraco.Web; using Umbraco.Web.PublishedCache; using Umbraco.Core.Composing; -using LightInject; using Moq; +using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Services; @@ -32,9 +33,9 @@ namespace Umbraco.Tests.PublishedContent { base.Compose(); - Container.RegisterSingleton(f => new PublishedModelFactory(f.GetInstance().GetTypes())); - Container.RegisterSingleton(); - Container.RegisterSingleton(); + Composition.RegisterUnique(f => new PublishedModelFactory(f.GetInstance().GetTypes())); + Composition.RegisterUnique(); + Composition.RegisterUnique(); var logger = Mock.Of(); var dataTypeService = new TestObjects.TestDataTypeService( @@ -44,14 +45,14 @@ namespace Umbraco.Tests.PublishedContent new DataType(new IntegerPropertyEditor(logger)) { Id = 1003 }, new DataType(new TextboxPropertyEditor(logger)) { Id = 1004 }, new DataType(new MediaPickerPropertyEditor(logger)) { Id = 1005 }); - Container.RegisterSingleton(f => dataTypeService); + Composition.RegisterUnique(f => dataTypeService); } protected override void Initialize() { base.Initialize(); - var factory = Container.GetInstance() as PublishedContentTypeFactory; + var factory = Factory.GetInstance() as PublishedContentTypeFactory; // need to specify a custom callback for unit tests // AutoPublishedContentTypes generates properties automatically @@ -72,9 +73,9 @@ namespace Umbraco.Tests.PublishedContent ContentTypesCache.GetPublishedContentTypeByAlias = alias => type; } - protected override TypeLoader CreateTypeLoader(IServiceFactory f) + protected override TypeLoader CreateTypeLoader(IRuntimeCacheProvider runtimeCache, IGlobalSettings globalSettings, IProfilingLogger logger) { - var pluginManager = base.CreateTypeLoader(f); + var pluginManager = base.CreateTypeLoader(runtimeCache, globalSettings, logger); // this is so the model factory looks into the test assembly pluginManager.AssembliesToScan = pluginManager.AssembliesToScan @@ -453,7 +454,7 @@ namespace Umbraco.Tests.PublishedContent public void FirstChildAsT() { var doc = GetNode(1046); // has child nodes - + var model = doc.FirstChild(x => true); // predicate Assert.IsNotNull(model); @@ -817,7 +818,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void FragmentProperty() { - var factory = Container.GetInstance() as PublishedContentTypeFactory; + var factory = Factory.GetInstance() as PublishedContentTypeFactory; var pt = factory.CreatePropertyType("detached", 1003); var ct = factory.CreateContentType(0, "alias", new[] { pt }); @@ -836,7 +837,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Fragment2() { - var factory = Container.GetInstance() as PublishedContentTypeFactory; + var factory = Factory.GetInstance() as PublishedContentTypeFactory; var pt1 = factory.CreatePropertyType("legend", 1004); var pt2 = factory.CreatePropertyType("image", 1005); diff --git a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs index 309ce1c0fb..88b211d0ee 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs @@ -1,7 +1,6 @@ using System.Web; using System.Xml.Linq; using System.Xml.XPath; -using Lucene.Net.Store; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Models; @@ -20,9 +19,8 @@ using Umbraco.Core.Strings; using Umbraco.Examine; using Current = Umbraco.Web.Composing.Current; using Umbraco.Tests.Testing; -using LightInject; +using Umbraco.Core.Composing; using Umbraco.Core.Models.Membership; -using Umbraco.Core.Services; using Umbraco.Core.PropertyEditors; namespace Umbraco.Tests.PublishedContent @@ -42,7 +40,7 @@ namespace Umbraco.Tests.PublishedContent { base.Compose(); - Container.GetInstance() + Composition.WithCollectionBuilder() .Clear() .Append(); } @@ -106,14 +104,14 @@ namespace Umbraco.Tests.PublishedContent Assert.AreEqual("
This is some content
", propVal2.ToString()); var propVal3 = publishedMedia.Value("Content"); - Assert.IsInstanceOf(propVal3); + Assert.IsInstanceOf(propVal3); Assert.AreEqual("
This is some content
", propVal3.ToString()); } [Test] public void Ensure_Children_Sorted_With_Examine() { - var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockMediaService()); + var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Factory.GetInstance(), IndexInitializer.GetMockMediaService()); using (var luceneDir = new RandomIdRamDirectory()) using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, @@ -142,7 +140,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Do_Not_Find_In_Recycle_Bin() { - var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockMediaService()); + var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Factory.GetInstance(), IndexInitializer.GetMockMediaService()); using (var luceneDir = new RandomIdRamDirectory()) using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, @@ -190,7 +188,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Children_With_Examine() { - var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockMediaService()); + var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Factory.GetInstance(), IndexInitializer.GetMockMediaService()); using (var luceneDir = new RandomIdRamDirectory()) using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, @@ -218,7 +216,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Descendants_With_Examine() { - var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockMediaService()); + var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Factory.GetInstance(), IndexInitializer.GetMockMediaService()); using (var luceneDir = new RandomIdRamDirectory()) using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, @@ -246,7 +244,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void DescendantsOrSelf_With_Examine() { - var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockMediaService()); + var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Factory.GetInstance(), IndexInitializer.GetMockMediaService()); using (var luceneDir = new RandomIdRamDirectory()) using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, @@ -274,7 +272,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Ancestors_With_Examine() { - var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockMediaService()); + var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Factory.GetInstance(), IndexInitializer.GetMockMediaService()); using (var luceneDir = new RandomIdRamDirectory()) @@ -300,7 +298,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void AncestorsOrSelf_With_Examine() { - var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockMediaService()); + var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Factory.GetInstance(), IndexInitializer.GetMockMediaService()); using (var luceneDir = new RandomIdRamDirectory()) using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, @@ -463,10 +461,6 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Convert_From_Standard_Xml() { - var config = SettingsForTests.GenerateMockUmbracoSettings(); - - SettingsForTests.ConfigureSettings(config); - var nodeId = 2112; var xml = XElement.Parse(@" diff --git a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs index 0c4059ca7c..deaecf821e 100644 --- a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs +++ b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs @@ -172,7 +172,7 @@ namespace Umbraco.Tests.PublishedContent public int Id { get; set; } public Guid Key { get; set; } - public int TemplateId { get; set; } + public int? TemplateId { get; set; } public int SortOrder { get; set; } public string Name { get; set; } public PublishedCultureInfo GetCulture(string culture = null) => throw new NotSupportedException(); diff --git a/src/Umbraco.Tests/Routing/ContentFinderByIdTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByIdTests.cs index 224b4a7934..72848753e5 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByIdTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByIdTests.cs @@ -1,8 +1,9 @@ using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Tests.TestHelpers; using Umbraco.Web.Routing; -using LightInject; namespace Umbraco.Tests.Routing { @@ -17,7 +18,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext(urlAsString); var publishedRouter = CreatePublishedRouter(); var frequest = publishedRouter.CreateRequest(umbracoContext); - var lookup = new ContentFinderByIdPath(Container.GetInstance().WebRouting, Logger); + var lookup = new ContentFinderByIdPath(Factory.GetInstance().WebRouting, Logger); var result = lookup.TryFindContent(frequest); diff --git a/src/Umbraco.Tests/Routing/ContentFinderByUrlAndTemplateTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByUrlAndTemplateTests.cs index 5080ab339d..c40b2e5876 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByUrlAndTemplateTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByUrlAndTemplateTests.cs @@ -1,12 +1,12 @@ using Moq; using NUnit.Framework; -using LightInject; +using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; using Umbraco.Tests.TestHelpers; using Umbraco.Web.Routing; using Umbraco.Core.Models; using Umbraco.Tests.Testing; using Current = Umbraco.Web.Composing.Current; -using Umbraco.Core.Configuration.UmbracoSettings; namespace Umbraco.Tests.Routing { @@ -29,10 +29,8 @@ namespace Umbraco.Tests.Routing [TestCase("/home/Sub1.aspx/blah")] public void Match_Document_By_Url_With_Template(string urlAsString) { - - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - SettingsForTests.ConfigureSettings(globalSettings.Object); var template1 = CreateTemplate("test"); var template2 = CreateTemplate("blah"); diff --git a/src/Umbraco.Tests/Routing/ContentFinderByUrlTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByUrlTests.cs index 8b591dfb84..fa8beea2c2 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByUrlTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByUrlTests.cs @@ -2,6 +2,8 @@ using System.Globalization; using Moq; using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Models; using Umbraco.Tests.TestHelpers; @@ -27,16 +29,15 @@ namespace Umbraco.Tests.Routing [TestCase("/test-page", 1172)] public void Match_Document_By_Url_Hide_Top_Level(string urlString, int expectedId) { - var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettingsMock = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettingsMock.Setup(x => x.HideTopLevelNodeFromPath).Returns(true); - SettingsForTests.ConfigureSettings(globalSettingsMock.Object); var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettingsMock.Object); var publishedRouter = CreatePublishedRouter(); var frequest = publishedRouter.CreateRequest(umbracoContext); var lookup = new ContentFinderByUrl(Logger); - Assert.IsTrue(UmbracoConfig.For.GlobalSettings().HideTopLevelNodeFromPath); + Assert.IsTrue(Current.Configs.Global().HideTopLevelNodeFromPath); // fixme debugging - going further down, the routes cache is NOT empty?! if (urlString == "/home/sub1") @@ -63,16 +64,15 @@ namespace Umbraco.Tests.Routing [TestCase("/home/Sub1.aspx", 1173)] public void Match_Document_By_Url(string urlString, int expectedId) { - var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettingsMock = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettingsMock.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - SettingsForTests.ConfigureSettings(globalSettingsMock.Object); - + var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettingsMock.Object); var publishedRouter = CreatePublishedRouter(); var frequest = publishedRouter.CreateRequest(umbracoContext); var lookup = new ContentFinderByUrl(Logger); - Assert.IsFalse(UmbracoConfig.For.GlobalSettings().HideTopLevelNodeFromPath); + Assert.IsFalse(Current.Configs.Global().HideTopLevelNodeFromPath); var result = lookup.TryFindContent(frequest); @@ -89,15 +89,14 @@ namespace Umbraco.Tests.Routing [TestCase("/home/sub1/custom-sub-4-with-æøå", 1180)] public void Match_Document_By_Url_With_Special_Characters(string urlString, int expectedId) { - var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettingsMock = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettingsMock.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - SettingsForTests.ConfigureSettings(globalSettingsMock.Object); var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettingsMock.Object); var publishedRouter = CreatePublishedRouter(); var frequest = publishedRouter.CreateRequest(umbracoContext); var lookup = new ContentFinderByUrl(Logger); - + var result = lookup.TryFindContent(frequest); Assert.IsTrue(result); @@ -117,9 +116,8 @@ namespace Umbraco.Tests.Routing [TestCase("/home/sub1/custom-sub-4-with-æøå", 1180)] public void Match_Document_By_Url_With_Special_Characters_Using_Hostname(string urlString, int expectedId) { - var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettingsMock = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettingsMock.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - SettingsForTests.ConfigureSettings(globalSettingsMock.Object); var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettingsMock.Object); var publishedRouter = CreatePublishedRouter(); @@ -147,16 +145,15 @@ namespace Umbraco.Tests.Routing [TestCase("/æøå/home/sub1/custom-sub-4-with-æøå", 1180)] public void Match_Document_By_Url_With_Special_Characters_In_Hostname(string urlString, int expectedId) { - var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettingsMock = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettingsMock.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - SettingsForTests.ConfigureSettings(globalSettingsMock.Object); var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettingsMock.Object); var publishedRouter = CreatePublishedRouter(); var frequest = publishedRouter.CreateRequest(umbracoContext); frequest.Domain = new DomainAndUri(new Domain(1, "mysite/æøå", -1, CultureInfo.CurrentCulture, false), new Uri("http://mysite/æøå")); var lookup = new ContentFinderByUrl(Logger); - + var result = lookup.TryFindContent(frequest); Assert.IsTrue(result); diff --git a/src/Umbraco.Tests/Routing/ContentFinderByUrlWithDomainsTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByUrlWithDomainsTests.cs index 5787d3e613..dfbe9b0cda 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByUrlWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByUrlWithDomainsTests.cs @@ -1,5 +1,7 @@ using Moq; using NUnit.Framework; +using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; using Umbraco.Core.Models; using Umbraco.Tests.TestHelpers; using Umbraco.Web.Routing; @@ -124,12 +126,11 @@ namespace Umbraco.Tests.Routing { SetDomains3(); - var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettingsMock = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettingsMock.Setup(x => x.HideTopLevelNodeFromPath).Returns(true); - SettingsForTests.ConfigureSettings(globalSettingsMock.Object); - + var umbracoContext = GetUmbracoContext(url, globalSettings:globalSettingsMock.Object); - var publishedRouter = CreatePublishedRouter(Container); + var publishedRouter = CreatePublishedRouter(Factory); var frequest = publishedRouter.CreateRequest(umbracoContext); // must lookup domain else lookup by url fails @@ -167,12 +168,11 @@ namespace Umbraco.Tests.Routing // defaults depend on test environment expectedCulture = expectedCulture ?? System.Threading.Thread.CurrentThread.CurrentUICulture.Name; - var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettingsMock = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettingsMock.Setup(x => x.HideTopLevelNodeFromPath).Returns(true); - SettingsForTests.ConfigureSettings(globalSettingsMock.Object); var umbracoContext = GetUmbracoContext(url, globalSettings:globalSettingsMock.Object); - var publishedRouter = CreatePublishedRouter(Container); + var publishedRouter = CreatePublishedRouter(Factory); var frequest = publishedRouter.CreateRequest(umbracoContext); // must lookup domain else lookup by url fails diff --git a/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs b/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs index 987fa0a869..a81f345e38 100644 --- a/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs +++ b/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs @@ -1,9 +1,11 @@ -using System; -using Moq; +using Moq; using NUnit.Framework; using Umbraco.Core.Models; using Umbraco.Tests.TestHelpers; using Umbraco.Web.Routing; +using Umbraco.Core; +using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; namespace Umbraco.Tests.Routing { @@ -14,7 +16,7 @@ namespace Umbraco.Tests.Routing { base.Compose(); - Container.Register(); + Composition.Register(); } private void SetDomains1() @@ -264,12 +266,11 @@ namespace Umbraco.Tests.Routing { SetDomains1(); - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - SettingsForTests.ConfigureSettings(globalSettings.Object); var umbracoContext = GetUmbracoContext(inputUrl, globalSettings:globalSettings.Object); - var publishedRouter = CreatePublishedRouter(Container); + var publishedRouter = CreatePublishedRouter(Factory); var frequest = publishedRouter.CreateRequest(umbracoContext); // lookup domain @@ -314,12 +315,11 @@ namespace Umbraco.Tests.Routing // defaults depend on test environment expectedCulture = expectedCulture ?? System.Threading.Thread.CurrentThread.CurrentUICulture.Name; - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - SettingsForTests.ConfigureSettings(globalSettings.Object); var umbracoContext = GetUmbracoContext(inputUrl, globalSettings:globalSettings.Object); - var publishedRouter = CreatePublishedRouter(Container); + var publishedRouter = CreatePublishedRouter(Factory); var frequest = publishedRouter.CreateRequest(umbracoContext); // lookup domain @@ -370,11 +370,10 @@ namespace Umbraco.Tests.Routing { SetDomains3(); - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - SettingsForTests.ConfigureSettings(globalSettings.Object); var umbracoContext = GetUmbracoContext(inputUrl, globalSettings:globalSettings.Object); - var publishedRouter = CreatePublishedRouter(Container); + var publishedRouter = CreatePublishedRouter(Factory); var frequest = publishedRouter.CreateRequest(umbracoContext); // lookup domain diff --git a/src/Umbraco.Tests/Routing/GetContentUrlsTests.cs b/src/Umbraco.Tests/Routing/GetContentUrlsTests.cs index a2a627227e..8e9e52258c 100644 --- a/src/Umbraco.Tests/Routing/GetContentUrlsTests.cs +++ b/src/Umbraco.Tests/Routing/GetContentUrlsTests.cs @@ -3,10 +3,11 @@ using System.Globalization; using System.Linq; using Moq; using NUnit.Framework; -using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core; +using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; using Umbraco.Core.Models; using Umbraco.Core.Services; -using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; using Umbraco.Web.Routing; @@ -15,17 +16,6 @@ namespace Umbraco.Tests.Routing [TestFixture] public class GetContentUrlsTests : UrlRoutingTestBase { - private IUmbracoSettingsSection _umbracoSettings; - - public override void SetUp() - { - base.SetUp(); - - //generate new mock settings and assign so we can configure in individual tests - _umbracoSettings = SettingsForTests.GenerateMockUmbracoSettings(); - SettingsForTests.ConfigureSettings(_umbracoSettings); - } - private ILocalizedTextService GetTextService() { var textService = Mock.Of( @@ -59,7 +49,7 @@ namespace Umbraco.Tests.Routing content.Path = "-1,1046"; var umbContext = GetUmbracoContext("http://localhost:8000"); - var publishedRouter = CreatePublishedRouter(Container, + var publishedRouter = CreatePublishedRouter(Factory, contentFinders: new ContentFinderCollection(new[] { new ContentFinderByUrl(Logger) })); var urls = content.GetContentUrls(publishedRouter, umbContext, @@ -79,10 +69,12 @@ namespace Umbraco.Tests.Routing content.Id = 1046; //fixme: we are using this ID only because it's built into the test XML published cache content.Path = "-1,1046"; content.Published = true; - + + var umbracoSettings = Current.Configs.Settings(); + var umbContext = GetUmbracoContext("http://localhost:8000", - urlProviders: new []{ new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, TestObjects.GetGlobalSettings(), new SiteDomainHelper()) }); - var publishedRouter = CreatePublishedRouter(Container, + urlProviders: new []{ new DefaultUrlProvider(umbracoSettings.RequestHandler, Logger, TestObjects.GetGlobalSettings(), new SiteDomainHelper()) }); + var publishedRouter = CreatePublishedRouter(Factory, contentFinders:new ContentFinderCollection(new[]{new ContentFinderByUrl(Logger) })); var urls = content.GetContentUrls(publishedRouter, umbContext, @@ -110,9 +102,11 @@ namespace Umbraco.Tests.Routing child.Path = "-1,1046,1173"; child.Published = true; + var umbracoSettings = Current.Configs.Settings(); + var umbContext = GetUmbracoContext("http://localhost:8000", - urlProviders: new[] { new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, TestObjects.GetGlobalSettings(), new SiteDomainHelper()) }); - var publishedRouter = CreatePublishedRouter(Container, + urlProviders: new[] { new DefaultUrlProvider(umbracoSettings.RequestHandler, Logger, TestObjects.GetGlobalSettings(), new SiteDomainHelper()) }); + var publishedRouter = CreatePublishedRouter(Factory, contentFinders: new ContentFinderCollection(new[] { new ContentFinderByUrl(Logger) })); var urls = child.GetContentUrls(publishedRouter, umbContext, diff --git a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs index b277194063..84f4f5dbd7 100644 --- a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs +++ b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs @@ -2,10 +2,10 @@ using System.Linq; using System.Web.Mvc; using System.Web.Routing; -using LightInject; using Moq; using NUnit.Framework; using Umbraco.Core; +using Umbraco.Core.Cache; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Tests.TestHelpers; @@ -13,11 +13,12 @@ using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Web; using Umbraco.Web.Models; using Umbraco.Web.Mvc; -using Umbraco.Web.Routing; using Umbraco.Web.WebApi; using Umbraco.Core.Strings; using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Services; using Umbraco.Tests.PublishedContent; using Umbraco.Tests.Testing; using Umbraco.Tests.Testing.Objects.Accessors; @@ -47,17 +48,8 @@ namespace Umbraco.Tests.Routing : base(umbracoApplication) { } - public override void Boot(ServiceContainer container) - { - // do it before anything else - this is the only place where it's possible - var logger = Mock.Of(); - container.RegisterInstance(logger); - var profiler = Mock.Of(); - container.RegisterInstance(profiler); - container.RegisterInstance(new ProfilingLogger(logger, profiler)); - - base.Boot(container); - } + protected override ILogger GetLogger() => Mock.Of(); + protected override IProfiler GetProfiler() => Mock.Of(); } protected override void Compose() @@ -67,13 +59,13 @@ namespace Umbraco.Tests.Routing // set the default RenderMvcController Current.DefaultRenderMvcControllerType = typeof(RenderMvcController); // fixme WRONG! - var surfaceControllerTypes = new SurfaceControllerTypeCollection(Current.TypeLoader.GetSurfaceControllers()); - Container.RegisterInstance(surfaceControllerTypes); + var surfaceControllerTypes = new SurfaceControllerTypeCollection(Composition.TypeLoader.GetSurfaceControllers()); + Composition.RegisterUnique(surfaceControllerTypes); - var umbracoApiControllerTypes = new UmbracoApiControllerTypeCollection(Current.TypeLoader.GetUmbracoApiControllers()); - Container.RegisterInstance(umbracoApiControllerTypes); + var umbracoApiControllerTypes = new UmbracoApiControllerTypeCollection(Composition.TypeLoader.GetUmbracoApiControllers()); + Composition.RegisterUnique(umbracoApiControllerTypes); - Container.RegisterSingleton(_ => new DefaultShortStringHelper(SettingsForTests.GetDefaultUmbracoSettings())); + Composition.RegisterUnique(_ => new DefaultShortStringHelper(SettingsForTests.GetDefaultUmbracoSettings())); } public override void TearDown() @@ -181,6 +173,11 @@ namespace Umbraco.Tests.Routing ///
public class CustomDocumentController : RenderMvcController { + public CustomDocumentController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ServiceContext services, CacheHelper applicationCache, ILogger logger, IProfilingLogger profilingLogger) + : base(globalSettings, umbracoContext, services, applicationCache, logger, profilingLogger) + { + } + public ActionResult HomePage(ContentModel model) { return View(); diff --git a/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs b/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs index 69533c3c77..c87a02f43a 100644 --- a/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs +++ b/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs @@ -4,13 +4,17 @@ using System.Threading; using Moq; using NUnit.Framework; using Umbraco.Core; -using Umbraco.Core.Configuration; +using Umbraco.Core.Composing; using Umbraco.Tests.TestHelpers; using Umbraco.Web; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Sync; using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Services; +using Umbraco.Web.PublishedCache; +using Umbraco.Web.Routing; namespace Umbraco.Tests.Routing { @@ -18,23 +22,34 @@ namespace Umbraco.Tests.Routing [Apartment(ApartmentState.STA)] public class UmbracoModuleTests : BaseWebTest { - private UmbracoModule _module; + private UmbracoInjectedModule _module; public override void SetUp() { base.SetUp(); + // fixme - be able to get the UmbracoModule from the container. any reason settings were from testobjects? //create the module - _module = new UmbracoModule - { - GlobalSettings = TestObjects.GetGlobalSettings(), - Logger = Mock.Of() - }; - var runtime = new RuntimeState(_module.Logger, new Lazy(), new Lazy(), Mock.Of(), _module.GlobalSettings); + var logger = Mock.Of(); + var globalSettings = TestObjects.GetGlobalSettings(); + var runtime = new RuntimeState(logger, Mock.Of(), globalSettings, + new Lazy(), new Lazy()); + + _module = new UmbracoInjectedModule + ( + globalSettings, + Mock.Of(), + Factory.GetInstance(), + Factory.GetInstance(), + new UrlProviderCollection(new IUrlProvider[0]), + runtime, + logger, + null, // fixme - PublishedRouter complexities... + Mock.Of() + ); - _module.Runtime = runtime; runtime.Level = RuntimeLevel.Run; - + //SettingsForTests.ReservedPaths = "~/umbraco,~/install/"; //SettingsForTests.ReservedUrls = "~/config/splashes/booting.aspx,~/install/default.aspx,~/config/splashes/noNodes.aspx,~/VSEnterpriseHelper.axd"; diff --git a/src/Umbraco.Tests/Routing/UrlProviderTests.cs b/src/Umbraco.Tests/Routing/UrlProviderTests.cs index 2f1e4e3476..819ea0b01c 100644 --- a/src/Umbraco.Tests/Routing/UrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/UrlProviderTests.cs @@ -4,7 +4,9 @@ using System.Globalization; using System.Linq; using Moq; using NUnit.Framework; -using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core; +using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Tests.TestHelpers; @@ -23,18 +25,13 @@ namespace Umbraco.Tests.Routing protected override void Compose() { base.Compose(); - Container.Register(); + Composition.Register(); } - private IUmbracoSettingsSection _umbracoSettings; - - public override void SetUp() + protected override void ComposeSettings() { - base.SetUp(); - - //generate new mock settings and assign so we can configure in individual tests - _umbracoSettings = SettingsForTests.GenerateMockUmbracoSettings(); - SettingsForTests.ConfigureSettings(_umbracoSettings); + Composition.Configs.Add(SettingsForTests.GenerateMockUmbracoSettings); + Composition.Configs.Add(SettingsForTests.GenerateMockGlobalSettings); } /// @@ -44,17 +41,18 @@ namespace Umbraco.Tests.Routing [Test] public void Ensure_Cache_Is_Correct() { - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - SettingsForTests.ConfigureSettings(globalSettings.Object); + + var umbracoSettings = Current.Configs.Settings(); var umbracoContext = GetUmbracoContext("/test", 1111, urlProviders: new[] { - new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + new DefaultUrlProvider(umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) }, globalSettings: globalSettings.Object); - var requestHandlerMock = Mock.Get(_umbracoSettings.RequestHandler); + var requestHandlerMock = Mock.Get(umbracoSettings.RequestHandler); requestHandlerMock.Setup(x => x.AddTrailingSlash).Returns(false);// (cached routes have none) var samples = new Dictionary { @@ -107,17 +105,18 @@ namespace Umbraco.Tests.Routing [TestCase(1172, "/test-page/")] public void Get_Url_Not_Hiding_Top_Level(int nodeId, string niceUrlMatch) { - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - SettingsForTests.ConfigureSettings(globalSettings.Object); + + var umbracoSettings = Current.Configs.Settings(); var umbracoContext = GetUmbracoContext("/test", 1111, urlProviders: new[] { - new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + new DefaultUrlProvider(umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) }, globalSettings: globalSettings.Object); - var requestMock = Mock.Get(_umbracoSettings.RequestHandler); + var requestMock = Mock.Get(umbracoSettings.RequestHandler); requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); var result = umbracoContext.UrlProvider.GetUrl(nodeId); @@ -137,17 +136,18 @@ namespace Umbraco.Tests.Routing [TestCase(1172, "/test-page/")] // not hidden because not first root public void Get_Url_Hiding_Top_Level(int nodeId, string niceUrlMatch) { - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(true); - SettingsForTests.ConfigureSettings(globalSettings.Object); + + var umbracoSettings = Current.Configs.Settings(); var umbracoContext = GetUmbracoContext("/test", 1111, urlProviders: new[] { - new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + new DefaultUrlProvider(umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) }, globalSettings: globalSettings.Object); - var requestMock = Mock.Get(_umbracoSettings.RequestHandler); + var requestMock = Mock.Get(umbracoSettings.RequestHandler); requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); var result = umbracoContext.UrlProvider.GetUrl(nodeId); @@ -159,12 +159,13 @@ namespace Umbraco.Tests.Routing { const string currentUri = "http://example.us/test"; - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - SettingsForTests.ConfigureSettings(globalSettings.Object); - var requestMock = Mock.Get(_umbracoSettings.RequestHandler); + var umbracoSettings = Current.Configs.Settings(); + + var requestMock = Mock.Get(umbracoSettings.RequestHandler); requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Culture); @@ -186,9 +187,9 @@ namespace Umbraco.Tests.Routing snapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())) .Returns(snapshot); - var umbracoContext = GetUmbracoContext(currentUri, umbracoSettings: _umbracoSettings, + var umbracoContext = GetUmbracoContext(currentUri, umbracoSettings: umbracoSettings, urlProviders: new[] { - new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + new DefaultUrlProvider(umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) }, globalSettings: globalSettings.Object, snapshotService: snapshotService.Object); @@ -207,12 +208,13 @@ namespace Umbraco.Tests.Routing { const string currentUri = "http://example.fr/test"; - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - SettingsForTests.ConfigureSettings(globalSettings.Object); - var requestMock = Mock.Get(_umbracoSettings.RequestHandler); + var umbracoSettings = Current.Configs.Settings(); + + var requestMock = Mock.Get(umbracoSettings.RequestHandler); requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Culture); @@ -243,9 +245,9 @@ namespace Umbraco.Tests.Routing snapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())) .Returns(snapshot); - var umbracoContext = GetUmbracoContext(currentUri, umbracoSettings: _umbracoSettings, + var umbracoContext = GetUmbracoContext(currentUri, umbracoSettings: umbracoSettings, urlProviders: new[] { - new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + new DefaultUrlProvider(umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) }, globalSettings: globalSettings.Object, snapshotService: snapshotService.Object); @@ -264,12 +266,13 @@ namespace Umbraco.Tests.Routing { const string currentUri = "http://example.us/test"; - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - SettingsForTests.ConfigureSettings(globalSettings.Object); - var requestMock = Mock.Get(_umbracoSettings.RequestHandler); + var umbracoSettings = Current.Configs.Settings(); + + var requestMock = Mock.Get(umbracoSettings.RequestHandler); requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Culture); @@ -300,9 +303,9 @@ namespace Umbraco.Tests.Routing snapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())) .Returns(snapshot); - var umbracoContext = GetUmbracoContext(currentUri, umbracoSettings: _umbracoSettings, + var umbracoContext = GetUmbracoContext(currentUri, umbracoSettings: umbracoSettings, urlProviders: new[] { - new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + new DefaultUrlProvider(umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) }, globalSettings: globalSettings.Object, snapshotService: snapshotService.Object); @@ -317,17 +320,18 @@ namespace Umbraco.Tests.Routing [Test] public void Get_Url_Relative_Or_Absolute() { - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - SettingsForTests.ConfigureSettings(globalSettings.Object); - var requestMock = Mock.Get(_umbracoSettings.RequestHandler); + var umbracoSettings = Current.Configs.Settings(); + + var requestMock = Mock.Get(umbracoSettings.RequestHandler); requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); - var umbracoContext = GetUmbracoContext("http://example.com/test", 1111, umbracoSettings: _umbracoSettings, urlProviders: new[] + var umbracoContext = GetUmbracoContext("http://example.com/test", 1111, umbracoSettings: umbracoSettings, urlProviders: new[] { - new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + new DefaultUrlProvider(umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) }, globalSettings: globalSettings.Object); Assert.AreEqual("/home/sub1/custom-sub-1/", umbracoContext.UrlProvider.GetUrl(1177)); @@ -343,18 +347,19 @@ namespace Umbraco.Tests.Routing [Test] public void Get_Url_Unpublished() { - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - SettingsForTests.ConfigureSettings(globalSettings.Object); + + var umbracoSettings = Current.Configs.Settings(); var umbracoContext = GetUmbracoContext("http://example.com/test", 1111, urlProviders: new[] { - new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + new DefaultUrlProvider(umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) }, globalSettings: globalSettings.Object); //mock the Umbraco settings that we need - var requestMock = Mock.Get(_umbracoSettings.RequestHandler); + var requestMock = Mock.Get(umbracoSettings.RequestHandler); requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); Assert.AreEqual("#", umbracoContext.UrlProvider.GetUrl(999999)); diff --git a/src/Umbraco.Tests/Routing/UrlRoutesTests.cs b/src/Umbraco.Tests/Routing/UrlRoutesTests.cs index 9b291249c9..4928cd01dc 100644 --- a/src/Umbraco.Tests/Routing/UrlRoutesTests.cs +++ b/src/Umbraco.Tests/Routing/UrlRoutesTests.cs @@ -1,6 +1,8 @@ using System; using Moq; using NUnit.Framework; +using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; using Umbraco.Core.Models; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; @@ -193,9 +195,8 @@ DetermineRouteById(id): [TestCase(2006, false, "/x/b/e")] public void GetRouteByIdNoHide(int id, bool hide, string expected) { - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(hide); - SettingsForTests.ConfigureSettings(globalSettings.Object); var umbracoContext = GetUmbracoContext("/test", 0, globalSettings: globalSettings.Object); var cache = umbracoContext.ContentCache as PublishedContentCache; @@ -218,9 +219,8 @@ DetermineRouteById(id): [TestCase(2006, true, "/b/e")] // risky! public void GetRouteByIdHide(int id, bool hide, string expected) { - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(hide); - SettingsForTests.ConfigureSettings(globalSettings.Object); var umbracoContext = GetUmbracoContext("/test", 0, globalSettings: globalSettings.Object); var cache = umbracoContext.ContentCache as PublishedContentCache; @@ -233,14 +233,13 @@ DetermineRouteById(id): [Test] public void GetRouteByIdCache() { - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - SettingsForTests.ConfigureSettings(globalSettings.Object); var umbracoContext = GetUmbracoContext("/test", 0, globalSettings:globalSettings.Object); var cache = umbracoContext.ContentCache as PublishedContentCache; if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); - + var route = cache.GetRouteById(false, 1000); Assert.AreEqual("/a", route); @@ -265,9 +264,8 @@ DetermineRouteById(id): [TestCase("/x", false, 2000)] public void GetByRouteNoHide(string route, bool hide, int expected) { - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(hide); - SettingsForTests.ConfigureSettings(globalSettings.Object); var umbracoContext = GetUmbracoContext("/test", 0, globalSettings:globalSettings.Object); var cache = umbracoContext.ContentCache as PublishedContentCache; @@ -297,9 +295,8 @@ DetermineRouteById(id): [TestCase("/b/c", true, 1002)] // (hence the 2005 collision) public void GetByRouteHide(string route, bool hide, int expected) { - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(hide); - SettingsForTests.ConfigureSettings(globalSettings.Object); var umbracoContext = GetUmbracoContext("/test", 0, globalSettings:globalSettings.Object); var cache = umbracoContext.ContentCache as PublishedContentCache; @@ -321,14 +318,13 @@ DetermineRouteById(id): [Test] public void GetByRouteCache() { - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - SettingsForTests.ConfigureSettings(globalSettings.Object); var umbracoContext = GetUmbracoContext("/test", 0, globalSettings:globalSettings.Object); var cache = umbracoContext.ContentCache as PublishedContentCache; if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); - + var content = cache.GetByRoute(false, "/a/b/c"); Assert.IsNotNull(content); Assert.AreEqual(1002, content.Id); diff --git a/src/Umbraco.Tests/Routing/UrlRoutingTestBase.cs b/src/Umbraco.Tests/Routing/UrlRoutingTestBase.cs index 6965c4dce9..7566c5372d 100644 --- a/src/Umbraco.Tests/Routing/UrlRoutingTestBase.cs +++ b/src/Umbraco.Tests/Routing/UrlRoutingTestBase.cs @@ -2,6 +2,7 @@ using System.Linq; using Moq; using NUnit.Framework; +using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Models; using Umbraco.Core.Services; @@ -33,13 +34,13 @@ namespace Umbraco.Tests.Routing { base.Compose(); - Container.RegisterSingleton(_ => GetServiceContext()); + Composition.RegisterUnique(_ => GetServiceContext()); } protected ServiceContext GetServiceContext() { // get the mocked service context to get the mocked domain service - var serviceContext = TestObjects.GetServiceContextMock(Container); + var serviceContext = TestObjects.GetServiceContextMock(Factory); //setup mock domain service var domainService = Mock.Get(serviceContext.DomainService); diff --git a/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs b/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs index c7ce42e4bc..ba646f6d94 100644 --- a/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs @@ -3,12 +3,14 @@ using System.Collections.Generic; using System.Linq; using Moq; using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; using Umbraco.Web.PublishedCache.XmlPublishedCache; using Umbraco.Web.Routing; -using Umbraco.Core.Composing; namespace Umbraco.Tests.Routing { @@ -19,8 +21,8 @@ namespace Umbraco.Tests.Routing { base.Compose(); - Container.RegisterSingleton(_ => Mock.Of()); - Container.Register(); + Composition.RegisterUnique(_ => Mock.Of()); + Composition.Register(); } void SetDomains1() @@ -177,10 +179,9 @@ namespace Umbraco.Tests.Routing var request = Mock.Get(settings.RequestHandler); request.Setup(x => x.UseDomainPrefixes).Returns(false); - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains - SettingsForTests.ConfigureSettings(globalSettings.Object); var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] { @@ -213,10 +214,9 @@ namespace Umbraco.Tests.Routing var request = Mock.Get(settings.RequestHandler); request.Setup(x => x.UseDomainPrefixes).Returns(false); - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains - SettingsForTests.ConfigureSettings(globalSettings.Object); var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] { @@ -241,10 +241,9 @@ namespace Umbraco.Tests.Routing var request = Mock.Get(settings.RequestHandler); request.Setup(x => x.UseDomainPrefixes).Returns(false); - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains - SettingsForTests.ConfigureSettings(globalSettings.Object); var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] { @@ -275,10 +274,9 @@ namespace Umbraco.Tests.Routing var request = Mock.Get(settings.RequestHandler); request.Setup(x => x.UseDomainPrefixes).Returns(false); - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains - SettingsForTests.ConfigureSettings(globalSettings.Object); var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] { @@ -299,10 +297,9 @@ namespace Umbraco.Tests.Routing var request = Mock.Get(settings.RequestHandler); request.Setup(x => x.UseDomainPrefixes).Returns(false); - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains - SettingsForTests.ConfigureSettings(globalSettings.Object); var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] { @@ -366,10 +363,9 @@ namespace Umbraco.Tests.Routing var requestMock = Mock.Get(settings.RequestHandler); requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains - SettingsForTests.ConfigureSettings(globalSettings.Object); var umbracoContext = GetUmbracoContext("http://domain1.com/test", 1111, umbracoSettings: settings, urlProviders: new[] { @@ -398,10 +394,9 @@ namespace Umbraco.Tests.Routing { var settings = SettingsForTests.GenerateMockUmbracoSettings(); - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains - SettingsForTests.ConfigureSettings(globalSettings.Object); var umbracoContext = GetUmbracoContext("http://domain1.com/en/test", 1111, umbracoSettings: settings, urlProviders: new[] { diff --git a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs index 6b2737a3d1..e28a6485d6 100644 --- a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs +++ b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs @@ -1,12 +1,14 @@ using System; using Moq; using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; using Umbraco.Core.Models; using Umbraco.Tests.TestHelpers; using Umbraco.Web.PublishedCache.XmlPublishedCache; using Umbraco.Web.Routing; using Umbraco.Core.Services; -using Umbraco.Core.Composing; namespace Umbraco.Tests.Routing { @@ -21,17 +23,16 @@ namespace Umbraco.Tests.Routing protected override void Compose() { base.Compose(); - Container.RegisterSingleton(_ => Mock.Of()); - Container.Register(); + Composition.RegisterUnique(_ => Mock.Of()); + Composition.Register(); } [Test] public void DoNotPolluteCache() { - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - SettingsForTests.ConfigureSettings(globalSettings.Object); var settings = SettingsForTests.GenerateMockUmbracoSettings(); var request = Mock.Get(settings.RequestHandler); diff --git a/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs b/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs old mode 100755 new mode 100644 index 9a8164356a..c894659865 --- a/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs +++ b/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs @@ -1,16 +1,20 @@ using System; using System.Collections.Generic; +using System.Data; using System.Web.Hosting; using Examine; -using LightInject; using Moq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Components; using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; +using Umbraco.Core.Events; +using Umbraco.Core.Exceptions; using Umbraco.Core.Logging; using Umbraco.Core.Persistence; using Umbraco.Core.Runtime; +using Umbraco.Core.Scoping; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Web; @@ -18,13 +22,13 @@ using Umbraco.Web; namespace Umbraco.Tests.Runtimes { [TestFixture] - [Ignore("cannot work until we refactor IUmbracoDatabaseFactory vs UmbracoDatabaseFactory")] public class CoreRuntimeTests { [SetUp] public void SetUp() { TestComponent.Reset(); + Current.Reset(); } public void TearDown() @@ -39,12 +43,28 @@ namespace Umbraco.Tests.Runtimes { app.HandleApplicationStart(app, new EventArgs()); + var e = app.Runtime.State.BootFailedException; + var m = ""; + switch (e) + { + case null: + m = ""; + break; + case BootFailedException bfe when bfe.InnerException != null: + m = "BootFailed: " + bfe.InnerException.GetType() + " " + bfe.InnerException.Message + " " + bfe.InnerException.StackTrace; + break; + default: + m = e.GetType() + " " + e.Message + " " + e.StackTrace; + break; + } + + Assert.AreNotEqual(RuntimeLevel.BootFailed, app.Runtime.State.Level, m); + Assert.IsTrue(TestComposer.Ctored); + Assert.IsTrue(TestComposer.Composed); Assert.IsTrue(TestComponent.Ctored); - Assert.IsTrue(TestComponent.Composed); - Assert.IsTrue(TestComponent.Initialized1); - Assert.IsTrue(TestComponent.Initialized2); Assert.IsNotNull(TestComponent.ProfilingLogger); - Assert.IsInstanceOf(TestComponent.ProfilingLogger.Logger); + Assert.IsInstanceOf(TestComponent.ProfilingLogger); + Assert.IsInstanceOf(((ProfilingLogger) TestComponent.ProfilingLogger).Logger); // note: components are NOT disposed after boot @@ -58,40 +78,23 @@ namespace Umbraco.Tests.Runtimes // test application public class TestUmbracoApplication : UmbracoApplicationBase { + public IRuntime Runtime { get; private set; } + protected override IRuntime GetRuntime() { - return new TestRuntime(); + return Runtime = new TestRuntime(); } } // test runtime public class TestRuntime : CoreRuntime { - // the application's logger is created by the application - // through GetLogger, that custom application can override - protected override ILogger GetLogger() - { - //return Mock.Of(); - return new DebugDiagnosticsLogger(); - } - - public override void Compose(ServiceContainer container) - { - base.Compose(container); - - // the application's profiler and profiling logger are - // registered by CoreRuntime.Compose() but can be - // overriden afterwards - they haven't been resolved yet - container.RegisterSingleton(_ => new TestProfiler()); - container.RegisterSingleton(factory => new ProfilingLogger(factory.GetInstance(), factory.GetInstance())); - - // must override the database factory - container.RegisterSingleton(_ => GetDatabaseFactory()); - } + protected override ILogger GetLogger() => new DebugDiagnosticsLogger(); + protected override IProfiler GetProfiler() => new TestProfiler(); // must override the database factory // else BootFailedException because U cannot connect to the configured db - private static IUmbracoDatabaseFactory GetDatabaseFactory() + protected internal override IUmbracoDatabaseFactory GetDatabaseFactory() { var mock = new Mock(); mock.Setup(x => x.Configured).Returns(true); @@ -99,19 +102,51 @@ namespace Umbraco.Tests.Runtimes return mock.Object; } + protected override Configs GetConfigs() + { + var configs = new Configs(); + configs.Add(SettingsForTests.GetDefaultGlobalSettings); + configs.Add(SettingsForTests.GetDefaultUmbracoSettings); + return configs; + } + + // fixme so how the f* should we do it now? + /* // pretend we have the proper migration // else BootFailedException because our mock IUmbracoDatabaseFactory does not provide databases - protected override bool EnsureUmbracoUpgradeState(IUmbracoDatabaseFactory databaseFactory, ILogger logger) + protected override bool EnsureUmbracoUpgradeState(IUmbracoDatabaseFactory databaseFactory) { return true; } + */ - private MainDom _mainDom; - - public override void Boot(ServiceContainer container) + // because we don't even have the core runtime component, + // there are a few required stuff that we need to compose + public override void Compose(Composition composition) { - base.Boot(container); - _mainDom = container.GetInstance(); + base.Compose(composition); + + var scopeProvider = Mock.Of(); + Mock.Get(scopeProvider) + .Setup(x => x.CreateScope( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .Returns(Mock.Of()); + + composition.RegisterUnique(scopeProvider); + } + + private IMainDom _mainDom; + + public override IFactory Boot(IRegister container) + { + var factory = base.Boot(container); + _mainDom = factory.GetInstance(); + return factory; } public override void Terminate() @@ -123,64 +158,75 @@ namespace Umbraco.Tests.Runtimes // runs with only one single component // UmbracoCoreComponent will be force-added too // and that's it - protected override IEnumerable GetComponentTypes() + protected override IEnumerable GetComposerTypes(TypeLoader typeLoader) { - return new[] { typeof(TestComponent) }; + return new[] { typeof(TestComposer) }; } } - public class TestComponent : UmbracoComponentBase + public class TestComposer : IComposer { // test flags public static bool Ctored; public static bool Composed; - public static bool Initialized1; - public static bool Initialized2; - public static bool Terminated; - public static ProfilingLogger ProfilingLogger; public static void Reset() { - Ctored = Composed = Initialized1 = Initialized2 = Terminated = false; - ProfilingLogger = null; + Ctored = Composed = false; } - public TestComponent() + public TestComposer() { Ctored = true; } - public override void Compose(Composition composition) + public void Compose(Composition composition) { - base.Compose(composition); - - composition.Container.Register(factory => SettingsForTests.GetDefaultUmbracoSettings()); - composition.Container.RegisterSingleton(); + composition.Register(factory => SettingsForTests.GetDefaultUmbracoSettings()); + composition.RegisterUnique(); + composition.Components().Append(); Composed = true; } + } + + public class TestComponent : IComponent, IDisposable + { + // test flags + public static bool Ctored; + public static bool Initialized; + public static bool Terminated; + public static IProfilingLogger ProfilingLogger; + + public bool Disposed; + + public static void Reset() + { + Ctored = Initialized = Terminated = false; + ProfilingLogger = null; + } + + public TestComponent(IProfilingLogger proflog) + { + Ctored = true; + ProfilingLogger = proflog; + } public void Initialize() { - Initialized1 = true; + Initialized = true; } - public void Initialize(ILogger logger) + public void Terminate() { - Initialized2 = true; - } - - public void Initialize(ProfilingLogger proflog) - { - ProfilingLogger = proflog; - } - - public override void Terminate() - { - base.Terminate(); Terminated = true; } + + public void Dispose() + { + Disposed = true; + } } } } diff --git a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs new file mode 100644 index 0000000000..ccb7427907 --- /dev/null +++ b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs @@ -0,0 +1,398 @@ +using System; +using System.Configuration; +using System.Data.SqlServerCe; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Web; +using Examine; +using Moq; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Components; +using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; +using Umbraco.Core.IO; +using Umbraco.Core.Logging; +using Umbraco.Core.Migrations.Install; +using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Mappers; +using Umbraco.Core.Runtime; +using Umbraco.Core.Scoping; +using Umbraco.Core.Services; +using Umbraco.Core.Sync; +using Umbraco.Tests.Composing; +using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.Testing.Objects.Accessors; +using Umbraco.Web; +using Umbraco.Web.Cache; +using Umbraco.Web.PublishedCache; +using Umbraco.Web.Routing; +using Umbraco.Web.Runtime; +using File = System.IO.File; + +namespace Umbraco.Tests.Runtimes +{ + [TestFixture] + public class StandaloneTests + { + [Test] + [Explicit("This test must be run manually")] + public void StandaloneTest() + { + IFactory factory = null; + + // clear + foreach (var file in Directory.GetFiles(Path.Combine(IOHelper.MapPath("~/App_Data")), "NuCache.*")) + File.Delete(file); + + // settings + // reset the current version to 0.0.0, clear connection strings + ConfigurationManager.AppSettings["umbracoConfigurationStatus"] = ""; + // fixme we need a better management of settings here (and, true config files?) + + // create the very basic and essential things we need + var logger = new ConsoleLogger(); + var profiler = new LogProfiler(logger); + var profilingLogger = new ProfilingLogger(logger, profiler); + var appCaches = new CacheHelper(); // fixme has HttpRuntime stuff? + var databaseFactory = new UmbracoDatabaseFactory(logger, new Lazy(() => factory.GetInstance())); + var typeLoader = new TypeLoader(appCaches.RuntimeCache, LocalTempStorage.Default, profilingLogger); + var mainDom = new SimpleMainDom(); + var runtimeState = new RuntimeState(logger, null, null, new Lazy(() => mainDom), new Lazy(() => factory.GetInstance())); + + // create the register and the composition + var register = RegisterFactory.Create(); + var composition = new Composition(register, typeLoader, profilingLogger, runtimeState); + composition.RegisterEssentials(logger, profiler, profilingLogger, mainDom, appCaches, databaseFactory, typeLoader, runtimeState); + + // create the core runtime and have it compose itself + var coreRuntime = new CoreRuntime(); + coreRuntime.Compose(composition); + + // determine actual runtime level + runtimeState.DetermineRuntimeLevel(databaseFactory, logger); + Console.WriteLine(runtimeState.Level); + // going to be Install BUT we want to force components to be there (nucache etc) + runtimeState.Level = RuntimeLevel.Run; + + var composerTypes = typeLoader.GetTypes() // all of them + .Where(x => !x.FullName.StartsWith("Umbraco.Tests.")) // exclude test components + .Where(x => x != typeof(WebRuntimeComposer)); // exclude web runtime + var composers = new Composers(composition, composerTypes, profilingLogger); + composers.Compose(); + + // must registers stuff that WebRuntimeComponent would register otherwise + // fixme UmbracoContext creates a snapshot that it does not register with the accessor + // and so, we have to use the UmbracoContextPublishedSnapshotAccessor + // the UmbracoContext does not know about the accessor + // else that would be a catch-22 where they both know about each other? + //composition.Register(Lifetime.Singleton); + composition.Register(Lifetime.Singleton); + composition.Register(Lifetime.Singleton); + composition.Register(Lifetime.Singleton); + composition.Register(Lifetime.Singleton); + composition.Register(_ => Mock.Of(), Lifetime.Singleton); + composition.RegisterUnique(f => new DistributedCache()); + composition.WithCollectionBuilder().Append(); + composition.RegisterUnique(); + composition.RegisterUnique(f => ExamineManager.Instance); + + // initialize some components only/individually + composition.WithCollectionBuilder() + .Clear() + .Append(); + + // configure + composition.Configs.Add(SettingsForTests.GetDefaultGlobalSettings); + composition.Configs.Add(SettingsForTests.GetDefaultUmbracoSettings); + + // create and register the factory + Current.Factory = factory = composition.CreateFactory(); + + // instantiate and initialize components + var components = factory.GetInstance(); + + // do stuff + Console.WriteLine(runtimeState.Level); + + // install + if (true || runtimeState.Level == RuntimeLevel.Install) + { + var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + var file = Path.Combine(path, "Umbraco.sdf"); + if (File.Exists(file)) + File.Delete(file); + + // create the database file + // databaseBuilder.ConfigureEmbeddedDatabaseConnection() can do it too, + // but then it wants to write the connection string to web.config = bad + using (var engine = new SqlCeEngine("Data Source=|DataDirectory|\\Umbraco.sdf;Flush Interval=1;")) + { + engine.CreateDatabase(); + } + + //var databaseBuilder = factory.GetInstance(); + //databaseFactory.Configure(DatabaseBuilder.EmbeddedDatabaseConnectionString, Constants.DbProviderNames.SqlCe); + //databaseBuilder.CreateDatabaseSchemaAndData(); + + databaseFactory.Configure(DatabaseBuilder.EmbeddedDatabaseConnectionString, Constants.DbProviderNames.SqlCe); + + var scopeProvider = factory.GetInstance(); + using (var scope = scopeProvider.CreateScope()) + { + var creator = new DatabaseSchemaCreator(scope.Database, logger); + creator.InitializeDatabaseSchema(); + scope.Complete(); + } + } + + // done installing + runtimeState.Level = RuntimeLevel.Run; + + // instantiate to register events + // should be done by Initialize? + // should we invoke Initialize? + _ = factory.GetInstance(); + + // at that point, Umbraco can run! + // though, we probably still need to figure out what depends on HttpContext... + var contentService = factory.GetInstance(); + var content = contentService.GetById(1234); + Assert.IsNull(content); + + // create a document type and a document + var contentType = new ContentType(-1) { Alias = "ctype", Name = "ctype" }; + factory.GetInstance().Save(contentType); + content = new Content("test", -1, contentType); + contentService.Save(content); + + // assert that it is possible to get the document back + content = contentService.GetById(content.Id); + Assert.IsNotNull(content); + Assert.AreEqual("test", content.Name); + + // need an UmbracoCOntext to access the cache + // fixme - not exactly pretty, should not depend on HttpContext + var httpContext = Mock.Of(); + var withUmbracoContext = UmbracoContext.EnsureContext(httpContext); + var umbracoContext = Umbraco.Web.Composing.Current.UmbracoContext; + + // assert that there is no published document + var pcontent = umbracoContext.ContentCache.GetById(content.Id); + Assert.IsNull(pcontent); + + // but a draft document + pcontent = umbracoContext.ContentCache.GetById(true, content.Id); + Assert.IsNotNull(pcontent); + Assert.AreEqual("test", pcontent.Name); + Assert.IsTrue(pcontent.IsDraft()); + + // no published url + Assert.AreEqual("#", pcontent.GetUrl()); + + // now publish the document + make some unpublished changes + contentService.SaveAndPublish(content); + content.Name = "testx"; + contentService.Save(content); + + // assert that snapshot has been updated and there is now a published document + pcontent = umbracoContext.ContentCache.GetById(content.Id); + Assert.IsNotNull(pcontent); + Assert.AreEqual("test", pcontent.Name); + Assert.IsFalse(pcontent.IsDraft()); + + // but the url is the published one - no draft url + Assert.AreEqual("/test/", pcontent.GetUrl()); + + // and also an updated draft document + pcontent = umbracoContext.ContentCache.GetById(true, content.Id); + Assert.IsNotNull(pcontent); + Assert.AreEqual("testx", pcontent.Name); + Assert.IsTrue(pcontent.IsDraft()); + + // and the published document has a url + Assert.AreEqual("/test/", pcontent.GetUrl()); + + withUmbracoContext.Dispose(); + mainDom.Stop(); + components.Terminate(); + + // exit! + } + + [Test] + [Explicit("This test must be run manually")] + public void ValidateComposition() + { + // this is almost what CoreRuntime does, without + // - managing MainDom + // - configuring for unhandled exceptions, assembly resolution, application root path + // - testing for database, and for upgrades (runtime level) + // - assigning the factory to Current.Factory + + // create the very basic and essential things we need + var logger = new ConsoleLogger(); + var profiler = Mock.Of(); + var profilingLogger = new ProfilingLogger(logger, profiler); + var appCaches = CacheHelper.Disabled; + var databaseFactory = Mock.Of(); + var typeLoader = new TypeLoader(appCaches.RuntimeCache, LocalTempStorage.Default, profilingLogger); + var runtimeState = Mock.Of(); + Mock.Get(runtimeState).Setup(x => x.Level).Returns(RuntimeLevel.Run); + var mainDom = Mock.Of(); + Mock.Get(mainDom).Setup(x => x.IsMainDom).Returns(true); + + // create the register and the composition + var register = RegisterFactory.Create(); + var composition = new Composition(register, typeLoader, profilingLogger, runtimeState); + composition.RegisterEssentials(logger, profiler, profilingLogger, mainDom, appCaches, databaseFactory, typeLoader, runtimeState); + + // create the core runtime and have it compose itself + var coreRuntime = new CoreRuntime(); + coreRuntime.Compose(composition); + + // get the components + // all of them? + var composerTypes = typeLoader.GetTypes(); + // filtered + composerTypes = composerTypes + .Where(x => !x.FullName.StartsWith("Umbraco.Tests")); + // single? + //var componentTypes = new[] { typeof(CoreRuntimeComponent) }; + var composers = new Composers(composition, composerTypes, profilingLogger); + + // get components to compose themselves + composers.Compose(); + + // create the factory + var factory = composition.CreateFactory(); + + // at that point Umbraco is fully composed + // but nothing is initialized (no maindom, nothing - beware!) + // to actually *run* Umbraco standalone, better use a StandaloneRuntime + // that would inherit from CoreRuntime and ensure everything starts + + // get components to initialize themselves + //components.Initialize(factory); + + // and then, validate + var lightInjectContainer = (LightInject.ServiceContainer) factory.Concrete; + var results = lightInjectContainer.Validate().ToList(); + foreach (var resultGroup in results.GroupBy(x => x.Severity).OrderBy(x => x.Key)) + { + Console.WriteLine($"{resultGroup.Key}: {resultGroup.Count()}"); + } + + foreach (var resultGroup in results.GroupBy(x => x.Severity).OrderBy(x => x.Key)) + foreach (var result in resultGroup) + { + Console.WriteLine(); + Console.Write(ToText(result)); + } + + Assert.AreEqual(0, results.Count); + } + + private static string ToText(ValidationResult result) + { + var text = new StringBuilder(); + + text.AppendLine($"{result.Severity}: {WordWrap(result.Message, 120)}"); + var target = result.ValidationTarget; + text.Append("\tsvce: "); + text.Append(target.ServiceName); + text.Append(target.DeclaringService.ServiceType); + if (!target.DeclaringService.ServiceName.IsNullOrWhiteSpace()) + { + text.Append(" '"); + text.Append(target.DeclaringService.ServiceName); + text.Append("'"); + } + + text.Append(" ("); + if (target.DeclaringService.Lifetime == null) + text.Append("Transient"); + else + text.Append(target.DeclaringService.Lifetime.ToString().TrimStart("LightInject.").TrimEnd("Lifetime")); + text.AppendLine(")"); + text.Append("\timpl: "); + text.Append(target.DeclaringService.ImplementingType); + text.AppendLine(); + text.Append("\tparm: "); + text.Append(target.Parameter); + text.AppendLine(); + + return text.ToString(); + } + + private static string WordWrap(string text, int width) + { + int pos, next; + var sb = new StringBuilder(); + var nl = Environment.NewLine; + + // Lucidity check + if (width < 1) + return text; + + // Parse each line of text + for (pos = 0; pos < text.Length; pos = next) + { + // Find end of line + var eol = text.IndexOf(nl, pos, StringComparison.Ordinal); + + if (eol == -1) + next = eol = text.Length; + else + next = eol + nl.Length; + + // Copy this line of text, breaking into smaller lines as needed + if (eol > pos) + { + do + { + var len = eol - pos; + + if (len > width) + len = BreakLine(text, pos, width); + + if (pos > 0) + sb.Append("\t\t"); + sb.Append(text, pos, len); + sb.Append(nl); + + // Trim whitespace following break + pos += len; + + while (pos < eol && char.IsWhiteSpace(text[pos])) + pos++; + + } while (eol > pos); + } + else sb.Append(nl); // Empty line + } + + return sb.ToString(); + } + + private static int BreakLine(string text, int pos, int max) + { + // Find last whitespace in line + var i = max - 1; + while (i >= 0 && !char.IsWhiteSpace(text[pos + i])) + i--; + if (i < 0) + return max; // No whitespace found; break at maximum length + // Find start of whitespace + while (i >= 0 && char.IsWhiteSpace(text[pos + i])) + i--; + // Return length of text before whitespace + return i + 1; + } + } +} diff --git a/src/Umbraco.Tests/Runtimes/WebRuntimeTests.cs b/src/Umbraco.Tests/Runtimes/WebRuntimeComponentTests.cs similarity index 92% rename from src/Umbraco.Tests/Runtimes/WebRuntimeTests.cs rename to src/Umbraco.Tests/Runtimes/WebRuntimeComponentTests.cs index b9f8f6576b..97688a7113 100644 --- a/src/Umbraco.Tests/Runtimes/WebRuntimeTests.cs +++ b/src/Umbraco.Tests/Runtimes/WebRuntimeComponentTests.cs @@ -2,14 +2,13 @@ using System.Web.Mvc; using NUnit.Framework; using Umbraco.Core.Profiling; -using Umbraco.Web; using Umbraco.Web.Mvc; using Umbraco.Web.Runtime; namespace Umbraco.Tests.Runtimes { [TestFixture] - public class WebRuntimeTests + public class WebRuntimeComponentTests { [Test] public void WrapViewEngines_HasEngines_WrapsAll() @@ -43,7 +42,6 @@ namespace Umbraco.Tests.Runtimes Assert.That(((ProfilingViewEngine)engines[1]).Inner, Is.InstanceOf()); } - [Test] public void WrapViewEngines_HasProfiledEngine_AddsSameInstance() { @@ -61,10 +59,7 @@ namespace Umbraco.Tests.Runtimes [Test] public void WrapViewEngines_CollectionIsNull_DoesNotThrow() { - IList engines = null; - Assert.DoesNotThrow(() => WebRuntimeComponent.WrapViewEngines(engines)); - Assert.That(engines, Is.Null); + Assert.DoesNotThrow(() => WebRuntimeComponent.WrapViewEngines(null)); } - } } diff --git a/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs b/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs index 59cf30c0fb..3664717af7 100644 --- a/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs +++ b/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs @@ -13,6 +13,7 @@ namespace Umbraco.Tests.Scheduling { [TestFixture] [Timeout(30000)] + [Category("Slow")] public class BackgroundTaskRunnerTests { private ILogger _logger; diff --git a/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs b/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs index 4d49747004..4aa28b5975 100644 --- a/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs @@ -1,10 +1,9 @@ using System; -using System.Collections.Generic; using System.Linq; -using LightInject; using Moq; using NUnit.Framework; -using Umbraco.Core.Collections; +using Umbraco.Core; +using Umbraco.Core.Components; using Umbraco.Core.Events; using Umbraco.Core.Models; using Umbraco.Core.IO; @@ -15,6 +14,7 @@ using Umbraco.Tests.TestHelpers.Entities; using Umbraco.Core.Composing; using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Services; +using Umbraco.Tests.Components; namespace Umbraco.Tests.Scoping { @@ -31,12 +31,20 @@ namespace Umbraco.Tests.Scoping DoThing2 = null; DoThing3 = null; - Current.Container = new ServiceContainer(); + var register = RegisterFactory.Create(); - _testObjects = new TestObjects(Current.Container); - Current.Container.RegisterSingleton(f => Current.Container); - Current.Container.RegisterSingleton(factory => new FileSystems(factory.TryGetInstance())); - Current.Container.RegisterCollectionBuilder(); + var composition = new Composition(register, new TypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); + + _testObjects = new TestObjects(register); + + composition.RegisterUnique(factory => new FileSystems(factory, factory.TryGetInstance())); + composition.WithCollectionBuilder(); + + composition.Configs.Add(SettingsForTests.GetDefaultGlobalSettings); + composition.Configs.Add(SettingsForTests.GetDefaultUmbracoSettings); + + Current.Reset(); + Current.Factory = composition.CreateFactory(); SettingsForTests.Reset(); // ensure we have configuration } diff --git a/src/Umbraco.Tests/Scoping/ScopeFileSystemsTests.cs b/src/Umbraco.Tests/Scoping/ScopeFileSystemsTests.cs index ae16ba484f..0ef0e9362b 100644 --- a/src/Umbraco.Tests/Scoping/ScopeFileSystemsTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopeFileSystemsTests.cs @@ -4,8 +4,8 @@ using System.Text; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Composing; +using Umbraco.Core.Composing.Composers; using Umbraco.Core.IO; -using Umbraco.Core.Scoping; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; @@ -23,11 +23,21 @@ namespace Umbraco.Tests.Scoping ClearFiles(); } + protected override void ComposeApplication(bool withApplication) + { + base.ComposeApplication(withApplication); + + if (!withApplication) return; + + // re-register with actual media fs + Composition.ComposeFileSystems(); + } + public override void TearDown() { base.TearDown(); SafeCallContext.Clear(); - ShadowFileSystems.ResetId(); + FileSystems.ResetShadowId(); ClearFiles(); } @@ -35,7 +45,7 @@ namespace Umbraco.Tests.Scoping { TestHelper.DeleteDirectory(IOHelper.MapPath("media")); TestHelper.DeleteDirectory(IOHelper.MapPath("FileSysTests")); - TestHelper.DeleteDirectory(IOHelper.MapPath("App_Data")); + TestHelper.DeleteDirectory(IOHelper.MapPath("App_Data/TEMP/ShadowFs")); } [TestCase(true)] @@ -43,7 +53,7 @@ namespace Umbraco.Tests.Scoping public void CreateMediaTest(bool complete) { var physMediaFileSystem = new PhysicalFileSystem(IOHelper.MapPath("media"), "ignore"); - var mediaFileSystem = Current.FileSystems.MediaFileSystem; + var mediaFileSystem = Current.MediaFileSystem; Assert.IsFalse(physMediaFileSystem.FileExists("f1.txt")); @@ -62,12 +72,12 @@ namespace Umbraco.Tests.Scoping if (complete) { - Assert.IsTrue(Current.FileSystems.MediaFileSystem.FileExists("f1.txt")); + Assert.IsTrue(Current.MediaFileSystem.FileExists("f1.txt")); Assert.IsTrue(physMediaFileSystem.FileExists("f1.txt")); } else { - Assert.IsFalse(Current.FileSystems.MediaFileSystem.FileExists("f1.txt")); + Assert.IsFalse(Current.MediaFileSystem.FileExists("f1.txt")); Assert.IsFalse(physMediaFileSystem.FileExists("f1.txt")); } } @@ -76,7 +86,7 @@ namespace Umbraco.Tests.Scoping public void MultiThread() { var physMediaFileSystem = new PhysicalFileSystem(IOHelper.MapPath("media"), "ignore"); - var mediaFileSystem = Current.FileSystems.MediaFileSystem; + var mediaFileSystem = Current.MediaFileSystem; var scopeProvider = ScopeProvider; using (var scope = scopeProvider.CreateScope(scopeFileSystems: true)) diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index 045a6ad147..6410750aff 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Web.Routing; -using LightInject; using Moq; using NUnit.Framework; using Umbraco.Core; @@ -15,7 +14,6 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Persistence.Repositories; -using Umbraco.Core.Persistence.Repositories.Implement; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Core.Services.Implement; @@ -47,10 +45,10 @@ namespace Umbraco.Tests.Scoping // but then, it requires a lot of plumbing ;( // fixme - and we cannot inject a DistributedCache yet // so doing all this mess - Container.RegisterSingleton(); - Container.RegisterSingleton(f => Mock.Of()); - Container.RegisterCollectionBuilder() - .Add(f => f.TryGetInstance().GetCacheRefreshers()); + Composition.RegisterUnique(); + Composition.RegisterUnique(f => Mock.Of()); + Composition.WithCollectionBuilder() + .Add(() => Composition.TypeLoader.GetCacheRefreshers()); } public override void TearDown() @@ -98,7 +96,7 @@ namespace Umbraco.Tests.Scoping documentRepository, mediaRepository, memberRepository, DefaultCultureAccessor, new DatabaseDataSource(), - Container.GetInstance(), new SiteDomainHelper(), contentTypeService); + Factory.GetInstance(), new SiteDomainHelper(), contentTypeService); } protected UmbracoContext GetUmbracoContextNu(string url, int templateId = 1234, RouteData routeData = null, bool setSingleton = false, IUmbracoSettingsSection umbracoSettings = null, IEnumerable urlProviders = null) diff --git a/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs b/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs index 9ace5860e1..3a0cb00ea9 100644 --- a/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs @@ -10,7 +10,6 @@ using Umbraco.Core.Scoping; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; using Umbraco.Web.Cache; -using LightInject; using Moq; using Umbraco.Core.Events; using Umbraco.Core.Logging; @@ -32,22 +31,20 @@ namespace Umbraco.Tests.Scoping // but then, it requires a lot of plumbing ;( // fixme - and we cannot inject a DistributedCache yet // so doing all this mess - Container.RegisterSingleton(); - Container.RegisterSingleton(f => Mock.Of()); - Container.RegisterCollectionBuilder() - .Add(f => f.TryGetInstance().GetCacheRefreshers()); + Composition.RegisterUnique(); + Composition.RegisterUnique(f => Mock.Of()); + Composition.WithCollectionBuilder() + .Add(() => Composition.TypeLoader.GetCacheRefreshers()); } - protected override void ComposeCacheHelper() + protected override CacheHelper GetCacheHelper() { // this is what's created core web runtime - var cacheHelper = new CacheHelper( + return new CacheHelper( new DeepCloneRuntimeCacheProvider(new ObjectCacheRuntimeCacheProvider()), new StaticCacheProvider(), NullCacheProvider.Instance, new IsolatedRuntimeCache(type => new DeepCloneRuntimeCacheProvider(new ObjectCacheRuntimeCacheProvider()))); - Container.RegisterSingleton(f => cacheHelper); - Container.RegisterSingleton(f => f.GetInstance().RuntimeCache); } [TearDown] diff --git a/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs b/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs index 1afbf5cb12..568bafa4e6 100644 --- a/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs @@ -3,9 +3,9 @@ using System.Collections.Generic; using System.Xml; using Moq; using NUnit.Framework; -using LightInject; using Umbraco.Core.Cache; using Umbraco.Core.Composing; +using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Events; using Umbraco.Core.Logging; using Umbraco.Core.Models; @@ -34,10 +34,16 @@ namespace Umbraco.Tests.Scoping // but then, it requires a lot of plumbing ;( // fixme - and we cannot inject a DistributedCache yet // so doing all this mess - Container.RegisterSingleton(); - Container.RegisterSingleton(f => Mock.Of()); - Container.RegisterCollectionBuilder() - .Add(f => f.TryGetInstance().GetCacheRefreshers()); + Composition.RegisterUnique(); + Composition.RegisterUnique(f => Mock.Of()); + Composition.WithCollectionBuilder() + .Add(() => Composition.TypeLoader.GetCacheRefreshers()); + } + + protected override void ComposeSettings() + { + Composition.Configs.Add(SettingsForTests.GenerateMockUmbracoSettings); + Composition.Configs.Add(SettingsForTests.GenerateMockGlobalSettings); } [TearDown] @@ -65,7 +71,7 @@ namespace Umbraco.Tests.Scoping // xmlStore.Xml - the actual main xml document // publishedContentCache.GetXml() - the captured xml - private static XmlStore XmlStore => (Current.Container.GetInstance() as PublishedSnapshotService).XmlStore; + private static XmlStore XmlStore => (Current.Factory.GetInstance() as PublishedSnapshotService).XmlStore; private static XmlDocument XmlMaster => XmlStore.Xml; private static XmlDocument XmlInContext => ((PublishedContentCache) Umbraco.Web.Composing.Current.UmbracoContext.ContentCache).GetXml(false); @@ -80,10 +86,8 @@ namespace Umbraco.Tests.Scoping Assert.AreSame(XmlStore, ((PublishedContentCache) umbracoContext.ContentCache).XmlStore); // settings - var settings = SettingsForTests.GenerateMockUmbracoSettings(); - var contentMock = Mock.Get(settings.Content); + var contentMock = Mock.Get(Factory.GetInstance().Content); contentMock.Setup(x => x.XmlCacheEnabled).Returns(false); - SettingsForTests.ConfigureSettings(settings); // create document type, document var contentType = new ContentType(-1) { Alias = "CustomDocument", Name = "Custom Document" }; @@ -201,9 +205,8 @@ namespace Umbraco.Tests.Scoping // settings var settings = SettingsForTests.GenerateMockUmbracoSettings(); - var contentMock = Mock.Get(settings.Content); + var contentMock = Mock.Get(Factory.GetInstance().Content); contentMock.Setup(x => x.XmlCacheEnabled).Returns(false); - SettingsForTests.ConfigureSettings(settings); // create document type var contentType = new ContentType(-1) { Alias = "CustomDocument", Name = "Custom Document" }; diff --git a/src/Umbraco.Tests/Services/ContentServicePerformanceTest.cs b/src/Umbraco.Tests/Services/ContentServicePerformanceTest.cs index 322b79429e..3aefa88a50 100644 --- a/src/Umbraco.Tests/Services/ContentServicePerformanceTest.cs +++ b/src/Umbraco.Tests/Services/ContentServicePerformanceTest.cs @@ -5,6 +5,8 @@ using System.Linq; using Moq; using NUnit.Framework; using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; using Umbraco.Core.Logging; @@ -34,7 +36,7 @@ namespace Umbraco.Tests.Services protected override void Compose() { base.Compose(); - Container.Register(); + Composition.Register(); } [Test] @@ -43,7 +45,7 @@ namespace Umbraco.Tests.Services Assert.IsInstanceOf(Current.Profiler); } - private static ProfilingLogger GetTestProfilingLogger() + private static IProfilingLogger GetTestProfilingLogger() { var logger = new DebugDiagnosticsLogger(); var profiler = new TestProfiler(); @@ -161,11 +163,11 @@ namespace Umbraco.Tests.Services var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var tRepository = new TemplateRepository((IScopeAccessor) provider, DisabledCache, Logger, Mock.Of(), Mock.Of(), Mock.Of()); - var tagRepo = new TagRepository((IScopeAccessor) provider, DisabledCache, Logger); - var ctRepository = new ContentTypeRepository((IScopeAccessor) provider, DisabledCache, Logger, tRepository); - var languageRepository = new LanguageRepository((IScopeAccessor) provider, DisabledCache, Logger); - var repository = new DocumentRepository((IScopeAccessor) provider, DisabledCache, Logger, ctRepository, tRepository, tagRepo, languageRepository, Mock.Of()); + var tRepository = new TemplateRepository((IScopeAccessor) provider, CacheHelper.Disabled, Logger, Mock.Of(), TestObjects.GetFileSystemsMock()); + var tagRepo = new TagRepository((IScopeAccessor) provider, CacheHelper.Disabled, Logger); + var ctRepository = new ContentTypeRepository((IScopeAccessor) provider, CacheHelper.Disabled, Logger, tRepository); + var languageRepository = new LanguageRepository((IScopeAccessor) provider, CacheHelper.Disabled, Logger); + var repository = new DocumentRepository((IScopeAccessor) provider, CacheHelper.Disabled, Logger, ctRepository, tRepository, tagRepo, languageRepository, Mock.Of()); // Act Stopwatch watch = Stopwatch.StartNew(); @@ -194,11 +196,11 @@ namespace Umbraco.Tests.Services var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var tRepository = new TemplateRepository((IScopeAccessor) provider, DisabledCache, Logger, Mock.Of(), Mock.Of(), Mock.Of()); - var tagRepo = new TagRepository((IScopeAccessor) provider, DisabledCache, Logger); - var ctRepository = new ContentTypeRepository((IScopeAccessor) provider, DisabledCache, Logger, tRepository); - var languageRepository = new LanguageRepository((IScopeAccessor) provider, DisabledCache, Logger); - var repository = new DocumentRepository((IScopeAccessor) provider, DisabledCache, Logger, ctRepository, tRepository, tagRepo, languageRepository, Mock.Of()); + var tRepository = new TemplateRepository((IScopeAccessor) provider, CacheHelper.Disabled, Logger, Mock.Of(), TestObjects.GetFileSystemsMock()); + var tagRepo = new TagRepository((IScopeAccessor) provider, CacheHelper.Disabled, Logger); + var ctRepository = new ContentTypeRepository((IScopeAccessor) provider, CacheHelper.Disabled, Logger, tRepository); + var languageRepository = new LanguageRepository((IScopeAccessor) provider, CacheHelper.Disabled, Logger); + var repository = new DocumentRepository((IScopeAccessor) provider, CacheHelper.Disabled, Logger, ctRepository, tRepository, tagRepo, languageRepository, Mock.Of()); // Act Stopwatch watch = Stopwatch.StartNew(); @@ -225,11 +227,11 @@ namespace Umbraco.Tests.Services var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var tRepository = new TemplateRepository((IScopeAccessor) provider, DisabledCache, Logger, Mock.Of(), Mock.Of(), Mock.Of()); - var tagRepo = new TagRepository((IScopeAccessor) provider, DisabledCache, Logger); - var ctRepository = new ContentTypeRepository((IScopeAccessor) provider, DisabledCache, Logger, tRepository); - var languageRepository = new LanguageRepository((IScopeAccessor) provider, DisabledCache, Logger); - var repository = new DocumentRepository((IScopeAccessor) provider, DisabledCache, Logger, ctRepository, tRepository, tagRepo, languageRepository, Mock.Of()); + var tRepository = new TemplateRepository((IScopeAccessor) provider, CacheHelper.Disabled, Logger, Mock.Of(), TestObjects.GetFileSystemsMock()); + var tagRepo = new TagRepository((IScopeAccessor) provider, CacheHelper.Disabled, Logger); + var ctRepository = new ContentTypeRepository((IScopeAccessor) provider, CacheHelper.Disabled, Logger, tRepository); + var languageRepository = new LanguageRepository((IScopeAccessor) provider, CacheHelper.Disabled, Logger); + var repository = new DocumentRepository((IScopeAccessor) provider, CacheHelper.Disabled, Logger, ctRepository, tRepository, tagRepo, languageRepository, Mock.Of()); // Act var contents = repository.GetMany(); @@ -259,11 +261,11 @@ namespace Umbraco.Tests.Services var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var tRepository = new TemplateRepository((IScopeAccessor) provider, DisabledCache, Logger, Mock.Of(), Mock.Of(), Mock.Of()); - var tagRepo = new TagRepository((IScopeAccessor) provider, DisabledCache, Logger); - var ctRepository = new ContentTypeRepository((IScopeAccessor) provider, DisabledCache, Logger, tRepository); - var languageRepository = new LanguageRepository((IScopeAccessor) provider, DisabledCache, Logger); - var repository = new DocumentRepository((IScopeAccessor) provider, DisabledCache, Logger, ctRepository, tRepository, tagRepo, languageRepository, Mock.Of()); + var tRepository = new TemplateRepository((IScopeAccessor) provider, CacheHelper.Disabled, Logger, Mock.Of(), TestObjects.GetFileSystemsMock()); + var tagRepo = new TagRepository((IScopeAccessor) provider, CacheHelper.Disabled, Logger); + var ctRepository = new ContentTypeRepository((IScopeAccessor) provider, CacheHelper.Disabled, Logger, tRepository); + var languageRepository = new LanguageRepository((IScopeAccessor) provider, CacheHelper.Disabled, Logger); + var repository = new DocumentRepository((IScopeAccessor) provider, CacheHelper.Disabled, Logger, ctRepository, tRepository, tagRepo, languageRepository, Mock.Of()); // Act var contents = repository.GetMany(); diff --git a/src/Umbraco.Tests/Services/ContentServiceTagsTests.cs b/src/Umbraco.Tests/Services/ContentServiceTagsTests.cs index 8f66e98b76..62a3526af5 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTagsTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTagsTests.cs @@ -1,6 +1,6 @@ using System; using System.Linq; -using LightInject; +using Umbraco.Core.Composing; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Models; @@ -36,7 +36,7 @@ namespace Umbraco.Tests.Services base.Compose(); // fixme - do it differently - Container.Register(factory => factory.GetInstance().TextService); + Composition.Register(factory => factory.GetInstance().TextService); } [Test] diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 0cd7207e6c..a442a40576 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -7,10 +7,8 @@ using Moq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.IO; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; -using LightInject; using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers.Entities; using Umbraco.Core.Events; @@ -22,6 +20,8 @@ using Umbraco.Core.Services.Implement; using Umbraco.Tests.Testing; using System.Reflection; using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Cache; +using Umbraco.Core.Composing; namespace Umbraco.Tests.Services { @@ -31,6 +31,7 @@ namespace Umbraco.Tests.Services /// as well as configuration. /// [TestFixture] + [Category("Slow")] [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, PublishedRepositoryEvents = true, WithApplication = true, @@ -56,8 +57,7 @@ namespace Umbraco.Tests.Services { base.Compose(); - // fixme - do it differently - Container.Register(factory => factory.GetInstance().TextService); + Composition.RegisterUnique(factory => Mock.Of()); } /// @@ -796,6 +796,54 @@ namespace Umbraco.Tests.Services } + [Test] + public void Pending_Invariant_Property_Changes_Affect_Default_Language_Edited_State() + { + // Arrange + + var langGB = new Language("en-GB") { IsDefault = true }; + var langFr = new Language("fr-FR"); + + ServiceContext.LocalizationService.Save(langFr); + ServiceContext.LocalizationService.Save(langGB); + + var contentType = MockedContentTypes.CreateMetaContentType(); + contentType.Variations = ContentVariation.Culture; + foreach(var prop in contentType.PropertyTypes) + prop.Variations = ContentVariation.Culture; + var keywordsProp = contentType.PropertyTypes.Single(x => x.Alias == "metakeywords"); + keywordsProp.Variations = ContentVariation.Nothing; // this one is invariant + + ServiceContext.ContentTypeService.Save(contentType); + + IContent content = new Content("content", -1, contentType); + content.SetCultureName("content-en", langGB.IsoCode); + content.SetCultureName("content-fr", langFr.IsoCode); + content.PublishCulture(langGB.IsoCode); + content.PublishCulture(langFr.IsoCode); + Assert.IsTrue(ServiceContext.ContentService.SavePublishing(content).Success); + + //re-get + content = ServiceContext.ContentService.GetById(content.Id); + Assert.AreEqual(PublishedState.Published, content.PublishedState); + Assert.IsTrue(content.IsCulturePublished(langGB.IsoCode)); + Assert.IsTrue(content.IsCulturePublished(langFr.IsoCode)); + Assert.IsFalse(content.IsCultureEdited(langGB.IsoCode)); + Assert.IsFalse(content.IsCultureEdited(langFr.IsoCode)); + + //update the invariant property and save a pending version + content.SetValue("metakeywords", "hello"); + ServiceContext.ContentService.Save(content); + + //re-get + content = ServiceContext.ContentService.GetById(content.Id); + Assert.AreEqual(PublishedState.Published, content.PublishedState); + Assert.IsTrue(content.IsCulturePublished(langGB.IsoCode)); + Assert.IsTrue(content.IsCulturePublished(langFr.IsoCode)); + Assert.IsTrue(content.IsCultureEdited(langGB.IsoCode)); + Assert.IsFalse(content.IsCultureEdited(langFr.IsoCode)); + } + [Test] public void Can_Publish_Content_Variation_And_Detect_Changed_Cultures() { @@ -2993,11 +3041,11 @@ namespace Umbraco.Tests.Services private DocumentRepository CreateRepository(IScopeProvider provider, out ContentTypeRepository contentTypeRepository) { var accessor = (IScopeAccessor) provider; - var templateRepository = new TemplateRepository(accessor, DisabledCache, Logger, Mock.Of(), Mock.Of(), Mock.Of()); - var tagRepository = new TagRepository(accessor, DisabledCache, Logger); - contentTypeRepository = new ContentTypeRepository(accessor, DisabledCache, Logger, templateRepository); - var languageRepository = new LanguageRepository(accessor, DisabledCache, Logger); - var repository = new DocumentRepository(accessor, DisabledCache, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, Mock.Of()); + var templateRepository = new TemplateRepository(accessor, CacheHelper.Disabled, Logger, Mock.Of(), TestObjects.GetFileSystemsMock()); + var tagRepository = new TagRepository(accessor, CacheHelper.Disabled, Logger); + contentTypeRepository = new ContentTypeRepository(accessor, CacheHelper.Disabled, Logger, templateRepository); + var languageRepository = new LanguageRepository(accessor, CacheHelper.Disabled, Logger); + var repository = new DocumentRepository(accessor, CacheHelper.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, Mock.Of()); return repository; } } diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs index 8ea4856861..b1a8fa26a8 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs @@ -17,6 +17,7 @@ using Umbraco.Tests.Scoping; namespace Umbraco.Tests.Services { [TestFixture] + [Category("Slow")] [Apartment(ApartmentState.STA)] [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, PublishedRepositoryEvents = true)] public class ContentTypeServiceTests : TestWithSomeContentBase diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs index 8f453c6d53..f9c15bc628 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Threading; -using LightInject; using Moq; using NUnit.Framework; using Umbraco.Core; @@ -35,10 +34,10 @@ namespace Umbraco.Tests.Services base.Compose(); // pfew - see note in ScopedNuCacheTests? - Container.RegisterSingleton(); - Container.RegisterSingleton(f => Mock.Of()); - Container.RegisterCollectionBuilder() - .Add(f => f.TryGetInstance().GetCacheRefreshers()); + Composition.RegisterUnique(); + Composition.RegisterUnique(f => Mock.Of()); + Composition.WithCollectionBuilder() + .Add(() => Composition.TypeLoader.GetCacheRefreshers()); } protected override IPublishedSnapshotService CreatePublishedSnapshotService() @@ -50,7 +49,7 @@ namespace Umbraco.Tests.Services var contentTypeFactory = new PublishedContentTypeFactory(Mock.Of(), new PropertyValueConverterCollection(Array.Empty()), Mock.Of()); //var documentRepository = Mock.Of(); - var documentRepository = Container.GetInstance(); + var documentRepository = Factory.GetInstance(); var mediaRepository = Mock.Of(); var memberRepository = Mock.Of(); var contentTypeService = Current.Services.ContentTypeService; @@ -69,7 +68,7 @@ namespace Umbraco.Tests.Services documentRepository, mediaRepository, memberRepository, DefaultCultureAccessor, new DatabaseDataSource(), - Container.GetInstance(), new SiteDomainHelper(), contentTypeService); + Factory.GetInstance(), new SiteDomainHelper(), contentTypeService); } public class LocalServerMessenger : ServerMessengerBase diff --git a/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs b/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs index b33ff83c4a..8e67aa4e1f 100644 --- a/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs +++ b/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Threading; using System.Xml.Linq; @@ -7,15 +6,16 @@ using NUnit.Framework; using Umbraco.Core.Models; using Umbraco.Core; using Umbraco.Core.Composing; +using Umbraco.Core.Composing.Composers; using Umbraco.Core.Logging; using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.PropertyEditors; using Umbraco.Tests.Testing; -using LightInject; namespace Umbraco.Tests.Services.Importing { [TestFixture] + [Category("Slow")] [Apartment(ApartmentState.STA)] [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] public class PackageImportTests : TestWithSomeContentBase @@ -49,11 +49,21 @@ namespace Umbraco.Tests.Services.Importing // pollute everything, they are ignored by the type finder and explicitely // added to the editors collection - Container.GetInstance() + Composition.WithCollectionBuilder() .Add() .Add(); } + protected override void ComposeApplication(bool withApplication) + { + base.ComposeApplication(withApplication); + + if (!withApplication) return; + + // re-register with actual media fs + Composition.ComposeFileSystems(); + } + [Test] public void PackagingService_Can_Import_uBlogsy_ContentTypes_And_Verify_Structure() { diff --git a/src/Umbraco.Tests/Services/MacroServiceTests.cs b/src/Umbraco.Tests/Services/MacroServiceTests.cs index 6539a37114..52cc8ab0d1 100644 --- a/src/Umbraco.Tests/Services/MacroServiceTests.cs +++ b/src/Umbraco.Tests/Services/MacroServiceTests.cs @@ -26,7 +26,7 @@ namespace Umbraco.Tests.Services var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); + var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.Disabled, Mock.Of()); repository.Save(new Macro("test1", "Test1", "~/views/macropartials/test1.cshtml", MacroTypes.PartialView)); repository.Save(new Macro("test2", "Test2", "~/views/macropartials/test2.cshtml", MacroTypes.PartialView)); diff --git a/src/Umbraco.Tests/Services/MemberServiceTests.cs b/src/Umbraco.Tests/Services/MemberServiceTests.cs index 078336262f..13cde1c659 100644 --- a/src/Umbraco.Tests/Services/MemberServiceTests.cs +++ b/src/Umbraco.Tests/Services/MemberServiceTests.cs @@ -24,6 +24,7 @@ using Umbraco.Web.Security.Providers; namespace Umbraco.Tests.Services { [TestFixture] + [Category("Slow")] [Apartment(ApartmentState.STA)] [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, PublishedRepositoryEvents = true, WithApplication = true)] public class MemberServiceTests : TestWithSomeContentBase diff --git a/src/Umbraco.Tests/Services/PerformanceTests.cs b/src/Umbraco.Tests/Services/PerformanceTests.cs index 900a466a1d..09743b350f 100644 --- a/src/Umbraco.Tests/Services/PerformanceTests.cs +++ b/src/Umbraco.Tests/Services/PerformanceTests.cs @@ -57,7 +57,7 @@ namespace Umbraco.Tests.Services base.TearDown(); } - private static ProfilingLogger GetTestProfilingLogger() + private static IProfilingLogger GetTestProfilingLogger() { var logger = new DebugDiagnosticsLogger(); var profiler = new TestProfiler(); diff --git a/src/Umbraco.Tests/Strings/CmsHelperCasingTests.cs b/src/Umbraco.Tests/Strings/CmsHelperCasingTests.cs index 0fb0dee68c..a4006409be 100644 --- a/src/Umbraco.Tests/Strings/CmsHelperCasingTests.cs +++ b/src/Umbraco.Tests/Strings/CmsHelperCasingTests.cs @@ -9,15 +9,6 @@ namespace Umbraco.Tests.Strings [TestFixture] public class CmsHelperCasingTests : UmbracoTestBase { - [SetUp] - public void Setup() - { - //set default config - var config = SettingsForTests.GetDefaultUmbracoSettings(); - SettingsForTests.ConfigureSettings(config); - - } - [TestCase("thisIsTheEnd", "This Is The End")] [TestCase("th", "Th")] [TestCase("t", "t")] diff --git a/src/Umbraco.Tests/Strings/DefaultShortStringHelperTests.cs b/src/Umbraco.Tests/Strings/DefaultShortStringHelperTests.cs index 36e5874e14..0d39fcc9e5 100644 --- a/src/Umbraco.Tests/Strings/DefaultShortStringHelperTests.cs +++ b/src/Umbraco.Tests/Strings/DefaultShortStringHelperTests.cs @@ -1,15 +1,13 @@ using System.Collections.Generic; using System.Diagnostics; -using System.Globalization; using System.Linq; using System.Text; using System.Text.RegularExpressions; -using LightInject; using Moq; using NUnit.Framework; using Umbraco.Core; -using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Composing; +using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Strings; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; @@ -71,7 +69,7 @@ namespace Umbraco.Tests.Strings })); // fixme - move to a "compose" thing? - Container.RegisterSingleton(f => _helper); + Composition.RegisterUnique(f => _helper); } private static readonly Regex FrenchElisionsRegex = new Regex("\\b(c|d|j|l|m|n|qu|s|t)('|\u8217)", RegexOptions.Compiled | RegexOptions.IgnoreCase); @@ -125,7 +123,6 @@ namespace Umbraco.Tests.Strings var contentMock = Mock.Get(settings.RequestHandler); contentMock.Setup(x => x.CharCollection).Returns(Enumerable.Empty()); contentMock.Setup(x => x.ConvertUrlsToAscii).Returns(false); - SettingsForTests.ConfigureSettings(settings); const string input1 = "ÆØÅ and æøå and 中文测试 and אודות האתר and größer БбДдЖж page"; const string input2 = "ÆØÅ and æøå and größer БбДдЖж page"; diff --git a/src/Umbraco.Tests/Strings/StringExtensionsTests.cs b/src/Umbraco.Tests/Strings/StringExtensionsTests.cs index 81dec809c8..f0db3991b8 100644 --- a/src/Umbraco.Tests/Strings/StringExtensionsTests.cs +++ b/src/Umbraco.Tests/Strings/StringExtensionsTests.cs @@ -1,15 +1,11 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Globalization; using System.Linq; -using Moq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Composing; -using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Strings; -using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; namespace Umbraco.Tests.Strings @@ -17,12 +13,10 @@ namespace Umbraco.Tests.Strings [TestFixture] public class StringExtensionsTests : UmbracoTestBase { - public override void SetUp() + protected override void Compose() { - base.SetUp(); - - // fixme - in "compose"? - Container.RegisterSingleton(_ => new MockShortStringHelper()); + base.Compose(); + Composition.RegisterUnique(_ => new MockShortStringHelper()); } [Test] @@ -75,6 +69,17 @@ namespace Umbraco.Tests.Strings Assert.AreEqual(stripped, result); } + [TestCase("'+alert(1234)+'", "+alert1234+")] + [TestCase("'+alert(56+78)+'", "+alert56+78+")] + [TestCase("{{file}}", "file")] + [TestCase("'+alert('hello')+'", "+alerthello+")] + [TestCase("Test", "Test")] + public void Clean_From_XSS(string input, string result) + { + var cleaned = input.CleanForXss(); + Assert.AreEqual(cleaned, result); + } + [TestCase("This is a string to encrypt")] [TestCase("This is a string to encrypt\nThis is a second line")] [TestCase(" White space is preserved ")] diff --git a/src/Umbraco.Tests/Templates/TemplateRepositoryTests.cs b/src/Umbraco.Tests/Templates/TemplateRepositoryTests.cs index 340f0ea79f..795d79ced1 100644 --- a/src/Umbraco.Tests/Templates/TemplateRepositoryTests.cs +++ b/src/Umbraco.Tests/Templates/TemplateRepositoryTests.cs @@ -6,9 +6,6 @@ using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; -using Umbraco.Core.Persistence; -using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.Repositories.Implement; using Umbraco.Core.Scoping; using Umbraco.Tests.TestHelpers; @@ -19,12 +16,11 @@ namespace Umbraco.Tests.Templates public class TemplateRepositoryTests { private readonly Mock _cacheMock = new Mock(); - private readonly Mock _viewFileSystemMock = new Mock(); - private readonly Mock _masterpageFileSystemMock = new Mock(); private readonly Mock _templateConfigMock = new Mock(); + private readonly IFileSystems _fileSystems = Mock.Of(); private TemplateRepository _templateRepository; - private readonly TestObjects TestObjects = new TestObjects(null); + private readonly TestObjects _testObjects = new TestObjects(null); [SetUp] public void Setup() @@ -33,12 +29,16 @@ namespace Umbraco.Tests.Templates var accessorMock = new Mock(); var scopeMock = new Mock(); - var database = TestObjects.GetUmbracoSqlCeDatabase(logger); + var database = _testObjects.GetUmbracoSqlCeDatabase(logger); scopeMock.Setup(x => x.Database).Returns(database); accessorMock.Setup(x => x.AmbientScope).Returns(scopeMock.Object); - _templateRepository = new TemplateRepository(accessorMock.Object, _cacheMock.Object, logger, _templateConfigMock.Object, _masterpageFileSystemMock.Object, _viewFileSystemMock.Object); + var mvcFs = Mock.Of(); + var masterFs = Mock.Of(); + Mock.Get(_fileSystems).Setup(x => x.MvcViewsFileSystem).Returns(mvcFs); + Mock.Get(_fileSystems).Setup(x => x.MasterPagesFileSystem).Returns(masterFs); + _templateRepository = new TemplateRepository(accessorMock.Object, _cacheMock.Object, logger, _templateConfigMock.Object, _fileSystems); } [Test] @@ -53,7 +53,8 @@ namespace Umbraco.Tests.Templates templateMock.Setup(x => x.Content).Returns(""); // but MVC View already exists - _viewFileSystemMock.Setup(x => x.FileExists(It.IsAny())).Returns(true); + Mock.Get(_fileSystems.MvcViewsFileSystem) + .Setup(x => x.FileExists(It.IsAny())).Returns(true); var res = _templateRepository.DetermineTemplateRenderingEngine(templateMock.Object); Assert.AreEqual(RenderingEngine.Mvc, res); @@ -71,7 +72,8 @@ namespace Umbraco.Tests.Templates templateMock.Setup(x => x.Content).Returns(""); // MVC View doesn't exist - _viewFileSystemMock.Setup(x => x.FileExists(It.IsAny())).Returns(false); + Mock.Get(_fileSystems.MvcViewsFileSystem) + .Setup(x => x.FileExists(It.IsAny())).Returns(false); var res = _templateRepository.DetermineTemplateRenderingEngine(templateMock.Object); Assert.AreEqual(RenderingEngine.WebForms, res); @@ -87,8 +89,10 @@ namespace Umbraco.Tests.Templates templateMock.Setup(x => x.Alias).Returns("Something"); // but masterpage already exists - _viewFileSystemMock.Setup(x => x.FileExists(It.IsAny())).Returns(false); - _masterpageFileSystemMock.Setup(x => x.FileExists(It.IsAny())).Returns(true); + Mock.Get(_fileSystems.MvcViewsFileSystem) + .Setup(x => x.FileExists(It.IsAny())).Returns(false); + Mock.Get(_fileSystems.MasterPagesFileSystem) + .Setup(x => x.FileExists(It.IsAny())).Returns(true); var res = _templateRepository.DetermineTemplateRenderingEngine(templateMock.Object); Assert.AreEqual(RenderingEngine.WebForms, res); @@ -104,8 +108,10 @@ namespace Umbraco.Tests.Templates templateMock.Setup(x => x.Alias).Returns("Something"); // but MVC View already exists - _viewFileSystemMock.Setup(x => x.FileExists(It.IsAny())).Returns(true); - _masterpageFileSystemMock.Setup(x => x.FileExists(It.IsAny())).Returns(false); + Mock.Get(_fileSystems.MvcViewsFileSystem) + .Setup(x => x.FileExists(It.IsAny())).Returns(true); + Mock.Get(_fileSystems.MasterPagesFileSystem) + .Setup(x => x.FileExists(It.IsAny())).Returns(false); var res = _templateRepository.DetermineTemplateRenderingEngine(templateMock.Object); Assert.AreEqual(RenderingEngine.Mvc, res); diff --git a/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs b/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs index bc29139918..7fd2f0f18b 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs @@ -1,15 +1,16 @@ -using LightInject; -using Moq; +using Moq; using NPoco; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Cache; +using Umbraco.Core.Components; using Umbraco.Core.Logging; using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.SqlSyntax; -using Umbraco.Core.Profiling; using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; using Umbraco.Core.Persistence; +using Umbraco.Tests.Components; namespace Umbraco.Tests.TestHelpers { @@ -34,22 +35,27 @@ namespace Umbraco.Tests.TestHelpers var sqlSyntax = new SqlCeSyntaxProvider(); - var container = new ServiceContainer(); - container.ConfigureUmbracoCore(); - - container.RegisterSingleton(factory => Mock.Of()); - container.RegisterSingleton(factory => Mock.Of()); + var container = RegisterFactory.Create(); var logger = new ProfilingLogger(Mock.Of(), Mock.Of()); - var pluginManager = new TypeLoader(NullCacheProvider.Instance, - SettingsForTests.GenerateMockGlobalSettings(), + var typeLoader = new TypeLoader(NullCacheProvider.Instance, + LocalTempStorage.Default, logger, false); - container.RegisterInstance(pluginManager); - container.RegisterCollectionBuilder() - .Add(() => Current.TypeLoader.GetAssignedMapperTypes()); - Mappers = container.GetInstance(); + var composition = new Composition(container, typeLoader, Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run)); + + composition.RegisterUnique(_ => Mock.Of()); + composition.RegisterUnique(_ => Mock.Of()); + + composition.RegisterUnique(typeLoader); + + composition.WithCollectionBuilder() + .Add(() => composition.TypeLoader.GetAssignedMapperTypes()); + + var factory = Current.Factory = composition.CreateFactory(); + + Mappers = factory.GetInstance(); var pocoMappers = new NPoco.MapperCollection { new PocoMapper() }; var pocoDataFactory = new FluentPocoDataFactory((type, iPocoDataFactory) => new PocoDataBuilder(type, pocoMappers).Init()); diff --git a/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs b/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs index 5eea6bcf72..f7e3744600 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs @@ -1,10 +1,9 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Threading; -using LightInject; using Moq; using NUnit.Framework; +using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; @@ -28,8 +27,8 @@ namespace Umbraco.Tests.TestHelpers { base.Compose(); - Container.RegisterSingleton(); - Container.RegisterSingleton(); + Composition.RegisterUnique(); + Composition.RegisterUnique(); } protected override void Initialize() @@ -86,19 +85,19 @@ namespace Umbraco.Tests.TestHelpers "; } - internal PublishedRouter CreatePublishedRouter(IServiceContainer container = null, ContentFinderCollection contentFinders = null) + internal PublishedRouter CreatePublishedRouter(IFactory container = null, ContentFinderCollection contentFinders = null) { return CreatePublishedRouter(TestObjects.GetUmbracoSettings().WebRouting, container, contentFinders); } - internal static PublishedRouter CreatePublishedRouter(IWebRoutingSection webRoutingSection, IServiceContainer container = null, ContentFinderCollection contentFinders = null) + internal static PublishedRouter CreatePublishedRouter(IWebRoutingSection webRoutingSection, IFactory container = null, ContentFinderCollection contentFinders = null) { return new PublishedRouter( webRoutingSection, contentFinders ?? new ContentFinderCollection(Enumerable.Empty()), new TestLastChanceFinder(), new TestVariationContextAccessor(), - container?.TryGetInstance() ?? new ServiceContext(), + container?.TryGetInstance() ?? ServiceContext.CreatePartial(), new ProfilingLogger(Mock.Of(), Mock.Of())); } } diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs index 7152622b07..9e2a2156ee 100644 --- a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs @@ -1,5 +1,4 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Linq; using System.Net.Http; @@ -9,26 +8,20 @@ using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using System.Web.Security; using Moq; -using Semver; -using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Composing; -using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Dictionary; -using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Security; using Umbraco.Core.Services; -using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Web; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; using Umbraco.Web.Security; using Umbraco.Web.WebApi; -using LightInject; -using System.Globalization; +using Umbraco.Core.Logging; using Umbraco.Tests.Testing.Objects.Accessors; namespace Umbraco.Tests.TestHelpers.ControllerTesting @@ -58,7 +51,7 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting var mockedDataTypeService = Mock.Of(); var mockedContentTypeService = Mock.Of(); - var serviceContext = new ServiceContext( + var serviceContext = ServiceContext.CreatePartial( userService: mockedUserService, contentService: mockedContentService, mediaService: mockedMediaService, @@ -141,10 +134,7 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting var publishedSnapshotService = new Mock(); publishedSnapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())).Returns(publishedSnapshot.Object); - //var umbracoContextAccessor = new TestUmbracoContextAccessor(); - //Umbraco.Web.Composing.Current.UmbracoContextAccessor = umbracoContextAccessor; var umbracoContextAccessor = Umbraco.Web.Composing.Current.UmbracoContextAccessor; - Current.Container.Register(factory => umbracoContextAccessor.UmbracoContext); // but really, should we inject this?! var umbCtx = UmbracoContext.EnsureContext( umbracoContextAccessor, @@ -161,7 +151,7 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting urlHelper.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(UrlInfo.Url("/hello/world/1234")); - var membershipHelper = new MembershipHelper(umbCtx, Mock.Of(), Mock.Of()); + var membershipHelper = new MembershipHelper(new TestUmbracoContextAccessor(umbCtx), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), null, Mock.Of(), Mock.Of()); var umbHelper = new UmbracoHelper(umbCtx, Mock.Of(), diff --git a/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs b/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs index d38cd8af6f..ea40ed9c84 100644 --- a/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs +++ b/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using System.IO; +using System.IO; using System.Configuration; using Moq; using Umbraco.Core; @@ -11,22 +10,6 @@ namespace Umbraco.Tests.TestHelpers { public class SettingsForTests { - public static void ConfigureSettings(IGlobalSettings settings) - { - UmbracoConfig.For.SetGlobalConfig(settings); - } - - // umbracoSettings - - /// - /// Sets the umbraco settings singleton to the object specified - /// - /// - public static void ConfigureSettings(IUmbracoSettingsSection settings) - { - UmbracoConfig.For.SetUmbracoSettings(settings); - } - public static IGlobalSettings GenerateMockGlobalSettings() { var config = Mock.Of( @@ -103,7 +86,7 @@ namespace Umbraco.Tests.TestHelpers // SaveSetting("umbracoConfigurationStatus"); //} - + // reset & defaults @@ -131,8 +114,6 @@ namespace Umbraco.Tests.TestHelpers private static void ResetSettings() { _defaultGlobalSettings = null; - ConfigureSettings(GetDefaultUmbracoSettings()); - ConfigureSettings(GetDefaultGlobalSettings()); } private static IUmbracoSettingsSection _defaultUmbracoSettings; diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs index 00e1a363b8..36c5961b9f 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs @@ -47,7 +47,8 @@ namespace Umbraco.Tests.TestHelpers.Stubs var allParams = ctor.GetParameters().ToArray(); foreach (var parameter in allParams) { - var found = possibleParams.SingleOrDefault(x => x.GetType() == parameter.ParameterType); + var found = possibleParams.SingleOrDefault(x => x.GetType() == parameter.ParameterType) + ?? Current.Factory.GetInstance(parameter.ParameterType); if (found != null) args.Add(found); } if (args.Count == allParams.Length) diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestExamineManager.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestExamineManager.cs index f8d48c5703..7bb3b90d81 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestExamineManager.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestExamineManager.cs @@ -9,14 +9,16 @@ namespace Umbraco.Tests.TestHelpers.Stubs private readonly ConcurrentDictionary _indexers = new ConcurrentDictionary(); private readonly ConcurrentDictionary _searchers = new ConcurrentDictionary(); - public void AddIndex(IIndex indexer) + public IIndex AddIndex(IIndex indexer) { _indexers.TryAdd(indexer.Name, indexer); + return indexer; } - public void AddSearcher(ISearcher searcher) + public ISearcher AddSearcher(ISearcher searcher) { _searchers.TryAdd(searcher.Name, searcher); + return searcher; } public void Dispose() diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs index 9c0bb61cb3..58c8e37cbf 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs @@ -15,7 +15,7 @@ namespace Umbraco.Tests.TestHelpers.Stubs } public int Id { get; } - public int TemplateId { get; set; } + public int? TemplateId { get; set; } public int SortOrder { get; set; } public string Name { get; set; } public IVariationContextAccessor VariationContextAccessor { get; set; } diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs index 167d6730ed..8c230f98d0 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs @@ -3,18 +3,18 @@ using System.Collections.Generic; using System.Data; using System.Data.Common; using System.Linq; +using System.Linq.Expressions; using System.Web; -using LightInject; using Moq; using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.Events; +using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Services; -using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web; using Umbraco.Web.PublishedCache; @@ -53,34 +53,37 @@ namespace Umbraco.Tests.TestHelpers /// Gets a mocked service context built with mocked services. /// /// A ServiceContext. - public ServiceContext GetServiceContextMock(IServiceFactory container = null) + public ServiceContext GetServiceContextMock(IFactory container = null) { - return new ServiceContext( - MockService(), - MockService(), - MockService(), - MockService(), - MockService(), - MockService(), - MockService(), - MockService(), - MockService(), - MockService(), - MockService(), - MockService(), - MockService(), - MockService(), - MockService(), - MockService(), - MockService(), - MockService(), - MockService(), - MockService(), - MockService(), - MockService()); + // fixme - else some tests break - figure it out + container = null; + + return ServiceContext.CreatePartial( + MockService(container), + MockService(container), + MockService(container), + MockService(container), + MockService(container), + MockService(container), + MockService(container), + MockService(container), + MockService(container), + MockService(container), + MockService(container), + MockService(container), + MockService(container), + MockService(container), + MockService(container), + MockService(container), + MockService(container), + MockService(container), + MockService(container), + MockService(container), + MockService(container), + MockService(container)); } - private T MockService(IServiceFactory container = null) + private T MockService(IFactory container = null) where T : class { return container?.TryGetInstance() ?? new Mock().Object; @@ -139,6 +142,26 @@ namespace Umbraco.Tests.TestHelpers return SettingsForTests.GetDefaultGlobalSettings(); } + public IFileSystems GetFileSystemsMock() + { + var fileSystems = Mock.Of(); + + MockFs(fileSystems, x => x.MasterPagesFileSystem); + MockFs(fileSystems, x => x.MacroPartialsFileSystem); + MockFs(fileSystems, x => x.MvcViewsFileSystem); + MockFs(fileSystems, x => x.PartialViewsFileSystem); + MockFs(fileSystems, x => x.ScriptsFileSystem); + MockFs(fileSystems, x => x.StylesheetsFileSystem); + + return fileSystems; + } + + private void MockFs(IFileSystems fileSystems, Expression> fileSystem) + { + var fs = Mock.Of(); + Mock.Get(fileSystems).Setup(fileSystem).Returns(fs); + } + #region Inner classes private class MockDbConnection : DbConnection diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects.cs b/src/Umbraco.Tests/TestHelpers/TestObjects.cs index 4529c4f1ef..cb36e6ca5f 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects.cs @@ -1,9 +1,7 @@ using System; -using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; -using LightInject; using Moq; using NPoco; using Umbraco.Core; @@ -32,27 +30,11 @@ namespace Umbraco.Tests.TestHelpers ///
internal partial class TestObjects { - private readonly IServiceContainer _container; + private readonly IRegister _register; - public TestObjects(IServiceContainer container) + public TestObjects(IRegister register) { - _container = container; - } - - /// - /// Gets the default ISqlSyntaxProvider objects. - /// - /// A logger. - /// A (lazy) scope provider. - /// The default ISqlSyntaxProvider objects. - public IEnumerable GetDefaultSqlSyntaxProviders(ILogger logger, Lazy lazyScopeProvider = null) - { - return new ISqlSyntaxProvider[] - { - new MySqlSyntaxProvider(logger), - new SqlCeSyntaxProvider(), - new SqlServerSyntaxProvider(lazyScopeProvider ?? new Lazy(() => null)) - }; + _register = register; } /// @@ -79,13 +61,13 @@ namespace Umbraco.Tests.TestHelpers /// that can begin a transaction. public UmbracoDatabase GetUmbracoSqlServerDatabase(ILogger logger) { - var syntax = new SqlServerSyntaxProvider(new Lazy(() => null)); // do NOT try to get the server's version! + var syntax = new SqlServerSyntaxProvider(); // do NOT try to get the server's version! var connection = GetDbConnection(); var sqlContext = new SqlContext(syntax, DatabaseType.SqlServer2008, Mock.Of()); return new UmbracoDatabase(connection, sqlContext, logger); } - public void RegisterServices(IServiceContainer container) + public void RegisterServices(IRegister register) { } /// @@ -109,9 +91,9 @@ namespace Umbraco.Tests.TestHelpers IGlobalSettings globalSettings, IUmbracoSettingsSection umbracoSettings, IEventMessagesFactory eventMessagesFactory, - IEnumerable urlSegmentProviders, + UrlSegmentProviderCollection urlSegmentProviders, TypeLoader typeLoader, - IServiceFactory container = null) + IFactory factory = null) { if (scopeProvider == null) throw new ArgumentNullException(nameof(scopeProvider)); if (scopeAccessor == null) throw new ArgumentNullException(nameof(scopeAccessor)); @@ -119,14 +101,17 @@ namespace Umbraco.Tests.TestHelpers if (logger == null) throw new ArgumentNullException(nameof(logger)); if (eventMessagesFactory == null) throw new ArgumentNullException(nameof(eventMessagesFactory)); - var mediaFileSystem = new MediaFileSystem(Mock.Of()); + var scheme = Mock.Of(); + var config = Mock.Of(); - var externalLoginService = GetLazyService(container, c => new ExternalLoginService(scopeProvider, logger, eventMessagesFactory, GetRepo(c))); - var publicAccessService = GetLazyService(container, c => new PublicAccessService(scopeProvider, logger, eventMessagesFactory, GetRepo(c))); - var domainService = GetLazyService(container, c => new DomainService(scopeProvider, logger, eventMessagesFactory, GetRepo(c))); - var auditService = GetLazyService(container, c => new AuditService(scopeProvider, logger, eventMessagesFactory, GetRepo(c), GetRepo(c))); + var mediaFileSystem = new MediaFileSystem(Mock.Of(), config, scheme, logger); - var localizedTextService = GetLazyService(container, c => new LocalizedTextService( + var externalLoginService = GetLazyService(factory, c => new ExternalLoginService(scopeProvider, logger, eventMessagesFactory, GetRepo(c))); + var publicAccessService = GetLazyService(factory, c => new PublicAccessService(scopeProvider, logger, eventMessagesFactory, GetRepo(c))); + var domainService = GetLazyService(factory, c => new DomainService(scopeProvider, logger, eventMessagesFactory, GetRepo(c))); + var auditService = GetLazyService(factory, c => new AuditService(scopeProvider, logger, eventMessagesFactory, GetRepo(c), GetRepo(c))); + + var localizedTextService = GetLazyService(factory, c => new LocalizedTextService( new Lazy(() => { var mainLangFolder = new DirectoryInfo(IOHelper.MapPath(SystemDirectories.Umbraco + "/config/lang/")); @@ -161,34 +146,34 @@ namespace Umbraco.Tests.TestHelpers var runtimeState = Mock.Of(); var idkMap = new IdkMap(scopeProvider); - var localizationService = GetLazyService(container, c => new LocalizationService(scopeProvider, logger, eventMessagesFactory, GetRepo(c), GetRepo(c), GetRepo(c))); - var userService = GetLazyService(container, c => new UserService(scopeProvider, logger, eventMessagesFactory, runtimeState, GetRepo(c), GetRepo(c),globalSettings)); - var dataTypeService = GetLazyService(container, c => new DataTypeService(scopeProvider, logger, eventMessagesFactory, GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c))); - var contentService = GetLazyService(container, c => new ContentService(scopeProvider, logger, eventMessagesFactory, mediaFileSystem, GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c))); - var notificationService = GetLazyService(container, c => new NotificationService(scopeProvider, userService.Value, contentService.Value, localizationService.Value, logger, GetRepo(c), globalSettings, umbracoSettings.Content)); - var serverRegistrationService = GetLazyService(container, c => new ServerRegistrationService(scopeProvider, logger, eventMessagesFactory, GetRepo(c))); - var memberGroupService = GetLazyService(container, c => new MemberGroupService(scopeProvider, logger, eventMessagesFactory, GetRepo(c))); - var memberService = GetLazyService(container, c => new MemberService(scopeProvider, logger, eventMessagesFactory, memberGroupService.Value, mediaFileSystem, GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c))); - var mediaService = GetLazyService(container, c => new MediaService(scopeProvider, mediaFileSystem, logger, eventMessagesFactory, GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c))); - var contentTypeService = GetLazyService(container, c => new ContentTypeService(scopeProvider, logger, eventMessagesFactory, contentService.Value, GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c))); - var mediaTypeService = GetLazyService(container, c => new MediaTypeService(scopeProvider, logger, eventMessagesFactory, mediaService.Value, GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c))); - var fileService = GetLazyService(container, c => new FileService(scopeProvider, logger, eventMessagesFactory, GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c))); + var localizationService = GetLazyService(factory, c => new LocalizationService(scopeProvider, logger, eventMessagesFactory, GetRepo(c), GetRepo(c), GetRepo(c))); + var userService = GetLazyService(factory, c => new UserService(scopeProvider, logger, eventMessagesFactory, runtimeState, GetRepo(c), GetRepo(c),globalSettings)); + var dataTypeService = GetLazyService(factory, c => new DataTypeService(scopeProvider, logger, eventMessagesFactory, GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c))); + var contentService = GetLazyService(factory, c => new ContentService(scopeProvider, logger, eventMessagesFactory, mediaFileSystem, GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c))); + var notificationService = GetLazyService(factory, c => new NotificationService(scopeProvider, userService.Value, contentService.Value, localizationService.Value, logger, GetRepo(c), globalSettings, umbracoSettings.Content)); + var serverRegistrationService = GetLazyService(factory, c => new ServerRegistrationService(scopeProvider, logger, eventMessagesFactory, GetRepo(c))); + var memberGroupService = GetLazyService(factory, c => new MemberGroupService(scopeProvider, logger, eventMessagesFactory, GetRepo(c))); + var memberService = GetLazyService(factory, c => new MemberService(scopeProvider, logger, eventMessagesFactory, memberGroupService.Value, mediaFileSystem, GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c))); + var mediaService = GetLazyService(factory, c => new MediaService(scopeProvider, mediaFileSystem, logger, eventMessagesFactory, GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c))); + var contentTypeService = GetLazyService(factory, c => new ContentTypeService(scopeProvider, logger, eventMessagesFactory, contentService.Value, GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c))); + var mediaTypeService = GetLazyService(factory, c => new MediaTypeService(scopeProvider, logger, eventMessagesFactory, mediaService.Value, GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c))); + var fileService = GetLazyService(factory, c => new FileService(scopeProvider, logger, eventMessagesFactory, GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c))); - var memberTypeService = GetLazyService(container, c => new MemberTypeService(scopeProvider, logger, eventMessagesFactory, memberService.Value, GetRepo(c), GetRepo(c), GetRepo(c))); - var entityService = GetLazyService(container, c => new EntityService( + var memberTypeService = GetLazyService(factory, c => new MemberTypeService(scopeProvider, logger, eventMessagesFactory, memberService.Value, GetRepo(c), GetRepo(c), GetRepo(c))); + var entityService = GetLazyService(factory, c => new EntityService( scopeProvider, logger, eventMessagesFactory, contentService.Value, contentTypeService.Value, mediaService.Value, mediaTypeService.Value, dataTypeService.Value, memberService.Value, memberTypeService.Value, idkMap, GetRepo(c))); - var macroService = GetLazyService(container, c => new MacroService(scopeProvider, logger, eventMessagesFactory, GetRepo(c), GetRepo(c))); - var packagingService = GetLazyService(container, c => new PackagingService(logger, contentService.Value, contentTypeService.Value, mediaService.Value, macroService.Value, dataTypeService.Value, fileService.Value, localizationService.Value, entityService.Value, userService.Value, scopeProvider, urlSegmentProviders, GetRepo(c), GetRepo(c), new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())))); - var relationService = GetLazyService(container, c => new RelationService(scopeProvider, logger, eventMessagesFactory, entityService.Value, GetRepo(c), GetRepo(c))); - var treeService = GetLazyService(container, c => new ApplicationTreeService(logger, cache, typeLoader)); - var tagService = GetLazyService(container, c => new TagService(scopeProvider, logger, eventMessagesFactory, GetRepo(c))); - var sectionService = GetLazyService(container, c => new SectionService(userService.Value, treeService.Value, scopeProvider, cache)); - var redirectUrlService = GetLazyService(container, c => new RedirectUrlService(scopeProvider, logger, eventMessagesFactory, GetRepo(c))); - var consentService = GetLazyService(container, c => new ConsentService(scopeProvider, logger, eventMessagesFactory, GetRepo(c))); + var macroService = GetLazyService(factory, c => new MacroService(scopeProvider, logger, eventMessagesFactory, GetRepo(c), GetRepo(c))); + var packagingService = GetLazyService(factory, c => new PackagingService(logger, contentService.Value, contentTypeService.Value, mediaService.Value, macroService.Value, dataTypeService.Value, fileService.Value, localizationService.Value, entityService.Value, userService.Value, scopeProvider, urlSegmentProviders, GetRepo(c), GetRepo(c), new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())))); + var relationService = GetLazyService(factory, c => new RelationService(scopeProvider, logger, eventMessagesFactory, entityService.Value, GetRepo(c), GetRepo(c))); + var treeService = GetLazyService(factory, c => new ApplicationTreeService(logger, cache, typeLoader)); + var tagService = GetLazyService(factory, c => new TagService(scopeProvider, logger, eventMessagesFactory, GetRepo(c))); + var sectionService = GetLazyService(factory, c => new SectionService(userService.Value, treeService.Value, scopeProvider, cache)); + var redirectUrlService = GetLazyService(factory, c => new RedirectUrlService(scopeProvider, logger, eventMessagesFactory, GetRepo(c))); + var consentService = GetLazyService(factory, c => new ConsentService(scopeProvider, logger, eventMessagesFactory, GetRepo(c))); return new ServiceContext( publicAccessService, @@ -220,13 +205,13 @@ namespace Umbraco.Tests.TestHelpers consentService); } - private Lazy GetLazyService(IServiceFactory container, Func ctor) + private Lazy GetLazyService(IFactory container, Func ctor) where T : class { return new Lazy(() => container?.TryGetInstance() ?? ctor(container)); } - private T GetRepo(IServiceFactory container) + private T GetRepo(IFactory container) where T : class, IRepository { return container?.TryGetInstance() ?? Mock.Of(); @@ -239,11 +224,11 @@ namespace Umbraco.Tests.TestHelpers //var mappersBuilder = new MapperCollectionBuilder(Current.Container); // fixme //mappersBuilder.AddCore(); //var mappers = mappersBuilder.CreateCollection(); - var mappers = Current.Container.GetInstance(); - databaseFactory = new UmbracoDatabaseFactory(Constants.System.UmbracoConnectionName, GetDefaultSqlSyntaxProviders(logger), logger, mappers); + var mappers = Current.Factory.GetInstance(); + databaseFactory = new UmbracoDatabaseFactory(Constants.System.UmbracoConnectionName, logger, new Lazy(() => mappers)); } - fileSystems = fileSystems ?? new FileSystems(logger); + fileSystems = fileSystems ?? new FileSystems(Current.Factory, logger); var scopeProvider = new ScopeProvider(databaseFactory, fileSystems, logger); return scopeProvider; } diff --git a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs index 6b52137542..a2a0c35c56 100644 --- a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs +++ b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs @@ -26,9 +26,7 @@ using File = System.IO.File; using Umbraco.Core.Composing; using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Scoping; -using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Tests.Testing; -using LightInject; using Umbraco.Core.Migrations.Install; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Persistence.Repositories; @@ -49,13 +47,9 @@ namespace Umbraco.Tests.TestHelpers [UmbracoTest(WithApplication = true)] public abstract class TestWithDatabaseBase : UmbracoTestBase { - private CacheHelper _disabledCacheHelper; - private string _databasePath; private static byte[] _databaseBytes; - protected CacheHelper DisabledCache => _disabledCacheHelper ?? (_disabledCacheHelper = CacheHelper.CreateDisabledCacheHelper()); - protected PublishedContentTypeCache ContentTypesCache { get; private set; } protected override ISqlSyntaxProvider SqlSyntax => GetSyntaxProvider(); @@ -64,7 +58,7 @@ namespace Umbraco.Tests.TestHelpers internal ScopeProvider ScopeProvider => Current.ScopeProvider as ScopeProvider; - protected ISqlContext SqlContext => Container.GetInstance(); + protected ISqlContext SqlContext => Factory.GetInstance(); public override void SetUp() { @@ -78,23 +72,22 @@ namespace Umbraco.Tests.TestHelpers { base.Compose(); - Container.Register(); - Container.Register(factory => PublishedSnapshotService); - Container.Register(factory => DefaultCultureAccessor); + Composition.Register(); + Composition.Register(factory => PublishedSnapshotService); + Composition.Register(factory => DefaultCultureAccessor); - Container.GetInstance() + Composition.WithCollectionBuilder() .Clear() - .Add(f => f.GetInstance().GetDataEditors()); + .Add(() => Composition.TypeLoader.GetDataEditors()); - Container.RegisterSingleton(f => + Composition.RegisterUnique(f => { if (Options.Database == UmbracoTestOptions.Database.None) return TestObjects.GetDatabaseFactoryMock(); - var sqlSyntaxProviders = new[] { new SqlCeSyntaxProvider() }; var logger = f.GetInstance(); var mappers = f.GetInstance(); - var factory = new UmbracoDatabaseFactory(GetDbConnectionString(), GetDbProviderName(), sqlSyntaxProviders, logger, mappers); + var factory = new UmbracoDatabaseFactory(GetDbConnectionString(), GetDbProviderName(), logger, new Lazy(() => mappers)); factory.ResetForTests(); return factory; }); @@ -108,7 +101,7 @@ namespace Umbraco.Tests.TestHelpers public override void TearDown() { - var profilingLogger = Container.TryGetInstance(); + var profilingLogger = Factory.TryGetInstance(); var timer = profilingLogger?.TraceDuration("teardown"); // fixme move that one up try { @@ -138,9 +131,8 @@ namespace Umbraco.Tests.TestHelpers } // ensure the configuration matches the current version for tests - var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + var globalSettingsMock = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettingsMock.Setup(x => x.ConfigurationStatus).Returns(UmbracoVersion.Current.ToString(3)); - SettingsForTests.ConfigureSettings(globalSettingsMock.Object); using (ProfilingLogger.TraceDuration("Initialize database.")) { @@ -257,10 +249,10 @@ namespace Umbraco.Tests.TestHelpers var cache = NullCacheProvider.Instance; ContentTypesCache = new PublishedContentTypeCache( - Container.GetInstance(), - Container.GetInstance(), - Container.GetInstance(), - Container.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), Logger); // testing=true so XmlStore will not use the file nor the database @@ -269,13 +261,13 @@ namespace Umbraco.Tests.TestHelpers var variationContextAccessor = new TestVariationContextAccessor(); var service = new PublishedSnapshotService( ServiceContext, - Container.GetInstance(), + Factory.GetInstance(), ScopeProvider, cache, publishedSnapshotAccessor, variationContextAccessor, - Container.GetInstance(), Container.GetInstance(), Container.GetInstance(), + Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), DefaultCultureAccessor, Logger, - Container.GetInstance(), new SiteDomainHelper(), + Factory.GetInstance(), new SiteDomainHelper(), ContentTypesCache, null, true, Options.PublishedRepositoryEvents); @@ -376,11 +368,11 @@ namespace Umbraco.Tests.TestHelpers var umbracoContext = new UmbracoContext( httpContext, service, - new WebSecurity(httpContext, Container.GetInstance(), - Container.GetInstance()), - umbracoSettings ?? Container.GetInstance(), + new WebSecurity(httpContext, Factory.GetInstance(), + Factory.GetInstance()), + umbracoSettings ?? Factory.GetInstance(), urlProviders ?? Enumerable.Empty(), - globalSettings ?? Container.GetInstance(), + globalSettings ?? Factory.GetInstance(), new TestVariationContextAccessor()); if (setSingleton) diff --git a/src/Umbraco.Tests/Testing/Objects/Accessors/TestUmbracoContextAccessor.cs b/src/Umbraco.Tests/Testing/Objects/Accessors/TestUmbracoContextAccessor.cs index da93218907..4f3b801af9 100644 --- a/src/Umbraco.Tests/Testing/Objects/Accessors/TestUmbracoContextAccessor.cs +++ b/src/Umbraco.Tests/Testing/Objects/Accessors/TestUmbracoContextAccessor.cs @@ -5,5 +5,14 @@ namespace Umbraco.Tests.Testing.Objects.Accessors public class TestUmbracoContextAccessor : IUmbracoContextAccessor { public UmbracoContext UmbracoContext { get; set; } + + public TestUmbracoContextAccessor() + { + } + + public TestUmbracoContextAccessor(UmbracoContext umbracoContext) + { + UmbracoContext = umbracoContext; + } } } diff --git a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs index 5b93de0c09..c413cb0e94 100644 --- a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs +++ b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs @@ -6,7 +6,9 @@ using Moq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Cache; +using Umbraco.Core.Composing; using Umbraco.Core.Dictionary; +using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Services; @@ -51,11 +53,11 @@ namespace Umbraco.Tests.Testing.TestingTests var umbracoContext = TestObjects.GetUmbracoContextMock(); // unless we can inject them in MembershipHelper, we need need this - Container.Register(_ => Mock.Of()); - Container.Register(_ => Mock.Of()); - Container.Register(_ => Mock.Of()); - Container.Register(_ => CacheHelper.CreateDisabledCacheHelper()); - Container.Register(); + Composition.Register(_ => Mock.Of()); + Composition.Register(_ => Mock.Of()); + Composition.Register(_ => Mock.Of()); + Composition.Register(_ => CacheHelper.Disabled); + Composition.Register(); // ReSharper disable once UnusedVariable var helper = new UmbracoHelper(umbracoContext, @@ -63,8 +65,8 @@ namespace Umbraco.Tests.Testing.TestingTests Mock.Of(), Mock.Of(), Mock.Of(), - new MembershipHelper(umbracoContext, Mock.Of(), Mock.Of()), - new ServiceContext()); + new MembershipHelper(new TestUmbracoContextAccessor(umbracoContext), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), null, Mock.Of(), Mock.Of()), + ServiceContext.CreatePartial()); Assert.Pass(); } diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index c7c493c64d..a79531af74 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -4,15 +4,15 @@ using System.Linq; using System.Reflection; using AutoMapper; using Examine; -using LightInject; using Moq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Components; using Umbraco.Core.Composing; -using Umbraco.Core.Composing.CompositionRoots; +using Umbraco.Core.Composing.Composers; using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Events; using Umbraco.Core.IO; using Umbraco.Core.IO.MediaPathSchemes; @@ -22,7 +22,6 @@ using Umbraco.Core.Manifest; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Mappers; -using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.Repositories.Implement; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.PropertyEditors; @@ -30,16 +29,15 @@ using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Services.Implement; using Umbraco.Core.Strings; +using Umbraco.Tests.Components; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Web; using Umbraco.Web.Services; -using Umbraco.Examine; using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web.Actions; -using Umbraco.Web.Composing.CompositionRoots; +using Umbraco.Web.Composing.Composers; using Umbraco.Web.ContentApps; - using Current = Umbraco.Core.Composing.Current; using Umbraco.Web.Routing; @@ -79,7 +77,9 @@ namespace Umbraco.Tests.Testing // test feature, and no test "base" class should be. only actual test feature classes // should be marked with that attribute. - protected ServiceContainer Container { get; private set; } + protected Composition Composition { get; private set; } + + protected IFactory Factory { get; private set; } protected UmbracoTestAttribute Options { get; private set; } @@ -95,17 +95,17 @@ namespace Umbraco.Tests.Testing #region Accessors - protected ILogger Logger => Container.GetInstance(); + protected ILogger Logger => Factory.GetInstance(); - protected IProfiler Profiler => Container.GetInstance(); + protected IProfiler Profiler => Factory.GetInstance(); - protected virtual ProfilingLogger ProfilingLogger => Container.GetInstance(); + protected virtual IProfilingLogger ProfilingLogger => Factory.GetInstance(); - protected CacheHelper CacheHelper => Container.GetInstance(); + protected CacheHelper CacheHelper => Factory.GetInstance(); - protected virtual ISqlSyntaxProvider SqlSyntax => Container.GetInstance(); + protected virtual ISqlSyntaxProvider SqlSyntax => Factory.GetInstance(); - protected IMapperCollection Mappers => Container.GetInstance(); + protected IMapperCollection Mappers => Factory.GetInstance(); #endregion @@ -118,24 +118,37 @@ namespace Umbraco.Tests.Testing // but hey, never know, better avoid garbage-in Reset(); - Container = new ServiceContainer(); - Container.ConfigureUmbracoCore(); - - TestObjects = new TestObjects(Container); - // get/merge the attributes marking the method and/or the classes Options = TestOptionAttributeBase.GetTestOptions(); + // fixme - align to runtimes & components - don't redo everything here + + var (logger, profiler) = GetLoggers(Options.Logger); + var proflogger = new ProfilingLogger(logger, profiler); + var cacheHelper = GetCacheHelper(); + var globalSettings = SettingsForTests.GetDefaultGlobalSettings(); + var typeLoader = GetTypeLoader(cacheHelper.RuntimeCache, globalSettings, proflogger, Options.TypeLoader); + + var register = RegisterFactory.Create(); + + Composition = new Composition(register, typeLoader, proflogger, ComponentTests.MockRuntimeState(RuntimeLevel.Run)); + + Composition.RegisterUnique(typeLoader); + Composition.RegisterUnique(logger); + Composition.RegisterUnique(profiler); + Composition.RegisterUnique(proflogger); + Composition.RegisterUnique(cacheHelper); + Composition.RegisterUnique(cacheHelper.RuntimeCache); + + TestObjects = new TestObjects(register); Compose(); + Current.Factory = Factory = Composition.CreateFactory(); Initialize(); } protected virtual void Compose() { - ComposeLogging(Options.Logger); - ComposeCacheHelper(); ComposeAutoMapper(Options.AutoMapper); - ComposeTypeLoader(Options.TypeLoader); ComposeDatabase(Options.Database); ComposeApplication(Options.WithApplication); @@ -144,8 +157,7 @@ namespace Umbraco.Tests.Testing ComposeWtf(); // not sure really - var composition = new Composition(Container, RuntimeLevel.Run); - Compose(composition); + Compose(Composition); } protected virtual void Compose(Composition composition) @@ -161,41 +173,49 @@ namespace Umbraco.Tests.Testing #region Compose - protected virtual void ComposeLogging(UmbracoTestOptions.Logger option) + protected virtual (ILogger, IProfiler) GetLoggers(UmbracoTestOptions.Logger option) { - if (option == UmbracoTestOptions.Logger.Mock) + ILogger logger; + IProfiler profiler; + + switch (option) { - Container.RegisterSingleton(f => Mock.Of()); - Container.RegisterSingleton(f => Mock.Of()); - } - else if (option == UmbracoTestOptions.Logger.Serilog) - { - Container.RegisterSingleton(f => new SerilogLogger(new FileInfo(TestHelper.MapPathForTest("~/unit-test.config")))); - Container.RegisterSingleton(f => new LogProfiler(f.GetInstance())); - } - else if (option == UmbracoTestOptions.Logger.Console) - { - Container.RegisterSingleton(f => new ConsoleLogger()); - Container.RegisterSingleton(f => new LogProfiler(f.GetInstance())); + case UmbracoTestOptions.Logger.Mock: + logger = Mock.Of(); + profiler = Mock.Of(); + break; + case UmbracoTestOptions.Logger.Serilog: + logger = new SerilogLogger(new FileInfo(TestHelper.MapPathForTest("~/unit-test.config"))); + profiler = new LogProfiler(logger); + break; + case UmbracoTestOptions.Logger.Console: + logger = new ConsoleLogger(); + profiler = new LogProfiler(logger); + break; + default: + throw new NotSupportedException($"Logger option {option} is not supported."); } - Container.RegisterSingleton(f => new ProfilingLogger(f.GetInstance(), f.GetInstance())); + return (logger, profiler); + } + + protected virtual CacheHelper GetCacheHelper() + { + return CacheHelper.Disabled; } protected virtual void ComposeWeb() { - //TODO: Should we 'just' register the WebRuntimeComponent? - // imported from TestWithSettingsBase // which was inherited by TestWithApplicationBase so pretty much used everywhere Umbraco.Web.Composing.Current.UmbracoContextAccessor = new TestUmbracoContextAccessor(); // web - Container.Register(_ => Umbraco.Web.Composing.Current.UmbracoContextAccessor); - Container.RegisterSingleton(); - Container.RegisterCollectionBuilder(); - Container.Register(); - Container.Register(); + Composition.RegisterUnique(_ => Umbraco.Web.Composing.Current.UmbracoContextAccessor); + Composition.RegisterUnique(); + Composition.WithCollectionBuilder(); + Composition.RegisterUnique(); + Composition.RegisterUnique(); } protected virtual void ComposeWtf() @@ -203,59 +223,52 @@ namespace Umbraco.Tests.Testing // what else? var runtimeStateMock = new Mock(); runtimeStateMock.Setup(x => x.Level).Returns(RuntimeLevel.Run); - Container.RegisterSingleton(f => runtimeStateMock.Object); + Composition.RegisterUnique(f => runtimeStateMock.Object); // ah... - Container.RegisterCollectionBuilder(); - Container.RegisterCollectionBuilder(); - Container.RegisterSingleton(); + Composition.WithCollectionBuilder(); + Composition.WithCollectionBuilder(); + Composition.RegisterUnique(); - Container.RegisterSingleton(); + Composition.RegisterUnique(); // register empty content apps collection - Container.RegisterCollectionBuilder(); - } - - protected virtual void ComposeCacheHelper() - { - Container.RegisterSingleton(f => CacheHelper.CreateDisabledCacheHelper()); - Container.RegisterSingleton(f => f.GetInstance().RuntimeCache); + Composition.WithCollectionBuilder(); } protected virtual void ComposeAutoMapper(bool configure) { if (configure == false) return; - Container.RegisterFrom(); - Container.RegisterFrom(); + Composition + .ComposeCoreMappingProfiles() + .ComposeWebMappingProfiles(); } - protected virtual void ComposeTypeLoader(UmbracoTestOptions.TypeLoader typeLoader) + protected virtual TypeLoader GetTypeLoader(IRuntimeCacheProvider runtimeCache, IGlobalSettings globalSettings, IProfilingLogger logger, UmbracoTestOptions.TypeLoader option) { - Container.RegisterSingleton(f => + switch (option) { - switch (typeLoader) - { - case UmbracoTestOptions.TypeLoader.Default: - return _commonTypeLoader ?? (_commonTypeLoader = CreateCommonTypeLoader(f)); - case UmbracoTestOptions.TypeLoader.PerFixture: - return _featureTypeLoader ?? (_featureTypeLoader = CreateTypeLoader(f)); - case UmbracoTestOptions.TypeLoader.PerTest: - return CreateTypeLoader(f); - default: - throw new ArgumentOutOfRangeException(nameof(typeLoader)); - } - }); + case UmbracoTestOptions.TypeLoader.Default: + return _commonTypeLoader ?? (_commonTypeLoader = CreateCommonTypeLoader(runtimeCache, globalSettings, logger)); + case UmbracoTestOptions.TypeLoader.PerFixture: + return _featureTypeLoader ?? (_featureTypeLoader = CreateTypeLoader(runtimeCache, globalSettings, logger)); + case UmbracoTestOptions.TypeLoader.PerTest: + return CreateTypeLoader(runtimeCache, globalSettings, logger); + default: + throw new ArgumentOutOfRangeException(nameof(option)); + } } - protected virtual TypeLoader CreateTypeLoader(IServiceFactory f) + protected virtual TypeLoader CreateTypeLoader(IRuntimeCacheProvider runtimeCache, IGlobalSettings globalSettings, IProfilingLogger logger) { - return CreateCommonTypeLoader(f); + return CreateCommonTypeLoader(runtimeCache, globalSettings, logger); } - private static TypeLoader CreateCommonTypeLoader(IServiceFactory f) + // common to all tests = cannot be overriden + private static TypeLoader CreateCommonTypeLoader(IRuntimeCacheProvider runtimeCache, IGlobalSettings globalSettings, IProfilingLogger logger) { - return new TypeLoader(f.GetInstance().RuntimeCache, f.GetInstance(), f.GetInstance(), false) + return new TypeLoader(runtimeCache, globalSettings.LocalTempStorageLocation, logger, false) { AssembliesToScan = new[] { @@ -272,80 +285,74 @@ namespace Umbraco.Tests.Testing // create the file // create the schema + } + protected virtual void ComposeSettings() + { + Composition.Configs.Add(SettingsForTests.GetDefaultUmbracoSettings); + Composition.Configs.Add(SettingsForTests.GetDefaultGlobalSettings); } protected virtual void ComposeApplication(bool withApplication) { + ComposeSettings(); + if (withApplication == false) return; - var umbracoSettings = SettingsForTests.GetDefaultUmbracoSettings(); - var globalSettings = SettingsForTests.GetDefaultGlobalSettings(); - //apply these globally - SettingsForTests.ConfigureSettings(umbracoSettings); - SettingsForTests.ConfigureSettings(globalSettings); - // default Datalayer/Repositories/SQL/Database/etc... - Container.RegisterFrom(); + Composition.ComposeRepositories(); // register basic stuff that might need to be there for some container resolvers to work - Container.RegisterSingleton(factory => umbracoSettings); - Container.RegisterSingleton(factory => globalSettings); - Container.RegisterSingleton(factory => umbracoSettings.Content); - Container.RegisterSingleton(factory => umbracoSettings.Templates); - Container.RegisterSingleton(factory => umbracoSettings.WebRouting); - Container.Register(factory => new MediaFileSystem(Mock.Of())); - Container.RegisterSingleton(factory => ExamineManager.Instance); + Composition.RegisterUnique(factory => factory.GetInstance().Content); + Composition.RegisterUnique(factory => factory.GetInstance().Templates); + Composition.RegisterUnique(factory => factory.GetInstance().WebRouting); - // replace some stuff - Container.RegisterSingleton(factory => Mock.Of(), "ScriptFileSystem"); - Container.RegisterSingleton(factory => Mock.Of(), "PartialViewFileSystem"); - Container.RegisterSingleton(factory => Mock.Of(), "PartialViewMacroFileSystem"); - Container.RegisterSingleton(factory => Mock.Of(), "StylesheetFileSystem"); + Composition.RegisterUnique(factory => ExamineManager.Instance); - // need real file systems here as templates content is on-disk only - //Container.RegisterSingleton(factory => Mock.Of(), "MasterpageFileSystem"); - //Container.RegisterSingleton(factory => Mock.Of(), "ViewFileSystem"); - Container.RegisterSingleton(factory => new PhysicalFileSystem("Views", "/views"), "ViewFileSystem"); - Container.RegisterSingleton(factory => new PhysicalFileSystem("MasterPages", "/masterpages"), "MasterpageFileSystem"); - Container.RegisterSingleton(factory => new PhysicalFileSystem("Xslt", "/xslt"), "XsltFileSystem"); + // register filesystems + Composition.RegisterUnique(factory => TestObjects.GetFileSystemsMock()); + + var logger = Mock.Of(); + var scheme = Mock.Of(); + var config = Mock.Of(); + + var mediaFileSystem = new MediaFileSystem(Mock.Of(), config, scheme, logger); + Composition.RegisterUnique(factory => mediaFileSystem); // no factory (noop) - Container.RegisterSingleton(); + Composition.RegisterUnique(); // register application stuff (database factory & context, services...) - Container.RegisterCollectionBuilder() - .AddCore(); + Composition.WithCollectionBuilder() + .AddCoreMappers(); - Container.RegisterSingleton(_ => new TransientEventMessagesFactory()); - var sqlSyntaxProviders = TestObjects.GetDefaultSqlSyntaxProviders(Logger); - Container.RegisterSingleton(_ => sqlSyntaxProviders.OfType().First()); - Container.RegisterSingleton(f => new UmbracoDatabaseFactory( + Composition.RegisterUnique(_ => new TransientEventMessagesFactory()); + Composition.RegisterUnique(f => new UmbracoDatabaseFactory( Constants.System.UmbracoConnectionName, - sqlSyntaxProviders, Logger, - Mock.Of())); - Container.RegisterSingleton(f => f.TryGetInstance().SqlContext); + new Lazy(Mock.Of))); + Composition.RegisterUnique(f => f.TryGetInstance().SqlContext); - Container.RegisterCollectionBuilder(); // empty - Container.RegisterSingleton(factory => new FileSystems(factory.TryGetInstance())); - Container.RegisterSingleton(factory + Composition.WithCollectionBuilder(); // empty + + Composition.RegisterUnique(factory => TestObjects.GetScopeProvider(factory.TryGetInstance(), factory.TryGetInstance(), factory.TryGetInstance())); - Container.RegisterSingleton(factory => (IScopeAccessor) factory.GetInstance()); + Composition.RegisterUnique(factory => (IScopeAccessor) factory.GetInstance()); + + Composition.ComposeServices(); - Container.RegisterFrom(); // composition root is doing weird things, fix - Container.RegisterSingleton(); - Container.RegisterSingleton(); + Composition.RegisterUnique(); + Composition.RegisterUnique(); // somehow property editor ends up wanting this - Container.RegisterCollectionBuilder(); - Container.RegisterSingleton(); + Composition.WithCollectionBuilder(); + Composition.RegisterUnique(); // note - don't register collections, use builders - Container.RegisterCollectionBuilder(); - Container.RegisterSingleton(); - Container.RegisterSingleton(); + Composition.WithCollectionBuilder(); + Composition.RegisterUnique(); + Composition.RegisterUnique(); } #endregion @@ -358,7 +365,7 @@ namespace Umbraco.Tests.Testing Mapper.Initialize(configuration => { - var profiles = Container.GetAllInstances(); + var profiles = Factory.GetAllInstances(); foreach (var profile in profiles) configuration.AddProfile(profile); }); @@ -395,9 +402,9 @@ namespace Umbraco.Tests.Testing // reset and dispose scopes // ensures we don't leak an opened database connection // which would lock eg SqlCe .sdf files - if (Container?.TryGetInstance() is ScopeProvider scopeProvider) + if (Factory?.TryGetInstance() is ScopeProvider scopeProvider) { - Core.Scoping.Scope scope; + Scope scope; while ((scope = scopeProvider.AmbientScope) != null) { scope.Reset(); @@ -405,10 +412,7 @@ namespace Umbraco.Tests.Testing } } - Current.Reset(); - - Container?.Dispose(); - Container = null; + Current.Reset(); // disposes the factory // reset all other static things that should not be static ;( UriUtility.ResetAppDomainAppVirtualPath(); diff --git a/src/Umbraco.Tests/Testing/UmbracoTestOptions.cs b/src/Umbraco.Tests/Testing/UmbracoTestOptions.cs index 2d62f104d1..da3ffccc55 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestOptions.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestOptions.cs @@ -28,11 +28,11 @@ public enum TypeLoader { - // the default, global plugin manager for tests + // the default, global type loader for tests Default, - // create one plugin manager for the feature + // create one type loader for the feature PerFixture, - // create one plugin manager for each test + // create one type loader for each test PerTest } } diff --git a/src/Umbraco.Tests/TreesAndSections/SectionTests.cs b/src/Umbraco.Tests/TreesAndSections/SectionTests.cs index e35bd5fd45..1f4a01fd3d 100644 --- a/src/Umbraco.Tests/TreesAndSections/SectionTests.cs +++ b/src/Umbraco.Tests/TreesAndSections/SectionTests.cs @@ -3,8 +3,7 @@ using NUnit.Framework; using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; using System; -using System.Linq; -using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Tests.Testing; using Umbraco.Web.Services; @@ -21,7 +20,7 @@ namespace Umbraco.Tests.TreesAndSections protected override void Compose() { base.Compose(); - Container.Register(); + Composition.RegisterUnique(); } public override void SetUp() diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index d58cf2a5b0..bed1281bf8 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -77,7 +77,7 @@ - + 1.8.9 @@ -116,6 +116,8 @@ + + @@ -132,6 +134,7 @@ + @@ -278,7 +281,7 @@ - + diff --git a/src/Umbraco.Tests/UmbracoExamine/ExamineBaseTest.cs b/src/Umbraco.Tests/UmbracoExamine/ExamineBaseTest.cs index 5ca195849b..5be3fe2e7e 100644 --- a/src/Umbraco.Tests/UmbracoExamine/ExamineBaseTest.cs +++ b/src/Umbraco.Tests/UmbracoExamine/ExamineBaseTest.cs @@ -18,8 +18,8 @@ namespace Umbraco.Tests.UmbracoExamine _profilingLogger = new ProfilingLogger(logger, new LogProfiler(logger)); } - private ProfilingLogger _profilingLogger; - protected override ProfilingLogger ProfilingLogger => _profilingLogger; + private IProfilingLogger _profilingLogger; + protected override IProfilingLogger ProfilingLogger => _profilingLogger; /// /// sets up resolvers before resolution is frozen @@ -28,7 +28,7 @@ namespace Umbraco.Tests.UmbracoExamine { base.Compose(); - Container.RegisterSingleton(_ => new DefaultShortStringHelper(SettingsForTests.GetDefaultUmbracoSettings())); + Composition.RegisterUnique(_ => new DefaultShortStringHelper(SettingsForTests.GetDefaultUmbracoSettings())); } } } diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index 8565321bae..41c705095f 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -44,7 +44,7 @@ namespace Umbraco.Tests.UmbracoExamine public static MediaIndexPopulator GetMediaIndexRebuilder(PropertyEditorCollection propertyEditors, IMediaService mediaService) { - var mediaValueSetBuilder = new MediaValueSetBuilder(propertyEditors, new[] { new DefaultUrlSegmentProvider() }, GetMockUserService()); + var mediaValueSetBuilder = new MediaValueSetBuilder(propertyEditors, new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }), GetMockUserService()); var mediaIndexDataSource = new MediaIndexPopulator(null, mediaService, mediaValueSetBuilder); return mediaIndexDataSource; } @@ -147,7 +147,7 @@ namespace Umbraco.Tests.UmbracoExamine } public static UmbracoContentIndex GetUmbracoIndexer( - ProfilingLogger profilingLogger, + IProfilingLogger profilingLogger, Directory luceneDir, Analyzer analyzer = null, ILocalizationService languageService = null, @@ -164,8 +164,8 @@ namespace Umbraco.Tests.UmbracoExamine var i = new UmbracoContentIndex( "testIndexer", - new UmbracoFieldDefinitionCollection(), luceneDir, + new UmbracoFieldDefinitionCollection(), analyzer, profilingLogger, languageService, diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs index 1bc51b6173..ba6a83adff 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs @@ -4,12 +4,11 @@ using Examine; using Examine.LuceneEngine.Providers; using Lucene.Net.Index; using Lucene.Net.Search; -using Lucene.Net.Store; using NUnit.Framework; using Umbraco.Tests.Testing; using Umbraco.Examine; +using Umbraco.Core.Composing; using Umbraco.Core.PropertyEditors; -using LightInject; using Umbraco.Tests.TestHelpers.Entities; using Umbraco.Core.Models; using Newtonsoft.Json; @@ -26,11 +25,10 @@ namespace Umbraco.Tests.UmbracoExamine [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] public class IndexTest : ExamineBaseTest { - [Test] public void Index_Property_Data_With_Value_Indexer() { - var contentValueSetBuilder = IndexInitializer.GetContentValueSetBuilder(Container.GetInstance(), false); + var contentValueSetBuilder = IndexInitializer.GetContentValueSetBuilder(Factory.GetInstance(), false); using (var luceneDir = new RandomIdRamDirectory()) using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, @@ -122,8 +120,8 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Rebuild_Index() { - var contentRebuilder = IndexInitializer.GetContentIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockContentService(), ScopeProvider.SqlContext, false); - var mediaRebuilder = IndexInitializer.GetMediaIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockMediaService()); + var contentRebuilder = IndexInitializer.GetContentIndexRebuilder(Factory.GetInstance(), IndexInitializer.GetMockContentService(), ScopeProvider.SqlContext, false); + var mediaRebuilder = IndexInitializer.GetMediaIndexRebuilder(Factory.GetInstance(), IndexInitializer.GetMockMediaService()); using (var luceneDir = new RandomIdRamDirectory()) using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, @@ -150,7 +148,7 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Index_Protected_Content_Not_Indexed() { - var rebuilder = IndexInitializer.GetContentIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockContentService(), ScopeProvider.SqlContext, false); + var rebuilder = IndexInitializer.GetContentIndexRebuilder(Factory.GetInstance(), IndexInitializer.GetMockContentService(), ScopeProvider.SqlContext, false); using (var luceneDir = new RandomIdRamDirectory()) @@ -275,7 +273,7 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Index_Reindex_Content() { - var rebuilder = IndexInitializer.GetContentIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockContentService(), ScopeProvider.SqlContext, false); + var rebuilder = IndexInitializer.GetContentIndexRebuilder(Factory.GetInstance(), IndexInitializer.GetMockContentService(), ScopeProvider.SqlContext, false); using (var luceneDir = new RandomIdRamDirectory()) using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, validator: new ContentValueSetValidator(false))) @@ -316,7 +314,7 @@ namespace Umbraco.Tests.UmbracoExamine public void Index_Delete_Index_Item_Ensure_Heirarchy_Removed() { - var rebuilder = IndexInitializer.GetContentIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockContentService(), ScopeProvider.SqlContext, false); + var rebuilder = IndexInitializer.GetContentIndexRebuilder(Factory.GetInstance(), IndexInitializer.GetMockContentService(), ScopeProvider.SqlContext, false); using (var luceneDir = new RandomIdRamDirectory()) using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir)) diff --git a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs index 85100410be..fc0d771fb8 100644 --- a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs +++ b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using LightInject; using Examine; using Examine.Search; using NUnit.Framework; @@ -11,6 +10,7 @@ using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Services; using Umbraco.Tests.Testing; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Composing; using Umbraco.Examine; namespace Umbraco.Tests.UmbracoExamine @@ -53,7 +53,7 @@ namespace Umbraco.Tests.UmbracoExamine == allRecs); - var propertyEditors = Container.GetInstance(); + var propertyEditors = Factory.GetInstance(); var rebuilder = IndexInitializer.GetContentIndexRebuilder(propertyEditors, contentService, ScopeProvider.SqlContext, true); using (var luceneDir = new RandomIdRamDirectory()) diff --git a/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs b/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs index 2a2aa536aa..3709952914 100644 --- a/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs +++ b/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs @@ -81,15 +81,15 @@ namespace Umbraco.Tests.Web.Controllers var textService = new Mock(); textService.Setup(x => x.Localize(It.IsAny(), It.IsAny(), It.IsAny>())).Returns("text"); - Container.RegisterSingleton(f => Mock.Of()); - Container.RegisterSingleton(f => Mock.Of()); - Container.RegisterSingleton(f => userServiceMock.Object); - Container.RegisterSingleton(f => entityService.Object); - Container.RegisterSingleton(f => dataTypeService.Object); - Container.RegisterSingleton(f => langService.Object); - Container.RegisterSingleton(f => textService.Object); - Container.RegisterSingleton(f => Mock.Of()); - Container.RegisterSingleton(f => new UmbracoApiControllerTypeCollection(new[] { typeof(ContentTreeController) })); + Composition.RegisterUnique(f => Mock.Of()); + Composition.RegisterUnique(f => Mock.Of()); + Composition.RegisterUnique(f => userServiceMock.Object); + Composition.RegisterUnique(f => entityService.Object); + Composition.RegisterUnique(f => dataTypeService.Object); + Composition.RegisterUnique(f => langService.Object); + Composition.RegisterUnique(f => textService.Object); + Composition.RegisterUnique(f => Mock.Of()); + Composition.RegisterUnique(f => new UmbracoApiControllerTypeCollection(new[] { typeof(ContentTreeController) })); } private MultipartFormDataContent GetMultiPartRequestContent(string json) @@ -214,7 +214,6 @@ namespace Umbraco.Tests.Web.Controllers var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); var usersController = new ContentController(propertyEditorCollection); - Container.InjectProperties(usersController); return usersController; } @@ -241,7 +240,6 @@ namespace Umbraco.Tests.Web.Controllers var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); var usersController = new ContentController(propertyEditorCollection); - Container.InjectProperties(usersController); return usersController; } @@ -273,7 +271,6 @@ namespace Umbraco.Tests.Web.Controllers var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); var usersController = new ContentController(propertyEditorCollection); - Container.InjectProperties(usersController); return usersController; } @@ -314,7 +311,6 @@ namespace Umbraco.Tests.Web.Controllers var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); var usersController = new ContentController(propertyEditorCollection); - Container.InjectProperties(usersController); return usersController; } @@ -349,7 +345,6 @@ namespace Umbraco.Tests.Web.Controllers var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); var usersController = new ContentController(propertyEditorCollection); - Container.InjectProperties(usersController); return usersController; } @@ -388,7 +383,6 @@ namespace Umbraco.Tests.Web.Controllers var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); var usersController = new ContentController(propertyEditorCollection); - Container.InjectProperties(usersController); return usersController; } diff --git a/src/Umbraco.Tests/Web/Controllers/PluginControllerAreaTests.cs b/src/Umbraco.Tests/Web/Controllers/PluginControllerAreaTests.cs index 669c607aea..01525f12da 100644 --- a/src/Umbraco.Tests/Web/Controllers/PluginControllerAreaTests.cs +++ b/src/Umbraco.Tests/Web/Controllers/PluginControllerAreaTests.cs @@ -54,8 +54,8 @@ namespace Umbraco.Tests.Web.Controllers public class Plugin1Controller : PluginController { public Plugin1Controller(UmbracoContext umbracoContext) + : base(umbracoContext, null, null, null, null, null) { - UmbracoContext = umbracoContext; } } @@ -63,8 +63,8 @@ namespace Umbraco.Tests.Web.Controllers public class Plugin2Controller : PluginController { public Plugin2Controller(UmbracoContext umbracoContext) + : base(umbracoContext, null, null, null, null, null) { - UmbracoContext = umbracoContext; } } @@ -72,16 +72,16 @@ namespace Umbraco.Tests.Web.Controllers public class Plugin3Controller : PluginController { public Plugin3Controller(UmbracoContext umbracoContext) + : base(umbracoContext, null, null, null, null, null) { - UmbracoContext = umbracoContext; } } public class Plugin4Controller : PluginController { public Plugin4Controller(UmbracoContext umbracoContext) + : base(umbracoContext, null, null, null, null, null) { - UmbracoContext = umbracoContext; } } diff --git a/src/Umbraco.Tests/Web/Controllers/UsersControllerTests.cs b/src/Umbraco.Tests/Web/Controllers/UsersControllerTests.cs index 6604e25aa2..857e922ac9 100644 --- a/src/Umbraco.Tests/Web/Controllers/UsersControllerTests.cs +++ b/src/Umbraco.Tests/Web/Controllers/UsersControllerTests.cs @@ -6,6 +6,7 @@ using System.Web.Http; using Moq; using Newtonsoft.Json; using NUnit.Framework; +using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; @@ -37,12 +38,12 @@ namespace Umbraco.Tests.Web.Controllers // replace the true IUserService implementation with a mock // so that each test can configure the service to their liking - Container.RegisterSingleton(f => Mock.Of()); + Composition.RegisterUnique(f => Mock.Of()); // kill the true IEntityService too - Container.RegisterSingleton(f => Mock.Of()); - - Container.RegisterSingleton(); + Composition.RegisterUnique(f => Mock.Of()); + + Composition.RegisterUnique(); } [Test] @@ -68,7 +69,6 @@ namespace Umbraco.Tests.Web.Controllers .Returns((int id) => id == 1234 ? new User(1234, "Test", "test@test.com", "test@test.com", "", new List(), new int[0], new int[0]) : null); var usersController = new UsersController(); - Container.InjectProperties(usersController); return usersController; } @@ -125,7 +125,6 @@ namespace Umbraco.Tests.Web.Controllers ApiController Factory(HttpRequestMessage message, UmbracoHelper helper) { var usersController = new UsersController(); - Container.InjectProperties(usersController); return usersController; } @@ -153,7 +152,6 @@ namespace Umbraco.Tests.Web.Controllers .Returns(() => users); var usersController = new UsersController(); - Container.InjectProperties(usersController); return usersController; } diff --git a/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs b/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs index f4c15f7c19..c6609f38c9 100644 --- a/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs @@ -9,6 +9,7 @@ using Moq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Cache; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; using Umbraco.Core.Profiling; @@ -17,12 +18,12 @@ using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web; -using Umbraco.Web.Composing; using Umbraco.Web.Models; using Umbraco.Web.Mvc; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; using Umbraco.Web.Security; +using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Tests.Web.Mvc { @@ -33,6 +34,7 @@ namespace Umbraco.Tests.Web.Mvc public void SetUp() { Current.UmbracoContextAccessor = new TestUmbracoContextAccessor(); + Core.Composing.Current.Factory = Mock.Of(); } [TearDown] @@ -156,7 +158,8 @@ namespace Umbraco.Tests.Web.Mvc } public class MatchesDefaultIndexController : RenderMvcController - { } + { + } public class MatchesOverriddenIndexController : RenderMvcController { diff --git a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs index dce975d0c4..35e8f0a937 100644 --- a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs @@ -7,8 +7,10 @@ using System.Web.Security; using Moq; using NUnit.Framework; using Umbraco.Core.Cache; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Dictionary; +using Umbraco.Core.Logging; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; @@ -16,11 +18,11 @@ using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Tests.Testing; using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web; -using Umbraco.Web.Composing; using Umbraco.Web.Mvc; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; using Umbraco.Web.Security; +using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Tests.Web.Mvc { @@ -49,7 +51,7 @@ namespace Umbraco.Tests.Web.Mvc new TestVariationContextAccessor(), true); - var ctrl = new TestSurfaceController { UmbracoContext = umbracoContext }; + var ctrl = new TestSurfaceController(umbracoContext); var result = ctrl.Index(); @@ -71,7 +73,7 @@ namespace Umbraco.Tests.Web.Mvc new TestVariationContextAccessor(), true); - var ctrl = new TestSurfaceController { UmbracoContext = umbCtx }; + var ctrl = new TestSurfaceController(umbCtx); Assert.IsNotNull(ctrl.UmbracoContext); } @@ -91,9 +93,8 @@ namespace Umbraco.Tests.Web.Mvc new TestVariationContextAccessor(), true); - var controller = new TestSurfaceController { UmbracoContext = umbracoContext }; - Container.Register(_ => umbracoContext); - Container.InjectProperties(controller); + var controller = new TestSurfaceController(umbracoContext); + Composition.Register(_ => umbracoContext); Assert.IsNotNull(controller.Umbraco); } @@ -131,10 +132,10 @@ namespace Umbraco.Tests.Web.Mvc Mock.Of(), Mock.Of(), Mock.Of(), - new MembershipHelper(umbracoContext, Mock.Of(), Mock.Of()), - new ServiceContext()); + new MembershipHelper(new TestUmbracoContextAccessor(umbracoContext), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), null, Mock.Of(), Mock.Of()), + ServiceContext.CreatePartial()); - var ctrl = new TestSurfaceController { UmbracoContext = umbracoContext, Umbraco = helper }; + var ctrl = new TestSurfaceController(umbracoContext, helper); var result = ctrl.GetContent(2) as PublishedContentResult; Assert.IsNotNull(result); @@ -173,7 +174,7 @@ namespace Umbraco.Tests.Web.Mvc var routeData = new RouteData(); routeData.DataTokens.Add(Core.Constants.Web.UmbracoRouteDefinitionDataToken, routeDefinition); - var ctrl = new TestSurfaceController { UmbracoContext = umbracoContext, Umbraco = new UmbracoHelper() }; + var ctrl = new TestSurfaceController(umbracoContext, new UmbracoHelper()); ctrl.ControllerContext = new ControllerContext(contextBase, routeData, ctrl); var result = ctrl.GetContentFromCurrentPage() as PublishedContentResult; @@ -183,6 +184,15 @@ namespace Umbraco.Tests.Web.Mvc public class TestSurfaceController : SurfaceController { + public TestSurfaceController(UmbracoContext ctx, UmbracoHelper helper = null) + : base(ctx, null, ServiceContext.CreatePartial(), Mock.Of(), null, null) + { + if (helper != null) + { + Umbraco = helper; + } + } + public ActionResult Index() { return View(); diff --git a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs index a4b210dbef..0ad3bff109 100644 --- a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs @@ -3,18 +3,13 @@ using System.Globalization; using System.Linq; using System.Web.Mvc; using System.Web.Routing; -using LightInject; using Moq; using NUnit.Framework; -using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Composing; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.Persistence; -using Umbraco.Core.PropertyEditors; -using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Tests.TestHelpers; diff --git a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs index d98afa6bcc..047d0b0b8f 100644 --- a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs +++ b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs @@ -1,7 +1,6 @@ using System; using System.Linq; using System.Web; -using LightInject; using Moq; using NUnit.Framework; using Umbraco.Core; @@ -35,20 +34,22 @@ namespace Umbraco.Tests.Web // should not depend on more than IdkMap maybe - fix this! var entityService = new Mock(); entityService.Setup(x => x.GetId(It.IsAny(), It.IsAny())).Returns(Attempt.Fail()); - var serviceContext = new ServiceContext(entityService: entityService.Object); + var serviceContext = ServiceContext.CreatePartial(entityService: entityService.Object); // fixme - bad in a unit test - but Udi has a static ctor that wants it?! - var container = new Mock(); - container.Setup(x => x.GetInstance(typeof(TypeLoader))).Returns( - new TypeLoader(NullCacheProvider.Instance, SettingsForTests.GenerateMockGlobalSettings(), new ProfilingLogger(Mock.Of(), Mock.Of()))); - container.Setup(x => x.GetInstance(typeof (ServiceContext))).Returns(serviceContext); - Current.Container = container.Object; + var factory = new Mock(); + factory.Setup(x => x.GetInstance(typeof(TypeLoader))).Returns( + new TypeLoader(NullCacheProvider.Instance, LocalTempStorage.Default, new ProfilingLogger(Mock.Of(), Mock.Of()))); + factory.Setup(x => x.GetInstance(typeof (ServiceContext))).Returns(serviceContext); + + var settings = SettingsForTests.GetDefaultUmbracoSettings(); + factory.Setup(x => x.GetInstance(typeof(IUmbracoSettingsSection))).Returns(settings); + + Current.Factory = factory.Object; Umbraco.Web.Composing.Current.UmbracoContextAccessor = new TestUmbracoContextAccessor(); Udi.ResetUdiTypes(); - - UmbracoConfig.For.SetUmbracoSettings(SettingsForTests.GetDefaultUmbracoSettings()); } [TearDown] @@ -82,7 +83,7 @@ namespace Umbraco.Tests.Web .Returns((UmbracoContext umbCtx, IPublishedContent content, UrlProviderMode mode, string culture, Uri url) => UrlInfo.Url("/my-test-url")); var globalSettings = SettingsForTests.GenerateMockGlobalSettings(); - + var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); var publishedContent = Mock.Of(); Mock.Get(publishedContent).Setup(x => x.Id).Returns(1234); diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 9e9d5779b6..7c3fe8b4f9 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -917,7 +917,7 @@ }, "ansi-colors": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", "dev": true, "requires": { @@ -935,7 +935,7 @@ }, "ansi-escapes": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", + "resolved": "http://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", "dev": true }, @@ -1196,7 +1196,7 @@ "arraybuffer.slice": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", - "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==", + "integrity": "sha1-O7xCdd1YTMGxCAm4nU6LY6aednU=", "dev": true }, "asap": { @@ -1249,7 +1249,7 @@ "async-limiter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", + "integrity": "sha1-ePrtjD0HSrgfIrTphdeehzj3IPg=", "dev": true }, "asynckit": { @@ -2327,7 +2327,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -2450,7 +2450,7 @@ "core-js": { "version": "2.5.7", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", - "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==", + "integrity": "sha1-+XJgj/DOrWi4QaFqky0LGDeRgU4=", "dev": true }, "core-util-is": { @@ -3373,7 +3373,7 @@ "doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "integrity": "sha1-XNAfwQFiG0LEzX9dGmYkNxbT850=", "dev": true, "requires": { "esutils": "^2.0.2" @@ -3901,7 +3901,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -3937,7 +3937,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -4101,7 +4101,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true, "optional": true } @@ -4196,7 +4196,7 @@ "eslint-scope": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz", - "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==", + "integrity": "sha1-UL8wcekzi83EMzF5Sgy1M/ATYXI=", "dev": true, "requires": { "esrecurse": "^4.1.0", @@ -4206,13 +4206,13 @@ "eslint-utils": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz", - "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==", + "integrity": "sha1-moUbqJ7nxGA0b5fPiTnHKYgn5RI=", "dev": true }, "eslint-visitor-keys": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", - "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", + "integrity": "sha1-PzGA+y4pEBdxastMnW1bXDSmqB0=", "dev": true }, "espree": { @@ -4235,7 +4235,7 @@ "esquery": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", - "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", + "integrity": "sha1-QGxRZYsfWZGl+bYrHcJbAOPlxwg=", "dev": true, "requires": { "estraverse": "^4.0.0" @@ -4244,7 +4244,7 @@ "esrecurse": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "integrity": "sha1-AHo7n9vCs7uH5IeeoZyS/b05Qs8=", "dev": true, "requires": { "estraverse": "^4.1.0" @@ -4304,13 +4304,13 @@ "eventemitter3": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", - "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==", + "integrity": "sha1-CQtNbNvWRe0Qv3UNS1QHlC17oWM=", "dev": true }, "exec-buffer": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/exec-buffer/-/exec-buffer-3.2.0.tgz", - "integrity": "sha512-wsiD+2Tp6BWHoVv3B+5Dcx6E7u5zky+hUwOHjuH2hKSLR3dvRmX8fk8UD8uqQixHs4Wk6eDmiegVrMPjKj7wpA==", + "integrity": "sha1-sWhtvZBMfPmC5lLB9aebHlVzCCs=", "dev": true, "optional": true, "requires": { @@ -4519,7 +4519,7 @@ "fill-range": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", - "integrity": "sha1-6x53OrsFbc2N8r/favWbizqTZWU=", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", "dev": true, "requires": { "is-number": "^2.1.0", @@ -5066,7 +5066,7 @@ "fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "integrity": "sha1-a+Dem+mYzhavivwkSXue6bfM2a0=", "dev": true }, "fs-extra": { @@ -6397,7 +6397,7 @@ "gulp-eslint": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/gulp-eslint/-/gulp-eslint-5.0.0.tgz", - "integrity": "sha512-9GUqCqh85C7rP9120cpxXuZz2ayq3BZc85pCTuPJS03VQYxne0aWPIXWx6LSvsGPa3uRqtSO537vaugOh+5cXg==", + "integrity": "sha1-KiaECV93Syz3kxAmIHjFbMehK1I=", "dev": true, "requires": { "eslint": "^5.0.1", @@ -7255,7 +7255,7 @@ "has-binary2": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", - "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", + "integrity": "sha1-d3asYn8+p3JQz8My2rfd9eT10R0=", "dev": true, "requires": { "isarray": "2.0.1" @@ -7406,7 +7406,7 @@ "http-proxy": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", - "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", + "integrity": "sha1-etOElGWPhGBeL220Q230EPTlvpo=", "dev": true, "requires": { "eventemitter3": "^3.0.0", @@ -8004,7 +8004,7 @@ "is-resolvable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "integrity": "sha1-+xj4fOH+uSUWnJpAfBkxijIG7Yg=", "dev": true }, "is-retry-allowed": { @@ -8209,7 +8209,7 @@ "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", "dev": true }, "json-stable-stringify-without-jsonify": { @@ -8336,7 +8336,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true } } @@ -8944,7 +8944,7 @@ "lowercase-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "integrity": "sha1-b54wtHCE2XGnyCD/FabFFnt0wm8=", "dev": true }, "lpad-align": { @@ -8969,7 +8969,7 @@ "make-dir": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "integrity": "sha1-ecEDO4BRW9bSTsmTPoYMp17ifww=", "dev": true, "requires": { "pify": "^3.0.0" @@ -9167,7 +9167,7 @@ "mimic-fn": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "integrity": "sha1-ggyGo5M0ZA6ZUWkovQP8qIBX0CI=", "dev": true }, "minimatch": { @@ -12683,7 +12683,7 @@ "pluralize": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", - "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", + "integrity": "sha1-KYuJ34uTsCIdv0Ia0rGx6iP8Z3c=", "dev": true }, "posix-character-classes": { @@ -13173,7 +13173,7 @@ "qjobs": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", - "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "integrity": "sha1-xF6cYYAL0IfviNfiVkI73Unl0HE=", "dev": true }, "qs": { @@ -13369,7 +13369,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -13867,7 +13867,7 @@ "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=", "dev": true }, "sax": { @@ -14054,7 +14054,7 @@ "setprototypeof": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "integrity": "sha1-0L2FU2iHtv58DYGMuWLZ2RxU5lY=", "dev": true }, "shebang-command": { @@ -14338,7 +14338,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -14618,7 +14618,7 @@ "stream-consume": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.1.tgz", - "integrity": "sha512-tNa3hzgkjEP7XbCkbRXe1jpg+ievoa0O4SCFlMOYEscGSS4JJsckGL8swUyAa/ApGU3Ae4t6Honor4HhL+tRyg==", + "integrity": "sha1-0721mMK9CugrjKx6xQsRB6eZbEg=", "dev": true }, "stream-shift": { @@ -14630,7 +14630,7 @@ "streamroller": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-0.7.0.tgz", - "integrity": "sha512-WREzfy0r0zUqp3lGO096wRuUp7ho1X6uo/7DJfTlEi0Iv/4gT7YHqXDjKC2ioVGBZtE8QzsQD9nx1nIuoZ57jQ==", + "integrity": "sha1-odG3z4PTmvsNYwSaWsv5NJO99ks=", "dev": true, "requires": { "date-format": "^1.2.0", @@ -14657,7 +14657,7 @@ "readable-stream": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -14672,7 +14672,7 @@ "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -14689,7 +14689,7 @@ "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "integrity": "sha1-q5Pyeo3BPSjKyBXEYhQ6bZASrp4=", "dev": true, "requires": { "is-fullwidth-code-point": "^2.0.0", @@ -14879,7 +14879,7 @@ "strip-outer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", - "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", + "integrity": "sha1-sv0qv2YEudHmATBXGV34Nrip1jE=", "dev": true, "requires": { "escape-string-regexp": "^1.0.2" @@ -15016,7 +15016,7 @@ "end-of-stream": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "integrity": "sha1-7SljTRm6ukY7bOa4CjchPqtx7EM=", "dev": true, "requires": { "once": "^1.4.0" @@ -15231,7 +15231,7 @@ "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "integrity": "sha1-bTQzWIl2jSGyvNoKonfO07G/rfk=", "dev": true, "requires": { "os-tmpdir": "~1.0.2" @@ -15266,7 +15266,7 @@ "to-buffer": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", - "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==", + "integrity": "sha1-STvUj2LXxD/N7TE6A9ytsuEhOoA=", "dev": true }, "to-fast-properties": { @@ -15437,7 +15437,7 @@ "ultron": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", - "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", + "integrity": "sha1-n+FTahCmZKZSZqHjzPhf02MCvJw=", "dev": true }, "unc-path-regex": { @@ -15599,13 +15599,13 @@ "upath": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", - "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==", + "integrity": "sha1-NSVll+RqWB20eT0M5H+prr/J+r0=", "dev": true }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "integrity": "sha1-lMVA4f93KVbiKZUHwBCupsiDjrA=", "dev": true, "requires": { "punycode": "^2.1.0" @@ -16040,7 +16040,7 @@ "ws": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", - "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "integrity": "sha1-8c+E/i1ekB686U767OeF8YeiKPI=", "dev": true, "requires": { "async-limiter": "~1.0.0", diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbtoggle.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbtoggle.directive.js index e3c4cbf40c..c2c9ec068b 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbtoggle.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbtoggle.directive.js @@ -18,6 +18,7 @@ @param {boolean} checked Set to true or false to toggle the switch. +@param {boolean} disabled Set to true or false to disable the switch. @param {callback} onClick The function which should be called when the toggle is clicked. @param {string=} showLabels Set to true or false to show a "On" or "Off" label next to the switch. @param {string=} labelOn Set a custom label for when the switched is turned on. It will default to "On". @@ -115,6 +118,7 @@ templateUrl: 'views/components/buttons/umb-toggle.html', scope: { checked: "=", + disabled: "=", onClick: "&", labelOn: "@?", labelOff: "@?", diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js index 8564cf2c43..89c8b541d1 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js @@ -109,7 +109,7 @@ }); } })); - + } /** @@ -308,6 +308,18 @@ } } + function ensureDirtyIsSetIfAnyVariantIsDirty() { + + $scope.contentForm.$dirty = false; + + for (var i = 0; i < $scope.content.variants.length; i++) { + if($scope.content.variants[i].isDirty){ + $scope.contentForm.$dirty = true; + return; + } + } + } + // This is a helper method to reduce the amount of code repitition for actions: Save, Publish, SendToPublish function performSave(args) { @@ -331,6 +343,7 @@ eventsService.emit("content.saved", { content: $scope.content, action: args.action }); resetNestedFieldValiation(fieldsToRollback); + ensureDirtyIsSetIfAnyVariantIsDirty(); return $q.when(data); }, diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js index f2e8b261c3..71fbabe943 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js @@ -3,7 +3,7 @@ function ContentNodeInfoDirective($timeout, logResource, eventsService, userService, localizationService, dateHelper, editorService, redirectUrlsResource, overlayService) { - function link(scope, element, attrs, umbVariantContentCtrl) { + function link(scope, umbVariantContentCtrl) { var evts = []; var isInfoTab = false; @@ -22,19 +22,12 @@ // set currentVariant scope.currentVariant = _.find(scope.node.variants, (v) => v.active); - // find the urls for the currently selected language - if(scope.node.variants.length > 1) { - // nodes with variants - scope.currentUrls = _.filter(scope.node.urls, (url) => scope.currentVariant.language.culture === url.culture); - } else { - // invariant nodes - scope.currentUrls = scope.node.urls; - } + updateCurrentUrls(); // if there are any infinite editors open we are in infinite editing scope.isInfiniteMode = editorService.getNumberOfEditors() > 0 ? true : false; - userService.getCurrentUser().then(function(user){ + userService.getCurrentUser().then(function (user) { // only allow change of media type if user has access to the settings sections const hasAccessToSettings = user.allowedSections.indexOf("settings") !== -1 ? true : false; scope.allowChangeDocumentType = hasAccessToSettings; @@ -42,8 +35,8 @@ }); var keys = [ - "general_deleted", - "content_unpublished", + "general_deleted", + "content_unpublished", "content_published", "content_publishedPendingChanges", "content_notCreated", @@ -55,7 +48,7 @@ ]; localizationService.localizeMany(keys) - .then(function(data){ + .then(function (data) { labels.deleted = data[0]; labels.unpublished = data[1]; //aka draft labels.published = data[2]; @@ -64,9 +57,9 @@ labels.unsavedChanges = data[5]; labels.doctypeChangeWarning = data[6]; labels.notPublished = data[9]; - + scope.historyLabel = scope.node.variants && scope.node.variants.length === 1 ? data[7] : data[8]; - + setNodePublishStatus(); if (scope.currentUrls.length === 0) { @@ -123,23 +116,23 @@ scope.openDocumentType = function (documentType) { - const variantIsDirty = _.some(scope.node.variants, function(variant) { + const variantIsDirty = _.some(scope.node.variants, function (variant) { return variant.isDirty; }); // add confirmation dialog before opening the doc type editor - if(variantIsDirty) { + if (variantIsDirty) { const confirm = { title: labels.unsavedChanges, view: "default", content: labels.doctypeChangeWarning, submitButtonLabelKey: "general_continue", closeButtonLabelKey: "general_cancel", - submit: function() { + submit: function () { openDocTypeEditor(documentType); overlayService.close(); }, - close: function() { + close: function () { overlayService.close(); } }; @@ -153,12 +146,12 @@ function openDocTypeEditor(documentType) { const editor = { id: documentType.id, - submit: function(model) { + submit: function (model) { const args = { node: scope.node }; eventsService.emit('editors.content.reload', args); editorService.close(); }, - close: function() { + close: function () { editorService.close(); } }; @@ -168,10 +161,10 @@ scope.openTemplate = function () { var templateEditor = { id: scope.node.templateId, - submit: function(model) { + submit: function (model) { editorService.close(); }, - close: function() { + close: function () { editorService.close(); } }; @@ -183,16 +176,16 @@ scope.node.template = templateAlias; }; - scope.openRollback = function() { - + scope.openRollback = function () { + var rollback = { node: scope.node, - submit: function(model) { + submit: function (model) { const args = { node: scope.node }; eventsService.emit("editors.content.reload", args); editorService.close(); }, - close: function() { + close: function () { editorService.close(); } }; @@ -304,6 +297,17 @@ }); } + function updateCurrentUrls() { + // find the urls for the currently selected language + if (scope.node.variants.length > 1) { + // nodes with variants + scope.currentUrls = _.filter(scope.node.urls, (url) => scope.currentVariant.language.culture === url.culture); + } else { + // invariant nodes + scope.currentUrls = scope.node.urls; + } + } + // load audit trail and redirects when on the info tab evts.push(eventsService.on("app.tabChange", function (event, args) { $timeout(function () { @@ -328,6 +332,7 @@ loadAuditTrail(); loadRedirectUrls(); setNodePublishStatus(); + updateCurrentUrls(); } }); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/events/events.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/events/events.directive.js index 7578ade867..6e75973ae7 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/events/events.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/events/events.directive.js @@ -136,7 +136,7 @@ angular.module('umbraco.directives') return; } - angularHelper.safeApply(scope, attrs.onOutsideClick); + scope.$apply(attrs.onOutsideClick); } diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/grid/grid.rte.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/grid/grid.rte.directive.js index 0dddada8ad..372fb472fa 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/grid/grid.rte.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/grid/grid.rte.directive.js @@ -55,7 +55,7 @@ angular.module("umbraco.directives") //initialize the standard editor functionality for Umbraco tinyMceService.initializeEditor({ editor: editor, - value: scope.value, + model: scope, currentForm: angularHelper.getCurrentForm(scope) }); @@ -67,7 +67,7 @@ angular.module("umbraco.directives") $timeout(function () { if (scope.value === null) { - editor.trigger("focus"); + editor.focus(); } }, 400); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js index eaf67bcb91..29920ebf00 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js @@ -146,7 +146,6 @@ function valFormManager(serverValidationManager, $rootScope, $timeout, $location var infiniteEditors = editorService.getEditors(); if (!formCtrl.$dirty && infiniteEditors.length === 0 || isSavingNewItem && infiniteEditors.length === 0) { - confirmed = true; return; } diff --git a/src/Umbraco.Web.UI.Client/src/common/filters/safehtml.filter.js b/src/Umbraco.Web.UI.Client/src/common/filters/safehtml.filter.js new file mode 100644 index 0000000000..50d8574306 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/filters/safehtml.filter.js @@ -0,0 +1,6 @@ +angular.module('umbraco.filters') + .filter('safe_html', ['$sce', function($sce){ + return function(text) { + return $sce.trustAsHtml(text); + }; + }]); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js index f6c2e93182..3fc11f8225 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js @@ -1194,7 +1194,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s .css("top", "auto") .css("margin-top", "0") .css("width", tinyMceWidth); - } + } }, @@ -1214,15 +1214,15 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s if (!args.editor) { throw "args.editor is required"; } - //if (!args.value) { - // throw "args.value is required"; + //if (!args.model.value) { + // throw "args.model.value is required"; //} var unwatch = null; //Starts a watch on the model value so that we can update TinyMCE if the model changes behind the scenes or from the server function startWatch() { - unwatch = $rootScope.$watch(() => args.value, function (newVal, oldVal) { + unwatch = $rootScope.$watch(() => args.model.value, function (newVal, oldVal) { if (newVal !== oldVal) { //update the display val again if it has changed from the server; //uses an empty string in the editor when the value is null @@ -1247,7 +1247,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s //stop watching before we update the value stopWatch(); angularHelper.safeApply($rootScope, function () { - args.value = args.editor.getContent(); + args.model.value = args.editor.getContent(); }); //re-watch the value startWatch(); @@ -1255,8 +1255,8 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s args.editor.on('init', function (e) { - if (args.value) { - args.editor.setContent(args.value); + if (args.model.value) { + args.editor.setContent(args.model.value); } //enable browser based spell checking args.editor.getBody().setAttribute('spellcheck', true); diff --git a/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle.less b/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle.less index 150963cbb2..4c30ae583c 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle.less @@ -28,7 +28,8 @@ .umb-toggle__toggle { cursor: pointer; - display: inline-block; + align-items: center; + display: flex; width: 48px; height: 24px; background: @gray-8; @@ -41,6 +42,11 @@ background-color: @green; } +.umb-toggle--disabled .umb-toggle__toggle { + cursor: not-allowed; + opacity: 0.8; +} + .umb-toggle--checked .umb-toggle__handler { transform: translate3d(24px, 0, 0) rotate(0); } @@ -63,7 +69,7 @@ .umb-toggle__icon { position: absolute; - top: 3px; + line-height: 1em; text-decoration: none; transition: all 0.2s ease; } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation.less index 3b49dceeb2..d8b83af67a 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation.less @@ -19,6 +19,7 @@ align-items: center; justify-content: center; height: @editorHeaderHeight; + position: relative; } .umb-sub-views-nav-item:focus { @@ -46,6 +47,33 @@ margin-bottom: 7px; } +.umb-sub-views-nav-item .badge { + position: absolute; + top: 6px; + right: 6px; + min-width: 16px; + color: @white; + background-color: @turquoise-d1; + border: 2px solid @white; + border-radius: 50%; + font-size: 10px; + font-weight: bold; + padding: 2px; + line-height: 16px; + display: block; + + &.-type-alert { + background-color: @red-l1; + } + &.-type-warning { + background-color: @yellow-d2; + } + &:empty { + height: 12px; + min-width: 12px; + } +} + .umb-sub-views-nav-item-text { font-size: 12px; line-height: 1em; @@ -80,4 +108,4 @@ grid-template-columns: 1fr 1fr 1fr; min-width: auto; margin-top: 10px; -} \ No newline at end of file +} diff --git a/src/Umbraco.Web.UI.Client/src/less/components/users/umb-permission.less b/src/Umbraco.Web.UI.Client/src/less/components/users/umb-permission.less index f5a81e3393..0d74986d65 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/users/umb-permission.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/users/umb-permission.less @@ -4,10 +4,6 @@ padding: 7px 0; } -.umb-permission--disabled { - opacity: 0.8; -} - .umb-permission:last-of-type { border-bottom: none; } @@ -25,6 +21,12 @@ cursor: pointer; } +.umb-permission--disabled .umb-permission__toggle, +.umb-permission--disabled .umb-permission__content { + cursor: not-allowed; + opacity: 0.8; +} + .umb-permission__description { font-size: 13px; color: @gray-4; diff --git a/src/Umbraco.Web.UI.Client/src/less/helveticons.less b/src/Umbraco.Web.UI.Client/src/less/helveticons.less index 60ccd3c357..1083a490e0 100644 --- a/src/Umbraco.Web.UI.Client/src/less/helveticons.less +++ b/src/Umbraco.Web.UI.Client/src/less/helveticons.less @@ -17,12 +17,14 @@ -webkit-font-smoothing: antialiased; *margin-right: .3em; } + [class^="icon-"]:before, [class*=" icon-"]:before { text-decoration: inherit; display: inline-block; speak: none; } + /* [class^="icon-"]:before, [class*=" icon-"]:before { font-family: 'icomoon'; @@ -38,7 +40,6 @@ i.large{ font-size: 32px; } - i.medium{ font-size: 24px; } @@ -187,8 +188,6 @@ i.small{ .icon-umb-translation:before, .traytranslation:before { content: "\e1fd"; } - - .icon-tv:before { content: "\e02e"; } @@ -213,7 +212,8 @@ i.small{ .icon-train:before { content: "\e035"; } -.icon-trafic:before { +.icon-trafic:before, +.icon-traffic:before { content: "\e036"; } .icon-traffic-alt:before { @@ -255,6 +255,7 @@ i.small{ .icon-target:before { content: "\e043"; } +.icon-temperature-alt:before, .icon-temperatrure-alt:before { content: "\e044"; } @@ -267,6 +268,7 @@ i.small{ .icon-theater:before { content: "\e047"; } +.icon-thief:before, .icon-theif:before { content: "\e048"; } @@ -375,6 +377,7 @@ i.small{ .icon-shuffle:before { content: "\e06b"; } +.icon-science:before, .icon-sience:before { content: "\e06c"; } @@ -747,6 +750,7 @@ i.small{ .icon-pictures-alt-2:before { content: "\e0e7"; } +.icon-panel-close:before, .icon-pannel-close:before { content: "\e0e8"; } @@ -1627,6 +1631,7 @@ i.small{ .icon-alarm-clock:before { content: "\e20c"; } +.icon-addressbook:before, .icon-adressbook:before { content: "\e20d"; } diff --git a/src/Umbraco.Web.UI.Client/src/views/components/buttons/umb-toggle.html b/src/Umbraco.Web.UI.Client/src/views/components/buttons/umb-toggle.html index bc5c114bb6..c8039448fd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/buttons/umb-toggle.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/buttons/umb-toggle.html @@ -1,4 +1,4 @@ -

- Choose where to copy {{source.name}} to in the tree structure below + Choose where to copy + {{source.name}} + to in the tree structure below

diff --git a/src/Umbraco.Web.UI.Client/src/views/content/create.html b/src/Umbraco.Web.UI.Client/src/views/content/create.html index e76d17caa8..94299f6a54 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/create.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/create.html @@ -25,13 +25,13 @@ -
    +
      -
    • - +
    • + - {{value}} + {{blueprint.name}}
    • diff --git a/src/Umbraco.Web.UI.Client/src/views/content/move.html b/src/Umbraco.Web.UI.Client/src/views/content/move.html index b655917f6f..49df26848d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/move.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/move.html @@ -11,7 +11,9 @@
      - {{source.name}} was moved underneath {{target.name}} + {{source.name}} + was moved to + {{target.name}}
      diff --git a/src/Umbraco.Web.UI.Client/src/views/content/notify.html b/src/Umbraco.Web.UI.Client/src/views/content/notify.html index e7c4d4d785..8e2f860661 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/notify.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/notify.html @@ -13,13 +13,13 @@
- {{currentNode.name}} + {{currentNode.name}}
- Set your notification for {{ currentNode.name }} + 0); + + if(result) return true; + + for (var t=0; t < variant.tabs.length; t++){ + for (var p=0; p < variant.tabs[t].properties.length; p++){ + + var property = variant.tabs[t].properties[p]; + + if(property.culture == null) continue; + + result = result || (property.value != null && property.value.length > 0); + + if(result) return true; + } + } + + return result; + } + function pristineVariantFilter(variant) { return !(dirtyVariantFilter(variant)); } function onInit() { + + vm.variants = $scope.model.variants; if (!$scope.model.title) { @@ -69,6 +93,13 @@ vm.hasPristineVariants = false; + _.each(vm.variants, + function (variant) { + if(variant.state !== "NotCreated"){ + vm.isNew = false; + } + }); + _.each(vm.variants, function (variant) { variant.compositeId = variant.language.culture + "_" + (variant.segment ? variant.segment : ""); @@ -78,6 +109,10 @@ if (!vm.hasPristineVariants) { vm.hasPristineVariants = pristineVariantFilter(variant); } + + if(vm.isNew && hasAnyData(variant)){ + variant.save = true; + } }); if (vm.variants.length !== 0) { @@ -103,8 +138,16 @@ $scope.model.disableSubmitButton = true; } - vm.loading = false; - + var labelKey = vm.isNew ? "content_languagesToPublishForFirstTime" : "content_languagesToPublish"; + + localizationService.localize(labelKey).then(function (value) { + vm.headline = value; + + vm.loading = false; + }); + + + } onInit(); diff --git a/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.html b/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.html index 8ca8b78b23..b33b7ccbfc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.html @@ -1,7 +1,7 @@
-

+

{{vm.headline}}

@@ -65,7 +65,7 @@
{{notification.message}}
- +
diff --git a/src/Umbraco.Web.UI.Client/src/views/content/overlays/save.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/overlays/save.controller.js index a99da13811..8d21234aee 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/overlays/save.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/overlays/save.controller.js @@ -1,16 +1,17 @@ (function () { "use strict"; - + function SaveContentController($scope, localizationService) { var vm = this; vm.loading = true; vm.hasPristineVariants = false; + vm.isNew = true; vm.changeSelection = changeSelection; vm.dirtyVariantFilter = dirtyVariantFilter; vm.pristineVariantFilter = pristineVariantFilter; - + function changeSelection(variant) { var firstSelected = _.find(vm.variants, function (v) { return v.save; @@ -30,8 +31,28 @@ return !(dirtyVariantFilter(variant)); } - function onInit() { + function hasAnyData(variant) { + var result = variant.isDirty != null || (variant.name != null && variant.name.length > 0); + if(result) return true; + + for (var t=0; t < variant.tabs.length; t++){ + for (var p=0; p < variant.tabs[t].properties.length; p++){ + + var property = variant.tabs[t].properties[p]; + + if(property.culture == null) continue; + + result = result || (property.value != null && property.value.length > 0); + + if(result) return true; + } + } + + return result; + } + + function onInit() { vm.variants = $scope.model.variants; if(!$scope.model.title) { @@ -42,6 +63,13 @@ vm.hasPristineVariants = false; + _.each(vm.variants, + function (variant) { + if(variant.state !== "NotCreated"){ + vm.isNew = false; + } + }); + _.each(vm.variants, function (variant) { variant.compositeId = variant.language.culture + "_" + (variant.segment ? variant.segment : ""); @@ -51,6 +79,10 @@ if (!vm.hasPristineVariants) { vm.hasPristineVariants = pristineVariantFilter(variant); } + + if(vm.isNew && hasAnyData(variant)){ + variant.save = true; + } }); if (vm.variants.length !== 0) { @@ -88,5 +120,5 @@ } angular.module("umbraco").controller("Umbraco.Overlays.SaveContentController", SaveContentController); - + })(); diff --git a/src/Umbraco.Web.UI.Client/src/views/content/overlays/save.html b/src/Umbraco.Web.UI.Client/src/views/content/overlays/save.html index 6f4aef4e84..c4dcd5b767 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/overlays/save.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/overlays/save.html @@ -1,72 +1,120 @@
-
-

-
+
-
-
- -
- -
- - -
- -
- -
-
{{saveVariantSelectorForm.saveVariantSelector.errorMsg}}
-
- -
-
{{notification.message}}
-
- -
-
- -
+
+
+

+
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+
{{saveVariantSelectorForm.saveVariantSelector.errorMsg}}
+
+ +
+
{{notification.message}}
+
+ +
+
+ +
+
+
-
-
-
-

+
+
+

-
-
-
- {{ variant.language.name }} - * -
+
-
- -
+
+ +
+ +
+ -
-
{{notification.message}}
+
+ +
+ +
+
{{saveVariantSelectorForm.saveVariantSelector.errorMsg}}
+
+ +
+
{{notification.message}}
+
+ +
+
+ + +
+
+
+ +
+
+

+
+ +
+
+
+ {{ variant.language.name }} + * +
+ +
+ +
+ +
+
{{notification.message}}
+
-
diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/content/redirecturls.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/content/redirecturls.html index 24b32815e5..837c24cb0c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/content/redirecturls.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/content/redirecturls.html @@ -54,9 +54,10 @@
-
Original URL
+
Culture
+
Original URL
-
Redirected To
+
Redirected To
@@ -64,8 +65,10 @@
- -
+
+ {{redirectUrl.culture ||'*'}} +
+ @@ -73,7 +76,7 @@
-
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/default/StartupDashboardIntro.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/default/StartupDashboardIntro.html index 72cc3ddb06..0478e6ba3c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/default/StartupDashboardIntro.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/default/StartupDashboardIntro.html @@ -19,7 +19,7 @@
-
+
@@ -65,7 +65,7 @@
- +
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/languages/edit.html b/src/Umbraco.Web.UI.Client/src/views/languages/edit.html index 533337549b..4c9ef8dc9a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/languages/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/languages/edit.html @@ -39,7 +39,8 @@
+ checked="vm.language.isDefault" + disabled="vm.initIsDefault">
{{vm.labels.defaultLanguage}}
diff --git a/src/Umbraco.Web.UI.Client/src/views/media/edit.html b/src/Umbraco.Web.UI.Client/src/views/media/edit.html index 2dfc8c967e..be1219f052 100644 --- a/src/Umbraco.Web.UI.Client/src/views/media/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/media/edit.html @@ -13,7 +13,8 @@ hide-icon="true" hide-description="true" hide-alias="true" - navigation="content.apps"> + navigation="content.apps" + on-select-navigation-item="appChanged(app)"> diff --git a/src/Umbraco.Web.UI.Client/src/views/media/media.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/media/media.edit.controller.js index cc63b5f9f0..19932887fd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/media/media.edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/media/media.edit.controller.js @@ -95,8 +95,11 @@ function mediaEditController($scope, $routeParams, $q, appState, mediaResource, function init() { - // set first app to active - $scope.content.apps[0].active = true; + if (!$scope.app) { + // set first app to active + $scope.content.apps[0].active = true; + $scope.app = $scope.content.apps[0]; + } // setup infinite mode if(infiniteMode) { @@ -207,6 +210,10 @@ function mediaEditController($scope, $routeParams, $q, appState, mediaResource, } }; + $scope.appChanged = function (app) { + $scope.app = app; + } + evts.push(eventsService.on("editors.mediaType.saved", function(name, args) { // if this media item uses the updated media type we need to reload the media item if(args && args.mediaType && args.mediaType.key === $scope.content.contentType.key) { diff --git a/src/Umbraco.Web.UI.Client/src/views/media/move.html b/src/Umbraco.Web.UI.Client/src/views/media/move.html index cb0b2fd7ca..081fd0124f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/media/move.html +++ b/src/Umbraco.Web.UI.Client/src/views/media/move.html @@ -11,7 +11,9 @@
- {{source.name}} was moved underneath {{target.name}} + {{source.name}} + was moved to + {{target.name}}
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js index 1d8cdfa03f..96e7ccdbfd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js @@ -701,16 +701,23 @@ function listViewController($scope, $routeParams, $injector, $timeout, currentUs } getContentTypesCallback(id).then(function (listViewAllowedTypes) { - $scope.listViewAllowedTypes = listViewAllowedTypes; + $scope.listViewAllowedTypes = listViewAllowedTypes; var blueprints = false; _.each(listViewAllowedTypes, function (allowedType) { if (_.isEmpty(allowedType.blueprints)) { - // this helps the view understand that there are no blueprints available - allowedType.blueprints = null; + // this helps the view understand that there are no blueprints available + allowedType.blueprints = null; } else { - blueprints = true; + blueprints = true; + // turn the content type blueprints object into an array of sortable objects for the view + allowedType.blueprints = _.map(_.pairs(allowedType.blueprints || {}), function (pair) { + return { + id: pair[0], + name: pair[1] + }; + }); } }); @@ -776,17 +783,19 @@ function listViewController($scope, $routeParams, $injector, $timeout, currentUs } } - function createBlank(entityType, docTypeAlias) { $location .path("/" + entityType + "/" + entityType + "/edit/" + $scope.contentId) - .search("doctype=" + docTypeAlias + "&create=true"); + .search("doctype", docTypeAlias) + .search("create", "true"); } function createFromBlueprint(entityType, docTypeAlias, blueprintId) { $location .path("/" + entityType + "/" + entityType + "/edit/" + $scope.contentId) - .search("doctype=" + docTypeAlias + "&create=true&blueprintId=" + blueprintId); + .search("doctype", docTypeAlias) + .search("create", "true") + .search("blueprintId", blueprintId); } $scope.createBlank = createBlank; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html index 44636d7033..77576cac6a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html @@ -27,10 +27,10 @@ - - + + - {{::value}} + {{::blueprint.name}} @@ -48,9 +48,9 @@ {{::contentType.name}} (blank) - +    - {{::value}} + {{::blueprint.name}} @@ -58,11 +58,11 @@
    -
  • - +
  • + - {{value}} + {{blueprint.name}}
  • diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js index 33a819fa1c..2181361470 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js @@ -58,7 +58,7 @@ angular.module("umbraco") //initialize the standard editor functionality for Umbraco tinyMceService.initializeEditor({ editor: editor, - value: $scope.model.value, + model: $scope.model, currentForm: angularHelper.getCurrentForm($scope) }); diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 267898e897..418e5de677 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -88,15 +88,15 @@ - + - - - + + + all @@ -107,7 +107,7 @@ - 8.0.0-alpha.24 + 8.0.0-alpha.31 @@ -164,13 +164,6 @@ directoryBrowser.aspx - - ChangeDocType.aspx - ASPXCodeBehind - - - ChangeDocType.aspx - default.Master ASPXCodeBehind @@ -267,16 +260,9 @@ scripting.config - - ExamineSettings.config - feedProxy.config - - ExamineIndex.config - Designer - Dashboard.config Designer @@ -301,15 +287,6 @@ - - - - - - - - ASPXCodeBehind - @@ -323,7 +300,6 @@ - @@ -334,10 +310,6 @@ Designer - - - Designer - diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EditProfile.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EditProfile.cshtml index d74b090c07..79c83a883d 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EditProfile.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EditProfile.cshtml @@ -9,9 +9,9 @@ Html.EnableClientValidation(); Html.EnableUnobtrusiveJavaScript(); - Html.RequiresJs("/umbraco/lib/jquery/jquery.min.js"); - Html.RequiresJs("/umbraco/lib/jquery-validate/jquery.validate.min.js"); - Html.RequiresJs("/umbraco/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"); + Html.RequiresJs("https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"); + Html.RequiresJs("https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.0/jquery.validate.min.js"); + Html.RequiresJs("https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min.js"); var success = TempData["ProfileUpdateSuccess"] != null; } diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Login.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Login.cshtml index 7c5ed032f7..31a3bd604d 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Login.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Login.cshtml @@ -10,9 +10,9 @@ Html.EnableClientValidation(); Html.EnableUnobtrusiveJavaScript(); - Html.RequiresJs("/umbraco/lib/jquery/jquery.min.js"); - Html.RequiresJs("/umbraco/lib/jquery-validate/jquery.validate.min.js"); - Html.RequiresJs("/umbraco/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"); + Html.RequiresJs("https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"); + Html.RequiresJs("https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.0/jquery.validate.min.js"); + Html.RequiresJs("https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min.js"); } @* NOTE: This RenderJsHere code should be put on your main template page where the rest of your script tags are placed *@ diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/LoginStatus.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/LoginStatus.cshtml index 4fa4bff173..8eadbb342f 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/LoginStatus.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/LoginStatus.cshtml @@ -1,5 +1,4 @@ @using System.Web.Mvc.Html -@using ClientDependency.Core.Mvc @using Umbraco.Web @using Umbraco.Web.Models @using Umbraco.Web.Controllers @@ -7,13 +6,6 @@ @{ var loginStatusModel = Members.GetCurrentLoginStatus(); - - Html.EnableClientValidation(); - Html.EnableUnobtrusiveJavaScript(); - Html.RequiresJs("/umbraco/lib/jquery/jquery.min.js"); - Html.RequiresJs("/umbraco/lib/jquery-validate/jquery.validate.min.js"); - Html.RequiresJs("/umbraco/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"); - var logoutModel = new PostRedirectModel(); @* @@ -24,9 +16,6 @@ *@ } -@* NOTE: This RenderJsHere code should be put on your main template page where the rest of your script tags are placed *@ -@Html.RenderJsHere() - @if (loginStatusModel.IsLoggedIn) {

    You are currently logged in as @loginStatusModel.Name

    diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/RegisterMember.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/RegisterMember.cshtml index b448a992d9..b9fbea4733 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/RegisterMember.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/RegisterMember.cshtml @@ -33,9 +33,9 @@ Html.EnableClientValidation(); Html.EnableUnobtrusiveJavaScript(); - Html.RequiresJs("/umbraco/lib/jquery/jquery.min.js"); - Html.RequiresJs("/umbraco/lib/jquery-validate/jquery.validate.min.js"); - Html.RequiresJs("/umbraco/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"); + Html.RequiresJs("https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"); + Html.RequiresJs("https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.0/jquery.validate.min.js"); + Html.RequiresJs("https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min.js"); var success = TempData["FormSuccess"] != null; } diff --git a/src/Umbraco.Web.UI/Umbraco/Views/Default.cshtml b/src/Umbraco.Web.UI/Umbraco/Views/Default.cshtml index 4659674c59..6b7906dac5 100644 --- a/src/Umbraco.Web.UI/Umbraco/Views/Default.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/Views/Default.cshtml @@ -1,6 +1,7 @@ @using Umbraco.Core @using ClientDependency.Core @using ClientDependency.Core.Mvc +@using Umbraco.Core.Composing @using Umbraco.Core.IO @using Umbraco.Web @using Umbraco.Core.Configuration @@ -76,7 +77,7 @@
- @@ -112,15 +113,15 @@ view="ysodOverlay.view"> - - @Html.BareMinimumServerVariablesScript(Url, Url.Action("ExternalLogin", "BackOffice", new { area = ViewBag.UmbracoPath }), Model.Features, UmbracoConfig.For.GlobalSettings()) + @Html.BareMinimumServerVariablesScript(Url, Url.Action("ExternalLogin", "BackOffice", new { area = ViewBag.UmbracoPath }), Model.Features, Current.Configs.Global()) - - - - -
-

<%= Services.TextService.Localize("defaultdialogs/siterepublishHelp")%>

-
- - - - - -
- - -
-

<%= Services.TextService.Localize("defaultdialogs/siterepublished")%>

- -
- -
-
\ No newline at end of file diff --git a/src/Umbraco.Web.UI/config/ExamineIndex.Release.config b/src/Umbraco.Web.UI/config/ExamineIndex.Release.config deleted file mode 100644 index ec0d18aa2d..0000000000 --- a/src/Umbraco.Web.UI/config/ExamineIndex.Release.config +++ /dev/null @@ -1,10 +0,0 @@ - - - - diff --git a/src/Umbraco.Web.UI/config/ExamineIndex.config b/src/Umbraco.Web.UI/config/ExamineIndex.config deleted file mode 100644 index 833a65c8d1..0000000000 --- a/src/Umbraco.Web.UI/config/ExamineIndex.config +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/src/Umbraco.Web.UI/config/ExamineSettings.Release.config b/src/Umbraco.Web.UI/config/ExamineSettings.Release.config deleted file mode 100644 index 6b3aaa0372..0000000000 --- a/src/Umbraco.Web.UI/config/ExamineSettings.Release.config +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/src/Umbraco.Web.UI/config/ExamineSettings.config b/src/Umbraco.Web.UI/config/ExamineSettings.config deleted file mode 100644 index f0fd2b69fc..0000000000 --- a/src/Umbraco.Web.UI/config/ExamineSettings.config +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - diff --git a/src/Umbraco.Web.UI/web.Template.Debug.config b/src/Umbraco.Web.UI/web.Template.Debug.config index 5a37c4a09b..8c4b3bb299 100644 --- a/src/Umbraco.Web.UI/web.Template.Debug.config +++ b/src/Umbraco.Web.UI/web.Template.Debug.config @@ -1,7 +1,8 @@ - + + +
+
+ + + + + - + diff --git a/src/Umbraco.Web.UI/web.Template.config b/src/Umbraco.Web.UI/web.Template.config index 50319370e9..68fee030a0 100644 --- a/src/Umbraco.Web.UI/web.Template.config +++ b/src/Umbraco.Web.UI/web.Template.config @@ -10,8 +10,6 @@
-
-
@@ -36,8 +34,6 @@ - - @@ -303,11 +299,11 @@ diff --git a/src/Umbraco.Web/Actions/ActionChangeDocType.cs b/src/Umbraco.Web/Actions/ActionChangeDocType.cs index 73772699d0..56868e9fb0 100644 --- a/src/Umbraco.Web/Actions/ActionChangeDocType.cs +++ b/src/Umbraco.Web/Actions/ActionChangeDocType.cs @@ -5,16 +5,17 @@ using Umbraco.Web.UI.Pages; namespace Umbraco.Web.Actions { - /// - /// This action is invoked when the document type of a piece of content is changed - /// - public class ActionChangeDocType : IAction - { - public char Letter => '7'; - public string Alias => "changeDocType"; - public string Category => Constants.Conventions.PermissionCategories.AdministrationCategory; - public string Icon => "axis-rotation-2"; - public bool ShowInNotifier => true; - public bool CanBePermissionAssigned => true; - } + //TODO: Add this back in when we support this functionality again + ///// + ///// This action is invoked when the document type of a piece of content is changed + ///// + //public class ActionChangeDocType : IAction + //{ + // public char Letter => '7'; + // public string Alias => "changeDocType"; + // public string Category => Constants.Conventions.PermissionCategories.AdministrationCategory; + // public string Icon => "axis-rotation-2"; + // public bool ShowInNotifier => true; + // public bool CanBePermissionAssigned => true; + //} } diff --git a/src/Umbraco.Web/Actions/ActionCollectionBuilder.cs b/src/Umbraco.Web/Actions/ActionCollectionBuilder.cs index 079705645d..ec1a9210a7 100644 --- a/src/Umbraco.Web/Actions/ActionCollectionBuilder.cs +++ b/src/Umbraco.Web/Actions/ActionCollectionBuilder.cs @@ -1,22 +1,16 @@ using System; using System.Collections.Generic; using System.Linq; -using LightInject; using Umbraco.Core.Composing; - namespace Umbraco.Web.Actions { internal class ActionCollectionBuilder : LazyCollectionBuilderBase { - public ActionCollectionBuilder(IServiceContainer container) - : base(container) - { } - protected override ActionCollectionBuilder This => this; - protected override IEnumerable CreateItems(params object[] args) + protected override IEnumerable CreateItems(IFactory factory) { - var items = base.CreateItems(args).ToList(); + var items = base.CreateItems(factory).ToList(); //validate the items, no actions should exist that do not either expose notifications or permissions var invalidItems = items.Where(x => !x.CanBePermissionAssigned && !x.ShowInNotifier).ToList(); diff --git a/src/Umbraco.Web/BatchedDatabaseServerMessenger.cs b/src/Umbraco.Web/BatchedDatabaseServerMessenger.cs index 8929c6edd9..ce0be7fbe6 100644 --- a/src/Umbraco.Web/BatchedDatabaseServerMessenger.cs +++ b/src/Umbraco.Web/BatchedDatabaseServerMessenger.cs @@ -26,7 +26,7 @@ namespace Umbraco.Web private readonly IUmbracoDatabaseFactory _databaseFactory; public BatchedDatabaseServerMessenger( - IRuntimeState runtime, IUmbracoDatabaseFactory databaseFactory, IScopeProvider scopeProvider, ISqlContext sqlContext, ProfilingLogger proflog, IGlobalSettings globalSettings, + IRuntimeState runtime, IUmbracoDatabaseFactory databaseFactory, IScopeProvider scopeProvider, ISqlContext sqlContext, IProfilingLogger proflog, IGlobalSettings globalSettings, bool enableDistCalls, DatabaseServerMessengerOptions options) : base(runtime, scopeProvider, sqlContext, proflog, globalSettings, enableDistCalls, options) { diff --git a/src/Umbraco.Web/Cache/DistributedCacheBinderComponent.cs b/src/Umbraco.Web/Cache/DistributedCacheBinderComponent.cs index 57b013a97c..94d274731d 100644 --- a/src/Umbraco.Web/Cache/DistributedCacheBinderComponent.cs +++ b/src/Umbraco.Web/Cache/DistributedCacheBinderComponent.cs @@ -1,24 +1,22 @@ -using Umbraco.Core; -using Umbraco.Core.Components; -using Umbraco.Core.Composing; +using Umbraco.Core.Components; namespace Umbraco.Web.Cache { - /// - /// Installs listeners on service events in order to refresh our caches. - /// - [RuntimeLevel(MinLevel = RuntimeLevel.Run)] - [RequiredComponent(typeof(IUmbracoCoreComponent))] // runs before every other IUmbracoCoreComponent! - public class DistributedCacheBinderComponent : UmbracoComponentBase, IUmbracoCoreComponent + public class DistributedCacheBinderComponent : IComponent { - public override void Compose(Composition composition) + private readonly IDistributedCacheBinder _binder; + + public DistributedCacheBinderComponent(IDistributedCacheBinder distributedCacheBinder) { - composition.Container.RegisterSingleton(); + _binder = distributedCacheBinder; } - public void Initialize(IDistributedCacheBinder distributedCacheBinder) + public void Initialize() { - distributedCacheBinder.BindEvents(); + _binder.BindEvents(); } + + public void Terminate() + { } } } diff --git a/src/Umbraco.Web/Cache/DistributedCacheBinderComposer.cs b/src/Umbraco.Web/Cache/DistributedCacheBinderComposer.cs new file mode 100644 index 0000000000..f0f57a203b --- /dev/null +++ b/src/Umbraco.Web/Cache/DistributedCacheBinderComposer.cs @@ -0,0 +1,21 @@ +using Umbraco.Core; +using Umbraco.Core.Components; +using Umbraco.Core.Composing; + +namespace Umbraco.Web.Cache +{ + /// + /// Installs listeners on service events in order to refresh our caches. + /// + [RuntimeLevel(MinLevel = RuntimeLevel.Run)] + [ComposeBefore(typeof(ICoreComposer))] // runs before every other IUmbracoCoreComponent! + public sealed class DistributedCacheBinderComposer : ComponentComposer, ICoreComposer + { + public override void Compose(Composition composition) + { + base.Compose(composition); + + composition.RegisterUnique(); + } + } +} diff --git a/src/Umbraco.Web/Components/BackOfficeUserAuditEventsComponent.cs b/src/Umbraco.Web/Components/BackOfficeUserAuditEventsComponent.cs index 79630ef613..545a781a8d 100644 --- a/src/Umbraco.Web/Components/BackOfficeUserAuditEventsComponent.cs +++ b/src/Umbraco.Web/Components/BackOfficeUserAuditEventsComponent.cs @@ -1,31 +1,25 @@ using System; using Umbraco.Core; using Umbraco.Core.Components; -using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; -using Umbraco.Core.Security; using Umbraco.Core.Services; using Umbraco.Web.Security; namespace Umbraco.Web.Components { - public sealed class BackOfficeUserAuditEventsComponent : UmbracoComponentBase, IUmbracoCoreComponent + public sealed class BackOfficeUserAuditEventsComponent : IComponent { - private IAuditService _auditService; - private IUserService _userService; - - private IUser GetPerformingUser(int userId) - { - var found = userId >= 0 ? _userService.GetUserById(userId) : null; - return found ?? new User {Id = 0, Name = "SYSTEM", Email = ""}; - } + private readonly IAuditService _auditService; + private readonly IUserService _userService; - - public void Initialize(IAuditService auditService, IUserService userService) + public BackOfficeUserAuditEventsComponent(IAuditService auditService, IUserService userService) { _auditService = auditService; _userService = userService; - + } + + public void Initialize() + { //BackOfficeUserManager.AccountLocked += ; //BackOfficeUserManager.AccountUnlocked += ; BackOfficeUserManager.ForgotPasswordRequested += OnForgotPasswordRequest; @@ -37,15 +31,22 @@ namespace Umbraco.Web.Components BackOfficeUserManager.PasswordChanged += OnPasswordChanged; BackOfficeUserManager.PasswordReset += OnPasswordReset; //BackOfficeUserManager.ResetAccessFailedCount += ; - } - + + public void Terminate() + { } + + private IUser GetPerformingUser(int userId) + { + var found = userId >= 0 ? _userService.GetUserById(userId) : null; + return found ?? new User { Id = 0, Name = "SYSTEM", Email = "" }; + } + private static string FormatEmail(IMembershipUser user) { return user == null ? string.Empty : user.Email.IsNullOrWhiteSpace() ? "" : $"<{user.Email}>"; } - private void OnLoginSuccess(object sender, EventArgs args) { if (args is IdentityAuditEventArgs identityArgs) diff --git a/src/Umbraco.Web/Components/BackOfficeUserAuditEventsComposer.cs b/src/Umbraco.Web/Components/BackOfficeUserAuditEventsComposer.cs new file mode 100644 index 0000000000..8364dc05f4 --- /dev/null +++ b/src/Umbraco.Web/Components/BackOfficeUserAuditEventsComposer.cs @@ -0,0 +1,7 @@ +using Umbraco.Core.Components; + +namespace Umbraco.Web.Components +{ + public sealed class BackOfficeUserAuditEventsComposer : ComponentComposer, ICoreComposer + { } +} diff --git a/src/Umbraco.Web/Components/DatabaseServerRegistrarAndMessengerComponent.cs b/src/Umbraco.Web/Components/DatabaseServerRegistrarAndMessengerComponent.cs index 990aa32ddd..2e3132a82c 100644 --- a/src/Umbraco.Web/Components/DatabaseServerRegistrarAndMessengerComponent.cs +++ b/src/Umbraco.Web/Components/DatabaseServerRegistrarAndMessengerComponent.cs @@ -1,9 +1,9 @@ using System; using System.Threading; using Examine; -using LightInject; using Umbraco.Core; using Umbraco.Core.Components; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Persistence; @@ -13,10 +13,10 @@ using Umbraco.Core.Services.Changes; using Umbraco.Core.Sync; using Umbraco.Examine; using Umbraco.Web.Cache; -using Umbraco.Web.Composing; using Umbraco.Web.Routing; using Umbraco.Web.Scheduling; using Umbraco.Web.Search; +using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Web.Components { @@ -35,32 +35,24 @@ namespace Umbraco.Web.Components // during Initialize / Startup, we end up checking Examine, which needs to be initialized beforehand // todo - should not be a strong dependency on "examine" but on an "indexing component" - [RequireComponent(typeof(ExamineComponent))] + [ComposeAfter(typeof(ExamineComposer))] - public sealed class DatabaseServerRegistrarAndMessengerComponent : UmbracoComponentBase, IUmbracoCoreComponent + public sealed class DatabaseServerRegistrarAndMessengerComposer : ComponentComposer, ICoreComposer { - private object _locker = new object(); - private DatabaseServerRegistrar _registrar; - private BatchedDatabaseServerMessenger _messenger; - private IRuntimeState _runtime; - private ILogger _logger; - private IServerRegistrationService _registrationService; - private BackgroundTaskRunner _touchTaskRunner; - private BackgroundTaskRunner _processTaskRunner; - private bool _started; - private IBackgroundTask[] _tasks; - private IndexRebuilder _indexRebuilder; - public override void Compose(Composition composition) { + base.Compose(composition); + composition.SetServerMessenger(factory => { var runtime = factory.GetInstance(); var databaseFactory = factory.GetInstance(); var globalSettings = factory.GetInstance(); - var proflog = factory.GetInstance(); + var proflog = factory.GetInstance(); var scopeProvider = factory.GetInstance(); var sqlContext = factory.GetInstance(); + var logger = factory.GetInstance(); + var indexRebuilder = factory.GetInstance(); return new BatchedDatabaseServerMessenger( runtime, databaseFactory, scopeProvider, sqlContext, proflog, globalSettings, @@ -88,13 +80,31 @@ namespace Umbraco.Web.Components //rebuild indexes if the server is not synced // NOTE: This will rebuild ALL indexes including the members, if developers want to target specific // indexes then they can adjust this logic themselves. - () => ExamineComponent.RebuildIndexes(_indexRebuilder, _logger, false, 5000) + () => + { + ExamineComponent.RebuildIndexes(indexRebuilder, logger, false, 5000); + } } }); }); } + } - public void Initialize(IRuntimeState runtime, IServerRegistrar serverRegistrar, IServerMessenger serverMessenger, IServerRegistrationService registrationService, ILogger logger, IndexRebuilder indexRebuilder) + public sealed class DatabaseServerRegistrarAndMessengerComponent : IComponent + { + private object _locker = new object(); + private readonly DatabaseServerRegistrar _registrar; + private readonly BatchedDatabaseServerMessenger _messenger; + private readonly IRuntimeState _runtime; + private readonly ILogger _logger; + private readonly IServerRegistrationService _registrationService; + private readonly BackgroundTaskRunner _touchTaskRunner; + private readonly BackgroundTaskRunner _processTaskRunner; + private bool _started; + private IBackgroundTask[] _tasks; + private IndexRebuilder _indexRebuilder; + + public DatabaseServerRegistrarAndMessengerComponent(IRuntimeState runtime, IServerRegistrar serverRegistrar, IServerMessenger serverMessenger, IServerRegistrationService registrationService, ILogger logger, IndexRebuilder indexRebuilder) { _registrar = serverRegistrar as DatabaseServerRegistrar; if (_registrar == null) throw new Exception("panic: registar."); @@ -111,7 +121,10 @@ namespace Umbraco.Web.Components new BackgroundTaskRunnerOptions { AutoStart = true }, logger); _processTaskRunner = new BackgroundTaskRunner("ServerInstProcess", new BackgroundTaskRunnerOptions { AutoStart = true }, logger); + } + public void Initialize() + { //We will start the whole process when a successful request is made UmbracoModule.RouteAttempt += RegisterBackgroundTasksOnce; @@ -119,6 +132,9 @@ namespace Umbraco.Web.Components _messenger.Startup(); } + public void Terminate() + { } + /// /// Handle when a request is made /// diff --git a/src/Umbraco.Web/Components/NotificationsComponent.cs b/src/Umbraco.Web/Components/NotificationsComponent.cs index 38ec283cfe..4b447598a3 100644 --- a/src/Umbraco.Web/Components/NotificationsComponent.cs +++ b/src/Umbraco.Web/Components/NotificationsComponent.cs @@ -7,7 +7,6 @@ using Umbraco.Core.Models.Entities; using Umbraco.Core.Services; using Umbraco.Core.Services.Implement; using Umbraco.Web.Actions; -using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; using System.Linq; @@ -17,36 +16,41 @@ using System.Globalization; namespace Umbraco.Web.Components { - [RuntimeLevel(MinLevel = RuntimeLevel.Run)] - public sealed class NotificationsComponent : UmbracoComponentBase, IUmbracoCoreComponent + public sealed class NotificationsComponent : IComponent { - public override void Compose(Composition composition) + private readonly Notifier _notifier; + private readonly ActionCollection _actions; + + public NotificationsComponent(Notifier notifier, ActionCollection actions) { - base.Compose(composition); - composition.Container.RegisterSingleton(); + _notifier = notifier; + _actions = actions; } - public void Initialize(INotificationService notificationService, Notifier notifier, ActionCollection actions) + public void Initialize() { //Send notifications for the send to publish action - ContentService.SentToPublish += (sender, args) => notifier.Notify(actions.GetAction(), args.Entity); + ContentService.SentToPublish += (sender, args) => _notifier.Notify(_actions.GetAction(), args.Entity); //Send notifications for the published action - ContentService.Published += (sender, args) => notifier.Notify(actions.GetAction(), args.PublishedEntities.ToArray()); + ContentService.Published += (sender, args) => _notifier.Notify(_actions.GetAction(), args.PublishedEntities.ToArray()); //Send notifications for the saved action - ContentService.Sorted += (sender, args) => ContentServiceSorted(notifier, sender, args, actions); + ContentService.Sorted += (sender, args) => ContentServiceSorted(_notifier, sender, args, _actions); //Send notifications for the update and created actions - ContentService.Saved += (sender, args) => ContentServiceSaved(notifier, sender, args, actions); + ContentService.Saved += (sender, args) => ContentServiceSaved(_notifier, sender, args, _actions); //Send notifications for the delete action - ContentService.Deleted += (sender, args) => notifier.Notify(actions.GetAction(), args.DeletedEntities.ToArray()); - + ContentService.Deleted += (sender, args) => _notifier.Notify(_actions.GetAction(), args.DeletedEntities.ToArray()); + //Send notifications for the unpublish action - ContentService.Unpublished += (sender, args) => notifier.Notify(actions.GetAction(), args.PublishedEntities.ToArray()); + ContentService.Unpublished += (sender, args) => _notifier.Notify(_actions.GetAction(), args.PublishedEntities.ToArray()); } + public void Terminate() + { } + private void ContentServiceSorted(Notifier notifier, IContentService sender, Core.Events.SaveEventArgs args, ActionCollection actions) { var parentId = args.SavedEntities.Select(x => x.ParentId).Distinct().ToList(); @@ -54,7 +58,7 @@ namespace Umbraco.Web.Components // in this case there's nothing to report since if the root is sorted we can't report on a fake entity. // this is how it was in v7, we can't report on root changes because you can't subscribe to root changes. - if (parentId[0] <= 0) return; + if (parentId[0] <= 0) return; var parent = sender.GetById(parentId[0]); if (parent == null) return; // this shouldn't happen @@ -191,5 +195,5 @@ namespace Umbraco.Web.Components } } - + } diff --git a/src/Umbraco.Web/Components/NotificationsComposer.cs b/src/Umbraco.Web/Components/NotificationsComposer.cs new file mode 100644 index 0000000000..c945facfc6 --- /dev/null +++ b/src/Umbraco.Web/Components/NotificationsComposer.cs @@ -0,0 +1,17 @@ +using Umbraco.Core; +using Umbraco.Core.Components; +using Umbraco.Core.Composing; + +namespace Umbraco.Web.Components +{ + [RuntimeLevel(MinLevel = RuntimeLevel.Run)] + public sealed class NotificationsComposer : ComponentComposer, ICoreComposer + { + public override void Compose(Composition composition) + { + base.Compose(composition); + + composition.RegisterUnique(); + } + } +} diff --git a/src/Umbraco.Web/Components/PublicAccessComponent.cs b/src/Umbraco.Web/Components/PublicAccessComponent.cs index 5c18eefc66..e08d42d961 100644 --- a/src/Umbraco.Web/Components/PublicAccessComponent.cs +++ b/src/Umbraco.Web/Components/PublicAccessComponent.cs @@ -6,17 +6,16 @@ using Umbraco.Web.Composing; namespace Umbraco.Web.Components { - /// - /// Used to ensure that the public access data file is kept up to date properly - /// - [RuntimeLevel(MinLevel = RuntimeLevel.Run)] - public sealed class PublicAccessComponent : UmbracoComponentBase, IUmbracoCoreComponent + public sealed class PublicAccessComponent : IComponent { public void Initialize() { MemberGroupService.Saved += MemberGroupService_Saved; } + public void Terminate() + { } + static void MemberGroupService_Saved(IMemberGroupService sender, Core.Events.SaveEventArgs e) { foreach (var grp in e.SavedEntities) diff --git a/src/Umbraco.Web/Components/PublicAccessComposer.cs b/src/Umbraco.Web/Components/PublicAccessComposer.cs new file mode 100644 index 0000000000..a4ae98d0e9 --- /dev/null +++ b/src/Umbraco.Web/Components/PublicAccessComposer.cs @@ -0,0 +1,12 @@ +using Umbraco.Core; +using Umbraco.Core.Components; + +namespace Umbraco.Web.Components +{ + /// + /// Used to ensure that the public access data file is kept up to date properly + /// + [RuntimeLevel(MinLevel = RuntimeLevel.Run)] + public sealed class PublicAccessComposer : ComponentComposer, ICoreComposer + { } +} diff --git a/src/Umbraco.Web/LightInjectExtensions.cs b/src/Umbraco.Web/Composing/Composers/ControllersComposer.cs similarity index 65% rename from src/Umbraco.Web/LightInjectExtensions.cs rename to src/Umbraco.Web/Composing/Composers/ControllersComposer.cs index 7ea55587e0..377a6bdb86 100644 --- a/src/Umbraco.Web/LightInjectExtensions.cs +++ b/src/Umbraco.Web/Composing/Composers/ControllersComposer.cs @@ -4,25 +4,25 @@ using System.Linq; using System.Reflection; using System.Web.Http.Controllers; using System.Web.Mvc; -using LightInject; +using Umbraco.Core.Components; using Umbraco.Core.Composing; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; -namespace Umbraco.Web +namespace Umbraco.Web.Composing.Composers { - internal static class LightInjectExtensions + internal static class ControllersComposer { /// /// Registers Umbraco controllers. /// - public static void RegisterUmbracoControllers(this IServiceRegistry container, TypeLoader typeLoader, Assembly umbracoWebAssembly) + public static Composition ComposeUmbracoControllers(this Composition composition, Assembly umbracoWebAssembly) { // notes // // We scan and auto-registers: // - every IController and IHttpController that *we* have in Umbraco.Web - // - PluginController and UmbracoApiController in every assembly + // - PluginController, RenderMvcController and UmbracoApiController in every assembly // // We do NOT scan: // - any IController or IHttpController (anything not PluginController nor UmbracoApiController), outside of Umbraco.Web @@ -53,30 +53,36 @@ namespace Umbraco.Web // Umbraco.Web.Editors.BackOfficeController // scan and register every IController in Umbraco.Web - var umbracoWebControllers = typeLoader.GetTypes(specificAssemblies: new[] { umbracoWebAssembly }); + var umbracoWebControllers = composition.TypeLoader.GetTypes(specificAssemblies: new[] { umbracoWebAssembly }); //foreach (var controller in umbracoWebControllers.Where(x => !typeof(PluginController).IsAssignableFrom(x))) // Current.Logger.Debug(typeof(LightInjectExtensions), "IController NOT PluginController: " + controller.FullName); - container.RegisterControllers(umbracoWebControllers); + composition.RegisterControllers(umbracoWebControllers); // scan and register every PluginController in everything (PluginController is IDiscoverable and IController) - var nonUmbracoWebPluginController = typeLoader.GetTypes().Where(x => x.Assembly != umbracoWebAssembly); - container.RegisterControllers(nonUmbracoWebPluginController); + var nonUmbracoWebPluginController = composition.TypeLoader.GetTypes().Where(x => x.Assembly != umbracoWebAssembly); + composition.RegisterControllers(nonUmbracoWebPluginController); + + // can and register every IRenderMvcController in everything (IRenderMvcController is IDiscoverable) + var renderMvcControllers = composition.TypeLoader.GetTypes().Where(x => x.Assembly != umbracoWebAssembly); + composition.RegisterControllers(renderMvcControllers); // scan and register every IHttpController in Umbraco.Web - var umbracoWebHttpControllers = typeLoader.GetTypes(specificAssemblies: new[] { umbracoWebAssembly }); + var umbracoWebHttpControllers = composition.TypeLoader.GetTypes(specificAssemblies: new[] { umbracoWebAssembly }); //foreach (var controller in umbracoWebControllers.Where(x => !typeof(UmbracoApiController).IsAssignableFrom(x))) // Current.Logger.Debug(typeof(LightInjectExtensions), "IHttpController NOT UmbracoApiController: " + controller.FullName); - container.RegisterControllers(umbracoWebHttpControllers); + composition.RegisterControllers(umbracoWebHttpControllers); // scan and register every UmbracoApiController in everything (UmbracoApiController is IDiscoverable and IHttpController) - var nonUmbracoWebApiControllers = typeLoader.GetTypes().Where(x => x.Assembly != umbracoWebAssembly); - container.RegisterControllers(nonUmbracoWebApiControllers); + var nonUmbracoWebApiControllers = composition.TypeLoader.GetTypes().Where(x => x.Assembly != umbracoWebAssembly); + composition.RegisterControllers(nonUmbracoWebApiControllers); + + return composition; } - private static void RegisterControllers(this IServiceRegistry container, IEnumerable controllerTypes) + private static void RegisterControllers(this Composition composition, IEnumerable controllerTypes) { foreach (var controllerType in controllerTypes) - container.Register(controllerType, new PerRequestLifeTime()); + composition.Register(controllerType, Lifetime.Request); } } } diff --git a/src/Umbraco.Web/Composing/Composers/InstallerComposer.cs b/src/Umbraco.Web/Composing/Composers/InstallerComposer.cs new file mode 100644 index 0000000000..b848425736 --- /dev/null +++ b/src/Umbraco.Web/Composing/Composers/InstallerComposer.cs @@ -0,0 +1,36 @@ +using Umbraco.Core.Components; +using Umbraco.Core.Composing; +using Umbraco.Web.Install; +using Umbraco.Web.Install.InstallSteps; +using Umbraco.Web.Install.Models; + +namespace Umbraco.Web.Composing.Composers +{ + public static class InstallerComposer + { + public static Composition ComposeInstaller(this Composition composition) + { + // register the installer steps + + composition.Register(Lifetime.Scope); + composition.Register(Lifetime.Scope); + composition.Register(Lifetime.Scope); + composition.Register(Lifetime.Scope); + composition.Register(Lifetime.Scope); + composition.Register(Lifetime.Scope); + composition.Register(Lifetime.Scope); + + //TODO: Add these back once we have a compatible starter kit + //container.Register(Lifetime.Scope); + //container.Register(Lifetime.Scope); + //container.Register(Lifetime.Scope); + + composition.Register(Lifetime.Scope); + + composition.Register(); + composition.Register(); + + return composition; + } + } +} diff --git a/src/Umbraco.Web/Composing/Composers/WebMappingProfilesComposer.cs b/src/Umbraco.Web/Composing/Composers/WebMappingProfilesComposer.cs new file mode 100644 index 0000000000..202849a975 --- /dev/null +++ b/src/Umbraco.Web/Composing/Composers/WebMappingProfilesComposer.cs @@ -0,0 +1,51 @@ +using AutoMapper; +using Umbraco.Core; +using Umbraco.Core.Components; +using Umbraco.Core.Composing; +using Umbraco.Core.Models; +using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Models.Mapping; +using Umbraco.Web.Trees; + +namespace Umbraco.Web.Composing.Composers +{ + public static class WebMappingProfilesComposer + { + public static Composition ComposeWebMappingProfiles(this Composition composition) + { + //register the profiles + composition.Register(); + composition.Register(); + composition.Register(); + composition.Register(); + composition.Register(); + composition.Register(); + composition.Register(); + composition.Register(); + composition.Register(); + composition.Register(); + composition.Register(); + composition.Register(); + composition.Register(); + composition.Register(); + composition.Register(); + composition.Register(); + composition.Register(); + composition.Register(); + + //register any resolvers, etc.. that the profiles use + composition.Register(); + composition.Register>(); + composition.Register>(); + composition.Register>(); + composition.Register>(); + composition.Register(); + composition.Register(); + composition.Register(); + composition.Register(); + composition.Register(); + + return composition; + } + } +} diff --git a/src/Umbraco.Web/Composing/CompositionRoots/InstallerCompositionRoot.cs b/src/Umbraco.Web/Composing/CompositionRoots/InstallerCompositionRoot.cs deleted file mode 100644 index b4f7d435f4..0000000000 --- a/src/Umbraco.Web/Composing/CompositionRoots/InstallerCompositionRoot.cs +++ /dev/null @@ -1,42 +0,0 @@ -using LightInject; -using Umbraco.Web.Install; -using Umbraco.Web.Install.Controllers; -using Umbraco.Web.Install.InstallSteps; -using Umbraco.Web.Install.Models; - -namespace Umbraco.Web.Composing.CompositionRoots -{ - /// - /// A composition root for dealing with the installer and installer steps - /// - public sealed class InstallerCompositionRoot : ICompositionRoot - { - public void Compose(IServiceRegistry container) - { - //register the installer steps in order - container.RegisterOrdered(typeof(InstallSetupStep), - new[] - { - typeof(NewInstallStep), - typeof(UpgradeStep), - typeof(FilePermissionsStep), - typeof(ConfigureMachineKey), - typeof(DatabaseConfigureStep), - typeof(DatabaseInstallStep), - typeof(DatabaseUpgradeStep), - - //TODO: Add these back once we have a compatible starter kit - //typeof(StarterKitDownloadStep), - //typeof(StarterKitInstallStep), - //typeof(StarterKitCleanupStep), - - typeof(SetUmbracoVersionStep) - }, type => new PerScopeLifetime()); - - container.Register(); - container.Register(); - - - } - } -} diff --git a/src/Umbraco.Web/Composing/CompositionRoots/WebMappingProfilesCompositionRoot.cs b/src/Umbraco.Web/Composing/CompositionRoots/WebMappingProfilesCompositionRoot.cs deleted file mode 100644 index d7b8054532..0000000000 --- a/src/Umbraco.Web/Composing/CompositionRoots/WebMappingProfilesCompositionRoot.cs +++ /dev/null @@ -1,46 +0,0 @@ -using LightInject; -using Umbraco.Core.Models; -using Umbraco.Web.Models.ContentEditing; -using Umbraco.Web.Models.Mapping; -using Umbraco.Web.Trees; - -namespace Umbraco.Web.Composing.CompositionRoots -{ - public sealed class WebMappingProfilesCompositionRoot : ICompositionRoot - { - public void Compose(IServiceRegistry container) - { - //register the profiles - container.Register(); - container.Register(); - container.Register(); - container.Register(); - container.Register(); - container.Register(); - container.Register(); - container.Register(); - container.Register(); - container.Register(); - container.Register(); - container.Register(); - container.Register(); - container.Register(); - container.Register(); - container.Register(); - container.Register(); - container.Register(); - - //register any resolvers, etc.. that the profiles use - container.Register(); - container.Register>(); - container.Register>(); - container.Register>(); - container.Register>(); - container.Register(); - container.Register(); - container.Register(); - container.Register(); - container.Register(); - } - } -} diff --git a/src/Umbraco.Web/Composing/Current.cs b/src/Umbraco.Web/Composing/Current.cs index 839254f03c..ffe07393e6 100644 --- a/src/Umbraco.Web/Composing/Current.cs +++ b/src/Umbraco.Web/Composing/Current.cs @@ -1,18 +1,16 @@ using System; using System.Threading; using System.Web; -using LightInject; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Dictionary; using Umbraco.Core.Events; using Umbraco.Core.IO; using Umbraco.Core.Logging; -using Umbraco.Core.Macros; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Persistence; using Umbraco.Core.Composing; -using Umbraco.Core.Migrations; +using Umbraco.Core.Configuration; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; using Umbraco.Core.Services; @@ -23,7 +21,6 @@ using Umbraco.Web.Actions; using Umbraco.Web.Cache; using Umbraco.Web.Editors; using Umbraco.Web.HealthCheck; -using Umbraco.Web.Media; using Umbraco.Web.Mvc; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; @@ -57,10 +54,10 @@ namespace Umbraco.Web.Composing } /// - /// Gets the DI container. + /// Gets the factory. /// - internal static IServiceContainer Container - => CoreCurrent.Container; + public static IFactory Factory + => CoreCurrent.Factory; #region Temp & Special @@ -71,7 +68,7 @@ namespace Umbraco.Web.Composing get { if (_umbracoContextAccessor != null) return _umbracoContextAccessor; - return _umbracoContextAccessor = Container.GetInstance(); + return _umbracoContextAccessor = Factory.GetInstance(); } set => _umbracoContextAccessor = value; // for tests } @@ -96,46 +93,46 @@ namespace Umbraco.Web.Composing => UmbracoContextAccessor.UmbracoContext; public static DistributedCache DistributedCache - => Container.GetInstance(); + => Factory.GetInstance(); public static IPublishedSnapshot PublishedSnapshot - => Container.GetInstance().PublishedSnapshot; + => Factory.GetInstance().PublishedSnapshot; public static EventMessages EventMessages - => Container.GetInstance().GetOrDefault(); + => Factory.GetInstance().GetOrDefault(); public static UrlProviderCollection UrlProviders - => Container.GetInstance(); + => Factory.GetInstance(); public static HealthCheckCollectionBuilder HealthCheckCollectionBuilder - => Container.GetInstance(); + => Factory.GetInstance(); internal static ActionCollectionBuilder ActionCollectionBuilder - => Container.GetInstance(); + => Factory.GetInstance(); public static ActionCollection Actions - => Container.GetInstance(); + => Factory.GetInstance(); public static ContentFinderCollection ContentFinders - => Container.GetInstance(); + => Factory.GetInstance(); public static IContentLastChanceFinder LastChanceContentFinder - => Container.GetInstance(); + => Factory.GetInstance(); internal static EditorValidatorCollection EditorValidators - => Container.GetInstance(); - + => Factory.GetInstance(); + internal static UmbracoApiControllerTypeCollection UmbracoApiControllerTypes - => Container.GetInstance(); + => Factory.GetInstance(); internal static SurfaceControllerTypeCollection SurfaceControllerTypes - => Container.GetInstance(); + => Factory.GetInstance(); public static FilteredControllerFactoryCollection FilteredControllerFactories - => Container.GetInstance(); + => Factory.GetInstance(); internal static IPublishedSnapshotService PublishedSnapshotService - => Container.GetInstance(); + => Factory.GetInstance(); #endregion @@ -204,6 +201,8 @@ namespace Umbraco.Web.Composing public static TypeLoader TypeLoader => CoreCurrent.TypeLoader; + public static Configs Configs => CoreCurrent.Configs; + public static UrlSegmentProviderCollection UrlSegmentProviders => CoreCurrent.UrlSegmentProviders; public static CacheRefresherCollection CacheRefreshers => CoreCurrent.CacheRefreshers; @@ -234,7 +233,7 @@ namespace Umbraco.Web.Composing public static IProfiler Profiler => CoreCurrent.Profiler; - public static ProfilingLogger ProfilingLogger => CoreCurrent.ProfilingLogger; + public static IProfilingLogger ProfilingLogger => CoreCurrent.ProfilingLogger; public static CacheHelper ApplicationCache => CoreCurrent.ApplicationCache; @@ -242,7 +241,7 @@ namespace Umbraco.Web.Composing public static IScopeProvider ScopeProvider => CoreCurrent.ScopeProvider; - public static FileSystems FileSystems => CoreCurrent.FileSystems; + public static IFileSystems FileSystems => CoreCurrent.FileSystems; public static ISqlContext SqlContext=> CoreCurrent.SqlContext; diff --git a/src/Umbraco.Web/Composing/LightInject/LightInjectContainer.cs b/src/Umbraco.Web/Composing/LightInject/LightInjectContainer.cs new file mode 100644 index 0000000000..103cd32912 --- /dev/null +++ b/src/Umbraco.Web/Composing/LightInject/LightInjectContainer.cs @@ -0,0 +1,38 @@ +using System; +using System.Web.Http; +using LightInject; +using Umbraco.Core.Composing.LightInject; + +namespace Umbraco.Web.Composing.LightInject +{ + /// + /// Implements DI with LightInject. + /// + public class LightInjectContainer : Core.Composing.LightInject.LightInjectContainer + { + /// + /// Initializes a new instance of the with a LightInject container. + /// + protected LightInjectContainer(ServiceContainer container) + : base(container) + { } + + /// + /// Creates a new instance of the class. + /// + public new static LightInjectContainer Create() + => new LightInjectContainer(CreateServiceContainer()); + + /// + public override void ConfigureForWeb() + { + // IoC setup for LightInject for MVC/WebApi + // see comments on MixedLightInjectScopeManagerProvider for explanations of what we are doing here + if (!(Container.ScopeManagerProvider is MixedLightInjectScopeManagerProvider smp)) + throw new Exception("Container.ScopeManagerProvider is not MixedLightInjectScopeManagerProvider."); + Container.EnableMvc(); // does container.EnablePerWebRequestScope() + Container.ScopeManagerProvider = smp; // reverts - we will do it last (in WebRuntime) + Container.EnableWebApi(GlobalConfiguration.Configuration); + } + } +} diff --git a/src/Umbraco.Web/Composing/ModuleInjector.cs b/src/Umbraco.Web/Composing/ModuleInjector.cs new file mode 100644 index 0000000000..01930d55fd --- /dev/null +++ b/src/Umbraco.Web/Composing/ModuleInjector.cs @@ -0,0 +1,55 @@ +using System; +using System.Web; +using Umbraco.Core; +using Umbraco.Core.Composing; +using Umbraco.Core.Exceptions; + +namespace Umbraco.Web.Composing +{ + /// + /// Provides a base class for module injectors. + /// + /// The type of the injected module. + public abstract class ModuleInjector : IHttpModule + where TModule : IHttpModule + { + protected TModule Module { get; private set; } + + /// + public void Init(HttpApplication context) + { + try + { + // using the service locator here - no other way, really + Module = Current.Factory.GetInstance(); + } + catch + { + // if GetInstance fails, it may be because of a boot error, in + // which case that is the error we actually want to report + IRuntimeState runtimeState = null; + + try + { + runtimeState = Current.Factory.GetInstance(); + } + catch { /* don't make it worse */ } + + if (runtimeState?.BootFailedException != null) + BootFailedException.Rethrow(runtimeState.BootFailedException); + + // else... throw what we have + throw; + } + + // initialize + Module.Init(context); + } + + /// + public void Dispose() + { + Module?.Dispose(); + } + } +} diff --git a/src/Umbraco.Web/CompositionExtensions.cs b/src/Umbraco.Web/CompositionExtensions.cs index 3031410f19..f72c876a61 100644 --- a/src/Umbraco.Web/CompositionExtensions.cs +++ b/src/Umbraco.Web/CompositionExtensions.cs @@ -1,17 +1,14 @@ using System; -using LightInject; using Umbraco.Core.Composing; using Current = Umbraco.Web.Composing.Current; -using Umbraco.Core.Macros; using Umbraco.Web.Actions; using Umbraco.Web.Editors; using Umbraco.Web.HealthCheck; -using Umbraco.Web.Media; using Umbraco.Web.Mvc; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; using Umbraco.Web.ContentApps; -using Umbraco.Web.Features; +using Umbraco.Web.Tour; // the namespace here is intentional - although defined in Umbraco.Web assembly, // this class should be visible when using Umbraco.Core.Components, alongside @@ -33,7 +30,7 @@ namespace Umbraco.Core.Components /// The composition. /// internal static ActionCollectionBuilder Actions(this Composition composition) - => composition.Container.GetInstance(); + => composition.WithCollectionBuilder(); /// /// Gets the content apps collection builder. @@ -41,7 +38,7 @@ namespace Umbraco.Core.Components /// The composition. /// public static ContentAppFactoryCollectionBuilder ContentApps(this Composition composition) - => composition.Container.GetInstance(); + => composition.WithCollectionBuilder(); /// /// Gets the content finders collection builder. @@ -49,7 +46,7 @@ namespace Umbraco.Core.Components /// The composition. /// public static ContentFinderCollectionBuilder ContentFinders(this Composition composition) - => composition.Container.GetInstance(); + => composition.WithCollectionBuilder(); /// /// Gets the editor validators collection builder. @@ -57,10 +54,7 @@ namespace Umbraco.Core.Components /// The composition. /// internal static EditorValidatorCollectionBuilder EditorValidators(this Composition composition) - => composition.Container.GetInstance(); - - public static UmbracoFeatures Features(this Composition composition) - => composition.Container.GetInstance(); + => composition.WithCollectionBuilder(); /// /// Gets the filtered controller factories collection builder. @@ -68,25 +62,31 @@ namespace Umbraco.Core.Components /// The composition. /// public static FilteredControllerFactoryCollectionBuilder FilderedControllerFactory(this Composition composition) - => composition.Container.GetInstance(); + => composition.WithCollectionBuilder(); /// /// Gets the health checks collection builder. /// /// The composition. public static HealthCheckCollectionBuilder HealthChecks(this Composition composition) - => composition.Container.GetInstance(); + => composition.WithCollectionBuilder(); + + /// + /// Gets the TourFilters collection builder. + /// + public static TourFilterCollectionBuilder TourFilters(this Composition composition) + => composition.WithCollectionBuilder(); /// /// Gets the url providers collection builder. /// /// The composition. internal static UrlProviderCollectionBuilder UrlProviders(this Composition composition) - => composition.Container.GetInstance(); + => composition.WithCollectionBuilder(); #endregion - #region Singletons + #region Uniques /// /// Sets the content last chance finder. @@ -96,7 +96,7 @@ namespace Umbraco.Core.Components public static void SetContentLastChanceFinder(this Composition composition) where T : IContentLastChanceFinder { - composition.Container.RegisterSingleton(); + composition.RegisterUnique(); } /// @@ -104,9 +104,9 @@ namespace Umbraco.Core.Components /// /// The composition. /// A function creating a last chance finder. - public static void SetContentLastChanceFinder(this Composition composition, Func factory) + public static void SetContentLastChanceFinder(this Composition composition, Func factory) { - composition.Container.RegisterSingleton(factory); + composition.RegisterUnique(factory); } /// @@ -116,17 +116,7 @@ namespace Umbraco.Core.Components /// A last chance finder. public static void SetContentLastChanceFinder(this Composition composition, IContentLastChanceFinder finder) { - composition.Container.RegisterSingleton(_ => finder); - } - - /// - /// Sets the type of the default rendering controller. - /// - /// The type of the default rendering controller. - /// The composition. - public static void SetDefaultRenderMvcControllerType(this Composition composition) - { - Current.DefaultRenderMvcControllerType = typeof(T); + composition.RegisterUnique(_ => finder); } /// @@ -137,7 +127,7 @@ namespace Umbraco.Core.Components public static void SetPublishedSnapshotService(this Composition composition) where T : IPublishedSnapshotService { - composition.Container.RegisterSingleton(); + composition.RegisterUnique(); } /// @@ -145,9 +135,9 @@ namespace Umbraco.Core.Components /// /// The composition. /// A function creating a published snapshot service. - public static void SetPublishedSnapshotService(this Composition composition, Func factory) + public static void SetPublishedSnapshotService(this Composition composition, Func factory) { - composition.Container.RegisterSingleton(factory); + composition.RegisterUnique(factory); } /// @@ -157,7 +147,7 @@ namespace Umbraco.Core.Components /// A published snapshot service. public static void SetPublishedSnapshotService(this Composition composition, IPublishedSnapshotService service) { - composition.Container.RegisterSingleton(_ => service); + composition.RegisterUnique(_ => service); } /// @@ -168,7 +158,7 @@ namespace Umbraco.Core.Components public static void SetSiteDomainHelper(this Composition composition) where T : ISiteDomainHelper { - composition.Container.RegisterSingleton(); + composition.RegisterUnique(); } /// @@ -176,9 +166,9 @@ namespace Umbraco.Core.Components /// /// The composition. /// A function creating a helper. - public static void SetSiteDomainHelper(this Composition composition, Func factory) + public static void SetSiteDomainHelper(this Composition composition, Func factory) { - composition.Container.RegisterSingleton(factory); + composition.RegisterUnique(factory); } /// @@ -188,7 +178,32 @@ namespace Umbraco.Core.Components /// A helper. public static void SetSiteDomainHelper(this Composition composition, ISiteDomainHelper helper) { - composition.Container.RegisterSingleton(_ => helper); + composition.RegisterUnique(_ => helper); + } + + /// + /// Sets the default controller for rendering template views. + /// + /// The type of the controller. + /// The composition. + /// The controller type is registered to the container by the composition. + public static void SetDefaultRenderMvcController(this Composition composition) + => composition.SetDefaultRenderMvcController(typeof(TController)); + + /// + /// Sets the default controller for rendering template views. + /// + /// The composition. + /// The type of the controller. + /// The controller type is registered to the container by the composition. + public static void SetDefaultRenderMvcController(this Composition composition, Type controllerType) + { + composition.OnCreatingFactory["Umbraco.Core.DefaultRenderMvcController"] = () => + { + // no need to register: all IRenderMvcController are registered + //composition.Register(controllerType, Lifetime.Request); + Current.DefaultRenderMvcControllerType = controllerType; + }; } #endregion diff --git a/src/Umbraco.Web/ContentApps/ContentAppFactoryCollectionBuilder.cs b/src/Umbraco.Web/ContentApps/ContentAppFactoryCollectionBuilder.cs index 8dd3b6bc98..2f35e77a7c 100644 --- a/src/Umbraco.Web/ContentApps/ContentAppFactoryCollectionBuilder.cs +++ b/src/Umbraco.Web/ContentApps/ContentAppFactoryCollectionBuilder.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Linq; -using LightInject; using Umbraco.Core.Composing; using Umbraco.Core.Logging; using Umbraco.Core.Manifest; @@ -10,29 +9,25 @@ namespace Umbraco.Web.ContentApps { public class ContentAppFactoryCollectionBuilder : OrderedCollectionBuilderBase { - public ContentAppFactoryCollectionBuilder(IServiceContainer container) - : base(container) - { } - protected override ContentAppFactoryCollectionBuilder This => this; // need to inject dependencies in the collection, so override creation - public override ContentAppFactoryCollection CreateCollection() + public override ContentAppFactoryCollection CreateCollection(IFactory factory) { // get the logger just-in-time - see note below for manifest parser - var logger = Container.GetInstance(); + var logger = factory.GetInstance(); - return new ContentAppFactoryCollection(CreateItems(), logger); + return new ContentAppFactoryCollection(CreateItems(factory), logger); } - protected override IEnumerable CreateItems(params object[] args) + protected override IEnumerable CreateItems(IFactory factory) { // get the manifest parser just-in-time - injecting it in the ctor would mean that // simply getting the builder in order to configure the collection, would require // its dependencies too, and that can create cycles or other oddities - var manifestParser = Container.GetInstance(); + var manifestParser = factory.GetInstance(); - return base.CreateItems(args).Concat(manifestParser.Manifest.ContentApps.Select(x => new ManifestContentAppFactory(x))); + return base.CreateItems(factory).Concat(manifestParser.Manifest.ContentApps.Select(x => new ManifestContentAppFactory(x))); } } } diff --git a/src/Umbraco.Web/Controllers/UmbLoginController.cs b/src/Umbraco.Web/Controllers/UmbLoginController.cs index b672cc2d46..fc6613200c 100644 --- a/src/Umbraco.Web/Controllers/UmbLoginController.cs +++ b/src/Umbraco.Web/Controllers/UmbLoginController.cs @@ -2,11 +2,25 @@ using Umbraco.Web.Models; using Umbraco.Web.Mvc; using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence; +using Umbraco.Core.Services; namespace Umbraco.Web.Controllers { public class UmbLoginController : SurfaceController { + // fixme - delete? + public UmbLoginController() + { + } + + public UmbLoginController(UmbracoContext umbracoContext, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, CacheHelper applicationCache, ILogger logger, IProfilingLogger profilingLogger) + : base(umbracoContext, databaseFactory, services, applicationCache, logger, profilingLogger) + { + } + [HttpPost] [ValidateAntiForgeryToken] public ActionResult HandleLogin([Bind(Prefix = "loginModel")]LoginModel model) diff --git a/src/Umbraco.Web/Controllers/UmbLoginStatusController.cs b/src/Umbraco.Web/Controllers/UmbLoginStatusController.cs index bf4a550801..83f4ae04a1 100644 --- a/src/Umbraco.Web/Controllers/UmbLoginStatusController.cs +++ b/src/Umbraco.Web/Controllers/UmbLoginStatusController.cs @@ -3,12 +3,25 @@ using System.Web.Security; using Umbraco.Web.Models; using Umbraco.Web.Mvc; using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence; +using Umbraco.Core.Services; namespace Umbraco.Web.Controllers { [MemberAuthorize] public class UmbLoginStatusController : SurfaceController { + // fixme - delete? + public UmbLoginStatusController() + { + } + + public UmbLoginStatusController(UmbracoContext umbracoContext, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, CacheHelper applicationCache, ILogger logger, IProfilingLogger profilingLogger) : base(umbracoContext, databaseFactory, services, applicationCache, logger, profilingLogger) + { + } + [HttpPost] [ValidateAntiForgeryToken] public ActionResult HandleLogout([Bind(Prefix = "logoutModel")]PostRedirectModel model) diff --git a/src/Umbraco.Web/Controllers/UmbProfileController.cs b/src/Umbraco.Web/Controllers/UmbProfileController.cs index 25064d2074..7ee8385edc 100644 --- a/src/Umbraco.Web/Controllers/UmbProfileController.cs +++ b/src/Umbraco.Web/Controllers/UmbProfileController.cs @@ -4,12 +4,25 @@ using Umbraco.Web.Models; using Umbraco.Web.Mvc; using Umbraco.Core.Security; using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence; +using Umbraco.Core.Services; namespace Umbraco.Web.Controllers { [MemberAuthorize] public class UmbProfileController : SurfaceController { + // fixme - delete? + public UmbProfileController() + { + } + + public UmbProfileController(UmbracoContext umbracoContext, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, CacheHelper applicationCache, ILogger logger, IProfilingLogger profilingLogger) : base(umbracoContext, databaseFactory, services, applicationCache, logger, profilingLogger) + { + } + [HttpPost] [ValidateAntiForgeryToken] public ActionResult HandleUpdateProfile([Bind(Prefix = "profileModel")] ProfileModel model) diff --git a/src/Umbraco.Web/Controllers/UmbRegisterController.cs b/src/Umbraco.Web/Controllers/UmbRegisterController.cs index 50b7f7588e..5a5024dad9 100644 --- a/src/Umbraco.Web/Controllers/UmbRegisterController.cs +++ b/src/Umbraco.Web/Controllers/UmbRegisterController.cs @@ -2,6 +2,10 @@ using System.Web.Mvc; using System.Web.Security; using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence; +using Umbraco.Core.Services; using Umbraco.Web.Models; using Umbraco.Web.Mvc; @@ -9,6 +13,15 @@ namespace Umbraco.Web.Controllers { public class UmbRegisterController : SurfaceController { + // fixme - delete? + public UmbRegisterController() + { + } + + public UmbRegisterController(UmbracoContext umbracoContext, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, CacheHelper applicationCache, ILogger logger, IProfilingLogger profilingLogger) : base(umbracoContext, databaseFactory, services, applicationCache, logger, profilingLogger) + { + } + [HttpPost] [ValidateAntiForgeryToken] public ActionResult HandleRegisterMember([Bind(Prefix = "registerModel")]RegisterModel model) diff --git a/src/Umbraco.Web/Editors/AuthenticationController.cs b/src/Umbraco.Web/Editors/AuthenticationController.cs index 4e56685300..231ec9ba67 100644 --- a/src/Umbraco.Web/Editors/AuthenticationController.cs +++ b/src/Umbraco.Web/Editors/AuthenticationController.cs @@ -11,8 +11,8 @@ using System.Web.Mvc; using AutoMapper; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.Owin; -using Microsoft.Owin; using Umbraco.Core; +using Umbraco.Core.Cache; using Umbraco.Core.Models; using Umbraco.Core.Models.Identity; using Umbraco.Core.Security; @@ -25,6 +25,7 @@ using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; +using Umbraco.Core.Persistence; using Umbraco.Web.Composing; using IUser = Umbraco.Core.Models.Membership.IUser; @@ -39,18 +40,27 @@ namespace Umbraco.Web.Editors [IsBackOffice] public class AuthenticationController : UmbracoApiController { - - //fixme inject these private BackOfficeUserManager _userManager; private BackOfficeSignInManager _signInManager; - protected BackOfficeUserManager UserManager - { - get { return _userManager ?? (_userManager = TryGetOwinContext().Result.GetBackOfficeUserManager()); } - } - protected BackOfficeSignInManager SignInManager - { - get { return _signInManager ?? (_signInManager = TryGetOwinContext().Result.GetBackOfficeSignInManager()); } - } + + /// + /// Initializes a new instance of the new class with auto dependencies. + /// + public AuthenticationController() + { } + + /// + /// Initializes a new instance of the class with all its dependencies. + /// + public AuthenticationController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, CacheHelper applicationCache, IProfilingLogger logger, IRuntimeState runtimeState) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, applicationCache, logger, runtimeState) + { } + + protected BackOfficeUserManager UserManager => _userManager + ?? (_userManager = TryGetOwinContext().Result.GetBackOfficeUserManager()); + + protected BackOfficeSignInManager SignInManager => _signInManager + ?? (_signInManager = TryGetOwinContext().Result.GetBackOfficeSignInManager()); /// /// Returns the configuration for the backoffice user membership provider - used to configure the change password dialog @@ -290,7 +300,7 @@ namespace Umbraco.Web.Editors { // If this feature is switched off in configuration the UI will be amended to not make the request to reset password available. // So this is just a server-side secondary check. - if (UmbracoConfig.For.UmbracoSettings().Security.AllowPasswordReset == false) + if (Current.Configs.Settings().Security.AllowPasswordReset == false) { throw new HttpResponseException(HttpStatusCode.BadRequest); } @@ -437,7 +447,7 @@ namespace Umbraco.Web.Editors if (LastLoginDate == default && IsApproved == false && InvitedDate != null) return UserState.Invited; */ - if (identityUser != null && !identityUser.IsApproved) + if (identityUser != null && !identityUser.IsApproved) { var user = Services.UserService.GetByUsername(identityUser.UserName); //also check InvitedDate and never logged in, otherwise this would allow a disabled user to reactivate their account with a forgot password diff --git a/src/Umbraco.Web/Editors/BackOfficeController.cs b/src/Umbraco.Web/Editors/BackOfficeController.cs index 4ed9251929..6294a0377f 100644 --- a/src/Umbraco.Web/Editors/BackOfficeController.cs +++ b/src/Umbraco.Web/Editors/BackOfficeController.cs @@ -49,7 +49,8 @@ namespace Umbraco.Web.Editors private const string TokenPasswordResetCode = "PasswordResetCode"; private static readonly string[] TempDataTokenNames = { TokenExternalSignInError, TokenPasswordResetCode }; - public BackOfficeController(ManifestParser manifestParser, UmbracoFeatures features, IRuntimeState runtimeState) + public BackOfficeController(ManifestParser manifestParser, UmbracoFeatures features, IGlobalSettings globalSettings, UmbracoContext umbracoContext, ServiceContext services, CacheHelper applicationCache, ILogger logger, IProfilingLogger profilingLogger, IRuntimeState runtimeState) + : base(globalSettings, umbracoContext, services, applicationCache, logger, profilingLogger) { _manifestParser = manifestParser; _features = features; @@ -85,7 +86,7 @@ namespace Umbraco.Web.Editors Core.Constants.Security.BackOfficeAuthenticationType, Core.Constants.Security.BackOfficeExternalAuthenticationType); } - + if (invite == null) { Logger.Warn("VerifyUser endpoint reached with invalid token: NULL"); @@ -191,7 +192,7 @@ namespace Umbraco.Web.Editors { var initJs = new JsInitialization(_manifestParser); var initCss = new CssInitialization(_manifestParser); - + var files = initJs.OptimizeBackOfficeScriptFiles(HttpContext, JsInitialization.GetDefaultInitialization()); var result = JsInitialization.GetJavascriptInitialization(HttpContext, files, "umbraco"); result += initCss.GetStylesheetInitialization(HttpContext); @@ -232,13 +233,7 @@ namespace Umbraco.Web.Editors [HttpGet] public JsonNetResult GetGridConfig() { - var gridConfig = UmbracoConfig.For.GridConfig( - Logger, - ApplicationCache.RuntimeCache, - new DirectoryInfo(Server.MapPath(SystemDirectories.AppPlugins)), - new DirectoryInfo(Server.MapPath(SystemDirectories.Config)), - HttpContext.IsDebuggingEnabled); - + var gridConfig = Current.Configs.Grids(); return new JsonNetResult { Data = gridConfig.EditorsConfig.Editors, Formatting = Formatting.Indented }; } @@ -378,9 +373,9 @@ namespace Umbraco.Web.Editors ExternalSignInAutoLinkOptions autoLinkOptions = null; //Here we can check if the provider associated with the request has been configured to allow - // new users (auto-linked external accounts). This would never be used with public providers such as + // new users (auto-linked external accounts). This would never be used with public providers such as // Google, unless you for some reason wanted anybody to be able to access the backend if they have a Google account - // .... not likely! + // .... not likely! var authType = OwinContext.Authentication.GetExternalAuthenticationTypes().FirstOrDefault(x => x.AuthenticationType == loginInfo.Login.LoginProvider); if (authType == null) { @@ -471,7 +466,7 @@ namespace Umbraco.Web.Editors { autoLinkUser.AddRole(userGroup.Alias); } - + //call the callback if one is assigned if (autoLinkOptions.OnAutoLinking != null) { @@ -510,7 +505,7 @@ namespace Umbraco.Web.Editors } return true; } - + private ActionResult RedirectToLocal(string returnUrl) { if (Url.IsLocalUrl(returnUrl)) diff --git a/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs b/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs index 5bc01fa534..71ceb6f4b4 100644 --- a/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs +++ b/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs @@ -314,25 +314,25 @@ namespace Umbraco.Web.Editors {"appPluginsPath", IOHelper.ResolveUrl(SystemDirectories.AppPlugins).TrimEnd('/')}, { "imageFileTypes", - string.Join(",", UmbracoConfig.For.UmbracoSettings().Content.ImageFileTypes) + string.Join(",", Current.Configs.Settings().Content.ImageFileTypes) }, { "disallowedUploadFiles", - string.Join(",", UmbracoConfig.For.UmbracoSettings().Content.DisallowedUploadFiles) + string.Join(",", Current.Configs.Settings().Content.DisallowedUploadFiles) }, { "allowedUploadFiles", - string.Join(",", UmbracoConfig.For.UmbracoSettings().Content.AllowedUploadFiles) + string.Join(",", Current.Configs.Settings().Content.AllowedUploadFiles) }, { "maxFileSize", GetMaxRequestLength() }, - {"keepUserLoggedIn", UmbracoConfig.For.UmbracoSettings().Security.KeepUserLoggedIn}, - {"usernameIsEmail", UmbracoConfig.For.UmbracoSettings().Security.UsernameIsEmail}, + {"keepUserLoggedIn", Current.Configs.Settings().Security.KeepUserLoggedIn}, + {"usernameIsEmail", Current.Configs.Settings().Security.UsernameIsEmail}, {"cssPath", IOHelper.ResolveUrl(SystemDirectories.Css).TrimEnd('/')}, - {"allowPasswordReset", UmbracoConfig.For.UmbracoSettings().Security.AllowPasswordReset}, - {"loginBackgroundImage", UmbracoConfig.For.UmbracoSettings().Content.LoginBackgroundImage}, + {"allowPasswordReset", Current.Configs.Settings().Security.AllowPasswordReset}, + {"loginBackgroundImage", Current.Configs.Settings().Content.LoginBackgroundImage}, {"showUserInvite", EmailSender.CanSendRequiredEmail}, {"canSendRequiredEmail", EmailSender.CanSendRequiredEmail}, } diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index d0699c4ad4..ecd97c334d 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -515,13 +515,14 @@ namespace Umbraco.Web.Editors [HttpPost] public SimpleNotificationModel CreateBlueprintFromContent([FromUri]int contentId, [FromUri]string name) { - if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value cannot be null or whitespace.", "name"); + if (string.IsNullOrWhiteSpace(name)) + throw new ArgumentException("Value cannot be null or whitespace.", nameof(name)); var content = Services.ContentService.GetById(contentId); if (content == null) throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound)); - EnsureUniqueName(name, content, "name"); + EnsureUniqueName(name, content, nameof(name)); var blueprint = Services.ContentService.CreateContentFromBlueprint(content, name, Security.GetUserId().ResultOr(0)); @@ -612,7 +613,7 @@ namespace Umbraco.Web.Editors var msKey = $"Variants[{variantCount}].Name"; if (ModelState.ContainsKey(msKey)) { - if (!variant.Save) + if (!variant.Save || IsCreatingAction(contentItem.Action)) ModelState.Remove(msKey); else variantNameErrors.Add(variant.Culture); @@ -1094,6 +1095,7 @@ namespace Umbraco.Web.Editors //TODO: Deal with multiple cancelations wasCancelled = publishStatus.Any(x => x.Result == PublishResultType.FailedPublishCancelledByEvent); successfulCultures = Array.Empty(); + return publishStatus; } //All variants in this collection should have a culture if we get here! but we'll double check and filter here @@ -1778,22 +1780,25 @@ namespace Umbraco.Web.Editors variantIndex++; } - //only set the template if it didn't change - var templateChanged = (contentSave.PersistedContent.Template == null && contentSave.TemplateAlias.IsNullOrWhiteSpace() == false) - || (contentSave.PersistedContent.Template != null && contentSave.PersistedContent.Template.Alias != contentSave.TemplateAlias) - || (contentSave.PersistedContent.Template != null && contentSave.TemplateAlias.IsNullOrWhiteSpace()); - if (templateChanged) + // handle template + if (string.IsNullOrWhiteSpace(contentSave.TemplateAlias)) // cleared: clear if not already null + { + if (contentSave.PersistedContent.TemplateId != null) + { + contentSave.PersistedContent.TemplateId = null; + } + } + else // set: update if different { var template = Services.FileService.GetTemplate(contentSave.TemplateAlias); - if (template == null && contentSave.TemplateAlias.IsNullOrWhiteSpace() == false) + if (template == null) { //ModelState.AddModelError("Template", "No template exists with the specified alias: " + contentItem.TemplateAlias); Logger.Warn("No template exists with the specified alias: {TemplateAlias}", contentSave.TemplateAlias); } - else + else if (template.Id != contentSave.PersistedContent.TemplateId) { - //NOTE: this could be null if there was a template and the posted template is null, this should remove the assigned template - contentSave.PersistedContent.Template = template; + contentSave.PersistedContent.TemplateId = template.Id; } } } diff --git a/src/Umbraco.Web/Editors/DashboardController.cs b/src/Umbraco.Web/Editors/DashboardController.cs index d8cbc34938..8960f110c3 100644 --- a/src/Umbraco.Web/Editors/DashboardController.cs +++ b/src/Umbraco.Web/Editors/DashboardController.cs @@ -1,127 +1,140 @@ -using System.Collections.Generic; -using Umbraco.Core; -using Umbraco.Core.Configuration; -using Umbraco.Web.Models.ContentEditing; -using Umbraco.Web.Mvc; -using Newtonsoft.Json.Linq; -using System.Threading.Tasks; -using System.Net.Http; -using System.Web.Http; -using System; -using System.Net; -using System.Text; -using Umbraco.Core.Cache; -using Umbraco.Web.WebApi; -using Umbraco.Web.WebApi.Filters; -using Umbraco.Core.Logging; - -namespace Umbraco.Web.Editors -{ - //we need to fire up the controller like this to enable loading of remote css directly from this controller - [PluginController("UmbracoApi")] - [ValidationFilter] - [AngularJsonOnlyConfiguration] - [IsBackOffice] - [WebApi.UmbracoAuthorize] - public class DashboardController : UmbracoApiController - { - private readonly Dashboards _dashboards; - - public DashboardController(Dashboards dashboards) - { - _dashboards = dashboards; - } - - //we have just one instance of HttpClient shared for the entire application - private static readonly HttpClient HttpClient = new HttpClient(); - //we have baseurl as a param to make previewing easier, so we can test with a dev domain from client side - [ValidateAngularAntiForgeryToken] - public async Task GetRemoteDashboardContent(string section, string baseUrl = "https://dashboard.umbraco.org/") - { - var user = Security.CurrentUser; - var allowedSections = string.Join(",", user.AllowedSections); - var language = user.Language; - var version = UmbracoVersion.SemanticVersion.ToSemanticString(); - - var url = string.Format(baseUrl + "{0}?section={0}&allowed={1}&lang={2}&version={3}", section, allowedSections, language, version); - var key = "umbraco-dynamic-dashboard-" + language + allowedSections.Replace(",", "-") + section; - - var content = ApplicationCache.RuntimeCache.GetCacheItem(key); - var result = new JObject(); - if (content != null) - { - result = content; - } - else - { - //content is null, go get it - try - { - //fetch dashboard json and parse to JObject - var json = await HttpClient.GetStringAsync(url); - content = JObject.Parse(json); - result = content; - - ApplicationCache.RuntimeCache.InsertCacheItem(key, () => result, new TimeSpan(0, 30, 0)); - } - catch (HttpRequestException ex) - { - Logger.Error(ex.InnerException ?? ex, "Error getting dashboard content from '{Url}'", url); - - //it's still new JObject() - we return it like this to avoid error codes which triggers UI warnings - ApplicationCache.RuntimeCache.InsertCacheItem(key, () => result, new TimeSpan(0, 5, 0)); - } - } - - return result; - } - - public async Task GetRemoteDashboardCss(string section, string baseUrl = "https://dashboard.umbraco.org/") - { - var url = string.Format(baseUrl + "css/dashboard.css?section={0}", section); - var key = "umbraco-dynamic-dashboard-css-" + section; - - var content = ApplicationCache.RuntimeCache.GetCacheItem(key); - var result = string.Empty; - - if (content != null) - { - result = content; - } - else - { - //content is null, go get it - try - { - //fetch remote css - content = await HttpClient.GetStringAsync(url); - - //can't use content directly, modified closure problem - result = content; - - //save server content for 30 mins - ApplicationCache.RuntimeCache.InsertCacheItem(key, () => result, new TimeSpan(0, 30, 0)); - } - catch (HttpRequestException ex) - { - Logger.Error(ex.InnerException ?? ex, "Error getting dashboard CSS from '{Url}'", url); - - //it's still string.Empty - we return it like this to avoid error codes which triggers UI warnings - ApplicationCache.RuntimeCache.InsertCacheItem(key, () => result, new TimeSpan(0, 5, 0)); - } - } - - return new HttpResponseMessage(HttpStatusCode.OK) - { - Content = new StringContent(result, Encoding.UTF8, "text/css") - }; - } - - [ValidateAngularAntiForgeryToken] - [OutgoingEditorModelEvent] - public IEnumerable> GetDashboard(string section) - { - return _dashboards.GetDashboards(section, Security.CurrentUser); - } - } -} +using System.Collections.Generic; +using Umbraco.Core; +using Umbraco.Core.Configuration; +using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Mvc; +using Newtonsoft.Json.Linq; +using System.Threading.Tasks; +using System.Net.Http; +using System.Web.Http; +using System; +using System.Net; +using System.Text; +using Umbraco.Core.Cache; +using Umbraco.Web.WebApi; +using Umbraco.Web.WebApi.Filters; +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence; +using Umbraco.Core.Services; + +namespace Umbraco.Web.Editors +{ + //we need to fire up the controller like this to enable loading of remote css directly from this controller + [PluginController("UmbracoApi")] + [ValidationFilter] + [AngularJsonOnlyConfiguration] + [IsBackOffice] + [WebApi.UmbracoAuthorize] + public class DashboardController : UmbracoApiController + { + private readonly Dashboards _dashboards; + + /// + /// Initializes a new instance of the with auto dependencies. + /// + public DashboardController() + { } + + /// + /// Initializes a new instance of the with all its dependencies. + /// + public DashboardController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, CacheHelper applicationCache, IProfilingLogger logger, IRuntimeState runtimeState, Dashboards dashboards) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, applicationCache, logger, runtimeState) + { + _dashboards = dashboards; + } + + //we have just one instance of HttpClient shared for the entire application + private static readonly HttpClient HttpClient = new HttpClient(); + + //we have baseurl as a param to make previewing easier, so we can test with a dev domain from client side + [ValidateAngularAntiForgeryToken] + public async Task GetRemoteDashboardContent(string section, string baseUrl = "https://dashboard.umbraco.org/") + { + var user = Security.CurrentUser; + var allowedSections = string.Join(",", user.AllowedSections); + var language = user.Language; + var version = UmbracoVersion.SemanticVersion.ToSemanticString(); + + var url = string.Format(baseUrl + "{0}?section={0}&allowed={1}&lang={2}&version={3}", section, allowedSections, language, version); + var key = "umbraco-dynamic-dashboard-" + language + allowedSections.Replace(",", "-") + section; + + var content = ApplicationCache.RuntimeCache.GetCacheItem(key); + var result = new JObject(); + if (content != null) + { + result = content; + } + else + { + //content is null, go get it + try + { + //fetch dashboard json and parse to JObject + var json = await HttpClient.GetStringAsync(url); + content = JObject.Parse(json); + result = content; + + ApplicationCache.RuntimeCache.InsertCacheItem(key, () => result, new TimeSpan(0, 30, 0)); + } + catch (HttpRequestException ex) + { + Logger.Error(ex.InnerException ?? ex, "Error getting dashboard content from '{Url}'", url); + + //it's still new JObject() - we return it like this to avoid error codes which triggers UI warnings + ApplicationCache.RuntimeCache.InsertCacheItem(key, () => result, new TimeSpan(0, 5, 0)); + } + } + + return result; + } + + public async Task GetRemoteDashboardCss(string section, string baseUrl = "https://dashboard.umbraco.org/") + { + var url = string.Format(baseUrl + "css/dashboard.css?section={0}", section); + var key = "umbraco-dynamic-dashboard-css-" + section; + + var content = ApplicationCache.RuntimeCache.GetCacheItem(key); + var result = string.Empty; + + if (content != null) + { + result = content; + } + else + { + //content is null, go get it + try + { + //fetch remote css + content = await HttpClient.GetStringAsync(url); + + //can't use content directly, modified closure problem + result = content; + + //save server content for 30 mins + ApplicationCache.RuntimeCache.InsertCacheItem(key, () => result, new TimeSpan(0, 30, 0)); + } + catch (HttpRequestException ex) + { + Logger.Error(ex.InnerException ?? ex, "Error getting dashboard CSS from '{Url}'", url); + + //it's still string.Empty - we return it like this to avoid error codes which triggers UI warnings + ApplicationCache.RuntimeCache.InsertCacheItem(key, () => result, new TimeSpan(0, 5, 0)); + } + } + + return new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(result, Encoding.UTF8, "text/css") + }; + } + + [ValidateAngularAntiForgeryToken] + [OutgoingEditorModelEvent] + public IEnumerable> GetDashboard(string section) + { + return _dashboards.GetDashboards(section, Security.CurrentUser); + } + } +} diff --git a/src/Umbraco.Web/Editors/DataTypeController.cs b/src/Umbraco.Web/Editors/DataTypeController.cs index e11ea7cbef..335b609b0a 100644 --- a/src/Umbraco.Web/Editors/DataTypeController.cs +++ b/src/Umbraco.Web/Editors/DataTypeController.cs @@ -344,7 +344,7 @@ namespace Umbraco.Web.Editors public IDictionary> GetGroupedPropertyEditors() { var datatypes = new List(); - var showDeprecatedPropertyEditors = UmbracoConfig.For.UmbracoSettings().Content.ShowDeprecatedPropertyEditors; + var showDeprecatedPropertyEditors = Current.Configs.Settings().Content.ShowDeprecatedPropertyEditors; var propertyEditors = Current.PropertyEditors .Where(x=>x.IsDeprecated == false || showDeprecatedPropertyEditors); diff --git a/src/Umbraco.Web/Editors/DataTypeValidateAttribute.cs b/src/Umbraco.Web/Editors/DataTypeValidateAttribute.cs index 8dd1f6755e..bbb9e22082 100644 --- a/src/Umbraco.Web/Editors/DataTypeValidateAttribute.cs +++ b/src/Umbraco.Web/Editors/DataTypeValidateAttribute.cs @@ -5,12 +5,11 @@ using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Filters; using AutoMapper; -using LightInject; using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; -using Umbraco.Web.Composing; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.WebApi; @@ -21,13 +20,25 @@ namespace Umbraco.Web.Editors /// internal sealed class DataTypeValidateAttribute : ActionFilterAttribute { - // LightInject can inject dependencies into properties + public IDataTypeService DataTypeService { get; } - [Inject] - public IDataTypeService DataTypeService { get; set; } + public PropertyEditorCollection PropertyEditors { get; } - [Inject] - public PropertyEditorCollection PropertyEditors { get; set; } + public DataTypeValidateAttribute() + : this(Current.Factory.GetInstance(), Current.Factory.GetInstance()) + { + } + + /// + /// For use in unit tests. Not possible to use as attribute ctor. + /// + /// + /// + public DataTypeValidateAttribute(IDataTypeService dataTypeService, PropertyEditorCollection propertyEditors) + { + DataTypeService = dataTypeService; + PropertyEditors = propertyEditors; + } public override void OnActionExecuting(HttpActionContext actionContext) { diff --git a/src/Umbraco.Web/Editors/EditorValidatorCollectionBuilder.cs b/src/Umbraco.Web/Editors/EditorValidatorCollectionBuilder.cs index e05c85a1b6..b3b7bab1a5 100644 --- a/src/Umbraco.Web/Editors/EditorValidatorCollectionBuilder.cs +++ b/src/Umbraco.Web/Editors/EditorValidatorCollectionBuilder.cs @@ -1,14 +1,9 @@ -using LightInject; -using Umbraco.Core.Composing; +using Umbraco.Core.Composing; namespace Umbraco.Web.Editors { internal class EditorValidatorCollectionBuilder : LazyCollectionBuilderBase { - public EditorValidatorCollectionBuilder(IServiceContainer container) - : base(container) - { } - protected override EditorValidatorCollectionBuilder This => this; } } diff --git a/src/Umbraco.Web/Editors/EntityController.cs b/src/Umbraco.Web/Editors/EntityController.cs index 5e73f4140e..993489855f 100644 --- a/src/Umbraco.Web/Editors/EntityController.cs +++ b/src/Umbraco.Web/Editors/EntityController.cs @@ -479,7 +479,9 @@ namespace Umbraco.Web.Editors if (objectType.HasValue) { var entities = Services.EntityService.GetPagedChildren(id, objectType.Value, pageNumber - 1, pageSize, out var totalRecords, - SqlContext.Query().Where(x => x.Name.Contains(filter)), + filter.IsNullOrWhiteSpace() + ? null + : SqlContext.Query().Where(x => x.Name.Contains(filter)), Ordering.By(orderBy, orderDirection)); if (totalRecords == 0) diff --git a/src/Umbraco.Web/Editors/ImagesController.cs b/src/Umbraco.Web/Editors/ImagesController.cs index e9b89a0ab4..0eb0f54882 100644 --- a/src/Umbraco.Web/Editors/ImagesController.cs +++ b/src/Umbraco.Web/Editors/ImagesController.cs @@ -17,10 +17,10 @@ namespace Umbraco.Web.Editors [PluginController("UmbracoApi")] public class ImagesController : UmbracoAuthorizedApiController { - private readonly MediaFileSystem _mediaFileSystem; + private readonly IMediaFileSystem _mediaFileSystem; private readonly IContentSection _contentSection; - public ImagesController(MediaFileSystem mediaFileSystem, IContentSection contentSection) + public ImagesController(IMediaFileSystem mediaFileSystem, IContentSection contentSection) { _mediaFileSystem = mediaFileSystem; _contentSection = contentSection; diff --git a/src/Umbraco.Web/Editors/LogController.cs b/src/Umbraco.Web/Editors/LogController.cs index dcd69d10b7..cc15b27977 100644 --- a/src/Umbraco.Web/Editors/LogController.cs +++ b/src/Umbraco.Web/Editors/LogController.cs @@ -27,7 +27,7 @@ namespace Umbraco.Web.Editors long totalRecords; var dateQuery = sinceDate.HasValue ? SqlContext.Query().Where(x => x.CreateDate >= sinceDate) : null; var result = Services.AuditService.GetPagedItemsByEntity(id, pageNumber - 1, pageSize, out totalRecords, orderDirection, customFilter: dateQuery); - var mapped = Mapper.Map>(result); + var mapped = result.Select(item => Mapper.Map(item)); var page = new PagedResult(totalRecords, pageNumber, pageSize) { @@ -56,11 +56,12 @@ namespace Umbraco.Web.Editors private IEnumerable MapAvatarsAndNames(IEnumerable items) { - var userIds = items.Select(x => x.UserId).ToArray(); + var mappedItems = items.ToList(); + var userIds = mappedItems.Select(x => x.UserId).ToArray(); var userAvatars = Services.UserService.GetUsersById(userIds) .ToDictionary(x => x.Id, x => x.GetUserAvatarUrls(ApplicationCache.RuntimeCache)); var userNames = Services.UserService.GetUsersById(userIds).ToDictionary(x => x.Id, x => x.Name); - foreach (var item in items) + foreach (var item in mappedItems) { if (userAvatars.TryGetValue(item.UserId, out var avatars)) { @@ -73,7 +74,7 @@ namespace Umbraco.Web.Editors } - return items; + return mappedItems; } } } diff --git a/src/Umbraco.Web/Editors/MacroController.cs b/src/Umbraco.Web/Editors/MacroController.cs index 3c576befb9..9780c23504 100644 --- a/src/Umbraco.Web/Editors/MacroController.cs +++ b/src/Umbraco.Web/Editors/MacroController.cs @@ -136,7 +136,7 @@ namespace Umbraco.Web.Editors var legacyPage = new global::umbraco.page(doc, _variationContextAccessor); UmbracoContext.HttpContext.Items["pageElements"] = legacyPage.Elements; - UmbracoContext.HttpContext.Items[global::Umbraco.Core.Constants.Conventions.Url.AltTemplate] = null; + UmbracoContext.HttpContext.Items[Core.Constants.Conventions.Url.AltTemplate] = null; var renderer = new UmbracoComponentRenderer(UmbracoContext); diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs index 1f73b0c629..dbe50b9781 100644 --- a/src/Umbraco.Web/Editors/MediaController.cs +++ b/src/Umbraco.Web/Editors/MediaController.cs @@ -23,6 +23,7 @@ using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; using System.Linq; using System.Web.Http.Controllers; +using Umbraco.Core.Composing; using Umbraco.Web.WebApi.Filters; using Constants = Umbraco.Core.Constants; using Umbraco.Core.Configuration; @@ -704,13 +705,13 @@ namespace Umbraco.Web.Editors var safeFileName = fileName.ToSafeFileName(); var ext = safeFileName.Substring(safeFileName.LastIndexOf('.') + 1).ToLower(); - if (UmbracoConfig.For.UmbracoSettings().Content.IsFileAllowedForUpload(ext)) + if (Current.Configs.Settings().Content.IsFileAllowedForUpload(ext)) { var mediaType = Constants.Conventions.MediaTypes.File; if (result.FormData["contentTypeAlias"] == Constants.Conventions.MediaTypes.AutoSelect) { - if (UmbracoConfig.For.UmbracoSettings().Content.ImageFileTypes.Contains(ext)) + if (Current.Configs.Settings().Content.ImageFileTypes.Contains(ext)) { mediaType = Constants.Conventions.MediaTypes.Image; } diff --git a/src/Umbraco.Web/Editors/PackageInstallController.cs b/src/Umbraco.Web/Editors/PackageInstallController.cs index 94465feab8..aee74551ea 100644 --- a/src/Umbraco.Web/Editors/PackageInstallController.cs +++ b/src/Umbraco.Web/Editors/PackageInstallController.cs @@ -8,7 +8,6 @@ using System.Net.Http; using System.Threading.Tasks; using System.Web.Http; using System.Xml; -using umbraco.cms.businesslogic.packager; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Events; @@ -26,6 +25,8 @@ using Umbraco.Web.UI; using Umbraco.Web.UI.JavaScript; using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; +using Umbraco.Web._Legacy.Packager; +using Umbraco.Web._Legacy.Packager.PackageInstance; using File = System.IO.File; using Notification = Umbraco.Web.Models.ContentEditing.Notification; using Version = System.Version; @@ -194,7 +195,7 @@ namespace Umbraco.Web.Editors { try { - global::umbraco.cms.businesslogic.packager.PackageAction + global::Umbraco.Web._Legacy.Packager.PackageInstance.PackageAction .UndoPackageAction(pack.Data.Name, n.Attributes["alias"].Value, n); } catch (Exception ex) @@ -310,7 +311,7 @@ namespace Umbraco.Web.Editors private void PopulateFromPackageData(LocalPackageInstallModel model) { - var ins = new global::umbraco.cms.businesslogic.packager.Installer(Security.CurrentUser.Id); + var ins = new global::Umbraco.Web._Legacy.Packager.Installer(Security.CurrentUser.Id); //this will load in all the metadata too var tempDir = ins.Import(model.ZipFilePath, false); @@ -500,7 +501,7 @@ namespace Umbraco.Web.Editors [HttpPost] public PackageInstallModel Import(PackageInstallModel model) { - var ins = new global::umbraco.cms.businesslogic.packager.Installer(Security.CurrentUser.Id); + var ins = new global::Umbraco.Web._Legacy.Packager.Installer(Security.CurrentUser.Id); var tempPath = ins.Import(model.ZipFilePath); //now we need to check for version comparison @@ -528,7 +529,7 @@ namespace Umbraco.Web.Editors [HttpPost] public PackageInstallModel InstallFiles(PackageInstallModel model) { - var ins = new global::umbraco.cms.businesslogic.packager.Installer(Security.CurrentUser.Id); + var ins = new global::Umbraco.Web._Legacy.Packager.Installer(Security.CurrentUser.Id); ins.LoadConfig(IOHelper.MapPath(model.TemporaryDirectoryPath)); ins.InstallFiles(model.Id, IOHelper.MapPath(model.TemporaryDirectoryPath)); @@ -562,7 +563,7 @@ namespace Umbraco.Web.Editors [HttpPost] public PackageInstallModel InstallData(PackageInstallModel model) { - var ins = new global::umbraco.cms.businesslogic.packager.Installer(Security.CurrentUser.Id); + var ins = new global::Umbraco.Web._Legacy.Packager.Installer(Security.CurrentUser.Id); ins.LoadConfig(IOHelper.MapPath(model.TemporaryDirectoryPath)); ins.InstallBusinessLogic(model.Id, IOHelper.MapPath(model.TemporaryDirectoryPath)); return model; @@ -576,7 +577,7 @@ namespace Umbraco.Web.Editors [HttpPost] public PackageInstallResult CleanUp(PackageInstallModel model) { - var ins = new global::umbraco.cms.businesslogic.packager.Installer(Security.CurrentUser.Id); + var ins = new global::Umbraco.Web._Legacy.Packager.Installer(Security.CurrentUser.Id); var tempDir = IOHelper.MapPath(model.TemporaryDirectoryPath); ins.LoadConfig(IOHelper.MapPath(model.TemporaryDirectoryPath)); ins.InstallCleanUp(model.Id, IOHelper.MapPath(model.TemporaryDirectoryPath)); diff --git a/src/Umbraco.Web/Editors/PasswordChanger.cs b/src/Umbraco.Web/Editors/PasswordChanger.cs index 54c3d12ea9..6777ae1f56 100644 --- a/src/Umbraco.Web/Editors/PasswordChanger.cs +++ b/src/Umbraco.Web/Editors/PasswordChanger.cs @@ -96,7 +96,7 @@ namespace Umbraco.Web.Editors { var errors = string.Join(". ", resetResult.Errors); _logger.Warn("Could not reset user password {PasswordErrors}", errors); - return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Could not reset password, errors: " + errors, new[] { "resetPassword" }) }); + return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult(errors, new[] { "resetPassword" }) }); } return Attempt.Succeed(new PasswordChangedModel()); @@ -116,21 +116,30 @@ namespace Umbraco.Web.Editors return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Password cannot be changed without the old password", new[] { "oldPassword" }) }); } - if (passwordModel.OldPassword.IsNullOrWhiteSpace() == false) + //get the user + var backOfficeIdentityUser = await userMgr.FindByIdAsync(savingUser.Id); + if (backOfficeIdentityUser == null) { - //if an old password is suplied try to change it - var changeResult = await userMgr.ChangePasswordAsync(savingUser.Id, passwordModel.OldPassword, passwordModel.NewPassword); - if (changeResult.Succeeded == false) - { - var errors = string.Join(". ", changeResult.Errors); - _logger.Warn("Could not change user password {PasswordErrors}", errors); - return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Could not change password, errors: " + errors, new[] { "oldPassword" }) }); - } - return Attempt.Succeed(new PasswordChangedModel()); + //this really shouldn't ever happen... but just in case + return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Password could not be verified", new[] { "oldPassword" }) }); } - - //We shouldn't really get here - return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Could not change password, invalid information supplied", new[] { "value" }) }); + //is the old password correct? + var validateResult = await userMgr.CheckPasswordAsync(backOfficeIdentityUser, passwordModel.OldPassword); + if(validateResult == false) + { + //no, fail with an error message for "oldPassword" + return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Incorrect password", new[] { "oldPassword" }) }); + } + //can we change to the new password? + var changeResult = await userMgr.ChangePasswordAsync(savingUser.Id, passwordModel.OldPassword, passwordModel.NewPassword); + if (changeResult.Succeeded == false) + { + //no, fail with error messages for "password" + var errors = string.Join(". ", changeResult.Errors); + _logger.Warn("Could not change user password {PasswordErrors}", errors); + return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult(errors, new[] { "password" }) }); + } + return Attempt.Succeed(new PasswordChangedModel()); } /// diff --git a/src/Umbraco.Web/Editors/RedirectUrlManagementController.cs b/src/Umbraco.Web/Editors/RedirectUrlManagementController.cs index 377b76f52a..aa2626ca3b 100644 --- a/src/Umbraco.Web/Editors/RedirectUrlManagementController.cs +++ b/src/Umbraco.Web/Editors/RedirectUrlManagementController.cs @@ -13,6 +13,7 @@ using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; using File = System.IO.File; using Umbraco.Core; +using Umbraco.Core.Composing; namespace Umbraco.Web.Editors { @@ -33,7 +34,7 @@ namespace Umbraco.Web.Editors [HttpGet] public IHttpActionResult GetEnableState() { - var enabled = UmbracoConfig.For.UmbracoSettings().WebRouting.DisableRedirectUrlTracking == false; + var enabled = Current.Configs.Settings().WebRouting.DisableRedirectUrlTracking == false; var userIsAdmin = Umbraco.UmbracoContext.Security.CurrentUser.IsAdmin(); return Ok(new { enabled, userIsAdmin }); } @@ -51,12 +52,6 @@ namespace Umbraco.Web.Editors : redirectUrlService.SearchRedirectUrls(searchTerm, page, pageSize, out resultCount); searchResult.SearchResults = Mapper.Map>(redirects).ToArray(); - //now map the Content/published url - foreach (var result in searchResult.SearchResults) - { - result.DestinationUrl = result.ContentId > 0 ? Umbraco.Url(result.ContentId) : "#"; - } - searchResult.TotalCount = resultCount; searchResult.CurrentPage = page; searchResult.PageCount = ((int)resultCount + pageSize - 1) / pageSize; diff --git a/src/Umbraco.Web/Editors/SectionController.cs b/src/Umbraco.Web/Editors/SectionController.cs index bcf451a5bb..f650c18fb3 100644 --- a/src/Umbraco.Web/Editors/SectionController.cs +++ b/src/Umbraco.Web/Editors/SectionController.cs @@ -1,94 +1,88 @@ -using System.Collections.Generic; -using AutoMapper; -using Umbraco.Web.Models.ContentEditing; -using Umbraco.Web.Mvc; -using System.Linq; -using Umbraco.Core.Composing; -using Umbraco.Core.Models; -using Umbraco.Web.Trees; -using Section = Umbraco.Web.Models.ContentEditing.Section; -using Umbraco.Web.Models.Trees; - -namespace Umbraco.Web.Editors -{ - /// - /// The API controller used for using the list of sections - /// - [PluginController("UmbracoApi")] - public class SectionController : UmbracoAuthorizedJsonController - { - private readonly Dashboards _dashboards; - - public SectionController(Dashboards dashboards) - { - _dashboards = dashboards; - } - - public IEnumerable
GetSections() - { - var sections = Services.SectionService.GetAllowedSections(Security.GetUserId().ResultOr(0)); - - var sectionModels = sections.Select(Mapper.Map).ToArray(); - - // this is a bit nasty since we'll be proxying via the app tree controller but we sort of have to do that - // since tree's by nature are controllers and require request contextual data - and then we have to - // remember to inject properties - nasty indeed - // fixme - this controller could/should be able to be created from the container and/or from webapi's IHttpControllerTypeResolver - var appTreeController = new ApplicationTreeController(); - Current.Container.InjectProperties(appTreeController); - appTreeController.ControllerContext = ControllerContext; - - var dashboards = _dashboards.GetDashboards(Security.CurrentUser); - - //now we can add metadata for each section so that the UI knows if there's actually anything at all to render for - //a dashboard for a given section, then the UI can deal with it accordingly (i.e. redirect to the first tree) - foreach (var section in sectionModels) - { - var hasDashboards = dashboards.TryGetValue(section.Alias, out var dashboardsForSection) && dashboardsForSection.Any(); - if (hasDashboards) continue; - - // get the first tree in the section and get its root node route path - var sectionRoot = appTreeController.GetApplicationTrees(section.Alias, null, null).Result; - section.RoutePath = GetRoutePathForFirstTree(sectionRoot); - } - - return sectionModels; - } - - /// - /// Returns the first non root/group node's route path - /// - /// - /// - private string GetRoutePathForFirstTree(TreeRootNode rootNode) - { - if (!rootNode.IsContainer || !rootNode.ContainsTrees) - return rootNode.RoutePath; - - foreach(var node in rootNode.Children) - { - if (node is TreeRootNode groupRoot) - return GetRoutePathForFirstTree(groupRoot);//recurse to get the first tree in the group - else - return node.RoutePath; - } - - return string.Empty; - } - - /// - /// Returns all the sections that the user has access to - /// - /// - public IEnumerable
GetAllSections() - { - var sections = Services.SectionService.GetSections(); - var mapped = sections.Select(Mapper.Map); - if (Security.CurrentUser.IsAdmin()) - return mapped; - - return mapped.Where(x => Security.CurrentUser.AllowedSections.Contains(x.Alias)).ToArray(); - } - - } -} +using System.Collections.Generic; +using AutoMapper; +using Umbraco.Web.Mvc; +using System.Linq; +using Umbraco.Core.Models; +using Umbraco.Web.Trees; +using Section = Umbraco.Web.Models.ContentEditing.Section; +using Umbraco.Web.Models.Trees; + +namespace Umbraco.Web.Editors +{ + /// + /// The API controller used for using the list of sections + /// + [PluginController("UmbracoApi")] + public class SectionController : UmbracoAuthorizedJsonController + { + private readonly Dashboards _dashboards; + + public SectionController(Dashboards dashboards) + { + _dashboards = dashboards; + } + + public IEnumerable
GetSections() + { + var sections = Services.SectionService.GetAllowedSections(Security.GetUserId().ResultOr(0)); + + var sectionModels = sections.Select(Mapper.Map).ToArray(); + + // this is a bit nasty since we'll be proxying via the app tree controller but we sort of have to do that + // since tree's by nature are controllers and require request contextual data + var appTreeController = new ApplicationTreeController { ControllerContext = ControllerContext }; + + var dashboards = _dashboards.GetDashboards(Security.CurrentUser); + + //now we can add metadata for each section so that the UI knows if there's actually anything at all to render for + //a dashboard for a given section, then the UI can deal with it accordingly (i.e. redirect to the first tree) + foreach (var section in sectionModels) + { + var hasDashboards = dashboards.TryGetValue(section.Alias, out var dashboardsForSection) && dashboardsForSection.Any(); + if (hasDashboards) continue; + + // get the first tree in the section and get its root node route path + var sectionRoot = appTreeController.GetApplicationTrees(section.Alias, null, null).Result; + section.RoutePath = GetRoutePathForFirstTree(sectionRoot); + } + + return sectionModels; + } + + /// + /// Returns the first non root/group node's route path + /// + /// + /// + private string GetRoutePathForFirstTree(TreeRootNode rootNode) + { + if (!rootNode.IsContainer || !rootNode.ContainsTrees) + return rootNode.RoutePath; + + foreach(var node in rootNode.Children) + { + if (node is TreeRootNode groupRoot) + return GetRoutePathForFirstTree(groupRoot);//recurse to get the first tree in the group + else + return node.RoutePath; + } + + return string.Empty; + } + + /// + /// Returns all the sections that the user has access to + /// + /// + public IEnumerable
GetAllSections() + { + var sections = Services.SectionService.GetSections(); + var mapped = sections.Select(Mapper.Map); + if (Security.CurrentUser.IsAdmin()) + return mapped; + + return mapped.Where(x => Security.CurrentUser.AllowedSections.Contains(x.Alias)).ToArray(); + } + + } +} diff --git a/src/Umbraco.Web/Editors/TourController.cs b/src/Umbraco.Web/Editors/TourController.cs index 483a910788..f2a546e407 100644 --- a/src/Umbraco.Web/Editors/TourController.cs +++ b/src/Umbraco.Web/Editors/TourController.cs @@ -2,8 +2,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using LightInject; using Newtonsoft.Json; +using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.IO; @@ -16,20 +16,25 @@ namespace Umbraco.Web.Editors [PluginController("UmbracoApi")] public class TourController : UmbracoAuthorizedJsonController { + private readonly TourFilterCollection _filters; + + public TourController(TourFilterCollection filters) + { + _filters = filters; + } + public IEnumerable GetTours() { var result = new List(); - if (UmbracoConfig.For.UmbracoSettings().BackOffice.Tours.EnableTours == false) + if (Current.Configs.Settings().BackOffice.Tours.EnableTours == false) return result; - var filters = Current.Container.GetInstance().ToList(); // fixme inject - //get all filters that will be applied to all tour aliases - var aliasOnlyFilters = filters.Where(x => x.PluginName == null && x.TourFileName == null).ToList(); + var aliasOnlyFilters = _filters.Where(x => x.PluginName == null && x.TourFileName == null).ToList(); //don't pass in any filters for core tours that have a plugin name assigned - var nonPluginFilters = filters.Where(x => x.PluginName == null).ToList(); + var nonPluginFilters = _filters.Where(x => x.PluginName == null).ToList(); //add core tour files var coreToursPath = Path.Combine(IOHelper.MapPath(SystemDirectories.Config), "BackOfficeTours"); @@ -45,7 +50,7 @@ namespace Umbraco.Web.Editors foreach (var plugin in Directory.EnumerateDirectories(IOHelper.MapPath(SystemDirectories.AppPlugins))) { var pluginName = Path.GetFileName(plugin.TrimEnd('\\')); - var pluginFilters = filters.Where(x => x.PluginName != null && x.PluginName.IsMatch(pluginName)).ToList(); + var pluginFilters = _filters.Where(x => x.PluginName != null && x.PluginName.IsMatch(pluginName)).ToList(); //If there is any filter applied to match the plugin only (no file or tour alias) then ignore the plugin entirely var isPluginFiltered = pluginFilters.Any(x => x.TourFileName == null && x.TourAlias == null); diff --git a/src/Umbraco.Web/Editors/UpdateCheckController.cs b/src/Umbraco.Web/Editors/UpdateCheckController.cs index 8960d2ab89..cd11382d13 100644 --- a/src/Umbraco.Web/Editors/UpdateCheckController.cs +++ b/src/Umbraco.Web/Editors/UpdateCheckController.cs @@ -3,6 +3,8 @@ using System.Linq; using System.Net.Http; using System.Net.Http.Headers; using System.Web.Http.Filters; +using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Models; using Umbraco.Web.Models; @@ -58,9 +60,9 @@ namespace Umbraco.Web.Editors var cookie = new CookieHeaderValue("UMB_UPDCHK", "1") { Path = "/", - Expires = DateTimeOffset.Now.AddDays(UmbracoConfig.For.GlobalSettings().VersionCheckPeriod), + Expires = DateTimeOffset.Now.AddDays(Current.Configs.Global().VersionCheckPeriod), HttpOnly = true, - Secure = UmbracoConfig.For.GlobalSettings().UseHttps + Secure = Current.Configs.Global().UseHttps }; context.Response.Headers.AddCookies(new[] { cookie }); } diff --git a/src/Umbraco.Web/Editors/UsersController.cs b/src/Umbraco.Web/Editors/UsersController.cs index f7edd3de8f..7577624621 100644 --- a/src/Umbraco.Web/Editors/UsersController.cs +++ b/src/Umbraco.Web/Editors/UsersController.cs @@ -98,14 +98,14 @@ namespace Umbraco.Web.Editors var safeFileName = fileName.ToSafeFileName(); var ext = safeFileName.Substring(safeFileName.LastIndexOf('.') + 1).ToLower(); - if (UmbracoConfig.For.UmbracoSettings().Content.DisallowedUploadFiles.Contains(ext) == false) + if (Current.Configs.Settings().Content.DisallowedUploadFiles.Contains(ext) == false) { //generate a path of known data, we don't want this path to be guessable user.Avatar = "UserAvatars/" + (user.Id + safeFileName).ToSHA1() + "." + ext; using (var fs = System.IO.File.OpenRead(file.LocalFileName)) { - Current.FileSystems.MediaFileSystem.AddFile(user.Avatar, fs, true); + Current.MediaFileSystem.AddFile(user.Avatar, fs, true); } userService.Save(user); @@ -146,8 +146,8 @@ namespace Umbraco.Web.Editors if (filePath.IsNullOrWhiteSpace() == false) { - if (Current.FileSystems.MediaFileSystem.FileExists(filePath)) - Current.FileSystems.MediaFileSystem.DeleteFile(filePath); + if (Current.MediaFileSystem.FileExists(filePath)) + Current.MediaFileSystem.DeleteFile(filePath); } return Request.CreateResponse(HttpStatusCode.OK, found.GetUserAvatarUrls(ApplicationCache.StaticCache)); @@ -195,7 +195,7 @@ namespace Umbraco.Web.Editors // so to do that here, we'll need to check if this current user is an admin and if not we should exclude all user who are // also admins - var hideDisabledUsers = UmbracoConfig.For.UmbracoSettings().Security.HideDisabledUsersInBackoffice; + var hideDisabledUsers = Current.Configs.Settings().Security.HideDisabledUsersInBackoffice; var excludeUserGroups = new string[0]; var isAdmin = Security.CurrentUser.IsAdmin(); if (isAdmin == false) @@ -253,7 +253,7 @@ namespace Umbraco.Web.Editors throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState)); } - if (UmbracoConfig.For.UmbracoSettings().Security.UsernameIsEmail) + if (Current.Configs.Settings().Security.UsernameIsEmail) { //ensure they are the same if we're using it userSave.Username = userSave.Email; @@ -345,7 +345,7 @@ namespace Umbraco.Web.Editors } IUser user; - if (UmbracoConfig.For.UmbracoSettings().Security.UsernameIsEmail) + if (Current.Configs.Settings().Security.UsernameIsEmail) { //ensure it's the same userSave.Username = userSave.Email; @@ -419,7 +419,7 @@ namespace Umbraco.Web.Editors if (user != null && (extraCheck == null || extraCheck(user))) { ModelState.AddModelError( - UmbracoConfig.For.UmbracoSettings().Security.UsernameIsEmail ? "Email" : "Username", + Current.Configs.Settings().Security.UsernameIsEmail ? "Email" : "Username", "A user with the username already exists"); throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState)); } @@ -539,7 +539,7 @@ namespace Umbraco.Web.Editors // if the found user has his email for username, we want to keep this synced when changing the email. // we have already cross-checked above that the email isn't colliding with anything, so we can safely assign it here. - if (UmbracoConfig.For.UmbracoSettings().Security.UsernameIsEmail && found.Username == found.Email && userSave.Username != userSave.Email) + if (Current.Configs.Settings().Security.UsernameIsEmail && found.Username == found.Email && userSave.Username != userSave.Email) { userSave.Username = userSave.Email; } @@ -552,7 +552,7 @@ namespace Umbraco.Web.Editors var passwordChangeResult = await passwordChanger.ChangePasswordWithIdentityAsync(Security.CurrentUser, found, userSave.ChangePassword, UserManager); if (passwordChangeResult.Success) { - //need to re-get the user + //need to re-get the user found = Services.UserService.GetUserById(intId.Result); } else @@ -712,7 +712,7 @@ namespace Umbraco.Web.Editors var userName = user.Name; Services.UserService.Delete(user, true); - + return Request.CreateNotificationSuccessResponse( Services.TextService.Localize("speechBubbles/deleteUserSuccess", new[] { userName })); } diff --git a/src/Umbraco.Web/HealthCheck/HealthCheckController.cs b/src/Umbraco.Web/HealthCheck/HealthCheckController.cs index fe68c5b01d..2f72b946de 100644 --- a/src/Umbraco.Web/HealthCheck/HealthCheckController.cs +++ b/src/Umbraco.Web/HealthCheck/HealthCheckController.cs @@ -4,6 +4,8 @@ using System.ComponentModel; using System.Configuration; using System.Linq; using System.Web.Http; +using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Core.Logging; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.HealthChecks; @@ -27,7 +29,7 @@ namespace Umbraco.Web.HealthCheck _checks = checks ?? throw new ArgumentNullException(nameof(checks)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - var healthCheckConfig = UmbracoConfig.For.HealthCheck(); + var healthCheckConfig = Current.Configs.HealthChecks(); _disabledCheckIds = healthCheckConfig.DisabledChecks .Select(x => x.Id) .ToList(); diff --git a/src/Umbraco.Web/HealthCheck/HealthCheckNotificationMethodCollectionBuilder.cs b/src/Umbraco.Web/HealthCheck/HealthCheckNotificationMethodCollectionBuilder.cs index 4f958da9a0..b78c188660 100644 --- a/src/Umbraco.Web/HealthCheck/HealthCheckNotificationMethodCollectionBuilder.cs +++ b/src/Umbraco.Web/HealthCheck/HealthCheckNotificationMethodCollectionBuilder.cs @@ -1,15 +1,10 @@ -using LightInject; -using Umbraco.Core.Composing; +using Umbraco.Core.Composing; using Umbraco.Web.HealthCheck.NotificationMethods; namespace Umbraco.Web.HealthCheck { internal class HealthCheckNotificationMethodCollectionBuilder : LazyCollectionBuilderBase { - public HealthCheckNotificationMethodCollectionBuilder(IServiceContainer container) - : base(container) - { } - protected override HealthCheckNotificationMethodCollectionBuilder This => this; } } diff --git a/src/Umbraco.Web/HealthCheck/HeathCheckCollectionBuilder.cs b/src/Umbraco.Web/HealthCheck/HeathCheckCollectionBuilder.cs index 9528f9a9dd..e616ba49ae 100644 --- a/src/Umbraco.Web/HealthCheck/HeathCheckCollectionBuilder.cs +++ b/src/Umbraco.Web/HealthCheck/HeathCheckCollectionBuilder.cs @@ -1,18 +1,13 @@ -using LightInject; -using Umbraco.Core.Composing; +using Umbraco.Core.Composing; namespace Umbraco.Web.HealthCheck { public class HealthCheckCollectionBuilder : LazyCollectionBuilderBase { - public HealthCheckCollectionBuilder(IServiceContainer container) - : base(container) - { } - protected override HealthCheckCollectionBuilder This => this; // note: in v7 they were per-request, not sure why? // the collection is injected into the controller & there's only 1 controller per request anyways - protected override ILifetime CollectionLifetime => null; // transient! + protected override Lifetime CollectionLifetime => Lifetime.Transient; // transient! } } diff --git a/src/Umbraco.Web/HealthCheck/NotificationMethods/EmailNotificationMethod.cs b/src/Umbraco.Web/HealthCheck/NotificationMethods/EmailNotificationMethod.cs index b697c87335..87c0e4f46d 100644 --- a/src/Umbraco.Web/HealthCheck/NotificationMethods/EmailNotificationMethod.cs +++ b/src/Umbraco.Web/HealthCheck/NotificationMethods/EmailNotificationMethod.cs @@ -4,6 +4,7 @@ using System.Net.Mail; using System.Threading; using System.Threading.Tasks; using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Services; @@ -69,7 +70,7 @@ namespace Umbraco.Web.HealthCheck.NotificationMethods private MailMessage CreateMailMessage(string subject, string message) { - var to = UmbracoConfig.For.UmbracoSettings().Content.NotificationEmailAddress; + var to = Current.Configs.Settings().Content.NotificationEmailAddress; if (string.IsNullOrWhiteSpace(subject)) subject = "Umbraco Health Check Status"; diff --git a/src/Umbraco.Web/HealthCheck/NotificationMethods/NotificationMethodBase.cs b/src/Umbraco.Web/HealthCheck/NotificationMethods/NotificationMethodBase.cs index f35df0062e..cf71be3c34 100644 --- a/src/Umbraco.Web/HealthCheck/NotificationMethods/NotificationMethodBase.cs +++ b/src/Umbraco.Web/HealthCheck/NotificationMethods/NotificationMethodBase.cs @@ -2,6 +2,8 @@ using System.Reflection; using System.Threading; using System.Threading.Tasks; +using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.HealthChecks; @@ -19,7 +21,7 @@ namespace Umbraco.Web.HealthCheck.NotificationMethods return; } - var healthCheckConfig = UmbracoConfig.For.HealthCheck(); + var healthCheckConfig = Current.Configs.HealthChecks(); var notificationMethods = healthCheckConfig.NotificationSettings.NotificationMethods; var notificationMethod = notificationMethods[attribute.Alias]; if (notificationMethod == null) diff --git a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs index e9e2dcccc6..2e82fe252d 100644 --- a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs +++ b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Linq; using System.Text; using System.Web; @@ -9,15 +8,13 @@ using System.Web.Mvc; using System.Web.Mvc.Html; using System.Web.Routing; using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Exceptions; using Umbraco.Core.IO; -using Umbraco.Core.Models.PublishedContent; -using Umbraco.Web.Composing; -using Umbraco.Web.Models; using Umbraco.Web.Mvc; using Umbraco.Web.Security; -using Constants = Umbraco.Core.Constants; +using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Web { @@ -68,7 +65,7 @@ namespace Umbraco.Web if (UmbracoContext.Current.InPreviewMode) { var htmlBadge = - String.Format(UmbracoConfig.For.UmbracoSettings().Content.PreviewBadge, + String.Format(Current.Configs.Settings().Content.PreviewBadge, IOHelper.ResolveUrl(SystemDirectories.Umbraco), UmbracoContext.Current.HttpContext.Server.UrlEncode(UmbracoContext.Current.HttpContext.Request.Path)); return new MvcHtmlString(htmlBadge); @@ -98,7 +95,7 @@ namespace Umbraco.Web } if (cacheByMember) { - var helper = new MembershipHelper(Current.UmbracoContext); + var helper = Current.Factory.GetInstance(); var currentMember = helper.GetCurrentMember(); cacheKey.AppendFormat("m{0}-", currentMember == null ? 0 : currentMember.Id); } @@ -832,7 +829,7 @@ namespace Umbraco.Web } #endregion - + } } diff --git a/src/Umbraco.Web/Install/Controllers/InstallApiController.cs b/src/Umbraco.Web/Install/Controllers/InstallApiController.cs index 81b1aac217..a63484fb39 100644 --- a/src/Umbraco.Web/Install/Controllers/InstallApiController.cs +++ b/src/Umbraco.Web/Install/Controllers/InstallApiController.cs @@ -5,7 +5,6 @@ using System.Reflection; using System.Web.Http; using Newtonsoft.Json.Linq; using Umbraco.Core; -using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Migrations.Install; using Umbraco.Web.Install.Models; @@ -18,18 +17,18 @@ namespace Umbraco.Web.Install.Controllers public class InstallApiController : ApiController { private readonly DatabaseBuilder _databaseBuilder; - private readonly ProfilingLogger _proflog; + private readonly IProfilingLogger _proflog; private readonly InstallStepCollection _installSteps; private readonly ILogger _logger; - public InstallApiController(UmbracoContext umbracoContext, DatabaseBuilder databaseBuilder, ProfilingLogger proflog, InstallHelper installHelper, InstallStepCollection installSteps) + public InstallApiController(UmbracoContext umbracoContext, DatabaseBuilder databaseBuilder, IProfilingLogger proflog, InstallHelper installHelper, InstallStepCollection installSteps) { UmbracoContext = umbracoContext ?? throw new ArgumentNullException(nameof(umbracoContext)); _databaseBuilder = databaseBuilder ?? throw new ArgumentNullException(nameof(databaseBuilder)); _proflog = proflog ?? throw new ArgumentNullException(nameof(proflog)); _installSteps = installSteps; InstallHelper = installHelper; - _logger = _proflog.Logger; + _logger = _proflog; } /// diff --git a/src/Umbraco.Web/Install/Controllers/InstallPackageController.cs b/src/Umbraco.Web/Install/Controllers/InstallPackageController.cs index eb7d02053b..1fd2ac27bb 100644 --- a/src/Umbraco.Web/Install/Controllers/InstallPackageController.cs +++ b/src/Umbraco.Web/Install/Controllers/InstallPackageController.cs @@ -56,7 +56,7 @@ namespace Umbraco.Web.Install.Controllers UmbracoVersion.Current, UmbracoContext.Current.Security.CurrentUser.Id); - var installer = new global::umbraco.cms.businesslogic.packager.Installer(UmbracoContext.Current.Security.CurrentUser.Id); + var installer = new global::Umbraco.Web._Legacy.Packager.Installer(UmbracoContext.Current.Security.CurrentUser.Id); var tempFile = installer.Import(packageFile); installer.LoadConfig(tempFile); @@ -79,7 +79,7 @@ namespace Umbraco.Web.Install.Controllers public HttpResponseMessage InstallPackageFiles(InstallPackageModel model) { model.PackageFile = HttpUtility.UrlDecode(model.PackageFile); - var installer = new global::umbraco.cms.businesslogic.packager.Installer(UmbracoContext.Current.Security.CurrentUser.Id); + var installer = new global::Umbraco.Web._Legacy.Packager.Installer(UmbracoContext.Current.Security.CurrentUser.Id); installer.LoadConfig(model.PackageFile); installer.InstallFiles(model.ManifestId, model.PackageFile); return Json(new @@ -135,7 +135,7 @@ namespace Umbraco.Web.Install.Controllers public HttpResponseMessage InstallBusinessLogic(InstallPackageModel model) { model.PackageFile = HttpUtility.UrlDecode(model.PackageFile); - var installer = new global::umbraco.cms.businesslogic.packager.Installer(UmbracoContext.Current.Security.CurrentUser.Id); + var installer = new global::Umbraco.Web._Legacy.Packager.Installer(UmbracoContext.Current.Security.CurrentUser.Id); installer.LoadConfig(model.PackageFile); installer.InstallBusinessLogic(model.ManifestId, model.PackageFile); return Json(new @@ -156,7 +156,7 @@ namespace Umbraco.Web.Install.Controllers public HttpResponseMessage CleanupInstallation(InstallPackageModel model) { model.PackageFile = HttpUtility.UrlDecode(model.PackageFile); - var installer = new global::umbraco.cms.businesslogic.packager.Installer(UmbracoContext.Current.Security.CurrentUser.Id); + var installer = new global::Umbraco.Web._Legacy.Packager.Installer(UmbracoContext.Current.Security.CurrentUser.Id); installer.LoadConfig(model.PackageFile); installer.InstallCleanUp(model.ManifestId, model.PackageFile); diff --git a/src/Umbraco.Web/Install/InstallHelper.cs b/src/Umbraco.Web/Install/InstallHelper.cs index 22087861d1..08a552d4d4 100644 --- a/src/Umbraco.Web/Install/InstallHelper.cs +++ b/src/Umbraco.Web/Install/InstallHelper.cs @@ -1,13 +1,10 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Configuration; using System.IO; using System.Linq; using System.Net.Http; using System.Web; -using LightInject; -using Semver; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.IO; @@ -15,11 +12,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Migrations.Install; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.SqlSyntax; -using Umbraco.Core.Scoping; -using Umbraco.Core.Services; -using Umbraco.Web.Cache; using Umbraco.Web.Composing; -using Umbraco.Web.Install.InstallSteps; using Umbraco.Web.Install.Models; namespace Umbraco.Web.Install diff --git a/src/Umbraco.Web/Install/InstallStepCollection.cs b/src/Umbraco.Web/Install/InstallStepCollection.cs index 22e084ede3..ffbd3f10cb 100644 --- a/src/Umbraco.Web/Install/InstallStepCollection.cs +++ b/src/Umbraco.Web/Install/InstallStepCollection.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Umbraco.Web.Install.InstallSteps; using Umbraco.Web.Install.Models; namespace Umbraco.Web.Install @@ -9,10 +10,29 @@ namespace Umbraco.Web.Install private readonly InstallHelper _installHelper; private readonly IEnumerable _orderedInstallerSteps; - public InstallStepCollection(InstallHelper installHelper, IEnumerable orderedInstallerSteps) + public InstallStepCollection(InstallHelper installHelper, IEnumerable installerSteps) { _installHelper = installHelper; - _orderedInstallerSteps = orderedInstallerSteps; + + // fixme this is ugly but I have a branch where it's nicely refactored - for now we just want to manage ordering + var a = installerSteps.ToArray(); + _orderedInstallerSteps = new InstallSetupStep[] + { + a.OfType().First(), + a.OfType().First(), + a.OfType().First(), + a.OfType().First(), + a.OfType().First(), + a.OfType().First(), + a.OfType().First(), + + //TODO: Add these back once we have a compatible starter kit + //orderedInstallerSteps.OfType().First(), + //orderedInstallerSteps.OfType().First(), + //orderedInstallerSteps.OfType().First(), + + a.OfType().First(), + }; } diff --git a/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs b/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs index 0b225e158b..64b8568fe5 100644 --- a/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs @@ -8,6 +8,7 @@ using System.Web; using System.Web.Security; using Newtonsoft.Json; using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Migrations.Install; using Umbraco.Core.Services; @@ -82,7 +83,7 @@ namespace Umbraco.Web.Install.InstallSteps admin.Username = user.Email.Trim(); _userService.Save(admin); - + if (user.SubscribeToNewsLetter) { if (_httpClient == null) @@ -144,7 +145,7 @@ namespace Umbraco.Web.Install.InstallSteps // In this one case when it's a brand new install and nothing has been configured, make sure the // back office cookie is cleared so there's no old cookies lying around causing problems - _http.ExpireCookie(UmbracoConfig.For.UmbracoSettings().Security.AuthCookieName); + _http.ExpireCookie(Current.Configs.Settings().Security.AuthCookieName); return true; } diff --git a/src/Umbraco.Web/Install/InstallSteps/StarterKitCleanupStep.cs b/src/Umbraco.Web/Install/InstallSteps/StarterKitCleanupStep.cs index 5738f2a67c..9345a0fc96 100644 --- a/src/Umbraco.Web/Install/InstallSteps/StarterKitCleanupStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/StarterKitCleanupStep.cs @@ -1,8 +1,8 @@ using System; using System.Linq; using System.Web; -using umbraco.cms.businesslogic.packager; using Umbraco.Web.Install.Models; +using Umbraco.Web._Legacy.Packager; namespace Umbraco.Web.Install.InstallSteps { diff --git a/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs b/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs index b8ae600dba..8f9f9242d7 100644 --- a/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs @@ -2,12 +2,13 @@ using System.Collections.Generic; using System.Linq; using System.Web; -using umbraco.cms.businesslogic.packager; using Umbraco.Core.Services; using Umbraco.Core.Configuration; using Umbraco.Web.Composing; using Umbraco.Web.Install.Models; using Umbraco.Web.Security; +using Umbraco.Web._Legacy.Packager; +using Umbraco.Web._Legacy.Packager.PackageInstance; namespace Umbraco.Web.Install.InstallSteps { diff --git a/src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs b/src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs index 844a27a576..8bec4ca199 100644 --- a/src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs @@ -1,9 +1,9 @@ using System; using System.Linq; using System.Web; -using umbraco.cms.businesslogic.packager; using Umbraco.Web.Composing; using Umbraco.Web.Install.Models; +using Umbraco.Web._Legacy.Packager; namespace Umbraco.Web.Install.InstallSteps { diff --git a/src/Umbraco.Web/Logging/WebProfilerComponent.cs b/src/Umbraco.Web/Logging/WebProfilerComponent.cs index c6c7b9a545..b61bcd372c 100755 --- a/src/Umbraco.Web/Logging/WebProfilerComponent.cs +++ b/src/Umbraco.Web/Logging/WebProfilerComponent.cs @@ -1,47 +1,45 @@ using System; using System.Web; -using Umbraco.Core; using Umbraco.Core.Components; using Umbraco.Core.Logging; namespace Umbraco.Web.Logging { - internal class WebProfilerComponent : UmbracoComponentBase, IUmbracoCoreComponent + internal sealed class WebProfilerComponent : IComponent { - // the profiler is too important to be composed in a component, - // it is composed first thing in WebRuntime.Compose - this component - // only initializes it if needed. - // - //public override void Compose(Composition Composition) - //{ - // composition.Container.RegisterSingleton(); - //} + private readonly WebProfiler _profiler; + private readonly bool _profile; - private WebProfiler _profiler; - - public void Initialize(IProfiler profiler, ILogger logger, IRuntimeState runtime) + public WebProfilerComponent(IProfiler profiler, ILogger logger) { + _profile = true; + // although registered in WebRuntime.Compose, ensure that we have not // been replaced by another component, and we are still "the" profiler _profiler = profiler as WebProfiler; - if (_profiler == null) - { - // if VoidProfiler was registered, let it be known - var vp = profiler as VoidProfiler; - if (vp != null) - logger.Info("Profiler is VoidProfiler, not profiling (must run debug mode to profile)."); - return; - } + if (_profiler != null) return; + + // if VoidProfiler was registered, let it be known + if (profiler is VoidProfiler) + logger.Info("Profiler is VoidProfiler, not profiling (must run debug mode to profile)."); + _profile = false; + } + + public void Initialize() + { + if (!_profile) return; // bind to ApplicationInit - ie execute the application initialization for *each* application // it would be a mistake to try and bind to the current application events UmbracoApplicationBase.ApplicationInit += InitializeApplication; } + public void Terminate() + { } + private void InitializeApplication(object sender, EventArgs args) { - var app = sender as HttpApplication; - if (app == null) return; + if (!(sender is HttpApplication app)) return; // for *each* application (this will run more than once) app.BeginRequest += (s, a) => _profiler.UmbracoApplicationBeginRequest(s, a); diff --git a/src/Umbraco.Web/Logging/WebProfilerComposer.cs b/src/Umbraco.Web/Logging/WebProfilerComposer.cs new file mode 100644 index 0000000000..383d8d1149 --- /dev/null +++ b/src/Umbraco.Web/Logging/WebProfilerComposer.cs @@ -0,0 +1,7 @@ +using Umbraco.Core.Components; + +namespace Umbraco.Web.Logging +{ + internal class WebProfilerComposer : ComponentComposer, ICoreComposer + { } +} diff --git a/src/Umbraco.Web/Macros/MacroRenderer.cs b/src/Umbraco.Web/Macros/MacroRenderer.cs index a8c89f179a..e0a0fd65c8 100755 --- a/src/Umbraco.Web/Macros/MacroRenderer.cs +++ b/src/Umbraco.Web/Macros/MacroRenderer.cs @@ -27,11 +27,11 @@ namespace Umbraco.Web.Macros { public class MacroRenderer { - private readonly ProfilingLogger _plogger; + private readonly IProfilingLogger _plogger; // todo: there are many more things that would need to be injected in here - public MacroRenderer(ProfilingLogger plogger) + public MacroRenderer(IProfilingLogger plogger) { _plogger = plogger; } @@ -143,7 +143,7 @@ namespace Umbraco.Web.Macros var key = member?.ProviderUserKey; if (key == null) return; } - + // this is legacy and I'm not sure what exactly it is supposed to do if (macroContent.Control != null) macroContent.ControlId = macroContent.Control.ID; @@ -304,7 +304,7 @@ namespace Umbraco.Web.Macros { Exceptions.Add(e); - _plogger.Logger.Warn(e, "Failed {MsgIn}", msgIn); + _plogger.Warn(e, "Failed {MsgIn}", msgIn); var macroErrorEventArgs = new MacroErrorEventArgs { @@ -312,7 +312,7 @@ namespace Umbraco.Web.Macros Alias = macro.Alias, MacroSource = macro.MacroSource, Exception = e, - Behaviour = UmbracoConfig.For.UmbracoSettings().Content.MacroErrorBehaviour + Behaviour = Current.Configs.Settings().Content.MacroErrorBehaviour }; OnError(macroErrorEventArgs); @@ -362,7 +362,7 @@ namespace Umbraco.Web.Macros "Executed PartialView.", () => ExecutePartialView(model), () => textService.Localize("errors/macroErrorLoadingPartialView", new[] { model.MacroSource })); - + case MacroTypes.UserControl: return ExecuteMacroWithErrorWrapper(model, $"Loading UserControl: MacroSource=\"{model.MacroSource}\".", @@ -567,7 +567,7 @@ namespace Umbraco.Web.Macros private static readonly Regex HrefRegex = new Regex("href=\"([^\"]*)\"", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); - public static string GetRenderedMacro(int macroId, Hashtable elements, Hashtable attributes, int pageId, IMacroService macroService, ProfilingLogger plogger) + public static string GetRenderedMacro(int macroId, Hashtable elements, Hashtable attributes, int pageId, IMacroService macroService, IProfilingLogger plogger) { var m = macroService.GetById(macroId); if (m == null) return string.Empty; @@ -604,7 +604,7 @@ namespace Umbraco.Web.Macros querystring += $"&umb_{ide.Key}={HttpContext.Current.Server.UrlEncode((ide.Value ?? String.Empty).ToString())}"; // create a new 'HttpWebRequest' object to the mentioned URL. - var useSsl = UmbracoConfig.For.GlobalSettings().UseHttps; + var useSsl = Current.Configs.Global().UseHttps; var protocol = useSsl ? "https" : "http"; var currentRequest = HttpContext.Current.Request; var serverVars = currentRequest.ServerVariables; @@ -619,7 +619,7 @@ namespace Umbraco.Web.Macros // propagate the user's context // TODO: this is the worst thing ever. // also will not work if people decide to put their own custom auth system in place. - var inCookie = currentRequest.Cookies[UmbracoConfig.For.UmbracoSettings().Security.AuthCookieName]; + var inCookie = currentRequest.Cookies[Current.Configs.Settings().Security.AuthCookieName]; if (inCookie == null) throw new NullReferenceException("No auth cookie found"); var cookie = new Cookie(inCookie.Name, inCookie.Value, inCookie.Path, serverVars["SERVER_NAME"]); myHttpWebRequest.CookieContainer = new CookieContainer(); diff --git a/src/Umbraco.Web/Macros/PartialViewMacroController.cs b/src/Umbraco.Web/Macros/PartialViewMacroController.cs index 9437c8c36d..21d7b3292c 100644 --- a/src/Umbraco.Web/Macros/PartialViewMacroController.cs +++ b/src/Umbraco.Web/Macros/PartialViewMacroController.cs @@ -2,14 +2,16 @@ using Umbraco.Web.Models; using Umbraco.Web.Mvc; using System.Linq; +using Umbraco.Core.Composing; using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Web.Macros { /// - /// Controller to render macro content for Parital View Macros + /// Controller to render macro content for Partial View Macros /// [MergeParentContextViewData] + [HideFromTypeFinder] // explicitly used: do *not* find and register it! internal class PartialViewMacroController : Controller { private readonly MacroModel _macro; diff --git a/src/Umbraco.Web/Media/UploadAutoFillProperties.cs b/src/Umbraco.Web/Media/UploadAutoFillProperties.cs index 6a56dec918..01ced179d6 100644 --- a/src/Umbraco.Web/Media/UploadAutoFillProperties.cs +++ b/src/Umbraco.Web/Media/UploadAutoFillProperties.cs @@ -15,11 +15,11 @@ namespace Umbraco.Web.Media /// internal class UploadAutoFillProperties { - private readonly MediaFileSystem _mediaFileSystem; + private readonly IMediaFileSystem _mediaFileSystem; private readonly ILogger _logger; private readonly IContentSection _contentSection; - public UploadAutoFillProperties(MediaFileSystem mediaFileSystem, ILogger logger, IContentSection contentSection) + public UploadAutoFillProperties(IMediaFileSystem mediaFileSystem, ILogger logger, IContentSection contentSection) { _mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentRedirectUrl.cs b/src/Umbraco.Web/Models/ContentEditing/ContentRedirectUrl.cs index 805a6d60be..41fcb98c31 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentRedirectUrl.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentRedirectUrl.cs @@ -20,5 +20,8 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "contentId")] public int ContentId { get; set; } + + [DataMember(Name = "culture")] + public string Culture { get; set; } } } diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentTypeBasic.cs b/src/Umbraco.Web/Models/ContentEditing/ContentTypeBasic.cs index ba6fca5fd7..6aca5d8054 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentTypeBasic.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentTypeBasic.cs @@ -4,6 +4,7 @@ using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Runtime.Serialization; using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.IO; using Umbraco.Core.Models.Validation; @@ -75,7 +76,7 @@ namespace Umbraco.Web.Models.ContentEditing { return IconIsClass ? string.Empty - : string.Format("{0}images/umbraco/{1}", UmbracoConfig.For.GlobalSettings().Path.EnsureEndsWith("/"), Icon); + : string.Format("{0}images/umbraco/{1}", Current.Configs.Global().Path.EnsureEndsWith("/"), Icon); } } diff --git a/src/Umbraco.Web/Models/ContentEditing/UserInvite.cs b/src/Umbraco.Web/Models/ContentEditing/UserInvite.cs index cb85f0f3db..b18b1539be 100644 --- a/src/Umbraco.Web/Models/ContentEditing/UserInvite.cs +++ b/src/Umbraco.Web/Models/ContentEditing/UserInvite.cs @@ -3,6 +3,7 @@ using System.ComponentModel.DataAnnotations; using System.Linq; using System.Runtime.Serialization; using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration; namespace Umbraco.Web.Models.ContentEditing @@ -33,7 +34,7 @@ namespace Umbraco.Web.Models.ContentEditing if (UserGroups.Any() == false) yield return new ValidationResult("A user must be assigned to at least one group", new[] { "UserGroups" }); - if (UmbracoConfig.For.UmbracoSettings().Security.UsernameIsEmail == false && Username.IsNullOrWhiteSpace()) + if (Current.Configs.Settings().Security.UsernameIsEmail == false && Username.IsNullOrWhiteSpace()) yield return new ValidationResult("A username cannot be empty", new[] { "Username" }); } } diff --git a/src/Umbraco.Web/Models/LoginStatusModel.cs b/src/Umbraco.Web/Models/LoginStatusModel.cs index d9f7ac35d0..d253a364ae 100644 --- a/src/Umbraco.Web/Models/LoginStatusModel.cs +++ b/src/Umbraco.Web/Models/LoginStatusModel.cs @@ -1,9 +1,8 @@ -using System; -using System.ComponentModel.DataAnnotations; -using System.Web; +using System.ComponentModel.DataAnnotations; using Umbraco.Core; -using Umbraco.Web.Composing; +using Umbraco.Core.Composing; using Umbraco.Web.Security; +using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Web.Models { @@ -25,7 +24,7 @@ namespace Umbraco.Web.Models { if (doLookup && Current.UmbracoContext != null) { - var helper = new MembershipHelper(Current.UmbracoContext); + var helper = Current.Factory.GetInstance(); var model = helper.GetCurrentLoginStatus(); if (model != null) { diff --git a/src/Umbraco.Web/Models/Mapping/DataTypeMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/DataTypeMapperProfile.cs index b74d563e88..425e447c9c 100644 --- a/src/Umbraco.Web/Models/Mapping/DataTypeMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/DataTypeMapperProfile.cs @@ -20,7 +20,7 @@ namespace Umbraco.Web.Models.Mapping public DataTypeMapperProfile(PropertyEditorCollection propertyEditors, ILogger logger) { // create, capture, cache - var availablePropertyEditorsResolver = new AvailablePropertyEditorsResolver(UmbracoConfig.For.UmbracoSettings().Content); + var availablePropertyEditorsResolver = new AvailablePropertyEditorsResolver(Current.Configs.Settings().Content); var configurationDisplayResolver = new DataTypeConfigurationFieldDisplayResolver(logger); var databaseTypeResolver = new DatabaseTypeResolver(); diff --git a/src/Umbraco.Web/Models/Mapping/DefaultTemplateResolver.cs b/src/Umbraco.Web/Models/Mapping/DefaultTemplateResolver.cs index ae32e7a691..12caf93681 100644 --- a/src/Umbraco.Web/Models/Mapping/DefaultTemplateResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/DefaultTemplateResolver.cs @@ -1,5 +1,7 @@ using AutoMapper; +using System.Web.Mvc; using Umbraco.Core.Models; +using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Models.Mapping @@ -8,17 +10,24 @@ namespace Umbraco.Web.Models.Mapping { public string Resolve(IContent source, ContentItemDisplay destination, string destMember, ResolutionContext context) { - if (source == null || source.Template == null) return null; + if (source == null) + return null; - var alias = source.Template.Alias; + // If no template id was set... + if (!source.TemplateId.HasValue) + { + // ... and no default template is set, return null... + if (string.IsNullOrWhiteSpace(source.ContentType.DefaultTemplate?.Alias)) + return null; - //set default template if template isn't set - if (string.IsNullOrEmpty(alias)) - alias = source.ContentType.DefaultTemplate == null - ? string.Empty - : source.ContentType.DefaultTemplate.Alias; + // ... otherwise return the content type default template alias. + return source.ContentType.DefaultTemplate?.Alias; + } + + var fileService = DependencyResolver.Current.GetService(); + var template = fileService.GetTemplate(source.TemplateId.Value); - return alias; + return template.Alias; } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/Models/Mapping/MediaMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/MediaMapperProfile.cs index feaa2917fe..4dcaaa2a0f 100644 --- a/src/Umbraco.Web/Models/Mapping/MediaMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/MediaMapperProfile.cs @@ -1,5 +1,6 @@ using AutoMapper; using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Models; @@ -52,7 +53,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.Tabs, opt => opt.ResolveUsing(tabsAndPropertiesResolver)) .ForMember(dest => dest.AdditionalData, opt => opt.Ignore()) .ForMember(dest => dest.ContentType, opt => opt.ResolveUsing(mediaTypeBasicResolver)) - .ForMember(dest => dest.MediaLink, opt => opt.ResolveUsing(content => string.Join(",", content.GetUrls(UmbracoConfig.For.UmbracoSettings().Content, logger)))) + .ForMember(dest => dest.MediaLink, opt => opt.ResolveUsing(content => string.Join(",", content.GetUrls(Current.Configs.Settings().Content, logger)))) .ForMember(dest => dest.ContentApps, opt => opt.ResolveUsing(mediaAppResolver)) .ForMember(dest => dest.VariesByCulture, opt => opt.MapFrom(src => src.ContentType.VariesByCulture())); diff --git a/src/Umbraco.Web/Models/Mapping/MemberTabsAndPropertiesResolver.cs b/src/Umbraco.Web/Models/Mapping/MemberTabsAndPropertiesResolver.cs index 0e4b902196..f64121e09a 100644 --- a/src/Umbraco.Web/Models/Mapping/MemberTabsAndPropertiesResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/MemberTabsAndPropertiesResolver.cs @@ -37,15 +37,6 @@ namespace Umbraco.Web.Models.Mapping _userService = userService ?? throw new System.ArgumentNullException(nameof(userService)); } - public MemberTabsAndPropertiesResolver(IUmbracoContextAccessor umbracoContextAccessor, ILocalizedTextService localizedTextService, IEnumerable ignoreProperties, IMemberService memberService, IUserService userService) - : base(localizedTextService, ignoreProperties) - { - _umbracoContextAccessor = umbracoContextAccessor ?? throw new System.ArgumentNullException(nameof(umbracoContextAccessor)); - _localizedTextService = localizedTextService ?? throw new System.ArgumentNullException(nameof(localizedTextService)); - _memberService = memberService ?? throw new System.ArgumentNullException(nameof(memberService)); - _userService = userService ?? throw new System.ArgumentNullException(nameof(userService)); - } - /// /// Overriden to deal with custom member properties and permissions. public override IEnumerable> Resolve(IMember source, MemberDisplay destination, IEnumerable> destMember, ResolutionContext context) diff --git a/src/Umbraco.Web/Models/Mapping/RedirectUrlMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/RedirectUrlMapperProfile.cs index 33e2164a21..53baa13379 100644 --- a/src/Umbraco.Web/Models/Mapping/RedirectUrlMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/RedirectUrlMapperProfile.cs @@ -8,18 +8,12 @@ namespace Umbraco.Web.Models.Mapping { internal class RedirectUrlMapperProfile : Profile { - private readonly UrlProvider _urlProvider; - - public RedirectUrlMapperProfile(UrlProvider urlProvider) - { - _urlProvider = urlProvider; - } public RedirectUrlMapperProfile() { CreateMap() - .ForMember(x => x.OriginalUrl, expression => expression.MapFrom(item => _urlProvider.GetUrlFromRoute(item.ContentId, item.Url, null))) - .ForMember(x => x.DestinationUrl, expression => expression.Ignore()) + .ForMember(x => x.OriginalUrl, expression => expression.MapFrom(item => Current.UmbracoContext.UrlProvider.GetUrlFromRoute(item.ContentId, item.Url, item.Culture))) + .ForMember(x => x.DestinationUrl, expression => expression.MapFrom(item => item.ContentId > 0 ? new UmbracoHelper(Current.UmbracoContext, Current.Services).Url(item.ContentId, item.Culture) : "#")) .ForMember(x => x.RedirectId, expression => expression.MapFrom(item => item.Key)); } } diff --git a/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesResolver.cs b/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesResolver.cs index 7bbdb85549..ab33c3886b 100644 --- a/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesResolver.cs @@ -27,7 +27,7 @@ namespace Umbraco.Web.Models.Mapping IgnoreProperties = ignoreProperties ?? throw new ArgumentNullException(nameof(ignoreProperties)); } - //TODO: This should deserialize to ListViewConfiguration + //TODO: This should deserialize to ListViewConfiguration private static int GetTabNumberFromConfig(IDictionary listViewConfig) { if (!listViewConfig.TryGetValue("displayAtTabNumber", out var displayTabNum)) @@ -139,13 +139,7 @@ namespace Umbraco.Web.Models.Mapping { public TabsAndPropertiesResolver(ILocalizedTextService localizedTextService) : base(localizedTextService) - { - } - - public TabsAndPropertiesResolver(ILocalizedTextService localizedTextService, IEnumerable ignoreProperties) - : base(localizedTextService, ignoreProperties) - { - } + { } public virtual IEnumerable> Resolve(TSource source, TDestination destination, IEnumerable> destMember, ResolutionContext context) { diff --git a/src/Umbraco.Web/Models/ProfileModel.cs b/src/Umbraco.Web/Models/ProfileModel.cs index 6bc28e6be5..8e66042d21 100644 --- a/src/Umbraco.Web/Models/ProfileModel.cs +++ b/src/Umbraco.Web/Models/ProfileModel.cs @@ -2,10 +2,11 @@ using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; -using System.Web; using System.Web.Mvc; -using Umbraco.Web.Composing; using Umbraco.Web.Security; +using Umbraco.Core; +using Umbraco.Core.Composing; +using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Web.Models { @@ -27,7 +28,7 @@ namespace Umbraco.Web.Models MemberProperties = new List(); if (doLookup && Current.UmbracoContext != null) { - var helper = new MembershipHelper(Current.UmbracoContext); + var helper = Current.Factory.GetInstance(); var model = helper.GetCurrentMemberProfileModel(); MemberProperties = model.MemberProperties; } diff --git a/src/Umbraco.Web/Models/PublishedContentBase.cs b/src/Umbraco.Web/Models/PublishedContentBase.cs index 667cf145bd..2d00370f14 100644 --- a/src/Umbraco.Web/Models/PublishedContentBase.cs +++ b/src/Umbraco.Web/Models/PublishedContentBase.cs @@ -51,7 +51,7 @@ namespace Umbraco.Web.Models public abstract string Path { get; } /// - public abstract int TemplateId { get; } + public abstract int? TemplateId { get; } /// public abstract int CreatorId { get; } diff --git a/src/Umbraco.Web/Models/RegisterModel.cs b/src/Umbraco.Web/Models/RegisterModel.cs index ec96f3a3ae..44845bfd43 100644 --- a/src/Umbraco.Web/Models/RegisterModel.cs +++ b/src/Umbraco.Web/Models/RegisterModel.cs @@ -5,8 +5,9 @@ using System.ComponentModel.DataAnnotations; using System.Web; using System.Web.Mvc; using Umbraco.Core; -using Umbraco.Web.Composing; +using Umbraco.Core.Composing; using Umbraco.Web.Security; +using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Web.Models { @@ -31,7 +32,7 @@ namespace Umbraco.Web.Models CreatePersistentLoginCookie = true; if (doLookup && Current.UmbracoContext != null) { - var helper = new MembershipHelper(Current.UmbracoContext); + var helper = Current.Factory.GetInstance(); var model = helper.CreateRegistrationModel(MemberTypeAlias); MemberProperties = model.MemberProperties; } diff --git a/src/Umbraco.Web/Models/Trees/TreeNode.cs b/src/Umbraco.Web/Models/Trees/TreeNode.cs index 01aa78ca36..6e13773754 100644 --- a/src/Umbraco.Web/Models/Trees/TreeNode.cs +++ b/src/Umbraco.Web/Models/Trees/TreeNode.cs @@ -2,6 +2,7 @@ using Umbraco.Core.IO; using System.Collections.Generic; using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Exceptions; using Umbraco.Web.Models.ContentEditing; @@ -111,7 +112,7 @@ namespace Umbraco.Web.Models.Trees return IOHelper.ResolveUrl("~" + Icon.TrimStart('~')); //legacy icon path - return string.Format("{0}images/umbraco/{1}", UmbracoConfig.For.GlobalSettings().Path.EnsureEndsWith("/"), Icon); + return string.Format("{0}images/umbraco/{1}", Current.Configs.Global().Path.EnsureEndsWith("/"), Icon); } } diff --git a/src/Umbraco.Web/Mvc/ContainerControllerFactory.cs b/src/Umbraco.Web/Mvc/ContainerControllerFactory.cs new file mode 100644 index 0000000000..9262903355 --- /dev/null +++ b/src/Umbraco.Web/Mvc/ContainerControllerFactory.cs @@ -0,0 +1,27 @@ +using System; +using System.Web.Mvc; +using System.Web.Routing; +using Umbraco.Core.Composing; + +namespace Umbraco.Web.Mvc +{ + public class ContainerControllerFactory : DefaultControllerFactory + { + private readonly IFactory _container; + + public ContainerControllerFactory(IFactory container) + { + _container = container; + } + + protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) + { + return (IController) _container.GetInstance(controllerType); + } + + public override void ReleaseController(IController controller) + { + _container.Release(controller); + } + } +} diff --git a/src/Umbraco.Web/Mvc/ControllerFactoryExtensions.cs b/src/Umbraco.Web/Mvc/ControllerFactoryExtensions.cs index 87f7227b82..07a3f2a5e3 100644 --- a/src/Umbraco.Web/Mvc/ControllerFactoryExtensions.cs +++ b/src/Umbraco.Web/Mvc/ControllerFactoryExtensions.cs @@ -14,25 +14,18 @@ namespace Umbraco.Web.Mvc /// /// /// - /// This is related to issue: http://issues.umbraco.org/issue/U4-1726. We already have a method called GetControllerTypeInternal on our MasterControlelrFactory, + /// This is related to issue: http://issues.umbraco.org/issue/U4-1726. We already have a method called GetControllerTypeInternal on our MasterControllerFactory, /// however, we cannot always guarantee that the usage of this will be a MasterControllerFactory like during unit tests. So we needed to create /// this extension method to do the checks instead. /// internal static Type GetControllerTypeInternal(this IControllerFactory factory, RequestContext requestContext, string controllerName) { - var controllerFactory = factory as MasterControllerFactory; - if (controllerFactory != null) - { + if (factory is MasterControllerFactory controllerFactory) return controllerFactory.GetControllerTypeInternal(requestContext, controllerName); - } //we have no choice but to instantiate the controller var instance = factory.CreateController(requestContext, controllerName); - if (instance != null) - { - return instance.GetType(); - } - return null; + return instance?.GetType(); } } } diff --git a/src/Umbraco.Web/Mvc/EnsurePublishedContentRequestAttribute.cs b/src/Umbraco.Web/Mvc/EnsurePublishedContentRequestAttribute.cs index 56d00e8ec9..a28afe1713 100644 --- a/src/Umbraco.Web/Mvc/EnsurePublishedContentRequestAttribute.cs +++ b/src/Umbraco.Web/Mvc/EnsurePublishedContentRequestAttribute.cs @@ -2,9 +2,9 @@ using System.Web.Mvc; using Umbraco.Web.Routing; using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Core.Models.PublishedContent; -using LightInject; -using Umbraco.Web.Composing; +using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Web.Mvc { @@ -74,7 +74,7 @@ namespace Umbraco.Web.Mvc protected UmbracoContext UmbracoContext => _umbracoContext ?? (_umbracoContext = UmbracoContext.Current); // todo - try lazy property injection? - private PublishedRouter PublishedRouter => Core.Composing.Current.Container.GetInstance(); + private PublishedRouter PublishedRouter => Core.Composing.Current.Factory.GetInstance(); /// /// Exposes an UmbracoHelper diff --git a/src/Umbraco.Web/Mvc/FilteredControllerFactoryCollectionBuilder.cs b/src/Umbraco.Web/Mvc/FilteredControllerFactoryCollectionBuilder.cs index 930b5a1eb2..2c4182beac 100644 --- a/src/Umbraco.Web/Mvc/FilteredControllerFactoryCollectionBuilder.cs +++ b/src/Umbraco.Web/Mvc/FilteredControllerFactoryCollectionBuilder.cs @@ -1,14 +1,9 @@ -using LightInject; -using Umbraco.Core.Composing; +using Umbraco.Core.Composing; namespace Umbraco.Web.Mvc { public class FilteredControllerFactoryCollectionBuilder : OrderedCollectionBuilderBase { - public FilteredControllerFactoryCollectionBuilder(IServiceContainer container) - : base(container) - { } - protected override FilteredControllerFactoryCollectionBuilder This => this; } } diff --git a/src/Umbraco.Web/Mvc/IRenderMvcController.cs b/src/Umbraco.Web/Mvc/IRenderMvcController.cs index b290f5f479..492291d7c0 100644 --- a/src/Umbraco.Web/Mvc/IRenderMvcController.cs +++ b/src/Umbraco.Web/Mvc/IRenderMvcController.cs @@ -1,8 +1,5 @@ -using System; -using System.Web.Http.Filters; -using System.Web.Mvc; -using System.Web.Routing; -using System.Windows.Forms; +using System.Web.Mvc; +using Umbraco.Core.Composing; using Umbraco.Web.Models; namespace Umbraco.Web.Mvc @@ -10,7 +7,7 @@ namespace Umbraco.Web.Mvc /// /// The interface that must be implemented for a controller to be designated to execute for route hijacking /// - public interface IRenderMvcController : IRenderController + public interface IRenderMvcController : IRenderController, IDiscoverable { /// /// The default action to render the front-end view diff --git a/src/Umbraco.Web/Mvc/MasterControllerFactory.cs b/src/Umbraco.Web/Mvc/MasterControllerFactory.cs index 20c5c1cfd5..6bc799baa5 100644 --- a/src/Umbraco.Web/Mvc/MasterControllerFactory.cs +++ b/src/Umbraco.Web/Mvc/MasterControllerFactory.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Web.Mvc; using System.Web.Routing; +using Umbraco.Web.Composing; namespace Umbraco.Web.Mvc { @@ -11,7 +12,7 @@ namespace Umbraco.Web.Mvc /// request. Allows circumvention of MVC3's singly registered IControllerFactory. /// /// - internal class MasterControllerFactory : DefaultControllerFactory + internal class MasterControllerFactory : ContainerControllerFactory { private readonly Func _factoriesAccessor; private FilteredControllerFactoryCollection _factories; @@ -21,6 +22,7 @@ namespace Umbraco.Web.Mvc /// /// The factories accessor. public MasterControllerFactory(Func factoriesAccessor) + : base(Current.Factory) { // note // because the MasterControllerFactory needs to be ctored to be assigned to @@ -52,8 +54,8 @@ namespace Umbraco.Web.Mvc { var factory = FactoryForRequest(requestContext); return factory != null - ? factory.CreateController(requestContext, controllerName) - : base.CreateController(requestContext, controllerName); + ? factory.CreateController(requestContext, controllerName) + : base.CreateController(requestContext, controllerName); } /// @@ -74,11 +76,9 @@ namespace Umbraco.Web.Mvc // an instance of the controller to figure out what it is. This is a work around for not having a breaking change for: // http://issues.umbraco.org/issue/U4-1726 - var umbFactory = factory as UmbracoControllerFactory; - if (umbFactory != null) - { + if (factory is UmbracoControllerFactory umbFactory) return umbFactory.GetControllerType(requestContext, controllerName); - } + //we have no choice but to instantiate the controller var instance = factory.CreateController(requestContext, controllerName); return instance?.GetType(); @@ -95,8 +95,8 @@ namespace Umbraco.Web.Mvc public override void ReleaseController(IController icontroller) { var released = false; - var controller = icontroller as Controller; - if (controller != null) + + if (icontroller is Controller controller) { var requestContext = controller.ControllerContext.RequestContext; var factory = FactoryForRequest(requestContext); @@ -106,6 +106,7 @@ namespace Umbraco.Web.Mvc released = true; } } + if (released == false) base.ReleaseController(icontroller); } diff --git a/src/Umbraco.Web/Mvc/PluginController.cs b/src/Umbraco.Web/Mvc/PluginController.cs index 2b4ebc3f71..a8d73fe575 100644 --- a/src/Umbraco.Web/Mvc/PluginController.cs +++ b/src/Umbraco.Web/Mvc/PluginController.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Concurrent; using System.Web.Mvc; -using LightInject; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Logging; @@ -36,38 +35,32 @@ namespace Umbraco.Web.Mvc /// /// Gets or sets the Umbraco context. /// - [Inject] - public virtual UmbracoContext UmbracoContext { get; set; } + public virtual UmbracoContext UmbracoContext { get; } /// /// Gets or sets the database context. /// - [Inject] - public IUmbracoDatabaseFactory DatabaseFactory { get; set; } + public IUmbracoDatabaseFactory DatabaseFactory { get; } /// /// Gets or sets the services context. /// - [Inject] - public ServiceContext Services { get; set; } + public ServiceContext Services { get; } /// /// Gets or sets the application cache. /// - [Inject] - public CacheHelper ApplicationCache { get; set; } + public CacheHelper ApplicationCache { get; } /// /// Gets or sets the logger. /// - [Inject] - public ILogger Logger { get; set; } + public ILogger Logger { get; } /// /// Gets or sets the profiling logger. /// - [Inject] - public ProfilingLogger ProfilingLogger { get; set; } + public IProfilingLogger ProfilingLogger { get; } /// /// Gets the membership helper. @@ -95,6 +88,28 @@ namespace Umbraco.Web.Mvc /// internal PluginControllerMetadata Metadata => GetMetadata(GetType()); + protected PluginController() + : this( + Current.Factory.GetInstance(), + Current.Factory.GetInstance(), + Current.Factory.GetInstance(), + Current.Factory.GetInstance(), + Current.Factory.GetInstance(), + Current.Factory.GetInstance() + ) + { + } + + protected PluginController(UmbracoContext umbracoContext, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, CacheHelper applicationCache, ILogger logger, IProfilingLogger profilingLogger) + { + UmbracoContext = umbracoContext; + DatabaseFactory = databaseFactory; + Services = services; + ApplicationCache = applicationCache; + Logger = logger; + ProfilingLogger = profilingLogger; + } + /// /// Gets metadata for a controller type. /// diff --git a/src/Umbraco.Web/Mvc/RenderControllerFactory.cs b/src/Umbraco.Web/Mvc/RenderControllerFactory.cs index 8158cc8d22..c9734e24b0 100644 --- a/src/Umbraco.Web/Mvc/RenderControllerFactory.cs +++ b/src/Umbraco.Web/Mvc/RenderControllerFactory.cs @@ -10,7 +10,6 @@ namespace Umbraco.Web.Mvc /// public class RenderControllerFactory : UmbracoControllerFactory { - /// /// Determines whether this instance can handle the specified request. /// @@ -34,16 +33,13 @@ namespace Umbraco.Web.Mvc /// public override IController CreateController(RequestContext requestContext, string controllerName) { - var instance = base.CreateController(requestContext, controllerName); - var controllerInstance = instance as Controller; - if (controllerInstance != null) - { - //set the action invoker! - controllerInstance.ActionInvoker = new RenderActionInvoker(); - } - - return instance; + var instance = base.CreateController(requestContext, controllerName); + if (instance is Controller controllerInstance) + { + //set the action invoker! + controllerInstance.ActionInvoker = new RenderActionInvoker(); + } + return instance; } - } } diff --git a/src/Umbraco.Web/Mvc/RenderMvcController.cs b/src/Umbraco.Web/Mvc/RenderMvcController.cs index cdf2717338..44a6a9346d 100644 --- a/src/Umbraco.Web/Mvc/RenderMvcController.cs +++ b/src/Umbraco.Web/Mvc/RenderMvcController.cs @@ -1,7 +1,7 @@ using System; using System.Web.Mvc; -using Umbraco.Core; using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Services; @@ -18,11 +18,18 @@ namespace Umbraco.Web.Mvc { private PublishedRequest _publishedRequest; + // fixme - delete? public RenderMvcController() { ActionInvoker = new RenderActionInvoker(); } + public RenderMvcController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ServiceContext services, CacheHelper applicationCache, ILogger logger, IProfilingLogger profilingLogger) + : base(globalSettings, umbracoContext, services, applicationCache, logger, profilingLogger) + { + ActionInvoker = new RenderActionInvoker(); + } + /// /// Gets the Umbraco context. /// diff --git a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs index 8480c9fb89..29e861aec4 100644 --- a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs +++ b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs @@ -12,7 +12,6 @@ using Umbraco.Web.Models; using Umbraco.Web.Routing; using System.Collections.Generic; using Current = Umbraco.Web.Composing.Current; -using LightInject; using Umbraco.Web.Features; namespace Umbraco.Web.Mvc @@ -31,17 +30,12 @@ namespace Umbraco.Web.Mvc private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly UmbracoContext _umbracoContext; - // fixme - that one could / should accept a PublishedRouter (engine) to work on the PublishedRequest (published content request) public RenderRouteHandler(IUmbracoContextAccessor umbracoContextAccessor, IControllerFactory controllerFactory) { _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); _controllerFactory = controllerFactory ?? throw new ArgumentNullException(nameof(controllerFactory)); } - // fixme - what about that one? - // called by TemplateRenderer - which is created in - // library - could get an engine without problem it's all ugly anyways - // UmbracoComponentRenderer - ?? that one is not so obvious public RenderRouteHandler(UmbracoContext umbracoContext, IControllerFactory controllerFactory) { _umbracoContext = umbracoContext ?? throw new ArgumentNullException(nameof(umbracoContext)); @@ -50,7 +44,7 @@ namespace Umbraco.Web.Mvc private UmbracoContext UmbracoContext => _umbracoContext ?? _umbracoContextAccessor.UmbracoContext; - private UmbracoFeatures Features => Current.Container.GetInstance(); // fixme inject + private UmbracoFeatures Features => Current.Factory.GetInstance(); // fixme inject #region IRouteHandler Members @@ -397,8 +391,6 @@ namespace Umbraco.Web.Mvc if ((request.HasTemplate == false && Features.Disabled.DisableTemplates == false) && routeDef.HasHijackedRoute == false) { - // fixme - better find a way to inject that engine? or at least Current.Engine of some sort! - var engine = Core.Composing.Current.Container.GetInstance(); request.UpdateOnMissingTemplate(); // request will go 404 // HandleHttpResponseStatus returns a value indicating that the request should @@ -427,7 +419,7 @@ namespace Umbraco.Web.Mvc routeDef = GetUmbracoRouteDefinition(requestContext, request); } - //no post values, just route to the controller/action requried (local) + //no post values, just route to the controller/action required (local) requestContext.RouteData.Values["controller"] = routeDef.ControllerName; if (string.IsNullOrWhiteSpace(routeDef.ActionName) == false) @@ -449,7 +441,7 @@ namespace Umbraco.Web.Mvc /// internal static IHttpHandler GetWebFormsHandler() { - return (global::umbraco.UmbracoDefault)BuildManager.CreateInstanceFromVirtualPath("~/default.aspx", typeof(global::umbraco.UmbracoDefault)); + return (umbraco.UmbracoDefault) BuildManager.CreateInstanceFromVirtualPath("~/default.aspx", typeof(umbraco.UmbracoDefault)); } private SessionStateBehavior GetSessionStateBehavior(RequestContext requestContext, string controllerName) diff --git a/src/Umbraco.Web/Mvc/SurfaceController.cs b/src/Umbraco.Web/Mvc/SurfaceController.cs index 3efbfea06d..b3e67e4b2b 100644 --- a/src/Umbraco.Web/Mvc/SurfaceController.cs +++ b/src/Umbraco.Web/Mvc/SurfaceController.cs @@ -1,7 +1,11 @@ using System; using Umbraco.Core; using System.Collections.Specialized; +using Umbraco.Core.Cache; +using Umbraco.Core.Logging; using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Persistence; +using Umbraco.Core.Services; namespace Umbraco.Web.Mvc { @@ -12,6 +16,16 @@ namespace Umbraco.Web.Mvc [MergeParentContextViewData] public abstract class SurfaceController : PluginController { + // fixme - delete? + protected SurfaceController() + { + } + + protected SurfaceController(UmbracoContext umbracoContext, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, CacheHelper applicationCache, ILogger logger, IProfilingLogger profilingLogger) + : base(umbracoContext, databaseFactory, services, applicationCache, logger, profilingLogger) + { + } + /// /// Redirects to the Umbraco page with the given id /// diff --git a/src/Umbraco.Web/Mvc/SurfaceControllerTypeCollection.cs b/src/Umbraco.Web/Mvc/SurfaceControllerTypeCollection.cs index ff0288ffe1..30c21fce96 100644 --- a/src/Umbraco.Web/Mvc/SurfaceControllerTypeCollection.cs +++ b/src/Umbraco.Web/Mvc/SurfaceControllerTypeCollection.cs @@ -8,7 +8,7 @@ namespace Umbraco.Web.Mvc // which we are not doing at the moment // we can inherit from BuilderCollectionBase and just be enumerable - internal class SurfaceControllerTypeCollection : BuilderCollectionBase + public class SurfaceControllerTypeCollection : BuilderCollectionBase { public SurfaceControllerTypeCollection(IEnumerable items) : base(items) diff --git a/src/Umbraco.Web/Mvc/UmbracoAuthorizeAttribute.cs b/src/Umbraco.Web/Mvc/UmbracoAuthorizeAttribute.cs index 2930e0a9ba..8b76a2069d 100644 --- a/src/Umbraco.Web/Mvc/UmbracoAuthorizeAttribute.cs +++ b/src/Umbraco.Web/Mvc/UmbracoAuthorizeAttribute.cs @@ -57,7 +57,7 @@ namespace Umbraco.Web.Mvc { if (redirectToUmbracoLogin) { - _redirectUrl = UmbracoConfig.For.GlobalSettings().Path.EnsureStartsWith("~"); + _redirectUrl = Current.Configs.Global().Path.EnsureStartsWith("~"); } } @@ -94,7 +94,7 @@ namespace Umbraco.Web.Mvc { filterContext.Result = (ActionResult)new HttpUnauthorizedResult("You must login to view this resource."); - + } else { diff --git a/src/Umbraco.Web/Mvc/UmbracoAuthorizedController.cs b/src/Umbraco.Web/Mvc/UmbracoAuthorizedController.cs index 940b6fe166..2f8f7a6e76 100644 --- a/src/Umbraco.Web/Mvc/UmbracoAuthorizedController.cs +++ b/src/Umbraco.Web/Mvc/UmbracoAuthorizedController.cs @@ -1,4 +1,10 @@ -namespace Umbraco.Web.Mvc +using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence; +using Umbraco.Core.Services; + +namespace Umbraco.Web.Mvc { /// /// Provides a base class for authorized Umbraco controllers. @@ -10,5 +16,12 @@ [UmbracoAuthorize] [DisableBrowserCache] public abstract class UmbracoAuthorizedController : UmbracoController - { } + { + protected UmbracoAuthorizedController() + { } + + protected UmbracoAuthorizedController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ServiceContext services, CacheHelper applicationCache, ILogger logger, IProfilingLogger profilingLogger) + : base(globalSettings, umbracoContext, services, applicationCache, logger, profilingLogger) + { } + } } diff --git a/src/Umbraco.Web/Mvc/UmbracoController.cs b/src/Umbraco.Web/Mvc/UmbracoController.cs index b8b55ae35d..36f688b714 100644 --- a/src/Umbraco.Web/Mvc/UmbracoController.cs +++ b/src/Umbraco.Web/Mvc/UmbracoController.cs @@ -1,13 +1,12 @@ using System; using System.Web; using System.Web.Mvc; -using LightInject; using Microsoft.Owin; -using Umbraco.Core; using Umbraco.Core.Cache; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; -using Umbraco.Core.Persistence; +using Umbraco.Core; using Umbraco.Core.Services; using Umbraco.Web.Security; @@ -29,48 +28,37 @@ namespace Umbraco.Web.Mvc // don't need to implement complex constructors + need to refactor them each time we change ours. // this means that these properties have a setter. // what can go wrong? + // fixme clear this comment /// /// Gets or sets the Umbraco context. /// - [Inject] public virtual IGlobalSettings GlobalSettings { get; set; } /// /// Gets or sets the Umbraco context. /// - [Inject] public virtual UmbracoContext UmbracoContext { get; set; } - /// - /// Gets or sets the database context. - /// - [Inject] - public IUmbracoDatabaseFactory DatabaseFactory { get; set; } - /// /// Gets or sets the services context. /// - [Inject] public ServiceContext Services { get; set; } /// /// Gets or sets the application cache. /// - [Inject] public CacheHelper ApplicationCache { get; set; } /// /// Gets or sets the logger. /// - [Inject] public ILogger Logger { get; set; } /// /// Gets or sets the profiling logger. /// - [Inject] - public ProfilingLogger ProfilingLogger { get; set; } + public IProfilingLogger ProfilingLogger { get; set; } protected IOwinContext OwinContext => Request.GetOwinContext(); @@ -89,5 +77,27 @@ namespace Umbraco.Web.Mvc /// Gets the web security helper. /// public virtual WebSecurity Security => UmbracoContext.Security; + + protected UmbracoController() + : this( + Current.Factory.GetInstance(), + Current.Factory.GetInstance(), + Current.Factory.GetInstance(), + Current.Factory.GetInstance(), + Current.Factory.GetInstance(), + Current.Factory.GetInstance() + ) + { + } + + protected UmbracoController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ServiceContext services, CacheHelper applicationCache, ILogger logger, IProfilingLogger profilingLogger) + { + GlobalSettings = globalSettings; + UmbracoContext = umbracoContext; + Services = services; + ApplicationCache = applicationCache; + Logger = logger; + ProfilingLogger = profilingLogger; + } } } diff --git a/src/Umbraco.Web/Mvc/UmbracoControllerFactory.cs b/src/Umbraco.Web/Mvc/UmbracoControllerFactory.cs index 2c68718ec2..1c041d417c 100644 --- a/src/Umbraco.Web/Mvc/UmbracoControllerFactory.cs +++ b/src/Umbraco.Web/Mvc/UmbracoControllerFactory.cs @@ -65,8 +65,12 @@ namespace Umbraco.Web.Mvc /// this nested class changes the visibility of 's internal methods in order to not have to rely on a try-catch. /// /// - internal class OverridenDefaultControllerFactory : DefaultControllerFactory + internal class OverridenDefaultControllerFactory : ContainerControllerFactory { + public OverridenDefaultControllerFactory() + : base(Current.Factory) + { } + public new IController GetControllerInstance(RequestContext requestContext, Type controllerType) { return base.GetControllerInstance(requestContext, controllerType); @@ -74,11 +78,9 @@ namespace Umbraco.Web.Mvc public new Type GetControllerType(RequestContext requestContext, string controllerName) { - if (controllerName.IsNullOrWhiteSpace()) - { - return null; - } - return base.GetControllerType(requestContext, controllerName); + return controllerName.IsNullOrWhiteSpace() + ? null + : base.GetControllerType(requestContext, controllerName); } } } diff --git a/src/Umbraco.Web/Mvc/UmbracoPageResult.cs b/src/Umbraco.Web/Mvc/UmbracoPageResult.cs index f23407ab4c..9769945f37 100644 --- a/src/Umbraco.Web/Mvc/UmbracoPageResult.cs +++ b/src/Umbraco.Web/Mvc/UmbracoPageResult.cs @@ -15,9 +15,9 @@ namespace Umbraco.Web.Mvc ///
public class UmbracoPageResult : ActionResult { - private readonly ProfilingLogger _profilingLogger; + private readonly IProfilingLogger _profilingLogger; - public UmbracoPageResult(ProfilingLogger profilingLogger) + public UmbracoPageResult(IProfilingLogger profilingLogger) { _profilingLogger = profilingLogger; } diff --git a/src/Umbraco.Web/Mvc/UmbracoRequireHttpsAttribute.cs b/src/Umbraco.Web/Mvc/UmbracoRequireHttpsAttribute.cs index 589090c08e..366f5de8db 100644 --- a/src/Umbraco.Web/Mvc/UmbracoRequireHttpsAttribute.cs +++ b/src/Umbraco.Web/Mvc/UmbracoRequireHttpsAttribute.cs @@ -1,4 +1,6 @@ using System.Web.Mvc; +using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using GlobalSettings = Umbraco.Core.Configuration.GlobalSettings; @@ -16,7 +18,7 @@ namespace Umbraco.Web.Mvc protected override void HandleNonHttpsRequest(AuthorizationContext filterContext) { // If umbracoUseSSL is set, let base method handle redirect. Otherwise, we don't care. - if (UmbracoConfig.For.GlobalSettings().UseHttps) + if (Current.Configs.Global().UseHttps) { base.HandleNonHttpsRequest(filterContext); } @@ -29,7 +31,7 @@ namespace Umbraco.Web.Mvc public override void OnAuthorization(AuthorizationContext filterContext) { // If umbracoSSL is set, let base method handle checking for HTTPS. Otherwise, we don't care. - if (UmbracoConfig.For.GlobalSettings().UseHttps) + if (Current.Configs.Global().UseHttps) { base.OnAuthorization(filterContext); } diff --git a/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs b/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs index bd1dd1ed6b..394aa39dd9 100644 --- a/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs +++ b/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs @@ -3,17 +3,17 @@ using System.Text; using System.Web; using System.Web.Mvc; using System.Web.WebPages; -using LightInject; using Umbraco.Core; using Umbraco.Core.Cache; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.IO; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Services; -using Umbraco.Web.Composing; using Umbraco.Web.Models; using Umbraco.Web.Routing; using Umbraco.Web.Security; +using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Web.Mvc { @@ -34,13 +34,11 @@ namespace Umbraco.Web.Mvc /// /// Gets or sets the database context. /// - [Inject] public ServiceContext Services { get; set; } /// /// Gets or sets the application cache. /// - [Inject] public CacheHelper ApplicationCache { get; set; } // fixme @@ -109,6 +107,20 @@ namespace Umbraco.Web.Mvc ///
public MembershipHelper Members => Umbraco.MembershipHelper; + protected UmbracoViewPage() + : this( + Current.Factory.GetInstance(), + Current.Factory.GetInstance() + ) + { + } + + protected UmbracoViewPage(ServiceContext services, CacheHelper applicationCache) + { + Services = services; + ApplicationCache = applicationCache; + } + // view logic below: /// @@ -204,7 +216,7 @@ namespace Umbraco.Web.Mvc { // creating previewBadge markup markupToInject = - string.Format(UmbracoConfig.For.UmbracoSettings().Content.PreviewBadge, + string.Format(Current.Configs.Settings().Content.PreviewBadge, IOHelper.ResolveUrl(SystemDirectories.Umbraco), Server.UrlEncode(UmbracoContext.Current.HttpContext.Request.Path)); } diff --git a/src/Umbraco.Web/Mvc/UmbracoVirtualNodeRouteHandler.cs b/src/Umbraco.Web/Mvc/UmbracoVirtualNodeRouteHandler.cs index ab474fa699..164fb7acf7 100644 --- a/src/Umbraco.Web/Mvc/UmbracoVirtualNodeRouteHandler.cs +++ b/src/Umbraco.Web/Mvc/UmbracoVirtualNodeRouteHandler.cs @@ -4,14 +4,15 @@ using System.Web.Routing; using Umbraco.Core.Models.PublishedContent; using Umbraco.Web.Models; using Umbraco.Web.Routing; -using LightInject; +using Umbraco.Core; +using Umbraco.Core.Composing; namespace Umbraco.Web.Mvc { public abstract class UmbracoVirtualNodeRouteHandler : IRouteHandler { // todo - try lazy property injection? - private PublishedRouter PublishedRouter => Core.Composing.Current.Container.GetInstance(); + private PublishedRouter PublishedRouter => Core.Composing.Current.Factory.GetInstance(); /// /// Returns the UmbracoContext for this route handler diff --git a/src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs index 412b8c9766..2b32e9d774 100644 --- a/src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs @@ -15,11 +15,11 @@ namespace Umbraco.Web.PropertyEditors [DataEditor(Constants.PropertyEditors.Aliases.UploadField, "File upload", "fileupload", Icon = "icon-download-alt", Group = "media")] public class FileUploadPropertyEditor : DataEditor { - private readonly MediaFileSystem _mediaFileSystem; + private readonly IMediaFileSystem _mediaFileSystem; private readonly IContentSection _contentSection; private readonly UploadAutoFillProperties _uploadAutoFillProperties; - public FileUploadPropertyEditor(ILogger logger, MediaFileSystem mediaFileSystem, IContentSection contentSection) + public FileUploadPropertyEditor(ILogger logger, IMediaFileSystem mediaFileSystem, IContentSection contentSection) : base(logger) { _mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); diff --git a/src/Umbraco.Web/PropertyEditors/FileUploadPropertyValueEditor.cs b/src/Umbraco.Web/PropertyEditors/FileUploadPropertyValueEditor.cs index 47711507b2..f36dd6bfa8 100644 --- a/src/Umbraco.Web/PropertyEditors/FileUploadPropertyValueEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/FileUploadPropertyValueEditor.cs @@ -15,9 +15,9 @@ namespace Umbraco.Web.PropertyEditors /// internal class FileUploadPropertyValueEditor : DataValueEditor { - private readonly MediaFileSystem _mediaFileSystem; + private readonly IMediaFileSystem _mediaFileSystem; - public FileUploadPropertyValueEditor(DataEditorAttribute attribute, MediaFileSystem mediaFileSystem) + public FileUploadPropertyValueEditor(DataEditorAttribute attribute, IMediaFileSystem mediaFileSystem) : base(attribute) { _mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); diff --git a/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyEditor.cs index c8a7bfc80c..70b705f397 100644 --- a/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyEditor.cs @@ -22,7 +22,7 @@ namespace Umbraco.Web.PropertyEditors [DataEditor(Constants.PropertyEditors.Aliases.ImageCropper, "Image Cropper", "imagecropper", ValueType = ValueTypes.Json, HideLabel = false, Group="media", Icon="icon-crop")] public class ImageCropperPropertyEditor : DataEditor { - private readonly MediaFileSystem _mediaFileSystem; + private readonly IMediaFileSystem _mediaFileSystem; private readonly IContentSection _contentSettings; private readonly IDataTypeService _dataTypeService; private readonly UploadAutoFillProperties _autoFillProperties; @@ -30,7 +30,7 @@ namespace Umbraco.Web.PropertyEditors /// /// Initializes a new instance of the class. /// - public ImageCropperPropertyEditor(ILogger logger, MediaFileSystem mediaFileSystem, IContentSection contentSettings, IDataTypeService dataTypeService) + public ImageCropperPropertyEditor(ILogger logger, IMediaFileSystem mediaFileSystem, IContentSection contentSettings, IDataTypeService dataTypeService) : base(logger) { _mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); diff --git a/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyValueEditor.cs b/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyValueEditor.cs index 4f44352d34..88da8982c7 100644 --- a/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyValueEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyValueEditor.cs @@ -19,9 +19,9 @@ namespace Umbraco.Web.PropertyEditors internal class ImageCropperPropertyValueEditor : DataValueEditor // fixme core vs web? { private readonly ILogger _logger; - private readonly MediaFileSystem _mediaFileSystem; + private readonly IMediaFileSystem _mediaFileSystem; - public ImageCropperPropertyValueEditor(DataEditorAttribute attribute, ILogger logger, MediaFileSystem mediaFileSystem) + public ImageCropperPropertyValueEditor(DataEditorAttribute attribute, ILogger logger, IMediaFileSystem mediaFileSystem) : base(attribute) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); diff --git a/src/Umbraco.Web/PropertyEditors/PropertyEditorsComponent.cs b/src/Umbraco.Web/PropertyEditors/PropertyEditorsComponent.cs index c4f9f6be15..2fc3badb85 100644 --- a/src/Umbraco.Web/PropertyEditors/PropertyEditorsComponent.cs +++ b/src/Umbraco.Web/PropertyEditors/PropertyEditorsComponent.cs @@ -1,29 +1,33 @@ using System.Linq; -using Examine; -using Umbraco.Core; using Umbraco.Core.Components; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services.Implement; -using Umbraco.Examine; namespace Umbraco.Web.PropertyEditors { - [RuntimeLevel(MinLevel = RuntimeLevel.Run)] - internal class PropertyEditorsComponent : UmbracoComponentBase, IUmbracoCoreComponent + internal sealed class PropertyEditorsComponent : IComponent { - public void Initialize(IRuntimeState runtime, PropertyEditorCollection propertyEditors) + private readonly PropertyEditorCollection _propertyEditors; + + public PropertyEditorsComponent(PropertyEditorCollection propertyEditors) { - var fileUpload = propertyEditors.OfType().FirstOrDefault(); + _propertyEditors = propertyEditors; + } + + public void Initialize() + { + var fileUpload = _propertyEditors.OfType().FirstOrDefault(); if (fileUpload != null) Initialize(fileUpload); - var imageCropper = propertyEditors.OfType().FirstOrDefault(); + var imageCropper = _propertyEditors.OfType().FirstOrDefault(); if (imageCropper != null) Initialize(imageCropper); // grid/examine moved to ExamineComponent } - // as long as these methods are private+static they won't be executed by the boot loader + public void Terminate() + { } private static void Initialize(FileUploadPropertyEditor fileUpload) { diff --git a/src/Umbraco.Web/PropertyEditors/PropertyEditorsComposer.cs b/src/Umbraco.Web/PropertyEditors/PropertyEditorsComposer.cs new file mode 100644 index 0000000000..1a1c218db6 --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/PropertyEditorsComposer.cs @@ -0,0 +1,9 @@ +using Umbraco.Core; +using Umbraco.Core.Components; + +namespace Umbraco.Web.PropertyEditors +{ + [RuntimeLevel(MinLevel = RuntimeLevel.Run)] + internal class PropertyEditorsComposer : ComponentComposer, ICoreComposer + { } +} diff --git a/src/Umbraco.Web/PropertyEditors/UploadFileTypeValidator.cs b/src/Umbraco.Web/PropertyEditors/UploadFileTypeValidator.cs index 9fd9c1ef0e..5394aca5ba 100644 --- a/src/Umbraco.Web/PropertyEditors/UploadFileTypeValidator.cs +++ b/src/Umbraco.Web/PropertyEditors/UploadFileTypeValidator.cs @@ -35,7 +35,7 @@ namespace Umbraco.Web.PropertyEditors { if (fileName.IndexOf('.') <= 0) return false; var extension = Path.GetExtension(fileName).TrimStart("."); - return UmbracoConfig.For.UmbracoSettings().Content.IsFileAllowedForUpload(extension); + return Current.Configs.Settings().Content.IsFileAllowedForUpload(extension); } } } diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs index fee6ee8f8a..93e4afc7e5 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs @@ -16,12 +16,12 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters /// public class NestedContentManyValueConverter : NestedContentValueConverterBase { - private readonly ProfilingLogger _proflog; + private readonly IProfilingLogger _proflog; /// /// Initializes a new instance of the class. /// - public NestedContentManyValueConverter(IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedModelFactory publishedModelFactory, ProfilingLogger proflog) + public NestedContentManyValueConverter(IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedModelFactory publishedModelFactory, IProfilingLogger proflog) : base(publishedSnapshotAccessor, publishedModelFactory) { _proflog = proflog; diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentSingleValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentSingleValueConverter.cs index 52d6c7d53a..e084b3a343 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentSingleValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentSingleValueConverter.cs @@ -15,12 +15,12 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters ///
public class NestedContentSingleValueConverter : NestedContentValueConverterBase { - private readonly ProfilingLogger _proflog; + private readonly IProfilingLogger _proflog; /// /// Initializes a new instance of the class. /// - public NestedContentSingleValueConverter(IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedModelFactory publishedModelFactory, ProfilingLogger proflog) + public NestedContentSingleValueConverter(IPublishedSnapshotAccessor publishedSnapshotAccessor, IPublishedModelFactory publishedModelFactory, IProfilingLogger proflog) : base(publishedSnapshotAccessor, publishedModelFactory) { _proflog = proflog; diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs index 0197d2d640..817c363fa5 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs @@ -29,6 +29,11 @@ namespace Umbraco.Web.PublishedCache.NuCache #region Constructor + // fixme ISSUE + // after the current snapshot has been resync-ed + // it's too late for UmbracoContext which has captured previewDefault and stuff into these ctor vars + // but, no, UmbracoContext returns snapshot.Content which comes from elements SO a resync should create a new cache + public ContentCache(bool previewDefault, ContentStore.Snapshot snapshot, ICacheProvider snapshotCache, ICacheProvider elementsCache, DomainHelper domainHelper, IGlobalSettings globalSettings, ILocalizationService localizationService) : base(previewDefault) { diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.ContentDataSerializer.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.ContentDataSerializer.cs index 7c793b69bd..80633efe2e 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.ContentDataSerializer.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.ContentDataSerializer.cs @@ -30,9 +30,12 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource PrimitiveSerializer.Int32.WriteTo(value.VersionId, stream); PrimitiveSerializer.DateTime.WriteTo(value.VersionDate, stream); PrimitiveSerializer.Int32.WriteTo(value.WriterId, stream); - PrimitiveSerializer.Int32.WriteTo(value.TemplateId, stream); + if (value.TemplateId.HasValue) + { + PrimitiveSerializer.Int32.WriteTo(value.TemplateId.Value, stream); + } PropertiesSerializer.WriteTo(value.Properties, stream); CultureVariationsSerializer.WriteTo(value.CultureInfos, stream); } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentData.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentData.cs index 4721a1f4ca..520cc99011 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentData.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentData.cs @@ -10,7 +10,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource public int VersionId { get; set; } public DateTime VersionDate { get; set; } public int WriterId { get; set; } - public int TemplateId { get; set; } + public int? TemplateId { get; set; } public bool Published { get; set; } public IDictionary Properties { get; set; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComponent.cs b/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComponent.cs index 1d497d73e0..089ff6a8ea 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComponent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComponent.cs @@ -1,30 +1,18 @@ using Umbraco.Core.Components; -using Umbraco.Web.PublishedCache.NuCache.DataSource; namespace Umbraco.Web.PublishedCache.NuCache { - public class NuCacheComponent : UmbracoComponentBase, IUmbracoCoreComponent + public sealed class NuCacheComponent : IComponent { - public override void Compose(Composition composition) - { - base.Compose(composition); - - // register the NuCache database data source - composition.Container.Register(); - - // register the NuCache published snapshot service - // must register default options, required in the service ctor - composition.Container.Register(factory => new PublishedSnapshotService.Options()); - composition.SetPublishedSnapshotService(); - - // add the NuCache health check (hidden from type finder) - // todo - no NuCache health check yet - //composition.HealthChecks().Add(); - } - - public void Initialize(IPublishedSnapshotService service) + public NuCacheComponent(IPublishedSnapshotService service) { // nothing - this just ensures that the service is created at boot time } + + public void Initialize() + { } + + public void Terminate() + { } } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComposer.cs b/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComposer.cs new file mode 100644 index 0000000000..0423084285 --- /dev/null +++ b/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComposer.cs @@ -0,0 +1,26 @@ +using Umbraco.Core.Components; +using Umbraco.Core.Composing; +using Umbraco.Web.PublishedCache.NuCache.DataSource; + +namespace Umbraco.Web.PublishedCache.NuCache +{ + public class NuCacheComposer : ComponentComposer, ICoreComposer + { + public override void Compose(Composition composition) + { + base.Compose(composition); + + // register the NuCache database data source + composition.Register(); + + // register the NuCache published snapshot service + // must register default options, required in the service ctor + composition.Register(factory => new PublishedSnapshotService.Options()); + composition.SetPublishedSnapshotService(); + + // add the NuCache health check (hidden from type finder) + // todo - no NuCache health check yet + //composition.HealthChecks().Add(); + } + } +} diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index 36e5698e32..25e5244d32 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -215,7 +215,7 @@ namespace Umbraco.Web.PublishedCache.NuCache public override string Path => _contentNode.Path; /// - public override int TemplateId => _contentData.TemplateId; + public override int? TemplateId => _contentData.TemplateId; /// public override int CreatorId => _contentNode.CreatorId; diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index 4df0cf2ae7..4d61e50f6f 100755 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -80,7 +80,7 @@ namespace Umbraco.Web.PublishedCache.NuCache //private static int _singletonCheck; - public PublishedSnapshotService(Options options, MainDom mainDom, IRuntimeState runtime, + public PublishedSnapshotService(Options options, IMainDom mainDom, IRuntimeState runtime, ServiceContext serviceContext, IPublishedContentTypeFactory publishedContentTypeFactory, IdkMap idkMap, IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor, ILogger logger, IScopeProvider scopeProvider, diff --git a/src/Umbraco.Web/PublishedCache/PublishedMember.cs b/src/Umbraco.Web/PublishedCache/PublishedMember.cs index 56c8f440d8..46abc098cc 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedMember.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedMember.cs @@ -129,7 +129,7 @@ namespace Umbraco.Web.PublishedCache public override Guid Key => _member.Key; - public override int TemplateId => throw new NotSupportedException(); + public override int? TemplateId => throw new NotSupportedException(); public override int SortOrder => 0; diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs index 7c311236c0..a845be286f 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs @@ -148,7 +148,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache public override Guid Key => _key; - public override int TemplateId => 0; + public override int? TemplateId => null; public override int SortOrder => _sortOrder; diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs index 229a981510..9e45bc17c5 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs @@ -239,9 +239,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache try { - if (eMgr.TryGetIndex(Constants.UmbracoIndexes.InternalIndexName, out var index)) - return index.GetSearcher(); - throw new InvalidOperationException($"No index found by name {Constants.UmbracoIndexes.InternalIndexName}"); + return eMgr.TryGetIndex(Constants.UmbracoIndexes.InternalIndexName, out var index) ? index.GetSearcher() : null; } catch (FileNotFoundException) { diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs index af867cc089..efd4535bd4 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs @@ -109,7 +109,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache } } - public override int TemplateId + public override int? TemplateId { get { diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlStore.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlStore.cs index 5b816c2f26..fef5039579 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlStore.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlStore.cs @@ -110,7 +110,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache _mediaRepository = mediaRepository; _memberRepository = memberRepository; _xmlFileEnabled = false; - _xmlFileName = IOHelper.MapPath(SystemFiles.GetContentCacheXml(UmbracoConfig.For.GlobalSettings())); + _xmlFileName = IOHelper.MapPath(SystemFiles.GetContentCacheXml(Current.Configs.Global())); // do not plug events, we may not have what it takes to handle them } @@ -124,7 +124,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache _memberRepository = memberRepository; GetXmlDocument = getXmlDocument ?? throw new ArgumentNullException(nameof(getXmlDocument)); _xmlFileEnabled = false; - _xmlFileName = IOHelper.MapPath(SystemFiles.GetContentCacheXml(UmbracoConfig.For.GlobalSettings())); + _xmlFileName = IOHelper.MapPath(SystemFiles.GetContentCacheXml(Current.Configs.Global())); // do not plug events, we may not have what it takes to handle them } @@ -253,16 +253,16 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache private readonly bool _xmlFileEnabled = true; // whether the disk cache is enabled - private bool XmlFileEnabled => _xmlFileEnabled && UmbracoConfig.For.UmbracoSettings().Content.XmlCacheEnabled; + private bool XmlFileEnabled => _xmlFileEnabled && Current.Configs.Settings().Content.XmlCacheEnabled; // whether the disk cache is enabled and to update the disk cache when xml changes - private bool SyncToXmlFile => XmlFileEnabled && UmbracoConfig.For.UmbracoSettings().Content.ContinouslyUpdateXmlDiskCache; + private bool SyncToXmlFile => XmlFileEnabled && Current.Configs.Settings().Content.ContinouslyUpdateXmlDiskCache; // whether the disk cache is enabled and to reload from disk cache if it changes - private bool SyncFromXmlFile => XmlFileEnabled && UmbracoConfig.For.UmbracoSettings().Content.XmlContentCheckForDiskChanges; + private bool SyncFromXmlFile => XmlFileEnabled && Current.Configs.Settings().Content.XmlContentCheckForDiskChanges; // whether _xml is immutable or not (achieved by cloning before changing anything) - private static bool XmlIsImmutable => UmbracoConfig.For.UmbracoSettings().Content.CloneXmlContent; + private static bool XmlIsImmutable => Current.Configs.Settings().Content.CloneXmlContent; // whether to keep version of everything (incl. medias & members) in cmsPreviewXml // for audit purposes - false by default, not in umbracoSettings.config @@ -1241,7 +1241,7 @@ ORDER BY umbracoNode.level, umbracoNode.sortOrder"; // the types will be reloaded if/when needed foreach (var payload in payloads) _contentTypeCache.ClearDataType(payload.Id); - + foreach (var payload in payloads) Current.Logger.Debug("Notified {RemovedStatus} for data type {payload.Id}", payload.Removed ? "Removed" : "Refreshed", diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index 709b1ff9e6..d4494c5f91 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -88,10 +88,10 @@ namespace Umbraco.Web public static bool IsAllowedTemplate(this IPublishedContent content, int templateId) { - if (UmbracoConfig.For.UmbracoSettings().WebRouting.DisableAlternativeTemplates == true) + if (Current.Configs.Settings().WebRouting.DisableAlternativeTemplates == true) return content.TemplateId == templateId; - if (content.TemplateId != templateId && UmbracoConfig.For.UmbracoSettings().WebRouting.ValidateAlternativeTemplates == true) + if (content.TemplateId != templateId && Current.Configs.Settings().WebRouting.ValidateAlternativeTemplates == true) { // fixme - perfs? nothing cached here var publishedContentContentType = Current.Services.ContentTypeService.Get(content.ContentType.Id); @@ -133,10 +133,15 @@ namespace Umbraco.Web /// Returns the current template Alias ///
/// - /// + /// Empty string if none is set. public static string GetTemplateAlias(this IPublishedContent content) { - var template = Current.Services.FileService.GetTemplate(content.TemplateId); + if(content.TemplateId.HasValue == false) + { + return string.Empty; + } + + var template = Current.Services.FileService.GetTemplate(content.TemplateId.Value); return template == null ? string.Empty : template.Alias; } diff --git a/src/Umbraco.Web/Routing/ContentFinderByRedirectUrl.cs b/src/Umbraco.Web/Routing/ContentFinderByRedirectUrl.cs index e2b174753a..bd85d02dab 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByRedirectUrl.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByRedirectUrl.cs @@ -45,7 +45,7 @@ namespace Umbraco.Web.Routing } var content = frequest.UmbracoContext.ContentCache.GetById(redirectUrl.ContentId); - var url = content == null ? "#" : content.Url; + var url = content == null ? "#" : content.GetUrl(redirectUrl.Culture); if (url.StartsWith("#")) { _logger.Debug("Route {Route} matches content {ContentId} which has no url.", route, redirectUrl.ContentId); @@ -54,14 +54,14 @@ namespace Umbraco.Web.Routing // Apending any querystring from the incoming request to the redirect url. url = string.IsNullOrEmpty(frequest.Uri.Query) ? url : url + frequest.Uri.Query; - + _logger.Debug("Route {Route} matches content {ContentId} with url '{Url}', redirecting.", route, content.Id, url); frequest.SetRedirectPermanent(url); - + // From: http://stackoverflow.com/a/22468386/5018 // See http://issues.umbraco.org/issue/U4-8361#comment=67-30532 - // Setting automatic 301 redirects to not be cached because browsers cache these very aggressively which then leads + // Setting automatic 301 redirects to not be cached because browsers cache these very aggressively which then leads // to problems if you rename a page back to it's original name or create a new page with the original name frequest.Cacheability = HttpCacheability.NoCache; frequest.CacheExtensions = new List { "no-store, must-revalidate" }; diff --git a/src/Umbraco.Web/Routing/ContentFinderCollectionBuilder.cs b/src/Umbraco.Web/Routing/ContentFinderCollectionBuilder.cs index 7d9ef952c4..401a7daf43 100644 --- a/src/Umbraco.Web/Routing/ContentFinderCollectionBuilder.cs +++ b/src/Umbraco.Web/Routing/ContentFinderCollectionBuilder.cs @@ -1,14 +1,9 @@ -using LightInject; -using Umbraco.Core.Composing; +using Umbraco.Core.Composing; namespace Umbraco.Web.Routing { public class ContentFinderCollectionBuilder : OrderedCollectionBuilderBase { - public ContentFinderCollectionBuilder(IServiceContainer container) - : base(container) - { } - protected override ContentFinderCollectionBuilder This => this; } } diff --git a/src/Umbraco.Web/Routing/PublishedRequest.cs b/src/Umbraco.Web/Routing/PublishedRequest.cs index 078aef8a54..6814761572 100644 --- a/src/Umbraco.Web/Routing/PublishedRequest.cs +++ b/src/Umbraco.Web/Routing/PublishedRequest.cs @@ -4,6 +4,8 @@ using System.Globalization; using System.Threading; using System.Web; using umbraco; +using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; @@ -180,7 +182,7 @@ namespace Umbraco.Web.Routing IsInternalRedirectPublishedContent = isInternalRedirect; // must restore the template if it's an internal redirect & the config option is set - if (isInternalRedirect && UmbracoConfig.For.UmbracoSettings().WebRouting.InternalRedirectPreservesTemplate) + if (isInternalRedirect && Current.Configs.Settings().WebRouting.InternalRedirectPreservesTemplate) { // restore _template = template; diff --git a/src/Umbraco.Web/Routing/PublishedRouter.cs b/src/Umbraco.Web/Routing/PublishedRouter.cs index 1122aaa11a..3d3b623838 100644 --- a/src/Umbraco.Web/Routing/PublishedRouter.cs +++ b/src/Umbraco.Web/Routing/PublishedRouter.cs @@ -7,6 +7,7 @@ using System.IO; using System.Web.Security; using umbraco; using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; using Umbraco.Core.Logging; @@ -20,13 +21,13 @@ namespace Umbraco.Web.Routing { // fixme - make this public // fixme - making sense to have an interface? - internal class PublishedRouter + public class PublishedRouter { private readonly IWebRoutingSection _webRoutingSection; private readonly ContentFinderCollection _contentFinders; private readonly IContentLastChanceFinder _contentLastChanceFinder; private readonly ServiceContext _services; - private readonly ProfilingLogger _profilingLogger; + private readonly IProfilingLogger _profilingLogger; private readonly IVariationContextAccessor _variationContextAccessor; private readonly ILogger _logger; @@ -39,8 +40,7 @@ namespace Umbraco.Web.Routing IContentLastChanceFinder contentLastChanceFinder, IVariationContextAccessor variationContextAccessor, ServiceContext services, - ProfilingLogger proflog, - Func> getRolesForLogin = null) + IProfilingLogger proflog) { _webRoutingSection = webRoutingSection ?? throw new ArgumentNullException(nameof(webRoutingSection)); _contentFinders = contentFinders ?? throw new ArgumentNullException(nameof(contentFinders)); @@ -48,9 +48,9 @@ namespace Umbraco.Web.Routing _services = services ?? throw new ArgumentNullException(nameof(services)); _profilingLogger = proflog ?? throw new ArgumentNullException(nameof(proflog)); _variationContextAccessor = variationContextAccessor ?? throw new ArgumentNullException(nameof(variationContextAccessor)); - _logger = proflog.Logger; + _logger = proflog; - GetRolesForLogin = getRolesForLogin ?? (s => Roles.Provider.GetRolesForUser(s)); + GetRolesForLogin = s => Roles.Provider.GetRolesForUser(s); } // fixme @@ -626,7 +626,7 @@ namespace Umbraco.Web.Routing { _logger.Debug("EnsurePublishedContentAccess: Page is protected, check for access"); - var membershipHelper = new MembershipHelper(request.UmbracoContext); + var membershipHelper = Current.Factory.GetInstance(); if (membershipHelper.IsLoggedIn() == false) { @@ -755,9 +755,9 @@ namespace Umbraco.Web.Routing } } - private ITemplate GetTemplateModel(int templateId) + private ITemplate GetTemplateModel(int? templateId) { - if (templateId <= 0) + if (templateId.HasValue == false) { _logger.Debug("GetTemplateModel: No template."); return null; @@ -765,7 +765,10 @@ namespace Umbraco.Web.Routing _logger.Debug("GetTemplateModel: Get template id={TemplateId}", templateId); - var template = _services.FileService.GetTemplate(templateId); + if (templateId == null) + throw new InvalidOperationException("The template is not set, the page cannot render."); + + var template = _services.FileService.GetTemplate(templateId.Value); if (template == null) throw new InvalidOperationException("The template with Id " + templateId + " does not exist, the page cannot render."); _logger.Debug("GetTemplateModel: Got template id={TemplateId} alias={TemplateAlias}", template.Id, template.Alias); diff --git a/src/Umbraco.Web/Routing/RedirectTrackingComponent.cs b/src/Umbraco.Web/Routing/RedirectTrackingComponent.cs index 2558f18077..669039f087 100644 --- a/src/Umbraco.Web/Routing/RedirectTrackingComponent.cs +++ b/src/Umbraco.Web/Routing/RedirectTrackingComponent.cs @@ -1,11 +1,13 @@ using System; -using Umbraco.Core; -using Umbraco.Core.Models; -using Umbraco.Core.Services; -using Umbraco.Core.Events; using System.Collections.Generic; +using System.Linq; using Umbraco.Core.Cache; using Umbraco.Core.Components; +using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Events; +using Umbraco.Core.Models; +using Umbraco.Core.Services; using Umbraco.Core.Services.Implement; using Umbraco.Core.Sync; using Umbraco.Web.Cache; @@ -13,24 +15,72 @@ using Umbraco.Web.Composing; namespace Umbraco.Web.Redirects { - /// /// Implements an Application Event Handler for managing redirect urls tracking. - /// - /// /// when content is renamed or moved, we want to create a permanent 301 redirect from it's old url - /// not managing domains because we don't know how to do it - changing domains => must create a higher level strategy using rewriting rules probably + /// + /// not managing domains because we don't know how to do it - changing domains => must create a higher level + /// strategy using rewriting rules probably + /// /// recycle bin = moving to and from does nothing: to = the node is gone, where would we redirect? from = same - /// - [RuntimeLevel(MinLevel = RuntimeLevel.Run)] - [DisableComponent] // fixme - re-enable when we fix redirect tracking with variants - public class RedirectTrackingComponent : UmbracoComponentBase, IUmbracoCoreComponent + public sealed class RedirectTrackingComponent : IComponent { private const string ContextKey1 = "Umbraco.Web.Redirects.RedirectTrackingEventHandler.1"; private const string ContextKey2 = "Umbraco.Web.Redirects.RedirectTrackingEventHandler.2"; private const string ContextKey3 = "Umbraco.Web.Redirects.RedirectTrackingEventHandler.3"; - protected void Initialize() + private readonly IUmbracoSettingsSection _umbracoSettings; + + public RedirectTrackingComponent(IUmbracoSettingsSection umbracoSettings) { + _umbracoSettings = umbracoSettings; + } + + private static Dictionary OldRoutes + { + get + { + var oldRoutes = + (Dictionary) UmbracoContext.Current.HttpContext.Items[ + ContextKey3]; + if (oldRoutes == null) + UmbracoContext.Current.HttpContext.Items[ContextKey3] = + oldRoutes = new Dictionary(); + return oldRoutes; + } + } + + private static bool LockedEvents + { + get => Moving && UmbracoContext.Current.HttpContext.Items[ContextKey2] != null; + set + { + if (Moving && value) + UmbracoContext.Current.HttpContext.Items[ContextKey2] = true; + else + UmbracoContext.Current.HttpContext.Items.Remove(ContextKey2); + } + } + + private static bool Moving + { + get => UmbracoContext.Current.HttpContext.Items[ContextKey1] != null; + set + { + if (value) + UmbracoContext.Current.HttpContext.Items[ContextKey1] = true; + else + { + UmbracoContext.Current.HttpContext.Items.Remove(ContextKey1); + UmbracoContext.Current.HttpContext.Items.Remove(ContextKey2); + } + } + } + + public void Initialize() + { + // don't let the event handlers kick in if Redirect Tracking is turned off in the config + if (_umbracoSettings.WebRouting.DisableRedirectUrlTracking) return; + // events are weird // on 'published' we 'could' get the old or the new route depending on event handlers order // so it is not reliable. getting the old route in 'publishing' to be sure and storing in http @@ -50,6 +100,7 @@ namespace Umbraco.Web.Redirects ContentService.Moved += ContentService_Moved; ContentCacheRefresher.CacheUpdated += ContentCacheRefresher_CacheUpdated; + // kill all redirects once a content is deleted //ContentService.Deleted += ContentService_Deleted; // BUT, doing it here would prevent content deletion due to FK @@ -58,69 +109,43 @@ namespace Umbraco.Web.Redirects // rolled back items have to be published, so publishing will take care of that } - private static void ContentCacheRefresher_CacheUpdated(ContentCacheRefresher sender, CacheRefresherEventArgs args) + public void Terminate() + { } + + private static void ContentCacheRefresher_CacheUpdated(ContentCacheRefresher sender, + CacheRefresherEventArgs args) { // sanity checks - if (args.MessageType != MessageType.RefreshByPayload) + { throw new InvalidOperationException("ContentCacheRefresher MessageType should be ByPayload."); + } + if (args.MessageObject == null) + { + return; + } - if (args.MessageObject == null) return; var payloads = args.MessageObject as ContentCacheRefresher.JsonPayload[]; if (payloads == null) + { throw new InvalidOperationException("ContentCacheRefresher MessageObject should be JsonPayload[]."); + } // manage routes - - var removeKeys = new List(); + var removeKeys = new List(); foreach (var oldRoute in OldRoutes) { // assuming we cannot have 'CacheUpdated' for only part of the infos else we'd need // to set a flag in 'Published' to indicate which entities have been refreshed ok - CreateRedirect(oldRoute.Key, oldRoute.Value.Item1, oldRoute.Value.Item2); + CreateRedirect(oldRoute.Key.ContentId, oldRoute.Key.Culture, oldRoute.Value.ContentKey, + oldRoute.Value.OldRoute); removeKeys.Add(oldRoute.Key); } foreach (var k in removeKeys) + { OldRoutes.Remove(k); - } - - private static Dictionary> OldRoutes - { - get - { - var oldRoutes = (Dictionary>) UmbracoContext.Current.HttpContext.Items[ContextKey3]; - if (oldRoutes == null) - UmbracoContext.Current.HttpContext.Items[ContextKey3] = oldRoutes = new Dictionary>(); - return oldRoutes; - } - } - - private static bool LockedEvents - { - get { return Moving && UmbracoContext.Current.HttpContext.Items[ContextKey2] != null; } - set - { - if (Moving && value) - UmbracoContext.Current.HttpContext.Items[ContextKey2] = true; - else - UmbracoContext.Current.HttpContext.Items.Remove(ContextKey2); - } - } - - private static bool Moving - { - get { return UmbracoContext.Current.HttpContext.Items[ContextKey1] != null; } - set - { - if (value) - UmbracoContext.Current.HttpContext.Items[ContextKey1] = true; - else - { - UmbracoContext.Current.HttpContext.Items.Remove(ContextKey1); - UmbracoContext.Current.HttpContext.Items.Remove(ContextKey2); - } } } @@ -135,9 +160,14 @@ namespace Umbraco.Web.Redirects if (entityContent == null) continue; foreach (var x in entityContent.DescendantsOrSelf()) { - var route = contentCache.GetRouteById(x.Id); - if (IsNotRoute(route)) continue; - OldRoutes[x.Id] = Tuple.Create(x.Key, route); + var cultures = x.Cultures.Any() ? x.Cultures.Select(c => c.Key) : new[] {(string) null}; + + foreach (var culture in cultures) + { + var route = contentCache.GetRouteById(x.Id, culture); + if (IsNotRoute(route)) return; + OldRoutes[new ContentIdAndCulture(x.Id, culture)] = new ContentKeyAndOldRoute(x.Key, route); + } } } @@ -162,13 +192,13 @@ namespace Umbraco.Web.Redirects LockedEvents = false; } - private static void CreateRedirect(int contentId, Guid contentKey, string oldRoute) + private static void CreateRedirect(int contentId, string culture, Guid contentKey, string oldRoute) { var contentCache = UmbracoContext.Current.ContentCache; - var newRoute = contentCache.GetRouteById(contentId); + var newRoute = contentCache.GetRouteById(contentId, culture); if (IsNotRoute(newRoute) || oldRoute == newRoute) return; var redirectUrlService = Current.Services.RedirectUrlService; - redirectUrlService.Register(oldRoute, contentKey); + redirectUrlService.Register(oldRoute, contentKey, culture); } private static bool IsNotRoute(string route) @@ -177,5 +207,25 @@ namespace Umbraco.Web.Redirects // err/- if collision or anomaly or ... return route == null || route.StartsWith("err/"); } + + private class ContentIdAndCulture : Tuple + { + public ContentIdAndCulture(int contentId, string culture) : base(contentId, culture) + { + } + + public int ContentId => Item1; + public string Culture => Item2; + } + + private class ContentKeyAndOldRoute : Tuple + { + public ContentKeyAndOldRoute(Guid contentKey, string oldRoute) : base(contentKey, oldRoute) + { + } + + public Guid ContentKey => Item1; + public string OldRoute => Item2; + } } } diff --git a/src/Umbraco.Web/Routing/RedirectTrackingComposer.cs b/src/Umbraco.Web/Routing/RedirectTrackingComposer.cs new file mode 100644 index 0000000000..dabf1c15c3 --- /dev/null +++ b/src/Umbraco.Web/Routing/RedirectTrackingComposer.cs @@ -0,0 +1,17 @@ +using Umbraco.Core; +using Umbraco.Core.Components; + +namespace Umbraco.Web.Redirects +{ + /// + /// Implements an Application Event Handler for managing redirect urls tracking. + /// + /// + /// when content is renamed or moved, we want to create a permanent 301 redirect from it's old url + /// not managing domains because we don't know how to do it - changing domains => must create a higher level strategy using rewriting rules probably + /// recycle bin = moving to and from does nothing: to = the node is gone, where would we redirect? from = same + /// + [RuntimeLevel(MinLevel = RuntimeLevel.Run)] + public class RedirectTrackingComposer : ComponentComposer, ICoreComposer + { } +} diff --git a/src/Umbraco.Web/Routing/UrlProviderCollectionBuilder.cs b/src/Umbraco.Web/Routing/UrlProviderCollectionBuilder.cs index 196c8a7a25..06f2728f4c 100644 --- a/src/Umbraco.Web/Routing/UrlProviderCollectionBuilder.cs +++ b/src/Umbraco.Web/Routing/UrlProviderCollectionBuilder.cs @@ -1,14 +1,9 @@ -using LightInject; -using Umbraco.Core.Composing; +using Umbraco.Core.Composing; namespace Umbraco.Web.Routing { public class UrlProviderCollectionBuilder : OrderedCollectionBuilderBase { - public UrlProviderCollectionBuilder(IServiceContainer container) - : base(container) - { } - protected override UrlProviderCollectionBuilder This => this; } } diff --git a/src/Umbraco.Web/Runtime/WebRuntime.cs b/src/Umbraco.Web/Runtime/WebRuntime.cs old mode 100755 new mode 100644 index da062367e9..ad1a4851f0 --- a/src/Umbraco.Web/Runtime/WebRuntime.cs +++ b/src/Umbraco.Web/Runtime/WebRuntime.cs @@ -1,7 +1,6 @@ -using System; -using System.Web; -using LightInject; +using System.Web; using Umbraco.Core.Cache; +using Umbraco.Core.Components; using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; @@ -24,13 +23,12 @@ namespace Umbraco.Web.Runtime ///
/// public WebRuntime(UmbracoApplicationBase umbracoApplication) - : base() { _umbracoApplication = umbracoApplication; } /// - public override void Boot(ServiceContainer container) + public override IFactory Boot(IRegister register) { // create and start asap to profile boot var debug = GlobalSettings.DebugMode; @@ -46,28 +44,22 @@ namespace Umbraco.Web.Runtime _webProfiler = new VoidProfiler(); } - base.Boot(container); + var factory = base.Boot(register); - // now (and only now) is the time to switch over to perWebRequest scopes - if (!(container.ScopeManagerProvider is MixedLightInjectScopeManagerProvider smp)) - throw new Exception("Container.ScopeManagerProvider is not MixedLightInjectScopeManagerProvider."); - smp.EnablePerWebRequestScope(); + // now (and only now) is the time to switch over to perWebRequest scopes. + // up until that point we may not have a request, and scoped services would + // fail to resolve - but we run Initialize within a factory scope - and then, + // here, we switch the factory to bind scopes to requests + factory.EnablePerWebRequestScope(); + + return factory; } - /// - public override void Compose(ServiceContainer container) - { - // some components may want to initialize with the UmbracoApplicationBase - // well, they should not - we should not do this - // TODO remove this eventually. - container.RegisterInstance(_umbracoApplication); - base.Compose(container); + #region Getters - // replace CoreRuntime's IProfiler registration - container.RegisterSingleton(_ => _webProfiler); + protected override IProfiler GetProfiler() => _webProfiler; - // replace CoreRuntime's CacheHelper registration - container.RegisterSingleton(_ => new CacheHelper( + protected override CacheHelper GetAppCaches() => new CacheHelper( // we need to have the dep clone runtime cache provider to ensure // all entities are cached properly (cloned in and cloned out) new DeepCloneRuntimeCacheProvider(new HttpRuntimeCacheProvider(HttpRuntime.Cache)), @@ -77,26 +69,7 @@ namespace Umbraco.Web.Runtime new IsolatedRuntimeCache(type => // we need to have the dep clone runtime cache provider to ensure // all entities are cached properly (cloned in and cloned out) - new DeepCloneRuntimeCacheProvider(new ObjectCacheRuntimeCacheProvider())))); - - container.RegisterSingleton(); // required for hybrid accessors - } - - #region Getters - - //protected override IProfiler GetProfiler() => new WebProfiler(); - - //protected override CacheHelper GetApplicationCache() => new CacheHelper( - // // we need to have the dep clone runtime cache provider to ensure - // // all entities are cached properly (cloned in and cloned out) - // new DeepCloneRuntimeCacheProvider(new HttpRuntimeCacheProvider(HttpRuntime.Cache)), - // new StaticCacheProvider(), - // // we need request based cache when running in web-based context - // new HttpRequestCacheProvider(), - // new IsolatedRuntimeCache(type => - // // we need to have the dep clone runtime cache provider to ensure - // // all entities are cached properly (cloned in and cloned out) - // new DeepCloneRuntimeCacheProvider(new ObjectCacheRuntimeCacheProvider()))); + new DeepCloneRuntimeCacheProvider(new ObjectCacheRuntimeCacheProvider()))); #endregion } diff --git a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs index 0d27253466..0199a34579 100644 --- a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs +++ b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs @@ -12,46 +12,19 @@ using System.Web.Mvc; using System.Web.Routing; using ClientDependency.Core.CompositeFiles.Providers; using ClientDependency.Core.Config; -using Examine; -using LightInject; -using Microsoft.AspNet.SignalR; using Umbraco.Core; using Umbraco.Core.Components; using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.Dictionary; -using Umbraco.Core.Events; -using Umbraco.Core.Logging; -using Umbraco.Core.Macros; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Profiling; -using Umbraco.Core.PropertyEditors; -using Umbraco.Core.PropertyEditors.ValueConverters; -using Umbraco.Core.Runtime; using Umbraco.Core.Services; -using Umbraco.Examine; -using Umbraco.Web.Actions; -using Umbraco.Web.Cache; -using Umbraco.Web.Composing.CompositionRoots; -using Umbraco.Web.ContentApps; -using Umbraco.Web.Dictionary; -using Umbraco.Web.Editors; -using Umbraco.Web.Features; -using Umbraco.Web.HealthCheck; using Umbraco.Web.Install; -using Umbraco.Web.Media; -using Umbraco.Web.Models.ContentEditing; -using Umbraco.Web.Models.PublishedContent; using Umbraco.Web.Mvc; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; -using Umbraco.Web.Search; using Umbraco.Web.Security; -using Umbraco.Web.Services; -using Umbraco.Web.SignalR; -using Umbraco.Web.Tour; -using Umbraco.Web.Trees; using Umbraco.Web.UI.JavaScript; using Umbraco.Web.WebApi; @@ -59,163 +32,19 @@ using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Web.Runtime { - [RequireComponent(typeof(CoreRuntimeComponent))] - public class WebRuntimeComponent : UmbracoComponentBase, IRuntimeComponent + public sealed class WebRuntimeComponent : IComponent { - public override void Compose(Composition composition) - { - base.Compose(composition); + private readonly IUmbracoContextAccessor _umbracoContextAccessor; + private readonly SurfaceControllerTypeCollection _surfaceControllerTypes; + private readonly UmbracoApiControllerTypeCollection _apiControllerTypes; + private readonly IPublishedSnapshotService _publishedSnapshotService; + private readonly IUserService _userService; + private readonly IUmbracoSettingsSection _umbracoSettings; + private readonly IGlobalSettings _globalSettings; + private readonly IVariationContextAccessor _variationContextAccessor; + private readonly UrlProviderCollection _urlProviders; - composition.Container.RegisterFrom(); - - //register the install components - //NOTE: i tried to not have these registered if we weren't installing or upgrading but post install when the site restarts - //it still needs to use the install controller so we can't do that - composition.Container.RegisterFrom(); - - // register accessors for cultures - composition.Container.RegisterSingleton(); - composition.Container.RegisterSingleton(); - - var typeLoader = composition.Container.GetInstance(); - var logger = composition.Container.GetInstance(); - var proflog = composition.Container.GetInstance(); - - // register the http context and umbraco context accessors - // we *should* use the HttpContextUmbracoContextAccessor, however there are cases when - // we have no http context, eg when booting Umbraco or in background threads, so instead - // let's use an hybrid accessor that can fall back to a ThreadStatic context. - composition.Container.RegisterSingleton(); - - // register the 'current' umbraco context - transient - for eg controllers - composition.Container.Register(factory => factory.GetInstance().UmbracoContext); - - // register a per-request HttpContextBase object - // is per-request so only one wrapper is created per request - composition.Container.Register(factory => new HttpContextWrapper(factory.GetInstance().HttpContext), new PerRequestLifeTime()); - - // register the published snapshot accessor - the "current" published snapshot is in the umbraco context - composition.Container.RegisterSingleton(); - - // register a per-request UmbracoContext object - // no real need to be per request but assuming it is faster - composition.Container.Register(factory => factory.GetInstance().UmbracoContext, new PerRequestLifeTime()); - - // register the umbraco helper - composition.Container.Register(new PerRequestLifeTime()); - - // register distributed cache - composition.Container.RegisterSingleton(f => new DistributedCache()); - - // replace some services - composition.Container.RegisterSingleton(); - composition.Container.RegisterSingleton(); - composition.Container.RegisterSingleton(); - composition.Container.RegisterSingleton(); - - composition.Container.RegisterSingleton(factory => ExamineManager.Instance); - - // IoC setup for LightInject for MVC/WebApi - // see comments on MixedLightInjectScopeManagerProvider for explainations of what we are doing here - if (!(composition.Container.ScopeManagerProvider is MixedLightInjectScopeManagerProvider smp)) - throw new Exception("Container.ScopeManagerProvider is not MixedLightInjectScopeManagerProvider."); - composition.Container.EnableMvc(); // does container.EnablePerWebRequestScope() - composition.Container.ScopeManagerProvider = smp; // reverts - we will do it last (in WebRuntime) - - composition.Container.RegisterSingleton(); - - composition.Container.RegisterUmbracoControllers(typeLoader, GetType().Assembly); - composition.Container.EnableWebApi(GlobalConfiguration.Configuration); - - composition.Container.RegisterCollectionBuilder() - .Add(() => typeLoader.GetTypes()); - - composition.Container.Register(new PerRequestLifeTime()); - - composition.Container.RegisterCollectionBuilder() - .Add(() => typeLoader.GetTypes()); - - composition.Container.RegisterCollectionBuilder(); - - composition.Container.RegisterSingleton(); - - // set the default RenderMvcController - Current.DefaultRenderMvcControllerType = typeof(RenderMvcController); // fixme WRONG! - - composition.Container.RegisterCollectionBuilder() - .Add(() => typeLoader.GetTypes()); - - var surfaceControllerTypes = new SurfaceControllerTypeCollection(typeLoader.GetSurfaceControllers()); - composition.Container.RegisterInstance(surfaceControllerTypes); - - var umbracoApiControllerTypes = new UmbracoApiControllerTypeCollection(typeLoader.GetUmbracoApiControllers()); - composition.Container.RegisterInstance(umbracoApiControllerTypes); - - // both TinyMceValueConverter (in Core) and RteMacroRenderingValueConverter (in Web) will be - // discovered when CoreBootManager configures the converters. We HAVE to remove one of them - // here because there cannot be two converters for one property editor - and we want the full - // RteMacroRenderingValueConverter that converts macros, etc. So remove TinyMceValueConverter. - // (the limited one, defined in Core, is there for tests) - same for others - composition.Container.GetInstance() - .Remove() - .Remove() - .Remove(); - - // add all known factories, devs can then modify this list on application - // startup either by binding to events or in their own global.asax - composition.Container.RegisterCollectionBuilder() - .Append(); - - composition.Container.RegisterCollectionBuilder() - .Append() - .Append() - .Append(); - - composition.Container.RegisterSingleton(); - - composition.Container.RegisterCollectionBuilder() - // all built-in finders in the correct order, - // devs can then modify this list on application startup - .Append() - .Append() - .Append() - //.Append() // disabled, this is an odd finder - .Append() - .Append(); - - composition.Container.RegisterSingleton(); - - composition.Container.RegisterSingleton(); - - // register *all* checks, except those marked [HideFromTypeFinder] of course - composition.Container.RegisterCollectionBuilder() - .Add(() => typeLoader.GetTypes()); - - composition.Container.RegisterCollectionBuilder() - .Add(() => typeLoader.GetTypes()); - - // auto-register views - composition.Container.RegisterAuto(typeof(UmbracoViewPage<>)); - - // register published router - composition.Container.RegisterSingleton(); - composition.Container.Register(_ => UmbracoConfig.For.UmbracoSettings().WebRouting); - - // register preview SignalR hub - composition.Container.Register(_ => GlobalHost.ConnectionManager.GetHubContext(), new PerContainerLifetime()); - - // register properties fallback - composition.Container.RegisterSingleton(); - - // register known content apps - composition.Container.RegisterCollectionBuilder() - .Append() - .Append() - .Append(); - } - - internal void Initialize( - IRuntimeState runtime, + public WebRuntimeComponent( IUmbracoContextAccessor umbracoContextAccessor, SurfaceControllerTypeCollection surfaceControllerTypes, UmbracoApiControllerTypeCollection apiControllerTypes, @@ -223,15 +52,27 @@ namespace Umbraco.Web.Runtime IUserService userService, IUmbracoSettingsSection umbracoSettings, IGlobalSettings globalSettings, - IEntityService entityService, IVariationContextAccessor variationContextAccessor, UrlProviderCollection urlProviders) { + _umbracoContextAccessor = umbracoContextAccessor; + _surfaceControllerTypes = surfaceControllerTypes; + _apiControllerTypes = apiControllerTypes; + _publishedSnapshotService = publishedSnapshotService; + _userService = userService; + _umbracoSettings = umbracoSettings; + _globalSettings = globalSettings; + _variationContextAccessor = variationContextAccessor; + _urlProviders = urlProviders; + } + + public void Initialize() + { // setup mvc and webapi services SetupMvcAndWebApi(); // client dependency - ConfigureClientDependency(globalSettings); + ConfigureClientDependency(_globalSettings); // Disable the X-AspNetMvc-Version HTTP Header MvcHandler.DisableMvcResponseHeader = true; @@ -245,7 +86,7 @@ namespace Umbraco.Web.Runtime ConfigureGlobalFilters(); // set routes - CreateRoutes(umbracoContextAccessor, globalSettings, surfaceControllerTypes, apiControllerTypes); + CreateRoutes(_umbracoContextAccessor, _globalSettings, _surfaceControllerTypes, _apiControllerTypes); // get an http context // at that moment, HttpContext.Current != null but its .Request property is null @@ -255,19 +96,22 @@ namespace Umbraco.Web.Runtime // (also sets the accessor) // this is a *temp* UmbracoContext UmbracoContext.EnsureContext( - umbracoContextAccessor, + _umbracoContextAccessor, new HttpContextWrapper(HttpContext.Current), - publishedSnapshotService, - new WebSecurity(httpContext, userService, globalSettings), - umbracoSettings, - urlProviders, - globalSettings, - variationContextAccessor); + _publishedSnapshotService, + new WebSecurity(httpContext, _userService, _globalSettings), + _umbracoSettings, + _urlProviders, + _globalSettings, + _variationContextAccessor); // ensure WebAPI is initialized, after everything GlobalConfiguration.Configuration.EnsureInitialized(); } + public void Terminate() + { } + private static void ConfigureGlobalFilters() { GlobalFilters.Filters.Add(new EnsurePartialViewMacroViewContextFilterAttribute()); @@ -278,7 +122,7 @@ namespace Umbraco.Web.Runtime { if (viewEngines == null || viewEngines.Count == 0) return; - var originalEngines = viewEngines.Select(e => e).ToArray(); + var originalEngines = viewEngines.ToList(); viewEngines.Clear(); foreach (var engine in originalEngines) { diff --git a/src/Umbraco.Web/Runtime/WebRuntimeComposer.cs b/src/Umbraco.Web/Runtime/WebRuntimeComposer.cs new file mode 100644 index 0000000000..51e4fbd008 --- /dev/null +++ b/src/Umbraco.Web/Runtime/WebRuntimeComposer.cs @@ -0,0 +1,194 @@ +using System.Web; +using System.Web.Security; +using Examine; +using Microsoft.AspNet.SignalR; +using Umbraco.Core; +using Umbraco.Core.Components; +using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; +using Umbraco.Core.Dictionary; +using Umbraco.Core.Events; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.PropertyEditors.ValueConverters; +using Umbraco.Core.Runtime; +using Umbraco.Core.Services; +using Umbraco.Web.Actions; +using Umbraco.Web.Cache; +using Umbraco.Web.Composing.Composers; +using Umbraco.Web.ContentApps; +using Umbraco.Web.Dictionary; +using Umbraco.Web.Editors; +using Umbraco.Web.Features; +using Umbraco.Web.HealthCheck; +using Umbraco.Web.Models.PublishedContent; +using Umbraco.Web.Mvc; +using Umbraco.Web.PublishedCache; +using Umbraco.Web.Routing; +using Umbraco.Web.Search; +using Umbraco.Web.Security; +using Umbraco.Web.Security.Providers; +using Umbraco.Web.Services; +using Umbraco.Web.SignalR; +using Umbraco.Web.Tour; +using Umbraco.Web.Trees; +using Umbraco.Web.WebApi; +using Current = Umbraco.Web.Composing.Current; + +namespace Umbraco.Web.Runtime +{ + [ComposeAfter(typeof(CoreRuntimeComposer))] + public sealed class WebRuntimeComposer : ComponentComposer, IRuntimeComposer + { + public override void Compose(Composition composition) + { + base.Compose(composition); + + composition.Register(); + + composition.RegisterUnique(); // required for hybrid accessors + + composition.ComposeWebMappingProfiles(); + + //register the install components + //NOTE: i tried to not have these registered if we weren't installing or upgrading but post install when the site restarts + //it still needs to use the install controller so we can't do that + composition.ComposeInstaller(); + + // register membership stuff + composition.Register(factory => Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider()); + composition.Register(factory => Roles.Enabled ? Roles.Provider : new MembersRoleProvider(factory.GetInstance())); + composition.Register(); + + // register accessors for cultures + composition.RegisterUnique(); + composition.RegisterUnique(); + + // register the http context and umbraco context accessors + // we *should* use the HttpContextUmbracoContextAccessor, however there are cases when + // we have no http context, eg when booting Umbraco or in background threads, so instead + // let's use an hybrid accessor that can fall back to a ThreadStatic context. + composition.RegisterUnique(); + + // register a per-request HttpContextBase object + // is per-request so only one wrapper is created per request + composition.Register(factory => new HttpContextWrapper(factory.GetInstance().HttpContext), Lifetime.Request); + + // register the published snapshot accessor - the "current" published snapshot is in the umbraco context + composition.RegisterUnique(); + + // we should stop injecting UmbracoContext and always inject IUmbracoContextAccessor, however at the moment + // there are tons of places (controllers...) which require UmbracoContext in their ctor - so let's register + // a way to inject the UmbracoContext - and register it per-request to be more efficient + //TODO: stop doing this + composition.Register(factory => factory.GetInstance().UmbracoContext, Lifetime.Request); + + // register the umbraco helper + composition.RegisterUnique(); + + // register distributed cache + composition.RegisterUnique(f => new DistributedCache()); + + // replace some services + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + + composition.RegisterUnique(factory => ExamineManager.Instance); + + // configure the container for web + composition.ConfigureForWeb(); + + + composition.RegisterUnique(); + + composition + .ComposeUmbracoControllers(GetType().Assembly) + .SetDefaultRenderMvcController(); // default controller for template views + + composition.WithCollectionBuilder() + .Add(() => composition.TypeLoader.GetTypes()); // fixme which searchable trees?! + + composition.Register(Lifetime.Request); + + composition.WithCollectionBuilder() + .Add(() => composition.TypeLoader.GetTypes()); + + composition.WithCollectionBuilder(); + + composition.RegisterUnique(); + + composition.WithCollectionBuilder() + .Add(() => composition.TypeLoader.GetTypes()); + + var surfaceControllerTypes = new SurfaceControllerTypeCollection(composition.TypeLoader.GetSurfaceControllers()); + composition.RegisterUnique(surfaceControllerTypes); + + var umbracoApiControllerTypes = new UmbracoApiControllerTypeCollection(composition.TypeLoader.GetUmbracoApiControllers()); + composition.RegisterUnique(umbracoApiControllerTypes); + + // both TinyMceValueConverter (in Core) and RteMacroRenderingValueConverter (in Web) will be + // discovered when CoreBootManager configures the converters. We HAVE to remove one of them + // here because there cannot be two converters for one property editor - and we want the full + // RteMacroRenderingValueConverter that converts macros, etc. So remove TinyMceValueConverter. + // (the limited one, defined in Core, is there for tests) - same for others + composition.WithCollectionBuilder() + .Remove() + .Remove() + .Remove(); + + // add all known factories, devs can then modify this list on application + // startup either by binding to events or in their own global.asax + composition.WithCollectionBuilder() + .Append(); + + composition.WithCollectionBuilder() + .Append() + .Append() + .Append(); + + composition.RegisterUnique(); + + composition.WithCollectionBuilder() + // all built-in finders in the correct order, + // devs can then modify this list on application startup + .Append() + .Append() + .Append() + //.Append() // disabled, this is an odd finder + .Append() + .Append(); + + composition.RegisterUnique(); + + composition.RegisterUnique(); + + // register *all* checks, except those marked [HideFromTypeFinder] of course + composition.WithCollectionBuilder() + .Add(() => composition.TypeLoader.GetTypes()); + + composition.WithCollectionBuilder() + .Add(() => composition.TypeLoader.GetTypes()); + + // auto-register views + composition.RegisterAuto(typeof(UmbracoViewPage<>)); + + // register published router + composition.RegisterUnique(); + composition.Register(_ => Current.Configs.Settings().WebRouting); + + // register preview SignalR hub + composition.RegisterUnique(_ => GlobalHost.ConnectionManager.GetHubContext()); + + // register properties fallback + composition.RegisterUnique(); + + // register known content apps + composition.WithCollectionBuilder() + .Append() + .Append() + .Append(); + } + } +} diff --git a/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs b/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs index ed08a0ddb6..b4d122b64f 100644 --- a/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs +++ b/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs @@ -6,7 +6,6 @@ using System.Web.Hosting; using Umbraco.Core; using Umbraco.Core.Events; using Umbraco.Core.Logging; -using LightInject; namespace Umbraco.Web.Scheduling { @@ -15,7 +14,51 @@ namespace Umbraco.Web.Scheduling ///
/// This class exists for logging purposes - the one you want to use is BackgroundTaskRunner{T}. public abstract class BackgroundTaskRunner - { } + { + /// + /// Represents a MainDom hook. + /// + public class MainDomHook + { + /// + /// Initializes a new instance of the class. + /// + /// The object. + /// A method to execute when hooking into the main domain. + /// A method to execute when the main domain releases. + public MainDomHook(IMainDom mainDom, Action install, Action release) + { + MainDom = mainDom; + Install = install; + Release = release; + } + + /// + /// Gets the object. + /// + public IMainDom MainDom { get; } + + /// + /// Gets the method to execute when hooking into the main domain. + /// + public Action Install { get; } + + /// + /// Gets the method to execute when the main domain releases. + /// + public Action Release { get; } + + internal bool Register() + { + if (MainDom != null) + return MainDom.Register(Install, Release); + + // tests + Install?.Invoke(); + return true; + } + } + } /// /// Manages a queue of tasks of type and runs them in the background. @@ -55,42 +98,6 @@ namespace Umbraco.Web.Scheduling private bool _terminated; // remember we've terminated private readonly TaskCompletionSource _terminatedSource = new TaskCompletionSource(); // enable awaiting termination - // fixme - this is temp - // at the moment MainDom is internal so we have to find a way to hook into it - temp - public class MainDomHook - { - private MainDomHook(MainDom mainDom, Action install, Action release) - { - MainDom = mainDom; - Install = install; - Release = release; - } - - internal MainDom MainDom { get; } - public Action Install { get; } - public Action Release { get; } - - public static MainDomHook Create(Action install, Action release) - { - return new MainDomHook(Core.Composing.Current.Container.GetInstance(), install, release); - } - - public static MainDomHook CreateForTest(Action install, Action release) - { - return new MainDomHook(null, install, release); - } - - public bool Register() - { - if (MainDom != null) - return MainDom.Register(Install, Release); - - // tests - Install?.Invoke(); - return true; - } - } - /// /// Initializes a new instance of the class. /// @@ -129,11 +136,9 @@ namespace Umbraco.Web.Scheduling /// An optional main domain hook. public BackgroundTaskRunner(string name, BackgroundTaskRunnerOptions options, ILogger logger, MainDomHook hook = null) { - if (options == null) throw new ArgumentNullException(nameof(options)); - if (logger == null) throw new ArgumentNullException(nameof(logger)); - _options = options; + _options = options ?? throw new ArgumentNullException(nameof(options)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _logPrefix = "[" + name + "] "; - _logger = logger; if (options.Hosted) HostingEnvironment.RegisterObject(this); @@ -319,7 +324,7 @@ namespace Umbraco.Web.Scheduling } /// - /// Shuts the taks runner down. + /// Shuts the tasks runner down. /// /// True for force the runner to stop. /// True to wait until the runner has stopped. @@ -355,7 +360,7 @@ namespace Umbraco.Web.Scheduling // tasks in the queue will be executed... if (wait == false) return; - _runningTask?.Wait(); // wait for whatever is running to end... + _runningTask?.Wait(CancellationToken.None); // wait for whatever is running to end... } private async Task Pump() @@ -648,13 +653,13 @@ namespace Umbraco.Web.Scheduling #endregion /// - /// Requests a registered object to unregister. + /// Requests a registered object to un-register. /// - /// true to indicate the registered object should unregister from the hosting + /// true to indicate the registered object should un-register from the hosting /// environment before returning; otherwise, false. /// /// "When the application manager needs to stop a registered object, it will call the Stop method." - /// The application manager will call the Stop method to ask a registered object to unregister. During + /// The application manager will call the Stop method to ask a registered object to un-register. During /// processing of the Stop method, the registered object must call the HostingEnvironment.UnregisterObject method. /// public void Stop(bool immediate) diff --git a/src/Umbraco.Web/Scheduling/HealthCheckNotifier.cs b/src/Umbraco.Web/Scheduling/HealthCheckNotifier.cs index 89fba7717d..4f66af11bd 100644 --- a/src/Umbraco.Web/Scheduling/HealthCheckNotifier.cs +++ b/src/Umbraco.Web/Scheduling/HealthCheckNotifier.cs @@ -2,6 +2,7 @@ using System.Threading; using System.Threading.Tasks; using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Sync; @@ -14,20 +15,18 @@ namespace Umbraco.Web.Scheduling private readonly IRuntimeState _runtimeState; private readonly HealthCheckCollection _healthChecks; private readonly HealthCheckNotificationMethodCollection _notifications; - private readonly ILogger _logger; - private readonly ProfilingLogger _proflog; + private readonly IProfilingLogger _logger; public HealthCheckNotifier(IBackgroundTaskRunner runner, int delayMilliseconds, int periodMilliseconds, HealthCheckCollection healthChecks, HealthCheckNotificationMethodCollection notifications, IRuntimeState runtimeState, - ILogger logger, ProfilingLogger proflog) + IProfilingLogger logger) : base(runner, delayMilliseconds, periodMilliseconds) { _healthChecks = healthChecks; _notifications = notifications; _runtimeState = runtimeState; _logger = logger; - _proflog = proflog; } public override async Task PerformRunAsync(CancellationToken token) @@ -52,9 +51,9 @@ namespace Umbraco.Web.Scheduling return false; // do NOT repeat, going down } - using (_proflog.DebugDuration("Health checks executing", "Health checks complete")) + using (_logger.DebugDuration("Health checks executing", "Health checks complete")) { - var healthCheckConfig = UmbracoConfig.For.HealthCheck(); + var healthCheckConfig = Current.Configs.HealthChecks(); // Don't notify for any checks that are disabled, nor for any disabled // just for notifications diff --git a/src/Umbraco.Web/Scheduling/KeepAlive.cs b/src/Umbraco.Web/Scheduling/KeepAlive.cs index 8068e387f8..6dd7572b87 100644 --- a/src/Umbraco.Web/Scheduling/KeepAlive.cs +++ b/src/Umbraco.Web/Scheduling/KeepAlive.cs @@ -11,17 +11,15 @@ namespace Umbraco.Web.Scheduling internal class KeepAlive : RecurringTaskBase { private readonly IRuntimeState _runtime; - private readonly ILogger _logger; + private readonly IProfilingLogger _logger; private static HttpClient _httpClient; - private readonly ProfilingLogger _proflog; public KeepAlive(IBackgroundTaskRunner runner, int delayMilliseconds, int periodMilliseconds, - IRuntimeState runtime, ILogger logger, ProfilingLogger proflog) + IRuntimeState runtime, IProfilingLogger logger) : base(runner, delayMilliseconds, periodMilliseconds) { _runtime = runtime; _logger = logger; - _proflog = proflog; if (_httpClient == null) _httpClient = new HttpClient(); } @@ -46,7 +44,7 @@ namespace Umbraco.Web.Scheduling return false; // do NOT repeat, going down } - using (_proflog.DebugDuration("Keep alive executing", "Keep alive complete")) + using (_logger.DebugDuration("Keep alive executing", "Keep alive complete")) { string umbracoAppUrl = null; diff --git a/src/Umbraco.Web/Scheduling/LogScrubber.cs b/src/Umbraco.Web/Scheduling/LogScrubber.cs index ae73da04c8..ae27809987 100644 --- a/src/Umbraco.Web/Scheduling/LogScrubber.cs +++ b/src/Umbraco.Web/Scheduling/LogScrubber.cs @@ -16,12 +16,11 @@ namespace Umbraco.Web.Scheduling private readonly IRuntimeState _runtime; private readonly IAuditService _auditService; private readonly IUmbracoSettingsSection _settings; - private readonly ILogger _logger; - private readonly ProfilingLogger _proflog; + private readonly IProfilingLogger _logger; private readonly IScopeProvider _scopeProvider; public LogScrubber(IBackgroundTaskRunner runner, int delayMilliseconds, int periodMilliseconds, - IRuntimeState runtime, IAuditService auditService, IUmbracoSettingsSection settings, IScopeProvider scopeProvider, ILogger logger, ProfilingLogger proflog) + IRuntimeState runtime, IAuditService auditService, IUmbracoSettingsSection settings, IScopeProvider scopeProvider, IProfilingLogger logger) : base(runner, delayMilliseconds, periodMilliseconds) { _runtime = runtime; @@ -29,7 +28,6 @@ namespace Umbraco.Web.Scheduling _settings = settings; _scopeProvider = scopeProvider; _logger = logger; - _proflog = proflog; } // maximum age, in minutes @@ -86,7 +84,7 @@ namespace Umbraco.Web.Scheduling // running on a background task, and Log.CleanLogs uses the old SqlHelper, // better wrap in a scope and ensure it's all cleaned up and nothing leaks using (var scope = _scopeProvider.CreateScope()) - using (_proflog.DebugDuration("Log scrubbing executing", "Log scrubbing complete")) + using (_logger.DebugDuration("Log scrubbing executing", "Log scrubbing complete")) { _auditService.CleanLogs(GetLogScrubbingMaximumAge(_settings)); scope.Complete(); diff --git a/src/Umbraco.Web/Scheduling/ScheduledTasks.cs b/src/Umbraco.Web/Scheduling/ScheduledTasks.cs index 49fa1e469b..115a28504c 100644 --- a/src/Umbraco.Web/Scheduling/ScheduledTasks.cs +++ b/src/Umbraco.Web/Scheduling/ScheduledTasks.cs @@ -20,18 +20,16 @@ namespace Umbraco.Web.Scheduling private static HttpClient _httpClient; private readonly IRuntimeState _runtime; private readonly IUmbracoSettingsSection _settings; - private readonly ILogger _logger; - private readonly ProfilingLogger _proflog; + private readonly IProfilingLogger _logger; private static readonly Hashtable ScheduledTaskTimes = new Hashtable(); public ScheduledTasks(IBackgroundTaskRunner runner, int delayMilliseconds, int periodMilliseconds, - IRuntimeState runtime, IUmbracoSettingsSection settings, ILogger logger, ProfilingLogger proflog) + IRuntimeState runtime, IUmbracoSettingsSection settings, IProfilingLogger logger) : base(runner, delayMilliseconds, periodMilliseconds) { _runtime = runtime; _settings = settings; _logger = logger; - _proflog = proflog; } private async Task ProcessTasksAsync(CancellationToken token) @@ -71,7 +69,7 @@ namespace Umbraco.Web.Scheduling { BaseAddress = _runtime.ApplicationUrl }; - + var request = new HttpRequestMessage(HttpMethod.Get, url); //TODO: pass custom the authorization header, currently these aren't really secured! @@ -109,7 +107,7 @@ namespace Umbraco.Web.Scheduling return false; // do NOT repeat, going down } - using (_proflog.DebugDuration("Scheduled tasks executing", "Scheduled tasks complete")) + using (_logger.DebugDuration("Scheduled tasks executing", "Scheduled tasks complete")) { try { diff --git a/src/Umbraco.Web/Scheduling/SchedulerComponent.cs b/src/Umbraco.Web/Scheduling/SchedulerComponent.cs index d14a7bb34f..e4bc74b7f2 100644 --- a/src/Umbraco.Web/Scheduling/SchedulerComponent.cs +++ b/src/Umbraco.Web/Scheduling/SchedulerComponent.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Threading; using Umbraco.Core; using Umbraco.Core.Components; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.HealthChecks; using Umbraco.Core.Configuration.UmbracoSettings; @@ -14,24 +15,15 @@ using Umbraco.Web.Routing; namespace Umbraco.Web.Scheduling { - /// - /// Used to do the scheduling for tasks, publishing, etc... - /// - /// - /// All tasks are run in a background task runner which is web aware and will wind down - /// the task correctly instead of killing it completely when the app domain shuts down. - /// - [RuntimeLevel(MinLevel = RuntimeLevel.Run)] - internal sealed class SchedulerComponent : UmbracoComponentBase, IUmbracoCoreComponent + internal sealed class SchedulerComponent : IComponent { - private IRuntimeState _runtime; - private IContentService _contentService; - private IAuditService _auditService; - private ILogger _logger; - private ProfilingLogger _proflog; - private IScopeProvider _scopeProvider; - private HealthCheckCollection _healthChecks; - private HealthCheckNotificationMethodCollection _notifications; + private readonly IRuntimeState _runtime; + private readonly IContentService _contentService; + private readonly IAuditService _auditService; + private readonly IProfilingLogger _logger; + private readonly IScopeProvider _scopeProvider; + private readonly HealthCheckCollection _healthChecks; + private readonly HealthCheckNotificationMethodCollection _notifications; private BackgroundTaskRunner _keepAliveRunner; private BackgroundTaskRunner _publishingRunner; @@ -43,32 +35,39 @@ namespace Umbraco.Web.Scheduling private object _locker = new object(); private IBackgroundTask[] _tasks; - public void Initialize(IRuntimeState runtime, + public SchedulerComponent(IRuntimeState runtime, IContentService contentService, IAuditService auditService, HealthCheckCollection healthChecks, HealthCheckNotificationMethodCollection notifications, - IScopeProvider scopeProvider, ILogger logger, ProfilingLogger proflog) + IScopeProvider scopeProvider, IProfilingLogger logger) { _runtime = runtime; _contentService = contentService; _auditService = auditService; _scopeProvider = scopeProvider; _logger = logger; - _proflog = proflog; _healthChecks = healthChecks; _notifications = notifications; + } + public void Initialize() + { // backgrounds runners are web aware, if the app domain dies, these tasks will wind down correctly - _keepAliveRunner = new BackgroundTaskRunner("KeepAlive", logger); - _publishingRunner = new BackgroundTaskRunner("ScheduledPublishing", logger); - _tasksRunner = new BackgroundTaskRunner("ScheduledTasks", logger); - _scrubberRunner = new BackgroundTaskRunner("LogScrubber", logger); - _healthCheckRunner = new BackgroundTaskRunner("HealthCheckNotifier", logger); + _keepAliveRunner = new BackgroundTaskRunner("KeepAlive", _logger); + _publishingRunner = new BackgroundTaskRunner("ScheduledPublishing", _logger); + _tasksRunner = new BackgroundTaskRunner("ScheduledTasks", _logger); + _scrubberRunner = new BackgroundTaskRunner("LogScrubber", _logger); + _healthCheckRunner = new BackgroundTaskRunner("HealthCheckNotifier", _logger); // we will start the whole process when a successful request is made UmbracoModule.RouteAttempt += RegisterBackgroundTasksOnce; } + public void Terminate() + { + // the appdomain / maindom / whatever takes care of stopping background task runners + } + private void RegisterBackgroundTasksOnce(object sender, RoutableAttemptEventArgs e) { switch (e.Outcome) @@ -86,7 +85,7 @@ namespace Umbraco.Web.Scheduling LazyInitializer.EnsureInitialized(ref _tasks, ref _started, ref _locker, () => { _logger.Debug("Initializing the scheduler"); - var settings = UmbracoConfig.For.UmbracoSettings(); + var settings = Current.Configs.Settings(); var tasks = new List(); @@ -95,9 +94,9 @@ namespace Umbraco.Web.Scheduling tasks.Add(RegisterTaskRunner(settings)); tasks.Add(RegisterLogScrubber(settings)); - var healthCheckConfig = UmbracoConfig.For.HealthCheck(); + var healthCheckConfig = Current.Configs.HealthChecks(); if (healthCheckConfig.NotificationSettings.Enabled) - tasks.Add(RegisterHealthCheckNotifier(healthCheckConfig, _healthChecks, _notifications, _logger, _proflog)); + tasks.Add(RegisterHealthCheckNotifier(healthCheckConfig, _healthChecks, _notifications, _logger)); return tasks.ToArray(); }); @@ -107,7 +106,7 @@ namespace Umbraco.Web.Scheduling { // ping/keepalive // on all servers - var task = new KeepAlive(_keepAliveRunner, 60000, 300000, _runtime, _logger, _proflog); + var task = new KeepAlive(_keepAliveRunner, 60000, 300000, _runtime, _logger); _keepAliveRunner.TryAdd(task); return task; } @@ -123,14 +122,14 @@ namespace Umbraco.Web.Scheduling private IBackgroundTask RegisterTaskRunner(IUmbracoSettingsSection settings) { - var task = new ScheduledTasks(_tasksRunner, 60000, 60000, _runtime, settings, _logger, _proflog); + var task = new ScheduledTasks(_tasksRunner, 60000, 60000, _runtime, settings, _logger); _tasksRunner.TryAdd(task); return task; } private IBackgroundTask RegisterHealthCheckNotifier(IHealthChecks healthCheckConfig, HealthCheckCollection healthChecks, HealthCheckNotificationMethodCollection notifications, - ILogger logger, ProfilingLogger proflog) + IProfilingLogger logger) { // If first run time not set, start with just small delay after application start int delayInMilliseconds; @@ -149,7 +148,7 @@ namespace Umbraco.Web.Scheduling } var periodInMilliseconds = healthCheckConfig.NotificationSettings.PeriodInHours * 60 * 60 * 1000; - var task = new HealthCheckNotifier(_healthCheckRunner, delayInMilliseconds, periodInMilliseconds, healthChecks, notifications, _runtime, logger, proflog); + var task = new HealthCheckNotifier(_healthCheckRunner, delayInMilliseconds, periodInMilliseconds, healthChecks, notifications, _runtime, logger); return task; } @@ -157,7 +156,7 @@ namespace Umbraco.Web.Scheduling { // log scrubbing // install on all, will only run on non-replica servers - var task = new LogScrubber(_scrubberRunner, 60000, LogScrubber.GetLogScrubbingInterval(settings, _logger), _runtime, _auditService, settings, _scopeProvider, _logger, _proflog); + var task = new LogScrubber(_scrubberRunner, 60000, LogScrubber.GetLogScrubbingInterval(settings, _logger), _runtime, _auditService, settings, _scopeProvider, _logger); _scrubberRunner.TryAdd(task); return task; } diff --git a/src/Umbraco.Web/Scheduling/SchedulerComposer.cs b/src/Umbraco.Web/Scheduling/SchedulerComposer.cs new file mode 100644 index 0000000000..b28312c8d6 --- /dev/null +++ b/src/Umbraco.Web/Scheduling/SchedulerComposer.cs @@ -0,0 +1,17 @@ +using System; +using Umbraco.Core; +using Umbraco.Core.Components; + +namespace Umbraco.Web.Scheduling +{ + /// + /// Used to do the scheduling for tasks, publishing, etc... + /// + /// + /// All tasks are run in a background task runner which is web aware and will wind down + /// the task correctly instead of killing it completely when the app domain shuts down. + /// + [RuntimeLevel(MinLevel = RuntimeLevel.Run)] + internal sealed class SchedulerComposer : ComponentComposer, ICoreComposer + { } +} diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index fff8e99aeb..09d0c555da 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -4,15 +4,11 @@ using System.Globalization; using System.Linq; using System.Threading; using Examine; -using Examine.LuceneEngine; -using Examine.LuceneEngine.Providers; -using Lucene.Net.Index; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Components; using Umbraco.Core.Logging; using Umbraco.Core.Models; -using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Services.Changes; @@ -23,85 +19,35 @@ using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Web.Scheduling; using System.Threading.Tasks; using Examine.LuceneEngine.Directories; -using Examine.LuceneEngine.Indexing; -using LightInject; -using Umbraco.Core.Composing; -using Umbraco.Core.Strings; -using Umbraco.Web.Models.ContentEditing; -using Umbraco.Web.Trees; namespace Umbraco.Web.Search { - /// - /// Configures and installs Examine. - /// - [RuntimeLevel(MinLevel = RuntimeLevel.Run)] - public sealed class ExamineComponent : UmbracoComponentBase, IUmbracoCoreComponent + public sealed class ExamineComponent : IComponent { - private IExamineManager _examineManager; - private IContentValueSetBuilder _contentValueSetBuilder; - private IPublishedContentValueSetBuilder _publishedContentValueSetBuilder; - private IValueSetBuilder _mediaValueSetBuilder; - private IValueSetBuilder _memberValueSetBuilder; + private readonly IExamineManager _examineManager; + private readonly IContentValueSetBuilder _contentValueSetBuilder; + private readonly IPublishedContentValueSetBuilder _publishedContentValueSetBuilder; + private readonly IValueSetBuilder _mediaValueSetBuilder; + private readonly IValueSetBuilder _memberValueSetBuilder; private static bool _disableExamineIndexing = false; private static volatile bool _isConfigured = false; private static readonly object IsConfiguredLocker = new object(); - private IScopeProvider _scopeProvider; - private ServiceContext _services; + private readonly IScopeProvider _scopeProvider; + private readonly ServiceContext _services; private static BackgroundTaskRunner _rebuildOnStartupRunner; private static readonly object RebuildLocker = new object(); + private readonly IMainDom _mainDom; + private readonly IProfilingLogger _logger; + private readonly IUmbracoIndexesCreator _indexCreator; + private readonly IndexRebuilder _indexRebuilder; // the default enlist priority is 100 // enlist with a lower priority to ensure that anything "default" runs after us // but greater that SafeXmlReaderWriter priority which is 60 private const int EnlistPriority = 80; - - public override void Compose(Composition composition) - { - base.Compose(composition); - - //fixme: I cannot do this since RegisterSingleton acts like TryRegisterSingleton and only allows one - //composition.Container.RegisterSingleton(); - //composition.Container.RegisterSingleton(); - //composition.Container.RegisterSingleton(); - //composition.Container.RegisterSingleton(); - - // fixme -- CHANGE THIS WHEN THE DI PR IS MERGED - //fixme: Instead i have to do this, but this means that developers adding their own will also need to do this which isn't ideal - composition.Container.RegisterMany(new[] - { - typeof(MemberIndexPopulator), - typeof(ContentIndexPopulator), - typeof(PublishedContentIndexPopulator), - typeof(MediaIndexPopulator), - }); - - composition.Container.RegisterSingleton(); - composition.Container.RegisterSingleton(); - composition.Container.Register(factory => - new ContentValueSetBuilder( - factory.GetInstance(), - factory.GetInstance>(), - factory.GetInstance(), - true)); - composition.Container.Register(factory => - new ContentValueSetBuilder( - factory.GetInstance(), - factory.GetInstance>(), - factory.GetInstance(), - false)); - composition.Container.RegisterSingleton, MediaValueSetBuilder>(); - composition.Container.RegisterSingleton, MemberValueSetBuilder>(); - - //We want to manage Examine's appdomain shutdown sequence ourselves so first we'll disable Examine's default behavior - //and then we'll use MainDom to control Examine's shutdown - this MUST be done in Compose ie before ExamineManager - //is instantiated, as the value is used during instantiation - ExamineManager.DisableDefaultHostingEnvironmentRegistration(); - } - - internal void Initialize(IRuntimeState runtime, MainDom mainDom, PropertyEditorCollection propertyEditors, - IExamineManager examineManager, ProfilingLogger profilingLogger, + public ExamineComponent(IMainDom mainDom, + IExamineManager examineManager, IProfilingLogger profilingLogger, IScopeProvider scopeProvider, IUmbracoIndexesCreator indexCreator, IndexRebuilder indexRebuilder, ServiceContext services, IContentValueSetBuilder contentValueSetBuilder, @@ -117,6 +63,14 @@ namespace Umbraco.Web.Search _mediaValueSetBuilder = mediaValueSetBuilder; _memberValueSetBuilder = memberValueSetBuilder; + _mainDom = mainDom; + _logger = profilingLogger; + _indexCreator = indexCreator; + _indexRebuilder = indexRebuilder; + } + + public void Initialize() + { //we want to tell examine to use a different fs lock instead of the default NativeFSFileLock which could cause problems if the appdomain //terminates and in some rare cases would only allow unlocking of the file if IIS is forcefully terminated. Instead we'll rely on the simplefslock //which simply checks the existence of the lock file @@ -127,9 +81,9 @@ namespace Umbraco.Web.Search }; //let's deal with shutting down Examine with MainDom - var examineShutdownRegistered = mainDom.Register(() => + var examineShutdownRegistered = _mainDom.Register(() => { - using (profilingLogger.TraceDuration("Examine shutting down")) + using (_logger.TraceDuration("Examine shutting down")) { _examineManager.Dispose(); } @@ -137,23 +91,23 @@ namespace Umbraco.Web.Search if (!examineShutdownRegistered) { - profilingLogger.Logger.Debug("Examine shutdown not registered, this appdomain is not the MainDom, Examine will be disabled"); + _logger.Debug("Examine shutdown not registered, this appdomain is not the MainDom, Examine will be disabled"); //if we could not register the shutdown examine ourselves, it means we are not maindom! in this case all of examine should be disabled! - Suspendable.ExamineEvents.SuspendIndexers(profilingLogger.Logger); + Suspendable.ExamineEvents.SuspendIndexers(_logger); _disableExamineIndexing = true; return; //exit, do not continue } //create the indexes and register them with the manager - foreach(var index in indexCreator.Create()) + foreach(var index in _indexCreator.Create()) _examineManager.AddIndex(index); - profilingLogger.Logger.Debug("Examine shutdown registered with MainDom"); + _logger.Debug("Examine shutdown registered with MainDom"); - var registeredIndexers = examineManager.Indexes.OfType().Count(x => x.EnableDefaultEventHandler); + var registeredIndexers = _examineManager.Indexes.OfType().Count(x => x.EnableDefaultEventHandler); - profilingLogger.Logger.Info("Adding examine event handlers for {RegisteredIndexers} index providers.", registeredIndexers); + _logger.Info("Adding examine event handlers for {RegisteredIndexers} index providers.", registeredIndexers); // don't bind event handlers if we're not suppose to listen if (registeredIndexers == 0) @@ -166,12 +120,15 @@ namespace Umbraco.Web.Search MediaCacheRefresher.CacheUpdated += MediaCacheRefresherUpdated; MemberCacheRefresher.CacheUpdated += MemberCacheRefresherUpdated; - EnsureUnlocked(profilingLogger.Logger, examineManager); + EnsureUnlocked(_logger, _examineManager); //TODO: Instead of waiting 5000 ms, we could add an event handler on to fulfilling the first request, then start? - RebuildIndexes(indexRebuilder, profilingLogger.Logger, true, 5000); + RebuildIndexes(_indexRebuilder, _logger, true, 5000); } + public void Terminate() + { } + /// /// Called to rebuild empty indexes on startup /// @@ -204,8 +161,6 @@ namespace Umbraco.Web.Search } } - - /// /// Must be called to each index is unlocked before any indexing occurs /// @@ -582,7 +537,6 @@ namespace Umbraco.Web.Search } } - #endregion #region ReIndex/Delete for entity @@ -783,7 +737,6 @@ namespace Umbraco.Web.Search { var strId = id.ToString(CultureInfo.InvariantCulture); foreach (var index in examineComponent._examineManager.Indexes.OfType() - .Where(x => (keepIfUnpublished && !x.PublishedValuesOnly) || !keepIfUnpublished) .Where(x => x.EnableDefaultEventHandler)) { diff --git a/src/Umbraco.Web/Search/ExamineComposer.cs b/src/Umbraco.Web/Search/ExamineComposer.cs new file mode 100644 index 0000000000..8ee2caedee --- /dev/null +++ b/src/Umbraco.Web/Search/ExamineComposer.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using Examine; +using Umbraco.Core; +using Umbraco.Core.Components; +using Umbraco.Core.Composing; +using Umbraco.Core.Models; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; +using Umbraco.Core.Strings; +using Umbraco.Examine; + +namespace Umbraco.Web.Search +{ + /// + /// Configures and installs Examine. + /// + [RuntimeLevel(MinLevel = RuntimeLevel.Run)] + public sealed class ExamineComposer : ComponentComposer, ICoreComposer + { + public override void Compose(Composition composition) + { + base.Compose(composition); + + // populators are not a collection: one cannot remove ours, and can only add more + // the container can inject IEnumerable and get them all + composition.Register(Lifetime.Singleton); + composition.Register(Lifetime.Singleton); + composition.Register(Lifetime.Singleton); + composition.Register(Lifetime.Singleton); + + composition.Register(Lifetime.Singleton); + composition.RegisterUnique(); + composition.RegisterUnique(factory => + new ContentValueSetBuilder( + factory.GetInstance(), + factory.GetInstance>(), + factory.GetInstance(), + true)); + composition.RegisterUnique(factory => + new ContentValueSetBuilder( + factory.GetInstance(), + factory.GetInstance>(), + factory.GetInstance(), + false)); + composition.RegisterUnique, MediaValueSetBuilder>(); + composition.RegisterUnique, MemberValueSetBuilder>(); + + //We want to manage Examine's appdomain shutdown sequence ourselves so first we'll disable Examine's default behavior + //and then we'll use MainDom to control Examine's shutdown - this MUST be done in Compose ie before ExamineManager + //is instantiated, as the value is used during instantiation + ExamineManager.DisableDefaultHostingEnvironmentRegistration(); + } + } +} diff --git a/src/Umbraco.Web/Search/SearchableTreeCollectionBuilder.cs b/src/Umbraco.Web/Search/SearchableTreeCollectionBuilder.cs index 22db27b1fb..2d668257ef 100644 --- a/src/Umbraco.Web/Search/SearchableTreeCollectionBuilder.cs +++ b/src/Umbraco.Web/Search/SearchableTreeCollectionBuilder.cs @@ -1,28 +1,13 @@ -using LightInject; -using Umbraco.Core.Composing; -using Umbraco.Core.Services; +using Umbraco.Core.Composing; using Umbraco.Web.Trees; namespace Umbraco.Web.Search { internal class SearchableTreeCollectionBuilder : LazyCollectionBuilderBase { - private readonly IApplicationTreeService _treeService; - - public SearchableTreeCollectionBuilder(IServiceContainer container, IApplicationTreeService treeService) - : base(container) - { - _treeService = treeService; - } - protected override SearchableTreeCollectionBuilder This => this; - public override SearchableTreeCollection CreateCollection() - { - return new SearchableTreeCollection(CreateItems(), _treeService); - } - //per request because generally an instance of ISearchableTree is a controller - protected override ILifetime CollectionLifetime => new PerRequestLifeTime(); + protected override Lifetime CollectionLifetime => Lifetime.Request; } } diff --git a/src/Umbraco.Web/Search/UmbracoIndexesCreator.cs b/src/Umbraco.Web/Search/UmbracoIndexesCreator.cs index 4296176abf..99f4c4b453 100644 --- a/src/Umbraco.Web/Search/UmbracoIndexesCreator.cs +++ b/src/Umbraco.Web/Search/UmbracoIndexesCreator.cs @@ -1,20 +1,11 @@ using System.Collections.Generic; using Umbraco.Core.Logging; using Umbraco.Core.Services; -using Umbraco.Core.Strings; using Umbraco.Examine; -using Umbraco.Core.Persistence; -using Umbraco.Core.IO; -using System.IO; -using Lucene.Net.Store; using Lucene.Net.Analysis.Standard; -using Lucene.Net.Analysis; using Examine.LuceneEngine; using Examine; -using Examine.LuceneEngine.Providers; -using System.Linq; using Umbraco.Core; -using Umbraco.Core.Models; namespace Umbraco.Web.Search { @@ -25,7 +16,7 @@ namespace Umbraco.Web.Search { //TODO: we should inject the different IValueSetValidator so devs can just register them instead of overriding this class? - public UmbracoIndexesCreator(ProfilingLogger profilingLogger, + public UmbracoIndexesCreator(IProfilingLogger profilingLogger, ILocalizationService languageService, IPublicAccessService publicAccessService, IMemberService memberService) @@ -36,7 +27,7 @@ namespace Umbraco.Web.Search MemberService = memberService ?? throw new System.ArgumentNullException(nameof(memberService)); } - protected ProfilingLogger ProfilingLogger { get; } + protected IProfilingLogger ProfilingLogger { get; } protected ILocalizationService LanguageService { get; } protected IPublicAccessService PublicAccessService { get; } protected IMemberService MemberService { get; } @@ -59,11 +50,11 @@ namespace Umbraco.Web.Search { var index = new UmbracoContentIndex( Constants.UmbracoIndexes.InternalIndexName, - new UmbracoFieldDefinitionCollection(), CreateFileSystemLuceneDirectory(Constants.UmbracoIndexes.InternalIndexPath), + new UmbracoFieldDefinitionCollection(), new CultureInvariantWhitespaceAnalyzer(), ProfilingLogger, - LanguageService, + LanguageService, GetContentValueSetValidator()); return index; } @@ -72,8 +63,8 @@ namespace Umbraco.Web.Search { var index = new UmbracoContentIndex( Constants.UmbracoIndexes.ExternalIndexName, - new UmbracoFieldDefinitionCollection(), CreateFileSystemLuceneDirectory(Constants.UmbracoIndexes.ExternalIndexPath), + new UmbracoFieldDefinitionCollection(), new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30), ProfilingLogger, LanguageService, @@ -88,7 +79,7 @@ namespace Umbraco.Web.Search new UmbracoFieldDefinitionCollection(), CreateFileSystemLuceneDirectory(Constants.UmbracoIndexes.MembersIndexPath), new CultureInvariantWhitespaceAnalyzer(), - ProfilingLogger, + ProfilingLogger, GetMemberValueSetValidator()); return index; } @@ -111,6 +102,6 @@ namespace Umbraco.Web.Search { return new MemberValueSetValidator(); } - + } } diff --git a/src/Umbraco.Web/Security/AppBuilderExtensions.cs b/src/Umbraco.Web/Security/AppBuilderExtensions.cs index ddcf87e9c7..22d87bdc14 100644 --- a/src/Umbraco.Web/Security/AppBuilderExtensions.cs +++ b/src/Umbraco.Web/Security/AppBuilderExtensions.cs @@ -211,8 +211,8 @@ namespace Umbraco.Web.Security //This is a custom middleware, we need to return the user's remaining logged in seconds app.Use( cookieAuthOptions, - UmbracoConfig.For.GlobalSettings(), - UmbracoConfig.For.UmbracoSettings().Security, + Current.Configs.Global(), + Current.Configs.Settings().Security, app.CreateLogger()); //This is required so that we can read the auth ticket format outside of this pipeline @@ -316,7 +316,7 @@ namespace Umbraco.Web.Security CookiePath = "/", CookieSecure = globalSettings.UseHttps ? CookieSecureOption.Always : CookieSecureOption.SameAsRequest, CookieHttpOnly = true, - CookieDomain = UmbracoConfig.For.UmbracoSettings().Security.AuthCookieDomain + CookieDomain = Current.Configs.Settings().Security.AuthCookieDomain }, stage); return app; @@ -362,7 +362,7 @@ namespace Umbraco.Web.Security if (runtimeState.Level != RuntimeLevel.Run) return app; var authOptions = app.CreateUmbracoCookieAuthOptions(umbracoContextAccessor, globalSettings, runtimeState, securitySettings); - app.Use(typeof(PreviewAuthenticationMiddleware), authOptions, UmbracoConfig.For.GlobalSettings()); + app.Use(typeof(PreviewAuthenticationMiddleware), authOptions, Current.Configs.Global()); // This middleware must execute at least on PostAuthentication, by default it is on Authorize // The middleware needs to execute after the RoleManagerModule executes which is during PostAuthenticate, @@ -391,7 +391,7 @@ namespace Umbraco.Web.Security /// /// public static UmbracoBackOfficeCookieAuthOptions CreateUmbracoCookieAuthOptions(this IAppBuilder app, - IUmbracoContextAccessor umbracoContextAccessor, + IUmbracoContextAccessor umbracoContextAccessor, IGlobalSettings globalSettings, IRuntimeState runtimeState, ISecuritySection securitySettings, string[] explicitPaths = null) { //this is how aspnet wires up the default AuthenticationTicket protector so we'll use the same code diff --git a/src/Umbraco.Web/Security/AuthenticationExtensions.cs b/src/Umbraco.Web/Security/AuthenticationExtensions.cs index 691d577d51..66fbd8e201 100644 --- a/src/Umbraco.Web/Security/AuthenticationExtensions.cs +++ b/src/Umbraco.Web/Security/AuthenticationExtensions.cs @@ -145,7 +145,7 @@ namespace Umbraco.Web.Security public static void UmbracoLogout(this HttpContextBase http) { if (http == null) throw new ArgumentNullException("http"); - Logout(http, UmbracoConfig.For.UmbracoSettings().Security.AuthCookieName); + Logout(http, Current.Configs.Settings().Security.AuthCookieName); } /// @@ -181,7 +181,7 @@ namespace Umbraco.Web.Security http.Items[Constants.Security.ForceReAuthFlag] = true; return true; } - + /// /// returns the number of seconds the user has until their auth session times out /// @@ -193,7 +193,7 @@ namespace Umbraco.Web.Security var ticket = http.GetUmbracoAuthTicket(); return ticket.GetRemainingAuthSeconds(); } - + /// /// returns the number of seconds the user has until their auth session times out /// @@ -215,7 +215,7 @@ namespace Umbraco.Web.Security public static AuthenticationTicket GetUmbracoAuthTicket(this HttpContextBase http) { if (http == null) throw new ArgumentNullException(nameof(http)); - return GetAuthTicket(http, UmbracoConfig.For.UmbracoSettings().Security.AuthCookieName); + return GetAuthTicket(http, Current.Configs.Settings().Security.AuthCookieName); } internal static AuthenticationTicket GetUmbracoAuthTicket(this HttpContext http) @@ -227,7 +227,7 @@ namespace Umbraco.Web.Security public static AuthenticationTicket GetUmbracoAuthTicket(this IOwinContext ctx) { if (ctx == null) throw new ArgumentNullException(nameof(ctx)); - return GetAuthTicket(ctx, UmbracoConfig.For.UmbracoSettings().Security.AuthCookieName); + return GetAuthTicket(ctx, Current.Configs.Settings().Security.AuthCookieName); } /// @@ -342,7 +342,7 @@ namespace Umbraco.Web.Security return null; } //get the ticket - + return secureDataFormat.Unprotect(formsCookie); } } diff --git a/src/Umbraco.Web/Security/BackOfficeCookieAuthenticationProvider.cs b/src/Umbraco.Web/Security/BackOfficeCookieAuthenticationProvider.cs index e12e2ac4f8..bf22e82bbc 100644 --- a/src/Umbraco.Web/Security/BackOfficeCookieAuthenticationProvider.cs +++ b/src/Umbraco.Web/Security/BackOfficeCookieAuthenticationProvider.cs @@ -21,7 +21,7 @@ namespace Umbraco.Web.Security private readonly IUserService _userService; private readonly IRuntimeState _runtimeState; private readonly IGlobalSettings _globalSettings; - + public BackOfficeCookieAuthenticationProvider(IUserService userService, IRuntimeState runtimeState, IGlobalSettings globalSettings) { _userService = userService; @@ -67,7 +67,7 @@ namespace Umbraco.Web.Security Expires = DateTime.Now.AddYears(-1), Path = "/" }); - context.Response.Cookies.Append(UmbracoConfig.For.UmbracoSettings().Security.AuthCookieName, "", new CookieOptions + context.Response.Cookies.Append(Current.Configs.Settings().Security.AuthCookieName, "", new CookieOptions { Expires = DateTime.Now.AddYears(-1), Path = "/" @@ -111,8 +111,8 @@ namespace Umbraco.Web.Security await SessionIdValidator.ValidateSessionAsync(TimeSpan.FromMinutes(1), context, _globalSettings); } - - + + } } diff --git a/src/Umbraco.Web/Security/ExternalSignInAutoLinkOptions.cs b/src/Umbraco.Web/Security/ExternalSignInAutoLinkOptions.cs index a143233ff2..44fbdda6c7 100644 --- a/src/Umbraco.Web/Security/ExternalSignInAutoLinkOptions.cs +++ b/src/Umbraco.Web/Security/ExternalSignInAutoLinkOptions.cs @@ -1,5 +1,7 @@ using System; using Microsoft.AspNet.Identity.Owin; +using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Models.Identity; @@ -23,7 +25,7 @@ namespace Umbraco.Web.Security { _defaultUserGroups = defaultUserGroups ?? new[] { "editor" }; _autoLinkExternalAccount = autoLinkExternalAccount; - _defaultCulture = defaultCulture ?? UmbracoConfig.For.GlobalSettings().DefaultUILanguage; + _defaultCulture = defaultCulture ?? Current.Configs.Global().DefaultUILanguage; } private readonly string[] _defaultUserGroups; diff --git a/src/Umbraco.Web/Security/MembershipHelper.cs b/src/Umbraco.Web/Security/MembershipHelper.cs index 0a80058d2f..eb4c24aabc 100644 --- a/src/Umbraco.Web/Security/MembershipHelper.cs +++ b/src/Umbraco.Web/Security/MembershipHelper.cs @@ -1,11 +1,9 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Linq; using System.Text; using System.Web; using System.Web.Security; -using LightInject; using Umbraco.Core; using Umbraco.Core.Logging; using Umbraco.Core.Models; @@ -17,9 +15,7 @@ using Umbraco.Core.Composing; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Services; using Umbraco.Web.Editors; -using Umbraco.Web.Security.Providers; using Umbraco.Web.Routing; -using MPE = global::Umbraco.Core.Security.MembershipProviderExtensions; namespace Umbraco.Web.Security { @@ -28,72 +24,52 @@ namespace Umbraco.Web.Security /// public class MembershipHelper { + private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly MembershipProvider _membershipProvider; private readonly RoleProvider _roleProvider; - private readonly HttpContextBase _httpContext; - private readonly IPublishedMemberCache _memberCache; - private readonly UmbracoContext _umbracoContext; - - [Inject] - private IMemberService MemberService { get; set; } - - [Inject] - private IMemberTypeService MemberTypeService { get; set; } - - [Inject] - private IUserService UserService { get; set; } - - [Inject] - private IPublicAccessService PublicAccessService { get; set; } - - [Inject] - private CacheHelper ApplicationCache { get; set; } - - [Inject] - private ILogger Logger { get; set; } - - [Inject] - private PublishedRouter Router { get; set; } + private readonly IMemberService _memberService; + private readonly IMemberTypeService _memberTypeService; + private readonly IUserService _userService; + private readonly IPublicAccessService _publicAccessService; + private readonly PublishedRouter _publishedRouter; + private readonly CacheHelper _appCaches; + private readonly ILogger _logger; #region Constructors - // used here and there for IMember operations (not front-end stuff, no need for _memberCache) - - - // used everywhere - public MembershipHelper(UmbracoContext umbracoContext) - : this(umbracoContext, MPE.GetMembersMembershipProvider(), Roles.Enabled ? Roles.Provider : new MembersRoleProvider(Current.Services.MemberService)) - { } - - // used in tests and (this) - public MembershipHelper(UmbracoContext umbracoContext, MembershipProvider membershipProvider, RoleProvider roleProvider) + public MembershipHelper + ( + IUmbracoContextAccessor accessor, + MembershipProvider membershipProvider, + RoleProvider roleProvider, + IMemberService memberService, + IMemberTypeService memberTypeService, + IUserService userService, + IPublicAccessService publicAccessService, + PublishedRouter publishedRouter, + CacheHelper appCaches, + ILogger logger + ) { - if (umbracoContext == null) throw new ArgumentNullException(nameof(umbracoContext)); - if (membershipProvider == null) throw new ArgumentNullException(nameof(membershipProvider)); - if (roleProvider == null) throw new ArgumentNullException(nameof(roleProvider)); + _umbracoContextAccessor = accessor ?? throw new ArgumentNullException(nameof(accessor)); - _httpContext = umbracoContext.HttpContext; - _umbracoContext = umbracoContext; - _membershipProvider = membershipProvider; - _roleProvider = roleProvider; - _memberCache = umbracoContext.PublishedSnapshot.Members; + _memberService = memberService; + _memberTypeService = memberTypeService; + _userService = userService; + _publicAccessService = publicAccessService; + _publishedRouter = publishedRouter; + _appCaches = appCaches; + _logger = logger; - // helpers are *not* instanciated by the container so we have to - // get our dependencies injected manually, through properties. - Current.Container.InjectProperties(this); + _membershipProvider = membershipProvider ?? throw new ArgumentNullException(nameof(membershipProvider)); + _roleProvider = roleProvider ?? throw new ArgumentNullException(nameof(roleProvider)); } #endregion - protected IPublishedMemberCache MemberCache - { - get - { - if (_memberCache == null) - throw new InvalidOperationException("No MemberCache."); - return _memberCache; - } - } + protected UmbracoContext UmbracoContext => _umbracoContextAccessor.UmbracoContext ?? throw new InvalidOperationException("No UmbracoContext."); + protected HttpContextBase HttpContext => UmbracoContext.HttpContext ?? throw new InvalidOperationException("No UmbracoContext.HttpContext."); + protected IPublishedMemberCache MemberCache => UmbracoContext.PublishedSnapshot.Members ?? throw new InvalidOperationException("No UmbracoContext.PUblishedSnapshot.Members."); /// /// Check if a document object is protected by the "Protect Pages" functionality in umbraco @@ -103,7 +79,7 @@ namespace Umbraco.Web.Security public virtual bool IsProtected(string path) { //this is a cached call - return PublicAccessService.IsProtected(path); + return _publicAccessService.IsProtected(path); } /// @@ -114,7 +90,7 @@ namespace Umbraco.Web.Security public virtual bool MemberHasAccess(string path) { //cache this in the request cache - return ApplicationCache.RequestCache.GetCacheItem(string.Format("{0}.{1}-{2}", typeof(MembershipHelper), "MemberHasAccess", path), () => + return _appCaches.RequestCache.GetCacheItem($"{typeof(MembershipHelper)}.MemberHasAccess-{path}", () => { if (IsProtected(path)) { @@ -138,9 +114,9 @@ namespace Umbraco.Web.Security /// private bool HasAccess(string path, RoleProvider roleProvider) { - return _umbracoContext.PublishedRequest == null - ? PublicAccessService.HasAccess(path, CurrentUserName, roleProvider.GetRolesForUser) - : PublicAccessService.HasAccess(path, CurrentUserName, Router.GetRolesForLogin); + return UmbracoContext.PublishedRequest == null + ? _publicAccessService.HasAccess(path, CurrentUserName, roleProvider.GetRolesForUser) + : _publicAccessService.HasAccess(path, CurrentUserName, _publishedRouter.GetRolesForLogin); } /// @@ -171,7 +147,7 @@ namespace Umbraco.Web.Security var provider = _membershipProvider; var membershipUser = provider.GetCurrentUser(); //NOTE: This should never happen since they are logged in - if (membershipUser == null) throw new InvalidOperationException("Could not find member with username " + _httpContext.User.Identity.Name); + if (membershipUser == null) throw new InvalidOperationException("Could not find member with username " + HttpContext.User.Identity.Name); try { @@ -211,7 +187,7 @@ namespace Umbraco.Web.Security } } - MemberService.Save(member); + _memberService.Save(member); //reset the FormsAuth cookie since the username might have changed FormsAuthentication.SetAuthCookie(member.Username, true); @@ -246,7 +222,7 @@ namespace Umbraco.Web.Security if (status != MembershipCreateStatus.Success) return null; - var member = MemberService.GetByUsername(membershipUser.UserName); + var member = _memberService.GetByUsername(membershipUser.UserName); member.Name = model.Name; if (model.MemberProperties != null) @@ -258,7 +234,7 @@ namespace Umbraco.Web.Security } } - MemberService.Save(member); + _memberService.Save(member); } else { @@ -461,7 +437,7 @@ namespace Umbraco.Web.Security if (provider.IsUmbracoMembershipProvider()) { memberTypeAlias = memberTypeAlias ?? Constants.Conventions.MemberTypes.DefaultAlias; - var memberType = MemberTypeService.Get(memberTypeAlias); + var memberType = _memberTypeService.Get(memberTypeAlias); if (memberType == null) throw new InvalidOperationException("Could not find a member type with alias " + memberTypeAlias); @@ -597,13 +573,13 @@ namespace Umbraco.Web.Security /// public bool IsLoggedIn() { - return _httpContext.User != null && _httpContext.User.Identity.IsAuthenticated; + return HttpContext.User != null && HttpContext.User.Identity.IsAuthenticated; } /// /// Returns the currently logged in username /// - public string CurrentUserName => _httpContext.User.Identity.Name; + public string CurrentUserName => HttpContext.User.Identity.Name; /// /// Returns true or false if the currently logged in member is authorized based on the parameters provided @@ -715,7 +691,7 @@ namespace Umbraco.Web.Security /// public virtual Attempt ChangePassword(string username, ChangingPasswordModel passwordModel, MembershipProvider membershipProvider) { - var passwordChanger = new PasswordChanger(Logger, UserService, Web.Composing.Current.UmbracoContext.HttpContext); + var passwordChanger = new PasswordChanger(_logger, _userService, Web.Composing.Current.UmbracoContext.HttpContext); return passwordChanger.ChangePasswordWithMembershipProvider(username, passwordModel, membershipProvider); } @@ -780,7 +756,7 @@ namespace Umbraco.Web.Security /// private IMember GetCurrentPersistedMember() { - return ApplicationCache.RequestCache.GetCacheItem( + return _appCaches.RequestCache.GetCacheItem( GetCacheKey("GetCurrentPersistedMember"), () => { var provider = _membershipProvider; @@ -790,7 +766,7 @@ namespace Umbraco.Web.Security throw new NotSupportedException("An IMember model can only be retreived when using the built-in Umbraco membership providers"); } var username = provider.GetCurrentUserName(); - var member = MemberService.GetByUsername(username); + var member = _memberService.GetByUsername(username); return member; }); } diff --git a/src/Umbraco.Web/Security/Providers/UsersMembershipProvider.cs b/src/Umbraco.Web/Security/Providers/UsersMembershipProvider.cs index 5af71f1bdd..f5002cdcd3 100644 --- a/src/Umbraco.Web/Security/Providers/UsersMembershipProvider.cs +++ b/src/Umbraco.Web/Security/Providers/UsersMembershipProvider.cs @@ -38,7 +38,7 @@ namespace Umbraco.Web.Security.Providers public override string ProviderName { - get { return UmbracoConfig.For.UmbracoSettings().Providers.DefaultBackOfficeUserProvider; } + get { return Current.Configs.Settings().Providers.DefaultBackOfficeUserProvider; } } protected override MembershipUser ConvertToMembershipUser(IUser entity) diff --git a/src/Umbraco.Web/Security/WebSecurity.cs b/src/Umbraco.Web/Security/WebSecurity.cs index 709f0d719a..18ef806ad1 100644 --- a/src/Umbraco.Web/Security/WebSecurity.cs +++ b/src/Umbraco.Web/Security/WebSecurity.cs @@ -1,24 +1,20 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Linq; using System.Security; using System.Web; -using System.Web.Security; -using AutoMapper; using Umbraco.Core; using Umbraco.Core.Services; -using Umbraco.Core.Logging; using Umbraco.Core.Models.Membership; using Umbraco.Core.Security; using Microsoft.AspNet.Identity.Owin; using Microsoft.Owin; using Umbraco.Core.Configuration; +using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.Models; using Umbraco.Core.Models.Identity; -using Umbraco.Web.Composing; -using GlobalSettings = Umbraco.Core.Configuration.GlobalSettings; +using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Web.Security { @@ -57,7 +53,7 @@ namespace Umbraco.Web.Security { return false; } - var helper = new MembershipHelper(Current.UmbracoContext); + var helper = Current.Factory.GetInstance(); return helper.IsMemberAuthorized(allowAll, allowTypes, allowGroups, allowMembers); } @@ -288,7 +284,7 @@ namespace Umbraco.Web.Security /// public bool IsAuthenticated() { - return _httpContext.User.Identity.IsAuthenticated && _httpContext.GetCurrentIdentity(false) != null; + return _httpContext.User != null && _httpContext.User.Identity.IsAuthenticated && _httpContext.GetCurrentIdentity(false) != null; } protected override void DisposeResources() diff --git a/src/Umbraco.Web/SignalR/PreviewHubComponent.cs b/src/Umbraco.Web/SignalR/PreviewHubComponent.cs index 07467d3bfa..328f5a06ed 100644 --- a/src/Umbraco.Web/SignalR/PreviewHubComponent.cs +++ b/src/Umbraco.Web/SignalR/PreviewHubComponent.cs @@ -1,47 +1,47 @@ using System; -using LightInject; using Microsoft.AspNet.SignalR; -using Umbraco.Core; +using Umbraco.Core.Cache; using Umbraco.Core.Components; using Umbraco.Core.Sync; using Umbraco.Web.Cache; namespace Umbraco.Web.SignalR { - [RuntimeLevel(MinLevel = RuntimeLevel.Run)] - public class PreviewHubComponent : UmbracoComponentBase, IUmbracoCoreComponent + public class PreviewHubComponent : IComponent { - public override void Compose(Composition composition) - { - base.Compose(composition); - composition.Container.Register(_ => GlobalHost.ConnectionManager.GetHubContext(), new PerContainerLifetime()); - } + private readonly Lazy> _hubContext; // using a lazy arg here means that we won't create the hub until necessary // and therefore we won't have too bad an impact on boot time - public void Initialize(Lazy> hubContext) + public PreviewHubComponent(Lazy> hubContext) { - // ContentService.Saved is too soon - the content cache is not ready yet - // try using the content cache refresher event, because when it triggers - // the cache has already been notified of the changes - //ContentService.Saved += (sender, args) => - //{ - // var entity = args.SavedEntities.FirstOrDefault(); - // if (entity != null) - // _previewHub.Clients.All.refreshed(entity.Id); - //}; + _hubContext = hubContext; + } - ContentCacheRefresher.CacheUpdated += (sender, args) => + public void Initialize() + { + // ContentService.Saved is too soon - the content cache is not ready yet, + // so use the content cache refresher event, because when it triggers + // the cache has already been notified of the changes + + ContentCacheRefresher.CacheUpdated += HandleCacheUpdated; + } + + public void Terminate() + { + ContentCacheRefresher.CacheUpdated -= HandleCacheUpdated; + } + + private void HandleCacheUpdated(ContentCacheRefresher sender, CacheRefresherEventArgs args) + { + if (args.MessageType != MessageType.RefreshByPayload) return; + var payloads = (ContentCacheRefresher.JsonPayload[])args.MessageObject; + var hubContextInstance = _hubContext.Value; + foreach (var payload in payloads) { - if (args.MessageType != MessageType.RefreshByPayload) return; - var payloads = (ContentCacheRefresher.JsonPayload[])args.MessageObject; - var hubContextInstance = hubContext.Value; - foreach (var payload in payloads) - { - var id = payload.Id; // keep it simple for now, ignore ChangeTypes - hubContextInstance.Clients.All.refreshed(id); - } - }; + var id = payload.Id; // keep it simple for now, ignore ChangeTypes + hubContextInstance.Clients.All.refreshed(id); + } } } } diff --git a/src/Umbraco.Web/SignalR/PreviewHubComposer.cs b/src/Umbraco.Web/SignalR/PreviewHubComposer.cs new file mode 100644 index 0000000000..b92c6fe5a4 --- /dev/null +++ b/src/Umbraco.Web/SignalR/PreviewHubComposer.cs @@ -0,0 +1,17 @@ +using Microsoft.AspNet.SignalR; +using Umbraco.Core; +using Umbraco.Core.Components; + +namespace Umbraco.Web.SignalR +{ + [RuntimeLevel(MinLevel = RuntimeLevel.Run)] + public class PreviewHubComposer : ComponentComposer, ICoreComposer + { + public override void Compose(Composition composition) + { + base.Compose(composition); + + composition.RegisterUnique(_ => GlobalHost.ConnectionManager.GetHubContext()); + } + } +} diff --git a/src/Umbraco.Web/Suspendable.cs b/src/Umbraco.Web/Suspendable.cs index 3317afe7a4..0ca43ff595 100644 --- a/src/Umbraco.Web/Suspendable.cs +++ b/src/Umbraco.Web/Suspendable.cs @@ -32,7 +32,7 @@ namespace Umbraco.Web public static void SuspendDocumentCache() { - Current.ProfilingLogger.Logger.Info(typeof (PageCacheRefresher), "Suspend document cache."); + Current.Logger.Info(typeof (PageCacheRefresher), "Suspend document cache."); _suspended = true; } @@ -40,7 +40,7 @@ namespace Umbraco.Web { _suspended = false; - Current.ProfilingLogger.Logger.Info(typeof (PageCacheRefresher), "Resume document cache (reload:{Tried}).", _tried); + Current.Logger.Info(typeof (PageCacheRefresher), "Resume document cache (reload:{Tried}).", _tried); if (_tried == false) return; _tried = false; @@ -92,13 +92,13 @@ namespace Umbraco.Web public static void Suspend() { - Current.ProfilingLogger.Logger.Info(typeof (ScheduledPublishing), "Suspend scheduled publishing."); + Current.Logger.Info(typeof (ScheduledPublishing), "Suspend scheduled publishing."); _suspended = true; } public static void Resume() { - Current.ProfilingLogger.Logger.Info(typeof (ScheduledPublishing), "Resume scheduled publishing."); + Current.Logger.Info(typeof (ScheduledPublishing), "Resume scheduled publishing."); _suspended = false; } } diff --git a/src/Umbraco.Web/TagsController.cs b/src/Umbraco.Web/TagsController.cs index 4d82b8928d..181b9f7da2 100644 --- a/src/Umbraco.Web/TagsController.cs +++ b/src/Umbraco.Web/TagsController.cs @@ -1,4 +1,10 @@ using System.Collections.Generic; +using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence; +using Umbraco.Core.Services; using Umbraco.Web.Models; using Umbraco.Web.WebApi; @@ -14,6 +20,19 @@ namespace Umbraco.Web.WebServices // TODO: This controller should be moved to a more suitable place. public class TagsController : UmbracoApiController { + /// + /// Initializes a new instance of the with auto dependencies. + /// + public TagsController() + { } + + /// + /// Initializes a new instance of the with all its dependencies. + /// + public TagsController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, CacheHelper applicationCache, IProfilingLogger logger, IRuntimeState runtimeState) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, applicationCache, logger, runtimeState) + { } + /// /// Get every tag stored in the database (with optional group) /// diff --git a/src/Umbraco.Web/Templates/TemplateRenderer.cs b/src/Umbraco.Web/Templates/TemplateRenderer.cs index a6ef39a82b..a339f45714 100644 --- a/src/Umbraco.Web/Templates/TemplateRenderer.cs +++ b/src/Umbraco.Web/Templates/TemplateRenderer.cs @@ -10,10 +10,10 @@ using Umbraco.Web.Models; using Umbraco.Web.Mvc; using Umbraco.Web.Routing; using umbraco; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Services; -using LightInject; -using Umbraco.Web.Composing; +using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Web.Templates { @@ -25,23 +25,20 @@ namespace Umbraco.Web.Templates /// internal class TemplateRenderer { - private readonly IFileService _fileService = Current.Services.FileService; // fixme inject private readonly UmbracoContext _umbracoContext; - private object _oldPageId; private object _oldPageElements; private PublishedRequest _oldPublishedRequest; private object _oldAltTemplate; public TemplateRenderer(UmbracoContext umbracoContext, int pageId, int? altTemplateId) { - if (umbracoContext == null) throw new ArgumentNullException(nameof(umbracoContext)); PageId = pageId; - AltTemplate = altTemplateId; - _umbracoContext = umbracoContext; + AltTemplateId = altTemplateId; + _umbracoContext = umbracoContext ?? throw new ArgumentNullException(nameof(umbracoContext)); } - // todo - inject! - private PublishedRouter PublishedRouter => Core.Composing.Current.Container.GetInstance(); + private IFileService FileService => Current.Services.FileService; // fixme inject + private PublishedRouter PublishedRouter => Core.Composing.Current.Factory.GetInstance(); // fixme inject /// @@ -52,16 +49,15 @@ namespace Umbraco.Web.Templates /// /// Gets/sets the alt template to render if there is one /// - public int? AltTemplate { get; } + public int? AltTemplateId { get; } public void Render(StringWriter writer) { if (writer == null) throw new ArgumentNullException(nameof(writer)); - // instanciate a request a process + // instantiate a request and process // important to use CleanedUmbracoUrl - lowercase path-only version of the current url, though this isn't going to matter // terribly much for this implementation since we are just creating a doc content request to modify it's properties manually. - // fixme - previously that would create an aengine... var contentRequest = PublishedRouter.CreateRequest(_umbracoContext); var doc = contentRequest.UmbracoContext.ContentCache.GetById(PageId); @@ -90,20 +86,22 @@ namespace Umbraco.Web.Templates //set the doc that was found by id contentRequest.PublishedContent = doc; //set the template, either based on the AltTemplate found or the standard template of the doc - contentRequest.TemplateModel = UmbracoConfig.For.UmbracoSettings().WebRouting.DisableAlternativeTemplates || AltTemplate.HasValue == false - ? _fileService.GetTemplate(doc.TemplateId) - : _fileService.GetTemplate(AltTemplate.Value); + var templateId = Current.Configs.Settings().WebRouting.DisableAlternativeTemplates || !AltTemplateId.HasValue + ? doc.TemplateId + : AltTemplateId.Value; + if (templateId.HasValue) + contentRequest.TemplateModel = FileService.GetTemplate(templateId.Value); //if there is not template then exit if (contentRequest.HasTemplate == false) { - if (AltTemplate.HasValue == false) + if (AltTemplateId.HasValue == false) { writer.Write("", doc.TemplateId); } else { - writer.Write("", AltTemplate); + writer.Write("", AltTemplateId); } return; } @@ -160,8 +158,8 @@ namespace Umbraco.Web.Templates break; case RenderingEngine.WebForms: default: - var webFormshandler = (global::umbraco.UmbracoDefault)BuildManager - .CreateInstanceFromVirtualPath("~/default.aspx", typeof(global::umbraco.UmbracoDefault)); + var webFormshandler = (UmbracoDefault) BuildManager + .CreateInstanceFromVirtualPath("~/default.aspx", typeof(UmbracoDefault)); //the 'true' parameter will ensure that the current query strings are carried through, we don't have // to build up the url again, it will just work. _umbracoContext.HttpContext.Server.Execute(webFormshandler, sw, true); @@ -213,11 +211,10 @@ namespace Umbraco.Web.Templates private void SaveExistingItems() { //Many objects require that these legacy items are in the http context items... before we render this template we need to first - //save the values in them so that we can re-set them after we render so the rest of the execution works as per normal - _oldPageId = _umbracoContext.PageId; + //save the values in them so that we can re-set them after we render so the rest of the execution works as per normal _oldPageElements = _umbracoContext.HttpContext.Items["pageElements"]; _oldPublishedRequest = _umbracoContext.PublishedRequest; - _oldAltTemplate = _umbracoContext.HttpContext.Items[Umbraco.Core.Constants.Conventions.Url.AltTemplate]; + _oldAltTemplate = _umbracoContext.HttpContext.Items[Core.Constants.Conventions.Url.AltTemplate]; } /// @@ -227,7 +224,7 @@ namespace Umbraco.Web.Templates { _umbracoContext.PublishedRequest = _oldPublishedRequest; _umbracoContext.HttpContext.Items["pageElements"] = _oldPageElements; - _umbracoContext.HttpContext.Items[Umbraco.Core.Constants.Conventions.Url.AltTemplate] = _oldAltTemplate; + _umbracoContext.HttpContext.Items[Core.Constants.Conventions.Url.AltTemplate] = _oldAltTemplate; } } } diff --git a/src/Umbraco.Web/Templates/TemplateUtilities.cs b/src/Umbraco.Web/Templates/TemplateUtilities.cs index 91548e44f1..6da133413d 100644 --- a/src/Umbraco.Web/Templates/TemplateUtilities.cs +++ b/src/Umbraco.Web/Templates/TemplateUtilities.cs @@ -86,7 +86,7 @@ namespace Umbraco.Web.Templates /// public static string ResolveUrlsFromTextString(string text) { - if (UmbracoConfig.For.UmbracoSettings().Content.ResolveUrlsFromTextString == false) return text; + if (Current.Configs.Settings().Content.ResolveUrlsFromTextString == false) return text; using (var timer = Current.ProfilingLogger.DebugDuration(typeof(IOHelper), "ResolveUrlsFromTextString starting", "ResolveUrlsFromTextString complete")) { diff --git a/src/Umbraco.Web/Tour/TourFilterCollectionBuilder.cs b/src/Umbraco.Web/Tour/TourFilterCollectionBuilder.cs index 5084975bbd..0d2f605cb1 100644 --- a/src/Umbraco.Web/Tour/TourFilterCollectionBuilder.cs +++ b/src/Umbraco.Web/Tour/TourFilterCollectionBuilder.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; -using LightInject; using Umbraco.Core; using Umbraco.Core.Composing; @@ -15,17 +14,10 @@ namespace Umbraco.Web.Tour { private readonly HashSet _instances = new HashSet(); - /// - /// Initializes a new instance of the class. - /// - public TourFilterCollectionBuilder(IServiceContainer container) - : base(container) - { } - /// - protected override IEnumerable CreateItems(params object[] args) + protected override IEnumerable CreateItems(IFactory factory) { - return base.CreateItems(args).Concat(_instances); + return base.CreateItems(factory).Concat(_instances); } /// diff --git a/src/Umbraco.Web/Trees/ApplicationTreeController.cs b/src/Umbraco.Web/Trees/ApplicationTreeController.cs index d824f32f4b..0454155772 100644 --- a/src/Umbraco.Web/Trees/ApplicationTreeController.cs +++ b/src/Umbraco.Web/Trees/ApplicationTreeController.cs @@ -9,7 +9,6 @@ using System.Web.Http; using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Services; -using Umbraco.Web.Composing; using Umbraco.Web.Models.Trees; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; diff --git a/src/Umbraco.Web/Trees/ApplicationTreeExtensions.cs b/src/Umbraco.Web/Trees/ApplicationTreeExtensions.cs index c688491ebb..2c43cbd5dc 100644 --- a/src/Umbraco.Web/Trees/ApplicationTreeExtensions.cs +++ b/src/Umbraco.Web/Trees/ApplicationTreeExtensions.cs @@ -14,7 +14,6 @@ using Umbraco.Web.Models.Trees; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; using Umbraco.Core.Composing; -using Umbraco.Core.Services; using Current = Umbraco.Web.Composing.Current; using ApplicationTree = Umbraco.Core.Models.ApplicationTree; diff --git a/src/Umbraco.Web/Trees/ContentBlueprintTreeController.cs b/src/Umbraco.Web/Trees/ContentBlueprintTreeController.cs index 365308a8f2..cce40eb047 100644 --- a/src/Umbraco.Web/Trees/ContentBlueprintTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentBlueprintTreeController.cs @@ -1,14 +1,12 @@ using System.Linq; using System.Net.Http.Formatting; using Umbraco.Core; -using Umbraco.Core.Services; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; using Umbraco.Web.Actions; using Umbraco.Web.Models.Trees; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi.Filters; - using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Trees @@ -117,6 +115,5 @@ namespace Umbraco.Web.Trees return menu; } - } } diff --git a/src/Umbraco.Web/Trees/ContentTreeController.cs b/src/Umbraco.Web/Trees/ContentTreeController.cs index 0fa4718766..3da8540fe3 100644 --- a/src/Umbraco.Web/Trees/ContentTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentTreeController.cs @@ -9,11 +9,9 @@ using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; using Umbraco.Core.Services; using Umbraco.Web.Actions; -using Umbraco.Web.Composing; using Umbraco.Web.Models.Trees; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi.Filters; - using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Search; using Constants = Umbraco.Core.Constants; @@ -143,8 +141,6 @@ namespace Umbraco.Web.Trees } // add default actions for *all* users - // fixme - temp disable RePublish as the page itself (republish.aspx) has been temp disabled - //menu.Items.Add(Services.TextService.Localize("actions", ActionRePublish.Instance.Alias)).ConvertLegacyMenuItem(null, "content", "content"); menu.Items.Add(new RefreshNode(Services.TextService, true)); return menu; diff --git a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs index e98f723501..ae62f800e9 100644 --- a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs @@ -14,7 +14,6 @@ using Umbraco.Web.Models.Trees; using Umbraco.Web.WebApi.Filters; using System.Globalization; using Umbraco.Core.Models.Entities; -using Umbraco.Core.Services; using Umbraco.Web.Actions; using Umbraco.Web.Composing; diff --git a/src/Umbraco.Web/Trees/ContentTypeTreeController.cs b/src/Umbraco.Web/Trees/ContentTypeTreeController.cs index a3d65aea5f..a4c820ae25 100644 --- a/src/Umbraco.Web/Trees/ContentTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentTypeTreeController.cs @@ -4,12 +4,10 @@ using System.Collections.Generic; using System.Linq; using System.Net.Http.Formatting; using Umbraco.Core; -using Umbraco.Core.Configuration; +using Umbraco.Core.Composing; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; -using Umbraco.Core.Services; using Umbraco.Web.Actions; - using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Models.Trees; using Umbraco.Web.WebApi.Filters; @@ -74,7 +72,7 @@ namespace Umbraco.Web.Trees { var menu = new MenuItemCollection(); - var enableInheritedDocumentTypes = UmbracoConfig.For.UmbracoSettings().Content.EnableInheritedDocumentTypes; + var enableInheritedDocumentTypes = Current.Configs.Settings().Content.EnableInheritedDocumentTypes; if (id == Constants.System.Root.ToInvariantString()) { diff --git a/src/Umbraco.Web/Trees/DataTypeTreeController.cs b/src/Umbraco.Web/Trees/DataTypeTreeController.cs index b8e77f981d..8b38bee865 100644 --- a/src/Umbraco.Web/Trees/DataTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/DataTypeTreeController.cs @@ -12,8 +12,6 @@ using Umbraco.Web.WebApi.Filters; using Umbraco.Core.Services; using Umbraco.Web.Actions; using Umbraco.Web.Models.ContentEditing; -using Umbraco.Web.Search; - using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Trees @@ -91,7 +89,6 @@ namespace Umbraco.Web.Trees Constants.DataTypes.DefaultContentListView, Constants.DataTypes.DefaultMediaListView, Constants.DataTypes.DefaultMembersListView - }; } diff --git a/src/Umbraco.Web/Trees/DictionaryTreeController.cs b/src/Umbraco.Web/Trees/DictionaryTreeController.cs index cac2e7f435..49bed23ed9 100644 --- a/src/Umbraco.Web/Trees/DictionaryTreeController.cs +++ b/src/Umbraco.Web/Trees/DictionaryTreeController.cs @@ -3,9 +3,7 @@ using System.Linq; using System.Net.Http.Formatting; using Umbraco.Core; using Umbraco.Core.Models; -using Umbraco.Core.Services; using Umbraco.Web.Actions; - using Umbraco.Web.Models.Trees; using Umbraco.Web.WebApi.Filters; diff --git a/src/Umbraco.Web/Trees/FileSystemTreeController.cs b/src/Umbraco.Web/Trees/FileSystemTreeController.cs index 9babb656fe..9b7d078e13 100644 --- a/src/Umbraco.Web/Trees/FileSystemTreeController.cs +++ b/src/Umbraco.Web/Trees/FileSystemTreeController.cs @@ -1,12 +1,10 @@ using System; -using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http.Formatting; using System.Web; using Umbraco.Core; using Umbraco.Core.IO; -using Umbraco.Core.Services; using Umbraco.Web.Actions; using Umbraco.Web.Models.Trees; diff --git a/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs b/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs index 1df9127b8e..f271d276c6 100644 --- a/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs +++ b/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs @@ -1,15 +1,9 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http.Formatting; -using System.Web.Http.Routing; using Umbraco.Core; -using Umbraco.Core.Configuration; using Umbraco.Core.Services; using Umbraco.Web.Actions; using Umbraco.Web.Composing; -using Umbraco.Web.Models.Trees; namespace Umbraco.Web.Trees { @@ -31,7 +25,7 @@ namespace Umbraco.Web.Trees { case ActionDelete actionDelete: return Attempt.Succeed( - UmbracoConfig.For.GlobalSettings().Path.EnsureEndsWith('/') + "views/common/dialogs/legacydelete.html"); + Current.Configs.Global().Path.EnsureEndsWith('/') + "views/common/dialogs/legacydelete.html"); } return Attempt.Fail(); @@ -78,6 +72,5 @@ namespace Umbraco.Web.Trees public ActionUrlMethod ActionMethod { get; private set; } public string DialogTitle { get; private set; } } - } } diff --git a/src/Umbraco.Web/Trees/MacrosTreeController.cs b/src/Umbraco.Web/Trees/MacrosTreeController.cs index 3f925eef8d..5576c09704 100644 --- a/src/Umbraco.Web/Trees/MacrosTreeController.cs +++ b/src/Umbraco.Web/Trees/MacrosTreeController.cs @@ -2,14 +2,11 @@ using System.Linq; using System.Net.Http.Formatting; using Umbraco.Core; -using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; using Umbraco.Web.Models.Trees; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi.Filters; -using Umbraco.Core.Services; using Umbraco.Web.Actions; - using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Trees @@ -70,7 +67,6 @@ namespace Umbraco.Web.Trees return menu; } - var macro = Services.MacroService.GetById(int.Parse(id)); if (macro == null) return new MenuItemCollection(); diff --git a/src/Umbraco.Web/Trees/MediaTreeController.cs b/src/Umbraco.Web/Trees/MediaTreeController.cs index d88b2b9402..5797dd930d 100644 --- a/src/Umbraco.Web/Trees/MediaTreeController.cs +++ b/src/Umbraco.Web/Trees/MediaTreeController.cs @@ -9,11 +9,9 @@ using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; using Umbraco.Core.Services; using Umbraco.Web.Actions; -using Umbraco.Web.Composing; using Umbraco.Web.Models.Trees; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi.Filters; - using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Search; using Constants = Umbraco.Core.Constants; diff --git a/src/Umbraco.Web/Trees/MediaTypeTreeController.cs b/src/Umbraco.Web/Trees/MediaTypeTreeController.cs index bdcc2dc029..b93c1ac9e3 100644 --- a/src/Umbraco.Web/Trees/MediaTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/MediaTypeTreeController.cs @@ -4,18 +4,15 @@ using System.Linq; using System.Net.Http.Formatting; using AutoMapper; using Umbraco.Core; -using Umbraco.Core.Configuration; +using Umbraco.Core.Composing; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; -using Umbraco.Core.Persistence.Querying; using Umbraco.Web.Models.Trees; using Umbraco.Web.WebApi.Filters; using Umbraco.Core.Services; using Umbraco.Web.Actions; using Umbraco.Web.Models.ContentEditing; -using Umbraco.Web.Search; - namespace Umbraco.Web.Trees { [UmbracoTreeAuthorize(Constants.Trees.MediaTypes)] @@ -68,7 +65,7 @@ namespace Umbraco.Web.Trees { var menu = new MenuItemCollection(); - var enableInheritedMediaTypes = UmbracoConfig.For.UmbracoSettings().Content.EnableInheritedMediaTypes; + var enableInheritedMediaTypes = Current.Configs.Settings().Content.EnableInheritedMediaTypes; if (id == Constants.System.Root.ToInvariantString()) { diff --git a/src/Umbraco.Web/Trees/MemberTreeController.cs b/src/Umbraco.Web/Trees/MemberTreeController.cs index 39568e2efa..d1219da466 100644 --- a/src/Umbraco.Web/Trees/MemberTreeController.cs +++ b/src/Umbraco.Web/Trees/MemberTreeController.cs @@ -14,7 +14,6 @@ using Umbraco.Web.Actions; using Umbraco.Web.Models.Trees; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi.Filters; - using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Search; using Constants = Umbraco.Core.Constants; @@ -116,8 +115,6 @@ namespace Umbraco.Web.Trees return node; } - - } protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings) @@ -188,7 +185,6 @@ namespace Umbraco.Web.Trees menu.Items.Add(new ExportMember(Services.TextService)); } - return menu; } diff --git a/src/Umbraco.Web/Trees/MemberTypeAndGroupTreeControllerBase.cs b/src/Umbraco.Web/Trees/MemberTypeAndGroupTreeControllerBase.cs index 9ea5908891..d3f9ee77c9 100644 --- a/src/Umbraco.Web/Trees/MemberTypeAndGroupTreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/MemberTypeAndGroupTreeControllerBase.cs @@ -1,9 +1,7 @@ using System.Collections.Generic; using System.Net.Http.Formatting; using Umbraco.Core; -using Umbraco.Core.Services; using Umbraco.Web.Actions; - using Umbraco.Web.Models.Trees; namespace Umbraco.Web.Trees diff --git a/src/Umbraco.Web/Trees/PackagesTreeController.cs b/src/Umbraco.Web/Trees/PackagesTreeController.cs index 8158b47985..c56ed716a1 100644 --- a/src/Umbraco.Web/Trees/PackagesTreeController.cs +++ b/src/Umbraco.Web/Trees/PackagesTreeController.cs @@ -1,15 +1,12 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Net.Http.Formatting; using Umbraco.Web.Models.Trees; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi.Filters; -using umbraco; -using umbraco.cms.businesslogic.packager; using Umbraco.Core.Services; using Umbraco.Web.Actions; - +using Umbraco.Web._Legacy.Packager.PackageInstance; using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Trees @@ -27,13 +24,11 @@ namespace Umbraco.Web.Trees protected override TreeNode CreateRootNode(FormDataCollection queryStrings) { var root = base.CreateRootNode(queryStrings); - root.RoutePath = $"{Constants.Applications.Packages}/{Constants.Trees.Packages}/overview"; - root.Icon = "icon-box"; - return root; } + protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings) { var nodes = new TreeNodeCollection(); @@ -47,7 +42,8 @@ namespace Umbraco.Web.Trees .OrderBy(entity => entity.Data.Name) .Select(dt => { - var node = CreateTreeNode(dt.Data.Id.ToString(), id, queryStrings, dt.Data.Name, "icon-inbox", false, + var node = CreateTreeNode(dt.Data.Id.ToString(), id, queryStrings, dt.Data.Name, + "icon-inbox", false, $"/{queryStrings.GetValue("application")}/framed/{Uri.EscapeDataString("developer/Packages/EditPackage.aspx?id=" + dt.Data.Id)}"); return node; })); @@ -64,16 +60,12 @@ namespace Umbraco.Web.Trees createdPackages.Count > 0, string.Empty); - - //TODO: This isn't the best way to ensure a noop process for clicking a node but it works for now. node.AdditionalData["jsClickCallback"] = "javascript:void(0);"; nodes.Add(node); } - - return nodes; } @@ -85,12 +77,14 @@ namespace Umbraco.Web.Trees if (id == "-1") { menu.Items.Add(Services.TextService, opensDialog: true) - .ConvertLegacyMenuItem(null, Constants.Trees.Packages, queryStrings.GetValue("application")); + .ConvertLegacyMenuItem(null, Constants.Trees.Packages, + queryStrings.GetValue("application")); } else if (id == "created") { menu.Items.Add(Services.TextService, opensDialog: true) - .ConvertLegacyMenuItem(null, Constants.Trees.Packages, queryStrings.GetValue("application")); + .ConvertLegacyMenuItem(null, Constants.Trees.Packages, + queryStrings.GetValue("application")); menu.Items.Add(new RefreshNode(Services.TextService, true)); } diff --git a/src/Umbraco.Web/Trees/PartialViewsTreeController.cs b/src/Umbraco.Web/Trees/PartialViewsTreeController.cs index 6a65f5dd3e..a7aa8f134e 100644 --- a/src/Umbraco.Web/Trees/PartialViewsTreeController.cs +++ b/src/Umbraco.Web/Trees/PartialViewsTreeController.cs @@ -1,5 +1,4 @@ -using umbraco; -using Umbraco.Core.IO; +using Umbraco.Core.IO; using Umbraco.Web.Composing; using Umbraco.Web.Models.Trees; using Umbraco.Web.Mvc; diff --git a/src/Umbraco.Web/Trees/RelationTypeTreeController.cs b/src/Umbraco.Web/Trees/RelationTypeTreeController.cs index 1ce319b6ac..1888044d8d 100644 --- a/src/Umbraco.Web/Trees/RelationTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/RelationTypeTreeController.cs @@ -44,9 +44,10 @@ namespace Umbraco.Web.Trees if (id == Constants.System.Root.ToInvariantString()) { nodes.AddRange(Services.RelationService.GetAllRelationTypes() - .Select(rt => CreateTreeNode(rt.Id.ToString(), id, queryStrings, rt.Name, - "icon-trafic", false))); + .Select(rt => CreateTreeNode(rt.Id.ToString(), id, queryStrings, rt.Name, + "icon-trafic", false))); } + return nodes; } } diff --git a/src/Umbraco.Web/Trees/ScriptsTreeController.cs b/src/Umbraco.Web/Trees/ScriptsTreeController.cs index 08a03ac912..cd56cc4790 100644 --- a/src/Umbraco.Web/Trees/ScriptsTreeController.cs +++ b/src/Umbraco.Web/Trees/ScriptsTreeController.cs @@ -1,5 +1,4 @@ -using System; -using Umbraco.Core; +using Umbraco.Core; using Umbraco.Core.IO; using Umbraco.Web.Composing; using Umbraco.Web.Models.Trees; diff --git a/src/Umbraco.Web/Trees/TemplatesTreeController.cs b/src/Umbraco.Web/Trees/TemplatesTreeController.cs index fce51e2435..d9493063e2 100644 --- a/src/Umbraco.Web/Trees/TemplatesTreeController.cs +++ b/src/Umbraco.Web/Trees/TemplatesTreeController.cs @@ -5,16 +5,13 @@ using System.Linq; using System.Net.Http.Formatting; using AutoMapper; using Umbraco.Core; -using Umbraco.Core.Services; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; using Umbraco.Web.Actions; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Models.Trees; using Umbraco.Web.Mvc; -using Umbraco.Web.Search; using Umbraco.Web.WebApi.Filters; - using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Trees diff --git a/src/Umbraco.Web/Trees/TreeAttribute.cs b/src/Umbraco.Web/Trees/TreeAttribute.cs index b214698721..1cf23c549f 100644 --- a/src/Umbraco.Web/Trees/TreeAttribute.cs +++ b/src/Umbraco.Web/Trees/TreeAttribute.cs @@ -48,8 +48,6 @@ namespace Umbraco.Web.Trees IsSingleNodeTree = isSingleNodeTree; } - - public string ApplicationAlias { get; private set; } public string Alias { get; private set; } public string Title { get; private set; } diff --git a/src/Umbraco.Web/Trees/TreeController.cs b/src/Umbraco.Web/Trees/TreeController.cs index b5708ff57d..1c37307db5 100644 --- a/src/Umbraco.Web/Trees/TreeController.cs +++ b/src/Umbraco.Web/Trees/TreeController.cs @@ -1,8 +1,4 @@ -using System; -using System.Linq; -using Umbraco.Core.Services; - -namespace Umbraco.Web.Trees +namespace Umbraco.Web.Trees { /// /// The base controller for all tree requests @@ -22,8 +18,8 @@ namespace Umbraco.Web.Trees /// public override string RootNodeDisplayName => _rootNodeDisplayName - ?? (_rootNodeDisplayName = Services.ApplicationTreeService.GetByAlias(_attribute.Alias) - ?.GetRootNodeDisplayName(Services.TextService)); + ?? (_rootNodeDisplayName = Services.ApplicationTreeService.GetByAlias(_attribute.Alias) + ?.GetRootNodeDisplayName(Services.TextService)); /// /// Gets the current tree alias from the attribute assigned to it. diff --git a/src/Umbraco.Web/Trees/TreeControllerBase.cs b/src/Umbraco.Web/Trees/TreeControllerBase.cs index ebf2f74e07..ffdcac4479 100644 --- a/src/Umbraco.Web/Trees/TreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/TreeControllerBase.cs @@ -10,7 +10,6 @@ using Umbraco.Core.Models.Entities; using Umbraco.Web.Models.Trees; using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; -using Umbraco.Web.Search; namespace Umbraco.Web.Trees { diff --git a/src/Umbraco.Web/Trees/UserPermissionsTreeController.cs b/src/Umbraco.Web/Trees/UserPermissionsTreeController.cs index 479cc20083..5473fee7bb 100644 --- a/src/Umbraco.Web/Trees/UserPermissionsTreeController.cs +++ b/src/Umbraco.Web/Trees/UserPermissionsTreeController.cs @@ -2,8 +2,6 @@ using System.Linq; using System.Net.Http.Formatting; using Umbraco.Core; -using Umbraco.Core.Services; -using Umbraco.Web.Actions; using Umbraco.Web.Models.Trees; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi.Filters; diff --git a/src/Umbraco.Web/Trees/UserTreeController.cs b/src/Umbraco.Web/Trees/UserTreeController.cs index f029a929de..95f041cac5 100644 --- a/src/Umbraco.Web/Trees/UserTreeController.cs +++ b/src/Umbraco.Web/Trees/UserTreeController.cs @@ -1,10 +1,7 @@ using System.Net.Http.Formatting; -using Umbraco.Core; -using Umbraco.Core.Services; using Umbraco.Web.Models.Trees; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi.Filters; - using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Trees diff --git a/src/Umbraco.Web/UI/Controls/UmbracoControl.cs b/src/Umbraco.Web/UI/Controls/UmbracoControl.cs index 923ab00c6b..596e834ff9 100644 --- a/src/Umbraco.Web/UI/Controls/UmbracoControl.cs +++ b/src/Umbraco.Web/UI/Controls/UmbracoControl.cs @@ -3,7 +3,6 @@ using System.Web.Mvc; using System.Web.UI; using Umbraco.Core.Cache; using Umbraco.Core.Logging; -using Umbraco.Core.Persistence; using Umbraco.Core.Services; using Umbraco.Web.Composing; @@ -47,7 +46,7 @@ namespace Umbraco.Web.UI.Controls /// /// Gets the profiling logger. /// - public ProfilingLogger ProfilingLogger { get; } + public IProfilingLogger ProfilingLogger { get; } /// /// Gets the Umbraco context. diff --git a/src/Umbraco.Web/UI/Controls/UmbracoUserControl.cs b/src/Umbraco.Web/UI/Controls/UmbracoUserControl.cs index 2a2aa43bb9..9c68540c58 100644 --- a/src/Umbraco.Web/UI/Controls/UmbracoUserControl.cs +++ b/src/Umbraco.Web/UI/Controls/UmbracoUserControl.cs @@ -1,16 +1,13 @@ using System; -using System.Web; using System.Web.Mvc; -using System.Web.Routing; using System.Web.UI; -using Umbraco.Core; using Umbraco.Core.Cache; +using Umbraco.Core.Composing; using Umbraco.Core.Logging; -using Umbraco.Core.Persistence; using Umbraco.Core.Services; -using Umbraco.Web.Composing; using Umbraco.Web.Security; using Umbraco.Web.UI.Pages; +using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Web.UI.Controls { @@ -32,7 +29,7 @@ namespace Umbraco.Web.UI.Controls if (umbracoContext == null) throw new ArgumentNullException(nameof(umbracoContext)); UmbracoContext = umbracoContext; Umbraco = new UmbracoHelper(umbracoContext, services); - Members = new MembershipHelper(umbracoContext); + Members = Current.Factory.GetInstance(); // fixme inject somehow Logger = Current.Logger; @@ -73,7 +70,7 @@ namespace Umbraco.Web.UI.Controls /// /// Gets the ProfilingLogger. /// - public ProfilingLogger ProfilingLogger { get; } + public IProfilingLogger ProfilingLogger { get; } /// /// Gets the Umbraco context. diff --git a/src/Umbraco.Web/UI/Pages/BasePage.cs b/src/Umbraco.Web/UI/Pages/BasePage.cs index def4288c6d..a8fbc92722 100644 --- a/src/Umbraco.Web/UI/Pages/BasePage.cs +++ b/src/Umbraco.Web/UI/Pages/BasePage.cs @@ -1,14 +1,12 @@ using System; using System.Web; using System.Web.Mvc; -using System.Web.Routing; -using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Services; using Umbraco.Web.Security; using System.Web.UI; -using Umbraco.Core.Persistence; +using Umbraco.Core; using Umbraco.Web.Composing; namespace Umbraco.Web.UI.Pages @@ -33,7 +31,7 @@ namespace Umbraco.Web.UI.Pages /// /// Gets the profiling helper. /// - public ProfilingLogger ProfilingLogger => Current.ProfilingLogger; + public IProfilingLogger ProfilingLogger => Current.ProfilingLogger; /// /// Gets the Url helper. @@ -75,7 +73,7 @@ namespace Umbraco.Web.UI.Pages { base.OnLoad(e); - if (Request.IsSecureConnection || UmbracoConfig.For.GlobalSettings().UseHttps == false) return; + if (Request.IsSecureConnection || Current.Configs.Global().UseHttps == false) return; var serverName = HttpUtility.UrlEncode(Request.ServerVariables["SERVER_NAME"]); Response.Redirect($"https://{serverName}{Request.FilePath}"); diff --git a/src/Umbraco.Web/UI/Pages/UmbracoEnsuredPage.cs b/src/Umbraco.Web/UI/Pages/UmbracoEnsuredPage.cs index f2edcaea35..bb9ce3c421 100644 --- a/src/Umbraco.Web/UI/Pages/UmbracoEnsuredPage.cs +++ b/src/Umbraco.Web/UI/Pages/UmbracoEnsuredPage.cs @@ -90,7 +90,7 @@ namespace Umbraco.Web.UI.Pages //If this is not a back office request, then the module won't have authenticated it, in this case we // need to do the auth manually and since this is an UmbracoEnsuredPage, this is the anticipated behavior // TODO: When we implement Identity, this process might not work anymore, will be an interesting challenge - if (Context.Request.Url.IsBackOfficeRequest(HttpRuntime.AppDomainAppVirtualPath, UmbracoConfig.For.GlobalSettings()) == false) + if (Context.Request.Url.IsBackOfficeRequest(HttpRuntime.AppDomainAppVirtualPath, Current.Configs.Global()) == false) { var http = new HttpContextWrapper(Context); var ticket = http.GetUmbracoAuthTicket(); diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 17c2a54d81..fa1b620152 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -63,7 +63,7 @@ - + 2.6.2.25 @@ -109,10 +109,17 @@ + + + + + + + @@ -120,6 +127,7 @@ + @@ -173,6 +181,12 @@ + + + + + + @@ -183,7 +197,6 @@ - @@ -194,11 +207,13 @@ + + @@ -250,13 +265,13 @@ - + - + @@ -367,6 +382,7 @@ + @@ -531,6 +547,7 @@ + @@ -1107,27 +1124,16 @@ - - - - - - - - - - - @@ -1166,9 +1172,6 @@ ASPXCodeBehind - - ASPXCodeBehind - ASPXCodeBehind @@ -1225,7 +1228,6 @@ Code - @@ -1235,26 +1237,6 @@ FeedProxy.aspx - - insertMasterpageContent.aspx - ASPXCodeBehind - - - insertMasterpageContent.aspx - - - insertMasterpagePlaceholder.aspx - ASPXCodeBehind - - - insertMasterpagePlaceholder.aspx - - - republish.aspx - - - republish.aspx - editPackage.aspx ASPXCodeBehind @@ -1269,7 +1251,7 @@ - + True @@ -1307,15 +1289,6 @@ - - ASPXCodeBehind - - - ASPXCodeBehind - - - ASPXCodeBehind - ASPXCodeBehind diff --git a/src/Umbraco.Web/UmbracoApplicationBase.cs b/src/Umbraco.Web/UmbracoApplicationBase.cs old mode 100755 new mode 100644 index 080002b18e..d8d0d65b55 --- a/src/Umbraco.Web/UmbracoApplicationBase.cs +++ b/src/Umbraco.Web/UmbracoApplicationBase.cs @@ -3,11 +3,9 @@ using System.Reflection; using System.Threading; using System.Web; using System.Web.Hosting; -using LightInject; using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Logging; -using Umbraco.Core.Logging.Serilog; namespace Umbraco.Web { @@ -23,6 +21,14 @@ namespace Umbraco.Web /// protected abstract IRuntime GetRuntime(); + /// + /// Gets the application register. + /// + protected virtual IRegister GetRegister() + { + return RegisterFactory.Create(); + } + // events - in the order they trigger // were part of the BootManager architecture, would trigger only for the initial @@ -51,13 +57,11 @@ namespace Umbraco.Web { // ******** THIS IS WHERE EVERYTHING BEGINS ******** - // create the container for the application, and configure. + // create the register for the application, and boot // the boot manager is responsible for registrations - var container = new ServiceContainer(); - - // get runtime & boot + var register = GetRegister(); _runtime = GetRuntime(); - _runtime.Boot(container); + _runtime.Boot(register); } // called by ASP.NET (auto event wireup) once per app domain diff --git a/src/Umbraco.Web/UmbracoComponentRenderer.cs b/src/Umbraco.Web/UmbracoComponentRenderer.cs index 76a2f17636..e2d4ecfc39 100644 --- a/src/Umbraco.Web/UmbracoComponentRenderer.cs +++ b/src/Umbraco.Web/UmbracoComponentRenderer.cs @@ -1,17 +1,12 @@ using System; using System.Collections; -using System.Globalization; using System.IO; using System.Web; using System.Web.UI; using Umbraco.Core; -using Umbraco.Core.Models; using Umbraco.Web.Templates; using umbraco; using System.Collections.Generic; -using umbraco.presentation.templateControls; -using Umbraco.Core.Exceptions; -using Umbraco.Core.Models.PublishedContent; using Umbraco.Web.Composing; using Umbraco.Web.Macros; diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs index 09c9a2a8eb..a4de900e7c 100644 --- a/src/Umbraco.Web/UmbracoContext.cs +++ b/src/Umbraco.Web/UmbracoContext.cs @@ -5,14 +5,13 @@ using System.Web; using System.Web.Hosting; using Umbraco.Core; using Umbraco.Core.Configuration; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.Services; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; using Umbraco.Web.Runtime; using Umbraco.Web.Security; -using LightInject; namespace Umbraco.Web { @@ -102,21 +101,21 @@ namespace Umbraco.Web /// If an actual current UmbracoContext is already present, the disposable object is null and this method does nothing. /// Otherwise, a temporary, dummy UmbracoContext is created and registered in the accessor. And disposed and removed from the accessor. /// - internal static IDisposable EnsureContext() // keep this internal for now! + internal static IDisposable EnsureContext(HttpContextBase httpContext = null) // keep this internal for now! { if (Composing.Current.UmbracoContext != null) return null; - var httpContext = new HttpContextWrapper(System.Web.HttpContext.Current ?? new HttpContext(new SimpleWorkerRequest("temp.aspx", "", new StringWriter()))); + httpContext = httpContext ?? new HttpContextWrapper(System.Web.HttpContext.Current ?? new HttpContext(new SimpleWorkerRequest("temp.aspx", "", new StringWriter()))); return EnsureContext( Composing.Current.UmbracoContextAccessor, httpContext, Composing.Current.PublishedSnapshotService, - new WebSecurity(httpContext, Composing.Current.Services.UserService, UmbracoConfig.For.GlobalSettings()), - UmbracoConfig.For.UmbracoSettings(), + new WebSecurity(httpContext, Composing.Current.Services.UserService, Composing.Current.Configs.Global()), + Composing.Current.Configs.Settings(), Composing.Current.UrlProviders, - UmbracoConfig.For.GlobalSettings(), - Composing.Current.Container.GetInstance(), + Composing.Current.Configs.Global(), + Composing.Current.Factory.GetInstance(), true); // when the context will be disposed, it will be removed from the accessor diff --git a/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs b/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs index 6d8df48adc..2651167a6b 100644 --- a/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs +++ b/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs @@ -24,8 +24,8 @@ namespace Umbraco.Web public class UmbracoDefaultOwinStartup { protected IUmbracoContextAccessor UmbracoContextAccessor => Current.UmbracoContextAccessor; - protected IGlobalSettings GlobalSettings => UmbracoConfig.For.GlobalSettings(); - protected IUmbracoSettingsSection UmbracoSettings => UmbracoConfig.For.UmbracoSettings(); + protected IGlobalSettings GlobalSettings => Current.Configs.Global(); + protected IUmbracoSettingsSection UmbracoSettings => Current.Configs.Settings(); protected IRuntimeState RuntimeState => Core.Composing.Current.RuntimeState; protected ServiceContext Services => Current.Services; @@ -63,7 +63,7 @@ namespace Umbraco.Web protected virtual void ConfigureMiddleware(IAppBuilder app) { - // Configure OWIN for authentication. + // Configure OWIN for authentication. ConfigureUmbracoAuthentication(app); app diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index b98db65b27..6914efb3e2 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -10,9 +10,10 @@ using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Services; using Umbraco.Core.Xml; -using Umbraco.Web.Composing; +using Umbraco.Core.Composing; using Umbraco.Web.Routing; using Umbraco.Web.Security; +using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Web { @@ -124,7 +125,7 @@ namespace Umbraco.Web /// Gets the membership helper. /// public MembershipHelper MembershipHelper => _membershipHelper - ?? (_membershipHelper = new MembershipHelper(UmbracoContext)); + ?? (_membershipHelper = Current.Factory.GetInstance()); /// /// Gets the url provider. diff --git a/src/Umbraco.Web/UmbracoHttpHandler.cs b/src/Umbraco.Web/UmbracoHttpHandler.cs index e3a15faf24..ed56ea6053 100644 --- a/src/Umbraco.Web/UmbracoHttpHandler.cs +++ b/src/Umbraco.Web/UmbracoHttpHandler.cs @@ -1,11 +1,8 @@ using System; using System.Web; using System.Web.Mvc; -using System.Web.Routing; -using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Logging; -using Umbraco.Core.Persistence; using Umbraco.Core.Services; using Umbraco.Web.Composing; using Umbraco.Web.Security; @@ -44,7 +41,7 @@ namespace Umbraco.Web /// /// Gets the ProfilingLogger. /// - public ProfilingLogger ProfilingLogger { get; } + public IProfilingLogger ProfilingLogger { get; } /// /// Gets the Umbraco context. diff --git a/src/Umbraco.Web/UmbracoInjectedModule.cs b/src/Umbraco.Web/UmbracoInjectedModule.cs new file mode 100644 index 0000000000..eb6fc54fbb --- /dev/null +++ b/src/Umbraco.Web/UmbracoInjectedModule.cs @@ -0,0 +1,564 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Web; +using System.Web.Routing; +using Umbraco.Core; +using Umbraco.Core.Configuration; +using Umbraco.Core.IO; +using Umbraco.Core.Logging; +using Umbraco.Web.Routing; +using Umbraco.Web.Security; +using Umbraco.Core.Exceptions; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Persistence.FaultHandling; +using Umbraco.Core.Security; +using Umbraco.Core.Services; +using Umbraco.Web.Composing; +using Umbraco.Web.PublishedCache; + +namespace Umbraco.Web +{ + // notes + // + // also look at IOHelper.ResolveUrlsFromTextString - nightmarish?! + // + // context.RewritePath supports ~/ or else must begin with /vdir + // Request.RawUrl is still there + // response.Redirect does?! always remap to /vdir?! + + /// + /// Represents the main Umbraco module. + /// + /// + /// Is registered by the . + /// Do *not* try to use that one as a module in web.config. + /// + public class UmbracoInjectedModule : IHttpModule + { + private readonly IGlobalSettings _globalSettings; + private readonly IUmbracoContextAccessor _umbracoContextAccessor; + private readonly IPublishedSnapshotService _publishedSnapshotService; + private readonly IUserService _userService; + private readonly UrlProviderCollection _urlProviders; + private readonly IRuntimeState _runtime; + private readonly ILogger _logger; + private readonly PublishedRouter _publishedRouter; + private readonly IVariationContextAccessor _variationContextAccessor; + + public UmbracoInjectedModule( + IGlobalSettings globalSettings, + IUmbracoContextAccessor umbracoContextAccessor, + IPublishedSnapshotService publishedSnapshotService, + IUserService userService, + UrlProviderCollection urlProviders, + IRuntimeState runtime, + ILogger logger, + PublishedRouter publishedRouter, + IVariationContextAccessor variationContextAccessor) + { + _combinedRouteCollection = new Lazy(CreateRouteCollection); + + _globalSettings = globalSettings; + _umbracoContextAccessor = umbracoContextAccessor; + _publishedSnapshotService = publishedSnapshotService; + _userService = userService; + _urlProviders = urlProviders; + _runtime = runtime; + _logger = logger; + _publishedRouter = publishedRouter; + _variationContextAccessor = variationContextAccessor; + } + + #region HttpModule event handlers + + /// + /// Begins to process a request. + /// + /// + private void BeginRequest(HttpContextBase httpContext) + { + // ensure application url is initialized + ((RuntimeState) Current.RuntimeState).EnsureApplicationUrl(httpContext.Request); + + // do not process if client-side request + if (httpContext.Request.Url.IsClientSideRequest()) + return; + + // write the trace output for diagnostics at the end of the request + httpContext.Trace.Write("UmbracoModule", "Umbraco request begins"); + + // ok, process + + // create the UmbracoContext singleton, one per request, and assign + // replace existing if any (eg during app startup, a temp one is created) + UmbracoContext.EnsureContext( + _umbracoContextAccessor, + httpContext, + _publishedSnapshotService, + new WebSecurity(httpContext, _userService, _globalSettings), + Current.Configs.Settings(), + _urlProviders, + _globalSettings, + _variationContextAccessor, + true); + } + + /// + /// Processses the Umbraco Request + /// + /// + /// + /// + /// This will check if we are trying to route to the default back office page (i.e. ~/Umbraco/ or ~/Umbraco or ~/Umbraco/Default ) + /// and ensure that the MVC handler executes for that. This is required because the route for /Umbraco will never execute because + /// files/folders exist there and we cannot set the RouteCollection.RouteExistingFiles = true since that will muck a lot of other things up. + /// So we handle it here and explicitly execute the MVC controller. + /// + /// + void ProcessRequest(HttpContextBase httpContext) + { + // do not process if client-side request + if (httpContext.Request.Url.IsClientSideRequest()) + return; + + if (UmbracoContext.Current == null) + throw new InvalidOperationException("The UmbracoContext.Current is null, ProcessRequest cannot proceed unless there is a current UmbracoContext"); + + var umbracoContext = UmbracoContext.Current; + + // re-write for the default back office path + if (httpContext.Request.Url.IsDefaultBackOfficeRequest(_globalSettings)) + { + if (EnsureRuntime(httpContext, umbracoContext.OriginalRequestUrl)) + RewriteToBackOfficeHandler(httpContext); + return; + } + + // do not process if this request is not a front-end routable page + var isRoutableAttempt = EnsureUmbracoRoutablePage(umbracoContext, httpContext); + + // raise event here + UmbracoModule.OnRouteAttempt(this, new RoutableAttemptEventArgs(isRoutableAttempt.Result, umbracoContext, httpContext)); + if (isRoutableAttempt.Success == false) return; + + httpContext.Trace.Write("UmbracoModule", "Umbraco request confirmed"); + + // ok, process + + // note: requestModule.UmbracoRewrite also did some stripping of &umbPage + // from the querystring... that was in v3.x to fix some issues with pre-forms + // auth. Paul Sterling confirmed in jan. 2013 that we can get rid of it. + + // instanciate, prepare and process the published content request + // important to use CleanedUmbracoUrl - lowercase path-only version of the current url + var request = _publishedRouter.CreateRequest(umbracoContext); + umbracoContext.PublishedRequest = request; + _publishedRouter.PrepareRequest(request); + + // HandleHttpResponseStatus returns a value indicating that the request should + // not be processed any further, eg because it has been redirect. then, exit. + if (UmbracoModule.HandleHttpResponseStatus(httpContext, request, _logger)) + return; + + if (request.HasPublishedContent == false) + httpContext.RemapHandler(new PublishedContentNotFoundHandler()); + else + RewriteToUmbracoHandler(httpContext, request); + } + + #endregion + + #region Methods + + /// + /// Checks the current request and ensures that it is routable based on the structure of the request and URI + /// + /// + /// + /// + internal Attempt EnsureUmbracoRoutablePage(UmbracoContext context, HttpContextBase httpContext) + { + var uri = context.OriginalRequestUrl; + + var reason = EnsureRoutableOutcome.IsRoutable; + + // ensure this is a document request + if (EnsureDocumentRequest(httpContext, uri) == false) + { + reason = EnsureRoutableOutcome.NotDocumentRequest; + } + // ensure the runtime is in the proper state + // and deal with needed redirects, etc + else if (EnsureRuntime(httpContext, uri) == false) + { + reason = EnsureRoutableOutcome.NotReady; + } + // ensure Umbraco has documents to serve + else if (EnsureHasContent(context, httpContext) == false) + { + reason = EnsureRoutableOutcome.NoContent; + } + + return Attempt.If(reason == EnsureRoutableOutcome.IsRoutable, reason); + } + + /// + /// Ensures that the request is a document request (i.e. one that the module should handle) + /// + /// + /// + /// + private bool EnsureDocumentRequest(HttpContextBase httpContext, Uri uri) + { + var maybeDoc = true; + var lpath = uri.AbsolutePath.ToLowerInvariant(); + + // handle directory-urls used for asmx + // legacy - what's the point really? + if (/*maybeDoc &&*/ _globalSettings.UseDirectoryUrls) + { + var asmxPos = lpath.IndexOf(".asmx/", StringComparison.OrdinalIgnoreCase); + if (asmxPos >= 0) + { + // use uri.AbsolutePath, not path, 'cos path has been lowercased + httpContext.RewritePath(uri.AbsolutePath.Substring(0, asmxPos + 5), // filePath + uri.AbsolutePath.Substring(asmxPos + 5), // pathInfo + uri.Query.TrimStart('?')); + maybeDoc = false; + } + } + + // a document request should be + // /foo/bar/nil + // /foo/bar/nil/ + // /foo/bar/nil.aspx + // where /foo is not a reserved path + + // if the path contains an extension that is not .aspx + // then it cannot be a document request + var extension = Path.GetExtension(lpath); + if (maybeDoc && extension.IsNullOrWhiteSpace() == false && extension != ".aspx") + maybeDoc = false; + + // at that point, either we have no extension, or it is .aspx + + // if the path is reserved then it cannot be a document request + if (maybeDoc && _globalSettings.IsReservedPathOrUrl(lpath, httpContext, _combinedRouteCollection.Value)) + maybeDoc = false; + + //NOTE: No need to warn, plus if we do we should log the document, as this message doesn't really tell us anything :) + //if (!maybeDoc) + //{ + // Logger.Warn("Not a document"); + //} + + return maybeDoc; + } + + private bool EnsureRuntime(HttpContextBase httpContext, Uri uri) + { + var debug = _runtime.Debug; + var level = _runtime.Level; + switch (level) + { + case RuntimeLevel.Unknown: + case RuntimeLevel.Boot: + // not ready yet, but wait + ReportRuntime(level, "Umbraco is booting."); + + // let requests pile up and wait for 10s then show the splash anyway + if (Current.Configs.Settings().Content.EnableSplashWhileLoading == false + && ((RuntimeState) _runtime).WaitForRunLevel(TimeSpan.FromSeconds(10))) return true; + + // redirect to booting page + httpContext.Response.StatusCode = 503; // temp not available + const string bootUrl = "~/config/splashes/booting.aspx"; + httpContext.Response.AddHeader("Retry-After", debug ? "1" : "30"); // seconds + httpContext.RewritePath(UriUtility.ToAbsolute(bootUrl) + "?url=" + HttpUtility.UrlEncode(uri.ToString())); + return false; // cannot serve content + + case RuntimeLevel.BootFailed: + // redirect to death page + ReportRuntime(level, "Umbraco has failed."); + + httpContext.Response.StatusCode = 503; // temp not available + const string deathUrl = "~/config/splashes/death.aspx"; + httpContext.Response.AddHeader("Retry-After", debug ? "1" : "300"); // seconds + httpContext.RewritePath(UriUtility.ToAbsolute(deathUrl) + "?url=" + HttpUtility.UrlEncode(uri.ToString())); + return false; // cannot serve content + + case RuntimeLevel.Run: + // ok + return true; + + case RuntimeLevel.Install: + case RuntimeLevel.Upgrade: + // redirect to install + ReportRuntime(level, "Umbraco must install or upgrade."); + var installPath = UriUtility.ToAbsolute(SystemDirectories.Install); + var installUrl = $"{installPath}/?redir=true&url={HttpUtility.UrlEncode(uri.ToString())}"; + httpContext.Response.Redirect(installUrl, true); + return false; // cannot serve content + + default: + throw new NotSupportedException($"Unexpected runtime level: {Current.RuntimeState.Level}."); + } + } + + private static bool _reported; + private static RuntimeLevel _reportedLevel; + + private void ReportRuntime(RuntimeLevel level, string message) + { + if (_reported && _reportedLevel == level) return; + _reported = true; + _reportedLevel = level; + _logger.Warn(message); + } + + // ensures Umbraco has at least one published node + // if not, rewrites to splash and return false + // if yes, return true + private bool EnsureHasContent(UmbracoContext context, HttpContextBase httpContext) + { + if (context.ContentCache.HasContent()) + return true; + + _logger.Warn("Umbraco has no content"); + + const string noContentUrl = "~/config/splashes/noNodes.aspx"; + httpContext.RewritePath(UriUtility.ToAbsolute(noContentUrl)); + + return false; + } + + /// + /// Rewrites to the default back office page. + /// + /// + private void RewriteToBackOfficeHandler(HttpContextBase context) + { + // GlobalSettings.Path has already been through IOHelper.ResolveUrl() so it begins with / and vdir (if any) + var rewritePath = _globalSettings.Path.TrimEnd('/') + "/Default"; + // rewrite the path to the path of the handler (i.e. /umbraco/RenderMvc) + context.RewritePath(rewritePath, "", "", false); + + //if it is MVC we need to do something special, we are not using TransferRequest as this will + //require us to rewrite the path with query strings and then reparse the query strings, this would + //also mean that we need to handle IIS 7 vs pre-IIS 7 differently. Instead we are just going to create + //an instance of the UrlRoutingModule and call it's PostResolveRequestCache method. This does: + // * Looks up the route based on the new rewritten URL + // * Creates the RequestContext with all route parameters and then executes the correct handler that matches the route + //we also cannot re-create this functionality because the setter for the HttpContext.Request.RequestContext is internal + //so really, this is pretty much the only way without using Server.TransferRequest and if we did that, we'd have to rethink + //a bunch of things! + var urlRouting = new UrlRoutingModule(); + urlRouting.PostResolveRequestCache(context); + } + + /// + /// Rewrites to the Umbraco handler - we always send the request via our MVC rendering engine, this will deal with + /// requests destined for webforms. + /// + /// + /// + private void RewriteToUmbracoHandler(HttpContextBase context, PublishedRequest pcr) + { + // NOTE: we do not want to use TransferRequest even though many docs say it is better with IIS7, turns out this is + // not what we need. The purpose of TransferRequest is to ensure that .net processes all of the rules for the newly + // rewritten url, but this is not what we want! + // read: http://forums.iis.net/t/1146511.aspx + + var query = pcr.Uri.Query.TrimStart('?'); + + // GlobalSettings.Path has already been through IOHelper.ResolveUrl() so it begins with / and vdir (if any) + var rewritePath = _globalSettings.Path.TrimEnd('/') + "/RenderMvc"; + // rewrite the path to the path of the handler (i.e. /umbraco/RenderMvc) + context.RewritePath(rewritePath, "", query, false); + + //if it is MVC we need to do something special, we are not using TransferRequest as this will + //require us to rewrite the path with query strings and then reparse the query strings, this would + //also mean that we need to handle IIS 7 vs pre-IIS 7 differently. Instead we are just going to create + //an instance of the UrlRoutingModule and call it's PostResolveRequestCache method. This does: + // * Looks up the route based on the new rewritten URL + // * Creates the RequestContext with all route parameters and then executes the correct handler that matches the route + //we also cannot re-create this functionality because the setter for the HttpContext.Request.RequestContext is internal + //so really, this is pretty much the only way without using Server.TransferRequest and if we did that, we'd have to rethink + //a bunch of things! + var urlRouting = new UrlRoutingModule(); + urlRouting.PostResolveRequestCache(context); + } + + + /// + /// Any object that is in the HttpContext.Items collection that is IDisposable will get disposed on the end of the request + /// + /// + private void DisposeHttpContextItems(HttpContext http) + { + // do not process if client-side request + if (http.Request.Url.IsClientSideRequest()) + return; + + //get a list of keys to dispose + var keys = new HashSet(); + foreach (DictionaryEntry i in http.Items) + { + if (i.Value is IDisposeOnRequestEnd || i.Key is IDisposeOnRequestEnd) + { + keys.Add(i.Key); + } + } + //dispose each item and key that was found as disposable. + foreach (var k in keys) + { + try + { + http.Items[k].DisposeIfDisposable(); + } + catch (Exception ex) + { + _logger.Error("Could not dispose item with key " + k, ex); + } + try + { + k.DisposeIfDisposable(); + } + catch (Exception ex) + { + _logger.Error("Could not dispose item key " + k, ex); + } + } + } + + #endregion + + #region IHttpModule + + /// + /// Initialize the module, this will trigger for each new application + /// and there may be more than 1 application per application domain + /// + /// + public void Init(HttpApplication app) + { + if (_runtime.Level == RuntimeLevel.BootFailed) + { + // there's nothing we can do really + app.BeginRequest += (sender, args) => + { + // would love to avoid throwing, and instead display a customized Umbraco 500 + // page - however if we don't throw here, something else might go wrong, and + // it's this later exception that would be reported. could not figure out how + // to prevent it, either with httpContext.Response.End() or .ApplicationInstance + // .CompleteRequest() + + // also, if something goes wrong with our DI setup, the logging subsystem may + // not even kick in, so here we try to give as much detail as possible + + BootFailedException.Rethrow(Core.Composing.Current.RuntimeState.BootFailedException); + }; + return; + } + + app.BeginRequest += (sender, e) => + { + var httpContext = ((HttpApplication) sender).Context; + + LogHttpRequest.TryGetCurrentHttpRequestId(out var httpRequestId); + + _logger.Verbose("Begin request [{HttpRequestId}]: {RequestUrl}", httpRequestId, httpContext.Request.Url); + BeginRequest(new HttpContextWrapper(httpContext)); + }; + + //disable asp.net headers (security) + // This is the correct place to modify headers according to MS: + // https://our.umbraco.com/forum/umbraco-7/using-umbraco-7/65241-Heap-error-from-header-manipulation?p=0#comment220889 + app.PostReleaseRequestState += (sender, args) => + { + var httpContext = ((HttpApplication) sender).Context; + try + { + httpContext.Response.Headers.Remove("Server"); + //this doesn't normally work since IIS sets it but we'll keep it here anyways. + httpContext.Response.Headers.Remove("X-Powered-By"); + httpContext.Response.Headers.Remove("X-AspNet-Version"); + httpContext.Response.Headers.Remove("X-AspNetMvc-Version"); + } + catch (PlatformNotSupportedException) + { + // can't remove headers this way on IIS6 or cassini. + } + }; + + app.PostAuthenticateRequest += (sender, e) => + { + var httpContext = ((HttpApplication) sender).Context; + //ensure the thread culture is set + httpContext.User?.Identity?.EnsureCulture(); + }; + + app.PostResolveRequestCache += (sender, e) => + { + var httpContext = ((HttpApplication) sender).Context; + ProcessRequest(new HttpContextWrapper(httpContext)); + }; + + app.EndRequest += (sender, args) => + { + var httpContext = ((HttpApplication) sender).Context; + + if (UmbracoContext.Current != null && UmbracoContext.Current.IsFrontEndUmbracoRequest) + { + LogHttpRequest.TryGetCurrentHttpRequestId(out var httpRequestId); + + _logger.Verbose("End Request [{HttpRequestId}]: {RequestUrl} ({RequestDuration}ms)", httpRequestId, httpContext.Request.Url, DateTime.Now.Subtract(UmbracoContext.Current.ObjectCreated).TotalMilliseconds); + } + + UmbracoModule.OnEndRequest(this, new UmbracoRequestEventArgs(UmbracoContext.Current, new HttpContextWrapper(httpContext))); + + DisposeHttpContextItems(httpContext); + }; + } + + public void Dispose() + { } + + #endregion + + /// + /// This is used to be passed into the GlobalSettings.IsReservedPathOrUrl and will include some 'fake' routes + /// used to determine if a path is reserved. + /// + /// + /// This is basically used to reserve paths dynamically + /// + private readonly Lazy _combinedRouteCollection; + + private RouteCollection CreateRouteCollection() + { + var routes = new RouteCollection(); + + foreach (var route in RouteTable.Routes) + routes.Add(route); + + foreach (var reservedPath in UmbracoModule.ReservedPaths) + { + try + { + routes.Add("_umbreserved_" + reservedPath.ReplaceNonAlphanumericChars(""), + new Route(reservedPath.TrimStart('/'), new StopRoutingHandler())); + } + catch (Exception ex) + { + _logger.Error("Could not add reserved path route", ex); + } + } + + return routes; + } + } +} diff --git a/src/Umbraco.Web/UmbracoModule.cs b/src/Umbraco.Web/UmbracoModule.cs index 1d2436cf66..332c955506 100644 --- a/src/Umbraco.Web/UmbracoModule.cs +++ b/src/Umbraco.Web/UmbracoModule.cs @@ -1,345 +1,48 @@ using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Text; using System.Web; -using System.Web.Routing; -using LightInject; using Umbraco.Core; -using Umbraco.Core.Configuration; -using Umbraco.Core.IO; -using Umbraco.Core.Logging; -using Umbraco.Web.Routing; -using Umbraco.Web.Security; using Umbraco.Core.Collections; -using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.Exceptions; -using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.Persistence; -using Umbraco.Core.Security; -using Umbraco.Core.Services; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; using Umbraco.Web.Composing; -using Umbraco.Web.PublishedCache; +using Umbraco.Web.Routing; using Umbraco.Core.Logging.Serilog.Enrichers; namespace Umbraco.Web { - // also look at IOHelper.ResolveUrlsFromTextString - nightmarish?! - - // context.RewritePath supports ~/ or else must begin with /vdir - // Request.RawUrl is still there - // response.Redirect does?! always remap to /vdir?! - - public class UmbracoModule : IHttpModule + /// + /// Represents the main Umbraco module. + /// + /// + /// Register that one in web.config. + /// It will inject which contains most of the actual code. + /// + public class UmbracoModule : ModuleInjector { - #region Dependencies - - // modules are *not* instanciated by the container so we have to - // get our dependencies injected manually, through properties, in - // Init(). works for dependencies that are singletons. - - [Inject] - public IUmbracoSettingsSection UmbracoSettings { get; set; } - - [Inject] - public IGlobalSettings GlobalSettings { get; set; } - - [Inject] - public IUmbracoContextAccessor UmbracoContextAccessor { get; set; } - - [Inject] - public IPublishedSnapshotService PublishedSnapshotService { get; set; } - - [Inject] - public IUserService UserService { get; set; } - - [Inject] - public UrlProviderCollection UrlProviders { get; set; } - - [Inject] - public IRuntimeState Runtime { get; set; } - - [Inject] - public ILogger Logger { get; set; } - - [Inject] - internal PublishedRouter PublishedRouter { get; set; } - - [Inject] - internal IUmbracoDatabaseFactory DatabaseFactory { get; set; } - - [Inject] - internal IVariationContextAccessor VariationContextAccessor { get; set; } - - #endregion - - public UmbracoModule() - { - _combinedRouteCollection = new Lazy(CreateRouteCollection); - } - - #region HttpModule event handlers + /// + /// Occurs when... + /// + internal static event EventHandler RouteAttempt; /// - /// Begins to process a request. + /// Occurs when... /// - /// - private void BeginRequest(HttpContextBase httpContext) + public static event EventHandler EndRequest; + + /// + /// Triggers the RouteAttempt event. + /// + internal static void OnRouteAttempt(object sender, RoutableAttemptEventArgs args) { - // ensure application url is initialized - ((RuntimeState) Current.RuntimeState).EnsureApplicationUrl(httpContext.Request); - - // do not process if client-side request - if (httpContext.Request.Url.IsClientSideRequest()) - return; - - // write the trace output for diagnostics at the end of the request - httpContext.Trace.Write("UmbracoModule", "Umbraco request begins"); - - // ok, process - - // create the UmbracoContext singleton, one per request, and assign - // replace existing if any (eg during app startup, a temp one is created) - UmbracoContext.EnsureContext( - UmbracoContextAccessor, - httpContext, - PublishedSnapshotService, - new WebSecurity(httpContext, UserService, GlobalSettings), - UmbracoConfig.For.UmbracoSettings(), - UrlProviders, - GlobalSettings, - VariationContextAccessor, - true); + RouteAttempt?.Invoke(sender, args); } /// - /// Processses the Umbraco Request + /// Triggers the EndRequest event. /// - /// - /// - /// - /// This will check if we are trying to route to the default back office page (i.e. ~/Umbraco/ or ~/Umbraco or ~/Umbraco/Default ) - /// and ensure that the MVC handler executes for that. This is required because the route for /Umbraco will never execute because - /// files/folders exist there and we cannot set the RouteCollection.RouteExistingFiles = true since that will muck a lot of other things up. - /// So we handle it here and explicitly execute the MVC controller. - /// - /// - void ProcessRequest(HttpContextBase httpContext) + internal static void OnEndRequest(object sender, UmbracoRequestEventArgs args) { - // do not process if client-side request - if (httpContext.Request.Url.IsClientSideRequest()) - return; - - if (UmbracoContext.Current == null) - throw new InvalidOperationException("The UmbracoContext.Current is null, ProcessRequest cannot proceed unless there is a current UmbracoContext"); - - var umbracoContext = UmbracoContext.Current; - - // re-write for the default back office path - if (httpContext.Request.Url.IsDefaultBackOfficeRequest(GlobalSettings)) - { - if (EnsureRuntime(httpContext, umbracoContext.OriginalRequestUrl)) - RewriteToBackOfficeHandler(httpContext); - return; - } - - // do not process if this request is not a front-end routable page - var isRoutableAttempt = EnsureUmbracoRoutablePage(umbracoContext, httpContext); - - // raise event here - OnRouteAttempt(new RoutableAttemptEventArgs(isRoutableAttempt.Result, umbracoContext, httpContext)); - if (isRoutableAttempt.Success == false) return; - - httpContext.Trace.Write("UmbracoModule", "Umbraco request confirmed"); - - // ok, process - - // note: requestModule.UmbracoRewrite also did some stripping of &umbPage - // from the querystring... that was in v3.x to fix some issues with pre-forms - // auth. Paul Sterling confirmed in jan. 2013 that we can get rid of it. - - // instanciate, prepare and process the published content request - // important to use CleanedUmbracoUrl - lowercase path-only version of the current url - var request = PublishedRouter.CreateRequest(umbracoContext); - umbracoContext.PublishedRequest = request; - PublishedRouter.PrepareRequest(request); - - // HandleHttpResponseStatus returns a value indicating that the request should - // not be processed any further, eg because it has been redirect. then, exit. - if (HandleHttpResponseStatus(httpContext, request, Logger)) - return; - - if (request.HasPublishedContent == false) - httpContext.RemapHandler(new PublishedContentNotFoundHandler()); - else - RewriteToUmbracoHandler(httpContext, request); - } - - #endregion - - #region Methods - - /// - /// Checks the current request and ensures that it is routable based on the structure of the request and URI - /// - /// - /// - /// - internal Attempt EnsureUmbracoRoutablePage(UmbracoContext context, HttpContextBase httpContext) - { - var uri = context.OriginalRequestUrl; - - var reason = EnsureRoutableOutcome.IsRoutable; - - // ensure this is a document request - if (EnsureDocumentRequest(httpContext, uri) == false) - { - reason = EnsureRoutableOutcome.NotDocumentRequest; - } - // ensure the runtime is in the proper state - // and deal with needed redirects, etc - else if (EnsureRuntime(httpContext, uri) == false) - { - reason = EnsureRoutableOutcome.NotReady; - } - // ensure Umbraco has documents to serve - else if (EnsureHasContent(context, httpContext) == false) - { - reason = EnsureRoutableOutcome.NoContent; - } - - return Attempt.If(reason == EnsureRoutableOutcome.IsRoutable, reason); - } - - /// - /// Ensures that the request is a document request (i.e. one that the module should handle) - /// - /// - /// - /// - private bool EnsureDocumentRequest(HttpContextBase httpContext, Uri uri) - { - var maybeDoc = true; - var lpath = uri.AbsolutePath.ToLowerInvariant(); - - // handle directory-urls used for asmx - // legacy - what's the point really? - if (/*maybeDoc &&*/ GlobalSettings.UseDirectoryUrls) - { - var asmxPos = lpath.IndexOf(".asmx/", StringComparison.OrdinalIgnoreCase); - if (asmxPos >= 0) - { - // use uri.AbsolutePath, not path, 'cos path has been lowercased - httpContext.RewritePath(uri.AbsolutePath.Substring(0, asmxPos + 5), // filePath - uri.AbsolutePath.Substring(asmxPos + 5), // pathInfo - uri.Query.TrimStart('?')); - maybeDoc = false; - } - } - - // a document request should be - // /foo/bar/nil - // /foo/bar/nil/ - // /foo/bar/nil.aspx - // where /foo is not a reserved path - - // if the path contains an extension that is not .aspx - // then it cannot be a document request - var extension = Path.GetExtension(lpath); - if (maybeDoc && extension.IsNullOrWhiteSpace() == false && extension != ".aspx") - maybeDoc = false; - - // at that point, either we have no extension, or it is .aspx - - // if the path is reserved then it cannot be a document request - if (maybeDoc && GlobalSettings.IsReservedPathOrUrl(lpath, httpContext, _combinedRouteCollection.Value)) - maybeDoc = false; - - //NOTE: No need to warn, plus if we do we should log the document, as this message doesn't really tell us anything :) - //if (!maybeDoc) - //{ - // Logger.Warn("Not a document"); - //} - - return maybeDoc; - } - - private bool EnsureRuntime(HttpContextBase httpContext, Uri uri) - { - var debug = Runtime.Debug; - var level = Runtime.Level; - switch (level) - { - case RuntimeLevel.Unknown: - case RuntimeLevel.Boot: - // not ready yet, but wait - ReportRuntime(level, "Umbraco is booting."); - - // let requests pile up and wait for 10s then show the splash anyway - if (UmbracoConfig.For.UmbracoSettings().Content.EnableSplashWhileLoading == false - && ((RuntimeState) Runtime).WaitForRunLevel(TimeSpan.FromSeconds(10))) return true; - - // redirect to booting page - httpContext.Response.StatusCode = 503; // temp not available - const string bootUrl = "~/config/splashes/booting.aspx"; - httpContext.Response.AddHeader("Retry-After", debug ? "1" : "30"); // seconds - httpContext.RewritePath(UriUtility.ToAbsolute(bootUrl) + "?url=" + HttpUtility.UrlEncode(uri.ToString())); - return false; // cannot serve content - - case RuntimeLevel.BootFailed: - // redirect to death page - ReportRuntime(level, "Umbraco has failed."); - - httpContext.Response.StatusCode = 503; // temp not available - const string deathUrl = "~/config/splashes/death.aspx"; - httpContext.Response.AddHeader("Retry-After", debug ? "1" : "300"); // seconds - httpContext.RewritePath(UriUtility.ToAbsolute(deathUrl) + "?url=" + HttpUtility.UrlEncode(uri.ToString())); - return false; // cannot serve content - - case RuntimeLevel.Run: - // ok - return true; - - case RuntimeLevel.Install: - case RuntimeLevel.Upgrade: - // redirect to install - ReportRuntime(level, "Umbraco must install or upgrade."); - var installPath = UriUtility.ToAbsolute(SystemDirectories.Install); - var installUrl = $"{installPath}/?redir=true&url={HttpUtility.UrlEncode(uri.ToString())}"; - httpContext.Response.Redirect(installUrl, true); - return false; // cannot serve content - - default: - throw new NotSupportedException($"Unexpected runtime level: {Current.RuntimeState.Level}."); - } - } - - private static bool _reported; - private static RuntimeLevel _reportedLevel; - - private void ReportRuntime(RuntimeLevel level, string message) - { - if (_reported && _reportedLevel == level) return; - _reported = true; - _reportedLevel = level; - Logger.Warn(message); - } - - // ensures Umbraco has at least one published node - // if not, rewrites to splash and return false - // if yes, return true - private bool EnsureHasContent(UmbracoContext context, HttpContextBase httpContext) - { - if (context.ContentCache.HasContent()) - return true; - - Logger.Warn("Umbraco has no content"); - - const string noContentUrl = "~/config/splashes/noNodes.aspx"; - httpContext.RewritePath(UriUtility.ToAbsolute(noContentUrl)); - - return false; + EndRequest?.Invoke(sender, args); } // returns a value indicating whether redirection took place and the request has @@ -375,7 +78,7 @@ namespace Umbraco.Web else if (pcr.Is404) { response.StatusCode = 404; - response.TrySkipIisCustomErrors = UmbracoConfig.For.UmbracoSettings().WebRouting.TrySkipIisCustomErrors; + response.TrySkipIisCustomErrors = Current.Configs.Settings().WebRouting.TrySkipIisCustomErrors; if (response.TrySkipIisCustomErrors == false) logger.Warn("Status code is 404 yet TrySkipIisCustomErrors is false - IIS will take over."); @@ -402,278 +105,6 @@ namespace Umbraco.Web return end; } - /// - /// Rewrites to the default back office page. - /// - /// - private void RewriteToBackOfficeHandler(HttpContextBase context) - { - // GlobalSettings.Path has already been through IOHelper.ResolveUrl() so it begins with / and vdir (if any) - var rewritePath = GlobalSettings.Path.TrimEnd('/') + "/Default"; - // rewrite the path to the path of the handler (i.e. /umbraco/RenderMvc) - context.RewritePath(rewritePath, "", "", false); - - //if it is MVC we need to do something special, we are not using TransferRequest as this will - //require us to rewrite the path with query strings and then reparse the query strings, this would - //also mean that we need to handle IIS 7 vs pre-IIS 7 differently. Instead we are just going to create - //an instance of the UrlRoutingModule and call it's PostResolveRequestCache method. This does: - // * Looks up the route based on the new rewritten URL - // * Creates the RequestContext with all route parameters and then executes the correct handler that matches the route - //we also cannot re-create this functionality because the setter for the HttpContext.Request.RequestContext is internal - //so really, this is pretty much the only way without using Server.TransferRequest and if we did that, we'd have to rethink - //a bunch of things! - var urlRouting = new UrlRoutingModule(); - urlRouting.PostResolveRequestCache(context); - } - - /// - /// Rewrites to the Umbraco handler - we always send the request via our MVC rendering engine, this will deal with - /// requests destined for webforms. - /// - /// - /// - private void RewriteToUmbracoHandler(HttpContextBase context, PublishedRequest pcr) - { - // NOTE: we do not want to use TransferRequest even though many docs say it is better with IIS7, turns out this is - // not what we need. The purpose of TransferRequest is to ensure that .net processes all of the rules for the newly - // rewritten url, but this is not what we want! - // read: http://forums.iis.net/t/1146511.aspx - - var query = pcr.Uri.Query.TrimStart('?'); - - // GlobalSettings.Path has already been through IOHelper.ResolveUrl() so it begins with / and vdir (if any) - var rewritePath = GlobalSettings.Path.TrimEnd('/') + "/RenderMvc"; - // rewrite the path to the path of the handler (i.e. /umbraco/RenderMvc) - context.RewritePath(rewritePath, "", query, false); - - //if it is MVC we need to do something special, we are not using TransferRequest as this will - //require us to rewrite the path with query strings and then reparse the query strings, this would - //also mean that we need to handle IIS 7 vs pre-IIS 7 differently. Instead we are just going to create - //an instance of the UrlRoutingModule and call it's PostResolveRequestCache method. This does: - // * Looks up the route based on the new rewritten URL - // * Creates the RequestContext with all route parameters and then executes the correct handler that matches the route - //we also cannot re-create this functionality because the setter for the HttpContext.Request.RequestContext is internal - //so really, this is pretty much the only way without using Server.TransferRequest and if we did that, we'd have to rethink - //a bunch of things! - var urlRouting = new UrlRoutingModule(); - urlRouting.PostResolveRequestCache(context); - } - - - /// - /// Any object that is in the HttpContext.Items collection that is IDisposable will get disposed on the end of the request - /// - /// - private void DisposeHttpContextItems(HttpContext http) - { - // do not process if client-side request - if (http.Request.Url.IsClientSideRequest()) - return; - - //get a list of keys to dispose - var keys = new HashSet(); - foreach (DictionaryEntry i in http.Items) - { - if (i.Value is IDisposeOnRequestEnd || i.Key is IDisposeOnRequestEnd) - { - keys.Add(i.Key); - } - } - //dispose each item and key that was found as disposable. - foreach (var k in keys) - { - try - { - http.Items[k].DisposeIfDisposable(); - } - catch (Exception ex) - { - Logger.Error(ex, "Could not dispose item with key {Key}", k); - } - try - { - k.DisposeIfDisposable(); - } - catch (Exception ex) - { - Logger.Error(ex, "Could not dispose item key {Key}", k); - } - } - } - - #endregion - - #region IHttpModule - - /// - /// Initialize the module, this will trigger for each new application - /// and there may be more than 1 application per application domain - /// - /// - public void Init(HttpApplication app) - { - if (Core.Composing.Current.RuntimeState.Level == RuntimeLevel.BootFailed) - { - // there's nothing we can do really - app.BeginRequest += (sender, args) => - { - // would love to avoid throwing, and instead display a customized Umbraco 500 - // page - however if we don't throw here, something else might go wrong, and - // it's this later exception that would be reported. could not figure out how - // to prevent it, either with httpContext.Response.End() or .ApplicationInstance - // .CompleteRequest() - - // also, if something goes wrong with our DI setup, the logging subsystem may - // not even kick in, so here we try to give as much detail as possible - - Exception e = Core.Composing.Current.RuntimeState.BootFailedException; - if (e == null) - throw new BootFailedException(BootFailedException.DefaultMessage); - var m = new StringBuilder(); - m.Append(BootFailedException.DefaultMessage); - while (e != null) - { - m.Append($"\n\n-> {e.GetType().FullName}: {e.Message}"); - if (string.IsNullOrWhiteSpace(e.StackTrace) == false) - m.Append($"\n{e.StackTrace}"); - e = e.InnerException; - } - throw new BootFailedException(m.ToString()); - }; - return; - } - - // modules are *not* instanciated by the container so we have to - // get our dependencies injected manually, through properties. - Core.Composing.Current.Container.InjectProperties(this); - - //Create a GUID to use as some form of request ID - var requestId = Guid.Empty; - - app.BeginRequest += (sender, e) => - { - var httpContext = ((HttpApplication) sender).Context; - - var httpRequestId = Guid.Empty; - LogHttpRequest.TryGetCurrentHttpRequestId(out httpRequestId); - - Logger.Verbose("Begin request [{HttpRequestId}]: {RequestUrl}", - httpRequestId, - httpContext.Request.Url); - - BeginRequest(new HttpContextWrapper(httpContext)); - }; - - //disable asp.net headers (security) - // This is the correct place to modify headers according to MS: - // https://our.umbraco.com/forum/umbraco-7/using-umbraco-7/65241-Heap-error-from-header-manipulation?p=0#comment220889 - app.PostReleaseRequestState += (sender, args) => - { - var httpContext = ((HttpApplication) sender).Context; - try - { - httpContext.Response.Headers.Remove("Server"); - //this doesn't normally work since IIS sets it but we'll keep it here anyways. - httpContext.Response.Headers.Remove("X-Powered-By"); - httpContext.Response.Headers.Remove("X-AspNet-Version"); - httpContext.Response.Headers.Remove("X-AspNetMvc-Version"); - } - catch (PlatformNotSupportedException) - { - // can't remove headers this way on IIS6 or cassini. - } - }; - - app.PostAuthenticateRequest += (sender, e) => - { - var httpContext = ((HttpApplication) sender).Context; - //ensure the thread culture is set - httpContext.User?.Identity?.EnsureCulture(); - }; - - app.PostResolveRequestCache += (sender, e) => - { - var httpContext = ((HttpApplication) sender).Context; - ProcessRequest(new HttpContextWrapper(httpContext)); - }; - - app.EndRequest += (sender, args) => - { - var httpContext = ((HttpApplication) sender).Context; - - if (UmbracoContext.Current != null) - { - var httpRequestId = Guid.Empty; - LogHttpRequest.TryGetCurrentHttpRequestId(out httpRequestId); - - Logger.Verbose( - "End request [{HttpRequestId}]: {RequestUrl} took {Duration}ms", - httpRequestId, - httpContext.Request.Url, - DateTime.Now.Subtract(UmbracoContext.Current.ObjectCreated).TotalMilliseconds); - } - - OnEndRequest(new UmbracoRequestEventArgs(UmbracoContext.Current, new HttpContextWrapper(httpContext))); - - DisposeHttpContextItems(httpContext); - }; - } - - public void Dispose() - { } - - #endregion - - #region Events - - public static event EventHandler RouteAttempt; - - private void OnRouteAttempt(RoutableAttemptEventArgs args) - { - RouteAttempt?.Invoke(this, args); - } - - public static event EventHandler EndRequest; - - private void OnEndRequest(UmbracoRequestEventArgs args) - { - EndRequest?.Invoke(this, args); - } - - #endregion - - /// - /// This is used to be passed into the GlobalSettings.IsReservedPathOrUrl and will include some 'fake' routes - /// used to determine if a path is reserved. - /// - /// - /// This is basically used to reserve paths dynamically - /// - private readonly Lazy _combinedRouteCollection; - - private RouteCollection CreateRouteCollection() - { - var routes = new RouteCollection(); - - foreach (var route in RouteTable.Routes) - routes.Add(route); - - foreach (var reservedPath in ReservedPaths) - { - try - { - routes.Add("_umbreserved_" + reservedPath.ReplaceNonAlphanumericChars(""), - new Route(reservedPath.TrimStart('/'), new StopRoutingHandler())); - } - catch (Exception ex) - { - Logger.Error(ex, "Could not add reserved path route"); - } - } - - return routes; - } - /// /// This is used internally to track any registered callback paths for Identity providers. If the request path matches /// any of the registered paths, then the module will let the request keep executing diff --git a/src/Umbraco.Web/UmbracoWebService.cs b/src/Umbraco.Web/UmbracoWebService.cs index 95764e40a7..b970a0e8c2 100644 --- a/src/Umbraco.Web/UmbracoWebService.cs +++ b/src/Umbraco.Web/UmbracoWebService.cs @@ -29,7 +29,7 @@ namespace Umbraco.Web Logger = Current.Logger; ProfilingLogger = Current.ProfilingLogger; Services = Current.Services; - GlobalSettings = UmbracoConfig.For.GlobalSettings(); + GlobalSettings = Current.Configs.Global(); } /// @@ -40,7 +40,7 @@ namespace Umbraco.Web /// /// Gets the ProfilingLogger. /// - public ProfilingLogger ProfilingLogger { get; } + public IProfilingLogger ProfilingLogger { get; } /// /// Gets the Umbraco context. diff --git a/src/Umbraco.Web/WebApi/Filters/CheckIfUserTicketDataIsStaleAttribute.cs b/src/Umbraco.Web/WebApi/Filters/CheckIfUserTicketDataIsStaleAttribute.cs index ae51f37ef5..84481868e4 100644 --- a/src/Umbraco.Web/WebApi/Filters/CheckIfUserTicketDataIsStaleAttribute.cs +++ b/src/Umbraco.Web/WebApi/Filters/CheckIfUserTicketDataIsStaleAttribute.cs @@ -76,7 +76,7 @@ namespace Umbraco.Web.WebApi.Filters () => user.Username != identity.Username, () => { - var culture = UserExtensions.GetUserCulture(user, Current.Services.TextService, UmbracoConfig.For.GlobalSettings()); + var culture = UserExtensions.GetUserCulture(user, Current.Services.TextService, Current.Configs.Global()); return culture != null && culture.ToString() != identity.Culture; }, () => user.AllowedSections.UnsortedSequenceEqual(identity.AllowedApplications) == false, @@ -111,10 +111,10 @@ namespace Umbraco.Web.WebApi.Filters if (owinCtx) { var signInManager = owinCtx.Result.GetBackOfficeSignInManager(); - + var backOfficeIdentityUser = Mapper.Map(user); await signInManager.SignInAsync(backOfficeIdentityUser, isPersistent: true, rememberBrowser: false); - + //ensure the remainder of the request has the correct principal set actionContext.Request.SetPrincipalForRequest(owinCtx.Result.Request.User); diff --git a/src/Umbraco.Web/WebApi/Filters/FeatureAuthorizeAttribute.cs b/src/Umbraco.Web/WebApi/Filters/FeatureAuthorizeAttribute.cs index 0046770599..d7bc311941 100644 --- a/src/Umbraco.Web/WebApi/Filters/FeatureAuthorizeAttribute.cs +++ b/src/Umbraco.Web/WebApi/Filters/FeatureAuthorizeAttribute.cs @@ -1,8 +1,8 @@ using System.Web.Http; using System.Web.Http.Controllers; -using LightInject; using Umbraco.Core.Composing; using Umbraco.Web.Features; +using Umbraco.Core; namespace Umbraco.Web.WebApi.Filters { @@ -12,18 +12,27 @@ namespace Umbraco.Web.WebApi.Filters /// Else returns unauthorized. public sealed class FeatureAuthorizeAttribute : AuthorizeAttribute { + private readonly UmbracoFeatures _features; + + /// + /// Initializes a new instance of the class. + /// + public FeatureAuthorizeAttribute() + { + // attributes have to use Current.Container + _features = Current.Factory?.TryGetInstance(); + } + protected override bool IsAuthorized(HttpActionContext actionContext) { // if no features resolver has been set then return true, this will occur in unit // tests and we don't want users to have to set a resolver //just so their unit tests work. - // fixme inject? - var features = Current.Container?.TryGetInstance(); - if (features == null) return true; + if (_features == null) return true; var controllerType = actionContext.ControllerContext.ControllerDescriptor.ControllerType; - return features.IsControllerEnabled(controllerType); + return _features.IsControllerEnabled(controllerType); } } } diff --git a/src/Umbraco.Web/WebApi/Filters/SetAngularAntiForgeryTokensAttribute.cs b/src/Umbraco.Web/WebApi/Filters/SetAngularAntiForgeryTokensAttribute.cs index b55d6aaa4f..b8cad1f26e 100644 --- a/src/Umbraco.Web/WebApi/Filters/SetAngularAntiForgeryTokensAttribute.cs +++ b/src/Umbraco.Web/WebApi/Filters/SetAngularAntiForgeryTokensAttribute.cs @@ -4,6 +4,8 @@ using System.Net.Http; using System.Net.Http.Headers; using System.Web.Helpers; using System.Web.Http.Filters; +using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration; namespace Umbraco.Web.WebApi.Filters @@ -43,14 +45,14 @@ namespace Umbraco.Web.WebApi.Filters Path = "/", //must be js readable HttpOnly = false, - Secure = UmbracoConfig.For.GlobalSettings().UseHttps + Secure = Current.Configs.Global().UseHttps }; var validationCookie = new CookieHeaderValue(AngularAntiForgeryHelper.CsrfValidationCookieName, cookieToken) { Path = "/", HttpOnly = true, - Secure = UmbracoConfig.For.GlobalSettings().UseHttps + Secure = Current.Configs.Global().UseHttps }; context.Response.Headers.AddCookies(new[] { angularCookie, validationCookie }); diff --git a/src/Umbraco.Web/WebApi/Filters/UmbracoWebApiRequireHttpsAttribute.cs b/src/Umbraco.Web/WebApi/Filters/UmbracoWebApiRequireHttpsAttribute.cs index 560e5c0512..50d997f40d 100644 --- a/src/Umbraco.Web/WebApi/Filters/UmbracoWebApiRequireHttpsAttribute.cs +++ b/src/Umbraco.Web/WebApi/Filters/UmbracoWebApiRequireHttpsAttribute.cs @@ -4,6 +4,8 @@ using System.Net.Http; using System.Text; using System.Web.Http.Controllers; using System.Web.Http.Filters; +using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration; namespace Umbraco.Web.WebApi.Filters @@ -23,7 +25,7 @@ namespace Umbraco.Web.WebApi.Filters public override void OnAuthorization(HttpActionContext actionContext) { var request = actionContext.Request; - if (UmbracoConfig.For.GlobalSettings().UseHttps && request.RequestUri.Scheme != Uri.UriSchemeHttps) + if (Current.Configs.Global().UseHttps && request.RequestUri.Scheme != Uri.UriSchemeHttps) { HttpResponseMessage response; var uri = new UriBuilder(request.RequestUri) diff --git a/src/Umbraco.Web/WebApi/UmbracoApiController.cs b/src/Umbraco.Web/WebApi/UmbracoApiController.cs index d52629d1b2..3db3610cc2 100644 --- a/src/Umbraco.Web/WebApi/UmbracoApiController.cs +++ b/src/Umbraco.Web/WebApi/UmbracoApiController.cs @@ -1,4 +1,10 @@ -using Umbraco.Core.Composing; +using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence; +using Umbraco.Core.Services; namespace Umbraco.Web.WebApi { @@ -6,5 +12,19 @@ namespace Umbraco.Web.WebApi /// Provides a base class for auto-routed Umbraco API controllers. /// public abstract class UmbracoApiController : UmbracoApiControllerBase, IDiscoverable - { } + { + /// + /// Initializes a new instance of the with auto dependencies. + /// + /// Dependencies are obtained from the service locator. + protected UmbracoApiController() + { } + + /// + /// Initialize a new instance of the with all its dependencies. + /// + protected UmbracoApiController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, CacheHelper applicationCache, IProfilingLogger logger, IRuntimeState runtimeState) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, applicationCache, logger, runtimeState) + { } + } } diff --git a/src/Umbraco.Web/WebApi/UmbracoApiControllerBase.cs b/src/Umbraco.Web/WebApi/UmbracoApiControllerBase.cs index 56cc884d39..21b88cc919 100644 --- a/src/Umbraco.Web/WebApi/UmbracoApiControllerBase.cs +++ b/src/Umbraco.Web/WebApi/UmbracoApiControllerBase.cs @@ -1,10 +1,10 @@ using System; using System.Web; using System.Web.Http; -using LightInject; using Microsoft.Owin; using Umbraco.Core; using Umbraco.Core.Cache; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Persistence; @@ -21,65 +21,84 @@ namespace Umbraco.Web.WebApi [FeatureAuthorize] public abstract class UmbracoApiControllerBase : ApiController { + private readonly IUmbracoContextAccessor _umbracoContextAccessor; private UmbracoHelper _umbracoHelper; - // for debugging purposes + // note: all Umbraco controllers have two constructors: one with all dependencies, which should be used, + // and one with auto dependencies, ie no dependencies - and then dependencies are automatically obtained + // here from the Current service locator - this is obviously evil, but it allows us to add new dependencies + // without breaking compatibility. + + /// + /// Initializes a new instance of the class with auto dependencies. + /// + /// Dependencies are obtained from the service locator. + protected UmbracoApiControllerBase() + : this( + Current.Factory.GetInstance(), + Current.Factory.GetInstance(), + Current.Factory.GetInstance(), + Current.Factory.GetInstance(), + Current.Factory.GetInstance(), + Current.Factory.GetInstance(), + Current.Factory.GetInstance() + ) + { } + + /// + /// Initializes a new instance of the class with all its dependencies. + /// + protected UmbracoApiControllerBase(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, CacheHelper applicationCache, IProfilingLogger logger, IRuntimeState runtimeState) + { + GlobalSettings = globalSettings; + _umbracoContextAccessor = umbracoContextAccessor; + SqlContext = sqlContext; + Services = services; + ApplicationCache = applicationCache; + Logger = logger; + RuntimeState = runtimeState; + } + + /// + /// Gets a unique instance identifier. + /// + /// For debugging purposes. internal Guid InstanceId { get; } = Guid.NewGuid(); - // note - // properties marked as [Inject] below will be property-injected (vs constructor-injected) in - // order to keep the constuctor as light as possible, so that ppl implementing eg a SurfaceController - // don't need to implement complex constructors + need to refactor them each time we change ours. - // this means that these properties have a setter. - // what can go wrong? + /// + /// Gets the Umbraco context. + /// + public virtual IGlobalSettings GlobalSettings { get; } /// - /// Gets or sets the Umbraco context. + /// Gets the Umbraco context. /// - [Inject] - public virtual IGlobalSettings GlobalSettings { get; set; } + public virtual UmbracoContext UmbracoContext => _umbracoContextAccessor.UmbracoContext; /// - /// Gets or sets the Umbraco context. + /// Gets the sql context. /// - [Inject] - public virtual UmbracoContext UmbracoContext { get; set; } + public ISqlContext SqlContext { get; } /// - /// Gets or sets the sql context. + /// Gets the services context. /// - [Inject] - public ISqlContext SqlContext { get; set; } + public ServiceContext Services { get; } /// - /// Gets or sets the services context. + /// Gets the application cache. /// - [Inject] - public ServiceContext Services { get; set; } + public CacheHelper ApplicationCache { get; } /// - /// Gets or sets the application cache. + /// Gets the logger. /// - [Inject] - public CacheHelper ApplicationCache { get; set; } + public IProfilingLogger Logger { get; } /// - /// Gets or sets the logger. + /// Gets the runtime state. /// - [Inject] - public ILogger Logger { get; set; } - - /// - /// Gets or sets the profiling logger. - /// - [Inject] - public ProfilingLogger ProfilingLogger { get; set; } - - /// - /// Gets or sets the runtime state. - /// - [Inject] - internal IRuntimeState RuntimeState { get; set; } + internal IRuntimeState RuntimeState { get; } /// /// Gets the application url. @@ -106,16 +125,12 @@ namespace Umbraco.Web.WebApi /// Tries to get the current HttpContext. /// protected Attempt TryGetHttpContext() - { - return Request.TryGetHttpContext(); - } + => Request.TryGetHttpContext(); /// /// Tries to get the current OWIN context. /// protected Attempt TryGetOwinContext() - { - return Request.TryGetOwinContext(); - } + => Request.TryGetOwinContext(); } } diff --git a/src/Umbraco.Web/WebApi/UmbracoApiControllerTypeCollection.cs b/src/Umbraco.Web/WebApi/UmbracoApiControllerTypeCollection.cs index c5c7f9dd2a..cdb1aa2f51 100644 --- a/src/Umbraco.Web/WebApi/UmbracoApiControllerTypeCollection.cs +++ b/src/Umbraco.Web/WebApi/UmbracoApiControllerTypeCollection.cs @@ -8,7 +8,7 @@ namespace Umbraco.Web.WebApi // which we are not doing at the moment // we can inherit from BuilderCollectionBase and just be enumerable - internal class UmbracoApiControllerTypeCollection : BuilderCollectionBase + public class UmbracoApiControllerTypeCollection : BuilderCollectionBase { public UmbracoApiControllerTypeCollection(IEnumerable items) : base(items) diff --git a/src/Umbraco.Web/WebApi/UmbracoAuthorizedApiController.cs b/src/Umbraco.Web/WebApi/UmbracoAuthorizedApiController.cs index 6152503d4e..4ae9c00a47 100644 --- a/src/Umbraco.Web/WebApi/UmbracoAuthorizedApiController.cs +++ b/src/Umbraco.Web/WebApi/UmbracoAuthorizedApiController.cs @@ -1,16 +1,22 @@ -using Umbraco.Web.WebApi.Filters; +using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; +using Umbraco.Web.WebApi.Filters; using Umbraco.Core.Models.Identity; -using Umbraco.Core.Security; +using Umbraco.Core.Persistence; +using Umbraco.Core.Services; using Umbraco.Web.Security; namespace Umbraco.Web.WebApi { /// - /// Provides a base class for autorized auto-routed Umbraco API controllers. + /// Provides a base class for authorized auto-routed Umbraco API controllers. /// /// - /// This controller will also append a custom header to the response if the user is logged in using forms authentication - /// which indicates the seconds remaining before their timeout expires. + /// This controller will also append a custom header to the response if the user + /// is logged in using forms authentication which indicates the seconds remaining + /// before their timeout expires. /// [IsBackOffice] [UmbracoUserTimeoutFilter] @@ -24,6 +30,23 @@ namespace Umbraco.Web.WebApi { private BackOfficeUserManager _userManager; + /// + /// Initializes a new instance of the with auto dependencies. + /// + /// Dependencies are obtained from the service locator. + protected UmbracoAuthorizedApiController() + { } + + /// + /// Initializes a new instance of the class with all its dependencies. + /// + protected UmbracoAuthorizedApiController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, CacheHelper applicationCache, IProfilingLogger logger, IRuntimeState runtimeState) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, applicationCache, logger, runtimeState) + { } + + /// + /// Gets the user manager. + /// protected BackOfficeUserManager UserManager => _userManager ?? (_userManager = TryGetOwinContext().Result.GetBackOfficeUserManager()); } diff --git a/src/Umbraco.Web/_Legacy/Controls/DataAttributes.cs b/src/Umbraco.Web/_Legacy/Controls/DataAttributes.cs deleted file mode 100644 index 0f83ecd53e..0000000000 --- a/src/Umbraco.Web/_Legacy/Controls/DataAttributes.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Collections.Generic; -using System.Web.UI.WebControls; - -namespace Umbraco.Web._Legacy.Controls -{ - internal class DataAttributes : Dictionary - { - - public void AppendTo(WebControl c) - { - foreach (var keyval in this) - c.Attributes.Add("data-" + keyval.Key, keyval.Value); - } - } -} diff --git a/src/Umbraco.Web/_Legacy/Controls/FieldDropDownList.cs b/src/Umbraco.Web/_Legacy/Controls/FieldDropDownList.cs deleted file mode 100644 index cf85b107e0..0000000000 --- a/src/Umbraco.Web/_Legacy/Controls/FieldDropDownList.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System.Web; -using System.Web.UI; -using System.Web.UI.WebControls; - -namespace Umbraco.Web._Legacy.Controls -{ - public class FieldDropDownList : DropDownList - { - private bool _customOptionsStarted; - private bool _standardOptionsStarted; - - public string CustomPropertiesLabel { get; set; } - public string StandardPropertiesLabel { get; set; } - public string ChooseText { get; set; } - - protected override void RenderContents(HtmlTextWriter writer) - { - if (Items.Count > 0 && Items[0].Text.StartsWith("#")) - { - Items.Insert(0, new ListItem(ChooseText, "")); - SelectedIndex = 0; - - base.RenderContents(writer); - return; - } - - writer.Write("", ChooseText); - - foreach (ListItem item in Items) - { - if (!_customOptionsStarted) - { - RenderOptionGroupBeginTag(CustomPropertiesLabel, writer); - _customOptionsStarted = true; - } - else if (item.Text.StartsWith("@") && !_standardOptionsStarted) - { - _standardOptionsStarted = true; - RenderOptionGroupEndTag(writer); - RenderOptionGroupBeginTag(StandardPropertiesLabel, writer); - } - - writer.WriteBeginTag("option"); - writer.WriteAttribute("value", item.Value, true); - - foreach (string key in item.Attributes.Keys) - writer.WriteAttribute(key, item.Attributes[key]); - - writer.Write(HtmlTextWriter.TagRightChar); - HttpUtility.HtmlEncode(item.Text.Replace("@", ""), writer); - writer.WriteEndTag("option"); - writer.WriteLine(); - } - - RenderOptionGroupEndTag(writer); - } - - private void RenderOptionGroupBeginTag(string name, HtmlTextWriter writer) - { - writer.WriteBeginTag("optgroup"); - writer.WriteAttribute("label", name); - writer.Write(HtmlTextWriter.TagRightChar); - writer.WriteLine(); - } - - private void RenderOptionGroupEndTag(HtmlTextWriter writer) - { - writer.WriteEndTag("optgroup"); - writer.WriteLine(); - } - } -} diff --git a/src/Umbraco.Web/_Legacy/Controls/MenuButton.cs b/src/Umbraco.Web/_Legacy/Controls/MenuButton.cs deleted file mode 100644 index bf44f3a379..0000000000 --- a/src/Umbraco.Web/_Legacy/Controls/MenuButton.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System; -using Umbraco.Core.IO; - -namespace Umbraco.Web._Legacy.Controls -{ - public class MenuButton : System.Web.UI.WebControls.LinkButton - { - public MenuButtonType ButtonType { get; set; } - internal DataAttributes Data { get; private set; } - public string Hotkey { get; set; } - - public string Icon { get; set; } - - public MenuButton() - { - Data = new DataAttributes(); - CssClass = "btn"; - } - - - protected override void Render(System.Web.UI.HtmlTextWriter writer) - { - //setup a hotkey if present - if (ButtonType == MenuButtonType.Primary && String.IsNullOrEmpty(Hotkey)) - Data.Add("hotkey", "ctrl+" + this.Text.ToLower()[0]); - else if (!string.IsNullOrEmpty(Hotkey)) - Data.Add("shortcut", Hotkey); - - Data.AppendTo(this); - - string cssClass = "btn"; - - if (Icon != null) - { - cssClass = "btn editorIcon"; - var i = Icon.Trim('.'); - - if (!string.IsNullOrEmpty(i)) - { - this.ToolTip = this.Text; - - if (i.Contains(".")) - this.Text = "" + this.ToolTip + " " + this.ToolTip; - else - this.Text = " " + this.ToolTip; - } - } - - cssClass += " btn-" + Enum.GetName(ButtonType.GetType(), ButtonType).ToLower() + " " + CssClass; - this.CssClass = cssClass.Trim(); - - - base.Render(writer); - } - - } - - - public enum MenuButtonType - { - Default, - Primary, - Info, - Success, - Warning, - Danger, - Inverse, - Link - } -} diff --git a/src/Umbraco.Web/_Legacy/Controls/MenuButtonI.cs b/src/Umbraco.Web/_Legacy/Controls/MenuButtonI.cs deleted file mode 100644 index 28ae95557a..0000000000 --- a/src/Umbraco.Web/_Legacy/Controls/MenuButtonI.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Collections; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Web; -using System.Web.SessionState; -using System.Web.UI; -using System.Web.UI.WebControls; -using System.Web.UI.HtmlControls; -using ClientDependency.Core; - -namespace Umbraco.Web._Legacy.Controls -{ - - public class MenuImageButton : System.Web.UI.WebControls.ImageButton, MenuIconI { - private string _OnClickCommand = ""; - - public string AltText { - get { return this.AlternateText; } - set { - this.AlternateText = value; - this.Attributes.Add("title", value); - } - } - public int IconWidth { - get { return (int)this.Width.Value; } - set { this.Width = value; } - } - public int IconHeight { - get { return (int)this.Height.Value; } - set { this.Height = value; } - } - - - public string ImageURL { - get { return base.ImageUrl; } - set { base.ImageUrl = value; } - } - - public string OnClickCommand { - get { return _OnClickCommand; } - set { _OnClickCommand = value; } - } - - protected override void CreateChildControls() - { - this.Width = Unit.Pixel(20); - this.Height = Unit.Pixel(20); - this.Style.Clear(); - this.Attributes.Add("class", "btn btn-default editorIcon"); - - - if (_OnClickCommand != "") - { - this.Attributes.Add("onClick", OnClickCommand); - } - base.CreateChildControls(); - } - } -} diff --git a/src/Umbraco.Web/_Legacy/Controls/MenuIcon.cs b/src/Umbraco.Web/_Legacy/Controls/MenuIcon.cs deleted file mode 100644 index fe8b654b8a..0000000000 --- a/src/Umbraco.Web/_Legacy/Controls/MenuIcon.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System.ComponentModel; -using System.Web.UI; -using ClientDependency.Core; - -namespace Umbraco.Web._Legacy.Controls -{ - - [ClientDependency(ClientDependencyType.Css, "menuicon/style.css", "UmbracoClient")] - internal class MenuIcon : System.Web.UI.WebControls.Image, MenuIconI - { - private string _OnClickCommand = ""; - private string _AltText = "init"; - - public string AltText { - get { return this.AlternateText; } - set { - _AltText = value; - this.AlternateText = value; - this.Attributes.Add("title", value); - } - } - public int IconWidth { - get { return (int)this.Width.Value; } - set { this.Width = value; } - } - public int IconHeight { - get { return (int)this.Height.Value; } - set { this.Height = value; } - } - - public string ImageURL { - get { return this.ImageUrl; } - set { this.ImageUrl = value; } - } - - public string OnClickCommand { - get { return _OnClickCommand; } - set { _OnClickCommand = value; } - } - - protected override void OnLoad(System.EventArgs EventArguments) { - - - // NH 17-01-2007. Trying to avoid inline styling soup - // Me.Width = WebControls.Unit.Pixel(22) - // Me.Height = WebControls.Unit.Pixel(23) - //Me.Style.Add("border", "0px") - this.Attributes.Add("class", "editorIcon"); - this.Attributes.Add("onMouseover", "this.className='editorIconOver'"); - string holder = ""; -// if (this.ID != "") { - //holder = this.ID.Substring(0, this.ID.LastIndexOf("_")) + "_menu"; - this.Attributes.Add("onMouseout", "hoverIconOut('" + holder + "','" + this.ID + "');"); - this.Attributes.Add("onMouseup", "hoverIconOut('" + holder + "','" + this.ID + "');"); -// } else { - this.Attributes.Add("onMouseout", "this.className='editorIcon'"); - this.Attributes.Add("onMouseup", "this.className='editorIcon'"); -// } - this.Attributes.Add("onMouseDown", "this.className='editorIconDown'; return false;"); - this.AlternateText = _AltText; - this.Attributes.Add("title", _AltText); - - if (_OnClickCommand != "") { - this.Attributes.Add("onClick", OnClickCommand); - } - } - - - } -} diff --git a/src/Umbraco.Web/_Legacy/Controls/MenuIconI.cs b/src/Umbraco.Web/_Legacy/Controls/MenuIconI.cs deleted file mode 100644 index 06716c6cb1..0000000000 --- a/src/Umbraco.Web/_Legacy/Controls/MenuIconI.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace Umbraco.Web._Legacy.Controls -{ - public interface MenuIconI { - string ImageURL { - get; - set; - } - string ID { - get; - set; - } - string OnClickCommand { - get; - set; - } - string AltText { - get; - set; - } - int IconWidth { - get; - set; - } - int IconHeight { - get; - set; - } - } -} diff --git a/src/Umbraco.Web/_Legacy/Controls/ProgressBar.cs b/src/Umbraco.Web/_Legacy/Controls/ProgressBar.cs deleted file mode 100644 index 7a30a5cbb1..0000000000 --- a/src/Umbraco.Web/_Legacy/Controls/ProgressBar.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using Umbraco.Core; -using Umbraco.Core.Composing; -using Umbraco.Core.Services; - -namespace Umbraco.Web._Legacy.Controls -{ - [Obsolete("Use Umbraco.Web.UI.Controls.ProgressBar")] - public class ProgressBar : System.Web.UI.WebControls.Panel - { - private string _title = Current.Services.TextService.Localize("publish/inProgress"); - public string Title { get; set; } - - protected override void Render(System.Web.UI.HtmlTextWriter writer) - { - if(!string.IsNullOrEmpty(Title)) - _title = Title; - - base.CssClass = "umb-loader"; - - base.Render(writer); - } - } -} diff --git a/src/Umbraco.Web/_Legacy/Controls/Splitter.cs b/src/Umbraco.Web/_Legacy/Controls/Splitter.cs deleted file mode 100644 index df6ade91bc..0000000000 --- a/src/Umbraco.Web/_Legacy/Controls/Splitter.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.ComponentModel; -using System.Web.UI; -using Umbraco.Core.IO; - -namespace Umbraco.Web._Legacy.Controls -{ - internal class Splitter : System.Web.UI.WebControls.Image { - - protected override void OnLoad(System.EventArgs EventArguments) { - this.Height = System.Web.UI.WebControls.Unit.Pixel(21); - this.Style.Add("border", "0px"); - this.Attributes.Add("class", "editorIconSplit"); - this.ImageUrl = "/menuicon/images/split.gif"; - } - } -} diff --git a/src/Umbraco.Web/_Legacy/Controls/feedback.cs b/src/Umbraco.Web/_Legacy/Controls/feedback.cs deleted file mode 100644 index 4c4b5bf143..0000000000 --- a/src/Umbraco.Web/_Legacy/Controls/feedback.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using ClientDependency.Core; - -namespace Umbraco.Web._Legacy.Controls -{ - - [ClientDependency(ClientDependencyType.Css, "ui/default.css", "UmbracoClient")] - public class Feedback : System.Web.UI.WebControls.Panel - { - - public Feedback() { - - } - - protected override void OnInit(EventArgs e) { - } - - protected override void OnLoad(System.EventArgs EventArguments) { - } - - public feedbacktype type { get; set; } - - private string _text = string.Empty; - public string Text { - get { - - return _text; - - } - set { _text = value; } - } - - public enum feedbacktype{ - notice, - error, - success - } - - protected override void Render(System.Web.UI.HtmlTextWriter writer) { - if (_text != string.Empty) { - base.CreateChildControls(); - - string styleString = ""; - foreach (string key in this.Style.Keys) { - styleString += key + ":" + this.Style[key] + ";"; - } - - writer.WriteLine("

"); - writer.WriteLine(_text); - writer.WriteLine("

"); - } - } - } -} diff --git a/src/Umbraco.Web/_Legacy/PackageActions/PackageHelper.cs b/src/Umbraco.Web/_Legacy/PackageActions/PackageHelper.cs index c479b720da..55171c2643 100644 --- a/src/Umbraco.Web/_Legacy/PackageActions/PackageHelper.cs +++ b/src/Umbraco.Web/_Legacy/PackageActions/PackageHelper.cs @@ -7,7 +7,7 @@ using Umbraco.Core.Xml; namespace Umbraco.Web._Legacy.PackageActions { - public class PackageHelper + internal class PackageHelper { //Helper method to replace umbraco tags that breaks the xml format.. public static string ParseToValidXml(ITemplate templateObj, ref bool hasAspNetContentBeginning, string template, bool toValid) diff --git a/src/Umbraco.Web/_Legacy/PackageActions/addApplicationTree.cs b/src/Umbraco.Web/_Legacy/PackageActions/addApplicationTree.cs deleted file mode 100644 index b83cb964c2..0000000000 --- a/src/Umbraco.Web/_Legacy/PackageActions/addApplicationTree.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System.Xml; -using Umbraco.Core; -using Umbraco.Core._Legacy.PackageActions; -using Umbraco.Web.Composing; - -namespace Umbraco.Web._Legacy.PackageActions -{ - /*Build in standard actions */ - - /// - /// This class implements the IPackageAction Interface, used to execute code when packages are installed. - /// All IPackageActions only takes a PackageName and a XmlNode as input, and executes based on the data in the xmlnode. - /// - public class addApplicationTree : IPackageAction - { - - #region IPackageAction Members - - /// - /// Executes the specified package action. - /// - /// Name of the package. - /// The XML data. - /// - /// - /// - /// - public bool Execute(string packageName, XmlNode xmlData) - { - bool initialize = bool.Parse(xmlData.Attributes["initialize"].Value); - byte sortOrder = byte.Parse(xmlData.Attributes["sortOrder"].Value); - - string applicationAlias = xmlData.Attributes["applicationAlias"].Value; - string treeAlias = xmlData.Attributes["treeAlias"].Value; - string treeTitle = xmlData.Attributes["treeTitle"].Value; - string iconOpened = xmlData.Attributes["iconOpened"].Value; - string iconClosed = xmlData.Attributes["iconClosed"].Value; - string type = xmlData.Attributes["treeHandlerType"].Value; - - Current.Services.ApplicationTreeService.MakeNew(initialize, sortOrder, applicationAlias, treeAlias, treeTitle, iconClosed, iconOpened, type); - - return true; - } - - /// - /// Undoes the action - /// - /// Name of the package. - /// The XML data. - /// - public bool Undo(string packageName, XmlNode xmlData) - { - string treeAlias = xmlData.Attributes["treeAlias"].Value; - var found = Current.Services.ApplicationTreeService.GetByAlias(treeAlias); - if (found != null) - { - Current.Services.ApplicationTreeService.DeleteTree(found); - } - return true; - } - - /// - /// Action alias. - /// - /// - public string Alias() - { - return "addApplicationTree"; - } - - #endregion - - - public XmlNode SampleXml() - { - - string sample = ""; - return PackageHelper.ParseStringToXmlNode(sample); - } - } -} diff --git a/src/Umbraco.Web/_Legacy/PackageActions/moveRootDocument.cs b/src/Umbraco.Web/_Legacy/PackageActions/moveRootDocument.cs deleted file mode 100644 index 83d9387352..0000000000 --- a/src/Umbraco.Web/_Legacy/PackageActions/moveRootDocument.cs +++ /dev/null @@ -1,102 +0,0 @@ - -//TODO: MIgrate this to core: http://issues.umbraco.org/issue/U4-5857 - -//using System; -//using System.Xml; - -//namespace umbraco.cms.businesslogic.packager.standardPackageActions -//{ -// /// -// /// This class implements the IPackageAction Interface, used to execute code when packages are installed. -// /// All IPackageActions only takes a PackageName and a XmlNode as input, and executes based on the data in the xmlnode. -// /// -// public class moveRootDocument : umbraco.interfaces.IPackageAction -// { -// #region IPackageAction Members - -// /// -// /// Executes the specified package action. -// /// -// /// Name of the package. -// /// The XML data. -// /// -// /// -// /// -// /// True if executed succesfully -// public bool Execute(string packageName, XmlNode xmlData) -// { - -// string documentName = xmlData.Attributes["documentName"].Value; -// string parentDocumentType = xmlData.Attributes["parentDocumentType"].Value; -// string parentDocumentName = ""; - -// if (xmlData.Attributes["parentDocumentName"] != null) -// parentDocumentName = xmlData.Attributes["parentDocumentName"].Value; - -// int parentDocid = 0; - -// ContentType ct = ContentType.GetByAlias(parentDocumentType); -// Content[] docs = web.Document.getContentOfContentType(ct); - -// if (docs.Length > 0) -// { -// if (String.IsNullOrEmpty(parentDocumentName)) -// parentDocid = docs[0].Id; -// else -// { -// foreach (Content doc in docs) -// { -// if (doc.Text == parentDocumentName) -// parentDocid = doc.Id; -// } -// } -// } - -// if (parentDocid > 0) -// { -// web.Document[] rootDocs = web.Document.GetRootDocuments(); - -// foreach (web.Document rootDoc in rootDocs) -// { -// if (rootDoc.Text == documentName) -// { -// rootDoc.Move(parentDocid); -// rootDoc.PublishWithSubs(new umbraco.BusinessLogic.User(0)); -// } -// } -// } - - -// return true; -// } - -// //this has no undo. -// /// -// /// This action has no undo. -// /// -// /// Name of the package. -// /// The XML data. -// /// -// public bool Undo(string packageName, XmlNode xmlData) -// { -// return true; -// } - -// /// -// /// Action alias -// /// -// /// -// public string Alias() -// { -// return "moveRootDocument"; -// } - -// #endregion - -// public XmlNode SampleXml() -// { -// throw new NotImplementedException(); -// } - -// } -//} diff --git a/src/Umbraco.Web/_Legacy/Packager/Installer.cs b/src/Umbraco.Web/_Legacy/Packager/Installer.cs index 020e34d4ce..795aa9ead7 100644 --- a/src/Umbraco.Web/_Legacy/Packager/Installer.cs +++ b/src/Umbraco.Web/_Legacy/Packager/Installer.cs @@ -1,24 +1,26 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.IO; -using System.Xml; using System.Linq; +using System.Net; +using System.Xml; using ICSharpCode.SharpZipLib.Zip; using Umbraco.Core; +using Umbraco.Core.Composing; +using Umbraco.Core.Events; using Umbraco.Core.IO; using Umbraco.Core.Logging; -using System.Diagnostics; using Umbraco.Core.Models; -using Umbraco.Core.Composing; -using System.Net; -using Umbraco.Core.Events; using Umbraco.Core.Models.Packaging; using Umbraco.Core.Services.Implement; using Umbraco.Core.Xml; +using Umbraco.Web._Legacy.Packager.PackageInstance; using File = System.IO.File; +using PackageAction = Umbraco.Web._Legacy.Packager.PackageInstance.PackageAction; -namespace umbraco.cms.businesslogic.packager +namespace Umbraco.Web._Legacy.Packager { /// /// The packager is a component which enables sharing of both data and functionality components between different umbraco installations. diff --git a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/CreatedPackage.cs b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/CreatedPackage.cs index 5d811ed3d8..3bfcdd3f86 100644 --- a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/CreatedPackage.cs +++ b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/CreatedPackage.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.IO; using System.Xml; -using Lucene.Net.Documents; using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.IO; @@ -11,7 +10,7 @@ using Umbraco.Core.Services; using File = System.IO.File; -namespace umbraco.cms.businesslogic.packager +namespace Umbraco.Web._Legacy.Packager.PackageInstance { public class CreatedPackage { diff --git a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/IPackageInstance.cs b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/IPackageInstance.cs index 28c7c05b6b..b920c85a9f 100644 --- a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/IPackageInstance.cs +++ b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/IPackageInstance.cs @@ -1,5 +1,4 @@ -using System; -namespace umbraco.cms.businesslogic.packager{ +namespace Umbraco.Web._Legacy.Packager.PackageInstance{ public interface IPackageInstance { string Actions { get; set; } string Author { get; set; } diff --git a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/InstalledPackage.cs b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/InstalledPackage.cs index 9e04bff636..3fca97759b 100644 --- a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/InstalledPackage.cs +++ b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/InstalledPackage.cs @@ -1,15 +1,15 @@ using System; using System.Collections.Generic; using System.Linq; -using Umbraco.Core; using Umbraco.Core.Composing; -using Umbraco.Core.Logging; using Umbraco.Core.IO; +using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Packaging; using Umbraco.Core.Services; -namespace umbraco.cms.businesslogic.packager { +namespace Umbraco.Web._Legacy.Packager.PackageInstance +{ public class InstalledPackage { diff --git a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackageActions.cs b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackageActions.cs index d1df47cd44..bfd1030d85 100644 --- a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackageActions.cs +++ b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackageActions.cs @@ -1,12 +1,10 @@ using System; using System.Xml; -using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Logging; using Umbraco.Core._Legacy.PackageActions; - -namespace umbraco.cms.businesslogic.packager +namespace Umbraco.Web._Legacy.Packager.PackageInstance { /// diff --git a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackageInstance.cs b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackageInstance.cs index 8f7cb56320..fe32633ccd 100644 --- a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackageInstance.cs +++ b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackageInstance.cs @@ -1,10 +1,7 @@ using System; - -using System.Xml; -using System.Xml.XPath; using System.Collections.Generic; -namespace umbraco.cms.businesslogic.packager +namespace Umbraco.Web._Legacy.Packager.PackageInstance { public class PackageInstance { diff --git a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackagerUtility.cs b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackagerUtility.cs index 53ed58810c..d972977bad 100644 --- a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackagerUtility.cs +++ b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackagerUtility.cs @@ -8,7 +8,7 @@ using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.Services; -namespace umbraco.cms.businesslogic.packager +namespace Umbraco.Web._Legacy.Packager.PackageInstance { /// /// A utillity class for working with packager data. diff --git a/src/Umbraco.Web/_Legacy/Packager/RequirementsType.cs b/src/Umbraco.Web/_Legacy/Packager/RequirementsType.cs index ea63ff6979..ca91626128 100644 --- a/src/Umbraco.Web/_Legacy/Packager/RequirementsType.cs +++ b/src/Umbraco.Web/_Legacy/Packager/RequirementsType.cs @@ -1,4 +1,4 @@ -namespace umbraco.cms.businesslogic.packager +namespace Umbraco.Web._Legacy.Packager { public enum RequirementsType { diff --git a/src/Umbraco.Web/_Legacy/Packager/Settings.cs b/src/Umbraco.Web/_Legacy/Packager/Settings.cs index 85cfebcfee..57ab4b9ea4 100644 --- a/src/Umbraco.Web/_Legacy/Packager/Settings.cs +++ b/src/Umbraco.Web/_Legacy/Packager/Settings.cs @@ -1,16 +1,8 @@ using System; -using System.Data; -using System.Configuration; -using System.Web; -using System.Web.Security; -using System.Web.UI; -using System.Web.UI.WebControls; -using System.Web.UI.WebControls.WebParts; -using System.Web.UI.HtmlControls; using System.IO; using Umbraco.Core.IO; -namespace umbraco.cms.businesslogic.packager +namespace Umbraco.Web._Legacy.Packager { public class Settings { diff --git a/src/Umbraco.Web/_Legacy/Packager/data.cs b/src/Umbraco.Web/_Legacy/Packager/data.cs index 88cc7a4b54..962b7d5ed4 100644 --- a/src/Umbraco.Web/_Legacy/Packager/data.cs +++ b/src/Umbraco.Web/_Legacy/Packager/data.cs @@ -1,15 +1,15 @@ using System; -using System.Xml; using System.Collections.Generic; using System.IO; +using System.Xml; using Umbraco.Core; -using Umbraco.Core.Configuration; using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Xml; -namespace umbraco.cms.businesslogic.packager +namespace Umbraco.Web._Legacy.Packager { /// /// This is the xml data for installed packages. This is not the same xml as a pckage format! @@ -85,7 +85,7 @@ namespace umbraco.cms.businesslogic.packager return Source.SelectSingleNode("/packages/package [@packageGuid = '" + guid + "']"); } - public static PackageInstance MakeNew(string Name, string dataSource) + public static PackageInstance.PackageInstance MakeNew(string Name, string dataSource) { Reload(dataSource); @@ -151,26 +151,26 @@ namespace umbraco.cms.businesslogic.packager return retVal; } - public static PackageInstance Package(int id, string datasource) + public static PackageInstance.PackageInstance Package(int id, string datasource) { return ConvertXmlToPackage(GetFromId(id, datasource, true)); } - public static PackageInstance Package(string guid, string datasource) + public static PackageInstance.PackageInstance Package(string guid, string datasource) { XmlNode node = GetFromGuid(guid, datasource, true); if (node != null) return ConvertXmlToPackage(node); else - return new PackageInstance(); + return new PackageInstance.PackageInstance(); } - public static List GetAllPackages(string dataSource) + public static List GetAllPackages(string dataSource) { Reload(dataSource); XmlNodeList nList = data.Source.SelectNodes("packages/package"); - List retVal = new List(); + List retVal = new List(); for (int i = 0; i < nList.Count; i++) { @@ -187,9 +187,9 @@ namespace umbraco.cms.businesslogic.packager return retVal; } - private static PackageInstance ConvertXmlToPackage(XmlNode n) + private static PackageInstance.PackageInstance ConvertXmlToPackage(XmlNode n) { - PackageInstance retVal = new PackageInstance(); + PackageInstance.PackageInstance retVal = new PackageInstance.PackageInstance(); if (n != null) { @@ -263,7 +263,7 @@ namespace umbraco.cms.businesslogic.packager } - public static void Save(PackageInstance package, string dataSource) + public static void Save(PackageInstance.PackageInstance package, string dataSource) { Reload(dataSource); var xmlDef = GetFromId(package.Id, dataSource, false); diff --git a/src/Umbraco.Web/umbraco.presentation/default.aspx.cs b/src/Umbraco.Web/umbraco.presentation/default.aspx.cs index 25dcae7a4e..c7a5d9fdd2 100644 --- a/src/Umbraco.Web/umbraco.presentation/default.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/default.aspx.cs @@ -141,7 +141,7 @@ namespace umbraco if (pos > -1) { string htmlBadge = - string.Format(UmbracoConfig.For.UmbracoSettings().Content.PreviewBadge, + string.Format(Current.Configs.Settings().Content.PreviewBadge, IOHelper.ResolveUrl(SystemDirectories.Umbraco), Server.UrlEncode(UmbracoContext.Current.HttpContext.Request.Path)); diff --git a/src/Umbraco.Web/umbraco.presentation/page.cs b/src/Umbraco.Web/umbraco.presentation/page.cs index 9e6c85c9ea..a9100f39ef 100644 --- a/src/Umbraco.Web/umbraco.presentation/page.cs +++ b/src/Umbraco.Web/umbraco.presentation/page.cs @@ -88,10 +88,10 @@ namespace umbraco doc.WriterName, doc.CreatorName, doc.CreateDate, doc.UpdateDate, doc.Path, doc.Parent == null ? -1 : doc.Parent.Id); - if (doc.TemplateId > 0) + if (doc.TemplateId.HasValue) { //set the template to whatever is assigned to the doc - _template = doc.TemplateId; + _template = doc.TemplateId.Value; _elements["template"] = _template.ToString(); } @@ -358,10 +358,7 @@ namespace umbraco get { return _key; } } - public int TemplateId - { - get { return _inner.Template == null ? 0 : _inner.Template.Id; } - } + public int? TemplateId => _inner.TemplateId; public int SortOrder { diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/dualSelectBox.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/controls/dualSelectBox.cs deleted file mode 100644 index 6e109b3327..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/dualSelectBox.cs +++ /dev/null @@ -1,153 +0,0 @@ -using System; -using System.Web.Razor; -using System.Web.UI; -using System.Web.UI.WebControls; -using System.Web.UI.HtmlControls; -using ClientDependency.Core; -using Umbraco.Core; -using Umbraco.Core.Services; -using Umbraco.Web; -using Umbraco.Web.Composing; - -namespace umbraco.controls -{ - /// - /// Summary description for dualSelectbox. - /// - [ClientDependency(ClientDependencyType.Javascript, "js/dualSelectBox.js", "UmbracoRoot")] - public class DualSelectbox : System.Web.UI.WebControls.WebControl, System.Web.UI.INamingContainer - { - private ListItemCollection _items = new ListItemCollection(); - - private ListBox _possibleValues = new ListBox(); - private ListBox _selectedValues = new ListBox(); - private HtmlInputHidden _value = new HtmlInputHidden(); - private HtmlInputButton _add = new HtmlInputButton(); - private HtmlInputButton _remove = new HtmlInputButton(); - private int _rows = 8; - - - public ListItemCollection Items - { - get - { - EnsureChildControls(); - return _items; - } - } - - public new int Width - { - set - { - _possibleValues.Width = new Unit(value); - _selectedValues.Width = new Unit(value); - } - } - - public new int Height - { - set - { - _possibleValues.Height = new Unit(value); - _selectedValues.Height = new Unit(value); - } - } - - protected override void CreateChildControls() - { - _possibleValues.ID = "posVals"; - _selectedValues.ID = "selVals"; - _possibleValues.SelectionMode = ListSelectionMode.Multiple; - _selectedValues.SelectionMode = ListSelectionMode.Multiple; - _possibleValues.CssClass = "guiInputTextStandard"; - _selectedValues.CssClass = "guiInputTextStandard"; - _possibleValues.Rows = _rows; - _selectedValues.Rows = _rows; - - _value.ID = "theValue"; - - HtmlTable table = new HtmlTable(); - table.CellPadding = 5; - table.CellSpacing = 0; - table.Border = 0; - - HtmlTableRow header = new HtmlTableRow(); - header.Controls.Add(new HtmlTableCell { InnerHtml = Current.Services.TextService.Localize("content/notmemberof") }); - header.Controls.Add(new HtmlTableCell { InnerHtml= " " }); - header.Controls.Add(new HtmlTableCell { InnerHtml = Current.Services.TextService.Localize("content/memberof") }); - table.Controls.Add(header); - - HtmlTableRow row = new HtmlTableRow(); - table.Controls.Add(row); - HtmlTableCell cFirst = new HtmlTableCell(); - cFirst.Controls.Add(_possibleValues); - row.Controls.Add(cFirst); - HtmlTableCell cButtons = new HtmlTableCell(); - _add.Value = ">>"; - _add.Attributes.Add("class", "guiInputButton"); - _remove.Value = "<<"; - _remove.Attributes.Add("class", "guiInputButton"); - cButtons.Controls.Add(_add); - cButtons.Controls.Add(new LiteralControl("

")); - cButtons.Controls.Add(_remove); - row.Controls.Add(cButtons); - HtmlTableCell cSecond = new HtmlTableCell(); - cSecond.Controls.Add(_selectedValues); - row.Controls.Add(cSecond); - - this.Controls.Add(table); - this.Controls.Add(_value); - } - - public string Value - { - get - { - return _value.Value; - } - - set - { - _value.Value = value; - } - } - - public int Rows - { - set - { - _rows = value; - } - } - - public DualSelectbox() - { - } - - - protected override void OnPreRender(EventArgs e) - { - base.OnPreRender (e); - - _selectedValues.Items.Clear(); - _possibleValues.Items.Clear(); - - foreach(ListItem li in _items) - { - if (((string) (","+ this.Value +",")).IndexOf(","+li.Value+",") > -1) - _selectedValues.Items.Add(li); - else - _possibleValues.Items.Add(li); - } - - // add js to buttons here to ensure full clientids - _add.Attributes.Add("onClick", "dualSelectBoxShift('" + this.ClientID + "');"); - _remove.Attributes.Add("onClick", "dualSelectBoxShift('" + this.ClientID + "');"); - } - - - - - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/CreatedPackageTasks.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/CreatedPackageTasks.cs index 64f2c94b0a..47980f2808 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/CreatedPackageTasks.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/CreatedPackageTasks.cs @@ -3,8 +3,8 @@ using Umbraco.Web.UI; using Umbraco.Core; using Umbraco.Web; using Umbraco.Web.Composing; +using Umbraco.Web._Legacy.Packager.PackageInstance; using Umbraco.Web._Legacy.UI; -using umbraco.cms.businesslogic.packager; namespace Umbraco.Web { diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx.cs index 22a9f4292f..645aa088f2 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx.cs @@ -12,6 +12,7 @@ using Umbraco.Core; using Umbraco.Core.IO; using Umbraco.Web.UI; using Umbraco.Web.UI.Pages; +using Umbraco.Web._Legacy.Packager.PackageInstance; namespace umbraco.presentation.developer.packages { @@ -31,14 +32,14 @@ namespace umbraco.presentation.developer.packages public Umbraco.Web._Legacy.Controls.TabPage packageActions; protected ContentPicker cp; - private cms.businesslogic.packager.PackageInstance pack; - private cms.businesslogic.packager.CreatedPackage createdPackage; + private PackageInstance pack; + private CreatedPackage createdPackage; protected void Page_Load(object sender, EventArgs e) { if (Request.QueryString["id"] != null) { - createdPackage = cms.businesslogic.packager.CreatedPackage.GetById(int.Parse(Request.QueryString["id"])); + createdPackage = CreatedPackage.GetById(int.Parse(Request.QueryString["id"])); pack = createdPackage.Data; /* CONTENT */ @@ -359,8 +360,8 @@ namespace umbraco.presentation.developer.packages if (newPath.Trim() != "") { - cms.businesslogic.packager.CreatedPackage createdPackage = cms.businesslogic.packager.CreatedPackage.GetById(int.Parse(Request.QueryString["id"])); - cms.businesslogic.packager.PackageInstance pack = createdPackage.Data; + CreatedPackage createdPackage = CreatedPackage.GetById(int.Parse(Request.QueryString["id"])); + PackageInstance pack = createdPackage.Data; pack.Files.Add(newPath); @@ -386,8 +387,8 @@ namespace umbraco.presentation.developer.packages tmpFilePathString += tmpFFFF + "�"; } - cms.businesslogic.packager.CreatedPackage createdPackage = cms.businesslogic.packager.CreatedPackage.GetById(int.Parse(Request.QueryString["id"])); - cms.businesslogic.packager.PackageInstance pack = createdPackage.Data; + CreatedPackage createdPackage = CreatedPackage.GetById(int.Parse(Request.QueryString["id"])); + PackageInstance pack = createdPackage.Data; pack.Files = new List(tmpFilePathString.Trim('�').Split('�')); pack.Files.TrimExcess(); diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/insertMasterpageContent.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/insertMasterpageContent.aspx deleted file mode 100644 index b188df39eb..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/insertMasterpageContent.aspx +++ /dev/null @@ -1,32 +0,0 @@ -<%@ Page Title="" Language="C#" MasterPageFile="../masterpages/umbracoDialog.Master" AutoEventWireup="true" CodeBehind="insertMasterpageContent.aspx.cs" Inherits="umbraco.presentation.umbraco.dialogs.insertMasterpageContent" %> -<%@ Register TagPrefix="cc1" Namespace="Umbraco.Web._Legacy.Controls" %> - - - - - - - - - -

- <%= Services.TextService.Localize("defaultdialogs/templateContentPlaceHolderHelp")%> -

-
- - - -
- - - <%=Services.TextService.Localize("general/cancel")%> - " /> - -
\ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/insertMasterpageContent.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/insertMasterpageContent.aspx.cs deleted file mode 100644 index 96b1006de5..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/insertMasterpageContent.aspx.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Umbraco.Core.Services; -using System; -using System.Linq; -using System.Web.UI.WebControls; -using Umbraco.Core; -using Umbraco.Core.IO; - -namespace umbraco.presentation.umbraco.dialogs -{ - public partial class insertMasterpageContent : Umbraco.Web.UI.Pages.UmbracoEnsuredPage - { - public insertMasterpageContent() - { - CurrentApp = Constants.Applications.Settings.ToString(); - } - - protected void Page_Load(object sender, EventArgs e) - { - //labels - pp_placeholder.Text = Services.TextService.Localize("placeHolderID"); - - //Add a default Item - var li = new ListItem("Choose ID..."); - li.Selected = true; - dd_detectedAlias.Items.Add(li); - - //var t = new cms.businesslogic.template.Template(int.Parse(Request["id"])); - var t = Services.FileService.GetTemplate(int.Parse(Request["id"])); - - - //if (t.MasterTemplate > 0) - if (string.IsNullOrWhiteSpace(t.MasterTemplateAlias) != true) - { - //t = new cms.businesslogic.template.Template(t.MasterTemplate); - t = Services.FileService.GetTemplate(t.MasterTemplateAlias); - - } - - //foreach (string cpId in t.contentPlaceholderIds()) - foreach (string cpId in MasterPageHelper.GetContentPlaceholderIds(t)) - { - dd_detectedAlias.Items.Add(cpId); - } - - if (dd_detectedAlias.Items.Count == 1) - dd_detectedAlias.Items.Add("ContentPlaceHolderDefault"); - - } - - - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/insertMasterpageContent.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/insertMasterpageContent.aspx.designer.cs deleted file mode 100644 index 7303e3d56b..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/insertMasterpageContent.aspx.designer.cs +++ /dev/null @@ -1,42 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace umbraco.presentation.umbraco.dialogs { - - - public partial class insertMasterpageContent { - - /// - /// pane_insert control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane pane_insert; - - /// - /// pp_placeholder control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_placeholder; - - /// - /// dd_detectedAlias control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.DropDownList dd_detectedAlias; - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/insertMasterpagePlaceholder.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/insertMasterpagePlaceholder.aspx deleted file mode 100644 index bb6bca8042..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/insertMasterpagePlaceholder.aspx +++ /dev/null @@ -1,34 +0,0 @@ -<%@ Page Title="" Language="C#" MasterPageFile="../masterpages/umbracoDialog.Master" AutoEventWireup="true" CodeBehind="insertMasterpagePlaceholder.aspx.cs" Inherits="umbraco.presentation.umbraco.dialogs.insertMasterpagePlaceholder" %> - - - - - - - - - -

- <%= Services.TextService.Localize("defaultdialogs/templateContentAreaHelp")%> -

-
- - - - -
- - - <%=Services.TextService.Localize("general/cancel")%> - " /> - -
\ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/insertMasterpagePlaceholder.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/insertMasterpagePlaceholder.aspx.cs deleted file mode 100644 index 48073a7f4f..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/insertMasterpagePlaceholder.aspx.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Umbraco.Core.Services; -using System; -using Umbraco.Core; - -namespace umbraco.presentation.umbraco.dialogs { - public partial class insertMasterpagePlaceholder : Umbraco.Web.UI.Pages.UmbracoEnsuredPage { - - public insertMasterpagePlaceholder() - { - CurrentApp = Constants.Applications.Settings.ToString(); - } - protected void Page_Load(object sender, EventArgs e) { - //labels - pp_placeholder.Text = Services.TextService.Localize("placeHolderID"); - - } - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/insertMasterpagePlaceholder.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/insertMasterpagePlaceholder.aspx.designer.cs deleted file mode 100644 index a12635e368..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/insertMasterpagePlaceholder.aspx.designer.cs +++ /dev/null @@ -1,26 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace umbraco.presentation.umbraco.dialogs { - - - public partial class insertMasterpagePlaceholder { - - /// - /// tb_alias control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox tb_alias; - - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_placeholder; - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/republish.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/republish.aspx deleted file mode 100644 index c58ca0d8fc..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/republish.aspx +++ /dev/null @@ -1,40 +0,0 @@ -<%@ Page Language="c#" Codebehind="republish.aspx.cs" MasterPageFile="../masterpages/umbracoDialog.Master" AutoEventWireup="True" Inherits="umbraco.cms.presentation.republish" %> -<%@ Register TagPrefix="cc1" Namespace="Umbraco.Web._Legacy.Controls" %> - - - - - - - -
-

<%= Services.TextService.Localize("defaultdialogs/siterepublishHelp")%>

-
- -
- - <%= Services.TextService.Localize("or") %> - <%=Services.TextService.Localize("cancel")%> -
- - - -
- - -
-

<%= Services.TextService.Localize("defaultdialogs/siterepublished")%>

- -
- -
-
\ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/republish.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/republish.aspx.cs deleted file mode 100644 index 4fee44eabe..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/republish.aspx.cs +++ /dev/null @@ -1,68 +0,0 @@ -//TODO: Re-create this in angular and new APIS! then remove it - -//using System; -//using System.Collections; -//using System.ComponentModel; -//using System.Data; -//using System.Drawing; -//using System.Web; -//using System.Web.SessionState; -//using System.Web.UI; -//using System.Web.UI.WebControls; -//using System.Web.UI.HtmlControls; -//using Umbraco.Web; - -//namespace umbraco.cms.presentation -//{ -// /// -// /// Summary description for republish. -// /// -// public partial class republish : Umbraco.Web.UI.Pages.UmbracoEnsuredPage -// { -// public republish() -// { -// CurrentApp = Constants.Applications.Content.ToString(); - -// } -// protected void go(object sender, EventArgs e) { -// // re-create xml -// if (Request.GetItemAsString("xml") != "") -// { -// Server.ScriptTimeout = 100000; -// Services.ContentService.RePublishAll(); -// } -// else if (Request.GetItemAsString("previews") != "") -// { -// Server.ScriptTimeout = 100000; -// umbraco.cms.businesslogic.web.Document.RegeneratePreviews(); -// } -// else if (Request.GetItemAsString("refreshNodes") != "") -// { -// Server.ScriptTimeout = 100000; -// System.Xml.XmlDocument xd = new System.Xml.XmlDocument(); - -// var doc = new cms.businesslogic.web.Document(int.Parse(Request.GetItemAsString("refreshNodes"))); - -// foreach (cms.businesslogic.web.Document d in doc.Children) -// { -// d.XmlGenerate(xd); -// Response.Write("
  • Creating xml for " + d.Text + "
  • "); -// Response.Flush(); -// } -// } - -// //PPH changed this to a general library call for load balancing support -// library.RefreshContent(); - -// p_republish.Visible = false; -// p_done.Visible = true; -// } - -// protected void Page_Load(object sender, System.EventArgs e) -// { -// bt_go.Text = Services.TextService.Localize("republish"); -// } - - -// } -//} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/republish.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/republish.aspx.designer.cs deleted file mode 100644 index a2311585a5..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/republish.aspx.designer.cs +++ /dev/null @@ -1,51 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace umbraco.cms.presentation { - - - public partial class republish { - - /// - /// p_republish control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Panel p_republish; - - /// - /// bt_go control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button bt_go; - - /// - /// progbar control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.ProgressBar progbar; - - /// - /// p_done control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Panel p_done; - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/umbracoField.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/umbracoField.aspx.cs deleted file mode 100644 index 244fdeb9da..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/umbracoField.aspx.cs +++ /dev/null @@ -1,251 +0,0 @@ -using System.Linq; -using System.Web.UI.WebControls; -using Umbraco.Core.Configuration; -using Umbraco.Core; -using Umbraco.Core.Services; -using Umbraco.Web; -using Umbraco.Web.Composing; - -namespace umbraco.dialogs -{ - /// - /// Summary description for umbracoField. - /// - public partial class umbracoField : Umbraco.Web.UI.Pages.UmbracoEnsuredPage - { - string[] preValuesSource = { "@createDate", "@creatorName", "@level", "@nodeType", "@nodeTypeAlias", "@pageID", "@pageName", "@parentID", "@path", "@template", "@updateDate", "@writerID", "@writerName" }; - bool m_IsDictionaryMode = false; - - public umbracoField() - { - CurrentApp = Constants.Applications.Settings.ToString(); - } - - protected void Page_Load(object sender, System.EventArgs e) - { - - //set labels on properties... - pp_insertAltField.Text = Services.TextService.Localize("templateEditor/alternativeField"); - pp_insertAltText.Text = Services.TextService.Localize("templateEditor/alternativeText"); - pp_insertBefore.Text = Services.TextService.Localize("templateEditor/preContent"); - pp_insertAfter.Text = Services.TextService.Localize("templateEditor/postContent"); - - pp_FormatAsDate.Text = Services.TextService.Localize("templateEditor/formatAsDate"); - pp_casing.Text = Services.TextService.Localize("templateEditor/casing"); - pp_encode.Text = Services.TextService.Localize("templateEditor/encoding"); - - tagName.Value = "umbraco:Item"; - - using (var scope = Current.ScopeProvider.CreateScope()) - { - // either get page fields or dictionary items - string fieldSql = ""; - if (Request.GetItemAsString("tagName") == "UMBRACOGETDICTIONARY") - { - fieldSql = "select '#'+[key] as alias from cmsDictionary order by alias"; - m_IsDictionaryMode = true; - pp_insertField.Text = "Insert Dictionary Item"; - } - else - { - //exclude built-in memberhip properties from showing up here - var exclude = Constants.Conventions.Member.GetStandardPropertyTypeStubs() - .Select(x => Current.SqlContext.SqlSyntax.GetQuotedValue(x.Key)).ToArray(); - - fieldSql = string.Format( - "select distinct alias from cmsPropertyType where alias not in ({0}) order by alias", - string.Join(",", exclude)); - pp_insertField.Text = Services.TextService.Localize("templateEditor/chooseField"); - } - - fieldPicker.ChooseText = Services.TextService.Localize("templateEditor/chooseField"); - fieldPicker.StandardPropertiesLabel = Services.TextService.Localize("templateEditor/standardFields"); - fieldPicker.CustomPropertiesLabel = Services.TextService.Localize("templateEditor/customFields"); - - var dataTypes = scope.Database.Fetch(fieldSql); - fieldPicker.DataTextField = "alias"; - fieldPicker.DataValueField = "alias"; - fieldPicker.DataSource = dataTypes; - fieldPicker.DataBind(); - fieldPicker.Attributes.Add("onChange", "document.forms[0].field.value = document.forms[0]." + fieldPicker.ClientID + "[document.forms[0]." + fieldPicker.ClientID + ".selectedIndex].value;"); - - altFieldPicker.ChooseText = Services.TextService.Localize("templateEditor/chooseField"); - altFieldPicker.StandardPropertiesLabel = Services.TextService.Localize("templateEditor/standardFields"); - altFieldPicker.CustomPropertiesLabel = Services.TextService.Localize("templateEditor/customFields"); - - var dataTypes2 = scope.Database.Fetch(fieldSql); - altFieldPicker.DataTextField = "alias"; - altFieldPicker.DataValueField = "alias"; - altFieldPicker.DataSource = dataTypes2; - altFieldPicker.DataBind(); - altFieldPicker.Attributes.Add("onChange", "document.forms[0].useIfEmpty.value = document.forms[0]." + altFieldPicker.ClientID + "[document.forms[0]." + altFieldPicker.ClientID + ".selectedIndex].value;"); - - scope.Complete(); - } - - // Pre values - if (!m_IsDictionaryMode) - { - foreach (string s in preValuesSource) - { - fieldPicker.Items.Add(new ListItem(s, s.Replace("@", ""))); - altFieldPicker.Items.Add(new ListItem(s, s.Replace("@", ""))); - } - } - } - - - /// - /// JsInclude1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.JsInclude JsInclude1; - - /// - /// JsInclude2 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.JsInclude JsInclude2; - - /// - /// tagName control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlInputHidden tagName; - - /// - /// pane_form control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane pane_form; - - /// - /// pp_insertField control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_insertField; - - /// - /// fieldPicker control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.FieldDropDownList fieldPicker; - - /// - /// pp_insertAltField control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_insertAltField; - - /// - /// altFieldPicker control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.FieldDropDownList altFieldPicker; - - /// - /// pp_insertAltText control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_insertAltText; - - /// - /// pp_recursive control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_recursive; - - /// - /// pp_insertBefore control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_insertBefore; - - /// - /// pp_insertAfter control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_insertAfter; - - /// - /// pp_FormatAsDate control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_FormatAsDate; - - /// - /// pp_casing control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_casing; - - /// - /// pp_encode control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_encode; - - /// - /// pp_convertLineBreaks control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_convertLineBreaks; - - /// - /// pp_removePTags control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_removePTags; - } -}