00001 using System;
00002 using System.Web.Routing;
00003 using System.Web.Mvc;
00004 using System.Web;
00005 using N2.Engine;
00006 using System.Collections.Specialized;
00007 using System.Diagnostics;
00008
00009 namespace N2.Web.Mvc
00010 {
00014 public class ContentRoute : RouteBase
00015 {
00017 public static string ContentItemKey
00018 {
00019 get { return PathData.ItemQueryKey; }
00020 }
00022 public static string ContentPageKey
00023 {
00024 get { return PathData.PageQueryKey; }
00025 }
00027 public static string ContentPartKey
00028 {
00029 get { return PathData.PartQueryKey; }
00030 }
00032 public const string ContentEngineKey = "engine";
00034 public const string ControllerKey = "controller";
00036 public const string AreaKey = "area";
00038 public const string ActionKey = "action";
00039
00040 readonly IEngine engine;
00041 readonly IRouteHandler routeHandler;
00042 readonly IControllerMapper controllerMapper;
00043 readonly Route innerRoute;
00044
00045 public ContentRoute(IEngine engine)
00046 : this(engine, null, null, null)
00047 {
00048 }
00049
00050 public ContentRoute(IEngine engine, IRouteHandler routeHandler, IControllerMapper controllerMapper, Route innerRoute)
00051 {
00052 this.engine = engine;
00053 this.routeHandler = routeHandler ?? new MvcRouteHandler();
00054 this.controllerMapper = controllerMapper ?? engine.Resolve<IControllerMapper>();
00055 this.innerRoute = innerRoute ?? new Route("{controller}/{action}",
00056 new RouteValueDictionary(new { action = "Index" }),
00057 new RouteValueDictionary(),
00058 new RouteValueDictionary(new { this.engine }),
00059 this.routeHandler);
00060 }
00061
00066 public virtual RouteValueDictionary GetRouteValues(ContentItem item, RouteValueDictionary routeValues)
00067 {
00068 string actionName = "Index";
00069 if (routeValues.ContainsKey(ActionKey))
00070 actionName = (string)routeValues[ActionKey];
00071
00072 string controllerName = controllerMapper.GetControllerName(item.GetContentType());
00073 if (controllerName == null || !controllerMapper.ControllerHasAction(controllerName, actionName))
00074 return null;
00075
00076 var values = new RouteValueDictionary(routeValues);
00077
00078 foreach (var kvp in innerRoute.Defaults)
00079 if(!values.ContainsKey(kvp.Key))
00080 values[kvp.Key] = kvp.Value;
00081
00082 values[ControllerKey] = controllerName;
00083 values[ActionKey] = actionName;
00084 values[ContentItemKey] = item.ID;
00085 values[AreaKey] = innerRoute.DataTokens["area"];
00086
00087 return values;
00088 }
00089
00090 public override RouteData GetRouteData(HttpContextBase httpContext)
00091 {
00092 string path = httpContext.Request.AppRelativeCurrentExecutionFilePath;
00093 if (path.StartsWith(engine.ManagementPaths.GetManagementInterfaceUrl(), StringComparison.InvariantCultureIgnoreCase))
00094 return new RouteData(this, new StopRoutingHandler());
00095 if (path.EndsWith(".axd", StringComparison.InvariantCultureIgnoreCase))
00096 return new RouteData(this, new StopRoutingHandler());
00097 if (path.EndsWith(".n2.ashx", StringComparison.InvariantCultureIgnoreCase))
00098 return new RouteData(this, new StopRoutingHandler());
00099
00100 RouteData routeData = null;
00101
00102
00103 if(httpContext.Request.QueryString[ContentPartKey] != null)
00104 routeData = CheckForContentController(httpContext);
00105
00106
00107 if(routeData == null)
00108 routeData = GetRouteDataForPath(httpContext.Request);
00109
00110
00111 if(routeData == null)
00112 routeData = CheckForContentController(httpContext);
00113
00114 Debug.WriteLine("GetRouteData for '" + path + "' got values: " + (routeData != null ? routeData.Values.ToQueryString() : "(null)"));
00115 return routeData;
00116 }
00117
00118 private RouteData GetRouteDataForPath(HttpRequestBase request)
00119 {
00120
00121
00122 string host = (request.Url.IsDefaultPort) ? request.Url.Host : request.Url.Authority;
00123 string hostAndRawUrl = String.Format("{0}://{1}{2}", request.Url.Scheme, host, Url.ToAbsolute(request.AppRelativeCurrentExecutionFilePath));
00124 PathData td = engine.UrlParser.ResolvePath(hostAndRawUrl);
00125
00126 var page = td.CurrentPage;
00127
00128 var actionName = td.Action;
00129 if (string.IsNullOrEmpty(actionName))
00130 actionName = request.QueryString["action"] ?? "Index";
00131
00132 if (!string.IsNullOrEmpty(request.QueryString[PathData.PageQueryKey]))
00133 {
00134 int pageId;
00135 if (int.TryParse(request.QueryString[PathData.PageQueryKey], out pageId))
00136 {
00137 td.CurrentPage = page = engine.Persister.Get(pageId);
00138 }
00139 }
00140
00141 ContentItem part = null;
00142 if (!string.IsNullOrEmpty(request.QueryString[PathData.PartQueryKey]))
00143 {
00144
00145 int partId;
00146 if (int.TryParse(request.QueryString[PathData.PartQueryKey], out partId))
00147 td.CurrentItem = part = engine.Persister.Get(partId);
00148 }
00149
00150 if (page == null && part == null)
00151 return null;
00152 else if (page == null)
00153 page = part.ClosestPage();
00154
00155 var controllerName = controllerMapper.GetControllerName((part ?? page).GetContentType());
00156
00157 if (controllerName == null)
00158 return null;
00159
00160 if (actionName == null || !controllerMapper.ControllerHasAction(controllerName, actionName))
00161 return null;
00162
00163 var data = new RouteData(this, routeHandler);
00164
00165 foreach (var defaultPair in innerRoute.Defaults)
00166 data.Values[defaultPair.Key] = defaultPair.Value;
00167 foreach (var tokenPair in innerRoute.DataTokens)
00168 data.DataTokens[tokenPair.Key] = tokenPair.Value;
00169
00170 data.ApplyCurrentItem(controllerName, actionName, page, part);
00171 data.DataTokens[ContentEngineKey] = engine;
00172
00173 return data;
00174 }
00175
00177 private RouteData CheckForContentController(HttpContextBase context)
00178 {
00179 var routeData = innerRoute.GetRouteData(context);
00180
00181 if (routeData == null)
00182 return null;
00183
00184 var controllerName = Convert.ToString(routeData.Values[ControllerKey]);
00185 var actionName = Convert.ToString(routeData.Values[ActionKey]);
00186
00187 if (!controllerMapper.ControllerHasAction(controllerName, actionName))
00188 return null;
00189
00190
00191 var part = ApplyContent(routeData, context.Request.QueryString, ContentPartKey);
00192 if (part != null)
00193 routeData.ApplyContentItem(ContentItemKey, part);
00194 else
00195 ApplyContent(routeData, context.Request.QueryString, ContentItemKey);
00196
00197 var page = ApplyContent(routeData, context.Request.QueryString, ContentPageKey);
00198 if (page == null)
00199 routeData.ApplyContentItem(ContentPageKey, part.ClosestPage());
00200 routeData.DataTokens[ContentEngineKey] = engine;
00201
00202 return routeData;
00203 }
00204
00205 private ContentItem ApplyContent(RouteData routeData, NameValueCollection query, string key)
00206 {
00207 int id;
00208 if (int.TryParse(query[key], out id))
00209 {
00210 var item = engine.Persister.Get(id);
00211 routeData.ApplyContentItem(key, item);
00212 return item;
00213 }
00214 return null;
00215 }
00216
00217
00218
00219 public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
00220 {
00221 ContentItem item;
00222
00223 Debug.WriteLine("GetVirtualPath for values: " + values.ToQueryString());
00224
00225 values = new RouteValueDictionary(values);
00226
00227
00228 if (!TryConvertContentToController(requestContext, values, ContentItemKey, out item))
00229 {
00230
00231 item = requestContext.CurrentItem();
00232
00233 if (item == null)
00234
00235 return null;
00236
00237 if (!RequestedControllerMatchesItemController(values, item))
00238
00239 return null;
00240 }
00241
00242 if (item.IsPage)
00243 return ResolveContentActionUrl(requestContext, values, item);
00244
00245
00246 ContentItem page = values.CurrentItem<ContentItem>(ContentPageKey, engine.Persister);
00247 if (page != null)
00248
00249 return ResolvePartActionUrl(requestContext, values, page, item);
00250
00251 page = requestContext.CurrentPage<ContentItem>();
00252 if (page != null && page.IsPage)
00253
00254 return ResolvePartActionUrl(requestContext, values, page, item);
00255
00256 page = item.ClosestPage();
00257 if (page != null && page.IsPage)
00258
00259 return ResolvePartActionUrl(requestContext, values, page, item);
00260
00261
00262 return null;
00263 }
00264
00265 private bool RequestedControllerMatchesItemController(RouteValueDictionary values, ContentItem item)
00266 {
00267 string requestedController = values[ControllerKey] as string;
00268 if (requestedController == null)
00269 return true;
00270
00271 string itemController = controllerMapper.GetControllerName(item.GetContentType());
00272
00273 return string.Equals(requestedController, itemController, StringComparison.InvariantCultureIgnoreCase);
00274 }
00275
00276 private VirtualPathData ResolvePartActionUrl(RequestContext requestContext, RouteValueDictionary values, ContentItem page, ContentItem item)
00277 {
00278 values[ContentPageKey] = page.ID;
00279 values[ContentPartKey] = item.ID;
00280 var vpd = innerRoute.GetVirtualPath(requestContext, values);
00281 if (vpd != null)
00282 vpd.Route = this;
00283 return vpd;
00284 }
00285
00286 private VirtualPathData ResolveContentActionUrl(RequestContext requestContext, RouteValueDictionary values, ContentItem item)
00287 {
00288 const string controllerPlaceHolder = "---(CTRL)---";
00289 const string areaPlaceHolder = "---(AREA)---";
00290
00291 values[ControllerKey] = controllerPlaceHolder;
00292 bool useAreas = innerRoute.DataTokens.ContainsKey("area");
00293 if (useAreas)
00294 values[AreaKey] = areaPlaceHolder;
00295
00296 VirtualPathData vpd = innerRoute.GetVirtualPath(requestContext, values);
00297 if (vpd == null)
00298 return null;
00299 vpd.Route = this;
00300
00301 string relativeUrl = Url.ToRelative(item.Url);
00302 Url actionUrl = vpd.VirtualPath
00303 .Replace(controllerPlaceHolder, Url.PathPart(relativeUrl).TrimStart('~'));
00304 if (useAreas)
00305 actionUrl = actionUrl.SetPath(actionUrl.Path.Replace(areaPlaceHolder + "/", ""));
00306
00307 foreach (var kvp in Url.ParseQueryString(Url.QueryPart(relativeUrl)))
00308 {
00309 if ("item".Equals(kvp.Value, StringComparison.InvariantCultureIgnoreCase))
00310 continue;
00311
00312 actionUrl = actionUrl.AppendQuery(kvp.Key, kvp.Value);
00313 }
00314 vpd.VirtualPath = actionUrl.PathAndQuery.TrimStart('/');
00315 return vpd;
00316 }
00317
00318 private bool TryConvertContentToController(RequestContext request, RouteValueDictionary values, string key, out ContentItem item)
00319 {
00320 if (!values.ContainsKey(key))
00321 {
00322 item = null;
00323 return false;
00324 }
00325
00326 object value = values[key];
00327 item = value as ContentItem;
00328 if (item == null && value is int)
00329 {
00330 item = engine.Persister.Get((int)value);
00331 }
00332
00333 if (item == null || item == request.CurrentItem())
00334
00335 return false;
00336
00337
00338 values.Remove(key);
00339 values[ControllerKey] = controllerMapper.GetControllerName(item.GetContentType());
00340
00341 return true;
00342 }
00343 }
00344 }