Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Andrew B 21 posts 141 karma points
    Nov 05, 2014 @ 17:38
    Andrew B
    0

    Custom file upload data type always invalid when marked as mandatory

    I have implemented a custom file upload field type loosely based on the built-in file upload field type to ensure that the file being uploaded is not over a certain size, and also to prevent any files which are uploaded with the same name from being overwritten. However I am currently encountering an issue where whenever one such field is marked as mandatory and the user attempts to submit the form, it is always reported as being invalid even if a file has been selected. I have added breakpoints at the start of the ProcessValue and Validate methods in my field type and neither are being hit when I submit the page, which indicates to me that there is something being run elsewhere after the postback which is marking the field as invalid - it doesn't appear to be something occuring in the Javascript validation. Can anybody advise please?

    The site uses Umbraco 4.11.10 with Contour 3.0.21. The production environment is Windows Server 2008 with IIS 7.5 and the development environment is Windows 7 with IIS 7.5 - the issue occurs in both environments.

    This is the backend code for my field type:

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Web;
    using System.Web.UI.WebControls;
    using Umbraco.Forms.Core;
    using Umbraco.Forms.Core.Controls;
    
    namespace HealthTech.Core.Contour
    {
        public class FileUploadWithLimit : FieldType
        {
            private List<object> _value = new List<object>();
    
            [Umbraco.Forms.Core.Attributes.Setting("Size limit", prevalues = "", description = "Size limit in kilobytes", control = "Umbraco.Forms.Core.FieldSetting.TextField")]
            public string SizeLimitInKbytes { get; set; }
    
            public FileUploadWithLimit()
            {
                Id = new Guid("0E750295-083B-4CCA-8DC9-13F9974AB5C0");
                Name = "File upload with limit";
                Description = "File upload with limit";
                Icon = "upload.png";
                DataType = FieldDataType.LongString;
            }
    
            public override List<object> ProcessValue(HttpContextBase httpContext)
            {
                _value.Clear();
    
                // if a file has previously been submitted, add to values list
                if (AssociatedField.Values.Any())
                {
                    var currentFile = AssociatedField.Values.FirstOrDefault();
                    _value.Add(currentFile);
                }
    
                // get reference to this field's post parameter
                var fieldId = AssociatedField.Id.ToString();
                var postedFile = httpContext.Request.Files[fieldId];
    
                if (postedFile != null)
                {
                    if (!string.IsNullOrEmpty(postedFile.FileName) && postedFile.ContentLength > 0)
                    {
                        // a file has been submitted
    
                        // if a file has previously been submitted, clear the values list
                        if (_value.Any()) _value.Clear();
    
                        // generate path for this session and create directory
                        var sess = httpContext.Session != null ? httpContext.Session.SessionID : "no-session";
                        var dir = String.Format("{0}/files/{1}/{2}/", Configuration.Path, AssociatedField.Form, sess);
                        if (!Directory.Exists(HttpContext.Current.Server.MapPath(dir)))
                        {
                            Directory.CreateDirectory(HttpContext.Current.Server.MapPath(dir));
                        }
    
                        // ensure filename is unique within directory
                        // try (1)filename.ext, (2)filename.ext... until unique name is found
                        var count = 1;
                        var path = dir + postedFile.FileName;
                        var exists = File.Exists(HttpContext.Current.Server.MapPath(path));
                        while (exists)
                        {
                            path = dir + "(" + count + ")" + postedFile.FileName;
                            exists = File.Exists(HttpContext.Current.Server.MapPath(path));
                            count++;
                        }
    
                        // save file to disk
                        postedFile.SaveAs(HttpContext.Current.Server.MapPath(path));
                        _value.Add(path);
                    }
                }
    
                return _value;
            }
    
            public override string RenderPreview()
            {
                return String.Format("<input type=\"file\" /><p>Maximum file size: {0:n0}KB", Convert.ToInt32(SizeLimitInKbytes));
            }
    
            public override string RenderPreviewWithPrevalues(List<object> prevalues)
            {
                return RenderPreview();
            }
    
            public override List<Exception> Validate(HttpContextBase httpContext)
            {
                var exceptions = new List<Exception>();
                var fieldId = AssociatedField.Id.ToString();
                var postedFiles = httpContext.Request.Files;
    
                if (postedFiles.Count > 0)
                {
                    var file = postedFiles[fieldId];
                    if (file != null && !string.IsNullOrEmpty(file.FileName))
                    {
                        int sizeLimit;
                        if (int.TryParse(SizeLimitInKbytes, out sizeLimit))
                        {
                            if (file.ContentLength / 1024 > sizeLimit)
                            {
                                exceptions.Add(
                                    new Exception(
                                        String.Format("Submitted file exceeds limit. (Size of {2} is {0:n0}KB, size limit is {1:n0}KB)",
                                        file.ContentLength / 1024,
                                        sizeLimit, 
                                        file.FileName)));
                            }
                        }
                        else
                        {
                            exceptions.Add(
                                new Exception(
                                    "Size limit must be an integer"));
                        }
                    }
                }
    
                return exceptions;
            }
    
            public override bool SupportsRegex
            {
                get
                {
                    return false;
                }
            }
    
            public override bool SupportsPrevalues
            {
                get
                {
                    return false;
                }
            }
        }
    }
    
  • Ian Dawson 24 posts 104 karma points
    Nov 07, 2014 @ 14:20
    Ian Dawson
    0

    Coincidentally, I've been trying to do something similar with the file upload and am having exactly the same problem. Marking it as mandatory causes it to always fail but where the validation is taking place I have no idea.

  • Andrew B 21 posts 141 karma points
    Nov 11, 2014 @ 16:08
    Andrew B
    0

    This is an extremely frustrating problem, especially since the inadequate default file upload field type works fine when set to mandatory. As Contour is closed-source I'm unable to debug/resolve this problem myself. It could stem from a bug in my field type implementation, but as the only documentation available concerning creating custom field types is for pre-Razor versions of Contour I'm unable to check this either.

  • Ian Dawson 24 posts 104 karma points
    Nov 12, 2014 @ 12:27
    Ian Dawson
    101

    Hi Andrew.

    I managed to write some (very hacky) code to get round this problem which you should be able to adapt for your needs. It basically checks the modelstate for any mandatory errors related to my upload control (I'm using the CogFilteredUpload) and checks the request for an uploaded file for that specific control.

    void FormRenderController_FormValidate(object sender, Umbraco.Forms.Mvc.FormViewEventArgs e)
        {
            Controller controller = sender as Controller;
            if (!controller.ModelState.IsValid && e.Context.Request.Files.Count > 0)
            { // The CogFilteredUpload always validates as false if set to mandatory so need to check
                // that it actually has a file
                foreach (var key in controller.ModelState.Keys)
                {
                    if (controller.ModelState[key].Errors.Count > 0)
                    {
                        Field field = e.Form.AllFields.FirstOrDefault(x => x.Id == Guid.Parse(key));
                        ModelError mandatoryError = controller.ModelState[key].Errors.FirstOrDefault(x => x.ErrorMessage.IndexOf("mandatory", StringComparison.OrdinalIgnoreCase) != -1);
                        if (field != null && field.Mandatory && mandatoryError != null)
                        {
                            if (field.FieldType.ToString().IndexOf("CogFilteredUpload", StringComparison.OrdinalIgnoreCase) != -1)
                            {
                                HttpPostedFile file = e.Context.Request.Files[key];
                                if (file != null && file.ContentLength > 0)
                                {
                                    controller.ModelState[key].Errors.Remove(mandatoryError);
                                }
                            }
                        }
                    }
                }                
            }
        }
    

    It's not an ideal solution but unless the contour developers can tell us what's wrong then it's what I'm stuck with at the moment.

  • Andrew B 21 posts 141 karma points
    Nov 13, 2014 @ 16:31
    Andrew B
    0

    Nice one Ian, thanks for sharing! I'll give that a go. :)

  • Andrew B 21 posts 141 karma points
    Nov 18, 2014 @ 12:11
    Andrew B
    0

    I gave your code a go and it's solved the issue nicely Ian, thanks again for your help!

    The lack of any feedback from Umbraco staff on this is concerning. Given the relative simplicity of the bundled File Upload field type (e.g. unable to specify permitted file types, maximum/minimum file sizes...) custom implementations of such fields are hardly going to be uncommon, as are cases where such fields are mandatory. At the very least it'd be good to know if this issue is due to a fault in my field implementation, or something deeper within Contour itself.

  • Andrew B 21 posts 141 karma points
    Jul 20, 2016 @ 15:44
    Andrew B
    0

    I encountered this exact same issue again today, and it's just as frustrating as it was over a year ago. Has there really been no progress made in resolving this issue? Ian's workaround may be nice, but it is just that - a workaround. Can details be provided of what validation takes place for mandatory fields within Contour, which may shed some light on why it is failing?

Please Sign in or register to post replies

Write your reply to:

Draft