diff --git a/src/Umbraco.Core/DisposableTimer.cs b/src/Umbraco.Core/DisposableTimer.cs index 2b9dd6a273..5269244609 100644 --- a/src/Umbraco.Core/DisposableTimer.cs +++ b/src/Umbraco.Core/DisposableTimer.cs @@ -41,17 +41,56 @@ namespace Umbraco.Core return new DisposableTimer(callback); } + /// + /// Adds a start and end log entry as Info and tracks how long it takes until disposed. + /// + /// + /// + /// + /// public static DisposableTimer TraceDuration(string startMessage, string completeMessage) { return TraceDuration(typeof(T), startMessage, completeMessage); } + /// + /// Adds a start and end log entry as Info and tracks how long it takes until disposed. + /// + /// + /// + /// + /// public static DisposableTimer TraceDuration(Type loggerType, string startMessage, string completeMessage) { LogHelper.Info(loggerType, () => startMessage); return new DisposableTimer(x => LogHelper.Info(loggerType, () => completeMessage + " (took " + x + "ms)")); } + /// + /// Adds a start and end log entry as Debug and tracks how long it takes until disposed. + /// + /// + /// + /// + /// + public static DisposableTimer DebugDuration(string startMessage, string completeMessage) + { + return DebugDuration(typeof(T), startMessage, completeMessage); + } + + /// + /// Adds a start and end log entry as Debug and tracks how long it takes until disposed. + /// + /// + /// + /// + /// + public static DisposableTimer DebugDuration(Type loggerType, string startMessage, string completeMessage) + { + LogHelper.Info(loggerType, () => startMessage); + return new DisposableTimer(x => LogHelper.Debug(loggerType, () => completeMessage + " (took " + x + "ms)")); + } + /// /// Handles the disposal of resources. Derived from abstract class which handles common required locking logic. /// diff --git a/src/Umbraco.Core/PluginTypeResolver.cs b/src/Umbraco.Core/PluginTypeResolver.cs index 62e548d901..f2e5994f57 100644 --- a/src/Umbraco.Core/PluginTypeResolver.cs +++ b/src/Umbraco.Core/PluginTypeResolver.cs @@ -85,7 +85,7 @@ namespace Umbraco.Core internal IEnumerable CreateInstances(IEnumerable types, bool throwException = false) { var typesAsArray = types.ToArray(); - using (DisposableTimer.TraceDuration( + using (DisposableTimer.DebugDuration( string.Format("Starting instantiation of {0} objects of type {1}", typesAsArray.Length, typeof(T).FullName), string.Format("Completed instantiation of {0} objects of type {1}", typesAsArray.Length, typeof(T).FullName))) { diff --git a/src/Umbraco.Core/Resolving/MultipleResolverBase.cs b/src/Umbraco.Core/Resolving/ManyObjectResolverBase.cs similarity index 81% rename from src/Umbraco.Core/Resolving/MultipleResolverBase.cs rename to src/Umbraco.Core/Resolving/ManyObjectResolverBase.cs index 01e4c861f8..6aac79de18 100644 --- a/src/Umbraco.Core/Resolving/MultipleResolverBase.cs +++ b/src/Umbraco.Core/Resolving/ManyObjectResolverBase.cs @@ -15,7 +15,7 @@ namespace Umbraco.Core.Resolving /// Used to resolve multiple types from a collection. The collection can also be modified at runtime/application startup. /// An example of this is MVCs ViewEngines collection. /// - internal abstract class MultipleResolverBase : ResolverBase + internal abstract class ManyObjectResolverBase : ResolverBase where TResolver : class where TResolved : class { @@ -23,18 +23,18 @@ namespace Umbraco.Core.Resolving protected readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim(); /// - /// Initializes a new instance of the class with an empty list of objects. + /// Initializes a new instance of the class with an empty list of objects. /// - protected MultipleResolverBase() + protected ManyObjectResolverBase() { _resolved = new List(); } /// - /// Initializes a new instance of the class with an initial list of objects. + /// Initializes a new instance of the class with an initial list of objects. /// /// The list of objects. - protected MultipleResolverBase(IEnumerable value) + protected ManyObjectResolverBase(IEnumerable value) { _resolved = new List(value); } diff --git a/src/Umbraco.Core/Resolving/ManyWeightedResolved.cs b/src/Umbraco.Core/Resolving/ManyWeightedResolved.cs index 683e9dce99..14161df9ec 100644 --- a/src/Umbraco.Core/Resolving/ManyWeightedResolved.cs +++ b/src/Umbraco.Core/Resolving/ManyWeightedResolved.cs @@ -5,155 +5,155 @@ using System.Text; namespace Umbraco.Core.Resolving { - internal class ManyWeightedResolved - { - List _resolved = new List(); - Dictionary _weights = new Dictionary(); + //internal class ManyWeightedResolved + //{ + // List _resolved = new List(); + // Dictionary _weights = new Dictionary(); - public ManyWeightedResolved() - { - Resolution.Frozen += (sender, e) => - { - _resolved = _resolved.OrderBy(r => _weights[r.GetType()]).ToList(); - _weights = null; - }; - } + // public ManyWeightedResolved() + // { + // Resolution.Frozen += (sender, e) => + // { + // _resolved = _resolved.OrderBy(r => _weights[r.GetType()]).ToList(); + // _weights = null; + // }; + // } - public ManyWeightedResolved(IEnumerable resolved) - : this() - { - _resolved.AddRange(resolved); - foreach (var type in _resolved.Select(r => r.GetType())) - _weights.Add(type, ResolutionWeightAttribute.ReadWeight(type)); - } + // public ManyWeightedResolved(IEnumerable resolved) + // : this() + // { + // _resolved.AddRange(resolved); + // foreach (var type in _resolved.Select(r => r.GetType())) + // _weights.Add(type, ResolutionWeightAttribute.ReadWeight(type)); + // } - public IEnumerable Values - { - get { return _resolved; } - } + // public IEnumerable Values + // { + // get { return _resolved; } + // } - #region Manage collection + // #region Manage collection - public void Add(TResolved value) - { - Resolution.EnsureNotFrozen(); + // public void Add(TResolved value) + // { + // Resolution.EnsureNotFrozen(); - var type = value.GetType(); - EnsureNotExists(type); - _resolved.Add(value); - _weights[type] = ResolutionWeightAttribute.ReadWeight(type); - } + // var type = value.GetType(); + // EnsureNotExists(type); + // _resolved.Add(value); + // _weights[type] = ResolutionWeightAttribute.ReadWeight(type); + // } - public void Add(TResolved value, int weight) - { - Resolution.EnsureNotFrozen(); + // public void Add(TResolved value, int weight) + // { + // Resolution.EnsureNotFrozen(); - var type = value.GetType(); - EnsureNotExists(type); - _resolved.Add(value); - _weights[type] = weight; - } + // var type = value.GetType(); + // EnsureNotExists(type); + // _resolved.Add(value); + // _weights[type] = weight; + // } - public void AddRange(IEnumerable values) - { - Resolution.EnsureNotFrozen(); + // public void AddRange(IEnumerable values) + // { + // Resolution.EnsureNotFrozen(); - foreach (var value in values) - { - var type = value.GetType(); - EnsureNotExists(type); - _resolved.Add(value); - _weights[type] = ResolutionWeightAttribute.ReadWeight(type); - } - } + // foreach (var value in values) + // { + // var type = value.GetType(); + // EnsureNotExists(type); + // _resolved.Add(value); + // _weights[type] = ResolutionWeightAttribute.ReadWeight(type); + // } + // } - //public void SetWeight(TResolved value, int weight) - //{ - // Resolution.EnsureNotFrozen(); + // //public void SetWeight(TResolved value, int weight) + // //{ + // // Resolution.EnsureNotFrozen(); - // var type = value.GetType(); - // EnsureExists(type); - // _weights[type] = weight; - //} + // // var type = value.GetType(); + // // EnsureExists(type); + // // _weights[type] = weight; + // //} - public void SetWeight(int weight) - { - Resolution.EnsureNotFrozen(); + // public void SetWeight(int weight) + // { + // Resolution.EnsureNotFrozen(); - var type = typeof(TResolving); - EnsureExists(type); - _weights[type] = weight; - } + // var type = typeof(TResolving); + // EnsureExists(type); + // _weights[type] = weight; + // } - //public int GetWeight(TResolved value) - //{ - // var type = value.GetType(); - // EnsureExists(type); - // return _weights[value.GetType()]; - //} + // //public int GetWeight(TResolved value) + // //{ + // // var type = value.GetType(); + // // EnsureExists(type); + // // return _weights[value.GetType()]; + // //} - public int GetWeight() - { - var type = typeof(TResolving); - EnsureExists(type); - return _weights[type]; - } + // public int GetWeight() + // { + // var type = typeof(TResolving); + // EnsureExists(type); + // return _weights[type]; + // } - //public void Remove(TResolved value) - //{ - // Resolution.EnsureNotFrozen(); + // //public void Remove(TResolved value) + // //{ + // // Resolution.EnsureNotFrozen(); - // var type = value.GetType(); - // var remove = _resolved.SingleOrDefault(r => r.GetType() == type); - // if (remove != null) - // { - // _resolved.Remove(remove); - // _weights.Remove(remove.GetType()); - // } - //} + // // var type = value.GetType(); + // // var remove = _resolved.SingleOrDefault(r => r.GetType() == type); + // // if (remove != null) + // // { + // // _resolved.Remove(remove); + // // _weights.Remove(remove.GetType()); + // // } + // //} - public void Remove() - { - Resolution.EnsureNotFrozen(); + // public void Remove() + // { + // Resolution.EnsureNotFrozen(); - var type = typeof(TResolving); - var remove = _resolved.SingleOrDefault(r => r.GetType() == type); - if (remove != null) - { - _resolved.Remove(remove); - _weights.Remove(remove.GetType()); - } - } + // var type = typeof(TResolving); + // var remove = _resolved.SingleOrDefault(r => r.GetType() == type); + // if (remove != null) + // { + // _resolved.Remove(remove); + // _weights.Remove(remove.GetType()); + // } + // } - public void Clear() - { - Resolution.EnsureNotFrozen(); + // public void Clear() + // { + // Resolution.EnsureNotFrozen(); - _resolved = new List(); - _weights = new Dictionary(); - } + // _resolved = new List(); + // _weights = new Dictionary(); + // } - #endregion + // #endregion - #region Utilities + // #region Utilities - public bool Exists(Type type) - { - return _resolved.Any(r => r.GetType() == type); - } + // public bool Exists(Type type) + // { + // return _resolved.Any(r => r.GetType() == type); + // } - void EnsureExists(Type type) - { - if (!Exists(type)) - throw new InvalidOperationException("There is not value of that type in the collection."); - } + // void EnsureExists(Type type) + // { + // if (!Exists(type)) + // throw new InvalidOperationException("There is not value of that type in the collection."); + // } - void EnsureNotExists(Type type) - { - if (Exists(type)) - throw new InvalidOperationException("A value of that type already exists in the collection."); - } + // void EnsureNotExists(Type type) + // { + // if (Exists(type)) + // throw new InvalidOperationException("A value of that type already exists in the collection."); + // } - #endregion - } + // #endregion + //} } diff --git a/src/Umbraco.Core/Resolving/ManyWeightedResolverBase.cs b/src/Umbraco.Core/Resolving/ManyWeightedResolverBase.cs index 44b313aec6..061f8423b2 100644 --- a/src/Umbraco.Core/Resolving/ManyWeightedResolverBase.cs +++ b/src/Umbraco.Core/Resolving/ManyWeightedResolverBase.cs @@ -5,63 +5,63 @@ using System.Text; namespace Umbraco.Core.Resolving { - internal abstract class ManyWeightedResolverBase : ResolverBase - where TResolver : class - { - readonly ManyWeightedResolved _resolved; + //internal abstract class ManyWeightedResolverBase : ResolverBase + // where TResolver : class + //{ + // readonly ManyWeightedResolved _resolved; - protected ManyWeightedResolverBase() - { - _resolved = new ManyWeightedResolved(); - } + // protected ManyWeightedResolverBase() + // { + // _resolved = new ManyWeightedResolved(); + // } - protected ManyWeightedResolverBase(IEnumerable values) - { - _resolved = new ManyWeightedResolved(values); - } + // protected ManyWeightedResolverBase(IEnumerable values) + // { + // _resolved = new ManyWeightedResolved(values); + // } - protected IEnumerable Values - { - get { return _resolved.Values; } - } + // protected IEnumerable Values + // { + // get { return _resolved.Values; } + // } - #region Manage collection + // #region Manage collection - public void Add(TResolved value) - { - _resolved.Add(value); - } + // public void Add(TResolved value) + // { + // _resolved.Add(value); + // } - public void Add(TResolved value, int weight) - { - _resolved.Add(value, weight); - } + // public void Add(TResolved value, int weight) + // { + // _resolved.Add(value, weight); + // } - public void AddRange(IEnumerable values) - { - _resolved.AddRange(values); - } + // public void AddRange(IEnumerable values) + // { + // _resolved.AddRange(values); + // } - public void SetWeight(int weight) - { - _resolved.SetWeight(weight); - } + // public void SetWeight(int weight) + // { + // _resolved.SetWeight(weight); + // } - public int GetWeight() - { - return _resolved.GetWeight(); - } + // public int GetWeight() + // { + // return _resolved.GetWeight(); + // } - public void Remove() - { - _resolved.Remove(); - } + // public void Remove() + // { + // _resolved.Remove(); + // } - public void Clear() - { - _resolved.Clear(); - } + // public void Clear() + // { + // _resolved.Clear(); + // } - #endregion - } + // #endregion + //} } diff --git a/src/Umbraco.Core/Resolving/ResolutionWeightAttribute.cs b/src/Umbraco.Core/Resolving/ResolutionWeightAttribute.cs index 6cd11ec910..c888798513 100644 --- a/src/Umbraco.Core/Resolving/ResolutionWeightAttribute.cs +++ b/src/Umbraco.Core/Resolving/ResolutionWeightAttribute.cs @@ -5,22 +5,22 @@ using System.Text; namespace Umbraco.Core.Resolving { - [AttributeUsage(AttributeTargets.Class, AllowMultiple=false, Inherited=false)] - internal class ResolutionWeightAttribute : Attribute - { - public const int DefaultWeight = 100; + //[AttributeUsage(AttributeTargets.Class, AllowMultiple=false, Inherited=false)] + //internal class ResolutionWeightAttribute : Attribute + //{ + // public const int DefaultWeight = 100; - public ResolutionWeightAttribute(int weight) - { - this.Weight = weight; - } + // public ResolutionWeightAttribute(int weight) + // { + // this.Weight = weight; + // } - public int Weight { get; private set; } + // public int Weight { get; private set; } - public static int ReadWeight(Type type) - { - var attr = type.GetCustomAttribute(false); - return attr != null ? attr.Weight : DefaultWeight; - } - } + // public static int ReadWeight(Type type) + // { + // var attr = type.GetCustomAttribute(false); + // return attr != null ? attr.Weight : DefaultWeight; + // } + //} } diff --git a/src/Umbraco.Core/Resolving/ResolverBase.cs b/src/Umbraco.Core/Resolving/ResolverBase.cs index 2d27ac1f21..56c0bcd735 100644 --- a/src/Umbraco.Core/Resolving/ResolverBase.cs +++ b/src/Umbraco.Core/Resolving/ResolverBase.cs @@ -7,9 +7,36 @@ namespace Umbraco.Core.Resolving /// base class for resolvers which declare a singleton accessor /// /// - internal abstract class ResolverBase - where TResolver : class + internal abstract class ResolverBase where TResolver : class { - public static TResolver Current { get; set; } + static TResolver _resolver; + + //TODO: This is not correct, this will be the same lock for all ResolverBase classes!! + static readonly ReaderWriterLockSlim ResolversLock = new ReaderWriterLockSlim(); + + public static TResolver Current + { + get + { + using (new ReadLock(ResolversLock)) + { + if (_resolver == null) + throw new InvalidOperationException("Current has not been initialized. You must initialize Current before trying to read it."); + return _resolver; + } + } + + set + { + using (new WriteLock(ResolversLock)) + { + if (value == null) + throw new ArgumentNullException("value"); + if (_resolver != null) + throw new InvalidOperationException("Current has already been initialized. It is not possible to re-initialize Current once it has been initialized."); + _resolver = value; + } + } + } } } diff --git a/src/Umbraco.Core/Resolving/SingleResolverBase.cs b/src/Umbraco.Core/Resolving/SingleObjectResolverBase.cs similarity index 72% rename from src/Umbraco.Core/Resolving/SingleResolverBase.cs rename to src/Umbraco.Core/Resolving/SingleObjectResolverBase.cs index fb5475dd3c..a77d98f680 100644 --- a/src/Umbraco.Core/Resolving/SingleResolverBase.cs +++ b/src/Umbraco.Core/Resolving/SingleObjectResolverBase.cs @@ -11,29 +11,29 @@ namespace Umbraco.Core.Resolving /// Used for 'singly' registered objects. An example is like the MVC Controller Factory, only one exists application wide and it can /// be get/set. /// - internal abstract class SingleResolverBase : ResolverBase + internal abstract class SingleObjectResolverBase : ResolverBase where TResolver : class where TResolved : class { TResolved _resolved; readonly bool _canBeNull; - protected SingleResolverBase() + protected SingleObjectResolverBase() : this(false) { } - protected SingleResolverBase(TResolved value) + protected SingleObjectResolverBase(TResolved value) : this(false) { _resolved = value; } - protected SingleResolverBase(bool canBeNull) + protected SingleObjectResolverBase(bool canBeNull) { _canBeNull = canBeNull; } - protected SingleResolverBase(TResolved value, bool canBeNull) + protected SingleObjectResolverBase(TResolved value, bool canBeNull) { _resolved = value; _canBeNull = canBeNull; diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index ee19019c11..a972e320fe 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -66,12 +66,12 @@ - + - + diff --git a/src/Umbraco.Web/ContentStore.cs b/src/Umbraco.Web/ContentStore.cs index e13714c9ac..45017bc842 100644 --- a/src/Umbraco.Web/ContentStore.cs +++ b/src/Umbraco.Web/ContentStore.cs @@ -120,7 +120,7 @@ namespace Umbraco.Web { // if not in a domain - what is the default page? // let's say it is the first one in the tree, if any - xpath = "/root/*[@isDoc and first]"; + xpath = "(/root/*[@isDoc])[1]"; } } else diff --git a/src/Umbraco.Web/PluginTypeResolverExtensions.cs b/src/Umbraco.Web/PluginTypeResolverExtensions.cs index fa8ac22980..3c02fdfeb5 100644 --- a/src/Umbraco.Web/PluginTypeResolverExtensions.cs +++ b/src/Umbraco.Web/PluginTypeResolverExtensions.cs @@ -13,15 +13,15 @@ namespace Umbraco.Web public static class PluginTypeResolverExtensions { - /// - /// Returns all IDocumentLookup types - /// - /// - /// - internal static IEnumerable ResolveLookups(this PluginTypeResolver resolver) - { - return resolver.ResolveTypes(); - } + ///// + ///// Returns all IDocumentLookup types + ///// + ///// + ///// + //internal static IEnumerable ResolveLookups(this PluginTypeResolver resolver) + //{ + // return resolver.ResolveTypes(); + //} } } \ No newline at end of file diff --git a/src/Umbraco.Web/Routing/DocumentLookupsResolver.cs b/src/Umbraco.Web/Routing/DocumentLookupsResolver.cs index 8f40b4a98c..908edcfadd 100644 --- a/src/Umbraco.Web/Routing/DocumentLookupsResolver.cs +++ b/src/Umbraco.Web/Routing/DocumentLookupsResolver.cs @@ -9,63 +9,109 @@ using Umbraco.Core.Resolving; namespace Umbraco.Web.Routing { - /// - /// Resolves the implementations and the implementation. - /// - class DocumentLookupsResolver : ResolverBase + class LastChanceLookupResolver : SingleObjectResolverBase { - /// - /// Initializes a new instance of the class with an enumeration of an an . - /// - /// The document lookups. - /// The document last chance lookup. - internal DocumentLookupsResolver(IEnumerable lookupTypes, IDocumentLastChanceLookup lastChanceLookup) + public LastChanceLookupResolver(IDocumentLastChanceLookup lastChanceLookup) { - //TODO: I've changed this to resolve types but the intances are not created yet! - // I've created a method on the PluginTypeResolver to create types: PluginTypesResolver.Current.FindAndCreateInstances() - - - _lookupTypes.AddRange(lookupTypes); - _lastChanceLookup.Value = lastChanceLookup; + Value = lastChanceLookup; } - #region LastChanceLookup - - readonly SingleResolved _lastChanceLookup = new SingleResolved(true); - /// - /// Gets or sets the implementation. + /// Returns the Last Chance Lookup /// - public IDocumentLastChanceLookup DocumentLastChanceLookup + public IDocumentLastChanceLookup LastChanceLookup { - get { return _lastChanceLookup.Value; } - set { _lastChanceLookup.Value = value; } + get { return Value; } } + } - #endregion + class DocumentLookupsResolver2 : ManyObjectResolverBase + { + private readonly LastChanceLookupResolver _lastChanceLookupResolver; - #region Lookups - - private readonly List _lookupTypes = new List(); - readonly ManyWeightedResolved _lookups = new ManyWeightedResolved(); + internal DocumentLookupsResolver2(IEnumerable lookups, LastChanceLookupResolver lastChanceLookupResolver) + { + _lastChanceLookupResolver = lastChanceLookupResolver; + foreach (var l in lookups) + { + this.Add(l); + } + } /// /// Gets the implementations. /// public IEnumerable DocumentLookups { - get { return _lookups.Values; } + get { return Values; } } - //why do we have this? /// - /// Gets the inner resolution. + /// Gets or sets the implementation. /// - public ManyWeightedResolved DocumentLookupsResolution + public IDocumentLastChanceLookup DocumentLastChanceLookup { - get { return _lookups; } + get { return _lastChanceLookupResolver.LastChanceLookup; } } - - #endregion } + + ///// + ///// Resolves the implementations and the implementation. + ///// + //class DocumentLookupsResolver : ResolverBase + //{ + // /// + // /// Initializes a new instance of the class with an enumeration of an an . + // /// + // /// The document lookups. + // /// The document last chance lookup. + // internal DocumentLookupsResolver(IEnumerable lookupTypes, IDocumentLastChanceLookup lastChanceLookup) + // { + // //TODO: I've changed this to resolve types but the intances are not created yet! + // // I've created a method on the PluginTypeResolver to create types: PluginTypesResolver.Current.FindAndCreateInstances() + + + // _lookupTypes.AddRange(lookupTypes); + // _lastChanceLookup.Value = lastChanceLookup; + // } + + // #region LastChanceLookup + + // readonly SingleResolved _lastChanceLookup = new SingleResolved(true); + + // /// + // /// Gets or sets the implementation. + // /// + // public IDocumentLastChanceLookup DocumentLastChanceLookup + // { + // get { return _lastChanceLookup.Value; } + // set { _lastChanceLookup.Value = value; } + // } + + // #endregion + + // #region Lookups + + // private readonly List _lookupTypes = new List(); + // readonly ManyWeightedResolved _lookups = new ManyWeightedResolved(); + + // /// + // /// Gets the implementations. + // /// + // public IEnumerable DocumentLookups + // { + // get { return _lookups.Values; } + // } + + // //why do we have this? + // /// + // /// Gets the inner resolution. + // /// + // public ManyWeightedResolved DocumentLookupsResolution + // { + // get { return _lookups; } + // } + + // #endregion + //} } diff --git a/src/Umbraco.Web/Routing/DocumentRequest.cs b/src/Umbraco.Web/Routing/DocumentRequest.cs index bc25fc0fbc..95f76615b2 100644 --- a/src/Umbraco.Web/Routing/DocumentRequest.cs +++ b/src/Umbraco.Web/Routing/DocumentRequest.cs @@ -6,6 +6,8 @@ using System.Globalization; using System.Diagnostics; // legacy +using Umbraco.Core; +using Umbraco.Core.Logging; using umbraco.BusinessLogic; using umbraco.cms.businesslogic.web; using umbraco.cms.businesslogic.template; @@ -16,9 +18,8 @@ namespace Umbraco.Web.Routing // represents a request for one specified Umbraco document to be rendered // by one specified template, using one particular culture. // - internal class DocumentRequest + public class DocumentRequest { - static readonly TraceSource Trace = new TraceSource("DocumentRequest"); public DocumentRequest(Uri uri, RoutingContext routingContext) { @@ -152,7 +153,7 @@ namespace Umbraco.Web.Routing // note - we are not handling schemes nor ports here. - Trace.TraceInformation("{0}Uri=\"{1}\"", tracePrefix, this.Uri); + LogHelper.Debug("{0}Uri=\"{1}\"", () => tracePrefix, () => this.Uri); // try to find a domain matching the current request var domainAndUri = Domains.DomainMatch(Domain.GetDomains(), RoutingContext.UmbracoContext.UmbracoUrl, false); @@ -161,9 +162,11 @@ namespace Umbraco.Web.Routing if (domainAndUri != null) { // matching an existing domain - Trace.TraceInformation("{0}Matches domain=\"{1}\", rootId={2}, culture=\"{3}\"", - tracePrefix, - domainAndUri.Domain.Name, domainAndUri.Domain.RootNodeId, domainAndUri.Domain.Language.CultureAlias); + LogHelper.Debug("{0}Matches domain=\"{1}\", rootId={2}, culture=\"{3}\"", + () => tracePrefix, + () => domainAndUri.Domain.Name, + () => domainAndUri.Domain.RootNodeId, + () => domainAndUri.Domain.Language.CultureAlias); this.Domain = domainAndUri.Domain; this.DomainUri = domainAndUri.Uri; @@ -179,13 +182,14 @@ namespace Umbraco.Web.Routing else { // not matching any existing domain - Trace.TraceInformation("{0}Matches no domain", tracePrefix); + LogHelper.Debug("{0}Matches no domain", () => tracePrefix); var defaultLanguage = Language.GetAllAsList().FirstOrDefault(); this.Culture = defaultLanguage == null ? CultureInfo.CurrentUICulture : new CultureInfo(defaultLanguage.CultureAlias); } - Trace.TraceInformation("{0}Culture=\"{1}\"", tracePrefix, this.Culture.Name); + LogHelper.Debug("{0}Culture=\"{1}\"", () => tracePrefix, () => this.Culture.Name); + return this.Domain != null; } @@ -196,15 +200,19 @@ namespace Umbraco.Web.Routing public bool LookupDocument() { const string tracePrefix = "LookupDocument: "; - Trace.TraceInformation("{0}Path=\"{1}\"", tracePrefix, this.Uri.AbsolutePath); + LogHelper.Debug("{0}Path=\"{1}\"", () => tracePrefix, () => this.Uri.AbsolutePath); // look for the document // the first successful resolver, if any, will set this.Node, and may also set this.Template // some lookups may implement caching - Trace.TraceInformation("{0}Begin resolvers", tracePrefix); - var lookups = RoutingContext.DocumentLookupsResolver.DocumentLookups; - lookups.Any(lookup => lookup.TrySetDocument(this)); - Trace.TraceInformation("{0}End resolvers, {1}", tracePrefix, (this.HasNode ? "a document was found" : "no document was found")); + + using (DisposableTimer.DebugDuration( + string.Format("{0}Begin resolvers", tracePrefix), + string.Format("{0}End resolvers, {1}", tracePrefix, (this.HasNode ? "a document was found" : "no document was found")))) + { + var lookups = RoutingContext.DocumentLookupsResolver.DocumentLookups; + lookups.Any(lookup => lookup.TrySetDocument(this)); + } // fixme - not handling umbracoRedirect // should come after internal redirects @@ -234,24 +242,24 @@ namespace Umbraco.Web.Routing const int maxLoop = 12; do { - Trace.TraceInformation("{0}{1}", tracePrefix, (i == 0 ? "Begin" : "Loop")); + LogHelper.Debug("{0}{1}", () => tracePrefix, () => (i == 0 ? "Begin" : "Loop")); // handle not found if (!this.HasNode) { this.Is404 = true; - Trace.TraceInformation("{0}No document, try last chance lookup", tracePrefix); + LogHelper.Debug("{0}No document, try last chance lookup", () => tracePrefix); // if it fails then give up, there isn't much more that we can do var lastChance = RoutingContext.DocumentLookupsResolver.DocumentLastChanceLookup; if (lastChance == null || !lastChance.TrySetDocument(this)) { - Trace.TraceInformation("{0}Failed to find a document, give up", tracePrefix); + LogHelper.Debug("{0}Failed to find a document, give up", () => tracePrefix); break; } else { - Trace.TraceInformation("{0}Found a document", tracePrefix); + LogHelper.Debug("{0}Found a document", () => tracePrefix); } } @@ -277,10 +285,10 @@ namespace Umbraco.Web.Routing if (i == maxLoop || j == maxLoop) { - Trace.TraceInformation("{0}Looks like we're running into an infinite loop, abort", tracePrefix); + LogHelper.Debug("{0}Looks like we're running into an infinite loop, abort", () => tracePrefix); this.Node = null; } - Trace.TraceInformation("{0}End", tracePrefix); + LogHelper.Debug("{0}End", () => tracePrefix); } /// @@ -300,7 +308,7 @@ namespace Umbraco.Web.Routing if (!string.IsNullOrWhiteSpace(internalRedirect)) { - Trace.TraceInformation("{0}Found umbracoInternalRedirectId={1}", tracePrefix, internalRedirect); + LogHelper.Debug("{0}Found umbracoInternalRedirectId={1}", () => tracePrefix, () => internalRedirect); int internalRedirectId; if (!int.TryParse(internalRedirect, out internalRedirectId)) @@ -310,12 +318,12 @@ namespace Umbraco.Web.Routing { // bad redirect this.Node = null; - Trace.TraceInformation("{0}Failed to redirect to id={1}: invalid value", tracePrefix, internalRedirect); + LogHelper.Debug("{0}Failed to redirect to id={1}: invalid value", () => tracePrefix, () => internalRedirect); } else if (internalRedirectId == this.NodeId) { // redirect to self - Trace.TraceInformation("{0}Redirecting to self, ignore", tracePrefix); + LogHelper.Debug("{0}Redirecting to self, ignore", () => tracePrefix); } else { @@ -325,11 +333,11 @@ namespace Umbraco.Web.Routing if (node != null) { redirect = true; - Trace.TraceInformation("{0}Redirecting to id={1}", tracePrefix, internalRedirectId); + LogHelper.Debug("{0}Redirecting to id={1}", () => tracePrefix, () => internalRedirectId); } else { - Trace.TraceInformation("{0}Failed to redirect to id={1}: no such published document", tracePrefix, internalRedirectId); + LogHelper.Debug("{0}Failed to redirect to id={1}: no such published document", () => tracePrefix, () => internalRedirectId); } } } @@ -352,32 +360,32 @@ namespace Umbraco.Web.Routing if (Access.IsProtected(this.NodeId, path)) { - Trace.TraceInformation("{0}Page is protected, check for access", tracePrefix); + LogHelper.Debug("{0}Page is protected, check for access", () => tracePrefix); var user = System.Web.Security.Membership.GetUser(); if (user == null || !Member.IsLoggedOn()) { - Trace.TraceInformation("{0}Not logged in, redirect to login page", tracePrefix); + LogHelper.Debug("{0}Not logged in, redirect to login page", () => tracePrefix); var loginPageId = Access.GetLoginPage(path); if (loginPageId != this.NodeId) this.Node = RoutingContext.ContentStore.GetNodeById(loginPageId); } else if (!Access.HasAccces(this.NodeId, user.ProviderUserKey)) { - Trace.TraceInformation("{0}Current member has not access, redirect to error page", tracePrefix); + LogHelper.Debug("{0}Current member has not access, redirect to error page", () => tracePrefix); var errorPageId = Access.GetErrorPage(path); if (errorPageId != this.NodeId) this.Node = RoutingContext.ContentStore.GetNodeById(errorPageId); } else { - Trace.TraceInformation("{0}Current member has access", tracePrefix); + LogHelper.Debug("{0}Current member has access", () => tracePrefix); } } else { - Trace.TraceInformation("{0}Page is not protected", tracePrefix); + LogHelper.Debug("{0}Page is not protected", () => tracePrefix); } } @@ -402,7 +410,7 @@ namespace Umbraco.Web.Routing if (string.IsNullOrWhiteSpace(templateAlias)) { templateAlias = RoutingContext.ContentStore.GetNodeProperty(this.Node, "@template"); - Trace.TraceInformation("{0}Look for template id={1}", tracePrefix, templateAlias); + LogHelper.Debug("{0}Look for template id={1}", () => tracePrefix, () => templateAlias); int templateId; if (!int.TryParse(templateAlias, out templateId)) templateId = 0; @@ -410,19 +418,19 @@ namespace Umbraco.Web.Routing } else { - Trace.TraceInformation("{0}Look for template alias=\"{1}\" (altTemplate)", tracePrefix, templateAlias); + LogHelper.Debug("{0}Look for template alias=\"{1}\" (altTemplate)", () => tracePrefix, () => templateAlias); this.Template = Template.GetByAlias(templateAlias); } if (!this.HasTemplate) { - Trace.TraceInformation("{0}No template was found", tracePrefix); + LogHelper.Debug("{0}No template was found", () => tracePrefix); //TODO: I like the idea of this new setting, but lets get this in to the core at a later time, for now lets just get the basics working. //if (Settings.HandleMissingTemplateAs404) //{ // this.Node = null; - // Trace.TraceInformation("{0}Assume page not found (404)", tracePrefix); + // LogHelper.Debug("{0}Assume page not found (404)", tracePrefix); //} // else we have no template @@ -430,7 +438,7 @@ namespace Umbraco.Web.Routing } else { - Trace.TraceInformation("{0}Found", tracePrefix); + LogHelper.Debug("{0}Found", () => tracePrefix); } } } diff --git a/src/Umbraco.Web/Routing/IDocumentLookup.cs b/src/Umbraco.Web/Routing/IDocumentLookup.cs index 17d74098ee..b84d9b37e2 100644 --- a/src/Umbraco.Web/Routing/IDocumentLookup.cs +++ b/src/Umbraco.Web/Routing/IDocumentLookup.cs @@ -3,7 +3,7 @@ namespace Umbraco.Web.Routing /// /// Provides a method to try to find an assign an Umbraco document to a DocumentRequest. /// - internal interface IDocumentLookup + public interface IDocumentLookup { /// /// Tries to find and assign an Umbraco document to a DocumentRequest. diff --git a/src/Umbraco.Web/Routing/LookupByAlias.cs b/src/Umbraco.Web/Routing/LookupByAlias.cs index 81147be85c..2d2e77ce8e 100644 --- a/src/Umbraco.Web/Routing/LookupByAlias.cs +++ b/src/Umbraco.Web/Routing/LookupByAlias.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Xml; +using Umbraco.Core.Logging; using Umbraco.Core.Resolving; namespace Umbraco.Web.Routing @@ -11,15 +12,14 @@ namespace Umbraco.Web.Routing /// Handles /just/about/anything where /just/about/anything is contained in the umbracoUrlAlias property of a document. /// The alias is the full path to the document. There can be more than one alias, separated by commas. /// - [ResolutionWeight(50)] + //[ResolutionWeight(50)] internal class LookupByAlias : IDocumentLookup { - static readonly TraceSource Trace = new TraceSource("LookupByAlias"); /// /// Tries to find and assign an Umbraco document to a DocumentRequest. /// - /// The DocumentRequest. + /// The DocumentRequest. /// A value indicating whether an Umbraco document was found and assigned. public bool TrySetDocument(DocumentRequest docreq) { @@ -30,13 +30,13 @@ namespace Umbraco.Web.Routing node = docreq.RoutingContext.ContentStore.GetNodeByUrlAlias(docreq.HasDomain ? docreq.Domain.RootNodeId : 0, docreq.Uri.AbsolutePath); if (node != null) { - Trace.TraceInformation("Path \"{0}\" is an alias for id={1}", docreq.Uri.AbsolutePath, docreq.NodeId); + LogHelper.Debug("Path \"{0}\" is an alias for id={1}", () => docreq.Uri.AbsolutePath, () => docreq.NodeId); docreq.Node = node; } } if (node == null) - Trace.TraceInformation("Not an alias"); + LogHelper.Debug("Not an alias"); return node != null; } diff --git a/src/Umbraco.Web/Routing/LookupById.cs b/src/Umbraco.Web/Routing/LookupById.cs index 9418d3843f..038e077cb7 100644 --- a/src/Umbraco.Web/Routing/LookupById.cs +++ b/src/Umbraco.Web/Routing/LookupById.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using System.Xml; +using Umbraco.Core.Logging; using Umbraco.Core.Resolving; namespace Umbraco.Web.Routing @@ -11,15 +12,13 @@ namespace Umbraco.Web.Routing /// /// Handles /1234 where 1234 is the identified of a document. /// - [ResolutionWeight(20)] + //[ResolutionWeight(20)] internal class LookupById : IDocumentLookup { - static readonly TraceSource Trace = new TraceSource("LookupById"); - /// /// Tries to find and assign an Umbraco document to a DocumentRequest. /// - /// The DocumentRequest. + /// The DocumentRequest. /// A value indicating whether an Umbraco document was found and assigned. public bool TrySetDocument(DocumentRequest docreq) { @@ -35,12 +34,12 @@ namespace Umbraco.Web.Routing if (nodeId > 0) { - Trace.TraceInformation("Id={0}", nodeId); + LogHelper.Debug("Id={0}", () => nodeId); node = docreq.RoutingContext.ContentStore.GetNodeById(nodeId); if (node != null) { docreq.Node = node; - Trace.TraceInformation("Found node with id={0}", docreq.NodeId); + LogHelper.Debug("Found node with id={0}", () => docreq.NodeId); } else { @@ -50,7 +49,7 @@ namespace Umbraco.Web.Routing } if (nodeId == -1) - Trace.TraceInformation("Not a node id"); + LogHelper.Debug("Not a node id"); return node != null; } diff --git a/src/Umbraco.Web/Routing/LookupByNiceUrl.cs b/src/Umbraco.Web/Routing/LookupByNiceUrl.cs index 83a089ddad..4bea29e415 100644 --- a/src/Umbraco.Web/Routing/LookupByNiceUrl.cs +++ b/src/Umbraco.Web/Routing/LookupByNiceUrl.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Xml; +using Umbraco.Core.Logging; using Umbraco.Core.Resolving; namespace Umbraco.Web.Routing @@ -10,15 +11,14 @@ namespace Umbraco.Web.Routing /// /// Handles /foo/bar where /foo/bar is the nice url of a document. /// - [ResolutionWeight(10)] + //[ResolutionWeight(10)] internal class LookupByNiceUrl : IDocumentLookup { - static readonly TraceSource Trace = new TraceSource("LookupByNiceUrl"); - + /// /// Tries to find and assign an Umbraco document to a DocumentRequest. /// - /// The DocumentRequest. + /// The DocumentRequest. /// A value indicating whether an Umbraco document was found and assigned. public virtual bool TrySetDocument(DocumentRequest docreq) { @@ -40,7 +40,7 @@ namespace Umbraco.Web.Routing /// The document node, or null. protected XmlNode LookupDocumentNode(DocumentRequest docreq, string route) { - Trace.TraceInformation("Test route \"{0}\"", route); + LogHelper.Debug("Test route \"{0}\"", () => route); //return '0' if in preview mode! var nodeId = !docreq.RoutingContext.UmbracoContext.InPreviewMode @@ -55,7 +55,7 @@ namespace Umbraco.Web.Routing if (node != null) { docreq.Node = node; - Trace.TraceInformation("Cache hit, id={0}", nodeId); + LogHelper.Debug("Cache hit, id={0}", () => nodeId); } else { @@ -65,12 +65,12 @@ namespace Umbraco.Web.Routing if (node == null) { - Trace.TraceInformation("Cache miss, query"); + LogHelper.Debug("Cache miss, query"); node = docreq.RoutingContext.ContentStore.GetNodeByRoute(route); if (node != null) { docreq.Node = node; - Trace.TraceInformation("Query matches, id={0}", docreq.NodeId); + LogHelper.Debug("Query matches, id={0}", () => docreq.NodeId); if (!docreq.RoutingContext.UmbracoContext.InPreviewMode) { @@ -80,7 +80,7 @@ namespace Umbraco.Web.Routing } else { - Trace.TraceInformation("Query does not match"); + LogHelper.Debug("Query does not match"); } } diff --git a/src/Umbraco.Web/Routing/LookupByNiceUrlAndTemplate.cs b/src/Umbraco.Web/Routing/LookupByNiceUrlAndTemplate.cs index 4c02d8793c..58ba1a904b 100644 --- a/src/Umbraco.Web/Routing/LookupByNiceUrlAndTemplate.cs +++ b/src/Umbraco.Web/Routing/LookupByNiceUrlAndTemplate.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Xml; +using Umbraco.Core.Logging; using Umbraco.Core.Resolving; using umbraco.cms.businesslogic.template; @@ -12,15 +13,13 @@ namespace Umbraco.Web.Routing /// Handles /foo/bar/template where /foo/bar is the nice url of a document, and template a template alias. /// If successful, then the template of the document request is also assigned. /// - [ResolutionWeight(30)] + //[ResolutionWeight(30)] internal class LookupByNiceUrlAndTemplate : LookupByNiceUrl, IDocumentLookup { - static readonly TraceSource Trace = new TraceSource("LookupByNiceUrlAndTemplate"); - /// /// Tries to find and assign an Umbraco document to a DocumentRequest. /// - /// The DocumentRequest. + /// The DocumentRequest. /// A value indicating whether an Umbraco document was found and assigned. /// If successful, also assigns the template. public override bool TrySetDocument(DocumentRequest docreq) @@ -39,7 +38,7 @@ namespace Umbraco.Web.Routing var template = Template.GetByAlias(templateAlias); if (template != null) { - Trace.TraceInformation("Valid template: \"{0}\"", templateAlias); + LogHelper.Debug("Valid template: \"{0}\"", () => templateAlias); var route = docreq.HasDomain ? (docreq.Domain.RootNodeId.ToString() + path) : path; node = LookupDocumentNode(docreq, route); @@ -49,12 +48,12 @@ namespace Umbraco.Web.Routing } else { - Trace.TraceInformation("Not a valid template: \"{0}\"", templateAlias); + LogHelper.Debug("Not a valid template: \"{0}\"", () => templateAlias); } } else { - Trace.TraceInformation("No template in path \"/\""); + LogHelper.Debug("No template in path \"/\""); } return node != null; diff --git a/src/Umbraco.Web/Routing/LookupByProfile.cs b/src/Umbraco.Web/Routing/LookupByProfile.cs index 655db393aa..29547b9b97 100644 --- a/src/Umbraco.Web/Routing/LookupByProfile.cs +++ b/src/Umbraco.Web/Routing/LookupByProfile.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Xml; +using Umbraco.Core.Logging; using Umbraco.Core.Resolving; using umbraco; @@ -13,15 +14,14 @@ namespace Umbraco.Web.Routing /// This should rather be done with a rewriting rule. There would be multiple profile pages in multi-sites/multi-langs setups. /// We keep it for backward compatility reasons. /// - [ResolutionWeight(40)] + //[ResolutionWeight(40)] internal class LookupByProfile : LookupByNiceUrl, IDocumentLookup { - static readonly TraceSource Trace = new TraceSource("LookupByProfile"); /// /// Tries to find and assign an Umbraco document to a DocumentRequest. /// - /// The DocumentRequest. + /// The DocumentRequest. /// A value indicating whether an Umbraco document was found and assigned. public override bool TrySetDocument(DocumentRequest docreq) { @@ -37,7 +37,7 @@ namespace Umbraco.Web.Routing if (path == GlobalSettings.ProfileUrl) { isProfile = true; - Trace.TraceInformation("Path \"{0}\" is the profile path", path); + LogHelper.Debug("Path \"{0}\" is the profile path", () => path); var route = docreq.HasDomain ? (docreq.Domain.RootNodeId.ToString() + path) : path; node = LookupDocumentNode(docreq, route); @@ -48,12 +48,16 @@ namespace Umbraco.Web.Routing docreq.RoutingContext.UmbracoContext.HttpContext.Items["umbMemberLogin"] = memberLogin; } else - Trace.TraceInformation("No document matching profile path?"); + { + LogHelper.Debug("No document matching profile path?"); + } } } if (!isProfile) - Trace.TraceInformation("Not the profile path"); + { + LogHelper.Debug("Not the profile path"); + } return node != null; } diff --git a/src/Umbraco.Web/Routing/RoutesCacheResolver.cs b/src/Umbraco.Web/Routing/RoutesCacheResolver.cs index 04a49f4e66..7ef5b11ebc 100644 --- a/src/Umbraco.Web/Routing/RoutesCacheResolver.cs +++ b/src/Umbraco.Web/Routing/RoutesCacheResolver.cs @@ -6,7 +6,7 @@ namespace Umbraco.Web.Routing /// /// Resolves the implementation. /// - class RoutesCacheResolver : SingleResolverBase + class RoutesCacheResolver : SingleObjectResolverBase { /// /// Initializes a new instance of the class with an implementation. diff --git a/src/Umbraco.Web/Routing/RoutingContext.cs b/src/Umbraco.Web/Routing/RoutingContext.cs index dbb3123034..d91568e44d 100644 --- a/src/Umbraco.Web/Routing/RoutingContext.cs +++ b/src/Umbraco.Web/Routing/RoutingContext.cs @@ -7,7 +7,7 @@ namespace Umbraco.Web.Routing /// /// Provides context for the routing of a request. /// - internal class RoutingContext + public class RoutingContext { /// /// Initializes a new instance of the class. @@ -16,9 +16,9 @@ namespace Umbraco.Web.Routing /// The document lookups resolver. /// The content store. /// The nice urls resolver. - public RoutingContext( + internal RoutingContext( UmbracoContext umbracoContext, - DocumentLookupsResolver documentLookupsResolver, + DocumentLookupsResolver2 documentLookupsResolver, ContentStore contentStore, NiceUrlProvider niceUrlResolver) { @@ -36,16 +36,16 @@ namespace Umbraco.Web.Routing /// /// Gets the document lookups resolver. /// - public DocumentLookupsResolver DocumentLookupsResolver { get; private set; } + internal DocumentLookupsResolver2 DocumentLookupsResolver { get; private set; } /// /// Gets the content store. /// - public ContentStore ContentStore { get; private set; } + internal ContentStore ContentStore { get; private set; } /// /// Gets the nice urls provider. /// - public NiceUrlProvider NiceUrlProvider { get; private set; } + internal NiceUrlProvider NiceUrlProvider { get; private set; } } } \ No newline at end of file diff --git a/src/Umbraco.Web/UmbracoApplication.cs b/src/Umbraco.Web/UmbracoApplication.cs index 60169ed3cd..9d9003c1a6 100644 --- a/src/Umbraco.Web/UmbracoApplication.cs +++ b/src/Umbraco.Web/UmbracoApplication.cs @@ -42,10 +42,22 @@ namespace Umbraco.Web //find and initialize the application startup handlers ApplicationStartupHandler.RegisterHandlers(); - // create the resolvers - DocumentLookupsResolver.Current = new DocumentLookupsResolver( - PluginTypeResolver.Current.ResolveLookups(), - new DefaultLastChanceLookup()); + // create the resolvers... + + LastChanceLookupResolver.Current = new LastChanceLookupResolver(new DefaultLastChanceLookup()); + + DocumentLookupsResolver2.Current = new DocumentLookupsResolver2( + //add all known resolvers in the correct order, devs can then modify this list on application startup either by binding to events + //or in their own global.asax + new IDocumentLookup[] + { + new LookupByNiceUrl(), + new LookupById(), + new LookupByNiceUrlAndTemplate(), + new LookupByProfile(), + new LookupByAlias() + }, + LastChanceLookupResolver.Current); RoutesCacheResolver.Current = new RoutesCacheResolver(new DefaultRoutesCache()); OnApplicationStarting(sender, e); diff --git a/src/Umbraco.Web/UmbracoModule.cs b/src/Umbraco.Web/UmbracoModule.cs index 3c3bc1be52..df9fa000a6 100644 --- a/src/Umbraco.Web/UmbracoModule.cs +++ b/src/Umbraco.Web/UmbracoModule.cs @@ -13,119 +13,117 @@ using UmbracoSettings = Umbraco.Core.Configuration.UmbracoSettings; namespace Umbraco.Web { - // also look at IOHelper.ResolveUrlsFromTextString - nightmarish?! + // 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?! + // 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 - { + public class UmbracoModule : IHttpModule + { - /// + /// /// Entry point for a request - /// - /// - void ProcessRequest(HttpContextBase httpContext) - { - LogHelper.Debug("Start processing request"); - + /// + /// + void ProcessRequest(HttpContextBase httpContext) + { + LogHelper.Debug("Start processing request"); + //TODO: We need to ensure the below only executes for real requests (i.e. not css, favicon, etc...) // I'm pretty sure we need to bind to the PostHandlerAssigned (or whatever event) and follow the same // practices that is in umbraMVCo - var uri = httpContext.Request.Url; - var lpath = uri.AbsolutePath.ToLower(); + var uri = httpContext.Request.Url; + var lpath = uri.AbsolutePath.ToLower(); - // add Umbraco's signature header - if (!UmbracoSettings.RemoveUmbracoVersionHeader) - httpContext.Response.AddHeader("X-Umbraco-Version", string.Format("{0}.{1}", GlobalSettings.VersionMajor, GlobalSettings.VersionMinor)); + // add Umbraco's signature header + if (!UmbracoSettings.RemoveUmbracoVersionHeader) + httpContext.Response.AddHeader("X-Umbraco-Version", string.Format("{0}.{1}", GlobalSettings.VersionMajor, GlobalSettings.VersionMinor)); //create the legacy UmbracoContext global::umbraco.presentation.UmbracoContext.Current = new global::umbraco.presentation.UmbracoContext(httpContext); - //create the UmbracoContext singleton, one per request!! - var umbracoContext = new UmbracoContext( - httpContext, + //create the UmbracoContext singleton, one per request!! + var umbracoContext = new UmbracoContext( + httpContext, ApplicationContext.Current, - RoutesCacheResolver.Current.RoutesCache); - UmbracoContext.Current = umbracoContext; + RoutesCacheResolver.Current.RoutesCache); + UmbracoContext.Current = umbracoContext; - // NO! - // these are application-wide singletons! + //create request based objects (one per http request)... + + //create a content store + var contentStore = new ContentStore(umbracoContext); + //create the nice urls + var niceUrls = new NiceUrlProvider(contentStore, umbracoContext); + //create the RoutingContext + var routingContext = new RoutingContext( + umbracoContext, + DocumentLookupsResolver2.Current, + contentStore, + niceUrls); - //create a content store - var contentStore = new ContentStore(umbracoContext); - //create the nice urls - var niceUrls = new NiceUrlProvider(contentStore, umbracoContext); - //create the RoutingContext (one per http request) - var routingContext = new RoutingContext( - umbracoContext, - DocumentLookupsResolver.Current, - contentStore, - niceUrls); - // NOT HERE BUT SEE **THERE** BELOW - // create the new document request which will cleanup the uri once and for all - var docreq = new DocumentRequest(uri, routingContext); + var docreq = new DocumentRequest(uri, routingContext); // initialize the DocumentRequest on the UmbracoContext (this is circular dependency but i think in this case is ok) - umbracoContext.DocumentRequest = docreq; + umbracoContext.DocumentRequest = docreq; - //create the LegacyRequestInitializer (one per http request as it relies on the umbraco context!) - var legacyRequestInitializer = new LegacyRequestInitializer(umbracoContext); + //create the LegacyRequestInitializer (one per http request as it relies on the umbraco context!) + var legacyRequestInitializer = new LegacyRequestInitializer(umbracoContext); - // legacy - initialize legacy stuff - legacyRequestInitializer.InitializeRequest(); + // legacy - initialize legacy stuff + legacyRequestInitializer.InitializeRequest(); - // note - at that point the original legacy module did something do handle IIS custom 404 errors - // ie pages looking like /anything.aspx?404;/path/to/document - I guess the reason was to support - // "directory urls" without having to do wildcard mapping to ASP.NET on old IIS. This is a pain - // to maintain and probably not used anymore - removed as of 06/2012. @zpqrtbnk. - // - // to trigger Umbraco's not-found, one should configure IIS and/or ASP.NET custom 404 errors - // so that they point to a non-existing page eg /redirect-404.aspx + // note - at that point the original legacy module did something do handle IIS custom 404 errors + // ie pages looking like /anything.aspx?404;/path/to/document - I guess the reason was to support + // "directory urls" without having to do wildcard mapping to ASP.NET on old IIS. This is a pain + // to maintain and probably not used anymore - removed as of 06/2012. @zpqrtbnk. + // + // to trigger Umbraco's not-found, one should configure IIS and/or ASP.NET custom 404 errors + // so that they point to a non-existing page eg /redirect-404.aspx - var ok = true; + var ok = true; - // ensure this is a document request - ok = ok && EnsureDocumentRequest(httpContext, uri, lpath); - // ensure Umbraco is ready to serve documents - ok = ok && EnsureIsReady(httpContext, uri); - // ensure Umbraco is properly configured to serve documents - ok = ok && EnsureIsConfigured(httpContext, uri); - ok = ok && (!UmbracoSettings.EnableBaseRestHandler || EnsureNotBaseRestHandler(httpContext, lpath)); - ok = ok && (EnsureNotBaseRestHandler(httpContext, lpath)); + // ensure this is a document request + ok = ok && EnsureDocumentRequest(httpContext, uri, lpath); + // ensure Umbraco is ready to serve documents + ok = ok && EnsureIsReady(httpContext, uri); + // ensure Umbraco is properly configured to serve documents + ok = ok && EnsureIsConfigured(httpContext, uri); + ok = ok && (!UmbracoSettings.EnableBaseRestHandler || EnsureNotBaseRestHandler(httpContext, lpath)); + ok = ok && (EnsureNotBaseRestHandler(httpContext, lpath)); - if (!ok) - { + if (!ok) + { LogHelper.Debug("End processing request, not transfering to handler"); - return; - } + return; + } - // legacy - no idea what this is - LegacyCleanUmbPageFromQueryString(ref uri, ref lpath); + // legacy - no idea what this is + LegacyCleanUmbPageFromQueryString(ref uri, ref lpath); //**THERE** we should create the doc request // before, we're not sure we handling a doc request docreq.LookupDomain(); - if (docreq.IsRedirect) - httpContext.Response.Redirect(docreq.RedirectUrl, true); - Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture = docreq.Culture; - docreq.LookupDocument(); - if (docreq.IsRedirect) - httpContext.Response.Redirect(docreq.RedirectUrl, true); + if (docreq.IsRedirect) + httpContext.Response.Redirect(docreq.RedirectUrl, true); + Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture = docreq.Culture; + docreq.LookupDocument(); + if (docreq.IsRedirect) + httpContext.Response.Redirect(docreq.RedirectUrl, true); - if (docreq.Is404) - httpContext.Response.StatusCode = 404; + if (docreq.Is404) + httpContext.Response.StatusCode = 404; TransferRequest("~/default.aspx" + docreq.Uri.Query); - // it is up to default.aspx to figure out what to display in case - // there is no document (ugly 404 page?) or no template (blank page?) - } + // it is up to default.aspx to figure out what to display in case + // there is no document (ugly 404 page?) or no template (blank page?) + } /// /// Checks if the xml cache file needs to be updated/persisted @@ -142,7 +140,7 @@ namespace Umbraco.Web content.Instance.PersistXmlToFile(); } } - + /// /// Ensures that the request is a document request (i.e. one that the module should handle) /// @@ -150,199 +148,205 @@ namespace Umbraco.Web /// /// /// - bool EnsureDocumentRequest(HttpContextBase httpContext, Uri uri, string lpath) - { - var maybeDoc = true; + bool EnsureDocumentRequest(HttpContextBase httpContext, Uri uri, string lpath) + { + var maybeDoc = true; - // handle directory-urls used for asmx - // legacy - what's the point really? - if (maybeDoc && GlobalSettings.UseDirectoryUrls) - { - int asmxPos = lpath.IndexOf(".asmx/"); - 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 - if (maybeDoc && lpath.Contains('.') && !lpath.EndsWith(".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)) - maybeDoc = false; - - if (!maybeDoc) + // handle directory-urls used for asmx + // legacy - what's the point really? + if (maybeDoc && GlobalSettings.UseDirectoryUrls) { - LogHelper.Warn("Not a document"); - } + int asmxPos = lpath.IndexOf(".asmx/"); + 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; + } + } - return maybeDoc; - } + // a document request should be + // /foo/bar/nil + // /foo/bar/nil/ + // /foo/bar/nil.aspx + // where /foo is not a reserved path - // ensures Umbraco is ready to handle requests - // if not, set status to 503 and transfer request, and return false - // if yes, return true - bool EnsureIsReady(HttpContextBase httpContext, Uri uri) - { - // ensure we are ready - if (!ApplicationContext.Current.IsReady) - { + // if the path contains an extension that is not .aspx + // then it cannot be a document request + if (maybeDoc && lpath.Contains('.') && !lpath.EndsWith(".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)) + maybeDoc = false; + + if (!maybeDoc) + { + LogHelper.Warn("Not a document"); + } + + return maybeDoc; + } + + // ensures Umbraco is ready to handle requests + // if not, set status to 503 and transfer request, and return false + // if yes, return true + bool EnsureIsReady(HttpContextBase httpContext, Uri uri) + { + // ensure we are ready + if (!ApplicationContext.Current.IsReady) + { LogHelper.Warn("Umbraco is not ready"); - - httpContext.Response.StatusCode = 503; + + httpContext.Response.StatusCode = 503; // fixme - default.aspx has to be ready for RequestContext.DocumentRequest==null // fixme - in fact we should transfer to an empty html page... - var bootUrl = UriUtility.ToAbsolute(UmbracoSettings.BootSplashPage); - + var bootUrl = UriUtility.ToAbsolute(UmbracoSettings.BootSplashPage); + if (UmbracoSettings.EnableSplashWhileLoading) // legacy - should go - { + { var configPath = UriUtility.ToAbsolute(SystemDirectories.Config); - bootUrl = string.Format("{0}/splashes/booting.aspx?url={1}", configPath, HttpUtility.UrlEncode(uri.ToString())); - // fixme ?orgurl=... ?retry=... - } - - TransferRequest(bootUrl); - return false; - } + bootUrl = string.Format("{0}/splashes/booting.aspx?url={1}", configPath, HttpUtility.UrlEncode(uri.ToString())); + // fixme ?orgurl=... ?retry=... + } - return true; - } + TransferRequest(bootUrl); + return false; + } - // ensures Umbraco is configured - // if not, redirect to install and return false - // if yes, return true - bool EnsureIsConfigured(HttpContextBase httpContext, Uri uri) - { - if (!ApplicationContext.Current.IsConfigured) - { + return true; + } + + // ensures Umbraco is configured + // if not, redirect to install and return false + // if yes, return true + bool EnsureIsConfigured(HttpContextBase httpContext, Uri uri) + { + if (!ApplicationContext.Current.IsConfigured) + { LogHelper.Warn("Umbraco is not configured"); string installPath = UriUtility.ToAbsolute(SystemDirectories.Install); - string installUrl = string.Format("{0}/default.aspx?redir=true&url={1}", installPath, HttpUtility.UrlEncode(uri.ToString())); - httpContext.Response.Redirect(installUrl, true); - return false; - } - return true; - } + string installUrl = string.Format("{0}/default.aspx?redir=true&url={1}", installPath, HttpUtility.UrlEncode(uri.ToString())); + httpContext.Response.Redirect(installUrl, true); + return false; + } + return true; + } - // checks if the current request is a /base REST handler request - // returns false if it is, otherwise true - bool EnsureNotBaseRestHandler(HttpContextBase httpContext, string lpath) - { - // the /base REST handler still lives in umbraco.dll and has - // not been refactored at the moment. it still is a module, - // although it should be a handler, or it should be replaced - // by clean WebAPI. + // checks if the current request is a /base REST handler request + // returns false if it is, otherwise true + bool EnsureNotBaseRestHandler(HttpContextBase httpContext, string lpath) + { + // the /base REST handler still lives in umbraco.dll and has + // not been refactored at the moment. it still is a module, + // although it should be a handler, or it should be replaced + // by clean WebAPI. - // fixme - do it once when initializing the module - string baseUrl = UriUtility.ToAbsolute(SystemDirectories.Base).ToLower(); - if (!baseUrl.EndsWith("/")) - baseUrl += "/"; - if (lpath.StartsWith(baseUrl)) - { + // fixme - do it once when initializing the module + string baseUrl = UriUtility.ToAbsolute(SystemDirectories.Base).ToLower(); + if (!baseUrl.EndsWith("/")) + baseUrl += "/"; + if (lpath.StartsWith(baseUrl)) + { LogHelper.Debug("Detected /base REST handler"); - return false; - } - return true; - } + return false; + } + return true; + } - // transfers the request using the fastest method available on the server - void TransferRequest(string path) - { + // transfers the request using the fastest method available on the server + void TransferRequest(string path) + { LogHelper.Debug("Transfering to " + path); - var integrated = HttpRuntime.UsingIntegratedPipeline; + var integrated = HttpRuntime.UsingIntegratedPipeline; - // fixme - are we doing this properly? - // fixme - handle virtual directory? - // fixme - this does not work 'cos it resets the HttpContext - // so we should move the DocumentRequest stuff etc back to default.aspx? - // but, also, with TransferRequest, auth & co will run on the new (default.aspx) url, - // is that really what we want? I need to talk about it with others. @zpqrtbnk - integrated = false; + // fixme - are we doing this properly? + // fixme - handle virtual directory? + // fixme - this does not work 'cos it resets the HttpContext + // so we should move the DocumentRequest stuff etc back to default.aspx? + // but, also, with TransferRequest, auth & co will run on the new (default.aspx) url, + // is that really what we want? I need to talk about it with others. @zpqrtbnk - // http://msmvps.com/blogs/luisabreu/archive/2007/10/09/are-you-using-the-new-transferrequest.aspx - // http://msdn.microsoft.com/en-us/library/aa344903.aspx - // http://forums.iis.net/t/1146511.aspx + // NOTE: SD: Need to look at how umbraMVCo does this. It is true that the TransferRequest initializes a new HttpContext, + // what we need to do is when we transfer to the handler we send a query string with the found page Id to be looked up + // after we have done our routing check. This however needs some though as we don't want to have to query for this + // page twice. Again, I'll check how umbraMVCo is doing it as I had though about that when i created it :) - if (integrated) - HttpContext.Current.Server.TransferRequest(path); - else - HttpContext.Current.RewritePath(path); - } + integrated = false; + + // http://msmvps.com/blogs/luisabreu/archive/2007/10/09/are-you-using-the-new-transferrequest.aspx + // http://msdn.microsoft.com/en-us/library/aa344903.aspx + // http://forums.iis.net/t/1146511.aspx + + if (integrated) + HttpContext.Current.Server.TransferRequest(path); + else + HttpContext.Current.RewritePath(path); + } - #region Legacy + #region Legacy - // "Clean umbPage from querystring, caused by .NET 2.0 default Auth Controls" - // but really, at the moment I have no idea what this does, and why... - void LegacyCleanUmbPageFromQueryString(ref Uri uri, ref string lpath) - { - string receivedQuery = uri.Query; - string path = uri.AbsolutePath; - string query = null; + // "Clean umbPage from querystring, caused by .NET 2.0 default Auth Controls" + // but really, at the moment I have no idea what this does, and why... + void LegacyCleanUmbPageFromQueryString(ref Uri uri, ref string lpath) + { + string receivedQuery = uri.Query; + string path = uri.AbsolutePath; + string query = null; - if (receivedQuery.Length > 0) - { - // Clean umbPage from querystring, caused by .NET 2.0 default Auth Controls - if (receivedQuery.IndexOf("umbPage") > 0) - { - int ampPos = receivedQuery.IndexOf('&'); - // query contains no ampersand? - if (ampPos < 0) - { - // no ampersand means no original query string - query = String.Empty; - // ampersand would occur past then end the of received query - ampPos = receivedQuery.Length; - } - else - { - // original query string past ampersand - query = receivedQuery.Substring(ampPos + 1, - receivedQuery.Length - ampPos - 1); - } - // get umbPage out of query string (9 = "&umbPage".Length() + 1) - path = receivedQuery.Substring(9, ampPos - 9); //this will fail if there are < 9 characters before the &umbPage query string + if (receivedQuery.Length > 0) + { + // Clean umbPage from querystring, caused by .NET 2.0 default Auth Controls + if (receivedQuery.IndexOf("umbPage") > 0) + { + int ampPos = receivedQuery.IndexOf('&'); + // query contains no ampersand? + if (ampPos < 0) + { + // no ampersand means no original query string + query = String.Empty; + // ampersand would occur past then end the of received query + ampPos = receivedQuery.Length; + } + else + { + // original query string past ampersand + query = receivedQuery.Substring(ampPos + 1, + receivedQuery.Length - ampPos - 1); + } + // get umbPage out of query string (9 = "&umbPage".Length() + 1) + path = receivedQuery.Substring(9, ampPos - 9); //this will fail if there are < 9 characters before the &umbPage query string - // --added when refactoring-- - uri = uri.Rewrite(path, query); - lpath = path.ToLower(); - } - //else - //{ - // // strip off question mark - // query = receivedQuery.Substring(1); - //} - } - } + // --added when refactoring-- + uri = uri.Rewrite(path, query); + lpath = path.ToLower(); + } + //else + //{ + // // strip off question mark + // query = receivedQuery.Substring(1); + //} + } + } - #endregion + #endregion - #region IHttpModule + #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) - { + // 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) + { // used to be done in PostAuthorizeRequest but then it disabled OutputCaching due // to rewriting happening too early in the chain (Alex Norcliffe 2010-02). app.PostResolveRequestCache += (sender, e) => @@ -352,19 +356,19 @@ namespace Umbraco.Web }; // used to check if the xml cache file needs to be updated/persisted - app.PostRequestHandlerExecute += (sender, e) => - { + app.PostRequestHandlerExecute += (sender, e) => + { var httpContext = ((HttpApplication)sender).Context; PersistXmlCache(new HttpContextWrapper(httpContext)); - }; + }; - // todo: initialize request errors handler - } + // todo: initialize request errors handler + } - public void Dispose() - { } + public void Dispose() + { } - #endregion + #endregion - } + } }