mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 03:25:23 +08:00
Merge from dev
--HG-- branch : dev
This commit is contained in:
104
src/Orchard.Web/Core/Common/Drivers/CommonDriver.cs
Normal file
104
src/Orchard.Web/Core/Common/Drivers/CommonDriver.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Drivers;
|
||||
using Orchard.Core.Common.Models;
|
||||
using Orchard.Core.Common.ViewModels;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Security;
|
||||
using Orchard.Services;
|
||||
|
||||
namespace Orchard.Core.Common.Drivers {
|
||||
public class CommonDriver : ContentPartDriver<CommonAspect> {
|
||||
private readonly IContentManager _contentManager;
|
||||
private readonly IAuthenticationService _authenticationService;
|
||||
private readonly IAuthorizationService _authorizationService;
|
||||
private readonly IMembershipService _membershipService;
|
||||
private readonly IClock _clock;
|
||||
|
||||
public CommonDriver(
|
||||
IContentManager contentManager,
|
||||
IAuthenticationService authenticationService,
|
||||
IAuthorizationService authorizationService,
|
||||
IMembershipService membershipService,
|
||||
IClock clock) {
|
||||
_contentManager = contentManager;
|
||||
_authenticationService = authenticationService;
|
||||
_authorizationService = authorizationService;
|
||||
_membershipService = membershipService;
|
||||
_clock = clock;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
|
||||
protected override DriverResult Editor(CommonAspect part) {
|
||||
return Combined(OwnerEditor(part, null), ContainerEditor(part, null));
|
||||
}
|
||||
|
||||
protected override DriverResult Editor(CommonAspect instance, ContentManagement.IUpdateModel updater) {
|
||||
// this event is hooked so the modified timestamp is changed when an edit-post occurs.
|
||||
instance.ModifiedUtc = _clock.UtcNow;
|
||||
instance.VersionModifiedUtc = _clock.UtcNow;
|
||||
|
||||
return Combined(OwnerEditor(instance, updater), ContainerEditor(instance, updater));
|
||||
}
|
||||
|
||||
DriverResult OwnerEditor(CommonAspect part, IUpdateModel updater) {
|
||||
var currentUser = _authenticationService.GetAuthenticatedUser();
|
||||
if (!_authorizationService.TryCheckAccess(Permissions.ChangeOwner, currentUser, part)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var model = new OwnerEditorViewModel();
|
||||
if (part.Owner != null)
|
||||
model.Owner = part.Owner.UserName;
|
||||
|
||||
if (updater != null) {
|
||||
var priorOwner = model.Owner;
|
||||
updater.TryUpdateModel(model, "CommonAspect", null, null);
|
||||
|
||||
if (model.Owner != null && model.Owner != priorOwner) {
|
||||
var newOwner = _membershipService.GetUser(model.Owner);
|
||||
if (newOwner == null) {
|
||||
updater.AddModelError("CommonAspect.Owner", T("Invalid user name"));
|
||||
}
|
||||
else {
|
||||
part.Owner = newOwner;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ContentPartTemplate(model, "Parts/Common.Owner", "CommonAspect").Location("primary", "10");
|
||||
}
|
||||
|
||||
DriverResult ContainerEditor(CommonAspect part, IUpdateModel updater) {
|
||||
var currentUser = _authenticationService.GetAuthenticatedUser();
|
||||
if (!_authorizationService.TryCheckAccess(Permissions.ChangeOwner, currentUser, part)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var model = new ContainerEditorViewModel();
|
||||
if (part.Container != null)
|
||||
model.ContainerId = part.Container.ContentItem.Id;
|
||||
|
||||
if (updater != null) {
|
||||
var priorContainerId = model.ContainerId;
|
||||
updater.TryUpdateModel(model, "CommonAspect", null, null);
|
||||
|
||||
if (model.ContainerId != null && model.ContainerId != priorContainerId) {
|
||||
var newContainer = _contentManager.Get((int)model.ContainerId, VersionOptions.Latest);
|
||||
if (newContainer == null) {
|
||||
updater.AddModelError("CommonAspect.ContainerId", T("Invalid container"));
|
||||
}
|
||||
else {
|
||||
part.Container = newContainer;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ContentPartTemplate(model, "Parts/Common.Container", "CommonAspect").Location("primary", "10.1");
|
||||
}
|
||||
}
|
||||
}
|
@@ -52,8 +52,8 @@ namespace Orchard.Core.Common.Handlers {
|
||||
OnPublishing<ContentPart<CommonVersionRecord>>(AssignPublishingDates);
|
||||
|
||||
//OnGetDisplayViewModel<CommonAspect>();
|
||||
OnGetEditorViewModel<CommonAspect>(GetEditor);
|
||||
OnUpdateEditorViewModel<CommonAspect>(UpdateEditor);
|
||||
//OnGetEditorViewModel<CommonAspect>(GetEditor);
|
||||
//OnUpdateEditorViewModel<CommonAspect>(UpdateEditor);
|
||||
|
||||
OnIndexing<CommonAspect>((context, commonAspect) => context.IndexDocument
|
||||
.Add("type", commonAspect.ContentItem.ContentType).Analyze(false)
|
||||
@@ -157,48 +157,48 @@ namespace Orchard.Core.Common.Handlers {
|
||||
}
|
||||
|
||||
|
||||
private void GetEditor(BuildEditorModelContext context, CommonAspect instance) {
|
||||
var currentUser = _authenticationService.GetAuthenticatedUser();
|
||||
if (!_authorizationService.TryCheckAccess(Permissions.ChangeOwner, currentUser, instance)) {
|
||||
return;
|
||||
}
|
||||
var viewModel = new OwnerEditorViewModel();
|
||||
if (instance.Owner != null)
|
||||
viewModel.Owner = instance.Owner.UserName;
|
||||
//private void GetEditor(BuildEditorModelContext context, CommonAspect instance) {
|
||||
// var currentUser = _authenticationService.GetAuthenticatedUser();
|
||||
// if (!_authorizationService.TryCheckAccess(Permissions.ChangeOwner, currentUser, instance)) {
|
||||
// return;
|
||||
// }
|
||||
// var viewModel = new OwnerEditorViewModel();
|
||||
// if (instance.Owner != null)
|
||||
// viewModel.Owner = instance.Owner.UserName;
|
||||
|
||||
context.AddEditor(new TemplateViewModel(viewModel, "CommonAspect") { TemplateName = "Parts/Common.Owner", ZoneName = "primary", Position = "999" });
|
||||
}
|
||||
// context.AddEditor(new TemplateViewModel(viewModel, "CommonAspect") { TemplateName = "Parts/Common.Owner", ZoneName = "primary", Position = "999" });
|
||||
//}
|
||||
|
||||
|
||||
private void UpdateEditor(UpdateEditorModelContext context, CommonAspect instance) {
|
||||
// this event is hooked so the modified timestamp is changed when an edit-post occurs.
|
||||
// kind of a loose rule of thumb. may not be sufficient
|
||||
instance.ModifiedUtc = _clock.UtcNow;
|
||||
instance.VersionModifiedUtc = _clock.UtcNow;
|
||||
//private void UpdateEditor(UpdateEditorModelContext context, CommonAspect instance) {
|
||||
// // this event is hooked so the modified timestamp is changed when an edit-post occurs.
|
||||
// // kind of a loose rule of thumb. may not be sufficient
|
||||
// instance.ModifiedUtc = _clock.UtcNow;
|
||||
// instance.VersionModifiedUtc = _clock.UtcNow;
|
||||
|
||||
var currentUser = _authenticationService.GetAuthenticatedUser();
|
||||
if (!_authorizationService.TryCheckAccess(Permissions.ChangeOwner, currentUser, instance)) {
|
||||
return;
|
||||
}
|
||||
// var currentUser = _authenticationService.GetAuthenticatedUser();
|
||||
// if (!_authorizationService.TryCheckAccess(Permissions.ChangeOwner, currentUser, instance)) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
var viewModel = new OwnerEditorViewModel();
|
||||
if (instance.Owner != null)
|
||||
viewModel.Owner = instance.Owner.UserName;
|
||||
// var viewModel = new OwnerEditorViewModel();
|
||||
// if (instance.Owner != null)
|
||||
// viewModel.Owner = instance.Owner.UserName;
|
||||
|
||||
var priorOwner = viewModel.Owner;
|
||||
context.Updater.TryUpdateModel(viewModel, "CommonAspect", null, null);
|
||||
// var priorOwner = viewModel.Owner;
|
||||
// context.Updater.TryUpdateModel(viewModel, "CommonAspect", null, null);
|
||||
|
||||
if (viewModel.Owner != null && viewModel.Owner != priorOwner) {
|
||||
var newOwner = _membershipService.GetUser(viewModel.Owner);
|
||||
if (newOwner == null) {
|
||||
context.Updater.AddModelError("CommonAspect.Owner", T("Invalid user name"));
|
||||
}
|
||||
else {
|
||||
instance.Owner = newOwner;
|
||||
}
|
||||
}
|
||||
// if (viewModel.Owner != null && viewModel.Owner != priorOwner) {
|
||||
// var newOwner = _membershipService.GetUser(viewModel.Owner);
|
||||
// if (newOwner == null) {
|
||||
// context.Updater.AddModelError("CommonAspect.Owner", T("Invalid user name"));
|
||||
// }
|
||||
// else {
|
||||
// instance.Owner = newOwner;
|
||||
// }
|
||||
// }
|
||||
|
||||
context.AddEditor(new TemplateViewModel(viewModel, "CommonAspect") { TemplateName = "Parts/Common.Owner", ZoneName = "primary", Position = "999" });
|
||||
}
|
||||
// context.AddEditor(new TemplateViewModel(viewModel, "CommonAspect") { TemplateName = "Parts/Common.Owner", ZoneName = "primary", Position = "999" });
|
||||
//}
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Orchard.Core.Common.ViewModels {
|
||||
public class ContainerEditorViewModel {
|
||||
|
||||
public int? ContainerId { get; set; }
|
||||
}
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<ContainerEditorViewModel>" %>
|
||||
<%@ Import Namespace="Orchard.Core.Common.ViewModels" %>
|
||||
<fieldset>
|
||||
<%=Html.LabelFor(m=>m.ContainerId) %>
|
||||
<%=Html.EditorFor(m=>m.ContainerId) %>
|
||||
<%=Html.ValidationMessageFor(m=>m.ContainerId) %>
|
||||
</fieldset>
|
@@ -66,9 +66,11 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Common\Drivers\BodyDriver.cs" />
|
||||
<Compile Include="Common\Drivers\CommonDriver.cs" />
|
||||
<Compile Include="Common\Drivers\RoutableDriver.cs" />
|
||||
<Compile Include="Common\Controllers\RoutableController.cs" />
|
||||
<Compile Include="Common\Handlers\RoutableAspectHandler.cs" />
|
||||
<Compile Include="Common\ViewModels\ContainerEditorViewModel.cs" />
|
||||
<Compile Include="Contents\Controllers\ItemController.cs" />
|
||||
<Compile Include="Contents\Handlers\ContentsModuleHandler.cs" />
|
||||
<Compile Include="Localization\Drivers\LocalizedDriver.cs" />
|
||||
@@ -155,8 +157,11 @@
|
||||
<Compile Include="Navigation\ViewModels\NavigationManagementViewModel.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Routable\Routes.cs" />
|
||||
<Compile Include="Routable\Services\IRoutableService.cs" />
|
||||
<Compile Include="Routable\Services\RoutablePathConstraint.cs" />
|
||||
<Compile Include="Routable\Services\RoutablePathConstraintUpdator.cs" />
|
||||
<Compile Include="Routable\Services\RoutableService.cs" />
|
||||
<Compile Include="Routable\ViewModels\RoutableEditorViewModel.cs" />
|
||||
<Compile Include="Routable\ViewModels\RoutableDisplayViewModel.cs" />
|
||||
<Compile Include="Scheduling\Models\ScheduledTaskRecord.cs" />
|
||||
<Compile Include="Scheduling\Services\PublishingTaskHandler.cs" />
|
||||
@@ -202,6 +207,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Common\Module.txt" />
|
||||
<Content Include="Common\Views\EditorTemplates\Parts\Common.Container.ascx" />
|
||||
<Content Include="Contents\Module.txt" />
|
||||
<Content Include="Contents\Views\Admin\Types.aspx" />
|
||||
<Content Include="Contents\Views\Admin\List.aspx" />
|
||||
@@ -215,6 +221,8 @@
|
||||
<Content Include="Indexing\Module.txt" />
|
||||
<Content Include="Localization\Module.txt" />
|
||||
<Content Include="Routable\Module.txt" />
|
||||
<Content Include="Routable\Scripts\jquery.slugify.js" />
|
||||
<Content Include="Routable\Views\EditorTemplates\Parts\Routable.IsRoutable.ascx" />
|
||||
<Content Include="Routable\Views\Item\Display.aspx" />
|
||||
<Content Include="Settings\Module.txt" />
|
||||
<Content Include="Settings\Styles\admin.css" />
|
||||
|
@@ -6,16 +6,20 @@ using Orchard.ContentManagement.Aspects;
|
||||
using Orchard.Core.Common.Models;
|
||||
using Orchard.Core.Routable.Models;
|
||||
using Orchard.Core.Routable.ViewModels;
|
||||
using Orchard.Data;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Mvc.ViewModels;
|
||||
|
||||
namespace Orchard.Core.Routable.Controllers {
|
||||
[ValidateInput(false)]
|
||||
public class ItemController : Controller {
|
||||
public class ItemController : Controller, IUpdateModel {
|
||||
private readonly IContentManager _contentManager;
|
||||
private readonly ITransactionManager _transactionManager;
|
||||
private readonly IRoutablePathConstraint _routablePathConstraint;
|
||||
|
||||
public ItemController(IContentManager contentManager, IRoutablePathConstraint routablePathConstraint) {
|
||||
public ItemController(IContentManager contentManager, ITransactionManager transactionManager, IRoutablePathConstraint routablePathConstraint) {
|
||||
_contentManager = contentManager;
|
||||
_transactionManager = transactionManager;
|
||||
_routablePathConstraint = routablePathConstraint;
|
||||
}
|
||||
|
||||
@@ -47,5 +51,39 @@ namespace Orchard.Core.Routable.Controllers {
|
||||
itemViewModel.TemplateName = "Items/Contents.Item";
|
||||
}
|
||||
}
|
||||
|
||||
public ActionResult Slugify(string contentType, int? id, int? containerId) {
|
||||
const string slug = "";
|
||||
ContentItem contentItem = null;
|
||||
|
||||
if (string.IsNullOrEmpty(contentType))
|
||||
return Json(slug);
|
||||
|
||||
if (id != null)
|
||||
contentItem = _contentManager.Get((int)id, VersionOptions.Latest);
|
||||
|
||||
if (contentItem == null) {
|
||||
contentItem = _contentManager.New(contentType);
|
||||
|
||||
if (containerId != null) {
|
||||
var containerItem = _contentManager.Get((int)containerId);
|
||||
contentItem.As<ICommonAspect>().Container = containerItem;
|
||||
}
|
||||
}
|
||||
|
||||
_contentManager.UpdateEditorModel(contentItem, this);
|
||||
_transactionManager.Cancel();
|
||||
|
||||
return Json(contentItem.As<IRoutableAspect>().Slug ?? slug);
|
||||
}
|
||||
|
||||
|
||||
bool IUpdateModel.TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) {
|
||||
return TryUpdateModel(model, prefix, includeProperties, excludeProperties);
|
||||
}
|
||||
|
||||
void IUpdateModel.AddModelError(string key, LocalizedString errorMessage) {
|
||||
ModelState.AddModelError(key, errorMessage.ToString());
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,61 +1,99 @@
|
||||
using JetBrains.Annotations;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Aspects;
|
||||
using Orchard.ContentManagement.Drivers;
|
||||
using Orchard.Core.Common.Models;
|
||||
using Orchard.Core.Common.ViewModels;
|
||||
using Orchard.Core.Common.Services;
|
||||
using Orchard.Core.Routable.Models;
|
||||
using Orchard.Core.Routable.Services;
|
||||
using Orchard.Core.Routable.ViewModels;
|
||||
using Orchard.Localization;
|
||||
using Orchard.UI.Notify;
|
||||
|
||||
namespace Orchard.Core.Routable.Drivers {
|
||||
public class RoutableDriver : ContentPartDriver<IsRoutable> {
|
||||
protected override DriverResult Editor(IsRoutable part, IUpdateModel updater) {
|
||||
part.Record.Title = "Routable #" + part.ContentItem.Id;
|
||||
part.Record.Slug = "routable" + part.ContentItem.Id;
|
||||
part.Record.Path = "routable" + part.ContentItem.Id;
|
||||
return base.Editor(part, updater);
|
||||
private readonly IOrchardServices _services;
|
||||
private readonly IRoutableService _routableService;
|
||||
|
||||
public RoutableDriver(IOrchardServices services, IRoutableService routableService) {
|
||||
_services = services;
|
||||
_routableService = routableService;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
//private const string TemplateName = "Parts/Common.Routable";
|
||||
|
||||
//private readonly IOrchardServices _services;
|
||||
//private readonly IRoutableService _routableService;
|
||||
//public Localizer T { get; set; }
|
||||
private const string TemplateName = "Parts/Routable.IsRoutable";
|
||||
|
||||
//protected override string Prefix {
|
||||
// get { return "Routable"; }
|
||||
//}
|
||||
public Localizer T { get; set; }
|
||||
|
||||
//public Routable(IOrchardServices services, IRoutableService routableService)
|
||||
//{
|
||||
// _services = services;
|
||||
// _routableService = routableService;
|
||||
protected override string Prefix {
|
||||
get { return "Routable"; }
|
||||
}
|
||||
|
||||
// T = NullLocalizer.Instance;
|
||||
//}
|
||||
int? GetContainerId(IContent item) {
|
||||
var commonAspect = item.As<ICommonAspect>();
|
||||
if (commonAspect != null && commonAspect.Container != null) {
|
||||
return commonAspect.Container.ContentItem.Id;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//protected override DriverResult Editor(RoutableAspect part) {
|
||||
// var model = new RoutableEditorViewModel { Prefix = Prefix, RoutableAspect = part };
|
||||
// return ContentPartTemplate(model, TemplateName, Prefix).Location("primary", "before.5");
|
||||
//}
|
||||
string GetContainerSlug(IContent item) {
|
||||
var commonAspect = item.As<ICommonAspect>();
|
||||
if (commonAspect != null && commonAspect.Container != null) {
|
||||
var routable = commonAspect.Container.As<IRoutableAspect>();
|
||||
if (routable != null) {
|
||||
return routable.Slug;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//protected override DriverResult Editor(RoutableAspect part, IUpdateModel updater) {
|
||||
// var model = new RoutableEditorViewModel { Prefix = Prefix, RoutableAspect = part };
|
||||
// updater.TryUpdateModel(model, Prefix, null, null);
|
||||
protected override DriverResult Editor(IsRoutable part) {
|
||||
var model = new RoutableEditorViewModel {
|
||||
ContentType = part.ContentItem.ContentType,
|
||||
Id = part.ContentItem.Id,
|
||||
Slug = part.Slug,
|
||||
Title = part.Title,
|
||||
ContainerId = GetContainerId(part),
|
||||
};
|
||||
|
||||
// if (!_routableService.IsSlugValid(part.Slug)){
|
||||
// updater.AddModelError("Routable.Slug", T("Please do not use any of the following characters in your slugs: \"/\", \":\", \"?\", \"#\", \"[\", \"]\", \"@\", \"!\", \"$\", \"&\", \"'\", \"(\", \")\", \"*\", \"+\", \",\", \";\", \"=\". No spaces are allowed (please use dashes or underscores instead).").ToString());
|
||||
// }
|
||||
// TEMP: path format patterns replaces this logic
|
||||
var path = part.Record.Path;
|
||||
if (path.EndsWith(part.Slug)) {
|
||||
model.DisplayLeadingPath = path.Substring(0, path.Length - part.Slug.Length);
|
||||
}
|
||||
|
||||
// string originalSlug = part.Slug;
|
||||
// if(!_routableService.ProcessSlug(part)) {
|
||||
// _services.Notifier.Warning(T("Slugs in conflict. \"{0}\" is already set for a previously created {2} so now it has the slug \"{1}\"",
|
||||
// originalSlug, part.Slug, part.ContentItem.ContentType));
|
||||
// }
|
||||
|
||||
// return ContentPartTemplate(model, TemplateName, Prefix).Location("primary", "before.5");
|
||||
//}
|
||||
return ContentPartTemplate(model, TemplateName, Prefix).Location("primary", "before.5");
|
||||
}
|
||||
|
||||
protected override DriverResult Editor(IsRoutable part, IUpdateModel updater) {
|
||||
|
||||
var model = new RoutableEditorViewModel();
|
||||
updater.TryUpdateModel(model, Prefix, null, null);
|
||||
part.Title = model.Title;
|
||||
part.Slug = model.Slug;
|
||||
|
||||
// TEMP: path format patterns replaces this logic
|
||||
var containerSlug = GetContainerSlug(part);
|
||||
if (string.IsNullOrEmpty(containerSlug)) {
|
||||
part.Record.Path = model.Slug;
|
||||
}
|
||||
else {
|
||||
part.Record.Path = containerSlug + "/" + model.Slug;
|
||||
}
|
||||
|
||||
if (!_routableService.IsSlugValid(part.Slug)) {
|
||||
updater.AddModelError("Routable.Slug", T("Please do not use any of the following characters in your slugs: \"/\", \":\", \"?\", \"#\", \"[\", \"]\", \"@\", \"!\", \"$\", \"&\", \"'\", \"(\", \")\", \"*\", \"+\", \",\", \";\", \"=\". No spaces are allowed (please use dashes or underscores instead).").ToString());
|
||||
}
|
||||
|
||||
string originalSlug = part.Slug;
|
||||
if (!_routableService.ProcessSlug(part)) {
|
||||
_services.Notifier.Warning(T("Slugs in conflict. \"{0}\" is already set for a previously created {2} so now it has the slug \"{1}\"",
|
||||
originalSlug, part.Slug, part.ContentItem.ContentType));
|
||||
}
|
||||
|
||||
return Editor(part);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
24
src/Orchard.Web/Core/Routable/Scripts/jquery.slugify.js
Normal file
24
src/Orchard.Web/Core/Routable/Scripts/jquery.slugify.js
Normal file
@@ -0,0 +1,24 @@
|
||||
jQuery.fn.extend({
|
||||
slugify: function(options) {
|
||||
//todo: (heskew) need messaging system
|
||||
if (!options.target || !options.url)
|
||||
return;
|
||||
|
||||
var args = {
|
||||
"contentType": options.contentType,
|
||||
"id": options.id,
|
||||
"containerId": options.containerId,
|
||||
__RequestVerificationToken: $("input[name=__RequestVerificationToken]").val()
|
||||
};
|
||||
args[$(this).attr("name")] = $(this).val();
|
||||
|
||||
jQuery.post(
|
||||
options.url,
|
||||
args,
|
||||
function(data) {
|
||||
options.target.val(data);
|
||||
},
|
||||
"json"
|
||||
);
|
||||
}
|
||||
});
|
28
src/Orchard.Web/Core/Routable/Services/IRoutableService.cs
Normal file
28
src/Orchard.Web/Core/Routable/Services/IRoutableService.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Core.Routable.Models;
|
||||
|
||||
namespace Orchard.Core.Routable.Services {
|
||||
public interface IRoutableService : IDependency {
|
||||
void FillSlug<TModel>(TModel model) where TModel : IsRoutable;
|
||||
void FillSlug<TModel>(TModel model, Func<string, string> generateSlug) where TModel : IsRoutable;
|
||||
string GenerateUniqueSlug(string slugCandidate, IEnumerable<string> existingSlugs);
|
||||
|
||||
/// <summary>
|
||||
/// Returns any content item of the specified content type with similar slugs
|
||||
/// </summary>
|
||||
IEnumerable<IsRoutable> GetSimilarSlugs(string contentType, string slug);
|
||||
|
||||
/// <summary>
|
||||
/// Validates the given slug
|
||||
/// </summary>
|
||||
bool IsSlugValid(string slug);
|
||||
|
||||
/// <summary>
|
||||
/// Defines the slug of a RoutableAspect and validate its unicity
|
||||
/// </summary>
|
||||
/// <returns>True if the slug has been created, False if a conflict occured</returns>
|
||||
bool ProcessSlug(IsRoutable part);
|
||||
|
||||
}
|
||||
}
|
113
src/Orchard.Web/Core/Routable/Services/RoutableService.cs
Normal file
113
src/Orchard.Web/Core/Routable/Services/RoutableService.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using JetBrains.Annotations;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Core.Common.Models;
|
||||
using Orchard.Core.Routable.Models;
|
||||
using Orchard.Localization;
|
||||
using Orchard.UI.Notify;
|
||||
|
||||
namespace Orchard.Core.Routable.Services {
|
||||
[UsedImplicitly]
|
||||
public class RoutableService : IRoutableService {
|
||||
private readonly IContentManager _contentManager;
|
||||
|
||||
public RoutableService(IContentManager contentManager) {
|
||||
_contentManager = contentManager;
|
||||
}
|
||||
|
||||
public void FillSlug<TModel>(TModel model) where TModel : IsRoutable {
|
||||
if (!string.IsNullOrEmpty(model.Slug) || string.IsNullOrEmpty(model.Title))
|
||||
return;
|
||||
|
||||
var slug = model.Title;
|
||||
var dissallowed = new Regex(@"[/:?#\[\]@!$&'()*+,;=\s]+");
|
||||
|
||||
slug = dissallowed.Replace(slug, "-");
|
||||
slug = slug.Trim('-');
|
||||
|
||||
if (slug.Length > 1000)
|
||||
slug = slug.Substring(0, 1000);
|
||||
|
||||
model.Slug = slug.ToLowerInvariant();
|
||||
}
|
||||
|
||||
public void FillSlug<TModel>(TModel model, Func<string, string> generateSlug) where TModel : IsRoutable {
|
||||
if (!string.IsNullOrEmpty(model.Slug) || string.IsNullOrEmpty(model.Title))
|
||||
return;
|
||||
|
||||
model.Slug = generateSlug(model.Title).ToLowerInvariant();
|
||||
}
|
||||
|
||||
public string GenerateUniqueSlug(string slugCandidate, IEnumerable<string> existingSlugs) {
|
||||
if (existingSlugs == null || !existingSlugs.Contains(slugCandidate))
|
||||
return slugCandidate;
|
||||
|
||||
int? version = existingSlugs.Select(s => GetSlugVersion(slugCandidate, s)).OrderBy(i => i).LastOrDefault();
|
||||
|
||||
return version != null
|
||||
? string.Format("{0}-{1}", slugCandidate, version)
|
||||
: slugCandidate;
|
||||
}
|
||||
|
||||
private static int? GetSlugVersion(string slugCandidate, string slug) {
|
||||
int v;
|
||||
string[] slugParts = slug.Split(new []{slugCandidate}, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if (slugParts.Length == 0)
|
||||
return 2;
|
||||
|
||||
return int.TryParse(slugParts[0].TrimStart('-'), out v)
|
||||
? (int?)++v
|
||||
: null;
|
||||
}
|
||||
|
||||
public IEnumerable<IsRoutable> GetSimilarSlugs(string contentType, string slug)
|
||||
{
|
||||
return
|
||||
_contentManager.Query(contentType).Join<RoutableRecord>()
|
||||
.List()
|
||||
.Select(i => i.As<IsRoutable>())
|
||||
.Where(routable => routable.Slug.StartsWith(slug, StringComparison.OrdinalIgnoreCase)) // todo: for some reason the filter doesn't work within the query, even without StringComparison or StartsWith
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
public bool IsSlugValid(string slug) {
|
||||
// see http://tools.ietf.org/html/rfc3987 for prohibited chars
|
||||
return slug == null || String.IsNullOrEmpty(slug.Trim()) || Regex.IsMatch(slug, @"^[^/:?#\[\]@!$&'()*+,;=\s]+$");
|
||||
}
|
||||
|
||||
public bool ProcessSlug(IsRoutable part)
|
||||
{
|
||||
FillSlug(part);
|
||||
|
||||
if (string.IsNullOrEmpty(part.Slug))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var slugsLikeThis = GetSimilarSlugs(part.ContentItem.ContentType, part.Slug);
|
||||
|
||||
// If the part is already a valid content item, don't include it in the list
|
||||
// of slug to consider for conflict detection
|
||||
if (part.ContentItem.Id != 0)
|
||||
slugsLikeThis = slugsLikeThis.Where(p => p.ContentItem.Id != part.ContentItem.Id);
|
||||
|
||||
//todo: (heskew) need better messages
|
||||
if (slugsLikeThis.Count() > 0)
|
||||
{
|
||||
var originalSlug = part.Slug;
|
||||
//todo: (heskew) make auto-uniqueness optional
|
||||
part.Slug = GenerateUniqueSlug(part.Slug, slugsLikeThis.Select(p => p.Slug));
|
||||
|
||||
if (originalSlug != part.Slug) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@@ -2,7 +2,7 @@
|
||||
using Orchard.Mvc.ViewModels;
|
||||
|
||||
namespace Orchard.Core.Routable.ViewModels {
|
||||
public class RoutableDisplayViewModel : BaseViewModel {
|
||||
public class RoutableDisplayViewModel : BaseViewModel {
|
||||
public ContentItemViewModel<IRoutableAspect> Routable {get;set;}
|
||||
}
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Orchard.Core.Routable.ViewModels {
|
||||
public class RoutableEditorViewModel {
|
||||
|
||||
public int Id { get; set; }
|
||||
public string ContentType { get; set; }
|
||||
|
||||
[Required]
|
||||
public string Title { get; set; }
|
||||
public string Slug { get; set; }
|
||||
public int? ContainerId { get; set; }
|
||||
|
||||
public string DisplayLeadingPath { get; set; }
|
||||
}
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<Orchard.Core.Routable.ViewModels.RoutableEditorViewModel>" %>
|
||||
<%@ Import Namespace="Orchard.Utility.Extensions"%>
|
||||
<%@ Import Namespace="Orchard.ContentManagement.Extenstions"%>
|
||||
|
||||
<% Html.RegisterFootScript("jquery.slugify.js"); %>
|
||||
<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.ToRootUrlString())%>/<%:Model.DisplayLeadingPath %></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(){
|
||||
//pull slug input from tab order
|
||||
$("#<%:Html.FieldIdFor(m=>m.Slug)%>").attr("tabindex",-1);
|
||||
$("#<%:Html.FieldIdFor(m=>m.Title)%>").blur(function(){
|
||||
$(this).slugify({
|
||||
target:$("#<%:Html.FieldIdFor(m=>m.Slug)%>"),
|
||||
url:"<%=Url.Action("Slugify","Item",new RouteValueDictionary{{"Area","Routable"}})%>",
|
||||
contentType:"<%=Model.ContentType %>",
|
||||
id:"<%=Model.Id %>" <%if (Model.ContainerId != null) { %>,
|
||||
containerId:<%=Model.ContainerId %><%} %>
|
||||
})
|
||||
})
|
||||
})</script>
|
||||
<% } %>
|
@@ -1,7 +1,8 @@
|
||||
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<SiteCulturesViewModel>" %>
|
||||
<%@ Import Namespace="Orchard.Core.Settings.ViewModels" %><%
|
||||
Html.RegisterStyle("admin.css"); %>
|
||||
<h1><%:Html.TitleForPage(T("Manage Settings").ToString()) %></h1>
|
||||
<h1><%:Html.TitleForPage(T("Supported Cultures").ToString()) %></h1>
|
||||
<p class="breadcrumb"><%:Html.ActionLink(T("Manage Settings").Text, "index") %><%:T(" > ") %><%:T("Supported Cultures")%></p>
|
||||
<h2><%:T("Cultures this site supports") %></h2>
|
||||
<%: Html.UnorderedList(
|
||||
Model.SiteCultures.OrderBy(s => s),
|
||||
@@ -10,7 +11,7 @@
|
||||
<% using (Html.BeginFormAntiForgeryPost("AddCulture")) { %>
|
||||
<%:Html.ValidationSummary() %>
|
||||
<fieldset>
|
||||
<legend><%:T("Add a culture...") %></legend>
|
||||
<label for="CultureName"><%:T("Add a culture...") %></label>
|
||||
<%:Html.DropDownList("CultureName", new SelectList(Model.AvailableSystemCultures.OrderBy(s => s), Model.CurrentCulture)) %>
|
||||
<button class="primaryAction" type="submit"><%:T("Add") %></button>
|
||||
</fieldset>
|
||||
|
@@ -2,5 +2,5 @@
|
||||
<div><%:Model %></div>
|
||||
<% using (Html.BeginFormAntiForgeryPost(Url.Action("DeleteCulture", "Admin", new { area = "Settings" }), FormMethod.Post, new {@class = "inline link"})) { %>
|
||||
<%=Html.Hidden("cultureName", Model, new { id = "" }) %>
|
||||
<button type="submit" title="<%:T("Delete") %>">x</button>
|
||||
<button type="submit" class="remove" title="<%:T("Delete") %>">x</button>
|
||||
<% } %>
|
@@ -2,24 +2,25 @@ using System.Web.Mvc;
|
||||
using Orchard.Blogs.Models;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Aspects;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Mvc.Html;
|
||||
|
||||
namespace Orchard.Blogs.Extensions {
|
||||
public static class HtmlHelperExtensions {
|
||||
public static string PublishedState(this HtmlHelper<BlogPost> htmlHelper) {
|
||||
return htmlHelper.PublishedState(htmlHelper.ViewData.Model);
|
||||
public static LocalizedString PublishedState(this HtmlHelper<BlogPost> htmlHelper, Localizer T) {
|
||||
return htmlHelper.PublishedState(htmlHelper.ViewData.Model, T);
|
||||
}
|
||||
|
||||
public static string PublishedState(this HtmlHelper htmlHelper, BlogPost blogPost) {
|
||||
return htmlHelper.DateTime(blogPost.As<ICommonAspect>().VersionPublishedUtc, "Draft");
|
||||
public static LocalizedString PublishedState(this HtmlHelper htmlHelper, BlogPost blogPost, Localizer T) {
|
||||
return htmlHelper.DateTime(blogPost.As<ICommonAspect>().VersionPublishedUtc, T("Draft"));
|
||||
}
|
||||
|
||||
public static string PublishedWhen(this HtmlHelper<BlogPost> htmlHelper) {
|
||||
return htmlHelper.PublishedWhen(htmlHelper.ViewData.Model);
|
||||
public static LocalizedString PublishedWhen(this HtmlHelper<BlogPost> htmlHelper, Localizer T) {
|
||||
return htmlHelper.PublishedWhen(htmlHelper.ViewData.Model, T);
|
||||
}
|
||||
|
||||
public static string PublishedWhen(this HtmlHelper htmlHelper, BlogPost blogPost) {
|
||||
return htmlHelper.DateTimeRelative(blogPost.As<ICommonAspect>().VersionPublishedUtc, "as a Draft");
|
||||
public static LocalizedString PublishedWhen(this HtmlHelper htmlHelper, BlogPost blogPost, Localizer T) {
|
||||
return htmlHelper.DateTimeRelative(blogPost.As<ICommonAspect>().VersionPublishedUtc, T("as a Draft"), T);
|
||||
}
|
||||
}
|
||||
}
|
@@ -5,21 +5,23 @@
|
||||
<%-- todo: Add helper text here when ready. <p><%: T("Possible text about setting up and managing a blog goes here.") %></p> --%><%
|
||||
if (Model.Entries.Count() > 0) { %>
|
||||
<div class="actions"><a class="add button primaryAction" href="<%=Url.BlogCreate() %>"><%: T("New Blog") %></a></div>
|
||||
<%: Html.UnorderedList(Model.Entries, (entry, i) => {
|
||||
<%=Html.UnorderedList(Model.Entries, (entry, i) => {
|
||||
// Add blog post count rendering into "meta" zone
|
||||
entry.ContentItemViewModel.Zones.AddAction("meta", html => {
|
||||
int draftCount = entry.TotalPostCount - entry.ContentItemViewModel.Item.PostCount;
|
||||
int totalPostCount = entry.TotalPostCount;
|
||||
var draftText = (draftCount == 0 ? "": string.Format(" ({0} draft{1})", draftCount, draftCount == 1 ? "" : "s"));
|
||||
|
||||
var linkText = T.Plural("1 post", "{0} posts", totalPostCount).ToString();
|
||||
if (draftCount==0){
|
||||
linkText = linkText + " (" + T.Plural("1 draft", "{0} drafts", draftCount).ToString() + ")";
|
||||
}
|
||||
|
||||
var linkContent = T("{0} post{1}{2}", totalPostCount, totalPostCount == 1 ? "" : "s", draftText);
|
||||
|
||||
html.ViewContext.Writer.Write(html.Link(linkContent.ToString(), Url.BlogForAdmin(entry.ContentItemViewModel.Item.Slug)));
|
||||
html.ViewContext.Writer.Write(html.Link(linkText, Url.BlogForAdmin(entry.ContentItemViewModel.Item.Slug)));
|
||||
});
|
||||
|
||||
// Display the summary for the blog post
|
||||
return Html.DisplayForItem(entry.ContentItemViewModel).ToHtmlString();
|
||||
}, "blogs contentItems")%><%
|
||||
} else { %>
|
||||
<div class="info message"><%=T("There are no blogs for you to see. Want to <a href=\"{0}\">add one</a>?", Url.BlogCreate()).ToString()%></div><%
|
||||
<div class="info message"><%:T("There are no blogs for you to see. Want to <a href=\"{0}\">add one</a>?", Url.BlogCreate())%></div><%
|
||||
} %>
|
@@ -4,4 +4,4 @@
|
||||
<%@ Import Namespace="Orchard.Blogs.Models"%>
|
||||
<h2><%: Html.Link(Model.Item.Name, Url.Blog(Model.Item.Slug)) %></h2>
|
||||
<% if (!string.IsNullOrEmpty(Model.Item.Description)) { %><p><%: Model.Item.Description %></p><% } %>
|
||||
<div class="blog metadata"><%: T("{0} post{1}", Model.Item.PostCount, Model.Item.PostCount == 1 ? "" : "s")%> | <%Html.Zone("meta");%></div>
|
||||
<div class="blog metadata"><%: T.Plural("1 post", "{0} posts", Model.Item.PostCount)%> | <%Html.Zone("meta");%></div>
|
||||
|
@@ -1,9 +1,7 @@
|
||||
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<ContentItemViewModel<BlogPost>>" %>
|
||||
<%@ Import Namespace="Orchard.ContentManagement"%>
|
||||
<%@ Import Namespace="Orchard.Core.Common.Models"%>
|
||||
<%@ Import Namespace="Orchard.Mvc.ViewModels"%>
|
||||
<%@ Import Namespace="Orchard.Blogs.Extensions"%>
|
||||
<%@ Import Namespace="Orchard.Blogs.Models"%>
|
||||
<h2><%: Html.Link(Model.Item.Title, Url.BlogPost(Model.Item)) %></h2>
|
||||
<div class="meta"><%=Html.PublishedState(Model.Item) %> | <%Html.Zone("meta");%></div>
|
||||
<div class="meta"><%=Html.PublishedState(Model.Item, T) %> | <%Html.Zone("meta");%></div>
|
||||
<div class="content"><% Html.Zone("primary", ":manage :metadata");%></div>
|
||||
|
@@ -1,7 +1,6 @@
|
||||
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<ContentItemViewModel<BlogPost>>" %>
|
||||
<%@ Import Namespace="Orchard.ContentManagement.Aspects"%>
|
||||
<%@ Import Namespace="Orchard.ContentManagement"%>
|
||||
<%@ Import Namespace="Orchard.Core.Common.Models"%>
|
||||
<%@ Import Namespace="Orchard.Mvc.ViewModels"%>
|
||||
<%@ Import Namespace="Orchard.Blogs.Extensions"%>
|
||||
<%@ Import Namespace="Orchard.Blogs.Models"%>
|
||||
@@ -11,15 +10,15 @@
|
||||
<ul>
|
||||
<li><%
|
||||
if (Model.Item.HasPublished) { %>
|
||||
<img class="icon" src="<%=ResolveUrl("~/Modules/Orchard.Blogs/Content/Admin/images/online.gif") %>" alt="<%: T("Online") %>" title="<%: T("The page is currently online") %>" /><%: T(" Published")%><%
|
||||
<img class="icon" src="<%=ResolveUrl("~/Modules/Orchard.Blogs/Content/Admin/images/online.gif") %>" alt="<%: T("Online") %>" title="<%: T("The page is currently online") %>" /> <%: T("Published")%><%
|
||||
}
|
||||
else { %>
|
||||
<img class="icon" src="<%=ResolveUrl("~/Modules/Orchard.Blogs/Content/Admin/images/offline.gif") %>" alt="<%: T("Offline") %>" title="<%: T("The page is currently offline") %>" /><%: T(" Not Published")%><%
|
||||
<img class="icon" src="<%=ResolveUrl("~/Modules/Orchard.Blogs/Content/Admin/images/offline.gif") %>" alt="<%: T("Offline") %>" title="<%: T("The page is currently offline") %>" /> <%: T("Not Published")%><%
|
||||
} %> |
|
||||
</li>
|
||||
<li><%
|
||||
if (Model.Item.HasDraft) { %>
|
||||
<img class="icon" src="<%=ResolveUrl("~/Modules/Orchard.Blogs/Content/Admin/images/draft.gif") %>" alt="<%: T("Draft") %>" title="<%: T("The post has a draft") %>" /><%=Html.PublishedState(Model.Item)%><%
|
||||
<img class="icon" src="<%=ResolveUrl("~/Modules/Orchard.Blogs/Content/Admin/images/draft.gif") %>" alt="<%: T("Draft") %>" title="<%: T("The post has a draft") %>" /><%=Html.PublishedState(Model.Item, T) %><%
|
||||
}
|
||||
else { %>
|
||||
<%: T("No draft")%><%
|
||||
@@ -31,10 +30,10 @@
|
||||
<%=Html.DateTime(Model.Item.ScheduledPublishUtc.Value, "M/d/yyyy h:mm tt")%><%
|
||||
}
|
||||
else if (Model.Item.IsPublished) { %>
|
||||
<%: T("Published: ") + Html.DateTimeRelative(Model.Item.As<ICommonAspect>().VersionPublishedUtc.Value)%><%
|
||||
<%: T("Published: {0}", Html.DateTimeRelative(Model.Item.As<ICommonAspect>().VersionPublishedUtc.Value, T)) %><%
|
||||
}
|
||||
else { %>
|
||||
<%: T("Last modified: ") + Html.DateTimeRelative(Model.Item.As<ICommonAspect>().ModifiedUtc.Value) %><%
|
||||
<%: T("Last modified: {0}", Html.DateTimeRelative(Model.Item.As<ICommonAspect>().ModifiedUtc.Value, T)) %><%
|
||||
} %> |
|
||||
</li>
|
||||
<li><%: T("By {0}", Model.Item.Creator.UserName)%></li>
|
||||
|
@@ -2,5 +2,5 @@
|
||||
<%@ Import Namespace="Orchard.Blogs.Extensions"%>
|
||||
<%@ Import Namespace="Orchard.Blogs.Models"%><%
|
||||
if (Model.Creator != null) {
|
||||
%><span class="posted"><%: T("Posted by {0} {1}", Model.Creator.UserName, Html.PublishedWhen(Model)) %> | </span><%
|
||||
%><span class="posted"><%: T("Posted by {0} {1}", Model.Creator.UserName, Html.PublishedWhen(Model, T)) %> | </span><%
|
||||
} %>
|
@@ -3,6 +3,6 @@
|
||||
<%@ Import Namespace="Orchard.Blogs.Models"%>
|
||||
<div class="metadata"><%
|
||||
if (Model.Creator != null) {
|
||||
%><div class="posted"><%: T("Posted by {0} {1}", Model.Creator.UserName, Html.PublishedWhen(Model)) %></div><%
|
||||
%><div class="posted"><%: T("Posted by {0} {1}", Model.Creator.UserName, Html.PublishedWhen(Model, T)) %></div><%
|
||||
} %>
|
||||
</div>
|
@@ -2,6 +2,7 @@ using System.Web.Mvc;
|
||||
using System.Web.Mvc.Html;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Mvc.Html;
|
||||
using Orchard.Utility.Extensions;
|
||||
|
||||
namespace Orchard.Comments.Extensions {
|
||||
@@ -11,14 +12,14 @@ namespace Orchard.Comments.Extensions {
|
||||
|
||||
if (item.Id != 0) {
|
||||
var totalCommentCount = commentCount + pendingCount;
|
||||
|
||||
var totalCommentText = T.Plural("1 comment", "{0} comments", totalCommentCount);
|
||||
if (totalCommentCount == 0) {
|
||||
commentText += html.Encode(T("0 comments"));
|
||||
commentText += totalCommentText.ToString();
|
||||
}
|
||||
else {
|
||||
commentText +=
|
||||
html.ActionLink(
|
||||
T("{0} comment{1}", totalCommentCount, totalCommentCount == 1 ? "" : "s").ToString(),
|
||||
totalCommentText.ToString(),
|
||||
"Details",
|
||||
new {
|
||||
Area = "Orchard.Comments",
|
||||
|
@@ -1,3 +1,3 @@
|
||||
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<CommentCountViewModel>" %>
|
||||
<%@ Import Namespace="Orchard.Comments.ViewModels"%>
|
||||
<span class="commentcount"><%: T("{0} Comment{1}", Model.CommentCount, Model.CommentCount == 1 ? "" : "s")%></span>
|
||||
<span class="commentcount"><%: T.Plural("1 Comment", "{0} Comments", Model.CommentCount)%></span>
|
||||
|
@@ -5,7 +5,7 @@
|
||||
<%@ Import Namespace="Orchard.Utility.Extensions" %>
|
||||
<%-- todo: clean up this template - waaay too much going on in here :/ --%><%
|
||||
if (Model.Comments.Count > 0) { %>
|
||||
<h2 id="comments"><%: T("{0} Comment{1}", Model.Comments.Count, Model.Comments.Count == 1 ? "" : "s")%></h2>
|
||||
<h2 id="comments"><%: T.Plural("1 Comment", "{0} Comments", Model.Comments.Count)%></h2>
|
||||
<% Html.RenderPartial("ListOfComments", Model.Comments);
|
||||
}
|
||||
|
||||
|
@@ -1,7 +1,6 @@
|
||||
<%@ Page Language="C#" Inherits="Orchard.Mvc.ViewPage<PagesViewModel>" %>
|
||||
<%@ Import Namespace="Orchard.ContentManagement.Aspects"%>
|
||||
<%@ Import Namespace="Orchard.ContentManagement"%>
|
||||
<%@ Import Namespace="Orchard.Core.Common.Models"%>
|
||||
<%@ Import Namespace="Orchard.Mvc.Html"%>
|
||||
<%@ Import Namespace="Orchard.Pages.ViewModels"%><%
|
||||
Html.RegisterStyle("admin.css"); %>
|
||||
@@ -45,10 +44,10 @@ using (Html.BeginFormAntiForgeryPost()) { %>
|
||||
<li><%
|
||||
// Published or not
|
||||
if (pageEntry.Page.HasPublished) { %>
|
||||
<img class="icon" src="<%=ResolveUrl("~/Modules/Orchard.Pages/Content/Admin/images/online.gif") %>" alt="<%: T("Online") %>" title="<%: T("The page is currently online") %>" /><%: T("Published") %> | <%
|
||||
<img class="icon" src="<%=ResolveUrl("~/Modules/Orchard.Pages/Content/Admin/images/online.gif") %>" alt="<%: T("Online") %>" title="<%: T("The page is currently online") %>" /> <%: T("Published") %> | <%
|
||||
}
|
||||
else { %>
|
||||
<img class="icon" src="<%=ResolveUrl("~/Modules/Orchard.Pages/Content/Admin/images/offline.gif") %>" alt="<%: T("Offline") %>" title="<%: T("The page is currently offline") %>" /><%: T("Not Published")%> | <%
|
||||
<img class="icon" src="<%=ResolveUrl("~/Modules/Orchard.Pages/Content/Admin/images/offline.gif") %>" alt="<%: T("Offline") %>" title="<%: T("The page is currently offline") %>" /> <%: T("Not Published")%> | <%
|
||||
} %>
|
||||
</li>
|
||||
<li><%
|
||||
@@ -66,10 +65,10 @@ using (Html.BeginFormAntiForgeryPost()) { %>
|
||||
<%=Html.DateTime(pageEntry.Page.ScheduledPublishUtc.Value, "M/d/yyyy h:mm tt")%><%
|
||||
}
|
||||
else if (pageEntry.Page.IsPublished) { %>
|
||||
<%: T("Published: ") + Html.DateTimeRelative(pageEntry.Page.As<ICommonAspect>().VersionPublishedUtc.Value) %><%
|
||||
<%: T("Published: {0}", Html.DateTimeRelative(pageEntry.Page.As<ICommonAspect>().VersionPublishedUtc.Value, T)) %><%
|
||||
}
|
||||
else { %>
|
||||
<%: T("Last modified: ") + Html.DateTimeRelative(pageEntry.Page.As<ICommonAspect>().ModifiedUtc.Value) %><%
|
||||
<%: T("Last modified: {0}", Html.DateTimeRelative(pageEntry.Page.As<ICommonAspect>().ModifiedUtc.Value, T)) %><%
|
||||
} %> |
|
||||
</li>
|
||||
<li><%: T("By {0}", pageEntry.Page.Creator.UserName)%></li>
|
||||
|
@@ -4,7 +4,7 @@ Html.RegisterStyle("admin.css"); %>
|
||||
<h1><%=Html.TitleForPage(T("Search Index Management").ToString()) %></h1><%
|
||||
using (Html.BeginForm("update", "admin", FormMethod.Post, new {area = "Orchard.Search"})) { %>
|
||||
<fieldset>
|
||||
<p><%=T("The search index was last updated {0}. <button type=\"submit\" title=\"Update the search index.\" class=\"primaryAction\">Update</button>", Html.DateTimeRelative(Model.IndexUpdatedUtc))%></p>
|
||||
<p><%=T("The search index was last updated {0}. <button type=\"submit\" title=\"Update the search index.\" class=\"primaryAction\">Update</button>", Html.DateTimeRelative(Model.IndexUpdatedUtc, T))%></p>
|
||||
<%=Html.AntiForgeryTokenOrchard() %>
|
||||
</fieldset><%
|
||||
}
|
||||
|
@@ -4,5 +4,5 @@
|
||||
<%@ Import Namespace="Orchard.Blogs.Models"%>
|
||||
|
||||
<h3><%: Html.Link(Model.Item.Name, Url.Blog(Model.Item.Slug)) %></h3>
|
||||
<div class="blog meta"><a href="<%=Url.Blog(Model.Item.Slug) %>"><%: T("{0} post{1}", Model.Item.PostCount, Model.Item.PostCount == 1 ? "" : "s")%></a></div>
|
||||
<div class="blog meta"><a href="<%=Url.Blog(Model.Item.Slug) %>"><%: T.Plural("1 post", "{0} posts", Model.Item.PostCount)%></a></div>
|
||||
<div class="blogdescription"><p><%: Model.Item.Description %></p></div>
|
||||
|
@@ -1,12 +1,10 @@
|
||||
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<ContentItemViewModel<BlogPost>>" %>
|
||||
<%@ Import Namespace="Orchard.ContentManagement"%>
|
||||
<%@ Import Namespace="Orchard.Core.Common.Models"%>
|
||||
<%@ Import Namespace="Orchard.Mvc.ViewModels"%>
|
||||
<%@ Import Namespace="Orchard.Blogs.Extensions"%>
|
||||
<%@ Import Namespace="Orchard.Blogs.Models"%>
|
||||
<%Model.Zones.AddRenderPartial("zonetest", "ZoneTest", Model); %>
|
||||
<h2><%: Html.Link(Model.Item.Title, Url.BlogPost(Model.Item)) %></h2>
|
||||
<div class="meta"><%=Html.PublishedState(Model.Item) %> | <%Html.Zone("meta");%></div>
|
||||
<div class="meta"><%=Html.PublishedState(Model.Item, T) %> | <%Html.Zone("meta");%></div>
|
||||
<div class="postsummary">
|
||||
<% Html.Zone("primary"); %>
|
||||
</div>
|
@@ -1,7 +1,6 @@
|
||||
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<BlogPost>" %>
|
||||
<%@ Import Namespace="Orchard.Blogs.Extensions"%>
|
||||
<%@ Import Namespace="Orchard.Blogs.Models"%>
|
||||
<%
|
||||
if (Model.Creator != null) {
|
||||
%><%: T("Posted by {0} {1}", Model.Creator.UserName, "|", Html.PublishedWhen(Model)) %><%
|
||||
} %>
|
||||
<%@ Import Namespace="Orchard.Blogs.Models"%><%
|
||||
if (Model.Creator != null) {
|
||||
%><%: T("Posted by {0} {1}", Model.Creator.UserName, Html.PublishedWhen(Model, T)) %><%
|
||||
} %>
|
@@ -4,5 +4,5 @@
|
||||
<%@ Import Namespace="Orchard.Blogs.Models"%>
|
||||
|
||||
<h3><%: Html.Link(Model.Item.Name, Url.Blog(Model.Item.Slug)) %></h3>
|
||||
<div class="blog meta"><a href="<%=Url.Blog(Model.Item.Slug) %>"><%: T("{0} post{1}", Model.Item.PostCount, Model.Item.PostCount == 1 ? "" : "s")%></a></div>
|
||||
<div class="blog meta"><a href="<%=Url.Blog(Model.Item.Slug) %>"><%: T.Plural("1 post", "{0} posts", Model.Item.PostCount)%></a></div>
|
||||
<div class="blogdescription"><p><%: Model.Item.Description %></p></div>
|
||||
|
@@ -1,12 +1,10 @@
|
||||
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<ContentItemViewModel<BlogPost>>" %>
|
||||
<%@ Import Namespace="Orchard.ContentManagement"%>
|
||||
<%@ Import Namespace="Orchard.Core.Common.Models"%>
|
||||
<%@ Import Namespace="Orchard.Mvc.ViewModels"%>
|
||||
<%@ Import Namespace="Orchard.Blogs.Extensions"%>
|
||||
<%@ Import Namespace="Orchard.Blogs.Models"%>
|
||||
<%Model.Zones.AddRenderPartial("zonetest", "ZoneTest", Model); %>
|
||||
<h2><%: Html.Link(Model.Item.Title, Url.BlogPost(Model.Item)) %></h2>
|
||||
<div class="meta"><%=Html.PublishedState(Model.Item) %> | <%Html.Zone("meta");%></div>
|
||||
<div class="meta"><%=Html.PublishedState(Model.Item, T) %> | <%Html.Zone("meta");%></div>
|
||||
<div class="postsummary">
|
||||
<% Html.Zone("primary"); %>
|
||||
</div>
|
@@ -1,7 +1,6 @@
|
||||
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<BlogPost>" %>
|
||||
<%@ Import Namespace="Orchard.Blogs.Extensions"%>
|
||||
<%@ Import Namespace="Orchard.Blogs.Models"%>
|
||||
<%
|
||||
if (Model.Creator != null) {
|
||||
%><%: T("Posted by {0} {1}", Model.Creator.UserName, "|", Html.PublishedWhen(Model)) %><%
|
||||
} %>
|
||||
<%@ Import Namespace="Orchard.Blogs.Models"%><%
|
||||
if (Model.Creator != null) {
|
||||
%><%: T("Posted by {0} {1}", Model.Creator.UserName, Html.PublishedWhen(Model, T)) %><%
|
||||
} %>
|
@@ -2,9 +2,10 @@
|
||||
<%@ Import Namespace="Orchard.Comments"%>
|
||||
<%@ Import Namespace="Orchard.Security" %>
|
||||
<%@ Import Namespace="Orchard.Comments.Models" %>
|
||||
|
||||
<%-- todo: clean up this template - waaay too much going on in here :/ --%><%
|
||||
if (Model.Comments.Count > 0) { %>
|
||||
<h2 id="comments"><%: T("{0} Comment{1}", Model.Comments.Count, Model.Comments.Count == 1 ? "" : "s")%></h2>
|
||||
<h2 id="comments"><%: T.Plural("1 Comment", "{0} Comments", Model.Comments.Count)%></h2>
|
||||
<% Html.RenderPartial("ListOfComments", Model.Comments);
|
||||
}
|
||||
|
||||
|
@@ -3,15 +3,12 @@
|
||||
<ul class="comments"><%
|
||||
foreach (var comment in Model) { %>
|
||||
<li>
|
||||
|
||||
<div class="comment">
|
||||
<p><%: comment.Record.CommentText %></p>
|
||||
</div>
|
||||
|
||||
<div class="commentauthor">
|
||||
<div class="comment">
|
||||
<p><%: comment.Record.CommentText %></p>
|
||||
</div>
|
||||
<div class="commentauthor">
|
||||
<span class="who"><%: Html.LinkOrDefault(comment.Record.UserName, comment.Record.SiteName, new { rel = "nofollow" })%></span> <span>said <%: Html.Link(Html.DateTimeRelative(comment.Record.CommentDateUtc.GetValueOrDefault()), "#")%></span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</li><%
|
||||
} %>
|
||||
</ul>
|
||||
|
@@ -4,7 +4,7 @@
|
||||
<%@ Import Namespace="Orchard.Comments.Models" %>
|
||||
<%-- todo: clean up this template - waaay too much going on in here :/ --%><%
|
||||
if (Model.Comments.Count > 0) { %>
|
||||
<h2 id="comments"><%: T("{0} Comment{1}", Model.Comments.Count, Model.Comments.Count == 1 ? "" : "s")%></h2>
|
||||
<h2 id="comments"><%: T.Plural("1 Comment", "{0} Comments", Model.Comments.Count)%></h2>
|
||||
<% Html.RenderPartial("ListOfComments", Model.Comments);
|
||||
}
|
||||
|
||||
|
@@ -3,15 +3,12 @@
|
||||
<ul class="comments"><%
|
||||
foreach (var comment in Model) { %>
|
||||
<li>
|
||||
|
||||
<div class="comment">
|
||||
<p><%: comment.Record.CommentText %></p>
|
||||
</div>
|
||||
|
||||
<div class="commentauthor">
|
||||
<span class="who"><%: Html.LinkOrDefault(comment.Record.UserName, comment.Record.SiteName, new { rel = "nofollow" })%></span> <span>said <%: Html.Link(Html.DateTimeRelative(comment.Record.CommentDateUtc.GetValueOrDefault()), "#")%></span>
|
||||
</div>
|
||||
|
||||
<div class="comment">
|
||||
<p><%: comment.Record.CommentText %></p>
|
||||
</div>
|
||||
<div class="commentauthor">
|
||||
<span class="who"><%=Html.LinkOrDefault(Html.Encode(comment.Record.UserName), Html.Encode(comment.Record.SiteName), new { rel = "nofollow" })%></span> <span>said <%=Html.Link(Html.DateTimeRelative(comment.Record.CommentDateUtc.GetValueOrDefault(), T).Text, "#")%></span>
|
||||
</div>
|
||||
</li><%
|
||||
} %>
|
||||
</ul>
|
||||
|
@@ -3,5 +3,5 @@
|
||||
<%@ Import Namespace="Orchard.Blogs.Extensions"%>
|
||||
<%@ Import Namespace="Orchard.Blogs.Models"%>
|
||||
<h3><%: Html.Link(Model.Item.Name, Url.Blog(Model.Item.Slug)) %></h3>
|
||||
<div class="blog meta"><a href="<%=Url.Blog(Model.Item.Slug) %>"><%: T("{0} post{1}", Model.Item.PostCount, Model.Item.PostCount == 1 ? "" : "s")%></a></div>
|
||||
<div class="blog meta"><a href="<%=Url.Blog(Model.Item.Slug) %>"><%: T.Plural("1 post", "{0} posts", Model.Item.PostCount)%></a></div>
|
||||
<div class="blogdescription"><p><%: Model.Item.Description %></p></div>
|
||||
|
@@ -1,14 +1,9 @@
|
||||
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<ContentItemViewModel<BlogPost>>" %>
|
||||
<%@ Import Namespace="Orchard.ContentManagement"%>
|
||||
<%@ Import Namespace="Orchard.Core.Common.Models"%>
|
||||
<%@ Import Namespace="Orchard.Mvc.ViewModels"%>
|
||||
<%@ Import Namespace="Orchard.Blogs.Extensions"%>
|
||||
<%@ Import Namespace="Orchard.Blogs.Models"%>
|
||||
|
||||
<h3><%: Html.Link(Model.Item.Title, Url.BlogPost(Model.Item)) %></h3>
|
||||
|
||||
<div class="meta"><%=Html.PublishedState(Model.Item) %> | <%Html.Zone("meta");%></div>
|
||||
|
||||
<div class="meta"><%=Html.PublishedState(Model.Item, T) %> | <%Html.Zone("meta");%></div>
|
||||
<div class="postsummary">
|
||||
<% Html.Zone("primary"); %>
|
||||
</div>
|
||||
</div>
|
@@ -1,7 +1,6 @@
|
||||
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<BlogPost>" %>
|
||||
<%@ Import Namespace="Orchard.Blogs.Extensions"%>
|
||||
<%@ Import Namespace="Orchard.Blogs.Models"%>
|
||||
<%
|
||||
if (Model.Creator != null) {
|
||||
%><%: T("Posted by {0} {1}", Model.Creator.UserName, "|", Html.PublishedWhen(Model)) %><%
|
||||
} %>
|
||||
<%@ Import Namespace="Orchard.Blogs.Models"%><%
|
||||
if (Model.Creator != null) {
|
||||
%><%: T("Posted by {0} {1}", Model.Creator.UserName, Html.PublishedWhen(Model, T)) %><%
|
||||
} %>
|
@@ -3,15 +3,12 @@
|
||||
<ul class="comments"><%
|
||||
foreach (var comment in Model) { %>
|
||||
<li>
|
||||
|
||||
<div class="comment">
|
||||
<p><%: comment.Record.CommentText %></p>
|
||||
</div>
|
||||
|
||||
<div class="commentauthor">
|
||||
<span class="who"><%: Html.LinkOrDefault(comment.Record.UserName, comment.Record.SiteName, new { rel = "nofollow" })%></span> <span>said <%: Html.Link(Html.DateTimeRelative(comment.Record.CommentDateUtc.GetValueOrDefault()), "#")%></span>
|
||||
</div>
|
||||
|
||||
<div class="comment">
|
||||
<p><%: comment.Record.CommentText %></p>
|
||||
</div>
|
||||
<div class="commentauthor">
|
||||
<span class="who"><%=Html.LinkOrDefault(Html.Encode(comment.Record.UserName), Html.Encode(comment.Record.SiteName), new { rel = "nofollow" })%></span> <span>said <%=Html.Link(Html.DateTimeRelative(comment.Record.CommentDateUtc.GetValueOrDefault(), T).Text, "#")%></span>
|
||||
</div>
|
||||
</li><%
|
||||
} %>
|
||||
</ul>
|
||||
|
@@ -721,6 +721,9 @@ table .button {
|
||||
|
||||
|
||||
/* ---------- Generic ---------- */
|
||||
#main .breadcrumb {
|
||||
margin-top:-1.5em;
|
||||
}
|
||||
/* todo: needed? */
|
||||
.clearBoth {
|
||||
clear:both;
|
||||
|
@@ -1,8 +1,20 @@
|
||||
using Orchard.ContentManagement.MetaData.Models;
|
||||
using System.Collections.Generic;
|
||||
using Orchard.ContentManagement.MetaData.Models;
|
||||
using Orchard.ContentManagement.Utilities;
|
||||
|
||||
namespace Orchard.ContentManagement {
|
||||
public class ContentField {
|
||||
public class ContentField : ContentPart {
|
||||
public virtual ContentPart ContentPart { get; set; }
|
||||
public string Name { get; set; }
|
||||
public ContentFieldDefinition Definition { get; set; }
|
||||
public IDictionary<string, string> Settings { get; private set; }
|
||||
|
||||
public new ContentPartDefinition PartDefinition { get { return ContentPart.PartDefinition; } }
|
||||
public ContentPartDefinition.Field PartFieldDefinition { get; set; }
|
||||
public ContentFieldDefinition FieldDefinition { get { return PartFieldDefinition.FieldDefinition; } }
|
||||
}
|
||||
|
||||
public class ContentField<TRecord> : ContentField {
|
||||
public readonly LazyField<TRecord> _record = new LazyField<TRecord>();
|
||||
public TRecord Record { get { return _record.Value; } set { _record.Value = value; } }
|
||||
}
|
||||
}
|
||||
|
@@ -1,12 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Orchard.ContentManagement.MetaData.Models;
|
||||
using Orchard.ContentManagement.Utilities;
|
||||
|
||||
namespace Orchard.ContentManagement {
|
||||
public abstract class ContentPart : IContent {
|
||||
private readonly IList<ContentField> _fields;
|
||||
|
||||
public ContentPart() {
|
||||
_fields = new List<ContentField>();
|
||||
}
|
||||
|
||||
public virtual ContentItem ContentItem { get; set; }
|
||||
public ContentTypeDefinition TypeDefinition { get { return ContentItem.TypeDefinition; } }
|
||||
public ContentTypeDefinition.Part TypePartDefinition { get; set; }
|
||||
public ContentPartDefinition PartDefinition { get { return TypePartDefinition.PartDefinition; } }
|
||||
|
||||
public IEnumerable<ContentField> Fields { get { return _fields; } }
|
||||
|
||||
|
||||
public bool Has(Type fieldType) {
|
||||
return fieldType == typeof(ContentItem) || _fields.Any(field => fieldType.IsAssignableFrom(field.GetType()));
|
||||
}
|
||||
|
||||
public IContent Get(Type fieldType) {
|
||||
if (fieldType == typeof(ContentItem))
|
||||
return this;
|
||||
return _fields.FirstOrDefault(field => fieldType.IsAssignableFrom(field.GetType()));
|
||||
}
|
||||
|
||||
public void Weld(ContentField field) {
|
||||
field.ContentPart = this;
|
||||
_fields.Add(field);
|
||||
}
|
||||
}
|
||||
|
||||
public class ContentPart<TRecord> : ContentPart {
|
||||
|
51
src/Orchard/ContentManagement/Drivers/ContentFieldDriver.cs
Normal file
51
src/Orchard/ContentManagement/Drivers/ContentFieldDriver.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
|
||||
namespace Orchard.ContentManagement.Drivers {
|
||||
|
||||
public interface IContentFieldDriver : IEvents {
|
||||
DriverResult BuildDisplayModel(BuildDisplayModelContext context);
|
||||
DriverResult BuildEditorModel(BuildEditorModelContext context);
|
||||
DriverResult UpdateEditorModel(UpdateEditorModelContext context);
|
||||
}
|
||||
|
||||
public abstract class ContentFieldDriver<TContent> : IContentFieldDriver where TContent : ContentField, new() {
|
||||
protected virtual string Prefix { get { return ""; } }
|
||||
protected virtual string Zone { get { return "body"; } }
|
||||
|
||||
DriverResult IContentFieldDriver.BuildDisplayModel(BuildDisplayModelContext context) {
|
||||
var field = context.ContentItem.As<TContent>();
|
||||
return field == null ? null : Display(field, context.DisplayType);
|
||||
}
|
||||
|
||||
DriverResult IContentFieldDriver.BuildEditorModel(BuildEditorModelContext context) {
|
||||
var field = context.ContentItem.As<TContent>();
|
||||
return field == null ? null : Editor(field);
|
||||
}
|
||||
|
||||
DriverResult IContentFieldDriver.UpdateEditorModel(UpdateEditorModelContext context) {
|
||||
var field = context.ContentItem.As<TContent>();
|
||||
return field == null ? null : Editor(field, context.Updater);
|
||||
}
|
||||
|
||||
protected virtual DriverResult Display(TContent field, string displayType) { return null; }
|
||||
protected virtual DriverResult Editor(TContent field) { return null; }
|
||||
protected virtual DriverResult Editor(TContent field, IUpdateModel updater) { return null; }
|
||||
|
||||
|
||||
public ContentFieldTemplateResult ContentPartTemplate(object model) {
|
||||
return new ContentFieldTemplateResult(model, null, Prefix).Location(Zone);
|
||||
}
|
||||
|
||||
public ContentFieldTemplateResult ContentPartTemplate(object model, string template) {
|
||||
return new ContentFieldTemplateResult(model, template, Prefix).Location(Zone);
|
||||
}
|
||||
|
||||
public ContentFieldTemplateResult ContentPartTemplate(object model, string template, string prefix) {
|
||||
return new ContentFieldTemplateResult(model, template, prefix).Location(Zone);
|
||||
}
|
||||
|
||||
public CombinedResult Combined(params DriverResult[] results) {
|
||||
return new CombinedResult(results);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
using Orchard.Logging;
|
||||
|
||||
namespace Orchard.ContentManagement.Drivers {
|
||||
[UsedImplicitly]
|
||||
public class ContentFieldDriverHandler : ContentHandlerBase {
|
||||
private readonly IEnumerable<IContentPartDriver> _drivers;
|
||||
|
||||
public ContentFieldDriverHandler(IEnumerable<IContentPartDriver> drivers) {
|
||||
_drivers = drivers;
|
||||
Logger = NullLogger.Instance;
|
||||
}
|
||||
|
||||
public ILogger Logger { get; set; }
|
||||
|
||||
public override void BuildDisplayModel(BuildDisplayModelContext context) {
|
||||
_drivers.Invoke(driver => {
|
||||
var result = driver.BuildDisplayModel(context);
|
||||
if (result != null)
|
||||
result.Apply(context);
|
||||
}, Logger);
|
||||
}
|
||||
|
||||
public override void BuildEditorModel(BuildEditorModelContext context) {
|
||||
_drivers.Invoke(driver => {
|
||||
var result = driver.BuildEditorModel(context);
|
||||
if (result != null)
|
||||
result.Apply(context);
|
||||
}, Logger);
|
||||
}
|
||||
|
||||
public override void UpdateEditorModel(UpdateEditorModelContext context) {
|
||||
_drivers.Invoke(driver => {
|
||||
var result = driver.UpdateEditorModel(context);
|
||||
if (result != null)
|
||||
result.Apply(context);
|
||||
}, Logger);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,56 @@
|
||||
using System.Linq;
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
|
||||
namespace Orchard.ContentManagement.Drivers {
|
||||
public class ContentFieldTemplateResult : DriverResult {
|
||||
public object Model { get; set; }
|
||||
public string TemplateName { get; set; }
|
||||
public string Prefix { get; set; }
|
||||
public string Zone { get; set; }
|
||||
public string Position { get; set; }
|
||||
|
||||
public ContentFieldTemplateResult(object model, string templateName, string prefix) {
|
||||
Model = model;
|
||||
TemplateName = templateName;
|
||||
Prefix = prefix;
|
||||
}
|
||||
|
||||
public override void Apply(BuildDisplayModelContext context) {
|
||||
context.ViewModel.Zones.AddDisplayPart(
|
||||
Zone + ":" + Position, Model, TemplateName, Prefix);
|
||||
}
|
||||
|
||||
public override void Apply(BuildEditorModelContext context) {
|
||||
context.ViewModel.Zones.AddEditorPart(
|
||||
Zone + ":" + Position, Model, TemplateName, Prefix);
|
||||
}
|
||||
|
||||
public ContentFieldTemplateResult Location(string zone) {
|
||||
Zone = zone;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ContentFieldTemplateResult Location(string zone, string position) {
|
||||
Zone = zone;
|
||||
Position = position;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ContentFieldTemplateResult LongestMatch(string displayType, params string[] knownDisplayTypes) {
|
||||
|
||||
if (string.IsNullOrEmpty(displayType))
|
||||
return this;
|
||||
|
||||
var longest = knownDisplayTypes.Aggregate("", (best, x) => {
|
||||
if (displayType.StartsWith(x) && x.Length > best.Length) return x;
|
||||
return best;
|
||||
});
|
||||
|
||||
if (string.IsNullOrEmpty(longest))
|
||||
return this;
|
||||
|
||||
TemplateName += "." + longest;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,5 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
using Orchard.Logging;
|
||||
|
@@ -25,6 +25,14 @@ namespace Orchard.ContentManagement.Handlers {
|
||||
Filters.Add(new InlineStorageFilter<TPart> { OnCreated = handler });
|
||||
}
|
||||
|
||||
protected void OnSaving<TPart>(Action<SaveContentContext, TPart> handler) where TPart : class, IContent {
|
||||
Filters.Add(new InlineStorageFilter<TPart> { OnSaving = handler });
|
||||
}
|
||||
|
||||
protected void OnSaved<TPart>(Action<SaveContentContext, TPart> handler) where TPart : class, IContent {
|
||||
Filters.Add(new InlineStorageFilter<TPart> { OnSaved = handler });
|
||||
}
|
||||
|
||||
protected void OnLoading<TPart>(Action<LoadContentContext, TPart> handler) where TPart : class, IContent {
|
||||
Filters.Add(new InlineStorageFilter<TPart> { OnLoading = handler });
|
||||
}
|
||||
@@ -84,6 +92,8 @@ namespace Orchard.ContentManagement.Handlers {
|
||||
public Action<ActivatedContentContext, TPart> OnActivated { get; set; }
|
||||
public Action<CreateContentContext, TPart> OnCreating { get; set; }
|
||||
public Action<CreateContentContext, TPart> OnCreated { get; set; }
|
||||
public Action<SaveContentContext, TPart> OnSaving { get; set; }
|
||||
public Action<SaveContentContext, TPart> OnSaved { get; set; }
|
||||
public Action<LoadContentContext, TPart> OnLoading { get; set; }
|
||||
public Action<LoadContentContext, TPart> OnLoaded { get; set; }
|
||||
public Action<VersionContentContext, TPart, TPart> OnVersioning { get; set; }
|
||||
@@ -103,6 +113,12 @@ namespace Orchard.ContentManagement.Handlers {
|
||||
protected override void Created(CreateContentContext context, TPart instance) {
|
||||
if (OnCreated != null) OnCreated(context, instance);
|
||||
}
|
||||
protected override void Saving(SaveContentContext context, TPart instance) {
|
||||
if (OnSaving != null) OnSaving(context, instance);
|
||||
}
|
||||
protected override void Saved(SaveContentContext context, TPart instance) {
|
||||
if (OnSaved != null) OnSaved(context, instance);
|
||||
}
|
||||
protected override void Loading(LoadContentContext context, TPart instance) {
|
||||
if (OnLoading != null) OnLoading(context, instance);
|
||||
}
|
||||
@@ -185,6 +201,18 @@ namespace Orchard.ContentManagement.Handlers {
|
||||
Created(context);
|
||||
}
|
||||
|
||||
void IContentHandler.Saving(SaveContentContext context) {
|
||||
foreach (var filter in Filters.OfType<IContentStorageFilter>())
|
||||
filter.Saving(context);
|
||||
Saving(context);
|
||||
}
|
||||
|
||||
void IContentHandler.Saved(SaveContentContext context) {
|
||||
foreach (var filter in Filters.OfType<IContentStorageFilter>())
|
||||
filter.Saved(context);
|
||||
Saved(context);
|
||||
}
|
||||
|
||||
void IContentHandler.Loading(LoadContentContext context) {
|
||||
foreach (var filter in Filters.OfType<IContentStorageFilter>())
|
||||
filter.Loading(context);
|
||||
@@ -272,6 +300,9 @@ namespace Orchard.ContentManagement.Handlers {
|
||||
protected virtual void Creating(CreateContentContext context) { }
|
||||
protected virtual void Created(CreateContentContext context) { }
|
||||
|
||||
protected virtual void Saving(SaveContentContext context) { }
|
||||
protected virtual void Saved(SaveContentContext context) { }
|
||||
|
||||
protected virtual void Loading(LoadContentContext context) { }
|
||||
protected virtual void Loaded(LoadContentContext context) { }
|
||||
|
||||
|
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Orchard.ContentManagement.Handlers {
|
||||
@@ -20,6 +19,12 @@ namespace Orchard.ContentManagement.Handlers {
|
||||
public virtual void Created(CreateContentContext context) {
|
||||
}
|
||||
|
||||
public virtual void Saving(SaveContentContext context) {
|
||||
}
|
||||
|
||||
public virtual void Saved(SaveContentContext context) {
|
||||
}
|
||||
|
||||
public virtual void Loading(LoadContentContext context) {
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Events;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Orchard.ContentManagement.Handlers {
|
||||
public interface IContentHandler : IEvents {
|
||||
@@ -10,6 +8,8 @@ namespace Orchard.ContentManagement.Handlers {
|
||||
void Activated(ActivatedContentContext context);
|
||||
void Creating(CreateContentContext context);
|
||||
void Created(CreateContentContext context);
|
||||
void Saving(SaveContentContext context);
|
||||
void Saved(SaveContentContext context);
|
||||
void Loading(LoadContentContext context);
|
||||
void Loaded(LoadContentContext context);
|
||||
void Versioning(VersionContentContext context);
|
||||
|
@@ -3,6 +3,8 @@ namespace Orchard.ContentManagement.Handlers {
|
||||
void Activated(ActivatedContentContext context);
|
||||
void Creating(CreateContentContext context);
|
||||
void Created(CreateContentContext context);
|
||||
void Saving(SaveContentContext context);
|
||||
void Saved(SaveContentContext context);
|
||||
void Loading(LoadContentContext context);
|
||||
void Loaded(LoadContentContext context);
|
||||
void Versioning(VersionContentContext context);
|
||||
|
12
src/Orchard/ContentManagement/Handlers/SaveContentContext.cs
Normal file
12
src/Orchard/ContentManagement/Handlers/SaveContentContext.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Orchard.ContentManagement.Records;
|
||||
|
||||
namespace Orchard.ContentManagement.Handlers {
|
||||
public class SaveContentContext : ContentContextBase {
|
||||
public SaveContentContext(ContentItem contentItem)
|
||||
: base(contentItem) {
|
||||
ContentItemVersionRecord = contentItem.VersionRecord;
|
||||
}
|
||||
|
||||
public ContentItemVersionRecord ContentItemVersionRecord { get; set; }
|
||||
}
|
||||
}
|
@@ -4,6 +4,8 @@ namespace Orchard.ContentManagement.Handlers {
|
||||
protected virtual void Activated(ActivatedContentContext context, TPart instance) { }
|
||||
protected virtual void Creating(CreateContentContext context, TPart instance) { }
|
||||
protected virtual void Created(CreateContentContext context, TPart instance) { }
|
||||
protected virtual void Saving(SaveContentContext context, TPart instance) { }
|
||||
protected virtual void Saved(SaveContentContext context, TPart instance) { }
|
||||
protected virtual void Loading(LoadContentContext context, TPart instance) { }
|
||||
protected virtual void Loaded(LoadContentContext context, TPart instance) { }
|
||||
protected virtual void Versioning(VersionContentContext context, TPart existing, TPart building) { }
|
||||
@@ -31,6 +33,16 @@ namespace Orchard.ContentManagement.Handlers {
|
||||
Created(context, context.ContentItem.As<TPart>());
|
||||
}
|
||||
|
||||
void IContentStorageFilter.Saving(SaveContentContext context) {
|
||||
if (context.ContentItem.Is<TPart>())
|
||||
Saving(context, context.ContentItem.As<TPart>());
|
||||
}
|
||||
|
||||
void IContentStorageFilter.Saved(SaveContentContext context) {
|
||||
if (context.ContentItem.Is<TPart>())
|
||||
Saved(context, context.ContentItem.As<TPart>());
|
||||
}
|
||||
|
||||
void IContentStorageFilter.Loading(LoadContentContext context) {
|
||||
if (context.ContentItem.Is<TPart>())
|
||||
Loading(context, context.ContentItem.As<TPart>());
|
||||
|
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Orchard.Utility.Extensions;
|
||||
|
||||
|
@@ -1,7 +1,8 @@
|
||||
using System;
|
||||
using System.Web;
|
||||
|
||||
namespace Orchard.Localization {
|
||||
public class LocalizedString : MarshalByRefObject {
|
||||
public class LocalizedString : MarshalByRefObject, IHtmlString {
|
||||
private readonly string _localized;
|
||||
|
||||
public LocalizedString(string localized) {
|
||||
@@ -24,6 +25,10 @@ namespace Orchard.Localization {
|
||||
return _localized;
|
||||
}
|
||||
|
||||
string IHtmlString.ToHtmlString() {
|
||||
return _localized;
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
var hashCode = 0;
|
||||
if (_localized != null)
|
||||
@@ -35,8 +40,9 @@ namespace Orchard.Localization {
|
||||
if (obj == null || obj.GetType() != GetType())
|
||||
return false;
|
||||
|
||||
var that = (LocalizedString) obj;
|
||||
var that = (LocalizedString)obj;
|
||||
return string.Equals(_localized, that._localized);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -1,3 +1,15 @@
|
||||
using System.Linq;
|
||||
using Orchard.Localization;
|
||||
|
||||
namespace Orchard.Localization {
|
||||
public delegate LocalizedString Localizer(string text, params object[] args);
|
||||
|
||||
}
|
||||
|
||||
namespace Orchard.Mvc.Html {
|
||||
public static class LocalizerExtensions {
|
||||
public static LocalizedString Plural(this Localizer T, string textSingular, string textPlural, int count, params object[] args) {
|
||||
return T(count == 1 ? textSingular : textPlural, new object[] { count }.Concat(args).ToArray());
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,3 +1,6 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using Orchard.Localization.Services;
|
||||
using Orchard.Logging;
|
||||
@@ -23,9 +26,25 @@ namespace Orchard.Localization {
|
||||
string currentCulture = _cultureManager.GetCurrentCulture(HttpContext.Current);
|
||||
var localizedFormat = _resourceManager.GetLocalizedString(_scope, textHint, currentCulture);
|
||||
|
||||
return args.Length < 1
|
||||
? new LocalizedString(localizedFormat)
|
||||
: new LocalizedString(string.Format(localizedFormat, args));
|
||||
return args.Length == 0
|
||||
? new LocalizedString(localizedFormat).ToString()
|
||||
: string.Format(GetFormatProvider(currentCulture), localizedFormat, args.Select(Encode).ToArray());
|
||||
}
|
||||
|
||||
private static IFormatProvider GetFormatProvider(string currentCulture) {
|
||||
try {
|
||||
return CultureInfo.GetCultureInfoByIetfLanguageTag(currentCulture);
|
||||
}
|
||||
catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static object Encode(object arg) {
|
||||
if (arg is IFormattable || arg is IHtmlString) {
|
||||
return arg;
|
||||
}
|
||||
return HttpUtility.HtmlEncode(arg);
|
||||
}
|
||||
}
|
||||
}
|
@@ -7,6 +7,7 @@ using System.Web.Mvc;
|
||||
using System.Web.Mvc.Html;
|
||||
using System.Web.Routing;
|
||||
using Orchard.Collections;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Mvc.ViewModels;
|
||||
using Orchard.Services;
|
||||
using Orchard.Settings;
|
||||
@@ -24,6 +25,14 @@ namespace Orchard.Mvc.Html {
|
||||
return Reflect.NameOf(html.ViewData.Model, expression);
|
||||
}
|
||||
|
||||
public static string FieldNameFor<T, TResult>(this HtmlHelper<T> html, Expression<Func<T, TResult>> expression) {
|
||||
return html.ViewData.TemplateInfo.GetFullHtmlFieldName(ExpressionHelper.GetExpressionText(expression));
|
||||
}
|
||||
public static string FieldIdFor<T, TResult>(this HtmlHelper<T> html, Expression<Func<T, TResult>> expression) {
|
||||
return html.ViewData.TemplateInfo.GetFullHtmlFieldId(ExpressionHelper.GetExpressionText(expression));
|
||||
}
|
||||
|
||||
|
||||
public static MvcHtmlString SelectOption<T>(this HtmlHelper html, T currentValue, T optionValue, string text) {
|
||||
return SelectOption(html, optionValue, object.Equals(optionValue, currentValue), text);
|
||||
}
|
||||
@@ -171,44 +180,44 @@ namespace Orchard.Mvc.Html {
|
||||
|
||||
#region Format Date/Time
|
||||
|
||||
public static string DateTimeRelative(this HtmlHelper htmlHelper, DateTime? value, string defaultIfNull) {
|
||||
return value.HasValue ? htmlHelper.DateTimeRelative(value.Value) : defaultIfNull;
|
||||
public static LocalizedString DateTimeRelative(this HtmlHelper htmlHelper, DateTime? value, LocalizedString defaultIfNull, Localizer T) {
|
||||
return value.HasValue ? htmlHelper.DateTimeRelative(value.Value, T) : defaultIfNull;
|
||||
}
|
||||
|
||||
//TODO: (erikpo) This method needs localized
|
||||
public static string DateTimeRelative(this HtmlHelper htmlHelper, DateTime value) {
|
||||
TimeSpan time = htmlHelper.Resolve<IClock>().UtcNow - value;
|
||||
public static LocalizedString DateTimeRelative(this HtmlHelper htmlHelper, DateTime value, Localizer T) {
|
||||
var time = htmlHelper.Resolve<IClock>().UtcNow - value;
|
||||
|
||||
if (time.TotalDays > 7)
|
||||
return "on " + htmlHelper.DateTime(value, "MMM d yyyy 'at' h:mm tt");
|
||||
return htmlHelper.DateTime(value, T("'on' MMM d yyyy 'at' h:mm tt"));
|
||||
if (time.TotalHours > 24)
|
||||
return string.Format("{0} day{1} ago", time.Days, time.Days == 1 ? "" : "s");
|
||||
return T.Plural("1 day ago", "{0} days ago", time.Days);
|
||||
if (time.TotalMinutes > 60)
|
||||
return string.Format("{0} hour{1} ago", time.Hours, time.Hours == 1 ? "" : "s");
|
||||
return T.Plural("1 hour ago", "{0} hours ago", time.Hours);
|
||||
if (time.TotalSeconds > 60)
|
||||
return string.Format("{0} minute{1} ago", time.Minutes, time.Minutes == 1 ? "" : "s");
|
||||
return T.Plural("1 minute ago", "{0} minutes ago", time.Minutes);
|
||||
if (time.TotalSeconds > 10)
|
||||
return string.Format("{0} second{1} ago", time.Seconds, time.Seconds == 1 ? "" : "s");
|
||||
return T.Plural("1 second ago", "{0} seconds ago", time.Seconds); //aware that the singular won't be used
|
||||
|
||||
return "a moment ago";
|
||||
return T("a moment ago");
|
||||
}
|
||||
|
||||
public static string DateTime(this HtmlHelper htmlHelper, DateTime? value, string defaultIfNull) {
|
||||
public static LocalizedString DateTime(this HtmlHelper htmlHelper, DateTime? value, LocalizedString defaultIfNull) {
|
||||
return value.HasValue ? htmlHelper.DateTime(value.Value) : defaultIfNull;
|
||||
}
|
||||
|
||||
public static string DateTime(this HtmlHelper htmlHelper, DateTime? value, string defaultIfNull, string customFormat) {
|
||||
public static LocalizedString DateTime(this HtmlHelper htmlHelper, DateTime? value, LocalizedString defaultIfNull, LocalizedString customFormat) {
|
||||
return value.HasValue ? htmlHelper.DateTime(value.Value, customFormat) : defaultIfNull;
|
||||
}
|
||||
|
||||
public static string DateTime(this HtmlHelper htmlHelper, DateTime value) {
|
||||
public static LocalizedString DateTime(this HtmlHelper htmlHelper, DateTime value) {
|
||||
//TODO: (erikpo) This default format should come from a site setting
|
||||
return htmlHelper.DateTime(value, "MMM d yyyy h:mm tt");
|
||||
return htmlHelper.DateTime(value, new LocalizedString("MMM d yyyy h:mm tt")); //todo: above comment and get rid of just wrapping this as a localized string
|
||||
}
|
||||
|
||||
public static string DateTime(this HtmlHelper htmlHelper, DateTime value, string customFormat) {
|
||||
public static LocalizedString DateTime(this HtmlHelper htmlHelper, DateTime value, LocalizedString customFormat) {
|
||||
//TODO: (erikpo) In the future, convert this to "local" time before calling ToString
|
||||
return value.ToString(customFormat);
|
||||
return value.ToString(customFormat.Text);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@@ -170,6 +170,9 @@
|
||||
<Compile Include="ContentManagement\Drivers\CombinedResult.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="ContentManagement\Drivers\ContentFieldDriver.cs" />
|
||||
<Compile Include="ContentManagement\Drivers\ContentFieldDriverHandler.cs" />
|
||||
<Compile Include="ContentManagement\Drivers\ContentFieldTemplateResult.cs" />
|
||||
<Compile Include="ContentManagement\Drivers\ContentItemDriver.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
@@ -255,6 +258,7 @@
|
||||
<Compile Include="ContentManagement\Handlers\RemoveContentContext.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="ContentManagement\Handlers\SaveContentContext.cs" />
|
||||
<Compile Include="ContentManagement\Handlers\StorageFilter.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
|
Reference in New Issue
Block a user