From 64a54157256b18b94343e6ddc15265517fbd6f96 Mon Sep 17 00:00:00 2001 From: Erik Porter Date: Mon, 8 Mar 2010 23:54:18 -0800 Subject: [PATCH 1/3] Add a FollowReturnUrl attribute to honor ReturnUrl in the querystring --HG-- branch : dev --- .../Parts/Common.Body.Manage.ascx | 2 +- .../Controllers/BlogPostAdminController.cs | 7 ++-- .../Controllers/AdminController.cs | 7 ++-- .../RouteValueDictionaryExtensions.cs | 21 ++++++++++ .../FollowReturnUrlAttribute.cs | 8 ++++ .../FollowReturnUrl/FollowReturnUrlFilter.cs | 42 +++++++++++++++++++ src/Orchard/Mvc/Html/ContentItemExtensions.cs | 12 +++++- src/Orchard/Mvc/Html/HtmlHelperExtensions.cs | 4 +- src/Orchard/Orchard.csproj | 3 ++ 9 files changed, 95 insertions(+), 11 deletions(-) create mode 100644 src/Orchard/Extensions/RouteValueDictionaryExtensions.cs create mode 100644 src/Orchard/Mvc/FollowReturnUrl/FollowReturnUrlAttribute.cs create mode 100644 src/Orchard/Mvc/FollowReturnUrl/FollowReturnUrlFilter.cs diff --git a/src/Orchard.Web/Core/Common/Views/DisplayTemplates/Parts/Common.Body.Manage.ascx b/src/Orchard.Web/Core/Common/Views/DisplayTemplates/Parts/Common.Body.Manage.ascx index a8d07423b..ab0c47ace 100644 --- a/src/Orchard.Web/Core/Common/Views/DisplayTemplates/Parts/Common.Body.Manage.ascx +++ b/src/Orchard.Web/Core/Common/Views/DisplayTemplates/Parts/Common.Body.Manage.ascx @@ -1,5 +1,5 @@ <%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl" %> <%@ Import Namespace="Orchard.Core.Common.ViewModels"%>
- <%=Html.ItemEditLink("Edit", Model.BodyAspect.ContentItem) %> + <%=Html.ItemEditLinkWithReturnUrl(_Encoded("Edit").ToString(), Model.BodyAspect.ContentItem) %>
\ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/BlogPostAdminController.cs b/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/BlogPostAdminController.cs index 3333b4c96..a9bd31f3b 100644 --- a/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/BlogPostAdminController.cs +++ b/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/BlogPostAdminController.cs @@ -7,6 +7,7 @@ using Orchard.Blogs.ViewModels; using Orchard.ContentManagement; using Orchard.Localization; using Orchard.Mvc.AntiForgery; +using Orchard.Mvc.FollowReturnUrl; using Orchard.Mvc.Results; using Orchard.UI.Admin; using Orchard.UI.Notify; @@ -100,17 +101,17 @@ namespace Orchard.Blogs.Controllers { return View(model); } - [HttpPost, ActionName("Edit")] + [HttpPost, ActionName("Edit"), FollowReturnUrl] public ActionResult EditPOST(string blogSlug, int postId) { if (!Services.Authorizer.Authorize(Permissions.EditBlogPost, T("Couldn't edit blog post"))) return new HttpUnauthorizedResult(); - Blog blog = _blogService.Get(blogSlug); + var blog = _blogService.Get(blogSlug); if (blog == null) return new NotFoundResult(); // Get draft (create a new version if needed) - BlogPost post = _blogPostService.Get(postId, VersionOptions.DraftRequired); + var post = _blogPostService.Get(postId, VersionOptions.DraftRequired); if (post == null) return new NotFoundResult(); diff --git a/src/Orchard.Web/Modules/Orchard.Pages/Controllers/AdminController.cs b/src/Orchard.Web/Modules/Orchard.Pages/Controllers/AdminController.cs index affd72536..1ce581847 100644 --- a/src/Orchard.Web/Modules/Orchard.Pages/Controllers/AdminController.cs +++ b/src/Orchard.Web/Modules/Orchard.Pages/Controllers/AdminController.cs @@ -7,6 +7,7 @@ using JetBrains.Annotations; using Orchard.Localization; using Orchard.ContentManagement; using Orchard.Mvc.AntiForgery; +using Orchard.Mvc.FollowReturnUrl; using Orchard.Mvc.Results; using Orchard.Pages.Drivers; using Orchard.Pages.Models; @@ -169,9 +170,9 @@ namespace Orchard.Pages.Controllers { return View(model); } - [HttpPost, ActionName("Edit")] + [HttpPost, ActionName("Edit"), FollowReturnUrl] public ActionResult EditPOST(int id) { - Page page = _pageService.GetPageOrDraft(id); + var page = _pageService.GetPageOrDraft(id); if (page == null) return new NotFoundResult(); @@ -209,7 +210,7 @@ namespace Orchard.Pages.Controllers { break; } - return RedirectToAction("Edit", "Admin", new { id = model.Page.Item.ContentItem.Id }); + return RedirectToAction("Edit", "Admin", new {id = model.Page.Item.ContentItem.Id}); } public ActionResult DiscardDraft(int id) { diff --git a/src/Orchard/Extensions/RouteValueDictionaryExtensions.cs b/src/Orchard/Extensions/RouteValueDictionaryExtensions.cs new file mode 100644 index 000000000..db080731b --- /dev/null +++ b/src/Orchard/Extensions/RouteValueDictionaryExtensions.cs @@ -0,0 +1,21 @@ +using System.Web.Routing; + +namespace Orchard.Extensions { + public static class RouteValueDictionaryExtensions { + public static RouteValueDictionary Merge(this RouteValueDictionary dictionary, object values) { + return values == null ? dictionary : dictionary.Merge(new RouteValueDictionary(values)); + } + + public static RouteValueDictionary Merge(this RouteValueDictionary dictionary, RouteValueDictionary dictionaryToMerge) { + if (dictionaryToMerge == null) + return dictionary; + + var newDictionary = new RouteValueDictionary(dictionary); + + foreach (var valueDictionary in dictionaryToMerge) + newDictionary[valueDictionary.Key] = valueDictionary.Value; + + return newDictionary; + } + } +} \ No newline at end of file diff --git a/src/Orchard/Mvc/FollowReturnUrl/FollowReturnUrlAttribute.cs b/src/Orchard/Mvc/FollowReturnUrl/FollowReturnUrlAttribute.cs new file mode 100644 index 000000000..98c6860a7 --- /dev/null +++ b/src/Orchard/Mvc/FollowReturnUrl/FollowReturnUrlAttribute.cs @@ -0,0 +1,8 @@ +using System; +using System.Web.Mvc; + +namespace Orchard.Mvc.FollowReturnUrl { + [AttributeUsage(AttributeTargets.Method)] + public class FollowReturnUrlAttribute : FilterAttribute { + } +} \ No newline at end of file diff --git a/src/Orchard/Mvc/FollowReturnUrl/FollowReturnUrlFilter.cs b/src/Orchard/Mvc/FollowReturnUrl/FollowReturnUrlFilter.cs new file mode 100644 index 000000000..cd5bb31a5 --- /dev/null +++ b/src/Orchard/Mvc/FollowReturnUrl/FollowReturnUrlFilter.cs @@ -0,0 +1,42 @@ +using System; +using System.Web.Mvc; +using Orchard.Mvc.Filters; + +namespace Orchard.Mvc.FollowReturnUrl { + public class FollowReturnUrlFilter : FilterProvider, IActionFilter { + public void OnActionExecuting(ActionExecutingContext filterContext) { + } + + public void OnActionExecuted(ActionExecutedContext filterContext) { + var attributes = + (FollowReturnUrlAttribute[]) + filterContext.ActionDescriptor.GetCustomAttributes(typeof (FollowReturnUrlAttribute), false); + + if (attributes.Length <= 0) { + return; + } + + var request = filterContext.HttpContext.Request; + Uri returnUrl = null; + try { + returnUrl = new Uri(request.QueryString["ReturnUrl"]); + } + catch { + try { + returnUrl = + new Uri(string.Format("{0}://{1}{2}{3}", request.Url.Scheme, request.Url.Host, + request.Url.Port != 80 ? ":" + request.Url.Port : "", + request.QueryString["ReturnUrl"])); + } + catch {} + } + + if (returnUrl != null && + returnUrl.Scheme == request.Url.Scheme && + returnUrl.Port == request.Url.Port && + returnUrl.Host == request.Url.Host) { + filterContext.Result = new RedirectResult(returnUrl.ToString()); + } + } + } +} \ No newline at end of file diff --git a/src/Orchard/Mvc/Html/ContentItemExtensions.cs b/src/Orchard/Mvc/Html/ContentItemExtensions.cs index 0c7b665e2..8096ff5a3 100644 --- a/src/Orchard/Mvc/Html/ContentItemExtensions.cs +++ b/src/Orchard/Mvc/Html/ContentItemExtensions.cs @@ -2,7 +2,7 @@ using System.Web.Mvc; using System.Web.Mvc.Html; using Orchard.ContentManagement; -using Orchard.Mvc.ViewModels; +using Orchard.Extensions; namespace Orchard.Mvc.Html { public static class ContentItemExtensions { @@ -29,7 +29,15 @@ namespace Orchard.Mvc.Html { return ItemDisplayLink(html, null, content); } + public static MvcHtmlString ItemEditLinkWithReturnUrl(this HtmlHelper html, string linkText, IContent content) { + return html.ItemEditLink(linkText, content, new {ReturnUrl = html.ViewContext.HttpContext.Request.RawUrl}); + } + public static MvcHtmlString ItemEditLink(this HtmlHelper html, string linkText, IContent content) { + return html.ItemEditLink(linkText, content, null); + } + + public static MvcHtmlString ItemEditLink(this HtmlHelper html, string linkText, IContent content, object additionalRouteValues) { var metadata = content.ContentItem.ContentManager.GetItemMetadata(content); if (metadata.EditorRouteValues == null) return null; @@ -37,7 +45,7 @@ namespace Orchard.Mvc.Html { return html.ActionLink( linkText ?? metadata.DisplayText ?? "edit", Convert.ToString(metadata.EditorRouteValues["action"]), - metadata.EditorRouteValues); + metadata.EditorRouteValues.Merge(additionalRouteValues)); } public static MvcHtmlString ItemEditLink(this HtmlHelper html, IContent content) { diff --git a/src/Orchard/Mvc/Html/HtmlHelperExtensions.cs b/src/Orchard/Mvc/Html/HtmlHelperExtensions.cs index 6f8605355..e9d0397b7 100644 --- a/src/Orchard/Mvc/Html/HtmlHelperExtensions.cs +++ b/src/Orchard/Mvc/Html/HtmlHelperExtensions.cs @@ -231,7 +231,7 @@ namespace Orchard.Mvc.Html { #region BeginFormAntiForgeryPost public static MvcForm BeginFormAntiForgeryPost(this HtmlHelper htmlHelper) { - return htmlHelper.BeginFormAntiForgeryPost(htmlHelper.ViewContext.HttpContext.Request.Url.AbsolutePath, FormMethod.Post, new RouteValueDictionary()); + return htmlHelper.BeginFormAntiForgeryPost(htmlHelper.ViewContext.HttpContext.Request.Url.PathAndQuery, FormMethod.Post, new RouteValueDictionary()); } public static MvcForm BeginFormAntiForgeryPost(this HtmlHelper htmlHelper, string formAction) { @@ -247,7 +247,7 @@ namespace Orchard.Mvc.Html { } public static MvcForm BeginFormAntiForgeryPost(this HtmlHelper htmlHelper, string formAction, FormMethod formMethod, IDictionary htmlAttributes) { - TagBuilder tagBuilder = new TagBuilder("form"); + var tagBuilder = new TagBuilder("form"); tagBuilder.MergeAttributes(htmlAttributes); tagBuilder.MergeAttribute("action", formAction); diff --git a/src/Orchard/Orchard.csproj b/src/Orchard/Orchard.csproj index f133ba627..f20ebe744 100644 --- a/src/Orchard/Orchard.csproj +++ b/src/Orchard/Orchard.csproj @@ -151,12 +151,15 @@ + + + ASPXCodeBehind From b435e8153377aabc58612c6ce5976a0e9746a375 Mon Sep 17 00:00:00 2001 From: Erik Porter Date: Tue, 9 Mar 2010 00:41:17 -0800 Subject: [PATCH 2/3] Converted LogOn, LogOff, AccessDenied to use the new FollowReturnUrl attribute --HG-- branch : dev --- .../Controllers/AccountController.cs | 33 ++++++++------- .../ViewModels/LogOnViewModel.cs | 5 +-- .../Orchard.Users/Views/Account/LogOn.ascx | 42 +++++++++---------- 3 files changed, 38 insertions(+), 42 deletions(-) diff --git a/src/Orchard.Web/Modules/Orchard.Users/Controllers/AccountController.cs b/src/Orchard.Web/Modules/Orchard.Users/Controllers/AccountController.cs index fe943792e..ee32a9988 100644 --- a/src/Orchard.Web/Modules/Orchard.Users/Controllers/AccountController.cs +++ b/src/Orchard.Web/Modules/Orchard.Users/Controllers/AccountController.cs @@ -5,6 +5,7 @@ using System.Security.Principal; using System.Web.Mvc; using System.Web.Security; using Orchard.Logging; +using Orchard.Mvc.FollowReturnUrl; using Orchard.Mvc.ViewModels; using Orchard.Security; using Orchard.Users.Services; @@ -17,7 +18,6 @@ namespace Orchard.Users.Controllers { private readonly IMembershipService _membershipService; private readonly IUserService _userService; - public AccountController( IAuthenticationService authenticationService, IMembershipService membershipService, @@ -30,46 +30,47 @@ namespace Orchard.Users.Controllers { public ILogger Logger { get; set; } - public ActionResult AccessDenied(string returnUrl) { + public ActionResult AccessDenied() { + var returnUrl = Request.QueryString["ReturnUrl"]; var currentUser = _authenticationService.GetAuthenticatedUser(); if (currentUser == null) { Logger.Information("Access denied to anonymous request on {0}", returnUrl); - return View("LogOn", new LogOnViewModel { Title = "Access Denied", ReturnUrl = returnUrl }); + return View("LogOn", new LogOnViewModel {Title = "Access Denied"}); } + //TODO: (erikpo) Add a setting for whether or not to log access denieds since these can fill up a database pretty fast from bots on a high traffic site Logger.Information("Access denied to user #{0} '{1}' on {2}", currentUser.Id, currentUser.UserName, returnUrl); + return View(new BaseViewModel()); } - public ActionResult LogOn(string returnUrl) { - if(_authenticationService.GetAuthenticatedUser() != null) + public ActionResult LogOn() { + if (_authenticationService.GetAuthenticatedUser() != null) return Redirect("~/"); - return View("LogOn", new LogOnViewModel { Title = "Log On", ReturnUrl = returnUrl }); + + return View("LogOn", new LogOnViewModel {Title = "Log On"}); } - [HttpPost] + [HttpPost, FollowReturnUrl] [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", Justification = "Needs to take same parameter type as Controller.Redirect()")] - public ActionResult LogOn(string userName, string password, bool rememberMe, string returnUrl) { + public ActionResult LogOn(string userName, string password, bool rememberMe) { var user = ValidateLogOn(userName, password); if (!ModelState.IsValid) { - return View("LogOn", new LogOnViewModel { Title = "Log On", ReturnUrl = returnUrl }); + return View("LogOn", new LogOnViewModel {Title = "Log On"}); } _authenticationService.SignIn(user, rememberMe); - return !String.IsNullOrEmpty(returnUrl) - ? Redirect(returnUrl) - : Redirect("~/"); + return Redirect("~/"); } - public ActionResult LogOff(string returnUrl) { + [FollowReturnUrl] + public ActionResult LogOff() { _authenticationService.SignOut(); - return !String.IsNullOrEmpty(returnUrl) - ? Redirect(returnUrl) - : Redirect("~/"); + return Redirect("~/"); } int MinPasswordLength { diff --git a/src/Orchard.Web/Modules/Orchard.Users/ViewModels/LogOnViewModel.cs b/src/Orchard.Web/Modules/Orchard.Users/ViewModels/LogOnViewModel.cs index b1f3eed4f..fabf9871b 100644 --- a/src/Orchard.Web/Modules/Orchard.Users/ViewModels/LogOnViewModel.cs +++ b/src/Orchard.Web/Modules/Orchard.Users/ViewModels/LogOnViewModel.cs @@ -1,10 +1,7 @@ -using System; -using Orchard.Mvc.ViewModels; +using Orchard.Mvc.ViewModels; namespace Orchard.Users.ViewModels { public class LogOnViewModel : BaseViewModel { public string Title { get; set; } - - public string ReturnUrl { get; set; } } } diff --git a/src/Orchard.Web/Modules/Orchard.Users/Views/Account/LogOn.ascx b/src/Orchard.Web/Modules/Orchard.Users/Views/Account/LogOn.ascx index d850a88e2..e6ff14d8b 100644 --- a/src/Orchard.Web/Modules/Orchard.Users/Views/Account/LogOn.ascx +++ b/src/Orchard.Web/Modules/Orchard.Users/Views/Account/LogOn.ascx @@ -3,25 +3,23 @@

<%=Html.TitleForPage(Model.Title)%>

<%=_Encoded("Please enter your username and password.")%> <%= Html.ActionLink("Register", "Register")%><%=_Encoded(" if you don't have an account.")%>

<%= Html.ValidationSummary(T("Login was unsuccessful. Please correct the errors and try again.").ToString())%> -<% using (Html.BeginForm(new { Action = "LogOn" })) - { %> -
- <%=_Encoded("Account Information")%> -
- - <%= Html.TextBox("username")%> - <%= Html.ValidationMessage("username")%> -
-
- - <%= Html.Password("password")%> - <%= Html.ValidationMessage("password")%> -
-
- <%= Html.CheckBox("rememberMe")%> -
- <%=Html.HiddenFor(m => m.ReturnUrl)%> - <%=Html.AntiForgeryTokenOrchard()%> - " /> -
-<% } %> \ No newline at end of file +<% +using (Html.BeginFormAntiForgeryPost(Url.Action("LogOn", new {ReturnUrl = Request.QueryString["ReturnUrl"]}))) { %> +
+ <%=_Encoded("Account Information")%> +
+ + <%= Html.TextBox("username")%> + <%= Html.ValidationMessage("username")%> +
+
+ + <%= Html.Password("password")%> + <%= Html.ValidationMessage("password")%> +
+
+ <%= Html.CheckBox("rememberMe")%> +
+ " /> +
<% +} %> \ No newline at end of file From 80d0155c9fe228ba3d17a7dbc1092577686dbbaa Mon Sep 17 00:00:00 2001 From: Erik Porter Date: Tue, 9 Mar 2010 00:45:02 -0800 Subject: [PATCH 3/3] Changed "Add to the main menu" to "Show on main menu" so they checkboxes reflect state instead of an action --HG-- branch : dev --- .../Views/EditorTemplates/Parts/Navigation.EditMenuPart.ascx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Orchard.Web/Core/Navigation/Views/EditorTemplates/Parts/Navigation.EditMenuPart.ascx b/src/Orchard.Web/Core/Navigation/Views/EditorTemplates/Parts/Navigation.EditMenuPart.ascx index 370d786a2..1b0a205b7 100644 --- a/src/Orchard.Web/Core/Navigation/Views/EditorTemplates/Parts/Navigation.EditMenuPart.ascx +++ b/src/Orchard.Web/Core/Navigation/Views/EditorTemplates/Parts/Navigation.EditMenuPart.ascx @@ -3,7 +3,7 @@ <%@ Import Namespace="Orchard.Core.Navigation.ViewModels"%>
<%=Html.EditorFor(m => m.OnMainMenu) %> - +
<%=Html.TextBoxFor(m => m.MenuText, new { @class = "large text" })%>