Some work on making a blog or (routable) content item inaccessible by its path if it is set as the home page

--HG--
branch : dev
This commit is contained in:
Nathan Heskew
2010-11-30 16:03:01 -08:00
parent dd7b718852
commit 2798850a38
9 changed files with 186 additions and 15 deletions

View File

@@ -147,4 +147,26 @@ Scenario: The virtual path of my installation when at the root is reflected in t
| Routable.Title | My Blog | | Routable.Title | My Blog |
And I hit "Save" And I hit "Save"
And I go to "admin/blogs/my-blog/posts/create" And I go to "admin/blogs/my-blog/posts/create"
Then I should see "<span>http\://localhost/my-blog/</span>" Then I should see "<span>http\://localhost/my-blog/</span>"
Scenario: I set my blog to be the content for the home page and the posts for the blog should still be at the blog path prefixed path
Given I have installed Orchard
When I go to "admin/blogs/create"
And I fill in
| name | value |
| Routable.Title | My Blog |
| Routable.PromoteToHomePage | true |
And I hit "Save"
And I go to "admin/blogs/my-blog/posts/create"
And I fill in
| name | value |
| Routable.Title | My Post |
| Body.Text | Hi there. |
And I hit "Publish Now"
And I am redirected
And I go to "/Default.aspx"
Then I should see "<h1>My Blog</h1>"
When I go to "/my-blog"
Then the status should be 404 "Not Found"
When I go to "/my-blog/my-post"
Then I should see "<h1>My Post</h1>"

View File

