Merge from dev

--HG--
branch : dev
This commit is contained in:
Phil Haack
2010-06-10 22:20:45 -07:00
60 changed files with 907 additions and 209 deletions

View 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");
}
}
}

View File

@@ -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" });
//}
}
}

View File

@@ -0,0 +1,8 @@
using System.ComponentModel.DataAnnotations;
namespace Orchard.Core.Common.ViewModels {
public class ContainerEditorViewModel {
public int? ContainerId { get; set; }
}
}

View File

@@ -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>

View File

@@ -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" />

View File

@@ -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());
}
}
}

View File

@@ -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);
}
}
}

View 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"
);
}
});

View 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);
}
}

View 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;
}
}
}

View File

@@ -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;}
}
}

View File

@@ -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; }
}
}

View File

@@ -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>
<% } %>

View File

@@ -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(" &#62; ") %><%: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>

View File

@@ -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>
<% } %>

View File

@@ -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);
}
}
}

View File

@@ -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><%
} %>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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")%><%
} %>&nbsp;&#124;&nbsp;
</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)) %><%
} %>&nbsp;&#124;&nbsp;
</li>
<li><%: T("By {0}", Model.Item.Creator.UserName)%></li>

View File

@@ -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><%
} %>

View File

@@ -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>

View File

@@ -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",

View File

@@ -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>

View File

@@ -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);
}

View File

@@ -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") %>&nbsp;&#124;&nbsp;<%
<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") %>&nbsp;&#124;&nbsp;<%
}
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")%>&nbsp;&#124;&nbsp;<%
<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")%>&nbsp;&#124;&nbsp;<%
} %>
</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)) %><%
} %>&nbsp;&#124;&nbsp;
</li>
<li><%: T("By {0}", pageEntry.Page.Creator.UserName)%></li>

View File

@@ -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><%
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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)) %><%
} %>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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)) %><%
} %>

View File

@@ -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);
}

View File

@@ -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>&nbsp;<span>said <%: Html.Link(Html.DateTimeRelative(comment.Record.CommentDateUtc.GetValueOrDefault()), "#")%></span>
</div>
</div>
</li><%
} %>
</ul>

View File

@@ -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);
}

View File

@@ -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>&nbsp;<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>&nbsp;<span>said <%=Html.Link(Html.DateTimeRelative(comment.Record.CommentDateUtc.GetValueOrDefault(), T).Text, "#")%></span>
</div>
</li><%
} %>
</ul>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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)) %><%
} %>

View File

@@ -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>&nbsp;<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>&nbsp;<span>said <%=Html.Link(Html.DateTimeRelative(comment.Record.CommentDateUtc.GetValueOrDefault(), T).Text, "#")%></span>
</div>
</li><%
} %>
</ul>

View File

@@ -721,6 +721,9 @@ table .button {
/* ---------- Generic ---------- */
#main .breadcrumb {
margin-top:-1.5em;
}
/* todo: needed? */
.clearBoth {
clear:both;

View File

@@ -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; } }
}
}

View File

@@ -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 {

View 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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -1,5 +1,4 @@
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using Orchard.ContentManagement.Handlers;
using Orchard.Logging;

View File

@@ -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) { }

View File

@@ -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) {
}

View File

@@ -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);

View File

@@ -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);

View 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; }
}
}

View File

@@ -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>());

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using Orchard.Utility.Extensions;

View File

@@ -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);
}
}
}

View File

@@ -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());
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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

View File

@@ -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>