diff --git a/src/Umbraco.Web.UI/config/404handlers.Release.config b/src/Umbraco.Web.UI/config/404handlers.Release.config
index 11bdbaf8ef..770f7ca64b 100644
--- a/src/Umbraco.Web.UI/config/404handlers.Release.config
+++ b/src/Umbraco.Web.UI/config/404handlers.Release.config
@@ -3,5 +3,5 @@
-
+
\ No newline at end of file
diff --git a/src/Umbraco.Web.UI/config/404handlers.config b/src/Umbraco.Web.UI/config/404handlers.config
index 69bf6f1dd0..b6dd88fa8b 100644
--- a/src/Umbraco.Web.UI/config/404handlers.config
+++ b/src/Umbraco.Web.UI/config/404handlers.config
@@ -3,5 +3,5 @@
-
+
\ No newline at end of file
diff --git a/src/Umbraco.Web/Routing/AliasUrlProvider.cs b/src/Umbraco.Web/Routing/AliasUrlProvider.cs
index f8e270a16c..d8846afd60 100644
--- a/src/Umbraco.Web/Routing/AliasUrlProvider.cs
+++ b/src/Umbraco.Web/Routing/AliasUrlProvider.cs
@@ -88,14 +88,25 @@ namespace Umbraco.Web.Routing
#region Utilities
- private bool FindByUrlAliasEnabled
+ private static bool FindByUrlAliasEnabled
{
get
{
- var hasFinder = ContentFinderResolver.Current.ContainsType();
- var hasHandler = ContentFinderResolver.Current.ContainsType()
- && NotFoundHandlerHelper.CustomHandlerTypes.Contains(typeof(global::umbraco.SearchForAlias));
- return hasFinder || hasHandler;
+ // finder
+ if (ContentFinderResolver.Current.ContainsType())
+ return true;
+
+ // handler wrapped into a finder
+ if (ContentFinderResolver.Current.ContainsType>())
+ return true;
+
+ // handler wrapped into special finder
+ if (ContentFinderResolver.Current.ContainsType()
+ && NotFoundHandlerHelper.IsNotFoundHandlerEnabled())
+ return true;
+
+ // anything else, we can't detect
+ return false;
}
}
diff --git a/src/Umbraco.Web/Routing/ContentFinderByNotFoundHandlers.cs b/src/Umbraco.Web/Routing/ContentFinderByNotFoundHandlers.cs
index 6c10a45dd7..cb80429167 100644
--- a/src/Umbraco.Web/Routing/ContentFinderByNotFoundHandlers.cs
+++ b/src/Umbraco.Web/Routing/ContentFinderByNotFoundHandlers.cs
@@ -29,115 +29,77 @@ namespace Umbraco.Web.Routing
#region Copied over and adapted from presentation.requestHandler
- void HandlePageNotFound(PublishedContentRequest docRequest)
+ private static void HandlePageNotFound(PublishedContentRequest docRequest)
{
var url = NotFoundHandlerHelper.GetLegacyUrlForNotFoundHandlers();
LogHelper.Debug("Running for legacy url='{0}'.", () => url);
- foreach (var handler in GetNotFoundHandlers())
+ foreach (var handler in NotFoundHandlerHelper.GetNotFoundHandlers())
{
- IContentFinder finder = null;
var handlerName = handler.GetType().FullName;
-
LogHelper.Debug("Handler '{0}'.", () => handlerName);
- // replace with our own implementation
- if (handler is global::umbraco.SearchForAlias)
- finder = new ContentFinderByUrlAlias();
- else if (handler is global::umbraco.SearchForProfile)
- finder = new ContentFinderByProfile();
- else if (handler is global::umbraco.SearchForTemplate)
- finder = new ContentFinderByNiceUrlAndTemplate();
- else if (handler is global::umbraco.handle404)
- finder = new ContentFinderByLegacy404();
-
+ var finder = NotFoundHandlerHelper.SubsituteFinder(handler);
if (finder != null)
{
var finderName = finder.GetType().FullName;
LogHelper.Debug("Replace handler '{0}' by new finder '{1}'.", () => handlerName, () => finderName);
- if (finder.TryFindContent(docRequest))
- {
- // do NOT set docRequest.PublishedContent again here as
- // it would clear any template that the finder might have set
- LogHelper.Debug("Finder '{0}' found node with id={1}.", () => finderName, () => docRequest.PublishedContent.Id);
- if (docRequest.Is404)
- LogHelper.Debug("Finder '{0}' set status to 404.", () => finderName);
- // if we found a document, break, don't look at more handler -- we're done
- break;
- }
+ // can't find a document => continue with other handlers
+ if (finder.TryFindContent(docRequest) == false)
+ continue;
- // if we did not find a document, continue, look at other handlers
- continue;
- }
+ // found a document => break, don't run other handlers, we're done
- // else it's a legacy handler, run
+ // in theory an IContentFinder can return true yet set no document
+ // but none of the substitued finders (see SubstituteFinder) do it.
- if (handler.Execute(url) && handler.redirectID > 0)
- {
- var redirectId = handler.redirectID;
- docRequest.PublishedContent = docRequest.RoutingContext.UmbracoContext.ContentCache.GetById(redirectId);
+ // do NOT set docRequest.PublishedContent again here
+ // as it would clear any template that the finder might have set
- if (!docRequest.HasPublishedContent)
- {
- LogHelper.Debug("Handler '{0}' found node with id={1} which is not valid.", () => handlerName, () => redirectId);
- break;
- }
+ LogHelper.Debug("Finder '{0}' found node with id={1}.", () => finderName, () => docRequest.PublishedContent.Id);
+ if (docRequest.Is404)
+ LogHelper.Debug("Finder '{0}' set status to 404.", () => finderName);
- LogHelper.Debug("Handler '{0}' found valid node with id={1}.", () => handlerName, () => redirectId);
-
- if (docRequest.RoutingContext.UmbracoContext.HttpContext.Response.StatusCode == 404)
- {
- LogHelper.Debug("Handler '{0}' set status code to 404.", () => handlerName);
- docRequest.Is404 = true;
- }
-
- //// check for caching
- //if (handler.CacheUrl)
- //{
- // if (url.StartsWith("/"))
- // url = "/" + url;
-
- // var cacheKey = (currentDomain == null ? "" : currentDomain.Name) + url;
- // var culture = currentDomain == null ? null : currentDomain.Language.CultureAlias;
- // SetCache(cacheKey, new CacheEntry(handler.redirectID.ToString(), culture));
-
- // HttpContext.Current.Trace.Write("NotFoundHandler",
- // string.Format("Added to cache '{0}', {1}.", url, handler.redirectID));
- //}
-
- // if we found a document, break, don't look at more handler -- we're done
+ LogHelper.Debug("Handler '{0}' found valid node with id={1}.", () => handlerName, () => docRequest.PublishedContent.Id);
break;
}
- // if we did not find a document, continue, look at other handlers
+ // else it's a legacy handler: run
+
+ // can't find a document => continue with other handlers
+ if (handler.Execute(url) == false || handler.redirectID <= 0)
+ continue;
+
+ // found a document ID => ensure it's a valid document
+ var redirectId = handler.redirectID;
+ docRequest.PublishedContent = docRequest.RoutingContext.UmbracoContext.ContentCache.GetById(redirectId);
+
+ if (docRequest.HasPublishedContent == false)
+ {
+ // the handler said it could handle the url, and returned a content ID
+ // yet that content ID is invalid... should we run the other handlers?
+ // I don't think so, not here, let the "last chance" finder take care.
+ // so, break.
+
+ LogHelper.Debug("Handler '{0}' found node with id={1} which is not valid.", () => handlerName, () => redirectId);
+ break;
+ }
+
+ // found a valid document => break, don't run other handlers, we're done
+
+ LogHelper.Debug("Handler '{0}' found valid node with id={1}.", () => handlerName, () => redirectId);
+
+ if (docRequest.RoutingContext.UmbracoContext.HttpContext.Response.StatusCode == 404)
+ {
+ LogHelper.Debug("Handler '{0}' set status code to 404.", () => handlerName);
+ docRequest.Is404 = true;
+ }
+
+ break;
}
}
- IEnumerable GetNotFoundHandlers()
- {
- // instanciate new handlers
- // using definition cache
-
- var handlers = new List();
-
- foreach (var type in NotFoundHandlerHelper.CustomHandlerTypes)
- {
- try
- {
- var handler = Activator.CreateInstance(type) as INotFoundHandler;
- if (handler != null)
- handlers.Add(handler);
- }
- catch (Exception e)
- {
- LogHelper.Error(string.Format("Error instanciating handler {0}, ignoring.", type.FullName), e);
- }
- }
-
- return handlers;
- }
-
#endregion
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Routing/ContentLastChanceFinderByNotFoundHandlers.cs b/src/Umbraco.Web/Routing/ContentLastChanceFinderByNotFoundHandlers.cs
new file mode 100644
index 0000000000..115e8cabc0
--- /dev/null
+++ b/src/Umbraco.Web/Routing/ContentLastChanceFinderByNotFoundHandlers.cs
@@ -0,0 +1,101 @@
+using System;
+using System.Collections.Generic;
+using Umbraco.Core;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Models;
+using umbraco.interfaces;
+
+namespace Umbraco.Web.Routing
+{
+ ///
+ /// Provides an implementation of that runs legacy INotFoundHandler in "last chance" situation.
+ ///
+ public class ContentLastChanceFinderByNotFoundHandlers : IContentFinder
+ {
+ // notes
+ //
+ // at the moment we load the legacy INotFoundHandler
+ // excluding those that have been replaced by proper finders,
+ // and run them.
+
+ ///
+ /// Tries to find and assign an Umbraco document to a PublishedContentRequest.
+ ///
+ /// The PublishedContentRequest.
+ /// A value indicating whether an Umbraco document was found and assigned.
+ public bool TryFindContent(PublishedContentRequest docRequest)
+ {
+ HandlePageNotFound(docRequest);
+ return docRequest.HasPublishedContent;
+ }
+
+ #region Copied over and adapted from presentation.requestHandler
+
+ private static void HandlePageNotFound(PublishedContentRequest docRequest)
+ {
+ var url = NotFoundHandlerHelper.GetLegacyUrlForNotFoundHandlers();
+ LogHelper.Debug("Running for legacy url='{0}'.", () => url);
+
+ var handler = NotFoundHandlerHelper.GetNotFoundLastChanceHandler();
+ var handlerName = handler.GetType().FullName;
+ LogHelper.Debug("Handler '{0}'.", () => handlerName);
+
+ var finder = NotFoundHandlerHelper.SubsituteFinder(handler);
+ if (finder != null)
+ {
+ var finderName = finder.GetType().FullName;
+ LogHelper.Debug("Replace handler '{0}' by new finder '{1}'.", () => handlerName, () => finderName);
+
+ // can't find a document => exit
+ if (finder.TryFindContent(docRequest) == false)
+ return;
+
+ // found a document => we're done
+
+ // in theory an IContentFinder can return true yet set no document
+ // but none of the substitued finders (see SubstituteFinder) do it.
+
+ // do NOT set docRequest.PublishedContent again here
+ // as it would clear any template that the finder might have set
+
+ LogHelper.Debug("Finder '{0}' found node with id={1}.", () => finderName, () => docRequest.PublishedContent.Id);
+ if (docRequest.Is404)
+ LogHelper.Debug("Finder '{0}' set status to 404.", () => finderName);
+
+ LogHelper.Debug("Handler '{0}' found valid node with id={1}.", () => handlerName, () => docRequest.PublishedContent.Id);
+ return;
+ }
+
+ // else it's a legacy handler, run
+
+ // can't find a document => exit
+ if (handler.Execute(url) == false || handler.redirectID <= 0)
+ return;
+
+ // found a document ID => ensure it's a valid document
+ var redirectId = handler.redirectID;
+ docRequest.PublishedContent = docRequest.RoutingContext.UmbracoContext.ContentCache.GetById(redirectId);
+
+ if (docRequest.HasPublishedContent == false)
+ {
+ // the handler said it could handle the url, and returned a content ID
+ // yet that content ID is invalid... exit.
+
+ LogHelper.Debug("Handler '{0}' found node with id={1} which is not valid.", () => handlerName, () => redirectId);
+ return;
+ }
+
+ // found a valid document => return
+
+ LogHelper.Debug("Handler '{0}' found valid node with id={1}.", () => handlerName, () => redirectId);
+
+ if (docRequest.RoutingContext.UmbracoContext.HttpContext.Response.StatusCode == 404)
+ {
+ LogHelper.Debug("Handler '{0}' set status code to 404.", () => handlerName);
+ docRequest.Is404 = true;
+ }
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Routing/NotFoundHandlerHelper.cs b/src/Umbraco.Web/Routing/NotFoundHandlerHelper.cs
index 4287b215d3..36d4a55bc9 100644
--- a/src/Umbraco.Web/Routing/NotFoundHandlerHelper.cs
+++ b/src/Umbraco.Web/Routing/NotFoundHandlerHelper.cs
@@ -1,9 +1,12 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Web;
using System.Xml;
using System.Reflection;
+using Umbraco.Core;
using Umbraco.Core.Logging;
+using umbraco.interfaces;
namespace Umbraco.Web.Routing
{
@@ -36,14 +39,14 @@ namespace Umbraco.Web.Routing
return url;
// code from requestModule.UmbracoRewrite
- string tmp = httpContext.Request.Path.ToLower();
+ var tmp = httpContext.Request.Path.ToLower();
// 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.
// code from requestHandler.cleanUrl
- string root = Core.IO.SystemDirectories.Root.ToLower();
+ var root = Core.IO.SystemDirectories.Root.ToLower();
if (!string.IsNullOrEmpty(root) && tmp.StartsWith(root))
tmp = tmp.Substring(root.Length);
tmp = tmp.TrimEnd('/');
@@ -55,7 +58,7 @@ namespace Umbraco.Web.Routing
// code from UmbracoDefault.Page_PreInit
if (tmp != "" && httpContext.Request["umbPageID"] == null)
{
- string tryIntParse = tmp.Replace("/", "").Replace(".aspx", string.Empty);
+ var tryIntParse = tmp.Replace("/", "").Replace(".aspx", string.Empty);
int result;
if (int.TryParse(tryIntParse, out result))
tmp = tmp.Replace(".aspx", string.Empty);
@@ -77,7 +80,8 @@ namespace Umbraco.Web.Routing
return tmp;
}
- static IEnumerable _customHandlerTypes;
+ private static IEnumerable _customHandlerTypes;
+ private static Type _customLastChanceHandlerType;
static void InitializeNotFoundHandlers()
{
@@ -87,6 +91,8 @@ namespace Umbraco.Web.Routing
LogHelper.Debug("Registering custom handlers.");
var customHandlerTypes = new List();
+ Type customLastChanceHandlerType = null;
+ var hasLast = false;
var customHandlers = new XmlDocument();
customHandlers.Load(Core.IO.IOHelper.MapPath(Core.IO.SystemFiles.NotFoundhandlersConfig));
@@ -96,12 +102,23 @@ namespace Umbraco.Web.Routing
var assemblyName = n.Attributes.GetNamedItem("assembly").Value;
var typeName = n.Attributes.GetNamedItem("type").Value;
- string ns = assemblyName;
+ var ns = assemblyName;
var nsAttr = n.Attributes.GetNamedItem("namespace");
- if (nsAttr != null && !string.IsNullOrWhiteSpace(nsAttr.Value))
+ if (nsAttr != null && string.IsNullOrWhiteSpace(nsAttr.Value) == false)
ns = nsAttr.Value;
- LogHelper.Debug("Registering '{0}.{1},{2}'.", () => ns, () => typeName, () => assemblyName);
+ var lcAttr = n.Attributes.GetNamedItem("last");
+ var last = lcAttr != null && lcAttr.Value != null && lcAttr.Value.InvariantEquals("true");
+
+ if (last) // there can only be one last chance handler in the config file
+ {
+ if (hasLast)
+ throw new Exception();
+ hasLast = true;
+ }
+
+ LogHelper.Debug("Registering '{0}.{1},{2}'{3}", () => ns, () => typeName, () => assemblyName,
+ () => last ? " (last)." : ".");
Type type = null;
try
@@ -114,19 +131,79 @@ namespace Umbraco.Web.Routing
LogHelper.Error("Error registering handler, ignoring.", e);
}
- if (type != null)
+ if (type == null) continue;
+
+ if (last)
+ _customLastChanceHandlerType = type;
+ else
customHandlerTypes.Add(type);
}
- _customHandlerTypes = customHandlerTypes;
+ _customHandlerTypes = customHandlerTypes.ToArray();
}
- public static IEnumerable CustomHandlerTypes
+ public static IEnumerable GetNotFoundHandlers()
{
- get
+ // instanciate new handlers
+ // using definition cache
+
+ var handlers = new List();
+
+ foreach (var type in _customHandlerTypes)
{
- return _customHandlerTypes;
+ try
+ {
+ var handler = Activator.CreateInstance(type) as INotFoundHandler;
+ if (handler != null)
+ handlers.Add(handler);
+ }
+ catch (Exception e)
+ {
+ LogHelper.Error(string.Format("Error instanciating handler {0}, ignoring.", type.FullName), e);
+ }
}
+
+ return handlers;
}
+
+ public static bool IsNotFoundHandlerEnabled()
+ {
+ return _customHandlerTypes.Contains(typeof (T));
+ }
+
+ public static INotFoundHandler GetNotFoundLastChanceHandler()
+ {
+ if (_customLastChanceHandlerType == null) return null;
+
+ try
+ {
+ var handler = Activator.CreateInstance(_customLastChanceHandlerType) as INotFoundHandler;
+ if (handler != null)
+ return handler;
+ }
+ catch (Exception e)
+ {
+ LogHelper.Error(string.Format("Error instanciating handler {0}, ignoring.", _customLastChanceHandlerType.FullName), e);
+ }
+
+ return null;
+ }
+
+ public static IContentFinder SubsituteFinder(INotFoundHandler handler)
+ {
+ IContentFinder finder = null;
+
+ if (handler is global::umbraco.SearchForAlias)
+ finder = new ContentFinderByUrlAlias();
+ else if (handler is global::umbraco.SearchForProfile)
+ finder = new ContentFinderByProfile();
+ else if (handler is global::umbraco.SearchForTemplate)
+ finder = new ContentFinderByNiceUrlAndTemplate();
+ else if (handler is global::umbraco.handle404)
+ finder = new ContentFinderByLegacy404();
+
+ return finder;
+ }
+
}
}
diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj
index c8ab6c7f95..d65bcae0e6 100644
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -292,6 +292,7 @@
+
diff --git a/src/Umbraco.Web/WebBootManager.cs b/src/Umbraco.Web/WebBootManager.cs
index b2507f2174..35869fb76b 100644
--- a/src/Umbraco.Web/WebBootManager.cs
+++ b/src/Umbraco.Web/WebBootManager.cs
@@ -302,23 +302,31 @@ namespace Umbraco.Web
typeof(DefaultUrlProvider)
);
- // the legacy 404 will run from within ContentFinderByNotFoundHandlers below
- // so for the time being there is no last chance finder
- ContentLastChanceFinderResolver.Current = new ContentLastChanceFinderResolver();
+ ContentLastChanceFinderResolver.Current = new ContentLastChanceFinderResolver(
+ // handled by ContentLastChanceFinderByNotFoundHandlers for the time being
+ // soon as we get rid of INotFoundHandler support, we must enable this
+ //new ContentFinderByLegacy404()
+
+ // implement INotFoundHandler support... remove once we get rid of it
+ new ContentLastChanceFinderByNotFoundHandlers());
ContentFinderResolver.Current = new ContentFinderResolver(
- // 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
- typeof (ContentFinderByPageIdQuery),
- typeof (ContentFinderByNiceUrl),
- typeof (ContentFinderByIdPath),
- // these will be handled by ContentFinderByNotFoundHandlers
- // so they can be enabled/disabled even though resolvers are not public yet
- //typeof (ContentFinderByNiceUrlAndTemplate),
- //typeof (ContentFinderByProfile),
- //typeof (ContentFinderByUrlAlias),
- typeof (ContentFinderByNotFoundHandlers)
- );
+ // all built-in finders in the correct order, devs can then modify this list
+ // on application startup via an application event handler.
+ typeof (ContentFinderByPageIdQuery),
+ typeof (ContentFinderByNiceUrl),
+ typeof (ContentFinderByIdPath),
+
+ // these will be handled by ContentFinderByNotFoundHandlers so they can be enabled/disabled
+ // via the config file... soon as we get rid of INotFoundHandler support, we must enable
+ // them here.
+ //typeof (ContentFinderByNiceUrlAndTemplate),
+ //typeof (ContentFinderByProfile),
+ //typeof (ContentFinderByUrlAlias),
+
+ // implement INotFoundHandler support... remove once we get rid of it
+ typeof (ContentFinderByNotFoundHandlers)
+ );
SiteDomainHelperResolver.Current = new SiteDomainHelperResolver(new SiteDomainHelper());