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

@@ -148,3 +148,25 @@ Scenario: The virtual path of my installation when at the root is reflected in t
And I hit "Save"
And I go to "admin/blogs/my-blog/posts/create"
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\"");
#line 150
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
testRunner.CollectScenarioErrors();
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using Orchard.ContentManagement;
@@ -8,6 +9,7 @@ using Orchard.Core.Routable.Services;
using Orchard.Data;
using Orchard.DisplayManagement;
using Orchard.Localization;
using Orchard.Services;
using Orchard.Themes;
namespace Orchard.Core.Routable.Controllers {
@@ -16,25 +18,33 @@ namespace Orchard.Core.Routable.Controllers {
private readonly IContentManager _contentManager;
private readonly ITransactionManager _transactionManager;
private readonly IRoutablePathConstraint _routablePathConstraint;
private readonly IWorkContextAccessor _workContextAccessor;
private readonly IHomePageProvider _routableHomePageProvider;
public ItemController(
IContentManager contentManager,
ITransactionManager transactionManager,
IRoutablePathConstraint routablePathConstraint,
IShapeFactory shapeFactory) {
IShapeFactory shapeFactory,
IWorkContextAccessor workContextAccessor,
IEnumerable<IHomePageProvider> homePageProviders) {
_contentManager = contentManager;
_transactionManager = transactionManager;
_routablePathConstraint = routablePathConstraint;
_workContextAccessor = workContextAccessor;
_routableHomePageProvider = homePageProviders.SingleOrDefault(p => p.GetProviderName() == RoutableHomePageProvider.Name);
Shape = shapeFactory;
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
dynamic Shape { get; set; }
[Themed]
public ActionResult Display(string path) {
var matchedPath = _routablePathConstraint.FindPath(path);
if (string.IsNullOrEmpty(matchedPath)) {
throw new ApplicationException("404 - should not have passed path constraint");
if (matchedPath == null) {
throw new ApplicationException(T("404 - should not have passed path constraint").Text);
}
var hits = _contentManager
@@ -42,13 +52,20 @@ namespace Orchard.Core.Routable.Controllers {
.Where(r => r.Path == matchedPath)
.Slice(0, 2);
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) {
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.
return View((object)model);
}

View File

@@ -23,6 +23,7 @@ namespace Orchard.Core.Routable.Handlers {
IRepository<RoutePartRecord> repository,
IRoutablePathConstraint routablePathConstraint,
IRoutableService routableService,
IWorkContextAccessor workContextAccessor,
IEnumerable<IHomePageProvider> homePageProviders) {
_services = services;
_routablePathConstraint = routablePathConstraint;
@@ -46,8 +47,10 @@ namespace Orchard.Core.Routable.Handlers {
OnPublished<RoutePart>((context, route) => {
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);
_routablePathConstraint.AddPath("");
}
});
OnRemoved<RoutePart>((context, route) => {
@@ -87,6 +90,14 @@ namespace Orchard.Core.Routable.Handlers {
}
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) {
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.
// 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") {
var itemPath = routable.Id == _routableHomePageProvider.GetHomePageId(_workContextAccessor.GetContext().CurrentSite.HomePage)
? ""
: routable.Path;
context.Metadata.DisplayRouteValues = new RouteValueDictionary {
{"Area", "Routable"},
{"Controller", "Item"},
{"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 Orchard.Core.Routable.Models;
using Orchard.DisplayManagement;
using Orchard.Localization;
using Orchard.Services;
using Orchard.ContentManagement;
@@ -16,8 +18,10 @@ namespace Orchard.Core.Routable.Services {
IShapeFactory shapeFactory) {
_contentManager = contentManager;
Shape = shapeFactory;
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
dynamic Shape { get; set; }
public string GetProviderName() {
@@ -28,6 +32,15 @@ namespace Orchard.Core.Routable.Services {
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) {
var contentItem = _contentManager.Get(id, VersionOptions.Published);
if (contentItem == null || !contentItem.Is<RoutePart>())

View File

@@ -1,11 +1,14 @@
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using Orchard.Blogs.Extensions;
using Orchard.Blogs.Routing;
using Orchard.Blogs.Services;
using Orchard.Core.Feeds;
using Orchard.Core.Routable.Services;
using Orchard.DisplayManagement;
using Orchard.Logging;
using Orchard.Services;
using Orchard.Themes;
using Orchard.UI.Navigation;
@@ -17,6 +20,8 @@ namespace Orchard.Blogs.Controllers {
private readonly IBlogPostService _blogPostService;
private readonly IBlogSlugConstraint _blogSlugConstraint;
private readonly IFeedManager _feedManager;
private readonly IWorkContextAccessor _workContextAccessor;
private readonly IHomePageProvider _routableHomePageProvider;
public BlogController(
IOrchardServices services,
@@ -24,12 +29,16 @@ namespace Orchard.Blogs.Controllers {
IBlogPostService blogPostService,
IBlogSlugConstraint blogSlugConstraint,
IFeedManager feedManager,
IShapeFactory shapeFactory) {
IShapeFactory shapeFactory,
IWorkContextAccessor workContextAccessor,
IEnumerable<IHomePageProvider> homePageProviders) {
_services = services;
_blogService = blogService;
_blogPostService = blogPostService;
_blogSlugConstraint = blogSlugConstraint;
_feedManager = feedManager;
_workContextAccessor = workContextAccessor;
_routableHomePageProvider = homePageProviders.SingleOrDefault(p => p.GetProviderName() == RoutableHomePageProvider.Name);
Logger = NullLogger.Instance;
Shape = shapeFactory;
}
@@ -59,6 +68,12 @@ namespace Orchard.Blogs.Controllers {
if (blogPart == null)
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);
var blogPosts = _blogPostService.Get(blogPart, pager.GetStartIndex(), pager.PageSize)
.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 JetBrains.Annotations;
using Orchard.Blogs.Models;
using Orchard.Blogs.Routing;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Handlers;
using Orchard.Core.Routable.Models;
using Orchard.Core.Routable.Services;
using Orchard.Data;
using Orchard.Services;
namespace Orchard.Blogs.Handlers {
[UsedImplicitly]
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));
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) => {
context.Shape.Description = blog.Description;
context.Shape.PostCount = blog.PostCount;
@@ -24,11 +46,15 @@ namespace Orchard.Blogs.Handlers {
if (blog == null)
return;
var blogSlug = blog.Id == _routableHomePageProvider.GetHomePageId(_workContextAccessor.GetContext().CurrentSite.HomePage)
? ""
: blog.As<RoutePart>().Slug;
context.Metadata.DisplayRouteValues = new RouteValueDictionary {
{"Area", "Orchard.Blogs"},
{"Controller", "Blog"},
{"Action", "Item"},
{"blogSlug", blog.As<RoutePart>().Slug}
{"blogSlug", blogSlug}
};
context.Metadata.CreateRouteValues = new RouteValueDictionary {
{"Area", "Orchard.Blogs"},

View File

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

View File

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