Complex Custom Validation in Foolproof Validation - validation

I have implemented Complex custom Foolproof validation in my application from this link but sadly its not working.My requirement is simple,I have a input file for uploading an image and there should be a validation if the user chooses to upload file other than specified below
".jpg",".png",".gif",".jpeg"
Code is
[Required(ErrorMessage = "Please upload Photo", AllowEmptyStrings = false)]
[IsValidPhoto(ErrorMessage="Please select files of type .jpg,.png,.gif,.jpeg")]
public HttpPostedFileBase PhotoUrl { get; set; }
public class IsValidPhotoAttribute : ModelAwareValidationAttribute
{
//this is needed to register this attribute with foolproof's validator adapter
static IsValidPhotoAttribute() { Register.Attribute(typeof(IsValidPhotoAttribute)); }
public override bool IsValid(object value, object container)
{
if (value != null)
{
string[] AllowedFileExtensions = new string[] { ".jpg", ".gif", ".png", ".jpeg" };
var file = value as HttpPostedFileBase;
if (!AllowedFileExtensions.Contains(file.FileName.Substring(file.FileName.LastIndexOf('.'))))
{
return false;
}
}
return true;
}
}
CSHTML is
#Html.TextBoxFor(m => m.PhotoUrl, new { #class = "form-control imgUpload",
#placeholder = "Please upload Photo", #id = "txtPhoto", #type = "file" })
#Html.ValidationMessageFor(m => m.PhotoUrl)

You will not be able to get client side validation unless you also create a script to add the rules. It is not necessary to use foolproof and the following method and scripts will give you both server and client side validation
public class FileAttachmentAttribute : ValidationAttribute, IClientValidatable
{
private List<string> _Extensions { get; set; }
private const string _DefaultErrorMessage = "Only file types with the following extensions are allowed: {0}";
public FileAttachmentAttribute(string fileExtensions)
{
_Extensions = fileExtensions.Split('|').ToList();
ErrorMessage = _DefaultErrorMessage;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
HttpPostedFileBase file = value as HttpPostedFileBase;
if (file != null)
{
var isValid = _Extensions.Any(e => file.FileName.EndsWith(e));
if (!isValid)
{
return new ValidationResult(string.Format(ErrorMessageString, string.Join(", ", _Extensions)));
}
}
return ValidationResult.Success;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ValidationType = "fileattachment",
ErrorMessage = string.Format(ErrorMessageString, string.Join(", ", _Extensions))
};
rule.ValidationParameters.Add("extensions", string.Join(",", _Extensions));
yield return rule;
}
}
Scripts
$.validator.unobtrusive.adapters.add('fileattachment', ['extensions'], function (options) {
var params = { fileattachment: options.params.extensions.split(',') };
options.rules['fileattachment'] = params;
if (options.message) {
options.messages['fileattachment'] = options.message;
}
});
$.validator.addMethod("fileattachment", function (value, element, param) {
var extension = getExtension(value);
return $.inArray(extension, param.fileextensions) !== -1;
});
function getExtension(fileName) {
var extension = (/[.]/.exec(fileName)) ? /[^.]+$/.exec(fileName) : undefined;
if (extension != undefined) {
return extension[0];
}
return extension;
};
and then use it as
[FileAttachment("jpg|gif|png|jpeg")]
public HttpPostedFileBase PhotoUrl { get; set; }

Related

Xamarin forms monkey chat

