diff --git a/src/Umbraco.Core/ApplicationContext.cs b/src/Umbraco.Core/ApplicationContext.cs index 4f1e449a20..6d4892e911 100644 --- a/src/Umbraco.Core/ApplicationContext.cs +++ b/src/Umbraco.Core/ApplicationContext.cs @@ -12,11 +12,7 @@ namespace Umbraco.Core /// public class ApplicationContext { - - private static ApplicationContext _instance; - private static readonly object Locker = new object(); - - /// + /// /// Constructor /// /// @@ -25,25 +21,12 @@ namespace Umbraco.Core Plugins = pluginResolver; } - /// - /// Singleton accessor - /// - public static ApplicationContext Current - { - get - { - return _instance; - } - set - { - lock (Locker) - { - _instance = value; - } - } - } + /// + /// Singleton accessor + /// + public static ApplicationContext Current { get; internal set; } - // IsReady is set to true by the boot manager once it has successfully booted + // IsReady is set to true by the boot manager once it has successfully booted // note - the original umbraco module checks on content.Instance in umbraco.dll // now, the boot task that setup the content store ensures that it is ready bool _isReady = false; diff --git a/src/Umbraco.Web.UI/Global.asax b/src/Umbraco.Web.UI/Global.asax new file mode 100644 index 0000000000..1627b363bc --- /dev/null +++ b/src/Umbraco.Web.UI/Global.asax @@ -0,0 +1 @@ +<%@ Application Codebehind="Global.asax.cs" Inherits="Umbraco.Web.UmbracoApplication" Language="C#" %> diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 6f4ac938d7..97a5a03c2b 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -306,6 +306,7 @@ UI.xml + diff --git a/src/Umbraco.Web.UI/web.Template.config b/src/Umbraco.Web.UI/web.Template.config index 773c4e0f47..ac9db87f0b 100644 --- a/src/Umbraco.Web.UI/web.Template.config +++ b/src/Umbraco.Web.UI/web.Template.config @@ -1,263 +1,267 @@ - -
-
+ +
+
- -
- + +
+ -
-
-
+
+
+
- - -
-
- - - - - - - - + + +
+
+ + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - + - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + - + - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - - - - - + --> + + + + + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - + --> + + + + - - + + - - + --> + - - - - - - - - - + + + + + - - - - - - - - + + + + + + + + + - - - - - + + + + + + + + - + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Umbraco.Web/ContentStore.cs b/src/Umbraco.Web/ContentStore.cs index d123342f22..e13714c9ac 100644 --- a/src/Umbraco.Web/ContentStore.cs +++ b/src/Umbraco.Web/ContentStore.cs @@ -12,8 +12,15 @@ namespace Umbraco.Web ///
internal class ContentStore { - private readonly UmbracoContext _umbracoContext; + /// + /// Delegate to return the current UmbracoContext + /// + private readonly UmbracoContext _umbracoContext; + /// + /// Constructor accepting a delegate to resolve the UmbracoContext + /// + /// public ContentStore(UmbracoContext umbracoContext) { _umbracoContext = umbracoContext; diff --git a/src/Umbraco.Web/NiceUrlResolver.cs b/src/Umbraco.Web/NiceUrlResolver.cs new file mode 100644 index 0000000000..57d5e2f78c --- /dev/null +++ b/src/Umbraco.Web/NiceUrlResolver.cs @@ -0,0 +1,159 @@ +using System.Collections.Generic; +using System.Linq; +using Umbraco.Web.Routing; +using umbraco; +using umbraco.IO; +using umbraco.cms.businesslogic.web; + +namespace Umbraco.Web +{ + /// + /// Resolves NiceUrls for a given node id + /// + internal class NiceUrlResolver + { + public NiceUrlResolver(ContentStore contentStore, UmbracoContext umbracoContext) + { + _umbracoContext = umbracoContext; + _contentStore = contentStore; + } + + private readonly UmbracoContext _umbracoContext; + private readonly ContentStore _contentStore; + + // note: this could be a parameter... + const string UrlNameProperty = "@urlName"; + + public virtual string GetNiceUrl(int nodeId) + { + int startNodeDepth = 1; + if (GlobalSettings.HideTopLevelNodeFromPath) + startNodeDepth = 2; + + return GetNiceUrl(nodeId, startNodeDepth, false); + } + + public virtual string GetNiceUrl(int nodeId, int startNodeDepth, bool forceDomain) + { + string path; + + // will not read cache if previewing! + var route = !_umbracoContext.InPreviewMode + ? _umbracoContext.RoutesCache.GetRoute(nodeId) + : null; + + if (route != null) + { + int pos = route.IndexOf('/'); + path = route.Substring(pos); + + if (UmbracoSettings.UseDomainPrefixes || forceDomain) + { + int rootNodeId = int.Parse(route.Substring(0, pos)); + if (rootNodeId > 0) + return DomainAtNode(rootNodeId) + path; + } + + return path; + } + + // else there was not route in the cache, must build route... + + var node = _contentStore.GetNodeById(nodeId); + if (node == null) + return "#"; // legacy wrote to the log here... + + var parts = new List(); + var depth = int.Parse(_contentStore.GetNodeProperty(node, "@level")); + var id = nodeId; + string domain = null; + while (depth >= 1) + { + // if not hiding that depth, add urlName + if (depth >= startNodeDepth) + parts.Add(_contentStore.GetNodeProperty(node, UrlNameProperty)); + + var tmp = DomainAtNode(id); + if (tmp != null) + { + if (UmbracoSettings.UseDomainPrefixes || forceDomain) + domain = tmp; + break; // break to capture the id + } + + node = _contentStore.GetNodeParent(node); + id = int.Parse(_contentStore.GetNodeProperty(node, "@id")); + depth--; + } + + parts.Reverse(); + path = "/" + string.Join("/", parts); + route = string.Format("{0}{1}", id, path); + + if (!_umbracoContext.InPreviewMode) + { + _umbracoContext.RoutesCache.Store(nodeId, route); // will not write if previewing + } + + return FormatUrl(domain, path); + } + + protected string DomainAtNode(int nodeId) + { + // be safe + if (nodeId <= 0) + return null; + + // get domains defined on that node + Domain[] domains = Domain.GetDomainsById(nodeId); + + // no domain set on that node, return null + if (domains.Length == 0) + return null; + + // else try to find the first domain that matches the current request + // else take the first domain of the list + Domain domain = domains.FirstOrDefault(d => UrlUtility.IsBaseOf(d.Name, _umbracoContext.OriginalUrl)) ?? domains[0]; + + var domainName = domain.Name.TrimEnd('/'); + domainName = UrlUtility.EnsureScheme(domainName, _umbracoContext.OriginalUrl.Scheme); + var pos = domainName.IndexOf("//"); + pos = domainName.IndexOf("/", pos + 2); + if (pos > 0) + domainName = domainName.Substring(0, pos); + + // return a scheme + host eg http://example.com with no trailing slash + return domainName; + } + + protected string FormatUrl(string domain, string path) + { + if (domain == null) // else vdir needs to be in the domain + { + // get the application virtual dir (empty if no vdir) + string vdir = SystemDirectories.Root; + if (!string.IsNullOrEmpty(vdir)) + domain = "/" + vdir; + } + + string url = (domain ?? "") + path; + if (path != "/") + { + // not at root + if (GlobalSettings.UseDirectoryUrls) + { + // add trailing / if required + if (UmbracoSettings.AddTrailingSlash) + url += "/"; + } + else + { + // add .aspx + url += ".aspx"; + } + } + + return url; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/NiceUrls.cs b/src/Umbraco.Web/NiceUrls.cs deleted file mode 100644 index efb70b7a96..0000000000 --- a/src/Umbraco.Web/NiceUrls.cs +++ /dev/null @@ -1,145 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Umbraco.Web.Routing; -using umbraco; -using umbraco.IO; -using umbraco.cms.businesslogic.web; - -namespace Umbraco.Web -{ - internal class NiceUrls - { - public NiceUrls(ContentStore contentStore, UmbracoContext umbracoContext, RoutesCache routesCache) - { - _umbracoContext = umbracoContext; - _contentStore = contentStore; - _routesCache = routesCache; - } - - private readonly UmbracoContext _umbracoContext; - private readonly ContentStore _contentStore; - private readonly RoutesCache _routesCache; - - // note: this could be a parameter... - const string UrlNameProperty = "@urlName"; - - public string GetNiceUrl(int nodeId) - { - return GetNiceUrl(nodeId, FIXME*Current.UmbracoUrl, false); - } - - public string GetNiceUrl(int nodeId, Uri current, bool absolute) - { - string path; - Uri domainUri; - - string route = _routesCache.GetRoute(nodeId); // will get null if previewing - - if (route != null) - { - // route is / eg "-1/", "-1/foo", "123/", "123/foo/bar"... - int pos = route.IndexOf('/'); - path = route.Substring(pos); - int id = int.Parse(route.Substring(0, pos)); // will be -1 or 1234 - domainUri = id > 0 ? DomainUriAtNode(id, current) : null; - } - else - { - var node = _contentStore.GetNodeById(nodeId); - if (node == null) - return "#"; - - var pathParts = new List(); - int id = nodeId; - domainUri = DomainUriAtNode(id, current); - while (domainUri == null && id > 0) - { - pathParts.Add(_contentStore.GetNodeProperty(node, UrlNameProperty)); - node = _contentStore.GetNodeParent(node); - id = int.Parse(_contentStore.GetNodeProperty(node, "@id")); - domainUri = id > 0 ? DomainUriAtNode(id, current) : null; - } - - // no domain, respect HideTopLevelNodeFromPath for legacy purposes - if (domainUri == null && umbraco.GlobalSettings.HideTopLevelNodeFromPath) - pathParts.RemoveAt(pathParts.Count - 1); - - pathParts.Reverse(); - path = "/" + string.Join("/", pathParts); // will be "/" or "/foo" or "/foo/bar" etc - route = id.ToString() + path; - _routesCache.Store(nodeId, route); // will not write if previewing - } - - return AssembleUrl(domainUri, path, current, absolute).ToString(); - } - - Uri AssembleUrl(Uri domain, string path, Uri current, bool absolute) - { - Uri uri; - - if (domain == null) - { - // no domain was found : return a relative url, add vdir if any - uri = new Uri(umbraco.IO.SystemDirectories.Root + path, UriKind.Relative); - } - else - { - // a domain was found : return an absolute or relative url - // cannot handle vdir, has to be in domain uri - if (!absolute && current != null && domain.GetLeftPart(UriPartial.Authority) == current.GetLeftPart(UriPartial.Authority)) - uri = new Uri(domain.AbsolutePath.TrimEnd('/') + path, UriKind.Relative); // relative - else - uri = new Uri(domain.GetLeftPart(UriPartial.Path).TrimEnd('/') + path); // absolute - } - - return UriFromUmbraco(uri); - } - - Uri DomainUriAtNode(int nodeId, Uri current) - { - // be safe - if (nodeId <= 0) - return null; - - // apply filter on domains defined on that node - var domainAndUri = Domains.ApplicableDomains(Domain.GetDomainsById(nodeId), current, true); - return domainAndUri == null ? null : domainAndUri.Uri; - } - - #endregion - - #region Map public urls to/from umbraco urls - - // fixme - what about vdir? - // path = path.Substring(UriUtility.AppVirtualPathPrefix.Length); // remove virtual directory - - public static Uri UriFromUmbraco(Uri uri) - { - var path = uri.GetSafeAbsolutePath(); - if (path == "/") - return uri; - - if (!umbraco.GlobalSettings.UseDirectoryUrls) - path += ".aspx"; - else if (umbraco.UmbracoSettings.AddTrailingSlash) - path += "/"; - - return uri.Rewrite(path); - } - - public static Uri UriToUmbraco(Uri uri) - { - var path = uri.GetSafeAbsolutePath(); - - path = path.ToLower(); - if (path != "/") - path = path.TrimEnd('/'); - if (path.EndsWith(".aspx")) - path = path.Substring(0, path.Length - ".aspx".Length); - - return uri.Rewrite(path); - } - - #endregion - } -} \ No newline at end of file diff --git a/src/Umbraco.Web/PluginResolverExtensions.cs b/src/Umbraco.Web/PluginResolverExtensions.cs index fbbf81c853..381fad68fb 100644 --- a/src/Umbraco.Web/PluginResolverExtensions.cs +++ b/src/Umbraco.Web/PluginResolverExtensions.cs @@ -7,49 +7,50 @@ using umbraco.BusinessLogic.Utils; namespace Umbraco.Web { - /// - /// Extension methods for the PluginResolver - /// - public static class PluginResolverExtensions - { + /// + /// Extension methods for the PluginResolver + /// + public static class PluginResolverExtensions + { - private static volatile IEnumerable _lookups; - private static readonly object Locker = new object(); + private static volatile IEnumerable _lookups; + private static readonly object Locker = new object(); - /// - /// Returns all available ILookup objects - /// - /// - /// - internal static IEnumerable ResolveLookups(this PluginResolver plugins) - { - if (_lookups == null) - { - lock(Locker) - { - if (_lookups == null) - { - var lookupTypes = TypeFinder.FindClassesOfType(); - var lookups = new List(); - foreach (var l in lookupTypes) - { - try - { - var typeInstance = Activator.CreateInstance(l) as ILookup; - lookups.Add(typeInstance); - } - catch (Exception ex) - { - Log.Add(LogTypes.Error, -1, "Error loading ILookup: " + ex.ToString()); - } - } - //set the global - _lookups = lookups; - } - } - } - return _lookups; - } + /// + /// Returns all available ILookup objects + /// + /// + /// + internal static IEnumerable ResolveLookups(this PluginResolver plugins) + { + if (_lookups == null) + { + lock (Locker) + { + if (_lookups == null) + { + var lookupTypes = TypeFinder.FindClassesOfType(); + var lookups = new List(); + foreach (var l in lookupTypes) + { + try + { + var typeInstance = Activator.CreateInstance(l) as ILookup; + lookups.Add(typeInstance); + } + catch (Exception ex) + { + //TODO: Need to fix logging so this doesn't bork if no SQL connection + //Log.Add(LogTypes.Error, -1, "Error loading ILookup: " + ex.ToString()); + } + } + //set the global + _lookups = lookups; + } + } + } + return _lookups; + } - } + } } \ No newline at end of file diff --git a/src/Umbraco.Web/Routing/DefaultRoutesCache.cs b/src/Umbraco.Web/Routing/DefaultRoutesCache.cs new file mode 100644 index 0000000000..a755e9c489 --- /dev/null +++ b/src/Umbraco.Web/Routing/DefaultRoutesCache.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; + +namespace Umbraco.Web.Routing +{ + /// + /// The default implementation of IRoutesCache + /// + internal class DefaultRoutesCache : IRoutesCache + { + private readonly object _lock = new object(); + private Dictionary _routes; + private Dictionary _nodeIds; + + public DefaultRoutesCache() + { + Clear(); + + // here we should register handlers to clear the cache when content changes + // at the moment this is done by library, which clears everything when content changed + // + // but really, we should do some partial refreshes! + // otherwise, we could even cache 404 errors... + } + + public void Store(int nodeId, string route) + { + lock (_lock) + { + _routes[nodeId] = route; + _nodeIds[route] = nodeId; + } + } + + public string GetRoute(int nodeId) + { + lock (_lock) + { + return _routes.ContainsKey(nodeId) ? _routes[nodeId] : null; + } + } + + public int GetNodeId(string route) + { + lock (_lock) + { + return _nodeIds.ContainsKey(route) ? _nodeIds[route] : 0; + } + } + + public void ClearNode(int nodeId) + { + lock (_lock) + { + if (_routes.ContainsKey(nodeId)) + { + _nodeIds.Remove(_routes[nodeId]); + _routes.Remove(nodeId); + } + } + } + + public void Clear() + { + lock (_lock) + { + _routes = new Dictionary(); + _nodeIds = new Dictionary(); + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Routing/DocumentRequest.cs b/src/Umbraco.Web/Routing/DocumentRequest.cs index 761929b15e..a1e153b342 100644 --- a/src/Umbraco.Web/Routing/DocumentRequest.cs +++ b/src/Umbraco.Web/Routing/DocumentRequest.cs @@ -6,6 +6,7 @@ using System.Globalization; using System.Diagnostics; // legacy +using umbraco.BusinessLogic; using umbraco.cms.businesslogic.web; using umbraco.cms.businesslogic.template; using umbraco.cms.businesslogic.member; @@ -19,21 +20,28 @@ namespace Umbraco.Web.Routing { static readonly TraceSource Trace = new TraceSource("DocumentRequest"); - public DocumentRequest(Uri uri, RoutingEnvironment lookups, UmbracoContext umbracoContext, NiceUrls niceUrls) + public DocumentRequest(Uri uri, RoutingContext routingContext) { this.Uri = uri; - _environment = lookups; - _umbracoContext = umbracoContext; - _niceUrls = niceUrls; + RoutingContext = routingContext; } + /// + /// the id of the requested node, if any, else zero. + /// + int _nodeId = 0; + + /// + /// the requested node, if any, else null. + /// + XmlNode _node = null; + #region Properties - // the id of the requested node, if any, else zero. - int _nodeId = 0; - - // the requested node, if any, else null. - XmlNode _node = null; + /// + /// Returns the current RoutingContext + /// + public RoutingContext RoutingContext { get; private set; } public Uri Uri { get; private set; } @@ -74,7 +82,7 @@ namespace Umbraco.Web.Routing _node = value; this.Template = null; if (_node != null) - _nodeId = int.Parse(_environment.ContentStore.GetNodeProperty(_node, "@id")); + _nodeId = int.Parse(RoutingContext.ContentStore.GetNodeProperty(_node, "@id")); else _nodeId = 0; } @@ -134,15 +142,9 @@ namespace Umbraco.Web.Routing #region Resolve - readonly RoutingEnvironment _environment; - private readonly UmbracoContext _umbracoContext; - private readonly NiceUrls _niceUrls; - /// /// Determines the site root (if any) matching the http request. - /// - /// The host name part of the http request, eg. www.example.com. - /// The url part of the http request, starting with a slash, eg. /foo/bar. + /// /// A value indicating whether a domain was found. public bool ResolveDomain() { @@ -200,7 +202,8 @@ namespace Umbraco.Web.Routing // the first successful lookup, if any, will set this.Node, and may also set this.Template // some lookups may implement caching Trace.TraceInformation("{0}Begin lookup", tracePrefix); - _environment.Lookups.Any(lookup => lookup.LookupDocument(this)); + var lookups = RoutingContext.RouteLookups.GetLookups(); + lookups.Any(lookup => lookup.LookupDocument(this)); Trace.TraceInformation("{0}End lookup, {1}", tracePrefix, (this.HasNode ? "a document was found" : "no document was found")); // fixme - not handling umbracoRedirect @@ -240,7 +243,7 @@ namespace Umbraco.Web.Routing Trace.TraceInformation("{0}No document, try notFound lookup", tracePrefix); // if it fails then give up, there isn't much more that we can do - if (_environment.LookupNotFound == null || !_environment.LookupNotFound.LookupDocument(this)) + if (RoutingContext.LookupNotFound == null || !RoutingContext.LookupNotFound.LookupDocument(this)) { Trace.TraceInformation("{0}Failed to find a document, give up", tracePrefix); break; @@ -292,7 +295,7 @@ namespace Umbraco.Web.Routing throw new InvalidOperationException("There is no node."); bool redirect = false; - string internalRedirect = _environment.ContentStore.GetNodeProperty(this.Node, "umbracoInternalRedirectId"); + string internalRedirect = RoutingContext.ContentStore.GetNodeProperty(this.Node, "umbracoInternalRedirectId"); if (!string.IsNullOrWhiteSpace(internalRedirect)) { @@ -316,7 +319,7 @@ namespace Umbraco.Web.Routing else { // redirect to another page - var node = _environment.ContentStore.GetNodeById(internalRedirectId); + var node = RoutingContext.ContentStore.GetNodeById(internalRedirectId); this.Node = node; if (node != null) { @@ -344,7 +347,7 @@ namespace Umbraco.Web.Routing if (this.Node == null) throw new InvalidOperationException("There is no node."); - var path = _environment.ContentStore.GetNodeProperty(this.Node, "@path"); + var path = RoutingContext.ContentStore.GetNodeProperty(this.Node, "@path"); if (Access.IsProtected(this.NodeId, path)) { @@ -357,14 +360,14 @@ namespace Umbraco.Web.Routing Trace.TraceInformation("{0}Not logged in, redirect to login page", tracePrefix); var loginPageId = Access.GetLoginPage(path); if (loginPageId != this.NodeId) - this.Node = _environment.ContentStore.GetNodeById(loginPageId); + 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); var errorPageId = Access.GetErrorPage(path); if (errorPageId != this.NodeId) - this.Node = _environment.ContentStore.GetNodeById(errorPageId); + this.Node = RoutingContext.ContentStore.GetNodeById(errorPageId); } else { @@ -387,9 +390,9 @@ namespace Umbraco.Web.Routing if (this.Node == null) throw new InvalidOperationException("There is no node."); - var templateAlias = _umbracoContext.HttpContext.Request.QueryString["altTemplate"]; + var templateAlias = RoutingContext.UmbracoContext.HttpContext.Request.QueryString["altTemplate"]; if (string.IsNullOrWhiteSpace(templateAlias)) - templateAlias = _umbracoContext.HttpContext.Request.Form["altTemplate"]; + templateAlias = RoutingContext.UmbracoContext.HttpContext.Request.Form["altTemplate"]; // fixme - we might want to support cookies?!? NO but provide a hook to change the template @@ -397,7 +400,7 @@ namespace Umbraco.Web.Routing { if (string.IsNullOrWhiteSpace(templateAlias)) { - templateAlias = _environment.ContentStore.GetNodeProperty(this.Node, "@template"); + templateAlias = RoutingContext.ContentStore.GetNodeProperty(this.Node, "@template"); Trace.TraceInformation("{0}Look for template id={1}", tracePrefix, templateAlias); int templateId; if (!int.TryParse(templateAlias, out templateId)) @@ -439,11 +442,11 @@ namespace Umbraco.Web.Routing if (this.HasNode) { int redirectId; - if (!int.TryParse(_environment.ContentStore.GetNodeProperty(this.Node, "umbracoRedirect"), out redirectId)) + if (!int.TryParse(RoutingContext.ContentStore.GetNodeProperty(this.Node, "umbracoRedirect"), out redirectId)) redirectId = -1; string redirectUrl = "#"; if (redirectId > 0) - redirectUrl = _niceUrls.GetNiceUrl(redirectId); + redirectUrl = RoutingContext.NiceUrlResolver.GetNiceUrl(redirectId); if (redirectUrl != "#") this.RedirectUrl = redirectUrl; } diff --git a/src/Umbraco.Web/Routing/IRoutesCache.cs b/src/Umbraco.Web/Routing/IRoutesCache.cs new file mode 100644 index 0000000000..0f4a1044ec --- /dev/null +++ b/src/Umbraco.Web/Routing/IRoutesCache.cs @@ -0,0 +1,11 @@ +namespace Umbraco.Web.Routing +{ + internal interface IRoutesCache + { + void Store(int nodeId, string route); + string GetRoute(int nodeId); + int GetNodeId(string route); + void ClearNode(int nodeId); + void Clear(); + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Routing/LookupByAlias.cs b/src/Umbraco.Web/Routing/LookupByAlias.cs index 7ed499f107..1580273f14 100644 --- a/src/Umbraco.Web/Routing/LookupByAlias.cs +++ b/src/Umbraco.Web/Routing/LookupByAlias.cs @@ -13,14 +13,9 @@ namespace Umbraco.Web.Routing [LookupWeight(50)] internal class LookupByAlias : ILookup { - public LookupByAlias(ContentStore contentStore) - { - _contentStore = contentStore; - } - + static readonly TraceSource Trace = new TraceSource("LookupByAlias"); - readonly ContentStore _contentStore; public bool LookupDocument(DocumentRequest docreq) { @@ -28,7 +23,7 @@ namespace Umbraco.Web.Routing if (docreq.Uri.AbsolutePath != "/") // no alias if "/" { - node = _contentStore.GetNodeByUrlAlias(docreq.HasDomain ? docreq.Domain.RootNodeId : 0, docreq.Uri.AbsolutePath); + 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); diff --git a/src/Umbraco.Web/Routing/LookupById.cs b/src/Umbraco.Web/Routing/LookupById.cs index a01330edec..f9d8ec46aa 100644 --- a/src/Umbraco.Web/Routing/LookupById.cs +++ b/src/Umbraco.Web/Routing/LookupById.cs @@ -10,24 +10,9 @@ namespace Umbraco.Web.Routing [LookupWeight(20)] internal class LookupById : ILookup { - public LookupById(ContentStore contentStore) - { - _contentStore = contentStore; - } - + static readonly TraceSource Trace = new TraceSource("LookupById"); - readonly ContentStore _contentStore; - - ////[Import] - //IContentStore ContentStoreImport - //{ - // set { _contentStore = value; } - //} - - //public LookupById() - //{ } - public bool LookupDocument(DocumentRequest docreq) { XmlNode node = null; @@ -43,7 +28,7 @@ namespace Umbraco.Web.Routing if (nodeId > 0) { Trace.TraceInformation("Id={0}", nodeId); - node = _contentStore.GetNodeById(nodeId); + node = docreq.RoutingContext.ContentStore.GetNodeById(nodeId); if (node != null) { docreq.Node = node; diff --git a/src/Umbraco.Web/Routing/LookupByPath.cs b/src/Umbraco.Web/Routing/LookupByPath.cs index b3e0332c45..4acb683a68 100644 --- a/src/Umbraco.Web/Routing/LookupByPath.cs +++ b/src/Umbraco.Web/Routing/LookupByPath.cs @@ -9,17 +9,9 @@ namespace Umbraco.Web.Routing [LookupWeight(10)] internal class LookupByPath : ILookup { - public LookupByPath(ContentStore contentStore, RoutesCache routesCache) - { - ContentStore = contentStore; - RoutesCache = routesCache; - } - + static readonly TraceSource Trace = new TraceSource("LookupByPath"); - protected ContentStore ContentStore; - protected RoutesCache RoutesCache; - public virtual bool LookupDocument(DocumentRequest docreq) { string route; @@ -36,11 +28,16 @@ namespace Umbraco.Web.Routing { Trace.TraceInformation("Test route \"{0}\"", route); - var nodeId = RoutesCache.GetNodeId(route); + //return '0' if in preview mode! + var nodeId = !docreq.RoutingContext.UmbracoContext.InPreviewMode + ? docreq.RoutingContext.UmbracoContext.RoutesCache.GetNodeId(route) + : 0; + + XmlNode node = null; if (nodeId > 0) { - node = ContentStore.GetNodeById(nodeId); + node = docreq.RoutingContext.ContentStore.GetNodeById(nodeId); if (node != null) { docreq.Node = node; @@ -48,19 +45,24 @@ namespace Umbraco.Web.Routing } else { - RoutesCache.ClearNode(nodeId); + docreq.RoutingContext.UmbracoContext.RoutesCache.ClearNode(nodeId); } } if (node == null) { Trace.TraceInformation("Cache miss, query"); - node = ContentStore.GetNodeByRoute(route); + node = docreq.RoutingContext.ContentStore.GetNodeByRoute(route); if (node != null) { docreq.Node = node; Trace.TraceInformation("Query matches, id={0}", docreq.NodeId); - RoutesCache.Store(docreq.NodeId, route); + + if (!docreq.RoutingContext.UmbracoContext.InPreviewMode) + { + docreq.RoutingContext.UmbracoContext.RoutesCache.Store(docreq.NodeId, route); // will not write if previewing + } + } else { diff --git a/src/Umbraco.Web/Routing/LookupByPathWithTemplate.cs b/src/Umbraco.Web/Routing/LookupByPathWithTemplate.cs index 91450fdabd..a14de76b6e 100644 --- a/src/Umbraco.Web/Routing/LookupByPathWithTemplate.cs +++ b/src/Umbraco.Web/Routing/LookupByPathWithTemplate.cs @@ -11,12 +11,7 @@ namespace Umbraco.Web.Routing [LookupWeight(30)] internal class LookupByPathWithTemplate : LookupByPath, ILookup { - static readonly TraceSource Trace = new TraceSource("LookupByPathWithTemplate"); - - public LookupByPathWithTemplate(ContentStore contentStore, RoutesCache routesCache) - : base(contentStore, routesCache) - { - } + static readonly TraceSource Trace = new TraceSource("LookupByPathWithTemplate"); public override bool LookupDocument(DocumentRequest docreq) { diff --git a/src/Umbraco.Web/Routing/LookupByProfile.cs b/src/Umbraco.Web/Routing/LookupByProfile.cs index 5e7cac1247..21ee4a5811 100644 --- a/src/Umbraco.Web/Routing/LookupByProfile.cs +++ b/src/Umbraco.Web/Routing/LookupByProfile.cs @@ -14,15 +14,7 @@ namespace Umbraco.Web.Routing [LookupWeight(40)] internal class LookupByProfile : LookupByPath, ILookup { - private readonly UmbracoContext _umbracoContext; - static readonly TraceSource Trace = new TraceSource("LookupByProfile"); - - - public LookupByProfile(ContentStore contentStore, RoutesCache routesCache, UmbracoContext umbracoContext) - : base(contentStore, routesCache) - { - _umbracoContext = umbracoContext; - } + static readonly TraceSource Trace = new TraceSource("LookupByProfile"); public override bool LookupDocument(DocumentRequest docreq) { @@ -44,7 +36,10 @@ namespace Umbraco.Web.Routing node = LookupDocumentNode(docreq, route); if (node != null) - _umbracoContext.HttpContext.Items["umbMemberLogin"] = memberLogin; + { + //TODO: Should be handled by Context Items class manager (http://issues.umbraco.org/issue/U4-61) + docreq.RoutingContext.UmbracoContext.HttpContext.Items["umbMemberLogin"] = memberLogin; + } else Trace.TraceInformation("No document matching profile path?"); } diff --git a/src/Umbraco.Web/Routing/LookupFor404.cs b/src/Umbraco.Web/Routing/LookupFor404.cs index f3a2354112..863b8c41ac 100644 --- a/src/Umbraco.Web/Routing/LookupFor404.cs +++ b/src/Umbraco.Web/Routing/LookupFor404.cs @@ -11,18 +11,12 @@ namespace Umbraco.Web.Routing { internal class LookupFor404 : ILookupNotFound { - public LookupFor404(ContentStore contentStore) - { - _contentStore = contentStore; - } - + static TraceSource _trace = new TraceSource("LookupByAlias"); - private readonly ContentStore _contentStore; - public bool LookupDocument(DocumentRequest docRequest) { - docRequest.Node = HandlePageNotFound(docRequest.Uri.AbsolutePath); + docRequest.Node = HandlePageNotFound(docRequest); return docRequest.HasNode; } @@ -30,17 +24,17 @@ namespace Umbraco.Web.Routing // copied from presentation/requestHandler // temporary!! - XmlNode HandlePageNotFound(string url) + XmlNode HandlePageNotFound(DocumentRequest docRequest) { - HttpContext.Current.Trace.Write("NotFoundHandler", string.Format("Running for url='{0}'.", url)); + HttpContext.Current.Trace.Write("NotFoundHandler", string.Format("Running for url='{0}'.", docRequest.Uri.AbsolutePath)); XmlNode currentPage = null; foreach (var handler in GetNotFoundHandlers()) { - if (handler.Execute(url) && handler.redirectID > 0) + if (handler.Execute(docRequest.Uri.AbsolutePath) && handler.redirectID > 0) { //currentPage = umbracoContent.GetElementById(handler.redirectID.ToString()); - currentPage = _contentStore.GetNodeById(handler.redirectID); + currentPage = docRequest.RoutingContext.ContentStore.GetNodeById(handler.redirectID); // FIXME - could it be null? diff --git a/src/Umbraco.Web/Routing/RouteLookups.cs b/src/Umbraco.Web/Routing/RouteLookups.cs new file mode 100644 index 0000000000..137ea8514d --- /dev/null +++ b/src/Umbraco.Web/Routing/RouteLookups.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using Umbraco.Core; + +namespace Umbraco.Web.Routing +{ + /// + /// Represents a collection of ILookup used for routing that are registered in the application + /// + internal class RouteLookups + { + private static readonly List Lookups = new List(); + private static readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim(); + + /// + /// Singleton accessor + /// + public static RouteLookups Current { get; internal set; } + + internal RouteLookups(IEnumerable lookups) + { + Lookups.AddRange(SortByWeight(lookups)); + } + + /// + /// Returns all of the lookups + /// + /// + public IEnumerable GetLookups() + { + return Lookups; + } + + /// + /// Removes an ILookup based on the specified Type + /// + /// + public void RemoveLookup() + where T : ILookup + { + using (new WriteLock(Lock)) + { + Lookups.Remove(Lookups.SingleOrDefault(x => x is T)); + } + } + + + /// + /// Adds a new lookup to the end of the list + /// + /// + public void AddLookup(ILookup lookup) + { + if (CheckExists(lookup)) + throw new InvalidOperationException("The lookup type " + lookup + " already exists in the lookup collection"); + + using (new WriteLock(Lock)) + { + Lookups.Add(lookup); + } + } + + /// + /// Inserts a lookup at the specified index + /// + /// + /// + public void InsertLookup(int index, ILookup lookup) + { + if (CheckExists(lookup)) + throw new InvalidOperationException("The lookup type " + lookup + " already exists in the lookup collection"); + + using (new WriteLock(Lock)) + { + Lookups.Insert(index, lookup); + } + } + + /// + /// checks if a lookup already exists by type + /// + /// + /// + private static bool CheckExists(ILookup lookup) + { + return Lookups.Any(x => x.GetType() == lookup.GetType()); + } + + /// + /// Sorts the ILookups in the list based on an attribute weight if one is specified + /// + /// + /// + private static IEnumerable SortByWeight(IEnumerable lookups) + { + return lookups.OrderBy(x => + { + var attribute = x.GetType().GetCustomAttributes(true).OfType().SingleOrDefault(); + return attribute == null ? LookupWeightAttribute.DefaultWeight : attribute.Weight; + }).ToList(); + } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Routing/RoutesCache.cs b/src/Umbraco.Web/Routing/RoutesCache.cs index cb99ceb2de..e46fa9681c 100644 --- a/src/Umbraco.Web/Routing/RoutesCache.cs +++ b/src/Umbraco.Web/Routing/RoutesCache.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System; namespace Umbraco.Web.Routing { @@ -8,82 +8,54 @@ namespace Umbraco.Web.Routing // // a route is [rootId]/path/to/node // where rootId is the id of the "site root" node - // if missing then the "site root" is the content root // - internal class RoutesCache - { - private readonly object _lock = new object(); - private Dictionary _routes; - private Dictionary _nodeIds; + internal class RoutesCache + { + private static readonly RoutesCache Instance = new RoutesCache(); + private static IRoutesCache _provider; - private readonly UmbracoContext _umbracoContext; + /// + /// public contructor assigns the DefaultRoutesCache as the default provider + /// + public RoutesCache() + :this(null) + { + } - public RoutesCache(UmbracoContext umbracoContext) - { - _umbracoContext = umbracoContext; + /// + /// Constructor sets a custom provider if specified + /// + /// + internal RoutesCache(IRoutesCache provider) + { + _provider = provider ?? new DefaultRoutesCache(); + } - Clear(); + /// + /// Set the routes cache provider + /// + /// + public static void SetProvider(IRoutesCache provider) + { + if (provider == null) throw new ArgumentNullException("provider"); + _provider = provider; + } - // here we should register handlers to clear the cache when content changes - // at the moment this is done by library, which clears everything when content changed - // - // but really, we should do some partial refreshes! - // otherwise, we could even cache 404 errors... - } + /// + /// Singleton accessor + /// + public static RoutesCache Current + { + get { return Instance; } + } - public void Store(int nodeId, string route) - { - if (_umbracoContext.InPreviewMode) - return; - - lock (_lock) - { - _routes[nodeId] = route; - _nodeIds[route] = nodeId; - } - } - - public string GetRoute(int nodeId) - { - if (_umbracoContext.InPreviewMode) - return null; - - lock (_lock) - { - return _routes.ContainsKey(nodeId) ? _routes[nodeId] : null; - } - } - - public int GetNodeId(string route) - { - if (_umbracoContext.InPreviewMode) - return 0; - - lock (_lock) - { - return _nodeIds.ContainsKey(route) ? _nodeIds[route] : 0; - } - } - - public void ClearNode(int nodeId) - { - lock (_lock) - { - if (_routes.ContainsKey(nodeId)) - { - _nodeIds.Remove(_routes[nodeId]); - _routes.Remove(nodeId); - } - } - } - - public void Clear() - { - lock (_lock) - { - _routes = new Dictionary(); - _nodeIds = new Dictionary(); - } - } - } + /// + /// Get the current provider + /// + /// + public IRoutesCache GetProvider() + { + return _provider; + } + } } \ No newline at end of file diff --git a/src/Umbraco.Web/Routing/RoutingContext.cs b/src/Umbraco.Web/Routing/RoutingContext.cs new file mode 100644 index 0000000000..7f2013a0d9 --- /dev/null +++ b/src/Umbraco.Web/Routing/RoutingContext.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.Linq; + +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 RoutingContext + { + public RoutingContext( + UmbracoContext umbracoContext, + RouteLookups lookups, + ILookupNotFound lookupNotFound, + ContentStore contentStore, + NiceUrlResolver niceUrlResolver) + { + UmbracoContext = umbracoContext; + RouteLookups = lookups; + LookupNotFound = lookupNotFound; + ContentStore = contentStore; + NiceUrlResolver = niceUrlResolver; + } + + public UmbracoContext UmbracoContext { get; private set; } + public RouteLookups RouteLookups { get; private set; } + public ILookupNotFound LookupNotFound { get; private set; } + public ContentStore ContentStore { get; private set; } + public NiceUrlResolver NiceUrlResolver { get; private set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Routing/RoutingEnvironment.cs b/src/Umbraco.Web/Routing/RoutingEnvironment.cs deleted file mode 100644 index 8e63f223cd..0000000000 --- a/src/Umbraco.Web/Routing/RoutingEnvironment.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Collections.Generic; -using System.Linq; - -namespace Umbraco.Web.Routing -{ - internal class RoutingEnvironment - { - public RoutingEnvironment( - IEnumerable lookups, - ILookupNotFound lookupNotFound, - ContentStore contentStore) - { - Lookups = SortByPartWeight(lookups); - LookupNotFound = lookupNotFound; - ContentStore = contentStore; - } - - private static IEnumerable SortByPartWeight(IEnumerable lookups) - { - return lookups.OrderBy(x => - { - var attribute = x.GetType().GetCustomAttributes(true).OfType().SingleOrDefault(); - return attribute == null ? LookupWeightAttribute.DefaultWeight : attribute.Weight; - }).ToList(); - } - - public IEnumerable Lookups - { - get; - private set; - } - - public ILookupNotFound LookupNotFound { get; private set; } - - public ContentStore ContentStore { get; private set; } - - } -} \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 98d3136fef..0dae665f09 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -240,11 +240,13 @@ - + + + @@ -252,8 +254,9 @@ + - + database.ascx @@ -1759,6 +1762,7 @@ + diff --git a/src/Umbraco.Web/UmbracoApplication.cs b/src/Umbraco.Web/UmbracoApplication.cs new file mode 100644 index 0000000000..5febadc78a --- /dev/null +++ b/src/Umbraco.Web/UmbracoApplication.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using Umbraco.Core; +using Umbraco.Web.Routing; + +namespace Umbraco.Web +{ + /// + /// The Umbraco global.asax class + /// + public class UmbracoApplication : System.Web.HttpApplication + { + + private static readonly TraceSource Trace = new TraceSource("UmbracoApplication"); + + /// + /// Initializes the Umbraco application + /// + /// + /// + protected virtual void Application_Start(object sender, EventArgs e) + { + Trace.TraceInformation("Initialize AppDomain"); + + // Backwards compatibility - set the path and URL type for ClientDependency 1.5.1 [LK] + ClientDependency.Core.CompositeFiles.Providers.XmlFileMapper.FileMapVirtualFolder = "~/App_Data/TEMP/ClientDependency"; + ClientDependency.Core.CompositeFiles.Providers.BaseCompositeFileProcessingProvider.UrlTypeDefault = ClientDependency.Core.CompositeFiles.Providers.CompositeUrlType.Base64QueryStrings; + + //create the ApplicationContext + ApplicationContext.Current = new ApplicationContext(new PluginResolver()) + { + IsReady = true // fixme + }; + + //create the route lookups singleton + RouteLookups.Current = new RouteLookups(ApplicationContext.Current.Plugins.ResolveLookups()); + + Trace.TraceInformation("AppDomain is initialized"); + } + + protected virtual void Application_Error(object sender, EventArgs e) + { + + } + + protected virtual void Application_End(object sender, EventArgs e) + { + + } + } +} diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs index 4992fa3e07..95625ab475 100644 --- a/src/Umbraco.Web/UmbracoContext.cs +++ b/src/Umbraco.Web/UmbracoContext.cs @@ -32,24 +32,32 @@ namespace Umbraco.Web /// private static UmbracoContext _umbracoContext; - /// - /// Creates a new Umbraco context. - /// - /// - /// - internal UmbracoContext(HttpContextBase httpContext, ApplicationContext applicationContext) + /// + /// Creates a new Umbraco context. + /// + /// + /// + /// + internal UmbracoContext( + HttpContextBase httpContext, + ApplicationContext applicationContext, + IRoutesCache routesCache) { if (httpContext == null) throw new ArgumentNullException("httpContext"); if (applicationContext == null) throw new ArgumentNullException("applicationContext"); HttpContext = httpContext; Application = applicationContext; + RoutesCache = routesCache; + + //set the original url + OriginalUrl = httpContext.Request.Url; } /// /// Gets the current Umbraco Context. /// - public static UmbracoContext Current + public static UmbracoContext Current { get { @@ -91,7 +99,9 @@ namespace Umbraco.Web /// public ApplicationContext Application { get; private set; } - /// + internal IRoutesCache RoutesCache { get; private set; } + + /// /// Gets/sets the original URL of the request /// internal Uri OriginalUrl { get; set; } @@ -124,7 +134,7 @@ namespace Umbraco.Web /// /// Gets/sets the DocumentRequest object /// - internal DocumentRequest DocumentRequest { get; set; } + internal DocumentRequest DocumentRequest { get; set; } /// /// Exposes the HttpContext for the current request @@ -193,29 +203,7 @@ namespace Umbraco.Web && !currentUrl.StartsWith(IOHelper.ResolveUrl(SystemDirectories.Umbraco)); // is not in admin UI } } - - /// - /// Gets the current Live Editing Context. - /// - public virtual ILiveEditingContext LiveEditingContext - { - get - { - //TODO: this should be done with a wrapper: http://issues.umbraco.org/issue/U4-61 - var value = (ILiveEditingContext)HttpContext.Items["LiveEditingContext"]; - if (value == null) - { - LiveEditingContext = value = new DefaultLiveEditingContext(); - } - return value; - } - - set - { - //TODO: this should be done with a wrapper: http://issues.umbraco.org/issue/U4-61 - HttpContext.Items["LiveEditingContext"] = value; - } - } + } } \ No newline at end of file diff --git a/src/Umbraco.Web/UmbracoModule.cs b/src/Umbraco.Web/UmbracoModule.cs index 178e691695..9d9df6b495 100644 --- a/src/Umbraco.Web/UmbracoModule.cs +++ b/src/Umbraco.Web/UmbracoModule.cs @@ -25,6 +25,11 @@ namespace Umbraco.Web { Trace.TraceInformation("Process 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(); @@ -32,31 +37,37 @@ namespace Umbraco.Web 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(new HttpContextWrapper(httpContext)); + //create the UmbracoContext singleton, one per request!! - var umbracoContext = new UmbracoContext(new HttpContextWrapper(httpContext), ApplicationContext.Current); + var umbracoContext = new UmbracoContext( + new HttpContextWrapper(httpContext), + ApplicationContext.Current, + RoutesCache.Current.GetProvider()); UmbracoContext.Current = umbracoContext; // NO! // these are application-wide singletons! //create a content store - var contentStore = new ContentStore(umbracoContext); - //create the routes cache - var routesCache = new RoutesCache(umbracoContext); + var contentStore = new ContentStore(umbracoContext); //create the nice urls - var niceUrls = new NiceUrls(contentStore, umbracoContext, routesCache); - //create the RoutingEnvironment (one per http request as it relies on the umbraco context!) - var routingEnvironment = new RoutingEnvironment( - ApplicationContext.Current.Plugins.ResolveLookups().ToArray(), - new LookupFor404(contentStore), - contentStore); - + var niceUrls = new NiceUrlResolver(contentStore, umbracoContext); + //create the RoutingContext (one per http request) + var routingContext = new RoutingContext( + umbracoContext, + RouteLookups.Current, + new LookupFor404(), + 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, routingEnvironment, umbracoContext, niceUrls); - - // initialize the document request on the UmbracoContext (this is circular dependency!!!) + 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; //create the LegacyRequestInitializer (one per http request as it relies on the umbraco context!) @@ -307,7 +318,16 @@ namespace Umbraco.Web // and there may be more than 1 application per application domain public void Init(HttpApplication app) { - InitializeApplication(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) => + { + HttpContext httpContext = ((HttpApplication)sender).Context; + ProcessRequest(httpContext); + }; + + // todo: initialize request errors handler + // todo: initialize XML cache flush } public void Dispose() @@ -315,62 +335,5 @@ namespace Umbraco.Web #endregion - #region Initialize - - private static volatile bool _appDomainInitialized = false; - static readonly object AppDomainLock = new object(); - - /// - /// Initializes the application one time only - /// - void InitializeDomain() - { - if (!_appDomainInitialized) - { - lock (AppDomainLock) - { - if (!_appDomainInitialized) - { - Trace.TraceInformation("Initialize AppDomain"); - - //create the ApplicationContext - ApplicationContext.Current = new ApplicationContext(new PluginResolver()); - - //TODO: Why is this necessary? if the ApplicationContext is null, then its not ready - ApplicationContext.Current.IsReady = true; // fixme - - // Backwards compatibility - set the path and URL type for ClientDependency 1.5.1 [LK] - ClientDependency.Core.CompositeFiles.Providers.XmlFileMapper.FileMapVirtualFolder = "~/App_Data/TEMP/ClientDependency"; - ClientDependency.Core.CompositeFiles.Providers.BaseCompositeFileProcessingProvider.UrlTypeDefault = ClientDependency.Core.CompositeFiles.Providers.CompositeUrlType.Base64QueryStrings; - - _appDomainInitialized = true; - } - Trace.TraceInformation("AppDomain is initialized"); - } - } - } - - void InitializeApplication(HttpApplication app) - { - // we can't do in in module.Init because we need HttpContext. - app.BeginRequest += (sender, e) => - { - Trace.TraceInformation("Welcome to Umbraco!"); - InitializeDomain(); - }; - - // 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) => - { - HttpContext httpContext = ((HttpApplication)sender).Context; - ProcessRequest(httpContext); - }; - - // todo: initialize request errors handler - // todo: initialize XML cache flush - } - - #endregion } } diff --git a/src/Umbraco.Web/umbraco.presentation/default.aspx.cs b/src/Umbraco.Web/umbraco.presentation/default.aspx.cs index 53b4b19dc1..f10b0bce4c 100644 --- a/src/Umbraco.Web/umbraco.presentation/default.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/default.aspx.cs @@ -1,359 +1,230 @@ using System; +using System.Threading; using System.Web; +using System.Web.Routing; using System.Web.UI; using System.IO; using System.Xml; using System.Text.RegularExpressions; -using umbraco.presentation; +using Umbraco.Web; using umbraco.cms.businesslogic.web; using umbraco.cms.businesslogic; namespace umbraco { - /// - /// Summary description for WebForm1. - /// - /// - public partial class UmbracoDefault : Page - { + /// + /// Summary description for WebForm1. + /// + /// + public partial class UmbracoDefault : Page + { + page _upage = null; - private Guid m_version = Guid.Empty; - private string m_tmp = requestHandler.cleanUrl(); - private page m_umbPage = null; - private requestHandler m_umbRequest = null; + bool _validateRequest = true; - private bool m_validateRequest = true; + const string TraceCategory = "UmbracoDefault"; - /// - /// To turn off request validation set this to false before the PageLoad event. This equelevant to the validateRequest page directive - /// and has nothing to do with "normal" validation controls. Default value is true. - /// - public bool ValidateRequest - { - get { return m_validateRequest; } - set { m_validateRequest = value; } - } + /// + /// To turn off request validation set this to false before the PageLoad event. This equivalent to the validateRequest page directive + /// and has nothing to do with "normal" validation controls. Default value is true. + /// + public bool ValidateRequest + { + get { return _validateRequest; } + set { _validateRequest = value; } + } - protected override void Render(HtmlTextWriter output) - { + // fixme - switch over to OnPreInit override + void Page_PreInit(Object sender, EventArgs e) + { + Trace.Write(TraceCategory, "Begin PreInit"); - // Get content - TextWriter tempWriter = new StringWriter(); - base.Render(new HtmlTextWriter(tempWriter)); - string pageContents = tempWriter.ToString(); + // moved into LegacyRequestInitializer + // initialize the UmbracoContext instance + //if (UmbracoContext.Current == null) + // UmbracoContext.Current = new UmbracoContext(HttpContext.Current); - pageContents = template.ParseInternalLinks(pageContents); + // get the document request + var docreq = UmbracoContext.Current.DocumentRequest; - // preview - if (UmbracoContext.Current.InPreviewMode) - { - Trace.Write("Runtime Engine", "Umbraco is running in preview mode."); + // load request parameters + // any reason other than liveEditing why we want to do this?! + Guid requestVersion; + if (!Guid.TryParse(Request["umbVersion"], out requestVersion)) + requestVersion = Guid.Empty; + int requestId; + if (!int.TryParse(Request["umbPageID"], out requestId)) + requestId = -1; - int bodyPos = pageContents.ToLower().IndexOf(""); - if (bodyPos > -1) - { - string htmlBadge = - String.Format(UmbracoSettings.PreviewBadge, - umbraco.IO.IOHelper.ResolveUrl(umbraco.IO.SystemDirectories.Umbraco), - umbraco.IO.IOHelper.ResolveUrl(umbraco.IO.SystemDirectories.Umbraco_client), - Server.UrlEncode(UmbracoContext.Current.Request.Path) - ); + if (requestId <= 0) + { + // asking for a different version of the default document + if (requestVersion != Guid.Empty) + { + // security check + var bp = new BasePages.UmbracoEnsuredPage(); + bp.ensureContext(); + requestId = docreq.NodeId; + } + } + else + { + // asking for a specific document (and maybe a specific version) + // security check + var bp = new BasePages.UmbracoEnsuredPage(); + bp.ensureContext(); + } - // inject badge - pageContents = - pageContents.Substring(0, bodyPos) + - htmlBadge + pageContents.Substring(bodyPos, pageContents.Length - bodyPos); - } - } + if (requestId <= 0) + { + // use the DocumentRequest if it has resolved + // else it means that no lookup could find a document to render + // or that the document to render has no template... + if (docreq.HasNode && docreq.HasTemplate) + { + _upage = new page(docreq); + UmbracoContext.Current.HttpContext.Items["pageID"] = docreq.NodeId; // legacy - fixme + var templatePath = umbraco.IO.SystemDirectories.Masterpages + "/" + docreq.Template.Alias.Replace(" ", "") + ".master"; // fixme - should be in .Template! + this.MasterPageFile = templatePath; // set the template + } + else + { + // if we know we're 404, display the ugly message, else a blank page + if (docreq.Is404) + RenderNotFound(); + Response.End(); + return; + } + } + else + { + // override the document = ignore the DocumentRequest + // fixme - what if it fails? + var document = requestVersion == Guid.Empty ? new Document(requestId) : new Document(requestId, requestVersion); + _upage = new page(document); + UmbracoContext.Current.HttpContext.Items["pageID"] = requestId; // legacy - fixme - output.Write(pageContents); + // must fall back to old code + OnPreInitLegacy(); + } - } + // reset the friendly path so it's used by forms, etc. + Trace.Write(TraceCategory, string.Format("Reset url to \"{0}\"", UmbracoContext.Current.OriginalUrl)); + Context.RewritePath(UmbracoContext.Current.OriginalUrl.PathAndQuery); - void Page_PreInit(Object sender, EventArgs e) - { - Trace.Write("umbracoInit", "handling request"); + Context.Items.Add("pageElements", _upage.Elements); // legacy - fixme - if (UmbracoContext.Current == null) - UmbracoContext.Current = new UmbracoContext(HttpContext.Current); + Trace.Write(TraceCategory, "End PreInit"); + } - bool editMode = UmbracoContext.Current.LiveEditingContext.Enabled; + void OnPreInitLegacy() + { + if (_upage.Template == 0) + { + string custom404 = umbraco.library.GetCurrentNotFoundPageId(); + if (!String.IsNullOrEmpty(custom404)) + { + XmlNode xmlNodeNotFound = content.Instance.XmlContent.GetElementById(custom404); + if (xmlNodeNotFound != null) + { + _upage = new page(xmlNodeNotFound); + } + } + } - if (editMode) - ValidateRequest = false; + if (_upage.Template != 0) + { + this.MasterPageFile = template.GetMasterPageName(_upage.Template); - if (m_tmp != "" && Request["umbPageID"] == null) - { - // Check numeric - string tryIntParse = m_tmp.Replace("/", "").Replace(".aspx", string.Empty); - int result; - if (int.TryParse(tryIntParse, out result)) - { - m_tmp = m_tmp.Replace(".aspx", string.Empty); + //TODO: The culture stuff needs to all be set in the module - // Check for request - if (!string.IsNullOrEmpty(Request["umbVersion"])) - { - // Security check - BasePages.UmbracoEnsuredPage bp = new BasePages.UmbracoEnsuredPage(); - bp.ensureContext(); - m_version = new Guid(Request["umbVersion"]); - } - } - } - else - { - if (!string.IsNullOrEmpty(Request["umbPageID"])) - { - int result; - if (int.TryParse(Request["umbPageID"], out result)) - { - m_tmp = Request["umbPageID"]; - } - } - } + string cultureAlias = null; + for (int i = _upage.SplitPath.Length - 1; i > 0; i--) + { + var domains = Domain.GetDomainsById(int.Parse(_upage.SplitPath[i])); + if (domains.Length > 0) + { + cultureAlias = domains[0].Language.CultureAlias; + break; + } + } - if (m_version != Guid.Empty) - { - HttpContext.Current.Items["pageID"] = m_tmp.Replace("/", ""); - m_umbPage = new page(int.Parse(m_tmp.Replace("/", "")), m_version); - } - else - { - m_umbRequest = new requestHandler(UmbracoContext.Current.GetXml(), m_tmp); - Trace.Write("umbracoInit", "Done handling request"); - if (m_umbRequest.currentPage != null) - { - HttpContext.Current.Items["pageID"] = m_umbRequest.currentPage.Attributes.GetNamedItem("id").Value; + if (cultureAlias != null) + { + UmbracoContext.Current.HttpContext.Trace.Write("default.aspx", "Culture changed to " + cultureAlias); + var culture = new System.Globalization.CultureInfo(cultureAlias); + Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture = culture; + } + } + else + { + Response.StatusCode = 404; + RenderNotFound(); + Response.End(); + } + } - // Handle edit - if (editMode) - { - Document d = new Document(int.Parse(m_umbRequest.currentPage.Attributes.GetNamedItem("id").Value)); - m_umbPage = new page(d.Id, d.Version); - } - else - m_umbPage = new page(m_umbRequest.currentPage); - } - } + // + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); - // set the friendly path so it's used by forms - HttpContext.Current.RewritePath(HttpContext.Current.Items[requestModule.ORIGINAL_URL_CXT_KEY].ToString()); + if (ValidateRequest) + Request.ValidateInput(); - if (UmbracoSettings.UseAspNetMasterPages) - { - HttpContext.Current.Trace.Write("umbracoPage", "Looking up skin information"); + // handle the infamous umbDebugShowTrace, etc + Page.Trace.IsEnabled &= GlobalSettings.DebugMode && !String.IsNullOrWhiteSpace(Request["umbDebugShowTrace"]); + } - if (m_umbPage != null) - { - if (m_umbPage.Template == 0) - { - string custom404 = umbraco.library.GetCurrentNotFoundPageId(); - if (!String.IsNullOrEmpty(custom404)) - { - XmlNode xmlNodeNotFound = content.Instance.XmlContent.GetElementById(custom404); - if (xmlNodeNotFound != null) - { - m_umbPage = new page(xmlNodeNotFound); - } - } - } + // + protected override void Render(HtmlTextWriter writer) + { + // do the original rendering + TextWriter sw = new StringWriter(); + base.Render(new HtmlTextWriter(sw)); + string text = sw.ToString(); - if (m_umbPage.Template != 0) - { - this.MasterPageFile = template.GetMasterPageName(m_umbPage.Template); - } - else - { - GenerateNotFoundContent(); - Response.End(); - } - } + // filter / parse internal links - although this should be done elsewhere! + text = template.ParseInternalLinks(text); - initUmbracoPage(); - } - } + // filter / add preview banner + if (UmbracoContext.Current.InPreviewMode) + { + Trace.Write("Runtime Engine", "Umbraco is running in preview mode."); - public Control pageContent = new Control(); + if (Response.ContentType == "text/HTML") // ASP.NET default value + { + int pos = text.ToLower().IndexOf(""); + if (pos > -1) + { + string htmlBadge = + String.Format(UmbracoSettings.PreviewBadge, + umbraco.IO.IOHelper.ResolveUrl(umbraco.IO.SystemDirectories.Umbraco), + umbraco.IO.IOHelper.ResolveUrl(umbraco.IO.SystemDirectories.Umbraco_client), + Server.UrlEncode(UmbracoContext.Current.HttpContext.Request.Path)); - protected void Page_Load(object sender, EventArgs e) - { + text = text.Substring(0, pos) + htmlBadge + text.Substring(pos, text.Length - pos); + } + } + } - if (ValidateRequest) - Request.ValidateInput(); + // render + writer.Write(text); + } - if (!String.IsNullOrEmpty(Request["umbDebugShowTrace"])) - { - if (!GlobalSettings.DebugMode) - { - Page.Trace.IsEnabled = false; - } - } - else - Page.Trace.IsEnabled = false; - } + // + void RenderNotFound() + { + // UmbracoModule has already set Response.Status to 404 + Response.Write("

Page not found

"); + UmbracoContext.Current.HttpContext.Response.Write("

No umbraco document matches the url '" + HttpUtility.HtmlEncode(Request.Url.ToString()) + "'.

"); - #region Web Form Designer generated code + // fixme - should try to get infos from the DocumentRequest? - protected override void OnInit(EventArgs e) - { - // - // CODEGEN: This call is required by the ASP.NET Web Form Designer. - // - InitializeComponent(); - - if (!UmbracoSettings.UseAspNetMasterPages) - initUmbracoPage(); - base.OnInit(e); - - // Add Umbraco header - if (!UmbracoSettings.RemoveUmbracoVersionHeader) - Response.AddHeader("X-Umbraco-Version", string.Format("{0}.{1}", GlobalSettings.VersionMajor, GlobalSettings.VersionMinor)); - } - - private void initUmbracoPage() - { - - RequestInitEventArgs e = new RequestInitEventArgs(); - e.Page = m_umbPage; - - if(m_umbPage != null) - e.PageId = m_umbPage.PageID; - - e.Context = System.Web.HttpContext.Current; - - FireBeforeRequestInit(e); - if (!e.Cancel) - { - if (!UmbracoSettings.EnableSplashWhileLoading || !content.Instance.isInitializing) - { - - if (m_umbPage != null) - { - // Add page elements to global items - try - { - - System.Web.HttpContext.Current.Items.Add("pageElements", m_umbPage.Elements); - - } - catch (ArgumentException) - { - - System.Web.HttpContext.Current.Items.Remove("pageElements"); - System.Web.HttpContext.Current.Items.Add("pageElements", m_umbPage.Elements); - } - - string tempCulture = m_umbPage.GetCulture(); - if (tempCulture != "") - { - System.Web.HttpContext.Current.Trace.Write("default.aspx", "Culture changed to " + tempCulture); - System.Threading.Thread.CurrentThread.CurrentCulture = - System.Globalization.CultureInfo.CreateSpecificCulture(tempCulture); - System.Threading.Thread.CurrentThread.CurrentUICulture = System.Threading.Thread.CurrentThread.CurrentCulture; - } - - if (!UmbracoSettings.UseAspNetMasterPages) - { - layoutControls.umbracoPageHolder pageHolder = new umbraco.layoutControls.umbracoPageHolder(); - pageHolder.ID = "umbPageHolder"; - Page.Controls.Add(pageHolder); - m_umbPage.RenderPage(m_umbPage.Template); - layoutControls.umbracoPageHolder umbPageHolder = - (layoutControls.umbracoPageHolder)Page.FindControl("umbPageHolder"); - umbPageHolder.Populate(m_umbPage); - } - - } - else - { - // If there's no published content, show friendly error - if (umbraco.content.Instance.XmlContent.SelectSingleNode("/root/*") == null) - Response.Redirect(IO.SystemDirectories.Config + "/splashes/noNodes.aspx"); - else - { - - GenerateNotFoundContent(); - } - } - } - else - { - Response.Redirect(IO.SystemDirectories.Config + "/splashes/booting.aspx?orgUrl=" + Request.Url); - } - - FireAfterRequestInit(e); - } - } - - private void GenerateNotFoundContent() - { - Response.StatusCode = 404; - Response.Write("

Page not found

"); - if (m_umbRequest != null) - HttpContext.Current.Response.Write("

No umbraco document matches the url '" + HttpUtility.HtmlEncode(Request.Url.ToString()) + "'

umbraco tried this to match it using this xpath query'" + m_umbRequest.PageXPathQuery + "')"); - else - HttpContext.Current.Response.Write("

No umbraco document matches the url '" + HttpUtility.HtmlEncode(Request.Url.ToString()) + "'

"); - Response.Write("

"); - Response.Write("

This page can be replaced with a custom 404 page by adding the id of the umbraco document to show as 404 page in the /config/umbracoSettings.config file. Just add the id to the '/settings/content/errors/error404' element.

"); - Response.Write("

For more information, visit information about custom 404 on the umbraco website.

"); - Response.Write("

This page is intentionally left ugly ;-)

"); - Response.Write(""); - } - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - } - - #endregion - - - - /// - /// The preinit event handler - /// - public delegate void RequestInitEventHandler(object sender, RequestInitEventArgs e); - /// - /// occurs before the umbraco page is initialized for rendering. - /// - public static event RequestInitEventHandler BeforeRequestInit; - /// - /// Raises the event. - /// - /// The instance containing the event data. - protected internal new virtual void FireBeforeRequestInit(RequestInitEventArgs e) - { - if (BeforeRequestInit != null) - BeforeRequestInit(this, e); - } - - /// - /// Occurs when [after save]. - /// - public static event RequestInitEventHandler AfterRequestInit; - /// - /// Raises the event. - /// - /// The instance containing the event data. - protected virtual void FireAfterRequestInit(RequestInitEventArgs e) - { - if (AfterRequestInit != null) - AfterRequestInit(this, e); - - } - } - - //Request Init Events Arguments - public class RequestInitEventArgs : System.ComponentModel.CancelEventArgs - { - public page Page { get; internal set; } - public HttpContext Context { get; internal set; } - public string Url { get; internal set; } - public int PageId { get; internal set; } - } + Response.Write("

This page can be replaced with a custom 404. Check the documentation for \"custom 404\".

"); + Response.Write("

This page is intentionally left ugly ;-)

"); + Response.Write(""); + } + } } diff --git a/src/Umbraco.Web/umbraco.presentation/page.cs b/src/Umbraco.Web/umbraco.presentation/page.cs index a81d093e46..c477e1f247 100644 --- a/src/Umbraco.Web/umbraco.presentation/page.cs +++ b/src/Umbraco.Web/umbraco.presentation/page.cs @@ -5,386 +5,372 @@ using System.Text; using System.Web; using System.Web.UI; using System.Xml; - +using Umbraco.Web.Routing; using umbraco.cms.businesslogic.property; using umbraco.cms.businesslogic.web; namespace umbraco { - /// - /// Summary description for page. - /// - public class page - { - #region private members and properties + /// + /// Summary description for page. + /// + public class page + { + const string TraceCategory = "UmbracoPage"; - private String pageName; - private int parentId; - private String writerName; - private String creatorName; - private String path; - private int nodeType; - private String nodeTypeAlias; - private String[] splitpath; - private DateTime createDate; - private DateTime updateDate; - private int pageID; - private Guid pageVersion; - private int template; + #region Private members and properties - - private Hashtable elements = new Hashtable(); - private StringBuilder pageContent = new StringBuilder(); - private Control pageContentControl = new Control(); + string pageName; + int parentId; + string writerName; + string creatorName; + string path; + int nodeType; + string nodeTypeAlias; + string[] splitpath; + DateTime createDate; + DateTime updateDate; + int pageID; + Guid pageVersion; + int template; - #endregion + Hashtable elements = new Hashtable(); + StringBuilder pageContent = new StringBuilder(); + Control pageContentControl = new Control(); - #region init + #endregion - /// - /// Constructor for creating a page in view mode (with viable content that's not yet published) - /// - /// The identifier of the page - /// The version to be displayed - public page(int Id, Guid Version) - { - pageID = Id; + #region Constructors - // create document object - Document d = new Document(Id, Version); + /// + /// Initializes a new instance of the class for a yet unpublished document, identified by its id and version. + /// + /// The identifier of the document. + /// The version to be displayed. + public page(int id, Guid version) + : this(new Document(id, version)) + { } - int tmpParentId = -1; - try - { - tmpParentId = d.Parent.Id; - } - catch - { - } - // create page - populatePageData(Id, d.Text, d.ContentType.Id, d.ContentType.Alias, d.User.Name, d.Creator.Name, d.CreateDateTime, - d.UpdateDate, d.Path, d.Version, tmpParentId); + /// + /// Initializes a new instance of the class for a yet unpublished document. + /// + /// The document. + public page(Document document) + { + HttpContext.Current.Trace.Write(TraceCategory, "Ctor(document)"); - // update page elements - var props = d.getProperties; - foreach (Property p in props) - { - string sValue = p.Value!=null ? p.Value.ToString() : String.Empty; - elements.Add(p.PropertyType.Alias, sValue); - } + var parent = document.Parent; - template = d.Template; + populatePageData(document.Id, + document.Text, document.ContentType.Id, document.ContentType.Alias, + document.User.Name, document.Creator.Name, document.CreateDateTime, document.UpdateDate, + document.Path, document.Version, parent == null ? -1 : parent.Id); + foreach (Property prop in document.GenericProperties) + { + string value = prop.Value != null ? prop.Value.ToString() : String.Empty; + elements.Add(prop.PropertyType.Alias, value); + } - HttpContext.Current.Trace.Write("umbracoPage", - "Pagedata loaded for " + pageName + " (ID: " + pageID.ToString() + - ", Version: " + pageVersion.ToString() + ")"); - //RenderPage(template); - } + template = document.Template; - private void populatePageData(int pageID, string pageName, int nodeType, string nodeTypeAlias, string writerName, string creatorName, - DateTime createDate, DateTime updateDate, string path, Guid pageVersion, - int parentId) - { - this.pageID = pageID; - this.pageName = pageName; - this.nodeType = nodeType; - this.nodeTypeAlias = nodeTypeAlias; - this.writerName = writerName; - this.creatorName = creatorName; - this.createDate = createDate; - this.updateDate = updateDate; - this.parentId = parentId; - this.path = path; - splitpath = path.Split(','); - this.pageVersion = pageVersion; + HttpContext.Current.Trace.Write(TraceCategory, string.Format("Loaded \"{0}\" (id={1}, version={2})", + this.PageName, this.pageID, this.pageVersion)); + } - // Update the elements hashtable - elements.Add("pageID", pageID); - elements.Add("parentID", parentId); - elements.Add("pageName", pageName); - elements.Add("nodeType", nodeType); - elements.Add("nodeTypeAlias", nodeTypeAlias); - elements.Add("writerName", writerName); - elements.Add("creatorName", creatorName); - elements.Add("createDate", createDate); - elements.Add("updateDate", updateDate); - elements.Add("path", path); - elements.Add("splitpath", splitpath); - elements.Add("pageVersion", pageVersion); - } + /// + /// Initializes a new instance of the class for a published document. + /// + /// The pointing to the document. + internal page(DocumentRequest docreq) + { + HttpContext.Current.Trace.Write(TraceCategory, "Ctor(documentRequest)"); - public page(XmlNode xmlNode) - { - pageID = Convert.ToInt32(xmlNode.Attributes.GetNamedItem("id").Value); - elements.Add("pageID", pageID); - try - { - pageName = xmlNode.Attributes.GetNamedItem("nodeName").Value; - elements.Add("pageName", pageName); - } - catch - { - } - try - { - parentId = int.Parse(xmlNode.Attributes.GetNamedItem("parentID").Value); - elements.Add("parentID", parentId); - } - catch - { - } + if (!docreq.HasNode) + throw new ArgumentException("Document request has no node.", "docreq"); - try - { - nodeType = Convert.ToInt32(xmlNode.Attributes.GetNamedItem("nodeType").Value); - nodeTypeAlias = UmbracoSettings.UseLegacyXmlSchema ? xmlNode.Attributes.GetNamedItem("nodeTypeAlias").Value : xmlNode.Name; - elements.Add("nodeType", nodeType); - elements.Add("nodeTypeAlias", nodeTypeAlias); - } - catch - { - } - try - { - writerName = xmlNode.Attributes.GetNamedItem("writerName").Value; - elements.Add("writerName", writerName); - } - catch - { - } - try - { - creatorName = xmlNode.Attributes.GetNamedItem("creatorName").Value; - elements.Add("creatorName", creatorName); - } - catch - { - } - try - { - createDate = Convert.ToDateTime(xmlNode.Attributes.GetNamedItem("createDate").Value); - elements.Add("createDate", createDate); - } - catch - { - } - try - { - updateDate = Convert.ToDateTime(xmlNode.Attributes.GetNamedItem("updateDate").Value); - elements.Add("updateDate", updateDate); - } - catch - { - } - try - { - pageVersion = new Guid(xmlNode.Attributes.GetNamedItem("version").Value); - elements.Add("pageVersion", pageVersion); - } - catch - { - } - try - { - path = xmlNode.Attributes.GetNamedItem("path").Value; - elements.Add("path", path); - splitpath = path.Split(','); - elements.Add("splitpath", splitpath); - } - catch - { - } + populatePageData(docreq.Node); - // Check for alternative template - if (HttpContext.Current.Items["altTemplate"] != null && - HttpContext.Current.Items["altTemplate"].ToString() != String.Empty) - { - template = - umbraco.cms.businesslogic.template.Template.GetTemplateIdFromAlias( - HttpContext.Current.Items["altTemplate"].ToString()); - elements.Add("template", template.ToString()); - } - else if (helper.Request("altTemplate") != String.Empty) - { - template = - umbraco.cms.businesslogic.template.Template.GetTemplateIdFromAlias(helper.Request("altTemplate").ToLower()); - elements.Add("template", template.ToString()); - } - if (template == 0) - { - try - { - template = Convert.ToInt32(xmlNode.Attributes.GetNamedItem("template").Value); - elements.Add("template", xmlNode.Attributes.GetNamedItem("template").Value); - } - catch - { - HttpContext.Current.Trace.Warn("umbracoPage", "No template defined"); - } - } - - - // Load all page elements - string xpath = UmbracoSettings.UseLegacyXmlSchema ? "./data" : "./* [not(@id)]"; - foreach (XmlNode dataNode in xmlNode.SelectNodes(xpath)) - { - if (dataNode == null) - continue; - // Only add those data-elements who has content (ie. a childnode) - if (dataNode.FirstChild != null) - { - string currentAlias = UmbracoSettings.UseLegacyXmlSchema - ? dataNode.Attributes.GetNamedItem("alias").Value - : dataNode.Name; + if (docreq.HasTemplate) + { + this.template = docreq.Template.Id; + elements["template"] = this.template.ToString(); + } - // Check for redirects - if (helper.IsNumeric(dataNode.FirstChild.Value) && - currentAlias == "umbracoRedirect" && - int.Parse(dataNode.FirstChild.Value) > 0) - { - HttpContext.Current.Response.Redirect(library.NiceUrl(int.Parse(dataNode.FirstChild.Value)), - true); - } - else - { - if (elements.ContainsKey(currentAlias)) - HttpContext.Current.Trace.Warn("umbracoPage", - "Aliases must be unique, an element with alias '" + - currentAlias + - "' has already been loaded!"); - else - { - elements.Add(currentAlias, - dataNode.FirstChild.Value - ); - HttpContext.Current.Trace.Write("umbracoPage", - "Element loaded: " + - currentAlias); - } - } - } - } + populateElementData(docreq.Node); - HttpContext.Current.Trace.Write("umbracoPage", - "Pagedata loaded for " + pageName + " (ID: " + pageID.ToString() + - ")"); + HttpContext.Current.Trace.Write(TraceCategory, string.Format("Loaded \"{0}\" (id={1}, version={2})", + this.PageName, this.pageID, this.pageVersion)); + } - // Save to cache - // System.Web.HttpRuntime.Cache.Insert("umbPage" + pageID.ToString(), this); - } + /// + /// Initializes a new instance of the class for a published document. + /// + /// The XmlNode representing the document. + public page(XmlNode node) + { + HttpContext.Current.Trace.Write(TraceCategory, "Ctor(xmlNode)"); - public void RenderPage(int Template) - { - if (Template != 0) - { - HttpContext.Current.Trace.Write("umbracoPage", "Loading template (ID: " + Template + ")"); - template templateDesign = new template(Template); + populatePageData(node); - HttpContext.Current.Trace.Write("page", "Template loaded"); - HttpContext.Current.Items["umbPageObject"] = this; + // Check for alternative template + if (HttpContext.Current.Items["altTemplate"] != null && + HttpContext.Current.Items["altTemplate"].ToString() != String.Empty) + { + template = + umbraco.cms.businesslogic.template.Template.GetTemplateIdFromAlias( + HttpContext.Current.Items["altTemplate"].ToString()); + elements.Add("template", template.ToString()); + } + else if (helper.Request("altTemplate") != String.Empty) + { + template = + umbraco.cms.businesslogic.template.Template.GetTemplateIdFromAlias(helper.Request("altTemplate").ToLower()); + elements.Add("template", template.ToString()); + } + if (template == 0) + { + try + { + template = Convert.ToInt32(node.Attributes.GetNamedItem("template").Value); + elements.Add("template", node.Attributes.GetNamedItem("template").Value); + } + catch + { + HttpContext.Current.Trace.Warn("umbracoPage", "No template defined"); + } + } - pageContentControl = templateDesign.ParseWithControls(this); - pageContent.Append(templateDesign.TemplateContent); - } - else - HttpContext.Current.Trace.Warn("page.RenderPage", "No template defined (value=0)"); - } + populateElementData(node); - public string GetCulture() - { - if (Domain.Exists(HttpContext.Current.Request.ServerVariables["SERVER_NAME"])) - { - Domain d = Domain.GetDomain(HttpContext.Current.Request.ServerVariables["SERVER_NAME"]); - return d.Language.CultureAlias; - } - else - { - for (int i = splitpath.Length - 1; i > 0; i--) - { - if (Domain.GetDomainsById(int.Parse(splitpath[i])).Length > 0) - { - return Domain.GetDomainsById(int.Parse(splitpath[i]))[0].Language.CultureAlias; - } - } - } - return ""; - } + HttpContext.Current.Trace.Write(TraceCategory, string.Format("Loaded \"{0}\" (id={1}, version={2})", + this.PageName, this.pageID, this.pageVersion)); + } - #endregion + #endregion - #region public properties + #region Initialize - public Control PageContentControl - { - get { return pageContentControl; } - } + void populatePageData(int pageID, + string pageName, int nodeType, string nodeTypeAlias, + string writerName, string creatorName, DateTime createDate, DateTime updateDate, + string path, Guid pageVersion, int parentId) + { + this.pageID = pageID; + this.pageName = pageName; + this.nodeType = nodeType; + this.nodeTypeAlias = nodeTypeAlias; + this.writerName = writerName; + this.creatorName = creatorName; + this.createDate = createDate; + this.updateDate = updateDate; + this.parentId = parentId; + this.path = path; + this.splitpath = path.Split(','); + this.pageVersion = pageVersion; - public String PageName - { - get { return pageName; } - } + // Update the elements hashtable + elements.Add("pageID", pageID); + elements.Add("parentID", parentId); + elements.Add("pageName", pageName); + elements.Add("nodeType", nodeType); + elements.Add("nodeTypeAlias", nodeTypeAlias); + elements.Add("writerName", writerName); + elements.Add("creatorName", creatorName); + elements.Add("createDate", createDate); + elements.Add("updateDate", updateDate); + elements.Add("path", path); + elements.Add("splitpath", splitpath); + elements.Add("pageVersion", pageVersion); + } - public int ParentId - { - get { return parentId; } - } + void populatePageData(XmlNode node) + { + String s; + DateTime dt; + Guid guid; + int i; - public string NodeTypeAlias - { - get { return nodeTypeAlias; } - } + if (int.TryParse(attrValue(node, "id"), out i)) + elements["pageID"] = this.pageID = i; - public int NodeType - { - get { return nodeType; } - } + if ((s = attrValue(node, "nodeName")) != null) + elements["pageName"] = this.pageName = s; - public String WriterName - { - get { return writerName; } - } + if (int.TryParse(attrValue(node, "parentId"), out i)) + elements["parentId"] = this.parentId = i; - public String CreatorName - { - get { return creatorName; } - } + if (int.TryParse(attrValue(node, "nodeType"), out i)) + elements["nodeType"] = this.nodeType = i; + if ((s = attrValue(node, "nodeTypeAlias")) != null) + elements["nodeTypeAlias"] = this.nodeTypeAlias = s; - public DateTime CreateDate - { - get { return createDate; } - } + if ((s = attrValue(node, "writerName")) != null) + elements["writerName"] = this.writerName = s; + if ((s = attrValue(node, "creatorName")) != null) + elements["creatorName"] = this.creatorName = s; - public DateTime UpdateDate - { - get { return updateDate; } - } + if (DateTime.TryParse(attrValue(node, "createDate"), out dt)) + elements["createDate"] = this.createDate = dt; + if (DateTime.TryParse(attrValue(node, "updateDate"), out dt)) + elements["updateDate"] = this.updateDate = dt; - public int PageID - { - get { return pageID; } - } + if (Guid.TryParse(attrValue(node, "pageVersion"), out guid)) + elements["pageVersion"] = this.pageVersion = guid; - public int Template - { - get { return template; } - } + if ((s = attrValue(node, "path")) != null) + { + elements["path"] = this.path = s; + elements["splitpath"] = this.splitpath = path.Split(','); + } + } - public Hashtable Elements - { - get { return elements; } - } + string attrValue(XmlNode node, string attributeName) + { + var attr = node.Attributes.GetNamedItem(attributeName); + var value = attr != null ? attr.Value : null; + return value; + } - public String PageContent - { - get { return pageContent.ToString(); } - } + void populateElementData(XmlNode node) + { + string xpath = "./* [not(@isDoc)]"; - public override string ToString() - { - return pageName; - } + foreach (XmlNode data in node.SelectNodes(xpath)) + { + // ignore empty elements + if (data.ChildNodes.Count == 0) + continue; - #endregion - } + string alias = data.Name; + string value = data.FirstChild.Value; + + // moved to DocumentRequest + UmbracoModule + //if (alias == "umbracoRedirect") + //{ + // int i; + // if (int.TryParse(value, out i)) + // HttpContext.Current.Response.Redirect(library.NiceUrl(int.Parse(data.FirstChild.Value)), true); + //} + + if (elements.ContainsKey(alias)) + { + HttpContext.Current.Trace.Warn(TraceCategory, + string.Format("Aliases must be unique, an element with alias \"{0}\" has already been loaded!", alias)); + } + else + { + elements[alias] = value; + HttpContext.Current.Trace.Write(TraceCategory, + string.Format("Load element \"{0}\"", alias)); + } + } + } + + #endregion + + #region Wtf? + + public void RenderPage(int templateId) + { + if (templateId != 0) + { + HttpContext.Current.Trace.Write(TraceCategory, string.Format("RenderPage: loading template (id={0})", templateId)); + template templateDesign = new template(templateId); + + HttpContext.Current.Trace.Write(TraceCategory, "RenderPage: template loaded"); + HttpContext.Current.Items["umbPageObject"] = this; + + pageContentControl = templateDesign.ParseWithControls(this); + pageContent.Append(templateDesign.TemplateContent); + } + else + HttpContext.Current.Trace.Warn(TraceCategory, "RenderPage: no template (id=0)"); + } + + #endregion + + #region Public properties + + public Control PageContentControl + { + get { return pageContentControl; } + } + + public string PageName + { + get { return pageName; } + } + + public int ParentId + { + get { return parentId; } + } + + public string NodeTypeAlias + { + get { return nodeTypeAlias; } + } + + public int NodeType + { + get { return nodeType; } + } + + public string WriterName + { + get { return writerName; } + } + + public string CreatorName + { + get { return creatorName; } + } + + public DateTime CreateDate + { + get { return createDate; } + } + + public DateTime UpdateDate + { + get { return updateDate; } + } + + public int PageID + { + get { return pageID; } + } + + public int Template + { + get { return template; } + } + + public Hashtable Elements + { + get { return elements; } + } + + public string PageContent + { + get { return pageContent.ToString(); } + } + + public string[] SplitPath + { + get { return this.splitpath; } + } + + #endregion + + #region ToString + + public override string ToString() + { + return pageName; + } + + #endregion + } }