From d62aae020594fb7f7356d4f02a7e2e2bcd613d0e Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 29 Oct 2013 18:17:10 +1100 Subject: [PATCH] Fixes property editors that have default pre-values for those prevalues to show up when creating a new data type, fixes: U4-3147 Create tag supported property editors - and adds the tag group to the pre-values for the tag property editor. --- .../PropertyEditors/SupportTagsAttribute.cs | 17 ++++- .../PropertyEditors/TagPropertyDefinition.cs | 51 +++++++++++++ src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../Editors/ContentControllerBase.cs | 4 +- src/Umbraco.Web/Editors/DataTypeController.cs | 16 ++--- src/Umbraco.Web/Editors/TagExtractor.cs | 41 +++++++++-- .../Models/Mapping/DataTypeModelMapper.cs | 17 ++++- .../Models/Mapping/PreValueDisplayResolver.cs | 72 +++++++++++-------- .../TagPropertyEditorTagDefinition.cs | 25 +++++++ .../PropertyEditors/TagsPropertyEditor.cs | 48 ++++++++++++- src/Umbraco.Web/Umbraco.Web.csproj | 1 + 11 files changed, 238 insertions(+), 55 deletions(-) create mode 100644 src/Umbraco.Core/PropertyEditors/TagPropertyDefinition.cs create mode 100644 src/Umbraco.Web/PropertyEditors/TagPropertyEditorTagDefinition.cs diff --git a/src/Umbraco.Core/PropertyEditors/SupportTagsAttribute.cs b/src/Umbraco.Core/PropertyEditors/SupportTagsAttribute.cs index 5ae30fd0ce..334341c2f6 100644 --- a/src/Umbraco.Core/PropertyEditors/SupportTagsAttribute.cs +++ b/src/Umbraco.Core/PropertyEditors/SupportTagsAttribute.cs @@ -8,9 +8,22 @@ namespace Umbraco.Core.PropertyEditors [AttributeUsage(AttributeTargets.Class)] public class SupportTagsAttribute : Attribute { - //TODO: We should be able to add an overload to this to provide a 'tag definition' so developers can dynamically change - // things like TagGroup and ReplaceTags at runtime. + public Type TagPropertyDefinitionType { get; private set; } + /// + /// Defines a tag property definition type to invoke at runtime to get the tags configuration for a property + /// + /// + public SupportTagsAttribute(Type tagPropertyDefinitionType) + : this() + { + if (tagPropertyDefinitionType == null) throw new ArgumentNullException("tagPropertyDefinitionType"); + TagPropertyDefinitionType = tagPropertyDefinitionType; + } + + /// + /// Normal constructor specifying the default tags configuration for a property + /// public SupportTagsAttribute() { ValueType = TagValueType.FromDelimitedValue; diff --git a/src/Umbraco.Core/PropertyEditors/TagPropertyDefinition.cs b/src/Umbraco.Core/PropertyEditors/TagPropertyDefinition.cs new file mode 100644 index 0000000000..a4a8d6dd30 --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/TagPropertyDefinition.cs @@ -0,0 +1,51 @@ +using Umbraco.Core.Models.Editors; + +namespace Umbraco.Core.PropertyEditors +{ + /// + /// Allows for dynamically changing how a property's data is tagged at runtime during property setting + /// + public abstract class TagPropertyDefinition + { + /// + /// The property data that will create the tag data + /// + public ContentPropertyData PropertySaving { get; private set; } + + /// + /// The attribute that has specified this definition type + /// + public SupportTagsAttribute TagsAttribute { get; set; } + + /// + /// Constructor specifies the defaults and sets the ContentPropertyData being used to set the tag values which + /// can be used to dynamically adjust the tags definition for this property. + /// + /// + /// + protected TagPropertyDefinition(ContentPropertyData propertySaving, SupportTagsAttribute tagsAttribute) + { + PropertySaving = propertySaving; + TagsAttribute = tagsAttribute; + Delimiter = tagsAttribute.Delimiter; + ReplaceTags = tagsAttribute.ReplaceTags; + TagGroup = tagsAttribute.TagGroup; + } + + /// + /// Defines a custom delimiter, the default is a comma + /// + public virtual string Delimiter { get; private set; } + + /// + /// Determines whether or not to replace the tags with the new value or append them (true to replace, false to append), default is true + /// + public virtual bool ReplaceTags { get; private set; } + + /// + /// The tag group to use when tagging, the default is 'default' + /// + public virtual string TagGroup { get; private set; } + } + +} \ No newline at end of file diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 0ded0c0d5e..e246fc1a4f 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -334,6 +334,7 @@ + diff --git a/src/Umbraco.Web/Editors/ContentControllerBase.cs b/src/Umbraco.Web/Editors/ContentControllerBase.cs index aff3624015..4d8add3866 100644 --- a/src/Umbraco.Web/Editors/ContentControllerBase.cs +++ b/src/Umbraco.Web/Editors/ContentControllerBase.cs @@ -116,8 +116,8 @@ namespace Umbraco.Web.Editors var propVal = p.PropertyEditor.ValueEditor.ConvertEditorToDb(data, dboProperty.Value); var supportTagsAttribute = TagExtractor.GetAttribute(p.PropertyEditor); if (supportTagsAttribute != null) - { - TagExtractor.SetPropertyTags(contentItem.PersistedContent, dboProperty, propVal, supportTagsAttribute); + { + TagExtractor.SetPropertyTags(contentItem.PersistedContent, dboProperty, data, propVal, supportTagsAttribute); } else { diff --git a/src/Umbraco.Web/Editors/DataTypeController.cs b/src/Umbraco.Web/Editors/DataTypeController.cs index 6919552540..bb6fcab711 100644 --- a/src/Umbraco.Web/Editors/DataTypeController.cs +++ b/src/Umbraco.Web/Editors/DataTypeController.cs @@ -10,6 +10,7 @@ using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Models.Mapping; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi.Binders; using Umbraco.Web.WebApi.Filters; @@ -42,10 +43,6 @@ namespace Umbraco.Web.Editors { throw new HttpResponseException(HttpStatusCode.NotFound); } - - //TODO: Here we need to do a legacy check... if we cannot find a property editor with the alias - // we should display a warning but let them select a different one! - return Mapper.Map(dataType); } @@ -62,8 +59,7 @@ namespace Umbraco.Web.Editors { throw new HttpResponseException(HttpStatusCode.NotFound); } - - + Services.DataTypeService.Delete(foundType, UmbracoUser.Id); return Request.CreateResponse(HttpStatusCode.OK); @@ -91,8 +87,8 @@ namespace Umbraco.Web.Editors if (dataTypeId == -1) { - //this is a new data type, so just return the field editors, there are no values yet - return propEd.PreValueEditor.Fields.Select(Mapper.Map); + //this is a new data type, so just return the field editors with default values + return Mapper.Map>(propEd); } //we have a data type associated @@ -111,8 +107,8 @@ namespace Umbraco.Web.Editors return Mapper.Map>(dataType); } - //return the pre value display without values - return propEd.PreValueEditor.Fields.Select(Mapper.Map); + //these are new pre-values, so just return the field editors with default values + return Mapper.Map>(propEd); } //TODO: Generally there probably won't be file uploads for pre-values but we should allow them just like we do for the content editor diff --git a/src/Umbraco.Web/Editors/TagExtractor.cs b/src/Umbraco.Web/Editors/TagExtractor.cs index ad2a8156d9..e8542ce588 100644 --- a/src/Umbraco.Web/Editors/TagExtractor.cs +++ b/src/Umbraco.Web/Editors/TagExtractor.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using Umbraco.Core; +using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.Models.Editors; using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.Editors @@ -22,22 +24,47 @@ namespace Umbraco.Web.Editors /// /// /// - /// + /// + /// /// - public static void SetPropertyTags(IContentBase content, Property property, object propertyValue, SupportTagsAttribute attribute) + public static void SetPropertyTags(IContentBase content, Property property, ContentPropertyData propertyData, object convertedPropertyValue, SupportTagsAttribute attribute) { - switch (attribute.ValueType) + //check for a custom definition + if (attribute.TagPropertyDefinitionType != null) + { + //try to create it + TagPropertyDefinition def; + try + { + def = (TagPropertyDefinition) Activator.CreateInstance(attribute.TagPropertyDefinitionType, propertyData, attribute); + } + catch (Exception ex) + { + LogHelper.Error("Could not create custom " + attribute.TagPropertyDefinitionType + " tag definition", ex); + throw; + } + SetPropertyTags(content, property, convertedPropertyValue, def.Delimiter, def.ReplaceTags, def.TagGroup, attribute.ValueType); + } + else + { + SetPropertyTags(content, property, convertedPropertyValue, attribute.Delimiter, attribute.ReplaceTags, attribute.TagGroup, attribute.ValueType); + } + } + + public static void SetPropertyTags(IContentBase content, Property property, object convertedPropertyValue, string delimiter, bool replaceTags, string tagGroup, TagValueType valueType) + { + switch (valueType) { case TagValueType.FromDelimitedValue: - var tags = propertyValue.ToString().Split(new[] { attribute.Delimiter }, StringSplitOptions.RemoveEmptyEntries); - content.SetTags(property.Alias, tags, attribute.ReplaceTags, attribute.TagGroup); + var tags = convertedPropertyValue.ToString().Split(new[] { delimiter }, StringSplitOptions.RemoveEmptyEntries); + content.SetTags(property.Alias, tags, replaceTags, tagGroup); break; case TagValueType.CustomTagList: //for this to work the object value must be IENumerable - var stringList = propertyValue as IEnumerable; + var stringList = convertedPropertyValue as IEnumerable; if (stringList != null) { - content.SetTags(property.Alias, stringList, attribute.ReplaceTags, attribute.TagGroup); + content.SetTags(property.Alias, stringList, replaceTags, tagGroup); } break; } diff --git a/src/Umbraco.Web/Models/Mapping/DataTypeModelMapper.cs b/src/Umbraco.Web/Models/Mapping/DataTypeModelMapper.cs index 3b0e941605..77c8201f75 100644 --- a/src/Umbraco.Web/Models/Mapping/DataTypeModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/DataTypeModelMapper.cs @@ -39,8 +39,7 @@ namespace Umbraco.Web.Models.Mapping { var resolver = new PreValueDisplayResolver(lazyDataTypeService); return resolver.Convert(definition); - }); - + }); config.CreateMap() .ConstructUsing(save => new DataTypeDefinition(-1, save.SelectedEditor) {CreateDate = DateTime.Now}) @@ -48,6 +47,20 @@ namespace Umbraco.Web.Models.Mapping .ForMember(definition => definition.PropertyEditorAlias, expression => expression.MapFrom(save => save.SelectedEditor)) .ForMember(definition => definition.ParentId, expression => expression.MapFrom(save => -1)) .ForMember(definition => definition.DatabaseType, expression => expression.ResolveUsing()); + + //Converts a property editor to a new list of pre-value fields - used when creating a new data type or changing a data type with new pre-vals + config.CreateMap>() + .ConvertUsing(editor => + { + //this is a new data type, so just return the field editors, there are no values yet + var defaultVals = editor.DefaultPreValues; + var fields = editor.PreValueEditor.Fields.Select(Mapper.Map).ToArray(); + if (defaultVals != null) + { + PreValueDisplayResolver.MapPreValueValuesToPreValueFields(fields, defaultVals, true); + } + return fields; + }); } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/PreValueDisplayResolver.cs b/src/Umbraco.Web/Models/Mapping/PreValueDisplayResolver.cs index 8af766ae5c..a7bf89a1d2 100644 --- a/src/Umbraco.Web/Models/Mapping/PreValueDisplayResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/PreValueDisplayResolver.cs @@ -20,6 +20,47 @@ namespace Umbraco.Web.Models.Mapping _dataTypeService = dataTypeService; } + /// + /// Maps pre-values in the dictionary to the values for the fields + /// + /// + /// + /// + internal static void MapPreValueValuesToPreValueFields(PreValueFieldDisplay[] fields, IDictionary preValues, bool isDictionaryBased) + { + if (fields == null) throw new ArgumentNullException("fields"); + if (preValues == null) throw new ArgumentNullException("preValues"); + //now we need to wire up the pre-values values with the actual fields defined + var currentIndex = 0; //used if the collection is non-dictionary based. + foreach (var field in fields) + { + if (isDictionaryBased == false) + { + //we'll just need to wire up the values based on the order that the pre-values are stored + var found = preValues.Any(x => x.Key.InvariantEquals(currentIndex.ToInvariantString())); + if (found == false) + { + LogHelper.Warn("Could not find persisted pre-value for index " + currentIndex); + continue; + } + field.Value = preValues.Single(x => x.Key.InvariantEquals(currentIndex.ToInvariantString())).Value.ToString(); + currentIndex++; + } + else + { + var found = preValues.Any(x => x.Key.InvariantEquals(field.Key)); + if (found == false) + { + LogHelper.Warn("Could not find persisted pre-value for field " + field.Key); + continue; + } + field.Value = preValues.Single(x => x.Key.InvariantEquals(field.Key)).Value; + } + + + } + } + internal IEnumerable Convert(IDataTypeDefinition source) { PropertyEditor propEd = null; @@ -45,36 +86,7 @@ namespace Umbraco.Web.Models.Mapping dictionaryVals = propEd.PreValueEditor.ConvertDbToEditor(propEd.DefaultPreValues, preVals); } - var currentIndex = 0; //used if the collection is non-dictionary based. - - //now we need to wire up the pre-values values with the actual fields defined - foreach (var field in result) - { - if (preVals.IsDictionaryBased == false) - { - //we'll just need to wire up the values based on the order that the pre-values are stored - var found = dictionaryVals.Any(x => x.Key.InvariantEquals(currentIndex.ToInvariantString())); - if (found == false) - { - LogHelper.Warn("Could not find persisted pre-value for index " + currentIndex); - continue; - } - field.Value = dictionaryVals.Single(x => x.Key.InvariantEquals(currentIndex.ToInvariantString())).Value.ToString(); - currentIndex++; - } - else - { - var found = dictionaryVals.Any(x => x.Key.InvariantEquals(field.Key)); - if (found == false) - { - LogHelper.Warn("Could not find persisted pre-value for field " + field.Key); - continue; - } - field.Value = dictionaryVals.Single(x => x.Key.InvariantEquals(field.Key)).Value; - } - - - } + MapPreValueValuesToPreValueFields(result, dictionaryVals, preVals.IsDictionaryBased); return result; } diff --git a/src/Umbraco.Web/PropertyEditors/TagPropertyEditorTagDefinition.cs b/src/Umbraco.Web/PropertyEditors/TagPropertyEditorTagDefinition.cs new file mode 100644 index 0000000000..31ceeb3d60 --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/TagPropertyEditorTagDefinition.cs @@ -0,0 +1,25 @@ +using Umbraco.Core.Models.Editors; +using Umbraco.Core.PropertyEditors; + +namespace Umbraco.Web.PropertyEditors +{ + /// + /// Used to dynamically change the tag group based on the pre-values + /// + internal class TagPropertyEditorTagDefinition : TagPropertyDefinition + { + public TagPropertyEditorTagDefinition(ContentPropertyData propertySaving, SupportTagsAttribute tagsAttribute) + : base(propertySaving, tagsAttribute) + { + } + + public override string TagGroup + { + get + { + var preVals = PropertySaving.PreValues.FormatAsDictionary(); + return preVals.ContainsKey("group") ? preVals["group"].Value : "default"; + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/PropertyEditors/TagsPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/TagsPropertyEditor.cs index 35b6afb8cc..59115ad263 100644 --- a/src/Umbraco.Web/PropertyEditors/TagsPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/TagsPropertyEditor.cs @@ -1,11 +1,55 @@ -using Umbraco.Core; +using System.Collections.Generic; +using Umbraco.Core; using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors { - [SupportTags] + [SupportTags(typeof(TagPropertyEditorTagDefinition))] [PropertyEditor(Constants.PropertyEditors.TagsAlias, "Tags", "tags")] public class TagsPropertyEditor : PropertyEditor { + public TagsPropertyEditor() + { + _defaultPreVals = new Dictionary + { + {"group", "default"} + }; + } + + private IDictionary _defaultPreVals; + + /// + /// Override to supply the default group + /// + public override IDictionary DefaultPreValues + { + get { return _defaultPreVals; } + set { _defaultPreVals = value; } + } + + protected override PreValueEditor CreatePreValueEditor() + { + return new TagPreValueEditor(); + } + + internal class TagPreValueEditor : PreValueEditor + { + public TagPreValueEditor() + { + Fields.Add(new PreValueField(new ManifestPropertyValidator { Type = "Required" }) + { + Description = "Define a tag group", + Key = "group", + Name = "Tag group", + View = "requiredfield" + }); + } + + public override IDictionary ConvertDbToEditor(IDictionary defaultPreVals, Core.Models.PreValueCollection persistedPreVals) + { + var result = base.ConvertDbToEditor(defaultPreVals, persistedPreVals); + return result; + } + } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index a4910ac271..44606d2f0d 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -384,6 +384,7 @@ +