@@ -457,6 +457,67 @@ this.ScenarioSetup(scenarioInfo);
testRunner.And("I go to \"admin/blogs/my-blog/posts/create\""); testRunner.And("I go to \"admin/blogs/my-blog/posts/create\"");
#line 150 #line 150
testRunner.Then("I should see \"<span>http\\://localhost/my-blog/</span>\""); testRunner.Then("I should see \"<span>http\\://localhost/my-blog/</span>\"");
#line hidden
testRunner.CollectScenarioErrors();
}
[NUnit.Framework.TestAttribute()]
[NUnit.Framework.DescriptionAttribute("I set my blog to be the content for the home page and the posts for the blog shou" +
"ld still be at the blog path prefixed path")]
public virtual void ISetMyBlogToBeTheContentForTheHomePageAndThePostsForTheBlogShouldStillBeAtTheBlogPathPrefixedPath()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("I set my blog to be the content for the home page and the posts for the blog shou" +
"ld still be at the blog path prefixed path", ((string[])(null)));
#line 152
this.ScenarioSetup(scenarioInfo);
#line 153
testRunner.Given("I have installed Orchard");
#line 154
testRunner.When("I go to \"admin/blogs/create\"");
#line hidden
TechTalk.SpecFlow.Table table15 = new TechTalk.SpecFlow.Table(new string[] {
"name",
"value"});
table15.AddRow(new string[] {
"Routable.Title",
"My Blog"});
table15.AddRow(new string[] {
"Routable.PromoteToHomePage",
"true"});
#line 155
testRunner.And("I fill in", ((string)(null)), table15);
#line 159
testRunner.And("I hit \"Save\"");
#line 160
testRunner.And("I go to \"admin/blogs/my-blog/posts/create\"");
#line hidden
TechTalk.SpecFlow.Table table16 = new TechTalk.SpecFlow.Table(new string[] {
"name",
"value"});
table16.AddRow(new string[] {
"Routable.Title",
"My Post"});
table16.AddRow(new string[] {
"Body.Text",
"Hi there."});
#line 161
testRunner.And("I fill in", ((string)(null)), table16);
#line 165
testRunner.And("I hit \"Publish Now\"");
#line 166
testRunner.And("I am redirected");
#line 167
testRunner.And("I go to \"/Default.aspx\"");
#line 168
testRunner.Then("I should see \"<h1>My Blog</h1>\"");
#line 169
testRunner.When("I go to \"/my-blog\"");
#line 170
testRunner.Then("the status should be 404 \"Not Found\"");
#line 171
testRunner.When("I go to \"/my-blog/my-post\"");
#line 172
testRunner.Then("I should see \"<h1>My Post</h1>\"");
#line hidden #line hidden
testRunner.CollectScenarioErrors(); testRunner.CollectScenarioErrors();
} }

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Web.Mvc; using System.Web.Mvc;
using Orchard.ContentManagement; using Orchard.ContentManagement;
@@ -8,6 +9,7 @@ using Orchard.Core.Routable.Services;
using Orchard.Data; using Orchard.Data;
using Orchard.DisplayManagement; using Orchard.DisplayManagement;
using Orchard.Localization; using Orchard.Localization;
using Orchard.Services;
using Orchard.Themes; using Orchard.Themes;
namespace Orchard.Core.Routable.Controllers { namespace Orchard.Core.Routable.Controllers {
@@ -16,25 +18,33 @@ namespace Orchard.Core.Routable.Controllers {
private readonly IContentManager _contentManager; private readonly IContentManager _contentManager;
private readonly ITransactionManager _transactionManager; private readonly ITransactionManager _transactionManager;
private readonly IRoutablePathConstraint _routablePathConstraint; private readonly IRoutablePathConstraint _routablePathConstraint;
private readonly IWorkContextAccessor _workContextAccessor;
private readonly IHomePageProvider _routableHomePageProvider;
public ItemController( public ItemController(
IContentManager contentManager, IContentManager contentManager,
ITransactionManager transactionManager, ITransactionManager transactionManager,
IRoutablePathConstraint routablePathConstraint, IRoutablePathConstraint routablePathConstraint,
IShapeFactory shapeFactory) { IShapeFactory shapeFactory,
IWorkContextAccessor workContextAccessor,
IEnumerable<IHomePageProvider> homePageProviders) {
_contentManager = contentManager; _contentManager = contentManager;
_transactionManager = transactionManager; _transactionManager = transactionManager;
_routablePathConstraint = routablePathConstraint; _routablePathConstraint = routablePathConstraint;
_workContextAccessor = workContextAccessor;
_routableHomePageProvider = homePageProviders.SingleOrDefault(p => p.GetProviderName() == RoutableHomePageProvider.Name);
Shape = shapeFactory; Shape = shapeFactory;
T = NullLocalizer.Instance;
} }
public Localizer T { get; set; }
dynamic Shape { get; set; } dynamic Shape { get; set; }
[Themed] [Themed]
public ActionResult Display(string path) { public ActionResult Display(string path) {
var matchedPath = _routablePathConstraint.FindPath(path); var matchedPath = _routablePathConstraint.FindPath(path);
if (string.IsNullOrEmpty(matchedPath)) { if (matchedPath == null) {
throw new ApplicationException("404 - should not have passed path constraint"); throw new ApplicationException(T("404 - should not have passed path constraint").Text);
} }
var hits = _contentManager var hits = _contentManager
@@ -42,13 +52,20 @@ namespace Orchard.Core.Routable.Controllers {
.Where(r => r.Path == matchedPath) .Where(r => r.Path == matchedPath)
.Slice(0, 2); .Slice(0, 2);
if (hits.Count() == 0) { if (hits.Count() == 0) {
throw new ApplicationException("404 - should not have passed path constraint"); throw new ApplicationException(T("404 - should not have passed path constraint").Text);
} }
if (hits.Count() != 1) { if (hits.Count() != 1) {
throw new ApplicationException("Ambiguous content"); throw new ApplicationException(T("Ambiguous content").Text);
} }
dynamic model = _contentManager.BuildDisplay(hits.Single()); var item = hits.Single();
// primary action run for a home paged item shall not pass
if (!RouteData.DataTokens.ContainsKey("ParentActionViewContext")
&& item.Id == _routableHomePageProvider.GetHomePageId(_workContextAccessor.GetContext().CurrentSite.HomePage)) {
return HttpNotFound();
}
dynamic model = _contentManager.BuildDisplay(item);
// Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation. // Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation.
return View((object)model); return View((object)model);
} }

View File

@@ -23,6 +23,7 @@ namespace Orchard.Core.Routable.Handlers {
IRepository<RoutePartRecord> repository, IRepository<RoutePartRecord> repository,
IRoutablePathConstraint routablePathConstraint, IRoutablePathConstraint routablePathConstraint,
IRoutableService routableService, IRoutableService routableService,
IWorkContextAccessor workContextAccessor,
IEnumerable<IHomePageProvider> homePageProviders) { IEnumerable<IHomePageProvider> homePageProviders) {
_services = services; _services = services;
_routablePathConstraint = routablePathConstraint; _routablePathConstraint = routablePathConstraint;
@@ -46,8 +47,10 @@ namespace Orchard.Core.Routable.Handlers {
OnPublished<RoutePart>((context, route) => { OnPublished<RoutePart>((context, route) => {
FinalizePath(route, context, processSlug); FinalizePath(route, context, processSlug);
if (route.ContentItem.Id != 0 && route.PromoteToHomePage && _routableHomePageProvider != null) if (route.ContentItem.Id != 0 && route.PromoteToHomePage && _routableHomePageProvider != null) {
_services.WorkContext.CurrentSite.HomePage = _routableHomePageProvider.GetSettingValue(route.ContentItem.Id); _services.WorkContext.CurrentSite.HomePage = _routableHomePageProvider.GetSettingValue(route.ContentItem.Id);
_routablePathConstraint.AddPath("");
}
}); });
OnRemoved<RoutePart>((context, route) => { OnRemoved<RoutePart>((context, route) => {
@@ -87,6 +90,14 @@ namespace Orchard.Core.Routable.Handlers {
} }
public class RoutePartHandlerBase : ContentHandlerBase { public class RoutePartHandlerBase : ContentHandlerBase {
private readonly IWorkContextAccessor _workContextAccessor;
private readonly IHomePageProvider _routableHomePageProvider;
public RoutePartHandlerBase(IWorkContextAccessor workContextAccessor, IEnumerable<IHomePageProvider> homePageProviders) {
_workContextAccessor = workContextAccessor;
_routableHomePageProvider = homePageProviders.SingleOrDefault(p => p.GetProviderName() == RoutableHomePageProvider.Name);
}
public override void GetContentItemMetadata(GetContentItemMetadataContext context) { public override void GetContentItemMetadata(GetContentItemMetadataContext context) {
var routable = context.ContentItem.As<RoutePart>(); var routable = context.ContentItem.As<RoutePart>();
@@ -98,11 +109,15 @@ namespace Orchard.Core.Routable.Handlers {
// set the display route values if it hasn't been set or only has been set by the Contents module. // set the display route values if it hasn't been set or only has been set by the Contents module.
// allows other modules to set their own display. probably not common enough to warrant some priority implemntation // allows other modules to set their own display. probably not common enough to warrant some priority implemntation
if (context.Metadata.DisplayRouteValues == null || context.Metadata.DisplayRouteValues["Area"] as string == "Contents") { if (context.Metadata.DisplayRouteValues == null || context.Metadata.DisplayRouteValues["Area"] as string == "Contents") {
var itemPath = routable.Id == _routableHomePageProvider.GetHomePageId(_workContextAccessor.GetContext().CurrentSite.HomePage)
? ""
: routable.Path;
context.Metadata.DisplayRouteValues = new RouteValueDictionary { context.Metadata.DisplayRouteValues = new RouteValueDictionary {
{"Area", "Routable"}, {"Area", "Routable"},
{"Controller", "Item"}, {"Controller", "Item"},
{"Action", "Display"}, {"Action", "Display"},
{"path", routable.Path} {"path", itemPath}
}; };
} }
} }

View File

@@ -1,7 +1,9 @@
using System.Web.Mvc; using System;
using System.Web.Mvc;
using JetBrains.Annotations; using JetBrains.Annotations;
using Orchard.Core.Routable.Models; using Orchard.Core.Routable.Models;
using Orchard.DisplayManagement; using Orchard.DisplayManagement;
using Orchard.Localization;
using Orchard.Services; using Orchard.Services;
using Orchard.ContentManagement; using Orchard.ContentManagement;
@@ -16,8 +18,10 @@ namespace Orchard.Core.Routable.Services {
IShapeFactory shapeFactory) { IShapeFactory shapeFactory) {
_contentManager = contentManager; _contentManager = contentManager;
Shape = shapeFactory; Shape = shapeFactory;
T = NullLocalizer.Instance;
} }
public Localizer T { get; set; }
dynamic Shape { get; set; } dynamic Shape { get; set; }
public string GetProviderName() { public string GetProviderName() {
@@ -28,6 +32,15 @@ namespace Orchard.Core.Routable.Services {
return GetProviderName() + ";" + id; return GetProviderName() + ";" + id;
} }
public int GetHomePageId(string value) {
int id;
if (string.IsNullOrWhiteSpace(value) || !int.TryParse(value.Substring(Name.Length + 1), out id))
throw new ApplicationException(T("Invalid home page setting value for {0}: {1}", Name, value).Text);
return id;
}
public ActionResult GetHomePage(int id) { public ActionResult GetHomePage(int id) {
var contentItem = _contentManager.Get(id, VersionOptions.Published); var contentItem = _contentManager.Get(id, VersionOptions.Published);
if (contentItem == null || !contentItem.Is<RoutePart>()) if (contentItem == null || !contentItem.Is<RoutePart>())

View File

@@ -1,11 +1,14 @@
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Web.Mvc; using System.Web.Mvc;
using Orchard.Blogs.Extensions; using Orchard.Blogs.Extensions;
using Orchard.Blogs.Routing; using Orchard.Blogs.Routing;
using Orchard.Blogs.Services; using Orchard.Blogs.Services;
using Orchard.Core.Feeds; using Orchard.Core.Feeds;
using Orchard.Core.Routable.Services;
using Orchard.DisplayManagement; using Orchard.DisplayManagement;
using Orchard.Logging; using Orchard.Logging;
using Orchard.Services;
using Orchard.Themes; using Orchard.Themes;
using Orchard.UI.Navigation; using Orchard.UI.Navigation;
@@ -17,6 +20,8 @@ namespace Orchard.Blogs.Controllers {
private readonly IBlogPostService _blogPostService; private readonly IBlogPostService _blogPostService;
private readonly IBlogSlugConstraint _blogSlugConstraint; private readonly IBlogSlugConstraint _blogSlugConstraint;
private readonly IFeedManager _feedManager; private readonly IFeedManager _feedManager;
private readonly IWorkContextAccessor _workContextAccessor;
private readonly IHomePageProvider _routableHomePageProvider;
public BlogController( public BlogController(
IOrchardServices services, IOrchardServices services,
@@ -24,12 +29,16 @@ namespace Orchard.Blogs.Controllers {
IBlogPostService blogPostService, IBlogPostService blogPostService,
IBlogSlugConstraint blogSlugConstraint, IBlogSlugConstraint blogSlugConstraint,
IFeedManager feedManager, IFeedManager feedManager,
IShapeFactory shapeFactory) { IShapeFactory shapeFactory,
IWorkContextAccessor workContextAccessor,
IEnumerable<IHomePageProvider> homePageProviders) {
_services = services; _services = services;
_blogService = blogService; _blogService = blogService;
_blogPostService = blogPostService; _blogPostService = blogPostService;
_blogSlugConstraint = blogSlugConstraint; _blogSlugConstraint = blogSlugConstraint;
_feedManager = feedManager; _feedManager = feedManager;
_workContextAccessor = workContextAccessor;
_routableHomePageProvider = homePageProviders.SingleOrDefault(p => p.GetProviderName() == RoutableHomePageProvider.Name);
Logger = NullLogger.Instance; Logger = NullLogger.Instance;
Shape = shapeFactory; Shape = shapeFactory;
} }
@@ -59,6 +68,12 @@ namespace Orchard.Blogs.Controllers {
if (blogPart == null) if (blogPart == null)
return HttpNotFound(); return HttpNotFound();
// primary action run for a home paged item shall not pass
if (!RouteData.DataTokens.ContainsKey("ParentActionViewContext")
&& blogPart.Id == _routableHomePageProvider.GetHomePageId(_workContextAccessor.GetContext().CurrentSite.HomePage)) {
return HttpNotFound();
}
_feedManager.Register(blogPart); _feedManager.Register(blogPart);
var blogPosts = _blogPostService.Get(blogPart, pager.GetStartIndex(), pager.PageSize) var blogPosts = _blogPostService.Get(blogPart, pager.GetStartIndex(), pager.PageSize)
.Select(b => _services.ContentManager.BuildDisplay(b, "Summary")); .Select(b => _services.ContentManager.BuildDisplay(b, "Summary"));

View File

@@ -1,17 +1,39 @@
using System.Collections.Generic;
using System.Linq;
using System.Web.Routing; using System.Web.Routing;
using JetBrains.Annotations; using JetBrains.Annotations;
using Orchard.Blogs.Models; using Orchard.Blogs.Models;
using Orchard.Blogs.Routing;
using Orchard.ContentManagement; using Orchard.ContentManagement;
using Orchard.ContentManagement.Handlers; using Orchard.ContentManagement.Handlers;
using Orchard.Core.Routable.Models; using Orchard.Core.Routable.Models;
using Orchard.Core.Routable.Services;
using Orchard.Data; using Orchard.Data;
using Orchard.Services;
namespace Orchard.Blogs.Handlers { namespace Orchard.Blogs.Handlers {
[UsedImplicitly] [UsedImplicitly]
public class BlogPartHandler : ContentHandler { public class BlogPartHandler : ContentHandler {
public BlogPartHandler(IRepository<BlogPartRecord> repository) { private readonly IWorkContextAccessor _workContextAccessor;
private readonly IBlogSlugConstraint _blogSlugConstraint;
private readonly IHomePageProvider _routableHomePageProvider;
public BlogPartHandler(IRepository<BlogPartRecord> repository, IWorkContextAccessor workContextAccessor, IEnumerable<IHomePageProvider> homePageProviders, IBlogSlugConstraint blogSlugConstraint) {
_workContextAccessor = workContextAccessor;
_blogSlugConstraint = blogSlugConstraint;
_routableHomePageProvider = homePageProviders.SingleOrDefault(p => p.GetProviderName() == RoutableHomePageProvider.Name);
Filters.Add(StorageFilter.For(repository)); Filters.Add(StorageFilter.For(repository));
OnPublished<RoutePart>((context, route) => {
if (route.Is<BlogPart>()) {
if (route.ContentItem.Id != 0 && route.PromoteToHomePage)
_blogSlugConstraint.AddSlug("");
}
else if (route.ContentItem.Id != 0 && route.PromoteToHomePage) {
_blogSlugConstraint.RemoveSlug("");
}
});
OnGetDisplayShape<BlogPart>((context, blog) => { OnGetDisplayShape<BlogPart>((context, blog) => {
context.Shape.Description = blog.Description; context.Shape.Description = blog.Description;
context.Shape.PostCount = blog.PostCount; context.Shape.PostCount = blog.PostCount;
@@ -24,11 +46,15 @@ namespace Orchard.Blogs.Handlers {
if (blog == null) if (blog == null)
return; return;
var blogSlug = blog.Id == _routableHomePageProvider.GetHomePageId(_workContextAccessor.GetContext().CurrentSite.HomePage)
? ""
: blog.As<RoutePart>().Slug;
context.Metadata.DisplayRouteValues = new RouteValueDictionary { context.Metadata.DisplayRouteValues = new RouteValueDictionary {
{"Area", "Orchard.Blogs"}, {"Area", "Orchard.Blogs"},
{"Controller", "Blog"}, {"Controller", "Blog"},
{"Action", "Item"}, {"Action", "Item"},
{"blogSlug", blog.As<RoutePart>().Slug} {"blogSlug", blogSlug}
}; };
context.Metadata.CreateRouteValues = new RouteValueDictionary { context.Metadata.CreateRouteValues = new RouteValueDictionary {
{"Area", "Orchard.Blogs"}, {"Area", "Orchard.Blogs"},

View File

@@ -243,7 +243,8 @@ namespace Orchard.Blogs {
new RouteValueDictionary { new RouteValueDictionary {
{"area", "Orchard.Blogs"}, {"area", "Orchard.Blogs"},
{"controller", "Blog"}, {"controller", "Blog"},
{"action", "Item"} {"action", "Item"},
{"blogSlug", ""}
}, },
new RouteValueDictionary { new RouteValueDictionary {
{"blogSlug", _blogSlugConstraint} {"blogSlug", _blogSlugConstraint}

View File

@@ -4,6 +4,7 @@ namespace Orchard.Services {
public interface IHomePageProvider : IDependency { public interface IHomePageProvider : IDependency {
string GetProviderName(); string GetProviderName();
string GetSettingValue(int itemId); string GetSettingValue(int itemId);
int GetHomePageId(string value);
ActionResult GetHomePage(int itemId); ActionResult GetHomePage(int itemId);
} }
} }