#18090: Fixing container's title issues by adding a new TitlePart

Work Items: 18090

--HG--
branch : 1.x
This commit is contained in:
Sebastien Ros 2011-09-20 16:11:54 -07:00
parent 025de7382c
commit 9a7d660510
24 changed files with 261 additions and 10 deletions

View File

@ -63,5 +63,6 @@ namespace Orchard.Core.Containers.Drivers {
return shapeHelper.EditorTemplate(TemplateName: "Containable", Model: model, Prefix: "Containable");
});
}
}
}

View File

@ -67,7 +67,7 @@ namespace Orchard.Core.Containers.Drivers {
? new[] {new SelectListItem {Text = T("(None - create container enabled items first)").Text, Value = "0"}}
: containers.Select(x => new SelectListItem {
Value = Convert.ToString(x.Id),
Text = x.ContentItem.TypeDefinition.DisplayName + ": " + x.As<IRoutableAspect>().Title,
Text = x.ContentItem.TypeDefinition.DisplayName + ": " + _contentManager.GetItemMetadata(x.ContentItem).DisplayText,
Selected = x.Id == model.Part.Record.ContainerId,
});

View File

@ -4,6 +4,7 @@ using Orchard.ContentManagement;
using Orchard.Core.Common.Models;
using Orchard.Core.Containers.Models;
using Orchard.Core.Routable.Models;
using Orchard.Core.Title.Models;
namespace Orchard.Core.Containers.Extensions
{
@ -11,6 +12,11 @@ namespace Orchard.Core.Containers.Extensions
public static IContentQuery<T> OrderBy<T>(this IContentQuery<T> query, string partAndProperty, bool descendingOrder) where T : IContent {
//todo: (heskew) order by custom part properties
switch (partAndProperty) {
case "TitlePart.Title":
query = descendingOrder
? query.OrderByDescending<TitlePartRecord, string>(record => record.Title)
: query.OrderBy<TitlePartRecord, string>(record => record.Title);
break;
case "RoutePart.Title":
query = descendingOrder
? query.OrderByDescending<RoutePartRecord, string>(record => record.Title)
@ -61,6 +67,10 @@ namespace Orchard.Core.Containers.Extensions
// convoluted: yes; quick and works for now: yes; technical debt: not much
private static readonly Dictionary<string, Func<IContentQuery<ContentItem>, string, IContentQuery<ContentItem>>> _filters = new Dictionary<string, Func<IContentQuery<ContentItem>, string, IContentQuery<ContentItem>>> {
{"TitlePart.Title|<", new Func<IContentQuery<ContentItem>, string, IContentQuery<ContentItem>>((q, s) => q.Where<TitlePartRecord>(r => true /* CompareTo is not implemented - r.Title.CompareTo(s) == -1*/))},
{"TitlePart.Title|>", new Func<IContentQuery<ContentItem>, string, IContentQuery<ContentItem>>((q, s) => q.Where<TitlePartRecord>(r => true /* CompareTo is not implemented - r.Title.CompareTo(s) == 1*/))},
{"TitlePart.Title|=", new Func<IContentQuery<ContentItem>, string, IContentQuery<ContentItem>>((q, s) => q.Where<TitlePartRecord>(r => r.Title.Equals(s, StringComparison.OrdinalIgnoreCase)))},
{"TitlePart.Title|^=", new Func<IContentQuery<ContentItem>, string, IContentQuery<ContentItem>>((q, s) => q.Where<TitlePartRecord>(r => r.Title.StartsWith(s, StringComparison.OrdinalIgnoreCase)))},
{"RoutePart.Title|<", new Func<IContentQuery<ContentItem>, string, IContentQuery<ContentItem>>((q, s) => q.Where<RoutePartRecord>(r => true /* CompareTo is not implemented - r.Title.CompareTo(s) == -1*/))},
{"RoutePart.Title|>", new Func<IContentQuery<ContentItem>, string, IContentQuery<ContentItem>>((q, s) => q.Where<RoutePartRecord>(r => true /* CompareTo is not implemented - r.Title.CompareTo(s) == 1*/))},
{"RoutePart.Title|=", new Func<IContentQuery<ContentItem>, string, IContentQuery<ContentItem>>((q, s) => q.Where<RoutePartRecord>(r => r.Title.Equals(s, StringComparison.OrdinalIgnoreCase)))},

View File

@ -10,7 +10,8 @@
<select id="@Html.FieldIdFor(m => m.Part.Record.OrderByProperty)" name="@Html.FieldNameFor(m => m.Part.Record.OrderByProperty)">
@Html.SelectOption(Model.Part.Record.OrderByProperty, "CommonPart.CreatedUtc", T("Date Created").Text)
@Html.SelectOption(Model.Part.Record.OrderByProperty, "CommonPart.PublishedUtc", T("Date Published").Text)
@Html.SelectOption(Model.Part.Record.OrderByProperty, "RoutePart.Title", T("Title").Text)
@Html.SelectOption(Model.Part.Record.OrderByProperty, "TitlePart.Title", T("Title").Text)
@Html.SelectOption(Model.Part.Record.OrderByProperty, "RoutePart.Title", T("Routable Title").Text)
@Html.SelectOption(Model.Part.Record.OrderByProperty, "RoutePart.Slug", T("Slug").Text)
@Html.SelectOption(Model.Part.Record.OrderByProperty, "CustomPropertiesPart.CustomOne", T("Custom 1").Text)
@Html.SelectOption(Model.Part.Record.OrderByProperty, "CustomPropertiesPart.CustomTwo", T("Custom 2").Text)

View File

@ -18,7 +18,8 @@
<select id="@Html.FieldIdFor(m => m.Part.Record.OrderByProperty)" name="@Html.FieldNameFor(m => m.Part.Record.OrderByProperty)">
@Html.SelectOption(Model.Part.Record.OrderByProperty, "CommonPart.CreatedUtc", T("Date Created").Text)
@Html.SelectOption(Model.Part.Record.OrderByProperty, "CommonPart.PublishedUtc", T("Date Published").Text)
@Html.SelectOption(Model.Part.Record.OrderByProperty, "RoutePart.Title", T("Title").Text)
@Html.SelectOption(Model.Part.Record.OrderByProperty, "TitlePart.Title", T("Title").Text)
@Html.SelectOption(Model.Part.Record.OrderByProperty, "RoutePart.Title", T("Routable Title").Text)
@Html.SelectOption(Model.Part.Record.OrderByProperty, "RoutePart.Slug", T("Slug").Text)
@Html.SelectOption(Model.Part.Record.OrderByProperty, "CustomPropertiesPart.CustomOne", T("Custom 1").Text)
@Html.SelectOption(Model.Part.Record.OrderByProperty, "CustomPropertiesPart.CustomTwo", T("Custom 2").Text)
@ -39,7 +40,8 @@
<select id="@Html.FieldIdFor(m => m.Part.Record.FilterByProperty)" name="@Html.FieldNameFor(m => m.Part.Record.FilterByProperty)">
@Html.SelectOption(Model.Part.Record.FilterByProperty, "CommonPart.CreatedUtx", T("Date Created").Text)
@Html.SelectOption(Model.Part.Record.FilterByProperty, "CommonPart.PublishedUtc", T("Date Published").Text)
@Html.SelectOption(Model.Part.Record.FilterByProperty, "RoutePart.Title", T("Title").Text)
@Html.SelectOption(Model.Part.Record.FilterByProperty, "TitlePart.Title", T("Title").Text)
@Html.SelectOption(Model.Part.Record.FilterByProperty, "RoutePart.Title", T("Routable Title").Text)
@Html.SelectOption(Model.Part.Record.FilterByProperty, "RoutePart.Slug", T("Slug").Text)
@Html.SelectOption(Model.Part.Record.FilterByProperty, "CustomPropertiesPart.CustomOne", T("Custom 1").Text)
@Html.SelectOption(Model.Part.Record.FilterByProperty, "CustomPropertiesPart.CustomTwo", T("Custom 2").Text)

View File

@ -242,6 +242,11 @@
<Compile Include="Shapes\ResourceManifest.cs" />
<Compile Include="Shapes\CoreShapes.cs" />
<Compile Include="Shapes\DateTimeShapes.cs" />
<Compile Include="Title\Drivers\TitlePartDriver.cs" />
<Compile Include="Title\Handlers\TitlePartHandler.cs" />
<Compile Include="Title\Migrations.cs" />
<Compile Include="Title\Models\TitlePart.cs" />
<Compile Include="Title\Models\TitlePartRecord.cs" />
<Compile Include="XmlRpc\Controllers\HomeController.cs" />
<Compile Include="XmlRpc\Controllers\LiveWriterController.cs" />
<Compile Include="XmlRpc\IXmlRpcDriver.cs" />
@ -318,6 +323,7 @@
<Content Include="Shapes\Views\HeadPreload.cshtml" />
<Content Include="Shapes\Views\Message.cshtml" />
<Content Include="Shapes\Views\NotFound.cshtml" />
<Content Include="Title\Module.txt" />
<Content Include="XmlRpc\Module.txt" />
<Content Include="Settings\Views\EditorTemplates\Parts.Settings.SiteSettingsPart.cshtml" />
</ItemGroup>
@ -457,6 +463,24 @@
<ItemGroup>
<Content Include="Common\Views\DefinitionTemplates\OwnerEditorSettings.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Title\Placement.info" />
</ItemGroup>
<ItemGroup>
<Content Include="Title\Views\EditorTemplates\Parts.Title.TitlePart.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Title\Views\Parts.Title.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Title\Views\Parts.Title_Summary.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Title\Views\Parts.Title_SummaryAdmin.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Title\Views\Web.config" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.

View File

@ -78,6 +78,7 @@ namespace Orchard.Core.Routable.Handlers {
_routablePathConstraint.AddPath(route.Path);
}
};
OnPublished<RoutePart>(handler);
OnUnpublished<RoutePart>(handler);
@ -140,10 +141,8 @@ namespace Orchard.Core.Routable.Handlers {
if (routable == null)
return;
context.Metadata.DisplayText = routable.Title;
// set the display route values if it hasn't been set or only has been set by the Contents module.
// allows other modules to set their own display. probably not common enough to warrant some priority implemntation
// allows other modules to set their own display. probably not common enough to warrant some priority implementation
if (context.Metadata.DisplayRouteValues == null || context.Metadata.DisplayRouteValues["Area"] as string == "Contents") {
var itemPath = routable.Id == _routableHomePageProvider.GetHomePageId(_workContextAccessor.GetContext().CurrentSite.HomePage)
? ""

View File

@ -0,0 +1,52 @@
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
using Orchard.ContentManagement.Handlers;
using Orchard.Core.Title.Models;
using Orchard.Localization;
namespace Orchard.Core.Title.Drivers {
public class TitlePartDriver : ContentPartDriver<TitlePart> {
private const string TemplateName = "Parts.Title.TitlePart";
public Localizer T { get; set; }
protected override string Prefix {
get { return "Title"; }
}
protected override DriverResult Display(TitlePart part, string displayType, dynamic shapeHelper) {
return Combined(
ContentShape("Parts_Title",
() => shapeHelper.Parts_RoutableTitle(ContentPart: part, Title: part.Title)),
ContentShape("Parts_Title_Summary",
() => shapeHelper.Parts_RoutableTitle_Summary(ContentPart: part, Title: part.Title)),
ContentShape("Parts_Title_SummaryAdmin",
() => shapeHelper.Parts_RoutableTitle_SummaryAdmin(ContentPart: part, Title: part.Title))
);
}
protected override DriverResult Editor(TitlePart part, dynamic shapeHelper) {
return ContentShape("Parts_Title_Edit",
() => shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: part, Prefix: Prefix));
}
protected override DriverResult Editor(TitlePart part, IUpdateModel updater, dynamic shapeHelper) {
updater.TryUpdateModel(part, Prefix, null, null);
return Editor(part, shapeHelper);
}
protected override void Importing(TitlePart part, ImportContentContext context) {
var title = context.Attribute(part.PartDefinition.Name, "Title");
if (title != null) {
part.Title = title;
}
}
protected override void Exporting(TitlePart part, ExportContentContext context) {
context.Element(part.PartDefinition.Name).SetAttributeValue("Title", part.Title);
}
}
}

View File

@ -0,0 +1,12 @@
using Orchard.ContentManagement.Handlers;
using Orchard.Core.Title.Models;
using Orchard.Data;
namespace Orchard.Core.Title.Handlers {
public class TitlePartHandler : ContentHandler {
public TitlePartHandler(IRepository<TitlePartRecord> repository) {
Filters.Add(StorageFilter.For(repository));
}
}
}

View File

@ -0,0 +1,20 @@
using Orchard.ContentManagement.MetaData;
using Orchard.Core.Contents.Extensions;
using Orchard.Data.Migration;
namespace Orchard.Core.Title {
public class Migrations : DataMigrationImpl {
public int Create() {
SchemaBuilder.CreateTable("TitlePartRecord",
table => table
.ContentPartVersionRecord()
.Column<string>("Title", column => column.WithLength(1024))
);
ContentDefinitionManager.AlterPartDefinition("TitlePart", builder => builder.Attachable());
return 1;
}
}
}

View File

@ -0,0 +1,13 @@
using System.ComponentModel.DataAnnotations;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Aspects;
namespace Orchard.Core.Title.Models {
public class TitlePart : ContentPart<TitlePartRecord>, ITitleAspect {
[Required]
public string Title {
get { return Record.Title; }
set { Record.Title = value; }
}
}
}

View File

@ -0,0 +1,9 @@
using System.ComponentModel.DataAnnotations;
using Orchard.ContentManagement.Records;
namespace Orchard.Core.Title.Models {
public class TitlePartRecord : ContentPartVersionRecord {
[StringLength(1024)]
public virtual string Title { get; set; }
}
}

View File

@ -0,0 +1,9 @@
Name: Title
AntiForgery: enabled
Author: The Orchard Team
Website: http://orchardproject.net
Version: 1.2.0
OrchardVersion: 1.2.0
Description: The title module enables content items to have titles.
FeatureDescription: Title content part.
Category: Core

View File

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

View File

@ -0,0 +1,6 @@
@model Orchard.Core.Title.Models.TitlePart
<fieldset>
@Html.LabelFor(m => m.Title, T("Title"))
@Html.TextBoxFor(m => m.Title, new { @class = "large text" })
</fieldset>

View File

@ -0,0 +1 @@
<h1>@Model.Title</h1>

View File

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

View File

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

View File

@ -0,0 +1,41 @@
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="webpages:Enabled" value="false" />
</appSettings>
<system.web>
<httpHandlers>
</httpHandlers>
<!--
Enabling request validation in view pages would cause validation to occur
after the input has already been processed by the controller. By default
MVC performs request validation before a controller processes the input.
To change this behavior apply the ValidateInputAttribute to a
controller or action.
-->
<pages
validateRequest="false"
pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"
pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"
userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<controls>
<add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" namespace="System.Web.Mvc" tagPrefix="mvc" />
</controls>
</pages>
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
<handlers>
</handlers>
</system.webServer>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="2.0.0.0" newVersion="3.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View File

@ -5,7 +5,7 @@ using Orchard.ContentManagement;
using Orchard.ContentManagement.Aspects;
namespace Orchard.Widgets.Models {
public class WidgetPart : ContentPart<WidgetPartRecord> {
public class WidgetPart : ContentPart<WidgetPartRecord>, ITitleAspect {
/// <summary>
/// The widget's title.

View File

@ -1,6 +1,5 @@
namespace Orchard.ContentManagement.Aspects {
public interface IRoutableAspect : IContent {
string Title { get; set; }
public interface IRoutableAspect : ITitleAspect {
string Slug { get; set; }
string Path { get; set; }
}

View File

@ -0,0 +1,5 @@
namespace Orchard.ContentManagement.Aspects {
public interface ITitleAspect : IContent {
string Title { get; }
}
}

View File

@ -0,0 +1,18 @@
using Orchard.ContentManagement.Aspects;
namespace Orchard.ContentManagement.Handlers {
public class TitlePartHandler : ContentHandler {
public TitlePartHandler() {
OnIndexing<ITitleAspect>((context, part) => context.DocumentIndex.Add("title", part.Title).RemoveTags().Analyze());
}
protected override void GetItemMetadata(GetContentItemMetadataContext context) {
var part = context.ContentItem.As<ITitleAspect>();
if (part != null) {
context.Metadata.DisplayText = part.Title;
}
}
}
}

View File

@ -164,6 +164,7 @@
<Compile Include="Caching\DefaultParallelCacheContext.cs" />
<Compile Include="Caching\ICacheContextAccessor.cs" />
<Compile Include="Caching\IParallelCacheContext.cs" />
<Compile Include="ContentManagement\Aspects\ITitleAspect.cs" />
<Compile Include="ContentManagement\Aspects\ILocalizableAspect.cs" />
<Compile Include="ContentManagement\ContentIdentity.cs" />
<Compile Include="ContentManagement\ContentItemBehavior.cs" />
@ -174,6 +175,7 @@
<Compile Include="ContentManagement\Handlers\BuildShapeContext.cs" />
<Compile Include="ContentManagement\Handlers\ExportContentContext.cs" />
<Compile Include="ContentManagement\Handlers\ImportContentContext.cs" />
<Compile Include="ContentManagement\Handlers\TitleAspectHandler.cs" />
<Compile Include="ContentManagement\IContentBehavior.cs" />
<Compile Include="ContentManagement\ImportContentSession.cs" />
<Compile Include="ContentManagement\QueryHints.cs" />