I am using monkey chat in my mobile application(Xamrin forms based). While sending a message on IOS, on a real device, I have to click the send button 2 times. First time when I click, it minimizes the keyboard and 2nd time, it sends the message. Please suggest.
How do I send a message on one click?
Here is my send message function
namespace Budocode.ViewModels
{
public class MainChatViewModel : BaseViewModel
{
public ObservableRangeCollection Messages { get; }
ITwilioMessenger twilioMessenger;
string outgoingText = string.Empty;
public string OutGoingText
{
get { return outgoingText; }
set { SetProperty(ref outgoingText, value); }
}
public ICommand SendCommand { get; set; }
public ICommand LocationCommand { get; set; }
public MainChatViewModel()
{
// Initialize with default values
twilioMessenger = DependencyService.Get<ITwilioMessenger>();
Messages = new ObservableRangeCollection<ChatMessage>();
SendCommand = new Command(() =>
{
var message = new ChatMessage
{
Text = OutGoingText,
IsIncoming = false,
ProfileId = "profile" + GlobalSettingsDataSource.Current.SelectedProfileImageId + ".png",
MessageDateTime = DateTime.Now,
FromUser = GlobalSettingsDataSource.Current.SelectedProfile
};
if (string.IsNullOrWhiteSpace(OutGoingText))
return;
Messages.Add(message);
twilioMessenger?.SendMessage(message.Text, message.ProfileId, GlobalSettingsDataSource.Current.SelectedProfile);
OutGoingText = string.Empty;
});
LocationCommand = new Command(async () =>
{
try
{
var local = await CrossGeolocator.Current.GetPositionAsync(TimeSpan.FromSeconds(15));
var map = $"https://maps.googleapis.com/maps/api/staticmap?center={local.Latitude.ToString(CultureInfo.InvariantCulture)},{local.Longitude.ToString(CultureInfo.InvariantCulture)}&zoom=17&size=400x400&maptype=street&markers=color:red%7Clabel:%7C{local.Latitude.ToString(CultureInfo.InvariantCulture)},{local.Longitude.ToString(CultureInfo.InvariantCulture)}&key=";
var message = new ChatMessage
{
Text = "I am here",
AttachementUrl = map,
ProfileId = "profile" + GlobalSettingsDataSource.Current.SelectedProfileImageId + ".png",
IsIncoming = false,
MessageDateTime = DateTime.Now
};
Messages.Add(message);
twilioMessenger?.SendMessage("attach:" + message.AttachementUrl, message.ProfileId, GlobalSettingsDataSource.Current.SelectedProfile);
}
catch (Exception ex)
{
}
});
if (twilioMessenger == null)
return;
twilioMessenger.MessageAdded = (message) =>
{
//if (message.ProfileId == "Icon.png")
Device.BeginInvokeOnMainThread(() =>
{
if (message.FromUser != GlobalSettingsDataSource.Current.SelectedProfile)
{
message.IsIncoming = true;
}
else
{
message.IsIncoming = false;
}
Messages.Add(message);
});
};
}
public async void InitializeMock(string channelName)
{
try
{
var id = CrossDeviceInfo.Current.Id;
var userId = PersistantData.Current.UserAccount.Properties["user_id"];
HttpResponseMessage appResponse = CommonUtility.GetApiContent(
String.Format(ClientConfiguration.TwilioServiceChatHistory, id, GlobalSettingsDataSource.Current.SelectedProfile, channelName));
if (appResponse.IsSuccessStatusCode)
{
var chatContent = await appResponse.Content.ReadAsStringAsync();
List<ChatMessage> chatMsgs = JsonConvert.DeserializeObject<List<ChatMessage>>(chatContent);
Messages.ReplaceRange(chatMsgs);
}
}
catch
{
// ignore if there are any issues with twilio
}
}
}
}

Asp.net MVC3 custom validator created using dataannotation showing messages in incorrect place

