Copied to clipboard

Flag this post as spam?

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


  • Carlos Casalicchio 176 posts 736 karma points
    Mar 02, 2023 @ 21:51
    Carlos Casalicchio
    0

    Mapping Umbraco Form Data Types to default Umbraco Data Types

    Is it possible to map Umbraco Forms data types into default Umbraco Data Types?

    What I mean is something like this

    foreach (var set in form.AllFieldSets)
            {
                var safeAlias = set.Caption.ToSafeAlias(shortStringHelper, true);
                if (type.PropertyGroups.SingleOrDefault(x => x.Alias == safeAlias) is null)
                    type.AddPropertyGroup(safeAlias, set.Caption);
    
                foreach (var field in set.AllFields)
                {
                    var dataType = dataTypeService.GetDataType(field.SOMETHING); // get the equivalent data type
                    type.AddPropertyType(dataType);
                }
            }
    

    In other words,

    Get the equivalent Umbraco Datatype from a Form Field (data type) in order to add to a document type or member type.

    Any suggestions or help will be much appreciated!

    EDIT

    I got as far as getting the details for the field using code from Umbraco Forms

        public static class FieldTypeExtensions
    {
        public static bool TryGetFieldTypeForFieldId(Form form, Guid fieldId, IFieldTypeStorage fieldTypeStorage, out FieldType? fieldType)
        {
            Field field = form.AllFields.SingleOrDefault((Field x) => x.Id == fieldId);
            if (field is null)
            {
                fieldType = null;
                return false;
            }
            fieldType = fieldTypeStorage.GetFieldTypeByField(field);
            return fieldType is not null;
        }
    }
    

    But I still haven't figured out how to map that field into a data type.

  • Andy Butland 422 posts 2334 karma points MVP 4x hq c-trib
    Mar 09, 2023 @ 13:59
    Andy Butland
    0

    Hi Carlos

    Although there are similarities of course - Forms has a text field, as does the CMS; Forms has a text area, as does the CMS - there's nothing really that links these up. They are separate implementations.

    As such you won't find a common alias or ID of the "data types" used in CMS and Forms.

    I think you'll have to maintain a mapping - perhaps in a dictionary - that relates the ID or alias of the Forms and CMS equivalents, and then use that to associate them in your code.

    Details of the Form's field types can be found here.

    Hope that helps.

    Andy

  • Carlos Casalicchio 176 posts 736 karma points
    Mar 09, 2023 @ 15:12
    Carlos Casalicchio
    0

    Thank you Andy!

    I got as far as mapping the default types now, thanks!

    public static IDataType? GetEquivalent(this IDataTypeService dataTypeService, FieldType fieldType)
        {
            string guid = fieldType.Id!.ToString().ToUpperInvariant();
            return guid switch
            {
                Constants.FieldTypes.CheckBox => dataTypeService.GetByEditorAlias(UmbracoConstants.PropertyEditors.Aliases.CheckBoxList).First(),
                Constants.FieldTypes.CheckBoxList => dataTypeService.GetByEditorAlias(UmbracoConstants.PropertyEditors.Aliases.CheckBoxList).First(),
                Constants.FieldTypes.DataConsentField => dataTypeService.GetByEditorAlias(UmbracoConstants.PropertyEditors.Aliases.Boolean).First(),
                Constants.FieldTypes.DatePicker => dataTypeService.GetByEditorAlias(UmbracoConstants.PropertyEditors.Aliases.DateTime).First(),
                Constants.FieldTypes.DropDownList => dataTypeService.GetByEditorAlias(UmbracoConstants.PropertyEditors.Aliases.DropDownListFlexible).First(),
                Constants.FieldTypes.RadioButtonList => dataTypeService.GetByEditorAlias(UmbracoConstants.PropertyEditors.Aliases.RadioButtonList).First(),
                Constants.FieldTypes.RichText => dataTypeService.GetByEditorAlias(UmbracoConstants.PropertyEditors.Aliases.TinyMce).First(),
                Constants.FieldTypes.Textfield or Constants.FieldTypes.Text => dataTypeService.GetByEditorAlias(UmbracoConstants.PropertyEditors.Aliases.TextBox).First(),
                Constants.FieldTypes.Textarea => dataTypeService.GetByEditorAlias(UmbracoConstants.PropertyEditors.Aliases.TextArea).First(),
                Constants.FieldTypes.Upload => dataTypeService.GetByEditorAlias(UmbracoConstants.PropertyEditors.Aliases.UploadField).First(),
                _ => null,
            };
        }
    

    But I'm still trying to figure out where the FieldType holds the DataType Id when it's a custom data type (like a checkbox list or dropdown). Or even the Form field itself.

    Do you have any suggestions on that?

  • Andy Butland 422 posts 2334 karma points MVP 4x hq c-trib
    Mar 09, 2023 @ 15:35
    Andy Butland
    0

    Sorry Carlos, but I'm not following that step I'm afraid. Are you referring to the "prevalues" (the options that you have to pick from when you have a checkbox list or a dropdown? If so, you probably need to be looking on the Field object, rather than the FieldType.

  • Carlos Casalicchio 176 posts 736 karma points
    Mar 09, 2023 @ 16:03
    Carlos Casalicchio
    0

    Actually, that's it, you got the idea.

    Basically, I want to be able to map the prevalues to the member properties.

    var type = memberTypeService.GetAll().FirstOrDefault(x => x.Alias == memberType.ToLower());
            foreach (var set in form.AllFieldSets)
            {
                var safeSetAlias = string.IsNullOrEmpty(set.Caption) ? "Ungrouped" : set.Caption.ToSafeAlias(shortStringHelper, true);
                var setName = string.IsNullOrEmpty(set.Caption) ? "Ungrouped" : set.Caption;
                if (type!.PropertyGroups.SingleOrDefault(x => x.Alias == safeSetAlias) is null)
                    type.AddPropertyGroup(safeSetAlias, setName);
    
    
                foreach (var field in set.AllFields)
                {
                    var sourceId = field.PreValueSourceId;
                    FieldTypeExtensions.TryGetFieldTypeForFieldId(form, field.Id, fieldTypeStorage, out FieldType? fieldType);
                    if (fieldType is not null)
                    {
                        IDataType dataType = dataTypeService.GetEquivalent(fieldType);
    
                        //WHERE DO I SET THE PREVALUE SOURCE ID?
                        PropertyType? property = null;
                        if (dataType is not null)
                        {
                            property = new PropertyType(shortStringHelper, dataType)
                            {
                                Alias = field.Alias,
                                Name = field.Caption,
                                CreateDate = DateTime.Now,
                                DataTypeId = dataType.Id,
                                DataTypeKey = dataType.Key,
                                LabelOnTop = true,
                                Mandatory = false,
                                PropertyEditorAlias = dataType.EditorAlias,
                                ValueStorageType = ValueStorageType.Ntext
                            };
                        }
                        else
                        {
                        }
    
                        if (property is not null)
                            type.AddPropertyType(property, safeSetAlias);
                    }
                }
            }
            memberTypeService.Save(type);
        }
    

    I want to be able to use each data type created for the pre-value source in the member properties

    enter image description here

  • Carlos Casalicchio 176 posts 736 karma points
    Mar 09, 2023 @ 20:33
    Carlos Casalicchio
    0

    It's not perfect yet, but I think I've figured it out. Here's the code:

           public static void UpdateMemberProperties(this IMemberTypeService memberTypeService, IServiceScopeFactory scopeFactory, string memberType, Form form)
        {
            using var scope = scopeFactory.CreateScope();
            var memberService = scope.ServiceProvider.GetRequiredService<IMemberService>();
            var dataTypeService = scope.ServiceProvider.GetRequiredService<IDataTypeService>();
            var memberManager = scope.ServiceProvider.GetRequiredService<IMemberManager>();
            var memberGroupService = scope.ServiceProvider.GetRequiredService<IMemberGroupService>();
            var shortStringHelper = scope.ServiceProvider.GetRequiredService<IShortStringHelper>();
            var fieldTypeStorage = scope.ServiceProvider.GetRequiredService<IFieldTypeStorage>();
            var contentTypeService = scope.ServiceProvider.GetRequiredService<IContentTypeService>();
            var prevalueSourceService = scope.ServiceProvider.GetRequiredService<IPrevalueSourceService>();
            var propertyService = scope.ServiceProvider.GetRequiredService<IPropertyTypeUsageService>();
    
            var type = memberTypeService.GetAll().FirstOrDefault(x => x.Alias == memberType.ToLower());
            foreach (var set in form.AllFieldSets)
            {
                var safeSetAlias = string.IsNullOrEmpty(set.Caption) ? "Ungrouped" : set.Caption.ToSafeAlias(shortStringHelper, true);
                var setName = string.IsNullOrEmpty(set.Caption) ? "Ungrouped" : set.Caption;
                if (type!.PropertyGroups.SingleOrDefault(x => x.Alias == safeSetAlias) is null)
                    type.AddPropertyGroup(safeSetAlias, setName);
    
    
                foreach (var field in set.AllFields)
                {
                    var preValueSource = prevalueSourceService.Get(field.PreValueSourceId);
                    IDataType? dataType = preValueSource != null ? dataTypeService.GetDataType(int.Parse(preValueSource.Settings["DataTypeId"])) : null;
                    if (dataType is null)
                    {
                        FieldTypeExtensions.TryGetFieldTypeForFieldId(form, field.Id, fieldTypeStorage, out FieldType? fieldType);
                        if (fieldType is not null)
                            dataType = dataTypeService.GetEquivalent(fieldType);
                    }
                    PropertyType? property = null;
                    if (dataType is not null)
                    {
                        property = new PropertyType(shortStringHelper, dataType)
                        {
                            Alias = field.Alias,
                            Name = field.Caption,
                            CreateDate = DateTime.Now,
                            DataTypeId = dataType.Id,
                            DataTypeKey = dataType.Key,
                            LabelOnTop = true,
                            Mandatory = false,
                            PropertyEditorAlias = dataType.EditorAlias,
                            ValueStorageType = dataType.DatabaseType
                        };
                    }
    
                    if (property is not null && property.Alias != "emailAddress" && property.Alias != "password" && property.Alias != "temporaryPassword")
                    {
                        type.AddPropertyType(property, safeSetAlias);
                        type.SetMemberCanEditProperty(property.Alias, true);
                        type.SetMemberCanViewProperty(property.Alias, true);
                    }
                }
            }
            memberTypeService.Save(type);
        }
    

    I figured out how to deal with the data types, except for file upload, how can I get the upload path?

                        public static string? ParseFieldValueType(this FieldType fieldType, Field field, Form form, Guid uniqueId)
                    {
            #pragma warning disable CS8603 // Possible null reference return.
                        switch (fieldType.Alias)
                        {
                            case "fileUpload":
                                if (field.Values.Count == 0) return "";
            #pragma warning disable CS8602 // Dereference of a possibly null reference.
                                var fileName = field.Values[0].ToString().Split("***|***");
            #pragma warning restore CS8602 // Dereference of a possibly null reference.
                                return $"/media/forms/upload/form_{form.Id}/{uniqueId}/{fileName[1]}";
                            case "checkbox":
                            case "dropdown":
                            case "multipleChoice":
                                return JsonConvert.SerializeObject(field.Values);
                            default: return field.Values.Count > 0 ? field.Values[0].ToString() : null;
            #pragma warning restore CS8603 // Possible null reference return.
                        }
                    }
                }
    

    How can I get the path to the uploaded file, programmatically?

  • Carlos Casalicchio 176 posts 736 karma points
    Mar 13, 2023 @ 19:45
    Carlos Casalicchio
    0

    Does anyone know how to get the Record Unique ID?

  • Andy Butland 422 posts 2334 karma points MVP 4x hq c-trib
    Mar 14, 2023 @ 05:49
    Andy Butland
    0

    It's not quite clear where this code is living, but you need a Record instance, and from there you can access record.UniqueId.

    If you are doing this in a workflow, the the Record is available on the WorkflowExecutionContext - see an example here.

    Andy

  • Carlos Casalicchio 176 posts 736 karma points
    Mar 14, 2023 @ 13:46
    Carlos Casalicchio
    0

    Yes Andy,

    That was my assumption as well. I've used the context.Record.UniqueId, but after some investigation, I've realized the upload path does not include the UniqueId.

    So, the actual question is: Is it possible to assert the folder path to the uploaded file, or is it a random Guid every time?

    $"/media/forms/upload/form_{context.Form.Id}/{UNKNOWN UNIQUE ID}/{fileName[1]}"
    

    Example: /media/forms/upload/form_4b41bb46-7d76-4b46-8e48-3a1c890520cd/74e760bd-9b8b-4448-8fe9-c6e01555ab6f/Requirements.pdf

  • Andy Butland 422 posts 2334 karma points MVP 4x hq c-trib
    Mar 14, 2023 @ 14:27
    Andy Butland
    0

    The second GUID is random, generated when the record is created and the file is saved into the media system. It's not the same as the record unique ID.

    Andy

  • Carlos Casalicchio 176 posts 736 karma points
    Mar 14, 2023 @ 14:28
    Carlos Casalicchio
    0

    Is there a way to get it, to save it to the member?

  • Andy Butland 422 posts 2334 karma points MVP 4x hq c-trib
    Mar 14, 2023 @ 14:36
    Andy Butland
    0

    I'm not quite sure where you are at the point your code is running, but the path to the media file for uploads is stored as the record field value.

    You can see this is what is stored in the database, e.g. with:

    select * from UFRecordDataString where Value like '/media%'

    So if you get the record field, as opposed to the form field. you may be able to retrieve it from the value there.

  • Carlos Casalicchio 176 posts 736 karma points
    Mar 14, 2023 @ 15:57
    Carlos Casalicchio
    0

    Running the code, it's apparent that the record is actually saved to the database after all the workflows are returned completed. Then the database gets populated with that random Id for the UFRecordStringData, and I'm not understanding the Key relation to anything else.

    • Do know how to map that table to the records? enter image description here

    Once the form is persisted, the UFRecords table gets populated with the full form data in the RecordData column. enter image description here

    The workflow I've created copies all the form responses to the member that is also created when the form is submitted.

    The workflow needs to grab the path to the uploaded files and just copy them over to the members as well, but I am not finding a way to do this.

  • Andy Butland 422 posts 2334 karma points MVP 4x hq c-trib
    Mar 15, 2023 @ 06:16
    Andy Butland
    100

    You might try attaching your workflow to the "approved" state rather than the "submitted" one. The record is saved after the "submitted" workflows are completed, but before the "approved" ones.

  • Carlos Casalicchio 176 posts 736 karma points
    Mar 15, 2023 @ 21:40
    Carlos Casalicchio
    0

    Yes!! that was the ticket! :D

    Here's the final code

    public static string? ParseFieldValueType(this FieldType fieldType, Field field, WorkflowExecutionContext context)
        {
            switch (fieldType.Alias)
            {
                case "fileUpload":
                    if (field.Values.Count == 0) return "";
    
                    //Needs to be set after approved, in order to work
                    var path = JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JObject>(context.Record.RecordData).GetValue(field.Id.ToString()).ToString();
                    return path;
                case "dataConsent":
                    if (field.Values.Count == 0) return "";
                    return field.Values[0].ToString() == "true" ? "1" : "0";
                case "checkbox":
                case "dropdown":
                case "multipleChoice":
                    return JsonConvert.SerializeObject(field.Values);
                default: return field.Values.Count > 0 ? field.Values[0].ToString() : null;
            }
        }
    

    Thank you Andy!!!

Please Sign in or register to post replies

Write your reply to:

Draft