Feature/maxlengthsetting (#8719)

* added MaxLength setting for TextField

# Conflicts:
#	src/Orchard.Web/Modules/Orchard.Taxonomies/Settings/TaxonomyFieldEditorEvents.cs

* added MaxLength setting for TitlePart

* added missing files

* Removed where clause

* changed title length to a constant and updated the recipe

* added comment to remind people to update the migration when they change the constant introduced previously

* fixed hint text for title

* fixed maxLength initialization in TitlePartSettings.cshtml

* MaxTitleLength constant is now Pascal case

* Correction on constant spelling in TitlePartSettings shape.

---------

Co-authored-by: Andrea Piovanelli <andrea.piovanelli@laser-group.com>
This commit is contained in:
Alessandro Agostini
2024-02-21 11:28:09 +01:00
committed by GitHub
parent eb09ab7f95
commit 44bfa390bc
18 changed files with 193 additions and 31 deletions

View File

@@ -10,6 +10,7 @@ using Orchard.Core.Common.Settings;
using Orchard.Core.Common.ViewModels;
using Orchard.Localization;
using Orchard.Services;
using Orchard.Utility.Extensions;
namespace Orchard.Core.Common.Drivers {
public class TextFieldDriver : ContentFieldDriver<TextField> {
@@ -71,6 +72,16 @@ namespace Orchard.Core.Common.Drivers {
if (settings.Required && String.IsNullOrWhiteSpace(field.Value)) {
updater.AddModelError("Text", T("The field {0} is mandatory", T(field.DisplayName)));
}
if (settings.MaxLength > 0) {
var value = new HtmlString(_htmlFilters.Aggregate(field.Value, (text, filter) => filter.ProcessContent(text, settings.Flavor)))
.ToString().RemoveTags();
if (value.Length > settings.MaxLength) {
updater.AddModelError("Text", T("The maximum allowed length for the field {0} is {1}", T(field.DisplayName), settings.MaxLength));
}
}
}
return Editor(part, field, shapeHelper);

View File

@@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace Orchard.Core.Common.Settings {
@@ -8,5 +9,8 @@ namespace Orchard.Core.Common.Settings {
public bool Required { get; set; }
public string Hint { get; set; }
public string DefaultValue { get; set; }
[Range(0, int.MaxValue)]
[DisplayName("Maximum Length")]
public int MaxLength { get; set; }
}
}

View File

@@ -34,9 +34,10 @@ namespace Orchard.Core.Common.Settings {
builder.WithSetting("TextFieldSettings.Hint", model.Settings.Hint);
builder.WithSetting("TextFieldSettings.Required", model.Settings.Required.ToString(CultureInfo.InvariantCulture));
builder.WithSetting("TextFieldSettings.DefaultValue", model.Settings.DefaultValue);
yield return DefinitionTemplate(model);
builder.WithSetting("TextFieldSettings.MaxLength", model.Settings.MaxLength.ToString());
}
yield return DefinitionTemplate(model);
}
}
}

View File