I am using Asp.net MVC3, razor view engine and data annotation for model validation.
I have a form in which have to input Url details(Url and Description).Both fields are not required. But if i input one field other must be required.If i input description Url is required and is in correct format and if i enter Url then description is required.
I created a customvalidator for data annotation .It validates and output error message.
But my problem is error message generated by ValidationMessageFor is in incorrect place.
ie,if i enter description ,the required url message, is part of description.
I expect that message as part of ValidationMessageFor url field.
Can any one can help me? Thanks in advance. Folowing are the code i used
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class IsExistAttribute : ValidationAttribute, IClientValidatable
{
private const string DefaultErrorMessage = "{0} is required.";
public string OtherProperty { get; private set; }
public IsExistAttribute (string otherProperty)
: base(DefaultErrorMessage)
{
if (string.IsNullOrEmpty(otherProperty))
{
throw new ArgumentNullException("otherProperty");
}
OtherProperty = otherProperty;
}
public override string FormatErrorMessage(string name)
{
return string.Format(ErrorMessageString, name, OtherProperty);
}
protected override ValidationResult IsValid(object value,ValidationContext validationContext)
{
if (value != null)
{
var otherProperty = validationContext.ObjectInstance.GetType()
.GetProperty(OtherProperty);
var otherPropertyValue = otherProperty
.GetValue(validationContext.ObjectInstance, null);
var strvalue=Convert.ToString(otherPropertyValue)
if (string.IsNullOrEmpty(strvalue))
{
//return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName),new[] { OtherProperty});
}
}
return ValidationResult.Success;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata,ControllerContext context)
{
var clientValidationRule = new ModelClientValidationRule()
{
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "isexist"
};
clientValidationRule.ValidationParameters.Add("otherproperty", OtherProperty);
return new[] { clientValidationRule };
}
}
in model
[Display(Name = "Description")]
[IsExist("Url")]
public string Description { get; set; }
[Display(Name = "Url")]
[IsExist("Description")]
[RegularExpression("(http(s)?://)?([\w-]+\.)+[\w-]+(/[\w- ;,./?%&=]*)?", ErrorMessage = "Invalid Url")]
public string Url { get; set; }
and in view
<div class="editor-field">
#Html.TextBoxFor(m => m.Description )
#Html.ValidationMessageFor(m => m.Description)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.Url)
#Html.ValidationMessageFor(m => m.Url)
</div>
unobstrusive validation logic
(function ($) {
$.validator.addMethod("isexist", function (value, element, params) {
if (!this.optional(element)) {
var otherProp = $('#' + params)
return (otherProp.val() !='' && value!='');//validation logic--edited by Rajesh
}
return true;
});
$.validator.unobtrusive.adapters.addSingleVal("isexist", "otherproperty");
} (jQuery));
You should make the validation the other way round. Change:
if (string.IsNullOrEmpty(otherPropertyValue))
{
//return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName),new[] { OtherProperty});
}
To:
if (string.IsNullOrEmpty(Convert.ToString(value)) && !string.IsNullOrEmpty(otherPropertyValue))
{
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
And remove if (value != null)
The field that will then get invalidated is the empty one, in the case the other one is filled.

client side validation in MVC3 not working

below is the code somehow client side validation is not working...I searched couple of questions in this forum and wrote this..
here is the custom validation attribute "startDateAttribute"
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class StartDateAttribute : ValidationAttribute, IClientValidatable
{
public StartDateAttribute ()
{
}
public override bool IsValid(object value)
{
var date = (DateTime)value;
if (date.Date >= DateTime.Now.Date)
{
return true;
}
return false;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
yield return new ModelClientValidationRule
{
ErrorMessage = this.ErrorMessage,
ValidationType = "DateRange"
};
}
}
[CurrentDateAttribute(ErrorMessage = "select the correct date")]
public DateTime? StartDate { get; set; }
here is the JQuery code added
jQuery.validator.addMethod('DateRange', function (value, element, params) {
var d = new Date();
var currentDate = (d.getMonth()+1) + "/"+d.getDate()+ "/" + d.getFullYear() ;
return value >= currentDate;
});
// and an unobtrusive adapter
jQuery.validator.unobtrusive.adapters.add('DateRange', { }, function (options) {
options.rules['DateRange'] = true;
options.messages['DateRange'] = options.message;
});
One of the requirements of client side validation is that the ValidationType and the adapter name should match and should be lower case.
Change the ValidationType and adapter name to 'daterange' and check

MVC unobtrusive validation

I want to use unobtrusive validation on my MVC app.
I've created class
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = true)]
public sealed class RequiredIfAttribute : ValidationAttribute, IClientValidatable
{
private const string _defaultErrorMessage = "{0} is required";
private string _targetPropertyName;
private bool _targetPropertyCondition;
public RequiredIfAttribute(string targetPropertyName, bool targetPropertyCondition)
: base(_defaultErrorMessage)
{
this._targetPropertyName = targetPropertyName;
this._targetPropertyCondition = targetPropertyCondition;
}
public override string FormatErrorMessage(string name)
{
return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString, name, _targetPropertyName, _targetPropertyCondition);
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
bool result = false;
bool propertyRequired = false;
var targetProperty = validationContext.ObjectType.GetProperty(_targetPropertyName);
var targetPropertyValue = (bool)targetProperty.GetValue(validationContext.ObjectInstance, null);
if (targetPropertyValue == _targetPropertyCondition)
{
propertyRequired = true;
}
if (propertyRequired)
{
if (value == null)
{
var message = FormatErrorMessage(validationContext.DisplayName);
return new ValidationResult(message);
}
}
return null;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule();
rule.ErrorMessage = FormatErrorMessage(metadata.GetDisplayName());
rule.ValidationType = "requiredif";
rule.ValidationParameters.Add("targetpropertyname", this._targetPropertyName);
rule.ValidationParameters.Add("targetpropertyvalue", this._targetPropertyCondition.ToString().ToLower());
yield return rule;
}
}
I have validation function on client
$(function () {
$.validator.addMethod("requiredif", function (value, element, param) {
if ($(param.propertyname).is(':checked').toString() == param.propertyvalue) {
if (!this.depend(param, element))
return "dependency-mismatch";
switch (element.nodeName.toLowerCase()) {
case 'select':
var val = $(element).val();
return val && val.length > 0;
case 'input':
if (this.checkable(element))
return this.getLength(value, element) > 0;
default:
return $.trim(value).length > 0;
}
}
return true;
});
$.validator.unobtrusive.adapters.add("requiredif", ["targetpropertyname", "targetpropertyvalue"], function (options) {
if (options.element.tagName.toUpperCase() !== "INPUT" || options.element.type.toUpperCase() !== "CHECKBOX") {
options.rules["requiredif"] = {
propertyname: "#" + options.params.targetpropertyname,
propertyvalue: options.params.targetpropertyvalue
};
options.messages["requiredif"] = options.message;
}
});
} (jQuery));
But that does not validate. Why?
Check your web.config if you have there smth. like this
<appSettings>
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
of course you should use proper attributes in your model
Use valid js file.
https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min.js
Can also be through the controller:
HtmlHelper.ClientValidationEnabled = true;
HtmlHelper.UnobtrusiveJavaScriptEnabled = true;

