mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-11-28 17:32:44 +08:00
Adding some slugification
--HG-- extra : convert_revision : svn%3A5ff7c347-ad56-4c35-b696-ccb81de16e03/trunk%4045366
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web.Mvc;
|
||||
|
||||
namespace Orchard.Core.Common.Controllers {
|
||||
public class RoutableController : Controller {
|
||||
[HttpPost]
|
||||
public ActionResult Slugify(FormCollection formCollection, string value) {
|
||||
if (!string.IsNullOrEmpty(value)) {
|
||||
|
||||
//todo: (heskew) improve - just doing multi-pass regex replaces for now with the simple rules of
|
||||
// (1) can't begin with a '/', (2) can't have adjacent '/'s and (3) can't have these characters
|
||||
var startsoffbad = new Regex(@"^[\s/]+");
|
||||
var slashhappy = new Regex("/{2,}");
|
||||
var dissallowed = new Regex(@"[:?#\[\]@!$&'()*+,;=\s]+");
|
||||
|
||||
value = value.Trim();
|
||||
value = startsoffbad.Replace(value, "-");
|
||||
value = slashhappy.Replace(value, "/");
|
||||
value = dissallowed.Replace(value, "-");
|
||||
|
||||
if (value.Length > 1000)
|
||||
value = value.Substring(0, 1000);
|
||||
}
|
||||
|
||||
return Json(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/Orchard.Web/Core/Common/Controllers/RoutableDriver.cs
Normal file
25
src/Orchard.Web/Core/Common/Controllers/RoutableDriver.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Drivers;
|
||||
using Orchard.Core.Common.Models;
|
||||
using Orchard.Core.Common.ViewModels;
|
||||
|
||||
namespace Orchard.Core.Common.Controllers {
|
||||
public class Routable : ContentPartDriver<RoutableAspect> {
|
||||
private const string TemplateName = "Parts/Common.Routable";
|
||||
|
||||
protected override string Prefix {
|
||||
get { return "Routable"; }
|
||||
}
|
||||
|
||||
protected override DriverResult Editor(RoutableAspect part) {
|
||||
var model = new RoutableEditorViewModel {RoutableAspect = part};
|
||||
return ContentPartTemplate(model, TemplateName, Prefix).Location("primary", "before.5");
|
||||
}
|
||||
|
||||
protected override DriverResult Editor(RoutableAspect part, IUpdateModel updater) {
|
||||
var model = new RoutableEditorViewModel {RoutableAspect = part};
|
||||
updater.TryUpdateModel(model, Prefix, null, null);
|
||||
return ContentPartTemplate(model, TemplateName, Prefix).Location("primary", "before.5");
|
||||
}
|
||||
}
|
||||
}
|
||||
37
src/Orchard.Web/Core/Common/Routes.cs
Normal file
37
src/Orchard.Web/Core/Common/Routes.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Routing;
|
||||
using Orchard.Mvc.Routes;
|
||||
|
||||
namespace Orchard.Core.Common {
|
||||
public class Routes : IRouteProvider {
|
||||
#region IRouteProvider Members
|
||||
|
||||
public IEnumerable<RouteDescriptor> GetRoutes() {
|
||||
return new[] {
|
||||
new RouteDescriptor {
|
||||
Route = new Route(
|
||||
"Admin/Blogs/Slugify",
|
||||
new RouteValueDictionary {
|
||||
{"area", "Orchard.Blogs"},
|
||||
{"controller", "BlogPost"},
|
||||
{"action", "Slugify"}
|
||||
},
|
||||
new RouteValueDictionary(),
|
||||
new RouteValueDictionary {
|
||||
{"area", "Orchard.Blogs"}
|
||||
},
|
||||
new MvcRouteHandler())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void GetRoutes(ICollection<RouteDescriptor> routes) {
|
||||
foreach (RouteDescriptor routeDescriptor in GetRoutes()) {
|
||||
routes.Add(routeDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
9
src/Orchard.Web/Core/Common/Scripts/jquery.slugify.js
Normal file
9
src/Orchard.Web/Core/Common/Scripts/jquery.slugify.js
Normal file
@@ -0,0 +1,9 @@
|
||||
jQuery.fn.extend({
|
||||
slugify: function(options) {
|
||||
//todo: (heskew) need messaging system
|
||||
if (!options.target || !options.url) return;
|
||||
jQuery.post(options.url, { value: $(this).val(), __RequestVerificationToken: $("input[name=__RequestVerificationToken]").val() }, function(data) {
|
||||
options.target.val(data);
|
||||
}, "json");
|
||||
}
|
||||
});
|
||||
@@ -1,11 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using Orchard.Core.Common.Models;
|
||||
using Orchard.Core.Common.Models;
|
||||
|
||||
namespace Orchard.Core.Common.ViewModels {
|
||||
public class BodyDisplayViewModel {
|
||||
public BodyAspect BodyAspect { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,4 +16,4 @@ namespace Orchard.Core.Common.ViewModels {
|
||||
|
||||
public string TextEditorTemplate { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Orchard.Core.Common.ViewModels {
|
||||
public class OwnerEditorViewModel {
|
||||
[Required]
|
||||
public string Owner { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Orchard.Core.Common.Models;
|
||||
|
||||
namespace Orchard.Core.Common.ViewModels {
|
||||
public class RoutableEditorViewModel {
|
||||
public RoutableAspect RoutableAspect { get; set; }
|
||||
|
||||
[Required]
|
||||
public string Title {
|
||||
get { return RoutableAspect.Record.Title; }
|
||||
set { RoutableAspect.Record.Title = value; }
|
||||
}
|
||||
|
||||
[Required]
|
||||
public string Slug {
|
||||
get { return RoutableAspect.Record.Slug; }
|
||||
set { RoutableAspect.Record.Slug = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<RoutableEditorViewModel>" %>
|
||||
<%@ Import Namespace="Orchard.Core.Common.ViewModels"%>
|
||||
<% Html.RegisterFootScript("jquery.slugify.js"); %>
|
||||
<fieldset>
|
||||
<%=Html.LabelFor(m => m.Title) %>
|
||||
<%=Html.EditorFor(m => m.Title) %>
|
||||
</fieldset>
|
||||
<fieldset class="permalink">
|
||||
<label class="sub" for="Slug"><%=_Encoded("Permalink")%><br /><span>[todo: (heskew) need path to here]/</span></label>
|
||||
<span><%=Html.TextBoxFor(m => m.Slug, new { @class = "text" })%></span>
|
||||
</fieldset>
|
||||
<% using (this.Capture("end-of-page-scripts")) { %>
|
||||
<script type="text/javascript">$(function(){$("input#Routable_Title").blur(function(){$(this).slugify({target:$("input#Routable_Slug"),url:"<%="/common/routable/slugify" %>"})})})</script>
|
||||
<% } %>
|
||||
@@ -62,8 +62,11 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Common\Controllers\BodyDriver.cs" />
|
||||
<Compile Include="Common\Controllers\RoutableDriver.cs" />
|
||||
<Compile Include="Common\Controllers\RoutableController.cs" />
|
||||
<Compile Include="Common\Permissions.cs" />
|
||||
<Compile Include="Common\Records\CommonVersionRecord.cs" />
|
||||
<Compile Include="Common\Routes.cs" />
|
||||
<Compile Include="Common\Utilities\LazyField.cs" />
|
||||
<Compile Include="Common\Providers\CommonAspectHandler.cs" />
|
||||
<Compile Include="Common\Models\CommonAspect.cs" />
|
||||
@@ -76,6 +79,7 @@
|
||||
<Compile Include="Common\Records\RoutableRecord.cs" />
|
||||
<Compile Include="Common\ViewModels\BodyDisplayViewModel.cs" />
|
||||
<Compile Include="Common\ViewModels\BodyEditorViewModel.cs" />
|
||||
<Compile Include="Common\ViewModels\RoutableEditorViewModel.cs" />
|
||||
<Compile Include="Common\ViewModels\OwnerEditorViewModel.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Scheduling\Models\ScheduledAspect.cs" />
|
||||
@@ -143,7 +147,9 @@
|
||||
<Content Include="Themes\Views\Admin\Index.aspx" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Common\Scripts\jquery.slugify.js" />
|
||||
<Content Include="Common\Views\DisplayTemplates\Parts\Common.Body.ascx" />
|
||||
<Content Include="Common\Views\EditorTemplates\Parts\Common.Routable.ascx" />
|
||||
<Content Include="Common\Views\EditorTemplates\Parts\Common.Body.ascx" />
|
||||
<Content Include="Common\Views\EditorTemplates\Parts\Common.Owner.ascx" />
|
||||
<Content Include="Scheduling\Package.txt" />
|
||||
|
||||
@@ -5,6 +5,7 @@ using Orchard.Blogs.Models;
|
||||
using Orchard.Blogs.Services;
|
||||
using Orchard.Blogs.ViewModels;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Core.Common.Models;
|
||||
using Orchard.Data;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Mvc.Results;
|
||||
@@ -65,7 +66,7 @@ namespace Orchard.Blogs.Controllers {
|
||||
var session = _sessionLocator.For(typeof(BlogRecord));
|
||||
session.Flush();
|
||||
|
||||
return Redirect(Url.BlogForAdmin(model.Blog.Item.Slug));
|
||||
return Redirect(Url.BlogForAdmin(model.Blog.Item.As<RoutableAspect>().Slug));
|
||||
}
|
||||
|
||||
public ActionResult Edit(string blogSlug) {
|
||||
|
||||
@@ -6,6 +6,7 @@ using Orchard.Blogs.Services;
|
||||
using Orchard.Blogs.ViewModels;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Records;
|
||||
using Orchard.Core.Common.Models;
|
||||
using Orchard.Data;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Mvc.Results;
|
||||
@@ -92,7 +93,7 @@ namespace Orchard.Blogs.Controllers {
|
||||
var session = _sessionLocator.For(typeof(ContentItemRecord));
|
||||
session.Flush();
|
||||
|
||||
return Redirect(Url.BlogPost(blogSlug, model.BlogPost.Item.Slug));
|
||||
return Redirect(Url.BlogPost(blogSlug, model.BlogPost.Item.As<RoutableAspect>().Slug));
|
||||
}
|
||||
|
||||
public ActionResult Edit(string blogSlug, string postSlug) {
|
||||
|
||||
@@ -65,29 +65,5 @@ namespace Orchard.Blogs.Controllers {
|
||||
|
||||
return View(model);
|
||||
}
|
||||
|
||||
//TODO: (erikpo) This should move to be part of the RoutableAspect
|
||||
public ActionResult Slugify(string value) {
|
||||
string slug = value;
|
||||
|
||||
//TODO: (erikpo) Move this into a utility class
|
||||
if (!string.IsNullOrEmpty(value)) {
|
||||
Regex regex = new Regex("([^a-z0-9-_]?)", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
slug = value.Trim();
|
||||
slug = slug.Replace(' ', '-');
|
||||
slug = slug.Replace("---", "-");
|
||||
slug = slug.Replace("--", "-");
|
||||
slug = regex.Replace(slug, "");
|
||||
|
||||
if (slug.Length * 2 < value.Length)
|
||||
return Json("");
|
||||
|
||||
if (slug.Length > 100)
|
||||
slug = slug.Substring(0, 100);
|
||||
}
|
||||
|
||||
return Json(slug);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,6 @@ namespace Orchard.Blogs.Controllers {
|
||||
protected override DriverResult Editor(BlogPost post) {
|
||||
return Combined(
|
||||
ContentItemTemplate("Items/Blogs.BlogPost"),
|
||||
ContentPartTemplate(post, "Parts/Blogs.BlogPost.Fields").Location("primary", "1"),
|
||||
ContentPartTemplate(post, "Parts/Blogs.BlogPost.Publish").Location("secondary", "1"));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Web.Mvc;
|
||||
using Orchard.Core.Common.Models;
|
||||
using Orchard.ContentManagement;
|
||||
@@ -8,17 +7,13 @@ namespace Orchard.Blogs.Models {
|
||||
[HiddenInput(DisplayValue = false)]
|
||||
public int Id { get { return ContentItem.Id; } }
|
||||
|
||||
[Required]
|
||||
public string Name {
|
||||
get { return this.As<RoutableAspect>().Title; }
|
||||
set { this.As<RoutableAspect>().Record.Title = value; }
|
||||
}
|
||||
|
||||
//TODO: (erikpo) Need a data type for slug
|
||||
[Required]
|
||||
public string Slug {
|
||||
get { return this.As<RoutableAspect>().Slug; }
|
||||
set { this.As<RoutableAspect>().Record.Slug = value; }
|
||||
}
|
||||
|
||||
public string Description {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Web.Mvc;
|
||||
using Orchard.ContentManagement.Aspects;
|
||||
using Orchard.Core.Common.Models;
|
||||
@@ -11,16 +10,12 @@ namespace Orchard.Blogs.Models {
|
||||
[HiddenInput(DisplayValue = false)]
|
||||
public int Id { get { return ContentItem.Id; } }
|
||||
|
||||
[Required]
|
||||
public string Title {
|
||||
get { return this.As<RoutableAspect>().Title; }
|
||||
set { this.As<RoutableAspect>().Title = value; }
|
||||
}
|
||||
|
||||
[Required]
|
||||
public string Slug {
|
||||
get { return this.As<RoutableAspect>().Slug; }
|
||||
set { this.As<RoutableAspect>().Slug = value; }
|
||||
}
|
||||
|
||||
public Blog Blog {
|
||||
|
||||
@@ -133,7 +133,6 @@
|
||||
<Content Include="Views\EditorTemplates\Parts\Blogs.Blog.Fields.ascx" />
|
||||
<Content Include="Views\EditorTemplates\Items\Blogs.Blog.ascx" />
|
||||
<Content Include="Views\EditorTemplates\Items\Blogs.BlogPost.ascx" />
|
||||
<Content Include="Views\EditorTemplates\Parts\Blogs.BlogPost.Fields.ascx" />
|
||||
<Content Include="Web.config" />
|
||||
<Content Include="Views\Web.config" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<Blog>" %>
|
||||
<%@ Import Namespace="Orchard.Blogs.Extensions"%>
|
||||
<%@ Import Namespace="Orchard.Blogs.Models"%>
|
||||
<fieldset>
|
||||
<%=Html.LabelFor(m => m.Name) %>
|
||||
<%=Html.EditorFor(m => m.Name) %>
|
||||
</fieldset>
|
||||
<fieldset class="permalink">
|
||||
<label class="sub" for="Slug"><%=_Encoded("Permalink")%><br /><span><%=Html.Encode(Request.Url.ToRootString()) %>/</span></label>
|
||||
<span><%=Html.TextBoxFor(m => m.Slug, new { @class = "text" })%></span>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<%=Html.LabelFor(m => m.Description) %>
|
||||
<%=Html.TextAreaFor(m => m.Description, 5, 60, null) %>
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<BlogPost>" %>
|
||||
<%@ Import Namespace="Orchard.Blogs.Extensions"%>
|
||||
<%@ Import Namespace="Orchard.Blogs.Models"%>
|
||||
<fieldset>
|
||||
<%=Html.LabelFor(m => m.Title) %>
|
||||
<%=Html.TextBoxFor(m => m.Title, new { @class = "large text" })%>
|
||||
</fieldset>
|
||||
<fieldset class="permalink">
|
||||
<label class="sub" for="Slug"><%=_Encoded("Permalink")%><br /><span><%=Html.Encode(Request.Url.ToRootString()) %>/<%=Html.Encode(Model.Blog.Slug) %>/</span></label>
|
||||
<span><%=Html.TextBoxFor(m => m.Slug, new { @class = "text" })%></span>
|
||||
</fieldset>
|
||||
@@ -47,7 +47,6 @@ namespace Orchard.Pages.Controllers {
|
||||
protected override DriverResult Editor(Page page) {
|
||||
return Combined(
|
||||
ContentItemTemplate("Items/Pages.Page"),
|
||||
ContentPartTemplate(page, "Parts/Pages.Page.Fields").Location("primary", "1"),
|
||||
ContentPartTemplate(page, "Parts/Pages.Page.Publish").Location("secondary", "1"));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Web.Mvc;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Core.Common.Models;
|
||||
@@ -10,16 +9,12 @@ namespace Orchard.Pages.Models {
|
||||
[HiddenInput(DisplayValue = false)]
|
||||
public int Id { get { return ContentItem.Id; } }
|
||||
|
||||
[Required]
|
||||
public string Title {
|
||||
get { return this.As<RoutableAspect>().Title; }
|
||||
set { this.As<RoutableAspect>().Title = value; }
|
||||
}
|
||||
|
||||
[Required]
|
||||
public string Slug {
|
||||
get { return this.As<RoutableAspect>().Slug; }
|
||||
set { this.As<RoutableAspect>().Slug = value; }
|
||||
}
|
||||
|
||||
public IUser Creator {
|
||||
|
||||
@@ -105,7 +105,6 @@
|
||||
<Content Include="Views\DisplayTemplates\Items\Pages.Page.ascx" />
|
||||
<Content Include="Views\DisplayTemplates\Items\Pages.Page.Summary.ascx" />
|
||||
<Content Include="Views\EditorTemplates\Items\Pages.Page.ascx" />
|
||||
<Content Include="Views\EditorTemplates\Parts\Pages.Page.Fields.ascx" />
|
||||
<Content Include="Views\EditorTemplates\Parts\Pages.Page.Publish.ascx" />
|
||||
<Content Include="Views\Admin\Create.ascx" />
|
||||
<Content Include="Views\Admin\Edit.ascx" />
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<Orchard.Pages.Models.Page>" %>
|
||||
<%@ Import Namespace="Orchard.Pages.Extensions"%>
|
||||
<fieldset>
|
||||
<%=Html.LabelFor(m => m.Title) %>
|
||||
<%=Html.TextBoxFor(m => m.Title, new { @class = "large text" })%>
|
||||
</fieldset>
|
||||
<fieldset class="permalink">
|
||||
<label class="sub" for="Slug"><%=_Encoded("Permalink")%><br /><span><%=Html.Encode(Request.Url.ToRootString()) %>/</span></label>
|
||||
<span><%=Html.TextBoxFor(m => m.Slug, new { @class = "text" })%></span>
|
||||
</fieldset>
|
||||
Reference in New Issue
Block a user