Updating Routable to fix the paths of contained items when a container's path changes

--HG--
branch : dev
This commit is contained in:
Nathan Heskew
2010-11-15 12:53:20 -08:00
parent 921bc46783
commit fc3ddf560e
14 changed files with 182 additions and 30 deletions

View File

@@ -33,11 +33,27 @@ namespace Orchard.Core.Routable.Handlers {
OnGetEditorShape<RoutePart>(SetModelProperties);
OnUpdateEditorShape<RoutePart>(SetModelProperties);
OnPublishing<RoutePart>((context, routable) => {
OnPublished<RoutePart>((context, route) => {
var path = route.Path;
route.Path = route.GetPathWithSlug(route.Slug);
if (context.PublishingItemVersionRecord != null)
processSlug(routable);
if (!string.IsNullOrEmpty(routable.Path))
_routablePathConstraint.AddPath(routable.Path);
processSlug(route);
// if the path has changed by having the slug changed on the way in (e.g. user input) or to avoid conflict
// then update and publish all contained items
if (path != route.Path) {
_routablePathConstraint.RemovePath(path);
_routableService.FixContainedPaths(route);
}
if (!string.IsNullOrWhiteSpace(route.Path))
_routablePathConstraint.AddPath(route.Path);
});
OnRemoved<RoutePart>((context, route) => {
if (!string.IsNullOrWhiteSpace(route.Path))
_routablePathConstraint.RemovePath(route.Path);
});
OnIndexing<RoutePart>((context, part) => context.DocumentIndex.Add("title", part.Record.Title).RemoveTags().Analyze());

View File

@@ -1,9 +1,15 @@
<Placement>
<!-- available display shapes -->
<!--
Parts_RoutableTitle
Parts_RoutableTitle_Summary
Parts_RoutableTitle_SummaryAdmin
-->
<Place Parts_Routable_Edit="Content:before.5"/>
<Match DisplayType="Detail">
<Place Parts_RoutableTitle="Header:5"/>
</Match>
<Match DisplayType="Summary">
<Place Parts_RoutableTitle="Header:5"/>
<Place Parts_RoutableTitle_Summary="Header:5"/>
</Match>
</Placement>

View File

@@ -22,5 +22,9 @@ namespace Orchard.Core.Routable.Services {
/// <returns>True if the slug has been created, False if a conflict occured</returns>
bool ProcessSlug(IRoutableAspect part);
/// <summary>
/// Updated the paths of all contained items to reflect the current path of this item
/// </summary>
void FixContainedPaths(IRoutableAspect part);
}
}

View File

@@ -45,7 +45,8 @@ namespace Orchard.Core.Routable.Services {
public void RemovePath(string path) {
lock (_syncLock) {
_paths.Remove(path);
if (path != null && _paths.ContainsKey(path))
_paths.Remove(path);
}
}

View File

@@ -4,6 +4,7 @@ using System.Linq;
using System.Text.RegularExpressions;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Aspects;
using Orchard.Core.Common.Models;
using Orchard.Core.Routable.Models;
namespace Orchard.Core.Routable.Services {
@@ -14,6 +15,18 @@ namespace Orchard.Core.Routable.Services {
_contentManager = contentManager;
}
public void FixContainedPaths(IRoutableAspect part) {
var items = _contentManager.Query(VersionOptions.Published)
.Join<CommonPartRecord>().Where(cr => cr.Container.Id == part.Id)
.List()
.Select(item => item.As<IRoutableAspect>()).Where(item => item != null);
foreach (var itemRoute in items) {
itemRoute.ContentItem.VersionRecord.Published = false; // <- to force a republish
_contentManager.Publish(itemRoute.ContentItem);
}
}
public void FillSlugFromTitle<TModel>(TModel model) where TModel : IRoutableAspect {
if (!string.IsNullOrEmpty(model.Slug) || string.IsNullOrEmpty(model.Title))
return;
@@ -86,6 +99,7 @@ namespace Orchard.Core.Routable.Services {
var originalSlug = part.Slug;
var newSlug = GenerateUniqueSlug(part, pathsLikeThis.Select(p => p.Path));
part.Path = part.GetPathWithSlug(newSlug);
part.Slug = newSlug;
if (originalSlug != newSlug)
return false;

View File

@@ -1,6 +1 @@
@{
Orchard.ContentManagement.ContentItem contentItem = Model.ContentPart.ContentItem;
string title = Model.Title.ToString();
}
<h1>@Html.ItemDisplayLink(title, contentItem)</h1>
<h1>@Model.Title</h1>

View File

@@ -73,17 +73,16 @@ namespace Orchard.Blogs.Controllers {
}
_contentManager.Publish(blog.ContentItem);
_blogSlugConstraint.AddSlug(blog.As<IRoutableAspect>().GetEffectiveSlug());
var slug = blog.As<IRoutableAspect>().GetEffectiveSlug();
_blogSlugConstraint.AddSlug(slug);
return Redirect(Url.BlogForAdmin(blog));
}
public ActionResult Edit(string blogSlug) {
public ActionResult Edit(int id) {
if (!Services.Authorizer.Authorize(Permissions.ManageBlogs, T("Not allowed to edit blog")))
return new HttpUnauthorizedResult();
var blog = _blogService.Get(blogSlug);
var blog = _blogService.Get(id, VersionOptions.Latest);
if (blog == null)
return HttpNotFound();
@@ -92,20 +91,24 @@ namespace Orchard.Blogs.Controllers {
}
[HttpPost, ActionName("Edit")]
public ActionResult EditPOST(string blogSlug) {
public ActionResult EditPOST(int id) {
if (!Services.Authorizer.Authorize(Permissions.ManageBlogs, T("Couldn't edit blog")))
return new HttpUnauthorizedResult();
var blog = _blogService.Get(blogSlug);
var blog = _blogService.Get(id, VersionOptions.DraftRequired);
if (blog == null)
return HttpNotFound();
var model = Services.ContentManager.UpdateEditor(blog, this);
if (!ModelState.IsValid)
if (!ModelState.IsValid) {
Services.TransactionManager.Cancel();
return View(model);
}
_contentManager.Publish(blog);
_blogSlugConstraint.AddSlug(blog.As<IRoutableAspect>().GetEffectiveSlug());
Services.Notifier.Information(T("Blog information updated"));
return Redirect(Url.BlogsForAdmin());
}

View File

@@ -132,6 +132,8 @@ namespace Orchard.Blogs.Controllers {
return View(model);
}
conditionallyPublish(blogPost.ContentItem);
Services.Notifier.Information(T("Your {0} has been saved.", blogPost.TypeDefinition.DisplayName));
if (!String.IsNullOrEmpty(returnUrl))

View File

@@ -48,11 +48,11 @@ namespace Orchard.Blogs.Extensions {
}
public static string BlogEdit(this UrlHelper urlHelper, BlogPart blogPart) {
return urlHelper.Action("Edit", "BlogAdmin", new { blogSlug = blogPart.As<IRoutableAspect>().Path, area = "Orchard.Blogs" });
return urlHelper.Action("Edit", "BlogAdmin", new { blogPart.Id, area = "Orchard.Blogs" });
}
public static string BlogRemove(this UrlHelper urlHelper, BlogPart blogPart) {
return urlHelper.Action("Remove", "BlogAdmin", new { blogSlug = blogPart.As<IRoutableAspect>().Path, area = "Orchard.Blogs" });
return urlHelper.Action("Remove", "BlogAdmin", new { blogPart.Id, area = "Orchard.Blogs" });
}
public static string BlogPostCreate(this UrlHelper urlHelper, BlogPart blogPart) {

View File

@@ -35,15 +35,13 @@ namespace Orchard.Blogs {
},
new RouteDescriptor {
Route = new Route(
"Admin/Blogs/{blogSlug}/Edit",
"Admin/Blogs/{id}/Edit",
new RouteValueDictionary {
{"area", "Orchard.Blogs"},
{"controller", "BlogAdmin"},
{"action", "Edit"}
},
new RouteValueDictionary {
{"blogSlug", _blogSlugConstraint}
},
new RouteValueDictionary (),
new RouteValueDictionary {
{"area", "Orchard.Blogs"}
},
@@ -51,15 +49,13 @@ namespace Orchard.Blogs {
},
new RouteDescriptor {
Route = new Route(
"Admin/Blogs/{blogSlug}/Remove",
"Admin/Blogs/{id}/Remove",
new RouteValueDictionary {
{"area", "Orchard.Blogs"},
{"controller", "BlogAdmin"},
{"action", "Remove"}
},
new RouteValueDictionary {
{"blogSlug", _blogSlugConstraint}
},
new RouteValueDictionary (),
new RouteValueDictionary {
{"area", "Orchard.Blogs"}
},

View File

@@ -24,6 +24,10 @@ namespace Orchard.Blogs.Services {
.List().FirstOrDefault();
}
public ContentItem Get(int id, VersionOptions versionOptions) {
return _contentManager.Get(id, versionOptions);
}
public IEnumerable<BlogPart> Get() {
return _contentManager.Query<BlogPart, BlogPartRecord>()
.Join<RoutePartRecord>()

View File

@@ -1,9 +1,11 @@
using System.Collections.Generic;
using Orchard.Blogs.Models;
using Orchard.ContentManagement;
namespace Orchard.Blogs.Services {
public interface IBlogService : IDependency {
BlogPart Get(string slug);
ContentItem Get(int id, VersionOptions versionOptions);
IEnumerable<BlogPart> Get();
void Delete(BlogPart blogPart);
}