public void Clear_Cache_Removes_Specific_Editors() { var sut = new ValueEditorCache(); var dataEditor1 = new FakeDataEditor("Editor 1"); var dataEditor2 = new FakeDataEditor("Editor 2"); IDataType dataType1 = CreateDataTypeMock(1).Object; IDataType dataType2 = CreateDataTypeMock(2).Object; // Load the editors into cache IDataValueEditor editor1DataType1 = sut.GetValueEditor(dataEditor1, dataType1); IDataValueEditor editor1Datatype2 = sut.GetValueEditor(dataEditor1, dataType2); IDataValueEditor editor2DataType1 = sut.GetValueEditor(dataEditor2, dataType1); IDataValueEditor editor2Datatype2 = sut.GetValueEditor(dataEditor2, dataType2); sut.ClearCache(new [] { dataType1.Id }); // New value editor objects should be created after it's cleared Assert.AreNotSame(editor1DataType1, sut.GetValueEditor(dataEditor1, dataType1), "Value editor was not cleared from cache"); Assert.AreNotSame(editor2DataType1, sut.GetValueEditor(dataEditor2, dataType1), "Value editor was not cleared from cache"); // But the value editors for data type 2 should be the same Assert.AreSame(editor1Datatype2, sut.GetValueEditor(dataEditor1, dataType2), "Too many editors was cleared from cache"); Assert.AreSame(editor2Datatype2, sut.GetValueEditor(dataEditor2, dataType2), "Too many editors was cleared from cache"); }
/// <inheritdoc /> protected override IDataValueEditor CreateValueEditor() { IDataValueEditor editor = base.CreateValueEditor(); editor.Validators.Add(new DateTimeValidator()); return(editor); }
/// <inheritdoc /> protected override IDataValueEditor CreateValueEditor() { IDataValueEditor editor = base.CreateValueEditor(); editor.Validators.Add(new IntegerValidator()); // ensure the value is validated return(editor); }
public override IDataValueEditor GetValueEditor(object configuration) { IDataValueEditor editor = base.GetValueEditor(configuration); if (editor is DataValueEditor dve) { dve.View += $"?v={_backOfficeHelper.GetCacheBuster()}"; } return(editor); }
public void Different_Data_Types_Returns_Different_Value_Editors() { var sut = new ValueEditorCache(); var dataEditor = new FakeDataEditor("Editor"); IDataType dataType1 = CreateDataTypeMock(1).Object; IDataType dataType2 = CreateDataTypeMock(2).Object; IDataValueEditor firstEditor = sut.GetValueEditor(dataEditor, dataType1); IDataValueEditor secondEditor = sut.GetValueEditor(dataEditor, dataType2); Assert.AreNotSame(firstEditor, secondEditor); }
public void DropDownMultipleValueEditor_Format_Data_For_Cache() { var dataValueEditorFactoryMock = new Mock <IDataValueEditorFactory>(); var serializer = new ConfigurationEditorJsonSerializer(); var checkBoxListPropertyEditor = new CheckBoxListPropertyEditor(dataValueEditorFactoryMock.Object, Mock.Of <ILocalizedTextService>(), Mock.Of <IIOHelper>(), Mock.Of <IEditorConfigurationParser>()); var dataType = new DataType(checkBoxListPropertyEditor, serializer) { Configuration = new ValueListConfiguration { Items = new List <ValueListConfiguration.ValueListItem> { new ValueListConfiguration.ValueListItem { Id = 4567, Value = "Value 1" }, new ValueListConfiguration.ValueListItem { Id = 1234, Value = "Value 2" }, new ValueListConfiguration.ValueListItem { Id = 8910, Value = "Value 3" } } }, Id = 1 }; var dataTypeServiceMock = new Mock <IDataTypeService>(); dataTypeServiceMock .Setup(x => x.GetDataType(It.IsAny <int>())) .Returns(dataType); //TODO use builders instead of this mess var multipleValueEditor = new MultipleValueEditor(Mock.Of <ILocalizedTextService>(), Mock.Of <IShortStringHelper>(), Mock.Of <IJsonSerializer>(), Mock.Of <IIOHelper>(), new DataEditorAttribute(Constants.PropertyEditors.Aliases.TextBox, "Test Textbox", "textbox")); dataValueEditorFactoryMock .Setup(x => x.Create <MultipleValueEditor>(It.IsAny <DataEditorAttribute>())) .Returns(multipleValueEditor); var prop = new Property(1, new PropertyType(Mock.Of <IShortStringHelper>(), dataType)); prop.SetValue("Value 1,Value 2,Value 3"); IDataValueEditor valueEditor = dataType.Editor.GetValueEditor(); ((DataValueEditor)valueEditor).Configuration = dataType.Configuration; var result = valueEditor.ConvertDbToString(prop.PropertyType, prop.GetValue()); Assert.AreEqual("Value 1,Value 2,Value 3", result); }
public override IDataValueEditor GetValueEditor(object configuration) { IDataValueEditor editor = base.GetValueEditor(configuration); if (editor is DataValueEditor dve) { dve.View += $"?v={_backOfficeHelper.GetCacheBuster()}"; dve.HideLabel = configuration is InboundRedirectsConfiguration { HideLabel : true }; } return(editor); } }
/// <summary> /// Determines whether a value is valid for this property type. /// </summary> private bool IsPropertyValueValid(IPropertyType propertyType, object?value) { IDataEditor?editor = _propertyEditors[propertyType.PropertyEditorAlias]; if (editor == null) { // nothing much we can do validation wise if the property editor has been removed. // the property will be displayed as a label, so flagging it as invalid would be pointless. return(true); } var configuration = _dataTypeService.GetDataType(propertyType.DataTypeId)?.Configuration; IDataValueEditor valueEditor = editor.GetValueEditor(configuration); return(!valueEditor.Validate(value, propertyType.Mandatory, propertyType.ValidationRegExp).Any()); }
public void CanParseManifest_ParameterEditors() { const string json = @"{'parameterEditors': [ { alias: 'parameter1', name: 'My Parameter', view: '~/App_Plugins/MyPackage/PropertyEditors/MyEditor.html' }, { alias: 'parameter2', name: 'Another parameter', config: { key1: 'some config val' }, view: '~/App_Plugins/MyPackage/PropertyEditors/CsvEditor.html' }, { alias: 'parameter3', name: 'Yet another parameter' } ]}"; PackageManifest manifest = _parser.ParseManifest(json); Assert.AreEqual(3, manifest.ParameterEditors.Length); Assert.IsTrue(manifest.ParameterEditors.All(x => (x.Type & EditorType.MacroParameter) > 0)); IDataEditor editor = manifest.ParameterEditors[1]; Assert.AreEqual("parameter2", editor.Alias); Assert.AreEqual("Another parameter", editor.Name); IDictionary <string, object> config = editor.DefaultConfiguration; Assert.AreEqual(1, config.Count); Assert.IsTrue(config.ContainsKey("key1")); Assert.AreEqual("some config val", config["key1"]); IDataValueEditor valueEditor = editor.GetValueEditor(); Assert.AreEqual(_ioHelper.ResolveUrl("/App_Plugins/MyPackage/PropertyEditors/CsvEditor.html"), valueEditor.View); editor = manifest.ParameterEditors[2]; Assert.Throws <InvalidOperationException>(() => { IDataValueEditor valueEditor = editor.GetValueEditor(); }); }
public void Caches_ValueEditor() { var sut = new ValueEditorCache(); var dataEditor = new FakeDataEditor("TestEditor"); IDataType dataType = CreateDataTypeMock(1).Object; // Request the same value editor twice IDataValueEditor firstEditor = sut.GetValueEditor(dataEditor, dataType); IDataValueEditor secondEditor = sut.GetValueEditor(dataEditor, dataType); Assert.Multiple(() => { Assert.AreSame(firstEditor, secondEditor); Assert.AreEqual(1, dataEditor.ValueEditorCount, "GetValueEditor invoked more than once."); }); }
public ElementsPropertyType(PropertyType propertyType, IDataType dataType) { IDataValueEditor valueEditor = dataType.Editor.GetValueEditor(dataType.Configuration); DataType = new ElementsDataType(dataType); Alias = propertyType.Alias; Label = propertyType.Name; Description = propertyType.Description; View = valueEditor.View; Config = DataType.Config; HideLabel = valueEditor.HideLabel; Validation = new ElementsValidation(propertyType); IsReadOnly = valueEditor.IsReadOnly; DataTypeKey = propertyType.DataTypeKey; Editor = dataType.EditorAlias; }
public override IDataEditor Build() { var name = _name ?? Guid.NewGuid().ToString(); var alias = _alias ?? name.ToCamelCase(); IDictionary <string, object> defaultConfiguration = _defaultConfiguration ?? new Dictionary <string, object>(); IConfigurationEditor explicitConfigurationEditor = _explicitConfigurationEditorBuilder.Build(); IDataValueEditor explicitValueEditor = _explicitValueEditorBuilder.Build(); return(new DataEditor( Mock.Of <IDataValueEditorFactory>()) { Alias = alias, Name = name, DefaultConfiguration = defaultConfiguration, ExplicitConfigurationEditor = explicitConfigurationEditor, ExplicitValueEditor = explicitValueEditor }); }
/// <inheritdoc /> public IEnumerable <ValidationResult> ValidatePropertyValue( IDataEditor editor, IDataType dataType, object?postedValue, bool isRequired, string?validationRegExp, string?isRequiredMessage, string?validationRegExpMessage) { // Retrieve default messages used for required and regex validatation. We'll replace these // if set with custom ones if they've been provided for a given property. var requiredDefaultMessages = new[] { _textService.Localize("validation", "invalidNull"), _textService.Localize("validation", "invalidEmpty") }; var formatDefaultMessages = new[] { _textService.Localize("validation", "invalidPattern"), }; IDataValueEditor valueEditor = _valueEditorCache.GetValueEditor(editor, dataType); foreach (var validationResult in valueEditor.Validate(postedValue, isRequired, validationRegExp)) { // If we've got custom error messages, we'll replace the default ones that will have been applied in the call to Validate(). if (isRequired && !string.IsNullOrWhiteSpace(isRequiredMessage) && requiredDefaultMessages.Contains(validationResult.ErrorMessage, StringComparer.OrdinalIgnoreCase)) { validationResult.ErrorMessage = isRequiredMessage; } if (!string.IsNullOrWhiteSpace(validationRegExp) && !string.IsNullOrWhiteSpace(validationRegExpMessage) && formatDefaultMessages.Contains(validationResult.ErrorMessage, StringComparer.OrdinalIgnoreCase)) { validationResult.ErrorMessage = validationRegExpMessage; } yield return(validationResult); } }
public void CanParseManifest_PropertyEditors() { const string json = @"{'propertyEditors': [ { alias: 'Test.Test1', name: 'Test 1', editor: { view: '~/App_Plugins/MyPackage/PropertyEditors/MyEditor.html', valueType: 'int', hideLabel: true, validation: { 'required': true, 'Regex': '\\d*' } }, prevalues: { fields: [ { label: 'Some config 1', key: 'key1', view: '~/App_Plugins/MyPackage/PropertyEditors/Views/pre-val1.html', validation: { required: true } }, { label: 'Some config 2', key: 'key2', view: '~/App_Plugins/MyPackage/PropertyEditors/Views/pre-val2.html' } ] } }, { alias: 'Test.Test2', name: 'Test 2', isParameterEditor: true, defaultConfig: { key1: 'some default val' }, editor: { view: '~/App_Plugins/MyPackage/PropertyEditors/MyEditor.html', valueType: 'int', validation: { required : true, regex : '\\d*' } } } ]}"; PackageManifest manifest = _parser.ParseManifest(json); Assert.AreEqual(2, manifest.PropertyEditors.Length); IDataEditor editor = manifest.PropertyEditors[1]; Assert.IsTrue((editor.Type & EditorType.MacroParameter) > 0); Assert.IsNotEmpty(editor.DefaultConfiguration); Assert.AreEqual("some default val", editor.DefaultConfiguration["key1"]); editor = manifest.PropertyEditors[0]; Assert.AreEqual("Test.Test1", editor.Alias); Assert.AreEqual("Test 1", editor.Name); Assert.IsFalse((editor.Type & EditorType.MacroParameter) > 0); IDataValueEditor valueEditor = editor.GetValueEditor(); Assert.AreEqual(_ioHelper.ResolveUrl("/App_Plugins/MyPackage/PropertyEditors/MyEditor.html"), valueEditor.View); Assert.AreEqual("int", valueEditor.ValueType); Assert.IsTrue(valueEditor.HideLabel); // these two don't make much sense here //// valueEditor.RegexValidator; //// valueEditor.RequiredValidator; List <IValueValidator> validators = valueEditor.Validators; Assert.AreEqual(2, validators.Count); IValueValidator validator = validators[0]; var v1 = validator as RequiredValidator; Assert.IsNotNull(v1); Assert.AreEqual("Required", v1.ValidationName); validator = validators[1]; var v2 = validator as RegexValidator; Assert.IsNotNull(v2); Assert.AreEqual("Regex", v2.ValidationName); Assert.AreEqual("\\d*", v2.Configuration); // this is not part of the manifest IDictionary <string, object> preValues = editor.GetConfigurationEditor().DefaultConfiguration; Assert.IsEmpty(preValues); IConfigurationEditor preValueEditor = editor.GetConfigurationEditor(); Assert.IsNotNull(preValueEditor); Assert.IsNotNull(preValueEditor.Fields); Assert.AreEqual(2, preValueEditor.Fields.Count); ConfigurationField f = preValueEditor.Fields[0]; Assert.AreEqual("key1", f.Key); Assert.AreEqual("Some config 1", f.Name); Assert.AreEqual(_ioHelper.ResolveUrl("/App_Plugins/MyPackage/PropertyEditors/Views/pre-val1.html"), f.View); List <IValueValidator> fvalidators = f.Validators; Assert.IsNotNull(fvalidators); Assert.AreEqual(1, fvalidators.Count); var fv = fvalidators[0] as RequiredValidator; Assert.IsNotNull(fv); Assert.AreEqual("Required", fv.ValidationName); f = preValueEditor.Fields[1]; Assert.AreEqual("key2", f.Key); Assert.AreEqual("Some config 2", f.Name); Assert.AreEqual(_ioHelper.ResolveUrl("/App_Plugins/MyPackage/PropertyEditors/Views/pre-val2.html"), f.View); fvalidators = f.Validators; Assert.IsNotNull(fvalidators); Assert.AreEqual(0, fvalidators.Count); }
/// <inheritdoc /> /// <remarks> /// <para>If an explicit value editor has been assigned, then this explicit /// instance is returned. Otherwise, a new instance is created by CreateValueEditor.</para> /// <para>The instance created by CreateValueEditor is not cached, i.e. /// a new instance is created each time the property value is retrieved. The /// property editor is a singleton, and the value editor cannot be a singleton /// since it depends on the datatype configuration.</para> /// <para>Technically, it could be cached by datatype but let's keep things /// simple enough for now.</para> /// </remarks> // TODO: point of that one? shouldn't we always configure? public IDataValueEditor GetValueEditor() => ExplicitValueEditor ?? (_reusableEditor ?? (_reusableEditor = CreateValueEditor()));
/// <summary> /// Maps the dto property values to the persisted model /// </summary> internal void MapPropertyValuesForPersistence <TPersisted, TSaved>( TSaved contentItem, ContentPropertyCollectionDto?dto, Func <TSaved, IProperty?, object?> getPropertyValue, Action <TSaved, IProperty?, object?> savePropertyValue, string?culture) where TPersisted : IContentBase where TSaved : IContentSave <TPersisted> { if (dto is null) { return; } // map the property values foreach (ContentPropertyDto propertyDto in dto.Properties) { // get the property editor if (propertyDto.PropertyEditor == null) { _logger.LogWarning("No property editor found for property {PropertyAlias}", propertyDto.Alias); continue; } // get the value editor // nothing to save/map if it is readonly IDataValueEditor valueEditor = propertyDto.PropertyEditor.GetValueEditor(); if (valueEditor.IsReadOnly) { continue; } // get the property IProperty property = contentItem.PersistedContent.Properties[propertyDto.Alias] !; // prepare files, if any matching property and culture ContentPropertyFile[] files = contentItem.UploadedFiles .Where(x => x.PropertyAlias == propertyDto.Alias && x.Culture == propertyDto.Culture && x.Segment == propertyDto.Segment) .ToArray(); foreach (ContentPropertyFile file in files) { file.FileName = file.FileName?.ToSafeFileName(ShortStringHelper); } // create the property data for the property editor var data = new ContentPropertyData(propertyDto.Value, propertyDto.DataType?.Configuration) { ContentKey = contentItem.PersistedContent !.Key, PropertyTypeKey = property.PropertyType.Key, Files = files }; // let the editor convert the value that was received, deal with files, etc object?value = valueEditor.FromEditor(data, getPropertyValue(contentItem, property)); // set the value - tags are special TagsPropertyEditorAttribute?tagAttribute = propertyDto.PropertyEditor.GetTagAttribute(); if (tagAttribute != null) { TagConfiguration?tagConfiguration = ConfigurationEditor.ConfigurationAs <TagConfiguration>(propertyDto.DataType?.Configuration); if (tagConfiguration is not null && tagConfiguration.Delimiter == default) { tagConfiguration.Delimiter = tagAttribute.Delimiter; } var tagCulture = property?.PropertyType.VariesByCulture() ?? false ? culture : null; property?.SetTagsValue(_serializer, value, tagConfiguration, tagCulture); } else { savePropertyValue(contentItem, property, value); } } }
public TextCountDataValueEditor(DataEditorAttribute attribute, IDataValueEditor wrapped, IDataTypeService dataTypeService) : base(attribute) { _wrapped = wrapped; _dataTypeService = dataTypeService; }