@@ -1,7 +1,14 @@
@{
var htmlAttributes = new Dictionary<string, object> {
{"class", "text large"}
};
var htmlAttributes = (Dictionary<string, object>)Model.HtmlAttributes;
if (htmlAttributes == null) {
htmlAttributes = new Dictionary<string, object> {
{"class", "text large"}
};
}
else {
htmlAttributes["class"] = "text large";
}
if (Model.Required == true) {
htmlAttributes["required"] = "required";

View File

@@ -1,7 +1,14 @@
@{
var htmlAttributes = new Dictionary<string, object> {
{"class", "text small"}
};
var htmlAttributes = (Dictionary<string, object>)Model.HtmlAttributes;
if (htmlAttributes == null) {
htmlAttributes = new Dictionary<string, object> {
{"class", "text small"}
};
}
else {
htmlAttributes["class"] = "text small";
}
if (Model.Required == true) {
htmlAttributes["required"] = "required";

View File

@@ -1,5 +1,10 @@
@{
var htmlAttributes = new Dictionary<string, object>();
var htmlAttributes = (Dictionary<string, object>)Model.HtmlAttributes;
if (htmlAttributes == null) {
htmlAttributes = new Dictionary<string, object>();
}
if (Model.Required == true) {
htmlAttributes["required"] = "required";

View File

@@ -1,7 +1,14 @@
@{
var htmlAttributes = new Dictionary<string, object> {
{"class", "text medium"}
};
var htmlAttributes = (Dictionary<string, object>)Model.HtmlAttributes;
if (htmlAttributes == null) {
htmlAttributes = new Dictionary<string, object> {
{"class", "text medium"}
};
}
else {
htmlAttributes["class"] = "text medium";
}
if (Model.Required == true) {
htmlAttributes["required"] = "required";

View File

@@ -1,10 +1,16 @@
@using Orchard.Utility.Extensions;
@{
string editorFlavor = Model.EditorFlavor;
var htmlAttributes = (Dictionary<string, object>)Model.HtmlAttributes;
var htmlAttributes = new Dictionary<string, object> {
{"class", editorFlavor.HtmlClassify()}
};
if (htmlAttributes == null) {
htmlAttributes = new Dictionary<string, object> {
{"class", editorFlavor.HtmlClassify()}};
}
else {
htmlAttributes["class"] = editorFlavor.HtmlClassify();
}
if (Model.Required == true) {
htmlAttributes["required"] = "required";

View File

@@ -7,6 +7,12 @@
@Html.ValidationMessageFor(m => m.Settings.Flavor)
</div>
</fieldset>
<fieldset>
<label for="@Html.FieldIdFor(m => m.Settings.MaxLength)">@T("Maximum length")</label>
@Html.EditorFor(m => m.Settings.MaxLength, new { htmlAttributes = new { min = 0 } })
<span class="hint">@T("Maximum length allowed for this field. Setting the value to 0 means unlimited length.")</span>
@Html.ValidationMessageFor(m => m.Settings.MaxLength)
</fieldset>
<fieldset>
<div>
@Html.CheckBoxFor(m => m.Settings.Required) <label for="@Html.FieldIdFor(m => m.Settings.Required)" class="forcheckbox">@T("Required")</label>
@@ -21,4 +27,4 @@
</fieldset>
<fieldset>
@Display.DefinitionTemplate(TemplateName: "TextFieldDefaultValueEditor", Model: Model)
</fieldset>
</fieldset>

View File

@@ -1,15 +1,26 @@
@model Orchard.Core.Common.ViewModels.TextFieldDriverViewModel
@{
var maxLength = Model.Settings.MaxLength > 0 ? Model.Settings.MaxLength.ToString() : "";
}
<fieldset>
<label for="@Html.FieldIdFor(m => m.Text)" @if(Model.Settings.Required) { <text>class="required"</text> }>@Model.Field.DisplayName</label>
<label for="@Html.FieldIdFor(m => m.Text)" @if (Model.Settings.Required) { <text> class="required" </text> }>@Model.Field.DisplayName</label>
@if (String.IsNullOrWhiteSpace(Model.Settings.Flavor)) {
@(Model.Settings.Required ? Html.TextBoxFor(m => m.Text, new {@class = "text", required = "required"}) : Html.TextBoxFor(m => m.Text, new {@class = "text"}))
@(Model.Settings.Required
? Html.TextBoxFor(m => m.Text, new {@class = "text", required = "required", maxlength = maxLength})
: Html.TextBoxFor(m => m.Text, new {@class = "text", maxlength = maxLength }))
@Html.ValidationMessageFor(m => m.Text)
}
else {
@Display.Body_Editor(Text: Model.Text, EditorFlavor: Model.Settings.Flavor, Required: Model.Settings.Required, ContentItem: Model.ContentItem, Field: Model.Field)
var htmlAttributes = new Dictionary<string, object> {
{"maxlength", maxLength}
};
@Display.Body_Editor(Text: Model.Text, EditorFlavor: Model.Settings.Flavor, Required: Model.Settings.Required,
ContentItem: Model.ContentItem, Field: Model.Field, HtmlAttributes: htmlAttributes)
}
@if (HasText(Model.Settings.Hint)) {
<span class="hint">@Model.Settings.Hint</span>
<span class="hint">@Model.Settings.Hint</span>
}
</fieldset>

View File

@@ -293,6 +293,8 @@
<Compile Include="Title\Migrations.cs" />
<Compile Include="Title\Models\TitlePart.cs" />
<Compile Include="Title\Models\TitlePartRecord.cs" />
<Compile Include="Title\Settings\TitlePartSettings.cs" />
<Compile Include="Title\Settings\TitlePartSettingsEvents.cs" />
<Compile Include="XmlRpc\Controllers\HomeController.cs" />
<Compile Include="XmlRpc\Controllers\LiveWriterController.cs" />
<Compile Include="XmlRpc\IXmlRpcDriver.cs" />
@@ -612,6 +614,7 @@
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<Content Include="Title\Views\DefinitionTemplates\TitlePartSettings.cshtml" />
</ItemGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>

View File

@@ -1,7 +1,9 @@
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
using Orchard.ContentManagement.Handlers;
using Orchard.Core.Common.Settings;
using Orchard.Core.Title.Models;
using Orchard.Core.Title.Settings;
using Orchard.Localization;
namespace Orchard.Core.Title.Drivers {
@@ -33,7 +35,14 @@ namespace Orchard.Core.Title.Drivers {
}
protected override DriverResult Editor(TitlePart part, IUpdateModel updater, dynamic shapeHelper) {
updater.TryUpdateModel(part, Prefix, null, null);
if (updater.TryUpdateModel(part, Prefix, null, null)){
var settings = part.Settings.GetModel<TitlePartSettings>();
if (settings.MaxLength > 0 && part.Title.Length > settings.MaxLength) {
updater.AddModelError("Title", T("The maximum allowed length for the title is {0}", settings.MaxLength));
}
}
return Editor(part, shapeHelper);
}

View File

@@ -1,6 +1,7 @@
using Orchard.ContentManagement.MetaData;
using Orchard.Core.Contents.Extensions;
using Orchard.Data.Migration;
using Orchard.Core.Title.Settings;
namespace Orchard.Core.Title {
public class Migrations : DataMigrationImpl {
@@ -9,7 +10,7 @@ namespace Orchard.Core.Title {
SchemaBuilder.CreateTable("TitlePartRecord",
table => table
.ContentPartVersionRecord()
.Column<string>("Title", column => column.WithLength(1024))
.Column<string>("Title", column => column.WithLength(TitlePartSettings.MaxTitleLength))
);
ContentDefinitionManager.AlterPartDefinition("TitlePart", builder => builder

View File

@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
using System.Linq;
using System.Web;
namespace Orchard.Core.Title.Settings {
public class TitlePartSettings {
// Whenever this constant is changed a new migration step must be created to update the length of the field on the DB
public const int MaxTitleLength = 1024;
[Range(0, MaxTitleLength)]
[DisplayName("Maximum Length")]
public int MaxLength {get; set;}
}
}

View File

@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Orchard.ContentManagement;
using Orchard.ContentManagement.MetaData;
using Orchard.ContentManagement.MetaData.Builders;
using Orchard.ContentManagement.MetaData.Models;
using Orchard.ContentManagement.ViewModels;
namespace Orchard.Core.Title.Settings {
public class TitlePartSettingsEvents : ContentDefinitionEditorEventsBase {
public override IEnumerable<TemplateViewModel> TypePartEditor(ContentTypePartDefinition definition) {
if (definition.PartDefinition.Name != "TitlePart") {
yield break;
}
var settings = definition
.Settings
.GetModel<TitlePartSettings>()
?? new TitlePartSettings();
yield return DefinitionTemplate(settings);
}
public override IEnumerable<TemplateViewModel> TypePartEditorUpdate(ContentTypePartDefinitionBuilder builder, IUpdateModel updateModel) {
if (builder.Name != "TitlePart") {
yield break;
}
var model = new TitlePartSettings();
if (updateModel.TryUpdateModel(model, "TitlePartSettings", null, null)) {
builder.WithSetting("TitlePartSettings.MaxLength", model.MaxLength.ToString());
}
yield return DefinitionTemplate(model);
}
}
}

View File

@@ -0,0 +1,12 @@
@using Orchard.Core.Title.Settings;
@model TitlePartSettings
@{
var maxLength = TitlePartSettings.MaxTitleLength;
}
<fieldset>
<label for="@Html.FieldIdFor(m => m.MaxLength)">@T("Maximum length")</label>
@Html.EditorFor(m => m.MaxLength, new { htmlAttributes = new { min = 0, max = maxLength } })
<span class="hint">@T("Maximum length allowed for the title. Setting the value to 0 means the maximum allowed length is {0} characters.", maxLength)</span>
@Html.ValidationMessageFor(m => m.MaxLength)
</fieldset>

View File

@@ -1,7 +1,11 @@
@model Orchard.Core.Title.Models.TitlePart
@using Orchard.Core.Title.Settings
@model Orchard.Core.Title.Models.TitlePart
@{
var maxLength = Model.Settings.GetModel<TitlePartSettings>().MaxLength > 0 ? Model.Settings.GetModel<TitlePartSettings>().MaxLength.ToString() : "";
}
<fieldset>
<label for="@Html.FieldIdFor(m => m.Title)" class="required">@T("Title")</label>
@Html.TextBoxFor(m => m.Title, new { @class = "text large", autofocus = "autofocus" })
<label for="@Html.FieldIdFor(m => m.Title)" class="required">@T("Title")</label>
@Html.TextBoxFor(m => m.Title, new { @class = "text large", autofocus = "autofocus", maxlength = maxLength })
<span class="hint">@T("You must provide a title for this content item")</span>
</fieldset>

View File

@@ -4,8 +4,8 @@ using Orchard.ContentManagement.MetaData;
using Orchard.ContentManagement.MetaData.Builders;
using Orchard.ContentManagement.MetaData.Models;
using Orchard.ContentManagement.ViewModels;
using Orchard.Taxonomies.Services;
using Orchard.Localization;
using Orchard.Taxonomies.Services;
namespace Orchard.Taxonomies.Settings {
public class TaxonomyFieldEditorEvents : ContentDefinitionEditorEventsBase {
@@ -20,8 +20,7 @@ namespace Orchard.Taxonomies.Settings {
public override IEnumerable<TemplateViewModel> PartFieldEditor(ContentPartFieldDefinition definition) {
if (definition.FieldDefinition.Name == "TaxonomyField") {
var model = definition.Settings.GetModel<TaxonomyFieldSettings>();
model.Taxonomies = _taxonomyService.GetTaxonomies();
var model = GetCurrentSettings(definition);
yield return DefinitionTemplate(model);
}
}
@@ -31,7 +30,8 @@ namespace Orchard.Taxonomies.Settings {
yield break;
}
var model = new TaxonomyFieldSettings();
// Init this model preventively so if the TryUpdateModel doesn't execute correctly it doesn't cause an error
var model = GetCurrentSettings(builder.Current);
if (updateModel.TryUpdateModel(model, "TaxonomyFieldSettings", null, null)) {
builder
@@ -43,8 +43,15 @@ namespace Orchard.Taxonomies.Settings {
.WithSetting("TaxonomyFieldSettings.AllowCustomTerms", model.AllowCustomTerms.ToString())
.WithSetting("TaxonomyFieldSettings.Hint", model.Hint);
}
yield return DefinitionTemplate(model);
}
private TaxonomyFieldSettings GetCurrentSettings(ContentPartFieldDefinition definition) {
var model = definition.Settings.GetModel<TaxonomyFieldSettings>();
model.Taxonomies = _taxonomyService.GetTaxonomies();
return model;
}
}
}