Set current, before current and after current elements in menu

For setting that I use Html helper method which is not the best imo, because I use static field.
public enum CurrentState
{
BeforeCurrent,
AfterCurrent
}
public static CurrentState currentState = CurrentState.BeforeCurrent;
public static MvcHtmlString ActiveActionLink(this HtmlHelper helper, string linkText, string actionName, string controllerName, bool checkAction = true)
{
string currentAction = helper.ViewContext.RouteData.GetRequiredString("action");
string currentController = helper.ViewContext.RouteData.GetRequiredString("controller");
if ((controllerName == currentController) && checkAction && (actionName == "Index"))
{
currentState = CurrentState.BeforeCurrent;
}
if ((controllerName == currentController) && checkAction && (actionName != currentAction))
{
if (currentState == CurrentState.BeforeCurrent)
{
return helper.ActionLink(linkText, actionName, controllerName, null, new { #class = "beforeCurrent" });
}
else if (currentState == CurrentState.AfterCurrent)
{
return helper.ActionLink(linkText, actionName, controllerName, null, new { #class = "afterCurrent" });
}
}
if ((controllerName == currentController) && (!checkAction || (actionName == currentAction)))
{
currentState = CurrentState.AfterCurrent;
return helper.ActionLink(linkText, actionName, controllerName, null, new { #class = "current" });
}
return helper.ActionLink(linkText, actionName, controllerName);
}
I have two levels of menus and that's why I use checkAction parameter:
main menu - #Html.ActiveActionLink(Resources.Global.mainMenuBoard, "Index", "Board", checkAction: false)
side menu - #Html.ActiveActionLink(#Resources.Global.managementOverview, "Index", "Management")
and in side menu I need to know if it's after and before current (overlapping items...).
Is it a way to improve that?
Additionally I must say that I use javascript also for that but it must work also for javascript disabled.
I finally solve that by generating whole menu in one helper:
public class Link
{
public string LinkText { get; set; }
public string ActionName { get; set; }
}
public static List<MvcHtmlString> SubMenuLinks(this HtmlHelper helper, string controllerName, List<Link> links)
{
List<MvcHtmlString> menuElements = new List<MvcHtmlString>();
string actualCssClass = "beforeCurrent";
string currentAction = helper.ViewContext.RouteData.GetRequiredString("action");
string currentController = helper.ViewContext.RouteData.GetRequiredString("controller");
foreach (Link link in links)
{
if (controllerName == currentController && link.ActionName == currentAction)
{
menuElements.Add(helper.ActionLink(link.LinkText, link.ActionName, controllerName, null, new { #class = "current" }));
actualCssClass = "afterCurrent";
}
else
{
menuElements.Add(helper.ActionLink(link.LinkText, link.ActionName, controllerName, null, new { #class = actualCssClass }));
}
}
return menuElements;
}
and in view:
#{
List<MvcHtmlString> actionMenu = Html.SubMenuLinks("Manager", new List<Link>()
{
new Link() { LinkText = "linkText", ActionName = "actionName" },
new Link() { LinkText = "linkText2", ActionName = "actionName2" }
});
}
#section SideMenu
{
#for (int i = 0; i < actionMenu.Count; i++)
{
<li id="menu#(i)">#actionMenu.ElementAt(i)</li>
}
}
Not perfect, but it works at least.

Resources