From e00bf0be82b4f1de5eb3ea58870af4efdbc9f3d5 Mon Sep 17 00:00:00 2001 From: ZioTino Date: Mon, 24 Jan 2022 15:16:12 +0100 Subject: [PATCH] v1.1.0: Added UmbracoIFormFileExtensions and UmbracoMaxFileSize attributes, as suggested in #1 --- docs/README.md | 46 ++++++++++--- .../Scripts/jquery.validation.custom.js | 51 +++++++++++++- src/Our.Umbraco.ValidationAttributes.csproj | 4 +- src/UmbracoIFormFileExtensionsAttribute.cs | 67 +++++++++++++++++++ src/UmbracoMaxFileSizeAttribute.cs | 57 ++++++++++++++++ 5 files changed, 212 insertions(+), 13 deletions(-) create mode 100644 src/UmbracoIFormFileExtensionsAttribute.cs create mode 100644 src/UmbracoMaxFileSizeAttribute.cs diff --git a/docs/README.md b/docs/README.md index 2edc7f0..f4a7841 100644 --- a/docs/README.md +++ b/docs/README.md @@ -29,7 +29,7 @@ Include the following scripts in your layout.cshtml file, or in your master page ``` The above is just a sample, you may use any method you like to include the scripts. -**NOTE: *jquery.validation.custom.js* is required to ensure that the UmbracoMustBeTrue attribute is working.** +**NOTE: *jquery.validation.custom.js* is required to ensure that UmbracoIFormFileExtensions, UmbracoMaxFileSize and UmbracoMustBeTrue attributes are working correctly.** **As an alternative you can include yourself its content with any method you like.** The end result for a page with validation could look like: @@ -70,6 +70,8 @@ Decorate your properties with the following attributes * UmbracoCompare * UmbracoDisplayName * UmbracoEmailAddress + * UmbracoIFormFileExtensions + * UmbracoMaxFileSize * UmbracoMaxLength * UmbracoMinLength * UmbracoMustBeTrue @@ -85,7 +87,7 @@ Decorate your properties with the following attributes public string MyProperty { get; set; } ``` -### UmbracoCompareAttribute +### UmbracoCompare | Umbraco Dictionary Key | Default | | -- | -- | @@ -109,7 +111,7 @@ public string ConfirmPassword { get; set; } | Key | Default | | -- | -- | -| Provied key | Must be created by your self. | +| Provied key | Must be created by yourself. | Example: ```C# @@ -122,7 +124,7 @@ public string Username { get; set; } | Key | Default | | -- | -- | -| EmailError | Must be created by your self. | +| EmailError | Must be created by yourself. | Example: ```C# @@ -130,11 +132,35 @@ Example: public string Email { get; set; } ``` +### UmbracoIFormFileExtensions + +| Key | Default | +| -- | -- | +| FormFileExtensionsError | Must be created by yourself. | + +Example: +```C# +[UmbracoIFormFileExtensions("jpeg,png,jpg")] // List of comma-separated file extensions +public IFormFile UmbracoIFormFileExtensions { get; set; } +``` + +### UmbracoMaxFileSize + +| Key | Default | +| -- | -- | +| MaxFileSizeError | Must be created by yourself. | + +Example: +```C# +[UmbracoMaxFileSize(5 * 1024 * 1024)] // Max size in bytes +public IFormFile UmbracoMaxFileSize { get; set; } +``` + ### UmbracoMinLength | Key | Default | | -- | -- | -| MinLengthError | Must be created by your self. | +| MinLengthError | Must be created by yourself. | Example: ```C# @@ -146,7 +172,7 @@ public string MyProperty { get; set; } | Key | Default | | -- | -- | -| MaxLengthError | Must be created by your self. | +| MaxLengthError | Must be created by yourself. | Example: ```C# @@ -158,7 +184,7 @@ public string MyProperty { get; set; } | Key | Default | -- | -- | -| MinMaxLengthError | Must be created by your self. | +| MinMaxLengthError | Must be created by yourself. | Examples: ```C# @@ -172,7 +198,7 @@ public string Message { get; set; } ### UmbracoMustBeTrue | Key | Default | | -- | -- | -| MustBeTrueError | Must be created by your self. | +| MustBeTrueError | Must be created by yourself. | Example: ```C# @@ -199,11 +225,11 @@ public string MyProperty { get; set; } ``` ## Custom dictionary keys -Each Attribute, has a public property `DictionaryKey` which can be set like this: +Each Attribute has a public property `DictionaryKey` which can be set like this: ```C# [UmbracoRequired(DictionaryKey = "MyCustomKey")] public string MyProperty { get; set; } ``` Not setting a custom key, will fallback to the default dictionary key. -**You have to create Dictionary Keys manually, taking example from this documentation.** \ No newline at end of file +**You have to create Dictionary Keys manually, as explained in this documentation.** \ No newline at end of file diff --git a/src/App_Plugins/Our.Umbraco.ValidationAttributes/Scripts/jquery.validation.custom.js b/src/App_Plugins/Our.Umbraco.ValidationAttributes/Scripts/jquery.validation.custom.js index da94558..3d5d558 100644 --- a/src/App_Plugins/Our.Umbraco.ValidationAttributes/Scripts/jquery.validation.custom.js +++ b/src/App_Plugins/Our.Umbraco.ValidationAttributes/Scripts/jquery.validation.custom.js @@ -1,2 +1,51 @@ // Requires jquery, jquery-validation and jquery-validation-unobtrusive to work. -$.validator.unobtrusive.adapters.addBool("mustbetrue", "required"); \ No newline at end of file + +// Validator for UmbracoMustBeTrueAttribute +$.validator.unobtrusive.adapters.addBool("mustbetrue", "required"); + +// Validators for UmbracoMaxFileSizeAttribute +$.validator.addMethod("maxfilesize", function (value, element, param) { + if (value === "") { + return true; + } + var maxBytes = parseInt(param); + if (element.files !== undefined && element.files[0] !== undefined && element.files[0].size !== undefined) { + var filesize = parseInt(element.files[0].size); + return filesize <= maxBytes; + } + return true; +}); +$.validator.unobtrusive.adapters.add('maxfilesize', ['size'], function (options) { + options.rules['maxfilesize'] = options.params.size; + if (options.message) { + options.messages['maxfilesize'] = options.message; + } +}); + +// Validators for UmbracoIFormFileExtensionsAttribute +$.validator.addMethod("filetypes", function(value, element, param) { + if (value === "") { + return true; + } + var validFileTypes = []; + if (param.indexOf(',') > -1) { + validFileTypes = param.split(','); + } else { + validFileTypes = [param]; + } + var currentFileType = value.split('.')[value.split('.').length - 1]; + for (var i = 0; i < validFileTypes.length; i++) + { + if (validFileTypes[i] === currentFileType) + { + return true; + } + } + return false; +}); +$.validator.unobtrusive.adapters.add('filetypes', ['types'], function(options) { + options.rules['filetypes'] = options.params.types; + if (options.message) { + options.messages['filetypes'] = options.message; + } +}); \ No newline at end of file diff --git a/src/Our.Umbraco.ValidationAttributes.csproj b/src/Our.Umbraco.ValidationAttributes.csproj index 23af7cc..9607b53 100644 --- a/src/Our.Umbraco.ValidationAttributes.csproj +++ b/src/Our.Umbraco.ValidationAttributes.csproj @@ -7,7 +7,7 @@ Umbraco.ValidationAttributes [Umbraco v9] Contains validation attributes to decorate your classes, but using Umbraco Dictionary as the resource. umbraco plugin package - 1.0.2 + 1.1.0 ZioTino Our.Umbraco.ValidationAttributes LICENSE.md @@ -24,7 +24,7 @@ - + diff --git a/src/UmbracoIFormFileExtensionsAttribute.cs b/src/UmbracoIFormFileExtensionsAttribute.cs new file mode 100644 index 0000000..02b7adf --- /dev/null +++ b/src/UmbracoIFormFileExtensionsAttribute.cs @@ -0,0 +1,67 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using J2N.Collections.Generic; +using Lucene.Net.Analysis.Hunspell; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; +using Our.Umbraco.ValidationAttributes.Helpers; +using Our.Umbraco.ValidationAttributes.Interfaces; +using Our.Umbraco.ValidationAttributes.Services; + +namespace Our.Umbraco.ValidationAttributes +{ + [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)] + public sealed class UmbracoIFormFileExtensionsAttribute : ValidationAttribute, IClientModelValidator, IUmbracoValidationAttribute + { + public string DictionaryKey {get; set;} = "FormFileExtensionsError"; + + public string[] ValidFileTypes { get; set; } + + public UmbracoIFormFileExtensionsAttribute(string fileTypes) + { + ValidFileTypes = ParseFileTypes(fileTypes); + } + + public UmbracoIFormFileExtensionsAttribute(string fileTypes, string dictionaryKey) + { + DictionaryKey = dictionaryKey; + ValidFileTypes = ParseFileTypes(fileTypes); + } + + public void AddValidation(ClientModelValidationContext context) + { + ErrorMessage = ValidationAttributesService.DictionaryValue(DictionaryKey); + ErrorMessage = FormatErrorMessage(string.Join(", ", ValidFileTypes)); + AttributeHelper.MergeAttribute(context.Attributes, "data-val", "true"); + AttributeHelper.MergeAttribute(context.Attributes, "data-val-filetypes", ErrorMessage); + AttributeHelper.MergeAttribute(context.Attributes, "data-val-filetypes-types", string.Join(',', ValidFileTypes)); + + // input type="file" accept attribute + List validExtensions = new List(); + foreach (string type in ValidFileTypes) + { + validExtensions.Add($".{type}"); + } + AttributeHelper.MergeAttribute(context.Attributes, "accept", string.Join(',', validExtensions)); + } + + public override bool IsValid(object value) + { + IFormFile file = value as IFormFile; + bool isValid = true; + + if (file != null) + { + isValid = ValidFileTypes.Any(x => file.FileName.EndsWith(x)); + } + + return isValid; + } + + private string[] ParseFileTypes(string fileTypes) + { + return fileTypes.ToLower().Split(','); + } + } +} \ No newline at end of file diff --git a/src/UmbracoMaxFileSizeAttribute.cs b/src/UmbracoMaxFileSizeAttribute.cs new file mode 100644 index 0000000..6b62608 --- /dev/null +++ b/src/UmbracoMaxFileSizeAttribute.cs @@ -0,0 +1,57 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Lucene.Net.Analysis.Hunspell; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; +using Our.Umbraco.ValidationAttributes.Helpers; +using Our.Umbraco.ValidationAttributes.Interfaces; +using Our.Umbraco.ValidationAttributes.Services; + +namespace Our.Umbraco.ValidationAttributes +{ + [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)] + public sealed class UmbracoMaxFileSizeAttribute : ValidationAttribute, IClientModelValidator, IUmbracoValidationAttribute + { + public string DictionaryKey { get; set; } = "MaxFileSizeError"; + + public int MaxFileSize { get; set; } + + public UmbracoMaxFileSizeAttribute(int maxFileSize) + { + MaxFileSize = maxFileSize; + } + + public UmbracoMaxFileSizeAttribute(int maxFileSize, string dictionaryKey) + { + DictionaryKey = dictionaryKey; + MaxFileSize = maxFileSize; + } + + public void AddValidation(ClientModelValidationContext context) + { + ErrorMessage = ValidationAttributesService.DictionaryValue(DictionaryKey); + ErrorMessage = FormatErrorMessage(GetMaxFileSizeInMB()); + AttributeHelper.MergeAttribute(context.Attributes, "data-val", "true"); + AttributeHelper.MergeAttribute(context.Attributes, "data-val-maxfilesize", ErrorMessage); + AttributeHelper.MergeAttribute(context.Attributes, "data-val-maxfilesize-size", MaxFileSize.ToString()); + } + + public override bool IsValid(object value) + { + IFormFile file = value as IFormFile; + bool isValid = true; + + if (file != null) + { + isValid = file.Length <= MaxFileSize; + } + + return isValid; + } + + private string GetMaxFileSizeInMB() + { + return (MaxFileSize % 1048576M == 0) ? (MaxFileSize / 1048576).ToString() : Math.Round(MaxFileSize / 1048576M, 3).ToString(); + } + } +} \ No newline at end of file