diff --git a/src/MvcSiteMapProvider/MvcSiteMapProvider/Web/Mvc/IMvcContextFactory.cs b/src/MvcSiteMapProvider/MvcSiteMapProvider/Web/Mvc/IMvcContextFactory.cs
index d9299898..03669e90 100644
--- a/src/MvcSiteMapProvider/MvcSiteMapProvider/Web/Mvc/IMvcContextFactory.cs
+++ b/src/MvcSiteMapProvider/MvcSiteMapProvider/Web/Mvc/IMvcContextFactory.cs
@@ -13,9 +13,11 @@ namespace MvcSiteMapProvider.Web.Mvc
public interface IMvcContextFactory
{
HttpContextBase CreateHttpContext();
- HttpContextBase CreateHttpContext(ISiteMapNode node, Uri nodeUri, TextWriter writer);
+ HttpContextBase CreateHttpContext(ISiteMapNode node, Uri uri, TextWriter writer);
RequestContext CreateRequestContext(ISiteMapNode node, RouteData routeData);
RequestContext CreateRequestContext();
+ RequestContext CreateRequestContext(HttpContextBase httpContext);
+ RequestContext CreateRequestContext(HttpContextBase httpContext, RouteData routeData);
ControllerContext CreateControllerContext(RequestContext requestContext, ControllerBase controller);
IRequestCache GetRequestCache();
RouteCollection GetRoutes();
diff --git a/src/MvcSiteMapProvider/MvcSiteMapProvider/Web/Mvc/MvcContextFactory.cs b/src/MvcSiteMapProvider/MvcSiteMapProvider/Web/Mvc/MvcContextFactory.cs
index 0f043ec2..f2caecc4 100644
--- a/src/MvcSiteMapProvider/MvcSiteMapProvider/Web/Mvc/MvcContextFactory.cs
+++ b/src/MvcSiteMapProvider/MvcSiteMapProvider/Web/Mvc/MvcContextFactory.cs
@@ -27,16 +27,16 @@ protected virtual HttpContextBase CreateHttpContext(ISiteMapNode node)
return new SiteMapHttpContext(HttpContext.Current, node);
}
- public virtual HttpContextBase CreateHttpContext(ISiteMapNode node, Uri nodeUri, TextWriter writer)
+ public virtual HttpContextBase CreateHttpContext(ISiteMapNode node, Uri uri, TextWriter writer)
{
if (node == null)
throw new ArgumentNullException("node");
- if (nodeUri == null)
- throw new ArgumentNullException("nodeUri");
+ if (uri == null)
+ throw new ArgumentNullException("uri");
if (writer == null)
throw new ArgumentNullException("writer");
- var request = new HttpRequest(string.Empty, nodeUri.ToString(), nodeUri.Query);
+ var request = new HttpRequest(string.Empty, uri.ToString(), uri.Query);
var response = new HttpResponse(writer);
var httpContext = new HttpContext(request, response);
return new SiteMapHttpContext(httpContext, node);
@@ -61,6 +61,16 @@ public virtual RequestContext CreateRequestContext()
return new RequestContext(httpContext, new RouteData());
}
+ public virtual RequestContext CreateRequestContext(HttpContextBase httpContext)
+ {
+ return new RequestContext(httpContext, new RouteData());
+ }
+
+ public virtual RequestContext CreateRequestContext(HttpContextBase httpContext, RouteData routeData)
+ {
+ return new RequestContext(httpContext, routeData);
+ }
+
public virtual ControllerContext CreateControllerContext(RequestContext requestContext, ControllerBase controller)
{
if (requestContext == null)
@@ -88,13 +98,13 @@ public virtual RouteCollection GetRoutes()
public virtual IUrlHelper CreateUrlHelper(RequestContext requestContext)
{
- return new UrlHelperAdapter(requestContext);
+ return new UrlHelperAdapter(requestContext, this.GetRoutes());
}
public virtual IUrlHelper CreateUrlHelper()
{
var requestContext = this.CreateRequestContext();
- return new UrlHelperAdapter(requestContext);
+ return new UrlHelperAdapter(requestContext, this.GetRoutes());
}
public virtual AuthorizationContext CreateAuthorizationContext(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
diff --git a/src/MvcSiteMapProvider/MvcSiteMapProvider/Web/Mvc/UrlHelperAdapter.cs b/src/MvcSiteMapProvider/MvcSiteMapProvider/Web/Mvc/UrlHelperAdapter.cs
index 36596261..2cf5a897 100644
--- a/src/MvcSiteMapProvider/MvcSiteMapProvider/Web/Mvc/UrlHelperAdapter.cs
+++ b/src/MvcSiteMapProvider/MvcSiteMapProvider/Web/Mvc/UrlHelperAdapter.cs
@@ -12,12 +12,12 @@ namespace MvcSiteMapProvider.Web
public class UrlHelperAdapter
: UrlHelper, IUrlHelper
{
- public UrlHelperAdapter(RequestContext requestContext)
+ private UrlHelperAdapter(RequestContext requestContext)
: base(requestContext)
{
}
- private UrlHelperAdapter(RequestContext requestContext, RouteCollection routeCollection)
+ public UrlHelperAdapter(RequestContext requestContext, RouteCollection routeCollection)
: base(requestContext, routeCollection)
{
}
diff --git a/src/MvcSiteMapProvider/MvcSiteMapProvider/Web/UrlResolver/SiteMapNodeUrlResolver.cs b/src/MvcSiteMapProvider/MvcSiteMapProvider/Web/UrlResolver/SiteMapNodeUrlResolver.cs
index fb9a074f..4f5461e8 100644
--- a/src/MvcSiteMapProvider/MvcSiteMapProvider/Web/UrlResolver/SiteMapNodeUrlResolver.cs
+++ b/src/MvcSiteMapProvider/MvcSiteMapProvider/Web/UrlResolver/SiteMapNodeUrlResolver.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.IO;
using System.Web;
using System.Web.Routing;
using MvcSiteMapProvider.Web.Mvc;
@@ -42,7 +43,7 @@ IUrlPath urlPath
/// The resolved URL.
public override string ResolveUrl(ISiteMapNode node, string area, string controller, string action, IDictionary routeValues)
{
- if (!String.IsNullOrEmpty(node.UnresolvedUrl))
+ if (!string.IsNullOrEmpty(node.UnresolvedUrl))
{
return this.ResolveVirtualPath(node);
}
@@ -63,8 +64,28 @@ protected virtual string ResolveVirtualPath(ISiteMapNode node)
protected virtual string ResolveRouteUrl(ISiteMapNode node, string area, string controller, string action, IDictionary routeValues)
{
- string result = String.Empty;
- var urlHelper = mvcContextFactory.CreateUrlHelper();
+ string result = string.Empty;
+ // Create a TextWriter with null stream as a backing stream
+ // which doesn't consume resources
+ using (var nullWriter = new StreamWriter(Stream.Null))
+ {
+ var requestContext = this.CreateRequestContext(node, true, nullWriter);
+ result = this.ResolveRouteUrl(node, area, controller, action, routeValues, requestContext);
+ }
+
+ if (string.IsNullOrEmpty(result))
+ {
+ // fixes #115 - UrlResolver should not throw exception.
+ return urlPath.MakeVirtualPathAppAbsolute(urlPath.Combine(urlPath.AppDomainAppVirtualPath, "~/")) + Guid.NewGuid().ToString();
+ }
+
+ return result;
+ }
+
+ protected virtual string ResolveRouteUrl(ISiteMapNode node, string area, string controller, string action, IDictionary routeValues, RequestContext requestContext)
+ {
+ string result = string.Empty;
+ var urlHelper = this.mvcContextFactory.CreateUrlHelper(requestContext);
var routeValueDictionary = new RouteValueDictionary(routeValues);
if (!string.IsNullOrEmpty(node.Route))
@@ -77,13 +98,29 @@ protected virtual string ResolveRouteUrl(ISiteMapNode node, string area, string
result = urlHelper.Action(action, controller, routeValueDictionary);
}
- if (string.IsNullOrEmpty(result))
+ return result;
+ }
+
+ protected virtual HttpContextBase CreateHttpContext(ISiteMapNode node, TextWriter writer)
+ {
+ var currentHttpContext = this.mvcContextFactory.CreateHttpContext();
+
+ // Create a URI with the home page and no query string values.
+ var uri = new Uri(currentHttpContext.Request.Url, "/");
+ return this.mvcContextFactory.CreateHttpContext(node, uri, writer);
+ }
+
+ protected virtual RequestContext CreateRequestContext(ISiteMapNode node, bool includeAmbientRequestValues, TextWriter writer)
+ {
+ if (!includeAmbientRequestValues)
{
- // fixes #115 - UrlResolver should not throw exception.
- return urlPath.MakeVirtualPathAppAbsolute(urlPath.Combine(urlPath.AppDomainAppVirtualPath, "~/")) + Guid.NewGuid().ToString();
+ var httpContext = this.CreateHttpContext(node, writer);
+ return this.mvcContextFactory.CreateRequestContext(httpContext);
+ }
+ else
+ {
+ return this.mvcContextFactory.CreateRequestContext();
}
-
- return result;
}
}
}
diff --git a/src/MvcSiteMapProvider/MvcSiteMapProvider/Xml/MvcSiteMapSchema.xsd b/src/MvcSiteMapProvider/MvcSiteMapProvider/Xml/MvcSiteMapSchema.xsd
new file mode 100644
index 00000000..2a291612
--- /dev/null
+++ b/src/MvcSiteMapProvider/MvcSiteMapProvider/Xml/MvcSiteMapSchema.xsd
@@ -0,0 +1,319 @@
+
+
+
+
+
+ MvcSiteMapProvider SiteMap File Schema
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ (autogenerated) The unique identifier for the node.
+
+
+
+
+
+
+ (autogenerated based on routes) The URL represented by the node.
+
+
+
+
+
+
+ Can optionally be specified to bind the node URL generation to a specific route.
+
+
+
+
+
+
+ The title of the node.
+
+
+
+
+
+
+ Description of the node.
+
+
+
+
+
+
+ The MVC area for the sitemap node. If not specified, it will be inherited from a node higher in the hierarchy.
+
+
+
+
+
+
+ The MVC controller for the sitemap node. Case-sensitive! If not specified, it will be inherited from a node higher in the hierarchy.
+
+
+
+
+
+
+ The MVC action method for the sitemap node. If not specified, it will be inherited from a node higher in the hierarchy.
+
+
+
+
+
+
+ Comma-separated list of roles allowed to access the node and its child nodes.
+
+
+
+
+
+
+ Optional resource key.
+
+
+
+
+
+
+ Is the node clickable or just a grouping node? Default is true.
+
+
+
+
+
+
+ Optional. Route parameters that should be inherited from the parent sitemap node. This is not a replacement for the SiteMapPreserveRouteDataAttribute.
+
+
+
+
+
+
+ A class name implementing MvcSiteMapProvider.IDynamicNodeProvider and providing dynamic nodes for the site map.
+
+
+
+
+
+
+ Class that will be used to generate URLs for sitemap nodes.
+
+
+
+
+
+
+ Should the resolved URL be cached (true) or resolved on each request (false)? Default is true.
+
+
+
+
+
+
+ Example from demo: visibility="SiteMapPathHelper,!*"
+
+
+
+
+
+
+
+ Class that will be used to determine visibility for a sitemap node.
+
+
+
+
+
+
+ Optional target frame for the node link.
+
+
+
+
+
+
+
+ Optional image to be shown by supported HtmlHelpers.
+
+
+
+
+
+
+
+ Optional preserved route parameter names (= values that will be used from the current request route).
+
+
+
+
+
+
+
+ Optional. The primary URL for what is completely or mostly duplicated content on the current node.
+ The value may be an absolute URL (in the current site or in an external site), a rooted URL starting with '~/', or a relative URL.
+
+
+
+
+
+
+ Optional. The key value for the node that contains the primary copy of the content for the current node, if it is largely similar.
+
+
+
+
+
+
+
+ Optional. A space-delimited list of values to apply to the robots meta tag. These will apply to all robots.
+
+
+
+
+
+
+
+ The HTTP method that will be used to check node accessibility. This value is used to select correct controller action if a controller has multiple action accepting different HTTP verbs. Set to * to use HTTP method of current request.
+
+
+
+
+
+
+
+ Optional. Sort order of this node relative to its sibling nodes (whether they are defined in XML or in code).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Last modified date for the node. Will be output to sitemaps XML for search engines.
+
+
+
+
+
+
+
+ Change frequency for the node. Will be output to sitemaps XML for search engines.
+
+
+
+
+
+
+ Update priority for the node. Will be output to sitemaps XML for search engines.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file