mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-09-22 20:13:50 +08:00
#17255: Adding a DisplayName property to fields
Work Item: 17255 --HG-- branch : 1.x
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
ec573e5476f7e8a5a61593d6393e9985e9484fcc src/Orchard.Web/Modules/Orchard.Forms
|
ec573e5476f7e8a5a61593d6393e9985e9484fcc src/Orchard.Web/Modules/Orchard.Forms
|
||||||
d4e8bde4741a16cb341311395b211fcf211d2808 src/Orchard.Web/Modules/Orchard.Projections
|
0d1100754d594a2977eaab40630f1c46a9e8cf2c src/Orchard.Web/Modules/Orchard.Projections
|
||||||
01b83c05050bb731d9f69256bbe8884d458ea1c9 src/Orchard.Web/Modules/Orchard.Rules
|
01b83c05050bb731d9f69256bbe8884d458ea1c9 src/Orchard.Web/Modules/Orchard.Rules
|
||||||
65057c6a5cd71f7994ba9bcbeece50dbb737620e src/Orchard.Web/Modules/Orchard.TaskLease
|
65057c6a5cd71f7994ba9bcbeece50dbb737620e src/Orchard.Web/Modules/Orchard.TaskLease
|
||||||
460f08a0d0befd36a3f7e974d8b782ae3df747e7 src/Orchard.Web/Modules/Orchard.Tokens
|
460f08a0d0befd36a3f7e974d8b782ae3df747e7 src/Orchard.Web/Modules/Orchard.Tokens
|
||||||
|
@@ -1,8 +1,7 @@
|
|||||||
@using Orchard.Utility.Extensions;
|
@{
|
||||||
@{
|
string name = Model.Field.DisplayName;
|
||||||
string name = Model.Name;
|
|
||||||
string value = Model.Value;
|
string value = Model.Value;
|
||||||
}
|
}
|
||||||
@if (HasText(name) && HasText(value)) {
|
@if (HasText(name) && HasText(value)) {
|
||||||
<p class="text-field"><span class="name">@name.CamelFriendly():</span> <span class="value">@value</span></p>
|
<p class="text-field"><span class="name">@name:</span> <span class="value">@value</span></p>
|
||||||
}
|
}
|
@@ -1,14 +1,13 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Orchard.ContentManagement;
|
using Orchard.ContentManagement;
|
||||||
using Orchard.ContentManagement.Aspects;
|
using Orchard.ContentManagement.Aspects;
|
||||||
using Orchard.Core.Common.Models;
|
using Orchard.Core.Common.Models;
|
||||||
using Orchard.Core.Routable.Events;
|
using Orchard.Core.Routable.Events;
|
||||||
using Orchard.Core.Routable.Models;
|
using Orchard.Core.Routable.Models;
|
||||||
|
using Orchard.Utility.Extensions;
|
||||||
|
|
||||||
namespace Orchard.Core.Routable.Services {
|
namespace Orchard.Core.Routable.Services {
|
||||||
public class RoutableService : IRoutableService {
|
public class RoutableService : IRoutableService {
|
||||||
@@ -51,20 +50,6 @@ namespace Orchard.Core.Routable.Services {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string RemoveDiacritics(string slug) {
|
|
||||||
string stFormD = slug.Normalize(NormalizationForm.FormD);
|
|
||||||
var sb = new StringBuilder();
|
|
||||||
|
|
||||||
foreach (char t in stFormD) {
|
|
||||||
UnicodeCategory uc = CharUnicodeInfo.GetUnicodeCategory(t);
|
|
||||||
if (uc != UnicodeCategory.NonSpacingMark) {
|
|
||||||
sb.Append(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (sb.ToString().Normalize(NormalizationForm.FormC));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void FillSlugFromTitle<TModel>(TModel model) where TModel : IRoutableAspect {
|
public void FillSlugFromTitle<TModel>(TModel model) where TModel : IRoutableAspect {
|
||||||
if ((model.Slug != null && !string.IsNullOrEmpty(model.Slug.Trim())) || string.IsNullOrEmpty(model.Title))
|
if ((model.Slug != null && !string.IsNullOrEmpty(model.Slug.Trim())) || string.IsNullOrEmpty(model.Title))
|
||||||
return;
|
return;
|
||||||
@@ -84,7 +69,7 @@ namespace Orchard.Core.Routable.Services {
|
|||||||
slugContext.Slug = slugContext.Slug.Substring(0, 1000);
|
slugContext.Slug = slugContext.Slug.Substring(0, 1000);
|
||||||
|
|
||||||
// dots are not allowed at the begin and the end of routes
|
// dots are not allowed at the begin and the end of routes
|
||||||
slugContext.Slug = RemoveDiacritics(slugContext.Slug.Trim('.').ToLower());
|
slugContext.Slug = StringExtensions.RemoveDiacritics(slugContext.Slug.Trim('.').ToLower());
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (ISlugEventHandler slugEventHandler in _slugEventHandlers) {
|
foreach (ISlugEventHandler slugEventHandler in _slugEventHandlers) {
|
||||||
|
@@ -10,6 +10,7 @@ using Orchard.Core.Contents.Controllers;
|
|||||||
using Orchard.Core.Contents.Settings;
|
using Orchard.Core.Contents.Settings;
|
||||||
using Orchard.Localization;
|
using Orchard.Localization;
|
||||||
using Orchard.UI.Notify;
|
using Orchard.UI.Notify;
|
||||||
|
using Orchard.Utility.Extensions;
|
||||||
|
|
||||||
namespace Orchard.ContentTypes.Controllers {
|
namespace Orchard.ContentTypes.Controllers {
|
||||||
public class AdminController : Controller, IUpdateModel {
|
public class AdminController : Controller, IUpdateModel {
|
||||||
@@ -63,7 +64,7 @@ namespace Orchard.ContentTypes.Controllers {
|
|||||||
ModelState.AddModelError("Name", T("A type with the same Id already exists.").ToString());
|
ModelState.AddModelError("Name", T("A type with the same Id already exists.").ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!String.IsNullOrWhiteSpace(viewModel.Name) && !ContentDefinitionService.IsLetter(viewModel.Name[0])) {
|
if (!String.IsNullOrWhiteSpace(viewModel.Name) && !viewModel.Name[0].IsLetter()) {
|
||||||
ModelState.AddModelError("Name", T("The technical name must start with a letter.").ToString());
|
ModelState.AddModelError("Name", T("The technical name must start with a letter.").ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,10 +72,6 @@ namespace Orchard.ContentTypes.Controllers {
|
|||||||
ModelState.AddModelError("DisplayName", T("A type with the same Display Name already exists.").ToString());
|
ModelState.AddModelError("DisplayName", T("A type with the same Display Name already exists.").ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_contentDefinitionService.GetTypes().Any(t => String.Equals(t.Name, viewModel.Name, StringComparison.OrdinalIgnoreCase))) {
|
|
||||||
ModelState.AddModelError("Name", T("A type with the same Name already exists.").ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ModelState.IsValid) {
|
if (!ModelState.IsValid) {
|
||||||
Services.TransactionManager.Cancel();
|
Services.TransactionManager.Cancel();
|
||||||
return View(viewModel);
|
return View(viewModel);
|
||||||
@@ -97,6 +94,10 @@ namespace Orchard.ContentTypes.Controllers {
|
|||||||
return Json(_contentDefinitionService.GenerateContentTypeNameFromDisplayName(displayName));
|
return Json(_contentDefinitionService.GenerateContentTypeNameFromDisplayName(displayName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ActionResult FieldName(string partName, string displayName) {
|
||||||
|
return Json(_contentDefinitionService.GenerateFieldNameFromDisplayName(partName, displayName));
|
||||||
|
}
|
||||||
|
|
||||||
public ActionResult Edit(string id) {
|
public ActionResult Edit(string id) {
|
||||||
if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to edit a content type.")))
|
if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to edit a content type.")))
|
||||||
return new HttpUnauthorizedResult();
|
return new HttpUnauthorizedResult();
|
||||||
@@ -147,7 +148,6 @@ namespace Orchard.ContentTypes.Controllers {
|
|||||||
return RedirectToAction("List");
|
return RedirectToAction("List");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[HttpPost, ActionName("Edit")]
|
[HttpPost, ActionName("Edit")]
|
||||||
[FormValueRequired("submit.Delete")]
|
[FormValueRequired("submit.Delete")]
|
||||||
public ActionResult Delete(string id) {
|
public ActionResult Delete(string id) {
|
||||||
@@ -371,21 +371,21 @@ namespace Orchard.ContentTypes.Controllers {
|
|||||||
|
|
||||||
var viewModel = new AddFieldViewModel {
|
var viewModel = new AddFieldViewModel {
|
||||||
Part = partViewModel,
|
Part = partViewModel,
|
||||||
Fields = _contentDefinitionService.GetFields()
|
Fields = _contentDefinitionService.GetFields().OrderBy(x => x.FieldTypeName)
|
||||||
};
|
};
|
||||||
|
|
||||||
return View(viewModel);
|
return View(viewModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost, ActionName("AddFieldTo")]
|
[HttpPost, ActionName("AddFieldTo")]
|
||||||
public ActionResult AddFieldToPOST(string id) {
|
public ActionResult AddFieldToPOST(AddFieldViewModel viewModel, string id) {
|
||||||
if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to edit a content part.")))
|
if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to edit a content part.")))
|
||||||
return new HttpUnauthorizedResult();
|
return new HttpUnauthorizedResult();
|
||||||
|
|
||||||
var partViewModel = _contentDefinitionService.GetPart(id);
|
var partViewModel = _contentDefinitionService.GetPart(id);
|
||||||
var typeViewModel = _contentDefinitionService.GetType(id);
|
var typeViewModel = _contentDefinitionService.GetType(id);
|
||||||
if (partViewModel == null) {
|
if (partViewModel == null) {
|
||||||
//id passed in might be that of a type w/ no implicit field
|
// id passed in might be that of a type w/ no implicit field
|
||||||
if (typeViewModel != null) {
|
if (typeViewModel != null) {
|
||||||
partViewModel = new EditPartViewModel {Name = typeViewModel.Name};
|
partViewModel = new EditPartViewModel {Name = typeViewModel.Name};
|
||||||
_contentDefinitionService.AddPart(new CreatePartViewModel {Name = partViewModel.Name});
|
_contentDefinitionService.AddPart(new CreatePartViewModel {Name = partViewModel.Name});
|
||||||
@@ -396,34 +396,137 @@ namespace Orchard.ContentTypes.Controllers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var viewModel = new AddFieldViewModel();
|
viewModel.DisplayName = viewModel.DisplayName ?? String.Empty;
|
||||||
if (!TryUpdateModel(viewModel)) {
|
viewModel.DisplayName = viewModel.DisplayName.Trim();
|
||||||
Services.TransactionManager.Cancel();
|
viewModel.Name = viewModel.Name ?? String.Empty;
|
||||||
return AddFieldTo(id);
|
|
||||||
|
if (String.IsNullOrWhiteSpace(viewModel.DisplayName)) {
|
||||||
|
ModelState.AddModelError("DisplayName", T("The Display Name name can't be empty.").ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
if (String.IsNullOrWhiteSpace(viewModel.Name)) {
|
||||||
_contentDefinitionService.AddFieldToPart(viewModel.DisplayName, viewModel.FieldTypeName, partViewModel.Name);
|
ModelState.AddModelError("Name", T("The Technical Name can't be empty.").ToString());
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
|
||||||
Services.Notifier.Information(T("The \"{0}\" field was not added. {1}", viewModel.DisplayName, ex.Message));
|
if (_contentDefinitionService.GetPart(partViewModel.Name).Fields.Any(t => String.Equals(t.Name.Trim(), viewModel.Name.Trim(), StringComparison.OrdinalIgnoreCase))) {
|
||||||
Services.TransactionManager.Cancel();
|
ModelState.AddModelError("Name", T("A field with the same name already exists.").ToString());
|
||||||
return AddFieldTo(id);
|
}
|
||||||
|
|
||||||
|
if (!String.IsNullOrWhiteSpace(viewModel.Name) && !viewModel.Name[0].IsLetter()) {
|
||||||
|
ModelState.AddModelError("Name", T("The technical name must start with a letter.").ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!String.Equals(viewModel.Name, viewModel.Name.ToSafeName(), StringComparison.OrdinalIgnoreCase)) {
|
||||||
|
ModelState.AddModelError("Name", T("The technical name contains invalid characters.").ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_contentDefinitionService.GetPart(partViewModel.Name).Fields.Any(t => String.Equals(t.DisplayName.Trim(), Convert.ToString(viewModel.DisplayName).Trim(), StringComparison.OrdinalIgnoreCase))) {
|
||||||
|
ModelState.AddModelError("DisplayName", T("A field with the same Display Name already exists.").ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ModelState.IsValid) {
|
if (!ModelState.IsValid) {
|
||||||
|
viewModel.Part = partViewModel;
|
||||||
|
viewModel.Fields = _contentDefinitionService.GetFields();
|
||||||
|
|
||||||
|
Services.TransactionManager.Cancel();
|
||||||
|
|
||||||
|
return View(viewModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
_contentDefinitionService.AddFieldToPart(viewModel.Name, viewModel.FieldTypeName, partViewModel.Name);
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
Services.Notifier.Information(T("The \"{0}\" field was not added. {1}", viewModel.Name, ex.Message));
|
||||||
Services.TransactionManager.Cancel();
|
Services.TransactionManager.Cancel();
|
||||||
return AddFieldTo(id);
|
return AddFieldTo(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
Services.Notifier.Information(T("The \"{0}\" field has been added.", viewModel.DisplayName));
|
Services.Notifier.Information(T("The \"{0}\" field has been added.", viewModel.Name));
|
||||||
|
|
||||||
if (typeViewModel != null)
|
if (typeViewModel != null) {
|
||||||
return RedirectToAction("Edit", new { id });
|
return RedirectToAction("Edit", new {id});
|
||||||
|
}
|
||||||
|
|
||||||
return RedirectToAction("EditPart", new { id });
|
return RedirectToAction("EditPart", new { id });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ActionResult EditField(string id, string name) {
|
||||||
|
if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to edit a content type.")))
|
||||||
|
return new HttpUnauthorizedResult();
|
||||||
|
|
||||||
|
var partViewModel = _contentDefinitionService.GetPart(id);
|
||||||
|
|
||||||
|
if (partViewModel == null) {
|
||||||
|
return HttpNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
var fieldViewModel = partViewModel.Fields.FirstOrDefault(x => x.Name == name);
|
||||||
|
|
||||||
|
if(fieldViewModel == null) {
|
||||||
|
return HttpNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
var viewModel = new EditFieldNameViewModel {
|
||||||
|
Name = fieldViewModel.Name,
|
||||||
|
DisplayName = fieldViewModel.DisplayName
|
||||||
|
};
|
||||||
|
|
||||||
|
return View(viewModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost, ActionName("EditField")]
|
||||||
|
[FormValueRequired("submit.Save")]
|
||||||
|
public ActionResult EditFieldPOST(string id, EditFieldNameViewModel viewModel) {
|
||||||
|
if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to edit a content type.")))
|
||||||
|
return new HttpUnauthorizedResult();
|
||||||
|
|
||||||
|
if (viewModel == null)
|
||||||
|
return HttpNotFound();
|
||||||
|
|
||||||
|
var partViewModel = _contentDefinitionService.GetPart(id);
|
||||||
|
|
||||||
|
if (partViewModel == null) {
|
||||||
|
return HttpNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
// prevent null reference exception in validation
|
||||||
|
viewModel.DisplayName = viewModel.DisplayName ?? String.Empty;
|
||||||
|
|
||||||
|
// remove extra spaces
|
||||||
|
viewModel.DisplayName = viewModel.DisplayName.Trim();
|
||||||
|
|
||||||
|
if (String.IsNullOrWhiteSpace(viewModel.DisplayName)) {
|
||||||
|
ModelState.AddModelError("DisplayName", T("The Display Name name can't be empty.").ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_contentDefinitionService.GetPart(partViewModel.Name).Fields.Any(t => t.Name != viewModel.Name && String.Equals(t.DisplayName.Trim(), viewModel.DisplayName.Trim(), StringComparison.OrdinalIgnoreCase))) {
|
||||||
|
ModelState.AddModelError("DisplayName", T("A field with the same Display Name already exists.").ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ModelState.IsValid) {
|
||||||
|
return View(viewModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
var field = _contentDefinitionManager.GetPartDefinition(id).Fields.FirstOrDefault(x => x.Name == viewModel.Name);
|
||||||
|
|
||||||
|
if(field == null) {
|
||||||
|
return HttpNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
field.DisplayName = viewModel.DisplayName;
|
||||||
|
_contentDefinitionManager.StorePartDefinition(partViewModel._Definition);
|
||||||
|
|
||||||
|
Services.Notifier.Information(T("Display name changed to {0}.", viewModel.DisplayName));
|
||||||
|
|
||||||
|
// redirect to the type editor if a type exists with this name
|
||||||
|
var typeViewModel = _contentDefinitionService.GetType(id);
|
||||||
|
if (typeViewModel != null) {
|
||||||
|
return RedirectToAction("Edit", new { id });
|
||||||
|
}
|
||||||
|
|
||||||
|
return RedirectToAction("EditPart", new { id });
|
||||||
|
}
|
||||||
|
|
||||||
public ActionResult RemoveFieldFrom(string id) {
|
public ActionResult RemoveFieldFrom(string id) {
|
||||||
if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to edit a content part.")))
|
if (!Services.Authorizer.Authorize(Permissions.EditContentTypes, T("Not allowed to edit a content part.")))
|
||||||
|
@@ -14,6 +14,7 @@
|
|||||||
<AssemblyName>Orchard.ContentTypes</AssemblyName>
|
<AssemblyName>Orchard.ContentTypes</AssemblyName>
|
||||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||||
<MvcBuildViews>false</MvcBuildViews>
|
<MvcBuildViews>false</MvcBuildViews>
|
||||||
|
<UseIISExpress>false</UseIISExpress>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
@@ -57,6 +58,7 @@
|
|||||||
<Compile Include="Services\ContentDefinitionService.cs" />
|
<Compile Include="Services\ContentDefinitionService.cs" />
|
||||||
<Compile Include="Services\IContentDefinitionService.cs" />
|
<Compile Include="Services\IContentDefinitionService.cs" />
|
||||||
<Compile Include="ViewModels\AddFieldViewModel.cs" />
|
<Compile Include="ViewModels\AddFieldViewModel.cs" />
|
||||||
|
<Compile Include="ViewModels\EditFieldNameViewModel.cs" />
|
||||||
<Compile Include="ViewModels\CreatePartViewModel.cs" />
|
<Compile Include="ViewModels\CreatePartViewModel.cs" />
|
||||||
<Compile Include="ViewModels\EditFieldViewModel.cs" />
|
<Compile Include="ViewModels\EditFieldViewModel.cs" />
|
||||||
<Compile Include="ViewModels\EditPartFieldViewModel.cs" />
|
<Compile Include="ViewModels\EditPartFieldViewModel.cs" />
|
||||||
@@ -115,6 +117,9 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="web.config" />
|
<Content Include="web.config" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="Views\Admin\EditField.cshtml" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.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.
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using Orchard.ContentManagement;
|
using Orchard.ContentManagement;
|
||||||
using Orchard.ContentManagement.Drivers;
|
using Orchard.ContentManagement.Drivers;
|
||||||
using Orchard.ContentManagement.MetaData;
|
using Orchard.ContentManagement.MetaData;
|
||||||
@@ -9,6 +8,7 @@ using Orchard.ContentManagement.MetaData.Models;
|
|||||||
using Orchard.ContentTypes.ViewModels;
|
using Orchard.ContentTypes.ViewModels;
|
||||||
using Orchard.Core.Contents.Extensions;
|
using Orchard.Core.Contents.Extensions;
|
||||||
using Orchard.Localization;
|
using Orchard.Localization;
|
||||||
|
using Orchard.Utility.Extensions;
|
||||||
|
|
||||||
namespace Orchard.ContentTypes.Services {
|
namespace Orchard.ContentTypes.Services {
|
||||||
public class ContentDefinitionService : IContentDefinitionService {
|
public class ContentDefinitionService : IContentDefinitionService {
|
||||||
@@ -72,7 +72,7 @@ namespace Orchard.ContentTypes.Services {
|
|||||||
name = GenerateContentTypeNameFromDisplayName(displayName);
|
name = GenerateContentTypeNameFromDisplayName(displayName);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(!IsLetter(name[0])) {
|
if(!name[0].IsLetter()) {
|
||||||
throw new ArgumentException("Content type name must start with a letter", "name");
|
throw new ArgumentException("Content type name must start with a letter", "name");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -219,7 +219,7 @@ namespace Orchard.ContentTypes.Services {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void AddFieldToPart(string fieldName, string fieldTypeName, string partName) {
|
public void AddFieldToPart(string fieldName, string fieldTypeName, string partName) {
|
||||||
fieldName = SafeName(fieldName);
|
fieldName = fieldName.ToSafeName();
|
||||||
if (string.IsNullOrEmpty(fieldName)) {
|
if (string.IsNullOrEmpty(fieldName)) {
|
||||||
throw new OrchardException(T("Fields must have a name containing no spaces or symbols."));
|
throw new OrchardException(T("Fields must have a name containing no spaces or symbols."));
|
||||||
}
|
}
|
||||||
@@ -231,35 +231,44 @@ namespace Orchard.ContentTypes.Services {
|
|||||||
_contentDefinitionManager.AlterPartDefinition(partName, typeBuilder => typeBuilder.RemoveField(fieldName));
|
_contentDefinitionManager.AlterPartDefinition(partName, typeBuilder => typeBuilder.RemoveField(fieldName));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string SafeName(string name) {
|
public string GenerateContentTypeNameFromDisplayName(string displayName) {
|
||||||
if (string.IsNullOrWhiteSpace(name))
|
displayName = displayName.ToSafeName();
|
||||||
return String.Empty;
|
|
||||||
|
|
||||||
var dissallowed = new Regex(@"[/:?#\[\]@!$&'()*+,;=\s\""<>]+");
|
while (_contentDefinitionManager.GetTypeDefinition(displayName) != null)
|
||||||
|
displayName = VersionName(displayName);
|
||||||
|
|
||||||
name = dissallowed.Replace(name, String.Empty);
|
return displayName;
|
||||||
name = name.Trim();
|
}
|
||||||
|
|
||||||
// don't allow non A-Z chars as first letter, as they are not allowed in prefixes
|
public string GenerateFieldNameFromDisplayName(string partName, string displayName) {
|
||||||
while(name.Length > 0 && !IsLetter(name[0])) {
|
IEnumerable<ContentPartFieldDefinition> fieldDefinitions;
|
||||||
name = name.Substring(1);
|
|
||||||
|
var part = _contentDefinitionManager.GetPartDefinition(partName);
|
||||||
|
displayName = displayName.ToSafeName();
|
||||||
|
|
||||||
|
if(part == null) {
|
||||||
|
var type = _contentDefinitionManager.GetTypeDefinition(partName);
|
||||||
|
|
||||||
|
if(type == null) {
|
||||||
|
throw new ArgumentException("The part doesn't exist: " + partName);
|
||||||
|
}
|
||||||
|
|
||||||
|
var typePart = type.Parts.FirstOrDefault(x => x.PartDefinition.Name == partName);
|
||||||
|
|
||||||
|
// id passed in might be that of a type w/ no implicit field
|
||||||
|
if(typePart == null) {
|
||||||
|
return displayName;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fieldDefinitions = typePart.PartDefinition.Fields.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fieldDefinitions = part.Fields.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name.Length > 128)
|
while (fieldDefinitions.Any(x => String.Equals(displayName.Trim(), x.Name.Trim(), StringComparison.OrdinalIgnoreCase)))
|
||||||
name = name.Substring(0, 128);
|
|
||||||
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsLetter(char c) {
|
|
||||||
return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
|
|
||||||
}
|
|
||||||
|
|
||||||
//gratuitously stolen from the RoutableService
|
|
||||||
public string GenerateContentTypeNameFromDisplayName(string displayName) {
|
|
||||||
displayName = SafeName(displayName);
|
|
||||||
|
|
||||||
while ( _contentDefinitionManager.GetTypeDefinition(displayName) != null )
|
|
||||||
displayName = VersionName(displayName);
|
displayName = VersionName(displayName);
|
||||||
|
|
||||||
return displayName;
|
return displayName;
|
||||||
|
@@ -14,6 +14,7 @@ namespace Orchard.ContentTypes.Services {
|
|||||||
void AddPartToType(string partName, string typeName);
|
void AddPartToType(string partName, string typeName);
|
||||||
void RemovePartFromType(string partName, string typeName);
|
void RemovePartFromType(string partName, string typeName);
|
||||||
string GenerateContentTypeNameFromDisplayName(string displayName);
|
string GenerateContentTypeNameFromDisplayName(string displayName);
|
||||||
|
string GenerateFieldNameFromDisplayName(string partName, string displayName);
|
||||||
|
|
||||||
IEnumerable<EditPartViewModel> GetParts(bool metadataPartsOnly);
|
IEnumerable<EditPartViewModel> GetParts(bool metadataPartsOnly);
|
||||||
EditPartViewModel GetPart(string name);
|
EditPartViewModel GetPart(string name);
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
using Orchard.ContentManagement.MetaData;
|
using Orchard.ContentManagement.MetaData;
|
||||||
|
|
||||||
namespace Orchard.ContentTypes.ViewModels {
|
namespace Orchard.ContentTypes.ViewModels {
|
||||||
@@ -7,9 +8,32 @@ namespace Orchard.ContentTypes.ViewModels {
|
|||||||
Fields = new List<ContentFieldInfo>();
|
Fields = new List<ContentFieldInfo>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The technical name of the field
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The display name of the field
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
public string DisplayName { get; set; }
|
public string DisplayName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The selected field type
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
public string FieldTypeName { get; set; }
|
public string FieldTypeName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The part to add the field to
|
||||||
|
/// </summary>
|
||||||
public EditPartViewModel Part { get; set; }
|
public EditPartViewModel Part { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// List of the available Field types
|
||||||
|
/// </summary>
|
||||||
public IEnumerable<ContentFieldInfo> Fields { get; set; }
|
public IEnumerable<ContentFieldInfo> Fields { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -0,0 +1,18 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Orchard.ContentManagement.MetaData;
|
||||||
|
|
||||||
|
namespace Orchard.ContentTypes.ViewModels {
|
||||||
|
public class EditFieldNameViewModel {
|
||||||
|
/// <summary>
|
||||||
|
/// The technical name of the field
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The display name of the field
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public string DisplayName { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@@ -12,6 +12,7 @@ namespace Orchard.ContentTypes.ViewModels {
|
|||||||
public EditPartFieldViewModel(int index, ContentPartFieldDefinition field) {
|
public EditPartFieldViewModel(int index, ContentPartFieldDefinition field) {
|
||||||
Index = index;
|
Index = index;
|
||||||
Name = field.Name;
|
Name = field.Name;
|
||||||
|
DisplayName = field.DisplayName;
|
||||||
FieldDefinition = new EditFieldViewModel(field.FieldDefinition);
|
FieldDefinition = new EditFieldViewModel(field.FieldDefinition);
|
||||||
Settings = field.Settings;
|
Settings = field.Settings;
|
||||||
_Definition = field;
|
_Definition = field;
|
||||||
@@ -22,6 +23,7 @@ namespace Orchard.ContentTypes.ViewModels {
|
|||||||
public EditPartViewModel Part { get; set; }
|
public EditPartViewModel Part { get; set; }
|
||||||
|
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
public string DisplayName { get; set; }
|
||||||
public IEnumerable<TemplateViewModel> Templates { get; set; }
|
public IEnumerable<TemplateViewModel> Templates { get; set; }
|
||||||
public EditFieldViewModel FieldDefinition { get; set; }
|
public EditFieldViewModel FieldDefinition { get; set; }
|
||||||
public SettingsDictionary Settings { get; set; }
|
public SettingsDictionary Settings { get; set; }
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
@using Orchard.Utility.Extensions
|
||||||
@model Orchard.ContentTypes.ViewModels.AddFieldViewModel
|
@model Orchard.ContentTypes.ViewModels.AddFieldViewModel
|
||||||
@{
|
@{
|
||||||
Style.Require("ContentTypesAdmin");
|
Style.Require("ContentTypesAdmin");
|
||||||
@@ -8,15 +9,61 @@
|
|||||||
@using (Html.BeginFormAntiForgeryPost()) {
|
@using (Html.BeginFormAntiForgeryPost()) {
|
||||||
@Html.ValidationSummary()
|
@Html.ValidationSummary()
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<label for="DisplayName">@T("Name")</label>
|
<label for="DisplayName">@T("Display Name")</label>
|
||||||
@Html.TextBoxFor(m => m.DisplayName, new {@class = "textMedium", autofocus = "autofocus"})
|
@Html.TextBoxFor(m => m.DisplayName, new {@class = "textMedium", autofocus = "autofocus"})
|
||||||
<span class="hint">@T("Use no spaces or special characters. For example, \"MyCustomField\"")</span>
|
<span class="hint">@T("Name of the field as it will be displayed in screens.")</span>
|
||||||
|
<label for="Name">@T("Technical Name")</label>
|
||||||
|
@Html.TextBoxFor(m => m.Name, new {@class = "text"})
|
||||||
|
<span class="hint">@T("Technical name of the field.")</span>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<label for="FieldTypeName">@T("Field Type")</label>
|
<label for="FieldTypeName">@T("Field Type")</label>
|
||||||
@Html.DropDownListFor(m => m.FieldTypeName, new SelectList(Model.Fields, "FieldTypeName", "FieldTypeName"))
|
@Html.DropDownListFor(m => m.FieldTypeName, new SelectList(Model.Fields.Select(x => new { x.FieldTypeName, DisplayName = x.FieldTypeName.CamelFriendly()}), "FieldTypeName", "DisplayName"))
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<button class="primaryAction" type="submit">@T("Save")</button>
|
<button class="primaryAction" type="submit">@T("Save")</button>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
}
|
||||||
|
|
||||||
|
@using(Script.Foot()){
|
||||||
|
<script type="text/javascript">
|
||||||
|
//<![CDATA[
|
||||||
|
$(function(){
|
||||||
|
var $name = $("#@Html.FieldIdFor(m=>m.Name)");
|
||||||
|
var $displayName = $("#@Html.FieldIdFor(m=>m.DisplayName)");
|
||||||
|
var partName = "@Model.Part.Name";
|
||||||
|
|
||||||
|
var jsonUrl = "@Url.Action("FieldName", "Admin", new RouteValueDictionary { {"Area","Orchard.ContentTypes"} } )";
|
||||||
|
|
||||||
|
var nameAltered;
|
||||||
|
$name.keypress(function() {
|
||||||
|
nameAltered = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
var compute = function() {
|
||||||
|
// stop processing automatically if altered by the user
|
||||||
|
if(nameAltered) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$.post(jsonUrl, {
|
||||||
|
partName: partName,
|
||||||
|
displayName: $displayName.val(),
|
||||||
|
__RequestVerificationToken: $("input[name=__RequestVerificationToken]").val()
|
||||||
|
},
|
||||||
|
function(data) {
|
||||||
|
$name.val(data);
|
||||||
|
},
|
||||||
|
"json"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//pull technical name input from tab order
|
||||||
|
$name.attr("tabindex", -1);
|
||||||
|
|
||||||
|
$displayName.keyup(compute);
|
||||||
|
$displayName.blur(compute);
|
||||||
|
})
|
||||||
|
//]]>
|
||||||
|
</script>
|
||||||
}
|
}
|
@@ -7,8 +7,10 @@
|
|||||||
<fieldset>
|
<fieldset>
|
||||||
<label for="DisplayName">@T("Display Name")</label>
|
<label for="DisplayName">@T("Display Name")</label>
|
||||||
@Html.TextBoxFor(m => m.DisplayName, new {@class = "textMedium", autofocus = "autofocus"})
|
@Html.TextBoxFor(m => m.DisplayName, new {@class = "textMedium", autofocus = "autofocus"})
|
||||||
|
<span class="hint">@T("Name of the type as it will be displayed in screens.")</span>
|
||||||
<label for="Name">@T("Content Type Id")</label>
|
<label for="Name">@T("Content Type Id")</label>
|
||||||
@Html.TextBoxFor(m => m.Name, new {@class = "text"})
|
@Html.TextBoxFor(m => m.Name, new {@class = "text"})
|
||||||
|
<span class="hint">@T("Technical name of the type.")</span>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<button class="primaryAction" type="submit">@T("Create")</button>
|
<button class="primaryAction" type="submit">@T("Create")</button>
|
||||||
@@ -18,24 +20,37 @@
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
//<![CDATA[
|
//<![CDATA[
|
||||||
$(function(){
|
$(function(){
|
||||||
//pull slug input from tab order
|
var $name = $("#@Html.FieldIdFor(m=>m.Name)");
|
||||||
$("#@Html.FieldIdFor(m=>m.Name)").attr("tabindex",-1);
|
var $displayName = $("#@Html.FieldIdFor(m=>m.DisplayName)");
|
||||||
$("#@Html.FieldIdFor(m=>m.DisplayName)").blur(function(){
|
var jsonUrl = "@Url.Action("ContentTypeName", "Admin", new RouteValueDictionary { {"Area","Orchard.ContentTypes"} } )";
|
||||||
var name = $("#@Html.FieldIdFor(m=>m.Name)");
|
|
||||||
if (name.val()) { return true; }
|
var nameAltered;
|
||||||
var displayName = $("#@Html.FieldIdFor(m=>m.DisplayName)").val();
|
$name.keypress(function() {
|
||||||
jQuery.post(
|
nameAltered = true;
|
||||||
"@Url.Action("ContentTypeName","Admin",new RouteValueDictionary{{"Area","Orchard.ContentTypes"}})",
|
});
|
||||||
{
|
|
||||||
displayName: $("#@Html.FieldIdFor(m=>m.DisplayName)").val(),
|
var compute = function() {
|
||||||
|
// stop processing automatically if altered by the user
|
||||||
|
if(nameAltered) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$.post(jsonUrl, {
|
||||||
|
displayName: $displayName.val(),
|
||||||
__RequestVerificationToken: $("input[name=__RequestVerificationToken]").val()
|
__RequestVerificationToken: $("input[name=__RequestVerificationToken]").val()
|
||||||
},
|
},
|
||||||
function(data) {
|
function(data) {
|
||||||
name.val(data);
|
$name.val(data);
|
||||||
},
|
},
|
||||||
"json"
|
"json"
|
||||||
);
|
);
|
||||||
})
|
};
|
||||||
|
|
||||||
|
//pull technical name input from tab order
|
||||||
|
$name.attr("tabindex", -1);
|
||||||
|
|
||||||
|
$displayName.keyup(compute);
|
||||||
|
$displayName.blur(compute);
|
||||||
})
|
})
|
||||||
//]]>
|
//]]>
|
||||||
</script>
|
</script>
|
||||||
|
@@ -0,0 +1,21 @@
|
|||||||
|
@using Orchard.Utility.Extensions
|
||||||
|
@model Orchard.ContentTypes.ViewModels.EditFieldNameViewModel
|
||||||
|
@{
|
||||||
|
Style.Require("ContentTypesAdmin");
|
||||||
|
|
||||||
|
Layout.Title = T("Edit Field \"{0}\"", Model.Name).ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@using (Html.BeginFormAntiForgeryPost()) {
|
||||||
|
@Html.ValidationSummary()
|
||||||
|
<fieldset>
|
||||||
|
<label for="DisplayName">@T("Display Name")</label>
|
||||||
|
@Html.TextBoxFor(m => m.DisplayName, new {@class = "textMedium", autofocus = "autofocus"})
|
||||||
|
<span class="hint">@T("Name of the field as it will be displayed in screens.")</span>
|
||||||
|
|
||||||
|
@Html.HiddenFor(m => m.Name)
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<button class="primaryAction" type="submit" name="submit.Save" value="Save">@T("Save")</button>
|
||||||
|
</fieldset>
|
||||||
|
}
|
@@ -1,7 +1,8 @@
|
|||||||
@model Orchard.ContentTypes.ViewModels.EditPartFieldViewModel
|
@model Orchard.ContentTypes.ViewModels.EditPartFieldViewModel
|
||||||
<fieldset class="manage-field">
|
<fieldset class="manage-field">
|
||||||
<h3>@Model.Name <span>(@Model.FieldDefinition.Name)</span></h3>
|
<h3>@Model.DisplayName <span>(@Model.FieldDefinition.Name)</span></h3>
|
||||||
<div class="manage">
|
<div class="manage">
|
||||||
|
@Html.ActionLink(T("Edit").Text, "EditField", new { area = "Orchard.ContentTypes", id = Model.Part.Name, Model.Name }, new { itemprop = "RemoveUrl UnsafeUrl" }) |
|
||||||
@Html.ActionLink(T("Remove").Text, "RemoveFieldFrom", new { area = "Orchard.ContentTypes", id = Model.Part.Name, Model.Name }, new { itemprop = "RemoveUrl UnsafeUrl" }) @* <- some experimentation *@
|
@Html.ActionLink(T("Remove").Text, "RemoveFieldFrom", new { area = "Orchard.ContentTypes", id = Model.Part.Name, Model.Name }, new { itemprop = "RemoveUrl UnsafeUrl" }) @* <- some experimentation *@
|
||||||
</div>
|
</div>
|
||||||
<div class="details">
|
<div class="details">
|
||||||
|
@@ -4,6 +4,7 @@ using Orchard.ContentManagement.MetaData.Models;
|
|||||||
namespace Orchard.ContentManagement {
|
namespace Orchard.ContentManagement {
|
||||||
public class ContentField {
|
public class ContentField {
|
||||||
public string Name { get { return PartFieldDefinition.Name; } }
|
public string Name { get { return PartFieldDefinition.Name; } }
|
||||||
|
public string DisplayName { get { return PartFieldDefinition.DisplayName; } }
|
||||||
|
|
||||||
public ContentPartFieldDefinition PartFieldDefinition { get; set; }
|
public ContentPartFieldDefinition PartFieldDefinition { get; set; }
|
||||||
public ContentFieldDefinition FieldDefinition { get { return PartFieldDefinition.FieldDefinition; } }
|
public ContentFieldDefinition FieldDefinition { get { return PartFieldDefinition.FieldDefinition; } }
|
||||||
|
@@ -14,6 +14,11 @@ namespace Orchard.ContentManagement.MetaData.Builders {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ContentPartFieldDefinitionBuilder WithDisplayName(string displayName) {
|
||||||
|
_settings[ContentPartFieldDefinition.DisplayNameKey] = displayName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public abstract string Name { get; }
|
public abstract string Name { get; }
|
||||||
public abstract string FieldType { get; }
|
public abstract string FieldType { get; }
|
||||||
|
|
||||||
|
@@ -1,5 +1,9 @@
|
|||||||
namespace Orchard.ContentManagement.MetaData.Models {
|
using Orchard.Utility.Extensions;
|
||||||
|
|
||||||
|
namespace Orchard.ContentManagement.MetaData.Models {
|
||||||
public class ContentPartFieldDefinition {
|
public class ContentPartFieldDefinition {
|
||||||
|
public const string DisplayNameKey = "DisplayName";
|
||||||
|
|
||||||
public ContentPartFieldDefinition(string name) {
|
public ContentPartFieldDefinition(string name) {
|
||||||
Name = name;
|
Name = name;
|
||||||
FieldDefinition = new ContentFieldDefinition(null);
|
FieldDefinition = new ContentFieldDefinition(null);
|
||||||
@@ -12,6 +16,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
public string Name { get; private set; }
|
public string Name { get; private set; }
|
||||||
|
|
||||||
|
public string DisplayName {
|
||||||
|
get {
|
||||||
|
// if none is defined, generate one from the technical name
|
||||||
|
return Settings.ContainsKey(DisplayNameKey) ? Settings[DisplayNameKey] : Name.CamelFriendly();
|
||||||
|
}
|
||||||
|
set { Settings[DisplayNameKey] = value; }
|
||||||
|
}
|
||||||
|
|
||||||
public ContentFieldDefinition FieldDefinition { get; private set; }
|
public ContentFieldDefinition FieldDefinition { get; private set; }
|
||||||
public SettingsDictionary Settings { get; private set; }
|
public SettingsDictionary Settings { get; private set; }
|
||||||
}
|
}
|
||||||
|
@@ -1,16 +1,20 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Orchard.Localization;
|
using Orchard.Localization;
|
||||||
|
|
||||||
namespace Orchard.Utility.Extensions {
|
namespace Orchard.Utility.Extensions {
|
||||||
public static class StringExtensions {
|
public static class StringExtensions {
|
||||||
private static readonly Regex humps = new Regex("(?:^[a-zA-Z][^A-Z]*|[A-Z][^A-Z]*)");
|
private static readonly Regex humps = new Regex("(?:^[a-zA-Z][^A-Z]*|[A-Z][^A-Z]*)");
|
||||||
|
private static readonly Regex safe = new Regex(@"[^_\-a-zA-Z]+");
|
||||||
|
|
||||||
public static string CamelFriendly(this string camel) {
|
public static string CamelFriendly(this string camel) {
|
||||||
if (string.IsNullOrWhiteSpace(camel))
|
if (String.IsNullOrWhiteSpace(camel))
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
var matches = humps.Matches(camel).OfType<Match>().Select(m => m.Value);
|
var matches = humps.Matches(camel).OfType<Match>().Select(m => m.Value).ToArray();
|
||||||
return matches.Any()
|
return matches.Any()
|
||||||
? matches.Aggregate((a, b) => a + " " + b).TrimStart(' ')
|
? matches.Aggregate((a, b) => a + " " + b).TrimStart(' ')
|
||||||
: camel;
|
: camel;
|
||||||
@@ -21,7 +25,7 @@ namespace Orchard.Utility.Extensions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static string Ellipsize(this string text, int characterCount, string ellipsis, bool wordBoundary = false) {
|
public static string Ellipsize(this string text, int characterCount, string ellipsis, bool wordBoundary = false) {
|
||||||
if (string.IsNullOrWhiteSpace(text))
|
if (String.IsNullOrWhiteSpace(text))
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
if (characterCount < 0 || text.Length <= characterCount)
|
if (characterCount < 0 || text.Length <= characterCount)
|
||||||
@@ -37,7 +41,7 @@ namespace Orchard.Utility.Extensions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static string HtmlClassify(this string text) {
|
public static string HtmlClassify(this string text) {
|
||||||
if (string.IsNullOrWhiteSpace(text))
|
if (String.IsNullOrWhiteSpace(text))
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
var friendlier = text.CamelFriendly();
|
var friendlier = text.CamelFriendly();
|
||||||
@@ -45,20 +49,20 @@ namespace Orchard.Utility.Extensions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static LocalizedString OrDefault(this string text, LocalizedString defaultValue) {
|
public static LocalizedString OrDefault(this string text, LocalizedString defaultValue) {
|
||||||
return string.IsNullOrEmpty(text)
|
return String.IsNullOrEmpty(text)
|
||||||
? defaultValue
|
? defaultValue
|
||||||
: new LocalizedString(text);
|
: new LocalizedString(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string RemoveTags(this string html) {
|
public static string RemoveTags(this string html) {
|
||||||
return string.IsNullOrEmpty(html)
|
return String.IsNullOrEmpty(html)
|
||||||
? ""
|
? ""
|
||||||
: Regex.Replace(html, "<[^<>]*>", "", RegexOptions.Singleline);
|
: Regex.Replace(html, "<[^<>]*>", "", RegexOptions.Singleline);
|
||||||
}
|
}
|
||||||
|
|
||||||
// not accounting for only \r (e.g. Apple OS 9 carriage return only new lines)
|
// not accounting for only \r (e.g. Apple OS 9 carriage return only new lines)
|
||||||
public static string ReplaceNewLinesWith(this string text, string replacement) {
|
public static string ReplaceNewLinesWith(this string text, string replacement) {
|
||||||
return string.IsNullOrWhiteSpace(text)
|
return String.IsNullOrWhiteSpace(text)
|
||||||
? ""
|
? ""
|
||||||
: Regex.Replace(text, @"(\r?\n)", replacement, RegexOptions.Singleline);
|
: Regex.Replace(text, @"(\r?\n)", replacement, RegexOptions.Singleline);
|
||||||
}
|
}
|
||||||
@@ -88,5 +92,52 @@ namespace Orchard.Utility.Extensions {
|
|||||||
|
|
||||||
return Regex.IsMatch(segment, @"^[^/?#[\]@""^{}|`<>\s]+$");
|
return Regex.IsMatch(segment, @"^[^/?#[\]@""^{}|`<>\s]+$");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a valid technical name.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Uses a white list set of chars.
|
||||||
|
/// </remarks>
|
||||||
|
public static string ToSafeName(this string name) {
|
||||||
|
if (String.IsNullOrWhiteSpace(name))
|
||||||
|
return String.Empty;
|
||||||
|
|
||||||
|
name = RemoveDiacritics(name);
|
||||||
|
name = safe.Replace(name, String.Empty);
|
||||||
|
name = name.Trim();
|
||||||
|
|
||||||
|
// don't allow non A-Z chars as first letter, as they are not allowed in prefixes
|
||||||
|
while (name.Length > 0 && !IsLetter(name[0])) {
|
||||||
|
name = name.Substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name.Length > 128)
|
||||||
|
name = name.Substring(0, 128);
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the char is a letter between A and Z or not
|
||||||
|
/// </summary>
|
||||||
|
public static bool IsLetter(this char c) {
|
||||||
|
return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static string RemoveDiacritics(string name) {
|
||||||
|
string stFormD = name.Normalize(NormalizationForm.FormD);
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
|
||||||
|
foreach (char t in stFormD) {
|
||||||
|
UnicodeCategory uc = CharUnicodeInfo.GetUnicodeCategory(t);
|
||||||
|
if (uc != UnicodeCategory.NonSpacingMark) {
|
||||||
|
sb.Append(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (sb.ToString().Normalize(NormalizationForm.FormC));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user